]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jh/checkout-auto-tracking' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 23 Oct 2013 20:32:50 +0000 (13:32 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 23 Oct 2013 20:32:50 +0000 (13:32 -0700)
"git branch --track" had a minor regression in v1.8.3.2 and later
that made it impossible to base your local work on anything but a
local branch of the upstream repository you are tracking from.

* jh/checkout-auto-tracking:
  t3200: fix failure on case-insensitive filesystems
  branch.c: Relax unnecessary requirement on upstream's remote ref name
  t3200: Add test demonstrating minor regression in 41c21f2
  Refer to branch.<name>.remote/merge when documenting --track
  t3200: Minor fix when preparing for tracking failure
  t2024: Fix &&-chaining and a couple of typos

600 files changed:
.gitignore
.mailmap
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/RelNotes/1.7.10.1.txt
Documentation/RelNotes/1.7.5.4.txt
Documentation/RelNotes/1.7.8.2.txt
Documentation/RelNotes/1.7.8.txt
Documentation/RelNotes/1.8.2.1.txt
Documentation/RelNotes/1.8.2.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.2.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.3.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.3.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.3.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.3.4.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.3.txt
Documentation/RelNotes/1.8.4.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.4.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.4.txt [new file with mode: 0644]
Documentation/blame-options.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-archive.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-blame.txt
Documentation/git-cat-file.txt
Documentation/git-check-attr.txt
Documentation/git-check-ignore.txt
Documentation/git-check-mailmap.txt [new file with mode: 0644]
Documentation/git-check-ref-format.txt
Documentation/git-checkout.txt
Documentation/git-clean.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-config.txt
Documentation/git-daemon.txt
Documentation/git-describe.txt
Documentation/git-diff-index.txt
Documentation/git-diff.txt
Documentation/git-difftool.txt
Documentation/git-fast-export.txt
Documentation/git-fetch-pack.txt
Documentation/git-fmt-merge-msg.txt
Documentation/git-format-patch.txt
Documentation/git-fsck.txt
Documentation/git-gc.txt
Documentation/git-grep.txt
Documentation/git-http-backend.txt
Documentation/git-index-pack.txt
Documentation/git-log.txt
Documentation/git-ls-files.txt
Documentation/git-ls-remote.txt
Documentation/git-mailinfo.txt
Documentation/git-merge-index.txt
Documentation/git-merge.txt
Documentation/git-mergetool.txt
Documentation/git-name-rev.txt
Documentation/git-p4.txt
Documentation/git-pack-objects.txt
Documentation/git-pack-refs.txt
Documentation/git-patch-id.txt
Documentation/git-prune-packed.txt
Documentation/git-prune.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-reflog.txt
Documentation/git-remote.txt
Documentation/git-replace.txt
Documentation/git-reset.txt
Documentation/git-rev-list.txt
Documentation/git-rev-parse.txt
Documentation/git-revert.txt
Documentation/git-send-email.txt
Documentation/git-shortlog.txt
Documentation/git-show-branch.txt
Documentation/git-show-index.txt
Documentation/git-show-ref.txt
Documentation/git-show.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git-update-index.txt
Documentation/git-verify-pack.txt
Documentation/git-verify-tag.txt
Documentation/git-web--browse.txt
Documentation/git.txt
Documentation/gitcore-tutorial.txt
Documentation/gitdiffcore.txt
Documentation/githooks.txt
Documentation/gitremote-helpers.txt
Documentation/gitrepository-layout.txt
Documentation/gittutorial-2.txt
Documentation/gitweb.conf.txt
Documentation/gitweb.txt
Documentation/glossary-content.txt
Documentation/howto/new-command.txt
Documentation/howto/recover-corrupted-blob-object.txt
Documentation/howto/revert-branch-rebase.txt
Documentation/line-range-format.txt [new file with mode: 0644]
Documentation/merge-options.txt
Documentation/pretty-formats.txt
Documentation/pretty-options.txt
Documentation/pull-fetch-param.txt
Documentation/rev-list-options.txt
Documentation/revisions.txt
Documentation/technical/api-builtin.txt
Documentation/technical/api-directory-listing.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/api-revision-walking.txt
Documentation/technical/api-sha1-array.txt
Documentation/technical/index-format.txt
Documentation/technical/pack-format.txt
Documentation/technical/pack-heuristics.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt
Documentation/technical/racy-git.txt
Documentation/technical/shallow.txt
Documentation/urls.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
Makefile
RelNotes
abspath.c
advice.c
advice.h
archive-zip.c
argv-array.h
attr.c
bisect.c
branch.c
builtin.h
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/cat-file.c
builtin/check-ignore.c
builtin/check-mailmap.c [new file with mode: 0644]
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/describe.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch.c
builtin/fsck.c
builtin/grep.c
builtin/help.c
builtin/index-pack.c
builtin/log.c
builtin/ls-files.c
builtin/merge-base.c
builtin/merge-index.c
builtin/merge-tree.c
builtin/merge.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/pack-refs.c
builtin/prune-packed.c
builtin/prune.c
builtin/push.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/rm.c
builtin/shortlog.c
builtin/show-branch.c
builtin/show-ref.c
builtin/update-index.c
bulk-checkin.c
bundle.c
cache-tree.c
cache-tree.h
cache.h
color.c
combine-diff.c
command-list.txt
commit-slab.h [new file with mode: 0644]
commit.c
commit.h
compat/cygwin.c [deleted file]
compat/cygwin.h [deleted file]
compat/fnmatch/fnmatch.c
compat/mingw.c
compat/mingw.h
compat/nedmalloc/Readme.txt
compat/nedmalloc/malloc.c.h
compat/nedmalloc/nedmalloc.c
compat/poll/poll.c
compat/precompose_utf8.c
compat/regex/regcomp.c
compat/regex/regexec.c
compat/terminal.c
compat/unsetenv.c
compat/win32.h
compat/win32/pthread.c
compat/win32mmap.c
config.c
config.mak.uname
configure.ac
connect.c
connected.c
connected.h
contrib/blameview/README [deleted file]
contrib/blameview/blameview.perl [deleted file]
contrib/ciabot/ciabot.py
contrib/ciabot/ciabot.sh
contrib/completion/git-completion.bash
contrib/completion/git-completion.zsh
contrib/completion/git-prompt.sh
contrib/contacts/git-contacts [new file with mode: 0755]
contrib/contacts/git-contacts.txt [new file with mode: 0644]
contrib/continuous/cidaemon [deleted file]
contrib/continuous/post-receive-cinotify [deleted file]
contrib/credential/osxkeychain/git-credential-osxkeychain.c
contrib/hg-to-git/hg-to-git.py
contrib/hooks/multimail/README [new file with mode: 0644]
contrib/hooks/multimail/README.Git [new file with mode: 0644]
contrib/hooks/multimail/README.migrate-from-post-receive-email [new file with mode: 0644]
contrib/hooks/multimail/git_multimail.py [new file with mode: 0755]
contrib/hooks/multimail/migrate-mailhook-config [new file with mode: 0755]
contrib/hooks/multimail/post-receive [new file with mode: 0755]
contrib/hooks/post-receive-email
contrib/mw-to-git/.gitignore
contrib/mw-to-git/.perlcriticrc [new file with mode: 0644]
contrib/mw-to-git/Git/Mediawiki.pm [new file with mode: 0644]
contrib/mw-to-git/Makefile
contrib/mw-to-git/bin-wrapper/git [new file with mode: 0755]
contrib/mw-to-git/git-mw.perl [new file with mode: 0755]
contrib/mw-to-git/git-remote-mediawiki.perl
contrib/mw-to-git/t/t9365-continuing-queries.sh [new file with mode: 0755]
contrib/mw-to-git/t/test-gitmw-lib.sh
contrib/mw-to-git/t/test.config
contrib/patches/docbook-xsl-manpages-charmap.patch [deleted file]
contrib/remote-helpers/git-remote-bzr
contrib/remote-helpers/git-remote-hg
contrib/remote-helpers/test-bzr.sh
contrib/remote-helpers/test-hg-bidi.sh
contrib/remote-helpers/test-hg-hg-git.sh
contrib/remote-helpers/test-hg.sh
contrib/subtree/Makefile
contrib/subtree/git-subtree.sh
contrib/subtree/git-subtree.txt
contrib/subtree/t/t7900-subtree.sh
convert.c
convert.h
credential-store.c
daemon.c
date.c
decorate.c
diff-lib.c
diff-no-index.c
diff.c
diffcore-pickaxe.c
dir.c
dir.h
entry.c
environment.c
exec_cmd.h
fast-import.c
fetch-pack.c
fetch-pack.h
fixup-builtins [deleted file]
git-add--interactive.perl
git-am.sh
git-bisect.sh
git-compat-util.h
git-cvsserver.perl
git-difftool.perl
git-gui/GIT-VERSION-GEN
git-gui/Makefile
git-gui/git-gui.sh
git-gui/lib/choose_repository.tcl
git-gui/lib/diff.tcl
git-gui/lib/mergetool.tcl
git-gui/lib/remote.tcl
git-gui/po/fr.po
git-mergetool--lib.sh
git-p4.py
git-pull.sh
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase--merge.sh
git-rebase.sh
git-remote-testgit.sh [moved from git-remote-testgit with 55% similarity]
git-request-pull.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
git.rc [new file with mode: 0644]
gitk-git/gitk
gitk-git/po/sv.po
gitweb/INSTALL
gitweb/gitweb.perl
help.c
help.h
http-backend.c
http-push.c
http.c
imap-send.c
kwset.c
line-log.c [new file with mode: 0644]
line-log.h [new file with mode: 0644]
line-range.c [new file with mode: 0644]
line-range.h [new file with mode: 0644]
list-objects.c
list-objects.h
lockfile.c
log-tree.c
log-tree.h
mailmap.c
match-trees.c
merge-recursive.c
mergetools/kdiff3
notes-merge.c
notes-merge.h
notes-utils.c [new file with mode: 0644]
notes-utils.h [new file with mode: 0644]
notes.c
notes.h
object.c
object.h
pack-refs.c [deleted file]
pack-refs.h [deleted file]
pack-revindex.c
parse-options-cb.c
parse-options.h
path.c
pathspec.c
perl/Git.pm
perl/Git/SVN/Editor.pm
perl/Git/SVN/Fetcher.pm
perl/Git/SVN/Prompt.pm
perl/Git/SVN/Ra.pm
po/TEAMS
po/de.po
po/fr.po [new file with mode: 0644]
po/git.pot
po/sv.po
po/vi.po
po/zh_CN.po
pretty.c
prio-queue.c [new file with mode: 0644]
prio-queue.h [new file with mode: 0644]
quote.c
quote.h
read-cache.c
refs.c
refs.h
remote-curl.c
remote-testsvn.c
remote.c
rerere.c
resolve-undo.c
revision.c
revision.h
run-command.c
run-command.h
sequencer.c
setup.c
sha1_file.c
sha1_name.c
shallow.c
shell.c
streaming.c
submodule.c
t/Makefile
t/README
t/annotate-tests.sh
t/check-non-portable-shell.pl
t/gitweb-lib.sh
t/lib-httpd/apache.conf
t/lib-rebase.sh
t/lib-t6000.sh
t/perf/README
t/perf/p0002-read-cache.sh [new file with mode: 0755]
t/perf/p4211-line-log.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/t0000-basic.sh
t/t0001-init.sh
t/t0005-signals.sh
t/t0008-ignores.sh
t/t0009-prio-queue.sh [new file with mode: 0755]
t/t0020-crlf.sh
t/t0021-conversion.sh
t/t0040-parse-options.sh
t/t0060-path-utils.sh
t/t0070-fundamental.sh
t/t0100-previous.sh
t/t1004-read-tree-m-u-wf.sh
t/t1006-cat-file.sh
t/t1011-read-tree-sparse-checkout.sh
t/t1307-config-blob.sh [new file with mode: 0755]
t/t1403-show-ref.sh [new file with mode: 0755]
t/t1411-reflog-show.sh
t/t1507-rev-parse-upstream.sh
t/t1508-at-combinations.sh
t/t1512-rev-parse-disambiguation.sh
t/t1513-rev-parse-prefix.sh [new file with mode: 0755]
t/t2001-checkout-cache-clash.sh
t/t2004-checkout-cache-temp.sh
t/t2007-checkout-symlink.sh
t/t2012-checkout-last.sh
t/t2021-checkout-overwrite.sh
t/t2200-add-update.sh
t/t2202-add-addremove.sh
t/t3001-ls-files-others-exclude.sh
t/t3010-ls-files-killed-modified.sh
t/t3030-merge-recursive.sh
t/t3032-merge-recursive-options.sh
t/t3070-wildmatch.sh
t/t3100-ls-tree-restrict.sh
t/t3210-pack-refs.sh
t/t3211-peel-ref.sh
t/t3400-rebase.sh
t/t3401-rebase-partial.sh [deleted file]
t/t3403-rebase-skip.sh
t/t3404-rebase-interactive.sh
t/t3406-rebase-message.sh
t/t3409-rebase-preserve-merges.sh
t/t3415-rebase-autosquash.sh
t/t3420-rebase-autostash.sh [new file with mode: 0755]
t/t3421-rebase-topology-linear.sh [new file with mode: 0755]
t/t3425-rebase-topology-merges.sh [new file with mode: 0755]
t/t3505-cherry-pick-empty.sh
t/t3509-cherry-pick-merge-df.sh
t/t3600-rm.sh
t/t3700-add.sh
t/t3900-i18n-commit.sh
t/t3900/UTF-16.txt [new file with mode: 0644]
t/t3903-stash.sh
t/t4000-diff-format.sh
t/t4008-diff-break-rewrite.sh
t/t4011-diff-symlink.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4023-diff-rename-typechange.sh
t/t4030-diff-textconv.sh
t/t4038-diff-combined.sh
t/t4041-diff-submodule-option.sh
t/t4111-apply-subdir.sh
t/t4114-apply-typechange.sh
t/t4115-apply-symlink.sh
t/t4122-apply-symlink-inside.sh
t/t4150-am.sh
t/t4202-log.sh
t/t4203-mailmap.sh
t/t4205-log-pretty-formats.sh
t/t4207-log-decoration-colors.sh
t/t4211-line-log.sh [new file with mode: 0755]
t/t4211/expect.beginning-of-file [new file with mode: 0644]
t/t4211/expect.end-of-file [new file with mode: 0644]
t/t4211/expect.move-support-f [new file with mode: 0644]
t/t4211/expect.multiple [new file with mode: 0644]
t/t4211/expect.multiple-overlapping [new file with mode: 0644]
t/t4211/expect.multiple-superset [new file with mode: 0644]
t/t4211/expect.parallel-change-f-to-main [new file with mode: 0644]
t/t4211/expect.simple-f [new file with mode: 0644]
t/t4211/expect.simple-f-to-main [new file with mode: 0644]
t/t4211/expect.simple-main [new file with mode: 0644]
t/t4211/expect.simple-main-to-end [new file with mode: 0644]
t/t4211/expect.two-ranges [new file with mode: 0644]
t/t4211/expect.vanishes-early [new file with mode: 0644]
t/t4211/history.export [new file with mode: 0644]
t/t4212-log-corrupt.sh [new file with mode: 0755]
t/t4254-am-corrupt.sh
t/t4300-merge-tree.sh
t/t5000-tar-tree.sh
t/t5000/pax.tar [new file with mode: 0644]
t/t5003-archive-zip.sh
t/t5004-archive-corner-cases.sh
t/t5004/empty-with-pax-header.tar [new file with mode: 0644]
t/t5150-request-pull.sh
t/t5303-pack-corruption-resilience.sh
t/t5407-post-rewrite-hook.sh
t/t5500-fetch-pack.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5520-pull.sh
t/t5521-pull-options.sh
t/t5528-push-default.sh
t/t5530-upload-pack-error.sh
t/t5541-http-push.sh
t/t5551-http-fetch.sh
t/t5560-http-backend-noserver.sh
t/t5601-clone.sh
t/t5700-clone-reference.sh
t/t5702-clone-options.sh
t/t5706-clone-branch.sh
t/t5710-info-alternate.sh
t/t5801-remote-helpers.sh
t/t5802-connect-helper.sh [new file with mode: 0755]
t/t6002-rev-list-bisect.sh
t/t6003-rev-list-topo-order.sh
t/t6006-rev-list-format.sh
t/t6012-rev-list-simplify.sh
t/t6019-rev-list-ancestry-path.sh
t/t6021-merge-criss-cross.sh
t/t6030-bisect-porcelain.sh
t/t6035-merge-dir-to-symlink.sh
t/t6111-rev-list-treesame.sh [new file with mode: 0755]
t/t6120-describe.sh
t/t7001-mv.sh
t/t7011-skip-worktree-reading.sh
t/t7061-wtstatus-ignore.sh
t/t7102-reset.sh
t/t7300-clean.sh
t/t7301-clean-interactive.sh [new file with mode: 0755]
t/t7400-submodule-basic.sh
t/t7401-submodule-summary.sh
t/t7403-submodule-sync.sh
t/t7406-submodule-update.sh
t/t7407-submodule-foreach.sh
t/t7409-submodule-detached-worktree.sh
t/t7500-commit.sh
t/t7501-commit.sh
t/t7502-commit.sh
t/t7508-status.sh
t/t7512-status-help.sh
t/t7600-merge.sh
t/t7601-merge-pull-config.sh
t/t7607-merge-overwrite.sh
t/t7800-difftool.sh
t/t8001-annotate.sh
t/t8002-blame.sh
t/t8003-blame-corner-cases.sh
t/t8006-blame-textconv.sh
t/t8007-cat-file-textconv.sh
t/t9001-send-email.sh
t/t9020-remote-svn.sh
t/t9112-git-svn-md5less-file.sh
t/t9147-git-svn-include-paths.sh [new file with mode: 0755]
t/t9161-git-svn-mergeinfo-push.sh
t/t9167-git-svn-cmd-branch-subproject.sh [new file with mode: 0755]
t/t9350-fast-export.sh
t/t9402-git-cvsserver-refs.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9501-gitweb-standalone-http-status.sh
t/t9801-git-p4-branch.sh
t/t9802-git-p4-filetype.sh
t/t9902-completion.sh
t/t9903-bash-prompt.sh
t/test-lib-functions.sh
t/test-lib.sh
t/valgrind/analyze.sh
t/valgrind/valgrind.sh
templates/hooks--pre-commit.sample
templates/hooks--pre-push.sample [changed mode: 0644->0755]
test-chmtime.c
test-dump-cache-tree.c
test-index-version.c
test-mergesort.c
test-parse-options.c
test-path-utils.c
test-prio-queue.c [new file with mode: 0644]
test-read-cache.c [new file with mode: 0644]
test-subprocess.c
thread-utils.c
trace.c
transport-helper.c
transport.c
transport.h
tree-walk.c
tree-walk.h
tree.c
unpack-trees.c
unpack-trees.h
upload-pack.c
utf8.c
utf8.h
wildmatch.c
wrap-for-bin.sh
wrapper.c
wt-status.c
wt-status.h
xdiff/xdiff.h
xdiff/xdiffi.c
xdiff/xdiffi.h
xdiff/xemit.c
xdiff/xemit.h
xdiff/xutils.c
xdiff/xutils.h
zlib.c

index 6669bf0c6c9a0b42f74e2ed189350a6a9f11f17d..6b1fd1bfb060c438d7b70684612cef08fc1a5859 100644 (file)
@@ -23,6 +23,7 @@
 /git-cat-file
 /git-check-attr
 /git-check-ignore
+/git-check-mailmap
 /git-check-ref-format
 /git-checkout
 /git-checkout-index
 /git-remote-ftps
 /git-remote-fd
 /git-remote-ext
+/git-remote-testgit
 /git-remote-testpy
 /git-remote-testsvn
 /git-repack
 /test-mktemp
 /test-parse-options
 /test-path-utils
+/test-prio-queue
+/test-read-cache
 /test-regex
 /test-revision-walking
 /test-run-command
 /cscope*
 *.obj
 *.lib
+*.res
 *.sln
 *.suo
 *.ncb
index 48d7acf013c611d4885cddffdf516bb5d0ea5a86..1c1f5ec6bf83ca9dd27f7dacaa05b722202e115b 100644 (file)
--- a/.mailmap
+++ b/.mailmap
 # same person appearing not to be so.
 #
 
+<nico@fluxnic.net> <nico@cam.org>
+Alejandro R. Sedeño <asedeno@MIT.EDU> <asedeno@mit.edu>
 Alex Bennée <kernel-hacker@bennee.com>
+Alex Riesen <raa.lkml@gmail.com> <fork0@t-online.de>
+Alex Riesen <raa.lkml@gmail.com> <raa@limbo.localdomain>
+Alex Riesen <raa.lkml@gmail.com> <raa@steel.home>
+Alex Vandiver <alex@chmrr.net> <alexmv@MIT.EDU>
 Alexander Gavrilov <angavrilov@gmail.com>
+Alexey Shumkin <alex.crezoff@gmail.com> <zapped@mail.ru>
+Alexey Shumkin <alex.crezoff@gmail.com> <Alex.Crezoff@gmail.com>
+Anders Kaseorg <andersk@MIT.EDU> <andersk@ksplice.com>
+Anders Kaseorg <andersk@MIT.EDU> <andersk@mit.edu>
 Aneesh Kumar K.V <aneesh.kumar@gmail.com>
-Brian M. Carlson <sandals@crustytoothpaste.ath.cx>
+Amos Waterland <apw@debian.org> <apw@rossby.metr.ou.edu>
+Amos Waterland <apw@debian.org> <apw@us.ibm.com>
+Ben Walton <bdwalton@gmail.com> <bwalton@artsci.utoronto.ca>
+Benoit Sigoure <tsunanet@gmail.com> <tsuna@lrde.epita.fr>
+Bernt Hansen <bernt@norang.ca> <bernt@alumni.uwaterloo.ca>
+Brandon Casey <drafnel@gmail.com> <casey@nrlssc.navy.mil>
+brian m. carlson <sandals@crustytoothpaste.ath.cx> Brian M. Carlson <sandals@crustytoothpaste.ath.cx>
+brian m. carlson <sandals@crustytoothpaste.ath.cx> <sandals@crustytoothpaste.net>
+Bryan Larsen <bryan@larsen.st> <bryan.larsen@gmail.com>
+Bryan Larsen <bryan@larsen.st> <bryanlarsen@yahoo.com>
 Cheng Renquan <crquan@gmail.com>
 Chris Shoemaker <c.shoemaker@cox.net>
+Chris Wright <chrisw@sous-sol.org> <chrisw@osdl.org>
+Cord Seele <cowose@gmail.com> <cowose@googlemail.com>
+Christian Stimming <stimming@tuhh.de> <chs@ckiste.goetheallee>
+Csaba Henk <csaba@gluster.com> <csaba@lowlife.hu>
 Dan Johnson <computerdruid@gmail.com>
-Dana L. How <danahow@gmail.com>
-Dana L. How <how@deathvalley.cswitch.com>
+Dana L. How <danahow@gmail.com> <how@deathvalley.cswitch.com>
+Dana L. How <danahow@gmail.com> Dana How
 Daniel Barkalow <barkalow@iabervon.org>
+Daniel Trstenjak <daniel.trstenjak@gmail.com> <daniel.trstenjak@online.de>
+Daniel Trstenjak <daniel.trstenjak@gmail.com> <trsten@science-computing.de>
+David Brown <git@davidb.org> <davidb@quicinc.com>
 David D. Kilzer <ddkilzer@kilzer.net>
 David Kågedal <davidk@lysator.liu.se>
+David Reiss <dreiss@facebook.com> <dreiss@dreiss-vmware.(none)>
 David S. Miller <davem@davemloft.net>
 Deskin Miller <deskinm@umich.edu>
 Dirk Süsserott <newsletter@dirk.my1.cc>
+Eric Blake <eblake@redhat.com> <ebb9@byu.net>
+Eric Hanchrow <eric.hanchrow@gmail.com> <offby1@blarg.net>
 Eric S. Raymond <esr@thyrsus.com>
 Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com>
-Fredrik Kuivinen <freku045@student.liu.se>
+Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com> <eyvind-git@orakel.ntnu.no>
+Florian Achleitner <florian.achleitner.2.6.31@gmail.com> <florian.achleitner2.6.31@gmail.com>
+Franck Bui-Huu <vagabon.xyz@gmail.com> <fbuihuu@gmail.com>
+Frank Lichtenheld <frank@lichtenheld.de> <djpig@debian.org>
+Frank Lichtenheld <frank@lichtenheld.de> <flichtenheld@astaro.com>
+Fredrik Kuivinen <frekui@gmail.com> <freku045@student.liu.se>
 Frédéric Heitzmann <frederic.heitzmann@gmail.com>
-H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
-H. Peter Anvin <hpa@tazenda.sc.orionmulti.com>
-H. Peter Anvin <hpa@trantor.hos.anvin.org>
+Garry Dolley <gdolley@ucla.edu> <gdolley@arpnetworks.com>
+Greg Price <price@mit.edu> <price@MIT.EDU>
+Greg Price <price@mit.edu> <price@ksplice.com>
+Heiko Voigt <hvoigt@hvoigt.net> <git-list@hvoigt.net>
+H. Merijn Brand <h.m.brand@xs4all.nl> H.Merijn Brand <h.m.brand@xs4all.nl>
+H. Peter Anvin <hpa@zytor.com> <hpa@bonde.sc.orionmulti.com>
+H. Peter Anvin <hpa@zytor.com> <hpa@smyrno.hos.anvin.org>
+H. Peter Anvin <hpa@zytor.com> <hpa@tazenda.sc.orionmulti.com>
+H. Peter Anvin <hpa@zytor.com> <hpa@trantor.hos.anvin.org>
+Han-Wen Nienhuys <hanwen@google.com> Han-Wen Nienhuys <hanwen@xs4all.nl>
 Horst H. von Brand <vonbrand@inf.utfsm.cl>
-İsmail Dönmez <ismail@pardus.org.tr>
+J. Bruce Fields <bfields@citi.umich.edu> <bfields@fieldses.org>
+J. Bruce Fields <bfields@citi.umich.edu> <bfields@pig.linuxdev.us.dell.com>
+J. Bruce Fields <bfields@citi.umich.edu> <bfields@puzzle.fieldses.org>
 Jakub Narębski <jnareb@gmail.com>
-Jay Soffian <jaysoffian+git@gmail.com>
+James Y Knight <jknight@itasoftware.com> <foom@fuhm.net>
+# The 2 following authors are probably the same person,
+# but both emails bounce.
+Jason McMullan <jason.mcmullan@timesys.com>
+Jason McMullan <mcmullan@netapp.com>
+Jason Riedy <ejr@eecs.berkeley.edu> <ejr@EECS.Berkeley.EDU>
+Jason Riedy <ejr@eecs.berkeley.edu> <ejr@cs.berkeley.edu>
+Jay Soffian <jaysoffian@gmail.com> <jaysoffian+git@gmail.com>
 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>
+Jim Meyering <jim@meyering.net> <meyering@redhat.com>
 Joachim Berdal Haga <cjhaga@fys.uio.no>
-Johannes Sixt <j6t@kdbg.org> <johannes.sixt@telecom.at>
-Johannes Sixt <j6t@kdbg.org> <j.sixt@viscovery.net>
+Johannes Schindelin <Johannes.Schindelin@gmx.de> <johannes.schindelin@gmx.de>
 Johannes Sixt <j6t@kdbg.org> <J.Sixt@eudaptics.com>
-Jon Loeliger <jdl@freescale.com>
-Jon Seymour <jon@blackcubes.dyndns.org>
-Jonathan Nieder <jrnieder@uchicago.edu>
+Johannes Sixt <j6t@kdbg.org> <j.sixt@viscovery.net>
+Johannes Sixt <j6t@kdbg.org> <johannes.sixt@telecom.at>
+John 'Warthog9' Hawley <warthog9@kernel.org> <warthog9@eaglescrag.net>
+Jon Loeliger <jdl@jdl.com> <jdl@freescale.com>
+Jon Loeliger <jdl@jdl.com> <jdl@freescale.org>
+Jon Seymour <jon.seymour@gmail.com> <jon@blackcubes.dyndns.org>
+Jonathan Nieder <jrnieder@gmail.com> <jrnieder@uchicago.edu>
+Jonathan del Strother <jon.delStrother@bestbefore.tv> <maillist@steelskies.com>
+Josh Triplett <josh@joshtriplett.org> <josh@freedesktop.org>
+Josh Triplett <josh@joshtriplett.org> <josht@us.ibm.com>
+Julian Phillips <julian@quantumfyre.co.uk> <jp3@quantumfyre.co.uk>
 Junio C Hamano <gitster@pobox.com> <gitster@pobox.com>
-Junio C Hamano <gitster@pobox.com> <junio@pobox.com>
-Junio C Hamano <gitster@pobox.com> <junio@twinsun.com>
-Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com>
 Junio C Hamano <gitster@pobox.com> <junio@hera.kernel.org>
 Junio C Hamano <gitster@pobox.com> <junio@kernel.org>
+Junio C Hamano <gitster@pobox.com> <junio@pobox.com>
+Junio C Hamano <gitster@pobox.com> <junio@twinsun.com>
 Junio C Hamano <gitster@pobox.com> <junkio@cox.net>
-Karl Hasselström <kha@treskal.com>
-Kevin Leung <kevinlsk@gmail.com>
+Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com>
+Karl Wiberg <kha@treskal.com> Karl  Hasselström
+Karl Wiberg <kha@treskal.com> <kha@yoghurt.hemma.treskal.com>
+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)>
+Keith Cascio <keith@CS.UCLA.EDU> <keith@cs.ucla.edu>
 Kent Engstrom <kent@lysator.liu.se>
+Kevin Leung <kevinlsk@gmail.com>
+Kirill Smelkov <kirr@navytux.spb.ru> <kirr@landau.phys.spbu.ru>
+Kirill Smelkov <kirr@navytux.spb.ru> <kirr@mns.spb.ru>
+Knut Franke <Knut.Franke@gmx.de> <k.franke@science-computing.de>
 Lars Doelle <lars.doelle@on-line ! de>
 Lars Doelle <lars.doelle@on-line.de>
+Lars Noschinski <lars@public.noschinski.de> <lars.noschinski@rwth-aachen.de>
 Li Hong <leehong@pku.edu.cn>
-Linus Torvalds <torvalds@linux-foundation.org> <torvalds@woody.linux-foundation.org>
-Linus Torvalds <torvalds@linux-foundation.org> <torvalds@osdl.org>
-Linus Torvalds <torvalds@linux-foundation.org> <torvalds@g5.osdl.org>
 Linus Torvalds <torvalds@linux-foundation.org> <torvalds@evo.osdl.org>
-Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org>
+Linus Torvalds <torvalds@linux-foundation.org> <torvalds@g5.osdl.org>
+Linus Torvalds <torvalds@linux-foundation.org> <torvalds@osdl.org>
 Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org.(none)>
-Lukas Sandström <lukass@etek.chalmers.se>
+Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org>
+Linus Torvalds <torvalds@linux-foundation.org> <torvalds@woody.linux-foundation.org>
+Lukas Sandström <luksan@gmail.com> <lukass@etek.chalmers.se>
+Marc Khouzam <marc.khouzam@ericsson.com> <marc.khouzam@gmail.com>
 Marc-André Lureau <marcandre.lureau@gmail.com>
+Marco Costalba <mcostalba@gmail.com> <mcostalba@yahoo.it>
+Mark Levedahl <mdl123@verizon.net> <mlevedahl@gmail.com>
 Mark Rada <marada@uwaterloo.ca>
 Martin Langhoff <martin@laptop.org> <martin@catalyst.net.nz>
 Martin von Zweigbergk <martinvonz@gmail.com> <martin.von.zweigbergk@gmail.com>
+Matt Draisey <matt@draisey.ca> <mattdraisey@sympatico.ca>
+Matt Kraai <kraai@ftbfs.org> <matt.kraai@amo.abbott.com>
+Matt McCutchen <matt@mattmccutchen.net> <hashproduct@gmail.com>
+Matthias Kestenholz <matthias@spinlock.ch> <mk@spinlock.ch>
+Matthias Urlichs <matthias@urlichs.de> <smurf@kiste.(none)>
+Matthias Urlichs <matthias@urlichs.de> <smurf@smurf.noris.de>
 Michael Coleman <tutufan@gmail.com>
 Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
+Michael S. Tsirkin <mst@kernel.org> <mst@redhat.com>
+Michael S. Tsirkin <mst@kernel.org> <mst@mellanox.co.il>
+Michael S. Tsirkin <mst@kernel.org> <mst@dev.mellanox.co.il>
 Michael W. Olson <mwolson@gnu.org>
+Michael Witten <mfwitten@gmail.com> <mfwitten@MIT.EDU>
+Michael Witten <mfwitten@gmail.com> <mfwitten@mit.edu>
+Michal Rokos <michal.rokos@nextsoft.cz> <rokos@nextsoft.cz>
 Michele Ballabio <barra_cuda@katamail.com>
-Nanako Shiraishi <nanako3@bluebottle.com>
+Miklos Vajna <vmiklos@frugalware.org> <vmiklos@suse.cz>
+Namhyung Kim <namhyung@gmail.com> <namhyung.kim@lge.com>
+Namhyung Kim <namhyung@gmail.com> <namhyung@kernel.org>
+Nanako Shiraishi <nanako3@lavabit.com> <nanako3@bluebottle.com>
 Nanako Shiraishi <nanako3@lavabit.com>
+Nelson Elhage <nelhage@mit.edu> <nelhage@MIT.EDU>
+Nelson Elhage <nelhage@mit.edu> <nelhage@ksplice.com>
 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
-<nico@fluxnic.net> <nico@cam.org>
-Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se>
+Nick Stokoe <nick@noodlefactory.co.uk> Nick Woolley <nick@noodlefactory.co.uk>
+Nick Stokoe <nick@noodlefactory.co.uk> Nick Woolley <nickwoolley@yahoo.co.uk>
+Nicolas Morey-Chaisemartin <devel-git@morey-chaisemartin.com> <nicolas.morey@free.fr>
+Nicolas Morey-Chaisemartin <devel-git@morey-chaisemartin.com> <nmorey@kalray.eu>
+Nicolas Sebrecht <nicolas.s.dev@gmx.fr> <ni.s@laposte.net>
+Paolo Bonzini <bonzini@gnu.org> <paolo.bonzini@lu.unisi.ch>
+Pascal Obry <pascal@obry.net> <pascal.obry@gmail.com>
+Pascal Obry <pascal@obry.net> <pascal.obry@wanadoo.fr>
+Pat Notz <patnotz@gmail.com> <pknotz@sandia.gov>
+Paul Mackerras <paulus@samba.org> <paulus@dorrigo.(none)>
+Paul Mackerras <paulus@samba.org> <paulus@pogo.(none)>
+Peter Baumann <waste.manager@gmx.de> <Peter.B.Baumann@stud.informatik.uni-erlangen.de>
+Peter Baumann <waste.manager@gmx.de> <siprbaum@stud.informatik.uni-erlangen.de>
 Peter Krefting <peter@softwolves.pp.se> <peter@softwolves.pp.se>
+Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se>
 Petr Baudis <pasky@ucw.cz> <pasky@suse.cz>
+Petr Baudis <pasky@ucw.cz> <xpasky@machine>
+Phil Hord <hordp@cisco.com> <phil.hord@gmail.com>
+Philip Jägenstedt <philip@foolip.org> <philip.jagenstedt@gmail.com>
+Philipp A. Hartmann <pah@qo.cx> <ph@sorgh.de>
 Philippe Bruhat <book@cpan.org>
 Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>
 Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
-René Scharfe <rene.scharfe@lsrfire.ath.cx>
+René Scharfe <l.s.r@web.de> <rene.scharfe@lsrfire.ath.cx>
 Robert Fitzsimons <robfitz@273k.net>
+Robert Shearman <robertshearman@gmail.com> <rob@codeweavers.com>
 Robert Zeh <robert.a.zeh@gmail.com>
-Sam Vilain <sam@vilain.net>
-Santi Béjar <sbejar@gmail.com>
+Robin Rosenberg <robin.rosenberg@dewire.com> <robin.rosenberg.lists@dewire.com>
+Rutger Nijlunsing <rutger.nijlunsing@gmail.com> <rutger@nospam.com>
+Rutger Nijlunsing <rutger.nijlunsing@gmail.com> <git@tux.tmfweb.nl>
+Ryan Anderson <ryan@michonline.com> <rda@google.com>
+Salikh Zakirov <salikh.zakirov@gmail.com> <Salikh.Zakirov@Intel.com>
+Sam Vilain <sam@vilain.net> <sam.vilain@catalyst.net.nz>
+Sam Vilain <sam@vilain.net> sam@vilain.net
+Santi Béjar <santi@agolina.net> <sbejar@gmail.com>
 Sean Estabrooks <seanlkml@sympatico.ca>
+Sebastian Schuberth <sschuberth@gmail.com> <sschuberth@visageimaging.com>
+Seth Falcon <seth@userprimary.net> <sfalcon@fhcrc.org>
 Shawn O. Pearce <spearce@spearce.org>
-Steven Grimm <koreth@midwinter.com>
+Simon Hausmann <hausmann@kde.org> <simon@lst.de>
+Simon Hausmann <hausmann@kde.org> <shausman@trolltech.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>
+Štěpán Němec <stepnem@gmail.com> <stepan.nemec@gmail.com>
+Stephen Boyd <bebarino@gmail.com> <sboyd@codeaurora.org>
+Steven Drake <sdrake@xnet.co.nz> <sdrake@ihug.co.nz>
+Steven Grimm <koreth@midwinter.com> <sgrimm@sgrimm-mbp.local>
+Steven Grimm <koreth@midwinter.com> koreth@midwinter.com
+Steven Walter <stevenrwalter@gmail.com> <swalter@lexmark.com>
+Steven Walter <stevenrwalter@gmail.com> <swalter@lpdev.prtdev.lexmark.com>
+Sven Verdoolaege <skimo@kotnet.org> <Sven.Verdoolaege@cs.kuleuven.ac.be>
+Sven Verdoolaege <skimo@kotnet.org> <skimo@liacs.nl>
 Tay Ray Chuan <rctay89@gmail.com>
+Ted Percival <ted@midg3t.net> <ted.percival@quest.com>
 Theodore Ts'o <tytso@mit.edu>
+Thomas Ackermann <th.acker@arcor.de> <th.acker66@arcor.de>
 Thomas Rast <trast@inf.ethz.ch> <trast@student.ethz.ch>
+Timo Hirvonen <tihirvon@gmail.com> <tihirvon@ee.oulu.fi>
+Toby Allsopp <Toby.Allsopp@navman.co.nz> <toby.allsopp@navman.co.nz>
+Tom Grennan <tmgrennan@gmail.com> <tgrennan@redback.com>
+Tommi Virtanen <tv@debian.org> <tv@eagain.net>
+Tommi Virtanen <tv@debian.org> <tv@inoi.fi>
+Tommy Thorn <tommy-git@thorn.ws> <tt1729@yahoo.com>
 Tony Luck <tony.luck@intel.com>
-Uwe Kleine-König <Uwe_Zeisberger@digi.com>
-Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
-Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
-Uwe Kleine-König <uzeisberger@io.fsforth.de>
-Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
-Ville Skyttä <scop@xemacs.org>
+Tor Arne Vestbø <torarnv@gmail.com> <tavestbo@trolltech.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>
+Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <ukleinek@informatik.uni-freiburg.de>
+Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <uzeisberger@io.fsforth.de>
+Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <zeisberg@informatik.uni-freiburg.de>
+Ville Skyttä <ville.skytta@iki.fi> <scop@xemacs.org>
 Vitaly "_Vi" Shukela <public_vi@tut.by>
+W. Trevor King <wking@tremily.us> <wking@drexel.edu>
 William Pursell <bill.pursell@gmail.com>
+YONETANI Tomokazu <y0n3t4n1@gmail.com> <qhwt+git@les.ath.cx>
+YONETANI Tomokazu <y0n3t4n1@gmail.com> <y0netan1@dragonflybsd.org>
 YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+# the two anonymous contributors are different persons:
 anonymous <linux@horizon.com>
 anonymous <linux@horizon.net>
+İsmail Dönmez <ismail@pardus.org.tr>
index 7e4d5716a62bf59e8f072163ebac3064dad323ae..559d5f9ebf6fefe57b403327ed3e06942d0b822b 100644 (file)
@@ -237,8 +237,10 @@ For Python scripts:
 
 Writing Documentation:
 
- Most (if not all) of the documentation pages are written in AsciiDoc
- and processed into HTML output and manpages.
+ Most (if not all) of the documentation pages are written in the
+ AsciiDoc format in *.txt files (e.g. Documentation/git.txt), and
+ processed into HTML and manpages (e.g. git.html and git.1 in the
+ same directory).
 
  Every user-visible change should be reflected in the documentation.
  The same general rule as for code applies -- imitate the existing
index 62dbd9ac7c93e8df4359b00b7c3a59b614bd8d57..0cfdc36b44ee51d827136c2bdd75c3d977f03742 100644 (file)
@@ -31,11 +31,11 @@ MAN7_TXT += gittutorial.txt
 MAN7_TXT += gitworkflows.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
-MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
-MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
+MAN_XML = $(patsubst %.txt,%.xml,$(MAN_TXT))
+MAN_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
 
 OBSOLETE_HTML = git-remote-helpers.html
-DOC_HTML=$(MAN_HTML) $(OBSOLETE_HTML)
+DOC_HTML = $(MAN_HTML) $(OBSOLETE_HTML)
 
 ARTICLES = howto-index
 ARTICLES += everyday
@@ -74,35 +74,35 @@ SP_ARTICLES += technical/api-index
 
 DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
 
-DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
-DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT))
-DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
-
-prefix?=$(HOME)
-bindir?=$(prefix)/bin
-htmldir?=$(prefix)/share/doc/git-doc
-pdfdir?=$(prefix)/share/doc/git-doc
-mandir?=$(prefix)/share/man
-man1dir=$(mandir)/man1
-man5dir=$(mandir)/man5
-man7dir=$(mandir)/man7
-# DESTDIR=
+DOC_MAN1 = $(patsubst %.txt,%.1,$(MAN1_TXT))
+DOC_MAN5 = $(patsubst %.txt,%.5,$(MAN5_TXT))
+DOC_MAN7 = $(patsubst %.txt,%.7,$(MAN7_TXT))
+
+prefix ?= $(HOME)
+bindir ?= $(prefix)/bin
+htmldir ?= $(prefix)/share/doc/git-doc
+infodir ?= $(prefix)/share/info
+pdfdir ?= $(prefix)/share/doc/git-doc
+mandir ?= $(prefix)/share/man
+man1dir = $(mandir)/man1
+man5dir = $(mandir)/man5
+man7dir = $(mandir)/man7
+# DESTDIR =
 
 ASCIIDOC = asciidoc
 ASCIIDOC_EXTRA =
 MANPAGE_XSL = manpage-normal.xsl
 XMLTO = xmlto
 XMLTO_EXTRA =
-INSTALL?=install
+INSTALL ?= install
 RM ?= rm -f
 MAN_REPO = ../../git-manpages
 HTML_REPO = ../../git-htmldocs
 
-infodir?=$(prefix)/share/info
-MAKEINFO=makeinfo
-INSTALL_INFO=install-info
-DOCBOOK2X_TEXI=docbook2x-texi
-DBLATEX=dblatex
+MAKEINFO = makeinfo
+INSTALL_INFO = install-info
+DOCBOOK2X_TEXI = docbook2x-texi
+DBLATEX = dblatex
 ifndef PERL_PATH
        PERL_PATH = /usr/bin/perl
 endif
index 806a965a1b1cc6c82e26d2c4d5ab54e5ae2add38..be68524cff388af3540db1f3237c417da3b84a35 100644 (file)
@@ -14,7 +14,7 @@ Fixes since v1.7.10
    not exclude them and tried to apply funny patches only to fail.
 
  * "git blame" started missing quite a few changes from the origin
-   since we stopped using the diff minimalization by default in v1.7.2
+   since we stopped using the diff minimization by default in v1.7.2
    era.
 
  * When PATH contains an unreadable directory, alias expansion code
index cf3f455ced07b2d84c9acb4e13313aec864a3b67..7796df3fe48295be33238a3891b7b4cb8857ffe2 100644 (file)
@@ -5,7 +5,7 @@ Fixes since v1.7.5.3
 --------------------
 
  * The single-key mode of "git add -p" was easily fooled into thinking
-   that it was told to add everthing ('a') when up-arrow was pressed by
+   that it was told to add everything ('a') when up-arrow was pressed by
    mistake.
 
  * Setting a git command that uses custom configuration via "-c var=val"
index e74f4ef1efd33383e24e5b45629687ad7b6f6289..b9c66aa1b79912922f4e66ea3b75141b599b3f51 100644 (file)
@@ -12,11 +12,11 @@ Fixes since v1.7.8.1
  * The configuration file parser used for sizes (e.g. bigFileThreshold)
    did not correctly interpret 'g' suffix.
 
- * The replacement implemention for snprintf used on platforms with
+ * The replacement implementation for snprintf used on platforms with
    native snprintf that is broken did not use va_copy correctly.
 
  * LF-to-CRLF streaming filter replaced all LF with CRLF, which might
-   be techinically correct but not friendly to people who are trying
+   be technically correct but not friendly to people who are trying
    to recover from earlier mistakes of using CRLF in the repository
    data in the first place. It now refrains from doing so for LF that
    follows a CR.
index b4d90bba0f6c761e61321bb9df8b7a8b3f725272..249311361e001d7b70ae52285520f060e2e99256 100644 (file)
@@ -9,7 +9,7 @@ Updates since v1.7.7
  * Updates to bash completion scripts.
 
  * The build procedure has been taught to take advantage of computed
-   dependency automatically when the complier supports it.
+   dependency automatically when the compiler supports it.
 
  * The date parser now accepts timezone designators that lack minutes
    part and also has a colon between "hh:mm".
@@ -31,7 +31,7 @@ Updates since v1.7.7
  * Variants of "git cherry-pick" and "git revert" that take multiple
    commits learned to "--continue" and "--abort".
 
- * "git daemon" gives more human readble error messages to clients
+ * "git daemon" gives more human readable error messages to clients
    using ERR packets when appropriate.
 
  * Errors at the network layer is logged by "git daemon".
index 1354ad03f5e65cf85ec4c1e2b68c6152c3f295b8..769a6fc06c9e87818612c5810b7e0d37fe8e0a1a 100644 (file)
@@ -49,7 +49,7 @@ Fixes since v1.8.2
    common prefix and suffix between the two filenames overlapped.
 
  * "git submodule update", when recursed into sub-submodules, did not
-   acccumulate the prefix paths.
+   accumulate the prefix paths.
 
  * "git am $maildir/" applied messages in an unexpected order; sort
    filenames read from the maildir/ in a way that is more likely to
diff --git a/Documentation/RelNotes/1.8.2.2.txt b/Documentation/RelNotes/1.8.2.2.txt
new file mode 100644 (file)
index 0000000..708df1a
--- /dev/null
@@ -0,0 +1,61 @@
+Git v1.8.2.2 Release Notes
+==========================
+
+Fixes since v1.8.2.1
+--------------------
+
+ * Zsh completion forgot that '%' character used to signal untracked
+   files needs to be escaped with another '%'.
+
+ * A commit object whose author or committer ident are malformed
+   crashed some code that trusted that a name, an email and an
+   timestamp can always be found in it.
+
+ * The new core.commentchar configuration was not applied to a few
+   places.
+
+ * "git pull --rebase" did not pass "-v/-q" options to underlying
+   "git rebase".
+
+ * When receive-pack detects error in the pack header it received in
+   order to decide which of unpack-objects or index-pack to run, it
+   returned without closing the error stream, which led to a hang
+   sideband thread.
+
+ * "git diff --diff-algorithm=algo" was understood by the command line
+   parser, but "git diff --diff-algorithm algo" was not.
+
+ * "git log -S/-G" started paying attention to textconv filter, but
+   there was no way to disable this.  Make it honor --no-textconv
+   option.
+
+ * "git merge $(git rev-parse v1.8.2)" behaved quite differently from
+   "git merge v1.8.2", as if v1.8.2 were written as v1.8.2^0 and did
+   not pay much attention to the annotated tag payload.  Make the code
+   notice the type of the tag object, in addition to the dwim_ref()
+   based classification the current code uses (i.e. the name appears
+   in refs/tags/) to decide when to special case merging of tags.
+
+ * "git cherry-pick" and "git revert" can take more than one commit
+   on the command line these days, but it was not mentioned on the usage
+   text.
+
+ * Perl scripts like "git-svn" closed (not redirecting to /dev/null)
+   the standard error stream, which is not a very smart thing to do.
+   Later open may return file descriptor #2 for unrelated purpose, and
+   error reporting code may write into them.
+
+ * "git apply --whitespace=fix" was not prepared to see a line getting
+   longer after fixing whitespaces (e.g. tab-in-indent aka Python).
+
+ * "git diff/log --cc" did not work well with options that ignore
+   whitespace changes.
+
+ * Documentation on setting up a http server that requires
+   authentication only on the push but not fetch has been clarified.
+
+ * A few bugfixes to "git rerere" working on corner case merge
+   conflicts have been applied.
+
+ * "git bundle" did not like a bundle created using a commit without
+   any message as its one of the prerequisites.
diff --git a/Documentation/RelNotes/1.8.2.3.txt b/Documentation/RelNotes/1.8.2.3.txt
new file mode 100644 (file)
index 0000000..6139482
--- /dev/null
@@ -0,0 +1,19 @@
+Git v1.8.2.3 Release Notes
+==========================
+
+Fixes since v1.8.2.2
+--------------------
+
+ * "rev-list --stdin" and friends kept bogus pointers into the input
+   buffer around as human readable object names.  This was not a
+   huge problem but was exposed by a new change that uses these
+   names in error output.
+
+ * When "git difftool" drove "kdiff3", it mistakenly passed --auto
+   option that was meant while resolving merge conflicts.
+
+ * "git remote add" command did not diagnose extra command line
+   arguments as an error and silently ignored them.
+
+Also contains a handful of trivial code clean-ups, documentation
+updates, updates to the test suite, etc.
diff --git a/Documentation/RelNotes/1.8.3.1.txt b/Documentation/RelNotes/1.8.3.1.txt
new file mode 100644 (file)
index 0000000..fc3ea18
--- /dev/null
@@ -0,0 +1,14 @@
+Git v1.8.3.1 Release Notes
+========================
+
+Fixes since v1.8.3
+------------------
+
+ * When $HOME is misconfigured to point at an unreadable directory, we
+   used to complain and die. The check has been loosened.
+
+ * Handling of negative exclude pattern for directories "!dir" was
+   broken in the update to v1.8.3.
+
+Also contains a handful of trivial code clean-ups, documentation
+updates, updates to the test suite, etc.
diff --git a/Documentation/RelNotes/1.8.3.2.txt b/Documentation/RelNotes/1.8.3.2.txt
new file mode 100644 (file)
index 0000000..26ae142
--- /dev/null
@@ -0,0 +1,59 @@
+Git v1.8.3.2 Release Notes
+==========================
+
+Fixes since v1.8.3.1
+--------------------
+
+ * Cloning with "git clone --depth N" while fetch.fsckobjects (or
+   transfer.fsckobjects) is set to true did not tell the cut-off
+   points of the shallow history to the process that validates the
+   objects and the history received, causing the validation to fail.
+
+ * "git checkout foo" DWIMs the intended "upstream" and turns it into
+   "git checkout -t -b foo remotes/origin/foo". This codepath has been
+   updated to correctly take existing remote definitions into account.
+
+ * "git fetch" into a shallow repository from a repository that does
+   not know about the shallow boundary commits (e.g. a different fork
+   from the repository the current shallow repository was cloned from)
+   did not work correctly.
+
+ * "git subtree" (in contrib/) had one codepath with loose error
+   checks to lose data at the remote side.
+
+ * "git log --ancestry-path A...B" did not work as expected, as it did
+   not pay attention to the fact that the merge base between A and B
+   was the bottom of the range being specified.
+
+ * "git diff -c -p" was not showing a deleted line from a hunk when
+   another hunk immediately begins where the earlier one ends.
+
+ * "git merge @{-1}~22" was rewritten to "git merge frotz@{1}~22"
+   incorrectly when your previous branch was "frotz" (it should be
+   rewritten to "git merge frotz~22" instead).
+
+ * "git commit --allow-empty-message -m ''" should not start an
+   editor.
+
+ * "git push --[no-]verify" was not documented.
+
+ * An entry for "file://" scheme in the enumeration of URL types Git
+   can take in the HTML documentation was made into a clickable link
+   by mistake.
+
+ * zsh prompt script that borrowed from bash prompt script did not
+   work due to slight differences in array variable notation between
+   these two shells.
+
+ * The bash prompt code (in contrib/) displayed the name of the branch
+   being rebased when "rebase -i/-m/-p" modes are in use, but not the
+   plain vanilla "rebase".
+
+ * "git push $there HEAD:branch" did not resolve HEAD early enough, so
+   it was easy to flip it around while push is still going on and push
+   out a branch that the user did not originally intended when the
+   command was started.
+
+ * "difftool --dir-diff" did not copy back changes made by the
+   end-user in the diff tool backend to the working tree in some
+   cases.
diff --git a/Documentation/RelNotes/1.8.3.3.txt b/Documentation/RelNotes/1.8.3.3.txt
new file mode 100644 (file)
index 0000000..9ba4f4d
--- /dev/null
@@ -0,0 +1,47 @@
+Git v1.8.3.3 Release Notes
+==========================
+
+Fixes since v1.8.3.2
+--------------------
+
+ * "git apply" parsed patches that add new files, generated by programs
+   other than Git, incorrectly.  This is an old breakage in v1.7.11.
+
+ * Older cURL wanted piece of memory we call it with to be stable, but
+   we updated the auth material after handing it to a call.
+
+ * "git pull" into nothing trashed "local changes" that were in the
+   index.
+
+ * Many "git submodule" operations did not work on a submodule at a
+   path whose name is not in ASCII.
+
+ * "cherry-pick" had a small leak in its error codepath.
+
+ * Logic used by git-send-email to suppress cc mishandled names like
+   "A U. Thor" <author@example.xz>, where the human readable part
+   needs to be quoted (the user input may not have the double quotes
+   around the name, and comparison was done between quoted and
+   unquoted strings).  It also mishandled names that need RFC2047
+   quoting.
+
+ * "gitweb" forgot to clear a global variable $search_regexp upon each
+   request, mistakenly carrying over the previous search to a new one
+   when used as a persistent CGI.
+
+ * The wildmatch engine did not honor WM_CASEFOLD option correctly.
+
+ * "git log -c --follow $path" segfaulted upon hitting the commit that
+   renamed the $path being followed.
+
+ * When a reflog notation is used for implicit "current branch",
+   e.g. "git log @{u}", we did not say which branch and worse said
+   "branch ''" in the error messages.
+
+ * Mac OS X does not like to write(2) more than INT_MAX number of
+   bytes; work it around by chopping write(2) into smaller pieces.
+
+ * Newer MacOS X encourages the programs to compile and link with
+   their CommonCrypto, not with OpenSSL.
+
+Also contains various minor documentation updates.
diff --git a/Documentation/RelNotes/1.8.3.4.txt b/Documentation/RelNotes/1.8.3.4.txt
new file mode 100644 (file)
index 0000000..56f106e
--- /dev/null
@@ -0,0 +1,20 @@
+Git v1.8.3.4 Release Notes
+==========================
+
+This update is mostly to propagate documentation fixes and test
+updates from the master front back to the maintenance track.
+
+Fixes since v1.8.3.3
+--------------------
+
+ * The bisect log listed incorrect commits when bisection ends with
+   only skipped ones.
+
+ * The test coverage framework was left broken for some time.
+
+ * The test suite for HTTP transport did not run with Apache 2.4.
+
+ * "git diff" used to fail when core.safecrlf is set and the working
+   tree contents had mixed CRLF/LF line endings. Committing such a
+   content must be prohibited, but "git diff" should help the user to
+   locate and fix such problems without failing.
index 7799cec1dddbdd24121885360f77c88c8b97a746..ead568e7f157132a5030ca9052fc0b36707522f6 100644 (file)
@@ -8,37 +8,77 @@ When "git push [$there]" does not say what to push, we have used the
 traditional "matching" semantics so far (all your branches were sent
 to the remote as long as there already are branches of the same name
 over there).  In Git 2.0, the default will change to the "simple"
-semantics that pushes the current branch to the branch with the same
-name, only when the current branch is set to integrate with that
-remote branch.  There is a user preference configuration variable
+semantics that pushes only the current branch to the branch with the same
+name, and only when the current branch is set to integrate with that
+remote branch.  Use the user preference configuration variable
 "push.default" to change this.  If you are an old-timer who is used
-to the "matching" semantics, you can set it to "matching" to keep the
-traditional behaviour.  If you want to live in the future early,
-you can set it to "simple" today without waiting for Git 2.0.
-
-When "git add -u" and "git add -A", that does not specify what paths
-to add on the command line is run from inside a subdirectory, these
-commands will operate on the entire tree in Git 2.0 for consistency
-with "git commit -a" and other commands. Because there will be no
-mechanism to make "git add -u" behave as if "git add -u .", it is
-important for those who are used to "git add -u" (without pathspec)
-updating the index only for paths in the current subdirectory to start
-training their fingers to explicitly say "git add -u ." when they mean
-it before Git 2.0 comes.  A warning is issued when these commands are
+to the "matching" semantics, you can set the variable to "matching"
+to keep the traditional behaviour.  If you want to live in the future
+early, you can set it to "simple" today without waiting for Git 2.0.
+
+When "git add -u" (and "git add -A") is run inside a subdirectory and
+does not specify which paths to add on the command line, it
+will operate on the entire tree in Git 2.0 for consistency
+with "git commit -a" and other commands.  There will be no
+mechanism to make plain "git add -u" behave like "git add -u .".
+Current users of "git add -u" (without a pathspec) should start
+training their fingers to explicitly say "git add -u ."
+before Git 2.0 comes.  A warning is issued when these commands are
 run without a pathspec and when you have local changes outside the
 current directory, because the behaviour in Git 2.0 will be different
 from today's version in such a situation.
 
+In Git 2.0, "git add <path>" will behave as "git add -A <path>", so
+that "git add dir/" will notice paths you removed from the directory
+and record the removal.  Versions before Git 2.0, including this
+release, will keep ignoring removals, but the users who rely on this
+behaviour are encouraged to start using "git add --ignore-removal <path>"
+now before 2.0 is released.
+
 
 Updates since v1.8.2
 --------------------
 
+Foreign interface
+
+ * remote-hg and remote-bzr helpers (in contrib/ since v1.8.2) have
+   been updated; especially, the latter has been done in an
+   accelerated schedule (read: we may not have merged to this release
+   if we were following the usual "cook sufficiently in next before
+   unleashing it to the world" workflow) in order to help Emacs folks,
+   whose primary SCM seems to be stagnating.
+
+
 UI, Workflows & Features
 
+ * A handful of updates applied to gitk, including an addition of
+   "revert" action, showing dates in tags in a nicer way, making
+   colors configurable, and support for -G'pickaxe' search.
+
+ * The prompt string generator (in contrib/completion/) learned to
+   show how many changes there are in total and how many have been
+   replayed during a "git rebase" session.
+
  * "git branch --vv" learned to paint the name of the branch it
    integrates with in a different color (color.branch.upstream,
    which defaults to blue).
 
+ * In a sparsely populated working tree, "git checkout <pathspec>" no
+   longer unmarks paths that match the given pathspec that were
+   originally ignored with "--sparse" (use --ignore-skip-worktree-bits
+   option to resurrect these paths out of the index if you really want
+   to).
+
+ * "git log --format" specifier learned %C(auto) token that tells Git
+   to use color when interpolating %d (decoration), %h (short commit
+   object name), etc. for terminal output.
+
+ * "git bisect" leaves the final outcome as a comment in its bisect
+   log file.
+
+ * "git clone --reference" can now refer to a gitfile "textual symlink"
+   that points at the real location of the repository.
+
  * "git count-objects" learned "--human-readable" aka "-H" option to
    show various large numbers in Ki/Mi/GiB scaled as necessary.
 
@@ -74,9 +114,10 @@ UI, Workflows & Features
    of erroneous inputs was suboptimal and has been improved.
 
  * When the interactive access to git-shell is not enabled, it issues
-   a message meant to help the system administrator to enable it.
-   An explicit way to help the end users who connect to the service by
-   issuing custom messages to refuse such an access has been added.
+   a message meant to help the system administrator to enable it.  An
+   explicit way has been added to issue custom messages to refuse an
+   access over the network to help the end users who connect to the
+   service expecting an interactive shell.
 
  * In addition to the case where the user edits the log message with
    the "e)dit" option of "am -i", replace the "Applying: this patch"
@@ -86,8 +127,8 @@ UI, Workflows & Features
  * "git status" suggests users to look into using --untracked=no option
    when it takes too long.
 
- * "git status" shows a bit more information to "git status" during a
-   rebase/bisect session.
+ * "git status" shows a bit more information during a rebase/bisect
+   session.
 
  * "git fetch" learned to fetch a commit at the tip of an unadvertised
    ref by specifying a raw object name from the command line when the
@@ -120,8 +161,8 @@ UI, Workflows & Features
 
  * "git mergetool" now feeds files to the "p4merge" backend in the
    order that matches the p4 convention, where "theirs" is usually
-   shown on the left side, which is the opposite from other backend
-   expects.
+   shown on the left side, which is the opposite from what other backends
+   expect.
 
  * "show/log" now honors gpg.program configuration just like other
    parts of the code that use GnuPG.
@@ -131,9 +172,9 @@ UI, Workflows & Features
 
  * "git difftool" allows the user to write into the temporary files
    being shown; if the user makes changes to the working tree at the
-   same time, one of the changes has to be lost in such a case, but it
-   tells the user what happened and refrains from overwriting the copy
-   in the working tree.
+   same time, it now refrains from overwriting the copy in the working
+   tree and leaves the temporary file so that changes can be merged
+   manually.
 
  * There was no good way to ask "I have a random string that came from
    outside world. I want to turn it into a 40-hex object name while
@@ -145,19 +186,24 @@ Performance, Internal Implementation, etc.
 
  * Updates for building under msvc.
 
+ * A handful of issues in the code that traverses the working tree to find
+   untracked and/or ignored files have been fixed, and the general
+   codepath involved in "status -u" and "clean" have been cleaned up
+   and optimized.
+
  * The stack footprint of some codepaths that access an object from a
    pack has been shrunk.
 
  * The logic to coalesce the same lines removed from the parents in
-   the output from "diff -c/--cc" has been updated, but with an O(n^2)
+   the output from "diff -c/--cc" has been updated, but with O(n^2)
    complexity, so this might turn out to be undesirable.
 
  * The code to enforce permission bits on files in $GIT_DIR/ for
-   shared repositories have been simplified.
+   shared repositories has been simplified.
 
- * A few codepaths knew how much data they need to put in the
-   hashtables they use upfront, but still started from a small table
-   repeatedly growing and rehashing.
+ * A few codepaths know how much data they need to put in the
+   hashtables they use when they start, but still began with small tables
+   and repeatedly grew and rehashed them.
 
  * The API to walk reflog entries from the latest to older, which was
    necessary for operations such as "git checkout -", was cumbersome
@@ -169,9 +215,9 @@ Performance, Internal Implementation, etc.
  * The pkt-line API, implementation and its callers have been cleaned
    up to make them more robust.
 
- * Cygwin port has a faster-but-lying lstat(2) emulation whose
+ * The Cygwin port has a faster-but-lying lstat(2) emulation whose
    incorrectness does not matter in practice except for a few
-   codepaths, and setting permission bits to directories is a codepath
+   codepaths, and setting permission bits on directories is a codepath
    that needs to use a more correct one.
 
  * "git checkout" had repeated pathspec matches on the same paths,
@@ -192,123 +238,126 @@ Unless otherwise noted, all the fixes since v1.8.2 in the maintenance
 track are contained in this release (see release notes to them for
 details).
 
+ * Recent versions of File::Temp (used by "git svn") started blowing
+   up when its tempfile sub is called as a class method; updated the
+   callsite to call it as a plain vanilla function to fix it.
+   (merge eafc2dd hb/git-pm-tempfile later to maint).
+
+ * Various subcommands of "git remote" simply ignored extraneous
+   command line arguments instead of diagnosing them as errors.
+
+ * When receive-pack detects an error in the pack header it received in
+   order to decide which of unpack-objects or index-pack to run, it
+   returned without closing the error stream, which led to a hung
+   sideband thread.
+
+ * Zsh completion forgot that the '%' character used to signal untracked
+   files needs to be escaped with another '%'.
+
+ * A commit object whose author or committer ident are malformed
+   crashed some code that trusted that a name, an email and a
+   timestamp can always be found in it.
+
  * When "upload-pack" fails while generating a pack in response to
-   "git fetch" (or "git clone"), the receiving side mistakenly said
-   there was a programming error to trigger the die handler
+   "git fetch" (or "git clone"), the receiving side had
+   a programming error that triggered the die handler
    recursively.
-   (merge 1ece66b jk/a-thread-only-dies-once later to maint).
 
- * "rev-list --stdin" and friends kept bogus pointers into input
-   buffer around as human readble object names.  This was not a huge
+ * "rev-list --stdin" and friends kept bogus pointers into the input
+   buffer around as human readable object names.  This was not a huge
    problem but was exposed by a new change that uses these names in
    error output.
-   (merge 70d26c6 tr/copy-revisions-from-stdin later to maint).
 
  * Smart-capable HTTP servers were not restricted via the
-   GIT_NAMESPACE mechanism when talking with commit-walker clients,
-   like they do when talking with smart HTTP clients.
+   GIT_NAMESPACE mechanism when talking with commit-walking clients,
+   like they are when talking with smart HTTP clients.
    (merge 6130f86 jk/http-dumb-namespaces later to maint).
 
  * "git merge-tree" did not omit a merge result that is identical to
-   "our" side in certain cases.
+   the "our" side in certain cases.
    (merge aacecc3 jk/merge-tree-added-identically later to maint).
 
- * Perl scripts like "git-svn" closed (not redirecting to /dev/null)
+ * Perl scripts like "git-svn" closed (instead of redirecting to /dev/null)
    the standard error stream, which is not a very smart thing to do.
-   Later open may return file descriptor #2 for unrelated purpose, and
-   error reporting code may write into them.
-   (merge a749c0b tr/perl-keep-stderr-open later to maint).
+   A later open may return file descriptor #2 for an unrelated purpose, and
+   error reporting code may write into it.
 
  * "git show-branch" was not prepared to show a very long run of
    ancestor operators e.g. foobar^2~2^2^2^2...^2~4 correctly.
-   (merge aaa07e3 jk/show-branch-strbuf later to maint).
 
  * "git diff --diff-algorithm algo" is also understood as "git diff
    --diff-algorithm=algo".
-   (merge 0895c6d jk/diff-algo-finishing-touches later to maint).
 
- * The new core.commentchar configuration was not applied to a few
+ * The new core.commentchar configuration was not applied in a few
    places.
-   (merge 89c3bbd rt/commentchar-fmt-merge-msg later to maint).
 
- * "git bundle" did not like a bundle created using a commit without
-   any message as its one of the prerequistes.
-   (merge 5446e33 lf/bundle-with-tip-wo-message later to maint).
+ * "git bundle" erroneously bailed out when parsing a valid bundle
+   containing a prerequisite commit without a commit message.
 
  * "git log -S/-G" started paying attention to textconv filter, but
-   there was no way to disable this.  Make it honor --no-textconv
+   there was no way to disable this.  Make it honor the --no-textconv
    option.
-   (merge 61690bf sr/log-SG-no-textconv later to maint).
 
- * When used with "-d temporary-directory" option, "git filter-branch"
+ * When used with the "-d temporary-directory" option, "git filter-branch"
    failed to come back to the original working tree to perform the
    final clean-up procedure.
-   (merge 9727601 jk/filter-branch-come-back-to-original later to maint).
 
  * "git merge $(git rev-parse v1.8.2)" behaved quite differently from
    "git merge v1.8.2", as if v1.8.2 were written as v1.8.2^0 and did
    not pay much attention to the annotated tag payload.  Make the code
    notice the type of the tag object, in addition to the dwim_ref()
    based classification the current code uses (i.e. the name appears
-   in refs/tags/) to decide when to special case merging of tags.
-   (merge a38d3d7 jc/merge-tag-object later to maint).
+   in refs/tags/) to decide when to special-case tag merging.
 
- * Fix 1.8.1.x regression that stopped matching "dir" (without
+ * Fix a 1.8.1.x regression that stopped matching "dir" (without a
    trailing slash) to a directory "dir".
-   (merge efa5f82 jc/directory-attrs-regression-fix later to maint-1.8.1).
 
  * "git apply --whitespace=fix" was not prepared to see a line getting
    longer after fixing whitespaces (e.g. tab-in-indent aka Python).
-   (merge 329b26e jc/apply-ws-fix-tab-in-indent later to maint-1.8.1).
 
  * The prompt string generator (in contrib/completion/) did not notice
    when we are in a middle of a "git revert" session.
-   (merge 3ee4452 rr/prompt-revert-head later to maint).
 
- * "submodule summary --summary-limit" option did not support
+ * "submodule summary --summary-limit" option did not support the
    "--option=value" form.
-   (merge 862ae6c rs/submodule-summary-limit later to maint).
 
  * "index-pack --fix-thin" used an uninitialized value to compute
-   delta depths of objects it appends to the resulting pack.
-   (merge 57165db jk/index-pack-correct-depth-fix later to maint).
+   the delta depths of objects it appends to the resulting pack.
 
- * "index-pack --verify-stat" used a few counters outside protection
-   of mutex, possibly showing incorrect numbers.
-   (merge 8f82aad nd/index-pack-threaded-fixes later to maint).
+ * "index-pack --verify-stat" used a few counters outside the protection
+   of a mutex, possibly showing incorrect numbers.
 
  * The code to keep track of what directory names are known to Git on
-   platforms with case insensitive filesystems can get confused upon a
-   hash collision between these pathnames and looped forever.
+   platforms with case insensitive filesystems could get confused upon a
+   hash collision between these pathnames and would loop forever.
 
- * Annotated tags outside refs/tags/ hierarchy were not advertised
-   correctly to the ls-remote and fetch with recent version of Git.
+ * Annotated tags outside the refs/tags/ hierarchy were not advertised
+   correctly to ls-remote and fetch with recent versions of Git.
 
- * Recent optimization broke shallow clones.
-   (merge f59de5d jk/peel-ref later to maint).
+ * Recent optimizations broke shallow clones.
 
  * "git cmd -- ':(top'" was not diagnosed as an invalid syntax, and
    instead the parser kept reading beyond the end of the string.
 
  * "git tag -f <tag>" always said "Updated tag '<tag>'" even when
-   creating a new tag (i.e. not overwriting nor updating).
+   creating a new tag (i.e. neither overwriting nor updating).
 
  * "git p4" did not behave well when the path to the root of the P4
    client was not its real path.
    (merge bbd8486 pw/p4-symlinked-root later to maint).
 
- * "git archive" reports a failure when asked to create an archive out
-   of an empty tree.  It would be more intuitive to give an empty
+ * "git archive" reported a failure when asked to create an archive out
+   of an empty tree.  It is more intuitive to give an empty
    archive back in such a case.
 
- * When "format-patch" quoted a non-ascii strings on the header files,
+ * When "format-patch" quoted a non-ascii string in header files,
    it incorrectly applied rfc2047 and chopped a single character in
-   the middle of it.
+   the middle of the string.
 
  * An aliased command spawned from a bare repository that does not say
-   it is bare with "core.bare = yes" is treated as non-bare by mistake.
+   it is bare with "core.bare = yes" was treated as non-bare by mistake.
 
- * In "git reflog expire", REACHABLE bit was not cleared from the
+ * In "git reflog expire", the REACHABLE bit was not cleared from the
    correct objects.
 
  * The logic used by "git diff -M --stat" to shorten the names of
@@ -316,9 +365,9 @@ details).
    common prefix and suffix between the two filenames overlapped.
 
  * The "--match=<pattern>" option of "git describe", when used with
-   "--all" to allow refs that are not annotated tags to be used as a
+   "--all" to allow refs that are not annotated tags to be a
    base of description, did not restrict the output from the command
-   to those that match the given pattern.
+   to those refs that match the given pattern.
 
  * Clarify in the documentation "what" gets pushed to "where" when the
    command line to "git push" does not say these explicitly.
@@ -326,7 +375,7 @@ details).
  * The "--color=<when>" argument to the commands in the diff family
    was described poorly.
 
- * The arguments given to pre-rebase hook were not documented.
+ * The arguments given to the pre-rebase hook were not documented.
 
  * The v4 index format was not documented.
 
@@ -344,7 +393,7 @@ details).
 
  * In the v1.8.0 era, we changed symbols that do not have to be global
    to file scope static, but a few functions in graph.c were used by
-   CGit from sideways bypassing the entry points of the API the
+   CGit sideways, bypassing the entry points of the API the
    in-tree users use.
 
  * "git update-index -h" did not do the usual "-h(elp)" thing.
@@ -357,30 +406,30 @@ details).
    $msg already ended with one.
 
  * The SSL peer verification done by "git imap-send" did not ask for
-   Server Name Indication (RFC 4366), failing to connect SSL/TLS
+   Server Name Indication (RFC 4366), failing to connect to SSL/TLS
    sites that serve multiple hostnames on a single IP.
 
  * perl/Git.pm::cat_blob slurped everything in core only to write it
    out to a file descriptor, which was not a very smart thing to do.
 
  * "git branch" did not bother to check nonsense command line
-   parameters and issue errors in many cases.
+   parameters.  It now issues errors in many cases.
 
- * Verification of signed tags were not done correctly when not in C
+ * Verification of signed tags was not done correctly when not in C
    or en/US locale.
 
  * Some platforms and users spell UTF-8 differently; retry with the
    most official "UTF-8" when the system does not understand the
-   user-supplied encoding name that are the common alternative
-   spellings of UTF-8.
+   user-supplied encoding name that is a common alternative
+   spelling of UTF-8.
 
- * When export-subst is used, "zip" output recorded incorrect
+ * When export-subst is used, "zip" output recorded an incorrect
    size of the file.
 
  * "git am $maildir/" applied messages in an unexpected order; sort
    filenames read from the maildir/ in a way that is more likely to
-   sort messages in the order the writing MUA meant to, by sorting
-   numeric segment in numeric order and non-numeric segment in
+   sort the messages in the order the writing MUA meant to, by sorting
+   numeric segments in numeric order and non-numeric segments in
    alphabetical order.
 
  * "git submodule update", when recursed into sub-submodules, did not
diff --git a/Documentation/RelNotes/1.8.4.1.txt b/Documentation/RelNotes/1.8.4.1.txt
new file mode 100644 (file)
index 0000000..3aa25a2
--- /dev/null
@@ -0,0 +1,71 @@
+Git v1.8.4.1 Release Notes
+========================
+
+Fixes since v1.8.4
+------------------
+
+ * Some old versions of bash do not grok some constructs like
+   'printf -v varname' which the prompt and completion code started
+   to use recently.  The completion and prompt scripts have been
+   adjusted to work better with these old versions of bash.
+
+ * In FreeBSD's and NetBSD's "sh", a return in a dot script in a
+   function returns from the function, not only in the dot script,
+   breaking "git rebase" on these platforms (regression introduced
+   in 1.8.4-rc1).
+
+ * "git rebase -i" and other scripted commands were feeding a
+   random, data dependant error message to 'echo' and expecting it
+   to come out literally.
+
+ * Setting the "submodule.<name>.path" variable to the empty
+   "true" caused the configuration parser to segfault.
+
+ * Output from "git log --full-diff -- <pathspec>" looked strange
+   because comparison was done with the previous ancestor that
+   touched the specified <pathspec>, causing the patches for paths
+   outside the pathspec to show more than the single commit has
+   changed.
+
+ * The auto-tag-following code in "git fetch" tries to reuse the
+   same transport twice when the serving end does not cooperate and
+   does not give tags that point to commits that are asked for as
+   part of the primary transfer.  Unfortunately, Git-aware transport
+   helper interface is not designed to be used more than once, hence
+   this did not work over smart-http transfer.  Fixed.
+
+ * Send a large request to read(2)/write(2) as a smaller but still
+   reasonably large chunks, which would improve the latency when the
+   operation needs to be killed and incidentally works around broken
+   64-bit systems that cannot take a 2GB write or read in one go.
+
+ * A ".mailmap" file that ends with an incomplete line, when read
+   from a blob, was not handled properly.
+
+ * The recent "short-cut clone connectivity check" topic broke a
+   shallow repository when a fetch operation tries to auto-follow
+   tags.
+
+ * When send-email comes up with an error message to die with upon
+   failure to start an SSL session, it tried to read the error
+   string from a wrong place.
+
+ * A call to xread() was used without a loop to cope with short
+   read in the codepath to stream large blobs to a pack.
+
+ * On platforms with fgetc() and friends defined as macros, the
+   configuration parser did not compile.
+
+ * New versions of MediaWiki introduced a new API for returning
+   more than 500 results in response to a query, which would cause
+   the MediaWiki remote helper to go into an infinite loop.
+
+ * Subversion's serf access method (the only one available in
+   Subversion 1.8) for http and https URLs in skelta mode tells its
+   caller to open multiple files at a time, which made "git svn
+   fetch" complain that "Temp file with moniker 'svn_delta' already
+   in use" instead of fetching.
+
+
+Also contains a handful of trivial code clean-ups, documentation
+updates, updates to the test suite, etc.
diff --git a/Documentation/RelNotes/1.8.4.2.txt b/Documentation/RelNotes/1.8.4.2.txt
new file mode 100644 (file)
index 0000000..ebe5e68
--- /dev/null
@@ -0,0 +1,50 @@
+Git v1.8.4.2 Release Notes
+========================
+
+Fixes since v1.8.4.1
+--------------------
+
+ * When running "fetch -q", a long silence while the sender side
+   computes the set of objects to send can be mistaken by proxies as
+   dropped connection.  The server side has been taught to send a
+   small empty messages to keep the connection alive.
+
+ * When the webserver responds with "405 Method Not Allowed", "git
+   http-backend" should tell the client what methods are allowed with
+   the "Allow" header.
+
+ * "git cvsserver" computed the permission mode bits incorrectly for
+   executable files.
+
+ * The implementation of "add -i" has a crippling code to work around
+   ActiveState Perl limitation but it by mistake also triggered on Git
+   for Windows where MSYS perl is used.
+
+ * We made sure that we notice the user-supplied GIT_DIR is actually a
+   gitfile, but did not do the same when the default ".git" is a
+   gitfile.
+
+ * When an object is not found after checking the packfiles and then
+   loose object directory, read_sha1_file() re-checks the packfiles to
+   prevent racing with a concurrent repacker; teach the same logic to
+   has_sha1_file().
+
+ * "git commit --author=$name", when $name is not in the canonical
+   "A. U. Thor <au.thor@example.xz>" format, looks for a matching name
+   from existing history, but did not consult mailmap to grab the
+   preferred author name.
+
+ * The commit object names in the insn sheet that was prepared at the
+   beginning of "rebase -i" session can become ambiguous as the
+   rebasing progresses and the repository gains more commits. Make
+   sure the internal record is kept with full 40-hex object names.
+
+ * "git rebase --preserve-merges" internally used the merge machinery
+   and as a side effect, left merge summary message in the log, but
+   when rebasing, there should not be a need for merge summary.
+
+ * "git rebase -i" forgot that the comment character can be
+   configurable while reading its insn sheet.
+
+Also contains a handful of trivial code clean-ups, documentation
+updates, updates to the test suite, etc.
diff --git a/Documentation/RelNotes/1.8.4.txt b/Documentation/RelNotes/1.8.4.txt
new file mode 100644 (file)
index 0000000..02f681b
--- /dev/null
@@ -0,0 +1,486 @@
+Git v1.8.4 Release Notes
+========================
+
+Backward compatibility notes (for Git 2.0)
+------------------------------------------
+
+When "git push [$there]" does not say what to push, we have used the
+traditional "matching" semantics so far (all your branches were sent
+to the remote as long as there already are branches of the same name
+over there).  In Git 2.0, the default will change to the "simple"
+semantics that pushes:
+
+ - only the current branch to the branch with the same name, and only
+   when the current branch is set to integrate with that remote
+   branch, if you are pushing to the same remote as you fetch from; or
+
+ - only the current branch to the branch with the same name, if you
+   are pushing to a remote that is not where you usually fetch from.
+
+Use the user preference configuration variable "push.default" to
+change this.  If you are an old-timer who is used to the "matching"
+semantics, you can set the variable to "matching" to keep the
+traditional behaviour.  If you want to live in the future early, you
+can set it to "simple" today without waiting for Git 2.0.
+
+When "git add -u" (and "git add -A") is run inside a subdirectory and
+does not specify which paths to add on the command line, it
+will operate on the entire tree in Git 2.0 for consistency
+with "git commit -a" and other commands.  There will be no
+mechanism to make plain "git add -u" behave like "git add -u .".
+Current users of "git add -u" (without a pathspec) should start
+training their fingers to explicitly say "git add -u ."
+before Git 2.0 comes.  A warning is issued when these commands are
+run without a pathspec and when you have local changes outside the
+current directory, because the behaviour in Git 2.0 will be different
+from today's version in such a situation.
+
+In Git 2.0, "git add <path>" will behave as "git add -A <path>", so
+that "git add dir/" will notice paths you removed from the directory
+and record the removal.  Versions before Git 2.0, including this
+release, will keep ignoring removals, but the users who rely on this
+behaviour are encouraged to start using "git add --ignore-removal <path>"
+now before 2.0 is released.
+
+
+Updates since v1.8.3
+--------------------
+
+Foreign interfaces, subsystems and ports.
+
+ * Cygwin port has been updated for more recent Cygwin 1.7.
+
+ * "git rebase -i" now honors --strategy and -X options.
+
+ * Git-gui has been updated to its 0.18.0 version.
+
+ * MediaWiki remote helper (in contrib/) has been updated to use the
+   credential helper interface from Git.pm.
+
+ * Update build for Cygwin 1.[57].  Torsten Bögershausen reports that
+   this is fine with Cygwin 1.7 ($gmane/225824) so let's try moving it
+   ahead.
+
+ * The credential helper to talk to keychain on OS X (in contrib/) has
+   been updated to kick in not just when talking http/https but also
+   imap(s) and smtp.
+
+ * Remote transport helper has been updated to report errors and
+   maintain ref hierarchy used to keep track of its own state better.
+
+ * With "export" remote-helper protocol, (1) a push that tries to
+   update a remote ref whose name is different from the pushing side
+   does not work yet, and (2) the helper may not know how to do
+   --dry-run; these problematic cases are disabled for now.
+
+ * git-remote-hg/bzr (in contrib/) updates.
+
+ * git-remote-mw (in contrib/) hints users to check the certificate,
+   when https:// connection failed.
+
+ * git-remote-mw (in contrib/) adds a command to allow previewing the
+   contents locally before pushing it out, when working with a
+   MediaWiki remote.
+
+
+UI, Workflows & Features
+
+ * Sample "post-receive-email" hook script got an enhanced replacement
+   "multimail" (in contrib/).
+
+ * Also in contrib/ is a new "contacts" script that runs "git blame"
+   to find out the people who may be interested in a set of changes.
+
+ * "git clean" command learned an interactive mode.
+
+ * The "--head" option to "git show-ref" was only to add "HEAD" to the
+   list of candidate refs to be filtered by the usual rules
+   (e.g. "--heads" that only show refs under refs/heads).  The meaning
+   of the option has been changed to always show "HEAD" regardless of
+   what filtering will be applied to any other ref.
+
+   This is a backward incompatible change and might cause breakages to
+   people's existing scripts.
+
+ * "git show -s" was less discoverable than it should have been.  It
+   now has a natural synonym "git show --no-patch".
+
+ * "git check-mailmap" is a new command that lets you map usernames
+   and e-mail addresses through the mailmap mechanism, just like many
+   built-in commands do.
+
+ * "git name-rev" learned to name an annotated tag object back to its
+   tagname; "git name-rev $(git rev-parse v1.0.0)" gives "tags/v1.0.0",
+   for example.
+
+ * "git cat-file --batch-check=<format>" is added, primarily to allow
+   on-disk footprint of objects in packfiles (often they are a lot
+   smaller than their true size, when expressed as deltas) to be
+   reported.
+
+ * "git rebase [-i]" used to leave just "rebase" as its reflog messages
+   for some operations. They have been reworded to be more informative.
+
+ * In addition to the choice from "rebase, merge, or checkout-detach",
+   "submodule update" can allow a custom command to be used in to
+   update the working tree of submodules via the "submodule.*.update"
+   configuration variable.
+
+ * "git submodule update" can optionally clone the submodule
+   repositories shallowly.
+
+ * "git format-patch" learned "--from[=whom]" option, which sets the
+   "From: " header to the specified person (or the person who runs the
+   command, if "=whom" part is missing) and move the original author
+   information to an in-body From: header as necessary.
+
+ * The configuration variable "merge.ff" was cleary a tri-state to
+   choose one from "favor fast-forward when possible", "always create
+   a merge even when the history could fast-forward" and "do not
+   create any merge, only update when the history fast-forwards", but
+   the command line parser did not implement the usual convention of
+   "last one wins, and command line overrides the configuration"
+   correctly.
+
+ * "gitweb" learned to optionally place extra links that point at the
+   levels higher than the Gitweb pages themselves in the breadcrumbs,
+   so that it can be used as part of a larger installation.
+
+ * "git log --format=" now honors i18n.logoutputencoding configuration
+   variable.
+
+ * The "push.default=simple" mode of "git push" has been updated to
+   behave like "current" without requiring a remote tracking
+   information, when you push to a remote that is different from where
+   you fetch from (i.e. a triangular workflow).
+
+ * Having multiple "fixup!" on a line in the rebase instruction sheet
+   did not work very well with "git rebase -i --autosquash".
+
+ * "git log" learned the "--author-date-order" option, with which the
+   output is topologically sorted and commits in parallel histories
+   are shown intermixed together based on the author timestamp.
+
+ * Various subcommands of "git submodule" refused to run from anywhere
+   other than the top of the working tree of the superproject, but
+   they have been taught to let you run from a subdirectory.
+
+ * "git diff" learned a mode that ignores hunks whose change consists
+   only of additions and removals of blank lines, which is the same as
+   "diff -B" (ignore blank lines) of GNU diff.
+
+ * "git rm" gives a single message followed by list of paths to report
+   multiple paths that cannot be removed.
+
+ * "git rebase" can be told with ":/look for this string" syntax commits
+   to replay the changes onto and where the work to be replayed begins.
+
+ * Many tutorials teach users to set "color.ui" to "auto" as the first
+   thing after you set "user.name/email" to introduce yourselves to
+   Git.  Now the variable defaults to "auto".
+
+ * On Cygwin, "cygstart" is now recognised as a possible way to start
+   a web browser (used in "help -w" and "instaweb" among others).
+
+ * "git status" learned status.branch and status.short configuration
+   variables to use --branch and --short options by default (override
+   with --no-branch and --no-short options from the command line).
+
+ * "git cmd <name>", when <name> happens to be a 40-hex string,
+   directly uses the 40-hex string as an object name, even if a ref
+   "refs/<some hierarchy>/<name>" exists.  This disambiguation order
+   is unlikely to change, but we should warn about the ambiguity just
+   like we warn when more than one refs/ hierarchies share the same
+   name.
+
+ * "git rebase" learned "--[no-]autostash" option to save local
+   changes instead of refusing to run (to which people's normal
+   response was to stash them and re-run).  This introduced a corner
+   case breakage to "git am --abort" but it has been fixed.
+
+ * "check-ignore" (new feature since 1.8.2) has been updated to work
+   more like "check-attr" over bidi-pipes.
+
+ * "git describe" learned "--first-parent" option to limit its closest
+   tagged commit search to the first-parent chain.
+
+ * "git merge foo" that might have meant "git merge origin/foo" is
+   diagnosed with a more informative error message.
+
+ * "git log -L<line>,<range>:<filename>" has been added.  This may
+   still have leaks and rough edges, though.
+
+ * We used the approxidate() parser for "--expire=<timestamp>" options
+   of various commands, but it is better to treat --expire=all and
+   --expire=now a bit more specially than using the current timestamp.
+   "git gc" and "git reflog" have been updated with a new parsing
+   function for expiry dates.
+
+ * Updates to completion (both bash and zsh) helpers.
+
+ * The behaviour of the "--chain-reply-to" option of "git send-email"
+   have changed at 1.7.0, and we added a warning/advice message to
+   help users adjust to the new behaviour back then, but we kept it
+   around for too long.  The message has finally been removed.
+
+ * "git fetch origin master" unlike "git fetch origin" or "git fetch"
+   did not update "refs/remotes/origin/master"; this was an early
+   design decision to keep the update of remote tracking branches
+   predictable, but in practice it turns out that people find it more
+   convenient to opportunistically update them whenever we have a
+   chance, and we have been updating them when we run "git push" which
+   already breaks the original "predictability" anyway.
+
+ * The configuration variable core.checkstat was advertised in the
+   documentation but the code expected core.statinfo instead.
+   For now, we accept both core.checkstat and core.statinfo, but the
+   latter will be removed in the longer term.
+
+
+Performance, Internal Implementation, etc.
+
+ * On Cygwin, we used to use our own lstat(2) emulation that is
+   allegedly faster than the platform one in codepaths where some of
+   the information it returns did not matter, but it started to bite
+   us in a few codepaths where the trick it uses to cheat does show
+   breakages. This emulation has been removed and we use the native
+   lstat(2) emulation supplied by Cygwin now.
+
+ * The function attributes extensions are used to catch mistakes in
+   use of our own variadic functions that use NULL sentinel at the end
+   (i.e. like execl(3)) and format strings (i.e. like printf(3)).
+
+ * The code to allow configuration data to be read from in-tree blob
+   objects is in.  This may help working in a bare repository and
+   submodule updates.
+
+ * Fetching between repositories with many refs employed O(n^2)
+   algorithm to match up the common objects, which has been corrected.
+
+ * The original way to specify remote repository using .git/branches/
+   used to have a nifty feature.  The code to support the feature was
+   still in a function but the caller was changed not to call it 5
+   years ago, breaking that feature and leaving the supporting code
+   unreachable.  The dead code has been removed.
+
+ * "git pack-refs" that races with new ref creation or deletion have
+   been susceptible to lossage of refs under right conditions, which
+   has been tightened up.
+
+ * We read loose and packed references in two steps, but after
+   deciding to read a loose ref but before actually opening it to read
+   it, another process racing with us can unlink it, which would cause
+   us to barf.  The codepath has been updated to retry when such a
+   race is detected, instead of outright failing.
+
+ * Uses of the platform fnmatch(3) function (many places in the code,
+   matching pathspec, .gitignore and .gitattributes to name a few)
+   have been replaced with wildmatch, allowing "foo/**/bar" that would
+   match foo/bar, foo/a/bar, foo/a/b/bar, etc.
+
+ * Memory ownership and lifetime rules for what for-each-ref feeds to
+   its callbacks have been clarified (in short, "you do not own it, so
+   make a copy if you want to keep it").
+
+ * The revision traversal logic to improve culling of irrelevant
+   parents while traversing a mergy history has been updated.
+
+ * Some leaks in unpack-trees (used in merge, cherry-pick and other
+   codepaths) have been plugged.
+
+ * The codepath to read from marks files in fast-import/export did not
+   have to accept anything but 40-hex representation of the object
+   name.  Further, fast-export did not need full in-core object
+   representation to have parsed wen reading from them.  These
+   codepaths have been optimized by taking advantage of these access
+   patterns.
+
+ * Object lookup logic, when the object hashtable starts to become
+   crowded, has been optimized.
+
+ * When TEST_OUTPUT_DIRECTORY setting is used, it was handled somewhat
+   inconsistently between the test framework and t/Makefile, and logic
+   to summarize the results looked at a wrong place.
+
+ * "git clone" uses a lighter-weight implementation when making sure
+   that the history behind refs are complete.
+
+ * Many warnings from sparse source checker in compat/ area has been
+   squelched.
+
+ * The code to reading and updating packed-refs file has been updated,
+   correcting corner case bugs.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v1.8.3
+------------------
+
+Unless otherwise noted, all the fixes since v1.8.3 in the maintenance
+track are contained in this release (see release notes to them for
+details).
+
+ * Newer Net::SMTP::SSL module does not want the user programs to use
+   the default behaviour to let server certificate go without
+   verification, so by default enable the verification with a
+   mechanism to turn it off if needed.
+   (merge 35035bb rr/send-email-ssl-verify later to maint).
+
+ * When "git" is spawned in such a way that any of the low 3 file
+   descriptors is closed, our first open() may yield file descriptor 2,
+   and writing error message to it would screw things up in a big way.
+   (merge a11c396 tr/protect-low-3-fds later to maint).
+
+ * The mailmap mechanism unnecessarily downcased the e-mail addresses
+   in the output, and also ignored the human name when it is a single
+   character name.
+   (merge bd23794 jc/mailmap-case-insensitivity later to maint).
+
+ * In two places we did not check return value (expected to be a file
+   descriptor) correctly.
+   (merge a77f106 tr/fd-gotcha-fixes later to maint).
+
+ * Logic to auto-detect character encodings in the commit log message
+   did not reject overlong and invalid UTF-8 characters.
+   (merge 81050ac bc/commit-invalid-utf8 later to maint).
+
+ * Pass port number as a separate argument when "send-email" initializes
+   Net::SMTP, instead of as a part of the hostname, i.e. host:port.
+   This allows GSSAPI codepath to match with the hostname given.
+   (merge 1a741bf bc/send-email-use-port-as-separate-param later to maint).
+
+ * "git diff" refused to even show difference when core.safecrlf is
+   set to true (i.e. error out) and there are offending lines in the
+   working tree files.
+   (merge 5430bb2 jc/maint-diff-core-safecrlf later to maint).
+
+ * A test that should have failed but didn't revealed a bug that needs
+   to be corrected.
+   (merge 94d75d1 jc/t1512-fix later to maint).
+
+ * An overlong path to a .git directory may have overflown the
+   temporary path buffer used to create a name for lockfiles.
+   (merge 2fbd4f9 mh/maint-lockfile-overflow later to maint).
+
+ * Invocations of "git checkout" used internally by "git rebase" were
+   counted as "checkout", and affected later "git checkout -" to the
+   the user to an unexpected place.
+   (merge 3bed291 rr/rebase-checkout-reflog later to maint).
+
+ * The configuration variable column.ui was poorly documented.
+   (merge 5e62cc1 rr/column-doc later to maint).
+
+ * "git name-rev --refs=tags/v*" were forbidden, which was a bit
+   inconvenient (you had to give a pattern to match refs fully, like
+   --refs=refs/tags/v*).
+   (merge 98c5c4a nk/name-rev-abbreviated-refs later to maint).
+
+ * "git apply" parsed patches that add new files, generated by
+   programs other than Git, incorrectly.  This is an old breakage in
+   v1.7.11 and will need to be merged down to the maintenance tracks.
+
+ * Older cURL wanted piece of memory we call it with to be stable, but
+   we updated the auth material after handing it to a call.
+
+ * "git pull" into nothing trashed "local changes" that were in the
+   index, and this avoids it.
+
+ * Many "git submodule" operations do not work on a submodule at a
+   path whose name is not in ASCII.
+
+ * "cherry-pick" had a small leak in an error codepath.
+
+ * Logic used by git-send-email to suppress cc mishandled names like
+   "A U. Thor" <author@example.xz>, where the human readable part
+   needs to be quoted (the user input may not have the double quotes
+   around the name, and comparison was done between quoted and
+   unquoted strings).  It also mishandled names that need RFC2047
+   quoting.
+
+ * Call to discard_cache/discard_index (used when we use different
+   contents of the index in-core, in many operations like commit,
+   apply, and merge) used to leak memory that held the array of index
+   entries, which has been plugged.
+   (merge a0fc4db rs/discard-index-discard-array later to maint).
+
+ * "gitweb" forgot to clear a global variable $search_regexp upon each
+   request, mistakenly carrying over the previous search to a new one
+   when used as a persistent CGI.
+
+ * The wildmatch engine did not honor WM_CASEFOLD option correctly.
+
+ * "git log -c --follow $path" segfaulted upon hitting the commit that
+   renamed the $path being followed.
+
+ * When a reflog notation is used for implicit "current branch", we
+   did not say which branch and worse said "branch ''".
+
+ * "difftool --dir-diff" did not copy back changes made by the
+   end-user in the diff tool backend to the working tree in some
+   cases.
+
+ * "git push $there HEAD:branch" did not resolve HEAD early enough, so
+   it was easy to flip it around while push is still going on and push
+   out a branch that the user did not originally intended when the
+   command was started.
+
+ * The bash prompt code (in contrib/) displayed the name of the branch
+   being rebased when "rebase -i/-m/-p" modes are in use, but not the
+   plain vanilla "rebase".
+
+ * Handling of negative exclude pattern for directories "!dir" was
+   broken in the update to v1.8.3.
+
+ * zsh prompt script that borrowed from bash prompt script did not
+   work due to slight differences in array variable notation between
+   these two shells.
+
+ * An entry for "file://" scheme in the enumeration of URL types Git
+   can take in the HTML documentation was made into a clickable link
+   by mistake.
+
+ * "git push --[no-]verify" was not documented.
+
+ * Stop installing the git-remote-testpy script that is only used for
+   testing.
+
+ * "git commit --allow-empty-message -m ''" should not start an
+   editor.
+
+ * "git merge @{-1}~22" was rewritten to "git merge frotz@{1}~22"
+   incorrectly when your previous branch was "frotz" (it should be
+   rewritten to "git merge frotz~22" instead).
+
+ * "git diff -c -p" was not showing a deleted line from a hunk when
+   another hunk immediately begins where the earlier one ends.
+
+ * "git log --ancestry-path A...B" did not work as expected, as it did
+   not pay attention to the fact that the merge base between A and B
+   was the bottom of the range being specified.
+
+ * Mac OS X does not like to write(2) more than INT_MAX number of
+   bytes; work it around by chopping write(2) into smaller pieces.
+
+ * Newer MacOS X encourages the programs to compile and link with
+   their CommonCrypto, not with OpenSSL.
+
+ * "git clone foo/bar:baz" cannot be a request to clone from a remote
+   over git-over-ssh specified in the scp style.  This case is now
+   detected and clones from a local repository at "foo/bar:baz".
+
+ * When $HOME is misconfigured to point at an unreadable directory, we
+   used to complain and die. Loosen the check.
+
+ * "git subtree" (in contrib/) had one codepath with loose error
+   checks to lose data at the remote side.
+
+ * "git fetch" into a shallow repository from a repository that does
+   not know about the shallow boundary commits (e.g. a different fork
+   from the repository the current shallow repository was cloned from)
+   did not work correctly.
+
+ * "git checkout foo" DWIMs the intended "upstream" and turns it into
+   "git checkout -t -b foo remotes/origin/foo". This codepath has been
+   updated to correctly take existing remote definitions into account.
index b0d31df0e750141fb8adc7a487c81663246ddf04..4e55b1564e8ad6526c53f797141cfccd83648fc7 100644 (file)
        Include additional statistics at the end of blame output.
 
 -L <start>,<end>::
-       Annotate only the given line range.  <start> and <end> can take
-       one of these forms:
-
-       - number
-+
-If <start> or <end> is a number, it specifies an
-absolute line number (lines count from 1).
+-L :<regex>::
+       Annotate only the given line range.  <start> and <end> are optional.
+       ``-L <start>'' or ``-L <start>,'' spans from <start> to end of file.
+       ``-L ,<end>'' spans from start of file to <end>.
 +
+<start> and <end> can take one of these forms:
 
-- /regex/
-+
-This form will use the first line matching the given
-POSIX regex.  If <end> is a regex, it will search
-starting at the line given by <start>.
-+
-
-- +offset or -offset
-+
-This is only valid for <end> and will specify a number
-of lines before or after the line given by <start>.
-+
+include::line-range-format.txt[]
 
 -l::
        Show long rev (Default: off).
index 29559c88af2e5cc2aeffdc29626b6710446bc10f..6b35578711a3710c36046e8149c2418729862f1f 100644 (file)
@@ -199,6 +199,9 @@ advice.*::
        amWorkDir::
                Advice that shows the location of the patch file when
                linkgit:git-am[1] fails to apply it.
+       rmHints::
+               In case of failure in the output of linkgit:git-rm[1],
+               show directions on how to proceed from the current state.
 --
 
 core.fileMode::
@@ -210,17 +213,6 @@ The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
 will probe and set core.fileMode false if appropriate when the
 repository is created.
 
-core.ignoreCygwinFSTricks::
-       This option is only used by Cygwin implementation of Git. If false,
-       the Cygwin stat() and lstat() functions are used. This may be useful
-       if your repository consists of a few separate directories joined in
-       one hierarchy using Cygwin mount. If true, Git uses native Win32 API
-       whenever it is possible and falls back to Cygwin functions only to
-       handle symbol links. The native mode is more than twice faster than
-       normal Cygwin l/stat() functions. True by default, unless core.filemode
-       is true, in which case ignoreCygwinFSTricks is ignored as Cygwin's
-       POSIX emulation is required to support core.filemode.
-
 core.ignorecase::
        If true, this option enables various workarounds to enable
        Git to work better on filesystems that are not case sensitive,
@@ -412,7 +404,7 @@ repository's usual working tree).
 core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
        "$GIT_DIR/logs/<ref>", by appending the new and old
-       SHA1, the date/time and the reason of the update, but
+       SHA-1, the date/time and the reason of the update, but
        only when the file exists.  If this configuration
        variable is set to true, missing "$GIT_DIR/logs/<ref>"
        file is automatically created for branch heads (i.e. under
@@ -876,16 +868,17 @@ The values of these variables may be specified as in color.branch.<slot>.
 
 color.interactive::
        When set to `always`, always use colors for interactive prompts
-       and displays (such as those used by "git-add --interactive").
-       When false (or `never`), never.  When set to `true` or `auto`, use
-       colors only when the output is to the terminal. Defaults to false.
+       and displays (such as those used by "git-add --interactive" and
+       "git-clean --interactive"). When false (or `never`), never.
+       When set to `true` or `auto`, use colors only when the output is
+       to the terminal. Defaults to false.
 
 color.interactive.<slot>::
-       Use customized color for 'git add --interactive'
-       output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
-       four distinct types of normal output from interactive
-       commands.  The values of these variables may be specified as
-       in color.branch.<slot>.
+       Use customized color for 'git add --interactive' and 'git clean
+       --interactive' output. `<slot>` may be `prompt`, `header`, `help`
+       or `error`, for four distinct types of normal output from
+       interactive commands.  The values of these variables may be
+       specified as in color.branch.<slot>.
 
 color.pager::
        A boolean to enable/disable colored output when the pager is in
@@ -919,17 +912,21 @@ color.ui::
        as `color.diff` and `color.grep` that control the use of color
        per command family. Its scope will expand as more commands learn
        configuration to set a default for the `--color` option.  Set it
-       to `always` if you want all output not intended for machine
-       consumption to use color, to `true` or `auto` if you want such
-       output to use color when written to the terminal, or to `false` or
-       `never` if you prefer Git commands not to use color unless enabled
-       explicitly with some other configuration or the `--color` option.
+       to `false` or `never` if you prefer Git commands not to use
+       color unless enabled explicitly with some other configuration
+       or the `--color` option. Set it to `always` if you want all
+       output not intended for machine consumption to use color, to
+       `true` or `auto` (this is the default since Git 1.8.4) if you
+       want such output to use color when written to the terminal.
 
 column.ui::
        Specify whether supported commands should output in columns.
        This variable consists of a list of tokens separated by spaces
        or commas:
 +
+These options control when the feature should be enabled
+(defaults to 'never'):
++
 --
 `always`;;
        always show in columns
@@ -937,24 +934,39 @@ column.ui::
        never show in columns
 `auto`;;
        show in columns if the output is to the terminal
+--
++
+These options control layout (defaults to 'column').  Setting any
+of these implies 'always' if none of 'always', 'never', or 'auto' are
+specified.
++
+--
 `column`;;
-       fill columns before rows (default)
+       fill columns before rows
 `row`;;
        fill rows before columns
 `plain`;;
        show in one column
+--
++
+Finally, these options can be combined with a layout option (defaults
+to 'nodense'):
++
+--
 `dense`;;
        make unequal size columns to utilize more space
 `nodense`;;
        make equal size columns
 --
-+
-This option defaults to 'never'.
 
 column.branch::
        Specify whether to output branch listing in `git branch` in columns.
        See `column.ui` for details.
 
+column.clean::
+       Specify the layout when list items in `git clean -i`, which always
+       shows files and directories in columns. See `column.ui` for details.
+
 column.status::
        Specify whether to output untracked files in `git status` in columns.
        See `column.ui` for details.
@@ -1104,11 +1116,11 @@ format.thread::
        value disables threading.
 
 format.signoff::
-    A boolean value which lets you enable the `-s/--signoff` option of
-    format-patch by default. *Note:* Adding the Signed-off-by: line to a
-    patch should be a conscious act and means that you certify you have
-    the rights to submit this work under the same open source license.
-    Please see the 'SubmittingPatches' document for further discussion.
+       A boolean value which lets you enable the `-s/--signoff` option of
+       format-patch by default. *Note:* Adding the Signed-off-by: line to a
+       patch should be a conscious act and means that you certify you have
+       the rights to submit this work under the same open source license.
+       Please see the 'SubmittingPatches' document for further discussion.
 
 format.coverLetter::
        A boolean that controls whether to generate a cover-letter when
@@ -1225,7 +1237,7 @@ gitcvs.dbname::
 
 gitcvs.dbdriver::
        Used Perl DBI driver. You can specify any available driver
-        for this here, but it might not work. git-cvsserver is tested
+       for this here, but it might not work. git-cvsserver is tested
        with 'DBD::SQLite', reported to work with 'DBD::Pg', and
        reported *not* to work with 'DBD::mysql'. Experimental feature.
        May not contain double colons (`:`). Default: 'SQLite'.
@@ -1826,39 +1838,59 @@ pull.twohead::
        The default merge strategy to use when pulling a single branch.
 
 push.default::
-       Defines the action `git push` should take if no refspec is given
-       on the command line, no refspec is configured in the remote, and
-       no refspec is implied by any of the options given on the command
-       line. Possible values are:
+       Defines the action `git push` should take if no refspec is
+       explicitly given.  Different values are well-suited for
+       specific workflows; for instance, in a purely central workflow
+       (i.e. the fetch source is equal to the push destination),
+       `upstream` is probably what you want.  Possible values are:
 +
 --
-* `nothing` - do not push anything.
-* `matching` - push all branches having the same name in both ends.
-  This is for those who prepare all the branches into a publishable
-  shape and then push them out with a single command.  It is not
-  appropriate for pushing into a repository shared by multiple users,
-  since locally stalled branches will attempt a non-fast forward push
-  if other users updated the branch.
-  +
-  This is currently the default, but Git 2.0 will change the default
-  to `simple`.
-* `upstream` - push the current branch to its upstream branch
-  (`tracking` is a deprecated synonym for this).
-  With this, `git push` will update the same remote ref as the one which
-  is merged by `git pull`, making `push` and `pull` symmetrical.
-  See "branch.<name>.merge" for how to configure the upstream branch.
-* `simple` - like `upstream`, but refuses to push if the upstream
-  branch's name is different from the local one. This is the safest
-  option and is well-suited for beginners. It will become the default
-  in Git 2.0.
-* `current` - push the current branch to a branch of the same name.
---
+
+* `nothing` - do not push anything (error out) unless a refspec is
+  explicitly given. This is primarily meant for people who want to
+  avoid mistakes by always being explicit.
+
+* `current` - push the current branch to update a branch with the same
+  name on the receiving end.  Works in both central and non-central
+  workflows.
+
+* `upstream` - push the current branch back to the branch whose
+  changes are usually integrated into the current branch (which is
+  called `@{upstream}`).  This mode only makes sense if you are
+  pushing to the same repository you would normally pull from
+  (i.e. central workflow).
+
+* `simple` - in centralized workflow, work like `upstream` with an
+  added safety to refuse to push if the upstream branch's name is
+  different from the local one.
++
+When pushing to a remote that is different from the remote you normally
+pull from, work as `current`.  This is the safest option and is suited
+for beginners.
 +
-The `simple`, `current` and `upstream` modes are for those who want to
-push out a single branch after finishing work, even when the other
-branches are not yet ready to be pushed out. If you are working with
-other people to push into the same shared repository, you would want
-to use one of these.
+This mode will become the default in Git 2.0.
+
+* `matching` - push all branches having the same name on both ends.
+  This makes the repository you are pushing to remember the set of
+  branches that will be pushed out (e.g. if you always push 'maint'
+  and 'master' there and no other branches, the repository you push
+  to will have these two branches, and your local 'maint' and
+  'master' will be pushed there).
++
+To use this mode effectively, you have to make sure _all_ the
+branches you would push out are ready to be pushed out before
+running 'git push', as the whole point of this mode is to allow you
+to push all of the branches in one go.  If you usually finish work
+on only one branch and push out the result, while other branches are
+unfinished, this mode is not for you.  Also this mode is not
+suitable for pushing into a shared central repository, as other
+people may add new branches there, or update the tip of existing
+branches outside your control.
++
+This is currently the default, but Git 2.0 will change the default
+to `simple`.
+
+--
 
 rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
@@ -1867,6 +1899,14 @@ rebase.stat::
 rebase.autosquash::
        If set to true enable '--autosquash' option by default.
 
+rebase.autostash::
+       When set to true, automatically create a temporary stash
+       before the operation begins, and apply it after the operation
+       ends.  This means that you can run rebase on a dirty worktree.
+       However, use with care: the final stash application after a
+       successful rebase might result in non-trivial conflicts.
+       Defaults to false.
+
 receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
        receiving data from git-push and updating refs.  You can stop
@@ -2022,6 +2062,10 @@ sendemail.smtpencryption::
 sendemail.smtpssl::
        Deprecated alias for 'sendemail.smtpencryption = ssl'.
 
+sendemail.smtpsslcertpath::
+       Path to ca-certificates (either a directory or a single file).
+       Set it to an empty string to disable certificate verification.
+
 sendemail.<identity>.*::
        Identity-specific versions of the 'sendemail.*' parameters
        found below, taking precedence over those when the this
@@ -2066,6 +2110,14 @@ status.relativePaths::
        relative to the repository root (this was the default for Git
        prior to v1.5.4).
 
+status.short::
+       Set to true to enable --short by default in linkgit:git-status[1].
+       The option --no-short takes precedence over this variable.
+
+status.branch::
+       Set to true to enable --branch by default in linkgit:git-status[1].
+       The option --no-branch takes precedence over this variable.
+
 status.showUntrackedFiles::
        By default, linkgit:git-status[1] and linkgit:git-commit[1] show
        files which are not currently tracked by Git. Directories which
@@ -2164,6 +2216,17 @@ uploadpack.allowtipsha1inwant::
        of a hidden ref (by default, such a request is rejected).
        see also `uploadpack.hiderefs`.
 
+uploadpack.keepalive::
+       When `upload-pack` has started `pack-objects`, there may be a
+       quiet period while `pack-objects` prepares the pack. Normally
+       it would output progress information, but if `--quiet` was used
+       for the fetch, `pack-objects` will output nothing at all until
+       the pack data begins. Some clients and networks may consider
+       the server to be hung and give up. Setting this option instructs
+       `upload-pack` to send an empty keepalive packet every
+       `uploadpack.keepalive` seconds. Setting this option to 0
+       disables keepalive packets entirely. The default is 5 seconds.
+
 url.<base>.insteadOf::
        Any URL that starts with this value will be rewritten to
        start, instead, with <base>. In cases where some site serves a
index 104579dc75128811e475d408035569ac09f3283b..bbed2cd79c6ebbb95e3b66b5f1ccaa3762d9dfce 100644 (file)
@@ -26,6 +26,11 @@ ifndef::git-format-patch[]
        {git-diff? This is the default.}
 endif::git-format-patch[]
 
+-s::
+--no-patch::
+       Suppress diff output. Useful for commands like `git show` that
+       show the patch by default, or to cancel the effect of `--patch`.
+
 -U<n>::
 --unified=<n>::
        Generate diffs with <n> lines of context instead of
@@ -333,7 +338,7 @@ endif::git-log[]
        a fraction, with a decimal point before it.  I.e., `-M5` becomes
        0.5, and is thus the same as `-M50%`.  Similarly, `-M05` is
        the same as `-M5%`.  To limit detection to exact renames, use
-       `-M100%`.
+       `-M100%`.  The default similarity index is 50%.
 
 -C[<n>]::
 --find-copies[=<n>]::
@@ -383,14 +388,36 @@ ifndef::git-format-patch[]
        that matches other criteria, nothing is selected.
 
 -S<string>::
-       Look for differences that introduce or remove an instance of
-       <string>. Note that this is different than the string simply
-       appearing in diff output; see the 'pickaxe' entry in
-       linkgit:gitdiffcore[7] for more details.
+       Look for differences that change the number of occurrences of
+       the specified string (i.e. addition/deletion) in a file.
+       Intended for the scripter's use.
++
+It is useful when you're looking for an exact block of code (like a
+struct), and want to know the history of that block since it first
+came into being: use the feature iteratively to feed the interesting
+block in the preimage back into `-S`, and keep going until you get the
+very first version of the block.
 
 -G<regex>::
-       Look for differences whose added or removed line matches
-       the given <regex>.
+       Look for differences whose patch text contains added/removed
+       lines that match <regex>.
++
+To illustrate the difference between `-S<regex> --pickaxe-regex` and
+`-G<regex>`, consider a commit with the following diff in the same
+file:
++
+----
++    return !regexec(regexp, two->ptr, 1, &regmatch, 0);
+...
+-    hit = !regexec(regexp, mf2.ptr, 1, &regmatch, 0);
+----
++
+While `git log -G"regexec\(regexp"` will show this commit, `git log
+-S"regexec\(regexp" --pickaxe-regex` will not (because the number of
+occurrences of that string did not change).
++
+See the 'pickaxe' entry in linkgit:gitdiffcore[7] for more
+information.
 
 --pickaxe-all::
        When `-S` or `-G` finds a change, show all the changes in that
@@ -398,8 +425,8 @@ ifndef::git-format-patch[]
        in <string>.
 
 --pickaxe-regex::
-       Make the <string> not a plain string but an extended POSIX
-       regex to match.
+       Treat the <string> given to `-S` as an extended POSIX regular
+       expression to match.
 endif::git-format-patch[]
 
 -O<orderfile>::
@@ -439,6 +466,9 @@ endif::git-format-patch[]
        differences even if one line has whitespace where the other
        line has none.
 
+--ignore-blank-lines::
+       Ignore changes whose lines are all blank.
+
 --inter-hunk-context=<lines>::
        Show the context between diff hunks, up to the specified number
        of lines, thereby fusing hunks that are close to each other.
@@ -480,7 +510,7 @@ endif::git-format-patch[]
 
 --ignore-submodules[=<when>]::
        Ignore changes to submodules in the diff generation. <when> can be
-       either "none", "untracked", "dirty" or "all", which is the default
+       either "none", "untracked", "dirty" or "all", which is the default.
        Using "none" will consider the submodule modified when it either contains
        untracked or modified files or its HEAD differs from the commit recorded
        in the superproject and can be used to override any settings of the
index 9cb649673d609c3b27b46ae5dd217edfcc28a5a9..ba1fe4958227dce5ab146a39374cf02c127c0c65 100644 (file)
@@ -61,7 +61,7 @@ endif::git-pull[]
 ifndef::git-pull[]
 -t::
 --tags::
-       This is a short-hand for giving "refs/tags/*:refs/tags/*"
+       This is a short-hand for giving `refs/tags/*:refs/tags/*`
        refspec from the command line, to ask all tags to be fetched
        and stored locally.  Because this acts as an explicit
        refspec, the default refspecs (configured with the
index b0944e57d52546d725d0d9d71692ff74f9fac55f..48754cbc67a2cc3819fac272cdef9d9d0d76c0d7 100644 (file)
@@ -9,9 +9,9 @@ SYNOPSIS
 --------
 [verse]
 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
-         [--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
-         [--refresh] [--ignore-errors] [--ignore-missing] [--]
-         [<pathspec>...]
+         [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
+         [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing]
+         [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -111,6 +111,7 @@ of Git, hence the form without <pathspec> should not be used.
 
 -A::
 --all::
+--no-ignore-removal::
        Update the index not only where the working tree has a file
        matching <pathspec> but also where the index already has an
        entry.  This adds, modifies, and removes index entries to
@@ -121,6 +122,19 @@ If no <pathspec> is given, the current version of Git defaults to
 and its subdirectories. This default will change in a future version
 of Git, hence the form without <pathspec> should not be used.
 
+--no-all::
+--ignore-removal::
+       Update the index by adding new files that are unknown to the
+       index and files modified in the working tree, but ignore
+       files that have been removed from the working tree.  This
+       option is a no-op when no <pathspec> is used.
++
+This option is primarily to help the current users of Git, whose
+"git add <pathspec>..." ignores removed files.  In future versions
+of Git, "git add <pathspec>..." will be a synonym to "git add -A
+<pathspec>..." and "git add --ignore-removal <pathspec>..." will behave like
+today's "git add <pathspec>...", ignoring removed files.
+
 -N::
 --intent-to-add::
        Record only the fact that the path will be added later. An entry
index 19d57a80f572c221c6a4d678a0673996416c5208..54d8461d61b2f2ad5dbf5ce8c9106534d1a74289 100644 (file)
@@ -9,12 +9,12 @@ git-am - Apply a series of patches from a mailbox
 SYNOPSIS
 --------
 [verse]
-'git am' [--signoff] [--keep] [--keep-cr | --no-keep-cr] [--utf8 | --no-utf8]
+'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8]
         [--3way] [--interactive] [--committer-date-is-author-date]
         [--ignore-date] [--ignore-space-change | --ignore-whitespace]
         [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
         [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
-        [--scissors | --no-scissors]
+        [--[no-]scissors]
         [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort)
 
@@ -43,8 +43,7 @@ OPTIONS
 --keep-non-patch::
        Pass `-b` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]).
 
---keep-cr::
---no-keep-cr::
+--[no-]keep-cr::
        With `--keep-cr`, call 'git mailsplit' (see linkgit:git-mailsplit[1])
        with the same option, to prevent it from stripping CR at the end of
        lines. `am.keepcr` configuration variable can be used to specify the
@@ -133,7 +132,7 @@ default.   You can use `--no-utf8` to override this.
 --resolvemsg=<msg>::
        When a patch failure occurs, <msg> will be printed
        to the screen before exiting.  This overrides the
-       standard message informing you to use `--resolved`
+       standard message informing you to use `--continue`
        or `--skip` to handle the failure.  This is solely
        for internal use between 'git rebase' and 'git am'.
 
@@ -177,7 +176,7 @@ aborts in the middle.  You can recover from this in one of two ways:
 
 . hand resolve the conflict in the working directory, and update
   the index file to bring it into a state that the patch should
-  have produced.  Then run the command with the '--resolved' option.
+  have produced.  Then run the command with the '--continue' option.
 
 The command refuses to process new mailboxes until the current
 operation is finished, so if you decide to start over from scratch,
index 250e5228a35165e72f2f55fd347941c3a2a1d08c..b97aaab4edfc33f407f7d167f015b174c2ddccb5 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git archive' [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>]
-             [-o | --output=<file>] [--worktree-attributes]
+             [-o <file> | --output=<file>] [--worktree-attributes]
              [--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
              [<path>...]
 
index 0eed3e3f2939665a23b972144ad38397b31a53e6..afeb86c6cd4ed695fd47019c016657fab217ac82 100644 (file)
@@ -1320,7 +1320,7 @@ So git bisect is unconditional goodness - and feel free to quote that
 ;-)
 _____________
 
-Acknowledgements
+Acknowledgments
 ----------------
 
 Many thanks to Junio Hamano for his help in reviewing this paper, for
index 9a05c2b3d24ca63163d76476d710e17f31617ff4..6cea7f1ce1ba3a917ddd0cc9a12195d6f768bf76 100644 (file)
@@ -8,9 +8,9 @@ git-blame - Show what revision and author last modified each line of a file
 SYNOPSIS
 --------
 [verse]
-'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental] [-L n,m]
-           [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>] [--abbrev=<n>]
-           [<rev> | --contents <file> | --reverse <rev>] [--] <file>
+'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-e] [-p] [-w] [--incremental]
+           [-L n,m | -L :fn] [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
+           [--abbrev=<n>] [<rev> | --contents <file> | --reverse <rev>] [--] <file>
 
 DESCRIPTION
 -----------
index 2fb95bbd19f26317a542abfdb78d3b2be72f577b..10fbc6a373758437f23ef80245b3bd9e164add1f 100644 (file)
@@ -20,7 +20,7 @@ object type, or '-s' is used to find the object size, or '--textconv' is used
 (which implies type "blob").
 
 In the second form, a list of objects (separated by linefeeds) is provided on
-stdin, and the SHA1, type, and size of each object is printed on stdout.
+stdin, and the SHA-1, type, and size of each object is printed on stdout.
 
 OPTIONS
 -------
@@ -58,12 +58,16 @@ OPTIONS
        to apply the filter to the content recorded in the index at <path>.
 
 --batch::
-       Print the SHA1, type, size, and contents of each object provided on
-       stdin. May not be combined with any other options or arguments.
+--batch=<format>::
+       Print object information and contents for each object provided
+       on stdin.  May not be combined with any other options or arguments.
+       See the section `BATCH OUTPUT` below for details.
 
 --batch-check::
-       Print the SHA1, type, and size of each object provided on stdin. May not
-       be combined with any other options or arguments.
+--batch-check=<format>::
+       Print object information for each object provided on stdin.  May
+       not be combined with any other options or arguments.  See the
+       section `BATCH OUTPUT` below for details.
 
 OUTPUT
 ------
@@ -78,28 +82,75 @@ If '-p' is specified, the contents of <object> are pretty-printed.
 If <type> is specified, the raw (though uncompressed) contents of the <object>
 will be returned.
 
-If '--batch' is specified, output of the following form is printed for each
-object specified on stdin:
+BATCH OUTPUT
+------------
+
+If `--batch` or `--batch-check` is given, `cat-file` will read objects
+from stdin, one per line, and print information about them.
+
+Each line is considered as a whole object name, and is parsed as if
+given to linkgit:git-rev-parse[1].
+
+You can specify the information shown for each object by using a custom
+`<format>`. The `<format>` is copied literally to stdout for each
+object, with placeholders of the form `%(atom)` expanded, followed by a
+newline. The available atoms are:
+
+`objectname`::
+       The 40-hex object name of the object.
+
+`objecttype`::
+       The type of of the object (the same as `cat-file -t` reports).
+
+`objectsize`::
+       The size, in bytes, of the object (the same as `cat-file -s`
+       reports).
+
+`objectsize:disk`::
+       The size, in bytes, that the object takes up on disk. See the
+       note about on-disk sizes in the `CAVEATS` section below.
+
+If no format is specified, the default format is `%(objectname)
+%(objecttype) %(objectsize)`.
+
+If `--batch` is specified, the object information is followed by the
+object contents (consisting of `%(objectsize)` bytes), followed by a
+newline.
+
+For example, `--batch` without a custom format would produce:
 
 ------------
 <sha1> SP <type> SP <size> LF
 <contents> LF
 ------------
 
-If '--batch-check' is specified, output of the following form is printed for
-each object specified on stdin:
+Whereas `--batch-check='%(objectname) %(objecttype)'` would produce:
 
 ------------
-<sha1> SP <type> SP <size> LF
+<sha1> SP <type> LF
 ------------
 
-For both '--batch' and '--batch-check', output of the following form is printed
-for each object specified on stdin that does not exist in the repository:
+If a name is specified on stdin that cannot be resolved to an object in
+the repository, then `cat-file` will ignore any custom format and print:
 
 ------------
 <object> SP missing LF
 ------------
 
+
+CAVEATS
+-------
+
+Note that the sizes of objects on disk are reported accurately, but care
+should be taken in drawing conclusions about which refs or objects are
+responsible for disk usage. The size of a packed non-delta object may be
+much larger than the size of objects which delta against it, but the
+choice of which object is the base and which is the delta is arbitrary
+and is subject to change during a repack. Note also that multiple copies
+of an object may be present in the object database; in this case, it is
+undefined which copy's size will be reported.
+
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 5abdbaa51cf58e216bbc63d28039a5abfba01669..a7be80d48bf6fd6928186116f00f3b48cf564041 100644 (file)
@@ -56,6 +56,11 @@ being queried and <info> can be either:
 'set';;                when the attribute is defined as true.
 <value>;;      when a value has been assigned to the attribute.
 
+Buffering happens as documented under the `GIT_FLUSH` option in
+linkgit:git[1].  The caller is responsible for avoiding deadlocks
+caused by overfilling an input buffer or reading from an empty output
+buffer.
+
 EXAMPLES
 --------
 
index 854e4d0c425a9396bdb71dc84093f43156b04cd5..d2df487aa20a89264afee08bcdddf681d7701fda 100644 (file)
@@ -39,6 +39,12 @@ OPTIONS
        below).  If `--stdin` is also given, input paths are separated
        with a NUL character instead of a linefeed character.
 
+-n, --non-matching::
+       Show given paths which don't match any pattern.  This only
+       makes sense when `--verbose` is enabled, otherwise it would
+       not be possible to distinguish between paths which match a
+       pattern and those which don't.
+
 OUTPUT
 ------
 
@@ -65,6 +71,20 @@ are also used instead of colons and hard tabs:
 
 <source> <NULL> <linenum> <NULL> <pattern> <NULL> <pathname> <NULL>
 
+If `-n` or `--non-matching` are specified, non-matching pathnames will
+also be output, in which case all fields in each output record except
+for <pathname> will be empty.  This can be useful when running
+non-interactively, so that files can be incrementally streamed to
+STDIN of a long-running check-ignore process, and for each of these
+files, STDOUT will indicate whether that file matched a pattern or
+not.  (Without this option, it would be impossible to tell whether the
+absence of output for a given file meant that it didn't match any
+pattern, or that the output hadn't been generated yet.)
+
+Buffering happens as documented under the `GIT_FLUSH` option in
+linkgit:git[1].  The caller is responsible for avoiding deadlocks
+caused by overfilling an input buffer or reading from an empty output
+buffer.
 
 EXIT STATUS
 -----------
@@ -82,7 +102,7 @@ SEE ALSO
 --------
 linkgit:gitignore[5]
 linkgit:gitconfig[5]
-linkgit:git-ls-files[5]
+linkgit:git-ls-files[1]
 
 GIT
 ---
diff --git a/Documentation/git-check-mailmap.txt b/Documentation/git-check-mailmap.txt
new file mode 100644 (file)
index 0000000..39028ee
--- /dev/null
@@ -0,0 +1,47 @@
+git-check-mailmap(1)
+====================
+
+NAME
+----
+git-check-mailmap - Show canonical names and email addresses of contacts
+
+
+SYNOPSIS
+--------
+[verse]
+'git check-mailmap' [options] <contact>...
+
+
+DESCRIPTION
+-----------
+
+For each ``Name $$<user@host>$$'' or ``$$<user@host>$$'' from the command-line
+or standard input (when using `--stdin`), look up the person's canonical name
+and email address (see "Mapping Authors" below). If found, print them;
+otherwise print the input as-is.
+
+
+OPTIONS
+-------
+--stdin::
+       Read contacts, one per line, from the standard input after exhausting
+       contacts provided on the command-line.
+
+
+OUTPUT
+------
+
+For each contact, a single line is output, terminated by a newline.  If the
+name is provided or known to the 'mailmap', ``Name $$<user@host>$$'' is
+printed; otherwise only ``$$<user@host>$$'' is printed.
+
+
+MAPPING AUTHORS
+---------------
+
+include::mailmap.txt[]
+
+
+GIT
+---
+Part of the linkgit:git[1] suite
index ec1739a896074c96bd94603592ca72bcee0b927f..a49be1bab49ddcdb65e4b45b578bd183a94657a5 100644 (file)
@@ -83,8 +83,7 @@ typed the branch name.
 
 OPTIONS
 -------
---allow-onelevel::
---no-allow-onelevel::
+--[no-]allow-onelevel::
        Controls whether one-level refnames are accepted (i.e.,
        refnames that do not contain multiple `/`-separated
        components).  The default is `--no-allow-onelevel`.
index bf0c99c5397653374200871c2d121273a02fd09d..ca118ac6bfff9f837d06de37f99f457f6d38916b 100644 (file)
@@ -180,6 +180,12 @@ branch by running "git rm -rf ." from the top level of the working tree.
 Afterwards you will be ready to prepare your new files, repopulating the
 working tree, by copying them from elsewhere, extracting a tarball, etc.
 
+--ignore-skip-worktree-bits::
+       In sparse checkout mode, `git checkout -- <paths>` would
+       update only entries matched by <paths> and sparse patterns
+       in $GIT_DIR/info/sparse-checkout. This option ignores
+       the sparse patterns and adds back any files in <paths>.
+
 -m::
 --merge::
        When switching branches,
index bdc3ab80c7990f569efecc2bacbc31d15b0357f2..89979228b12080606f5becc268792c6eae8f128e 100644 (file)
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -34,7 +34,13 @@ OPTIONS
 -f::
 --force::
        If the Git configuration variable clean.requireForce is not set
-       to false, 'git clean' will refuse to run unless given -f or -n.
+       to false, 'git clean' will refuse to run unless given -f, -n or
+       -i.
+
+-i::
+--interactive::
+       Show what would be done and clean files interactively. See
+       ``Interactive mode'' for details.
 
 -n::
 --dry-run::
@@ -63,6 +69,67 @@ OPTIONS
        Remove only files ignored by Git.  This may be useful to rebuild
        everything from scratch, but keep manually created files.
 
+Interactive mode
+----------------
+When the command enters the interactive mode, it shows the
+files and directories to be cleaned, and goes into its
+interactive command loop.
+
+The command loop shows the list of subcommands available, and
+gives a prompt "What now> ".  In general, when the prompt ends
+with a single '>', you can pick only one of the choices given
+and type return, like this:
+
+------------
+    *** Commands ***
+       1: clean                2: filter by pattern    3: select by numbers
+       4: ask each             5: quit                 6: help
+    What now> 1
+------------
+
+You also could say `c` or `clean` above as long as the choice is unique.
+
+The main command loop has 6 subcommands.
+
+clean::
+
+   Start cleaning files and directories, and then quit.
+
+filter by pattern::
+
+   This shows the files and directories to be deleted and issues an
+   "Input ignore patterns>>" prompt. You can input space-seperated
+   patterns to exclude files and directories from deletion.
+   E.g. "*.c *.h" will excludes files end with ".c" and ".h" from
+   deletion. When you are satisfied with the filtered result, press
+   ENTER (empty) back to the main menu.
+
+select by numbers::
+
+   This shows the files and directories to be deleted and issues an
+   "Select items to delete>>" prompt. When the prompt ends with double
+   '>>' like this, you can make more than one selection, concatenated
+   with whitespace or comma.  Also you can say ranges.  E.g. "2-5 7,9"
+   to choose 2,3,4,5,7,9 from the list.  If the second number in a
+   range is omitted, all remaining items are selected.  E.g. "7-" to
+   choose 7,8,9 from the list.  You can say '*' to choose everything.
+   Also when you are satisfied with the filtered result, press ENTER
+   (empty) back to the main menu.
+
+ask each::
+
+  This will start to clean, and you must confirm one by one in order
+  to delete items. Please note that this action is not as efficient
+  as the above two actions.
+
+quit::
+
+  This lets you quit without do cleaning.
+
+help::
+
+  Show brief usage of interactive git-clean.
+
 SEE ALSO
 --------
 linkgit:gitignore[5]
index 5c16e317f66f326fac0dd0e5d70e26152aee66d3..450f158779fbfda31c0b3f85d4768a294ef782ef 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
          [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
          [--separate-git-dir <git dir>]
          [--depth <depth>] [--[no-]single-branch]
-         [--recursive|--recurse-submodules] [--] <repository>
+         [--recursive | --recurse-submodules] [--] <repository>
          [<directory>]
 
 DESCRIPTION
@@ -188,7 +188,7 @@ objects from the source repository into a pack in the cloned repository.
        with a long history, and would want to send in fixes
        as patches.
 
---single-branch::
+--[no-]single-branch::
        Clone only the history leading to the tip of a single branch,
        either specified by the `--branch` option or the primary
        branch remote's `HEAD` points at. When creating a shallow
@@ -239,8 +239,8 @@ Examples
 * Clone from upstream:
 +
 ------------
-$ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
-$ cd my2.6
+$ git clone git://git.kernel.org/pub/scm/.../linux.git my-linux
+$ cd my-linux
 $ make
 ------------
 
@@ -257,10 +257,10 @@ $ git show-branch
 * Clone from upstream while borrowing from an existing local directory:
 +
 ------------
-$ git clone --reference my2.6 \
-       git://git.kernel.org/pub/scm/.../linux-2.7 \
-       my2.7
-$ cd my2.7
+$ git clone --reference /git/linux.git \
+       git://git.kernel.org/pub/scm/.../linux.git \
+       my-linux
+$ cd my-linux
 ------------
 
 
@@ -271,13 +271,6 @@ $ git clone --bare -l /home/proj/.git /pub/scm/proj.git
 ------------
 
 
-* Create a repository on the kernel.org machine that borrows from Linus:
-+
-------------
-$ git clone --bare -l -s /pub/scm/.../torvalds/linux-2.6.git \
-    /pub/scm/.../me/subsys-2.6.git
-------------
-
 GIT
 ---
 Part of the linkgit:git[1] suite
index 9b1be5581d34b9911e3d60f23dcdda440d3bc17f..1a7616c73a204f69bca98d0ff7f4cdfda20ba420 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
           [--dry-run] [(-c | -C | --fixup | --squash) <commit>]
           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]
           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
-          [--date=<date>] [--cleanup=<mode>] [--status | --no-status]
+          [--date=<date>] [--cleanup=<mode>] [--[no-]status]
           [-i | -o] [-S[<keyid>]] [--] [<file>...]
 
 DESCRIPTION
@@ -197,8 +197,8 @@ variable (see linkgit:git-config[1]).
 -e::
 --edit::
        The message taken from file with `-F`, command line with
-       `-m`, and from file with `-C` are usually used as the
-       commit log message unmodified.  This option lets you
+       `-m`, and from commit object with `-C` are usually used as
+       the commit log message unmodified. This option lets you
        further edit the message taken from these sources.
 
 --no-edit::
index 9ae2508f3fb79b9da5fb4481a4fe2120f4a082b1..2dbe486eb16d8edde01ece856fa1e0ad75053042 100644 (file)
@@ -82,7 +82,7 @@ OPTIONS
 --get::
        Get the value for a given key (optionally filtered by a regex
        matching the value). Returns error code 1 if the key was not
-       found and error code 2 if multiple key values were found.
+       found and the last value if multiple key values were found.
 
 --get-all::
        Like get, but does not fail if the number of values for the key
@@ -96,28 +96,46 @@ OPTIONS
        names are not.
 
 --global::
-       For writing options: write to global ~/.gitconfig file rather than
-       the repository .git/config, write to $XDG_CONFIG_HOME/git/config file
-       if this file exists and the ~/.gitconfig file doesn't.
+       For writing options: write to global `~/.gitconfig` file
+       rather than the repository `.git/config`, write to
+       `$XDG_CONFIG_HOME/git/config` file if this file exists and the
+       `~/.gitconfig` file doesn't.
 +
-For reading options: read only from global ~/.gitconfig and from
-$XDG_CONFIG_HOME/git/config rather than from all available files.
+For reading options: read only from global `~/.gitconfig` and from
+`$XDG_CONFIG_HOME/git/config` rather than from all available files.
 +
 See also <<FILES>>.
 
 --system::
-       For writing options: write to system-wide $(prefix)/etc/gitconfig
-       rather than the repository .git/config.
+       For writing options: write to system-wide
+       `$(prefix)/etc/gitconfig` rather than the repository
+       `.git/config`.
 +
-For reading options: read only from system-wide $(prefix)/etc/gitconfig
+For reading options: read only from system-wide `$(prefix)/etc/gitconfig`
 rather than from all available files.
 +
 See also <<FILES>>.
 
+--local::
+       For writing options: write to the repository `.git/config` file.
+       This is the default behavior.
++
+For reading options: read only from the repository `.git/config` rather than
+from all available files.
++
+See also <<FILES>>.
+
 -f config-file::
 --file config-file::
        Use the given config file instead of the one specified by GIT_CONFIG.
 
+--blob blob::
+       Similar to '--file' but use the given blob instead of a file. E.g.
+       you can use 'master:.gitmodules' to read values from the file
+       '.gitmodules' in the master branch. See "SPECIFYING REVISIONS"
+       section in linkgit:gitrevisions[7] for a more complete list of
+       ways to spell blob names.
+
 --remove-section::
        Remove the given section from the configuration file.
 
@@ -186,8 +204,7 @@ See also <<FILES>>.
        Opens an editor to modify the specified config file; either
        '--system', '--global', or repository (default).
 
---includes::
---no-includes::
+--[no-]includes::
        Respect `include.*` directives in config files when looking up
        values. Defaults to on.
 
@@ -198,23 +215,23 @@ FILES
 If not set explicitly with '--file', there are four files where
 'git config' will search for configuration options:
 
-$GIT_DIR/config::
-       Repository specific configuration file.
-
-~/.gitconfig::
-       User-specific configuration file. Also called "global"
-       configuration file.
+$(prefix)/etc/gitconfig::
+       System-wide configuration file.
 
 $XDG_CONFIG_HOME/git/config::
        Second user-specific configuration file. If $XDG_CONFIG_HOME is not set
-       or empty, $HOME/.config/git/config will be used. Any single-valued
+       or empty, `$HOME/.config/git/config` will be used. Any single-valued
        variable set in this file will be overwritten by whatever is in
-       ~/.gitconfig.  It is a good idea not to create this file if
+       `~/.gitconfig`.  It is a good idea not to create this file if
        you sometimes use older versions of Git, as support for this
        file was added fairly recently.
 
-$(prefix)/etc/gitconfig::
-       System-wide configuration file.
+~/.gitconfig::
+       User-specific configuration file. Also called "global"
+       configuration file.
+
+$GIT_DIR/config::
+       Repository specific configuration file.
 
 If no further options are given, all reading options will read all of these
 files that are available. If the global or the system-wide configuration
@@ -222,6 +239,10 @@ file are not available they will be ignored. If the repository configuration
 file is not available or readable, 'git config' will exit with a non-zero
 error code. However, in neither case will an error message be issued.
 
+The files are read in the order given above, with last value found taking
+precedence over values read earlier.  When multiple values are taken then all
+values of a key from all files will be used.
+
 All writing options will per default write to the repository specific
 configuration file. Note that this also affects options like '--replace-all'
 and '--unset'. *'git config' will only ever change one file at a time*.
index bfb106cccd5880f0cd40b03059aa79a5569acb63..223f7315236be845a158f63d5909804f965837ba 100644 (file)
@@ -16,8 +16,10 @@ SYNOPSIS
             [--reuseaddr] [--detach] [--pid-file=<file>]
             [--enable=<service>] [--disable=<service>]
             [--allow-override=<service>] [--forbid-override=<service>]
-            [--access-hook=<path>]
-            [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>] [--user=<user> [--group=<group>]]
+            [--access-hook=<path>] [--[no-]informative-errors]
+            [--inetd |
+             [--listen=<host_or_ipaddr>] [--port=<n>]
+             [--user=<user> [--group=<group>]]]
             [<directory>...]
 
 DESCRIPTION
@@ -169,8 +171,7 @@ Git configuration files in that directory are readable by `<user>`.
        repository configuration.  By default, all the services
        are overridable.
 
---informative-errors::
---no-informative-errors::
+--[no-]informative-errors::
        When informative errors are turned on, git-daemon will report
        more verbose errors to the client, differentiating conditions
        like "no such repository" from "repository not exported". This
index 3c81e85ec5cf1b3090a879d49ad2445176312872..9439cd6d56b0d3aa2115b9843936b449b6765652 100644 (file)
@@ -88,6 +88,11 @@ OPTIONS
 --always::
        Show uniquely abbreviated commit object as fallback.
 
+--first-parent::
+       Follow only the first parent commit upon seeing a merge commit.
+       This is useful when you wish to not match tags on branches merged
+       in the history of the target commit.
+
 EXAMPLES
 --------
 
@@ -149,7 +154,9 @@ is found, its name will be output and searching will stop.
 If an exact match was not found, 'git describe' will walk back
 through the commit history to locate an ancestor commit which
 has been tagged.  The ancestor's tag will be output along with an
-abbreviation of the input committish's SHA1.
+abbreviation of the input committish's SHA-1. If '--first-parent' was
+specified then the walk will only consider the first parent of each
+commit.
 
 If multiple tags were found during the walk then the tag which
 has the fewest commits different from the input committish will be
index c0b7c581add35e24128a6b7e9aa79a50f914aad5..a86cf62e68999f698c1a49edab207a9721c6dfaf 100644 (file)
@@ -3,7 +3,7 @@ git-diff-index(1)
 
 NAME
 ----
-git-diff-index - Compares content and mode of blobs between the index and repository
+git-diff-index - Compare a tree to the working tree or index
 
 
 SYNOPSIS
@@ -13,11 +13,11 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Compares the content and mode of the blobs found via a tree
-object with the content of the current index and, optionally
-ignoring the stat state of the file on disk.  When paths are
-specified, compares only those named paths.  Otherwise all
-entries in the index are compared.
+Compares the content and mode of the blobs found in a tree object
+with the corresponding tracked files in the working tree, or with the
+corresponding paths in the index.  When <path> arguments are present,
+compares only paths matching those patterns.  Otherwise all tracked
+files are compared.
 
 OPTIONS
 -------
index a7b46208f62c81b63ef4b8fdc5cf3ac67bd8b541..78d6d50489502638013509fbf72c811b50bfee86 100644 (file)
@@ -18,8 +18,8 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Show changes between the working tree and the index or a tree, changes
-between the index and a tree, changes between two trees, or changes
-between two files on disk.
+between the index and a tree, changes between two trees, changes between
+two blob objects, or changes between two files on disk.
 
 'git diff' [--options] [--] [<path>...]::
 
@@ -56,11 +56,6 @@ directories. This behavior can be forced by --no-index.
        This is to view the changes between two arbitrary
        <commit>.
 
-'git diff' [options] <blob> <blob>::
-
-       This form is to view the differences between the raw
-       contents of two blob objects.
-
 'git diff' [--options] <commit>..<commit> [--] [<path>...]::
 
        This is synonymous to the previous form.  If <commit> on
@@ -87,6 +82,11 @@ and the range notations ("<commit>..<commit>" and
 "<commit>\...<commit>") do not mean a range as defined in the
 "SPECIFYING RANGES" section in linkgit:gitrevisions[7].
 
+'git diff' [options] <blob> <blob>::
+
+       This form is to view the differences between the raw
+       contents of two blob objects.
+
 OPTIONS
 -------
 :git-diff: 1
index 8361e6e4e3d2d4829d3ce3c6f3ec4a2ac974e86e..11887e63a05904d95f7d07d51b818721b33c0b66 100644 (file)
@@ -69,8 +69,7 @@ with custom merge tool commands and has the same value as `$MERGED`.
 --tool-help::
        Print a list of diff tools that may be used with `--tool`.
 
---symlinks::
---no-symlinks::
+--[no-]symlinks::
        'git difftool''s default behavior is create symlinks to the
        working tree when run in `--dir-diff` mode and the right-hand
        side of the comparison yields the same content as the file in
index feab7a3e4ecb56aa17c32bd555e907a51001ef1f..85f1f30fdf5c0a741c3c575bfa393fc7d88d308f 100644 (file)
@@ -27,15 +27,17 @@ OPTIONS
        Insert 'progress' statements every <n> objects, to be shown by
        'git fast-import' during import.
 
---signed-tags=(verbatim|warn|strip|abort)::
+--signed-tags=(verbatim|warn|warn-strip|strip|abort)::
        Specify how to handle signed tags.  Since any transformation
        after the export can change the tag names (which can also happen
        when excluding revisions) the signatures will not match.
 +
 When asking to 'abort' (which is the default), this program will die
-when encountering a signed tag.  With 'strip', the tags will be made
-unsigned, with 'verbatim', they will be silently exported
-and with 'warn', they will be exported, but you will see a warning.
+when encountering a signed tag.  With 'strip', the tags will silently
+be made unsigned, with 'warn-strip' they will be made unsigned but a
+warning will be displayed, with 'verbatim', they will be silently
+exported and with 'warn', they will be exported, but you will see a
+warning.
 
 --tag-of-filtered-object=(abort|drop|rewrite)::
        Specify how to handle tags whose tagged object is filtered out.
@@ -104,11 +106,11 @@ marks the same across runs.
        different from the commit's first parent).
 
 [<git-rev-list-args>...]::
-       A list of arguments, acceptable to 'git rev-parse' and
-       'git rev-list', that specifies the specific objects and references
-       to export.  For example, `master~10..master` causes the
-       current master reference to be exported along with all objects
-       added since its 10th ancestor commit.
+       A list of arguments, acceptable to 'git rev-parse' and
+       'git rev-list', that specifies the specific objects and references
+       to export.  For example, `master~10..master` causes the
+       current master reference to be exported along with all objects
+       added since its 10th ancestor commit.
 
 EXAMPLES
 --------
@@ -139,7 +141,7 @@ Limitations
 -----------
 
 Since 'git fast-import' cannot tag trees, you will not be
-able to export the linux-2.6.git repository completely, as it contains
+able to export the linux.git repository completely, as it contains
 a tag referencing a tree instead of a commit.
 
 GIT
index b81e90d8e70722e0da690ab5afa1bca4ad356b68..1e717543470c0440a72812a046bcadf0e52e4953 100644 (file)
@@ -10,9 +10,9 @@ SYNOPSIS
 --------
 [verse]
 'git fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag]
-                               [--upload-pack=<git-upload-pack>]
-                               [--depth=<n>] [--no-progress]
-                               [-v] [<host>:]<directory> [<refs>...]
+       [--upload-pack=<git-upload-pack>]
+       [--depth=<n>] [--no-progress]
+       [-v] [<host>:]<directory> [<refs>...]
 
 DESCRIPTION
 -----------
index 3a0f55ec8e273545af3a92a9b886511ac79ba73c..bb1232a52c4f0e4995f1fd38737461165772c239 100644 (file)
@@ -35,8 +35,7 @@ OPTIONS
        Do not list one-line descriptions from the actual commits being
        merged.
 
---summary::
---no-summary::
+--[no-]summary::
        Synonyms to --log and --no-log; these are deprecated and will be
        removed in the future.
 
index 39118774afbbd730e4464f44e24a05af91432c30..e394276b1a2e2edfbbf8ab1dea9fcd30ca66e61f 100644 (file)
@@ -187,6 +187,21 @@ will want to ensure that threading is disabled for `git send-email`.
        The negated form `--no-cc` discards all `Cc:` headers added so
        far (from config or command line).
 
+--from::
+--from=<ident>::
+       Use `ident` in the `From:` header of each commit email. If the
+       author ident of the commit is not textually identical to the
+       provided `ident`, place a `From:` header in the body of the
+       message with the original author. If no `ident` is given, use
+       the committer ident.
++
+Note that this option is only useful if you are actually sending the
+emails and want to identify yourself as the sender, but retain the
+original author (and `git am` will correctly pick up the in-body
+header). Note also that `git send-email` already handles this
+transformation for you, and this option should not be used if you are
+feeding the result to `git send-email`.
+
 --add-header=<header>::
        Add an arbitrary header to the email headers.  This is in addition
        to any configured headers, and may be used multiple times.
index eff91889d7aab1a5262aad2f22855b52716a820d..25c431d3c552d523c79473cf10867a3cab00c259 100644 (file)
@@ -23,15 +23,14 @@ OPTIONS
        An object to treat as the head of an unreachability trace.
 +
 If no objects are given, 'git fsck' defaults to using the
-index file, all SHA1 references in `refs` namespace, and all reflogs
+index file, all SHA-1 references in `refs` namespace, and all reflogs
 (unless --no-reflogs is given) as heads.
 
 --unreachable::
        Print out objects that exist but that aren't reachable from any
        of the reference nodes.
 
---dangling::
---no-dangling::
+--[no-]dangling::
        Print objects that exist but that are never 'directly' used (default).
        `--no-dangling` can be used to omit this information from the output.
 
@@ -78,8 +77,7 @@ index file, all SHA1 references in `refs` namespace, and all reflogs
        a blob, the contents are written into the file, rather than
        its object name.
 
---progress::
---no-progress::
+--[no-]progress::
        Progress status is reported on the standard error stream by
        default when it is attached to a terminal, unless
        --no-progress or --verbose is specified. --progress forces
@@ -89,7 +87,7 @@ index file, all SHA1 references in `refs` namespace, and all reflogs
 DISCUSSION
 ----------
 
-git-fsck tests SHA1 and general object sanity, and it does full tracking
+git-fsck tests SHA-1 and general object sanity, and it does full tracking
 of the resulting reachability and everything else. It prints out any
 corruption it finds (missing or bad objects), and if you use the
 '--unreachable' flag it will also print out objects that exist but that
index b370b025b89ffad0664171a03527b9a775172dee..2402ed68280ded314c340e394da64b3372038113 100644 (file)
@@ -62,8 +62,9 @@ automatic consolidation of packs.
 
 --prune=<date>::
        Prune loose objects older than date (default is 2 weeks ago,
-       overridable by the config variable `gc.pruneExpire`).  This
-       option is on by default.
+       overridable by the config variable `gc.pruneExpire`).
+       --prune=all prunes loose objects regardless of their age.
+       --prune is on by default.
 
 --no-prune::
        Do not prune any loose objects.
index 50d46e1a7bae054d7c1afdeb01b9174a3a9c7002..8497aa4494778134cad5ca2c2b11fb1c824e2a24 100644 (file)
@@ -25,7 +25,7 @@ SYNOPSIS
           [-W | --function-context]
           [-f <file>] [-e] <pattern>
           [--and|--or|--not|(|)|-e <pattern>...]
-          [ [--exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
+          [ [--[no-]exclude-standard] [--cached | --no-index | --untracked] | <tree>...]
           [--] [<pathspec>...]
 
 DESCRIPTION
index 7b1e85cd15b8ced793e56b8accc1e32dfa5aabc9..e3bcdb50e30ea984cb8ae842df0cf1e561e32a62 100644 (file)
@@ -80,7 +80,30 @@ ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
 ----------------------------------------------------------------
 +
 To enable anonymous read access but authenticated write access,
-require authorization with a LocationMatch directive:
+require authorization for both the initial ref advertisement (which we
+detect as a push via the service parameter in the query string), and the
+receive-pack invocation itself:
++
+----------------------------------------------------------------
+RewriteCond %{QUERY_STRING} service=git-receive-pack [OR]
+RewriteCond %{REQUEST_URI} /git-receive-pack$
+RewriteRule ^/git/ - [E=AUTHREQUIRED:yes]
+
+<LocationMatch "^/git/">
+       Order Deny,Allow
+       Deny from env=AUTHREQUIRED
+
+       AuthType Basic
+       AuthName "Git Access"
+       Require group committers
+       Satisfy Any
+       ...
+</LocationMatch>
+----------------------------------------------------------------
++
+If you do not have `mod_rewrite` available to match against the query
+string, it is sufficient to just protect `git-receive-pack` itself,
+like:
 +
 ----------------------------------------------------------------
 <LocationMatch "^/git/.*/git-receive-pack$">
@@ -91,6 +114,15 @@ require authorization with a LocationMatch directive:
 </LocationMatch>
 ----------------------------------------------------------------
 +
+In this mode, the server will not request authentication until the
+client actually starts the object negotiation phase of the push, rather
+than during the initial contact.  For this reason, you must also enable
+the `http.receivepack` config option in any repositories that should
+accept a push. The default behavior, if `http.receivepack` is not set,
+is to reject any pushes by unauthenticated users; the initial request
+will therefore report `403 Forbidden` to the client, without even giving
+an opportunity for authentication.
++
 To require authentication for both reads and writes, use a Location
 directive around the repository, or one of its parent directories:
 +
@@ -158,6 +190,54 @@ ScriptAliasMatch \
 ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/
 ----------------------------------------------------------------
 
+Lighttpd::
+       Ensure that `mod_cgi`, `mod_alias, `mod_auth`, `mod_setenv` are
+       loaded, then set `GIT_PROJECT_ROOT` appropriately and redirect
+       all requests to the CGI:
++
+----------------------------------------------------------------
+alias.url += ( "/git" => "/usr/lib/git-core/git-http-backend" )
+$HTTP["url"] =~ "^/git" {
+       cgi.assign = ("" => "")
+       setenv.add-environment = (
+               "GIT_PROJECT_ROOT" => "/var/www/git",
+               "GIT_HTTP_EXPORT_ALL" => ""
+       )
+}
+----------------------------------------------------------------
++
+To enable anonymous read access but authenticated write access:
++
+----------------------------------------------------------------
+$HTTP["querystring"] =~ "service=git-receive-pack" {
+       include "git-auth.conf"
+}
+$HTTP["url"] =~ "^/git/.*/git-receive-pack$" {
+       include "git-auth.conf"
+}
+----------------------------------------------------------------
++
+where `git-auth.conf` looks something like:
++
+----------------------------------------------------------------
+auth.require = (
+       "/" => (
+               "method" => "basic",
+               "realm" => "Git Access",
+               "require" => "valid-user"
+              )
+)
+# ...and set up auth.backend here
+----------------------------------------------------------------
++
+To require authentication for both reads and writes:
++
+----------------------------------------------------------------
+$HTTP["url"] =~ "^/git/private" {
+       include "git-auth.conf"
+}
+----------------------------------------------------------------
+
 
 ENVIRONMENT
 -----------
index 36adc5fc147d758ecc636ffd57ead2d1e63399e7..7a4e0555205be5d8bb26e5a183f1552cc057637a 100644 (file)
@@ -74,6 +74,9 @@ OPTIONS
 --strict::
        Die, if the pack contains broken objects or links.
 
+--check-self-contained-and-connected::
+       Die if the pack contains broken links. For internal use only.
+
 --threads=<n>::
        Specifies the number of threads to spawn when resolving
        deltas. This requires that index-pack be compiled with
@@ -89,7 +92,7 @@ Note
 ----
 
 Once the index has been created, the list of object names is sorted
-and the SHA1 hash of that list is printed to stdout. If --stdin was
+and the SHA-1 hash of that list is printed to stdout. If --stdin was
 also used then this is prefixed by either "pack\t", or "keep\t" if a
 new .keep file was successfully created. This is useful to remove a
 .keep file used as a lock to prevent the race with 'git repack'
index 69db5783cef46dedabd1169f962fbcc92a028e4e..ac2694d04cfc73c9b5ae6e6982d2f7f290ed67fe 100644 (file)
@@ -9,7 +9,7 @@ git-log - Show commit logs
 SYNOPSIS
 --------
 [verse]
-'git log' [<options>] [<since>..<until>] [[\--] <path>...]
+'git log' [<options>] [<revision range>] [[\--] <path>...]
 
 DESCRIPTION
 -----------
@@ -24,13 +24,6 @@ each commit introduces are shown.
 OPTIONS
 -------
 
-<since>..<until>::
-       Show only commits between the named two commits.  When
-       either <since> or <until> is omitted, it defaults to
-       `HEAD`, i.e. the tip of the current branch.
-       For a more complete list of ways to spell <since>
-       and <until>, see linkgit:gitrevisions[7].
-
 --follow::
        Continue listing the history of a file beyond renames
        (works only for a single file).
@@ -69,20 +62,42 @@ produced by --stat etc.
        Note that only message is considered, if also a diff is shown
        its size is not included.
 
+-L <start>,<end>:<file>, -L :<regex>:<file>::
+
+       Trace the evolution of the line range given by "<start>,<end>"
+       (or the funcname regex <regex>) within the <file>.  You may
+       not give any pathspec limiters.  This is currently limited to
+       a walk starting from a single revision, i.e., you may only
+       give zero or one positive revision arguments.
+       You can specify this option more than once.
++
+<start> and <end> can take one of these forms:
+
+include::line-range-format.txt[]
+
+<revision range>::
+       Show only commits in the specified revision range.  When no
+       <revision range> is specified, it defaults to `HEAD` (i.e. the
+       whole history leading to the current commit).  `origin..HEAD`
+       specifies all the commits reachable from the current commit
+       (i.e. `HEAD`), but not from `origin`. For a complete list of
+       ways to spell <revision range>, see the "Specifying Ranges"
+       section of linkgit:gitrevisions[7].
+
 [\--] <path>...::
        Show only commits that are enough to explain how the files
        that match the specified paths came to be.  See "History
        Simplification" below for details and other simplification
        modes.
 +
-To prevent confusion with options and branch names, paths may need to
-be prefixed with "\-- " to separate them from options or refnames.
+Paths may need to be prefixed with "\-- " to separate them from
+options or the revision range, when confusion arises.
 
 include::rev-list-options.txt[]
 
 include::pretty-formats.txt[]
 
-Common diff options
+COMMON DIFF OPTIONS
 -------------------
 
 :git-log: 1
@@ -90,7 +105,7 @@ include::diff-options.txt[]
 
 include::diff-generate-patch.txt[]
 
-Examples
+EXAMPLES
 --------
 `git log --no-merges`::
 
@@ -113,9 +128,9 @@ Examples
        in the "release" branch, along with the list of paths
        each commit modifies.
 
-`git log --follow builtin-rev-list.c`::
+`git log --follow builtin/rev-list.c`::
 
-       Shows the commits that changed builtin-rev-list.c, including
+       Shows the commits that changed builtin/rev-list.c, including
        those commits that occurred before the file was given its
        present name.
 
@@ -138,15 +153,20 @@ Examples
        This makes sense only when following a strict policy of merging all
        topic branches when staying on a single integration branch.
 
+`git log -L '/int main/',/^}/:main.c`::
+
+       Shows how the function `main()` in the file 'main.c' evolved
+       over time.
+
 `git log -3`::
        Limits the number of commits to show to 3.
 
-Discussion
+DISCUSSION
 ----------
 
 include::i18n.txt[]
 
-Configuration
+CONFIGURATION
 -------------
 
 See linkgit:git-config[1] for core variables and linkgit:git-diff[1]
index 0bdebff6f7985918855286b5fc5072dce4b644e5..c0856a6e0a0a625fcc4c394057597d633a0487ac 100644 (file)
@@ -164,7 +164,7 @@ which case it outputs:
 'git ls-files --unmerged' and 'git ls-files --stage' can be used to examine
 detailed information on unmerged paths.
 
-For an unmerged path, instead of recording a single mode/SHA1 pair,
+For an unmerged path, instead of recording a single mode/SHA-1 pair,
 the index records up to three such pairs; one from tree O in stage
 1, A in stage 2, and B in stage 3.  This information can be used by
 the user (or the porcelain) to see what should eventually be recorded at the
index 774de5e9d9fc2d4d88e4f820be177ea6c0a09360..2e22915eb857bd960f09afc725107198642bfd22 100644 (file)
@@ -48,9 +48,9 @@ OPTIONS
        exit without talking to the remote.
 
 <repository>::
-       Location of the repository.  The shorthand defined in
-       $GIT_DIR/branches/ can be used. Use "." (dot) to list references in
-       the local repository.
+       The "remote" repository to query.  This parameter can be
+       either a URL or the name of a remote (see the GIT URLS and
+       REMOTES sections of linkgit:git-fetch[1]).
 
 <refs>...::
        When unspecified, all references, after filtering done
@@ -70,9 +70,8 @@ EXAMPLES
        $ git ls-remote http://www.kernel.org/pub/scm/git/git.git master pu rc
        5fe978a5381f1fbad26a80e682ddd2a401966740        refs/heads/master
        c781a84b5204fb294c9ccc79f8b3baceeb32c061        refs/heads/pu
-       b1d096f2926c4e37c9c0b6a7bf2119bedaa277cb        refs/heads/rc
-       $ echo http://www.kernel.org/pub/scm/git/git.git >.git/branches/public
-       $ git ls-remote --tags public v\*
+       $ git remote add korg http://www.kernel.org/pub/scm/git/git.git
+       $ git ls-remote --tags korg v\*
        d6602ec5194c87b0fc87103ca4d67251c76f233a        refs/tags/v0.99
        f25a265a342aed6041ab0cc484224d9ca54b6f41        refs/tags/v0.99.1
        c5db5456ae3b0873fc659c19fafdde22313cc441        refs/tags/v0.99.2
index 97e7a8e9e7cc01bc350d57ca66e08f5ed96221de..164a3c6ede9be2c3a42cf0ba3612403114d4dde5 100644 (file)
@@ -9,7 +9,7 @@ git-mailinfo - Extracts patch and authorship from a single e-mail message
 SYNOPSIS
 --------
 [verse]
-'git mailinfo' [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
+'git mailinfo' [-k|-b] [-u | --encoding=<encoding> | -n] [--[no-]scissors] <msg> <patch>
 
 
 DESCRIPTION
index 0c80cec0e8d6bd2bf5798e9c34316b57b7afc776..02676fb39197d8c4ae33b3d6aa3e8fa27b8e3350 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
 DESCRIPTION
 -----------
 This looks up the <file>(s) in the index and, if there are any merge
-entries, passes the SHA1 hash for those files as arguments 1, 2, 3 (empty
+entries, passes the SHA-1 hash for those files as arguments 1, 2, 3 (empty
 argument if no file), and <file> as argument 4.  File modes for the three
 files are passed as arguments 5, 6 and 7.
 
index 42391f2ae76aa579605470b6cad8cf4812f29aae..439545926ecdcf609c299ca0e5ea44032969c1be 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
-       [-s <strategy>] [-X <strategy-option>]
+       [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
        [--[no-]rerere-autoupdate] [-m <msg>] [<commit>...]
 'git merge' <msg> HEAD <commit>...
 'git merge' --abort
@@ -56,8 +56,8 @@ especially if those changes were further modified after the merge
 was started), 'git merge --abort' will in some cases be unable to
 reconstruct the original (pre-merge) changes. Therefore:
 
-*Warning*: Running 'git merge' with uncommitted changes is
-discouraged: while possible, it leaves you in a state that is hard to
+*Warning*: Running 'git merge' with non-trivial uncommitted changes is
+discouraged: while possible, it may leave you in a state that is hard to
 back out of in the case of a conflict.
 
 
@@ -65,6 +65,10 @@ OPTIONS
 -------
 include::merge-options.txt[]
 
+-S[<keyid>]::
+--gpg-sign[=<keyid>]::
+       GPG-sign the resulting merge commit.
+
 -m <msg>::
        Set the commit message to be used for the merge commit (in
        case one is created).
@@ -76,8 +80,7 @@ The 'git fmt-merge-msg' command can be
 used to give a good default for automated 'git merge'
 invocations.
 
---rerere-autoupdate::
---no-rerere-autoupdate::
+--[no-]rerere-autoupdate::
        Allow the rerere mechanism to update the index with the
        result of auto-conflict resolution if possible.
 
@@ -187,11 +190,11 @@ In such a case, you can "unwrap" the tag yourself before feeding it
 to `git merge`, or pass `--ff-only` when you do not have any work on
 your own. e.g.
 
----
+----
 git fetch origin
 git merge v1.2.3^0
 git merge --ff-only v1.2.3
----
+----
 
 
 HOW CONFLICTS ARE PRESENTED
index 6b563c500f660486951cbcac62876bbda5cc3975..07137f252b0fd57a671ef69884bea893cdd7767c 100644 (file)
@@ -8,7 +8,7 @@ git-mergetool - Run merge conflict resolution tools to resolve merge conflicts
 SYNOPSIS
 --------
 [verse]
-'git mergetool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<file>...]
+'git mergetool' [--tool=<tool>] [-y | --[no-]prompt] [<file>...]
 
 DESCRIPTION
 -----------
index ad1d1468c9936d206701d379003177e7457c884e..15b00e09916fff47a8244432ce396c9b6f7fbec1 100644 (file)
@@ -25,14 +25,17 @@ OPTIONS
        Do not use branch names, but only tags to name the commits
 
 --refs=<pattern>::
-       Only use refs whose names match a given shell pattern.
+       Only use refs whose names match a given shell pattern.  The pattern
+       can be one of branch name, tag name or fully qualified ref name.
 
 --all::
        List all commits reachable from all refs
 
 --stdin::
-       Read from stdin, append "(<rev_name>)" to all sha1's of nameable
-       commits, and pass to stdout
+       Transform stdin by substituting all the 40-character SHA-1
+       hexes (say $hex) with "$hex ($rev_name)".  When used with
+       --name-only, substitute with "$rev_name", omitting $hex
+       altogether.  Intended for the scripter's use.
 
 --name-only::
        Instead of printing both the SHA-1 and the name, print only
index c579fbc2b858d0a6fb5cd8dcbcaa25d3b3fca2a5..8cba16d67f29ec48738b00a607add4a20bc7d4b2 100644 (file)
@@ -176,13 +176,16 @@ Sync options
 These options can be used in the initial 'clone' as well as in
 subsequent 'sync' operations.
 
---branch <branch>::
-       Import changes into given branch.  If the branch starts with
-       'refs/', it will be used as is.  Otherwise if it does not start
-       with 'p4/', that prefix is added.  The branch is assumed to
-       name a remote tracking, but this can be modified using
-       '--import-local', or by giving a full ref name.  The default
-       branch is 'master'.
+--branch <ref>::
+       Import changes into <ref> instead of refs/remotes/p4/master.
+       If <ref> starts with refs/, it is used as is.  Otherwise, if
+       it does not start with p4/, that prefix is added.
++
+By default a <ref> not starting with refs/ is treated as the
+name of a remote-tracking branch (under refs/remotes/).  This
+behavior can be modified using the --import-local option.
++
+The default <ref> is "master".
 +
 This example imports a new remote "p4/proj2" into an existing
 Git repository:
index 69c9313cf59cde569b08e905fe4f732ad4c4c517..d94edcd4b4fdd8bd57150bd0ad8f870a45e0249c 100644 (file)
@@ -50,7 +50,7 @@ base-name::
        Write into a pair of files (.pack and .idx), using
        <base-name> to determine the name of the created file.
        When this option is used, the two files are written in
-       <base-name>-<SHA1>.{pack,idx} files.  <SHA1> is a hash
+       <base-name>-<SHA-1>.{pack,idx} files.  <SHA-1> is a hash
        of the sorted object names to make the resulting filename
        based on the pack content, and written to the standard
        output of the command.
index f131677478e91f8ab6611a6e45dda56ff48a9e2a..154081f2de269975a09c1f5899dc7d5948c54ca7 100644 (file)
@@ -33,8 +33,8 @@ Subsequent updates to branches always create new files under
 `$GIT_DIR/refs` directory hierarchy.
 
 A recommended practice to deal with a repository with too many
-refs is to pack its refs with `--all --prune` once, and
-occasionally run `git pack-refs --prune`.  Tags are by
+refs is to pack its refs with `--all` once, and
+occasionally run `git pack-refs`.  Tags are by
 definition stationary and are not expected to change.  Branch
 heads will be packed with the initial `pack-refs --all`, but
 only the currently active branch heads will become unpacked,
index 90268f02e7381b6f1403e54103988bfde6522073..312c3b1fe5f970a5fdab0e7e1ba0fcbe021eaaa5 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-A "patch ID" is nothing but a SHA1 of the diff associated with a patch, with
+A "patch ID" is nothing but a SHA-1 of the diff associated with a patch, with
 whitespace and line numbers ignored.  As such, it's "reasonably stable", but at
 the same time also reasonably unique, i.e., two patches that have the same "patch
 ID" are almost guaranteed to be the same thing.
index 80dc022edea58b76aeea2f7df0876fd98bc6dc05..6738055bd3083825c06c82d1b7678ef453854253 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-This program searches the `$GIT_OBJECT_DIR` for all objects that currently
+This program searches the `$GIT_OBJECT_DIRECTORY` for all objects that currently
 exist in a pack file as well as the independent object directories.
 
 All such extra objects are removed.
index 80d01b05710e250a5f1d548fca3fba54d50e0f29..bf824108c116a34972d1dacf1e9fb77db097b2c5 100644 (file)
@@ -59,7 +59,7 @@ borrows from your repository via its
 `.git/objects/info/alternates`:
 
 ------------
-$ git prune $(cd ../another && $(git rev-parse --all))
+$ git prune $(cd ../another && git rev-parse --all)
 ------------
 
 Notes
index 24ab07a3f8f80c56cbbd7feffc1b5bf65e137968..6ef8d599d31ac20982fa74f213b81494ceae162a 100644 (file)
@@ -3,7 +3,7 @@ git-pull(1)
 
 NAME
 ----
-git-pull - Fetch from and merge with another repository or a local branch
+git-pull - Fetch from and integrate with another repository or a local branch
 
 
 SYNOPSIS
index eb2883c94cfad91efcc1f70bbdbc1609a3702d3b..f7dfe48d2813324e1a699efa530afe8974d7c400 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
           [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]
-          [<repository> [<refspec>...]]
+          [--no-verify] [<repository> [<refspec>...]]
 
 DESCRIPTION
 -----------
@@ -136,6 +136,15 @@ already exists on the remote side.
        not an ancestor of the local ref used to overwrite it.
        This flag disables the check.  This can cause the
        remote repository to lose commits; use it with care.
+       Note that `--force` applies to all the refs that are pushed,
+       hence using it with `push.default` set to `matching` or with
+       multiple push destinations configured with `remote.*.push`
+       may overwrite refs other than the current branch (including
+       local refs that are strictly behind their remote counterpart).
+       To force a push to only one branch, use a `+` in front of the
+       refspec to push (e.g `git push origin +master` to force a push
+       to the `master` branch). See the `<refspec>...` section above
+       for details.
 
 --repo=<repository>::
        This option is only relevant if no <repository> argument is
@@ -162,8 +171,7 @@ useful if you write an alias or script around 'git push'.
        linkgit:git-pull[1] and other commands. For more information,
        see 'branch.<name>.merge' in linkgit:git-config[1].
 
---thin::
---no-thin::
+--[no-]thin::
        These options are passed to linkgit:git-send-pack[1]. A thin transfer
        significantly reduces the amount of sent data when the sender and
        receiver share many of the same objects in common. The default is
@@ -196,6 +204,11 @@ useful if you write an alias or script around 'git push'.
        be pushed. If on-demand was not able to push all necessary
        revisions it will also be aborted and exit with non-zero status.
 
+--[no-]verify::
+       Toggle the pre-push hook (see linkgit:githooks[5]).  The
+       default is \--verify, giving the hook a chance to prevent the
+       push.  With \--no-verify, the hook is bypassed completely.
+
 
 include::urls-remotes.txt[]
 
index aca840525eee7bb3743baf7f72d8c0f240ff1b60..6b2e1c86ab6c1084421e06b5f677264dc4099b6a 100644 (file)
@@ -208,6 +208,9 @@ rebase.stat::
 rebase.autosquash::
        If set to true enable '--autosquash' option by default.
 
+rebase.autostash::
+       If set to true enable '--autostash' option by default.
+
 OPTIONS
 -------
 --onto <newbase>::
@@ -386,7 +389,9 @@ squash/fixup series.
        the same ..., automatically modify the todo list of rebase -i
        so that the commit marked for squashing comes right after the
        commit to be modified, and change the action of the moved
-       commit from `pick` to `squash` (or `fixup`).
+       commit from `pick` to `squash` (or `fixup`).  Ignores subsequent
+       "fixup! " or "squash! " after the first, in case you referred to an
+       earlier fixup/squash with `git commit --fixup/--squash`.
 +
 This option is only valid when the '--interactive' option is used.
 +
@@ -394,6 +399,13 @@ If the '--autosquash' option is enabled by default using the
 configuration variable `rebase.autosquash`, this option can be
 used to override and disable this setting.
 
+--[no-]autostash::
+       Automatically create a temporary stash before the operation
+       begins, and apply it after the operation ends.  This means
+       that you can run rebase on a dirty worktree.  However, use
+       with care: the final stash application after a successful
+       rebase might result in non-trivial conflicts.
+
 --no-ff::
        With --interactive, cherry-pick all rebased commits instead of
        fast-forwarding over the unchanged ones.  This ensures that the
index fb8697ea4c390154b63dd88612ddf1effcb57ffe..70791b9fd88b4c64260523a5fcadcb46ce504f8f 100644 (file)
@@ -67,14 +67,19 @@ them.
 --expire=<time>::
        Entries older than this time are pruned.  Without the
        option it is taken from configuration `gc.reflogExpire`,
-       which in turn defaults to 90 days.
+       which in turn defaults to 90 days.  --expire=all prunes
+       entries regardless of their age; --expire=never turns off
+       pruning of reachable entries (but see --expire-unreachable).
 
 --expire-unreachable=<time>::
        Entries older than this time and not reachable from
        the current tip of the branch are pruned.  Without the
        option it is taken from configuration
        `gc.reflogExpireUnreachable`, which in turn defaults to
-       30 days.
+       30 days.  --expire-unreachable=all prunes unreachable
+       entries regardless of their age; --expire-unreachable=never
+       turns off early pruning of unreachable entries (but see
+       --expire).
 
 --all::
        Instead of listing <refs> explicitly, prune all refs.
index e8c396b5f92903777ce5905cf52e2ef879850739..9c3e3bf83a2721678a2c7a430ec88bee0d39dd0a 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git remote' [-v | --verbose]
-'git remote add' [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>
+'git remote add' [-t <branch>] [-m <master>] [-f] [--[no-]tags] [--mirror=<fetch|push>] <name> <url>
 'git remote rename' <old> <new>
 'git remote remove' <name>
 'git remote set-head' <name> (-a | -d | <branch>)
@@ -18,8 +18,8 @@ SYNOPSIS
 'git remote set-url' [--push] <name> <newurl> [<oldurl>]
 'git remote set-url --add' [--push] <name> <newurl>
 'git remote set-url --delete' [--push] <name> <url>
-'git remote' [-v | --verbose] 'show' [-n] <name>
-'git remote prune' [-n | --dry-run] <name>
+'git remote' [-v | --verbose] 'show' [-n] <name>...
+'git remote prune' [-n | --dry-run] <name>...
 'git remote' [-v | --verbose] 'update' [-p | --prune] [(<group> | <remote>)...]
 
 DESCRIPTION
@@ -187,18 +187,25 @@ Examples
 $ git remote
 origin
 $ git branch -r
-origin/master
-$ git remote add linux-nfs git://linux-nfs.org/pub/linux/nfs-2.6.git
+  origin/HEAD -> origin/master
+  origin/master
+$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
 $ git remote
-linux-nfs
 origin
-$ git fetch
-* refs/remotes/linux-nfs/master: storing branch 'master' ...
-  commit: bf81b46
+staging
+$ git fetch staging
+...
+From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
+ * [new branch]      master     -> staging/master
+ * [new branch]      staging-linus -> staging/staging-linus
+ * [new branch]      staging-next -> staging/staging-next
 $ git branch -r
-origin/master
-linux-nfs/master
-$ git checkout -b nfs linux-nfs/master
+  origin/HEAD -> origin/master
+  origin/master
+  staging/master
+  staging/staging-linus
+  staging/staging-next
+$ git checkout -b staging staging/master
 ...
 ------------
 
index 0142cd18ae7c1f78f345c9a511891a4d962a2677..e0b405797630c066f57082abc98fb1c03a1b219d 100644 (file)
@@ -16,8 +16,8 @@ DESCRIPTION
 -----------
 Adds a 'replace' reference in `refs/replace/` namespace.
 
-The name of the 'replace' reference is the SHA1 of the object that is
-replaced. The content of the 'replace' reference is the SHA1 of the
+The name of the 'replace' reference is the SHA-1 of the object that is
+replaced. The content of the 'replace' reference is the SHA-1 of the
 replacement object.
 
 Unless `-f` is given, the 'replace' reference must not yet exist.
index a404b47b7bf80ef85e5f34595044090b7025f32c..f445cb38fa26a6daa62b5be7f65122948ca669ab 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git reset' [-q] [<tree-ish>] [--] <paths>...
-'git reset' (--patch | -p) [<tree-sh>] [--] [<paths>...]
+'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
 'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
index 65ac27e0c9f1f7c8a0313314574831a546bb3862..045b37b82e7544ddb753cdec7ead2bc8d97ad1c8 100644 (file)
@@ -40,7 +40,7 @@ SYNOPSIS
             [ \--right-only ]
             [ \--cherry-mark ]
             [ \--cherry-pick ]
-            [ \--encoding[=<encoding>] ]
+            [ \--encoding=<encoding> ]
             [ \--(author|committer|grep)=<pattern> ]
             [ \--regexp-ignore-case | -i ]
             [ \--extended-regexp | -E ]
index 1f9ed6cfd28622afcf269eb8dac05fb9414d9901..2b126c0a77f9901eea1ba5ac37a2fb2d66c4d21f 100644 (file)
@@ -59,6 +59,22 @@ OPTIONS
        If there is no parameter given by the user, use `<arg>`
        instead.
 
+--prefix <arg>::
+       Behave as if 'git rev-parse' was invoked from the `<arg>`
+       subdirectory of the working tree.  Any relative filenames are
+       resolved as if they are prefixed by `<arg>` and will be printed
+       in that form.
++
+This can be used to convert arguments to a command run in a subdirectory
+so that they can still be used after moving to the top-level of the
+repository.  For example:
++
+----
+prefix=$(git rev-parse --show-prefix)
+cd "$(git rev-parse --show-toplevel)"
+eval "set -- $(git rev-parse --sq --prefix "$prefix" "$@")"
+----
+
 --verify::
        Verify that exactly one parameter is provided, and that it
        can be turned into a raw 20-byte SHA-1 that can be used to
@@ -67,7 +83,7 @@ OPTIONS
 +
 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 parmeter.
+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`
@@ -95,7 +111,7 @@ can be used.
        one.
 
 --symbolic::
-       Usually the object names are output in SHA1 form (with
+       Usually the object names are output in SHA-1 form (with
        possible '{caret}' prefix); this option makes them output in a
        form as close to the original input as possible.
 
@@ -180,7 +196,7 @@ print a message to stderr and exit with nonzero status.
 
 --short::
 --short=number::
-       Instead of outputting the full SHA1 values of object names try to
+       Instead of outputting the full SHA-1 values of object names try to
        abbreviate them to a shorter unique name. When no length is specified
        7 is used. The minimum length is 4.
 
index 70152e8b1eb31e5ed6e7da889a424bc65c1f16c2..f79c9d858301353043fe0878bee268d8eacde189 100644 (file)
@@ -8,7 +8,7 @@ git-revert - Revert some existing commits
 SYNOPSIS
 --------
 [verse]
-'git revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>...
+'git revert' [--[no-]edit] [-n] [-m parent-number] [-s] <commit>...
 'git revert' --continue
 'git revert' --quit
 'git revert' --abort
index 40a9a9abc14f175738986b244207655b296cc31d..f0e57a597b6e551d891c064fa69837db632237c6 100644 (file)
@@ -198,6 +198,12 @@ must be used for each option.
 --smtp-ssl::
        Legacy alias for '--smtp-encryption ssl'.
 
+--smtp-ssl-cert-path::
+       Path to ca-certificates (either a directory or a single file).
+       Set it to an empty string to disable certificate verification.
+       Defaults to the value set to the 'sendemail.smtpsslcertpath'
+       configuration variable, if set, or `/etc/ssl/certs` otherwise.
+
 --smtp-user=<user>::
        Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser';
        if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'),
index c308e91537a02817560d3108be5517d1806b0c9e..31af7f27360d277ccdc3e5bd59b4788c83934b8e 100644 (file)
@@ -8,8 +8,8 @@ git-shortlog - Summarize 'git log' output
 SYNOPSIS
 --------
 [verse]
-git log --pretty=short | 'git shortlog' [-h] [-n] [-s] [-e] [-w]
-'git shortlog' [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] <commit>...
+git log --pretty=short | 'git shortlog' [<options>]
+'git shortlog' [<options>] [<revision range>] [[\--] <path>...]
 
 DESCRIPTION
 -----------
@@ -26,10 +26,6 @@ reference to the current repository.
 OPTIONS
 -------
 
--h::
---help::
-       Print a short usage message and exit.
-
 -n::
 --numbered::
        Sort output according to the number of commits per author instead
@@ -60,6 +56,21 @@ OPTIONS
 If width is `0` (zero) then indent the lines of the output without wrapping
 them.
 
+<revision range>::
+       Show only commits in the specified revision range.  When no
+       <revision range> is specified, it defaults to `HEAD` (i.e. the
+       whole history leading to the current commit).  `origin..HEAD`
+       specifies all the commits reachable from the current commit
+       (i.e. `HEAD`), but not from `origin`. For a complete list of
+       ways to spell <revision range>, see the "Specifying Ranges"
+       section of linkgit:gitrevisions[7].
+
+[\--] <path>...::
+       Consider only commits that are enough to explain how the files
+       that match the specified paths came to be.
++
+Paths may need to be prefixed with "\-- " to separate them from
+options or the revision range, when confusion arises.
 
 MAPPING AUTHORS
 ---------------
index a8e77b5350c8c6a3cf62c22f811974ea64cff8ae..a515648ab018b849897dd3506183f57359be598a 100644 (file)
@@ -31,7 +31,7 @@ no <rev> nor <glob> is given on the command line.
 OPTIONS
 -------
 <rev>::
-       Arbitrary extended SHA1 expression (see linkgit:gitrevisions[7])
+       Arbitrary extended SHA-1 expression (see linkgit:gitrevisions[7])
        that typically names a branch head or a tag.
 
 <glob>::
@@ -142,7 +142,7 @@ displayed, indented N places.  If a commit is on the I-th
 branch, the I-th indentation character shows a `+` sign;
 otherwise it shows a space.  Merge commits are denoted by
 a `-` sign.  Each commit shows a short name that
-can be used as an extended SHA1 to name that commit.
+can be used as an extended SHA-1 to name that commit.
 
 The following example shows three branches, "master", "fixes"
 and "mhf":
index 9cbbed944c9ab7511e5e27876174968624ded5ad..fbdc8adae5b10a8ed88883647d2f0ccc3636cddc 100644 (file)
@@ -19,7 +19,7 @@ Reads given idx file for packed Git archive created with
 
 The information it outputs is subset of what you can get from
 'git verify-pack -v'; this command only shows the packfile
-offset and SHA1 of each object.
+offset and SHA-1 of each object.
 
 GIT
 ---
index 5dbcd47fec3b605d0b4b3e6ed47c06cc57f1dd6d..b0a309b117bd12170ccd9194d1cf5083d5675365 100644 (file)
@@ -21,6 +21,8 @@ commit IDs. Results can be filtered using a pattern and tags can be
 dereferenced into object IDs. Additionally, it can be used to test whether a
 particular ref exists.
 
+By default, shows the tags, heads, and remote refs.
+
 The --exclude-existing form is a filter that does the inverse, it shows the
 refs from stdin that don't exist in the local repository.
 
@@ -32,14 +34,14 @@ OPTIONS
 
 --head::
 
-       Show the HEAD reference.
+       Show the HEAD reference, even if it would normally be filtered out.
 
 --tags::
 --heads::
 
-       Limit to only "refs/heads" and "refs/tags", respectively.  These
-       options are not mutually exclusive; when given both, references stored
-       in "refs/heads" and "refs/tags" are displayed.
+       Limit to "refs/heads" and "refs/tags", respectively.  These options
+       are not mutually exclusive; when given both, references stored in
+       "refs/heads" and "refs/tags" are displayed.
 
 -d::
 --dereference::
@@ -50,8 +52,8 @@ OPTIONS
 -s::
 --hash[=<n>]::
 
-       Only show the SHA1 hash, not the reference name. When combined with
-       --dereference the dereferenced tag will still be shown after the SHA1.
+       Only show the SHA-1 hash, not the reference name. When combined with
+       --dereference the dereferenced tag will still be shown after the SHA-1.
 
 --verify::
 
index ae4edcccfbc2c862f4a1f77a5d58c348fbee2def..4e617e6979dda86138e14cb93d84016d304de07a 100644 (file)
@@ -45,6 +45,15 @@ include::pretty-options.txt[]
 include::pretty-formats.txt[]
 
 
+COMMON DIFF OPTIONS
+-------------------
+
+:git-log: 1
+include::diff-options.txt[]
+
+include::diff-generate-patch.txt[]
+
+
 EXAMPLES
 --------
 
index 711ffe17a7574511de6c20cb85b6cdd5a4fff5a5..db7e803038090510609a6534ab7d086245c15270 100644 (file)
@@ -13,10 +13,11 @@ SYNOPSIS
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
             [-u|--include-untracked] [-a|--all] [<message>]]
 'git stash' clear
-'git stash' create
+'git stash' create [<message>]
+'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
 
 DESCRIPTION
 -----------
@@ -151,7 +152,15 @@ create::
 
        Create a stash (which is a regular commit object) and return its
        object name, without storing it anywhere in the ref namespace.
+       This is intended to be useful for scripts.  It is probably not
+       the command you want to use; see "save" above.
 
+store::
+
+       Store a given stash created via 'git stash create' (which is a
+       dangling merge commit) in the stash ref, updating the stash
+       reflog.  This is intended to be useful for scripts.  It is
+       probably not the command you want to use; see "save" above.
 
 DISCUSSION
 ----------
index 74d5bdc59d58f743bc6f5cb5f7f9ee24d384d5c8..bfef8a0c62a56865cfe2335841efcb4535642287 100644 (file)
@@ -10,12 +10,12 @@ SYNOPSIS
 --------
 [verse]
 'git submodule' [--quiet] add [-b <branch>] [-f|--force] [--name <name>]
-             [--reference <repository>] [--] <repository> [<path>]
+             [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]
 'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
 'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
-             [-f|--force] [--rebase] [--reference <repository>]
+             [-f|--force] [--rebase] [--reference <repository>] [--depth <depth>]
              [--merge] [--recursive] [--] [<path>...]
 'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
              [commit] [--] [<path>...]
@@ -77,6 +77,8 @@ argument <path> is the relative location for the cloned submodule
 to exist in the superproject. If <path> is not given, the
 "humanish" part of the source repository is used ("repo" for
 "/path/to/repo.git" and "foo" for "host.xz:foo/.git").
+The <path> is also used as the submodule's logical name in its
+configuration entries unless `--name` is used to specify a logical name.
 +
 <repository> is the URL of the new submodule's origin repository.
 This may be either an absolute URL, or (if it begins with ./
@@ -124,8 +126,10 @@ linkgit:git-status[1] and linkgit:git-diff[1] will provide that information
 too (and can also report changes to a submodule's work tree).
 
 init::
-       Initialize the submodules, i.e. register each submodule name
-       and url found in .gitmodules into .git/config.
+       Initialize the submodules recorded in the index (which were
+       added and committed elsewhere) by copying submodule
+       names and urls from .gitmodules to .git/config.
+       Optional <path> arguments limit which submodules will be initialized.
        It will also copy the value of `submodule.$name.update` into
        .git/config.
        The key used in .git/config is `submodule.$name.url`.
@@ -155,7 +159,9 @@ update::
        This will make the submodules HEAD be detached unless `--rebase` or
        `--merge` is specified or the key `submodule.$name.update` is set to
        `rebase`, `merge` or `none`. `none` can be overridden by specifying
-       `--checkout`.
+       `--checkout`. Setting the key `submodule.$name.update` to `!command`
+       will cause `command` to be run. `command` can be any arbitrary shell
+       command that takes a single argument, namely the sha1 to update to.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
@@ -258,7 +264,7 @@ OPTIONS
 --remote::
        This option is only valid for the update command.  Instead of using
        the superproject's recorded SHA-1 to update the submodule, use the
-       status of the submodule's remote tracking branch.  The remote used
+       status of the submodule's remote-tracking branch.  The remote used
        is branch's remote (`branch.<name>.remote`), defaulting to `origin`.
        The remote branch used defaults to `master`, but the branch name may
        be overridden by setting the `submodule.<name>.branch` option in
@@ -324,6 +330,12 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
        only in the submodules of the current repo, but also
        in any nested submodules inside those submodules (and so on).
 
+--depth::
+       This option is valid for add and update commands. Create a 'shallow'
+       clone with a history truncated to the specified number of revisions.
+       See linkgit:git-clone[1]
+
+
 <path>...::
        Paths to submodule(s). When specified this will restrict the command
        to only operate on the submodules found at the specified paths.
index 7706d41c8704679c00a302306651628e5b99d1c1..4dd3bcb5117f31a76281ef09847b0171cdbf9b80 100644 (file)
@@ -85,6 +85,10 @@ COMMANDS
        When passed to 'init' or 'clone' this regular expression will
        be preserved as a config key.  See 'fetch' for a description
        of '--ignore-paths'.
+--include-paths=<regex>;;
+       When passed to 'init' or 'clone' this regular expression will
+       be preserved as a config key.  See 'fetch' for a description
+       of '--include-paths'.
 --no-minimize-url;;
        When tracking multiple directories (using --stdlayout,
        --branches, or --tags options), git svn will attempt to connect
@@ -146,6 +150,14 @@ Skip "branches" and "tags" of first level directories;;
 ------------------------------------------------------------------------
 --
 
+--include-paths=<regex>;;
+       This allows one to specify a Perl regular expression that will
+       cause the inclusion of only matching paths from checkout from SVN.
+       The '--include-paths' option should match for every 'fetch'
+       (including automatic fetches due to 'clone', 'dcommit',
+       'rebase', etc) on a given repository. '--ignore-paths' takes
+       precedence over '--include-paths'.
+
 --log-window-size=<n>;;
     Fetch <n> log entries per request when scanning Subversion history.
     The default is 100. For very large Subversion repositories, larger
@@ -244,7 +256,7 @@ first have already been pushed into SVN.
        For each patch, one may answer "yes" (accept this patch), "no" (discard this
        patch), "all" (accept all patches), or "quit".
        +
-       'git svn dcommit' returns immediately if answer if "no" or "quit", without
+       'git svn dcommit' returns immediately if answer is "no" or "quit", without
        committing anything to SVN.
 
 'branch'::
@@ -259,13 +271,15 @@ first have already been pushed into SVN.
        Create a tag by using the tags_subdir instead of the branches_subdir
        specified during git svn init.
 
--d;;
---destination;;
+-d<path>;;
+--destination=<path>;;
+
        If more than one --branches (or --tags) option was given to the 'init'
        or 'clone' command, you must provide the location of the branch (or
-       tag) you wish to create in the SVN repository.  The value of this
-       option must match one of the paths specified by a --branches (or
-       --tags) option.  You can see these paths with the commands
+       tag) you wish to create in the SVN repository.  <path> specifies which
+       path to use to create the branch or tag and should match the pattern
+       on the left-hand side of one of the configured branches or tags
+       refspecs.  You can see these refspecs with the commands
 +
        git config --get-all svn-remote.<name>.branches
        git config --get-all svn-remote.<name>.tags
@@ -286,6 +300,11 @@ where <name> is the name of the SVN repository as specified by the -R option to
        git config --get-all svn-remote.<name>.commiturl
 +
 
+--parents;;
+       Create parent folders. This parameter is equivalent to the parameter
+       --parents on svn cp commands and is useful for non-standard repository
+       layouts.
+
 'tag'::
        Create a tag in the SVN repository. This is a shorthand for
        'branch -t'.
@@ -1020,6 +1039,25 @@ comma-separated list of names within braces. For example:
        tags = tags/{1.0,2.0}/src:refs/remotes/tags/*
 ------------------------------------------------------------------------
 
+Multiple fetch, branches, and tags keys are supported:
+
+------------------------------------------------------------------------
+[svn-remote "messy-repo"]
+       url = http://server.org/svn
+       fetch = trunk/project-a:refs/remotes/project-a/trunk
+       fetch = branches/demos/june-project-a-demo:refs/remotes/project-a/demos/june-demo
+       branches = branches/server/*:refs/remotes/project-a/branches/*
+       branches = branches/demos/2011/*:refs/remotes/project-a/2011-demos/*
+       tags = tags/server/*:refs/remotes/project-a/tags/*
+------------------------------------------------------------------------
+
+Creating a branch in such a configuration requires disambiguating which
+location to use using the -d or --destination flag:
+
+------------------------------------------------------------------------
+$ git svn branch -d branches/server release-2-3-0
+------------------------------------------------------------------------
+
 Note that git-svn keeps track of the highest revision in which a branch
 or tag has appeared. If the subset of branches or tags is changed after
 fetching, then .git/svn/.metadata must be manually edited to remove (or
index b21aa87fe87a26f738c971f8c3dffdcf44fb13cd..c418c44d40d1494d2f87d6ed45c229a56064f0b3 100644 (file)
@@ -33,7 +33,7 @@ in the tag message.
 If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
 are absent, `-a` is implied.
 
-Otherwise just a tag reference for the SHA1 object name of the commit object is
+Otherwise just a tag reference for the SHA-1 object name of the commit object is
 created (i.e. a lightweight tag).
 
 A GnuPG signed tag object will be created when `-s` or `-u
@@ -42,6 +42,17 @@ committer identity for the current user is used to find the
 GnuPG key for signing.         The configuration variable `gpg.program`
 is used to specify custom GnuPG binary.
 
+Tag objects (created with `-a`, `s`, or `-u`) are called "annotated"
+tags; they contain a creation date, the tagger name and e-mail, a
+tagging message, and an optional GnuPG signature. Whereas a
+"lightweight" tag is simply a name for an object (usually a commit
+object).
+
+Annotated tags are meant for release while lightweight tags are meant
+for private or temporary object labels. For this reason, some git
+commands for naming objects (like `git describe`) will ignore
+lightweight tags by default.
+
 
 OPTIONS
 -------
index c92775829bd26d3d993600fd5bb104e527de59f4..e0a87029cdfe9fadd9edd4cc493bedd6b5044ac3 100644 (file)
@@ -14,8 +14,8 @@ SYNOPSIS
             [--refresh] [-q] [--unmerged] [--ignore-missing]
             [(--cacheinfo <mode> <object> <file>)...]
             [--chmod=(+|-)x]
-            [--assume-unchanged | --no-assume-unchanged]
-            [--skip-worktree | --no-skip-worktree]
+            [--[no-]assume-unchanged]
+            [--[no-]skip-worktree]
             [--ignore-submodules]
             [--really-refresh] [--unresolve] [--again | -g]
             [--info-only] [--index-info]
@@ -77,8 +77,7 @@ OPTIONS
 --chmod=(+|-)x::
         Set the execute permissions on the updated files.
 
---assume-unchanged::
---no-assume-unchanged::
+--[no-]assume-unchanged::
        When these flags are specified, the object names recorded
        for the paths are not updated.  Instead, these options
        set and unset the "assume unchanged" bit for the
@@ -102,8 +101,7 @@ you will need to handle the situation manually.
        Like '--refresh', but checks stat information unconditionally,
        without regard to the "assume unchanged" setting.
 
---skip-worktree::
---no-skip-worktree::
+--[no-]skip-worktree::
        When one of these flags is specified, the object name recorded
        for the paths are not updated. Instead, these options
        set and unset the "skip-worktree" bit for the paths. See
@@ -247,7 +245,7 @@ $ git update-index --index-info
 ------------
 
 The first line of the input feeds 0 as the mode to remove the
-path; the SHA1 does not matter as long as it is well formatted.
+path; the SHA-1 does not matter as long as it is well formatted.
 Then the second and third line feeds stage 1 and stage 2 entries
 for that path.  After the above, we would end up with this:
 
index 0eb9ffbdd2e691c24ed07eb8c97828bc3a81b427..526ba7be9ca08ff1925a387a4d98a9a3aaf867a8 100644 (file)
@@ -40,11 +40,11 @@ OUTPUT FORMAT
 -------------
 When specifying the -v option the format used is:
 
-       SHA1 type size size-in-pack-file offset-in-packfile
+       SHA-1 type size size-in-pack-file offset-in-packfile
 
 for objects that are not deltified in the pack, and
 
-       SHA1 type size size-in-packfile offset-in-packfile depth base-SHA1
+       SHA-1 type size size-in-packfile offset-in-packfile depth base-SHA-1
 
 for objects that are deltified.
 
index e996135be900067dc4054fc8eec20404d1b61380..f88ba96f023ac9f497f01ef699d0931a6d14e59a 100644 (file)
@@ -21,7 +21,7 @@ OPTIONS
        Print the contents of the tag object before validating it.
 
 <tag>...::
-       SHA1 identifiers of Git tag objects.
+       SHA-1 identifiers of Git tag objects.
 
 GIT
 ---
index ba79cb4f35a1f525c98c0bc6b95acf4c46ddc1dc..5aec4ecffb8c9dd58c444319d24064c8fee1770e 100644 (file)
@@ -34,6 +34,7 @@ The following browsers (or commands) are currently supported:
 * dillo
 * open (this is the default under Mac OS X GUI)
 * start (this is the default under MinGW)
+* cygstart (this is the default under Cygwin)
 
 Custom commands may also be specified.
 
index 6a875f2adefe34e27bc0fc3b6fdb9d7bcfd23301..5b83e0ab98b1da06c749b016f231584e644a73c0 100644 (file)
@@ -43,10 +43,27 @@ unreleased) version of Git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.8.2.1/git.html[documentation for release 1.8.2.1]
+* link:v1.8.4.1/git.html[documentation for release 1.8.4.1]
 
 * release notes for
-  link:RelNotes/1.8.2.1.txt[1.8.2.1].
+  link:RelNotes/1.8.4.1.txt[1.8.4.1],
+  link:RelNotes/1.8.4.txt[1.8.4].
+
+* link:v1.8.3.4/git.html[documentation for release 1.8.3.4]
+
+* release notes for
+  link:RelNotes/1.8.3.4.txt[1.8.3.4],
+  link:RelNotes/1.8.3.3.txt[1.8.3.3],
+  link:RelNotes/1.8.3.2.txt[1.8.3.2],
+  link:RelNotes/1.8.3.1.txt[1.8.3.1],
+  link:RelNotes/1.8.3.txt[1.8.3].
+
+* link:v1.8.2.3/git.html[documentation for release 1.8.2.3]
+
+* release notes for
+  link:RelNotes/1.8.2.3.txt[1.8.2.3],
+  link:RelNotes/1.8.2.2.txt[1.8.2.2],
+  link:RelNotes/1.8.2.1.txt[1.8.2.1],
   link:RelNotes/1.8.2.txt[1.8.2].
 
 * link:v1.8.1.6/git.html[documentation for release 1.8.1.6]
@@ -675,9 +692,7 @@ Git so take care if using Cogito etc.
        The '--git-dir' command-line option also sets this value.
 
 'GIT_WORK_TREE'::
-       Set the path to the working tree.  The value will not be
-       used in combination with repositories found automatically in
-       a .git directory (i.e. $GIT_DIR is not set).
+       Set the path to the root of the working tree.
        This can also be controlled by the '--work-tree' command line
        option and the core.worktree configuration variable.
 
@@ -741,7 +756,7 @@ where:
 
        <old|new>-file:: are files GIT_EXTERNAL_DIFF can use to read the
                          contents of <old|new>,
-       <old|new>-hex:: are the 40-hexdigit SHA1 hashes,
+       <old|new>-hex:: are the 40-hexdigit SHA-1 hashes,
        <old|new>-mode:: are the octal representation of the file modes.
 +
 The file parameters can point at the user's working file
@@ -809,8 +824,9 @@ for further details.
 'GIT_FLUSH'::
        If this environment variable is set to "1", then commands such
        as 'git blame' (in incremental mode), 'git rev-list', 'git log',
-       and 'git whatchanged' will force a flush of the output stream
-       after each commit-oriented record have been flushed.   If this
+       'git check-attr', 'git check-ignore', and 'git whatchanged' will
+       force a flush of the output stream after each record have been
+       flushed. If this
        variable is set to "0", the output of these commands will be done
        using completely buffered I/O.   If this environment variable is
        not set, Git will choose buffered or record-oriented flushing
@@ -830,6 +846,19 @@ for further details.
        as a file path and will try to write the trace messages
        into it.
 
+'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
+       access, the pack file name and an offset in the pack is
+       recorded. This may be helpful for troubleshooting some
+       pack-related performance problems.
+
+'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".
+
 GIT_LITERAL_PATHSPECS::
        Setting this variable to `1` will cause Git to treat all
        pathspecs literally, rather than as glob patterns. For example,
@@ -864,7 +893,7 @@ The commit, equivalent to what other systems call a "changeset" or
 represents an immediately preceding step.  Commits with more than one
 parent represent merges of independent lines of development.
 
-All objects are named by the SHA1 hash of their contents, normally
+All objects are named by the SHA-1 hash of their contents, normally
 written as a string of 40 hex digits.  Such names are globally unique.
 The entire history leading up to a commit can be vouched for by signing
 just that commit.  A fourth object type, the tag, is provided for this
@@ -874,9 +903,9 @@ When first created, objects are stored in individual files, but for
 efficiency may later be compressed together into "pack files".
 
 Named pointers called refs mark interesting points in history.  A ref
-may contain the SHA1 name of an object or the name of another ref.  Refs
-with names beginning `ref/head/` contain the SHA1 name of the most
-recent commit (or "head") of a branch under development.  SHA1 names of
+may contain the SHA-1 name of an object or the name of another ref.  Refs
+with names beginning `ref/head/` contain the SHA-1 name of the most
+recent commit (or "head") of a branch under development.  SHA-1 names of
 tags of interest are stored under `ref/tags/`.  A special ref named
 `HEAD` contains the name of the currently checked-out branch.
 
index 59c1c17cca863d2b90c0ac3d7dabd37918473bdf..f538a870c72dec8a022c23d5023cf5252e9925cf 100644 (file)
@@ -106,9 +106,9 @@ branch. A number of the Git tools will assume that `.git/HEAD` is
 valid, though.
 
 [NOTE]
-An 'object' is identified by its 160-bit SHA1 hash, aka 'object name',
+An 'object' is identified by its 160-bit SHA-1 hash, aka 'object name',
 and a reference to an object is always the 40-byte hex
-representation of that SHA1 name. The files in the `refs`
+representation of that SHA-1 name. The files in the `refs`
 subdirectory are expected to contain these hex references
 (usually with a final `\n` at the end), and you should thus
 expect to see a number of 41-byte files containing these
@@ -763,7 +763,7 @@ already discussed, the `HEAD` branch is nothing but a symlink to one of
 these object pointers.
 
 You can at any time create a new branch by just picking an arbitrary
-point in the project history, and just writing the SHA1 name of that
+point in the project history, and just writing the SHA-1 name of that
 object into a file under `.git/refs/heads/`. You can use any filename you
 want (and indeed, subdirectories), but the convention is that the
 "normal" branch is called `master`. That's just a convention, though,
@@ -1233,7 +1233,7 @@ file (the first tree goes to stage 1, the second to stage 2,
 etc.).  After reading three trees into three stages, the paths
 that are the same in all three stages are 'collapsed' into stage
 0.  Also paths that are the same in two of three stages are
-collapsed into stage 0, taking the SHA1 from either stage 2 or
+collapsed into stage 0, taking the SHA-1 from either stage 2 or
 stage 3, whichever is different from stage 1 (i.e. only one side
 changed from the common ancestor).
 
index 4ed71c76cb4ee7855521b1a04c2bddf6d63d23a8..c8b3e51c84a58d6f9e4a0ba324f9071cc125881f 100644 (file)
@@ -108,7 +108,7 @@ it changes it to:
 For the purpose of breaking a filepair, diffcore-break examines
 the extent of changes between the contents of the files before
 and after modification (i.e. the contents that have "bcd1234..."
-and "0123456..." as their SHA1 content ID, in the above
+and "0123456..." as their SHA-1 content ID, in the above
 example).  The amount of deletion of original contents and
 insertion of new material are added together, and if it exceeds
 the "break score", the filepair is broken into two.  The break
@@ -222,26 +222,35 @@ version prefixed with '+'.
 diffcore-pickaxe: For Detecting Addition/Deletion of Specified String
 ---------------------------------------------------------------------
 
-This transformation is used to find filepairs that represent
-changes that touch a specified string, and is controlled by the
--S option and the `--pickaxe-all` option to the 'git diff-*'
-commands.
-
-When diffcore-pickaxe is in use, it checks if there are
-filepairs whose "result" side and whose "origin" side have
-different number of specified string.  Such a filepair represents
-"the string appeared in this changeset".  It also checks for the
-opposite case that loses the specified string.
-
-When `--pickaxe-all` is not in effect, diffcore-pickaxe leaves
-only such filepairs that touch the specified string in its
-output.  When `--pickaxe-all` is used, diffcore-pickaxe leaves all
-filepairs intact if there is such a filepair, or makes the
-output empty otherwise.  The latter behaviour is designed to
-make reviewing of the changes in the context of the whole
+This transformation limits the set of filepairs to those that change
+specified strings between the preimage and the postimage in a certain
+way.  -S<block of text> and -G<regular expression> options are used to
+specify different ways these strings are sought.
+
+"-S<block of text>" detects filepairs whose preimage and postimage
+have different number of occurrences of the specified block of text.
+By definition, it will not detect in-file moves.  Also, when a
+changeset moves a file wholesale without affecting the interesting
+string, diffcore-rename kicks in as usual, and `-S` omits the filepair
+(since the number of occurrences of that string didn't change in that
+rename-detected filepair).  When used with `--pickaxe-regex`, treat
+the <block of text> as an extended POSIX regular expression to match,
+instead of a literal string.
+
+"-G<regular expression>" (mnemonic: grep) detects filepairs whose
+textual diff has an added or a deleted line that matches the given
+regular expression.  This means that it will detect in-file (or what
+rename-detection considers the same file) moves, which is noise.  The
+implementation runs diff twice and greps, and this can be quite
+expensive.
+
+When `-S` or `-G` are used without `--pickaxe-all`, only filepairs
+that match their respective criterion are kept in the output.  When
+`--pickaxe-all` is used, if even one filepair matches their respective
+criterion in a changeset, the entire changeset is kept.  This behavior
+is designed to make reviewing changes in the context of the whole
 changeset easier.
 
-
 diffcore-order: For Sorting the Output Based on Filenames
 ---------------------------------------------------------
 
index dc6693fe482e2311a7d0ed5b5e60abec7c150c0c..d48bf4d6fae12a21232bc74ddeb230885c1bcbb9 100644 (file)
@@ -99,7 +99,7 @@ given); `template` (if a `-t` option was given or the
 configuration option `commit.template` is set); `merge` (if the
 commit is a merge or a `.git/MERGE_MSG` file exists); `squash`
 (if a `.git/SQUASH_MSG` file exists); or `commit`, followed by
-a commit SHA1 (if a `-c`, `-C` or `--amend` option was given).
+a commit SHA-1 (if a `-c`, `-C` or `--amend` option was given).
 
 If the exit status is non-zero, 'git commit' will abort.
 
@@ -196,11 +196,11 @@ hook would receive a line like the following:
 
   refs/heads/master 67890 refs/heads/foreign 12345
 
-although the full, 40-character SHA1s would be supplied.  If the foreign ref
-does not yet exist the `<remote SHA1>` will be 40 `0`.  If a ref is to be
+although the full, 40-character SHA-1s would be supplied.  If the foreign ref
+does not yet exist the `<remote SHA-1>` will be 40 `0`.  If a ref is to be
 deleted, the `<local ref>` will be supplied as `(delete)` and the `<local
-SHA1>` will be 40 `0`.  If the local commit was specified by something other
-than a name which could be expanded (such as `HEAD~`, or a SHA1) it will be
+SHA-1>` will be 40 `0`.  If the local commit was specified by something other
+than a name which could be expanded (such as `HEAD~`, or a SHA-1) it will be
 supplied as it was originally given.
 
 If this hook exits with a non-zero status, 'git push' will abort without
index f506031ae49070e216e71914d8ef241da58430e1..0827f691396ba698d3976eba1eddedd9e7ff3e9e 100644 (file)
@@ -159,11 +159,11 @@ Miscellaneous capabilities
        carried out.
 
 'refspec' <refspec>::
-       This modifies the 'import' capability, allowing the produced
-       fast-import stream to modify refs in a private namespace
-       instead of writing to refs/heads or refs/remotes directly.
+       For remote helpers that implement 'import' or 'export', this capability
+       allows the refs to be constrained to a private namespace, instead of
+       writing to refs/heads or refs/remotes directly.
        It is recommended that all importers providing the 'import'
-       capability use this.
+       capability use this. It's mandatory for 'export'.
 +
 A helper advertising the capability
 `refspec refs/heads/*:refs/svn/origin/branches/*`
@@ -174,8 +174,8 @@ ref.
 This capability can be advertised multiple times.  The first
 applicable refspec takes precedence.  The left-hand of refspecs
 advertised with this capability must cover all refs reported by
-the list command.  If a helper does not need a specific 'refspec'
-capability then it should advertise `refspec *:*`.
+the list command.  If no 'refspec' capability is advertised,
+there is an implied `refspec *:*`.
 
 'bidi-import'::
        This modifies the 'import' capability.
@@ -202,6 +202,10 @@ capability then it should advertise `refspec *:*`.
        marks specified in <file> before processing any input. For details,
        read up on '--import-marks=<file>' in linkgit:git-fast-export[1].
 
+'signed-tags'::
+       This modifies the 'export' capability, instructing Git to pass
+       '--signed-tags=verbatim' to linkgit:git-fast-export[1].  In the
+       absence of this capability, Git will use '--signed-tags=warn-strip'.
 
 
 
index f0eef765b96284fe42915bea18ea5fa6073d0dfe..aa03882ddb119d2e61da50079a72e1a6ab12f636 100644 (file)
@@ -106,7 +106,7 @@ refs/remotes/`name`::
        from a remote repository.
 
 refs/replace/`<obj-sha1>`::
-       records the SHA1 of the object that replaces `<obj-sha1>`.
+       records the SHA-1 of the object that replaces `<obj-sha1>`.
        This is similar to info/grafts and is internally used and
        maintained by linkgit:git-replace[1]. Such refs can be exchanged
        between repositories while grafts are not.
@@ -184,6 +184,10 @@ info/exclude::
        'git clean' look at it but the core Git commands do not look
        at it.  See also: linkgit:gitignore[5].
 
+info/sparse-checkout::
+       This file stores sparse checkout patterns.
+       See also: linkgit:git-read-tree[1].
+
 remotes::
        Stores shorthands for URL and default refnames for use
        when interacting with remote repositories via 'git fetch',
@@ -207,6 +211,9 @@ shallow::
        and maintained by shallow clone mechanism.  See `--depth`
        option to linkgit:git-clone[1] and linkgit:git-fetch[1].
 
+modules::
+       Contains the git-repositories of the submodules.
+
 SEE ALSO
 --------
 linkgit:git-init[1],
index 94c906eda8d65c940252def28399e724967e24f8..3109ea8aade1ceb47de67823388d865a1505e616 100644 (file)
@@ -46,9 +46,9 @@ What are the 7 digits of hex that Git responded to the commit with?
 
 We saw in part one of the tutorial that commits have names like this.
 It turns out that every object in the Git history is stored under
-a 40-digit hex name.  That name is the SHA1 hash of the object's
+a 40-digit hex name.  That name is the SHA-1 hash of the object's
 contents; among other things, this ensures that Git will never store
-the same data twice (since identical data is given an identical SHA1
+the same data twice (since identical data is given an identical SHA-1
 name), and that the contents of a Git object will never change (since
 that would change the object's name as well). The 7 char hex strings
 here are simply the abbreviation of such 40 character long strings.
@@ -56,7 +56,7 @@ Abbreviations can be used everywhere where the 40 character strings
 can be used, so long as they are unambiguous.
 
 It is expected that the content of the commit object you created while
-following the example above generates a different SHA1 hash than
+following the example above generates a different SHA-1 hash than
 the one shown above because the commit object records the time when
 it was created and the name of the person performing the commit.
 
@@ -80,14 +80,14 @@ A tree can refer to one or more "blob" objects, each corresponding to
 a file.  In addition, a tree can also refer to other tree objects,
 thus creating a directory hierarchy.  You can examine the contents of
 any tree using ls-tree (remember that a long enough initial portion
-of the SHA1 will also work):
+of the SHA-1 will also work):
 
 ------------------------------------------------
 $ git ls-tree 92b8b694
 100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad    file.txt
 ------------------------------------------------
 
-Thus we see that this tree has one file in it.  The SHA1 hash is a
+Thus we see that this tree has one file in it.  The SHA-1 hash is a
 reference to that file's data:
 
 ------------------------------------------------
@@ -106,7 +106,7 @@ Note that this is the old file data; so the object that Git named in
 its response to the initial tree was a tree with a snapshot of the
 directory state that was recorded by the first commit.
 
-All of these objects are stored under their SHA1 names inside the Git
+All of these objects are stored under their SHA-1 names inside the Git
 directory:
 
 ------------------------------------------------
@@ -142,7 +142,7 @@ ref: refs/heads/master
 
 As you can see, this tells us which branch we're currently on, and it
 tells us this by naming a file under the .git directory, which itself
-contains a SHA1 name referring to a commit object, which we can
+contains a SHA-1 name referring to a commit object, which we can
 examine with cat-file:
 
 ------------------------------------------------
@@ -208,7 +208,7 @@ project's history:
 
 Note, by the way, that lots of commands take a tree as an argument.
 But as we can see above, a tree can be referred to in many different
-ways--by the SHA1 name for that tree, by the name of a commit that
+ways--by the SHA-1 name for that tree, by the name of a commit that
 refers to the tree, by the name of a branch whose head refers to that
 tree, etc.--and most such commands can accept any of these names.
 
index eb636317be501b6d448f0f7b861952c74b9ff2e0..305db633ccb1165e819a1061e8ec8b60e33e9eed 100644 (file)
@@ -336,8 +336,26 @@ $home_link_str::
        used as the first component of gitweb's "breadcrumb trail":
        `<home link> / <project> / <action>`.  Can be set at build time using
        the `GITWEB_HOME_LINK_STR` variable.  By default it is set to "projects",
-       as this link leads to the list of projects.  Other popular choice it to
-       set it to the name of site.
+       as this link leads to the list of projects.  Another popular choice is to
+       set it to the name of site.  Note that it is treated as raw HTML so it
+       should not be set from untrusted sources.
+
+@extra_breadcrumbs::
+       Additional links to be added to the start of the breadcrumb trail before
+       the home link, to pages that are logically "above" the gitweb projects
+       list, such as the organization and department which host the gitweb
+       server. Each element of the list is a reference to an array, in which
+       element 0 is the link text (equivalent to `$home_link_str`) and element
+       1 is the target URL (equivalent to `$home_link`).
++
+For example, the following setting produces a breadcrumb trail like
+"home / dev / projects / ..." where "projects" is the home link.
+----------------------------------------------------------------------------
+    our @extra_breadcrumbs = (
+      [ 'home' => 'https://www.example.org/' ],
+      [ 'dev'  => 'https://dev.example.org/' ],
+    );
+----------------------------------------------------------------------------
 
 $logo_url::
 $logo_label::
@@ -857,6 +875,13 @@ adding the following lines to your gitweb configuration file:
        $known_snapshot_formats{'zip'}{'disabled'} = 1;
        $known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6'];
 
+BUGS
+----
+Debugging would be easier if the fallback configuration file
+(`/etc/gitweb.conf`) and environment variable to override its location
+('GITWEB_CONFIG_SYSTEM') had names reflecting their "fallback" role.
+The current names are kept to avoid breaking working setups.
+
 ENVIRONMENT
 -----------
 The location of per-instance and system-wide configuration files can be
index 40969f10980dbe829b748eaadb71644f28c65870..cca14b8cc365c021e8da629ebfd56acd66ee14ca 100644 (file)
@@ -504,7 +504,7 @@ repositories, you can configure Apache like this:
 
 The above configuration expects your public repositories to live under
 '/pub/git' and will serve them as `http://git.domain.org/dir-under-pub-git`,
-both as cloneable Git URL and as browseable gitweb interface.  If you then
+both as clonable Git URL and as browseable gitweb interface.  If you then
 start your linkgit:git-daemon[1] with `--base-path=/pub/git --export-all`
 then you can even use the `git://` URL with exactly the same path.
 
index 7a79f26231c2ffa0cd0bfb7ffb9274803e32d8be..dba5062b379cdfcdde31ee0f6809e964f979b5f5 100644 (file)
@@ -113,13 +113,10 @@ Note that commands that operate on the history of the current branch
 while the HEAD is detached. They update the HEAD to point at the tip
 of the updated history without affecting any branch.  Commands that
 update or inquire information _about_ the current branch (e.g. `git
-branch --set-upstream-to` that sets what remote tracking branch the
+branch --set-upstream-to` that sets what remote-tracking branch the
 current branch integrates with) obviously do not work, as there is no
 (real) current branch to ask about in this state.
 
-[[def_dircache]]dircache::
-       You are *waaaaay* behind. See <<def_index,index>>.
-
 [[def_directory]]directory::
        The list you get with "ls" :-)
 
@@ -128,11 +125,6 @@ current branch integrates with) obviously do not work, as there is no
        it contains modifications which have not been <<def_commit,committed>> to the current
        <<def_branch,branch>>.
 
-[[def_ent]]ent::
-       Favorite synonym to "<<def_tree-ish,tree-ish>>" by some total geeks. See
-       http://en.wikipedia.org/wiki/Ent_(Middle-earth) for an in-depth
-       explanation. Avoid this term, not to confuse people.
-
 [[def_evil_merge]]evil merge::
        An evil merge is a <<def_merge,merge>> that introduces changes that
        do not appear in any <<def_parent,parent>>.
@@ -174,7 +166,7 @@ current branch integrates with) obviously do not work, as there is no
        created. Configured via the `.git/info/grafts` file.
 
 [[def_hash]]hash::
-       In Git's context, synonym to <<def_object_name,object name>>.
+       In Git's context, synonym for <<def_object_name,object name>>.
 
 [[def_head]]head::
        A <<def_ref,named reference>> to the <<def_commit,commit>> at the tip of a
@@ -246,7 +238,7 @@ This commit is referred to as a "merge commit", or sometimes just a
 
 [[def_object]]object::
        The unit of storage in Git. It is uniquely identified by the
-       <<def_SHA1,SHA1>> of its contents. Consequently, an
+       <<def_SHA1,SHA-1>> of its contents. Consequently, an
        object can not be changed.
 
 [[def_object_database]]object database::
@@ -258,10 +250,9 @@ This commit is referred to as a "merge commit", or sometimes just a
        Synonym for <<def_object_name,object name>>.
 
 [[def_object_name]]object name::
-       The unique identifier of an <<def_object,object>>. The <<def_hash,hash>>
-       of the object's contents using the Secure Hash Algorithm
-       1 and usually represented by the 40 character hexadecimal encoding of
-       the <<def_hash,hash>> of the object.
+       The unique identifier of an <<def_object,object>>.  The
+       object name is usually represented by a 40 character
+       hexadecimal string.  Also colloquially called <<def_SHA1,SHA-1>>.
 
 [[def_object_type]]object type::
        One of the identifiers "<<def_commit_object,commit>>",
@@ -270,14 +261,13 @@ This commit is referred to as a "merge commit", or sometimes just a
        <<def_object,object>>.
 
 [[def_octopus]]octopus::
-       To <<def_merge,merge>> more than two <<def_branch,branches>>. Also denotes an
-       intelligent predator.
+       To <<def_merge,merge>> more than two <<def_branch,branches>>.
 
 [[def_origin]]origin::
        The default upstream <<def_repository,repository>>. Most projects have
        at least one upstream project which they track. By default
        'origin' is used for that purpose. New upstream updates
-       will be fetched into remote <<def_remote_tracking_branch,remote-tracking branches>> named
+       will be fetched into <<def_remote_tracking_branch,remote-tracking branches>> named
        origin/name-of-upstream-branch, which you can see using
        `git branch -r`.
 
@@ -291,7 +281,7 @@ This commit is referred to as a "merge commit", or sometimes just a
        pack.
 
 [[def_pathspec]]pathspec::
-       Pattern used to specify paths.
+       Pattern used to limit paths in Git commands.
 +
 Pathspecs are used on the command line of "git ls-files", "git
 ls-tree", "git add", "git grep", "git diff", "git checkout",
@@ -300,6 +290,8 @@ limit the scope of operations to some subset of the tree or
 worktree.  See the documentation of each command for whether
 paths are relative to the current directory or toplevel.  The
 pathspec syntax is as follows:
++
+--
 
 * any path matches itself
 * the pathspec up to the last slash represents a
@@ -309,11 +301,12 @@ pathspec syntax is as follows:
   of the pathname.  Paths relative to the directory
   prefix will be matched against that pattern using fnmatch(3);
   in particular, '*' and '?' _can_ match directory separators.
+
+--
 +
 For example, Documentation/*.jpg will match all .jpg files
 in the Documentation subtree,
 including Documentation/chapter_1/figure_1.jpg.
-
 +
 A pathspec that begins with a colon `:` has special meaning.  In the
 short form, the leading colon `:` is followed by zero or more "magic
@@ -329,18 +322,10 @@ and a close parentheses `)`, and the remainder is the pattern to match
 against the path.
 +
 The "magic signature" consists of an ASCII symbol that is not
-alphanumeric.
-+
---
-top `/`;;
-       The magic word `top` (mnemonic: `/`) makes the pattern match
-       from the root of the working tree, even when you are running
-       the command from inside a subdirectory.
---
-+
-Currently only the slash `/` is recognized as the "magic signature",
-but it is envisioned that we will support more types of magic in later
-versions of Git.
+alphanumeric. Currently only the slash `/` is recognized as a
+"magic signature": it makes the pattern match from the root of
+the working tree, even when you are running the command from
+inside a subdirectory.
 +
 A pathspec with only a colon means "there is no pathspec". This form
 should not be combined with other pathspec.
@@ -398,7 +383,7 @@ should not be combined with other pathspec.
        to the result.
 
 [[def_ref]]ref::
-       A 40-byte hex representation of a <<def_SHA1,SHA1>> or a name that
+       A 40-byte hex representation of a <<def_SHA1,SHA-1>> or a name that
        denotes a particular <<def_object,object>>. They may be stored in
        a file under `$GIT_DIR/refs/` directory, or
        in the `$GIT_DIR/packed-refs` file.
@@ -412,15 +397,7 @@ should not be combined with other pathspec.
 [[def_refspec]]refspec::
        A "refspec" is used by <<def_fetch,fetch>> and
        <<def_push,push>> to describe the mapping between remote
-       <<def_ref,ref>> and local ref. They are combined with a colon in
-       the format <src>:<dst>, preceded by an optional plus sign, +.
-       For example: `git fetch $URL
-       refs/heads/master:refs/heads/origin` means "grab the master
-       <<def_branch,branch>> <<def_head,head>> from the $URL and store
-       it as my origin branch head". And `git push
-       $URL refs/heads/master:refs/heads/to-upstream` means "publish my
-       master branch head as to-upstream branch at $URL". See also
-       linkgit:git-push[1].
+       <<def_ref,ref>> and local ref.
 
 [[def_remote_tracking_branch]]remote-tracking branch::
        A <<def_ref,ref>> that is used to follow changes from another
@@ -444,9 +421,7 @@ should not be combined with other pathspec.
        <<def_merge,merge>> left behind.
 
 [[def_revision]]revision::
-       A particular state of files and directories which was stored in the
-       <<def_object_database,object database>>. It is referenced by a
-       <<def_commit_object,commit object>>.
+       Synonym for <<def_commit,commit>> (the noun).
 
 [[def_rewind]]rewind::
        To throw away part of the development, i.e. to assign the
@@ -455,8 +430,9 @@ should not be combined with other pathspec.
 [[def_SCM]]SCM::
        Source code management (tool).
 
-[[def_SHA1]]SHA1::
-       Synonym for <<def_object_name,object name>>.
+[[def_SHA1]]SHA-1::
+       "Secure Hash Algorithm 1"; a cryptographic hash function.
+       In the context of Git used as a synonym for <<def_object_name,object name>>.
 
 [[def_shallow_repository]]shallow repository::
        A shallow <<def_repository,repository>> has an incomplete
@@ -470,7 +446,7 @@ should not be combined with other pathspec.
        its history can be later deepened with linkgit:git-fetch[1].
 
 [[def_symref]]symref::
-       Symbolic reference: instead of containing the <<def_SHA1,SHA1>>
+       Symbolic reference: instead of containing the <<def_SHA1,SHA-1>>
        id itself, it is of the format 'ref: refs/some/thing' and when
        referenced, it recursively dereferences to this reference.
        '<<def_HEAD,HEAD>>' is a prime example of a symref. Symbolic
index 2abc3a0a0e7bc60c40211ce630fdb098848f825e..d7de5a3e9ed236c87a62b577ee5978e02d855a2a 100644 (file)
@@ -94,7 +94,7 @@ your language, document it in the INSTALL file.
 6. There is a file command-list.txt in the distribution main directory
 that categorizes commands by type, so they can be listed in appropriate
 subsections in the documentation's summary command list.  Add an entry
-for yours.  To understand the categories, look at git-cmmands.txt
+for yours.  To understand the categories, look at git-commands.txt
 in the main directory.
 
 7. Give the maintainer one paragraph to include in the RelNotes file
index 6d362ceb10c798600a89540548c48c406b6ca4a9..1b3b188d3c1579ac6cac7c4dcefcff1024249206 100644 (file)
@@ -15,7 +15,7 @@ On Fri, 9 Nov 2007, Yossi Leybovich wrote:
 > Any one know how can I track this object and understand which file is it
 -----------------------------------------------------------
 
-So exactly *because* the SHA1 hash is cryptographically secure, the hash
+So exactly *because* the SHA-1 hash is cryptographically secure, the hash
 itself doesn't actually tell you anything, in order to fix a corrupt
 object you basically have to find the "original source" for it.
 
@@ -44,7 +44,7 @@ So:
 -----------------------------------------------------------
 
 This is the right thing to do, although it's usually best to save it under
-it's full SHA1 name (you just dropped the "4b" from the result ;).
+it's full SHA-1 name (you just dropped the "4b" from the result ;).
 
 Let's see what that tells us:
 
@@ -89,7 +89,7 @@ working tree, in which case fixing this problem is really simple, just do
 
        git hash-object -w my-magic-file
 
-again, and if it outputs the missing SHA1 (4b945..) you're now all done!
+again, and if it outputs the missing SHA-1 (4b945..) you're now all done!
 
 But that's the really lucky case, so let's assume that it was some older
 version that was broken. How do you tell which version it was?
index 84dd839db43560d5d459f403904ebb3769521064..0d5419e1a9a6be235069f5807da08cc4e4052247 100644 (file)
@@ -12,7 +12,7 @@ How to revert an existing commit
 ================================
 
 One of the changes I pulled into the 'master' branch turns out to
-break building Git with GCC 2.95.  While they were well intentioned
+break building Git with GCC 2.95.  While they were well-intentioned
 portability fixes, keeping things working with gcc-2.95 was also
 important.  Here is what I did to revert the change in the 'master'
 branch and to adjust the 'pu' branch, using core Git tools and
diff --git a/Documentation/line-range-format.txt b/Documentation/line-range-format.txt
new file mode 100644 (file)
index 0000000..3e7ce72
--- /dev/null
@@ -0,0 +1,25 @@
+- number
++
+If <start> or <end> is a number, it specifies an
+absolute line number (lines count from 1).
++
+
+- /regex/
++
+This form will use the first line matching the given
+POSIX regex.  If <end> is a regex, it will search
+starting at the line given by <start>.
++
+
+- +offset or -offset
++
+This is only valid for <end> and will specify a number
+of lines before or after the line given by <start>.
++
+
+- :regex
++
+If the option's argument is of the form :regex, it denotes the range
+from the first funcname line that matches <regex>, up to the next
+funcname line.
++
index 2adccf8fec71c10f8223490f1be4b2a215ace5ea..afba8d4f3b2dcc952310c270daf26e01f8b30800 100644 (file)
@@ -8,12 +8,13 @@ failed and do not autocommit, to give the user a chance to
 inspect and further tweak the merge result before committing.
 
 --edit::
+-e::
 --no-edit::
        Invoke an editor before committing successful mechanical merge to
        further edit the auto-generated merge message, so that the user
        can explain and justify the merge. The `--no-edit` option can be
        used to accept the auto-generated message (this is generally
-       discouraged). The `--edit` option is still useful if you are
+       discouraged). The `--edit` (or `-e`) option is still useful if you are
        giving a draft message with the `-m` option from the command line
        and want to edit it in the editor.
 +
index afac703f21245b6fa34af90b1da4b3194372a81d..1d174fd0b6fa7f9fae3eb1523c8c6e2192e71e45 100644 (file)
@@ -75,7 +75,7 @@ This is designed to be as compact as possible.
 * 'raw'
 +
 The 'raw' format shows the entire commit exactly as
-stored in the commit object.  Notably, the SHA1s are
+stored in the commit object.  Notably, the SHA-1s are
 displayed in full, regardless of whether --abbrev or
 --no-abbrev are used, and 'parents' information show the
 true parent commits, without taking grafts nor history
@@ -106,18 +106,22 @@ The placeholders are:
 - '%P': parent hashes
 - '%p': abbreviated parent hashes
 - '%an': author name
-- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
+- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1]
+  or linkgit:git-blame[1])
 - '%ae': author email
-- '%aE': author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
+- '%aE': author email (respecting .mailmap, see
+  linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ad': author date (format respects --date= option)
 - '%aD': author date, RFC2822 style
 - '%ar': author date, relative
 - '%at': author date, UNIX timestamp
 - '%ai': author date, ISO 8601 format
 - '%cn': committer name
-- '%cN': committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
+- '%cN': committer name (respecting .mailmap, see
+  linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ce': committer email
-- '%cE': committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
+- '%cE': committer email (respecting .mailmap, see
+  linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%cd': committer date
 - '%cD': committer date, RFC2822 style
 - '%cr': committer date, relative
@@ -138,9 +142,11 @@ The placeholders are:
 - '%gD': reflog selector, e.g., `refs/stash@{1}`
 - '%gd': shortened reflog selector, e.g., `stash@{1}`
 - '%gn': reflog identity name
-- '%gN': reflog identity name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
+- '%gN': reflog identity name (respecting .mailmap, see
+  linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ge': reflog identity email
-- '%gE': reflog identity email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
+- '%gE': reflog identity email (respecting .mailmap, see
+  linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%gs': reflog subject
 - '%Cred': switch color to red
 - '%Cgreen': switch color to green
@@ -150,13 +156,28 @@ The placeholders are:
   adding `auto,` at the beginning will emit color only when colors are
   enabled for log output (by `color.diff`, `color.ui`, or `--color`, and
   respecting the `auto` settings of the former if we are going to a
-  terminal)
+  terminal). `auto` alone (i.e. `%C(auto)`) will turn on auto coloring
+  on the next placeholders until the color is switched again.
 - '%m': left, right or boundary mark
 - '%n': newline
 - '%%': a raw '%'
 - '%x00': print a byte from a hex code
 - '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of
   linkgit:git-shortlog[1].
+- '%<(<N>[,trunc|ltrunc|mtrunc])': make the next placeholder take at
+  least N columns, padding spaces on the right if necessary.
+  Optionally truncate at the beginning (ltrunc), the middle (mtrunc)
+  or the end (trunc) if the output is longer than N columns.
+  Note that truncating only works correctly with N >= 2.
+- '%<|(<N>)': make the next placeholder take at least until Nth
+  columns, padding spaces on the right if necessary
+- '%>(<N>)', '%>|(<N>)': similar to '%<(<N>)', '%<|(<N>)'
+  respectively, but padding spaces on the left
+- '%>>(<N>)', '%>>|(<N>)': similar to '%>(<N>)', '%>|(<N>)'
+  respectively, except that if the next placeholder takes more spaces
+  than given and there are spaces on its left, use those spaces
+- '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'
+  respectively, but padding both sides (i.e. the text is centered)
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
index 5e499421a43526e133c7bd3be134da803b35c00d..eea0e306a83834e75f44c1b932e5a7747195f85c 100644 (file)
@@ -28,7 +28,7 @@ people using 80-column terminals.
        This is a shorthand for "--pretty=oneline --abbrev-commit"
        used together.
 
---encoding[=<encoding>]::
+--encoding=<encoding>::
        The commit objects record the encoding used for the log message
        in their encoding header; this option can be used to tell the
        command to re-code the commit log message in the encoding
index 94a9d32f1d5bd66ac359103d21e671e08140b745..18cffc25b809fa3695b0cc60c732d058e8843ed6 100644 (file)
@@ -68,6 +68,11 @@ Some short-cut notations are also supported.
 +
 * `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`;
   it requests fetching everything up to the given tag.
-* A parameter <ref> without a colon is equivalent to
-  <ref>: when pulling/fetching, so it merges <ref> into the current
-  branch without storing the remote branch anywhere locally
+ifndef::git-pull[]
+* A parameter <ref> without a colon fetches that ref into FETCH_HEAD,
+endif::git-pull[]
+ifdef::git-pull[]
+* A parameter <ref> without a colon merges <ref> into the current
+  branch,
+endif::git-pull[]
+  and updates the remote-tracking branches (if any).
index 3bdbf5e856b68692745a91304d80b09186d487b4..5bdfb4285247770722434541700b0f7547e3a113 100644 (file)
@@ -119,7 +119,7 @@ if it is part of the log message.
 --no-min-parents::
 --no-max-parents::
 
-       Show only commits which have at least (or at most) that many
+       Show only commits which have at least (or at most) that many parent
        commits. In particular, `--max-parents=1` is the same as `--no-merges`,
        `--min-parents=2` is the same as `--merges`.  `--max-parents=0`
        gives all root commits and `--min-parents=3` all octopus merges.
@@ -271,8 +271,8 @@ See also linkgit:git-reflog[1].
 
 --boundary::
 
-       Output uninteresting commits at the boundary, which are usually
-       not shown.
+       Output excluded boundary commits. Boundary commits are
+       prefixed with `-`.
 
 --
 
@@ -342,13 +342,13 @@ In the following, we will always refer to the same example history to
 illustrate the differences between simplification settings.  We assume
 that you are filtering for a file `foo` in this commit graph:
 -----------------------------------------------------------------------
-         .-A---M---N---O---P
-        /     /   /   /   /
-       I     B   C   D   E
-        \   /   /   /   /
-         `-------------'
+         .-A---M---N---O---P---Q
+        /     /   /   /   /   /
+       I     B   C   D   E   Y
+        \   /   /   /   /   /
+         `-------------'   X
 -----------------------------------------------------------------------
-The horizontal line of history A---P is taken to be the first parent of
+The horizontal line of history A---Q is taken to be the first parent of
 each merge.  The commits are:
 
 * `I` is the initial commit, in which `foo` exists with contents
@@ -367,8 +367,11 @@ each merge.  The commits are:
   `N` and `D` to "foobarbaz"; i.e., it is not TREESAME to any parent.
 
 * `E` changes `quux` to "xyzzy", and its merge `P` combines the
-  strings to "quux xyzzy".  Despite appearing interesting, `P` is
-  TREESAME to all parents.
+  strings to "quux xyzzy".  `P` is TREESAME to `O`, but not to `E`.
+
+* `X` is an independent root commit that added a new file `side`, and `Y`
+  modified it. `Y` is TREESAME to `X`. Its merge `Q` added `side` to `P`, and
+  `Q` is TREESAME to `P`, but not to `Y`.
 
 'rev-list' walks backwards through history, including or excluding
 commits based on whether '\--full-history' and/or parent rewriting
@@ -410,10 +413,10 @@ parent lines.
        the example, we get
 +
 -----------------------------------------------------------------------
-       I  A  B  N  D  O
+       I  A  B  N  D  O  P  Q
 -----------------------------------------------------------------------
 +
-`P` and `M` were excluded because they are TREESAME to a parent.  `E`,
+`M` was excluded because it is TREESAME to both parents.  `E`,
 `C` and `B` were all walked, but only `B` was !TREESAME, so the others
 do not appear.
 +
@@ -431,7 +434,7 @@ Along each parent, prune away commits that are not included
 themselves.  This results in
 +
 -----------------------------------------------------------------------
-         .-A---M---N---O---P
+         .-A---M---N---O---P---Q
         /     /   /   /   /
        I     B   /   D   /
         \   /   /   /   /
@@ -441,7 +444,7 @@ themselves.  This results in
 Compare to '\--full-history' without rewriting above.  Note that `E`
 was pruned away because it is TREESAME, but the parent list of P was
 rewritten to contain `E`'s parent `I`.  The same happened for `C` and
-`N`.  Note also that `P` was included despite being TREESAME.
+`N`, and `X`, `Y` and `Q`.
 
 In addition to the above settings, you can change whether TREESAME
 affects inclusion:
@@ -471,8 +474,9 @@ history according to the following rules:
 * Set `C'` to `C`.
 +
 * Replace each parent `P` of `C'` with its simplification `P'`.  In
-  the process, drop parents that are ancestors of other parents, and
-  remove duplicates.
+  the process, drop parents that are ancestors of other parents or that are
+  root commits TREESAME to an empty tree, and remove duplicates, but take care
+  to never drop all parents that we are TREESAME to.
 +
 * If after this parent rewriting, `C'` is a root or merge commit (has
   zero or >1 parents), a boundary commit, or !TREESAME, it remains.
@@ -490,7 +494,7 @@ The effect of this is best shown by way of comparing to
          `---------'
 -----------------------------------------------------------------------
 +
-Note the major differences in `N` and `P` over '--full-history':
+Note the major differences in `N`, `P` and `Q` over '--full-history':
 +
 --
 * `N`'s parent list had `I` removed, because it is an ancestor of the
@@ -498,6 +502,10 @@ Note the major differences in `N` and `P` over '--full-history':
 +
 * `P`'s parent list similarly had `I` removed.  `P` was then
   removed completely, because it had one parent and is TREESAME.
++
+* `Q`'s parent list had `Y` simplified to `X`. `X` was then removed, because it
+  was a TREESAME root. `Q` was then removed completely, because it had one
+  parent and is TREESAME.
 --
 
 Finally, there is a fifth simplification mode available:
@@ -617,6 +625,10 @@ By default, the commits are shown in reverse chronological order.
        Show no parents before all of its children are shown, but
        otherwise show commits in the commit timestamp order.
 
+--author-date-order::
+       Show no parents before all of its children are shown, but
+       otherwise show commits in the author timestamp order.
+
 --topo-order::
        Show no parents before all of its children are shown, and
        avoid showing commits on multiple lines of history
@@ -837,7 +849,4 @@ options may be given. See linkgit:git-diff-files[1] for more options.
 -t::
 
        Show the tree objects in the diff output. This implies '-r'.
-
--s::
-       Suppress diff output.
 endif::git-rev-list[]
index 8855b1a0ac99012fcd98fcb3272b39e465d84626..d477b3f6bcb2c410952b573421d28189d1d1a790 100644 (file)
@@ -2,13 +2,13 @@ SPECIFYING REVISIONS
 --------------------
 
 A revision parameter '<rev>' typically, but not necessarily, names a
-commit object.  It uses what is called an 'extended SHA1'
+commit object.  It uses what is called an 'extended SHA-1'
 syntax.  Here are various ways to spell object names.  The
 ones listed near the end of this list name trees and
 blobs contained in a commit.
 
 '<sha1>', e.g. 'dae86e1950b1277e545cee180551750029cfe735', 'dae86e'::
-  The full SHA1 object name (40-byte hexadecimal string), or
+  The full SHA-1 object name (40-byte hexadecimal string), or
   a leading substring that is unique within the repository.
   E.g. dae86e1950b1277e545cee180551750029cfe735 and dae86e both
   name the same commit object if there is no other object in
@@ -244,11 +244,13 @@ To summarize:
 
 '<rev1>..<rev2>'::
        Include commits that are reachable from <rev2> but exclude
-       those that are reachable from <rev1>.
+       those that are reachable from <rev1>.  When either <rev1> or
+       <rev2> is omitted, it defaults to 'HEAD'.
 
 '<rev1>\...<rev2>'::
        Include commits that are reachable from either <rev1> or
-       <rev2> but exclude those that are reachable from both.
+       <rev2> but exclude those that are reachable from both.  When
+       either <rev1> or <rev2> is omitted, it defaults to 'HEAD'.
 
 '<rev>{caret}@', e.g. 'HEAD{caret}@'::
   A suffix '{caret}' followed by an at sign is the same as listing
index 4a4228b896df1c06b565f330b6d7eb9d2cd9b10c..f3c1357b7cc037681b5d0f28974b199e326f8c2d 100644 (file)
@@ -39,7 +39,7 @@ where options is the bitwise-or of:
        on bare repositories.
        This only makes sense when `RUN_SETUP` is also set.
 
-. Add `builtin-foo.o` to `BUILTIN_OBJS` in `Makefile`.
+. Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`.
 
 Additionally, if `foo` is a new command, there are 3 more things to do:
 
index 1f349b28ae175327706fa67a3e0b09f1a26a9c02..7f8e78d916c0b38ed6757aee6578a53cab30d9d0 100644 (file)
@@ -22,12 +22,23 @@ The notable options are:
 
 `flags`::
 
-       A bit-field of options:
+       A bit-field of options (the `*IGNORED*` flags are mutually exclusive):
 
 `DIR_SHOW_IGNORED`:::
 
-       The traversal is for finding just ignored files, not unignored
-       files.
+       Return just ignored files in `entries[]`, not untracked files.
+
+`DIR_SHOW_IGNORED_TOO`:::
+
+       Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
+       in addition to untracked files in `entries[]`.
+
+`DIR_COLLECT_IGNORED`:::
+
+       Special mode for git-add. Return ignored files in `ignored[]` and
+       untracked files in `entries[]`. Only returns ignored files that match
+       pathspec exactly (no wildcards). Does not recurse into ignored
+       directories.
 
 `DIR_SHOW_OTHER_DIRECTORIES`:::
 
@@ -57,6 +68,14 @@ The result of the enumeration is left in these fields:
 
        Internal use; keeps track of allocation of `entries[]` array.
 
+`ignored[]`::
+
+       An array of `struct dir_entry`, used for ignored paths with the
+       `DIR_SHOW_IGNORED_TOO` and `DIR_COLLECT_IGNORED` flags.
+
+`ignored_nr`::
+
+       The number of members in `ignored[]` array.
 
 Calling sequence
 ----------------
index 32ddc1cf13741bea4b809e98508df3b5e71fc828..0be2b5159f1b349f0c263eb9e8ba696e2a57ebc0 100644 (file)
@@ -41,6 +41,8 @@ The parse-options API allows:
 * Boolean long options can be 'negated' (or 'unset') by prepending
   `no-`, e.g. `--no-abbrev` instead of `--abbrev`. Conversely,
   options that begin with `no-` can be 'negated' by removing it.
+  Other long options can be unset (e.g., set string to NULL, set
+  integer to 0) by prepending `no-`.
 
 * Options and non-option arguments can clearly be separated using the `--`
   option, e.g. `-a -b --option -- --this-is-a-file` indicates that
@@ -174,6 +176,10 @@ There are some macros to easily define options:
        Introduce an option with date argument, see `approxidate()`.
        The timestamp is put into `int_var`.
 
+`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+       Introduce an option with expiry date argument, see `parse_expiry_date()`.
+       The timestamp is put into `int_var`.
+
 `OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
        Introduce an option with argument.
        The argument will be fed into the function given by `func_ptr`
@@ -269,10 +275,10 @@ Examples
 --------
 
 See `test-parse-options.c` and
-`builtin-add.c`,
-`builtin-clone.c`,
-`builtin-commit.c`,
-`builtin-fetch.c`,
-`builtin-fsck.c`,
-`builtin-rm.c`
+`builtin/add.c`,
+`builtin/clone.c`,
+`builtin/commit.c`,
+`builtin/fetch.c`,
+`builtin/fsck.c`,
+`builtin/rm.c`
 for real-world examples.
index b7d0d9a8a7b45f4988c0ee8170fec25c415cc918..55b878ade81e2b3b265032e281f82ab86016d5ae 100644 (file)
@@ -59,7 +59,7 @@ function.
 `reset_revision_walk`::
 
        Reset the flags used by the revision walking api. You can use
-       this to do multiple sequencial revision walks.
+       this to do multiple sequential revision walks.
 
 Data structures
 ---------------
index 45d1c517cd862878198db6023d5fdbd35f9fbb6c..3e75497a37d696d534c16ff0270a808b57ac9180 100644 (file)
@@ -1,7 +1,7 @@
 sha1-array API
 ==============
 
-The sha1-array API provides storage and manipulation of sets of SHA1
+The sha1-array API provides storage and manipulation of sets of SHA-1
 identifiers. The emphasis is on storage and processing efficiency,
 making them suitable for large lists. Note that the ordering of items is
 not preserved over some operations.
@@ -11,7 +11,7 @@ Data Structures
 
 `struct sha1_array`::
 
-       A single array of SHA1 hashes. This should be initialized by
+       A single array of SHA-1 hashes. This should be initialized by
        assignment from `SHA1_ARRAY_INIT`.  The `sha1` member contains
        the actual data. The `nr` member contains the number of items in
        the set.  The `alloc` and `sorted` members are used internally,
index 0810251f5a316de548d906bde65fba5af3507e4d..f352a9b22e3138fa674c393d99d420de4f2ff144 100644 (file)
@@ -175,7 +175,7 @@ Git index format
 
   A conflict is represented in the index as a set of higher stage entries.
   When a conflict is resolved (e.g. with "git add path"), these higher
-  stage entries will be removed and a stage-0 entry with proper resoluton
+  stage entries will be removed and a stage-0 entry with proper resolution
   is added.
 
   When these higher stage entries are removed, they are saved in the
index a37f1378d05d6def0ea3cac840acf62875536a70..8e5bf60be3f0689d61feb8ce43cb379b2417fd8f 100644 (file)
@@ -34,7 +34,7 @@ Git pack format
      Observation: length of each object is encoded in a variable
      length format and is not constrained to 32-bit or anything.
 
-  - The trailer records 20-byte SHA1 checksum of all of the above.
+  - The trailer records 20-byte SHA-1 checksum of all of the above.
 
 == Original (version 1) pack-*.idx files have the following format:
 
@@ -55,10 +55,10 @@ Git pack format
 
   - The file is concluded with a trailer:
 
-    A copy of the 20-byte SHA1 checksum at the end of
+    A copy of the 20-byte SHA-1 checksum at the end of
     corresponding packfile.
 
-    20-byte SHA1-checksum of all of the above.
+    20-byte SHA-1-checksum of all of the above.
 
 Pack Idx file:
 
@@ -106,7 +106,7 @@ Pack file entry: <+
         If it is not DELTA, then deflated bytes (the size above
                is the size before compression).
        If it is REF_DELTA, then
-         20-byte base object name SHA1 (the size above is the
+         20-byte base object name SHA-1 (the size above is the
                size of the delta data that follows).
           delta data, deflated.
        If it is OFS_DELTA, then
@@ -135,7 +135,7 @@ Pack file entry: <+
 
   - A 256-entry fan-out table just like v1.
 
-  - A table of sorted 20-byte SHA1 object names.  These are
+  - A table of sorted 20-byte SHA-1 object names.  These are
     packed together without offset values to reduce the cache
     footprint of the binary search for a specific object name.
 
@@ -156,7 +156,7 @@ Pack file entry: <+
 
   - The same trailer as a v1 pack file:
 
-    A copy of the 20-byte SHA1 checksum at the end of
+    A copy of the 20-byte SHA-1 checksum at the end of
     corresponding packfile.
 
-    20-byte SHA1-checksum of all of the above.
+    20-byte SHA-1-checksum of all of the above.
index dbdf7ba9c811837d70dde4e22fafec7a8f6b49d0..8b7ae1c140bc9d6d5fe01ea30dfbef0d6eb7ca8e 100644 (file)
@@ -89,7 +89,7 @@ Ah, grasshopper!  And thus the enlightenment begins anew.
 
     <linus> The "magic" is actually in theory totally arbitrary.
         ANY order will give you a working pack, but no, it's not
-        ordered by SHA1.
+       ordered by SHA-1.
 
         Before talking about the ordering for the sliding delta
         window, let's talk about the recency order. That's more
index f1a51edf47f94f45ce916f7800924e6f135800e2..b898e97988311fe411c02f9f3fe45608e862a71a 100644 (file)
@@ -228,8 +228,7 @@ obtained through ref discovery.
 The client MUST write all obj-ids which it only has shallow copies
 of (meaning that it does not have the parents of a commit) as
 'shallow' lines so that the server is aware of the limitations of
-the client's history. Clients MUST NOT mention an obj-id which
-it does not know exists on the server.
+the client's history.
 
 The client now sends the maximum commit history depth it wants for
 this transaction, which is the number of commits it wants from the
index b15517fa06bd782fb757e6b0836f9bceea8b7c05..fd8ffa5df38c8dd1a682ab77327654a4c2b80cd4 100644 (file)
@@ -18,11 +18,12 @@ was sent.  Server MUST NOT ignore capabilities that client requested
 and server advertised.  As a consequence of these rules, server MUST
 NOT advertise capabilities it does not understand.
 
-The 'report-status' and 'delete-refs' capabilities are sent and
+The 'report-status', 'delete-refs', and 'quiet' capabilities are sent and
 recognized by the receive-pack (push to server) process.
 
-The 'ofs-delta' capability is sent and recognized by both upload-pack
-and receive-pack protocols.
+The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized
+by both upload-pack and receive-pack protocols.  The 'agent' capability
+may optionally be sent in both protocols.
 
 All other capabilities are only recognized by the upload-pack (fetch
 from server) process.
@@ -123,6 +124,20 @@ Server can send, and client understand PACKv2 with delta referring to
 its base by position in pack rather than by an obj-id.  That is, they can
 send/read OBJ_OFS_DELTA (aka type 6) in a packfile.
 
+agent
+-----
+
+The server may optionally send a capability of the form `agent=X` to
+notify the client that the server is running version `X`. The client may
+optionally return its own agent string by responding with an `agent=Y`
+capability (but it MUST NOT do so if the server did not mention the
+agent capability). The `X` and `Y` strings may contain any printable
+ASCII characters except space (i.e., the byte range 32 < x < 127), and
+are typically of the form "package/version" (e.g., "git/1.8.3.1"). The
+agent strings are purely informative for statistics and debugging
+purposes, and MUST NOT be used to programatically assume the presence
+or absence of particular features.
+
 shallow
 -------
 
@@ -168,7 +183,7 @@ of whether or not there are tags available.
 report-status
 -------------
 
-The upload-pack process can receive a 'report-status' capability,
+The receive-pack process can receive a 'report-status' capability,
 which tells it that the client wants a report of what happened after
 a packfile upload and reference update.  If the pushing client requests
 this capability, after unpacking and updating references the server
@@ -185,3 +200,20 @@ it is capable of accepting a zero-id value as the target
 value of a reference update.  It is not sent back by the client, it
 simply informs the client that it can be sent zero-id values
 to delete references.
+
+quiet
+-----
+
+If the receive-pack server advertises the 'quiet' capability, it is
+capable of silencing human-readable progress output which otherwise may
+be shown when processing the received pack. A send-pack client should
+respond with the 'quiet' capability to suppress server-side progress
+reporting if the local progress reporting is also being suppressed
+(e.g., via `push -q`, or if stderr does not go to a tty).
+
+allow-tip-sha1-in-want
+----------------------
+
+If the upload-pack server advertises this capability, fetch-pack may
+send "want" lines with SHA-1s that exist at the server but are not
+advertised by upload-pack.
index 6dc82ca5a8a8b744fad1aad6ee7578c3b02495d1..242a044db978b8bd7cf9dabcbc31a2eb51056da6 100644 (file)
@@ -46,7 +46,7 @@ because in-core timestamps can have finer granularity than
 on-disk timestamps, resulting in meaningless changes when an
 inode is evicted from the inode cache.  See commit 8ce13b0
 of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
-([PATCH] Sync in core time granuality with filesystems,
+([PATCH] Sync in core time granularity with filesystems,
 2005-01-04).
 
 Racy Git
@@ -135,9 +135,9 @@ them, and give the same timestamp to the index file:
   $ git ls-files | git update-index --stdin
   $ touch -r .datestamp .git/index
 
-This will make all index entries racily clean.  The linux-2.6
-project, for example, there are over 20,000 files in the working
-tree.  On my Athlon 64 X2 3800+, after the above:
+This will make all index entries racily clean.  The linux project, for
+example, there are over 20,000 files in the working tree.  On my
+Athlon 64 X2 3800+, after the above:
 
   $ /usr/bin/time git diff-files
   1.68user 0.54system 0:02.22elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
index ea2f69faf5cd1e87226eb2a8c281aa4d5f6ed204..5183b154229d7c25f0feab3d0afcd1d52ca652e0 100644 (file)
@@ -8,7 +8,7 @@ repo, and therefore grafts are introduced pretending that
 these commits have no parents.
 *********************************************************
 
-The basic idea is to write the SHA1s of shallow commits into
+The basic idea is to write the SHA-1s of shallow commits into
 $GIT_DIR/shallow, and handle its contents like the contents
 of $GIT_DIR/info/grafts (with the difference that shallow
 cannot contain parent information).
@@ -18,7 +18,7 @@ even the config, since the user should not touch that file
 at all (even throughout development of the shallow clone, it
 was never manually edited!).
 
-Each line contains exactly one SHA1. When read, a commit_graft
+Each line contains exactly one SHA-1. When read, a commit_graft
 will be constructed, which has nr_parent < 0 to make it easier
 to discern from user provided grafts.
 
index 3ca122faedd01cfbd61bf07dbc245b04f40f2b23..9ccb24677e95b8aff605c9020bc7a0537e23f751 100644 (file)
@@ -11,6 +11,9 @@ and ftps can be used for fetching and rsync can be used for fetching
 and pushing, but these are inefficient and deprecated; do not use
 them).
 
+The native transport (i.e. git:// URL) does no authentication and
+should be used with caution on unsecured networks.
+
 The following syntaxes may be used with them:
 
 - ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
@@ -23,6 +26,12 @@ An alternative scp-like syntax may also be used with the ssh protocol:
 
 - {startsb}user@{endsb}host.xz:path/to/repo.git/
 
+This syntax is only recognized if there are no slashes before the
+first colon. This helps differentiate a local path that contains a
+colon. For example the local path `foo:bar` could be specified as an
+absolute path or `./foo:bar` to avoid being misinterpreted as an ssh
+url.
+
 The ssh and git protocols additionally support ~username expansion:
 
 - ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
@@ -33,7 +42,7 @@ For local repositories, also supported by Git natively, the following
 syntaxes may be used:
 
 - /path/to/repo.git/
-- file:///path/to/repo.git/
+- \file:///path/to/repo.git/
 
 ifndef::git-clone[]
 These two syntaxes are mostly equivalent, except when cloning, when
index e831cc2020b2b728d972c9e33f7fef8041381547..fe723e472275dc8989338be9a1bc592a4dd18425 100644 (file)
@@ -57,17 +57,17 @@ download a copy of an existing repository.  If you don't already have a
 project in mind, here are some interesting examples:
 
 ------------------------------------------------
-       # Git itself (approx. 10MB download):
+       # Git itself (approx. 40MB download):
 $ git clone git://git.kernel.org/pub/scm/git/git.git
-       # the Linux kernel (approx. 150MB download):
-$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+       # the Linux kernel (approx. 640MB download):
+$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
 ------------------------------------------------
 
 The initial clone may be time-consuming for a large project, but you
 will only need to clone once.
 
-The clone command creates a new directory named after the project (`git`
-or `linux-2.6` in the examples above).  After you cd into this
+The clone command creates a new directory named after the project
+(`git` or `linux` in the examples above).  After you cd into this
 directory, you will see that it contains a copy of the project files,
 called the <<def_working_tree,working tree>>, together with a special
 top-level directory named `.git`, which contains all the information
@@ -431,19 +431,25 @@ You can also track branches from repositories other than the one you
 cloned from, using linkgit:git-remote[1]:
 
 -------------------------------------------------
-$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
-$ git fetch linux-nfs
-* refs/remotes/linux-nfs/master: storing branch 'master' ...
-  commit: bf81b46
+$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
+$ git fetch staging
+...
+From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
+ * [new branch]      master     -> staging/master
+ * [new branch]      staging-linus -> staging/staging-linus
+ * [new branch]      staging-next -> staging/staging-next
 -------------------------------------------------
 
 New remote-tracking branches will be stored under the shorthand name
-that you gave `git remote add`, in this case `linux-nfs`:
+that you gave `git remote add`, in this case `staging`:
 
 -------------------------------------------------
 $ git branch -r
-linux-nfs/master
-origin/master
+  origin/HEAD -> origin/master
+  origin/master
+  staging/master
+  staging/staging-linus
+  staging/staging-next
 -------------------------------------------------
 
 If you run `git fetch <remote>` later, the remote-tracking branches
@@ -455,9 +461,9 @@ a new stanza:
 -------------------------------------------------
 $ cat .git/config
 ...
-[remote "linux-nfs"]
-       url = git://linux-nfs.org/pub/nfs-2.6.git
-       fetch = +refs/heads/*:refs/remotes/linux-nfs/*
+[remote "staging"]
+       url = git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
+       fetch = +refs/heads/*:refs/remotes/staging/*
 ...
 -------------------------------------------------
 
@@ -1835,7 +1841,7 @@ Once the index is updated with the results of the conflict
 resolution, instead of creating a new commit, just run
 
 -------------------------------------------------
-$ git am --resolved
+$ git am --continue
 -------------------------------------------------
 
 and Git will create the commit for you and continue applying the
@@ -2156,7 +2162,7 @@ To set this up, first create your work tree by cloning Linus's public
 tree:
 
 -------------------------------------------------
-$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
+$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git work
 $ cd work
 -------------------------------------------------
 
@@ -2198,7 +2204,7 @@ make it easy to push both branches to your public tree.  (See
 -------------------------------------------------
 $ cat >> .git/config <<EOF
 [remote "mytree"]
-       url =  master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
+       url =  master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux.git
        push = release
        push = test
 EOF
@@ -4256,15 +4262,16 @@ no longer need to call `setup_pager()` directly).
 Nowadays, `git log` is a builtin, which means that it is _contained_ in the
 command `git`.  The source side of a builtin is
 
-- a function called `cmd_<bla>`, typically defined in `builtin-<bla>.c`,
-  and declared in `builtin.h`,
+- a function called `cmd_<bla>`, typically defined in `builtin/<bla.c>`
+  (note that older versions of Git used to have it in `builtin-<bla>.c`
+  instead), and declared in `builtin.h`.
 
 - an entry in the `commands[]` array in `git.c`, and
 
 - an entry in `BUILTIN_OBJECTS` in the `Makefile`.
 
 Sometimes, more than one builtin is contained in one source file.  For
-example, `cmd_whatchanged()` and `cmd_log()` both reside in `builtin-log.c`,
+example, `cmd_whatchanged()` and `cmd_log()` both reside in `builtin/log.c`,
 since they share quite a bit of code.  In that case, the commands which are
 _not_ named like the `.c` file in which they live have to be listed in
 `BUILT_INS` in the `Makefile`.
@@ -4287,10 +4294,10 @@ For the sake of clarity, let's stay with `git cat-file`, because it
 - is plumbing, and
 
 - was around even in the initial commit (it literally went only through
-  some 20 revisions as `cat-file.c`, was renamed to `builtin-cat-file.c`
+  some 20 revisions as `cat-file.c`, was renamed to `builtin/cat-file.c`
   when made a builtin, and then saw less than 10 versions).
 
-So, look into `builtin-cat-file.c`, search for `cmd_cat_file()` and look what
+So, look into `builtin/cat-file.c`, search for `cmd_cat_file()` and look what
 it does.
 
 ------------------------------------------------------------------
@@ -4366,7 +4373,7 @@ Another example: Find out what to do in order to make some script a
 builtin:
 
 -------------------------------------------------
-$ git log --no-merges --diff-filter=A builtin-*.c
+$ git log --no-merges --diff-filter=A builtin/*.c
 -------------------------------------------------
 
 You see, Git is actually the best tool to find out about the source of Git
@@ -4668,5 +4675,5 @@ Write a chapter on using plumbing and writing scripts.
 Alternates, clone -reference, etc.
 
 More on recovery from repository corruption.  See:
-       http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2
-       http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
+       http://marc.info/?l=git&m=117263864820799&w=2
+       http://marc.info/?l=git&m=117147855503798&w=2
index 6722e621d119dbefeda2e8ea02dfcc0ebb2b4a5a..b4147d6db58133bb88d9011d1153410cf9f419ca 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.2.GIT
+DEF_VER=v1.8.4.1
 
 LF='
 '
@@ -11,7 +11,7 @@ LF='
 if test -f version
 then
        VN=$(cat version) || VN="$DEF_VER"
-elif test -d .git -o -f .git &&
+elif test -d ${GIT_DIR:-.git} -o -f .git &&
        VN=$(git describe --match "v[0-9]*" --abbrev=7 HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
index 0f931a203002aec397f8b454f93df9aee97a21ad..4026211cb42e8caca91b2bbe0c9236a4db543352 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -106,7 +106,7 @@ all::
 # Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
 # FNM_CASEFOLD GNU extension.
 #
-# Define USE_WILDMATCH if you want to use Git's wildmatch
+# Define NO_WILDMATCH if you do not want to use Git's wildmatch
 # implementation as fnmatch
 #
 # Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
@@ -137,6 +137,10 @@ all::
 # specify your own (or DarwinPort's) include directories and
 # library directories by defining CFLAGS and LDFLAGS appropriately.
 #
+# Define NO_APPLE_COMMON_CRYPTO if you are building on Darwin/Mac OS X
+# and do not want to use Apple's CommonCrypto library.  This allows you
+# to provide your own OpenSSL library, for example from MacPorts.
+#
 # Define BLK_SHA1 environment variable to make use of the bundled
 # optimized C SHA1 routine.
 #
@@ -290,10 +294,6 @@ all::
 #
 # Define NO_REGEX if you have no or inferior regex support in your C library.
 #
-# Define CYGWIN_V15_WIN32API if you are using Cygwin v1.7.x but are not
-# using the current w32api packages. The recommended approach, however,
-# is to update your installation if compilation errors occur.
-#
 # Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
 # user.
 #
@@ -460,6 +460,7 @@ SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
 SCRIPT_SH += git-rebase.sh
+SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-repack.sh
 SCRIPT_SH += git-request-pull.sh
 SCRIPT_SH += git-stash.sh
@@ -487,11 +488,18 @@ SCRIPT_PERL += git-svn.perl
 SCRIPT_PYTHON += git-remote-testpy.py
 SCRIPT_PYTHON += git-p4.py
 
+NO_INSTALL += git-remote-testgit
+NO_INSTALL += git-remote-testpy
+
 # Generated files for scripts
 SCRIPT_SH_GEN = $(patsubst %.sh,%,$(SCRIPT_SH))
 SCRIPT_PERL_GEN = $(patsubst %.perl,%,$(SCRIPT_PERL))
 SCRIPT_PYTHON_GEN = $(patsubst %.py,%,$(SCRIPT_PYTHON))
 
+SCRIPT_SH_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_SH_GEN))
+SCRIPT_PERL_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PERL_GEN))
+SCRIPT_PYTHON_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PYTHON_GEN))
+
 # Individual rules to allow e.g.
 # "make -C ../.. SCRIPT_PERL=contrib/foo/bar.perl build-perl-script"
 # from subdirectories like contrib/*/
@@ -501,12 +509,12 @@ build-sh-script: $(SCRIPT_SH_GEN)
 build-python-script: $(SCRIPT_PYTHON_GEN)
 
 .PHONY: install-perl-script install-sh-script install-python-script
-install-sh-script: $(SCRIPT_SH_GEN)
-       $(INSTALL) $(SCRIPT_SH_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-install-perl-script: $(SCRIPT_PERL_GEN)
-       $(INSTALL) $(SCRIPT_PERL_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-install-python-script: $(SCRIPT_PYTHON_GEN)
-       $(INSTALL) $(SCRIPT_PYTHON_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+install-sh-script: $(SCRIPT_SH_INS)
+       $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+install-perl-script: $(SCRIPT_PERL_INS)
+       $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
+install-python-script: $(SCRIPT_PYTHON_INS)
+       $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 
 .PHONY: clean-perl-script clean-sh-script clean-python-script
 clean-sh-script:
@@ -516,9 +524,9 @@ clean-perl-script:
 clean-python-script:
        $(RM) $(SCRIPT_PYTHON_GEN)
 
-SCRIPTS = $(SCRIPT_SH_GEN) \
-         $(SCRIPT_PERL_GEN) \
-         $(SCRIPT_PYTHON_GEN) \
+SCRIPTS = $(SCRIPT_SH_INS) \
+         $(SCRIPT_PERL_INS) \
+         $(SCRIPT_PYTHON_INS) \
          git-instaweb
 
 ETAGS_TARGET = TAGS
@@ -558,6 +566,8 @@ TEST_PROGRAMS_NEED_X += test-mergesort
 TEST_PROGRAMS_NEED_X += test-mktemp
 TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-prio-queue
+TEST_PROGRAMS_NEED_X += test-read-cache
 TEST_PROGRAMS_NEED_X += test-regex
 TEST_PROGRAMS_NEED_X += test-revision-walking
 TEST_PROGRAMS_NEED_X += test-run-command
@@ -641,7 +651,6 @@ LIB_H += color.h
 LIB_H += column.h
 LIB_H += commit.h
 LIB_H += compat/bswap.h
-LIB_H += compat/cygwin.h
 LIB_H += compat/mingw.h
 LIB_H += compat/obstack.h
 LIB_H += compat/poll/poll.h
@@ -673,6 +682,8 @@ LIB_H += help.h
 LIB_H += http.h
 LIB_H += kwset.h
 LIB_H += levenshtein.h
+LIB_H += line-log.h
+LIB_H += line-range.h
 LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
@@ -682,15 +693,16 @@ LIB_H += merge-recursive.h
 LIB_H += mergesort.h
 LIB_H += notes-cache.h
 LIB_H += notes-merge.h
+LIB_H += notes-utils.h
 LIB_H += notes.h
 LIB_H += object.h
-LIB_H += pack-refs.h
 LIB_H += pack-revindex.h
 LIB_H += pack.h
 LIB_H += parse-options.h
 LIB_H += patch-ids.h
 LIB_H += pathspec.h
 LIB_H += pkt-line.h
+LIB_H += prio-queue.h
 LIB_H += progress.h
 LIB_H += prompt.h
 LIB_H += quote.h
@@ -801,6 +813,8 @@ LIB_OBJS += hex.o
 LIB_OBJS += ident.o
 LIB_OBJS += kwset.o
 LIB_OBJS += levenshtein.o
+LIB_OBJS += line-log.o
+LIB_OBJS += line-range.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
@@ -815,9 +829,9 @@ LIB_OBJS += name-hash.o
 LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
+LIB_OBJS += notes-utils.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
-LIB_OBJS += pack-refs.o
 LIB_OBJS += pack-revindex.o
 LIB_OBJS += pack-write.o
 LIB_OBJS += pager.o
@@ -830,6 +844,7 @@ LIB_OBJS += pathspec.o
 LIB_OBJS += pkt-line.o
 LIB_OBJS += preload-index.o
 LIB_OBJS += pretty.o
+LIB_OBJS += prio-queue.o
 LIB_OBJS += progress.o
 LIB_OBJS += prompt.o
 LIB_OBJS += quote.o
@@ -893,6 +908,7 @@ BUILTIN_OBJS += builtin/bundle.o
 BUILTIN_OBJS += builtin/cat-file.o
 BUILTIN_OBJS += builtin/check-attr.o
 BUILTIN_OBJS += builtin/check-ignore.o
+BUILTIN_OBJS += builtin/check-mailmap.o
 BUILTIN_OBJS += builtin/check-ref-format.o
 BUILTIN_OBJS += builtin/checkout-index.o
 BUILTIN_OBJS += builtin/checkout.o
@@ -1054,6 +1070,11 @@ ifeq ($(uname_S),Darwin)
                        BASIC_LDFLAGS += -L/opt/local/lib
                endif
        endif
+       ifndef NO_APPLE_COMMON_CRYPTO
+               APPLE_COMMON_CRYPTO = YesPlease
+               COMPAT_CFLAGS += -DAPPLE_COMMON_CRYPTO
+       endif
+       NO_REGEX = YesPlease
        PTHREAD_LIBS =
 endif
 
@@ -1261,7 +1282,7 @@ ifdef NO_FNMATCH_CASEFOLD
        COMPAT_OBJS += compat/fnmatch/fnmatch.o
 endif
 endif
-ifdef USE_WILDMATCH
+ifndef NO_WILDMATCH
        COMPAT_CFLAGS += -DUSE_WILDMATCH
 endif
 ifdef NO_SETENV
@@ -1387,11 +1408,17 @@ ifdef PPC_SHA1
        SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
        LIB_H += ppc/sha1.h
+else
+ifdef APPLE_COMMON_CRYPTO
+       COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
+       SHA1_HEADER = <CommonCrypto/CommonDigest.h>
 else
        SHA1_HEADER = <openssl/sha.h>
        EXTLIBS += $(LIB_4_CRYPTO)
 endif
 endif
+endif
+
 ifdef NO_PERL_MAKEMAKER
        export NO_PERL_MAKEMAKER
 endif
@@ -1449,9 +1476,6 @@ ifdef NO_REGEX
        COMPAT_CFLAGS += -Icompat/regex
        COMPAT_OBJS += compat/regex/regex.o
 endif
-ifdef CYGWIN_V15_WIN32API
-       COMPAT_CFLAGS += -DCYGWIN_V15_WIN32API
-endif
 
 ifdef USE_NED_ALLOCATOR
        COMPAT_CFLAGS += -Icompat/nedmalloc
@@ -1503,6 +1527,7 @@ ifndef V
        QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
        QUIET_GCOV     = @echo '   ' GCOV $@;
        QUIET_SP       = @echo '   ' SP $<;
+       QUIET_RC       = @echo '   ' RC $@;
        QUIET_SUBDIR0  = +@subdir=
        QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
                         $(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1645,7 +1670,7 @@ please_set_SHELL_PATH_to_a_more_modern_shell:
 shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
 
 strip: $(PROGRAMS) git$X
-       $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
+       $(STRIP) $(STRIP_OPTS) $^
 
 ### Target-specific flags and dependencies
 
@@ -1705,9 +1730,9 @@ version.sp version.s version.o: EXTRA_CPPFLAGS = \
 
 $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
-       ln git$X $@ 2>/dev/null || \
-       ln -s git$X $@ 2>/dev/null || \
-       cp git$X $@
+       ln $< $@ 2>/dev/null || \
+       ln -s $< $@ 2>/dev/null || \
+       cp $< $@
 
 common-cmds.h: ./generate-cmdlist.sh command-list.txt
 
@@ -1748,6 +1773,11 @@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        mv $@+ $@
 
+git.res: git.rc GIT-VERSION-FILE
+       $(QUIET_RC)$(RC) \
+         $(join -DMAJOR= -DMINOR= -DPATCH=, $(wordlist 1,3,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
+         -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
+
 ifndef NO_PERL
 $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
 
@@ -1772,7 +1802,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE
            -e '        x' \
            -e '}' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-           $@.perl >$@+ && \
+           $< >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
 
@@ -1796,8 +1826,8 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
 endif # NO_PERL
 
 ifndef NO_PYTHON
-$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
-$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
+$(SCRIPT_PYTHON_GEN): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
+$(SCRIPT_PYTHON_GEN): % : %.py
        $(QUIET_GEN)$(RM) $@ $@+ && \
        INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
                --no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
@@ -1805,11 +1835,11 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
        sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
            -e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
            -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
-           $@.py >$@+ && \
+           $< >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
 else # NO_PYTHON
-$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : unimplemented.sh
+$(SCRIPT_PYTHON_GEN): % : unimplemented.sh
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's|@@REASON@@|NO_PYTHON=$(NO_PYTHON)|g' \
@@ -2004,6 +2034,7 @@ endif
 ifdef USE_NED_ALLOCATOR
 compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
        -DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
+compat/nedmalloc/nedmalloc.sp: SPARSE_FLAGS += -Wno-non-pointer-null
 endif
 
 git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
@@ -2035,13 +2066,13 @@ $(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o GIT-LDFLAGS $(GITLIBS
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
 $(LIB_FILE): $(LIB_OBJS)
-       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
+       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
 
 $(XDIFF_LIB): $(XDIFF_OBJS)
-       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
+       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
 
 $(VCSSVN_LIB): $(VCSSVN_OBJS)
-       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
+       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
 
 export DEFAULT_EDITOR DEFAULT_PAGER
 
@@ -2159,6 +2190,9 @@ GIT-BUILD-OPTIONS: FORCE
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
+ifdef TEST_OUTPUT_DIRECTORY
+       @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@
+endif
 ifdef GIT_TEST_OPTS
        @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
 endif
@@ -2198,6 +2232,7 @@ endif
 test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
 
 all:: $(TEST_PROGRAMS) $(test_bindir_programs)
+all:: $(NO_INSTALL)
 
 bin-wrappers/%: wrap-for-bin.sh
        @mkdir -p bin-wrappers
@@ -2262,9 +2297,6 @@ check: common-cmds.h
                exit 1; \
        fi
 
-remove-dashes:
-       ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)
-
 ### Installation rules
 
 ifneq ($(filter /%,$(firstword $(template_dir))),)
@@ -2443,11 +2475,11 @@ profile-clean:
        $(RM) $(addsuffix *.gcda,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
        $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
 
-clean: profile-clean
-       $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
+clean: profile-clean coverage-clean
+       $(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
                builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
-       $(RM) $(TEST_PROGRAMS)
+       $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
        $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
@@ -2524,29 +2556,34 @@ check-builtins::
 
 ### Test suite coverage testing
 #
-.PHONY: coverage coverage-clean coverage-build coverage-report
+.PHONY: coverage coverage-clean coverage-compile coverage-test coverage-report
+.PHONY: coverage-clean-results
 
 coverage:
-       $(MAKE) coverage-build
-       $(MAKE) coverage-report
+       $(MAKE) coverage-test
+       $(MAKE) coverage-untested-functions
 
 object_dirs := $(sort $(dir $(OBJECTS)))
-coverage-clean:
+coverage-clean-results:
        $(RM) $(addsuffix *.gcov,$(object_dirs))
        $(RM) $(addsuffix *.gcda,$(object_dirs))
-       $(RM) $(addsuffix *.gcno,$(object_dirs))
        $(RM) coverage-untested-functions
        $(RM) -r cover_db/
        $(RM) -r cover_db_html/
 
+coverage-clean: coverage-clean-results
+       $(RM) $(addsuffix *.gcno,$(object_dirs))
+
 COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
 COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
 GCOVFLAGS = --preserve-paths --branch-probabilities --all-blocks
 
-coverage-build: coverage-clean
+coverage-compile:
        $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
+
+coverage-test: coverage-clean-results coverage-compile
        $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
-               -j1 test
+               DEFAULT_TEST_TARGET=test -j1 test
 
 coverage-report:
        $(QUIET_GCOV)for dir in $(object_dirs); do \
index 80b7e388ad321ea679e2b14ce4f18972da35156c..c3d6c6c277c370119d47a79e6119953efc68409f 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.8.3.txt
\ No newline at end of file
+Documentation/RelNotes/1.8.4.2.txt
\ No newline at end of file
index 40cdc46219d4eba87ba947d9f5556eb80dfdfa9b..64adbe2a1b5e5679fb0dc772452b4aca1865ee4c 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -216,7 +216,7 @@ const char *absolute_path(const char *path)
 const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
 {
        static char path[PATH_MAX];
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
        if (!pfx_len || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
index a8deee6e6419a1e867e06e90a70300cd2e01b556..3eca9f5ffdd6e3596584ed0aa79e2634683bd496 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -14,6 +14,8 @@ int advice_resolve_conflict = 1;
 int advice_implicit_identity = 1;
 int advice_detached_head = 1;
 int advice_set_upstream_failure = 1;
+int advice_object_name_warning = 1;
+int advice_rm_hints = 1;
 
 static struct {
        const char *name;
@@ -33,6 +35,8 @@ static struct {
        { "implicitidentity", &advice_implicit_identity },
        { "detachedhead", &advice_detached_head },
        { "setupstreamfailure", &advice_set_upstream_failure },
+       { "objectnamewarning", &advice_object_name_warning },
+       { "rmhints", &advice_rm_hints },
 
        /* make this an alias for backward compatibility */
        { "pushnonfastforward", &advice_push_update_rejected }
index 94caa32f9213f59a627176ec8b4aea022f6b0f8f..08fbc8ee3ce79f265bbe15b63e2ae561ee8a0857 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -17,8 +17,11 @@ extern int advice_resolve_conflict;
 extern int advice_implicit_identity;
 extern int advice_detached_head;
 extern int advice_set_upstream_failure;
+extern int advice_object_name_warning;
+extern int advice_rm_hints;
 
 int git_default_advice_config(const char *var, const char *value);
+__attribute__((format (printf, 1, 2)))
 void advise(const char *advice, ...);
 int error_resolve_conflict(const char *me);
 extern void NORETURN die_resolve_conflict(const char *me);
index b2c4fe0e9f9816a6cb79aa8f7a0cb59ae5bf7f8d..4bde019bce632703d4e336d6404693487cdbcdfb 100644 (file)
@@ -232,7 +232,6 @@ static int write_zip_entry(struct archiver_args *args,
                size = 0;
                compressed_size = 0;
                buffer = NULL;
-               size = 0;
        } else if (S_ISREG(mode) || S_ISLNK(mode)) {
                enum object_type type = sha1_object_info(sha1, &size);
 
index 40248d424c31662a3a574cfea13635084feb14f5..85ba438ac1e877fa865e5b4d2301733735fa5d03 100644 (file)
@@ -15,6 +15,7 @@ void argv_array_init(struct argv_array *);
 void argv_array_push(struct argv_array *, const char *);
 __attribute__((format (printf,2,3)))
 void argv_array_pushf(struct argv_array *, const char *fmt, ...);
+LAST_ARG_MUST_BE_NULL
 void argv_array_pushl(struct argv_array *, ...);
 void argv_array_pop(struct argv_array *);
 void argv_array_clear(struct argv_array *);
diff --git a/attr.c b/attr.c
index 689bc2a8961fac72a01e615764af53f49d01c3ec..0e774c6f22f9fd6c7e27490e5bed5be1f9f10de5 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -381,46 +381,13 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
        return res;
 }
 
-static void *read_index_data(const char *path)
-{
-       int pos, len;
-       unsigned long sz;
-       enum object_type type;
-       void *data;
-       struct index_state *istate = use_index ? use_index : &the_index;
-
-       len = strlen(path);
-       pos = index_name_pos(istate, path, len);
-       if (pos < 0) {
-               /*
-                * We might be in the middle of a merge, in which
-                * case we would read stage #2 (ours).
-                */
-               int i;
-               for (i = -pos - 1;
-                    (pos < 0 && i < istate->cache_nr &&
-                     !strcmp(istate->cache[i]->name, path));
-                    i++)
-                       if (ce_stage(istate->cache[i]) == 2)
-                               pos = i;
-       }
-       if (pos < 0)
-               return NULL;
-       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
-       if (!data || type != OBJ_BLOB) {
-               free(data);
-               return NULL;
-       }
-       return data;
-}
-
 static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)
 {
        struct attr_stack *res;
        char *buf, *sp;
        int lineno = 0;
 
-       buf = read_index_data(path);
+       buf = read_blob_data_from_index(use_index ? use_index : &the_index, path, NULL);
        if (!buf)
                return NULL;
 
index 374d9e24bd0a18b0453f3e948db93251a859ba18..1e46a4f50e16f059fa768cecc4c8331a88997021 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -15,7 +15,7 @@
 static struct sha1_array good_revs;
 static struct sha1_array skipped_revs;
 
-static const unsigned char *current_bad_sha1;
+static unsigned char *current_bad_sha1;
 
 static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
 static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
@@ -404,7 +404,8 @@ static int register_ref(const char *refname, const unsigned char *sha1,
                        int flags, void *cb_data)
 {
        if (!strcmp(refname, "bad")) {
-               current_bad_sha1 = sha1;
+               current_bad_sha1 = xmalloc(20);
+               hashcpy(current_bad_sha1, sha1);
        } else if (!prefixcmp(refname, "good-")) {
                sha1_array_append(&good_revs, sha1);
        } else if (!prefixcmp(refname, "skip-")) {
@@ -623,7 +624,7 @@ static void bisect_common(struct rev_info *revs)
        if (prepare_revision_walk(revs))
                die("revision walk setup failed");
        if (revs->tree_objects)
-               mark_edges_uninteresting(revs->commits, revs, NULL);
+               mark_edges_uninteresting(revs, NULL);
 }
 
 static void exit_if_skipped_commits(struct commit_list *tried,
index 428be60b8ce5c12b5b40b24f856962fed13999ec..2d15c19e3e8d5d2ae317d0cb0e06a35ea8f5745e 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -57,7 +57,7 @@ void install_branch_config(int flag, const char *local, const char *origin, cons
        if (remote_is_branch
            && !strcmp(local, shortname)
            && !origin) {
-               warning("Not setting branch %s as its own upstream.",
+               warning(_("Not setting branch %s as its own upstream."),
                        local);
                return;
        }
@@ -78,25 +78,25 @@ void install_branch_config(int flag, const char *local, const char *origin, cons
 
        if (flag & BRANCH_CONFIG_VERBOSE) {
                if (remote_is_branch && origin)
-                       printf(rebasing ?
-                              "Branch %s set up to track remote branch %s from %s by rebasing.\n" :
-                              "Branch %s set up to track remote branch %s from %s.\n",
-                              local, shortname, origin);
+                       printf_ln(rebasing ?
+                                 _("Branch %s set up to track remote branch %s from %s by rebasing.") :
+                                 _("Branch %s set up to track remote branch %s from %s."),
+                                 local, shortname, origin);
                else if (remote_is_branch && !origin)
-                       printf(rebasing ?
-                              "Branch %s set up to track local branch %s by rebasing.\n" :
-                              "Branch %s set up to track local branch %s.\n",
-                              local, shortname);
+                       printf_ln(rebasing ?
+                                 _("Branch %s set up to track local branch %s by rebasing.") :
+                                 _("Branch %s set up to track local branch %s."),
+                                 local, shortname);
                else if (!remote_is_branch && origin)
-                       printf(rebasing ?
-                              "Branch %s set up to track remote ref %s by rebasing.\n" :
-                              "Branch %s set up to track remote ref %s.\n",
-                              local, remote);
+                       printf_ln(rebasing ?
+                                 _("Branch %s set up to track remote ref %s by rebasing.") :
+                                 _("Branch %s set up to track remote ref %s."),
+                                 local, remote);
                else if (!remote_is_branch && !origin)
-                       printf(rebasing ?
-                              "Branch %s set up to track local ref %s by rebasing.\n" :
-                              "Branch %s set up to track local ref %s.\n",
-                              local, remote);
+                       printf_ln(rebasing ?
+                                 _("Branch %s set up to track local ref %s by rebasing.") :
+                                 _("Branch %s set up to track local ref %s."),
+                                 local, remote);
                else
                        die("BUG: impossible combination of %d and %p",
                            remote_is_branch, origin);
@@ -115,7 +115,7 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
        int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
 
        if (strlen(new_ref) > 1024 - 7 - 7 - 1)
-               return error("Tracking not set up: name too long: %s",
+               return error(_("Tracking not set up: name too long: %s"),
                                new_ref);
 
        memset(&tracking, 0, sizeof(tracking));
@@ -134,7 +134,7 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
                }
 
        if (tracking.matches > 1)
-               return error("Not tracking: ambiguous information for ref %s",
+               return error(_("Not tracking: ambiguous information for ref %s"),
                                orig_ref);
 
        install_branch_config(config_flags, new_ref, tracking.remote,
@@ -179,12 +179,12 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
                            int force, int attr_only)
 {
        if (strbuf_check_branch_ref(ref, name))
-               die("'%s' is not a valid branch name.", name);
+               die(_("'%s' is not a valid branch name."), name);
 
        if (!ref_exists(ref->buf))
                return 0;
        else if (!force && !attr_only)
-               die("A branch named '%s' already exists.", ref->buf + strlen("refs/heads/"));
+               die(_("A branch named '%s' already exists."), ref->buf + strlen("refs/heads/"));
 
        if (!attr_only) {
                const char *head;
@@ -192,7 +192,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
 
                head = resolve_ref_unsafe("HEAD", sha1, 0, NULL);
                if (!is_bare_repository() && head && !strcmp(head, ref->buf))
-                       die("Cannot force update the current branch.");
+                       die(_("Cannot force update the current branch."));
        }
        return 1;
 }
@@ -261,7 +261,7 @@ void create_branch(const char *head,
                        }
                        die(_(upstream_missing), start_name);
                }
-               die("Not a valid object name: '%s'.", start_name);
+               die(_("Not a valid object name: '%s'."), start_name);
        }
 
        switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
@@ -281,18 +281,18 @@ void create_branch(const char *head,
                }
                break;
        default:
-               die("Ambiguous object name: '%s'.", start_name);
+               die(_("Ambiguous object name: '%s'."), start_name);
                break;
        }
 
        if ((commit = lookup_commit_reference(sha1)) == NULL)
-               die("Not a valid branch point: '%s'.", start_name);
+               die(_("Not a valid branch point: '%s'."), start_name);
        hashcpy(sha1, commit->object.sha1);
 
        if (!dont_change_ref) {
                lock = lock_any_ref_for_update(ref.buf, NULL, 0);
                if (!lock)
-                       die_errno("Failed to lock ref for update");
+                       die_errno(_("Failed to lock ref for update"));
        }
 
        if (reflog)
@@ -310,7 +310,7 @@ void create_branch(const char *head,
 
        if (!dont_change_ref)
                if (write_ref_sha1(lock, sha1, msg) < 0)
-                       die_errno("Failed to write ref");
+                       die_errno(_("Failed to write ref"));
 
        strbuf_release(&ref);
        free(real_ref);
index faef55913634a21a757b4814f855ed834c32915e..8afa2def491ac08d6c0a2962bee152631fb956ca 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -5,13 +5,15 @@
 #include "strbuf.h"
 #include "cache.h"
 #include "commit.h"
-#include "notes.h"
 
 #define DEFAULT_MERGE_LOG_LEN 20
 
 extern const char git_usage_string[];
 extern const char git_more_info_string[];
 
+#define PRUNE_PACKED_DRY_RUN 01
+#define PRUNE_PACKED_VERBOSE 02
+
 extern void prune_packed_objects(int);
 
 struct fmt_merge_msg_opts {
@@ -23,21 +25,6 @@ struct fmt_merge_msg_opts {
 extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                         struct fmt_merge_msg_opts *);
 
-struct notes_rewrite_cfg {
-       struct notes_tree **trees;
-       const char *cmd;
-       int enabled;
-       combine_notes_fn combine;
-       struct string_list *refs;
-       int refs_from_env;
-       int mode_from_env;
-};
-
-struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
-int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
-                         const unsigned char *from_obj, const unsigned char *to_obj);
-void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
-
 extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
@@ -53,6 +40,7 @@ extern int cmd_checkout(int argc, const char **argv, const char *prefix);
 extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
+extern int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
index daf02c6affb6115d058736a6859ceeb10e0791f0..8266a9cb7091c046f63c7b0137daf0bce0180a12 100644 (file)
@@ -28,6 +28,9 @@ struct update_callback_data {
        int add_errors;
        const char *implicit_dot;
        size_t implicit_dot_len;
+
+       /* only needed for 2.0 transition preparation */
+       int warn_add_would_remove;
 };
 
 static const char *option_with_implicit_dot;
@@ -93,6 +96,24 @@ static int fix_unmerged_status(struct diff_filepair *p,
                return DIFF_STATUS_MODIFIED;
 }
 
+static const char *add_would_remove_warning = N_(
+       "You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
+"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
+"Paths like '%s' that are\n"
+"removed from your working tree are ignored with this version of Git.\n"
+"\n"
+"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
+"  ignores paths you removed from your working tree.\n"
+"\n"
+"* 'git add --all <pathspec>' will let you also record the removals.\n"
+"\n"
+"Run 'git status' to check the paths you removed from your working tree.\n");
+
+static void warn_add_would_remove(const char *path)
+{
+       warning(_(add_would_remove_warning), path);
+}
+
 static void update_callback(struct diff_queue_struct *q,
                            struct diff_options *opt, void *cbdata)
 {
@@ -130,6 +151,10 @@ static void update_callback(struct diff_queue_struct *q,
                        }
                        break;
                case DIFF_STATUS_DELETED:
+                       if (data->warn_add_would_remove) {
+                               warn_add_would_remove(path);
+                               data->warn_add_would_remove = 0;
+                       }
                        if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
                                break;
                        if (!(data->flags & ADD_CACHE_PRETEND))
@@ -141,32 +166,28 @@ static void update_callback(struct diff_queue_struct *q,
        }
 }
 
-int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
+static void update_files_in_cache(const char *prefix, const char **pathspec,
+                                 struct update_callback_data *data)
 {
-       struct update_callback_data data;
        struct rev_info rev;
 
-       memset(&data, 0, sizeof(data));
-       data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
-       if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) {
-               /*
-                * Check for modified files throughout the worktree so
-                * update_callback has a chance to warn about changes
-                * outside the cwd.
-                */
-               data.implicit_dot = prefix;
-               data.implicit_dot_len = strlen(prefix);
-               pathspec = NULL;
-       }
-
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
        init_pathspec(&rev.prune_data, pathspec);
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
-       rev.diffopt.format_callback_data = &data;
+       rev.diffopt.format_callback_data = data;
        rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+}
+
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
+{
+       struct update_callback_data data;
+
+       memset(&data, 0, sizeof(data));
+       data.flags = flags;
+       update_files_in_cache(prefix, pathspec, &data);
        return !!data.add_errors;
 }
 
@@ -322,6 +343,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 
        argc = setup_revisions(argc, argv, &rev, NULL);
        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+       rev.diffopt.use_color = 0;
        DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
        out = open(file, O_CREAT | O_WRONLY, 0666);
        if (out < 0)
@@ -354,23 +376,38 @@ static struct lock_file lock_file;
 static const char ignore_error[] =
 N_("The following paths are ignored by one of your .gitignore files:\n");
 
-static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
-static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
+static int verbose, show_only, ignored_too, refresh_only;
+static int ignore_add_errors, intent_to_add, ignore_missing;
+
+#define ADDREMOVE_DEFAULT 0 /* Change to 1 in Git 2.0 */
+static int addremove = ADDREMOVE_DEFAULT;
+static int addremove_explicit = -1; /* unspecified */
+
+static int ignore_removal_cb(const struct option *opt, const char *arg, int unset)
+{
+       /* if we are told to ignore, we are not adding removals */
+       *(int *)opt->value = !unset ? 0 : 1;
+       return 0;
+}
 
 static struct option builtin_add_options[] = {
        OPT__DRY_RUN(&show_only, N_("dry run")),
        OPT__VERBOSE(&verbose, N_("be verbose")),
        OPT_GROUP(""),
-       OPT_BOOLEAN('i', "interactive", &add_interactive, N_("interactive picking")),
-       OPT_BOOLEAN('p', "patch", &patch_interactive, N_("select hunks interactively")),
-       OPT_BOOLEAN('e', "edit", &edit_interactive, N_("edit current diff and apply")),
+       OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")),
+       OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")),
+       OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")),
        OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
-       OPT_BOOLEAN('u', "update", &take_worktree_changes, N_("update tracked files")),
-       OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
-       OPT_BOOLEAN('A', "all", &addremove, N_("add changes from all tracked and untracked files")),
-       OPT_BOOLEAN( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
-       OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
-       OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
+       OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")),
+       OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
+       OPT_BOOL('A', "all", &addremove_explicit, N_("add changes from all tracked and untracked files")),
+       { OPTION_CALLBACK, 0, "ignore-removal", &addremove_explicit,
+         NULL /* takes no arguments */,
+         N_("ignore paths removed in the working tree (same as --no-all)"),
+         PARSE_OPT_NOARG, ignore_removal_cb },
+       OPT_BOOL( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
+       OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
+       OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
        OPT_END(),
 };
 
@@ -416,6 +453,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        int require_pathspec;
        char *seen = NULL;
        int implicit_dot = 0;
+       struct update_callback_data update_data;
 
        git_config(add_config, NULL);
 
@@ -431,8 +469,29 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        argc--;
        argv++;
 
+       if (0 <= addremove_explicit)
+               addremove = addremove_explicit;
+       else if (take_worktree_changes && ADDREMOVE_DEFAULT)
+               addremove = 0; /* "-u" was given but not "-A" */
+
        if (addremove && take_worktree_changes)
                die(_("-A and -u are mutually incompatible"));
+
+       /*
+        * Warn when "git add pathspec..." was given without "-u" or "-A"
+        * and pathspec... covers a removed path.
+        */
+       memset(&update_data, 0, sizeof(update_data));
+       if (!take_worktree_changes && addremove_explicit < 0)
+               update_data.warn_add_would_remove = 1;
+
+       if (!take_worktree_changes && addremove_explicit < 0 && argc)
+               /*
+                * Turn "git add pathspec..." to "git add -A pathspec..."
+                * in Git 2.0 but not yet
+                */
+               ; /* addremove = 1; */
+
        if (!show_only && ignore_missing)
                die(_("Option --ignore-missing can only be used together with --dry-run"));
        if (addremove) {
@@ -495,12 +554,12 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                refresh(verbose, pathspec);
                goto finish;
        }
+       if (implicit_dot && prefix)
+               refresh_cache(REFRESH_QUIET);
 
        if (pathspec) {
                int i;
-               struct path_exclude_check check;
 
-               path_exclude_check_init(&check, &dir);
                if (!seen)
                        seen = find_pathspecs_matching_against_index(pathspec);
                for (i = 0; pathspec[i]; i++) {
@@ -508,7 +567,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                            && !file_exists(pathspec[i])) {
                                if (ignore_missing) {
                                        int dtype = DT_UNKNOWN;
-                                       if (is_path_excluded(&check, pathspec[i], -1, &dtype))
+                                       if (is_excluded(&dir, pathspec[i], &dtype))
                                                dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
                                } else
                                        die(_("pathspec '%s' did not match any files"),
@@ -516,13 +575,24 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                        }
                }
                free(seen);
-               path_exclude_check_clear(&check);
        }
 
        plug_bulk_checkin();
 
-       exit_status |= add_files_to_cache(prefix, pathspec, flags);
+       if ((flags & ADD_CACHE_IMPLICIT_DOT) && prefix) {
+               /*
+                * Check for modified files throughout the worktree so
+                * update_callback has a chance to warn about changes
+                * outside the cwd.
+                */
+               update_data.implicit_dot = prefix;
+               update_data.implicit_dot_len = strlen(prefix);
+               pathspec = NULL;
+       }
+       update_data.flags = flags & ~ADD_CACHE_IMPLICIT_DOT;
+       update_files_in_cache(prefix, pathspec, &update_data);
 
+       exit_status |= !!update_data.add_errors;
        if (add_new_files)
                exit_status |= add_files(&dir, flags);
 
index 30eefc3c7b390800e9452a06da18208d2fef4f11..50912c928f79cc0bcb4acf3ae50fd9fdc32193e8 100644 (file)
@@ -473,7 +473,7 @@ static char *find_name_gnu(const char *line, const char *def, int p_value)
 
        /*
         * Proposed "new-style" GNU patch/diff format; see
-        * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+        * http://marc.info/?l=git&m=112927316408690&w=2
         */
        if (unquote_c_style(&name, line, NULL)) {
                strbuf_release(&name);
@@ -722,7 +722,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
 
 static char *find_name_traditional(const char *line, char *def, int p_value)
 {
-       size_t len = strlen(line);
+       size_t len;
        size_t date_len;
 
        if (*line == '"') {
@@ -906,7 +906,7 @@ static void parse_traditional_patch(const char *first, const char *second, struc
                        patch->old_name = name;
                } else {
                        patch->old_name = name;
-                       patch->new_name = xstrdup(name);
+                       patch->new_name = null_strdup(name);
                }
        }
        if (!name)
@@ -2999,7 +2999,7 @@ static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsig
        return 0;
 }
 
-static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
+static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf)
 {
        if (!ce)
                return 0;
@@ -3117,7 +3117,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone)
        return previous;
 }
 
-static int verify_index_match(struct cache_entry *ce, struct stat *st)
+static int verify_index_match(const struct cache_entry *ce, struct stat *st)
 {
        if (S_ISGITLINK(ce->ce_mode)) {
                if (!S_ISDIR(st->st_mode))
@@ -3130,7 +3130,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st)
 #define SUBMODULE_PATCH_WITHOUT_INDEX 1
 
 static int load_patch_target(struct strbuf *buf,
-                            struct cache_entry *ce,
+                            const struct cache_entry *ce,
                             struct stat *st,
                             const char *name,
                             unsigned expected_mode)
@@ -3160,7 +3160,8 @@ static int load_patch_target(struct strbuf *buf,
  * we read from the result of a previous diff.
  */
 static int load_preimage(struct image *image,
-                        struct patch *patch, struct stat *st, struct cache_entry *ce)
+                        struct patch *patch, struct stat *st,
+                        const struct cache_entry *ce)
 {
        struct strbuf buf = STRBUF_INIT;
        size_t len;
@@ -3273,7 +3274,7 @@ static int load_current(struct image *image, struct patch *patch)
 }
 
 static int try_threeway(struct image *image, struct patch *patch,
-                       struct stat *st, struct cache_entry *ce)
+                       struct stat *st, const struct cache_entry *ce)
 {
        unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
        struct strbuf buf = STRBUF_INIT;
@@ -3343,7 +3344,7 @@ static int try_threeway(struct image *image, struct patch *patch,
        return 0;
 }
 
-static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
+static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce)
 {
        struct image image;
 
@@ -3525,7 +3526,7 @@ static int check_patch(struct patch *patch)
                ok_if_exists = 0;
 
        if (new_name &&
-           ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
+           ((0 < patch->is_new) || patch->is_rename || patch->is_copy)) {
                int err = check_to_create(new_name, ok_if_exists);
 
                if (err && threeway) {
@@ -3847,7 +3848,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
                const char *s = buf;
 
                if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
-                       die(_("corrupt patch for subproject %s"), path);
+                       die(_("corrupt patch for submodule %s"), path);
        } else {
                if (!cached) {
                        if (lstat(path, &st) < 0)
index 86100e96627e4f839fb5b627bc09ed4264c36176..079dcd3407881ce0ea988c3f487faf8ca6d3e5d2 100644 (file)
@@ -21,6 +21,7 @@
 #include "parse-options.h"
 #include "utf8.h"
 #include "userdiff.h"
+#include "line-range.h"
 
 static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file");
 
@@ -566,11 +567,16 @@ static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
        dst->score = 0;
 }
 
-static const char *nth_line(struct scoreboard *sb, int lno)
+static const char *nth_line(struct scoreboard *sb, long lno)
 {
        return sb->final_buf + sb->lineno[lno];
 }
 
+static const char *nth_line_cb(void *data, long lno)
+{
+       return nth_line((struct scoreboard *)data, lno);
+}
+
 /*
  * It is known that lines between tlno to same came from parent, and e
  * has an overlap with that range.  it also is known that parent's
@@ -1375,10 +1381,15 @@ static void get_ac_line(const char *inbuf, const char *what,
        maillen = ident.mail_end - ident.mail_begin;
        mailbuf = ident.mail_begin;
 
-       *time = strtoul(ident.date_begin, NULL, 10);
+       if (ident.date_begin && ident.date_end)
+               *time = strtoul(ident.date_begin, NULL, 10);
+       else
+               *time = 0;
 
-       len = ident.tz_end - ident.tz_begin;
-       strbuf_add(tz, ident.tz_begin, len);
+       if (ident.tz_begin && ident.tz_end)
+               strbuf_add(tz, ident.tz_begin, ident.tz_end - ident.tz_begin);
+       else
+               strbuf_addstr(tz, "(unknown)");
 
        /*
         * Now, convert both name and e-mail using mailmap
@@ -1425,7 +1436,7 @@ static void get_commit_info(struct commit *commit,
        commit_info_init(ret);
 
        encoding = get_log_output_encoding();
-       message = logmsg_reencode(commit, encoding);
+       message = logmsg_reencode(commit, NULL, encoding);
        get_ac_line(message, "\nauthor ",
                    &ret->author, &ret->author_mail,
                    &ret->author_time, &ret->author_tz);
@@ -1926,83 +1937,6 @@ static const char *add_prefix(const char *prefix, const char *path)
        return prefix_path(prefix, prefix ? strlen(prefix) : 0, path);
 }
 
-/*
- * Parsing of (comma separated) one item in the -L option
- */
-static const char *parse_loc(const char *spec,
-                            struct scoreboard *sb, long lno,
-                            long begin, long *ret)
-{
-       char *term;
-       const char *line;
-       long num;
-       int reg_error;
-       regex_t regexp;
-       regmatch_t match[1];
-
-       /* Allow "-L <something>,+20" to mean starting at <something>
-        * for 20 lines, or "-L <something>,-5" for 5 lines ending at
-        * <something>.
-        */
-       if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
-               num = strtol(spec + 1, &term, 10);
-               if (term != spec + 1) {
-                       if (spec[0] == '-')
-                               num = 0 - num;
-                       if (0 < num)
-                               *ret = begin + num - 2;
-                       else if (!num)
-                               *ret = begin;
-                       else
-                               *ret = begin + num;
-                       return term;
-               }
-               return spec;
-       }
-       num = strtol(spec, &term, 10);
-       if (term != spec) {
-               *ret = num;
-               return term;
-       }
-       if (spec[0] != '/')
-               return spec;
-
-       /* it could be a regexp of form /.../ */
-       for (term = (char *) spec + 1; *term && *term != '/'; term++) {
-               if (*term == '\\')
-                       term++;
-       }
-       if (*term != '/')
-               return spec;
-
-       /* try [spec+1 .. term-1] as regexp */
-       *term = 0;
-       begin--; /* input is in human terms */
-       line = nth_line(sb, begin);
-
-       if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
-           !(reg_error = regexec(&regexp, line, 1, match, 0))) {
-               const char *cp = line + match[0].rm_so;
-               const char *nline;
-
-               while (begin++ < lno) {
-                       nline = nth_line(sb, begin);
-                       if (line <= cp && cp < nline)
-                               break;
-                       line = nline;
-               }
-               *ret = begin;
-               regfree(&regexp);
-               *term++ = '/';
-               return term;
-       }
-       else {
-               char errbuf[1024];
-               regerror(reg_error, &regexp, errbuf, 1024);
-               die("-L parameter '%s': %s", spec + 1, errbuf);
-       }
-}
-
 /*
  * Parsing of -L option
  */
@@ -2011,15 +1945,7 @@ static void prepare_blame_range(struct scoreboard *sb,
                                long lno,
                                long *bottom, long *top)
 {
-       const char *term;
-
-       term = parse_loc(bottomtop, sb, lno, 1, bottom);
-       if (*term == ',') {
-               term = parse_loc(term + 1, sb, lno, *bottom + 1, top);
-               if (*term)
-                       usage(blame_usage);
-       }
-       if (*term)
+       if (parse_range_arg(bottomtop, nth_line_cb, sb, lno, bottom, top, sb->path))
                usage(blame_usage);
 }
 
@@ -2569,10 +2495,6 @@ parse_done:
        bottom = top = 0;
        if (bottomtop)
                prepare_blame_range(&sb, bottomtop, lno, &bottom, &top);
-       if (bottom && top && top < bottom) {
-               long tmp;
-               tmp = top; top = bottom; bottom = tmp;
-       }
        if (bottom < 1)
                bottom = 1;
        if (top < 1)
index 40f87b4649aa97ca1f33a02f09f5938ce5f41f74..425346048bbba66874eb857985bed9937c44e1d6 100644 (file)
 #include "userdiff.h"
 #include "streaming.h"
 
-#define BATCH 1
-#define BATCH_CHECK 2
-
-static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
-{
-       /* the parser in tag.c is useless here. */
-       const char *endp = buf + size;
-       const char *cp = buf;
-
-       while (cp < endp) {
-               char c = *cp++;
-               if (c != '\n')
-                       continue;
-               if (7 <= endp - cp && !memcmp("tagger ", cp, 7)) {
-                       const char *tagger = cp;
-
-                       /* Found the tagger line.  Copy out the contents
-                        * of the buffer so far.
-                        */
-                       write_or_die(1, buf, cp - buf);
-
-                       /*
-                        * Do something intelligent, like pretty-printing
-                        * the date.
-                        */
-                       while (cp < endp) {
-                               if (*cp++ == '\n') {
-                                       /* tagger to cp is a line
-                                        * that has ident and time.
-                                        */
-                                       const char *sp = tagger;
-                                       char *ep;
-                                       unsigned long date;
-                                       long tz;
-                                       while (sp < cp && *sp != '>')
-                                               sp++;
-                                       if (sp == cp) {
-                                               /* give up */
-                                               write_or_die(1, tagger,
-                                                            cp - tagger);
-                                               break;
-                                       }
-                                       while (sp < cp &&
-                                              !('0' <= *sp && *sp <= '9'))
-                                               sp++;
-                                       write_or_die(1, tagger, sp - tagger);
-                                       date = strtoul(sp, &ep, 10);
-                                       tz = strtol(ep, NULL, 10);
-                                       sp = show_date(date, tz, 0);
-                                       write_or_die(1, sp, strlen(sp));
-                                       xwrite(1, "\n", 1);
-                                       break;
-                               }
-                       }
-                       break;
-               }
-               if (cp < endp && *cp == '\n')
-                       /* end of header */
-                       break;
-       }
-       /* At this point, we have copied out the header up to the end of
-        * the tagger line and cp points at one past \n.  It could be the
-        * next header line after the tagger line, or it could be another
-        * \n that marks the end of the headers.  We need to copy out the
-        * remainder as is.
-        */
-       if (cp < endp)
-               write_or_die(1, cp, endp - cp);
-}
-
 static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
 {
        unsigned char sha1[20];
@@ -133,10 +63,6 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                buf = read_sha1_file(sha1, &type, &size);
                if (!buf)
                        die("Cannot read object %s", obj_name);
-               if (type == OBJ_TAG) {
-                       pprint_tag(sha1, buf, size);
-                       return 0;
-               }
 
                /* otherwise just spit out the data */
                break;
@@ -188,54 +114,166 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
        return 0;
 }
 
-static int batch_one_object(const char *obj_name, int print_contents)
-{
+struct expand_data {
        unsigned char sha1[20];
-       enum object_type type = 0;
+       enum object_type type;
        unsigned long size;
-       void *contents = NULL;
+       unsigned long disk_size;
+
+       /*
+        * If mark_query is true, we do not expand anything, but rather
+        * just mark the object_info with items we wish to query.
+        */
+       int mark_query;
+
+       /*
+        * After a mark_query run, this object_info is set up to be
+        * passed to sha1_object_info_extended. It will point to the data
+        * elements above, so you can retrieve the response from there.
+        */
+       struct object_info info;
+};
+
+static int is_atom(const char *atom, const char *s, int slen)
+{
+       int alen = strlen(atom);
+       return alen == slen && !memcmp(atom, s, alen);
+}
+
+static void expand_atom(struct strbuf *sb, const char *atom, int len,
+                       void *vdata)
+{
+       struct expand_data *data = vdata;
+
+       if (is_atom("objectname", atom, len)) {
+               if (!data->mark_query)
+                       strbuf_addstr(sb, sha1_to_hex(data->sha1));
+       } else if (is_atom("objecttype", atom, len)) {
+               if (data->mark_query)
+                       data->info.typep = &data->type;
+               else
+                       strbuf_addstr(sb, typename(data->type));
+       } else if (is_atom("objectsize", atom, len)) {
+               if (data->mark_query)
+                       data->info.sizep = &data->size;
+               else
+                       strbuf_addf(sb, "%lu", data->size);
+       } else if (is_atom("objectsize:disk", atom, len)) {
+               if (data->mark_query)
+                       data->info.disk_sizep = &data->disk_size;
+               else
+                       strbuf_addf(sb, "%lu", data->disk_size);
+       } else
+               die("unknown format element: %.*s", len, atom);
+}
+
+static size_t expand_format(struct strbuf *sb, const char *start, void *data)
+{
+       const char *end;
+
+       if (*start != '(')
+               return 0;
+       end = strchr(start + 1, ')');
+       if (!end)
+               die("format element '%s' does not end in ')'", start);
+
+       expand_atom(sb, start + 1, end - start - 1, data);
+
+       return end - start + 1;
+}
+
+static void print_object_or_die(int fd, const unsigned char *sha1,
+                               enum object_type type, unsigned long size)
+{
+       if (type == OBJ_BLOB) {
+               if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0)
+                       die("unable to stream %s to stdout", sha1_to_hex(sha1));
+       }
+       else {
+               enum object_type rtype;
+               unsigned long rsize;
+               void *contents;
+
+               contents = read_sha1_file(sha1, &rtype, &rsize);
+               if (!contents)
+                       die("object %s disappeared", sha1_to_hex(sha1));
+               if (rtype != type)
+                       die("object %s changed type!?", sha1_to_hex(sha1));
+               if (rsize != size)
+                       die("object %s change size!?", sha1_to_hex(sha1));
+
+               write_or_die(fd, contents, size);
+               free(contents);
+       }
+}
+
+struct batch_options {
+       int enabled;
+       int print_contents;
+       const char *format;
+};
+
+static int batch_one_object(const char *obj_name, struct batch_options *opt,
+                           struct expand_data *data)
+{
+       struct strbuf buf = STRBUF_INIT;
 
        if (!obj_name)
           return 1;
 
-       if (get_sha1(obj_name, sha1)) {
+       if (get_sha1(obj_name, data->sha1)) {
                printf("%s missing\n", obj_name);
                fflush(stdout);
                return 0;
        }
 
-       if (print_contents == BATCH)
-               contents = read_sha1_file(sha1, &type, &size);
-       else
-               type = sha1_object_info(sha1, &size);
-
-       if (type <= 0) {
+       if (sha1_object_info_extended(data->sha1, &data->info) < 0) {
                printf("%s missing\n", obj_name);
                fflush(stdout);
-               if (print_contents == BATCH)
-                       free(contents);
                return 0;
        }
 
-       printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size);
-       fflush(stdout);
+       strbuf_expand(&buf, opt->format, expand_format, data);
+       strbuf_addch(&buf, '\n');
+       write_or_die(1, buf.buf, buf.len);
+       strbuf_release(&buf);
 
-       if (print_contents == BATCH) {
-               write_or_die(1, contents, size);
-               printf("\n");
-               fflush(stdout);
-               free(contents);
+       if (opt->print_contents) {
+               print_object_or_die(1, data->sha1, data->type, data->size);
+               write_or_die(1, "\n", 1);
        }
-
        return 0;
 }
 
-static int batch_objects(int print_contents)
+static int batch_objects(struct batch_options *opt)
 {
        struct strbuf buf = STRBUF_INIT;
+       struct expand_data data;
+
+       if (!opt->format)
+               opt->format = "%(objectname) %(objecttype) %(objectsize)";
+
+       /*
+        * Expand once with our special mark_query flag, which will prime the
+        * object_info to be handed to sha1_object_info_extended for each
+        * object.
+        */
+       memset(&data, 0, sizeof(data));
+       data.mark_query = 1;
+       strbuf_expand(&buf, opt->format, expand_format, &data);
+       data.mark_query = 0;
+
+       /*
+        * We are going to call get_sha1 on a potentially very large number of
+        * objects. In most large cases, these will be actual object sha1s. The
+        * cost to double-check that each one is not also a ref (just so we can
+        * warn) ends up dwarfing the actual cost of the object lookups
+        * themselves. We can work around it by just turning off the warning.
+        */
+       warn_on_object_refname_ambiguity = 0;
 
        while (strbuf_getline(&buf, stdin, '\n') != EOF) {
-               int error = batch_one_object(buf.buf, print_contents);
+               int error = batch_one_object(buf.buf, opt, &data);
                if (error)
                        return error;
        }
@@ -257,10 +295,29 @@ static int git_cat_file_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
+static int batch_option_callback(const struct option *opt,
+                                const char *arg,
+                                int unset)
+{
+       struct batch_options *bo = opt->value;
+
+       if (unset) {
+               memset(bo, 0, sizeof(*bo));
+               return 0;
+       }
+
+       bo->enabled = 1;
+       bo->print_contents = !strcmp(opt->long_name, "batch");
+       bo->format = arg;
+
+       return 0;
+}
+
 int cmd_cat_file(int argc, const char **argv, const char *prefix)
 {
-       int opt = 0, batch = 0;
+       int opt = 0;
        const char *exp_type = NULL, *obj_name = NULL;
+       struct batch_options batch = {0};
 
        const struct option options[] = {
                OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")),
@@ -271,12 +328,12 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
                OPT_SET_INT(0, "textconv", &opt,
                            N_("for blob objects, run textconv on object's content"), 'c'),
-               OPT_SET_INT(0, "batch", &batch,
-                           N_("show info and content of objects fed from the standard input"),
-                           BATCH),
-               OPT_SET_INT(0, "batch-check", &batch,
-                           N_("show info about objects fed from the standard input"),
-                           BATCH_CHECK),
+               { OPTION_CALLBACK, 0, "batch", &batch, "format",
+                       N_("show info and content of objects fed from the standard input"),
+                       PARSE_OPT_OPTARG, batch_option_callback },
+               { OPTION_CALLBACK, 0, "batch-check", &batch, "format",
+                       N_("show info about objects fed from the standard input"),
+                       PARSE_OPT_OPTARG, batch_option_callback },
                OPT_END()
        };
 
@@ -293,19 +350,19 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
                else
                        usage_with_options(cat_file_usage, options);
        }
-       if (!opt && !batch) {
+       if (!opt && !batch.enabled) {
                if (argc == 2) {
                        exp_type = argv[0];
                        obj_name = argv[1];
                } else
                        usage_with_options(cat_file_usage, options);
        }
-       if (batch && (opt || argc)) {
+       if (batch.enabled && (opt || argc)) {
                usage_with_options(cat_file_usage, options);
        }
 
-       if (batch)
-               return batch_objects(batch);
+       if (batch.enabled)
+               return batch_objects(&batch);
 
        return cat_one_file(opt, exp_type, obj_name);
 }
index 0240f99b57a2f81320b84951bf6dc89ab60d6282..4a8fc707c747596e31dcc6f57abf5f965cdf612f 100644 (file)
@@ -5,7 +5,7 @@
 #include "pathspec.h"
 #include "parse-options.h"
 
-static int quiet, verbose, stdin_paths;
+static int quiet, verbose, stdin_paths, show_non_matching;
 static const char * const check_ignore_usage[] = {
 "git check-ignore [options] pathname...",
 "git check-ignore [options] --stdin < <list-of-paths>",
@@ -22,21 +22,28 @@ static const struct option check_ignore_options[] = {
                    N_("read file names from stdin")),
        OPT_BOOLEAN('z', NULL, &null_term_line,
                    N_("input paths are terminated by a null character")),
+       OPT_BOOLEAN('n', "non-matching", &show_non_matching,
+                   N_("show non-matching input paths")),
        OPT_END()
 };
 
 static void output_exclude(const char *path, struct exclude *exclude)
 {
-       char *bang  = exclude->flags & EXC_FLAG_NEGATIVE  ? "!" : "";
-       char *slash = exclude->flags & EXC_FLAG_MUSTBEDIR ? "/" : "";
+       char *bang  = (exclude && exclude->flags & EXC_FLAG_NEGATIVE)  ? "!" : "";
+       char *slash = (exclude && exclude->flags & EXC_FLAG_MUSTBEDIR) ? "/" : "";
        if (!null_term_line) {
                if (!verbose) {
                        write_name_quoted(path, stdout, '\n');
                } else {
-                       quote_c_style(exclude->el->src, NULL, stdout, 0);
-                       printf(":%d:%s%s%s\t",
-                              exclude->srcpos,
-                              bang, exclude->pattern, slash);
+                       if (exclude) {
+                               quote_c_style(exclude->el->src, NULL, stdout, 0);
+                               printf(":%d:%s%s%s\t",
+                                      exclude->srcpos,
+                                      bang, exclude->pattern, slash);
+                       }
+                       else {
+                               printf("::\t");
+                       }
                        quote_c_style(path, NULL, stdout, 0);
                        fputc('\n', stdout);
                }
@@ -44,39 +51,32 @@ static void output_exclude(const char *path, struct exclude *exclude)
                if (!verbose) {
                        printf("%s%c", path, '\0');
                } else {
-                       printf("%s%c%d%c%s%s%s%c%s%c",
-                              exclude->el->src, '\0',
-                              exclude->srcpos, '\0',
-                              bang, exclude->pattern, slash, '\0',
-                              path, '\0');
+                       if (exclude)
+                               printf("%s%c%d%c%s%s%s%c%s%c",
+                                      exclude->el->src, '\0',
+                                      exclude->srcpos, '\0',
+                                      bang, exclude->pattern, slash, '\0',
+                                      path, '\0');
+                       else
+                               printf("%c%c%c%s%c", '\0', '\0', '\0', path, '\0');
                }
        }
 }
 
-static int check_ignore(const char *prefix, const char **pathspec)
+static int check_ignore(struct dir_struct *dir,
+                       const char *prefix, const char **pathspec)
 {
-       struct dir_struct dir;
        const char *path, *full_path;
        char *seen;
        int num_ignored = 0, dtype = DT_UNKNOWN, i;
-       struct path_exclude_check check;
        struct exclude *exclude;
 
-       /* read_cache() is only necessary so we can watch out for submodules. */
-       if (read_cache() < 0)
-               die(_("index file corrupt"));
-
-       memset(&dir, 0, sizeof(dir));
-       dir.flags |= DIR_COLLECT_IGNORED;
-       setup_standard_excludes(&dir);
-
        if (!pathspec || !*pathspec) {
                if (!quiet)
                        fprintf(stderr, "no pathspec given.\n");
                return 0;
        }
 
-       path_exclude_check_init(&check, &dir);
        /*
         * look for pathspecs matching entries in the index, since these
         * should not be ignored, in order to be consistent with
@@ -89,30 +89,26 @@ static int check_ignore(const char *prefix, const char **pathspec)
                                        ? strlen(prefix) : 0, path);
                full_path = check_path_for_gitlink(full_path);
                die_if_path_beyond_symlink(full_path, prefix);
+               exclude = NULL;
                if (!seen[i]) {
-                       exclude = last_exclude_matching_path(&check, full_path,
-                                                            -1, &dtype);
-                       if (exclude) {
-                               if (!quiet)
-                                       output_exclude(path, exclude);
-                               num_ignored++;
-                       }
+                       exclude = last_exclude_matching(dir, full_path, &dtype);
                }
+               if (!quiet && (exclude || show_non_matching))
+                       output_exclude(path, exclude);
+               if (exclude)
+                       num_ignored++;
        }
        free(seen);
-       clear_directory(&dir);
-       path_exclude_check_clear(&check);
 
        return num_ignored;
 }
 
-static int check_ignore_stdin_paths(const char *prefix)
+static int check_ignore_stdin_paths(struct dir_struct *dir, const char *prefix)
 {
        struct strbuf buf, nbuf;
-       char **pathspec = NULL;
-       size_t nr = 0, alloc = 0;
+       char *pathspec[2] = { NULL, NULL };
        int line_termination = null_term_line ? 0 : '\n';
-       int num_ignored;
+       int num_ignored = 0;
 
        strbuf_init(&buf, 0);
        strbuf_init(&nbuf, 0);
@@ -123,23 +119,19 @@ static int check_ignore_stdin_paths(const char *prefix)
                                die("line is badly quoted");
                        strbuf_swap(&buf, &nbuf);
                }
-               ALLOC_GROW(pathspec, nr + 1, alloc);
-               pathspec[nr] = xcalloc(strlen(buf.buf) + 1, sizeof(*buf.buf));
-               strcpy(pathspec[nr++], buf.buf);
+               pathspec[0] = buf.buf;
+               num_ignored += check_ignore(dir, prefix, (const char **)pathspec);
+               maybe_flush_or_die(stdout, "check-ignore to stdout");
        }
-       ALLOC_GROW(pathspec, nr + 1, alloc);
-       pathspec[nr] = NULL;
-       num_ignored = check_ignore(prefix, (const char **)pathspec);
-       maybe_flush_or_die(stdout, "attribute to stdout");
        strbuf_release(&buf);
        strbuf_release(&nbuf);
-       free(pathspec);
        return num_ignored;
 }
 
 int cmd_check_ignore(int argc, const char **argv, const char *prefix)
 {
        int num_ignored;
+       struct dir_struct dir;
 
        git_config(git_default_config, NULL);
 
@@ -161,13 +153,24 @@ int cmd_check_ignore(int argc, const char **argv, const char *prefix)
                if (verbose)
                        die(_("cannot have both --quiet and --verbose"));
        }
+       if (show_non_matching && !verbose)
+               die(_("--non-matching is only valid with --verbose"));
+
+       /* read_cache() is only necessary so we can watch out for submodules. */
+       if (read_cache() < 0)
+               die(_("index file corrupt"));
+
+       memset(&dir, 0, sizeof(dir));
+       setup_standard_excludes(&dir);
 
        if (stdin_paths) {
-               num_ignored = check_ignore_stdin_paths(prefix);
+               num_ignored = check_ignore_stdin_paths(&dir, prefix);
        } else {
-               num_ignored = check_ignore(prefix, argv);
+               num_ignored = check_ignore(&dir, prefix, argv);
                maybe_flush_or_die(stdout, "ignore to stdout");
        }
 
+       clear_directory(&dir);
+
        return !num_ignored;
 }
diff --git a/builtin/check-mailmap.c b/builtin/check-mailmap.c
new file mode 100644 (file)
index 0000000..8f4d809
--- /dev/null
@@ -0,0 +1,66 @@
+#include "builtin.h"
+#include "mailmap.h"
+#include "parse-options.h"
+#include "string-list.h"
+
+static int use_stdin;
+static const char * const check_mailmap_usage[] = {
+N_("git check-mailmap [options] <contact>..."),
+NULL
+};
+
+static const struct option check_mailmap_options[] = {
+       OPT_BOOL(0, "stdin", &use_stdin, N_("also read contacts from stdin")),
+       OPT_END()
+};
+
+static void check_mailmap(struct string_list *mailmap, const char *contact)
+{
+       const char *name, *mail;
+       size_t namelen, maillen;
+       struct ident_split ident;
+
+       if (split_ident_line(&ident, contact, strlen(contact)))
+               die(_("unable to parse contact: %s"), contact);
+
+       name = ident.name_begin;
+       namelen = ident.name_end - ident.name_begin;
+       mail = ident.mail_begin;
+       maillen = ident.mail_end - ident.mail_begin;
+
+       map_user(mailmap, &mail, &maillen, &name, &namelen);
+
+       if (namelen)
+               printf("%.*s ", (int)namelen, name);
+       printf("<%.*s>\n", (int)maillen, mail);
+}
+
+int cmd_check_mailmap(int argc, const char **argv, const char *prefix)
+{
+       int i;
+       struct string_list mailmap = STRING_LIST_INIT_NODUP;
+
+       git_config(git_default_config, NULL);
+       argc = parse_options(argc, argv, prefix, check_mailmap_options,
+                            check_mailmap_usage, 0);
+       if (argc == 0 && !use_stdin)
+               die(_("no contacts specified"));
+
+       read_mailmap(&mailmap, NULL);
+
+       for (i = 0; i < argc; ++i)
+               check_mailmap(&mailmap, argv[i]);
+       maybe_flush_or_die(stdout, "stdout");
+
+       if (use_stdin) {
+               struct strbuf buf = STRBUF_INIT;
+               while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+                       check_mailmap(&mailmap, buf.buf);
+                       maybe_flush_or_die(stdout, "stdout");
+               }
+               strbuf_release(&buf);
+       }
+
+       clear_mailmap(&mailmap);
+       return 0;
+}
index bcb18c8d206774efde9453a64775ebd9379ea342..7025938ae30f2ce3104b27f66bf6e1f294edb810 100644 (file)
@@ -35,6 +35,7 @@ struct checkout_opts {
        int force_detach;
        int writeout_stage;
        int overwrite_ignore;
+       int ignore_skipworktree;
 
        const char *new_branch;
        const char *new_branch_force;
@@ -96,7 +97,7 @@ static int read_tree_some(struct tree *tree, const char **pathspec)
        return 0;
 }
 
-static int skip_same_name(struct cache_entry *ce, int pos)
+static int skip_same_name(const struct cache_entry *ce, int pos)
 {
        while (++pos < active_nr &&
               !strcmp(active_cache[pos]->name, ce->name))
@@ -104,7 +105,7 @@ static int skip_same_name(struct cache_entry *ce, int pos)
        return pos;
 }
 
-static int check_stage(int stage, struct cache_entry *ce, int pos)
+static int check_stage(int stage, const struct cache_entry *ce, int pos)
 {
        while (pos < active_nr &&
               !strcmp(active_cache[pos]->name, ce->name)) {
@@ -118,7 +119,7 @@ static int check_stage(int stage, struct cache_entry *ce, int pos)
                return error(_("path '%s' does not have their version"), ce->name);
 }
 
-static int check_stages(unsigned stages, struct cache_entry *ce, int pos)
+static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
 {
        unsigned seen = 0;
        const char *name = ce->name;
@@ -278,6 +279,8 @@ static int checkout_paths(const struct checkout_opts *opts,
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
                ce->ce_flags &= ~CE_MATCHED;
+               if (!opts->ignore_skipworktree && ce_skip_worktree(ce))
+                       continue;
                if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
                        /*
                         * "git checkout tree-ish -- path", but this entry
@@ -318,7 +321,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        /* Any unmerged paths? */
        for (pos = 0; pos < active_nr; pos++) {
-               struct cache_entry *ce = active_cache[pos];
+               const struct cache_entry *ce = active_cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce))
                                continue;
@@ -584,7 +587,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
                                   struct branch_info *new)
 {
        struct strbuf msg = STRBUF_INIT;
-       const char *old_desc;
+       const char *old_desc, *reflog_msg;
        if (opts->new_branch) {
                if (opts->new_orphan_branch) {
                        if (opts->new_branch_log && !log_all_ref_updates) {
@@ -617,8 +620,13 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        old_desc = old->name;
        if (!old_desc && old->commit)
                old_desc = sha1_to_hex(old->commit->object.sha1);
-       strbuf_addf(&msg, "checkout: moving from %s to %s",
-                   old_desc ? old_desc : "(invalid)", new->name);
+
+       reflog_msg = getenv("GIT_REFLOG_ACTION");
+       if (!reflog_msg)
+               strbuf_addf(&msg, "checkout: moving from %s to %s",
+                       old_desc ? old_desc : "(invalid)", new->name);
+       else
+               strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg));
 
        if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
                /* Nothing to do. */
@@ -835,13 +843,16 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
        memset(&query, 0, sizeof(struct refspec));
        query.src = cb->src_ref;
        if (remote_find_tracking(remote, &query) ||
-           get_sha1(query.dst, cb->dst_sha1))
+           get_sha1(query.dst, cb->dst_sha1)) {
+               free(query.dst);
                return 0;
+       }
        if (cb->dst_ref) {
+               free(query.dst);
                cb->unique = 0;
                return 0;
        }
-       cb->dst_ref = xstrdup(query.dst);
+       cb->dst_ref = query.dst;
        return 0;
 }
 
@@ -1060,6 +1071,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_STRING(0, "conflict", &conflict_style, N_("style"),
                           N_("conflict style (merge or diff3)")),
                OPT_BOOLEAN('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
+               OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
+                        N_("do not limit pathspecs to sparse entries only")),
                { OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL,
                  N_("second guess 'git checkout no-such-branch'"),
                  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
index 04e396b17acc2443a663dd085ef812c3c2746d27..3c85e152e140741855590f422c599407b6d6ae55 100644 (file)
 #include "refs.h"
 #include "string-list.h"
 #include "quote.h"
+#include "column.h"
+#include "color.h"
 
 static int force = -1; /* unset */
+static int interactive;
+static struct string_list del_list = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
 
 static const char *const builtin_clean_usage[] = {
-       N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
+       N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
        NULL
 };
 
@@ -27,11 +32,112 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
 static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
 static const char *msg_warn_remove_failed = N_("failed to remove %s");
 
+static int clean_use_color = -1;
+static char clean_colors[][COLOR_MAXLEN] = {
+       GIT_COLOR_RESET,
+       GIT_COLOR_NORMAL,       /* PLAIN */
+       GIT_COLOR_BOLD_BLUE,    /* PROMPT */
+       GIT_COLOR_BOLD,         /* HEADER */
+       GIT_COLOR_BOLD_RED,     /* HELP */
+       GIT_COLOR_BOLD_RED,     /* ERROR */
+};
+enum color_clean {
+       CLEAN_COLOR_RESET = 0,
+       CLEAN_COLOR_PLAIN = 1,
+       CLEAN_COLOR_PROMPT = 2,
+       CLEAN_COLOR_HEADER = 3,
+       CLEAN_COLOR_HELP = 4,
+       CLEAN_COLOR_ERROR = 5,
+};
+
+#define MENU_OPTS_SINGLETON            01
+#define MENU_OPTS_IMMEDIATE            02
+#define MENU_OPTS_LIST_ONLY            04
+
+struct menu_opts {
+       const char *header;
+       const char *prompt;
+       int flags;
+};
+
+#define MENU_RETURN_NO_LOOP            10
+
+struct menu_item {
+       char hotkey;
+       const char *title;
+       int selected;
+       int (*fn)();
+};
+
+enum menu_stuff_type {
+       MENU_STUFF_TYPE_STRING_LIST = 1,
+       MENU_STUFF_TYPE_MENU_ITEM
+};
+
+struct menu_stuff {
+       enum menu_stuff_type type;
+       int nr;
+       void *stuff;
+};
+
+static int parse_clean_color_slot(const char *var)
+{
+       if (!strcasecmp(var, "reset"))
+               return CLEAN_COLOR_RESET;
+       if (!strcasecmp(var, "plain"))
+               return CLEAN_COLOR_PLAIN;
+       if (!strcasecmp(var, "prompt"))
+               return CLEAN_COLOR_PROMPT;
+       if (!strcasecmp(var, "header"))
+               return CLEAN_COLOR_HEADER;
+       if (!strcasecmp(var, "help"))
+               return CLEAN_COLOR_HELP;
+       if (!strcasecmp(var, "error"))
+               return CLEAN_COLOR_ERROR;
+       return -1;
+}
+
 static int git_clean_config(const char *var, const char *value, void *cb)
 {
-       if (!strcmp(var, "clean.requireforce"))
+       if (!prefixcmp(var, "column."))
+               return git_column_config(var, value, "clean", &colopts);
+
+       /* honors the color.interactive* config variables which also
+          applied in git-add--interactive and git-stash */
+       if (!strcmp(var, "color.interactive")) {
+               clean_use_color = git_config_colorbool(var, value);
+               return 0;
+       }
+       if (!prefixcmp(var, "color.interactive.")) {
+               int slot = parse_clean_color_slot(var +
+                                                 strlen("color.interactive."));
+               if (slot < 0)
+                       return 0;
+               if (!value)
+                       return config_error_nonbool(var);
+               color_parse(value, var, clean_colors[slot]);
+               return 0;
+       }
+
+       if (!strcmp(var, "clean.requireforce")) {
                force = !git_config_bool(var, value);
-       return git_default_config(var, value, cb);
+               return 0;
+       }
+
+       /* inspect the color.ui config variable and others */
+       return git_color_default_config(var, value, cb);
+}
+
+static const char *clean_get_color(enum color_clean ix)
+{
+       if (want_color(clean_use_color))
+               return clean_colors[ix];
+       return "";
+}
+
+static void clean_print_color(enum color_clean ix)
+{
+       printf("%s", clean_get_color(ix));
 }
 
 static int exclude_cb(const struct option *opt, const char *arg, int unset)
@@ -56,7 +162,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
        if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
                        !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
                if (!quiet) {
-                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       quote_path_relative(path->buf, prefix, &quoted);
                        printf(dry_run ?  _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
                                        quoted.buf);
                }
@@ -70,7 +176,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                /* an empty dir could be removed even if it is unreadble */
                res = dry_run ? 0 : rmdir(path->buf);
                if (res) {
-                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       quote_path_relative(path->buf, prefix, &quoted);
                        warning(_(msg_warn_remove_failed), quoted.buf);
                        *dir_gone = 0;
                }
@@ -94,7 +200,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                        if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
                                ret = 1;
                        if (gone) {
-                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               quote_path_relative(path->buf, prefix, &quoted);
                                string_list_append(&dels, quoted.buf);
                        } else
                                *dir_gone = 0;
@@ -102,10 +208,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                } else {
                        res = dry_run ? 0 : unlink(path->buf);
                        if (!res) {
-                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               quote_path_relative(path->buf, prefix, &quoted);
                                string_list_append(&dels, quoted.buf);
                        } else {
-                               quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                               quote_path_relative(path->buf, prefix, &quoted);
                                warning(_(msg_warn_remove_failed), quoted.buf);
                                *dir_gone = 0;
                                ret = 1;
@@ -127,7 +233,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                if (!res)
                        *dir_gone = 1;
                else {
-                       quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+                       quote_path_relative(path->buf, prefix, &quoted);
                        warning(_(msg_warn_remove_failed), quoted.buf);
                        *dir_gone = 0;
                        ret = 1;
@@ -142,24 +248,633 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
        return ret;
 }
 
+static void pretty_print_dels(void)
+{
+       struct string_list list = STRING_LIST_INIT_DUP;
+       struct string_list_item *item;
+       struct strbuf buf = STRBUF_INIT;
+       const char *qname;
+       struct column_options copts;
+
+       for_each_string_list_item(item, &del_list) {
+               qname = quote_path_relative(item->string, NULL, &buf);
+               string_list_append(&list, qname);
+       }
+
+       /*
+        * always enable column display, we only consult column.*
+        * about layout strategy and stuff
+        */
+       colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
+       memset(&copts, 0, sizeof(copts));
+       copts.indent = "  ";
+       copts.padding = 2;
+       print_columns(&list, colopts, &copts);
+       strbuf_release(&buf);
+       string_list_clear(&list, 0);
+}
+
+static void pretty_print_menus(struct string_list *menu_list)
+{
+       unsigned int local_colopts = 0;
+       struct column_options copts;
+
+       local_colopts = COL_ENABLED | COL_ROW;
+       memset(&copts, 0, sizeof(copts));
+       copts.indent = "  ";
+       copts.padding = 2;
+       print_columns(menu_list, local_colopts, &copts);
+}
+
+static void prompt_help_cmd(int singleton)
+{
+       clean_print_color(CLEAN_COLOR_HELP);
+       printf_ln(singleton ?
+                 _("Prompt help:\n"
+                   "1          - select a numbered item\n"
+                   "foo        - select item based on unique prefix\n"
+                   "           - (empty) select nothing") :
+                 _("Prompt help:\n"
+                   "1          - select a single item\n"
+                   "3-5        - select a range of items\n"
+                   "2-3,6-9    - select multiple ranges\n"
+                   "foo        - select item based on unique prefix\n"
+                   "-...       - unselect specified items\n"
+                   "*          - choose all items\n"
+                   "           - (empty) finish selecting"));
+       clean_print_color(CLEAN_COLOR_RESET);
+}
+
+/*
+ * display menu stuff with number prefix and hotkey highlight
+ */
+static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
+{
+       struct string_list menu_list = STRING_LIST_INIT_DUP;
+       struct strbuf menu = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       struct menu_item *menu_item;
+       struct string_list_item *string_list_item;
+       int i;
+
+       switch (stuff->type) {
+       default:
+               die("Bad type of menu_staff when print menu");
+       case MENU_STUFF_TYPE_MENU_ITEM:
+               menu_item = (struct menu_item *)stuff->stuff;
+               for (i = 0; i < stuff->nr; i++, menu_item++) {
+                       const char *p;
+                       int highlighted = 0;
+
+                       p = menu_item->title;
+                       if ((*chosen)[i] < 0)
+                               (*chosen)[i] = menu_item->selected ? 1 : 0;
+                       strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
+                       for (; *p; p++) {
+                               if (!highlighted && *p == menu_item->hotkey) {
+                                       strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT));
+                                       strbuf_addch(&menu, *p);
+                                       strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET));
+                                       highlighted = 1;
+                               } else {
+                                       strbuf_addch(&menu, *p);
+                               }
+                       }
+                       string_list_append(&menu_list, menu.buf);
+                       strbuf_reset(&menu);
+               }
+               break;
+       case MENU_STUFF_TYPE_STRING_LIST:
+               i = 0;
+               for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) {
+                       if ((*chosen)[i] < 0)
+                               (*chosen)[i] = 0;
+                       strbuf_addf(&menu, "%s%2d: %s",
+                                   (*chosen)[i] ? "*" : " ", i+1, string_list_item->string);
+                       string_list_append(&menu_list, menu.buf);
+                       strbuf_reset(&menu);
+                       i++;
+               }
+               break;
+       }
+
+       pretty_print_menus(&menu_list);
+
+       strbuf_release(&menu);
+       strbuf_release(&buf);
+       string_list_clear(&menu_list, 0);
+}
+
+static int find_unique(const char *choice, struct menu_stuff *menu_stuff)
+{
+       struct menu_item *menu_item;
+       struct string_list_item *string_list_item;
+       int i, len, found = 0;
+
+       len = strlen(choice);
+       switch (menu_stuff->type) {
+       default:
+               die("Bad type of menu_stuff when parse choice");
+       case MENU_STUFF_TYPE_MENU_ITEM:
+
+               menu_item = (struct menu_item *)menu_stuff->stuff;
+               for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
+                       if (len == 1 && *choice == menu_item->hotkey) {
+                               found = i + 1;
+                               break;
+                       }
+                       if (!strncasecmp(choice, menu_item->title, len)) {
+                               if (found) {
+                                       if (len == 1) {
+                                               /* continue for hotkey matching */
+                                               found = -1;
+                                       } else {
+                                               found = 0;
+                                               break;
+                                       }
+                               } else {
+                                       found = i + 1;
+                               }
+                       }
+               }
+               break;
+       case MENU_STUFF_TYPE_STRING_LIST:
+               string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
+               for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
+                       if (!strncasecmp(choice, string_list_item->string, len)) {
+                               if (found) {
+                                       found = 0;
+                                       break;
+                               }
+                               found = i + 1;
+                       }
+               }
+               break;
+       }
+       return found;
+}
+
+
+/*
+ * Parse user input, and return choice(s) for menu (menu_stuff).
+ *
+ * Input
+ *     (for single choice)
+ *         1          - select a numbered item
+ *         foo        - select item based on menu title
+ *                    - (empty) select nothing
+ *
+ *     (for multiple choice)
+ *         1          - select a single item
+ *         3-5        - select a range of items
+ *         2-3,6-9    - select multiple ranges
+ *         foo        - select item based on menu title
+ *         -...       - unselect specified items
+ *         *          - choose all items
+ *                    - (empty) finish selecting
+ *
+ * The parse result will be saved in array **chosen, and
+ * return number of total selections.
+ */
+static int parse_choice(struct menu_stuff *menu_stuff,
+                       int is_single,
+                       struct strbuf input,
+                       int **chosen)
+{
+       struct strbuf **choice_list, **ptr;
+       int nr = 0;
+       int i;
+
+       if (is_single) {
+               choice_list = strbuf_split_max(&input, '\n', 0);
+       } else {
+               char *p = input.buf;
+               do {
+                       if (*p == ',')
+                               *p = ' ';
+               } while (*p++);
+               choice_list = strbuf_split_max(&input, ' ', 0);
+       }
+
+       for (ptr = choice_list; *ptr; ptr++) {
+               char *p;
+               int choose = 1;
+               int bottom = 0, top = 0;
+               int is_range, is_number;
+
+               strbuf_trim(*ptr);
+               if (!(*ptr)->len)
+                       continue;
+
+               /* Input that begins with '-'; unchoose */
+               if (*(*ptr)->buf == '-') {
+                       choose = 0;
+                       strbuf_remove((*ptr), 0, 1);
+               }
+
+               is_range = 0;
+               is_number = 1;
+               for (p = (*ptr)->buf; *p; p++) {
+                       if ('-' == *p) {
+                               if (!is_range) {
+                                       is_range = 1;
+                                       is_number = 0;
+                               } else {
+                                       is_number = 0;
+                                       is_range = 0;
+                                       break;
+                               }
+                       } else if (!isdigit(*p)) {
+                               is_number = 0;
+                               is_range = 0;
+                               break;
+                       }
+               }
+
+               if (is_number) {
+                       bottom = atoi((*ptr)->buf);
+                       top = bottom;
+               } else if (is_range) {
+                       bottom = atoi((*ptr)->buf);
+                       /* a range can be specified like 5-7 or 5- */
+                       if (!*(strchr((*ptr)->buf, '-') + 1))
+                               top = menu_stuff->nr;
+                       else
+                               top = atoi(strchr((*ptr)->buf, '-') + 1);
+               } else if (!strcmp((*ptr)->buf, "*")) {
+                       bottom = 1;
+                       top = menu_stuff->nr;
+               } else {
+                       bottom = find_unique((*ptr)->buf, menu_stuff);
+                       top = bottom;
+               }
+
+               if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
+                   (is_single && bottom != top)) {
+                       clean_print_color(CLEAN_COLOR_ERROR);
+                       printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+                       clean_print_color(CLEAN_COLOR_RESET);
+                       continue;
+               }
+
+               for (i = bottom; i <= top; i++)
+                       (*chosen)[i-1] = choose;
+       }
+
+       strbuf_list_free(choice_list);
+
+       for (i = 0; i < menu_stuff->nr; i++)
+               nr += (*chosen)[i];
+       return nr;
+}
+
+/*
+ * Implement a git-add-interactive compatible UI, which is borrowed
+ * from git-add--interactive.perl.
+ *
+ * Return value:
+ *
+ *   - Return an array of integers
+ *   - , and it is up to you to free the allocated memory.
+ *   - The array ends with EOF.
+ *   - If user pressed CTRL-D (i.e. EOF), no selection returned.
+ */
+static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
+{
+       struct strbuf choice = STRBUF_INIT;
+       int *chosen, *result;
+       int nr = 0;
+       int eof = 0;
+       int i;
+
+       chosen = xmalloc(sizeof(int) * stuff->nr);
+       /* set chosen as uninitialized */
+       for (i = 0; i < stuff->nr; i++)
+               chosen[i] = -1;
+
+       for (;;) {
+               if (opts->header) {
+                       printf_ln("%s%s%s",
+                                 clean_get_color(CLEAN_COLOR_HEADER),
+                                 _(opts->header),
+                                 clean_get_color(CLEAN_COLOR_RESET));
+               }
+
+               /* chosen will be initialized by print_highlight_menu_stuff */
+               print_highlight_menu_stuff(stuff, &chosen);
+
+               if (opts->flags & MENU_OPTS_LIST_ONLY)
+                       break;
+
+               if (opts->prompt) {
+                       printf("%s%s%s%s",
+                              clean_get_color(CLEAN_COLOR_PROMPT),
+                              _(opts->prompt),
+                              opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ",
+                              clean_get_color(CLEAN_COLOR_RESET));
+               }
+
+               if (strbuf_getline(&choice, stdin, '\n') != EOF) {
+                       strbuf_trim(&choice);
+               } else {
+                       eof = 1;
+                       break;
+               }
+
+               /* help for prompt */
+               if (!strcmp(choice.buf, "?")) {
+                       prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON);
+                       continue;
+               }
+
+               /* for a multiple-choice menu, press ENTER (empty) will return back */
+               if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len)
+                       break;
+
+               nr = parse_choice(stuff,
+                                 opts->flags & MENU_OPTS_SINGLETON,
+                                 choice,
+                                 &chosen);
+
+               if (opts->flags & MENU_OPTS_SINGLETON) {
+                       if (nr)
+                               break;
+               } else if (opts->flags & MENU_OPTS_IMMEDIATE) {
+                       break;
+               }
+       }
+
+       if (eof) {
+               result = xmalloc(sizeof(int));
+               *result = EOF;
+       } else {
+               int j = 0;
+
+               /*
+                * recalculate nr, if return back from menu directly with
+                * default selections.
+                */
+               if (!nr) {
+                       for (i = 0; i < stuff->nr; i++)
+                               nr += chosen[i];
+               }
+
+               result = xmalloc(sizeof(int) * (nr + 1));
+               memset(result, 0, sizeof(int) * (nr + 1));
+               for (i = 0; i < stuff->nr && j < nr; i++) {
+                       if (chosen[i])
+                               result[j++] = i;
+               }
+               result[j] = EOF;
+       }
+
+       free(chosen);
+       strbuf_release(&choice);
+       return result;
+}
+
+static int clean_cmd(void)
+{
+       return MENU_RETURN_NO_LOOP;
+}
+
+static int filter_by_patterns_cmd(void)
+{
+       struct dir_struct dir;
+       struct strbuf confirm = STRBUF_INIT;
+       struct strbuf **ignore_list;
+       struct string_list_item *item;
+       struct exclude_list *el;
+       int changed = -1, i;
+
+       for (;;) {
+               if (!del_list.nr)
+                       break;
+
+               if (changed)
+                       pretty_print_dels();
+
+               clean_print_color(CLEAN_COLOR_PROMPT);
+               printf(_("Input ignore patterns>> "));
+               clean_print_color(CLEAN_COLOR_RESET);
+               if (strbuf_getline(&confirm, stdin, '\n') != EOF)
+                       strbuf_trim(&confirm);
+               else
+                       putchar('\n');
+
+               /* quit filter_by_pattern mode if press ENTER or Ctrl-D */
+               if (!confirm.len)
+                       break;
+
+               memset(&dir, 0, sizeof(dir));
+               el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
+               ignore_list = strbuf_split_max(&confirm, ' ', 0);
+
+               for (i = 0; ignore_list[i]; i++) {
+                       strbuf_trim(ignore_list[i]);
+                       if (!ignore_list[i]->len)
+                               continue;
+
+                       add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
+               }
+
+               changed = 0;
+               for_each_string_list_item(item, &del_list) {
+                       int dtype = DT_UNKNOWN;
+
+                       if (is_excluded(&dir, item->string, &dtype)) {
+                               *item->string = '\0';
+                               changed++;
+                       }
+               }
+
+               if (changed) {
+                       string_list_remove_empty_items(&del_list, 0);
+               } else {
+                       clean_print_color(CLEAN_COLOR_ERROR);
+                       printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf);
+                       clean_print_color(CLEAN_COLOR_RESET);
+               }
+
+               strbuf_list_free(ignore_list);
+               clear_directory(&dir);
+       }
+
+       strbuf_release(&confirm);
+       return 0;
+}
+
+static int select_by_numbers_cmd(void)
+{
+       struct menu_opts menu_opts;
+       struct menu_stuff menu_stuff;
+       struct string_list_item *items;
+       int *chosen;
+       int i, j;
+
+       menu_opts.header = NULL;
+       menu_opts.prompt = N_("Select items to delete");
+       menu_opts.flags = 0;
+
+       menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
+       menu_stuff.stuff = &del_list;
+       menu_stuff.nr = del_list.nr;
+
+       chosen = list_and_choose(&menu_opts, &menu_stuff);
+       items = del_list.items;
+       for (i = 0, j = 0; i < del_list.nr; i++) {
+               if (i < chosen[j]) {
+                       *(items[i].string) = '\0';
+               } else if (i == chosen[j]) {
+                       /* delete selected item */
+                       j++;
+                       continue;
+               } else {
+                       /* end of chosen (chosen[j] == EOF), won't delete */
+                       *(items[i].string) = '\0';
+               }
+       }
+
+       string_list_remove_empty_items(&del_list, 0);
+
+       free(chosen);
+       return 0;
+}
+
+static int ask_each_cmd(void)
+{
+       struct strbuf confirm = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT;
+       struct string_list_item *item;
+       const char *qname;
+       int changed = 0, eof = 0;
+
+       for_each_string_list_item(item, &del_list) {
+               /* Ctrl-D should stop removing files */
+               if (!eof) {
+                       qname = quote_path_relative(item->string, NULL, &buf);
+                       printf(_("remove %s? "), qname);
+                       if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
+                               strbuf_trim(&confirm);
+                       } else {
+                               putchar('\n');
+                               eof = 1;
+                       }
+               }
+               if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
+                       *item->string = '\0';
+                       changed++;
+               }
+       }
+
+       if (changed)
+               string_list_remove_empty_items(&del_list, 0);
+
+       strbuf_release(&buf);
+       strbuf_release(&confirm);
+       return MENU_RETURN_NO_LOOP;
+}
+
+static int quit_cmd(void)
+{
+       string_list_clear(&del_list, 0);
+       printf_ln(_("Bye."));
+       return MENU_RETURN_NO_LOOP;
+}
+
+static int help_cmd(void)
+{
+       clean_print_color(CLEAN_COLOR_HELP);
+       printf_ln(_(
+                   "clean               - start cleaning\n"
+                   "filter by pattern   - exclude items from deletion\n"
+                   "select by numbers   - select items to be deleted by numbers\n"
+                   "ask each            - confirm each deletion (like \"rm -i\")\n"
+                   "quit                - stop cleaning\n"
+                   "help                - this screen\n"
+                   "?                   - help for prompt selection"
+                  ));
+       clean_print_color(CLEAN_COLOR_RESET);
+       return 0;
+}
+
+static void interactive_main_loop(void)
+{
+       while (del_list.nr) {
+               struct menu_opts menu_opts;
+               struct menu_stuff menu_stuff;
+               struct menu_item menus[] = {
+                       {'c', "clean",                  0, clean_cmd},
+                       {'f', "filter by pattern",      0, filter_by_patterns_cmd},
+                       {'s', "select by numbers",      0, select_by_numbers_cmd},
+                       {'a', "ask each",               0, ask_each_cmd},
+                       {'q', "quit",                   0, quit_cmd},
+                       {'h', "help",                   0, help_cmd},
+               };
+               int *chosen;
+
+               menu_opts.header = N_("*** Commands ***");
+               menu_opts.prompt = N_("What now");
+               menu_opts.flags = MENU_OPTS_SINGLETON;
+
+               menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM;
+               menu_stuff.stuff = menus;
+               menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item);
+
+               clean_print_color(CLEAN_COLOR_HEADER);
+               printf_ln(Q_("Would remove the following item:",
+                            "Would remove the following items:",
+                            del_list.nr));
+               clean_print_color(CLEAN_COLOR_RESET);
+
+               pretty_print_dels();
+
+               chosen = list_and_choose(&menu_opts, &menu_stuff);
+
+               if (*chosen != EOF) {
+                       int ret;
+                       ret = menus[*chosen].fn();
+                       if (ret != MENU_RETURN_NO_LOOP) {
+                               free(chosen);
+                               chosen = NULL;
+                               if (!del_list.nr) {
+                                       clean_print_color(CLEAN_COLOR_ERROR);
+                                       printf_ln(_("No more files to clean, exiting."));
+                                       clean_print_color(CLEAN_COLOR_RESET);
+                                       break;
+                               }
+                               continue;
+                       }
+               } else {
+                       quit_cmd();
+               }
+
+               free(chosen);
+               chosen = NULL;
+               break;
+       }
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
        int i, res;
        int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
        int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
        int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
-       struct strbuf directory = STRBUF_INIT;
+       struct strbuf abs_path = STRBUF_INIT;
        struct dir_struct dir;
        static const char **pathspec;
        struct strbuf buf = STRBUF_INIT;
        struct string_list exclude_list = STRING_LIST_INIT_NODUP;
        struct exclude_list *el;
+       struct string_list_item *item;
        const char *qname;
        char *seen = NULL;
        struct option options[] = {
                OPT__QUIET(&quiet, N_("do not print names of files removed")),
                OPT__DRY_RUN(&dry_run, N_("dry run")),
                OPT__FORCE(&force, N_("force")),
+               OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
                OPT_BOOLEAN('d', NULL, &remove_directories,
                                N_("remove whole directories")),
                { OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
@@ -186,12 +901,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        if (ignored && ignored_only)
                die(_("-x and -X cannot be used together"));
 
-       if (!dry_run && !force) {
+       if (!interactive && !dry_run && !force) {
                if (config_set)
-                       die(_("clean.requireForce set to true and neither -n nor -f given; "
+                       die(_("clean.requireForce set to true and neither -i, -n nor -f given; "
                                  "refusing to clean"));
                else
-                       die(_("clean.requireForce defaults to true and neither -n nor -f given; "
+                       die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; "
                                  "refusing to clean"));
        }
 
@@ -221,8 +936,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                struct dir_entry *ent = dir.entries[i];
                int len, pos;
                int matches = 0;
-               struct cache_entry *ce;
+               const struct cache_entry *ce;
                struct stat st;
+               const char *rel;
 
                /*
                 * Remove the '/' at the end that directory
@@ -242,13 +958,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                                continue; /* Yup, this one exists unmerged */
                }
 
-               /*
-                * we might have removed this as part of earlier
-                * recursive directory removal, so lstat() here could
-                * fail with ENOENT.
-                */
                if (lstat(ent->name, &st))
-                       continue;
+                       die_errno("Cannot lstat '%s'", ent->name);
 
                if (pathspec) {
                        memset(seen, 0, argc > 0 ? argc : 1);
@@ -257,33 +968,62 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                }
 
                if (S_ISDIR(st.st_mode)) {
-                       strbuf_addstr(&directory, ent->name);
                        if (remove_directories || (matches == MATCHED_EXACTLY)) {
-                               if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
-                                       errors++;
-                               if (gone && !quiet) {
-                                       qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
-                                       printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
-                               }
+                               rel = relative_path(ent->name, prefix, &buf);
+                               string_list_append(&del_list, rel);
                        }
-                       strbuf_reset(&directory);
                } else {
                        if (pathspec && !matches)
                                continue;
-                       res = dry_run ? 0 : unlink(ent->name);
+                       rel = relative_path(ent->name, prefix, &buf);
+                       string_list_append(&del_list, rel);
+               }
+       }
+
+       if (interactive && del_list.nr > 0)
+               interactive_main_loop();
+
+       for_each_string_list_item(item, &del_list) {
+               struct stat st;
+
+               if (prefix)
+                       strbuf_addstr(&abs_path, prefix);
+
+               strbuf_addstr(&abs_path, item->string);
+
+               /*
+                * we might have removed this as part of earlier
+                * recursive directory removal, so lstat() here could
+                * fail with ENOENT.
+                */
+               if (lstat(abs_path.buf, &st))
+                       continue;
+
+               if (S_ISDIR(st.st_mode)) {
+                       if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
+                               errors++;
+                       if (gone && !quiet) {
+                               qname = quote_path_relative(item->string, NULL, &buf);
+                               printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
+                       }
+               } else {
+                       res = dry_run ? 0 : unlink(abs_path.buf);
                        if (res) {
-                               qname = quote_path_relative(ent->name, -1, &buf, prefix);
+                               qname = quote_path_relative(item->string, NULL, &buf);
                                warning(_(msg_warn_remove_failed), qname);
                                errors++;
                        } else if (!quiet) {
-                               qname = quote_path_relative(ent->name, -1, &buf, prefix);
+                               qname = quote_path_relative(item->string, NULL, &buf);
                                printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
                        }
                }
+               strbuf_reset(&abs_path);
        }
        free(seen);
 
-       strbuf_release(&directory);
+       strbuf_release(&abs_path);
+       strbuf_release(&buf);
+       string_list_clear(&del_list, 0);
        string_list_clear(&exclude_list, 0);
        return (errors != 0);
 }
index f9c380eb6c536b657ddc65b88f6839ec761f7f25..21ad9f945c019625ace5f5dba0d4e54a8b2bb70f 100644 (file)
@@ -18,7 +18,6 @@
 #include "transport.h"
 #include "strbuf.h"
 #include "dir.h"
-#include "pack-refs.h"
 #include "sigchain.h"
 #include "branch.h"
 #include "remote.h"
@@ -232,16 +231,26 @@ static void strip_trailing_slashes(char *dir)
 static int add_one_reference(struct string_list_item *item, void *cb_data)
 {
        char *ref_git;
+       const char *repo;
        struct strbuf alternate = STRBUF_INIT;
 
-       /* Beware: real_path() and mkpath() return static buffer */
+       /* Beware: read_gitfile(), real_path() and mkpath() return static buffer */
        ref_git = xstrdup(real_path(item->string));
-       if (is_directory(mkpath("%s/.git/objects", ref_git))) {
+
+       repo = read_gitfile(ref_git);
+       if (!repo)
+               repo = read_gitfile(mkpath("%s/.git", ref_git));
+       if (repo) {
+               free(ref_git);
+               ref_git = xstrdup(repo);
+       }
+
+       if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) {
                char *ref_git_git = mkpathdup("%s/.git", ref_git);
                free(ref_git);
                ref_git = ref_git_git;
        } else if (!is_directory(mkpath("%s/objects", ref_git)))
-               die(_("reference repository '%s' is not a local directory."),
+               die(_("reference repository '%s' is not a local repository."),
                    item->string);
 
        strbuf_addf(&alternate, "%s/objects", ref_git);
@@ -377,7 +386,7 @@ static void clone_local(const char *src_repo, const char *dest_repo)
 static const char *junk_work_tree;
 static const char *junk_git_dir;
 static pid_t junk_pid;
-enum {
+static enum {
        JUNK_LEAVE_NONE,
        JUNK_LEAVE_REPO,
        JUNK_LEAVE_ALL
@@ -484,13 +493,16 @@ static void write_remote_refs(const struct ref *local_refs)
 {
        const struct ref *r;
 
+       lock_packed_refs(LOCK_DIE_ON_ERROR);
+
        for (r = local_refs; r; r = r->next) {
                if (!r->peer_ref)
                        continue;
                add_packed_ref(r->peer_ref->name, r->old_sha1);
        }
 
-       pack_refs(PACK_REFS_ALL);
+       if (commit_packed_refs())
+               die_errno("unable to overwrite old ref-pack file");
 }
 
 static void write_followtags(const struct ref *refs, const char *msg)
@@ -532,12 +544,21 @@ static void update_remote_refs(const struct ref *refs,
                               const struct ref *mapped_refs,
                               const struct ref *remote_head_points_at,
                               const char *branch_top,
-                              const char *msg)
+                              const char *msg,
+                              struct transport *transport,
+                              int check_connectivity)
 {
        const struct ref *rm = mapped_refs;
 
-       if (check_everything_connected(iterate_ref_map, 0, &rm))
-               die(_("remote did not send all necessary objects"));
+       if (check_connectivity) {
+               if (0 <= option_verbosity)
+                       printf(_("Checking connectivity... "));
+               if (check_everything_connected_with_transport(iterate_ref_map,
+                                                             0, &rm, transport))
+                       die(_("remote did not send all necessary objects"));
+               if (0 <= option_verbosity)
+                       printf(_("done\n"));
+       }
 
        if (refs) {
                write_remote_refs(mapped_refs);
@@ -683,7 +704,7 @@ static void write_refspec_config(const char* src_ref_prefix,
                        /*
                         * otherwise, the next "git fetch" will
                         * simply fetch from HEAD without updating
-                        * any remote tracking branch, which is what
+                        * any remote-tracking branch, which is what
                         * we want.
                         */
                } else {
@@ -773,6 +794,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        is_local = option_local != 0 && path && !is_bundle;
        if (is_local && option_depth)
                warning(_("--depth is ignored in local clones; use file:// instead."));
+       if (option_local > 0 && !is_local)
+               warning(_("--local is ignored"));
 
        if (argc == 2)
                dir = xstrdup(argv[1]);
@@ -879,6 +902,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (option_upload_pack)
                        transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                             option_upload_pack);
+
+               if (transport->smart_options && !option_depth)
+                       transport->smart_options->check_self_contained_and_connected = 1;
        }
 
        refs = transport_get_remote_refs(transport);
@@ -920,6 +946,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        our_head_points_at = remote_head_points_at;
        }
        else {
+               if (option_branch)
+                       die(_("Remote branch %s not found in upstream %s"),
+                                       option_branch, option_origin);
+
                warning(_("You appear to have cloned an empty repository."));
                mapped_refs = NULL;
                our_head_points_at = NULL;
@@ -940,7 +970,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport_fetch_refs(transport, mapped_refs);
 
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
-                          branch_top.buf, reflog_msg.buf);
+                          branch_top.buf, reflog_msg.buf, transport, !is_local);
 
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
index 46204375e786ea583ca1a7eabc3060611adc3d04..a48a7fe59c9abf32ce0ecc5e4ca5126761fc5a75 100644 (file)
@@ -29,6 +29,8 @@
 #include "gpg-interface.h"
 #include "column.h"
 #include "sequencer.h"
+#include "notes-utils.h"
+#include "mailmap.h"
 
 static const char * const builtin_commit_usage[] = {
        N_("git commit [options] [--] <pathspec>..."),
@@ -62,8 +64,18 @@ N_("The previous cherry-pick is now empty, possibly due to conflict resolution.\
 "If you wish to commit it anyway, use:\n"
 "\n"
 "    git commit --allow-empty\n"
+"\n");
+
+static const char empty_cherry_pick_advice_single[] =
+N_("Otherwise, please use 'git reset'\n");
+
+static const char empty_cherry_pick_advice_multi[] =
+N_("If you wish to skip this commit, use:\n"
+"\n"
+"    git reset\n"
 "\n"
-"Otherwise, please use 'git reset'\n");
+"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
+"the remaining commits.\n");
 
 static const char *use_message_buffer;
 static const char commit_editmsg[] = "COMMIT_EDITMSG";
@@ -106,24 +118,29 @@ static enum {
 static const char *cleanup_arg;
 
 static enum commit_whence whence;
+static int sequencer_in_use;
 static int use_editor = 1, include_status = 1;
-static int show_ignored_in_status;
+static int show_ignored_in_status, have_option_m;
 static const char *only_include_assumed;
 static struct strbuf message = STRBUF_INIT;
 
-static enum {
+static enum status_format {
        STATUS_FORMAT_NONE = 0,
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
-       STATUS_FORMAT_PORCELAIN
-} status_format;
+       STATUS_FORMAT_PORCELAIN,
+
+       STATUS_FORMAT_UNSPECIFIED
+} status_format = STATUS_FORMAT_UNSPECIFIED;
 
 static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 {
        struct strbuf *buf = opt->value;
-       if (unset)
+       if (unset) {
+               have_option_m = 0;
                strbuf_setlen(buf, 0);
-       else {
+       } else {
+               have_option_m = 1;
                if (buf->len)
                        strbuf_addch(buf, '\n');
                strbuf_addstr(buf, arg);
@@ -136,8 +153,11 @@ static void determine_whence(struct wt_status *s)
 {
        if (file_exists(git_path("MERGE_HEAD")))
                whence = FROM_MERGE;
-       else if (file_exists(git_path("CHERRY_PICK_HEAD")))
+       else if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
                whence = FROM_CHERRY_PICK;
+               if (file_exists(git_path("sequencer")))
+                       sequencer_in_use = 1;
+       }
        else
                whence = FROM_COMMIT;
        if (s)
@@ -202,7 +222,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
        }
 
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                struct string_list_item *item;
 
                if (ce->ce_flags & CE_UPDATE)
@@ -457,6 +477,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(s);
                break;
+       case STATUS_FORMAT_UNSPECIFIED:
+               die("BUG: finalize_deferred_config() should have been called");
+               break;
        case STATUS_FORMAT_NONE:
        case STATUS_FORMAT_LONG:
                wt_status_print(s);
@@ -526,7 +549,6 @@ static void determine_author_info(struct strbuf *author_ident)
                                        (lb - strlen(" ") -
                                         (a + strlen("\nauthor "))));
                email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
-               date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
                len = eol - (rb + strlen("> "));
                date = xmalloc(len + 2);
                *date = '@';
@@ -803,8 +825,13 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                run_status(stdout, index_file, prefix, 0, s);
                if (amend)
                        fputs(_(empty_amend_advice), stderr);
-               else if (whence == FROM_CHERRY_PICK)
+               else if (whence == FROM_CHERRY_PICK) {
                        fputs(_(empty_cherry_pick_advice), stderr);
+                       if (!sequencer_in_use)
+                               fputs(_(empty_cherry_pick_advice_single), stderr);
+                       else
+                               fputs(_(empty_cherry_pick_advice_multi), stderr);
+               }
                return 0;
        }
 
@@ -909,6 +936,7 @@ static const char *find_author_by_nickname(const char *name)
        struct rev_info revs;
        struct commit *commit;
        struct strbuf buf = STRBUF_INIT;
+       struct string_list mailmap = STRING_LIST_INIT_NODUP;
        const char *av[20];
        int ac = 0;
 
@@ -919,13 +947,17 @@ static const char *find_author_by_nickname(const char *name)
        av[++ac] = buf.buf;
        av[++ac] = NULL;
        setup_revisions(ac, av, &revs, NULL);
+       revs.mailmap = &mailmap;
+       read_mailmap(revs.mailmap, NULL);
+
        prepare_revision_walk(&revs);
        commit = get_revision(&revs);
        if (commit) {
                struct pretty_print_context ctx = {0};
                ctx.date_mode = DATE_NORMAL;
                strbuf_release(&buf);
-               format_commit_message(commit, "%an <%ae>", &buf, &ctx);
+               format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
+               clear_mailmap(&mailmap);
                return strbuf_detach(&buf, NULL);
        }
        die(_("No existing author found with '%s'"), name);
@@ -955,7 +987,43 @@ static const char *read_commit_message(const char *name)
        if (!commit)
                die(_("could not lookup commit %s"), name);
        out_enc = get_commit_output_encoding();
-       return logmsg_reencode(commit, out_enc);
+       return logmsg_reencode(commit, NULL, out_enc);
+}
+
+/*
+ * Enumerate what needs to be propagated when --porcelain
+ * is not in effect here.
+ */
+static struct status_deferred_config {
+       enum status_format status_format;
+       int show_branch;
+} status_deferred_config = {
+       STATUS_FORMAT_UNSPECIFIED,
+       -1 /* unspecified */
+};
+
+static void finalize_deferred_config(struct wt_status *s)
+{
+       int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
+                                  !s->null_termination);
+
+       if (s->null_termination) {
+               if (status_format == STATUS_FORMAT_NONE ||
+                   status_format == STATUS_FORMAT_UNSPECIFIED)
+                       status_format = STATUS_FORMAT_PORCELAIN;
+               else if (status_format == STATUS_FORMAT_LONG)
+                       die(_("--long and -z are incompatible"));
+       }
+
+       if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
+               status_format = status_deferred_config.status_format;
+       if (status_format == STATUS_FORMAT_UNSPECIFIED)
+               status_format = STATUS_FORMAT_NONE;
+
+       if (use_deferred_config && s->show_branch < 0)
+               s->show_branch = status_deferred_config.show_branch;
+       if (s->show_branch < 0)
+               s->show_branch = 0;
 }
 
 static int parse_and_validate_options(int argc, const char *argv[],
@@ -968,6 +1036,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
        int f = 0;
 
        argc = parse_options(argc, argv, prefix, options, usage, 0);
+       finalize_deferred_config(s);
 
        if (force_author && !strchr(force_author, '>'))
                force_author = find_author_by_nickname(force_author);
@@ -975,7 +1044,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (force_author && renew_authorship)
                die(_("Using both --reset-author and --author does not make sense"));
 
-       if (logfile || message.len || use_message || fixup_message)
+       if (logfile || have_option_m || use_message || fixup_message)
                use_editor = 0;
        if (0 <= edit_flag)
                use_editor = edit_flag;
@@ -1052,12 +1121,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (all && argc > 0)
                die(_("Paths with -a does not make sense."));
 
-       if (s->null_termination) {
-               if (status_format == STATUS_FORMAT_NONE)
-                       status_format = STATUS_FORMAT_PORCELAIN;
-               else if (status_format == STATUS_FORMAT_LONG)
-                       die(_("--long and -z are incompatible"));
-       }
        if (status_format != STATUS_FORMAT_NONE)
                dry_run = 1;
 
@@ -1110,6 +1173,17 @@ static int git_status_config(const char *k, const char *v, void *cb)
                        s->submodule_summary = -1;
                return 0;
        }
+       if (!strcmp(k, "status.short")) {
+               if (git_config_bool(k, v))
+                       status_deferred_config.status_format = STATUS_FORMAT_SHORT;
+               else
+                       status_deferred_config.status_format = STATUS_FORMAT_NONE;
+               return 0;
+       }
+       if (!strcmp(k, "status.branch")) {
+               status_deferred_config.show_branch = git_config_bool(k, v);
+               return 0;
+       }
        if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
                s->use_color = git_config_colorbool(k, v);
                return 0;
@@ -1152,8 +1226,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_SET_INT('s', "short", &status_format,
                            N_("show status concisely"), STATUS_FORMAT_SHORT),
-               OPT_BOOLEAN('b', "branch", &s.show_branch,
-                           N_("show branch information")),
+               OPT_BOOL('b', "branch", &s.show_branch,
+                        N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
                            N_("machine-readable output"),
                            STATUS_FORMAT_PORCELAIN),
@@ -1186,13 +1260,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                             builtin_status_options,
                             builtin_status_usage, 0);
        finalize_colopts(&s.colopts, -1);
-
-       if (s.null_termination) {
-               if (status_format == STATUS_FORMAT_NONE)
-                       status_format = STATUS_FORMAT_PORCELAIN;
-               else if (status_format == STATUS_FORMAT_LONG)
-                       die(_("--long and -z are incompatible"));
-       }
+       finalize_deferred_config(&s);
 
        handle_untracked_files_arg(&s);
        if (show_ignored_in_status)
@@ -1221,6 +1289,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        case STATUS_FORMAT_PORCELAIN:
                wt_porcelain_print(&s);
                break;
+       case STATUS_FORMAT_UNSPECIFIED:
+               die("BUG: finalize_deferred_config() should have been called");
+               break;
        case STATUS_FORMAT_NONE:
        case STATUS_FORMAT_LONG:
                s.verbose = verbose;
@@ -1389,7 +1460,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")),
                OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
                            STATUS_FORMAT_SHORT),
-               OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
+               OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
                            N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
                OPT_SET_INT(0, "long", &status_format,
@@ -1430,6 +1501,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        wt_status_prepare(&s);
        gitmodules_config();
        git_config(git_commit_config, &s);
+       status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
        determine_whence(&s);
        s.colopts = 0;
 
@@ -1591,7 +1663,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                if (cfg) {
                        /* we are amending, so current_head is not NULL */
                        copy_note_for_rewrite(cfg, current_head->object.sha1, sha1);
-                       finish_copy_notes_for_rewrite(cfg);
+                       finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
                }
                run_rewrite_hook(current_head->object.sha1, sha1);
        }
index 33c9bf9d84f514330f12de5cbd9dfb610a1b16de..4010c4320a585d84bdb0914d8a9bbc27d38b7f18 100644 (file)
@@ -21,6 +21,7 @@ static char term = '\n';
 
 static int use_global_config, use_system_config, use_local_config;
 static const char *given_config_file;
+static const char *given_config_blob;
 static int actions, types;
 static const char *get_color_slot, *get_colorbool_slot;
 static int end_null;
@@ -53,6 +54,7 @@ static struct option builtin_config_options[] = {
        OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
        OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
        OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
+       OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
        OPT_GROUP(N_("Action")),
        OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
        OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
@@ -218,7 +220,8 @@ static int get_value(const char *key_, const char *regex_)
        }
 
        git_config_with_options(collect_config, &values,
-                               given_config_file, respect_includes);
+                               given_config_file, given_config_blob,
+                               respect_includes);
 
        ret = !values.nr;
 
@@ -302,7 +305,8 @@ static void get_color(const char *def_color)
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config_with_options(git_get_color_config, NULL,
-                               given_config_file, respect_includes);
+                               given_config_file, given_config_blob,
+                               respect_includes);
 
        if (!get_color_found && def_color)
                color_parse(def_color, "command line", parsed_color);
@@ -329,8 +333,10 @@ static int get_colorbool(int print)
 {
        get_colorbool_found = -1;
        get_diff_color_found = -1;
+       get_color_ui_found = -1;
        git_config_with_options(git_get_colorbool_config, NULL,
-                               given_config_file, respect_includes);
+                               given_config_file, given_config_blob,
+                               respect_includes);
 
        if (get_colorbool_found < 0) {
                if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -339,6 +345,10 @@ static int get_colorbool(int print)
                        get_colorbool_found = get_color_ui_found;
        }
 
+       if (get_colorbool_found < 0)
+               /* default value if none found in config */
+               get_colorbool_found = GIT_COLOR_AUTO;
+
        get_colorbool_found = want_color(get_colorbool_found);
 
        if (print) {
@@ -348,6 +358,12 @@ static int get_colorbool(int print)
                return get_colorbool_found ? 0 : 1;
 }
 
+static void check_blob_write(void)
+{
+       if (given_config_blob)
+               die("writing config blobs is not supported");
+}
+
 int cmd_config(int argc, const char **argv, const char *prefix)
 {
        int nongit = !startup_info->have_repository;
@@ -359,7 +375,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                             builtin_config_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
 
-       if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
+       if (use_global_config + use_system_config + use_local_config +
+           !!given_config_file + !!given_config_blob > 1) {
                error("only one config file at a time.");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
@@ -379,8 +396,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                         */
                        die("$HOME not set");
 
-               if (access_or_warn(user_config, R_OK) &&
-                   xdg_config && !access_or_warn(xdg_config, R_OK))
+               if (access_or_warn(user_config, R_OK, 0) &&
+                   xdg_config && !access_or_warn(xdg_config, R_OK, 0))
                        given_config_file = xdg_config;
                else
                        given_config_file = user_config;
@@ -438,6 +455,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_argc(argc, 0, 0);
                if (git_config_with_options(show_all_config, NULL,
                                            given_config_file,
+                                           given_config_blob,
                                            respect_includes) < 0) {
                        if (given_config_file)
                                die_errno("unable to read config file '%s'",
@@ -450,6 +468,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_argc(argc, 0, 0);
                if (!given_config_file && nongit)
                        die("not in a git directory");
+               if (given_config_blob)
+                       die("editing blobs is not supported");
                git_config(git_default_config, NULL);
                launch_editor(given_config_file ?
                              given_config_file : git_path("config"),
@@ -457,6 +477,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        }
        else if (actions == ACTION_SET) {
                int ret;
+               check_blob_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
                ret = git_config_set_in_file(given_config_file, argv[0], value);
@@ -466,18 +487,21 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                return ret;
        }
        else if (actions == ACTION_SET_ALL) {
+               check_blob_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
                return git_config_set_multivar_in_file(given_config_file,
                                                       argv[0], value, argv[2], 0);
        }
        else if (actions == ACTION_ADD) {
+               check_blob_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
                return git_config_set_multivar_in_file(given_config_file,
                                                       argv[0], value, "^$", 0);
        }
        else if (actions == ACTION_REPLACE_ALL) {
+               check_blob_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
                return git_config_set_multivar_in_file(given_config_file,
@@ -500,6 +524,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                return get_value(argv[0], argv[1]);
        }
        else if (actions == ACTION_UNSET) {
+               check_blob_write();
                check_argc(argc, 1, 2);
                if (argc == 2)
                        return git_config_set_multivar_in_file(given_config_file,
@@ -509,12 +534,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                                                      argv[0], NULL);
        }
        else if (actions == ACTION_UNSET_ALL) {
+               check_blob_write();
                check_argc(argc, 1, 2);
                return git_config_set_multivar_in_file(given_config_file,
                                                       argv[0], NULL, argv[1], 1);
        }
        else if (actions == ACTION_RENAME_SECTION) {
                int ret;
+               check_blob_write();
                check_argc(argc, 2, 2);
                ret = git_config_rename_section_in_file(given_config_file,
                                                        argv[0], argv[1]);
@@ -525,6 +552,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        }
        else if (actions == ACTION_REMOVE_SECTION) {
                int ret;
+               check_blob_write();
                check_argc(argc, 1, 1);
                ret = git_config_rename_section_in_file(given_config_file,
                                                        argv[0], NULL);
index 6636a68cd919ba227a10cfa39efcd5f4e77a801d..7d73722f59b10b5fe302498bb83c9ab3313b89b0 100644 (file)
@@ -7,6 +7,7 @@
 #include "parse-options.h"
 #include "diff.h"
 #include "hash.h"
+#include "argv-array.h"
 
 #define SEEN           (1u<<0)
 #define MAX_TAGS       (FLAG_BITS - 1)
@@ -21,6 +22,7 @@ static int debug;     /* Display lots of verbose info */
 static int all;        /* Any valid ref can be used */
 static int tags;       /* Allow lightweight tags */
 static int longformat;
+static int first_parent;
 static int abbrev = -1; /* unspecified */
 static int max_candidates = 10;
 static struct hash_table names;
@@ -42,7 +44,7 @@ struct commit_name {
        unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
        unsigned name_checked:1;
        unsigned char sha1[20];
-       const char *path;
+       char *path;
 };
 static const char *prio_names[] = {
        "head", "lightweight", "annotated",
@@ -126,12 +128,14 @@ static void add_to_known_names(const char *path,
                        } else {
                                e->next = NULL;
                        }
+                       e->path = NULL;
                }
                e->tag = tag;
                e->prio = prio;
                e->name_checked = 0;
                hashcpy(e->sha1, sha1);
-               e->path = path;
+               free(e->path);
+               e->path = xstrdup(path);
        }
 }
 
@@ -336,6 +340,9 @@ static void describe(const char *arg, int last_one)
                                commit_list_insert_by_date(p, &list);
                        p->object.flags |= c->object.flags;
                        parents = parents->next;
+
+                       if (first_parent)
+                               break;
                }
        }
 
@@ -404,6 +411,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "all",        &all, N_("use any ref")),
                OPT_BOOLEAN(0, "tags",       &tags, N_("use any tag, even unannotated")),
                OPT_BOOLEAN(0, "long",       &longformat, N_("always use long format")),
+               OPT_BOOLEAN(0, "first-parent", &first_parent, N_("only follow first parent")),
                OPT__ABBREV(&abbrev),
                OPT_SET_INT(0, "exact-match", &max_candidates,
                            N_("only output exact matches"), 0),
@@ -435,24 +443,24 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                die(_("--long is incompatible with --abbrev=0"));
 
        if (contains) {
-               const char **args = xmalloc((7 + argc) * sizeof(char *));
-               int i = 0;
-               args[i++] = "name-rev";
-               args[i++] = "--name-only";
-               args[i++] = "--no-undefined";
+               struct argv_array args;
+
+               argv_array_init(&args);
+               argv_array_pushl(&args, "name-rev",
+                                "--peel-tag", "--name-only", "--no-undefined",
+                                NULL);
                if (always)
-                       args[i++] = "--always";
+                       argv_array_push(&args, "--always");
                if (!all) {
-                       args[i++] = "--tags";
-                       if (pattern) {
-                               char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
-                               sprintf(s, "--refs=refs/tags/%s", pattern);
-                               args[i++] = s;
-                       }
+                       argv_array_push(&args, "--tags");
+                       if (pattern)
+                               argv_array_pushf(&args, "--refs=refs/tags/%s", pattern);
+               }
+               while (*argv) {
+                       argv_array_push(&args, *argv);
+                       argv++;
                }
-               memcpy(args + i, argv, argc * sizeof(char *));
-               args[i + argc] = NULL;
-               return cmd_name_rev(i + argc, args, prefix);
+               return cmd_name_rev(args.argc, args.argv, prefix);
        }
 
        init_hash(&names);
index 8c2af6cb43ca62f253e07903a8e3cce1080e335b..9fc273d8cd78d53a55047e17d44eb89291741167 100644 (file)
@@ -153,7 +153,8 @@ static int builtin_diff_index(struct rev_info *revs,
 
 static int builtin_diff_tree(struct rev_info *revs,
                             int argc, const char **argv,
-                            struct object_array_entry *ent)
+                            struct object_array_entry *ent0,
+                            struct object_array_entry *ent1)
 {
        const unsigned char *(sha1[2]);
        int swap = 0;
@@ -161,13 +162,14 @@ static int builtin_diff_tree(struct rev_info *revs,
        if (argc > 1)
                usage(builtin_diff_usage);
 
-       /* We saw two trees, ent[0] and ent[1].
-        * if ent[1] is uninteresting, they are swapped
+       /*
+        * We saw two trees, ent0 and ent1.  If ent1 is uninteresting,
+        * swap them.
         */
-       if (ent[1].item->flags & UNINTERESTING)
+       if (ent1->item->flags & UNINTERESTING)
                swap = 1;
-       sha1[swap] = ent[0].item->sha1;
-       sha1[1-swap] = ent[1].item->sha1;
+       sha1[swap] = ent0->item->sha1;
+       sha1[1-swap] = ent1->item->sha1;
        diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
        log_tree_diff_flush(revs);
        return 0;
@@ -251,8 +253,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 {
        int i;
        struct rev_info rev;
-       struct object_array_entry ent[100];
-       int ents = 0, blobs = 0, paths = 0;
+       struct object_array ent = OBJECT_ARRAY_INIT;
+       int blobs = 0, paths = 0;
        const char *path = NULL;
        struct blobinfo blob[2];
        int nongit;
@@ -337,9 +339,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        }
 
        for (i = 0; i < rev.pending.nr; i++) {
-               struct object_array_entry *list = rev.pending.objects+i;
-               struct object *obj = list->item;
-               const char *name = list->name;
+               struct object_array_entry *entry = &rev.pending.objects[i];
+               struct object *obj = entry->item;
+               const char *name = entry->name;
                int flags = (obj->flags & UNINTERESTING);
                if (!obj->parsed)
                        obj = parse_object(obj->sha1);
@@ -348,27 +350,21 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                        die(_("invalid object '%s' given."), name);
                if (obj->type == OBJ_COMMIT)
                        obj = &((struct commit *)obj)->tree->object;
+
                if (obj->type == OBJ_TREE) {
-                       if (ARRAY_SIZE(ent) <= ents)
-                               die(_("more than %d trees given: '%s'"),
-                                   (int) ARRAY_SIZE(ent), name);
                        obj->flags |= flags;
-                       ent[ents].item = obj;
-                       ent[ents].name = name;
-                       ents++;
-                       continue;
-               }
-               if (obj->type == OBJ_BLOB) {
+                       add_object_array(obj, name, &ent);
+               } else if (obj->type == OBJ_BLOB) {
                        if (2 <= blobs)
                                die(_("more than two blobs given: '%s'"), name);
                        hashcpy(blob[blobs].sha1, obj->sha1);
                        blob[blobs].name = name;
-                       blob[blobs].mode = list->mode;
+                       blob[blobs].mode = entry->mode;
                        blobs++;
-                       continue;
 
+               } else {
+                       die(_("unhandled object '%s' given."), name);
                }
-               die(_("unhandled object '%s' given."), name);
        }
        if (rev.prune_data.nr) {
                if (!path)
@@ -379,7 +375,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        /*
         * Now, do the arguments look reasonable?
         */
-       if (!ents) {
+       if (!ent.nr) {
                switch (blobs) {
                case 0:
                        result = builtin_diff_files(&rev, argc, argv);
@@ -400,23 +396,26 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        }
        else if (blobs)
                usage(builtin_diff_usage);
-       else if (ents == 1)
+       else if (ent.nr == 1)
                result = builtin_diff_index(&rev, argc, argv);
-       else if (ents == 2)
-               result = builtin_diff_tree(&rev, argc, argv, ent);
-       else if (ent[0].item->flags & UNINTERESTING) {
+       else if (ent.nr == 2)
+               result = builtin_diff_tree(&rev, argc, argv,
+                                          &ent.objects[0], &ent.objects[1]);
+       else if (ent.objects[0].item->flags & UNINTERESTING) {
                /*
                 * diff A...B where there is at least one merge base
-                * between A and B.  We have ent[0] == merge-base,
-                * ent[ents-2] == A, and ent[ents-1] == B.  Show diff
-                * between the base and B.  Note that we pick one
-                * merge base at random if there are more than one.
+                * between A and B.  We have ent.objects[0] ==
+                * merge-base, ent.objects[ents-2] == A, and
+                * ent.objects[ents-1] == B.  Show diff between the
+                * base and B.  Note that we pick one merge base at
+                * random if there are more than one.
                 */
-               ent[1] = ent[ents-1];
-               result = builtin_diff_tree(&rev, argc, argv, ent);
+               result = builtin_diff_tree(&rev, argc, argv,
+                                          &ent.objects[0],
+                                          &ent.objects[ent.nr-1]);
        } else
                result = builtin_diff_combined(&rev, argc, argv,
-                                              ent, ents);
+                                              ent.objects, ent.nr);
        result = diff_result_code(&rev.diffopt, result);
        if (1 < rev.diffopt.skip_stat_unmatch)
                refresh_index_quietly();
index 725c0a7dca70ae8cbb31f571e0284f5c31a61cff..8e19058744756e9978eac6244156ed1c27f1e20e 100644 (file)
@@ -24,7 +24,7 @@ static const char *fast_export_usage[] = {
 };
 
 static int progress;
-static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
+static enum { ABORT, VERBATIM, WARN, WARN_STRIP, STRIP } signed_tag_mode = ABORT;
 static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ERROR;
 static int fake_missing_tagger;
 static int use_done_feature;
@@ -40,6 +40,8 @@ static int parse_opt_signed_tag_mode(const struct option *opt,
                signed_tag_mode = VERBATIM;
        else if (!strcmp(arg, "warn"))
                signed_tag_mode = WARN;
+       else if (!strcmp(arg, "warn-strip"))
+               signed_tag_mode = WARN_STRIP;
        else if (!strcmp(arg, "strip"))
                signed_tag_mode = STRIP;
        else
@@ -377,7 +379,7 @@ static void handle_tag(const char *name, struct tag *tag)
        int tagged_mark;
        struct commit *p;
 
-       /* Trees have no identifer in fast-export output, thus we have no way
+       /* Trees have no identifier in fast-export output, thus we have no way
         * to output tags of trees, tags of tags of trees, etc.  Simply omit
         * such tags.
         */
@@ -428,6 +430,10 @@ static void handle_tag(const char *name, struct tag *tag)
                                /* fallthru */
                        case VERBATIM:
                                break;
+                       case WARN_STRIP:
+                               warning ("Stripping signature from tag %s",
+                                        sha1_to_hex(tag->object.sha1));
+                               /* fallthru */
                        case STRIP:
                                message_size = signature + 1 - message;
                                break;
@@ -607,6 +613,8 @@ static void import_marks(char *input_file)
                char *line_end, *mark_end;
                unsigned char sha1[20];
                struct object *object;
+               struct commit *commit;
+               enum object_type type;
 
                line_end = strchr(line, '\n');
                if (line[0] != ':' || !line_end)
@@ -615,23 +623,29 @@ static void import_marks(char *input_file)
 
                mark = strtoumax(line + 1, &mark_end, 10);
                if (!mark || mark_end == line + 1
-                       || *mark_end != ' ' || get_sha1(mark_end + 1, sha1))
+                       || *mark_end != ' ' || get_sha1_hex(mark_end + 1, sha1))
                        die("corrupt mark line: %s", line);
 
                if (last_idnum < mark)
                        last_idnum = mark;
 
-               object = parse_object(sha1);
-               if (!object)
+               type = sha1_object_info(sha1, NULL);
+               if (type < 0)
+                       die("object not found: %s", sha1_to_hex(sha1));
+
+               if (type != OBJ_COMMIT)
+                       /* only commits */
                        continue;
 
+               commit = lookup_commit(sha1);
+               if (!commit)
+                       die("not a commit? can't happen: %s", sha1_to_hex(sha1));
+
+               object = &commit->object;
+
                if (object->flags & SHOWN)
                        error("Object %s already has a mark", sha1_to_hex(sha1));
 
-               if (object->type != OBJ_COMMIT)
-                       /* only commits */
-                       continue;
-
                mark_object(object, mark);
 
                object->flags |= SHOWN;
index 4b6b1dfe66952a7e3fb1516e7e14473febde3d2d..564705555b897bde0a1a2c3fad3c1f1b39903b53 100644 (file)
@@ -36,7 +36,8 @@ static int tags = TAGS_DEFAULT, unshallow;
 static const char *depth;
 static const char *upload_pack;
 static struct strbuf default_rla = STRBUF_INIT;
-static struct transport *transport;
+static struct transport *gtransport;
+static struct transport *gsecondary;
 static const char *submodule_prefix = "";
 static const char *recurse_submodules_default;
 
@@ -95,8 +96,10 @@ static struct option builtin_fetch_options[] = {
 
 static void unlock_pack(void)
 {
-       if (transport)
-               transport_unlock_pack(transport);
+       if (gtransport)
+               transport_unlock_pack(gtransport);
+       if (gsecondary)
+               transport_unlock_pack(gsecondary);
 }
 
 static void unlock_pack_on_signal(int signo)
@@ -119,7 +122,7 @@ static void add_merge_config(struct ref **head,
 
                for (rm = *head; rm; rm = rm->next) {
                        if (branch_merge_matches(branch, i, rm->name)) {
-                               rm->merge = 1;
+                               rm->fetch_head_status = FETCH_HEAD_MERGE;
                                break;
                        }
                }
@@ -140,7 +143,7 @@ static void add_merge_config(struct ref **head,
                refspec.src = branch->merge[i]->src;
                get_fetch_map(remote_refs, &refspec, tail, 1);
                for (rm = *old_tail; rm; rm = rm->next)
-                       rm->merge = 1;
+                       rm->fetch_head_status = FETCH_HEAD_MERGE;
        }
 }
 
@@ -160,6 +163,8 @@ static struct ref *get_ref_map(struct transport *transport,
        const struct ref *remote_refs = transport_get_remote_refs(transport);
 
        if (ref_count || tags == TAGS_SET) {
+               struct ref **old_tail;
+
                for (i = 0; i < ref_count; i++) {
                        get_fetch_map(remote_refs, &refs[i], &tail, 0);
                        if (refs[i].dst && refs[i].dst[0])
@@ -167,9 +172,23 @@ static struct ref *get_ref_map(struct transport *transport,
                }
                /* Merge everything on the command line, but not --tags */
                for (rm = ref_map; rm; rm = rm->next)
-                       rm->merge = 1;
+                       rm->fetch_head_status = FETCH_HEAD_MERGE;
                if (tags == TAGS_SET)
                        get_fetch_map(remote_refs, tag_refspec, &tail, 0);
+
+               /*
+                * For any refs that we happen to be fetching via command-line
+                * arguments, take the opportunity to update their configured
+                * counterparts. However, we do not want to mention these
+                * entries in FETCH_HEAD at all, as they would simply be
+                * duplicates of existing entries.
+                */
+               old_tail = tail;
+               for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
+                       get_fetch_map(ref_map, &transport->remote->fetch[i],
+                                     &tail, 1);
+               for (rm = *old_tail; rm; rm = rm->next)
+                       rm->fetch_head_status = FETCH_HEAD_IGNORE;
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
@@ -186,7 +205,7 @@ static struct ref *get_ref_map(struct transport *transport,
                                        *autotags = 1;
                                if (!i && !has_merge && ref_map &&
                                    !remote->fetch[0].pattern)
-                                       ref_map->merge = 1;
+                                       ref_map->fetch_head_status = FETCH_HEAD_MERGE;
                        }
                        /*
                         * if the remote we're fetching from is the same
@@ -202,7 +221,7 @@ static struct ref *get_ref_map(struct transport *transport,
                        ref_map = get_remote_ref(remote_refs, "HEAD");
                        if (!ref_map)
                                die(_("Couldn't find remote ref HEAD"));
-                       ref_map->merge = 1;
+                       ref_map->fetch_head_status = FETCH_HEAD_MERGE;
                        tail = &ref_map->next;
                }
        }
@@ -389,7 +408,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        const char *what, *kind;
        struct ref *rm;
        char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
-       int want_merge;
+       int want_status;
 
        fp = fopen(filename, "a");
        if (!fp)
@@ -407,19 +426,22 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        }
 
        /*
-        * The first pass writes objects to be merged and then the
-        * second pass writes the rest, in order to allow using
-        * FETCH_HEAD as a refname to refer to the ref to be merged.
+        * We do a pass for each fetch_head_status type in their enum order, so
+        * merged entries are written before not-for-merge. That lets readers
+        * use FETCH_HEAD as a refname to refer to the ref to be merged.
         */
-       for (want_merge = 1; 0 <= want_merge; want_merge--) {
+       for (want_status = FETCH_HEAD_MERGE;
+            want_status <= FETCH_HEAD_IGNORE;
+            want_status++) {
                for (rm = ref_map; rm; rm = rm->next) {
                        struct ref *ref = NULL;
+                       const char *merge_status_marker = "";
 
                        commit = lookup_commit_reference_gently(rm->old_sha1, 1);
                        if (!commit)
-                               rm->merge = 0;
+                               rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
 
-                       if (rm->merge != want_merge)
+                       if (rm->fetch_head_status != want_status)
                                continue;
 
                        if (rm->peer_ref) {
@@ -465,16 +487,26 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                        strbuf_addf(&note, "%s ", kind);
                                strbuf_addf(&note, "'%s' of ", what);
                        }
-                       fprintf(fp, "%s\t%s\t%s",
-                               sha1_to_hex(rm->old_sha1),
-                               rm->merge ? "" : "not-for-merge",
-                               note.buf);
-                       for (i = 0; i < url_len; ++i)
-                               if ('\n' == url[i])
-                                       fputs("\\n", fp);
-                               else
-                                       fputc(url[i], fp);
-                       fputc('\n', fp);
+                       switch (rm->fetch_head_status) {
+                       case FETCH_HEAD_NOT_FOR_MERGE:
+                               merge_status_marker = "not-for-merge";
+                               /* fall-through */
+                       case FETCH_HEAD_MERGE:
+                               fprintf(fp, "%s\t%s\t%s",
+                                       sha1_to_hex(rm->old_sha1),
+                                       merge_status_marker,
+                                       note.buf);
+                               for (i = 0; i < url_len; ++i)
+                                       if ('\n' == url[i])
+                                               fputs("\\n", fp);
+                                       else
+                                               fputc(url[i], fp);
+                               fputc('\n', fp);
+                               break;
+                       default:
+                               /* do not write anything to FETCH_HEAD */
+                               break;
+                       }
 
                        strbuf_reset(&note);
                        if (ref) {
@@ -571,7 +603,8 @@ static int add_existing(const char *refname, const unsigned char *sha1,
 {
        struct string_list *list = (struct string_list *)cbdata;
        struct string_list_item *item = string_list_insert(list, refname);
-       item->util = (void *)sha1;
+       item->util = xmalloc(20);
+       hashcpy(item->util, sha1);
        return 0;
 }
 
@@ -590,7 +623,7 @@ static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
                        struct ref ***tail)
 {
-       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct string_list remote_refs = STRING_LIST_INIT_NODUP;
        const struct ref *ref;
        struct string_list_item *item = NULL;
@@ -636,7 +669,7 @@ static void find_non_local_tags(struct transport *transport,
                item = string_list_insert(&remote_refs, ref->name);
                item->util = (void *)ref->old_sha1;
        }
-       string_list_clear(&existing_refs, 0);
+       string_list_clear(&existing_refs, 1);
 
        /*
         * We may have a final lightweight tag that needs to be
@@ -690,14 +723,56 @@ static int truncate_fetch_head(void)
        return 0;
 }
 
+static void set_option(struct transport *transport, const char *name, const char *value)
+{
+       int r = transport_set_option(transport, name, value);
+       if (r < 0)
+               die(_("Option \"%s\" value \"%s\" is not valid for %s"),
+                   name, value, transport->url);
+       if (r > 0)
+               warning(_("Option \"%s\" is ignored for %s\n"),
+                       name, transport->url);
+}
+
+static struct transport *prepare_transport(struct remote *remote)
+{
+       struct transport *transport;
+       transport = transport_get(remote, NULL);
+       transport_set_verbosity(transport, verbosity, progress);
+       if (upload_pack)
+               set_option(transport, TRANS_OPT_UPLOADPACK, upload_pack);
+       if (keep)
+               set_option(transport, TRANS_OPT_KEEP, "yes");
+       if (depth)
+               set_option(transport, TRANS_OPT_DEPTH, depth);
+       return transport;
+}
+
+static void backfill_tags(struct transport *transport, struct ref *ref_map)
+{
+       if (transport->cannot_reuse) {
+               gsecondary = prepare_transport(transport->remote);
+               transport = gsecondary;
+       }
+
+       transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
+       transport_set_option(transport, TRANS_OPT_DEPTH, "0");
+       fetch_refs(transport, ref_map);
+
+       if (gsecondary) {
+               transport_disconnect(gsecondary);
+               gsecondary = NULL;
+       }
+}
+
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
-       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
-       struct string_list_item *peer_item = NULL;
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct ref *ref_map;
        struct ref *rm;
        int autotags = (transport->remote->fetch_tags == 1);
+       int retcode = 0;
 
        for_each_ref(add_existing, &existing_refs);
 
@@ -713,9 +788,9 @@ static int do_fetch(struct transport *transport,
 
        /* if not appending, truncate FETCH_HEAD */
        if (!append && !dry_run) {
-               int errcode = truncate_fetch_head();
-               if (errcode)
-                       return errcode;
+               retcode = truncate_fetch_head();
+               if (retcode)
+                       goto cleanup;
        }
 
        ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
@@ -724,8 +799,9 @@ static int do_fetch(struct transport *transport,
 
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref) {
-                       peer_item = string_list_lookup(&existing_refs,
-                                                      rm->peer_ref->name);
+                       struct string_list_item *peer_item =
+                               string_list_lookup(&existing_refs,
+                                                  rm->peer_ref->name);
                        if (peer_item)
                                hashcpy(rm->peer_ref->old_sha1,
                                        peer_item->util);
@@ -736,7 +812,8 @@ static int do_fetch(struct transport *transport,
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
        if (fetch_refs(transport, ref_map)) {
                free_refs(ref_map);
-               return 1;
+               retcode = 1;
+               goto cleanup;
        }
        if (prune) {
                /* If --tags was specified, pretend the user gave us the canonical tags refspec */
@@ -771,26 +848,14 @@ static int do_fetch(struct transport *transport,
                struct ref **tail = &ref_map;
                ref_map = NULL;
                find_non_local_tags(transport, &ref_map, &tail);
-               if (ref_map) {
-                       transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
-                       transport_set_option(transport, TRANS_OPT_DEPTH, "0");
-                       fetch_refs(transport, ref_map);
-               }
+               if (ref_map)
+                       backfill_tags(transport, ref_map);
                free_refs(ref_map);
        }
 
-       return 0;
-}
-
-static void set_option(const char *name, const char *value)
-{
-       int r = transport_set_option(transport, name, value);
-       if (r < 0)
-               die(_("Option \"%s\" value \"%s\" is not valid for %s"),
-                       name, value, transport->url);
-       if (r > 0)
-               warning(_("Option \"%s\" is ignored for %s\n"),
-                       name, transport->url);
+ cleanup:
+       string_list_clear(&existing_refs, 1);
+       return retcode;
 }
 
 static int get_one_remote_for_fetch(struct remote *remote, void *priv)
@@ -915,15 +980,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
                die(_("No remote repository specified.  Please, specify either a URL or a\n"
                    "remote name from which new revisions should be fetched."));
 
-       transport = transport_get(remote, NULL);
-       transport_set_verbosity(transport, verbosity, progress);
-       if (upload_pack)
-               set_option(TRANS_OPT_UPLOADPACK, upload_pack);
-       if (keep)
-               set_option(TRANS_OPT_KEEP, "yes");
-       if (depth)
-               set_option(TRANS_OPT_DEPTH, depth);
-
+       gtransport = prepare_transport(remote);
        if (argc > 0) {
                int j = 0;
                refs = xcalloc(argc + 1, sizeof(const char *));
@@ -949,10 +1006,10 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack);
        refspec = parse_fetch_refspec(ref_nr, refs);
-       exit_code = do_fetch(transport, refspec, ref_nr);
+       exit_code = do_fetch(gtransport, refspec, ref_nr);
        free_refspec(ref_nr, refspec);
-       transport_disconnect(transport);
-       transport = NULL;
+       transport_disconnect(gtransport);
+       gtransport = NULL;
        return exit_code;
 }
 
index bb9a2cd44722dc27d54aa5451278a10f512becb8..9909b6d519f0a7d3d3b53313e7886c9482d98ba5 100644 (file)
@@ -112,7 +112,7 @@ static int mark_object(struct object *obj, int type, void *data)
                return 1;
        }
 
-       add_object_array(obj, (void *) parent, &pending);
+       add_object_array(obj, NULL, &pending);
        return 0;
 }
 
index 159e65d47a41b56634bc3fb480f9c051d40e692c..d3b3b1db1121b37d5a8d0e440e03273526003774 100644 (file)
@@ -286,8 +286,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
        struct strbuf pathbuf = STRBUF_INIT;
 
        if (opt->relative && opt->prefix_length) {
-               quote_path_relative(filename + tree_name_len, -1, &pathbuf,
-                                   opt->prefix);
+               quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
                strbuf_insert(&pathbuf, 0, filename, tree_name_len);
        } else {
                strbuf_addstr(&pathbuf, filename);
@@ -318,7 +317,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
        struct strbuf buf = STRBUF_INIT;
 
        if (opt->relative && opt->prefix_length)
-               quote_path_relative(filename, -1, &buf, opt->prefix);
+               quote_path_relative(filename, opt->prefix, &buf);
        else
                strbuf_addstr(&buf, filename);
 
@@ -376,7 +375,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
        read_cache();
 
        for (nr = 0; nr < active_nr; nr++) {
-               struct cache_entry *ce = active_cache[nr];
+               const struct cache_entry *ce = active_cache[nr];
                if (!S_ISREG(ce->ce_mode))
                        continue;
                if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
index 062957f629af09db81d78b9cb0be1ce60fd75ec4..f1e236b9124f8b7bfd9aa5e926d525c6f4568268 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * builtin-help.c
- *
  * Builtin help command
  */
 #include "cache.h"
index 79dfe47320e244c3110ce65d5b19fa87ebca3cb5..9c1cfac4427ef8d8fbca0834e1c8b644ceddf998 100644 (file)
@@ -77,8 +77,10 @@ static int nr_threads;
 
 static int from_stdin;
 static int strict;
+static int do_fsck_object;
 static int verbose;
 static int show_stat;
+static int check_self_contained_and_connected;
 
 static struct progress *progress;
 
@@ -187,13 +189,13 @@ static int mark_link(struct object *obj, int type, void *data)
 
 /* The content of each linked object must have been checked
    or it must be already present in the object database */
-static void check_object(struct object *obj)
+static unsigned check_object(struct object *obj)
 {
        if (!obj)
-               return;
+               return 0;
 
        if (!(obj->flags & FLAG_LINK))
-               return;
+               return 0;
 
        if (!(obj->flags & FLAG_CHECKED)) {
                unsigned long size;
@@ -201,17 +203,20 @@ static void check_object(struct object *obj)
                if (type != obj->type || type <= 0)
                        die(_("object of unexpected type"));
                obj->flags |= FLAG_CHECKED;
-               return;
+               return 1;
        }
+
+       return 0;
 }
 
-static void check_objects(void)
+static unsigned check_objects(void)
 {
-       unsigned i, max;
+       unsigned i, max, foreign_nr = 0;
 
        max = get_max_object_index();
        for (i = 0; i < max; i++)
-               check_object(get_indexed_object(i));
+               foreign_nr += check_object(get_indexed_object(i));
+       return foreign_nr;
 }
 
 
@@ -747,8 +752,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                        int eaten;
                        void *buf = (void *) data;
 
-                       if (!buf)
-                               buf = new_data = get_data_from_pack(obj_entry);
+                       assert(data && "data can only be NULL for large _blobs_");
 
                        /*
                         * we do not need to free the memory here, as the
@@ -757,7 +761,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                        obj = parse_object_buffer(sha1, type, size, buf, &eaten);
                        if (!obj)
                                die(_("invalid %s"), typename(type));
-                       if (fsck_object(obj, 1, fsck_error_function))
+                       if (do_fsck_object &&
+                           fsck_object(obj, 1, fsck_error_function))
                                die(_("Error in object"));
                        if (fsck_walk(obj, mark_link, NULL))
                                die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
@@ -1491,6 +1496,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        struct pack_idx_entry **idx_objects;
        struct pack_idx_option opts;
        unsigned char pack_sha1[20];
+       unsigned foreign_nr = 1;        /* zero is a "good" value, assume bad */
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(index_pack_usage);
@@ -1512,6 +1518,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                fix_thin_pack = 1;
                        } else if (!strcmp(arg, "--strict")) {
                                strict = 1;
+                               do_fsck_object = 1;
+                       } else if (!strcmp(arg, "--check-self-contained-and-connected")) {
+                               strict = 1;
+                               check_self_contained_and_connected = 1;
                        } else if (!strcmp(arg, "--verify")) {
                                verify = 1;
                        } else if (!strcmp(arg, "--verify-stat")) {
@@ -1625,7 +1635,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
        free(deltas);
        if (strict)
-               check_objects();
+               foreign_nr = check_objects();
 
        if (show_stat)
                show_pack_info(stat_only);
@@ -1651,5 +1661,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (index_name == NULL)
                free((void *) curr_index);
 
+       /*
+        * Let the caller know this pack is not self contained
+        */
+       if (check_self_contained_and_connected && foreign_nr)
+               return 1;
+
        return 0;
 }
index ad46f72950f3e4fda292da25f936e48c59705410..2625f9881ad836d1a3e303f4c159d6b76a3ae04f 100644 (file)
@@ -19,6 +19,7 @@
 #include "remote.h"
 #include "string-list.h"
 #include "parse-options.h"
+#include "line-log.h"
 #include "branch.h"
 #include "streaming.h"
 #include "version.h"
@@ -37,11 +38,17 @@ static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
 static const char * const builtin_log_usage[] = {
-       N_("git log [<options>] [<since>..<until>] [[--] <path>...]\n")
+       N_("git log [<options>] [<revision range>] [[--] <path>...]\n")
        N_("   or: git show [options] <object>..."),
        NULL
 };
 
+struct line_opt_callback_data {
+       struct rev_info *rev;
+       const char *prefix;
+       struct string_list args;
+};
+
 static int parse_decoration_style(const char *var, const char *value)
 {
        switch (git_config_maybe_bool(var, value)) {
@@ -76,6 +83,19 @@ static int decorate_callback(const struct option *opt, const char *arg, int unse
        return 0;
 }
 
+static int log_line_range_callback(const struct option *option, const char *arg, int unset)
+{
+       struct line_opt_callback_data *data = option->value;
+
+       if (!arg)
+               return -1;
+
+       data->rev->line_level_traverse = 1;
+       string_list_append(&data->args, arg);
+
+       return 0;
+}
+
 static void cmd_log_init_defaults(struct rev_info *rev)
 {
        if (fmt_pretty)
@@ -98,6 +118,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 {
        struct userformat_want w;
        int quiet = 0, source = 0, mailmap = 0;
+       static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
 
        const struct option builtin_log_options[] = {
                OPT_BOOL(0, "quiet", &quiet, N_("suppress diff output")),
@@ -105,9 +126,15 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
                { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
                  PARSE_OPT_OPTARG, decorate_callback},
+               OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
+                            "Process line range n,m in file, counting from 1",
+                            log_line_range_callback),
                OPT_END()
        };
 
+       line_cb.rev = rev;
+       line_cb.prefix = prefix;
+
        mailmap = use_mailmap_config;
        argc = parse_options(argc, argv, prefix,
                             builtin_log_options, builtin_log_usage,
@@ -161,6 +188,10 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                rev->show_decorations = 1;
                load_ref_decorations(decoration_style);
        }
+
+       if (rev->line_level_traverse)
+               line_log_init(rev, line_cb.prefix, &line_cb.args);
+
        setup_pager();
 }
 
@@ -206,7 +237,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
        int i = revs->early_output;
        int show_header = 1;
 
-       sort_in_topological_order(&list, revs->lifo);
+       sort_in_topological_order(&list, revs->sort_order);
        while (list && i) {
                struct commit *commit = list->item;
                switch (simplify_commit(revs, commit)) {
@@ -1081,6 +1112,21 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int from_callback(const struct option *opt, const char *arg, int unset)
+{
+       char **from = opt->value;
+
+       free(*from);
+
+       if (unset)
+               *from = NULL;
+       else if (arg)
+               *from = xstrdup(arg);
+       else
+               *from = xstrdup(git_committer_info(IDENT_NO_DATE));
+       return 0;
+}
+
 int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
        struct commit *commit;
@@ -1103,6 +1149,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int reroll_count = -1;
        char *branch_name = NULL;
+       char *from = NULL;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            N_("use [PATCH n/m] even with a single patch"),
@@ -1146,6 +1193,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                            0, to_callback },
                { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
                            0, cc_callback },
+               { OPTION_CALLBACK, 0, "from", &from, N_("ident"),
+                           N_("set From address to <ident> (or committer ident if absent)"),
+                           PARSE_OPT_OPTARG, from_callback },
                OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
                            N_("make first mail a reply to <message-id>")),
                { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
@@ -1233,6 +1283,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
        rev.extra_headers = strbuf_detach(&buf, NULL);
 
+       if (from) {
+               if (split_ident_line(&rev.from_ident, from, strlen(from)))
+                       die(_("invalid ident line: %s"), from);
+       }
+
        if (start_number < 0)
                start_number = 1;
 
index 175e6e3e72003387bfa4f78dbd0ed10c02c4e318..5cf3e313700e4cbcd26ae1a9f3f99f2819e40702 100644 (file)
@@ -46,10 +46,14 @@ static const char *tag_modified = "";
 static const char *tag_skip_worktree = "";
 static const char *tag_resolve_undo = "";
 
-static void write_name(const char* name, size_t len)
+static void write_name(const char *name)
 {
-       write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
-                       line_terminator);
+       /*
+        * With "--full-name", prefix_len=0; this caller needs to pass
+        * an empty string in that case (a NULL is good for "").
+        */
+       write_name_quoted_relative(name, prefix_len ? prefix : NULL,
+                                  stdout, line_terminator);
 }
 
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
@@ -63,7 +67,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
                return;
 
        fputs(tag, stdout);
-       write_name(ent->name, ent->len);
+       write_name(ent->name);
 }
 
 static void show_other_files(struct dir_struct *dir)
@@ -127,7 +131,7 @@ static void show_killed_files(struct dir_struct *dir)
        }
 }
 
-static void show_ce_entry(const char *tag, struct cache_entry *ce)
+static void show_ce_entry(const char *tag, const struct cache_entry *ce)
 {
        int len = max_prefix_len;
 
@@ -163,13 +167,15 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
                       find_unique_abbrev(ce->sha1,abbrev),
                       ce_stage(ce));
        }
-       write_name(ce->name, ce_namelen(ce));
+       write_name(ce->name);
        if (debug_mode) {
-               printf("  ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
-               printf("  mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
-               printf("  dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
-               printf("  uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
-               printf("  size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
+               const struct stat_data *sd = &ce->ce_stat_data;
+
+               printf("  ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+               printf("  mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+               printf("  dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
+               printf("  uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
+               printf("  size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
        }
 }
 
@@ -196,24 +202,20 @@ static void show_ru_info(void)
                        printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
                               find_unique_abbrev(ui->sha1[i], abbrev),
                               i + 1);
-                       write_name(path, len);
+                       write_name(path);
                }
        }
 }
 
-static int ce_excluded(struct path_exclude_check *check, struct cache_entry *ce)
+static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce)
 {
        int dtype = ce_to_dtype(ce);
-       return is_path_excluded(check, ce->name, ce_namelen(ce), &dtype);
+       return is_excluded(dir, ce->name, &dtype);
 }
 
 static void show_files(struct dir_struct *dir)
 {
        int i;
-       struct path_exclude_check check;
-
-       if ((dir->flags & DIR_SHOW_IGNORED))
-               path_exclude_check_init(&check, dir);
 
        /* For cached/deleted files we don't need to even do the readdir */
        if (show_others || show_killed) {
@@ -223,11 +225,11 @@ static void show_files(struct dir_struct *dir)
                if (show_killed)
                        show_killed_files(dir);
        }
-       if (show_cached | show_stage) {
+       if (show_cached || show_stage) {
                for (i = 0; i < active_nr; i++) {
-                       struct cache_entry *ce = active_cache[i];
+                       const struct cache_entry *ce = active_cache[i];
                        if ((dir->flags & DIR_SHOW_IGNORED) &&
-                           !ce_excluded(&check, ce))
+                           !ce_excluded(dir, ce))
                                continue;
                        if (show_unmerged && !ce_stage(ce))
                                continue;
@@ -237,13 +239,13 @@ static void show_files(struct dir_struct *dir)
                                (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
                }
        }
-       if (show_deleted | show_modified) {
+       if (show_deleted || show_modified) {
                for (i = 0; i < active_nr; i++) {
-                       struct cache_entry *ce = active_cache[i];
+                       const struct cache_entry *ce = active_cache[i];
                        struct stat st;
                        int err;
                        if ((dir->flags & DIR_SHOW_IGNORED) &&
-                           !ce_excluded(&check, ce))
+                           !ce_excluded(dir, ce))
                                continue;
                        if (ce->ce_flags & CE_UPDATE)
                                continue;
@@ -256,9 +258,6 @@ static void show_files(struct dir_struct *dir)
                                show_ce_entry(tag_modified, ce);
                }
        }
-
-       if ((dir->flags & DIR_SHOW_IGNORED))
-               path_exclude_check_clear(&check);
 }
 
 /*
@@ -278,7 +277,7 @@ static void prune_cache(const char *prefix)
        last = active_nr;
        while (last > first) {
                int next = (last + first) >> 1;
-               struct cache_entry *ce = active_cache[next];
+               const struct cache_entry *ce = active_cache[next];
                if (!strncmp(ce->name, prefix, max_prefix_len)) {
                        first = next+1;
                        continue;
@@ -396,7 +395,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char
                if (found_dup)
                        continue;
 
-               name = quote_path_relative(pathspec[num], -1, &sb, prefix);
+               name = quote_path_relative(pathspec[num], prefix, &sb);
                error("pathspec '%s' did not match any file(s) known to git.",
                      name);
                errors++;
@@ -578,8 +577,8 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                die("ls-files --ignored needs some exclude pattern");
 
        /* With no flags, we default to showing the cached files */
-       if (!(show_stage | show_deleted | show_others | show_unmerged |
-             show_killed | show_modified | show_resolve_undo))
+       if (!(show_stage || show_deleted || show_others || show_unmerged ||
+             show_killed || show_modified || show_resolve_undo))
                show_cached = 1;
 
        if (max_prefix)
index 1bc79910481e6ee04b470620da5609c1c6fbcac4..0c4cd2f9f792cdf47cecf3401eb2bf7c676f7f6e 100644 (file)
@@ -107,7 +107,7 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
        if (!octopus && !reduce && argc < 2)
                usage_with_options(merge_base_usage, options);
-       if (is_ancestor && (show_all | octopus | reduce))
+       if (is_ancestor && (show_all || octopus || reduce))
                die("--is-ancestor cannot be used with other options");
        if (is_ancestor)
                return handle_is_ancestor(argc, argv);
index be5e514324aba1d50d8afe653dd72ccc5db060d8..b416d928492c43e78dc7ac0b3bd1d7a99abfe78b 100644 (file)
@@ -16,7 +16,7 @@ static int merge_entry(int pos, const char *path)
                die("git merge-index: %s not in the cache", path);
        found = 0;
        do {
-               struct cache_entry *ce = active_cache[pos];
+               const struct cache_entry *ce = active_cache[pos];
                int stage = ce_stage(ce);
 
                if (strcmp(ce->name, path))
@@ -58,7 +58,7 @@ static void merge_all(void)
 {
        int i;
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
                i += merge_entry(i, ce->name)-1;
index ed25d81b880eb830481d4e729d7e9acc3266de65..61cbde4094bf27ea9dfaba158233ba7f4613f2ee 100644 (file)
@@ -251,7 +251,11 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 
        for (i = 0; i < 3; i++) {
                mask |= (1 << i);
-               if (n[i].mode && S_ISDIR(n[i].mode))
+               /*
+                * Treat missing entries as directories so that we return
+                * after unresolved_directory has handled this.
+                */
+               if (!n[i].mode || S_ISDIR(n[i].mode))
                        dirmask |= (1 << i);
        }
 
@@ -302,7 +306,7 @@ static void unresolved(const struct traverse_info *info, struct name_entry n[3])
 static int threeway_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *entry, struct traverse_info *info)
 {
        /* Same in both? */
-       if (same_entry(entry+1, entry+2) || both_empty(entry+0, entry+2)) {
+       if (same_entry(entry+1, entry+2) || both_empty(entry+1, entry+2)) {
                /* Modified, added or removed identically */
                resolve(info, NULL, entry+1);
                return mask;
index 3e2daa37c367560450217cfae5cbb717bfb508af..34a6166b520c8ede9973fbf497b21ac6ecb761ad 100644 (file)
@@ -47,8 +47,8 @@ static const char * const builtin_merge_usage[] = {
 };
 
 static int show_diffstat = 1, shortlog_len = -1, squash;
-static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only, option_edit = -1;
+static int option_commit = 1;
+static int option_edit = -1;
 static int allow_trivial = 1, have_message, verify_signatures;
 static int overwrite_ignore = 1;
 static struct strbuf merge_msg = STRBUF_INIT;
@@ -76,6 +76,14 @@ static struct strategy all_strategy[] = {
 
 static const char *pull_twohead, *pull_octopus;
 
+enum ff_type {
+       FF_NO,
+       FF_ALLOW,
+       FF_ONLY
+};
+
+static enum ff_type fast_forward = FF_ALLOW;
+
 static int option_parse_message(const struct option *opt,
                                const char *arg, int unset)
 {
@@ -178,6 +186,13 @@ static int option_parse_n(const struct option *opt,
        return 0;
 }
 
+static int option_parse_ff_only(const struct option *opt,
+                         const char *arg, int unset)
+{
+       fast_forward = FF_ONLY;
+       return 0;
+}
+
 static struct option builtin_merge_options[] = {
        { OPTION_CALLBACK, 'n', NULL, NULL, NULL,
                N_("do not show a diffstat at the end of the merge"),
@@ -194,10 +209,10 @@ static struct option builtin_merge_options[] = {
                N_("perform a commit if the merge succeeds (default)")),
        OPT_BOOL('e', "edit", &option_edit,
                N_("edit message before committing")),
-       OPT_BOOLEAN(0, "ff", &allow_fast_forward,
-               N_("allow fast-forward (default)")),
-       OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
-               N_("abort if fast-forward is not possible")),
+       OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
+       { OPTION_CALLBACK, 0, "ff-only", NULL, NULL,
+               N_("abort if fast-forward is not possible"),
+               PARSE_OPT_NOARG | PARSE_OPT_NONEG, option_parse_ff_only },
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_BOOL(0, "verify-signatures", &verify_signatures,
                N_("Verify that the named commit has a valid GPG signature")),
@@ -581,10 +596,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
        else if (!strcmp(k, "merge.ff")) {
                int boolval = git_config_maybe_bool(k, v);
                if (0 <= boolval) {
-                       allow_fast_forward = boolval;
+                       fast_forward = boolval ? FF_ALLOW : FF_NO;
                } else if (v && !strcmp(v, "only")) {
-                       allow_fast_forward = 1;
-                       fast_forward_only = 1;
+                       fast_forward = FF_ONLY;
                } /* do not barf on values from future versions of git */
                return 0;
        } else if (!strcmp(k, "merge.defaulttoupstream")) {
@@ -863,7 +877,7 @@ static int finish_automerge(struct commit *head,
 
        free_commit_list(common);
        parents = remoteheads;
-       if (!head_subsumed || !allow_fast_forward)
+       if (!head_subsumed || fast_forward == FF_NO)
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
@@ -889,7 +903,7 @@ static int suggest_conflicts(int renormalizing)
                die_errno(_("Could not open '%s' for writing"), filename);
        fprintf(fp, "\nConflicts:\n");
        for (pos = 0; pos < active_nr; pos++) {
-               struct cache_entry *ce = active_cache[pos];
+               const struct cache_entry *ce = active_cache[pos];
 
                if (ce_stage(ce)) {
                        fprintf(fp, "\t%s\n", ce->name);
@@ -948,7 +962,7 @@ static int evaluate_result(void)
 }
 
 /*
- * Pretend as if the user told us to merge with the tracking
+ * Pretend as if the user told us to merge with the remote-tracking
  * branch we have for the upstream of the current branch
  */
 static int setup_with_upstream(const char ***argv)
@@ -967,7 +981,7 @@ static int setup_with_upstream(const char ***argv)
        args = xcalloc(branch->merge_nr + 1, sizeof(char *));
        for (i = 0; i < branch->merge_nr; i++) {
                if (!branch->merge[i]->dst)
-                       die(_("No remote tracking branch for %s from %s"),
+                       die(_("No remote-tracking branch for %s from %s"),
                            branch->merge[i]->src, branch->remote_name);
                args[i] = branch->merge[i]->dst;
        }
@@ -1008,7 +1022,7 @@ static void write_merge_state(struct commit_list *remoteheads)
        if (fd < 0)
                die_errno(_("Could not open '%s' for writing"), filename);
        strbuf_reset(&buf);
-       if (!allow_fast_forward)
+       if (fast_forward == FF_NO)
                strbuf_addf(&buf, "no-ff");
        if (write_in_full(fd, buf.buf, buf.len) != buf.len)
                die_errno(_("Could not write to '%s'"), filename);
@@ -1054,7 +1068,8 @@ static struct commit_list *collect_parents(struct commit *head_commit,
        for (i = 0; i < argc; i++) {
                struct commit *commit = get_merge_parent(argv[i]);
                if (!commit)
-                       die(_("%s - not something we can merge"), argv[i]);
+                       help_unknown_ref(argv[i], "merge",
+                                        "not something we can merge");
                remotes = &commit_list_insert(commit, remotes)->next;
        }
        *remotes = NULL;
@@ -1156,14 +1171,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                show_diffstat = 0;
 
        if (squash) {
-               if (!allow_fast_forward)
+               if (fast_forward == FF_NO)
                        die(_("You cannot combine --squash with --no-ff."));
                option_commit = 0;
        }
 
-       if (!allow_fast_forward && fast_forward_only)
-               die(_("You cannot combine --no-ff with --ff-only."));
-
        if (!abort_current_merge) {
                if (!argc) {
                        if (default_to_upstream)
@@ -1205,7 +1217,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                "empty head"));
                if (squash)
                        die(_("Squash commit into empty head not supported yet"));
-               if (!allow_fast_forward)
+               if (fast_forward == FF_NO)
                        die(_("Non-fast-forward commit does not make sense into "
                            "an empty head"));
                remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
@@ -1293,11 +1305,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                            sha1_to_hex(commit->object.sha1));
                setenv(buf.buf, merge_remote_util(commit)->name, 1);
                strbuf_reset(&buf);
-               if (!fast_forward_only &&
+               if (fast_forward != FF_ONLY &&
                    merge_remote_util(commit) &&
                    merge_remote_util(commit)->obj &&
                    merge_remote_util(commit)->obj->type == OBJ_TAG)
-                       allow_fast_forward = 0;
+                       fast_forward = FF_NO;
        }
 
        if (option_edit < 0)
@@ -1314,7 +1326,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
        for (i = 0; i < use_strategies_nr; i++) {
                if (use_strategies[i]->attr & NO_FAST_FORWARD)
-                       allow_fast_forward = 0;
+                       fast_forward = FF_NO;
                if (use_strategies[i]->attr & NO_TRIVIAL)
                        allow_trivial = 0;
        }
@@ -1344,7 +1356,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 */
                finish_up_to_date("Already up-to-date.");
                goto done;
-       } else if (allow_fast_forward && !remoteheads->next &&
+       } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
                        !hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
                /* Again the most common case of merging one remote. */
@@ -1391,7 +1403,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * only one common.
                 */
                refresh_cache(REFRESH_QUIET);
-               if (allow_trivial && !fast_forward_only) {
+               if (allow_trivial && fast_forward != FF_ONLY) {
                        /* See if it is really trivial. */
                        git_committer_info(IDENT_STRICT);
                        printf(_("Trying really trivial in-index merge...\n"));
@@ -1432,7 +1444,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (fast_forward_only)
+       if (fast_forward == FF_ONLY)
                die(_("Not possible to fast-forward, aborting."));
 
        /* We are going to make a new commit. */
index 62382479749909d449b718fc2f3bef36906caa4b..0aaa19e4abeb462b2f1d05d5e03aa29bee810db8 100644 (file)
@@ -4,6 +4,7 @@
 #include "tag.h"
 #include "refs.h"
 #include "parse-options.h"
+#include "sha1-lookup.h"
 
 #define CUTOFF_DATE_SLOP 86400 /* one day */
 
@@ -82,23 +83,88 @@ copy_data:
        }
 }
 
+static int subpath_matches(const char *path, const char *filter)
+{
+       const char *subpath = path;
+
+       while (subpath) {
+               if (!fnmatch(filter, subpath, 0))
+                       return subpath - path;
+               subpath = strchr(subpath, '/');
+               if (subpath)
+                       subpath++;
+       }
+       return -1;
+}
+
+static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
+{
+       if (shorten_unambiguous)
+               refname = shorten_unambiguous_ref(refname, 0);
+       else if (!prefixcmp(refname, "refs/heads/"))
+               refname = refname + 11;
+       else if (!prefixcmp(refname, "refs/"))
+               refname = refname + 5;
+       return refname;
+}
+
 struct name_ref_data {
        int tags_only;
        int name_only;
        const char *ref_filter;
 };
 
+static struct tip_table {
+       struct tip_table_entry {
+               unsigned char sha1[20];
+               const char *refname;
+       } *table;
+       int nr;
+       int alloc;
+       int sorted;
+} tip_table;
+
+static void add_to_tip_table(const unsigned char *sha1, const char *refname,
+                            int shorten_unambiguous)
+{
+       refname = name_ref_abbrev(refname, shorten_unambiguous);
+
+       ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
+       hashcpy(tip_table.table[tip_table.nr].sha1, sha1);
+       tip_table.table[tip_table.nr].refname = xstrdup(refname);
+       tip_table.nr++;
+       tip_table.sorted = 0;
+}
+
+static int tipcmp(const void *a_, const void *b_)
+{
+       const struct tip_table_entry *a = a_, *b = b_;
+       return hashcmp(a->sha1, b->sha1);
+}
+
 static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
 {
        struct object *o = parse_object(sha1);
        struct name_ref_data *data = cb_data;
+       int can_abbreviate_output = data->tags_only && data->name_only;
        int deref = 0;
 
        if (data->tags_only && prefixcmp(path, "refs/tags/"))
                return 0;
 
-       if (data->ref_filter && fnmatch(data->ref_filter, path, 0))
-               return 0;
+       if (data->ref_filter) {
+               switch (subpath_matches(path, data->ref_filter)) {
+               case -1: /* did not match */
+                       return 0;
+               case 0:  /* matched fully */
+                       break;
+               default: /* matched subpath */
+                       can_abbreviate_output = 1;
+                       break;
+               }
+       }
+
+       add_to_tip_table(sha1, path, can_abbreviate_output);
 
        while (o && o->type == OBJ_TAG) {
                struct tag *t = (struct tag *) o;
@@ -110,20 +176,38 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
        if (o && o->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *)o;
 
-               if (!prefixcmp(path, "refs/heads/"))
-                       path = path + 11;
-               else if (data->tags_only
-                   && data->name_only
-                   && !prefixcmp(path, "refs/tags/"))
-                       path = path + 10;
-               else if (!prefixcmp(path, "refs/"))
-                       path = path + 5;
-
+               path = name_ref_abbrev(path, can_abbreviate_output);
                name_rev(commit, xstrdup(path), 0, 0, deref);
        }
        return 0;
 }
 
+static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
+{
+       struct tip_table_entry *table = table_;
+       return table[ix].sha1;
+}
+
+static const char *get_exact_ref_match(const struct object *o)
+{
+       int found;
+
+       if (!tip_table.table || !tip_table.nr)
+               return NULL;
+
+       if (!tip_table.sorted) {
+               qsort(tip_table.table, tip_table.nr, sizeof(*tip_table.table),
+                     tipcmp);
+               tip_table.sorted = 1;
+       }
+
+       found = sha1_pos(o->sha1, tip_table.table, tip_table.nr,
+                        nth_tip_table_ent);
+       if (0 <= found)
+               return tip_table.table[found].refname;
+       return NULL;
+}
+
 /* returns a static buffer */
 static const char *get_rev_name(const struct object *o)
 {
@@ -132,7 +216,7 @@ static const char *get_rev_name(const struct object *o)
        struct commit *c;
 
        if (o->type != OBJ_COMMIT)
-               return NULL;
+               return get_exact_ref_match(o);
        c = (struct commit *) o;
        n = c->util;
        if (!n)
@@ -223,7 +307,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
        struct object_array revs = OBJECT_ARRAY_INIT;
-       int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
+       int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
                OPT_BOOLEAN(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
@@ -236,6 +320,12 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN(0, "undefined", &allow_undefined, N_("allow to print `undefined` names")),
                OPT_BOOLEAN(0, "always",     &always,
                           N_("show abbreviated commit object as fallback")),
+               {
+                       /* A Hidden OPT_BOOL */
+                       OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
+                       N_("dereference tags in the input (internal use)"),
+                       PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
+               },
                OPT_END(),
        };
 
@@ -250,7 +340,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
 
        for (; argc; argc--, argv++) {
                unsigned char sha1[20];
-               struct object *o;
+               struct object *object;
                struct commit *commit;
 
                if (get_sha1(*argv, sha1)) {
@@ -259,17 +349,34 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
-               o = deref_tag(parse_object(sha1), *argv, 0);
-               if (!o || o->type != OBJ_COMMIT) {
-                       fprintf(stderr, "Could not get commit for %s. Skipping.\n",
+               commit = NULL;
+               object = parse_object(sha1);
+               if (object) {
+                       struct object *peeled = deref_tag(object, *argv, 0);
+                       if (peeled && peeled->type == OBJ_COMMIT)
+                               commit = (struct commit *)peeled;
+               }
+
+               if (!object) {
+                       fprintf(stderr, "Could not get object for %s. Skipping.\n",
                                        *argv);
                        continue;
                }
 
-               commit = (struct commit *)o;
-               if (cutoff > commit->date)
-                       cutoff = commit->date;
-               add_object_array((struct object *)commit, *argv, &revs);
+               if (commit) {
+                       if (cutoff > commit->date)
+                               cutoff = commit->date;
+               }
+
+               if (peel_tag) {
+                       if (!commit) {
+                               fprintf(stderr, "Could not get commit for %s. Skipping.\n",
+                                       *argv);
+                               continue;
+                       }
+                       object = (struct object *)commit;
+               }
+               add_object_array(object, *argv, &revs);
        }
 
        if (cutoff)
index 57748a6fb67820c15ee6a8a92b32d4a0392dd933..e4100c49820306b881a20db4bb3af7c13f12c019 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (c) 2010 Johan Herland <johan@herland.net>
  *
  * Based on git-notes.sh by Johannes Schindelin,
- * and builtin-tag.c by Kristian Høgsberg and Carlos Rica.
+ * and builtin/tag.c by Kristian Høgsberg and Carlos Rica.
  */
 
 #include "cache.h"
@@ -18,9 +18,7 @@
 #include "parse-options.h"
 #include "string-list.h"
 #include "notes-merge.h"
-
-static void commit_notes(struct notes_tree *t, const char *msg);
-static combine_notes_fn parse_combine_notes_fn(const char *v);
+#include "notes-utils.h"
 
 static const char * const git_notes_usage[] = {
        N_("git notes [--ref <notes_ref>] [list [<object>]]"),
@@ -287,139 +285,13 @@ static int parse_reedit_arg(const struct option *opt, const char *arg, int unset
        return parse_reuse_arg(opt, arg, unset);
 }
 
-static void commit_notes(struct notes_tree *t, const char *msg)
-{
-       struct strbuf buf = STRBUF_INIT;
-       unsigned char commit_sha1[20];
-
-       if (!t)
-               t = &default_notes_tree;
-       if (!t->initialized || !t->ref || !*t->ref)
-               die(_("Cannot commit uninitialized/unreferenced notes tree"));
-       if (!t->dirty)
-               return; /* don't have to commit an unchanged tree */
-
-       /* Prepare commit message and reflog message */
-       strbuf_addstr(&buf, 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);
-       strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
-       update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
-
-       strbuf_release(&buf);
-}
-
-static combine_notes_fn parse_combine_notes_fn(const char *v)
-{
-       if (!strcasecmp(v, "overwrite"))
-               return combine_notes_overwrite;
-       else if (!strcasecmp(v, "ignore"))
-               return combine_notes_ignore;
-       else if (!strcasecmp(v, "concatenate"))
-               return combine_notes_concatenate;
-       else if (!strcasecmp(v, "cat_sort_uniq"))
-               return combine_notes_cat_sort_uniq;
-       else
-               return NULL;
-}
-
-static int notes_rewrite_config(const char *k, const char *v, void *cb)
-{
-       struct notes_rewrite_cfg *c = cb;
-       if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
-               c->enabled = git_config_bool(k, v);
-               return 0;
-       } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
-               if (!v)
-                       config_error_nonbool(k);
-               c->combine = parse_combine_notes_fn(v);
-               if (!c->combine) {
-                       error(_("Bad notes.rewriteMode value: '%s'"), v);
-                       return 1;
-               }
-               return 0;
-       } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
-               /* note that a refs/ prefix is implied in the
-                * underlying for_each_glob_ref */
-               if (!prefixcmp(v, "refs/notes/"))
-                       string_list_add_refs_by_glob(c->refs, v);
-               else
-                       warning(_("Refusing to rewrite notes in %s"
-                               " (outside of refs/notes/)"), v);
-               return 0;
-       }
-
-       return 0;
-}
-
-
-struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
-{
-       struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
-       const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
-       const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
-       c->cmd = cmd;
-       c->enabled = 1;
-       c->combine = combine_notes_concatenate;
-       c->refs = xcalloc(1, sizeof(struct string_list));
-       c->refs->strdup_strings = 1;
-       c->refs_from_env = 0;
-       c->mode_from_env = 0;
-       if (rewrite_mode_env) {
-               c->mode_from_env = 1;
-               c->combine = parse_combine_notes_fn(rewrite_mode_env);
-               if (!c->combine)
-                       /* TRANSLATORS: The first %s is the name of the
-                          environment variable, the second %s is its value */
-                       error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT,
-                                       rewrite_mode_env);
-       }
-       if (rewrite_refs_env) {
-               c->refs_from_env = 1;
-               string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
-       }
-       git_config(notes_rewrite_config, c);
-       if (!c->enabled || !c->refs->nr) {
-               string_list_clear(c->refs, 0);
-               free(c->refs);
-               free(c);
-               return NULL;
-       }
-       c->trees = load_notes_trees(c->refs);
-       string_list_clear(c->refs, 0);
-       free(c->refs);
-       return c;
-}
-
-int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
-                         const unsigned char *from_obj, const unsigned char *to_obj)
-{
-       int ret = 0;
-       int i;
-       for (i = 0; c->trees[i]; i++)
-               ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
-       return ret;
-}
-
-void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c)
-{
-       int i;
-       for (i = 0; c->trees[i]; i++) {
-               commit_notes(c->trees[i], "Notes added by 'git notes copy'");
-               free_notes(c->trees[i]);
-       }
-       free(c->trees);
-       free(c);
-}
-
 static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
 {
        struct strbuf buf = STRBUF_INIT;
        struct notes_rewrite_cfg *c = NULL;
        struct notes_tree *t = NULL;
        int ret = 0;
+       const char *msg = "Notes added by 'git notes copy'";
 
        if (rewrite_cmd) {
                c = init_copy_notes_for_rewrite(rewrite_cmd);
@@ -461,10 +333,10 @@ static int notes_copy_from_stdin(int force, const char *rewrite_cmd)
        }
 
        if (!rewrite_cmd) {
-               commit_notes(t, "Notes added by 'git notes copy'");
+               commit_notes(t, msg);
                free_notes(t);
        } else {
-               finish_copy_notes_for_rewrite(c);
+               finish_copy_notes_for_rewrite(c, msg);
        }
        return ret;
 }
index f069462cb03bbae46dae8d6b97420b3726690c47..dd117b379ac9d7b58b439a691b90c6cb4f1dac90 100644 (file)
@@ -2378,7 +2378,7 @@ static void get_object_list(int ac, const char **av)
 
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
-       mark_edges_uninteresting(revs.commits, &revs, show_edge);
+       mark_edges_uninteresting(&revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object, NULL);
 
        if (keep_unreachable)
index b5a0f88eb8c5641b41bfe95bd342fb180e7fadad..b20b1ec4c117f7529a1a3f2a8151fa0297dffb6a 100644 (file)
@@ -1,6 +1,6 @@
 #include "builtin.h"
 #include "parse-options.h"
-#include "pack-refs.h"
+#include "refs.h"
 
 static char const * const pack_refs_usage[] = {
        N_("git pack-refs [options]"),
index 83382c1fe13e1f99aad4736993c08877c989a748..fa6ce42f44f82d34299f2dc0109be4d831cb3d48 100644 (file)
@@ -8,9 +8,6 @@ static const char * const prune_packed_usage[] = {
        NULL
 };
 
-#define DRY_RUN 01
-#define VERBOSE 02
-
 static struct progress *progress;
 
 static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
@@ -29,7 +26,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
                if (!has_sha1_pack(sha1))
                        continue;
                memcpy(pathname + len, de->d_name, 38);
-               if (opts & DRY_RUN)
+               if (opts & PRUNE_PACKED_DRY_RUN)
                        printf("rm -f %s\n", pathname);
                else
                        unlink_or_warn(pathname);
@@ -44,7 +41,7 @@ void prune_packed_objects(int opts)
        const char *dir = get_object_directory();
        int len = strlen(dir);
 
-       if (opts == VERBOSE)
+       if (opts & PRUNE_PACKED_VERBOSE)
                progress = start_progress_delay("Removing duplicate objects",
                        256, 95, 2);
 
@@ -71,10 +68,12 @@ void prune_packed_objects(int opts)
 
 int cmd_prune_packed(int argc, const char **argv, const char *prefix)
 {
-       int opts = isatty(2) ? VERBOSE : 0;
+       int opts = isatty(2) ? PRUNE_PACKED_VERBOSE : 0;
        const struct option prune_packed_options[] = {
-               OPT_BIT('n', "dry-run", &opts, N_("dry run"), DRY_RUN),
-               OPT_NEGBIT('q', "quiet", &opts, N_("be quiet"), VERBOSE),
+               OPT_BIT('n', "dry-run", &opts, N_("dry run"),
+                       PRUNE_PACKED_DRY_RUN),
+               OPT_NEGBIT('q', "quiet", &opts, N_("be quiet"),
+                          PRUNE_PACKED_VERBOSE),
                OPT_END()
        };
 
index 85843d4f1728a907aafab9938b4c827990a7695f..6366917c6de55e171a7f697b22f1de10293addf5 100644 (file)
@@ -132,8 +132,8 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
                OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
                OPT__VERBOSE(&verbose, N_("report pruned objects")),
                OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
-               OPT_DATE(0, "expire", &expire,
-                        N_("expire objects older than <time>")),
+               OPT_EXPIRY_DATE(0, "expire", &expire,
+                               N_("expire objects older than <time>")),
                OPT_END()
        };
        char *s;
@@ -165,7 +165,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        stop_progress(&progress);
        prune_object_dir(get_object_directory());
 
-       prune_packed_objects(show_only);
+       prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0);
        remove_temporary_files(get_object_directory());
        s = mkpathdup("%s/pack", get_object_directory());
        remove_temporary_files(s);
index 909c34dfda984be86fba2013fedc3fdb10b5e2f1..04f0eaf179044dce1e87137978bcbecca18f3527 100644 (file)
@@ -92,7 +92,7 @@ static NORETURN int die_push_simple(struct branch *branch, struct remote *remote
        if (!short_upstream)
                short_upstream = branch->merge[0]->src;
        /*
-        * Don't show advice for people who explicitely set
+        * Don't show advice for people who explicitly set
         * push.default.
         */
        if (push_default == PUSH_DEFAULT_UNSPECIFIED)
@@ -113,17 +113,20 @@ static NORETURN int die_push_simple(struct branch *branch, struct remote *remote
            remote->name, branch->name, advice_maybe);
 }
 
-static void setup_push_upstream(struct remote *remote, int simple)
+static const char message_detached_head_die[] =
+       N_("You are not currently on a branch.\n"
+          "To push the history leading to the current (detached HEAD)\n"
+          "state now, use\n"
+          "\n"
+          "    git push %s HEAD:<name-of-remote-branch>\n");
+
+static void setup_push_upstream(struct remote *remote, struct branch *branch,
+                               int triangular)
 {
        struct strbuf refspec = STRBUF_INIT;
-       struct branch *branch = branch_get(NULL);
+
        if (!branch)
-               die(_("You are not currently on a branch.\n"
-                   "To push the history leading to the current (detached HEAD)\n"
-                   "state now, use\n"
-                   "\n"
-                   "    git push %s HEAD:<name-of-remote-branch>\n"),
-                   remote->name);
+               die(_(message_detached_head_die), remote->name);
        if (!branch->merge_nr || !branch->merge || !branch->remote_name)
                die(_("The current branch %s has no upstream branch.\n"
                    "To push the current branch and set the remote as upstream, use\n"
@@ -135,18 +138,29 @@ static void setup_push_upstream(struct remote *remote, int simple)
        if (branch->merge_nr != 1)
                die(_("The current branch %s has multiple upstream branches, "
                    "refusing to push."), branch->name);
-       if (strcmp(branch->remote_name, remote->name))
+       if (triangular)
                die(_("You are pushing to remote '%s', which is not the upstream of\n"
                      "your current branch '%s', without telling me what to push\n"
                      "to update which remote branch."),
                    remote->name, branch->name);
-       if (simple && strcmp(branch->refname, branch->merge[0]->src))
-               die_push_simple(branch, remote);
+
+       if (push_default == PUSH_DEFAULT_SIMPLE) {
+               /* Additional safety */
+               if (strcmp(branch->refname, branch->merge[0]->src))
+                       die_push_simple(branch, remote);
+       }
 
        strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
        add_refspec(refspec.buf);
 }
 
+static void setup_push_current(struct remote *remote, struct branch *branch)
+{
+       if (!branch)
+               die(_(message_detached_head_die), remote->name);
+       add_refspec(branch->name);
+}
+
 static char warn_unspecified_push_default_msg[] =
 N_("push.default is unset; its implicit value is changing in\n"
    "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -171,8 +185,17 @@ static void warn_unspecified_push_default_configuration(void)
        warning("%s\n", _(warn_unspecified_push_default_msg));
 }
 
+static int is_workflow_triangular(struct remote *remote)
+{
+       struct remote *fetch_remote = remote_get(NULL);
+       return (fetch_remote && fetch_remote != remote);
+}
+
 static void setup_default_push_refspecs(struct remote *remote)
 {
+       struct branch *branch = branch_get(NULL);
+       int triangular = is_workflow_triangular(remote);
+
        switch (push_default) {
        default:
        case PUSH_DEFAULT_UNSPECIFIED:
@@ -184,15 +207,18 @@ static void setup_default_push_refspecs(struct remote *remote)
                break;
 
        case PUSH_DEFAULT_SIMPLE:
-               setup_push_upstream(remote, 1);
+               if (triangular)
+                       setup_push_current(remote, branch);
+               else
+                       setup_push_upstream(remote, branch, triangular);
                break;
 
        case PUSH_DEFAULT_UPSTREAM:
-               setup_push_upstream(remote, 0);
+               setup_push_upstream(remote, branch, triangular);
                break;
 
        case PUSH_DEFAULT_CURRENT:
-               add_refspec("HEAD");
+               setup_push_current(remote, branch);
                break;
 
        case PUSH_DEFAULT_NOTHING:
@@ -204,8 +230,8 @@ static void setup_default_push_refspecs(struct remote *remote)
 
 static const char message_advice_pull_before_push[] =
        N_("Updates were rejected because the tip of your current branch is behind\n"
-          "its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
-          "before pushing again.\n"
+          "its remote counterpart. Integrate the remote changes (e.g.\n"
+          "'git pull ...') before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_use_upstream[] =
@@ -216,15 +242,15 @@ static const char message_advice_use_upstream[] =
 
 static const char message_advice_checkout_pull_push[] =
        N_("Updates were rejected because a pushed branch tip is behind its remote\n"
-          "counterpart. Check out this branch and merge the remote changes\n"
-          "(e.g. 'git pull') before pushing again.\n"
+          "counterpart. Check out this branch and integrate the remote changes\n"
+          "(e.g. 'git pull ...') before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_fetch_first[] =
        N_("Updates were rejected because the remote contains work that you do\n"
           "not have locally. This is usually caused by another repository pushing\n"
-          "to the same ref. You may want to first merge the remote changes (e.g.,\n"
-          "'git pull') before pushing again.\n"
+          "to the same ref. You may want to first integrate the remote changes\n"
+          "(e.g., 'git pull ...') before pushing again.\n"
           "See the 'Note about fast-forwards' in 'git push --help' for details.");
 
 static const char message_advice_ref_already_exists[] =
index 042ac1b84f22257d1a8a4d32d34124b6dc372f16..0f5d7fe23f5b1e15e24e93daed68ece7542cf104 100644 (file)
@@ -66,7 +66,7 @@ static int exclude_per_directory_cb(const struct option *opt, const char *arg,
        return 0;
 }
 
-static void debug_stage(const char *label, struct cache_entry *ce,
+static void debug_stage(const char *label, const struct cache_entry *ce,
                        struct unpack_trees_options *o)
 {
        printf("%s ", label);
@@ -80,7 +80,8 @@ static void debug_stage(const char *label, struct cache_entry *ce,
                       sha1_to_hex(ce->sha1));
 }
 
-static int debug_merge(struct cache_entry **stages, struct unpack_trees_options *o)
+static int debug_merge(const struct cache_entry * const *stages,
+                      struct unpack_trees_options *o)
 {
        int i;
 
index ccebd74f166f5d02e8e6a0bb6033d741f8580f7b..e3eb5fc0588a79b2b6c29bccdaf71ba49dc14c1f 100644 (file)
@@ -826,8 +826,11 @@ static const char *unpack(int err_fd)
                            : 0);
 
        hdr_err = parse_pack_header(&hdr);
-       if (hdr_err)
+       if (hdr_err) {
+               if (err_fd > 0)
+                       close(err_fd);
                return hdr_err;
+       }
        snprintf(hdr_arg, sizeof(hdr_arg),
                        "--pack_header=%"PRIu32",%"PRIu32,
                        ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
index 72a0af70c3dcd7d108887cd289bc7b3c606226aa..54184b3d133bb66db272fd21ad3cad53a4a9a5cb 100644 (file)
@@ -496,11 +496,9 @@ static int parse_expire_cfg_value(const char *var, const char *value, unsigned l
 {
        if (!value)
                return config_error_nonbool(var);
-       if (!strcmp(value, "never") || !strcmp(value, "false")) {
-               *expire = 0;
-               return 0;
-       }
-       *expire = approxidate(value);
+       if (parse_expiry_date(value, expire))
+               return error(_("%s' for '%s' is not a valid timestamp"),
+                            value, var);
        return 0;
 }
 
@@ -614,11 +612,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
                        cb.dry_run = 1;
                else if (!prefixcmp(arg, "--expire=")) {
-                       cb.expire_total = approxidate(arg + 9);
+                       if (parse_expiry_date(arg + 9, &cb.expire_total))
+                               die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_TOTAL;
                }
                else if (!prefixcmp(arg, "--expire-unreachable=")) {
-                       cb.expire_unreachable = approxidate(arg + 21);
+                       if (parse_expiry_date(arg + 21, &cb.expire_unreachable))
+                               die(_("'%s' is not a valid timestamp"), arg);
                        explicit_expiry |= EXPIRE_UNREACH;
                }
                else if (!strcmp(arg, "--stale-fix"))
index 937484d7c77c448995481f246c1afe2dedd4c561..5e54d367b82cd91dbba70e2570dc620a3a13cd21 100644 (file)
@@ -178,7 +178,7 @@ static int add(int argc, const char **argv)
        argc = parse_options(argc, argv, NULL, options, builtin_remote_add_usage,
                             0);
 
-       if (argc < 2)
+       if (argc != 2)
                usage_with_options(builtin_remote_add_usage, options);
 
        if (mirror && master)
index 398ccd5eaa243045c5d21af1e37462846665568a..59d31152d0068dd4a542d2c8e076c629d508500f 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
  *
- * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com>
+ * Based on builtin/tag.c by Kristian Høgsberg <krh@redhat.com>
  * and Carlos Rica <jasampler@gmail.com> that was itself based on
  * git-tag.sh and mktag.c by Linus Torvalds.
  */
index 6032131a9087281e97d0abc05365b7485004121b..afa6e020e83ebac739db474c278e44bc22079848 100644 (file)
@@ -93,10 +93,12 @@ 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;
 
        hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        printf(_("HEAD is now at %s"), hex);
-       body = strstr(commit->buffer, "\n\n");
+       msg = logmsg_reencode(commit, NULL, get_log_output_encoding());
+       body = strstr(msg, "\n\n");
        if (body) {
                const char *eol;
                size_t len;
@@ -107,6 +109,7 @@ static void print_new_head_line(struct commit *commit)
        }
        else
                printf("\n");
+       logmsg_free(msg, commit);
 }
 
 static void update_index_from_diff(struct diff_queue_struct *q,
index 67701be55193db0fd9733ee7c4962db08072045c..4fc16166374b86927e605ccbfe341aebe0e3240f 100644 (file)
@@ -111,6 +111,7 @@ static void show_commit(struct commit *commit, void *data)
                ctx.date_mode = revs->date_mode;
                ctx.date_mode_explicit = revs->date_mode_explicit;
                ctx.fmt = revs->commit_format;
+               ctx.output_encoding = get_log_output_encoding();
                pretty_print_commit(&ctx, commit, &buf);
                if (revs->graph) {
                        if (buf.len) {
@@ -335,7 +336,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        if (revs.tree_objects)
-               mark_edges_uninteresting(revs.commits, &revs, show_edge);
+               mark_edges_uninteresting(&revs, show_edge);
 
        if (bisect_list) {
                int reaches = reaches, all = all;
index f267a1d3b57c1f1ab44c83a197f46051f4f13060..de894c75773c78afb2c0f1ea4ea5aff8ec7de6bc 100644 (file)
@@ -212,11 +212,17 @@ static void show_datestring(const char *flag, const char *datestr)
        show(buffer);
 }
 
-static int show_file(const char *arg)
+static int show_file(const char *arg, int output_prefix)
 {
        show_default();
        if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
-               show(arg);
+               if (output_prefix) {
+                       const char *prefix = startup_info->prefix;
+                       show(prefix_filename(prefix,
+                                            prefix ? strlen(prefix) : 0,
+                                            arg));
+               } else
+                       show(arg);
                return 1;
        }
        return 0;
@@ -470,6 +476,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n"
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+       int output_prefix = 0;
        unsigned char sha1[20];
        const char *name = NULL;
 
@@ -503,7 +510,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                const char *arg = argv[i];
 
                if (as_is) {
-                       if (show_file(arg) && as_is < 2)
+                       if (show_file(arg, output_prefix) && as_is < 2)
                                verify_filename(prefix, arg, 0);
                        continue;
                }
@@ -527,7 +534,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                as_is = 2;
                                /* Pass on the "--" if we show anything but files.. */
                                if (filter & (DO_FLAGS | DO_REVS))
-                                       show_file(arg);
+                                       show_file(arg, 0);
                                continue;
                        }
                        if (!strcmp(arg, "--default")) {
@@ -535,6 +542,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                i++;
                                continue;
                        }
+                       if (!strcmp(arg, "--prefix")) {
+                               prefix = argv[i+1];
+                               startup_info->prefix = prefix;
+                               output_prefix = 1;
+                               i++;
+                               continue;
+                       }
                        if (!strcmp(arg, "--revs-only")) {
                                filter &= ~DO_NOREV;
                                continue;
@@ -754,7 +768,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                if (verify)
                        die_no_single_rev(quiet);
                as_is = 1;
-               if (!show_file(arg))
+               if (!show_file(arg, output_prefix))
                        continue;
                verify_filename(prefix, arg, 1);
        }
index c5e36b94c080212ec9241146c20ef2e323663bea..1d2648b756907f467edbe1e206535009df4c65e8 100644 (file)
  */
 
 static const char * const revert_usage[] = {
-       N_("git revert [options] <commit-ish>"),
+       N_("git revert [options] <commit-ish>..."),
        N_("git revert <subcommand>"),
        NULL
 };
 
 static const char * const cherry_pick_usage[] = {
-       N_("git cherry-pick [options] <commit-ish>"),
+       N_("git cherry-pick [options] <commit-ish>..."),
        N_("git cherry-pick <subcommand>"),
        NULL
 };
@@ -54,6 +54,7 @@ static int option_parse_x(const struct option *opt,
        return 0;
 }
 
+LAST_ARG_MUST_BE_NULL
 static void verify_opt_compatible(const char *me, const char *base_opt, ...)
 {
        const char *this_opt;
@@ -70,6 +71,7 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
                die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
 }
 
+LAST_ARG_MUST_BE_NULL
 static void verify_opt_mutually_compatible(const char *me, ...)
 {
        const char *opt1, *opt2 = NULL;
index 7b91d52f39004dbfcc9b0dc5b8aadf3df5c6d320..0df0b4d942c09769ff42539f6aaf83dcc31e20fb 100644 (file)
@@ -9,6 +9,7 @@
 #include "cache-tree.h"
 #include "tree-walk.h"
 #include "parse-options.h"
+#include "string-list.h"
 #include "submodule.h"
 
 static const char * const builtin_rm_usage[] = {
@@ -36,15 +37,52 @@ static int get_ours_cache_pos(const char *path, int pos)
        return -1;
 }
 
+static void print_error_files(struct string_list *files_list,
+                             const char *main_msg,
+                             const char *hints_msg,
+                             int *errs)
+{
+       if (files_list->nr) {
+               int i;
+               struct strbuf err_msg = STRBUF_INIT;
+
+               strbuf_addstr(&err_msg, main_msg);
+               for (i = 0; i < files_list->nr; i++)
+                       strbuf_addf(&err_msg,
+                                   "\n    %s",
+                                   files_list->items[i].string);
+               if (advice_rm_hints)
+                       strbuf_addstr(&err_msg, hints_msg);
+               *errs = error("%s", err_msg.buf);
+               strbuf_release(&err_msg);
+       }
+}
+
+static void error_removing_concrete_submodules(struct string_list *files, int *errs)
+{
+       print_error_files(files,
+                         Q_("the following submodule (or one of its nested "
+                            "submodules)\n"
+                            "uses a .git directory:",
+                            "the following submodules (or one of its nested "
+                            "submodules)\n"
+                            "use a .git directory:", files->nr),
+                         _("\n(use 'rm -rf' if you really want to remove "
+                           "it including all of its history)"),
+                         errs);
+       string_list_clear(files, 0);
+}
+
 static int check_submodules_use_gitfiles(void)
 {
        int i;
        int errs = 0;
+       struct string_list files = STRING_LIST_INIT_NODUP;
 
        for (i = 0; i < list.nr; i++) {
                const char *name = list.entry[i].name;
                int pos;
-               struct cache_entry *ce;
+               const struct cache_entry *ce;
                struct stat st;
 
                pos = cache_name_pos(name, strlen(name));
@@ -61,12 +99,11 @@ static int check_submodules_use_gitfiles(void)
                        continue;
 
                if (!submodule_uses_gitfile(name))
-                       errs = error(_("submodule '%s' (or one of its nested "
-                                    "submodules) uses a .git directory\n"
-                                    "(use 'rm -rf' if you really want to remove "
-                                    "it including all of its history)"), name);
+                       string_list_append(&files, name);
        }
 
+       error_removing_concrete_submodules(&files, &errs);
+
        return errs;
 }
 
@@ -81,12 +118,16 @@ static int check_local_mod(unsigned char *head, int index_only)
         */
        int i, no_head;
        int errs = 0;
+       struct string_list files_staged = STRING_LIST_INIT_NODUP;
+       struct string_list files_cached = STRING_LIST_INIT_NODUP;
+       struct string_list files_submodule = STRING_LIST_INIT_NODUP;
+       struct string_list files_local = STRING_LIST_INIT_NODUP;
 
        no_head = is_null_sha1(head);
        for (i = 0; i < list.nr; i++) {
                struct stat st;
                int pos;
-               struct cache_entry *ce;
+               const struct cache_entry *ce;
                const char *name = list.entry[i].name;
                unsigned char sha1[20];
                unsigned mode;
@@ -171,29 +212,50 @@ static int check_local_mod(unsigned char *head, int index_only)
                 */
                if (local_changes && staged_changes) {
                        if (!index_only || !(ce->ce_flags & CE_INTENT_TO_ADD))
-                               errs = error(_("'%s' has staged content different "
-                                            "from both the file and the HEAD\n"
-                                            "(use -f to force removal)"), name);
+                               string_list_append(&files_staged, name);
                }
                else if (!index_only) {
                        if (staged_changes)
-                               errs = error(_("'%s' has changes staged in the index\n"
-                                            "(use --cached to keep the file, "
-                                            "or -f to force removal)"), name);
+                               string_list_append(&files_cached, name);
                        if (local_changes) {
                                if (S_ISGITLINK(ce->ce_mode) &&
-                                   !submodule_uses_gitfile(name)) {
-                                       errs = error(_("submodule '%s' (or one of its nested "
-                                                    "submodules) uses a .git directory\n"
-                                                    "(use 'rm -rf' if you really want to remove "
-                                                    "it including all of its history)"), name);
-                               } else
-                                       errs = error(_("'%s' has local modifications\n"
-                                                    "(use --cached to keep the file, "
-                                                    "or -f to force removal)"), name);
+                                   !submodule_uses_gitfile(name))
+                                       string_list_append(&files_submodule, name);
+                               else
+                                       string_list_append(&files_local, name);
                        }
                }
        }
+       print_error_files(&files_staged,
+                         Q_("the following file has staged content different "
+                            "from both the\nfile and the HEAD:",
+                            "the following files have staged content different"
+                            " from both the\nfile and the HEAD:",
+                            files_staged.nr),
+                         _("\n(use -f to force removal)"),
+                         &errs);
+       string_list_clear(&files_staged, 0);
+       print_error_files(&files_cached,
+                         Q_("the following file has changes "
+                            "staged in the index:",
+                            "the following files have changes "
+                            "staged in the index:", files_cached.nr),
+                         _("\n(use --cached to keep the file,"
+                           " or -f to force removal)"),
+                         &errs);
+       string_list_clear(&files_cached, 0);
+
+       error_removing_concrete_submodules(&files_submodule, &errs);
+
+       print_error_files(&files_local,
+                         Q_("the following file has local modifications:",
+                            "the following files have local modifications:",
+                            files_local.nr),
+                         _("\n(use --cached to keep the file,"
+                           " or -f to force removal)"),
+                         &errs);
+       string_list_clear(&files_local, 0);
+
        return errs;
 }
 
@@ -258,7 +320,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        seen = xcalloc(i, 1);
 
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
                        continue;
                ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
index 240bff3efa80b3af087fe1e6c16a75108ffee4de..1434f8fee487751abe069250c611bdaed2fa5857 100644 (file)
@@ -10,9 +10,7 @@
 #include "parse-options.h"
 
 static char const * const shortlog_usage[] = {
-       N_("git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"),
-       "",
-       N_("[rev-opts] are documented in git-rev-list(1)"),
+       N_("git shortlog [<options>] [<revision range>] [[--] [<path>...]]"),
        NULL
 };
 
@@ -139,6 +137,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                ctx.subject = "";
                ctx.after_subject = "";
                ctx.date_mode = DATE_NORMAL;
+               ctx.output_encoding = get_log_output_encoding();
                pretty_print_commit(&ctx, commit, &ufbuf);
                buffer = ufbuf.buf;
        } else if (*buffer) {
index 90fc6b1b9d2a397ae43dfc4c0aba6bc192c9823c..9788eb115beb2ab5663bd1d318ce411099589321 100644 (file)
@@ -630,7 +630,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        int num_rev, i, extra = 0;
        int all_heads = 0, all_remotes = 0;
        int all_mask, all_revs;
-       int lifo = 1;
+       enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER;
        char head[128];
        const char *head_p;
        int head_len;
@@ -665,15 +665,17 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                            N_("show possible merge bases")),
                OPT_BOOLEAN(0, "independent", &independent,
                            N_("show refs unreachable from any other ref")),
-               OPT_BOOLEAN(0, "topo-order", &lifo,
-                           N_("show commits in topological order")),
+               OPT_SET_INT(0, "topo-order", &sort_order,
+                           N_("show commits in topological order"),
+                           REV_SORT_IN_GRAPH_ORDER),
                OPT_BOOLEAN(0, "topics", &topics,
                            N_("show only commits not on the first branch")),
                OPT_SET_INT(0, "sparse", &dense,
                            N_("show merges reachable from only one tip"), 0),
-               OPT_SET_INT(0, "date-order", &lifo,
-                           N_("show commits where no parent comes before its "
-                              "children"), 0),
+               OPT_SET_INT(0, "date-order", &sort_order,
+                           N_("topologically sort, maintaining date order "
+                              "where possible"),
+                           REV_SORT_BY_COMMIT_DATE),
                { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"),
                            N_("show <n> most recent ref-log entries starting at "
                               "base"),
@@ -900,7 +902,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                exit(0);
 
        /* Sort topologically */
-       sort_in_topological_order(&seen, lifo);
+       sort_in_topological_order(&seen, sort_order);
 
        /* Give names to commits */
        if (!sha1_name && !no_name)
index 8d9b76a02fd115c0285216c27c5232f345d0582d..87806ad5b088f5c905757a4fab3c3499ec20a308 100644 (file)
@@ -31,6 +31,9 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
        const char *hex;
        unsigned char peeled[20];
 
+       if (show_head && !strcmp(refname, "HEAD"))
+               goto match;
+
        if (tags_only || heads_only) {
                int match;
 
@@ -103,7 +106,7 @@ static int add_existing(const char *refname, const unsigned char *sha1, int flag
  */
 static int exclude_existing(const char *match)
 {
-       static struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+       static struct string_list existing_refs = STRING_LIST_INIT_DUP;
        char buf[1024];
        int matchlen = match ? strlen(match) : 0;
 
@@ -167,9 +170,10 @@ static const struct option show_ref_options[] = {
        OPT_BOOLEAN(0, "verify", &verify, N_("stricter reference checking, "
                    "requires exact ref path")),
        { OPTION_BOOLEAN, 'h', NULL, &show_head, NULL,
-         N_("show the HEAD reference"),
+         N_("show the HEAD reference, even if it would be filtered out"),
          PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-       OPT_BOOLEAN(0, "head", &show_head, N_("show the HEAD reference")),
+       OPT_BOOLEAN(0, "head", &show_head,
+         N_("show the HEAD reference, even if it would be filtered out")),
        OPT_BOOLEAN('d', "dereference", &deref_tags,
                    N_("dereference tags into object IDs")),
        { OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"),
index 5c7762eef404e7ea21d88991a81407f1cb99f306..c3179815164c1ba2b39f3f57690c607dfc3ea0b6 100644 (file)
@@ -83,7 +83,7 @@ static int process_lstat_error(const char *path, int err)
        return error("lstat(\"%s\"): %s", path, strerror(errno));
 }
 
-static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
+static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
 {
        int option, size;
        struct cache_entry *ce;
@@ -142,7 +142,7 @@ static int process_directory(const char *path, int len, struct stat *st)
 
        /* Exact match: file or existing gitlink */
        if (pos >= 0) {
-               struct cache_entry *ce = active_cache[pos];
+               const struct cache_entry *ce = active_cache[pos];
                if (S_ISGITLINK(ce->ce_mode)) {
 
                        /* Do nothing to the index if there is no HEAD! */
@@ -158,7 +158,7 @@ static int process_directory(const char *path, int len, struct stat *st)
        /* Inexact match: is there perhaps a subdirectory match? */
        pos = -pos-1;
        while (pos < active_nr) {
-               struct cache_entry *ce = active_cache[pos++];
+               const struct cache_entry *ce = active_cache[pos++];
 
                if (strncmp(ce->name, path, len))
                        break;
@@ -183,7 +183,7 @@ static int process_path(const char *path)
 {
        int pos, len;
        struct stat st;
-       struct cache_entry *ce;
+       const struct cache_entry *ce;
 
        len = strlen(path);
        if (has_symlink_leading_path(path, len))
@@ -448,7 +448,7 @@ static int unresolve_one(const char *path)
                /* already merged */
                pos = unmerge_cache_entry_at(pos);
                if (pos < active_nr) {
-                       struct cache_entry *ce = active_cache[pos];
+                       const struct cache_entry *ce = active_cache[pos];
                        if (ce_stage(ce) &&
                            ce_namelen(ce) == namelen &&
                            !memcmp(ce->name, path, namelen))
@@ -462,7 +462,7 @@ static int unresolve_one(const char *path)
                 */
                pos = -pos-1;
                if (pos < active_nr) {
-                       struct cache_entry *ce = active_cache[pos];
+                       const struct cache_entry *ce = active_cache[pos];
                        if (ce_namelen(ce) == namelen &&
                            !memcmp(ce->name, path, namelen)) {
                                fprintf(stderr,
@@ -558,7 +558,7 @@ static int do_reupdate(int ac, const char **av,
                has_head = 0;
  redo:
        for (pos = 0; pos < active_nr; pos++) {
-               struct cache_entry *ce = active_cache[pos];
+               const struct cache_entry *ce = active_cache[pos];
                struct cache_entry *old = NULL;
                int save_nr;
 
index 6b0b6d490440cc59f8ef451c5da9bcff5b3e1563..118c62528b0bb6c919d146d60516825a5336fe75 100644 (file)
@@ -114,7 +114,7 @@ static int stream_to_pack(struct bulk_checkin_state *state,
 
                if (size && !s.avail_in) {
                        ssize_t rsize = size < sizeof(ibuf) ? size : sizeof(ibuf);
-                       if (xread(fd, ibuf, rsize) != rsize)
+                       if (read_in_full(fd, ibuf, rsize) != rsize)
                                die("failed to read %d bytes from '%s'",
                                    (int)rsize, path);
                        offset += rsize;
index 4b0e5cd51b82f916c26d3776f81f22cdb3045fa6..3d6431137336f9748247dfad625555d0a546b26b 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -281,7 +281,7 @@ int create_bundle(struct bundle_header *header, const char *path,
                        if (!get_sha1_hex(buf.buf + 1, sha1)) {
                                struct object *object = parse_object_or_die(sha1, buf.buf);
                                object->flags |= UNINTERESTING;
-                               add_pending_object(&revs, object, xstrdup(buf.buf));
+                               add_pending_object(&revs, object, buf.buf);
                        }
                } else if (!get_sha1_hex(buf.buf, sha1)) {
                        struct object *object = parse_object_or_die(sha1, buf.buf);
index 37e4d008b59420338ce97f1b2cf2eb660dab8a34..0bbec432165874bd36b1f220301de7f1363626dc 100644 (file)
@@ -149,7 +149,7 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
                cache_tree_invalidate_path(down->cache_tree, slash + 1);
 }
 
-static int verify_cache(struct cache_entry **cache,
+static int verify_cache(const struct cache_entry * const *cache,
                        int entries, int flags)
 {
        int i, funny;
@@ -158,7 +158,7 @@ static int verify_cache(struct cache_entry **cache,
        /* Verify that the tree is merged */
        funny = 0;
        for (i = 0; i < entries; i++) {
-               struct cache_entry *ce = cache[i];
+               const struct cache_entry *ce = cache[i];
                if (ce_stage(ce)) {
                        if (silent)
                                return -1;
@@ -234,7 +234,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
 }
 
 static int update_one(struct cache_tree *it,
-                     struct cache_entry **cache,
+                     const struct cache_entry * const *cache,
                      int entries,
                      const char *base,
                      int baselen,
@@ -265,7 +265,7 @@ static int update_one(struct cache_tree *it,
         */
        i = 0;
        while (i < entries) {
-               struct cache_entry *ce = cache[i];
+               const struct cache_entry *ce = cache[i];
                struct cache_tree_sub *sub;
                const char *path, *slash;
                int pathlen, sublen, subcnt, subskip;
@@ -312,7 +312,7 @@ static int update_one(struct cache_tree *it,
 
        i = 0;
        while (i < entries) {
-               struct cache_entry *ce = cache[i];
+               const struct cache_entry *ce = cache[i];
                struct cache_tree_sub *sub;
                const char *path, *slash;
                int pathlen, entlen;
@@ -397,7 +397,7 @@ static int update_one(struct cache_tree *it,
 }
 
 int cache_tree_update(struct cache_tree *it,
-                     struct cache_entry **cache,
+                     const struct cache_entry * const *cache,
                      int entries,
                      int flags)
 {
@@ -599,8 +599,8 @@ 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,
-                                     active_cache, active_nr,
-                                     flags) < 0)
+                                     (const struct cache_entry * const *)active_cache,
+                                     active_nr, flags) < 0)
                        return WRITE_TREE_UNMERGED_INDEX;
                if (0 <= newfd) {
                        if (!write_cache(newfd, active_cache, active_nr) &&
@@ -701,5 +701,6 @@ 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,
-                                the_index.cache, the_index.cache_nr, flags);
+                                (const struct cache_entry * const *)the_index.cache,
+                                the_index.cache_nr, flags);
 }
index 55d0f59f2b7a42b40fe4be32297b56656206983d..f1923ad1e9ddd3ad9b77a436e66ad083843094a0 100644 (file)
@@ -30,7 +30,7 @@ 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 *, struct cache_entry **, int, int);
+int cache_tree_update(struct cache_tree *, const struct cache_entry * const *, int, int);
 
 int update_main_cache_tree(int);
 
diff --git a/cache.h b/cache.h
index ba5a47c480c4f6a69aaf8ffc5f302af9ee3ab5a0..85b544f38d934fe68d1e155c86337f1400eea14d 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -119,15 +119,19 @@ struct cache_time {
        unsigned int nsec;
 };
 
+struct stat_data {
+       struct cache_time sd_ctime;
+       struct cache_time sd_mtime;
+       unsigned int sd_dev;
+       unsigned int sd_ino;
+       unsigned int sd_uid;
+       unsigned int sd_gid;
+       unsigned int sd_size;
+};
+
 struct cache_entry {
-       struct cache_time ce_ctime;
-       struct cache_time ce_mtime;
-       unsigned int ce_dev;
-       unsigned int ce_ino;
+       struct stat_data ce_stat_data;
        unsigned int ce_mode;
-       unsigned int ce_uid;
-       unsigned int ce_gid;
-       unsigned int ce_size;
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned char sha1[20];
@@ -190,7 +194,8 @@ struct cache_entry {
  * another. But we never change the name, or the hash state!
  */
 #define CE_STATE_MASK (CE_HASHED | CE_UNHASHED)
-static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry *src)
+static inline void copy_cache_entry(struct cache_entry *dst,
+                                   const struct cache_entry *src)
 {
        unsigned int state = dst->ce_flags & CE_STATE_MASK;
 
@@ -222,7 +227,8 @@ static inline unsigned int create_ce_mode(unsigned int mode)
                return S_IFGITLINK;
        return S_IFREG | ce_permissions(mode);
 }
-static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
+static inline unsigned int ce_mode_from_stat(const struct cache_entry *ce,
+                                            unsigned int mode)
 {
        extern int trust_executable_bit, has_symlinks;
        if (!has_symlinks && S_ISREG(mode) &&
@@ -311,6 +317,7 @@ extern void free_name_hash(struct index_state *istate);
 #define resolve_undo_clear() resolve_undo_clear_index(&the_index)
 #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
 #define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
+#define read_blob_data_from_cache(path, sz) read_blob_data_from_index(&the_index, (path), (sz))
 #endif
 
 enum object_type {
@@ -418,6 +425,8 @@ extern int path_inside_repo(const char *prefix, const char *path);
 extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
 extern int init_db(const char *template_dir, unsigned int flags);
 
+extern void sanitize_stdfds(void);
+
 #define alloc_nr(x) (((x)+16)*3/2)
 
 /*
@@ -469,8 +478,9 @@ extern int remove_file_from_index(struct index_state *, const char *path);
 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
-extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
+extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
 extern int index_name_is_other(const struct index_state *, const char *, int);
+extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
 
 /* do stat comparison even if CE_VALID is true */
 #define CE_MATCH_IGNORE_VALID          01
@@ -478,10 +488,10 @@ extern int index_name_is_other(const struct index_state *, const char *, int);
 #define CE_MATCH_RACY_IS_DIRTY         02
 /* do stat comparison even if CE_SKIP_WORKTREE is true */
 #define CE_MATCH_IGNORE_SKIP_WORKTREE  04
-extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
-extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
+extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 
-#define PATHSPEC_ONESTAR 1     /* the pathspec pattern sastisfies GFNM_ONESTAR */
+#define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
 
 struct pathspec {
        const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
@@ -507,6 +517,21 @@ extern int limit_pathspec_to_literal(void);
 #define HASH_FORMAT_CHECK 2
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
+
+/*
+ * Record to sd the data from st that we use to check whether a file
+ * might have changed.
+ */
+extern void fill_stat_data(struct stat_data *sd, struct stat *st);
+
+/*
+ * Return 0 if st is consistent with a file not having been changed
+ * since sd was filled.  If there are differences, return a
+ * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
+ * INODE_CHANGED, and DATA_CHANGED.
+ */
+extern int match_stat_data(const struct stat_data *sd, struct stat *st);
+
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
 #define REFRESH_REALLY         0x0001  /* ignore_valid */
@@ -552,6 +577,7 @@ extern int assume_unchanged;
 extern int prefer_symlink_refs;
 extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
+extern int warn_on_object_refname_ambiguity;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
 extern const char *apply_default_ignorewhitespace;
@@ -735,7 +761,7 @@ int is_directory(const char *);
 const char *real_path(const char *path);
 const char *real_path_if_valid(const char *path);
 const char *absolute_path(const char *path);
-const char *relative_path(const char *abs, const char *base);
+const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
 int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
@@ -770,9 +796,6 @@ extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
 /* global flag to enable extra checks when accessing packed objects */
 extern int do_check_packed_object_crc;
 
-/* for development: log offset of pack access */
-extern const char *log_pack_access;
-
 extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 
 extern int move_temp_to_file(const char *tmpfile, const char *filename);
@@ -908,6 +931,7 @@ void show_date_relative(unsigned long time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
 int parse_date(const char *date, char *buf, int bufsize);
 int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
+int parse_expiry_date(const char *date, unsigned long *timestamp);
 void datestamp(char *buf, int bufsize);
 #define approxidate(s) approxidate_careful((s), NULL)
 unsigned long approxidate_careful(const char *, int *);
@@ -1022,9 +1046,21 @@ struct ref {
        unsigned int
                force:1,
                forced_update:1,
-               merge:1,
                deletion:1,
                matched:1;
+
+       /*
+        * Order is important here, as we write to FETCH_HEAD
+        * in numeric order. And the default NOT_FOR_MERGE
+        * should be 0, so that xcalloc'd structures get it
+        * by default.
+        */
+       enum {
+               FETCH_HEAD_MERGE = -1,
+               FETCH_HEAD_NOT_FOR_MERGE = 0,
+               FETCH_HEAD_IGNORE = 1
+       } fetch_head_status;
+
        enum {
                REF_STATUS_NONE = 0,
                REF_STATUS_OK,
@@ -1096,7 +1132,9 @@ extern int unpack_object_header(struct packed_git *, struct pack_window **, off_
 
 struct object_info {
        /* Request */
+       enum object_type *typep;
        unsigned long *sizep;
+       unsigned long *disk_sizep;
 
        /* Response */
        enum {
@@ -1140,11 +1178,15 @@ extern int update_server_info(int);
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
+extern int git_config_from_buf(config_fn_t fn, const char *name,
+                              const char *buf, size_t len, void *data);
 extern void git_config_push_parameter(const char *text);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
 extern int git_config(config_fn_t fn, void *);
 extern int git_config_with_options(config_fn_t fn, void *,
-                                  const char *filename, int respect_includes);
+                                  const char *filename,
+                                  const char *blob_ref,
+                                  int respect_includes);
 extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_config_int(const char *, const char *);
@@ -1324,4 +1366,31 @@ int checkout_fast_forward(const unsigned char *from,
 
 int sane_execvp(const char *file, char *const argv[]);
 
+/*
+ * A struct to encapsulate the concept of whether a file has changed
+ * since we last checked it. This uses criteria similar to those used
+ * for the index.
+ */
+struct stat_validity {
+       struct stat_data *sd;
+};
+
+void stat_validity_clear(struct stat_validity *sv);
+
+/*
+ * Returns 1 if the path is a regular file (or a symlink to a regular
+ * file) and matches the saved stat_validity, 0 otherwise.  A missing
+ * or inaccessible file is considered a match if the struct was just
+ * initialized, or if the previous update found an inaccessible file.
+ */
+int stat_validity_check(struct stat_validity *sv, const char *path);
+
+/*
+ * Update the stat_validity from a file opened at descriptor fd. If
+ * the file is missing, inaccessible, or not a regular file, then
+ * future calls to stat_validity_check will match iff one of those
+ * conditions continues to be true.
+ */
+void stat_validity_update(struct stat_validity *sv, int fd);
+
 #endif /* CACHE_H */
diff --git a/color.c b/color.c
index e8e26818b3b1f2ffce1374e2edf88b40c575c3dd..f672885b71acef4108c8cefc9cf887c220f614d3 100644 (file)
--- a/color.c
+++ b/color.c
@@ -1,7 +1,7 @@
 #include "cache.h"
 #include "color.h"
 
-static int git_use_color_default = 0;
+static int git_use_color_default = GIT_COLOR_AUTO;
 int color_stdout_is_tty = -1;
 
 /*
index 77d7872aafe659045e9ec228de97e87c9cea00a1..4fc16ad4f35c558daf5bf96865801828d10b4b9b 100644 (file)
@@ -10,6 +10,7 @@
 #include "refs.h"
 #include "userdiff.h"
 #include "sha1-array.h"
+#include "revision.h"
 
 static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
 {
@@ -165,7 +166,7 @@ static struct lline *coalesce_lines(struct lline *base, int *lenbase,
 
        /*
         * Coalesce new lines into base by finding the LCS
-        * - Create the table to run dynamic programing
+        * - Create the table to run dynamic programming
         * - Compute the LCS
         * - Then reverse read the direction structure:
         *   - If we have MATCH, assign parent to base flag, and consume
@@ -518,8 +519,11 @@ static int give_context(struct sline *sline, unsigned long cnt, int num_parent)
                unsigned long k;
 
                /* Paint a few lines before the first interesting line. */
-               while (j < i)
-                       sline[j++].flag |= mark | no_pre_delete;
+               while (j < i) {
+                       if (!(sline[j].flag & mark))
+                               sline[j].flag |= no_pre_delete;
+                       sline[j++].flag |= mark;
+               }
 
        again:
                /* we know up to i is to be included.  where does the
@@ -1302,6 +1306,7 @@ void diff_tree_combined(const unsigned char *sha1,
        int i, num_paths, needsep, show_log_first, num_parent = parents->nr;
 
        diffopts = *opt;
+       diff_tree_setup_paths(diffopts.pathspec.raw, &diffopts);
        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
        DIFF_OPT_SET(&diffopts, RECURSIVE);
        DIFF_OPT_CLR(&diffopts, ALLOW_EXTERNAL);
@@ -1372,12 +1377,14 @@ void diff_tree_combined(const unsigned char *sha1,
                paths = paths->next;
                free(tmp);
        }
+
+       diff_tree_release_paths(&diffopts);
 }
 
 void diff_tree_combined_merge(const struct commit *commit, int dense,
                              struct rev_info *rev)
 {
-       struct commit_list *parent = commit->parents;
+       struct commit_list *parent = get_saved_parents(rev, commit);
        struct sha1_array parents = SHA1_ARRAY_INIT;
 
        while (parent) {
index bf83303c48dc5cfbccfc4a31b347dcdec197d7d4..08b04e2c66429a05f6fc5ac1f5b2aa94160a5c37 100644 (file)
@@ -13,6 +13,7 @@ git-bundle                              mainporcelain
 git-cat-file                            plumbinginterrogators
 git-check-attr                          purehelpers
 git-check-ignore                        purehelpers
+git-check-mailmap                       purehelpers
 git-checkout                            mainporcelain common
 git-checkout-index                      plumbingmanipulators
 git-check-ref-format                    purehelpers
diff --git a/commit-slab.h b/commit-slab.h
new file mode 100644 (file)
index 0000000..d4c8286
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef COMMIT_SLAB_H
+#define COMMIT_SLAB_H
+
+/*
+ * define_commit_slab(slabname, elemtype) creates boilerplate code to define
+ * a new struct (struct slabname) that is used to associate a piece of data
+ * of elemtype to commits, and a few functions to use that struct.
+ *
+ * After including this header file, using:
+ *
+ * define_commit_slab(indegee, int);
+ *
+ * will let you call the following functions:
+ *
+ * - int *indegree_at(struct indegree *, struct commit *);
+ *
+ *   This function locates the data associated with the given commit in
+ *   the indegree slab, and returns the pointer to it.
+ *
+ * - void init_indegree(struct indegree *);
+ *   void init_indegree_with_stride(struct indegree *, int);
+ *
+ *   Initializes the indegree slab that associates an array of integers
+ *   to each commit. 'stride' specifies how big each array is.  The slab
+ *   that id initialied by the variant without "_with_stride" associates
+ *   each commit with an array of one integer.
+ */
+
+/* allocate ~512kB at once, allowing for malloc overhead */
+#ifndef COMMIT_SLAB_SIZE
+#define COMMIT_SLAB_SIZE (512*1024-32)
+#endif
+
+#define define_commit_slab(slabname, elemtype)                                 \
+                                                                       \
+struct slabname {                                                      \
+       unsigned slab_size;                                             \
+       unsigned stride;                                                \
+       unsigned slab_count;                                            \
+       elemtype **slab;                                                \
+};                                                                     \
+static int stat_ ##slabname## realloc;                                 \
+                                                                       \
+static void init_ ##slabname## _with_stride(struct slabname *s,                \
+                                           unsigned stride)            \
+{                                                                      \
+       unsigned int elem_size;                                         \
+       if (!stride)                                                    \
+               stride = 1;                                             \
+       s->stride = stride;                                             \
+       elem_size = sizeof(elemtype) * stride;                          \
+       s->slab_size = COMMIT_SLAB_SIZE / elem_size;                    \
+       s->slab_count = 0;                                              \
+       s->slab = NULL;                                                 \
+}                                                                      \
+                                                                       \
+static void init_ ##slabname(struct slabname *s)                       \
+{                                                                      \
+       init_ ##slabname## _with_stride(s, 1);                          \
+}                                                                      \
+                                                                       \
+static void clear_ ##slabname(struct slabname *s)                      \
+{                                                                      \
+       int i;                                                          \
+       for (i = 0; i < s->slab_count; i++)                             \
+               free(s->slab[i]);                                       \
+       s->slab_count = 0;                                              \
+       free(s->slab);                                                  \
+       s->slab = NULL;                                                 \
+}                                                                      \
+                                                                       \
+static elemtype *slabname## _at(struct slabname *s,                    \
+                               const struct commit *c)                 \
+{                                                                      \
+       int nth_slab, nth_slot;                                         \
+                                                                       \
+       nth_slab = c->index / s->slab_size;                             \
+       nth_slot = c->index % s->slab_size;                             \
+                                                                       \
+       if (s->slab_count <= nth_slab) {                                \
+               int i;                                                  \
+               s->slab = xrealloc(s->slab,                             \
+                                  (nth_slab + 1) * sizeof(s->slab));   \
+               stat_ ##slabname## realloc++;                           \
+               for (i = s->slab_count; i <= nth_slab; i++)             \
+                       s->slab[i] = NULL;                              \
+               s->slab_count = nth_slab + 1;                           \
+       }                                                               \
+       if (!s->slab[nth_slab])                                         \
+               s->slab[nth_slab] = xcalloc(s->slab_size,               \
+                                           sizeof(**s->slab) * s->stride);             \
+       return &s->slab[nth_slab][nth_slot * s->stride];                                \
+}                                                                      \
+                                                                       \
+static int stat_ ##slabname## realloc
+
+#endif /* COMMIT_SLAB_H */
index 888e02ae2f65ab566555465e5d88d02bbe52420f..de16a3c0a2d6693173678247ba0755e4d7483a1c 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -8,12 +8,15 @@
 #include "notes.h"
 #include "gpg-interface.h"
 #include "mergesort.h"
+#include "commit-slab.h"
+#include "prio-queue.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
 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,
@@ -58,8 +61,11 @@ 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)
-               return create_object(sha1, OBJ_COMMIT, alloc_commit_node());
+       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);
@@ -371,6 +377,22 @@ unsigned commit_list_count(const struct commit_list *l)
        return c;
 }
 
+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;
+               list = list->next;
+       }
+       return head;
+}
+
 void free_commit_list(struct commit_list *list)
 {
        while (list) {
@@ -506,33 +528,137 @@ struct commit *pop_commit(struct commit_list **stack)
        return item;
 }
 
+/*
+ * Topological sort support
+ */
+
+/* count number of children that have not been emitted */
+define_commit_slab(indegree_slab, int);
+
+/* record author-date for each commit object */
+define_commit_slab(author_date_slab, unsigned long);
+
+static void record_author_date(struct author_date_slab *author_date,
+                              struct commit *commit)
+{
+       const char *buf, *line_end;
+       char *buffer = 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) {
+               line_end = strchrnul(buf, '\n');
+               if (prefixcmp(buf, "author ")) {
+                       if (!line_end[0] || line_end[1] == '\n')
+                               return; /* end of header */
+                       continue;
+               }
+               if (split_ident_line(&ident,
+                                    buf + strlen("author "),
+                                    line_end - (buf + strlen("author "))) ||
+                   !ident.date_begin || !ident.date_end)
+                       goto fail_exit; /* malformed "author" line */
+               break;
+       }
+
+       date = strtoul(ident.date_begin, &date_end, 10);
+       if (date_end != ident.date_end)
+               goto fail_exit; /* malformed date */
+       *(author_date_slab_at(author_date, commit)) = date;
+
+fail_exit:
+       free(buffer);
+}
+
+static int compare_commits_by_author_date(const void *a_, const void *b_,
+                                         void *cb_data)
+{
+       const struct commit *a = a_, *b = b_;
+       struct author_date_slab *author_date = cb_data;
+       unsigned long a_date = *(author_date_slab_at(author_date, a));
+       unsigned long b_date = *(author_date_slab_at(author_date, b));
+
+       /* newer commits with larger date first */
+       if (a_date < b_date)
+               return 1;
+       else if (a_date > b_date)
+               return -1;
+       return 0;
+}
+
+int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
+{
+       const struct commit *a = a_, *b = b_;
+       /* newer commits with larger date first */
+       if (a->date < b->date)
+               return 1;
+       else if (a->date > b->date)
+               return -1;
+       return 0;
+}
+
 /*
  * Performs an in-place topological sort on the list supplied.
  */
-void sort_in_topological_order(struct commit_list ** list, int lifo)
+void sort_in_topological_order(struct commit_list **list, enum rev_sort_order sort_order)
 {
        struct commit_list *next, *orig = *list;
-       struct commit_list *work, **insert;
        struct commit_list **pptr;
+       struct indegree_slab indegree;
+       struct prio_queue queue;
+       struct commit *commit;
+       struct author_date_slab author_date;
 
        if (!orig)
                return;
        *list = NULL;
 
+       init_indegree_slab(&indegree);
+       memset(&queue, '\0', sizeof(queue));
+
+       switch (sort_order) {
+       default: /* REV_SORT_IN_GRAPH_ORDER */
+               queue.compare = NULL;
+               break;
+       case REV_SORT_BY_COMMIT_DATE:
+               queue.compare = compare_commits_by_commit_date;
+               break;
+       case REV_SORT_BY_AUTHOR_DATE:
+               init_author_date_slab(&author_date);
+               queue.compare = compare_commits_by_author_date;
+               queue.cb_data = &author_date;
+               break;
+       }
+
        /* Mark them and clear the indegree */
        for (next = orig; next; next = next->next) {
                struct commit *commit = next->item;
-               commit->indegree = 1;
+               *(indegree_slab_at(&indegree, commit)) = 1;
+               /* also record the author dates, if needed */
+               if (sort_order == REV_SORT_BY_AUTHOR_DATE)
+                       record_author_date(&author_date, commit);
        }
 
        /* update the indegree */
        for (next = orig; next; next = next->next) {
-               struct commit_list * parents = next->item->parents;
+               struct commit_list *parents = next->item->parents;
                while (parents) {
                        struct commit *parent = parents->item;
+                       int *pi = indegree_slab_at(&indegree, parent);
 
-                       if (parent->indegree)
-                               parent->indegree++;
+                       if (*pi)
+                               (*pi)++;
                        parents = parents->next;
                }
        }
@@ -544,34 +670,33 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
         *
         * the tips serve as a starting set for the work queue.
         */
-       work = NULL;
-       insert = &work;
        for (next = orig; next; next = next->next) {
                struct commit *commit = next->item;
 
-               if (commit->indegree == 1)
-                       insert = &commit_list_insert(commit, insert)->next;
+               if (*(indegree_slab_at(&indegree, commit)) == 1)
+                       prio_queue_put(&queue, commit);
        }
 
-       /* process the list in topological order */
-       if (!lifo)
-               commit_list_sort_by_date(&work);
+       /*
+        * This is unfortunate; the initial tips need to be shown
+        * in the order given from the revision traversal machinery.
+        */
+       if (sort_order == REV_SORT_IN_GRAPH_ORDER)
+               prio_queue_reverse(&queue);
+
+       /* We no longer need the commit list */
+       free_commit_list(orig);
 
        pptr = list;
        *list = NULL;
-       while (work) {
-               struct commit *commit;
-               struct commit_list *parents, *work_item;
-
-               work_item = work;
-               work = work_item->next;
-               work_item->next = NULL;
+       while ((commit = prio_queue_get(&queue)) != NULL) {
+               struct commit_list *parents;
 
-               commit = work_item->item;
                for (parents = commit->parents; parents ; parents = parents->next) {
                        struct commit *parent = parents->item;
+                       int *pi = indegree_slab_at(&indegree, parent);
 
-                       if (!parent->indegree)
+                       if (!*pi)
                                continue;
 
                        /*
@@ -579,21 +704,22 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
                         * when all their children have been emitted thereby
                         * guaranteeing topological order.
                         */
-                       if (--parent->indegree == 1) {
-                               if (!lifo)
-                                       commit_list_insert_by_date(parent, &work);
-                               else
-                                       commit_list_insert(parent, &work);
-                       }
+                       if (--(*pi) == 1)
+                               prio_queue_put(&queue, parent);
                }
                /*
-                * work_item is a commit all of whose children
-                * have already been emitted. we can emit it now.
+                * all children of commit have already been
+                * emitted. we can emit it now.
                 */
-               commit->indegree = 0;
-               *pptr = work_item;
-               pptr = &work_item->next;
+               *(indegree_slab_at(&indegree, commit)) = 0;
+
+               pptr = &commit_list_insert(commit, pptr)->next;
        }
+
+       clear_indegree_slab(&indegree);
+       clear_prio_queue(&queue);
+       if (sort_order == REV_SORT_BY_AUTHOR_DATE)
+               clear_author_date_slab(&author_date);
 }
 
 /* merge-base stuff */
@@ -1240,10 +1366,15 @@ int commit_tree(const struct strbuf *msg, unsigned char *tree,
 static int find_invalid_utf8(const char *buf, int len)
 {
        int offset = 0;
+       static const unsigned int max_codepoint[] = {
+               0x7f, 0x7ff, 0xffff, 0x10ffff
+       };
 
        while (len) {
                unsigned char c = *buf++;
                int bytes, bad_offset;
+               unsigned int codepoint;
+               unsigned int min_val, max_val;
 
                len--;
                offset++;
@@ -1264,24 +1395,48 @@ static int find_invalid_utf8(const char *buf, int len)
                        bytes++;
                }
 
-               /* Must be between 1 and 5 more bytes */
-               if (bytes < 1 || bytes > 5)
+               /*
+                * Must be between 1 and 3 more bytes.  Longer sequences result in
+                * codepoints beyond U+10FFFF, which are guaranteed never to exist.
+                */
+               if (bytes < 1 || 3 < bytes)
                        return bad_offset;
 
                /* Do we *have* that many bytes? */
                if (len < bytes)
                        return bad_offset;
 
+               /*
+                * Place the encoded bits at the bottom of the value and compute the
+                * valid range.
+                */
+               codepoint = (c & 0x7f) >> bytes;
+               min_val = max_codepoint[bytes-1] + 1;
+               max_val = max_codepoint[bytes];
+
                offset += bytes;
                len -= bytes;
 
                /* And verify that they are good continuation bytes */
                do {
+                       codepoint <<= 6;
+                       codepoint |= *buf & 0x3f;
                        if ((*buf++ & 0xc0) != 0x80)
                                return bad_offset;
                } while (--bytes);
 
-               /* We could/should check the value and length here too */
+               /* Reject codepoints that are out of range for the sequence length. */
+               if (codepoint < min_val || codepoint > max_val)
+                       return bad_offset;
+               /* Surrogates are only for UTF-16 and cannot be encoded in UTF-8. */
+               if ((codepoint & 0x1ff800) == 0xd800)
+                       return bad_offset;
+               /* U+xxFFFE and U+xxFFFF are guaranteed non-characters. */
+               if ((codepoint & 0xfffe) == 0xfffe)
+                       return bad_offset;
+               /* So are anything in the range U+FDD0..U+FDEF. */
+               if (codepoint >= 0xfdd0 && codepoint <= 0xfdef)
+                       return bad_offset;
        }
        return -1;
 }
@@ -1291,9 +1446,6 @@ static int find_invalid_utf8(const char *buf, int len)
  *
  * If it isn't, it assumes any non-utf8 characters are Latin1,
  * and does the conversion.
- *
- * Fixme: we should probably also disallow overlong forms and
- * invalid characters. But we don't do that currently.
  */
 static int verify_utf8(struct strbuf *buf)
 {
index 057ff8045efb2cc80f747ee15b2a308a6dead5e5..c308674120112f09ff8a9625e357c62e2029bbba 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -6,6 +6,7 @@
 #include "strbuf.h"
 #include "decorate.h"
 #include "gpg-interface.h"
+#include "string-list.h"
 
 struct commit_list {
        struct commit *item;
@@ -15,7 +16,7 @@ struct commit_list {
 struct commit {
        struct object object;
        void *util;
-       unsigned int indegree;
+       unsigned int index;
        unsigned long date;
        struct commit_list *parents;
        struct tree *tree;
@@ -61,6 +62,9 @@ struct commit_list *commit_list_insert_by_date(struct commit *item,
                                    struct commit_list **list);
 void commit_list_sort_by_date(struct commit_list **list);
 
+/* Shallow copy of the input list */
+struct commit_list *copy_commit_list(struct commit_list *list);
+
 void free_commit_list(struct commit_list *list);
 
 /* Commit formats */
@@ -79,6 +83,9 @@ enum cmit_fmt {
 };
 
 struct pretty_print_context {
+       /*
+        * Callers should tweak these to change the behavior of pp_* functions.
+        */
        enum cmit_fmt fmt;
        int abbrev;
        const char *subject;
@@ -92,6 +99,13 @@ struct pretty_print_context {
        const char *output_encoding;
        struct string_list *mailmap;
        int color;
+       struct ident_split *from_ident;
+
+       /*
+        * Fields below here are manipulated internally by pp_* functions and
+        * should not be counted on by callers.
+        */
+       struct string_list in_body_headers;
 };
 
 struct userformat_want {
@@ -101,6 +115,7 @@ 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 void get_commit_format(const char *arg, struct rev_info *);
@@ -110,20 +125,20 @@ extern void userformat_find_requirements(const char *fmt, struct userformat_want
 extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
                                  const struct pretty_print_context *context);
-extern void pretty_print_commit(const struct pretty_print_context *pp,
+extern void pretty_print_commit(struct pretty_print_context *pp,
                                const struct commit *commit,
                                struct strbuf *sb);
 extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
                           struct strbuf *sb);
-void pp_user_info(const struct pretty_print_context *pp,
+void pp_user_info(struct pretty_print_context *pp,
                  const char *what, struct strbuf *sb,
                  const char *line, const char *encoding);
-void pp_title_line(const struct pretty_print_context *pp,
+void pp_title_line(struct pretty_print_context *pp,
                   const char **msg_p,
                   struct strbuf *sb,
                   const char *encoding,
                   int need_8bit_cte);
-void pp_remainder(const struct pretty_print_context *pp,
+void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                  int indent);
@@ -141,15 +156,24 @@ void clear_commit_marks(struct commit *commit, unsigned int mark);
 void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark);
 void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
 
+
+enum rev_sort_order {
+       REV_SORT_IN_GRAPH_ORDER = 0,
+       REV_SORT_BY_COMMIT_DATE,
+       REV_SORT_BY_AUTHOR_DATE
+};
+
 /*
  * Performs an in-place topological sort of list supplied.
  *
  *   invariant of resulting list is:
  *      a reachable from b => ord(b) < ord(a)
- *   in addition, when lifo == 0, commits on parallel tracks are
- *   sorted in the dates order.
+ *   sort_order further specifies:
+ *   REV_SORT_IN_GRAPH_ORDER: try to show a commit on a single-parent
+ *                            chain together.
+ *   REV_SORT_BY_COMMIT_DATE: show eligible commits in committer-date order.
  */
-void sort_in_topological_order(struct commit_list ** list, int lifo);
+void sort_in_topological_order(struct commit_list **, enum rev_sort_order);
 
 struct commit_graft {
        unsigned char sha1[20];
@@ -175,6 +199,12 @@ extern int for_each_commit_graft(each_commit_graft_fn, void *);
 extern int is_repository_shallow(void);
 extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
+extern void check_shallow_file_for_update(void);
+extern void set_alternate_shallow_file(const char *path);
+extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol);
+extern void setup_alternate_shallow(struct lock_file *shallow_lock,
+                                   const char **alternate_shallow_file);
+extern char *setup_temporary_shallow(void);
 
 int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit *);
@@ -242,4 +272,6 @@ extern void print_commit_list(struct commit_list *list,
  */
 extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc);
 
+int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
+
 #endif /* COMMIT_H */
diff --git a/compat/cygwin.c b/compat/cygwin.c
deleted file mode 100644 (file)
index 871b41d..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-#define CYGWIN_C
-#define WIN32_LEAN_AND_MEAN
-#ifdef CYGWIN_V15_WIN32API
-#include "../git-compat-util.h"
-#include "win32.h"
-#else
-#include <sys/stat.h>
-#include <sys/errno.h>
-#include "win32.h"
-#include "../git-compat-util.h"
-#endif
-#include "../cache.h" /* to read configuration */
-
-/*
- * Return POSIX permission bits, regardless of core.ignorecygwinfstricks
- */
-int cygwin_get_st_mode_bits(const char *path, int *mode)
-{
-       struct stat st;
-       if (lstat(path, &st) < 0)
-               return -1;
-       *mode = st.st_mode;
-       return 0;
-}
-
-static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
-{
-       long long winTime = ((long long)ft->dwHighDateTime << 32) +
-                       ft->dwLowDateTime;
-       winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
-       /* convert 100-nsecond interval to seconds and nanoseconds */
-       ts->tv_sec = (time_t)(winTime/10000000);
-       ts->tv_nsec = (long)(winTime - ts->tv_sec*10000000LL) * 100;
-}
-
-#define size_to_blocks(s) (((s)+511)/512)
-
-/* do_stat is a common implementation for cygwin_lstat and cygwin_stat.
- *
- * To simplify its logic, in the case of cygwin symlinks, this implementation
- * falls back to the cygwin version of stat/lstat, which is provided as the
- * last argument.
- */
-static int do_stat(const char *file_name, struct stat *buf, stat_fn_t cygstat)
-{
-       WIN32_FILE_ATTRIBUTE_DATA fdata;
-
-       if (file_name[0] == '/')
-               return cygstat (file_name, buf);
-
-       if (!(errno = get_file_attr(file_name, &fdata))) {
-               /*
-                * If the system attribute is set and it is not a directory then
-                * it could be a symbol link created in the nowinsymlinks mode.
-                * Normally, Cygwin works in the winsymlinks mode, so this situation
-                * is very unlikely. For the sake of simplicity of our code, let's
-                * Cygwin to handle it.
-                */
-               if ((fdata.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) &&
-                   !(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
-                       return cygstat(file_name, buf);
-
-               /* fill out the stat structure */
-               buf->st_dev = buf->st_rdev = 0; /* not used by Git */
-               buf->st_ino = 0;
-               buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
-               buf->st_nlink = 1;
-               buf->st_uid = buf->st_gid = 0;
-#ifdef __CYGWIN_USE_BIG_TYPES__
-               buf->st_size = ((_off64_t)fdata.nFileSizeHigh << 32) +
-                       fdata.nFileSizeLow;
-#else
-               buf->st_size = (off_t)fdata.nFileSizeLow;
-#endif
-               buf->st_blocks = size_to_blocks(buf->st_size);
-               filetime_to_timespec(&fdata.ftLastAccessTime, &buf->st_atim);
-               filetime_to_timespec(&fdata.ftLastWriteTime, &buf->st_mtim);
-               filetime_to_timespec(&fdata.ftCreationTime, &buf->st_ctim);
-               return 0;
-       } else if (errno == ENOENT) {
-               /*
-                * In the winsymlinks mode (which is the default), Cygwin
-                * emulates symbol links using Windows shortcut files. These
-                * files are formed by adding .lnk extension. So, if we have
-                * not found the specified file name, it could be that it is
-                * a symbol link. Let's Cygwin to deal with that.
-                */
-               return cygstat(file_name, buf);
-       }
-       return -1;
-}
-
-/* We provide our own lstat/stat functions, since the provided Cygwin versions
- * of these functions are too slow. These stat functions are tailored for Git's
- * usage, and therefore they are not meant to be complete and correct emulation
- * of lstat/stat functionality.
- */
-static int cygwin_lstat(const char *path, struct stat *buf)
-{
-       return do_stat(path, buf, lstat);
-}
-
-static int cygwin_stat(const char *path, struct stat *buf)
-{
-       return do_stat(path, buf, stat);
-}
-
-
-/*
- * At start up, we are trying to determine whether Win32 API or cygwin stat
- * functions should be used. The choice is determined by core.ignorecygwinfstricks.
- * Reading this option is not always possible immediately as git_dir may
- * not be set yet. So until it is set, use cygwin lstat/stat functions.
- * However, if core.filemode is set, we must use the Cygwin posix
- * stat/lstat as the Windows stat functions do not determine posix filemode.
- *
- * Note that git_cygwin_config() does NOT call git_default_config() and this
- * is deliberate.  Many commands read from config to establish initial
- * values in variables and later tweak them from elsewhere (e.g. command line).
- * init_stat() is called lazily on demand, typically much late in the program,
- * and calling git_default_config() from here would break such variables.
- */
-static int native_stat = 1;
-static int core_filemode = 1; /* matches trust_executable_bit default */
-
-static int git_cygwin_config(const char *var, const char *value, void *cb)
-{
-       if (!strcmp(var, "core.ignorecygwinfstricks"))
-               native_stat = git_config_bool(var, value);
-       else if (!strcmp(var, "core.filemode"))
-               core_filemode = git_config_bool(var, value);
-       return 0;
-}
-
-static int init_stat(void)
-{
-       if (have_git_dir() && git_config(git_cygwin_config,NULL)) {
-               if (!core_filemode && native_stat) {
-                       cygwin_stat_fn = cygwin_stat;
-                       cygwin_lstat_fn = cygwin_lstat;
-               } else {
-                       cygwin_stat_fn = stat;
-                       cygwin_lstat_fn = lstat;
-               }
-               return 0;
-       }
-       return 1;
-}
-
-static int cygwin_stat_stub(const char *file_name, struct stat *buf)
-{
-       return (init_stat() ? stat : *cygwin_stat_fn)(file_name, buf);
-}
-
-static int cygwin_lstat_stub(const char *file_name, struct stat *buf)
-{
-       return (init_stat() ? lstat : *cygwin_lstat_fn)(file_name, buf);
-}
-
-stat_fn_t cygwin_stat_fn = cygwin_stat_stub;
-stat_fn_t cygwin_lstat_fn = cygwin_lstat_stub;
-
diff --git a/compat/cygwin.h b/compat/cygwin.h
deleted file mode 100644 (file)
index c04965a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <sys/types.h>
-#include <sys/stat.h>
-
-typedef int (*stat_fn_t)(const char*, struct stat*);
-extern stat_fn_t cygwin_stat_fn;
-extern stat_fn_t cygwin_lstat_fn;
-int cygwin_get_st_mode_bits(const char *path, int *mode);
-
-#define get_st_mode_bits(p,m) cygwin_get_st_mode_bits((p),(m))
-#ifndef CYGWIN_C
-/* cygwin.c needs the original lstat() */
-#define stat(path, buf) (*cygwin_stat_fn)(path, buf)
-#define lstat(path, buf) (*cygwin_lstat_fn)(path, buf)
-#endif
index 5ef0685135662e3cefc80eed5c5ae030fe1a5d49..378c467401ea156cb189313bd75f3c28406277c0 100644 (file)
@@ -25,6 +25,7 @@
 # define _GNU_SOURCE   1
 #endif
 
+#include <stddef.h>
 #include <errno.h>
 #include <fnmatch.h>
 #include <ctype.h>
    whose names are inconsistent.  */
 
 # if !defined _LIBC && !defined getenv
-extern char *getenv ();
+extern char *getenv (const char *name);
 # endif
 
 # ifndef errno
index b67362558046f354b167fe185369b3d2b98879c1..bb92c436f70790ba907991f3307aa0fe4afdf0f5 100644 (file)
@@ -841,8 +841,8 @@ struct pinfo_t {
        struct pinfo_t *next;
        pid_t pid;
        HANDLE proc;
-} pinfo_t;
-struct pinfo_t *pinfo = NULL;
+};
+static struct pinfo_t *pinfo = NULL;
 CRITICAL_SECTION pinfo_cs;
 
 static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
@@ -1253,7 +1253,7 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
        else
                sin->sin_addr.s_addr = INADDR_LOOPBACK;
        ai->ai_addr = (struct sockaddr *)sin;
-       ai->ai_next = 0;
+       ai->ai_next = NULL;
        return 0;
 }
 
@@ -1677,14 +1677,16 @@ int sigaction(int sig, struct sigaction *in, struct sigaction *out)
 #undef signal
 sig_handler_t mingw_signal(int sig, sig_handler_t handler)
 {
-       sig_handler_t old = timer_fn;
+       sig_handler_t old;
 
        switch (sig) {
        case SIGALRM:
+               old = timer_fn;
                timer_fn = handler;
                break;
 
        case SIGINT:
+               old = sigint_fn;
                sigint_fn = handler;
                break;
 
index 685cd2c3d4746521e047782f9630763044e42809..bd0a88bc1d19583b7dfed36fd8f7a7af7f386296 100644 (file)
@@ -333,14 +333,21 @@ void mingw_open_html(const char *path);
 char **make_augmented_environ(const char *const *vars);
 void free_environ(char **env);
 
+/*
+ * A critical section used in the implementation of the spawn
+ * functions (mingw_spawnv[p]e()) and waitpid(). Intialised in
+ * the replacement main() macro below.
+ */
+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
  */
 
 #define main(c,v) dummy_decl_mingw_main(); \
-static int mingw_main(); \
-int main(int argc, const char **argv) \
+static int mingw_main(c,v); \
+int main(int argc, char **argv) \
 { \
        extern CRITICAL_SECTION pinfo_cs; \
        _fmode = _O_BINARY; \
index e46d8f112cc6a2c39f5ae922ef8b2ca5203d50b1..07cbf50c0f9a7b0ace9a54fe01b481db873ae971 100644 (file)
@@ -97,7 +97,7 @@ Chew for reporting this.
 
 v1.04alpha_svn915 7th October 2006:
  * Fixed failure to unlock thread cache list if allocating a new list failed.
-Thanks to Dmitry Chichkov for reporting this. Futher thanks to Aleksey Sanin.
+Thanks to Dmitry Chichkov for reporting this. Further thanks to Aleksey Sanin.
  * Fixed realloc(0, <size>) segfaulting. Thanks to Dmitry Chichkov for
 reporting this.
  * Made config defines #ifndef so they can be overridden by the build system.
index 1401a6727414480e181023f8de974bad76a04d24..ed4f1fa5af8d911382101116c8ee522ef1e9b629 100644 (file)
@@ -484,6 +484,10 @@ MAX_RELEASE_CHECK_RATE   default: 4095 unless not HAVE_MMAP
 #define DLMALLOC_VERSION 20804
 #endif /* DLMALLOC_VERSION */
 
+#if defined(linux)
+#define _GNU_SOURCE 1
+#endif
+
 #ifndef WIN32
 #ifdef _WIN32
 #define WIN32 1
@@ -1802,7 +1806,7 @@ struct win32_mlock_t
 
 static MLOCK_T malloc_global_mutex = { 0, 0, 0};
 
-static FORCEINLINE long win32_getcurrentthreadid() {
+static FORCEINLINE long win32_getcurrentthreadid(void) {
 #ifdef _MSC_VER
 #if defined(_M_IX86)
   long *threadstruct=(long *)__readfsdword(0x18);
@@ -3598,8 +3602,8 @@ static void internal_malloc_stats(mstate m) {
      and choose its bk node as its replacement.
   2. If x was the last node of its size, but not a leaf node, it must
      be replaced with a leaf node (not merely one with an open left or
-     right), to make sure that lefts and rights of descendents
-     correspond properly to bit masks.  We use the rightmost descendent
+     right), to make sure that lefts and rights of descendants
+     correspond properly to bit masks.  We use the rightmost descendant
      of x.  We could use any other leaf, but this is easy to locate and
      tends to counteract removal of leftmosts elsewhere, and so keeps
      paths shorter than minimally guaranteed.  This doesn't loop much
index 91c4e7f27b4063c7ceffeeb1cd43530ce18cf096..609ebba1258eac47983f2b9da5d582a5d5af5482 100644 (file)
@@ -159,8 +159,8 @@ struct mallinfo nedmallinfo(void) THROWSPEC                 { return nedpmallinfo(0); }
 #endif
 int    nedmallopt(int parno, int value) THROWSPEC      { return nedpmallopt(0, parno, value); }
 int    nedmalloc_trim(size_t pad) THROWSPEC                    { return nedpmalloc_trim(0, pad); }
-void   nedmalloc_stats() THROWSPEC                                     { nedpmalloc_stats(0); }
-size_t nedmalloc_footprint() THROWSPEC                         { return nedpmalloc_footprint(0); }
+void   nedmalloc_stats(void) THROWSPEC                                 { nedpmalloc_stats(0); }
+size_t nedmalloc_footprint(void) THROWSPEC                             { return nedpmalloc_footprint(0); }
 void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC { return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
 void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC   { return nedpindependent_comalloc(0, elems, sizes, chunks); }
 
index 7d226ecb293ee3c5f9b17aa3e5e36b7c8701dbae..44103103a4a71ff25e5e8e1d106221a9dd2e9908 100644 (file)
@@ -576,7 +576,7 @@ restart:
        {
          /* It's a socket.  */
          WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
-         WSAEventSelect ((SOCKET) h, 0, 0);
+         WSAEventSelect ((SOCKET) h, NULL, 0);
 
          /* If we're lucky, WSAEnumNetworkEvents already provided a way
             to distinguish FD_READ and FD_ACCEPT; this saves a recv later.  */
index 030174db515e764aa883b21afe9be8fb3ae41f2e..7980abd1a71d0676f35eb300c46a30a7298e82f3 100644 (file)
@@ -78,7 +78,7 @@ void precompose_argv(int argc, const char **argv)
                size_t namelen;
                oldarg = argv[i];
                if (has_non_ascii(oldarg, (size_t)-1, &namelen)) {
-                       newarg = reencode_string_iconv(oldarg, namelen, ic_precompose);
+                       newarg = reencode_string_iconv(oldarg, namelen, ic_precompose, NULL);
                        if (newarg)
                                argv[i] = newarg;
                }
index d0025bd58d48cfcf137e5ef3bba90881dc00ec68..b2c5d465acd2bd37c1d316353eae544e564fc334 100644 (file)
@@ -2617,7 +2617,7 @@ parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa,
      Build the range expression which starts from START_ELEM, and ends
      at END_ELEM.  The result are written to MBCSET and SBCSET.
      RANGE_ALLOC is the allocated size of mbcset->range_starts, and
-     mbcset->range_ends, is a pointer argument sinse we may
+     mbcset->range_ends, is a pointer argument since we may
      update it.  */
 
 static reg_errcode_t
@@ -2788,7 +2788,7 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
   const int32_t *symb_table;
   const unsigned char *extra;
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      Seek the collating symbol entry correspondings to NAME.
      Return the index of the symbol in the SYMB_TABLE.  */
 
@@ -2892,11 +2892,11 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
       return UINT_MAX;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      Build the range expression which starts from START_ELEM, and ends
      at END_ELEM.  The result are written to MBCSET and SBCSET.
      RANGE_ALLOC is the allocated size of mbcset->range_starts, and
-     mbcset->range_ends, is a pointer argument sinse we may
+     mbcset->range_ends, is a pointer argument since we may
      update it.  */
 
   auto inline reg_errcode_t
@@ -2976,11 +2976,11 @@ parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
       return REG_NOERROR;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      Build the collating element which is represented by NAME.
      The result are written to MBCSET and SBCSET.
      COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
-     pointer argument sinse we may update it.  */
+     pointer argument since we may update it.  */
 
   auto inline reg_errcode_t
   __attribute ((always_inline))
@@ -3419,7 +3419,7 @@ parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
      Build the equivalence class which is represented by NAME.
      The result are written to MBCSET and SBCSET.
      EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
-     is a pointer argument sinse we may update it.  */
+     is a pointer argument since we may update it.  */
 
 static reg_errcode_t
 #ifdef RE_ENABLE_I18N
@@ -3515,7 +3515,7 @@ build_equiv_class (bitset_t sbcset, const unsigned char *name)
      Build the character class which is represented by NAME.
      The result are written to MBCSET and SBCSET.
      CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
-     is a pointer argument sinse we may update it.  */
+     is a pointer argument since we may update it.  */
 
 static reg_errcode_t
 #ifdef RE_ENABLE_I18N
index 0194965c5d255a914de814f3e623698932f8a917..eb5e1d44397cdad8f0db910e39037cf6b39f35a9 100644 (file)
@@ -455,7 +455,7 @@ re_search_stub (struct re_pattern_buffer *bufp,
 
   rval = 0;
 
-  /* I hope we needn't fill ther regs with -1's when no match was found.  */
+  /* I hope we needn't fill their regs with -1's when no match was found.  */
   if (result != REG_NOERROR)
     rval = -1;
   else if (regs != NULL)
@@ -1071,7 +1071,7 @@ acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx,
    FL_LONGEST_MATCH means we want the POSIX longest matching.
    If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
    next place where we may want to try matching.
-   Note that the matcher assume that the maching starts from the current
+   Note that the matcher assume that the matching starts from the current
    index of the buffer.  */
 
 static int
@@ -2239,7 +2239,7 @@ sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx,
                            dfa->nexts[node_idx]))
     /* The node can't accept the `multi byte', or the
        destination was already thrown away, then the node
-       could't accept the current input `multi byte'.   */
+       couldn't accept the current input `multi byte'.   */
     naccepted = 0;
   /* Otherwise, it is sure that the node could accept
      `naccepted' bytes input.  */
@@ -2313,7 +2313,7 @@ transit_state (reg_errcode_t *err, re_match_context_t *mctx,
 }
 
 /* Update the state_log if we need */
-re_dfastate_t *
+static re_dfastate_t *
 internal_function
 merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
                      re_dfastate_t *next_state)
@@ -2326,7 +2326,7 @@ merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
       mctx->state_log[cur_idx] = next_state;
       mctx->state_log_top = cur_idx;
     }
-  else if (mctx->state_log[cur_idx] == 0)
+  else if (mctx->state_log[cur_idx] == NULL)
     {
       mctx->state_log[cur_idx] = next_state;
     }
@@ -2392,7 +2392,7 @@ merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
 /* Skip bytes in the input that correspond to part of a
    multi-byte match, then look in the log for a state
    from which to restart matching.  */
-re_dfastate_t *
+static re_dfastate_t *
 internal_function
 find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
 {
index 9b5e3d1bb8fe0adafb78721c09caff5b165dbf40..313897d581f0c5f2eae4d09b82d6ff8b98a21ea4 100644 (file)
@@ -3,7 +3,7 @@
 #include "sigchain.h"
 #include "strbuf.h"
 
-#if defined(HAVE_DEV_TTY) || defined(WIN32)
+#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
 
 static void restore_term(void);
 
@@ -53,7 +53,7 @@ error:
        return -1;
 }
 
-#elif defined(WIN32)
+#elif defined(GIT_WINDOWS_NATIVE)
 
 #define INPUT_PATH "CONIN$"
 #define OUTPUT_PATH "CONOUT$"
index eb29f5e0849370afe90c400271fea12e0f9090aa..bf5fd7063bc98964382b193cc9171a734c7aa790 100644 (file)
@@ -2,7 +2,9 @@
 
 void gitunsetenv (const char *name)
 {
+#if !defined(__MINGW32__)
      extern char **environ;
+#endif
      int src, dst;
      size_t nmln;
 
index 8ce91048deffc17bdf8d51672ddaa0c867052ac5..a97e880757b6f1328fea90371da8f857648e6923 100644 (file)
@@ -2,7 +2,7 @@
 #define WIN32_H
 
 /* common Win32 functions for MinGW and Cygwin */
-#ifndef WIN32         /* Not defined by Cygwin */
+#ifndef GIT_WINDOWS_NATIVE     /* Not defined for Cygwin */
 #include <windows.h>
 #endif
 
index 010e875ec4dd8d7154a0911661570165ac1ae874..e18f5c6e2e55a4b42f178e3042301cd77a654d80 100644 (file)
@@ -52,7 +52,7 @@ int win32_pthread_join(pthread_t *thread, void **value_ptr)
 
 pthread_t pthread_self(void)
 {
-       pthread_t t = { 0 };
+       pthread_t t = { NULL };
        t.tid = GetCurrentThreadId();
        return t;
 }
index 61d2ef8e46084ef14ea22690a68c6c99e2644455..80a8c9af4f0ddcc883370fe05781337cef0d5ef0 100644 (file)
@@ -21,8 +21,8 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
        if (!(flags & MAP_PRIVATE))
                die("Invalid usage of mmap when built with USE_WIN32_MMAP");
 
-       hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), 0, PAGE_WRITECOPY,
-               0, 0, 0);
+       hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), NULL,
+               PAGE_WRITECOPY, 0, 0, NULL);
 
        if (!hmap)
                return MAP_FAILED;
index aefd80b12a079d4a3c91d43c8a2c33ed6fbd0a38..9f9bf0cc9a1405e938fddbedc44468782ad61021 100644 (file)
--- a/config.c
+++ b/config.c
 #include "strbuf.h"
 #include "quote.h"
 
-typedef struct config_file {
-       struct config_file *prev;
-       FILE *f;
+struct config_source {
+       struct config_source *prev;
+       union {
+               FILE *file;
+               struct config_buf {
+                       const char *buf;
+                       size_t len;
+                       size_t pos;
+               } buf;
+       } u;
        const char *name;
+       int die_on_error;
        int linenr;
        int eof;
        struct strbuf value;
        struct strbuf var;
-} config_file;
 
-static config_file *cf;
+       int (*do_fgetc)(struct config_source *c);
+       int (*do_ungetc)(int c, struct config_source *conf);
+       long (*do_ftell)(struct config_source *c);
+};
+
+static struct config_source *cf;
 
 static int zlib_compression_seen;
 
+static int config_file_fgetc(struct config_source *conf)
+{
+       return fgetc(conf->u.file);
+}
+
+static int config_file_ungetc(int c, struct config_source *conf)
+{
+       return ungetc(c, conf->u.file);
+}
+
+static long config_file_ftell(struct config_source *conf)
+{
+       return ftell(conf->u.file);
+}
+
+
+static int config_buf_fgetc(struct config_source *conf)
+{
+       if (conf->u.buf.pos < conf->u.buf.len)
+               return conf->u.buf.buf[conf->u.buf.pos++];
+
+       return EOF;
+}
+
+static int config_buf_ungetc(int c, struct config_source *conf)
+{
+       if (conf->u.buf.pos > 0)
+               return conf->u.buf.buf[--conf->u.buf.pos];
+
+       return EOF;
+}
+
+static long config_buf_ftell(struct config_source *conf)
+{
+       return conf->u.buf.pos;
+}
+
 #define MAX_INCLUDE_DEPTH 10
 static const char include_depth_advice[] =
 "exceeded maximum include depth (%d) while including\n"
@@ -58,7 +107,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
                path = buf.buf;
        }
 
-       if (!access_or_die(path, R_OK)) {
+       if (!access_or_die(path, R_OK, 0)) {
                if (++inc->depth > MAX_INCLUDE_DEPTH)
                        die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
                            cf && cf->name ? cf->name : "the command line");
@@ -168,27 +217,22 @@ int git_config_from_parameters(config_fn_t fn, void *data)
 
 static int get_next_char(void)
 {
-       int c;
-       FILE *f;
-
-       c = '\n';
-       if (cf && ((f = cf->f) != NULL)) {
-               c = fgetc(f);
-               if (c == '\r') {
-                       /* DOS like systems */
-                       c = fgetc(f);
-                       if (c != '\n') {
-                               ungetc(c, f);
-                               c = '\r';
-                       }
-               }
-               if (c == '\n')
-                       cf->linenr++;
-               if (c == EOF) {
-                       cf->eof = 1;
-                       c = '\n';
+       int c = cf->do_fgetc(cf);
+
+       if (c == '\r') {
+               /* DOS like systems */
+               c = cf->do_fgetc(cf);
+               if (c != '\n') {
+                       cf->do_ungetc(c, cf);
+                       c = '\r';
                }
        }
+       if (c == '\n')
+               cf->linenr++;
+       if (c == EOF) {
+               cf->eof = 1;
+               c = '\n';
+       }
        return c;
 }
 
@@ -339,7 +383,7 @@ static int get_base_var(struct strbuf *name)
        }
 }
 
-static int git_parse_file(config_fn_t fn, void *data)
+static int git_parse_source(config_fn_t fn, void *data)
 {
        int comment = 0;
        int baselen = 0;
@@ -399,7 +443,10 @@ static int git_parse_file(config_fn_t fn, void *data)
                if (get_value(fn, data, var) < 0)
                        break;
        }
-       die("bad config file line %d in %s", cf->linenr, cf->name);
+       if (cf->die_on_error)
+               die("bad config file line %d in %s", cf->linenr, cf->name);
+       else
+               return error("bad config file line %d in %s", cf->linenr, cf->name);
 }
 
 static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -566,7 +613,20 @@ static int git_default_core_config(const char *var, const char *value)
                trust_ctime = git_config_bool(var, value);
                return 0;
        }
-       if (!strcmp(var, "core.statinfo")) {
+       if (!strcmp(var, "core.statinfo") ||
+           !strcmp(var, "core.checkstat")) {
+               /*
+                * NEEDSWORK: statinfo was a typo in v1.8.2 that has
+                * never been advertised.  we will remove it at Git
+                * 2.0 boundary.
+                */
+               if (!strcmp(var, "core.statinfo")) {
+                       static int warned;
+                       if (!warned++) {
+                               warning("'core.statinfo' will be removed in Git 2.0; "
+                                       "use 'core.checkstat' instead.");
+                       }
+               }
                if (!strcasecmp(value, "default"))
                        check_stat = 1;
                else if (!strcasecmp(value, "minimal"))
@@ -675,9 +735,6 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
-       if (!strcmp(var, "core.logpackaccess"))
-               return git_config_string(&log_pack_access, var, value);
-
        if (!strcmp(var, "core.autocrlf")) {
                if (value && !strcasecmp(value, "input")) {
                        if (core_eol == EOL_CRLF)
@@ -896,6 +953,33 @@ int git_default_config(const char *var, const char *value, void *dummy)
        return 0;
 }
 
+/*
+ * All source specific fields in the union, die_on_error, name and the callbacks
+ * fgetc, ungetc, ftell of top need to be initialized before calling
+ * this function.
+ */
+static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
+{
+       int ret;
+
+       /* push config-file parsing state stack */
+       top->prev = cf;
+       top->linenr = 1;
+       top->eof = 0;
+       strbuf_init(&top->value, 1024);
+       strbuf_init(&top->var, 1024);
+       cf = top;
+
+       ret = git_parse_source(fn, data);
+
+       /* pop config-file parsing state stack */
+       strbuf_release(&top->value);
+       strbuf_release(&top->var);
+       cf = top->prev;
+
+       return ret;
+}
+
 int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 {
        int ret;
@@ -903,30 +987,74 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
 
        ret = -1;
        if (f) {
-               config_file top;
+               struct config_source top;
 
-               /* push config-file parsing state stack */
-               top.prev = cf;
-               top.f = f;
+               top.u.file = f;
                top.name = filename;
-               top.linenr = 1;
-               top.eof = 0;
-               strbuf_init(&top.value, 1024);
-               strbuf_init(&top.var, 1024);
-               cf = &top;
-
-               ret = git_parse_file(fn, data);
+               top.die_on_error = 1;
+               top.do_fgetc = config_file_fgetc;
+               top.do_ungetc = config_file_ungetc;
+               top.do_ftell = config_file_ftell;
 
-               /* pop config-file parsing state stack */
-               strbuf_release(&top.value);
-               strbuf_release(&top.var);
-               cf = top.prev;
+               ret = do_config_from(&top, fn, data);
 
                fclose(f);
        }
        return ret;
 }
 
+int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
+                       size_t len, void *data)
+{
+       struct config_source top;
+
+       top.u.buf.buf = buf;
+       top.u.buf.len = len;
+       top.u.buf.pos = 0;
+       top.name = name;
+       top.die_on_error = 0;
+       top.do_fgetc = config_buf_fgetc;
+       top.do_ungetc = config_buf_ungetc;
+       top.do_ftell = config_buf_ftell;
+
+       return do_config_from(&top, fn, data);
+}
+
+static int git_config_from_blob_sha1(config_fn_t fn,
+                                    const char *name,
+                                    const unsigned char *sha1,
+                                    void *data)
+{
+       enum object_type type;
+       char *buf;
+       unsigned long size;
+       int ret;
+
+       buf = read_sha1_file(sha1, &type, &size);
+       if (!buf)
+               return error("unable to load config blob object '%s'", name);
+       if (type != OBJ_BLOB) {
+               free(buf);
+               return error("reference '%s' does not point to a blob", name);
+       }
+
+       ret = git_config_from_buf(fn, name, buf, size, data);
+       free(buf);
+
+       return ret;
+}
+
+static int git_config_from_blob_ref(config_fn_t fn,
+                                   const char *name,
+                                   void *data)
+{
+       unsigned char sha1[20];
+
+       if (get_sha1(name, sha1) < 0)
+               return error("unable to resolve config blob '%s'", name);
+       return git_config_from_blob_sha1(fn, name, sha1, data);
+}
+
 const char *git_etc_gitconfig(void)
 {
        static const char *system_wide;
@@ -954,23 +1082,23 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 
        home_config_paths(&user_config, &xdg_config, "config");
 
-       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK)) {
+       if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
                found += 1;
        }
 
-       if (xdg_config && !access_or_die(xdg_config, R_OK)) {
+       if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) {
                ret += git_config_from_file(fn, xdg_config, data);
                found += 1;
        }
 
-       if (user_config && !access_or_die(user_config, R_OK)) {
+       if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) {
                ret += git_config_from_file(fn, user_config, data);
                found += 1;
        }
 
-       if (repo_config && !access_or_die(repo_config, R_OK)) {
+       if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
                ret += git_config_from_file(fn, repo_config, data);
                found += 1;
        }
@@ -992,7 +1120,9 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 }
 
 int git_config_with_options(config_fn_t fn, void *data,
-                           const char *filename, int respect_includes)
+                           const char *filename,
+                           const char *blob_ref,
+                           int respect_includes)
 {
        char *repo_config = NULL;
        int ret;
@@ -1011,6 +1141,8 @@ int git_config_with_options(config_fn_t fn, void *data,
         */
        if (filename)
                return git_config_from_file(fn, filename, data);
+       else if (blob_ref)
+               return git_config_from_blob_ref(fn, blob_ref, data);
 
        repo_config = git_pathdup("config");
        ret = git_config_early(fn, data, repo_config);
@@ -1021,7 +1153,7 @@ int git_config_with_options(config_fn_t fn, void *data,
 
 int git_config(config_fn_t fn, void *data)
 {
-       return git_config_with_options(fn, data, NULL, 1);
+       return git_config_with_options(fn, data, NULL, NULL, 1);
 }
 
 /*
@@ -1053,7 +1185,6 @@ static int store_aux(const char *key, const char *value, void *cb)
 {
        const char *ep;
        size_t section_len;
-       FILE *f = cf->f;
 
        switch (store.state) {
        case KEY_SEEN:
@@ -1065,7 +1196,7 @@ static int store_aux(const char *key, const char *value, void *cb)
                                return 1;
                        }
 
-                       store.offset[store.seen] = ftell(f);
+                       store.offset[store.seen] = cf->do_ftell(cf);
                        store.seen++;
                }
                break;
@@ -1092,19 +1223,19 @@ static int store_aux(const char *key, const char *value, void *cb)
                 * Do not increment matches: this is no match, but we
                 * just made sure we are in the desired section.
                 */
-               store.offset[store.seen] = ftell(f);
+               store.offset[store.seen] = cf->do_ftell(cf);
                /* fallthru */
        case SECTION_END_SEEN:
        case START:
                if (matches(key, value)) {
-                       store.offset[store.seen] = ftell(f);
+                       store.offset[store.seen] = cf->do_ftell(cf);
                        store.state = KEY_SEEN;
                        store.seen++;
                } else {
                        if (strrchr(key, '.') - key == store.baselen &&
                              !strncmp(key, store.key, store.baselen)) {
                                        store.state = SECTION_SEEN;
-                                       store.offset[store.seen] = ftell(f);
+                                       store.offset[store.seen] = cf->do_ftell(cf);
                        }
                }
        }
index d78fd3df5b211130ba8ff77197a3f852c32aab86..7d615314f41f33df245efcecbe49f2aee3c90ad7 100644 (file)
@@ -158,19 +158,18 @@ ifeq ($(uname_O),Cygwin)
                NO_SYMLINK_HEAD = YesPlease
                NO_IPV6 = YesPlease
                OLD_ICONV = UnfortunatelyYes
-               CYGWIN_V15_WIN32API = YesPlease
+               NO_THREAD_SAFE_PREAD = YesPlease
+               # There are conflicting reports about this.
+               # On some boxes NO_MMAP is needed, and not so elsewhere.
+               # Try commenting this out if you suspect MMAP is more efficient
+               NO_MMAP = YesPlease
+       else
+               NO_REGEX = UnfortunatelyYes
        endif
-       NO_THREAD_SAFE_PREAD = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
-       NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
-       # There are conflicting reports about this.
-       # On some boxes NO_MMAP is needed, and not so elsewhere.
-       # Try commenting this out if you suspect MMAP is more efficient
-       NO_MMAP = YesPlease
        X = .exe
-       COMPAT_OBJS += compat/cygwin.o
        UNRELIABLE_FSTAT = UnfortunatelyYes
        SPARSE_FLAGS = -isystem /usr/include/w32api -Wno-one-bit-signed-bitfield
 endif
@@ -480,7 +479,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_FNMATCH = YesPlease
        NO_MEMMEM = YesPlease
        NEEDS_LIBICONV = YesPlease
-       OLD_ICONV = YesPlease
        NO_STRTOUMAX = YesPlease
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
@@ -500,13 +498,17 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_INET_PTON = YesPlease
        NO_INET_NTOP = YesPlease
        NO_POSIX_GOODIES = UnfortunatelyYes
+       DEFAULT_HELP_FORMAT = html
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/win32
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/dirent.o
+       BASIC_LDFLAGS += -Wl,--large-address-aware
        EXTLIBS += -lws2_32
+       GITLIBS += git.res
        PTHREAD_LIBS =
+       RC = windres -O coff
        NATIVE_CRLF = YesPlease
        X = .exe
        SPARSE_FLAGS = -Wno-one-bit-signed-bitfield
@@ -518,6 +520,7 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        NO_R_TO_GCC_LINKER = YesPlease
        INTERNAL_QSORT = YesPlease
        HAVE_LIBCHARSET_H = YesPlease
+       NO_GETTEXT = YesPlease
 else
        NO_CURL = YesPlease
 endif
index f3462d9c81f12ea07229128f6bef7a4ff3fcfe10..2f433939dc9d9001c7ace83bfeadf567eb90c30c 100644 (file)
@@ -193,7 +193,7 @@ AC_ARG_ENABLE([pthreads],
   [FLAGS is the value to pass to the compiler to enable POSIX Threads.]
   [The default if FLAGS is not specified is to try first -pthread]
   [and then -lpthread.]
-  [--without-pthreads will disable threading.])],
+  [--disable-pthreads will disable threading.])],
 [
 if test "x$enableval" = "xyes"; then
    AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads])
index f57efd06c1d7ab01076b67d37ed24e34e17c4ebb..a0783d4867c5e6a9496e11ed043f666a402c5db9 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -551,8 +551,11 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
        path = strchr(end, c);
        if (path && !has_dos_drive_prefix(end)) {
                if (c == ':') {
-                       protocol = PROTO_SSH;
-                       *path++ = '\0';
+                       if (path < strchrnul(host, '/')) {
+                               protocol = PROTO_SSH;
+                               *path++ = '\0';
+                       } else /* '/' in the host part, assume local path */
+                               path = end;
                }
        } else
                path = end;
index 1e89c1cd1d63f160bf7c95de41be61ff2d888e70..fae8d64c12e44cf6bcf4bab210abc4bc4b9c8ed9 100644 (file)
@@ -2,7 +2,12 @@
 #include "run-command.h"
 #include "sigchain.h"
 #include "connected.h"
+#include "transport.h"
 
+int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
+{
+       return check_everything_connected_with_transport(fn, quiet, cb_data, NULL);
+}
 /*
  * If we feed all the commits we want to verify to this command
  *
  *
  * Returns 0 if everything is connected, non-zero otherwise.
  */
-int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
+int check_everything_connected_with_transport(sha1_iterate_fn fn,
+                                             int quiet,
+                                             void *cb_data,
+                                             struct transport *transport)
 {
        struct child_process rev_list;
        const char *argv[] = {"rev-list", "--objects",
@@ -22,10 +30,23 @@ int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
        char commit[41];
        unsigned char sha1[20];
        int err = 0;
+       struct packed_git *new_pack = NULL;
 
        if (fn(cb_data, sha1))
                return err;
 
+       if (transport && transport->smart_options &&
+           transport->smart_options->self_contained_and_connected &&
+           transport->pack_lockfile &&
+           !suffixcmp(transport->pack_lockfile, ".keep")) {
+               struct strbuf idx_file = STRBUF_INIT;
+               strbuf_addstr(&idx_file, transport->pack_lockfile);
+               strbuf_setlen(&idx_file, idx_file.len - 5); /* ".keep" */
+               strbuf_addstr(&idx_file, ".idx");
+               new_pack = add_packed_git(idx_file.buf, idx_file.len, 1);
+               strbuf_release(&idx_file);
+       }
+
        if (quiet)
                argv[5] = "--quiet";
 
@@ -42,6 +63,17 @@ int check_everything_connected(sha1_iterate_fn fn, int quiet, void *cb_data)
 
        commit[40] = '\n';
        do {
+               /*
+                * If index-pack already checked that:
+                * - there are no dangling pointers in the new pack
+                * - the pack is self contained
+                * Then if the updated ref is in the new pack, then we
+                * are sure the ref is good and not sending it to
+                * rev-list for verification.
+                */
+               if (new_pack && find_pack_entry_one(sha1, new_pack))
+                       continue;
+
                memcpy(commit, sha1_to_hex(sha1), 40);
                if (write_in_full(rev_list.in, commit, 41) < 0) {
                        if (errno != EPIPE && errno != EINVAL)
index 7e4585a6cbccdb35f4a4481fae1eaf417539e97d..0b060b7429ff2035ac3db108f80308c4a8a8e785 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef CONNECTED_H
 #define CONNECTED_H
 
+struct transport;
+
 /*
  * Take callback data, and return next object name in the buffer.
  * When called after returning the name for the last object, return -1
@@ -16,5 +18,8 @@ typedef int (*sha1_iterate_fn)(void *, unsigned char [20]);
  * Return 0 if Ok, non zero otherwise (i.e. some missing objects)
  */
 extern int check_everything_connected(sha1_iterate_fn, int quiet, void *cb_data);
+extern int check_everything_connected_with_transport(sha1_iterate_fn, int quiet,
+                                                    void *cb_data,
+                                                    struct transport *transport);
 
 #endif /* CONNECTED_H */
diff --git a/contrib/blameview/README b/contrib/blameview/README
deleted file mode 100644 (file)
index fada5ce..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a sample program to use 'git-blame --incremental', based
-on this message.
-
-From: Jeff King <peff@peff.net>
-Subject: Re: More precise tag following
-To: Linus Torvalds <torvalds@linux-foundation.org>
-Cc: git@vger.kernel.org
-Date: Sat, 27 Jan 2007 18:52:38 -0500
-Message-ID: <20070127235238.GA28706@coredump.intra.peff.net>
diff --git a/contrib/blameview/blameview.perl b/contrib/blameview/blameview.perl
deleted file mode 100755 (executable)
index 1dec001..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/perl
-
-use Gtk2 -init;
-use Gtk2::SimpleList;
-
-my $hash;
-my $fn;
-if ( @ARGV == 1 ) {
-       $hash = "HEAD";
-       $fn = shift;
-} elsif ( @ARGV == 2 ) {
-       $hash = shift;
-       $fn = shift;
-} else {
-       die "Usage blameview [<rev>] <filename>";
-}
-
-Gtk2::Rc->parse_string(<<'EOS');
-style "treeview_style"
-{
-  GtkTreeView::vertical-separator = 0
-}
-class "GtkTreeView" style "treeview_style"
-EOS
-
-my $window = Gtk2::Window->new('toplevel');
-$window->signal_connect(destroy => sub { Gtk2->main_quit });
-my $vpan = Gtk2::VPaned->new();
-$window->add($vpan);
-my $scrolled_window = Gtk2::ScrolledWindow->new;
-$vpan->pack1($scrolled_window, 1, 1);
-my $fileview = Gtk2::SimpleList->new(
-    'Commit' => 'text',
-    'FileLine' => 'text',
-    'Data' => 'text'
-);
-$scrolled_window->add($fileview);
-$fileview->get_column(0)->set_spacing(0);
-$fileview->set_size_request(1024, 768);
-$fileview->set_rules_hint(1);
-$fileview->signal_connect (row_activated => sub {
-               my ($sl, $path, $column) = @_;
-               my $row_ref = $sl->get_row_data_from_path ($path);
-               system("blameview @$row_ref[0]~1 $fn &");
-               });
-
-my $commitwindow = Gtk2::ScrolledWindow->new();
-$commitwindow->set_policy ('GTK_POLICY_AUTOMATIC','GTK_POLICY_AUTOMATIC');
-$vpan->pack2($commitwindow, 1, 1);
-my $commit_text = Gtk2::TextView->new();
-my $commit_buffer = Gtk2::TextBuffer->new();
-$commit_text->set_buffer($commit_buffer);
-$commitwindow->add($commit_text);
-
-$fileview->signal_connect (cursor_changed => sub {
-               my ($sl) = @_;
-               my ($path, $focus_column) = $sl->get_cursor();
-               my $row_ref = $sl->get_row_data_from_path ($path);
-               my $c_fh;
-               open($c_fh,  '-|', "git cat-file commit @$row_ref[0]")
-                                       or die "unable to find commit @$row_ref[0]";
-               my @buffer = <$c_fh>;
-               $commit_buffer->set_text("@buffer");
-               close($c_fh);
-               });
-
-my $fh;
-open($fh, '-|', "git cat-file blob $hash:$fn")
-  or die "unable to open $fn: $!";
-
-while(<$fh>) {
-  chomp;
-  $fileview->{data}->[$.] = ['HEAD', "$fn:$.", $_];
-}
-
-my $blame;
-open($blame, '-|', qw(git blame --incremental --), $fn, $hash)
-    or die "cannot start git-blame $fn";
-
-Glib::IO->add_watch(fileno($blame), 'in', \&read_blame_line);
-
-$window->show_all;
-Gtk2->main;
-exit 0;
-
-my %commitinfo = ();
-
-sub flush_blame_line {
-       my ($attr) = @_;
-
-       return unless defined $attr;
-
-       my ($commit, $s_lno, $lno, $cnt) =
-           @{$attr}{qw(COMMIT S_LNO LNO CNT)};
-
-       my ($filename, $author, $author_time, $author_tz) =
-           @{$commitinfo{$commit}}{qw(FILENAME AUTHOR AUTHOR-TIME AUTHOR-TZ)};
-       my $info = $author . ' ' . format_time($author_time, $author_tz);
-
-       for(my $i = 0; $i < $cnt; $i++) {
-               @{$fileview->{data}->[$lno+$i-1]}[0,1,2] =
-               (substr($commit, 0, 8), $filename . ':' . ($s_lno+$i));
-       }
-}
-
-my $buf;
-my $current;
-sub read_blame_line {
-
-       my $r = sysread($blame, $buf, 1024, length($buf));
-       die "I/O error" unless defined $r;
-
-       if ($r == 0) {
-               flush_blame_line($current);
-               $current = undef;
-               return 0;
-       }
-
-       while ($buf =~ s/([^\n]*)\n//) {
-               my $line = $1;
-
-               if (($commit, $s_lno, $lno, $cnt) =
-                   ($line =~ /^([0-9a-f]{40}) (\d+) (\d+) (\d+)$/)) {
-                       flush_blame_line($current);
-                       $current = +{
-                               COMMIT => $1,
-                               S_LNO => $2,
-                               LNO => $3,
-                               CNT => $4,
-                       };
-                       next;
-               }
-
-               # extended attribute values
-               if ($line =~ /^(author|author-mail|author-time|author-tz|committer|committer-mail|committer-time|committer-tz|summary|filename) (.*)$/) {
-                       my $commit = $current->{COMMIT};
-                       $commitinfo{$commit}{uc($1)} = $2;
-                       next;
-               }
-       }
-       return 1;
-}
-
-sub format_time {
-  my $time = shift;
-  my $tz = shift;
-
-  my $minutes = $tz < 0 ? 0-$tz : $tz;
-  $minutes = ($minutes / 100)*60 + ($minutes % 100);
-  $minutes = $tz < 0 ? 0-$minutes : $minutes;
-  $time += $minutes * 60;
-  my @t = gmtime($time);
-  return sprintf('%04d-%02d-%02d %02d:%02d:%02d %s',
-                $t[5] + 1900, @t[4,3,2,1,0], $tz);
-}
index 36b5665ff81af1d52be5e926b029452d3e838c35..befa0c39679aab55b5537b187ed0996914697f40 100755 (executable)
@@ -70,7 +70,7 @@ tinyifier = "http://tinyurl.com/api-create.php?url="
 
 # The template used to generate the XML messages to CIA.  You can make
 # visible changes to the IRC-bot notification lines by hacking this.
-# The default will produce a notfication line that looks like this:
+# The default will produce a notification line that looks like this:
 #
 # ${project}: ${author} ${repo}:${branch} * ${rev} ${files}: ${logmsg} ${url}
 #
index 3fbbc534ae668b979132f6df60c936314917d1f0..dfb71a1a15a8653152d8328e78695f58cf32c43d 100755 (executable)
@@ -39,7 +39,7 @@
 #       done
 #
 # The reason for the tac call is that git rev-list emits commits from
-# most recent to least - better to ship notifactions from oldest to newest.
+# most recent to least - better to ship notifications from oldest to newest.
 #
 # Configuration variables affecting this script:
 #
@@ -64,7 +64,7 @@
 # down. It is unknown whether this is still an issue in 2010, but
 # XML-RPC would be annoying to do from sh in any case. (XML-RPC does
 # have the advantage that it guarantees notification of multiple commits
-# shpped from an update in their actual order.)
+# shipped from an update in their actual order.)
 #
 
 # The project as known to CIA. You can set this with a -p option,
index 6df62c217321b4f66fd230a1b95f2b3cfe2becd2..e1b73130723a57e7f641236331699e67bb0d4d82 100644 (file)
@@ -33,8 +33,6 @@ esac
 # returns location of .git repo
 __gitdir ()
 {
-       # Note: this function is duplicated in git-prompt.sh
-       # When updating it, make sure you update the other one to match.
        if [ -z "${1-}" ]; then
                if [ -n "${__git_dir-}" ]; then
                        echo "$__git_dir"
@@ -252,106 +250,50 @@ __gitcomp_file ()
        # since tilde expansion is not applied.
        # This means that COMPREPLY will be empty and Bash default
        # completion will be used.
-       COMPREPLY=($(compgen -P "${2-}" -W "$1" -- "${3-$cur}"))
+       __gitcompadd "$1" "${2-}" "${3-$cur}" ""
 
-       # Tell Bash that compspec generates filenames.
-       compopt -o filenames 2>/dev/null
+       # use a hack to enable file mode in bash < 4
+       compopt -o filenames +o nospace 2>/dev/null ||
+       compgen -f /non-existing-dir/ > /dev/null
 }
 
-__git_index_file_list_filter_compat ()
-{
-       local path
-
-       while read -r path; do
-               case "$path" in
-               ?*/*) echo "${path%%/*}/" ;;
-               *) echo "$path" ;;
-               esac
-       done
-}
-
-__git_index_file_list_filter_bash ()
-{
-       local path
-
-       while read -r path; do
-               case "$path" in
-               ?*/*)
-                       # XXX if we append a slash to directory names when using
-                       # `compopt -o filenames`, Bash will append another slash.
-                       # This is pretty stupid, and this the reason why we have to
-                       # define a compatible version for this function.
-                       echo "${path%%/*}" ;;
-               *)
-                       echo "$path" ;;
-               esac
-       done
-}
-
-# Process path list returned by "ls-files" and "diff-index --name-only"
-# commands, in order to list only file names relative to a specified
-# directory, and append a slash to directory names.
-__git_index_file_list_filter ()
-{
-       # Default to Bash >= 4.x
-       __git_index_file_list_filter_bash
-}
-
-# Execute git ls-files, returning paths relative to the directory
-# specified in the first argument, and using the options specified in
-# the second argument.
+# Execute 'git ls-files', unless the --committable option is specified, in
+# which case it runs 'git diff-index' to find out the files that can be
+# committed.  It return paths relative to the directory specified in the first
+# argument, and using the options specified in the second argument.
 __git_ls_files_helper ()
 {
        (
                test -n "${CDPATH+set}" && unset CDPATH
-               # NOTE: $2 is not quoted in order to support multiple options
-               cd "$1" && git ls-files --exclude-standard $2
+               cd "$1"
+               if [ "$2" == "--committable" ]; then
+                       git diff-index --name-only --relative HEAD
+               else
+                       # NOTE: $2 is not quoted in order to support multiple options
+                       git ls-files --exclude-standard $2
+               fi
        ) 2>/dev/null
 }
 
 
-# Execute git diff-index, returning paths relative to the directory
-# specified in the first argument, and using the tree object id
-# specified in the second argument.
-__git_diff_index_helper ()
-{
-       (
-               test -n "${CDPATH+set}" && unset CDPATH
-               cd "$1" && git diff-index --name-only --relative "$2"
-       ) 2>/dev/null
-}
-
 # __git_index_files accepts 1 or 2 arguments:
 # 1: Options to pass to ls-files (required).
-#    Supported options are --cached, --modified, --deleted, --others,
-#    and --directory.
 # 2: A directory path (optional).
 #    If provided, only files within the specified directory are listed.
 #    Sub directories are never recursed.  Path must have a trailing
 #    slash.
 __git_index_files ()
 {
-       local dir="$(__gitdir)" root="${2-.}"
+       local dir="$(__gitdir)" root="${2-.}" file
 
        if [ -d "$dir" ]; then
-               __git_ls_files_helper "$root" "$1" | __git_index_file_list_filter |
-                       sort | uniq
-       fi
-}
-
-# __git_diff_index_files accepts 1 or 2 arguments:
-# 1) The id of a tree object.
-# 2) A directory path (optional).
-#    If provided, only files within the specified directory are listed.
-#    Sub directories are never recursed.  Path must have a trailing
-#    slash.
-__git_diff_index_files ()
-{
-       local dir="$(__gitdir)" root="${2-.}"
-
-       if [ -d "$dir" ]; then
-               __git_diff_index_helper "$root" "$1" | __git_index_file_list_filter |
-                       sort | uniq
+               __git_ls_files_helper "$root" "$1" |
+               while read -r file; do
+                       case "$file" in
+                       ?*/*) echo "${file%%/*}" ;;
+                       *) echo "$file" ;;
+                       esac
+               done | sort | uniq
        fi
 }
 
@@ -427,14 +369,8 @@ __git_refs ()
                done
                ;;
        *)
-               git ls-remote "$dir" HEAD ORIG_HEAD 'refs/tags/*' 'refs/heads/*' 'refs/remotes/*' 2>/dev/null | \
-               while read -r hash i; do
-                       case "$i" in
-                       *^{}) ;;
-                       refs/*) echo "${i#refs/*/}" ;;
-                       *) echo "$i" ;;
-                       esac
-               done
+               echo "HEAD"
+               git for-each-ref --format="%(refname:short)" -- "refs/remotes/$dir/" | sed -e "s#^$dir/##"
                ;;
        esac
 }
@@ -552,44 +488,23 @@ __git_complete_revlist_file ()
 }
 
 
-# __git_complete_index_file requires 1 argument: the options to pass to
-# ls-file
+# __git_complete_index_file requires 1 argument:
+# 1: the options to pass to ls-file
+#
+# The exception is --committable, which finds the files appropriate commit.
 __git_complete_index_file ()
 {
-       local pfx cur_="$cur"
+       local pfx="" cur_="$cur"
 
        case "$cur_" in
        ?*/*)
                pfx="${cur_%/*}"
                cur_="${cur_##*/}"
                pfx="${pfx}/"
-
-               __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
-               ;;
-       *)
-               __gitcomp_file "$(__git_index_files "$1")" "" "$cur_"
                ;;
        esac
-}
-
-# __git_complete_diff_index_file requires 1 argument: the id of a tree
-# object
-__git_complete_diff_index_file ()
-{
-       local pfx cur_="$cur"
-
-       case "$cur_" in
-       ?*/*)
-               pfx="${cur_%/*}"
-               cur_="${cur_##*/}"
-               pfx="${pfx}/"
 
-               __gitcomp_file "$(__git_diff_index_files "$1" "$pfx")" "$pfx" "$cur_"
-               ;;
-       *)
-               __gitcomp_file "$(__git_diff_index_files "$1")" "" "$cur_"
-               ;;
-       esac
+       __gitcomp_file "$(__git_index_files "$1" "$pfx")" "$pfx" "$cur_"
 }
 
 __git_complete_file ()
@@ -733,6 +648,7 @@ __git_list_porcelain_commands ()
                cat-file)         : plumbing;;
                check-attr)       : plumbing;;
                check-ignore)     : plumbing;;
+               check-mailmap)    : plumbing;;
                check-ref-format) : plumbing;;
                checkout-index)   : plumbing;;
                commit-tree)      : plumbing;;
@@ -1184,13 +1100,6 @@ _git_commit ()
                ;;
        esac
 
-       case "$prev" in
-       -c|-C)
-               __gitcomp_nl "$(__git_refs)" "" "${cur}"
-               return
-               ;;
-       esac
-
        case "$cur" in
        --cleanup=*)
                __gitcomp "default strip verbatim whitespace
@@ -1220,7 +1129,7 @@ _git_commit ()
        esac
 
        if git rev-parse --verify --quiet HEAD >/dev/null; then
-               __git_complete_diff_index_file "HEAD"
+               __git_complete_index_file "--committable"
        else
                # This is the first commit
                __git_complete_index_file "--cached"
@@ -1253,7 +1162,7 @@ __git_diff_common_options="--stat --numstat --shortstat --summary
                        --no-prefix --src-prefix= --dst-prefix=
                        --inter-hunk-context=
                        --patience --histogram --minimal
-                       --raw
+                       --raw --word-diff
                        --dirstat --dirstat= --dirstat-by-file
                        --dirstat-by-file= --cumulative
                        --diff-algorithm=
@@ -1301,7 +1210,7 @@ _git_difftool ()
                return
                ;;
        esac
-       __git_complete_file
+       __git_complete_revlist_file
 }
 
 __git_fetch_options="
@@ -1321,11 +1230,12 @@ _git_fetch ()
 }
 
 __git_format_patch_options="
-       --stdout --attach --no-attach --thread --thread= --output-directory
+       --stdout --attach --no-attach --thread --thread= --no-thread
        --numbered --start-number --numbered-files --keep-subject --signoff
        --signature --no-signature --in-reply-to= --cc= --full-index --binary
        --not --all --cover-letter --no-prefix --src-prefix= --dst-prefix=
        --inline --suffix= --ignore-if-in-upstream --subject-prefix=
+       --output-directory --reroll-count --to= --quiet --notes
 "
 
 _git_format_patch ()
@@ -1817,7 +1727,7 @@ __git_config_get_set_variables ()
 _git_config ()
 {
        case "$prev" in
-       branch.*.remote)
+       branch.*.remote|branch.*.pushremote)
                __gitcomp_nl "$(__git_remotes)"
                return
                ;;
@@ -1825,11 +1735,19 @@ _git_config ()
                __gitcomp_nl "$(__git_refs)"
                return
                ;;
+       branch.*.rebase)
+               __gitcomp "false true"
+               return
+               ;;
+       remote.pushdefault)
+               __gitcomp_nl "$(__git_remotes)"
+               return
+               ;;
        remote.*.fetch)
                local remote="${prev#remote.}"
                remote="${remote%.fetch}"
                if [ -z "$cur" ]; then
-                       __gitcompadd "refs/heads/" "" "" ""
+                       __gitcomp_nl "refs/heads/" "" "" ""
                        return
                fi
                __gitcomp_nl "$(__git_refs_remotes "$remote")"
@@ -1864,6 +1782,10 @@ _git_config ()
                        "
                return
                ;;
+       diff.submodule)
+               __gitcomp "log short"
+               return
+               ;;
        help.format)
                __gitcomp "man info web html"
                return
@@ -1905,7 +1827,7 @@ _git_config ()
                ;;
        branch.*.*)
                local pfx="${cur%.*}." cur_="${cur##*.}"
-               __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur_"
+               __gitcomp "remote pushremote merge mergeoptions rebase" "$pfx" "$cur_"
                return
                ;;
        branch.*)
@@ -2036,7 +1958,6 @@ _git_config ()
                core.fileMode
                core.fsyncobjectfiles
                core.gitProxy
-               core.ignoreCygwinFSTricks
                core.ignoreStat
                core.ignorecase
                core.logAllRefUpdates
@@ -2058,13 +1979,14 @@ _git_config ()
                core.whitespace
                core.worktree
                diff.autorefreshindex
-               diff.statGraphWidth
                diff.external
                diff.ignoreSubmodules
                diff.mnemonicprefix
                diff.noprefix
                diff.renameLimit
                diff.renames
+               diff.statGraphWidth
+               diff.submodule
                diff.suppressBlankEmpty
                diff.tool
                diff.wordRegex
@@ -2199,6 +2121,7 @@ _git_config ()
                receive.fsckObjects
                receive.unpackLimit
                receive.updateserverinfo
+               remote.pushdefault
                remotes.
                repack.usedeltabaseoffset
                rerere.autoupdate
@@ -2352,7 +2275,7 @@ _git_show ()
                return
                ;;
        esac
-       __git_complete_file
+       __git_complete_revlist_file
 }
 
 _git_show_branch ()
@@ -2443,7 +2366,7 @@ _git_svn ()
                        --no-metadata --use-svm-props --use-svnsync-props
                        --log-window-size= --no-checkout --quiet
                        --repack-flags --use-log-author --localtime
-                       --ignore-paths= $remote_opts
+                       --ignore-paths= --include-paths= $remote_opts
                        "
                local init_opts="
                        --template= --shared= --trunk= --tags=
@@ -2567,9 +2490,10 @@ __git_main ()
                i="${words[c]}"
                case "$i" in
                --git-dir=*) __git_dir="${i#--git-dir=}" ;;
+               --git-dir)   ((c++)) ; __git_dir="${words[c]}" ;;
                --bare)      __git_dir="." ;;
                --help) command="help"; break ;;
-               -c) c=$((++c)) ;;
+               -c|--work-tree|--namespace) ((c++)) ;;
                -*) ;;
                *) command="$i"; break ;;
                esac
@@ -2587,6 +2511,7 @@ __git_main ()
                        --exec-path
                        --exec-path=
                        --html-path
+                       --man-path
                        --info-path
                        --work-tree=
                        --namespace=
@@ -2655,7 +2580,7 @@ if [[ -n ${ZSH_VERSION-} ]]; then
                                --*=*|*.) ;;
                                *) c="$c " ;;
                                esac
-                               array[$#array+1]="$c"
+                               array[${#array[@]}+1]="$c"
                        done
                        compset -P '*[=:]'
                        compadd -Q -S '' -p "${2-}" -a -- array && _ret=0
@@ -2681,35 +2606,19 @@ if [[ -n ${ZSH_VERSION-} ]]; then
                compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
        }
 
-       __git_zsh_helper ()
-       {
-               emulate -L ksh
-               local cur cword prev
-               cur=${words[CURRENT-1]}
-               prev=${words[CURRENT-2]}
-               let cword=CURRENT-1
-               __${service}_main
-       }
-
        _git ()
        {
-               emulate -L zsh
-               local _ret=1
-               __git_zsh_helper
-               let _ret && _default -S '' && _ret=0
+               local _ret=1 cur cword prev
+               cur=${words[CURRENT]}
+               prev=${words[CURRENT-1]}
+               let cword=CURRENT-1
+               emulate ksh -c __${service}_main
+               let _ret && _default && _ret=0
                return _ret
        }
 
        compdef _git git gitk
        return
-elif [[ -n ${BASH_VERSION-} ]]; then
-       if ((${BASH_VERSINFO[0]} < 4)); then
-               # compopt is not supported
-               __git_index_file_list_filter ()
-               {
-                       __git_index_file_list_filter_compat
-               }
-       fi
 fi
 
 __git_func_wrap ()
index cf8116d4770c5924eaa647763d2bfae3dc14bacf..fac5e711ebbbd074366d89848165e0f9dbc36c36 100644 (file)
@@ -2,18 +2,19 @@
 
 # zsh completion wrapper for git
 #
-# You need git's bash completion script installed somewhere, by default on the
-# same directory as this script.
+# Copyright (c) 2012-2013 Felipe Contreras <felipe.contreras@gmail.com>
 #
-# If your script is on ~/.git-completion.sh instead, you can configure it on
-# your ~/.zshrc:
+# You need git's bash completion script installed somewhere, by default it
+# would be the location bash-completion uses.
+#
+# If your script is somewhere else, you can configure it on your ~/.zshrc:
 #
 #  zstyle ':completion:*:*:git:*' script ~/.git-completion.sh
 #
-# The recommended way to install this script is to copy to
-# '~/.zsh/completion/_git', and then add the following to your ~/.zshrc file:
+# The recommended way to install this script is to copy to '~/.zsh/_git', and
+# then add the following to your ~/.zshrc file:
 #
-#  fpath=(~/.zsh/completion $fpath)
+#  fpath=(~/.zsh $fpath)
 
 complete ()
 {
@@ -21,8 +22,23 @@ complete ()
        return 0
 }
 
+zstyle -T ':completion:*:*:git:*' tag-order && \
+       zstyle ':completion:*:*:git:*' tag-order 'common-commands'
+
 zstyle -s ":completion:*:*:git:*" script script
-test -z "$script" && script="$(dirname ${funcsourcetrace[1]%:*})"/git-completion.bash
+if [ -z "$script" ]; then
+       local -a locations
+       local e
+       locations=(
+               '/etc/bash_completion.d/git' # fedora, old debian
+               '/usr/share/bash-completion/completions/git' # arch, ubuntu, new debian
+               '/usr/share/bash-completion/git' # gentoo
+               $(dirname ${funcsourcetrace[1]%:*})/git-completion.bash
+               )
+       for e in $locations; do
+               test -f $e && script="$e" && break
+       done
+fi
 ZSH_VERSION='' . "$script"
 
 __gitcomp ()
@@ -69,18 +85,131 @@ __gitcomp_file ()
        compadd -Q -p "${2-}" -f -- ${=1} && _ret=0
 }
 
+__git_zsh_bash_func ()
+{
+       emulate -L ksh
+
+       local command=$1
+
+       local completion_func="_git_${command//-/_}"
+       declare -f $completion_func >/dev/null && $completion_func && return
+
+       local expansion=$(__git_aliased_command "$command")
+       if [ -n "$expansion" ]; then
+               completion_func="_git_${expansion//-/_}"
+               declare -f $completion_func >/dev/null && $completion_func
+       fi
+}
+
+__git_zsh_cmd_common ()
+{
+       local -a list
+       list=(
+       add:'add file contents to the index'
+       bisect:'find by binary search the change that introduced a bug'
+       branch:'list, create, or delete branches'
+       checkout:'checkout a branch or paths to the working tree'
+       clone:'clone a repository into a new directory'
+       commit:'record changes to the repository'
+       diff:'show changes between commits, commit and working tree, etc'
+       fetch:'download objects and refs from another repository'
+       grep:'print lines matching a pattern'
+       init:'create an empty Git repository or reinitialize an existing one'
+       log:'show commit logs'
+       merge:'join two or more development histories together'
+       mv:'move or rename a file, a directory, or a symlink'
+       pull:'fetch from and merge with another repository or a local branch'
+       push:'update remote refs along with associated objects'
+       rebase:'forward-port local commits to the updated upstream head'
+       reset:'reset current HEAD to the specified state'
+       rm:'remove files from the working tree and from the index'
+       show:'show various types of objects'
+       status:'show the working tree status'
+       tag:'create, list, delete or verify a tag object signed with GPG')
+       _describe -t common-commands 'common commands' list && _ret=0
+}
+
+__git_zsh_cmd_alias ()
+{
+       local -a list
+       list=(${${${(0)"$(git config -z --get-regexp '^alias\.')"}#alias.}%$'\n'*})
+       _describe -t alias-commands 'aliases' list $* && _ret=0
+}
+
+__git_zsh_cmd_all ()
+{
+       local -a list
+       emulate ksh -c __git_compute_all_commands
+       list=( ${=__git_all_commands} )
+       _describe -t all-commands 'all commands' list && _ret=0
+}
+
+__git_zsh_main ()
+{
+       local curcontext="$curcontext" state state_descr line
+       typeset -A opt_args
+       local -a orig_words
+
+       orig_words=( ${words[@]} )
+
+       _arguments -C \
+               '(-p --paginate --no-pager)'{-p,--paginate}'[pipe all output into ''less'']' \
+               '(-p --paginate)--no-pager[do not pipe git output into a pager]' \
+               '--git-dir=-[set the path to the repository]: :_directories' \
+               '--bare[treat the repository as a bare repository]' \
+               '(- :)--version[prints the git suite version]' \
+               '--exec-path=-[path to where your core git programs are installed]:: :_directories' \
+               '--html-path[print the path where git''s HTML documentation is installed]' \
+               '--info-path[print the path where the Info files are installed]' \
+               '--man-path[print the manpath (see `man(1)`) for the man pages]' \
+               '--work-tree=-[set the path to the working tree]: :_directories' \
+               '--namespace=-[set the git namespace]' \
+               '--no-replace-objects[do not use replacement refs to replace git objects]' \
+               '(- :)--help[prints the synopsis and a list of the most commonly used commands]: :->arg' \
+               '(-): :->command' \
+               '(-)*:: :->arg' && return
+
+       case $state in
+       (command)
+               _alternative \
+                         'alias-commands:alias:__git_zsh_cmd_alias' \
+                         'common-commands:common:__git_zsh_cmd_common' \
+                         'all-commands:all:__git_zsh_cmd_all' && _ret=0
+               ;;
+       (arg)
+               local command="${words[1]}" __git_dir
+
+               if (( $+opt_args[--bare] )); then
+                       __git_dir='.'
+               else
+                       __git_dir=${opt_args[--git-dir]}
+               fi
+
+               (( $+opt_args[--help] )) && command='help'
+
+               words=( ${orig_words[@]} )
+
+               __git_zsh_bash_func $command
+               ;;
+       esac
+}
+
 _git ()
 {
        local _ret=1
-       () {
-               emulate -L ksh
-               local cur cword prev
-               cur=${words[CURRENT-1]}
-               prev=${words[CURRENT-2]}
-               let cword=CURRENT-1
-               __${service}_main
-       }
-       let _ret && _default -S '' && _ret=0
+       local cur cword prev
+
+       cur=${words[CURRENT]}
+       prev=${words[CURRENT-1]}
+       let cword=CURRENT-1
+
+       if (( $+functions[__${service}_zsh_main] )); then
+               __${service}_zsh_main
+       else
+               emulate ksh -c __${service}_main
+       fi
+
+       let _ret && _default && _ret=0
        return _ret
 }
 
index 756a9514591c7664c82c93d3a6d50d91f46857d8..d6c61b2bdecc5ca89e21ece5df7c2e4bbec9373c 100644 (file)
@@ -3,7 +3,7 @@
 # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
 # Distributed under the GNU General Public License, version 2.0.
 #
-# This script allows you to see the current branch in your prompt.
+# This script allows you to see repository status in your prompt.
 #
 # To enable:
 #
 #    3a) Change your PS1 to call __git_ps1 as
 #        command-substitution:
 #        Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
-#        ZSH:  PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
+#        ZSH:  setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
 #        the optional argument will be used as format string.
-#    3b) Alternatively, if you are using bash, __git_ps1 can be
-#        used for PROMPT_COMMAND with two parameters, <pre> and
-#        <post>, which are strings you would put in $PS1 before
-#        and after the status string generated by the git-prompt
-#        machinery.  e.g.
-#           PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
-#        will show username, at-sign, host, colon, cwd, then
-#        various status string, followed by dollar and SP, as
-#        your prompt.
+#    3b) Alternatively, for a slightly faster prompt, __git_ps1 can
+#        be used for PROMPT_COMMAND in Bash or for precmd() in Zsh
+#        with two parameters, <pre> and <post>, which are strings
+#        you would put in $PS1 before and after the status string
+#        generated by the git-prompt machinery.  e.g.
+#        Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
+#          will show username, at-sign, host, colon, cwd, then
+#          various status string, followed by dollar and SP, as
+#          your prompt.
+#        ZSH:  precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
+#          will show username, pipe, then various status string,
+#          followed by colon, cwd, dollar and SP, as your prompt.
 #        Optionally, you can supply a third argument with a printf
 #        format string to finetune the output of the branch status
 #
-# The argument to __git_ps1 will be displayed only if you are currently
-# in a git repository.  The %s token will be the name of the current
-# branch.
+# The repository status will be displayed only if you are currently in a
+# git repository. The %s token is the placeholder for the shown status.
+#
+# The prompt status always includes the current branch name.
 #
 # In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
 # unstaged (*) and staged (+) changes will be shown next to the branch
 #
 # If you would like a colored hint about the current dirty state, set
 # GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb".
+# the colored output of "git status -sb" and are available only when
+# using __git_ps1 for PROMPT_COMMAND or precmd.
 
-# __gitdir accepts 0 or 1 arguments (i.e., location)
-# returns location of .git repo
-__gitdir ()
-{
-       # Note: this function is duplicated in git-completion.bash
-       # When updating it, make sure you update the other one to match.
-       if [ -z "${1-}" ]; then
-               if [ -n "${__git_dir-}" ]; then
-                       echo "$__git_dir"
-               elif [ -n "${GIT_DIR-}" ]; then
-                       test -d "${GIT_DIR-}" || return 1
-                       echo "$GIT_DIR"
-               elif [ -d .git ]; then
-                       echo .git
-               else
-                       git rev-parse --git-dir 2>/dev/null
-               fi
-       elif [ -d "$1/.git" ]; then
-               echo "$1/.git"
-       else
-               echo "$1"
-       fi
-}
+# check whether printf supports -v
+__git_printf_supports_v=
+printf -v __git_printf_supports_v -- '%s' yes >/dev/null 2>&1
 
 # stores the divergence from upstream in $p
 # used by GIT_PS1_SHOWUPSTREAM
@@ -124,7 +109,7 @@ __git_ps1_show_upstream ()
                        fi
                        ;;
                svn-remote.*.url)
-                       svn_remote[ $((${#svn_remote[@]} + 1)) ]="$value"
+                       svn_remote[$((${#svn_remote[@]} + 1))]="$value"
                        svn_url_pattern+="\\|$value"
                        upstream=svn+git # default upstream is SVN if available, else git
                        ;;
@@ -146,10 +131,11 @@ __git_ps1_show_upstream ()
        svn*)
                # get the upstream from the "git-svn-id: ..." in a commit message
                # (git-svn uses essentially the same procedure internally)
-               local svn_upstream=($(git log --first-parent -1 \
+               local -a svn_upstream
+               svn_upstream=($(git log --first-parent -1 \
                                        --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
                if [[ 0 -ne ${#svn_upstream[@]} ]]; then
-                       svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
+                       svn_upstream=${svn_upstream[${#svn_upstream[@]} - 2]}
                        svn_upstream=${svn_upstream%@*}
                        local n_stop="${#svn_remote[@]}"
                        for ((n=1; n <= n_stop; n++)); do
@@ -222,6 +208,51 @@ __git_ps1_show_upstream ()
 
 }
 
+# Helper function that is meant to be called from __git_ps1.  It
+# injects color codes into the appropriate gitstring variables used
+# to build a gitstring.
+__git_ps1_colorize_gitstring ()
+{
+       if [[ -n ${ZSH_VERSION-} ]]; then
+               local c_red='%F{red}'
+               local c_green='%F{green}'
+               local c_lblue='%F{blue}'
+               local c_clear='%f'
+       else
+               # Using \[ and \] around colors is necessary to prevent
+               # issues with command line editing/browsing/completion!
+               local c_red='\[\e[31m\]'
+               local c_green='\[\e[32m\]'
+               local c_lblue='\[\e[1;34m\]'
+               local c_clear='\[\e[0m\]'
+       fi
+       local bad_color=$c_red
+       local ok_color=$c_green
+       local flags_color="$c_lblue"
+
+       local branch_color=""
+       if [ $detached = no ]; then
+               branch_color="$ok_color"
+       else
+               branch_color="$bad_color"
+       fi
+       c="$branch_color$c"
+
+       z="$c_clear$z"
+       if [ "$w" = "*" ]; then
+               w="$bad_color$w"
+       fi
+       if [ -n "$i" ]; then
+               i="$ok_color$i"
+       fi
+       if [ -n "$s" ]; then
+               s="$flags_color$s"
+       fi
+       if [ -n "$u" ]; then
+               u="$bad_color$u"
+       fi
+       r="$c_clear$r"
+}
 
 # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
 # when called from PS1 using command substitution
@@ -254,41 +285,83 @@ __git_ps1 ()
                ;;
        esac
 
-       local g="$(__gitdir)"
-       if [ -z "$g" ]; then
+       local repo_info rev_parse_exit_code
+       repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
+               --is-bare-repository --is-inside-work-tree \
+               --short HEAD 2>/dev/null)"
+       rev_parse_exit_code="$?"
+
+       if [ -z "$repo_info" ]; then
                if [ $pcmode = yes ]; then
                        #In PC mode PS1 always needs to be set
                        PS1="$ps1pc_start$ps1pc_end"
                fi
-       else
-               local r=""
-               local b=""
+               return
+       fi
+
+       local short_sha
+       if [ "$rev_parse_exit_code" = "0" ]; then
+               short_sha="${repo_info##*$'\n'}"
+               repo_info="${repo_info%$'\n'*}"
+       fi
+       local inside_worktree="${repo_info##*$'\n'}"
+       repo_info="${repo_info%$'\n'*}"
+       local bare_repo="${repo_info##*$'\n'}"
+       repo_info="${repo_info%$'\n'*}"
+       local inside_gitdir="${repo_info##*$'\n'}"
+       local g="${repo_info%$'\n'*}"
+
+       local r=""
+       local b=""
+       local step=""
+       local total=""
+       if [ -d "$g/rebase-merge" ]; then
+               read b 2>/dev/null <"$g/rebase-merge/head-name"
+               read step 2>/dev/null <"$g/rebase-merge/msgnum"
+               read total 2>/dev/null <"$g/rebase-merge/end"
                if [ -f "$g/rebase-merge/interactive" ]; then
                        r="|REBASE-i"
-                       b="$(cat "$g/rebase-merge/head-name")"
-               elif [ -d "$g/rebase-merge" ]; then
+               else
                        r="|REBASE-m"
-                       b="$(cat "$g/rebase-merge/head-name")"
+               fi
+       else
+               if [ -d "$g/rebase-apply" ]; then
+                       read step 2>/dev/null <"$g/rebase-apply/next"
+                       read total 2>/dev/null <"$g/rebase-apply/last"
+                       if [ -f "$g/rebase-apply/rebasing" ]; then
+                               read b 2>/dev/null <"$g/rebase-apply/head-name"
+                               r="|REBASE"
+                       elif [ -f "$g/rebase-apply/applying" ]; then
+                               r="|AM"
+                       else
+                               r="|AM/REBASE"
+                       fi
+               elif [ -f "$g/MERGE_HEAD" ]; then
+                       r="|MERGING"
+               elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
+                       r="|CHERRY-PICKING"
+               elif [ -f "$g/REVERT_HEAD" ]; then
+                       r="|REVERTING"
+               elif [ -f "$g/BISECT_LOG" ]; then
+                       r="|BISECTING"
+               fi
+
+               if [ -n "$b" ]; then
+                       :
+               elif [ -h "$g/HEAD" ]; then
+                       # symlink symbolic ref
+                       b="$(git symbolic-ref HEAD 2>/dev/null)"
                else
-                       if [ -d "$g/rebase-apply" ]; then
-                               if [ -f "$g/rebase-apply/rebasing" ]; then
-                                       r="|REBASE"
-                               elif [ -f "$g/rebase-apply/applying" ]; then
-                                       r="|AM"
-                               else
-                                       r="|AM/REBASE"
+                       local head=""
+                       if ! read head 2>/dev/null <"$g/HEAD"; then
+                               if [ $pcmode = yes ]; then
+                                       PS1="$ps1pc_start$ps1pc_end"
                                fi
-                       elif [ -f "$g/MERGE_HEAD" ]; then
-                               r="|MERGING"
-                       elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
-                               r="|CHERRY-PICKING"
-                       elif [ -f "$g/REVERT_HEAD" ]; then
-                               r="|REVERTING"
-                       elif [ -f "$g/BISECT_LOG" ]; then
-                               r="|BISECTING"
+                               return
                        fi
-
-                       b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+                       # is it a symbolic ref?
+                       b="${head#ref: }"
+                       if [ "$head" = "$b" ]; then
                                detached=yes
                                b="$(
                                case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -302,100 +375,75 @@ __git_ps1 ()
                                        git describe --tags --exact-match HEAD ;;
                                esac 2>/dev/null)" ||
 
-                               b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
-                               b="unknown"
+                               b="$short_sha..."
                                b="($b)"
-                       }
+                       fi
                fi
+       fi
+
+       if [ -n "$step" ] && [ -n "$total" ]; then
+               r="$r $step/$total"
+       fi
 
-               local w=""
-               local i=""
-               local s=""
-               local u=""
-               local c=""
-               local p=""
+       local w=""
+       local i=""
+       local s=""
+       local u=""
+       local c=""
+       local p=""
 
-               if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
-                       if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
-                               c="BARE:"
+       if [ "true" = "$inside_gitdir" ]; then
+               if [ "true" = "$bare_repo" ]; then
+                       c="BARE:"
+               else
+                       b="GIT_DIR!"
+               fi
+       elif [ "true" = "$inside_worktree" ]; then
+               if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
+                  [ "$(git config --bool bash.showDirtyState)" != "false" ]
+               then
+                       git diff --no-ext-diff --quiet --exit-code || w="*"
+                       if [ -n "$short_sha" ]; then
+                               git diff-index --cached --quiet HEAD -- || i="+"
                        else
-                               b="GIT_DIR!"
-                       fi
-               elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
-                       if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
-                          [ "$(git config --bool bash.showDirtyState)" != "false" ]
-                       then
-                               git diff --no-ext-diff --quiet --exit-code || w="*"
-                               if git rev-parse --quiet --verify HEAD >/dev/null; then
-                                       git diff-index --cached --quiet HEAD -- || i="+"
-                               else
-                                       i="#"
-                               fi
-                       fi
-                       if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
-                               git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+                               i="#"
                        fi
+               fi
+               if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
+                  [ -r "$g/refs/stash" ]; then
+                       s="$"
+               fi
 
-                       if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
-                          [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
-                          [ -n "$(git ls-files --others --exclude-standard)" ]
-                       then
-                               u="%"
-                       fi
+               if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
+                  [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
+                  git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null
+               then
+                       u="%${ZSH_VERSION+%}"
+               fi
 
-                       if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
-                               __git_ps1_show_upstream
-                       fi
+               if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
+                       __git_ps1_show_upstream
                fi
+       fi
 
-               local f="$w$i$s$u"
-               if [ $pcmode = yes ]; then
-                       local gitstring=
-                       if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
-                               local c_red='\e[31m'
-                               local c_green='\e[32m'
-                               local c_lblue='\e[1;34m'
-                               local c_clear='\e[0m'
-                               local bad_color=$c_red
-                               local ok_color=$c_green
-                               local branch_color="$c_clear"
-                               local flags_color="$c_lblue"
-                               local branchstring="$c${b##refs/heads/}"
+       local z="${GIT_PS1_STATESEPARATOR-" "}"
 
-                               if [ $detached = no ]; then
-                                       branch_color="$ok_color"
-                               else
-                                       branch_color="$bad_color"
-                               fi
+       # NO color option unless in PROMPT_COMMAND mode
+       if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
+               __git_ps1_colorize_gitstring
+       fi
 
-                               # Setting gitstring directly with \[ and \] around colors
-                               # is necessary to prevent wrapping issues!
-                               gitstring="\[$branch_color\]$branchstring\[$c_clear\]"
+       local f="$w$i$s$u"
+       local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
 
-                               if [ -n "$w$i$s$u$r$p" ]; then
-                                       gitstring="$gitstring "
-                               fi
-                               if [ "$w" = "*" ]; then
-                                       gitstring="$gitstring\[$bad_color\]$w"
-                               fi
-                               if [ -n "$i" ]; then
-                                       gitstring="$gitstring\[$ok_color\]$i"
-                               fi
-                               if [ -n "$s" ]; then
-                                       gitstring="$gitstring\[$flags_color\]$s"
-                               fi
-                               if [ -n "$u" ]; then
-                                       gitstring="$gitstring\[$bad_color\]$u"
-                               fi
-                               gitstring="$gitstring\[$c_clear\]$r$p"
-                       else
-                               gitstring="$c${b##refs/heads/}${f:+ $f}$r$p"
-                       fi
+       if [ $pcmode = yes ]; then
+               if [ "${__git_printf_supports_v-}" != yes ]; then
                        gitstring=$(printf -- "$printf_format" "$gitstring")
-                       PS1="$ps1pc_start$gitstring$ps1pc_end"
                else
-                       # NO color option unless in PROMPT_COMMAND mode
-                       printf -- "$printf_format" "$c${b##refs/heads/}${f:+ $f}$r$p"
+                       printf -v gitstring -- "$printf_format" "$gitstring"
                fi
+               PS1="$ps1pc_start$gitstring$ps1pc_end"
+       else
+               printf -- "$printf_format" "$gitstring"
        fi
 }
diff --git a/contrib/contacts/git-contacts b/contrib/contacts/git-contacts
new file mode 100755 (executable)
index 0000000..d80f7d1
--- /dev/null
@@ -0,0 +1,188 @@
+#!/usr/bin/perl
+
+# List people who might be interested in a patch.  Useful as the argument to
+# git-send-email --cc-cmd option, and in other situations.
+#
+# Usage: git contacts <file | rev-list option> ...
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+my $since = '5-years-ago';
+my $min_percent = 10;
+my $labels_rx = qr/Signed-off-by|Reviewed-by|Acked-by|Cc/i;
+my %seen;
+
+sub format_contact {
+       my ($name, $email) = @_;
+       return "$name <$email>";
+}
+
+sub parse_commit {
+       my ($commit, $data) = @_;
+       my $contacts = $commit->{contacts};
+       my $inbody = 0;
+       for (split(/^/m, $data)) {
+               if (not $inbody) {
+                       if (/^author ([^<>]+) <(\S+)> .+$/) {
+                               $contacts->{format_contact($1, $2)} = 1;
+                       } elsif (/^$/) {
+                               $inbody = 1;
+                       }
+               } elsif (/^$labels_rx:\s+([^<>]+)\s+<(\S+?)>$/o) {
+                       $contacts->{format_contact($1, $2)} = 1;
+               }
+       }
+}
+
+sub import_commits {
+       my ($commits) = @_;
+       return unless %$commits;
+       my $pid = open2 my $reader, my $writer, qw(git cat-file --batch);
+       for my $id (keys(%$commits)) {
+               print $writer "$id\n";
+               my $line = <$reader>;
+               if ($line =~ /^([0-9a-f]{40}) commit (\d+)/) {
+                       my ($cid, $len) = ($1, $2);
+                       die "expected $id but got $cid\n" unless $id eq $cid;
+                       my $data;
+                       # cat-file emits newline after data, so read len+1
+                       read $reader, $data, $len + 1;
+                       parse_commit($commits->{$id}, $data);
+               }
+       }
+       close $reader;
+       close $writer;
+       waitpid($pid, 0);
+       die "git-cat-file error: $?\n" if $?;
+}
+
+sub get_blame {
+       my ($commits, $source, $start, $len, $from) = @_;
+       $len = 1 unless defined($len);
+       return if $len == 0;
+       open my $f, '-|',
+               qw(git blame --porcelain -C), '-L', "$start,+$len",
+               '--since', $since, "$from^", '--', $source or die;
+       while (<$f>) {
+               if (/^([0-9a-f]{40}) \d+ \d+ \d+$/) {
+                       my $id = $1;
+                       $commits->{$id} = { id => $id, contacts => {} }
+                               unless $seen{$id};
+                       $seen{$id} = 1;
+               }
+       }
+       close $f;
+}
+
+sub scan_patches {
+       my ($commits, $id, $f) = @_;
+       my $source;
+       while (<$f>) {
+               if (/^From ([0-9a-f]{40}) Mon Sep 17 00:00:00 2001$/) {
+                       $id = $1;
+                       $seen{$id} = 1;
+               }
+               next unless $id;
+               if (m{^--- (?:a/(.+)|/dev/null)$}) {
+                       $source = $1;
+               } elsif (/^--- /) {
+                       die "Cannot parse hunk source: $_\n";
+               } elsif (/^@@ -(\d+)(?:,(\d+))?/ && $source) {
+                       get_blame($commits, $source, $1, $2, $id);
+               }
+       }
+}
+
+sub scan_patch_file {
+       my ($commits, $file) = @_;
+       open my $f, '<', $file or die "read failure: $file: $!\n";
+       scan_patches($commits, undef, $f);
+       close $f;
+}
+
+sub parse_rev_args {
+       my @args = @_;
+       open my $f, '-|',
+               qw(git rev-parse --revs-only --default HEAD --symbolic), @args
+               or die;
+       my @revs;
+       while (<$f>) {
+               chomp;
+               push @revs, $_;
+       }
+       close $f;
+       return @revs if scalar(@revs) != 1;
+       return "^$revs[0]", 'HEAD' unless $revs[0] =~ /^-/;
+       return $revs[0], 'HEAD';
+}
+
+sub scan_rev_args {
+       my ($commits, $args) = @_;
+       my @revs = parse_rev_args(@$args);
+       open my $f, '-|', qw(git rev-list --reverse), @revs or die;
+       while (<$f>) {
+               chomp;
+               my $id = $_;
+               $seen{$id} = 1;
+               open my $g, '-|', qw(git show -C --oneline), $id or die;
+               scan_patches($commits, $id, $g);
+               close $g;
+       }
+       close $f;
+}
+
+sub mailmap_contacts {
+       my ($contacts) = @_;
+       my %mapped;
+       my $pid = open2 my $reader, my $writer, qw(git check-mailmap --stdin);
+       for my $contact (keys(%$contacts)) {
+               print $writer "$contact\n";
+               my $canonical = <$reader>;
+               chomp $canonical;
+               $mapped{$canonical} += $contacts->{$contact};
+       }
+       close $reader;
+       close $writer;
+       waitpid($pid, 0);
+       die "git-check-mailmap error: $?\n" if $?;
+       return \%mapped;
+}
+
+if (!@ARGV) {
+       die "No input revisions or patch files\n";
+}
+
+my (@files, @rev_args);
+for (@ARGV) {
+       if (-e) {
+               push @files, $_;
+       } else {
+               push @rev_args, $_;
+       }
+}
+
+my %commits;
+for (@files) {
+       scan_patch_file(\%commits, $_);
+}
+if (@rev_args) {
+       scan_rev_args(\%commits, \@rev_args)
+}
+import_commits(\%commits);
+
+my $contacts = {};
+for my $commit (values %commits) {
+       for my $contact (keys %{$commit->{contacts}}) {
+               $contacts->{$contact}++;
+       }
+}
+$contacts = mailmap_contacts($contacts);
+
+my $ncommits = scalar(keys %commits);
+for my $contact (keys %$contacts) {
+       my $percent = $contacts->{$contact} * 100 / $ncommits;
+       next if $percent < $min_percent;
+       print "$contact\n";
+}
diff --git a/contrib/contacts/git-contacts.txt b/contrib/contacts/git-contacts.txt
new file mode 100644 (file)
index 0000000..dd914d1
--- /dev/null
@@ -0,0 +1,94 @@
+git-contacts(1)
+===============
+
+NAME
+----
+git-contacts - List people who might be interested in a set of changes
+
+
+SYNOPSIS
+--------
+[verse]
+'git contacts' (<patch>|<range>|<rev>)...
+
+
+DESCRIPTION
+-----------
+
+Given a set of changes, specified as patch files or revisions, determine people
+who might be interested in those changes.  This is done by consulting the
+history of each patch or revision hunk to find people mentioned by commits
+which touched the lines of files under consideration.
+
+Input consists of one or more patch files or revision arguments.  A revision
+argument can be a range or a single `<rev>` which is interpreted as
+`<rev>..HEAD`, thus the same revision arguments are accepted as for
+linkgit:git-format-patch[1]. Patch files and revision arguments can be combined
+in the same invocation.
+
+This command can be useful for determining the list of people with whom to
+discuss proposed changes, or for finding the list of recipients to Cc: when
+submitting a patch series via `git send-email`. For the latter case, `git
+contacts` can be used as the argument to `git send-email`'s `--cc-cmd` option.
+
+
+DISCUSSION
+----------
+
+`git blame` is invoked for each hunk in a patch file or revision.  For each
+commit mentioned by `git blame`, the commit message is consulted for people who
+authored, reviewed, signed, acknowledged, or were Cc:'d.  Once the list of
+participants is known, each person's relevance is computed by considering how
+many commits mentioned that person compared with the total number of commits
+under consideration.  The final output consists only of participants who exceed
+a minimum threshold of participation.
+
+
+OUTPUT
+------
+
+For each person of interest, a single line is output, terminated by a newline.
+If the person's name is known, ``Name $$<user@host>$$'' is printed; otherwise
+only ``$$<user@host>$$'' is printed.
+
+
+EXAMPLES
+--------
+
+* Consult patch files:
++
+------------
+$ git contacts feature/*.patch
+------------
+
+* Revision range:
++
+------------
+$ git contacts R1..R2
+------------
+
+* From a single revision to `HEAD`:
++
+------------
+$ git contacts origin
+------------
+
+* Helper for `git send-email`:
++
+------------
+$ git send-email --cc-cmd='git contacts' feature/*.patch
+------------
+
+
+LIMITATIONS
+-----------
+
+Several conditions controlling a person's significance are currently
+hard-coded, such as minimum participation level (10%), blame date-limiting (5
+years), and `-C` level for detecting moved and copied lines (a single `-C`). In
+the future, these conditions may become configurable.
+
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/contrib/continuous/cidaemon b/contrib/continuous/cidaemon
deleted file mode 100644 (file)
index 4009a15..0000000
+++ /dev/null
@@ -1,503 +0,0 @@
-#!/usr/bin/perl
-#
-# A daemon that waits for update events sent by its companion
-# post-receive-cinotify hook, checks out a new copy of source,
-# compiles it, and emails the guilty parties if the compile
-# (and optionally test suite) fails.
-#
-# To use this daemon, configure it and run it.  It will disconnect
-# from your terminal and fork into the background.  The daemon must
-# have local filesystem access to the source repositories, as it
-# uses objects/info/alternates to avoid copying objects.
-#
-# Add its companion post-receive-cinotify hook as the post-receive
-# hook to each repository that the daemon should monitor.  Yes, a
-# single daemon can monitor more than one repository.
-#
-# To use multiple daemons on the same system, give them each a
-# unique queue file and tmpdir.
-#
-# Global Config
-# -------------
-# Reads from a Git style configuration file.  This will be
-# ~/.gitconfig by default but can be overridden by setting
-# the GIT_CONFIG_FILE environment variable before starting.
-#
-# cidaemon.smtpHost
-#   Hostname of the SMTP server the daemon will send email
-#   through.  Defaults to 'localhost'.
-#
-# cidaemon.smtpUser
-#   Username to authenticate to the SMTP server as.  This
-#   variable is optional; if it is not supplied then no
-#   authentication will be performed.
-#
-# cidaemon.smtpPassword
-#   Password to authenticate to the SMTP server as.  This
-#   variable is optional.  If not supplied but smtpUser was,
-#   the daemon prompts for the password before forking into
-#   the background.
-#
-# cidaemon.smtpAuth
-#   Type of authentication to perform with the SMTP server.
-#   If set to 'login' and smtpUser was defined, this will
-#   use the AUTH LOGIN command, which is suitable for use
-#   with at least one version of Microsoft Exchange Server.
-#   If not set the daemon will use whatever auth methods
-#   are supported by your version of Net::SMTP.
-#
-# cidaemon.email
-#   Email address that daemon generated emails will be sent
-#   from.  This should be a useful email address within your
-#   organization.  Required.
-#
-# cidaemon.name
-#   Human friendly name that the daemon will send emails as.
-#   Defaults to 'cidaemon'.
-#
-# cidaemon.scanDelay
-#   Number of seconds to sleep between polls of the queue file.
-#   Defaults to 60.
-#
-# cidaemon.recentCache
-#   Number of recent commit SHA-1s per repository to cache and
-#   skip building if they appear again.  This is useful to avoid
-#   rebuilding the same commit multiple times just because it was
-#   pushed into more than one branch.  Defaults to 100.
-#
-# cidaemon.tmpdir
-#   Scratch directory to create the builds within.  The daemon
-#   makes a new subdirectory for each build, then deletes it when
-#   the build has finished.  The pid file is also placed here.
-#   Defaults to '/tmp'.
-#
-# cidaemon.queue
-#   Path to the queue file that the post-receive-cinotify hook
-#   appends events to.  This file is polled by the daemon.  It
-#   must not be on an NFS mount (uses flock).  Required.
-#
-# cidaemon.nocc
-#   Perl regex patterns to match against author and committer
-#   lines.  If a pattern matches, that author or committer will
-#   not be notified of a build failure.
-#
-# Per Repository Config
-# ----------------------
-# Read from the source repository's config file.
-#
-# builder.command
-#   Shell command to execute the build.  This command must
-#   return 0 on "success" and non-zero on failure.  If you
-#   also want to run a test suite, make sure your command
-#   does that too.  Required.
-#
-# builder.queue
-#   Queue file to notify the cidaemon through.  Should match
-#   cidaemon.queue.  If not set the hook will not notify the
-#   cidaemon.
-#
-# builder.skip
-#   Perl regex patterns of refs that should not be sent to
-#   cidaemon.  Updates of these refs will be ignored.
-#
-# builder.newBranchBase
-#   Glob patterns of refs that should be used to form the
-#   'old' revions of a newly created ref.  This should set
-#   to be globs that match your 'mainline' branches.  This
-#   way a build failure of a brand new topic branch does not
-#   attempt to email everyone since the beginning of time;
-#   instead it only emails those authors of commits not in
-#   these 'mainline' branches.
-
-local $ENV{PATH} = join ':', qw(
-       /opt/git/bin
-       /usr/bin
-       /bin
-       );
-
-use strict;
-use warnings;
-use FindBin qw($RealBin);
-use File::Spec;
-use lib File::Spec->catfile($RealBin, '..', 'perl5');
-use Storable qw(retrieve nstore);
-use Fcntl ':flock';
-use POSIX qw(strftime);
-use Getopt::Long qw(:config no_auto_abbrev auto_help);
-
-sub git_config ($;$)
-{
-       my $var = shift;
-       my $required = shift || 0;
-       local *GIT;
-       open GIT, '-|','git','config','--get',$var;
-       my $r = <GIT>;
-       chop $r if $r;
-       close GIT;
-       die "error: $var not set.\n" if ($required && !$r);
-       return $r;
-}
-
-package EXCHANGE_NET_SMTP;
-
-# Microsoft Exchange Server requires an 'AUTH LOGIN'
-# style of authentication.  This is different from
-# the default supported by Net::SMTP so we subclass
-# and override the auth method to support that.
-
-use Net::SMTP;
-use Net::Cmd;
-use MIME::Base64 qw(encode_base64);
-our @ISA = qw(Net::SMTP);
-our $auth_type = ::git_config 'cidaemon.smtpAuth';
-
-sub new
-{
-       my $self = shift;
-       my $type = ref($self) || $self;
-       $type->SUPER::new(@_);
-}
-
-sub auth
-{
-       my $self = shift;
-       return $self->SUPER::auth(@_) unless $auth_type eq 'login';
-
-       my $user = encode_base64 shift, '';
-       my $pass = encode_base64 shift, '';
-       return 0 unless CMD_MORE == $self->command("AUTH LOGIN")->response;
-       return 0 unless CMD_MORE == $self->command($user)->response;
-       CMD_OK == $self->command($pass)->response;
-}
-
-package main;
-
-my ($debug_flag, %recent);
-
-my $ex_host = git_config('cidaemon.smtpHost') || 'localhost';
-my $ex_user = git_config('cidaemon.smtpUser');
-my $ex_pass = git_config('cidaemon.smtpPassword');
-
-my $ex_from_addr = git_config('cidaemon.email', 1);
-my $ex_from_name = git_config('cidaemon.name') || 'cidaemon';
-
-my $scan_delay = git_config('cidaemon.scanDelay') || 60;
-my $recent_size = git_config('cidaemon.recentCache') || 100;
-my $tmpdir = git_config('cidaemon.tmpdir') || '/tmp';
-my $queue_name = git_config('cidaemon.queue', 1);
-my $queue_lock = "$queue_name.lock";
-
-my @nocc_list;
-open GIT,'git config --get-all cidaemon.nocc|';
-while (<GIT>) {
-       chop;
-       push @nocc_list, $_;
-}
-close GIT;
-
-sub nocc_author ($)
-{
-       local $_ = shift;
-       foreach my $pat (@nocc_list) {
-               return 1 if /$pat/;
-       }
-       0;
-}
-
-sub input_echo ($)
-{
-       my $prompt = shift;
-
-       local $| = 1;
-       print $prompt;
-       my $input = <STDIN>;
-       chop $input;
-       return $input;
-}
-
-sub input_noecho ($)
-{
-       my $prompt = shift;
-
-       my $end = sub {system('stty','echo');print "\n";exit};
-       local $SIG{TERM} = $end;
-       local $SIG{INT} = $end;
-       system('stty','-echo');
-
-       local $| = 1;
-       print $prompt;
-       my $input = <STDIN>;
-       system('stty','echo');
-       print "\n";
-       chop $input;
-       return $input;
-}
-
-sub rfc2822_date ()
-{
-        strftime("%a, %d %b %Y %H:%M:%S %Z", localtime);
-}
-
-sub send_email ($$$)
-{
-       my ($subj, $body, $to) = @_;
-       my $now = rfc2822_date;
-       my $to_str = '';
-       my @rcpt_to;
-       foreach (@$to) {
-               my $s = $_;
-               $s =~ s/^/"/;
-               $s =~ s/(\s+<)/"$1/;
-               $to_str .= ', ' if $to_str;
-               $to_str .= $s;
-               push @rcpt_to, $1 if $s =~ /<(.*)>/;
-       }
-       die "Nobody to send to.\n" unless @rcpt_to;
-       my $msg = <<EOF;
-From: "$ex_from_name" <$ex_from_addr>
-To: $to_str
-Date: $now
-Subject: $subj
-
-$body
-EOF
-
-       my $smtp = EXCHANGE_NET_SMTP->new(Host => $ex_host)
-               or die "Cannot connect to $ex_host: $!\n";
-       if ($ex_user && $ex_pass) {
-               $smtp->auth($ex_user,$ex_pass)
-                       or die "$ex_host rejected $ex_user\n";
-       }
-       $smtp->mail($ex_from_addr)
-               or die "$ex_host rejected $ex_from_addr\n";
-       scalar($smtp->recipient(@rcpt_to, { SkipBad => 1 }))
-               or die "$ex_host did not accept any addresses.\n";
-       $smtp->data($msg)
-               or die "$ex_host rejected message data\n";
-       $smtp->quit;
-}
-
-sub pop_queue ()
-{
-       open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
-       flock LOCK, LOCK_EX;
-
-       my $queue = -f $queue_name ? retrieve $queue_name : [];
-       my $ent = shift @$queue;
-       nstore $queue, $queue_name;
-
-       flock LOCK, LOCK_UN;
-       close LOCK;
-       $ent;
-}
-
-sub git_exec (@)
-{
-       system('git',@_) == 0 or die "Cannot git " . join(' ', @_) . "\n";
-}
-
-sub git_val (@)
-{
-       open(C, '-|','git',@_);
-       my $r = <C>;
-       chop $r if $r;
-       close C;
-       $r;
-}
-
-sub do_build ($$)
-{
-       my ($git_dir, $new) = @_;
-
-       my $tmp = File::Spec->catfile($tmpdir, "builder$$");
-       system('rm','-rf',$tmp) == 0 or die "Cannot clear $tmp\n";
-       die "Cannot clear $tmp.\n" if -e $tmp;
-
-       my $result = 1;
-       eval {
-               my $command;
-               {
-                       local $ENV{GIT_DIR} = $git_dir;
-                       $command = git_val 'config','builder.command';
-               }
-               die "No builder.command for $git_dir.\n" unless $command;
-
-               git_exec 'clone','-n','-l','-s',$git_dir,$tmp;
-               chmod 0700, $tmp or die "Cannot lock $tmp\n";
-               chdir $tmp or die "Cannot enter $tmp\n";
-
-               git_exec 'update-ref','HEAD',$new;
-               git_exec 'read-tree','-m','-u','HEAD','HEAD';
-               system $command;
-               if ($? == -1) {
-                       print STDERR "failed to execute '$command': $!\n";
-                       $result = 1;
-               } elsif ($? & 127) {
-                       my $sig = $? & 127;
-                       print STDERR "'$command' died from signal $sig\n";
-                       $result = 1;
-               } else {
-                       my $r = $? >> 8;
-                       print STDERR "'$command' exited with $r\n" if $r;
-                       $result = $r;
-               }
-       };
-       if ($@) {
-               $result = 2;
-               print STDERR "$@\n";
-       }
-
-       chdir '/';
-       system('rm','-rf',$tmp);
-       rmdir $tmp;
-       $result;
-}
-
-sub build_failed ($$$$$)
-{
-       my ($git_dir, $ref, $old, $new, $msg) = @_;
-
-       $git_dir =~ m,/([^/]+)$,;
-       my $repo_name = $1;
-       $ref =~ s,^refs/(heads|tags)/,,;
-
-       my %authors;
-       my $shortlog;
-       my $revstr;
-       {
-               local $ENV{GIT_DIR} = $git_dir;
-               my @revs = ($new);
-               push @revs, '--not', @$old if @$old;
-               open LOG,'-|','git','rev-list','--pretty=raw',@revs;
-               while (<LOG>) {
-                       if (s/^(author|committer) //) {
-                               chomp;
-                               s/>.*$/>/;
-                               $authors{$_} = 1 unless nocc_author $_;
-                       }
-               }
-               close LOG;
-               open LOG,'-|','git','shortlog',@revs;
-               $shortlog .= $_ while <LOG>;
-               close LOG;
-               $revstr = join(' ', @revs);
-       }
-
-       my @to = sort keys %authors;
-       unless (@to) {
-               print STDERR "error: No authors in $revstr\n";
-               return;
-       }
-
-       my $subject = "[$repo_name] $ref : Build Failed";
-       my $body = <<EOF;
-Project: $git_dir
-Branch:  $ref
-Commits: $revstr
-
-$shortlog
-Build Output:
---------------------------------------------------------------
-$msg
-EOF
-       send_email($subject, $body, \@to);
-}
-
-sub run_build ($$$$)
-{
-       my ($git_dir, $ref, $old, $new) = @_;
-
-       if ($debug_flag) {
-               my @revs = ($new);
-               push @revs, '--not', @$old if @$old;
-               print "BUILDING $git_dir\n";
-               print "  BRANCH: $ref\n";
-               print "  COMMITS: ", join(' ', @revs), "\n";
-       }
-
-       local(*R, *W);
-       pipe R, W or die "cannot pipe builder: $!";
-
-       my $builder = fork();
-       if (!defined $builder) {
-               die "cannot fork builder: $!";
-       } elsif (0 == $builder) {
-               close R;
-               close STDIN;open(STDIN, '/dev/null');
-               open(STDOUT, '>&W');
-               open(STDERR, '>&W');
-               exit do_build $git_dir, $new;
-       } else {
-               close W;
-               my $out = '';
-               $out .= $_ while <R>;
-               close R;
-               waitpid $builder, 0;
-               build_failed $git_dir, $ref, $old, $new, $out if $?;
-       }
-
-       print "DONE\n\n" if $debug_flag;
-}
-
-sub daemon_loop ()
-{
-       my $run = 1;
-       my $stop_sub = sub {$run = 0};
-       $SIG{HUP} = $stop_sub;
-       $SIG{INT} = $stop_sub;
-       $SIG{TERM} = $stop_sub;
-
-       mkdir $tmpdir, 0755;
-       my $pidfile = File::Spec->catfile($tmpdir, "cidaemon.pid");
-       open(O, ">$pidfile"); print O "$$\n"; close O;
-
-       while ($run) {
-               my $ent = pop_queue;
-               if ($ent) {
-                       my ($git_dir, $ref, $old, $new) = @$ent;
-
-                       $ent = $recent{$git_dir};
-                       $recent{$git_dir} = $ent = [[], {}] unless $ent;
-                       my ($rec_arr, $rec_hash) = @$ent;
-                       next if $rec_hash->{$new}++;
-                       while (@$rec_arr >= $recent_size) {
-                               my $to_kill = shift @$rec_arr;
-                               delete $rec_hash->{$to_kill};
-                       }
-                       push @$rec_arr, $new;
-
-                       run_build $git_dir, $ref, $old, $new;
-               } else {
-                       sleep $scan_delay;
-               }
-       }
-
-       unlink $pidfile;
-}
-
-$debug_flag = 0;
-GetOptions(
-       'debug|d' => \$debug_flag,
-       'smtp-user=s' => \$ex_user,
-) or die "usage: $0 [--debug] [--smtp-user=user]\n";
-
-$ex_pass = input_noecho("$ex_user SMTP password: ")
-       if ($ex_user && !$ex_pass);
-
-if ($debug_flag) {
-       daemon_loop;
-       exit 0;
-}
-
-my $daemon = fork();
-if (!defined $daemon) {
-       die "cannot fork daemon: $!";
-} elsif (0 == $daemon) {
-       close STDIN;open(STDIN, '/dev/null');
-       close STDOUT;open(STDOUT, '>/dev/null');
-       close STDERR;open(STDERR, '>/dev/null');
-       daemon_loop;
-       exit 0;
-} else {
-       print "Daemon $daemon running in the background.\n";
-}
diff --git a/contrib/continuous/post-receive-cinotify b/contrib/continuous/post-receive-cinotify
deleted file mode 100644 (file)
index b8f5a60..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/perl
-#
-# A hook that notifies its companion cidaemon through a simple
-# queue file that a ref has been updated via a push (actually
-# by a receive-pack running on the server).
-#
-# See cidaemon for per-repository configuration details.
-#
-# To use this hook, add it as the post-receive hook, make it
-# executable, and set its configuration options.
-#
-
-local $ENV{PATH} = '/opt/git/bin';
-
-use strict;
-use warnings;
-use File::Spec;
-use Storable qw(retrieve nstore);
-use Fcntl ':flock';
-
-my $git_dir = File::Spec->rel2abs($ENV{GIT_DIR});
-my $queue_name = `git config --get builder.queue`;chop $queue_name;
-$queue_name =~ m,^([^\s]+)$,; $queue_name = $1; # untaint
-unless ($queue_name) {
-       1 while <STDIN>;
-       print STDERR "\nerror: builder.queue not set.  Not enqueing.\n\n";
-       exit;
-}
-my $queue_lock = "$queue_name.lock";
-
-my @skip;
-open S, "git config --get-all builder.skip|";
-while (<S>) {
-       chop;
-       push @skip, $_;
-}
-close S;
-
-my @new_branch_base;
-open S, "git config --get-all builder.newBranchBase|";
-while (<S>) {
-       chop;
-       push @new_branch_base, $_;
-}
-close S;
-
-sub skip ($)
-{
-       local $_ = shift;
-       foreach my $p (@skip) {
-               return 1 if /^$p/;
-       }
-       0;
-}
-
-open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
-flock LOCK, LOCK_EX;
-
-my $queue = -f $queue_name ? retrieve $queue_name : [];
-my %existing;
-foreach my $r (@$queue) {
-       my ($gd, $ref) = @$r;
-       $existing{$gd}{$ref} = $r;
-}
-
-my @new_branch_commits;
-my $loaded_new_branch_commits = 0;
-
-while (<STDIN>) {
-       chop;
-       my ($old, $new, $ref) = split / /, $_, 3;
-
-       next if $old eq $new;
-       next if $new =~ /^0{40}$/;
-       next if skip $ref;
-
-       my $r = $existing{$git_dir}{$ref};
-       if ($r) {
-               $r->[3] = $new;
-       } else {
-               if ($old =~ /^0{40}$/) {
-                       if (!$loaded_new_branch_commits && @new_branch_base) {
-                               open M,'-|','git','show-ref',@new_branch_base;
-                               while (<M>) {
-                                       ($_) = split / /, $_;
-                                       push @new_branch_commits, $_;
-                               }
-                               close M;
-                               $loaded_new_branch_commits = 1;
-                       }
-                       $old = [@new_branch_commits];
-               } else {
-                       $old = [$old];
-               }
-
-               $r = [$git_dir, $ref, $old, $new];
-               $existing{$git_dir}{$ref} = $r;
-               push @$queue, $r;
-       }
-}
-nstore $queue, $queue_name;
-
-flock LOCK, LOCK_UN;
-close LOCK;
index 3940202b36ce00a77bdc2e17a6002dac158afcb6..bcd3f575a3e38611a88a8a0fcd51d39b9edb1599 100644 (file)
@@ -127,10 +127,20 @@ static void read_credential(void)
                *v++ = '\0';
 
                if (!strcmp(buf, "protocol")) {
-                       if (!strcmp(v, "https"))
+                       if (!strcmp(v, "imap"))
+                               protocol = kSecProtocolTypeIMAP;
+                       else if (!strcmp(v, "imaps"))
+                               protocol = kSecProtocolTypeIMAPS;
+                       else if (!strcmp(v, "ftp"))
+                               protocol = kSecProtocolTypeFTP;
+                       else if (!strcmp(v, "ftps"))
+                               protocol = kSecProtocolTypeFTPS;
+                       else if (!strcmp(v, "https"))
                                protocol = kSecProtocolTypeHTTPS;
                        else if (!strcmp(v, "http"))
                                protocol = kSecProtocolTypeHTTP;
+                       else if (!strcmp(v, "smtp"))
+                               protocol = kSecProtocolTypeSMTP;
                        else /* we don't yet handle other protocols */
                                exit(0);
                }
index 232625a7b72aef88ef9cc0285eb7208ce38a2138..60dec86d37efbffb557e611cde4bf2b2dbfbf478 100755 (executable)
@@ -225,7 +225,7 @@ for cset in range(int(tip) + 1):
     os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin')
 
     # commit
-    os.system(getgitenv(user, date) + 'git commit --allow-empty -a -F %s' % filecomment)
+    os.system(getgitenv(user, date) + 'git commit --allow-empty --allow-empty-message -a -F %s' % filecomment)
     os.unlink(filecomment)
 
     # tag
diff --git a/contrib/hooks/multimail/README b/contrib/hooks/multimail/README
new file mode 100644 (file)
index 0000000..9904396
--- /dev/null
@@ -0,0 +1,486 @@
+                          git-multimail
+                          =============
+
+git-multimail is a tool for sending notification emails on pushes to a
+Git repository.  It includes a Python module called git_multimail.py,
+which can either be used as a hook script directly or can be imported
+as a Python module into another script.
+
+git-multimail is derived from the Git project's old
+contrib/hooks/post-receive-email, and is mostly compatible with that
+script.  See README.migrate-from-post-receive-email for details about
+the differences and for how to migrate from post-receive-email to
+git-multimail.
+
+git-multimail, like the rest of the Git project, is licensed under
+GPLv2 (see the COPYING file for details).
+
+Please note: although, as a convenience, git-multimail may be
+distributed along with the main Git project, development of
+git-multimail takes place in its own, separate project.  See section
+"Getting involved" below for more information.
+
+
+By default, for each push received by the repository, git-multimail:
+
+1. Outputs one email summarizing each reference that was changed.
+   These "reference change" (called "refchange" below) emails describe
+   the nature of the change (e.g., was the reference created, deleted,
+   fast-forwarded, etc.) and include a one-line summary of each commit
+   that was added to the reference.
+
+2. Outputs one email for each new commit that was introduced by the
+   reference change.  These "commit" emails include a list of the
+   files changed by the commit, followed by the diffs of files
+   modified by the commit.  The commit emails are threaded to the
+   corresponding reference change email via "In-Reply-To".  This style
+   (similar to the "git format-patch" style used on the Git mailing
+   list) makes it easy to scan through the emails, jump to patches
+   that need further attention, and write comments about specific
+   commits.  Commits are handled in reverse topological order (i.e.,
+   parents shown before children).  For example,
+
+   [git] branch master updated
+   + [git] 01/08: doc: fix xref link from api docs to manual pages
+   + [git] 02/08: api-credentials.txt: show the big picture first
+   + [git] 03/08: api-credentials.txt: mention credential.helper explicitly
+   + [git] 04/08: api-credentials.txt: add "see also" section
+   + [git] 05/08: t3510 (cherry-pick-sequence): add missing '&&'
+   + [git] 06/08: Merge branch 'rr/maint-t3510-cascade-fix'
+   + [git] 07/08: Merge branch 'mm/api-credentials-doc'
+   + [git] 08/08: Git 1.7.11-rc2
+
+   Each commit appears in exactly one commit email, the first time
+   that it is pushed to the repository.  If a commit is later merged
+   into another branch, then a one-line summary of the commit is
+   included in the reference change email (as usual), but no
+   additional commit email is generated.
+
+   By default, reference change emails have their "Reply-To" field set
+   to the person who pushed the change, and commit emails have their
+   "Reply-To" field set to the author of the commit.
+
+3. Output one "announce" mail for each new annotated tag, including
+   information about the tag and optionally a shortlog describing the
+   changes since the previous tag.  Such emails might be useful if you
+   use annotated tags to mark releases of your project.
+
+
+Requirements
+------------
+
+* Python 2.x, version 2.4 or later.  No non-standard Python modules
+  are required.  git-multimail does *not* currently work with Python
+  3.x.
+
+  The example scripts invoke Python using the following shebang line
+  (following PEP 394 [1]):
+
+      #! /usr/bin/env python2
+
+  If your system's Python2 interpreter is not in your PATH or is not
+  called "python2", you can change the lines accordingly.  Or you can
+  invoke the Python interpreter explicitly, for example via a tiny
+  shell script like
+
+      #! /bin/sh
+      /usr/local/bin/python /path/to/git_multimail.py "$@"
+
+* The "git" command must be in your PATH.  git-multimail is known to
+  work with Git versions back to 1.7.1.  (Earlier versions have not
+  been tested; if you do so, please report your results.)
+
+* To send emails using the default configuration, a standard sendmail
+  program must be located at '/usr/sbin/sendmail' and configured
+  correctly to send emails.  If this is not the case, see the
+  multimailhook.mailer configuration variable below for how to
+  configure git-multimail to send emails via an SMTP server.
+
+
+Invocation
+----------
+
+git_multimail.py is designed to be used as a "post-receive" hook in a
+Git repository (see githooks(5)).  Link or copy it to
+$GIT_DIR/hooks/post-receive within the repository for which email
+notifications are desired.  Usually it should be installed on the
+central repository for a project, to which all commits are eventually
+pushed.
+
+For use on pre-v1.5.1 Git servers, git_multimail.py can also work as
+an "update" hook, taking its arguments on the command line.  To use
+this script in this manner, link or copy it to $GIT_DIR/hooks/update.
+Please note that the script is not completely reliable in this mode
+[2].
+
+Alternatively, git_multimail.py can be imported as a Python module
+into your own Python post-receive script.  This method is a bit more
+work, but allows the behavior of the hook to be customized using
+arbitrary Python code.  For example, you can use a custom environment
+(perhaps inheriting from GenericEnvironment or GitoliteEnvironment) to
+
+* change how the user who did the push is determined
+
+* read users' email addresses from an LDAP server or from a database
+
+* decide which users should be notified about which commits based on
+  the contents of the commits (e.g., for users who want to be notified
+  only about changes affecting particular files or subdirectories)
+
+Or you can change how emails are sent by writing your own Mailer
+class.  The "post-receive" script in this directory demonstrates how
+to use git_multimail.py as a Python module.  (If you make interesting
+changes of this type, please consider sharing them with the
+community.)
+
+
+Configuration
+-------------
+
+By default, git-multimail mostly takes its configuration from the
+following "git config" settings:
+
+multimailhook.environment
+
+    This describes the general environment of the repository.
+    Currently supported values:
+
+    "generic" -- the username of the pusher is read from $USER and the
+        repository name is derived from the repository's path.
+
+    "gitolite" -- the username of the pusher is read from $GL_USER and
+        the repository name from $GL_REPO.
+
+    If neither of these environments is suitable for your setup, then
+    you can implement a Python class that inherits from Environment
+    and instantiate it via a script that looks like the example
+    post-receive script.
+
+    The environment value can be specified on the command line using
+    the --environment option.  If it is not specified on the command
+    line or by multimailhook.environment, then it defaults to
+    "gitolite" if the environment contains variables $GL_USER and
+    $GL_REPO; otherwise "generic".
+
+multimailhook.repoName
+
+    A short name of this Git repository, to be used in various places
+    in the notification email text.  The default is to use $GL_REPO
+    for gitolite repositories, or otherwise to derive this value from
+    the repository path name.
+
+multimailhook.mailinglist
+
+    The list of email addresses to which notification emails should be
+    sent, as RFC 2822 email addresses separated by commas.  This
+    configuration option can be multivalued.  Leave it unset or set it
+    to the empty string to not send emails by default.  The next few
+    settings can be used to configure specific address lists for
+    specific types of notification email.
+
+multimailhook.refchangeList
+
+    The list of email addresses to which summary emails about
+    reference changes should be sent, as RFC 2822 email addresses
+    separated by commas.  This configuration option can be
+    multivalued.  The default is the value in
+    multimailhook.mailinglist.  Set this value to the empty string to
+    prevent reference change emails from being sent.
+
+multimailhook.announceList
+
+    The list of email addresses to which emails about new annotated
+    tags should be sent, as RFC 2822 email addresses separated by
+    commas.  This configuration option can be multivalued.  The
+    default is the value in multimailhook.refchangelist or
+    multimailhook.mailinglist.  Set this value to the empty string to
+    prevent annotated tag announcement emails from being sent.
+
+multimailhook.commitList
+
+    The list of email addresses to which emails about individual new
+    commits should be sent, as RFC 2822 email addresses separated by
+    commas.  This configuration option can be multivalued.  The
+    default is the value in multimailhook.mailinglist.  Set this value
+    to the empty string to prevent notification emails about
+    individual commits from being sent.
+
+multimailhook.announceShortlog
+
+    If this option is set to true, then emails about changes to
+    annotated tags include a shortlog of changes since the previous
+    tag.  This can be useful if the annotated tags represent releases;
+    then the shortlog will be a kind of rough summary of what has
+    happened since the last release.  But if your tagging policy is
+    not so straightforward, then the shortlog might be confusing
+    rather than useful.  Default is false.
+
+multimailhook.refchangeShowLog
+
+    If this option is set to true, then summary emails about reference
+    changes will include a detailed log of the added commits in
+    addition to the one line summary.  The log is generated by running
+    "git log" with the options specified in multimailhook.logOpts.
+    Default is false.
+
+multimailhook.mailer
+
+    This option changes the way emails are sent.  Accepted values are:
+
+    - sendmail (the default): use the command /usr/sbin/sendmail or
+      /usr/lib/sendmail (or sendmailCommand, if configured).  This
+      mode can be further customized via the following options:
+
+       multimailhook.sendmailCommand
+
+           The command used by mailer "sendmail" to send emails.  Shell
+           quoting is allowed in the value of this setting, but remember that
+           Git requires double-quotes to be escaped; e.g.,
+
+             git config multimailhook.sendmailcommand '/usr/sbin/sendmail -t -F \"Git Repo\"'
+
+           Default is '/usr/sbin/sendmail -t' or '/usr/lib/sendmail
+           -t' (depending on which file is present and executable).
+
+       multimailhook.envelopeSender
+
+           If set then pass this value to sendmail via the -f option to set
+           the envelope sender address.
+
+    - smtp: use Python's smtplib.  This is useful when the sendmail
+      command is not available on the system.  This mode can be
+      further customized via the following options:
+
+       multimailhook.smtpServer
+
+           The name of the SMTP server to connect to.  The value can
+           also include a colon and a port number; e.g.,
+           "mail.example.com:25".  Default is 'localhost' using port
+           25.
+
+       multimailhook.envelopeSender
+
+           The sender address to be passed to the SMTP server.  If
+           unset, then the value of multimailhook.from is used.
+
+multimailhook.from
+
+    If set then use this value in the From: field of generated emails.
+    If unset, then use the repository's user configuration (user.name
+    and user.email).  If user.email is also unset, then use
+    multimailhook.envelopeSender.
+
+multimailhook.administrator
+
+    The name and/or email address of the administrator of the Git
+    repository; used in FOOTER_TEMPLATE.  Default is
+    multimailhook.envelopesender if it is set; otherwise a generic
+    string is used.
+
+multimailhook.emailPrefix
+
+    All emails have this string prepended to their subjects, to aid
+    email filtering (though filtering based on the X-Git-* email
+    headers is probably more robust).  Default is the short name of
+    the repository in square brackets; e.g., "[myrepo]".
+
+multimailhook.emailMaxLines
+
+    The maximum number of lines that should be included in the body of
+    a generated email.  If not specified, there is no limit.  Lines
+    beyond the limit are suppressed and counted, and a final line is
+    added indicating the number of suppressed lines.
+
+multimailhook.emailMaxLineLength
+
+    The maximum length of a line in the email body.  Lines longer than
+    this limit are truncated to this length with a trailing " [...]"
+    added to indicate the missing text.  The default is 500, because
+    (a) diffs with longer lines are probably from binary files, for
+    which a diff is useless, and (b) even if a text file has such long
+    lines, the diffs are probably unreadable anyway.  To disable line
+    truncation, set this option to 0.
+
+multimailhook.maxCommitEmails
+
+    The maximum number of commit emails to send for a given change.
+    When the number of patches is larger that this value, only the
+    summary refchange email is sent.  This can avoid accidental
+    mailbombing, for example on an initial push.  To disable commit
+    emails limit, set this option to 0.  The default is 500.
+
+multimailhook.emailStrictUTF8
+
+    If this boolean option is set to "true", then the main part of the
+    email body is forced to be valid UTF-8.  Any characters that are
+    not valid UTF-8 are converted to the Unicode replacement
+    character, U+FFFD.  The default is "true".
+
+multimailhook.diffOpts
+
+    Options passed to "git diff-tree" when generating the summary
+    information for ReferenceChange emails.  Default is "--stat
+    --summary --find-copies-harder".  Add -p to those options to
+    include a unified diff of changes in addition to the usual summary
+    output.  Shell quoting is allowed; see multimailhook.logOpts for
+    details.
+
+multimailhook.logOpts
+
+    Options passed to "git log" to generate additional info for
+    reference change emails (used only if refchangeShowLog is set).
+    For example, adding --graph will show the graph of revisions, -p
+    will show the complete diff, etc.  The default is empty.
+
+    Shell quoting is allowed; for example, a log format that contains
+    spaces can be specified using something like:
+
+      git config multimailhook.logopts '--pretty=format:"%h %aN <%aE>%n%s%n%n%b%n"'
+
+    If you want to set this by editing your configuration file
+    directly, remember that Git requires double-quotes to be escaped
+    (see git-config(1) for more information):
+
+      [multimailhook]
+              logopts = --pretty=format:\"%h %aN <%aE>%n%s%n%n%b%n\"
+
+multimailhook.emailDomain
+
+    Domain name appended to the username of the person doing the push
+    to convert it into an email address (via "%s@%s" % (username,
+    emaildomain)).  More complicated schemes can be implemented by
+    overriding Environment and overriding its get_pusher_email()
+    method.
+
+multimailhook.replyTo
+multimailhook.replyToCommit
+multimailhook.replyToRefchange
+
+    Addresses to use in the Reply-To: field for commit emails
+    (replyToCommit) and refchange emails (replyToRefchange).
+    multimailhook.replyTo is used as default when replyToCommit or
+    replyToRefchange is not set.  The value for these variables can be
+    either:
+
+    - An email address, which will be used directly.
+
+    - The value "pusher", in which case the pusher's address (if
+      available) will be used.  This is the default for refchange
+      emails.
+
+    - The value "author" (meaningful only for replyToCommit), in which
+      case the commit author's address will be used.  This is the
+      default for commit emails.
+
+    - The value "none", in which case the Reply-To: field will be
+      omitted.
+
+
+Email filtering aids
+--------------------
+
+All emails include extra headers to enable fine tuned filtering and
+give information for debugging.  All emails include the headers
+"X-Git-Repo", "X-Git-Refname", and "X-Git-Reftype".  ReferenceChange
+emails also include headers "X-Git-Oldrev" and "X-Git-Newrev";
+Revision emails also include header "X-Git-Rev".
+
+
+Customizing email contents
+--------------------------
+
+git-multimail mostly generates emails by expanding templates.  The
+templates can be customized.  To avoid the need to edit
+git_multimail.py directly, the preferred way to change the templates
+is to write a separate Python script that imports git_multimail.py as
+a module, then replaces the templates in place.  See the provided
+post-receive script for an example of how this is done.
+
+
+Customizing git-multimail for your environment
+----------------------------------------------
+
+git-multimail is mostly customized via an "environment" that describes
+the local environment in which Git is running.  Two types of
+environment are built in:
+
+* GenericEnvironment: a stand-alone Git repository.
+
+* GitoliteEnvironment: a Git repository that is managed by gitolite
+  [3].  For such repositories, the identity of the pusher is read from
+  environment variable $GL_USER, and the name of the repository is
+  read from $GL_REPO (if it is not overridden by
+  multimailhook.reponame).
+
+By default, git-multimail assumes GitoliteEnvironment if $GL_USER and
+$GL_REPO are set, and otherwise assumes GenericEnvironment.
+Alternatively, you can choose one of these two environments explicitly
+by setting a "multimailhook.environment" config setting (which can
+have the value "generic" or "gitolite") or by passing an --environment
+option to the script.
+
+If you need to customize the script in ways that are not supported by
+the existing environments, you can define your own environment class
+class using arbitrary Python code.  To do so, you need to import
+git_multimail.py as a Python module, as demonstrated by the example
+post-receive script.  Then implement your environment class; it should
+usually inherit from one of the existing Environment classes and
+possibly one or more of the EnvironmentMixin classes.  Then set the
+"environment" variable to an instance of your own environment class
+and pass it to run_as_post_receive_hook().
+
+The standard environment classes, GenericEnvironment and
+GitoliteEnvironment, are in fact themselves put together out of a
+number of mixin classes, each of which handles one aspect of the
+customization.  For the finest control over your configuration, you
+can specify exactly which mixin classes your own environment class
+should inherit from, and override individual methods (or even add your
+own mixin classes) to implement entirely new behaviors.  If you
+implement any mixins that might be useful to other people, please
+consider sharing them with the community!
+
+
+Getting involved
+----------------
+
+git-multimail is an open-source project, built by volunteers.  We
+would welcome your help!
+
+The current maintainer is Michael Haggerty <mhagger@alum.mit.edu>.
+
+General discussion of git-multimail takes place on the main Git
+mailing list,
+
+    git@vger.kernel.org
+
+Please CC emails regarding git-multimail to me so that I don't
+overlook them.
+
+The git-multimail project itself is currently hosted on GitHub:
+
+    https://github.com/mhagger/git-multimail
+
+We use the GitHub issue tracker to keep track of bugs and feature
+requests, and GitHub pull requests to exchange patches (though, if you
+prefer, you can send patches via the Git mailing list with cc to me).
+
+Please note that although a copy of git-multimail will probably be
+distributed in the "contrib" section of the main Git project,
+development takes place in the separate git-multimail repository on
+GitHub!  (Whenever enough changes to git-multimail have accumulated, a
+new code-drop of git-multimail will be submitted for inclusion in the
+Git project.)
+
+
+Footnotes
+---------
+
+[1] http://www.python.org/dev/peps/pep-0394/
+
+[2] Because of the way information is passed to update hooks, the
+    script's method of determining whether a commit has already been
+    seen does not work when it is used as an "update" script.  In
+    particular, no notification email will be generated for a new
+    commit that is added to multiple references in the same push.
+
+[3] https://github.com/sitaramc/gitolite
diff --git a/contrib/hooks/multimail/README.Git b/contrib/hooks/multimail/README.Git
new file mode 100644 (file)
index 0000000..9c2e66a
--- /dev/null
@@ -0,0 +1,15 @@
+This copy of git-multimail is distributed as part of the "contrib"
+section of the Git project as a convenience to Git users.
+git-multimail is developed as an independent project at the following
+website:
+
+    https://github.com/mhagger/git-multimail
+
+The version in this directory was obtained from the upstream project
+on 2013-07-14 and consists of the "git-multimail" subdirectory from
+revision
+
+    1a5cb09c698a74d15a715a86b09ead5f56bf4b06
+
+Please see the README file in this directory for information about how
+to report bugs or contribute to git-multimail.
diff --git a/contrib/hooks/multimail/README.migrate-from-post-receive-email b/contrib/hooks/multimail/README.migrate-from-post-receive-email
new file mode 100644 (file)
index 0000000..1e6a976
--- /dev/null
@@ -0,0 +1,145 @@
+git-multimail is close to, but not exactly, a plug-in replacement for
+the old Git project script contrib/hooks/post-receive-email.  This
+document describes the differences and explains how to configure
+git-multimail to get behavior closest to that of post-receive-email.
+
+If you are in a hurry
+=====================
+
+A script called migrate-mailhook-config is included with
+git-multimail.  If you run this script within a Git repository that is
+configured to use post-receive-email, it will convert the
+configuration settings into the approximate equivalent settings for
+git-multimail.  For more information, run
+
+    migrate-mailhook-config --help
+
+
+Configuration differences
+=========================
+
+* The names of the config options for git-multimail are in namespace
+  "multimailhook.*" instead of "hooks.*".  (Editorial comment:
+  post-receive-email should never have used such a generic top-level
+  namespace.)
+
+* In emails about new annotated tags, post-receive-email includes a
+  shortlog of all changes since the previous annotated tag.  To get
+  this behavior with git-multimail, you need to set
+  multimailhook.announceshortlog to true:
+
+      git config multimailhook.announceshortlog true
+
+* multimailhook.commitlist -- This is a new configuration variable.
+  Recipients listed here will receive a separate email for each new
+  commit.  However, if this variable is *not* set, it defaults to the
+  value of multimailhook.mailinglist.  Therefore, if you *don't* want
+  the members of multimailhook.mailinglist to receive one email per
+  commit, then set this value to the empty string:
+
+      git config multimailhook.commitlist ''
+
+* multimailhook.emailprefix -- If this value is not set, then the
+  subjects of generated emails are prefixed with the short name of the
+  repository enclosed in square brackets; e.g., "[myrepo]".
+  post-receive-email defaults to prefix "[SCM]" if this option is not
+  set.  So if you were using the old default and want to retain it
+  (for example, to avoid having to change your email filters), set
+  this variable explicitly to the old value:
+
+      git config multimailhook.emailprefix "[SCM]"
+
+* The "multimailhook.showrev" configuration option is not supported.
+  Its main use is obsoleted by the one-email-per-commit feature of
+  git-multimail.
+
+
+Other differences
+=================
+
+This section describes other differences in the behavior of
+git-multimail vs. post-receive-email.  For full details, please refer
+to the main README file:
+
+* One email per commit.  For each reference change, the script first
+  outputs one email summarizing the reference change (including
+  one-line summaries of the new commits), then it outputs a separate
+  email for each new commit that was introduced, including patches.
+  These one-email-per-commit emails go to the addresses listed in
+  multimailhook.commitlist.  post-receive-email sends only one email
+  for each *reference* that is changed, no matter how many commits
+  were added to the reference.
+
+* Better algorithm for detecting new commits.  post-receive-email
+  processes one reference change at a time, which causes it to fail to
+  describe new commits that were included in multiple branches.  For
+  example, if a single push adds the "*" commits in the diagram below,
+  then post-receive-email would never include the details of the two
+  commits that are common to "master" and "branch" in its
+  notifications.
+
+      o---o---o---*---*---*    <-- master
+                       \
+                        *---*  <-- branch
+
+  git-multimail analyzes all reference modifications to determine
+  which commits were not present before the change, therefore avoiding
+  that error.
+
+* In reference change emails, git-multimail tells which commits have
+  been added to the reference vs. are entirely new to the repository,
+  and which commits that have been omitted from the reference
+  vs. entirely discarded from the repository.
+
+* The environment in which Git is running can be configured via an
+  "Environment" abstraction.
+
+* Built-in support for Gitolite-managed repositories.
+
+* Instead of using full SHA1 object names in emails, git-multimail
+  mostly uses abbreviated SHA1s, plus one-line log message summaries
+  where appropriate.
+
+* In the schematic diagrams that explain non-fast-forward commits,
+  git-multimail shows the names of the branches involved.
+
+* The emails generated by git-multimail include the name of the Git
+  repository that was modified; this is convenient for recipients who
+  are monitoring multiple repositories.
+
+* git-multimail allows the email "From" addresses to be configured.
+
+* The recipients lists (multimailhook.mailinglist,
+  multimailhook.refchangelist, multimailhook.announcelist, and
+  multimailhook.commitlist) can be comma-separated values and/or
+  multivalued settings in the config file; e.g.,
+
+      [multimailhook]
+              mailinglist = mr.brown@example.com, mr.black@example.com
+              announcelist = Him <him@example.com>
+              announcelist = Jim <jim@example.com>
+              announcelist = pop@example.com
+
+  This might make it easier to maintain short recipients lists without
+  requiring full-fledged mailing list software.
+
+* By default, git-multimail sets email "Reply-To" headers to reply to
+  the pusher (for reference updates) and to the author (for commit
+  notifications).  By default, the pusher's email address is
+  constructed by appending "multimailhook.emaildomain" to the pusher's
+  username.
+
+* The generated emails contain a configurable footer.  By default, it
+  lists the name of the administrator who should be contacted to
+  unsubscribe from notification emails.
+
+* New option multimailhook.emailmaxlinelength to limit the length of
+  lines in the main part of the email body.  The default limit is 500
+  characters.
+
+* New option multimailhook.emailstrictutf8 to ensure that the main
+  part of the email body is valid UTF-8.  Invalid characters are
+  turned into the Unicode replacement character, U+FFFD.  By default
+  this option is turned on.
+
+* Written in Python.  Easier to add new features.
diff --git a/contrib/hooks/multimail/git_multimail.py b/contrib/hooks/multimail/git_multimail.py
new file mode 100755 (executable)
index 0000000..81c6a51
--- /dev/null
@@ -0,0 +1,2393 @@
+#! /usr/bin/env python2
+
+# Copyright (c) 2012,2013 Michael Haggerty
+# Derived from contrib/hooks/post-receive-email, which is
+# Copyright (c) 2007 Andy Parkins
+# and also includes contributions by other authors.
+#
+# This file is part of git-multimail.
+#
+# git-multimail is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version
+# 2 as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""Generate notification emails for pushes to a git repository.
+
+This hook sends emails describing changes introduced by pushes to a
+git repository.  For each reference that was changed, it emits one
+ReferenceChange email summarizing how the reference was changed,
+followed by one Revision email for each new commit that was introduced
+by the reference change.
+
+Each commit is announced in exactly one Revision email.  If the same
+commit is merged into another branch in the same or a later push, then
+the ReferenceChange email will list the commit's SHA1 and its one-line
+summary, but no new Revision email will be generated.
+
+This script is designed to be used as a "post-receive" hook in a git
+repository (see githooks(5)).  It can also be used as an "update"
+script, but this usage is not completely reliable and is deprecated.
+
+To help with debugging, this script accepts a --stdout option, which
+causes the emails to be written to standard output rather than sent
+using sendmail.
+
+See the accompanying README file for the complete documentation.
+
+"""
+
+import sys
+import os
+import re
+import bisect
+import subprocess
+import shlex
+import optparse
+import smtplib
+
+try:
+    from email.utils import make_msgid
+    from email.utils import getaddresses
+    from email.utils import formataddr
+    from email.header import Header
+except ImportError:
+    # Prior to Python 2.5, the email module used different names:
+    from email.Utils import make_msgid
+    from email.Utils import getaddresses
+    from email.Utils import formataddr
+    from email.Header import Header
+
+
+DEBUG = False
+
+ZEROS = '0' * 40
+LOGBEGIN = '- Log -----------------------------------------------------------------\n'
+LOGEND = '-----------------------------------------------------------------------\n'
+
+
+# It is assumed in many places that the encoding is uniformly UTF-8,
+# so changing these constants is unsupported.  But define them here
+# anyway, to make it easier to find (at least most of) the places
+# where the encoding is important.
+(ENCODING, CHARSET) = ('UTF-8', 'utf-8')
+
+
+REF_CREATED_SUBJECT_TEMPLATE = (
+    '%(emailprefix)s%(refname_type)s %(short_refname)s created'
+    ' (now %(newrev_short)s)'
+    )
+REF_UPDATED_SUBJECT_TEMPLATE = (
+    '%(emailprefix)s%(refname_type)s %(short_refname)s updated'
+    ' (%(oldrev_short)s -> %(newrev_short)s)'
+    )
+REF_DELETED_SUBJECT_TEMPLATE = (
+    '%(emailprefix)s%(refname_type)s %(short_refname)s deleted'
+    ' (was %(oldrev_short)s)'
+    )
+
+REFCHANGE_HEADER_TEMPLATE = """\
+To: %(recipients)s
+Subject: %(subject)s
+MIME-Version: 1.0
+Content-Type: text/plain; charset=%(charset)s
+Content-Transfer-Encoding: 8bit
+Message-ID: %(msgid)s
+From: %(fromaddr)s
+Reply-To: %(reply_to)s
+X-Git-Repo: %(repo_shortname)s
+X-Git-Refname: %(refname)s
+X-Git-Reftype: %(refname_type)s
+X-Git-Oldrev: %(oldrev)s
+X-Git-Newrev: %(newrev)s
+Auto-Submitted: auto-generated
+"""
+
+REFCHANGE_INTRO_TEMPLATE = """\
+This is an automated email from the git hooks/post-receive script.
+
+%(pusher)s pushed a change to %(refname_type)s %(short_refname)s
+in repository %(repo_shortname)s.
+
+"""
+
+
+FOOTER_TEMPLATE = """\
+
+-- \n\
+To stop receiving notification emails like this one, please contact
+%(administrator)s.
+"""
+
+
+REWIND_ONLY_TEMPLATE = """\
+This update removed existing revisions from the reference, leaving the
+reference pointing at a previous point in the repository history.
+
+ * -- * -- N   %(refname)s (%(newrev_short)s)
+            \\
+             O -- O -- O   (%(oldrev_short)s)
+
+Any revisions marked "omits" are not gone; other references still
+refer to them.  Any revisions marked "discards" are gone forever.
+"""
+
+
+NON_FF_TEMPLATE = """\
+This update added new revisions after undoing existing revisions.
+That is to say, some revisions that were in the old version of the
+%(refname_type)s are not in the new version.  This situation occurs
+when a user --force pushes a change and generates a repository
+containing something like this:
+
+ * -- * -- B -- O -- O -- O   (%(oldrev_short)s)
+            \\
+             N -- N -- N   %(refname)s (%(newrev_short)s)
+
+You should already have received notification emails for all of the O
+revisions, and so the following emails describe only the N revisions
+from the common base, B.
+
+Any revisions marked "omits" are not gone; other references still
+refer to them.  Any revisions marked "discards" are gone forever.
+"""
+
+
+NO_NEW_REVISIONS_TEMPLATE = """\
+No new revisions were added by this update.
+"""
+
+
+DISCARDED_REVISIONS_TEMPLATE = """\
+This change permanently discards the following revisions:
+"""
+
+
+NO_DISCARDED_REVISIONS_TEMPLATE = """\
+The revisions that were on this %(refname_type)s are still contained in
+other references; therefore, this change does not discard any commits
+from the repository.
+"""
+
+
+NEW_REVISIONS_TEMPLATE = """\
+The %(tot)s revisions listed above as "new" are entirely new to this
+repository and will be described in separate emails.  The revisions
+listed as "adds" were already present in the repository and have only
+been added to this reference.
+
+"""
+
+
+TAG_CREATED_TEMPLATE = """\
+        at  %(newrev_short)-9s (%(newrev_type)s)
+"""
+
+
+TAG_UPDATED_TEMPLATE = """\
+*** WARNING: tag %(short_refname)s was modified! ***
+
+      from  %(oldrev_short)-9s (%(oldrev_type)s)
+        to  %(newrev_short)-9s (%(newrev_type)s)
+"""
+
+
+TAG_DELETED_TEMPLATE = """\
+*** WARNING: tag %(short_refname)s was deleted! ***
+
+"""
+
+
+# The template used in summary tables.  It looks best if this uses the
+# same alignment as TAG_CREATED_TEMPLATE and TAG_UPDATED_TEMPLATE.
+BRIEF_SUMMARY_TEMPLATE = """\
+%(action)10s  %(rev_short)-9s %(text)s
+"""
+
+
+NON_COMMIT_UPDATE_TEMPLATE = """\
+This is an unusual reference change because the reference did not
+refer to a commit either before or after the change.  We do not know
+how to provide full information about this reference change.
+"""
+
+
+REVISION_HEADER_TEMPLATE = """\
+To: %(recipients)s
+Subject: %(emailprefix)s%(num)02d/%(tot)02d: %(oneline)s
+MIME-Version: 1.0
+Content-Type: text/plain; charset=%(charset)s
+Content-Transfer-Encoding: 8bit
+From: %(fromaddr)s
+Reply-To: %(reply_to)s
+In-Reply-To: %(reply_to_msgid)s
+References: %(reply_to_msgid)s
+X-Git-Repo: %(repo_shortname)s
+X-Git-Refname: %(refname)s
+X-Git-Reftype: %(refname_type)s
+X-Git-Rev: %(rev)s
+Auto-Submitted: auto-generated
+"""
+
+REVISION_INTRO_TEMPLATE = """\
+This is an automated email from the git hooks/post-receive script.
+
+%(pusher)s pushed a commit to %(refname_type)s %(short_refname)s
+in repository %(repo_shortname)s.
+
+"""
+
+
+REVISION_FOOTER_TEMPLATE = FOOTER_TEMPLATE
+
+
+class CommandError(Exception):
+    def __init__(self, cmd, retcode):
+        self.cmd = cmd
+        self.retcode = retcode
+        Exception.__init__(
+            self,
+            'Command "%s" failed with retcode %s' % (' '.join(cmd), retcode,)
+            )
+
+
+class ConfigurationException(Exception):
+    pass
+
+
+def read_git_output(args, input=None, keepends=False, **kw):
+    """Read the output of a Git command."""
+
+    return read_output(
+        ['git', '-c', 'i18n.logoutputencoding=%s' % (ENCODING,)] + args,
+        input=input, keepends=keepends, **kw
+        )
+
+
+def read_output(cmd, input=None, keepends=False, **kw):
+    if input:
+        stdin = subprocess.PIPE
+    else:
+        stdin = None
+    p = subprocess.Popen(
+        cmd, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kw
+        )
+    (out, err) = p.communicate(input)
+    retcode = p.wait()
+    if retcode:
+        raise CommandError(cmd, retcode)
+    if not keepends:
+        out = out.rstrip('\n\r')
+    return out
+
+
+def read_git_lines(args, keepends=False, **kw):
+    """Return the lines output by Git command.
+
+    Return as single lines, with newlines stripped off."""
+
+    return read_git_output(args, keepends=True, **kw).splitlines(keepends)
+
+
+class Config(object):
+    def __init__(self, section, git_config=None):
+        """Represent a section of the git configuration.
+
+        If git_config is specified, it is passed to "git config" in
+        the GIT_CONFIG environment variable, meaning that "git config"
+        will read the specified path rather than the Git default
+        config paths."""
+
+        self.section = section
+        if git_config:
+            self.env = os.environ.copy()
+            self.env['GIT_CONFIG'] = git_config
+        else:
+            self.env = None
+
+    @staticmethod
+    def _split(s):
+        """Split NUL-terminated values."""
+
+        words = s.split('\0')
+        assert words[-1] == ''
+        return words[:-1]
+
+    def get(self, name, default=None):
+        try:
+            values = self._split(read_git_output(
+                    ['config', '--get', '--null', '%s.%s' % (self.section, name)],
+                    env=self.env, keepends=True,
+                    ))
+            assert len(values) == 1
+            return values[0]
+        except CommandError:
+            return default
+
+    def get_bool(self, name, default=None):
+        try:
+            value = read_git_output(
+                ['config', '--get', '--bool', '%s.%s' % (self.section, name)],
+                env=self.env,
+                )
+        except CommandError:
+            return default
+        return value == 'true'
+
+    def get_all(self, name, default=None):
+        """Read a (possibly multivalued) setting from the configuration.
+
+        Return the result as a list of values, or default if the name
+        is unset."""
+
+        try:
+            return self._split(read_git_output(
+                ['config', '--get-all', '--null', '%s.%s' % (self.section, name)],
+                env=self.env, keepends=True,
+                ))
+        except CommandError, e:
+            if e.retcode == 1:
+                # "the section or key is invalid"; i.e., there is no
+                # value for the specified key.
+                return default
+            else:
+                raise
+
+    def get_recipients(self, name, default=None):
+        """Read a recipients list from the configuration.
+
+        Return the result as a comma-separated list of email
+        addresses, or default if the option is unset.  If the setting
+        has multiple values, concatenate them with comma separators."""
+
+        lines = self.get_all(name, default=None)
+        if lines is None:
+            return default
+        return ', '.join(line.strip() for line in lines)
+
+    def set(self, name, value):
+        read_git_output(
+            ['config', '%s.%s' % (self.section, name), value],
+            env=self.env,
+            )
+
+    def add(self, name, value):
+        read_git_output(
+            ['config', '--add', '%s.%s' % (self.section, name), value],
+            env=self.env,
+            )
+
+    def has_key(self, name):
+        return self.get_all(name, default=None) is not None
+
+    def unset_all(self, name):
+        try:
+            read_git_output(
+                ['config', '--unset-all', '%s.%s' % (self.section, name)],
+                env=self.env,
+                )
+        except CommandError, e:
+            if e.retcode == 5:
+                # The name doesn't exist, which is what we wanted anyway...
+                pass
+            else:
+                raise
+
+    def set_recipients(self, name, value):
+        self.unset_all(name)
+        for pair in getaddresses([value]):
+            self.add(name, formataddr(pair))
+
+
+def generate_summaries(*log_args):
+    """Generate a brief summary for each revision requested.
+
+    log_args are strings that will be passed directly to "git log" as
+    revision selectors.  Iterate over (sha1_short, subject) for each
+    commit specified by log_args (subject is the first line of the
+    commit message as a string without EOLs)."""
+
+    cmd = [
+        'log', '--abbrev', '--format=%h %s',
+        ] + list(log_args) + ['--']
+    for line in read_git_lines(cmd):
+        yield tuple(line.split(' ', 1))
+
+
+def limit_lines(lines, max_lines):
+    for (index, line) in enumerate(lines):
+        if index < max_lines:
+            yield line
+
+    if index >= max_lines:
+        yield '... %d lines suppressed ...\n' % (index + 1 - max_lines,)
+
+
+def limit_linelength(lines, max_linelength):
+    for line in lines:
+        # Don't forget that lines always include a trailing newline.
+        if len(line) > max_linelength + 1:
+            line = line[:max_linelength - 7] + ' [...]\n'
+        yield line
+
+
+class CommitSet(object):
+    """A (constant) set of object names.
+
+    The set should be initialized with full SHA1 object names.  The
+    __contains__() method returns True iff its argument is an
+    abbreviation of any the names in the set."""
+
+    def __init__(self, names):
+        self._names = sorted(names)
+
+    def __len__(self):
+        return len(self._names)
+
+    def __contains__(self, sha1_abbrev):
+        """Return True iff this set contains sha1_abbrev (which might be abbreviated)."""
+
+        i = bisect.bisect_left(self._names, sha1_abbrev)
+        return i < len(self) and self._names[i].startswith(sha1_abbrev)
+
+
+class GitObject(object):
+    def __init__(self, sha1, type=None):
+        if sha1 == ZEROS:
+            self.sha1 = self.type = self.commit_sha1 = None
+        else:
+            self.sha1 = sha1
+            self.type = type or read_git_output(['cat-file', '-t', self.sha1])
+
+            if self.type == 'commit':
+                self.commit_sha1 = self.sha1
+            elif self.type == 'tag':
+                try:
+                    self.commit_sha1 = read_git_output(
+                        ['rev-parse', '--verify', '%s^0' % (self.sha1,)]
+                        )
+                except CommandError:
+                    # Cannot deref tag to determine commit_sha1
+                    self.commit_sha1 = None
+            else:
+                self.commit_sha1 = None
+
+        self.short = read_git_output(['rev-parse', '--short', sha1])
+
+    def get_summary(self):
+        """Return (sha1_short, subject) for this commit."""
+
+        if not self.sha1:
+            raise ValueError('Empty commit has no summary')
+
+        return iter(generate_summaries('--no-walk', self.sha1)).next()
+
+    def __eq__(self, other):
+        return isinstance(other, GitObject) and self.sha1 == other.sha1
+
+    def __hash__(self):
+        return hash(self.sha1)
+
+    def __nonzero__(self):
+        return bool(self.sha1)
+
+    def __str__(self):
+        return self.sha1 or ZEROS
+
+
+class Change(object):
+    """A Change that has been made to the Git repository.
+
+    Abstract class from which both Revisions and ReferenceChanges are
+    derived.  A Change knows how to generate a notification email
+    describing itself."""
+
+    def __init__(self, environment):
+        self.environment = environment
+        self._values = None
+
+    def _compute_values(self):
+        """Return a dictionary {keyword : expansion} for this Change.
+
+        Derived classes overload this method to add more entries to
+        the return value.  This method is used internally by
+        get_values().  The return value should always be a new
+        dictionary."""
+
+        return self.environment.get_values()
+
+    def get_values(self, **extra_values):
+        """Return a dictionary {keyword : expansion} for this Change.
+
+        Return a dictionary mapping keywords to the values that they
+        should be expanded to for this Change (used when interpolating
+        template strings).  If any keyword arguments are supplied, add
+        those to the return value as well.  The return value is always
+        a new dictionary."""
+
+        if self._values is None:
+            self._values = self._compute_values()
+
+        values = self._values.copy()
+        if extra_values:
+            values.update(extra_values)
+        return values
+
+    def expand(self, template, **extra_values):
+        """Expand template.
+
+        Expand the template (which should be a string) using string
+        interpolation of the values for this Change.  If any keyword
+        arguments are provided, also include those in the keywords
+        available for interpolation."""
+
+        return template % self.get_values(**extra_values)
+
+    def expand_lines(self, template, **extra_values):
+        """Break template into lines and expand each line."""
+
+        values = self.get_values(**extra_values)
+        for line in template.splitlines(True):
+            yield line % values
+
+    def expand_header_lines(self, template, **extra_values):
+        """Break template into lines and expand each line as an RFC 2822 header.
+
+        Encode values and split up lines that are too long.  Silently
+        skip lines that contain references to unknown variables."""
+
+        values = self.get_values(**extra_values)
+        for line in template.splitlines():
+            (name, value) = line.split(':', 1)
+
+            try:
+                value = value % values
+            except KeyError, e:
+                if DEBUG:
+                    sys.stderr.write(
+                        'Warning: unknown variable %r in the following line; line skipped:\n'
+                        '    %s\n'
+                        % (e.args[0], line,)
+                        )
+            else:
+                try:
+                    h = Header(value, header_name=name)
+                except UnicodeDecodeError:
+                    h = Header(value, header_name=name, charset=CHARSET, errors='replace')
+                for splitline in ('%s: %s\n' % (name, h.encode(),)).splitlines(True):
+                    yield splitline
+
+    def generate_email_header(self):
+        """Generate the RFC 2822 email headers for this Change, a line at a time.
+
+        The output should not include the trailing blank line."""
+
+        raise NotImplementedError()
+
+    def generate_email_intro(self):
+        """Generate the email intro for this Change, a line at a time.
+
+        The output will be used as the standard boilerplate at the top
+        of the email body."""
+
+        raise NotImplementedError()
+
+    def generate_email_body(self):
+        """Generate the main part of the email body, a line at a time.
+
+        The text in the body might be truncated after a specified
+        number of lines (see multimailhook.emailmaxlines)."""
+
+        raise NotImplementedError()
+
+    def generate_email_footer(self):
+        """Generate the footer of the email, a line at a time.
+
+        The footer is always included, irrespective of
+        multimailhook.emailmaxlines."""
+
+        raise NotImplementedError()
+
+    def generate_email(self, push, body_filter=None):
+        """Generate an email describing this change.
+
+        Iterate over the lines (including the header lines) of an
+        email describing this change.  If body_filter is not None,
+        then use it to filter the lines that are intended for the
+        email body."""
+
+        for line in self.generate_email_header():
+            yield line
+        yield '\n'
+        for line in self.generate_email_intro():
+            yield line
+
+        body = self.generate_email_body(push)
+        if body_filter is not None:
+            body = body_filter(body)
+        for line in body:
+            yield line
+
+        for line in self.generate_email_footer():
+            yield line
+
+
+class Revision(Change):
+    """A Change consisting of a single git commit."""
+
+    def __init__(self, reference_change, rev, num, tot):
+        Change.__init__(self, reference_change.environment)
+        self.reference_change = reference_change
+        self.rev = rev
+        self.change_type = self.reference_change.change_type
+        self.refname = self.reference_change.refname
+        self.num = num
+        self.tot = tot
+        self.author = read_git_output(['log', '--no-walk', '--format=%aN <%aE>', self.rev.sha1])
+        self.recipients = self.environment.get_revision_recipients(self)
+
+    def _compute_values(self):
+        values = Change._compute_values(self)
+
+        oneline = read_git_output(
+            ['log', '--format=%s', '--no-walk', self.rev.sha1]
+            )
+
+        values['rev'] = self.rev.sha1
+        values['rev_short'] = self.rev.short
+        values['change_type'] = self.change_type
+        values['refname'] = self.refname
+        values['short_refname'] = self.reference_change.short_refname
+        values['refname_type'] = self.reference_change.refname_type
+        values['reply_to_msgid'] = self.reference_change.msgid
+        values['num'] = self.num
+        values['tot'] = self.tot
+        values['recipients'] = self.recipients
+        values['oneline'] = oneline
+        values['author'] = self.author
+
+        reply_to = self.environment.get_reply_to_commit(self)
+        if reply_to:
+            values['reply_to'] = reply_to
+
+        return values
+
+    def generate_email_header(self):
+        for line in self.expand_header_lines(REVISION_HEADER_TEMPLATE):
+            yield line
+
+    def generate_email_intro(self):
+        for line in self.expand_lines(REVISION_INTRO_TEMPLATE):
+            yield line
+
+    def generate_email_body(self, push):
+        """Show this revision."""
+
+        return read_git_lines(
+            [
+                'log', '-C',
+                 '--stat', '-p', '--cc',
+                '-1', self.rev.sha1,
+                ],
+            keepends=True,
+            )
+
+    def generate_email_footer(self):
+        return self.expand_lines(REVISION_FOOTER_TEMPLATE)
+
+
+class ReferenceChange(Change):
+    """A Change to a Git reference.
+
+    An abstract class representing a create, update, or delete of a
+    Git reference.  Derived classes handle specific types of reference
+    (e.g., tags vs. branches).  These classes generate the main
+    reference change email summarizing the reference change and
+    whether it caused any any commits to be added or removed.
+
+    ReferenceChange objects are usually created using the static
+    create() method, which has the logic to decide which derived class
+    to instantiate."""
+
+    REF_RE = re.compile(r'^refs\/(?P<area>[^\/]+)\/(?P<shortname>.*)$')
+
+    @staticmethod
+    def create(environment, oldrev, newrev, refname):
+        """Return a ReferenceChange object representing the change.
+
+        Return an object that represents the type of change that is being
+        made. oldrev and newrev should be SHA1s or ZEROS."""
+
+        old = GitObject(oldrev)
+        new = GitObject(newrev)
+        rev = new or old
+
+        # The revision type tells us what type the commit is, combined with
+        # the location of the ref we can decide between
+        #  - working branch
+        #  - tracking branch
+        #  - unannotated tag
+        #  - annotated tag
+        m = ReferenceChange.REF_RE.match(refname)
+        if m:
+            area = m.group('area')
+            short_refname = m.group('shortname')
+        else:
+            area = ''
+            short_refname = refname
+
+        if rev.type == 'tag':
+            # Annotated tag:
+            klass = AnnotatedTagChange
+        elif rev.type == 'commit':
+            if area == 'tags':
+                # Non-annotated tag:
+                klass = NonAnnotatedTagChange
+            elif area == 'heads':
+                # Branch:
+                klass = BranchChange
+            elif area == 'remotes':
+                # Tracking branch:
+                sys.stderr.write(
+                    '*** Push-update of tracking branch %r\n'
+                    '***  - incomplete email generated.\n'
+                     % (refname,)
+                    )
+                klass = OtherReferenceChange
+            else:
+                # Some other reference namespace:
+                sys.stderr.write(
+                    '*** Push-update of strange reference %r\n'
+                    '***  - incomplete email generated.\n'
+                     % (refname,)
+                    )
+                klass = OtherReferenceChange
+        else:
+            # Anything else (is there anything else?)
+            sys.stderr.write(
+                '*** Unknown type of update to %r (%s)\n'
+                '***  - incomplete email generated.\n'
+                 % (refname, rev.type,)
+                )
+            klass = OtherReferenceChange
+
+        return klass(
+            environment,
+            refname=refname, short_refname=short_refname,
+            old=old, new=new, rev=rev,
+            )
+
+    def __init__(self, environment, refname, short_refname, old, new, rev):
+        Change.__init__(self, environment)
+        self.change_type = {
+            (False, True) : 'create',
+            (True, True) : 'update',
+            (True, False) : 'delete',
+            }[bool(old), bool(new)]
+        self.refname = refname
+        self.short_refname = short_refname
+        self.old = old
+        self.new = new
+        self.rev = rev
+        self.msgid = make_msgid()
+        self.diffopts = environment.diffopts
+        self.logopts = environment.logopts
+        self.showlog = environment.refchange_showlog
+
+    def _compute_values(self):
+        values = Change._compute_values(self)
+
+        values['change_type'] = self.change_type
+        values['refname_type'] = self.refname_type
+        values['refname'] = self.refname
+        values['short_refname'] = self.short_refname
+        values['msgid'] = self.msgid
+        values['recipients'] = self.recipients
+        values['oldrev'] = str(self.old)
+        values['oldrev_short'] = self.old.short
+        values['newrev'] = str(self.new)
+        values['newrev_short'] = self.new.short
+
+        if self.old:
+            values['oldrev_type'] = self.old.type
+        if self.new:
+            values['newrev_type'] = self.new.type
+
+        reply_to = self.environment.get_reply_to_refchange(self)
+        if reply_to:
+            values['reply_to'] = reply_to
+
+        return values
+
+    def get_subject(self):
+        template = {
+            'create' : REF_CREATED_SUBJECT_TEMPLATE,
+            'update' : REF_UPDATED_SUBJECT_TEMPLATE,
+            'delete' : REF_DELETED_SUBJECT_TEMPLATE,
+            }[self.change_type]
+        return self.expand(template)
+
+    def generate_email_header(self):
+        for line in self.expand_header_lines(
+            REFCHANGE_HEADER_TEMPLATE, subject=self.get_subject(),
+            ):
+            yield line
+
+    def generate_email_intro(self):
+        for line in self.expand_lines(REFCHANGE_INTRO_TEMPLATE):
+            yield line
+
+    def generate_email_body(self, push):
+        """Call the appropriate body-generation routine.
+
+        Call one of generate_create_summary() /
+        generate_update_summary() / generate_delete_summary()."""
+
+        change_summary = {
+            'create' : self.generate_create_summary,
+            'delete' : self.generate_delete_summary,
+            'update' : self.generate_update_summary,
+            }[self.change_type](push)
+        for line in change_summary:
+            yield line
+
+        for line in self.generate_revision_change_summary(push):
+            yield line
+
+    def generate_email_footer(self):
+        return self.expand_lines(FOOTER_TEMPLATE)
+
+    def generate_revision_change_log(self, new_commits_list):
+        if self.showlog:
+            yield '\n'
+            yield 'Detailed log of new commits:\n\n'
+            for line in read_git_lines(
+                    ['log', '--no-walk']
+                    + self.logopts
+                    + new_commits_list
+                    + ['--'],
+                    keepends=True,
+                ):
+                yield line
+
+    def generate_revision_change_summary(self, push):
+        """Generate a summary of the revisions added/removed by this change."""
+
+        if self.new.commit_sha1 and not self.old.commit_sha1:
+            # A new reference was created.  List the new revisions
+            # brought by the new reference (i.e., those revisions that
+            # were not in the repository before this reference
+            # change).
+            sha1s = list(push.get_new_commits(self))
+            sha1s.reverse()
+            tot = len(sha1s)
+            new_revisions = [
+                Revision(self, GitObject(sha1), num=i+1, tot=tot)
+                for (i, sha1) in enumerate(sha1s)
+                ]
+
+            if new_revisions:
+                yield self.expand('This %(refname_type)s includes the following new commits:\n')
+                yield '\n'
+                for r in new_revisions:
+                    (sha1, subject) = r.rev.get_summary()
+                    yield r.expand(
+                        BRIEF_SUMMARY_TEMPLATE, action='new', text=subject,
+                        )
+                yield '\n'
+                for line in self.expand_lines(NEW_REVISIONS_TEMPLATE, tot=tot):
+                    yield line
+                for line in self.generate_revision_change_log([r.rev.sha1 for r in new_revisions]):
+                    yield line
+            else:
+                for line in self.expand_lines(NO_NEW_REVISIONS_TEMPLATE):
+                    yield line
+
+        elif self.new.commit_sha1 and self.old.commit_sha1:
+            # A reference was changed to point at a different commit.
+            # List the revisions that were removed and/or added *from
+            # that reference* by this reference change, along with a
+            # diff between the trees for its old and new values.
+
+            # List of the revisions that were added to the branch by
+            # this update.  Note this list can include revisions that
+            # have already had notification emails; we want such
+            # revisions in the summary even though we will not send
+            # new notification emails for them.
+            adds = list(generate_summaries(
+                    '--topo-order', '--reverse', '%s..%s'
+                    % (self.old.commit_sha1, self.new.commit_sha1,)
+                    ))
+
+            # List of the revisions that were removed from the branch
+            # by this update.  This will be empty except for
+            # non-fast-forward updates.
+            discards = list(generate_summaries(
+                    '%s..%s' % (self.new.commit_sha1, self.old.commit_sha1,)
+                    ))
+
+            if adds:
+                new_commits_list = push.get_new_commits(self)
+            else:
+                new_commits_list = []
+            new_commits = CommitSet(new_commits_list)
+
+            if discards:
+                discarded_commits = CommitSet(push.get_discarded_commits(self))
+            else:
+                discarded_commits = CommitSet([])
+
+            if discards and adds:
+                for (sha1, subject) in discards:
+                    if sha1 in discarded_commits:
+                        action = 'discards'
+                    else:
+                        action = 'omits'
+                    yield self.expand(
+                        BRIEF_SUMMARY_TEMPLATE, action=action,
+                        rev_short=sha1, text=subject,
+                        )
+                for (sha1, subject) in adds:
+                    if sha1 in new_commits:
+                        action = 'new'
+                    else:
+                        action = 'adds'
+                    yield self.expand(
+                        BRIEF_SUMMARY_TEMPLATE, action=action,
+                        rev_short=sha1, text=subject,
+                        )
+                yield '\n'
+                for line in self.expand_lines(NON_FF_TEMPLATE):
+                    yield line
+
+            elif discards:
+                for (sha1, subject) in discards:
+                    if sha1 in discarded_commits:
+                        action = 'discards'
+                    else:
+                        action = 'omits'
+                    yield self.expand(
+                        BRIEF_SUMMARY_TEMPLATE, action=action,
+                        rev_short=sha1, text=subject,
+                        )
+                yield '\n'
+                for line in self.expand_lines(REWIND_ONLY_TEMPLATE):
+                    yield line
+
+            elif adds:
+                (sha1, subject) = self.old.get_summary()
+                yield self.expand(
+                    BRIEF_SUMMARY_TEMPLATE, action='from',
+                    rev_short=sha1, text=subject,
+                    )
+                for (sha1, subject) in adds:
+                    if sha1 in new_commits:
+                        action = 'new'
+                    else:
+                        action = 'adds'
+                    yield self.expand(
+                        BRIEF_SUMMARY_TEMPLATE, action=action,
+                        rev_short=sha1, text=subject,
+                        )
+
+            yield '\n'
+
+            if new_commits:
+                for line in self.expand_lines(NEW_REVISIONS_TEMPLATE, tot=len(new_commits)):
+                    yield line
+                for line in self.generate_revision_change_log(new_commits_list):
+                    yield line
+            else:
+                for line in self.expand_lines(NO_NEW_REVISIONS_TEMPLATE):
+                    yield line
+
+            # The diffstat is shown from the old revision to the new
+            # revision.  This is to show the truth of what happened in
+            # this change.  There's no point showing the stat from the
+            # base to the new revision because the base is effectively a
+            # random revision at this point - the user will be interested
+            # in what this revision changed - including the undoing of
+            # previous revisions in the case of non-fast-forward updates.
+            yield '\n'
+            yield 'Summary of changes:\n'
+            for line in read_git_lines(
+                ['diff-tree']
+                + self.diffopts
+                + ['%s..%s' % (self.old.commit_sha1, self.new.commit_sha1,)],
+                keepends=True,
+                ):
+                yield line
+
+        elif self.old.commit_sha1 and not self.new.commit_sha1:
+            # A reference was deleted.  List the revisions that were
+            # removed from the repository by this reference change.
+
+            sha1s = list(push.get_discarded_commits(self))
+            tot = len(sha1s)
+            discarded_revisions = [
+                Revision(self, GitObject(sha1), num=i+1, tot=tot)
+                for (i, sha1) in enumerate(sha1s)
+                ]
+
+            if discarded_revisions:
+                for line in self.expand_lines(DISCARDED_REVISIONS_TEMPLATE):
+                    yield line
+                yield '\n'
+                for r in discarded_revisions:
+                    (sha1, subject) = r.rev.get_summary()
+                    yield r.expand(
+                        BRIEF_SUMMARY_TEMPLATE, action='discards', text=subject,
+                        )
+            else:
+                for line in self.expand_lines(NO_DISCARDED_REVISIONS_TEMPLATE):
+                    yield line
+
+        elif not self.old.commit_sha1 and not self.new.commit_sha1:
+            for line in self.expand_lines(NON_COMMIT_UPDATE_TEMPLATE):
+                yield line
+
+    def generate_create_summary(self, push):
+        """Called for the creation of a reference."""
+
+        # This is a new reference and so oldrev is not valid
+        (sha1, subject) = self.new.get_summary()
+        yield self.expand(
+            BRIEF_SUMMARY_TEMPLATE, action='at',
+            rev_short=sha1, text=subject,
+            )
+        yield '\n'
+
+    def generate_update_summary(self, push):
+        """Called for the change of a pre-existing branch."""
+
+        return iter([])
+
+    def generate_delete_summary(self, push):
+        """Called for the deletion of any type of reference."""
+
+        (sha1, subject) = self.old.get_summary()
+        yield self.expand(
+            BRIEF_SUMMARY_TEMPLATE, action='was',
+            rev_short=sha1, text=subject,
+            )
+        yield '\n'
+
+
+class BranchChange(ReferenceChange):
+    refname_type = 'branch'
+
+    def __init__(self, environment, refname, short_refname, old, new, rev):
+        ReferenceChange.__init__(
+            self, environment,
+            refname=refname, short_refname=short_refname,
+            old=old, new=new, rev=rev,
+            )
+        self.recipients = environment.get_refchange_recipients(self)
+
+
+class AnnotatedTagChange(ReferenceChange):
+    refname_type = 'annotated tag'
+
+    def __init__(self, environment, refname, short_refname, old, new, rev):
+        ReferenceChange.__init__(
+            self, environment,
+            refname=refname, short_refname=short_refname,
+            old=old, new=new, rev=rev,
+            )
+        self.recipients = environment.get_announce_recipients(self)
+        self.show_shortlog = environment.announce_show_shortlog
+
+    ANNOTATED_TAG_FORMAT = (
+        '%(*objectname)\n'
+        '%(*objecttype)\n'
+        '%(taggername)\n'
+        '%(taggerdate)'
+        )
+
+    def describe_tag(self, push):
+        """Describe the new value of an annotated tag."""
+
+        # Use git for-each-ref to pull out the individual fields from
+        # the tag
+        [tagobject, tagtype, tagger, tagged] = read_git_lines(
+            ['for-each-ref', '--format=%s' % (self.ANNOTATED_TAG_FORMAT,), self.refname],
+            )
+
+        yield self.expand(
+            BRIEF_SUMMARY_TEMPLATE, action='tagging',
+            rev_short=tagobject, text='(%s)' % (tagtype,),
+            )
+        if tagtype == 'commit':
+            # If the tagged object is a commit, then we assume this is a
+            # release, and so we calculate which tag this tag is
+            # replacing
+            try:
+                prevtag = read_git_output(['describe', '--abbrev=0', '%s^' % (self.new,)])
+            except CommandError:
+                prevtag = None
+            if prevtag:
+                yield '  replaces  %s\n' % (prevtag,)
+        else:
+            prevtag = None
+            yield '    length  %s bytes\n' % (read_git_output(['cat-file', '-s', tagobject]),)
+
+        yield ' tagged by  %s\n' % (tagger,)
+        yield '        on  %s\n' % (tagged,)
+        yield '\n'
+
+        # Show the content of the tag message; this might contain a
+        # change log or release notes so is worth displaying.
+        yield LOGBEGIN
+        contents = list(read_git_lines(['cat-file', 'tag', self.new.sha1], keepends=True))
+        contents = contents[contents.index('\n') + 1:]
+        if contents and contents[-1][-1:] != '\n':
+            contents.append('\n')
+        for line in contents:
+            yield line
+
+        if self.show_shortlog and tagtype == 'commit':
+            # Only commit tags make sense to have rev-list operations
+            # performed on them
+            yield '\n'
+            if prevtag:
+                # Show changes since the previous release
+                revlist = read_git_output(
+                    ['rev-list', '--pretty=short', '%s..%s' % (prevtag, self.new,)],
+                    keepends=True,
+                    )
+            else:
+                # No previous tag, show all the changes since time
+                # began
+                revlist = read_git_output(
+                    ['rev-list', '--pretty=short', '%s' % (self.new,)],
+                    keepends=True,
+                    )
+            for line in read_git_lines(['shortlog'], input=revlist, keepends=True):
+                yield line
+
+        yield LOGEND
+        yield '\n'
+
+    def generate_create_summary(self, push):
+        """Called for the creation of an annotated tag."""
+
+        for line in self.expand_lines(TAG_CREATED_TEMPLATE):
+            yield line
+
+        for line in self.describe_tag(push):
+            yield line
+
+    def generate_update_summary(self, push):
+        """Called for the update of an annotated tag.
+
+        This is probably a rare event and may not even be allowed."""
+
+        for line in self.expand_lines(TAG_UPDATED_TEMPLATE):
+            yield line
+
+        for line in self.describe_tag(push):
+            yield line
+
+    def generate_delete_summary(self, push):
+        """Called when a non-annotated reference is updated."""
+
+        for line in self.expand_lines(TAG_DELETED_TEMPLATE):
+            yield line
+
+        yield self.expand('   tag was  %(oldrev_short)s\n')
+        yield '\n'
+
+
+class NonAnnotatedTagChange(ReferenceChange):
+    refname_type = 'tag'
+
+    def __init__(self, environment, refname, short_refname, old, new, rev):
+        ReferenceChange.__init__(
+            self, environment,
+            refname=refname, short_refname=short_refname,
+            old=old, new=new, rev=rev,
+            )
+        self.recipients = environment.get_refchange_recipients(self)
+
+    def generate_create_summary(self, push):
+        """Called for the creation of an annotated tag."""
+
+        for line in self.expand_lines(TAG_CREATED_TEMPLATE):
+            yield line
+
+    def generate_update_summary(self, push):
+        """Called when a non-annotated reference is updated."""
+
+        for line in self.expand_lines(TAG_UPDATED_TEMPLATE):
+            yield line
+
+    def generate_delete_summary(self, push):
+        """Called when a non-annotated reference is updated."""
+
+        for line in self.expand_lines(TAG_DELETED_TEMPLATE):
+            yield line
+
+        for line in ReferenceChange.generate_delete_summary(self, push):
+            yield line
+
+
+class OtherReferenceChange(ReferenceChange):
+    refname_type = 'reference'
+
+    def __init__(self, environment, refname, short_refname, old, new, rev):
+        # We use the full refname as short_refname, because otherwise
+        # the full name of the reference would not be obvious from the
+        # text of the email.
+        ReferenceChange.__init__(
+            self, environment,
+            refname=refname, short_refname=refname,
+            old=old, new=new, rev=rev,
+            )
+        self.recipients = environment.get_refchange_recipients(self)
+
+
+class Mailer(object):
+    """An object that can send emails."""
+
+    def send(self, lines, to_addrs):
+        """Send an email consisting of lines.
+
+        lines must be an iterable over the lines constituting the
+        header and body of the email.  to_addrs is a list of recipient
+        addresses (can be needed even if lines already contains a
+        "To:" field).  It can be either a string (comma-separated list
+        of email addresses) or a Python list of individual email
+        addresses.
+
+        """
+
+        raise NotImplementedError()
+
+
+class SendMailer(Mailer):
+    """Send emails using 'sendmail -t'."""
+
+    SENDMAIL_CANDIDATES = [
+        '/usr/sbin/sendmail',
+        '/usr/lib/sendmail',
+        ]
+
+    @staticmethod
+    def find_sendmail():
+        for path in SendMailer.SENDMAIL_CANDIDATES:
+            if os.access(path, os.X_OK):
+                return path
+        else:
+            raise ConfigurationException(
+                'No sendmail executable found.  '
+                'Try setting multimailhook.sendmailCommand.'
+                )
+
+    def __init__(self, command=None, envelopesender=None):
+        """Construct a SendMailer instance.
+
+        command should be the command and arguments used to invoke
+        sendmail, as a list of strings.  If an envelopesender is
+        provided, it will also be passed to the command, via '-f
+        envelopesender'."""
+
+        if command:
+            self.command = command[:]
+        else:
+            self.command = [self.find_sendmail(), '-t']
+
+        if envelopesender:
+            self.command.extend(['-f', envelopesender])
+
+    def send(self, lines, to_addrs):
+        try:
+            p = subprocess.Popen(self.command, stdin=subprocess.PIPE)
+        except OSError, e:
+            sys.stderr.write(
+                '*** Cannot execute command: %s\n' % ' '.join(self.command)
+                + '*** %s\n' % str(e)
+                + '*** Try setting multimailhook.mailer to "smtp"\n'
+                '*** to send emails without using the sendmail command.\n'
+                )
+            sys.exit(1)
+        try:
+            p.stdin.writelines(lines)
+        except:
+            sys.stderr.write(
+                '*** Error while generating commit email\n'
+                '***  - mail sending aborted.\n'
+                )
+            p.terminate()
+            raise
+        else:
+            p.stdin.close()
+            retcode = p.wait()
+            if retcode:
+                raise CommandError(self.command, retcode)
+
+
+class SMTPMailer(Mailer):
+    """Send emails using Python's smtplib."""
+
+    def __init__(self, envelopesender, smtpserver):
+        if not envelopesender:
+            sys.stderr.write(
+                'fatal: git_multimail: cannot use SMTPMailer without a sender address.\n'
+                'please set either multimailhook.envelopeSender or user.email\n'
+                )
+            sys.exit(1)
+        self.envelopesender = envelopesender
+        self.smtpserver = smtpserver
+        try:
+            self.smtp = smtplib.SMTP(self.smtpserver)
+        except Exception, e:
+            sys.stderr.write('*** Error establishing SMTP connection to %s***\n' % self.smtpserver)
+            sys.stderr.write('*** %s\n' % str(e))
+            sys.exit(1)
+
+    def __del__(self):
+        self.smtp.quit()
+
+    def send(self, lines, to_addrs):
+        try:
+            msg = ''.join(lines)
+            # turn comma-separated list into Python list if needed.
+            if isinstance(to_addrs, basestring):
+                to_addrs = [email for (name, email) in getaddresses([to_addrs])]
+            self.smtp.sendmail(self.envelopesender, to_addrs, msg)
+        except Exception, e:
+            sys.stderr.write('*** Error sending email***\n')
+            sys.stderr.write('*** %s\n' % str(e))
+            self.smtp.quit()
+            sys.exit(1)
+
+
+class OutputMailer(Mailer):
+    """Write emails to an output stream, bracketed by lines of '=' characters.
+
+    This is intended for debugging purposes."""
+
+    SEPARATOR = '=' * 75 + '\n'
+
+    def __init__(self, f):
+        self.f = f
+
+    def send(self, lines, to_addrs):
+        self.f.write(self.SEPARATOR)
+        self.f.writelines(lines)
+        self.f.write(self.SEPARATOR)
+
+
+def get_git_dir():
+    """Determine GIT_DIR.
+
+    Determine GIT_DIR either from the GIT_DIR environment variable or
+    from the working directory, using Git's usual rules."""
+
+    try:
+        return read_git_output(['rev-parse', '--git-dir'])
+    except CommandError:
+        sys.stderr.write('fatal: git_multimail: not in a git directory\n')
+        sys.exit(1)
+
+
+class Environment(object):
+    """Describes the environment in which the push is occurring.
+
+    An Environment object encapsulates information about the local
+    environment.  For example, it knows how to determine:
+
+    * the name of the repository to which the push occurred
+
+    * what user did the push
+
+    * what users want to be informed about various types of changes.
+
+    An Environment object is expected to have the following methods:
+
+        get_repo_shortname()
+
+            Return a short name for the repository, for display
+            purposes.
+
+        get_repo_path()
+
+            Return the absolute path to the Git repository.
+
+        get_emailprefix()
+
+            Return a string that will be prefixed to every email's
+            subject.
+
+        get_pusher()
+
+            Return the username of the person who pushed the changes.
+            This value is used in the email body to indicate who
+            pushed the change.
+
+        get_pusher_email() (may return None)
+
+            Return the email address of the person who pushed the
+            changes.  The value should be a single RFC 2822 email
+            address as a string; e.g., "Joe User <user@example.com>"
+            if available, otherwise "user@example.com".  If set, the
+            value is used as the Reply-To address for refchange
+            emails.  If it is impossible to determine the pusher's
+            email, this attribute should be set to None (in which case
+            no Reply-To header will be output).
+
+        get_sender()
+
+            Return the address to be used as the 'From' email address
+            in the email envelope.
+
+        get_fromaddr()
+
+            Return the 'From' email address used in the email 'From:'
+            headers.  (May be a full RFC 2822 email address like 'Joe
+            User <user@example.com>'.)
+
+        get_administrator()
+
+            Return the name and/or email of the repository
+            administrator.  This value is used in the footer as the
+            person to whom requests to be removed from the
+            notification list should be sent.  Ideally, it should
+            include a valid email address.
+
+        get_reply_to_refchange()
+        get_reply_to_commit()
+
+            Return the address to use in the email "Reply-To" header,
+            as a string.  These can be an RFC 2822 email address, or
+            None to omit the "Reply-To" header.
+            get_reply_to_refchange() is used for refchange emails;
+            get_reply_to_commit() is used for individual commit
+            emails.
+
+    They should also define the following attributes:
+
+        announce_show_shortlog (bool)
+
+            True iff announce emails should include a shortlog.
+
+        refchange_showlog (bool)
+
+            True iff refchanges emails should include a detailed log.
+
+        diffopts (list of strings)
+
+            The options that should be passed to 'git diff' for the
+            summary email.  The value should be a list of strings
+            representing words to be passed to the command.
+
+        logopts (list of strings)
+
+            Analogous to diffopts, but contains options passed to
+            'git log' when generating the detailed log for a set of
+            commits (see refchange_showlog)
+
+    """
+
+    REPO_NAME_RE = re.compile(r'^(?P<name>.+?)(?:\.git)$')
+
+    def __init__(self, osenv=None):
+        self.osenv = osenv or os.environ
+        self.announce_show_shortlog = False
+        self.maxcommitemails = 500
+        self.diffopts = ['--stat', '--summary', '--find-copies-harder']
+        self.logopts = []
+        self.refchange_showlog = False
+
+        self.COMPUTED_KEYS = [
+            'administrator',
+            'charset',
+            'emailprefix',
+            'fromaddr',
+            'pusher',
+            'pusher_email',
+            'repo_path',
+            'repo_shortname',
+            'sender',
+            ]
+
+        self._values = None
+
+    def get_repo_shortname(self):
+        """Use the last part of the repo path, with ".git" stripped off if present."""
+
+        basename = os.path.basename(os.path.abspath(self.get_repo_path()))
+        m = self.REPO_NAME_RE.match(basename)
+        if m:
+            return m.group('name')
+        else:
+            return basename
+
+    def get_pusher(self):
+        raise NotImplementedError()
+
+    def get_pusher_email(self):
+        return None
+
+    def get_administrator(self):
+        return 'the administrator of this repository'
+
+    def get_emailprefix(self):
+        return ''
+
+    def get_repo_path(self):
+        if read_git_output(['rev-parse', '--is-bare-repository']) == 'true':
+            path = get_git_dir()
+        else:
+            path = read_git_output(['rev-parse', '--show-toplevel'])
+        return os.path.abspath(path)
+
+    def get_charset(self):
+        return CHARSET
+
+    def get_values(self):
+        """Return a dictionary {keyword : expansion} for this Environment.
+
+        This method is called by Change._compute_values().  The keys
+        in the returned dictionary are available to be used in any of
+        the templates.  The dictionary is created by calling
+        self.get_NAME() for each of the attributes named in
+        COMPUTED_KEYS and recording those that do not return None.
+        The return value is always a new dictionary."""
+
+        if self._values is None:
+            values = {}
+
+            for key in self.COMPUTED_KEYS:
+                value = getattr(self, 'get_%s' % (key,))()
+                if value is not None:
+                    values[key] = value
+
+            self._values = values
+
+        return self._values.copy()
+
+    def get_refchange_recipients(self, refchange):
+        """Return the recipients for notifications about refchange.
+
+        Return the list of email addresses to which notifications
+        about the specified ReferenceChange should be sent."""
+
+        raise NotImplementedError()
+
+    def get_announce_recipients(self, annotated_tag_change):
+        """Return the recipients for notifications about annotated_tag_change.
+
+        Return the list of email addresses to which notifications
+        about the specified AnnotatedTagChange should be sent."""
+
+        raise NotImplementedError()
+
+    def get_reply_to_refchange(self, refchange):
+        return self.get_pusher_email()
+
+    def get_revision_recipients(self, revision):
+        """Return the recipients for messages about revision.
+
+        Return the list of email addresses to which notifications
+        about the specified Revision should be sent.  This method
+        could be overridden, for example, to take into account the
+        contents of the revision when deciding whom to notify about
+        it.  For example, there could be a scheme for users to express
+        interest in particular files or subdirectories, and only
+        receive notification emails for revisions that affecting those
+        files."""
+
+        raise NotImplementedError()
+
+    def get_reply_to_commit(self, revision):
+        return revision.author
+
+    def filter_body(self, lines):
+        """Filter the lines intended for an email body.
+
+        lines is an iterable over the lines that would go into the
+        email body.  Filter it (e.g., limit the number of lines, the
+        line length, character set, etc.), returning another iterable.
+        See FilterLinesEnvironmentMixin and MaxlinesEnvironmentMixin
+        for classes implementing this functionality."""
+
+        return lines
+
+
+class ConfigEnvironmentMixin(Environment):
+    """A mixin that sets self.config to its constructor's config argument.
+
+    This class's constructor consumes the "config" argument.
+
+    Mixins that need to inspect the config should inherit from this
+    class (1) to make sure that "config" is still in the constructor
+    arguments with its own constructor runs and/or (2) to be sure that
+    self.config is set after construction."""
+
+    def __init__(self, config, **kw):
+        super(ConfigEnvironmentMixin, self).__init__(**kw)
+        self.config = config
+
+
+class ConfigOptionsEnvironmentMixin(ConfigEnvironmentMixin):
+    """An Environment that reads most of its information from "git config"."""
+
+    def __init__(self, config, **kw):
+        super(ConfigOptionsEnvironmentMixin, self).__init__(
+            config=config, **kw
+            )
+
+        self.announce_show_shortlog = config.get_bool(
+            'announceshortlog', default=self.announce_show_shortlog
+            )
+
+        self.refchange_showlog = config.get_bool(
+            'refchangeshowlog', default=self.refchange_showlog
+            )
+
+        maxcommitemails = config.get('maxcommitemails')
+        if maxcommitemails is not None:
+            try:
+                self.maxcommitemails = int(maxcommitemails)
+            except ValueError:
+                sys.stderr.write(
+                    '*** Malformed value for multimailhook.maxCommitEmails: %s\n' % maxcommitemails
+                    + '*** Expected a number.  Ignoring.\n'
+                    )
+
+        diffopts = config.get('diffopts')
+        if diffopts is not None:
+            self.diffopts = shlex.split(diffopts)
+
+        logopts = config.get('logopts')
+        if logopts is not None:
+            self.logopts = shlex.split(logopts)
+
+        reply_to = config.get('replyTo')
+        self.__reply_to_refchange = config.get('replyToRefchange', default=reply_to)
+        if (
+            self.__reply_to_refchange is not None
+            and self.__reply_to_refchange.lower() == 'author'
+            ):
+            raise ConfigurationException(
+                '"author" is not an allowed setting for replyToRefchange'
+                )
+        self.__reply_to_commit = config.get('replyToCommit', default=reply_to)
+
+    def get_administrator(self):
+        return (
+            self.config.get('administrator')
+            or self.get_sender()
+            or super(ConfigOptionsEnvironmentMixin, self).get_administrator()
+            )
+
+    def get_repo_shortname(self):
+        return (
+            self.config.get('reponame')
+            or super(ConfigOptionsEnvironmentMixin, self).get_repo_shortname()
+            )
+
+    def get_emailprefix(self):
+        emailprefix = self.config.get('emailprefix')
+        if emailprefix and emailprefix.strip():
+            return emailprefix.strip() + ' '
+        else:
+            return '[%s] ' % (self.get_repo_shortname(),)
+
+    def get_sender(self):
+        return self.config.get('envelopesender')
+
+    def get_fromaddr(self):
+        fromaddr = self.config.get('from')
+        if fromaddr:
+            return fromaddr
+        else:
+            config = Config('user')
+            fromname = config.get('name', default='')
+            fromemail = config.get('email', default='')
+            if fromemail:
+                return formataddr([fromname, fromemail])
+            else:
+                return self.get_sender()
+
+    def get_reply_to_refchange(self, refchange):
+        if self.__reply_to_refchange is None:
+            return super(ConfigOptionsEnvironmentMixin, self).get_reply_to_refchange(refchange)
+        elif self.__reply_to_refchange.lower() == 'pusher':
+            return self.get_pusher_email()
+        elif self.__reply_to_refchange.lower() == 'none':
+            return None
+        else:
+            return self.__reply_to_refchange
+
+    def get_reply_to_commit(self, revision):
+        if self.__reply_to_commit is None:
+            return super(ConfigOptionsEnvironmentMixin, self).get_reply_to_commit(revision)
+        elif self.__reply_to_commit.lower() == 'author':
+            return revision.get_author()
+        elif self.__reply_to_commit.lower() == 'pusher':
+            return self.get_pusher_email()
+        elif self.__reply_to_commit.lower() == 'none':
+            return None
+        else:
+            return self.__reply_to_commit
+
+
+class FilterLinesEnvironmentMixin(Environment):
+    """Handle encoding and maximum line length of body lines.
+
+        emailmaxlinelength (int or None)
+
+            The maximum length of any single line in the email body.
+            Longer lines are truncated at that length with ' [...]'
+            appended.
+
+        strict_utf8 (bool)
+
+            If this field is set to True, then the email body text is
+            expected to be UTF-8.  Any invalid characters are
+            converted to U+FFFD, the Unicode replacement character
+            (encoded as UTF-8, of course).
+
+    """
+
+    def __init__(self, strict_utf8=True, emailmaxlinelength=500, **kw):
+        super(FilterLinesEnvironmentMixin, self).__init__(**kw)
+        self.__strict_utf8 = strict_utf8
+        self.__emailmaxlinelength = emailmaxlinelength
+
+    def filter_body(self, lines):
+        lines = super(FilterLinesEnvironmentMixin, self).filter_body(lines)
+        if self.__strict_utf8:
+            lines = (line.decode(ENCODING, 'replace') for line in lines)
+            # Limit the line length in Unicode-space to avoid
+            # splitting characters:
+            if self.__emailmaxlinelength:
+                lines = limit_linelength(lines, self.__emailmaxlinelength)
+            lines = (line.encode(ENCODING, 'replace') for line in lines)
+        elif self.__emailmaxlinelength:
+            lines = limit_linelength(lines, self.__emailmaxlinelength)
+
+        return lines
+
+
+class ConfigFilterLinesEnvironmentMixin(
+    ConfigEnvironmentMixin,
+    FilterLinesEnvironmentMixin,
+    ):
+    """Handle encoding and maximum line length based on config."""
+
+    def __init__(self, config, **kw):
+        strict_utf8 = config.get_bool('emailstrictutf8', default=None)
+        if strict_utf8 is not None:
+            kw['strict_utf8'] = strict_utf8
+
+        emailmaxlinelength = config.get('emailmaxlinelength')
+        if emailmaxlinelength is not None:
+            kw['emailmaxlinelength'] = int(emailmaxlinelength)
+
+        super(ConfigFilterLinesEnvironmentMixin, self).__init__(
+            config=config, **kw
+            )
+
+
+class MaxlinesEnvironmentMixin(Environment):
+    """Limit the email body to a specified number of lines."""
+
+    def __init__(self, emailmaxlines, **kw):
+        super(MaxlinesEnvironmentMixin, self).__init__(**kw)
+        self.__emailmaxlines = emailmaxlines
+
+    def filter_body(self, lines):
+        lines = super(MaxlinesEnvironmentMixin, self).filter_body(lines)
+        if self.__emailmaxlines:
+            lines = limit_lines(lines, self.__emailmaxlines)
+        return lines
+
+
+class ConfigMaxlinesEnvironmentMixin(
+    ConfigEnvironmentMixin,
+    MaxlinesEnvironmentMixin,
+    ):
+    """Limit the email body to the number of lines specified in config."""
+
+    def __init__(self, config, **kw):
+        emailmaxlines = int(config.get('emailmaxlines', default='0'))
+        super(ConfigMaxlinesEnvironmentMixin, self).__init__(
+            config=config,
+            emailmaxlines=emailmaxlines,
+            **kw
+            )
+
+
+class PusherDomainEnvironmentMixin(ConfigEnvironmentMixin):
+    """Deduce pusher_email from pusher by appending an emaildomain."""
+
+    def __init__(self, **kw):
+        super(PusherDomainEnvironmentMixin, self).__init__(**kw)
+        self.__emaildomain = self.config.get('emaildomain')
+
+    def get_pusher_email(self):
+        if self.__emaildomain:
+            # Derive the pusher's full email address in the default way:
+            return '%s@%s' % (self.get_pusher(), self.__emaildomain)
+        else:
+            return super(PusherDomainEnvironmentMixin, self).get_pusher_email()
+
+
+class StaticRecipientsEnvironmentMixin(Environment):
+    """Set recipients statically based on constructor parameters."""
+
+    def __init__(
+        self,
+        refchange_recipients, announce_recipients, revision_recipients,
+        **kw
+        ):
+        super(StaticRecipientsEnvironmentMixin, self).__init__(**kw)
+
+        # The recipients for various types of notification emails, as
+        # RFC 2822 email addresses separated by commas (or the empty
+        # string if no recipients are configured).  Although there is
+        # a mechanism to choose the recipient lists based on on the
+        # actual *contents* of the change being reported, we only
+        # choose based on the *type* of the change.  Therefore we can
+        # compute them once and for all:
+        self.__refchange_recipients = refchange_recipients
+        self.__announce_recipients = announce_recipients
+        self.__revision_recipients = revision_recipients
+
+    def get_refchange_recipients(self, refchange):
+        return self.__refchange_recipients
+
+    def get_announce_recipients(self, annotated_tag_change):
+        return self.__announce_recipients
+
+    def get_revision_recipients(self, revision):
+        return self.__revision_recipients
+
+
+class ConfigRecipientsEnvironmentMixin(
+    ConfigEnvironmentMixin,
+    StaticRecipientsEnvironmentMixin
+    ):
+    """Determine recipients statically based on config."""
+
+    def __init__(self, config, **kw):
+        super(ConfigRecipientsEnvironmentMixin, self).__init__(
+            config=config,
+            refchange_recipients=self._get_recipients(
+                config, 'refchangelist', 'mailinglist',
+                ),
+            announce_recipients=self._get_recipients(
+                config, 'announcelist', 'refchangelist', 'mailinglist',
+                ),
+            revision_recipients=self._get_recipients(
+                config, 'commitlist', 'mailinglist',
+                ),
+            **kw
+            )
+
+    def _get_recipients(self, config, *names):
+        """Return the recipients for a particular type of message.
+
+        Return the list of email addresses to which a particular type
+        of notification email should be sent, by looking at the config
+        value for "multimailhook.$name" for each of names.  Use the
+        value from the first name that is configured.  The return
+        value is a (possibly empty) string containing RFC 2822 email
+        addresses separated by commas.  If no configuration could be
+        found, raise a ConfigurationException."""
+
+        for name in names:
+            retval = config.get_recipients(name)
+            if retval is not None:
+                return retval
+        if len(names) == 1:
+            hint = 'Please set "%s.%s"' % (config.section, name)
+        else:
+            hint = (
+                'Please set one of the following:\n    "%s"'
+                % ('"\n    "'.join('%s.%s' % (config.section, name) for name in names))
+                )
+
+        raise ConfigurationException(
+            'The list of recipients for %s is not configured.\n%s' % (names[0], hint)
+            )
+
+
+class ProjectdescEnvironmentMixin(Environment):
+    """Make a "projectdesc" value available for templates.
+
+    By default, it is set to the first line of $GIT_DIR/description
+    (if that file is present and appears to be set meaningfully)."""
+
+    def __init__(self, **kw):
+        super(ProjectdescEnvironmentMixin, self).__init__(**kw)
+        self.COMPUTED_KEYS += ['projectdesc']
+
+    def get_projectdesc(self):
+        """Return a one-line descripition of the project."""
+
+        git_dir = get_git_dir()
+        try:
+            projectdesc = open(os.path.join(git_dir, 'description')).readline().strip()
+            if projectdesc and not projectdesc.startswith('Unnamed repository'):
+                return projectdesc
+        except IOError:
+            pass
+
+        return 'UNNAMED PROJECT'
+
+
+class GenericEnvironmentMixin(Environment):
+    def get_pusher(self):
+        return self.osenv.get('USER', 'unknown user')
+
+
+class GenericEnvironment(
+    ProjectdescEnvironmentMixin,
+    ConfigMaxlinesEnvironmentMixin,
+    ConfigFilterLinesEnvironmentMixin,
+    ConfigRecipientsEnvironmentMixin,
+    PusherDomainEnvironmentMixin,
+    ConfigOptionsEnvironmentMixin,
+    GenericEnvironmentMixin,
+    Environment,
+    ):
+    pass
+
+
+class GitoliteEnvironmentMixin(Environment):
+    def get_repo_shortname(self):
+        # The gitolite environment variable $GL_REPO is a pretty good
+        # repo_shortname (though it's probably not as good as a value
+        # the user might have explicitly put in his config).
+        return (
+            self.osenv.get('GL_REPO', None)
+            or super(GitoliteEnvironmentMixin, self).get_repo_shortname()
+            )
+
+    def get_pusher(self):
+        return self.osenv.get('GL_USER', 'unknown user')
+
+
+class GitoliteEnvironment(
+    ProjectdescEnvironmentMixin,
+    ConfigMaxlinesEnvironmentMixin,
+    ConfigFilterLinesEnvironmentMixin,
+    ConfigRecipientsEnvironmentMixin,
+    PusherDomainEnvironmentMixin,
+    ConfigOptionsEnvironmentMixin,
+    GitoliteEnvironmentMixin,
+    Environment,
+    ):
+    pass
+
+
+class Push(object):
+    """Represent an entire push (i.e., a group of ReferenceChanges).
+
+    It is easy to figure out what commits were added to a *branch* by
+    a Reference change:
+
+        git rev-list change.old..change.new
+
+    or removed from a *branch*:
+
+        git rev-list change.new..change.old
+
+    But it is not quite so trivial to determine which entirely new
+    commits were added to the *repository* by a push and which old
+    commits were discarded by a push.  A big part of the job of this
+    class is to figure out these things, and to make sure that new
+    commits are only detailed once even if they were added to multiple
+    references.
+
+    The first step is to determine the "other" references--those
+    unaffected by the current push.  They are computed by
+    Push._compute_other_ref_sha1s() by listing all references then
+    removing any affected by this push.
+
+    The commits contained in the repository before this push were
+
+        git rev-list other1 other2 other3 ... change1.old change2.old ...
+
+    Where "changeN.old" is the old value of one of the references
+    affected by this push.
+
+    The commits contained in the repository after this push are
+
+        git rev-list other1 other2 other3 ... change1.new change2.new ...
+
+    The commits added by this push are the difference between these
+    two sets, which can be written
+
+        git rev-list \
+            ^other1 ^other2 ... \
+            ^change1.old ^change2.old ... \
+            change1.new change2.new ...
+
+    The commits removed by this push can be computed by
+
+        git rev-list \
+            ^other1 ^other2 ... \
+            ^change1.new ^change2.new ... \
+            change1.old change2.old ...
+
+    The last point is that it is possible that other pushes are
+    occurring simultaneously to this one, so reference values can
+    change at any time.  It is impossible to eliminate all race
+    conditions, but we reduce the window of time during which problems
+    can occur by translating reference names to SHA1s as soon as
+    possible and working with SHA1s thereafter (because SHA1s are
+    immutable)."""
+
+    # A map {(changeclass, changetype) : integer} specifying the order
+    # that reference changes will be processed if multiple reference
+    # changes are included in a single push.  The order is significant
+    # mostly because new commit notifications are threaded together
+    # with the first reference change that includes the commit.  The
+    # following order thus causes commits to be grouped with branch
+    # changes (as opposed to tag changes) if possible.
+    SORT_ORDER = dict(
+        (value, i) for (i, value) in enumerate([
+            (BranchChange, 'update'),
+            (BranchChange, 'create'),
+            (AnnotatedTagChange, 'update'),
+            (AnnotatedTagChange, 'create'),
+            (NonAnnotatedTagChange, 'update'),
+            (NonAnnotatedTagChange, 'create'),
+            (BranchChange, 'delete'),
+            (AnnotatedTagChange, 'delete'),
+            (NonAnnotatedTagChange, 'delete'),
+            (OtherReferenceChange, 'update'),
+            (OtherReferenceChange, 'create'),
+            (OtherReferenceChange, 'delete'),
+            ])
+        )
+
+    def __init__(self, changes):
+        self.changes = sorted(changes, key=self._sort_key)
+
+        # The SHA-1s of commits referred to by references unaffected
+        # by this push:
+        other_ref_sha1s = self._compute_other_ref_sha1s()
+
+        self._old_rev_exclusion_spec = self._compute_rev_exclusion_spec(
+            other_ref_sha1s.union(
+                change.old.sha1
+                for change in self.changes
+                if change.old.type in ['commit', 'tag']
+                )
+            )
+        self._new_rev_exclusion_spec = self._compute_rev_exclusion_spec(
+            other_ref_sha1s.union(
+                change.new.sha1
+                for change in self.changes
+                if change.new.type in ['commit', 'tag']
+                )
+            )
+
+    @classmethod
+    def _sort_key(klass, change):
+        return (klass.SORT_ORDER[change.__class__, change.change_type], change.refname,)
+
+    def _compute_other_ref_sha1s(self):
+        """Return the GitObjects referred to by references unaffected by this push."""
+
+        # The refnames being changed by this push:
+        updated_refs = set(
+            change.refname
+            for change in self.changes
+            )
+
+        # The SHA-1s of commits referred to by all references in this
+        # repository *except* updated_refs:
+        sha1s = set()
+        fmt = (
+            '%(objectname) %(objecttype) %(refname)\n'
+            '%(*objectname) %(*objecttype) %(refname)'
+            )
+        for line in read_git_lines(['for-each-ref', '--format=%s' % (fmt,)]):
+            (sha1, type, name) = line.split(' ', 2)
+            if sha1 and type == 'commit' and name not in updated_refs:
+                sha1s.add(sha1)
+
+        return sha1s
+
+    def _compute_rev_exclusion_spec(self, sha1s):
+        """Return an exclusion specification for 'git rev-list'.
+
+        git_objects is an iterable over GitObject instances.  Return a
+        string that can be passed to the standard input of 'git
+        rev-list --stdin' to exclude all of the commits referred to by
+        git_objects."""
+
+        return ''.join(
+            ['^%s\n' % (sha1,) for sha1 in sorted(sha1s)]
+            )
+
+    def get_new_commits(self, reference_change=None):
+        """Return a list of commits added by this push.
+
+        Return a list of the object names of commits that were added
+        by the part of this push represented by reference_change.  If
+        reference_change is None, then return a list of *all* commits
+        added by this push."""
+
+        if not reference_change:
+            new_revs = sorted(
+                change.new.sha1
+                for change in self.changes
+                if change.new
+                )
+        elif not reference_change.new.commit_sha1:
+            return []
+        else:
+            new_revs = [reference_change.new.commit_sha1]
+
+        cmd = ['rev-list', '--stdin'] + new_revs
+        return read_git_lines(cmd, input=self._old_rev_exclusion_spec)
+
+    def get_discarded_commits(self, reference_change):
+        """Return a list of commits discarded by this push.
+
+        Return a list of the object names of commits that were
+        entirely discarded from the repository by the part of this
+        push represented by reference_change."""
+
+        if not reference_change.old.commit_sha1:
+            return []
+        else:
+            old_revs = [reference_change.old.commit_sha1]
+
+        cmd = ['rev-list', '--stdin'] + old_revs
+        return read_git_lines(cmd, input=self._new_rev_exclusion_spec)
+
+    def send_emails(self, mailer, body_filter=None):
+        """Use send all of the notification emails needed for this push.
+
+        Use send all of the notification emails (including reference
+        change emails and commit emails) needed for this push.  Send
+        the emails using mailer.  If body_filter is not None, then use
+        it to filter the lines that are intended for the email
+        body."""
+
+        # The sha1s of commits that were introduced by this push.
+        # They will be removed from this set as they are processed, to
+        # guarantee that one (and only one) email is generated for
+        # each new commit.
+        unhandled_sha1s = set(self.get_new_commits())
+        for change in self.changes:
+            # Check if we've got anyone to send to
+            if not change.recipients:
+                sys.stderr.write(
+                    '*** no recipients configured so no email will be sent\n'
+                    '*** for %r update %s->%s\n'
+                    % (change.refname, change.old.sha1, change.new.sha1,)
+                    )
+            else:
+                sys.stderr.write('Sending notification emails to: %s\n' % (change.recipients,))
+                mailer.send(change.generate_email(self, body_filter), change.recipients)
+
+            sha1s = []
+            for sha1 in reversed(list(self.get_new_commits(change))):
+                if sha1 in unhandled_sha1s:
+                    sha1s.append(sha1)
+                    unhandled_sha1s.remove(sha1)
+
+            max_emails = change.environment.maxcommitemails
+            if max_emails and len(sha1s) > max_emails:
+                sys.stderr.write(
+                    '*** Too many new commits (%d), not sending commit emails.\n' % len(sha1s)
+                    + '*** Try setting multimailhook.maxCommitEmails to a greater value\n'
+                    + '*** Currently, multimailhook.maxCommitEmails=%d\n' % max_emails
+                    )
+                return
+
+            for (num, sha1) in enumerate(sha1s):
+                rev = Revision(change, GitObject(sha1), num=num+1, tot=len(sha1s))
+                if rev.recipients:
+                    mailer.send(rev.generate_email(self, body_filter), rev.recipients)
+
+        # Consistency check:
+        if unhandled_sha1s:
+            sys.stderr.write(
+                'ERROR: No emails were sent for the following new commits:\n'
+                '    %s\n'
+                % ('\n    '.join(sorted(unhandled_sha1s)),)
+                )
+
+
+def run_as_post_receive_hook(environment, mailer):
+    changes = []
+    for line in sys.stdin:
+        (oldrev, newrev, refname) = line.strip().split(' ', 2)
+        changes.append(
+            ReferenceChange.create(environment, oldrev, newrev, refname)
+            )
+    push = Push(changes)
+    push.send_emails(mailer, body_filter=environment.filter_body)
+
+
+def run_as_update_hook(environment, mailer, refname, oldrev, newrev):
+    changes = [
+        ReferenceChange.create(
+            environment,
+            read_git_output(['rev-parse', '--verify', oldrev]),
+            read_git_output(['rev-parse', '--verify', newrev]),
+            refname,
+            ),
+        ]
+    push = Push(changes)
+    push.send_emails(mailer, body_filter=environment.filter_body)
+
+
+def choose_mailer(config, environment):
+    mailer = config.get('mailer', default='sendmail')
+
+    if mailer == 'smtp':
+        smtpserver = config.get('smtpserver', default='localhost')
+        mailer = SMTPMailer(
+            envelopesender=(environment.get_sender() or environment.get_fromaddr()),
+            smtpserver=smtpserver,
+            )
+    elif mailer == 'sendmail':
+        command = config.get('sendmailcommand')
+        if command:
+            command = shlex.split(command)
+        mailer = SendMailer(command=command, envelopesender=environment.get_sender())
+    else:
+        sys.stderr.write(
+            'fatal: multimailhook.mailer is set to an incorrect value: "%s"\n' % mailer
+            + 'please use one of "smtp" or "sendmail".\n'
+            )
+        sys.exit(1)
+    return mailer
+
+
+KNOWN_ENVIRONMENTS = {
+    'generic' : GenericEnvironmentMixin,
+    'gitolite' : GitoliteEnvironmentMixin,
+    }
+
+
+def choose_environment(config, osenv=None, env=None, recipients=None):
+    if not osenv:
+        osenv = os.environ
+
+    environment_mixins = [
+        ProjectdescEnvironmentMixin,
+        ConfigMaxlinesEnvironmentMixin,
+        ConfigFilterLinesEnvironmentMixin,
+        PusherDomainEnvironmentMixin,
+        ConfigOptionsEnvironmentMixin,
+        ]
+    environment_kw = {
+        'osenv' : osenv,
+        'config' : config,
+        }
+
+    if not env:
+        env = config.get('environment')
+
+    if not env:
+        if 'GL_USER' in osenv and 'GL_REPO' in osenv:
+            env = 'gitolite'
+        else:
+            env = 'generic'
+
+    environment_mixins.append(KNOWN_ENVIRONMENTS[env])
+
+    if recipients:
+        environment_mixins.insert(0, StaticRecipientsEnvironmentMixin)
+        environment_kw['refchange_recipients'] = recipients
+        environment_kw['announce_recipients'] = recipients
+        environment_kw['revision_recipients'] = recipients
+    else:
+        environment_mixins.insert(0, ConfigRecipientsEnvironmentMixin)
+
+    environment_klass = type(
+        'EffectiveEnvironment',
+        tuple(environment_mixins) + (Environment,),
+        {},
+        )
+    return environment_klass(**environment_kw)
+
+
+def main(args):
+    parser = optparse.OptionParser(
+        description=__doc__,
+        usage='%prog [OPTIONS]\n   or: %prog [OPTIONS] REFNAME OLDREV NEWREV',
+        )
+
+    parser.add_option(
+        '--environment', '--env', action='store', type='choice',
+        choices=['generic', 'gitolite'], default=None,
+        help=(
+            'Choose type of environment is in use.  Default is taken from '
+            'multimailhook.environment if set; otherwise "generic".'
+            ),
+        )
+    parser.add_option(
+        '--stdout', action='store_true', default=False,
+        help='Output emails to stdout rather than sending them.',
+        )
+    parser.add_option(
+        '--recipients', action='store', default=None,
+        help='Set list of email recipients for all types of emails.',
+        )
+    parser.add_option(
+        '--show-env', action='store_true', default=False,
+        help=(
+            'Write to stderr the values determined for the environment '
+            '(intended for debugging purposes).'
+            ),
+        )
+
+    (options, args) = parser.parse_args(args)
+
+    config = Config('multimailhook')
+
+    try:
+        environment = choose_environment(
+            config, osenv=os.environ,
+            env=options.environment,
+            recipients=options.recipients,
+            )
+
+        if options.show_env:
+            sys.stderr.write('Environment values:\n')
+            for (k,v) in sorted(environment.get_values().items()):
+                sys.stderr.write('    %s : %r\n' % (k,v))
+            sys.stderr.write('\n')
+
+        if options.stdout:
+            mailer = OutputMailer(sys.stdout)
+        else:
+            mailer = choose_mailer(config, environment)
+
+        # Dual mode: if arguments were specified on the command line, run
+        # like an update hook; otherwise, run as a post-receive hook.
+        if args:
+            if len(args) != 3:
+                parser.error('Need zero or three non-option arguments')
+            (refname, oldrev, newrev) = args
+            run_as_update_hook(environment, mailer, refname, oldrev, newrev)
+        else:
+            run_as_post_receive_hook(environment, mailer)
+    except ConfigurationException, e:
+        sys.exit(str(e))
+
+
+if __name__ == '__main__':
+    main(sys.argv[1:])
diff --git a/contrib/hooks/multimail/migrate-mailhook-config b/contrib/hooks/multimail/migrate-mailhook-config
new file mode 100755 (executable)
index 0000000..04eeaac
--- /dev/null
@@ -0,0 +1,269 @@
+#! /usr/bin/env python2
+
+"""Migrate a post-receive-email configuration to be usable with git_multimail.py.
+
+See README.migrate-from-post-receive-email for more information.
+
+"""
+
+import sys
+import optparse
+
+from git_multimail import CommandError
+from git_multimail import Config
+from git_multimail import read_output
+
+
+OLD_NAMES = [
+    'mailinglist',
+    'announcelist',
+    'envelopesender',
+    'emailprefix',
+    'showrev',
+    'emailmaxlines',
+    'diffopts',
+    ]
+
+NEW_NAMES = [
+    'environment',
+    'reponame',
+    'mailinglist',
+    'refchangelist',
+    'commitlist',
+    'announcelist',
+    'announceshortlog',
+    'envelopesender',
+    'administrator',
+    'emailprefix',
+    'emailmaxlines',
+    'diffopts',
+    'emaildomain',
+    ]
+
+
+INFO = """\
+
+SUCCESS!
+
+Your post-receive-email configuration has been converted to
+git-multimail format.  Please see README and
+README.migrate-from-post-receive-email to learn about other
+git-multimail configuration possibilities.
+
+For example, git-multimail has the following new options with no
+equivalent in post-receive-email.  You might want to read about them
+to see if they would be useful in your situation:
+
+"""
+
+
+def _check_old_config_exists(old):
+    """Check that at least one old configuration value is set."""
+
+    for name in OLD_NAMES:
+        if old.has_key(name):
+            return True
+
+    return False
+
+
+def _check_new_config_clear(new):
+    """Check that none of the new configuration names are set."""
+
+    retval = True
+    for name in NEW_NAMES:
+        if new.has_key(name):
+            if retval:
+                sys.stderr.write('INFO: The following configuration values already exist:\n\n')
+            sys.stderr.write('    "%s.%s"\n' % (new.section, name))
+            retval = False
+
+    return retval
+
+
+def erase_values(config, names):
+    for name in names:
+        if config.has_key(name):
+            try:
+                sys.stderr.write('...unsetting "%s.%s"\n' % (config.section, name))
+                config.unset_all(name)
+            except CommandError:
+                sys.stderr.write(
+                    '\nWARNING: could not unset "%s.%s".  '
+                    'Perhaps it is not set at the --local level?\n\n'
+                    % (config.section, name)
+                    )
+
+
+def is_section_empty(section, local):
+    """Return True iff the specified configuration section is empty.
+
+    Iff local is True, use the --local option when invoking 'git
+    config'."""
+
+    if local:
+        local_option = ['--local']
+    else:
+        local_option = []
+
+    try:
+        read_output(
+            ['git', 'config']
+            + local_option
+            + ['--get-regexp', '^%s\.' % (section,)]
+            )
+    except CommandError, e:
+        if e.retcode == 1:
+            # This means that no settings were found.
+            return True
+        else:
+            raise
+    else:
+        return False
+
+
+def remove_section_if_empty(section):
+    """If the specified configuration section is empty, delete it."""
+
+    try:
+        empty = is_section_empty(section, local=True)
+    except CommandError:
+        # Older versions of git do not support the --local option, so
+        # if the first attempt fails, try without --local.
+        try:
+            empty = is_section_empty(section, local=False)
+        except CommandError:
+            sys.stderr.write(
+                '\nINFO: If configuration section "%s.*" is empty, you might want '
+                'to delete it.\n\n'
+                % (section,)
+                )
+            return
+
+    if empty:
+        sys.stderr.write('...removing section "%s.*"\n' % (section,))
+        read_output(['git', 'config', '--remove-section', section])
+    else:
+        sys.stderr.write(
+            '\nINFO: Configuration section "%s.*" still has contents.  '
+            'It will not be deleted.\n\n'
+            % (section,)
+            )
+
+
+def migrate_config(strict=False, retain=False, overwrite=False):
+    old = Config('hooks')
+    new = Config('multimailhook')
+    if not _check_old_config_exists(old):
+        sys.exit(
+            'Your repository has no post-receive-email configuration.  '
+            'Nothing to do.'
+            )
+    if not _check_new_config_clear(new):
+        if overwrite:
+            sys.stderr.write('\nWARNING: Erasing the above values...\n\n')
+            erase_values(new, NEW_NAMES)
+        else:
+            sys.exit(
+                '\nERROR: Refusing to overwrite existing values.  Use the --overwrite\n'
+                'option to continue anyway.'
+                )
+
+    name = 'showrev'
+    if old.has_key(name):
+        msg = 'git-multimail does not support "%s.%s"' % (old.section, name,)
+        if strict:
+            sys.exit(
+                'ERROR: %s.\n'
+                'Please unset that value then try again, or run without --strict.'
+                % (msg,)
+                )
+        else:
+            sys.stderr.write('\nWARNING: %s (ignoring).\n\n' % (msg,))
+
+    for name in ['mailinglist', 'announcelist']:
+        if old.has_key(name):
+            sys.stderr.write(
+                '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
+                )
+            new.set_recipients(name, old.get_recipients(name))
+
+    if strict:
+        sys.stderr.write(
+            '...setting "%s.commitlist" to the empty string\n' % (new.section,)
+            )
+        new.set_recipients('commitlist', '')
+        sys.stderr.write(
+            '...setting "%s.announceshortlog" to "true"\n' % (new.section,)
+            )
+        new.set('announceshortlog', 'true')
+
+    for name in ['envelopesender', 'emailmaxlines', 'diffopts']:
+        if old.has_key(name):
+            sys.stderr.write(
+                '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
+                )
+            new.set(name, old.get(name))
+
+    name = 'emailprefix'
+    if old.has_key(name):
+        sys.stderr.write(
+            '...copying "%s.%s" to "%s.%s"\n' % (old.section, name, new.section, name)
+            )
+        new.set(name, old.get(name))
+    elif strict:
+        sys.stderr.write(
+            '...setting "%s.%s" to "[SCM]" to preserve old subject lines\n'
+            % (new.section, name)
+            )
+        new.set(name, '[SCM]')
+
+    if not retain:
+        erase_values(old, OLD_NAMES)
+        remove_section_if_empty(old.section)
+
+    sys.stderr.write(INFO)
+    for name in NEW_NAMES:
+        if name not in OLD_NAMES:
+            sys.stderr.write('    "%s.%s"\n' % (new.section, name,))
+    sys.stderr.write('\n')
+
+
+def main(args):
+    parser = optparse.OptionParser(
+        description=__doc__,
+        usage='%prog [OPTIONS]',
+        )
+
+    parser.add_option(
+        '--strict', action='store_true', default=False,
+        help=(
+            'Slavishly configure git-multimail as closely as possible to '
+            'the post-receive-email configuration.  Default is to turn '
+            'on some new features that have no equivalent in post-receive-email.'
+            ),
+        )
+    parser.add_option(
+        '--retain', action='store_true', default=False,
+        help=(
+            'Retain the post-receive-email configuration values.  '
+            'Default is to delete them after the new values are set.'
+            ),
+        )
+    parser.add_option(
+        '--overwrite', action='store_true', default=False,
+        help=(
+            'Overwrite any existing git-multimail configuration settings.  '
+            'Default is to abort if such settings already exist.'
+            ),
+        )
+
+    (options, args) = parser.parse_args(args)
+
+    if args:
+        parser.error('Unexpected arguments: %s' % (' '.join(args),))
+
+    migrate_config(strict=options.strict, retain=options.retain, overwrite=options.overwrite)
+
+
+main(sys.argv[1:])
diff --git a/contrib/hooks/multimail/post-receive b/contrib/hooks/multimail/post-receive
new file mode 100755 (executable)
index 0000000..93ebb43
--- /dev/null
@@ -0,0 +1,90 @@
+#! /usr/bin/env python2
+
+"""Example post-receive hook based on git-multimail.
+
+This script is a simple example of a post-receive hook implemented
+using git_multimail.py as a Python module.  It is intended to be
+customized before use; see the comments in the script to help you get
+started.
+
+It is possible to use git_multimail.py itself as a post-receive or
+update hook, configured via git config settings and/or command-line
+parameters.  But for more flexibility, it can also be imported as a
+Python module by a custom post-receive script as done here.  The
+latter has the following advantages:
+
+* The tool's behavior can be customized using arbitrary Python code,
+  without having to edit git_multimail.py.
+
+* Configuration settings can be read from other sources; for example,
+  user names and email addresses could be read from LDAP or from a
+  database.  Or the settings can even be hardcoded in the importing
+  Python script, if this is preferred.
+
+This script is a very basic example of how to use git_multimail.py as
+a module.  The comments below explain some of the points at which the
+script's behavior could be changed or customized.
+
+"""
+
+import sys
+import os
+
+# If necessary, add the path to the directory containing
+# git_multimail.py to the Python path as follows.  (This is not
+# necessary if git_multimail.py is in the same directory as this
+# script):
+
+#LIBDIR = 'path/to/directory/containing/module'
+#sys.path.insert(0, LIBDIR)
+
+import git_multimail
+
+
+# It is possible to modify the output templates here; e.g.:
+
+#git_multimail.FOOTER_TEMPLATE = """\
+#
+#-- \n\
+#This email was generated by the wonderful git-multimail tool.
+#"""
+
+
+# Specify which "git config" section contains the configuration for
+# git-multimail:
+config = git_multimail.Config('multimailhook')
+
+
+# Select the type of environment:
+environment = git_multimail.GenericEnvironment(config=config)
+#environment = git_multimail.GitoliteEnvironment(config=config)
+
+
+# Choose the method of sending emails based on the git config:
+mailer = git_multimail.choose_mailer(config, environment)
+
+# Alternatively, you may hardcode the mailer using code like one of
+# the following:
+
+# Use "/usr/sbin/sendmail -t" to send emails.  The envelopesender
+# argument is optional:
+#mailer = git_multimail.SendMailer(
+#    command=['/usr/sbin/sendmail', '-t'],
+#    envelopesender='git-repo@example.com',
+#    )
+
+# Use Python's smtplib to send emails.  Both arguments are required.
+#mailer = git_multimail.SMTPMailer(
+#    envelopesender='git-repo@example.com',
+#    # The smtpserver argument can also include a port number; e.g.,
+#    #     smtpserver='mail.example.com:25'
+#    smtpserver='mail.example.com',
+#    )
+
+# OutputMailer is intended only for testing; it writes the emails to
+# the specified file stream.
+#mailer = git_multimail.OutputMailer(sys.stdout)
+
+
+# Read changes from stdin and send notification emails:
+git_multimail.run_as_post_receive_hook(environment, mailer)
index 0e5b72d7f19185c3b6b36150085d475c7ef15db4..153115029df0764a3f42812a9a2bd8992360c4bd 100755 (executable)
@@ -2,10 +2,19 @@
 #
 # Copyright (c) 2007 Andy Parkins
 #
-# An example hook script to mail out commit update information.  This hook
-# sends emails listing new revisions to the repository introduced by the
-# change being reported.  The rule is that (for branch updates) each commit
-# will appear on one email and one email only.
+# An example hook script to mail out commit update information.
+#
+# NOTE: This script is no longer under active development.  There
+# is another script, git-multimail, which is more capable and
+# configurable and is largely backwards-compatible with this script;
+# please see "contrib/hooks/multimail/".  For instructions on how to
+# migrate from post-receive-email to git-multimail, please see
+# "README.migrate-from-post-receive-email" in that directory.
+#
+# This hook sends emails listing new revisions to the repository
+# introduced by the change being reported.  The rule is that (for
+# branch updates) each commit will appear on one email and one email
+# only.
 #
 # This hook is stored in the contrib/hooks directory.  Your distribution
 # will have put this somewhere standard.  You should make this script
index b9196555e56ba9d0e522cfe8d2964662248a0b78..ae545b013dc818472ebecd86c61b2f1cda0279e9 100644 (file)
@@ -1 +1,2 @@
 git-remote-mediawiki
+git-mw
diff --git a/contrib/mw-to-git/.perlcriticrc b/contrib/mw-to-git/.perlcriticrc
new file mode 100644 (file)
index 0000000..5a9955d
--- /dev/null
@@ -0,0 +1,28 @@
+# These 3 rules demand to add the s, m and x flag to *every* regexp. This is
+# overkill and would be harmful for readability.
+[-RegularExpressions::RequireExtendedFormatting]
+[-RegularExpressions::RequireDotMatchAnything]
+[-RegularExpressions::RequireLineBoundaryMatching]
+
+# This rule says that builtin functions should not be called with parentheses
+# e.g.: (taken from CPAN's documentation)
+# open($handle, '>', $filename); #not ok
+# open $handle, '>', $filename;  #ok
+# Applying such a rule would mean modifying a huge number of lines for a
+# question of style.
+[-CodeLayout::ProhibitParensWithBuiltins]
+
+# This rule states that each system call should have its return value checked
+# The problem is that it includes the print call. Checking every print call's
+# return value would be harmful to the code readabilty.
+# This configuration keeps all default function but print.
+[InputOutput::RequireCheckedSyscalls]
+functions = open say close
+
+# This rules demands to add a dependancy for the Readonly module. This is not
+# wished.
+[-ValuesAndExpressions::ProhibitConstantPragma]
+
+# This rule is not really useful (rather a question of style) and produces many
+# warnings among the code.
+[-ValuesAndExpressions::ProhibitNoisyQuotes]
diff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm
new file mode 100644 (file)
index 0000000..d13c4df
--- /dev/null
@@ -0,0 +1,100 @@
+package Git::Mediawiki;
+
+use 5.008;
+use strict;
+use Git;
+
+BEGIN {
+
+our ($VERSION, @ISA, @EXPORT, @EXPORT_OK);
+
+# Totally unstable API.
+$VERSION = '0.01';
+
+require Exporter;
+
+@ISA = qw(Exporter);
+
+@EXPORT = ();
+
+# Methods which can be called as standalone functions as well:
+@EXPORT_OK = qw(clean_filename smudge_filename connect_maybe
+                               EMPTY HTTP_CODE_OK HTTP_CODE_PAGE_NOT_FOUND);
+}
+
+# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced
+use constant SLASH_REPLACEMENT => '%2F';
+
+# Used to test for empty strings
+use constant EMPTY => q{};
+
+# HTTP codes
+use constant HTTP_CODE_OK => 200;
+use constant HTTP_CODE_PAGE_NOT_FOUND => 404;
+
+sub clean_filename {
+       my $filename = shift;
+       $filename =~ s{@{[SLASH_REPLACEMENT]}}{/}g;
+       # [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded.
+       # Do a variant of URL-encoding, i.e. looks like URL-encoding,
+       # but with _ added to prevent MediaWiki from thinking this is
+       # an actual special character.
+       $filename =~ s/[\[\]\{\}\|]/sprintf("_%%_%x", ord($&))/ge;
+       # If we use the uri escape before
+       # we should unescape here, before anything
+
+       return $filename;
+}
+
+sub smudge_filename {
+       my $filename = shift;
+       $filename =~ s{/}{@{[SLASH_REPLACEMENT]}}g;
+       $filename =~ s/ /_/g;
+       # Decode forbidden characters encoded in clean_filename
+       $filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge;
+       return $filename;
+}
+
+sub connect_maybe {
+       my $wiki = shift;
+       if ($wiki) {
+               return $wiki;
+       }
+
+       my $remote_name = shift;
+       my $remote_url = shift;
+       my ($wiki_login, $wiki_password, $wiki_domain);
+
+       $wiki_login = Git::config("remote.${remote_name}.mwLogin");
+       $wiki_password = Git::config("remote.${remote_name}.mwPassword");
+       $wiki_domain = Git::config("remote.${remote_name}.mwDomain");
+
+       $wiki = MediaWiki::API->new;
+       $wiki->{config}->{api_url} = "${remote_url}/api.php";
+       if ($wiki_login) {
+               my %credential = (
+                       'url' => $remote_url,
+                       'username' => $wiki_login,
+                       'password' => $wiki_password
+               );
+               Git::credential(\%credential);
+               my $request = {lgname => $credential{username},
+                              lgpassword => $credential{password},
+                              lgdomain => $wiki_domain};
+               if ($wiki->login($request)) {
+                       Git::credential(\%credential, 'approve');
+                       print {*STDERR} qq(Logged in mediawiki user "$credential{username}".\n);
+               } else {
+                       print {*STDERR} qq(Failed to log in mediawiki user "$credential{username}" on ${remote_url}\n);
+                       print {*STDERR} '  (error ' .
+                               $wiki->{error}->{code} . ': ' .
+                               $wiki->{error}->{details} . ")\n";
+                       Git::credential(\%credential, 'reject');
+                       exit 1;
+               }
+       }
+
+       return $wiki;
+}
+
+1; # Famous last words
index f14971987c3d14892ad212485c4b1662d73165f7..76fcd4defcc3ac751aa0c85b6388f6d408b476ca 100644 (file)
@@ -2,16 +2,43 @@
 # Copyright (C) 2013
 #     Matthieu Moy <Matthieu.Moy@imag.fr>
 #
-## Build git-remote-mediawiki
+# To build and test:
+#
+#   make
+#   bin-wrapper/git mw preview Some_page.mw
+#   bin-wrapper/git clone mediawiki::http://example.com/wiki/
+#
+# To install, run Git's toplevel 'make install' then run:
+#
+#   make install
 
+GIT_MEDIAWIKI_PM=Git/Mediawiki.pm
 SCRIPT_PERL=git-remote-mediawiki.perl
+SCRIPT_PERL+=git-mw.perl
 GIT_ROOT_DIR=../..
 HERE=contrib/mw-to-git/
 
 SCRIPT_PERL_FULL=$(patsubst %,$(HERE)/%,$(SCRIPT_PERL))
+INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/perl \
+                -s --no-print-directory instlibdir)
 
 all: build
 
-build install clean:
-       $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=$(SCRIPT_PERL_FULL) \
-                $@-perl-script
+install_pm:
+       install $(GIT_MEDIAWIKI_PM) $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM)
+
+build:
+       $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                build-perl-script
+
+install: install_pm
+       $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                install-perl-script
+
+clean:
+       $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                clean-perl-script
+       rm $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM)
+
+perlcritic:
+       perlcritic -2 *.perl
diff --git a/contrib/mw-to-git/bin-wrapper/git b/contrib/mw-to-git/bin-wrapper/git
new file mode 100755 (executable)
index 0000000..6663ae5
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# git executable wrapper script for Git-Mediawiki to run tests without
+# installing all the scripts and perl packages.
+
+GIT_ROOT_DIR=../../..
+GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd ${GIT_ROOT_DIR} && pwd)
+
+GITPERLLIB="$GIT_EXEC_PATH"'/contrib/mw-to-git'"${GITPERLLIB:+:$GITPERLLIB}"
+PATH="$GIT_EXEC_PATH"'/contrib/mw-to-git:'"$PATH"
+
+export GITPERLLIB PATH
+
+exec "${GIT_EXEC_PATH}/bin-wrappers/git" "$@"
diff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl
new file mode 100755 (executable)
index 0000000..28df3ee
--- /dev/null
@@ -0,0 +1,368 @@
+#!/usr/bin/perl
+
+# Copyright (C) 2013
+#     Benoit Person <benoit.person@ensimag.imag.fr>
+#     Celestin Matte <celestin.matte@ensimag.imag.fr>
+# License: GPL v2 or later
+
+# Set of tools for git repo with a mediawiki remote.
+# Documentation & bugtracker: https://github.com/moy/Git-Mediawiki/
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use URI::URL qw(url);
+use LWP::UserAgent;
+use HTML::TreeBuilder;
+
+use Git;
+use MediaWiki::API;
+use Git::Mediawiki qw(clean_filename connect_maybe
+                                       EMPTY HTTP_CODE_PAGE_NOT_FOUND);
+
+# By default, use UTF-8 to communicate with Git and the user
+binmode STDERR, ':encoding(UTF-8)';
+binmode STDOUT, ':encoding(UTF-8)';
+
+# Global parameters
+my $verbose = 0;
+sub v_print {
+       if ($verbose) {
+               return print {*STDERR} @_;
+       }
+       return;
+}
+
+# Preview parameters
+my $file_name = EMPTY;
+my $remote_name = EMPTY;
+my $preview_file_name = EMPTY;
+my $autoload = 0;
+sub file {
+       $file_name = shift;
+       return $file_name;
+}
+
+my %commands = (
+       'help' =>
+               [\&help, {}, \&help],
+       'preview' =>
+               [\&preview, {
+                       '<>' => \&file,
+                       'output|o=s' => \$preview_file_name,
+                       'remote|r=s' => \$remote_name,
+                       'autoload|a' => \$autoload
+               }, \&preview_help]
+);
+
+# Search for sub-command
+my $cmd = $commands{'help'};
+for (0..@ARGV-1) {
+       if (defined $commands{$ARGV[$_]}) {
+               $cmd = $commands{$ARGV[$_]};
+               splice @ARGV, $_, 1;
+               last;
+       }
+};
+GetOptions( %{$cmd->[1]},
+       'help|h' => \&{$cmd->[2]},
+       'verbose|v'  => \$verbose);
+
+# Launch command
+&{$cmd->[0]};
+
+############################# Preview Functions ################################
+
+sub preview_help {
+       print {*STDOUT} <<'END';
+USAGE: git mw preview [--remote|-r <remote name>] [--autoload|-a]
+                      [--output|-o <output filename>] [--verbose|-v]
+                      <blob> | <filename>
+
+DESCRIPTION:
+Preview is an utiliy to preview local content of a mediawiki repo as if it was
+pushed on the remote.
+
+For that, preview searches for the remote name of the current branch's
+upstream if --remote is not set. If that remote is not found or if it
+is not a mediawiki, it lists all mediawiki remotes configured and asks
+you to replay your command with the --remote option set properly.
+
+Then, it searches for a file named 'filename'. If it's not found in
+the current dir, it will assume it's a blob.
+
+The content retrieved in the file (or in the blob) will then be parsed
+by the remote mediawiki and combined with a template retrieved from
+the mediawiki.
+
+Finally, preview will save the HTML result in a file. and autoload it
+in your default web browser if the option --autoload is present.
+
+OPTIONS:
+    -r <remote name>, --remote <remote name>
+        If the remote is a mediawiki, the template and the parse engine
+        used for the preview will be those of that remote.
+        If not, a list of valid remotes will be shown.
+
+    -a, --autoload
+        Try to load the HTML output in a new tab (or new window) of your
+        default web browser.
+
+    -o <output filename>, --output <output filename>
+        Change the HTML output filename. Default filename is based on the
+        input filename with its extension replaced by '.html'.
+
+    -v, --verbose
+        Show more information on what's going on under the hood.
+END
+       exit;
+}
+
+sub preview {
+       my $wiki;
+       my ($remote_url, $wiki_page_name);
+       my ($new_content, $template);
+       my $file_content;
+
+       if ($file_name eq EMPTY) {
+               die "Missing file argument, see `git mw help`\n";
+       }
+
+       v_print("### Selecting remote\n");
+       if ($remote_name eq EMPTY) {
+               $remote_name = find_upstream_remote_name();
+               if ($remote_name) {
+                       $remote_url = mediawiki_remote_url_maybe($remote_name);
+               }
+
+               if (! $remote_url) {
+                       my @valid_remotes = find_mediawiki_remotes();
+
+                       if ($#valid_remotes == 0) {
+                               print {*STDERR} "No mediawiki remote in this repo. \n";
+                               exit 1;
+                       } else {
+                               my $remotes_list = join("\n\t", @valid_remotes);
+                               print {*STDERR} <<"MESSAGE";
+There are multiple mediawiki remotes, which of:
+       ${remotes_list}
+do you want ? Use the -r option to specify the remote.
+MESSAGE
+                       }
+
+                       exit 1;
+               }
+       } else {
+               if (!is_valid_remote($remote_name)) {
+                       die "${remote_name} is not a remote\n";
+               }
+
+               $remote_url = mediawiki_remote_url_maybe($remote_name);
+               if (! $remote_url) {
+                       die "${remote_name} is not a mediawiki remote\n";
+               }
+       }
+       v_print("selected remote:\n\tname: ${remote_name}\n\turl: ${remote_url}\n");
+
+       $wiki = connect_maybe($wiki, $remote_name, $remote_url);
+
+       # Read file content
+       if (! -e $file_name) {
+               $file_content = git_cmd_try {
+                       Git::command('cat-file', 'blob', $file_name); }
+                       "%s failed w/ code %d";
+
+               if ($file_name =~ /(.+):(.+)/) {
+                       $file_name = $2;
+               }
+       } else {
+               open my $read_fh, "<", $file_name
+                       or die "could not open ${file_name}: $!\n";
+               $file_content = do { local $/ = undef; <$read_fh> };
+               close $read_fh
+                       or die "unable to close: $!\n";
+       }
+
+       v_print("### Retrieving template\n");
+       ($wiki_page_name = clean_filename($file_name)) =~ s/\.[^.]+$//;
+       $template = get_template($remote_url, $wiki_page_name);
+
+       v_print("### Parsing local content\n");
+       $new_content = $wiki->api({
+               action => 'parse',
+               text => $file_content,
+               title => $wiki_page_name
+       }, {
+               skip_encoding => 1
+       }) or die "No response from remote mediawiki\n";
+       $new_content = $new_content->{'parse'}->{'text'}->{'*'};
+
+       v_print("### Merging contents\n");
+       if ($preview_file_name eq EMPTY) {
+               ($preview_file_name = $file_name) =~ s/\.[^.]+$/.html/;
+       }
+       open(my $save_fh, '>:encoding(UTF-8)', $preview_file_name)
+               or die "Could not open: $!\n";
+       print {$save_fh} merge_contents($template, $new_content, $remote_url);
+       close($save_fh)
+               or die "Could not close: $!\n";
+
+       v_print("### Results\n");
+       if ($autoload) {
+               v_print("Launching browser w/ file: ${preview_file_name}");
+               system('git', 'web--browse', $preview_file_name);
+       } else {
+               print {*STDERR} "Preview file saved as: ${preview_file_name}\n";
+       }
+
+       exit;
+}
+
+# uses global scope variable: $remote_name
+sub merge_contents {
+       my $template = shift;
+       my $content = shift;
+       my $remote_url = shift;
+       my ($content_tree, $html_tree, $mw_content_text);
+       my $template_content_id = 'bodyContent';
+
+       $html_tree = HTML::TreeBuilder->new;
+       $html_tree->parse($template);
+
+       $content_tree = HTML::TreeBuilder->new;
+       $content_tree->parse($content);
+
+       $template_content_id = Git::config("remote.${remote_name}.mwIDcontent")
+               || $template_content_id;
+       v_print("Using '${template_content_id}' as the content ID\n");
+
+       $mw_content_text = $html_tree->look_down('id', $template_content_id);
+       if (!defined $mw_content_text) {
+               print {*STDERR} <<"CONFIG";
+Could not combine the new content with the template. You might want to
+configure `mediawiki.IDContent` in your config:
+       git config --add remote.${remote_name}.mwIDcontent <id>
+and re-run the command afterward.
+CONFIG
+               exit 1;
+       }
+       $mw_content_text->delete_content();
+       $mw_content_text->push_content($content_tree);
+
+       make_links_absolute($html_tree, $remote_url);
+
+       return $html_tree->as_HTML;
+}
+
+sub make_links_absolute {
+       my $html_tree = shift;
+       my $remote_url = shift;
+       for (@{ $html_tree->extract_links() }) {
+               my ($link, $element, $attr) = @{ $_ };
+               my $url = url($link)->canonical;
+               if ($url !~ /#/) {
+                       $element->attr($attr, URI->new_abs($url, $remote_url));
+               }
+       }
+       return $html_tree;
+}
+
+sub is_valid_remote {
+       my $remote = shift;
+       my @remotes = git_cmd_try {
+               Git::command('remote') }
+               "%s failed w/ code %d";
+       my $found_remote = 0;
+       foreach my $remote (@remotes) {
+               if ($remote eq $remote) {
+                       $found_remote = 1;
+                       last;
+               }
+       }
+       return $found_remote;
+}
+
+sub find_mediawiki_remotes {
+       my @remotes = git_cmd_try {
+               Git::command('remote'); }
+               "%s failed w/ code %d";
+       my $remote_url;
+       my @valid_remotes = ();
+       foreach my $remote (@remotes) {
+               $remote_url = mediawiki_remote_url_maybe($remote);
+               if ($remote_url) {
+                       push(@valid_remotes, $remote);
+               }
+       }
+       return @valid_remotes;
+}
+
+sub find_upstream_remote_name {
+       my $current_branch = git_cmd_try {
+               Git::command_oneline('symbolic-ref', '--short', 'HEAD') }
+               "%s failed w/ code %d";
+       return Git::config("branch.${current_branch}.remote");
+}
+
+sub mediawiki_remote_url_maybe {
+       my $remote = shift;
+
+       # Find remote url
+       my $remote_url = Git::config("remote.${remote}.url");
+       if ($remote_url =~ s/mediawiki::(.*)/$1/) {
+               return url($remote_url)->canonical;
+       }
+
+       return;
+}
+
+sub get_template {
+       my $url = shift;
+       my $page_name = shift;
+       my ($req, $res, $code, $url_after);
+
+       $req = LWP::UserAgent->new;
+       if ($verbose) {
+               $req->show_progress(1);
+       }
+
+       $res = $req->get("${url}/index.php?title=${page_name}");
+       if (!$res->is_success) {
+               $code = $res->code;
+               $url_after = $res->request()->uri(); # resolve all redirections
+               if ($code == HTTP_CODE_PAGE_NOT_FOUND) {
+                       if ($verbose) {
+                               print {*STDERR} <<"WARNING";
+Warning: Failed to retrieve '$page_name'. Create it on the mediawiki if you want
+all the links to work properly.
+Trying to use the mediawiki homepage as a fallback template ...
+WARNING
+                       }
+
+                       # LWP automatically redirects GET request
+                       $res = $req->get("${url}/index.php");
+                       if (!$res->is_success) {
+                               $url_after = $res->request()->uri(); # resolve all redirections
+                               die "Failed to get homepage @ ${url_after} w/ code ${code}\n";
+                       }
+               } else {
+                       die "Failed to get '${page_name}' @ ${url_after} w/ code ${code}\n";
+               }
+       }
+
+       return $res->decoded_content;
+}
+
+############################## Help Functions ##################################
+
+sub help {
+       print {*STDOUT} <<'END';
+usage: git mw <command> <args>
+
+git mw commands are:
+    help        Display help information about git mw
+    preview     Parse and render local file into HTML
+END
+       exit;
+}
index 9c14c1f88d3a8f23df54db2adc7985eb00722c12..85d0c422961f990aa70a6cc5ed70ab319b5ca99a 100755 (executable)
 
 use strict;
 use MediaWiki::API;
+use Git;
+use Git::Mediawiki qw(clean_filename smudge_filename connect_maybe
+                                       EMPTY HTTP_CODE_OK);
 use DateTime::Format::ISO8601;
+use warnings;
 
 # By default, use UTF-8 to communicate with Git and the user
-binmode STDERR, ":utf8";
-binmode STDOUT, ":utf8";
+binmode STDERR, ':encoding(UTF-8)';
+binmode STDOUT, ':encoding(UTF-8)';
 
 use URI::Escape;
-use IPC::Open2;
-
-use warnings;
-
-# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced
-use constant SLASH_REPLACEMENT => "%2F";
 
 # It's not always possible to delete pages (may require some
 # privileges). Deleted pages are replaced with this content.
@@ -36,45 +34,57 @@ use constant DELETED_CONTENT => "[[Category:Deleted]]\n";
 use constant EMPTY_CONTENT => "<!-- empty page -->\n";
 
 # used to reflect file creation or deletion in diff.
-use constant NULL_SHA1 => "0000000000000000000000000000000000000000";
+use constant NULL_SHA1 => '0000000000000000000000000000000000000000';
 
 # Used on Git's side to reflect empty edit messages on the wiki
 use constant EMPTY_MESSAGE => '*Empty MediaWiki Message*';
 
+# Number of pages taken into account at once in submodule get_mw_page_list
+use constant SLICE_SIZE => 50;
+
+# Number of linked mediafile to get at once in get_linked_mediafiles
+# The query is split in small batches because of the MW API limit of
+# the number of links to be returned (500 links max).
+use constant BATCH_SIZE => 10;
+
+if (@ARGV != 2) {
+       exit_error_usage();
+}
+
 my $remotename = $ARGV[0];
 my $url = $ARGV[1];
 
 # Accept both space-separated and multiple keys in config file.
 # Spaces should be written as _ anyway because we'll use chomp.
-my @tracked_pages = split(/[ \n]/, run_git("config --get-all remote.". $remotename .".pages"));
+my @tracked_pages = split(/[ \n]/, run_git("config --get-all remote.${remotename}.pages"));
 chomp(@tracked_pages);
 
 # Just like @tracked_pages, but for MediaWiki categories.
-my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.". $remotename .".categories"));
+my @tracked_categories = split(/[ \n]/, run_git("config --get-all remote.${remotename}.categories"));
 chomp(@tracked_categories);
 
 # Import media files on pull
-my $import_media = run_git("config --get --bool remote.". $remotename .".mediaimport");
+my $import_media = run_git("config --get --bool remote.${remotename}.mediaimport");
 chomp($import_media);
-$import_media = ($import_media eq "true");
+$import_media = ($import_media eq 'true');
 
 # Export media files on push
-my $export_media = run_git("config --get --bool remote.". $remotename .".mediaexport");
+my $export_media = run_git("config --get --bool remote.${remotename}.mediaexport");
 chomp($export_media);
-$export_media = !($export_media eq "false");
+$export_media = !($export_media eq 'false');
 
-my $wiki_login = run_git("config --get remote.". $remotename .".mwLogin");
+my $wiki_login = run_git("config --get remote.${remotename}.mwLogin");
 # Note: mwPassword is discourraged. Use the credential system instead.
-my $wiki_passwd = run_git("config --get remote.". $remotename .".mwPassword");
-my $wiki_domain = run_git("config --get remote.". $remotename .".mwDomain");
+my $wiki_passwd = run_git("config --get remote.${remotename}.mwPassword");
+my $wiki_domain = run_git("config --get remote.${remotename}.mwDomain");
 chomp($wiki_login);
 chomp($wiki_passwd);
 chomp($wiki_domain);
 
 # Import only last revisions (both for clone and fetch)
-my $shallow_import = run_git("config --get --bool remote.". $remotename .".shallow");
+my $shallow_import = run_git("config --get --bool remote.${remotename}.shallow");
 chomp($shallow_import);
-$shallow_import = ($shallow_import eq "true");
+$shallow_import = ($shallow_import eq 'true');
 
 # Fetch (clone and pull) by revisions instead of by pages. This behavior
 # is more efficient when we have a wiki with lots of pages and we fetch
@@ -82,15 +92,18 @@ $shallow_import = ($shallow_import eq "true");
 # Possible values:
 # - by_rev: perform one query per new revision on the remote wiki
 # - by_page: query each tracked page for new revision
-my $fetch_strategy = run_git("config --get remote.$remotename.fetchStrategy");
-unless ($fetch_strategy) {
-       $fetch_strategy = run_git("config --get mediawiki.fetchStrategy");
+my $fetch_strategy = run_git("config --get remote.${remotename}.fetchStrategy");
+if (!$fetch_strategy) {
+       $fetch_strategy = run_git('config --get mediawiki.fetchStrategy');
 }
 chomp($fetch_strategy);
-unless ($fetch_strategy) {
-       $fetch_strategy = "by_page";
+if (!$fetch_strategy) {
+       $fetch_strategy = 'by_page';
 }
 
+# Remember the timestamp corresponding to a revision id.
+my %basetimestamps;
+
 # Dumb push: don't update notes and mediawiki ref to reflect the last push.
 #
 # Configurable with mediawiki.dumbPush, or per-remote with
@@ -105,48 +118,25 @@ unless ($fetch_strategy) {
 # will get the history with information lost). If the import is
 # deterministic, this means everybody gets the same sha1 for each
 # MediaWiki revision.
-my $dumb_push = run_git("config --get --bool remote.$remotename.dumbPush");
-unless ($dumb_push) {
-       $dumb_push = run_git("config --get --bool mediawiki.dumbPush");
+my $dumb_push = run_git("config --get --bool remote.${remotename}.dumbPush");
+if (!$dumb_push) {
+       $dumb_push = run_git('config --get --bool mediawiki.dumbPush');
 }
 chomp($dumb_push);
-$dumb_push = ($dumb_push eq "true");
+$dumb_push = ($dumb_push eq 'true');
 
 my $wiki_name = $url;
-$wiki_name =~ s/[^\/]*:\/\///;
+$wiki_name =~ s{[^/]*://}{};
 # If URL is like http://user:password@example.com/, we clearly don't
 # want the password in $wiki_name. While we're there, also remove user
 # and '@' sign, to avoid author like MWUser@HTTPUser@host.com
 $wiki_name =~ s/^.*@//;
 
 # Commands parser
-my $entry;
-my @cmd;
 while (<STDIN>) {
        chomp;
-       @cmd = split(/ /);
-       if (defined($cmd[0])) {
-               # Line not blank
-               if ($cmd[0] eq "capabilities") {
-                       die("Too many arguments for capabilities") unless (!defined($cmd[1]));
-                       mw_capabilities();
-               } elsif ($cmd[0] eq "list") {
-                       die("Too many arguments for list") unless (!defined($cmd[2]));
-                       mw_list($cmd[1]);
-               } elsif ($cmd[0] eq "import") {
-                       die("Invalid arguments for import") unless ($cmd[1] ne "" && !defined($cmd[2]));
-                       mw_import($cmd[1]);
-               } elsif ($cmd[0] eq "option") {
-                       die("Too many arguments for option") unless ($cmd[1] ne "" && $cmd[2] ne "" && !defined($cmd[3]));
-                       mw_option($cmd[1],$cmd[2]);
-               } elsif ($cmd[0] eq "push") {
-                       mw_push($cmd[1]);
-               } else {
-                       print STDERR "Unknown command. Aborting...\n";
-                       last;
-               }
-       } else {
-               # blank line: we should terminate
+
+       if (!parse_command($_)) {
                last;
        }
 
@@ -156,107 +146,91 @@ while (<STDIN>) {
 
 ########################## Functions ##############################
 
-## credential API management (generic functions)
-
-sub credential_read {
-       my %credential;
-       my $reader = shift;
-       my $op = shift;
-       while (<$reader>) {
-               my ($key, $value) = /([^=]*)=(.*)/;
-               if (not defined $key) {
-                       die "ERROR receiving response from git credential $op:\n$_\n";
-               }
-               $credential{$key} = $value;
-       }
-       return %credential;
+## error handling
+sub exit_error_usage {
+       die "ERROR: git-remote-mediawiki module was not called with a correct number of\n" .
+           "parameters\n" .
+           "You may obtain this error because you attempted to run the git-remote-mediawiki\n" .
+            "module directly.\n" .
+           "This module can be used the following way:\n" .
+           "\tgit clone mediawiki://<address of a mediawiki>\n" .
+           "Then, use git commit, push and pull as with every normal git repository.\n";
 }
 
-sub credential_write {
-       my $credential = shift;
-       my $writer = shift;
-       # url overwrites other fields, so it must come first
-       print $writer "url=$credential->{url}\n" if exists $credential->{url};
-       while (my ($key, $value) = each(%$credential) ) {
-               if (length $value && $key ne 'url') {
-                       print $writer "$key=$value\n";
-               }
+sub parse_command {
+       my ($line) = @_;
+       my @cmd = split(/ /, $line);
+       if (!defined $cmd[0]) {
+               return 0;
        }
-}
-
-sub credential_run {
-       my $op = shift;
-       my $credential = shift;
-       my $pid = open2(my $reader, my $writer, "git credential $op");
-       credential_write($credential, $writer);
-       print $writer "\n";
-       close($writer);
-
-       if ($op eq "fill") {
-               %$credential = credential_read($reader, $op);
+       if ($cmd[0] eq 'capabilities') {
+               die("Too many arguments for capabilities\n")
+                   if (defined($cmd[1]));
+               mw_capabilities();
+       } elsif ($cmd[0] eq 'list') {
+               die("Too many arguments for list\n") if (defined($cmd[2]));
+               mw_list($cmd[1]);
+       } elsif ($cmd[0] eq 'import') {
+               die("Invalid argument for import\n")
+                   if ($cmd[1] eq EMPTY);
+               die("Too many arguments for import\n")
+                   if (defined($cmd[2]));
+               mw_import($cmd[1]);
+       } elsif ($cmd[0] eq 'option') {
+               die("Invalid arguments for option\n")
+                   if ($cmd[1] eq EMPTY || $cmd[2] eq EMPTY);
+               die("Too many arguments for option\n")
+                   if (defined($cmd[3]));
+               mw_option($cmd[1],$cmd[2]);
+       } elsif ($cmd[0] eq 'push') {
+               mw_push($cmd[1]);
        } else {
-               if (<$reader>) {
-                       die "ERROR while running git credential $op:\n$_";
-               }
-       }
-       close($reader);
-       waitpid($pid, 0);
-       my $child_exit_status = $? >> 8;
-       if ($child_exit_status != 0) {
-               die "'git credential $op' failed with code $child_exit_status.";
+               print {*STDERR} "Unknown command. Aborting...\n";
+               return 0;
        }
+       return 1;
 }
 
 # MediaWiki API instance, created lazily.
 my $mediawiki;
 
-sub mw_connect_maybe {
-       if ($mediawiki) {
-               return;
-       }
-       $mediawiki = MediaWiki::API->new;
-       $mediawiki->{config}->{api_url} = "$url/api.php";
-       if ($wiki_login) {
-               my %credential = (url => $url);
-               $credential{username} = $wiki_login;
-               $credential{password} = $wiki_passwd;
-               credential_run("fill", \%credential);
-               my $request = {lgname => $credential{username},
-                              lgpassword => $credential{password},
-                              lgdomain => $wiki_domain};
-               if ($mediawiki->login($request)) {
-                       credential_run("approve", \%credential);
-                       print STDERR "Logged in mediawiki user \"$credential{username}\".\n";
-               } else {
-                       print STDERR "Failed to log in mediawiki user \"$credential{username}\" on $url\n";
-                       print STDERR "  (error " .
-                               $mediawiki->{error}->{code} . ': ' .
-                               $mediawiki->{error}->{details} . ")\n";
-                       credential_run("reject", \%credential);
-                       exit 1;
-               }
+sub fatal_mw_error {
+       my $action = shift;
+       print STDERR "fatal: could not $action.\n";
+       print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
+       if ($url =~ /^https/) {
+               print STDERR "fatal: make sure '$url/api.php' is a valid page\n";
+               print STDERR "fatal: and the SSL certificate is correct.\n";
+       } else {
+               print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
        }
+       print STDERR "fatal: (error " .
+           $mediawiki->{error}->{code} . ': ' .
+           $mediawiki->{error}->{details} . ")\n";
+       exit 1;
 }
 
 ## Functions for listing pages on the remote wiki
 sub get_mw_tracked_pages {
        my $pages = shift;
        get_mw_page_list(\@tracked_pages, $pages);
+       return;
 }
 
 sub get_mw_page_list {
        my $page_list = shift;
        my $pages = shift;
-       my @some_pages = @$page_list;
+       my @some_pages = @{$page_list};
        while (@some_pages) {
-               my $last = 50;
-               if ($#some_pages < $last) {
-                       $last = $#some_pages;
+               my $last_page = SLICE_SIZE;
+               if ($#some_pages < $last_page) {
+                       $last_page = $#some_pages;
                }
-               my @slice = @some_pages[0..$last];
+               my @slice = @some_pages[0..$last_page];
                get_mw_first_pages(\@slice, $pages);
-               @some_pages = @some_pages[51..$#some_pages];
+               @some_pages = @some_pages[(SLICE_SIZE + 1)..$#some_pages];
        }
+       return;
 }
 
 sub get_mw_tracked_categories {
@@ -266,7 +240,7 @@ sub get_mw_tracked_categories {
                        # Mediawiki requires the Category
                        # prefix, but let's not force the user
                        # to specify it.
-                       $category = "Category:" . $category;
+                       $category = "Category:${category}";
                }
                my $mw_pages = $mediawiki->list( {
                        action => 'query',
@@ -274,11 +248,12 @@ sub get_mw_tracked_categories {
                        cmtitle => $category,
                        cmlimit => 'max' } )
                        || die $mediawiki->{error}->{code} . ': '
-                               . $mediawiki->{error}->{details};
+                               . $mediawiki->{error}->{details} . "\n";
                foreach my $page (@{$mw_pages}) {
                        $pages->{$page->{title}} = $page;
                }
        }
+       return;
 }
 
 sub get_mw_all_pages {
@@ -290,14 +265,12 @@ sub get_mw_all_pages {
                aplimit => 'max'
        });
        if (!defined($mw_pages)) {
-               print STDERR "fatal: could not get the list of wiki pages.\n";
-               print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
-               print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
-               exit 1;
+               fatal_mw_error("get the list of wiki pages");
        }
        foreach my $page (@{$mw_pages}) {
                $pages->{$page->{title}} = $page;
        }
+       return;
 }
 
 # queries the wiki for a set of pages. Meant to be used within a loop
@@ -316,25 +289,23 @@ sub get_mw_first_pages {
                titles => $titles,
        });
        if (!defined($mw_pages)) {
-               print STDERR "fatal: could not query the list of wiki pages.\n";
-               print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
-               print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
-               exit 1;
+               fatal_mw_error("query the list of wiki pages");
        }
        while (my ($id, $page) = each(%{$mw_pages->{query}->{pages}})) {
                if ($id < 0) {
-                       print STDERR "Warning: page $page->{title} not found on wiki\n";
+                       print {*STDERR} "Warning: page $page->{title} not found on wiki\n";
                } else {
                        $pages->{$page->{title}} = $page;
                }
        }
+       return;
 }
 
 # Get the list of pages to be fetched according to configuration.
 sub get_mw_pages {
-       mw_connect_maybe();
+       $mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
-       print STDERR "Listing pages on remote wiki...\n";
+       print {*STDERR} "Listing pages on remote wiki...\n";
 
        my %pages; # hash on page titles to avoid duplicates
        my $user_defined;
@@ -352,14 +323,14 @@ sub get_mw_pages {
                get_mw_all_pages(\%pages);
        }
        if ($import_media) {
-               print STDERR "Getting media files for selected pages...\n";
+               print {*STDERR} "Getting media files for selected pages...\n";
                if ($user_defined) {
                        get_linked_mediafiles(\%pages);
                } else {
                        get_all_mediafiles(\%pages);
                }
        }
-       print STDERR (scalar keys %pages) . " pages found.\n";
+       print {*STDERR} (scalar keys %pages) . " pages found.\n";
        return %pages;
 }
 
@@ -367,9 +338,13 @@ sub get_mw_pages {
 #        $out = run_git("command args", "raw"); # don't interpret output as UTF-8.
 sub run_git {
        my $args = shift;
-       my $encoding = (shift || "encoding(UTF-8)");
-       open(my $git, "-|:$encoding", "git " . $args);
-       my $res = do { local $/; <$git> };
+       my $encoding = (shift || 'encoding(UTF-8)');
+       open(my $git, "-|:${encoding}", "git ${args}")
+           or die "Unable to fork: $!\n";
+       my $res = do {
+               local $/ = undef;
+               <$git>
+       };
        close($git);
 
        return $res;
@@ -384,27 +359,26 @@ sub get_all_mediafiles {
        my $mw_pages = $mediawiki->list({
                action => 'query',
                list => 'allpages',
-               apnamespace => get_mw_namespace_id("File"),
+               apnamespace => get_mw_namespace_id('File'),
                aplimit => 'max'
        });
        if (!defined($mw_pages)) {
-               print STDERR "fatal: could not get the list of pages for media files.\n";
-               print STDERR "fatal: '$url' does not appear to be a mediawiki\n";
-               print STDERR "fatal: make sure '$url/api.php' is a valid page.\n";
+               print {*STDERR} "fatal: could not get the list of pages for media files.\n";
+               print {*STDERR} "fatal: '$url' does not appear to be a mediawiki\n";
+               print {*STDERR} "fatal: make sure '$url/api.php' is a valid page.\n";
                exit 1;
        }
        foreach my $page (@{$mw_pages}) {
                $pages->{$page->{title}} = $page;
        }
+       return;
 }
 
 sub get_linked_mediafiles {
        my $pages = shift;
-       my @titles = map $_->{title}, values(%{$pages});
+       my @titles = map { $_->{title} } values(%{$pages});
 
-       # The query is split in small batches because of the MW API limit of
-       # the number of links to be returned (500 links max).
-       my $batch = 10;
+       my $batch = BATCH_SIZE;
        while (@titles) {
                if ($#titles < $batch) {
                        $batch = $#titles;
@@ -420,7 +394,7 @@ sub get_linked_mediafiles {
                        action => 'query',
                        prop => 'links|images',
                        titles => $mw_titles,
-                       plnamespace => get_mw_namespace_id("File"),
+                       plnamespace => get_mw_namespace_id('File'),
                        pllimit => 'max'
                };
                my $result = $mediawiki->api($query);
@@ -428,11 +402,13 @@ sub get_linked_mediafiles {
                while (my ($id, $page) = each(%{$result->{query}->{pages}})) {
                        my @media_titles;
                        if (defined($page->{links})) {
-                               my @link_titles = map $_->{title}, @{$page->{links}};
+                               my @link_titles
+                                   = map { $_->{title} } @{$page->{links}};
                                push(@media_titles, @link_titles);
                        }
                        if (defined($page->{images})) {
-                               my @image_titles = map $_->{title}, @{$page->{images}};
+                               my @image_titles
+                                   = map { $_->{title} } @{$page->{images}};
                                push(@media_titles, @image_titles);
                        }
                        if (@media_titles) {
@@ -442,6 +418,7 @@ sub get_linked_mediafiles {
 
                @titles = @titles[($batch+1)..$#titles];
        }
+       return;
 }
 
 sub get_mw_mediafile_for_page_revision {
@@ -455,7 +432,7 @@ sub get_mw_mediafile_for_page_revision {
        my $query = {
                action => 'query',
                prop => 'imageinfo',
-               titles => "File:" . $filename,
+               titles => "File:${filename}",
                iistart => $timestamp,
                iiend => $timestamp,
                iiprop => 'timestamp|archivename|url',
@@ -473,53 +450,50 @@ sub get_mw_mediafile_for_page_revision {
                $mediafile{timestamp} = $fileinfo->{timestamp};
                # Mediawiki::API's download function doesn't support https URLs
                # and can't download old versions of files.
-               print STDERR "\tDownloading file $mediafile{title}, version $mediafile{timestamp}\n";
+               print {*STDERR} "\tDownloading file $mediafile{title}, version $mediafile{timestamp}\n";
                $mediafile{content} = download_mw_mediafile($fileinfo->{url});
        }
        return %mediafile;
 }
 
 sub download_mw_mediafile {
-       my $url = shift;
+       my $download_url = shift;
 
-       my $response = $mediawiki->{ua}->get($url);
-       if ($response->code == 200) {
+       my $response = $mediawiki->{ua}->get($download_url);
+       if ($response->code == HTTP_CODE_OK) {
                return $response->decoded_content;
        } else {
-               print STDERR "Error downloading mediafile from :\n";
-               print STDERR "URL: $url\n";
-               print STDERR "Server response: " . $response->code . " " . $response->message . "\n";
+               print {*STDERR} "Error downloading mediafile from :\n";
+               print {*STDERR} "URL: ${download_url}\n";
+               print {*STDERR} 'Server response: ' . $response->code . q{ } . $response->message . "\n";
                exit 1;
        }
 }
 
 sub get_last_local_revision {
        # Get note regarding last mediawiki revision
-       my $note = run_git("notes --ref=$remotename/mediawiki show refs/mediawiki/$remotename/master 2>/dev/null");
+       my $note = run_git("notes --ref=${remotename}/mediawiki show refs/mediawiki/${remotename}/master 2>/dev/null");
        my @note_info = split(/ /, $note);
 
        my $lastrevision_number;
-       if (!(defined($note_info[0]) && $note_info[0] eq "mediawiki_revision:")) {
-               print STDERR "No previous mediawiki revision found";
+       if (!(defined($note_info[0]) && $note_info[0] eq 'mediawiki_revision:')) {
+               print {*STDERR} 'No previous mediawiki revision found';
                $lastrevision_number = 0;
        } else {
                # Notes are formatted : mediawiki_revision: #number
                $lastrevision_number = $note_info[1];
                chomp($lastrevision_number);
-               print STDERR "Last local mediawiki revision found is $lastrevision_number";
+               print {*STDERR} "Last local mediawiki revision found is ${lastrevision_number}";
        }
        return $lastrevision_number;
 }
 
-# Remember the timestamp corresponding to a revision id.
-my %basetimestamps;
-
 # Get the last remote revision without taking in account which pages are
 # tracked or not. This function makes a single request to the wiki thus
 # avoid a loop onto all tracked pages. This is useful for the fetch-by-rev
 # option.
 sub get_last_global_remote_rev {
-       mw_connect_maybe();
+       $mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
        my $query = {
                action => 'query',
@@ -535,14 +509,14 @@ sub get_last_global_remote_rev {
 # Get the last remote revision concerning the tracked pages and the tracked
 # categories.
 sub get_last_remote_revision {
-       mw_connect_maybe();
+       $mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
        my %pages_hash = get_mw_pages();
        my @pages = values(%pages_hash);
 
        my $max_rev_num = 0;
 
-       print STDERR "Getting last revision id on tracked pages...\n";
+       print {*STDERR} "Getting last revision id on tracked pages...\n";
 
        foreach my $page (@pages) {
                my $id = $page->{pageid};
@@ -563,7 +537,7 @@ sub get_last_remote_revision {
                $max_rev_num = ($lastrev->{revid} > $max_rev_num ? $lastrev->{revid} : $max_rev_num);
        }
 
-       print STDERR "Last remote revision found is $max_rev_num.\n";
+       print {*STDERR} "Last remote revision found is $max_rev_num.\n";
        return $max_rev_num;
 }
 
@@ -574,7 +548,7 @@ sub mediawiki_clean {
        # Mediawiki does not allow blank space at the end of a page and ends with a single \n.
        # This function right trims a string and adds a \n at the end to follow this rule
        $string =~ s/\s+$//;
-       if ($string eq "" && $page_created) {
+       if ($string eq EMPTY && $page_created) {
                # Creating empty pages is forbidden.
                $string = EMPTY_CONTENT;
        }
@@ -585,38 +559,16 @@ sub mediawiki_clean {
 sub mediawiki_smudge {
        my $string = shift;
        if ($string eq EMPTY_CONTENT) {
-               $string = "";
+               $string = EMPTY;
        }
        # This \n is important. This is due to mediawiki's way to handle end of files.
-       return $string."\n";
-}
-
-sub mediawiki_clean_filename {
-       my $filename = shift;
-       $filename =~ s/@{[SLASH_REPLACEMENT]}/\//g;
-       # [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded.
-       # Do a variant of URL-encoding, i.e. looks like URL-encoding,
-       # but with _ added to prevent MediaWiki from thinking this is
-       # an actual special character.
-       $filename =~ s/[\[\]\{\}\|]/sprintf("_%%_%x", ord($&))/ge;
-       # If we use the uri escape before
-       # we should unescape here, before anything
-
-       return $filename;
-}
-
-sub mediawiki_smudge_filename {
-       my $filename = shift;
-       $filename =~ s/\//@{[SLASH_REPLACEMENT]}/g;
-       $filename =~ s/ /_/g;
-       # Decode forbidden characters encoded in mediawiki_clean_filename
-       $filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf("%c", hex($1))/ge;
-       return $filename;
+       return "${string}\n";
 }
 
 sub literal_data {
        my ($content) = @_;
-       print STDOUT "data ", bytes::length($content), "\n", $content;
+       print {*STDOUT} 'data ', bytes::length($content), "\n", $content;
+       return;
 }
 
 sub literal_data_raw {
@@ -624,33 +576,37 @@ sub literal_data_raw {
        my ($content) = @_;
        # Avoid confusion between size in bytes and in characters
        utf8::downgrade($content);
-       binmode STDOUT, ":raw";
-       print STDOUT "data ", bytes::length($content), "\n", $content;
-       binmode STDOUT, ":utf8";
+       binmode STDOUT, ':raw';
+       print {*STDOUT} 'data ', bytes::length($content), "\n", $content;
+       binmode STDOUT, ':encoding(UTF-8)';
+       return;
 }
 
 sub mw_capabilities {
        # Revisions are imported to the private namespace
        # refs/mediawiki/$remotename/ by the helper and fetched into
        # refs/remotes/$remotename later by fetch.
-       print STDOUT "refspec refs/heads/*:refs/mediawiki/$remotename/*\n";
-       print STDOUT "import\n";
-       print STDOUT "list\n";
-       print STDOUT "push\n";
-       print STDOUT "\n";
+       print {*STDOUT} "refspec refs/heads/*:refs/mediawiki/${remotename}/*\n";
+       print {*STDOUT} "import\n";
+       print {*STDOUT} "list\n";
+       print {*STDOUT} "push\n";
+       print {*STDOUT} "\n";
+       return;
 }
 
 sub mw_list {
        # MediaWiki do not have branches, we consider one branch arbitrarily
        # called master, and HEAD pointing to it.
-       print STDOUT "? refs/heads/master\n";
-       print STDOUT "\@refs/heads/master HEAD\n";
-       print STDOUT "\n";
+       print {*STDOUT} "? refs/heads/master\n";
+       print {*STDOUT} "\@refs/heads/master HEAD\n";
+       print {*STDOUT} "\n";
+       return;
 }
 
 sub mw_option {
-       print STDERR "remote-helper command 'option $_[0]' not yet implemented\n";
-       print STDOUT "unsupported\n";
+       print {*STDERR} "remote-helper command 'option $_[0]' not yet implemented\n";
+       print {*STDOUT} "unsupported\n";
+       return;
 }
 
 sub fetch_mw_revisions_for_page {
@@ -666,6 +622,9 @@ sub fetch_mw_revisions_for_page {
                rvstartid => $fetch_from,
                rvlimit => 500,
                pageids => $id,
+
+               # Let MediaWiki know that we support the latest API.
+               continue => '',
        };
 
        my $revnum = 0;
@@ -681,15 +640,22 @@ sub fetch_mw_revisions_for_page {
                        push(@page_revs, $page_rev_ids);
                        $revnum++;
                }
-               last unless $result->{'query-continue'};
-               $query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid};
+
+               if ($result->{'query-continue'}) { # For legacy APIs
+                       $query->{rvstartid} = $result->{'query-continue'}->{revisions}->{rvstartid};
+               } elsif ($result->{continue}) { # For newer APIs
+                       $query->{rvstartid} = $result->{continue}->{rvcontinue};
+                       $query->{continue} = $result->{continue}->{continue};
+               } else {
+                       last;
+               }
        }
        if ($shallow_import && @page_revs) {
-               print STDERR "  Found 1 revision (shallow import).\n";
+               print {*STDERR} "  Found 1 revision (shallow import).\n";
                @page_revs = sort {$b->{revid} <=> $a->{revid}} (@page_revs);
                return $page_revs[0];
        }
-       print STDERR "  Found ", $revnum, " revision(s).\n";
+       print {*STDERR} "  Found ${revnum} revision(s).\n";
        return @page_revs;
 }
 
@@ -701,8 +667,7 @@ sub fetch_mw_revisions {
        my $n = 1;
        foreach my $page (@pages) {
                my $id = $page->{pageid};
-
-               print STDERR "page $n/", scalar(@pages), ": ". $page->{title} ."\n";
+               print {*STDERR} "page ${n}/", scalar(@pages), ': ', $page->{title}, "\n";
                $n++;
                my @page_revs = fetch_mw_revisions_for_page($page, $id, $fetch_from);
                @revisions = (@page_revs, @revisions);
@@ -716,7 +681,7 @@ sub fe_escape_path {
     $path =~ s/\\/\\\\/g;
     $path =~ s/"/\\"/g;
     $path =~ s/\n/\\n/g;
-    return '"' . $path . '"';
+    return qq("${path}");
 }
 
 sub import_file_revision {
@@ -736,42 +701,43 @@ sub import_file_revision {
        my $author = $commit{author};
        my $date = $commit{date};
 
-       print STDOUT "commit refs/mediawiki/$remotename/master\n";
-       print STDOUT "mark :$n\n";
-       print STDOUT "committer $author <$author\@$wiki_name> ", $date->epoch, " +0000\n";
+       print {*STDOUT} "commit refs/mediawiki/${remotename}/master\n";
+       print {*STDOUT} "mark :${n}\n";
+       print {*STDOUT} "committer ${author} <${author}\@${wiki_name}> " . $date->epoch . " +0000\n";
        literal_data($comment);
 
        # If it's not a clone, we need to know where to start from
        if (!$full_import && $n == 1) {
-               print STDOUT "from refs/mediawiki/$remotename/master^0\n";
+               print {*STDOUT} "from refs/mediawiki/${remotename}/master^0\n";
        }
        if ($content ne DELETED_CONTENT) {
-               print STDOUT "M 644 inline " .
-                   fe_escape_path($title . ".mw") . "\n";
+               print {*STDOUT} 'M 644 inline ' .
+                   fe_escape_path("${title}.mw") . "\n";
                literal_data($content);
                if (%mediafile) {
-                       print STDOUT "M 644 inline "
+                       print {*STDOUT} 'M 644 inline '
                            . fe_escape_path($mediafile{title}) . "\n";
                        literal_data_raw($mediafile{content});
                }
-               print STDOUT "\n\n";
+               print {*STDOUT} "\n\n";
        } else {
-               print STDOUT "D " . fe_escape_path($title . ".mw") . "\n";
+               print {*STDOUT} 'D ' . fe_escape_path("${title}.mw") . "\n";
        }
 
        # mediawiki revision number in the git note
        if ($full_import && $n == 1) {
-               print STDOUT "reset refs/notes/$remotename/mediawiki\n";
+               print {*STDOUT} "reset refs/notes/${remotename}/mediawiki\n";
        }
-       print STDOUT "commit refs/notes/$remotename/mediawiki\n";
-       print STDOUT "committer $author <$author\@$wiki_name> ", $date->epoch, " +0000\n";
-       literal_data("Note added by git-mediawiki during import");
+       print {*STDOUT} "commit refs/notes/${remotename}/mediawiki\n";
+       print {*STDOUT} "committer ${author} <${author}\@${wiki_name}> " . $date->epoch . " +0000\n";
+       literal_data('Note added by git-mediawiki during import');
        if (!$full_import && $n == 1) {
-               print STDOUT "from refs/notes/$remotename/mediawiki^0\n";
+               print {*STDOUT} "from refs/notes/${remotename}/mediawiki^0\n";
        }
-       print STDOUT "N inline :$n\n";
-       literal_data("mediawiki_revision: " . $commit{mw_revision});
-       print STDOUT "\n\n";
+       print {*STDOUT} "N inline :${n}\n";
+       literal_data("mediawiki_revision: $commit{mw_revision}");
+       print {*STDOUT} "\n\n";
+       return;
 }
 
 # parse a sequence of
@@ -784,23 +750,25 @@ sub get_more_refs {
        my @refs;
        while (1) {
                my $line = <STDIN>;
-               if ($line =~ m/^$cmd (.*)$/) {
+               if ($line =~ /^$cmd (.*)$/) {
                        push(@refs, $1);
                } elsif ($line eq "\n") {
                        return @refs;
                } else {
-                       die("Invalid command in a '$cmd' batch: ". $_);
+                       die("Invalid command in a '$cmd' batch: $_\n");
                }
        }
+       return;
 }
 
 sub mw_import {
        # multiple import commands can follow each other.
-       my @refs = (shift, get_more_refs("import"));
+       my @refs = (shift, get_more_refs('import'));
        foreach my $ref (@refs) {
                mw_import_ref($ref);
        }
-       print STDOUT "done\n";
+       print {*STDOUT} "done\n";
+       return;
 }
 
 sub mw_import_ref {
@@ -810,40 +778,41 @@ sub mw_import_ref {
        # Since HEAD is a symbolic ref to master (by convention,
        # followed by the output of the command "list" that we gave),
        # we don't need to do anything in this case.
-       if ($ref eq "HEAD") {
+       if ($ref eq 'HEAD') {
                return;
        }
 
-       mw_connect_maybe();
+       $mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
-       print STDERR "Searching revisions...\n";
+       print {*STDERR} "Searching revisions...\n";
        my $last_local = get_last_local_revision();
        my $fetch_from = $last_local + 1;
        if ($fetch_from == 1) {
-               print STDERR ", fetching from beginning.\n";
+               print {*STDERR} ", fetching from beginning.\n";
        } else {
-               print STDERR ", fetching from here.\n";
+               print {*STDERR} ", fetching from here.\n";
        }
 
        my $n = 0;
-       if ($fetch_strategy eq "by_rev") {
-               print STDERR "Fetching & writing export data by revs...\n";
+       if ($fetch_strategy eq 'by_rev') {
+               print {*STDERR} "Fetching & writing export data by revs...\n";
                $n = mw_import_ref_by_revs($fetch_from);
-       } elsif ($fetch_strategy eq "by_page") {
-               print STDERR "Fetching & writing export data by pages...\n";
+       } elsif ($fetch_strategy eq 'by_page') {
+               print {*STDERR} "Fetching & writing export data by pages...\n";
                $n = mw_import_ref_by_pages($fetch_from);
        } else {
-               print STDERR "fatal: invalid fetch strategy \"$fetch_strategy\".\n";
-               print STDERR "Check your configuration variables remote.$remotename.fetchStrategy and mediawiki.fetchStrategy\n";
+               print {*STDERR} qq(fatal: invalid fetch strategy "${fetch_strategy}".\n);
+               print {*STDERR} "Check your configuration variables remote.${remotename}.fetchStrategy and mediawiki.fetchStrategy\n";
                exit 1;
        }
 
        if ($fetch_from == 1 && $n == 0) {
-               print STDERR "You appear to have cloned an empty MediaWiki.\n";
+               print {*STDERR} "You appear to have cloned an empty MediaWiki.\n";
                # Something has to be done remote-helper side. If nothing is done, an error is
                # thrown saying that HEAD is referring to unknown object 0000000000000000000
                # and the clone fails.
        }
+       return;
 }
 
 sub mw_import_ref_by_pages {
@@ -855,7 +824,7 @@ sub mw_import_ref_by_pages {
        my ($n, @revisions) = fetch_mw_revisions(\@pages, $fetch_from);
 
        @revisions = sort {$a->{revid} <=> $b->{revid}} @revisions;
-       my @revision_ids = map $_->{revid}, @revisions;
+       my @revision_ids = map { $_->{revid} } @revisions;
 
        return mw_import_revids($fetch_from, \@revision_ids, \%pages_hash);
 }
@@ -882,7 +851,7 @@ sub mw_import_revids {
        my $n_actual = 0;
        my $last_timestamp = 0; # Placeholer in case $rev->timestamp is undefined
 
-       foreach my $pagerevid (@$revision_ids) {
+       foreach my $pagerevid (@{$revision_ids}) {
                # Count page even if we skip it, since we display
                # $n/$total and $total includes skipped pages.
                $n++;
@@ -898,7 +867,7 @@ sub mw_import_revids {
                my $result = $mediawiki->api($query);
 
                if (!$result) {
-                       die "Failed to retrieve modified page for revision $pagerevid";
+                       die "Failed to retrieve modified page for revision $pagerevid\n";
                }
 
                if (defined($result->{query}->{badrevids}->{$pagerevid})) {
@@ -907,7 +876,7 @@ sub mw_import_revids {
                }
 
                if (!defined($result->{query}->{pages})) {
-                       die "Invalid revision $pagerevid.";
+                       die "Invalid revision ${pagerevid}.\n";
                }
 
                my @result_pages = values(%{$result->{query}->{pages}});
@@ -917,8 +886,8 @@ sub mw_import_revids {
                my $page_title = $result_page->{title};
 
                if (!exists($pages->{$page_title})) {
-                       print STDERR "$n/", scalar(@$revision_ids),
-                               ": Skipping revision #$rev->{revid} of $page_title\n";
+                       print {*STDERR} "${n}/", scalar(@{$revision_ids}),
+                               ": Skipping revision #$rev->{revid} of ${page_title}\n";
                        next;
                }
 
@@ -927,7 +896,7 @@ sub mw_import_revids {
                my %commit;
                $commit{author} = $rev->{user} || 'Anonymous';
                $commit{comment} = $rev->{comment} || EMPTY_MESSAGE;
-               $commit{title} = mediawiki_smudge_filename($page_title);
+               $commit{title} = smudge_filename($page_title);
                $commit{mw_revision} = $rev->{revid};
                $commit{content} = mediawiki_smudge($rev->{'*'});
 
@@ -943,14 +912,14 @@ sub mw_import_revids {
                my %mediafile;
                if ($namespace) {
                        my $id = get_mw_namespace_id($namespace);
-                       if ($id && $id == get_mw_namespace_id("File")) {
+                       if ($id && $id == get_mw_namespace_id('File')) {
                                %mediafile = get_mw_mediafile_for_page_revision($filename, $rev->{timestamp});
                        }
                }
                # If this is a revision of the media page for new version
                # of a file do one common commit for both file and media page.
                # Else do commit only for that page.
-               print STDERR "$n/", scalar(@$revision_ids), ": Revision #$rev->{revid} of $commit{title}\n";
+               print {*STDERR} "${n}/", scalar(@{$revision_ids}), ": Revision #$rev->{revid} of $commit{title}\n";
                import_file_revision(\%commit, ($fetch_from == 1), $n_actual, \%mediafile);
        }
 
@@ -958,17 +927,17 @@ sub mw_import_revids {
 }
 
 sub error_non_fast_forward {
-       my $advice = run_git("config --bool advice.pushNonFastForward");
+       my $advice = run_git('config --bool advice.pushNonFastForward');
        chomp($advice);
-       if ($advice ne "false") {
+       if ($advice ne 'false') {
                # Native git-push would show this after the summary.
                # We can't ask it to display it cleanly, so print it
                # ourselves before.
-               print STDERR "To prevent you from losing history, non-fast-forward updates were rejected\n";
-               print STDERR "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n";
-               print STDERR "'Note about fast-forwards' section of 'git push --help' for details.\n";
+               print {*STDERR} "To prevent you from losing history, non-fast-forward updates were rejected\n";
+               print {*STDERR} "Merge the remote changes (e.g. 'git pull') before pushing again. See the\n";
+               print {*STDERR} "'Note about fast-forwards' section of 'git push --help' for details.\n";
        }
-       print STDOUT "error $_[0] \"non-fast-forward\"\n";
+       print {*STDOUT} qq(error $_[0] "non-fast-forward"\n);
        return 0;
 }
 
@@ -979,34 +948,34 @@ sub mw_upload_file {
        my $file_deleted = shift;
        my $summary = shift;
        my $newrevid;
-       my $path = "File:" . $complete_file_name;
+       my $path = "File:${complete_file_name}";
        my %hashFiles = get_allowed_file_extensions();
        if (!exists($hashFiles{$extension})) {
-               print STDERR "$complete_file_name is not a permitted file on this wiki.\n";
-               print STDERR "Check the configuration of file uploads in your mediawiki.\n";
+               print {*STDERR} "${complete_file_name} is not a permitted file on this wiki.\n";
+               print {*STDERR} "Check the configuration of file uploads in your mediawiki.\n";
                return $newrevid;
        }
        # Deleting and uploading a file requires a priviledged user
        if ($file_deleted) {
-               mw_connect_maybe();
+               $mediawiki = connect_maybe($mediawiki, $remotename, $url);
                my $query = {
                        action => 'delete',
                        title => $path,
                        reason => $summary
                };
                if (!$mediawiki->edit($query)) {
-                       print STDERR "Failed to delete file on remote wiki\n";
-                       print STDERR "Check your permissions on the remote site. Error code:\n";
-                       print STDERR $mediawiki->{error}->{code} . ':' . $mediawiki->{error}->{details};
+                       print {*STDERR} "Failed to delete file on remote wiki\n";
+                       print {*STDERR} "Check your permissions on the remote site. Error code:\n";
+                       print {*STDERR} $mediawiki->{error}->{code} . ':' . $mediawiki->{error}->{details};
                        exit 1;
                }
        } else {
                # Don't let perl try to interpret file content as UTF-8 => use "raw"
-               my $content = run_git("cat-file blob $new_sha1", "raw");
-               if ($content ne "") {
-                       mw_connect_maybe();
+               my $content = run_git("cat-file blob ${new_sha1}", 'raw');
+               if ($content ne EMPTY) {
+                       $mediawiki = connect_maybe($mediawiki, $remotename, $url);
                        $mediawiki->{config}->{upload_url} =
-                               "$url/index.php/Special:Upload";
+                               "${url}/index.php/Special:Upload";
                        $mediawiki->edit({
                                action => 'upload',
                                filename => $complete_file_name,
@@ -1018,12 +987,12 @@ sub mw_upload_file {
                        }, {
                                skip_encoding => 1
                        } ) || die $mediawiki->{error}->{code} . ':'
-                                . $mediawiki->{error}->{details};
+                                . $mediawiki->{error}->{details} . "\n";
                        my $last_file_page = $mediawiki->get_page({title => $path});
                        $newrevid = $last_file_page->{revid};
-                       print STDERR "Pushed file: $new_sha1 - $complete_file_name.\n";
+                       print {*STDERR} "Pushed file: ${new_sha1} - ${complete_file_name}.\n";
                } else {
-                       print STDERR "Empty file $complete_file_name not pushed.\n";
+                       print {*STDERR} "Empty file ${complete_file_name} not pushed.\n";
                }
        }
        return $newrevid;
@@ -1045,24 +1014,24 @@ sub mw_push_file {
        my $newrevid;
 
        if ($summary eq EMPTY_MESSAGE) {
-               $summary = '';
+               $summary = EMPTY;
        }
 
        my $new_sha1 = $diff_info_split[3];
        my $old_sha1 = $diff_info_split[2];
        my $page_created = ($old_sha1 eq NULL_SHA1);
        my $page_deleted = ($new_sha1 eq NULL_SHA1);
-       $complete_file_name = mediawiki_clean_filename($complete_file_name);
+       $complete_file_name = clean_filename($complete_file_name);
 
        my ($title, $extension) = $complete_file_name =~ /^(.*)\.([^\.]*)$/;
        if (!defined($extension)) {
-               $extension = "";
+               $extension = EMPTY;
        }
-       if ($extension eq "mw") {
+       if ($extension eq 'mw') {
                my $ns = get_mw_namespace_id_for_page($complete_file_name);
-               if ($ns && $ns == get_mw_namespace_id("File") && (!$export_media)) {
-                       print STDERR "Ignoring media file related page: $complete_file_name\n";
-                       return ($oldrevid, "ok");
+               if ($ns && $ns == get_mw_namespace_id('File') && (!$export_media)) {
+                       print {*STDERR} "Ignoring media file related page: ${complete_file_name}\n";
+                       return ($oldrevid, 'ok');
                }
                my $file_content;
                if ($page_deleted) {
@@ -1072,10 +1041,10 @@ sub mw_push_file {
                        # with this content instead:
                        $file_content = DELETED_CONTENT;
                } else {
-                       $file_content = run_git("cat-file blob $new_sha1");
+                       $file_content = run_git("cat-file blob ${new_sha1}");
                }
 
-               mw_connect_maybe();
+               $mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
                my $result = $mediawiki->edit( {
                        action => 'edit',
@@ -1089,49 +1058,49 @@ sub mw_push_file {
                if (!$result) {
                        if ($mediawiki->{error}->{code} == 3) {
                                # edit conflicts, considered as non-fast-forward
-                               print STDERR 'Warning: Error ' .
+                               print {*STDERR} 'Warning: Error ' .
                                    $mediawiki->{error}->{code} .
-                                   ' from mediwiki: ' . $mediawiki->{error}->{details} .
+                                   ' from mediawiki: ' . $mediawiki->{error}->{details} .
                                    ".\n";
-                               return ($oldrevid, "non-fast-forward");
+                               return ($oldrevid, 'non-fast-forward');
                        } else {
                                # Other errors. Shouldn't happen => just die()
                                die 'Fatal: Error ' .
                                    $mediawiki->{error}->{code} .
-                                   ' from mediwiki: ' . $mediawiki->{error}->{details};
+                                   ' from mediawiki: ' . $mediawiki->{error}->{details} . "\n";
                        }
                }
                $newrevid = $result->{edit}->{newrevid};
-               print STDERR "Pushed file: $new_sha1 - $title\n";
+               print {*STDERR} "Pushed file: ${new_sha1} - ${title}\n";
        } elsif ($export_media) {
                $newrevid = mw_upload_file($complete_file_name, $new_sha1,
                                           $extension, $page_deleted,
                                           $summary);
        } else {
-               print STDERR "Ignoring media file $title\n";
+               print {*STDERR} "Ignoring media file ${title}\n";
        }
        $newrevid = ($newrevid or $oldrevid);
-       return ($newrevid, "ok");
+       return ($newrevid, 'ok');
 }
 
 sub mw_push {
        # multiple push statements can follow each other
-       my @refsspecs = (shift, get_more_refs("push"));
+       my @refsspecs = (shift, get_more_refs('push'));
        my $pushed;
        for my $refspec (@refsspecs) {
                my ($force, $local, $remote) = $refspec =~ /^(\+)?([^:]*):([^:]*)$/
-                   or die("Invalid refspec for push. Expected <src>:<dst> or +<src>:<dst>");
+                   or die("Invalid refspec for push. Expected <src>:<dst> or +<src>:<dst>\n");
                if ($force) {
-                       print STDERR "Warning: forced push not allowed on a MediaWiki.\n";
+                       print {*STDERR} "Warning: forced push not allowed on a MediaWiki.\n";
                }
-               if ($local eq "") {
-                       print STDERR "Cannot delete remote branch on a MediaWiki\n";
-                       print STDOUT "error $remote cannot delete\n";
+               if ($local eq EMPTY) {
+                       print {*STDERR} "Cannot delete remote branch on a MediaWiki\n";
+                       print {*STDOUT} "error ${remote} cannot delete\n";
                        next;
                }
-               if ($remote ne "refs/heads/master") {
-                       print STDERR "Only push to the branch 'master' is supported on a MediaWiki\n";
-                       print STDOUT "error $remote only master allowed\n";
+               if ($remote ne 'refs/heads/master') {
+                       print {*STDERR} "Only push to the branch 'master' is supported on a MediaWiki\n";
+                       print {*STDOUT} "error ${remote} only master allowed\n";
                        next;
                }
                if (mw_push_revision($local, $remote)) {
@@ -1140,30 +1109,32 @@ sub mw_push {
        }
 
        # Notify Git that the push is done
-       print STDOUT "\n";
+       print {*STDOUT} "\n";
 
        if ($pushed && $dumb_push) {
-               print STDERR "Just pushed some revisions to MediaWiki.\n";
-               print STDERR "The pushed revisions now have to be re-imported, and your current branch\n";
-               print STDERR "needs to be updated with these re-imported commits. You can do this with\n";
-               print STDERR "\n";
-               print STDERR "  git pull --rebase\n";
-               print STDERR "\n";
+               print {*STDERR} "Just pushed some revisions to MediaWiki.\n";
+               print {*STDERR} "The pushed revisions now have to be re-imported, and your current branch\n";
+               print {*STDERR} "needs to be updated with these re-imported commits. You can do this with\n";
+               print {*STDERR} "\n";
+               print {*STDERR} "  git pull --rebase\n";
+               print {*STDERR} "\n";
        }
+       return;
 }
 
 sub mw_push_revision {
        my $local = shift;
        my $remote = shift; # actually, this has to be "refs/heads/master" at this point.
        my $last_local_revid = get_last_local_revision();
-       print STDERR ".\n"; # Finish sentence started by get_last_local_revision()
+       print {*STDERR} ".\n"; # Finish sentence started by get_last_local_revision()
        my $last_remote_revid = get_last_remote_revision();
        my $mw_revision = $last_remote_revid;
 
        # Get sha1 of commit pointed by local HEAD
-       my $HEAD_sha1 = run_git("rev-parse $local 2>/dev/null"); chomp($HEAD_sha1);
+       my $HEAD_sha1 = run_git("rev-parse ${local} 2>/dev/null");
+       chomp($HEAD_sha1);
        # Get sha1 of commit pointed by remotes/$remotename/master
-       my $remoteorigin_sha1 = run_git("rev-parse refs/remotes/$remotename/master 2>/dev/null");
+       my $remoteorigin_sha1 = run_git("rev-parse refs/remotes/${remotename}/master 2>/dev/null");
        chomp($remoteorigin_sha1);
 
        if ($last_local_revid > 0 &&
@@ -1182,22 +1153,22 @@ sub mw_push_revision {
        if ($last_local_revid > 0) {
                my $parsed_sha1 = $remoteorigin_sha1;
                # Find a path from last MediaWiki commit to pushed commit
-               print STDERR "Computing path from local to remote ...\n";
-               my @local_ancestry = split(/\n/, run_git("rev-list --boundary --parents $local ^$parsed_sha1"));
+               print {*STDERR} "Computing path from local to remote ...\n";
+               my @local_ancestry = split(/\n/, run_git("rev-list --boundary --parents ${local} ^${parsed_sha1}"));
                my %local_ancestry;
                foreach my $line (@local_ancestry) {
-                       if (my ($child, $parents) = $line =~ m/^-?([a-f0-9]+) ([a-f0-9 ]+)/) {
-                               foreach my $parent (split(' ', $parents)) {
+                       if (my ($child, $parents) = $line =~ /^-?([a-f0-9]+) ([a-f0-9 ]+)/) {
+                               foreach my $parent (split(/ /, $parents)) {
                                        $local_ancestry{$parent} = $child;
                                }
-                       } elsif (!$line =~ m/^([a-f0-9]+)/) {
-                               die "Unexpected output from git rev-list: $line";
+                       } elsif (!$line =~ /^([a-f0-9]+)/) {
+                               die "Unexpected output from git rev-list: ${line}\n";
                        }
                }
                while ($parsed_sha1 ne $HEAD_sha1) {
                        my $child = $local_ancestry{$parsed_sha1};
                        if (!$child) {
-                               printf STDERR "Cannot find a path in history from remote commit to last commit\n";
+                               print {*STDERR} "Cannot find a path in history from remote commit to last commit\n";
                                return error_non_fast_forward($remote);
                        }
                        push(@commit_pairs, [$parsed_sha1, $child]);
@@ -1206,12 +1177,12 @@ sub mw_push_revision {
        } else {
                # No remote mediawiki revision. Export the whole
                # history (linearized with --first-parent)
-               print STDERR "Warning: no common ancestor, pushing complete history\n";
-               my $history = run_git("rev-list --first-parent --children $local");
-               my @history = split('\n', $history);
+               print {*STDERR} "Warning: no common ancestor, pushing complete history\n";
+               my $history = run_git("rev-list --first-parent --children ${local}");
+               my @history = split(/\n/, $history);
                @history = @history[1..$#history];
                foreach my $line (reverse @history) {
-                       my @commit_info_split = split(/ |\n/, $line);
+                       my @commit_info_split = split(/[ \n]/, $line);
                        push(@commit_pairs, \@commit_info_split);
                }
        }
@@ -1219,12 +1190,12 @@ sub mw_push_revision {
        foreach my $commit_info_split (@commit_pairs) {
                my $sha1_child = @{$commit_info_split}[0];
                my $sha1_commit = @{$commit_info_split}[1];
-               my $diff_infos = run_git("diff-tree -r --raw -z $sha1_child $sha1_commit");
+               my $diff_infos = run_git("diff-tree -r --raw -z ${sha1_child} ${sha1_commit}");
                # TODO: we could detect rename, and encode them with a #redirect on the wiki.
                # TODO: for now, it's just a delete+add
                my @diff_info_list = split(/\0/, $diff_infos);
                # Keep the subject line of the commit message as mediawiki comment for the revision
-               my $commit_msg = run_git("log --no-walk --format=\"%s\" $sha1_commit");
+               my $commit_msg = run_git(qq(log --no-walk --format="%s" ${sha1_commit}));
                chomp($commit_msg);
                # Push every blob
                while (@diff_info_list) {
@@ -1236,7 +1207,7 @@ sub mw_push_revision {
                        my $info = shift(@diff_info_list);
                        my $file = shift(@diff_info_list);
                        ($mw_revision, $status) = mw_push_file($info, $file, $commit_msg, $mw_revision);
-                       if ($status eq "non-fast-forward") {
+                       if ($status eq 'non-fast-forward') {
                                # we may already have sent part of the
                                # commit to MediaWiki, but it's too
                                # late to cancel it. Stop the push in
@@ -1244,22 +1215,22 @@ sub mw_push_revision {
                                # accurate error message.
                                return error_non_fast_forward($remote);
                        }
-                       if ($status ne "ok") {
-                               die("Unknown error from mw_push_file()");
+                       if ($status ne 'ok') {
+                               die("Unknown error from mw_push_file()\n");
                        }
                }
-               unless ($dumb_push) {
-                       run_git("notes --ref=$remotename/mediawiki add -f -m \"mediawiki_revision: $mw_revision\" $sha1_commit");
-                       run_git("update-ref -m \"Git-MediaWiki push\" refs/mediawiki/$remotename/master $sha1_commit $sha1_child");
+               if (!$dumb_push) {
+                       run_git(qq(notes --ref=${remotename}/mediawiki add -f -m "mediawiki_revision: ${mw_revision}" ${sha1_commit}));
+                       run_git(qq(update-ref -m "Git-MediaWiki push" refs/mediawiki/${remotename}/master ${sha1_commit} ${sha1_child}));
                }
        }
 
-       print STDOUT "ok $remote\n";
+       print {*STDOUT} "ok ${remote}\n";
        return 1;
 }
 
 sub get_allowed_file_extensions {
-       mw_connect_maybe();
+       $mediawiki = connect_maybe($mediawiki, $remotename, $url);
 
        my $query = {
                action => 'query',
@@ -1267,8 +1238,8 @@ sub get_allowed_file_extensions {
                siprop => 'fileextensions'
                };
        my $result = $mediawiki->api($query);
-       my @file_extensions= map $_->{ext},@{$result->{query}->{fileextensions}};
-       my %hashFile = map {$_ => 1}@file_extensions;
+       my @file_extensions = map { $_->{ext}} @{$result->{query}->{fileextensions}};
+       my %hashFile = map { $_ => 1 } @file_extensions;
 
        return %hashFile;
 }
@@ -1283,15 +1254,15 @@ my %cached_mw_namespace_id;
 # Return MediaWiki id for a canonical namespace name.
 # Ex.: "File", "Project".
 sub get_mw_namespace_id {
-       mw_connect_maybe();
+       $mediawiki = connect_maybe($mediawiki, $remotename, $url);
        my $name = shift;
 
        if (!exists $namespace_id{$name}) {
                # Look at configuration file, if the record for that namespace is
                # already cached. Namespaces are stored in form:
                # "Name_of_namespace:Id_namespace", ex.: "File:6".
-               my @temp = split(/[\n]/, run_git("config --get-all remote."
-                                               . $remotename .".namespaceCache"));
+               my @temp = split(/\n/,
+                                run_git("config --get-all remote.${remotename}.namespaceCache"));
                chomp(@temp);
                foreach my $ns (@temp) {
                        my ($n, $id) = split(/:/, $ns);
@@ -1305,7 +1276,7 @@ sub get_mw_namespace_id {
        }
 
        if (!exists $namespace_id{$name}) {
-               print STDERR "Namespace $name not found in cache, querying the wiki ...\n";
+               print {*STDERR} "Namespace ${name} not found in cache, querying the wiki ...\n";
                # NS not found => get namespace id from MW and store it in
                # configuration file.
                my $query = {
@@ -1329,8 +1300,8 @@ sub get_mw_namespace_id {
        my $ns = $namespace_id{$name};
        my $id;
 
-       unless (defined $ns) {
-               print STDERR "No such namespace $name on MediaWiki.\n";
+       if (!defined $ns) {
+               print {*STDERR} "No such namespace ${name} on MediaWiki.\n";
                $ns = {is_namespace => 0};
                $namespace_id{$name} = $ns;
        }
@@ -1344,15 +1315,15 @@ sub get_mw_namespace_id {
 
        # Store explicitely requested namespaces on disk
        if (!exists $cached_mw_namespace_id{$name}) {
-               run_git("config --add remote.". $remotename
-                       .".namespaceCache \"". $name .":". $store_id ."\"");
+               run_git(qq(config --add remote.${remotename}.namespaceCache "${name}:${store_id}"));
                $cached_mw_namespace_id{$name} = 1;
        }
        return $id;
 }
 
 sub get_mw_namespace_id_for_page {
-       if (my ($namespace) = $_[0] =~ /^([^:]*):/) {
+       my $namespace = shift;
+       if ($namespace =~ /^([^:]*):/) {
                return get_mw_namespace_id($namespace);
        } else {
                return;
diff --git a/contrib/mw-to-git/t/t9365-continuing-queries.sh b/contrib/mw-to-git/t/t9365-continuing-queries.sh
new file mode 100755 (executable)
index 0000000..27e267f
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='Test the Git Mediawiki remote helper: queries w/ more than 500 results'
+
+. ./test-gitmw-lib.sh
+. $TEST_DIRECTORY/test-lib.sh
+
+test_check_precond
+
+test_expect_success 'creating page w/ >500 revisions' '
+       wiki_reset &&
+       for i in `test_seq 501`
+       do
+               echo "creating revision $i" &&
+               wiki_editpage foo "revision $i<br/>" true
+       done
+'
+
+test_expect_success 'cloning page w/ >500 revisions' '
+       git clone mediawiki::'"$WIKI_URL"' mw_dir
+'
+
+test_done
index 3b2cfacf512b0476c157e475b3ad6ea15cadeb2b..ca6860ff301163201a484f4e5ca1348410304719 100755 (executable)
@@ -62,12 +62,8 @@ test_check_precond () {
                test_done
        fi
 
-       if [ ! -f "$GIT_BUILD_DIR"/git-remote-mediawiki ];
-       then
-               echo "No remote mediawiki for git found. Copying it in git"
-               echo "cp $GIT_BUILD_DIR/contrib/mw-to-git/git-remote-mediawiki $GIT_BUILD_DIR/"
-               ln -s "$GIT_BUILD_DIR"/contrib/mw-to-git/git-remote-mediawiki "$GIT_BUILD_DIR"
-       fi
+       GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd "../.." && pwd)
+       PATH="$GIT_EXEC_PATH"'/bin-wrapper:'"$PATH"
 
        if [ ! -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" ];
        then
@@ -336,20 +332,21 @@ wiki_install () {
        fi
 
        # Fetch MediaWiki's archive if not already present in the TMP directory
+       MW_FILENAME="mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz"
        cd "$TMP"
-       if [ ! -f "$MW_VERSION.tar.gz" ] ; then
-               echo "Downloading $MW_VERSION sources ..."
-               wget "http://download.wikimedia.org/mediawiki/1.19/mediawiki-1.19.0.tar.gz" ||
+       if [ ! -f $MW_FILENAME ] ; then
+               echo "Downloading $MW_VERSION_MAJOR.$MW_VERSION_MINOR sources ..."
+               wget "http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/$MW_FILENAME" ||
                        error "Unable to download "\
-                       "http://download.wikimedia.org/mediawiki/1.19/"\
-                       "mediawiki-1.19.0.tar.gz. "\
+                       "http://download.wikimedia.org/mediawiki/$MW_VERSION_MAJOR/"\
+                       "$MW_FILENAME. "\
                        "Please fix your connection and launch the script again."
-               echo "$MW_VERSION.tar.gz downloaded in `pwd`. "\
+               echo "$MW_FILENAME downloaded in `pwd`. "\
                        "You can delete it later if you want."
        else
-               echo "Reusing existing $MW_VERSION.tar.gz downloaded in `pwd`."
+               echo "Reusing existing $MW_FILENAME downloaded in `pwd`."
        fi
-       archive_abs_path=$(pwd)/"$MW_VERSION.tar.gz"
+       archive_abs_path=$(pwd)/$MW_FILENAME
        cd "$WIKI_DIR_INST/$WIKI_DIR_NAME/" ||
                error "can't cd to $WIKI_DIR_INST/$WIKI_DIR_NAME/"
        tar xzf "$archive_abs_path" --strip-components=1 ||
@@ -431,5 +428,5 @@ wiki_delete () {
        # Delete the wiki's SQLite database
        rm -f "$TMP/$DB_FILE" || error "Database $TMP/$DB_FILE could not be deleted."
        rm -f "$FILES_FOLDER/$DB_FILE"
-       rm -rf "$TMP/$MW_VERSION"
+       rm -rf "$TMP/mediawiki-$MW_VERSION_MAJOR.$MW_VERSION_MINOR.tar.gz"
 }
index 958b37b4a701b9fada02eaa6abdeb32e3591a436..4cfebe9c69bda210ba8de6882a3ed269415de559 100644 (file)
@@ -30,6 +30,8 @@ WEB_WWW=$WEB/www
 
 # The variables below are used by the script to install a wiki.
 # You should not modify these unless you are modifying the script itself.
-MW_VERSION=mediawiki-1.19.0
+# tested versions: 1.19.X -> 1.21.1
+MW_VERSION_MAJOR=1.21
+MW_VERSION_MINOR=1
 FILES_FOLDER=install-wiki
 DB_INSTALL_SCRIPT=db_install.php
diff --git a/contrib/patches/docbook-xsl-manpages-charmap.patch b/contrib/patches/docbook-xsl-manpages-charmap.patch
deleted file mode 100644 (file)
index f2b08b4..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-From: Ismail Dönmez <ismail@pardus.org.tr>
-
-Trying to build the documentation with docbook-xsl 1.73 may result in
-the following error.  This patch fixes it.
-
-$ xmlto -m callouts.xsl man git-add.xml
-runtime error: file
-file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line
-129 element call-template
-The called template 'read-character-map' was not found.
-
---- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap   2007-07-23 16:24:23.000000000 +0100
-+++ docbook-xsl-1.73.0/manpages/docbook.xsl    2007-07-23 16:25:16.000000000 +0100
-@@ -37,6 +37,7 @@
-   <xsl:include href="lists.xsl"/>
-   <xsl:include href="endnotes.xsl"/>
-   <xsl:include href="table.xsl"/>
-+  <xsl:include href="../common/charmap.xsl"/>
-
-   <!-- * we rename the following just to avoid using params with "man" -->
-   <!-- * prefixes in the table.xsl stylesheet (because that stylesheet -->
index aa7bc97beecc6af7adca5b6886d8e7c61f9173b1..c3a3cac77b875b2cb925971e2baca141f9c4f163 100755 (executable)
@@ -13,6 +13,9 @@
 # or
 # % git clone bzr::lp:myrepo
 #
+# If you want to specify which branches you want track (per repo):
+# git config remote-bzr.branches 'trunk, devel, test'
+#
 
 import sys
 
@@ -26,15 +29,20 @@ bzrlib.plugin.load_plugins()
 import bzrlib.generate_ids
 import bzrlib.transport
 import bzrlib.errors
+import bzrlib.ui
+import bzrlib.urlutils
+import bzrlib.branch
 
 import sys
 import os
 import json
 import re
 import StringIO
+import atexit, shutil, hashlib, urlparse, subprocess
 
 NAME_RE = re.compile('^([^<>]+)')
 AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
+EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
 RAW_AUTHOR_RE = re.compile('^(\w+) (.+)? <(.*)> (\d+) ([+-]\d+)')
 
 def die(msg, *args):
@@ -47,6 +55,12 @@ def warn(msg, *args):
 def gittz(tz):
     return '%+03d%02d' % (tz / 3600, tz % 3600 / 60)
 
+def get_config(config):
+    cmd = ['git', 'config', '--get', config]
+    process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+    output, _ = process.communicate()
+    return output
+
 class Marks:
 
     def __init__(self, path):
@@ -82,7 +96,7 @@ class Marks:
         return self.marks[rev]
 
     def to_rev(self, mark):
-        return self.rev_marks[mark]
+        return str(self.rev_marks[mark])
 
     def next_mark(self):
         self.last_mark += 1
@@ -94,7 +108,7 @@ class Marks:
         return self.last_mark
 
     def is_marked(self, rev):
-        return self.marks.has_key(rev)
+        return rev in self.marks
 
     def new_mark(self, rev, mark):
         self.marks[rev] = mark
@@ -102,7 +116,10 @@ class Marks:
         self.last_mark = mark
 
     def get_tip(self, branch):
-        return self.tips.get(branch, None)
+        try:
+            return str(self.tips[branch])
+        except KeyError:
+            return None
 
     def set_tip(self, branch, tip):
         self.tips[branch] = tip
@@ -172,9 +189,19 @@ def fixup_user(user):
         name = m.group(1)
         mail = m.group(2).strip()
     else:
-        m = NAME_RE.match(user)
+        m = EMAIL_RE.match(user)
         if m:
-            name = m.group(1).strip()
+            name = m.group(1)
+            mail = m.group(2)
+        else:
+            m = NAME_RE.match(user)
+            if m:
+                name = m.group(1).strip()
+
+    if not name:
+        name = 'unknown'
+    if not mail:
+        mail = 'Unknown'
 
     return '%s <%s>' % (name, mail)
 
@@ -224,7 +251,7 @@ def export_files(tree, files):
             else:
                 mode = '100644'
 
-            # is the blog already exported?
+            # is the blob already exported?
             if h in filenodes:
                 mark = filenodes[h]
                 final.append((mode, mark, path))
@@ -248,22 +275,32 @@ def export_files(tree, files):
 
     return final
 
-def export_branch(branch, name):
+def export_branch(repo, name):
     global prefix
 
     ref = '%s/heads/%s' % (prefix, name)
     tip = marks.get_tip(name)
 
+    branch = get_remote_branch(name)
     repo = branch.repository
-    repo.lock_read()
+
+    branch.lock_read()
     revs = branch.iter_merge_sorted_revisions(None, tip, 'exclude', 'forward')
-    count = 0
+    try:
+        tip_revno = branch.revision_id_to_revno(tip)
+        last_revno, _ = branch.last_revision_info()
+        total = last_revno - tip_revno
+    except bzrlib.errors.NoSuchRevision:
+        tip_revno = 0
+        total = 0
 
-    revs = [revid for revid, _, _, _ in revs if not marks.is_marked(revid)]
+    for revid, _, seq, _ in revs:
 
-    for revid in revs:
+        if marks.is_marked(revid):
+            continue
 
         rev = repo.get_revision(revid)
+        revno = seq[0]
 
         parents = rev.parent_ids
         time = rev.timestamp
@@ -318,12 +355,18 @@ def export_branch(branch, name):
             print "M %s :%u %s" % f
         print
 
-        count += 1
-        if (count % 100 == 0):
-            print "progress revision %s (%d/%d)" % (revid, count, len(revs))
-            print "#############################################################"
+        if len(seq) > 1:
+            # let's skip branch revisions from the progress report
+            continue
 
-    repo.unlock()
+        progress = (revno - tip_revno)
+        if (progress % 100 == 0):
+            if total:
+                print "progress revision %d '%s' (%d/%d)" % (revno, name, progress, total)
+            else:
+                print "progress revision %d '%s' (%d)" % (revno, name, progress)
+
+    branch.unlock()
 
     revid = branch.last_revision()
 
@@ -345,23 +388,24 @@ def export_tag(repo, name):
 def do_import(parser):
     global dirname
 
-    branch = parser.repo
+    repo = parser.repo
     path = os.path.join(dirname, 'marks-git')
 
     print "feature done"
     if os.path.exists(path):
         print "feature import-marks=%s" % path
     print "feature export-marks=%s" % path
+    print "feature force"
     sys.stdout.flush()
 
     while parser.check('import'):
         ref = parser[1]
         if ref.startswith('refs/heads/'):
             name = ref[len('refs/heads/'):]
-            export_branch(branch, name)
+            export_branch(repo, name)
         if ref.startswith('refs/tags/'):
             name = ref[len('refs/tags/'):]
-            export_tag(branch, name)
+            export_tag(repo, name)
         parser.next()
 
     print 'done'
@@ -380,23 +424,21 @@ def parse_blob(parser):
 
 class CustomTree():
 
-    def __init__(self, repo, revid, parents, files):
+    def __init__(self, branch, revid, parents, files):
         global files_cache
 
-        self.repo = repo
-        self.revid = revid
-        self.parents = parents
         self.updates = {}
+        self.branch = branch
 
         def copy_tree(revid):
             files = files_cache[revid] = {}
-            tree = repo.repository.revision_tree(revid)
-            repo.lock_read()
+            branch.lock_read()
+            tree = branch.repository.revision_tree(revid)
             try:
                 for path, entry in tree.iter_entries_by_dir():
-                    files[path] = entry.file_id
+                    files[path] = [entry.file_id, None]
             finally:
-                repo.unlock()
+                branch.unlock()
             return files
 
         if len(parents) == 0:
@@ -409,12 +451,18 @@ class CustomTree():
                 self.base_files = copy_tree(self.base_id)
 
         self.files = files_cache[revid] = self.base_files.copy()
+        self.rev_files = {}
+
+        for path, data in self.files.iteritems():
+            fid, mark = data
+            self.rev_files[fid] = [path, mark]
 
         for path, f in files.iteritems():
-            fid = self.files.get(path, None)
+            fid, mark = self.files.get(path, [None, None])
             if not fid:
                 fid = bzrlib.generate_ids.gen_file_id(path)
             f['path'] = path
+            self.rev_files[fid] = [path, mark]
             self.updates[fid] = f
 
     def last_revision(self):
@@ -424,16 +472,16 @@ class CustomTree():
         changes = []
 
         def get_parent(dirname, basename):
-            parent_fid = self.base_files.get(dirname, None)
+            parent_fid, mark = self.base_files.get(dirname, [None, None])
             if parent_fid:
                 return parent_fid
-            parent_fid = self.files.get(dirname, None)
+            parent_fid, mark = self.files.get(dirname, [None, None])
             if parent_fid:
                 return parent_fid
             if basename == '':
                 return None
             fid = bzrlib.generate_ids.gen_file_id(path)
-            d = add_entry(fid, dirname, 'directory')
+            add_entry(fid, dirname, 'directory')
             return fid
 
         def add_entry(fid, path, kind, mode = None):
@@ -454,9 +502,8 @@ class CustomTree():
                     (None, basename),
                     (None, kind),
                     (None, executable))
-            self.files[path] = change[0]
+            self.files[path] = [change[0], None]
             changes.append(change)
-            return change
 
         def update_entry(fid, path, kind, mode = None):
             dirname, basename = os.path.split(path)
@@ -476,9 +523,8 @@ class CustomTree():
                     (None, basename),
                     (None, kind),
                     (None, executable))
-            self.files[path] = change[0]
+            self.files[path] = [change[0], None]
             changes.append(change)
-            return change
 
         def remove_entry(fid, path, kind):
             dirname, basename = os.path.split(path)
@@ -493,7 +539,6 @@ class CustomTree():
                     (None, None))
             del self.files[path]
             changes.append(change)
-            return change
 
         for fid, f in self.updates.iteritems():
             path = f['path']
@@ -507,13 +552,30 @@ class CustomTree():
             else:
                 add_entry(fid, path, 'file', f['mode'])
 
+            self.files[path][1] = f['mark']
+            self.rev_files[fid][1] = f['mark']
+
         return changes
 
+    def get_content(self, file_id):
+        path, mark = self.rev_files[file_id]
+        if mark:
+            return blob_marks[mark]
+
+        # last resort
+        tree = self.branch.repository.revision_tree(self.base_id)
+        return tree.get_file_text(file_id)
+
     def get_file_with_stat(self, file_id, path=None):
-        return (StringIO.StringIO(self.updates[file_id]['data']), None)
+        content = self.get_content(file_id)
+        return (StringIO.StringIO(content), None)
 
     def get_symlink_target(self, file_id):
-        return self.updates[file_id]['data']
+        return self.get_content(file_id)
+
+    def id2path(self, file_id):
+        path, mark = self.rev_files[file_id]
+        return path
 
 def c_style_unescape(string):
     if string[0] == string[-1] == '"':
@@ -521,7 +583,7 @@ def c_style_unescape(string):
     return string
 
 def parse_commit(parser):
-    global marks, blob_marks, bmarks, parsed_refs
+    global marks, blob_marks, parsed_refs
     global mode
 
     parents = []
@@ -529,8 +591,11 @@ def parse_commit(parser):
     ref = parser[1]
     parser.next()
 
-    if ref != 'refs/heads/master':
-        die("bzr doesn't support multiple branches; use 'master'")
+    if ref.startswith('refs/heads/'):
+        name = ref[len('refs/heads/'):]
+        branch = get_remote_branch(name)
+    else:
+        die('unknown ref')
 
     commit_mark = parser.get_mark()
     parser.next()
@@ -547,35 +612,37 @@ def parse_commit(parser):
         parents.append(parser.get_mark())
         parser.next()
 
+    # fast-export adds an extra newline
+    if data[-1] == '\n':
+        data = data[:-1]
+
     files = {}
 
     for line in parser:
         if parser.check('M'):
             t, m, mark_ref, path = line.split(' ', 3)
             mark = int(mark_ref[1:])
-            f = { 'mode' : m, 'data' : blob_marks[mark] }
+            f = { 'mode' : m, 'mark' : mark }
         elif parser.check('D'):
-            t, path = line.split(' ')
+            t, path = line.split(' ', 1)
             f = { 'deleted' : True }
         else:
             die('Unknown file command: %s' % line)
         path = c_style_unescape(path).decode('utf-8')
         files[path] = f
 
-    repo = parser.repo
-
     committer, date, tz = committer
-    parents = [str(mark_to_rev(p)) for p in parents]
+    parents = [mark_to_rev(p) for p in parents]
     revid = bzrlib.generate_ids.gen_revision_id(committer, date)
     props = {}
-    props['branch-nick'] = repo.nick
+    props['branch-nick'] = branch.nick
 
-    mtree = CustomTree(repo, revid, parents, files)
+    mtree = CustomTree(branch, revid, parents, files)
     changes = mtree.iter_changes()
 
-    repo.lock_write()
+    branch.lock_write()
     try:
-        builder = repo.get_commit_builder(parents, None, date, tz, committer, props, revid)
+        builder = branch.get_commit_builder(parents, None, date, tz, committer, props, revid)
         try:
             list(builder.record_iter_changes(mtree, mtree.last_revision(), changes))
             builder.finish_inventory()
@@ -584,7 +651,7 @@ def parse_commit(parser):
             builder.abort()
             raise
     finally:
-        repo.unlock()
+        branch.unlock()
 
     parsed_refs[ref] = revid
     marks.new_mark(revid, commit_mark)
@@ -595,9 +662,6 @@ def parse_reset(parser):
     ref = parser[1]
     parser.next()
 
-    if ref != 'refs/heads/master':
-        die("bzr doesn't support multiple branches; use 'master'")
-
     # ugh
     if parser.check('commit'):
         parse_commit(parser)
@@ -610,7 +674,7 @@ def parse_reset(parser):
     parsed_refs[ref] = mark_to_rev(from_mark)
 
 def do_export(parser):
-    global parsed_refs, dirname, peer
+    global parsed_refs, dirname
 
     parser.next()
 
@@ -628,21 +692,35 @@ def do_export(parser):
         else:
             die('unhandled export command: %s' % line)
 
-    repo = parser.repo
-
     for ref, revid in parsed_refs.iteritems():
-        if ref == 'refs/heads/master':
-            repo.generate_revision_history(revid, marks.get_tip('master'))
-            revno, revid = repo.last_revision_info()
-            if peer:
-                if hasattr(peer, "import_last_revision_info_and_tags"):
-                    peer.import_last_revision_info_and_tags(repo, revno, revid)
-                else:
-                    peer.import_last_revision_info(repo.repository, revno, revid)
-            else:
-                wt = repo.bzrdir.open_workingtree()
+        if ref.startswith('refs/heads/'):
+            name = ref[len('refs/heads/'):]
+            branch = get_remote_branch(name)
+            branch.generate_revision_history(revid, marks.get_tip(name))
+
+            if name in peers:
+                peer = bzrlib.branch.Branch.open(peers[name])
+                try:
+                    peer.bzrdir.push_branch(branch, revision_id=revid)
+                except bzrlib.errors.DivergedBranches:
+                    print "error %s non-fast forward" % ref
+                    continue
+
+            try:
+                wt = branch.bzrdir.open_workingtree()
                 wt.update()
+            except bzrlib.errors.NoWorkingTree:
+                pass
+        elif ref.startswith('refs/tags/'):
+            # TODO: implement tag push
+            print "error %s pushing tags not supported" % ref
+            continue
+        else:
+            # transport-helper/fast-export bugs
+            continue
+
         print "ok %s" % ref
+
     print
 
 def do_capabilities(parser):
@@ -666,9 +744,15 @@ def ref_is_valid(name):
 
 def do_list(parser):
     global tags
-    print "? refs/heads/%s" % 'master'
 
-    branch = parser.repo
+    master_branch = None
+
+    for name in branches:
+        if not master_branch:
+            master_branch = name
+        print "? refs/heads/%s" % name
+
+    branch = get_remote_branch(master_branch)
     branch.lock_read()
     for tag, revid in branch.tags.get_tag_dict().items():
         try:
@@ -680,60 +764,171 @@ def do_list(parser):
         print "? refs/tags/%s" % tag
         tags[tag] = revid
     branch.unlock()
-    print "@refs/heads/%s HEAD" % 'master'
+
+    print "@refs/heads/%s HEAD" % master_branch
     print
 
+def clone(path, remote_branch):
+    try:
+        bdir = bzrlib.bzrdir.BzrDir.create(path)
+    except bzrlib.errors.AlreadyControlDirError:
+        bdir = bzrlib.bzrdir.BzrDir.open(path)
+    repo = bdir.find_repository()
+    repo.fetch(remote_branch.repository)
+    return remote_branch.sprout(bdir, repository=repo)
+
+def get_remote_branch(name):
+    global dirname, branches
+
+    remote_branch = bzrlib.branch.Branch.open(branches[name])
+    if isinstance(remote_branch.user_transport, bzrlib.transport.local.LocalTransport):
+        return remote_branch
+
+    branch_path = os.path.join(dirname, 'clone', name)
+
+    try:
+        branch = bzrlib.branch.Branch.open(branch_path)
+    except bzrlib.errors.NotBranchError:
+        # clone
+        branch = clone(branch_path, remote_branch)
+    else:
+        # pull
+        try:
+            branch.pull(remote_branch, overwrite=True)
+        except bzrlib.errors.DivergedBranches:
+            # use remote branch for now
+            return remote_branch
+
+    return branch
+
+def find_branches(repo):
+    transport = repo.bzrdir.root_transport
+
+    for fn in transport.iter_files_recursive():
+        if not fn.endswith('.bzr/branch-format'):
+            continue
+
+        name = subdir = fn[:-len('/.bzr/branch-format')]
+        name = name if name != '' else 'master'
+        name = name.replace('/', '+')
+
+        try:
+            cur = transport.clone(subdir)
+            branch = bzrlib.branch.Branch.open_from_transport(cur)
+        except bzrlib.errors.NotBranchError:
+            continue
+        else:
+            yield name, branch.base
+
 def get_repo(url, alias):
-    global dirname, peer
+    global dirname, peer, branches
 
+    normal_url = bzrlib.urlutils.normalize_url(url)
     origin = bzrlib.bzrdir.BzrDir.open(url)
-    branch = origin.open_branch()
+    is_local = isinstance(origin.transport, bzrlib.transport.local.LocalTransport)
+
+    shared_path = os.path.join(gitdir, 'bzr')
+    try:
+        shared_dir = bzrlib.bzrdir.BzrDir.open(shared_path)
+    except bzrlib.errors.NotBranchError:
+        shared_dir = bzrlib.bzrdir.BzrDir.create(shared_path)
+    try:
+        shared_repo = shared_dir.open_repository()
+    except bzrlib.errors.NoRepositoryPresent:
+        shared_repo = shared_dir.create_repository(shared=True)
 
-    if not isinstance(origin.transport, bzrlib.transport.local.LocalTransport):
+    if not is_local:
         clone_path = os.path.join(dirname, 'clone')
-        remote_branch = branch
-        if os.path.exists(clone_path):
-            # pull
-            d = bzrlib.bzrdir.BzrDir.open(clone_path)
-            branch = d.open_branch()
-            result = branch.pull(remote_branch, [], None, False)
+        if not os.path.exists(clone_path):
+            os.mkdir(clone_path)
         else:
-            # clone
-            d = origin.sprout(clone_path, None,
-                    hardlink=True, create_tree_if_local=False,
-                    source_branch=remote_branch)
-            branch = d.open_branch()
-            branch.bind(remote_branch)
-
-        peer = remote_branch
+            # check and remove old organization
+            try:
+                bdir = bzrlib.bzrdir.BzrDir.open(clone_path)
+                bdir.destroy_repository()
+            except bzrlib.errors.NotBranchError:
+                pass
+            except bzrlib.errors.NoRepositoryPresent:
+                pass
+
+    wanted = get_config('remote-bzr.branches').rstrip().split(', ')
+    # stupid python
+    wanted = [e for e in wanted if e]
+
+    if not wanted:
+        try:
+            repo = origin.open_repository()
+            if not repo.user_transport.listable():
+                # this repository is not usable for us
+                raise bzrlib.errors.NoRepositoryPresent(repo.bzrdir)
+        except bzrlib.errors.NoRepositoryPresent:
+            wanted = ['master']
+
+    if wanted:
+        def list_wanted(url, wanted):
+            for name in wanted:
+                subdir = name if name != 'master' else ''
+                yield name, bzrlib.urlutils.join(url, subdir)
+
+        branch_list = list_wanted(url, wanted)
     else:
-        peer = None
+        branch_list = find_branches(repo)
 
-    return branch
+    for name, url in branch_list:
+        if not is_local:
+            peers[name] = url
+        branches[name] = url
+
+    return origin
+
+def fix_path(alias, orig_url):
+    url = urlparse.urlparse(orig_url, 'file')
+    if url.scheme != 'file' or os.path.isabs(url.path):
+        return
+    abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
+    cmd = ['git', 'config', 'remote.%s.url' % alias, "bzr::%s" % abs_url]
+    subprocess.call(cmd)
 
 def main(args):
-    global marks, prefix, dirname
+    global marks, prefix, gitdir, dirname
     global tags, filenodes
     global blob_marks
     global parsed_refs
     global files_cache
+    global is_tmp
+    global branches, peers
 
     alias = args[1]
     url = args[2]
 
-    prefix = 'refs/bzr/%s' % alias
     tags = {}
     filenodes = {}
     blob_marks = {}
     parsed_refs = {}
     files_cache = {}
+    marks = None
+    branches = {}
+    peers = {}
 
+    if alias[5:] == url:
+        is_tmp = True
+        alias = hashlib.sha1(alias).hexdigest()
+    else:
+        is_tmp = False
+
+    prefix = 'refs/bzr/%s' % alias
     gitdir = os.environ['GIT_DIR']
     dirname = os.path.join(gitdir, 'bzr', alias)
 
+    if not is_tmp:
+        fix_path(alias, url)
+
     if not os.path.exists(dirname):
         os.makedirs(dirname)
 
+    if hasattr(bzrlib.ui.ui_factory, 'be_quiet'):
+        bzrlib.ui.ui_factory.be_quiet(True)
+
     repo = get_repo(url, alias)
 
     marks_path = os.path.join(dirname, 'marks-int')
@@ -753,6 +948,13 @@ def main(args):
             die('unhandled command: %s' % line)
         sys.stdout.flush()
 
-    marks.store()
+def bye():
+    if not marks:
+        return
+    if not is_tmp:
+        marks.store()
+    else:
+        shutil.rmtree(dirname)
 
+atexit.register(bye)
 sys.exit(main(sys.argv))
index 45f6c80d45ab9848d1197e34a00b8ac5977a05fc..0194c67fb1db1dc6fdd4bd90b67d04cfb7a34e1d 100755 (executable)
@@ -8,8 +8,11 @@
 # Just copy to your ~/bin, or anywhere in your $PATH.
 # Then you can clone with:
 # git clone hg::/path/to/mercurial/repo/
+#
+# For remote repositories a local clone is stored in
+# "$GIT_DIR/hg/origin/clone/.hg/".
 
-from mercurial import hg, ui, bookmarks, context, util, encoding
+from mercurial import hg, ui, bookmarks, context, encoding, node, error, extensions, discovery, util
 
 import re
 import sys
@@ -18,7 +21,16 @@ import json
 import shutil
 import subprocess
 import urllib
+import atexit
+import urlparse, hashlib
 
+#
+# If you are not in hg-git-compat mode and want to disable the tracking of
+# named branches:
+# git config --global remote-hg.track-branches false
+#
+# If you want the equivalent of hg's clone/pull--insecure option:
+# git config --global remote-hg.insecure true
 #
 # If you want to switch to hg-git compatibility mode:
 # git config --global remote-hg.hg-git-compat true
@@ -36,9 +48,12 @@ import urllib
 
 NAME_RE = re.compile('^([^<>]+)')
 AUTHOR_RE = re.compile('^([^<>]+?)? ?<([^<>]*)>$')
+EMAIL_RE = re.compile('^([^<>]+[^ \\\t<>])?\\b(?:[ \\t<>]*?)\\b([^ \\t<>]+@[^ \\t<>]+)')
 AUTHOR_HG_RE = re.compile('^(.*?) ?<(.*?)(?:>(.+)?)?$')
 RAW_AUTHOR_RE = re.compile('^(\w+) (?:(.+)? )?<(.*)> (\d+) ([+-]\d+)')
 
+VERSION = 2
+
 def die(msg, *args):
     sys.stderr.write('ERROR: %s\n' % (msg % args))
     sys.exit(1)
@@ -56,22 +71,61 @@ def hgmode(mode):
     m = { '100755': 'x', '120000': 'l' }
     return m.get(mode, '')
 
+def hghex(n):
+    return node.hex(n)
+
+def hgbin(n):
+    return node.bin(n)
+
+def hgref(ref):
+    return ref.replace('___', ' ')
+
+def gitref(ref):
+    return ref.replace(' ', '___')
+
+def check_version(*check):
+    if not hg_version:
+        return True
+    return hg_version >= check
+
 def get_config(config):
     cmd = ['git', 'config', '--get', config]
     process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
     output, _ = process.communicate()
     return output
 
+def get_config_bool(config, default=False):
+    value = get_config(config).rstrip('\n')
+    if value == "true":
+        return True
+    elif value == "false":
+        return False
+    else:
+        return default
+
 class Marks:
 
-    def __init__(self, path):
+    def __init__(self, path, repo):
         self.path = path
+        self.repo = repo
+        self.clear()
+        self.load()
+
+        if self.version < VERSION:
+            if self.version == 1:
+                self.upgrade_one()
+
+            # upgraded?
+            if self.version < VERSION:
+                self.clear()
+                self.version = VERSION
+
+    def clear(self):
         self.tips = {}
         self.marks = {}
         self.rev_marks = {}
         self.last_mark = 0
-
-        self.load()
+        self.version = 0
 
     def load(self):
         if not os.path.exists(self.path):
@@ -82,12 +136,21 @@ class Marks:
         self.tips = tmp['tips']
         self.marks = tmp['marks']
         self.last_mark = tmp['last-mark']
+        self.version = tmp.get('version', 1)
 
         for rev, mark in self.marks.iteritems():
-            self.rev_marks[mark] = int(rev)
+            self.rev_marks[mark] = rev
+
+    def upgrade_one(self):
+        def get_id(rev):
+            return hghex(self.repo.changelog.node(int(rev)))
+        self.tips = dict((name, get_id(rev)) for name, rev in self.tips.iteritems())
+        self.marks = dict((get_id(rev), mark) for rev, mark in self.marks.iteritems())
+        self.rev_marks = dict((mark, get_id(rev)) for mark, rev in self.rev_marks.iteritems())
+        self.version = 2
 
     def dict(self):
-        return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark }
+        return { 'tips': self.tips, 'marks': self.marks, 'last-mark' : self.last_mark, 'version' : self.version }
 
     def store(self):
         json.dump(self.dict(), open(self.path, 'w'))
@@ -96,26 +159,30 @@ class Marks:
         return str(self.dict())
 
     def from_rev(self, rev):
-        return self.marks[str(rev)]
+        return self.marks[rev]
 
     def to_rev(self, mark):
-        return self.rev_marks[mark]
+        return str(self.rev_marks[mark])
+
+    def next_mark(self):
+        self.last_mark += 1
+        return self.last_mark
 
     def get_mark(self, rev):
         self.last_mark += 1
-        self.marks[str(rev)] = self.last_mark
+        self.marks[rev] = self.last_mark
         return self.last_mark
 
     def new_mark(self, rev, mark):
-        self.marks[str(rev)] = mark
+        self.marks[rev] = mark
         self.rev_marks[mark] = rev
         self.last_mark = mark
 
     def is_marked(self, rev):
-        return self.marks.has_key(str(rev))
+        return rev in self.marks
 
     def get_tip(self, branch):
-        return self.tips.get(branch, 0)
+        return str(self.tips[branch])
 
     def set_tip(self, branch, tip):
         self.tips[branch] = tip
@@ -188,19 +255,43 @@ class Parser:
         tz = ((tz / 100) * 3600) + ((tz % 100) * 60)
         return (user, int(date), -tz)
 
-def export_file(fc):
-    d = fc.data()
-    print "M %s inline %s" % (gitmode(fc.flags()), fc.path())
-    print "data %d" % len(d)
-    print d
+def fix_file_path(path):
+    if not os.path.isabs(path):
+        return path
+    return os.path.relpath(path, '/')
+
+def export_files(files):
+    global marks, filenodes
+
+    final = []
+    for f in files:
+        fid = node.hex(f.filenode())
+
+        if fid in filenodes:
+            mark = filenodes[fid]
+        else:
+            mark = marks.next_mark()
+            filenodes[fid] = mark
+            d = f.data()
+
+            print "blob"
+            print "mark :%u" % mark
+            print "data %d" % len(d)
+            print d
+
+        path = fix_file_path(f.path())
+        final.append((gitmode(f.flags()), mark, path))
+
+    return final
 
 def get_filechanges(repo, ctx, parent):
     modified = set()
     added = set()
     removed = set()
 
+    # load earliest manifest first for caching reasons
+    prev = parent.manifest().copy()
     cur = ctx.manifest()
-    prev = repo[parent].manifest().copy()
 
     for fn in cur:
         if fn in prev:
@@ -221,9 +312,14 @@ def fixup_user_git(user):
         name = m.group(1)
         mail = m.group(2).strip()
     else:
-        m = NAME_RE.match(user)
+        m = EMAIL_RE.match(user)
         if m:
-            name = m.group(1).strip()
+            name = m.group(1)
+            mail = m.group(2)
+        else:
+            m = NAME_RE.match(user)
+            if m:
+                name = m.group(1).strip()
     return (name, mail)
 
 def fixup_user_hg(user):
@@ -262,29 +358,66 @@ def fixup_user(user):
 
     return '%s <%s>' % (name, mail)
 
+def updatebookmarks(repo, peer):
+    remotemarks = peer.listkeys('bookmarks')
+    localmarks = repo._bookmarks
+
+    if not remotemarks:
+        return
+
+    for k, v in remotemarks.iteritems():
+        localmarks[k] = hgbin(v)
+
+    if hasattr(localmarks, 'write'):
+        localmarks.write()
+    else:
+        bookmarks.write(repo)
+
 def get_repo(url, alias):
     global dirname, peer
 
     myui = ui.ui()
     myui.setconfig('ui', 'interactive', 'off')
+    myui.fout = sys.stderr
+
+    if get_config_bool('remote-hg.insecure'):
+        myui.setconfig('web', 'cacerts', '')
 
-    if hg.islocal(url):
+    extensions.loadall(myui)
+
+    if hg.islocal(url) and not os.environ.get('GIT_REMOTE_HG_TEST_REMOTE'):
         repo = hg.repository(myui, url)
+        if not os.path.exists(dirname):
+            os.makedirs(dirname)
     else:
+        shared_path = os.path.join(gitdir, 'hg')
+        if not os.path.exists(shared_path):
+            try:
+                hg.clone(myui, {}, url, shared_path, update=False, pull=True)
+            except:
+                die('Repository error')
+
+        if not os.path.exists(dirname):
+            os.makedirs(dirname)
+
         local_path = os.path.join(dirname, 'clone')
         if not os.path.exists(local_path):
-            peer, dstpeer = hg.clone(myui, {}, url, local_path, update=False, pull=True)
-            repo = dstpeer.local()
-        else:
-            repo = hg.repository(myui, local_path)
+            hg.share(myui, shared_path, local_path, update=False)
+
+        repo = hg.repository(myui, local_path)
+        try:
             peer = hg.peer(myui, {}, url)
-            repo.pull(peer, heads=None, force=True)
+        except:
+            die('Repository error')
+        repo.pull(peer, heads=None, force=True)
+
+        updatebookmarks(repo, peer)
 
     return repo
 
 def rev_to_mark(rev):
     global marks
-    return marks.from_rev(rev)
+    return marks.from_rev(rev.hex())
 
 def mark_to_rev(mark):
     global marks
@@ -294,21 +427,24 @@ def export_ref(repo, name, kind, head):
     global prefix, marks, mode
 
     ename = '%s/%s' % (kind, name)
-    tip = marks.get_tip(ename)
+    try:
+        tip = marks.get_tip(ename)
+        tip = repo[tip].rev()
+    except:
+        tip = 0
 
-    # mercurial takes too much time checking this
-    if tip and tip == head.rev():
-        # nothing to do
-        return
     revs = xrange(tip, head.rev() + 1)
-    count = 0
-
-    revs = [rev for rev in revs if not marks.is_marked(rev)]
+    total = len(revs)
 
     for rev in revs:
 
         c = repo[rev]
-        (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(c.node())
+        node = c.node()
+
+        if marks.is_marked(c.hex()):
+            continue
+
+        (manifest, user, (time, tz), files, desc, extra) = repo.changelog.read(node)
         rev_branch = extra['branch']
 
         author = "%s %d %s" % (fixup_user(user), time, gittz(tz))
@@ -318,7 +454,7 @@ def export_ref(repo, name, kind, head):
         else:
             committer = author
 
-        parents = [p for p in repo.changelog.parentrevs(rev) if p >= 0]
+        parents = [repo[p] for p in repo.changelog.parentrevs(rev) if p >= 0]
 
         if len(parents) == 0:
             modified = c.manifest().keys()
@@ -357,8 +493,10 @@ def export_ref(repo, name, kind, head):
         if len(parents) == 0 and rev:
             print 'reset %s/%s' % (prefix, ename)
 
+        modified_final = export_files(c.filectx(f) for f in modified)
+
         print "commit %s/%s" % (prefix, ename)
-        print "mark :%d" % (marks.get_mark(rev))
+        print "mark :%d" % (marks.get_mark(c.hex()))
         print "author %s" % (author)
         print "committer %s" % (committer)
         print "data %d" % (len(desc))
@@ -369,29 +507,28 @@ def export_ref(repo, name, kind, head):
             if len(parents) > 1:
                 print "merge :%s" % (rev_to_mark(parents[1]))
 
-        for f in modified:
-            export_file(c.filectx(f))
         for f in removed:
-            print "D %s" % (f)
+            print "D %s" % (fix_file_path(f))
+        for f in modified_final:
+            print "M %s :%u %s" % f
         print
 
-        count += 1
-        if (count % 100 == 0):
-            print "progress revision %d '%s' (%d/%d)" % (rev, name, count, len(revs))
-            print "#############################################################"
+        progress = (rev - tip)
+        if (progress % 100 == 0):
+            print "progress revision %d '%s' (%d/%d)" % (rev, name, progress, total)
 
     # make sure the ref is updated
     print "reset %s/%s" % (prefix, ename)
-    print "from :%u" % rev_to_mark(rev)
+    print "from :%u" % rev_to_mark(head)
     print
 
-    marks.set_tip(ename, rev)
+    marks.set_tip(ename, head.hex())
 
 def export_tag(repo, tag):
-    export_ref(repo, tag, 'tags', repo[tag])
+    export_ref(repo, tag, 'tags', repo[hgref(tag)])
 
 def export_bookmark(repo, bmark):
-    head = bmarks[bmark]
+    head = bmarks[hgref(bmark)]
     export_ref(repo, bmark, 'bookmarks', head)
 
 def export_branch(repo, branch):
@@ -417,73 +554,72 @@ def do_capabilities(parser):
     if os.path.exists(path):
         print "*import-marks %s" % path
     print "*export-marks %s" % path
+    print "option"
 
     print
 
+def branch_tip(branch):
+    return branches[branch][-1]
+
 def get_branch_tip(repo, branch):
     global branches
 
-    heads = branches.get(branch, None)
+    heads = branches.get(hgref(branch), None)
     if not heads:
         return None
 
     # verify there's only one head
     if (len(heads) > 1):
         warn("Branch '%s' has more than one head, consider merging" % branch)
-        # older versions of mercurial don't have this
-        if hasattr(repo, "branchtip"):
-            return repo.branchtip(branch)
+        return branch_tip(hgref(branch))
 
     return heads[0]
 
 def list_head(repo, cur):
-    global g_head, bmarks
+    global g_head, bmarks, fake_bmark
 
-    head = bookmarks.readcurrent(repo)
-    if head:
-        node = repo[head]
-    else:
-        # fake bookmark from current branch
-        head = cur
-        node = repo['.']
-        if not node:
-            node = repo['tip']
-        if not node:
-            return
-        if head == 'default':
-            head = 'master'
-        bmarks[head] = node
+    if 'default' not in branches:
+        # empty repo
+        return
 
+    node = repo[branch_tip('default')]
+    head = 'master' if not 'master' in bmarks else 'default'
+    fake_bmark = head
+    bmarks[head] = node
+
+    head = gitref(head)
     print "@refs/heads/%s HEAD" % head
     g_head = (head, node)
 
 def do_list(parser):
-    global branches, bmarks, mode, track_branches
+    global branches, bmarks, track_branches
 
     repo = parser.repo
     for bmark, node in bookmarks.listbookmarks(repo).iteritems():
         bmarks[bmark] = repo[node]
 
     cur = repo.dirstate.branch()
+    orig = peer if peer else repo
+
+    for branch, heads in orig.branchmap().iteritems():
+        # only open heads
+        heads = [h for h in heads if 'close' not in repo.changelog.read(h)[5]]
+        if heads:
+            branches[branch] = heads
 
     list_head(repo, cur)
 
     if track_branches:
-        for branch in repo.branchmap():
-            heads = repo.branchheads(branch)
-            if len(heads):
-                branches[branch] = heads
-
         for branch in branches:
-            print "? refs/heads/branches/%s" % branch
+            print "? refs/heads/branches/%s" % gitref(branch)
 
     for bmark in bmarks:
-        print "? refs/heads/%s" % bmark
+        print "? refs/heads/%s" % gitref(bmark)
 
     for tag, node in repo.tagslist():
         if tag == 'tip':
             continue
-        print "? refs/tags/%s" % tag
+        print "? refs/tags/%s" % gitref(tag)
 
     print
 
@@ -496,6 +632,7 @@ def do_import(parser):
     if os.path.exists(path):
         print "feature import-marks=%s" % path
     print "feature export-marks=%s" % path
+    print "feature force"
     sys.stdout.flush()
 
     tmp = encoding.encoding
@@ -532,7 +669,6 @@ def parse_blob(parser):
     data = parser.get_data()
     blob_marks[mark] = data
     parser.next()
-    return
 
 def get_merge_files(repo, p1, p2, files):
     for e in repo[p1].files():
@@ -543,7 +679,7 @@ def get_merge_files(repo, p1, p2, files):
             files[e] = f
 
 def parse_commit(parser):
-    global marks, blob_marks, bmarks, parsed_refs
+    global marks, blob_marks, parsed_refs
     global mode
 
     from_mark = merge_mark = None
@@ -568,6 +704,10 @@ def parse_commit(parser):
         if parser.check('merge'):
             die('octopus merges are not supported yet')
 
+    # fast-export adds an extra newline
+    if data[-1] == '\n':
+        data = data[:-1]
+
     files = {}
 
     for line in parser:
@@ -576,12 +716,17 @@ def parse_commit(parser):
             mark = int(mark_ref[1:])
             f = { 'mode' : hgmode(m), 'data' : blob_marks[mark] }
         elif parser.check('D'):
-            t, path = line.split(' ')
+            t, path = line.split(' ', 1)
             f = { 'deleted' : True }
         else:
             die('Unknown file command: %s' % line)
         files[path] = f
 
+    # only export the commits if we are on an internal proxy repo
+    if dry_run and not peer:
+        parsed_refs[ref] = None
+        return
+
     def getfilectx(repo, memctx, f):
         of = files[f]
         if 'deleted' in of:
@@ -603,14 +748,14 @@ def parse_commit(parser):
         extra['committer'] = "%s %u %u" % committer
 
     if from_mark:
-        p1 = repo.changelog.node(mark_to_rev(from_mark))
+        p1 = mark_to_rev(from_mark)
     else:
-        p1 = '\0' * 20
+        p1 = '0' * 40
 
     if merge_mark:
-        p2 = repo.changelog.node(mark_to_rev(merge_mark))
+        p2 = mark_to_rev(merge_mark)
     else:
-        p2 = '\0' * 20
+        p2 = '0' * 40
 
     #
     # If files changed from any of the parents, hg wants to know, but in git if
@@ -619,11 +764,16 @@ def parse_commit(parser):
     if merge_mark:
         get_merge_files(repo, p1, p2, files)
 
+    # Check if the ref is supposed to be a named branch
+    if ref.startswith('refs/heads/branches/'):
+        branch = ref[len('refs/heads/branches/'):]
+        extra['branch'] = hgref(branch)
+
     if mode == 'hg':
         i = data.find('\n--HG--\n')
         if i >= 0:
             tmp = data[i + len('\n--HG--\n'):].strip()
-            for k, v in [e.split(' : ') for e in tmp.split('\n')]:
+            for k, v in [e.split(' : ', 1) for e in tmp.split('\n')]:
                 if k == 'rename':
                     old, new = v.split(' => ', 1)
                     files[new]['rename'] = old
@@ -641,17 +791,16 @@ def parse_commit(parser):
     tmp = encoding.encoding
     encoding.encoding = 'utf-8'
 
-    node = repo.commitctx(ctx)
+    node = hghex(repo.commitctx(ctx))
 
     encoding.encoding = tmp
 
-    rev = repo[node].rev()
-
     parsed_refs[ref] = node
-
-    marks.new_mark(rev, commit_mark)
+    marks.new_mark(node, commit_mark)
 
 def parse_reset(parser):
+    global parsed_refs
+
     ref = parser[1]
     parser.next()
     # ugh
@@ -663,8 +812,11 @@ def parse_reset(parser):
     from_mark = parser.get_mark()
     parser.next()
 
-    node = parser.repo.changelog.node(mark_to_rev(from_mark))
-    parsed_refs[ref] = node
+    try:
+        rev = mark_to_rev(from_mark)
+    except KeyError:
+        rev = None
+    parsed_refs[ref] = rev
 
 def parse_tag(parser):
     name = parser[1]
@@ -676,11 +828,176 @@ def parse_tag(parser):
     data = parser.get_data()
     parser.next()
 
-    # nothing to do
+    parsed_tags[name] = (tagger, data)
+
+def write_tag(repo, tag, node, msg, author):
+    branch = repo[node].branch()
+    tip = branch_tip(branch)
+    tip = repo[tip]
+
+    def getfilectx(repo, memctx, f):
+        try:
+            fctx = tip.filectx(f)
+            data = fctx.data()
+        except error.ManifestLookupError:
+            data = ""
+        content = data + "%s %s\n" % (node, tag)
+        return context.memfilectx(f, content, False, False, None)
+
+    p1 = tip.hex()
+    p2 = '0' * 40
+    if author:
+        user, date, tz = author
+        date_tz = (date, tz)
+    else:
+        cmd = ['git', 'var', 'GIT_COMMITTER_IDENT']
+        process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+        output, _ = process.communicate()
+        m = re.match('^.* <.*>', output)
+        if m:
+            user = m.group(0)
+        else:
+            user = repo.ui.username()
+        date_tz = None
+
+    ctx = context.memctx(repo, (p1, p2), msg,
+            ['.hgtags'], getfilectx,
+            user, date_tz, {'branch' : branch})
+
+    tmp = encoding.encoding
+    encoding.encoding = 'utf-8'
+
+    tagnode = repo.commitctx(ctx)
+
+    encoding.encoding = tmp
+
+    return (tagnode, branch)
+
+def checkheads_bmark(repo, ref, ctx):
+    bmark = ref[len('refs/heads/'):]
+    if not bmark in bmarks:
+        # new bmark
+        return True
+
+    ctx_old = bmarks[bmark]
+    ctx_new = ctx
+    if not repo.changelog.descendant(ctx_old.rev(), ctx_new.rev()):
+        if force_push:
+            print "ok %s forced update" % ref
+        else:
+            print "error %s non-fast forward" % ref
+            return False
+
+    return True
+
+def checkheads(repo, remote, p_revs):
+
+    remotemap = remote.branchmap()
+    if not remotemap:
+        # empty repo
+        return True
+
+    new = {}
+    ret = True
+
+    for node, ref in p_revs.iteritems():
+        ctx = repo[node]
+        branch = ctx.branch()
+        if not branch in remotemap:
+            # new branch
+            continue
+        if not ref.startswith('refs/heads/branches'):
+            if ref.startswith('refs/heads/'):
+                if not checkheads_bmark(repo, ref, ctx):
+                    ret = False
+
+            # only check branches
+            continue
+        new.setdefault(branch, []).append(ctx.rev())
+
+    for branch, heads in new.iteritems():
+        old = [repo.changelog.rev(x) for x in remotemap[branch]]
+        for rev in heads:
+            if check_version(2, 3):
+                ancestors = repo.changelog.ancestors([rev], stoprev=min(old))
+            else:
+                ancestors = repo.changelog.ancestors(rev)
+            found = False
+
+            for x in old:
+                if x in ancestors:
+                    found = True
+                    break
+
+            if found:
+                continue
+
+            node = repo.changelog.node(rev)
+            ref = p_revs[node]
+            if force_push:
+                print "ok %s forced update" % ref
+            else:
+                print "error %s non-fast forward" % ref
+                ret = False
+
+    return ret
+
+def push_unsafe(repo, remote, parsed_refs, p_revs):
+
+    force = force_push
+
+    fci = discovery.findcommonincoming
+    commoninc = fci(repo, remote, force=force)
+    common, _, remoteheads = commoninc
+
+    if not checkheads(repo, remote, p_revs):
+        return None
+
+    cg = repo.getbundle('push', heads=list(p_revs), common=common)
+
+    unbundle = remote.capable('unbundle')
+    if unbundle:
+        if force:
+            remoteheads = ['force']
+        return remote.unbundle(cg, remoteheads, 'push')
+    else:
+        return remote.addchangegroup(cg, 'push', repo.url())
+
+def push(repo, remote, parsed_refs, p_revs):
+    if hasattr(remote, 'canpush') and not remote.canpush():
+        print "error cannot push"
+
+    if not p_revs:
+        # nothing to push
+        return
+
+    lock = None
+    unbundle = remote.capable('unbundle')
+    if not unbundle:
+        lock = remote.lock()
+    try:
+        ret = push_unsafe(repo, remote, parsed_refs, p_revs)
+    finally:
+        if lock is not None:
+            lock.release()
+
+    return ret
+
+def check_tip(ref, kind, name, heads):
+    try:
+        ename = '%s/%s' % (kind, name)
+        tip = marks.get_tip(ename)
+    except KeyError:
+        return True
+    else:
+        return tip in heads
 
 def do_export(parser):
     global parsed_refs, bmarks, peer
 
+    p_bmarks = []
+    p_revs = {}
+
     parser.next()
 
     for line in parser.each_block('done'):
@@ -697,58 +1014,139 @@ def do_export(parser):
         else:
             die('unhandled export command: %s' % line)
 
+    need_fetch = False
+
     for ref, node in parsed_refs.iteritems():
+        bnode = hgbin(node) if node else None
         if ref.startswith('refs/heads/branches'):
-            pass
+            branch = ref[len('refs/heads/branches/'):]
+            if branch in branches and bnode in branches[branch]:
+                # up to date
+                continue
+
+            if peer:
+                remotemap = peer.branchmap()
+                if remotemap and branch in remotemap:
+                    heads = [hghex(e) for e in remotemap[branch]]
+                    if not check_tip(ref, 'branches', branch, heads):
+                        print "error %s fetch first" % ref
+                        need_fetch = True
+                        continue
+
+            p_revs[bnode] = ref
+            print "ok %s" % ref
         elif ref.startswith('refs/heads/'):
             bmark = ref[len('refs/heads/'):]
-            if bmark in bmarks:
-                old = bmarks[bmark].hex()
-            else:
-                old = ''
-            if not bookmarks.pushbookmark(parser.repo, bmark, old, node):
+            new = node
+            old = bmarks[bmark].hex() if bmark in bmarks else ''
+
+            if old == new:
                 continue
+
+            print "ok %s" % ref
+            if bmark != fake_bmark and \
+                    not (bmark == 'master' and bmark not in parser.repo._bookmarks):
+                p_bmarks.append((ref, bmark, old, new))
+
+            if peer:
+                remote_old = peer.listkeys('bookmarks').get(bmark)
+                if remote_old:
+                    if not check_tip(ref, 'bookmarks', bmark, remote_old):
+                        print "error %s fetch first" % ref
+                        need_fetch = True
+                        continue
+
+            p_revs[bnode] = ref
         elif ref.startswith('refs/tags/'):
+            if dry_run:
+                print "ok %s" % ref
+                continue
             tag = ref[len('refs/tags/'):]
-            parser.repo.tag([tag], node, None, True, None, {})
+            tag = hgref(tag)
+            author, msg = parsed_tags.get(tag, (None, None))
+            if mode == 'git':
+                if not msg:
+                    msg = 'Added tag %s for changeset %s' % (tag, node[:12]);
+                tagnode, branch = write_tag(parser.repo, tag, node, msg, author)
+                p_revs[tagnode] = 'refs/heads/branches/' + gitref(branch)
+            else:
+                fp = parser.repo.opener('localtags', 'a')
+                fp.write('%s %s\n' % (node, tag))
+                fp.close()
+            p_revs[bnode] = ref
+            print "ok %s" % ref
         else:
             # transport-helper/fast-export bugs
             continue
-        print "ok %s" % ref
 
-    print
+    if need_fetch:
+        print
+        return
+
+    if dry_run:
+        if peer and not force_push:
+            checkheads(parser.repo, peer, p_revs)
+        print
+        return
 
     if peer:
-        parser.repo.push(peer, force=False)
+        if not push(parser.repo, peer, parsed_refs, p_revs):
+            # do not update bookmarks
+            print
+            return
+
+        # update remote bookmarks
+        remote_bmarks = peer.listkeys('bookmarks')
+        for ref, bmark, old, new in p_bmarks:
+            if force_push:
+                old = remote_bmarks.get(bmark, '')
+            if not peer.pushkey('bookmarks', bmark, old, new):
+                print "error %s" % ref
+    else:
+        # update local bookmarks
+        for ref, bmark, old, new in p_bmarks:
+            if not bookmarks.pushbookmark(parser.repo, bmark, old, new):
+                print "error %s" % ref
+
+    print
+
+def do_option(parser):
+    global dry_run, force_push
+    _, key, value = parser.line.split(' ')
+    if key == 'dry-run':
+        dry_run = (value == 'true')
+        print 'ok'
+    elif key == 'force':
+        force_push = (value == 'true')
+        print 'ok'
+    else:
+        print 'unsupported'
 
 def fix_path(alias, repo, orig_url):
-    repo_url = util.url(repo.url())
-    url = util.url(orig_url)
-    if str(url) == str(repo_url):
+    url = urlparse.urlparse(orig_url, 'file')
+    if url.scheme != 'file' or os.path.isabs(url.path):
         return
-    cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % repo_url]
+    abs_url = urlparse.urljoin("%s/" % os.getcwd(), orig_url)
+    cmd = ['git', 'config', 'remote.%s.url' % alias, "hg::%s" % abs_url]
     subprocess.call(cmd)
 
 def main(args):
-    global prefix, dirname, branches, bmarks
+    global prefix, gitdir, dirname, branches, bmarks
     global marks, blob_marks, parsed_refs
     global peer, mode, bad_mail, bad_name
-    global track_branches
+    global track_branches, force_push, is_tmp
+    global parsed_tags
+    global filenodes
+    global fake_bmark, hg_version
+    global dry_run
 
     alias = args[1]
     url = args[2]
     peer = None
 
-    hg_git_compat = False
-    track_branches = True
-    try:
-        if get_config('remote-hg.hg-git-compat') == 'true\n':
-            hg_git_compat = True
-            track_branches = False
-        if get_config('remote-hg.track-branches') == 'false\n':
-            track_branches = False
-    except subprocess.CalledProcessError:
-        pass
+    hg_git_compat = get_config_bool('remote-hg.hg-git-compat')
+    track_branches = get_config_bool('remote-hg.track-branches', True)
+    force_push = False
 
     if hg_git_compat:
         mode = 'hg'
@@ -761,7 +1159,7 @@ def main(args):
 
     if alias[4:] == url:
         is_tmp = True
-        alias = util.sha1(alias).hexdigest()
+        alias = hashlib.sha1(alias).hexdigest()
     else:
         is_tmp = False
 
@@ -771,6 +1169,15 @@ def main(args):
     bmarks = {}
     blob_marks = {}
     parsed_refs = {}
+    marks = None
+    parsed_tags = {}
+    filenodes = {}
+    fake_bmark = None
+    try:
+        hg_version = tuple(int(e) for e in util.version().split('.'))
+    except:
+        hg_version = None
+    dry_run = False
 
     repo = get_repo(url, alias)
     prefix = 'refs/hg/%s' % alias
@@ -778,11 +1185,12 @@ def main(args):
     if not is_tmp:
         fix_path(alias, peer or repo, url)
 
-    if not os.path.exists(dirname):
-        os.makedirs(dirname)
-
     marks_path = os.path.join(dirname, 'marks-hg')
-    marks = Marks(marks_path)
+    marks = Marks(marks_path, repo)
+
+    if sys.platform == 'win32':
+        import msvcrt
+        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
 
     parser = Parser(repo)
     for line in parser:
@@ -794,13 +1202,19 @@ def main(args):
             do_import(parser)
         elif parser.check('export'):
             do_export(parser)
+        elif parser.check('option'):
+            do_option(parser)
         else:
             die('unhandled command: %s' % line)
         sys.stdout.flush()
 
+def bye():
+    if not marks:
+        return
     if not is_tmp:
         marks.store()
     else:
         shutil.rmtree(dirname)
 
+atexit.register(bye)
 sys.exit(main(sys.argv))
index 34666e1d0f60813abf65fa231dc13f2b4c080440..dce281f91195cde4937b935910ec73f597bb826a 100755 (executable)
@@ -12,86 +12,90 @@ if ! test_have_prereq PYTHON; then
        test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import bzrlib'; then
+if ! python -c 'import bzrlib'; then
        skip_all='skipping remote-bzr tests; bzr not available'
        test_done
 fi
 
 check () {
-       (cd $1 &&
-       git log --format='%s' -1 &&
-       git symbolic-ref HEAD) > actual &&
-       (echo $2 &&
-       echo "refs/heads/$3") > expected &&
+       echo $3 > expected &&
+       git --git-dir=$1/.git log --format='%s' -1 $2 > actual
        test_cmp expected actual
 }
 
 bzr whoami "A U Thor <author@example.com>"
 
 test_expect_success 'cloning' '
-  (bzr init bzrrepo &&
-  cd bzrrepo &&
-  echo one > content &&
-  bzr add content &&
-  bzr commit -m one
-  ) &&
-
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
-  check gitrepo one master
+       (
+       bzr init bzrrepo &&
+       cd bzrrepo &&
+       echo one > content &&
+       bzr add content &&
+       bzr commit -m one
+       ) &&
+
+       git clone "bzr::bzrrepo" gitrepo &&
+       check gitrepo HEAD one
 '
 
 test_expect_success 'pulling' '
-  (cd bzrrepo &&
-  echo two > content &&
-  bzr commit -m two
-  ) &&
+       (
+       cd bzrrepo &&
+       echo two > content &&
+       bzr commit -m two
+       ) &&
 
-  (cd gitrepo && git pull) &&
+       (cd gitrepo && git pull) &&
 
-  check gitrepo two master
+       check gitrepo HEAD two
 '
 
 test_expect_success 'pushing' '
-  (cd gitrepo &&
-  echo three > content &&
-  git commit -a -m three &&
-  git push
-  ) &&
-
-  echo three > expected &&
-  cat bzrrepo/content > actual &&
-  test_cmp expected actual
+       (
+       cd gitrepo &&
+       echo three > content &&
+       git commit -a -m three &&
+       git push
+       ) &&
+
+       echo three > expected &&
+       cat bzrrepo/content > actual &&
+       test_cmp expected actual
 '
 
 test_expect_success 'roundtrip' '
-  (cd gitrepo &&
-  git pull &&
-  git log --format="%s" -1 origin/master > actual) &&
-  echo three > expected &&
-  test_cmp expected actual &&
+       (
+       cd gitrepo &&
+       git pull &&
+       git log --format="%s" -1 origin/master > actual
+       ) &&
+       echo three > expected &&
+       test_cmp expected actual &&
 
-  (cd gitrepo && git push && git pull) &&
+       (cd gitrepo && git push && git pull) &&
 
-  (cd bzrrepo &&
-  echo four > content &&
-  bzr commit -m four
-  ) &&
+       (
+       cd bzrrepo &&
+       echo four > content &&
+       bzr commit -m four
+       ) &&
 
-  (cd gitrepo && git pull && git push) &&
+       (cd gitrepo && git pull && git push) &&
 
-  check gitrepo four master &&
+       check gitrepo HEAD four &&
 
-  (cd gitrepo &&
-  echo five > content &&
-  git commit -a -m five &&
-  git push && git pull
-  ) &&
+       (
+       cd gitrepo &&
+       echo five > content &&
+       git commit -a -m five &&
+       git push && git pull
+       ) &&
 
-  (cd bzrrepo && bzr revert) &&
+       (cd bzrrepo && bzr revert) &&
 
-  echo five > expected &&
-  cat bzrrepo/content > actual &&
-  test_cmp expected actual
+       echo five > expected &&
+       cat bzrrepo/content > actual &&
+       test_cmp expected actual
 '
 
 cat > expected <<EOF
@@ -101,29 +105,35 @@ cat > expected <<EOF
 EOF
 
 test_expect_success 'special modes' '
-  (cd bzrrepo &&
-  echo exec > executable
-  chmod +x executable &&
-  bzr add executable
-  bzr commit -m exec &&
-  ln -s content link
-  bzr add link
-  bzr commit -m link &&
-  mkdir dir &&
-  bzr add dir &&
-  bzr commit -m dir) &&
-
-  (cd gitrepo &&
-  git pull
-  git ls-tree HEAD > ../actual) &&
-
-  test_cmp expected actual &&
-
-  (cd gitrepo &&
-  git cat-file -p HEAD:link > ../actual) &&
-
-  printf content > expected &&
-  test_cmp expected actual
+       (
+       cd bzrrepo &&
+       echo exec > executable
+       chmod +x executable &&
+       bzr add executable
+       bzr commit -m exec &&
+       ln -s content link
+       bzr add link
+       bzr commit -m link &&
+       mkdir dir &&
+       bzr add dir &&
+       bzr commit -m dir
+       ) &&
+
+       (
+       cd gitrepo &&
+       git pull
+       git ls-tree HEAD > ../actual
+       ) &&
+
+       test_cmp expected actual &&
+
+       (
+       cd gitrepo &&
+       git cat-file -p HEAD:link > ../actual
+       ) &&
+
+       printf content > expected &&
+       test_cmp expected actual
 '
 
 cat > expected <<EOF
@@ -134,98 +144,218 @@ cat > expected <<EOF
 EOF
 
 test_expect_success 'moving directory' '
-  (cd bzrrepo &&
-  mkdir movedir &&
-  echo one > movedir/one &&
-  echo two > movedir/two &&
-  bzr add movedir &&
-  bzr commit -m movedir &&
-  bzr mv movedir movedir-new &&
-  bzr commit -m movedir-new) &&
-
-  (cd gitrepo &&
-  git pull &&
-  git ls-tree HEAD > ../actual) &&
-
-  test_cmp expected actual
+       (
+       cd bzrrepo &&
+       mkdir movedir &&
+       echo one > movedir/one &&
+       echo two > movedir/two &&
+       bzr add movedir &&
+       bzr commit -m movedir &&
+       bzr mv movedir movedir-new &&
+       bzr commit -m movedir-new
+       ) &&
+
+       (
+       cd gitrepo &&
+       git pull &&
+       git ls-tree HEAD > ../actual
+       ) &&
+
+       test_cmp expected actual
 '
 
 test_expect_success 'different authors' '
-  (cd bzrrepo &&
-  echo john >> content &&
-  bzr commit -m john \
-    --author "Jane Rey <jrey@example.com>" \
-    --author "John Doe <jdoe@example.com>") &&
-
-  (cd gitrepo &&
-  git pull &&
-  git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual) &&
-
-  echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected &&
-  test_cmp expected actual
+       (
+       cd bzrrepo &&
+       echo john >> content &&
+       bzr commit -m john \
+         --author "Jane Rey <jrey@example.com>" \
+         --author "John Doe <jdoe@example.com>"
+       ) &&
+
+       (
+       cd gitrepo &&
+       git pull &&
+       git show --format="%an <%ae>, %cn <%ce>" --quiet > ../actual
+       ) &&
+
+       echo "Jane Rey <jrey@example.com>, A U Thor <author@example.com>" > expected &&
+       test_cmp expected actual
 '
 
+# cleanup previous stuff
+rm -rf bzrrepo gitrepo
+
 test_expect_success 'fetch utf-8 filenames' '
-  mkdir -p tmp && cd tmp &&
-  test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" &&
-
-  LC_ALL=en_US.UTF-8
-  export LC_ALL
-  (
-  bzr init bzrrepo &&
-  cd bzrrepo &&
-
-  echo test >> "ærø" &&
-  bzr add "ærø" &&
-  echo test >> "ø~?" &&
-  bzr add "ø~?" &&
-  bzr commit -m add-utf-8 &&
-  echo test >> "ærø" &&
-  bzr commit -m test-utf-8 &&
-  bzr rm "ø~?" &&
-  bzr mv "ærø" "ø~?" &&
-  bzr commit -m bzr-mv-utf-8
-  ) &&
-
-  (
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
-  cd gitrepo &&
-  git -c core.quotepath=false ls-files > ../actual
-  ) &&
-  echo "ø~?" > expected &&
-  test_cmp expected actual
+       test_when_finished "rm -rf bzrrepo gitrepo && LC_ALL=C" &&
+
+       LC_ALL=en_US.UTF-8
+       export LC_ALL
+
+       (
+       bzr init bzrrepo &&
+       cd bzrrepo &&
+
+       echo test >> "ærø" &&
+       bzr add "ærø" &&
+       echo test >> "ø~?" &&
+       bzr add "ø~?" &&
+       bzr commit -m add-utf-8 &&
+       echo test >> "ærø" &&
+       bzr commit -m test-utf-8 &&
+       bzr rm "ø~?" &&
+       bzr mv "ærø" "ø~?" &&
+       bzr commit -m bzr-mv-utf-8
+       ) &&
+
+       (
+       git clone "bzr::bzrrepo" gitrepo &&
+       cd gitrepo &&
+       git -c core.quotepath=false ls-files > ../actual
+       ) &&
+       echo "ø~?" > expected &&
+       test_cmp expected actual
 '
 
 test_expect_success 'push utf-8 filenames' '
-  mkdir -p tmp && cd tmp &&
-  test_when_finished "cd .. && rm -rf tmp && LC_ALL=C" &&
+       test_when_finished "rm -rf bzrrepo gitrepo && LC_ALL=C" &&
+
+       mkdir -p tmp && cd tmp &&
+
+       LC_ALL=en_US.UTF-8
+       export LC_ALL
+
+       (
+       bzr init bzrrepo &&
+       cd bzrrepo &&
+
+       echo one >> content &&
+       bzr add content &&
+       bzr commit -m one
+       ) &&
+
+       (
+       git clone "bzr::bzrrepo" gitrepo &&
+       cd gitrepo &&
+
+       echo test >> "ærø" &&
+       git add "ærø" &&
+       git commit -m utf-8 &&
+
+       git push
+       ) &&
+
+       (cd bzrrepo && bzr ls > ../actual) &&
+       printf "content\nærø\n" > expected &&
+       test_cmp expected actual
+'
 
-  LC_ALL=en_US.UTF-8
-  export LC_ALL
+test_expect_success 'pushing a merge' '
+       test_when_finished "rm -rf bzrrepo gitrepo" &&
+
+       (
+       bzr init bzrrepo &&
+       cd bzrrepo &&
+       echo one > content &&
+       bzr add content &&
+       bzr commit -m one
+       ) &&
+
+       git clone "bzr::bzrrepo" gitrepo &&
+
+       (
+       cd bzrrepo &&
+       echo two > content &&
+       bzr commit -m two
+       ) &&
+
+       (
+       cd gitrepo &&
+       echo three > content &&
+       git commit -a -m three &&
+       git fetch &&
+       git merge origin/master || true &&
+       echo three > content &&
+       git commit -a --no-edit &&
+       git push
+       ) &&
+
+       echo three > expected &&
+       cat bzrrepo/content > actual &&
+       test_cmp expected actual
+'
+
+cat > expected <<EOF
+origin/HEAD
+origin/branch
+origin/trunk
+EOF
+
+test_expect_success 'proper bzr repo' '
+       test_when_finished "rm -rf bzrrepo gitrepo" &&
+
+       bzr init-repo bzrrepo &&
+
+       (
+       bzr init bzrrepo/trunk &&
+       cd bzrrepo/trunk &&
+       echo one >> content &&
+       bzr add content &&
+       bzr commit -m one
+       ) &&
+
+       (
+       bzr branch bzrrepo/trunk bzrrepo/branch &&
+       cd bzrrepo/branch &&
+       echo two >> content &&
+       bzr commit -m one
+       ) &&
 
-  (
-  bzr init bzrrepo &&
-  cd bzrrepo &&
+       (
+       git clone "bzr::bzrrepo" gitrepo &&
+       cd gitrepo &&
+       git for-each-ref --format "%(refname:short)" refs/remotes/origin > ../actual
+       ) &&
 
-  echo one >> content &&
-  bzr add content &&
-  bzr commit -m one
-  ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'strip' '
+       test_when_finished "rm -rf bzrrepo gitrepo" &&
+
+       (
+       bzr init bzrrepo &&
+       cd bzrrepo &&
+
+       echo one >> content &&
+       bzr add content &&
+       bzr commit -m one &&
 
-  (
-  git clone "bzr::$PWD/bzrrepo" gitrepo &&
-  cd gitrepo &&
+       echo two >> content &&
+       bzr commit -m two
+       ) &&
 
-  echo test >> "ærø" &&
-  git add "ærø" &&
-  git commit -m utf-8 &&
+       git clone "bzr::bzrrepo" gitrepo &&
 
-  git push
-  ) &&
+       (
+       cd bzrrepo &&
+       bzr uncommit --force &&
 
-  (cd bzrrepo && bzr ls > ../actual) &&
-  printf "content\nærø\n" > expected &&
-  test_cmp expected actual
+       echo three >> content &&
+       bzr commit -m three &&
+
+       echo four >> content &&
+       bzr commit -m four &&
+       bzr log --line | sed -e "s/^[0-9][0-9]*: //" > ../expected
+       ) &&
+
+       (
+       cd gitrepo &&
+       git fetch &&
+       git log --format="%an %ad %s" --date=short origin/master > ../actual
+       ) &&
+
+       test_cmp expected actual
 '
 
 test_done
index 1d6198243667c655639781019d8d608e0791e00b..f83d67d74fc97cb597388ab556d7a31e9d01835b 100755 (executable)
@@ -15,15 +15,14 @@ if ! test_have_prereq PYTHON; then
        test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import mercurial'; then
+if ! python -c 'import mercurial'; then
        skip_all='skipping remote-hg tests; mercurial not available'
        test_done
 fi
 
 # clone to a git repo
 git_clone () {
-       hg -R $1 bookmark -f -r tip master &&
-       git clone -q "hg::$PWD/$1" $2
+       git clone -q "hg::$1" $2
 }
 
 # clone to an hg repo
@@ -31,7 +30,7 @@ hg_clone () {
        (
        hg init $2 &&
        cd $1 &&
-       git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
+       git push -q "hg::../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
        ) &&
 
        (cd $2 && hg -q update)
@@ -41,16 +40,15 @@ hg_clone () {
 hg_push () {
        (
        cd $2
-       old=$(git symbolic-ref --short HEAD)
        git checkout -q -b tmp &&
-       git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
-       git checkout -q $old &&
+       git fetch -q "hg::../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
+       git checkout -q @{-1} &&
        git branch -q -D tmp 2> /dev/null || true
        )
 }
 
 hg_log () {
-       hg -R $1 log --graph --debug | grep -v 'tag: *default/'
+       hg -R $1 log --graph --debug
 }
 
 setup () {
@@ -62,20 +60,22 @@ setup () {
        echo "commit = -d \"0 0\""
        echo "debugrawcommit = -d \"0 0\""
        echo "tag = -d \"0 0\""
+       echo "[extensions]"
+       echo "graphlog ="
        ) >> "$HOME"/.hgrc &&
        git config --global remote-hg.hg-git-compat true
+       git config --global remote-hg.track-branches true
 
-       export HGEDITOR=/usr/bin/true
-
-       export GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
-       export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+       HGEDITOR=/usr/bin/true
+       GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
+       GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+       export HGEDITOR GIT_AUTHOR_DATE GIT_COMMITTER_DATE
 }
 
 setup
 
 test_expect_success 'encoding' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -85,7 +85,8 @@ test_expect_success 'encoding' '
        git add alpha &&
        git commit -m "add älphà" &&
 
-       export GIT_AUTHOR_NAME="tést èncödîng" &&
+       GIT_AUTHOR_NAME="tést èncödîng" &&
+       export GIT_AUTHOR_NAME &&
        echo beta > beta &&
        git add beta &&
        git commit -m "add beta" &&
@@ -111,8 +112,7 @@ test_expect_success 'encoding' '
 '
 
 test_expect_success 'file removal' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -144,8 +144,7 @@ test_expect_success 'file removal' '
 '
 
 test_expect_success 'git tags' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -173,8 +172,7 @@ test_expect_success 'git tags' '
 '
 
 test_expect_success 'hg branch' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -190,7 +188,7 @@ test_expect_success 'hg branch' '
        hg_clone gitrepo hgrepo &&
 
        cd hgrepo &&
-       hg -q co master &&
+       hg -q co default &&
        hg mv alpha beta &&
        hg -q commit -m "rename alpha to beta" &&
        hg branch gamma | grep -v "permanent and global" &&
@@ -200,8 +198,8 @@ test_expect_success 'hg branch' '
        hg_push hgrepo gitrepo &&
        hg_clone gitrepo hgrepo2 &&
 
-       : TODO, avoid "master" bookmark &&
-       (cd hgrepo2 && hg checkout gamma) &&
+       : Back to the common revision &&
+       (cd hgrepo && hg checkout default) &&
 
        hg_log hgrepo > expected &&
        hg_log hgrepo2 > actual &&
@@ -210,8 +208,7 @@ test_expect_success 'hg branch' '
 '
 
 test_expect_success 'hg tags' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -227,7 +224,7 @@ test_expect_success 'hg tags' '
        hg_clone gitrepo hgrepo &&
 
        cd hgrepo &&
-       hg co master &&
+       hg co default &&
        hg tag alpha
        ) &&
 
index 3f253b7de75a6fa44c4cff3b2e1f53f9feffcf5d..2219284382f0e987423ae00e5bd89e9b7a4b0046 100755 (executable)
@@ -15,28 +15,29 @@ if ! test_have_prereq PYTHON; then
        test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import mercurial'; then
+if ! python -c 'import mercurial'; then
        skip_all='skipping remote-hg tests; mercurial not available'
        test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import hggit'; then
+if ! python -c 'import hggit'; then
        skip_all='skipping remote-hg tests; hg-git not available'
        test_done
 fi
 
 # clone to a git repo with git
 git_clone_git () {
-       hg -R $1 bookmark -f -r tip master &&
-       git clone -q "hg::$PWD/$1" $2
+       git clone -q "hg::$1" $2 &&
+       (cd $2 && git checkout master && git branch -D default)
 }
 
 # clone to an hg repo with git
 hg_clone_git () {
        (
        hg init $2 &&
+       hg -R $2 bookmark -i master &&
        cd $1 &&
-       git push -q "hg::$PWD/../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
+       git push -q "hg::../$2" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*'
        ) &&
 
        (cd $2 && hg -q update)
@@ -47,7 +48,7 @@ git_clone_hg () {
        (
        git init -q $2 &&
        cd $1 &&
-       hg bookmark -f -r tip master &&
+       hg bookmark -i -f -r tip master &&
        hg -q push -r master ../$2 || true
        )
 }
@@ -61,10 +62,10 @@ hg_clone_hg () {
 hg_push_git () {
        (
        cd $2
-       old=$(git symbolic-ref --short HEAD)
        git checkout -q -b tmp &&
-       git fetch -q "hg::$PWD/../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
-       git checkout -q $old &&
+       git fetch -q "hg::../$1" 'refs/tags/*:refs/tags/*' 'refs/heads/*:refs/heads/*' &&
+       git branch -D default &&
+       git checkout -q @{-1} &&
        git branch -q -D tmp 2> /dev/null || true
        )
 }
@@ -78,7 +79,8 @@ hg_push_hg () {
 }
 
 hg_log () {
-       hg -R $1 log --graph --debug | grep -v 'tag: *default/'
+       hg -R $1 log --graph --debug >log &&
+       grep -v 'tag: *default/' log
 }
 
 git_log () {
@@ -97,21 +99,24 @@ setup () {
        echo "[extensions]"
        echo "hgext.bookmarks ="
        echo "hggit ="
+       echo "graphlog ="
        ) >> "$HOME"/.hgrc &&
        git config --global receive.denycurrentbranch warn
        git config --global remote-hg.hg-git-compat true
+       git config --global remote-hg.track-branches false
 
-       export HGEDITOR=/usr/bin/true
+       HGEDITOR=true
+       HGMERGE=true
 
-       export GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
-       export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+       GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230"
+       GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
+       export HGEDITOR HGMERGE GIT_AUTHOR_DATE GIT_COMMITTER_DATE
 }
 
 setup
 
 test_expect_success 'executable bit' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -146,8 +151,7 @@ test_expect_success 'executable bit' '
 '
 
 test_expect_success 'symlink' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -177,8 +181,7 @@ test_expect_success 'symlink' '
 '
 
 test_expect_success 'merge conflict 1' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        hg init hgrepo1 &&
@@ -194,7 +197,7 @@ test_expect_success 'merge conflict 1' '
        echo C > afile &&
        hg ci -m "A->C" &&
 
-       hg merge -r1 || true &&
+       hg merge -r1 &&
        echo C > afile &&
        hg resolve -m afile &&
        hg ci -m "merge to C"
@@ -212,8 +215,7 @@ test_expect_success 'merge conflict 1' '
 '
 
 test_expect_success 'merge conflict 2' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        hg init hgrepo1 &&
@@ -247,8 +249,7 @@ test_expect_success 'merge conflict 2' '
 '
 
 test_expect_success 'converged merge' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        hg init hgrepo1 &&
@@ -283,8 +284,7 @@ test_expect_success 'converged merge' '
 '
 
 test_expect_success 'encoding' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -294,7 +294,8 @@ test_expect_success 'encoding' '
        git add alpha &&
        git commit -m "add älphà" &&
 
-       export GIT_AUTHOR_NAME="tést èncödîng" &&
+       GIT_AUTHOR_NAME="tést èncödîng" &&
+       export GIT_AUTHOR_NAME &&
        echo beta > beta &&
        git add beta &&
        git commit -m "add beta" &&
@@ -322,8 +323,7 @@ test_expect_success 'encoding' '
 '
 
 test_expect_success 'file removal' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -362,8 +362,7 @@ test_expect_success 'file removal' '
 '
 
 test_expect_success 'git tags' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        (
        git init -q gitrepo &&
@@ -389,8 +388,7 @@ test_expect_success 'git tags' '
 '
 
 test_expect_success 'hg author' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        for x in hg git; do
                (
@@ -451,15 +449,12 @@ test_expect_success 'hg author' '
                git_log gitrepo-$x > git-log-$x
        done &&
 
-       test_cmp git-log-hg git-log-git &&
-
        test_cmp hg-log-hg hg-log-git &&
        test_cmp git-log-hg git-log-git
 '
 
 test_expect_success 'hg branch' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        for x in hg git; do
                (
@@ -495,8 +490,7 @@ test_expect_success 'hg branch' '
 '
 
 test_expect_success 'hg tags' '
-       mkdir -p tmp && cd tmp &&
-       test_when_finished "cd .. && rm -rf tmp" &&
+       test_when_finished "rm -rf gitrepo* hgrepo*" &&
 
        for x in hg git; do
                (
index 7bb81f2f8e0e288eb7e392075491fc1df0bd91d1..f7ce8aa853d5ad9689f49a9cadf0d43ce16ed34f 100755 (executable)
@@ -15,107 +15,678 @@ if ! test_have_prereq PYTHON; then
        test_done
 fi
 
-if ! "$PYTHON_PATH" -c 'import mercurial'; then
+if ! python -c 'import mercurial'; then
        skip_all='skipping remote-hg tests; mercurial not available'
        test_done
 fi
 
 check () {
-       (cd $1 &&
-       git log --format='%s' -1 &&
-       git symbolic-ref HEAD) > actual &&
-       (echo $2 &&
-       echo "refs/heads/$3") > expected &&
+       echo $3 > expected &&
+       git --git-dir=$1/.git log --format='%s' -1 $2 > actual
        test_cmp expected actual
 }
 
+check_branch () {
+       if [ -n "$3" ]; then
+               echo $3 > expected &&
+               hg -R $1 log -r $2 --template '{desc}\n' > actual &&
+               test_cmp expected actual
+       else
+               hg -R $1 branches > out &&
+               ! grep $2 out
+       fi
+}
+
+check_bookmark () {
+       if [ -n "$3" ]; then
+               echo $3 > expected &&
+               hg -R $1 log -r "bookmark('$2')" --template '{desc}\n' > actual &&
+               test_cmp expected actual
+       else
+               hg -R $1 bookmarks > out &&
+               ! grep $2 out
+       fi
+}
+
+check_push () {
+       local expected_ret=$1 ret=0 ref_ret=0 IFS=':'
+
+       shift
+       git push origin "$@" 2> error
+       ret=$?
+       cat error
+
+       while read branch kind
+       do
+               case "$kind" in
+               'new')
+                       grep "^ \* \[new branch\] *${branch} -> ${branch}$" error || ref_ret=1
+                       ;;
+               'non-fast-forward')
+                       grep "^ ! \[rejected\] *${branch} -> ${branch} (non-fast-forward)$" error || ref_ret=1
+                       ;;
+               'fetch-first')
+                       grep "^ ! \[rejected\] *${branch} -> ${branch} (fetch first)$" error || ref_ret=1
+                       ;;
+               'forced-update')
+                       grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *${branch} -> ${branch} (forced update)$" error || ref_ret=1
+                       ;;
+               '')
+                       grep "^   [a-f0-9]*\.\.[a-f0-9]* *${branch} -> ${branch}$" error || ref_ret=1
+                       ;;
+               esac
+               let 'ref_ret' && echo "match for '$branch' failed" && break
+       done
+
+       if let 'expected_ret != ret || ref_ret'
+       then
+               return 1
+       fi
+
+       return 0
+}
+
 setup () {
        (
        echo "[ui]"
        echo "username = H G Wells <wells@example.com>"
-       ) >> "$HOME"/.hgrc
+       echo "[extensions]"
+       echo "mq ="
+       ) >> "$HOME"/.hgrc &&
+
+       GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0230" &&
+       GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" &&
+       export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
 setup
 
 test_expect_success 'cloning' '
-  test_when_finished "rm -rf gitrepo*" &&
+       test_when_finished "rm -rf gitrepo*" &&
 
-  (
-  hg init hgrepo &&
-  cd hgrepo &&
-  echo zero > content &&
-  hg add content &&
-  hg commit -m zero
-  ) &&
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+       echo zero > content &&
+       hg add content &&
+       hg commit -m zero
+       ) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  check gitrepo zero master
+       git clone "hg::hgrepo" gitrepo &&
+       check gitrepo HEAD zero
 '
 
 test_expect_success 'cloning with branches' '
-  test_when_finished "rm -rf gitrepo*" &&
+       test_when_finished "rm -rf gitrepo*" &&
 
-  (
-  cd hgrepo &&
-  hg branch next &&
-  echo next > content &&
-  hg commit -m next
-  ) &&
+       (
+       cd hgrepo &&
+       hg branch next &&
+       echo next > content &&
+       hg commit -m next
+       ) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  check gitrepo next next &&
+       git clone "hg::hgrepo" gitrepo &&
+       check gitrepo origin/branches/next next
+'
+
+test_expect_success 'cloning with bookmarks' '
+       test_when_finished "rm -rf gitrepo*" &&
 
-  (cd hgrepo && hg checkout default) &&
+       (
+       cd hgrepo &&
+       hg checkout default &&
+       hg bookmark feature-a &&
+       echo feature-a > content &&
+       hg commit -m feature-a
+       ) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo2 &&
-  check gitrepo2 zero master
+       git clone "hg::hgrepo" gitrepo &&
+       check gitrepo origin/feature-a feature-a
 '
 
-test_expect_success 'cloning with bookmarks' '
-  test_when_finished "rm -rf gitrepo*" &&
+test_expect_success 'update bookmark' '
+       test_when_finished "rm -rf gitrepo*" &&
 
-  (
-  cd hgrepo &&
-  hg bookmark feature-a &&
-  echo feature-a > content &&
-  hg commit -m feature-a
-  ) &&
+       (
+       cd hgrepo &&
+       hg bookmark devel
+       ) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  check gitrepo feature-a feature-a
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git checkout --quiet devel &&
+       echo devel > content &&
+       git commit -a -m devel &&
+       git push --quiet
+       ) &&
+
+       check_bookmark hgrepo devel devel
 '
 
-test_expect_success 'cloning with detached head' '
-  test_when_finished "rm -rf gitrepo*" &&
+test_expect_success 'new bookmark' '
+       test_when_finished "rm -rf gitrepo*" &&
 
-  (
-  cd hgrepo &&
-  hg update -r 0
-  ) &&
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git checkout --quiet -b feature-b &&
+       echo feature-b > content &&
+       git commit -a -m feature-b &&
+       git push --quiet origin feature-b
+       ) &&
 
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  check gitrepo zero master
+       check_bookmark hgrepo feature-b feature-b
 '
 
-test_expect_success 'update bookmark' '
-  test_when_finished "rm -rf gitrepo*" &&
-
-  (
-  cd hgrepo &&
-  hg bookmark devel
-  ) &&
-
-  (
-  git clone "hg::$PWD/hgrepo" gitrepo &&
-  cd gitrepo &&
-  git checkout devel &&
-  echo devel > content &&
-  git commit -a -m devel &&
-  git push
-  ) &&
-
-  hg -R hgrepo bookmarks | egrep "devel[        ]+3:"
+# cleanup previous stuff
+rm -rf hgrepo
+
+author_test () {
+       echo $1 >> content &&
+       hg commit -u "$2" -m "add $1" &&
+       echo "$3" >> ../expected
+}
+
+test_expect_success 'authors' '
+       test_when_finished "rm -rf hgrepo gitrepo" &&
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+
+       touch content &&
+       hg add content &&
+
+       > ../expected &&
+       author_test alpha "" "H G Wells <wells@example.com>" &&
+       author_test beta "test" "test <unknown>" &&
+       author_test beta "test <test@example.com> (comment)" "test <test@example.com>" &&
+       author_test gamma "<test@example.com>" "Unknown <test@example.com>" &&
+       author_test delta "name<test@example.com>" "name <test@example.com>" &&
+       author_test epsilon "name <test@example.com" "name <test@example.com>" &&
+       author_test zeta " test " "test <unknown>" &&
+       author_test eta "test < test@example.com >" "test <test@example.com>" &&
+       author_test theta "test >test@example.com>" "test <test@example.com>" &&
+       author_test iota "test < test <at> example <dot> com>" "test <unknown>" &&
+       author_test kappa "test@example.com" "Unknown <test@example.com>"
+       ) &&
+
+       git clone "hg::hgrepo" gitrepo &&
+       git --git-dir=gitrepo/.git log --reverse --format="%an <%ae>" > actual &&
+
+       test_cmp expected actual
+'
+
+test_expect_success 'strip' '
+       test_when_finished "rm -rf hgrepo gitrepo" &&
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+
+       echo one >> content &&
+       hg add content &&
+       hg commit -m one &&
+
+       echo two >> content &&
+       hg commit -m two
+       ) &&
+
+       git clone "hg::hgrepo" gitrepo &&
+
+       (
+       cd hgrepo &&
+       hg strip 1 &&
+
+       echo three >> content &&
+       hg commit -m three &&
+
+       echo four >> content &&
+       hg commit -m four
+       ) &&
+
+       (
+       cd gitrepo &&
+       git fetch &&
+       git log --format="%s" origin/master > ../actual
+       ) &&
+
+       hg -R hgrepo log --template "{desc}\n" > expected &&
+       test_cmp actual expected
+'
+
+test_expect_success 'remote push with master bookmark' '
+       test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+       echo zero > content &&
+       hg add content &&
+       hg commit -m zero &&
+       hg bookmark master &&
+       echo one > content &&
+       hg commit -m one
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       echo two > content &&
+       git commit -a -m two &&
+       git push
+       ) &&
+
+       check_branch hgrepo default two
+'
+
+cat > expected <<EOF
+changeset:   0:6e2126489d3d
+tag:         tip
+user:        A U Thor <author@example.com>
+date:        Mon Jan 01 00:00:00 2007 +0230
+summary:     one
+
+EOF
+
+test_expect_success 'remote push from master branch' '
+       test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+       hg init hgrepo &&
+
+       (
+       git init gitrepo &&
+       cd gitrepo &&
+       git remote add origin "hg::../hgrepo" &&
+       echo one > content &&
+       git add content &&
+       git commit -a -m one &&
+       git push origin master
+       ) &&
+
+       hg -R hgrepo log > actual &&
+       cat actual &&
+       test_cmp expected actual &&
+
+       check_branch hgrepo default one
+'
+
+GIT_REMOTE_HG_TEST_REMOTE=1
+export GIT_REMOTE_HG_TEST_REMOTE
+
+test_expect_success 'remote cloning' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+       echo zero > content &&
+       hg add content &&
+       hg commit -m zero
+       ) &&
+
+       git clone "hg::hgrepo" gitrepo &&
+       check gitrepo HEAD zero
+'
+
+test_expect_success 'remote update bookmark' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       (
+       cd hgrepo &&
+       hg bookmark devel
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git checkout --quiet devel &&
+       echo devel > content &&
+       git commit -a -m devel &&
+       git push --quiet
+       ) &&
+
+       check_bookmark hgrepo devel devel
+'
+
+test_expect_success 'remote new bookmark' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git checkout --quiet -b feature-b &&
+       echo feature-b > content &&
+       git commit -a -m feature-b &&
+       git push --quiet origin feature-b
+       ) &&
+
+       check_bookmark hgrepo feature-b feature-b
+'
+
+test_expect_success 'remote push diverged' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       git clone "hg::hgrepo" gitrepo &&
+
+       (
+       cd hgrepo &&
+       hg checkout default &&
+       echo bump > content &&
+       hg commit -m bump
+       ) &&
+
+       (
+       cd gitrepo &&
+       echo diverge > content &&
+       git commit -a -m diverged &&
+       check_push 1 <<-EOF
+       master:non-fast-forward
+       EOF
+       ) &&
+
+       check_branch hgrepo default bump
+'
+
+test_expect_success 'remote update bookmark diverge' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       (
+       cd hgrepo &&
+       hg checkout tip^ &&
+       hg bookmark diverge
+       ) &&
+
+       git clone "hg::hgrepo" gitrepo &&
+
+       (
+       cd hgrepo &&
+       echo "bump bookmark" > content &&
+       hg commit -m "bump bookmark"
+       ) &&
+
+       (
+       cd gitrepo &&
+       git checkout --quiet diverge &&
+       echo diverge > content &&
+       git commit -a -m diverge &&
+       check_push 1 <<-EOF
+       diverge:fetch-first
+       EOF
+       ) &&
+
+       check_bookmark hgrepo diverge "bump bookmark"
+'
+
+test_expect_success 'remote new bookmark multiple branch head' '
+       test_when_finished "rm -rf gitrepo*" &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git checkout --quiet -b feature-c HEAD^ &&
+       echo feature-c > content &&
+       git commit -a -m feature-c &&
+       git push --quiet origin feature-c
+       ) &&
+
+       check_bookmark hgrepo feature-c feature-c
+'
+
+# cleanup previous stuff
+rm -rf hgrepo
+
+setup_big_push () {
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+       echo zero > content &&
+       hg add content &&
+       hg commit -m zero &&
+       hg bookmark bad_bmark1 &&
+       echo one > content &&
+       hg commit -m one &&
+       hg bookmark bad_bmark2 &&
+       hg bookmark good_bmark &&
+       hg bookmark -i good_bmark &&
+       hg -q branch good_branch &&
+       echo "good branch" > content &&
+       hg commit -m "good branch" &&
+       hg -q branch bad_branch &&
+       echo "bad branch" > content &&
+       hg commit -m "bad branch"
+       ) &&
+
+       git clone "hg::hgrepo" gitrepo &&
+
+       (
+       cd gitrepo &&
+       echo two > content &&
+       git commit -q -a -m two &&
+
+       git checkout -q good_bmark &&
+       echo three > content &&
+       git commit -q -a -m three &&
+
+       git checkout -q bad_bmark1 &&
+       git reset --hard HEAD^ &&
+       echo four > content &&
+       git commit -q -a -m four &&
+
+       git checkout -q bad_bmark2 &&
+       git reset --hard HEAD^ &&
+       echo five > content &&
+       git commit -q -a -m five &&
+
+       git checkout -q -b new_bmark master &&
+       echo six > content &&
+       git commit -q -a -m six &&
+
+       git checkout -q branches/good_branch &&
+       echo seven > content &&
+       git commit -q -a -m seven &&
+       echo eight > content &&
+       git commit -q -a -m eight &&
+
+       git checkout -q branches/bad_branch &&
+       git reset --hard HEAD^ &&
+       echo nine > content &&
+       git commit -q -a -m nine &&
+
+       git checkout -q -b branches/new_branch master &&
+       echo ten > content &&
+       git commit -q -a -m ten
+       )
+}
+
+test_expect_success 'remote big push' '
+       test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+       setup_big_push
+
+       (
+       cd gitrepo &&
+
+       check_push 1 --all <<-EOF
+       master
+       good_bmark
+       branches/good_branch
+       new_bmark:new
+       branches/new_branch:new
+       bad_bmark1:non-fast-forward
+       bad_bmark2:non-fast-forward
+       branches/bad_branch:non-fast-forward
+       EOF
+       ) &&
+
+       check_branch hgrepo default one &&
+       check_branch hgrepo good_branch "good branch" &&
+       check_branch hgrepo bad_branch "bad branch" &&
+       check_branch hgrepo new_branch '' &&
+       check_bookmark hgrepo good_bmark one &&
+       check_bookmark hgrepo bad_bmark1 one &&
+       check_bookmark hgrepo bad_bmark2 one &&
+       check_bookmark hgrepo new_bmark ''
+'
+
+test_expect_success 'remote big push fetch first' '
+       test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+       echo zero > content &&
+       hg add content &&
+       hg commit -m zero &&
+       hg bookmark bad_bmark &&
+       hg bookmark good_bmark &&
+       hg bookmark -i good_bmark &&
+       hg -q branch good_branch &&
+       echo "good branch" > content &&
+       hg commit -m "good branch" &&
+       hg -q branch bad_branch &&
+       echo "bad branch" > content &&
+       hg commit -m "bad branch"
+       ) &&
+
+       git clone "hg::hgrepo" gitrepo &&
+
+       (
+       cd hgrepo &&
+       hg bookmark -f bad_bmark &&
+       echo update_bmark > content &&
+       hg commit -m "update bmark"
+       ) &&
+
+       (
+       cd gitrepo &&
+       echo two > content &&
+       git commit -q -a -m two &&
+
+       git checkout -q good_bmark &&
+       echo three > content &&
+       git commit -q -a -m three &&
+
+       git checkout -q bad_bmark &&
+       echo four > content &&
+       git commit -q -a -m four &&
+
+       git checkout -q branches/bad_branch &&
+       echo five > content &&
+       git commit -q -a -m five &&
+
+       check_push 1 --all <<-EOF
+       master
+       good_bmark
+       new_bmark:new
+       new_branch:new
+       bad_bmark:fetch-first
+       branches/bad_branch:festch-first
+       EOF
+
+       git fetch &&
+
+       check_push 1 --all <<-EOF
+       master
+       good_bmark
+       bad_bmark:non-fast-forward
+       branches/bad_branch:non-fast-forward
+       EOF
+       )
+'
+
+test_expect_failure 'remote big push force' '
+       test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+       setup_big_push
+
+       (
+       cd gitrepo &&
+
+       check_push 0 --force --all <<-EOF
+       master
+       good_bmark
+       branches/good_branch
+       new_bmark:new
+       branches/new_branch:new
+       bad_bmark1:forced-update
+       bad_bmark2:forced-update
+       branches/bad_branch:forced-update
+       EOF
+       ) &&
+
+       check_branch hgrepo default six &&
+       check_branch hgrepo good_branch eight &&
+       check_branch hgrepo bad_branch nine &&
+       check_branch hgrepo new_branch ten &&
+       check_bookmark hgrepo good_bmark three &&
+       check_bookmark hgrepo bad_bmark1 four &&
+       check_bookmark hgrepo bad_bmark2 five &&
+       check_bookmark hgrepo new_bmark six
+'
+
+test_expect_failure 'remote big push dry-run' '
+       test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+       setup_big_push
+
+       (
+       cd gitrepo &&
+
+       check_push 0 --dry-run --all <<-EOF
+       master
+       good_bmark
+       branches/good_branch
+       new_bmark:new
+       branches/new_branch:new
+       bad_bmark1:non-fast-forward
+       bad_bmark2:non-fast-forward
+       branches/bad_branch:non-fast-forward
+       EOF
+
+       check_push 0 --dry-run master good_bmark new_bmark branches/good_branch branches/new_branch <<-EOF
+       master
+       good_bmark
+       branches/good_branch
+       new_bmark:new
+       branches/new_branch:new
+       EOF
+       ) &&
+
+       check_branch hgrepo default one &&
+       check_branch hgrepo good_branch "good branch" &&
+       check_branch hgrepo bad_branch "bad branch" &&
+       check_branch hgrepo new_branch '' &&
+       check_bookmark hgrepo good_bmark one &&
+       check_bookmark hgrepo bad_bmark1 one &&
+       check_bookmark hgrepo bad_bmark2 one &&
+       check_bookmark hgrepo new_bmark ''
+'
+
+test_expect_success 'remote double failed push' '
+       test_when_finished "rm -rf hgrepo gitrepo*" &&
+
+       (
+       hg init hgrepo &&
+       cd hgrepo &&
+       echo zero > content &&
+       hg add content &&
+       hg commit -m zero &&
+       echo one > content &&
+       hg commit -m one
+       ) &&
+
+       (
+       git clone "hg::hgrepo" gitrepo &&
+       cd gitrepo &&
+       git reset --hard HEAD^ &&
+       echo two > content &&
+       git commit -a -m two &&
+       test_expect_code 1 git push &&
+       test_expect_code 1 git push
+       )
 '
 
 test_done
index b50750565ff0499dcbdf2f2b9802f109a830d797..435b2dea293b01daaad83e6905148be2256c02df 100644 (file)
@@ -30,6 +30,7 @@ $(GIT_SUBTREE): $(GIT_SUBTREE_SH)
 doc: $(GIT_SUBTREE_DOC)
 
 install: $(GIT_SUBTREE)
+       $(INSTALL) -d -m 755 $(DESTDIR)$(libexecdir)
        $(INSTALL) -m 755 $(GIT_SUBTREE) $(DESTDIR)$(libexecdir)
 
 install-doc: install-man
index 8a23f58ba04c4b1402c66d03db69d0c109c90b58..7d7af03274ee0759c9e915a6ea7b52ecce65cf55 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 #
 # git-subtree.sh: split/join git repositories in subdirectories of this one
 #
@@ -311,7 +311,7 @@ copy_commit()
                        GIT_COMMITTER_NAME \
                        GIT_COMMITTER_EMAIL \
                        GIT_COMMITTER_DATE
-               (echo -n "$annotate"; cat ) |
+               (printf "%s" "$annotate"; cat ) |
                git commit-tree "$2" $3  # reads the rest of stdin
        ) || die "Can't copy commit $1"
 }
@@ -715,7 +715,8 @@ cmd_push()
            repository=$1
            refspec=$2
            echo "git push using: " $repository $refspec
-           git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec
+           localrev=$(git subtree split --prefix="$prefix") || die
+           git push $repository $localrev:refs/heads/$refspec
        else
            die "'$dir' must already exist. Try 'git subtree add'."
        fi
index 7ba853eeda96ec9a4b4a3b4ebb8c88dda055c67d..e0957eee55d7fb4a530717d1ced39bcabb2057b2 100644 (file)
@@ -270,7 +270,7 @@ git-extensions repository in ~/git-extensions/:
 name
 
 You can omit the --squash flag, but doing so will increase the number
-of commits that are incldued in your local repository.
+of commits that are included in your local repository.
 
 We now have a ~/git-extensions/git-subtree directory containing code
 from the master branch of git://github.com/apenwarr/git-subtree.git
index b0f8536fca58d910f85334727b89925651bfa8ae..66ce4b07c2dc2d2d56dc260883f86d8672d2ac69 100755 (executable)
@@ -182,9 +182,9 @@ test_expect_success 'merge new subproj history into subdir' '
 test_expect_success 'Check that prefix argument is required for split' '
         echo "You must provide the --prefix option." > expected &&
         test_must_fail git subtree split > actual 2>&1 &&
-        test_debug "echo -n expected: " &&
+       test_debug "printf '"'"'expected: '"'"'" &&
         test_debug "cat expected" &&
-        test_debug "echo -n actual: " &&
+       test_debug "printf '"'"'actual: '"'"'" &&
         test_debug "cat actual" &&
         test_cmp expected actual &&
         rm -f expected actual
@@ -193,9 +193,9 @@ test_expect_success 'Check that prefix argument is required for split' '
 test_expect_success 'Check that the <prefix> exists for a split' '
         echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
         test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
-        test_debug "echo -n expected: " &&
+       test_debug "printf '"'"'expected: '"'"'" &&
         test_debug "cat expected" &&
-        test_debug "echo -n actual: " &&
+       test_debug "printf '"'"'actual: '"'"'" &&
         test_debug "cat actual" &&
         test_cmp expected actual
 #        rm -f expected actual
index 3520252d3abaf49d4b7682ad083da8b7ae6be4c6..11a95fc8b5b5eea6de2014b9209e16f9a3422186 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -153,36 +153,13 @@ static void check_safe_crlf(const char *path, enum crlf_action crlf_action,
 
 static int has_cr_in_index(const char *path)
 {
-       int pos, len;
        unsigned long sz;
-       enum object_type type;
        void *data;
        int has_cr;
-       struct index_state *istate = &the_index;
 
-       len = strlen(path);
-       pos = index_name_pos(istate, path, len);
-       if (pos < 0) {
-               /*
-                * We might be in the middle of a merge, in which
-                * case we would read stage #2 (ours).
-                */
-               int i;
-               for (i = -pos - 1;
-                    (pos < 0 && i < istate->cache_nr &&
-                     !strcmp(istate->cache[i]->name, path));
-                    i++)
-                       if (ce_stage(istate->cache[i]) == 2)
-                               pos = i;
-       }
-       if (pos < 0)
+       data = read_blob_data_from_cache(path, &sz);
+       if (!data)
                return 0;
-       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
-       if (!data || type != OBJ_BLOB) {
-               free(data);
-               return 0;
-       }
-
        has_cr = memchr(data, '\r', sz) != NULL;
        free(data);
        return has_cr;
@@ -858,7 +835,7 @@ int renormalize_buffer(const char *path, const char *src, size_t len, struct str
 
 /*****************************************************************
  *
- * Streaming converison support
+ * Streaming conversion support
  *
  *****************************************************************/
 
index ec5fd69430908915cf9c0de62a2021882fd2c658..0c2143c152734feb3bdc7ba44b61b8327f02881c 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -48,7 +48,7 @@ static inline int would_convert_to_git(const char *path, const char *src,
 
 /*****************************************************************
  *
- * Streaming converison support
+ * Streaming conversion support
  *
  *****************************************************************/
 
index 26f7589a60b882251b1e601433a6d8be4ddfe794..f9146e576f82cd774f697420e2bb456f56ad65dd 100644 (file)
@@ -114,7 +114,7 @@ static int lookup_credential(const char *fn, struct credential *c)
        return c->username && c->password;
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        const char * const usage[] = {
                "git credential-store [options] <action>",
@@ -131,7 +131,7 @@ int main(int argc, const char **argv)
 
        umask(077);
 
-       argc = parse_options(argc, argv, NULL, options, usage, 0);
+       argc = parse_options(argc, (const char **)argv, NULL, options, usage, 0);
        if (argc != 1)
                usage_with_options(usage, options);
        op = argv[0];
index 6aeddcb98d2694a705d34e80293fc81a20bc39a8..34916c5e105812e36d659dfeeb8341e003e746bd 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -760,7 +760,7 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
                snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
                    ntohs(sin_addr->sin_port));
 #ifndef NO_IPV6
-       } else if (addr && addr->sa_family == AF_INET6) {
+       } else if (addr->sa_family == AF_INET6) {
                struct sockaddr_in6 *sin6_addr = (void *) addr;
 
                char *buf = addrbuf + 12;
@@ -1047,18 +1047,6 @@ static int service_loop(struct socketlist *socklist)
        }
 }
 
-/* if any standard file descriptor is missing open it to /dev/null */
-static void sanitize_stdfds(void)
-{
-       int fd = open("/dev/null", O_RDWR, 0);
-       while (fd != -1 && fd < 2)
-               fd = dup(fd);
-       if (fd == -1)
-               die_errno("open /dev/null or dup failed");
-       if (fd > 2)
-               close(fd);
-}
-
 #ifdef NO_POSIX_GOODIES
 
 struct credentials;
diff --git a/date.c b/date.c
index df20d0ba1d9c5d9fa03316968c1b7038659add05..29f15404feab0d98f377b312a3fcf5d8888fdb0a 100644 (file)
--- a/date.c
+++ b/date.c
@@ -711,6 +711,28 @@ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
        return 0; /* success */
 }
 
+int parse_expiry_date(const char *date, unsigned long *timestamp)
+{
+       int errors = 0;
+
+       if (!strcmp(date, "never") || !strcmp(date, "false"))
+               *timestamp = 0;
+       else if (!strcmp(date, "all") || !strcmp(date, "now"))
+               /*
+                * We take over "now" here, which usually translates
+                * to the current timestamp.  This is because the user
+                * really means to expire everything she has done in
+                * the past, and by definition reflogs are the record
+                * of the past, and there is nothing from the future
+                * to be kept.
+                */
+               *timestamp = ULONG_MAX;
+       else
+               *timestamp = approxidate_careful(date, &errors);
+
+       return errors;
+}
+
 int parse_date(const char *date, char *result, int maxlen)
 {
        unsigned long timestamp;
index 2f8a63e38881587fe29fcb72a5272ef54b9efa6e..7cb5d29a89a43ecf7e433db513b66184df3accb8 100644 (file)
@@ -49,7 +49,7 @@ static void grow_decoration(struct decoration *n)
                const struct object *base = old_hash[i].base;
                void *decoration = old_hash[i].decoration;
 
-               if (!base)
+               if (!decoration)
                        continue;
                insert_decoration(n, base, decoration);
        }
index f35de0ffa0ec263acae7ac6211e3c2817987f760..b6f4b21637fe66a614a0dd6581f90fc12e80a6f8 100644 (file)
@@ -64,8 +64,9 @@ static int check_removed(const struct cache_entry *ce, struct stat *st)
  * commits, untracked content and/or modified content).
  */
 static int match_stat_with_submodule(struct diff_options *diffopt,
-                                     struct cache_entry *ce, struct stat *st,
-                                     unsigned ce_option, unsigned *dirty_submodule)
+                                    const struct cache_entry *ce,
+                                    struct stat *st, unsigned ce_option,
+                                    unsigned *dirty_submodule)
 {
        int changed = ce_match_stat(ce, st, ce_option);
        if (S_ISGITLINK(ce->ce_mode)) {
@@ -237,7 +238,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 /* A file entry went away or appeared */
 static void diff_index_show_file(struct rev_info *revs,
                                 const char *prefix,
-                                struct cache_entry *ce,
+                                const struct cache_entry *ce,
                                 const unsigned char *sha1, int sha1_valid,
                                 unsigned int mode,
                                 unsigned dirty_submodule)
@@ -246,7 +247,7 @@ static void diff_index_show_file(struct rev_info *revs,
                       sha1, sha1_valid, ce->name, dirty_submodule);
 }
 
-static int get_stat_data(struct cache_entry *ce,
+static int get_stat_data(const struct cache_entry *ce,
                         const unsigned char **sha1p,
                         unsigned int *modep,
                         int cached, int match_missing,
@@ -283,7 +284,7 @@ static int get_stat_data(struct cache_entry *ce,
 }
 
 static void show_new_file(struct rev_info *revs,
-                         struct cache_entry *new,
+                         const struct cache_entry *new,
                          int cached, int match_missing)
 {
        const unsigned char *sha1;
@@ -302,8 +303,8 @@ static void show_new_file(struct rev_info *revs,
 }
 
 static int show_modified(struct rev_info *revs,
-                        struct cache_entry *old,
-                        struct cache_entry *new,
+                        const struct cache_entry *old,
+                        const struct cache_entry *new,
                         int report_missing,
                         int cached, int match_missing)
 {
@@ -362,8 +363,8 @@ static int show_modified(struct rev_info *revs,
  * give you the position and number of entries in the index).
  */
 static void do_oneway_diff(struct unpack_trees_options *o,
-       struct cache_entry *idx,
-       struct cache_entry *tree)
+                          const struct cache_entry *idx,
+                          const struct cache_entry *tree)
 {
        struct rev_info *revs = o->unpack_data;
        int match_missing, cached;
@@ -423,10 +424,11 @@ static void do_oneway_diff(struct unpack_trees_options *o,
  * the fairly complex unpack_trees() semantic requirements, including
  * the skipping, the path matching, the type conflict cases etc.
  */
-static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
+static int oneway_diff(const struct cache_entry * const *src,
+                      struct unpack_trees_options *o)
 {
-       struct cache_entry *idx = src[0];
-       struct cache_entry *tree = src[1];
+       const struct cache_entry *idx = src[0];
+       const struct cache_entry *tree = src[1];
        struct rev_info *revs = o->unpack_data;
 
        /*
index 74da659368e4db67715a9c81dc7a5e0edcd3bcce..e66fdf33da9e3dd3da561aadffec938e6bc2ad49 100644 (file)
@@ -45,7 +45,7 @@ static int get_mode(const char *path, int *mode)
 
        if (!path || !strcmp(path, "/dev/null"))
                *mode = 0;
-#ifdef _WIN32
+#ifdef GIT_WINDOWS_NATIVE
        else if (!strcasecmp(path, "nul"))
                *mode = 0;
 #endif
diff --git a/diff.c b/diff.c
index f0b3e7cfe34a99c0e5fea85fcd00ec54e9b578d8..266112ca6104450497fb9be504c2404cced595f0 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1683,9 +1683,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                del = deleted;
 
                if (graph_width <= max_change) {
-                       int total = add + del;
-
-                       total = scale_linear(add + del, graph_width, max_change);
+                       int total = scale_linear(add + del, graph_width, max_change);
                        if (total < 2 && add && del)
                                /* width >= 2 due to the sanity check */
                                total = 2;
@@ -2586,7 +2584,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
  */
 static int reuse_worktree_file(const char *name, const unsigned char *sha1, int want_file)
 {
-       struct cache_entry *ce;
+       const struct cache_entry *ce;
        struct stat st;
        int pos, len;
 
@@ -2677,6 +2675,14 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 int diff_populate_filespec(struct diff_filespec *s, int size_only)
 {
        int err = 0;
+       /*
+        * demote FAIL to WARN to allow inspecting the situation
+        * instead of refusing.
+        */
+       enum safe_crlf crlf_warn = (safe_crlf == SAFE_CRLF_FAIL
+                                   ? SAFE_CRLF_WARN
+                                   : safe_crlf);
+
        if (!DIFF_FILE_VALID(s))
                die("internal error: asking to populate invalid file.");
        if (S_ISDIR(s->mode))
@@ -2732,7 +2738,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
                /*
                 * Convert from working tree format to canonical git format
                 */
-               if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
+               if (convert_to_git(s->path, s->data, s->size, &buf, crlf_warn)) {
                        size_t size = 0;
                        munmap(s->data, s->size);
                        s->should_munmap = 0;
@@ -3497,6 +3503,11 @@ static int parse_submodule_opt(struct diff_options *options, const char *value)
        return 1;
 }
 
+static void enable_patch_output(int *fmt) {
+       *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
+       *fmt |= DIFF_FORMAT_PATCH;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
        const char *arg = av[0];
@@ -3504,15 +3515,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        int argcount;
 
        /* Output format options */
-       if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
-               options->output_format |= DIFF_FORMAT_PATCH;
-       else if (opt_arg(arg, 'U', "unified", &options->context))
-               options->output_format |= DIFF_FORMAT_PATCH;
+       if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")
+           || opt_arg(arg, 'U', "unified", &options->context))
+               enable_patch_output(&options->output_format);
        else if (!strcmp(arg, "--raw"))
                options->output_format |= DIFF_FORMAT_RAW;
-       else if (!strcmp(arg, "--patch-with-raw"))
-               options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
-       else if (!strcmp(arg, "--numstat"))
+       else if (!strcmp(arg, "--patch-with-raw")) {
+               enable_patch_output(&options->output_format);
+               options->output_format |= DIFF_FORMAT_RAW;
+       } else if (!strcmp(arg, "--numstat"))
                options->output_format |= DIFF_FORMAT_NUMSTAT;
        else if (!strcmp(arg, "--shortstat"))
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
@@ -3534,13 +3545,14 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_CHECKDIFF;
        else if (!strcmp(arg, "--summary"))
                options->output_format |= DIFF_FORMAT_SUMMARY;
-       else if (!strcmp(arg, "--patch-with-stat"))
-               options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT;
-       else if (!strcmp(arg, "--name-only"))
+       else if (!strcmp(arg, "--patch-with-stat")) {
+               enable_patch_output(&options->output_format);
+               options->output_format |= DIFF_FORMAT_DIFFSTAT;
+       } else if (!strcmp(arg, "--name-only"))
                options->output_format |= DIFF_FORMAT_NAME;
        else if (!strcmp(arg, "--name-status"))
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
-       else if (!strcmp(arg, "-s"))
+       else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch"))
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
        else if (!prefixcmp(arg, "--stat"))
                /* --stat, --stat-width, --stat-name-width, or --stat-count */
@@ -3593,6 +3605,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
        else if (!strcmp(arg, "--ignore-space-at-eol"))
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
+       else if (!strcmp(arg, "--ignore-blank-lines"))
+               DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
@@ -3611,7 +3625,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 
        /* flags options */
        else if (!strcmp(arg, "--binary")) {
-               options->output_format |= DIFF_FORMAT_PATCH;
+               enable_patch_output(&options->output_format);
                DIFF_OPT_SET(options, BINARY);
        }
        else if (!strcmp(arg, "--full-index"))
@@ -4450,7 +4464,7 @@ void diff_flush(struct diff_options *options)
            DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) {
                /*
                 * run diff_flush_patch for the exit status. setting
-                * options->file to /dev/null should be safe, becaue we
+                * options->file to /dev/null should be safe, because we
                 * aren't supposed to produce any output anyway.
                 */
                if (options->close_file)
index 63722f86dca4c00389d6f305fd8aa1af8ef8ed6c..401eb72c619d432c92fc66b470fae778117bcace 100644 (file)
@@ -122,7 +122,7 @@ static void diffcore_pickaxe_grep(struct diff_options *o)
                char errbuf[1024];
                regerror(err, &regex, errbuf, 1024);
                regfree(&regex);
-               die("invalid log-grep regex: %s", errbuf);
+               die("invalid regex: %s", errbuf);
        }
 
        pickaxe(&diff_queued_diff, o, &regex, NULL, diff_grep);
@@ -131,8 +131,7 @@ static void diffcore_pickaxe_grep(struct diff_options *o)
        return;
 }
 
-static unsigned int contains(mmfile_t *mf, struct diff_options *o,
-                            regex_t *regexp, kwset_t kws)
+static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
 {
        unsigned int cnt;
        unsigned long sz;
@@ -176,11 +175,9 @@ static int has_changes(mmfile_t *one, mmfile_t *two,
                       struct diff_options *o,
                       regex_t *regexp, kwset_t kws)
 {
-       if (!one)
-               return contains(two, o, regexp, kws) != 0;
-       if (!two)
-               return contains(one, o, regexp, kws) != 0;
-       return contains(one, o, regexp, kws) != contains(two, o, regexp, kws);
+       unsigned int one_contains = one ? contains(one, regexp, kws) : 0;
+       unsigned int two_contains = two ? contains(two, regexp, kws) : 0;
+       return one_contains != two_contains;
 }
 
 static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
@@ -246,7 +243,7 @@ static void diffcore_pickaxe_count(struct diff_options *o)
                        char errbuf[1024];
                        regerror(err, &regex, errbuf, 1024);
                        regfree(&regex);
-                       die("invalid pickaxe regex: %s", errbuf);
+                       die("invalid regex: %s", errbuf);
                }
                regexp = &regex;
        } else {
diff --git a/dir.c b/dir.c
index 91cfd996711f8b9523da92ff75088292e16d316c..910bfcde4e432fae3bf57ec951258bc5a02bebb6 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -17,7 +17,21 @@ struct path_simplify {
        const char *path;
 };
 
-static int read_directory_recursive(struct dir_struct *dir, const char *path, int len,
+/*
+ * Tells read_directory_recursive how a file or directory should be treated.
+ * Values are ordered by significance, e.g. if a directory contains both
+ * excluded and untracked files, it is listed as untracked because
+ * path_untracked > path_excluded.
+ */
+enum path_treatment {
+       path_none = 0,
+       path_recurse,
+       path_excluded,
+       path_untracked
+};
+
+static enum path_treatment read_directory_recursive(struct dir_struct *dir,
+       const char *path, int len,
        int check_only, const struct path_simplify *simplify);
 static int get_dtype(struct dirent *de, const char *path, int len);
 
@@ -578,78 +592,6 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)
                die("cannot use %s as an exclude file", fname);
 }
 
-/*
- * Loads the per-directory exclude list for the substring of base
- * which has a char length of baselen.
- */
-static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
-{
-       struct exclude_list_group *group;
-       struct exclude_list *el;
-       struct exclude_stack *stk = NULL;
-       int current;
-
-       if ((!dir->exclude_per_dir) ||
-           (baselen + strlen(dir->exclude_per_dir) >= PATH_MAX))
-               return; /* too long a path -- ignore */
-
-       group = &dir->exclude_list_group[EXC_DIRS];
-
-       /* Pop the exclude lists from the EXCL_DIRS exclude_list_group
-        * which originate from directories not in the prefix of the
-        * path being checked. */
-       while ((stk = dir->exclude_stack) != NULL) {
-               if (stk->baselen <= baselen &&
-                   !strncmp(dir->basebuf, base, stk->baselen))
-                       break;
-               el = &group->el[dir->exclude_stack->exclude_ix];
-               dir->exclude_stack = stk->prev;
-               free((char *)el->src); /* see strdup() below */
-               clear_exclude_list(el);
-               free(stk);
-               group->nr--;
-       }
-
-       /* Read from the parent directories and push them down. */
-       current = stk ? stk->baselen : -1;
-       while (current < baselen) {
-               struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
-               const char *cp;
-
-               if (current < 0) {
-                       cp = base;
-                       current = 0;
-               }
-               else {
-                       cp = strchr(base + current + 1, '/');
-                       if (!cp)
-                               die("oops in prep_exclude");
-                       cp++;
-               }
-               stk->prev = dir->exclude_stack;
-               stk->baselen = cp - base;
-               memcpy(dir->basebuf + current, base + current,
-                      stk->baselen - current);
-               strcpy(dir->basebuf + stk->baselen, 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.
-                */
-               el = add_exclude_list(dir, EXC_DIRS, strdup(dir->basebuf));
-               stk->exclude_ix = group->nr - 1;
-               add_excludes_from_file_to_list(dir->basebuf,
-                                              dir->basebuf, stk->baselen,
-                                              el, 1);
-               dir->exclude_stack = stk;
-               current = stk->baselen;
-       }
-       dir->basebuf[baselen] = '\0';
-}
-
 int match_basename(const char *basename, int basenamelen,
                   const char *pattern, int prefix, int patternlen,
                   int flags)
@@ -795,25 +737,13 @@ int is_excluded_from_list(const char *pathname,
        return -1; /* undecided */
 }
 
-/*
- * Loads the exclude lists for the directory containing pathname, then
- * scans all exclude lists to determine whether pathname is excluded.
- * Returns the exclude_list element which matched, or NULL for
- * undecided.
- */
-static struct exclude *last_exclude_matching(struct dir_struct *dir,
-                                            const char *pathname,
-                                            int *dtype_p)
+static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir,
+               const char *pathname, int pathlen, const char *basename,
+               int *dtype_p)
 {
-       int pathlen = strlen(pathname);
        int i, j;
        struct exclude_list_group *group;
        struct exclude *exclude;
-       const char *basename = strrchr(pathname, '/');
-       basename = (basename) ? basename+1 : pathname;
-
-       prep_exclude(dir, pathname, basename-pathname);
-
        for (i = EXC_CMDL; i <= EXC_FILE; i++) {
                group = &dir->exclude_list_group[i];
                for (j = group->nr - 1; j >= 0; j--) {
@@ -828,101 +758,134 @@ static struct exclude *last_exclude_matching(struct dir_struct *dir,
 }
 
 /*
- * Loads the exclude lists for the directory containing pathname, then
- * scans all exclude lists to determine whether pathname is excluded.
- * Returns 1 if true, otherwise 0.
+ * Loads the per-directory exclude list for the substring of base
+ * which has a char length of baselen.
  */
-static int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
-{
-       struct exclude *exclude =
-               last_exclude_matching(dir, pathname, dtype_p);
-       if (exclude)
-               return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
-       return 0;
-}
-
-void path_exclude_check_init(struct path_exclude_check *check,
-                            struct dir_struct *dir)
+static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
 {
-       check->dir = dir;
-       check->exclude = NULL;
-       strbuf_init(&check->path, 256);
-}
+       struct exclude_list_group *group;
+       struct exclude_list *el;
+       struct exclude_stack *stk = NULL;
+       int current;
 
-void path_exclude_check_clear(struct path_exclude_check *check)
-{
-       strbuf_release(&check->path);
-}
+       group = &dir->exclude_list_group[EXC_DIRS];
 
-/*
- * For each subdirectory in name, starting with the top-most, checks
- * to see if that subdirectory is excluded, and if so, returns the
- * corresponding exclude structure.  Otherwise, checks whether name
- * itself (which is presumably a file) is excluded.
- *
- * A path to a directory known to be excluded is left in check->path to
- * optimize for repeated checks for files in the same excluded directory.
- */
-struct exclude *last_exclude_matching_path(struct path_exclude_check *check,
-                                          const char *name, int namelen,
-                                          int *dtype)
-{
-       int i;
-       struct strbuf *path = &check->path;
-       struct exclude *exclude;
+       /* Pop the exclude lists from the EXCL_DIRS exclude_list_group
+        * which originate from directories not in the prefix of the
+        * path being checked. */
+       while ((stk = dir->exclude_stack) != NULL) {
+               if (stk->baselen <= baselen &&
+                   !strncmp(dir->basebuf, 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 */
+               clear_exclude_list(el);
+               free(stk);
+               group->nr--;
+       }
 
-       /*
-        * we allow the caller to pass namelen as an optimization; it
-        * must match the length of the name, as we eventually call
-        * is_excluded() on the whole name string.
-        */
-       if (namelen < 0)
-               namelen = strlen(name);
+       /* Skip traversing into sub directories if the parent is excluded */
+       if (dir->exclude)
+               return;
 
-       /*
-        * If path is non-empty, and name is equal to path or a
-        * subdirectory of path, name should be excluded, because
-        * it's inside a directory which is already known to be
-        * excluded and was previously left in check->path.
-        */
-       if (path->len &&
-           path->len <= namelen &&
-           !memcmp(name, path->buf, path->len) &&
-           (!name[path->len] || name[path->len] == '/'))
-               return check->exclude;
+       /* Read from the parent directories and push them down. */
+       current = stk ? stk->baselen : -1;
+       while (current < baselen) {
+               struct exclude_stack *stk = xcalloc(1, sizeof(*stk));
+               const char *cp;
 
-       strbuf_setlen(path, 0);
-       for (i = 0; name[i]; i++) {
-               int ch = name[i];
+               if (current < 0) {
+                       cp = base;
+                       current = 0;
+               }
+               else {
+                       cp = strchr(base + current + 1, '/');
+                       if (!cp)
+                               die("oops in prep_exclude");
+                       cp++;
+               }
+               stk->prev = dir->exclude_stack;
+               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);
 
-               if (ch == '/') {
+               /* Abort if the directory is excluded */
+               if (stk->baselen) {
                        int dt = DT_DIR;
-                       exclude = last_exclude_matching(check->dir,
-                                                       path->buf, &dt);
-                       if (exclude) {
-                               check->exclude = exclude;
-                               return exclude;
+                       dir->basebuf[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] = '/';
+                       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;
                        }
                }
-               strbuf_addch(path, ch);
+
+               /* 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);
+                       /*
+                        * 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.
+                        */
+                       el->src = strdup(dir->basebuf);
+                       add_excludes_from_file_to_list(dir->basebuf,
+                                       dir->basebuf, stk->baselen, el, 1);
+               }
+               dir->exclude_stack = stk;
+               current = stk->baselen;
        }
+       dir->basebuf[baselen] = '\0';
+}
 
-       /* An entry in the index; cannot be a directory with subentries */
-       strbuf_setlen(path, 0);
+/*
+ * Loads the exclude lists for the directory containing pathname, then
+ * scans all exclude lists to determine whether pathname is excluded.
+ * Returns the exclude_list element which matched, or NULL for
+ * undecided.
+ */
+struct exclude *last_exclude_matching(struct dir_struct *dir,
+                                            const char *pathname,
+                                            int *dtype_p)
+{
+       int pathlen = strlen(pathname);
+       const char *basename = strrchr(pathname, '/');
+       basename = (basename) ? basename+1 : pathname;
 
-       return last_exclude_matching(check->dir, name, dtype);
+       prep_exclude(dir, pathname, basename-pathname);
+
+       if (dir->exclude)
+               return dir->exclude;
+
+       return last_exclude_matching_from_lists(dir, pathname, pathlen,
+                       basename, dtype_p);
 }
 
 /*
- * Is this name excluded?  This is for a caller like show_files() that
- * do not honor directory hierarchy and iterate through paths that are
- * possibly in an ignored directory.
+ * Loads the exclude lists for the directory containing pathname, then
+ * scans all exclude lists to determine whether pathname is excluded.
+ * Returns 1 if true, otherwise 0.
  */
-int is_path_excluded(struct path_exclude_check *check,
-                 const char *name, int namelen, int *dtype)
+int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
 {
        struct exclude *exclude =
-               last_exclude_matching_path(check, name, namelen, dtype);
+               last_exclude_matching(dir, pathname, dtype_p);
        if (exclude)
                return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
        return 0;
@@ -941,8 +904,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
 
 static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
-       if (!(dir->flags & DIR_SHOW_IGNORED) &&
-           cache_name_exists(pathname, len, ignore_case))
+       if (cache_name_exists(pathname, len, ignore_case))
                return NULL;
 
        ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
@@ -971,7 +933,7 @@ enum exist_status {
  */
 static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
 {
-       struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+       const struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
        unsigned char endchar;
 
        if (!ce)
@@ -1015,7 +977,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
        if (pos < 0)
                pos = -pos-1;
        while (pos < active_nr) {
-               struct cache_entry *ce = active_cache[pos++];
+               const struct cache_entry *ce = active_cache[pos++];
                unsigned char endchar;
 
                if (strncmp(ce->name, dirname, len))
@@ -1044,9 +1006,8 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
  * traversal routine.
  *
  * Case 1: If we *already* have entries in the index under that
- * directory name, we recurse into the directory to see all the files,
- * unless the directory is excluded and we want to show ignored
- * directories
+ * directory name, we always recurse into the directory to see
+ * all the files.
  *
  * Case 2: If we *already* have that directory name as a gitlink,
  * we always continue to see it as a gitlink, regardless of whether
@@ -1058,38 +1019,24 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
  *
  *  (a) if "show_other_directories" is true, we show it as
  *      just a directory, unless "hide_empty_directories" is
- *      also true and the directory is empty, in which case
- *      we just ignore it entirely.
- *      if we are looking for ignored directories, look if it
- *      contains only ignored files to decide if it must be shown as
- *      ignored or not.
+ *      also true, in which case we need to check if it contains any
+ *      untracked and / or ignored files.
  *  (b) if it looks like a git directory, and we don't have
  *      'no_gitlinks' set we treat it as a gitlink, and show it
  *      as a directory.
  *  (c) otherwise, we recurse into it.
  */
-enum directory_treatment {
-       show_directory,
-       ignore_directory,
-       recurse_into_directory
-};
-
-static enum directory_treatment treat_directory(struct dir_struct *dir,
+static enum path_treatment treat_directory(struct dir_struct *dir,
        const char *dirname, int len, int exclude,
        const struct path_simplify *simplify)
 {
        /* The "len-1" is to strip the final '/' */
        switch (directory_exists_in_index(dirname, len-1)) {
        case index_directory:
-               if ((dir->flags & DIR_SHOW_OTHER_DIRECTORIES) && exclude)
-                       break;
-
-               return recurse_into_directory;
+               return path_recurse;
 
        case index_gitdir:
-               if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
-                       return ignore_directory;
-               return show_directory;
+               return path_none;
 
        case index_nonexistent:
                if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
@@ -1097,72 +1044,17 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
                if (!(dir->flags & DIR_NO_GITLINKS)) {
                        unsigned char sha1[20];
                        if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
-                               return show_directory;
+                               return path_untracked;
                }
-               return recurse_into_directory;
+               return path_recurse;
        }
 
        /* This is the "show_other_directories" case */
 
-       /*
-        * We are looking for ignored files and our directory is not ignored,
-        * check if it contains only ignored files
-        */
-       if ((dir->flags & DIR_SHOW_IGNORED) && !exclude) {
-               int ignored;
-               dir->flags &= ~DIR_SHOW_IGNORED;
-               dir->flags |= DIR_HIDE_EMPTY_DIRECTORIES;
-               ignored = read_directory_recursive(dir, dirname, len, 1, simplify);
-               dir->flags &= ~DIR_HIDE_EMPTY_DIRECTORIES;
-               dir->flags |= DIR_SHOW_IGNORED;
-
-               return ignored ? ignore_directory : show_directory;
-       }
-       if (!(dir->flags & DIR_SHOW_IGNORED) &&
-           !(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
-               return show_directory;
-       if (!read_directory_recursive(dir, dirname, len, 1, simplify))
-               return ignore_directory;
-       return show_directory;
-}
-
-/*
- * Decide what to do when we find a file while traversing the
- * filesystem. Mostly two cases:
- *
- *  1. We are looking for ignored files
- *   (a) File is ignored, include it
- *   (b) File is in ignored path, include it
- *   (c) File is not ignored, exclude it
- *
- *  2. Other scenarios, include the file if not excluded
- *
- * Return 1 for exclude, 0 for include.
- */
-static int treat_file(struct dir_struct *dir, struct strbuf *path, int exclude, int *dtype)
-{
-       struct path_exclude_check check;
-       int exclude_file = 0;
-
-       if (exclude)
-               exclude_file = !(dir->flags & DIR_SHOW_IGNORED);
-       else if (dir->flags & DIR_SHOW_IGNORED) {
-               /* Always exclude indexed files */
-               struct cache_entry *ce = index_name_exists(&the_index,
-                   path->buf, path->len, ignore_case);
-
-               if (ce)
-                       return 1;
-
-               path_exclude_check_init(&check, dir);
-
-               if (!is_path_excluded(&check, path->buf, path->len, dtype))
-                       exclude_file = 1;
-
-               path_exclude_check_clear(&check);
-       }
+       if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
+               return exclude ? path_excluded : path_untracked;
 
-       return exclude_file;
+       return read_directory_recursive(dir, dirname, len, 1, simplify);
 }
 
 /*
@@ -1221,7 +1113,7 @@ static int exclude_matches_pathspec(const char *path, int len,
 static int get_index_dtype(const char *path, int len)
 {
        int pos;
-       struct cache_entry *ce;
+       const struct cache_entry *ce;
 
        ce = cache_name_exists(path, len, 0);
        if (ce) {
@@ -1277,57 +1169,40 @@ static int get_dtype(struct dirent *de, const char *path, int len)
        return dtype;
 }
 
-enum path_treatment {
-       path_ignored,
-       path_handled,
-       path_recurse
-};
-
 static enum path_treatment treat_one_path(struct dir_struct *dir,
                                          struct strbuf *path,
                                          const struct path_simplify *simplify,
                                          int dtype, struct dirent *de)
 {
-       int exclude = is_excluded(dir, path->buf, &dtype);
-       if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
-           && exclude_matches_pathspec(path->buf, path->len, simplify))
-               dir_add_ignored(dir, path->buf, path->len);
+       int exclude;
+       if (dtype == DT_UNKNOWN)
+               dtype = get_dtype(de, path->buf, path->len);
+
+       /* Always exclude indexed files */
+       if (dtype != DT_DIR &&
+           cache_name_exists(path->buf, path->len, ignore_case))
+               return path_none;
+
+       exclude = is_excluded(dir, path->buf, &dtype);
 
        /*
         * Excluded? If we don't explicitly want to show
         * ignored files, ignore it
         */
-       if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
-               return path_ignored;
-
-       if (dtype == DT_UNKNOWN)
-               dtype = get_dtype(de, path->buf, path->len);
+       if (exclude && !(dir->flags & (DIR_SHOW_IGNORED|DIR_SHOW_IGNORED_TOO)))
+               return path_excluded;
 
        switch (dtype) {
        default:
-               return path_ignored;
+               return path_none;
        case DT_DIR:
                strbuf_addch(path, '/');
-
-               switch (treat_directory(dir, path->buf, path->len, exclude, simplify)) {
-               case show_directory:
-                       break;
-               case recurse_into_directory:
-                       return path_recurse;
-               case ignore_directory:
-                       return path_ignored;
-               }
-               break;
+               return treat_directory(dir, path->buf, path->len, exclude,
+                       simplify);
        case DT_REG:
        case DT_LNK:
-               switch (treat_file(dir, path, exclude, &dtype)) {
-               case 1:
-                       return path_ignored;
-               default:
-                       break;
-               }
+               return exclude ? path_excluded : path_untracked;
        }
-       return path_handled;
 }
 
 static enum path_treatment treat_path(struct dir_struct *dir,
@@ -1339,11 +1214,11 @@ static enum path_treatment treat_path(struct dir_struct *dir,
        int dtype;
 
        if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
-               return path_ignored;
+               return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
        if (simplify_away(path->buf, path->len, simplify))
-               return path_ignored;
+               return path_none;
 
        dtype = DTYPE(de);
        return treat_one_path(dir, path, simplify, dtype, de);
@@ -1357,14 +1232,16 @@ static enum path_treatment treat_path(struct dir_struct *dir,
  *
  * Also, we ignore the name ".git" (even if it is not a directory).
  * That likely will not change.
+ *
+ * Returns the most significant path_treatment value encountered in the scan.
  */
-static int read_directory_recursive(struct dir_struct *dir,
+static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                                    const char *base, int baselen,
                                    int check_only,
                                    const struct path_simplify *simplify)
 {
        DIR *fdir;
-       int contents = 0;
+       enum path_treatment state, subdir_state, dir_state = path_none;
        struct dirent *de;
        struct strbuf path = STRBUF_INIT;
 
@@ -1375,27 +1252,53 @@ static int read_directory_recursive(struct dir_struct *dir,
                goto out;
 
        while ((de = readdir(fdir)) != NULL) {
-               switch (treat_path(dir, de, &path, baselen, simplify)) {
-               case path_recurse:
-                       contents += read_directory_recursive(dir, path.buf,
-                                                            path.len, 0,
-                                                            simplify);
-                       continue;
-               case path_ignored:
+               /* check how the file or directory should be treated */
+               state = treat_path(dir, de, &path, baselen, simplify);
+               if (state > dir_state)
+                       dir_state = state;
+
+               /* recurse into subdir if instructed by treat_path */
+               if (state == path_recurse) {
+                       subdir_state = read_directory_recursive(dir, path.buf,
+                               path.len, check_only, simplify);
+                       if (subdir_state > dir_state)
+                               dir_state = subdir_state;
+               }
+
+               if (check_only) {
+                       /* abort early if maximum state has been reached */
+                       if (dir_state == path_untracked)
+                               break;
+                       /* skip the dir_add_* part */
                        continue;
-               case path_handled:
-                       break;
                }
-               contents++;
-               if (check_only)
+
+               /* add the path to the appropriate result list */
+               switch (state) {
+               case path_excluded:
+                       if (dir->flags & DIR_SHOW_IGNORED)
+                               dir_add_name(dir, path.buf, path.len);
+                       else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
+                               ((dir->flags & DIR_COLLECT_IGNORED) &&
+                               exclude_matches_pathspec(path.buf, path.len,
+                                       simplify)))
+                               dir_add_ignored(dir, path.buf, path.len);
                        break;
-               dir_add_name(dir, path.buf, path.len);
+
+               case path_untracked:
+                       if (!(dir->flags & DIR_SHOW_IGNORED))
+                               dir_add_name(dir, path.buf, path.len);
+                       break;
+
+               default:
+                       break;
+               }
        }
        closedir(fdir);
  out:
        strbuf_release(&path);
 
-       return contents;
+       return dir_state;
 }
 
 static int cmp_name(const void *p1, const void *p2)
@@ -1444,12 +1347,14 @@ static int treat_leading_path(struct dir_struct *dir,
        struct strbuf sb = STRBUF_INIT;
        int baselen, rc = 0;
        const char *cp;
+       int old_flags = dir->flags;
 
        while (len && path[len - 1] == '/')
                len--;
        if (!len)
                return 1;
        baselen = 0;
+       dir->flags &= ~DIR_SHOW_OTHER_DIRECTORIES;
        while (1) {
                cp = path + baselen + !!baselen;
                cp = memchr(cp, '/', path + len - cp);
@@ -1464,7 +1369,7 @@ static int treat_leading_path(struct dir_struct *dir,
                if (simplify_away(sb.buf, sb.len, simplify))
                        break;
                if (treat_one_path(dir, &sb, simplify,
-                                  DT_DIR, NULL) == path_ignored)
+                                  DT_DIR, NULL) == path_none)
                        break; /* do not recurse into it */
                if (len <= baselen) {
                        rc = 1;
@@ -1472,6 +1377,7 @@ static int treat_leading_path(struct dir_struct *dir,
                }
        }
        strbuf_release(&sb);
+       dir->flags = old_flags;
        return rc;
 }
 
@@ -1637,9 +1543,9 @@ void setup_standard_excludes(struct dir_struct *dir)
                home_config_paths(NULL, &xdg_path, "ignore");
                excludes_file = xdg_path;
        }
-       if (!access_or_warn(path, R_OK))
+       if (!access_or_warn(path, R_OK, 0))
                add_excludes_from_file(dir, path);
-       if (excludes_file && !access_or_warn(excludes_file, R_OK))
+       if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
                add_excludes_from_file(dir, excludes_file);
 }
 
diff --git a/dir.h b/dir.h
index c3eb4b520effb63e73653194e4266c0ef628cccd..3d6b80c933b395644c005f6d8df4697215ff000a 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -79,7 +79,8 @@ struct dir_struct {
                DIR_SHOW_OTHER_DIRECTORIES = 1<<1,
                DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
                DIR_NO_GITLINKS = 1<<3,
-               DIR_COLLECT_IGNORED = 1<<4
+               DIR_COLLECT_IGNORED = 1<<4,
+               DIR_SHOW_IGNORED_TOO = 1<<5
        } flags;
        struct dir_entry **entries;
        struct dir_entry **ignored;
@@ -110,9 +111,11 @@ struct dir_struct {
         *
         * exclude_stack points to the top of the exclude_stack, and
         * basebuf contains the full path to the current
-        * (sub)directory in the traversal.
+        * (sub)directory in the traversal. Exclude points to the
+        * matching exclude struct if the directory is excluded.
         */
        struct exclude_stack *exclude_stack;
+       struct exclude *exclude;
        char basebuf[PATH_MAX];
 };
 
@@ -149,22 +152,10 @@ extern int match_pathname(const char *, int,
                          const char *, int,
                          const char *, int, int, int);
 
-/*
- * The is_excluded() API is meant for callers that check each level of leading
- * directory hierarchies with is_excluded() to avoid recursing into excluded
- * directories.  Callers that do not do so should use this API instead.
- */
-struct path_exclude_check {
-       struct dir_struct *dir;
-       struct exclude *exclude;
-       struct strbuf path;
-};
-extern void path_exclude_check_init(struct path_exclude_check *, struct dir_struct *);
-extern void path_exclude_check_clear(struct path_exclude_check *);
-extern struct exclude *last_exclude_matching_path(struct path_exclude_check *, const char *,
-                                                 int namelen, int *dtype);
-extern int is_path_excluded(struct path_exclude_check *, const char *, int namelen, int *dtype);
+extern struct exclude *last_exclude_matching(struct dir_struct *dir,
+                                            const char *name, int *dtype);
 
+extern int is_excluded(struct dir_struct *dir, const char *name, int *dtype);
 
 extern struct exclude_list *add_exclude_list(struct dir_struct *dir,
                                             int group_type, const char *src);
diff --git a/entry.c b/entry.c
index d7c131d45309a496714221616244a70569155913..acc892f9a62b5bc3d3442b044b29bf8d763bf3ba 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -79,7 +79,7 @@ static int create_file(const char *path, unsigned int mode)
        return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
 }
 
-static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
+static void *read_blob_entry(const struct cache_entry *ce, unsigned long *size)
 {
        enum object_type type;
        void *new = read_sha1_file(ce->sha1, &type, size);
@@ -92,7 +92,7 @@ static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
        return NULL;
 }
 
-static int open_output_fd(char *path, struct cache_entry *ce, int to_tempfile)
+static int open_output_fd(char *path, const struct cache_entry *ce, int to_tempfile)
 {
        int symlink = (ce->ce_mode & S_IFMT) != S_IFREG;
        if (to_tempfile) {
@@ -115,7 +115,7 @@ static int fstat_output(int fd, const struct checkout *state, struct stat *st)
        return 0;
 }
 
-static int streaming_write_entry(struct cache_entry *ce, char *path,
+static int streaming_write_entry(const struct cache_entry *ce, char *path,
                                 struct stream_filter *filter,
                                 const struct checkout *state, int to_tempfile,
                                 int *fstat_done, struct stat *statbuf)
@@ -136,7 +136,8 @@ static int streaming_write_entry(struct cache_entry *ce, char *path,
        return result;
 }
 
-static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
+static int write_entry(struct cache_entry *ce,
+                      char *path, const struct checkout *state, int to_tempfile)
 {
        unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
        int fd, ret, fstat_done = 0;
@@ -199,9 +200,9 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
                break;
        case S_IFGITLINK:
                if (to_tempfile)
-                       return error("cannot create temporary subproject %s", path);
+                       return error("cannot create temporary submodule %s", path);
                if (mkdir(path, 0777) < 0)
-                       return error("cannot create subproject directory %s", path);
+                       return error("cannot create submodule directory %s", path);
                break;
        default:
                return error("unknown file mode for %s in index", path);
@@ -233,7 +234,8 @@ static int check_path(const char *path, int len, struct stat *st, int skiplen)
        return lstat(path, st);
 }
 
-int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)
+int checkout_entry(struct cache_entry *ce,
+                  const struct checkout *state, char *topath)
 {
        static char path[PATH_MAX + 1];
        struct stat st;
index e2e75c16602d8e5841c2461defaba7b9866115d8..378254c77a00f93ef667c0510fa11b75988ef05a 100644 (file)
@@ -22,6 +22,7 @@ int prefer_symlink_refs;
 int is_bare_repository_cfg = -1; /* unspecified */
 int log_all_ref_updates = -1; /* unspecified */
 int warn_ambiguous_refs = 1;
+int warn_on_object_refname_ambiguity = 1;
 int repository_format_version;
 const char *git_commit_encoding;
 const char *git_log_output_encoding;
@@ -37,7 +38,6 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
 size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
 size_t delta_base_cache_limit = 16 * 1024 * 1024;
 unsigned long big_file_threshold = 512 * 1024 * 1024;
-const char *log_pack_access;
 const char *pager_program;
 int pager_use_color = 1;
 const char *editor_program;
@@ -123,14 +123,13 @@ static char *expand_namespace(const char *raw_namespace)
 
 static void setup_git_env(void)
 {
+       const char *gitfile;
+
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
-       git_dir = git_dir ? xstrdup(git_dir) : NULL;
-       if (!git_dir) {
-               git_dir = read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
-               git_dir = git_dir ? xstrdup(git_dir) : NULL;
-       }
        if (!git_dir)
                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);
index e2b546b615e2806bf7d733099ca0ac7bcfaef823..e4c9702f02858973096f6ef0f41e5a572ff4d0db 100644 (file)
@@ -7,6 +7,7 @@ extern const char *git_exec_path(void);
 extern void setup_path(void);
 extern const char **prepare_git_cmd(const char **argv);
 extern int execv_git_cmd(const char **argv); /* NULL terminated */
+LAST_ARG_MUST_BE_NULL
 extern int execl_git_cmd(const char *cmd, ...);
 extern const char *system_path(const char *path);
 
index 5f539d7d8f7e087423734fb1d19e0d5e580fbe6e..23f625f561dfd664a460827d507e7c0b720e9849 100644 (file)
@@ -297,7 +297,7 @@ static int failure;
 static FILE *pack_edges;
 static unsigned int show_stats = 1;
 static int global_argc;
-static const char **global_argv;
+static char **global_argv;
 
 /* Memory pools */
 static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@ -1822,7 +1822,7 @@ static void read_marks(void)
                *end = 0;
                mark = strtoumax(line + 1, &end, 10);
                if (!mark || end == line + 1
-                       || *end != ' ' || get_sha1(end + 1, sha1))
+                       || *end != ' ' || get_sha1_hex(end + 1, sha1))
                        die("corrupt mark line: %s", line);
                e = find_object(sha1);
                if (!e) {
@@ -3347,7 +3347,7 @@ static void parse_argv(void)
                read_marks();
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        unsigned int i;
 
index f156dd4fac30cda4e09c508b7091cdb8d96917e2..aff4f5abab667d054c9ab1b925e4877a4fd8d243 100644 (file)
@@ -11,6 +11,7 @@
 #include "run-command.h"
 #include "transport.h"
 #include "version.h"
+#include "prio-queue.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -20,6 +21,8 @@ static int no_done;
 static int fetch_fsck_objects = -1;
 static int transfer_fsck_objects = -1;
 static int agent_supported;
+static struct lock_file shallow_lock;
+static const char *alternate_shallow_file;
 
 #define COMPLETE       (1U << 0)
 #define COMMON         (1U << 1)
@@ -35,7 +38,7 @@ static int marked;
  */
 #define MAX_IN_VAIN 256
 
-static struct commit_list *rev_list;
+static struct prio_queue rev_list = { compare_commits_by_commit_date };
 static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
 
 static void rev_list_push(struct commit *commit, int mark)
@@ -47,7 +50,7 @@ static void rev_list_push(struct commit *commit, int mark)
                        if (parse_commit(commit))
                                return;
 
-               commit_list_insert_by_date(commit, &rev_list);
+               prio_queue_put(&rev_list, commit);
 
                if (!(commit->object.flags & COMMON))
                        non_common_revs++;
@@ -120,10 +123,10 @@ static const unsigned char *get_rev(void)
                unsigned int mark;
                struct commit_list *parents;
 
-               if (rev_list == NULL || non_common_revs == 0)
+               if (rev_list.nr == 0 || non_common_revs == 0)
                        return NULL;
 
-               commit = rev_list->item;
+               commit = prio_queue_get(&rev_list);
                if (!commit->object.parsed)
                        parse_commit(commit);
                parents = commit->parents;
@@ -150,8 +153,6 @@ static const unsigned char *get_rev(void)
                                mark_common(parents->item, 1, 0);
                        parents = parents->next;
                }
-
-               rev_list = rev_list->next;
        }
 
        return commit->object.sha1;
@@ -183,36 +184,6 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
        }
 }
 
-struct write_shallow_data {
-       struct strbuf *out;
-       int use_pack_protocol;
-       int count;
-};
-
-static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
-{
-       struct write_shallow_data *data = cb_data;
-       const char *hex = sha1_to_hex(graft->sha1);
-       data->count++;
-       if (data->use_pack_protocol)
-               packet_buf_write(data->out, "shallow %s", hex);
-       else {
-               strbuf_addstr(data->out, hex);
-               strbuf_addch(data->out, '\n');
-       }
-       return 0;
-}
-
-static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
-{
-       struct write_shallow_data data;
-       data.out = out;
-       data.use_pack_protocol = use_pack_protocol;
-       data.count = 0;
-       for_each_commit_graft(write_one_shallow, &data);
-       return data.count;
-}
-
 static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 {
        int len;
@@ -440,7 +411,7 @@ static int find_common(struct fetch_pack_args *args,
                                        in_vain = 0;
                                        got_continue = 1;
                                        if (ack == ACK_ready) {
-                                               rev_list = NULL;
+                                               clear_prio_queue(&rev_list);
                                                got_ready = 1;
                                        }
                                        break;
@@ -503,7 +474,7 @@ static int mark_complete(const char *refname, const unsigned char *sha1, int fla
                struct commit *commit = (struct commit *)o;
                if (!(commit->object.flags & COMPLETE)) {
                        commit->object.flags |= COMPLETE;
-                       commit_list_insert_by_date(commit, &complete);
+                       commit_list_insert(commit, &complete);
                }
        }
        return 0;
@@ -620,6 +591,7 @@ static int everything_local(struct fetch_pack_args *args,
        if (!args->depth) {
                for_each_ref(mark_complete, NULL);
                for_each_alternate_ref(mark_alternate_complete, NULL);
+               commit_list_sort_by_date(&complete);
                if (cutoff)
                        mark_recent_complete_commits(args, cutoff);
        }
@@ -683,12 +655,13 @@ static int get_pack(struct fetch_pack_args *args,
                    int xd[2], char **pack_lockfile)
 {
        struct async demux;
-       const char *argv[20];
+       const char *argv[22];
        char keep_arg[256];
        char hdr_arg[256];
        const char **av;
        int do_keep = args->keep_pack;
        struct child_process cmd;
+       int ret;
 
        memset(&demux, 0, sizeof(demux));
        if (use_sideband) {
@@ -724,6 +697,11 @@ static int get_pack(struct fetch_pack_args *args,
                        do_keep = 1;
        }
 
+       if (alternate_shallow_file) {
+               *av++ = "--shallow-file";
+               *av++ = alternate_shallow_file;
+       }
+
        if (do_keep) {
                if (pack_lockfile)
                        cmd.out = -1;
@@ -740,11 +718,14 @@ static int get_pack(struct fetch_pack_args *args,
                                strcpy(keep_arg + s, "localhost");
                        *av++ = keep_arg;
                }
+               if (args->check_self_contained_and_connected)
+                       *av++ = "--check-self-contained-and-connected";
        }
        else {
                *av++ = "unpack-objects";
                if (args->quiet || args->no_progress)
                        *av++ = "-q";
+               args->check_self_contained_and_connected = 0;
        }
        if (*hdr_arg)
                *av++ = hdr_arg;
@@ -765,7 +746,12 @@ static int get_pack(struct fetch_pack_args *args,
                close(cmd.out);
        }
 
-       if (finish_command(&cmd))
+       ret = finish_command(&cmd);
+       if (!ret || (args->check_self_contained_and_connected && ret == 1))
+               args->self_contained_and_connected =
+                       args->check_self_contained_and_connected &&
+                       ret == 0;
+       else
                die("%s failed", argv[0]);
        if (use_sideband && finish_async(&demux))
                die("error in sideband demultiplexer");
@@ -858,6 +844,10 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 
        if (args->stateless_rpc)
                packet_flush(fd[1]);
+       if (args->depth > 0)
+               setup_alternate_shallow(&shallow_lock, &alternate_shallow_file);
+       else
+               alternate_shallow_file = NULL;
        if (get_pack(args, fd, pack_lockfile))
                die("git fetch-pack: fetch failed.");
 
@@ -936,15 +926,9 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                       struct ref **sought, int nr_sought,
                       char **pack_lockfile)
 {
-       struct stat st;
        struct ref *ref_cpy;
 
        fetch_pack_setup();
-       if (args->depth > 0) {
-               if (stat(git_path("shallow"), &st))
-                       st.st_mtime = 0;
-       }
-
        if (nr_sought)
                nr_sought = remove_duplicates_in_refs(sought, nr_sought);
 
@@ -954,35 +938,12 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
        }
        ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
 
-       if (args->depth > 0) {
-               static struct lock_file lock;
-               struct cache_time mtime;
-               struct strbuf sb = STRBUF_INIT;
-               char *shallow = git_path("shallow");
-               int fd;
-
-               mtime.sec = st.st_mtime;
-               mtime.nsec = ST_MTIME_NSEC(st);
-               if (stat(shallow, &st)) {
-                       if (mtime.sec)
-                               die("shallow file was removed during fetch");
-               } else if (st.st_mtime != mtime.sec
-#ifdef USE_NSEC
-                               || ST_MTIME_NSEC(st) != mtime.nsec
-#endif
-                         )
-                       die("shallow file was changed during fetch");
-
-               fd = hold_lock_file_for_update(&lock, shallow,
-                                              LOCK_DIE_ON_ERROR);
-               if (!write_shallow_commits(&sb, 0)
-                || write_in_full(fd, sb.buf, sb.len) != sb.len) {
-                       unlink_or_warn(shallow);
-                       rollback_lock_file(&lock);
-               } else {
-                       commit_lock_file(&lock);
-               }
-               strbuf_release(&sb);
+       if (args->depth > 0 && alternate_shallow_file) {
+               if (*alternate_shallow_file == '\0') { /* --unshallow */
+                       unlink_or_warn(git_path("shallow"));
+                       rollback_lock_file(&shallow_lock);
+               } else
+                       commit_lock_file(&shallow_lock);
        }
 
        reprepare_packed_git();
index dc5266c970655a9fe4f971a0132c14e1cf731acc..40f08bab24df917986de73c8d7f4d9d36fe658da 100644 (file)
@@ -16,7 +16,9 @@ struct fetch_pack_args {
                verbose:1,
                no_progress:1,
                include_tag:1,
-               stateless_rpc:1;
+               stateless_rpc:1,
+               check_self_contained_and_connected:1,
+               self_contained_and_connected:1;
 };
 
 /*
diff --git a/fixup-builtins b/fixup-builtins
deleted file mode 100755 (executable)
index 63dfa4c..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-while [ "$1" ]
-do
-       if [ "$1" != "git-sh-setup" -a "$1" != "git-parse-remote" -a "$1" != "git-svn" ]; then
-               old="$1"
-               new=$(echo "$1" | sed 's/git-/git /')
-               echo "Converting '$old' to '$new'"
-               sed -i "s/\\<$old\\>/$new/g" $(git ls-files '*.sh')
-       fi
-       shift
-done
-
-sed -i 's/git merge-one-file/git-merge-one-file/g
-s/git rebase-todo/git-rebase-todo/g' $(git ls-files '*.sh')
-git update-index --refresh >& /dev/null
-exit 0
index d2c4ce6e1e88f543bd06cd8347e8949fef11a381..51563840f49089cf250992fd47fe70a45cef0c52 100755 (executable)
@@ -44,6 +44,8 @@ my ($diff_new_color) =
 
 my $normal_color = $repo->get_color("", "reset");
 
+my $diff_algorithm = $repo->config('diff.algorithm');
+
 my $use_readkey = 0;
 my $use_termcap = 0;
 my %term_escapes;
@@ -167,7 +169,7 @@ my %patch_modes = (
 my %patch_mode_flavour = %{$patch_modes{stage}};
 
 sub run_cmd_pipe {
-       if ($^O eq 'MSWin32' || $^O eq 'msys') {
+       if ($^O eq 'MSWin32') {
                my @invalid = grep {m/[":*]/} @_;
                die "$^O does not support: @invalid\n" if @invalid;
                my @args = map { m/ /o ? "\"$_\"": $_ } @_;
@@ -731,6 +733,9 @@ sub run_git_apply {
 sub parse_diff {
        my ($path) = @_;
        my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
+       if (defined $diff_algorithm) {
+               splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
+       }
        if (defined $patch_mode_revision) {
                push @diff_cmd, $patch_mode_revision;
        }
index c092855dd7c277d41288ee89f033bef0a8677a9f..7ea40fe6449b665713a8272f125936edf632189b 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -6,7 +6,7 @@ SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
 git am [options] [(<mbox>|<Maildir>)...]
-git am [options] (--resolved | --skip | --abort)
+git am [options] (--continue | --skip | --abort)
 --
 i,interactive   run interactively
 b,binary*       (historical option -- no-op)
@@ -102,7 +102,7 @@ stop_here_user_resolve () {
            printf '%s\n' "$resolvemsg"
            stop_here $1
     fi
-    eval_gettextln "When you have resolved this problem, run \"\$cmdline --resolved\".
+    eval_gettextln "When you have resolved this problem, run \"\$cmdline --continue\".
 If you prefer to skip this patch, run \"\$cmdline --skip\" instead.
 To restore the original branch and stop patching, run \"\$cmdline --abort\"."
 
@@ -446,6 +446,8 @@ done
 # If the dotest directory exists, but we have finished applying all the
 # patches in them, clear it out.
 if test -d "$dotest" &&
+   test -f "$dotest/last" &&
+   test -f "$dotest/next" &&
    last=$(cat "$dotest/last") &&
    next=$(cat "$dotest/next") &&
    test $# != 0 &&
@@ -454,7 +456,7 @@ then
    rm -fr "$dotest"
 fi
 
-if test -d "$dotest"
+if test -d "$dotest" && test -f "$dotest/last" && test -f "$dotest/next"
 then
        case "$#,$skip$resolved$abort" in
        0,*t*)
@@ -504,7 +506,24 @@ then
        esac
        rm -f "$dotest/dirtyindex"
 else
-       # Make sure we are not given --skip, --resolved, nor --abort
+       # Possible stray $dotest directory in the independent-run
+       # case; in the --rebasing case, it is upto the caller
+       # (git-rebase--am) to take care of stray directories.
+       if test -d "$dotest" && test -z "$rebasing"
+       then
+               case "$skip,$resolved,$abort" in
+               ,,t)
+                       rm -fr "$dotest"
+                       exit 0
+                       ;;
+               *)
+                       die "$(eval_gettext "Stray \$dotest directory found.
+Use \"git am --abort\" to remove it.")"
+                       ;;
+               esac
+       fi
+
+       # Make sure we are not given --skip, --continue, nor --abort
        test "$skip$resolved$abort" = "" ||
                die "$(gettext "Resolve operation not in progress, we are not resuming.")"
 
@@ -651,7 +670,7 @@ do
        #  - patch is the patch body.
        #
        # When we are resuming, these files are either already prepared
-       # by the user, or the user can tell us to do so by --resolved flag.
+       # by the user, or the user can tell us to do so by --continue flag.
        case "$resume" in
        '')
                if test -f "$dotest/rebasing"
@@ -904,5 +923,10 @@ if test -s "$dotest"/rewritten; then
     fi
 fi
 
-rm -fr "$dotest"
-git gc --auto
+# If am was called with --rebasing (from git-rebase--am), it's up to
+# the caller to take care of housekeeping.
+if ! test -f "$dotest/rebasing"
+then
+       rm -fr "$dotest"
+       git gc --auto
+fi
index 99efbe884528ffa730d6c0297e20d9de93d5ad9e..9f064b6f4f0c209a0315538e48049c2d013316df 100755 (executable)
@@ -311,7 +311,23 @@ bisect_next() {
        res=$?
 
        # Check if we should exit because bisection is finished
-       test $res -eq 10 && exit 0
+       if test $res -eq 10
+       then
+               bad_rev=$(git show-ref --hash --verify refs/bisect/bad)
+               bad_commit=$(git show-branch $bad_rev)
+               echo "# first bad commit: $bad_commit" >>"$GIT_DIR/BISECT_LOG"
+               exit 0
+       elif test $res -eq 2
+       then
+               echo "# only skipped commits left to test" >>"$GIT_DIR/BISECT_LOG"
+               good_revs=$(git for-each-ref --format="%(objectname)" "refs/bisect/good-*")
+               for skipped in $(git rev-list refs/bisect/bad --not $good_revs)
+               do
+                       skipped_commit=$(git show-branch $skipped)
+                       echo "# possible first bad commit: $skipped_commit" >>"$GIT_DIR/BISECT_LOG"
+               done
+               exit $res
+       fi
 
        # Check for an error in the bisection process
        test $res -ne 0 && exit $res
index e955bb5e8b3101cc8c753cf541beabf5cd037b39..96d888165b9185e146aae4ab29998a11aeec312e 100644 (file)
 #define _NETBSD_SOURCE 1
 #define _SGI_SOURCE 1
 
-#ifdef WIN32 /* Both MinGW and MSVC */
+#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
 # if defined (_MSC_VER)
 #  define _WIN32_WINNT 0x0502
 # endif
 #define WIN32_LEAN_AND_MEAN  /* stops windows.h including winsock.h */
 #include <winsock2.h>
 #include <windows.h>
+#define GIT_WINDOWS_NATIVE
 #endif
 
 #include <unistd.h>
 #else
 #include <poll.h>
 #endif
+
 #if defined(__MINGW32__)
 /* pull in Windows compatibility stuff */
 #include "compat/mingw.h"
 typedef long intptr_t;
 typedef unsigned long uintptr_t;
 #endif
-int get_st_mode_bits(const char *path, int *mode);
 #if defined(__CYGWIN__)
 #undef _XOPEN_SOURCE
 #include <grp.h>
 #define _XOPEN_SOURCE 600
-#include "compat/cygwin.h"
 #else
 #undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
 #include <grp.h>
@@ -295,6 +295,13 @@ extern char *gitbasename(char *);
 #endif
 #endif
 
+/* The sentinel attribute is valid from gcc version 4.0 */
+#if defined(__GNUC__) && (__GNUC__ >= 4)
+#define LAST_ARG_MUST_BE_NULL __attribute__((sentinel))
+#else
+#define LAST_ARG_MUST_BE_NULL
+#endif
+
 #include "compat/bswap.h"
 
 #ifdef USE_WILDMATCH
@@ -692,8 +699,9 @@ int remove_or_warn(unsigned int mode, const char *path);
  * Call access(2), but warn for any error except "missing file"
  * (ENOENT or ENOTDIR).
  */
-int access_or_warn(const char *path, int mode);
-int access_or_die(const char *path, int mode);
+#define ACCESS_EACCES_OK (1U << 0)
+int access_or_warn(const char *path, int mode, unsigned flag);
+int access_or_die(const char *path, int mode, unsigned flag);
 
 /* Warn on an inaccessible file that ought to be accessible */
 void warn_on_inaccessible(const char *path);
index a0d796e57087e0ca5bac94520997ecacec9fd295..67b1e7b463fd26f202e7265baaf14c68aa3bd818 100755 (executable)
@@ -4167,7 +4167,7 @@ sub convertToDbMode
     #  this half-converted form, but it isn't currently worth the
     #  backwards compatibility headaches.
 
-    $mode=~/^\d\d(\d)\d{3}$/;
+    $mode=~/^\d{3}(\d)\d\d$/;
     my $userBits=$1;
 
     my $dbMode = "";
index 67802922ccc41fa2993c1bce4ea1a3d2899c7a40..e57d3d1295a5ba0a9c02c19df512c909a0631baa 100755 (executable)
@@ -85,11 +85,13 @@ sub exit_cleanup
 
 sub use_wt_file
 {
-       my ($repo, $workdir, $file, $sha1, $symlinks) = @_;
+       my ($repo, $workdir, $file, $sha1) = @_;
        my $null_sha1 = '0' x 40;
 
-       if ($sha1 ne $null_sha1 and not $symlinks) {
-               return 0;
+       if (! -e "$workdir/$file") {
+               # If the file doesn't exist in the working tree, we cannot
+               # use it.
+               return (0, $null_sha1);
        }
 
        my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
@@ -207,8 +209,7 @@ EOF
 
                if ($rmode ne $null_mode) {
                        my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
-                                                         $dst_path, $rsha1,
-                                                         $symlinks);
+                                                         $dst_path, $rsha1);
                        if ($use) {
                                push @working_tree, $dst_path;
                                $wtindex .= "$rmode $wt_sha1\t$dst_path\0";
index 49b62ea6ebc61c4af16d7d740e5b423e5787b915..0a1f8d4f8a9187a0d5decf20f9c926dbd570a8db 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=0.17.GITGUI
+DEF_VER=0.18.GITGUI
 
 LF='
 '
index e22ba5c321dc47fdf512f30c5e6c741c41fb99eb..e9c2bc347dc77fb08bc6515a604a744fc4ee5795 100644 (file)
@@ -254,7 +254,7 @@ lib/tclIndex: $(ALL_LIBFILES) GIT-GUI-VARS
          auto_mkindex lib '*.tcl' \
        | $(TCL_PATH) $(QUIET_2DEVNULL); then : ok; \
        else \
-        echo 1>&2 "    * $(TCL_PATH) failed; using unoptimized loading"; \
+        echo >&2 "    * $(TCL_PATH) failed; using unoptimized loading"; \
         rm -f $@ ; \
         echo '# Autogenerated by git-gui Makefile' >$@ && \
         echo >>$@ && \
@@ -274,8 +274,8 @@ TRACK_VARS = \
 GIT-GUI-VARS: FORCE
        @VARS='$(TRACK_VARS)'; \
        if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
-               echo 1>&2 "    * new locations or Tcl/Tk interpreter"; \
-               echo 1>$@ "$$VARS"; \
+               echo >&2 "    * new locations or Tcl/Tk interpreter"; \
+               echo >$@ "$$VARS"; \
        fi
 
 ifdef GITGUI_MACOSXAPP
index 89f636f4963cf7933639f6a23b3be2e8fd4c0964..153f85da068294917f5e60afca4fb6715f3f32ab 100755 (executable)
@@ -135,6 +135,20 @@ proc strcat {args} {
 ::msgcat::mcload $oguimsg
 unset oguimsg
 
+######################################################################
+##
+## On Mac, bring the current Wish process window to front
+
+if {[tk windowingsystem] eq "aqua"} {
+       catch {
+               exec osascript -e [format {
+                       tell application "System Events"
+                               set frontmost of processes whose unix id is %d to true
+                       end tell
+               } [pid]]
+       }
+}
+
 ######################################################################
 ##
 ## read only globals
@@ -3003,18 +3017,11 @@ blame {
        set jump_spec {}
        set is_path 0
        foreach a $argv {
-               if {[file exists $a]} {
-                       if {$path ne {}} usage
-                       set path [normalize_relpath $a]
-                       break
-               } elseif {[file exists $_prefix$a]} {
-                       if {$path ne {}} usage
-                       set path [normalize_relpath $_prefix$a]
-                       break
-               }
+               set p [file join $_prefix $a]
 
-               if {$is_path} {
+               if {$is_path || [file exists $p]} {
                        if {$path ne {}} usage
+                       set path [normalize_relpath $p]
                        break
                } elseif {$a eq {--}} {
                        if {$path ne {}} {
index 657f7d5dc19b13f36a31b833f04407b8ca0b2901..ee58981f539bfd27dbba99db47d6f85102b0e7a6 100644 (file)
@@ -286,7 +286,9 @@ method _next {action} {
        destroy $w_body
        if {![winfo exists $w_next]} {
                ${NS}::button $w_next -default active
-               pack $w_next -side right -padx 5 -before $w_quit
+               set pos -before
+               if {[tk windowingsystem] eq "win32"} { set pos -after }
+               pack $w_next -side right -padx 5 $pos $w_quit
        }
        _do_$action $this
 }
index ec4405567a9b86a9bfd8bf50815b3c9c14e2df40..30d9a797769b49cdd9d42fba925d8fa8ecbffb21 100644 (file)
@@ -764,8 +764,15 @@ proc apply_range_or_line {x y} {
                                # context line
                                set ln [$ui_diff get $i_l $next_l]
                                set patch "$patch$pre_context$ln"
-                               set n [expr $n+1]
-                               set m [expr $m+1]
+                               # Skip the "\ No newline at end of
+                               # file". Depending on the locale setting
+                               # we don't know what this line looks
+                               # like exactly. The only thing we do
+                               # know is that it starts with "\ "
+                               if {![string match {\\ *} $ln]} {
+                                       set n [expr $n+1]
+                                       set m [expr $m+1]
+                               }
                                set pre_context {}
                        } elseif {$c1 eq $to_context} {
                                # turn change line into context line
index 3c8e73bcebea652c11e328169f63a6d9950d6dc2..120bc4064b6f88023fb067a7aedaa67e13802f2c 100644 (file)
@@ -189,9 +189,9 @@ proc merge_resolve_tool2 {} {
        }
        bc3 {
                if {$base_stage ne {}} {
-                       set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" -mergeoutput="$MERGED"]
+                       set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" "-mergeoutput=$MERGED"]
                } else {
-                       set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -mergeoutput="$MERGED"]
+                       set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "-mergeoutput=$MERGED"]
                }
        }
        ecmerge {
index 5e4e7f4c83952ac2ec4c60eb79426248071ae54e..4e5c7844188ee2eab0c9f6e00835b99c69bbdfc2 100644 (file)
@@ -245,7 +245,8 @@ proc update_all_remotes_menu_entry {} {
        set prune_m $remote_m.prune
        if {$have_remote > 1} {
                make_sure_remote_submenues_exist $remote_m
-               if {[$fetch_m entrycget end -label] ne "All"} {
+               if {[$fetch_m type end] eq "command" \
+                       && [$fetch_m entrycget end -label] ne "All"} {
 
                        $fetch_m insert end separator
                        $fetch_m insert end command \
@@ -259,7 +260,8 @@ proc update_all_remotes_menu_entry {} {
                }
        } else {
                if {[winfo exists $fetch_m]} {
-                       if {[$fetch_m entrycget end -label] eq "All"} {
+                       if {[$fetch_m type end] eq "command" \
+                               && [$fetch_m entrycget end -label] eq "All"} {
 
                                delete_from_menu $fetch_m end
                                delete_from_menu $fetch_m end
index 40441dbb0da4fe5a62776054aa2d5d2d30d9af8f..0aff18691d168d7826995d5288e37d8421009141 100644 (file)
@@ -1139,7 +1139,7 @@ msgstr "Standard (rapide, semi-redondant, liens durs)"
 
 #: lib/choose_repository.tcl:514
 msgid "Full Copy (Slower, Redundant Backup)"
-msgstr "Copy complète (plus lent, sauvegarde redondante)"
+msgstr "Copie complète (plus lent, sauvegarde redondante)"
 
 #: lib/choose_repository.tcl:520
 msgid "Shared (Fastest, Not Recommended, No Backup)"
index e338be5e57e32f52cfa818fd44a6364c00c24cdd..feee6a4a0d3a970c94fe506070ddc497c4b40658 100644 (file)
@@ -114,10 +114,37 @@ valid_tool () {
        test -n "$cmd"
 }
 
+setup_user_tool () {
+       merge_tool_cmd=$(get_merge_tool_cmd "$tool")
+       test -n "$merge_tool_cmd" || return 1
+
+       diff_cmd () {
+               ( eval $merge_tool_cmd )
+               status=$?
+               return $status
+       }
+
+       merge_cmd () {
+               trust_exit_code=$(git config --bool \
+                       "mergetool.$1.trustExitCode" || echo false)
+               if test "$trust_exit_code" = "false"
+               then
+                       touch "$BACKUP"
+                       ( eval $merge_tool_cmd )
+                       status=$?
+                       check_unchanged
+               else
+                       ( eval $merge_tool_cmd )
+                       status=$?
+               fi
+               return $status
+       }
+}
+
 setup_tool () {
        tool="$1"
 
-       # Fallback definitions, to be overriden by tools.
+       # Fallback definitions, to be overridden by tools.
        can_merge () {
                return 0
        }
@@ -142,15 +169,15 @@ setup_tool () {
 
        if ! test -f "$MERGE_TOOLS_DIR/$tool"
        then
-               # Use a special return code for this case since we want to
-               # source "defaults" even when an explicit tool path is
-               # configured since the user can use that to override the
-               # default path in the scriptlet.
-               return 2
+               setup_user_tool
+               return $?
        fi
 
        # Load the redefined functions
        . "$MERGE_TOOLS_DIR/$tool"
+       # Now let the user override the default command for the tool.  If
+       # they have not done so then this will return 1 which we ignore.
+       setup_user_tool
 
        if merge_mode && ! can_merge
        then
@@ -187,20 +214,7 @@ run_merge_tool () {
        status=0
 
        # Bring tool-specific functions into scope
-       setup_tool "$1"
-       exitcode=$?
-       case $exitcode in
-       0)
-               :
-               ;;
-       2)
-               # The configured tool is not a built-in tool.
-               test -n "$merge_tool_path" || return 1
-               ;;
-       *)
-               return $exitcode
-               ;;
-       esac
+       setup_tool "$1" || return 1
 
        if merge_mode
        then
@@ -213,38 +227,12 @@ run_merge_tool () {
 
 # Run a either a configured or built-in diff tool
 run_diff_cmd () {
-       merge_tool_cmd=$(get_merge_tool_cmd "$1")
-       if test -n "$merge_tool_cmd"
-       then
-               ( eval $merge_tool_cmd )
-               status=$?
-               return $status
-       else
-               diff_cmd "$1"
-       fi
+       diff_cmd "$1"
 }
 
 # Run a either a configured or built-in merge tool
 run_merge_cmd () {
-       merge_tool_cmd=$(get_merge_tool_cmd "$1")
-       if test -n "$merge_tool_cmd"
-       then
-               trust_exit_code=$(git config --bool \
-                       "mergetool.$1.trustExitCode" || echo false)
-               if test "$trust_exit_code" = "false"
-               then
-                       touch "$BACKUP"
-                       ( eval $merge_tool_cmd )
-                       status=$?
-                       check_unchanged
-               else
-                       ( eval $merge_tool_cmd )
-                       status=$?
-               fi
-               return $status
-       else
-               merge_cmd "$1"
-       fi
+       merge_cmd "$1"
 }
 
 list_merge_tool_candidates () {
index 911bbce6c5d4e19243e50d5916005872ac672961..31e71ff8b248339749bbcd045acdf602a1a21463 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1786,7 +1786,7 @@ class P4Submit(Command, P4UserMap):
             missingGitTags = gitTags - p4Labels
             self.exportGitTags(missingGitTags)
 
-        # exit with error unless everything applied perfecly
+        # exit with error unless everything applied perfectly
         if len(commits) != len(applied):
                 sys.exit(1)
 
@@ -3168,7 +3168,7 @@ class P4Rebase(Command):
         if os.system("git update-index --refresh") != 0:
             die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up-to-date or stash away all your changes with git stash.");
         if len(read_pipe("git diff-index HEAD --")) > 0:
-            die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash.");
+            die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash.");
 
         [upstream, settings] = findUpstreamBranchPoint()
         if len(upstream) == 0:
index 638aabb7b347e2afeb9bf327902de9e3702cd9d4..f0df41c841644208c89230516677db162498be26 100755 (executable)
@@ -5,7 +5,7 @@
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
 USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
-LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
+LONG_USAGE='Fetch one or more remote refs and integrate it/them with the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
 . git-sh-setup
@@ -266,10 +266,17 @@ case "$merge_head" in
        ;;
 esac
 
+# Pulling into unborn branch: a shorthand for branching off
+# FETCH_HEAD, for lazy typers.
 if test -z "$orig_head"
 then
-       git update-ref -m "initial pull" HEAD $merge_head "$curr_head" &&
-       git read-tree -m -u HEAD || exit 1
+       # Two-way merge: we claim the index is based on an empty tree,
+       # and try to fast-forward to HEAD.  This ensures we will not
+       # lose index/worktree changes that the user already made on
+       # the unborn branch.
+       empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+       git read-tree -m -u $empty_tree $merge_head &&
+       git update-ref -m "initial pull" HEAD $merge_head "$curr_head"
        exit
 fi
 
index f84854f09a14b92790bad543cbe78c2662def9f7..34e3102fcbfa64ec101c1fe009c9b14a5976e4c2 100644 (file)
@@ -7,12 +7,12 @@ case "$action" in
 continue)
        git am --resolved --resolvemsg="$resolvemsg" &&
        move_to_original_branch
-       exit
+       return
        ;;
 skip)
        git am --skip --resolvemsg="$resolvemsg" &&
        move_to_original_branch
-       exit
+       return
        ;;
 esac
 
@@ -56,7 +56,7 @@ else
 
                As a result, git cannot rebase them.
                EOF
-               exit $?
+               return $?
        fi
 
        git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" <"$GIT_DIR/rebased-patches"
@@ -68,7 +68,7 @@ fi
 if test 0 != $ret
 then
        test -d "$state_dir" && write_basic_state
-       exit $ret
+       return $ret
 fi
 
 move_to_original_branch
index 048a140a6f6dd565a0d0d98b3b41439298626a96..10bf318d0d4ecc03dcf52d3f665410c48d26bdfd 100644 (file)
@@ -57,6 +57,9 @@ rewritten="$state_dir"/rewritten
 
 dropped="$state_dir"/dropped
 
+end="$state_dir"/end
+msgnum="$state_dir"/msgnum
+
 # A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
 # GIT_AUTHOR_DATE that will be used for the commit that is currently
 # being rebased.
@@ -77,6 +80,18 @@ amend="$state_dir"/amend
 rewritten_list="$state_dir"/rewritten-list
 rewritten_pending="$state_dir"/rewritten-pending
 
+strategy_args=
+if test -n "$do_merge"
+then
+       strategy_args=${strategy:+--strategy=$strategy}
+       eval '
+               for strategy_opt in '"$strategy_opts"'
+               do
+                       strategy_args="$strategy_args -X$(git rev-parse --sq-quote "${strategy_opt#--}")"
+               done
+       '
+fi
+
 GIT_CHERRY_PICK_HELP="$resolvemsg"
 export GIT_CHERRY_PICK_HELP
 
@@ -109,7 +124,9 @@ mark_action_done () {
        sed -e 1d < "$todo" >> "$todo".new
        mv -f "$todo".new "$todo"
        new_count=$(git stripspace --strip-comments <"$done" | wc -l)
+       echo $new_count >"$msgnum"
        total=$(($new_count + $(git stripspace --strip-comments <"$todo" | wc -l)))
+       echo $total >"$end"
        if test "$last_count" != "$new_count"
        then
                last_count=$new_count
@@ -234,7 +251,7 @@ pick_one () {
 
        test -d "$rewritten" &&
                pick_one_preserving_merges "$@" && return
-       output git cherry-pick $empty_args $ff "$@"
+       output eval git cherry-pick "$strategy_args" $empty_args $ff "$@"
 }
 
 pick_one_preserving_merges () {
@@ -335,9 +352,9 @@ pick_one_preserving_merges () {
                        msg_content="$(commit_message $sha1)"
                        # No point in merging the first parent, that's HEAD
                        new_parents=${new_parents# $first_parent}
-                       if ! do_with_author output \
-                               git merge --no-ff ${strategy:+-s $strategy} -m \
-                                       "$msg_content" $new_parents
+                       merge_args="--no-log --no-ff"
+                       if ! do_with_author output eval \
+                       'git merge $merge_args $strategy_args -m "$msg_content" $new_parents'
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
                                die_with_patch $sha1 "Error redoing merge $sha1"
@@ -345,7 +362,7 @@ pick_one_preserving_merges () {
                        echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
                        ;;
                *)
-                       output git cherry-pick "$@" ||
+                       output eval git cherry-pick "$strategy_args" "$@" ||
                                die_with_patch $sha1 "Could not pick $sha1"
                        ;;
                esac
@@ -623,17 +640,16 @@ do_next () {
                "$GIT_DIR"/hooks/post-rewrite rebase < "$rewritten_list"
                true # we don't care if this hook failed
        fi &&
-       rm -rf "$state_dir" &&
-       git gc --auto &&
        warn "Successfully rebased and updated $head_name."
 
-       exit
+       return 1 # not failure; just to break the do_rest loop
 }
 
+# can only return 0, when the infinite loop breaks
 do_rest () {
        while :
        do
-               do_next
+               do_next || break
        done
 }
 
@@ -656,7 +672,7 @@ skip_unnecessary_picks () {
                                ;;
                        esac
                        ;;
-               3,#*|3,)
+               3,"$comment_char"*|3,)
                        # copy comments
                        ;;
                *)
@@ -674,6 +690,32 @@ skip_unnecessary_picks () {
        die "Could not skip unnecessary pick commands"
 }
 
+transform_todo_ids () {
+       while read -r command rest
+       do
+               case "$command" in
+               "$comment_char"* | exec)
+                       # Be careful for oddball commands like 'exec'
+                       # that do not have a SHA-1 at the beginning of $rest.
+                       ;;
+               *)
+                       sha1=$(git rev-parse --verify --quiet "$@" ${rest%% *}) &&
+                       rest="$sha1 ${rest#* }"
+                       ;;
+               esac
+               printf '%s\n' "$command${rest:+ }$rest"
+       done <"$todo" >"$todo.new" &&
+       mv -f "$todo.new" "$todo"
+}
+
+expand_todo_ids() {
+       transform_todo_ids
+}
+
+collapse_todo_ids() {
+       transform_todo_ids --short=7
+}
+
 # Rearrange the todo list that has both "pick sha1 msg" and
 # "pick sha1 fixup!/squash! msg" appears in it so that the latter
 # comes immediately after the former, and change "pick" to
@@ -685,8 +727,22 @@ rearrange_squash () {
                case "$message" in
                "squash! "*|"fixup! "*)
                        action="${message%%!*}"
-                       rest="${message#*! }"
-                       echo "$sha1 $action $rest"
+                       rest=$message
+                       prefix=
+                       # skip all squash! or fixup! (but save for later)
+                       while :
+                       do
+                               case "$rest" in
+                               "squash! "*|"fixup! "*)
+                                       prefix="$prefix${rest%%!*},"
+                                       rest="${rest#*! }"
+                                       ;;
+                               *)
+                                       break
+                                       ;;
+                               esac
+                       done
+                       echo "$sha1 $action $prefix $rest"
                        # if it's a single word, try to resolve to a full sha1 and
                        # emit a second copy. This allows us to match on both message
                        # and on sha1 prefix
@@ -695,7 +751,7 @@ rearrange_squash () {
                                if test -n "$fullsha"; then
                                        # prefix the action to uniquely identify this line as
                                        # intended for full sha1 match
-                                       echo "$sha1 +$action $fullsha"
+                                       echo "$sha1 +$action $prefix $fullsha"
                                fi
                        fi
                esac
@@ -710,7 +766,7 @@ rearrange_squash () {
                esac
                printf '%s\n' "$pick $sha1 $message"
                used="$used$sha1 "
-               while read -r squash action msg_content
+               while read -r squash action msg_prefix msg_content
                do
                        case " $used" in
                        *" $squash "*) continue ;;
@@ -726,7 +782,8 @@ rearrange_squash () {
                                case "$message" in "$msg_content"*) emit=1;; esac ;;
                        esac
                        if test $emit = 1; then
-                               printf '%s\n' "$action $squash $action! $msg_content"
+                               real_prefix=$(echo "$msg_prefix" | sed "s/,/! /g")
+                               printf '%s\n' "$action $squash ${real_prefix}$msg_content"
                                used="$used$squash "
                        fi
                done <"$1.sq"
@@ -800,15 +857,18 @@ first and then run 'git rebase --continue' again."
 
        require_clean_work_tree "rebase"
        do_rest
+       return 0
        ;;
 skip)
        git rerere clear
 
        do_rest
+       return 0
        ;;
 edit-todo)
        git stripspace --strip-comments <"$todo" >"$todo".new
        mv -f "$todo".new "$todo"
+       collapse_todo_ids
        append_todo_help
        git stripspace --comment-lines >>"$todo" <<\EOF
 
@@ -820,6 +880,7 @@ EOF
 
        git_sequence_editor "$todo" ||
                die "Could not execute editor"
+       expand_todo_ids
 
        exit
        ;;
@@ -832,12 +893,15 @@ comment_for_reflog start
 
 if test ! -z "$switch_to"
 then
+       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to"
        output git checkout "$switch_to" -- ||
-               die "Could not checkout $switch_to"
+       die "Could not checkout $switch_to"
+
+       comment_for_reflog start
 fi
 
 orig_head=$(git rev-parse --verify HEAD) || die "No HEAD?"
-mkdir "$state_dir" || die "Could not create temporary $state_dir"
+mkdir -p "$state_dir" || die "Could not create temporary $state_dir"
 
 : > "$state_dir"/interactive || die "Could not mark as interactive"
 write_basic_state
@@ -973,8 +1037,11 @@ git_sequence_editor "$todo" ||
 has_action "$todo" ||
        die_abort "Nothing to do"
 
+expand_todo_ids
+
 test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
 
+GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
 output git checkout $onto || die_abort "could not detach HEAD"
 git update-ref ORIG_HEAD $orig_head
 do_rest
index b10f2cf21b904bbbc3fafc27d183d8ad7917b479..16d18176ece5eecf4a3b0b0a9dea0a5b41ac43d4 100644 (file)
@@ -96,7 +96,6 @@ finish_rb_merge () {
                        "$GIT_DIR"/hooks/post-rewrite rebase <"$state_dir"/rewritten
                fi
        fi
-       rm -r "$state_dir"
        say All done.
 }
 
@@ -110,7 +109,7 @@ continue)
                continue_merge
        done
        finish_rb_merge
-       exit
+       return
        ;;
 skip)
        read_state
@@ -122,7 +121,7 @@ skip)
                continue_merge
        done
        finish_rb_merge
-       exit
+       return
        ;;
 esac
 
index b2f1c76dc3ede707870564a8ba73af6d1753e95c..226752fbff62f4f27da95f7d711c898503fd7148 100755 (executable)
@@ -13,6 +13,7 @@ git-rebase --continue | --abort | --skip | --edit-todo
  Available options are
 v,verbose!         display a diffstat of what changed upstream
 q,quiet!           be quiet. implies --no-stat
+autostash!         automatically stash/stash pop before and after
 onto=!             rebase onto given branch instead of upstream
 p,preserve-merges! try to recreate merges instead of ignoring them
 s,strategy=!       use the given merge strategy
@@ -64,6 +65,7 @@ apply_dir="$GIT_DIR"/rebase-apply
 verbose=
 diffstat=
 test "$(git config --bool rebase.stat)" = true && diffstat=t
+autostash="$(git config --bool rebase.autostash || echo false)"
 git_am_opt=
 rebase_root=
 force_rebase=
@@ -82,6 +84,8 @@ keep_empty=
 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 
 read_basic_state () {
+       test -f "$state_dir/head-name" &&
+       test -f "$state_dir/onto" &&
        head_name=$(cat "$state_dir"/head-name) &&
        onto=$(cat "$state_dir"/onto) &&
        # We always write to orig-head, but interactive rebase used to write to
@@ -143,15 +147,50 @@ move_to_original_branch () {
        esac
 }
 
-run_specific_rebase () {
+finish_rebase () {
+       if test -f "$state_dir/autostash"
+       then
+               stash_sha1=$(cat "$state_dir/autostash")
+               if git stash apply $stash_sha1 2>&1 >/dev/null
+               then
+                       echo "$(gettext 'Applied autostash.')"
+               else
+                       git stash store -m "autostash" -q $stash_sha1 ||
+                       die "$(eval_gettext "Cannot store \$stash_sha1")"
+                       gettext 'Applying autostash resulted in conflicts.
+Your changes are safe in the stash.
+You can run "git stash pop" or "git stash drop" at any time.
+'
+               fi
+       fi
+       git gc --auto &&
+       rm -rf "$state_dir"
+}
+
+run_specific_rebase_internal () {
        if [ "$interactive_rebase" = implied ]; then
                GIT_EDITOR=:
                export GIT_EDITOR
                autosquash=
        fi
+       # On FreeBSD, the shell's "return" returns from the current
+       # function, not from the current file inclusion.
+       # run_specific_rebase_internal has the file inclusion as a
+       # last statement, so POSIX and FreeBSD's return will do the
+       # same thing.
        . git-rebase--$type
 }
 
+run_specific_rebase () {
+       run_specific_rebase_internal
+       ret=$?
+       if test $ret -eq 0
+       then
+               finish_rebase
+       fi
+       exit $ret
+}
+
 run_pre_rebase_hook () {
        if test -z "$ok_to_skip_pre_rebase" &&
           test -x "$GIT_DIR/hooks/pre-rebase"
@@ -241,6 +280,9 @@ do
        --stat)
                diffstat=t
                ;;
+       --autostash)
+               autostash=true
+               ;;
        -v)
                verbose=t
                diffstat=t
@@ -341,7 +383,7 @@ abort)
                ;;
        esac
        output git reset --hard $orig_head
-       rm -r "$state_dir"
+       finish_rebase
        exit
        ;;
 edit-todo)
@@ -400,7 +442,7 @@ then
                shift
                ;;
        esac
-       upstream=`git rev-parse --verify "${upstream_name}^0"` ||
+       upstream=$(peel_committish "${upstream_name}") ||
        die "$(eval_gettext "invalid upstream \$upstream_name")"
        upstream_arg="$upstream_name"
 else
@@ -436,7 +478,7 @@ case "$onto_name" in
        fi
        ;;
 *)
-       onto=$(git rev-parse --verify "${onto_name}^0") ||
+       onto=$(peel_committish "$onto_name") ||
        die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
        ;;
 esac
@@ -473,13 +515,25 @@ case "$#" in
                head_name="detached HEAD"
                branch_name=HEAD ;# detached
        fi
-       orig_head=$(git rev-parse --verify "${branch_name}^0") || exit
+       orig_head=$(git rev-parse --verify HEAD) || exit
        ;;
 *)
        die "BUG: unexpected number of arguments left to parse"
        ;;
 esac
 
+if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
+then
+       stash_sha1=$(git stash create "autostash") ||
+       die "$(gettext 'Cannot autostash')"
+
+       mkdir -p "$state_dir" &&
+       echo $stash_sha1 >"$state_dir/autostash" &&
+       stash_abbrev=$(git rev-parse --short $stash_sha1) &&
+       echo "$(eval_gettext 'Created autostash: $stash_abbrev')" &&
+       git reset --hard
+fi
+
 require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
 
 # Now we are rebasing commits $upstream..$orig_head (or with --root,
@@ -497,8 +551,11 @@ then
        if test -z "$force_rebase"
        then
                # Lazily switch to the target branch if needed...
-               test -z "$switch_to" || git checkout "$switch_to" --
+               test -z "$switch_to" ||
+               GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
+                       git checkout "$switch_to" --
                say "$(eval_gettext "Current branch \$branch_name is up to date.")"
+               finish_rebase
                exit 0
        else
                say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
@@ -522,7 +579,9 @@ test "$type" = interactive && run_specific_rebase
 
 # Detach HEAD and reset the tree
 say "$(gettext "First, rewinding head to replay your work on top of it...")"
-git checkout -q "$onto^0" || die "could not detach HEAD"
+
+GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
+       git checkout -q "$onto^0" || die "could not detach HEAD"
 git update-ref ORIG_HEAD $orig_head
 
 # If the $onto is a proper descendant of the tip of the branch, then
@@ -531,6 +590,7 @@ if test "$mb" = "$orig_head"
 then
        say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
        move_to_original_branch
+       finish_rebase
        exit 0
 fi
 
similarity index 55%
rename from git-remote-testgit
rename to git-remote-testgit.sh
index b395c8de59c33768f1a957248447d162bda8ef94..2109070d00411dbd9f1ba70e55587091d50a8a08 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/bin/sh
 # Copyright (c) 2012 Felipe Contreras
 
 alias=$1
@@ -23,7 +23,6 @@ then
        testgitmarks="$dir/testgit.marks"
        test -e "$gitmarks" || >"$gitmarks"
        test -e "$testgitmarks" || >"$testgitmarks"
-       testgitmarks_args=( "--"{import,export}"-marks=$testgitmarks" )
 fi
 
 while read line
@@ -38,6 +37,7 @@ do
                        echo "*import-marks $gitmarks"
                        echo "*export-marks $gitmarks"
                fi
+               test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
                echo
                ;;
        list)
@@ -61,24 +61,55 @@ do
                        echo "feature import-marks=$gitmarks"
                        echo "feature export-marks=$gitmarks"
                fi
+
+               if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
+               then
+                       echo "feature done"
+                       exit 1
+               fi
+
                echo "feature done"
-               git fast-export "${testgitmarks_args[@]}" $refs |
+               git fast-export \
+                       ${testgitmarks:+"--import-marks=$testgitmarks"} \
+                       ${testgitmarks:+"--export-marks=$testgitmarks"} \
+                       $refs |
                sed -e "s#refs/heads/#${prefix}/heads/#g"
                echo "done"
                ;;
        export)
-               before=$(git for-each-ref --format='%(refname) %(objectname)')
+               if test -n "$GIT_REMOTE_TESTGIT_FAILURE"
+               then
+                       # consume input so fast-export doesn't get SIGPIPE;
+                       # git would also notice that case, but we want
+                       # to make sure we are exercising the later
+                       # error checks
+                       while read line; do
+                               test "done" = "$line" && break
+                       done
+                       exit 1
+               fi
 
-               git fast-import "${testgitmarks_args[@]}" --quiet
+               before=$(git for-each-ref --format=' %(refname) %(objectname) ')
 
-               after=$(git for-each-ref --format='%(refname) %(objectname)')
+               git fast-import \
+                       ${testgitmarks:+"--import-marks=$testgitmarks"} \
+                       ${testgitmarks:+"--export-marks=$testgitmarks"} \
+                       --quiet
 
                # figure out which refs were updated
-               join -e 0 -o '0 1.2 2.2' -a 2 <(echo "$before") <(echo "$after") |
-               while read ref a b
+               git for-each-ref --format='%(refname) %(objectname)' |
+               while read ref a
                do
-                       test $a == $b && continue
-                       echo "ok $ref"
+                       case "$before" in
+                       *" $ref $a "*)
+                               continue ;;     # unchanged
+                       esac
+                       if test -z "$GIT_REMOTE_TESTGIT_PUSH_ERROR"
+                       then
+                               echo "ok $ref"
+                       else
+                               echo "error $ref $GIT_REMOTE_TESTGIT_PUSH_ERROR"
+                       fi
                done
 
                echo
index d566015975d317683c89bfcfde93ae998efd1058..ebf1269d297cbc9513cb6b52726ae4590520ae92 100755 (executable)
@@ -51,8 +51,18 @@ fi
 tag_name=$(git describe --exact "$head^0" 2>/dev/null)
 
 test -n "$base" && test -n "$url" || usage
-baserev=$(git rev-parse --verify "$base"^0) &&
-headrev=$(git rev-parse --verify "$head"^0) || exit
+
+baserev=$(git rev-parse --verify --quiet "$base"^0)
+if test -z "$baserev"
+then
+    die "fatal: Not a valid revision: $base"
+fi
+
+headrev=$(git rev-parse --verify --quiet "$head"^0)
+if test -z "$headrev"
+then
+    die "fatal: Not a valid revision: $head"
+fi
 
 merge_base=$(git merge-base $baserev $headrev) ||
 die "fatal: No commits in common between $base and $head"
index bd13cc812d2a0115edcdd2c3ec146665ddfc5e29..3782c3b0cb4f9356790ae581c0d408dc4f4cae75 100755 (executable)
@@ -69,6 +69,9 @@ git send-email [options] <file | directory | rev-list options >
     --smtp-pass             <str>  * Password for SMTP-AUTH; not necessary.
     --smtp-encryption       <str>  * tls or ssl; anything else disables.
     --smtp-ssl                     * Deprecated. Use '--smtp-encryption ssl'.
+    --smtp-ssl-cert-path    <str>  * Path to ca-certificates (either directory or file).
+                                     Pass an empty string to disable certificate
+                                     verification.
     --smtp-domain           <str>  * The domain name sent to HELO/EHLO handshake
     --smtp-debug            <0|1>  * Disable, enable Net::SMTP debug.
 
@@ -194,7 +197,7 @@ sub do_edit {
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
 my ($to_cmd, $cc_cmd);
 my ($smtp_server, $smtp_server_port, @smtp_server_options);
-my ($smtp_authuser, $smtp_encryption);
+my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
 my ($identity, $aliasfiletype, @alias_files, $smtp_domain);
 my ($validate, $confirm);
 my (@suppress_cc);
@@ -203,11 +206,9 @@ my ($compose_encoding);
 
 my ($debug_net_smtp) = 0;              # Net::SMTP, see send_message()
 
-my $not_set_by_user = "true but not set by the user";
-
 my %config_bool_settings = (
     "thread" => [\$thread, 1],
-    "chainreplyto" => [\$chain_reply_to, $not_set_by_user],
+    "chainreplyto" => [\$chain_reply_to, 0],
     "suppressfrom" => [\$suppress_from, undef],
     "signedoffbycc" => [\$signed_off_by_cc, undef],
     "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
@@ -222,6 +223,7 @@ my %config_settings = (
     "smtpserveroption" => \@smtp_server_options,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
+    "smtpsslcertpath" => \$smtp_ssl_cert_path,
     "smtpdomain" => \$smtp_domain,
     "to" => \@initial_to,
     "tocmd" => \$to_cmd,
@@ -241,19 +243,6 @@ my %config_path_settings = (
     "aliasesfile" => \@alias_files,
 );
 
-# Help users prepare for 1.7.0
-sub chain_reply_to {
-       if (defined $chain_reply_to &&
-           $chain_reply_to eq $not_set_by_user) {
-               print STDERR
-                   "In git 1.7.0, the default has changed to --no-chain-reply-to\n" .
-                   "Set sendemail.chainreplyto configuration variable to true if\n" .
-                   "you want to keep --chain-reply-to as your default.\n";
-               $chain_reply_to = 0;
-       }
-       return $chain_reply_to;
-}
-
 # Handle Uncouth Termination
 sub signal_handler {
 
@@ -302,6 +291,7 @@ my $rc = GetOptions("h" => \$help,
                    "smtp-pass:s" => \$smtp_authpass,
                    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
                    "smtp-encryption=s" => \$smtp_encryption,
+                   "smtp-ssl-cert-path" => \$smtp_ssl_cert_path,
                    "smtp-debug:i" => \$debug_net_smtp,
                    "smtp-domain:s" => \$smtp_domain,
                    "identity=s" => \$identity,
@@ -760,6 +750,11 @@ if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
 }
 
+# $sender could be an already sanitized address
+# (e.g. sendemail.from could be manually sanitized by user).
+# But it's a no-op to run sanitize_address on an already sanitized address.
+$sender = sanitize_address($sender);
+
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
        my $to = ask("Who should the emails be sent to (if any)? ",
@@ -1089,6 +1084,34 @@ sub smtp_auth_maybe {
        return $auth;
 }
 
+sub ssl_verify_params {
+       eval {
+               require IO::Socket::SSL;
+               IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);
+       };
+       if ($@) {
+               print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";
+               return;
+       }
+
+       if (!defined $smtp_ssl_cert_path) {
+               $smtp_ssl_cert_path = "/etc/ssl/certs";
+       }
+
+       if ($smtp_ssl_cert_path eq "") {
+               return (SSL_verify_mode => SSL_VERIFY_NONE());
+       } elsif (-d $smtp_ssl_cert_path) {
+               return (SSL_verify_mode => SSL_VERIFY_PEER(),
+                       SSL_ca_path => $smtp_ssl_cert_path);
+       } elsif (-f $smtp_ssl_cert_path) {
+               return (SSL_verify_mode => SSL_VERIFY_PEER(),
+                       SSL_ca_file => $smtp_ssl_cert_path);
+       } else {
+               print STDERR "Not using SSL_VERIFY_PEER because the CA path does not exist.\n";
+               return (SSL_verify_mode => SSL_VERIFY_NONE());
+       }
+}
+
 # Returns 1 if the message was sent, and 0 otherwise.
 # In actuality, the whole program dies when there
 # is an error sending a message.
@@ -1113,10 +1136,9 @@ sub send_message {
        if ($cc ne '') {
                $ccline = "\nCc: $cc";
        }
-       my $sanitized_sender = sanitize_address($sender);
        make_message_id() unless defined($message_id);
 
-       my $header = "From: $sanitized_sender
+       my $header = "From: $sender
 To: $to${ccline}
 Subject: $subject
 Date: $date
@@ -1133,7 +1155,7 @@ X-Mailer: git-send-email $gitversion
        }
 
        my @sendmail_parameters = ('-i', @recipients);
-       my $raw_from = $sanitized_sender;
+       my $raw_from = $sender;
        if (defined $envelope_sender && $envelope_sender ne "auto") {
                $raw_from = $envelope_sender;
        }
@@ -1194,21 +1216,25 @@ X-Mailer: git-send-email $gitversion
                        $smtp_domain ||= maildomain();
                        $smtp ||= Net::SMTP::SSL->new($smtp_server,
                                                      Hello => $smtp_domain,
-                                                     Port => $smtp_server_port);
+                                                     Port => $smtp_server_port,
+                                                     ssl_verify_params());
                }
                else {
                        require Net::SMTP;
                        $smtp_domain ||= maildomain();
-                       $smtp ||= Net::SMTP->new(smtp_host_string(),
+                       $smtp_server_port ||= 25;
+                       $smtp ||= Net::SMTP->new($smtp_server,
                                                 Hello => $smtp_domain,
-                                                Debug => $debug_net_smtp);
+                                                Debug => $debug_net_smtp,
+                                                Port => $smtp_server_port);
                        if ($smtp_encryption eq 'tls' && $smtp) {
                                require Net::SMTP::SSL;
                                $smtp->command('STARTTLS');
                                $smtp->response();
                                if ($smtp->code == 220) {
-                                       $smtp = Net::SMTP::SSL->start_SSL($smtp)
-                                               or die "STARTTLS failed! ".$smtp->message;
+                                       $smtp = Net::SMTP::SSL->start_SSL($smtp,
+                                                                         ssl_verify_params())
+                                               or die "STARTTLS failed! ".IO::Socket::SSL::errstr();
                                        $smtp_encryption = '';
                                        # Send EHLO again to receive fresh
                                        # supported commands
@@ -1270,6 +1296,7 @@ foreach my $t (@files) {
        open my $fh, "<", $t or die "can't open file $t";
 
        my $author = undef;
+       my $sauthor = undef;
        my $author_encoding;
        my $has_content_type;
        my $body_encoding;
@@ -1308,8 +1335,9 @@ foreach my $t (@files) {
                        }
                        elsif (/^From:\s+(.*)$/i) {
                                ($author, $author_encoding) = unquote_rfc2047($1);
+                               $sauthor = sanitize_address($author);
                                next if $suppress_cc{'author'};
-                               next if $suppress_cc{'self'} and $author eq $sender;
+                               next if $suppress_cc{'self'} and $sauthor eq $sender;
                                printf("(mbox) Adding cc: %s from line '%s'\n",
                                        $1, $_) unless $quiet;
                                push @cc, $1;
@@ -1323,7 +1351,9 @@ foreach my $t (@files) {
                        }
                        elsif (/^Cc:\s+(.*)$/i) {
                                foreach my $addr (parse_address_line($1)) {
-                                       if (unquote_rfc2047($addr) eq $sender) {
+                                       my $qaddr = unquote_rfc2047($addr);
+                                       my $saddr = sanitize_address($qaddr);
+                                       if ($saddr eq $sender) {
                                                next if ($suppress_cc{'self'});
                                        } else {
                                                next if ($suppress_cc{'cc'});
@@ -1370,7 +1400,8 @@ foreach my $t (@files) {
                        chomp;
                        my ($what, $c) = ($1, $2);
                        chomp $c;
-                       if ($c eq $sender) {
+                       my $sc = sanitize_address($c);
+                       if ($sc eq $sender) {
                                next if ($suppress_cc{'self'});
                        } else {
                                next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i;
@@ -1400,7 +1431,7 @@ foreach my $t (@files) {
                $subject = quote_subject($subject, $auto_8bit_encoding);
        }
 
-       if (defined $author and $author ne $sender) {
+       if (defined $sauthor and $sauthor ne $sender) {
                $message = "From: $author\n\n$message";
                if (defined $author_encoding) {
                        if ($has_content_type) {
@@ -1437,7 +1468,7 @@ foreach my $t (@files) {
 
        # set up for the next message
        if ($thread && $message_was_sent &&
-               (chain_reply_to() || !defined $reply_to || length($reply_to) == 0 ||
+               ($chain_reply_to || !defined $reply_to || length($reply_to) == 0 ||
                $message_num == 1)) {
                $reply_to = $message_id;
                if (length $references > 0) {
@@ -1454,7 +1485,6 @@ foreach my $t (@files) {
 sub recipients_cmd {
        my ($prefix, $what, $cmd, $file) = @_;
 
-       my $sanitized_sender = sanitize_address($sender);
        my @addresses = ();
        open my $fh, "-|", "$cmd \Q$file\E"
            or die "($prefix) Could not execute '$cmd'";
@@ -1462,7 +1492,7 @@ sub recipients_cmd {
                $address =~ s/^\s*//g;
                $address =~ s/\s*$//g;
                $address = sanitize_address($address);
-               next if ($address eq $sanitized_sender and $suppress_from);
+               next if ($address eq $sender and $suppress_cc{'self'});
                push @addresses, $address;
                printf("($prefix) Adding %s: %s from: '%s'\n",
                       $what, $address, $cmd) unless $quiet;
index 2f7835941ecaa48be17b6dd64e64fb0df3db37e2..e15be516369e5e5a9096bb957966938e134e616c 100644 (file)
@@ -53,7 +53,7 @@ die () {
 die_with_status () {
        status=$1
        shift
-       echo >&2 "$*"
+       printf >&2 '%s\n' "$*"
        exit "$status"
 }
 
@@ -313,3 +313,15 @@ then
        }
        : ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
 fi
+
+peel_committish () {
+       case "$1" in
+       :/*)
+               peeltmp=$(git rev-parse --verify "$1") &&
+               git rev-parse --verify "${peeltmp}^0"
+               ;;
+       *)
+               git rev-parse --verify "${1}^0"
+               ;;
+       esac
+}
index bbefdf64244063f4708f381b8a772466d64e90e5..1e541a21257c70fa67c0fee552ccb05fc5b6a280 100755 (executable)
@@ -156,6 +156,41 @@ create_stash () {
        die "$(gettext "Cannot record working tree state")"
 }
 
+store_stash () {
+       while test $# != 0
+       do
+               case "$1" in
+               -m|--message)
+                       shift
+                       stash_msg="$1"
+                       ;;
+               -q|--quiet)
+                       quiet=t
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+       test $# = 1 ||
+       die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"
+
+       w_commit="$1"
+       if test -z "$stash_msg"
+       then
+               stash_msg="Created via \"git stash store\"."
+       fi
+
+       # Make sure the reflog for stash is kept.
+       : >>"$GIT_DIR/logs/$ref_stash"
+       git update-ref -m "$stash_msg" $ref_stash $w_commit
+       ret=$?
+       test $ret != 0 && test -z $quiet &&
+       die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
+       return $ret
+}
+
 save_stash () {
        keep_index=
        patch_mode=
@@ -227,12 +262,8 @@ save_stash () {
                clear_stash || die "$(gettext "Cannot initialize stash")"
 
        create_stash "$stash_msg" $untracked
-
-       # Make sure the reflog for stash is kept.
-       : >>"$GIT_DIR/logs/$ref_stash"
-
-       git update-ref -m "$stash_msg" $ref_stash $w_commit ||
-               die "$(gettext "Cannot save the current status")"
+       store_stash -m "$stash_msg" -q $w_commit ||
+       die "$(gettext "Cannot save the current status")"
        say Saved working directory and index state "$stash_msg"
 
        if test -z "$patch_mode"
@@ -546,12 +577,13 @@ clear)
        clear_stash "$@"
        ;;
 create)
-       if test $# -gt 0 && test "$1" = create
-       then
-               shift
-       fi
+       shift
        create_stash "$*" && echo "$w_commit"
        ;;
+store)
+       shift
+       store_stash "$@"
+       ;;
 drop)
        shift
        drop_stash "$@"
index 79bfaac9d4cb9a04e5e1fd7675d740cf9fc27e87..2979197087f2c6d97e2945008394d50c16a195a5 100755 (executable)
@@ -14,10 +14,13 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re
    or: $dashless [--quiet] foreach [--recursive] <command>
    or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
 OPTIONS_SPEC=
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 . git-sh-i18n
 . git-parse-remote
 require_work_tree
+wt_prefix=$(git rev-parse --show-prefix)
+cd_to_toplevel
 
 command=
 branch=
@@ -32,6 +35,7 @@ nofetch=
 update=
 prefix=
 custom_name=
+depth=
 
 # The function takes at most 2 arguments. The first argument is the
 # URL that navigates to the submodule origin repo. When relative, this URL
@@ -106,14 +110,50 @@ resolve_relative_url ()
        echo "${is_relative:+${up_path}}${remoteurl#./}"
 }
 
+# Resolve a path to be relative to another path.  This is intended for
+# converting submodule paths when git-submodule is run in a subdirectory
+# and only handles paths where the directory separator is '/'.
+#
+# The output is the first argument as a path relative to the second argument,
+# which defaults to $wt_prefix if it is omitted.
+relative_path ()
+{
+       local target curdir result
+       target=$1
+       curdir=${2-$wt_prefix}
+       curdir=${curdir%/}
+       result=
+
+       while test -n "$curdir"
+       do
+               case "$target" in
+               "$curdir/"*)
+                       target=${target#"$curdir"/}
+                       break
+                       ;;
+               esac
+
+               result="${result}../"
+               if test "$curdir" = "${curdir%/*}"
+               then
+                       curdir=
+               else
+                       curdir="${curdir%/*}"
+               fi
+       done
+
+       echo "$result$target"
+}
+
 #
 # Get submodule info for registered submodules
 # $@ = path to limit submodule list
 #
 module_list()
 {
+       eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
        (
-               git ls-files --error-unmatch --stage -- "$@" ||
+               git ls-files -z --error-unmatch --stage -- "$@" ||
                echo "unmatched pathspec exists"
        ) |
        perl -e '
@@ -121,6 +161,7 @@ module_list()
        my ($null_sha1) = ("0" x 40);
        my @out = ();
        my $unmatched = 0;
+       $/ = "\0";
        while (<STDIN>) {
                if (/^unmatched pathspec/) {
                        $unmatched = 1;
@@ -211,6 +252,7 @@ module_clone()
        name=$2
        url=$3
        reference="$4"
+       depth="$5"
        quiet=
        if test -n "$GIT_QUIET"
        then
@@ -233,7 +275,7 @@ module_clone()
                mkdir -p "$gitdir_base"
                (
                        clear_local_git_env
-                       git clone $quiet -n ${reference:+"$reference"} \
+                       git clone $quiet ${depth:+"$depth"} -n ${reference:+"$reference"} \
                                --separate-git-dir "$gitdir" "$url" "$sm_path"
                ) ||
                die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
@@ -282,6 +324,7 @@ isnumber()
 cmd_add()
 {
        # parse $args after "submodule ... add".
+       reference_path=
        while test $# -ne 0
        do
                case "$1" in
@@ -298,17 +341,25 @@ cmd_add()
                        ;;
                --reference)
                        case "$2" in '') usage ;; esac
-                       reference="--reference=$2"
+                       reference_path=$2
                        shift
                        ;;
                --reference=*)
-                       reference="$1"
+                       reference_path="${1#--reference=}"
                        ;;
                --name)
                        case "$2" in '') usage ;; esac
                        custom_name=$2
                        shift
                        ;;
+               --depth)
+                       case "$2" in '') usage ;; esac
+                       depth="--depth=$2"
+                       shift
+                       ;;
+               --depth=*)
+                       depth=$1
+                       ;;
                --)
                        shift
                        break
@@ -323,6 +374,14 @@ cmd_add()
                shift
        done
 
+       if test -n "$reference_path"
+       then
+               is_absolute_path "$reference_path" ||
+               reference_path="$wt_prefix$reference_path"
+
+               reference="--reference=$reference_path"
+       fi
+
        repo=$1
        sm_path=$2
 
@@ -335,9 +394,14 @@ cmd_add()
                usage
        fi
 
+       is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
+
        # assure repo is absolute or relative to parent
        case "$repo" in
        ./*|../*)
+               test -z "$wt_prefix" ||
+               die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
+
                # dereference source url relative to parent's url
                realrepo=$(resolve_relative_url "$repo") || exit
                ;;
@@ -405,7 +469,7 @@ Use -f if you really want to add it." >&2
                                echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
                        fi
                fi
-               module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" || exit
+               module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" || exit
                (
                        clear_local_git_env
                        cd "$sm_path" &&
@@ -471,21 +535,23 @@ cmd_foreach()
                die_if_unmatched "$mode"
                if test -e "$sm_path"/.git
                then
-                       say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
+                       displaypath=$(relative_path "$sm_path")
+                       say "$(eval_gettext "Entering '\$prefix\$displaypath'")"
                        name=$(module_name "$sm_path")
                        (
                                prefix="$prefix$sm_path/"
                                clear_local_git_env
-                               # we make $path available to scripts ...
-                               path=$sm_path
                                cd "$sm_path" &&
+                               sm_path=$(relative_path "$sm_path") &&
+                               # we make $path available to scripts ...
+                               path=$sm_path &&
                                eval "$@" &&
                                if test -n "$recursive"
                                then
                                        cmd_foreach "--recursive" "$@"
                                fi
                        ) <&3 3<&- ||
-                       die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")"
+                       die "$(eval_gettext "Stopping at '\$prefix\$displaypath'; script returned non-zero status.")"
                fi
        done
 }
@@ -524,12 +590,14 @@ cmd_init()
                die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
 
+               displaypath=$(relative_path "$sm_path")
+
                # Copy url setting when it is not set yet
                if test -z "$(git config "submodule.$name.url")"
                then
                        url=$(git config -f .gitmodules submodule."$name".url)
                        test -z "$url" &&
-                       die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
+                       die "$(eval_gettext "No url found for submodule path '\$displaypath' in .gitmodules")"
 
                        # Possibly a url relative to parent
                        case "$url" in
@@ -538,9 +606,9 @@ cmd_init()
                                ;;
                        esac
                        git config submodule."$name".url "$url" ||
-                       die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
+                       die "$(eval_gettext "Failed to register url for submodule path '\$displaypath'")"
 
-                       say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")"
+                       say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$displaypath'")"
                fi
 
                # Copy "update" setting when it is not set yet
@@ -548,7 +616,7 @@ cmd_init()
                test -z "$upd" ||
                test -n "$(git config submodule."$name".update)" ||
                git config submodule."$name".update "$upd" ||
-               die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")"
+               die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")"
        done
 }
 
@@ -594,27 +662,29 @@ cmd_deinit()
                die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
 
+               displaypath=$(relative_path "$sm_path")
+
                # Remove the submodule work tree (unless the user already did it)
                if test -d "$sm_path"
                then
                        # Protect submodules containing a .git directory
                        if test -d "$sm_path/.git"
                        then
-                               echo >&2 "$(eval_gettext "Submodule work tree '\$sm_path' contains a .git directory")"
+                               echo >&2 "$(eval_gettext "Submodule work tree '\$displaypath' contains a .git directory")"
                                die "$(eval_gettext "(use 'rm -rf' if you really want to remove it including all of its history)")"
                        fi
 
                        if test -z "$force"
                        then
                                git rm -qn "$sm_path" ||
-                               die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
+                               die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
                        fi
                        rm -rf "$sm_path" &&
-                       say "$(eval_gettext "Cleared directory '\$sm_path'")" ||
-                       say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
+                       say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
+                       say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
                fi
 
-               mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"
+               mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
 
                # Remove the .git/config entries (unless the user already did it)
                if test -n "$(git config --get-regexp submodule."$name\.")"
@@ -623,7 +693,7 @@ cmd_deinit()
                        # the user later decides to init this submodule again
                        url=$(git config submodule."$name".url)
                        git config --remove-section submodule."$name" 2>/dev/null &&
-                       say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$sm_path'")"
+                       say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
                fi
        done
 }
@@ -676,6 +746,14 @@ cmd_update()
                --checkout)
                        update="checkout"
                        ;;
+               --depth)
+                       case "$2" in '') usage ;; esac
+                       depth="--depth=$2"
+                       shift
+                       ;;
+               --depth=*)
+                       depth=$1
+                       ;;
                --)
                        shift
                        break
@@ -717,9 +795,11 @@ cmd_update()
                        update_module=$(git config submodule."$name".update)
                fi
 
+               displaypath=$(relative_path "$prefix$sm_path")
+
                if test "$update_module" = "none"
                then
-                       echo "Skipping submodule '$prefix$sm_path'"
+                       echo "Skipping submodule '$displaypath'"
                        continue
                fi
 
@@ -728,20 +808,20 @@ cmd_update()
                        # Only mention uninitialized submodules when its
                        # path have been specified
                        test "$#" != "0" &&
-                       say "$(eval_gettext "Submodule path '\$prefix\$sm_path' not initialized
+                       say "$(eval_gettext "Submodule path '\$displaypath' not initialized
 Maybe you want to use 'update --init'?")"
                        continue
                fi
 
                if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
                then
-                       module_clone "$sm_path" "$name" "$url" "$reference" || exit
+                       module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
                        cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
                        subsha1=$(clear_local_git_env; cd "$sm_path" &&
                                git rev-parse --verify HEAD) ||
-                       die "$(eval_gettext "Unable to find current revision in submodule path '\$prefix\$sm_path'")"
+                       die "$(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
                fi
 
                if test -n "$remote"
@@ -774,7 +854,7 @@ Maybe you want to use 'update --init'?")"
                                (clear_local_git_env; cd "$sm_path" &&
                                        ( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
                                         test -z "$rev") || git-fetch)) ||
-                               die "$(eval_gettext "Unable to fetch in submodule path '\$prefix\$sm_path'")"
+                               die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
                        fi
 
                        # Is this something we just cloned?
@@ -788,20 +868,26 @@ Maybe you want to use 'update --init'?")"
                        case "$update_module" in
                        rebase)
                                command="git rebase"
-                               die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$prefix\$sm_path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': rebased into '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$displaypath'")"
+                               say_msg="$(eval_gettext "Submodule path '\$displaypath': rebased into '\$sha1'")"
                                must_die_on_failure=yes
                                ;;
                        merge)
                                command="git merge"
-                               die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$prefix\$sm_path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': merged in '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$displaypath'")"
+                               say_msg="$(eval_gettext "Submodule path '\$displaypath': merged in '\$sha1'")"
+                               must_die_on_failure=yes
+                               ;;
+                       !*)
+                               command="${update_module#!}"
+                               die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule  path '\$prefix\$sm_path'")"
+                               say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': '\$command \$sha1'")"
                                must_die_on_failure=yes
                                ;;
                        *)
                                command="git checkout $subforce -q"
-                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$prefix\$sm_path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': checked out '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
+                               say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
                                ;;
                        esac
 
@@ -828,7 +914,7 @@ Maybe you want to use 'update --init'?")"
                        res=$?
                        if test $res -gt 0
                        then
-                               die_msg="$(eval_gettext "Failed to recurse into submodule path '\$prefix\$sm_path'")"
+                               die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
                                if test $res -eq 1
                                then
                                        err="${err};$die_msg"
@@ -942,6 +1028,7 @@ cmd_summary() {
        fi
 
        cd_to_toplevel
+       eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
        # Get modified modules cared by user
        modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
                sane_egrep '^:([0-7]* )?160000' |
@@ -991,16 +1078,18 @@ cmd_summary() {
                ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
                missing_dst=t
 
+               display_name=$(relative_path "$name")
+
                total_commits=
                case "$missing_src,$missing_dst" in
                t,)
-                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_src")"
+                       errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_src")"
                        ;;
                ,t)
-                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commit \$sha1_dst")"
+                       errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commit \$sha1_dst")"
                        ;;
                t,t)
-                       errmsg="$(eval_gettext "  Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")"
+                       errmsg="$(eval_gettext "  Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
                        ;;
                *)
                        errmsg=
@@ -1029,12 +1118,12 @@ cmd_summary() {
                        submodule="$(gettext "submodule")"
                        if test $mod_dst = 160000
                        then
-                               echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
+                               echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
                        else
-                               echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
+                               echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
                        fi
                else
-                       echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
+                       echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
                fi
                if test -n "$errmsg"
                then
@@ -1118,7 +1207,7 @@ cmd_status()
                die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
                url=$(git config submodule."$name".url)
-               displaypath="$prefix$sm_path"
+               displaypath=$(relative_path "$prefix$sm_path")
                if test "$stage" = U
                then
                        say "U$sha1 $displaypath"
@@ -1129,16 +1218,16 @@ cmd_status()
                        say "-$sha1 $displaypath"
                        continue;
                fi
-               set_name_rev "$sm_path" "$sha1"
                if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path"
                then
+                       set_name_rev "$sm_path" "$sha1"
                        say " $sha1 $displaypath$revname"
                else
                        if test -z "$cached"
                        then
                                sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD)
-                               set_name_rev "$sm_path" "$sha1"
                        fi
+                       set_name_rev "$sm_path" "$sha1"
                        say "+$sha1 $displaypath$revname"
                fi
 
@@ -1213,7 +1302,8 @@ cmd_sync()
 
                if git config "submodule.$name.url" >/dev/null 2>/dev/null
                then
-                       say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")"
+                       displaypath=$(relative_path "$prefix$sm_path")
+                       say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
                        git config submodule."$name".url "$super_config_url"
 
                        if test -e "$sm_path"/.git
index 6c7bd95032b890d2f79cff0d18ac6aa688a74757..ff1ce3d351c8e4e175321d45b7284070f9ce691a 100755 (executable)
@@ -113,7 +113,7 @@ my ($_stdin, $_help, $_edit,
        $_template, $_shared,
        $_version, $_fetch_all, $_no_rebase, $_fetch_parent,
        $_before, $_after,
-       $_merge, $_strategy, $_preserve_merges, $_dry_run, $_local,
+       $_merge, $_strategy, $_preserve_merges, $_dry_run, $_parents, $_local,
        $_prefix, $_no_checkout, $_url, $_verbose,
        $_commit_url, $_tag, $_merge_info, $_interactive);
 
@@ -126,6 +126,7 @@ my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
                     'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
                     'ignore-paths=s' => \$Git::SVN::Fetcher::_ignore_regex,
+                    'include-paths=s' => \$Git::SVN::Fetcher::_include_regex,
                     'ignore-refs=s' => \$Git::SVN::Ra::_ignore_refs_regex );
 my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
                'authors-file|A=s' => \$_authors,
@@ -202,6 +203,7 @@ my %cmd = (
                    { 'message|m=s' => \$_message,
                      'destination|d=s' => \$_branch_dest,
                      'dry-run|n' => \$_dry_run,
+                     'parents' => \$_parents,
                      'tag|t' => \$_tag,
                      'username=s' => \$Git::SVN::Prompt::_username,
                      'commit-url=s' => \$_commit_url } ],
@@ -210,6 +212,7 @@ my %cmd = (
                 { 'message|m=s' => \$_message,
                   'destination|d=s' => \$_branch_dest,
                   'dry-run|n' => \$_dry_run,
+                  'parents' => \$_parents,
                   'username=s' => \$Git::SVN::Prompt::_username,
                   'commit-url=s' => \$_commit_url } ],
        'set-tree' => [ \&cmd_set_tree,
@@ -470,6 +473,9 @@ sub do_git_init_db {
        my $ignore_paths_regex = \$Git::SVN::Fetcher::_ignore_regex;
        command_noisy('config', "$pfx.ignore-paths", $$ignore_paths_regex)
                if defined $$ignore_paths_regex;
+       my $include_paths_regex = \$Git::SVN::Fetcher::_include_regex;
+       command_noisy('config', "$pfx.include-paths", $$include_paths_regex)
+               if defined $$include_paths_regex;
        my $ignore_refs_regex = \$Git::SVN::Ra::_ignore_refs_regex;
        command_noisy('config', "$pfx.ignore-refs", $$ignore_refs_regex)
                if defined $$ignore_refs_regex;
@@ -669,12 +675,14 @@ sub merge_revs_into_hash {
 }
 
 sub merge_merge_info {
-       my ($mergeinfo_one, $mergeinfo_two) = @_;
+       my ($mergeinfo_one, $mergeinfo_two, $ignore_branch) = @_;
        my %result_hash = ();
 
        merge_revs_into_hash(\%result_hash, $mergeinfo_one);
        merge_revs_into_hash(\%result_hash, $mergeinfo_two);
 
+       delete $result_hash{$ignore_branch} if $ignore_branch;
+
        my $result = '';
        # Sort below is for consistency's sake
        for my $branchname (sort keys(%result_hash)) {
@@ -695,6 +703,7 @@ sub populate_merge_info {
                my $all_parents_ok = 1;
                my $aggregate_mergeinfo = '';
                my $rooturl = $gs->repos_root;
+               my ($target_branch) = $gs->full_pushurl =~ /^\Q$rooturl\E(.*)/;
 
                if (defined($rewritten_parent)) {
                        # Replace first parent with newly-rewritten version
@@ -726,7 +735,8 @@ sub populate_merge_info {
                        # Merge previous mergeinfo values
                        $aggregate_mergeinfo =
                                merge_merge_info($aggregate_mergeinfo,
-                                                                $par_mergeinfo, 0);
+                                                               $par_mergeinfo,
+                                                               $target_branch);
 
                        next if $parent eq $parents[0]; # Skip first parent
                        # Add new changes being placed in tree by merge
@@ -769,7 +779,8 @@ sub populate_merge_info {
                        my $newmergeinfo = "$branchpath:" . join(',', @revsin);
                        $aggregate_mergeinfo =
                                merge_merge_info($aggregate_mergeinfo,
-                                                                $newmergeinfo, 1);
+                                                               $newmergeinfo,
+                                                               $target_branch);
                }
                if ($all_parents_ok and $aggregate_mergeinfo) {
                        return $aggregate_mergeinfo;
@@ -820,7 +831,7 @@ sub dcommit_rebase {
 sub cmd_dcommit {
        my $head = shift;
        command_noisy(qw/update-index --refresh/);
-       git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
+       git_cmd_try { command_oneline(qw/diff-index --quiet HEAD --/) }
                'Cannot dcommit with a dirty index.  Commit your changes first, '
                . "or stash them with `git stash'.\n";
        $head ||= 'HEAD';
@@ -1163,6 +1174,10 @@ sub cmd_branch {
                $ctx->ls($dst, 'HEAD', 0);
        } and die "branch ${branch_name} already exists\n";
 
+       if ($_parents) {
+               mk_parent_dirs($ctx, $dst);
+       }
+
        print "Copying ${src} at r${rev} to ${dst}...\n";
        $ctx->copy($src, $rev, $dst)
                unless $_dry_run;
@@ -1170,6 +1185,17 @@ sub cmd_branch {
        $gs->fetch_all;
 }
 
+sub mk_parent_dirs {
+       my ($ctx, $parent) = @_;
+       $parent =~ s{/[^/]*$}{};
+
+       if (!eval{$ctx->ls($parent, 'HEAD', 0)}) {
+               mk_parent_dirs($ctx, $parent);
+               print "Creating parent folder ${parent} ...\n";
+               $ctx->mkdir($parent) unless $_dry_run;
+       }
+}
+
 sub cmd_find_rev {
        my $revision_or_hash = shift or die "SVN or git revision required ",
                                            "as a command-line argument\n";
@@ -1220,7 +1246,7 @@ sub cmd_rebase {
                return;
        }
        if (command(qw/diff-index HEAD --/)) {
-               print STDERR "Cannot rebase with uncommited changes:\n";
+               print STDERR "Cannot rebase with uncommitted changes:\n";
                command_noisy('status');
                exit 1;
        }
@@ -1906,7 +1932,7 @@ sub cmt_sha2rev_batch {
 sub working_head_info {
        my ($head, $refs) = @_;
        my @args = qw/rev-list --first-parent --pretty=medium/;
-       my ($fh, $ctx) = command_output_pipe(@args, $head);
+       my ($fh, $ctx) = command_output_pipe(@args, $head, "--");
        my $hash;
        my %max;
        while (<$fh>) {
index 9f446798d473cee7dedc82d5d2c820cb0967b375..1d72ec760e9d69e0fc29a699fa60be945f872a93 100755 (executable)
@@ -32,8 +32,9 @@ valid_custom_tool()
 valid_tool() {
        case "$1" in
        firefox | iceweasel | seamonkey | iceape | \
-       chrome | google-chrome | chromium | chromium-browser |\
-       konqueror | opera | w3m | elinks | links | lynx | dillo | open | start)
+       chrome | google-chrome | chromium | chromium-browser | \
+       konqueror | opera | w3m | elinks | links | lynx | dillo | open | \
+       start | cygstart)
                ;; # happy
        *)
                valid_custom_tool "$1" || return 1
@@ -127,6 +128,10 @@ if test -z "$browser" ; then
        if test -x /bin/start; then
                browser_candidates="start $browser_candidates"
        fi
+       # /usr/bin/cygstart indicates Cygwin
+       if test -x /usr/bin/cygstart; then
+               browser_candidates="cygstart $browser_candidates"
+       fi
 
        for i in $browser_candidates; do
                init_browser_path $i
@@ -174,7 +179,7 @@ konqueror)
                ;;
        esac
        ;;
-w3m|elinks|links|lynx|open)
+w3m|elinks|links|lynx|open|cygstart)
        "$browser_path" "$@"
        ;;
 start)
diff --git a/git.c b/git.c
index 1ada169d5cff3051effee33c6f9ba5b9be15b2e6..2025f77d0181ba646e3c793a693e5d71348b966c 100644 (file)
--- a/git.c
+++ b/git.c
@@ -4,6 +4,7 @@
 #include "help.h"
 #include "quote.h"
 #include "run-command.h"
+#include "commit.h"
 
 const char git_usage_string[] =
        "git [--version] [--help] [-c name=value]\n"
@@ -146,6 +147,12 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
                        setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
                        if (envchanged)
                                *envchanged = 1;
+               } else if (!strcmp(cmd, "--shallow-file")) {
+                       (*argv)++;
+                       (*argc)--;
+                       set_alternate_shallow_file((*argv)[0]);
+                       if (envchanged)
+                               *envchanged = 1;
                } else {
                        fprintf(stderr, "Unknown option: %s\n", cmd);
                        usage(git_usage_string);
@@ -317,6 +324,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "check-attr", cmd_check_attr, RUN_SETUP },
                { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
+               { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
                { "check-ref-format", cmd_check_ref_format },
                { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
                { "checkout-index", cmd_checkout_index,
@@ -507,8 +515,9 @@ static int run_argv(int *argcp, const char ***argv)
 }
 
 
-int main(int argc, const char **argv)
+int main(int argc, char **av)
 {
+       const char **argv = (const char **) av;
        const char *cmd;
 
        startup_info = &git_startup_info;
@@ -517,6 +526,13 @@ int main(int argc, const char **argv)
        if (!cmd)
                cmd = "git-help";
 
+       /*
+        * Always open file descriptors 0/1/2 to avoid clobbering files
+        * in die().  It also avoids messing up when the pipes are dup'ed
+        * onto stdin/stdout/stderr in the child processes we spawn.
+        */
+       sanitize_stdfds();
+
        git_setup_gettext();
 
        /*
diff --git a/git.rc b/git.rc
new file mode 100644 (file)
index 0000000..bce6db9
--- /dev/null
+++ b/git.rc
@@ -0,0 +1,22 @@
+1 VERSIONINFO
+FILEVERSION     MAJOR,MINOR,PATCH,0
+PRODUCTVERSION  MAJOR,MINOR,PATCH,0
+BEGIN
+  BLOCK "StringFileInfo"
+  BEGIN
+    BLOCK "040904b0"   /* LANG_ENGLISH/SUBLANG_ENGLISH_US, Unicode CP */
+    BEGIN
+      VALUE "CompanyName", "The Git Development Community\0"
+      VALUE "FileDescription", "Git for Windows\0"
+      VALUE "InternalName", "git\0"
+      VALUE "OriginalFilename", "git.exe\0"
+      VALUE "ProductName", "Git\0"
+      VALUE "ProductVersion", GIT_VERSION "\0"
+    END
+  END
+
+  BLOCK "VarFileInfo"
+  BEGIN
+    VALUE "Translation", 0x409, 1200
+  END
+END
index b3706fc9b9bef1361f6872b00905443dea584a7d..5cd00d80fe2fb80a07a59b7e5327f8ff6c29d8f6 100755 (executable)
@@ -1998,6 +1998,9 @@ proc mca {str} {
     return [string map {&& & & {}} [mc $str]]
 }
 
+proc cleardropsel {w} {
+    $w selection clear
+}
 proc makedroplist {w varname args} {
     global use_ttk
     if {$use_ttk} {
@@ -2007,7 +2010,9 @@ proc makedroplist {w varname args} {
             if {$cx > $width} {set width $cx}
         }
        set gm [ttk::combobox $w -width $width -state readonly\
-                   -textvariable $varname -values $args]
+                   -textvariable $varname -values $args \
+                   -exportselection false]
+       bind $gm <<ComboboxSelected>> [list $gm selection clear]
     } else {
        set gm [eval [linsert $args 0 tk_optionMenu $w $varname]]
     }
@@ -2026,6 +2031,9 @@ proc makewindow {} {
     global highlight_files gdttype
     global searchstring sstring
     global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
+    global uifgcolor uifgdisabledcolor
+    global filesepbgcolor filesepfgcolor
+    global mergecolors foundbgcolor currentsearchhitbgcolor
     global headctxmenu progresscanv progressitem progresscoords statusw
     global fprogitem fprogcoord lastprogupdate progupdatepending
     global rprogitem rprogcoord rownumsel numcommits
@@ -2177,10 +2185,10 @@ proc makewindow {} {
        0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
        0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
     }
-    image create bitmap bm-left -data $bm_left_data
-    image create bitmap bm-left-gray -data $bm_left_data -foreground "#999"
-    image create bitmap bm-right -data $bm_right_data
-    image create bitmap bm-right-gray -data $bm_right_data -foreground "#999"
+    image create bitmap bm-left -data $bm_left_data -foreground $uifgcolor
+    image create bitmap bm-left-gray -data $bm_left_data -foreground $uifgdisabledcolor
+    image create bitmap bm-right -data $bm_right_data -foreground $uifgcolor
+    image create bitmap bm-right-gray -data $bm_right_data -foreground $uifgdisabledcolor
 
     ${NS}::button .tf.bar.leftbut -command goback -state disabled -width 26
     if {$use_ttk} {
@@ -2245,7 +2253,8 @@ proc makewindow {} {
     set gm [makedroplist .tf.lbar.gdttype gdttype \
                [mc "containing:"] \
                [mc "touching paths:"] \
-               [mc "adding/removing string:"]]
+               [mc "adding/removing string:"] \
+               [mc "changing lines matching:"]]
     trace add variable gdttype write gdttype_change
     pack .tf.lbar.gdttype -side left -fill y
 
@@ -2349,32 +2358,32 @@ proc makewindow {} {
     lappend fglist $ctext
 
     $ctext tag conf comment -wrap $wrapcomment
-    $ctext tag conf filesep -font textfontbold -back "#aaaaaa"
+    $ctext tag conf filesep -font textfontbold -fore $filesepfgcolor -back $filesepbgcolor
     $ctext tag conf hunksep -fore [lindex $diffcolors 2]
     $ctext tag conf d0 -fore [lindex $diffcolors 0]
     $ctext tag conf dresult -fore [lindex $diffcolors 1]
-    $ctext tag conf m0 -fore red
-    $ctext tag conf m1 -fore blue
-    $ctext tag conf m2 -fore green
-    $ctext tag conf m3 -fore purple
-    $ctext tag conf m4 -fore brown
-    $ctext tag conf m5 -fore "#009090"
-    $ctext tag conf m6 -fore magenta
-    $ctext tag conf m7 -fore "#808000"
-    $ctext tag conf m8 -fore "#009000"
-    $ctext tag conf m9 -fore "#ff0080"
-    $ctext tag conf m10 -fore cyan
-    $ctext tag conf m11 -fore "#b07070"
-    $ctext tag conf m12 -fore "#70b0f0"
-    $ctext tag conf m13 -fore "#70f0b0"
-    $ctext tag conf m14 -fore "#f0b070"
-    $ctext tag conf m15 -fore "#ff70b0"
+    $ctext tag conf m0 -fore [lindex $mergecolors 0]
+    $ctext tag conf m1 -fore [lindex $mergecolors 1]
+    $ctext tag conf m2 -fore [lindex $mergecolors 2]
+    $ctext tag conf m3 -fore [lindex $mergecolors 3]
+    $ctext tag conf m4 -fore [lindex $mergecolors 4]
+    $ctext tag conf m5 -fore [lindex $mergecolors 5]
+    $ctext tag conf m6 -fore [lindex $mergecolors 6]
+    $ctext tag conf m7 -fore [lindex $mergecolors 7]
+    $ctext tag conf m8 -fore [lindex $mergecolors 8]
+    $ctext tag conf m9 -fore [lindex $mergecolors 9]
+    $ctext tag conf m10 -fore [lindex $mergecolors 10]
+    $ctext tag conf m11 -fore [lindex $mergecolors 11]
+    $ctext tag conf m12 -fore [lindex $mergecolors 12]
+    $ctext tag conf m13 -fore [lindex $mergecolors 13]
+    $ctext tag conf m14 -fore [lindex $mergecolors 14]
+    $ctext tag conf m15 -fore [lindex $mergecolors 15]
     $ctext tag conf mmax -fore darkgrey
     set mergemax 16
     $ctext tag conf mresult -font textfontbold
     $ctext tag conf msep -font textfontbold
-    $ctext tag conf found -back yellow
-    $ctext tag conf currentsearchhit -back orange
+    $ctext tag conf found -back $foundbgcolor
+    $ctext tag conf currentsearchhit -back $currentsearchhitbgcolor
     $ctext tag conf wwrap -wrap word
 
     .pwbottom add .bleft
@@ -2559,6 +2568,7 @@ proc makewindow {} {
        {mc "Compare with marked commit" command compare_commits}
        {mc "Diff this -> marked commit" command {diffvsmark 0}}
        {mc "Diff marked commit -> this" command {diffvsmark 1}}
+       {mc "Revert this commit" command revert}
     }
     $rowctxmenu configure -tearoff 0
 
@@ -2721,6 +2731,14 @@ proc savestuff {w} {
     global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors uicolor bgcolor fgcolor diffcolors diffcontext selectbgcolor
+    global uifgcolor uifgdisabledcolor
+    global headbgcolor headfgcolor headoutlinecolor remotebgcolor
+    global tagbgcolor tagfgcolor tagoutlinecolor
+    global reflinecolor filesepbgcolor filesepfgcolor
+    global mergecolors foundbgcolor currentsearchhitbgcolor
+    global linehoverbgcolor linehoverfgcolor linehoveroutlinecolor circlecolors
+    global mainheadcirclecolor workingfilescirclecolor indexcirclecolor
+    global linkfgcolor circleoutlinecolor
     global autoselect autosellen extdifftool perfile_attrs markbgcolor use_ttk
     global hideremotes want_ttk maxrefs
 
@@ -2753,13 +2771,37 @@ proc savestuff {w} {
        puts $f [list set want_ttk $want_ttk]
        puts $f [list set bgcolor $bgcolor]
        puts $f [list set fgcolor $fgcolor]
+       puts $f [list set uifgcolor $uifgcolor]
+       puts $f [list set uifgdisabledcolor $uifgdisabledcolor]
        puts $f [list set colors $colors]
        puts $f [list set diffcolors $diffcolors]
+       puts $f [list set mergecolors $mergecolors]
        puts $f [list set markbgcolor $markbgcolor]
        puts $f [list set diffcontext $diffcontext]
        puts $f [list set selectbgcolor $selectbgcolor]
+       puts $f [list set foundbgcolor $foundbgcolor]
+       puts $f [list set currentsearchhitbgcolor $currentsearchhitbgcolor]
        puts $f [list set extdifftool $extdifftool]
        puts $f [list set perfile_attrs $perfile_attrs]
+       puts $f [list set headbgcolor $headbgcolor]
+       puts $f [list set headfgcolor $headfgcolor]
+       puts $f [list set headoutlinecolor $headoutlinecolor]
+       puts $f [list set remotebgcolor $remotebgcolor]
+       puts $f [list set tagbgcolor $tagbgcolor]
+       puts $f [list set tagfgcolor $tagfgcolor]
+       puts $f [list set tagoutlinecolor $tagoutlinecolor]
+       puts $f [list set reflinecolor $reflinecolor]
+       puts $f [list set filesepbgcolor $filesepbgcolor]
+       puts $f [list set filesepfgcolor $filesepfgcolor]
+       puts $f [list set linehoverbgcolor $linehoverbgcolor]
+       puts $f [list set linehoverfgcolor $linehoverfgcolor]
+       puts $f [list set linehoveroutlinecolor $linehoveroutlinecolor]
+       puts $f [list set mainheadcirclecolor $mainheadcirclecolor]
+       puts $f [list set workingfilescirclecolor $workingfilescirclecolor]
+       puts $f [list set indexcirclecolor $indexcirclecolor]
+       puts $f [list set circlecolors $circlecolors]
+       puts $f [list set linkfgcolor $linkfgcolor]
+       puts $f [list set circleoutlinecolor $circleoutlinecolor]
 
        puts $f "set geometry(main) [wm geometry .]"
        puts $f "set geometry(state) [wm state .]"
@@ -4617,6 +4659,8 @@ proc do_file_hl {serial} {
        set gdtargs [concat -- $relative_paths]
     } elseif {$gdttype eq [mc "adding/removing string:"]} {
        set gdtargs [list "-S$highlight_files"]
+    } elseif {$gdttype eq [mc "changing lines matching:"]} {
+       set gdtargs [list "-G$highlight_files"]
     } else {
        # must be "containing:", i.e. we're searching commit info
        return
@@ -5925,15 +5969,17 @@ proc drawcmittext {id row col} {
     global linehtag linentag linedtag selectedline
     global canvxmax boldids boldnameids fgcolor markedid
     global mainheadid nullid nullid2 circleitem circlecolors ctxbut
+    global mainheadcirclecolor workingfilescirclecolor indexcirclecolor
+    global circleoutlinecolor
 
     # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
     set listed $cmitlisted($curview,$id)
     if {$id eq $nullid} {
-       set ofill red
+       set ofill $workingfilescirclecolor
     } elseif {$id eq $nullid2} {
-       set ofill green
+       set ofill $indexcirclecolor
     } elseif {$id eq $mainheadid} {
-       set ofill yellow
+       set ofill $mainheadcirclecolor
     } else {
        set ofill [lindex $circlecolors $listed]
     }
@@ -5943,21 +5989,21 @@ proc drawcmittext {id row col} {
     if {$listed <= 2} {
        set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
                   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
-                  -fill $ofill -outline $fgcolor -width 1 -tags circle]
+                  -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
     } elseif {$listed == 3} {
        # triangle pointing left for left-side commits
        set t [$canv create polygon \
                   [expr {$x - $orad}] $y \
                   [expr {$x + $orad - 1}] [expr {$y - $orad}] \
                   [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
-                  -fill $ofill -outline $fgcolor -width 1 -tags circle]
+                  -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
     } else {
        # triangle pointing right for right-side commits
        set t [$canv create polygon \
                   [expr {$x + $orad - 1}] $y \
                   [expr {$x - $orad}] [expr {$y - $orad}] \
                   [expr {$x - $orad}] [expr {$y + $orad - 1}] \
-                  -fill $ofill -outline $fgcolor -width 1 -tags circle]
+                  -fill $ofill -outline $circleoutlinecolor -width 1 -tags circle]
     }
     set circleitem($row) $t
     $canv raise $t
@@ -6345,6 +6391,9 @@ proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs mainhead
     global linespc lthickness
     global canv rowtextx curview fgcolor bgcolor ctxbut
+    global headbgcolor headfgcolor headoutlinecolor remotebgcolor
+    global tagbgcolor tagfgcolor tagoutlinecolor
+    global reflinecolor
 
     set marks {}
     set ntags 0
@@ -6382,7 +6431,7 @@ proc drawtags {id x xt y1} {
        set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
     }
     set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
-              -width $lthickness -fill black -tags tag.$id]
+              -width $lthickness -fill $reflinecolor -tags tag.$id]
     $canv lower $t
     foreach tag $marks x $xvals wid $wvals {
        set tag_quoted [string map {% %%} $tag]
@@ -6393,13 +6442,14 @@ proc drawtags {id x xt y1} {
            # draw a tag
            set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
                       $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
-                      -width 1 -outline black -fill yellow -tags tag.$id]
+                      -width 1 -outline $tagoutlinecolor -fill $tagbgcolor \
+                      -tags tag.$id]
            $canv bind $t <1> [list showtag $tag_quoted 1]
            set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
        } else {
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
-               set col green
+               set col $headbgcolor
                if {$tag eq $mainhead} {
                    set font mainfontbold
                }
@@ -6415,10 +6465,10 @@ proc drawtags {id x xt y1} {
                set yti [expr {$yt + 1}]
                set xri [expr {$x + $rwid}]
                $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
-                       -width 0 -fill "#ffddaa" -tags tag.$id
+                       -width 0 -fill $remotebgcolor -tags tag.$id
            }
        }
-       set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
+       set t [$canv create text $xl $y1 -anchor w -text $tag -fill $headfgcolor \
                   -font $font -tags [list tag.$id text]]
        if {$ntags >= 0} {
            $canv bind $t <1> [list showtag $tag_quoted 1]
@@ -6799,6 +6849,7 @@ proc appendwithlinks {text tags} {
 
 proc setlink {id lk} {
     global curview ctext pendinglinks
+    global linkfgcolor
 
     if {[string range $id 0 1] eq "-g"} {
       set id [string range $id 2 end]
@@ -6816,7 +6867,7 @@ proc setlink {id lk} {
        set known [commitinview $id $curview]
     }
     if {$known} {
-       $ctext tag conf $lk -foreground blue -underline 1
+       $ctext tag conf $lk -foreground $linkfgcolor -underline 1
        $ctext tag bind $lk <1> [list selbyid $id]
        $ctext tag bind $lk <Enter> {linkcursor %W 1}
        $ctext tag bind $lk <Leave> {linkcursor %W -1}
@@ -7571,9 +7622,13 @@ proc diffcmd {ids flags} {
 }
 
 proc gettreediffs {ids} {
-    global treediff treepending
+    global treediff treepending limitdiffs vfilelimit curview
 
-    if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
+    set cmd [diffcmd $ids {--no-commit-id}]
+    if {$limitdiffs && $vfilelimit($curview) ne {}} {
+           set cmd [concat $cmd -- $vfilelimit($curview)]
+    }
+    if {[catch {set gdtf [open $cmd r]}]} return
 
     set treepending $ids
     set treediff {}
@@ -7617,17 +7672,7 @@ proc gettreediffline {gdtf ids} {
        return [expr {$nr >= $max? 2: 1}]
     }
     close $gdtf
-    if {$limitdiffs && $vfilelimit($curview) ne {}} {
-       set flist {}
-       foreach f $treediff {
-           if {[path_filter $vfilelimit($curview) $f]} {
-               lappend flist $f
-           }
-       }
-       set treediffs($ids) $flist
-    } else {
-       set treediffs($ids) $treediff
-    }
+    set treediffs($ids) $treediff
     unset treepending
     if {$cmitmode eq "tree" && [llength $diffids] == 1} {
        gettree $diffids
@@ -8459,6 +8504,8 @@ proc lineleave {id} {
 proc linehover {} {
     global hoverx hovery hoverid hovertimer
     global canv linespc lthickness
+    global linehoverbgcolor linehoverfgcolor linehoveroutlinecolor
+
     global commitinfo
 
     set text [lindex $commitinfo($hoverid) 0]
@@ -8472,10 +8519,11 @@ proc linehover {} {
     set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
     set y1 [expr {$y + $linespc + 2 * $lthickness}]
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
-              -fill \#ffff80 -outline black -width 1 -tags hover]
+              -fill $linehoverbgcolor -outline $linehoveroutlinecolor \
+              -width 1 -tags hover]
     $canv raise $t
     set t [$canv create text $x $y -anchor nw -text $text -tags hover \
-              -font mainfont]
+              -font mainfont -fill $linehoverfgcolor]
     $canv raise $t
 }
 
@@ -9039,12 +9087,13 @@ proc domktag {} {
 proc redrawtags {id} {
     global canv linehtag idpos currentid curview cmitlisted markedid
     global canvxmax iddrawn circleitem mainheadid circlecolors
+    global mainheadcirclecolor
 
     if {![commitinview $id $curview]} return
     if {![info exists iddrawn($id)]} return
     set row [rowofcommit $id]
     if {$id eq $mainheadid} {
-       set ofill yellow
+       set ofill $mainheadcirclecolor
     } else {
        set ofill [lindex $circlecolors $cmitlisted($curview,$id)]
     }
@@ -9301,6 +9350,67 @@ proc cherrypick {} {
     notbusy cherrypick
 }
 
+proc revert {} {
+    global rowmenuid curview
+    global mainhead mainheadid
+    global gitdir
+
+    set oldhead [exec git rev-parse HEAD]
+    set dheads [descheads $rowmenuid]
+    if { $dheads eq {} || [lsearch -exact $dheads $oldhead] == -1 } {
+       set ok [confirm_popup [mc "Commit %s is not\
+           included in branch %s -- really revert it?" \
+                      [string range $rowmenuid 0 7] $mainhead]]
+       if {!$ok} return
+    }
+    nowbusy revert [mc "Reverting"]
+    update
+
+    if [catch {exec git revert --no-edit $rowmenuid} err] {
+        notbusy revert
+        if [regexp {files would be overwritten by merge:(\n(( |\t)+[^\n]+\n)+)}\
+                $err match files] {
+            regsub {\n( |\t)+} $files "\n" files
+            error_popup [mc "Revert failed because of local changes to\
+                the following files:%s Please commit, reset or stash \
+                your changes and try again." $files]
+        } elseif [regexp {error: could not revert} $err] {
+            if [confirm_popup [mc "Revert failed because of merge conflict.\n\
+                Do you wish to run git citool to resolve it?"]] {
+                # Force citool to read MERGE_MSG
+                file delete [file join $gitdir "GITGUI_MSG"]
+                exec_citool {} $rowmenuid
+            }
+        } else { error_popup $err }
+        run updatecommits
+        return
+    }
+
+    set newhead [exec git rev-parse HEAD]
+    if { $newhead eq $oldhead } {
+        notbusy revert
+        error_popup [mc "No changes committed"]
+        return
+    }
+
+    addnewchild $newhead $oldhead
+
+    if [commitinview $oldhead $curview] {
+        # XXX this isn't right if we have a path limit...
+        insertrow $newhead $oldhead $curview
+        if {$mainhead ne {}} {
+            movehead $newhead $mainhead
+            movedhead $newhead $mainhead
+        }
+        set mainheadid $newhead
+        redrawtags $oldhead
+        redrawtags $newhead
+        selbyid $newhead
+    }
+
+    notbusy revert
+}
+
 proc resethead {} {
     global mainhead rowmenuid confirm_ok resettype NS
 
@@ -10780,7 +10890,7 @@ proc showtag {tag isnew} {
     set linknum 0
     if {![info exists cached_tagcontent($tag)]} {
        catch {
-           set cached_tagcontent($tag) [exec git cat-file tag $tag]
+           set cached_tagcontent($tag) [exec git cat-file -p $tag]
        }
     }
     if {[info exists cached_tagcontent($tag)]} {
@@ -11641,6 +11751,15 @@ if {[catch {package require Tk 8.4} err]} {
     exit 1
 }
 
+# on OSX bring the current Wish process window to front
+if {[tk windowingsystem] eq "aqua"} {
+    exec osascript -e [format {
+        tell application "System Events"
+            set frontmost of processes whose unix id is %d to true
+        end tell
+    } [pid] ]
+}
+
 # Unset GIT_TRACE var if set
 if { [info exists ::env(GIT_TRACE)] } {
     unset ::env(GIT_TRACE)
@@ -11728,22 +11847,47 @@ if {[tk windowingsystem] eq "aqua"} {
 set colors {green red blue magenta darkgrey brown orange}
 if {[tk windowingsystem] eq "win32"} {
     set uicolor SystemButtonFace
+    set uifgcolor SystemButtonText
+    set uifgdisabledcolor SystemDisabledText
     set bgcolor SystemWindow
-    set fgcolor SystemButtonText
+    set fgcolor SystemWindowText
     set selectbgcolor SystemHighlight
 } else {
     set uicolor grey85
+    set uifgcolor black
+    set uifgdisabledcolor "#999"
     set bgcolor white
     set fgcolor black
     set selectbgcolor gray85
 }
 set diffcolors {red "#00a000" blue}
 set diffcontext 3
+set mergecolors {red blue green purple brown "#009090" magenta "#808000" "#009000" "#ff0080" cyan "#b07070" "#70b0f0" "#70f0b0" "#f0b070" "#ff70b0"}
 set ignorespace 0
 set worddiff ""
 set markbgcolor "#e0e0ff"
 
+set headbgcolor green
+set headfgcolor black
+set headoutlinecolor black
+set remotebgcolor #ffddaa
+set tagbgcolor yellow
+set tagfgcolor black
+set tagoutlinecolor black
+set reflinecolor black
+set filesepbgcolor #aaaaaa
+set filesepfgcolor black
+set linehoverbgcolor #ffff80
+set linehoverfgcolor black
+set linehoveroutlinecolor black
+set mainheadcirclecolor yellow
+set workingfilescirclecolor red
+set indexcirclecolor green
 set circlecolors {white blue gray blue blue}
+set linkfgcolor blue
+set circleoutlinecolor $fgcolor
+set foundbgcolor yellow
+set currentsearchhitbgcolor orange
 
 # button for popping up context menus
 if {[tk windowingsystem] eq "aqua"} {
index 8cc98dc0794d6a6fae196b4fa17b922ca5394436..df95e01b9042217148a5d9a12cc426b769355c38 100644 (file)
@@ -1,16 +1,16 @@
 # Swedish translation for gitk
-# Copyright (C) 2005-2012 Paul Mackerras
+# Copyright (C) 2005-2013 Paul Mackerras
 # This file is distributed under the same license as the gitk package.
 #
 # Mikael Magnusson <mikachu@gmail.com>, 2008.
-# Peter Krefting <peter@softwolves.pp.se>, 2008, 2009, 2010, 2012.
+# Peter Krefting <peter@softwolves.pp.se>, 2008, 2009, 2010, 2012, 2013.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-10-03 08:09+0100\n"
-"PO-Revision-Date: 2012-10-03 08:13+0100\n"
+"POT-Creation-Date: 2013-05-16 08:06+0100\n"
+"PO-Revision-Date: 2013-05-16 08:12+0100\n"
 "Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
 "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
 "Language: sv\n"
@@ -22,11 +22,11 @@ msgstr ""
 msgid "Couldn't get list of unmerged files:"
 msgstr "Kunde inte hämta lista över ej sammanslagna filer:"
 
-#: gitk:210 gitk:2317
+#: gitk:210 gitk:2334
 msgid "Color words"
 msgstr "Färga ord"
 
-#: gitk:215 gitk:2317 gitk:7888 gitk:7921
+#: gitk:215 gitk:2334 gitk:7977 gitk:8010
 msgid "Markup words"
 msgstr "Märk upp ord"
 
@@ -60,11 +60,11 @@ msgstr "Fel vid körning av git log:"
 msgid "Reading"
 msgstr "Läser"
 
-#: gitk:484 gitk:4353
+#: gitk:484 gitk:4409
 msgid "Reading commits..."
 msgstr "Läser incheckningar..."
 
-#: gitk:487 gitk:1625 gitk:4356
+#: gitk:487 gitk:1625 gitk:4412
 msgid "No commits selected"
 msgstr "Inga incheckningar markerade"
 
@@ -80,278 +80,286 @@ msgstr "Ingen incheckningsinformation är tillgänglig"
 msgid "mc"
 msgstr "mc"
 
-#: gitk:1911 gitk:4146 gitk:9282 gitk:10824 gitk:11100
+#: gitk:1911 gitk:4202 gitk:9437 gitk:10979 gitk:11258
 msgid "OK"
 msgstr "OK"
 
-#: gitk:1913 gitk:4148 gitk:8871 gitk:8950 gitk:9065 gitk:9114 gitk:9284
-#: gitk:10825 gitk:11101
+#: gitk:1913 gitk:4204 gitk:8964 gitk:9043 gitk:9159 gitk:9208 gitk:9439
+#: gitk:10980 gitk:11259
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: gitk:2040
+#: gitk:2048
 msgid "Update"
 msgstr "Uppdatera"
 
-#: gitk:2041
+#: gitk:2049
 msgid "Reload"
 msgstr "Ladda om"
 
-#: gitk:2042
+#: gitk:2050
 msgid "Reread references"
 msgstr "Läs om referenser"
 
-#: gitk:2043
+#: gitk:2051
 msgid "List references"
 msgstr "Visa referenser"
 
-#: gitk:2045
+#: gitk:2053
 msgid "Start git gui"
 msgstr "Starta git gui"
 
-#: gitk:2047
+#: gitk:2055
 msgid "Quit"
 msgstr "Avsluta"
 
-#: gitk:2039
+#: gitk:2047
 msgid "File"
 msgstr "Arkiv"
 
-#: gitk:2051
+#: gitk:2059
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: gitk:2050
+#: gitk:2058
 msgid "Edit"
 msgstr "Redigera"
 
-#: gitk:2055
+#: gitk:2063
 msgid "New view..."
 msgstr "Ny vy..."
 
-#: gitk:2056
+#: gitk:2064
 msgid "Edit view..."
 msgstr "Ändra vy..."
 
-#: gitk:2057
+#: gitk:2065
 msgid "Delete view"
 msgstr "Ta bort vy"
 
-#: gitk:2059
+#: gitk:2067
 msgid "All files"
 msgstr "Alla filer"
 
-#: gitk:2054 gitk:3899
+#: gitk:2062 gitk:3955
 msgid "View"
 msgstr "Visa"
 
-#: gitk:2064 gitk:2074 gitk:2869
+#: gitk:2072 gitk:2082 gitk:2925
 msgid "About gitk"
 msgstr "Om gitk"
 
-#: gitk:2065 gitk:2079
+#: gitk:2073 gitk:2087
 msgid "Key bindings"
 msgstr "Tangentbordsbindningar"
 
-#: gitk:2063 gitk:2078
+#: gitk:2071 gitk:2086
 msgid "Help"
 msgstr "Hjälp"
 
-#: gitk:2156 gitk:8330
+#: gitk:2164 gitk:8420
 msgid "SHA1 ID:"
 msgstr "SHA1-id:"
 
-#: gitk:2192
+#: gitk:2208
 msgid "Row"
 msgstr "Rad"
 
-#: gitk:2230
+#: gitk:2246
 msgid "Find"
 msgstr "Sök"
 
-#: gitk:2231
+#: gitk:2247
 msgid "next"
 msgstr "nästa"
 
-#: gitk:2232
+#: gitk:2248
 msgid "prev"
 msgstr "föreg"
 
-#: gitk:2233
+#: gitk:2249
 msgid "commit"
 msgstr "incheckning"
 
-#: gitk:2236 gitk:2238 gitk:4514 gitk:4537 gitk:4561 gitk:6528 gitk:6600
-#: gitk:6685
+#: gitk:2252 gitk:2254 gitk:4570 gitk:4593 gitk:4617 gitk:6592 gitk:6664
+#: gitk:6749
 msgid "containing:"
 msgstr "som innehåller:"
 
-#: gitk:2239 gitk:3381 gitk:3386 gitk:4590
+#: gitk:2255 gitk:3437 gitk:3442 gitk:4646
 msgid "touching paths:"
 msgstr "som rör sökväg:"
 
-#: gitk:2240 gitk:4604
+#: gitk:2256 gitk:4660
 msgid "adding/removing string:"
 msgstr "som lägger/till tar bort sträng:"
 
-#: gitk:2249 gitk:2251 gitk:4593
+#: gitk:2257 gitk:4662
+msgid "changing lines matching:"
+msgstr "ändrar rader som matchar:"
+
+#: gitk:2266 gitk:2268 gitk:4649
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:2251 gitk:4679 gitk:6496
+#: gitk:2268 gitk:4737 gitk:6560
 msgid "IgnCase"
 msgstr "IgnVersaler"
 
-#: gitk:2251 gitk:4563 gitk:4677 gitk:6492
+#: gitk:2268 gitk:4619 gitk:4735 gitk:6556
 msgid "Regexp"
 msgstr "Reg.uttr."
 
-#: gitk:2253 gitk:2254 gitk:4699 gitk:4729 gitk:4736 gitk:6621 gitk:6689
+#: gitk:2270 gitk:2271 gitk:4757 gitk:4787 gitk:4794 gitk:6685 gitk:6753
 msgid "All fields"
 msgstr "Alla fält"
 
-#: gitk:2254 gitk:4696 gitk:4729 gitk:6559
+#: gitk:2271 gitk:4754 gitk:4787 gitk:6623
 msgid "Headline"
 msgstr "Rubrik"
 
-#: gitk:2255 gitk:4696 gitk:6559 gitk:6689 gitk:7126
+#: gitk:2272 gitk:4754 gitk:6623 gitk:6753 gitk:7221
 msgid "Comments"
 msgstr "Kommentarer"
 
-#: gitk:2255 gitk:4696 gitk:4701 gitk:4736 gitk:6559 gitk:7061 gitk:8505
-#: gitk:8520
+#: gitk:2272 gitk:4754 gitk:4759 gitk:4794 gitk:6623 gitk:7156 gitk:8598
+#: gitk:8613
 msgid "Author"
 msgstr "Författare"
 
-#: gitk:2255 gitk:4696 gitk:6559 gitk:7063
+#: gitk:2272 gitk:4754 gitk:6623 gitk:7158
 msgid "Committer"
 msgstr "Incheckare"
 
-#: gitk:2286
+#: gitk:2303
 msgid "Search"
 msgstr "Sök"
 
-#: gitk:2294
+#: gitk:2311
 msgid "Diff"
 msgstr "Diff"
 
-#: gitk:2296
+#: gitk:2313
 msgid "Old version"
 msgstr "Gammal version"
 
-#: gitk:2298
+#: gitk:2315
 msgid "New version"
 msgstr "Ny version"
 
-#: gitk:2300
+#: gitk:2317
 msgid "Lines of context"
 msgstr "Rader sammanhang"
 
-#: gitk:2310
+#: gitk:2327
 msgid "Ignore space change"
 msgstr "Ignorera ändringar i blanksteg"
 
-#: gitk:2314 gitk:2316 gitk:7646 gitk:7874
+#: gitk:2331 gitk:2333 gitk:7735 gitk:7963
 msgid "Line diff"
 msgstr "Rad-diff"
 
-#: gitk:2379
+#: gitk:2397
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:2381
+#: gitk:2399
 msgid "Tree"
 msgstr "Träd"
 
-#: gitk:2540 gitk:2559
+#: gitk:2557 gitk:2577
 msgid "Diff this -> selected"
 msgstr "Diff denna -> markerad"
 
-#: gitk:2541 gitk:2560
+#: gitk:2558 gitk:2578
 msgid "Diff selected -> this"
 msgstr "Diff markerad -> denna"
 
-#: gitk:2542 gitk:2561
+#: gitk:2559 gitk:2579
 msgid "Make patch"
 msgstr "Skapa patch"
 
-#: gitk:2543 gitk:8929
+#: gitk:2560 gitk:9022
 msgid "Create tag"
 msgstr "Skapa tagg"
 
-#: gitk:2544 gitk:9045
+#: gitk:2561 gitk:9139
 msgid "Write commit to file"
 msgstr "Skriv incheckning till fil"
 
-#: gitk:2545 gitk:9102
+#: gitk:2562 gitk:9196
 msgid "Create new branch"
 msgstr "Skapa ny gren"
 
-#: gitk:2546
+#: gitk:2563
 msgid "Cherry-pick this commit"
 msgstr "Plocka denna incheckning"
 
-#: gitk:2547
+#: gitk:2564
 msgid "Reset HEAD branch to here"
 msgstr "Återställ HEAD-grenen hit"
 
-#: gitk:2548
+#: gitk:2565
 msgid "Mark this commit"
 msgstr "Markera denna incheckning"
 
-#: gitk:2549
+#: gitk:2566
 msgid "Return to mark"
 msgstr "Återgå till markering"
 
-#: gitk:2550
+#: gitk:2567
 msgid "Find descendant of this and mark"
 msgstr "Hitta efterföljare till denna och markera"
 
-#: gitk:2551
+#: gitk:2568
 msgid "Compare with marked commit"
 msgstr "Jämför med markerad incheckning"
 
-#: gitk:2552 gitk:2562
+#: gitk:2569 gitk:2580
 msgid "Diff this -> marked commit"
 msgstr "Diff denna -> markerad incheckning"
 
-#: gitk:2553 gitk:2563
+#: gitk:2570 gitk:2581
 msgid "Diff marked commit -> this"
 msgstr "Diff markerad incheckning -> denna"
 
-#: gitk:2569
+#: gitk:2571
+msgid "Revert this commit"
+msgstr "Ångra denna incheckning"
+
+#: gitk:2587
 msgid "Check out this branch"
 msgstr "Checka ut denna gren"
 
-#: gitk:2570
+#: gitk:2588
 msgid "Remove this branch"
 msgstr "Ta bort denna gren"
 
-#: gitk:2577
+#: gitk:2595
 msgid "Highlight this too"
 msgstr "Markera även detta"
 
-#: gitk:2578
+#: gitk:2596
 msgid "Highlight this only"
 msgstr "Markera bara detta"
 
-#: gitk:2579
+#: gitk:2597
 msgid "External diff"
 msgstr "Extern diff"
 
-#: gitk:2580
+#: gitk:2598
 msgid "Blame parent commit"
 msgstr "Klandra föräldraincheckning"
 
-#: gitk:2587
+#: gitk:2605
 msgid "Show origin of this line"
 msgstr "Visa ursprunget för den här raden"
 
-#: gitk:2588
+#: gitk:2606
 msgid "Run git gui blame on this line"
 msgstr "Kör git gui blame på den här raden"
 
-#: gitk:2871
+#: gitk:2927
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -367,302 +375,302 @@ msgstr ""
 "\n"
 "Använd och vidareförmedla enligt villkoren i GNU General Public License"
 
-#: gitk:2879 gitk:2944 gitk:9468
+#: gitk:2935 gitk:3000 gitk:9623
 msgid "Close"
 msgstr "Stäng"
 
-#: gitk:2900
+#: gitk:2956
 msgid "Gitk key bindings"
 msgstr "Tangentbordsbindningar för Gitk"
 
-#: gitk:2903
+#: gitk:2959
 msgid "Gitk key bindings:"
 msgstr "Tangentbordsbindningar för Gitk:"
 
-#: gitk:2905
+#: gitk:2961
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tAvsluta"
 
-#: gitk:2906
+#: gitk:2962
 #, tcl-format
 msgid "<%s-W>\t\tClose window"
 msgstr "<%s-W>\t\tStäng fönster"
 
-#: gitk:2907
+#: gitk:2963
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tGå till första incheckning"
 
-#: gitk:2908
+#: gitk:2964
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tGå till sista incheckning"
 
-#: gitk:2909
+#: gitk:2965
 msgid "<Up>, p, k\tMove up one commit"
 msgstr "<Upp>, p, k\tGå en incheckning upp"
 
-#: gitk:2910
+#: gitk:2966
 msgid "<Down>, n, j\tMove down one commit"
 msgstr "<Ned>, n, j\tGå en incheckning ned"
 
-#: gitk:2911
+#: gitk:2967
 msgid "<Left>, z, h\tGo back in history list"
 msgstr "<Vänster>, z, h\tGå bakåt i historiken"
 
-#: gitk:2912
+#: gitk:2968
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Höger>, x, l\tGå framåt i historiken"
 
-#: gitk:2913
+#: gitk:2969
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tGå upp en sida i incheckningslistan"
 
-#: gitk:2914
+#: gitk:2970
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tGå ned en sida i incheckningslistan"
 
-#: gitk:2915
+#: gitk:2971
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tRulla till början av incheckningslistan"
 
-#: gitk:2916
+#: gitk:2972
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tRulla till slutet av incheckningslistan"
 
-#: gitk:2917
+#: gitk:2973
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg"
 
-#: gitk:2918
+#: gitk:2974
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg"
 
-#: gitk:2919
+#: gitk:2975
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida"
 
-#: gitk:2920
+#: gitk:2976
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida"
 
-#: gitk:2921
+#: gitk:2977
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)"
 
-#: gitk:2922
+#: gitk:2978
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)"
 
-#: gitk:2923
+#: gitk:2979
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tRulla diffvisningen upp en sida"
 
-#: gitk:2924
+#: gitk:2980
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Baksteg>\tRulla diffvisningen upp en sida"
 
-#: gitk:2925
+#: gitk:2981
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Blanksteg>\tRulla diffvisningen ned en sida"
 
-#: gitk:2926
+#: gitk:2982
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tRulla diffvisningen upp 18 rader"
 
-#: gitk:2927
+#: gitk:2983
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tRulla diffvisningen ned 18 rader"
 
-#: gitk:2928
+#: gitk:2984
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSök"
 
-#: gitk:2929
+#: gitk:2985
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tGå till nästa sökträff"
 
-#: gitk:2930
+#: gitk:2986
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\t\tGå till nästa sökträff"
 
-#: gitk:2931
+#: gitk:2987
 msgid "/\t\tFocus the search box"
 msgstr "/\t\tFokusera sökrutan"
 
-#: gitk:2932
+#: gitk:2988
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tGå till föregående sökträff"
 
-#: gitk:2933
+#: gitk:2989
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tRulla diffvisningen till nästa fil"
 
-#: gitk:2934
+#: gitk:2990
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tGå till nästa sökträff i diffvisningen"
 
-#: gitk:2935
+#: gitk:2991
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tGå till föregående sökträff i diffvisningen"
 
-#: gitk:2936
+#: gitk:2992
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Num+>\tÖka teckenstorlek"
 
-#: gitk:2937
+#: gitk:2993
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tÖka teckenstorlek"
 
-#: gitk:2938
+#: gitk:2994
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Num->\tMinska teckenstorlek"
 
-#: gitk:2939
+#: gitk:2995
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tMinska teckenstorlek"
 
-#: gitk:2940
+#: gitk:2996
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tUppdatera"
 
-#: gitk:3395 gitk:3404
+#: gitk:3451 gitk:3460
 #, tcl-format
 msgid "Error creating temporary directory %s:"
 msgstr "Fel vid skapande av temporär katalog %s:"
 
-#: gitk:3417
+#: gitk:3473
 #, tcl-format
 msgid "Error getting \"%s\" from %s:"
 msgstr "Fel vid hämtning av  \"%s\" från %s:"
 
-#: gitk:3480
+#: gitk:3536
 msgid "command failed:"
 msgstr "kommando misslyckades:"
 
-#: gitk:3629
+#: gitk:3685
 msgid "No such commit"
 msgstr "Incheckning saknas"
 
-#: gitk:3643
+#: gitk:3699
 msgid "git gui blame: command failed:"
 msgstr "git gui blame: kommando misslyckades:"
 
-#: gitk:3674
+#: gitk:3730
 #, tcl-format
 msgid "Couldn't read merge head: %s"
 msgstr "Kunde inte läsa sammanslagningshuvud: %s"
 
-#: gitk:3682
+#: gitk:3738
 #, tcl-format
 msgid "Error reading index: %s"
 msgstr "Fel vid läsning av index: %s"
 
-#: gitk:3707
+#: gitk:3763
 #, tcl-format
 msgid "Couldn't start git blame: %s"
 msgstr "Kunde inte starta git blame: %s"
 
-#: gitk:3710 gitk:6527
+#: gitk:3766 gitk:6591
 msgid "Searching"
 msgstr "Söker"
 
-#: gitk:3742
+#: gitk:3798
 #, tcl-format
 msgid "Error running git blame: %s"
 msgstr "Fel vid körning av git blame: %s"
 
-#: gitk:3770
+#: gitk:3826
 #, tcl-format
 msgid "That line comes from commit %s,  which is not in this view"
 msgstr "Raden kommer från incheckningen %s, som inte finns i denna vy"
 
-#: gitk:3784
+#: gitk:3840
 msgid "External diff viewer failed:"
 msgstr "Externt diff-verktyg misslyckades:"
 
-#: gitk:3902
+#: gitk:3958
 msgid "Gitk view definition"
 msgstr "Definition av Gitk-vy"
 
-#: gitk:3906
+#: gitk:3962
 msgid "Remember this view"
 msgstr "Spara denna vy"
 
-#: gitk:3907
+#: gitk:3963
 msgid "References (space separated list):"
 msgstr "Referenser (blankstegsavdelad lista):"
 
-#: gitk:3908
+#: gitk:3964
 msgid "Branches & tags:"
 msgstr "Grenar & taggar:"
 
-#: gitk:3909
+#: gitk:3965
 msgid "All refs"
 msgstr "Alla referenser"
 
-#: gitk:3910
+#: gitk:3966
 msgid "All (local) branches"
 msgstr "Alla (lokala) grenar"
 
-#: gitk:3911
+#: gitk:3967
 msgid "All tags"
 msgstr "Alla taggar"
 
-#: gitk:3912
+#: gitk:3968
 msgid "All remote-tracking branches"
 msgstr "Alla fjärrspårande grenar"
 
-#: gitk:3913
+#: gitk:3969
 msgid "Commit Info (regular expressions):"
 msgstr "Incheckningsinfo (reguljära uttryck):"
 
-#: gitk:3914
+#: gitk:3970
 msgid "Author:"
 msgstr "Författare:"
 
-#: gitk:3915
+#: gitk:3971
 msgid "Committer:"
 msgstr "Incheckare:"
 
-#: gitk:3916
+#: gitk:3972
 msgid "Commit Message:"
 msgstr "Incheckningsmeddelande:"
 
-#: gitk:3917
+#: gitk:3973
 msgid "Matches all Commit Info criteria"
 msgstr "Motsvarar alla kriterier för incheckningsinfo"
 
-#: gitk:3918
+#: gitk:3974
 msgid "Changes to Files:"
 msgstr "Ändringar av filer:"
 
-#: gitk:3919
+#: gitk:3975
 msgid "Fixed String"
 msgstr "Fast sträng"
 
-#: gitk:3920
+#: gitk:3976
 msgid "Regular Expression"
 msgstr "Reguljärt uttryck"
 
-#: gitk:3921
+#: gitk:3977
 msgid "Search string:"
 msgstr "Söksträng:"
 
-#: gitk:3922
+#: gitk:3978
 msgid ""
 "Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
 "15:27:38\"):"
@@ -670,197 +678,201 @@ msgstr ""
 "Incheckingsdatum (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
 "15:27:38\"):"
 
-#: gitk:3923
+#: gitk:3979
 msgid "Since:"
 msgstr "Från:"
 
-#: gitk:3924
+#: gitk:3980
 msgid "Until:"
 msgstr "Till:"
 
-#: gitk:3925
+#: gitk:3981
 msgid "Limit and/or skip a number of revisions (positive integer):"
 msgstr "Begränsa och/eller hoppa över ett antal revisioner (positivt heltal):"
 
-#: gitk:3926
+#: gitk:3982
 msgid "Number to show:"
 msgstr "Antal att visa:"
 
-#: gitk:3927
+#: gitk:3983
 msgid "Number to skip:"
 msgstr "Antal att hoppa över:"
 
-#: gitk:3928
+#: gitk:3984
 msgid "Miscellaneous options:"
 msgstr "Diverse alternativ:"
 
-#: gitk:3929
+#: gitk:3985
 msgid "Strictly sort by date"
 msgstr "Strikt datumsortering"
 
-#: gitk:3930
+#: gitk:3986
 msgid "Mark branch sides"
 msgstr "Markera sidogrenar"
 
-#: gitk:3931
+#: gitk:3987
 msgid "Limit to first parent"
 msgstr "Begränsa till första förälder"
 
-#: gitk:3932
+#: gitk:3988
 msgid "Simple history"
 msgstr "Enkel historik"
 
-#: gitk:3933
+#: gitk:3989
 msgid "Additional arguments to git log:"
 msgstr "Ytterligare argument till git log:"
 
-#: gitk:3934
+#: gitk:3990
 msgid "Enter files and directories to include, one per line:"
 msgstr "Ange filer och kataloger att ta med, en per rad:"
 
-#: gitk:3935
+#: gitk:3991
 msgid "Command to generate more commits to include:"
 msgstr "Kommando för att generera fler incheckningar att ta med:"
 
-#: gitk:4059
+#: gitk:4115
 msgid "Gitk: edit view"
 msgstr "Gitk: redigera vy"
 
-#: gitk:4067
+#: gitk:4123
 msgid "-- criteria for selecting revisions"
 msgstr " - kriterier för val av revisioner"
 
-#: gitk:4072
+#: gitk:4128
 msgid "View Name"
 msgstr "Namn på vy"
 
-#: gitk:4147
+#: gitk:4203
 msgid "Apply (F5)"
 msgstr "Använd (F5)"
 
-#: gitk:4185
+#: gitk:4241
 msgid "Error in commit selection arguments:"
 msgstr "Fel i argument för val av incheckningar:"
 
-#: gitk:4238 gitk:4290 gitk:4749 gitk:4763 gitk:6027 gitk:11849 gitk:11850
+#: gitk:4294 gitk:4346 gitk:4807 gitk:4821 gitk:6087 gitk:12041 gitk:12042
 msgid "None"
 msgstr "Inget"
 
-#: gitk:4846 gitk:4851
+#: gitk:4904 gitk:4909
 msgid "Descendant"
 msgstr "Avkomling"
 
-#: gitk:4847
+#: gitk:4905
 msgid "Not descendant"
 msgstr "Inte avkomling"
 
-#: gitk:4854 gitk:4859
+#: gitk:4912 gitk:4917
 msgid "Ancestor"
 msgstr "Förfader"
 
-#: gitk:4855
+#: gitk:4913
 msgid "Not ancestor"
 msgstr "Inte förfader"
 
-#: gitk:5145
+#: gitk:5203
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokala ändringar sparade i indexet men inte incheckade"
 
-#: gitk:5181
+#: gitk:5239
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokala ändringar, ej sparade i indexet"
 
-#: gitk:6882
+#: gitk:6971
+msgid "and many more"
+msgstr "med många flera"
+
+#: gitk:6974
 msgid "many"
 msgstr "många"
 
-#: gitk:7065
+#: gitk:7160
 msgid "Tags:"
 msgstr "Taggar:"
 
-#: gitk:7082 gitk:7088 gitk:8500
+#: gitk:7177 gitk:7183 gitk:8593
 msgid "Parent"
 msgstr "Förälder"
 
-#: gitk:7093
+#: gitk:7188
 msgid "Child"
 msgstr "Barn"
 
-#: gitk:7102
+#: gitk:7197
 msgid "Branch"
 msgstr "Gren"
 
-#: gitk:7105
+#: gitk:7200
 msgid "Follows"
 msgstr "Följer"
 
-#: gitk:7108
+#: gitk:7203
 msgid "Precedes"
 msgstr "Föregår"
 
-#: gitk:7653
+#: gitk:7742
 #, tcl-format
 msgid "Error getting diffs: %s"
 msgstr "Fel vid hämtning av diff: %s"
 
-#: gitk:8328
+#: gitk:8418
 msgid "Goto:"
 msgstr "Gå till:"
 
-#: gitk:8349
+#: gitk:8439
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Förkortat SHA1-id %s är tvetydigt"
 
-#: gitk:8356
+#: gitk:8446
 #, tcl-format
 msgid "Revision %s is not known"
 msgstr "Revisionen %s är inte känd"
 
-#: gitk:8366
+#: gitk:8456
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA-id:t %s är inte känt"
 
-#: gitk:8368
+#: gitk:8458
 #, tcl-format
 msgid "Revision %s is not in the current view"
 msgstr "Revisionen %s finns inte i den nuvarande vyn"
 
-#: gitk:8507 gitk:8522
+#: gitk:8600 gitk:8615
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:8510
+#: gitk:8603
 msgid "Children"
 msgstr "Barn"
 
-#: gitk:8573
+#: gitk:8666
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Återställ grenen %s hit"
 
-#: gitk:8575
+#: gitk:8668
 msgid "Detached head: can't reset"
 msgstr "Frånkopplad head: kan inte återställa"
 
-#: gitk:8680 gitk:8686
+#: gitk:8773 gitk:8779
 msgid "Skipping merge commit "
 msgstr "Hoppar över sammanslagningsincheckning "
 
-#: gitk:8695 gitk:8700
+#: gitk:8788 gitk:8793
 msgid "Error getting patch ID for "
 msgstr "Fel vid hämtning av patch-id för "
 
-#: gitk:8696 gitk:8701
+#: gitk:8789 gitk:8794
 msgid " - stopping\n"
 msgstr " - stannar\n"
 
-#: gitk:8706 gitk:8709 gitk:8717 gitk:8731 gitk:8740
+#: gitk:8799 gitk:8802 gitk:8810 gitk:8824 gitk:8833
 msgid "Commit "
 msgstr "Incheckning "
 
-#: gitk:8710
+#: gitk:8803
 msgid ""
 " is the same patch as\n"
 "       "
@@ -868,7 +880,7 @@ msgstr ""
 " är samma patch som\n"
 "       "
 
-#: gitk:8718
+#: gitk:8811
 msgid ""
 " differs from\n"
 "       "
@@ -876,7 +888,7 @@ msgstr ""
 " skiljer sig från\n"
 "       "
 
-#: gitk:8720
+#: gitk:8813
 msgid ""
 "Diff of commits:\n"
 "\n"
@@ -884,131 +896,131 @@ msgstr ""
 "Skillnad mellan incheckningar:\n"
 "\n"
 
-#: gitk:8732 gitk:8741
+#: gitk:8825 gitk:8834
 #, tcl-format
 msgid " has %s children - stopping\n"
 msgstr " har %s barn - stannar\n"
 
-#: gitk:8760
+#: gitk:8853
 #, tcl-format
 msgid "Error writing commit to file: %s"
 msgstr "Fel vid skrivning av incheckning till fil: %s"
 
-#: gitk:8766
+#: gitk:8859
 #, tcl-format
 msgid "Error diffing commits: %s"
 msgstr "Fel vid jämförelse av incheckningar: %s"
 
-#: gitk:8812
+#: gitk:8905
 msgid "Top"
 msgstr "Topp"
 
-#: gitk:8813
+#: gitk:8906
 msgid "From"
 msgstr "Från"
 
-#: gitk:8818
+#: gitk:8911
 msgid "To"
 msgstr "Till"
 
-#: gitk:8842
+#: gitk:8935
 msgid "Generate patch"
 msgstr "Generera patch"
 
-#: gitk:8844
+#: gitk:8937
 msgid "From:"
 msgstr "Från:"
 
-#: gitk:8853
+#: gitk:8946
 msgid "To:"
 msgstr "Till:"
 
-#: gitk:8862
+#: gitk:8955
 msgid "Reverse"
 msgstr "Vänd"
 
-#: gitk:8864 gitk:9059
+#: gitk:8957 gitk:9153
 msgid "Output file:"
 msgstr "Utdatafil:"
 
-#: gitk:8870
+#: gitk:8963
 msgid "Generate"
 msgstr "Generera"
 
-#: gitk:8908
+#: gitk:9001
 msgid "Error creating patch:"
 msgstr "Fel vid generering av patch:"
 
-#: gitk:8931 gitk:9047 gitk:9104
+#: gitk:9024 gitk:9141 gitk:9198
 msgid "ID:"
 msgstr "Id:"
 
-#: gitk:8940
+#: gitk:9033
 msgid "Tag name:"
 msgstr "Taggnamn:"
 
-#: gitk:8943
+#: gitk:9036
 msgid "Tag message is optional"
 msgstr "Taggmeddelandet är valfritt"
 
-#: gitk:8945
+#: gitk:9038
 msgid "Tag message:"
 msgstr "Taggmeddelande:"
 
-#: gitk:8949 gitk:9113
+#: gitk:9042 gitk:9207
 msgid "Create"
 msgstr "Skapa"
 
-#: gitk:8967
+#: gitk:9060
 msgid "No tag name specified"
 msgstr "Inget taggnamn angavs"
 
-#: gitk:8971
+#: gitk:9064
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Taggen \"%s\" finns redan"
 
-#: gitk:8981
+#: gitk:9074
 msgid "Error creating tag:"
 msgstr "Fel vid skapande av tagg:"
 
-#: gitk:9056
+#: gitk:9150
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:9064
+#: gitk:9158
 msgid "Write"
 msgstr "Skriv"
 
-#: gitk:9082
+#: gitk:9176
 msgid "Error writing commit:"
 msgstr "Fel vid skrivning av incheckning:"
 
-#: gitk:9109
+#: gitk:9203
 msgid "Name:"
 msgstr "Namn:"
 
-#: gitk:9132
+#: gitk:9226
 msgid "Please specify a name for the new branch"
 msgstr "Ange ett namn för den nya grenen"
 
-#: gitk:9137
+#: gitk:9231
 #, tcl-format
 msgid "Branch '%s' already exists. Overwrite?"
 msgstr "Grenen \"%s\" finns redan. Skriva över?"
 
-#: gitk:9204
+#: gitk:9298
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Incheckningen %s finns redan på grenen %s -- skall den verkligen appliceras "
 "på nytt?"
 
-#: gitk:9209
+#: gitk:9303
 msgid "Cherry-picking"
 msgstr "Plockar"
 
-#: gitk:9218
+#: gitk:9312
 #, tcl-format
 msgid ""
 "Cherry-pick failed because of local changes to file '%s'.\n"
@@ -1018,7 +1030,7 @@ msgstr ""
 "Checka in, återställ eller spara undan (stash) dina ändringar och försök "
 "igen."
 
-#: gitk:9224
+#: gitk:9318
 msgid ""
 "Cherry-pick failed because of merge conflict.\n"
 "Do you wish to run git citool to resolve it?"
@@ -1026,32 +1038,59 @@ msgstr ""
 "Cherry-pick misslyckades på grund av en sammanslagningskonflikt.\n"
 "Vill du köra git citool för att lösa den?"
 
-#: gitk:9240
+#: gitk:9334 gitk:9392
 msgid "No changes committed"
 msgstr "Inga ändringar incheckade"
 
-#: gitk:9266
+#: gitk:9361
+#, tcl-format
+msgid "Commit %s is not included in branch %s -- really revert it?"
+msgstr "Incheckningen %s finns inte på grenen %s -- vill du verkligen ångra?"
+
+#: gitk:9366
+msgid "Reverting"
+msgstr "Ångrar"
+
+#: gitk:9374
+#, 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 ""
+"Misslyckades med att ångra på grund av lokala ändringar i följande filer:%s. "
+"Checka in, återställ eller spara undan (stash) dina ändringar och försök "
+"igen."
+
+#: gitk:9378
+msgid ""
+"Revert failed because of merge conflict.\n"
+" Do you wish to run git citool to resolve it?"
+msgstr ""
+"Misslyckades med att ångra på grund av en sammanslagningskonflikt.\n"
+" Vill du köra git citool för att lösa den?"
+
+#: gitk:9421
 msgid "Confirm reset"
 msgstr "Bekräfta återställning"
 
-#: gitk:9268
+#: gitk:9423
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Återställa grenen %s till %s?"
 
-#: gitk:9270
+#: gitk:9425
 msgid "Reset type:"
 msgstr "Typ av återställning:"
 
-#: gitk:9273
+#: gitk:9428
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Mjuk: Rör inte utcheckning och index"
 
-#: gitk:9276
+#: gitk:9431
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Blandad: Rör inte utcheckning, återställ index"
 
-#: gitk:9279
+#: gitk:9434
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -1059,19 +1098,19 @@ msgstr ""
 "Hård: Återställ utcheckning och index\n"
 "(förkastar ALLA lokala ändringar)"
 
-#: gitk:9296
+#: gitk:9451
 msgid "Resetting"
 msgstr "Återställer"
 
-#: gitk:9356
+#: gitk:9511
 msgid "Checking out"
 msgstr "Checkar ut"
 
-#: gitk:9409
+#: gitk:9564
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Kan inte ta bort den just nu utcheckade grenen"
 
-#: gitk:9415
+#: gitk:9570
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -1080,16 +1119,16 @@ msgstr ""
 "Incheckningarna på grenen %s existerar inte på någon annan gren.\n"
 "Vill du verkligen ta bort grenen %s?"
 
-#: gitk:9446
+#: gitk:9601
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Taggar och huvuden: %s"
 
-#: gitk:9461
+#: gitk:9616
 msgid "Filter"
 msgstr "Filter"
 
-#: gitk:9757
+#: gitk:9912
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -1097,210 +1136,214 @@ msgstr ""
 "Fel vid läsning av information om incheckningstopologi; information om "
 "grenar och föregående/senare taggar kommer inte vara komplett."
 
-#: gitk:10744
+#: gitk:10899
 msgid "Tag"
 msgstr "Tagg"
 
-#: gitk:10744
+#: gitk:10899
 msgid "Id"
 msgstr "Id"
 
-#: gitk:10793
+#: gitk:10948
 msgid "Gitk font chooser"
 msgstr "Teckensnittsväljare för Gitk"
 
-#: gitk:10810
+#: gitk:10965
 msgid "B"
 msgstr "F"
 
-#: gitk:10813
+#: gitk:10968
 msgid "I"
 msgstr "K"
 
-#: gitk:10931
+#: gitk:11086
 msgid "Commit list display options"
 msgstr "Alternativ för incheckningslistvy"
 
-#: gitk:10934
+#: gitk:11089
 msgid "Maximum graph width (lines)"
 msgstr "Maximal grafbredd (rader)"
 
-#: gitk:10937
+#: gitk:11092
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximal grafbredd (% av ruta)"
 
-#: gitk:10940
+#: gitk:11095
 msgid "Show local changes"
 msgstr "Visa lokala ändringar"
 
-#: gitk:10943
+#: gitk:11098
 msgid "Auto-select SHA1 (length)"
 msgstr "Välj SHA1 (längd) automatiskt"
 
-#: gitk:10947
+#: gitk:11102
 msgid "Hide remote refs"
 msgstr "Dölj fjärr-referenser"
 
-#: gitk:10951
+#: gitk:11106
 msgid "Diff display options"
 msgstr "Alternativ för diffvy"
 
-#: gitk:10953
+#: gitk:11108
 msgid "Tab spacing"
 msgstr "Blanksteg för tabulatortecken"
 
-#: gitk:10956
-msgid "Display nearby tags"
-msgstr "Visa närliggande taggar"
+#: gitk:11111
+msgid "Display nearby tags/heads"
+msgstr "Visa närliggande taggar/huvuden"
+
+#: gitk:11114
+msgid "Maximum # tags/heads to show"
+msgstr "Maximalt antal taggar/huvuden att visa"
 
-#: gitk:10959
+#: gitk:11117
 msgid "Limit diffs to listed paths"
 msgstr "Begränsa diff till listade sökvägar"
 
-#: gitk:10962
+#: gitk:11120
 msgid "Support per-file encodings"
 msgstr "Stöd för filspecifika teckenkodningar"
 
-#: gitk:10968 gitk:11115
+#: gitk:11126 gitk:11273
 msgid "External diff tool"
 msgstr "Externt diff-verktyg"
 
-#: gitk:10969
+#: gitk:11127
 msgid "Choose..."
 msgstr "Välj..."
 
-#: gitk:10974
+#: gitk:11132
 msgid "General options"
 msgstr "Allmänna inställningar"
 
-#: gitk:10977
+#: gitk:11135
 msgid "Use themed widgets"
 msgstr "Använd tema på fönsterelement"
 
-#: gitk:10979
+#: gitk:11137
 msgid "(change requires restart)"
 msgstr "(ändringen kräver omstart)"
 
-#: gitk:10981
+#: gitk:11139
 msgid "(currently unavailable)"
 msgstr "(för närvarande inte tillgängligt)"
 
-#: gitk:10992
+#: gitk:11150
 msgid "Colors: press to choose"
 msgstr "Färger: tryck för att välja"
 
-#: gitk:10995
+#: gitk:11153
 msgid "Interface"
 msgstr "Gränssnitt"
 
-#: gitk:10996
+#: gitk:11154
 msgid "interface"
 msgstr "gränssnitt"
 
-#: gitk:10999
+#: gitk:11157
 msgid "Background"
 msgstr "Bakgrund"
 
-#: gitk:11000 gitk:11030
+#: gitk:11158 gitk:11188
 msgid "background"
 msgstr "bakgrund"
 
-#: gitk:11003
+#: gitk:11161
 msgid "Foreground"
 msgstr "Förgrund"
 
-#: gitk:11004
+#: gitk:11162
 msgid "foreground"
 msgstr "förgrund"
 
-#: gitk:11007
+#: gitk:11165
 msgid "Diff: old lines"
 msgstr "Diff: gamla rader"
 
-#: gitk:11008
+#: gitk:11166
 msgid "diff old lines"
 msgstr "diff gamla rader"
 
-#: gitk:11012
+#: gitk:11170
 msgid "Diff: new lines"
 msgstr "Diff: nya rader"
 
-#: gitk:11013
+#: gitk:11171
 msgid "diff new lines"
 msgstr "diff nya rader"
 
-#: gitk:11017
+#: gitk:11175
 msgid "Diff: hunk header"
 msgstr "Diff: delhuvud"
 
-#: gitk:11019
+#: gitk:11177
 msgid "diff hunk header"
 msgstr "diff delhuvud"
 
-#: gitk:11023
+#: gitk:11181
 msgid "Marked line bg"
 msgstr "Markerad rad bakgrund"
 
-#: gitk:11025
+#: gitk:11183
 msgid "marked line background"
 msgstr "markerad rad bakgrund"
 
-#: gitk:11029
+#: gitk:11187
 msgid "Select bg"
 msgstr "Markerad bakgrund"
 
-#: gitk:11038
+#: gitk:11196
 msgid "Fonts: press to choose"
 msgstr "Teckensnitt: tryck för att välja"
 
-#: gitk:11040
+#: gitk:11198
 msgid "Main font"
 msgstr "Huvudteckensnitt"
 
-#: gitk:11041
+#: gitk:11199
 msgid "Diff display font"
 msgstr "Teckensnitt för diffvisning"
 
-#: gitk:11042
+#: gitk:11200
 msgid "User interface font"
 msgstr "Teckensnitt för användargränssnitt"
 
-#: gitk:11064
+#: gitk:11222
 msgid "Gitk preferences"
 msgstr "Inställningar för Gitk"
 
-#: gitk:11073
+#: gitk:11231
 msgid "General"
 msgstr "Allmänt"
 
-#: gitk:11074
+#: gitk:11232
 msgid "Colors"
 msgstr "Färger"
 
-#: gitk:11075
+#: gitk:11233
 msgid "Fonts"
 msgstr "Teckensnitt"
 
-#: gitk:11125
+#: gitk:11283
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: välj färg för %s"
 
-#: gitk:11745
+#: gitk:11937
 msgid "Cannot find a git repository here."
 msgstr "Hittar inget git-arkiv här."
 
-#: gitk:11792
+#: gitk:11984
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Tvetydigt argument \"%s\": både revision och filnamn"
 
-#: gitk:11804
+#: gitk:11996
 msgid "Bad arguments to gitk:"
 msgstr "Felaktiga argument till gitk:"
 
-#: gitk:11907
+#: gitk:12099
 msgid "Command line"
 msgstr "Kommandorad"
 
index 08f3dda02d43f9b2e994ee2f7a8dccaecb82733e..408f2859d3c42becc333b8c0e4ee6366de7fdb14 100644 (file)
@@ -243,14 +243,11 @@ for gitweb (in gitweb/README), and gitweb.conf(5) manpage.
   GITWEB_CONFIG_SYSTEM build configuration variable, and override it
   through the GITWEB_CONFIG_SYSTEM environment variable.
 
-  Note that if per-instance configuration file exists, then system-wide
-  configuration is _not used at all_.  This is quite untypical and surprising
-  behavior.  On the other hand changing current behavior would break backwards
-  compatibility and can lead to unexpected changes in gitweb behavior.
-  Therefore gitweb also looks for common system-wide configuration file,
-  normally /etc/gitweb-common.conf (set during build time using build time
-  configuration variable GITWEB_CONFIG_COMMON, set it at runtime using
-  environment variable with the same name).  Settings from per-instance or
+  Note that the GITWEB_CONFIG_SYSTEM system-wide configuration file is
+  only used for instances that lack per-instance configuration file.
+  You can use GITWEB_CONFIG_COMMON common system-wide configuration
+  file (normally /etc/gitweb-common.conf) to keep common default
+  settings that apply to all instances.  Settings from per-instance or
   system-wide configuration file override those from common system-wide
   configuration file.
 
index 80950c018d54f3bbb7b700286b4923d39cbe2d00..f429f75897ed10684d017aeaeb93f1c9d9478e0b 100755 (executable)
@@ -85,6 +85,9 @@ our $project_maxdepth = "++GITWEB_PROJECT_MAXDEPTH++";
 # string of the home link on top of all pages
 our $home_link_str = "++GITWEB_HOME_LINK_STR++";
 
+# extra breadcrumbs preceding the home link
+our @extra_breadcrumbs = ();
+
 # name of your site or organization to appear in page titles
 # replace this with something more descriptive for clearer bookmarks
 our $site_name = "++GITWEB_SITENAME++"
@@ -1086,7 +1089,7 @@ sub evaluate_and_validate_params {
        our $search_use_regexp = $input_params{'search_use_regexp'};
 
        our $searchtext = $input_params{'searchtext'};
-       our $search_regexp;
+       our $search_regexp = undef;
        if (defined $searchtext) {
                if (length($searchtext) < 2) {
                        die_error(403, "At least two characters are required for search parameter");
@@ -3982,7 +3985,9 @@ sub print_nav_breadcrumbs_path {
 sub print_nav_breadcrumbs {
        my %opts = @_;
 
-       print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
+       for my $crumb (@extra_breadcrumbs, [ $home_link_str => $home_link ]) {
+               print $cgi->a({-href => esc_url($crumb->[1])}, $crumb->[0]) . " / ";
+       }
        if (defined $project) {
                my @dirname = split '/', $project;
                my $projectbasename = pop @dirname;
diff --git a/help.c b/help.c
index 02ba043319932411e90121d4147f25395782dfb5..f068925bb7f26055fb699cda7173a0c06ac46a22 100644 (file)
--- a/help.c
+++ b/help.c
@@ -7,6 +7,7 @@
 #include "string-list.h"
 #include "column.h"
 #include "version.h"
+#include "refs.h"
 
 void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
@@ -106,10 +107,7 @@ static int is_executable(const char *name)
            !S_ISREG(st.st_mode))
                return 0;
 
-#if defined(WIN32) || defined(__CYGWIN__)
-#if defined(__CYGWIN__)
-if ((st.st_mode & S_IXUSR) == 0)
-#endif
+#if defined(GIT_WINDOWS_NATIVE)
 {      /* cannot trust the executable bit, peek into the file instead */
        char buf[3] = { 0 };
        int n;
@@ -404,3 +402,52 @@ int cmd_version(int argc, const char **argv, const char *prefix)
        printf("git version %s\n", git_version_string);
        return 0;
 }
+
+struct similar_ref_cb {
+       const char *base_ref;
+       struct string_list *similar_refs;
+};
+
+static int append_similar_ref(const char *refname, const unsigned char *sha1,
+                             int flags, void *cb_data)
+{
+       struct similar_ref_cb *cb = (struct similar_ref_cb *)(cb_data);
+       char *branch = strrchr(refname, '/') + 1;
+       /* A remote branch of the same name is deemed similar */
+       if (!prefixcmp(refname, "refs/remotes/") &&
+           !strcmp(branch, cb->base_ref))
+               string_list_append(cb->similar_refs,
+                                  refname + strlen("refs/remotes/"));
+       return 0;
+}
+
+static struct string_list guess_refs(const char *ref)
+{
+       struct similar_ref_cb ref_cb;
+       struct string_list similar_refs = STRING_LIST_INIT_NODUP;
+
+       ref_cb.base_ref = ref;
+       ref_cb.similar_refs = &similar_refs;
+       for_each_ref(append_similar_ref, &ref_cb);
+       return similar_refs;
+}
+
+void help_unknown_ref(const char *ref, const char *cmd, const char *error)
+{
+       int i;
+       struct string_list suggested_refs = guess_refs(ref);
+
+       fprintf_ln(stderr, _("%s: %s - %s"), cmd, ref, error);
+
+       if (suggested_refs.nr > 0) {
+               fprintf_ln(stderr,
+                          Q_("\nDid you mean this?",
+                             "\nDid you mean one of these?",
+                             suggested_refs.nr));
+               for (i = 0; i < suggested_refs.nr; i++)
+                       fprintf(stderr, "\t%s\n", suggested_refs.items[i].string);
+       }
+
+       string_list_clear(&suggested_refs, 0);
+       exit(1);
+}
diff --git a/help.h b/help.h
index 0ae5a124a3af9912d551caed909aac77acc59b0b..b21d7c94e8ce429ac1f58bd566c69e154d84d6a0 100644 (file)
--- a/help.h
+++ b/help.h
@@ -27,4 +27,9 @@ extern void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
 extern int is_in_cmdlist(struct cmdnames *cmds, const char *name);
 extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
 
+/*
+ * call this to die(), when it is suspected that the user mistyped a
+ * ref to the command, to give suggested "correct" refs.
+ */
+extern void help_unknown_ref(const char *ref, const char *cmd, const char *error);
 #endif /* HELP_H */
index 6b85ffac271596cf469984a06d27423af7df2b35..8c464bd8051ed813175b01f832e5e78a91bba8ea 100644 (file)
@@ -410,14 +410,14 @@ static void get_info_refs(char *arg)
        strbuf_release(&buf);
 }
 
-static int show_head_ref(const char *name, const unsigned char *sha1,
+static int show_head_ref(const char *refname, const unsigned char *sha1,
        int flag, void *cb_data)
 {
        struct strbuf *buf = cb_data;
 
        if (flag & REF_ISSYMREF) {
-               unsigned char sha1[20];
-               const char *target = resolve_ref_unsafe(name, sha1, 1, NULL);
+               unsigned char unused[20];
+               const char *target = resolve_ref_unsafe(refname, unused, 1, NULL);
                const char *target_nons = strip_namespace(target);
 
                strbuf_addf(buf, "ref: %s\n", target_nons);
@@ -594,9 +594,11 @@ int main(int argc, char **argv)
 
                        if (strcmp(method, c->method)) {
                                const char *proto = getenv("SERVER_PROTOCOL");
-                               if (proto && !strcmp(proto, "HTTP/1.1"))
+                               if (proto && !strcmp(proto, "HTTP/1.1")) {
                                        http_status(405, "Method Not Allowed");
-                               else
+                                       hdr_str("Allow", !strcmp(c->method, "GET") ?
+                                               "GET, HEAD" : c->method);
+                               } else
                                        http_status(400, "Bad Request");
                                hdr_nocache();
                                end_headers();
index 395a8cfc1055fb6febc9cee559d8943bb4d9e829..cde6416d37751fe0724e7c021e858530a74cf466 100644 (file)
@@ -663,7 +663,7 @@ static void add_fetch_request(struct object *obj)
 
 static int add_send_request(struct object *obj, struct remote_lock *lock)
 {
-       struct transfer_request *request = request_queue_head;
+       struct transfer_request *request;
        struct packed_git *target;
 
        /* Keep locks active */
@@ -1976,7 +1976,7 @@ int main(int argc, char **argv)
                pushing = 0;
                if (prepare_revision_walk(&revs))
                        die("revision walk setup failed");
-               mark_edges_uninteresting(revs.commits, &revs, NULL);
+               mark_edges_uninteresting(&revs, NULL);
                objects_to_send = get_delta(&revs, ref_lock);
                finish_all_active_slots();
 
diff --git a/http.c b/http.c
index 92aba59082a4ed9aa0c92d8522793abcd332e279..2d086aedfac9a8ea384c53f3b1963149b880ed31 100644 (file)
--- a/http.c
+++ b/http.c
@@ -228,9 +228,15 @@ static void init_curl_http_auth(CURL *result)
 #else
        {
                static struct strbuf up = STRBUF_INIT;
-               strbuf_reset(&up);
-               strbuf_addf(&up, "%s:%s",
-                           http_auth.username, http_auth.password);
+               /*
+                * Note that we assume we only ever have a single set of
+                * credentials in a given program run, so we do not have
+                * to worry about updating this buffer, only setting its
+                * initial value.
+                */
+               if (!up.len)
+                       strbuf_addf(&up, "%s:%s",
+                               http_auth.username, http_auth.password);
                curl_easy_setopt(result, CURLOPT_USERPWD, up.buf);
        }
 #endif
index d9bcfb44dc334d86363fd60b576fcc2e2121f971..d6b65e204c6009e5c30f358810198319b70eda25 100644 (file)
 #ifdef NO_OPENSSL
 typedef void *SSL;
 #else
+#ifdef APPLE_COMMON_CRYPTO
+#include <CommonCrypto/CommonHMAC.h>
+#define HMAC_CTX CCHmacContext
+#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len)
+#define HMAC_Update CCHmacUpdate
+#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash)
+#define HMAC_CTX_cleanup(ignore)
+#define EVP_md5() kCCHmacAlgMD5
+#else
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#endif
 #include <openssl/x509v3.h>
 #endif
 
diff --git a/kwset.c b/kwset.c
index 5800999b4c16cd8f5eeb2301f72c33ec62c5ea71..a0f49b3aca934008465842dd37e52d11d099e23e 100644 (file)
--- a/kwset.c
+++ b/kwset.c
@@ -65,7 +65,7 @@ struct trie
   struct trie *fail;           /* Aho-Corasick failure function. */
   int depth;                   /* Depth of this node from the root. */
   int shift;                   /* Shift function for search failures. */
-  int maxshift;                        /* Max shift of self and descendents. */
+  int maxshift;                        /* Max shift of self and descendants. */
 };
 
 /* Structure returned opaquely to the caller, containing everything. */
@@ -308,7 +308,7 @@ treefails (register struct tree const *tree, struct trie const *fail,
   treefails(tree->rlink, fail, recourse);
 
   /* Find, in the chain of fails going back to the root, the first
-     node that has a descendent on the current label. */
+     node that has a descendant on the current label. */
   while (fail)
     {
       link = fail->links;
@@ -426,13 +426,13 @@ kwsprep (kwset_t kws)
         computing the delta table, failure function, and shift function. */
       for (curr = last = kwset->trie; curr; curr = curr->next)
        {
-         /* Enqueue the immediate descendents in the level order queue. */
+         /* Enqueue the immediate descendants in the level order queue. */
          enqueue(curr->links, &last);
 
          curr->shift = kwset->mind;
          curr->maxshift = kwset->mind;
 
-         /* Update the delta table for the descendents of this node. */
+         /* Update the delta table for the descendants of this node. */
          treedelta(curr->links, curr->depth, delta);
 
          /* Compute the failure function for the descendants of this node. */
@@ -450,7 +450,7 @@ kwsprep (kwset_t kws)
                  fail->shift = curr->depth - fail->depth;
 
              /* If the current node is accepting then the shift at the
-                fail and its descendents should be no larger than the
+                fail and its descendants should be no larger than the
                 difference of their depths. */
              if (curr->accepting && fail->maxshift > curr->depth - fail->depth)
                fail->maxshift = curr->depth - fail->depth;
diff --git a/line-log.c b/line-log.c
new file mode 100644 (file)
index 0000000..c2d01dc
--- /dev/null
@@ -0,0 +1,1277 @@
+#include "git-compat-util.h"
+#include "line-range.h"
+#include "cache.h"
+#include "tag.h"
+#include "blob.h"
+#include "tree.h"
+#include "diff.h"
+#include "commit.h"
+#include "decorate.h"
+#include "revision.h"
+#include "xdiff-interface.h"
+#include "strbuf.h"
+#include "log-tree.h"
+#include "graph.h"
+#include "userdiff.h"
+#include "line-log.h"
+
+static void range_set_grow(struct range_set *rs, size_t extra)
+{
+       ALLOC_GROW(rs->ranges, rs->nr + extra, rs->alloc);
+}
+
+/* Either initialization would be fine */
+#define RANGE_SET_INIT {0}
+
+static void range_set_init(struct range_set *rs, size_t prealloc)
+{
+       rs->alloc = rs->nr = 0;
+       rs->ranges = NULL;
+       if (prealloc)
+               range_set_grow(rs, prealloc);
+}
+
+static void range_set_release(struct range_set *rs)
+{
+       free(rs->ranges);
+       rs->alloc = rs->nr = 0;
+       rs->ranges = NULL;
+}
+
+/* dst must be uninitialized! */
+static void range_set_copy(struct range_set *dst, struct range_set *src)
+{
+       range_set_init(dst, src->nr);
+       memcpy(dst->ranges, src->ranges, src->nr*sizeof(struct range_set));
+       dst->nr = src->nr;
+}
+static void range_set_move(struct range_set *dst, struct range_set *src)
+{
+       range_set_release(dst);
+       dst->ranges = src->ranges;
+       dst->nr = src->nr;
+       dst->alloc = src->alloc;
+       src->ranges = NULL;
+       src->alloc = src->nr = 0;
+}
+
+/* tack on a _new_ range _at the end_ */
+static void range_set_append_unsafe(struct range_set *rs, long a, long b)
+{
+       assert(a <= b);
+       range_set_grow(rs, 1);
+       rs->ranges[rs->nr].start = a;
+       rs->ranges[rs->nr].end = b;
+       rs->nr++;
+}
+
+static void range_set_append(struct range_set *rs, long a, long b)
+{
+       assert(rs->nr == 0 || rs->ranges[rs->nr-1].end <= a);
+       range_set_append_unsafe(rs, a, b);
+}
+
+static int range_cmp(const void *_r, const void *_s)
+{
+       const struct range *r = _r;
+       const struct range *s = _s;
+
+       /* this could be simply 'return r.start-s.start', but for the types */
+       if (r->start == s->start)
+               return 0;
+       if (r->start < s->start)
+               return -1;
+       return 1;
+}
+
+/*
+ * Check that the ranges are non-empty, sorted and non-overlapping
+ */
+static void range_set_check_invariants(struct range_set *rs)
+{
+       int i;
+
+       if (!rs)
+               return;
+
+       if (rs->nr)
+               assert(rs->ranges[0].start < rs->ranges[0].end);
+
+       for (i = 1; i < rs->nr; i++) {
+               assert(rs->ranges[i-1].end < rs->ranges[i].start);
+               assert(rs->ranges[i].start < rs->ranges[i].end);
+       }
+}
+
+/*
+ * In-place pass of sorting and merging the ranges in the range set,
+ * to establish the invariants when we get the ranges from the user
+ */
+static void sort_and_merge_range_set(struct range_set *rs)
+{
+       int i;
+       int o = 0; /* output cursor */
+
+       qsort(rs->ranges, rs->nr, sizeof(struct range), range_cmp);
+
+       for (i = 0; i < rs->nr; i++) {
+               if (rs->ranges[i].start == rs->ranges[i].end)
+                       continue;
+               if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].end) {
+                       if (rs->ranges[o-1].end < rs->ranges[i].end)
+                               rs->ranges[o-1].end = rs->ranges[i].end;
+               } else {
+                       rs->ranges[o].start = rs->ranges[i].start;
+                       rs->ranges[o].end = rs->ranges[i].end;
+                       o++;
+               }
+       }
+       assert(o <= rs->nr);
+       rs->nr = o;
+
+       range_set_check_invariants(rs);
+}
+
+/*
+ * Union of range sets (i.e., sets of line numbers).  Used to merge
+ * them when searches meet at a common ancestor.
+ *
+ * This is also where the ranges are consolidated into canonical form:
+ * overlapping and adjacent ranges are merged, and empty ranges are
+ * removed.
+ */
+static void range_set_union(struct range_set *out,
+                            struct range_set *a, struct range_set *b)
+{
+       int i = 0, j = 0, o = 0;
+       struct range *ra = a->ranges;
+       struct range *rb = b->ranges;
+       /* cannot make an alias of out->ranges: it may change during grow */
+
+       assert(out->nr == 0);
+       while (i < a->nr || j < b->nr) {
+               struct range *new;
+               if (i < a->nr && j < b->nr) {
+                       if (ra[i].start < rb[j].start)
+                               new = &ra[i++];
+                       else if (ra[i].start > rb[j].start)
+                               new = &rb[j++];
+                       else if (ra[i].end < rb[j].end)
+                               new = &ra[i++];
+                       else
+                               new = &rb[j++];
+               } else if (i < a->nr)      /* b exhausted */
+                       new = &ra[i++];
+               else                       /* a exhausted */
+                       new = &rb[j++];
+               if (new->start == new->end)
+                       ; /* empty range */
+               else if (!o || out->ranges[o-1].end < new->start) {
+                       range_set_grow(out, 1);
+                       out->ranges[o].start = new->start;
+                       out->ranges[o].end = new->end;
+                       o++;
+               } else if (out->ranges[o-1].end < new->end) {
+                       out->ranges[o-1].end = new->end;
+               }
+       }
+       out->nr = o;
+}
+
+/*
+ * Difference of range sets (out = a \ b).  Pass the "interesting"
+ * ranges as 'a' and the target side of the diff as 'b': it removes
+ * the ranges for which the commit is responsible.
+ */
+static void range_set_difference(struct range_set *out,
+                                 struct range_set *a, struct range_set *b)
+{
+       int i, j =  0;
+       for (i = 0; i < a->nr; i++) {
+               long start = a->ranges[i].start;
+               long end = a->ranges[i].end;
+               while (start < end) {
+                       while (j < b->nr && start >= b->ranges[j].end)
+                               /*
+                                * a:         |-------
+                                * b: ------|
+                                */
+                               j++;
+                       if (j >= b->nr || end < b->ranges[j].start) {
+                               /*
+                                * b exhausted, or
+                                * a:  ----|
+                                * b:         |----
+                                */
+                               range_set_append(out, start, end);
+                               break;
+                       }
+                       if (start >= b->ranges[j].start) {
+                               /*
+                                * a:     |--????
+                                * b: |------|
+                                */
+                               start = b->ranges[j].end;
+                       } else if (end > b->ranges[j].start) {
+                               /*
+                                * a: |-----|
+                                * b:    |--?????
+                                */
+                               if (start < b->ranges[j].start)
+                                       range_set_append(out, start, b->ranges[j].start);
+                               start = b->ranges[j].end;
+                       }
+               }
+       }
+}
+
+static void diff_ranges_init(struct diff_ranges *diff)
+{
+       range_set_init(&diff->parent, 0);
+       range_set_init(&diff->target, 0);
+}
+
+static void diff_ranges_release(struct diff_ranges *diff)
+{
+       range_set_release(&diff->parent);
+       range_set_release(&diff->target);
+}
+
+void line_log_data_init(struct line_log_data *r)
+{
+       memset(r, 0, sizeof(struct line_log_data));
+       range_set_init(&r->ranges, 0);
+}
+
+static void line_log_data_clear(struct line_log_data *r)
+{
+       range_set_release(&r->ranges);
+       if (r->pair)
+               diff_free_filepair(r->pair);
+}
+
+static void free_line_log_data(struct line_log_data *r)
+{
+       while (r) {
+               struct line_log_data *next = r->next;
+               line_log_data_clear(r);
+               free(r);
+               r = next;
+       }
+}
+
+static struct line_log_data *
+search_line_log_data(struct line_log_data *list, const char *path,
+                    struct line_log_data **insertion_point)
+{
+       struct line_log_data *p = list;
+       if (insertion_point)
+               *insertion_point = NULL;
+       while (p) {
+               int cmp = strcmp(p->path, path);
+               if (!cmp)
+                       return p;
+               if (insertion_point && cmp < 0)
+                       *insertion_point = p;
+               p = p->next;
+       }
+       return NULL;
+}
+
+/*
+ * Note: takes ownership of 'path', which happens to be what the only
+ * caller needs.
+ */
+static void line_log_data_insert(struct line_log_data **list,
+                                char *path,
+                                long begin, long end)
+{
+       struct line_log_data *ip;
+       struct line_log_data *p = search_line_log_data(*list, path, &ip);
+
+       if (p) {
+               range_set_append_unsafe(&p->ranges, begin, end);
+               sort_and_merge_range_set(&p->ranges);
+               free(path);
+               return;
+       }
+
+       p = xcalloc(1, sizeof(struct line_log_data));
+       p->path = path;
+       range_set_append(&p->ranges, begin, end);
+       sort_and_merge_range_set(&p->ranges);
+       if (ip) {
+               p->next = ip->next;
+               ip->next = p;
+       } else {
+               p->next = *list;
+               *list = p;
+       }
+}
+
+struct collect_diff_cbdata {
+       struct diff_ranges *diff;
+};
+
+static int collect_diff_cb(long start_a, long count_a,
+                          long start_b, long count_b,
+                          void *data)
+{
+       struct collect_diff_cbdata *d = data;
+
+       if (count_a >= 0)
+               range_set_append(&d->diff->parent, start_a, start_a + count_a);
+       if (count_b >= 0)
+               range_set_append(&d->diff->target, start_b, start_b + count_b);
+
+       return 0;
+}
+
+static void collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out)
+{
+       struct collect_diff_cbdata cbdata = {NULL};
+       xpparam_t xpp;
+       xdemitconf_t xecfg;
+       xdemitcb_t ecb;
+
+       memset(&xpp, 0, sizeof(xpp));
+       memset(&xecfg, 0, sizeof(xecfg));
+       xecfg.ctxlen = xecfg.interhunkctxlen = 0;
+
+       cbdata.diff = out;
+       xecfg.hunk_func = collect_diff_cb;
+       memset(&ecb, 0, sizeof(ecb));
+       ecb.priv = &cbdata;
+       xdi_diff(parent, target, &xpp, &xecfg, &ecb);
+}
+
+/*
+ * These are handy for debugging.  Removing them with #if 0 silences
+ * the "unused function" warning.
+ */
+#if 0
+static void dump_range_set(struct range_set *rs, const char *desc)
+{
+       int i;
+       printf("range set %s (%d items):\n", desc, rs->nr);
+       for (i = 0; i < rs->nr; i++)
+               printf("\t[%ld,%ld]\n", rs->ranges[i].start, rs->ranges[i].end);
+}
+
+static void dump_line_log_data(struct line_log_data *r)
+{
+       char buf[4096];
+       while (r) {
+               snprintf(buf, 4096, "file %s\n", r->path);
+               dump_range_set(&r->ranges, buf);
+               r = r->next;
+       }
+}
+
+static void dump_diff_ranges(struct diff_ranges *diff, const char *desc)
+{
+       int i;
+       assert(diff->parent.nr == diff->target.nr);
+       printf("diff ranges %s (%d items):\n", desc, diff->parent.nr);
+       printf("\tparent\ttarget\n");
+       for (i = 0; i < diff->parent.nr; i++) {
+               printf("\t[%ld,%ld]\t[%ld,%ld]\n",
+                      diff->parent.ranges[i].start,
+                      diff->parent.ranges[i].end,
+                      diff->target.ranges[i].start,
+                      diff->target.ranges[i].end);
+       }
+}
+#endif
+
+
+static int ranges_overlap(struct range *a, struct range *b)
+{
+       return !(a->end <= b->start || b->end <= a->start);
+}
+
+/*
+ * Given a diff and the set of interesting ranges, determine all hunks
+ * of the diff which touch (overlap) at least one of the interesting
+ * ranges in the target.
+ */
+static void diff_ranges_filter_touched(struct diff_ranges *out,
+                                      struct diff_ranges *diff,
+                                      struct range_set *rs)
+{
+       int i, j = 0;
+
+       assert(out->target.nr == 0);
+
+       for (i = 0; i < diff->target.nr; i++) {
+               while (diff->target.ranges[i].start > rs->ranges[j].end) {
+                       j++;
+                       if (j == rs->nr)
+                               return;
+               }
+               if (ranges_overlap(&diff->target.ranges[i], &rs->ranges[j])) {
+                       range_set_append(&out->parent,
+                                        diff->parent.ranges[i].start,
+                                        diff->parent.ranges[i].end);
+                       range_set_append(&out->target,
+                                        diff->target.ranges[i].start,
+                                        diff->target.ranges[i].end);
+               }
+       }
+}
+
+/*
+ * Adjust the line counts in 'rs' to account for the lines
+ * added/removed in the diff.
+ */
+static void range_set_shift_diff(struct range_set *out,
+                                struct range_set *rs,
+                                struct diff_ranges *diff)
+{
+       int i, j = 0;
+       long offset = 0;
+       struct range *src = rs->ranges;
+       struct range *target = diff->target.ranges;
+       struct range *parent = diff->parent.ranges;
+
+       for (i = 0; i < rs->nr; i++) {
+               while (j < diff->target.nr && src[i].start >= target[j].start) {
+                       offset += (parent[j].end-parent[j].start)
+                               - (target[j].end-target[j].start);
+                       j++;
+               }
+               range_set_append(out, src[i].start+offset, src[i].end+offset);
+       }
+}
+
+/*
+ * Given a diff and the set of interesting ranges, map the ranges
+ * across the diff.  That is: observe that the target commit takes
+ * blame for all the + (target-side) ranges.  So for every pair of
+ * ranges in the diff that was touched, we remove the latter and add
+ * its parent side.
+ */
+static void range_set_map_across_diff(struct range_set *out,
+                                     struct range_set *rs,
+                                     struct diff_ranges *diff,
+                                     struct diff_ranges **touched_out)
+{
+       struct diff_ranges *touched = xmalloc(sizeof(*touched));
+       struct range_set tmp1 = RANGE_SET_INIT;
+       struct range_set tmp2 = RANGE_SET_INIT;
+
+       diff_ranges_init(touched);
+       diff_ranges_filter_touched(touched, diff, rs);
+       range_set_difference(&tmp1, rs, &touched->target);
+       range_set_shift_diff(&tmp2, &tmp1, diff);
+       range_set_union(out, &tmp2, &touched->parent);
+       range_set_release(&tmp1);
+       range_set_release(&tmp2);
+
+       *touched_out = touched;
+}
+
+static struct commit *check_single_commit(struct rev_info *revs)
+{
+       struct object *commit = NULL;
+       int found = -1;
+       int i;
+
+       for (i = 0; i < revs->pending.nr; i++) {
+               struct object *obj = revs->pending.objects[i].item;
+               if (obj->flags & UNINTERESTING)
+                       continue;
+               while (obj->type == OBJ_TAG)
+                       obj = deref_tag(obj, NULL, 0);
+               if (obj->type != OBJ_COMMIT)
+                       die("Non commit %s?", revs->pending.objects[i].name);
+               if (commit)
+                       die("More than one commit to dig from: %s and %s?",
+                           revs->pending.objects[i].name,
+                           revs->pending.objects[found].name);
+               commit = obj;
+               found = i;
+       }
+
+       if (!commit)
+               die("No commit specified?");
+
+       return (struct commit *) commit;
+}
+
+static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)
+{
+       unsigned mode;
+       unsigned char sha1[20];
+
+       if (get_tree_entry(commit->object.sha1, spec->path,
+                          sha1, &mode))
+               die("There is no path %s in the commit", spec->path);
+       fill_filespec(spec, sha1, 1, mode);
+
+       return;
+}
+
+static void fill_line_ends(struct diff_filespec *spec, long *lines,
+                          unsigned long **line_ends)
+{
+       int num = 0, size = 50;
+       long cur = 0;
+       unsigned long *ends = NULL;
+       char *data = NULL;
+
+       if (diff_populate_filespec(spec, 0))
+               die("Cannot read blob %s", sha1_to_hex(spec->sha1));
+
+       ends = xmalloc(size * sizeof(*ends));
+       ends[cur++] = 0;
+       data = spec->data;
+       while (num < spec->size) {
+               if (data[num] == '\n' || num == spec->size - 1) {
+                       ALLOC_GROW(ends, (cur + 1), size);
+                       ends[cur++] = num;
+               }
+               num++;
+       }
+
+       /* shrink the array to fit the elements */
+       ends = xrealloc(ends, cur * sizeof(*ends));
+       *lines = cur-1;
+       *line_ends = ends;
+}
+
+struct nth_line_cb {
+       struct diff_filespec *spec;
+       long lines;
+       unsigned long *line_ends;
+};
+
+static const char *nth_line(void *data, long line)
+{
+       struct nth_line_cb *d = data;
+       assert(d && line <= d->lines);
+       assert(d->spec && d->spec->data);
+
+       if (line == 0)
+               return (char *)d->spec->data;
+       else
+               return (char *)d->spec->data + d->line_ends[line] + 1;
+}
+
+static struct line_log_data *
+parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
+{
+       long lines = 0;
+       unsigned long *ends = NULL;
+       struct nth_line_cb cb_data;
+       struct string_list_item *item;
+       struct line_log_data *ranges = NULL;
+
+       for_each_string_list_item(item, args) {
+               const char *name_part, *range_part;
+               char *full_name;
+               struct diff_filespec *spec;
+               long begin = 0, end = 0;
+
+               name_part = skip_range_arg(item->string);
+               if (!name_part || *name_part != ':' || !name_part[1])
+                       die("-L argument '%s' not of the form start,end:file",
+                           item->string);
+               range_part = xstrndup(item->string, name_part - item->string);
+               name_part++;
+
+               full_name = prefix_path(prefix, prefix ? strlen(prefix) : 0,
+                                       name_part);
+
+               spec = alloc_filespec(full_name);
+               fill_blob_sha1(commit, spec);
+               fill_line_ends(spec, &lines, &ends);
+               cb_data.spec = spec;
+               cb_data.lines = lines;
+               cb_data.line_ends = ends;
+
+               if (parse_range_arg(range_part, nth_line, &cb_data,
+                                   lines, &begin, &end,
+                                   full_name))
+                       die("malformed -L argument '%s'", range_part);
+               if (begin < 1)
+                       begin = 1;
+               if (end < 1)
+                       end = lines;
+               begin--;
+               if (lines < end || lines < begin)
+                       die("file %s has only %ld lines", name_part, lines);
+               line_log_data_insert(&ranges, full_name, begin, end);
+
+               free_filespec(spec);
+               free(ends);
+               ends = NULL;
+       }
+
+       return ranges;
+}
+
+static struct line_log_data *line_log_data_copy_one(struct line_log_data *r)
+{
+       struct line_log_data *ret = xmalloc(sizeof(*ret));
+
+       assert(r);
+       line_log_data_init(ret);
+       range_set_copy(&ret->ranges, &r->ranges);
+
+       ret->path = xstrdup(r->path);
+
+       return ret;
+}
+
+static struct line_log_data *
+line_log_data_copy(struct line_log_data *r)
+{
+       struct line_log_data *ret = NULL;
+       struct line_log_data *tmp = NULL, *prev = NULL;
+
+       assert(r);
+       ret = tmp = prev = line_log_data_copy_one(r);
+       r = r->next;
+       while (r) {
+               tmp = line_log_data_copy_one(r);
+               prev->next = tmp;
+               prev = tmp;
+               r = r->next;
+       }
+
+       return ret;
+}
+
+/* merge two range sets across files */
+static struct line_log_data *line_log_data_merge(struct line_log_data *a,
+                                                struct line_log_data *b)
+{
+       struct line_log_data *head = NULL, **pp = &head;
+
+       while (a || b) {
+               struct line_log_data *src;
+               struct line_log_data *src2 = NULL;
+               struct line_log_data *d;
+               int cmp;
+               if (!a)
+                       cmp = 1;
+               else if (!b)
+                       cmp = -1;
+               else
+                       cmp = strcmp(a->path, b->path);
+               if (cmp < 0) {
+                       src = a;
+                       a = a->next;
+               } else if (cmp == 0) {
+                       src = a;
+                       a = a->next;
+                       src2 = b;
+                       b = b->next;
+               } else {
+                       src = b;
+                       b = b->next;
+               }
+               d = xmalloc(sizeof(struct line_log_data));
+               line_log_data_init(d);
+               d->path = xstrdup(src->path);
+               *pp = d;
+               pp = &d->next;
+               if (src2)
+                       range_set_union(&d->ranges, &src->ranges, &src2->ranges);
+               else
+                       range_set_copy(&d->ranges, &src->ranges);
+       }
+
+       return head;
+}
+
+static void add_line_range(struct rev_info *revs, struct commit *commit,
+                          struct line_log_data *range)
+{
+       struct line_log_data *old = NULL;
+       struct line_log_data *new = NULL;
+
+       old = lookup_decoration(&revs->line_log_data, &commit->object);
+       if (old && range) {
+               new = line_log_data_merge(old, range);
+               free_line_log_data(old);
+       } else if (range)
+               new = line_log_data_copy(range);
+
+       if (new)
+               add_decoration(&revs->line_log_data, &commit->object, new);
+}
+
+static void clear_commit_line_range(struct rev_info *revs, struct commit *commit)
+{
+       struct line_log_data *r;
+       r = lookup_decoration(&revs->line_log_data, &commit->object);
+       if (!r)
+               return;
+       free_line_log_data(r);
+       add_decoration(&revs->line_log_data, &commit->object, NULL);
+}
+
+static struct line_log_data *lookup_line_range(struct rev_info *revs,
+                                              struct commit *commit)
+{
+       struct line_log_data *ret = NULL;
+       struct line_log_data *d;
+
+       ret = lookup_decoration(&revs->line_log_data, &commit->object);
+
+       for (d = ret; d; d = d->next)
+               range_set_check_invariants(&d->ranges);
+
+       return ret;
+}
+
+void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args)
+{
+       struct commit *commit = NULL;
+       struct line_log_data *range;
+
+       commit = check_single_commit(rev);
+       range = parse_lines(commit, prefix, args);
+       add_line_range(rev, commit, range);
+
+       if (!rev->diffopt.detect_rename) {
+               int i, count = 0;
+               struct line_log_data *r = range;
+               const char **paths;
+               while (r) {
+                       count++;
+                       r = r->next;
+               }
+               paths = xmalloc((count+1)*sizeof(char *));
+               r = range;
+               for (i = 0; i < count; i++) {
+                       paths[i] = xstrdup(r->path);
+                       r = r->next;
+               }
+               paths[count] = NULL;
+               init_pathspec(&rev->diffopt.pathspec, paths);
+               free(paths);
+       }
+}
+
+static void load_tree_desc(struct tree_desc *desc, void **tree,
+                          const unsigned char *sha1)
+{
+       unsigned long size;
+       *tree = read_object_with_reference(sha1, tree_type, &size, NULL);
+       if (!*tree)
+               die("Unable to read tree (%s)", sha1_to_hex(sha1));
+       init_tree_desc(desc, *tree, size);
+}
+
+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)
+{
+       assert(src != dst);
+       memcpy(dst, src, sizeof(struct diff_queue_struct));
+       DIFF_QUEUE_CLEAR(src);
+}
+
+static void filter_diffs_for_paths(struct line_log_data *range, int keep_deletions)
+{
+       int i;
+       struct diff_queue_struct outq;
+       DIFF_QUEUE_CLEAR(&outq);
+
+       for (i = 0; i < diff_queued_diff.nr; i++) {
+               struct diff_filepair *p = diff_queued_diff.queue[i];
+               struct line_log_data *rg = NULL;
+
+               if (!DIFF_FILE_VALID(p->two)) {
+                       if (keep_deletions)
+                               diff_q(&outq, p);
+                       else
+                               diff_free_filepair(p);
+                       continue;
+               }
+               for (rg = range; rg; rg = rg->next) {
+                       if (!strcmp(rg->path, p->two->path))
+                               break;
+               }
+               if (rg)
+                       diff_q(&outq, p);
+               else
+                       diff_free_filepair(p);
+       }
+       free(diff_queued_diff.queue);
+       diff_queued_diff = outq;
+}
+
+static inline int diff_might_be_rename(void)
+{
+       int i;
+       for (i = 0; i < diff_queued_diff.nr; i++)
+               if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one)) {
+                       /* fprintf(stderr, "diff_might_be_rename found creation of: %s\n", */
+                       /*      diff_queued_diff.queue[i]->two->path); */
+                       return 1;
+               }
+       return 0;
+}
+
+static void queue_diffs(struct line_log_data *range,
+                       struct diff_options *opt,
+                       struct diff_queue_struct *queue,
+                       struct commit *commit, struct commit *parent)
+{
+       void *tree1 = NULL, *tree2 = NULL;
+       struct tree_desc desc1, desc2;
+
+       assert(commit);
+       load_tree_desc(&desc2, &tree2, commit->tree->object.sha1);
+       if (parent)
+               load_tree_desc(&desc1, &tree1, parent->tree->object.sha1);
+       else
+               init_tree_desc(&desc1, "", 0);
+
+       DIFF_QUEUE_CLEAR(&diff_queued_diff);
+       diff_tree(&desc1, &desc2, "", opt);
+       if (opt->detect_rename) {
+               filter_diffs_for_paths(range, 1);
+               if (diff_might_be_rename())
+                       diffcore_std(opt);
+               filter_diffs_for_paths(range, 0);
+       }
+       move_diff_queue(queue, &diff_queued_diff);
+
+       if (tree1)
+               free(tree1);
+       if (tree2)
+               free(tree2);
+}
+
+static char *get_nth_line(long line, unsigned long *ends, void *data)
+{
+       if (line == 0)
+               return (char *)data;
+       else
+               return (char *)data + ends[line] + 1;
+}
+
+static void print_line(const char *prefix, char first,
+                      long line, unsigned long *ends, void *data,
+                      const char *color, const char *reset)
+{
+       char *begin = get_nth_line(line, ends, data);
+       char *end = get_nth_line(line+1, ends, data);
+       int had_nl = 0;
+
+       if (end > begin && end[-1] == '\n') {
+               end--;
+               had_nl = 1;
+       }
+
+       fputs(prefix, stdout);
+       fputs(color, stdout);
+       putchar(first);
+       fwrite(begin, 1, end-begin, stdout);
+       fputs(reset, stdout);
+       putchar('\n');
+       if (!had_nl)
+               fputs("\\ No newline at end of file\n", stdout);
+}
+
+static char *output_prefix(struct diff_options *opt)
+{
+       char *prefix = "";
+
+       if (opt->output_prefix) {
+               struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data);
+               prefix = sb->buf;
+       }
+
+       return prefix;
+}
+
+static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range)
+{
+       int i, j = 0;
+       long p_lines, t_lines;
+       unsigned long *p_ends = NULL, *t_ends = NULL;
+       struct diff_filepair *pair = range->pair;
+       struct diff_ranges *diff = &range->diff;
+
+       struct diff_options *opt = &rev->diffopt;
+       char *prefix = output_prefix(opt);
+       const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET);
+       const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO);
+       const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO);
+       const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD);
+       const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW);
+       const char *c_plain = diff_get_color(opt->use_color, DIFF_PLAIN);
+
+       if (!pair || !diff)
+               return;
+
+       if (pair->one->sha1_valid)
+               fill_line_ends(pair->one, &p_lines, &p_ends);
+       fill_line_ends(pair->two, &t_lines, &t_ends);
+
+       printf("%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
+       printf("%s%s--- %s%s%s\n", prefix, c_meta,
+              pair->one->sha1_valid ? "a/" : "",
+              pair->one->sha1_valid ? pair->one->path : "/dev/null",
+              c_reset);
+       printf("%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset);
+       for (i = 0; i < range->ranges.nr; i++) {
+               long p_start, p_end;
+               long t_start = range->ranges.ranges[i].start;
+               long t_end = range->ranges.ranges[i].end;
+               long t_cur = t_start;
+               int j_last;
+
+               while (j < diff->target.nr && diff->target.ranges[j].end < t_start)
+                       j++;
+               if (j == diff->target.nr || diff->target.ranges[j].start > t_end)
+                       continue;
+
+               /* Scan ahead to determine the last diff that falls in this range */
+               j_last = j;
+               while (j_last < diff->target.nr && diff->target.ranges[j_last].start < t_end)
+                       j_last++;
+               if (j_last > j)
+                       j_last--;
+
+               /*
+                * Compute parent hunk headers: we know that the diff
+                * has the correct line numbers (but not all hunks).
+                * So it suffices to shift the start/end according to
+                * the line numbers of the first/last hunk(s) that
+                * fall in this range.
+                */
+               if (t_start < diff->target.ranges[j].start)
+                       p_start = diff->parent.ranges[j].start - (diff->target.ranges[j].start-t_start);
+               else
+                       p_start = diff->parent.ranges[j].start;
+               if (t_end > diff->target.ranges[j_last].end)
+                       p_end = diff->parent.ranges[j_last].end + (t_end-diff->target.ranges[j_last].end);
+               else
+                       p_end = diff->parent.ranges[j_last].end;
+
+               if (!p_start && !p_end) {
+                       p_start = -1;
+                       p_end = -1;
+               }
+
+               /* Now output a diff hunk for this range */
+               printf("%s%s@@ -%ld,%ld +%ld,%ld @@%s\n",
+                      prefix, c_frag,
+                      p_start+1, p_end-p_start, t_start+1, t_end-t_start,
+                      c_reset);
+               while (j < diff->target.nr && diff->target.ranges[j].start < t_end) {
+                       int k;
+                       for (; t_cur < diff->target.ranges[j].start; t_cur++)
+                               print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
+                                          c_plain, c_reset);
+                       for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++)
+                               print_line(prefix, '-', k, p_ends, pair->one->data,
+                                          c_old, c_reset);
+                       for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++)
+                               print_line(prefix, '+', t_cur, t_ends, pair->two->data,
+                                          c_new, c_reset);
+                       j++;
+               }
+               for (; t_cur < t_end; t_cur++)
+                       print_line(prefix, ' ', t_cur, t_ends, pair->two->data,
+                                  c_plain, c_reset);
+       }
+
+       free(p_ends);
+       free(t_ends);
+}
+
+/*
+ * NEEDSWORK: manually building a diff here is not the Right
+ * Thing(tm).  log -L should be built into the diff pipeline.
+ */
+static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range)
+{
+       puts(output_prefix(&rev->diffopt));
+       while (range) {
+               dump_diff_hacky_one(rev, range);
+               range = range->next;
+       }
+}
+
+/*
+ * Unlike most other functions, this destructively operates on
+ * 'range'.
+ */
+static int process_diff_filepair(struct rev_info *rev,
+                                struct diff_filepair *pair,
+                                struct line_log_data *range,
+                                struct diff_ranges **diff_out)
+{
+       struct line_log_data *rg = range;
+       struct range_set tmp;
+       struct diff_ranges diff;
+       mmfile_t file_parent, file_target;
+
+       assert(pair->two->path);
+       while (rg) {
+               assert(rg->path);
+               if (!strcmp(rg->path, pair->two->path))
+                       break;
+               rg = rg->next;
+       }
+
+       if (!rg)
+               return 0;
+       if (rg->ranges.nr == 0)
+               return 0;
+
+       assert(pair->two->sha1_valid);
+       diff_populate_filespec(pair->two, 0);
+       file_target.ptr = pair->two->data;
+       file_target.size = pair->two->size;
+
+       if (pair->one->sha1_valid) {
+               diff_populate_filespec(pair->one, 0);
+               file_parent.ptr = pair->one->data;
+               file_parent.size = pair->one->size;
+       } else {
+               file_parent.ptr = "";
+               file_parent.size = 0;
+       }
+
+       diff_ranges_init(&diff);
+       collect_diff(&file_parent, &file_target, &diff);
+
+       /* NEEDSWORK should apply some heuristics to prevent mismatches */
+       free(rg->path);
+       rg->path = xstrdup(pair->one->path);
+
+       range_set_init(&tmp, 0);
+       range_set_map_across_diff(&tmp, &rg->ranges, &diff, diff_out);
+       range_set_release(&rg->ranges);
+       range_set_move(&rg->ranges, &tmp);
+
+       diff_ranges_release(&diff);
+
+       return ((*diff_out)->parent.nr > 0);
+}
+
+static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair)
+{
+       struct diff_filepair *new = xmalloc(sizeof(struct diff_filepair));
+       new->one = pair->one;
+       new->two = pair->two;
+       new->one->count++;
+       new->two->count++;
+       return new;
+}
+
+static void free_diffqueues(int n, struct diff_queue_struct *dq)
+{
+       int i, j;
+       for (i = 0; i < n; i++)
+               for (j = 0; j < dq[i].nr; j++)
+                       diff_free_filepair(dq[i].queue[j]);
+       free(dq);
+}
+
+static int process_all_files(struct line_log_data **range_out,
+                            struct rev_info *rev,
+                            struct diff_queue_struct *queue,
+                            struct line_log_data *range)
+{
+       int i, changed = 0;
+
+       *range_out = line_log_data_copy(range);
+
+       for (i = 0; i < queue->nr; i++) {
+               struct diff_ranges *pairdiff = NULL;
+               struct diff_filepair *pair = queue->queue[i];
+               if (process_diff_filepair(rev, pair, *range_out, &pairdiff)) {
+                       /*
+                        * Store away the diff for later output.  We
+                        * tuck it in the ranges we got as _input_,
+                        * since that's the commit that caused the
+                        * diff.
+                        *
+                        * NEEDSWORK not enough when we get around to
+                        * doing something interesting with merges;
+                        * currently each invocation on a merge parent
+                        * trashes the previous one's diff.
+                        *
+                        * NEEDSWORK tramples over data structures not owned here
+                        */
+                       struct line_log_data *rg = range;
+                       changed++;
+                       while (rg && strcmp(rg->path, pair->two->path))
+                               rg = rg->next;
+                       assert(rg);
+                       rg->pair = diff_filepair_dup(queue->queue[i]);
+                       memcpy(&rg->diff, pairdiff, sizeof(struct diff_ranges));
+               }
+       }
+
+       return changed;
+}
+
+int line_log_print(struct rev_info *rev, struct commit *commit)
+{
+       struct line_log_data *range = lookup_line_range(rev, commit);
+
+       show_log(rev);
+       dump_diff_hacky(rev, range);
+       return 1;
+}
+
+static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *commit,
+                                         struct line_log_data *range)
+{
+       struct commit *parent = NULL;
+       struct diff_queue_struct queue;
+       struct line_log_data *parent_range;
+       int changed;
+
+       if (commit->parents)
+               parent = commit->parents->item;
+
+       queue_diffs(range, &rev->diffopt, &queue, commit, parent);
+       changed = process_all_files(&parent_range, rev, &queue, range);
+       if (parent)
+               add_line_range(rev, parent, parent_range);
+       return changed;
+}
+
+static int process_ranges_merge_commit(struct rev_info *rev, struct commit *commit,
+                                      struct line_log_data *range)
+{
+       struct diff_queue_struct *diffqueues;
+       struct line_log_data **cand;
+       struct commit **parents;
+       struct commit_list *p;
+       int i;
+       int nparents = count_parents(commit);
+
+       diffqueues = xmalloc(nparents * sizeof(*diffqueues));
+       cand = xmalloc(nparents * sizeof(*cand));
+       parents = xmalloc(nparents * sizeof(*parents));
+
+       p = commit->parents;
+       for (i = 0; i < nparents; i++) {
+               parents[i] = p->item;
+               p = p->next;
+               queue_diffs(range, &rev->diffopt, &diffqueues[i], commit, parents[i]);
+       }
+
+       for (i = 0; i < nparents; i++) {
+               int changed;
+               cand[i] = NULL;
+               changed = process_all_files(&cand[i], rev, &diffqueues[i], range);
+               if (!changed) {
+                       /*
+                        * This parent can take all the blame, so we
+                        * don't follow any other path in history
+                        */
+                       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;
+                       free(parents);
+                       free(cand);
+                       free_diffqueues(nparents, diffqueues);
+                       /* NEEDSWORK leaking like a sieve */
+                       return 0;
+               }
+       }
+
+       /*
+        * No single parent took the blame.  We add the candidates
+        * from the above loop to the parents.
+        */
+       for (i = 0; i < nparents; i++) {
+               add_line_range(rev, parents[i], cand[i]);
+       }
+
+       clear_commit_line_range(rev, commit);
+       free(parents);
+       free(cand);
+       free_diffqueues(nparents, diffqueues);
+       return 1;
+
+       /* NEEDSWORK evil merge detection stuff */
+       /* NEEDSWORK leaking like a sieve */
+}
+
+static int process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit)
+{
+       struct line_log_data *range = lookup_line_range(rev, commit);
+       int changed = 0;
+
+       if (range) {
+               if (!commit->parents || !commit->parents->next)
+                       changed = process_ranges_ordinary_commit(rev, commit, range);
+               else
+                       changed = process_ranges_merge_commit(rev, commit, range);
+       }
+
+       if (!changed)
+               commit->object.flags |= TREESAME;
+
+       return changed;
+}
+
+static enum rewrite_result line_log_rewrite_one(struct rev_info *rev, struct commit **pp)
+{
+       for (;;) {
+               struct commit *p = *pp;
+               if (p->parents && p->parents->next)
+                       return rewrite_one_ok;
+               if (p->object.flags & UNINTERESTING)
+                       return rewrite_one_ok;
+               if (!(p->object.flags & TREESAME))
+                       return rewrite_one_ok;
+               if (!p->parents)
+                       return rewrite_one_noparents;
+               *pp = p->parents->item;
+       }
+}
+
+int line_log_filter(struct rev_info *rev)
+{
+       struct commit *commit;
+       struct commit_list *list = rev->commits;
+       struct commit_list *out = NULL, **pp = &out;
+
+       while (list) {
+               struct commit_list *to_free = NULL;
+               commit = list->item;
+               if (process_ranges_arbitrary_commit(rev, commit)) {
+                       *pp = list;
+                       pp = &list->next;
+               } else
+                       to_free = list;
+               list = list->next;
+               free(to_free);
+       }
+       *pp = NULL;
+
+       for (list = out; list; list = list->next)
+               rewrite_parents(rev, list->item, line_log_rewrite_one);
+
+       rev->commits = out;
+
+       return 0;
+}
diff --git a/line-log.h b/line-log.h
new file mode 100644 (file)
index 0000000..8bea45f
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef LINE_LOG_H
+#define LINE_LOG_H
+
+#include "diffcore.h"
+
+struct rev_info;
+struct commit;
+
+/* A range [start,end].  Lines are numbered starting at 0, and the
+ * ranges include start but exclude end. */
+struct range {
+       long start, end;
+};
+
+/* A set of ranges.  The ranges must always be disjoint and sorted. */
+struct range_set {
+       int alloc, nr;
+       struct range *ranges;
+};
+
+/* A diff, encoded as the set of pre- and post-image ranges where the
+ * files differ. A pair of ranges corresponds to a hunk. */
+struct diff_ranges {
+       struct range_set parent;
+       struct range_set target;
+};
+
+/* Linked list of interesting files and their associated ranges.  The
+ * list must be kept sorted by path.
+ *
+ * For simplicity, even though this is highly redundant, each
+ * line_log_data owns its 'path'.
+ */
+struct line_log_data {
+       struct line_log_data *next;
+       char *path;
+       char status;
+       struct range_set ranges;
+       int arg_alloc, arg_nr;
+       const char **args;
+       struct diff_filepair *pair;
+       struct diff_ranges diff;
+};
+
+extern void line_log_data_init(struct line_log_data *r);
+
+extern void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args);
+
+extern int line_log_filter(struct rev_info *rev);
+
+extern int line_log_print(struct rev_info *rev, struct commit *commit);
+
+#endif /* LINE_LOG_H */
diff --git a/line-range.c b/line-range.c
new file mode 100644 (file)
index 0000000..3942475
--- /dev/null
@@ -0,0 +1,250 @@
+#include "git-compat-util.h"
+#include "line-range.h"
+#include "xdiff-interface.h"
+#include "strbuf.h"
+#include "userdiff.h"
+
+/*
+ * Parse one item in the -L option
+ */
+static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
+                            void *data, long lines, long begin, long *ret)
+{
+       char *term;
+       const char *line;
+       long num;
+       int reg_error;
+       regex_t regexp;
+       regmatch_t match[1];
+
+       /* Allow "-L <something>,+20" to mean starting at <something>
+        * for 20 lines, or "-L <something>,-5" for 5 lines ending at
+        * <something>.
+        */
+       if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
+               num = strtol(spec + 1, &term, 10);
+               if (term != spec + 1) {
+                       if (!ret)
+                               return term;
+                       if (spec[0] == '-')
+                               num = 0 - num;
+                       if (0 < num)
+                               *ret = begin + num - 2;
+                       else if (!num)
+                               *ret = begin;
+                       else
+                               *ret = begin + num;
+                       return term;
+               }
+               return spec;
+       }
+       num = strtol(spec, &term, 10);
+       if (term != spec) {
+               if (ret)
+                       *ret = num;
+               return term;
+       }
+       if (spec[0] != '/')
+               return spec;
+
+       /* it could be a regexp of form /.../ */
+       for (term = (char *) spec + 1; *term && *term != '/'; term++) {
+               if (*term == '\\')
+                       term++;
+       }
+       if (*term != '/')
+               return spec;
+
+       /* in the scan-only case we are not interested in the regex */
+       if (!ret)
+               return term+1;
+
+       /* try [spec+1 .. term-1] as regexp */
+       *term = 0;
+       begin--; /* input is in human terms */
+       line = nth_line(data, begin);
+
+       if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
+           !(reg_error = regexec(&regexp, line, 1, match, 0))) {
+               const char *cp = line + match[0].rm_so;
+               const char *nline;
+
+               while (begin++ < lines) {
+                       nline = nth_line(data, begin);
+                       if (line <= cp && cp < nline)
+                               break;
+                       line = nline;
+               }
+               *ret = begin;
+               regfree(&regexp);
+               *term++ = '/';
+               return term;
+       }
+       else {
+               char errbuf[1024];
+               regerror(reg_error, &regexp, errbuf, 1024);
+               die("-L parameter '%s': %s", spec + 1, errbuf);
+       }
+}
+
+static int match_funcname(xdemitconf_t *xecfg, const char *bol, const char *eol)
+{
+       if (xecfg) {
+               char buf[1];
+               return xecfg->find_func(bol, eol - bol, buf, 1,
+                                       xecfg->find_func_priv) >= 0;
+       }
+
+       if (bol == eol)
+               return 0;
+       if (isalpha(*bol) || *bol == '_' || *bol == '$')
+               return 1;
+       return 0;
+}
+
+static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char *start,
+                                                regex_t *regexp)
+{
+       int reg_error;
+       regmatch_t match[1];
+       while (1) {
+               const char *bol, *eol;
+               reg_error = regexec(regexp, start, 1, match, 0);
+               if (reg_error == REG_NOMATCH)
+                       return NULL;
+               else if (reg_error) {
+                       char errbuf[1024];
+                       regerror(reg_error, regexp, errbuf, 1024);
+                       die("-L parameter: regexec() failed: %s", errbuf);
+               }
+               /* determine extent of line matched */
+               bol = start+match[0].rm_so;
+               eol = start+match[0].rm_eo;
+               while (bol > start && *bol != '\n')
+                       bol--;
+               if (*bol == '\n')
+                       bol++;
+               while (*eol && *eol != '\n')
+                       eol++;
+               if (*eol == '\n')
+                       eol++;
+               /* is it a funcname line? */
+               if (match_funcname(xecfg, (char*) bol, (char*) eol))
+                       return bol;
+               start = eol;
+       }
+}
+
+static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb,
+                                       void *cb_data, long lines, long *begin, long *end,
+                                       const char *path)
+{
+       char *pattern;
+       const char *term;
+       struct userdiff_driver *drv;
+       xdemitconf_t *xecfg = NULL;
+       const char *start;
+       const char *p;
+       int reg_error;
+       regex_t regexp;
+
+       assert(*arg == ':');
+       term = arg+1;
+       while (*term && *term != ':') {
+               if (*term == '\\' && *(term+1))
+                       term++;
+               term++;
+       }
+       if (term == arg+1)
+               return NULL;
+       if (!begin) /* skip_range_arg case */
+               return term;
+
+       pattern = xstrndup(arg+1, term-(arg+1));
+
+       start = nth_line_cb(cb_data, 0);
+
+       drv = userdiff_find_by_path(path);
+       if (drv && drv->funcname.pattern) {
+               const struct userdiff_funcname *pe = &drv->funcname;
+               xecfg = xcalloc(1, sizeof(*xecfg));
+               xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
+       }
+
+       reg_error = regcomp(&regexp, pattern, REG_NEWLINE);
+       if (reg_error) {
+               char errbuf[1024];
+               regerror(reg_error, &regexp, errbuf, 1024);
+               die("-L parameter '%s': %s", pattern, errbuf);
+       }
+
+       p = find_funcname_matching_regexp(xecfg, (char*) start, &regexp);
+       if (!p)
+               die("-L parameter '%s': no match", pattern);
+       *begin = 0;
+       while (p > nth_line_cb(cb_data, *begin))
+               (*begin)++;
+
+       if (*begin >= lines)
+               die("-L parameter '%s' matches at EOF", pattern);
+
+       *end = *begin+1;
+       while (*end < lines) {
+               const char *bol = nth_line_cb(cb_data, *end);
+               const char *eol = nth_line_cb(cb_data, *end+1);
+               if (match_funcname(xecfg, bol, eol))
+                       break;
+               (*end)++;
+       }
+
+       regfree(&regexp);
+       free(xecfg);
+       free(pattern);
+
+       /* compensate for 1-based numbering */
+       (*begin)++;
+
+       return term;
+}
+
+int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
+                   void *cb_data, long lines, long *begin, long *end,
+                   const char *path)
+{
+       *begin = *end = 0;
+
+       if (*arg == ':') {
+               arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path);
+               if (!arg || *arg)
+                       return -1;
+               return 0;
+       }
+
+       arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin);
+
+       if (*arg == ',')
+               arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end);
+
+       if (*arg)
+               return -1;
+
+       if (*begin && *end && *end < *begin) {
+               long tmp;
+               tmp = *end; *end = *begin; *begin = tmp;
+       }
+
+       return 0;
+}
+
+const char *skip_range_arg(const char *arg)
+{
+       if (*arg == ':')
+               return parse_range_funcname(arg, NULL, NULL, 0, NULL, NULL, NULL);
+
+       arg = parse_loc(arg, NULL, NULL, 0, -1, NULL);
+
+       if (*arg == ',')
+               arg = parse_loc(arg+1, NULL, NULL, 0, 0, NULL);
+
+       return arg;
+}
diff --git a/line-range.h b/line-range.h
new file mode 100644 (file)
index 0000000..ae3d012
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef LINE_RANGE_H
+#define LINE_RANGE_H
+
+/*
+ * Parse one item in an -L begin,end option w.r.t. the notional file
+ * object 'cb_data' consisting of 'lines' lines.
+ *
+ * The 'nth_line_cb' callback is used to determine the start of the
+ * line 'lno' inside the 'cb_data'.  The caller is expected to already
+ * have a suitable map at hand to make this a constant-time lookup.
+ *
+ * Returns 0 in case of success and -1 if there was an error.  The
+ * actual range is stored in *begin and *end.  The counting starts
+ * at 1!  In case of error, the caller should show usage message.
+ */
+
+typedef const char *(*nth_line_fn_t)(void *data, long lno);
+
+extern int parse_range_arg(const char *arg,
+                          nth_line_fn_t nth_line_cb,
+                          void *cb_data, long lines,
+                          long *begin, long *end,
+                          const char *path);
+
+/*
+ * Scan past a range argument that could be parsed by
+ * 'parse_range_arg', to help the caller determine the start of the
+ * filename in '-L n,m:file' syntax.
+ *
+ * Returns a pointer to the first character after the 'n,m' part, or
+ * NULL in case the argument is obviously malformed.
+ */
+
+extern const char *skip_range_arg(const char *arg);
+
+#endif /* LINE_RANGE_H */
index 3dd4a960190a1b0016b26dec9187692111b73e3f..05c8c5c61660fdf351246df4279b7642695f8c7e 100644 (file)
@@ -145,19 +145,35 @@ static void mark_edge_parents_uninteresting(struct commit *commit,
        }
 }
 
-void mark_edges_uninteresting(struct commit_list *list,
-                             struct rev_info *revs,
-                             show_edge_fn show_edge)
+void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
 {
-       for ( ; list; list = list->next) {
+       struct commit_list *list;
+       int i;
+
+       for (list = revs->commits; list; list = list->next) {
                struct commit *commit = list->item;
 
                if (commit->object.flags & UNINTERESTING) {
                        mark_tree_uninteresting(commit->tree);
+                       if (revs->edge_hint && !(commit->object.flags & SHOWN)) {
+                               commit->object.flags |= SHOWN;
+                               show_edge(commit);
+                       }
                        continue;
                }
                mark_edge_parents_uninteresting(commit, revs, show_edge);
        }
+       for (i = 0; i < revs->cmdline.nr; i++) {
+               struct object *obj = revs->cmdline.rev[i].item;
+               struct commit *commit = (struct commit *)obj;
+               if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
+                       continue;
+               mark_tree_uninteresting(commit->tree);
+               if (revs->edge_hint && !(obj->flags & SHOWN)) {
+                       obj->flags |= SHOWN;
+                       show_edge(commit);
+               }
+       }
 }
 
 static void add_pending_tree(struct rev_info *revs, struct tree *tree)
index 3db7bb6fa386df2ccb73b78ee72881f32074a1b8..136a1da5a6f048c0f4645112cdbf29c7b0982526 100644 (file)
@@ -6,6 +6,6 @@ typedef void (*show_object_fn)(struct object *, const struct name_path *, const
 void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
 
 typedef void (*show_edge_fn)(struct commit *);
-void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
+void mark_edges_uninteresting(struct rev_info *, show_edge_fn);
 
 #endif
index c6fb77b26fd88f4edf85a8920a6d72e49551deed..8fbcb6a98aae85b48c6f8de29c6f95af58823411 100644 (file)
@@ -124,15 +124,17 @@ static char *resolve_symlink(char *p, size_t s)
 
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-       if (strlen(path) >= sizeof(lk->filename))
-               return -1;
-       strcpy(lk->filename, path);
        /*
         * subtract 5 from size to make sure there's room for adding
         * ".lock" for the lock file name
         */
+       static const size_t max_path_len = sizeof(lk->filename) - 5;
+
+       if (strlen(path) >= max_path_len)
+               return -1;
+       strcpy(lk->filename, path);
        if (!(flags & LOCK_NODEREF))
-               resolve_symlink(lk->filename, sizeof(lk->filename)-5);
+               resolve_symlink(lk->filename, max_path_len);
        strcat(lk->filename, ".lock");
        lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
        if (0 <= lk->fd) {
index 7cc7d598e712fab531f7c6f938be17c317161f3e..8534d91826f3cf05f810a28728ee8bd51649d819 100644 (file)
@@ -10,6 +10,7 @@
 #include "color.h"
 #include "gpg-interface.h"
 #include "sequencer.h"
+#include "line-log.h"
 
 struct decoration name_decoration = { "object names" };
 
@@ -175,36 +176,52 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre
        }
 }
 
-void show_decorations(struct rev_info *opt, struct commit *commit)
+/*
+ * The caller makes sure there is no funny color before
+ * calling. format_decorations makes sure the same after return.
+ */
+void format_decorations(struct strbuf *sb,
+                       const struct commit *commit,
+                       int use_color)
 {
        const char *prefix;
        struct name_decoration *decoration;
        const char *color_commit =
-               diff_get_color_opt(&opt->diffopt, DIFF_COMMIT);
+               diff_get_color(use_color, DIFF_COMMIT);
        const char *color_reset =
-               decorate_get_color_opt(&opt->diffopt, DECORATION_NONE);
+               decorate_get_color(use_color, DECORATION_NONE);
 
-       if (opt->show_source && commit->util)
-               printf("\t%s", (char *) commit->util);
-       if (!opt->show_decorations)
-               return;
        decoration = lookup_decoration(&name_decoration, &commit->object);
        if (!decoration)
                return;
        prefix = " (";
        while (decoration) {
-               printf("%s", prefix);
-               fputs(decorate_get_color_opt(&opt->diffopt, decoration->type),
-                     stdout);
+               strbuf_addstr(sb, color_commit);
+               strbuf_addstr(sb, prefix);
+               strbuf_addstr(sb, decorate_get_color(use_color, decoration->type));
                if (decoration->type == DECORATION_REF_TAG)
-                       fputs("tag: ", stdout);
-               printf("%s", decoration->name);
-               fputs(color_reset, stdout);
-               fputs(color_commit, stdout);
+                       strbuf_addstr(sb, "tag: ");
+               strbuf_addstr(sb, decoration->name);
+               strbuf_addstr(sb, color_reset);
                prefix = ", ";
                decoration = decoration->next;
        }
-       putchar(')');
+       strbuf_addstr(sb, color_commit);
+       strbuf_addch(sb, ')');
+       strbuf_addstr(sb, color_reset);
+}
+
+void show_decorations(struct rev_info *opt, struct commit *commit)
+{
+       struct strbuf sb = STRBUF_INIT;
+
+       if (opt->show_source && commit->util)
+               printf("\t%s", (char *) commit->util);
+       if (!opt->show_decorations)
+               return;
+       format_decorations(&sb, commit, opt->diffopt.use_color);
+       fputs(sb.buf, stdout);
+       strbuf_release(&sb);
 }
 
 static unsigned int digits_in_number(unsigned int number)
@@ -540,8 +557,8 @@ void show_log(struct rev_info *opt)
                        printf(" (from %s)",
                               find_unique_abbrev(parent->object.sha1,
                                                  abbrev_commit));
+               fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);
                show_decorations(opt, commit);
-               printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
                if (opt->commit_format == CMIT_FMT_ONELINE) {
                        putchar(' ');
                } else {
@@ -600,6 +617,9 @@ void show_log(struct rev_info *opt)
        ctx.fmt = opt->commit_format;
        ctx.mailmap = opt->mailmap;
        ctx.color = opt->diffopt.use_color;
+       ctx.output_encoding = get_log_output_encoding();
+       if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
+               ctx.from_ident = &opt->from_ident;
        pretty_print_commit(&ctx, commit, &msgbuf);
 
        if (opt->add_signoff)
@@ -718,7 +738,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
        sha1 = commit->tree->object.sha1;
 
        /* Root commit? */
-       parents = commit->parents;
+       parents = get_saved_parents(opt, commit);
        if (!parents) {
                if (opt->show_root_diff) {
                        diff_root_tree_sha1(sha1, "", &opt->diffopt);
@@ -780,6 +800,9 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
        log.parent = NULL;
        opt->loginfo = &log;
 
+       if (opt->line_level_traverse)
+               return line_log_print(opt, commit);
+
        shown = log_tree_diff(opt, commit, &log);
        if (!shown && opt->loginfo && opt->always_show_header) {
                log.parent = NULL;
index 9140f48216be10085c9a1aea7ef1d210817c3332..d6ecd4dc46b82147c5c7e00dcfa2e9088ab00d7c 100644 (file)
@@ -13,6 +13,7 @@ int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
 int log_tree_opt_parse(struct rev_info *, const char **, int);
 void show_log(struct rev_info *opt);
+void format_decorations(struct strbuf *sb, const struct commit *commit, int use_color);
 void show_decorations(struct rev_info *opt, struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                             const char **subject_p,
index 2a7b36628cb5623eb0ffb95bfb54e52a6dd300f4..30614343213442806c5fbc28e904639d35c670cf 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -5,8 +5,10 @@
 #define DEBUG_MAILMAP 0
 #if DEBUG_MAILMAP
 #define debug_mm(...) fprintf(stderr, __VA_ARGS__)
+#define debug_str(X) ((X) ? (X) : "(none)")
 #else
 static inline void debug_mm(const char *format, ...) {}
+static inline const char *debug_str(const char *s) { return s; }
 #endif
 
 const char *git_mailmap_file;
@@ -29,7 +31,8 @@ struct mailmap_entry {
 static void free_mailmap_info(void *p, const char *s)
 {
        struct mailmap_info *mi = (struct mailmap_info *)p;
-       debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", s, mi->name, mi->email);
+       debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
+                s, debug_str(mi->name), debug_str(mi->email));
        free(mi->name);
        free(mi->email);
 }
@@ -37,8 +40,11 @@ static void free_mailmap_info(void *p, const char *s)
 static void free_mailmap_entry(void *p, const char *s)
 {
        struct mailmap_entry *me = (struct mailmap_entry *)p;
-       debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", s, me->namemap.nr);
-       debug_mm("mailmap: - simple: '%s' <%s>\n", me->name, me->email);
+       debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n",
+                s, me->namemap.nr);
+       debug_mm("mailmap: - simple: '%s' <%s>\n",
+                debug_str(me->name), debug_str(me->email));
+
        free(me->name);
        free(me->email);
 
@@ -47,18 +53,11 @@ static void free_mailmap_entry(void *p, const char *s)
 }
 
 static void add_mapping(struct string_list *map,
-                       char *new_name, char *new_email, char *old_name, char *old_email)
+                       char *new_name, char *new_email,
+                       char *old_name, char *old_email)
 {
        struct mailmap_entry *me;
        int index;
-       char *p;
-
-       if (old_email)
-               for (p = old_email; *p; p++)
-                       *p = tolower(*p);
-       if (new_email)
-               for (p = new_email; *p; p++)
-                       *p = tolower(*p);
 
        if (old_email == NULL) {
                old_email = new_email;
@@ -68,16 +67,21 @@ static void add_mapping(struct string_list *map,
        if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) {
                /* mailmap entry exists, invert index value */
                index = -1 - index;
+               me = (struct mailmap_entry *)map->items[index].util;
        } else {
                /* create mailmap entry */
-               struct string_list_item *item = string_list_insert_at_index(map, index, old_email);
-               item->util = xcalloc(1, sizeof(struct mailmap_entry));
-               ((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
+               struct string_list_item *item;
+
+               item = string_list_insert_at_index(map, index, old_email);
+               me = xcalloc(1, sizeof(struct mailmap_entry));
+               me->namemap.strdup_strings = 1;
+               me->namemap.cmp = strcasecmp;
+               item->util = me;
        }
-       me = (struct mailmap_entry *)map->items[index].util;
 
        if (old_name == NULL) {
-               debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index);
+               debug_mm("mailmap: adding (simple) entry for %s at index %d\n",
+                        old_email, index);
                /* Replace current name and new email for simple entry */
                if (new_name) {
                        free(me->name);
@@ -89,7 +93,8 @@ static void add_mapping(struct string_list *map,
                }
        } else {
                struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
-               debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
+               debug_mm("mailmap: adding (complex) entry for %s at index %d\n",
+                        old_email, index);
                if (new_name)
                        mi->name = xstrdup(new_name);
                if (new_email)
@@ -98,11 +103,12 @@ static void add_mapping(struct string_list *map,
        }
 
        debug_mm("mailmap:  '%s' <%s> -> '%s' <%s>\n",
-                old_name, old_email, new_name, new_email);
+                debug_str(old_name), old_email,
+                debug_str(new_name), debug_str(new_email));
 }
 
 static char *parse_name_and_email(char *buffer, char **name,
-               char **email, int allow_empty_email)
+                                 char **email, int allow_empty_email)
 {
        char *left, *right, *nstart, *nend;
        *name = *email = NULL;
@@ -122,7 +128,7 @@ static char *parse_name_and_email(char *buffer, char **name,
        while (nend > nstart && isspace(*nend))
                --nend;
 
-       *name = (nstart < nend ? nstart : NULL);
+       *name = (nstart <= nend ? nstart : NULL);
        *email = left+1;
        *(nend+1) = '\0';
        *right++ = '\0';
@@ -187,20 +193,17 @@ static int read_mailmap_file(struct string_list *map, const char *filename,
        return 0;
 }
 
-static void read_mailmap_buf(struct string_list *map,
-                            const char *buf, unsigned long len,
-                            char **repo_abbrev)
+static void read_mailmap_string(struct string_list *map, char *buf,
+                               char **repo_abbrev)
 {
-       while (len) {
-               const char *end = strchrnul(buf, '\n');
-               unsigned long linelen = end - buf + 1;
-               char *line = xmemdupz(buf, linelen);
+       while (*buf) {
+               char *end = strchrnul(buf, '\n');
 
-               read_mailmap_line(map, line, repo_abbrev);
+               if (*end)
+                       *end++ = '\0';
 
-               free(line);
-               buf += linelen;
-               len -= linelen;
+               read_mailmap_line(map, buf, repo_abbrev);
+               buf = end;
        }
 }
 
@@ -224,7 +227,7 @@ static int read_mailmap_blob(struct string_list *map,
        if (type != OBJ_BLOB)
                return error("mailmap is not a blob: %s", name);
 
-       read_mailmap_buf(map, buf, size, repo_abbrev);
+       read_mailmap_string(map, buf, repo_abbrev);
 
        free(buf);
        return 0;
@@ -306,21 +309,25 @@ static struct string_list_item *lookup_prefix(struct string_list *map,
 }
 
 int map_user(struct string_list *map,
-                        const char **email, size_t *emaillen,
-                        const char **name, size_t *namelen)
+            const char **email, size_t *emaillen,
+            const char **name, size_t *namelen)
 {
        struct string_list_item *item;
        struct mailmap_entry *me;
 
        debug_mm("map_user: map '%.*s' <%.*s>\n",
-                *name, *namelen, *emaillen, *email);
+                (int)*namelen, debug_str(*name),
+                (int)*emaillen, debug_str(*email));
 
        item = lookup_prefix(map, *email, *emaillen);
        if (item != NULL) {
                me = (struct mailmap_entry *)item->util;
                if (me->namemap.nr) {
-                       /* The item has multiple items, so we'll look up on name too */
-                       /* If the name is not found, we choose the simple entry      */
+                       /*
+                        * The item has multiple items, so we'll look up on
+                        * name too. If the name is not found, we choose the
+                        * simple entry.
+                        */
                        struct string_list_item *subitem;
                        subitem = lookup_prefix(&me->namemap, *name, *namelen);
                        if (subitem)
@@ -341,8 +348,9 @@ int map_user(struct string_list *map,
                                *name = mi->name;
                                *namelen = strlen(*name);
                }
-               debug_mm("map_user:  to '%.*s' <.*%s>\n", *namelen, *name,
-                                *emaillen, *email);
+               debug_mm("map_user:  to '%.*s' <%.*s>\n",
+                        (int)*namelen, debug_str(*name),
+                        (int)*emaillen, debug_str(*email));
                return 1;
        }
        debug_mm("map_user:  --\n");
index 2bb734d51cbe59e3eed7c82e7522c594d5efa579..7873cdec581eea66feab06c22b83ef7c27c7c5b6 100644 (file)
@@ -47,6 +47,22 @@ static int score_matches(unsigned mode1, unsigned mode2, const char *path)
        return score;
 }
 
+static void *fill_tree_desc_strict(struct tree_desc *desc,
+                                  const unsigned char *hash)
+{
+       void *buffer;
+       enum object_type type;
+       unsigned long size;
+
+       buffer = read_sha1_file(hash, &type, &size);
+       if (!buffer)
+               die("unable to read tree (%s)", sha1_to_hex(hash));
+       if (type != OBJ_TREE)
+               die("%s is not a tree", sha1_to_hex(hash));
+       init_tree_desc(desc, buffer, size);
+       return buffer;
+}
+
 static int base_name_entries_compare(const struct name_entry *a,
                                     const struct name_entry *b)
 {
@@ -61,23 +77,10 @@ static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
 {
        struct tree_desc one;
        struct tree_desc two;
-       void *one_buf, *two_buf;
+       void *one_buf = fill_tree_desc_strict(&one, hash1);
+       void *two_buf = fill_tree_desc_strict(&two, hash2);
        int score = 0;
-       enum object_type type;
-       unsigned long size;
 
-       one_buf = read_sha1_file(hash1, &type, &size);
-       if (!one_buf)
-               die("unable to read tree (%s)", sha1_to_hex(hash1));
-       if (type != OBJ_TREE)
-               die("%s is not a tree", sha1_to_hex(hash1));
-       init_tree_desc(&one, one_buf, size);
-       two_buf = read_sha1_file(hash2, &type, &size);
-       if (!two_buf)
-               die("unable to read tree (%s)", sha1_to_hex(hash2));
-       if (type != OBJ_TREE)
-               die("%s is not a tree", sha1_to_hex(hash2));
-       init_tree_desc(&two, two_buf, size);
        for (;;) {
                struct name_entry e1, e2;
                int got_entry_from_one = tree_entry(&one, &e1);
@@ -124,16 +127,7 @@ static void match_trees(const unsigned char *hash1,
                        int recurse_limit)
 {
        struct tree_desc one;
-       void *one_buf;
-       enum object_type type;
-       unsigned long size;
-
-       one_buf = read_sha1_file(hash1, &type, &size);
-       if (!one_buf)
-               die("unable to read tree (%s)", sha1_to_hex(hash1));
-       if (type != OBJ_TREE)
-               die("%s is not a tree", sha1_to_hex(hash1));
-       init_tree_desc(&one, one_buf, size);
+       void *one_buf = fill_tree_desc_strict(&one, hash1);
 
        while (one.size) {
                const char *path;
index ea9dbd307c60a16f290b44964c386e566b6bc7e3..f95933b0aacce84a8b911634e5c40d5e937a322b 100644 (file)
@@ -251,7 +251,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
                int i;
                fprintf(stderr, "BUG: There are unmerged index entries:\n");
                for (i = 0; i < active_nr; i++) {
-                       struct cache_entry *ce = active_cache[i];
+                       const struct cache_entry *ce = active_cache[i];
                        if (ce_stage(ce))
                                fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
                                        (int)ce_namelen(ce), ce->name);
@@ -264,7 +264,8 @@ struct tree *write_tree_from_memory(struct merge_options *o)
 
        if (!cache_tree_fully_valid(active_cache_tree) &&
            cache_tree_update(active_cache_tree,
-                             active_cache, active_nr, 0) < 0)
+                             (const struct cache_entry * const *)active_cache,
+                             active_nr, 0) < 0)
                die(_("error building trees"));
 
        result = lookup_tree(active_cache_tree->sha1);
@@ -339,7 +340,7 @@ static struct string_list *get_unmerged(void)
        for (i = 0; i < active_nr; i++) {
                struct string_list_item *item;
                struct stage_data *e;
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
 
index 28fead428b333448566f480a434967fb7325c744..a30034f116fda6f90448aeb4ddd42063ea60aad1 100644 (file)
@@ -1,5 +1,5 @@
 diff_cmd () {
-       "$merge_tool_path" --auto \
+       "$merge_tool_path" \
                --L1 "$MERGED (A)" --L2 "$MERGED (B)" \
                "$LOCAL" "$REMOTE" >/dev/null 2>&1
 }
index 0f67bd3f9605aa7a3c98b88b4ae4aae037ace3d7..ab1885707403e06fe058ece2e0455e25e194224c 100644 (file)
@@ -9,6 +9,7 @@
 #include "notes.h"
 #include "notes-merge.h"
 #include "strbuf.h"
+#include "notes-utils.h"
 
 struct notes_merge_pair {
        unsigned char obj[20], base[20], local[20], remote[20];
@@ -530,32 +531,6 @@ static int merge_from_diffs(struct notes_merge_options *o,
        return conflicts ? -1 : 1;
 }
 
-void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
-                        const struct strbuf *msg, unsigned char *result_sha1)
-{
-       unsigned char tree_sha1[20];
-
-       assert(t->initialized);
-
-       if (write_notes_tree(t, tree_sha1))
-               die("Failed to write notes tree to database");
-
-       if (!parents) {
-               /* Deduce parent commit from t->ref */
-               unsigned char parent_sha1[20];
-               if (!read_ref(t->ref, parent_sha1)) {
-                       struct commit *parent = lookup_commit(parent_sha1);
-                       if (!parent || parse_commit(parent))
-                               die("Failed to find/parse commit %s", t->ref);
-                       commit_list_insert(parent, &parents);
-               }
-               /* else: t->ref points to nothing, assume root/orphan commit */
-       }
-
-       if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
-               die("Failed to commit notes tree to database");
-}
-
 int notes_merge(struct notes_merge_options *o,
                struct notes_tree *local_tree,
                unsigned char *result_sha1)
index 0c11b173a1e38ddeb702e022ebdd571ed2ad12f3..1d01f6aacf54b27a498d6071b5ec80fee5c35a47 100644 (file)
@@ -25,20 +25,6 @@ struct notes_merge_options {
 
 void init_notes_merge_options(struct notes_merge_options *o);
 
-/*
- * Create new notes commit from the given notes tree
- *
- * Properties of the created commit:
- * - tree: the result of converting t to a tree object with write_notes_tree().
- * - parents: the given parents OR (if NULL) the commit referenced by t->ref.
- * - author/committer: the default determined by commmit_tree().
- * - commit message: msg
- *
- * 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);
-
 /*
  * Merge notes from o->remote_ref into o->local_ref
  *
diff --git a/notes-utils.c b/notes-utils.c
new file mode 100644 (file)
index 0000000..9107c37
--- /dev/null
@@ -0,0 +1,157 @@
+#include "cache.h"
+#include "commit.h"
+#include "refs.h"
+#include "notes-utils.h"
+
+void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
+                        const struct strbuf *msg, unsigned char *result_sha1)
+{
+       unsigned char tree_sha1[20];
+
+       assert(t->initialized);
+
+       if (write_notes_tree(t, tree_sha1))
+               die("Failed to write notes tree to database");
+
+       if (!parents) {
+               /* Deduce parent commit from t->ref */
+               unsigned char parent_sha1[20];
+               if (!read_ref(t->ref, parent_sha1)) {
+                       struct commit *parent = lookup_commit(parent_sha1);
+                       if (!parent || parse_commit(parent))
+                               die("Failed to find/parse commit %s", t->ref);
+                       commit_list_insert(parent, &parents);
+               }
+               /* else: t->ref points to nothing, assume root/orphan commit */
+       }
+
+       if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL, NULL))
+               die("Failed to commit notes tree to database");
+}
+
+void commit_notes(struct notes_tree *t, const char *msg)
+{
+       struct strbuf buf = STRBUF_INIT;
+       unsigned char commit_sha1[20];
+
+       if (!t)
+               t = &default_notes_tree;
+       if (!t->initialized || !t->ref || !*t->ref)
+               die(_("Cannot commit uninitialized/unreferenced notes tree"));
+       if (!t->dirty)
+               return; /* don't have to commit an unchanged tree */
+
+       /* Prepare commit message and reflog message */
+       strbuf_addstr(&buf, 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);
+       strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
+       update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
+
+       strbuf_release(&buf);
+}
+
+static combine_notes_fn parse_combine_notes_fn(const char *v)
+{
+       if (!strcasecmp(v, "overwrite"))
+               return combine_notes_overwrite;
+       else if (!strcasecmp(v, "ignore"))
+               return combine_notes_ignore;
+       else if (!strcasecmp(v, "concatenate"))
+               return combine_notes_concatenate;
+       else if (!strcasecmp(v, "cat_sort_uniq"))
+               return combine_notes_cat_sort_uniq;
+       else
+               return NULL;
+}
+
+static int notes_rewrite_config(const char *k, const char *v, void *cb)
+{
+       struct notes_rewrite_cfg *c = cb;
+       if (!prefixcmp(k, "notes.rewrite.") && !strcmp(k+14, c->cmd)) {
+               c->enabled = git_config_bool(k, v);
+               return 0;
+       } else if (!c->mode_from_env && !strcmp(k, "notes.rewritemode")) {
+               if (!v)
+                       config_error_nonbool(k);
+               c->combine = parse_combine_notes_fn(v);
+               if (!c->combine) {
+                       error(_("Bad notes.rewriteMode value: '%s'"), v);
+                       return 1;
+               }
+               return 0;
+       } else if (!c->refs_from_env && !strcmp(k, "notes.rewriteref")) {
+               /* note that a refs/ prefix is implied in the
+                * underlying for_each_glob_ref */
+               if (!prefixcmp(v, "refs/notes/"))
+                       string_list_add_refs_by_glob(c->refs, v);
+               else
+                       warning(_("Refusing to rewrite notes in %s"
+                               " (outside of refs/notes/)"), v);
+               return 0;
+       }
+
+       return 0;
+}
+
+
+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
+{
+       struct notes_rewrite_cfg *c = xmalloc(sizeof(struct notes_rewrite_cfg));
+       const char *rewrite_mode_env = getenv(GIT_NOTES_REWRITE_MODE_ENVIRONMENT);
+       const char *rewrite_refs_env = getenv(GIT_NOTES_REWRITE_REF_ENVIRONMENT);
+       c->cmd = cmd;
+       c->enabled = 1;
+       c->combine = combine_notes_concatenate;
+       c->refs = xcalloc(1, sizeof(struct string_list));
+       c->refs->strdup_strings = 1;
+       c->refs_from_env = 0;
+       c->mode_from_env = 0;
+       if (rewrite_mode_env) {
+               c->mode_from_env = 1;
+               c->combine = parse_combine_notes_fn(rewrite_mode_env);
+               if (!c->combine)
+                       /* TRANSLATORS: The first %s is the name of the
+                          environment variable, the second %s is its value */
+                       error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT,
+                                       rewrite_mode_env);
+       }
+       if (rewrite_refs_env) {
+               c->refs_from_env = 1;
+               string_list_add_refs_from_colon_sep(c->refs, rewrite_refs_env);
+       }
+       git_config(notes_rewrite_config, c);
+       if (!c->enabled || !c->refs->nr) {
+               string_list_clear(c->refs, 0);
+               free(c->refs);
+               free(c);
+               return NULL;
+       }
+       c->trees = load_notes_trees(c->refs);
+       string_list_clear(c->refs, 0);
+       free(c->refs);
+       return c;
+}
+
+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
+                         const unsigned char *from_obj, const unsigned char *to_obj)
+{
+       int ret = 0;
+       int i;
+       for (i = 0; c->trees[i]; i++)
+               ret = copy_note(c->trees[i], from_obj, to_obj, 1, c->combine) || ret;
+       return ret;
+}
+
+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c, const char *msg)
+{
+       int i;
+       for (i = 0; c->trees[i]; i++) {
+               commit_notes(c->trees[i], msg);
+               free_notes(c->trees[i]);
+       }
+       free(c->trees);
+       free(c);
+}
diff --git a/notes-utils.h b/notes-utils.h
new file mode 100644 (file)
index 0000000..b4cb1bf
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef NOTES_UTILS_H
+#define NOTES_UTILS_H
+
+#include "notes.h"
+
+/*
+ * Create new notes commit from the given notes tree
+ *
+ * Properties of the created commit:
+ * - tree: the result of converting t to a tree object with write_notes_tree().
+ * - parents: the given parents OR (if NULL) the commit referenced by t->ref.
+ * - author/committer: the default determined by commmit_tree().
+ * - commit message: msg
+ *
+ * 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);
+
+void commit_notes(struct notes_tree *t, const char *msg);
+
+struct notes_rewrite_cfg {
+       struct notes_tree **trees;
+       const char *cmd;
+       int enabled;
+       combine_notes_fn combine;
+       struct string_list *refs;
+       int refs_from_env;
+       int mode_from_env;
+};
+
+struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd);
+int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
+                         const unsigned char *from_obj, const unsigned char *to_obj);
+void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c, const char *msg);
+
+#endif
diff --git a/notes.c b/notes.c
index f63fd572d6db125559e68556a8fc152a53700644..b69c0b82577e0d4958ddf3efbb4aec5a9c5fa99e 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -918,17 +918,21 @@ out:
        return ret;
 }
 
-static int string_list_add_one_ref(const char *path, const unsigned char *sha1,
+static int string_list_add_one_ref(const char *refname, const unsigned char *sha1,
                                   int flag, void *cb)
 {
        struct string_list *refs = cb;
-       if (!unsorted_string_list_has_string(refs, path))
-               string_list_append(refs, path);
+       if (!unsorted_string_list_has_string(refs, refname))
+               string_list_append(refs, refname);
        return 0;
 }
 
+/*
+ * The list argument must have strdup_strings set on it.
+ */
 void string_list_add_refs_by_glob(struct string_list *list, const char *glob)
 {
+       assert(list->strdup_strings);
        if (has_glob_specials(glob)) {
                for_each_glob_ref(string_list_add_one_ref, glob, list);
        } else {
diff --git a/notes.h b/notes.h
index 3324c486a1a106053621ff215f5689cf1d92af74..2a3f92338076ef8e4b8238aaf570c3273b0f28f0 100644 (file)
--- a/notes.h
+++ b/notes.h
@@ -77,7 +77,7 @@ const char *default_notes_ref(void);
  * variable is used, and if that is missing, the default notes ref is used
  * ("refs/notes/commits").
  *
- * If you need to re-intialize a notes_tree structure (e.g. when switching from
+ * If you need to re-initialize a notes_tree structure (e.g. when switching from
  * one notes ref to another), you must first de-initialize the notes_tree
  * structure by calling free_notes(struct notes_tree *).
  *
index 20703f52ed24aa227d451bec332ac73cc3d49d3a..d8a4b1ffbe68d6e9f720e979168f9d010c9c5a06 100644 (file)
--- a/object.c
+++ b/object.c
@@ -71,13 +71,13 @@ static unsigned int hashtable_index(const unsigned char *sha1)
 
 struct object *lookup_object(const unsigned char *sha1)
 {
-       unsigned int i;
+       unsigned int i, first;
        struct object *obj;
 
        if (!obj_hash)
                return NULL;
 
-       i = hashtable_index(sha1);
+       first = i = hashtable_index(sha1);
        while ((obj = obj_hash[i]) != NULL) {
                if (!hashcmp(sha1, obj->sha1))
                        break;
@@ -85,6 +85,16 @@ struct object *lookup_object(const unsigned char *sha1)
                if (i == obj_hash_size)
                        i = 0;
        }
+       if (obj && i != first) {
+               /*
+                * Move object to where we started to look for it so
+                * that we do not need to walk the hash table the next
+                * time we look for it.
+                */
+               struct object *tmp = obj_hash[i];
+               obj_hash[i] = obj_hash[first];
+               obj_hash[first] = tmp;
+       }
        return obj;
 }
 
@@ -135,7 +145,7 @@ struct object *lookup_unknown_object(const unsigned char *sha1)
 struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
 {
        struct object *obj;
-       int eaten = 0;
+       *eaten_p = 0;
 
        obj = NULL;
        if (type == OBJ_BLOB) {
@@ -154,7 +164,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
                        if (!tree->object.parsed) {
                                if (parse_tree_buffer(tree, buffer, size))
                                        return NULL;
-                               eaten = 1;
+                               *eaten_p = 1;
                        }
                }
        } else if (type == OBJ_COMMIT) {
@@ -164,7 +174,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
                                return NULL;
                        if (!commit->buffer) {
                                commit->buffer = buffer;
-                               eaten = 1;
+                               *eaten_p = 1;
                        }
                        obj = &commit->object;
                }
@@ -181,7 +191,6 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
        }
        if (obj && obj->type == OBJ_NONE)
                obj->type = type;
-       *eaten_p = eaten;
        return obj;
 }
 
@@ -260,11 +269,18 @@ void add_object_array(struct object *obj, const char *name, struct object_array
        add_object_array_with_mode(obj, name, array, S_IFINVALID);
 }
 
+/*
+ * A zero-length string to which object_array_entry::name can be
+ * initialized without requiring a malloc/free.
+ */
+static char object_array_slopbuf[1];
+
 void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode)
 {
        unsigned nr = array->nr;
        unsigned alloc = array->alloc;
        struct object_array_entry *objects = array->objects;
+       struct object_array_entry *entry;
 
        if (nr >= alloc) {
                alloc = (alloc + 32) * 2;
@@ -272,28 +288,67 @@ void add_object_array_with_mode(struct object *obj, const char *name, struct obj
                array->alloc = alloc;
                array->objects = objects;
        }
-       objects[nr].item = obj;
-       objects[nr].name = name;
-       objects[nr].mode = mode;
+       entry = &objects[nr];
+       entry->item = obj;
+       if (!name)
+               entry->name = NULL;
+       else if (!*name)
+               /* Use our own empty string instead of allocating one: */
+               entry->name = object_array_slopbuf;
+       else
+               entry->name = xstrdup(name);
+       entry->mode = mode;
        array->nr = ++nr;
 }
 
-void object_array_remove_duplicates(struct object_array *array)
+void object_array_filter(struct object_array *array,
+                        object_array_each_func_t want, void *cb_data)
 {
-       unsigned int ref, src, dst;
+       unsigned nr = array->nr, src, dst;
        struct object_array_entry *objects = array->objects;
 
-       for (ref = 0; ref + 1 < array->nr; ref++) {
-               for (src = ref + 1, dst = src;
-                    src < array->nr;
-                    src++) {
-                       if (!strcmp(objects[ref].name, objects[src].name))
-                               continue;
+       for (src = dst = 0; src < nr; src++) {
+               if (want(&objects[src], cb_data)) {
                        if (src != dst)
                                objects[dst] = objects[src];
                        dst++;
+               } else {
+                       if (objects[src].name != object_array_slopbuf)
+                               free(objects[src].name);
+               }
+       }
+       array->nr = dst;
+}
+
+/*
+ * Return true iff array already contains an entry with name.
+ */
+static int contains_name(struct object_array *array, const char *name)
+{
+       unsigned nr = array->nr, i;
+       struct object_array_entry *object = array->objects;
+
+       for (i = 0; i < nr; i++, object++)
+               if (!strcmp(object->name, name))
+                       return 1;
+       return 0;
+}
+
+void object_array_remove_duplicates(struct object_array *array)
+{
+       unsigned nr = array->nr, src;
+       struct object_array_entry *objects = array->objects;
+
+       array->nr = 0;
+       for (src = 0; src < nr; src++) {
+               if (!contains_name(array, objects[src].name)) {
+                       if (src != array->nr)
+                               objects[array->nr] = objects[src];
+                       array->nr++;
+               } else {
+                       if (objects[src].name != object_array_slopbuf)
+                               free(objects[src].name);
                }
-               array->nr = dst;
        }
 }
 
index 97d384b80a5dbf0cac88b59cbb8a2d333d2b9582..2ff68c52dd48842a188eb8f0c9b1e12ff27fae37 100644 (file)
--- a/object.h
+++ b/object.h
@@ -11,7 +11,13 @@ struct object_array {
        unsigned int alloc;
        struct object_array_entry {
                struct object *item;
-               const char *name;
+               /*
+                * name or NULL.  If non-NULL, the memory pointed to
+                * is owned by this object *except* if it points at
+                * object_array_slopbuf, which is a static copy of the
+                * empty string.
+                */
+               char *name;
                unsigned mode;
        } *objects;
 };
@@ -85,7 +91,22 @@ int object_list_contains(struct object_list *list, struct object *obj);
 /* Object array handling .. */
 void add_object_array(struct object *obj, const char *name, struct object_array *array);
 void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode);
-void object_array_remove_duplicates(struct object_array *);
+
+typedef int (*object_array_each_func_t)(struct object_array_entry *, void *);
+
+/*
+ * Apply want to each entry in array, retaining only the entries for
+ * which the function returns true.  Preserve the order of the entries
+ * that are retained.
+ */
+void object_array_filter(struct object_array *array,
+                        object_array_each_func_t want, void *cb_data);
+
+/*
+ * Remove from array all but the first entry with a given name.
+ * Warning: this function uses an O(N^2) algorithm.
+ */
+void object_array_remove_duplicates(struct object_array *array);
 
 void clear_object_flags(unsigned flags);
 
diff --git a/pack-refs.c b/pack-refs.c
deleted file mode 100644 (file)
index 4461f71..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-#include "cache.h"
-#include "refs.h"
-#include "tag.h"
-#include "pack-refs.h"
-
-struct ref_to_prune {
-       struct ref_to_prune *next;
-       unsigned char sha1[20];
-       char name[FLEX_ARRAY];
-};
-
-struct pack_refs_cb_data {
-       unsigned int flags;
-       struct ref_to_prune *ref_to_prune;
-       FILE *refs_file;
-};
-
-static int do_not_prune(int flags)
-{
-       /* If it is already packed or if it is a symref,
-        * do not prune it.
-        */
-       return (flags & (REF_ISSYMREF|REF_ISPACKED));
-}
-
-static int handle_one_ref(const char *path, const unsigned char *sha1,
-                         int flags, void *cb_data)
-{
-       struct pack_refs_cb_data *cb = cb_data;
-       struct object *o;
-       int is_tag_ref;
-
-       /* Do not pack the symbolic refs */
-       if ((flags & REF_ISSYMREF))
-               return 0;
-       is_tag_ref = !prefixcmp(path, "refs/tags/");
-
-       /* ALWAYS pack refs that were already packed or are tags */
-       if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref && !(flags & REF_ISPACKED))
-               return 0;
-
-       fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
-
-       o = parse_object_or_die(sha1, path);
-       if (o->type == OBJ_TAG) {
-               o = deref_tag(o, path, 0);
-               if (o)
-                       fprintf(cb->refs_file, "^%s\n",
-                               sha1_to_hex(o->sha1));
-       }
-
-       if ((cb->flags & PACK_REFS_PRUNE) && !do_not_prune(flags)) {
-               int namelen = strlen(path) + 1;
-               struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
-               hashcpy(n->sha1, sha1);
-               strcpy(n->name, path);
-               n->next = cb->ref_to_prune;
-               cb->ref_to_prune = n;
-       }
-       return 0;
-}
-
-/*
- * Remove empty parents, but spare refs/ and immediate subdirs.
- * Note: munges *name.
- */
-static void try_remove_empty_parents(char *name)
-{
-       char *p, *q;
-       int i;
-       p = name;
-       for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
-               while (*p && *p != '/')
-                       p++;
-               /* tolerate duplicate slashes; see check_refname_format() */
-               while (*p == '/')
-                       p++;
-       }
-       for (q = p; *q; q++)
-               ;
-       while (1) {
-               while (q > p && *q != '/')
-                       q--;
-               while (q > p && *(q-1) == '/')
-                       q--;
-               if (q == p)
-                       break;
-               *q = '\0';
-               if (rmdir(git_path("%s", name)))
-                       break;
-       }
-}
-
-/* make sure nobody touched the ref, and unlink */
-static void prune_ref(struct ref_to_prune *r)
-{
-       struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
-
-       if (lock) {
-               unlink_or_warn(git_path("%s", r->name));
-               unlock_ref(lock);
-               try_remove_empty_parents(r->name);
-       }
-}
-
-static void prune_refs(struct ref_to_prune *r)
-{
-       while (r) {
-               prune_ref(r);
-               r = r->next;
-       }
-}
-
-static struct lock_file packed;
-
-int pack_refs(unsigned int flags)
-{
-       int fd;
-       struct pack_refs_cb_data cbdata;
-
-       memset(&cbdata, 0, sizeof(cbdata));
-       cbdata.flags = flags;
-
-       fd = hold_lock_file_for_update(&packed, git_path("packed-refs"),
-                                      LOCK_DIE_ON_ERROR);
-       cbdata.refs_file = fdopen(fd, "w");
-       if (!cbdata.refs_file)
-               die_errno("unable to create ref-pack file structure");
-
-       /* perhaps other traits later as well */
-       fprintf(cbdata.refs_file, "# pack-refs with: peeled fully-peeled \n");
-
-       for_each_ref(handle_one_ref, &cbdata);
-       if (ferror(cbdata.refs_file))
-               die("failed to write ref-pack file");
-       if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file))
-               die_errno("failed to write ref-pack file");
-       /*
-        * Since the lock file was fdopen()'ed and then fclose()'ed above,
-        * assign -1 to the lock file descriptor so that commit_lock_file()
-        * won't try to close() it.
-        */
-       packed.fd = -1;
-       if (commit_lock_file(&packed) < 0)
-               die_errno("unable to overwrite old ref-pack file");
-       prune_refs(cbdata.ref_to_prune);
-       return 0;
-}
diff --git a/pack-refs.h b/pack-refs.h
deleted file mode 100644 (file)
index 518acfb..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef PACK_REFS_H
-#define PACK_REFS_H
-
-/*
- * Flags for controlling behaviour of pack_refs()
- * PACK_REFS_PRUNE: Prune loose refs after packing
- * PACK_REFS_ALL:   Pack _all_ refs, not just tags and already packed refs
- */
-#define PACK_REFS_PRUNE 0x0001
-#define PACK_REFS_ALL   0x0002
-
-/*
- * Write a packed-refs file for the current repository.
- * flags: Combination of the above PACK_REFS_* flags.
- */
-int pack_refs(unsigned int flags);
-
-#endif /* PACK_REFS_H */
index 77a0465be6f6a79814aa3c009612736770b342a1..b4d2b35bb37120f97ec000524d12a85d91949119 100644 (file)
@@ -59,11 +59,101 @@ static void init_pack_revindex(void)
        /* revindex elements are lazily initialized */
 }
 
-static int cmp_offset(const void *a_, const void *b_)
+/*
+ * This is a least-significant-digit radix sort.
+ *
+ * It sorts each of the "n" items in "entries" by its offset field. The "max"
+ * parameter must be at least as large as the largest offset in the array,
+ * and lets us quit the sort early.
+ */
+static void sort_revindex(struct revindex_entry *entries, unsigned n, off_t max)
 {
-       const struct revindex_entry *a = a_;
-       const struct revindex_entry *b = b_;
-       return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
+       /*
+        * We use a "digit" size of 16 bits. That keeps our memory
+        * usage reasonable, and we can generally (for a 4G or smaller
+        * packfile) quit after two rounds of radix-sorting.
+        */
+#define DIGIT_SIZE (16)
+#define BUCKETS (1 << DIGIT_SIZE)
+       /*
+        * We want to know the bucket that a[i] will go into when we are using
+        * the digit that is N bits from the (least significant) end.
+        */
+#define BUCKET_FOR(a, i, bits) (((a)[(i)].offset >> (bits)) & (BUCKETS-1))
+
+       /*
+        * We need O(n) temporary storage. Rather than do an extra copy of the
+        * partial results into "entries", we sort back and forth between the
+        * real array and temporary storage. In each iteration of the loop, we
+        * keep track of them with alias pointers, always sorting from "from"
+        * to "to".
+        */
+       struct revindex_entry *tmp = xmalloc(n * sizeof(*tmp));
+       struct revindex_entry *from = entries, *to = tmp;
+       int bits;
+       unsigned *pos = xmalloc(BUCKETS * sizeof(*pos));
+
+       /*
+        * If (max >> bits) is zero, then we know that the radix digit we are
+        * on (and any higher) will be zero for all entries, and our loop will
+        * be a no-op, as everybody lands in the same zero-th bucket.
+        */
+       for (bits = 0; max >> bits; bits += DIGIT_SIZE) {
+               struct revindex_entry *swap;
+               unsigned i;
+
+               memset(pos, 0, BUCKETS * sizeof(*pos));
+
+               /*
+                * We want pos[i] to store the index of the last element that
+                * will go in bucket "i" (actually one past the last element).
+                * To do this, we first count the items that will go in each
+                * bucket, which gives us a relative offset from the last
+                * bucket. We can then cumulatively add the index from the
+                * previous bucket to get the true index.
+                */
+               for (i = 0; i < n; i++)
+                       pos[BUCKET_FOR(from, i, bits)]++;
+               for (i = 1; i < BUCKETS; i++)
+                       pos[i] += pos[i-1];
+
+               /*
+                * Now we can drop the elements into their correct buckets (in
+                * our temporary array).  We iterate the pos counter backwards
+                * to avoid using an extra index to count up. And since we are
+                * going backwards there, we must also go backwards through the
+                * array itself, to keep the sort stable.
+                *
+                * Note that we use an unsigned iterator to make sure we can
+                * handle 2^32-1 objects, even on a 32-bit system. But this
+                * means we cannot use the more obvious "i >= 0" loop condition
+                * for counting backwards, and must instead check for
+                * wrap-around with UINT_MAX.
+                */
+               for (i = n - 1; i != UINT_MAX; i--)
+                       to[--pos[BUCKET_FOR(from, i, bits)]] = from[i];
+
+               /*
+                * Now "to" contains the most sorted list, so we swap "from" and
+                * "to" for the next iteration.
+                */
+               swap = from;
+               from = to;
+               to = swap;
+       }
+
+       /*
+        * If we ended with our data in the original array, great. If not,
+        * we have to move it back from the temporary storage.
+        */
+       if (from != entries)
+               memcpy(entries, tmp, n * sizeof(*entries));
+       free(tmp);
+       free(pos);
+
+#undef BUCKET_FOR
+#undef BUCKETS
+#undef DIGIT_SIZE
 }
 
 /*
@@ -72,8 +162,8 @@ static int cmp_offset(const void *a_, const void *b_)
 static void create_pack_revindex(struct pack_revindex *rix)
 {
        struct packed_git *p = rix->p;
-       int num_ent = p->num_objects;
-       int i;
+       unsigned num_ent = p->num_objects;
+       unsigned i;
        const char *index = p->index_data;
 
        rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
@@ -108,13 +198,13 @@ static void create_pack_revindex(struct pack_revindex *rix)
         */
        rix->revindex[num_ent].offset = p->pack_size - 20;
        rix->revindex[num_ent].nr = -1;
-       qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
+       sort_revindex(rix->revindex, num_ent, p->pack_size);
 }
 
 struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
 {
        int num;
-       int lo, hi;
+       unsigned lo, hi;
        struct pack_revindex *rix;
        struct revindex_entry *revindex;
 
@@ -132,7 +222,7 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
        lo = 0;
        hi = p->num_objects + 1;
        do {
-               int mi = (lo + hi) / 2;
+               unsigned mi = lo + (hi - lo) / 2;
                if (revindex[mi].offset == ofs) {
                        return revindex + mi;
                } else if (ofs < revindex[mi].offset)
index 0de5fb168a5c1b86c50be0d0c3f2d7228e7159a1..be8c413cfebb414bcbb7815cf4fe42bc31444d65 100644 (file)
@@ -33,6 +33,12 @@ int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
        return 0;
 }
 
+int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
+                            int unset)
+{
+       return parse_expiry_date(arg, (unsigned long *)opt->value);
+}
+
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
                            int unset)
 {
index 1c8bd8d5a0894d9deda1e849165ff38e8d594a4d..9b94596e4aa113518d502502a0ca1bf804a60cbc 100644 (file)
@@ -140,6 +140,9 @@ struct option {
 #define OPT_DATE(s, l, v, h) \
        { OPTION_CALLBACK, (s), (l), (v), N_("time"),(h), 0,    \
          parse_opt_approxidate_cb }
+#define OPT_EXPIRY_DATE(s, l, v, h) \
+       { OPTION_CALLBACK, (s), (l), (v), N_("expiry date"),(h), 0,     \
+         parse_opt_expiry_date_cb }
 #define OPT_CALLBACK(s, l, v, a, h, f) \
        { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
 #define OPT_NUMBER_CALLBACK(v, h, f) \
@@ -177,7 +180,7 @@ extern NORETURN void usage_msg_opt(const char *msg,
 
 extern int optbug(const struct option *opt, const char *reason);
 extern int opterror(const struct option *opt, const char *reason, int flags);
-#if defined(__GNUC__) && ! defined(clang)
+#if defined(__GNUC__) && ! defined(__clang__)
 #define opterror(o,r,f) (opterror((o),(r),(f)), -1)
 #endif
 
@@ -219,6 +222,7 @@ extern int parse_options_concat(struct option *dst, size_t, struct option *src);
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
 extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_expiry_date_cb(const struct option *, const char *, int);
 extern int parse_opt_color_flag_cb(const struct option *, const char *, int);
 extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
 extern int parse_opt_with_commit(const struct option *, const char *, int);
diff --git a/path.c b/path.c
index 04ff1487ed31685b6b3f7923f9bfbfc7217f70a0..3d244d3e03ae5c3d5afe452f903ba204849ad03b 100644 (file)
--- a/path.c
+++ b/path.c
@@ -5,13 +5,7 @@
 #include "strbuf.h"
 #include "string-list.h"
 
-#ifndef get_st_mode_bits
-/*
- * The replacement lstat(2) we use on Cygwin is incomplete and
- * may return wrong permission bits. Most of the time we do not care,
- * but the callsites of this wrapper do care.
- */
-int get_st_mode_bits(const char *path, int *mode)
+static int get_st_mode_bits(const char *path, int *mode)
 {
        struct stat st;
        if (lstat(path, &st) < 0)
@@ -19,7 +13,6 @@ int get_st_mode_bits(const char *path, int *mode)
        *mode = st.st_mode;
        return 0;
 }
-#endif
 
 static char bad_path[] = "/bad-path/";
 
@@ -441,42 +434,100 @@ int adjust_shared_perm(const char *path)
        return 0;
 }
 
-const char *relative_path(const char *abs, const char *base)
+/*
+ * Give path as relative to prefix.
+ *
+ * The strbuf may or may not be used, so do not assume it contains the
+ * returned path.
+ */
+const char *relative_path(const char *in, const char *prefix,
+                         struct strbuf *sb)
 {
-       static char buf[PATH_MAX + 1];
+       int in_len = in ? strlen(in) : 0;
+       int prefix_len = prefix ? strlen(prefix) : 0;
+       int in_off = 0;
+       int prefix_off = 0;
        int i = 0, j = 0;
 
-       if (!base || !base[0])
-               return abs;
-       while (base[i]) {
-               if (is_dir_sep(base[i])) {
-                       if (!is_dir_sep(abs[j]))
-                               return abs;
-                       while (is_dir_sep(base[i]))
+       if (!in_len)
+               return "./";
+       else if (!prefix_len)
+               return in;
+
+       while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
+               if (is_dir_sep(prefix[i])) {
+                       while (is_dir_sep(prefix[i]))
                                i++;
-                       while (is_dir_sep(abs[j]))
+                       while (is_dir_sep(in[j]))
+                               j++;
+                       prefix_off = i;
+                       in_off = j;
+               } else {
+                       i++;
+                       j++;
+               }
+       }
+
+       if (
+           /* "prefix" seems like prefix of "in" */
+           i >= prefix_len &&
+           /*
+            * but "/foo" is not a prefix of "/foobar"
+            * (i.e. prefix not end with '/')
+            */
+           prefix_off < prefix_len) {
+               if (j >= in_len) {
+                       /* in="/a/b", prefix="/a/b" */
+                       in_off = in_len;
+               } else if (is_dir_sep(in[j])) {
+                       /* in="/a/b/c", prefix="/a/b" */
+                       while (is_dir_sep(in[j]))
                                j++;
+                       in_off = j;
+               } else {
+                       /* in="/a/bbb/c", prefix="/a/b" */
+                       i = prefix_off;
+               }
+       } else if (
+                  /* "in" is short than "prefix" */
+                  j >= in_len &&
+                  /* "in" not end with '/' */
+                  in_off < in_len) {
+               if (is_dir_sep(prefix[i])) {
+                       /* in="/a/b", prefix="/a/b/c/" */
+                       while (is_dir_sep(prefix[i]))
+                               i++;
+                       in_off = in_len;
+               }
+       }
+       in += in_off;
+       in_len -= in_off;
+
+       if (i >= prefix_len) {
+               if (!in_len)
+                       return "./";
+               else
+                       return in;
+       }
+
+       strbuf_reset(sb);
+       strbuf_grow(sb, in_len);
+
+       while (i < prefix_len) {
+               if (is_dir_sep(prefix[i])) {
+                       strbuf_addstr(sb, "../");
+                       while (is_dir_sep(prefix[i]))
+                               i++;
                        continue;
-               } else if (abs[j] != base[i]) {
-                       return abs;
                }
                i++;
-               j++;
        }
-       if (
-           /* "/foo" is a prefix of "/foo" */
-           abs[j] &&
-           /* "/foo" is not a prefix of "/foobar" */
-           !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j])
-          )
-               return abs;
-       while (is_dir_sep(abs[j]))
-               j++;
-       if (!abs[j])
-               strcpy(buf, ".");
-       else
-               strcpy(buf, abs + j);
-       return buf;
+       if (!is_dir_sep(prefix[prefix_len - 1]))
+               strbuf_addstr(sb, "../");
+
+       strbuf_addstr(sb, in);
+
+       return sb->buf;
 }
 
 /*
index 284f3970a31eb86a121712267e36ff162452590d..6ea0867493ad3e8cfd613dd57fa5ac9649507b47 100644 (file)
@@ -32,7 +32,7 @@ void add_pathspec_matches_against_index(const char **pathspec,
        if (!num_unmatched)
                return;
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
        }
 }
@@ -67,7 +67,7 @@ const char *check_path_for_gitlink(const char *path)
 {
        int i, path_len = strlen(path);
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                if (S_ISGITLINK(ce->ce_mode)) {
                        int ce_len = ce_namelen(ce);
                        if (path_len <= ce_len || path[ce_len] != '/' ||
index dc48159ccab1cf2f888a6169460c5fc60d0d1bab..204fdc673754c82637c59992198a2c8d31e05fa8 100644 (file)
@@ -61,7 +61,7 @@ require Exporter;
                 remote_refs prompt
                 get_tz_offset
                 credential credential_read credential_write
-                temp_acquire temp_release temp_reset temp_path);
+                temp_acquire temp_is_locked temp_release temp_reset temp_path);
 
 
 =head1 DESCRIPTION
@@ -1206,6 +1206,35 @@ sub temp_acquire {
        $temp_fd;
 }
 
+=item temp_is_locked ( NAME )
+
+Returns true if the internal lock created by a previous C<temp_acquire()>
+call with C<NAME> is still in effect.
+
+When temp_acquire is called on a C<NAME>, it internally locks the temporary
+file mapped to C<NAME>.  That lock will not be released until C<temp_release()>
+is called with either the original C<NAME> or the L<File::Handle> that was
+returned from the original call to temp_acquire.
+
+Subsequent attempts to call C<temp_acquire()> with the same C<NAME> will fail
+unless there has been an intervening C<temp_release()> call for that C<NAME>
+(or its corresponding L<File::Handle> that was returned by the original
+C<temp_acquire()> call).
+
+If true is returned by C<temp_is_locked()> for a C<NAME>, an attempt to
+C<temp_acquire()> the same C<NAME> will cause an error unless
+C<temp_release> is first called on that C<NAME> (or its corresponding
+L<File::Handle> that was returned by the original C<temp_acquire()> call).
+
+=cut
+
+sub temp_is_locked {
+       my ($self, $name) = _maybe_self(@_);
+       my $temp_fd = \$TEMP_FILEMAP{$name};
+
+       defined $$temp_fd && $$temp_fd->opened && $TEMP_FILES{$$temp_fd}{locked};
+}
+
 =item temp_release ( NAME )
 
 =item temp_release ( FILEHANDLE )
@@ -1265,7 +1294,7 @@ sub _temp_cache {
                        $tmpdir = $self->repo_path();
                }
 
-               ($$temp_fd, $fname) = File::Temp->tempfile(
+               ($$temp_fd, $fname) = File::Temp::tempfile(
                        'Git_XXXXXX', UNLINK => 1, DIR => $tmpdir,
                        ) or throw Error::Simple("couldn't open new temp file");
 
index fa0d3c6cddfa7a36745c576ba69e484675238593..b3bcd476daa7d4ab32c30c6fbd2f0a73ec9d0b4f 100644 (file)
@@ -499,6 +499,8 @@ sub apply_diff {
 1;
 __END__
 
+=head1 NAME
+
 Git::SVN::Editor - commit driver for "git svn set-tree" and dcommit
 
 =head1 SYNOPSIS
index 046a7a2f31cf923f634bd8b516b3da38ff989a46..10edb27732f439e5290a7b354efefeb10d219669 100644 (file)
@@ -1,6 +1,7 @@
 package Git::SVN::Fetcher;
-use vars qw/@ISA $_ignore_regex $_preserve_empty_dirs $_placeholder_filename
-            @deleted_gpath %added_placeholder $repo_id/;
+use vars qw/@ISA $_ignore_regex $_include_regex $_preserve_empty_dirs
+            $_placeholder_filename @deleted_gpath %added_placeholder
+            $repo_id/;
 use strict;
 use warnings;
 use SVN::Delta;
@@ -33,6 +34,10 @@ sub new {
        my $v = eval { command_oneline('config', '--get', $k) };
        $self->{ignore_regex} = $v;
 
+       $k = "svn-remote.$repo_id.include-paths";
+       $v = eval { command_oneline('config', '--get', $k) };
+       $self->{include_regex} = $v;
+
        $k = "svn-remote.$repo_id.preserve-empty-dirs";
        $v = eval { command_oneline('config', '--get', '--bool', $k) };
        if ($v && $v eq 'true') {
@@ -117,11 +122,18 @@ sub in_dot_git {
 }
 
 # return value: 0 -- don't ignore, 1 -- ignore
+# This will also check whether the path is explicitly included
 sub is_path_ignored {
        my ($self, $path) = @_;
        return 1 if in_dot_git($path);
        return 1 if defined($self->{ignore_regex}) &&
                    $path =~ m!$self->{ignore_regex}!;
+       return 0 if defined($self->{include_regex}) &&
+                   $path =~ m!$self->{include_regex}!;
+       return 0 if defined($_include_regex) &&
+                   $path =~ m!$_include_regex!;
+       return 1 if defined($self->{include_regex});
+       return 1 if defined($_include_regex);
        return 0 unless defined($_ignore_regex);
        return 1 if $path =~ m!$_ignore_regex!o;
        return 0;
@@ -303,11 +315,13 @@ sub change_file_prop {
 sub apply_textdelta {
        my ($self, $fb, $exp) = @_;
        return undef if $self->is_path_ignored($fb->{path});
-       my $fh = $::_repository->temp_acquire('svn_delta');
+       my $suffix = 0;
+       ++$suffix while $::_repository->temp_is_locked("svn_delta_${$}_$suffix");
+       my $fh = $::_repository->temp_acquire("svn_delta_${$}_$suffix");
        # $fh gets auto-closed() by SVN::TxDelta::apply(),
        # (but $base does not,) so dup() it for reading in close_file
        open my $dup, '<&', $fh or croak $!;
-       my $base = $::_repository->temp_acquire('git_blob');
+       my $base = $::_repository->temp_acquire("git_blob_${$}_$suffix");
 
        if ($fb->{blob}) {
                my ($base_is_link, $size);
@@ -512,6 +526,8 @@ sub stash_placeholder_list {
 1;
 __END__
 
+=head1 NAME
+
 Git::SVN::Fetcher - tree delta consumer for "git svn fetch"
 
 =head1 SYNOPSIS
index 74daa7a597fcf55a649a4e90367b1e783a74fcd5..e940b08505f1488efbe257201b3881b7e00a0a2a 100644 (file)
@@ -125,6 +125,8 @@ sub _read_password {
 1;
 __END__
 
+=head1 NAME
+
 Git::SVN::Prompt - authentication callbacks for git-svn
 
 =head1 SYNOPSIS
index 6a212eb7a8182acc00eb597d2100310b7855738a..75ecc425b6ebbf2cb1941b6b6bca78f8d0b70d62 100644 (file)
@@ -627,6 +627,8 @@ sub skip_unknown_revs {
 1;
 __END__
 
+=head1 NAME
+
 Git::SVN::Ra - Subversion remote access functions for git-svn
 
 =head1 SYNOPSIS
index 5eb5aadb3aa29103ddcb524ba26273f1f9340869..107aa5917cac70b715d8bdac1d0e83a2f5da2d3d 100644 (file)
--- a/po/TEAMS
+++ b/po/TEAMS
@@ -12,6 +12,11 @@ Members:     Thomas Rast <trast@student.ethz.ch>
                Jan Krüger <jk@jk.gs>
                Christian Stimming <stimming@tuhh.de>
 
+Language:      fr (French)
+Repository:    https://github.com/jnavila/git
+Leader:                Jean-Noël Avila <jn.avila@free.fr>
+Members:       Sébastien Helleu <flashcode@flashtux.org>
+
 Language:      is (Icelandic)
 Leader:                Ævar Arnfjörð Bjarmason <avarab@gmail.com>
 
index 83c30b158764d4e88c21cc8d332d6c0eacb1eba1..f4076fb633ca970f22281a6ff98c229587945c69 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -5,10 +5,10 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: git 1.8.2\n"
+"Project-Id-Version: git 1.8.4\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2013-03-05 12:36+0800\n"
-"PO-Revision-Date: 2012-10-02 19:35+0200\n"
+"POT-Creation-Date: 2013-08-06 14:10+0800\n"
+"PO-Revision-Date: 2013-07-28 18:42+0200\n"
 "Last-Translator: Ralf Thielow <ralf.thielow@gmail.com>\n"
 "Language-Team: German <>\n"
 "Language: de\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n!=1);\n"
 
-#: advice.c:49
+#: advice.c:57
 #, c-format
 msgid "hint: %.*s\n"
 msgstr "Hinweis: %.*s\n"
@@ -26,21 +26,21 @@ msgstr "Hinweis: %.*s\n"
 #. * Message used both when 'git commit' fails and when
 #. * other commands doing a merge do.
 #.
-#: advice.c:79
+#: advice.c:87
 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'."
 msgstr ""
-"Korrigieren Sie dies im Arbeitsbaum,\n"
+"Korrigieren Sie dies im Arbeitsverzeichnis,\n"
 "und benutzen Sie dann 'git add/rm <Datei>'\n"
-"um die Auflösung entsprechend zu markieren und einzutragen,\n"
+"um die Auflösung entsprechend zu markieren und zu committen,\n"
 "oder benutzen Sie 'git commit -a'."
 
 #: archive.c:10
 msgid "git archive [options] <tree-ish> [<path>...]"
-msgstr "git archive [Optionen] <Versionsreferenz> [<Pfad>...]"
+msgstr "git archive [Optionen] <Commit-Referenz> [<Pfad>...]"
 
 #: archive.c:11
 msgid "git archive --list"
@@ -50,12 +50,12 @@ msgstr "git archive --list"
 msgid ""
 "git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]"
 msgstr ""
-"git archive --remote <Projektarchiv> [--exec <Programm>] [Optionen] "
-"<Versionsreferenz> [Pfad...]"
+"git archive --remote <Repository> [--exec <Programm>] [Optionen] <Commit-"
+"Referenz> [Pfad...]"
 
 #: archive.c:13
 msgid "git archive --remote <repo> [--exec <cmd>] --list"
-msgstr "git archive --remote <Projektarchiv> [--exec <Programm>] --list"
+msgstr "git archive --remote <Repository> [--exec <Programm>] --list"
 
 #: archive.c:323
 msgid "fmt"
@@ -63,27 +63,27 @@ msgstr "Format"
 
 #: archive.c:323
 msgid "archive format"
-msgstr "Ausgabeformat"
+msgstr "Archivformat"
 
-#: archive.c:324 builtin/log.c:1115
+#: archive.c:324 builtin/log.c:1173
 msgid "prefix"
 msgstr "Prefix"
 
 #: archive.c:325
 msgid "prepend prefix to each pathname in the archive"
-msgstr "stellt einen Präfix vor jeden Pfadnamen in der Ausgabe"
+msgstr "stellt einen Präfix vor jeden Pfadnamen in dem Archiv"
 
-#: archive.c:326 builtin/archive.c:91 builtin/blame.c:2366
-#: builtin/blame.c:2367 builtin/config.c:55 builtin/fast-export.c:653
-#: builtin/fast-export.c:655 builtin/grep.c:715 builtin/hash-object.c:77
-#: builtin/ls-files.c:497 builtin/ls-files.c:500 builtin/notes.c:536
-#: builtin/notes.c:693 builtin/read-tree.c:107 parse-options.h:149
+#: archive.c:326 builtin/archive.c:88 builtin/blame.c:2292
+#: builtin/blame.c:2293 builtin/config.c:56 builtin/fast-export.c:673
+#: builtin/fast-export.c:675 builtin/grep.c:714 builtin/hash-object.c:77
+#: builtin/ls-files.c:496 builtin/ls-files.c:499 builtin/notes.c:408
+#: builtin/notes.c:565 builtin/read-tree.c:108 parse-options.h:152
 msgid "file"
 msgstr "Datei"
 
-#: archive.c:327 builtin/archive.c:92
+#: archive.c:327 builtin/archive.c:89
 msgid "write the archive to this file"
-msgstr "schreibt die Ausgabe in diese Datei"
+msgstr "schreibt das Archiv in diese Datei"
 
 #: archive.c:329
 msgid "read .gitattributes in working directory"
@@ -107,21 +107,21 @@ msgstr "besser komprimieren"
 
 #: archive.c:343
 msgid "list supported archive formats"
-msgstr "listet unterstützte Ausgabeformate auf"
+msgstr "listet unterstützte Archivformate auf"
 
-#: archive.c:345 builtin/archive.c:93 builtin/clone.c:85
+#: archive.c:345 builtin/archive.c:90 builtin/clone.c:85
 msgid "repo"
-msgstr "Projektarchiv"
+msgstr "Repository"
 
-#: archive.c:346 builtin/archive.c:94
+#: archive.c:346 builtin/archive.c:91
 msgid "retrieve the archive from remote repository <repo>"
-msgstr "ruft das Archiv von externem Projektarchiv <Projektarchiv> ab"
+msgstr "ruft das Archiv von Remote-Repository <Repository> ab"
 
-#: archive.c:347 builtin/archive.c:95 builtin/notes.c:615
+#: archive.c:347 builtin/archive.c:92 builtin/notes.c:487
 msgid "command"
 msgstr "Programm"
 
-#: archive.c:348 builtin/archive.c:96
+#: archive.c:348 builtin/archive.c:93
 msgid "path to the remote git-upload-archive command"
 msgstr "Pfad zum externen \"git-upload-archive\"-Programm"
 
@@ -133,6 +133,136 @@ msgstr ""
 "Verneinende Muster werden in Git-Attributen ignoriert.\n"
 "Benutzen Sie '\\!' für führende Ausrufezeichen."
 
+#: branch.c:60
+#, c-format
+msgid "Not setting branch %s as its own upstream."
+msgstr "Branch %s kann nicht sein eigener Upstream-Branch sein."
+
+#: branch.c:82
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s by rebasing."
+msgstr ""
+"Branch %s konfiguriert zum Folgen von Remote-Branch %s von %s durch Rebase."
+
+#: branch.c:83
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s."
+msgstr "Branch %s konfiguriert zum Folgen von Remote-Branch %s von %s."
+
+#: branch.c:87
+#, c-format
+msgid "Branch %s set up to track local branch %s by rebasing."
+msgstr "Branch %s konfiguriert zum Folgen von lokalem Branch %s durch Rebase."
+
+#: branch.c:88
+#, c-format
+msgid "Branch %s set up to track local branch %s."
+msgstr "Branch %s konfiguriert zum Folgen von lokalem Branch %s."
+
+#: branch.c:92
+#, c-format
+msgid "Branch %s set up to track remote ref %s by rebasing."
+msgstr "Branch %s konfiguriert zum Folgen von Remote-Referenz %s durch Rebase."
+
+#: branch.c:93
+#, c-format
+msgid "Branch %s set up to track remote ref %s."
+msgstr "Branch %s konfiguriert zum Folgen von Remote-Referenz %s."
+
+#: branch.c:97
+#, c-format
+msgid "Branch %s set up to track local ref %s by rebasing."
+msgstr ""
+"Branch %s konfiguriert zum Folgen von lokaler Referenz %s durch Rebase."
+
+#: branch.c:98
+#, c-format
+msgid "Branch %s set up to track local ref %s."
+msgstr "Branch %s konfiguriert zum Folgen von lokaler Referenz %s."
+
+#: branch.c:118
+#, c-format
+msgid "Tracking not set up: name too long: %s"
+msgstr ""
+"Konfiguration zum Folgen von Branch nicht eingerichtet. Name zu lang: %s"
+
+#: branch.c:137
+#, c-format
+msgid "Not tracking: ambiguous information for ref %s"
+msgstr ""
+"Konfiguration zum Folgen von Branch nicht eingerichtet. Referenz %s ist "
+"mehrdeutig."
+
+#: branch.c:182
+#, c-format
+msgid "'%s' is not a valid branch name."
+msgstr "'%s' ist kein gültiger Branchname."
+
+#: branch.c:187
+#, c-format
+msgid "A branch named '%s' already exists."
+msgstr "Branch '%s' existiert bereits."
+
+#: branch.c:195
+msgid "Cannot force update the current branch."
+msgstr "Kann Aktualisierung des aktuellen Branches nicht erzwingen."
+
+#: branch.c:216
+#, c-format
+msgid "Cannot setup tracking information; starting point '%s' is not a branch."
+msgstr ""
+"Kann Tracking-Informationen nicht einrichten; Startpunkt '%s' ist kein "
+"Branch."
+
+#: branch.c:218
+#, c-format
+msgid "the requested upstream branch '%s' does not exist"
+msgstr "der angeforderte Upstream-Branch '%s' existiert nicht"
+
+#: branch.c:220
+msgid ""
+"\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push."
+msgstr ""
+"\n"
+"Falls Sie vorhaben, Ihre Arbeit auf einem bereits existierenden\n"
+"Upstream-Branch aufzubauen, sollten Sie \"git fetch\"\n"
+"ausführen, um diesen abzurufen.\n"
+"\n"
+"Falls Sie vorhaben, einen neuen lokalen Branch zu versenden\n"
+"der seinem externen Gegenstück folgen soll, können Sie\n"
+"\"git push -u\" verwenden, um den Upstream-Branch beim \"push\"\n"
+"zu konfigurieren."
+
+#: branch.c:265
+#, c-format
+msgid "Not a valid object name: '%s'."
+msgstr "Ungültiger Objekt-Name: '%s'"
+
+#: branch.c:285
+#, c-format
+msgid "Ambiguous object name: '%s'."
+msgstr "mehrdeutiger Objekt-Name: '%s'"
+
+#: branch.c:290
+#, c-format
+msgid "Not a valid branch point: '%s'."
+msgstr "Ungültiger Branchpunkt: '%s'"
+
+#: branch.c:296
+msgid "Failed to lock ref for update"
+msgstr "Fehler beim Sperren der Referenz zur Aktualisierung."
+
+#: branch.c:314
+msgid "Failed to write ref"
+msgstr "Fehler beim Schreiben der Referenz."
+
 #: bundle.c:36
 #, c-format
 msgid "'%s' does not look like a v2 bundle file"
@@ -143,44 +273,44 @@ msgstr "'%s' sieht nicht wie eine v2 Paketdatei aus"
 msgid "unrecognized header: %s%s (%d)"
 msgstr "nicht erkannter Kopfbereich: %s%s (%d)"
 
-#: bundle.c:89 builtin/commit.c:674
+#: bundle.c:89 builtin/commit.c:697
 #, c-format
 msgid "could not open '%s'"
 msgstr "Konnte '%s' nicht öffnen"
 
 #: bundle.c:140
 msgid "Repository lacks these prerequisite commits:"
-msgstr "Dem Projektarchiv fehlen folgende vorausgesetzte Versionen:"
+msgstr "Dem Repository fehlen folgende vorausgesetzte Commits:"
 
-#: bundle.c:164 sequencer.c:566 sequencer.c:998 builtin/log.c:299
-#: builtin/log.c:751 builtin/log.c:1358 builtin/log.c:1574 builtin/merge.c:347
-#: builtin/shortlog.c:157
+#: bundle.c:164 sequencer.c:661 sequencer.c:1111 builtin/log.c:331
+#: builtin/log.c:801 builtin/log.c:1399 builtin/log.c:1625 builtin/merge.c:364
+#: builtin/shortlog.c:156
 msgid "revision walk setup failed"
 msgstr "Einrichtung des Revisionsgangs fehlgeschlagen"
 
 #: bundle.c:186
 #, c-format
-msgid "The bundle contains %d ref"
-msgid_plural "The bundle contains %d refs"
-msgstr[0] "Das Paket enthält %d Referenz"
-msgstr[1] "Das Paket enthält %d Referenzen"
+msgid "The bundle contains this ref:"
+msgid_plural "The bundle contains these %d refs:"
+msgstr[0] "Das Paket enthält diese Referenz:"
+msgstr[1] "Das Paket enthält diese %d Referenzen:"
 
-#: bundle.c:192
+#: bundle.c:193
 msgid "The bundle records a complete history."
 msgstr "Das Paket speichert eine komplette Historie."
 
 #: bundle.c:195
 #, c-format
-msgid "The bundle requires this ref"
-msgid_plural "The bundle requires these %d refs"
-msgstr[0] "Das Paket benötigt diese Referenz"
-msgstr[1] "Das Paket benötigt diese %d Referenzen"
+msgid "The bundle requires this ref:"
+msgid_plural "The bundle requires these %d refs:"
+msgstr[0] "Das Paket benötigt diese Referenz:"
+msgstr[1] "Das Paket benötigt diese %d Referenzen:"
 
 #: bundle.c:294
 msgid "rev-list died"
 msgstr "\"rev-list\" abgebrochen"
 
-#: bundle.c:300 builtin/log.c:1254 builtin/shortlog.c:260
+#: bundle.c:300 builtin/log.c:1310 builtin/shortlog.c:259
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "nicht erkanntes Argument: %s"
@@ -211,30 +341,30 @@ msgstr "kann '%s' nicht erstellen"
 msgid "index-pack died"
 msgstr "Erstellung der Paketindexdatei abgebrochen"
 
-#: commit.c:50
+#: commit.c:53
 #, c-format
 msgid "could not parse %s"
 msgstr "konnte %s nicht parsen"
 
-#: commit.c:52
+#: commit.c:55
 #, c-format
 msgid "%s %s is not a commit!"
-msgstr "%s %s ist keine Version!"
+msgstr "%s %s ist kein Commit!"
 
 #: compat/obstack.c:406 compat/obstack.c:408
 msgid "memory exhausted"
 msgstr "Speicher verbraucht"
 
-#: connected.c:39
+#: connected.c:60
 msgid "Could not run 'git rev-list'"
 msgstr "Konnte 'git rev-list' nicht ausführen"
 
-#: connected.c:48
+#: connected.c:80
 #, c-format
 msgid "failed write to rev-list: %s"
 msgstr "Fehler beim Schreiben nach rev-list: %s"
 
-#: connected.c:56
+#: connected.c:88
 #, c-format
 msgid "failed to close rev-list's stdin: %s"
 msgstr "Fehler beim Schließen von rev-list's Standard-Eingabe: %s"
@@ -331,7 +461,7 @@ msgstr ""
 "Fehler in 'diff.dirstat' Konfigurationsvariable gefunden:\n"
 "%s"
 
-#: diff.c:3468
+#: diff.c:3487
 #, c-format
 msgid ""
 "Failed to parse --dirstat/-X option parameter:\n"
@@ -340,12 +470,12 @@ msgstr ""
 "Fehler beim Parsen des --dirstat/-X Optionsparameters:\n"
 "%s"
 
-#: diff.c:3482
+#: diff.c:3501
 #, c-format
 msgid "Failed to parse --submodule option parameter: '%s'"
 msgstr "Fehler beim Parsen des --submodule Optionsparameters: '%s'"
 
-#: gpg-interface.c:59 gpg-interface.c:127
+#: gpg-interface.c:59 gpg-interface.c:131
 msgid "could not run gpg."
 msgstr "konnte gpg nicht ausführen"
 
@@ -357,45 +487,45 @@ msgstr "gpg hat die Daten nicht akzeptiert"
 msgid "gpg failed to sign the data"
 msgstr "gpg beim Signieren der Daten fehlgeschlagen"
 
-#: gpg-interface.c:112
+#: gpg-interface.c:115
 #, c-format
 msgid "could not create temporary file '%s': %s"
 msgstr "konnte temporäre Datei '%s' nicht erstellen: %s"
 
-#: gpg-interface.c:115
+#: gpg-interface.c:118
 #, c-format
 msgid "failed writing detached signature to '%s': %s"
 msgstr "Fehler beim Schreiben der Signatur nach '%s': %s"
 
-#: grep.c:1622
+#: grep.c:1623
 #, c-format
 msgid "'%s': unable to read %s"
 msgstr "'%s': konnte nicht lesen %s"
 
-#: grep.c:1639
+#: grep.c:1640
 #, c-format
 msgid "'%s': %s"
 msgstr "'%s': %s"
 
-#: grep.c:1650
+#: grep.c:1651
 #, c-format
 msgid "'%s': short read %s"
 msgstr "'%s': read() zu kurz %s"
 
-#: help.c:212
+#: help.c:210
 #, c-format
 msgid "available git commands in '%s'"
 msgstr "Vorhandene Git-Kommandos in '%s'"
 
-#: help.c:219
+#: help.c:217
 msgid "git commands available from elsewhere on your $PATH"
 msgstr "Vorhandene Git-Kommandos irgendwo in Ihrem $PATH"
 
-#: help.c:235
+#: help.c:233
 msgid "The most commonly used git commands are:"
 msgstr "Die allgemein verwendeten Git-Kommandos sind:"
 
-#: help.c:292
+#: help.c:290
 #, c-format
 msgid ""
 "'%s' appears to be a git command, but we were not\n"
@@ -404,11 +534,11 @@ msgstr ""
 "'%s' scheint ein git-Kommando zu sein, konnte aber\n"
 "nicht ausgeführt werden. Vielleicht ist git-%s fehlerhaft?"
 
-#: help.c:349
+#: help.c:347
 msgid "Uh oh. Your system reports no Git commands at all."
 msgstr "Uh oh. Keine Git-Kommandos auf Ihrem System vorhanden."
 
-#: help.c:371
+#: help.c:369
 #, c-format
 msgid ""
 "WARNING: You called a Git command named '%s', which does not exist.\n"
@@ -417,17 +547,17 @@ msgstr ""
 "Warnung: Sie haben das nicht existierende Git-Kommando '%s' ausgeführt.\n"
 "Setze fort unter der Annahme, dass Sie '%s' gemeint haben"
 
-#: help.c:376
+#: help.c:374
 #, c-format
 msgid "in %0.1f seconds automatically..."
 msgstr "automatisch in %0.1f Sekunden..."
 
-#: help.c:383
+#: help.c:381
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
 msgstr "git: '%s' ist kein Git-Kommando. Siehe 'git --help'."
 
-#: help.c:387
+#: help.c:385 help.c:444
 msgid ""
 "\n"
 "Did you mean this?"
@@ -441,89 +571,94 @@ msgstr[1] ""
 "\n"
 "Haben Sie eines von diesen gemeint?"
 
+#: help.c:440
+#, c-format
+msgid "%s: %s - %s"
+msgstr "%s: %s - %s"
+
 #: merge.c:56
 msgid "failed to read the cache"
 msgstr "Lesen des Zwischenspeichers fehlgeschlagen"
 
-#: merge.c:110 builtin/checkout.c:333 builtin/checkout.c:534
-#: builtin/clone.c:586
+#: merge.c:110 builtin/checkout.c:365 builtin/checkout.c:566
+#: builtin/clone.c:656
 msgid "unable to write new index file"
-msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+msgstr "Konnte neue Staging-Area-Datei nicht schreiben."
 
 #: merge-recursive.c:190
 #, c-format
 msgid "(bad commit)\n"
-msgstr "(ungültige Version)\n"
+msgstr "(ungültiger Commit)\n"
 
 #: merge-recursive.c:206
 #, c-format
 msgid "addinfo_cache failed for path '%s'"
 msgstr "addinfo_cache für Pfad '%s' fehlgeschlagen"
 
-#: merge-recursive.c:268
+#: merge-recursive.c:269
 msgid "error building trees"
-msgstr "Fehler beim Erstellen der Bäume"
+msgstr "Fehler beim Erstellen der \"Tree\"-Objekte"
 
-#: merge-recursive.c:672
+#: merge-recursive.c:673
 #, c-format
 msgid "failed to create path '%s'%s"
 msgstr "Fehler beim Erstellen des Pfades '%s'%s"
 
-#: merge-recursive.c:683
+#: merge-recursive.c:684
 #, c-format
 msgid "Removing %s to make room for subdirectory\n"
 msgstr "Entferne %s um Platz für Unterverzeichnis zu schaffen\n"
 
 #. something else exists
 #. .. but not some other error (who really cares what?)
-#: merge-recursive.c:697 merge-recursive.c:718
+#: merge-recursive.c:698 merge-recursive.c:719
 msgid ": perhaps a D/F conflict?"
 msgstr ": vielleicht ein Verzeichnis/Datei-Konflikt?"
 
-#: merge-recursive.c:708
+#: merge-recursive.c:709
 #, c-format
 msgid "refusing to lose untracked file at '%s'"
 msgstr "verweigere, da unbeobachtete Dateien in '%s' verloren gehen würden"
 
-#: merge-recursive.c:748
+#: merge-recursive.c:749
 #, c-format
 msgid "cannot read object %s '%s'"
 msgstr "kann Objekt %s '%s' nicht lesen"
 
-#: merge-recursive.c:750
+#: merge-recursive.c:751
 #, c-format
 msgid "blob expected for %s '%s'"
 msgstr "Blob erwartet für %s '%s'"
 
-#: merge-recursive.c:773 builtin/clone.c:302
+#: merge-recursive.c:774 builtin/clone.c:312
 #, c-format
 msgid "failed to open '%s'"
 msgstr "Fehler beim Öffnen von '%s'"
 
-#: merge-recursive.c:781
+#: merge-recursive.c:782
 #, c-format
 msgid "failed to symlink '%s'"
-msgstr "Fehler beim Erstellen einer symbolischen Verknüpfung für '%s'"
+msgstr "Fehler beim Erstellen eines symbolischen Verweises für '%s'"
 
-#: merge-recursive.c:784
+#: merge-recursive.c:785
 #, c-format
 msgid "do not know what to do with %06o %s '%s'"
 msgstr "weiß nicht was mit %06o %s '%s' zu machen ist"
 
-#: merge-recursive.c:922
+#: merge-recursive.c:923
 msgid "Failed to execute internal merge"
-msgstr "Fehler bei Ausführung der internen Zusammenführung"
+msgstr "Fehler bei Ausführung des internen Merges"
 
-#: merge-recursive.c:926
+#: merge-recursive.c:927
 #, c-format
 msgid "Unable to add %s to database"
 msgstr "Konnte %s nicht zur Datenbank hinzufügen"
 
-#: merge-recursive.c:942
+#: merge-recursive.c:943
 msgid "unsupported object type in the tree"
-msgstr "nicht unterstützter Objekttyp im Baum"
+msgstr "nicht unterstützter Objekttyp im Verzeichnis"
 
-#: merge-recursive.c:1021 merge-recursive.c:1035
+#: merge-recursive.c:1022 merge-recursive.c:1036
 #, c-format
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
@@ -532,7 +667,7 @@ msgstr ""
 "KONFLIKT (%s/löschen): %s gelöscht in %s und %s in %s. Stand %s von %s wurde "
 "im Arbeitsbereich gelassen."
 
-#: merge-recursive.c:1027 merge-recursive.c:1040
+#: merge-recursive.c:1028 merge-recursive.c:1041
 #, c-format
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
@@ -541,172 +676,200 @@ msgstr ""
 "KONFLIKT (%s/löschen): %s gelöscht in %s und %s in %s. Stand %s von %s wurde "
 "im Arbeitsbereich bei %s gelassen."
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "rename"
 msgstr "umbenennen"
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "renamed"
 msgstr "umbenannt"
 
-#: merge-recursive.c:1137
+#: merge-recursive.c:1138
 #, c-format
 msgid "%s is a directory in %s adding as %s instead"
 msgstr "%s ist ein Verzeichnis in %s, füge es stattdessen als %s hinzu"
 
-#: merge-recursive.c:1159
+#: merge-recursive.c:1160
 #, c-format
 msgid ""
 "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
 "\"->\"%s\" in \"%s\"%s"
 msgstr ""
-"KONFLIKT (umbenennen/umbenennen): Benenne um \"%s\"->\"%s\" in Zweig \"%s\" "
-"und \"%s\"->\"%s\" in Zweig \"%s\"%s"
+"KONFLIKT (umbenennen/umbenennen): Benenne um \"%s\"->\"%s\" in Branch \"%s\" "
+"und \"%s\"->\"%s\" in Branch \"%s\"%s"
 
-#: merge-recursive.c:1164
+#: merge-recursive.c:1165
 msgid " (left unresolved)"
 msgstr " (bleibt unaufgelöst)"
 
-#: merge-recursive.c:1218
+#: merge-recursive.c:1219
 #, c-format
 msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
 msgstr ""
 "KONFLIKT (umbenennen/umbenennen): Benenne um %s->%s in %s. Benenne um %s->%s "
 "in %s"
 
-#: merge-recursive.c:1248
+#: merge-recursive.c:1249
 #, c-format
 msgid "Renaming %s to %s and %s to %s instead"
 msgstr "Benenne stattdessen %s nach %s und %s nach %s um"
 
-#: merge-recursive.c:1447
+#: merge-recursive.c:1448
 #, c-format
 msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s"
 msgstr ""
 "KONFLIKT (umbenennen/hinzufügen): Benenne um %s->%s in %s. %s hinzugefügt in "
 "%s"
 
-#: merge-recursive.c:1457
+#: merge-recursive.c:1458
 #, c-format
 msgid "Adding merged %s"
 msgstr "Füge zusammengeführte Datei %s hinzu"
 
-#: merge-recursive.c:1462 merge-recursive.c:1660
+#: merge-recursive.c:1463 merge-recursive.c:1661
 #, c-format
 msgid "Adding as %s instead"
 msgstr "Füge stattdessen als %s hinzu"
 
-#: merge-recursive.c:1513
+#: merge-recursive.c:1514
 #, c-format
 msgid "cannot read object %s"
 msgstr "kann Objekt %s nicht lesen"
 
-#: merge-recursive.c:1516
+#: merge-recursive.c:1517
 #, c-format
 msgid "object %s is not a blob"
 msgstr "Objekt %s ist kein Blob"
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modify"
 msgstr "ändern"
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modified"
 msgstr "geändert"
 
-#: merge-recursive.c:1574
+#: merge-recursive.c:1575
 msgid "content"
 msgstr "Inhalt"
 
-#: merge-recursive.c:1581
+#: merge-recursive.c:1582
 msgid "add/add"
 msgstr "hinzufügen/hinzufügen"
 
-#: merge-recursive.c:1615
+#: merge-recursive.c:1616
 #, c-format
 msgid "Skipped %s (merged same as existing)"
-msgstr "%s ausgelassen (Ergebnis der Zusammenführung existiert bereits)"
+msgstr "%s ausgelassen (Ergebnis des Merges existiert bereits)"
 
-#: merge-recursive.c:1629
+#: merge-recursive.c:1630
 #, c-format
 msgid "Auto-merging %s"
-msgstr "automatische Zusammenführung von %s"
+msgstr "automatischer Merge von %s"
 
-#: merge-recursive.c:1633 git-submodule.sh:942
+#: merge-recursive.c:1634 git-submodule.sh:1118
 msgid "submodule"
-msgstr "Unterprojekt"
+msgstr "Submodul"
 
-#: merge-recursive.c:1634
+#: merge-recursive.c:1635
 #, c-format
 msgid "CONFLICT (%s): Merge conflict in %s"
-msgstr "KONFLIKT (%s): Zusammenführungskonflikt in %s"
+msgstr "KONFLIKT (%s): Merge-Konflikt in %s"
 
-#: merge-recursive.c:1724
+#: merge-recursive.c:1725
 #, c-format
 msgid "Removing %s"
 msgstr "Entferne %s"
 
-#: merge-recursive.c:1749
+#: merge-recursive.c:1750
 msgid "file/directory"
 msgstr "Datei/Verzeichnis"
 
-#: merge-recursive.c:1755
+#: merge-recursive.c:1756
 msgid "directory/file"
 msgstr "Verzeichnis/Datei"
 
-#: merge-recursive.c:1760
+#: merge-recursive.c:1761
 #, c-format
 msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
 msgstr ""
 "KONFLIKT (%s): Es existiert bereits ein Verzeichnis %s in %s. Füge %s als %s "
 "hinzu."
 
-#: merge-recursive.c:1770
+#: merge-recursive.c:1771
 #, c-format
 msgid "Adding %s"
 msgstr "Füge %s hinzu"
 
-#: merge-recursive.c:1787
+#: merge-recursive.c:1788
 msgid "Fatal merge failure, shouldn't happen."
-msgstr "Fataler Fehler bei der Zusammenführung. Sollte nicht passieren."
+msgstr "Fataler Merge-Fehler. Sollte nicht passieren."
 
-#: merge-recursive.c:1806
+#: merge-recursive.c:1807
 msgid "Already up-to-date!"
 msgstr "Bereits aktuell!"
 
-#: merge-recursive.c:1815
+#: merge-recursive.c:1816
 #, c-format
 msgid "merging of trees %s and %s failed"
-msgstr "Zusammenführen der Bäume %s und %s fehlgeschlagen"
+msgstr "Zusammenführen der \"Tree\"-Objekte %s und %s fehlgeschlagen"
 
-#: merge-recursive.c:1845
+#: merge-recursive.c:1846
 #, c-format
 msgid "Unprocessed path??? %s"
 msgstr "unverarbeiteter Pfad??? %s"
 
-#: merge-recursive.c:1890
+#: merge-recursive.c:1891
 msgid "Merging:"
-msgstr "Zusammenführung:"
+msgstr "Merge:"
 
-#: merge-recursive.c:1903
+#: merge-recursive.c:1904
 #, c-format
 msgid "found %u common ancestor:"
 msgid_plural "found %u common ancestors:"
-msgstr[0] "%u gemeinsamen Vorfahren gefunden"
-msgstr[1] "%u gemeinsame Vorfahren gefunden"
+msgstr[0] "%u gemeinsamen Vorgänger-Commit gefunden"
+msgstr[1] "%u gemeinsame Vorgänger-Commits gefunden"
 
-#: merge-recursive.c:1940
+#: merge-recursive.c:1941
 msgid "merge returned no commit"
-msgstr "Zusammenführung hat keine Version zurückgegeben"
+msgstr "Merge hat keinen Commit zurückgegeben"
 
-#: merge-recursive.c:1997
+#: merge-recursive.c:1998
 #, c-format
 msgid "Could not parse object '%s'"
 msgstr "Konnte Objekt '%s' nicht parsen."
 
-#: merge-recursive.c:2009 builtin/merge.c:643
+#: merge-recursive.c:2010 builtin/merge.c:672
 msgid "Unable to write index."
-msgstr "Konnte Bereitstellung nicht schreiben."
+msgstr "Konnte Staging-Area nicht schreiben."
+
+#: notes-utils.c:40
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+"Kann uninitialisiertes/unreferenzierte Notiz-Verzeichnis nicht committen."
+
+#: notes-utils.c:81
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Ungültiger notes.rewriteMode Wert: '%s'"
+
+#: notes-utils.c:91
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr ""
+"Umschreiben der Notizen in %s zurückgewiesen (außerhalb von refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: notes-utils.c:118
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Ungültiger %s Wert: '%s'"
+
+#: object.c:204
+#, c-format
+msgid "unable to parse object: %s"
+msgstr "Konnte Objekt '%s' nicht parsen."
 
 #: parse-options.c:489
 msgid "..."
@@ -736,41 +899,40 @@ msgstr "-NUM"
 #: pathspec.c:83
 #, c-format
 msgid "Path '%s' is in submodule '%.*s'"
-msgstr "Pfad '%s' befindet sich in Unterprojekt '%.*s'"
+msgstr "Pfad '%s' befindet sich in Submodul '%.*s'"
 
 #: pathspec.c:99
 #, c-format
 msgid "'%s' is beyond a symbolic link"
-msgstr "'%s' ist über einem symbolischen Link"
+msgstr "'%s' ist hinter einem symbolischen Verweis"
 
-#: remote.c:1653
+#: remote.c:1788
 #, 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] "Ihr Zweig ist vor '%s' um %d Version.\n"
-msgstr[1] "Ihr Zweig ist vor '%s' um %d Versionen.\n"
+msgstr[0] "Ihr Branch ist vor '%s' um %d Commit.\n"
+msgstr[1] "Ihr Branch ist vor '%s' um %d Commits.\n"
 
-#: remote.c:1659
+#: remote.c:1794
 msgid "  (use \"git push\" to publish your local commits)\n"
-msgstr "  (benutzen Sie \"git push\" um lokalen Versionen herauszubringen)\n"
+msgstr "  (benutzen Sie \"git push\" um lokale Commits zu publizieren)\n"
 
-#: remote.c:1662
+#: remote.c:1797
 #, c-format
 msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
 msgid_plural ""
 "Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
 msgstr[0] ""
-"Ihr Zweig ist zu '%s' um %d Version hinterher, und kann vorgespult werden.\n"
+"Ihr Branch ist zu '%s' um %d Commit hinterher, und kann vorgespult werden.\n"
 msgstr[1] ""
-"Ihr Zweig ist zu '%s' um %d Versionen hinterher, und kann vorgespult "
-"werden.\n"
+"Ihr Branch ist zu '%s' um %d Commits hinterher, und kann vorgespult werden.\n"
 
-#: remote.c:1670
+#: remote.c:1805
 msgid "  (use \"git pull\" to update your local branch)\n"
 msgstr ""
-"  (benutzen Sie \"git pull\" um Ihren lokalen Zweig zu aktualisieren)\n"
+"  (benutzen Sie \"git pull\" um Ihren lokalen Branch zu aktualisieren)\n"
 
-#: remote.c:1673
+#: remote.c:1808
 #, c-format
 msgid ""
 "Your branch and '%s' have diverged,\n"
@@ -779,31 +941,40 @@ msgid_plural ""
 "Your branch and '%s' have diverged,\n"
 "and have %d and %d different commits each, respectively.\n"
 msgstr[0] ""
-"Ihr Zweig und '%s' sind divergiert,\n"
-"und haben jeweils %d und %d unterschiedliche Versionen.\n"
+"Ihr Branch und '%s' sind divergiert,\n"
+"und haben jeweils %d und %d unterschiedliche Commits.\n"
 msgstr[1] ""
-"Ihr Zweig und '%s' sind divergiert,\n"
-"und haben jeweils %d und %d unterschiedliche Versionen.\n"
+"Ihr Branch und '%s' sind divergiert,\n"
+"und haben jeweils %d und %d unterschiedliche Commits.\n"
 
-#: remote.c:1683
+#: remote.c:1818
 msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
 msgstr ""
-"  (benutzen Sie \"git pull\" um Ihren Zweig mit dem externen "
+"  (benutzen Sie \"git pull\" um Ihren Branch mit dem Remote-Branch "
 "zusammenzuführen)\n"
 
-#: sequencer.c:123 builtin/merge.c:761 builtin/merge.c:874 builtin/merge.c:984
-#: builtin/merge.c:994
+#: run-command.c:80
+msgid "open /dev/null failed"
+msgstr "Öffnen von /dev/null fehlgeschlagen"
+
+#: run-command.c:82
+#, c-format
+msgid "dup2(%d,%d) failed"
+msgstr "dup2(%d,%d) fehlgeschlagen"
+
+#: sequencer.c:206 builtin/merge.c:790 builtin/merge.c:903
+#: builtin/merge.c:1013 builtin/merge.c:1023
 #, c-format
 msgid "Could not open '%s' for writing"
 msgstr "Konnte '%s' nicht zum Schreiben öffnen."
 
-#: sequencer.c:125 builtin/merge.c:333 builtin/merge.c:764 builtin/merge.c:986
-#: builtin/merge.c:999
+#: sequencer.c:208 builtin/merge.c:350 builtin/merge.c:793
+#: builtin/merge.c:1015 builtin/merge.c:1028
 #, c-format
 msgid "Could not write to '%s'"
 msgstr "Konnte nicht nach '%s' schreiben."
 
-#: sequencer.c:146
+#: sequencer.c:229
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'"
@@ -811,7 +982,7 @@ msgstr ""
 "nach Auflösung der Konflikte, markieren Sie die korrigierten Pfade\n"
 "mit 'git add <Pfade>' oder 'git rm <Pfade>'"
 
-#: sequencer.c:149
+#: sequencer.c:232
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'\n"
@@ -821,652 +992,742 @@ msgstr ""
 "mit 'git add <Pfade>' oder 'git rm <Pfade>'und tragen Sie das Ergebnis mit\n"
 "'git commit' ein"
 
-#: sequencer.c:162 sequencer.c:774 sequencer.c:857
+#: sequencer.c:245 sequencer.c:869 sequencer.c:952
 #, c-format
 msgid "Could not write to %s"
 msgstr "Konnte nicht nach %s schreiben"
 
-#: sequencer.c:165
+#: sequencer.c:248
 #, c-format
 msgid "Error wrapping up %s"
 msgstr "Fehler bei Nachbereitung von %s"
 
-#: sequencer.c:180
+#: sequencer.c:263
 msgid "Your local changes would be overwritten by cherry-pick."
 msgstr ""
 "Ihre lokalen Änderungen würden von \"cherry-pick\" überschrieben werden."
 
-#: sequencer.c:182
+#: sequencer.c:265
 msgid "Your local changes would be overwritten by revert."
 msgstr "Ihre lokalen Änderungen würden von \"revert\" überschrieben werden."
 
-#: sequencer.c:185
+#: sequencer.c:268
 msgid "Commit your changes or stash them to proceed."
 msgstr ""
 "Tragen Sie Ihre Änderungen ein oder benutzen Sie \"stash\" um fortzufahren."
 
 #. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:236
+#: sequencer.c:324
 #, c-format
 msgid "%s: Unable to write new index file"
-msgstr "%s: Konnte neue Bereitstellungsdatei nicht schreiben"
+msgstr "%s: Konnte neue Staging-Area-Datei nicht schreiben"
 
-#: sequencer.c:267
+#: sequencer.c:355
 msgid "Could not resolve HEAD commit\n"
-msgstr "Konnte Version der Zweigspitze (HEAD) nicht auflösen\n"
+msgstr "Konnte Commit von HEAD nicht auflösen\n"
 
-#: sequencer.c:288
+#: sequencer.c:377
 msgid "Unable to update cache tree\n"
-msgstr "Konnte zwischengespeicherten Baum nicht aktualisieren\n"
+msgstr "Konnte Cache-Verzeichnis nicht aktualisieren\n"
 
-#: sequencer.c:333
+#: sequencer.c:422
 #, c-format
 msgid "Could not parse commit %s\n"
-msgstr "Konnte Version %s nicht parsen\n"
+msgstr "Konnte Commit %s nicht parsen\n"
 
-#: sequencer.c:338
+#: sequencer.c:427
 #, c-format
 msgid "Could not parse parent commit %s\n"
-msgstr "Konnte Elternversion %s nicht parsen\n"
+msgstr "Konnte Eltern-Commit %s nicht parsen\n"
 
-#: sequencer.c:404
+#: sequencer.c:493
 msgid "Your index file is unmerged."
-msgstr "Ihre Bereitstellungsdatei ist nicht zusammengeführt."
+msgstr "Ihre Staging-Area-Datei ist nicht zusammengeführt."
 
-#: sequencer.c:423
+#: sequencer.c:512
 #, c-format
 msgid "Commit %s is a merge but no -m option was given."
-msgstr ""
-"Version %s ist eine Zusammenführung, aber die Option -m wurde nicht "
-"angegeben."
+msgstr "Commit %s ist ein Merge, aber die Option -m wurde nicht angegeben."
 
-#: sequencer.c:431
+#: sequencer.c:520
 #, c-format
 msgid "Commit %s does not have parent %d"
-msgstr "Version %s hat keinen Elternteil %d"
+msgstr "Commit %s hat keinen Elternteil %d"
 
-#: sequencer.c:435
+#: sequencer.c:524
 #, c-format
 msgid "Mainline was specified but commit %s is not a merge."
-msgstr ""
-"Hauptlinie wurde spezifiziert, aber Version %s ist keine Zusammenführung."
+msgstr "Hauptlinie wurde spezifiziert, aber Commit %s ist kein Merge."
 
 #. TRANSLATORS: The first %s will be "revert" or
 #. "cherry-pick", the second %s a SHA1
-#: sequencer.c:448
+#: sequencer.c:537
 #, c-format
 msgid "%s: cannot parse parent commit %s"
-msgstr "%s: kann Elternversion %s nicht parsen"
+msgstr "%s: kann Eltern-Commit %s nicht parsen"
 
-#: sequencer.c:452
+#: sequencer.c:541
 #, c-format
 msgid "Cannot get commit message for %s"
-msgstr "Kann keine Versionsbeschreibung für %s bekommen"
+msgstr "Kann keine Commit-Beschreibung für %s bekommen"
 
-#: sequencer.c:536
+#: sequencer.c:627
 #, c-format
 msgid "could not revert %s... %s"
-msgstr "Konnte %s nicht zurücksetzen... %s"
+msgstr "Konnte \"revert\" nicht auf %s ausführen... %s"
 
-#: sequencer.c:537
+#: sequencer.c:628
 #, c-format
 msgid "could not apply %s... %s"
 msgstr "Konnte %s nicht anwenden... %s"
 
-#: sequencer.c:569
+#: sequencer.c:664
 msgid "empty commit set passed"
-msgstr "leere Menge von Versionen übergeben"
+msgstr "leere Menge von Commits übergeben"
 
-#: sequencer.c:577
+#: sequencer.c:672
 #, c-format
 msgid "git %s: failed to read the index"
-msgstr "git %s: Fehler beim Lesen der Bereitstellung"
+msgstr "git %s: Fehler beim Lesen der Staging-Area"
 
-#: sequencer.c:582
+#: sequencer.c:677
 #, c-format
 msgid "git %s: failed to refresh the index"
-msgstr "git %s: Fehler beim Aktualisieren der Bereitstellung"
+msgstr "git %s: Fehler beim Aktualisieren der Staging-Area"
 
-#: sequencer.c:640
+#: sequencer.c:735
 #, c-format
 msgid "Cannot %s during a %s"
 msgstr "Kann %s nicht während eines %s durchführen"
 
-#: sequencer.c:662
+#: sequencer.c:757
 #, c-format
 msgid "Could not parse line %d."
 msgstr "Konnte Zeile %d nicht parsen."
 
-#: sequencer.c:667
+#: sequencer.c:762
 msgid "No commits parsed."
-msgstr "Keine Versionen geparst."
+msgstr "Keine Commits geparst."
 
-#: sequencer.c:680
+#: sequencer.c:775
 #, c-format
 msgid "Could not open %s"
 msgstr "Konnte %s nicht öffnen"
 
-#: sequencer.c:684
+#: sequencer.c:779
 #, c-format
 msgid "Could not read %s."
 msgstr "Konnte %s nicht lesen."
 
-#: sequencer.c:691
+#: sequencer.c:786
 #, c-format
 msgid "Unusable instruction sheet: %s"
 msgstr "Unbenutzbares Instruktionsblatt: %s"
 
-#: sequencer.c:719
+#: sequencer.c:814
 #, c-format
 msgid "Invalid key: %s"
 msgstr "Ungültiger Schlüssel: %s"
 
-#: sequencer.c:722
+#: sequencer.c:817
 #, c-format
 msgid "Invalid value for %s: %s"
 msgstr "Ungültiger Wert für %s: %s"
 
-#: sequencer.c:734
+#: sequencer.c:829
 #, c-format
 msgid "Malformed options sheet: %s"
 msgstr "Fehlerhaftes Optionsblatt: %s"
 
-#: sequencer.c:755
+#: sequencer.c:850
 msgid "a cherry-pick or revert is already in progress"
 msgstr "\"cherry-pick\" oder \"revert\" ist bereits im Gang"
 
-#: sequencer.c:756
+#: sequencer.c:851
 msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
 msgstr "versuchen Sie \"git cherry-pick (--continue | --quit | --abort)\""
 
-#: sequencer.c:760
+#: sequencer.c:855
 #, c-format
 msgid "Could not create sequencer directory %s"
 msgstr "Konnte \"sequencer\"-Verzeichnis %s nicht erstellen"
 
-#: sequencer.c:776 sequencer.c:861
+#: sequencer.c:871 sequencer.c:956
 #, c-format
 msgid "Error wrapping up %s."
 msgstr "Fehler beim Einpacken von %s."
 
-#: sequencer.c:795 sequencer.c:929
+#: sequencer.c:890 sequencer.c:1024
 msgid "no cherry-pick or revert in progress"
 msgstr "kein \"cherry-pick\" oder \"revert\" im Gang"
 
-#: sequencer.c:797
+#: sequencer.c:892
 msgid "cannot resolve HEAD"
-msgstr "kann Zweigspitze (HEAD) nicht auflösen"
+msgstr "kann HEAD nicht auflösen"
 
-#: sequencer.c:799
+#: sequencer.c:894
 msgid "cannot abort from a branch yet to be born"
-msgstr "kann nicht abbrechen: bin auf einem Zweig, der noch geboren wird"
+msgstr "kann nicht abbrechen: bin auf einem Branch, der noch geboren wird"
 
-#: sequencer.c:821 builtin/apply.c:4056
+#: sequencer.c:916 builtin/apply.c:4061
 #, c-format
 msgid "cannot open %s: %s"
 msgstr "Kann %s nicht öffnen: %s"
 
-#: sequencer.c:824
+#: sequencer.c:919
 #, c-format
 msgid "cannot read %s: %s"
 msgstr "Kann %s nicht lesen: %s"
 
-#: sequencer.c:825
+#: sequencer.c:920
 msgid "unexpected end of file"
 msgstr "Unerwartetes Dateiende"
 
-#: sequencer.c:831
+#: sequencer.c:926
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
-msgstr ""
-"gespeicherte \"pre-cherry-pick\" Datei der Zweigspitze (HEAD) '%s' ist "
-"beschädigt"
+msgstr "gespeicherte \"pre-cherry-pick\" HEAD Datei '%s' ist beschädigt"
 
-#: sequencer.c:854
+#: sequencer.c:949
 #, c-format
 msgid "Could not format %s."
 msgstr "Konnte %s nicht formatieren."
 
-#: sequencer.c:1016
+#: sequencer.c:1092
+#, c-format
+msgid "%s: can't cherry-pick a %s"
+msgstr "%s: %s kann nicht in \"cherry-pick\" benutzt werden"
+
+#: sequencer.c:1095
+#, c-format
+msgid "%s: bad revision"
+msgstr "%s: ungültiger Commit"
+
+#: sequencer.c:1129
 msgid "Can't revert as initial commit"
-msgstr "Kann nicht zu initialer Version zurücksetzen."
+msgstr "Kann nicht als allerersten Commit einen Revert ausführen."
 
-#: sequencer.c:1017
+#: sequencer.c:1130
 msgid "Can't cherry-pick into empty head"
-msgstr "Kann \"cherry-pick\" nicht in einem leerem Kopf ausführen."
+msgstr "Kann nicht als allerersten Commit einen Cherry-Pick ausführen."
 
-#: sha1_name.c:1044
+#: sha1_name.c:440
+msgid ""
+"Git normally never creates a ref that ends with 40 hex characters\n"
+"because it will be ignored when you just specify 40-hex. These refs\n"
+"may be created by mistake. For example,\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+"examine these refs and maybe delete them. Turn this message off by\n"
+"running \"git config advice.objectNameWarning false\""
+msgstr ""
+"Git erzeugt normalerweise keine Referenzen die mit\n"
+"40 Hex-Zeichen enden, da diese ignoriert werden wenn\n"
+"Sie diese angeben. Diese Referenzen könnten aus Versehen\n"
+"erzeugt worden sein. Zum Beispiel,\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"wobei \"$br\" leer ist und eine 40-Hex-Referenz erzeugt\n"
+"wurde. Bitte prüfen Sie diese Referenzen und löschen\n"
+"Sie sie gegebenenfalls. Unterdrücken Sie diese Meldung\n"
+"indem Sie \"git config advice.objectNameWarning false\"\n"
+"ausführen."
+
+#: sha1_name.c:1097
 msgid "HEAD does not point to a branch"
-msgstr "Zweigspitze (HEAD) zeigt auf keinen Zweig"
+msgstr "HEAD zeigt auf keinen Branch"
 
-#: sha1_name.c:1047
+#: sha1_name.c:1100
 #, c-format
 msgid "No such branch: '%s'"
-msgstr "Kein solcher Zweig '%s'"
+msgstr "Kein solcher Branch '%s'"
 
-#: sha1_name.c:1049
+#: sha1_name.c:1102
 #, c-format
 msgid "No upstream configured for branch '%s'"
-msgstr "Kein entferntes Projektarchiv für Zweig '%s' konfiguriert."
+msgstr "Kein Upstream-Branch für Branch '%s' konfiguriert."
 
-#: sha1_name.c:1052
+#: sha1_name.c:1106
 #, c-format
 msgid "Upstream branch '%s' not stored as a remote-tracking branch"
-msgstr ""
-"Zweig '%s' des entfernten Projektarchivs ist kein gefolgter Übernahmezweig"
+msgstr "Upstream-Branch '%s' ist nicht als Remote-Tracking-Branch gespeichert"
 
 #: wrapper.c:408
 #, c-format
 msgid "unable to access '%s': %s"
 msgstr "konnte nicht auf '%s' zugreifen: %s"
 
-#: wrapper.c:423
+#: wrapper.c:429
 #, c-format
 msgid "unable to access '%s'"
 msgstr "konnte nicht auf '%s' zugreifen"
 
-#: wrapper.c:434
+#: wrapper.c:440
 #, c-format
 msgid "unable to look up current user in the passwd file: %s"
 msgstr "konnte aktuellen Benutzer nicht in Passwort-Datei finden: %s"
 
-#: wrapper.c:435
+#: wrapper.c:441
 msgid "no such user"
 msgstr "kein solcher Benutzer"
 
-#: wt-status.c:140
+#: wt-status.c:141
 msgid "Unmerged paths:"
 msgstr "Nicht zusammengeführte Pfade:"
 
-#: wt-status.c:167 wt-status.c:194
+#: wt-status.c:168 wt-status.c:195
 #, c-format
 msgid "  (use \"git reset %s <file>...\" to unstage)"
 msgstr ""
-"  (benutzen Sie \"git reset %s <Datei>...\" zum Herausnehmen aus der "
-"Bereitstellung)"
+"  (benutzen Sie \"git reset %s <Datei>...\" zum Entfernen aus der Staging-"
+"Area)"
 
-#: wt-status.c:169 wt-status.c:196
+#: wt-status.c:170 wt-status.c:197
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
 msgstr ""
-"  (benutzen Sie \"git rm --cached <Datei>...\" zum Herausnehmen aus der "
-"Bereitstellung)"
+"  (benutzen Sie \"git rm --cached <Datei>...\" zum Entfernen aus der Staging-"
+"Area)"
 
-#: wt-status.c:173
+#: wt-status.c:174
 msgid "  (use \"git add <file>...\" to mark resolution)"
 msgstr ""
 "  (benutzen Sie \"git add/rm <Datei>...\" um die Auflösung zu markieren)"
 
-#: wt-status.c:175 wt-status.c:179
+#: wt-status.c:176 wt-status.c:180
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
 msgstr ""
 "  (benutzen Sie \"git add/rm <Datei>...\" um die Auflösung entsprechend zu "
 "markieren)"
 
-#: wt-status.c:177
+#: wt-status.c:178
 msgid "  (use \"git rm <file>...\" to mark resolution)"
 msgstr ""
 "  (benutzen Sie \"git add/rm <Datei>...\" um die Auflösung zu markieren)"
 
-#: wt-status.c:188
+#: wt-status.c:189
 msgid "Changes to be committed:"
-msgstr "zum Eintragen bereitgestellte Änderungen:"
+msgstr "zum Commit vorgemerkte Änderungen:"
 
-#: wt-status.c:206
+#: wt-status.c:207
 msgid "Changes not staged for commit:"
-msgstr "Änderungen, die nicht zum Eintragen bereitgestellt sind:"
+msgstr "Änderungen, die nicht zum Commit vorgemerkt sind:"
 
-#: wt-status.c:210
+#: wt-status.c:211
 msgid "  (use \"git add <file>...\" to update what will be committed)"
-msgstr "  (benutzen Sie \"git add <Datei>...\" zum Bereitstellen)"
+msgstr ""
+"  (benutzen Sie \"git add <Datei>...\" um die Änderungen zum Commit "
+"vorzumerken)"
 
-#: wt-status.c:212
+#: wt-status.c:213
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
-msgstr "  (benutzen Sie \"git add/rm <Datei>...\" zum Bereitstellen)"
+msgstr ""
+"  (benutzen Sie \"git add/rm <Datei>...\" um die Änderungen zum Commit "
+"vorzumerken)"
 
-#: wt-status.c:213
+#: wt-status.c:214
 msgid ""
 "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
 msgstr ""
 "  (benutzen Sie \"git checkout -- <Datei>...\" um die Änderungen im "
 "Arbeitsverzeichnis zu verwerfen)"
 
-#: wt-status.c:215
+#: wt-status.c:216
 msgid "  (commit or discard the untracked or modified content in submodules)"
 msgstr ""
-"  (tragen Sie ein oder verwerfen Sie den unbeobachteten oder geänderten "
-"Inhalt in den Unterprojekten)"
+"  (committen oder verwerfen Sie den unbeobachteten oder geänderten Inhalt in "
+"den Submodulen)"
 
-#: wt-status.c:227
+#: wt-status.c:228
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
-msgstr "  (benutzen Sie \"git %s <Datei>...\" zum Einfügen in die Eintragung)"
+msgstr ""
+"  (benutzen Sie \"git %s <Datei>...\" um die Änderungen zum Commit "
+"vorzumerken)"
 
-#: wt-status.c:244
+#: wt-status.c:245
 msgid "bug"
 msgstr "Fehler"
 
-#: wt-status.c:249
+#: wt-status.c:250
 msgid "both deleted:"
 msgstr "beide gelöscht:"
 
-#: wt-status.c:250
+#: wt-status.c:251
 msgid "added by us:"
 msgstr "von uns hinzugefügt:"
 
-#: wt-status.c:251
+#: wt-status.c:252
 msgid "deleted by them:"
 msgstr "von denen gelöscht:"
 
-#: wt-status.c:252
+#: wt-status.c:253
 msgid "added by them:"
 msgstr "von denen hinzugefügt:"
 
-#: wt-status.c:253
+#: wt-status.c:254
 msgid "deleted by us:"
 msgstr "von uns gelöscht:"
 
-#: wt-status.c:254
+#: wt-status.c:255
 msgid "both added:"
 msgstr "von beiden hinzugefügt:"
 
-#: wt-status.c:255
+#: wt-status.c:256
 msgid "both modified:"
 msgstr "von beiden geändert:"
 
-#: wt-status.c:285
+#: wt-status.c:286
 msgid "new commits, "
-msgstr "neue Versionen, "
+msgstr "neue Commits, "
 
-#: wt-status.c:287
+#: wt-status.c:288
 msgid "modified content, "
 msgstr "geänderter Inhalt, "
 
-#: wt-status.c:289
+#: wt-status.c:290
 msgid "untracked content, "
 msgstr "unbeobachteter Inhalt, "
 
-#: wt-status.c:303
+#: wt-status.c:307
 #, c-format
 msgid "new file:   %s"
 msgstr "neue Datei:   %s"
 
-#: wt-status.c:306
+#: wt-status.c:310
 #, c-format
 msgid "copied:     %s -> %s"
 msgstr "kopiert:     %s -> %s"
 
-#: wt-status.c:309
+#: wt-status.c:313
 #, c-format
 msgid "deleted:    %s"
 msgstr "gelöscht:    %s"
 
-#: wt-status.c:312
+#: wt-status.c:316
 #, c-format
 msgid "modified:   %s"
 msgstr "geändert:   %s"
 
-#: wt-status.c:315
+#: wt-status.c:319
 #, c-format
 msgid "renamed:    %s -> %s"
 msgstr "umbenannt:    %s -> %s"
 
-#: wt-status.c:318
+#: wt-status.c:322
 #, c-format
 msgid "typechange: %s"
 msgstr "Typänderung: %s"
 
-#: wt-status.c:321
+#: wt-status.c:325
 #, c-format
 msgid "unknown:    %s"
 msgstr "unbekannt:    %s"
 
-#: wt-status.c:324
+#: wt-status.c:328
 #, c-format
 msgid "unmerged:   %s"
 msgstr "nicht zusammengeführt:   %s"
 
-#: wt-status.c:327
+#: wt-status.c:331
 #, c-format
 msgid "bug: unhandled diff status %c"
 msgstr "Fehler: unbehandelter Differenz-Status %c"
 
-#: wt-status.c:789
+#: wt-status.c:803
 msgid "You have unmerged paths."
 msgstr "Sie haben nicht zusammengeführte Pfade."
 
-#: wt-status.c:792 wt-status.c:944
+#: wt-status.c:806
 msgid "  (fix conflicts and run \"git commit\")"
 msgstr " (beheben Sie die Konflikte und führen Sie \"git commit\" aus)"
 
-#: wt-status.c:795
+#: wt-status.c:809
 msgid "All conflicts fixed but you are still merging."
-msgstr ""
-"Alle Konflikte sind behoben, aber Sie sind immer noch beim Zusammenführen."
+msgstr "Alle Konflikte sind behoben, aber Sie sind immer noch beim Merge."
 
-#: wt-status.c:798
+#: wt-status.c:812
 msgid "  (use \"git commit\" to conclude merge)"
-msgstr "  (benutzen Sie \"git commit\" um die Zusammenführung abzuschließen)"
+msgstr "  (benutzen Sie \"git commit\" um den Merge abzuschließen)"
 
-#: wt-status.c:808
+#: wt-status.c:822
 msgid "You are in the middle of an am session."
 msgstr "Eine \"am\"-Sitzung ist im Gange."
 
-#: wt-status.c:811
+#: wt-status.c:825
 msgid "The current patch is empty."
 msgstr "Der aktuelle Patch ist leer."
 
-#: wt-status.c:815
-msgid "  (fix conflicts and then run \"git am --resolved\")"
+#: wt-status.c:829
+msgid "  (fix conflicts and then run \"git am --continue\")"
 msgstr ""
-"  (beheben Sie die Konflikte und führen Sie dann \"git am --resolved\" aus)"
+"  (beheben Sie die Konflikte und führen Sie dann \"git am --continue\" aus)"
 
-#: wt-status.c:817
+#: wt-status.c:831
 msgid "  (use \"git am --skip\" to skip this patch)"
-msgstr " (benutzen Sie \"git am --skip\" um diesen Patch auszulassen)"
+msgstr "  (benutzen Sie \"git am --skip\" um diesen Patch auszulassen)"
 
-#: wt-status.c:819
+#: wt-status.c:833
 msgid "  (use \"git am --abort\" to restore the original branch)"
 msgstr ""
-"  (benutzen Sie \"git am --abort\" um den ursprünglichen Zweig "
+"  (benutzen Sie \"git am --abort\" um den ursprünglichen Branch "
 "wiederherzustellen)"
 
-#: wt-status.c:879 wt-status.c:896
+#: wt-status.c:893 wt-status.c:910
 #, c-format
 msgid "You are currently rebasing branch '%s' on '%s'."
-msgstr "Sie sind gerade beim Neuaufbau von Zweig '%s' auf '%s'."
+msgstr "Sie sind gerade beim Rebase von Branch '%s' auf '%s'."
 
-#: wt-status.c:884 wt-status.c:901
+#: wt-status.c:898 wt-status.c:915
 msgid "You are currently rebasing."
-msgstr "Sie sind gerade beim Neuaufbau."
+msgstr "Sie sind gerade beim Rebase."
 
-#: wt-status.c:887
+#: wt-status.c:901
 msgid "  (fix conflicts and then run \"git rebase --continue\")"
 msgstr ""
 "  (beheben Sie die Konflikte und führen Sie dann \"git rebase --continue\" "
 "aus)"
 
-#: wt-status.c:889
+#: wt-status.c:903
 msgid "  (use \"git rebase --skip\" to skip this patch)"
 msgstr "  (benutzen Sie \"git rebase --skip\" um diesen Patch auszulassen)"
 
-#: wt-status.c:891
+#: wt-status.c:905
 msgid "  (use \"git rebase --abort\" to check out the original branch)"
 msgstr ""
-"  (benutzen Sie \"git rebase --abort\" um den ursprünglichen Zweig "
+"  (benutzen Sie \"git rebase --abort\" um den ursprünglichen Branch "
 "auszuchecken)"
 
-#: wt-status.c:904
+#: wt-status.c:918
 msgid "  (all conflicts fixed: run \"git rebase --continue\")"
 msgstr "  (alle Konflikte behoben: führen Sie \"git rebase --continue\" aus)"
 
-#: wt-status.c:908
+#: wt-status.c:922
 #, c-format
 msgid ""
 "You are currently splitting a commit while rebasing branch '%s' on '%s'."
 msgstr ""
-"Sie teilen gerade eine Version auf, während ein Neuaufbau von Zweig '%s' auf "
+"Sie teilen gerade einen Commit auf, während ein Rebase von Branch '%s' auf "
 "'%s' im Gange ist."
 
-#: wt-status.c:913
+#: wt-status.c:927
 msgid "You are currently splitting a commit during a rebase."
-msgstr "Sie teilen gerade eine Version während eines Neuaufbaus auf."
+msgstr "Sie teilen gerade einen Commit während eines Rebase auf."
 
-#: wt-status.c:916
+#: wt-status.c:930
 msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
 msgstr ""
-"  (Sobald Ihr Arbeitsverzeichnis sauber ist, führen Sie \"git rebase --"
+"  (Sobald Ihr Arbeitsverzeichnis unverändert ist, führen Sie \"git rebase --"
 "continue\" aus)"
 
-#: wt-status.c:920
+#: wt-status.c:934
 #, c-format
 msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
 msgstr ""
-"Sie editieren gerade eine Version während eines Neuaufbaus von Zweig '%s' "
-"auf '%s'."
+"Sie editieren gerade einen Commit während eines Rebase von Branch '%s' auf "
+"'%s'."
 
-#: wt-status.c:925
+#: wt-status.c:939
 msgid "You are currently editing a commit during a rebase."
-msgstr "Sie editieren gerade eine Version während eines Neuaufbaus."
+msgstr "Sie editieren gerade einen Commit während eines Rebase."
 
-#: wt-status.c:928
+#: wt-status.c:942
 msgid "  (use \"git commit --amend\" to amend the current commit)"
 msgstr ""
-"  (benutzen Sie \"git commit --amend\" um die aktuelle Version nachzubessern)"
+"  (benutzen Sie \"git commit --amend\" um den aktuellen Commit nachzubessern)"
 
-#: wt-status.c:930
+#: wt-status.c:944
 msgid ""
 "  (use \"git rebase --continue\" once you are satisfied with your changes)"
 msgstr ""
 "  (benutzen Sie \"git rebase --continue\" sobald Ihre Änderungen "
 "abgeschlossen sind)"
 
-#: wt-status.c:940
+#: wt-status.c:954
 msgid "You are currently cherry-picking."
 msgstr "Sie führen gerade \"cherry-pick\" aus."
 
-#: wt-status.c:947
-msgid "  (all conflicts fixed: run \"git commit\")"
-msgstr "  (alle Konflikte behoben: führen Sie \"git commit\" aus)"
-
 #: wt-status.c:958
+msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
+msgstr ""
+"  (beheben Sie die Konflikte und führen Sie dann \"git cherry-pick --continue"
+"\" aus)"
+
+#: wt-status.c:961
+msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
+msgstr ""
+"  (alle Konflikte behoben: führen Sie \"git cherry-pick --continue\" aus)"
+
+#: wt-status.c:963
+msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
+msgstr ""
+"  (benutzen Sie \"git cherry-pick --abort\" um die Cherry-Pick-Operation "
+"abzubrechen)"
+
+#: wt-status.c:972
 #, c-format
-msgid "You are currently bisecting branch '%s'."
-msgstr "Sie sind gerade bei einer binären Suche in Zweig '%s'."
+msgid "You are currently reverting commit %s."
+msgstr "Sie sind gerade an einem Revert von Commit '%s'."
+
+#: wt-status.c:977
+msgid "  (fix conflicts and run \"git revert --continue\")"
+msgstr ""
+"  (beheben Sie die Konflikte und führen Sie dann \"git revert --continue\" "
+"aus)"
 
-#: wt-status.c:962
+#: wt-status.c:980
+msgid "  (all conflicts fixed: run \"git revert --continue\")"
+msgstr "  (alle Konflikte behoben: führen Sie \"git revert --continue\" aus)"
+
+#: wt-status.c:982
+msgid "  (use \"git revert --abort\" to cancel the revert operation)"
+msgstr ""
+"  (benutzen Sie \"git revert --abort\" um die Revert-Operation abzubrechen)"
+
+#: wt-status.c:993
+#, c-format
+msgid "You are currently bisecting, started from branch '%s'."
+msgstr "Sie sind gerade bei einer binären Suche, gestartet von Branch '%s'."
+
+#: wt-status.c:997
 msgid "You are currently bisecting."
 msgstr "Sie sind gerade bei einer binären Suche."
 
-#: wt-status.c:965
+#: wt-status.c:1000
 msgid "  (use \"git bisect reset\" to get back to the original branch)"
 msgstr ""
-"  (benutzen Sie \"git bisect reset\" um zum ursprünglichen Zweig "
+"  (benutzen Sie \"git bisect reset\" um zum ursprünglichen Branch "
 "zurückzukehren)"
 
-#: wt-status.c:1064
+#: wt-status.c:1173
 msgid "On branch "
-msgstr "Auf Zweig "
+msgstr "Auf Branch "
+
+#: wt-status.c:1180
+msgid "rebase in progress; onto "
+msgstr "Rebase im Gange; auf "
+
+#: wt-status.c:1187
+msgid "HEAD detached at "
+msgstr "HEAD losgelöst bei "
 
-#: wt-status.c:1071
+#: wt-status.c:1189
+msgid "HEAD detached from "
+msgstr "HEAD losgelöst von "
+
+#: wt-status.c:1192
 msgid "Not currently on any branch."
-msgstr "Im Moment auf keinem Zweig."
+msgstr "Im Moment auf keinem Branch."
 
-#: wt-status.c:1083
+#: wt-status.c:1209
 msgid "Initial commit"
-msgstr "Initiale Version"
+msgstr "Initialer Commit"
 
-#: wt-status.c:1097
+#: wt-status.c:1223
 msgid "Untracked files"
 msgstr "Unbeobachtete Dateien"
 
-#: wt-status.c:1099
+#: wt-status.c:1225
 msgid "Ignored files"
 msgstr "Ignorierte Dateien"
 
-#: wt-status.c:1101
+#: wt-status.c:1229
+#, c-format
+msgid ""
+"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
+"may speed it up, but you have to be careful not to forget to add\n"
+"new files yourself (see 'git help status')."
+msgstr ""
+"Es dauerte %.2f Sekunden die unbeobachteten Dateien zu bestimmen.\n"
+"'status -uno' könnte das beschleunigen, aber Sie müssen darauf achten,\n"
+"neue Dateien selbstständig hinzuzufügen (siehe 'git help status')."
+
+#: wt-status.c:1235
 #, c-format
 msgid "Untracked files not listed%s"
 msgstr "Unbeobachtete Dateien nicht aufgelistet%s"
 
-#: wt-status.c:1103
+#: wt-status.c:1237
 msgid " (use -u option to show untracked files)"
 msgstr " (benutzen Sie die Option -u um unbeobachteten Dateien anzuzeigen)"
 
-#: wt-status.c:1109
+#: wt-status.c:1243
 msgid "No changes"
 msgstr "Keine Änderungen"
 
-#: wt-status.c:1114
+#: wt-status.c:1248
 #, c-format
 msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
 msgstr ""
-"keine Änderungen zum Eintragen hinzugefügt (benutzen Sie \"git add\" und/"
-"oder \"git commit -a\")\n"
+"keine Änderungen zum Commit vorgemerkt (benutzen Sie \"git add\" und/oder "
+"\"git commit -a\")\n"
 
-#: wt-status.c:1117
+#: wt-status.c:1251
 #, c-format
 msgid "no changes added to commit\n"
-msgstr "keine Änderungen zum Eintragen hinzugefügt\n"
+msgstr "keine Änderungen zum Commit vorgemerkt\n"
 
-#: wt-status.c:1120
+#: wt-status.c:1254
 #, c-format
 msgid ""
 "nothing added to commit but untracked files present (use \"git add\" to "
 "track)\n"
 msgstr ""
-"nichts zum Eintragen hinzugefügt, aber es gibt unbeobachtete Dateien "
-"(benutzen Sie \"git add\" zum Beobachten)\n"
+"nichts zum Commit vorgemerkt, aber es gibt unbeobachtete Dateien (benutzen "
+"Sie \"git add\" zum Beobachten)\n"
 
-#: wt-status.c:1123
+#: wt-status.c:1257
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
-msgstr "nichts zum Eintragen hinzugefügt, aber es gibt unbeobachtete Dateien\n"
+msgstr "nichts zum Commit vorgemerkt, aber es gibt unbeobachtete Dateien\n"
 
-#: wt-status.c:1126
+#: wt-status.c:1260
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
 msgstr ""
-"nichts einzutragen (Erstellen/Kopieren Sie Dateien und benutzen Sie \"git add"
-"\" zum Beobachten)\n"
+"nichts zu committen (Erstellen/Kopieren Sie Dateien und benutzen Sie \"git "
+"add\" zum Beobachten)\n"
 
-#: wt-status.c:1129 wt-status.c:1134
+#: wt-status.c:1263 wt-status.c:1268
 #, c-format
 msgid "nothing to commit\n"
-msgstr "nichts einzutragen\n"
+msgstr "nichts zu committen\n"
 
-#: wt-status.c:1132
+#: wt-status.c:1266
 #, c-format
 msgid "nothing to commit (use -u to show untracked files)\n"
 msgstr ""
-"nichts einzutragen (benutzen Sie die Option -u, um unbeobachtete Dateien "
+"nichts zu committen (benutzen Sie die Option -u, um unbeobachtete Dateien "
 "anzuzeigen)\n"
 
-#: wt-status.c:1136
+#: wt-status.c:1270
 #, c-format
 msgid "nothing to commit, working directory clean\n"
-msgstr "nichts einzutragen, Arbeitsverzeichnis sauber\n"
+msgstr "nichts zu committen, Arbeitsverzeichnis unverändert\n"
 
-#: wt-status.c:1244
+#: wt-status.c:1378
 msgid "HEAD (no branch)"
-msgstr "HEAD (kein Zweig)"
+msgstr "HEAD (kein Branch)"
 
-#: wt-status.c:1250
+#: wt-status.c:1384
 msgid "Initial commit on "
-msgstr "Initiale Version auf "
+msgstr "Initialer Commit auf "
 
-#: wt-status.c:1265
+#: wt-status.c:1399
 msgid "behind "
 msgstr "hinterher "
 
-#: wt-status.c:1268 wt-status.c:1271
+#: wt-status.c:1402 wt-status.c:1405
 msgid "ahead "
 msgstr "voraus "
 
-#: wt-status.c:1273
+#: wt-status.c:1407
 msgid ", behind "
 msgstr ", hinterher "
 
-#: compat/precompose_utf8.c:58 builtin/clone.c:341
+#: compat/precompose_utf8.c:58 builtin/clone.c:351
 #, c-format
 msgid "failed to unlink '%s'"
 msgstr "Konnte '%s' nicht entfernen."
@@ -1475,203 +1736,235 @@ msgstr "Konnte '%s' nicht entfernen."
 msgid "git add [options] [--] <pathspec>..."
 msgstr "git add [Optionen] [--] [<Pfadspezifikation>...]"
 
-#: builtin/add.c:63
+#.
+#. * To be consistent with "git add -p" and most Git
+#. * commands, we should default to being tree-wide, but
+#. * this is not the original behavior and can't be
+#. * changed until users trained themselves not to type
+#. * "git add -u" or "git add -A". For now, we warn and
+#. * keep the old behavior. Later, the behavior can be changed
+#. * to tree-wide, keeping the warning for a while, and
+#. * eventually we can drop the warning.
+#.
+#: builtin/add.c:58
+#, c-format
+msgid ""
+"The behavior of 'git add %s (or %s)' with no path argument from a\n"
+"subdirectory of the tree will change in Git 2.0 and should not be used "
+"anymore.\n"
+"To add content for the whole tree, run:\n"
+"\n"
+"  git add %s :/\n"
+"  (or git add %s :/)\n"
+"\n"
+"To restrict the command to the current directory, run:\n"
+"\n"
+"  git add %s .\n"
+"  (or git add %s .)\n"
+"\n"
+"With the current Git version, the command is restricted to the current "
+"directory.\n"
+msgstr ""
+"Das Verhalten von 'git add %s (oder %s)' ohne ein Pfad-Argument von\n"
+"einem Unterverzeichnis aus wird in Git 2.0 geändert und sollte nicht\n"
+"mehr verwendet werden.\n"
+"Um Dateien des gesamten Projektverzeichnisses hinzuzufügen, führen Sie aus:\n"
+"\n"
+"  git add %s :/\n"
+"  (oder git add %s :/)\n"
+"\n"
+"Zur Einschränkung auf das aktuelle Verzeichnis führen Sie aus:\n"
+"\n"
+"  git add %s .\n"
+"  (oder git add %s .)\n"
+"\n"
+"Mit der aktuellen Version von Git ist das Kommando auf das aktuelle\n"
+"Verzeichnis beschränkt.\n"
+
+#: builtin/add.c:100
+#, c-format
+msgid ""
+"You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
+"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
+"Paths like '%s' that are\n"
+"removed from your working tree are ignored with this version of Git.\n"
+"\n"
+"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
+"  ignores paths you removed from your working tree.\n"
+"\n"
+"* 'git add --all <pathspec>' will let you also record the removals.\n"
+"\n"
+"Run 'git status' to check the paths you removed from your working tree.\n"
+msgstr ""
+"Sie haben 'git add' weder mit '-A (--all)' noch mit '--ignore-removal'\n"
+"ausgeführt. Das Verhalten des Kommandos ändert sich in Git 2.0 durch\n"
+"Berücksichtigung der gelöschten Pfade.\n"
+"Pfade wie '%s', die im Arbeitsverzeichnis gelöscht wurden, werden in\n"
+"dieser Version von Git ignoriert.\n"
+"\n"
+"* 'git add --ignore-removal <Pfadspezifikation>', was der aktuelle\n"
+"  Standardwert ist, ignoriert gelöschte Pfade im Arbeitsverzeichnis.\n"
+"* 'git add --all <Pfadspezifikation>' berücksichtigt ebenfalls gelöschte\n"
+"  Pfade.\n"
+"Führen Sie 'git status' aus, um die gelöschten Pfade zu überprüfen.\n"
+
+#: builtin/add.c:144
 #, c-format
 msgid "unexpected diff status %c"
 msgstr "unerwarteter Differenz-Status %c"
 
-#: builtin/add.c:68 builtin/commit.c:231
+#: builtin/add.c:149 builtin/commit.c:252
 msgid "updating files failed"
 msgstr "Aktualisierung der Dateien fehlgeschlagen"
 
-#: builtin/add.c:78
+#: builtin/add.c:163
 #, c-format
 msgid "remove '%s'\n"
 msgstr "lösche '%s'\n"
 
-#: builtin/add.c:148
+#: builtin/add.c:253
 msgid "Unstaged changes after refreshing the index:"
 msgstr ""
-"Nicht bereitgestellte Änderungen nach Aktualisierung der Bereitstellung:"
+"Nicht zum Commit vorgemerkte Änderungen nach Aktualisierung der Staging-Area:"
 
-#: builtin/add.c:151 builtin/add.c:460 builtin/rm.c:275
+#: builtin/add.c:256 builtin/add.c:573 builtin/rm.c:337
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "Pfadspezifikation '%s' stimmt mit keinen Dateien überein"
 
-#: builtin/add.c:234
+#: builtin/add.c:339
 msgid "Could not read the index"
-msgstr "Konnte die Bereitstellung nicht lesen"
+msgstr "Konnte die Staging-Area nicht lesen"
 
-#: builtin/add.c:244
+#: builtin/add.c:350
 #, c-format
 msgid "Could not open '%s' for writing."
 msgstr "Konnte '%s' nicht zum Schreiben öffnen."
 
-#: builtin/add.c:248
+#: builtin/add.c:354
 msgid "Could not write patch"
 msgstr "Konnte Patch nicht schreiben"
 
-#: builtin/add.c:253
+#: builtin/add.c:359
 #, c-format
 msgid "Could not stat '%s'"
 msgstr "Konnte Verzeichnis '%s' nicht lesen"
 
-#: builtin/add.c:255
+#: builtin/add.c:361
 msgid "Empty patch. Aborted."
 msgstr "Leerer Patch. Abgebrochen."
 
-#: builtin/add.c:261
+#: builtin/add.c:367
 #, c-format
 msgid "Could not apply '%s'"
 msgstr "Konnte '%s' nicht anwenden."
 
-#: builtin/add.c:271
+#: builtin/add.c:377
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
 "Die folgenden Pfade werden durch eine Ihrer \".gitignore\" Dateien "
 "ignoriert:\n"
 
-#: builtin/add.c:277 builtin/clean.c:161 builtin/fetch.c:78 builtin/mv.c:63
-#: builtin/prune-packed.c:76 builtin/push.c:425 builtin/remote.c:1253
-#: builtin/rm.c:206
+#: builtin/add.c:394 builtin/clean.c:875 builtin/fetch.c:78 builtin/mv.c:63
+#: builtin/prune-packed.c:73 builtin/push.c:451 builtin/remote.c:1253
+#: builtin/rm.c:268
 msgid "dry run"
 msgstr "Probelauf"
 
-#: builtin/add.c:278 builtin/apply.c:4405 builtin/check-ignore.c:19
-#: builtin/commit.c:1150 builtin/count-objects.c:82 builtin/fsck.c:613
-#: builtin/log.c:1522 builtin/mv.c:62 builtin/read-tree.c:112
+#: builtin/add.c:395 builtin/apply.c:4410 builtin/check-ignore.c:19
+#: builtin/commit.c:1220 builtin/count-objects.c:95 builtin/fsck.c:613
+#: builtin/log.c:1573 builtin/mv.c:62 builtin/read-tree.c:113
 msgid "be verbose"
 msgstr "erweiterte Ausgaben"
 
-#: builtin/add.c:280
+#: builtin/add.c:397
 msgid "interactive picking"
 msgstr "interaktives Auswählen"
 
-#: builtin/add.c:281 builtin/checkout.c:1031 builtin/reset.c:258
+#: builtin/add.c:398 builtin/checkout.c:1073 builtin/reset.c:261
 msgid "select hunks interactively"
-msgstr "interaktiv Bereiche auswählen"
+msgstr "Blöcke interaktiv auswählen"
 
-#: builtin/add.c:282
+#: builtin/add.c:399
 msgid "edit current diff and apply"
 msgstr "aktuelle Unterschiede editieren und anwenden"
 
-#: builtin/add.c:283
+#: builtin/add.c:400
 msgid "allow adding otherwise ignored files"
 msgstr "erlaubt das Hinzufügen andernfalls ignorierter Dateien"
 
-#: builtin/add.c:284
+#: builtin/add.c:401
 msgid "update tracked files"
 msgstr "aktualisiert beobachtete Dateien"
 
-#: builtin/add.c:285
+#: builtin/add.c:402
 msgid "record only the fact that the path will be added later"
 msgstr "speichert nur, dass der Pfad später hinzugefügt werden soll"
 
-#: builtin/add.c:286
+#: builtin/add.c:403
 msgid "add changes from all tracked and untracked files"
 msgstr ""
 "fügt Änderungen von allen beobachteten und unbeobachteten Dateien hinzu"
 
-#: builtin/add.c:287
+#. takes no arguments
+#: builtin/add.c:406
+msgid "ignore paths removed in the working tree (same as --no-all)"
+msgstr "ignoriert gelöschte Pfade im Arbeitsverzeichnis (genau wie --no-all)"
+
+#: builtin/add.c:408
 msgid "don't add, only refresh the index"
-msgstr "fügt nichts hinzu, aktualisiert nur die Bereitstellung"
+msgstr "fügt nichts hinzu, aktualisiert nur die Staging-Area"
 
-#: builtin/add.c:288
+#: builtin/add.c:409
 msgid "just skip files which cannot be added because of errors"
 msgstr ""
 "überspringt Dateien, die aufgrund von Fehlern nicht hinzugefügt werden "
 "konnten"
 
-#: builtin/add.c:289
+#: builtin/add.c:410
 msgid "check if - even missing - files are ignored in dry run"
 msgstr "prüft ob - auch fehlende - Dateien im Probelauf ignoriert werden"
 
-#: builtin/add.c:311
+#: builtin/add.c:432
 #, c-format
 msgid "Use -f if you really want to add them.\n"
 msgstr "Verwenden Sie -f wenn Sie diese wirklich hinzufügen möchten.\n"
 
-#: builtin/add.c:312
+#: builtin/add.c:433
 msgid "no files added"
 msgstr "keine Dateien hinzugefügt"
 
-#: builtin/add.c:318
+#: builtin/add.c:439
 msgid "adding files failed"
 msgstr "Hinzufügen von Dateien fehlgeschlagen"
 
-#.
-#. * To be consistent with "git add -p" and most Git
-#. * commands, we should default to being tree-wide, but
-#. * this is not the original behavior and can't be
-#. * changed until users trained themselves not to type
-#. * "git add -u" or "git add -A". For now, we warn and
-#. * keep the old behavior. Later, this warning can be
-#. * turned into a die(...), and eventually we may
-#. * reallow the command with a new behavior.
-#.
-#: builtin/add.c:335
-#, c-format
-msgid ""
-"The behavior of 'git add %s (or %s)' with no path argument from a\n"
-"subdirectory of the tree will change in Git 2.0 and should not be used "
-"anymore.\n"
-"To add content for the whole tree, run:\n"
-"\n"
-"  git add %s :/\n"
-"  (or git add %s :/)\n"
-"\n"
-"To restrict the command to the current directory, run:\n"
-"\n"
-"  git add %s .\n"
-"  (or git add %s .)\n"
-"\n"
-"With the current Git version, the command is restricted to the current "
-"directory."
-msgstr ""
-"Das Verhalten von 'git add %s (oder %s)' ohne ein Pfad-Argument von\n"
-"einem Unterverzeichnis aus wird in Git 2.0 geändert und sollte nicht\n"
-"mehr verwendet werden.\n"
-"Um Dateien des gesamten Projektverzeichnisses hinzuzufügen, führen Sie aus:\n"
-"\n"
-"  git add %s :/\n"
-"  (oder git add %s :/)\n"
-"\n"
-"Zur Einschränkung auf das aktuelle Verzeichnis führen Sie aus:\n"
-"\n"
-"  git add %s .\n"
-"  (oder git add %s .)\n"
-"\n"
-"Mit der aktuellen Version von Git ist das Kommando auf das aktuelle\n"
-"Verzeichnis beschränkt."
-
-#: builtin/add.c:381
+#: builtin/add.c:478
 msgid "-A and -u are mutually incompatible"
 msgstr "Die Optionen -A und -u sind zueinander inkompatibel."
 
-#: builtin/add.c:383
+#: builtin/add.c:496
 msgid "Option --ignore-missing can only be used together with --dry-run"
 msgstr ""
 "Die Option --ignore-missing kann nur zusammen mit --dry-run verwendet werden."
 
-#: builtin/add.c:414
+#: builtin/add.c:526
 #, c-format
 msgid "Nothing specified, nothing added.\n"
 msgstr "Nichts spezifiziert, nichts hinzugefügt.\n"
 
-#: builtin/add.c:415
+#: builtin/add.c:527
 #, c-format
 msgid "Maybe you wanted to say 'git add .'?\n"
 msgstr "Wollten Sie vielleicht 'git add .' sagen?\n"
 
-#: builtin/add.c:421 builtin/check-ignore.c:67 builtin/clean.c:204
-#: builtin/commit.c:291 builtin/mv.c:82 builtin/rm.c:235
+#: builtin/add.c:533 builtin/check-ignore.c:161 builtin/clean.c:919
+#: builtin/commit.c:312 builtin/mv.c:82 builtin/rm.c:297
 msgid "index file corrupt"
-msgstr "Bereitstellungsdatei beschädigt"
+msgstr "Staging-Area-Datei beschädigt"
 
-#: builtin/add.c:481 builtin/apply.c:4501 builtin/mv.c:229 builtin/rm.c:370
+#: builtin/add.c:605 builtin/apply.c:4506 builtin/mv.c:229 builtin/rm.c:432
 msgid "Unable to write new index file"
-msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+msgstr "Konnte neue Staging-Area-Datei nicht schreiben."
 
 #: builtin/apply.c:57
 msgid "git apply [options] [<patch>...]"
@@ -1680,12 +1973,12 @@ msgstr "git apply [Optionen] [<Patch>...]"
 #: builtin/apply.c:110
 #, c-format
 msgid "unrecognized whitespace option '%s'"
-msgstr "nicht erkannte Option für Leerzeichen: '%s'"
+msgstr "nicht erkannte Whitespace-Option: '%s'"
 
 #: builtin/apply.c:125
 #, c-format
 msgid "unrecognized whitespace ignore option '%s'"
-msgstr "nicht erkannte Option zum Ignorieren von Leerzeichen: '%s'"
+msgstr "nicht erkannte Option zum Ignorieren von Whitespace: '%s'"
 
 #: builtin/apply.c:823
 #, c-format
@@ -1798,19 +2091,19 @@ msgstr "Patch mit nutzlosen Informationen bei Zeile %d"
 #: builtin/apply.c:2076
 #, c-format
 msgid "unable to read symlink %s"
-msgstr "konnte symbolische Verknüpfung %s nicht lesen"
+msgstr "konnte symbolischen Verweis %s nicht lesen"
 
 #: builtin/apply.c:2080
 #, c-format
 msgid "unable to open or read %s"
 msgstr "konnte %s nicht öffnen oder lesen"
 
-#: builtin/apply.c:2684
+#: builtin/apply.c:2688
 #, c-format
 msgid "invalid start of line: '%c'"
 msgstr "Ungültiger Zeilenanfang: '%c'"
 
-#: builtin/apply.c:2802
+#: builtin/apply.c:2806
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
 msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
@@ -1818,12 +2111,12 @@ msgstr[0] "Patch-Bereich #%d erfolgreich angewendet bei %d (%d Zeile versetzt)"
 msgstr[1] ""
 "Patch-Bereich #%d erfolgreich angewendet bei %d (%d Zeilen versetzt)"
 
-#: builtin/apply.c:2814
+#: builtin/apply.c:2818
 #, c-format
 msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
 msgstr "Kontext reduziert zu (%ld/%ld) um Patch-Bereich bei %d anzuwenden"
 
-#: builtin/apply.c:2820
+#: builtin/apply.c:2824
 #, c-format
 msgid ""
 "while searching for:\n"
@@ -1832,345 +2125,341 @@ msgstr ""
 "bei der Suche nach:\n"
 "%.*s"
 
-#: builtin/apply.c:2839
+#: builtin/apply.c:2843
 #, c-format
 msgid "missing binary patch data for '%s'"
 msgstr "keine Daten in Binär-Patch für '%s'"
 
-#: builtin/apply.c:2942
+#: builtin/apply.c:2946
 #, c-format
 msgid "binary patch does not apply to '%s'"
 msgstr "Konnte Binär-Patch nicht auf '%s' anwenden"
 
-#: builtin/apply.c:2948
+#: builtin/apply.c:2952
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
 msgstr ""
 "Binär-Patch für '%s' erzeugt falsches Ergebnis (erwartete %s, bekam %s)"
 
-#: builtin/apply.c:2969
+#: builtin/apply.c:2973
 #, c-format
 msgid "patch failed: %s:%ld"
 msgstr "Anwendung des Patches fehlgeschlagen: %s:%ld"
 
-#: builtin/apply.c:3091
+#: builtin/apply.c:3095
 #, c-format
 msgid "cannot checkout %s"
 msgstr "kann %s nicht auschecken"
 
-#: builtin/apply.c:3136 builtin/apply.c:3145 builtin/apply.c:3189
+#: builtin/apply.c:3140 builtin/apply.c:3149 builtin/apply.c:3194
 #, c-format
 msgid "read of %s failed"
 msgstr "Konnte %s nicht lesen"
 
-#: builtin/apply.c:3169 builtin/apply.c:3391
+#: builtin/apply.c:3174 builtin/apply.c:3396
 #, c-format
 msgid "path %s has been renamed/deleted"
 msgstr "Pfad %s wurde umbenannt/gelöscht"
 
-#: builtin/apply.c:3250 builtin/apply.c:3405
+#: builtin/apply.c:3255 builtin/apply.c:3410
 #, c-format
 msgid "%s: does not exist in index"
-msgstr "%s ist nicht bereitgestellt"
+msgstr "%s ist nicht in der Staging-Area"
 
-#: builtin/apply.c:3254 builtin/apply.c:3397 builtin/apply.c:3419
+#: builtin/apply.c:3259 builtin/apply.c:3402 builtin/apply.c:3424
 #, c-format
 msgid "%s: %s"
 msgstr "%s: %s"
 
-#: builtin/apply.c:3259 builtin/apply.c:3413
+#: builtin/apply.c:3264 builtin/apply.c:3418
 #, c-format
 msgid "%s: does not match index"
-msgstr "%s entspricht nicht der Bereitstellung"
+msgstr "%s entspricht nicht der Version in der Staging-Area"
 
-#: builtin/apply.c:3361
+#: builtin/apply.c:3366
 msgid "removal patch leaves file contents"
 msgstr "Lösch-Patch hinterlässt Dateiinhalte"
 
-#: builtin/apply.c:3430
+#: builtin/apply.c:3435
 #, c-format
 msgid "%s: wrong type"
 msgstr "%s: falscher Typ"
 
-#: builtin/apply.c:3432
+#: builtin/apply.c:3437
 #, c-format
 msgid "%s has type %o, expected %o"
 msgstr "%s ist vom Typ %o, erwartete %o"
 
-#: builtin/apply.c:3533
+#: builtin/apply.c:3538
 #, c-format
 msgid "%s: already exists in index"
 msgstr "%s ist bereits bereitgestellt"
 
-#: builtin/apply.c:3536
+#: builtin/apply.c:3541
 #, c-format
 msgid "%s: already exists in working directory"
 msgstr "%s existiert bereits im Arbeitsverzeichnis"
 
-#: builtin/apply.c:3556
+#: builtin/apply.c:3561
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o)"
 msgstr "neuer Modus (%o) von %s entspricht nicht dem alten Modus (%o)"
 
-#: builtin/apply.c:3561
+#: builtin/apply.c:3566
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
 msgstr "neuer Modus (%o) von %s entspricht nicht dem alten Modus (%o) von %s"
 
-#: builtin/apply.c:3569
+#: builtin/apply.c:3574
 #, c-format
 msgid "%s: patch does not apply"
 msgstr "%s: Patch konnte nicht angewendet werden"
 
-#: builtin/apply.c:3582
+#: builtin/apply.c:3587
 #, c-format
 msgid "Checking patch %s..."
 msgstr "Prüfe Patch %s..."
 
-#: builtin/apply.c:3675 builtin/checkout.c:215 builtin/reset.c:124
+#: builtin/apply.c:3680 builtin/checkout.c:216 builtin/reset.c:127
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
 msgstr "make_cache_entry für Pfad '%s' fehlgeschlagen"
 
-#: builtin/apply.c:3818
+#: builtin/apply.c:3823
 #, c-format
 msgid "unable to remove %s from index"
-msgstr "konnte %s nicht aus der Bereitstellung entfernen"
+msgstr "konnte %s nicht aus der Staging-Area entfernen"
 
-#: builtin/apply.c:3846
+#: builtin/apply.c:3851
 #, c-format
-msgid "corrupt patch for subproject %s"
-msgstr "fehlerhafter Patch für Unterprojekt %s"
+msgid "corrupt patch for submodule %s"
+msgstr "fehlerhafter Patch für Submodul %s"
 
-#: builtin/apply.c:3850
+#: builtin/apply.c:3855
 #, c-format
 msgid "unable to stat newly created file '%s'"
 msgstr "konnte neu erstellte Datei '%s' nicht lesen"
 
-#: builtin/apply.c:3855
+#: builtin/apply.c:3860
 #, c-format
 msgid "unable to create backing store for newly created file %s"
 msgstr "kann internen Speicher für eben erstellte Datei %s nicht erzeugen"
 
-#: builtin/apply.c:3858 builtin/apply.c:3966
+#: builtin/apply.c:3863 builtin/apply.c:3971
 #, c-format
 msgid "unable to add cache entry for %s"
 msgstr "kann für %s keinen Eintrag in den Zwischenspeicher hinzufügen"
 
-#: builtin/apply.c:3891
+#: builtin/apply.c:3896
 #, c-format
 msgid "closing file '%s'"
 msgstr "schließe Datei '%s'"
 
-#: builtin/apply.c:3940
+#: builtin/apply.c:3945
 #, c-format
 msgid "unable to write file '%s' mode %o"
 msgstr "konnte Datei '%s' mit Modus %o nicht schreiben"
 
-#: builtin/apply.c:4027
+#: builtin/apply.c:4032
 #, c-format
 msgid "Applied patch %s cleanly."
 msgstr "Patch %s sauber angewendet"
 
-#: builtin/apply.c:4035
+#: builtin/apply.c:4040
 msgid "internal error"
 msgstr "interner Fehler"
 
 #. Say this even without --verbose
-#: builtin/apply.c:4038
+#: builtin/apply.c:4043
 #, c-format
 msgid "Applying patch %%s with %d reject..."
 msgid_plural "Applying patch %%s with %d rejects..."
 msgstr[0] "Wende Patch %%s mit %d Zurückweisung an..."
 msgstr[1] "Wende Patch %%s mit %d Zurückweisungen an..."
 
-#: builtin/apply.c:4048
+#: builtin/apply.c:4053
 #, c-format
 msgid "truncating .rej filename to %.*s.rej"
 msgstr "Verkürze Name von .rej Datei zu %.*s.rej"
 
-#: builtin/apply.c:4069
+#: builtin/apply.c:4074
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Patch-Bereich #%d sauber angewendet."
 
-#: builtin/apply.c:4072
+#: builtin/apply.c:4077
 #, c-format
 msgid "Rejected hunk #%d."
-msgstr "Patch-Bereich #%d zurückgewiesen."
+msgstr "Patch-Block #%d zurückgewiesen."
 
-#: builtin/apply.c:4222
+#: builtin/apply.c:4227
 msgid "unrecognized input"
 msgstr "nicht erkannte Eingabe"
 
-#: builtin/apply.c:4233
+#: builtin/apply.c:4238
 msgid "unable to read index file"
-msgstr "Konnte Bereitstellungsdatei nicht lesen"
+msgstr "Konnte Staging-Area-Datei nicht lesen"
 
-#: builtin/apply.c:4352 builtin/apply.c:4355 builtin/clone.c:91
+#: builtin/apply.c:4357 builtin/apply.c:4360 builtin/clone.c:91
 #: builtin/fetch.c:63
 msgid "path"
 msgstr "Pfad"
 
-#: builtin/apply.c:4353
+#: builtin/apply.c:4358
 msgid "don't apply changes matching the given path"
 msgstr "wendet keine Änderungen im angegebenen Pfad an"
 
-#: builtin/apply.c:4356
+#: builtin/apply.c:4361
 msgid "apply changes matching the given path"
 msgstr "wendet Änderungen nur im angegebenen Pfad an"
 
-#: builtin/apply.c:4358
+#: builtin/apply.c:4363
 msgid "num"
 msgstr "Anzahl"
 
-#: builtin/apply.c:4359
+#: builtin/apply.c:4364
 msgid "remove <num> leading slashes from traditional diff paths"
 msgstr ""
 "entfernt <Anzahl> vorangestellte Schrägstriche von herkömmlichen "
 "Differenzpfaden"
 
-#: builtin/apply.c:4362
+#: builtin/apply.c:4367
 msgid "ignore additions made by the patch"
 msgstr "ignoriert hinzugefügte Zeilen des Patches"
 
-#: builtin/apply.c:4364
+#: builtin/apply.c:4369
 msgid "instead of applying the patch, output diffstat for the input"
 msgstr ""
 "anstatt der Anwendung des Patches, wird der \"diffstat\" für die Eingabe "
 "ausgegeben"
 
-#: builtin/apply.c:4368
+#: builtin/apply.c:4373
 msgid "show number of added and deleted lines in decimal notation"
 msgstr ""
 "zeigt die Anzahl von hinzugefügten/entfernten Zeilen in Dezimalnotation"
 
-#: builtin/apply.c:4370
+#: builtin/apply.c:4375
 msgid "instead of applying the patch, output a summary for the input"
 msgstr ""
 "anstatt der Anwendung des Patches, wird eine Zusammenfassung für die Eingabe "
 "ausgegeben"
 
-#: builtin/apply.c:4372
+#: builtin/apply.c:4377
 msgid "instead of applying the patch, see if the patch is applicable"
 msgstr ""
 "anstatt der Anwendung des Patches, zeige ob Patch angewendet werden kann"
 
-#: builtin/apply.c:4374
+#: builtin/apply.c:4379
 msgid "make sure the patch is applicable to the current index"
 msgstr ""
-"stellt sicher, dass der Patch in der aktuellen Bereitstellung angewendet "
+"stellt sicher, dass der Patch in der aktuellen Staging-Area angewendet "
 "werden kann"
 
-#: builtin/apply.c:4376
+#: builtin/apply.c:4381
 msgid "apply a patch without touching the working tree"
-msgstr "wendet einen Patch an, ohne Änderungen im Arbeitszweig vorzunehmen"
+msgstr ""
+"wendet einen Patch an, ohne Änderungen im Arbeitsverzeichnis vorzunehmen"
 
-#: builtin/apply.c:4378
+#: builtin/apply.c:4383
 msgid "also apply the patch (use with --stat/--summary/--check)"
 msgstr "wendet den Patch an (Benutzung mit --stat/--summary/--check)"
 
-#: builtin/apply.c:4380
+#: builtin/apply.c:4385
 msgid "attempt three-way merge if a patch does not apply"
-msgstr ""
-"versucht 3-Wege-Zusammenführung, wenn der Patch nicht angewendet werden "
-"konnte"
+msgstr "versucht 3-Wege-Merge, wenn der Patch nicht angewendet werden konnte"
 
-#: builtin/apply.c:4382
+#: builtin/apply.c:4387
 msgid "build a temporary index based on embedded index information"
 msgstr ""
-"erstellt eine temporäre Bereitstellung basierend auf den integrierten "
-"Bereitstellungsinformationen"
+"erstellt eine temporäre Staging-Area basierend auf den integrierten Staging-"
+"Area-Informationen"
 
-#: builtin/apply.c:4384 builtin/checkout-index.c:197 builtin/ls-files.c:463
+#: builtin/apply.c:4389 builtin/checkout-index.c:197 builtin/ls-files.c:462
 msgid "paths are separated with NUL character"
 msgstr "Pfade sind getrennt durch NUL Zeichen"
 
-#: builtin/apply.c:4387
+#: builtin/apply.c:4392
 msgid "ensure at least <n> lines of context match"
 msgstr "stellt sicher, dass mindestens <n> Zeilen des Kontextes übereinstimmen"
 
-#: builtin/apply.c:4388
+#: builtin/apply.c:4393
 msgid "action"
 msgstr "Aktion"
 
-#: builtin/apply.c:4389
+#: builtin/apply.c:4394
 msgid "detect new or modified lines that have whitespace errors"
-msgstr "ermittelt neue oder geänderte Zeilen die Fehler in Leerzeichen haben"
+msgstr "ermittelt neue oder geänderte Zeilen die Whitespace-Fehler haben"
 
-#: builtin/apply.c:4392 builtin/apply.c:4395
+#: builtin/apply.c:4397 builtin/apply.c:4400
 msgid "ignore changes in whitespace when finding context"
-msgstr "ignoriert Änderungen in Leerzeichen bei der Suche des Kontextes"
+msgstr "ignoriert Änderungen im Whitespace bei der Suche des Kontextes"
 
-#: builtin/apply.c:4398
+#: builtin/apply.c:4403
 msgid "apply the patch in reverse"
 msgstr "wendet den Patch in umgekehrter Reihenfolge an"
 
-#: builtin/apply.c:4400
+#: builtin/apply.c:4405
 msgid "don't expect at least one line of context"
 msgstr "erwartet keinen Kontext"
 
-#: builtin/apply.c:4402
+#: builtin/apply.c:4407
 msgid "leave the rejected hunks in corresponding *.rej files"
 msgstr ""
-"hinterlässt zurückgewiesene Patch-Bereiche in den entsprechenden *.rej "
-"Dateien"
+"hinterlässt zurückgewiesene Patch-Blöcke in entsprechenden *.rej Dateien"
 
-#: builtin/apply.c:4404
+#: builtin/apply.c:4409
 msgid "allow overlapping hunks"
-msgstr "erlaubt sich überlappende Patch-Bereiche"
+msgstr "erlaubt sich überlappende Patch-Blöcke"
 
-#: builtin/apply.c:4407
+#: builtin/apply.c:4412
 msgid "tolerate incorrectly detected missing new-line at the end of file"
 msgstr "toleriert fehlerhaft erkannten fehlenden Zeilenumbruch am Dateiende"
 
-#: builtin/apply.c:4410
+#: builtin/apply.c:4415
 msgid "do not trust the line counts in the hunk headers"
-msgstr "vertraut nicht den Zeilennummern im Kopf des Patch-Bereiches"
+msgstr "vertraut nicht den Zeilennummern im Kopf des Patch-Blocks"
 
-#: builtin/apply.c:4412
+#: builtin/apply.c:4417
 msgid "root"
 msgstr "Wurzelverzeichnis"
 
-#: builtin/apply.c:4413
+#: builtin/apply.c:4418
 msgid "prepend <root> to all filenames"
 msgstr "stellt <Wurzelverzeichnis> vor alle Dateinamen"
 
-#: builtin/apply.c:4435
+#: builtin/apply.c:4440
 msgid "--3way outside a repository"
 msgstr ""
-"Die Option --3way kann nicht außerhalb eines Projektarchivs verwendet werden."
+"Die Option --3way kann nicht außerhalb eines Repositories verwendet werden."
 
-#: builtin/apply.c:4443
+#: builtin/apply.c:4448
 msgid "--index outside a repository"
 msgstr ""
-"Die Option --index kann nicht außerhalb eines Projektarchivs verwendet "
-"werden."
+"Die Option --index kann nicht außerhalb eines Repositories verwendet werden."
 
-#: builtin/apply.c:4446
+#: builtin/apply.c:4451
 msgid "--cached outside a repository"
 msgstr ""
-"Die Option --cached kann nicht außerhalb eines Projektarchivs verwendet "
-"werden."
+"Die Option --cached kann nicht außerhalb eines Repositories verwendet werden."
 
-#: builtin/apply.c:4462
+#: builtin/apply.c:4467
 #, c-format
 msgid "can't open patch '%s'"
 msgstr "kann Patch '%s' nicht öffnen"
 
-#: builtin/apply.c:4476
+#: builtin/apply.c:4481
 #, c-format
 msgid "squelched %d whitespace error"
 msgid_plural "squelched %d whitespace errors"
-msgstr[0] "unterdrückte %d Fehler in Leerzeichen"
-msgstr[1] "unterdrückte %d Fehler in Leerzeichen"
+msgstr[0] "unterdrückte %d Whitespace-Fehler"
+msgstr[1] "unterdrückte %d Whitespace-Fehler"
 
-#: builtin/apply.c:4482 builtin/apply.c:4492
+#: builtin/apply.c:4487 builtin/apply.c:4497
 #, c-format
 msgid "%d line adds whitespace errors."
 msgid_plural "%d lines add whitespace errors."
-msgstr[0] "%d Zeile fügt Fehler in Leerzeichen hinzu."
-msgstr[1] "%d Zeilen fügen Fehler in Leerzeichen hinzu."
+msgstr[0] "%d Zeile fügt Whitespace-Fehler hinzu."
+msgstr[1] "%d Zeilen fügen Whitespace-Fehler hinzu."
 
 #: builtin/archive.c:17
 #, c-format
@@ -2189,21 +2478,21 @@ msgstr "git archive: Externes Archiv ohne URL"
 msgid "git archive: expected ACK/NAK, got EOF"
 msgstr "git archive: habe ACK/NAK erwartet, aber EOF bekommen"
 
-#: builtin/archive.c:63
+#: builtin/archive.c:61
 #, c-format
 msgid "git archive: NACK %s"
 msgstr "git archive: NACK %s"
 
-#: builtin/archive.c:65
+#: builtin/archive.c:63
 #, c-format
 msgid "remote error: %s"
 msgstr "Fehler am anderen Ende: %s"
 
-#: builtin/archive.c:66
+#: builtin/archive.c:64
 msgid "git archive: protocol error"
 msgstr "git archive: Protokollfehler"
 
-#: builtin/archive.c:71
+#: builtin/archive.c:68
 msgid "git archive: expected a flush"
 msgstr "git archive: erwartete eine Spülung (flush)"
 
@@ -2217,446 +2506,491 @@ msgstr "führt 'git bisect next' aus"
 
 #: builtin/bisect--helper.c:19
 msgid "update BISECT_HEAD instead of checking out the current commit"
-msgstr "aktualisiert BISECT_HEAD, anstatt die aktuelle Version auszuchecken"
+msgstr "aktualisiert BISECT_HEAD, anstatt den aktuellen Commit auszuchecken"
 
-#: builtin/blame.c:25
+#: builtin/blame.c:26
 msgid "git blame [options] [rev-opts] [rev] [--] file"
 msgstr "git blame [Optionen] [rev-opts] [rev] [--] Datei"
 
-#: builtin/blame.c:30 builtin/shortlog.c:15
+#: builtin/blame.c:31
 msgid "[rev-opts] are documented in git-rev-list(1)"
 msgstr "[rev-opts] sind dokumentiert in git-rev-list(1)"
 
-#: builtin/blame.c:2350
+#: builtin/blame.c:2276
 msgid "Show blame entries as we find them, incrementally"
 msgstr "Zeigt \"blame\"-Einträge schrittweise, während wir sie generieren"
 
-#: builtin/blame.c:2351
+#: builtin/blame.c:2277
 msgid "Show blank SHA-1 for boundary commits (Default: off)"
-msgstr "Zeigt leere SHA-1 für Grenzversionen (Standard: aus)"
+msgstr "Zeigt leere SHA-1 für Grenz-Commits (Standard: aus)"
 
-#: builtin/blame.c:2352
+#: builtin/blame.c:2278
 msgid "Do not treat root commits as boundaries (Default: off)"
-msgstr "Behandelt Ursprungsversionen nicht als Grenzen (Standard: aus)"
+msgstr "Behandelt Ursprungs-Commit nicht als Grenzen (Standard: aus)"
 
-#: builtin/blame.c:2353
+#: builtin/blame.c:2279
 msgid "Show work cost statistics"
 msgstr "Zeigt Statistiken zum Arbeitsaufwand"
 
-#: builtin/blame.c:2354
+#: builtin/blame.c:2280
 msgid "Show output score for blame entries"
 msgstr "Zeigt Ausgabebewertung für \"blame\"-Einträge"
 
-#: builtin/blame.c:2355
+#: builtin/blame.c:2281
 msgid "Show original filename (Default: auto)"
 msgstr "Zeigt ursprünglichen Dateinamen (Standard: auto)"
 
-#: builtin/blame.c:2356
+#: builtin/blame.c:2282
 msgid "Show original linenumber (Default: off)"
 msgstr "Zeigt ursprüngliche Zeilennummer (Standard: aus)"
 
-#: builtin/blame.c:2357
+#: builtin/blame.c:2283
 msgid "Show in a format designed for machine consumption"
 msgstr "Anzeige in einem Format für maschinelle Auswertung"
 
-#: builtin/blame.c:2358
+#: builtin/blame.c:2284
 msgid "Show porcelain format with per-line commit information"
 msgstr ""
-"Anzeige in Format für Fremdprogramme mit Versionsinformationen pro Zeile"
+"Anzeige in Format für Fremdprogramme mit Commit-Informationen pro Zeile"
 
-#: builtin/blame.c:2359
+#: builtin/blame.c:2285
 msgid "Use the same output mode as git-annotate (Default: off)"
 msgstr "Benutzt den gleichen Ausgabemodus wie \"git-annotate\" (Standard: aus)"
 
-#: builtin/blame.c:2360
+#: builtin/blame.c:2286
 msgid "Show raw timestamp (Default: off)"
 msgstr "Zeigt unbearbeiteten Zeitstempel (Standard: aus)"
 
-#: builtin/blame.c:2361
+#: builtin/blame.c:2287
 msgid "Show long commit SHA1 (Default: off)"
-msgstr "Zeigt langen Versions-SHA1 (Standard: aus)"
+msgstr "Zeigt langen Commit-SHA1 (Standard: aus)"
 
-#: builtin/blame.c:2362
+#: builtin/blame.c:2288
 msgid "Suppress author name and timestamp (Default: off)"
 msgstr "Unterdrückt den Namen des Autors und den Zeitstempel (Standard: aus)"
 
-#: builtin/blame.c:2363
+#: builtin/blame.c:2289
 msgid "Show author email instead of name (Default: off)"
 msgstr "Zeigt anstatt des Namens die Email-Adresse des Autors (Standard: aus)"
 
-#: builtin/blame.c:2364
+#: builtin/blame.c:2290
 msgid "Ignore whitespace differences"
-msgstr "Ignoriert Unterschiede in Leerzeichen"
+msgstr "Ignoriert Unterschiede im Whitespace"
 
-#: builtin/blame.c:2365
+#: builtin/blame.c:2291
 msgid "Spend extra cycles to find better match"
 msgstr "arbeite länger, um bessere Übereinstimmungen zu finden"
 
-#: builtin/blame.c:2366
+#: builtin/blame.c:2292
 msgid "Use revisions from <file> instead of calling git-rev-list"
-msgstr "Benutzt Revisionen von <Datei> anstatt \"git-rev-list\" aufzurufen"
+msgstr "Benutzt Commits von <Datei> anstatt \"git-rev-list\" aufzurufen"
 
-#: builtin/blame.c:2367
+#: builtin/blame.c:2293
 msgid "Use <file>'s contents as the final image"
 msgstr "Benutzt Inhalte der <Datei>en als entgültiges Abbild"
 
-#: builtin/blame.c:2368 builtin/blame.c:2369
+#: builtin/blame.c:2294 builtin/blame.c:2295
 msgid "score"
 msgstr "Bewertung"
 
-#: builtin/blame.c:2368
+#: builtin/blame.c:2294
 msgid "Find line copies within and across files"
 msgstr "Findet kopierte Zeilen innerhalb oder zwischen Dateien"
 
-#: builtin/blame.c:2369
+#: builtin/blame.c:2295
 msgid "Find line movements within and across files"
 msgstr "Findet verschobene Zeilen innerhalb oder zwischen Dateien"
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "n,m"
 msgstr "n,m"
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "Process only line range n,m, counting from 1"
 msgstr "Verarbeitet nur Zeilen im Bereich n,m, gezählt von 1"
 
-#: builtin/branch.c:23
+#: builtin/branch.c:24
 msgid "git branch [options] [-r | -a] [--merged | --no-merged]"
 msgstr "git branch [Optionen] [-r | -a] [--merged | --no-merged]"
 
-#: builtin/branch.c:24
+#: builtin/branch.c:25
 msgid "git branch [options] [-l] [-f] <branchname> [<start-point>]"
-msgstr "git branch [Optionen] [-l] [-f] <Zweigname> [<Startpunkt>]"
+msgstr "git branch [Optionen] [-l] [-f] <Branchname> [<Startpunkt>]"
 
-#: builtin/branch.c:25
+#: builtin/branch.c:26
 msgid "git branch [options] [-r] (-d | -D) <branchname>..."
-msgstr "git branch [Optionen] [-r] (-d | -D) <Zweigname>..."
+msgstr "git branch [Optionen] [-r] (-d | -D) <Branchname>..."
 
-#: builtin/branch.c:26
+#: builtin/branch.c:27
 msgid "git branch [options] (-m | -M) [<oldbranch>] <newbranch>"
-msgstr "git branch [Optionen] (-m | -M) [<alterZweig>] <neuerZweig>"
+msgstr "git branch [Optionen] (-m | -M) [<alterBranch>] <neuerBranch>"
 
-#: builtin/branch.c:145
+#: builtin/branch.c:150
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
 "         '%s', but not yet merged to HEAD."
 msgstr ""
-"entferne Zweig '%s', der zusammengeführt wurde mit\n"
-"         '%s', aber noch nicht mit der Zweigspitze (HEAD) zusammengeführt "
-"wurde."
+"entferne Branch '%s', der zusammengeführt wurde mit\n"
+"         '%s', aber noch nicht mit HEAD zusammengeführt wurde."
 
-#: builtin/branch.c:149
+#: builtin/branch.c:154
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
 "         '%s', even though it is merged to HEAD."
 msgstr ""
-"entferne Zweig '%s' nicht, der noch nicht zusammengeführt wurde mit\n"
-"         '%s', obwohl er mit der Zweigspitze (HEAD) zusammengeführt wurde."
+"entferne Branch '%s' nicht, der noch nicht zusammengeführt wurde mit\n"
+"         '%s', obwohl er mit HEAD zusammengeführt wurde."
 
-#: builtin/branch.c:163
+#: builtin/branch.c:168
 #, c-format
 msgid "Couldn't look up commit object for '%s'"
-msgstr "Konnte Versionsobjekt für '%s' nicht nachschlagen."
+msgstr "Konnte Commit-Objekt für '%s' nicht nachschlagen."
 
-#: builtin/branch.c:167
+#: builtin/branch.c:172
 #, c-format
 msgid ""
 "The branch '%s' is not fully merged.\n"
 "If you are sure you want to delete it, run 'git branch -D %s'."
 msgstr ""
-"Der Zweig '%s' ist nicht vollständig zusammengeführt.\n"
-"Wenn Sie sicher sind diesen Zweig zu entfernen, führen Sie 'git branch -D "
+"Der Branch '%s' ist nicht vollständig zusammengeführt.\n"
+"Wenn Sie sicher sind diesen Branch zu entfernen, führen Sie 'git branch -D "
 "%s' aus."
 
-#: builtin/branch.c:180
+#: builtin/branch.c:185
 msgid "Update of config-file failed"
 msgstr "Aktualisierung der Konfigurationsdatei fehlgeschlagen."
 
-#: builtin/branch.c:208
+#: builtin/branch.c:213
 msgid "cannot use -a with -d"
 msgstr "kann -a nicht mit -d benutzen"
 
-#: builtin/branch.c:214
+#: builtin/branch.c:219
 msgid "Couldn't look up commit object for HEAD"
-msgstr "Konnte Versionsobjekt für Zweigspitze (HEAD) nicht nachschlagen."
+msgstr "Konnte Commit-Objekt für HEAD nicht nachschlagen."
 
-#: builtin/branch.c:222
+#: builtin/branch.c:227
 #, c-format
 msgid "Cannot delete the branch '%s' which you are currently on."
 msgstr ""
-"Kann Zweig '%s' nicht entfernen, da Sie sich gerade auf diesem befinden."
+"Kann Branch '%s' nicht entfernen, da Sie sich gerade auf diesem befinden."
 
-#: builtin/branch.c:235
+#: builtin/branch.c:240
 #, c-format
 msgid "remote branch '%s' not found."
-msgstr "externer Zweig '%s' nicht gefunden"
+msgstr "Remote-Branch '%s' nicht gefunden"
 
-#: builtin/branch.c:236
+#: builtin/branch.c:241
 #, c-format
 msgid "branch '%s' not found."
-msgstr "Zweig '%s' nicht gefunden."
+msgstr "Branch '%s' nicht gefunden."
 
-#: builtin/branch.c:250
+#: builtin/branch.c:255
 #, c-format
 msgid "Error deleting remote branch '%s'"
-msgstr "Fehler beim Entfernen des externen Zweiges '%s'"
+msgstr "Fehler beim Entfernen des Remote-Branches '%s'"
 
-#: builtin/branch.c:251
+#: builtin/branch.c:256
 #, c-format
 msgid "Error deleting branch '%s'"
-msgstr "Fehler beim Entfernen des Zweiges '%s'"
+msgstr "Fehler beim Entfernen des Branches '%s'"
 
-#: builtin/branch.c:258
+#: builtin/branch.c:263
 #, c-format
 msgid "Deleted remote branch %s (was %s).\n"
-msgstr "Externer Zweig %s entfernt (war %s).\n"
+msgstr "Remote-Branch %s entfernt (war %s).\n"
 
-#: builtin/branch.c:259
+#: builtin/branch.c:264
 #, c-format
 msgid "Deleted branch %s (was %s).\n"
-msgstr "Zweig %s entfernt (war %s).\n"
+msgstr "Branch %s entfernt (war %s).\n"
 
-#: builtin/branch.c:361
+#: builtin/branch.c:366
 #, c-format
 msgid "branch '%s' does not point at a commit"
-msgstr "Zweig '%s' zeigt auf keine Version"
+msgstr "Branch '%s' zeigt auf keinen Commit"
 
-#: builtin/branch.c:433
+#: builtin/branch.c:453
 #, c-format
 msgid "[%s: behind %d]"
 msgstr "[%s: %d hinterher]"
 
-#: builtin/branch.c:435
+#: builtin/branch.c:455
 #, c-format
 msgid "[behind %d]"
 msgstr "[%d hinterher]"
 
-#: builtin/branch.c:439
+#: builtin/branch.c:459
 #, c-format
 msgid "[%s: ahead %d]"
 msgstr "[%s: %d voraus]"
 
-#: builtin/branch.c:441
+#: builtin/branch.c:461
 #, c-format
 msgid "[ahead %d]"
 msgstr "[%d voraus]"
 
-#: builtin/branch.c:444
+#: builtin/branch.c:464
 #, c-format
 msgid "[%s: ahead %d, behind %d]"
 msgstr "[%s: %d voraus, %d hinterher]"
 
-#: builtin/branch.c:447
+#: builtin/branch.c:467
 #, c-format
 msgid "[ahead %d, behind %d]"
 msgstr "[%d voraus, %d hinterher]"
 
-#: builtin/branch.c:469
+#: builtin/branch.c:490
 msgid " **** invalid ref ****"
 msgstr " **** ungültige Referenz ****"
 
-#: builtin/branch.c:560
+#: builtin/branch.c:582
+#, c-format
+msgid "(no branch, rebasing %s)"
+msgstr "(kein Branch, Rebase von Branch %s im Gange)"
+
+#: builtin/branch.c:585
+#, c-format
+msgid "(no branch, bisect started on %s)"
+msgstr "(kein Branch, binäre Suche begonnen bei %s)"
+
+#: builtin/branch.c:588
+#, c-format
+msgid "(detached from %s)"
+msgstr "(losgelöst von %s)"
+
+#: builtin/branch.c:591
 msgid "(no branch)"
-msgstr "(kein Zweig)"
+msgstr "(kein Branch)"
 
-#: builtin/branch.c:593
+#: builtin/branch.c:637
 #, c-format
 msgid "object '%s' does not point to a commit"
-msgstr "Objekt '%s' zeigt auf keine Version"
+msgstr "Objekt '%s' zeigt auf keinen Commit"
 
-#: builtin/branch.c:625
+#: builtin/branch.c:669
 msgid "some refs could not be read"
 msgstr "Konnte einige Referenzen nicht lesen"
 
-#: builtin/branch.c:638
+#: builtin/branch.c:682
 msgid "cannot rename the current branch while not on any."
 msgstr ""
-"Kann aktuellen Zweig nicht umbenennen, solange Sie sich auf keinem befinden."
+"Kann aktuellen Branch nicht umbenennen, solange Sie sich auf keinem befinden."
 
-#: builtin/branch.c:648
+#: builtin/branch.c:692
 #, c-format
 msgid "Invalid branch name: '%s'"
-msgstr "Ungültiger Zweig-Name: '%s'"
+msgstr "Ungültiger Branchname: '%s'"
 
-#: builtin/branch.c:663
+#: builtin/branch.c:707
 msgid "Branch rename failed"
-msgstr "Umbenennung des Zweiges fehlgeschlagen"
+msgstr "Umbenennung des Branches fehlgeschlagen"
 
-#: builtin/branch.c:667
+#: builtin/branch.c:711
 #, c-format
 msgid "Renamed a misnamed branch '%s' away"
-msgstr "falsch benannten Zweig '%s' umbenannt"
+msgstr "falsch benannten Branch '%s' umbenannt"
 
-#: builtin/branch.c:671
+#: builtin/branch.c:715
 #, c-format
 msgid "Branch renamed to %s, but HEAD is not updated!"
-msgstr "Zweig umbenannt zu %s, aber Zweigspitze (HEAD) ist nicht aktualisiert!"
+msgstr "Branch umbenannt zu %s, aber HEAD ist nicht aktualisiert!"
 
-#: builtin/branch.c:678
+#: builtin/branch.c:722
 msgid "Branch is renamed, but update of config-file failed"
 msgstr ""
-"Zweig ist umbenannt, aber die Aktualisierung der Konfigurationsdatei ist "
+"Branch ist umbenannt, aber die Aktualisierung der Konfigurationsdatei ist "
 "fehlgeschlagen."
 
-#: builtin/branch.c:693
+#: builtin/branch.c:737
 #, c-format
 msgid "malformed object name %s"
 msgstr "Missgebildeter Objektname %s"
 
-#: builtin/branch.c:717
+#: builtin/branch.c:761
 #, c-format
 msgid "could not write branch description template: %s"
-msgstr "Konnte Beschreibungsvorlage für Zweig nicht schreiben: %s"
+msgstr "Konnte Beschreibungsvorlage für Branch nicht schreiben: %s"
 
-#: builtin/branch.c:747
+#: builtin/branch.c:791
 msgid "Generic options"
 msgstr "Allgemeine Optionen"
 
-#: builtin/branch.c:749
+#: builtin/branch.c:793
 msgid "show hash and subject, give twice for upstream branch"
-msgstr "Zeigt Hash und Betreff; -vv: zusätzlich externen Übernahmezweig"
+msgstr "Zeigt Hash und Betreff; -vv: zusätzlich Upstream-Branch"
 
-#: builtin/branch.c:750
+#: builtin/branch.c:794
 msgid "suppress informational messages"
 msgstr "unterdrückt Informationsmeldungen"
 
-#: builtin/branch.c:751
+#: builtin/branch.c:795
 msgid "set up tracking mode (see git-pull(1))"
 msgstr "stellt den Übernahmemodus ein (siehe git-pull(1))"
 
-#: builtin/branch.c:753
+#: builtin/branch.c:797
 msgid "change upstream info"
-msgstr "ändert Informationen zum externen Übernahmezweig"
+msgstr "ändert Informationen zum Upstream-Branch"
 
-#: builtin/branch.c:757
+#: builtin/branch.c:801
 msgid "use colored output"
 msgstr "verwendet farbliche Ausgaben"
 
-#: builtin/branch.c:758
+#: builtin/branch.c:802
 msgid "act on remote-tracking branches"
-msgstr "wirkt auf externe Übernahmezweige"
+msgstr "wirkt auf Remote-Tracking-Branches"
 
-#: builtin/branch.c:761 builtin/branch.c:767 builtin/branch.c:788
-#: builtin/branch.c:794 builtin/commit.c:1366 builtin/commit.c:1367
-#: builtin/commit.c:1368 builtin/commit.c:1369 builtin/tag.c:468
+#: builtin/branch.c:805 builtin/branch.c:811 builtin/branch.c:832
+#: builtin/branch.c:838 builtin/commit.c:1433 builtin/commit.c:1434
+#: builtin/commit.c:1435 builtin/commit.c:1436 builtin/tag.c:468
 msgid "commit"
-msgstr "Version"
+msgstr "Commit"
 
-#: builtin/branch.c:762 builtin/branch.c:768
+#: builtin/branch.c:806 builtin/branch.c:812
 msgid "print only branches that contain the commit"
-msgstr "gibt nur Zweige aus, welche diese Version beinhalten"
+msgstr "gibt nur Branches aus, welche diesen Commit beinhalten"
 
-#: builtin/branch.c:774
+#: builtin/branch.c:818
 msgid "Specific git-branch actions:"
 msgstr "spezifische Aktionen für \"git-branch\":"
 
-#: builtin/branch.c:775
+#: builtin/branch.c:819
 msgid "list both remote-tracking and local branches"
-msgstr "listet externe Übernahmezweige und lokale Zweige auf"
+msgstr "listet Remote-Tracking und lokale Branches auf"
 
-#: builtin/branch.c:777
+#: builtin/branch.c:821
 msgid "delete fully merged branch"
-msgstr "entfernt vollständig zusammengeführten Zweig"
+msgstr "entfernt vollständig zusammengeführten Branch"
 
-#: builtin/branch.c:778
+#: builtin/branch.c:822
 msgid "delete branch (even if not merged)"
-msgstr "löscht Zweig (auch wenn nicht zusammengeführt)"
+msgstr "löscht Branch (auch wenn nicht zusammengeführt)"
 
-#: builtin/branch.c:779
+#: builtin/branch.c:823
 msgid "move/rename a branch and its reflog"
-msgstr "verschiebt/benennt einen Zweig und dessen Referenzprotokoll um"
+msgstr "verschiebt/benennt einen Branch und dessen Reflog um"
 
-#: builtin/branch.c:780
+#: builtin/branch.c:824
 msgid "move/rename a branch, even if target exists"
 msgstr ""
-"verschiebt/benennt einen Zweig um, auch wenn das Ziel bereits existiert"
+"verschiebt/benennt einen Branch um, auch wenn das Ziel bereits existiert"
 
-#: builtin/branch.c:781
+#: builtin/branch.c:825
 msgid "list branch names"
-msgstr "listet Zweignamen auf"
+msgstr "listet Branchnamen auf"
 
-#: builtin/branch.c:782
+#: builtin/branch.c:826
 msgid "create the branch's reflog"
-msgstr "erzeugt das Referenzprotokoll des Zweiges"
+msgstr "erzeugt das Reflog des Branches"
 
-#: builtin/branch.c:784
+#: builtin/branch.c:828
 msgid "edit the description for the branch"
-msgstr "bearbeitet die Beschreibung für den Zweig"
+msgstr "bearbeitet die Beschreibung für den Branch"
 
-#: builtin/branch.c:785
+#: builtin/branch.c:829
 msgid "force creation (when already exists)"
-msgstr "erzeuge auch, wenn der Zweig bereits existiert"
+msgstr "erzeuge auch, wenn der Branch bereits existiert"
 
-#: builtin/branch.c:788
+#: builtin/branch.c:832
 msgid "print only not merged branches"
-msgstr "gibt nur Zweige aus, die nicht zusammengeführt sind"
+msgstr "gibt nur Branches aus, die nicht zusammengeführt sind"
 
-#: builtin/branch.c:794
+#: builtin/branch.c:838
 msgid "print only merged branches"
-msgstr "gibt nur Zweige aus, die zusammengeführt sind"
+msgstr "gibt nur Branches aus, die zusammengeführt sind"
 
-#: builtin/branch.c:798
+#: builtin/branch.c:842
 msgid "list branches in columns"
-msgstr "listet Zweige in Spalten auf"
+msgstr "listet Branches in Spalten auf"
 
-#: builtin/branch.c:811
+#: builtin/branch.c:855
 msgid "Failed to resolve HEAD as a valid ref."
-msgstr "Konnte Zweigspitze (HEAD) nicht als gültige Referenz auflösen."
+msgstr "Konnte HEAD nicht als gültige Referenz auflösen."
 
-#: builtin/branch.c:816 builtin/clone.c:561
+#: builtin/branch.c:860 builtin/clone.c:630
 msgid "HEAD not found below refs/heads!"
-msgstr "Zweigspitze (HEAD) wurde nicht unter \"refs/heads\" gefunden!"
+msgstr "HEAD wurde nicht unter \"refs/heads\" gefunden!"
 
-#: builtin/branch.c:839
+#: builtin/branch.c:883
 msgid "--column and --verbose are incompatible"
 msgstr "Die Optionen --column und --verbose sind inkompatibel."
 
-#: builtin/branch.c:845
+#: builtin/branch.c:889 builtin/branch.c:928
 msgid "branch name required"
-msgstr "Zweigname erforderlich"
+msgstr "Branchname erforderlich"
 
-#: builtin/branch.c:860
+#: builtin/branch.c:904
 msgid "Cannot give description to detached HEAD"
-msgstr ""
-"zu losgelöster Zweigspitze (HEAD) kann keine Beschreibung hinterlegt werden"
+msgstr "zu losgelöstem HEAD kann keine Beschreibung hinterlegt werden"
 
-#: builtin/branch.c:865
+#: builtin/branch.c:909
 msgid "cannot edit description of more than one branch"
-msgstr "Beschreibung von mehr als einem Zweig kann nicht bearbeitet werden"
+msgstr "Beschreibung von mehr als einem Branch kann nicht bearbeitet werden"
 
-#: builtin/branch.c:872
+#: builtin/branch.c:916
 #, c-format
 msgid "No commit on branch '%s' yet."
-msgstr "Noch keine Version in Zweig '%s'."
+msgstr "Noch kein Commit in Branch '%s'."
 
-#: builtin/branch.c:875
+#: builtin/branch.c:919
 #, c-format
 msgid "No branch named '%s'."
-msgstr "Zweig '%s' nicht vorhanden."
+msgstr "Branch '%s' nicht vorhanden."
 
-#: builtin/branch.c:888
+#: builtin/branch.c:934
 msgid "too many branches for a rename operation"
-msgstr "zu viele Zweige für eine Umbenennen-Operation angegeben"
+msgstr "zu viele Branches für eine Umbenennen-Operation angegeben"
 
-#: builtin/branch.c:893
+#: builtin/branch.c:939
+msgid "too many branches to set new upstream"
+msgstr "zu viele Branches angegeben um Upstream-Branch zu setzen"
+
+#: builtin/branch.c:943
+#, c-format
+msgid ""
+"could not set upstream of HEAD to %s when it does not point to any branch."
+msgstr ""
+"Konnte keinen neuen Upstream-Branch von HEAD zu %s setzen, da dieser auf\n"
+"keinen Branch zeigt."
+
+#: builtin/branch.c:946 builtin/branch.c:968 builtin/branch.c:990
+#, c-format
+msgid "no such branch '%s'"
+msgstr "Kein solcher Branch '%s'"
+
+#: builtin/branch.c:950
 #, c-format
 msgid "branch '%s' does not exist"
-msgstr "Zweig '%s' existiert nicht"
+msgstr "Branch '%s' existiert nicht"
+
+#: builtin/branch.c:962
+msgid "too many branches to unset upstream"
+msgstr ""
+"zu viele Branches angegeben um Konfiguration zu Upstream-Branch zu entfernen"
+
+#: builtin/branch.c:966
+msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgstr ""
+"Konnte Konfiguration zu Upstream-Branch von HEAD nicht entfernen, da dieser\n"
+"auf keinen Branch zeigt."
 
-#: builtin/branch.c:905
+#: builtin/branch.c:972
 #, c-format
 msgid "Branch '%s' has no upstream information"
-msgstr "Zweig '%s' hat keinen externen Übernahmezweig gesetzt"
+msgstr "Branch '%s' hat keinen Upstream-Branch gesetzt"
 
-#: builtin/branch.c:920
+#: builtin/branch.c:987
+msgid "it does not make sense to create 'HEAD' manually"
+msgstr "'HEAD' darf nicht manuell erstellt werden"
+
+#: builtin/branch.c:993
 msgid "-a and -r options to 'git branch' do not make sense with a branch name"
 msgstr ""
 "Die Optionen -a und -r bei 'git branch' können nicht gemeimsam mit einem "
-"Zweignamen verwendet werden."
+"Branchnamen verwendet werden."
 
-#: builtin/branch.c:923
+#: builtin/branch.c:996
 #, c-format
 msgid ""
 "The --set-upstream flag is deprecated and will be removed. Consider using --"
@@ -2665,7 +2999,7 @@ msgstr ""
 "Die --set-upstream Option ist veraltet und wird entfernt. Benutzen Sie --"
 "track oder --set-upstream-to\n"
 
-#: builtin/branch.c:940
+#: builtin/branch.c:1013
 #, c-format
 msgid ""
 "\n"
@@ -2673,15 +3007,15 @@ msgid ""
 "\n"
 msgstr ""
 "\n"
-"Wenn Sie wollten, dass '%s' den Zweig '%s' als externen Übernahmezweig hat, "
-"führen Sie aus:\n"
+"Wenn Sie wollten, dass '%s' den Branch '%s' als Upstream-Branch hat, führen "
+"Sie aus:\n"
 
-#: builtin/branch.c:941
+#: builtin/branch.c:1014
 #, c-format
 msgid "    git branch -d %s\n"
 msgstr "    git branch -d %s\n"
 
-#: builtin/branch.c:942
+#: builtin/branch.c:1015
 #, c-format
 msgid "    git branch --set-upstream-to %s\n"
 msgstr "    git branch --set-upstream-to %s\n"
@@ -2693,51 +3027,51 @@ msgstr "%s ist in Ordnung\n"
 
 #: builtin/bundle.c:56
 msgid "Need a repository to create a bundle."
-msgstr "Um ein Paket zu erstellen wird ein Projektarchiv benötigt."
+msgstr "Um ein Paket zu erstellen wird ein Repository benötigt."
 
 #: builtin/bundle.c:60
 msgid "Need a repository to unbundle."
-msgstr "Zum Entpacken wird ein Projektarchiv benötigt."
+msgstr "Zum Entpacken wird ein Repository benötigt."
 
-#: builtin/cat-file.c:247
+#: builtin/cat-file.c:285
 msgid "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"
 msgstr "git cat-file (-t|-s|-e|-p|<Art>|--textconv) <Objekt>"
 
-#: builtin/cat-file.c:248
+#: builtin/cat-file.c:286
 msgid "git cat-file (--batch|--batch-check) < <list_of_objects>"
 msgstr "git cat-file (--batch|--batch-check) < <Liste_von_Objekten>"
 
-#: builtin/cat-file.c:266
+#: builtin/cat-file.c:323
 msgid "<type> can be one of: blob, tree, commit, tag"
 msgstr "<Art> kann sein: blob, tree, commit, tag"
 
-#: builtin/cat-file.c:267
+#: builtin/cat-file.c:324
 msgid "show object type"
 msgstr "zeigt Objektart"
 
-#: builtin/cat-file.c:268
+#: builtin/cat-file.c:325
 msgid "show object size"
 msgstr "zeigt Objektgröße"
 
-#: builtin/cat-file.c:270
+#: builtin/cat-file.c:327
 msgid "exit with zero when there's no error"
 msgstr "beendet mit Rückgabewert 0, wenn kein Fehler aufgetreten ist"
 
-#: builtin/cat-file.c:271
+#: builtin/cat-file.c:328
 msgid "pretty-print object's content"
 msgstr "ansprechende Anzeige des Objektinhaltes"
 
-#: builtin/cat-file.c:273
+#: builtin/cat-file.c:330
 msgid "for blob objects, run textconv on object's content"
 msgstr "führt eine Textkonvertierung auf den Inhalt von Blob-Objekten aus"
 
-#: builtin/cat-file.c:275
+#: builtin/cat-file.c:332
 msgid "show info and content of objects fed from the standard input"
 msgstr ""
 "Anzeige von Informationen und Inhalt von Objekten, gelesen von der Standard-"
 "Eingabe"
 
-#: builtin/cat-file.c:278
+#: builtin/cat-file.c:335
 msgid "show info about objects fed from the standard input"
 msgstr ""
 "Anzeige von Informationen über Objekte, gelesen von der Standard-Eingabe"
@@ -2757,7 +3091,7 @@ msgstr "gibt alle Attribute einer Datei aus"
 
 #: builtin/check-attr.c:20
 msgid "use .gitattributes only from the index"
-msgstr "verwendet .gitattributes nur von der Bereitstellung"
+msgstr "verwendet .gitattributes nur von der Staging-Area"
 
 #: builtin/check-attr.c:21 builtin/check-ignore.c:22 builtin/hash-object.c:75
 msgid "read file names from stdin"
@@ -2767,38 +3101,63 @@ msgstr "liest Dateinamen von der Standard-Eingabe"
 msgid "input paths are terminated by a null character"
 msgstr "Eingabepfade sind durch ein NUL Zeichen abgeschlossen"
 
-#: builtin/check-ignore.c:18 builtin/checkout.c:1012 builtin/gc.c:177
+#: builtin/check-ignore.c:18 builtin/checkout.c:1054 builtin/gc.c:177
 msgid "suppress progress reporting"
 msgstr "unterdrückt Fortschrittsanzeige"
 
-#: builtin/check-ignore.c:151
+#: builtin/check-ignore.c:26
+msgid "show non-matching input paths"
+msgstr "zeigt Eingabe-Pfade ohne Übereinstimmungen"
+
+#: builtin/check-ignore.c:143
 msgid "cannot specify pathnames with --stdin"
 msgstr "Angabe von Pfadnamen kann nicht gemeinsam mit --stdin verwendet werden"
 
-#: builtin/check-ignore.c:154
+#: builtin/check-ignore.c:146
 msgid "-z only makes sense with --stdin"
 msgstr "Die Option -z kann nur mit --stdin verwendet werden."
 
-#: builtin/check-ignore.c:156
+#: builtin/check-ignore.c:148
 msgid "no path specified"
 msgstr "kein Pfad angegeben"
 
-#: builtin/check-ignore.c:160
+#: builtin/check-ignore.c:152
 msgid "--quiet is only valid with a single pathname"
 msgstr "Die Option --quiet ist nur mit einem einzelnen Pfadnamen gültig."
 
-#: builtin/check-ignore.c:162
+#: builtin/check-ignore.c:154
 msgid "cannot have both --quiet and --verbose"
 msgstr ""
 "Die Optionen --quiet und --verbose können nicht gemeinsam verwendet werden."
 
+#: builtin/check-ignore.c:157
+msgid "--non-matching is only valid with --verbose"
+msgstr "--non-matching ist nur mit --verbose zulässig"
+
+#: builtin/check-mailmap.c:8
+msgid "git check-mailmap [options] <contact>..."
+msgstr "git check-mailmap [Optionen] <Kontakt>..."
+
+#: builtin/check-mailmap.c:13
+msgid "also read contacts from stdin"
+msgstr "liest ebenfalls Kontakte von der Standard-Eingabe"
+
+#: builtin/check-mailmap.c:24
+#, c-format
+msgid "unable to parse contact: %s"
+msgstr "Konnte Kontakt '%s' nicht parsen."
+
+#: builtin/check-mailmap.c:47
+msgid "no contacts specified"
+msgstr "keine Kontakte angegeben"
+
 #: builtin/checkout-index.c:126
 msgid "git checkout-index [options] [--] [<file>...]"
 msgstr "git checkout-index [Optionen] [--] [<Datei>...]"
 
 #: builtin/checkout-index.c:187
 msgid "check out all files in the index"
-msgstr "checkt alle Dateien in der Bereitstellung aus"
+msgstr "checkt alle Dateien in der Staging-Area aus"
 
 #: builtin/checkout-index.c:188
 msgid "force overwrite of existing files"
@@ -2808,7 +3167,7 @@ msgstr "erzwingt das Überschreiben bereits existierender Dateien"
 msgid "no warning for existing files and files not in index"
 msgstr ""
 "keine Warnung für existierende Dateien, und Dateien, die sich nicht in der "
-"Bereitstellung befinden"
+"Staging-Area befinden"
 
 #: builtin/checkout-index.c:192
 msgid "don't checkout new files"
@@ -2816,7 +3175,7 @@ msgstr "checkt keine neuen Dateien aus"
 
 #: builtin/checkout-index.c:194
 msgid "update stat information in the index file"
-msgstr "aktualisiert Dateiinformationen in der Bereitstellungsdatei"
+msgstr "aktualisiert Dateiinformationen in der Staging-Area-Datei"
 
 #: builtin/checkout-index.c:200
 msgid "read list of paths from the standard input"
@@ -2841,112 +3200,113 @@ msgstr "kopiert Dateien von dem benannten Stand"
 
 #: builtin/checkout.c:25
 msgid "git checkout [options] <branch>"
-msgstr "git checkout [Optionen] <Zweig>"
+msgstr "git checkout [Optionen] <Branch>"
 
 #: builtin/checkout.c:26
 msgid "git checkout [options] [<branch>] -- <file>..."
-msgstr "git checkout [Optionen] [Zweig>] -- <Datei>..."
+msgstr "git checkout [Optionen] [<Branch>] -- <Datei>..."
 
-#: builtin/checkout.c:116 builtin/checkout.c:149
+#: builtin/checkout.c:117 builtin/checkout.c:150
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "Pfad '%s' hat nicht unsere Version."
 
-#: builtin/checkout.c:118 builtin/checkout.c:151
+#: builtin/checkout.c:119 builtin/checkout.c:152
 #, c-format
 msgid "path '%s' does not have their version"
 msgstr "Pfad '%s' hat nicht deren Version."
 
-#: builtin/checkout.c:134
+#: builtin/checkout.c:135
 #, c-format
 msgid "path '%s' does not have all necessary versions"
 msgstr "Pfad '%s' hat nicht alle notwendigen Versionen."
 
-#: builtin/checkout.c:178
+#: builtin/checkout.c:179
 #, c-format
 msgid "path '%s' does not have necessary versions"
 msgstr "Pfad '%s' hat nicht die notwendigen Versionen."
 
-#: builtin/checkout.c:195
+#: builtin/checkout.c:196
 #, c-format
 msgid "path '%s': cannot merge"
 msgstr "Pfad '%s': kann nicht zusammenführen"
 
-#: builtin/checkout.c:212
+#: builtin/checkout.c:213
 #, c-format
 msgid "Unable to add merge result for '%s'"
-msgstr "Konnte Ergebnis der Zusammenführung von '%s' nicht hinzufügen."
+msgstr "Konnte Merge-Ergebnis von '%s' nicht hinzufügen."
 
-#: builtin/checkout.c:236 builtin/checkout.c:239 builtin/checkout.c:242
-#: builtin/checkout.c:245
+#: builtin/checkout.c:237 builtin/checkout.c:240 builtin/checkout.c:243
+#: builtin/checkout.c:246
 #, c-format
 msgid "'%s' cannot be used with updating paths"
 msgstr "'%s' kann nicht mit Pfaden verwendet werden"
 
-#: builtin/checkout.c:248 builtin/checkout.c:251
+#: builtin/checkout.c:249 builtin/checkout.c:252
 #, c-format
 msgid "'%s' cannot be used with %s"
 msgstr "'%s' kann nicht mit '%s' verwendet werden"
 
-#: builtin/checkout.c:254
+#: builtin/checkout.c:255
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
-msgstr "Kann nicht gleichzeitig Pfade aktualisieren und zu Zweig '%s' wechseln"
+msgstr ""
+"Kann nicht gleichzeitig Pfade aktualisieren und zu Branch '%s' wechseln"
 
-#: builtin/checkout.c:265 builtin/checkout.c:426
+#: builtin/checkout.c:266 builtin/checkout.c:458
 msgid "corrupt index file"
-msgstr "beschädigte Bereitstellungsdatei"
+msgstr "beschädigte Staging-Area-Datei"
 
-#: builtin/checkout.c:295 builtin/checkout.c:302
+#: builtin/checkout.c:329 builtin/checkout.c:336
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "Pfad '%s' ist nicht zusammengeführt."
 
-#: builtin/checkout.c:448
+#: builtin/checkout.c:480
 msgid "you need to resolve your current index first"
-msgstr "Sie müssen zuerst Ihre aktuelle Bereitstellung auflösen."
+msgstr "Sie müssen zuerst Ihre aktuelle Staging-Area auflösen."
 
-#: builtin/checkout.c:569
+#: builtin/checkout.c:601
 #, c-format
 msgid "Can not do reflog for '%s'\n"
 msgstr "Konnte \"reflog\" für '%s' nicht durchführen\n"
 
-#: builtin/checkout.c:602
+#: builtin/checkout.c:639
 msgid "HEAD is now at"
-msgstr "Zweigspitze (HEAD) ist jetzt bei"
+msgstr "HEAD ist jetzt bei"
 
-#: builtin/checkout.c:609
+#: builtin/checkout.c:646
 #, c-format
 msgid "Reset branch '%s'\n"
-msgstr "Setze Zweig '%s' neu\n"
+msgstr "Setze Branch '%s' neu\n"
 
-#: builtin/checkout.c:612
+#: builtin/checkout.c:649
 #, c-format
 msgid "Already on '%s'\n"
 msgstr "Bereits auf '%s'\n"
 
-#: builtin/checkout.c:616
+#: builtin/checkout.c:653
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
-msgstr "Gewechselt zu neu gesetztem Zweig '%s'\n"
+msgstr "Gewechselt zu umgesetzten Branch '%s'\n"
 
-#: builtin/checkout.c:618 builtin/checkout.c:955
+#: builtin/checkout.c:655 builtin/checkout.c:997
 #, c-format
 msgid "Switched to a new branch '%s'\n"
-msgstr "Gewechselt zu einem neuen Zweig '%s'\n"
+msgstr "Gewechselt zu einem neuem Branch '%s'\n"
 
-#: builtin/checkout.c:620
+#: builtin/checkout.c:657
 #, c-format
 msgid "Switched to branch '%s'\n"
-msgstr "Gewechselt zu Zweig '%s'\n"
+msgstr "Gewechselt zu Branch '%s'\n"
 
-#: builtin/checkout.c:676
+#: builtin/checkout.c:713
 #, c-format
 msgid " ... and %d more.\n"
 msgstr " ... und %d weitere.\n"
 
 #. The singular version
-#: builtin/checkout.c:682
+#: builtin/checkout.c:719
 #, c-format
 msgid ""
 "Warning: you are leaving %d commit behind, not connected to\n"
@@ -2959,17 +3319,17 @@ msgid_plural ""
 "\n"
 "%s\n"
 msgstr[0] ""
-"Warnung: Sie sind um %d Version hinterher, nicht verbunden zu\n"
-"einem Ihrer Zweige:\n"
+"Warnung: Sie sind um %d Commit hinterher, nicht verbunden zu\n"
+"einem Ihrer Branches:\n"
 "\n"
 "%s\n"
 msgstr[1] ""
-"Warnung: Sie sind um %d Versionen hinterher, nicht verbunden zu\n"
-"einem Ihrer Zweige:\n"
+"Warnung: Sie sind um %d Commits hinterher, nicht verbunden zu\n"
+"einem Ihrer Branches:\n"
 "\n"
 "%s\n"
 
-#: builtin/checkout.c:700
+#: builtin/checkout.c:737
 #, c-format
 msgid ""
 "If you want to keep them by creating a new branch, this may be a good time\n"
@@ -2978,243 +3338,350 @@ msgid ""
 " git branch new_branch_name %s\n"
 "\n"
 msgstr ""
-"Wenn Sie diese durch einen neuen Zweig behalten möchten, dann könnte jetzt\n"
+"Wenn Sie diese durch einen neuen Branch behalten möchten, dann könnte jetzt\n"
 "ein guter Zeitpunkt sein dies zu tun mit:\n"
 "\n"
-" git branch neuer_zweig_name %s\n"
+" git branch neuer_branch_name %s\n"
 "\n"
 
-#: builtin/checkout.c:730
+#: builtin/checkout.c:767
 msgid "internal error in revision walk"
 msgstr "interner Fehler im Revisionsgang"
 
-#: builtin/checkout.c:734
+#: builtin/checkout.c:771
 msgid "Previous HEAD position was"
-msgstr "Vorherige Position der Zweigspitze (HEAD) war"
+msgstr "Vorherige Position von HEAD war"
 
-#: builtin/checkout.c:761 builtin/checkout.c:950
+#: builtin/checkout.c:798 builtin/checkout.c:992
 msgid "You are on a branch yet to be born"
-msgstr "Sie sind auf einem Zweig, der noch geboren wird"
+msgstr "Sie sind auf einem Branch, der noch geboren wird"
 
 #. case (1)
-#: builtin/checkout.c:886
+#: builtin/checkout.c:928
 #, c-format
 msgid "invalid reference: %s"
 msgstr "Ungültige Referenz: %s"
 
 #. case (1): want a tree
-#: builtin/checkout.c:925
+#: builtin/checkout.c:967
 #, c-format
 msgid "reference is not a tree: %s"
-msgstr "Referenz ist kein Baum: %s"
+msgstr "Referenz ist kein \"Tree\"-Objekt: %s"
 
-#: builtin/checkout.c:964
+#: builtin/checkout.c:1006
 msgid "paths cannot be used with switching branches"
-msgstr "Pfade können nicht beim Wechseln von Zweigen verwendet werden"
+msgstr "Pfade können nicht beim Wechseln von Branches verwendet werden"
 
-#: builtin/checkout.c:967 builtin/checkout.c:971
+#: builtin/checkout.c:1009 builtin/checkout.c:1013
 #, c-format
 msgid "'%s' cannot be used with switching branches"
-msgstr "'%s' kann nicht beim Wechseln von Zweigen verwendet werden"
+msgstr "'%s' kann nicht beim Wechseln von Branches verwendet werden"
 
-#: builtin/checkout.c:975 builtin/checkout.c:978 builtin/checkout.c:983
-#: builtin/checkout.c:986
+#: builtin/checkout.c:1017 builtin/checkout.c:1020 builtin/checkout.c:1025
+#: builtin/checkout.c:1028
 #, c-format
 msgid "'%s' cannot be used with '%s'"
 msgstr "'%s' kann nicht mit '%s' verwendet werden"
 
-#: builtin/checkout.c:991
+#: builtin/checkout.c:1033
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
-msgstr "Kann Zweig nicht zu Nicht-Version '%s' wechseln"
+msgstr "Kann Branch nicht zu Nicht-Commit '%s' wechseln"
 
-#: builtin/checkout.c:1013 builtin/checkout.c:1015 builtin/clone.c:89
+#: builtin/checkout.c:1055 builtin/checkout.c:1057 builtin/clone.c:89
 #: builtin/remote.c:169 builtin/remote.c:171
 msgid "branch"
-msgstr "Zweig"
+msgstr "Branch"
 
-#: builtin/checkout.c:1014
+#: builtin/checkout.c:1056
 msgid "create and checkout a new branch"
-msgstr "erzeugt und checkt einen neuen Zweig aus"
+msgstr "erzeugt und checkt einen neuen Branch aus"
 
-#: builtin/checkout.c:1016
+#: builtin/checkout.c:1058
 msgid "create/reset and checkout a branch"
-msgstr "erzeugt/setzt neu und checkt einen Zweig aus"
+msgstr "erzeugt/setzt um und checkt einen Branch aus"
 
-#: builtin/checkout.c:1017
+#: builtin/checkout.c:1059
 msgid "create reflog for new branch"
-msgstr "erzeugt Referenzprotokoll für den neuen Zweig"
+msgstr "erzeugt das Reflog für den neuen Branch"
 
-#: builtin/checkout.c:1018
+#: builtin/checkout.c:1060
 msgid "detach the HEAD at named commit"
-msgstr "setzt die Zweigspitze (HEAD) zu benannter Version"
+msgstr "setzt HEAD zu benanntem Commit"
 
-#: builtin/checkout.c:1019
+#: builtin/checkout.c:1061
 msgid "set upstream info for new branch"
-msgstr "setzt Informationen zum externen Übernahmezweig für den neuen Zweig"
+msgstr "setzt Informationen zum Upstream-Branch für den neuen Branch"
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new branch"
-msgstr "neuer Zweig"
+msgstr "neuer Branch"
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new unparented branch"
-msgstr "neuer Zweig ohne Elternversion"
+msgstr "neuer Branch ohne Eltern-Commit"
 
-#: builtin/checkout.c:1022
+#: builtin/checkout.c:1064
 msgid "checkout our version for unmerged files"
 msgstr "checkt unsere Variante für nicht zusammengeführte Dateien aus"
 
-#: builtin/checkout.c:1024
+#: builtin/checkout.c:1066
 msgid "checkout their version for unmerged files"
 msgstr "checkt ihre Variante für nicht zusammengeführte Dateien aus"
 
-#: builtin/checkout.c:1026
+#: builtin/checkout.c:1068
 msgid "force checkout (throw away local modifications)"
 msgstr "erzwingt Auschecken (verwirft lokale Änderungen)"
 
-#: builtin/checkout.c:1027
+#: builtin/checkout.c:1069
 msgid "perform a 3-way merge with the new branch"
-msgstr "führt eine 3-Wege-Zusammenführung mit dem neuen Zweig aus"
+msgstr "führt einen 3-Wege-Merge mit dem neuen Branch aus"
 
-#: builtin/checkout.c:1028 builtin/merge.c:215
+#: builtin/checkout.c:1070 builtin/merge.c:232
 msgid "update ignored files (default)"
 msgstr "aktualisiert ignorierte Dateien (Standard)"
 
-#: builtin/checkout.c:1029 builtin/log.c:1147 parse-options.h:245
+#: builtin/checkout.c:1071 builtin/log.c:1208 parse-options.h:249
 msgid "style"
 msgstr "Stil"
 
-#: builtin/checkout.c:1030
+#: builtin/checkout.c:1072
 msgid "conflict style (merge or diff3)"
 msgstr "Konfliktstil (merge oder diff3)"
 
-#: builtin/checkout.c:1033
+#: builtin/checkout.c:1075
+msgid "do not limit pathspecs to sparse entries only"
+msgstr "Pfadspezifikationen ignorieren Einstellungen zum partiellen Auschecken"
+
+#: builtin/checkout.c:1077
 msgid "second guess 'git checkout no-such-branch'"
 msgstr "second guess 'git checkout no-such-branch'"
 
-#: builtin/checkout.c:1057
+#: builtin/checkout.c:1101
 msgid "-b, -B and --orphan are mutually exclusive"
 msgstr "Die Optionen -b, -B und --orphan schließen sich gegenseitig aus."
 
-#: builtin/checkout.c:1074
+#: builtin/checkout.c:1118
 msgid "--track needs a branch name"
-msgstr "Bei der Option --track muss ein Zweigname angegeben werden."
+msgstr "Bei der Option --track muss ein Branchname angegeben werden."
 
-#: builtin/checkout.c:1081
+#: builtin/checkout.c:1125
 msgid "Missing branch name; try -b"
-msgstr "Vermisse Zweignamen; versuchen Sie -b"
+msgstr "Vermisse Branchnamen; versuchen Sie -b"
 
-#: builtin/checkout.c:1116
+#: builtin/checkout.c:1160
 msgid "invalid path specification"
 msgstr "ungültige Pfadspezifikation"
 
-#: builtin/checkout.c:1123
+#: builtin/checkout.c:1167
 #, 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 ""
-"Kann nicht gleichzeitig Pfade aktualisieren und zu Zweig '%s' wechseln.\n"
-"Haben Sie beabsichtigt '%s' auszuchecken, welcher nicht als Version "
-"aufgelöst werden kann?"
+"Kann nicht gleichzeitig Pfade aktualisieren und zu Branch '%s' wechseln.\n"
+"Haben Sie beabsichtigt '%s' auszuchecken, welcher nicht als Commit aufgelöst "
+"werden kann?"
 
-#: builtin/checkout.c:1128
+#: builtin/checkout.c:1172
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
 msgstr "git checkout: --detach nimmt kein Pfad-Argument '%s'"
 
-#: builtin/checkout.c:1132
+#: builtin/checkout.c:1176
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
 msgstr ""
 "git checkout: --ours/--theirs, --force und --merge sind inkompatibel wenn\n"
-"Sie aus der Bereitstellung auschecken."
+"Sie aus der Staging-Area auschecken."
 
-#: builtin/clean.c:20
-msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
-msgstr "git clean [-d] [-f] [-n] [-q] [-e <Muster>] [-x | -X] [--] <Pfade>..."
+#: builtin/clean.c:25
+msgid ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+msgstr ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <Muster>] [-x | -X] [--] <Pfade>..."
 
-#: builtin/clean.c:24
+#: builtin/clean.c:29
 #, c-format
 msgid "Removing %s\n"
 msgstr "Lösche %s\n"
 
-#: builtin/clean.c:25
+#: builtin/clean.c:30
 #, c-format
 msgid "Would remove %s\n"
 msgstr "Würde %s löschen\n"
 
-#: builtin/clean.c:26
+#: builtin/clean.c:31
 #, c-format
 msgid "Skipping repository %s\n"
-msgstr "Überspringe Projektarchiv %s\n"
+msgstr "Überspringe Repository %s\n"
 
-#: builtin/clean.c:27
+#: builtin/clean.c:32
 #, c-format
 msgid "Would skip repository %s\n"
-msgstr "Würde Projektarchiv %s überspringen\n"
+msgstr "Würde Repository %s überspringen\n"
 
-#: builtin/clean.c:28
+#: builtin/clean.c:33
 #, c-format
 msgid "failed to remove %s"
 msgstr "Fehler beim Löschen von %s"
 
-#: builtin/clean.c:160
+#: builtin/clean.c:293
+msgid ""
+"Prompt help:\n"
+"1          - select a numbered item\n"
+"foo        - select item based on unique prefix\n"
+"           - (empty) select nothing"
+msgstr ""
+"Eingabehilfe:\n"
+"1          - nummeriertes Element auswählen\n"
+"foo        - Element anhand eines eindeutigen Prefix auswählen\n"
+"           - (leer) nichts auswählen"
+
+#: builtin/clean.c:297
+msgid ""
+"Prompt help:\n"
+"1          - select a single item\n"
+"3-5        - select a range of items\n"
+"2-3,6-9    - select multiple ranges\n"
+"foo        - select item based on unique prefix\n"
+"-...       - unselect specified items\n"
+"*          - choose all items\n"
+"           - (empty) finish selecting"
+msgstr ""
+"Eingabehilfe:\n"
+"1          - einzelnes Element auswählen\n"
+"3-5        - Bereich von Elementen auswählen\n"
+"2-3,6-9    - mehrere Bereiche auswählen\n"
+"foo        - Element anhand eines eindeutigen Prefix auswählen\n"
+"-...       - angegebenes Element abwählen\n"
+"*          - alle Elemente auswählen\n"
+"           - (leer) Auswahl beenden"
+
+#: builtin/clean.c:515
+#, c-format
+msgid "Huh (%s)?"
+msgstr "Wie bitte (%s)?"
+
+#: builtin/clean.c:658
+#, c-format
+msgid "Input ignore patterns>> "
+msgstr "Ignorier-Muster eingeben>> "
+
+#: builtin/clean.c:695
+#, c-format
+msgid "WARNING: Cannot find items matched by: %s"
+msgstr "WARNUNG: Kann keine Einträge finden die Muster entsprechen: %s"
+
+#: builtin/clean.c:716
+msgid "Select items to delete"
+msgstr "Wählen Sie Einträge zum Löschen"
+
+#: builtin/clean.c:756
+#, c-format
+msgid "remove %s? "
+msgstr "'%s' löschen? "
+
+#: builtin/clean.c:781
+msgid "Bye."
+msgstr "Tschüss."
+
+#: builtin/clean.c:789
+msgid ""
+"clean               - start cleaning\n"
+"filter by pattern   - exclude items from deletion\n"
+"select by numbers   - select items to be deleted by numbers\n"
+"ask each            - confirm each deletion (like \"rm -i\")\n"
+"quit                - stop cleaning\n"
+"help                - this screen\n"
+"?                   - help for prompt selection"
+msgstr ""
+"clean               - Clean starten\n"
+"filter by pattern   - Einträge von Löschung ausschließen\n"
+"select by numbers   - Auswahl von Einträgen über Nummern\n"
+"ask each            - jede Löschung bestätigen (wie \"rm -i\")\n"
+"quit                - Clean beenden\n"
+"help                - diese Meldung anzeigen\n"
+"?                   - Hilfe zur Auswahl mittels Eingabe anzeigen"
+
+#: builtin/clean.c:816
+msgid "*** Commands ***"
+msgstr "*** Kommandos ***"
+
+#: builtin/clean.c:817
+msgid "What now"
+msgstr "Was nun"
+
+#: builtin/clean.c:825
+msgid "Would remove the following item:"
+msgid_plural "Would remove the following items:"
+msgstr[0] "Würde das folgende Element entfernen:"
+msgstr[1] "Würde die folgenden Elemente entfernen:"
+
+#: builtin/clean.c:842
+msgid "No more files to clean, exiting."
+msgstr "Keine Dateien mehr zum Löschen, beende."
+
+#: builtin/clean.c:874
 msgid "do not print names of files removed"
 msgstr "gibt keine Namen von gelöschten Dateien aus"
 
-#: builtin/clean.c:162
+#: builtin/clean.c:876
 msgid "force"
 msgstr "erzwingt Aktion"
 
-#: builtin/clean.c:164
+#: builtin/clean.c:877
+msgid "interactive cleaning"
+msgstr "interaktives Clean"
+
+#: builtin/clean.c:879
 msgid "remove whole directories"
 msgstr "löscht ganze Verzeichnisse"
 
-#: builtin/clean.c:165 builtin/describe.c:413 builtin/grep.c:717
-#: builtin/ls-files.c:494 builtin/name-rev.c:231 builtin/show-ref.c:182
+#: builtin/clean.c:880 builtin/describe.c:420 builtin/grep.c:716
+#: builtin/ls-files.c:493 builtin/name-rev.c:315 builtin/show-ref.c:186
 msgid "pattern"
 msgstr "Muster"
 
-#: builtin/clean.c:166
+#: builtin/clean.c:881
 msgid "add <pattern> to ignore rules"
 msgstr "fügt <Muster> zu den Regeln für ignorierte Pfade hinzu"
 
-#: builtin/clean.c:167
+#: builtin/clean.c:882
 msgid "remove ignored files, too"
 msgstr "löscht auch ignorierte Dateien"
 
-#: builtin/clean.c:169
+#: builtin/clean.c:884
 msgid "remove only ignored files"
 msgstr "löscht nur ignorierte Dateien"
 
-#: builtin/clean.c:187
+#: builtin/clean.c:902
 msgid "-x and -X cannot be used together"
 msgstr "Die Optionen -x und -X können nicht gemeinsam verwendet werden."
 
-#: builtin/clean.c:191
+#: builtin/clean.c:906
 msgid ""
-"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+"clean.requireForce set to true and neither -i, -n nor -f given; refusing to "
+"clean"
 msgstr ""
-"clean.requireForce auf \"true\" gesetzt und weder -n noch -f gegeben; "
-"Säuberung verweigert"
+"clean.requireForce auf \"true\" gesetzt und weder -i, -n noch -f gegeben; "
+"\"clean\" verweigert"
 
-#: builtin/clean.c:194
+#: builtin/clean.c:909
 msgid ""
-"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
-"clean"
+"clean.requireForce defaults to true and neither -i, -n nor -f given; "
+"refusing to clean"
 msgstr ""
-"clean.requireForce standardmäßig auf \"true\" gesetzt und weder -n noch -f "
-"gegeben; Säuberung verweigert"
+"clean.requireForce standardmäßig auf \"true\" gesetzt und weder -i, -n noch -"
+"f gegeben; \"clean\" verweigert"
 
 #: builtin/clone.c:36
 msgid "git clone [options] [--] <repo> [<dir>]"
-msgstr "git clone [Optionen] [--] <Projektarchiv> [<Verzeichnis>]"
+msgstr "git clone [Optionen] [--] <Repository> [<Verzeichnis>]"
 
-#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:212
-#: builtin/push.c:436
+#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:229
+#: builtin/push.c:462
 msgid "force progress reporting"
 msgstr "erzwingt Fortschrittsanzeige"
 
@@ -3224,27 +3691,27 @@ msgstr "kein Auschecken"
 
 #: builtin/clone.c:67 builtin/clone.c:69 builtin/init-db.c:488
 msgid "create a bare repository"
-msgstr "erstellt ein bloßes Projektarchiv"
+msgstr "erstellt ein Bare-Repository"
 
 #: builtin/clone.c:72
 msgid "create a mirror repository (implies bare)"
-msgstr "erstellt ein Spiegelarchiv (impliziert bloßes Projektarchiv)"
+msgstr "erstellt ein Spiegelarchiv (impliziert --bare)"
 
 #: builtin/clone.c:74
 msgid "to clone from a local repository"
-msgstr "um von einem lokalen Projektarchiv zu klonen"
+msgstr "um von einem lokalen Repository zu klonen"
 
 #: builtin/clone.c:76
 msgid "don't use local hardlinks, always copy"
-msgstr "verwendet lokal keine harten Links, immer Kopien"
+msgstr "verwendet lokal keine harten Verweise, immer Kopien"
 
 #: builtin/clone.c:78
 msgid "setup as shared repository"
-msgstr "Einrichtung als verteiltes Projektarchiv"
+msgstr "Einrichtung als verteiltes Repository"
 
 #: builtin/clone.c:80 builtin/clone.c:82
 msgid "initialize submodules in the clone"
-msgstr "initialisiert Unterprojekte im Klon"
+msgstr "initialisiert Submodule im Klon"
 
 #: builtin/clone.c:83 builtin/init-db.c:485
 msgid "template-directory"
@@ -3256,7 +3723,7 @@ msgstr "Verzeichnis, von welchem die Vorlagen verwendet werden"
 
 #: builtin/clone.c:86
 msgid "reference repository"
-msgstr "referenziert Projektarchiv"
+msgstr "referenziert Repository"
 
 #: builtin/clone.c:87 builtin/column.c:26 builtin/merge-file.c:44
 msgid "name"
@@ -3264,18 +3731,17 @@ msgstr "Name"
 
 #: builtin/clone.c:88
 msgid "use <name> instead of 'origin' to track upstream"
-msgstr "verwendet <Name> statt 'origin' für externes Projektarchiv"
+msgstr "verwendet <Name> statt 'origin' für Upstream-Repository"
 
 #: builtin/clone.c:90
 msgid "checkout <branch> instead of the remote's HEAD"
-msgstr ""
-"checkt <Zweig> aus, anstatt Zweigspitze (HEAD) des externen Projektarchivs"
+msgstr "checkt <Branch> aus, anstatt HEAD des Remote-Repositories"
 
 #: builtin/clone.c:92
 msgid "path to git-upload-pack on the remote"
 msgstr "Pfad zu \"git-upload-pack\" auf der Gegenseite"
 
-#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:662
+#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:661
 msgid "depth"
 msgstr "Tiefe"
 
@@ -3285,7 +3751,7 @@ msgstr "erstellt einen flachen Klon mit dieser Tiefe"
 
 #: builtin/clone.c:96
 msgid "clone only one branch, HEAD or --branch"
-msgstr "klont nur einen Zweig, Zweigspitze (HEAD) oder --branch"
+msgstr "klont nur einen Branch, HEAD oder --branch"
 
 #: builtin/clone.c:97 builtin/init-db.c:494
 msgid "gitdir"
@@ -3293,7 +3759,7 @@ msgstr ".git-Verzeichnis"
 
 #: builtin/clone.c:98 builtin/init-db.c:495
 msgid "separate git dir from working tree"
-msgstr "separiert Git-Verzeichnis vom Arbeitsbaum"
+msgstr "separiert Git-Verzeichnis vom Arbeitsverzeichnis"
 
 #: builtin/clone.c:99
 msgid "key=value"
@@ -3301,130 +3767,162 @@ msgstr "Schlüssel=Wert"
 
 #: builtin/clone.c:100
 msgid "set config inside the new repository"
-msgstr "setzt Konfiguration innerhalb des neuen Projektarchivs"
+msgstr "setzt Konfiguration innerhalb des neuen Repositories"
 
-#: builtin/clone.c:243
+#: builtin/clone.c:253
 #, c-format
-msgid "reference repository '%s' is not a local directory."
-msgstr "Referenziertes Projektarchiv '%s' ist kein lokales Verzeichnis."
+msgid "reference repository '%s' is not a local repository."
+msgstr "Referenziertes Repository '%s' ist kein lokales Repository."
 
-#: builtin/clone.c:306
+#: builtin/clone.c:316
 #, c-format
 msgid "failed to create directory '%s'"
 msgstr "Fehler beim Erstellen von Verzeichnis '%s'"
 
-#: builtin/clone.c:308 builtin/diff.c:77
+#: builtin/clone.c:318 builtin/diff.c:77
 #, c-format
 msgid "failed to stat '%s'"
 msgstr "Konnte '%s' nicht lesen"
 
-#: builtin/clone.c:310
+#: builtin/clone.c:320
 #, c-format
 msgid "%s exists and is not a directory"
 msgstr "%s existiert und ist kein Verzeichnis"
 
-#: builtin/clone.c:324
+#: builtin/clone.c:334
 #, c-format
 msgid "failed to stat %s\n"
 msgstr "Konnte %s nicht lesen\n"
 
-#: builtin/clone.c:346
+#: builtin/clone.c:356
 #, c-format
 msgid "failed to create link '%s'"
-msgstr "Konnte Verknüpfung '%s' nicht erstellen"
+msgstr "Konnte Verweis '%s' nicht erstellen"
 
-#: builtin/clone.c:350
+#: builtin/clone.c:360
 #, c-format
 msgid "failed to copy file to '%s'"
 msgstr "Konnte Datei nicht nach '%s' kopieren"
 
-#: builtin/clone.c:373
+#: builtin/clone.c:383
 #, c-format
 msgid "done.\n"
 msgstr "Fertig.\n"
 
-#: builtin/clone.c:443
+#: builtin/clone.c:396
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry the checkout with 'git checkout -f HEAD'\n"
+msgstr ""
+"Klonen erfolgreich, Auschecken ist aber fehlgeschlagen.\n"
+"Sie können mit 'git status' prüfen, was ausgecheckt worden ist\n"
+"und das Auschecken mit 'git checkout -f HEAD' erneut versuchen.\n"
+
+#: builtin/clone.c:475
 #, c-format
 msgid "Could not find remote branch %s to clone."
-msgstr "Konnte zu klonenden externer Zweig %s nicht finden."
+msgstr "Konnte zu klonenden Remote-Branch %s nicht finden."
+
+#: builtin/clone.c:555
+#, c-format
+msgid "Checking connectivity... "
+msgstr "Prüfe Konnektivität... "
+
+#: builtin/clone.c:558
+msgid "remote did not send all necessary objects"
+msgstr "Remote-Repository hat nicht alle erforderlichen Objekte gesendet."
+
+#: builtin/clone.c:560
+#, c-format
+msgid "done\n"
+msgstr "Fertig\n"
 
-#: builtin/clone.c:552
+#: builtin/clone.c:621
 msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
 msgstr ""
-"Externe Zweigspitze (HEAD) bezieht sich auf eine nicht existierende Referenz "
-"und kann nicht ausgecheckt werden.\n"
+"Externer HEAD bezieht sich auf eine nicht existierende Referenz und kann "
+"nicht ausgecheckt werden.\n"
 
-#: builtin/clone.c:690
+#: builtin/clone.c:652
+msgid "unable to checkout working tree"
+msgstr "Arbeitsverzeichnis konnte nicht ausgecheckt werden"
+
+#: builtin/clone.c:760
 msgid "Too many arguments."
 msgstr "Zu viele Argumente."
 
-#: builtin/clone.c:694
+#: builtin/clone.c:764
 msgid "You must specify a repository to clone."
-msgstr "Sie müssen ein Projektarchiv zum Klonen angeben."
+msgstr "Sie müssen ein Repository zum Klonen angeben."
 
-#: builtin/clone.c:705
+#: builtin/clone.c:775
 #, c-format
 msgid "--bare and --origin %s options are incompatible."
 msgstr "Die Optionen --bare und --origin %s sind inkompatibel."
 
-#: builtin/clone.c:708
+#: builtin/clone.c:778
 msgid "--bare and --separate-git-dir are incompatible."
 msgstr "Die Optionen --bare und --separate-git-dir sind inkompatibel."
 
-#: builtin/clone.c:721
+#: builtin/clone.c:791
 #, c-format
 msgid "repository '%s' does not exist"
-msgstr "Projektarchiv '%s' existiert nicht."
+msgstr "Repository '%s' existiert nicht."
 
-#: builtin/clone.c:726
+#: builtin/clone.c:796
 msgid "--depth is ignored in local clones; use file:// instead."
 msgstr ""
 "Die Option --depth wird in lokalen Klonen ignoriert; benutzen Sie "
 "stattdessen file://"
 
-#: builtin/clone.c:736
+#: builtin/clone.c:798
+msgid "--local is ignored"
+msgstr "--local wird ignoriert"
+
+#: builtin/clone.c:808
 #, c-format
 msgid "destination path '%s' already exists and is not an empty directory."
 msgstr "Zielpfad '%s' existiert bereits und ist kein leeres Verzeichnis."
 
-#: builtin/clone.c:746
+#: builtin/clone.c:818
 #, c-format
 msgid "working tree '%s' already exists."
-msgstr "Arbeitsbaum '%s' existiert bereits."
+msgstr "Arbeitsverzeichnis '%s' existiert bereits."
 
-#: builtin/clone.c:759 builtin/clone.c:771
+#: builtin/clone.c:831 builtin/clone.c:843
 #, c-format
 msgid "could not create leading directories of '%s'"
 msgstr "Konnte führende Verzeichnisse von '%s' nicht erstellen."
 
-#: builtin/clone.c:762
+#: builtin/clone.c:834
 #, c-format
 msgid "could not create work tree dir '%s'."
 msgstr "Konnte Arbeitsverzeichnis '%s' nicht erstellen."
 
-#: builtin/clone.c:781
+#: builtin/clone.c:853
 #, c-format
 msgid "Cloning into bare repository '%s'...\n"
-msgstr "Klone in bloßes Projektarchiv '%s'...\n"
+msgstr "Klone in Bare-Repository '%s'...\n"
 
-#: builtin/clone.c:783
+#: builtin/clone.c:855
 #, c-format
 msgid "Cloning into '%s'...\n"
 msgstr "Klone nach '%s'...\n"
 
-#: builtin/clone.c:818
+#: builtin/clone.c:890
 #, c-format
 msgid "Don't know how to clone %s"
 msgstr "Weiß nicht wie %s zu klonen ist."
 
-#: builtin/clone.c:867
+#: builtin/clone.c:942
 #, c-format
 msgid "Remote branch %s not found in upstream %s"
-msgstr "externer Zweig %s nicht im anderen Projektarchiv %s gefunden"
+msgstr "Remote-Branch %s nicht im Upstream-Repository %s gefunden"
 
-#: builtin/clone.c:874
+#: builtin/clone.c:949
 msgid "You appear to have cloned an empty repository."
-msgstr "Sie scheinen ein leeres Projektarchiv geklont zu haben."
+msgstr "Sie scheinen ein leeres Repository geklont zu haben."
 
 #: builtin/column.c:9
 msgid "git column [options]"
@@ -3458,15 +3956,15 @@ msgstr "Abstand zwischen Spalten"
 msgid "--command must be the first argument"
 msgstr "Die Option --command muss an erster Stelle stehen."
 
-#: builtin/commit.c:34
+#: builtin/commit.c:35
 msgid "git commit [options] [--] <pathspec>..."
 msgstr "git commit [Optionen] [--] <Pfadspezifikation>..."
 
-#: builtin/commit.c:39
+#: builtin/commit.c:40
 msgid "git status [options] [--] <pathspec>..."
 msgstr "git status [Optionen] [--] <Pfadspezifikation>..."
 
-#: builtin/commit.c:44
+#: builtin/commit.c:45
 msgid ""
 "Your name and email address were configured automatically based\n"
 "on your username and hostname. Please check that they are accurate.\n"
@@ -3487,129 +3985,144 @@ msgstr ""
 "    git config --global user.name \"Ihr Name\"\n"
 "    git config --global user.email ihre@emailadresse.de\n"
 "\n"
-"Nachdem Sie das getan hast, können Sie Ihre Identität für diese Version "
+"Nachdem Sie das getan hast, können Sie Ihre Identität für diesen Commit "
 "ändern mit:\n"
 "\n"
 "    git commit --amend --reset-author\n"
 
-#: builtin/commit.c:56
+#: builtin/commit.c:57
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
 "remove the commit entirely with \"git reset HEAD^\".\n"
 msgstr ""
-"Sie fragten die jüngste Version nachzubessern, aber das würde diese leer\n"
-"machen. Sie können Ihr Kommando mit --allow-empty wiederholen, oder die\n"
-"Version mit \"git reset HEAD^\" vollständig entfernen.\n"
+"Sie fragten den jüngsten Commit nachzubessern, aber das würde diesen leer\n"
+"machen. Sie können Ihr Kommando mit --allow-empty wiederholen, oder diesen\n"
+"Commit mit \"git reset HEAD^\" vollständig entfernen.\n"
 
-#: builtin/commit.c:61
+#: builtin/commit.c:62
 msgid ""
 "The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
 "If you wish to commit it anyway, use:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"Otherwise, please use 'git reset'\n"
 msgstr ""
 "Der letzte \"cherry-pick\" ist jetzt leer, möglicherweise durch eine "
 "Konfliktauflösung.\n"
-"Wenn Sie dies trotzdem eintragen wollen, benutzen Sie:\n"
+"Wenn Sie dies trotzdem committen wollen, benutzen Sie:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"Andernfalls benutzen Sie bitte 'git reset'\n"
 
-#: builtin/commit.c:258
+#: builtin/commit.c:69
+msgid "Otherwise, please use 'git reset'\n"
+msgstr "Andernfalls benutzen Sie bitte 'git reset'\n"
+
+#: builtin/commit.c:72
+msgid ""
+"If you wish to skip this commit, use:\n"
+"\n"
+"    git reset\n"
+"\n"
+"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
+"the remaining commits.\n"
+msgstr ""
+"Wenn Sie diesen Commit auslassen möchten, benutzen Sie:\n"
+"\n"
+"    git reset\n"
+"\n"
+"Benutzen Sie anschließend \"git cherry-pick --continue\", um die\n"
+"Cherry-Pick-Operation mit den verbleibenden Commits fortzusetzen.\n"
+
+#: builtin/commit.c:279
 msgid "failed to unpack HEAD tree object"
-msgstr "Fehler beim Entpacken des Baum-Objektes der Zweigspitze (HEAD)."
+msgstr "Fehler beim Entpacken des \"Tree\"-Objektes von HEAD."
 
-#: builtin/commit.c:300
+#: builtin/commit.c:321
 msgid "unable to create temporary index"
-msgstr "Konnte temporäre Bereitstellung nicht erstellen."
+msgstr "Konnte temporäre Staging-Area nicht erstellen."
 
-#: builtin/commit.c:306
+#: builtin/commit.c:327
 msgid "interactive add failed"
 msgstr "interaktives Hinzufügen fehlgeschlagen"
 
-#: builtin/commit.c:339 builtin/commit.c:360 builtin/commit.c:410
+#: builtin/commit.c:360 builtin/commit.c:381 builtin/commit.c:431
 msgid "unable to write new_index file"
 msgstr "Konnte new_index Datei nicht schreiben"
 
-#: builtin/commit.c:391
+#: builtin/commit.c:412
 msgid "cannot do a partial commit during a merge."
-msgstr ""
-"Kann keine partielle Eintragung durchführen, während eine Zusammenführung im "
-"Gange ist."
+msgstr "Kann keinen Teil-Commit durchführen, während ein Merge im Gange ist."
 
-#: builtin/commit.c:393
+#: builtin/commit.c:414
 msgid "cannot do a partial commit during a cherry-pick."
 msgstr ""
-"Kann keine partielle Eintragung durchführen, während \"cherry-pick\" im "
-"Gange ist."
+"Kann keinen Teil-Commit durchführen, während \"cherry-pick\" im Gange ist."
 
-#: builtin/commit.c:403
+#: builtin/commit.c:424
 msgid "cannot read the index"
-msgstr "Kann Bereitstellung nicht lesen"
+msgstr "Kann Staging-Area nicht lesen"
 
-#: builtin/commit.c:423
+#: builtin/commit.c:444
 msgid "unable to write temporary index file"
-msgstr "Konnte temporäre Bereitstellungsdatei nicht schreiben."
+msgstr "Konnte temporäre Staging-Area-Datei nicht schreiben."
 
-#: builtin/commit.c:511 builtin/commit.c:517
+#: builtin/commit.c:535 builtin/commit.c:541
 #, c-format
 msgid "invalid commit: %s"
-msgstr "Ungültige Version: %s"
+msgstr "Ungültiger Commit: %s"
 
-#: builtin/commit.c:540
+#: builtin/commit.c:563
 msgid "malformed --author parameter"
 msgstr "Fehlerhafter --author Parameter"
 
-#: builtin/commit.c:560
+#: builtin/commit.c:583
 #, c-format
 msgid "Malformed ident string: '%s'"
-msgstr "Fehlerhafte Identifikations-String: '%s'"
+msgstr "Fehlerhafter Ident-String: '%s'"
 
-#: builtin/commit.c:598 builtin/commit.c:631 builtin/commit.c:954
+#: builtin/commit.c:621 builtin/commit.c:654 builtin/commit.c:982
 #, c-format
 msgid "could not lookup commit %s"
-msgstr "Konnte Version %s nicht nachschlagen"
+msgstr "Konnte Commit %s nicht nachschlagen"
 
-#: builtin/commit.c:610 builtin/shortlog.c:272
+#: builtin/commit.c:633 builtin/shortlog.c:271
 #, c-format
 msgid "(reading log message from standard input)\n"
 msgstr "(lese Log-Nachricht von Standard-Eingabe)\n"
 
-#: builtin/commit.c:612
+#: builtin/commit.c:635
 msgid "could not read log from standard input"
 msgstr "Konnte Log nicht von Standard-Eingabe lesen."
 
-#: builtin/commit.c:616
+#: builtin/commit.c:639
 #, c-format
 msgid "could not read log file '%s'"
 msgstr "Konnte Log-Datei '%s' nicht lesen"
 
-#: builtin/commit.c:622
+#: builtin/commit.c:645
 msgid "commit has empty message"
-msgstr "Version hat eine leere Beschreibung"
+msgstr "Commit hat eine leere Beschreibung"
 
-#: builtin/commit.c:638
+#: builtin/commit.c:661
 msgid "could not read MERGE_MSG"
 msgstr "Konnte MERGE_MSG nicht lesen"
 
-#: builtin/commit.c:642
+#: builtin/commit.c:665
 msgid "could not read SQUASH_MSG"
 msgstr "Konnte SQUASH_MSG nicht lesen"
 
-#: builtin/commit.c:646
+#: builtin/commit.c:669
 #, c-format
 msgid "could not read '%s'"
 msgstr "Konnte '%s' nicht lesen"
 
-#: builtin/commit.c:707
+#: builtin/commit.c:730
 msgid "could not write commit template"
-msgstr "Konnte Versionsvorlage nicht schreiben"
+msgstr "Konnte Commit-Vorlage nicht schreiben"
 
-#: builtin/commit.c:718
+#: builtin/commit.c:741
 #, c-format
 msgid ""
 "\n"
@@ -3619,12 +4132,12 @@ msgid ""
 "and try again.\n"
 msgstr ""
 "\n"
-"Es sieht so aus, als tragen Sie eine Zusammenführung ein.\n"
+"Es sieht so aus, als committen Sie einen Merge.\n"
 "Falls das nicht korrekt ist, löschen Sie bitte die Datei\n"
 "\t%s\n"
 "und versuchen Sie es erneut.\n"
 
-#: builtin/commit.c:723
+#: builtin/commit.c:746
 #, c-format
 msgid ""
 "\n"
@@ -3634,406 +4147,406 @@ msgid ""
 "and try again.\n"
 msgstr ""
 "\n"
-"Es sieht so aus, als tragen Sie ein \"cherry-pick\" ein.\n"
+"Es sieht so aus, als committen Sie einen \"cherry-pick\".\n"
 "Falls das nicht korrekt ist, löschen Sie bitte die Datei\n"
 "\t%s\n"
 "und versuchen Sie es erneut.\n"
 
-#: builtin/commit.c:735
+#: builtin/commit.c:758
 #, 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 ""
-"Bitte geben Sie eine Versionsbeschreibung für Ihre Änderungen ein. Zeilen,\n"
-"die mit '%c' beginnen, werden ignoriert, und eine leere "
-"Versionsbeschreibung\n"
-"bricht die Eintragung ab.\n"
+"Bitte geben Sie eine Commit-Beschreibung für Ihre Änderungen ein. Zeilen,\n"
+"die mit '%c' beginnen, werden ignoriert, und eine leere Beschreibung\n"
+"bricht den Commit ab.\n"
 
-#: builtin/commit.c:740
+#: builtin/commit.c:763
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
 "with '%c' will be kept; you may remove them yourself if you want to.\n"
 "An empty message aborts the commit.\n"
 msgstr ""
-"Bitte geben Sie eine Versionsbeschreibung für Ihre Änderungen ein. Zeilen, "
+"Bitte geben Sie eine Commit-Beschreibung für Ihre Änderungen ein. Zeilen, "
 "die\n"
 "mit '%c' beginnen, werden beibehalten; wenn Sie möchten, können Sie diese "
 "entfernen.\n"
-"Eine leere Versionsbeschreibung bricht die Eintragung ab.\n"
+"Eine leere Beschreibung bricht den Commit ab.\n"
 
-#: builtin/commit.c:753
+#: builtin/commit.c:776
 #, c-format
 msgid "%sAuthor:    %s"
 msgstr "%sAutor:    %s"
 
-#: builtin/commit.c:760
+#: builtin/commit.c:783
 #, c-format
 msgid "%sCommitter: %s"
-msgstr "%sEintragender: %s"
+msgstr "%sCommit-Ersteller: %s"
 
-#: builtin/commit.c:780
+#: builtin/commit.c:803
 msgid "Cannot read index"
-msgstr "Kann Bereitstellung nicht lesen"
+msgstr "Kann Staging-Area nicht lesen"
 
-#: builtin/commit.c:817
+#: builtin/commit.c:845
 msgid "Error building trees"
-msgstr "Fehler beim Erzeugen der Zweige"
+msgstr "Fehler beim Erzeugen der \"Tree\"-Objekte"
 
-#: builtin/commit.c:832 builtin/tag.c:359
+#: builtin/commit.c:860 builtin/tag.c:359
 #, c-format
 msgid "Please supply the message using either -m or -F option.\n"
-msgstr "Bitte liefere eine Beschreibung entweder mit der Option -m oder -F.\n"
+msgstr ""
+"Bitte liefern Sie eine Beschreibung entweder mit der Option -m oder -F.\n"
 
-#: builtin/commit.c:929
+#: builtin/commit.c:957
 #, c-format
 msgid "No existing author found with '%s'"
 msgstr "Kein existierender Autor mit '%s' gefunden."
 
-#: builtin/commit.c:944 builtin/commit.c:1138
+#: builtin/commit.c:972 builtin/commit.c:1208
 #, c-format
 msgid "Invalid untracked files mode '%s'"
 msgstr "Ungültiger Modus '%s' für unbeobachtete Dateien"
 
-#: builtin/commit.c:974
+#: builtin/commit.c:1009
+msgid "--long and -z are incompatible"
+msgstr "Die Optionen --long und -z sind inkompatibel."
+
+#: builtin/commit.c:1039
 msgid "Using both --reset-author and --author does not make sense"
 msgstr ""
 "Die Optionen --reset-author und --author können nicht gemeinsam verwendet "
 "werden."
 
-#: builtin/commit.c:985
+#: builtin/commit.c:1050
 msgid "You have nothing to amend."
-msgstr "Sie haben nichts zum nachbessern."
+msgstr "Sie haben nichts für \"--amend\"."
 
-#: builtin/commit.c:988
+#: builtin/commit.c:1053
 msgid "You are in the middle of a merge -- cannot amend."
-msgstr "Eine Zusammenführung ist im Gange -- kann nicht nachbessern."
+msgstr "Ein Merge ist im Gange -- kann \"--amend\" nicht ausführen."
 
-#: builtin/commit.c:990
+#: builtin/commit.c:1055
 msgid "You are in the middle of a cherry-pick -- cannot amend."
-msgstr "\"cherry-pick\" ist im Gange -- kann nicht nachbessern."
+msgstr "\"cherry-pick\" ist im Gange -- kann \"--amend\" nicht ausführen."
 
-#: builtin/commit.c:993
+#: builtin/commit.c:1058
 msgid "Options --squash and --fixup cannot be used together"
 msgstr ""
 "Die Optionen --squash und --fixup können nicht gemeinsam verwendet werden."
 
-#: builtin/commit.c:1003
+#: builtin/commit.c:1068
 msgid "Only one of -c/-C/-F/--fixup can be used."
 msgstr "Es kann nur eine Option von -c/-C/-F/--fixup verwendet werden."
 
-#: builtin/commit.c:1005
+#: builtin/commit.c:1070
 msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
 msgstr "Die Option -m kann nicht mit -c/-C/-F/--fixup kombiniert werden."
 
-#: builtin/commit.c:1013
+#: builtin/commit.c:1078
 msgid "--reset-author can be used only with -C, -c or --amend."
 msgstr ""
 "Die Option --reset--author kann nur mit -C, -c oder --amend verwendet werden."
 
-#: builtin/commit.c:1030
+#: builtin/commit.c:1095
 msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
 msgstr ""
 "Es kann nur eine Option von --include/--only/--all/--interactive/--patch "
 "verwendet werden."
 
-#: builtin/commit.c:1032
+#: builtin/commit.c:1097
 msgid "No paths with --include/--only does not make sense."
 msgstr ""
 "Die Optionen --include und --only können nur mit der Angabe von Pfaden "
 "verwendet werden."
 
-#: builtin/commit.c:1034
+#: builtin/commit.c:1099
 msgid "Clever... amending the last one with dirty index."
 msgstr ""
-"Klug... die letzte Version mit einer unsauberen Bereitstellung nachbessern."
+"Klug... den letzten Commit mit einer geänderten Staging-Area nachbessern."
 
-#: builtin/commit.c:1036
+#: builtin/commit.c:1101
 msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
 msgstr ""
 "Explizite Pfade ohne -i oder -o angegeben; unter der Annahme von --only "
 "Pfaden..."
 
-#: builtin/commit.c:1046 builtin/tag.c:575
+#: builtin/commit.c:1111 builtin/tag.c:575
 #, c-format
 msgid "Invalid cleanup mode %s"
 msgstr "Ungültiger \"cleanup\" Modus %s"
 
-#: builtin/commit.c:1051
+#: builtin/commit.c:1116
 msgid "Paths with -a does not make sense."
 msgstr "Die Option -a kann nur mit der Angabe von Pfaden verwendet werden."
 
-#: builtin/commit.c:1057 builtin/commit.c:1192
-msgid "--long and -z are incompatible"
-msgstr "Die Optionen --long und -z sind inkompatibel."
-
-#: builtin/commit.c:1152 builtin/commit.c:1388
+#: builtin/commit.c:1222 builtin/commit.c:1455
 msgid "show status concisely"
 msgstr "zeigt Status im Kurzformat"
 
-#: builtin/commit.c:1154 builtin/commit.c:1390
+#: builtin/commit.c:1224 builtin/commit.c:1457
 msgid "show branch information"
-msgstr "zeigt Zweiginformationen"
+msgstr "zeigt Branchinformationen"
 
-#: builtin/commit.c:1156 builtin/commit.c:1392 builtin/push.c:426
+#: builtin/commit.c:1226 builtin/commit.c:1459 builtin/push.c:452
 msgid "machine-readable output"
 msgstr "maschinenlesbare Ausgabe"
 
-#: builtin/commit.c:1159 builtin/commit.c:1394
+#: builtin/commit.c:1229 builtin/commit.c:1461
 msgid "show status in long format (default)"
 msgstr "zeigt Status im Langformat (Standard)"
 
-#: builtin/commit.c:1162 builtin/commit.c:1397
+#: builtin/commit.c:1232 builtin/commit.c:1464
 msgid "terminate entries with NUL"
 msgstr "schließt Einträge mit NUL-Zeichen ab"
 
-#: builtin/commit.c:1164 builtin/commit.c:1400 builtin/fast-export.c:647
-#: builtin/fast-export.c:650 builtin/tag.c:459
+#: builtin/commit.c:1234 builtin/commit.c:1467 builtin/fast-export.c:667
+#: builtin/fast-export.c:670 builtin/tag.c:459
 msgid "mode"
 msgstr "Modus"
 
-#: builtin/commit.c:1165 builtin/commit.c:1400
+#: builtin/commit.c:1235 builtin/commit.c:1467
 msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
 msgstr ""
 "zeigt nicht beobachtete Dateien, optionale Modi: all, normal, no. (Standard: "
 "all)"
 
-#: builtin/commit.c:1168
+#: builtin/commit.c:1238
 msgid "show ignored files"
 msgstr "zeigt ignorierte Dateien"
 
-#: builtin/commit.c:1169 parse-options.h:151
+#: builtin/commit.c:1239 parse-options.h:154
 msgid "when"
 msgstr "wann"
 
-#: builtin/commit.c:1170
+#: builtin/commit.c:1240
 msgid ""
 "ignore changes to submodules, optional when: all, dirty, untracked. "
 "(Default: all)"
 msgstr ""
-"ignoriert Änderungen in Unterprojekten, optional wenn: all, dirty, "
-"untracked. (Standard: all)"
+"ignoriert Änderungen in Submodulen, optional wenn: all, dirty, untracked. "
+"(Standard: all)"
 
-#: builtin/commit.c:1172
+#: builtin/commit.c:1242
 msgid "list untracked files in columns"
 msgstr "listet unbeobachtete Dateien in Spalten auf"
 
-#: builtin/commit.c:1246
+#: builtin/commit.c:1313
 msgid "couldn't look up newly created commit"
-msgstr "Konnte neu erstellte Version nicht nachschlagen."
+msgstr "Konnte neu erstellten Commit nicht nachschlagen."
 
-#: builtin/commit.c:1248
+#: builtin/commit.c:1315
 msgid "could not parse newly created commit"
-msgstr "Konnte neulich erstellte Version nicht analysieren."
+msgstr "Konnte neulich erstellten Commit nicht analysieren."
 
-#: builtin/commit.c:1289
+#: builtin/commit.c:1356
 msgid "detached HEAD"
-msgstr "losgelöste Zweigspitze (HEAD)"
+msgstr "losgelöster HEAD"
 
-#: builtin/commit.c:1291
+#: builtin/commit.c:1358
 msgid " (root-commit)"
-msgstr " (Basis-Version)"
+msgstr " (Basis-Commit)"
 
-#: builtin/commit.c:1358
+#: builtin/commit.c:1425
 msgid "suppress summary after successful commit"
-msgstr "unterdrückt Zusammenfassung nach erfolgreicher Eintragung"
+msgstr "unterdrückt Zusammenfassung nach erfolgreichem Commit"
 
-#: builtin/commit.c:1359
+#: builtin/commit.c:1426
 msgid "show diff in commit message template"
-msgstr "zeigt Unterschiede in Versionsbeschreibungsvorlage an"
+msgstr "zeigt Unterschiede in Commit-Beschreibungsvorlage an"
 
-#: builtin/commit.c:1361
+#: builtin/commit.c:1428
 msgid "Commit message options"
-msgstr "Optionen für Versionsbeschreibung"
+msgstr "Optionen für Commit-Beschreibung"
 
-#: builtin/commit.c:1362 builtin/tag.c:457
+#: builtin/commit.c:1429 builtin/tag.c:457
 msgid "read message from file"
 msgstr "liest Beschreibung von Datei"
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "author"
 msgstr "Autor"
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "override author for commit"
-msgstr "überschreibt Autor von Version"
+msgstr "überschreibt Autor eines Commits"
 
-#: builtin/commit.c:1364 builtin/gc.c:178
+#: builtin/commit.c:1431 builtin/gc.c:178
 msgid "date"
 msgstr "Datum"
 
-#: builtin/commit.c:1364
+#: builtin/commit.c:1431
 msgid "override date for commit"
-msgstr "überschreibt Datum von Version"
+msgstr "überschreibt Datum eines Commits"
 
-#: builtin/commit.c:1365 builtin/merge.c:206 builtin/notes.c:533
-#: builtin/notes.c:690 builtin/tag.c:455
+#: builtin/commit.c:1432 builtin/merge.c:223 builtin/notes.c:405
+#: builtin/notes.c:562 builtin/tag.c:455
 msgid "message"
 msgstr "Beschreibung"
 
-#: builtin/commit.c:1365
+#: builtin/commit.c:1432
 msgid "commit message"
-msgstr "Versionsbeschreibung"
+msgstr "Commit-Beschreibung"
 
-#: builtin/commit.c:1366
+#: builtin/commit.c:1433
 msgid "reuse and edit message from specified commit"
-msgstr "verwendet wieder und editiert Beschreibung von der angegebenen Version"
+msgstr "verwendet wieder und editiert Beschreibung des angegebenen Commits"
 
-#: builtin/commit.c:1367
+#: builtin/commit.c:1434
 msgid "reuse message from specified commit"
-msgstr "verwendet Beschreibung der angegebenen Version wieder"
+msgstr "verwendet Beschreibung des angegebenen Commits wieder"
 
-#: builtin/commit.c:1368
+#: builtin/commit.c:1435
 msgid "use autosquash formatted message to fixup specified commit"
 msgstr ""
-"verwendet eine automatisch zusammengesetzte Beschreibung zum Nachbessern der "
-"angegebenen Version"
+"verwendet eine automatisch zusammengesetzte Beschreibung zum Nachbessern des "
+"angegebenen Commits"
 
-#: builtin/commit.c:1369
+#: builtin/commit.c:1436
 msgid "use autosquash formatted message to squash specified commit"
 msgstr ""
-"verwendet eine automatisch zusammengesetzte Beschreibung zum Zusammenführen "
-"der angegebenen Version"
+"verwendet eine automatisch zusammengesetzte Beschreibung beim \"squash\" des "
+"angegebenen Commits"
 
-#: builtin/commit.c:1370
+#: builtin/commit.c:1437
 msgid "the commit is authored by me now (used with -C/-c/--amend)"
-msgstr "Setzt Sie als Autor der Version (verwendet mit -C/-c/--amend)"
+msgstr "Setzt Sie als Autor des Commits (verwendet mit -C/-c/--amend)"
 
-#: builtin/commit.c:1371 builtin/log.c:1102 builtin/revert.c:109
+#: builtin/commit.c:1438 builtin/log.c:1160 builtin/revert.c:111
 msgid "add Signed-off-by:"
 msgstr "fügt 'Signed-off-by:'-Zeile hinzu"
 
-#: builtin/commit.c:1372
+#: builtin/commit.c:1439
 msgid "use specified template file"
 msgstr "verwendet angegebene Vorlagendatei"
 
-#: builtin/commit.c:1373
+#: builtin/commit.c:1440
 msgid "force edit of commit"
-msgstr "erzwingt Bearbeitung der Version"
+msgstr "erzwingt Bearbeitung des Commits"
 
-#: builtin/commit.c:1374
+#: builtin/commit.c:1441
 msgid "default"
 msgstr "Standard"
 
-#: builtin/commit.c:1374 builtin/tag.c:460
+#: builtin/commit.c:1441 builtin/tag.c:460
 msgid "how to strip spaces and #comments from message"
 msgstr ""
 "wie Leerzeichen und #Kommentare von der Beschreibung getrennt werden sollen"
 
-#: builtin/commit.c:1375
+#: builtin/commit.c:1442
 msgid "include status in commit message template"
-msgstr "fügt Status in die Versionsbeschreibungsvorlage ein"
+msgstr "fügt Status in die Commit-Beschreibungsvorlage ein"
 
-#: builtin/commit.c:1376 builtin/merge.c:213 builtin/tag.c:461
+#: builtin/commit.c:1443 builtin/merge.c:230 builtin/tag.c:461
 msgid "key id"
 msgstr "Schlüssel-ID"
 
-#: builtin/commit.c:1377 builtin/merge.c:214
+#: builtin/commit.c:1444 builtin/merge.c:231
 msgid "GPG sign commit"
-msgstr "signiert Version mit GPG"
+msgstr "signiert Commit mit GPG"
 
 #. end commit message options
-#: builtin/commit.c:1380
+#: builtin/commit.c:1447
 msgid "Commit contents options"
-msgstr "Optionen für Versionsinhalt"
+msgstr "Optionen für Commit-Inhalt"
 
-#: builtin/commit.c:1381
+#: builtin/commit.c:1448
 msgid "commit all changed files"
-msgstr "trägt alle geänderten Dateien ein"
+msgstr "committet alle geänderten Dateien"
 
-#: builtin/commit.c:1382
+#: builtin/commit.c:1449
 msgid "add specified files to index for commit"
-msgstr "trägt die angegebenen Dateien zusätzlich zur Bereitstellung ein"
+msgstr "merkt die angegebenen Dateien zusätzlich zum Commit vor"
 
-#: builtin/commit.c:1383
+#: builtin/commit.c:1450
 msgid "interactively add files"
 msgstr "interaktives Hinzufügen von Dateien"
 
-#: builtin/commit.c:1384
+#: builtin/commit.c:1451
 msgid "interactively add changes"
 msgstr "interaktives Hinzufügen von Änderungen"
 
-#: builtin/commit.c:1385
+#: builtin/commit.c:1452
 msgid "commit only specified files"
-msgstr "trägt nur die angegebenen Dateien ein"
+msgstr "committet nur die angegebenen Dateien"
 
-#: builtin/commit.c:1386
+#: builtin/commit.c:1453
 msgid "bypass pre-commit hook"
 msgstr "umgeht \"pre-commit hook\""
 
-#: builtin/commit.c:1387
+#: builtin/commit.c:1454
 msgid "show what would be committed"
-msgstr "zeigt an, was eingetragen werden würde"
+msgstr "zeigt an, was committet werden würde"
 
-#: builtin/commit.c:1398
+#: builtin/commit.c:1465
 msgid "amend previous commit"
-msgstr "ändert vorherige Version"
+msgstr "ändert vorherigen Commit"
 
-#: builtin/commit.c:1399
+#: builtin/commit.c:1466
 msgid "bypass post-rewrite hook"
 msgstr "umgeht \"post-rewrite hook\""
 
-#: builtin/commit.c:1404
+#: builtin/commit.c:1471
 msgid "ok to record an empty change"
 msgstr "erlaubt Aufzeichnung einer leeren Änderung"
 
-#: builtin/commit.c:1407
+#: builtin/commit.c:1474
 msgid "ok to record a change with an empty message"
 msgstr "erlaubt Aufzeichnung einer Änderung mit einer leeren Beschreibung"
 
-#: builtin/commit.c:1439
+#: builtin/commit.c:1507
 msgid "could not parse HEAD commit"
-msgstr "Konnte Version der Zweigspitze (HEAD) nicht analysieren."
+msgstr "Konnte Commit von HEAD nicht analysieren."
 
-#: builtin/commit.c:1477 builtin/merge.c:508
+#: builtin/commit.c:1545 builtin/merge.c:525
 #, c-format
 msgid "could not open '%s' for reading"
 msgstr "Konnte '%s' nicht zum Lesen öffnen."
 
-#: builtin/commit.c:1484
+#: builtin/commit.c:1552
 #, c-format
 msgid "Corrupt MERGE_HEAD file (%s)"
 msgstr "Beschädigte MERGE_HEAD-Datei (%s)"
 
-#: builtin/commit.c:1491
+#: builtin/commit.c:1559
 msgid "could not read MERGE_MODE"
 msgstr "Konnte MERGE_MODE nicht lesen"
 
-#: builtin/commit.c:1510
+#: builtin/commit.c:1578
 #, c-format
 msgid "could not read commit message: %s"
-msgstr "Konnte Versionsbeschreibung nicht lesen: %s"
+msgstr "Konnte Commit-Beschreibung nicht lesen: %s"
 
-#: builtin/commit.c:1524
+#: builtin/commit.c:1592
 #, c-format
 msgid "Aborting commit; you did not edit the message.\n"
-msgstr "Eintragung abgebrochen; Sie haben die Beschreibung nicht editiert.\n"
+msgstr "Commit abgebrochen; Sie haben die Beschreibung nicht editiert.\n"
 
-#: builtin/commit.c:1529
+#: builtin/commit.c:1597
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
-msgstr "Eintragung aufgrund leerer Versionsbeschreibung abgebrochen.\n"
+msgstr "Commit aufgrund leerer Beschreibung abgebrochen.\n"
 
-#: builtin/commit.c:1544 builtin/merge.c:832 builtin/merge.c:857
+#: builtin/commit.c:1612 builtin/merge.c:861 builtin/merge.c:886
 msgid "failed to write commit object"
-msgstr "Fehler beim Schreiben des Versionsobjektes."
+msgstr "Fehler beim Schreiben des Commit-Objektes."
 
-#: builtin/commit.c:1565
+#: builtin/commit.c:1633
 msgid "cannot lock HEAD ref"
-msgstr "Kann Referenz der Zweigspitze (HEAD) nicht sperren."
+msgstr "Kann Referenz von HEAD nicht sperren."
 
-#: builtin/commit.c:1569
+#: builtin/commit.c:1637
 msgid "cannot update HEAD ref"
-msgstr "Kann Referenz der Zweigspitze (HEAD) nicht aktualisieren."
+msgstr "Kann Referenz von HEAD nicht aktualisieren."
 
-#: builtin/commit.c:1580
+#: builtin/commit.c:1648
 msgid ""
 "Repository has been updated, but unable to write\n"
 "new_index file. Check that disk is not full or quota is\n"
 "not exceeded, and then \"git reset HEAD\" to recover."
 msgstr ""
-"Das Projektarchiv wurde aktualisiert, aber die \"new_index\"-Datei\n"
+"Das Repository wurde aktualisiert, aber die \"new_index\"-Datei\n"
 "konnte nicht geschrieben werden. Prüfen Sie, dass Ihre Festplatte nicht\n"
 "voll und Ihr Kontingent nicht aufgebraucht ist und führen Sie\n"
 "anschließend \"git reset HEAD\" zu Wiederherstellung aus."
@@ -4042,289 +4555,301 @@ msgstr ""
 msgid "git config [options]"
 msgstr "git config [Optionen]"
 
-#: builtin/config.c:51
+#: builtin/config.c:52
 msgid "Config file location"
 msgstr "Ort der Konfigurationsdatei"
 
-#: builtin/config.c:52
+#: builtin/config.c:53
 msgid "use global config file"
 msgstr "verwendet globale Konfigurationsdatei"
 
-#: builtin/config.c:53
+#: builtin/config.c:54
 msgid "use system config file"
 msgstr "verwendet systemweite Konfigurationsdatei"
 
-#: builtin/config.c:54
+#: builtin/config.c:55
 msgid "use repository config file"
-msgstr "verwendet Konfigurationsdatei des Projektarchivs"
+msgstr "verwendet Konfigurationsdatei des Repositories"
 
-#: builtin/config.c:55
+#: builtin/config.c:56
 msgid "use given config file"
 msgstr "verwendet die angegebene Konfigurationsdatei"
 
-#: builtin/config.c:56
+#: builtin/config.c:57
+msgid "blob-id"
+msgstr "Blob-Id"
+
+#: builtin/config.c:57
+msgid "read config from given blob object"
+msgstr "liest Konfiguration von angegebenem Blob-Objekt"
+
+#: builtin/config.c:58
 msgid "Action"
 msgstr "Aktion"
 
-#: builtin/config.c:57
+#: builtin/config.c:59
 msgid "get value: name [value-regex]"
 msgstr "gibt Wert zurück: Name [Wert-regex]"
 
-#: builtin/config.c:58
+#: builtin/config.c:60
 msgid "get all values: key [value-regex]"
 msgstr "gibt alle Werte zurück: Schlüssel [Wert-regex]"
 
-#: builtin/config.c:59
+#: builtin/config.c:61
 msgid "get values for regexp: name-regex [value-regex]"
 msgstr "gibt Werte für den regulären Ausdruck zurück: Name-regex [Wert-regex]"
 
-#: builtin/config.c:60
+#: builtin/config.c:62
 msgid "replace all matching variables: name value [value_regex]"
 msgstr "ersetzt alle passenden Variablen: Name Wert [Wert-regex] "
 
-#: builtin/config.c:61
+#: builtin/config.c:63
 msgid "add a new variable: name value"
 msgstr "fügt neue Variable hinzu: Name Wert"
 
-#: builtin/config.c:62
+#: builtin/config.c:64
 msgid "remove a variable: name [value-regex]"
 msgstr "entfernt eine Variable: Name [Wert-regex]"
 
-#: builtin/config.c:63
+#: builtin/config.c:65
 msgid "remove all matches: name [value-regex]"
 msgstr "entfernt alle Übereinstimmungen: Name [Wert-regex]"
 
-#: builtin/config.c:64
+#: builtin/config.c:66
 msgid "rename section: old-name new-name"
 msgstr "benennt eine Sektion um: alter-Name neuer-Name"
 
-#: builtin/config.c:65
+#: builtin/config.c:67
 msgid "remove a section: name"
 msgstr "entfernt eine Sektion: Name"
 
-#: builtin/config.c:66
+#: builtin/config.c:68
 msgid "list all"
 msgstr "listet alles auf"
 
-#: builtin/config.c:67
+#: builtin/config.c:69
 msgid "open an editor"
 msgstr "öffnet einen Editor"
 
-#: builtin/config.c:68 builtin/config.c:69
+#: builtin/config.c:70 builtin/config.c:71
 msgid "slot"
 msgstr "Slot"
 
-#: builtin/config.c:68
+#: builtin/config.c:70
 msgid "find the color configured: [default]"
 msgstr "findet die konfigurierte Farbe: [Standard]"
 
-#: builtin/config.c:69
+#: builtin/config.c:71
 msgid "find the color setting: [stdout-is-tty]"
 msgstr "findet die Farbeinstellung: [Standard-Ausgabe-ist-Terminal]"
 
-#: builtin/config.c:70
+#: builtin/config.c:72
 msgid "Type"
 msgstr "Typ"
 
-#: builtin/config.c:71
+#: builtin/config.c:73
 msgid "value is \"true\" or \"false\""
 msgstr "Wert ist \"true\" oder \"false\""
 
-#: builtin/config.c:72
+#: builtin/config.c:74
 msgid "value is decimal number"
 msgstr "Wert ist eine Dezimalzahl"
 
-#: builtin/config.c:73
+#: builtin/config.c:75
 msgid "value is --bool or --int"
 msgstr "Wert ist --bool oder --int"
 
-#: builtin/config.c:74
+#: builtin/config.c:76
 msgid "value is a path (file or directory name)"
 msgstr "Wert ist ein Pfad (Datei oder Verzeichnisname)"
 
-#: builtin/config.c:75
+#: builtin/config.c:77
 msgid "Other"
 msgstr "Sonstiges"
 
-#: builtin/config.c:76
+#: builtin/config.c:78
 msgid "terminate values with NUL byte"
 msgstr "schließt Werte mit NUL-Byte ab"
 
-#: builtin/config.c:77
+#: builtin/config.c:79
 msgid "respect include directives on lookup"
 msgstr "beachtet \"include\"-Direktiven beim Nachschlagen"
 
-#: builtin/count-objects.c:69
-msgid "git count-objects [-v]"
-msgstr "git count-objects [-v]"
+#: builtin/count-objects.c:82
+msgid "git count-objects [-v] [-H | --human-readable]"
+msgstr "git count-objects [-v] [-H | --human-readable]"
 
-#: builtin/describe.c:15
+#: builtin/count-objects.c:97
+msgid "print sizes in human readable format"
+msgstr "gibt Größenangaben in menschenlesbaren Format aus"
+
+#: builtin/describe.c:16
 msgid "git describe [options] <committish>*"
 msgstr "git describe [Optionen] <committish>*"
 
-#: builtin/describe.c:16
+#: builtin/describe.c:17
 msgid "git describe [options] --dirty"
 msgstr "git describe [Optionen] --dirty"
 
-#: builtin/describe.c:234
+#: builtin/describe.c:237
 #, c-format
 msgid "annotated tag %s not available"
-msgstr "annotierte Markierung %s ist nicht verfügbar"
+msgstr "annotiertes Tag %s ist nicht verfügbar"
 
-#: builtin/describe.c:238
+#: builtin/describe.c:241
 #, c-format
 msgid "annotated tag %s has no embedded name"
-msgstr "annotierte Markierung %s hat keinen eingebetteten Namen"
+msgstr "annotiertes Tag %s hat keinen eingebetteten Namen"
 
-#: builtin/describe.c:240
+#: builtin/describe.c:243
 #, c-format
 msgid "tag '%s' is really '%s' here"
-msgstr "Markierung '%s' ist eigentlich '%s' hier"
+msgstr "Tag '%s' ist eigentlich '%s' hier"
 
-#: builtin/describe.c:267
+#: builtin/describe.c:270
 #, c-format
 msgid "Not a valid object name %s"
 msgstr "%s ist kein gültiger Objekt-Name"
 
-#: builtin/describe.c:270
+#: builtin/describe.c:273
 #, c-format
 msgid "%s is not a valid '%s' object"
 msgstr "%s ist kein gültiges '%s' Objekt"
 
-#: builtin/describe.c:287
+#: builtin/describe.c:290
 #, c-format
 msgid "no tag exactly matches '%s'"
-msgstr "kein Markierung entspricht exakt '%s'"
+msgstr "kein Tag entspricht exakt '%s'"
 
-#: builtin/describe.c:289
+#: builtin/describe.c:292
 #, c-format
 msgid "searching to describe %s\n"
 msgstr "suche zur Beschreibung von %s\n"
 
-#: builtin/describe.c:329
+#: builtin/describe.c:332
 #, c-format
 msgid "finished search at %s\n"
 msgstr "beendete Suche bei %s\n"
 
-#: builtin/describe.c:353
+#: builtin/describe.c:359
 #, c-format
 msgid ""
 "No annotated tags can describe '%s'.\n"
 "However, there were unannotated tags: try --tags."
 msgstr ""
-"Keine annotierten Markierungen können '%s' beschreiben.\n"
-"Jedoch gab es nicht annotierte Markierungen: versuchen Sie --tags."
+"Keine annotierten Tags können '%s' beschreiben.\n"
+"Jedoch gab es nicht-annotierte Tags: versuchen Sie --tags."
 
-#: builtin/describe.c:357
+#: builtin/describe.c:363
 #, c-format
 msgid ""
 "No tags can describe '%s'.\n"
 "Try --always, or create some tags."
 msgstr ""
-"Keine Markierungen können '%s' beschreiben.\n"
-"Versuchen Sie --always oder erstellen Sie einige Markierungen."
+"Keine Tags können '%s' beschreiben.\n"
+"Versuchen Sie --always oder erstellen Sie einige Tags."
 
-#: builtin/describe.c:378
+#: builtin/describe.c:384
 #, c-format
 msgid "traversed %lu commits\n"
-msgstr "%lu Versionen durchlaufen\n"
+msgstr "%lu Commits durchlaufen\n"
 
-#: builtin/describe.c:381
+#: builtin/describe.c:387
 #, c-format
 msgid ""
 "more than %i tags found; listed %i most recent\n"
 "gave up search at %s\n"
 msgstr ""
-"mehr als %i Markierungen gefunden; Führe die ersten %i auf\n"
+"mehr als %i Tags gefunden; Führe die ersten %i auf\n"
 "Suche bei %s aufgegeben\n"
 
-#: builtin/describe.c:403
+#: builtin/describe.c:409
 msgid "find the tag that comes after the commit"
-msgstr "findet die Markierung, die nach der Version kommt"
+msgstr "findet das Tag, das nach Commit kommt"
 
-#: builtin/describe.c:404
+#: builtin/describe.c:410
 msgid "debug search strategy on stderr"
 msgstr "protokolliert die Suchstrategie in der Standard-Fehlerausgabe"
 
-#: builtin/describe.c:405
-msgid "use any ref in .git/refs"
-msgstr "verwendet alle Referenzen in .git/refs"
+#: builtin/describe.c:411
+msgid "use any ref"
+msgstr "verwendet alle Referenzen"
 
-#: builtin/describe.c:406
-msgid "use any tag in .git/refs/tags"
-msgstr "verwendet alle Markierungen in .git/refs/tags"
+#: builtin/describe.c:412
+msgid "use any tag, even unannotated"
+msgstr "verwendet jedes Tag, auch nicht-annotierte"
 
-#: builtin/describe.c:407
+#: builtin/describe.c:413
 msgid "always use long format"
 msgstr "verwendet immer langes Format"
 
-#: builtin/describe.c:410
+#: builtin/describe.c:414
+msgid "only follow first parent"
+msgstr "folgt nur erstem Elternteil"
+
+#: builtin/describe.c:417
 msgid "only output exact matches"
 msgstr "gibt nur exakte Übereinstimmungen aus"
 
-#: builtin/describe.c:412
+#: builtin/describe.c:419
 msgid "consider <n> most recent tags (default: 10)"
-msgstr "betrachtet die jüngsten <n> Markierungen (Standard: 10)"
+msgstr "betrachtet die jüngsten <n> Tags (Standard: 10)"
 
-#: builtin/describe.c:414
+#: builtin/describe.c:421
 msgid "only consider tags matching <pattern>"
-msgstr "betrachtet nur Markierungen die <Muster> entsprechen"
+msgstr "betrachtet nur Tags die <Muster> entsprechen"
 
-#: builtin/describe.c:416 builtin/name-rev.c:238
+#: builtin/describe.c:423 builtin/name-rev.c:322
 msgid "show abbreviated commit object as fallback"
-msgstr "zeigt gekürztes Versionsobjekt, wenn sonst nichts zutrifft"
+msgstr "zeigt gekürztes Commit-Objekt, wenn sonst nichts zutrifft"
 
-#: builtin/describe.c:417
+#: builtin/describe.c:424
 msgid "mark"
 msgstr "Kennzeichen"
 
-#: builtin/describe.c:418
+#: builtin/describe.c:425
 msgid "append <mark> on dirty working tree (default: \"-dirty\")"
 msgstr ""
-"fügt <Kennzeichen> bei geändertem Arbeitsbaum hinzu (Standard: \"-dirty\")"
+"fügt <Kennzeichen> bei geändertem Arbeitsverzeichnis hinzu (Standard: \"-"
+"dirty\")"
 
-#: builtin/describe.c:436
+#: builtin/describe.c:443
 msgid "--long is incompatible with --abbrev=0"
 msgstr "Die Optionen --long und --abbrev=0 sind inkompatibel."
 
-#: builtin/describe.c:462
+#: builtin/describe.c:469
 msgid "No names found, cannot describe anything."
 msgstr "Keine Namen gefunden, kann nichts beschreiben."
 
-#: builtin/describe.c:482
+#: builtin/describe.c:489
 msgid "--dirty is incompatible with committishes"
-msgstr "Die Option --dirty kann nicht mit Versionen verwendet werden."
+msgstr "Die Option --dirty kann nicht mit Commits verwendet werden."
 
 #: builtin/diff.c:79
 #, c-format
 msgid "'%s': not a regular file or symlink"
-msgstr "'%s': keine reguläre Datei oder symbolischer Link"
+msgstr "'%s': keine reguläre Datei oder symbolischer Verweis"
 
-#: builtin/diff.c:228
+#: builtin/diff.c:230
 #, c-format
 msgid "invalid option: %s"
 msgstr "Ungültige Option: %s"
 
-#: builtin/diff.c:305
+#: builtin/diff.c:307
 msgid "Not a git repository"
-msgstr "Kein Git-Projektarchiv"
+msgstr "Kein Git-Repository"
 
-#: builtin/diff.c:348
+#: builtin/diff.c:350
 #, c-format
 msgid "invalid object '%s' given."
 msgstr "Objekt '%s' ist ungültig."
 
-#: builtin/diff.c:353
-#, c-format
-msgid "more than %d trees given: '%s'"
-msgstr "Mehr als %d Zweige angegeben: '%s'"
-
-#: builtin/diff.c:363
+#: builtin/diff.c:359
 #, c-format
 msgid "more than two blobs given: '%s'"
 msgstr "Mehr als zwei Blobs angegeben: '%s'"
 
-#: builtin/diff.c:371
+#: builtin/diff.c:366
 #, c-format
 msgid "unhandled object '%s' given."
 msgstr "unbehandeltes Objekt '%s' angegeben"
@@ -4333,46 +4858,45 @@ msgstr "unbehandeltes Objekt '%s' angegeben"
 msgid "git fast-export [rev-list-opts]"
 msgstr "git fast-export [rev-list-opts]"
 
-#: builtin/fast-export.c:646
+#: builtin/fast-export.c:666
 msgid "show progress after <n> objects"
 msgstr "zeigt Fortschritt nach <n> Objekten an"
 
-#: builtin/fast-export.c:648
+#: builtin/fast-export.c:668
 msgid "select handling of signed tags"
-msgstr "wählt Behandlung von signierten Markierungen"
+msgstr "wählt Behandlung von signierten Tags"
 
-#: builtin/fast-export.c:651
+#: builtin/fast-export.c:671
 msgid "select handling of tags that tag filtered objects"
-msgstr "wählt Behandlung von Markierungen, die gefilterte Objekte markieren"
+msgstr "wählt Behandlung von Tags, die gefilterte Objekte markieren"
 
-#: builtin/fast-export.c:654
+#: builtin/fast-export.c:674
 msgid "Dump marks to this file"
 msgstr "Schreibt Kennzeichen in diese Datei"
 
-#: builtin/fast-export.c:656
+#: builtin/fast-export.c:676
 msgid "Import marks from this file"
 msgstr "Importiert Kennzeichen von dieser Datei"
 
-#: builtin/fast-export.c:658
+#: builtin/fast-export.c:678
 msgid "Fake a tagger when tags lack one"
-msgstr ""
-"erzeugt künstlich einen Markierungsersteller, wenn die Markierung keinen hat"
+msgstr "erzeugt künstlich einen Tag-Ersteller, wenn das Tag keinen hat"
 
-#: builtin/fast-export.c:660
+#: builtin/fast-export.c:680
 msgid "Output full tree for each commit"
-msgstr "gibt für jede Version den gesamten Baum aus"
+msgstr "gibt für jeden Commit das gesamte Verzeichnis aus"
 
-#: builtin/fast-export.c:662
+#: builtin/fast-export.c:682
 msgid "Use the done feature to terminate the stream"
 msgstr "Benutzt die \"done\"-Funktion um den Strom abzuschließen"
 
-#: builtin/fast-export.c:663
+#: builtin/fast-export.c:683
 msgid "Skip output of blob data"
 msgstr "Überspringt Ausgabe von Blob-Daten"
 
 #: builtin/fetch.c:20
 msgid "git fetch [<options>] [<repository> [<refspec>...]]"
-msgstr "git fetch [<Optionen>] [<Projektarchiv> [<Referenzspezifikation>...]]"
+msgstr "git fetch [<Optionen>] [<Repository> [<Refspec>...]]"
 
 #: builtin/fetch.c:21
 msgid "git fetch [<options>] <group>"
@@ -4380,7 +4904,7 @@ msgstr "git fetch [<Optionen>] <Gruppe>"
 
 #: builtin/fetch.c:22
 msgid "git fetch --multiple [<options>] [(<repository> | <group>)...]"
-msgstr "git fetch --multiple [<Optionen>] [(<Projektarchiv> | <Gruppe>)...]"
+msgstr "git fetch --multiple [<Optionen>] [(<Repository> | <Gruppe>)...]"
 
 #: builtin/fetch.c:23
 msgid "git fetch --all [<options>]"
@@ -4388,7 +4912,7 @@ msgstr "git fetch --all [<Optionen>]"
 
 #: builtin/fetch.c:60
 msgid "fetch from all remotes"
-msgstr "fordert von allen externen Projektarchiven an"
+msgstr "fordert von allen Remote-Repositories an"
 
 #: builtin/fetch.c:62
 msgid "append to .git/FETCH_HEAD instead of overwriting"
@@ -4400,24 +4924,25 @@ msgstr "Pfad des Programms zum Hochladen von Paketen auf der Gegenseite"
 
 #: builtin/fetch.c:65
 msgid "force overwrite of local branch"
-msgstr "erzwingt das Überschreiben von lokalen Zweigen"
+msgstr "erzwingt das Überschreiben von lokalen Branches"
 
 #: builtin/fetch.c:67
 msgid "fetch from multiple remotes"
-msgstr "fordert von mehreren externen Projektarchiven an"
+msgstr "fordert von mehreren Remote-Repositories an"
 
 #: builtin/fetch.c:69
 msgid "fetch all tags and associated objects"
-msgstr "fordert alle Markierungen und verbundene Objekte an"
+msgstr "fordert alle Tags und verbundene Objekte an"
 
 #: builtin/fetch.c:71
 msgid "do not fetch all tags (--no-tags)"
-msgstr "fordert nicht alle Markierungen an (--no-tags)"
+msgstr "fordert nicht alle Tags an (--no-tags)"
 
 #: builtin/fetch.c:73
 msgid "prune remote-tracking branches no longer on remote"
 msgstr ""
-"entfernt externe Übernahmezweige, die sich nicht mehr im Fernarchiv befinden"
+"entfernt Remote-Tracking-Branches, die sich nicht mehr im Remote-Repository "
+"befinden"
 
 #: builtin/fetch.c:74
 msgid "on-demand"
@@ -4425,7 +4950,7 @@ msgstr "bei-Bedarf"
 
 #: builtin/fetch.c:75
 msgid "control recursive fetching of submodules"
-msgstr "kontrolliert rekursive Anforderungen von Unterprojekten"
+msgstr "kontrolliert rekursive Anforderungen von Submodulen"
 
 #: builtin/fetch.c:79
 msgid "keep downloaded pack"
@@ -4437,223 +4962,224 @@ msgstr "erlaubt Aktualisierung der \"HEAD\"-Referenz"
 
 #: builtin/fetch.c:84
 msgid "deepen history of shallow clone"
-msgstr "vertieft die Historie eines flachen Klon"
+msgstr "vertieft die Historie eines flachen Klons"
 
 #: builtin/fetch.c:86
 msgid "convert to a complete repository"
-msgstr "konvertiert zu einem vollständigen Projektarchiv"
+msgstr "konvertiert zu einem vollständigen Repository"
 
-#: builtin/fetch.c:88 builtin/log.c:1119
+#: builtin/fetch.c:88 builtin/log.c:1177
 msgid "dir"
 msgstr "Verzeichnis"
 
 #: builtin/fetch.c:89
 msgid "prepend this to submodule path output"
-msgstr "stellt dies an die Ausgabe der Unterprojekt-Pfade voran"
+msgstr "stellt dies an die Ausgabe der Submodul-Pfade voran"
 
 #: builtin/fetch.c:92
 msgid "default mode for recursion"
 msgstr "Standard-Modus für Rekursion"
 
-#: builtin/fetch.c:204
+#: builtin/fetch.c:220
 msgid "Couldn't find remote ref HEAD"
-msgstr "Konnte externe Referenz der Zweigspitze (HEAD) nicht finden."
+msgstr "Konnte Remote-Referenz von HEAD nicht finden."
 
-#: builtin/fetch.c:257
+#: builtin/fetch.c:273
 #, c-format
 msgid "object %s not found"
 msgstr "Objekt %s nicht gefunden"
 
-#: builtin/fetch.c:262
+#: builtin/fetch.c:278
 msgid "[up to date]"
 msgstr "[aktuell]"
 
-#: builtin/fetch.c:276
+#: builtin/fetch.c:292
 #, c-format
 msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
-msgstr "! %-*s %-*s -> %s  (kann nicht im aktuellen Zweig anfordern)"
+msgstr ""
+"! %-*s %-*s -> %s  (kann \"fetch\" im aktuellen Branch nicht ausführen)"
 
-#: builtin/fetch.c:277 builtin/fetch.c:363
+#: builtin/fetch.c:293 builtin/fetch.c:379
 msgid "[rejected]"
 msgstr "[zurückgewiesen]"
 
-#: builtin/fetch.c:288
+#: builtin/fetch.c:304
 msgid "[tag update]"
-msgstr "[Markierungsaktualisierung]"
+msgstr "[Tag Aktualisierung]"
 
-#: builtin/fetch.c:290 builtin/fetch.c:325 builtin/fetch.c:343
+#: builtin/fetch.c:306 builtin/fetch.c:341 builtin/fetch.c:359
 msgid "  (unable to update local ref)"
 msgstr "  (kann lokale Referenz nicht aktualisieren)"
 
-#: builtin/fetch.c:308
+#: builtin/fetch.c:324
 msgid "[new tag]"
-msgstr "[neue Markierung]"
+msgstr "[neues Tag]"
 
-#: builtin/fetch.c:311
+#: builtin/fetch.c:327
 msgid "[new branch]"
-msgstr "[neuer Zweig]"
+msgstr "[neuer Branch]"
 
-#: builtin/fetch.c:314
+#: builtin/fetch.c:330
 msgid "[new ref]"
 msgstr "[neue Referenz]"
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "unable to update local ref"
 msgstr "kann lokale Referenz nicht aktualisieren"
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "forced update"
 msgstr "Aktualisierung erzwungen"
 
-#: builtin/fetch.c:365
+#: builtin/fetch.c:381
 msgid "(non-fast-forward)"
 msgstr "(kein Vorspulen)"
 
-#: builtin/fetch.c:396 builtin/fetch.c:688
+#: builtin/fetch.c:412 builtin/fetch.c:718
 #, c-format
 msgid "cannot open %s: %s\n"
 msgstr "kann %s nicht öffnen: %s\n"
 
-#: builtin/fetch.c:405
+#: builtin/fetch.c:421
 #, c-format
 msgid "%s did not send all necessary objects\n"
 msgstr "%s hat nicht alle erforderlichen Objekte gesendet\n"
 
-#: builtin/fetch.c:491
+#: builtin/fetch.c:520
 #, c-format
 msgid "From %.*s\n"
 msgstr "Von %.*s\n"
 
-#: builtin/fetch.c:502
+#: builtin/fetch.c:531
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
 " 'git remote prune %s' to remove any old, conflicting branches"
 msgstr ""
 "Einige lokale Referenzen konnten nicht aktualisiert werden; versuchen Sie\n"
-"'git remote prune %s' um jeden älteren, widersprüchlichen Zweig zu löschen."
+"'git remote prune %s' um jeden älteren, widersprüchlichen Branch zu löschen."
 
-#: builtin/fetch.c:552
+#: builtin/fetch.c:581
 #, c-format
 msgid "   (%s will become dangling)"
 msgstr "   (%s wird unreferenziert)"
 
-#: builtin/fetch.c:553
+#: builtin/fetch.c:582
 #, c-format
 msgid "   (%s has become dangling)"
 msgstr "   (%s wurde unreferenziert)"
 
-#: builtin/fetch.c:560
+#: builtin/fetch.c:589
 msgid "[deleted]"
 msgstr "[gelöscht]"
 
-#: builtin/fetch.c:561 builtin/remote.c:1055
+#: builtin/fetch.c:590 builtin/remote.c:1055
 msgid "(none)"
 msgstr "(nichts)"
 
-#: builtin/fetch.c:678
+#: builtin/fetch.c:708
 #, c-format
 msgid "Refusing to fetch into current branch %s of non-bare repository"
 msgstr ""
-"Das Anfordern in den aktuellen Zweig %s von einem nicht-bloßen Projektarchiv "
+"Der \"fetch\" in den aktuellen Branch %s von einem nicht-Bare-Repository "
 "wurde verweigert."
 
-#: builtin/fetch.c:712
+#: builtin/fetch.c:742
 #, c-format
 msgid "Don't know how to fetch from %s"
 msgstr "Weiß nicht wie von %s angefordert wird."
 
-#: builtin/fetch.c:789
+#: builtin/fetch.c:823
 #, c-format
 msgid "Option \"%s\" value \"%s\" is not valid for %s"
 msgstr "Option \"%s\" Wert \"%s\" ist nicht gültig für %s"
 
-#: builtin/fetch.c:792
+#: builtin/fetch.c:826
 #, c-format
 msgid "Option \"%s\" is ignored for %s\n"
 msgstr "Option \"%s\" wird ignoriert für %s\n"
 
-#: builtin/fetch.c:894
+#: builtin/fetch.c:928
 #, c-format
 msgid "Fetching %s\n"
 msgstr "Fordere an von %s\n"
 
-#: builtin/fetch.c:896 builtin/remote.c:100
+#: builtin/fetch.c:930 builtin/remote.c:100
 #, c-format
 msgid "Could not fetch %s"
 msgstr "Konnte nicht von %s anfordern"
 
-#: builtin/fetch.c:915
+#: builtin/fetch.c:949
 msgid ""
 "No remote repository specified.  Please, specify either a URL or a\n"
 "remote name from which new revisions should be fetched."
 msgstr ""
-"Kein externes Projektarchiv angegeben. Bitte geben Sie entweder eine URL\n"
-"oder den Namen des externen Archivs an, von welchem neue\n"
-"Revisionen angefordert werden sollen."
+"Kein Remote-Repository angegeben. Bitte geben Sie entweder eine URL\n"
+"oder den Namen des Remote-Repositories an, von welchem neue\n"
+"Commits angefordert werden sollen."
 
-#: builtin/fetch.c:935
+#: builtin/fetch.c:969
 msgid "You need to specify a tag name."
-msgstr "Sie müssen den Namen der Markierung angeben."
+msgstr "Sie müssen den Namen des Tags angeben."
 
-#: builtin/fetch.c:981
+#: builtin/fetch.c:1015
 msgid "--depth and --unshallow cannot be used together"
 msgstr ""
 "Die Optionen --depth und --unshallow können nicht gemeinsam verwendet werden."
 
-#: builtin/fetch.c:983
+#: builtin/fetch.c:1017
 msgid "--unshallow on a complete repository does not make sense"
 msgstr ""
-"Die Option --unshallow kann nicht in einem vollständigen Projektarchiv "
+"Die Option --unshallow kann nicht in einem vollständigen Repository "
 "verwendet werden."
 
-#: builtin/fetch.c:1002
+#: builtin/fetch.c:1036
 msgid "fetch --all does not take a repository argument"
-msgstr "fetch --all akzeptiert kein Projektarchiv als Argument"
+msgstr "fetch --all akzeptiert kein Repository als Argument"
 
-#: builtin/fetch.c:1004
+#: builtin/fetch.c:1038
 msgid "fetch --all does not make sense with refspecs"
-msgstr "fetch --all kann nicht mit Referenzspezifikationen verwendet werden."
+msgstr "fetch --all kann nicht mit Refspecs verwendet werden."
 
-#: builtin/fetch.c:1015
+#: builtin/fetch.c:1049
 #, c-format
 msgid "No such remote or remote group: %s"
-msgstr "Kein externes Archiv (einzeln oder Gruppe): %s"
+msgstr "Kein Remote-Repository (einzeln oder Gruppe): %s"
 
-#: builtin/fetch.c:1023
+#: builtin/fetch.c:1057
 msgid "Fetching a group and specifying refspecs does not make sense"
 msgstr ""
-"Das Abholen einer Gruppe von externen Archiven kann nicht mit der Angabe\n"
-"von Referenzspezifikationen verwendet werden."
+"Das Abholen einer Gruppe von Remote-Repositories kann nicht mit der Angabe\n"
+"von Refspecs verwendet werden."
 
 #: builtin/fmt-merge-msg.c:13
 msgid "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"
 msgstr ""
 "git fmt-merge-msg [-m <Beschreibung>] [--log[=<n>]|--no-log] [--file <Datei>]"
 
-#: builtin/fmt-merge-msg.c:659 builtin/fmt-merge-msg.c:662 builtin/grep.c:701
-#: builtin/merge.c:188 builtin/show-branch.c:656 builtin/show-ref.c:175
-#: builtin/tag.c:446 parse-options.h:133 parse-options.h:239
+#: builtin/fmt-merge-msg.c:663 builtin/fmt-merge-msg.c:666 builtin/grep.c:700
+#: builtin/merge.c:203 builtin/show-branch.c:655 builtin/show-ref.c:179
+#: builtin/tag.c:446 parse-options.h:133 parse-options.h:243
 msgid "n"
 msgstr "Anzahl"
 
-#: builtin/fmt-merge-msg.c:660
+#: builtin/fmt-merge-msg.c:664
 msgid "populate log with at most <n> entries from shortlog"
 msgstr "fügt Historie mit höchstens <n> Einträgen von \"shortlog\" hinzu"
 
-#: builtin/fmt-merge-msg.c:663
+#: builtin/fmt-merge-msg.c:667
 msgid "alias for --log (deprecated)"
 msgstr "Alias für --log (veraltet)"
 
-#: builtin/fmt-merge-msg.c:666
+#: builtin/fmt-merge-msg.c:670
 msgid "text"
 msgstr "Text"
 
-#: builtin/fmt-merge-msg.c:667
+#: builtin/fmt-merge-msg.c:671
 msgid "use <text> as start of message"
 msgstr "verwendet <Text> als Beschreibungsanfang"
 
-#: builtin/fmt-merge-msg.c:668
+#: builtin/fmt-merge-msg.c:672
 msgid "file to read from"
 msgstr "Datei zum Einlesen"
 
@@ -4711,7 +5237,7 @@ msgstr "zeigt unreferenzierte Objekte"
 
 #: builtin/fsck.c:616
 msgid "report tags"
-msgstr "meldet Markierungen"
+msgstr "meldet Tags"
 
 #: builtin/fsck.c:617
 msgid "report root nodes"
@@ -4719,11 +5245,11 @@ msgstr "meldet Hauptwurzeln"
 
 #: builtin/fsck.c:618
 msgid "make index objects head nodes"
-msgstr "erzeugt Kopfknoten der Bereitstellungsobjekte"
+msgstr "prüft Objekte in der Staging-Area"
 
 #: builtin/fsck.c:619
 msgid "make reflogs head nodes (default)"
-msgstr "erzeugt Kopfknoten des Referenzprotokolls (Standard)"
+msgstr "prüft die Reflogs (Standard)"
 
 #: builtin/fsck.c:620
 msgid "also consider packs and alternate objects"
@@ -4773,7 +5299,7 @@ msgid ""
 "Auto packing the repository for optimum performance. You may also\n"
 "run \"git gc\" manually. See \"git help gc\" for more information.\n"
 msgstr ""
-"Die Datenbank des Projektarchivs wird für eine optimale Performance\n"
+"Die Datenbank des Repositories wird für eine optimale Performance\n"
 "komprimiert. Sie können auch \"git gc\" manuell ausführen.\n"
 "Siehe \"git help gc\" für weitere Informationen.\n"
 
@@ -4786,245 +5312,240 @@ msgstr ""
 
 #: builtin/grep.c:22
 msgid "git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]"
-msgstr "git grep [Optionen] [-e] <Muster> [<Revision>...] [[--] <Pfad>...]"
+msgstr "git grep [Optionen] [-e] <Muster> [<Commit>...] [[--] <Pfad>...]"
 
 #: builtin/grep.c:217
 #, c-format
 msgid "grep: failed to create thread: %s"
 msgstr "grep: Fehler beim Erzeugen eines Thread: %s"
 
-#: builtin/grep.c:365
+#: builtin/grep.c:364
 #, c-format
 msgid "Failed to chdir: %s"
 msgstr "Fehler beim Verzeichniswechsel: %s"
 
-#: builtin/grep.c:443 builtin/grep.c:478
+#: builtin/grep.c:442 builtin/grep.c:477
 #, c-format
 msgid "unable to read tree (%s)"
-msgstr "konnte Zweig (%s) nicht lesen"
+msgstr "konnte \"Tree\"-Objekt (%s) nicht lesen"
 
-#: builtin/grep.c:493
+#: builtin/grep.c:492
 #, c-format
 msgid "unable to grep from object of type %s"
 msgstr "kann \"grep\" nicht mit Objekten des Typs %s durchführen"
 
-#: builtin/grep.c:551
+#: builtin/grep.c:550
 #, c-format
 msgid "switch `%c' expects a numerical value"
 msgstr "Schalter '%c' erwartet einen numerischen Wert"
 
-#: builtin/grep.c:568
+#: builtin/grep.c:567
 #, c-format
 msgid "cannot open '%s'"
 msgstr "kann '%s' nicht öffnen"
 
-#: builtin/grep.c:643
+#: builtin/grep.c:642
 msgid "search in index instead of in the work tree"
-msgstr "sucht in der Bereitstellung anstatt im Arbeitsbaum"
+msgstr "sucht in der Staging-Area anstatt im Arbeitsverzeichnis"
 
-#: builtin/grep.c:645
+#: builtin/grep.c:644
 msgid "find in contents not managed by git"
 msgstr "findet auch in Inhalten, die nicht von Git verwaltet werden"
 
-#: builtin/grep.c:647
+#: builtin/grep.c:646
 msgid "search in both tracked and untracked files"
 msgstr "sucht in beobachteten und unbeobachteten Dateien"
 
-#: builtin/grep.c:649
+#: builtin/grep.c:648
 msgid "search also in ignored files"
 msgstr "sucht auch in ignorierten Dateien"
 
-#: builtin/grep.c:652
+#: builtin/grep.c:651
 msgid "show non-matching lines"
 msgstr "zeigt Zeilen ohne Übereinstimmungen"
 
-#: builtin/grep.c:654
+#: builtin/grep.c:653
 msgid "case insensitive matching"
 msgstr "findet Übereinstimmungen unabhängig von Groß- und Kleinschreibung"
 
-#: builtin/grep.c:656
+#: builtin/grep.c:655
 msgid "match patterns only at word boundaries"
 msgstr "sucht nur ganze Wörter"
 
-#: builtin/grep.c:658
+#: builtin/grep.c:657
 msgid "process binary files as text"
 msgstr "verarbeitet binäre Dateien als Text"
 
-#: builtin/grep.c:660
+#: builtin/grep.c:659
 msgid "don't match patterns in binary files"
 msgstr "findet keine Muster in Binärdateien"
 
-#: builtin/grep.c:663
+#: builtin/grep.c:662
 msgid "descend at most <depth> levels"
 msgstr "durchläuft höchstens <Tiefe> Ebenen"
 
-#: builtin/grep.c:667
+#: builtin/grep.c:666
 msgid "use extended POSIX regular expressions"
 msgstr "verwendet erweiterte reguläre Ausdrücke aus POSIX"
 
-#: builtin/grep.c:670
+#: builtin/grep.c:669
 msgid "use basic POSIX regular expressions (default)"
 msgstr "verwendet grundlegende reguläre Ausdrücke aus POSIX (Standard)"
 
-#: builtin/grep.c:673
+#: builtin/grep.c:672
 msgid "interpret patterns as fixed strings"
 msgstr "interpretiert Muster als feste Zeichenketten"
 
-#: builtin/grep.c:676
+#: builtin/grep.c:675
 msgid "use Perl-compatible regular expressions"
 msgstr "verwendet Perl-kompatible reguläre Ausdrücke"
 
-#: builtin/grep.c:679
+#: builtin/grep.c:678
 msgid "show line numbers"
 msgstr "zeigt Zeilennummern"
 
-#: builtin/grep.c:680
+#: builtin/grep.c:679
 msgid "don't show filenames"
 msgstr "zeigt keine Dateinamen"
 
-#: builtin/grep.c:681
+#: builtin/grep.c:680
 msgid "show filenames"
 msgstr "zeigt Dateinamen"
 
-#: builtin/grep.c:683
+#: builtin/grep.c:682
 msgid "show filenames relative to top directory"
 msgstr "zeigt Dateinamen relativ zum Projektverzeichnis"
 
-#: builtin/grep.c:685
+#: builtin/grep.c:684
 msgid "show only filenames instead of matching lines"
 msgstr "zeigt nur Dateinamen anstatt übereinstimmende Zeilen"
 
-#: builtin/grep.c:687
+#: builtin/grep.c:686
 msgid "synonym for --files-with-matches"
 msgstr "Synonym für --files-with-matches"
 
-#: builtin/grep.c:690
+#: builtin/grep.c:689
 msgid "show only the names of files without match"
 msgstr "zeigt nur die Dateinamen ohne Übereinstimmungen"
 
-#: builtin/grep.c:692
+#: builtin/grep.c:691
 msgid "print NUL after filenames"
 msgstr "gibt NUL-Zeichen nach Dateinamen aus"
 
-#: builtin/grep.c:694
+#: builtin/grep.c:693
 msgid "show the number of matches instead of matching lines"
 msgstr "zeigt, anstatt der Zeilen, die Anzahl der übereinstimmenden Zeilen"
 
-#: builtin/grep.c:695
+#: builtin/grep.c:694
 msgid "highlight matches"
 msgstr "hebt Übereinstimmungen hervor"
 
-#: builtin/grep.c:697
+#: builtin/grep.c:696
 msgid "print empty line between matches from different files"
 msgstr ""
 "gibt eine Leerzeile zwischen Übereinstimmungen in verschiedenen Dateien aus"
 
-#: builtin/grep.c:699
+#: builtin/grep.c:698
 msgid "show filename only once above matches from same file"
 msgstr ""
 "zeigt den Dateinamen nur einmal oberhalb der Übereinstimmungen aus dieser "
 "Datei an"
 
-#: builtin/grep.c:702
+#: builtin/grep.c:701
 msgid "show <n> context lines before and after matches"
 msgstr "zeigt <n> Zeilen vor und nach den Übereinstimmungen an"
 
-#: builtin/grep.c:705
+#: builtin/grep.c:704
 msgid "show <n> context lines before matches"
 msgstr "zeigt <n> Zeilen vor den Übereinstimmungen an"
 
-#: builtin/grep.c:707
+#: builtin/grep.c:706
 msgid "show <n> context lines after matches"
 msgstr "zeigt <n> Zeilen nach den Übereinstimmungen an"
 
-#: builtin/grep.c:708
+#: builtin/grep.c:707
 msgid "shortcut for -C NUM"
 msgstr "Kurzform für -C NUM"
 
-#: builtin/grep.c:711
+#: builtin/grep.c:710
 msgid "show a line with the function name before matches"
 msgstr "zeigt eine Zeile mit dem Funktionsnamen vor Übereinstimmungen an"
 
-#: builtin/grep.c:713
+#: builtin/grep.c:712
 msgid "show the surrounding function"
 msgstr "zeigt die umgebende Funktion an"
 
-#: builtin/grep.c:716
+#: builtin/grep.c:715
 msgid "read patterns from file"
 msgstr "liest Muster von einer Datei"
 
-#: builtin/grep.c:718
+#: builtin/grep.c:717
 msgid "match <pattern>"
 msgstr "findet <Muster>"
 
-#: builtin/grep.c:720
+#: builtin/grep.c:719
 msgid "combine patterns specified with -e"
 msgstr "kombiniert Muster, die mit -e angegeben wurden"
 
-#: builtin/grep.c:732
+#: builtin/grep.c:731
 msgid "indicate hit with exit status without output"
 msgstr "zeigt Übereinstimmungen nur durch Beendigungsstatus an"
 
-#: builtin/grep.c:734
+#: builtin/grep.c:733
 msgid "show only matches from files that match all patterns"
 msgstr "zeigt nur Übereinstimmungen von Dateien, die allen Mustern entsprechen"
 
-#: builtin/grep.c:736
+#: builtin/grep.c:735
 msgid "show parse tree for grep expression"
-msgstr "zeigt geparsten Baum für \"grep\"-Ausdruck"
+msgstr "zeigt geparstes Verzeichnis für \"grep\"-Ausdruck"
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "pager"
 msgstr "Anzeigeprogramm"
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "show matching files in the pager"
 msgstr "zeigt Dateien mit Übereinstimmungen im Anzeigeprogramm"
 
-#: builtin/grep.c:743
+#: builtin/grep.c:742
 msgid "allow calling of grep(1) (ignored by this build)"
 msgstr "erlaubt den Aufruf von grep(1) (von dieser Programmversion ignoriert)"
 
-#: builtin/grep.c:744 builtin/show-ref.c:184
+#: builtin/grep.c:743 builtin/show-ref.c:188
 msgid "show usage"
 msgstr "zeigt Verwendung"
 
-#: builtin/grep.c:811
+#: builtin/grep.c:810
 msgid "no pattern given."
 msgstr "keine Muster angegeben"
 
-#: builtin/grep.c:825
-#, c-format
-msgid "bad object %s"
-msgstr "ungültiges Objekt %s"
-
-#: builtin/grep.c:868
+#: builtin/grep.c:865
 msgid "--open-files-in-pager only works on the worktree"
 msgstr ""
-"Die Option --open-files-in-pager kann nur innerhalb des Arbeitsbaums "
-"verwendet werden."
+"Die Option --open-files-in-pager kann nur innerhalb des "
+"Arbeitsverzeichnisses verwendet werden."
 
-#: builtin/grep.c:891
+#: builtin/grep.c:888
 msgid "--cached or --untracked cannot be used with --no-index."
 msgstr ""
 "Die Optionen --cached und --untracked können nicht mit --no-index verwendet "
 "werden."
 
-#: builtin/grep.c:896
+#: builtin/grep.c:893
 msgid "--no-index or --untracked cannot be used with revs."
 msgstr ""
-"Die Optionen --no-index und --untracked können nicht mit Versionen verwendet "
+"Die Optionen --no-index und --untracked können nicht mit Commits verwendet "
 "werden."
 
-#: builtin/grep.c:899
+#: builtin/grep.c:896
 msgid "--[no-]exclude-standard cannot be used for tracked contents."
 msgstr ""
 "Die Option --[no-]exclude-standard kann nicht mit beobachteten Inhalten "
 "verwendet werden."
 
-#: builtin/grep.c:907
+#: builtin/grep.c:904
 msgid "both --cached and trees are given."
-msgstr "Die Option --cached kann nicht mit Zweigen verwendet werden."
+msgstr "Die Option --cached kann nicht mit \"Tree\"-Objekten verwendet werden."
 
 #: builtin/hash-object.c:60
 msgid ""
@@ -5062,10 +5583,14 @@ msgstr "speichert Datei wie sie ist, ohne Filter"
 msgid "process file as it were from this path"
 msgstr "verarbeitet Datei, als ob sie von diesem Pfad wäre"
 
-#: builtin/help.c:42
+#: builtin/help.c:41
 msgid "print all available commands"
 msgstr "Anzeige aller vorhandenen Kommandos"
 
+#: builtin/help.c:42
+msgid "print list of useful guides"
+msgstr "zeigt Liste von allgemein verwendeten Anleitungen"
+
 #: builtin/help.c:43
 msgid "show man page"
 msgstr "zeigt Handbuch"
@@ -5079,8 +5604,8 @@ msgid "show info page"
 msgstr "zeigt Info-Seite"
 
 #: builtin/help.c:52
-msgid "git help [--all] [--man|--web|--info] [command]"
-msgstr "git help [--all] [--man|--web|--info] [Kommando]"
+msgid "git help [--all] [--guides] [--man|--web|--info] [command]"
+msgstr "git help [--all] [--guides] [--man|--web|--info] [Kommando]"
 
 #: builtin/help.c:64
 #, c-format
@@ -5136,291 +5661,323 @@ msgstr "kein Handbuch-Betrachter konnte mit dieser Anfrage umgehen"
 msgid "no info viewer handled the request"
 msgstr "kein Informations-Betrachter konnte mit dieser Anfrage umgehen"
 
-#: builtin/help.c:429 builtin/help.c:436
+#: builtin/help.c:420
+msgid "Defining attributes per path"
+msgstr "Definition von Attributen pro Pfad"
+
+#: builtin/help.c:421
+msgid "A Git glossary"
+msgstr "Ein Git-Glossar"
+
+#: builtin/help.c:422
+msgid "Specifies intentionally untracked files to ignore"
+msgstr "Spezifikation von bewusst ignorierten, unbeobachteten Dateien"
+
+#: builtin/help.c:423
+msgid "Defining submodule properties"
+msgstr "Definition von Submodul-Eigenschaften"
+
+#: builtin/help.c:424
+msgid "Specifying revisions and ranges for Git"
+msgstr "Spezifikation von Commits und Bereichen für Git"
+
+#: builtin/help.c:425
+msgid "A tutorial introduction to Git (for version 1.5.1 or newer)"
+msgstr "Eine einführende Anleitung zu Git (für Version 1.5.1 oder neuer)"
+
+#: builtin/help.c:426
+msgid "An overview of recommended workflows with Git"
+msgstr "Eine Übersicht über empfohlene Arbeitsabläufe mit Git"
+
+#: builtin/help.c:438
+msgid "The common Git guides are:\n"
+msgstr "Die allgemein verwendeten Git-Anleitungen sind:\n"
+
+#: builtin/help.c:460 builtin/help.c:476
 #, c-format
 msgid "usage: %s%s"
 msgstr "Verwendung: %s%s"
 
-#: builtin/help.c:452
+#: builtin/help.c:492
 #, c-format
 msgid "`git %s' is aliased to `%s'"
 msgstr "für `git %s' wurde der Alias `%s' angelegt"
 
-#: builtin/index-pack.c:170
+#: builtin/index-pack.c:184
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "Objekt-Typen passen bei %s nicht zusammen"
 
-#: builtin/index-pack.c:190
+#: builtin/index-pack.c:204
 msgid "object of unexpected type"
 msgstr "Objekt hat unerwarteten Typ"
 
-#: builtin/index-pack.c:227
+#: builtin/index-pack.c:244
 #, c-format
 msgid "cannot fill %d byte"
 msgid_plural "cannot fill %d bytes"
 msgstr[0] "kann %d Byte nicht lesen"
 msgstr[1] "kann %d Bytes nicht lesen"
 
-#: builtin/index-pack.c:237
+#: builtin/index-pack.c:254
 msgid "early EOF"
 msgstr "zu frühes Dateiende"
 
-#: builtin/index-pack.c:238
+#: builtin/index-pack.c:255
 msgid "read error on input"
 msgstr "Fehler beim Lesen der Eingabe"
 
-#: builtin/index-pack.c:250
+#: builtin/index-pack.c:267
 msgid "used more bytes than were available"
 msgstr "verwendete mehr Bytes als verfügbar waren"
 
-#: builtin/index-pack.c:257
+#: builtin/index-pack.c:274
 msgid "pack too large for current definition of off_t"
 msgstr "Paket ist zu groß für die aktuelle Definition von off_t"
 
-#: builtin/index-pack.c:273
+#: builtin/index-pack.c:290
 #, c-format
 msgid "unable to create '%s'"
 msgstr "konnte '%s' nicht erstellen"
 
-#: builtin/index-pack.c:278
+#: builtin/index-pack.c:295
 #, c-format
 msgid "cannot open packfile '%s'"
 msgstr "Kann Paketdatei '%s' nicht öffnen"
 
-#: builtin/index-pack.c:292
+#: builtin/index-pack.c:309
 msgid "pack signature mismatch"
 msgstr "Paketsignatur stimmt nicht überein"
 
-#: builtin/index-pack.c:294
+#: builtin/index-pack.c:311
 #, c-format
 msgid "pack version %<PRIu32> unsupported"
 msgstr "Paketversion %<PRIu32> nicht unterstützt"
 
-#: builtin/index-pack.c:312
+#: builtin/index-pack.c:329
 #, c-format
 msgid "pack has bad object at offset %lu: %s"
 msgstr "Paket hat ein ungültiges Objekt bei Versatz %lu: %s"
 
-#: builtin/index-pack.c:434
+#: builtin/index-pack.c:451
 #, c-format
 msgid "inflate returned %d"
 msgstr "Dekomprimierung gab %d zurück"
 
-#: builtin/index-pack.c:483
+#: builtin/index-pack.c:500
 msgid "offset value overflow for delta base object"
 msgstr "Wert für Versatz bei Differenzobjekt übergelaufen"
 
-#: builtin/index-pack.c:491
+#: builtin/index-pack.c:508
 msgid "delta base offset is out of bound"
 msgstr ""
 "Wert für Versatz bei Differenzobjekt liegt außerhalb des gültigen Bereichs"
 
-#: builtin/index-pack.c:499
+#: builtin/index-pack.c:516
 #, c-format
 msgid "unknown object type %d"
 msgstr "Unbekannter Objekt-Typ %d"
 
-#: builtin/index-pack.c:530
+#: builtin/index-pack.c:547
 msgid "cannot pread pack file"
 msgstr "Kann Paketdatei %s nicht lesen"
 
-#: builtin/index-pack.c:532
+#: builtin/index-pack.c:549
 #, c-format
 msgid "premature end of pack file, %lu byte missing"
 msgid_plural "premature end of pack file, %lu bytes missing"
 msgstr[0] "frühzeitiges Ende der Paketdatei, vermisse %lu Byte"
 msgstr[1] "frühzeitiges Ende der Paketdatei, vermisse %lu Bytes"
 
-#: builtin/index-pack.c:558
+#: builtin/index-pack.c:575
 msgid "serious inflate inconsistency"
 msgstr "ernsthafte Inkonsistenz nach Dekomprimierung"
 
-#: builtin/index-pack.c:649 builtin/index-pack.c:655 builtin/index-pack.c:678
-#: builtin/index-pack.c:712 builtin/index-pack.c:721
+#: 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
 #, c-format
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "SHA1 KOLLISION MIT %s GEFUNDEN !"
 
-#: builtin/index-pack.c:652 builtin/pack-objects.c:170
+#: builtin/index-pack.c:669 builtin/pack-objects.c:170
 #: builtin/pack-objects.c:262
 #, c-format
 msgid "unable to read %s"
 msgstr "kann %s nicht lesen"
 
-#: builtin/index-pack.c:718
+#: builtin/index-pack.c:735
 #, c-format
 msgid "cannot read existing object %s"
 msgstr "Kann existierendes Objekt %s nicht lesen."
 
-#: builtin/index-pack.c:732
+#: builtin/index-pack.c:749
 #, c-format
 msgid "invalid blob object %s"
 msgstr "ungültiges Blob-Objekt %s"
 
-#: builtin/index-pack.c:747
+#: builtin/index-pack.c:763
 #, c-format
 msgid "invalid %s"
 msgstr "Ungültiger Objekt-Typ %s"
 
-#: builtin/index-pack.c:749
+#: builtin/index-pack.c:766
 msgid "Error in object"
 msgstr "Fehler in Objekt"
 
-#: builtin/index-pack.c:751
+#: builtin/index-pack.c:768
 #, c-format
 msgid "Not all child objects of %s are reachable"
 msgstr "Nicht alle Kind-Objekte von %s sind erreichbar"
 
-#: builtin/index-pack.c:821 builtin/index-pack.c:847
+#: builtin/index-pack.c:838 builtin/index-pack.c:868
 msgid "failed to apply delta"
 msgstr "Konnte Dateiunterschied nicht anwenden"
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Receiving objects"
 msgstr "Empfange Objekte"
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Indexing objects"
 msgstr "Indiziere Objekte"
 
-#: builtin/index-pack.c:1012
+#: builtin/index-pack.c:1035
 msgid "pack is corrupted (SHA1 mismatch)"
 msgstr "Paket ist beschädigt (SHA1 unterschiedlich)"
 
-#: builtin/index-pack.c:1017
+#: builtin/index-pack.c:1040
 msgid "cannot fstat packfile"
 msgstr "kann Paketdatei nicht lesen"
 
-#: builtin/index-pack.c:1020
+#: builtin/index-pack.c:1043
 msgid "pack has junk at the end"
 msgstr "Paketende enthält nicht verwendbaren Inhalt"
 
-#: builtin/index-pack.c:1031
+#: builtin/index-pack.c:1054
 msgid "confusion beyond insanity in parse_pack_objects()"
 msgstr "Fehler beim Ausführen von \"parse_pack_objects()\""
 
-#: builtin/index-pack.c:1054
+#: builtin/index-pack.c:1077
 msgid "Resolving deltas"
 msgstr "Löse Unterschiede auf"
 
-#: builtin/index-pack.c:1064
+#: builtin/index-pack.c:1087
 #, c-format
 msgid "unable to create thread: %s"
 msgstr "kann Thread nicht erzeugen: %s"
 
-#: builtin/index-pack.c:1106
+#: builtin/index-pack.c:1129
 msgid "confusion beyond insanity"
 msgstr "Fehler beim Auflösen der Unterschiede"
 
-#: builtin/index-pack.c:1112
+#: builtin/index-pack.c:1137
 #, c-format
 msgid "completed with %d local objects"
 msgstr "vervollständigt mit %d lokalen Objekten"
 
-#: builtin/index-pack.c:1121
+#: builtin/index-pack.c:1147
 #, c-format
 msgid "Unexpected tail checksum for %s (disk corruption?)"
 msgstr "Unerwartete Prüfsumme für %s (Festplattenfehler?)"
 
-#: builtin/index-pack.c:1125
+#: builtin/index-pack.c:1151
 #, c-format
 msgid "pack has %d unresolved delta"
 msgid_plural "pack has %d unresolved deltas"
 msgstr[0] "Paket hat %d unaufgelöste Unterschied"
 msgstr[1] "Paket hat %d unaufgelöste Unterschiede"
 
-#: builtin/index-pack.c:1150
+#: builtin/index-pack.c:1176
 #, c-format
 msgid "unable to deflate appended object (%d)"
 msgstr "Konnte angehängtes Objekt (%d) nicht komprimieren"
 
-#: builtin/index-pack.c:1229
+#: builtin/index-pack.c:1255
 #, c-format
 msgid "local object %s is corrupt"
 msgstr "lokales Objekt %s ist beschädigt"
 
-#: builtin/index-pack.c:1253
+#: builtin/index-pack.c:1279
 msgid "error while closing pack file"
 msgstr "Fehler beim Schließen der Paketdatei"
 
-#: builtin/index-pack.c:1266
+#: builtin/index-pack.c:1292
 #, c-format
 msgid "cannot write keep file '%s'"
 msgstr "Kann Paketbeschreibungsdatei '%s' nicht schreiben"
 
-#: builtin/index-pack.c:1274
+#: builtin/index-pack.c:1300
 #, c-format
 msgid "cannot close written keep file '%s'"
 msgstr "Kann eben erstellte Paketbeschreibungsdatei '%s' nicht schließen"
 
-#: builtin/index-pack.c:1287
+#: builtin/index-pack.c:1313
 msgid "cannot store pack file"
 msgstr "Kann Paketdatei nicht speichern"
 
-#: builtin/index-pack.c:1298
+#: builtin/index-pack.c:1324
 msgid "cannot store index file"
 msgstr "Kann Indexdatei nicht speichern"
 
-#: builtin/index-pack.c:1331
+#: builtin/index-pack.c:1357
 #, c-format
 msgid "bad pack.indexversion=%<PRIu32>"
 msgstr "\"pack.indexversion=%<PRIu32>\" ist ungültig"
 
-#: builtin/index-pack.c:1337
+#: builtin/index-pack.c:1363
 #, c-format
 msgid "invalid number of threads specified (%d)"
 msgstr "ungültige Anzahl von Threads angegeben (%d)"
 
-#: builtin/index-pack.c:1341 builtin/index-pack.c:1514
+#: builtin/index-pack.c:1367 builtin/index-pack.c:1545
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "keine Unterstützung von Threads, '%s' wird ignoriert"
 
-#: builtin/index-pack.c:1399
+#: builtin/index-pack.c:1425
 #, c-format
 msgid "Cannot open existing pack file '%s'"
 msgstr "Kann existierende Paketdatei '%s' nicht öffnen"
 
-#: builtin/index-pack.c:1401
+#: builtin/index-pack.c:1427
 #, c-format
 msgid "Cannot open existing pack idx file for '%s'"
 msgstr "Kann existierende Indexdatei für Paket '%s' nicht öffnen"
 
-#: builtin/index-pack.c:1448
+#: builtin/index-pack.c:1474
 #, c-format
 msgid "non delta: %d object"
 msgid_plural "non delta: %d objects"
 msgstr[0] "kein Unterschied: %d Objekt"
 msgstr[1] "kein Unterschied: %d Objekte"
 
-#: builtin/index-pack.c:1455
+#: builtin/index-pack.c:1481
 #, c-format
 msgid "chain length = %d: %lu object"
 msgid_plural "chain length = %d: %lu objects"
 msgstr[0] "Länge der Objekt-Liste = %d: %lu Objekt"
 msgstr[1] "Länge der Objekt-Liste = %d: %lu Objekte"
 
-#: builtin/index-pack.c:1482
+#: builtin/index-pack.c:1509
 msgid "Cannot come back to cwd"
 msgstr "Kann nicht zurück zu Arbeitsverzeichnis wechseln"
 
-#: builtin/index-pack.c:1526 builtin/index-pack.c:1529
-#: builtin/index-pack.c:1541 builtin/index-pack.c:1545
+#: builtin/index-pack.c:1557 builtin/index-pack.c:1560
+#: builtin/index-pack.c:1572 builtin/index-pack.c:1576
 #, c-format
 msgid "bad %s"
 msgstr "%s ist ungültig"
 
-#: builtin/index-pack.c:1559
+#: builtin/index-pack.c:1590
 msgid "--fix-thin cannot be used without --stdin"
 msgstr "Die Option --fix-thin kann nicht ohne --stdin verwendet werden."
 
-#: builtin/index-pack.c:1563 builtin/index-pack.c:1573
+#: builtin/index-pack.c:1594 builtin/index-pack.c:1604
 #, c-format
 msgid "packfile name '%s' does not end with '.pack'"
 msgstr "Name der Paketdatei '%s' endet nicht mit '.pack'"
 
-#: builtin/index-pack.c:1582
+#: builtin/index-pack.c:1613
 msgid "--verify with no packfile name given"
 msgstr "Die Option --verify wurde ohne Namen der Paketdatei angegeben."
 
@@ -5452,17 +6009,17 @@ msgstr "kann Verzeichnis '%s' nicht öffnen"
 #: builtin/init-db.c:97
 #, c-format
 msgid "cannot readlink '%s'"
-msgstr "kann Verknüpfung '%s' nicht lesen"
+msgstr "kann Verweis '%s' nicht lesen"
 
 #: builtin/init-db.c:99
 #, c-format
 msgid "insanely long symlink %s"
-msgstr "zu lange symbolische Verknüpfung %s"
+msgstr "zu langer symbolischer Verweis %s"
 
 #: builtin/init-db.c:102
 #, c-format
 msgid "cannot symlink '%s' '%s'"
-msgstr "kann '%s' nicht mit '%s' symbolisch verknüpfen"
+msgstr "kann symbolischen Verweis '%s' auf '%s' nicht erstellen"
 
 #: builtin/init-db.c:106
 #, c-format
@@ -5512,7 +6069,7 @@ msgstr "Konnte %s nicht nach %s verschieben"
 #: builtin/init-db.c:363
 #, c-format
 msgid "Could not create git link %s"
-msgstr "Konnte git-Verknüpfung %s nicht erstellen"
+msgstr "Konnte git-Verweis %s nicht erstellen"
 
 #.
 #. * TRANSLATORS: The first '%s' is either "Reinitialized
@@ -5522,7 +6079,7 @@ msgstr "Konnte git-Verknüpfung %s nicht erstellen"
 #: builtin/init-db.c:420
 #, c-format
 msgid "%s%s Git repository in %s%s\n"
-msgstr "%s%s Git-Projektarchiv in %s%s\n"
+msgstr "%s%s Git-Repository in %s%s\n"
 
 #: builtin/init-db.c:421
 msgid "Reinitialized existing"
@@ -5554,10 +6111,9 @@ msgstr "Berechtigungen"
 
 #: builtin/init-db.c:491
 msgid "specify that the git repository is to be shared amongst several users"
-msgstr ""
-"gibt an, dass das Git-Projektarchiv mit mehreren Benutzern geteilt wird"
+msgstr "gibt an, dass das Git-Repository mit mehreren Benutzern geteilt wird"
 
-#: builtin/init-db.c:493 builtin/prune-packed.c:77
+#: builtin/init-db.c:493 builtin/prune-packed.c:75
 msgid "be quiet"
 msgstr "weniger Ausgaben"
 
@@ -5587,373 +6143,385 @@ msgstr "Kann nicht auf aktuelles Arbeitsverzeichnis zugreifen."
 #: builtin/init-db.c:586
 #, c-format
 msgid "Cannot access work tree '%s'"
-msgstr "Kann nicht auf Arbeitsbaum '%s' zugreifen."
+msgstr "Kann nicht auf Arbeitsverzeichnis '%s' zugreifen."
 
-#: builtin/log.c:39
-msgid "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
-msgstr "git log [<Optionen>] [<seit>..<bis>] [[--] <Pfad>...]\n"
+#: builtin/log.c:41
+msgid "git log [<options>] [<revision range>] [[--] <path>...]\n"
+msgstr "git log [<Optionen>] [Commitbereich>] [[--] <Pfad>...]\n"
 
-#: builtin/log.c:40
+#: builtin/log.c:42
 msgid "   or: git show [options] <object>..."
 msgstr "   oder: git show [Optionen] <Objekt>..."
 
-#: builtin/log.c:102
+#: builtin/log.c:124
 msgid "suppress diff output"
 msgstr "unterdrückt Ausgabe der Unterschiede"
 
-#: builtin/log.c:103
+#: builtin/log.c:125
 msgid "show source"
 msgstr "zeigt Quelle"
 
-#: builtin/log.c:104
+#: builtin/log.c:126
 msgid "Use mail map file"
 msgstr "verwendet \"mailmap\"-Datei"
 
-#: builtin/log.c:105
+#: builtin/log.c:127
 msgid "decorate options"
 msgstr "decorate-Optionen"
 
-#: builtin/log.c:198
+#: builtin/log.c:230
 #, c-format
 msgid "Final output: %d %s\n"
 msgstr "letzte Ausgabe: %d %s\n"
 
-#: builtin/log.c:419 builtin/log.c:511
+#: builtin/log.c:453 builtin/log.c:545
 #, c-format
 msgid "Could not read object %s"
 msgstr "Kann Objekt %s nicht lesen."
 
-#: builtin/log.c:535
+#: builtin/log.c:569
 #, c-format
 msgid "Unknown type: %d"
 msgstr "Unbekannter Typ: %d"
 
-#: builtin/log.c:627
+#: builtin/log.c:669
 msgid "format.headers without value"
 msgstr "format.headers ohne Wert"
 
-#: builtin/log.c:701
+#: builtin/log.c:751
 msgid "name of output directory is too long"
 msgstr "Name des Ausgabeverzeichnisses ist zu lang."
 
-#: builtin/log.c:717
+#: builtin/log.c:767
 #, c-format
 msgid "Cannot open patch file %s"
 msgstr "Kann Patch-Datei %s nicht öffnen"
 
-#: builtin/log.c:731
+#: builtin/log.c:781
 msgid "Need exactly one range."
-msgstr "Brauche genau einen Versionsbereich."
+msgstr "Brauche genau einen Commit-Bereich."
 
-#: builtin/log.c:739
+#: builtin/log.c:789
 msgid "Not a range."
-msgstr "Kein Versionsbereich."
+msgstr "Kein Commit-Bereich."
 
-#: builtin/log.c:812
+#: builtin/log.c:891
 msgid "Cover letter needs email format"
 msgstr "Anschreiben benötigt E-Mail-Format"
 
-#: builtin/log.c:885
+#: builtin/log.c:967
 #, c-format
 msgid "insane in-reply-to: %s"
 msgstr "ungültiges in-reply-to: %s"
 
-#: builtin/log.c:913
+#: builtin/log.c:995
 msgid "git format-patch [options] [<since> | <revision range>]"
-msgstr "git format-patch [Optionen] [<seit> | <Revisionsbereich>]"
+msgstr "git format-patch [Optionen] [<seit> | <Commitbereich>]"
 
-#: builtin/log.c:958
+#: builtin/log.c:1040
 msgid "Two output directories?"
 msgstr "Zwei Ausgabeverzeichnisse?"
 
-#: builtin/log.c:1097
+#: builtin/log.c:1155
 msgid "use [PATCH n/m] even with a single patch"
 msgstr "verwendet [PATCH n/m] auch mit einzelnem Patch"
 
-#: builtin/log.c:1100
+#: builtin/log.c:1158
 msgid "use [PATCH] even with multiple patches"
 msgstr "verwendet [PATCH] auch mit mehreren Patches"
 
-#: builtin/log.c:1104
+#: builtin/log.c:1162
 msgid "print patches to standard out"
 msgstr "Ausgabe der Patches in Standard-Ausgabe"
 
-#: builtin/log.c:1106
+#: builtin/log.c:1164
 msgid "generate a cover letter"
 msgstr "erzeugt ein Deckblatt"
 
-#: builtin/log.c:1108
+#: builtin/log.c:1166
 msgid "use simple number sequence for output file names"
 msgstr "verwendet einfache Nummernfolge für die Namen der Ausgabedateien"
 
-#: builtin/log.c:1109
+#: builtin/log.c:1167
 msgid "sfx"
 msgstr "Dateiendung"
 
-#: builtin/log.c:1110
+#: builtin/log.c:1168
 msgid "use <sfx> instead of '.patch'"
 msgstr "verwendet <Dateiendung> anstatt '.patch'"
 
-#: builtin/log.c:1112
+#: builtin/log.c:1170
 msgid "start numbering patches at <n> instead of 1"
 msgstr "beginnt die Nummerierung der Patches bei <n> anstatt bei 1"
 
-#: builtin/log.c:1114
+#: builtin/log.c:1172
 msgid "mark the series as Nth re-roll"
 msgstr "kennzeichnet die Serie als n-te Fassung"
 
-#: builtin/log.c:1116
+#: builtin/log.c:1174
 msgid "Use [<prefix>] instead of [PATCH]"
 msgstr "verwendet [<Prefix>] anstatt [PATCH]"
 
-#: builtin/log.c:1119
+#: builtin/log.c:1177
 msgid "store resulting files in <dir>"
 msgstr "speichert erzeugte Dateien in <Verzeichnis>"
 
-#: builtin/log.c:1122
+#: builtin/log.c:1180
 msgid "don't strip/add [PATCH]"
 msgstr "[PATCH] wird nicht entfernt/hinzugefügt"
 
-#: builtin/log.c:1125
+#: builtin/log.c:1183
 msgid "don't output binary diffs"
 msgstr "gibt keine binären Unterschiede aus"
 
-#: builtin/log.c:1127
+#: builtin/log.c:1185
 msgid "don't include a patch matching a commit upstream"
 msgstr ""
-"schließt keine Patches ein, die einer Version im Übernahmezweig entsprechen"
+"schließt keine Patches ein, die einem Commit im Upstream-Branch entsprechen"
 
-#: builtin/log.c:1129
+#: builtin/log.c:1187
 msgid "show patch format instead of default (patch + stat)"
 msgstr "zeigt Patchformat anstatt des Standards (Patch + Zusammenfassung)"
 
-#: builtin/log.c:1131
+#: builtin/log.c:1189
 msgid "Messaging"
 msgstr "Email-Einstellungen"
 
-#: builtin/log.c:1132
+#: builtin/log.c:1190
 msgid "header"
 msgstr "Header"
 
-#: builtin/log.c:1133
+#: builtin/log.c:1191
 msgid "add email header"
 msgstr "fügt Email-Header hinzu"
 
-#: builtin/log.c:1134 builtin/log.c:1136
+#: builtin/log.c:1192 builtin/log.c:1194
 msgid "email"
 msgstr "Email"
 
-#: builtin/log.c:1134
+#: builtin/log.c:1192
 msgid "add To: header"
 msgstr "fügt  \"To:\"-Header hinzu"
 
-#: builtin/log.c:1136
+#: builtin/log.c:1194
 msgid "add Cc: header"
 msgstr "fügt \"Cc:\"-Header hinzu"
 
-#: builtin/log.c:1138
+#: builtin/log.c:1196
+msgid "ident"
+msgstr "Ident"
+
+#: builtin/log.c:1197
+msgid "set From address to <ident> (or committer ident if absent)"
+msgstr ""
+"setzt \"From\"-Adresse auf <Ident> (oder Ident des Commit-Erstellers,wenn "
+"fehlend)"
+
+#: builtin/log.c:1199
 msgid "message-id"
 msgstr "message-id"
 
-#: builtin/log.c:1139
+#: builtin/log.c:1200
 msgid "make first mail a reply to <message-id>"
 msgstr "macht aus erster Email eine Antwort zu <message-id>"
 
-#: builtin/log.c:1140 builtin/log.c:1143
+#: builtin/log.c:1201 builtin/log.c:1204
 msgid "boundary"
 msgstr "Grenze"
 
-#: builtin/log.c:1141
+#: builtin/log.c:1202
 msgid "attach the patch"
 msgstr "hängt einen Patch an"
 
-#: builtin/log.c:1144
+#: builtin/log.c:1205
 msgid "inline the patch"
 msgstr "fügt den Patch direkt in die Nachricht ein"
 
-#: builtin/log.c:1148
+#: builtin/log.c:1209
 msgid "enable message threading, styles: shallow, deep"
 msgstr "aktiviert Nachrichtenverkettung, Stile: shallow, deep"
 
-#: builtin/log.c:1150
+#: builtin/log.c:1211
 msgid "signature"
 msgstr "Signatur"
 
-#: builtin/log.c:1151
+#: builtin/log.c:1212
 msgid "add a signature"
 msgstr "fügt eine Signatur hinzu"
 
-#: builtin/log.c:1153
+#: builtin/log.c:1214
 msgid "don't print the patch filenames"
 msgstr "zeigt keine Dateinamen der Patches"
 
-#: builtin/log.c:1202
+#: builtin/log.c:1288
 #, c-format
-msgid "bogus committer info %s"
-msgstr "unechte Einreicher-Informationen %s"
+msgid "invalid ident line: %s"
+msgstr "Ungültige Identifikationszeile: %s"
 
-#: builtin/log.c:1247
+#: builtin/log.c:1303
 msgid "-n and -k are mutually exclusive."
 msgstr "Die Optionen -n und -k schließen sich gegenseitig aus."
 
-#: builtin/log.c:1249
+#: builtin/log.c:1305
 msgid "--subject-prefix and -k are mutually exclusive."
 msgstr "Die Optionen --subject-prefix und -k schließen sich gegenseitig aus."
 
-#: builtin/log.c:1257
+#: builtin/log.c:1313
 msgid "--name-only does not make sense"
 msgstr "Die Option --name-only kann nicht verwendet werden."
 
-#: builtin/log.c:1259
+#: builtin/log.c:1315
 msgid "--name-status does not make sense"
 msgstr "Die Option --name-status kann nicht verwendet werden."
 
-#: builtin/log.c:1261
+#: builtin/log.c:1317
 msgid "--check does not make sense"
 msgstr "Die Option --check kann nicht verwendet werden."
 
-#: builtin/log.c:1284
+#: builtin/log.c:1340
 msgid "standard output, or directory, which one?"
 msgstr "Standard-Ausgabe oder Verzeichnis, welches von beidem?"
 
-#: builtin/log.c:1286
+#: builtin/log.c:1342
 #, c-format
 msgid "Could not create directory '%s'"
 msgstr "Konnte Verzeichnis '%s' nicht erstellen."
 
-#: builtin/log.c:1439
+#: builtin/log.c:1490
 msgid "Failed to create output files"
 msgstr "Fehler beim Erstellen der Ausgabedateien."
 
-#: builtin/log.c:1488
+#: builtin/log.c:1539
 msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
-msgstr "git cherry [-v] [<Übernahmezweig> [<Arbeitszweig> [<Limit>]]]"
+msgstr "git cherry [-v] [<Upstream> [<Branch> [<Limit>]]]"
 
-#: builtin/log.c:1543
+#: builtin/log.c:1594
 #, c-format
 msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr ""
-"Konnte gefolgten, externen Zweig nicht finden, bitte geben Sie <upstream> "
+"Konnte gefolgten Remote-Branch nicht finden, bitte geben Sie <Upstream> "
 "manuell an.\n"
 
-#: builtin/log.c:1556 builtin/log.c:1558 builtin/log.c:1570
+#: builtin/log.c:1607 builtin/log.c:1609 builtin/log.c:1621
 #, c-format
 msgid "Unknown commit %s"
-msgstr "Unbekannte Version %s"
+msgstr "Unbekannter Commit %s"
 
-#: builtin/ls-files.c:409
+#: builtin/ls-files.c:408
 msgid "git ls-files [options] [<file>...]"
 msgstr "git ls-files [Optionen] [<Datei>...]"
 
-#: builtin/ls-files.c:466
+#: builtin/ls-files.c:465
 msgid "identify the file status with tags"
-msgstr "zeigt den Dateistatus mit Markierungen"
+msgstr "zeigt den Dateistatus mit Tags"
 
-#: builtin/ls-files.c:468
+#: builtin/ls-files.c:467
 msgid "use lowercase letters for 'assume unchanged' files"
 msgstr ""
 "verwendet Kleinbuchstaben für Dateien mit 'assume unchanged' Markierung"
 
-#: builtin/ls-files.c:470
+#: builtin/ls-files.c:469
 msgid "show cached files in the output (default)"
 msgstr "zeigt zwischengespeicherten Dateien in der Ausgabe an (Standard)"
 
-#: builtin/ls-files.c:472
+#: builtin/ls-files.c:471
 msgid "show deleted files in the output"
 msgstr "zeigt entfernte Dateien in der Ausgabe an"
 
-#: builtin/ls-files.c:474
+#: builtin/ls-files.c:473
 msgid "show modified files in the output"
 msgstr "zeigt geänderte Dateien in der Ausgabe an"
 
-#: builtin/ls-files.c:476
+#: builtin/ls-files.c:475
 msgid "show other files in the output"
 msgstr "zeigt sonstige Dateien in der Ausgabe an"
 
-#: builtin/ls-files.c:478
+#: builtin/ls-files.c:477
 msgid "show ignored files in the output"
 msgstr "zeigt ignorierte Dateien in der Ausgabe an"
 
-#: builtin/ls-files.c:481
+#: builtin/ls-files.c:480
 msgid "show staged contents' object name in the output"
-msgstr "zeigt Objektnamen von Inhalten in der Bereitstellung in der Ausgabe an"
+msgstr ""
+"zeigt Objektnamen von Inhalten, die zum Commit vorgemerkt sind, in der "
+"Ausgabe an"
 
-#: builtin/ls-files.c:483
+#: builtin/ls-files.c:482
 msgid "show files on the filesystem that need to be removed"
 msgstr "zeigt Dateien im Dateisystem, die gelöscht werden müssen, an"
 
-#: builtin/ls-files.c:485
+#: builtin/ls-files.c:484
 msgid "show 'other' directories' name only"
 msgstr "zeigt nur Namen von 'sonstigen' Verzeichnissen an"
 
-#: builtin/ls-files.c:488
+#: builtin/ls-files.c:487
 msgid "don't show empty directories"
 msgstr "zeigt keine leeren Verzeichnisse an"
 
-#: builtin/ls-files.c:491
+#: builtin/ls-files.c:490
 msgid "show unmerged files in the output"
 msgstr "zeigt nicht zusammengeführte Dateien in der Ausgabe an"
 
-#: builtin/ls-files.c:493
+#: builtin/ls-files.c:492
 msgid "show resolve-undo information"
 msgstr "zeigt 'resolve-undo' Informationen an"
 
-#: builtin/ls-files.c:495
+#: builtin/ls-files.c:494
 msgid "skip files matching pattern"
 msgstr "lässt Dateien aus, die einem Muster entsprechen"
 
-#: builtin/ls-files.c:498
+#: builtin/ls-files.c:497
 msgid "exclude patterns are read from <file>"
 msgstr "schließt Muster, gelesen von <Datei>, aus"
 
-#: builtin/ls-files.c:501
+#: builtin/ls-files.c:500
 msgid "read additional per-directory exclude patterns in <file>"
 msgstr "liest zusätzliche pro-Verzeichnis Auschlussmuster aus <Datei>"
 
-#: builtin/ls-files.c:503
+#: builtin/ls-files.c:502
 msgid "add the standard git exclusions"
 msgstr "fügt die standardmäßigen Git-Ausschlüsse hinzu"
 
-#: builtin/ls-files.c:506
+#: builtin/ls-files.c:505
 msgid "make the output relative to the project top directory"
 msgstr "Ausgabe relativ zum Projektverzeichnis"
 
-#: builtin/ls-files.c:509
+#: builtin/ls-files.c:508
 msgid "if any <file> is not in the index, treat this as an error"
 msgstr ""
-"behandle es als Fehler, wenn sich eine <Datei> nicht in der Bereitstellung "
+"behandle es als Fehler, wenn sich eine <Datei> nicht in der Staging-Area "
 "befindet"
 
-#: builtin/ls-files.c:510
+#: builtin/ls-files.c:509
 msgid "tree-ish"
-msgstr "Versionsreferenz"
+msgstr "Commit-Referenz"
 
-#: builtin/ls-files.c:511
+#: builtin/ls-files.c:510
 msgid "pretend that paths removed since <tree-ish> are still present"
 msgstr ""
-"gibt vor, dass Pfade, die seit <Versionsreferenz> gelöscht wurden, immer "
-"noch vorhanden sind"
+"gibt vor, dass Pfade, die seit <Commit-Referenz> gelöscht wurden, immer noch "
+"vorhanden sind"
 
-#: builtin/ls-files.c:513
+#: builtin/ls-files.c:512
 msgid "show debugging data"
 msgstr "zeigt Ausgaben zur Fehlersuche an"
 
 #: builtin/ls-tree.c:27
 msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
-msgstr "git ls-tree [<Optionen>] <Versionsreferenz> [<Pfad>...]"
+msgstr "git ls-tree [<Optionen>] <Commit-Referenz> [<Pfad>...]"
 
 #: builtin/ls-tree.c:125
 msgid "only show trees"
-msgstr "zeigt nur Bäume an"
+msgstr "zeigt nur Verzeichnisse an"
 
 #: builtin/ls-tree.c:127
 msgid "recurse into subtrees"
-msgstr "führt Rekursion in Teilbäumen durch"
+msgstr "führt Rekursion in Unterverzeichnissen durch"
 
 #: builtin/ls-tree.c:129
 msgid "show trees when recursing"
-msgstr "zeigt Bäume bei Rekursion an"
+msgstr "zeigt Verzeichnisse bei Rekursion an"
 
 #: builtin/ls-tree.c:132
 msgid "terminate entries with NUL byte"
@@ -5974,191 +6542,187 @@ msgstr "verwendet vollständige Pfadnamen"
 #: builtin/ls-tree.c:142
 msgid "list entire tree; not just current directory (implies --full-name)"
 msgstr ""
-"listet den gesamten Baum auf; nicht nur das aktuelle Verzeichnis (impliziert "
-"--full-name)"
+"listet das gesamte Verzeichnis auf; nicht nur das aktuelle Verzeichnis "
+"(impliziert --full-name)"
 
 #: builtin/merge.c:43
 msgid "git merge [options] [<commit>...]"
-msgstr "git merge [Optionen] [<Version>...]"
+msgstr "git merge [Optionen] [<Commit>...]"
 
 #: builtin/merge.c:44
 msgid "git merge [options] <msg> HEAD <commit>"
-msgstr "git merge [Optionen] <Beschreibung> HEAD <Version>"
+msgstr "git merge [Optionen] <Beschreibung> HEAD <Commit>"
 
 #: builtin/merge.c:45
 msgid "git merge --abort"
 msgstr "git merge --abort"
 
-#: builtin/merge.c:90
+#: builtin/merge.c:98
 msgid "switch `m' requires a value"
 msgstr "Schalter 'm' erfordert einen Wert."
 
-#: builtin/merge.c:127
+#: builtin/merge.c:135
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
-msgstr "Konnte Zusammenführungsstrategie '%s' nicht finden.\n"
+msgstr "Konnte Merge-Strategie '%s' nicht finden.\n"
 
-#: builtin/merge.c:128
+#: builtin/merge.c:136
 #, c-format
 msgid "Available strategies are:"
 msgstr "Verfügbare Strategien sind:"
 
-#: builtin/merge.c:133
+#: builtin/merge.c:141
 #, c-format
 msgid "Available custom strategies are:"
 msgstr "Verfügbare benutzerdefinierte Strategien sind:"
 
-#: builtin/merge.c:183
+#: builtin/merge.c:198
 msgid "do not show a diffstat at the end of the merge"
-msgstr ""
-"zeigt keine Zusammenfassung der Unterschiede am Schluss der Zusammenführung "
-"an"
+msgstr "zeigt keine Zusammenfassung der Unterschiede am Schluss des Merges an"
 
-#: builtin/merge.c:186
+#: builtin/merge.c:201
 msgid "show a diffstat at the end of the merge"
-msgstr ""
-"zeigt eine Zusammenfassung der Unterschiede am Schluss der Zusammenführung an"
+msgstr "zeigt eine Zusammenfassung der Unterschiede am Schluss des Merges an"
 
-#: builtin/merge.c:187
+#: builtin/merge.c:202
 msgid "(synonym to --stat)"
 msgstr "(Synonym für --stat)"
 
-#: builtin/merge.c:189
+#: builtin/merge.c:204
 msgid "add (at most <n>) entries from shortlog to merge commit message"
 msgstr ""
-"fügt (höchstens <n>) Einträge von \"shortlog\" zur Beschreibung de"
-"Zusammenführung hinzu"
+"fügt (höchstens <n>) Einträge von \"shortlog\" zur Beschreibung des Merge-"
+"Commits hinzu"
 
-#: builtin/merge.c:192
+#: builtin/merge.c:207
 msgid "create a single commit instead of doing a merge"
-msgstr "erzeugt eine einzelne Version anstatt einer Zusammenführung"
+msgstr "erzeugt einen einzelnen Commit anstatt eines Merges"
 
-#: builtin/merge.c:194
+#: builtin/merge.c:209
 msgid "perform a commit if the merge succeeds (default)"
-msgstr ""
-"führt eine Eintragung durch, wenn die Zusammenführung erfolgreich war "
-"(Standard)"
+msgstr "führt einen Commit durch, wenn der Merge erfolgreich war (Standard)"
 
-#: builtin/merge.c:196
+#: builtin/merge.c:211
 msgid "edit message before committing"
-msgstr "Bearbeitung der Versionsbeschreibung vor der Eintragung"
+msgstr "Bearbeitung der Beschreibung vor dem Commit"
 
-#: builtin/merge.c:198
+#: builtin/merge.c:212
 msgid "allow fast-forward (default)"
 msgstr "erlaubt Vorspulen (Standard)"
 
-#: builtin/merge.c:200
+#: builtin/merge.c:214
 msgid "abort if fast-forward is not possible"
 msgstr "bricht ab, wenn kein Vorspulen möglich ist"
 
-#: builtin/merge.c:202 builtin/notes.c:866 builtin/revert.c:112
+#: builtin/merge.c:218
+msgid "Verify that the named commit has a valid GPG signature"
+msgstr "überprüft den genannten Commit auf eine gültige GPG-Signatur"
+
+#: builtin/merge.c:219 builtin/notes.c:738 builtin/revert.c:114
 msgid "strategy"
 msgstr "Strategie"
 
-#: builtin/merge.c:203
+#: builtin/merge.c:220
 msgid "merge strategy to use"
-msgstr "zu verwendende Zusammenführungsstrategie"
+msgstr "zu verwendende Merge-Strategie"
 
-#: builtin/merge.c:204
+#: builtin/merge.c:221
 msgid "option=value"
 msgstr "Option=Wert"
 
-#: builtin/merge.c:205
+#: builtin/merge.c:222
 msgid "option for selected merge strategy"
-msgstr "Option für ausgewählte Zusammenführungsstrategie"
+msgstr "Option für ausgewählte Merge-Strategie"
 
-#: builtin/merge.c:207
+#: builtin/merge.c:224
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
-"führt Versionsbeschreibung zusammen (für eine Zusammenführung, die kein "
-"Vorspulen war)"
+"führt Commit-Beschreibung zusammen (für einen Merge, der kein Vorspulen war)"
 
-#: builtin/merge.c:211
+#: builtin/merge.c:228
 msgid "abort the current in-progress merge"
-msgstr "bricht die sich im Gange befindliche Zusammenführung ab"
+msgstr "bricht den sich im Gange befindlichen Merge ab"
 
-#: builtin/merge.c:240
+#: builtin/merge.c:257
 msgid "could not run stash."
 msgstr "Konnte \"stash\" nicht ausführen."
 
-#: builtin/merge.c:245
+#: builtin/merge.c:262
 msgid "stash failed"
 msgstr "\"stash\" fehlgeschlagen"
 
-#: builtin/merge.c:250
+#: builtin/merge.c:267
 #, c-format
 msgid "not a valid object: %s"
 msgstr "kein gültiges Objekt: %s"
 
-#: builtin/merge.c:269 builtin/merge.c:286
+#: builtin/merge.c:286 builtin/merge.c:303
 msgid "read-tree failed"
 msgstr "read-tree fehlgeschlagen"
 
-#: builtin/merge.c:316
+#: builtin/merge.c:333
 msgid " (nothing to squash)"
 msgstr " (nichts zu quetschen)"
 
-#: builtin/merge.c:329
+#: builtin/merge.c:346
 #, c-format
 msgid "Squash commit -- not updating HEAD\n"
-msgstr "Quetsche Version -- Zweigspitze (HEAD) wird nicht aktualisiert\n"
+msgstr "Quetsche Commit -- HEAD wird nicht aktualisiert\n"
 
-#: builtin/merge.c:361
+#: builtin/merge.c:378
 msgid "Writing SQUASH_MSG"
 msgstr "Schreibe SQUASH_MSG"
 
-#: builtin/merge.c:363
+#: builtin/merge.c:380
 msgid "Finishing SQUASH_MSG"
 msgstr "Schließe SQUASH_MSG ab"
 
-#: builtin/merge.c:386
+#: builtin/merge.c:403
 #, c-format
 msgid "No merge message -- not updating HEAD\n"
-msgstr ""
-"Keine Zusammenführungsbeschreibung -- Zweigspitze (HEAD) wird nicht "
-"aktualisiert\n"
+msgstr "Keine Merge-Commit-Beschreibung -- HEAD wird nicht aktualisiert\n"
 
-#: builtin/merge.c:436
+#: builtin/merge.c:453
 #, c-format
 msgid "'%s' does not point to a commit"
-msgstr "'%s' zeigt auf keine Version"
+msgstr "'%s' zeigt auf keinen Commit"
 
-#: builtin/merge.c:535
+#: builtin/merge.c:565
 #, c-format
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr "Ungültiger branch.%s.mergeoptions String: %s"
 
-#: builtin/merge.c:628
+#: builtin/merge.c:657
 msgid "git write-tree failed to write a tree"
-msgstr "\"git write-tree\" schlug beim Schreiben eines Baumes fehl"
+msgstr "\"git write-tree\" schlug beim Schreiben eines \"Tree\"-Objektes fehl"
 
-#: builtin/merge.c:656
+#: builtin/merge.c:685
 msgid "Not handling anything other than two heads merge."
-msgstr "Es wird nur die Zusammenführung von zwei Zweigen behandelt."
+msgstr "Es wird nur der Merge von zwei Branches behandelt."
 
-#: builtin/merge.c:670
+#: builtin/merge.c:699
 #, c-format
 msgid "Unknown option for merge-recursive: -X%s"
 msgstr "Unbekannte Option für merge-recursive: -X%s"
 
-#: builtin/merge.c:684
+#: builtin/merge.c:713
 #, c-format
 msgid "unable to write %s"
 msgstr "konnte %s nicht schreiben"
 
-#: builtin/merge.c:773
+#: builtin/merge.c:802
 #, c-format
 msgid "Could not read from '%s'"
 msgstr "konnte nicht von '%s' lesen"
 
-#: builtin/merge.c:782
+#: builtin/merge.c:811
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr ""
-"Zusammenführung wurde nicht eingetragen; benutzen Sie 'git commit' um die "
-"Zusammenführung abzuschließen.\n"
+"Merge wurde nicht committet; benutzen Sie 'git commit' um den Merge "
+"abzuschließen.\n"
 
-#: builtin/merge.c:788
+#: builtin/merge.c:817
 #, c-format
 msgid ""
 "Please enter a commit message to explain why this merge is necessary,\n"
@@ -6167,197 +6731,212 @@ msgid ""
 "Lines starting with '%c' will be ignored, and an empty message aborts\n"
 "the commit.\n"
 msgstr ""
-"Bitte geben Sie eine Versionsbeschreibung ein um zu erklären, warum diese "
-"Zusammenführung erforderlich ist,\n"
-"insbesondere wenn es einen aktualisierten, externen Zweig mit einem Thema-"
-"Zweig zusammenführt.\n"
+"Bitte geben Sie eine Commit-Beschreibung ein um zu erklären, warum dieser\n"
+"Merge erforderlich ist, insbesondere wenn es einen aktualisierten\n"
+"Upstream-Branch mit einem Thema-Branch zusammenführt.\n"
 "\n"
-"Zeilen beginnend mit '%c' werden ignoriert, und eine leere Beschreibung "
-"bricht die Eintragung ab.\n"
+"Zeilen beginnend mit '%c' werden ignoriert, und eine leere Beschreibung\n"
+"bricht den Commit ab.\n"
 
-#: builtin/merge.c:812
+#: builtin/merge.c:841
 msgid "Empty commit message."
-msgstr "Leere Versionsbeschreibung"
+msgstr "Leere Commit-Beschreibung"
 
-#: builtin/merge.c:824
+#: builtin/merge.c:853
 #, c-format
 msgid "Wonderful.\n"
 msgstr "Wunderbar.\n"
 
-#: builtin/merge.c:889
+#: builtin/merge.c:918
 #, c-format
 msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
 msgstr ""
-"Automatische Zusammenführung fehlgeschlagen; beheben Sie die Konflikte und "
-"tragen Sie dann das Ergebnis ein.\n"
+"Automatischer Merge fehlgeschlagen; beheben Sie die Konflikte und committen "
+"Sie dann das Ergebnis.\n"
 
-#: builtin/merge.c:905
+#: builtin/merge.c:934
 #, c-format
 msgid "'%s' is not a commit"
-msgstr "'%s' ist keine Version"
+msgstr "'%s' ist kein Commit"
 
-#: builtin/merge.c:946
+#: builtin/merge.c:975
 msgid "No current branch."
-msgstr "Sie befinden sich auf keinem Zweig."
+msgstr "Sie befinden sich auf keinem Branch."
 
-#: builtin/merge.c:948
+#: builtin/merge.c:977
 msgid "No remote for the current branch."
-msgstr "Kein externes Archiv für den aktuellen Zweig."
+msgstr "Kein Remote-Repository für den aktuellen Branch."
 
-#: builtin/merge.c:950
+#: builtin/merge.c:979
 msgid "No default upstream defined for the current branch."
 msgstr ""
-"Es ist kein externes Standard-Projektarchiv für den aktuellen Zweig "
-"definiert."
+"Es ist kein Standard-Upstream-Branch für den aktuellen Branch definiert."
 
-#: builtin/merge.c:955
+#: builtin/merge.c:984
 #, c-format
-msgid "No remote tracking branch for %s from %s"
-msgstr "Kein externer Übernahmezweig für %s von %s"
-
-#: builtin/merge.c:1042 builtin/merge.c:1199
-#, c-format
-msgid "%s - not something we can merge"
-msgstr "%s - nichts was wir zusammenführen können"
+msgid "No remote-tracking branch for %s from %s"
+msgstr "Kein Remote-Tracking-Branch für %s von %s"
 
-#: builtin/merge.c:1110
+#: builtin/merge.c:1140
 msgid "There is no merge to abort (MERGE_HEAD missing)."
-msgstr "Es gibt keine Zusammenführung zum Abbrechen (vermisse MERGE_HEAD)"
+msgstr "Es gibt keinen Merge zum Abbrechen (vermisse MERGE_HEAD)"
 
-#: builtin/merge.c:1126 git-pull.sh:31
+#: builtin/merge.c:1156 git-pull.sh:31
 msgid ""
 "You have not concluded your merge (MERGE_HEAD exists).\n"
 "Please, commit your changes before you can merge."
 msgstr ""
-"Sie haben Ihre Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert).\n"
-"Bitte tragen Sie Ihre Änderungen ein, bevor Sie zusammenführen können."
+"Sie haben Ihren Merge nicht abgeschlossen (MERGE_HEAD existiert).\n"
+"Bitte committen Sie Ihre Änderungen, bevor Sie \"merge\" ausführen können."
 
-#: builtin/merge.c:1129 git-pull.sh:34
+#: builtin/merge.c:1159 git-pull.sh:34
 msgid "You have not concluded your merge (MERGE_HEAD exists)."
-msgstr ""
-"Sie haben Ihre Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert)."
+msgstr "Sie haben Ihren Merge nicht abgeschlossen (MERGE_HEAD existiert)."
 
-#: builtin/merge.c:1133
+#: builtin/merge.c:1163
 msgid ""
 "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 "Please, commit your changes before you can merge."
 msgstr ""
 "Sie haben \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert).\n"
-"Bitte tragen Sie Ihre Änderungen ein, bevor Sie zusammenführen können."
+"Bitte committen Sie Ihre Änderungen, bevor Sie \"merge\" ausführen können."
 
-#: builtin/merge.c:1136
+#: builtin/merge.c:1166
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
 msgstr ""
 "Sie haben \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert)."
 
-#: builtin/merge.c:1145
+#: builtin/merge.c:1175
 msgid "You cannot combine --squash with --no-ff."
 msgstr "Sie können --squash nicht mit --no-ff kombinieren."
 
-#: builtin/merge.c:1150
-msgid "You cannot combine --no-ff with --ff-only."
-msgstr "Sie können --no-ff nicht mit --ff--only kombinieren."
-
-#: builtin/merge.c:1157
+#: builtin/merge.c:1184
 msgid "No commit specified and merge.defaultToUpstream not set."
-msgstr "Keine Version angegeben und merge.defaultToUpstream ist nicht gesetzt."
+msgstr "Kein Commit angegeben und merge.defaultToUpstream ist nicht gesetzt."
 
-#: builtin/merge.c:1189
+#: builtin/merge.c:1216
 msgid "Can merge only exactly one commit into empty head"
-msgstr "Kann nur exakt eine Version in einem leeren Zweig zusammenführen."
+msgstr "Kann nur exakt einen Commit in einem leeren Branch zusammenführen."
 
-#: builtin/merge.c:1192
+#: builtin/merge.c:1219
 msgid "Squash commit into empty head not supported yet"
-msgstr "Bin auf einem Zweig, der noch geboren wird; kann nicht quetschen."
+msgstr ""
+"Bin auf einem Commit, der noch geboren wird; kann \"squash\" nicht ausführen."
 
-#: builtin/merge.c:1194
+#: builtin/merge.c:1221
 msgid "Non-fast-forward commit does not make sense into an empty head"
 msgstr ""
-"Nicht vorzuspulende Version kann nicht in einem leeren Zweig verwendet "
+"Nicht vorzuspulender Commit kann nicht in einem leeren Branch verwendet "
 "werden."
 
-#: builtin/merge.c:1310
+#: builtin/merge.c:1226
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - nichts was wir zusammenführen können"
+
+#: builtin/merge.c:1277
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr ""
+"Commit %s hat eine nicht vertrauenswürdige GPG-Signatur, angeblich von %s."
+
+#: builtin/merge.c:1280
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr "Commit %s hat eine ungültige GPG-Signatur, angeblich von %s."
+
+#. 'N'
+#: builtin/merge.c:1283
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr "Commit %s hat keine GPG-Signatur."
+
+#: builtin/merge.c:1286
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr "Commit %s hat eine gültige GPG-Signatur von %s\n"
+
+#: builtin/merge.c:1370
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr "Aktualisiere %s..%s\n"
 
-#: builtin/merge.c:1349
+#: builtin/merge.c:1409
 #, c-format
 msgid "Trying really trivial in-index merge...\n"
-msgstr "Probiere wirklich triviale \"in-index\"-Zusammenführung...\n"
+msgstr "Probiere wirklich trivialen \"in-index\"-Merge...\n"
 
-#: builtin/merge.c:1356
+#: builtin/merge.c:1416
 #, c-format
 msgid "Nope.\n"
 msgstr "Nein.\n"
 
-#: builtin/merge.c:1388
+#: builtin/merge.c:1448
 msgid "Not possible to fast-forward, aborting."
 msgstr "Vorspulen nicht möglich, breche ab."
 
-#: builtin/merge.c:1411 builtin/merge.c:1490
+#: builtin/merge.c:1471 builtin/merge.c:1550
 #, c-format
 msgid "Rewinding the tree to pristine...\n"
-msgstr "Rücklauf des Zweiges bis zum Ursprung...\n"
+msgstr "Rücklauf des Verzeichnisses bis zum Ursprung...\n"
 
-#: builtin/merge.c:1415
+#: builtin/merge.c:1475
 #, c-format
 msgid "Trying merge strategy %s...\n"
-msgstr "Probiere Zusammenführungsstrategie %s...\n"
+msgstr "Probiere Merge-Strategie %s...\n"
 
-#: builtin/merge.c:1481
+#: builtin/merge.c:1541
 #, c-format
 msgid "No merge strategy handled the merge.\n"
-msgstr "Keine Zusammenführungsstrategie behandelt diese Zusammenführung.\n"
+msgstr "Keine Merge-Strategie behandelt diesen Merge.\n"
 
-#: builtin/merge.c:1483
+#: builtin/merge.c:1543
 #, c-format
 msgid "Merge with strategy %s failed.\n"
-msgstr "Zusammenführung mit Strategie %s fehlgeschlagen.\n"
+msgstr "Merge mit Strategie %s fehlgeschlagen.\n"
 
-#: builtin/merge.c:1492
+#: builtin/merge.c:1552
 #, c-format
 msgid "Using the %s to prepare resolving by hand.\n"
 msgstr "Benutzen Sie \"%s\" um die Auflösung per Hand vorzubereiten.\n"
 
-#: builtin/merge.c:1504
+#: builtin/merge.c:1564
 #, c-format
 msgid "Automatic merge went well; stopped before committing as requested\n"
 msgstr ""
-"Automatische Zusammenführung abgeschlossen; halte, wie gewünscht, vor der "
-"Eintragung an\n"
+"Automatischer Merge abgeschlossen; halte, wie gewünscht, vor dem Commit an\n"
 
 #: builtin/merge-base.c:26
 msgid "git merge-base [-a|--all] <commit> <commit>..."
-msgstr "git merge-base [-a|--all] <Version> <Version>..."
+msgstr "git merge-base [-a|--all] <Commit> <Commit>..."
 
 #: builtin/merge-base.c:27
 msgid "git merge-base [-a|--all] --octopus <commit>..."
-msgstr "git merge-base [-a|--all] --octopus <Version>..."
+msgstr "git merge-base [-a|--all] --octopus <Commit>..."
 
 #: builtin/merge-base.c:28
 msgid "git merge-base --independent <commit>..."
-msgstr "git merge-base --independent <Version>..."
+msgstr "git merge-base --independent <Commit>..."
 
 #: builtin/merge-base.c:29
 msgid "git merge-base --is-ancestor <commit> <commit>"
-msgstr "git merge-base --is-ancestor <Version> <Version>"
+msgstr "git merge-base --is-ancestor <Commit> <Commit>"
 
 #: builtin/merge-base.c:98
 msgid "output all common ancestors"
-msgstr "Ausgabe aller gemeinsamen Vorfahren"
+msgstr "Ausgabe aller gemeinsamen Vorgänger-Commits"
 
 #: builtin/merge-base.c:99
 msgid "find ancestors for a single n-way merge"
-msgstr "findet Vorfahren für eine einzelne n-Wege-Zusammenführung"
+msgstr "findet Vorgänger-Commits für einen einzelnen n-Wege-Merge"
 
 #: builtin/merge-base.c:100
 msgid "list revs not reachable from others"
-msgstr "listet Versionen auf, die nicht durch Andere erreichbar sind"
+msgstr "listet Commits auf, die nicht durch Andere erreichbar sind"
 
 #: builtin/merge-base.c:102
 msgid "is the first one ancestor of the other?"
-msgstr "ist der Erste ein Vorfahre von dem Anderen?"
+msgstr "ist der Erste ein Vorgänger-Commit von dem Anderen?"
 
 #: builtin/merge-file.c:8
 msgid ""
@@ -6373,7 +6952,7 @@ msgstr "sendet Ergebnisse zur Standard-Ausgabe"
 
 #: builtin/merge-file.c:34
 msgid "use a diff3 based merge"
-msgstr "verwendet eine diff3 basierte Zusammenführung"
+msgstr "verwendet einen diff3 basierten Merge"
 
 #: builtin/merge-file.c:35
 msgid "for conflicts, use our version"
@@ -6413,7 +6992,7 @@ msgstr "erlaubt fehlende Objekte"
 
 #: builtin/mktree.c:155
 msgid "allow creation of more than one tree"
-msgstr "erlaubt die Erstellung von mehr als einem Baum"
+msgstr "erlaubt die Erstellung von mehr als einem \"Tree\"-Objekt"
 
 #: builtin/mv.c:14
 msgid "git mv [options] <source>... <destination>"
@@ -6447,7 +7026,7 @@ msgstr "kann Verzeichnis nicht über Datei verschieben"
 #: builtin/mv.c:128
 #, c-format
 msgid "Huh? %.*s is in index?"
-msgstr "Huh? %.*s ist in der Bereitstellung?"
+msgstr "Huh? %.*s ist zum Commit vorgemerkt?"
 
 #: builtin/mv.c:140
 msgid "source directory is empty"
@@ -6489,47 +7068,51 @@ msgstr "Benenne %s nach %s um\n"
 msgid "renaming '%s' failed"
 msgstr "Umbenennung von '%s' fehlgeschlagen"
 
-#: builtin/name-rev.c:175
+#: builtin/name-rev.c:259
 msgid "git name-rev [options] <commit>..."
-msgstr "git name-rev [Optionen] <Version>..."
+msgstr "git name-rev [Optionen] <Commit>..."
 
-#: builtin/name-rev.c:176
+#: builtin/name-rev.c:260
 msgid "git name-rev [options] --all"
 msgstr "git name-rev [Optionen] --all"
 
-#: builtin/name-rev.c:177
+#: builtin/name-rev.c:261
 msgid "git name-rev [options] --stdin"
 msgstr "git name-rev [Optionen] --stdin"
 
-#: builtin/name-rev.c:229
+#: builtin/name-rev.c:313
 msgid "print only names (no SHA-1)"
 msgstr "zeigt nur Namen an (keine SHA-1)"
 
-#: builtin/name-rev.c:230
+#: builtin/name-rev.c:314
 msgid "only use tags to name the commits"
-msgstr "verwendet nur Markierungen um die Versionen zu benennen"
+msgstr "verwendet nur Tags um die Commits zu benennen"
 
-#: builtin/name-rev.c:232
+#: builtin/name-rev.c:316
 msgid "only use refs matching <pattern>"
 msgstr "verwendet nur Referenzen die <Muster> entsprechen"
 
-#: builtin/name-rev.c:234
+#: builtin/name-rev.c:318
 msgid "list all commits reachable from all refs"
-msgstr "listet alle Versionen auf, die von allen Referenzen erreichbar sind"
+msgstr "listet alle Commits auf, die von allen Referenzen erreichbar sind"
 
-#: builtin/name-rev.c:235
+#: builtin/name-rev.c:319
 msgid "read from stdin"
 msgstr "liest von der Standard-Eingabe"
 
-#: builtin/name-rev.c:236
+#: builtin/name-rev.c:320
 msgid "allow to print `undefined` names"
 msgstr "erlaubt Ausgabe von `undefinierten` Namen"
 
-#: builtin/notes.c:26
+#: builtin/name-rev.c:326
+msgid "dereference tags in the input (internal use)"
+msgstr "dereferenziert Tags in der Eingabe (interne Verwendung)"
+
+#: builtin/notes.c:24
 msgid "git notes [--ref <notes_ref>] [list [<object>]]"
 msgstr "git notes [--ref <Notiz-Referenz>] [list [<Objekt>]]"
 
-#: builtin/notes.c:27
+#: builtin/notes.c:25
 msgid ""
 "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
@@ -6537,12 +7120,12 @@ msgstr ""
 "git notes [--ref <Notiz-Referenz>] add [-f] [-m <Nachricht> | -F <Datei> | (-"
 "c | -C) <Objekt>] [<Objekt>]"
 
-#: builtin/notes.c:28
+#: builtin/notes.c:26
 msgid "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <Notiz-Referenz>] copy [-f] <von-Objekt> <nach-Objekt>"
 
-#: builtin/notes.c:29
+#: builtin/notes.c:27
 msgid ""
 "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
@@ -6550,223 +7133,201 @@ msgstr ""
 "git notes [--ref <Notiz-Referenz>] append [-m <Nachricht> | -F <Datei> | (-c "
 "| -C) <Objekt>] [<Objekt>]"
 
-#: builtin/notes.c:30
+#: builtin/notes.c:28
 msgid "git notes [--ref <notes_ref>] edit [<object>]"
 msgstr "git notes [--ref <Notiz-Referenz>] edit [<Objekt>]"
 
-#: builtin/notes.c:31
+#: builtin/notes.c:29
 msgid "git notes [--ref <notes_ref>] show [<object>]"
 msgstr "git notes [--ref <Notiz-Referenz>] show [<Objekt>]"
 
-#: builtin/notes.c:32
+#: builtin/notes.c:30
 msgid ""
 "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"
 msgstr ""
 "git notes [--ref <Notiz-Referenz>] merge [-v | -q] [-s <Strategie> ] <Notiz-"
 "Referenz>"
 
-#: builtin/notes.c:33
+#: builtin/notes.c:31
 msgid "git notes merge --commit [-v | -q]"
 msgstr "git notes merge --commit [-v | -q]"
 
-#: builtin/notes.c:34
+#: builtin/notes.c:32
 msgid "git notes merge --abort [-v | -q]"
 msgstr "git notes merge --abort [-v | -q]"
 
-#: builtin/notes.c:35
+#: builtin/notes.c:33
 msgid "git notes [--ref <notes_ref>] remove [<object>...]"
 msgstr "git notes [--ref <Notiz-Referenz>] remove [<Objekt>...]"
 
-#: builtin/notes.c:36
+#: builtin/notes.c:34
 msgid "git notes [--ref <notes_ref>] prune [-n | -v]"
 msgstr "git notes [--ref <Notiz-Referenz>] prune [-n | -v]"
 
-#: builtin/notes.c:37
+#: builtin/notes.c:35
 msgid "git notes [--ref <notes_ref>] get-ref"
 msgstr "git notes [--ref <Notiz-Referenz>] get-ref"
 
-#: builtin/notes.c:42
+#: builtin/notes.c:40
 msgid "git notes [list [<object>]]"
 msgstr "git notes [list [<Objekt>]]"
 
-#: builtin/notes.c:47
+#: builtin/notes.c:45
 msgid "git notes add [<options>] [<object>]"
 msgstr "git notes add [<Optionen>] [<Objekt>]"
 
-#: builtin/notes.c:52
+#: builtin/notes.c:50
 msgid "git notes copy [<options>] <from-object> <to-object>"
 msgstr "git notes copy [<Optionen>] <von-Objekt> <nach-Objekt>"
 
-#: builtin/notes.c:53
+#: builtin/notes.c:51
 msgid "git notes copy --stdin [<from-object> <to-object>]..."
 msgstr "git notes copy --stdin [<von-Objekt> <nach-Objekt>]..."
 
-#: builtin/notes.c:58
+#: builtin/notes.c:56
 msgid "git notes append [<options>] [<object>]"
 msgstr "git notes append [<Optionen>] [<Objekt>]"
 
-#: builtin/notes.c:63
+#: builtin/notes.c:61
 msgid "git notes edit [<object>]"
 msgstr "git notes edit [<Objekt>]"
 
-#: builtin/notes.c:68
+#: builtin/notes.c:66
 msgid "git notes show [<object>]"
 msgstr "git notes show [<Objekt>]"
 
-#: builtin/notes.c:73
+#: builtin/notes.c:71
 msgid "git notes merge [<options>] <notes_ref>"
 msgstr "git notes merge [<Optionen>] <Notiz-Referenz>"
 
-#: builtin/notes.c:74
+#: builtin/notes.c:72
 msgid "git notes merge --commit [<options>]"
 msgstr "git notes merge --commit [<Optionen>]"
 
-#: builtin/notes.c:75
+#: builtin/notes.c:73
 msgid "git notes merge --abort [<options>]"
 msgstr "git notes merge --abort [<Optionen>]"
 
-#: builtin/notes.c:80
+#: builtin/notes.c:78
 msgid "git notes remove [<object>]"
 msgstr "git notes remove [<Objekt>]"
 
-#: builtin/notes.c:85
+#: builtin/notes.c:83
 msgid "git notes prune [<options>]"
 msgstr "git notes prune [<Optionen>]"
 
-#: builtin/notes.c:90
+#: builtin/notes.c:88
 msgid "git notes get-ref"
 msgstr "git notes get-ref"
 
-#: builtin/notes.c:139
+#: builtin/notes.c:137
 #, c-format
 msgid "unable to start 'show' for object '%s'"
 msgstr "konnte 'show' für Objekt '%s' nicht starten"
 
-#: builtin/notes.c:143
+#: builtin/notes.c:141
 msgid "could not read 'show' output"
 msgstr "Konnte Ausgabe von 'show' nicht lesen."
 
-#: builtin/notes.c:151
+#: builtin/notes.c:149
 #, c-format
 msgid "failed to finish 'show' for object '%s'"
 msgstr "konnte 'show' für Objekt '%s' nicht abschließen"
 
-#: builtin/notes.c:169 builtin/tag.c:341
+#: builtin/notes.c:167 builtin/tag.c:341
 #, c-format
 msgid "could not create file '%s'"
 msgstr "konnte Datei '%s' nicht erstellen"
 
-#: builtin/notes.c:188
+#: builtin/notes.c:186
 msgid "Please supply the note contents using either -m or -F option"
 msgstr ""
 "Bitte liefern Sie den Notiz-Inhalt unter Verwendung der Option -m oder -F."
 
-#: builtin/notes.c:209 builtin/notes.c:972
+#: builtin/notes.c:207 builtin/notes.c:844
 #, c-format
 msgid "Removing note for object %s\n"
 msgstr "Entferne Notiz für Objekt %s\n"
 
-#: builtin/notes.c:214
+#: builtin/notes.c:212
 msgid "unable to write note object"
 msgstr "Konnte Notiz-Objekt nicht schreiben"
 
-#: builtin/notes.c:216
+#: builtin/notes.c:214
 #, c-format
 msgid "The note contents has been left in %s"
 msgstr "Die Notiz-Inhalte wurden in %s belassen"
 
-#: builtin/notes.c:250 builtin/tag.c:540
+#: builtin/notes.c:248 builtin/tag.c:540
 #, c-format
 msgid "cannot read '%s'"
 msgstr "kann '%s' nicht lesen"
 
-#: builtin/notes.c:252 builtin/tag.c:543
+#: builtin/notes.c:250 builtin/tag.c:543
 #, c-format
 msgid "could not open or read '%s'"
 msgstr "konnte '%s' nicht öffnen oder lesen"
 
-#: builtin/notes.c:271 builtin/notes.c:444 builtin/notes.c:446
-#: builtin/notes.c:506 builtin/notes.c:560 builtin/notes.c:643
-#: builtin/notes.c:648 builtin/notes.c:723 builtin/notes.c:765
-#: builtin/notes.c:967 builtin/tag.c:556
+#: builtin/notes.c:269 builtin/notes.c:316 builtin/notes.c:318
+#: builtin/notes.c:378 builtin/notes.c:432 builtin/notes.c:515
+#: builtin/notes.c:520 builtin/notes.c:595 builtin/notes.c:637
+#: builtin/notes.c:839 builtin/tag.c:556
 #, c-format
 msgid "Failed to resolve '%s' as a valid ref."
 msgstr "Konnte '%s' nicht als gültige Referenz auflösen."
 
-#: builtin/notes.c:274
+#: builtin/notes.c:272
 #, c-format
 msgid "Failed to read object '%s'."
 msgstr "Fehler beim Lesen des Objektes '%s'."
 
-#: builtin/notes.c:298
-msgid "Cannot commit uninitialized/unreferenced notes tree"
-msgstr "Kann uninitialisierten/unreferenzierten Notiz-Baum nicht eintragen."
-
-#: builtin/notes.c:339
-#, c-format
-msgid "Bad notes.rewriteMode value: '%s'"
-msgstr "Ungültiger notes.rewriteMode Wert: '%s'"
-
-#: builtin/notes.c:349
-#, c-format
-msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
-msgstr ""
-"Umschreiben der Notizen in %s zurückgewiesen (außerhalb von refs/notes/)"
-
-#. TRANSLATORS: The first %s is the name of the
-#. environment variable, the second %s is its value
-#: builtin/notes.c:376
-#, c-format
-msgid "Bad %s value: '%s'"
-msgstr "Ungültiger %s Wert: '%s'"
-
-#: builtin/notes.c:440
+#: builtin/notes.c:312
 #, c-format
 msgid "Malformed input line: '%s'."
 msgstr "Fehlerhafte Eingabezeile: '%s'."
 
-#: builtin/notes.c:455
+#: builtin/notes.c:327
 #, c-format
 msgid "Failed to copy notes from '%s' to '%s'"
 msgstr "Fehler beim Kopieren der Notizen von '%s' nach '%s'"
 
-#: builtin/notes.c:499 builtin/notes.c:553 builtin/notes.c:626
-#: builtin/notes.c:638 builtin/notes.c:711 builtin/notes.c:758
-#: builtin/notes.c:1032
+#: builtin/notes.c:371 builtin/notes.c:425 builtin/notes.c:498
+#: builtin/notes.c:510 builtin/notes.c:583 builtin/notes.c:630
+#: builtin/notes.c:904
 msgid "too many parameters"
 msgstr "zu viele Parameter"
 
-#: builtin/notes.c:512 builtin/notes.c:771
+#: builtin/notes.c:384 builtin/notes.c:643
 #, c-format
 msgid "No note found for object %s."
 msgstr "Kein Notiz für Objekt %s gefunden."
 
-#: builtin/notes.c:534 builtin/notes.c:691
+#: builtin/notes.c:406 builtin/notes.c:563
 msgid "note contents as a string"
 msgstr "Notizinhalte als Zeichenkette"
 
-#: builtin/notes.c:537 builtin/notes.c:694
+#: builtin/notes.c:409 builtin/notes.c:566
 msgid "note contents in a file"
 msgstr "Notizinhalte in einer Datei"
 
-#: builtin/notes.c:539 builtin/notes.c:542 builtin/notes.c:696
-#: builtin/notes.c:699 builtin/tag.c:474
+#: builtin/notes.c:411 builtin/notes.c:414 builtin/notes.c:568
+#: builtin/notes.c:571 builtin/tag.c:474
 msgid "object"
 msgstr "Objekt"
 
-#: builtin/notes.c:540 builtin/notes.c:697
+#: builtin/notes.c:412 builtin/notes.c:569
 msgid "reuse and edit specified note object"
 msgstr "Wiederverwendung und Bearbeitung des angegebenen Notiz-Objektes"
 
-#: builtin/notes.c:543 builtin/notes.c:700
+#: builtin/notes.c:415 builtin/notes.c:572
 msgid "reuse specified note object"
 msgstr "Wiederverwendung des angegebenen Notiz-Objektes"
 
-#: builtin/notes.c:545 builtin/notes.c:613
+#: builtin/notes.c:417 builtin/notes.c:485
 msgid "replace existing notes"
 msgstr "ersetzt existierende Notizen"
 
-#: builtin/notes.c:579
+#: builtin/notes.c:451
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -6775,26 +7336,26 @@ msgstr ""
 "Konnte Notizen nicht hinzufügen. Existierende Notizen für Objekt %s "
 "gefunden. Verwenden Sie '-f' um die existierenden Notizen zu überschreiben."
 
-#: builtin/notes.c:584 builtin/notes.c:661
+#: builtin/notes.c:456 builtin/notes.c:533
 #, c-format
 msgid "Overwriting existing notes for object %s\n"
 msgstr "Überschreibe existierende Notizen für Objekt %s\n"
 
-#: builtin/notes.c:614
+#: builtin/notes.c:486
 msgid "read objects from stdin"
 msgstr "liest Objekte von der Standard-Eingabe"
 
-#: builtin/notes.c:616
+#: builtin/notes.c:488
 msgid "load rewriting config for <command> (implies --stdin)"
 msgstr ""
 "lädt Konfiguration für <Kommando> beim Umschreiben von Versionen (impliziert "
 "--stdin)"
 
-#: builtin/notes.c:634
+#: builtin/notes.c:506
 msgid "too few parameters"
 msgstr "zu wenig Parameter"
 
-#: builtin/notes.c:655
+#: builtin/notes.c:527
 #, c-format
 msgid ""
 "Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -6803,12 +7364,12 @@ msgstr ""
 "Kann Notizen nicht kopieren. Existierende Notizen für Objekt %s gefunden. "
 "Verwenden Sie '-f' um die existierenden Notizen zu überschreiben."
 
-#: builtin/notes.c:667
+#: builtin/notes.c:539
 #, c-format
 msgid "Missing notes on source object %s. Cannot copy."
 msgstr "Keine Notizen für Quell-Objekt %s. Kopie nicht möglich."
 
-#: builtin/notes.c:716
+#: builtin/notes.c:588
 #, c-format
 msgid ""
 "The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
@@ -6817,15 +7378,15 @@ msgstr ""
 "Die Optionen -m/-F/-c/-C sind für das Unterkommando 'edit' veraltet.\n"
 "Bitte benutzen Sie stattdessen 'git notes add -f -m/-F/-c/-C'.\n"
 
-#: builtin/notes.c:863
+#: builtin/notes.c:735
 msgid "General options"
 msgstr "Allgemeine Optionen"
 
-#: builtin/notes.c:865
+#: builtin/notes.c:737
 msgid "Merge options"
-msgstr "Optionen für Zusammenführung"
+msgstr "Merge-Optionen"
 
-#: builtin/notes.c:867
+#: builtin/notes.c:739
 msgid ""
 "resolve notes conflicts using the given strategy (manual/ours/theirs/union/"
 "cat_sort_uniq)"
@@ -6833,46 +7394,46 @@ msgstr ""
 "löst Konflikte bei Notizen mit der angegebenen Strategie auf (manual/ours/"
 "theirs/union/cat_sort_uniq)"
 
-#: builtin/notes.c:869
+#: builtin/notes.c:741
 msgid "Committing unmerged notes"
 msgstr "trägt nicht zusammengeführte Notizen ein"
 
-#: builtin/notes.c:871
+#: builtin/notes.c:743
 msgid "finalize notes merge by committing unmerged notes"
 msgstr ""
-"schließt Zusammenführung von Notizen ab, in dem nicht zusammengeführte "
-"Notizen eingetragen werden"
+"schließt Merge von Notizen ab, in dem nicht zusammengeführte Notizen "
+"committet werden"
 
-#: builtin/notes.c:873
+#: builtin/notes.c:745
 msgid "Aborting notes merge resolution"
-msgstr "bricht Konfliktauflösung bei Zusammenführung von Notizen ab"
+msgstr "bricht Konfliktauflösung beim Merge von Notizen ab"
 
-#: builtin/notes.c:875
+#: builtin/notes.c:747
 msgid "abort notes merge"
-msgstr "bricht Zusammenführung von Notizen ab"
+msgstr "bricht Merge von Notizen ab"
 
-#: builtin/notes.c:970
+#: builtin/notes.c:842
 #, c-format
 msgid "Object %s has no note\n"
 msgstr "Objekt %s hat keine Notiz\n"
 
-#: builtin/notes.c:982
+#: builtin/notes.c:854
 msgid "attempt to remove non-existent note is not an error"
 msgstr "der Versuch, eine nicht existierende Notiz zu löschen, ist kein Fehler"
 
-#: builtin/notes.c:985
+#: builtin/notes.c:857
 msgid "read object names from the standard input"
 msgstr "liest Objektnamen von der Standard-Eingabe"
 
-#: builtin/notes.c:1066
+#: builtin/notes.c:938
 msgid "notes_ref"
 msgstr "Notiz-Referenz"
 
-#: builtin/notes.c:1067
+#: builtin/notes.c:939
 msgid "use notes from <notes_ref>"
 msgstr "verwendet Notizen von <Notiz-Referenz>"
 
-#: builtin/notes.c:1102 builtin/remote.c:1598
+#: builtin/notes.c:974 builtin/remote.c:1598
 #, c-format
 msgid "Unknown subcommand: %s"
 msgstr "Unbekanntes Unterkommando: %s"
@@ -6895,12 +7456,12 @@ msgstr "Fehler beim Komprimieren (%d)"
 #: builtin/pack-objects.c:2397
 #, c-format
 msgid "unsupported index version %s"
-msgstr "Nicht unterstützte Bereitstellungsversion %s"
+msgstr "Nicht unterstützte Staging-Area-Version %s"
 
 #: builtin/pack-objects.c:2401
 #, c-format
 msgid "bad index version '%s'"
-msgstr "Ungültige Bereitstellungsversion '%s'"
+msgstr "Ungültige Staging-Area-Version '%s'"
 
 #: builtin/pack-objects.c:2424
 #, c-format
@@ -6986,7 +7547,7 @@ msgstr "erzeugt keine leeren Pakete"
 
 #: builtin/pack-objects.c:2481
 msgid "read revision arguments from standard input"
-msgstr "liest Argumente bezüglich Revisionen von der Standard-Eingabe"
+msgstr "liest Argumente bezüglich Commits von der Standard-Eingabe"
 
 #: builtin/pack-objects.c:2483
 msgid "limit the objects to those that are not yet packed"
@@ -6999,8 +7560,7 @@ msgstr "schließt Objekte ein, die von jeder Referenz erreichbar sind"
 #: builtin/pack-objects.c:2489
 msgid "include objects referred by reflog entries"
 msgstr ""
-"schließt Objekte ein, die von Einträgen des Referenzprotokolls referenziert "
-"werden"
+"schließt Objekte ein, die von Einträgen des Reflogs referenziert werden"
 
 #: builtin/pack-objects.c:2492
 msgid "output pack to stdout"
@@ -7008,8 +7568,7 @@ msgstr "schreibt Paket in die Standard-Ausgabe"
 
 #: builtin/pack-objects.c:2494
 msgid "include tag objects that refer to objects to be packed"
-msgstr ""
-"schließt Markierungsobjekte ein, die auf gepackte Objekte referenzieren"
+msgstr "schließt Tag-Objekte ein, die auf gepackte Objekte referenzieren"
 
 #: builtin/pack-objects.c:2496
 msgid "keep unreachable objects"
@@ -7037,7 +7596,7 @@ msgstr "Komprimierungsgrad für Paketierung"
 
 #: builtin/pack-objects.c:2507
 msgid "do not hide commits by grafts"
-msgstr "verbirgt keine Versionen mit künstlichen Vorgängern (\"grafts\")"
+msgstr "verbirgt keine Commits mit künstlichen Vorgängern (\"grafts\")"
 
 #: builtin/pack-refs.c:6
 msgid "git pack-refs [options]"
@@ -7057,7 +7616,7 @@ msgstr "git prune-packed [-n|--dry-run] [-q|--quiet]"
 
 #: builtin/prune.c:12
 msgid "git prune [-n] [-v] [--expire <time>] [--] [<head>...]"
-msgstr "git prune [-n] [-v] [--expire <Zeit>] [--] [<Zweigspitze>...]"
+msgstr "git prune [-n] [-v] [--expire <Zeit>] [--] [<head>...]"
 
 #: builtin/prune.c:132
 msgid "do not remove, show only"
@@ -7073,11 +7632,11 @@ msgstr "lässt Objekte älter als <Zeit> verfallen"
 
 #: builtin/push.c:14
 msgid "git push [<options>] [<repository> [<refspec>...]]"
-msgstr "git push [<Optionen>] [<Projektarchiv> [<Referenzspezifikation>...]]"
+msgstr "git push [<Optionen>] [<Repository> [<Refspec>...]]"
 
 #: builtin/push.c:45
 msgid "tag shorthand without <tag>"
-msgstr "Kurzschrift für Markierung ohne <Markierung>"
+msgstr "Kurzschrift für Tag ohne <Tag>"
 
 #: builtin/push.c:64
 msgid "--delete only accepts plain target ref names"
@@ -7106,19 +7665,19 @@ msgid ""
 "    git push %s %s\n"
 "%s"
 msgstr ""
-"Der Name des externen Übernahmezweiges stimmt nicht mit dem Namen Ihres\n"
-"aktuellen Zweiges überein. Um auf den Übernahmezweig in dem externen\n"
-"Projektarchiv zu versenden, benutzen Sie:\n"
+"Der Name des Upstream-Branches stimmt nicht mit dem Namen Ihres\n"
+"aktuellen Branches überein. Um auf den Upstream-Branch in dem Remote-\n"
+"Repository zu versenden, benutzen Sie:\n"
 "\n"
 "    git push %s HEAD:%s\n"
 "\n"
-"Um auf den Zweig mit dem selben Namen in dem externen Projekarchiv\n"
-"zu versenden, benutzen Sie:\n"
+"Um auf den Branch mit dem selben Namen im Remote-Repository zu versenden,\n"
+"benutzen Sie:\n"
 "\n"
 "    git push %s %s\n"
 "%s"
 
-#: builtin/push.c:121
+#: builtin/push.c:117
 #, c-format
 msgid ""
 "You are not currently on a branch.\n"
@@ -7127,13 +7686,13 @@ msgid ""
 "\n"
 "    git push %s HEAD:<name-of-remote-branch>\n"
 msgstr ""
-"Sie befinden sich im Moment auf keinem Zweig.\n"
-"Um die Historie, führend zum aktuellen (freistehende Zweigspitze (HEAD))\n"
+"Sie befinden sich im Moment auf keinem Branch.\n"
+"Um die Historie, führend zum aktuellen (losgelöster HEAD)\n"
 "Status zu versenden, benutzen Sie\n"
 "\n"
-"    git push %s HEAD:<Name-des-externen-Zweiges>\n"
+"    git push %s HEAD:<Name-des-Remote-Branches>\n"
 
-#: builtin/push.c:128
+#: builtin/push.c:131
 #, c-format
 msgid ""
 "The current branch %s has no upstream branch.\n"
@@ -7141,29 +7700,30 @@ msgid ""
 "\n"
 "    git push --set-upstream %s %s\n"
 msgstr ""
-"Der aktuelle Zweig %s hat keinen Zweig im externen Projektarchiv.\n"
-"Um den aktuellen Zweig zu versenden und das Fernarchiv als externes\n"
-"Projektarchiv zu verwenden, benutzen Sie\n"
+"Der aktuelle Branch %s hat keinen Upstream-Branch.\n"
+"Um den aktuellen Branch zu versenden und den Remote-Branch\n"
+"als Upstream-Branch zu setzen, benutzen Sie\n"
 "\n"
 "    git push --set-upstream %s %s\n"
 
-#: builtin/push.c:136
+#: builtin/push.c:139
 #, c-format
 msgid "The current branch %s has multiple upstream branches, refusing to push."
-msgstr "Der aktuelle Zweig %s hat mehrere externe Zweige, Versand verweigert."
+msgstr ""
+"Der aktuelle Branch %s hat mehrere Upstream-Branches, \"push\" verweigert."
 
-#: builtin/push.c:139
+#: builtin/push.c:142
 #, c-format
 msgid ""
 "You are pushing to remote '%s', which is not the upstream of\n"
 "your current branch '%s', without telling me what to push\n"
 "to update which remote branch."
 msgstr ""
-"Sie versenden nach '%s', welches kein externes Projektarchiv Ihres\n"
-"aktuellen Zweiges '%s' ist, ohne mir mitzuteilen, was ich versenden\n"
-"soll, um welchen externen Zweig zu aktualisieren."
+"Sie versenden nach '%s', welches kein Upstream-Repository Ihres aktuellen\n"
+"Branches '%s' ist, ohne mir mitzuteilen, was ich versenden soll, um welchen\n"
+"Remote-Branch zu aktualisieren."
 
-#: builtin/push.c:151
+#: builtin/push.c:165
 msgid ""
 "push.default is unset; its implicit value is changing in\n"
 "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -7184,6 +7744,7 @@ msgstr ""
 "wird in Git 2.0 von 'matching' nach 'simple' geändert. Um diese Meldung zu\n"
 "unterdrücken und das aktuelle Verhalten nach Änderung des Standardwertes\n"
 "beizubehalten, benutzen Sie:\n"
+"\n"
 "  git config --global push.default matching\n"
 "\n"
 "Um diese Meldung zu unterdrücken und das neue Verhalten jetzt zu übernehmen, "
@@ -7197,28 +7758,28 @@ msgstr ""
 "Modus 'current' anstatt 'simple', falls Sie gelegentlich ältere Versionen "
 "von Git benutzen.)"
 
-#: builtin/push.c:199
+#: builtin/push.c:225
 msgid ""
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
-"Sie haben keine Referenzspezifikationen zum Versenden angegeben, und push."
-"default ist \"nothing\"."
+"Sie haben keine Refspec für \"push\" angegeben, und push.default ist "
+"\"nothing\"."
 
-#: builtin/push.c:206
+#: builtin/push.c:232
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
-"before pushing again.\n"
+"its remote counterpart. Integrate the remote changes (e.g.\n"
+"'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil die Spitze Ihres aktuellen\n"
-"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Führen Sie\n"
-"die externen Änderungen zusammen (z.B. 'git pull') bevor Sie erneut\n"
-"versenden.\n"
+"Branches hinter seinem externen Gegenstück zurückgefallen ist. Führen Sie\n"
+"die externen Änderungen zusammen (z.B. 'git pull ...') bevor Sie \"push\"\n"
+"erneut ausführen.\n"
 "Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
 "für weitere Details."
 
-#: builtin/push.c:212
+#: builtin/push.c:238
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
 "counterpart. If you did not intend to push that branch, you may want to\n"
@@ -7226,80 +7787,79 @@ msgid ""
 "to 'simple', 'current' or 'upstream' to push only the current branch."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n"
-"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Wenn Sie "
+"Branches hinter seinem externen Gegenstück zurückgefallen ist. Wenn Sie "
 "nicht\n"
-"beabsichtigt haben, diesen Zweig zu versenden, können Sie auch den zu "
+"beabsichtigt haben, diesen Branch zu versenden, können Sie auch den zu "
 "versendenden\n"
-"Zweig spezifizieren oder die Konfigurationsvariable 'push.default' zu "
+"Branch spezifizieren oder die Konfigurationsvariable 'push.default' zu "
 "'simple', 'current'\n"
-"oder 'upstream' setzen, um nur den aktuellen Zweig zu versenden."
+"oder 'upstream' setzen, um nur den aktuellen Branch zu versenden."
 
-#: builtin/push.c:218
+#: builtin/push.c:244
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and merge the remote changes\n"
-"(e.g. 'git pull') before pushing again.\n"
+"counterpart. Check out this branch and integrate the remote changes\n"
+"(e.g. 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n"
-"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Checken Sie\n"
-"diesen Zweig aus und führen Sie die externen Änderungen zusammen\n"
-"(z.B. 'git pull') bevor Sie erneut versenden.\n"
+"Branches hinter seinem externen Gegenstück zurückgefallen ist. Checken Sie\n"
+"diesen Branch aus und führen Sie die externen Änderungen zusammen\n"
+"(z.B. 'git pull ...') bevor Sie erneut \"push\" ausführen.\n"
 "Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
 "für weitere Details."
 
-#: builtin/push.c:224
+#: builtin/push.c:250
 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"
-"to the same ref. You may want to first merge the remote changes (e.g.,\n"
-"'git pull') before pushing again.\n"
+"to the same ref. You may want to first integrate the remote changes\n"
+"(e.g., 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
-"Aktualisierungen wurden zurückgewiesen, weil das Fernarchiv Versionen "
+"Aktualisierungen wurden zurückgewiesen, weil das Remote-Repository Commits "
 "enthält,\n"
-"die lokal nicht vorhanden sind. Das wird üblicherweise durch das Versenden "
+"die lokal nicht vorhanden sind. Das wird üblicherweise durch einen \"push\" "
 "von\n"
-"Versionen auf dieselbe Referenz von einem anderen Projektarchiv aus "
-"verursacht.\n"
+"Commits auf dieselbe Referenz von einem anderen Repository aus verursacht.\n"
 "Vielleicht müssen Sie die externen Änderungen zusammenzuführen (z.B. 'git "
-"pull')\n"
-"bevor Sie erneut versenden.\n"
+"pull ...')\n"
+"bevor Sie erneut \"push\" ausführen.\n"
 "Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
 "für weitere Details."
 
-#: builtin/push.c:231
+#: builtin/push.c:257
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr ""
-"Aktualisierungen wurden zurückgewiesen, weil die Markierung bereits\n"
-"im Fernarchiv existiert."
+"Aktualisierungen wurden zurückgewiesen, weil das Tag bereits\n"
+"im Remote-Repository existiert."
 
-#: builtin/push.c:234
+#: builtin/push.c:260
 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 ""
-"Sie können keine externe Referenz aktualisieren, die auf ein Objekt zeigt,\n"
-"das keine Version ist, oder es auf ein solches Objekt zeigen lassen, ohne\n"
+"Sie können keine Remote-Referenz aktualisieren, die auf ein Objekt zeigt,\n"
+"das kein Commit ist, oder es auf ein solches Objekt zeigen lassen, ohne\n"
 "die Option '--force' zu verwenden.\n"
 
-#: builtin/push.c:294
+#: builtin/push.c:320
 #, c-format
 msgid "Pushing to %s\n"
 msgstr "Versende nach %s\n"
 
-#: builtin/push.c:298
+#: builtin/push.c:324
 #, c-format
 msgid "failed to push some refs to '%s'"
 msgstr "Fehler beim Versenden einiger Referenzen nach '%s'"
 
-#: builtin/push.c:331
+#: builtin/push.c:357
 #, c-format
 msgid "bad repository '%s'"
-msgstr "ungültiges Projektarchiv '%s'"
+msgstr "ungültiges Repository '%s'"
 
-#: builtin/push.c:332
+#: builtin/push.c:358
 msgid ""
 "No configured push destination.\n"
 "Either specify the URL from the command-line or configure a remote "
@@ -7311,96 +7871,97 @@ msgid ""
 "\n"
 "    git push <name>\n"
 msgstr ""
-"Kein Ziel zum Versenden konfiguriert.\n"
+"Kein Ziel für \"push\" konfiguriert.\n"
 "Entweder spezifizieren Sie die URL von der Kommandozeile oder konfigurieren "
-"ein externes Projektarchiv unter Benutzung von\n"
+"ein Remote-Repository unter Benutzung von\n"
 "\n"
 "    git remote add <Name> <URL>\n"
 "\n"
-"und versenden dann unter Benutzung dieses Namens\n"
+"und führen \"push\" dann unter Benutzung dieses Namens aus\n"
 "\n"
 "    git push <Name>\n"
 
-#: builtin/push.c:347
+#: builtin/push.c:373
 msgid "--all and --tags are incompatible"
 msgstr "Die Optionen --all und --tags sind inkompatibel."
 
-#: builtin/push.c:348
+#: builtin/push.c:374
 msgid "--all can't be combined with refspecs"
-msgstr ""
-"Die Option --all kann nicht mit Referenzspezifikationen kombiniert werden."
+msgstr "Die Option --all kann nicht mit Refspecs kombiniert werden."
 
-#: builtin/push.c:353
+#: builtin/push.c:379
 msgid "--mirror and --tags are incompatible"
 msgstr "Die Optionen --mirror und --tags sind inkompatibel."
 
-#: builtin/push.c:354
+#: builtin/push.c:380
 msgid "--mirror can't be combined with refspecs"
-msgstr ""
-"Die Option --mirror kann nicht mit Referenzspezifikationen kombiniert werden."
+msgstr "Die Option --mirror kann nicht mit Refspecs kombiniert werden."
 
-#: builtin/push.c:359
+#: builtin/push.c:385
 msgid "--all and --mirror are incompatible"
 msgstr "Die Optionen --all und --mirror sind inkompatibel."
 
-#: builtin/push.c:419
+#: builtin/push.c:445
 msgid "repository"
-msgstr "Projektarchiv"
+msgstr "Repository"
 
-#: builtin/push.c:420
+#: builtin/push.c:446
 msgid "push all refs"
 msgstr "versendet alle Referenzen"
 
-#: builtin/push.c:421
+#: builtin/push.c:447
 msgid "mirror all refs"
 msgstr "spiegelt alle Referenzen"
 
-#: builtin/push.c:423
+#: builtin/push.c:449
 msgid "delete refs"
 msgstr "löscht Referenzen"
 
-#: builtin/push.c:424
+#: builtin/push.c:450
 msgid "push tags (can't be used with --all or --mirror)"
-msgstr ""
-"versendet Markierungen (kann nicht mit --all oder --mirror verwendet werden)"
+msgstr "versendet Tags (kann nicht mit --all oder --mirror verwendet werden)"
 
-#: builtin/push.c:427
+#: builtin/push.c:453
 msgid "force updates"
 msgstr "erzwingt Aktualisierung"
 
-#: builtin/push.c:428
+#: builtin/push.c:454
 msgid "check"
 msgstr ""
 
-#: builtin/push.c:429
+#: builtin/push.c:455
 msgid "control recursive pushing of submodules"
-msgstr "steuert rekursives Versenden von Unterprojekten"
+msgstr "steuert rekursiven \"push\" von Submodulen"
 
-#: builtin/push.c:431
+#: builtin/push.c:457
 msgid "use thin pack"
 msgstr "verwendet kleinere Pakete"
 
-#: builtin/push.c:432 builtin/push.c:433
+#: builtin/push.c:458 builtin/push.c:459
 msgid "receive pack program"
 msgstr "'receive pack' Programm"
 
-#: builtin/push.c:434
+#: builtin/push.c:460
 msgid "set upstream for git pull/status"
-msgstr "setzt externes Projektarchiv für \"git pull/status\""
+msgstr "setzt Upstream für \"git pull/status\""
 
-#: builtin/push.c:437
+#: builtin/push.c:463
 msgid "prune locally removed refs"
 msgstr "entfernt lokal gelöschte Referenzen"
 
-#: builtin/push.c:439
+#: builtin/push.c:465
 msgid "bypass pre-push hook"
 msgstr "umgeht \"pre-push hook\""
 
-#: builtin/push.c:448
+#: builtin/push.c:466
+msgid "push missing but relevant tags"
+msgstr "versendet fehlende, aber relevante Tags"
+
+#: builtin/push.c:476
 msgid "--delete is incompatible with --all, --mirror and --tags"
 msgstr "Die Option --delete ist inkompatibel mit --all, --mirror und --tags."
 
-#: builtin/push.c:450
+#: builtin/push.c:478
 msgid "--delete doesn't make sense without any refs"
 msgstr "Die Option --delete kann nur mit Referenzen verwendet werden."
 
@@ -7412,76 +7973,83 @@ msgid ""
 msgstr ""
 "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<Prefix>] "
 "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
-"index-output=<Datei>] (--empty | <Versionsreferenz1> [<Versionsreferenz2> "
-"[<Versionsreferenz3>]])"
+"index-output=<Datei>] (--empty | <Commit-Referenz1> [<Commit-Referenz2> "
+"[<Commit-Referenz3>]])"
 
-#: builtin/read-tree.c:108
+#: builtin/read-tree.c:109
 msgid "write resulting index to <file>"
-msgstr "schreibt resultierende Bereitstellung nach <Datei>"
+msgstr "schreibt resultierende Staging-Area nach <Datei>"
 
-#: builtin/read-tree.c:111
+#: builtin/read-tree.c:112
 msgid "only empty the index"
-msgstr "leert die Bereitstellung"
+msgstr "leert die Staging-Area"
 
-#: builtin/read-tree.c:113
+#: builtin/read-tree.c:114
 msgid "Merging"
-msgstr "Zusammenführung"
+msgstr "Merge"
 
-#: builtin/read-tree.c:115
+#: builtin/read-tree.c:116
 msgid "perform a merge in addition to a read"
-msgstr "führt eine Zusammenführung zusätzlich zum Lesen aus"
+msgstr "führt einen Merge zusätzlich zum Lesen aus"
 
-#: builtin/read-tree.c:117
+#: builtin/read-tree.c:118
 msgid "3-way merge if no file level merging required"
-msgstr ""
-"3-Wege-Zusammenführung, wenn keine Zusammenführung auf Dateiebene "
-"erforderlich ist"
+msgstr "3-Wege-Merge, wenn kein Merge auf Dateiebene erforderlich ist"
 
-#: builtin/read-tree.c:119
+#: builtin/read-tree.c:120
 msgid "3-way merge in presence of adds and removes"
-msgstr ""
-"3-Wege-Zusammenführung bei Vorhandensein von hinzugefügten/entfernten Zeilen"
+msgstr "3-Wege-Merge bei Vorhandensein von hinzugefügten/entfernten Zeilen"
 
-#: builtin/read-tree.c:121
+#: builtin/read-tree.c:122
 msgid "same as -m, but discard unmerged entries"
 msgstr "genau wie -m, verwirft aber nicht zusammengeführte Einträge"
 
-#: builtin/read-tree.c:122
+#: builtin/read-tree.c:123
 msgid "<subdirectory>/"
 msgstr "<Unterverzeichnis>/"
 
-#: builtin/read-tree.c:123
+#: builtin/read-tree.c:124
 msgid "read the tree into the index under <subdirectory>/"
-msgstr "liest den Baum in die Bereitstellung unter <Unterverzeichnis>/"
+msgstr "liest das Verzeichnis in die Staging-Area unter <Unterverzeichnis>/"
 
-#: builtin/read-tree.c:126
+#: builtin/read-tree.c:127
 msgid "update working tree with merge result"
-msgstr "aktualisiert Arbeitsbaum mit Ergebnis der Zusammenführung"
+msgstr "aktualisiert Arbeitsverzeichnis mit dem Ergebnis des Merges"
 
-#: builtin/read-tree.c:128
+#: builtin/read-tree.c:129
 msgid "gitignore"
 msgstr "gitignore"
 
-#: builtin/read-tree.c:129
+#: builtin/read-tree.c:130
 msgid "allow explicitly ignored files to be overwritten"
 msgstr "erlaubt explizit ignorierte Dateien zu überschreiben"
 
-#: builtin/read-tree.c:132
+#: builtin/read-tree.c:133
 msgid "don't check the working tree after merging"
-msgstr "prüft nicht den Arbeitsbaum nach der Zusammenführung"
+msgstr "prüft nicht das Arbeitsverzeichnis nach dem Merge"
 
-#: builtin/read-tree.c:133
+#: builtin/read-tree.c:134
 msgid "don't update the index or the work tree"
-msgstr "aktualisiert weder die Bereitstellung, noch den Arbeitsbaum"
+msgstr "aktualisiert weder die Staging-Area, noch das Arbeitsverzeichnis"
 
-#: builtin/read-tree.c:135
+#: builtin/read-tree.c:136
 msgid "skip applying sparse checkout filter"
 msgstr "überspringt Anwendung des Filters für spärliches Auschecken"
 
-#: builtin/read-tree.c:137
+#: builtin/read-tree.c:138
 msgid "debug unpack-trees"
 msgstr "protokolliert Entpacken der Bäume"
 
+#: builtin/reflog.c:500
+#, c-format
+msgid "%s' for '%s' is not a valid timestamp"
+msgstr "'%s' für '%s' ist kein gültiger Zeitstempel"
+
+#: builtin/reflog.c:616 builtin/reflog.c:621
+#, c-format
+msgid "'%s' is not a valid timestamp"
+msgstr "'%s' ist kein gültiger Zeitstempel"
+
 #: builtin/remote.c:11
 msgid "git remote [-v | --verbose]"
 msgstr "git remote [-v | --verbose]"
@@ -7491,7 +8059,7 @@ msgid ""
 "git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--"
 "mirror=<fetch|push>] <name> <url>"
 msgstr ""
-"git remote add [-t <Zweig>] [-m <master>] [-f] [--tags|--no-tags] [--"
+"git remote add [-t <Branch>] [-m <master>] [-f] [--tags|--no-tags] [--"
 "mirror=<fetch|push>] <Name> <URL>"
 
 #: builtin/remote.c:13 builtin/remote.c:32
@@ -7504,7 +8072,7 @@ msgstr "git remote remove <Name>"
 
 #: builtin/remote.c:15 builtin/remote.c:42
 msgid "git remote set-head <name> (-a | -d | <branch>)"
-msgstr "git remote set-head <Name> (-a | -d | <Zweig>)"
+msgstr "git remote set-head <Name> (-a | -d | <Branch>)"
 
 #: builtin/remote.c:16
 msgid "git remote [-v | --verbose] show [-n] <name>"
@@ -7518,12 +8086,11 @@ msgstr "git remote prune [-n | --dry-run] <Name>"
 msgid ""
 "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"
 msgstr ""
-"git remote [-v | --verbose] update [-p | --prune] [(<Gruppe> | "
-"<externesProjektarchiv>)...]"
+"git remote [-v | --verbose] update [-p | --prune] [(<Gruppe> | <Remote>)...]"
 
 #: builtin/remote.c:19
 msgid "git remote set-branches [--add] <name> <branch>..."
-msgstr "git remote set-branches [--add] <Name> <Zweig>..."
+msgstr "git remote set-branches [--add] <Name> <Branch>..."
 
 #: builtin/remote.c:20 builtin/remote.c:68
 msgid "git remote set-url [--push] <name> <newurl> [<oldurl>]"
@@ -7543,11 +8110,11 @@ msgstr "git remote add [<Optionen>] <Name> <URL>"
 
 #: builtin/remote.c:47
 msgid "git remote set-branches <name> <branch>..."
-msgstr "git remote set-branches <Name> <Zweig>..."
+msgstr "git remote set-branches <Name> <Branch>..."
 
 #: builtin/remote.c:48
 msgid "git remote set-branches --add <name> <branch>..."
-msgstr "git remote set-branches --add <Name> <Zweig>..."
+msgstr "git remote set-branches --add <Name> <Branch>..."
 
 #: builtin/remote.c:53
 msgid "git remote show [<options>] <name>"
@@ -7559,7 +8126,7 @@ msgstr "git remote prune [<Optionen>] <Name>"
 
 #: builtin/remote.c:63
 msgid "git remote update [<options>] [<group> | <remote>]..."
-msgstr "git remote update [<Optionen>] [<Gruppe> | <externesProjektarchiv>]..."
+msgstr "git remote update [<Optionen>] [<Gruppe> | <externesRepository>]..."
 
 #: builtin/remote.c:98
 #, c-format
@@ -7581,11 +8148,11 @@ msgstr "unbekanntes Argument für Option --mirror: %s"
 
 #: builtin/remote.c:163
 msgid "fetch the remote branches"
-msgstr "fordert die externen Zweige an"
+msgstr "fordert die Remote-Branches an"
 
 #: builtin/remote.c:165
 msgid "import all tags and associated objects when fetching"
-msgstr "importiert alle Markierungen und verbundene Objekte beim Anfordern"
+msgstr "importiert alle Tags und verbundene Objekte beim Anfordern"
 
 #: builtin/remote.c:168
 msgid "or do not fetch any tag at all (--no-tags)"
@@ -7593,11 +8160,11 @@ msgstr "oder fordere gar keine Zweige an (--no-tags)"
 
 #: builtin/remote.c:170
 msgid "branch(es) to track"
-msgstr "Zweige zur Übernahme"
+msgstr "Branch(es) zur Übernahme"
 
 #: builtin/remote.c:171
 msgid "master branch"
-msgstr "Hauptzweig"
+msgstr "Hauptbranch"
 
 #: builtin/remote.c:172
 msgid "push|fetch"
@@ -7606,34 +8173,35 @@ msgstr "push|fetch"
 #: builtin/remote.c:173
 msgid "set up remote as a mirror to push to or fetch from"
 msgstr ""
-"Aufsetzen des Fernarchivs als Spiegelarchiv zum Versenden und Anfordern"
+"Aufsetzen des Remote-Repositories als Spiegel-Repository für \"push\" und "
+"\"fetch\""
 
 #: builtin/remote.c:185
 msgid "specifying a master branch makes no sense with --mirror"
 msgstr ""
-"Die Option --mirror kann nicht mit der Angabe eines Hauptzweiges verwendet "
+"Die Option --mirror kann nicht mit der Angabe eines Hauptbranches verwendet "
 "werden."
 
 #: builtin/remote.c:187
 msgid "specifying branches to track makes sense only with fetch mirrors"
 msgstr ""
-"Die Angabe von zu folgenden Zweigen kann nur mit dem Anfordern von "
+"Die Angabe von zu folgenden Branches kann nur mit dem Anfordern von "
 "Spiegelarchiven verwendet werden."
 
 #: builtin/remote.c:195 builtin/remote.c:646
 #, c-format
 msgid "remote %s already exists."
-msgstr "externes Projektarchiv %s existiert bereits"
+msgstr "externes Repository %s existiert bereits"
 
 #: builtin/remote.c:199 builtin/remote.c:650
 #, c-format
 msgid "'%s' is not a valid remote name"
-msgstr "'%s' ist kein gültiger Name für ein externes Projektarchiv"
+msgstr "'%s' ist kein gültiger Name für ein Remote-Repository"
 
 #: builtin/remote.c:243
 #, c-format
 msgid "Could not setup master '%s'"
-msgstr "Konnte symbolische Referenz für Hauptzweig von '%s' nicht einrichten"
+msgstr "Konnte symbolische Referenz für Hauptbranch von '%s' nicht einrichten"
 
 #: builtin/remote.c:299
 #, c-format
@@ -7643,7 +8211,7 @@ msgstr "mehr als ein %s"
 #: builtin/remote.c:339
 #, c-format
 msgid "Could not get fetch map for refspec %s"
-msgstr "Konnte Abholungszuordnung für Referenzspezifikation %s nicht bekommen"
+msgstr "Konnte Fetch-Map für Refspec %s nicht bekommen"
 
 #: builtin/remote.c:440 builtin/remote.c:448
 msgid "(matching)"
@@ -7661,7 +8229,7 @@ msgstr "Konnte '%s' nicht an '%s' anhängen."
 #: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
 #, c-format
 msgid "No such remote: %s"
-msgstr "Kein solches externes Archiv: %s"
+msgstr "Kein solches Remote-Repository: %s"
 
 #: builtin/remote.c:656
 #, c-format
@@ -7680,8 +8248,7 @@ msgid ""
 "\t%s\n"
 "\tPlease update the configuration manually if necessary."
 msgstr ""
-"Keine Aktualisierung der nicht standardmäßigen Referenzspezifikation zum "
-"Abholen\n"
+"Keine Aktualisierung der nicht standardmäßigen Refspec für \"fetch\"\n"
 "\t%s\n"
 "\tBitte aktualisieren Sie, falls notwendig, die Konfiguration manuell."
 
@@ -7708,7 +8275,7 @@ msgstr "Konnte '%s' nicht erstellen"
 #: builtin/remote.c:764
 #, c-format
 msgid "Could not remove branch %s"
-msgstr "Konnte Zweig %s nicht löschen"
+msgstr "Konnte Branch %s nicht löschen"
 
 #: builtin/remote.c:834
 msgid ""
@@ -7718,18 +8285,18 @@ msgid_plural ""
 "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
 "to delete them, use:"
 msgstr[0] ""
-"Hinweis: Ein Zweig außerhalb der /refs/remotes/ Hierachie wurde nicht "
+"Hinweis: Ein Branch außerhalb der refs/remotes/ Hierachie wurde nicht "
 "gelöscht;\n"
 "um diesen zu löschen, benutzen Sie:"
 msgstr[1] ""
-"Hinweis: Einige Zweige außer der /refs/remotes/ Hierarchie wurden nicht "
+"Hinweis: Einige Branches außer der refs/remotes/ Hierarchie wurden nicht "
 "entfernt;\n"
 "um diese zu entfernen, benutzen Sie:"
 
 #: builtin/remote.c:943
 #, c-format
 msgid " new (next fetch will store in remotes/%s)"
-msgstr " neu (wird bei nächster Abholung in remotes/%s gespeichert)"
+msgstr " neu (wird bei nächstem \"fetch\" in remotes/%s gespeichert)"
 
 #: builtin/remote.c:946
 msgid " tracked"
@@ -7746,30 +8313,30 @@ msgstr " ???"
 #: builtin/remote.c:991
 #, c-format
 msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
-msgstr "ungültiges branch.%s.merge; kann nicht auf > 1 Zweig neu aufbauen"
+msgstr "ungültiges branch.%s.merge; kann Rebase nicht auf > 1 Branch ausführen"
 
 #: builtin/remote.c:998
 #, c-format
 msgid "rebases onto remote %s"
-msgstr "baut neu auf externen Zweig %s auf"
+msgstr "Rebase auf Remote-Branch %s"
 
 #: builtin/remote.c:1001
 #, c-format
 msgid " merges with remote %s"
-msgstr " führt mit externem Zweig %s zusammen"
+msgstr " führt mit Remote-Branch %s zusammen"
 
 #: builtin/remote.c:1002
 msgid "    and with remote"
-msgstr "    und mit externem Zweig"
+msgstr "    und mit Remote-Branch"
 
 #: builtin/remote.c:1004
 #, c-format
 msgid "merges with remote %s"
-msgstr "führt mit externem Zweig %s zusammen"
+msgstr "führt mit Remote-Branch %s zusammen"
 
 #: builtin/remote.c:1005
 msgid "   and with remote"
-msgstr "   und mit externem Zweig"
+msgstr "   und mit Remote-Branch"
 
 #: builtin/remote.c:1051
 msgid "create"
@@ -7813,12 +8380,12 @@ msgstr "    %-*s versendet nach %s"
 
 #: builtin/remote.c:1091
 msgid "do not query remotes"
-msgstr "keine Abfrage von Fernarchiven"
+msgstr "keine Abfrage von Remote-Repositories"
 
 #: builtin/remote.c:1118
 #, c-format
 msgid "* remote %s"
-msgstr "* externes Projektarchiv %s"
+msgstr "* Remote-Repository %s"
 
 #: builtin/remote.c:1119
 #, c-format
@@ -7837,22 +8404,22 @@ msgstr "  URL zum Versenden: %s"
 #: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
 #, c-format
 msgid "  HEAD branch: %s"
-msgstr "  Hauptzweig: %s"
+msgstr "  Hauptbranch: %s"
 
 #: builtin/remote.c:1139
 #, c-format
 msgid ""
 "  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
 msgstr ""
-"  Hauptzweig (externer Hauptzweig ist mehrdeutig, könnte einer der folgenden "
+"  Hauptbranch (externer HEAD ist mehrdeutig, könnte einer der folgenden "
 "sein):\n"
 
 #: builtin/remote.c:1151
 #, c-format
 msgid "  Remote branch:%s"
 msgid_plural "  Remote branches:%s"
-msgstr[0] "  externer Zweig:%s"
-msgstr[1] "  externe Zweige:%s"
+msgstr[0] "  Remote-Branch:%s"
+msgstr[1] "  Remote-Branches:%s"
 
 #: builtin/remote.c:1154 builtin/remote.c:1181
 msgid " (status not queried)"
@@ -7861,8 +8428,8 @@ msgstr " (Zustand nicht abgefragt)"
 #: builtin/remote.c:1163
 msgid "  Local branch configured for 'git pull':"
 msgid_plural "  Local branches configured for 'git pull':"
-msgstr[0] "  Lokaler Zweig konfiguriert für 'git pull':"
-msgstr[1] "  Lokale Zweige konfiguriert für 'git pull':"
+msgstr[0] "  Lokaler Branch konfiguriert für 'git pull':"
+msgstr[1] "  Lokale Branches konfiguriert für 'git pull':"
 
 #: builtin/remote.c:1171
 msgid "  Local refs will be mirrored by 'git push'"
@@ -7877,7 +8444,7 @@ msgstr[1] "  Lokale Referenzen konfiguriert für 'git push'%s:"
 
 #: builtin/remote.c:1199
 msgid "set refs/remotes/<name>/HEAD according to remote"
-msgstr "setzt refs/remotes/<Name>/HEAD gemäß dem Fernarchiv"
+msgstr "setzt refs/remotes/<Name>/HEAD gemäß dem Remote-Repository"
 
 #: builtin/remote.c:1201
 msgid "delete refs/remotes/<name>/HEAD"
@@ -7885,13 +8452,13 @@ msgstr "entfernt refs/remotes/<Name>/HEAD"
 
 #: builtin/remote.c:1216
 msgid "Cannot determine remote HEAD"
-msgstr "Kann Hauptzweig des externen Projektarchivs nicht bestimmen"
+msgstr "Kann HEAD des Remote-Repositories nicht bestimmen"
 
 #: builtin/remote.c:1218
 msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
 msgstr ""
-"Mehrere Hauptzweige im externen Projektarchiv. Bitte wählen Sie explizit "
-"einen aus mit:"
+"Mehrere Hauptbranches im Remote-Repository. Bitte wählen Sie explizit einen "
+"aus mit:"
 
 #: builtin/remote.c:1228
 #, c-format
@@ -7921,7 +8488,7 @@ msgstr " %s wurde unreferenziert!"
 #: builtin/remote.c:1281
 #, c-format
 msgid "Pruning %s"
-msgstr "entferne veraltete Zweige von %s"
+msgstr "entferne veraltete Branches von %s"
 
 #: builtin/remote.c:1282
 #, c-format
@@ -7931,33 +8498,33 @@ msgstr "URL: %s"
 #: builtin/remote.c:1295
 #, c-format
 msgid " * [would prune] %s"
-msgstr " * [würde veralteten Zweig entfernen] %s"
+msgstr " * [würde veralteten Branch entfernen] %s"
 
 #: builtin/remote.c:1298
 #, c-format
 msgid " * [pruned] %s"
-msgstr "* [veralteten Zweig entfernt] %s"
+msgstr "* [veralteten Branch entfernt] %s"
 
 #: builtin/remote.c:1321
 msgid "prune remotes after fetching"
-msgstr "entfernt veraltete Zweige im Fernarchiv nach dem Abholen"
+msgstr "entfernt veraltete Branches im Remote-Repository nach \"fetch\""
 
 #: builtin/remote.c:1387 builtin/remote.c:1461
 #, c-format
 msgid "No such remote '%s'"
-msgstr "Kein solches externes Projektarchiv '%s'"
+msgstr "Kein solches Remote-Repository '%s'"
 
 #: builtin/remote.c:1407
 msgid "add branch"
-msgstr "fügt Zweig hinzu"
+msgstr "fügt Branch hinzu"
 
 #: builtin/remote.c:1414
 msgid "no remote specified"
-msgstr "kein externes Projektarchiv angegeben"
+msgstr "kein Remote-Repository angegeben"
 
 #: builtin/remote.c:1436
 msgid "manipulate push URLs"
-msgstr "manipuliert URLs zum Versenden"
+msgstr "manipuliert URLs für \"push\""
 
 #: builtin/remote.c:1438
 msgid "add URL"
@@ -7984,7 +8551,7 @@ msgstr "Keine solche URL gefunden: %s"
 
 #: builtin/remote.c:1497
 msgid "Will not delete all non-push URLs"
-msgstr "Werde keine URLs entfernen, die nicht für den Versand bestimmt sind"
+msgstr "Werde keine URLs entfernen, die nicht für \"push\" bestimmt sind"
 
 #: builtin/remote.c:1569
 msgid "be verbose; must be placed before a subcommand"
@@ -8020,21 +8587,21 @@ msgstr "git rerere [clean | forget path... | status | remaining | diff | gc]"
 
 #: builtin/rerere.c:56
 msgid "register clean resolutions in index"
-msgstr "registriert saubere Auflösungen in der Bereitstellung"
+msgstr "registriert saubere Auflösungen in der Staging-Area"
 
 #: builtin/reset.c:25
 msgid ""
 "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
 msgstr ""
-"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<Version>]"
+"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<Commit>]"
 
 #: builtin/reset.c:26
 msgid "git reset [-q] <tree-ish> [--] <paths>..."
-msgstr "git reset [-q] <Versionsreferenz> [--] <Pfade>..."
+msgstr "git reset [-q] <Commit-Referenz> [--] <Pfade>..."
 
 #: builtin/reset.c:27
 msgid "git reset --patch [<tree-ish>] [--] [<paths>...]"
-msgstr "git reset --patch [<Versionsreferenz>] [--] [<Pfade>...]"
+msgstr "git reset --patch [<Commit-Referenz>] [--] [<Pfade>...]"
 
 #: builtin/reset.c:33
 msgid "mixed"
@@ -8058,110 +8625,108 @@ msgstr "keep"
 
 #: builtin/reset.c:73
 msgid "You do not have a valid HEAD."
-msgstr "Sie haben keine gültige Zweigspitze (HEAD)."
+msgstr "Sie haben keinen gültigen HEAD."
 
 #: builtin/reset.c:75
 msgid "Failed to find tree of HEAD."
-msgstr "Fehler beim Finden des Baumes der Zweigspitze (HEAD)."
+msgstr "Fehler beim Finden des \"Tree\"-Objektes von HEAD."
 
 #: builtin/reset.c:81
 #, c-format
 msgid "Failed to find tree of %s."
-msgstr "Fehler beim Finden des Baumes von %s."
+msgstr "Fehler beim Finden des \"Tree\"-Objektes von %s."
 
-#: builtin/reset.c:98
+#: builtin/reset.c:99
 #, c-format
 msgid "HEAD is now at %s"
-msgstr "Zweigspitze (HEAD) ist jetzt bei %s"
+msgstr "HEAD ist jetzt bei %s"
 
-#: builtin/reset.c:169
+#: builtin/reset.c:172
 #, c-format
 msgid "Cannot do a %s reset in the middle of a merge."
-msgstr ""
-"Kann keine '%s' Zurücksetzung durchführen, während eine Zusammenführung im "
-"Gange ist."
+msgstr "Kann keinen '%s'-Reset durchführen, während ein Merge im Gange ist."
 
-#: builtin/reset.c:248
+#: builtin/reset.c:251
 msgid "be quiet, only report errors"
 msgstr "weniger Ausgaben, meldet nur Fehler"
 
-#: builtin/reset.c:250
+#: builtin/reset.c:253
 msgid "reset HEAD and index"
-msgstr "setzt Zweigspitze (HEAD) und Bereitstellung neu"
+msgstr "setzt HEAD und Staging-Area um"
 
-#: builtin/reset.c:251
+#: builtin/reset.c:254
 msgid "reset only HEAD"
-msgstr "setzt nur Zweigspitze (HEAD) neu"
+msgstr "setzt nur HEAD um"
 
-#: builtin/reset.c:253 builtin/reset.c:255
+#: builtin/reset.c:256 builtin/reset.c:258
 msgid "reset HEAD, index and working tree"
-msgstr "setzt Zweigspitze (HEAD), Bereitstellung und Arbeitsbaum neu"
+msgstr "setzt HEAD, Staging-Area und Arbeitsverzeichnis um"
 
-#: builtin/reset.c:257
+#: builtin/reset.c:260
 msgid "reset HEAD but keep local changes"
-msgstr "setzt Zweigspitze (HEAD) neu, behält aber lokale Änderungen"
+msgstr "setzt HEAD um, behält aber lokale Änderungen"
 
-#: builtin/reset.c:275
+#: builtin/reset.c:278
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
-msgstr "Konnte '%s' nicht als gültige Revision auflösen."
+msgstr "Konnte '%s' nicht als gültigen Commit auflösen."
 
-#: builtin/reset.c:278 builtin/reset.c:286
+#: builtin/reset.c:281 builtin/reset.c:289
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "Konnte Objekt '%s' nicht parsen."
 
-#: builtin/reset.c:283
+#: builtin/reset.c:286
 #, c-format
 msgid "Failed to resolve '%s' as a valid tree."
-msgstr "Konnte '%s' nicht als gültigen Baum auflösen."
+msgstr "Konnte '%s' nicht als gültiges \"Tree\"-Objekt auflösen."
 
-#: builtin/reset.c:292
+#: builtin/reset.c:295
 msgid "--patch is incompatible with --{hard,mixed,soft}"
 msgstr "--patch ist inkompatibel mit --{hard,mixed,soft}"
 
-#: builtin/reset.c:301
+#: builtin/reset.c:304
 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
 msgstr ""
 "--mixed mit Pfaden ist veraltet; benutzen Sie stattdessen 'git reset -- "
 "<Pfade>'."
 
-#: builtin/reset.c:303
+#: builtin/reset.c:306
 #, c-format
 msgid "Cannot do %s reset with paths."
-msgstr "Eine '%s' Zurücksetzung mit Pfaden ist nicht möglich."
+msgstr "Ein '%s'-Reset mit Pfaden ist nicht möglich."
 
-#: builtin/reset.c:313
+#: builtin/reset.c:316
 #, c-format
 msgid "%s reset is not allowed in a bare repository"
-msgstr "'%s' Zurücksetzung ist in einem bloßen Projektarchiv nicht erlaubt"
+msgstr "'%s'-Reset ist in einem Bare-Repository nicht erlaubt"
 
-#: builtin/reset.c:333
+#: builtin/reset.c:336
 #, c-format
 msgid "Could not reset index file to revision '%s'."
-msgstr "Konnte Bereitstellungsdatei nicht zu Revision '%s' setzen."
+msgstr "Konnte Staging-Area-Datei nicht zu Commit '%s' setzen."
 
-#: builtin/reset.c:339
+#: builtin/reset.c:342
 msgid "Unstaged changes after reset:"
-msgstr "Nicht bereitgestellte Änderungen nach Zurücksetzung:"
+msgstr "Nicht zum Commit vorgemerkte Änderungen nach Zurücksetzung:"
 
-#: builtin/reset.c:344
+#: builtin/reset.c:347
 msgid "Could not write new index file."
-msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+msgstr "Konnte neue Staging-Area-Datei nicht schreiben."
 
-#: builtin/rev-parse.c:339
+#: builtin/rev-parse.c:345
 msgid "git rev-parse --parseopt [options] -- [<args>...]"
 msgstr "git rev-parse --parseopt [Optionen] -- [<Argumente>...]"
 
-#: builtin/rev-parse.c:344
+#: builtin/rev-parse.c:350
 msgid "keep the `--` passed as an arg"
 msgstr "lässt `--` als Argument"
 
-#: builtin/rev-parse.c:346
+#: builtin/rev-parse.c:352
 msgid "stop parsing after the first non-option argument"
 msgstr "stoppt das Parsen nach dem ersten Argument was keine Option ist"
 
-#: builtin/rev-parse.c:464
+#: builtin/rev-parse.c:470
 msgid ""
 "git rev-parse --parseopt [options] -- [<args>...]\n"
 "   or: git rev-parse --sq-quote [<arg>...]\n"
@@ -8177,196 +8742,221 @@ msgstr ""
 "erster Verwendung aus."
 
 #: builtin/revert.c:22
-msgid "git revert [options] <commit-ish>"
-msgstr "git revert [options] <Versionsangabe>"
+msgid "git revert [options] <commit-ish>..."
+msgstr "git revert [Optionen] <Commit-Angabe>..."
 
 #: builtin/revert.c:23
 msgid "git revert <subcommand>"
 msgstr "git revert <Unterkommando>"
 
 #: builtin/revert.c:28
-msgid "git cherry-pick [options] <commit-ish>"
-msgstr "git cherry-pick [Optionen] <Versionsangabe>"
+msgid "git cherry-pick [options] <commit-ish>..."
+msgstr "git cherry-pick [Optionen] <Commit-Angabe>..."
 
 #: builtin/revert.c:29
 msgid "git cherry-pick <subcommand>"
 msgstr "git cherry-pick <Unterkommando>"
 
-#: builtin/revert.c:70 builtin/revert.c:92
+#: builtin/revert.c:71 builtin/revert.c:94
 #, c-format
 msgid "%s: %s cannot be used with %s"
 msgstr "%s: %s kann nicht mit %s verwendet werden"
 
-#: builtin/revert.c:103
+#: builtin/revert.c:105
 msgid "end revert or cherry-pick sequence"
 msgstr "beendet \"revert\" oder \"cherry-pick\" Ablauf"
 
-#: builtin/revert.c:104
+#: builtin/revert.c:106
 msgid "resume revert or cherry-pick sequence"
 msgstr "setzt \"revert\" oder \"cherry-pick\" Ablauf fort"
 
-#: builtin/revert.c:105
+#: builtin/revert.c:107
 msgid "cancel revert or cherry-pick sequence"
 msgstr "bricht \"revert\" oder \"cherry-pick\" Ablauf ab"
 
-#: builtin/revert.c:106
+#: builtin/revert.c:108
 msgid "don't automatically commit"
-msgstr "trägt nicht automatisch ein"
+msgstr "committet nicht automatisch"
 
-#: builtin/revert.c:107
+#: builtin/revert.c:109
 msgid "edit the commit message"
-msgstr "Bearbeitung der Versionsbeschreibung"
+msgstr "Bearbeitung der Commit-Beschreibung"
 
-#: builtin/revert.c:110
+#: builtin/revert.c:112
 msgid "parent number"
 msgstr "Nummer des Elternteils"
 
-#: builtin/revert.c:112
+#: builtin/revert.c:114
 msgid "merge strategy"
-msgstr "Zusammenführungsstrategie"
+msgstr "Merge-Strategie"
 
-#: builtin/revert.c:113
+#: builtin/revert.c:115
 msgid "option"
 msgstr "Option"
 
-#: builtin/revert.c:114
+#: builtin/revert.c:116
 msgid "option for merge strategy"
-msgstr "Option für Zusammenführungsstrategie"
+msgstr "Option für Merge-Strategie"
 
-#: builtin/revert.c:125
+#: builtin/revert.c:127
 msgid "append commit name"
-msgstr "hängt Versionsnamen an"
+msgstr "hängt Commit-Namen an"
 
-#: builtin/revert.c:126
+#: builtin/revert.c:128
 msgid "allow fast-forward"
 msgstr "erlaubt Vorspulen"
 
-#: builtin/revert.c:127
+#: builtin/revert.c:129
 msgid "preserve initially empty commits"
-msgstr "erhält ursprüngliche, leere Versionen"
+msgstr "erhält ursprüngliche, leere Commits"
 
-#: builtin/revert.c:128
+#: builtin/revert.c:130
 msgid "allow commits with empty messages"
-msgstr "erlaubt Version mit leerer Beschreibung"
+msgstr "erlaubt Commits mit leerer Beschreibung"
 
-#: builtin/revert.c:129
+#: builtin/revert.c:131
 msgid "keep redundant, empty commits"
-msgstr "behält redundante, leere Versionen"
+msgstr "behält redundante, leere Commits"
 
-#: builtin/revert.c:133
+#: builtin/revert.c:135
 msgid "program error"
 msgstr "Programmfehler"
 
-#: builtin/revert.c:223
+#: builtin/revert.c:225
 msgid "revert failed"
 msgstr "\"revert\" fehlgeschlagen"
 
-#: builtin/revert.c:238
+#: builtin/revert.c:240
 msgid "cherry-pick failed"
 msgstr "\"cherry-pick\" fehlgeschlagen"
 
-#: builtin/rm.c:15
+#: builtin/rm.c:16
 msgid "git rm [options] [--] <file>..."
 msgstr "git rm [Optionen] [--] [<Datei>...]"
 
-#: builtin/rm.c:64 builtin/rm.c:186
-#, c-format
+#: builtin/rm.c:64
 msgid ""
-"submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+"the following submodule (or one of its nested submodules)\n"
+"uses a .git directory:"
+msgid_plural ""
+"the following submodules (or one of its nested submodules)\n"
+"use a .git directory:"
+msgstr[0] ""
+"das folgende Submodul (oder ein geschachteltes Submodul hiervon)\n"
+"benutzt ein .git-Verzeichnis:"
+msgstr[1] ""
+"die folgenden Submodule (oder ein geschachteltes Submodul hiervon)\n"
+"benutzen ein .git-Verzeichnis:"
+
+#: builtin/rm.c:70
+msgid ""
+"\n"
 "(use 'rm -rf' if you really want to remove it including all of its history)"
 msgstr ""
-"Unterprojekt '%s' (oder ein geschachteltes Unterprojekt hiervon) verwendet\n"
-"ein .git-Verzeichnis (benutzen Sie 'rm -rf' wenn Sie dieses wirklich "
-"mitsamt\n"
+"\n"
+"(benutzen Sie 'rm -rf' wenn Sie dieses Submodul wirklich mitsamt\n"
 "seiner Historie löschen möchten)"
 
-#: builtin/rm.c:174
-#, c-format
+#: builtin/rm.c:230
 msgid ""
-"'%s' has staged content different from both the file and the HEAD\n"
-"(use -f to force removal)"
-msgstr ""
-"'%s' hat bereitgestellten Inhalt unterschiedlich zu der Datei und der\n"
-"Zweigspitze (HEAD) (benutzen Sie -f um die Entfernung zu erzwingen)"
+"the following file has staged content different from both the\n"
+"file and the HEAD:"
+msgid_plural ""
+"the following files have staged content different from both the\n"
+"file and the HEAD:"
+msgstr[0] ""
+"die folgende Datei hat zum Commit vorgemerkte Änderungen unterschiedlich\n"
+"zu der Datei und HEAD:"
+msgstr[1] ""
+"die folgenden Dateien haben zum Commit vorgemerkte Änderungen "
+"unterschiedlich\n"
+"zu der Datei und HEAD:"
 
-#: builtin/rm.c:180
-#, c-format
+#: builtin/rm.c:235
 msgid ""
-"'%s' has changes staged in the index\n"
-"(use --cached to keep the file, or -f to force removal)"
+"\n"
+"(use -f to force removal)"
 msgstr ""
-"'%s' hat Änderungen in der Bereitstellung\n"
-"(benutzen Sie --cached um die Datei zu behalten, oder -f um die Entfernung "
-"zu erzwingen)"
+"\n"
+"(benutzen Sie -f um die Löschung zu erzwingen)"
 
-#: builtin/rm.c:191
-#, c-format
+#: builtin/rm.c:239
+msgid "the following file has changes staged in the index:"
+msgid_plural "the following files have changes staged in the index:"
+msgstr[0] "die folgende Datei hat zum Commit vorgemerkte Änderungen:"
+msgstr[1] "die folgenden Dateien haben zum Commit vorgemerkte Änderungen:"
+
+#: builtin/rm.c:243 builtin/rm.c:254
 msgid ""
-"'%s' has local modifications\n"
+"\n"
 "(use --cached to keep the file, or -f to force removal)"
 msgstr ""
-"'%s' hat lokale Modifikationen\n"
+"\n"
 "(benutzen Sie --cached um die Datei zu behalten, oder -f um die Entfernung "
 "zu erzwingen)"
 
-#: builtin/rm.c:207
+#: builtin/rm.c:251
+msgid "the following file has local modifications:"
+msgid_plural "the following files have local modifications:"
+msgstr[0] "die folgende Datei hat lokale Änderungen:"
+msgstr[1] "die folgenden Dateien haben lokale Änderungen:"
+
+#: builtin/rm.c:269
 msgid "do not list removed files"
 msgstr "listet keine gelöschten Dateien auf"
 
-#: builtin/rm.c:208
+#: builtin/rm.c:270
 msgid "only remove from the index"
-msgstr "entfernt nur aus der Bereitstellung"
+msgstr "entfernt nur aus der Staging-Area"
 
-#: builtin/rm.c:209
+#: builtin/rm.c:271
 msgid "override the up-to-date check"
 msgstr "überschreibt die \"up-to-date\" Prüfung"
 
-#: builtin/rm.c:210
+#: builtin/rm.c:272
 msgid "allow recursive removal"
 msgstr "erlaubt rekursive Entfernung"
 
-#: builtin/rm.c:212
+#: builtin/rm.c:274
 msgid "exit with a zero status even if nothing matched"
 msgstr "beendet mit Rückgabewert 0, wenn keine Übereinstimmung gefunden wurde"
 
-#: builtin/rm.c:283
+#: builtin/rm.c:345
 #, c-format
 msgid "not removing '%s' recursively without -r"
 msgstr "'%s' wird nicht ohne -r rekursiv entfernt"
 
-#: builtin/rm.c:322
+#: builtin/rm.c:384
 #, c-format
 msgid "git rm: unable to remove %s"
 msgstr "git rm: konnte %s nicht löschen"
 
 #: builtin/shortlog.c:13
-msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
-msgstr ""
-"git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] "
-"[<Versionsidentifikation>... ]"
+msgid "git shortlog [<options>] [<revision range>] [[--] [<path>...]]"
+msgstr "git shortlog [<Optionen>] [Commitbereich] [[--] <Pfad>...]"
 
-#: builtin/shortlog.c:133
+#: builtin/shortlog.c:131
 #, c-format
 msgid "Missing author: %s"
 msgstr "fehlender Autor: %s"
 
-#: builtin/shortlog.c:229
+#: builtin/shortlog.c:228
 msgid "sort output according to the number of commits per author"
-msgstr "sortiert die Ausgabe entsprechend der Anzahl von Versionen pro Autor"
+msgstr "sortiert die Ausgabe entsprechend der Anzahl von Commits pro Autor"
 
-#: builtin/shortlog.c:231
+#: builtin/shortlog.c:230
 msgid "Suppress commit descriptions, only provides commit count"
-msgstr "Unterdrückt Versionsbeschreibungen, liefert nur Anzahl der Versionen"
+msgstr "Unterdrückt Commit-Beschreibungen, liefert nur Anzahl der Commits"
 
-#: builtin/shortlog.c:233
+#: builtin/shortlog.c:232
 msgid "Show the email address of each author"
 msgstr "Zeigt die Email-Adresse von jedem Autor"
 
-#: builtin/shortlog.c:234
+#: builtin/shortlog.c:233
 msgid "w[,i1[,i2]]"
 msgstr "w[,i1[,i2]]"
 
-#: builtin/shortlog.c:235
+#: builtin/shortlog.c:234
 msgid "Linewrap output"
 msgstr "Ausgabe mit Zeilenumbrüchen"
 
@@ -8379,78 +8969,76 @@ msgid ""
 msgstr ""
 "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--"
 "current] [--color[=<Wann>] | --no-color] [--sparse] [--more=<n> | --list | --"
-"independent | --merge-base] [--no-name | --sha1-name] [--topics] "
-"[(<Revision> | <glob>)...]"
+"independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<Commit> "
+"| <glob>)...]"
 
 #: builtin/show-branch.c:10
 msgid "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g|--reflog)[=<n>[,<Basis>]] [--list] [<Referenz>]"
 
-#: builtin/show-branch.c:651
+#: builtin/show-branch.c:650
 msgid "show remote-tracking and local branches"
-msgstr "zeigt externe Übernahmezweige und lokale Zweige an"
+msgstr "zeigt Remote-Tracking und lokale Branches an"
 
-#: builtin/show-branch.c:653
+#: builtin/show-branch.c:652
 msgid "show remote-tracking branches"
-msgstr "zeigt externe Übernahmezweige an"
+msgstr "zeigt Remote-Tracking-Branches an"
 
-#: builtin/show-branch.c:655
+#: builtin/show-branch.c:654
 msgid "color '*!+-' corresponding to the branch"
-msgstr "färbt '*!+-' entsprechend des Zweiges ein"
+msgstr "färbt '*!+-' entsprechend des Branches ein"
 
-#: builtin/show-branch.c:657
+#: builtin/show-branch.c:656
 msgid "show <n> more commits after the common ancestor"
-msgstr "zeigt <n> weitere Versionen nach dem gemeinsamen Vorfahren"
+msgstr "zeigt <n> weitere Commits nach dem gemeinsamen Vorgänger-Commit"
 
-#: builtin/show-branch.c:659
+#: builtin/show-branch.c:658
 msgid "synonym to more=-1"
 msgstr "Synonym für more=-1"
 
-#: builtin/show-branch.c:660
+#: builtin/show-branch.c:659
 msgid "suppress naming strings"
 msgstr "unterdrückt Namen"
 
-#: builtin/show-branch.c:662
+#: builtin/show-branch.c:661
 msgid "include the current branch"
-msgstr "bezieht den aktuellen Zweig ein"
+msgstr "bezieht den aktuellen Branch ein"
 
-#: builtin/show-branch.c:664
+#: builtin/show-branch.c:663
 msgid "name commits with their object names"
-msgstr "benennt Versionen nach ihren Objektnamen"
+msgstr "benennt Commits nach ihren Objektnamen"
 
-#: builtin/show-branch.c:666
+#: builtin/show-branch.c:665
 msgid "show possible merge bases"
-msgstr "zeigt mögliche Basen für Zusammenführung"
+msgstr "zeigt mögliche Merge-Basen an"
 
-#: builtin/show-branch.c:668
+#: builtin/show-branch.c:667
 msgid "show refs unreachable from any other ref"
 msgstr "zeigt Referenzen die unerreichbar von allen anderen Referenzen sind"
 
-#: builtin/show-branch.c:670
+#: builtin/show-branch.c:669
 msgid "show commits in topological order"
-msgstr "zeigt Versionen in topologischer Ordnung"
+msgstr "zeigt Commits in topologischer Ordnung"
 
 #: builtin/show-branch.c:672
 msgid "show only commits not on the first branch"
-msgstr "zeigt nur Versionen, die sich nicht im ersten Zweig befinden"
+msgstr "zeigt nur Commits, die sich nicht im ersten Branch befinden"
 
 #: builtin/show-branch.c:674
 msgid "show merges reachable from only one tip"
-msgstr ""
-"zeigt Zusammenführungen, die nur von einer Zweigspitze aus erreichbar sind"
+msgstr "zeigt Merges, die nur von einem Branch aus erreichbar sind"
 
 #: builtin/show-branch.c:676
-msgid "show commits where no parent comes before its children"
-msgstr "zeigt Versionen, wo kein Elternteil vor seinem Kind kommt"
+msgid "topologically sort, maintaining date order where possible"
+msgstr "topologische Sortierung, Beibehaltung Datumsordnung wo möglich"
 
-#: builtin/show-branch.c:678
+#: builtin/show-branch.c:679
 msgid "<n>[,<base>]"
 msgstr "<n>[,<Basis>]"
 
-#: builtin/show-branch.c:679
+#: builtin/show-branch.c:680
 msgid "show <n> most recent ref-log entries starting at base"
-msgstr ""
-"zeigt die <n> jüngsten Einträge im Referenzprotokoll beginnend an der Basis"
+msgstr "zeigt die <n> jüngsten Einträge im Reflog beginnend an der Basis"
 
 #: builtin/show-ref.c:10
 msgid ""
@@ -8464,40 +9052,40 @@ msgstr ""
 msgid "git show-ref --exclude-existing[=pattern] < ref-list"
 msgstr "git show-ref --exclude-existing[=Muster] < ref-list"
 
-#: builtin/show-ref.c:165
+#: builtin/show-ref.c:168
 msgid "only show tags (can be combined with heads)"
-msgstr "zeigt nur Markierungen (kann mit \"heads\" kombiniert werden)"
+msgstr "zeigt nur Tags (kann mit \"heads\" kombiniert werden)"
 
-#: builtin/show-ref.c:166
+#: builtin/show-ref.c:169
 msgid "only show heads (can be combined with tags)"
-msgstr "zeigt nur Zweigspitzen (kann mit \"tags\" kombiniert werden)"
+msgstr "zeigt nur Branches (kann mit \"tags\" kombiniert werden)"
 
-#: builtin/show-ref.c:167
+#: builtin/show-ref.c:170
 msgid "stricter reference checking, requires exact ref path"
 msgstr "strengere Referenzprüfung, erfordert exakten Referenzpfad"
 
-#: builtin/show-ref.c:170 builtin/show-ref.c:172
-msgid "show the HEAD reference"
-msgstr "zeigt Referenz der Zweigspitze (HEAD)"
+#: builtin/show-ref.c:173 builtin/show-ref.c:176
+msgid "show the HEAD reference, even if it would be filtered out"
+msgstr "zeigt die HEAD-Referenz, selbst wenn diese ausgefiltert werden würde"
 
-#: builtin/show-ref.c:174
+#: builtin/show-ref.c:178
 msgid "dereference tags into object IDs"
-msgstr "dereferenziert Markierungen in Objekt-Identifikationen"
+msgstr "dereferenziert Tags in Objekt-Identifikationen"
 
-#: builtin/show-ref.c:176
+#: builtin/show-ref.c:180
 msgid "only show SHA1 hash using <n> digits"
 msgstr "zeigt nur SHA1 Hash mit <n> Ziffern"
 
-#: builtin/show-ref.c:180
+#: builtin/show-ref.c:184
 msgid "do not print results to stdout (useful with --verify)"
 msgstr ""
 "keine Ausgabe der Ergebnisse in die Standard-Ausgabe (nützlich mit --verify)"
 
-#: builtin/show-ref.c:182
+#: builtin/show-ref.c:186
 msgid "show refs from stdin that aren't in local repository"
 msgstr ""
 "zeigt Referenzen von der Standard-Eingabe, die sich nicht im lokalen "
-"Projektarchiv befinden, an"
+"Repository befinden, an"
 
 #: builtin/symbolic-ref.c:7
 msgid "git symbolic-ref [options] name [ref]"
@@ -8532,24 +9120,24 @@ msgstr "Grund für die Aktualisierung"
 msgid ""
 "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]"
 msgstr ""
-"git tag [-a|-s|-u <Schlüssel-id>] [-f] [-m <Nachricht>|-F <Datei>] "
-"<Markierungsname> [<Zweig>]"
+"git tag [-a|-s|-u <Schlüssel-id>] [-f] [-m <Nachricht>|-F <Datei>] <Tagname> "
+"[<Commit>]"
 
 #: builtin/tag.c:23
 msgid "git tag -d <tagname>..."
-msgstr "git tag -d <Markierungsname>..."
+msgstr "git tag -d <Tagname>..."
 
 #: builtin/tag.c:24
 msgid ""
 "git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] \n"
 "\t\t[<pattern>...]"
 msgstr ""
-"git tag -l [-n[<Nummer>]] [--contains <Version>] [--points-at <Objekt>] \n"
+"git tag -l [-n[<Nummer>]] [--contains <Commit>] [--points-at <Objekt>] \n"
 "\t\t[<Muster>...]"
 
 #: builtin/tag.c:26
 msgid "git tag -v <tagname>..."
-msgstr "git tag -v <Markierungsname>..."
+msgstr "git tag -v <Tagname>..."
 
 #: builtin/tag.c:60
 #, c-format
@@ -8559,22 +9147,22 @@ msgstr "fehlerhaftes Objekt bei '%s'"
 #: builtin/tag.c:207
 #, c-format
 msgid "tag name too long: %.*s..."
-msgstr "Markierungsname zu lang: %.*s..."
+msgstr "Tagname zu lang: %.*s..."
 
 #: builtin/tag.c:212
 #, c-format
 msgid "tag '%s' not found."
-msgstr "Markierung '%s' nicht gefunden."
+msgstr "Tag '%s' nicht gefunden."
 
 #: builtin/tag.c:227
 #, c-format
 msgid "Deleted tag '%s' (was %s)\n"
-msgstr "Gelöschte Markierung '%s' (war %s)\n"
+msgstr "Tag '%s' gelöscht (war %s)\n"
 
 #: builtin/tag.c:239
 #, c-format
 msgid "could not verify the tag '%s'"
-msgstr "Konnte Markierung '%s' nicht verifizieren"
+msgstr "Konnte Tag '%s' nicht verifizieren"
 
 #: builtin/tag.c:249
 #, c-format
@@ -8584,7 +9172,7 @@ msgid ""
 "Lines starting with '%c' will be ignored.\n"
 msgstr ""
 "\n"
-"Geben Sie eine Markierungsbeschreibung ein.\n"
+"Geben Sie eine Tag-Beschreibung ein.\n"
 "Zeilen, die mit '%c' beginnen, werden ignoriert.\n"
 
 #: builtin/tag.c:253
@@ -8596,17 +9184,17 @@ msgid ""
 "want to.\n"
 msgstr ""
 "\n"
-"Geben Sie eine Markierungsbeschreibung ein.\n"
+"Geben Sie eine Tag-Beschreibung ein.\n"
 "Zeilen, die mit '%c' beginnen, werden behalten; Sie dürfen diese\n"
 "selbst entfernen wenn Sie möchten.\n"
 
 #: builtin/tag.c:292
 msgid "unable to sign the tag"
-msgstr "konnte Markierung nicht signieren"
+msgstr "konnte Tag nicht signieren"
 
 #: builtin/tag.c:294
 msgid "unable to write tag file"
-msgstr "konnte Markierungsdatei nicht schreiben"
+msgstr "konnte Tag-Datei nicht schreiben"
 
 #: builtin/tag.c:319
 msgid "bad object type."
@@ -8614,16 +9202,16 @@ msgstr "ungültiger Objekt-Typ"
 
 #: builtin/tag.c:332
 msgid "tag header too big."
-msgstr "Markierungskopf zu groß."
+msgstr "Tag-Kopf zu groß."
 
 #: builtin/tag.c:368
 msgid "no tag message?"
-msgstr "keine Markierungsbeschreibung?"
+msgstr "keine Tag-Beschreibung?"
 
 #: builtin/tag.c:374
 #, c-format
 msgid "The tag message has been left in %s\n"
-msgstr "Die Markierungsbeschreibung wurde gelassen in %s\n"
+msgstr "Die Tag-Beschreibung wurde gelassen in %s\n"
 
 #: builtin/tag.c:423
 msgid "switch 'points-at' requires an object"
@@ -8636,59 +9224,59 @@ msgstr "fehlerhafter Objekt-Name '%s'"
 
 #: builtin/tag.c:445
 msgid "list tag names"
-msgstr "listet Markierungsnamen auf"
+msgstr "listet Tagnamen auf"
 
 #: builtin/tag.c:447
 msgid "print <n> lines of each tag message"
-msgstr "zeigt <n> Zeilen jeder Markierungsbeschreibung"
+msgstr "zeigt <n> Zeilen jeder Tag-Beschreibung"
 
 #: builtin/tag.c:449
 msgid "delete tags"
-msgstr "löscht Markierungen"
+msgstr "löscht Tags"
 
 #: builtin/tag.c:450
 msgid "verify tags"
-msgstr "überprüft Markierungen"
+msgstr "überprüft Tags"
 
 #: builtin/tag.c:452
 msgid "Tag creation options"
-msgstr "Optionen für Erstellung von Markierungen"
+msgstr "Optionen für Erstellung von Tags"
 
 #: builtin/tag.c:454
 msgid "annotated tag, needs a message"
-msgstr "annotierte Markierung, benötigt eine Beschreibung"
+msgstr "annotiertes Tag, benötigt eine Beschreibung"
 
 #: builtin/tag.c:456
 msgid "tag message"
-msgstr "Markierungsbeschreibung"
+msgstr "Tag-Beschreibung"
 
 #: builtin/tag.c:458
 msgid "annotated and GPG-signed tag"
-msgstr "annotierte und GPG-signierte Markierung"
+msgstr "annotiertes und GPG-signiertes Tag"
 
 #: builtin/tag.c:462
 msgid "use another key to sign the tag"
-msgstr "verwendet einen anderen Schlüssel um die Markierung zu signieren"
+msgstr "verwendet einen anderen Schlüssel um das Tag zu signieren"
 
 #: builtin/tag.c:463
 msgid "replace the tag if exists"
-msgstr "ersetzt die Markierung, wenn sie existiert"
+msgstr "ersetzt das Tag, wenn es existiert"
 
 #: builtin/tag.c:464
 msgid "show tag list in columns"
-msgstr "zeigt Liste der Markierungen in Spalten"
+msgstr "zeigt Liste der Tags in Spalten"
 
 #: builtin/tag.c:466
 msgid "Tag listing options"
-msgstr "Optionen für Auflistung der Markierungen"
+msgstr "Optionen für Auflistung der Tags"
 
 #: builtin/tag.c:469
 msgid "print only tags that contain the commit"
-msgstr "gibt nur Markierungen aus, die diese Version beinhalten"
+msgstr "gibt nur Tags aus, die diesen Commit beinhalten"
 
 #: builtin/tag.c:475
 msgid "print only tags of the object"
-msgstr "gibt nur Markierungen von dem Objekt aus"
+msgstr "gibt nur Tags von dem Objekt aus"
 
 #: builtin/tag.c:504
 msgid "--column and -n are incompatible"
@@ -8717,12 +9305,12 @@ msgstr "zu viele Parameter"
 #: builtin/tag.c:559
 #, c-format
 msgid "'%s' is not a valid tag name."
-msgstr "'%s' ist kein gültiger Markierungsname."
+msgstr "'%s' ist kein gültiger Tagname."
 
 #: builtin/tag.c:564
 #, c-format
 msgid "tag '%s' already exists"
-msgstr "Markierung '%s' existiert bereits"
+msgstr "Tag '%s' existiert bereits"
 
 #: builtin/tag.c:582
 #, c-format
@@ -8737,7 +9325,7 @@ msgstr "%s: kann Referenz nicht aktualisieren"
 #: builtin/tag.c:586
 #, c-format
 msgid "Updated tag '%s' (was %s)\n"
-msgstr "Aktualisierte Markierung '%s' (war %s)\n"
+msgstr "Tag '%s' aktualisiert (war %s)\n"
 
 #: builtin/update-index.c:401
 msgid "git update-index [options] [--] [<file>...]"
@@ -8746,12 +9334,12 @@ msgstr "git update-index [Optionen] [--] [<Datei>...]"
 #: builtin/update-index.c:718
 msgid "continue refresh even when index needs update"
 msgstr ""
-"Aktualisierung fortsetzen, auch wenn die Bereitstellung aktualisiert werden "
+"Aktualisierung fortsetzen, auch wenn die Staging-Area aktualisiert werden "
 "muss"
 
 #: builtin/update-index.c:721
 msgid "refresh: ignore submodules"
-msgstr "Aktualisierung: ignoriert Unterprojekte"
+msgstr "Aktualisierung: ignoriert Submodule"
 
 #: builtin/update-index.c:724
 msgid "do not ignore new files"
@@ -8763,12 +9351,12 @@ msgstr "lässt Dateien Verzeichnisse ersetzen, und umgedreht"
 
 #: builtin/update-index.c:728
 msgid "notice files missing from worktree"
-msgstr "beachtet fehlende Dateien im Arbeitsbaum"
+msgstr "beachtet fehlende Dateien im Arbeitsverzeichnis"
 
 #: builtin/update-index.c:730
 msgid "refresh even if index contains unmerged entries"
 msgstr ""
-"aktualisiert, auch wenn die Bereitstellung nicht zusammengeführte Einträge "
+"aktualisiert, auch wenn die Staging-Area nicht zusammengeführte Einträge "
 "beinhaltet"
 
 #: builtin/update-index.c:733
@@ -8785,7 +9373,7 @@ msgstr "<Modus> <Objekt> <Pfad>"
 
 #: builtin/update-index.c:742
 msgid "add the specified entry to the index"
-msgstr "stellt den angegebenen Eintrag zur Eintragung bereit"
+msgstr "merkt den angegebenen Eintrag zum Commit vor"
 
 #: builtin/update-index.c:746
 msgid "(+/-)x"
@@ -8814,12 +9402,13 @@ msgstr "löscht \"skip-worktree\"-Bit"
 #: builtin/update-index.c:763
 msgid "add to index only; do not add content to object database"
 msgstr ""
-"fügt nur der Bereitstellung hinzu; Inhalt wird nicht der Objekt-Datenbank "
-"hinzugefügt"
+"merkt die Änderungen nur zum Commit vor; Inhalt wird nicht der Objekt-"
+"Datenbank hinzugefügt"
 
 #: builtin/update-index.c:765
 msgid "remove named paths even if present in worktree"
-msgstr "löscht benannte Pfade, auch wenn sie sich im Arbeitsbaum befinden"
+msgstr ""
+"löscht benannte Pfade, auch wenn sie sich im Arbeitsverzeichnis befinden"
 
 #: builtin/update-index.c:767
 msgid "with --stdin: input lines are terminated by null bytes"
@@ -8831,7 +9420,7 @@ msgstr "liest Liste der zu aktualisierenden Pfade von der Standard-Eingabe"
 
 #: builtin/update-index.c:773
 msgid "add entries from standard input to the index"
-msgstr "fügt Einträge von der Standard-Eingabe der Bereitstellung hinzu"
+msgstr "merkt Einträge von der Standard-Eingabe zum Commit vor"
 
 #: builtin/update-index.c:777
 msgid "repopulate stages #2 and #3 for the listed paths"
@@ -8840,12 +9429,11 @@ msgstr ""
 
 #: builtin/update-index.c:781
 msgid "only update entries that differ from HEAD"
-msgstr ""
-"aktualisiert nur Einträge, die unterschiedlich zur Zweigspitze (HEAD) sind"
+msgstr "aktualisiert nur Einträge, die unterschiedlich zu HEAD sind"
 
 #: builtin/update-index.c:785
 msgid "ignore files missing from worktree"
-msgstr "ignoriert fehlende Dateien im Arbeitsbaum"
+msgstr "ignoriert fehlende Dateien im Arbeitsverzeichnis"
 
 #: builtin/update-index.c:788
 msgid "report actions to standard output"
@@ -8857,7 +9445,7 @@ msgstr "(für Fremdprogramme) keine gespeicherten, nicht aufgelöste Konflikte"
 
 #: builtin/update-index.c:794
 msgid "write index in this format"
-msgstr "schreibt Bereitstellungsdatei in diesem Format"
+msgstr "schreibt Staging-Area-Datei in diesem Format"
 
 #: builtin/update-ref.c:7
 msgid "git update-ref [options] -d <refname> [<oldval>]"
@@ -8897,11 +9485,11 @@ msgstr "zeigt nur Statistiken"
 
 #: builtin/verify-tag.c:17
 msgid "git verify-tag [-v|--verbose] <tag>..."
-msgstr "git verify-tag [-v|--verbose] <Markierung>..."
+msgstr "git verify-tag [-v|--verbose] <Tag>..."
 
 #: builtin/verify-tag.c:73
 msgid "print tag contents"
-msgstr "gibt Markierungsinhalte aus"
+msgstr "gibt Tag-Inhalte aus"
 
 #: builtin/write-tree.c:13
 msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
@@ -8913,37 +9501,46 @@ msgstr "<Prefix>/"
 
 #: builtin/write-tree.c:27
 msgid "write tree object for a subdirectory <prefix>"
-msgstr "schreibt das Baumobjekt für ein Unterverzeichnis <Prefix>"
+msgstr "schreibt das \"Tree\"-Objekt für ein Unterverzeichnis <Prefix>"
 
 #: builtin/write-tree.c:30
 msgid "only useful for debugging"
 msgstr "nur nützlich für Fehlersuche"
 
-#: git.c:16
-msgid "See 'git help <command>' for more information on a specific command."
+#: git.c:17
+msgid ""
+"'git help -a' and 'git help -g' lists available subcommands and some\n"
+"concept guides. See 'git help <command>' or 'git help <concept>'\n"
+"to read about a specific subcommand or concept."
 msgstr ""
-"Siehe 'git help <Kommando>' für weitere Informationen zu einem spezifischen "
-"Kommando"
+"'git help -a' und 'git help -g' listet verfügbare Unterkommandos und\n"
+"einige Anleitungen zu Git-Konzepten auf. Benutzen Sie 'git help <Kommando>'\n"
+"oder 'git help <Konzept>' um mehr über ein spezifisches Kommando oder\n"
+"Konzept zu erfahren."
+
+#: parse-options.h:144
+msgid "expiry date"
+msgstr "Verfallsdatum"
 
-#: parse-options.h:156
+#: parse-options.h:159
 msgid "no-op (backward compatibility)"
 msgstr "Kein Effekt (Rückwärtskompatibilität)"
 
-#: parse-options.h:232
+#: parse-options.h:236
 msgid "be more verbose"
 msgstr "erweiterte Ausgaben"
 
-#: parse-options.h:234
+#: parse-options.h:238
 msgid "be more quiet"
 msgstr "weniger Ausgaben"
 
-#: parse-options.h:240
+#: parse-options.h:244
 msgid "use <n> digits to display SHA-1s"
 msgstr "benutze <n> Ziffern zur Anzeige von SHA-1s"
 
 #: common-cmds.h:8
 msgid "Add file contents to the index"
-msgstr "stellt Dateiinhalte zur Eintragung bereit"
+msgstr "merkt Dateiinhalte zum Commit vor"
 
 #: common-cmds.h:9
 msgid "Find by binary search the change that introduced a bug"
@@ -8952,27 +9549,28 @@ msgstr ""
 
 #: common-cmds.h:10
 msgid "List, create, or delete branches"
-msgstr "Zeigt an, erstellt oder entfernt Zweige"
+msgstr "Zeigt an, erstellt oder entfernt Branches"
 
 #: common-cmds.h:11
 msgid "Checkout a branch or paths to the working tree"
-msgstr "Checkt Zweige oder Pfade im Arbeitszweig aus"
+msgstr "Checkt Branches oder Pfade im Arbeitsverzeichnis aus"
 
 #: common-cmds.h:12
 msgid "Clone a repository into a new directory"
-msgstr "Klont ein Projektarchiv in einem neuen Verzeichnis"
+msgstr "Klont ein Repository in einem neuen Verzeichnis"
 
 #: common-cmds.h:13
 msgid "Record changes to the repository"
-msgstr "Trägt Änderungen in das Projektarchiv ein"
+msgstr "Trägt Änderungen in das Repository ein"
 
 #: common-cmds.h:14
 msgid "Show changes between commits, commit and working tree, etc"
-msgstr "Zeigt Änderungen zwischen Versionen, Version und Arbeitszweig, etc. an"
+msgstr ""
+"Zeigt Änderungen zwischen Commits, Commit und Arbeitsverzeichnis, etc. an"
 
 #: common-cmds.h:15
 msgid "Download objects and refs from another repository"
-msgstr "Lädt Objekte und Referenzen von einem anderen Projektarchiv herunter"
+msgstr "Lädt Objekte und Referenzen von einem anderen Repository herunter"
 
 #: common-cmds.h:16
 msgid "Print lines matching a pattern"
@@ -8981,11 +9579,11 @@ msgstr "Stellt Zeilen dar, die einem Muster entsprechen"
 #: common-cmds.h:17
 msgid "Create an empty Git repository or reinitialize an existing one"
 msgstr ""
-"Erstellt ein leeres Git-Projektarchiv oder initialisiert ein bestehendes neu"
+"Erstellt ein leeres Git-Repository oder initialisiert ein bestehendes neu"
 
 #: common-cmds.h:18
 msgid "Show commit logs"
-msgstr "Zeigt Versionshistorie an"
+msgstr "Zeigt Commit-Historie an"
 
 #: common-cmds.h:19
 msgid "Join two or more development histories together"
@@ -8994,30 +9592,30 @@ msgstr "Führt zwei oder mehr Entwicklungszweige zusammen"
 #: common-cmds.h:20
 msgid "Move or rename a file, a directory, or a symlink"
 msgstr ""
-"Verschiebt oder benennt eine Datei, ein Verzeichnis, oder eine symbolische "
-"Verknüpfung um"
+"Verschiebt oder benennt eine Datei, ein Verzeichnis, oder einen symbolischen "
+"Verweis um"
 
 #: common-cmds.h:21
-msgid "Fetch from and merge with another repository or a local branch"
+msgid "Fetch from and integrate with another repository or a local branch"
 msgstr ""
-"Fordert Objekte von einem externen Projektarchiv an und führt sie mit einem "
-"anderen Projektarchiv oder einem lokalen Zweig zusammen"
+"Fordert Objekte von einem externen Repository an und führt sie mit einem "
+"anderen Repository oder einem lokalen Branch zusammen"
 
 #: common-cmds.h:22
 msgid "Update remote refs along with associated objects"
-msgstr "Aktualisiert externe Referenzen mitsamt den verbundenen Objekten"
+msgstr "Aktualisiert Remote-Referenzen mitsamt den verbundenen Objekten"
 
 #: common-cmds.h:23
 msgid "Forward-port local commits to the updated upstream head"
-msgstr "Baut lokale Versionen auf einem aktuellerem externen Zweig neu auf"
+msgstr "Baut lokale Commits auf einem aktuellerem Upstream-Branch neu auf"
 
 #: common-cmds.h:24
 msgid "Reset current HEAD to the specified state"
-msgstr "Setzt die aktuelle Zweigspitze (HEAD) zu einem spezifizierten Zustand"
+msgstr "Setzt aktuellen HEAD zu einem spezifizierten Zustand"
 
 #: common-cmds.h:25
 msgid "Remove files from the working tree and from the index"
-msgstr "Löscht Dateien im Arbeitszweig und von der Bereitstellung"
+msgstr "Löscht Dateien im Arbeitsverzeichnis und von der Staging-Area"
 
 #: common-cmds.h:26
 msgid "Show various types of objects"
@@ -9025,54 +9623,53 @@ msgstr "Zeigt verschiedene Arten von Objekten an"
 
 #: common-cmds.h:27
 msgid "Show the working tree status"
-msgstr "Zeigt den Zustand des Arbeitszweiges an"
+msgstr "Zeigt den Zustand des Arbeitsverzeichnisses an"
 
 #: common-cmds.h:28
 msgid "Create, list, delete or verify a tag object signed with GPG"
 msgstr ""
-"Erzeugt, listet auf, löscht oder verifiziert ein mit GPG signiertes "
-"Markierungsobjekt"
+"Erzeugt, listet auf, löscht oder verifiziert ein mit GPG signiertes Tag-"
+"Objekt"
 
 #: git-am.sh:50
 msgid "You need to set your committer info first"
-msgstr "Sie müssen zuerst die Informationen des Eintragenden setzen."
+msgstr "Sie müssen zuerst die Informationen zum Commit-Ersteller setzen."
 
 #: git-am.sh:95
 msgid ""
 "You seem to have moved HEAD since the last 'am' failure.\n"
 "Not rewinding to ORIG_HEAD"
 msgstr ""
-"Sie scheinen seit dem letzten gescheiterten 'am' die Zweigspitze (HEAD)\n"
-"geändert zu haben.\n"
+"Sie scheinen seit dem letzten gescheiterten 'am' HEAD geändert zu haben.\n"
 "Keine Zurücksetzung zu ORIG_HEAD."
 
 #: git-am.sh:105
 #, sh-format
 msgid ""
-"When you have resolved this problem, run \"$cmdline --resolved\".\n"
+"When you have resolved this problem, run \"$cmdline --continue\".\n"
 "If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n"
 "To restore the original branch and stop patching, run \"$cmdline --abort\"."
 msgstr ""
-"Wenn Sie das Problem gelöst haben, führen Sie \"$cmdline --resolved\" aus.\n"
+"Wenn Sie das Problem gelöst haben, führen Sie \"$cmdline --continue\" aus.\n"
 "Falls Sie diesen Patch auslassen möchten, führen Sie stattdessen\n"
 "\"$cmdline --skip\" aus.\n"
-"Um den ursprünglichen Zweig wiederherzustellen und die Anwendung der\n"
+"Um den ursprünglichen Branch wiederherzustellen und die Anwendung der\n"
 "Patches abzubrechen, führen Sie \"$cmdline --abort\" aus."
 
 #: git-am.sh:121
 msgid "Cannot fall back to three-way merge."
-msgstr "Kann nicht zu 3-Wege-Zusammenführung zurückfallen."
+msgstr "Kann nicht zu 3-Wege-Merge zurückfallen."
 
 #: git-am.sh:137
 msgid "Repository lacks necessary blobs to fall back on 3-way merge."
 msgstr ""
-"Dem Projektarchiv fehlen notwendige Blobs um auf eine 3-Wege-Zusammenführung "
+"Dem Repository fehlen notwendige Blobs um auf einen 3-Wege-Merge "
 "zurückzufallen."
 
 #: git-am.sh:139
 msgid "Using index info to reconstruct a base tree..."
 msgstr ""
-"Verwende Informationen aus der Bereitstellung um einen Basisbaum "
+"Verwende Informationen aus der Staging-Area um einen Basisverzeichnis "
 "nachzustellen"
 
 #: git-am.sh:154
@@ -9085,11 +9682,11 @@ msgstr ""
 
 #: git-am.sh:163
 msgid "Falling back to patching base and 3-way merge..."
-msgstr "Falle zurück zum Patchen der Basis und der 3-Wege-Zusammenführung..."
+msgstr "Falle zurück zum Patchen der Basis und des 3-Wege-Merges..."
 
 #: git-am.sh:179
 msgid "Failed to merge in the changes."
-msgstr "Zusammenführung der Änderungen fehlgeschlagen"
+msgstr "Merge der Änderungen fehlgeschlagen"
 
 #: git-am.sh:274
 msgid "Only one StGIT patch series can be applied at once"
@@ -9112,28 +9709,35 @@ msgstr ""
 "Die -b/--binary Option hat seit Langem keinen Effekt und wird\n"
 "entfernt. Bitte nicht mehr verwenden."
 
-#: git-am.sh:477
+#: git-am.sh:479
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
 msgstr ""
-"Vorheriges Verzeichnis des Neuaufbaus $dotest existiert noch, aber mbox "
-"gegeben."
+"Vorheriges Rebase-Verzeichnis $dotest existiert noch, aber mbox gegeben."
 
-#: git-am.sh:482
+#: git-am.sh:484
 msgid "Please make up your mind. --skip or --abort?"
 msgstr "Bitte werden Sie sich klar. --skip oder --abort?"
 
-#: git-am.sh:509
+#: git-am.sh:520
+#, sh-format
+msgid ""
+"Stray $dotest directory found.\n"
+"Use \"git am --abort\" to remove it."
+msgstr ""
+"Stray $dotest Verzeichnis gefunden.\n"
+"Benutzen Sie \"git am --abort\" um es zu entfernen."
+
+#: git-am.sh:528
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr "Es ist keine Auflösung im Gange, es wird nicht fortgesetzt."
 
-#: git-am.sh:575
+#: git-am.sh:594
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
-msgstr ""
-"Unsaubere Bereitstellung: kann Patches nicht anwenden (unsauber: $files)"
+msgstr "Geänderte Staging-Area: kann Patches nicht anwenden (geändert: $files)"
 
-#: git-am.sh:679
+#: git-am.sh:698
 #, sh-format
 msgid ""
 "Patch is empty.  Was it split wrong?\n"
@@ -9143,65 +9747,65 @@ msgstr ""
 "Patch ist leer. Wurde er falsch aufgeteilt?\n"
 "Wenn Sie diesen Patch auslassen möchten, führen Sie stattdessen\n"
 "\"$cmdline --skip\" aus.\n"
-"Um den ursprünglichen Zweig wiederherzustellen und die Anwendung der "
+"Um den ursprünglichen Branch wiederherzustellen und die Anwendung der "
 "Patches\n"
 "abzubrechen, führen Sie \"$cmdline --abort\" aus."
 
-#: git-am.sh:706
+#: git-am.sh:725
 msgid "Patch does not have a valid e-mail address."
 msgstr "Patch enthält keine gültige Email-Adresse."
 
-#: git-am.sh:753
+#: git-am.sh:772
 msgid "cannot be interactive without stdin connected to a terminal."
 msgstr ""
 "Kann nicht interaktiv sein, ohne dass die Standard-Eingabe mit einem "
 "Terminal verbunden ist."
 
-#: git-am.sh:757
+#: git-am.sh:776
 msgid "Commit Body is:"
-msgstr "Beschreibung der Eintragung ist:"
+msgstr "Commit-Beschreibung ist:"
 
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
-#: git-am.sh:764
+#: git-am.sh:783
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 msgstr "Anwenden? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 
-#: git-am.sh:800
+#: git-am.sh:819
 #, sh-format
 msgid "Applying: $FIRSTLINE"
 msgstr "Wende an: $FIRSTLINE"
 
-#: git-am.sh:821
+#: git-am.sh:840
 msgid ""
 "No changes - did you forget to use 'git add'?\n"
 "If there is nothing left to stage, chances are that something else\n"
 "already introduced the same changes; you might want to skip this patch."
 msgstr ""
 "Keine Änderungen - haben Sie vergessen 'git add' zu benutzen?\n"
-"Wenn keine Änderungen mehr zum Bereitstellen vorhanden sind, könnten\n"
+"Wenn keine Änderungen mehr zum Commit vorzumerken sind, könnten\n"
 "diese bereits anderweitig eingefügt worden sein; Sie könnten diesen Patch\n"
 "auslassen."
 
-#: git-am.sh:829
+#: git-am.sh:848
 msgid ""
 "You still have unmerged paths in your index\n"
 "did you forget to use 'git add'?"
 msgstr ""
-"Sie haben immer noch nicht zusammengeführte Pfade in der Bereitstellung.\n"
+"Sie haben immer noch nicht zusammengeführte Pfade in der Staging-Area.\n"
 "Haben Sie vergessen 'git add' zu benutzen?"
 
-#: git-am.sh:845
+#: git-am.sh:864
 msgid "No changes -- Patch already applied."
 msgstr "Keine Änderungen -- Patches bereits angewendet."
 
-#: git-am.sh:855
+#: git-am.sh:874
 #, sh-format
 msgid "Patch failed at $msgnum $FIRSTLINE"
 msgstr "Anwendung des Patches fehlgeschlagen bei $msgnum $FIRSTLINE"
 
-#: git-am.sh:858
+#: git-am.sh:877
 #, sh-format
 msgid ""
 "The copy of the patch that failed is found in:\n"
@@ -9210,7 +9814,7 @@ msgstr ""
 "Die Kopie des fehlgeschlagenen Patches befindet sich in:\n"
 "   $dotest/patch"
 
-#: git-am.sh:876
+#: git-am.sh:895
 msgid "applying to an empty history"
 msgstr "wende zu leerer Historie an"
 
@@ -9233,11 +9837,11 @@ msgstr "nicht erkannte Option: '$arg'"
 #: git-bisect.sh:99
 #, sh-format
 msgid "'$arg' does not appear to be a valid revision"
-msgstr "'$arg' scheint keine gültige Revision zu sein"
+msgstr "'$arg' scheint kein gültiger Commit zu sein"
 
 #: git-bisect.sh:117
 msgid "Bad HEAD - I need a HEAD"
-msgstr "Ungültige Zweigspitze (HEAD) - Zweigspitze (HEAD) wird benötigt"
+msgstr "Ungültiger HEAD - HEAD wird benötigt"
 
 #: git-bisect.sh:130
 #, sh-format
@@ -9245,15 +9849,15 @@ msgid ""
 "Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
 msgstr ""
 "Auschecken von '$start_head' fehlgeschlagen. Versuchen Sie 'git bisect reset "
-"<gueltigerzweig>'."
+"<gueltigerbranch>'."
 
 #: git-bisect.sh:140
 msgid "won't bisect on seeked tree"
-msgstr "binäre Suche auf gesuchtem Zweig nicht möglich"
+msgstr "binäre Suche auf gesuchtem Branch nicht möglich"
 
 #: git-bisect.sh:144
 msgid "Bad HEAD - strange symbolic ref"
-msgstr "Ungültige Zweigspitze (HEAD) - merkwürdige symbolische Referenz"
+msgstr "Ungültiger HEAD - merkwürdige symbolische Referenz"
 
 #: git-bisect.sh:189
 #, sh-format
@@ -9282,7 +9886,7 @@ msgstr "'git bisect bad' kann nur ein Argument entgegennehmen."
 #. this is less optimum.
 #: git-bisect.sh:273
 msgid "Warning: bisecting only with a bad commit."
-msgstr "Warnung: binäre Suche nur mit einer fehlerhaften Version"
+msgstr "Warnung: binäre Suche nur mit einem fehlerhaften Commit"
 
 #. TRANSLATORS: Make sure to include [Y] and [n] in your
 #. translation. The program will only accept English input
@@ -9296,7 +9900,8 @@ msgid ""
 "You need to give me at least one good and one bad revisions.\n"
 "(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
 msgstr ""
-"Sie müssen mindestens eine korrekte und eine fehlerhafte Revision angeben.\n"
+"Sie müssen mindestens einen korrekten und einen fehlerhaften Commit "
+"angeben.\n"
 "(Sie können dafür \"git bisect bad\" und \"git bisect good\" benutzen.)"
 
 #: git-bisect.sh:292
@@ -9306,47 +9911,47 @@ msgid ""
 "(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
 msgstr ""
 "Sie müssen mit \"git bisect start\" beginnen.\n"
-"Danach müssen Sie mindestens eine korrekte und eine fehlerhafte Revision "
+"Danach müssen Sie mindestens einen korrekten und einen fehlerhaften Commit "
 "angeben.\n"
 "(Sie können dafür \"git bisect bad\" und \"git bisect good\" benutzen.)"
 
-#: git-bisect.sh:347 git-bisect.sh:474
+#: git-bisect.sh:363 git-bisect.sh:490
 msgid "We are not bisecting."
 msgstr "keine binäre Suche im Gange"
 
-#: git-bisect.sh:354
+#: git-bisect.sh:370
 #, sh-format
 msgid "'$invalid' is not a valid commit"
-msgstr "'$invalid' ist keine gültige Version"
+msgstr "'$invalid' ist kein gültiger Commit"
 
-#: git-bisect.sh:363
+#: git-bisect.sh:379
 #, sh-format
 msgid ""
 "Could not check out original HEAD '$branch'.\n"
 "Try 'git bisect reset <commit>'."
 msgstr ""
-"Konnte die ursprüngliche Zweigspitze (HEAD) '$branch' nicht auschecken.\n"
-"Versuchen Sie 'git bisect reset <Version>'."
+"Konnte den ursprünglichen HEAD '$branch' nicht auschecken.\n"
+"Versuchen Sie 'git bisect reset <Commit>'."
 
-#: git-bisect.sh:390
+#: git-bisect.sh:406
 msgid "No logfile given"
 msgstr "Keine Log-Datei gegeben"
 
-#: git-bisect.sh:391
+#: git-bisect.sh:407
 #, sh-format
 msgid "cannot read $file for replaying"
 msgstr "kann $file nicht für das Abspielen lesen"
 
-#: git-bisect.sh:408
+#: git-bisect.sh:424
 msgid "?? what are you talking about?"
 msgstr "?? Was reden Sie da?"
 
-#: git-bisect.sh:420
+#: git-bisect.sh:436
 #, sh-format
 msgid "running $command"
 msgstr "führe $command aus"
 
-#: git-bisect.sh:427
+#: git-bisect.sh:443
 #, sh-format
 msgid ""
 "bisect run failed:\n"
@@ -9355,11 +9960,11 @@ msgstr ""
 "'bisect run' fehlgeschlagen:\n"
 "Rückkehrwert $res von '$command' ist < 0 oder >= 128"
 
-#: git-bisect.sh:453
+#: git-bisect.sh:469
 msgid "bisect run cannot continue any more"
 msgstr "'bisect run' kann nicht mehr fortgesetzt werden"
 
-#: git-bisect.sh:459
+#: git-bisect.sh:475
 #, sh-format
 msgid ""
 "bisect run failed:\n"
@@ -9368,7 +9973,7 @@ msgstr ""
 "'bisect run' fehlgeschlagen:\n"
 "'bisect_state $state' wurde mit Fehlerwert $res beendet"
 
-#: git-bisect.sh:466
+#: git-bisect.sh:482
 msgid "bisect run success"
 msgstr "'bisect run' erfolgreich ausgeführt"
 
@@ -9379,45 +9984,44 @@ msgid ""
 "as appropriate to mark resolution, or use 'git commit -a'."
 msgstr ""
 "\"pull\" ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben.\n"
-"Bitte korrigieren Sie dies im Arbeitsbaum und benutzen Sie dann 'git add/rm "
-"<Datei>'\n"
-"um die Auflösung entsprechend zu markieren, oder benutzen Sie 'git commit -"
-"a'."
+"Bitte korrigieren Sie dies im Arbeitsverzeichnis und benutzen Sie dann \n"
+"'git add/rm <Datei>' um die Auflösung entsprechend zu markieren, oder\n"
+"benutzen Sie 'git commit -a'."
 
 #: git-pull.sh:25
 msgid "Pull is not possible because you have unmerged files."
 msgstr ""
 "\"pull\" ist nicht möglich, weil Sie nicht zusammengeführte Dateien haben."
 
-#: git-pull.sh:197
+#: git-pull.sh:203
 msgid "updating an unborn branch with changes added to the index"
 msgstr ""
-"Aktualisiere eine ungeborenen Zweig mit Änderungen, die zur Bereitstellung "
-"hinzugefügt wurden"
+"Aktualisiere einen ungeborenen Branch mit Änderungen, die zum Commit "
+"vorgemerkt sind"
 
 #. The fetch involved updating the current branch.
 #. The working tree and the index file is still based on the
 #. $orig_head commit, but we are merging into $curr_head.
 #. First update the working tree to match $curr_head.
-#: git-pull.sh:229
+#: git-pull.sh:235
 #, sh-format
 msgid ""
 "Warning: fetch updated the current branch head.\n"
 "Warning: fast-forwarding your working tree from\n"
 "Warning: commit $orig_head."
 msgstr ""
-"Warnung: Die Anforderung aktualisierte die Spitze des aktuellen Zweiges.\n"
-"Warnung: Spule Ihren Arbeitszweig von Version $orig_head vor."
+"Warnung: \"fetch\" aktualisierte die Spitze des aktuellen Branches.\n"
+"Warnung: Spule Ihr Arbeitsverzeichnis von Commit $orig_head vor."
 
-#: git-pull.sh:254
+#: git-pull.sh:260
 msgid "Cannot merge multiple branches into empty head"
-msgstr "Kann nicht mehrere Zweige in einen ungeborenen Zweig zusammenführen"
+msgstr "Kann nicht mehrere Branches in einen ungeborenen Branch zusammenführen"
 
-#: git-pull.sh:258
+#: git-pull.sh:264
 msgid "Cannot rebase onto multiple branches"
-msgstr "kann nicht auf mehrere Zweige neu aufbauen"
+msgstr "kann Rebase nicht auf mehrere Branches ausführen"
 
-#: git-rebase.sh:53
+#: git-rebase.sh:54
 msgid ""
 "When you have resolved this problem, run \"git rebase --continue\".\n"
 "If you prefer to skip this patch, run \"git rebase --skip\" instead.\n"
@@ -9428,50 +10032,68 @@ msgstr ""
 "aus.\n"
 "Falls Sie diesen Patch auslassen möchten, führen Sie stattdessen \"git "
 "rebase --skip\" aus.\n"
-"Um den ursprünglichen Zweig wiederherzustellen und den Neuaufbau "
-"abzubrechen,\n"
+"Um den ursprünglichen Branch wiederherzustellen und den Rebase abzubrechen,\n"
 "führen Sie \"git rebase --abort\" aus."
 
+#: git-rebase.sh:156
+msgid "Applied autostash."
+msgstr "\"autostash\" angewendet."
+
+#: git-rebase.sh:159
+#, sh-format
+msgid "Cannot store $stash_sha1"
+msgstr "Kann $stash_sha1 nicht speichern."
+
 #: git-rebase.sh:160
+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 ""
+"Anwendung von \"autostash\" resultierte in Konflikten.\n"
+"Ihre Änderungen sind im Stash sicher.\n"
+"Sie können jederzeit \"git stash pop\" oder \"git stash drop\" ausführen.\n"
+
+#: git-rebase.sh:190
 msgid "The pre-rebase hook refused to rebase."
-msgstr "Der \"pre-rebase hook\" hat den Neuaufbau zurückgewiesen."
+msgstr "Der \"pre-rebase hook\" hat den Rebase zurückgewiesen."
 
-#: git-rebase.sh:165
+#: git-rebase.sh:195
 msgid "It looks like git-am is in progress. Cannot rebase."
-msgstr "\"git-am\" scheint im Gange zu sein. Kann nicht neu aufbauen."
+msgstr "\"git-am\" scheint im Gange zu sein. Kann Rebase nicht durchführen."
 
-#: git-rebase.sh:296
+#: git-rebase.sh:329
 msgid "The --exec option must be used with the --interactive option"
 msgstr "Die Option --exec muss mit --interactive verwendet werden."
 
-#: git-rebase.sh:301
+#: git-rebase.sh:334
 msgid "No rebase in progress?"
-msgstr "Kein Neuaufbau im Gange?"
+msgstr "Kein Rebase im Gange?"
 
-#: git-rebase.sh:312
+#: git-rebase.sh:345
 msgid "The --edit-todo action can only be used during interactive rebase."
 msgstr ""
-"Die --edit-todo Aktion kann nur während eines interaktiven Neuaufbaus "
-"verwendet werden."
+"Die --edit-todo Aktion kann nur während eines interaktiven Rebase verwendet "
+"werden."
 
-#: git-rebase.sh:319
+#: git-rebase.sh:352
 msgid "Cannot read HEAD"
-msgstr "Kann Zweigspitze (HEAD) nicht lesen"
+msgstr "Kann HEAD nicht lesen"
 
-#: git-rebase.sh:322
+#: git-rebase.sh:355
 msgid ""
 "You must edit all merge conflicts and then\n"
 "mark them as resolved using git add"
 msgstr ""
-"Sie müssen alle Zusammenführungskonflikte editieren und diese dann\n"
+"Sie müssen alle Merge-Konflikte editieren und diese dann\n"
 "mittels \"git add\" als aufgelöst markieren"
 
-#: git-rebase.sh:340
+#: git-rebase.sh:373
 #, sh-format
 msgid "Could not move back to $head_name"
 msgstr "Konnte nicht zu $head_name zurückgehen"
 
-#: git-rebase.sh:359
+#: git-rebase.sh:392
 #, sh-format
 msgid ""
 "It seems that there is already a $state_dir_base directory, and\n"
@@ -9484,7 +10106,7 @@ msgid ""
 "valuable there."
 msgstr ""
 "Es sieht so aus, als ob es das Verzeichnis $state_dir_base bereits gibt\n"
-"und es könnte ein anderer Neuaufbau im Gange sein. Wenn das der Fall ist,\n"
+"und es könnte ein anderer Rebase im Gange sein. Wenn das der Fall ist,\n"
 "probieren Sie bitte\n"
 "\t$cmd_live_rebase\n"
 "Wenn das nicht der Fall ist, probieren Sie bitte\n"
@@ -9492,59 +10114,68 @@ msgstr ""
 "und führen Sie dieses Kommando nochmal aus. Es wird angehalten, falls noch\n"
 "etwas Schützenswertes vorhanden ist."
 
-#: git-rebase.sh:404
+#: git-rebase.sh:437
 #, sh-format
 msgid "invalid upstream $upstream_name"
-msgstr "ungültiger Übernahmezweig $upstream_name"
+msgstr "ungültiger Upstream-Branch $upstream_name"
 
-#: git-rebase.sh:428
+#: git-rebase.sh:461
 #, sh-format
 msgid "$onto_name: there are more than one merge bases"
-msgstr "$onto_name: es gibt mehr als eine Zusammenführungsbasis"
+msgstr "$onto_name: es gibt mehr als eine Merge-Basis"
 
-#: git-rebase.sh:431 git-rebase.sh:435
+#: git-rebase.sh:464 git-rebase.sh:468
 #, sh-format
 msgid "$onto_name: there is no merge base"
-msgstr "$onto_name: es gibt keine Zusammenführungsbasis"
+msgstr "$onto_name: es gibt keine Merge-Basis"
 
-#: git-rebase.sh:440
+#: git-rebase.sh:473
 #, sh-format
 msgid "Does not point to a valid commit: $onto_name"
-msgstr "$onto_name zeigt auf keine gültige Version"
+msgstr "$onto_name zeigt auf keinen gültigen Commit"
 
-#: git-rebase.sh:463
+#: git-rebase.sh:496
 #, sh-format
 msgid "fatal: no such branch: $branch_name"
-msgstr "fatal: Zweig $branch_name nicht gefunden"
+msgstr "fatal: Branch $branch_name nicht gefunden"
+
+#: git-rebase.sh:519
+msgid "Cannot autostash"
+msgstr "Kann \"autostash\" nicht ausführen."
+
+#: git-rebase.sh:524
+#, sh-format
+msgid "Created autostash: $stash_abbrev"
+msgstr "\"autostash\" erzeugt: $stash_abbrev"
 
-#: git-rebase.sh:483
+#: git-rebase.sh:528
 msgid "Please commit or stash them."
-msgstr "Bitte tragen Sie die Änderungen ein oder benutzen Sie \"stash\"."
+msgstr "Bitte committen Sie die Änderungen oder benutzen Sie \"stash\"."
 
-#: git-rebase.sh:501
+#: git-rebase.sh:548
 #, sh-format
 msgid "Current branch $branch_name is up to date."
-msgstr "Aktueller Zweig $branch_name ist auf dem neusten Stand."
+msgstr "Aktueller Branch $branch_name ist auf dem neusten Stand."
 
-#: git-rebase.sh:504
+#: git-rebase.sh:552
 #, sh-format
 msgid "Current branch $branch_name is up to date, rebase forced."
 msgstr ""
-"Aktueller Zweig $branch_name ist auf dem neusten Stand, Neuaufbau erzwungen."
+"Aktueller Branch $branch_name ist auf dem neusten Stand, Rebase erzwungen."
 
-#: git-rebase.sh:515
+#: git-rebase.sh:563
 #, sh-format
 msgid "Changes from $mb to $onto:"
 msgstr "Änderungen von $mb zu $onto:"
 
 #. Detach HEAD and reset the tree
-#: git-rebase.sh:524
+#: git-rebase.sh:572
 msgid "First, rewinding head to replay your work on top of it..."
 msgstr ""
-"Zunächst wird die Zweigspitze zurückgespult, um Ihre Änderungen\n"
+"Zunächst wird der Branch zurückgespult, um Ihre Änderungen\n"
 "darauf neu anzuwenden..."
 
-#: git-rebase.sh:532
+#: git-rebase.sh:582
 #, sh-format
 msgid "Fast-forwarded $branch_name to $onto_name."
 msgstr "$branch_name zu $onto_name vorgespult."
@@ -9555,15 +10186,15 @@ msgstr "git stash clear mit Parametern ist nicht implementiert"
 
 #: git-stash.sh:74
 msgid "You do not have the initial commit yet"
-msgstr "Sie haben bisher noch keine initiale Version"
+msgstr "Sie haben bisher noch keinen initialen Commit"
 
 #: git-stash.sh:89
 msgid "Cannot save the current index state"
-msgstr "Kann den aktuellen Zustand der Bereitstellung nicht speichern"
+msgstr "Kann den aktuellen Zustand der Staging-Area nicht speichern"
 
 #: git-stash.sh:123 git-stash.sh:136
 msgid "Cannot save the current worktree state"
-msgstr "Kann den aktuellen Zustand des Arbeitsbaumes nicht speichern"
+msgstr "Kann den aktuellen Zustand des Arbeitsverzeichnisses nicht speichern"
 
 #: git-stash.sh:140
 msgid "No changes selected"
@@ -9571,11 +10202,16 @@ msgstr "Keine Änderungen ausgewählt"
 
 #: git-stash.sh:143
 msgid "Cannot remove temporary index (can't happen)"
-msgstr "Kann temporäre Bereitstellung nicht löschen (kann nicht passieren)"
+msgstr "Kann temporäre Staging-Area nicht löschen (kann nicht passieren)"
 
 #: git-stash.sh:156
 msgid "Cannot record working tree state"
-msgstr "Kann Zustand des Arbeitsbaumes nicht aufzeichnen"
+msgstr "Kann Zustand des Arbeitsverzeichnisses nicht aufzeichnen"
+
+#: git-stash.sh:190
+#, sh-format
+msgid "Cannot update $ref_stash with $w_commit"
+msgstr "Kann $ref_stash nicht mit $w_commit aktualisieren."
 
 #. TRANSLATORS: $option is an invalid option, like
 #. `--blah-blah'. The 7 spaces at the beginning of the
@@ -9587,7 +10223,7 @@ msgstr "Kann Zustand des Arbeitsbaumes nicht aufzeichnen"
 #. $ 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:202
+#: git-stash.sh:241
 #, sh-format
 msgid ""
 "error: unknown option for 'stash save': $option\n"
@@ -9597,122 +10233,140 @@ msgstr ""
 "        Um eine Beschreibung anzugeben, benutzen Sie \"git stash save -- "
 "'$option'\""
 
-#: git-stash.sh:223
+#: git-stash.sh:262
 msgid "No local changes to save"
 msgstr "Keine lokalen Änderungen zum Speichern"
 
-#: git-stash.sh:227
+#: git-stash.sh:268
+msgid ""
+"The following untracked files would NOT be saved but need to be removed by "
+"stash save:"
+msgstr ""
+"Die folgenden unbeobachteten Dateien würden NICHT gespeichert werden,\n"
+"müssen aber durch \"stash save\" entfernt werden:"
+
+#: git-stash.sh:270
+msgid ""
+"Aborting. Consider using either the --force or --include-untracked option."
+msgstr ""
+"Abgebrochen. Benutzen Sie entweder die Option --force oder --include-"
+"untracked."
+
+#: git-stash.sh:274
 msgid "Cannot initialize stash"
 msgstr "Kann \"stash\" nicht initialisieren"
 
-#: git-stash.sh:235
+#: git-stash.sh:278
 msgid "Cannot save the current status"
 msgstr "Kann den aktuellen Status nicht speichern"
 
-#: git-stash.sh:253
+#: git-stash.sh:296
 msgid "Cannot remove worktree changes"
-msgstr "Kann Änderungen am Arbeitsbaum nicht löschen"
+msgstr "Kann Änderungen im Arbeitsverzeichnis nicht löschen"
 
-#: git-stash.sh:352
+#: git-stash.sh:395
 msgid "No stash found."
 msgstr "Kein \"stash\" gefunden."
 
-#: git-stash.sh:359
+#: git-stash.sh:402
 #, sh-format
 msgid "Too many revisions specified: $REV"
-msgstr "Zu viele Revisionen angegeben: $REV"
+msgstr "Zu viele Commits angegeben: $REV"
 
-#: git-stash.sh:365
+#: git-stash.sh:408
 #, sh-format
 msgid "$reference is not valid reference"
 msgstr "$reference ist keine gültige Referenz"
 
-#: git-stash.sh:393
+#: git-stash.sh:436
 #, sh-format
 msgid "'$args' is not a stash-like commit"
-msgstr "'$args' ist keine \"stash\"-artige Version"
+msgstr "'$args' ist kein \"stash\"-artiger Commit"
 
-#: git-stash.sh:404
+#: git-stash.sh:447
 #, sh-format
 msgid "'$args' is not a stash reference"
 msgstr "'$args' ist keine \"stash\"-Referenz"
 
-#: git-stash.sh:412
+#: git-stash.sh:455
 msgid "unable to refresh index"
-msgstr "unfähig die Bereitstellung zu aktualisieren"
+msgstr "unfähig die Staging-Area zu aktualisieren"
 
-#: git-stash.sh:416
+#: git-stash.sh:459
 msgid "Cannot apply a stash in the middle of a merge"
-msgstr ""
-"Kann \"stash\" nicht anwenden, solang eine Zusammenführung im Gange ist"
+msgstr "Kann \"stash\" nicht anwenden, solang ein Merge im Gange ist"
 
-#: git-stash.sh:424
+#: git-stash.sh:467
 msgid "Conflicts in index. Try without --index."
-msgstr "Konflikte in der Bereitstellung. Versuchen Sie es ohne --index."
+msgstr "Konflikte in der Staging-Area. Versuchen Sie es ohne --index."
 
-#: git-stash.sh:426
+#: git-stash.sh:469
 msgid "Could not save index tree"
-msgstr "Konnte Bereitstellungsbaum nicht speichern"
+msgstr "Konnte Staging-Area-Verzeichnis nicht speichern"
 
-#: git-stash.sh:460
+#: git-stash.sh:503
 msgid "Cannot unstage modified files"
-msgstr "Kann geänderte Dateien nicht aus der Bereitstellung herausnehmen"
+msgstr "Kann geänderte Dateien nicht aus der Staging-Area entfernen"
 
-#: git-stash.sh:475
+#: git-stash.sh:518
 msgid "Index was not unstashed."
-msgstr "Bereitstellung wurde nicht ausgelagert."
+msgstr "Staging-Area wurde nicht aus dem Stash zurückgeladen."
 
-#: git-stash.sh:492
+#: git-stash.sh:535
 #, sh-format
 msgid "Dropped ${REV} ($s)"
 msgstr "Gelöscht ${REV} ($s)"
 
-#: git-stash.sh:493
+#: git-stash.sh:536
 #, sh-format
 msgid "${REV}: Could not drop stash entry"
 msgstr "${REV}: Konnte \"stash\"-Eintrag nicht löschen"
 
-#: git-stash.sh:500
+#: git-stash.sh:543
 msgid "No branch name specified"
-msgstr "Kein Zweigname spezifiziert"
+msgstr "Kein Branchname spezifiziert"
 
-#: git-stash.sh:571
+#: git-stash.sh:615
 msgid "(To restore them type \"git stash apply\")"
 msgstr "(Zur Wiederherstellung geben Sie \"git stash apply\" ein)"
 
-#: git-submodule.sh:90
+#: git-submodule.sh:95
 #, sh-format
 msgid "cannot strip one component off url '$remoteurl'"
 msgstr "Kann eine Komponente von URL '$remoteurl' nicht extrahieren"
 
-#: git-submodule.sh:195
+#: git-submodule.sh:237
 #, sh-format
 msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
-msgstr ""
-"Keine Unterprojekt-Zuordnung in .gitmodules für Pfad '$sm_path' gefunden"
+msgstr "Keine Submodul-Zuordnung in .gitmodules für Pfad '$sm_path' gefunden"
 
-#: git-submodule.sh:238
+#: git-submodule.sh:281
 #, sh-format
 msgid "Clone of '$url' into submodule path '$sm_path' failed"
-msgstr "Klonen von '$url' in Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+msgstr "Klonen von '$url' in Submodul-Pfad '$sm_path' fehlgeschlagen"
 
-#: git-submodule.sh:250
+#: git-submodule.sh:293
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr "Git-Verzeichnis '$a' ist Teil des Submodul-Pfades '$b', oder umgekehrt"
+
+#: git-submodule.sh:403
+msgid "Relative path can only be used from the toplevel of the working tree"
 msgstr ""
-"Git-Verzeichnis '$a' ist Teil des Unterprojekt-Pfades '$b', oder umgekehrt"
+"Relative Pfade können nur von der obersten Ebene des Arbeitsverzeichnisses "
+"benutzt werden."
 
-#: git-submodule.sh:343
+#: git-submodule.sh:413
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
 msgstr "repo URL: '$repo' muss absolut sein oder mit ./|../ beginnen"
 
-#: git-submodule.sh:360
+#: git-submodule.sh:430
 #, sh-format
 msgid "'$sm_path' already exists in the index"
-msgstr "'$sm_path' existiert bereits in der Bereitstellung"
+msgstr "'$sm_path' ist bereits zum Commit vorgemerkt"
 
-#: git-submodule.sh:364
+#: git-submodule.sh:434
 #, sh-format
 msgid ""
 "The following path is ignored by one of your .gitignore files:\n"
@@ -9723,25 +10377,24 @@ msgstr ""
 "$sm_path\n"
 "Benutzen Sie -f wenn Sie diesen wirklich hinzufügen möchten."
 
-#: git-submodule.sh:382
+#: git-submodule.sh:452
 #, sh-format
 msgid "Adding existing repo at '$sm_path' to the index"
-msgstr ""
-"Füge existierendes Projektarchiv in '$sm_path' der Bereitstellung hinzu."
+msgstr "Füge existierendes Repository in '$sm_path' der Staging-Area hinzu."
 
-#: git-submodule.sh:384
+#: git-submodule.sh:454
 #, sh-format
 msgid "'$sm_path' already exists and is not a valid git repo"
-msgstr "'$sm_path' existiert bereits und ist kein gültiges Git-Projektarchiv"
+msgstr "'$sm_path' existiert bereits und ist kein gültiges Git-Repository"
 
-#: git-submodule.sh:392
+#: git-submodule.sh:462
 #, sh-format
 msgid "A git directory for '$sm_name' is found locally with remote(s):"
 msgstr ""
-"Ein Git-Verzeichnis für '$sm_name' wurde lokal gefunden mit den Fernarchiv"
-"(en):"
+"Ein Git-Verzeichnis für '$sm_name' wurde lokal gefunden mit den Remote-"
+"Repositories:"
 
-#: git-submodule.sh:394
+#: git-submodule.sh:464
 #, sh-format
 msgid ""
 "If you want to reuse this local git directory instead of cloning again from"
@@ -9749,15 +10402,15 @@ msgstr ""
 "Wenn Sie dieses lokale Git-Verzeichnis wiederverwenden möchtest, anstatt "
 "erneut zu klonen"
 
-#: git-submodule.sh:396
+#: git-submodule.sh:466
 #, sh-format
 msgid ""
 "use the '--force' option. If the local git directory is not the correct repo"
 msgstr ""
 "benutzen Sie die Option '--force'. Wenn das lokale Git-Verzeichnis nicht das "
-"korrekte Projektarchiv ist"
+"korrekte Repository ist"
 
-#: git-submodule.sh:397
+#: git-submodule.sh:467
 #, sh-format
 msgid ""
 "or you are unsure what this means choose another name with the '--name' "
@@ -9766,156 +10419,315 @@ msgstr ""
 "oder Sie sich unsicher sind, was das bedeutet, wählen Sie einen anderen "
 "Namenmit der Option '--name'."
 
-#: git-submodule.sh:399
+#: git-submodule.sh:469
 #, sh-format
 msgid "Reactivating local git directory for submodule '$sm_name'."
-msgstr "Reaktiviere lokales Git-Verzeichnis für Unterprojekt '$sm_name'."
+msgstr "Reaktiviere lokales Git-Verzeichnis für Submodul '$sm_name'."
 
-#: git-submodule.sh:411
+#: git-submodule.sh:481
 #, sh-format
 msgid "Unable to checkout submodule '$sm_path'"
-msgstr "Unfähig Unterprojekt '$sm_path' auszuchecken"
+msgstr "Unfähig Submodul '$sm_path' auszuchecken"
 
-#: git-submodule.sh:416
+#: git-submodule.sh:486
 #, sh-format
 msgid "Failed to add submodule '$sm_path'"
-msgstr "Hinzufügen von Unterprojekt '$sm_path' fehlgeschlagen"
+msgstr "Hinzufügen von Submodul '$sm_path' fehlgeschlagen"
 
-#: git-submodule.sh:425
+#: git-submodule.sh:495
 #, sh-format
 msgid "Failed to register submodule '$sm_path'"
-msgstr "Registierung von Unterprojekt '$sm_path' fehlgeschlagen"
+msgstr "Fehler beim Eintragen von Submodul '$sm_path' in die Konfiguration."
+
+#: git-submodule.sh:539
+#, sh-format
+msgid "Entering '$prefix$displaypath'"
+msgstr "Betrete '$prefix$displaypath'"
 
-#: git-submodule.sh:468
+#: git-submodule.sh:554
 #, sh-format
-msgid "Entering '$prefix$sm_path'"
-msgstr "Betrete '$prefix$sm_path'"
+msgid "Stopping at '$prefix$displaypath'; script returned non-zero status."
+msgstr "Stoppe bei '$prefix$displaypath'; Skript gab nicht-Null Status zurück."
 
-#: git-submodule.sh:482
+#: git-submodule.sh:600
 #, sh-format
-msgid "Stopping at '$sm_path'; script returned non-zero status."
-msgstr "Stoppe bei '$sm_path'; Skript gab nicht-Null Status zurück."
+msgid "No url found for submodule path '$displaypath' in .gitmodules"
+msgstr "Keine URL für Submodul-Pfad '$displaypath' in .gitmodules gefunden"
 
-#: git-submodule.sh:526
+#: git-submodule.sh:609
 #, sh-format
-msgid "No url found for submodule path '$sm_path' in .gitmodules"
-msgstr "Keine URL für Unterprojekt-Pfad '$sm_path' in .gitmodules gefunden"
+msgid "Failed to register url for submodule path '$displaypath'"
+msgstr ""
+"Fehler beim Eintragen der URL für Submodul-Pfad '$displaypath' in die "
+"Konfiguration."
 
-#: git-submodule.sh:535
+#: git-submodule.sh:611
 #, sh-format
-msgid "Failed to register url for submodule path '$sm_path'"
-msgstr "Registrierung der URL für Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+msgid "Submodule '$name' ($url) registered for path '$displaypath'"
+msgstr ""
+"Submodul '$name' ($url) für Pfad '$displaypath' in die Konfiguration "
+"eingetragen."
 
-#: git-submodule.sh:537
+#: git-submodule.sh:619
 #, sh-format
-msgid "Submodule '$name' ($url) registered for path '$sm_path'"
-msgstr "Unterprojekt '$name' ($url) ist für Pfad '$sm_path' registriert"
+msgid "Failed to register update mode for submodule path '$displaypath'"
+msgstr ""
+"Fehler bei Änderung des Aktualisierungsmodus für Submodul-Pfad "
+"'$displaypath' in der Konfiguration."
 
-#: git-submodule.sh:545
+#: git-submodule.sh:656
 #, sh-format
-msgid "Failed to register update mode for submodule path '$sm_path'"
+msgid "Use '.' if you really want to deinitialize all submodules"
 msgstr ""
-"Registrierung des Aktualisierungsmodus für Unterprojekt-Pfad '$sm_path' "
-"fehlgeschlagen"
+"Verwenden Sie '.' wenn Sie wirklich alle Submodule\n"
+"deinitialisieren möchten."
 
-#: git-submodule.sh:649
+#: git-submodule.sh:673
+#, sh-format
+msgid "Submodule work tree '$displaypath' contains a .git directory"
+msgstr ""
+"Arbeitsverzeichnis des Submoduls in '$displaypath' enthält ein .git-"
+"Verzeichnis"
+
+#: git-submodule.sh:674
+#, sh-format
+msgid ""
+"(use 'rm -rf' if you really want to remove it including all of its history)"
+msgstr ""
+"(benutzen Sie 'rm -rf' wenn Sie dieses Submodul wirklich mitsamt\n"
+"seiner Historie löschen möchten)"
+
+#: git-submodule.sh:680
+#, sh-format
+msgid ""
+"Submodule work tree '$displaypath' contains local modifications; use '-f' to "
+"discard them"
+msgstr ""
+"Arbeitsverzeichnis von Submodul in '$displaypath' enthält lokale Änderungen; "
+"verwenden Sie '-f' um diese zu verwerfen"
+
+#: git-submodule.sh:683
+#, sh-format
+msgid "Cleared directory '$displaypath'"
+msgstr "Verzeichnis '$displaypath' bereinigt."
+
+#: git-submodule.sh:684
+#, sh-format
+msgid "Could not remove submodule work tree '$displaypath'"
+msgstr ""
+"Konnte Arbeitsverzeichnis des Submoduls in '$displaypath' nicht löschen."
+
+#: git-submodule.sh:687
+#, sh-format
+msgid "Could not create empty submodule directory '$displaypath'"
+msgstr ""
+"Konnte kein leeres Verzeichnis für Submodul in '$displaypath' erstellen."
+
+#: git-submodule.sh:696
+#, sh-format
+msgid "Submodule '$name' ($url) unregistered for path '$displaypath'"
+msgstr ""
+"Submodul '$name' ($url) für Pfad '$displaypath' wurde aus der Konfiguration "
+"entfernt."
+
+#: git-submodule.sh:811
 #, sh-format
 msgid ""
-"Submodule path '$sm_path' not initialized\n"
+"Submodule path '$displaypath' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
-"Unterprojekt-Pfad '$sm_path' ist nicht initialisiert\n"
+"Submodul-Pfad '$displaypath' ist nicht initialisiert.\n"
 "Vielleicht möchten Sie 'update --init' benutzen?"
 
-#: git-submodule.sh:662
+#: git-submodule.sh:824
 #, sh-format
-msgid "Unable to find current revision in submodule path '$sm_path'"
-msgstr "Konnte aktuelle Revision in Unterprojekt-Pfad '$sm_path' nicht finden"
+msgid "Unable to find current revision in submodule path '$displaypath'"
+msgstr "Konnte aktuellen Commit in Submodul-Pfad '$displaypath' nicht finden."
 
-#: git-submodule.sh:671 git-submodule.sh:695
+#: git-submodule.sh:833
 #, sh-format
 msgid "Unable to fetch in submodule path '$sm_path'"
-msgstr "Konnte in Unterprojekt-Pfad '$sm_path' nicht anfordern"
+msgstr "Konnte \"fetch\" in Submodul-Pfad '$sm_path' nicht ausführen"
 
-#: git-submodule.sh:709
+#: git-submodule.sh:857
 #, sh-format
-msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
-msgstr "Neuaufbau von '$sha1' in Unterprojekt-Pfad '$sm_path' nicht möglich"
+msgid "Unable to fetch in submodule path '$displaypath'"
+msgstr "Konnte \"fetch\" in Submodul-Pfad '$displaypath' nicht ausführen"
 
-#: git-submodule.sh:710
+#: git-submodule.sh:871
 #, sh-format
-msgid "Submodule path '$sm_path': rebased into '$sha1'"
-msgstr "Unterprojekt-Pfad '$sm_path': neu aufgebaut in '$sha1'"
+msgid "Unable to rebase '$sha1' in submodule path '$displaypath'"
+msgstr "Rebase auf '$sha1' in Submodul-Pfad '$displaypath' nicht möglich"
 
-#: git-submodule.sh:715
+#: git-submodule.sh:872
 #, sh-format
-msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgid "Submodule path '$displaypath': rebased into '$sha1'"
+msgstr "Submodul-Pfad '$displaypath': Rebase auf '$sha1'"
+
+#: git-submodule.sh:877
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$displaypath'"
+msgstr "Merge von '$sha1' in Submodul-Pfad '$displaypath' fehlgeschlagen"
+
+#: git-submodule.sh:878
+#, sh-format
+msgid "Submodule path '$displaypath': merged in '$sha1'"
+msgstr "Submodul-Pfad '$displaypath': zusammengeführt in '$sha1'"
+
+#: git-submodule.sh:883
+#, sh-format
+msgid ""
+"Execution of '$command $sha1' failed in submodule  path '$prefix$sm_path'"
 msgstr ""
-"Zusammenführung von '$sha1' in Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+"Ausführung von '$command $sha1' in Submodul-Pfad '$prefix$sm_path' "
+"fehlgeschlagen"
 
-#: git-submodule.sh:716
+#: git-submodule.sh:884
 #, sh-format
-msgid "Submodule path '$sm_path': merged in '$sha1'"
-msgstr "Unterprojekt-Pfad '$sm_path': zusammengeführt in '$sha1'"
+msgid "Submodule path '$prefix$sm_path': '$command $sha1'"
+msgstr "Submodul-Pfad '$prefix$sm_path': '$command $sha1'"
 
-#: git-submodule.sh:721
+#: git-submodule.sh:889
 #, sh-format
-msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
-msgstr "Konnte '$sha1' in Unterprojekt-Pfad '$sm_path' nicht auschecken."
+msgid "Unable to checkout '$sha1' in submodule path '$displaypath'"
+msgstr "Konnte '$sha1' in Submodul-Pfad '$displaypath' nicht auschecken."
 
-#: git-submodule.sh:722
+#: git-submodule.sh:890
 #, sh-format
-msgid "Submodule path '$sm_path': checked out '$sha1'"
-msgstr "Unterprojekt-Pfad: '$sm_path': '$sha1' ausgecheckt"
+msgid "Submodule path '$displaypath': checked out '$sha1'"
+msgstr "Submodul-Pfad: '$displaypath': '$sha1' ausgecheckt"
 
-#: git-submodule.sh:744 git-submodule.sh:1066
+#: git-submodule.sh:917
 #, sh-format
-msgid "Failed to recurse into submodule path '$sm_path'"
-msgstr "Fehler bei Rekursion in Unterprojekt-Pfad '$sm_path'"
+msgid "Failed to recurse into submodule path '$displaypath'"
+msgstr "Fehler bei Rekursion in Submodul-Pfad '$displaypath'"
 
-#: git-submodule.sh:852
+#: git-submodule.sh:1025
 msgid "The --cached option cannot be used with the --files option"
 msgstr ""
 "Die Optionen --cached und --files können nicht gemeinsam verwendet werden."
 
 #. unexpected type
-#: git-submodule.sh:892
+#: git-submodule.sh:1066
 #, sh-format
 msgid "unexpected mode $mod_dst"
 msgstr "unerwarteter Modus $mod_dst"
 
-#: git-submodule.sh:910
+#: git-submodule.sh:1086
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_src"
-msgstr "  Warnung: $name beinhaltet nicht Version $sha1_src"
+msgid "  Warn: $display_name doesn't contain commit $sha1_src"
+msgstr "  Warnung: $display_name beinhaltet nicht Commit $sha1_src"
 
-#: git-submodule.sh:913
+#: git-submodule.sh:1089
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_dst"
-msgstr "  Warnung: $name beinhaltet nicht Version $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commit $sha1_dst"
+msgstr "  Warnung: $display_name beinhaltet nicht Commit $sha1_dst"
 
-#: git-submodule.sh:916
+#: git-submodule.sh:1092
 #, sh-format
-msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commits $sha1_src and $sha1_dst"
 msgstr ""
-"  Warnung: $name beinhaltet nicht die Versionen $sha1_src und $sha1_dst"
+"  Warnung: $display_name beinhaltet nicht die Commits $sha1_src und $sha1_dst"
 
-#: git-submodule.sh:941
+#: git-submodule.sh:1117
 msgid "blob"
 msgstr "Blob"
 
-#: git-submodule.sh:979
+#: git-submodule.sh:1155
 msgid "Submodules changed but not updated:"
-msgstr "Unterprojekte geändert, aber nicht aktualisiert:"
+msgstr "Submodule geändert, aber nicht aktualisiert:"
 
-#: git-submodule.sh:981
+#: git-submodule.sh:1157
 msgid "Submodule changes to be committed:"
-msgstr "Änderungen in Unterprojekt zum Eintragen:"
+msgstr "Änderungen in Submodul zum Committen:"
+
+#: git-submodule.sh:1242
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "Fehler bei Rekursion in Submodul-Pfad '$sm_path'"
 
-#: git-submodule.sh:1129
+#: git-submodule.sh:1306
 #, sh-format
-msgid "Synchronizing submodule url for '$prefix$sm_path'"
-msgstr "Synchronisiere Unterprojekt-URL für '$prefix$sm_path'"
+msgid "Synchronizing submodule url for '$displaypath'"
+msgstr "Synchronisiere Submodul-URL für '$displaypath'"
+
+#~ msgid "  (fix conflicts and then run \"git am --resolved\")"
+#~ msgstr ""
+#~ "  (beheben Sie die Konflikte und führen Sie dann \"git am --resolved\" "
+#~ "aus)"
+
+#~ msgid "  (all conflicts fixed: run \"git commit\")"
+#~ msgstr "  (alle Konflikte behoben: führen Sie \"git commit\" aus)"
+
+#~ msgid "more than %d trees given: '%s'"
+#~ msgstr "Mehr als %d \"Tree\"-Objekte angegeben: '%s'"
+
+#~ msgid "You cannot combine --no-ff with --ff-only."
+#~ msgstr "Sie können --no-ff nicht mit --ff--only kombinieren."
+
+#~ msgid ""
+#~ "submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+#~ "(use 'rm -rf' if you really want to remove it including all of its "
+#~ "history)"
+#~ msgstr ""
+#~ "Submodul '%s' (oder ein geschachteltes Submodul hiervon) verwendet\n"
+#~ "ein .git-Verzeichnis (benutzen Sie 'rm -rf' wenn Sie dieses wirklich "
+#~ "mitsamt\n"
+#~ "seiner Historie löschen möchten)"
+
+#~ msgid ""
+#~ "'%s' has changes staged in the index\n"
+#~ "(use --cached to keep the file, or -f to force removal)"
+#~ msgstr ""
+#~ "'%s' hat zum Commit vorgemerkte Änderungen\n"
+#~ "(benutzen Sie --cached um die Datei zu behalten, oder -f um die "
+#~ "Entfernung zu erzwingen)"
+
+#~ msgid "show commits where no parent comes before its children"
+#~ msgstr ""
+#~ "zeigt Commits, bei denen kein Eltern-Commit vor seinem Kind-Commit kommt"
+
+#~ msgid "show the HEAD reference"
+#~ msgstr "zeigt Referenz von HEAD"
+
+#~ msgid "Unable to fetch in submodule path '$prefix$sm_path'"
+#~ msgstr "Konnte \"fetch\" in Submodul-Pfad '$prefix$sm_path' nicht ausführen"
+
+#~ msgid "Failed to recurse into submodule path '$prefix$sm_path'"
+#~ msgstr "Fehler bei Rekursion in Submodul-Pfad '$prefix$sm_path'"
+
+#~ msgid "It took %.2f seconds to enumerate untracked files.  'status -uno'"
+#~ msgstr ""
+#~ "Es dauerte %.2f Sekunden die unbeobachteten Dateien zu bestimmen.'status -"
+#~ "uno'"
+
+#~ msgid "may speed it up, but you have to be careful not to forget to add"
+#~ msgstr "könnte das beschleunigen, aber Sie müssen darauf achten, neue"
+
+#~ msgid "new files yourself (see 'git help status')."
+#~ msgstr "Dateien selbstständig hinzuzufügen (siehe 'git help status')."
+
+#~ msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
+#~ msgstr ""
+#~ "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] "
+#~ "[<Versionsidentifikation>... ]"
+
+#~ msgid "See 'git help <command>' for more information on a specific command."
+#~ msgstr ""
+#~ "Siehe 'git help <Kommando>' für weitere Informationen zu einem "
+#~ "spezifischen Kommando"
+
+#~ msgid "use any ref in .git/refs"
+#~ msgstr "verwendet alle Referenzen in .git/refs"
+
+#~ msgid "use any tag in .git/refs/tags"
+#~ msgstr "verwendet alle Markierungen in .git/refs/tags"
+
+#~ msgid "bad object %s"
+#~ msgstr "ungültiges Objekt %s"
+
+#~ msgid "bogus committer info %s"
+#~ msgstr "unechte Einreicher-Informationen %s"
 
 #~ msgid "can't fdopen 'show' output fd"
 #~ msgstr "konnte Datei-Deskriptor für Ausgabe von 'show' nicht öffnen"
diff --git a/po/fr.po b/po/fr.po
new file mode 100644 (file)
index 0000000..2cdb6f9
--- /dev/null
+++ b/po/fr.po
@@ -0,0 +1,9823 @@
+# French translations for Git.
+# Copyright (C) 2013 Jean-Noël Avila <jn.avila@free.fr>
+# This file is distributed under the same license as the Git package.
+# Jean-Noël Avila <jn.avila@free.fr>, 2013.
+# Sébastien Helleu <flashcode@flashtux.org>, 2013.
+#
+# French translations of common Git words used in this file:
+#
+#   English          |  French
+#   -----------------+---------------------------------
+#   #NN              |  n°NN
+#   a commit         |  un commit
+#   backward         |
+#     compatibility  |  rétrocompatibilité
+#   bare repository  |  dépôt nu
+#   bisect           |  bissection
+#   blob             |  blob
+#   bug              |  bogue
+#   bundle           |  colis
+#   cherry-pick      |  picorer
+#   dangling         |  en suspens
+#   debugging        |  débogage
+#   fast-forward     |  avance rapide
+#   fast-forwarded   |  mis à jour en avance rapide
+#   glob             |  glob
+#   hash             |  hachage
+#   HEAD             |  HEAD (genre féminin)
+#   hook             |  hook
+#   hunk             |  section
+#   merge            |  fusion
+#   pattern          |  motif
+#   repository       |  dépôt
+#   remote           |  distante (ou serveur distant)
+#   revision         |  révision
+#   stash            |  remisage
+#   tag              |  étiquette
+#   template         |  modèle
+#   to checkout      |  extraire
+#   to commit        |  valider
+#   to fetch         |  rapatrier
+#   to prune         |  élaguer
+#   to push          |  pousser
+#   to rebase        |  rebaser
+#   to stash         |  remiser
+#   to track         |  suivre
+#   to unstage       |  désindexer
+#   tree-ish         |  arbre
+#   upstream         |  amont
+#   worktree /       |
+#   work(ing) tree   |  copie de travail
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2013-04-30 08:25+0800\n"
+"PO-Revision-Date: 2013-08-27 19:43+0200\n"
+"Last-Translator: Sébastien Helleu <flashcode@flashtux.org>\n"
+"Language-Team: Jean-Noël Avila <jn.avila@free.fr>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n<=1 ?0 : 1;\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: advice.c:53
+#, c-format
+msgid "hint: %.*s\n"
+msgstr "astuce: %.*s\n"
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:83
+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'."
+msgstr ""
+"Corrigez-les dans la copie de travail,\n"
+"et utilisez 'git add/rm <fichier>' si\n"
+"nécessaire pour marquer la résolution et valider,\n"
+"ou utilisez 'git commit -a'."
+
+#: archive.c:10
+msgid "git archive [options] <tree-ish> [<path>...]"
+msgstr "git archive [options] <arbre> [<chemin>...]"
+
+#: archive.c:11
+msgid "git archive --list"
+msgstr "git archive --list"
+
+#: archive.c:12
+msgid ""
+"git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]"
+msgstr "git archive --remote <dépôt> [--exec <commande>] [options] <arbre> [<chemin>...]"
+
+#: archive.c:13
+msgid "git archive --remote <repo> [--exec <cmd>] --list"
+msgstr "git archive --remote <dépôt> [--exec <commande>] --list"
+
+#: archive.c:323
+msgid "fmt"
+msgstr "fmt"
+
+#: archive.c:323
+msgid "archive format"
+msgstr "format d'archive"
+
+#: archive.c:324 builtin/log.c:1126
+msgid "prefix"
+msgstr "préfixe"
+
+#: archive.c:325
+msgid "prepend prefix to each pathname in the archive"
+msgstr "préfixer chaque chemin de fichier dans l'archive"
+
+#: archive.c:326 builtin/archive.c:88 builtin/blame.c:2371
+#: builtin/blame.c:2372 builtin/config.c:55 builtin/fast-export.c:665
+#: builtin/fast-export.c:667 builtin/grep.c:715 builtin/hash-object.c:77
+#: builtin/ls-files.c:490 builtin/ls-files.c:493 builtin/notes.c:536
+#: builtin/notes.c:693 builtin/read-tree.c:107 parse-options.h:149
+msgid "file"
+msgstr "fichier"
+
+#: archive.c:327 builtin/archive.c:89
+msgid "write the archive to this file"
+msgstr "écrire l'archive dans ce fichier"
+
+#: archive.c:329
+msgid "read .gitattributes in working directory"
+msgstr "lire .gitattributes dans le répertoire de travail"
+
+#: archive.c:330
+msgid "report archived files on stderr"
+msgstr "afficher les fichiers archivés sur stderr"
+
+#: archive.c:331
+msgid "store only"
+msgstr "stockage seulement"
+
+#: archive.c:332
+msgid "compress faster"
+msgstr "compression rapide"
+
+#: archive.c:340
+msgid "compress better"
+msgstr "compression efficace"
+
+#: archive.c:343
+msgid "list supported archive formats"
+msgstr "lister les formats d'archive supportés"
+
+#: archive.c:345 builtin/archive.c:90 builtin/clone.c:86
+msgid "repo"
+msgstr "dépôt"
+
+#: archive.c:346 builtin/archive.c:91
+msgid "retrieve the archive from remote repository <repo>"
+msgstr "récupérer l'archive depuis le dépôt distant <dépôt>"
+
+#: archive.c:347 builtin/archive.c:92 builtin/notes.c:615
+msgid "command"
+msgstr "commande"
+
+#: archive.c:348 builtin/archive.c:93
+msgid "path to the remote git-upload-archive command"
+msgstr "chemin vers la commande distante git-upload-archive"
+
+#: attr.c:259
+msgid ""
+"Negative patterns are ignored in git attributes\n"
+"Use '\\!' for literal leading exclamation."
+msgstr ""
+"Les motifs de négation sont ignorés dans les attributs git\n"
+"Utilisez '\\!' pour un point d'exclamation littéral."
+
+#: branch.c:60
+#, c-format
+msgid "Not setting branch %s as its own upstream."
+msgstr "La branche %s ne peut pas être sa propre branche amont."
+
+#: branch.c:82
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s by rebasing."
+msgstr "La branche %s est paramétrée pour suivre la branche distante %s de %s en rebasant."
+
+#: branch.c:83
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s."
+msgstr "La branche %s est paramétrée pour suivre la branche distante %s depuis %s."
+
+#: branch.c:87
+#, c-format
+msgid "Branch %s set up to track local branch %s by rebasing."
+msgstr "La branche %s est paramétrée pour suivre la branche locale %s en rebasant."
+
+#: branch.c:88
+#, c-format
+msgid "Branch %s set up to track local branch %s."
+msgstr "La branche %s est paramétrée pour suivre la branche locale %s."
+
+#: branch.c:92
+#, c-format
+msgid "Branch %s set up to track remote ref %s by rebasing."
+msgstr "La branche %s est paramétrée pour suivre la référence distante %s en rebasant."
+
+#: branch.c:93
+#, c-format
+msgid "Branch %s set up to track remote ref %s."
+msgstr "La branche %s est paramétrée pour suivre la référence distante %s."
+
+#: branch.c:97
+#, c-format
+msgid "Branch %s set up to track local ref %s by rebasing."
+msgstr "La branche %s est paramétrée pour suivre la référence locale %s en rebasant."
+
+#: branch.c:98
+#, c-format
+msgid "Branch %s set up to track local ref %s."
+msgstr "La branche %s est paramétrée pour suivre la référence locale %s."
+
+#: branch.c:118
+#, c-format
+msgid "Tracking not set up: name too long: %s"
+msgstr "Suivi de branche non paramétré : le nom est trop long : %s"
+
+#: branch.c:137
+#, c-format
+msgid "Not tracking: ambiguous information for ref %s"
+msgstr "Pas de suivi : information ambiguë pour la référence %s"
+
+#: branch.c:182
+#, c-format
+msgid "'%s' is not a valid branch name."
+msgstr "'%s' n'est pas un nom de branche valide."
+
+#: branch.c:187
+#, c-format
+msgid "A branch named '%s' already exists."
+msgstr "Une branche nommée '%s' existe déjà."
+
+#: branch.c:195
+msgid "Cannot force update the current branch."
+msgstr "Impossible de forcer la mise à jour de la branche courante."
+
+#: branch.c:201
+#, c-format
+msgid "Cannot setup tracking information; starting point '%s' is not a branch."
+msgstr "Impossible de paramétrer le suivi de branche ; le point de départ '%s' n'est pas une branche."
+
+#: branch.c:203
+#, c-format
+msgid "the requested upstream branch '%s' does not exist"
+msgstr "la branche amont demandée '%s' n'existe pas"
+
+#: branch.c:205
+msgid ""
+"\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push."
+msgstr ""
+"\n"
+"Si vous comptez baser votre travail sur une branche\n"
+"amont qui existe déjà sur le serveur distant, vous pourriez\n"
+"devoir lancer \"git fetch\" pour la récupérer.\n"
+"\n"
+"Si vous comptez pousser une nouvelle branche locale qui suivra\n"
+"sa jumelle distante, vous souhaiterez utiliser \"git push -u\"\n"
+"pour paramétrer le suivi distant en même temps que vous poussez."
+
+#: branch.c:250
+#, c-format
+msgid "Not a valid object name: '%s'."
+msgstr "Nom d'objet invalide : '%s'."
+
+#: branch.c:270
+#, c-format
+msgid "Ambiguous object name: '%s'."
+msgstr "Nom d'objet ambigu : '%s'."
+
+#: branch.c:275
+#, c-format
+msgid "Not a valid branch point: '%s'."
+msgstr "Point d'embranchement invalide : '%s'."
+
+#: branch.c:281
+msgid "Failed to lock ref for update"
+msgstr "Échec du verrouillage de la référence pour mise à jour"
+
+#: branch.c:299
+msgid "Failed to write ref"
+msgstr "Échec de l'écriture de la référence"
+
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr "'%s' ne semble pas être un fichier bundle v2"
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "en-tête non reconnu : %s%s (%d)"
+
+#: bundle.c:89 builtin/commit.c:676
+#, c-format
+msgid "could not open '%s'"
+msgstr "impossible d'ouvrir '%s'"
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr "Le dépôt ne dispose pas des commits prérequis :"
+
+#: bundle.c:164 sequencer.c:651 sequencer.c:1101 builtin/log.c:300
+#: builtin/log.c:770 builtin/log.c:1344 builtin/log.c:1570 builtin/merge.c:349
+#: builtin/shortlog.c:155
+msgid "revision walk setup failed"
+msgstr "échec de la préparation du parcours des révisions"
+
+#: bundle.c:186
+#, c-format
+msgid "The bundle contains this ref:"
+msgid_plural "The bundle contains these %d refs:"
+msgstr[0] "Le colis contient cette référence :"
+msgstr[1] "Le colis contient ces %d références :"
+
+#: bundle.c:193
+msgid "The bundle records a complete history."
+msgstr "Le colis enregistre l'historique complet."
+
+#: bundle.c:195
+#, c-format
+msgid "The bundle requires this ref:"
+msgid_plural "The bundle requires these %d refs:"
+msgstr[0] "Le colis exige cette référence :"
+msgstr[1] "Le colis exige ces %d références :"
+
+#: bundle.c:294
+msgid "rev-list died"
+msgstr "rev-list a disparu"
+
+#: bundle.c:300 builtin/log.c:1255 builtin/shortlog.c:258
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "argument non reconnu : %s"
+
+#: bundle.c:335
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr "la référence '%s' est exclue par les options de rev-list"
+
+#: bundle.c:380
+msgid "Refusing to create empty bundle."
+msgstr "Refus de créer un colis vide."
+
+#: bundle.c:398
+msgid "Could not spawn pack-objects"
+msgstr "Impossible de créer des objets groupés"
+
+#: bundle.c:416
+msgid "pack-objects died"
+msgstr "les objets groupés ont disparu"
+
+#: bundle.c:419
+#, c-format
+msgid "cannot create '%s'"
+msgstr "impossible de créer '%s'"
+
+#: bundle.c:441
+msgid "index-pack died"
+msgstr "l'index de groupe a disparu"
+
+#: commit.c:50
+#, c-format
+msgid "could not parse %s"
+msgstr "impossible d'analyser %s"
+
+#: commit.c:52
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s n'est pas un commit !"
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr "plus de mémoire"
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr "Impossible de lancer 'git rev-list'"
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr "impossible d'écrire dans la rev-list : %s"
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr "impossible de fermer le stdin du rev-list : %s"
+
+#: date.c:95
+msgid "in the future"
+msgstr "dans le futur"
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] "il y a %lu seconde"
+msgstr[1] "il y a %lu secondes"
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] "il y a %lu minute"
+msgstr[1] "il y a %lu minutes"
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] "il y a %lu heure"
+msgstr[1] "il y a %lu heures"
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] "il y a %lu jour"
+msgstr[1] "il y a %lu jours"
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] "il y a %lu semaine"
+msgstr[1] "il y a %lu semaines"
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] "il y a %lu mois"
+msgstr[1] "il y a %lu mois"
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] "%lu an"
+msgstr[1] "%lu ans"
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] "il y a %s et %lu mois"
+msgstr[1] "il y a %s et %lu mois"
+
+#: date.c:154 date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] "il y a %lu an"
+msgstr[1] "il y a %lu ans"
+
+#: diff.c:112
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%s'\n"
+msgstr "  Impossible d'analyser le pourcentage de modification de dirstat '%s'\n"
+
+#: diff.c:117
+#, c-format
+msgid "  Unknown dirstat parameter '%s'\n"
+msgstr "  Paramètre dirstat inconnu '%s'\n"
+
+#: diff.c:210
+#, c-format
+msgid "Unknown value for 'diff.submodule' config variable: '%s'"
+msgstr "Valeur inconnue pour la variable de configuration 'diff.submodule' : '%s'"
+
+#: diff.c:260
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr "Erreurs dans la variable de configuration 'diff.dirstat' :\n"
+"%s"
+
+#: diff.c:3481
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr "Impossible d'analyser le paramètre de l'option --dirstat/-X :\n"
+"%s"
+
+#: diff.c:3495
+#, c-format
+msgid "Failed to parse --submodule option parameter: '%s'"
+msgstr "Impossible d'analyser le paramètre de l'option --submodule : '%s'"
+
+#: gpg-interface.c:59 gpg-interface.c:131
+msgid "could not run gpg."
+msgstr "impossible de lancer gpg."
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr "gpg n'a pas accepté les données"
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr "gpg n'a pas pu signer les données"
+
+#: gpg-interface.c:115
+#, c-format
+msgid "could not create temporary file '%s': %s"
+msgstr "impossible de créer un fichier temporaire '%s' : %s"
+
+#: gpg-interface.c:118
+#, c-format
+msgid "failed writing detached signature to '%s': %s"
+msgstr "impossible d'écrire la signature détachée dans '%s' : %s"
+
+#: grep.c:1623
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "'%s' : lecture de %s impossible"
+
+#: grep.c:1640
+#, c-format
+msgid "'%s': %s"
+msgstr "'%s' : %s"
+
+#: grep.c:1651
+#, c-format
+msgid "'%s': short read %s"
+msgstr "'%s' : lecture tronquée %s"
+
+#: help.c:212
+#, c-format
+msgid "available git commands in '%s'"
+msgstr "commandes git disponibles dans '%s'"
+
+#: help.c:219
+msgid "git commands available from elsewhere on your $PATH"
+msgstr "commandes git disponibles depuis un autre endroit de votre $PATH"
+
+#: help.c:235
+msgid "The most commonly used git commands are:"
+msgstr "Les commandes git les plus utilisées sont :"
+
+#: help.c:292
+#, c-format
+msgid ""
+"'%s' appears to be a git command, but we were not\n"
+"able to execute it. Maybe git-%s is broken?"
+msgstr ""
+"'%s' semble être une commande git, mais elle n'a pas pu\n"
+"être exécutée. Peut-être git-%s est-elle cassée ?"
+
+#: help.c:349
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr "Ahem. Votre système n'indique aucune commande Git."
+
+#: help.c:371
+#, c-format
+msgid ""
+"WARNING: You called a Git command named '%s', which does not exist.\n"
+"Continuing under the assumption that you meant '%s'"
+msgstr "ATTENTION : vous avez invoqué une commande Git nommée '%s' qui n'existe pas.\n"
+"Poursuite en supposant que vous avez voulu dire '%s'"
+
+#: help.c:376
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr "dans %0.1f secondes automatiquement..."
+
+#: help.c:383
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr "git : '%s' n'est pas une commande git. Voir 'git --help'."
+
+#: help.c:387
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] "\n"
+"Vouliez-vous dire cela ?"
+msgstr[1] "\nVouliez-vous dire un de ceux-là ?"
+
+#: merge.c:56
+msgid "failed to read the cache"
+msgstr "impossible de lire le cache"
+
+#: merge.c:110 builtin/checkout.c:365 builtin/checkout.c:566
+#: builtin/clone.c:645
+msgid "unable to write new index file"
+msgstr "impossible d'écrire le nouveau fichier d'index"
+
+#: merge-recursive.c:190
+#, c-format
+msgid "(bad commit)\n"
+msgstr "(mauvais commit)\n"
+
+#: merge-recursive.c:206
+#, c-format
+msgid "addinfo_cache failed for path '%s'"
+msgstr "échec de addinfo_cache pour le chemin '%s'"
+
+#: merge-recursive.c:268
+msgid "error building trees"
+msgstr "erreur de construction des arbres"
+
+#: merge-recursive.c:672
+#, c-format
+msgid "failed to create path '%s'%s"
+msgstr "impossible de créer le chemin '%s' %s"
+
+#: merge-recursive.c:683
+#, c-format
+msgid "Removing %s to make room for subdirectory\n"
+msgstr "Suppression de %s pour faire de la place pour le sous-répertoire\n"
+
+#. something else exists
+#. .. but not some other error (who really cares what?)
+#: merge-recursive.c:697 merge-recursive.c:718
+msgid ": perhaps a D/F conflict?"
+msgstr ": peut-être un conflit D/F ?"
+
+#: merge-recursive.c:708
+#, c-format
+msgid "refusing to lose untracked file at '%s'"
+msgstr "refus de perdre le fichier non suivi '%s'"
+
+#: merge-recursive.c:748
+#, c-format
+msgid "cannot read object %s '%s'"
+msgstr "impossible de lire l'objet %s '%s'"
+
+#: merge-recursive.c:750
+#, c-format
+msgid "blob expected for %s '%s'"
+msgstr "blob attendu pour %s '%s'"
+
+#: merge-recursive.c:773 builtin/clone.c:313
+#, c-format
+msgid "failed to open '%s'"
+msgstr "échec à l'ouverture de '%s'"
+
+#: merge-recursive.c:781
+#, c-format
+msgid "failed to symlink '%s'"
+msgstr "échec à la création du lien symbolique '%s'"
+
+#: merge-recursive.c:784
+#, c-format
+msgid "do not know what to do with %06o %s '%s'"
+msgstr "ne sait pas traiter %06o %s '%s'"
+
+#: merge-recursive.c:922
+msgid "Failed to execute internal merge"
+msgstr "Échec à l'exécution de la fusion interne"
+
+#: merge-recursive.c:926
+#, c-format
+msgid "Unable to add %s to database"
+msgstr "Impossible d'ajouter %s à la base de données"
+
+#: merge-recursive.c:942
+msgid "unsupported object type in the tree"
+msgstr "type d'objet non supporté dans l'arbre"
+
+#: merge-recursive.c:1021 merge-recursive.c:1035
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
+"in tree."
+msgstr "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans %s. Version %s de %s laissée dans l'arbre."
+
+#: merge-recursive.c:1027 merge-recursive.c:1040
+#, c-format
+msgid ""
+"CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
+"in tree at %s."
+msgstr "CONFLIT (%s/suppression) : %s supprimé dans %s et %s dans %s. Version %s de %s laissée dans l'arbre dans le fichier %s."
+
+#: merge-recursive.c:1081
+msgid "rename"
+msgstr "renommage"
+
+#: merge-recursive.c:1081
+msgid "renamed"
+msgstr "renommé"
+
+#: merge-recursive.c:1137
+#, c-format
+msgid "%s is a directory in %s adding as %s instead"
+msgstr "%s est un répertoire dans %s ajouté plutôt comme %s"
+
+#: merge-recursive.c:1159
+#, c-format
+msgid ""
+"CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
+"\"->\"%s\" in \"%s\"%s"
+msgstr "CONFLIT (renommage/renommage) : Renommage de \"%s\"->\"%s\" dans la branche \"%s\" et renommage \"%s\"->\"%s\" dans \"%s\"%s"
+
+#: merge-recursive.c:1164
+msgid " (left unresolved)"
+msgstr " (laissé non résolu)"
+
+#: merge-recursive.c:1218
+#, c-format
+msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
+msgstr "CONFLIT (renommage/renommage) : renommage '%s'->'%s' dans %s. Renommage '%s'->'%s' dans %s"
+
+#: merge-recursive.c:1248
+#, c-format
+msgid "Renaming %s to %s and %s to %s instead"
+msgstr "Renommage de %s en %s et de %s en %s à la place"
+
+#: merge-recursive.c:1447
+#, c-format
+msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s"
+msgstr "CONFLIT (renommage/ajout) : Renommage de %s->%s dans %s. %s ajouté dans %s"
+
+#: merge-recursive.c:1457
+#, c-format
+msgid "Adding merged %s"
+msgstr "Ajout de %s fusionné"
+
+#: merge-recursive.c:1462 merge-recursive.c:1660
+#, c-format
+msgid "Adding as %s instead"
+msgstr "Ajout plutôt comme %s"
+
+#: merge-recursive.c:1513
+#, c-format
+msgid "cannot read object %s"
+msgstr "impossible de lire l'objet %s"
+
+#: merge-recursive.c:1516
+#, c-format
+msgid "object %s is not a blob"
+msgstr "l'objet %s n'est pas un blob"
+
+#: merge-recursive.c:1564
+msgid "modify"
+msgstr "modification"
+
+#: merge-recursive.c:1564
+msgid "modified"
+msgstr "modifié"
+
+#: merge-recursive.c:1574
+msgid "content"
+msgstr "contenu"
+
+#: merge-recursive.c:1581
+msgid "add/add"
+msgstr "ajout/ajout"
+
+#: merge-recursive.c:1615
+#, c-format
+msgid "Skipped %s (merged same as existing)"
+msgstr "%s ignoré (fusion identique à l'existant)"
+
+#: merge-recursive.c:1629
+#, c-format
+msgid "Auto-merging %s"
+msgstr "Fusion automatique de %s"
+
+#: merge-recursive.c:1633 git-submodule.sh:1029
+msgid "submodule"
+msgstr "sous-module"
+
+#: merge-recursive.c:1634
+#, c-format
+msgid "CONFLICT (%s): Merge conflict in %s"
+msgstr "CONFLIT (%s) : Conflit de fusion dans %s"
+
+#: merge-recursive.c:1724
+#, c-format
+msgid "Removing %s"
+msgstr "Suppression de %s"
+
+#: merge-recursive.c:1749
+msgid "file/directory"
+msgstr "fichier/répertoire"
+
+#: merge-recursive.c:1755
+msgid "directory/file"
+msgstr "répertoire/fichier"
+
+#: merge-recursive.c:1760
+#, c-format
+msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
+msgstr "CONFLIT (%s) : Il y a un répertoire nommé %s dans %s. Ajout de %s comme %s"
+
+#: merge-recursive.c:1770
+#, c-format
+msgid "Adding %s"
+msgstr "Ajout de %s"
+
+#: merge-recursive.c:1787
+msgid "Fatal merge failure, shouldn't happen."
+msgstr "Échec fatal de fusion, qui ne devrait jamais arriver."
+
+#: merge-recursive.c:1806
+msgid "Already up-to-date!"
+msgstr "Déjà à jour !"
+
+#: merge-recursive.c:1815
+#, c-format
+msgid "merging of trees %s and %s failed"
+msgstr "échec de fusion des arbres %s et %s"
+
+#: merge-recursive.c:1845
+#, c-format
+msgid "Unprocessed path??? %s"
+msgstr "Chemin non traité ??? %s"
+
+#: merge-recursive.c:1890
+msgid "Merging:"
+msgstr "Fusion :"
+
+#: merge-recursive.c:1903
+#, c-format
+msgid "found %u common ancestor:"
+msgid_plural "found %u common ancestors:"
+msgstr[0] "%u ancêtre commun trouvé :"
+msgstr[1] "%u ancêtres communs trouvés :"
+
+#: merge-recursive.c:1940
+msgid "merge returned no commit"
+msgstr "la fusion n'a pas retourné de commit"
+
+#: merge-recursive.c:1997
+#, c-format
+msgid "Could not parse object '%s'"
+msgstr "Impossible d'analyser l'objet '%s'"
+
+#: merge-recursive.c:2009 builtin/merge.c:658
+msgid "Unable to write index."
+msgstr "Impossible d'écrire l'index."
+
+#: object.c:195
+#, c-format
+msgid "unable to parse object: %s"
+msgstr "impossible d'analyser l'objet : %s"
+
+#: parse-options.c:489
+msgid "..."
+msgstr "..."
+
+#: parse-options.c:507
+#, c-format
+msgid "usage: %s"
+msgstr "usage : %s"
+
+#. TRANSLATORS: the colon here should align with the
+#. one in "usage: %s" translation
+#: parse-options.c:511
+#, c-format
+msgid "   or: %s"
+msgstr "   ou : %s"
+
+#: parse-options.c:514
+#, c-format
+msgid "    %s"
+msgstr "    %s"
+
+#: parse-options.c:548
+msgid "-NUM"
+msgstr "-NUM"
+
+#: pathspec.c:83
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr "Le chemin '%s' est dans le sous-module '%.*s'"
+
+#: pathspec.c:99
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr "'%s' est au delà d'un lien symbolique"
+
+#: remote.c:1781
+#, 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] "Votre branche est en avance sur '%s' de %d commit.\n"
+msgstr[1] "Votre branche est en avance sur '%s' de %d commits.\n"
+
+#: remote.c:1787
+msgid "  (use \"git push\" to publish your local commits)\n"
+msgstr "  (utilisez \"git push\" pour publier vos commits locaux)\n"
+
+#: remote.c:1790
+#, c-format
+msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
+msgid_plural ""
+"Your branch is behind '%s' by %d commits, and can be fast-forwarded.\n"
+msgstr[0] "Votre branche est en retard sur '%s' de %d commit, et peut être mise à jour en avance rapide.\n"
+msgstr[1] "Votre branche est en retard sur '%s' de %d commits, et peut être mise à jour en avance rapide.\n"
+
+#: remote.c:1798
+msgid "  (use \"git pull\" to update your local branch)\n"
+msgstr "  (utilisez \"git pull\" pour mettre à jour votre branche locale)\n"
+
+#: remote.c:1801
+#, c-format
+msgid ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commit each, respectively.\n"
+msgid_plural ""
+"Your branch and '%s' have diverged,\n"
+"and have %d and %d different commits each, respectively.\n"
+msgstr[0] "Votre branche et '%s' ont divergé,\n"
+"et ont %d et %d commit différent chacune respectivement.\n"
+msgstr[1] "Votre branche et '%s' ont divergé,\n"
+"et ont %d et %d commits différents chacune respectivement.\n"
+
+#: remote.c:1811
+msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
+msgstr "  (utilisez \"git pull\" pour fusionner la branche distante dans la vôtre)\n"
+
+#: sequencer.c:206 builtin/merge.c:776 builtin/merge.c:889 builtin/merge.c:999
+#: builtin/merge.c:1009
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr "Impossible d'ouvrir '%s' en écriture"
+
+#: sequencer.c:208 builtin/merge.c:335 builtin/merge.c:779
+#: builtin/merge.c:1001 builtin/merge.c:1014
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "Impossible d'écrire dans '%s'"
+
+#: sequencer.c:229
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr "après résolution des conflits, marquez les chemins corrigés\n"
+"avec 'git add <chemins>' ou 'git rm <chemins>'"
+
+#: sequencer.c:232
+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 ""
+"après résolution des conflits, marquez les chemins corrigés\n"
+"avec 'git add <chemins>' ou 'git rm <chemins>'\n"
+"puis validez le résultat avec 'git commit'"
+
+#: sequencer.c:245 sequencer.c:859 sequencer.c:942
+#, c-format
+msgid "Could not write to %s"
+msgstr "Impossible d'écrire dans %s"
+
+#: sequencer.c:248
+#, c-format
+msgid "Error wrapping up %s"
+msgstr "Erreur à l'emballage de %s"
+
+#: sequencer.c:263
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr "Vos modifications locales seraient écrasées par cherry-pick."
+
+#: sequencer.c:265
+msgid "Your local changes would be overwritten by revert."
+msgstr "Vos modifications locales seraient écrasées par revert."
+
+#: sequencer.c:268
+msgid "Commit your changes or stash them to proceed."
+msgstr "Validez vos modifications ou remisez-les pour continuer."
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:319
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s: Impossible d'écrire le nouveau fichier index"
+
+#: sequencer.c:350
+msgid "Could not resolve HEAD commit\n"
+msgstr "Impossible de résoudre le commit HEAD\n"
+
+#: sequencer.c:371
+msgid "Unable to update cache tree\n"
+msgstr "Impossible de mettre à jour l'arbre de cache\n"
+
+#: sequencer.c:416
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr "Impossible d'analyser le commit %s\n"
+
+#: sequencer.c:421
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr "Impossible d'analyser le commit parent %s\n"
+
+#: sequencer.c:487
+msgid "Your index file is unmerged."
+msgstr "Votre fichier d'index n'est pas fusionné."
+
+#: sequencer.c:506
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr "Le commit %s est une fusion mais l'option -m n'a pas été spécifiée."
+
+#: sequencer.c:514
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr "Le commit %s n'a pas le parent %d"
+
+#: sequencer.c:518
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr "Une branche principale a été spécifiée mais le commit %s n'est pas une fusion."
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:531
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr "%s : impossible d'analyser le commit parent %s"
+
+#: sequencer.c:535
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr "Impossible d'obtenir un message de validation pour %s"
+
+#: sequencer.c:621
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "impossible d'annuler %s... %s"
+
+#: sequencer.c:622
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "impossible d'appliquer %s... %s"
+
+#: sequencer.c:654
+msgid "empty commit set passed"
+msgstr "l'ensemble de commits spéficifié est vide"
+
+#: sequencer.c:662
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s : échec à la lecture de l'index"
+
+#: sequencer.c:667
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s : échec du raffraîchissement de l'index"
+
+#: sequencer.c:725
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr "Impossible de %s pendant un %s"
+
+#: sequencer.c:747
+#, c-format
+msgid "Could not parse line %d."
+msgstr "Impossible d'analyser la ligne %d."
+
+#: sequencer.c:752
+msgid "No commits parsed."
+msgstr "Aucun commit analysé."
+
+#: sequencer.c:765
+#, c-format
+msgid "Could not open %s"
+msgstr "Impossible d'ouvrir %s"
+
+#: sequencer.c:769
+#, c-format
+msgid "Could not read %s."
+msgstr "Impossible de lire %s."
+
+#: sequencer.c:776
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr "Feuille d'instruction inutilisable : %s"
+
+#: sequencer.c:804
+#, c-format
+msgid "Invalid key: %s"
+msgstr "Clé invalide: %s"
+
+#: sequencer.c:807
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr "Valeur invalide pour %s : %s"
+
+#: sequencer.c:819
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr "Feuille d'options malformée : %s"
+
+#: sequencer.c:840
+msgid "a cherry-pick or revert is already in progress"
+msgstr "un picorage ou un retour est déjà en cours"
+
+#: sequencer.c:841
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr "essayez \"git cherry-pick (--continue|--quit|-- abort)\""
+
+#: sequencer.c:845
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr "Impossible de créer le répertoire de séquenceur %s"
+
+#: sequencer.c:861 sequencer.c:946
+#, c-format
+msgid "Error wrapping up %s."
+msgstr "Erreur lors de l'emballage de %s."
+
+#: sequencer.c:880 sequencer.c:1014
+msgid "no cherry-pick or revert in progress"
+msgstr "aucun picorage ou retour en cours"
+
+#: sequencer.c:882
+msgid "cannot resolve HEAD"
+msgstr "impossible de résoudre HEAD"
+
+#: sequencer.c:884
+msgid "cannot abort from a branch yet to be born"
+msgstr "impossible d'abandonner depuis une branche non encore créée"
+
+#: sequencer.c:906 builtin/apply.c:4060
+#, c-format
+msgid "cannot open %s: %s"
+msgstr "impossible d'ouvrir %s : %s"
+
+#: sequencer.c:909
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "impossible de lire %s : %s"
+
+#: sequencer.c:910
+msgid "unexpected end of file"
+msgstr "fin de fichier inattendue"
+
+#: sequencer.c:916
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr "le fichier HEAD de préparation de picorage '%s' est corrompu"
+
+#: sequencer.c:939
+#, c-format
+msgid "Could not format %s."
+msgstr "Impossible de formater %s."
+
+#: sequencer.c:1083
+#, c-format
+msgid "%s: can't cherry-pick a %s"
+msgstr "%s : impossible de picorer un %s"
+
+#: sequencer.c:1085
+#, c-format
+msgid "%s: bad revision"
+msgstr "%s : mauvaise révision"
+
+#: sequencer.c:1119
+msgid "Can't revert as initial commit"
+msgstr "Impossible d'annuler en tant que commit initial"
+
+#: sequencer.c:1120
+msgid "Can't cherry-pick into empty head"
+msgstr "Impossible de picorer vers une HEAD vide"
+
+#: sha1_name.c:1036
+msgid "HEAD does not point to a branch"
+msgstr "HEAD ne pointe pas sur une branche"
+
+#: sha1_name.c:1039
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "Branche inconnue : '%s'"
+
+#: sha1_name.c:1041
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr "Aucune branche amont configurée pour la branche '%s'"
+
+#: sha1_name.c:1044
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr "La branche amont '%s' n'est pas stockée comme branche de suivi"
+
+#: wrapper.c:408
+#, c-format
+msgid "unable to access '%s': %s"
+msgstr "impossible d'accéder à '%s' : %s"
+
+#: wrapper.c:423
+#, c-format
+msgid "unable to access '%s'"
+msgstr "impossible d'accéder à '%s'"
+
+#: wrapper.c:434
+#, c-format
+msgid "unable to look up current user in the passwd file: %s"
+msgstr "impossible de rechercher l'utilisateur actuel dans le fichier de mots de passe : %s"
+
+#: wrapper.c:435
+msgid "no such user"
+msgstr "utilisateur inconnu"
+
+#: wt-status.c:140
+msgid "Unmerged paths:"
+msgstr "Chemins non fusionnés :"
+
+#: wt-status.c:167 wt-status.c:194
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr "  (utilisez \"git reset %s <fichier>...\" pour désindexer)"
+
+#: wt-status.c:169 wt-status.c:196
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr "  (utilisez \"git rm --cached <fichier>...\" pour désindexer)"
+
+#: wt-status.c:173
+msgid "  (use \"git add <file>...\" to mark resolution)"
+msgstr "  (utilisez \"git add <fichier>...\" pour marquer comme résolu)"
+
+#: wt-status.c:175 wt-status.c:179
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr "  (utilisez \"git add/rm <fichier>...\" si nécessaire pour marquer comme résolu)"
+
+#: wt-status.c:177
+msgid "  (use \"git rm <file>...\" to mark resolution)"
+msgstr "  (utilisez \"git rm <fichier>...\" pour marquer comme résolu)"
+
+#: wt-status.c:188
+msgid "Changes to be committed:"
+msgstr "Modifications qui seront validées :"
+
+#: wt-status.c:206
+msgid "Changes not staged for commit:"
+msgstr "Modifications qui ne seront pas validées :"
+
+#: wt-status.c:210
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr "  (utilisez \"git add <fichier>...\" pour mettre à jour ce qui sera validé)"
+
+#: wt-status.c:212
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr "  (utilisez \"git add/rm <fichier>...\" pour mettre à jour ce qui sera validé)"
+
+#: wt-status.c:213
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr "  (utilisez \"git checkout -- <fichier>...\" pour annuler les modifications dans la copie de travail)"
+
+#: wt-status.c:215
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr "  (valider ou annuler le contenu non suivi ou modifié dans les sous-modules)"
+
+#: wt-status.c:227
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr "  (utilisez \"git %s <fichier>...\" pour inclure dans ce qui sera validé)"
+
+#: wt-status.c:244
+msgid "bug"
+msgstr "bogue"
+
+#: wt-status.c:249
+msgid "both deleted:"
+msgstr "supprimé des deux côtés :"
+
+#: wt-status.c:250
+msgid "added by us:"
+msgstr "ajouté par nous :"
+
+#: wt-status.c:251
+msgid "deleted by them:"
+msgstr "supprimé par eux :"
+
+#: wt-status.c:252
+msgid "added by them:"
+msgstr "ajouté par eux :"
+
+#: wt-status.c:253
+msgid "deleted by us:"
+msgstr "supprimé par nous :"
+
+#: wt-status.c:254
+msgid "both added:"
+msgstr "ajouté de deux côtés :"
+
+#: wt-status.c:255
+msgid "both modified:"
+msgstr "modifié des deux côtés :"
+
+#: wt-status.c:285
+msgid "new commits, "
+msgstr "nouveaux commits, "
+
+#: wt-status.c:287
+msgid "modified content, "
+msgstr "contenu modifié, "
+
+#: wt-status.c:289
+msgid "untracked content, "
+msgstr "contenu non suivi, "
+
+#: wt-status.c:306
+#, c-format
+msgid "new file:   %s"
+msgstr "nouveau :   %s"
+
+#: wt-status.c:309
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr "copié :     %s -> %s"
+
+#: wt-status.c:312
+#, c-format
+msgid "deleted:    %s"
+msgstr "supprimé :  %s"
+
+#: wt-status.c:315
+#, c-format
+msgid "modified:   %s"
+msgstr "modifié :   %s"
+
+#: wt-status.c:318
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr "renommé :   %s -> %s"
+
+#: wt-status.c:321
+#, c-format
+msgid "typechange: %s"
+msgstr "nv type :   %s"
+
+#: wt-status.c:324
+#, c-format
+msgid "unknown:    %s"
+msgstr "inconnu :   %s"
+
+#: wt-status.c:327
+#, c-format
+msgid "unmerged:   %s"
+msgstr "non fus. :  %s"
+
+#: wt-status.c:330
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr "bogue : état de diff non géré %c"
+
+#: wt-status.c:803
+msgid "You have unmerged paths."
+msgstr "Vous avez des chemins non fusionnés."
+
+#: wt-status.c:806 wt-status.c:958
+msgid "  (fix conflicts and run \"git commit\")"
+msgstr "  (réglez les conflits et lancez \"git commit\")"
+
+#: wt-status.c:809
+msgid "All conflicts fixed but you are still merging."
+msgstr "Tous les conflits sont réglés mais la fusion n'est pas terminée."
+
+#: wt-status.c:812
+msgid "  (use \"git commit\" to conclude merge)"
+msgstr "  (utilisez \"git commit\" pour terminer la fusion)"
+
+#: wt-status.c:822
+msgid "You are in the middle of an am session."
+msgstr "Vous êtes au milieu d'une session am."
+
+#: wt-status.c:825
+msgid "The current patch is empty."
+msgstr "Le patch actuel est vide."
+
+#: wt-status.c:829
+msgid "  (fix conflicts and then run \"git am --resolved\")"
+msgstr "  (réglez les conflits puis lancez \"git am --resolved\")"
+
+#: wt-status.c:831
+msgid "  (use \"git am --skip\" to skip this patch)"
+msgstr "  (utilisez \"git am --skip\" pour sauter ce patch)"
+
+#: wt-status.c:833
+msgid "  (use \"git am --abort\" to restore the original branch)"
+msgstr "  (utilisez \"git am --abort\" pour restaurer la branche d'origine)"
+
+#: wt-status.c:893 wt-status.c:910
+#, c-format
+msgid "You are currently rebasing branch '%s' on '%s'."
+msgstr "Vous êtes en train de rebaser la branche '%s' sur '%s'."
+
+#: wt-status.c:898 wt-status.c:915
+msgid "You are currently rebasing."
+msgstr "Vous êtes en train de rebaser."
+
+#: wt-status.c:901
+msgid "  (fix conflicts and then run \"git rebase --continue\")"
+msgstr "  (résolvez les conflits puis lancez \"git rebase --continue\")"
+
+#: wt-status.c:903
+msgid "  (use \"git rebase --skip\" to skip this patch)"
+msgstr "  (utilisez \"git rebase --skip\" pour sauter ce patch)"
+
+#: wt-status.c:905
+msgid "  (use \"git rebase --abort\" to check out the original branch)"
+msgstr "  (utilisez \"git rebase --abort\" pour extraire la branche d'origine)"
+
+#: wt-status.c:918
+msgid "  (all conflicts fixed: run \"git rebase --continue\")"
+msgstr "  (tous les conflits sont résolus : lancez \"git rebase --continue\")"
+
+#: wt-status.c:922
+#, c-format
+msgid ""
+"You are currently splitting a commit while rebasing branch '%s' on '%s'."
+msgstr "Vous êtes actuellement en train de fractionner un commit pendant un rebasage de la branche '%s' sur '%s'."
+
+#: wt-status.c:927
+msgid "You are currently splitting a commit during a rebase."
+msgstr "Vous êtes actuellement en train de fractionner un commit pendant un rebasage."
+
+#: wt-status.c:930
+msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
+msgstr "  (Une fois la copie de travail nettoyée, lancez \"git rebase --continue\")"
+
+#: wt-status.c:934
+#, c-format
+msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
+msgstr "Vous êtes actuellement en train d'éditer un commit pendant un rebasage de la branche '%s' sur '%s'."
+
+#: wt-status.c:939
+msgid "You are currently editing a commit during a rebase."
+msgstr "Vous êtes actuellement en train d'éditer un commit pendant un rebasage."
+
+#: wt-status.c:942
+msgid "  (use \"git commit --amend\" to amend the current commit)"
+msgstr "  (utilisez \"git commit --amend\" pour corriger le commit actuel)"
+
+#: wt-status.c:944
+msgid ""
+"  (use \"git rebase --continue\" once you are satisfied with your changes)"
+msgstr "  (utilisez \"git rebase --continue\" quand vous êtes satisfait de vos modifications)"
+
+#: wt-status.c:954
+msgid "You are currently cherry-picking."
+msgstr "Vous êtes actuellement en train de picorer."
+
+#: wt-status.c:961
+msgid "  (all conflicts fixed: run \"git commit\")"
+msgstr "  (tous les conflits sont résolus : lancez \"git commit\")"
+
+#: wt-status.c:970
+#, c-format
+msgid "You are currently reverting commit %s."
+msgstr "Vous êtes actuellement en train de rétablir le commit %s."
+
+#: wt-status.c:975
+msgid "  (fix conflicts and run \"git revert --continue\")"
+msgstr "  (résolvez les conflits et lancez \"git revert --continue\")"
+
+#: wt-status.c:978
+msgid "  (all conflicts fixed: run \"git revert --continue\")"
+msgstr "  (tous les conflits sont résolus : lancez \"git revert --continue\")"
+
+#: wt-status.c:980
+msgid "  (use \"git revert --abort\" to cancel the revert operation)"
+msgstr "  (utilisez \"git revert --abort\" pour annuler le rétablissement)"
+
+#: wt-status.c:991
+#, c-format
+msgid "You are currently bisecting, started from branch '%s'."
+msgstr "Vous êtes en cours de bissection, depuis la branche '%s'."
+
+#: wt-status.c:995
+msgid "You are currently bisecting."
+msgstr "Vous êtes en cours de bissection."
+
+#: wt-status.c:998
+msgid "  (use \"git bisect reset\" to get back to the original branch)"
+msgstr "  (utilisez \"git bisect reset\" pour revenir à la branche d'origine)"
+
+#: wt-status.c:1173
+msgid "On branch "
+msgstr "Sur la branche "
+
+#: wt-status.c:1184
+msgid "HEAD detached at "
+msgstr "HEAD détachée sur "
+
+#: wt-status.c:1186
+msgid "HEAD detached from "
+msgstr "HEAD détachée depuis "
+
+#: wt-status.c:1189
+msgid "Not currently on any branch."
+msgstr "Actuellement sur aucun branche."
+
+#: wt-status.c:1206
+msgid "Initial commit"
+msgstr "Validation initiale"
+
+#: wt-status.c:1220
+msgid "Untracked files"
+msgstr "Fichiers non suivis"
+
+#: wt-status.c:1222
+msgid "Ignored files"
+msgstr "Fichiers ignorés"
+
+#: wt-status.c:1226
+#, c-format
+msgid ""
+"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
+"may speed it up, but you have to be careful not to forget to add\n"
+"new files yourself (see 'git help status')."
+msgstr "L'énumération des fichiers non suivis a duré %.2f secondes. 'status -uno'\n"
+"peut l'accélerer, mais vous devez alors faire attention à ne pas\n"
+"oublier d'ajouter les nouveaux fichiers par vous-même (voir 'git help status')."
+
+#: wt-status.c:1232
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr "Fichiers non suivis non listés%s"
+
+#: wt-status.c:1234
+msgid " (use -u option to show untracked files)"
+msgstr " (utilisez -u pour afficher les fichiers non suivis)"
+
+#: wt-status.c:1240
+msgid "No changes"
+msgstr "Aucune modification"
+
+#: wt-status.c:1245
+#, c-format
+msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
+msgstr "aucune modification n'a été ajoutée à la validation (utilisez \"git add\" ou \"git commit -a\")\n"
+
+#: wt-status.c:1248
+#, c-format
+msgid "no changes added to commit\n"
+msgstr "aucune modification ajoutée à la validation\n"
+
+#: wt-status.c:1251
+#, c-format
+msgid ""
+"nothing added to commit but untracked files present (use \"git add\" to "
+"track)\n"
+msgstr "aucune modification ajoutée à la validation mais des fichiers non suivis sont présents (utilisez \"git add\" pour les suivre)\n"
+
+#: wt-status.c:1254
+#, c-format
+msgid "nothing added to commit but untracked files present\n"
+msgstr "aucune modification ajoutée à la validation mais des fichiers non suivis sont présents\n"
+
+#: wt-status.c:1257
+#, c-format
+msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
+msgstr "rien à valider (créez/copiez des fichiers et utilisez \"git add\" pour les suivre)\n"
+
+#: wt-status.c:1260 wt-status.c:1265
+#, c-format
+msgid "nothing to commit\n"
+msgstr "rien à valider\n"
+
+#: wt-status.c:1263
+#, c-format
+msgid "nothing to commit (use -u to show untracked files)\n"
+msgstr "rien à valider (utilisez -u pour afficher les fichiers non suivis)\n"
+
+#: wt-status.c:1267
+#, c-format
+msgid "nothing to commit, working directory clean\n"
+msgstr "rien à valider, la copie de travail est propre\n"
+
+#: wt-status.c:1375
+msgid "HEAD (no branch)"
+msgstr "HEAD (aucune branche)"
+
+#: wt-status.c:1381
+msgid "Initial commit on "
+msgstr "Validation initiale sur "
+
+#: wt-status.c:1396
+msgid "behind "
+msgstr "derrière "
+
+#: wt-status.c:1399 wt-status.c:1402
+msgid "ahead "
+msgstr "devant "
+
+#: wt-status.c:1404
+msgid ", behind "
+msgstr ", derrière "
+
+#: compat/precompose_utf8.c:58 builtin/clone.c:352
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "echec lors de l'unlink de '%s'"
+
+#: builtin/add.c:20
+msgid "git add [options] [--] <pathspec>..."
+msgstr "git add [options] [--] <chemin>..."
+
+#.
+#. * To be consistent with "git add -p" and most Git
+#. * commands, we should default to being tree-wide, but
+#. * this is not the original behavior and can't be
+#. * changed until users trained themselves not to type
+#. * "git add -u" or "git add -A". For now, we warn and
+#. * keep the old behavior. Later, the behavior can be changed
+#. * to tree-wide, keeping the warning for a while, and
+#. * eventually we can drop the warning.
+#.
+#: builtin/add.c:58
+#, c-format
+msgid ""
+"The behavior of 'git add %s (or %s)' with no path argument from a\n"
+"subdirectory of the tree will change in Git 2.0 and should not be used "
+"anymore.\n"
+"To add content for the whole tree, run:\n"
+"\n"
+"  git add %s :/\n"
+"  (or git add %s :/)\n"
+"\n"
+"To restrict the command to the current directory, run:\n"
+"\n"
+"  git add %s .\n"
+"  (or git add %s .)\n"
+"\n"
+"With the current Git version, the command is restricted to the current "
+"directory.\n"
+msgstr ""
+"Le comportement de 'git add %s (ou %s)' sans argument de chemin depuis un\n"
+"sous-répertoire du projet va changer dans Git 2.0 et ne doit plus être utilisé.\n"
+"Pour ajouter le contenu de toute l'arborescence, lancez :\n"
+"\n"
+"  git add %s :/\n"
+"  (ou git add %s :/)\n"
+"\n"
+"Pour restreindre la commande au répertoire courant, lancez :\n"
+"\n"
+"  git add %s .\n"
+"  (ou git add %s .)\n"
+"\n"
+"Avec la version actuelle de Git, la commande est restreinte au répertoire courant.\n"
+
+#: builtin/add.c:100
+#, c-format
+msgid ""
+"You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
+"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
+"Paths like '%s' that are\n"
+"removed from your working tree are ignored with this version of Git.\n"
+"\n"
+"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
+"  ignores paths you removed from your working tree.\n"
+"\n"
+"* 'git add --all <pathspec>' will let you also record the removals.\n"
+"\n"
+"Run 'git status' to check the paths you removed from your working tree.\n"
+msgstr ""
+"Vous avez lancé 'git add' sans '-A (--all)' ni '--ignore-removal',\n"
+"dont le comportement va changer dans Git 2.0 avec le respect des chemins que vous supprimez.\n"
+"Les chemins tels que '%s' qui ont été\n"
+"retirés de votre copie de travail sont ignorés avec cette version de Git.\n"
+"\n"
+"* 'git add --ignore-removal <chemin>', qui est l'option par défaut actuelle,\n"
+"  ignore les chemins que vous avez supprimés de votre copie de travail.\n"
+"\n"
+"* 'git add --all <chemin>' permet d'enregistrer aussi les suppressions.\n"
+"\n"
+"Lancez 'git status' pour vérifier les chemins que vous avez supprimés de votre copie de travail.\n"
+
+#: builtin/add.c:144
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "status de diff inattendu %c"
+
+#: builtin/add.c:149 builtin/commit.c:233
+msgid "updating files failed"
+msgstr "échec de la mise à jour des fichiers"
+
+#: builtin/add.c:163
+#, c-format
+msgid "remove '%s'\n"
+msgstr "suppression de '%s'\n"
+
+#: builtin/add.c:253
+msgid "Unstaged changes after refreshing the index:"
+msgstr "Modifications non indexées après rafraîchissement de l'index :"
+
+#: builtin/add.c:256 builtin/add.c:572 builtin/rm.c:275
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr "le chemin '%s' ne correspond à aucun fichier"
+
+#: builtin/add.c:339
+msgid "Could not read the index"
+msgstr "Impossible de lire l'index"
+
+#: builtin/add.c:349
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr "Impossible d'ouvrir '%s' en écriture."
+
+#: builtin/add.c:353
+msgid "Could not write patch"
+msgstr "Impossible d'écrire le patch"
+
+#: builtin/add.c:358
+#, c-format
+msgid "Could not stat '%s'"
+msgstr "Stat de '%s' impossible"
+
+#: builtin/add.c:360
+msgid "Empty patch. Aborted."
+msgstr "Patch vide. Abandon."
+
+#: builtin/add.c:366
+#, c-format
+msgid "Could not apply '%s'"
+msgstr "Impossible d'appliquer '%s'"
+
+#: builtin/add.c:376
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr "Les chemins suivants sont ignorés par un de vos fichiers .gitignore :\n"
+
+#: builtin/add.c:393 builtin/clean.c:161 builtin/fetch.c:78 builtin/mv.c:63
+#: builtin/prune-packed.c:76 builtin/push.c:425 builtin/remote.c:1253
+#: builtin/rm.c:206
+msgid "dry run"
+msgstr "à vide"
+
+#: builtin/add.c:394 builtin/apply.c:4409 builtin/check-ignore.c:19
+#: builtin/commit.c:1152 builtin/count-objects.c:95 builtin/fsck.c:613
+#: builtin/log.c:1518 builtin/mv.c:62 builtin/read-tree.c:112
+msgid "be verbose"
+msgstr "mode verbeux"
+
+#: builtin/add.c:396
+msgid "interactive picking"
+msgstr "sélection interactive"
+
+#: builtin/add.c:397 builtin/checkout.c:1063 builtin/reset.c:258
+msgid "select hunks interactively"
+msgstr "sélection interactive des sections"
+
+#: builtin/add.c:398
+msgid "edit current diff and apply"
+msgstr "édition du diff actuel et application"
+
+#: builtin/add.c:399
+msgid "allow adding otherwise ignored files"
+msgstr "permettre l'ajout de fichiers ignorés"
+
+#: builtin/add.c:400
+msgid "update tracked files"
+msgstr "mettre à jour les fichiers suivis"
+
+#: builtin/add.c:401
+msgid "record only the fact that the path will be added later"
+msgstr "enregistrer seulement le fait que le chemin sera ajouté plus tard"
+
+#: builtin/add.c:402
+msgid "add changes from all tracked and untracked files"
+msgstr "ajouter les modifications de tous les fichiers suivis et non suivis"
+
+#. takes no arguments
+#: builtin/add.c:405
+msgid "ignore paths removed in the working tree (same as --no-all)"
+msgstr "ignorer les chemins effacés dans la copie de travail (identique à --no-all)"
+
+#: builtin/add.c:407
+msgid "don't add, only refresh the index"
+msgstr "ne pas ajouter, juste rafraîchir l'index"
+
+#: builtin/add.c:408
+msgid "just skip files which cannot be added because of errors"
+msgstr "sauter seulement les fichiers qui ne peuvent pas être ajoutés du fait d'erreurs"
+
+#: builtin/add.c:409
+msgid "check if - even missing - files are ignored in dry run"
+msgstr "vérifier si des fichiers - même manquants - sont ignorés, à vide"
+
+#: builtin/add.c:431
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr "Utilisez -f si vous voulez réellement les ajouter.\n"
+
+#: builtin/add.c:432
+msgid "no files added"
+msgstr "aucun fichier ajouté"
+
+#: builtin/add.c:438
+msgid "adding files failed"
+msgstr "échec de l'ajout de fichiers"
+
+#: builtin/add.c:477
+msgid "-A and -u are mutually incompatible"
+msgstr "-A et -u sont mutuellement incompatibles"
+
+#: builtin/add.c:495
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr "L'option --ignore-missing ne peut être utilisée qu'en complément de --dry-run"
+
+#: builtin/add.c:525
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr "Rien de spécifié, rien n'a été ajouté.\n"
+
+#: builtin/add.c:526
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr "Vous vouliez sûrement dire 'git add .' ?\n"
+
+#: builtin/add.c:532 builtin/check-ignore.c:66 builtin/clean.c:204
+#: builtin/commit.c:293 builtin/mv.c:82 builtin/rm.c:235
+msgid "index file corrupt"
+msgstr "fichier d'index corrompu"
+
+#: builtin/add.c:604 builtin/apply.c:4505 builtin/mv.c:229 builtin/rm.c:370
+msgid "Unable to write new index file"
+msgstr "Impossible d'écrire le nouveau fichier d'index"
+
+#: builtin/apply.c:57
+msgid "git apply [options] [<patch>...]"
+msgstr "git apply [options] [<patch>...]"
+
+#: builtin/apply.c:110
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "option d'espace non reconnue '%s'"
+
+#: builtin/apply.c:125
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr "option d'ignorance d'espce non reconnue '%s'"
+
+#: builtin/apply.c:823
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr "Impossible de préparer la regexp d'horodatage %s"
+
+#: builtin/apply.c:832
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr "regexec a retourné %d pour l'entrée : %s"
+
+#: builtin/apply.c:913
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr "nom de fichier du patch introuvable à la ligne %d"
+
+#: builtin/apply.c:945
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr "git apply : mauvais format de git-diff - /dev/null attendu, %s trouvé à la ligne %d"
+
+#: builtin/apply.c:949
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
+msgstr "git apply : mauvais format de git-diff - nouveau nom de fichier inconsistant à la ligne %d"
+
+#: builtin/apply.c:950
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
+msgstr "git apply : mauvais format de git-diff - ancien nom de fichier inconsistant à la ligne %d"
+
+#: builtin/apply.c:957
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr "git apply : mauvais format de git-diff - /dev/null attendu à la ligne %d"
+
+#: builtin/apply.c:1422
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr "recount : ligne inattendue : %.*s"
+
+#: builtin/apply.c:1479
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr "fragment de patch sans en-tête à la ligne %d : %.*s"
+
+#: builtin/apply.c:1496
+#, c-format
+msgid ""
+"git diff header lacks filename information when removing %d leading pathname "
+"component (line %d)"
+msgid_plural ""
+"git diff header lacks filename information when removing %d leading pathname "
+"components (line %d)"
+msgstr[0] "information de nom de fichier manquante dans l'en-tête de git diff lors de la suppression de %d composant de préfixe de chemin (ligne %d)"
+msgstr[1] "information de nom de fichier manquante dans l'en-tête de git diff lors de la suppression de %d composants de préfixe de chemin (ligne %d)"
+
+#: builtin/apply.c:1656
+msgid "new file depends on old contents"
+msgstr "le nouveau fichier dépend de contenus anciens"
+
+#: builtin/apply.c:1658
+msgid "deleted file still has contents"
+msgstr "le fichier supprimé a encore du contenu"
+
+#: builtin/apply.c:1684
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr "patch corrompu à la ligne %d"
+
+#: builtin/apply.c:1720
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr "le nouveau fichier %s dépend de contenus anciens"
+
+#: builtin/apply.c:1722
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr "le fichier supprimé %s a encore du contenu"
+
+#: builtin/apply.c:1725
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr "** attention : le fichier %s devient vide mais n'est pas supprimé"
+
+#: builtin/apply.c:1871
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr "patch binaire corrompu à la ligne %d : %.*s"
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1900
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr "patch binaire non reconnu à la ligne %d"
+
+#: builtin/apply.c:1986
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr "patch totalement incompréhensible à la ligne %d"
+
+#: builtin/apply.c:2076
+#, c-format
+msgid "unable to read symlink %s"
+msgstr "lecture du lien symbolique %s impossible"
+
+#: builtin/apply.c:2080
+#, c-format
+msgid "unable to open or read %s"
+msgstr "ouverture ou lecture de %s impossible"
+
+#: builtin/apply.c:2688
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "début de ligne invalide : '%c'"
+
+#: builtin/apply.c:2806
+#, c-format
+msgid "Hunk #%d succeeded at %d (offset %d line)."
+msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
+msgstr[0] "La section n°%d a réussi à la ligne %d (offset %d ligne)."
+msgstr[1] "La section n°%d a réussi à la ligne %d (offset %d lignes)."
+
+#: builtin/apply.c:2818
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr "Contexte réduit à (%ld/%ld) pour appliquer le fragment à la ligne %d"
+
+#: builtin/apply.c:2824
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr "pendant la recherche de :\n"
+"%.*s"
+
+#: builtin/apply.c:2843
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr "données de patch binaire manquantes pour '%s'"
+
+#: builtin/apply.c:2946
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr "le patch binaire ne s'applique par correctement à '%s'"
+
+#: builtin/apply.c:2952
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr "le patch binaire sur '%s' crée un résultat incorrect (%s attendu, mais %s trouvé)"
+
+#: builtin/apply.c:2973
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr "le patch a échoué : %s:%ld"
+
+#: builtin/apply.c:3095
+#, c-format
+msgid "cannot checkout %s"
+msgstr "extraction de %s impossible"
+
+#: builtin/apply.c:3140 builtin/apply.c:3149 builtin/apply.c:3193
+#, c-format
+msgid "read of %s failed"
+msgstr "echec de la lecture de %s"
+
+#: builtin/apply.c:3173 builtin/apply.c:3395
+#, c-format
+msgid "path %s has been renamed/deleted"
+msgstr "le chemin %s a été renommé/supprimé"
+
+#: builtin/apply.c:3254 builtin/apply.c:3409
+#, c-format
+msgid "%s: does not exist in index"
+msgstr "%s : n'existe pas dans l'index"
+
+#: builtin/apply.c:3258 builtin/apply.c:3401 builtin/apply.c:3423
+#, c-format
+msgid "%s: %s"
+msgstr "%s : %s"
+
+#: builtin/apply.c:3263 builtin/apply.c:3417
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s : ne correspond pas à l'index"
+
+#: builtin/apply.c:3365
+msgid "removal patch leaves file contents"
+msgstr "le patch de suppression laisse un contenu dans le fichier"
+
+#: builtin/apply.c:3434
+#, c-format
+msgid "%s: wrong type"
+msgstr "%s : type erroné"
+
+#: builtin/apply.c:3436
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr "%s est de type %o, mais %o attendu"
+
+#: builtin/apply.c:3537
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s : existe déjà dans l'index"
+
+#: builtin/apply.c:3540
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s : existe déjà dans la copie de travail"
+
+#: builtin/apply.c:3560
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)"
+msgstr "le nouveau mode (%o) de %s ne correspond pas à l'ancien mode (%o)"
+
+#: builtin/apply.c:3565
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o) of %s"
+msgstr "le nouveau mode (%o) de %s ne correspond pas à l'ancien mode (%o) de %s"
+
+#: builtin/apply.c:3573
+#, c-format
+msgid "%s: patch does not apply"
+msgstr "%s : le patch ne s'applique pas"
+
+#: builtin/apply.c:3586
+#, c-format
+msgid "Checking patch %s..."
+msgstr "Vérification du patch %s..."
+
+#: builtin/apply.c:3679 builtin/checkout.c:216 builtin/reset.c:124
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "echec de make_cache_entry pour le chemin '%s'"
+
+#: builtin/apply.c:3822
+#, c-format
+msgid "unable to remove %s from index"
+msgstr "suppression de %s dans l'index impossible"
+
+#: builtin/apply.c:3850
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr "patch corrompu pour le sous-projet %s"
+
+#: builtin/apply.c:3854
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "stat du fichier nouvellement créé '%s' impossible"
+
+#: builtin/apply.c:3859
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr "création du magasin de stockage pour le fichier nouvellement créé %s impossible"
+
+#: builtin/apply.c:3862 builtin/apply.c:3970
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr "ajout de l'entrée de cache %s impossible"
+
+#: builtin/apply.c:3895
+#, c-format
+msgid "closing file '%s'"
+msgstr "fermeture du fichier '%s'"
+
+#: builtin/apply.c:3944
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr "écriture du fichier '%s' mode %o impossible"
+
+#: builtin/apply.c:4031
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr "Patch %s appliqué proprement."
+
+#: builtin/apply.c:4039
+msgid "internal error"
+msgstr "erreur interne"
+
+#. Say this even without --verbose
+#: builtin/apply.c:4042
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] "Application du patch %%s avec %d rejet..."
+msgstr[1] "Application du patch %%s avec %d rejets..."
+
+#: builtin/apply.c:4052
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr "troncature du nom de fichier .rej en %.*s.rej"
+
+#: builtin/apply.c:4073
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr "Section n°%d appliquée proprement."
+
+#: builtin/apply.c:4076
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr "Section n°%d rejetée."
+
+#: builtin/apply.c:4226
+msgid "unrecognized input"
+msgstr "entrée non reconnue"
+
+#: builtin/apply.c:4237
+msgid "unable to read index file"
+msgstr "lecture du fichier d'index impossible"
+
+#: builtin/apply.c:4356 builtin/apply.c:4359 builtin/clone.c:92
+#: builtin/fetch.c:63
+msgid "path"
+msgstr "chemin"
+
+#: builtin/apply.c:4357
+msgid "don't apply changes matching the given path"
+msgstr "ne pas appliquer les modifications qui correspondent au chemin donné"
+
+#: builtin/apply.c:4360
+msgid "apply changes matching the given path"
+msgstr "appliquer les modifications qui correspondent au chemin donné"
+
+#: builtin/apply.c:4362
+msgid "num"
+msgstr "num"
+
+#: builtin/apply.c:4363
+msgid "remove <num> leading slashes from traditional diff paths"
+msgstr "supprimer <num> barres obliques des chemins traditionnels de diff"
+
+#: builtin/apply.c:4366
+msgid "ignore additions made by the patch"
+msgstr "ignorer les additions réalisées par le patch"
+
+#: builtin/apply.c:4368
+msgid "instead of applying the patch, output diffstat for the input"
+msgstr "au lieu d'appliquer le patch, afficher le diffstat de l'entrée"
+
+#: builtin/apply.c:4372
+msgid "show number of added and deleted lines in decimal notation"
+msgstr "afficher le nombre de lignes ajoutées et supprimées en notation décimale"
+
+#: builtin/apply.c:4374
+msgid "instead of applying the patch, output a summary for the input"
+msgstr "au lieu d'appliquer le patch, afficher un résumer de l'entrée"
+
+#: builtin/apply.c:4376
+msgid "instead of applying the patch, see if the patch is applicable"
+msgstr "au lieu d'appliquer le patch, voir si le patch est applicable"
+
+#: builtin/apply.c:4378
+msgid "make sure the patch is applicable to the current index"
+msgstr "s'assurer que le patch est applicable sur l'index actuel"
+
+#: builtin/apply.c:4380
+msgid "apply a patch without touching the working tree"
+msgstr "appliquer les patch sans toucher à la copie de travail"
+
+#: builtin/apply.c:4382
+msgid "also apply the patch (use with --stat/--summary/--check)"
+msgstr "appliquer aussi le patch (à utiliser avec ---stat/--summary/--check)"
+
+#: builtin/apply.c:4384
+msgid "attempt three-way merge if a patch does not apply"
+msgstr "tenter une fusion à 3 points si le patch ne s'applique pas proprement"
+
+#: builtin/apply.c:4386
+msgid "build a temporary index based on embedded index information"
+msgstr "construire un index temporaire fondé sur l'information de l'index embarqué"
+
+#: builtin/apply.c:4388 builtin/checkout-index.c:197 builtin/ls-files.c:456
+msgid "paths are separated with NUL character"
+msgstr "les chemins sont séparés par un caractère NUL"
+
+#: builtin/apply.c:4391
+msgid "ensure at least <n> lines of context match"
+msgstr "s'assurer d'au moins <n> lignes de correspondance de contexte"
+
+#: builtin/apply.c:4392
+msgid "action"
+msgstr "action"
+
+#: builtin/apply.c:4393
+msgid "detect new or modified lines that have whitespace errors"
+msgstr "détecter des lignes nouvelles ou modifiées qui contiennent des erreurs d'espace"
+
+#: builtin/apply.c:4396 builtin/apply.c:4399
+msgid "ignore changes in whitespace when finding context"
+msgstr "ignorer des modifications d'espace lors de la recherche de contexte"
+
+#: builtin/apply.c:4402
+msgid "apply the patch in reverse"
+msgstr "appliquer le patch en sens inverse"
+
+#: builtin/apply.c:4404
+msgid "don't expect at least one line of context"
+msgstr "ne pas s'attendre à au moins une ligne de contexte"
+
+#: builtin/apply.c:4406
+msgid "leave the rejected hunks in corresponding *.rej files"
+msgstr "laisser les sections rejetées dans les fichiers *.rej correspondants"
+
+#: builtin/apply.c:4408
+msgid "allow overlapping hunks"
+msgstr "accepter les recouvrements de sections"
+
+#: builtin/apply.c:4411
+msgid "tolerate incorrectly detected missing new-line at the end of file"
+msgstr "tolérer des erreurs de détection de retours chariot manquants en fin de fichier"
+
+#: builtin/apply.c:4414
+msgid "do not trust the line counts in the hunk headers"
+msgstr "ne pas se fier au compte de lignes dans les en-têtes de section"
+
+#: builtin/apply.c:4416
+msgid "root"
+msgstr "racine"
+
+#: builtin/apply.c:4417
+msgid "prepend <root> to all filenames"
+msgstr "préfixer tous les noms de fichier avec <root>"
+
+#: builtin/apply.c:4439
+msgid "--3way outside a repository"
+msgstr "--3way hors d'un dépôt"
+
+#: builtin/apply.c:4447
+msgid "--index outside a repository"
+msgstr "--index hors d'un dépôt"
+
+#: builtin/apply.c:4450
+msgid "--cached outside a repository"
+msgstr "--cached hors d'un dépôt"
+
+#: builtin/apply.c:4466
+#, c-format
+msgid "can't open patch '%s'"
+msgstr "ouverture impossible du patch '%s'"
+
+#: builtin/apply.c:4480
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] "%d erreur d'espace ignorée"
+msgstr[1] "%d erreurs d'espace ignorées"
+
+#: builtin/apply.c:4486 builtin/apply.c:4496
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] "%d ligne a ajouté des erreurs d'espace."
+msgstr[1] "%d lignes ont ajouté des erreurs d'espace."
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr "création impossible du fichier d'archive '%s'"
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr "impossible de rediriger la sortie"
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr "git archive : Dépôt distant sans URL"
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr "git archive : ACK/NACK attendu, EOF reçu"
+
+#: builtin/archive.c:61
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive : NACK %s"
+
+#: builtin/archive.c:63
+#, c-format
+msgid "remote error: %s"
+msgstr "erreur distante : %s"
+
+#: builtin/archive.c:64
+msgid "git archive: protocol error"
+msgstr "git archive : erreur de protocole"
+
+#: builtin/archive.c:68
+msgid "git archive: expected a flush"
+msgstr "git archive : vidage attendu"
+
+#: builtin/bisect--helper.c:7
+msgid "git bisect--helper --next-all [--no-checkout]"
+msgstr "git bisect --helper --next-all [--no-checkout]"
+
+#: builtin/bisect--helper.c:17
+msgid "perform 'git bisect next'"
+msgstr "effectuer 'git bisect next'"
+
+#: builtin/bisect--helper.c:19
+msgid "update BISECT_HEAD instead of checking out the current commit"
+msgstr "mettre à jour BISECT_HEAD au lieu d'extraire le commit actuel"
+
+#: builtin/blame.c:25
+msgid "git blame [options] [rev-opts] [rev] [--] file"
+msgstr "git blame [options] [options-de-révision] [rev] [--] fichier"
+
+#: builtin/blame.c:30
+msgid "[rev-opts] are documented in git-rev-list(1)"
+msgstr "[options-de-révision] sont documentés dans git-rev-list(1)"
+
+#: builtin/blame.c:2355
+msgid "Show blame entries as we find them, incrementally"
+msgstr "Montrer les entrée de blâme au fur et à mesure de leur découverte, de manière incrémentale"
+
+#: builtin/blame.c:2356
+msgid "Show blank SHA-1 for boundary commits (Default: off)"
+msgstr "Montrer un SHA-1 blanc pour les commits de limite (Défaut : désactivé)"
+
+#: builtin/blame.c:2357
+msgid "Do not treat root commits as boundaries (Default: off)"
+msgstr "Ne pas traiter les commits racine comme des limites (Défaut : désactivé)"
+
+#: builtin/blame.c:2358
+msgid "Show work cost statistics"
+msgstr "Montrer les statistiques de coût d'activité"
+
+#: builtin/blame.c:2359
+msgid "Show output score for blame entries"
+msgstr "Montrer le score de sortie pour les entrées de blâme"
+
+#: builtin/blame.c:2360
+msgid "Show original filename (Default: auto)"
+msgstr "Montrer les noms de fichier originaux (Défaut : auto)"
+
+#: builtin/blame.c:2361
+msgid "Show original linenumber (Default: off)"
+msgstr "Montrer les numéros de lignes originaux (Défaut : désactivé)"
+
+#: builtin/blame.c:2362
+msgid "Show in a format designed for machine consumption"
+msgstr "Afficher dans un format propice à la consommation par machine"
+
+#: builtin/blame.c:2363
+msgid "Show porcelain format with per-line commit information"
+msgstr "Afficher en format porcelaine avec l'information de commit par ligne"
+
+#: builtin/blame.c:2364
+msgid "Use the same output mode as git-annotate (Default: off)"
+msgstr "Utiliser le même mode de sortie que git-annotate (Défaut : désactivé)"
+
+#: builtin/blame.c:2365
+msgid "Show raw timestamp (Default: off)"
+msgstr "Afficher les horodatages bruts (Défaut: désactivé)"
+
+#: builtin/blame.c:2366
+msgid "Show long commit SHA1 (Default: off)"
+msgstr "Afficher les longs SHA1 de commits (Défaut : désactivé)"
+
+#: builtin/blame.c:2367
+msgid "Suppress author name and timestamp (Default: off)"
+msgstr "Supprimer le nom de l'auteur et l'horodatage (Défaut : désactivé)"
+
+#: builtin/blame.c:2368
+msgid "Show author email instead of name (Default: off)"
+msgstr "Afficher l'e-mail de l'auteur au lieu du nom (Défaut : désactivé)"
+
+#: builtin/blame.c:2369
+msgid "Ignore whitespace differences"
+msgstr "Ignorer les différences d'espace"
+
+#: builtin/blame.c:2370
+msgid "Spend extra cycles to find better match"
+msgstr "Dépenser des cycles supplémentaires pour trouver une meilleure correspondance"
+
+#: builtin/blame.c:2371
+msgid "Use revisions from <file> instead of calling git-rev-list"
+msgstr "Utiliser les révisions du fichier <fichier> au lieu d'appeler git-rev-list"
+
+#: builtin/blame.c:2372
+msgid "Use <file>'s contents as the final image"
+msgstr "Utiliser le contenu de <fichier> comme image finale"
+
+#: builtin/blame.c:2373 builtin/blame.c:2374
+msgid "score"
+msgstr "score"
+
+#: builtin/blame.c:2373
+msgid "Find line copies within and across files"
+msgstr "Trouver les copies de ligne dans et entre les fichiers"
+
+#: builtin/blame.c:2374
+msgid "Find line movements within and across files"
+msgstr "Trouver les mouvements de ligne dans et entre les fichiers"
+
+#: builtin/blame.c:2375
+msgid "n,m"
+msgstr "n,m"
+
+#: builtin/blame.c:2375
+msgid "Process only line range n,m, counting from 1"
+msgstr "Traiter seulement l'intervalle de ligne n,m en commençant le compte à 1"
+
+#: builtin/branch.c:24
+msgid "git branch [options] [-r | -a] [--merged | --no-merged]"
+msgstr "git branch [options] [-r | -a] [--merged | --no-merged]"
+
+#: builtin/branch.c:25
+msgid "git branch [options] [-l] [-f] <branchname> [<start-point>]"
+msgstr "git branch [options] [-l] [-f] <nomdebranch> [<point-de-départ>]"
+
+#: builtin/branch.c:26
+msgid "git branch [options] [-r] (-d | -D) <branchname>..."
+msgstr "git branch [options] [-r] (-d | -D) <nomdebranche>..."
+
+#: builtin/branch.c:27
+msgid "git branch [options] (-m | -M) [<oldbranch>] <newbranch>"
+msgstr "git branch [options] (-m | -M) [<anciennebranche>] <nouvellebranche>"
+
+#: builtin/branch.c:150
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+"suppression de la branche '%s' qui a été fusionnée dans\n"
+"         '%s', mais pas dans HEAD."
+
+#: builtin/branch.c:154
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+"branche '%s' non supprimée car elle n'a pas été fusionnée dans\n"
+"         '%s', même si elle est fusionnée dans HEAD."
+
+#: builtin/branch.c:168
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr "Impossible de rechercher l'objet commit pour '%s'"
+
+#: builtin/branch.c:172
+#, c-format
+msgid ""
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
+msgstr ""
+"La branche '%s' n'est pas totalement fusionnée.\n"
+"Si vous êtes sur que vous voulez la supprimer, lancez 'git branch -D %s'."
+
+#: builtin/branch.c:185
+msgid "Update of config-file failed"
+msgstr "Échec de la mise à jour du fichier de config"
+
+#: builtin/branch.c:213
+msgid "cannot use -a with -d"
+msgstr "impossible d'utiliser -a avec -d"
+
+#: builtin/branch.c:219
+msgid "Couldn't look up commit object for HEAD"
+msgstr "Impossible de rechercher l'objet commit pour HEAD"
+
+#: builtin/branch.c:227
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr "Impossible de supprimer la branche '%s' sur laquelle vous êtes."
+
+#: builtin/branch.c:240
+#, c-format
+msgid "remote branch '%s' not found."
+msgstr "branche distante %s' non trouvée."
+
+#: builtin/branch.c:241
+#, c-format
+msgid "branch '%s' not found."
+msgstr "branche '%s' non trouvée."
+
+#: builtin/branch.c:255
+#, c-format
+msgid "Error deleting remote branch '%s'"
+msgstr "Erreur lors de la suppression de la branche distante '%s'"
+
+#: builtin/branch.c:256
+#, c-format
+msgid "Error deleting branch '%s'"
+msgstr "Erreur lors de la suppression de la branche '%s'"
+
+#: builtin/branch.c:263
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
+msgstr "Branche distante %s supprimée (précédemment %s).\n"
+
+#: builtin/branch.c:264
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Branche %s supprimée (précédemment %s).\n"
+
+#: builtin/branch.c:366
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr "la branche '%s' ne pointe pas sur un commit"
+
+#: builtin/branch.c:453
+#, c-format
+msgid "[%s: behind %d]"
+msgstr "[%s: en retard de %d]"
+
+#: builtin/branch.c:455
+#, c-format
+msgid "[behind %d]"
+msgstr "[en retard de %d]"
+
+#: builtin/branch.c:459
+#, c-format
+msgid "[%s: ahead %d]"
+msgstr "[%s : en avance de %d]"
+
+#: builtin/branch.c:461
+#, c-format
+msgid "[ahead %d]"
+msgstr "[en avance de %d]"
+
+#: builtin/branch.c:464
+#, c-format
+msgid "[%s: ahead %d, behind %d]"
+msgstr "[%s : en avance de %d, en retard de %d]"
+
+#: builtin/branch.c:467
+#, c-format
+msgid "[ahead %d, behind %d]"
+msgstr "[en avance de %d, en retard de %d]"
+
+#: builtin/branch.c:490
+msgid " **** invalid ref ****"
+msgstr " **** référence invalide ****"
+
+#: builtin/branch.c:582
+#, c-format
+msgid "(no branch, rebasing %s)"
+msgstr "(aucune branche, rebasage de %s)"
+
+#: builtin/branch.c:585
+#, c-format
+msgid "(no branch, bisect started on %s)"
+msgstr "(aucune branche, bisect a démarré sur %s)"
+
+#: builtin/branch.c:588
+#, c-format
+msgid "(detached from %s)"
+msgstr "(détaché de %s)"
+
+#: builtin/branch.c:591
+msgid "(no branch)"
+msgstr "(aucune branche)"
+
+#: builtin/branch.c:637
+#, c-format
+msgid "object '%s' does not point to a commit"
+msgstr "l'objet '%s' ne pointe pas sur un commit"
+
+#: builtin/branch.c:669
+msgid "some refs could not be read"
+msgstr "des références n'ont pas pu être lues"
+
+#: builtin/branch.c:682
+msgid "cannot rename the current branch while not on any."
+msgstr "impossible de renommer la branche actuelle, il n'y en a pas."
+
+#: builtin/branch.c:692
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr "Nom de branche invalide : '%s'"
+
+#: builtin/branch.c:707
+msgid "Branch rename failed"
+msgstr "Echec de renommage de la branche"
+
+#: builtin/branch.c:711
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr "Renommage d'un branche mal nommée '%s'"
+
+#: builtin/branch.c:715
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr "La branche a été renommée en %s, mais HEAD n'est pas mise à jour !"
+
+#: builtin/branch.c:722
+msgid "Branch is renamed, but update of config-file failed"
+msgstr "La branche est renommée, mais la mise à jour du fichier de config a échoué"
+
+#: builtin/branch.c:737
+#, c-format
+msgid "malformed object name %s"
+msgstr "nom d'objet malformé %s"
+
+#: builtin/branch.c:761
+#, c-format
+msgid "could not write branch description template: %s"
+msgstr "impossible d'écrire le modèle de description de branche : %s"
+
+#: builtin/branch.c:791
+msgid "Generic options"
+msgstr "Options génériques"
+
+#: builtin/branch.c:793
+msgid "show hash and subject, give twice for upstream branch"
+msgstr "afficher le hachage et le sujet, doublé pour la branche amont"
+
+#: builtin/branch.c:794
+msgid "suppress informational messages"
+msgstr "supprimer les messages d'information"
+
+#: builtin/branch.c:795
+msgid "set up tracking mode (see git-pull(1))"
+msgstr "régler le mode de suivi (voir git-pull(1))"
+
+#: builtin/branch.c:797
+msgid "change upstream info"
+msgstr "modifier l'information amont"
+
+#: builtin/branch.c:801
+msgid "use colored output"
+msgstr "utiliser la coloration dans la sortie"
+
+#: builtin/branch.c:802
+msgid "act on remote-tracking branches"
+msgstr "agir sur les branches de suivi distantes"
+
+#: builtin/branch.c:805 builtin/branch.c:811 builtin/branch.c:832
+#: builtin/branch.c:838 builtin/commit.c:1368 builtin/commit.c:1369
+#: builtin/commit.c:1370 builtin/commit.c:1371 builtin/tag.c:468
+msgid "commit"
+msgstr "valider"
+
+#: builtin/branch.c:806 builtin/branch.c:812
+msgid "print only branches that contain the commit"
+msgstr "afficher seulement les branches qui contiennent le commit"
+
+#: builtin/branch.c:818
+msgid "Specific git-branch actions:"
+msgstr "Actions spécifiques à git-branch :"
+
+#: builtin/branch.c:819
+msgid "list both remote-tracking and local branches"
+msgstr "afficher à la fois les branches de suivi et les branches locales"
+
+#: builtin/branch.c:821
+msgid "delete fully merged branch"
+msgstr "supprimer une branche totalement fusionnée"
+
+#: builtin/branch.c:822
+msgid "delete branch (even if not merged)"
+msgstr "supprimer une branche (même non fusionnée)"
+
+#: builtin/branch.c:823
+msgid "move/rename a branch and its reflog"
+msgstr "déplacer/renommer une branche et son reflog"
+
+#: builtin/branch.c:824
+msgid "move/rename a branch, even if target exists"
+msgstr "déplacer/renommer une branche, même si la cible existe"
+
+#: builtin/branch.c:825
+msgid "list branch names"
+msgstr "afficher les noms des branches"
+
+#: builtin/branch.c:826
+msgid "create the branch's reflog"
+msgstr "créer le reflog de la branche"
+
+#: builtin/branch.c:828
+msgid "edit the description for the branch"
+msgstr "éditer la description de la branche"
+
+#: builtin/branch.c:829
+msgid "force creation (when already exists)"
+msgstr "forcer la création (même si la cible existe)"
+
+#: builtin/branch.c:832
+msgid "print only not merged branches"
+msgstr "afficher seulement les branches non fusionnées"
+
+#: builtin/branch.c:838
+msgid "print only merged branches"
+msgstr "afficher seulement les branches fusionnées"
+
+#: builtin/branch.c:842
+msgid "list branches in columns"
+msgstr "afficher les branches en colonnes"
+
+#: builtin/branch.c:855
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Échec de résolution de HEAD comme référence valide."
+
+#: builtin/branch.c:860 builtin/clone.c:619
+msgid "HEAD not found below refs/heads!"
+msgstr "HEAD non trouvée sous refs/heads !"
+
+#: builtin/branch.c:883
+msgid "--column and --verbose are incompatible"
+msgstr "--column et --verbose sont incompatibles"
+
+#: builtin/branch.c:889 builtin/branch.c:928
+msgid "branch name required"
+msgstr "le nom de branche est requis"
+
+#: builtin/branch.c:904
+msgid "Cannot give description to detached HEAD"
+msgstr "Impossible de décrire une HEAD détachée"
+
+#: builtin/branch.c:909
+msgid "cannot edit description of more than one branch"
+msgstr "impossible d'éditer la description de plus d'une branche"
+
+#: builtin/branch.c:916
+#, c-format
+msgid "No commit on branch '%s' yet."
+msgstr "Aucun commit sur la branche '%s'."
+
+#: builtin/branch.c:919
+#, c-format
+msgid "No branch named '%s'."
+msgstr "Aucune branche nommée '%s'."
+
+#: builtin/branch.c:934
+msgid "too many branches for a rename operation"
+msgstr "trop de branches pour une opération de renommage"
+
+#: builtin/branch.c:939
+msgid "too many branches to set new upstream"
+msgstr "trop de branches pour spécifier une branche amont"
+
+#: builtin/branch.c:943
+#, c-format
+msgid ""
+"could not set upstream of HEAD to %s when it does not point to any branch."
+msgstr "impossible de spécifier une branche amont de HEAD par %s qui ne pointe sur aucune branche."
+
+#: builtin/branch.c:946 builtin/branch.c:968 builtin/branch.c:990
+#, c-format
+msgid "no such branch '%s'"
+msgstr "pas de branche '%s'"
+
+#: builtin/branch.c:950
+#, c-format
+msgid "branch '%s' does not exist"
+msgstr "la branche '%s' n'existe pas"
+
+#: builtin/branch.c:962
+msgid "too many branches to unset upstream"
+msgstr "trop de branches pour désactiver un amont"
+
+#: builtin/branch.c:966
+msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgstr "impossible de désactiver une branche amont de HEAD quand elle ne pointe sur aucune branche."
+
+#: builtin/branch.c:972
+#, c-format
+msgid "Branch '%s' has no upstream information"
+msgstr "La branche '%s' n'a aucune information de branche amont"
+
+#: builtin/branch.c:987
+msgid "it does not make sense to create 'HEAD' manually"
+msgstr "créer manuellement 'HEAD' n'a aucun sens"
+
+#: builtin/branch.c:993
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr "les options -a et -r de 'git branch' n'ont pas de sens avec un nom de branche"
+
+#: builtin/branch.c:996
+#, c-format
+msgid ""
+"The --set-upstream flag is deprecated and will be removed. Consider using --"
+"track or --set-upstream-to\n"
+msgstr "l'option --set-upstream est obsolète et va disparaître. Utilisez plutôt --track ou --set-upstream-to\n"
+
+#: builtin/branch.c:1013
+#, c-format
+msgid ""
+"\n"
+"If you wanted to make '%s' track '%s', do this:\n"
+"\n"
+msgstr "\n"
+"Si vous vouliez que '%s' suive '%s', faîtes ceci :\n"
+"\n"
+
+#: builtin/branch.c:1014
+#, c-format
+msgid "    git branch -d %s\n"
+msgstr "    git branch -d %s\n"
+
+#: builtin/branch.c:1015
+#, c-format
+msgid "    git branch --set-upstream-to %s\n"
+msgstr "    git branch -set-upstream-to %s\n"
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr "%s est correct\n"
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr "La création d'un colis requiert un dépôt."
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr "Le dépaquetage d'un colis requiert un dépôt."
+
+#: builtin/cat-file.c:176
+msgid "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"
+msgstr "git cat-file (-t|-s|-e|-p|<type>|--textconv) <objet>"
+
+#: builtin/cat-file.c:177
+msgid "git cat-file (--batch|--batch-check) < <list_of_objects>"
+msgstr "git cat-file (--batch|--batch-check) < <liste_d_objets>"
+
+#: builtin/cat-file.c:195
+msgid "<type> can be one of: blob, tree, commit, tag"
+msgstr "<type> peut être : blob, tree, commit ou tag"
+
+#: builtin/cat-file.c:196
+msgid "show object type"
+msgstr "afficher le type de l'objet"
+
+#: builtin/cat-file.c:197
+msgid "show object size"
+msgstr "afficher la taille de l'objet"
+
+#: builtin/cat-file.c:199
+msgid "exit with zero when there's no error"
+msgstr "sortir avec un code d'erreur nul quand il n'y a aucune erreur"
+
+#: builtin/cat-file.c:200
+msgid "pretty-print object's content"
+msgstr "afficher avec mise en forme le contenu de l'objet"
+
+#: builtin/cat-file.c:202
+msgid "for blob objects, run textconv on object's content"
+msgstr "pour les objets blob, lancer textconv sur le contenu de l'objet"
+
+#: builtin/cat-file.c:204
+msgid "show info and content of objects fed from the standard input"
+msgstr "afficher l'information et le contenu des objets passés en entrée standard"
+
+#: builtin/cat-file.c:207
+msgid "show info about objects fed from the standard input"
+msgstr "afficher l'information des objets passés en entrée standard"
+
+#: builtin/check-attr.c:11
+msgid "git check-attr [-a | --all | attr...] [--] pathname..."
+msgstr "git check-attr [-a | --all | attr...] [--] chemin..."
+
+#: builtin/check-attr.c:12
+msgid "git check-attr --stdin [-z] [-a | --all | attr...] < <list-of-paths>"
+msgstr "git check-attr --stdin [-z] [-a | --all | attr...] < <liste-de-chemins>"
+
+#: builtin/check-attr.c:19
+msgid "report all attributes set on file"
+msgstr "afficher tous les attributs associés au fichier"
+
+#: builtin/check-attr.c:20
+msgid "use .gitattributes only from the index"
+msgstr "utiliser .gitattributes seulement depuis l'index"
+
+#: builtin/check-attr.c:21 builtin/check-ignore.c:22 builtin/hash-object.c:75
+msgid "read file names from stdin"
+msgstr "lire les noms de fichier depuis stdin"
+
+#: builtin/check-attr.c:23 builtin/check-ignore.c:24
+msgid "input paths are terminated by a null character"
+msgstr "les chemins en entrée sont terminés par le caractère nul"
+
+#: builtin/check-ignore.c:18 builtin/checkout.c:1044 builtin/gc.c:177
+msgid "suppress progress reporting"
+msgstr "supprimer l'état d'avancement"
+
+#: builtin/check-ignore.c:146
+msgid "cannot specify pathnames with --stdin"
+msgstr "impossible de spécifier les chemins avec --stdin"
+
+#: builtin/check-ignore.c:149
+msgid "-z only makes sense with --stdin"
+msgstr "-z n'a de sens qu'avec l'option --stdin"
+
+#: builtin/check-ignore.c:151
+msgid "no path specified"
+msgstr "aucun chemin spécifié"
+
+#: builtin/check-ignore.c:155
+msgid "--quiet is only valid with a single pathname"
+msgstr "--quiet n'est valide qu'avec un seul chemin"
+
+#: builtin/check-ignore.c:157
+msgid "cannot have both --quiet and --verbose"
+msgstr "impossible d'avoir --quiet et --verbose"
+
+#: builtin/checkout-index.c:126
+msgid "git checkout-index [options] [--] [<file>...]"
+msgstr "git checkout-index [options] [--] [<fichier>...]"
+
+#: builtin/checkout-index.c:187
+msgid "check out all files in the index"
+msgstr "extraire tous les fichiers présents dans l'index"
+
+#: builtin/checkout-index.c:188
+msgid "force overwrite of existing files"
+msgstr "forcer l'écrasement des fichiers existants"
+
+#: builtin/checkout-index.c:190
+msgid "no warning for existing files and files not in index"
+msgstr "pas d'avertissement pour les fichiers existants et les fichiers absents de l'index"
+
+#: builtin/checkout-index.c:192
+msgid "don't checkout new files"
+msgstr "ne pas extraire les nouveaux fichiers"
+
+#: builtin/checkout-index.c:194
+msgid "update stat information in the index file"
+msgstr "mettre à jour l'information de stat dans le fichier d'index"
+
+#: builtin/checkout-index.c:200
+msgid "read list of paths from the standard input"
+msgstr "lire la liste des chemins depuis l'entrée standard"
+
+#: builtin/checkout-index.c:202
+msgid "write the content to temporary files"
+msgstr "écrire le contenu dans des fichiers temporaires"
+
+#: builtin/checkout-index.c:203 builtin/column.c:30
+msgid "string"
+msgstr "chaîne"
+
+#: builtin/checkout-index.c:204
+msgid "when creating files, prepend <string>"
+msgstr "lors de la création de fichiers, préfixer par <chaîne>"
+
+#: builtin/checkout-index.c:207
+msgid "copy out the files from named stage"
+msgstr "copier les fichiers depuis l'index nommé"
+
+#: builtin/checkout.c:25
+msgid "git checkout [options] <branch>"
+msgstr "git checkout [options] <branche>"
+
+#: builtin/checkout.c:26
+msgid "git checkout [options] [<branch>] -- <file>..."
+msgstr "git checkout [options] [<branche>] -- <fichier>..."
+
+#: builtin/checkout.c:117 builtin/checkout.c:150
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr "le chemin '%s' n'a pas notre version"
+
+#: builtin/checkout.c:119 builtin/checkout.c:152
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "le chemin '%s' n'a pas leur version"
+
+#: builtin/checkout.c:135
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr "le chemin '%s' n'a aucune des versions nécessaires"
+
+#: builtin/checkout.c:179
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "le chemin '%s' n'a pas les versions nécessaires"
+
+#: builtin/checkout.c:196
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr "chemin '%s' : impossible de fusionner"
+
+#: builtin/checkout.c:213
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr "Impossible d'ajouter le résultat de fusion pour '%s'"
+
+#: builtin/checkout.c:237 builtin/checkout.c:240 builtin/checkout.c:243
+#: builtin/checkout.c:246
+#, c-format
+msgid "'%s' cannot be used with updating paths"
+msgstr "'%s' ne peut pas être utilisé avec des mises à jour de chemins"
+
+#: builtin/checkout.c:249 builtin/checkout.c:252
+#, c-format
+msgid "'%s' cannot be used with %s"
+msgstr "'%s' ne peut pas être utilisé avec %s"
+
+#: builtin/checkout.c:255
+#, c-format
+msgid "Cannot update paths and switch to branch '%s' at the same time."
+msgstr "Impossible de mettre à jour les chemins et basculer sur la branche '%s' en même temps."
+
+#: builtin/checkout.c:266 builtin/checkout.c:458
+msgid "corrupt index file"
+msgstr "fichier d'index corrompu"
+
+#: builtin/checkout.c:329 builtin/checkout.c:336
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr "le chemin '%s' n'est pas fusionné"
+
+#: builtin/checkout.c:480
+msgid "you need to resolve your current index first"
+msgstr "vous devez d'abord résoudre votre index courant"
+
+#: builtin/checkout.c:601
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr "Impossible de faire un reflog pour '%s'\n"
+
+#: builtin/checkout.c:634
+msgid "HEAD is now at"
+msgstr "HEAD est maintenant sur"
+
+#: builtin/checkout.c:641
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Remise à zéro de la branche '%s'\n"
+
+#: builtin/checkout.c:644
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Déjà sur '%s'\n"
+
+#: builtin/checkout.c:648
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr "Basculement et remise à zéro de la branche '%s'\n"
+
+#: builtin/checkout.c:650 builtin/checkout.c:987
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "Basculement sur la nouvelle branche '%s'\n"
+
+#: builtin/checkout.c:652
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Basculement sur la branche '%s'\n"
+
+#: builtin/checkout.c:708
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... et %d en plus.\n"
+
+#. The singular version
+#: builtin/checkout.c:714
+#, c-format
+msgid ""
+"Warning: you are leaving %d commit behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgid_plural ""
+"Warning: you are leaving %d commits behind, not connected to\n"
+"any of your branches:\n"
+"\n"
+"%s\n"
+msgstr[0] ""
+"Attention : vous laissez %d commit en retard, non connectés à\n"
+"une branche :\n"
+"\n"
+"%s\n"
+msgstr[1] ""
+"Attention : vous laissez %d commits en retard, non connectés à\n"
+"une branche :\n"
+"\n"
+"%s\n"
+
+#: builtin/checkout.c:732
+#, c-format
+msgid ""
+"If you want to keep them by creating a new branch, this may be a good time\n"
+"to do so with:\n"
+"\n"
+" git branch new_branch_name %s\n"
+"\n"
+msgstr ""
+"Si vous souhaitez les garder en créant une nouvelle branche, c'est le bon moment\n"
+"de le faire avec :\n"
+"\n"
+"git branche nouvelle_branche %s\n"
+"\n"
+
+#: builtin/checkout.c:762
+msgid "internal error in revision walk"
+msgstr "erreur interne lors du parcours des révisions"
+
+#: builtin/checkout.c:766
+msgid "Previous HEAD position was"
+msgstr "La position précédente de HEAD était"
+
+#: builtin/checkout.c:793 builtin/checkout.c:982
+msgid "You are on a branch yet to be born"
+msgstr "Vous êtes sur une branche qui doit encore naître"
+
+#. case (1)
+#: builtin/checkout.c:918
+#, c-format
+msgid "invalid reference: %s"
+msgstr "référence invalide : %s"
+
+#. case (1): want a tree
+#: builtin/checkout.c:957
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr "la référence n'est pas un arbre : %s"
+
+#: builtin/checkout.c:996
+msgid "paths cannot be used with switching branches"
+msgstr "impossible d'utiliser des chemins avec un basculement de branches"
+
+#: builtin/checkout.c:999 builtin/checkout.c:1003
+#, c-format
+msgid "'%s' cannot be used with switching branches"
+msgstr "'%s' ne peut pas être utilisé avec un basculement de branches"
+
+#: builtin/checkout.c:1007 builtin/checkout.c:1010 builtin/checkout.c:1015
+#: builtin/checkout.c:1018
+#, c-format
+msgid "'%s' cannot be used with '%s'"
+msgstr "'%s' ne peut pas être utilisé avec '%s'"
+
+#: builtin/checkout.c:1023
+#, c-format
+msgid "Cannot switch branch to a non-commit '%s'"
+msgstr "Impossible de basculer de branche vers '%s' qui n'est pas un commit"
+
+#: builtin/checkout.c:1045 builtin/checkout.c:1047 builtin/clone.c:90
+#: builtin/remote.c:169 builtin/remote.c:171
+msgid "branch"
+msgstr "branche"
+
+#: builtin/checkout.c:1046
+msgid "create and checkout a new branch"
+msgstr "créer et extraire une nouvelle branche"
+
+#: builtin/checkout.c:1048
+msgid "create/reset and checkout a branch"
+msgstr "créer/réinitialiser et extraire une branche"
+
+#: builtin/checkout.c:1049
+msgid "create reflog for new branch"
+msgstr "créer un refog pour une nouvelle branche"
+
+#: builtin/checkout.c:1050
+msgid "detach the HEAD at named commit"
+msgstr "détacher la HEAD à la validation nommée"
+
+#: builtin/checkout.c:1051
+msgid "set upstream info for new branch"
+msgstr "paramétrer l'information de branche amont pour une nouvelle branche"
+
+#: builtin/checkout.c:1053
+msgid "new branch"
+msgstr "nouvelle branche"
+
+#: builtin/checkout.c:1053
+msgid "new unparented branch"
+msgstr "nouvelle branche sans parent"
+
+#: builtin/checkout.c:1054
+msgid "checkout our version for unmerged files"
+msgstr "extraire notre version pour les fichiers non fusionnés"
+
+#: builtin/checkout.c:1056
+msgid "checkout their version for unmerged files"
+msgstr "extraire leur version pour les fichiers non fusionnés"
+
+#: builtin/checkout.c:1058
+msgid "force checkout (throw away local modifications)"
+msgstr "forcer l'extraction (laisser tomber les modifications locales)"
+
+#: builtin/checkout.c:1059
+msgid "perform a 3-way merge with the new branch"
+msgstr "réaliser une fusion à 3 points avec la nouvelle branche"
+
+#: builtin/checkout.c:1060 builtin/merge.c:217
+msgid "update ignored files (default)"
+msgstr "mettre à jour les fichiers ignorés (par défaut)"
+
+#: builtin/checkout.c:1061 builtin/log.c:1158 parse-options.h:245
+msgid "style"
+msgstr "style"
+
+#: builtin/checkout.c:1062
+msgid "conflict style (merge or diff3)"
+msgstr "style de conflit (fusion ou diff3)"
+
+#: builtin/checkout.c:1065
+msgid "do not limit pathspecs to sparse entries only"
+msgstr "ne pas limiter les spécificateurs de chemins aux seules entrées creuses"
+
+#: builtin/checkout.c:1067
+msgid "second guess 'git checkout no-such-branch'"
+msgstr "deuxième chance 'git checkout branche-inexistante'"
+
+#: builtin/checkout.c:1091
+msgid "-b, -B and --orphan are mutually exclusive"
+msgstr "-b, -B et --orphan sont mutuellement exclusifs"
+
+#: builtin/checkout.c:1108
+msgid "--track needs a branch name"
+msgstr "--track requiert un nom de branche"
+
+#: builtin/checkout.c:1115
+msgid "Missing branch name; try -b"
+msgstr "Nom de branche manquant ; essayez -b"
+
+#: builtin/checkout.c:1150
+msgid "invalid path specification"
+msgstr "spécification de chemin invalide"
+
+#: builtin/checkout.c:1157
+#, 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 ""
+"Impossible de mettre à jour les chemins et de basculer sur la branche '%s' en même temps.\n"
+"Souhaitiez-vous extraire '%s' qui ne peut être résolu comme commit ?"
+
+#: builtin/checkout.c:1162
+#, c-format
+msgid "git checkout: --detach does not take a path argument '%s'"
+msgstr "git checkout: --detach n'accepte pas un argument de chemin '%s'"
+
+#: builtin/checkout.c:1166
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+"git checkout: --ours/--theirs, --force et --merge sont incompatibles lors\n"
+"de l'extraction de l'index."
+
+#: builtin/clean.c:20
+msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+msgstr "git clean [-d] [-f] [-n] [-q] [-e <motif>] [-x | -X] [--] <chemins>..."
+
+#: builtin/clean.c:24
+#, c-format
+msgid "Removing %s\n"
+msgstr "Suppression de %s\n"
+
+#: builtin/clean.c:25
+#, c-format
+msgid "Would remove %s\n"
+msgstr "Supprimerait %s\n"
+
+#: builtin/clean.c:26
+#, c-format
+msgid "Skipping repository %s\n"
+msgstr "Ignore le dépôt %s\n"
+
+#: builtin/clean.c:27
+#, c-format
+msgid "Would skip repository %s\n"
+msgstr "Ignorerait le dépôt %s\n"
+
+#: builtin/clean.c:28
+#, c-format
+msgid "failed to remove %s"
+msgstr "échec de la suppression de %s"
+
+#: builtin/clean.c:160
+msgid "do not print names of files removed"
+msgstr "ne pas afficher les noms des fichiers supprimés"
+
+#: builtin/clean.c:162
+msgid "force"
+msgstr "forcer"
+
+#: builtin/clean.c:164
+msgid "remove whole directories"
+msgstr "supprimer les répertoires entiers"
+
+#: builtin/clean.c:165 builtin/describe.c:412 builtin/grep.c:717
+#: builtin/ls-files.c:487 builtin/name-rev.c:231 builtin/show-ref.c:182
+msgid "pattern"
+msgstr "motif"
+
+#: builtin/clean.c:166
+msgid "add <pattern> to ignore rules"
+msgstr "ajouter <motif> aux règles ignore"
+
+#: builtin/clean.c:167
+msgid "remove ignored files, too"
+msgstr "supprimer les fichiers ignorés, aussi"
+
+#: builtin/clean.c:169
+msgid "remove only ignored files"
+msgstr "supprimer seulement les fichiers ignorés"
+
+#: builtin/clean.c:187
+msgid "-x and -X cannot be used together"
+msgstr "-x et -X ne peuvent pas être utilisés ensemble"
+
+#: builtin/clean.c:191
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr "clean.requireForce positionné à true et ni -n ni -f fourni ; refus de nettoyer"
+
+#: builtin/clean.c:194
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr "clean.requireForce à true par défaut et ni -n ni -f fourni ; refus de nettoyer"
+
+#: builtin/clone.c:37
+msgid "git clone [options] [--] <repo> [<dir>]"
+msgstr "git clone [options] [--] <dépôt> [<répertoire>]"
+
+#: builtin/clone.c:65 builtin/fetch.c:82 builtin/merge.c:214
+#: builtin/push.c:436
+msgid "force progress reporting"
+msgstr "forcer l'état d'avancement"
+
+#: builtin/clone.c:67
+msgid "don't create a checkout"
+msgstr "ne pas créer d'extraction"
+
+#: builtin/clone.c:68 builtin/clone.c:70 builtin/init-db.c:488
+msgid "create a bare repository"
+msgstr "créer un dépôt nu"
+
+#: builtin/clone.c:73
+msgid "create a mirror repository (implies bare)"
+msgstr "créer un dépôt miroir (implique dépôt nu)"
+
+#: builtin/clone.c:75
+msgid "to clone from a local repository"
+msgstr "pour cloner depuis un dépôt local"
+
+#: builtin/clone.c:77
+msgid "don't use local hardlinks, always copy"
+msgstr "ne pas utiliser de liens durs locaux, toujours copier"
+
+#: builtin/clone.c:79
+msgid "setup as shared repository"
+msgstr "régler comme dépôt partagé"
+
+#: builtin/clone.c:81 builtin/clone.c:83
+msgid "initialize submodules in the clone"
+msgstr "initialiser les sous-modules dans le clone"
+
+#: builtin/clone.c:84 builtin/init-db.c:485
+msgid "template-directory"
+msgstr "répertoire-modèle"
+
+#: builtin/clone.c:85 builtin/init-db.c:486
+msgid "directory from which templates will be used"
+msgstr "répertoire depuis lequel les modèles vont être utilisés"
+
+#: builtin/clone.c:87
+msgid "reference repository"
+msgstr "dépôt de référence"
+
+#: builtin/clone.c:88 builtin/column.c:26 builtin/merge-file.c:44
+msgid "name"
+msgstr "nom"
+
+#: builtin/clone.c:89
+msgid "use <name> instead of 'origin' to track upstream"
+msgstr "utiliser <nom> au lieu de 'origin' pour suivre la branche amont"
+
+#: builtin/clone.c:91
+msgid "checkout <branch> instead of the remote's HEAD"
+msgstr "extraire <branche> au lieu de la HEAD du répertoire distant"
+
+#: builtin/clone.c:93
+msgid "path to git-upload-pack on the remote"
+msgstr "chemin vers git-upload-pack sur le serveur distant"
+
+#: builtin/clone.c:94 builtin/fetch.c:83 builtin/grep.c:662
+msgid "depth"
+msgstr "profondeur"
+
+#: builtin/clone.c:95
+msgid "create a shallow clone of that depth"
+msgstr "créer un clone superficiel de cette profondeur"
+
+#: builtin/clone.c:97
+msgid "clone only one branch, HEAD or --branch"
+msgstr "cloner seulement une branche, HEAD ou --branch"
+
+#: builtin/clone.c:98 builtin/init-db.c:494
+msgid "gitdir"
+msgstr "gitdir"
+
+#: builtin/clone.c:99 builtin/init-db.c:495
+msgid "separate git dir from working tree"
+msgstr "séparer le répertoire git de la copie de travail"
+
+#: builtin/clone.c:100
+msgid "key=value"
+msgstr "clé=valeur"
+
+#: builtin/clone.c:101
+msgid "set config inside the new repository"
+msgstr "régler la configuration dans le nouveau dépôt"
+
+#: builtin/clone.c:254
+#, c-format
+msgid "reference repository '%s' is not a local repository."
+msgstr "le dépôt de référence '%s' n'est pas un dépôt local."
+
+#: builtin/clone.c:317
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "échec de la création du répertoire '%s'"
+
+#: builtin/clone.c:319 builtin/diff.c:77
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "échec du stat de '%s'"
+
+#: builtin/clone.c:321
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s existe et n'est pas un répertoire"
+
+#: builtin/clone.c:335
+#, c-format
+msgid "failed to stat %s\n"
+msgstr "échec du stat de %s\n"
+
+#: builtin/clone.c:357
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "échec de la création du lien '%s'"
+
+#: builtin/clone.c:361
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "échec de la copie vers '%s'"
+
+#: builtin/clone.c:384
+#, c-format
+msgid "done.\n"
+msgstr "fait.\n"
+
+#: builtin/clone.c:397
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry the checkout with 'git checkout -f HEAD'\n"
+msgstr "Le clone a réussi, mais l'extraction a échoué.\n"
+"Vous pouvez inspecter ce qui a été extrait avec 'git status'\n"
+"et réessayer l'extraction avec 'git checkout -f HEAD'\n"
+
+#: builtin/clone.c:476
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "Impossible de trouver la branche distante '%s' à cloner."
+
+#: builtin/clone.c:550
+msgid "remote did not send all necessary objects"
+msgstr "le serveur distant n'a pas envoyé tous les objets nécessaires"
+
+#: builtin/clone.c:610
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr "la HEAD distante réfère à une référence non existante, impossible de l'extraire.\n"
+
+#: builtin/clone.c:641
+msgid "unable to checkout working tree"
+msgstr "inpossible d'extraire la copie de travail"
+
+#: builtin/clone.c:749
+msgid "Too many arguments."
+msgstr "Trop d'arguments."
+
+#: builtin/clone.c:753
+msgid "You must specify a repository to clone."
+msgstr "Vous devez spécifier un dépôt à cloner."
+
+#: builtin/clone.c:764
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr "les options --bare et --origin %s sont incompatibles."
+
+#: builtin/clone.c:767
+msgid "--bare and --separate-git-dir are incompatible."
+msgstr "--bare et --separate-git-dir sont incompatibles."
+
+#: builtin/clone.c:780
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "le dépôt '%s' n'existe pas"
+
+#: builtin/clone.c:785
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr "--depth est ignoré dans les clones locaux : utilisez plutôt \"file://\"."
+
+#: builtin/clone.c:795
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr "le chemin de destination '%s' existe déjà et n'est pas un répertoire vide."
+
+#: builtin/clone.c:805
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr "la copie de travail '%s' existe déjà."
+
+#: builtin/clone.c:818 builtin/clone.c:830
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr "impossible de créer les répertoires de premier niveau dans '%s'"
+
+#: builtin/clone.c:821
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr "impossible de créer le répertoire de la copie de travail '%s'."
+
+#: builtin/clone.c:840
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Clonage dans le dépôt nu '%s'\n"
+
+#: builtin/clone.c:842
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "Clonage dans '%s'...\n"
+
+#: builtin/clone.c:877
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr "Je ne sais pas cloner %s"
+
+#: builtin/clone.c:926
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "La branche distante %s n'a pas été trouvée dans le dépôt amont %s"
+
+#: builtin/clone.c:933
+msgid "You appear to have cloned an empty repository."
+msgstr "Vous semblez avoir cloné un dépôt vide."
+
+#: builtin/column.c:9
+msgid "git column [options]"
+msgstr "git column [options]"
+
+#: builtin/column.c:26
+msgid "lookup config vars"
+msgstr "rechercher les variables de configuration"
+
+#: builtin/column.c:27 builtin/column.c:28
+msgid "layout to use"
+msgstr "mise en page à utiliser"
+
+#: builtin/column.c:29
+msgid "Maximum width"
+msgstr "Largeur maximale"
+
+#: builtin/column.c:30
+msgid "Padding space on left border"
+msgstr "Remplissage d'espace sur la bordure gauche"
+
+#: builtin/column.c:31
+msgid "Padding space on right border"
+msgstr "Remplissage d'espace sur le côté droit"
+
+#: builtin/column.c:32
+msgid "Padding space between columns"
+msgstr "Remplissage d'espace entre les colonnes"
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr "--command doit être le premier argument"
+
+#: builtin/commit.c:34
+msgid "git commit [options] [--] <pathspec>..."
+msgstr "git commit [options] [--] <spécification-de-chemin>..."
+
+#: builtin/commit.c:39
+msgid "git status [options] [--] <pathspec>..."
+msgstr "git status [options] [--] <spécification-de-chemin>..."
+
+#: builtin/commit.c:44
+msgid ""
+"Your name and email address were configured automatically based\n"
+"on your username and hostname. Please check that they are accurate.\n"
+"You can suppress this message by setting them explicitly:\n"
+"\n"
+"    git config --global user.name \"Your Name\"\n"
+"    git config --global user.email you@example.com\n"
+"\n"
+"After doing this, you may fix the identity used for this commit with:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+msgstr ""
+"Votre nom et votre adresse e-mail ont été configurés automatiquement en se fondant\n"
+"sur votre nom d'utilisateur et votre nom d'ordinateur. Veuillez vérifier qu'ils sont corrects.\n"
+"Vous pouvez supprimer ce message en les paramétrant explicitement :\n"
+"\n"
+"    git config --global user.name \"Votre Nom\"\n"
+"    git config --global user.email vous@exemple.com\n"
+"\n"
+"Après ceci, vous pouvez corriger l'identité utilisée pour ce commit avec :\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+#: builtin/commit.c:56
+msgid ""
+"You asked to amend the most recent commit, but doing so would make\n"
+"it empty. You can repeat your command with --allow-empty, or you can\n"
+"remove the commit entirely with \"git reset HEAD^\".\n"
+msgstr "Vous avez demandé de corriger le commit le plus récent, mais le faire le rendrait\n"
+"vide. Vous pouvez répéter votre commande avec --allow-empty, ou vous pouvez\n"
+"supprimer complètement le commit avec \"git reset HEAD^\".\n"
+
+#: builtin/commit.c:61
+msgid ""
+"The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
+"If you wish to commit it anyway, use:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Otherwise, please use 'git reset'\n"
+msgstr ""
+"Le picorage précédent est à présent vide, vraisemblablement dû à une résolution de conflit.\n"
+"Si vous souhaitez tout de même le valider, utilisez :\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Sinon, utilisez 'git reset'\n"
+
+#: builtin/commit.c:260
+msgid "failed to unpack HEAD tree object"
+msgstr "échec du dépaquetage de l'objet arbre HEAD"
+
+#: builtin/commit.c:302
+msgid "unable to create temporary index"
+msgstr "impossible de créer l'index temporaire"
+
+#: builtin/commit.c:308
+msgid "interactive add failed"
+msgstr "échec de l'ajout interactif"
+
+#: builtin/commit.c:341 builtin/commit.c:362 builtin/commit.c:412
+msgid "unable to write new_index file"
+msgstr "impossible d'écrire le fichier new_index"
+
+#: builtin/commit.c:393
+msgid "cannot do a partial commit during a merge."
+msgstr "impossible de faire une validation partielle pendant une fusion."
+
+#: builtin/commit.c:395
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr "impossible de faire une validation partielle pendant un picorage."
+
+#: builtin/commit.c:405
+msgid "cannot read the index"
+msgstr "impossible de lire l'index"
+
+#: builtin/commit.c:425
+msgid "unable to write temporary index file"
+msgstr "impossible d'écrire le fichier d'index temporaire"
+
+#: builtin/commit.c:513 builtin/commit.c:519
+#, c-format
+msgid "invalid commit: %s"
+msgstr "commit invalide : %s"
+
+#: builtin/commit.c:542
+msgid "malformed --author parameter"
+msgstr "paramètre --author mal formé"
+
+#: builtin/commit.c:562
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr "Chaîne ident mal formée : '%s'"
+
+#: builtin/commit.c:600 builtin/commit.c:633 builtin/commit.c:956
+#, c-format
+msgid "could not lookup commit %s"
+msgstr "impossible de rechercher le commit %s"
+
+#: builtin/commit.c:612 builtin/shortlog.c:270
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(lecture du message de journal depuis l'entrée standard)\n"
+
+#: builtin/commit.c:614
+msgid "could not read log from standard input"
+msgstr "impossible de lire le journal depuis l'entrée standard"
+
+#: builtin/commit.c:618
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "impossible de lire le fichier de journal '%s'"
+
+#: builtin/commit.c:624
+msgid "commit has empty message"
+msgstr "le commit a un message vide"
+
+#: builtin/commit.c:640
+msgid "could not read MERGE_MSG"
+msgstr "impossible de lire MERGE_MSG"
+
+#: builtin/commit.c:644
+msgid "could not read SQUASH_MSG"
+msgstr "impossible de lire SQUASH_MSG"
+
+#: builtin/commit.c:648
+#, c-format
+msgid "could not read '%s'"
+msgstr "impossible de lire '%s'"
+
+#: builtin/commit.c:709
+msgid "could not write commit template"
+msgstr "impossible d'écrire le modèle de commit"
+
+#: builtin/commit.c:720
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a merge.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Il semble que vous validiez une fusion.\n"
+"Si ce n'est pas le cas, veuillez supprimer le fichier\n"
+"\t%s\n"
+"et essayez à nouveau.\n"
+
+#: builtin/commit.c:725
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a cherry-pick.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Il semble que vous validiez un picorage.\n"
+"Si ce n'est pas le cas, veuillez supprimer le fichier\n"
+"\t%s\n"
+"et essayez à nouveau.\n"
+
+#: builtin/commit.c:737
+#, 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 ""
+"Veuillez saisir le message de validation pour vos modifications. Les lignes\n"
+"commençant par '%c' seront ignorées, et un message vide abandonne la validation.\n"
+
+#: builtin/commit.c:742
+#, c-format
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '%c' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+"Veuillez saisir le message de validation pour vos modifications. Les lignes\n"
+"commençant par '%c' seront conservées ; vous pouvez les supprimer vous-même\n"
+"si vous le souhaitez. Un message vide abandonne la validation.\n"
+
+#: builtin/commit.c:755
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr "%sAuteur :     %s"
+
+#: builtin/commit.c:762
+#, c-format
+msgid "%sCommitter: %s"
+msgstr "%sValidateur : %s"
+
+#: builtin/commit.c:782
+msgid "Cannot read index"
+msgstr "Impossible de lire l'index"
+
+#: builtin/commit.c:819
+msgid "Error building trees"
+msgstr "Erreur lors de la construction des arbres"
+
+#: builtin/commit.c:834 builtin/tag.c:359
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "Veuillez fournir le message en utilisant l'option -m ou -F.\n"
+
+#: builtin/commit.c:931
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr "Aucun auteur existant trouvé avec '%s'"
+
+#: builtin/commit.c:946 builtin/commit.c:1140
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr "Mode de fichier non suivi invalide '%s'"
+
+#: builtin/commit.c:976
+msgid "Using both --reset-author and --author does not make sense"
+msgstr "L'utilisation simultanée de --reset-author et --author n'a pas de sens"
+
+#: builtin/commit.c:987
+msgid "You have nothing to amend."
+msgstr "Il n'y a rien à corriger."
+
+#: builtin/commit.c:990
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr "Vous êtes en pleine fusion -- impossible de corriger (amend)."
+
+#: builtin/commit.c:992
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr "Vous êtes en plein picorage -- impossible de corriger (amend)."
+
+#: builtin/commit.c:995
+msgid "Options --squash and --fixup cannot be used together"
+msgstr "Les options --squash et --fixup ne peuvent pas être utilisées ensemble"
+
+#: builtin/commit.c:1005
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr "Une seule option parmi -c/-C/-F/--fixup peut être utilisée."
+
+#: builtin/commit.c:1007
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr "L'option -m ne peut pas être combinée avec -c/-C/-F/--fixup."
+
+#: builtin/commit.c:1015
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "--reset-author ne peut être utilisé qu'avec -C, -c ou --amend."
+
+#: builtin/commit.c:1032
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr "Une seule option parmi --include/--only/--all/--interactive/--patch peut être utilisée."
+
+#: builtin/commit.c:1034
+msgid "No paths with --include/--only does not make sense."
+msgstr "Aucun chemin avec les options --include/--only n'a pas de sens."
+
+#: builtin/commit.c:1036
+msgid "Clever... amending the last one with dirty index."
+msgstr "Malin... correction du dernier avec un index sale."
+
+#: builtin/commit.c:1038
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+
+#: builtin/commit.c:1048 builtin/tag.c:575
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr ""
+
+#: builtin/commit.c:1053
+msgid "Paths with -a does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1059 builtin/commit.c:1194
+msgid "--long and -z are incompatible"
+msgstr ""
+
+#: builtin/commit.c:1154 builtin/commit.c:1390
+msgid "show status concisely"
+msgstr ""
+
+#: builtin/commit.c:1156 builtin/commit.c:1392
+msgid "show branch information"
+msgstr ""
+
+#: builtin/commit.c:1158 builtin/commit.c:1394 builtin/push.c:426
+msgid "machine-readable output"
+msgstr ""
+
+#: builtin/commit.c:1161 builtin/commit.c:1396
+msgid "show status in long format (default)"
+msgstr ""
+
+#: builtin/commit.c:1164 builtin/commit.c:1399
+msgid "terminate entries with NUL"
+msgstr ""
+
+#: builtin/commit.c:1166 builtin/commit.c:1402 builtin/fast-export.c:659
+#: builtin/fast-export.c:662 builtin/tag.c:459
+msgid "mode"
+msgstr ""
+
+#: builtin/commit.c:1167 builtin/commit.c:1402
+msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
+msgstr ""
+
+#: builtin/commit.c:1170
+msgid "show ignored files"
+msgstr ""
+
+#: builtin/commit.c:1171 parse-options.h:151
+msgid "when"
+msgstr ""
+
+#: builtin/commit.c:1172
+msgid ""
+"ignore changes to submodules, optional when: all, dirty, untracked. "
+"(Default: all)"
+msgstr ""
+
+#: builtin/commit.c:1174
+msgid "list untracked files in columns"
+msgstr ""
+
+#: builtin/commit.c:1248
+msgid "couldn't look up newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1250
+msgid "could not parse newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1291
+msgid "detached HEAD"
+msgstr ""
+
+#: builtin/commit.c:1293
+msgid " (root-commit)"
+msgstr ""
+
+#: builtin/commit.c:1360
+msgid "suppress summary after successful commit"
+msgstr ""
+
+#: builtin/commit.c:1361
+msgid "show diff in commit message template"
+msgstr ""
+
+#: builtin/commit.c:1363
+msgid "Commit message options"
+msgstr ""
+
+#: builtin/commit.c:1364 builtin/tag.c:457
+msgid "read message from file"
+msgstr ""
+
+#: builtin/commit.c:1365
+msgid "author"
+msgstr ""
+
+#: builtin/commit.c:1365
+msgid "override author for commit"
+msgstr ""
+
+#: builtin/commit.c:1366 builtin/gc.c:178
+msgid "date"
+msgstr ""
+
+#: builtin/commit.c:1366
+msgid "override date for commit"
+msgstr ""
+
+#: builtin/commit.c:1367 builtin/merge.c:208 builtin/notes.c:533
+#: builtin/notes.c:690 builtin/tag.c:455
+msgid "message"
+msgstr ""
+
+#: builtin/commit.c:1367
+msgid "commit message"
+msgstr ""
+
+#: builtin/commit.c:1368
+msgid "reuse and edit message from specified commit"
+msgstr ""
+
+#: builtin/commit.c:1369
+msgid "reuse message from specified commit"
+msgstr ""
+
+#: builtin/commit.c:1370
+msgid "use autosquash formatted message to fixup specified commit"
+msgstr ""
+
+#: builtin/commit.c:1371
+msgid "use autosquash formatted message to squash specified commit"
+msgstr ""
+
+#: builtin/commit.c:1372
+msgid "the commit is authored by me now (used with -C/-c/--amend)"
+msgstr ""
+
+#: builtin/commit.c:1373 builtin/log.c:1113 builtin/revert.c:109
+msgid "add Signed-off-by:"
+msgstr ""
+
+#: builtin/commit.c:1374
+msgid "use specified template file"
+msgstr ""
+
+#: builtin/commit.c:1375
+msgid "force edit of commit"
+msgstr ""
+
+#: builtin/commit.c:1376
+msgid "default"
+msgstr ""
+
+#: builtin/commit.c:1376 builtin/tag.c:460
+msgid "how to strip spaces and #comments from message"
+msgstr ""
+
+#: builtin/commit.c:1377
+msgid "include status in commit message template"
+msgstr ""
+
+#: builtin/commit.c:1378 builtin/merge.c:215 builtin/tag.c:461
+msgid "key id"
+msgstr ""
+
+#: builtin/commit.c:1379 builtin/merge.c:216
+msgid "GPG sign commit"
+msgstr ""
+
+#. end commit message options
+#: builtin/commit.c:1382
+msgid "Commit contents options"
+msgstr ""
+
+#: builtin/commit.c:1383
+msgid "commit all changed files"
+msgstr ""
+
+#: builtin/commit.c:1384
+msgid "add specified files to index for commit"
+msgstr ""
+
+#: builtin/commit.c:1385
+msgid "interactively add files"
+msgstr ""
+
+#: builtin/commit.c:1386
+msgid "interactively add changes"
+msgstr ""
+
+#: builtin/commit.c:1387
+msgid "commit only specified files"
+msgstr ""
+
+#: builtin/commit.c:1388
+msgid "bypass pre-commit hook"
+msgstr ""
+
+#: builtin/commit.c:1389
+msgid "show what would be committed"
+msgstr ""
+
+#: builtin/commit.c:1400
+msgid "amend previous commit"
+msgstr ""
+
+#: builtin/commit.c:1401
+msgid "bypass post-rewrite hook"
+msgstr ""
+
+#: builtin/commit.c:1406
+msgid "ok to record an empty change"
+msgstr ""
+
+#: builtin/commit.c:1409
+msgid "ok to record a change with an empty message"
+msgstr ""
+
+#: builtin/commit.c:1441
+msgid "could not parse HEAD commit"
+msgstr ""
+
+#: builtin/commit.c:1479 builtin/merge.c:510
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr ""
+
+#: builtin/commit.c:1486
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr ""
+
+#: builtin/commit.c:1493
+msgid "could not read MERGE_MODE"
+msgstr ""
+
+#: builtin/commit.c:1512
+#, c-format
+msgid "could not read commit message: %s"
+msgstr ""
+
+#: builtin/commit.c:1526
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr ""
+
+#: builtin/commit.c:1531
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr ""
+
+#: builtin/commit.c:1546 builtin/merge.c:847 builtin/merge.c:872
+msgid "failed to write commit object"
+msgstr ""
+
+#: builtin/commit.c:1567
+msgid "cannot lock HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1571
+msgid "cannot update HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1582
+msgid ""
+"Repository has been updated, but unable to write\n"
+"new_index file. Check that disk is not full or quota is\n"
+"not exceeded, and then \"git reset HEAD\" to recover."
+msgstr ""
+
+#: builtin/config.c:7
+msgid "git config [options]"
+msgstr ""
+
+#: builtin/config.c:51
+msgid "Config file location"
+msgstr ""
+
+#: builtin/config.c:52
+msgid "use global config file"
+msgstr ""
+
+#: builtin/config.c:53
+msgid "use system config file"
+msgstr ""
+
+#: builtin/config.c:54
+msgid "use repository config file"
+msgstr ""
+
+#: builtin/config.c:55
+msgid "use given config file"
+msgstr ""
+
+#: builtin/config.c:56
+msgid "Action"
+msgstr ""
+
+#: builtin/config.c:57
+msgid "get value: name [value-regex]"
+msgstr ""
+
+#: builtin/config.c:58
+msgid "get all values: key [value-regex]"
+msgstr ""
+
+#: builtin/config.c:59
+msgid "get values for regexp: name-regex [value-regex]"
+msgstr ""
+
+#: builtin/config.c:60
+msgid "replace all matching variables: name value [value_regex]"
+msgstr ""
+
+#: builtin/config.c:61
+msgid "add a new variable: name value"
+msgstr ""
+
+#: builtin/config.c:62
+msgid "remove a variable: name [value-regex]"
+msgstr ""
+
+#: builtin/config.c:63
+msgid "remove all matches: name [value-regex]"
+msgstr ""
+
+#: builtin/config.c:64
+msgid "rename section: old-name new-name"
+msgstr ""
+
+#: builtin/config.c:65
+msgid "remove a section: name"
+msgstr ""
+
+#: builtin/config.c:66
+msgid "list all"
+msgstr ""
+
+#: builtin/config.c:67
+msgid "open an editor"
+msgstr ""
+
+#: builtin/config.c:68 builtin/config.c:69
+msgid "slot"
+msgstr ""
+
+#: builtin/config.c:68
+msgid "find the color configured: [default]"
+msgstr ""
+
+#: builtin/config.c:69
+msgid "find the color setting: [stdout-is-tty]"
+msgstr ""
+
+#: builtin/config.c:70
+msgid "Type"
+msgstr ""
+
+#: builtin/config.c:71
+msgid "value is \"true\" or \"false\""
+msgstr ""
+
+#: builtin/config.c:72
+msgid "value is decimal number"
+msgstr ""
+
+#: builtin/config.c:73
+msgid "value is --bool or --int"
+msgstr ""
+
+#: builtin/config.c:74
+msgid "value is a path (file or directory name)"
+msgstr ""
+
+#: builtin/config.c:75
+msgid "Other"
+msgstr ""
+
+#: builtin/config.c:76
+msgid "terminate values with NUL byte"
+msgstr ""
+
+#: builtin/config.c:77
+msgid "respect include directives on lookup"
+msgstr ""
+
+#: builtin/count-objects.c:82
+msgid "git count-objects [-v] [-H | --human-readable]"
+msgstr ""
+
+#: builtin/count-objects.c:97
+msgid "print sizes in human readable format"
+msgstr ""
+
+#: builtin/describe.c:15
+msgid "git describe [options] <committish>*"
+msgstr ""
+
+#: builtin/describe.c:16
+msgid "git describe [options] --dirty"
+msgstr ""
+
+#: builtin/describe.c:233
+#, c-format
+msgid "annotated tag %s not available"
+msgstr ""
+
+#: builtin/describe.c:237
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr ""
+
+#: builtin/describe.c:239
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr ""
+
+#: builtin/describe.c:266
+#, c-format
+msgid "Not a valid object name %s"
+msgstr ""
+
+#: builtin/describe.c:269
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s n'est pas un objet '%s' valide"
+
+#: builtin/describe.c:286
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr ""
+
+#: builtin/describe.c:288
+#, c-format
+msgid "searching to describe %s\n"
+msgstr ""
+
+#: builtin/describe.c:328
+#, c-format
+msgid "finished search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:352
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+
+#: builtin/describe.c:356
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+
+#: builtin/describe.c:377
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr ""
+
+#: builtin/describe.c:380
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:402
+msgid "find the tag that comes after the commit"
+msgstr ""
+
+#: builtin/describe.c:403
+msgid "debug search strategy on stderr"
+msgstr ""
+
+#: builtin/describe.c:404
+msgid "use any ref"
+msgstr ""
+
+#: builtin/describe.c:405
+msgid "use any tag, even unannotated"
+msgstr ""
+
+#: builtin/describe.c:406
+msgid "always use long format"
+msgstr ""
+
+#: builtin/describe.c:409
+msgid "only output exact matches"
+msgstr ""
+
+#: builtin/describe.c:411
+msgid "consider <n> most recent tags (default: 10)"
+msgstr ""
+
+#: builtin/describe.c:413
+msgid "only consider tags matching <pattern>"
+msgstr ""
+
+#: builtin/describe.c:415 builtin/name-rev.c:238
+msgid "show abbreviated commit object as fallback"
+msgstr ""
+
+#: builtin/describe.c:416
+msgid "mark"
+msgstr ""
+
+#: builtin/describe.c:417
+msgid "append <mark> on dirty working tree (default: \"-dirty\")"
+msgstr ""
+
+#: builtin/describe.c:435
+msgid "--long is incompatible with --abbrev=0"
+msgstr ""
+
+#: builtin/describe.c:461
+msgid "No names found, cannot describe anything."
+msgstr ""
+
+#: builtin/describe.c:481
+msgid "--dirty is incompatible with committishes"
+msgstr ""
+
+#: builtin/diff.c:79
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr ""
+
+#: builtin/diff.c:228
+#, c-format
+msgid "invalid option: %s"
+msgstr ""
+
+#: builtin/diff.c:305
+msgid "Not a git repository"
+msgstr ""
+
+#: builtin/diff.c:348
+#, c-format
+msgid "invalid object '%s' given."
+msgstr ""
+
+#: builtin/diff.c:353
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:363
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:371
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr ""
+
+#: builtin/fast-export.c:22
+msgid "git fast-export [rev-list-opts]"
+msgstr ""
+
+#: builtin/fast-export.c:658
+msgid "show progress after <n> objects"
+msgstr ""
+
+#: builtin/fast-export.c:660
+msgid "select handling of signed tags"
+msgstr ""
+
+#: builtin/fast-export.c:663
+msgid "select handling of tags that tag filtered objects"
+msgstr ""
+
+#: builtin/fast-export.c:666
+msgid "Dump marks to this file"
+msgstr ""
+
+#: builtin/fast-export.c:668
+msgid "Import marks from this file"
+msgstr ""
+
+#: builtin/fast-export.c:670
+msgid "Fake a tagger when tags lack one"
+msgstr ""
+
+#: builtin/fast-export.c:672
+msgid "Output full tree for each commit"
+msgstr ""
+
+#: builtin/fast-export.c:674
+msgid "Use the done feature to terminate the stream"
+msgstr ""
+
+#: builtin/fast-export.c:675
+msgid "Skip output of blob data"
+msgstr ""
+
+#: builtin/fetch.c:20
+msgid "git fetch [<options>] [<repository> [<refspec>...]]"
+msgstr ""
+
+#: builtin/fetch.c:21
+msgid "git fetch [<options>] <group>"
+msgstr ""
+
+#: builtin/fetch.c:22
+msgid "git fetch --multiple [<options>] [(<repository> | <group>)...]"
+msgstr ""
+
+#: builtin/fetch.c:23
+msgid "git fetch --all [<options>]"
+msgstr ""
+
+#: builtin/fetch.c:60
+msgid "fetch from all remotes"
+msgstr ""
+
+#: builtin/fetch.c:62
+msgid "append to .git/FETCH_HEAD instead of overwriting"
+msgstr ""
+
+#: builtin/fetch.c:64
+msgid "path to upload pack on remote end"
+msgstr ""
+
+#: builtin/fetch.c:65
+msgid "force overwrite of local branch"
+msgstr ""
+
+#: builtin/fetch.c:67
+msgid "fetch from multiple remotes"
+msgstr ""
+
+#: builtin/fetch.c:69
+msgid "fetch all tags and associated objects"
+msgstr ""
+
+#: builtin/fetch.c:71
+msgid "do not fetch all tags (--no-tags)"
+msgstr ""
+
+#: builtin/fetch.c:73
+msgid "prune remote-tracking branches no longer on remote"
+msgstr ""
+
+#: builtin/fetch.c:74
+msgid "on-demand"
+msgstr ""
+
+#: builtin/fetch.c:75
+msgid "control recursive fetching of submodules"
+msgstr ""
+
+#: builtin/fetch.c:79
+msgid "keep downloaded pack"
+msgstr ""
+
+#: builtin/fetch.c:81
+msgid "allow updating of HEAD ref"
+msgstr ""
+
+#: builtin/fetch.c:84
+msgid "deepen history of shallow clone"
+msgstr ""
+
+#: builtin/fetch.c:86
+msgid "convert to a complete repository"
+msgstr ""
+
+#: builtin/fetch.c:88 builtin/log.c:1130
+msgid "dir"
+msgstr ""
+
+#: builtin/fetch.c:89
+msgid "prepend this to submodule path output"
+msgstr ""
+
+#: builtin/fetch.c:92
+msgid "default mode for recursion"
+msgstr ""
+
+#: builtin/fetch.c:204
+msgid "Couldn't find remote ref HEAD"
+msgstr ""
+
+#: builtin/fetch.c:257
+#, c-format
+msgid "object %s not found"
+msgstr ""
+
+#: builtin/fetch.c:262
+msgid "[up to date]"
+msgstr ""
+
+#: builtin/fetch.c:276
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr ""
+
+#: builtin/fetch.c:277 builtin/fetch.c:363
+msgid "[rejected]"
+msgstr ""
+
+#: builtin/fetch.c:288
+msgid "[tag update]"
+msgstr ""
+
+#: builtin/fetch.c:290 builtin/fetch.c:325 builtin/fetch.c:343
+msgid "  (unable to update local ref)"
+msgstr ""
+
+#: builtin/fetch.c:308
+msgid "[new tag]"
+msgstr ""
+
+#: builtin/fetch.c:311
+msgid "[new branch]"
+msgstr ""
+
+#: builtin/fetch.c:314
+msgid "[new ref]"
+msgstr ""
+
+#: builtin/fetch.c:359
+msgid "unable to update local ref"
+msgstr ""
+
+#: builtin/fetch.c:359
+msgid "forced update"
+msgstr ""
+
+#: builtin/fetch.c:365
+msgid "(non-fast-forward)"
+msgstr ""
+
+#: builtin/fetch.c:396 builtin/fetch.c:688
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr "impossible d'ouvrir %s : %s\n"
+
+#: builtin/fetch.c:405
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s n'a pas envoyé tous les objets nécessaires\n"
+
+#: builtin/fetch.c:491
+#, c-format
+msgid "From %.*s\n"
+msgstr ""
+
+#: builtin/fetch.c:502
+#, 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:552
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:553
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:560
+msgid "[deleted]"
+msgstr ""
+
+#: builtin/fetch.c:561 builtin/remote.c:1055
+msgid "(none)"
+msgstr ""
+
+#: builtin/fetch.c:678
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+
+#: builtin/fetch.c:712
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr ""
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#: builtin/fetch.c:792
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr ""
+
+#: builtin/fetch.c:894
+#, c-format
+msgid "Fetching %s\n"
+msgstr ""
+
+#: builtin/fetch.c:896 builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr ""
+
+#: builtin/fetch.c:915
+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:935
+msgid "You need to specify a tag name."
+msgstr ""
+
+#: builtin/fetch.c:981
+msgid "--depth and --unshallow cannot be used together"
+msgstr ""
+
+#: builtin/fetch.c:983
+msgid "--unshallow on a complete repository does not make sense"
+msgstr ""
+
+#: builtin/fetch.c:1002
+msgid "fetch --all does not take a repository argument"
+msgstr ""
+
+#: builtin/fetch.c:1004
+msgid "fetch --all does not make sense with refspecs"
+msgstr ""
+
+#: builtin/fetch.c:1015
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr ""
+
+#: builtin/fetch.c:1023
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+
+#: builtin/fmt-merge-msg.c:13
+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:701
+#: builtin/merge.c:188 builtin/show-branch.c:655 builtin/show-ref.c:175
+#: builtin/tag.c:446 parse-options.h:133 parse-options.h:239
+msgid "n"
+msgstr ""
+
+#: builtin/fmt-merge-msg.c:664
+msgid "populate log with at most <n> entries from shortlog"
+msgstr ""
+
+#: builtin/fmt-merge-msg.c:667
+msgid "alias for --log (deprecated)"
+msgstr ""
+
+#: builtin/fmt-merge-msg.c:670
+msgid "text"
+msgstr ""
+
+#: builtin/fmt-merge-msg.c:671
+msgid "use <text> as start of message"
+msgstr ""
+
+#: builtin/fmt-merge-msg.c:672
+msgid "file to read from"
+msgstr ""
+
+#: builtin/for-each-ref.c:979
+msgid "git for-each-ref [options] [<pattern>]"
+msgstr ""
+
+#: builtin/for-each-ref.c:994
+msgid "quote placeholders suitably for shells"
+msgstr ""
+
+#: builtin/for-each-ref.c:996
+msgid "quote placeholders suitably for perl"
+msgstr ""
+
+#: builtin/for-each-ref.c:998
+msgid "quote placeholders suitably for python"
+msgstr ""
+
+#: builtin/for-each-ref.c:1000
+msgid "quote placeholders suitably for tcl"
+msgstr ""
+
+#: builtin/for-each-ref.c:1003
+msgid "show only <n> matched refs"
+msgstr ""
+
+#: builtin/for-each-ref.c:1004
+msgid "format"
+msgstr ""
+
+#: builtin/for-each-ref.c:1004
+msgid "format to use for the output"
+msgstr ""
+
+#: builtin/for-each-ref.c:1005
+msgid "key"
+msgstr ""
+
+#: builtin/for-each-ref.c:1006
+msgid "field name to sort on"
+msgstr ""
+
+#: builtin/fsck.c:608
+msgid "git fsck [options] [<object>...]"
+msgstr ""
+
+#: builtin/fsck.c:614
+msgid "show unreachable objects"
+msgstr ""
+
+#: builtin/fsck.c:615
+msgid "show dangling objects"
+msgstr ""
+
+#: builtin/fsck.c:616
+msgid "report tags"
+msgstr ""
+
+#: builtin/fsck.c:617
+msgid "report root nodes"
+msgstr ""
+
+#: builtin/fsck.c:618
+msgid "make index objects head nodes"
+msgstr ""
+
+#: builtin/fsck.c:619
+msgid "make reflogs head nodes (default)"
+msgstr ""
+
+#: builtin/fsck.c:620
+msgid "also consider packs and alternate objects"
+msgstr ""
+
+#: builtin/fsck.c:621
+msgid "enable more strict checking"
+msgstr ""
+
+#: builtin/fsck.c:623
+msgid "write dangling objects in .git/lost-found"
+msgstr ""
+
+#: builtin/fsck.c:624 builtin/prune.c:134
+msgid "show progress"
+msgstr ""
+
+#: builtin/gc.c:22
+msgid "git gc [options]"
+msgstr ""
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr ""
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr ""
+
+#: builtin/gc.c:179
+msgid "prune unreferenced objects"
+msgstr ""
+
+#: builtin/gc.c:181
+msgid "be more thorough (increased runtime)"
+msgstr ""
+
+#: builtin/gc.c:182
+msgid "enable auto-gc mode"
+msgstr ""
+
+#: builtin/gc.c:222
+#, c-format
+msgid ""
+"Auto packing the repository for optimum performance. You may also\n"
+"run \"git gc\" manually. See \"git help gc\" for more information.\n"
+msgstr ""
+
+#: builtin/gc.c:249
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+
+#: builtin/grep.c:22
+msgid "git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]"
+msgstr ""
+
+#: builtin/grep.c:217
+#, c-format
+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
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr ""
+
+#: builtin/grep.c:493
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr ""
+
+#: builtin/grep.c:551
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr ""
+
+#: builtin/grep.c:568
+#, c-format
+msgid "cannot open '%s'"
+msgstr ""
+
+#: builtin/grep.c:643
+msgid "search in index instead of in the work tree"
+msgstr ""
+
+#: builtin/grep.c:645
+msgid "find in contents not managed by git"
+msgstr ""
+
+#: builtin/grep.c:647
+msgid "search in both tracked and untracked files"
+msgstr ""
+
+#: builtin/grep.c:649
+msgid "search also in ignored files"
+msgstr ""
+
+#: builtin/grep.c:652
+msgid "show non-matching lines"
+msgstr ""
+
+#: builtin/grep.c:654
+msgid "case insensitive matching"
+msgstr ""
+
+#: builtin/grep.c:656
+msgid "match patterns only at word boundaries"
+msgstr ""
+
+#: builtin/grep.c:658
+msgid "process binary files as text"
+msgstr ""
+
+#: builtin/grep.c:660
+msgid "don't match patterns in binary files"
+msgstr ""
+
+#: builtin/grep.c:663
+msgid "descend at most <depth> levels"
+msgstr ""
+
+#: builtin/grep.c:667
+msgid "use extended POSIX regular expressions"
+msgstr ""
+
+#: builtin/grep.c:670
+msgid "use basic POSIX regular expressions (default)"
+msgstr ""
+
+#: builtin/grep.c:673
+msgid "interpret patterns as fixed strings"
+msgstr ""
+
+#: builtin/grep.c:676
+msgid "use Perl-compatible regular expressions"
+msgstr ""
+
+#: builtin/grep.c:679
+msgid "show line numbers"
+msgstr ""
+
+#: builtin/grep.c:680
+msgid "don't show filenames"
+msgstr ""
+
+#: builtin/grep.c:681
+msgid "show filenames"
+msgstr ""
+
+#: builtin/grep.c:683
+msgid "show filenames relative to top directory"
+msgstr ""
+
+#: builtin/grep.c:685
+msgid "show only filenames instead of matching lines"
+msgstr ""
+
+#: builtin/grep.c:687
+msgid "synonym for --files-with-matches"
+msgstr ""
+
+#: builtin/grep.c:690
+msgid "show only the names of files without match"
+msgstr ""
+
+#: builtin/grep.c:692
+msgid "print NUL after filenames"
+msgstr ""
+
+#: builtin/grep.c:694
+msgid "show the number of matches instead of matching lines"
+msgstr ""
+
+#: builtin/grep.c:695
+msgid "highlight matches"
+msgstr ""
+
+#: builtin/grep.c:697
+msgid "print empty line between matches from different files"
+msgstr ""
+
+#: builtin/grep.c:699
+msgid "show filename only once above matches from same file"
+msgstr ""
+
+#: builtin/grep.c:702
+msgid "show <n> context lines before and after matches"
+msgstr ""
+
+#: builtin/grep.c:705
+msgid "show <n> context lines before matches"
+msgstr ""
+
+#: builtin/grep.c:707
+msgid "show <n> context lines after matches"
+msgstr ""
+
+#: builtin/grep.c:708
+msgid "shortcut for -C NUM"
+msgstr ""
+
+#: builtin/grep.c:711
+msgid "show a line with the function name before matches"
+msgstr ""
+
+#: builtin/grep.c:713
+msgid "show the surrounding function"
+msgstr ""
+
+#: builtin/grep.c:716
+msgid "read patterns from file"
+msgstr ""
+
+#: builtin/grep.c:718
+msgid "match <pattern>"
+msgstr ""
+
+#: builtin/grep.c:720
+msgid "combine patterns specified with -e"
+msgstr ""
+
+#: builtin/grep.c:732
+msgid "indicate hit with exit status without output"
+msgstr ""
+
+#: builtin/grep.c:734
+msgid "show only matches from files that match all patterns"
+msgstr ""
+
+#: builtin/grep.c:736
+msgid "show parse tree for grep expression"
+msgstr ""
+
+#: builtin/grep.c:740
+msgid "pager"
+msgstr ""
+
+#: builtin/grep.c:740
+msgid "show matching files in the pager"
+msgstr ""
+
+#: builtin/grep.c:743
+msgid "allow calling of grep(1) (ignored by this build)"
+msgstr ""
+
+#: builtin/grep.c:744 builtin/show-ref.c:184
+msgid "show usage"
+msgstr ""
+
+#: builtin/grep.c:811
+msgid "no pattern given."
+msgstr ""
+
+#: builtin/grep.c:866
+msgid "--open-files-in-pager only works on the worktree"
+msgstr ""
+
+#: builtin/grep.c:889
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr ""
+
+#: builtin/grep.c:894
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr ""
+
+#: builtin/grep.c:897
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+
+#: builtin/grep.c:905
+msgid "both --cached and trees are given."
+msgstr ""
+
+#: builtin/hash-object.c:60
+msgid ""
+"git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] "
+"<file>..."
+msgstr ""
+
+#: builtin/hash-object.c:61
+msgid "git hash-object  --stdin-paths < <list-of-paths>"
+msgstr ""
+
+#: builtin/hash-object.c:72
+msgid "type"
+msgstr ""
+
+#: builtin/hash-object.c:72
+msgid "object type"
+msgstr ""
+
+#: builtin/hash-object.c:73
+msgid "write the object into the object database"
+msgstr ""
+
+#: builtin/hash-object.c:74
+msgid "read the object from stdin"
+msgstr ""
+
+#: builtin/hash-object.c:76
+msgid "store file as is without filters"
+msgstr ""
+
+#: builtin/hash-object.c:77
+msgid "process file as it were from this path"
+msgstr ""
+
+#: builtin/help.c:43
+msgid "print all available commands"
+msgstr ""
+
+#: builtin/help.c:44
+msgid "print list of useful guides"
+msgstr ""
+
+#: builtin/help.c:45
+msgid "show man page"
+msgstr ""
+
+#: builtin/help.c:46
+msgid "show manual in web browser"
+msgstr ""
+
+#: builtin/help.c:48
+msgid "show info page"
+msgstr ""
+
+#: builtin/help.c:54
+msgid "git help [--all] [--guides] [--man|--web|--info] [command]"
+msgstr ""
+
+#: builtin/help.c:66
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr ""
+
+#: builtin/help.c:94
+msgid "Failed to start emacsclient."
+msgstr ""
+
+#: builtin/help.c:107
+msgid "Failed to parse emacsclient version."
+msgstr ""
+
+#: builtin/help.c:115
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr ""
+
+#: builtin/help.c:133 builtin/help.c:161 builtin/help.c:170 builtin/help.c:178
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr ""
+
+#: builtin/help.c:218
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+
+#: builtin/help.c:230
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+
+#: builtin/help.c:351
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr ""
+
+#: builtin/help.c:368
+msgid "no man viewer handled the request"
+msgstr ""
+
+#: builtin/help.c:376
+msgid "no info viewer handled the request"
+msgstr ""
+
+#: builtin/help.c:422
+msgid "Defining attributes per path"
+msgstr ""
+
+#: builtin/help.c:423
+msgid "A Git glossary"
+msgstr ""
+
+#: builtin/help.c:424
+msgid "Specifies intentionally untracked files to ignore"
+msgstr ""
+
+#: builtin/help.c:425
+msgid "Defining submodule properties"
+msgstr ""
+
+#: builtin/help.c:426
+msgid "Specifying revisions and ranges for Git"
+msgstr ""
+
+#: builtin/help.c:427
+msgid "A tutorial introduction to Git (for version 1.5.1 or newer)"
+msgstr ""
+
+#: builtin/help.c:428
+msgid "An overview of recommended workflows with Git"
+msgstr ""
+
+#: builtin/help.c:440
+msgid "The common Git guides are:\n"
+msgstr ""
+
+#: builtin/help.c:462 builtin/help.c:478
+#, c-format
+msgid "usage: %s%s"
+msgstr ""
+
+#: builtin/help.c:494
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr ""
+
+#: builtin/index-pack.c:182
+#, c-format
+msgid "object type mismatch at %s"
+msgstr ""
+
+#: builtin/index-pack.c:202
+msgid "object of unexpected type"
+msgstr ""
+
+#: builtin/index-pack.c:239
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:249
+msgid "early EOF"
+msgstr ""
+
+#: builtin/index-pack.c:250
+msgid "read error on input"
+msgstr ""
+
+#: builtin/index-pack.c:262
+msgid "used more bytes than were available"
+msgstr ""
+
+#: builtin/index-pack.c:269
+msgid "pack too large for current definition of off_t"
+msgstr ""
+
+#: builtin/index-pack.c:285
+#, c-format
+msgid "unable to create '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:290
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:304
+msgid "pack signature mismatch"
+msgstr ""
+
+#: builtin/index-pack.c:306
+#, c-format
+msgid "pack version %<PRIu32> unsupported"
+msgstr ""
+
+#: builtin/index-pack.c:324
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr ""
+
+#: builtin/index-pack.c:446
+#, c-format
+msgid "inflate returned %d"
+msgstr ""
+
+#: builtin/index-pack.c:495
+msgid "offset value overflow for delta base object"
+msgstr ""
+
+#: builtin/index-pack.c:503
+msgid "delta base offset is out of bound"
+msgstr ""
+
+#: builtin/index-pack.c:511
+#, c-format
+msgid "unknown object type %d"
+msgstr ""
+
+#: builtin/index-pack.c:542
+msgid "cannot pread pack file"
+msgstr ""
+
+#: builtin/index-pack.c:544
+#, 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:570
+msgid "serious inflate inconsistency"
+msgstr ""
+
+#: builtin/index-pack.c:661 builtin/index-pack.c:667 builtin/index-pack.c:690
+#: builtin/index-pack.c:724 builtin/index-pack.c:733
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr ""
+
+#: builtin/index-pack.c:664 builtin/pack-objects.c:170
+#: builtin/pack-objects.c:262
+#, c-format
+msgid "unable to read %s"
+msgstr ""
+
+#: builtin/index-pack.c:730
+#, c-format
+msgid "cannot read existing object %s"
+msgstr ""
+
+#: builtin/index-pack.c:744
+#, c-format
+msgid "invalid blob object %s"
+msgstr ""
+
+#: builtin/index-pack.c:759
+#, c-format
+msgid "invalid %s"
+msgstr ""
+
+#: builtin/index-pack.c:761
+msgid "Error in object"
+msgstr ""
+
+#: builtin/index-pack.c:763
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr ""
+
+#: builtin/index-pack.c:833 builtin/index-pack.c:863
+msgid "failed to apply delta"
+msgstr ""
+
+#: builtin/index-pack.c:1004
+msgid "Receiving objects"
+msgstr ""
+
+#: builtin/index-pack.c:1004
+msgid "Indexing objects"
+msgstr ""
+
+#: builtin/index-pack.c:1030
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr ""
+
+#: builtin/index-pack.c:1035
+msgid "cannot fstat packfile"
+msgstr ""
+
+#: builtin/index-pack.c:1038
+msgid "pack has junk at the end"
+msgstr ""
+
+#: builtin/index-pack.c:1049
+msgid "confusion beyond insanity in parse_pack_objects()"
+msgstr ""
+
+#: builtin/index-pack.c:1072
+msgid "Resolving deltas"
+msgstr ""
+
+#: builtin/index-pack.c:1082
+#, c-format
+msgid "unable to create thread: %s"
+msgstr ""
+
+#: builtin/index-pack.c:1124
+msgid "confusion beyond insanity"
+msgstr ""
+
+#: builtin/index-pack.c:1132
+#, c-format
+msgid "completed with %d local objects"
+msgstr ""
+
+#: builtin/index-pack.c:1142
+#, c-format
+msgid "Unexpected tail checksum for %s (disk corruption?)"
+msgstr ""
+
+#: builtin/index-pack.c:1146
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1171
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr ""
+
+#: builtin/index-pack.c:1250
+#, c-format
+msgid "local object %s is corrupt"
+msgstr ""
+
+#: builtin/index-pack.c:1274
+msgid "error while closing pack file"
+msgstr ""
+
+#: builtin/index-pack.c:1287
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1295
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1308
+msgid "cannot store pack file"
+msgstr ""
+
+#: builtin/index-pack.c:1319
+msgid "cannot store index file"
+msgstr ""
+
+#: builtin/index-pack.c:1352
+#, c-format
+msgid "bad pack.indexversion=%<PRIu32>"
+msgstr ""
+
+#: builtin/index-pack.c:1358
+#, c-format
+msgid "invalid number of threads specified (%d)"
+msgstr ""
+
+#: builtin/index-pack.c:1362 builtin/index-pack.c:1535
+#, c-format
+msgid "no threads support, ignoring %s"
+msgstr ""
+
+#: builtin/index-pack.c:1420
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1422
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr ""
+
+#: builtin/index-pack.c:1469
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1476
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1503
+msgid "Cannot come back to cwd"
+msgstr ""
+
+#: builtin/index-pack.c:1547 builtin/index-pack.c:1550
+#: builtin/index-pack.c:1562 builtin/index-pack.c:1566
+#, c-format
+msgid "bad %s"
+msgstr ""
+
+#: builtin/index-pack.c:1580
+msgid "--fix-thin cannot be used without --stdin"
+msgstr ""
+
+#: builtin/index-pack.c:1584 builtin/index-pack.c:1594
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr ""
+
+#: builtin/index-pack.c:1603
+msgid "--verify with no packfile name given"
+msgstr ""
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr ""
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr ""
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr ""
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr ""
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr ""
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr ""
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr ""
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr ""
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr ""
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr ""
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr ""
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr ""
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr ""
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr ""
+
+#: builtin/init-db.c:323 builtin/init-db.c:326
+#, c-format
+msgid "%s already exists"
+msgstr "%s existe déjà"
+
+#: builtin/init-db.c:355
+#, c-format
+msgid "unable to handle file type %d"
+msgstr ""
+
+#: builtin/init-db.c:358
+#, c-format
+msgid "unable to move %s to %s"
+msgstr ""
+
+#: builtin/init-db.c:363
+#, c-format
+msgid "Could not create git link %s"
+msgstr ""
+
+#.
+#. * TRANSLATORS: The first '%s' is either "Reinitialized
+#. * existing" or "Initialized empty", the second " shared" or
+#. * "", and the last '%s%s' is the verbatim directory name.
+#.
+#: builtin/init-db.c:420
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr "%s%s Dépôt git dans %s%s\n"
+
+#: builtin/init-db.c:421
+msgid "Reinitialized existing"
+msgstr ""
+
+#: builtin/init-db.c:421
+msgid "Initialized empty"
+msgstr ""
+
+#: builtin/init-db.c:422
+msgid " shared"
+msgstr ""
+
+#: builtin/init-db.c:441
+msgid "cannot tell cwd"
+msgstr ""
+
+#: builtin/init-db.c:467
+msgid ""
+"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--"
+"shared[=<permissions>]] [directory]"
+msgstr ""
+
+#: builtin/init-db.c:490
+msgid "permissions"
+msgstr ""
+
+#: builtin/init-db.c:491
+msgid "specify that the git repository is to be shared amongst several users"
+msgstr ""
+
+#: builtin/init-db.c:493 builtin/prune-packed.c:77
+msgid "be quiet"
+msgstr ""
+
+#: builtin/init-db.c:522 builtin/init-db.c:529
+#, c-format
+msgid "cannot mkdir %s"
+msgstr ""
+
+#: builtin/init-db.c:533
+#, c-format
+msgid "cannot chdir to %s"
+msgstr ""
+
+#: builtin/init-db.c:555
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+
+#: builtin/init-db.c:579
+msgid "Cannot access current working directory"
+msgstr ""
+
+#: builtin/init-db.c:586
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr ""
+
+#: builtin/log.c:40
+msgid "git log [<options>] [<revision range>] [[--] <path>...]\n"
+msgstr ""
+
+#: builtin/log.c:41
+msgid "   or: git show [options] <object>..."
+msgstr ""
+
+#: builtin/log.c:103
+msgid "suppress diff output"
+msgstr ""
+
+#: builtin/log.c:104
+msgid "show source"
+msgstr ""
+
+#: builtin/log.c:105
+msgid "Use mail map file"
+msgstr ""
+
+#: builtin/log.c:106
+msgid "decorate options"
+msgstr ""
+
+#: builtin/log.c:199
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr ""
+
+#: builtin/log.c:422 builtin/log.c:514
+#, c-format
+msgid "Could not read object %s"
+msgstr ""
+
+#: builtin/log.c:538
+#, c-format
+msgid "Unknown type: %d"
+msgstr ""
+
+#: builtin/log.c:638
+msgid "format.headers without value"
+msgstr ""
+
+#: builtin/log.c:720
+msgid "name of output directory is too long"
+msgstr ""
+
+#: builtin/log.c:736
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr ""
+
+#: builtin/log.c:750
+msgid "Need exactly one range."
+msgstr ""
+
+#: builtin/log.c:758
+msgid "Not a range."
+msgstr ""
+
+#: builtin/log.c:860
+msgid "Cover letter needs email format"
+msgstr ""
+
+#: builtin/log.c:936
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr ""
+
+#: builtin/log.c:964
+msgid "git format-patch [options] [<since> | <revision range>]"
+msgstr ""
+
+#: builtin/log.c:1009
+msgid "Two output directories?"
+msgstr ""
+
+#: builtin/log.c:1108
+msgid "use [PATCH n/m] even with a single patch"
+msgstr ""
+
+#: builtin/log.c:1111
+msgid "use [PATCH] even with multiple patches"
+msgstr ""
+
+#: builtin/log.c:1115
+msgid "print patches to standard out"
+msgstr ""
+
+#: builtin/log.c:1117
+msgid "generate a cover letter"
+msgstr ""
+
+#: builtin/log.c:1119
+msgid "use simple number sequence for output file names"
+msgstr ""
+
+#: builtin/log.c:1120
+msgid "sfx"
+msgstr ""
+
+#: builtin/log.c:1121
+msgid "use <sfx> instead of '.patch'"
+msgstr ""
+
+#: builtin/log.c:1123
+msgid "start numbering patches at <n> instead of 1"
+msgstr ""
+
+#: builtin/log.c:1125
+msgid "mark the series as Nth re-roll"
+msgstr ""
+
+#: builtin/log.c:1127
+msgid "Use [<prefix>] instead of [PATCH]"
+msgstr ""
+
+#: builtin/log.c:1130
+msgid "store resulting files in <dir>"
+msgstr ""
+
+#: builtin/log.c:1133
+msgid "don't strip/add [PATCH]"
+msgstr ""
+
+#: builtin/log.c:1136
+msgid "don't output binary diffs"
+msgstr ""
+
+#: builtin/log.c:1138
+msgid "don't include a patch matching a commit upstream"
+msgstr ""
+
+#: builtin/log.c:1140
+msgid "show patch format instead of default (patch + stat)"
+msgstr ""
+
+#: builtin/log.c:1142
+msgid "Messaging"
+msgstr ""
+
+#: builtin/log.c:1143
+msgid "header"
+msgstr ""
+
+#: builtin/log.c:1144
+msgid "add email header"
+msgstr ""
+
+#: builtin/log.c:1145 builtin/log.c:1147
+msgid "email"
+msgstr ""
+
+#: builtin/log.c:1145
+msgid "add To: header"
+msgstr ""
+
+#: builtin/log.c:1147
+msgid "add Cc: header"
+msgstr ""
+
+#: builtin/log.c:1149
+msgid "message-id"
+msgstr ""
+
+#: builtin/log.c:1150
+msgid "make first mail a reply to <message-id>"
+msgstr ""
+
+#: builtin/log.c:1151 builtin/log.c:1154
+msgid "boundary"
+msgstr ""
+
+#: builtin/log.c:1152
+msgid "attach the patch"
+msgstr ""
+
+#: builtin/log.c:1155
+msgid "inline the patch"
+msgstr ""
+
+#: builtin/log.c:1159
+msgid "enable message threading, styles: shallow, deep"
+msgstr ""
+
+#: builtin/log.c:1161
+msgid "signature"
+msgstr ""
+
+#: builtin/log.c:1162
+msgid "add a signature"
+msgstr ""
+
+#: builtin/log.c:1164
+msgid "don't print the patch filenames"
+msgstr ""
+
+#: builtin/log.c:1248
+msgid "-n and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1250
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1258
+msgid "--name-only does not make sense"
+msgstr ""
+
+#: builtin/log.c:1260
+msgid "--name-status does not make sense"
+msgstr ""
+
+#: builtin/log.c:1262
+msgid "--check does not make sense"
+msgstr ""
+
+#: builtin/log.c:1285
+msgid "standard output, or directory, which one?"
+msgstr ""
+
+#: builtin/log.c:1287
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr ""
+
+#: builtin/log.c:1435
+msgid "Failed to create output files"
+msgstr ""
+
+#: builtin/log.c:1484
+msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
+msgstr ""
+
+#: builtin/log.c:1539
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+
+#: builtin/log.c:1552 builtin/log.c:1554 builtin/log.c:1566
+#, c-format
+msgid "Unknown commit %s"
+msgstr ""
+
+#: builtin/ls-files.c:402
+msgid "git ls-files [options] [<file>...]"
+msgstr ""
+
+#: builtin/ls-files.c:459
+msgid "identify the file status with tags"
+msgstr ""
+
+#: builtin/ls-files.c:461
+msgid "use lowercase letters for 'assume unchanged' files"
+msgstr ""
+
+#: builtin/ls-files.c:463
+msgid "show cached files in the output (default)"
+msgstr ""
+
+#: builtin/ls-files.c:465
+msgid "show deleted files in the output"
+msgstr ""
+
+#: builtin/ls-files.c:467
+msgid "show modified files in the output"
+msgstr ""
+
+#: builtin/ls-files.c:469
+msgid "show other files in the output"
+msgstr ""
+
+#: builtin/ls-files.c:471
+msgid "show ignored files in the output"
+msgstr ""
+
+#: builtin/ls-files.c:474
+msgid "show staged contents' object name in the output"
+msgstr ""
+
+#: builtin/ls-files.c:476
+msgid "show files on the filesystem that need to be removed"
+msgstr ""
+
+#: builtin/ls-files.c:478
+msgid "show 'other' directories' name only"
+msgstr ""
+
+#: builtin/ls-files.c:481
+msgid "don't show empty directories"
+msgstr ""
+
+#: builtin/ls-files.c:484
+msgid "show unmerged files in the output"
+msgstr ""
+
+#: builtin/ls-files.c:486
+msgid "show resolve-undo information"
+msgstr ""
+
+#: builtin/ls-files.c:488
+msgid "skip files matching pattern"
+msgstr ""
+
+#: builtin/ls-files.c:491
+msgid "exclude patterns are read from <file>"
+msgstr ""
+
+#: builtin/ls-files.c:494
+msgid "read additional per-directory exclude patterns in <file>"
+msgstr ""
+
+#: builtin/ls-files.c:496
+msgid "add the standard git exclusions"
+msgstr ""
+
+#: builtin/ls-files.c:499
+msgid "make the output relative to the project top directory"
+msgstr ""
+
+#: builtin/ls-files.c:502
+msgid "if any <file> is not in the index, treat this as an error"
+msgstr ""
+
+#: builtin/ls-files.c:503
+msgid "tree-ish"
+msgstr ""
+
+#: builtin/ls-files.c:504
+msgid "pretend that paths removed since <tree-ish> are still present"
+msgstr ""
+
+#: builtin/ls-files.c:506
+msgid "show debugging data"
+msgstr ""
+
+#: builtin/ls-tree.c:27
+msgid "git ls-tree [<options>] <tree-ish> [<path>...]"
+msgstr ""
+
+#: builtin/ls-tree.c:125
+msgid "only show trees"
+msgstr ""
+
+#: builtin/ls-tree.c:127
+msgid "recurse into subtrees"
+msgstr ""
+
+#: builtin/ls-tree.c:129
+msgid "show trees when recursing"
+msgstr ""
+
+#: builtin/ls-tree.c:132
+msgid "terminate entries with NUL byte"
+msgstr ""
+
+#: builtin/ls-tree.c:133
+msgid "include object size"
+msgstr ""
+
+#: builtin/ls-tree.c:135 builtin/ls-tree.c:137
+msgid "list only filenames"
+msgstr ""
+
+#: builtin/ls-tree.c:140
+msgid "use full path names"
+msgstr ""
+
+#: builtin/ls-tree.c:142
+msgid "list entire tree; not just current directory (implies --full-name)"
+msgstr ""
+
+#: builtin/merge.c:43
+msgid "git merge [options] [<commit>...]"
+msgstr ""
+
+#: builtin/merge.c:44
+msgid "git merge [options] <msg> HEAD <commit>"
+msgstr ""
+
+#: builtin/merge.c:45
+msgid "git merge --abort"
+msgstr ""
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr ""
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr ""
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr ""
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr ""
+
+#: builtin/merge.c:183
+msgid "do not show a diffstat at the end of the merge"
+msgstr ""
+
+#: builtin/merge.c:186
+msgid "show a diffstat at the end of the merge"
+msgstr ""
+
+#: builtin/merge.c:187
+msgid "(synonym to --stat)"
+msgstr ""
+
+#: builtin/merge.c:189
+msgid "add (at most <n>) entries from shortlog to merge commit message"
+msgstr ""
+
+#: builtin/merge.c:192
+msgid "create a single commit instead of doing a merge"
+msgstr ""
+
+#: builtin/merge.c:194
+msgid "perform a commit if the merge succeeds (default)"
+msgstr ""
+
+#: builtin/merge.c:196
+msgid "edit message before committing"
+msgstr ""
+
+#: builtin/merge.c:198
+msgid "allow fast-forward (default)"
+msgstr ""
+
+#: builtin/merge.c:200
+msgid "abort if fast-forward is not possible"
+msgstr ""
+
+#: builtin/merge.c:203
+msgid "Verify that the named commit has a valid GPG signature"
+msgstr ""
+
+#: builtin/merge.c:204 builtin/notes.c:866 builtin/revert.c:112
+msgid "strategy"
+msgstr ""
+
+#: builtin/merge.c:205
+msgid "merge strategy to use"
+msgstr ""
+
+#: builtin/merge.c:206
+msgid "option=value"
+msgstr ""
+
+#: builtin/merge.c:207
+msgid "option for selected merge strategy"
+msgstr ""
+
+#: builtin/merge.c:209
+msgid "merge commit message (for a non-fast-forward merge)"
+msgstr ""
+
+#: builtin/merge.c:213
+msgid "abort the current in-progress merge"
+msgstr ""
+
+#: builtin/merge.c:242
+msgid "could not run stash."
+msgstr ""
+
+#: builtin/merge.c:247
+msgid "stash failed"
+msgstr ""
+
+#: builtin/merge.c:252
+#, c-format
+msgid "not a valid object: %s"
+msgstr ""
+
+#: builtin/merge.c:271 builtin/merge.c:288
+msgid "read-tree failed"
+msgstr ""
+
+#: builtin/merge.c:318
+msgid " (nothing to squash)"
+msgstr " (rien à compresser)"
+
+#: builtin/merge.c:331
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:363
+msgid "Writing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:365
+msgid "Finishing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:388
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:438
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr ""
+
+#: builtin/merge.c:550
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr ""
+
+#: builtin/merge.c:643
+msgid "git write-tree failed to write a tree"
+msgstr ""
+
+#: builtin/merge.c:671
+msgid "Not handling anything other than two heads merge."
+msgstr ""
+
+#: builtin/merge.c:685
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr ""
+
+#: builtin/merge.c:699
+#, c-format
+msgid "unable to write %s"
+msgstr ""
+
+#: builtin/merge.c:788
+#, c-format
+msgid "Could not read from '%s'"
+msgstr ""
+
+#: builtin/merge.c:797
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:803
+#, c-format
+msgid ""
+"Please enter a commit message to explain why this merge is necessary,\n"
+"especially if it merges an updated upstream into a topic branch.\n"
+"\n"
+"Lines starting with '%c' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+
+#: builtin/merge.c:827
+msgid "Empty commit message."
+msgstr ""
+
+#: builtin/merge.c:839
+#, c-format
+msgid "Wonderful.\n"
+msgstr ""
+
+#: builtin/merge.c:904
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+
+#: builtin/merge.c:920
+#, c-format
+msgid "'%s' is not a commit"
+msgstr ""
+
+#: builtin/merge.c:961
+msgid "No current branch."
+msgstr ""
+
+#: builtin/merge.c:963
+msgid "No remote for the current branch."
+msgstr ""
+
+#: builtin/merge.c:965
+msgid "No default upstream defined for the current branch."
+msgstr ""
+
+#: builtin/merge.c:970
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr ""
+
+#: builtin/merge.c:1057 builtin/merge.c:1214
+#, c-format
+msgid "%s - not something we can merge"
+msgstr ""
+
+#: builtin/merge.c:1125
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr ""
+
+#: builtin/merge.c:1141 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:1144 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1148
+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:1151
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1160
+msgid "You cannot combine --squash with --no-ff."
+msgstr ""
+
+#: builtin/merge.c:1165
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr ""
+
+#: builtin/merge.c:1172
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr ""
+
+#: builtin/merge.c:1204
+msgid "Can merge only exactly one commit into empty head"
+msgstr ""
+
+#: builtin/merge.c:1207
+msgid "Squash commit into empty head not supported yet"
+msgstr ""
+
+#: builtin/merge.c:1209
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr ""
+
+#: builtin/merge.c:1265
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr ""
+
+#: builtin/merge.c:1268
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr ""
+
+#. 'N'
+#: builtin/merge.c:1271
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr ""
+
+#: builtin/merge.c:1274
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr ""
+
+#: builtin/merge.c:1358
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr ""
+
+#: builtin/merge.c:1397
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr ""
+
+#: builtin/merge.c:1404
+#, c-format
+msgid "Nope.\n"
+msgstr ""
+
+#: builtin/merge.c:1436
+msgid "Not possible to fast-forward, aborting."
+msgstr ""
+
+#: builtin/merge.c:1459 builtin/merge.c:1538
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr ""
+
+#: builtin/merge.c:1463
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr ""
+
+#: builtin/merge.c:1529
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:1531
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr ""
+
+#: builtin/merge.c:1540
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr ""
+
+#: builtin/merge.c:1552
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+msgstr ""
+
+#: builtin/merge-base.c:26
+msgid "git merge-base [-a|--all] <commit> <commit>..."
+msgstr ""
+
+#: builtin/merge-base.c:27
+msgid "git merge-base [-a|--all] --octopus <commit>..."
+msgstr ""
+
+#: builtin/merge-base.c:28
+msgid "git merge-base --independent <commit>..."
+msgstr ""
+
+#: builtin/merge-base.c:29
+msgid "git merge-base --is-ancestor <commit> <commit>"
+msgstr ""
+
+#: builtin/merge-base.c:98
+msgid "output all common ancestors"
+msgstr ""
+
+#: builtin/merge-base.c:99
+msgid "find ancestors for a single n-way merge"
+msgstr ""
+
+#: builtin/merge-base.c:100
+msgid "list revs not reachable from others"
+msgstr ""
+
+#: builtin/merge-base.c:102
+msgid "is the first one ancestor of the other?"
+msgstr ""
+
+#: builtin/merge-file.c:8
+msgid ""
+"git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file "
+"file2"
+msgstr ""
+
+#: builtin/merge-file.c:33
+msgid "send results to standard output"
+msgstr ""
+
+#: builtin/merge-file.c:34
+msgid "use a diff3 based merge"
+msgstr ""
+
+#: builtin/merge-file.c:35
+msgid "for conflicts, use our version"
+msgstr ""
+
+#: builtin/merge-file.c:37
+msgid "for conflicts, use their version"
+msgstr ""
+
+#: builtin/merge-file.c:39
+msgid "for conflicts, use a union version"
+msgstr ""
+
+#: builtin/merge-file.c:42
+msgid "for conflicts, use this marker size"
+msgstr ""
+
+#: builtin/merge-file.c:43
+msgid "do not warn about conflicts"
+msgstr ""
+
+#: builtin/merge-file.c:45
+msgid "set labels for file1/orig_file/file2"
+msgstr ""
+
+#: builtin/mktree.c:67
+msgid "git mktree [-z] [--missing] [--batch]"
+msgstr ""
+
+#: builtin/mktree.c:153
+msgid "input is NUL terminated"
+msgstr ""
+
+#: builtin/mktree.c:154 builtin/write-tree.c:24
+msgid "allow missing objects"
+msgstr ""
+
+#: builtin/mktree.c:155
+msgid "allow creation of more than one tree"
+msgstr ""
+
+#: builtin/mv.c:14
+msgid "git mv [options] <source>... <destination>"
+msgstr ""
+
+#: builtin/mv.c:64
+msgid "force move/rename even if target exists"
+msgstr ""
+
+#: builtin/mv.c:65
+msgid "skip move/rename errors"
+msgstr ""
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr ""
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr ""
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr ""
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+msgstr ""
+
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
+msgstr ""
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr ""
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr ""
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr ""
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr ""
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr ""
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr ""
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s, source=%s, destination=%s"
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr ""
+
+#: builtin/mv.c:215 builtin/remote.c:731
+#, c-format
+msgid "renaming '%s' failed"
+msgstr ""
+
+#: builtin/name-rev.c:175
+msgid "git name-rev [options] <commit>..."
+msgstr ""
+
+#: builtin/name-rev.c:176
+msgid "git name-rev [options] --all"
+msgstr ""
+
+#: builtin/name-rev.c:177
+msgid "git name-rev [options] --stdin"
+msgstr ""
+
+#: builtin/name-rev.c:229
+msgid "print only names (no SHA-1)"
+msgstr ""
+
+#: builtin/name-rev.c:230
+msgid "only use tags to name the commits"
+msgstr ""
+
+#: builtin/name-rev.c:232
+msgid "only use refs matching <pattern>"
+msgstr ""
+
+#: builtin/name-rev.c:234
+msgid "list all commits reachable from all refs"
+msgstr ""
+
+#: builtin/name-rev.c:235
+msgid "read from stdin"
+msgstr ""
+
+#: builtin/name-rev.c:236
+msgid "allow to print `undefined` names"
+msgstr ""
+
+#: builtin/notes.c:26
+msgid "git notes [--ref <notes_ref>] [list [<object>]]"
+msgstr ""
+
+#: builtin/notes.c:27
+msgid ""
+"git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) "
+"<object>] [<object>]"
+msgstr ""
+
+#: builtin/notes.c:28
+msgid "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"
+msgstr ""
+
+#: builtin/notes.c:29
+msgid ""
+"git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) "
+"<object>] [<object>]"
+msgstr ""
+
+#: builtin/notes.c:30
+msgid "git notes [--ref <notes_ref>] edit [<object>]"
+msgstr ""
+
+#: builtin/notes.c:31
+msgid "git notes [--ref <notes_ref>] show [<object>]"
+msgstr ""
+
+#: builtin/notes.c:32
+msgid ""
+"git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"
+msgstr ""
+
+#: builtin/notes.c:33
+msgid "git notes merge --commit [-v | -q]"
+msgstr ""
+
+#: builtin/notes.c:34
+msgid "git notes merge --abort [-v | -q]"
+msgstr ""
+
+#: builtin/notes.c:35
+msgid "git notes [--ref <notes_ref>] remove [<object>...]"
+msgstr ""
+
+#: builtin/notes.c:36
+msgid "git notes [--ref <notes_ref>] prune [-n | -v]"
+msgstr ""
+
+#: builtin/notes.c:37
+msgid "git notes [--ref <notes_ref>] get-ref"
+msgstr ""
+
+#: builtin/notes.c:42
+msgid "git notes [list [<object>]]"
+msgstr ""
+
+#: builtin/notes.c:47
+msgid "git notes add [<options>] [<object>]"
+msgstr ""
+
+#: builtin/notes.c:52
+msgid "git notes copy [<options>] <from-object> <to-object>"
+msgstr ""
+
+#: builtin/notes.c:53
+msgid "git notes copy --stdin [<from-object> <to-object>]..."
+msgstr ""
+
+#: builtin/notes.c:58
+msgid "git notes append [<options>] [<object>]"
+msgstr ""
+
+#: builtin/notes.c:63
+msgid "git notes edit [<object>]"
+msgstr ""
+
+#: builtin/notes.c:68
+msgid "git notes show [<object>]"
+msgstr ""
+
+#: builtin/notes.c:73
+msgid "git notes merge [<options>] <notes_ref>"
+msgstr ""
+
+#: builtin/notes.c:74
+msgid "git notes merge --commit [<options>]"
+msgstr ""
+
+#: builtin/notes.c:75
+msgid "git notes merge --abort [<options>]"
+msgstr ""
+
+#: builtin/notes.c:80
+msgid "git notes remove [<object>]"
+msgstr ""
+
+#: builtin/notes.c:85
+msgid "git notes prune [<options>]"
+msgstr ""
+
+#: builtin/notes.c:90
+msgid "git notes get-ref"
+msgstr ""
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:143
+msgid "could not read 'show' output"
+msgstr ""
+
+#: builtin/notes.c:151
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:169 builtin/tag.c:341
+#, c-format
+msgid "could not create file '%s'"
+msgstr ""
+
+#: builtin/notes.c:188
+msgid "Please supply the note contents using either -m or -F option"
+msgstr ""
+
+#: builtin/notes.c:209 builtin/notes.c:972
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:214
+msgid "unable to write note object"
+msgstr ""
+
+#: builtin/notes.c:216
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr ""
+
+#: builtin/notes.c:250 builtin/tag.c:540
+#, c-format
+msgid "cannot read '%s'"
+msgstr ""
+
+#: builtin/notes.c:252 builtin/tag.c:543
+#, c-format
+msgid "could not open or read '%s'"
+msgstr ""
+
+#: builtin/notes.c:271 builtin/notes.c:444 builtin/notes.c:446
+#: builtin/notes.c:506 builtin/notes.c:560 builtin/notes.c:643
+#: builtin/notes.c:648 builtin/notes.c:723 builtin/notes.c:765
+#: builtin/notes.c:967 builtin/tag.c:556
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr ""
+
+#: builtin/notes.c:274
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr ""
+
+#: builtin/notes.c:298
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+
+#: builtin/notes.c:339
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:349
+#, 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
+#: builtin/notes.c:376
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:440
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr ""
+
+#: builtin/notes.c:455
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr ""
+
+#: builtin/notes.c:499 builtin/notes.c:553 builtin/notes.c:626
+#: builtin/notes.c:638 builtin/notes.c:711 builtin/notes.c:758
+#: builtin/notes.c:1032
+msgid "too many parameters"
+msgstr ""
+
+#: builtin/notes.c:512 builtin/notes.c:771
+#, c-format
+msgid "No note found for object %s."
+msgstr ""
+
+#: builtin/notes.c:534 builtin/notes.c:691
+msgid "note contents as a string"
+msgstr ""
+
+#: builtin/notes.c:537 builtin/notes.c:694
+msgid "note contents in a file"
+msgstr ""
+
+#: builtin/notes.c:539 builtin/notes.c:542 builtin/notes.c:696
+#: builtin/notes.c:699 builtin/tag.c:474
+msgid "object"
+msgstr ""
+
+#: builtin/notes.c:540 builtin/notes.c:697
+msgid "reuse and edit specified note object"
+msgstr ""
+
+#: builtin/notes.c:543 builtin/notes.c:700
+msgid "reuse specified note object"
+msgstr ""
+
+#: builtin/notes.c:545 builtin/notes.c:613
+msgid "replace existing notes"
+msgstr ""
+
+#: builtin/notes.c:579
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:584 builtin/notes.c:661
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:614
+msgid "read objects from stdin"
+msgstr ""
+
+#: builtin/notes.c:616
+msgid "load rewriting config for <command> (implies --stdin)"
+msgstr ""
+
+#: builtin/notes.c:634
+msgid "too few parameters"
+msgstr ""
+
+#: builtin/notes.c:655
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:667
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr ""
+
+#: builtin/notes.c:716
+#, c-format
+msgid ""
+"The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
+"Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
+msgstr ""
+
+#: builtin/notes.c:863
+msgid "General options"
+msgstr ""
+
+#: builtin/notes.c:865
+msgid "Merge options"
+msgstr ""
+
+#: builtin/notes.c:867
+msgid ""
+"resolve notes conflicts using the given strategy (manual/ours/theirs/union/"
+"cat_sort_uniq)"
+msgstr ""
+
+#: builtin/notes.c:869
+msgid "Committing unmerged notes"
+msgstr ""
+
+#: builtin/notes.c:871
+msgid "finalize notes merge by committing unmerged notes"
+msgstr ""
+
+#: builtin/notes.c:873
+msgid "Aborting notes merge resolution"
+msgstr ""
+
+#: builtin/notes.c:875
+msgid "abort notes merge"
+msgstr ""
+
+#: builtin/notes.c:970
+#, c-format
+msgid "Object %s has no note\n"
+msgstr ""
+
+#: builtin/notes.c:982
+msgid "attempt to remove non-existent note is not an error"
+msgstr ""
+
+#: builtin/notes.c:985
+msgid "read object names from the standard input"
+msgstr ""
+
+#: builtin/notes.c:1066
+msgid "notes_ref"
+msgstr ""
+
+#: builtin/notes.c:1067
+msgid "use notes from <notes_ref>"
+msgstr ""
+
+#: builtin/notes.c:1102 builtin/remote.c:1598
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr ""
+
+#: builtin/pack-objects.c:23
+msgid "git pack-objects --stdout [options...] [< ref-list | < object-list]"
+msgstr ""
+
+#: builtin/pack-objects.c:24
+msgid "git pack-objects [options...] base-name [< ref-list | < object-list]"
+msgstr ""
+
+#: builtin/pack-objects.c:183 builtin/pack-objects.c:186
+#, c-format
+msgid "deflate error (%d)"
+msgstr ""
+
+#: builtin/pack-objects.c:2397
+#, c-format
+msgid "unsupported index version %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2401
+#, c-format
+msgid "bad index version '%s'"
+msgstr ""
+
+#: builtin/pack-objects.c:2424
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr ""
+
+#: builtin/pack-objects.c:2428
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2447
+msgid "do not show progress meter"
+msgstr ""
+
+#: builtin/pack-objects.c:2449
+msgid "show progress meter"
+msgstr ""
+
+#: builtin/pack-objects.c:2451
+msgid "show progress meter during object writing phase"
+msgstr ""
+
+#: builtin/pack-objects.c:2454
+msgid "similar to --all-progress when progress meter is shown"
+msgstr ""
+
+#: builtin/pack-objects.c:2455
+msgid "version[,offset]"
+msgstr ""
+
+#: builtin/pack-objects.c:2456
+msgid "write the pack index file in the specified idx format version"
+msgstr ""
+
+#: builtin/pack-objects.c:2459
+msgid "maximum size of each output pack file"
+msgstr ""
+
+#: builtin/pack-objects.c:2461
+msgid "ignore borrowed objects from alternate object store"
+msgstr ""
+
+#: builtin/pack-objects.c:2463
+msgid "ignore packed objects"
+msgstr ""
+
+#: builtin/pack-objects.c:2465
+msgid "limit pack window by objects"
+msgstr ""
+
+#: builtin/pack-objects.c:2467
+msgid "limit pack window by memory in addition to object limit"
+msgstr ""
+
+#: builtin/pack-objects.c:2469
+msgid "maximum length of delta chain allowed in the resulting pack"
+msgstr ""
+
+#: builtin/pack-objects.c:2471
+msgid "reuse existing deltas"
+msgstr ""
+
+#: builtin/pack-objects.c:2473
+msgid "reuse existing objects"
+msgstr ""
+
+#: builtin/pack-objects.c:2475
+msgid "use OFS_DELTA objects"
+msgstr ""
+
+#: builtin/pack-objects.c:2477
+msgid "use threads when searching for best delta matches"
+msgstr ""
+
+#: builtin/pack-objects.c:2479
+msgid "do not create an empty pack output"
+msgstr ""
+
+#: builtin/pack-objects.c:2481
+msgid "read revision arguments from standard input"
+msgstr ""
+
+#: builtin/pack-objects.c:2483
+msgid "limit the objects to those that are not yet packed"
+msgstr ""
+
+#: builtin/pack-objects.c:2486
+msgid "include objects reachable from any reference"
+msgstr ""
+
+#: builtin/pack-objects.c:2489
+msgid "include objects referred by reflog entries"
+msgstr ""
+
+#: builtin/pack-objects.c:2492
+msgid "output pack to stdout"
+msgstr ""
+
+#: builtin/pack-objects.c:2494
+msgid "include tag objects that refer to objects to be packed"
+msgstr ""
+
+#: builtin/pack-objects.c:2496
+msgid "keep unreachable objects"
+msgstr ""
+
+#: builtin/pack-objects.c:2497 parse-options.h:141
+msgid "time"
+msgstr ""
+
+#: builtin/pack-objects.c:2498
+msgid "unpack unreachable objects newer than <time>"
+msgstr ""
+
+#: builtin/pack-objects.c:2501
+msgid "create thin packs"
+msgstr ""
+
+#: builtin/pack-objects.c:2503
+msgid "ignore packs that have companion .keep file"
+msgstr ""
+
+#: builtin/pack-objects.c:2505
+msgid "pack compression level"
+msgstr ""
+
+#: builtin/pack-objects.c:2507
+msgid "do not hide commits by grafts"
+msgstr ""
+
+#: builtin/pack-refs.c:6
+msgid "git pack-refs [options]"
+msgstr ""
+
+#: builtin/pack-refs.c:14
+msgid "pack everything"
+msgstr ""
+
+#: builtin/pack-refs.c:15
+msgid "prune loose refs (default)"
+msgstr ""
+
+#: builtin/prune-packed.c:7
+msgid "git prune-packed [-n|--dry-run] [-q|--quiet]"
+msgstr ""
+
+#: builtin/prune.c:12
+msgid "git prune [-n] [-v] [--expire <time>] [--] [<head>...]"
+msgstr ""
+
+#: builtin/prune.c:132
+msgid "do not remove, show only"
+msgstr ""
+
+#: builtin/prune.c:133
+msgid "report pruned objects"
+msgstr ""
+
+#: builtin/prune.c:136
+msgid "expire objects older than <time>"
+msgstr ""
+
+#: builtin/push.c:14
+msgid "git push [<options>] [<repository> [<refspec>...]]"
+msgstr ""
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr ""
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr ""
+
+#: builtin/push.c:99
+msgid ""
+"\n"
+"To choose either option permanently, see push.default in 'git help config'."
+msgstr ""
+
+#: builtin/push.c:102
+#, c-format
+msgid ""
+"The upstream branch of your current branch does not match\n"
+"the name of your current branch.  To push to the upstream branch\n"
+"on the remote, use\n"
+"\n"
+"    git push %s HEAD:%s\n"
+"\n"
+"To push to the branch of the same name on the remote, use\n"
+"\n"
+"    git push %s %s\n"
+"%s"
+msgstr ""
+
+#: builtin/push.c:121
+#, c-format
+msgid ""
+"You are not currently on a branch.\n"
+"To push the history leading to the current (detached HEAD)\n"
+"state now, use\n"
+"\n"
+"    git push %s HEAD:<name-of-remote-branch>\n"
+msgstr ""
+
+#: builtin/push.c:128
+#, c-format
+msgid ""
+"The current branch %s has no upstream branch.\n"
+"To push the current branch and set the remote as upstream, use\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+msgstr ""
+
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr ""
+
+#: builtin/push.c:139
+#, c-format
+msgid ""
+"You are pushing to remote '%s', which is not the upstream of\n"
+"your current branch '%s', without telling me what to push\n"
+"to update which remote branch."
+msgstr ""
+
+#: builtin/push.c:151
+msgid ""
+"push.default is unset; its implicit value is changing in\n"
+"Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
+"and maintain the current behavior after the default changes, use:\n"
+"\n"
+"  git config --global push.default matching\n"
+"\n"
+"To squelch this message and adopt the new behavior now, use:\n"
+"\n"
+"  git config --global push.default simple\n"
+"\n"
+"See 'git help config' and search for 'push.default' for further "
+"information.\n"
+"(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode\n"
+"'current' instead of 'simple' if you sometimes use older versions of Git)"
+msgstr ""
+
+#: builtin/push.c:199
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+
+#: builtin/push.c:206
+msgid ""
+"Updates were rejected because the tip of your current branch is behind\n"
+"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
+"before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:212
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. If you did not intend to push that branch, you may want to\n"
+"specify branches to push or set the 'push.default' configuration variable\n"
+"to 'simple', 'current' or 'upstream' to push only the current branch."
+msgstr ""
+
+#: builtin/push.c:218
+msgid ""
+"Updates were rejected because a pushed branch tip is behind its remote\n"
+"counterpart. Check out this branch and merge the remote changes\n"
+"(e.g. 'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:224
+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"
+"to the same ref. You may want to first merge the remote changes (e.g.,\n"
+"'git pull') before pushing again.\n"
+"See the 'Note about fast-forwards' in 'git push --help' for details."
+msgstr ""
+
+#: builtin/push.c:231
+msgid "Updates were rejected because the tag already exists in the remote."
+msgstr ""
+
+#: builtin/push.c:234
+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:294
+#, c-format
+msgid "Pushing to %s\n"
+msgstr ""
+
+#: builtin/push.c:298
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr ""
+
+#: builtin/push.c:331
+#, c-format
+msgid "bad repository '%s'"
+msgstr ""
+
+#: builtin/push.c:332
+msgid ""
+"No configured push destination.\n"
+"Either specify the URL from the command-line or configure a remote "
+"repository using\n"
+"\n"
+"    git remote add <name> <url>\n"
+"\n"
+"and then push using the remote name\n"
+"\n"
+"    git push <name>\n"
+msgstr ""
+
+#: builtin/push.c:347
+msgid "--all and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:348
+msgid "--all can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:353
+msgid "--mirror and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:354
+msgid "--mirror can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:359
+msgid "--all and --mirror are incompatible"
+msgstr ""
+
+#: builtin/push.c:419
+msgid "repository"
+msgstr ""
+
+#: builtin/push.c:420
+msgid "push all refs"
+msgstr ""
+
+#: builtin/push.c:421
+msgid "mirror all refs"
+msgstr ""
+
+#: builtin/push.c:423
+msgid "delete refs"
+msgstr ""
+
+#: builtin/push.c:424
+msgid "push tags (can't be used with --all or --mirror)"
+msgstr ""
+
+#: builtin/push.c:427
+msgid "force updates"
+msgstr ""
+
+#: builtin/push.c:428
+msgid "check"
+msgstr ""
+
+#: builtin/push.c:429
+msgid "control recursive pushing of submodules"
+msgstr ""
+
+#: builtin/push.c:431
+msgid "use thin pack"
+msgstr ""
+
+#: builtin/push.c:432 builtin/push.c:433
+msgid "receive pack program"
+msgstr ""
+
+#: builtin/push.c:434
+msgid "set upstream for git pull/status"
+msgstr ""
+
+#: builtin/push.c:437
+msgid "prune locally removed refs"
+msgstr ""
+
+#: builtin/push.c:439
+msgid "bypass pre-push hook"
+msgstr ""
+
+#: builtin/push.c:440
+msgid "push missing but relevant tags"
+msgstr ""
+
+#: builtin/push.c:450
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr ""
+
+#: builtin/push.c:452
+msgid "--delete doesn't make sense without any refs"
+msgstr ""
+
+#: builtin/read-tree.c:36
+msgid ""
+"git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] "
+"[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
+"index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
+msgstr ""
+
+#: builtin/read-tree.c:108
+msgid "write resulting index to <file>"
+msgstr ""
+
+#: builtin/read-tree.c:111
+msgid "only empty the index"
+msgstr ""
+
+#: builtin/read-tree.c:113
+msgid "Merging"
+msgstr ""
+
+#: builtin/read-tree.c:115
+msgid "perform a merge in addition to a read"
+msgstr ""
+
+#: builtin/read-tree.c:117
+msgid "3-way merge if no file level merging required"
+msgstr ""
+
+#: builtin/read-tree.c:119
+msgid "3-way merge in presence of adds and removes"
+msgstr ""
+
+#: builtin/read-tree.c:121
+msgid "same as -m, but discard unmerged entries"
+msgstr ""
+
+#: builtin/read-tree.c:122
+msgid "<subdirectory>/"
+msgstr ""
+
+#: builtin/read-tree.c:123
+msgid "read the tree into the index under <subdirectory>/"
+msgstr ""
+
+#: builtin/read-tree.c:126
+msgid "update working tree with merge result"
+msgstr ""
+
+#: builtin/read-tree.c:128
+msgid "gitignore"
+msgstr ""
+
+#: builtin/read-tree.c:129
+msgid "allow explicitly ignored files to be overwritten"
+msgstr ""
+
+#: builtin/read-tree.c:132
+msgid "don't check the working tree after merging"
+msgstr ""
+
+#: builtin/read-tree.c:133
+msgid "don't update the index or the work tree"
+msgstr ""
+
+#: builtin/read-tree.c:135
+msgid "skip applying sparse checkout filter"
+msgstr ""
+
+#: builtin/read-tree.c:137
+msgid "debug unpack-trees"
+msgstr ""
+
+#: builtin/remote.c:11
+msgid "git remote [-v | --verbose]"
+msgstr "git remote [-v | --verbose]"
+
+#: builtin/remote.c:12
+msgid ""
+"git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--"
+"mirror=<fetch|push>] <name> <url>"
+msgstr "git remote add [-t <branche>] [-m <maîtresse>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <nom> <URL>"
+
+#: builtin/remote.c:13 builtin/remote.c:32
+msgid "git remote rename <old> <new>"
+msgstr "git remote rename <ancienne> <nouvelle>"
+
+#: builtin/remote.c:14 builtin/remote.c:37
+msgid "git remote remove <name>"
+msgstr "git remote remove <nom>"
+
+#: builtin/remote.c:15 builtin/remote.c:42
+msgid "git remote set-head <name> (-a | -d | <branch>)"
+msgstr "git remote set-head <nom> (-a | -d | <branche>)"
+
+#: builtin/remote.c:16
+msgid "git remote [-v | --verbose] show [-n] <name>"
+msgstr "git remote [-v | --verbose] show [-n] <nom>"
+
+#: builtin/remote.c:17
+msgid "git remote prune [-n | --dry-run] <name>"
+msgstr "git remote prune [-n | --dry-run] <nom>"
+
+#: builtin/remote.c:18
+msgid ""
+"git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"
+msgstr "git remote [-v | --verbose] update [-p | --prune] [(<groupe> | <distante>)...]"
+
+#: builtin/remote.c:19
+msgid "git remote set-branches [--add] <name> <branch>..."
+msgstr "git remote set-branches [--add] <nom> <branche>..."
+
+#: builtin/remote.c:20 builtin/remote.c:68
+msgid "git remote set-url [--push] <name> <newurl> [<oldurl>]"
+msgstr "git remote set-url [--push] <nom> <nouvelle-URL> [<ancienne-URL>]"
+
+#: builtin/remote.c:21 builtin/remote.c:69
+msgid "git remote set-url --add <name> <newurl>"
+msgstr "git remote set-url --add <nom> <nouvelle-URL>"
+
+#: builtin/remote.c:22 builtin/remote.c:70
+msgid "git remote set-url --delete <name> <url>"
+msgstr "git remote set-url --delete <nom> <URL>"
+
+#: builtin/remote.c:27
+msgid "git remote add [<options>] <name> <url>"
+msgstr "git remote add [<options>] <nom> <URL>"
+
+#: builtin/remote.c:47
+msgid "git remote set-branches <name> <branch>..."
+msgstr "git remote set-branches <nom> <branche>..."
+
+#: builtin/remote.c:48
+msgid "git remote set-branches --add <name> <branch>..."
+msgstr "git remote set-branches --add <nom> <branche>..."
+
+#: builtin/remote.c:53
+msgid "git remote show [<options>] <name>"
+msgstr "git remote show [<options>] <nom>"
+
+#: builtin/remote.c:58
+msgid "git remote prune [<options>] <name>"
+msgstr "git remote prune [<options>] <nom>"
+
+#: builtin/remote.c:63
+msgid "git remote update [<options>] [<group> | <remote>]..."
+msgstr "git remote update [<options>] [<groupe> | <distante>]..."
+
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr "Mise à jour de %s"
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+"--mirror est dangereux et obsolète ; merci\n"
+"\t d'utiliser --mirror=fetch ou --mirror=push à la place"
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "argument miroir inconnu : %s"
+
+#: builtin/remote.c:163
+msgid "fetch the remote branches"
+msgstr "rapatrier les branches distantes"
+
+#: builtin/remote.c:165
+msgid "import all tags and associated objects when fetching"
+msgstr "importer toutes les étiquettes et les objets associés lors du rapatriement"
+
+#: builtin/remote.c:168
+msgid "or do not fetch any tag at all (--no-tags)"
+msgstr "ou ne rapatrier aucune étiquette (--no-tags)"
+
+#: builtin/remote.c:170
+msgid "branch(es) to track"
+msgstr "branche(s) à suivre"
+
+#: builtin/remote.c:171
+msgid "master branch"
+msgstr "branche maîtresse"
+
+#: builtin/remote.c:172
+msgid "push|fetch"
+msgstr "push|fetch"
+
+#: builtin/remote.c:173
+msgid "set up remote as a mirror to push to or fetch from"
+msgstr "paramétrer la distante comme miroir pour pousser ou pour rapatrier depuis"
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr "spécifier une branche maîtresse n'a pas de sens avec --mirror"
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr "spécifier les branches à suivre n'a de sens qu'avec des miroirs de rapatriement"
+
+#: builtin/remote.c:195 builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr "la distante %s existe déjà."
+
+#: builtin/remote.c:199 builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "'%s' n'est pas un nom valide de distante"
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Impossible de paramétrer la maîtresse '%s'"
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr "plus d'un %s"
+
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
+msgstr ""
+
+#: builtin/remote.c:440 builtin/remote.c:448
+msgid "(matching)"
+msgstr "(correspond)"
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr "(supprimer)"
+
+#: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr "Impossible d'ajouter '%s' à '%s'"
+
+#: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr "Distante inconnue : %s"
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Impossible de renommer la section de configuration '%s' en '%s'"
+
+#: builtin/remote.c:662 builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Impossible de supprimer la section de configuration '%s'"
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch refspec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+"Pas de mise à jour du refspec de rapatriement qui n'est pas par défaut\n"
+"\t%s\n"
+"\tMerci de mettre à jour la configuration si nécessaire."
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr "Impossible d'ajouter '%s'"
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr "Impossible de définir '%s'"
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "échec de suppression de '%s'"
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr "échec de création de '%s'"
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr "Impossible de supprimer la branche %s"
+
+#: builtin/remote.c:834
+msgid ""
+"Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+"to delete it, use:"
+msgid_plural ""
+"Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+"to delete them, use:"
+msgstr[0] ""
+"Note : Une branche en dehors de refs/remotes/ n'a pas été supprimée ;\n"
+"pour la supprimer, utilisez :"
+msgstr[1] ""
+"Note : Plusieurs branches en dehors de refs/remotes/ n'ont pas été supprimées ;\n"
+"pour les supprimer, utilisez :"
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr " nouveau (le prochain rapatriement (fetch) stockera dans remotes/%s)"
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr " suivi"
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr " dépassé (utilisez 'git remote prune' pour supprimer)"
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr " ???"
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr "branch.%s.merge invalide ; ne peut pas rebaser sur plus d'une branche"
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr "rebase sur la distante %s"
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr " fusionne avec la distante %s"
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr "    et avec la distante"
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr "fusionne avec la distante %s"
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr "   et avec la distante"
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr "créer"
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr "supprimer"
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr "à jour"
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr "peut être mis à jour en avance rapide"
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr "le local n'est pas à jour"
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr "    %-*s force vers %-*s (%s)"
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr "    %-*s pousse vers %-*s (%s)"
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr "    %-*s force vers %s"
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr "    %-*s pousse vers %s"
+
+#: builtin/remote.c:1091
+msgid "do not query remotes"
+msgstr "ne pas interroger les distantes"
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr "* distante %s"
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr "  URL de rapatriement : %s"
+
+#: builtin/remote.c:1120 builtin/remote.c:1285
+msgid "(no URL)"
+msgstr "(pas d'URL)"
+
+#: builtin/remote.c:1129 builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr "  URL push : %s"
+
+#: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "  Branche HEAD : %s"
+
+#: builtin/remote.c:1139
+#, c-format
+msgid ""
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr "  Branche HEAD (la HEAD distante est ambiguë, peut être l'une des suivantes) :\n"
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] "  Branche distante :%s"
+msgstr[1] "  Branches distantes :%s"
+
+#: builtin/remote.c:1154 builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr " (statut non demandé)"
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] "  Branche locale configurée pour 'git pull' :"
+msgstr[1] "  Branches locales configurées pour 'git pull' :"
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr "  Les références locales seront reflétées par 'git push'"
+
+#: builtin/remote.c:1178
+#, c-format
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] "  Référence locale configurée pour 'git push'%s :"
+msgstr[1] "  Références locales configurées pour 'git push'%s :"
+
+#: builtin/remote.c:1199
+msgid "set refs/remotes/<name>/HEAD according to remote"
+msgstr "définir refs/remotes/<nom>/HEAD selon la distante"
+
+#: builtin/remote.c:1201
+msgid "delete refs/remotes/<name>/HEAD"
+msgstr "supprimer refs/remotes/<nom>/HEAD"
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr "Impossible de déterminer la HEAD distante"
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr "Il y a de multiples branches HEAD distantes. Merci d'en choisir une explicitement avec :"
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr "Impossible de supprimer %s"
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr "Référence non valide : %s"
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr "Impossible de paramétrer %s"
+
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr " %s deviendra en suspens !"
+
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr " %s est devenu en suspens !"
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr "Élagage de %s"
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr "URL : %s"
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr " * [serait élagué] %s"
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr " * [élagué] %s"
+
+#: builtin/remote.c:1321
+msgid "prune remotes after fetching"
+msgstr "élaguer les distants après le rapatriement"
+
+#: builtin/remote.c:1387 builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Pas de serveur remote '%s'"
+
+#: builtin/remote.c:1407
+msgid "add branch"
+msgstr "ajouter une branche"
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr "pas de serveur distant spécifié"
+
+#: builtin/remote.c:1436
+msgid "manipulate push URLs"
+msgstr "manipuler les URLs push"
+
+#: builtin/remote.c:1438
+msgid "add URL"
+msgstr "ajouter une URL"
+
+#: builtin/remote.c:1440
+msgid "delete URLs"
+msgstr "supprimer des URLs"
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr "--add --delete n'a aucun sens"
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr "Motif d'URL ancien invalide : %s"
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Pas d'URL trouvée : %s"
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr ""
+
+#: builtin/remote.c:1569
+msgid "be verbose; must be placed before a subcommand"
+msgstr "être verbeux : doit être placé avant une sous-commande"
+
+#: builtin/replace.c:17
+msgid "git replace [-f] <object> <replacement>"
+msgstr "git replace [-f] <objet> <remplacement>"
+
+#: builtin/replace.c:18
+msgid "git replace -d <object>..."
+msgstr "git replace -d <objet>..."
+
+#: builtin/replace.c:19
+msgid "git replace -l [<pattern>]"
+msgstr "git replace -l [<motif>]"
+
+#: builtin/replace.c:121
+msgid "list replace refs"
+msgstr "afficher les références de remplacement"
+
+#: builtin/replace.c:122
+msgid "delete replace refs"
+msgstr "supprimer les références de remplacement"
+
+#: builtin/replace.c:123
+msgid "replace the ref if it exists"
+msgstr "remplacer la référence si elle existe"
+
+#: builtin/rerere.c:11
+msgid "git rerere [clear | forget path... | status | remaining | diff | gc]"
+msgstr "git rerere [clear | forget chemin... | status | remaining | diff | gc]"
+
+#: builtin/rerere.c:56
+msgid "register clean resolutions in index"
+msgstr "enregistrer des résolutions propres dans l'index"
+
+#: builtin/reset.c:25
+msgid ""
+"git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
+msgstr "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"
+
+#: builtin/reset.c:26
+msgid "git reset [-q] <tree-ish> [--] <paths>..."
+msgstr "git reset [-q] <arbre> [--] <chemins>..."
+
+#: builtin/reset.c:27
+msgid "git reset --patch [<tree-ish>] [--] [<paths>...]"
+msgstr "git reset --patch [<arbre>] [--] [<chemins>...]"
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr "mixed"
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr "soft"
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr "hard"
+
+#: builtin/reset.c:33
+msgid "merge"
+msgstr "merge"
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr "keep"
+
+#: builtin/reset.c:73
+msgid "You do not have a valid HEAD."
+msgstr "Vous n'avez pas une HEAD valide."
+
+#: builtin/reset.c:75
+msgid "Failed to find tree of HEAD."
+msgstr "Impossible de trouver l'arbre pour HEAD."
+
+#: builtin/reset.c:81
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr "Impossible de trouver l'arbre pour %s."
+
+#: builtin/reset.c:98
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "HEAD est maintenant à %s"
+
+#: builtin/reset.c:169
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr "Impossible de faire un \"reset %s\" au milieu d'une fusion."
+
+#: builtin/reset.c:248
+msgid "be quiet, only report errors"
+msgstr "être silencieux, afficher seulement les erreurs"
+
+#: builtin/reset.c:250
+msgid "reset HEAD and index"
+msgstr "réinitialiser HEAD et l'index"
+
+#: builtin/reset.c:251
+msgid "reset only HEAD"
+msgstr "réinitialiser seulement HEAD"
+
+#: builtin/reset.c:253 builtin/reset.c:255
+msgid "reset HEAD, index and working tree"
+msgstr "réinitialiser HEAD, l'index et la copie de travail"
+
+#: builtin/reset.c:257
+msgid "reset HEAD but keep local changes"
+msgstr "réinitialiser HEAD mais garder les changements locaux"
+
+#: builtin/reset.c:275
+#, c-format
+msgid "Failed to resolve '%s' as a valid revision."
+msgstr "Échec de résolution de '%s' comme une révision valide."
+
+#: builtin/reset.c:278 builtin/reset.c:286
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Impossible d'analyser l'objet '%s'."
+
+#: builtin/reset.c:283
+#, c-format
+msgid "Failed to resolve '%s' as a valid tree."
+msgstr "Échec de résolution de '%s' comme un arbre valide."
+
+#: builtin/reset.c:292
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr "--patch est incompatible avec --{hard,mixed,soft}"
+
+#: builtin/reset.c:301
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr "--mixed avec des chemins est obsolète ; utilisez 'git reset -- <paths>' à la place."
+
+#: builtin/reset.c:303
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr "Impossible de faire un \"%s reset\" avec des chemins."
+
+#: builtin/reset.c:313
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "Le \"%s reset\" n'est pas permis dans un dépôt nu"
+
+#: builtin/reset.c:333
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr "Impossible de réinitialiser le fichier d'index à la révision '%s'."
+
+#: builtin/reset.c:339
+msgid "Unstaged changes after reset:"
+msgstr "Modifications non indexées après reset :"
+
+#: builtin/reset.c:344
+msgid "Could not write new index file."
+msgstr "Impossible d'écrire le nouveau fichier d'index."
+
+#: builtin/rev-parse.c:339
+msgid "git rev-parse --parseopt [options] -- [<args>...]"
+msgstr "git rev-parse --parseopt [options] -- [<arguments>...]"
+
+#: builtin/rev-parse.c:344
+msgid "keep the `--` passed as an arg"
+msgstr "garder le `--` passé en argument"
+
+#: builtin/rev-parse.c:346
+msgid "stop parsing after the first non-option argument"
+msgstr "arrêt de l'analyse après le premier argument qui n'est pas une option"
+
+#: builtin/rev-parse.c:464
+msgid ""
+"git rev-parse --parseopt [options] -- [<args>...]\n"
+"   or: git rev-parse --sq-quote [<arg>...]\n"
+"   or: git rev-parse [options] [<arg>...]\n"
+"\n"
+"Run \"git rev-parse --parseopt -h\" for more information on the first usage."
+msgstr ""
+"git rev-parse --parseopt [options] -- [<arguments>...]\n"
+"   ou : git rev-parse --sq-quote [<argument>...]\n"
+"   ou : git rev-parse [options] [<argument>...]\n"
+"\n"
+"Lancez \"git rev-parse --parseopt -h\" pour plus d'information sur l'utilisation principale."
+
+#: builtin/revert.c:22
+msgid "git revert [options] <commit-ish>..."
+msgstr "git revert [options] <commit>..."
+
+#: builtin/revert.c:23
+msgid "git revert <subcommand>"
+msgstr "git revert <sous-commande>"
+
+#: builtin/revert.c:28
+msgid "git cherry-pick [options] <commit-ish>..."
+msgstr "git cherry-pick [options] <commit>..."
+
+#: builtin/revert.c:29
+msgid "git cherry-pick <subcommand>"
+msgstr "git cherry-pick <sous-commande>"
+
+#: builtin/revert.c:70 builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s : %s ne peut pas être utilisé avec %s"
+
+#: builtin/revert.c:103
+msgid "end revert or cherry-pick sequence"
+msgstr "mettre fin au retour ou picorage"
+
+#: builtin/revert.c:104
+msgid "resume revert or cherry-pick sequence"
+msgstr "reprendre le retour ou picorage"
+
+#: builtin/revert.c:105
+msgid "cancel revert or cherry-pick sequence"
+msgstr "annuler le retour ou picorage"
+
+#: builtin/revert.c:106
+msgid "don't automatically commit"
+msgstr "ne pas valider automatiquement"
+
+#: builtin/revert.c:107
+msgid "edit the commit message"
+msgstr "éditer le message de validation"
+
+#: builtin/revert.c:110
+msgid "parent number"
+msgstr "numéro de parent"
+
+#: builtin/revert.c:112
+msgid "merge strategy"
+msgstr "stratégie de fusion"
+
+#: builtin/revert.c:113
+msgid "option"
+msgstr "option"
+
+#: builtin/revert.c:114
+msgid "option for merge strategy"
+msgstr "option pour la stratégie de fusion"
+
+#: builtin/revert.c:125
+msgid "append commit name"
+msgstr "ajouter le nom de validation"
+
+#: builtin/revert.c:126
+msgid "allow fast-forward"
+msgstr "autoriser l'avance rapide"
+
+#: builtin/revert.c:127
+msgid "preserve initially empty commits"
+msgstr "préserver les validations vides initialement"
+
+#: builtin/revert.c:128
+msgid "allow commits with empty messages"
+msgstr "autoriser les validations avec des messages vides"
+
+#: builtin/revert.c:129
+msgid "keep redundant, empty commits"
+msgstr "garder les validations redondantes, vides"
+
+#: builtin/revert.c:133
+msgid "program error"
+msgstr "erreur du programme"
+
+#: builtin/revert.c:223
+msgid "revert failed"
+msgstr "revert a échoué"
+
+#: builtin/revert.c:238
+msgid "cherry-pick failed"
+msgstr "le picorage a échoué"
+
+#: builtin/rm.c:15
+msgid "git rm [options] [--] <file>..."
+msgstr "git rm [options] [--] <fichier>..."
+
+#: builtin/rm.c:64 builtin/rm.c:186
+#, c-format
+msgid ""
+"submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+"(use 'rm -rf' if you really want to remove it including all of its history)"
+msgstr ""
+"le sous-module '%s' (ou un des sous-modules imbriqués) utilise un répertoire .git\n"
+"(utilisez 'rm -rf' si vous souhaitez vraiment le supprimer avec tout son historique)"
+
+#: builtin/rm.c:174
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+"'%s' a du contenu indexé différent du fichier et de HEAD\n"
+"(utilisez -f pour forcer la suppression)"
+
+#: builtin/rm.c:180
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' a des changements dans l'index\n"
+"(utilisez --cached pour garder le fichier, ou -f pour forcer la suppression)"
+
+#: builtin/rm.c:191
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' a des modifications locales\n"
+"(utilisez --cached pour garder le fichier, ou -f pour forcer la suppression)"
+
+#: builtin/rm.c:207
+msgid "do not list removed files"
+msgstr "ne pas afficher les fichiers supprimés"
+
+#: builtin/rm.c:208
+msgid "only remove from the index"
+msgstr "supprimer seulement de l'index"
+
+#: builtin/rm.c:209
+msgid "override the up-to-date check"
+msgstr "outrepasser la vérification des fichiers à jour"
+
+#: builtin/rm.c:210
+msgid "allow recursive removal"
+msgstr "autoriser la suppression récursive"
+
+#: builtin/rm.c:212
+msgid "exit with a zero status even if nothing matched"
+msgstr "sortir avec un statut zéro même si rien ne correspondait"
+
+#: builtin/rm.c:283
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr "pas de suppression récursive de '%s' sans -r"
+
+#: builtin/rm.c:322
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm : impossible de supprimer %s"
+
+#: builtin/shortlog.c:13
+msgid "git shortlog [<options>] [<revision range>] [[--] [<path>...]]"
+msgstr "git shortlog [<options>] [<intervalle révisions>] [[--] [<chemin>...]]"
+
+#: builtin/shortlog.c:131
+#, c-format
+msgid "Missing author: %s"
+msgstr "Auteur manquant : %s"
+
+#: builtin/shortlog.c:227
+msgid "sort output according to the number of commits per author"
+msgstr "trier la sortie sur le nombre de validations par auteur"
+
+#: builtin/shortlog.c:229
+msgid "Suppress commit descriptions, only provides commit count"
+msgstr "Supprimer les descriptions de validation, fournit seulement le nombre de validations"
+
+#: builtin/shortlog.c:231
+msgid "Show the email address of each author"
+msgstr "Afficher l'adresse e-mail de chaque auteur"
+
+#: builtin/shortlog.c:232
+msgid "w[,i1[,i2]]"
+msgstr "w[,i1[,i2]]"
+
+#: builtin/shortlog.c:233
+msgid "Linewrap output"
+msgstr "Couper les lignes"
+
+#: builtin/show-branch.c:9
+msgid ""
+"git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--"
+"current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --"
+"independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | "
+"<glob>)...]"
+msgstr "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<quand>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<révision> | <glob>)...]"
+
+#: builtin/show-branch.c:10
+msgid "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"
+msgstr "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<référence>]"
+
+#: builtin/show-branch.c:650
+msgid "show remote-tracking and local branches"
+msgstr "afficher les branches de suivi distantes et les branches locales"
+
+#: builtin/show-branch.c:652
+msgid "show remote-tracking branches"
+msgstr "afficher les branches de suivi distantes"
+
+#: builtin/show-branch.c:654
+msgid "color '*!+-' corresponding to the branch"
+msgstr "couleur '*!+-' correspondant à la branche"
+
+#: builtin/show-branch.c:656
+msgid "show <n> more commits after the common ancestor"
+msgstr "afficher <n> validations de plus après l'ancêtre commun"
+
+#: builtin/show-branch.c:658
+msgid "synonym to more=-1"
+msgstr "synonyme de more=-1"
+
+#: builtin/show-branch.c:659
+msgid "suppress naming strings"
+msgstr ""
+
+#: builtin/show-branch.c:661
+msgid "include the current branch"
+msgstr "inclure la branche courante"
+
+#: builtin/show-branch.c:663
+msgid "name commits with their object names"
+msgstr "nommer les validations avec leurs noms d'objet"
+
+#: builtin/show-branch.c:665
+msgid "show possible merge bases"
+msgstr "afficher les bases possibles de fusion"
+
+#: builtin/show-branch.c:667
+msgid "show refs unreachable from any other ref"
+msgstr "afficher les références non accessibles depuis toute autre référence"
+
+#: builtin/show-branch.c:669
+msgid "show commits in topological order"
+msgstr "afficher les validations dans l'ordre topologique"
+
+#: builtin/show-branch.c:671
+msgid "show only commits not on the first branch"
+msgstr "afficher seulement les validations qui ne sont pas sur la première branche"
+
+#: builtin/show-branch.c:673
+msgid "show merges reachable from only one tip"
+msgstr "afficher les fusions accessibles depuis une seule pointe"
+
+#: builtin/show-branch.c:675
+msgid "show commits where no parent comes before its children"
+msgstr "afficher les validations où aucun parent ne vient avant ses enfants"
+
+#: builtin/show-branch.c:677
+msgid "<n>[,<base>]"
+msgstr "<n>[,<base>]"
+
+#: builtin/show-branch.c:678
+msgid "show <n> most recent ref-log entries starting at base"
+msgstr "afficher les <n> plus récentes entrées de ref-log en commençant à la base"
+
+#: builtin/show-ref.c:10
+msgid ""
+"git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--"
+"hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] "
+msgstr "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [motif*] "
+
+#: builtin/show-ref.c:11
+msgid "git show-ref --exclude-existing[=pattern] < ref-list"
+msgstr "git show-ref --exclude-existing[=motif] < liste-références"
+
+#: builtin/show-ref.c:165
+msgid "only show tags (can be combined with heads)"
+msgstr "afficher seulement les étiquettes (peut être combiné avec des têtes)"
+
+#: builtin/show-ref.c:166
+msgid "only show heads (can be combined with tags)"
+msgstr "afficher seulement les têtes (peut être combiné avec des étiquettes)"
+
+#: builtin/show-ref.c:167
+msgid "stricter reference checking, requires exact ref path"
+msgstr "vérification de référence plus stricte, requiert un chemin de référence exact"
+
+#: builtin/show-ref.c:170 builtin/show-ref.c:172
+msgid "show the HEAD reference"
+msgstr "afficher la référence HEAD"
+
+#: builtin/show-ref.c:174
+msgid "dereference tags into object IDs"
+msgstr "déréférencer les étiquettes en IDs d'objet"
+
+#: builtin/show-ref.c:176
+msgid "only show SHA1 hash using <n> digits"
+msgstr "afficher seulement le hachage SHA1 en utilisant <n> chiffres"
+
+#: builtin/show-ref.c:180
+msgid "do not print results to stdout (useful with --verify)"
+msgstr "ne pas afficher les résultats sur la sortie standard (pratique avec --verify)"
+
+#: builtin/show-ref.c:182
+msgid "show refs from stdin that aren't in local repository"
+msgstr "afficher les références de l'entrée standard qui ne sont pas dans le dépôt local"
+
+#: builtin/symbolic-ref.c:7
+msgid "git symbolic-ref [options] name [ref]"
+msgstr "git symbolic-ref [options] nom [référence]"
+
+#: builtin/symbolic-ref.c:8
+msgid "git symbolic-ref -d [-q] name"
+msgstr "git symbolic-ref -d [-q] nom"
+
+#: builtin/symbolic-ref.c:40
+msgid "suppress error message for non-symbolic (detached) refs"
+msgstr "supprimer le message d'erreur pour une référence non symbolique (détachée)"
+
+#: builtin/symbolic-ref.c:41
+msgid "delete symbolic ref"
+msgstr "supprimer la référence symbolique"
+
+#: builtin/symbolic-ref.c:42
+msgid "shorten ref output"
+msgstr "raccourcir l'affichage de la référence"
+
+#: builtin/symbolic-ref.c:43 builtin/update-ref.c:18
+msgid "reason"
+msgstr "raison"
+
+#: builtin/symbolic-ref.c:43 builtin/update-ref.c:18
+msgid "reason of the update"
+msgstr "raison de la mise à jour"
+
+#: builtin/tag.c:22
+msgid ""
+"git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]"
+msgstr "git tag [-a|-s|-u <id-clé>] [-f] [-m <msg>|-F <file>] <nométiquette> [<head>]"
+
+#: builtin/tag.c:23
+msgid "git tag -d <tagname>..."
+msgstr "git tag -d <nométiquette>..."
+
+#: builtin/tag.c:24
+msgid ""
+"git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] \n"
+"\t\t[<pattern>...]"
+msgstr ""
+"git tag -l [-n[<num>]] [--contains <commit>] [--points-at <objet>] \n"
+"\t\t[<motif>...]"
+
+#: builtin/tag.c:26
+msgid "git tag -v <tagname>..."
+msgstr "git tag -v <nométiquette>..."
+
+#: builtin/tag.c:60
+#, c-format
+msgid "malformed object at '%s'"
+msgstr "objet malformé à '%s'"
+
+#: builtin/tag.c:207
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr "nom d'étiquette trop long : %.*s..."
+
+#: builtin/tag.c:212
+#, c-format
+msgid "tag '%s' not found."
+msgstr "étiquette '%s' non trouvée."
+
+#: builtin/tag.c:227
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "Étiquette '%s' supprimée (elle était %s)\n"
+
+#: builtin/tag.c:239
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr "impossible de vérifier l'étiquette '%s'"
+
+#: builtin/tag.c:249
+#, c-format
+msgid ""
+"\n"
+"Write a tag message\n"
+"Lines starting with '%c' will be ignored.\n"
+msgstr ""
+"\n"
+"Écrire un message pour l'étiquette\n"
+"Les lignes commençant par '%c' seront ignorées.\n"
+
+#: builtin/tag.c:253
+#, c-format
+msgid ""
+"\n"
+"Write a tag message\n"
+"Lines starting with '%c' will be kept; you may remove them yourself if you "
+"want to.\n"
+msgstr ""
+"\n"
+"Écrire un message pour l'étiquette\n"
+"Les lignes commençant par '%c' seront gardées ; vous pouvez les retirer vous-même si vous le souhaitez.\n"
+
+#: builtin/tag.c:292
+msgid "unable to sign the tag"
+msgstr "impossible de signer l'étiquette"
+
+#: builtin/tag.c:294
+msgid "unable to write tag file"
+msgstr "impossible d'écrire le fichier d'étiquettes"
+
+#: builtin/tag.c:319
+msgid "bad object type."
+msgstr "mauvais type d'objet."
+
+#: builtin/tag.c:332
+msgid "tag header too big."
+msgstr "en-tête d'étiquette trop gros."
+
+#: builtin/tag.c:368
+msgid "no tag message?"
+msgstr "pas de message pour l'étiquette ?"
+
+#: builtin/tag.c:374
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "Le message pour l'étiquette a été laissé dans %s\n"
+
+#: builtin/tag.c:423
+msgid "switch 'points-at' requires an object"
+msgstr "le commutateur 'points-at' requiert un objet"
+
+#: builtin/tag.c:425
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "nom d'objet malformé '%s'"
+
+#: builtin/tag.c:445
+msgid "list tag names"
+msgstr "afficher les noms des étiquettes"
+
+#: builtin/tag.c:447
+msgid "print <n> lines of each tag message"
+msgstr "affiche <n> lignes de chaque message d'étiquette"
+
+#: builtin/tag.c:449
+msgid "delete tags"
+msgstr "supprimer des étiquettes"
+
+#: builtin/tag.c:450
+msgid "verify tags"
+msgstr "vérifier des étiquettes"
+
+#: builtin/tag.c:452
+msgid "Tag creation options"
+msgstr "Options de création de l'étiquette"
+
+#: builtin/tag.c:454
+msgid "annotated tag, needs a message"
+msgstr "étiquette annotée, nécessite un message"
+
+#: builtin/tag.c:456
+msgid "tag message"
+msgstr "message pour l'étiquette"
+
+#: builtin/tag.c:458
+msgid "annotated and GPG-signed tag"
+msgstr "étiquette annotée et signée avec GPG"
+
+#: builtin/tag.c:462
+msgid "use another key to sign the tag"
+msgstr "utiliser une autre clé pour signer l'étiquette"
+
+#: builtin/tag.c:463
+msgid "replace the tag if exists"
+msgstr "remplacer l'étiquette si elle existe"
+
+#: builtin/tag.c:464
+msgid "show tag list in columns"
+msgstr "afficher la liste des étiquettes sous forme de colonnes"
+
+#: builtin/tag.c:466
+msgid "Tag listing options"
+msgstr "Options d'affichage des étiquettes"
+
+#: builtin/tag.c:469
+msgid "print only tags that contain the commit"
+msgstr "afficher seulement les étiquettes qui contiennent la validation"
+
+#: builtin/tag.c:475
+msgid "print only tags of the object"
+msgstr "afficher seulement les étiquettes de l'objet"
+
+#: builtin/tag.c:504
+msgid "--column and -n are incompatible"
+msgstr "--column et -n sont incompatibles"
+
+#: builtin/tag.c:521
+msgid "-n option is only allowed with -l."
+msgstr "l'option -n est autorisée seulement avec -l."
+
+#: builtin/tag.c:523
+msgid "--contains option is only allowed with -l."
+msgstr "l'option --contains est autorisée seulement avec -l."
+
+#: builtin/tag.c:525
+msgid "--points-at option is only allowed with -l."
+msgstr "l'option --points-at est autorisée seulement avec -l."
+
+#: builtin/tag.c:533
+msgid "only one -F or -m option is allowed."
+msgstr "une seule option -F ou -m est autorisée."
+
+#: builtin/tag.c:553
+msgid "too many params"
+msgstr "trop de paramètres"
+
+#: builtin/tag.c:559
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "'%s' n'est pas un nom d'étiquette valide."
+
+#: builtin/tag.c:564
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "l'étiquette '%s' existe déjà"
+
+#: builtin/tag.c:582
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr "%s : impossible de verrouiller la référence"
+
+#: builtin/tag.c:584
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr "%s : impossible de mettre à jour la référence"
+
+#: builtin/tag.c:586
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "Étiquette '%s' mise à jour (elle était %s)\n"
+
+#: builtin/update-index.c:401
+msgid "git update-index [options] [--] [<file>...]"
+msgstr "git update-index [options] [--] [<fichier>...]"
+
+#: builtin/update-index.c:718
+msgid "continue refresh even when index needs update"
+msgstr "continuer de rafraîchir même si l'index a besoin d'une mise à jour"
+
+#: builtin/update-index.c:721
+msgid "refresh: ignore submodules"
+msgstr "rafraîchir : ignorer les sous-modules"
+
+#: builtin/update-index.c:724
+msgid "do not ignore new files"
+msgstr "ne pas ignorer les nouveaux fichiers"
+
+#: builtin/update-index.c:726
+msgid "let files replace directories and vice-versa"
+msgstr "laisser les fichiers remplacer des répertoires et vice-versa"
+
+#: builtin/update-index.c:728
+msgid "notice files missing from worktree"
+msgstr "aviser des fichiers manquants dans la copie de travail"
+
+#: builtin/update-index.c:730
+msgid "refresh even if index contains unmerged entries"
+msgstr "rafraîchir même si l'index contient des entrées non fusionnées"
+
+#: builtin/update-index.c:733
+msgid "refresh stat information"
+msgstr "rafraîchir l'information de stat"
+
+#: builtin/update-index.c:737
+msgid "like --refresh, but ignore assume-unchanged setting"
+msgstr "comme --refresh, mais en ignorant l'option assume-unchanged"
+
+#: builtin/update-index.c:741
+msgid "<mode> <object> <path>"
+msgstr "<mode> <objet> <chemin>"
+
+#: builtin/update-index.c:742
+msgid "add the specified entry to the index"
+msgstr "ajouter l'entrée spécifiée dans l'index"
+
+#: builtin/update-index.c:746
+msgid "(+/-)x"
+msgstr "(+/-)x"
+
+#: builtin/update-index.c:747
+msgid "override the executable bit of the listed files"
+msgstr "outrepasser le bit exécutable pour les fichiers listés"
+
+#: builtin/update-index.c:751
+msgid "mark files as \"not changing\""
+msgstr "marquer les fichiers comme \"non changeants\""
+
+#: builtin/update-index.c:754
+msgid "clear assumed-unchanged bit"
+msgstr ""
+
+#: builtin/update-index.c:757
+msgid "mark files as \"index-only\""
+msgstr "marquer les fichiers comme \"index seulement\""
+
+#: builtin/update-index.c:760
+msgid "clear skip-worktree bit"
+msgstr ""
+
+#: builtin/update-index.c:763
+msgid "add to index only; do not add content to object database"
+msgstr "ajouter seulement à l'index ; ne pas ajouter le contenu dans la base de données des objets"
+
+#: builtin/update-index.c:765
+msgid "remove named paths even if present in worktree"
+msgstr "supprimer les chemins nommés même s'ils sont présents dans la copie de travail"
+
+#: builtin/update-index.c:767
+msgid "with --stdin: input lines are terminated by null bytes"
+msgstr "avec --stdin : les lignes en entrée sont terminées par des octets nuls"
+
+#: builtin/update-index.c:769
+msgid "read list of paths to be updated from standard input"
+msgstr "lire la liste des chemins à mettre à jour depuis l'entrée standard"
+
+#: builtin/update-index.c:773
+msgid "add entries from standard input to the index"
+msgstr "ajouter les entrées depuis l'entrée standard à l'index"
+
+#: builtin/update-index.c:777
+msgid "repopulate stages #2 and #3 for the listed paths"
+msgstr "repeupler les étapes n°2 et n°3 pour les chemins listés"
+
+#: builtin/update-index.c:781
+msgid "only update entries that differ from HEAD"
+msgstr "mettre à jour seulement les entrées qui diffèrent de HEAD"
+
+#: builtin/update-index.c:785
+msgid "ignore files missing from worktree"
+msgstr "ignorer les fichiers manquants dans la copie de travail"
+
+#: builtin/update-index.c:788
+msgid "report actions to standard output"
+msgstr "afficher les actions sur la sortie standard"
+
+#: builtin/update-index.c:790
+msgid "(for porcelains) forget saved unresolved conflicts"
+msgstr "(pour porcelaines) oublier les conflits sauvés et non résolus"
+
+#: builtin/update-index.c:794
+msgid "write index in this format"
+msgstr "écrire l'index dans ce format"
+
+#: builtin/update-ref.c:7
+msgid "git update-ref [options] -d <refname> [<oldval>]"
+msgstr "git update-ref [options] -d <nomréférence> [<anciennevaleur>]"
+
+#: builtin/update-ref.c:8
+msgid "git update-ref [options]    <refname> <newval> [<oldval>]"
+msgstr "git update-ref [options]    <nomréférence> <nouvellevaleur> [<anciennevaleur>]"
+
+#: builtin/update-ref.c:19
+msgid "delete the reference"
+msgstr "supprimer la référence"
+
+#: builtin/update-ref.c:21
+msgid "update <refname> not the one it points to"
+msgstr ""
+
+#: builtin/update-server-info.c:6
+msgid "git update-server-info [--force]"
+msgstr "git update-server-info [--force]"
+
+#: builtin/update-server-info.c:14
+msgid "update the info files from scratch"
+msgstr "mettre à jour les fichiers d'information à partir de zéro"
+
+#: builtin/verify-pack.c:56
+msgid "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..."
+msgstr "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..."
+
+#: builtin/verify-pack.c:66
+msgid "verbose"
+msgstr "verbeux"
+
+#: builtin/verify-pack.c:68
+msgid "show statistics only"
+msgstr "afficher seulement les statistiques"
+
+#: builtin/verify-tag.c:17
+msgid "git verify-tag [-v|--verbose] <tag>..."
+msgstr "git verify-tag [-v|--verbose] <étiquette>..."
+
+#: builtin/verify-tag.c:73
+msgid "print tag contents"
+msgstr "afficher le contenu de l'étiquette"
+
+#: builtin/write-tree.c:13
+msgid "git write-tree [--missing-ok] [--prefix=<prefix>/]"
+msgstr "git write-tree [--missing-ok] [--prefix=<préfixe>/]"
+
+#: builtin/write-tree.c:26
+msgid "<prefix>/"
+msgstr "<préfixe>/"
+
+#: builtin/write-tree.c:27
+msgid "write tree object for a subdirectory <prefix>"
+msgstr "écrire l'objet arbre pour un sous-répertoire <préfixe>"
+
+#: builtin/write-tree.c:30
+msgid "only useful for debugging"
+msgstr "seulement utile pour le débogage"
+
+#: git.c:16
+msgid ""
+"'git help -a' and 'git help -g' lists available subcommands and some\n"
+"concept guides. See 'git help <command>' or 'git help <concept>'\n"
+"to read about a specific subcommand or concept."
+msgstr ""
+"'git help -a' et 'git help -g' listent les sous-commandes disponibles et\n"
+"quelques concepts. Voir 'git help <command>' ou 'git help <concept>'\n"
+"pour en lire plus à propos d'une commande spécifique ou d'un concept."
+
+#: parse-options.h:156
+msgid "no-op (backward compatibility)"
+msgstr "sans action (rétrocompatibilité)"
+
+#: parse-options.h:232
+msgid "be more verbose"
+msgstr "être plus verbeux"
+
+#: parse-options.h:234
+msgid "be more quiet"
+msgstr "être plus silencieux"
+
+#: parse-options.h:240
+msgid "use <n> digits to display SHA-1s"
+msgstr "utiliser <n> chiffres pour afficher les SHA-1s"
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr "Ajouter le contenu du fichier dans l'index"
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr "Rechercher de manière binaire le changement qui a introduit un bogue"
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr "Lister, créer ou supprimer des branches"
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr "Extraire une branche ou des chemins dans la copie de travail"
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr "Cloner un dépôt dans un nouveau répertoire"
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr "Enregistrer les changements dans le dépôt"
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr "Afficher les changements entre les validations, entre validation et copie de travail, etc"
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr "Télécharger les objets et références depuis un autre dépôt"
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr "Afficher les lignes correspondant à un motif"
+
+#: common-cmds.h:17
+msgid "Create an empty Git repository or reinitialize an existing one"
+msgstr "Créer un dépôt Git vide ou réinitialiser un existant"
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr "Afficher l'historique des validations"
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr "Joindre deux ou plusieurs historiques de développement ensemble"
+
+#: common-cmds.h:20
+msgid "Move or rename a file, a directory, or a symlink"
+msgstr "Déplacer ou renommer un fichier, un répertoire, ou un lien symbolique"
+
+#: common-cmds.h:21
+msgid "Fetch from and merge with another repository or a local branch"
+msgstr "Rapatrier et fusionner avec un autre dépôt ou une branche locale"
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr "Mettre à jour les références distantes ainsi que les objets associés"
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr "Reporter en avant les validations locales dans la tête en amont mise à jour"
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr "Réinitialiser la HEAD courante à l'état spécifié"
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr "Supprimer les fichiers de la copie de travail et de l'index"
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr "Afficher différents types d'objects"
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr "Afficher le statut de la copie de travail"
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr "Créer, lister, supprimer ou vérifier un objet d'étiquette signé avec GPG"
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "Vous devez d'abord définir vos informations de validateur"
+
+#: git-am.sh:95
+msgid ""
+"You seem to have moved HEAD since the last 'am' failure.\n"
+"Not rewinding to ORIG_HEAD"
+msgstr ""
+"Vous semblez avoir déplacé la HEAD depuis le dernier échec de 'am'.\n"
+"Pas de retour à ORIG_HEAD"
+
+#: git-am.sh:105
+#, sh-format
+msgid ""
+"When you have resolved this problem, run \"$cmdline --resolved\".\n"
+"If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n"
+"To restore the original branch and stop patching, run \"$cmdline --abort\"."
+msgstr ""
+"Lorsque vous aurez résolu ce problème, lancez \"$cmdline --resolved\".\n"
+"Si vous préférez sauter ce patch, lancez \"$cmdline --skip\" à la place.\n"
+"Pour restaurer la branche d'origine et stopper le patchage, lancez \"$cmdline --abort\"."
+
+#: git-am.sh:121
+msgid "Cannot fall back to three-way merge."
+msgstr "Impossible de retourner à une fusion 3-way."
+
+#: git-am.sh:137
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr "Le dépôt n'a pas les blobs nécessaires pour un retour à une fusion 3-way."
+
+#: git-am.sh:139
+msgid "Using index info to reconstruct a base tree..."
+msgstr "Utilisation de l'information de l'index pour reconstruire un arbre de base..."
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"Avez-vous édité le patch à la main ?\n"
+"Il ne s'applique pas aux blobs enregistrés dans son index."
+
+#: git-am.sh:163
+msgid "Falling back to patching base and 3-way merge..."
+msgstr "Retour à un patch de la base et fusion 3-way..."
+
+#: git-am.sh:179
+msgid "Failed to merge in the changes."
+msgstr "Échec de fusion dans les changements."
+
+#: git-am.sh:274
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Seulement une série de patches StGIT peut être appliquée à la fois"
+
+#: git-am.sh:361
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr "Le format de patch $patch_format n'est pas supporté."
+
+#: git-am.sh:363
+msgid "Patch format detection failed."
+msgstr "Échec de détection du format du patch."
+
+#: git-am.sh:389
+msgid ""
+"The -b/--binary option has been a no-op for long time, and\n"
+"it will be removed. Please do not use it anymore."
+msgstr ""
+"L'option -b/--binary ne fait plus rien depuis longtemps,\n"
+"et elle sera supprimée. Merci de ne plus l'utiliser."
+
+#: git-am.sh:477
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr "le répertoire précédent de rebasage $dotest existe toujours mais mbox donnée."
+
+#: git-am.sh:482
+msgid "Please make up your mind. --skip or --abort?"
+msgstr "Décidez-vous. --skip ou --abort ?"
+
+#: git-am.sh:509
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr "Pas de résolution de l'opération en cours, nous ne sommes pas dans une reprise."
+
+#: git-am.sh:575
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr "Index sale : impossible d'appliquer des patches (sales : $files)"
+
+#: git-am.sh:679
+#, sh-format
+msgid ""
+"Patch is empty.  Was it split wrong?\n"
+"If you would prefer to skip this patch, instead run \"$cmdline --skip\".\n"
+"To restore the original branch and stop patching run \"$cmdline --abort\"."
+msgstr ""
+"Le patch est vide.  Était-il mal découpé ?\n"
+"Si vous préférez sauter ce patch, lancez plutôt \"$cmdline --skip\".\n"
+"Pour restaurer la branche d'origine et stopper le patchage, lancez \"$cmdline --abort\"."
+
+#: git-am.sh:706
+msgid "Patch does not have a valid e-mail address."
+msgstr "Le patch n'a pas d'adresse e-mail valide."
+
+#: git-am.sh:753
+msgid "cannot be interactive without stdin connected to a terminal."
+msgstr "impossible d'être interactif sans entrée standard connectée à un terminal."
+
+#: git-am.sh:757
+msgid "Commit Body is:"
+msgstr "Le corps de la validation est :"
+
+#. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
+#. in your translation. The program will only accept English
+#. input at this point.
+#: git-am.sh:764
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr "Appliquer ? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+
+#: git-am.sh:800
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr "Application : $FIRSTLINE"
+
+#: git-am.sh:821
+msgid ""
+"No changes - did you forget to use 'git add'?\n"
+"If there is nothing left to stage, chances are that something else\n"
+"already introduced the same changes; you might want to skip this patch."
+msgstr ""
+
+#: git-am.sh:829
+msgid ""
+"You still have unmerged paths in your index\n"
+"did you forget to use 'git add'?"
+msgstr ""
+"Vous avez toujours des chemins non fusionnés dans votre index\n"
+"auriez-vous oublié de faire 'git add' ?"
+
+#: git-am.sh:845
+msgid "No changes -- Patch already applied."
+msgstr "Pas de changement -- Patch déjà appliqué."
+
+#: git-am.sh:855
+#, sh-format
+msgid "Patch failed at $msgnum $FIRSTLINE"
+msgstr "Le patch a échoué à $msgnum $FIRSTLINE"
+
+#: git-am.sh:858
+#, sh-format
+msgid ""
+"The copy of the patch that failed is found in:\n"
+"   $dotest/patch"
+msgstr ""
+"La copie du patch qui a échoué se trouve dans :\n"
+"   $dotest/patch"
+
+#: git-am.sh:876
+msgid "applying to an empty history"
+msgstr "application à un historique vide"
+
+#: git-bisect.sh:48
+msgid "You need to start by \"git bisect start\""
+msgstr "Vous devez démarrer avec \"git bisect start\""
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:54
+msgid "Do you want me to do it for you [Y/n]? "
+msgstr "Souhaitez-vous que je le fasse pour vous [Y/n] ? "
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr "option inconnue : '$arg'"
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr "'$arg' ne semble être une révision valide"
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr "Mauvaise HEAD - j'ai besoin d'une HEAD"
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr "L'extraction de '$start_head' a échoué. Essayez 'git bisect reset <branchevalide>'."
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr ""
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr "Mauvaise HEAD - référence symbolique douteuse"
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr "Mauvais argument pour bisect_write : $state"
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr "Mauvaise révision en entrée : $arg"
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr "Merci d'appeler 'bisect_state' avec au moins un argument."
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr "Mauvaise révision en entrée : $rev"
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr "'git bisect bad' n'accepte qu'un seul argument."
+
+#. have bad but not good.  we could bisect although
+#. this is less optimum.
+#: git-bisect.sh:273
+msgid "Warning: bisecting only with a bad commit."
+msgstr "Attention : bissection avec seulement une mauvaise validation."
+
+#. TRANSLATORS: Make sure to include [Y] and [n] in your
+#. translation. The program will only accept English input
+#. at this point.
+#: git-bisect.sh:279
+msgid "Are you sure [Y/n]? "
+msgstr "Êtes-vous sûr [Y/n] ? "
+
+#: git-bisect.sh:289
+msgid ""
+"You need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Vous devez me donner au moins une bonne et une mauvaise révision.\n"
+"(Vous pouvez utiliser \"git bisect bad\" et \"git bisect good\" pour cela.)"
+
+#: git-bisect.sh:292
+msgid ""
+"You need to start by \"git bisect start\".\n"
+"You then need to give me at least one good and one bad revisions.\n"
+"(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
+msgstr ""
+"Vous devez démarrer avec \"git bisect start\".\n"
+"Puis vous devez me donner au moins une bonne et une mauvaise révision.\n"
+"(Vous pouvez utiliser \"git bisect bad\" et \"git bisect good\" pour cela.)"
+
+#: git-bisect.sh:363 git-bisect.sh:490
+msgid "We are not bisecting."
+msgstr "Pas de bissection en cours."
+
+#: git-bisect.sh:370
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr "'$invalid' n'est pas une validation valide"
+
+#: git-bisect.sh:379
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+"Échec d'extraction de la HEAD d'origine '$branch'.\n"
+"Essayez 'git bisect reset <commit>'."
+
+#: git-bisect.sh:406
+msgid "No logfile given"
+msgstr "Pas de fichier de log donné"
+
+#: git-bisect.sh:407
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr "impossible de lire $file pour rejouer"
+
+#: git-bisect.sh:424
+msgid "?? what are you talking about?"
+msgstr "?? de quoi parlez-vous ?"
+
+#: git-bisect.sh:436
+#, sh-format
+msgid "running $command"
+msgstr "lancement de $command"
+
+#: git-bisect.sh:443
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"exit code $res from '$command' is < 0 or >= 128"
+msgstr ""
+"la bissection a échoué :\n"
+"le code retour $res de '$command' est < 0 ou >= 128"
+
+#: git-bisect.sh:469
+msgid "bisect run cannot continue any more"
+msgstr "la bissection ne peut plus continuer"
+
+#: git-bisect.sh:475
+#, sh-format
+msgid ""
+"bisect run failed:\n"
+"'bisect_state $state' exited with error code $res"
+msgstr ""
+"la bissection a échoué :\n"
+"'bisect_state $state' a retourné le code erreur $res"
+
+#: git-bisect.sh:482
+msgid "bisect run success"
+msgstr "succès de la bissection"
+
+#: git-pull.sh:21
+msgid ""
+"Pull is not possible because you have unmerged files.\n"
+"Please, fix them up in the work tree, and then use 'git add/rm <file>'\n"
+"as appropriate to mark resolution, or use 'git commit -a'."
+msgstr ""
+"Le pull n'est pas possible car vous avez des fichiers non fusionnés.\n"
+"Merci de corriger dans votre copie de travail, et utilisez alors 'git add/rm <file>'\n"
+"si nécessaire pour marquer comme résolu, ou utilisez 'git commit -a'."
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr "Le pull n'est pas possible car vous avez des fichiers non fusionnés."
+
+#: git-pull.sh:203
+msgid "updating an unborn branch with changes added to the index"
+msgstr "mise à jour d'une branche non encore créée avec les changements ajoutés dans l'index"
+
+#. The fetch involved updating the current branch.
+#. The working tree and the index file is still based on the
+#. $orig_head commit, but we are merging into $curr_head.
+#. First update the working tree to match $curr_head.
+#: git-pull.sh:235
+#, sh-format
+msgid ""
+"Warning: fetch updated the current branch head.\n"
+"Warning: fast-forwarding your working tree from\n"
+"Warning: commit $orig_head."
+msgstr ""
+"Attention : fetch a mis à jour la tête de la branche courante.\n"
+"Attention : mise à jour en avance rapide de votre copie de travail\n"
+"Attention : depuis la validation $orig_head."
+
+#: git-pull.sh:260
+msgid "Cannot merge multiple branches into empty head"
+msgstr "Impossible de fusionner de multiples branches sur une tête vide"
+
+#: git-pull.sh:264
+msgid "Cannot rebase onto multiple branches"
+msgstr "Impossible de rebaser sur de multiples branches"
+
+#: git-rebase.sh:53
+msgid ""
+"When you have resolved this problem, run \"git rebase --continue\".\n"
+"If you prefer to skip this patch, run \"git rebase --skip\" instead.\n"
+"To check out the original branch and stop rebasing, run \"git rebase --abort"
+"\"."
+msgstr ""
+"Lorsque vous aurez résolu ce problème, lancez \"git rebase --continue\".\n"
+"Si vous préférez sauter ce patch, lancez \"git rebase --skip\" à la place.\n"
+"Pour extraire la branche d'origine et stopper le rebasage, lancez \"git rebase --abort\"."
+
+#: git-rebase.sh:160
+msgid "The pre-rebase hook refused to rebase."
+msgstr "Le hook pre-rebase a refusé de rebaser."
+
+#: git-rebase.sh:165
+msgid "It looks like git-am is in progress. Cannot rebase."
+msgstr "Il semble que git-am soit en cours. Impossible de rebaser."
+
+#: git-rebase.sh:296
+msgid "The --exec option must be used with the --interactive option"
+msgstr "L'option --exec doit être utilisée avec l'option --interactive"
+
+#: git-rebase.sh:301
+msgid "No rebase in progress?"
+msgstr "Pas de rebasage en cours ?"
+
+#: git-rebase.sh:312
+msgid "The --edit-todo action can only be used during interactive rebase."
+msgstr "L'action --edit-todo peut seulement être utilisée lors d'un rebasage interactif."
+
+#: git-rebase.sh:319
+msgid "Cannot read HEAD"
+msgstr "Impossible de lire HEAD"
+
+#: git-rebase.sh:322
+msgid ""
+"You must edit all merge conflicts and then\n"
+"mark them as resolved using git add"
+msgstr ""
+"Vous devez éditer tous les conflits de fusion et\n"
+"les marquer comme résolus avec git add"
+
+#: git-rebase.sh:340
+#, sh-format
+msgid "Could not move back to $head_name"
+msgstr "Impossible de revenir à $head_name"
+
+#: git-rebase.sh:359
+#, sh-format
+msgid ""
+"It seems that there is already a $state_dir_base directory, and\n"
+"I wonder if you are in the middle of another rebase.  If that is the\n"
+"case, please try\n"
+"\t$cmd_live_rebase\n"
+"If that is not the case, please\n"
+"\t$cmd_clear_stale_rebase\n"
+"and run me again.  I am stopping in case you still have something\n"
+"valuable there."
+msgstr ""
+"Il semble qu'il y ait déjà un répertoire $state_dir_base, et je me demande\n"
+"si vous n'êtes pas au milieu d'un autre rebasage.  Si c'est le cas,\n"
+"merci d'essayer\n"
+"\t$cmd_live_rebase\n"
+"Si ça n'est pas le cas, merci de\n"
+"\t$cmd_clear_stale_rebase\n"
+"et relancer à nouveau.  Je m'arrête au cas où vous auriez quelque chose\n"
+"d'important ici."
+
+#: git-rebase.sh:404
+#, sh-format
+msgid "invalid upstream $upstream_name"
+msgstr "invalide $upstream_name en amont"
+
+#: git-rebase.sh:428
+#, sh-format
+msgid "$onto_name: there are more than one merge bases"
+msgstr "$onto_name : il y a plus d'une base de fusion"
+
+#: git-rebase.sh:431 git-rebase.sh:435
+#, sh-format
+msgid "$onto_name: there is no merge base"
+msgstr "$onto_name : il n'y a pas de base de fusion"
+
+#: git-rebase.sh:440
+#, sh-format
+msgid "Does not point to a valid commit: $onto_name"
+msgstr "Ne pointe pas sur une validation valide : $onto_name"
+
+#: git-rebase.sh:463
+#, sh-format
+msgid "fatal: no such branch: $branch_name"
+msgstr "fatal : pas de branche : $branch_name"
+
+#: git-rebase.sh:483
+msgid "Please commit or stash them."
+msgstr "Merci de les valider ou de les remiser."
+
+#: git-rebase.sh:501
+#, sh-format
+msgid "Current branch $branch_name is up to date."
+msgstr "La branche courante $branch_name est à jour."
+
+#: git-rebase.sh:504
+#, sh-format
+msgid "Current branch $branch_name is up to date, rebase forced."
+msgstr "La branche courante $branch_name est à jour, rebasage forcé."
+
+#: git-rebase.sh:515
+#, sh-format
+msgid "Changes from $mb to $onto:"
+msgstr "Changements de $mb sur $onto :"
+
+#. Detach HEAD and reset the tree
+#: git-rebase.sh:524
+msgid "First, rewinding head to replay your work on top of it..."
+msgstr "Premièrement, retour de head pour rejouer votre travail par-dessus..."
+
+#: git-rebase.sh:532
+#, sh-format
+msgid "Fast-forwarded $branch_name to $onto_name."
+msgstr "$branch_name mise à jour en avance rapide sur $onto_name."
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr "git stash clear avec des paramètres n'est pas implémenté"
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr "Vous n'avez pas encore la validation initiale"
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr "Impossible de sauver l'état courant de l'index"
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr "Impossible de sauver l'état courant de la copie de travail"
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr "Aucun changement sélectionné"
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr "Impossible de supprimer l'index temporaire (ne peut pas se produire)"
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr "Impossible d'enregistrer l'état de la copie de travail"
+
+#. TRANSLATORS: $option is an invalid option, like
+#. `--blah-blah'. The 7 spaces at the beginning of the
+#. second line correspond to "error: ". So you should line
+#. up the second line with however many characters the
+#. translation of "error: " takes in your language. E.g. in
+#. English this is:
+#.
+#. $ 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:202
+#, sh-format
+msgid ""
+"error: unknown option for 'stash save': $option\n"
+"       To provide a message, use git stash save -- '$option'"
+msgstr ""
+"erreur: option inconnue pour 'stash save': $option\n"
+"        Pour fournir un message, utilisez git stash save -- '$option'"
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr "Pas de changements en local à sauver"
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr "Impossible d'initialiser le remisage"
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr "Impossible de sauver le statut courant"
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr "Impossible de supprimer les changements de la copie de travail"
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr "Pas de remisage trouvé."
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr "Trop de révisions spécifiées : $REV"
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr "$reference n'est pas une référence valide"
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr "'$args' n'est pas une validation de type remisage"
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr "'$args' n'est pas une référence de remisage"
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr "impossible de rafraîchir l'index"
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr "Impossible d'appliquer un remisage au milieu d'une fusion"
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr "Conflits dans l'index. Essayez sans --index."
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr "Impossible de sauver l'arbre d'index"
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr "Impossible de désindexer les fichiers modifiés"
+
+#: git-stash.sh:475
+msgid "Index was not unstashed."
+msgstr ""
+
+#: git-stash.sh:492
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr "${REV} supprimé ($s)"
+
+#: git-stash.sh:493
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr "${REV}: Impossible de supprimer l'entrée de stash"
+
+#: git-stash.sh:500
+msgid "No branch name specified"
+msgstr "Aucune branche spécifiée"
+
+#: git-stash.sh:571
+msgid "(To restore them type \"git stash apply\")"
+msgstr "(Pour les restaurer tapez \"git stash apply\")"
+
+#: git-submodule.sh:91
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr "impossible de supprimer un composant de l'URL '$remoteurl'"
+
+#: git-submodule.sh:196
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr "Pas de mappage du sous-module trouvé dans .gitmodules pour le chemin '$sm_path'"
+
+#: git-submodule.sh:239
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr "Le clonage de '$url' dans le chemin de sous-module '$sm_path' a échoué"
+
+#: git-submodule.sh:251
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr "Le répertoire Git '$a' fait partie du chemin de sous-module '$b' ou vice-versa"
+
+#: git-submodule.sh:349
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr "L'URL de dépôt '$repo' doit être absolu ou commencer par ./|../"
+
+#: git-submodule.sh:366
+#, sh-format
+msgid "'$sm_path' already exists in the index"
+msgstr "'$sm_path' existe déjà dans l'index"
+
+#: git-submodule.sh:370
+#, sh-format
+msgid ""
+"The following path is ignored by one of your .gitignore files:\n"
+"$sm_path\n"
+"Use -f if you really want to add it."
+msgstr ""
+"Le chemin suivant est ignoré par un de vos fichiers .gitignore :\n"
+"$sm_path\n"
+"Utilisez -f si vous voulez vraiment l'ajouter."
+
+#: git-submodule.sh:388
+#, sh-format
+msgid "Adding existing repo at '$sm_path' to the index"
+msgstr "Ajout du dépôt existant à '$sm_path' dans l'index"
+
+#: git-submodule.sh:390
+#, sh-format
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr "'$sm_path' existe déjà et n'est pas un dépôt git valide"
+
+#: git-submodule.sh:398
+#, sh-format
+msgid "A git directory for '$sm_name' is found locally with remote(s):"
+msgstr "Un répertoire git pour '$sm_name' est trouvé en local avec le(s) serveur(s) distant(s) :"
+
+#: git-submodule.sh:400
+#, sh-format
+msgid ""
+"If you want to reuse this local git directory instead of cloning again from"
+msgstr "Si vous voulez réutiliser ce répertoire git local au lieu de cloner à nouveau depuis"
+
+#: git-submodule.sh:402
+#, sh-format
+msgid ""
+"use the '--force' option. If the local git directory is not the correct repo"
+msgstr "utilisez l'option '--force'. Si le répertoire local git n'est pas le dépôt correct"
+
+#: git-submodule.sh:403
+#, sh-format
+msgid ""
+"or you are unsure what this means choose another name with the '--name' "
+"option."
+msgstr "ou vous ne savez pas ce que cela signifie de choisir un autre nom avec l'option '--name'."
+
+#: git-submodule.sh:405
+#, sh-format
+msgid "Reactivating local git directory for submodule '$sm_name'."
+msgstr "Réactivation du répertoire git local pour le sous-module '$sm_name'."
+
+#: git-submodule.sh:417
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr "Impossible d'extraire le sous-module '$sm_path'"
+
+#: git-submodule.sh:422
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr "Échec d'ajout du sous-module '$sm_path'"
+
+#: git-submodule.sh:431
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr "Échec d'enregistrement du sous-module '$sm_path'"
+
+#: git-submodule.sh:474
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr "Entrée dans '$prefix$sm_path'"
+
+#: git-submodule.sh:488
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr "Arrêt sur '$sm_path' ; le script a retourné un statut non nul."
+
+#: git-submodule.sh:532
+#, sh-format
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr "URL non trouvé pour le chemin de sous-module '$sm_path' dans .gitmodules"
+
+#: git-submodule.sh:541
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr "Échec d'enregistrement de l'URL pour le chemin de sous-module '$sm_path'"
+
+#: git-submodule.sh:543
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr "Sous-module '$name' ($url) enregistré pour le chemin '$sm_path'"
+
+#: git-submodule.sh:551
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr "Échec d'enregistrement du mode de mise à jour pour le chemin de sous-module '$sm_path'"
+
+#: git-submodule.sh:588
+#, sh-format
+msgid "Use '.' if you really want to deinitialize all submodules"
+msgstr "Utilisez '.' si vous voulez vraiment réinitialiser tous les sous-modules"
+
+#: git-submodule.sh:603
+#, sh-format
+msgid "Submodule work tree '$sm_path' contains a .git directory"
+msgstr "La copie de travail du sous-module '$sm_path' contient un répertoire .git"
+
+#: git-submodule.sh:604
+#, sh-format
+msgid ""
+"(use 'rm -rf' if you really want to remove it including all of its history)"
+msgstr "(utilisez 'rm -rf' si vous voulez vraiment le supprimer en incluant tout son historique)"
+
+#: git-submodule.sh:610
+#, sh-format
+msgid ""
+"Submodule work tree '$sm_path' contains local modifications; use '-f' to "
+"discard them"
+msgstr "La copie de travail du sous-module '$sm_path' contient des modifications locales; utilisez '-f' pour les annuler"
+
+#: git-submodule.sh:613
+#, sh-format
+msgid "Cleared directory '$sm_path'"
+msgstr "Répertoire '$sm_path' nettoyé"
+
+#: git-submodule.sh:614
+#, sh-format
+msgid "Could not remove submodule work tree '$sm_path'"
+msgstr "Impossible de supprimer la copie de travail du sous-module '$sm_path'"
+
+#: git-submodule.sh:617
+#, sh-format
+msgid "Could not create empty submodule directory '$sm_path'"
+msgstr "Impossible de créer le répertoire vide du sous-module '$sm_path'"
+
+#: git-submodule.sh:626
+#, sh-format
+msgid "Submodule '$name' ($url) unregistered for path '$sm_path'"
+msgstr "Le sous-module '$name' ($url) n'est pas enregistré pour le chemin '$sm_path'"
+
+#: git-submodule.sh:731
+#, sh-format
+msgid ""
+"Submodule path '$prefix$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+"Chemin de sous-module '$prefix$sm_path' non initialisé\n"
+"Peut-être souhaitez-vous utiliser 'update --init' ?"
+
+#: git-submodule.sh:744
+#, sh-format
+msgid "Unable to find current revision in submodule path '$prefix$sm_path'"
+msgstr "Impossible de trouver la révision courante dans le chemin de sous-module '$prefix$sm_path'"
+
+#: git-submodule.sh:753
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr "Impossible de rapatrier dans le chemin de sous-module '$sm_path'"
+
+#: git-submodule.sh:777
+#, sh-format
+msgid "Unable to fetch in submodule path '$prefix$sm_path'"
+msgstr "Impossible de rapatrier dans le chemin de sous-module '$prefix$sm_path'"
+
+#: git-submodule.sh:791
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$prefix$sm_path'"
+msgstr "Impossible de rebaser '$sha1' dans le chemin de sous-module '$prefix$sm_path'"
+
+#: git-submodule.sh:792
+#, sh-format
+msgid "Submodule path '$prefix$sm_path': rebased into '$sha1'"
+msgstr "Chemin de sous-module '$prefix$sm_path' : rebasé dans '$sha1'"
+
+#: git-submodule.sh:797
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$prefix$sm_path'"
+msgstr "Impossible de fusionner '$sha1' dans le chemin de sous-module '$prefix$sm_path'"
+
+#: git-submodule.sh:798
+#, sh-format
+msgid "Submodule path '$prefix$sm_path': merged in '$sha1'"
+msgstr "Chemin de sous-module '$prefix$sm_path' : fusionné dans '$sha1'"
+
+#: git-submodule.sh:803
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$prefix$sm_path'"
+msgstr "Impossible d'extraire '$sha1' dans le chemin de sous-module '$prefix$sm_path'"
+
+#: git-submodule.sh:804
+#, sh-format
+msgid "Submodule path '$prefix$sm_path': checked out '$sha1'"
+msgstr "Chemin de sous-module '$prefix$sm_path' : '$sha1' extrait"
+
+#: git-submodule.sh:831
+#, sh-format
+msgid "Failed to recurse into submodule path '$prefix$sm_path'"
+msgstr "Échec de parcours dans le chemin du sous-module '$prefix$sm_path'"
+
+#: git-submodule.sh:939
+msgid "The --cached option cannot be used with the --files option"
+msgstr "L'option --cached ne peut pas être utilisée avec l'option --files"
+
+#. unexpected type
+#: git-submodule.sh:979
+#, sh-format
+msgid "unexpected mode $mod_dst"
+msgstr "mode $mod_dst inattendu"
+
+#: git-submodule.sh:997
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr "  Attention : $name ne contient pas la validation $sha1_src"
+
+#: git-submodule.sh:1000
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr "  Attention : $name ne contient pas la validation $sha1_dst"
+
+#: git-submodule.sh:1003
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr "  Attention : $name ne contient pas les validations $sha1_src et $sha1_dst"
+
+#: git-submodule.sh:1028
+msgid "blob"
+msgstr "blob"
+
+#: git-submodule.sh:1066
+msgid "Submodules changed but not updated:"
+msgstr "Sous-modules modifiés mais non mis à jour :"
+
+#: git-submodule.sh:1068
+msgid "Submodule changes to be committed:"
+msgstr "Changements du sous-module à valider :"
+
+#: git-submodule.sh:1153
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "Échec de parcours dans le chemin du sous-module '$sm_path'"
+
+#: git-submodule.sh:1216
+#, sh-format
+msgid "Synchronizing submodule url for '$prefix$sm_path'"
+msgstr "Synchronisation de l'URL sous-module pour '$prefix$sm_path'"
index a826dcbf9ffa723b85eb9fa8bc0e715ddf0e42ad..cf1e44633079895608a3b0da3ff27c986e8903ac 100644 (file)
@@ -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: 2013-03-05 12:36+0800\n"
+"POT-Creation-Date: 2013-08-06 14:10+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"
@@ -18,7 +18,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
-#: advice.c:49
+#: advice.c:57
 #, c-format
 msgid "hint: %.*s\n"
 msgstr ""
@@ -27,7 +27,7 @@ msgstr ""
 #. * Message used both when 'git commit' fails and when
 #. * other commands doing a merge do.
 #.
-#: advice.c:79
+#: advice.c:87
 msgid ""
 "Fix them up in the work tree,\n"
 "and then use 'git add/rm <file>' as\n"
@@ -60,7 +60,7 @@ msgstr ""
 msgid "archive format"
 msgstr ""
 
-#: archive.c:324 builtin/log.c:1115
+#: archive.c:324 builtin/log.c:1173
 msgid "prefix"
 msgstr ""
 
@@ -68,15 +68,15 @@ msgstr ""
 msgid "prepend prefix to each pathname in the archive"
 msgstr ""
 
-#: archive.c:326 builtin/archive.c:91 builtin/blame.c:2366
-#: builtin/blame.c:2367 builtin/config.c:55 builtin/fast-export.c:653
-#: builtin/fast-export.c:655 builtin/grep.c:715 builtin/hash-object.c:77
-#: builtin/ls-files.c:497 builtin/ls-files.c:500 builtin/notes.c:536
-#: builtin/notes.c:693 builtin/read-tree.c:107 parse-options.h:149
+#: archive.c:326 builtin/archive.c:88 builtin/blame.c:2292
+#: builtin/blame.c:2293 builtin/config.c:56 builtin/fast-export.c:673
+#: builtin/fast-export.c:675 builtin/grep.c:714 builtin/hash-object.c:77
+#: builtin/ls-files.c:496 builtin/ls-files.c:499 builtin/notes.c:408
+#: builtin/notes.c:565 builtin/read-tree.c:108 parse-options.h:152
 msgid "file"
 msgstr ""
 
-#: archive.c:327 builtin/archive.c:92
+#: archive.c:327 builtin/archive.c:89
 msgid "write the archive to this file"
 msgstr ""
 
@@ -104,19 +104,19 @@ msgstr ""
 msgid "list supported archive formats"
 msgstr ""
 
-#: archive.c:345 builtin/archive.c:93 builtin/clone.c:85
+#: archive.c:345 builtin/archive.c:90 builtin/clone.c:85
 msgid "repo"
 msgstr ""
 
-#: archive.c:346 builtin/archive.c:94
+#: archive.c:346 builtin/archive.c:91
 msgid "retrieve the archive from remote repository <repo>"
 msgstr ""
 
-#: archive.c:347 builtin/archive.c:95 builtin/notes.c:615
+#: archive.c:347 builtin/archive.c:92 builtin/notes.c:487
 msgid "command"
 msgstr ""
 
-#: archive.c:348 builtin/archive.c:96
+#: archive.c:348 builtin/archive.c:93
 msgid "path to the remote git-upload-archive command"
 msgstr ""
 
@@ -126,6 +126,120 @@ msgid ""
 "Use '\\!' for literal leading exclamation."
 msgstr ""
 
+#: branch.c:60
+#, c-format
+msgid "Not setting branch %s as its own upstream."
+msgstr ""
+
+#: branch.c:82
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s by rebasing."
+msgstr ""
+
+#: branch.c:83
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s."
+msgstr ""
+
+#: branch.c:87
+#, c-format
+msgid "Branch %s set up to track local branch %s by rebasing."
+msgstr ""
+
+#: branch.c:88
+#, c-format
+msgid "Branch %s set up to track local branch %s."
+msgstr ""
+
+#: branch.c:92
+#, c-format
+msgid "Branch %s set up to track remote ref %s by rebasing."
+msgstr ""
+
+#: branch.c:93
+#, c-format
+msgid "Branch %s set up to track remote ref %s."
+msgstr ""
+
+#: branch.c:97
+#, c-format
+msgid "Branch %s set up to track local ref %s by rebasing."
+msgstr ""
+
+#: branch.c:98
+#, c-format
+msgid "Branch %s set up to track local ref %s."
+msgstr ""
+
+#: branch.c:118
+#, c-format
+msgid "Tracking not set up: name too long: %s"
+msgstr ""
+
+#: branch.c:137
+#, c-format
+msgid "Not tracking: ambiguous information for ref %s"
+msgstr ""
+
+#: branch.c:182
+#, c-format
+msgid "'%s' is not a valid branch name."
+msgstr ""
+
+#: branch.c:187
+#, c-format
+msgid "A branch named '%s' already exists."
+msgstr ""
+
+#: branch.c:195
+msgid "Cannot force update the current branch."
+msgstr ""
+
+#: branch.c:216
+#, c-format
+msgid "Cannot setup tracking information; starting point '%s' is not a branch."
+msgstr ""
+
+#: branch.c:218
+#, c-format
+msgid "the requested upstream branch '%s' does not exist"
+msgstr ""
+
+#: branch.c:220
+msgid ""
+"\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push."
+msgstr ""
+
+#: branch.c:265
+#, c-format
+msgid "Not a valid object name: '%s'."
+msgstr ""
+
+#: branch.c:285
+#, c-format
+msgid "Ambiguous object name: '%s'."
+msgstr ""
+
+#: branch.c:290
+#, c-format
+msgid "Not a valid branch point: '%s'."
+msgstr ""
+
+#: branch.c:296
+msgid "Failed to lock ref for update"
+msgstr ""
+
+#: branch.c:314
+msgid "Failed to write ref"
+msgstr ""
+
 #: bundle.c:36
 #, c-format
 msgid "'%s' does not look like a v2 bundle file"
@@ -136,7 +250,7 @@ msgstr ""
 msgid "unrecognized header: %s%s (%d)"
 msgstr ""
 
-#: bundle.c:89 builtin/commit.c:674
+#: bundle.c:89 builtin/commit.c:697
 #, c-format
 msgid "could not open '%s'"
 msgstr ""
@@ -145,27 +259,27 @@ msgstr ""
 msgid "Repository lacks these prerequisite commits:"
 msgstr ""
 
-#: bundle.c:164 sequencer.c:566 sequencer.c:998 builtin/log.c:299
-#: builtin/log.c:751 builtin/log.c:1358 builtin/log.c:1574 builtin/merge.c:347
-#: builtin/shortlog.c:157
+#: bundle.c:164 sequencer.c:661 sequencer.c:1111 builtin/log.c:331
+#: builtin/log.c:801 builtin/log.c:1399 builtin/log.c:1625 builtin/merge.c:364
+#: builtin/shortlog.c:156
 msgid "revision walk setup failed"
 msgstr ""
 
 #: bundle.c:186
 #, c-format
-msgid "The bundle contains %d ref"
-msgid_plural "The bundle contains %d refs"
+msgid "The bundle contains this ref:"
+msgid_plural "The bundle contains these %d refs:"
 msgstr[0] ""
 msgstr[1] ""
 
-#: bundle.c:192
+#: bundle.c:193
 msgid "The bundle records a complete history."
 msgstr ""
 
 #: bundle.c:195
 #, c-format
-msgid "The bundle requires this ref"
-msgid_plural "The bundle requires these %d refs"
+msgid "The bundle requires this ref:"
+msgid_plural "The bundle requires these %d refs:"
 msgstr[0] ""
 msgstr[1] ""
 
@@ -173,7 +287,7 @@ msgstr[1] ""
 msgid "rev-list died"
 msgstr ""
 
-#: bundle.c:300 builtin/log.c:1254 builtin/shortlog.c:260
+#: bundle.c:300 builtin/log.c:1310 builtin/shortlog.c:259
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr ""
@@ -204,12 +318,12 @@ msgstr ""
 msgid "index-pack died"
 msgstr ""
 
-#: commit.c:50
+#: commit.c:53
 #, c-format
 msgid "could not parse %s"
 msgstr ""
 
-#: commit.c:52
+#: commit.c:55
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr ""
@@ -218,16 +332,16 @@ msgstr ""
 msgid "memory exhausted"
 msgstr ""
 
-#: connected.c:39
+#: connected.c:60
 msgid "Could not run 'git rev-list'"
 msgstr ""
 
-#: connected.c:48
+#: connected.c:80
 #, c-format
 msgid "failed write to rev-list: %s"
 msgstr ""
 
-#: connected.c:56
+#: connected.c:88
 #, c-format
 msgid "failed to close rev-list's stdin: %s"
 msgstr ""
@@ -321,19 +435,19 @@ msgid ""
 "%s"
 msgstr ""
 
-#: diff.c:3468
+#: diff.c:3487
 #, c-format
 msgid ""
 "Failed to parse --dirstat/-X option parameter:\n"
 "%s"
 msgstr ""
 
-#: diff.c:3482
+#: diff.c:3501
 #, c-format
 msgid "Failed to parse --submodule option parameter: '%s'"
 msgstr ""
 
-#: gpg-interface.c:59 gpg-interface.c:127
+#: gpg-interface.c:59 gpg-interface.c:131
 msgid "could not run gpg."
 msgstr ""
 
@@ -345,73 +459,73 @@ msgstr ""
 msgid "gpg failed to sign the data"
 msgstr ""
 
-#: gpg-interface.c:112
+#: gpg-interface.c:115
 #, c-format
 msgid "could not create temporary file '%s': %s"
 msgstr ""
 
-#: gpg-interface.c:115
+#: gpg-interface.c:118
 #, c-format
 msgid "failed writing detached signature to '%s': %s"
 msgstr ""
 
-#: grep.c:1622
+#: grep.c:1623
 #, c-format
 msgid "'%s': unable to read %s"
 msgstr ""
 
-#: grep.c:1639
+#: grep.c:1640
 #, c-format
 msgid "'%s': %s"
 msgstr ""
 
-#: grep.c:1650
+#: grep.c:1651
 #, c-format
 msgid "'%s': short read %s"
 msgstr ""
 
-#: help.c:212
+#: help.c:210
 #, c-format
 msgid "available git commands in '%s'"
 msgstr ""
 
-#: help.c:219
+#: help.c:217
 msgid "git commands available from elsewhere on your $PATH"
 msgstr ""
 
-#: help.c:235
+#: help.c:233
 msgid "The most commonly used git commands are:"
 msgstr ""
 
-#: help.c:292
+#: help.c:290
 #, c-format
 msgid ""
 "'%s' appears to be a git command, but we were not\n"
 "able to execute it. Maybe git-%s is broken?"
 msgstr ""
 
-#: help.c:349
+#: help.c:347
 msgid "Uh oh. Your system reports no Git commands at all."
 msgstr ""
 
-#: help.c:371
+#: help.c:369
 #, c-format
 msgid ""
 "WARNING: You called a Git command named '%s', which does not exist.\n"
 "Continuing under the assumption that you meant '%s'"
 msgstr ""
 
-#: help.c:376
+#: help.c:374
 #, c-format
 msgid "in %0.1f seconds automatically..."
 msgstr ""
 
-#: help.c:383
+#: help.c:381
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
 msgstr ""
 
-#: help.c:387
+#: help.c:385 help.c:444
 msgid ""
 "\n"
 "Did you mean this?"
@@ -421,12 +535,17 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
+#: help.c:440
+#, c-format
+msgid "%s: %s - %s"
+msgstr ""
+
 #: merge.c:56
 msgid "failed to read the cache"
 msgstr ""
 
-#: merge.c:110 builtin/checkout.c:333 builtin/checkout.c:534
-#: builtin/clone.c:586
+#: merge.c:110 builtin/checkout.c:365 builtin/checkout.c:566
+#: builtin/clone.c:656
 msgid "unable to write new index file"
 msgstr ""
 
@@ -440,242 +559,268 @@ msgstr ""
 msgid "addinfo_cache failed for path '%s'"
 msgstr ""
 
-#: merge-recursive.c:268
+#: merge-recursive.c:269
 msgid "error building trees"
 msgstr ""
 
-#: merge-recursive.c:672
+#: merge-recursive.c:673
 #, c-format
 msgid "failed to create path '%s'%s"
 msgstr ""
 
-#: merge-recursive.c:683
+#: merge-recursive.c:684
 #, c-format
 msgid "Removing %s to make room for subdirectory\n"
 msgstr ""
 
 #. something else exists
 #. .. but not some other error (who really cares what?)
-#: merge-recursive.c:697 merge-recursive.c:718
+#: merge-recursive.c:698 merge-recursive.c:719
 msgid ": perhaps a D/F conflict?"
 msgstr ""
 
-#: merge-recursive.c:708
+#: merge-recursive.c:709
 #, c-format
 msgid "refusing to lose untracked file at '%s'"
 msgstr ""
 
-#: merge-recursive.c:748
+#: merge-recursive.c:749
 #, c-format
 msgid "cannot read object %s '%s'"
 msgstr ""
 
-#: merge-recursive.c:750
+#: merge-recursive.c:751
 #, c-format
 msgid "blob expected for %s '%s'"
 msgstr ""
 
-#: merge-recursive.c:773 builtin/clone.c:302
+#: merge-recursive.c:774 builtin/clone.c:312
 #, c-format
 msgid "failed to open '%s'"
 msgstr ""
 
-#: merge-recursive.c:781
+#: merge-recursive.c:782
 #, c-format
 msgid "failed to symlink '%s'"
 msgstr ""
 
-#: merge-recursive.c:784
+#: merge-recursive.c:785
 #, c-format
 msgid "do not know what to do with %06o %s '%s'"
 msgstr ""
 
-#: merge-recursive.c:922
+#: merge-recursive.c:923
 msgid "Failed to execute internal merge"
 msgstr ""
 
-#: merge-recursive.c:926
+#: merge-recursive.c:927
 #, c-format
 msgid "Unable to add %s to database"
 msgstr ""
 
-#: merge-recursive.c:942
+#: merge-recursive.c:943
 msgid "unsupported object type in the tree"
 msgstr ""
 
-#: merge-recursive.c:1021 merge-recursive.c:1035
+#: merge-recursive.c:1022 merge-recursive.c:1036
 #, 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:1027 merge-recursive.c:1040
+#: merge-recursive.c:1028 merge-recursive.c:1041
 #, 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:1081
+#: merge-recursive.c:1082
 msgid "rename"
 msgstr ""
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "renamed"
 msgstr ""
 
-#: merge-recursive.c:1137
+#: merge-recursive.c:1138
 #, c-format
 msgid "%s is a directory in %s adding as %s instead"
 msgstr ""
 
-#: merge-recursive.c:1159
+#: merge-recursive.c:1160
 #, c-format
 msgid ""
 "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
 "\"->\"%s\" in \"%s\"%s"
 msgstr ""
 
-#: merge-recursive.c:1164
+#: merge-recursive.c:1165
 msgid " (left unresolved)"
 msgstr ""
 
-#: merge-recursive.c:1218
+#: merge-recursive.c:1219
 #, c-format
 msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
 msgstr ""
 
-#: merge-recursive.c:1248
+#: merge-recursive.c:1249
 #, c-format
 msgid "Renaming %s to %s and %s to %s instead"
 msgstr ""
 
-#: merge-recursive.c:1447
+#: merge-recursive.c:1448
 #, c-format
 msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s"
 msgstr ""
 
-#: merge-recursive.c:1457
+#: merge-recursive.c:1458
 #, c-format
 msgid "Adding merged %s"
 msgstr ""
 
-#: merge-recursive.c:1462 merge-recursive.c:1660
+#: merge-recursive.c:1463 merge-recursive.c:1661
 #, c-format
 msgid "Adding as %s instead"
 msgstr ""
 
-#: merge-recursive.c:1513
+#: merge-recursive.c:1514
 #, c-format
 msgid "cannot read object %s"
 msgstr ""
 
-#: merge-recursive.c:1516
+#: merge-recursive.c:1517
 #, c-format
 msgid "object %s is not a blob"
 msgstr ""
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modify"
 msgstr ""
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modified"
 msgstr ""
 
-#: merge-recursive.c:1574
+#: merge-recursive.c:1575
 msgid "content"
 msgstr ""
 
-#: merge-recursive.c:1581
+#: merge-recursive.c:1582
 msgid "add/add"
 msgstr ""
 
-#: merge-recursive.c:1615
+#: merge-recursive.c:1616
 #, c-format
 msgid "Skipped %s (merged same as existing)"
 msgstr ""
 
-#: merge-recursive.c:1629
+#: merge-recursive.c:1630
 #, c-format
 msgid "Auto-merging %s"
 msgstr ""
 
-#: merge-recursive.c:1633 git-submodule.sh:942
+#: merge-recursive.c:1634 git-submodule.sh:1118
 msgid "submodule"
 msgstr ""
 
-#: merge-recursive.c:1634
+#: merge-recursive.c:1635
 #, c-format
 msgid "CONFLICT (%s): Merge conflict in %s"
 msgstr ""
 
-#: merge-recursive.c:1724
+#: merge-recursive.c:1725
 #, c-format
 msgid "Removing %s"
 msgstr ""
 
-#: merge-recursive.c:1749
+#: merge-recursive.c:1750
 msgid "file/directory"
 msgstr ""
 
-#: merge-recursive.c:1755
+#: merge-recursive.c:1756
 msgid "directory/file"
 msgstr ""
 
-#: merge-recursive.c:1760
+#: merge-recursive.c:1761
 #, c-format
 msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
 msgstr ""
 
-#: merge-recursive.c:1770
+#: merge-recursive.c:1771
 #, c-format
 msgid "Adding %s"
 msgstr ""
 
-#: merge-recursive.c:1787
+#: merge-recursive.c:1788
 msgid "Fatal merge failure, shouldn't happen."
 msgstr ""
 
-#: merge-recursive.c:1806
+#: merge-recursive.c:1807
 msgid "Already up-to-date!"
 msgstr ""
 
-#: merge-recursive.c:1815
+#: merge-recursive.c:1816
 #, c-format
 msgid "merging of trees %s and %s failed"
 msgstr ""
 
-#: merge-recursive.c:1845
+#: merge-recursive.c:1846
 #, c-format
 msgid "Unprocessed path??? %s"
 msgstr ""
 
-#: merge-recursive.c:1890
+#: merge-recursive.c:1891
 msgid "Merging:"
 msgstr ""
 
-#: merge-recursive.c:1903
+#: merge-recursive.c:1904
 #, c-format
 msgid "found %u common ancestor:"
 msgid_plural "found %u common ancestors:"
 msgstr[0] ""
 msgstr[1] ""
 
-#: merge-recursive.c:1940
+#: merge-recursive.c:1941
 msgid "merge returned no commit"
 msgstr ""
 
-#: merge-recursive.c:1997
+#: merge-recursive.c:1998
 #, c-format
 msgid "Could not parse object '%s'"
 msgstr ""
 
-#: merge-recursive.c:2009 builtin/merge.c:643
+#: merge-recursive.c:2010 builtin/merge.c:672
 msgid "Unable to write index."
 msgstr ""
 
+#: notes-utils.c:40
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+
+#: notes-utils.c:81
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr ""
+
+#: notes-utils.c:91
+#, 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
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr ""
+
+#: object.c:204
+#, c-format
+msgid "unable to parse object: %s"
+msgstr ""
+
 #: parse-options.c:489
 msgid "..."
 msgstr ""
@@ -711,18 +856,18 @@ msgstr ""
 msgid "'%s' is beyond a symbolic link"
 msgstr ""
 
-#: remote.c:1653
+#: remote.c:1788
 #, 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:1659
+#: remote.c:1794
 msgid "  (use \"git push\" to publish your local commits)\n"
 msgstr ""
 
-#: remote.c:1662
+#: remote.c:1797
 #, c-format
 msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
 msgid_plural ""
@@ -730,11 +875,11 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: remote.c:1670
+#: remote.c:1805
 msgid "  (use \"git pull\" to update your local branch)\n"
 msgstr ""
 
-#: remote.c:1673
+#: remote.c:1808
 #, c-format
 msgid ""
 "Your branch and '%s' have diverged,\n"
@@ -745,257 +890,289 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: remote.c:1683
+#: remote.c:1818
 msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
 msgstr ""
 
-#: sequencer.c:123 builtin/merge.c:761 builtin/merge.c:874 builtin/merge.c:984
-#: builtin/merge.c:994
+#: run-command.c:80
+msgid "open /dev/null failed"
+msgstr ""
+
+#: run-command.c:82
+#, c-format
+msgid "dup2(%d,%d) failed"
+msgstr ""
+
+#: sequencer.c:206 builtin/merge.c:790 builtin/merge.c:903
+#: builtin/merge.c:1013 builtin/merge.c:1023
 #, c-format
 msgid "Could not open '%s' for writing"
 msgstr ""
 
-#: sequencer.c:125 builtin/merge.c:333 builtin/merge.c:764 builtin/merge.c:986
-#: builtin/merge.c:999
+#: sequencer.c:208 builtin/merge.c:350 builtin/merge.c:793
+#: builtin/merge.c:1015 builtin/merge.c:1028
 #, c-format
 msgid "Could not write to '%s'"
 msgstr ""
 
-#: sequencer.c:146
+#: sequencer.c:229
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'"
 msgstr ""
 
-#: sequencer.c:149
+#: sequencer.c:232
 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:162 sequencer.c:774 sequencer.c:857
+#: sequencer.c:245 sequencer.c:869 sequencer.c:952
 #, c-format
 msgid "Could not write to %s"
 msgstr ""
 
-#: sequencer.c:165
+#: sequencer.c:248
 #, c-format
 msgid "Error wrapping up %s"
 msgstr ""
 
-#: sequencer.c:180
+#: sequencer.c:263
 msgid "Your local changes would be overwritten by cherry-pick."
 msgstr ""
 
-#: sequencer.c:182
+#: sequencer.c:265
 msgid "Your local changes would be overwritten by revert."
 msgstr ""
 
-#: sequencer.c:185
+#: sequencer.c:268
 msgid "Commit your changes or stash them to proceed."
 msgstr ""
 
 #. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:236
+#: sequencer.c:324
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr ""
 
-#: sequencer.c:267
+#: sequencer.c:355
 msgid "Could not resolve HEAD commit\n"
 msgstr ""
 
-#: sequencer.c:288
+#: sequencer.c:377
 msgid "Unable to update cache tree\n"
 msgstr ""
 
-#: sequencer.c:333
+#: sequencer.c:422
 #, c-format
 msgid "Could not parse commit %s\n"
 msgstr ""
 
-#: sequencer.c:338
+#: sequencer.c:427
 #, c-format
 msgid "Could not parse parent commit %s\n"
 msgstr ""
 
-#: sequencer.c:404
+#: sequencer.c:493
 msgid "Your index file is unmerged."
 msgstr ""
 
-#: sequencer.c:423
+#: sequencer.c:512
 #, c-format
 msgid "Commit %s is a merge but no -m option was given."
 msgstr ""
 
-#: sequencer.c:431
+#: sequencer.c:520
 #, c-format
 msgid "Commit %s does not have parent %d"
 msgstr ""
 
-#: sequencer.c:435
+#: sequencer.c:524
 #, 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:448
+#: sequencer.c:537
 #, c-format
 msgid "%s: cannot parse parent commit %s"
 msgstr ""
 
-#: sequencer.c:452
+#: sequencer.c:541
 #, c-format
 msgid "Cannot get commit message for %s"
 msgstr ""
 
-#: sequencer.c:536
+#: sequencer.c:627
 #, c-format
 msgid "could not revert %s... %s"
 msgstr ""
 
-#: sequencer.c:537
+#: sequencer.c:628
 #, c-format
 msgid "could not apply %s... %s"
 msgstr ""
 
-#: sequencer.c:569
+#: sequencer.c:664
 msgid "empty commit set passed"
 msgstr ""
 
-#: sequencer.c:577
+#: sequencer.c:672
 #, c-format
 msgid "git %s: failed to read the index"
 msgstr ""
 
-#: sequencer.c:582
+#: sequencer.c:677
 #, c-format
 msgid "git %s: failed to refresh the index"
 msgstr ""
 
-#: sequencer.c:640
+#: sequencer.c:735
 #, c-format
 msgid "Cannot %s during a %s"
 msgstr ""
 
-#: sequencer.c:662
+#: sequencer.c:757
 #, c-format
 msgid "Could not parse line %d."
 msgstr ""
 
-#: sequencer.c:667
+#: sequencer.c:762
 msgid "No commits parsed."
 msgstr ""
 
-#: sequencer.c:680
+#: sequencer.c:775
 #, c-format
 msgid "Could not open %s"
 msgstr ""
 
-#: sequencer.c:684
+#: sequencer.c:779
 #, c-format
 msgid "Could not read %s."
 msgstr ""
 
-#: sequencer.c:691
+#: sequencer.c:786
 #, c-format
 msgid "Unusable instruction sheet: %s"
 msgstr ""
 
-#: sequencer.c:719
+#: sequencer.c:814
 #, c-format
 msgid "Invalid key: %s"
 msgstr ""
 
-#: sequencer.c:722
+#: sequencer.c:817
 #, c-format
 msgid "Invalid value for %s: %s"
 msgstr ""
 
-#: sequencer.c:734
+#: sequencer.c:829
 #, c-format
 msgid "Malformed options sheet: %s"
 msgstr ""
 
-#: sequencer.c:755
+#: sequencer.c:850
 msgid "a cherry-pick or revert is already in progress"
 msgstr ""
 
-#: sequencer.c:756
+#: sequencer.c:851
 msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
 msgstr ""
 
-#: sequencer.c:760
+#: sequencer.c:855
 #, c-format
 msgid "Could not create sequencer directory %s"
 msgstr ""
 
-#: sequencer.c:776 sequencer.c:861
+#: sequencer.c:871 sequencer.c:956
 #, c-format
 msgid "Error wrapping up %s."
 msgstr ""
 
-#: sequencer.c:795 sequencer.c:929
+#: sequencer.c:890 sequencer.c:1024
 msgid "no cherry-pick or revert in progress"
 msgstr ""
 
-#: sequencer.c:797
+#: sequencer.c:892
 msgid "cannot resolve HEAD"
 msgstr ""
 
-#: sequencer.c:799
+#: sequencer.c:894
 msgid "cannot abort from a branch yet to be born"
 msgstr ""
 
-#: sequencer.c:821 builtin/apply.c:4056
+#: sequencer.c:916 builtin/apply.c:4061
 #, c-format
 msgid "cannot open %s: %s"
 msgstr ""
 
-#: sequencer.c:824
+#: sequencer.c:919
 #, c-format
 msgid "cannot read %s: %s"
 msgstr ""
 
-#: sequencer.c:825
+#: sequencer.c:920
 msgid "unexpected end of file"
 msgstr ""
 
-#: sequencer.c:831
+#: sequencer.c:926
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
 msgstr ""
 
-#: sequencer.c:854
+#: sequencer.c:949
 #, c-format
 msgid "Could not format %s."
 msgstr ""
 
-#: sequencer.c:1016
+#: sequencer.c:1092
+#, c-format
+msgid "%s: can't cherry-pick a %s"
+msgstr ""
+
+#: sequencer.c:1095
+#, c-format
+msgid "%s: bad revision"
+msgstr ""
+
+#: sequencer.c:1129
 msgid "Can't revert as initial commit"
 msgstr ""
 
-#: sequencer.c:1017
+#: sequencer.c:1130
 msgid "Can't cherry-pick into empty head"
 msgstr ""
 
-#: sha1_name.c:1044
+#: sha1_name.c:440
+msgid ""
+"Git normally never creates a ref that ends with 40 hex characters\n"
+"because it will be ignored when you just specify 40-hex. These refs\n"
+"may be created by mistake. For example,\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+"examine these refs and maybe delete them. Turn this message off by\n"
+"running \"git config advice.objectNameWarning false\""
+msgstr ""
+
+#: sha1_name.c:1097
 msgid "HEAD does not point to a branch"
 msgstr ""
 
-#: sha1_name.c:1047
+#: sha1_name.c:1100
 #, c-format
 msgid "No such branch: '%s'"
 msgstr ""
 
-#: sha1_name.c:1049
+#: sha1_name.c:1102
 #, c-format
 msgid "No upstream configured for branch '%s'"
 msgstr ""
 
-#: sha1_name.c:1052
+#: sha1_name.c:1106
 #, c-format
 msgid "Upstream branch '%s' not stored as a remote-tracking branch"
 msgstr ""
@@ -1005,374 +1182,419 @@ msgstr ""
 msgid "unable to access '%s': %s"
 msgstr ""
 
-#: wrapper.c:423
+#: wrapper.c:429
 #, c-format
 msgid "unable to access '%s'"
 msgstr ""
 
-#: wrapper.c:434
+#: wrapper.c:440
 #, c-format
 msgid "unable to look up current user in the passwd file: %s"
 msgstr ""
 
-#: wrapper.c:435
+#: wrapper.c:441
 msgid "no such user"
 msgstr ""
 
-#: wt-status.c:140
+#: wt-status.c:141
 msgid "Unmerged paths:"
 msgstr ""
 
-#: wt-status.c:167 wt-status.c:194
+#: wt-status.c:168 wt-status.c:195
 #, c-format
 msgid "  (use \"git reset %s <file>...\" to unstage)"
 msgstr ""
 
-#: wt-status.c:169 wt-status.c:196
+#: wt-status.c:170 wt-status.c:197
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
 msgstr ""
 
-#: wt-status.c:173
+#: wt-status.c:174
 msgid "  (use \"git add <file>...\" to mark resolution)"
 msgstr ""
 
-#: wt-status.c:175 wt-status.c:179
+#: wt-status.c:176 wt-status.c:180
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
 msgstr ""
 
-#: wt-status.c:177
+#: wt-status.c:178
 msgid "  (use \"git rm <file>...\" to mark resolution)"
 msgstr ""
 
-#: wt-status.c:188
+#: wt-status.c:189
 msgid "Changes to be committed:"
 msgstr ""
 
-#: wt-status.c:206
+#: wt-status.c:207
 msgid "Changes not staged for commit:"
 msgstr ""
 
-#: wt-status.c:210
+#: wt-status.c:211
 msgid "  (use \"git add <file>...\" to update what will be committed)"
 msgstr ""
 
-#: wt-status.c:212
+#: wt-status.c:213
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
 msgstr ""
 
-#: wt-status.c:213
+#: wt-status.c:214
 msgid ""
 "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
 msgstr ""
 
-#: wt-status.c:215
+#: wt-status.c:216
 msgid "  (commit or discard the untracked or modified content in submodules)"
 msgstr ""
 
-#: wt-status.c:227
+#: wt-status.c:228
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
 msgstr ""
 
-#: wt-status.c:244
+#: wt-status.c:245
 msgid "bug"
 msgstr ""
 
-#: wt-status.c:249
+#: wt-status.c:250
 msgid "both deleted:"
 msgstr ""
 
-#: wt-status.c:250
+#: wt-status.c:251
 msgid "added by us:"
 msgstr ""
 
-#: wt-status.c:251
+#: wt-status.c:252
 msgid "deleted by them:"
 msgstr ""
 
-#: wt-status.c:252
+#: wt-status.c:253
 msgid "added by them:"
 msgstr ""
 
-#: wt-status.c:253
+#: wt-status.c:254
 msgid "deleted by us:"
 msgstr ""
 
-#: wt-status.c:254
+#: wt-status.c:255
 msgid "both added:"
 msgstr ""
 
-#: wt-status.c:255
+#: wt-status.c:256
 msgid "both modified:"
 msgstr ""
 
-#: wt-status.c:285
+#: wt-status.c:286
 msgid "new commits, "
 msgstr ""
 
-#: wt-status.c:287
+#: wt-status.c:288
 msgid "modified content, "
 msgstr ""
 
-#: wt-status.c:289
+#: wt-status.c:290
 msgid "untracked content, "
 msgstr ""
 
-#: wt-status.c:303
+#: wt-status.c:307
 #, c-format
 msgid "new file:   %s"
 msgstr ""
 
-#: wt-status.c:306
+#: wt-status.c:310
 #, c-format
 msgid "copied:     %s -> %s"
 msgstr ""
 
-#: wt-status.c:309
+#: wt-status.c:313
 #, c-format
 msgid "deleted:    %s"
 msgstr ""
 
-#: wt-status.c:312
+#: wt-status.c:316
 #, c-format
 msgid "modified:   %s"
 msgstr ""
 
-#: wt-status.c:315
+#: wt-status.c:319
 #, c-format
 msgid "renamed:    %s -> %s"
 msgstr ""
 
-#: wt-status.c:318
+#: wt-status.c:322
 #, c-format
 msgid "typechange: %s"
 msgstr ""
 
-#: wt-status.c:321
+#: wt-status.c:325
 #, c-format
 msgid "unknown:    %s"
 msgstr ""
 
-#: wt-status.c:324
+#: wt-status.c:328
 #, c-format
 msgid "unmerged:   %s"
 msgstr ""
 
-#: wt-status.c:327
+#: wt-status.c:331
 #, c-format
 msgid "bug: unhandled diff status %c"
 msgstr ""
 
-#: wt-status.c:789
+#: wt-status.c:803
 msgid "You have unmerged paths."
 msgstr ""
 
-#: wt-status.c:792 wt-status.c:944
+#: wt-status.c:806
 msgid "  (fix conflicts and run \"git commit\")"
 msgstr ""
 
-#: wt-status.c:795
+#: wt-status.c:809
 msgid "All conflicts fixed but you are still merging."
 msgstr ""
 
-#: wt-status.c:798
+#: wt-status.c:812
 msgid "  (use \"git commit\" to conclude merge)"
 msgstr ""
 
-#: wt-status.c:808
+#: wt-status.c:822
 msgid "You are in the middle of an am session."
 msgstr ""
 
-#: wt-status.c:811
+#: wt-status.c:825
 msgid "The current patch is empty."
 msgstr ""
 
-#: wt-status.c:815
-msgid "  (fix conflicts and then run \"git am --resolved\")"
+#: wt-status.c:829
+msgid "  (fix conflicts and then run \"git am --continue\")"
 msgstr ""
 
-#: wt-status.c:817
+#: wt-status.c:831
 msgid "  (use \"git am --skip\" to skip this patch)"
 msgstr ""
 
-#: wt-status.c:819
+#: wt-status.c:833
 msgid "  (use \"git am --abort\" to restore the original branch)"
 msgstr ""
 
-#: wt-status.c:879 wt-status.c:896
+#: wt-status.c:893 wt-status.c:910
 #, c-format
 msgid "You are currently rebasing branch '%s' on '%s'."
 msgstr ""
 
-#: wt-status.c:884 wt-status.c:901
+#: wt-status.c:898 wt-status.c:915
 msgid "You are currently rebasing."
 msgstr ""
 
-#: wt-status.c:887
+#: wt-status.c:901
 msgid "  (fix conflicts and then run \"git rebase --continue\")"
 msgstr ""
 
-#: wt-status.c:889
+#: wt-status.c:903
 msgid "  (use \"git rebase --skip\" to skip this patch)"
 msgstr ""
 
-#: wt-status.c:891
+#: wt-status.c:905
 msgid "  (use \"git rebase --abort\" to check out the original branch)"
 msgstr ""
 
-#: wt-status.c:904
+#: wt-status.c:918
 msgid "  (all conflicts fixed: run \"git rebase --continue\")"
 msgstr ""
 
-#: wt-status.c:908
+#: wt-status.c:922
 #, c-format
 msgid ""
 "You are currently splitting a commit while rebasing branch '%s' on '%s'."
 msgstr ""
 
-#: wt-status.c:913
+#: wt-status.c:927
 msgid "You are currently splitting a commit during a rebase."
 msgstr ""
 
-#: wt-status.c:916
+#: wt-status.c:930
 msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
 msgstr ""
 
-#: wt-status.c:920
+#: wt-status.c:934
 #, c-format
 msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
 msgstr ""
 
-#: wt-status.c:925
+#: wt-status.c:939
 msgid "You are currently editing a commit during a rebase."
 msgstr ""
 
-#: wt-status.c:928
+#: wt-status.c:942
 msgid "  (use \"git commit --amend\" to amend the current commit)"
 msgstr ""
 
-#: wt-status.c:930
+#: wt-status.c:944
 msgid ""
 "  (use \"git rebase --continue\" once you are satisfied with your changes)"
 msgstr ""
 
-#: wt-status.c:940
+#: wt-status.c:954
 msgid "You are currently cherry-picking."
 msgstr ""
 
-#: wt-status.c:947
-msgid "  (all conflicts fixed: run \"git commit\")"
+#: wt-status.c:958
+msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
 msgstr ""
 
-#: wt-status.c:958
+#: wt-status.c:961
+msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
+msgstr ""
+
+#: wt-status.c:963
+msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
+msgstr ""
+
+#: wt-status.c:972
+#, c-format
+msgid "You are currently reverting commit %s."
+msgstr ""
+
+#: wt-status.c:977
+msgid "  (fix conflicts and run \"git revert --continue\")"
+msgstr ""
+
+#: wt-status.c:980
+msgid "  (all conflicts fixed: run \"git revert --continue\")"
+msgstr ""
+
+#: wt-status.c:982
+msgid "  (use \"git revert --abort\" to cancel the revert operation)"
+msgstr ""
+
+#: wt-status.c:993
 #, c-format
-msgid "You are currently bisecting branch '%s'."
+msgid "You are currently bisecting, started from branch '%s'."
 msgstr ""
 
-#: wt-status.c:962
+#: wt-status.c:997
 msgid "You are currently bisecting."
 msgstr ""
 
-#: wt-status.c:965
+#: wt-status.c:1000
 msgid "  (use \"git bisect reset\" to get back to the original branch)"
 msgstr ""
 
-#: wt-status.c:1064
+#: wt-status.c:1173
 msgid "On branch "
 msgstr ""
 
-#: wt-status.c:1071
+#: wt-status.c:1180
+msgid "rebase in progress; onto "
+msgstr ""
+
+#: wt-status.c:1187
+msgid "HEAD detached at "
+msgstr ""
+
+#: wt-status.c:1189
+msgid "HEAD detached from "
+msgstr ""
+
+#: wt-status.c:1192
 msgid "Not currently on any branch."
 msgstr ""
 
-#: wt-status.c:1083
+#: wt-status.c:1209
 msgid "Initial commit"
 msgstr ""
 
-#: wt-status.c:1097
+#: wt-status.c:1223
 msgid "Untracked files"
 msgstr ""
 
-#: wt-status.c:1099
+#: wt-status.c:1225
 msgid "Ignored files"
 msgstr ""
 
-#: wt-status.c:1101
+#: wt-status.c:1229
+#, c-format
+msgid ""
+"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
+"may speed it up, but you have to be careful not to forget to add\n"
+"new files yourself (see 'git help status')."
+msgstr ""
+
+#: wt-status.c:1235
 #, c-format
 msgid "Untracked files not listed%s"
 msgstr ""
 
-#: wt-status.c:1103
+#: wt-status.c:1237
 msgid " (use -u option to show untracked files)"
 msgstr ""
 
-#: wt-status.c:1109
+#: wt-status.c:1243
 msgid "No changes"
 msgstr ""
 
-#: wt-status.c:1114
+#: wt-status.c:1248
 #, c-format
 msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
 msgstr ""
 
-#: wt-status.c:1117
+#: wt-status.c:1251
 #, c-format
 msgid "no changes added to commit\n"
 msgstr ""
 
-#: wt-status.c:1120
+#: wt-status.c:1254
 #, c-format
 msgid ""
 "nothing added to commit but untracked files present (use \"git add\" to "
 "track)\n"
 msgstr ""
 
-#: wt-status.c:1123
+#: wt-status.c:1257
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
 msgstr ""
 
-#: wt-status.c:1126
+#: wt-status.c:1260
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
 msgstr ""
 
-#: wt-status.c:1129 wt-status.c:1134
+#: wt-status.c:1263 wt-status.c:1268
 #, c-format
 msgid "nothing to commit\n"
 msgstr ""
 
-#: wt-status.c:1132
+#: wt-status.c:1266
 #, c-format
 msgid "nothing to commit (use -u to show untracked files)\n"
 msgstr ""
 
-#: wt-status.c:1136
+#: wt-status.c:1270
 #, c-format
 msgid "nothing to commit, working directory clean\n"
 msgstr ""
 
-#: wt-status.c:1244
+#: wt-status.c:1378
 msgid "HEAD (no branch)"
 msgstr ""
 
-#: wt-status.c:1250
+#: wt-status.c:1384
 msgid "Initial commit on "
 msgstr ""
 
-#: wt-status.c:1265
+#: wt-status.c:1399
 msgid "behind "
 msgstr ""
 
-#: wt-status.c:1268 wt-status.c:1271
+#: wt-status.c:1402 wt-status.c:1405
 msgid "ahead "
 msgstr ""
 
-#: wt-status.c:1273
+#: wt-status.c:1407
 msgid ", behind "
 msgstr ""
 
-#: compat/precompose_utf8.c:58 builtin/clone.c:341
+#: compat/precompose_utf8.c:58 builtin/clone.c:351
 #, c-format
 msgid "failed to unlink '%s'"
 msgstr ""
@@ -1381,179 +1603,200 @@ msgstr ""
 msgid "git add [options] [--] <pathspec>..."
 msgstr ""
 
-#: builtin/add.c:63
+#.
+#. * To be consistent with "git add -p" and most Git
+#. * commands, we should default to being tree-wide, but
+#. * this is not the original behavior and can't be
+#. * changed until users trained themselves not to type
+#. * "git add -u" or "git add -A". For now, we warn and
+#. * keep the old behavior. Later, the behavior can be changed
+#. * to tree-wide, keeping the warning for a while, and
+#. * eventually we can drop the warning.
+#.
+#: builtin/add.c:58
+#, c-format
+msgid ""
+"The behavior of 'git add %s (or %s)' with no path argument from a\n"
+"subdirectory of the tree will change in Git 2.0 and should not be used "
+"anymore.\n"
+"To add content for the whole tree, run:\n"
+"\n"
+"  git add %s :/\n"
+"  (or git add %s :/)\n"
+"\n"
+"To restrict the command to the current directory, run:\n"
+"\n"
+"  git add %s .\n"
+"  (or git add %s .)\n"
+"\n"
+"With the current Git version, the command is restricted to the current "
+"directory.\n"
+msgstr ""
+
+#: builtin/add.c:100
+#, c-format
+msgid ""
+"You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
+"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
+"Paths like '%s' that are\n"
+"removed from your working tree are ignored with this version of Git.\n"
+"\n"
+"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
+"  ignores paths you removed from your working tree.\n"
+"\n"
+"* 'git add --all <pathspec>' will let you also record the removals.\n"
+"\n"
+"Run 'git status' to check the paths you removed from your working tree.\n"
+msgstr ""
+
+#: builtin/add.c:144
 #, c-format
 msgid "unexpected diff status %c"
 msgstr ""
 
-#: builtin/add.c:68 builtin/commit.c:231
+#: builtin/add.c:149 builtin/commit.c:252
 msgid "updating files failed"
 msgstr ""
 
-#: builtin/add.c:78
+#: builtin/add.c:163
 #, c-format
 msgid "remove '%s'\n"
 msgstr ""
 
-#: builtin/add.c:148
+#: builtin/add.c:253
 msgid "Unstaged changes after refreshing the index:"
 msgstr ""
 
-#: builtin/add.c:151 builtin/add.c:460 builtin/rm.c:275
+#: builtin/add.c:256 builtin/add.c:573 builtin/rm.c:337
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr ""
 
-#: builtin/add.c:234
+#: builtin/add.c:339
 msgid "Could not read the index"
 msgstr ""
 
-#: builtin/add.c:244
+#: builtin/add.c:350
 #, c-format
 msgid "Could not open '%s' for writing."
 msgstr ""
 
-#: builtin/add.c:248
+#: builtin/add.c:354
 msgid "Could not write patch"
 msgstr ""
 
-#: builtin/add.c:253
+#: builtin/add.c:359
 #, c-format
 msgid "Could not stat '%s'"
 msgstr ""
 
-#: builtin/add.c:255
+#: builtin/add.c:361
 msgid "Empty patch. Aborted."
 msgstr ""
 
-#: builtin/add.c:261
+#: builtin/add.c:367
 #, c-format
 msgid "Could not apply '%s'"
 msgstr ""
 
-#: builtin/add.c:271
+#: builtin/add.c:377
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
 
-#: builtin/add.c:277 builtin/clean.c:161 builtin/fetch.c:78 builtin/mv.c:63
-#: builtin/prune-packed.c:76 builtin/push.c:425 builtin/remote.c:1253
-#: builtin/rm.c:206
+#: builtin/add.c:394 builtin/clean.c:875 builtin/fetch.c:78 builtin/mv.c:63
+#: builtin/prune-packed.c:73 builtin/push.c:451 builtin/remote.c:1253
+#: builtin/rm.c:268
 msgid "dry run"
 msgstr ""
 
-#: builtin/add.c:278 builtin/apply.c:4405 builtin/check-ignore.c:19
-#: builtin/commit.c:1150 builtin/count-objects.c:82 builtin/fsck.c:613
-#: builtin/log.c:1522 builtin/mv.c:62 builtin/read-tree.c:112
+#: builtin/add.c:395 builtin/apply.c:4410 builtin/check-ignore.c:19
+#: builtin/commit.c:1220 builtin/count-objects.c:95 builtin/fsck.c:613
+#: builtin/log.c:1573 builtin/mv.c:62 builtin/read-tree.c:113
 msgid "be verbose"
 msgstr ""
 
-#: builtin/add.c:280
+#: builtin/add.c:397
 msgid "interactive picking"
 msgstr ""
 
-#: builtin/add.c:281 builtin/checkout.c:1031 builtin/reset.c:258
+#: builtin/add.c:398 builtin/checkout.c:1073 builtin/reset.c:261
 msgid "select hunks interactively"
 msgstr ""
 
-#: builtin/add.c:282
+#: builtin/add.c:399
 msgid "edit current diff and apply"
 msgstr ""
 
-#: builtin/add.c:283
+#: builtin/add.c:400
 msgid "allow adding otherwise ignored files"
 msgstr ""
 
-#: builtin/add.c:284
+#: builtin/add.c:401
 msgid "update tracked files"
 msgstr ""
 
-#: builtin/add.c:285
+#: builtin/add.c:402
 msgid "record only the fact that the path will be added later"
 msgstr ""
 
-#: builtin/add.c:286
+#: builtin/add.c:403
 msgid "add changes from all tracked and untracked files"
 msgstr ""
 
-#: builtin/add.c:287
+#. takes no arguments
+#: builtin/add.c:406
+msgid "ignore paths removed in the working tree (same as --no-all)"
+msgstr ""
+
+#: builtin/add.c:408
 msgid "don't add, only refresh the index"
 msgstr ""
 
-#: builtin/add.c:288
+#: builtin/add.c:409
 msgid "just skip files which cannot be added because of errors"
 msgstr ""
 
-#: builtin/add.c:289
+#: builtin/add.c:410
 msgid "check if - even missing - files are ignored in dry run"
 msgstr ""
 
-#: builtin/add.c:311
+#: builtin/add.c:432
 #, c-format
 msgid "Use -f if you really want to add them.\n"
 msgstr ""
 
-#: builtin/add.c:312
+#: builtin/add.c:433
 msgid "no files added"
 msgstr ""
 
-#: builtin/add.c:318
+#: builtin/add.c:439
 msgid "adding files failed"
 msgstr ""
 
-#.
-#. * To be consistent with "git add -p" and most Git
-#. * commands, we should default to being tree-wide, but
-#. * this is not the original behavior and can't be
-#. * changed until users trained themselves not to type
-#. * "git add -u" or "git add -A". For now, we warn and
-#. * keep the old behavior. Later, this warning can be
-#. * turned into a die(...), and eventually we may
-#. * reallow the command with a new behavior.
-#.
-#: builtin/add.c:335
-#, c-format
-msgid ""
-"The behavior of 'git add %s (or %s)' with no path argument from a\n"
-"subdirectory of the tree will change in Git 2.0 and should not be used "
-"anymore.\n"
-"To add content for the whole tree, run:\n"
-"\n"
-"  git add %s :/\n"
-"  (or git add %s :/)\n"
-"\n"
-"To restrict the command to the current directory, run:\n"
-"\n"
-"  git add %s .\n"
-"  (or git add %s .)\n"
-"\n"
-"With the current Git version, the command is restricted to the current "
-"directory."
-msgstr ""
-
-#: builtin/add.c:381
+#: builtin/add.c:478
 msgid "-A and -u are mutually incompatible"
 msgstr ""
 
-#: builtin/add.c:383
+#: builtin/add.c:496
 msgid "Option --ignore-missing can only be used together with --dry-run"
 msgstr ""
 
-#: builtin/add.c:414
+#: builtin/add.c:526
 #, c-format
 msgid "Nothing specified, nothing added.\n"
 msgstr ""
 
-#: builtin/add.c:415
+#: builtin/add.c:527
 #, c-format
 msgid "Maybe you wanted to say 'git add .'?\n"
 msgstr ""
 
-#: builtin/add.c:421 builtin/check-ignore.c:67 builtin/clean.c:204
-#: builtin/commit.c:291 builtin/mv.c:82 builtin/rm.c:235
+#: builtin/add.c:533 builtin/check-ignore.c:161 builtin/clean.c:919
+#: builtin/commit.c:312 builtin/mv.c:82 builtin/rm.c:297
 msgid "index file corrupt"
 msgstr ""
 
-#: builtin/add.c:481 builtin/apply.c:4501 builtin/mv.c:229 builtin/rm.c:370
+#: builtin/add.c:605 builtin/apply.c:4506 builtin/mv.c:229 builtin/rm.c:432
 msgid "Unable to write new index file"
 msgstr ""
 
@@ -1681,342 +1924,342 @@ msgstr ""
 msgid "unable to open or read %s"
 msgstr ""
 
-#: builtin/apply.c:2684
+#: builtin/apply.c:2688
 #, c-format
 msgid "invalid start of line: '%c'"
 msgstr ""
 
-#: builtin/apply.c:2802
+#: builtin/apply.c:2806
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
 msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/apply.c:2814
+#: builtin/apply.c:2818
 #, c-format
 msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
 msgstr ""
 
-#: builtin/apply.c:2820
+#: builtin/apply.c:2824
 #, c-format
 msgid ""
 "while searching for:\n"
 "%.*s"
 msgstr ""
 
-#: builtin/apply.c:2839
+#: builtin/apply.c:2843
 #, c-format
 msgid "missing binary patch data for '%s'"
 msgstr ""
 
-#: builtin/apply.c:2942
+#: builtin/apply.c:2946
 #, c-format
 msgid "binary patch does not apply to '%s'"
 msgstr ""
 
-#: builtin/apply.c:2948
+#: builtin/apply.c:2952
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
 msgstr ""
 
-#: builtin/apply.c:2969
+#: builtin/apply.c:2973
 #, c-format
 msgid "patch failed: %s:%ld"
 msgstr ""
 
-#: builtin/apply.c:3091
+#: builtin/apply.c:3095
 #, c-format
 msgid "cannot checkout %s"
 msgstr ""
 
-#: builtin/apply.c:3136 builtin/apply.c:3145 builtin/apply.c:3189
+#: builtin/apply.c:3140 builtin/apply.c:3149 builtin/apply.c:3194
 #, c-format
 msgid "read of %s failed"
 msgstr ""
 
-#: builtin/apply.c:3169 builtin/apply.c:3391
+#: builtin/apply.c:3174 builtin/apply.c:3396
 #, c-format
 msgid "path %s has been renamed/deleted"
 msgstr ""
 
-#: builtin/apply.c:3250 builtin/apply.c:3405
+#: builtin/apply.c:3255 builtin/apply.c:3410
 #, c-format
 msgid "%s: does not exist in index"
 msgstr ""
 
-#: builtin/apply.c:3254 builtin/apply.c:3397 builtin/apply.c:3419
+#: builtin/apply.c:3259 builtin/apply.c:3402 builtin/apply.c:3424
 #, c-format
 msgid "%s: %s"
 msgstr ""
 
-#: builtin/apply.c:3259 builtin/apply.c:3413
+#: builtin/apply.c:3264 builtin/apply.c:3418
 #, c-format
 msgid "%s: does not match index"
 msgstr ""
 
-#: builtin/apply.c:3361
+#: builtin/apply.c:3366
 msgid "removal patch leaves file contents"
 msgstr ""
 
-#: builtin/apply.c:3430
+#: builtin/apply.c:3435
 #, c-format
 msgid "%s: wrong type"
 msgstr ""
 
-#: builtin/apply.c:3432
+#: builtin/apply.c:3437
 #, c-format
 msgid "%s has type %o, expected %o"
 msgstr ""
 
-#: builtin/apply.c:3533
+#: builtin/apply.c:3538
 #, c-format
 msgid "%s: already exists in index"
 msgstr ""
 
-#: builtin/apply.c:3536
+#: builtin/apply.c:3541
 #, c-format
 msgid "%s: already exists in working directory"
 msgstr ""
 
-#: builtin/apply.c:3556
+#: builtin/apply.c:3561
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o)"
 msgstr ""
 
-#: builtin/apply.c:3561
+#: builtin/apply.c:3566
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
 msgstr ""
 
-#: builtin/apply.c:3569
+#: builtin/apply.c:3574
 #, c-format
 msgid "%s: patch does not apply"
 msgstr ""
 
-#: builtin/apply.c:3582
+#: builtin/apply.c:3587
 #, c-format
 msgid "Checking patch %s..."
 msgstr ""
 
-#: builtin/apply.c:3675 builtin/checkout.c:215 builtin/reset.c:124
+#: builtin/apply.c:3680 builtin/checkout.c:216 builtin/reset.c:127
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
 msgstr ""
 
-#: builtin/apply.c:3818
+#: builtin/apply.c:3823
 #, c-format
 msgid "unable to remove %s from index"
 msgstr ""
 
-#: builtin/apply.c:3846
+#: builtin/apply.c:3851
 #, c-format
-msgid "corrupt patch for subproject %s"
+msgid "corrupt patch for submodule %s"
 msgstr ""
 
-#: builtin/apply.c:3850
+#: builtin/apply.c:3855
 #, c-format
 msgid "unable to stat newly created file '%s'"
 msgstr ""
 
-#: builtin/apply.c:3855
+#: builtin/apply.c:3860
 #, c-format
 msgid "unable to create backing store for newly created file %s"
 msgstr ""
 
-#: builtin/apply.c:3858 builtin/apply.c:3966
+#: builtin/apply.c:3863 builtin/apply.c:3971
 #, c-format
 msgid "unable to add cache entry for %s"
 msgstr ""
 
-#: builtin/apply.c:3891
+#: builtin/apply.c:3896
 #, c-format
 msgid "closing file '%s'"
 msgstr ""
 
-#: builtin/apply.c:3940
+#: builtin/apply.c:3945
 #, c-format
 msgid "unable to write file '%s' mode %o"
 msgstr ""
 
-#: builtin/apply.c:4027
+#: builtin/apply.c:4032
 #, c-format
 msgid "Applied patch %s cleanly."
 msgstr ""
 
-#: builtin/apply.c:4035
+#: builtin/apply.c:4040
 msgid "internal error"
 msgstr ""
 
 #. Say this even without --verbose
-#: builtin/apply.c:4038
+#: builtin/apply.c:4043
 #, c-format
 msgid "Applying patch %%s with %d reject..."
 msgid_plural "Applying patch %%s with %d rejects..."
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/apply.c:4048
+#: builtin/apply.c:4053
 #, c-format
 msgid "truncating .rej filename to %.*s.rej"
 msgstr ""
 
-#: builtin/apply.c:4069
+#: builtin/apply.c:4074
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr ""
 
-#: builtin/apply.c:4072
+#: builtin/apply.c:4077
 #, c-format
 msgid "Rejected hunk #%d."
 msgstr ""
 
-#: builtin/apply.c:4222
+#: builtin/apply.c:4227
 msgid "unrecognized input"
 msgstr ""
 
-#: builtin/apply.c:4233
+#: builtin/apply.c:4238
 msgid "unable to read index file"
 msgstr ""
 
-#: builtin/apply.c:4352 builtin/apply.c:4355 builtin/clone.c:91
+#: builtin/apply.c:4357 builtin/apply.c:4360 builtin/clone.c:91
 #: builtin/fetch.c:63
 msgid "path"
 msgstr ""
 
-#: builtin/apply.c:4353
+#: builtin/apply.c:4358
 msgid "don't apply changes matching the given path"
 msgstr ""
 
-#: builtin/apply.c:4356
+#: builtin/apply.c:4361
 msgid "apply changes matching the given path"
 msgstr ""
 
-#: builtin/apply.c:4358
+#: builtin/apply.c:4363
 msgid "num"
 msgstr ""
 
-#: builtin/apply.c:4359
+#: builtin/apply.c:4364
 msgid "remove <num> leading slashes from traditional diff paths"
 msgstr ""
 
-#: builtin/apply.c:4362
+#: builtin/apply.c:4367
 msgid "ignore additions made by the patch"
 msgstr ""
 
-#: builtin/apply.c:4364
+#: builtin/apply.c:4369
 msgid "instead of applying the patch, output diffstat for the input"
 msgstr ""
 
-#: builtin/apply.c:4368
+#: builtin/apply.c:4373
 msgid "show number of added and deleted lines in decimal notation"
 msgstr ""
 
-#: builtin/apply.c:4370
+#: builtin/apply.c:4375
 msgid "instead of applying the patch, output a summary for the input"
 msgstr ""
 
-#: builtin/apply.c:4372
+#: builtin/apply.c:4377
 msgid "instead of applying the patch, see if the patch is applicable"
 msgstr ""
 
-#: builtin/apply.c:4374
+#: builtin/apply.c:4379
 msgid "make sure the patch is applicable to the current index"
 msgstr ""
 
-#: builtin/apply.c:4376
+#: builtin/apply.c:4381
 msgid "apply a patch without touching the working tree"
 msgstr ""
 
-#: builtin/apply.c:4378
+#: builtin/apply.c:4383
 msgid "also apply the patch (use with --stat/--summary/--check)"
 msgstr ""
 
-#: builtin/apply.c:4380
+#: builtin/apply.c:4385
 msgid "attempt three-way merge if a patch does not apply"
 msgstr ""
 
-#: builtin/apply.c:4382
+#: builtin/apply.c:4387
 msgid "build a temporary index based on embedded index information"
 msgstr ""
 
-#: builtin/apply.c:4384 builtin/checkout-index.c:197 builtin/ls-files.c:463
+#: builtin/apply.c:4389 builtin/checkout-index.c:197 builtin/ls-files.c:462
 msgid "paths are separated with NUL character"
 msgstr ""
 
-#: builtin/apply.c:4387
+#: builtin/apply.c:4392
 msgid "ensure at least <n> lines of context match"
 msgstr ""
 
-#: builtin/apply.c:4388
+#: builtin/apply.c:4393
 msgid "action"
 msgstr ""
 
-#: builtin/apply.c:4389
+#: builtin/apply.c:4394
 msgid "detect new or modified lines that have whitespace errors"
 msgstr ""
 
-#: builtin/apply.c:4392 builtin/apply.c:4395
+#: builtin/apply.c:4397 builtin/apply.c:4400
 msgid "ignore changes in whitespace when finding context"
 msgstr ""
 
-#: builtin/apply.c:4398
+#: builtin/apply.c:4403
 msgid "apply the patch in reverse"
 msgstr ""
 
-#: builtin/apply.c:4400
+#: builtin/apply.c:4405
 msgid "don't expect at least one line of context"
 msgstr ""
 
-#: builtin/apply.c:4402
+#: builtin/apply.c:4407
 msgid "leave the rejected hunks in corresponding *.rej files"
 msgstr ""
 
-#: builtin/apply.c:4404
+#: builtin/apply.c:4409
 msgid "allow overlapping hunks"
 msgstr ""
 
-#: builtin/apply.c:4407
+#: builtin/apply.c:4412
 msgid "tolerate incorrectly detected missing new-line at the end of file"
 msgstr ""
 
-#: builtin/apply.c:4410
+#: builtin/apply.c:4415
 msgid "do not trust the line counts in the hunk headers"
 msgstr ""
 
-#: builtin/apply.c:4412
+#: builtin/apply.c:4417
 msgid "root"
 msgstr ""
 
-#: builtin/apply.c:4413
+#: builtin/apply.c:4418
 msgid "prepend <root> to all filenames"
 msgstr ""
 
-#: builtin/apply.c:4435
+#: builtin/apply.c:4440
 msgid "--3way outside a repository"
 msgstr ""
 
-#: builtin/apply.c:4443
+#: builtin/apply.c:4448
 msgid "--index outside a repository"
 msgstr ""
 
-#: builtin/apply.c:4446
+#: builtin/apply.c:4451
 msgid "--cached outside a repository"
 msgstr ""
 
-#: builtin/apply.c:4462
+#: builtin/apply.c:4467
 #, c-format
 msgid "can't open patch '%s'"
 msgstr ""
 
-#: builtin/apply.c:4476
+#: builtin/apply.c:4481
 #, c-format
 msgid "squelched %d whitespace error"
 msgid_plural "squelched %d whitespace errors"
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/apply.c:4482 builtin/apply.c:4492
+#: builtin/apply.c:4487 builtin/apply.c:4497
 #, c-format
 msgid "%d line adds whitespace errors."
 msgid_plural "%d lines add whitespace errors."
@@ -2040,21 +2283,21 @@ msgstr ""
 msgid "git archive: expected ACK/NAK, got EOF"
 msgstr ""
 
-#: builtin/archive.c:63
+#: builtin/archive.c:61
 #, c-format
 msgid "git archive: NACK %s"
 msgstr ""
 
-#: builtin/archive.c:65
+#: builtin/archive.c:63
 #, c-format
 msgid "remote error: %s"
 msgstr ""
 
-#: builtin/archive.c:66
+#: builtin/archive.c:64
 msgid "git archive: protocol error"
 msgstr ""
 
-#: builtin/archive.c:71
+#: builtin/archive.c:68
 msgid "git archive: expected a flush"
 msgstr ""
 
@@ -2070,434 +2313,476 @@ msgstr ""
 msgid "update BISECT_HEAD instead of checking out the current commit"
 msgstr ""
 
-#: builtin/blame.c:25
+#: builtin/blame.c:26
 msgid "git blame [options] [rev-opts] [rev] [--] file"
 msgstr ""
 
-#: builtin/blame.c:30 builtin/shortlog.c:15
+#: builtin/blame.c:31
 msgid "[rev-opts] are documented in git-rev-list(1)"
 msgstr ""
 
-#: builtin/blame.c:2350
+#: builtin/blame.c:2276
 msgid "Show blame entries as we find them, incrementally"
 msgstr ""
 
-#: builtin/blame.c:2351
+#: builtin/blame.c:2277
 msgid "Show blank SHA-1 for boundary commits (Default: off)"
 msgstr ""
 
-#: builtin/blame.c:2352
+#: builtin/blame.c:2278
 msgid "Do not treat root commits as boundaries (Default: off)"
 msgstr ""
 
-#: builtin/blame.c:2353
+#: builtin/blame.c:2279
 msgid "Show work cost statistics"
 msgstr ""
 
-#: builtin/blame.c:2354
+#: builtin/blame.c:2280
 msgid "Show output score for blame entries"
 msgstr ""
 
-#: builtin/blame.c:2355
+#: builtin/blame.c:2281
 msgid "Show original filename (Default: auto)"
 msgstr ""
 
-#: builtin/blame.c:2356
+#: builtin/blame.c:2282
 msgid "Show original linenumber (Default: off)"
 msgstr ""
 
-#: builtin/blame.c:2357
+#: builtin/blame.c:2283
 msgid "Show in a format designed for machine consumption"
 msgstr ""
 
-#: builtin/blame.c:2358
+#: builtin/blame.c:2284
 msgid "Show porcelain format with per-line commit information"
 msgstr ""
 
-#: builtin/blame.c:2359
+#: builtin/blame.c:2285
 msgid "Use the same output mode as git-annotate (Default: off)"
 msgstr ""
 
-#: builtin/blame.c:2360
+#: builtin/blame.c:2286
 msgid "Show raw timestamp (Default: off)"
 msgstr ""
 
-#: builtin/blame.c:2361
+#: builtin/blame.c:2287
 msgid "Show long commit SHA1 (Default: off)"
 msgstr ""
 
-#: builtin/blame.c:2362
+#: builtin/blame.c:2288
 msgid "Suppress author name and timestamp (Default: off)"
 msgstr ""
 
-#: builtin/blame.c:2363
+#: builtin/blame.c:2289
 msgid "Show author email instead of name (Default: off)"
 msgstr ""
 
-#: builtin/blame.c:2364
+#: builtin/blame.c:2290
 msgid "Ignore whitespace differences"
 msgstr ""
 
-#: builtin/blame.c:2365
+#: builtin/blame.c:2291
 msgid "Spend extra cycles to find better match"
 msgstr ""
 
-#: builtin/blame.c:2366
+#: builtin/blame.c:2292
 msgid "Use revisions from <file> instead of calling git-rev-list"
 msgstr ""
 
-#: builtin/blame.c:2367
+#: builtin/blame.c:2293
 msgid "Use <file>'s contents as the final image"
 msgstr ""
 
-#: builtin/blame.c:2368 builtin/blame.c:2369
+#: builtin/blame.c:2294 builtin/blame.c:2295
 msgid "score"
 msgstr ""
 
-#: builtin/blame.c:2368
+#: builtin/blame.c:2294
 msgid "Find line copies within and across files"
 msgstr ""
 
-#: builtin/blame.c:2369
+#: builtin/blame.c:2295
 msgid "Find line movements within and across files"
 msgstr ""
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "n,m"
 msgstr ""
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "Process only line range n,m, counting from 1"
 msgstr ""
 
-#: builtin/branch.c:23
+#: builtin/branch.c:24
 msgid "git branch [options] [-r | -a] [--merged | --no-merged]"
 msgstr ""
 
-#: builtin/branch.c:24
+#: builtin/branch.c:25
 msgid "git branch [options] [-l] [-f] <branchname> [<start-point>]"
 msgstr ""
 
-#: builtin/branch.c:25
+#: builtin/branch.c:26
 msgid "git branch [options] [-r] (-d | -D) <branchname>..."
 msgstr ""
 
-#: builtin/branch.c:26
+#: builtin/branch.c:27
 msgid "git branch [options] (-m | -M) [<oldbranch>] <newbranch>"
 msgstr ""
 
-#: builtin/branch.c:145
+#: builtin/branch.c:150
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
 "         '%s', but not yet merged to HEAD."
 msgstr ""
 
-#: builtin/branch.c:149
+#: builtin/branch.c:154
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
 "         '%s', even though it is merged to HEAD."
 msgstr ""
 
-#: builtin/branch.c:163
+#: builtin/branch.c:168
 #, c-format
 msgid "Couldn't look up commit object for '%s'"
 msgstr ""
 
-#: builtin/branch.c:167
+#: builtin/branch.c:172
 #, c-format
 msgid ""
 "The branch '%s' is not fully merged.\n"
 "If you are sure you want to delete it, run 'git branch -D %s'."
 msgstr ""
 
-#: builtin/branch.c:180
+#: builtin/branch.c:185
 msgid "Update of config-file failed"
 msgstr ""
 
-#: builtin/branch.c:208
+#: builtin/branch.c:213
 msgid "cannot use -a with -d"
 msgstr ""
 
-#: builtin/branch.c:214
+#: builtin/branch.c:219
 msgid "Couldn't look up commit object for HEAD"
 msgstr ""
 
-#: builtin/branch.c:222
+#: builtin/branch.c:227
 #, c-format
 msgid "Cannot delete the branch '%s' which you are currently on."
 msgstr ""
 
-#: builtin/branch.c:235
+#: builtin/branch.c:240
 #, c-format
 msgid "remote branch '%s' not found."
 msgstr ""
 
-#: builtin/branch.c:236
+#: builtin/branch.c:241
 #, c-format
 msgid "branch '%s' not found."
 msgstr ""
 
-#: builtin/branch.c:250
+#: builtin/branch.c:255
 #, c-format
 msgid "Error deleting remote branch '%s'"
 msgstr ""
 
-#: builtin/branch.c:251
+#: builtin/branch.c:256
 #, c-format
 msgid "Error deleting branch '%s'"
 msgstr ""
 
-#: builtin/branch.c:258
+#: builtin/branch.c:263
 #, c-format
 msgid "Deleted remote branch %s (was %s).\n"
 msgstr ""
 
-#: builtin/branch.c:259
+#: builtin/branch.c:264
 #, c-format
 msgid "Deleted branch %s (was %s).\n"
 msgstr ""
 
-#: builtin/branch.c:361
+#: builtin/branch.c:366
 #, c-format
 msgid "branch '%s' does not point at a commit"
 msgstr ""
 
-#: builtin/branch.c:433
+#: builtin/branch.c:453
 #, c-format
 msgid "[%s: behind %d]"
 msgstr ""
 
-#: builtin/branch.c:435
+#: builtin/branch.c:455
 #, c-format
 msgid "[behind %d]"
 msgstr ""
 
-#: builtin/branch.c:439
+#: builtin/branch.c:459
 #, c-format
 msgid "[%s: ahead %d]"
 msgstr ""
 
-#: builtin/branch.c:441
+#: builtin/branch.c:461
 #, c-format
 msgid "[ahead %d]"
 msgstr ""
 
-#: builtin/branch.c:444
+#: builtin/branch.c:464
 #, c-format
 msgid "[%s: ahead %d, behind %d]"
 msgstr ""
 
-#: builtin/branch.c:447
+#: builtin/branch.c:467
 #, c-format
 msgid "[ahead %d, behind %d]"
 msgstr ""
 
-#: builtin/branch.c:469
+#: builtin/branch.c:490
 msgid " **** invalid ref ****"
 msgstr ""
 
-#: builtin/branch.c:560
+#: builtin/branch.c:582
+#, c-format
+msgid "(no branch, rebasing %s)"
+msgstr ""
+
+#: builtin/branch.c:585
+#, c-format
+msgid "(no branch, bisect started on %s)"
+msgstr ""
+
+#: builtin/branch.c:588
+#, c-format
+msgid "(detached from %s)"
+msgstr ""
+
+#: builtin/branch.c:591
 msgid "(no branch)"
 msgstr ""
 
-#: builtin/branch.c:593
+#: builtin/branch.c:637
 #, c-format
 msgid "object '%s' does not point to a commit"
 msgstr ""
 
-#: builtin/branch.c:625
+#: builtin/branch.c:669
 msgid "some refs could not be read"
 msgstr ""
 
-#: builtin/branch.c:638
+#: builtin/branch.c:682
 msgid "cannot rename the current branch while not on any."
 msgstr ""
 
-#: builtin/branch.c:648
+#: builtin/branch.c:692
 #, c-format
 msgid "Invalid branch name: '%s'"
 msgstr ""
 
-#: builtin/branch.c:663
+#: builtin/branch.c:707
 msgid "Branch rename failed"
 msgstr ""
 
-#: builtin/branch.c:667
+#: builtin/branch.c:711
 #, c-format
 msgid "Renamed a misnamed branch '%s' away"
 msgstr ""
 
-#: builtin/branch.c:671
+#: builtin/branch.c:715
 #, c-format
 msgid "Branch renamed to %s, but HEAD is not updated!"
 msgstr ""
 
-#: builtin/branch.c:678
+#: builtin/branch.c:722
 msgid "Branch is renamed, but update of config-file failed"
 msgstr ""
 
-#: builtin/branch.c:693
+#: builtin/branch.c:737
 #, c-format
 msgid "malformed object name %s"
 msgstr ""
 
-#: builtin/branch.c:717
+#: builtin/branch.c:761
 #, c-format
 msgid "could not write branch description template: %s"
 msgstr ""
 
-#: builtin/branch.c:747
+#: builtin/branch.c:791
 msgid "Generic options"
 msgstr ""
 
-#: builtin/branch.c:749
+#: builtin/branch.c:793
 msgid "show hash and subject, give twice for upstream branch"
 msgstr ""
 
-#: builtin/branch.c:750
+#: builtin/branch.c:794
 msgid "suppress informational messages"
 msgstr ""
 
-#: builtin/branch.c:751
+#: builtin/branch.c:795
 msgid "set up tracking mode (see git-pull(1))"
 msgstr ""
 
-#: builtin/branch.c:753
+#: builtin/branch.c:797
 msgid "change upstream info"
 msgstr ""
 
-#: builtin/branch.c:757
+#: builtin/branch.c:801
 msgid "use colored output"
 msgstr ""
 
-#: builtin/branch.c:758
+#: builtin/branch.c:802
 msgid "act on remote-tracking branches"
 msgstr ""
 
-#: builtin/branch.c:761 builtin/branch.c:767 builtin/branch.c:788
-#: builtin/branch.c:794 builtin/commit.c:1366 builtin/commit.c:1367
-#: builtin/commit.c:1368 builtin/commit.c:1369 builtin/tag.c:468
+#: builtin/branch.c:805 builtin/branch.c:811 builtin/branch.c:832
+#: builtin/branch.c:838 builtin/commit.c:1433 builtin/commit.c:1434
+#: builtin/commit.c:1435 builtin/commit.c:1436 builtin/tag.c:468
 msgid "commit"
 msgstr ""
 
-#: builtin/branch.c:762 builtin/branch.c:768
+#: builtin/branch.c:806 builtin/branch.c:812
 msgid "print only branches that contain the commit"
 msgstr ""
 
-#: builtin/branch.c:774
+#: builtin/branch.c:818
 msgid "Specific git-branch actions:"
 msgstr ""
 
-#: builtin/branch.c:775
+#: builtin/branch.c:819
 msgid "list both remote-tracking and local branches"
 msgstr ""
 
-#: builtin/branch.c:777
+#: builtin/branch.c:821
 msgid "delete fully merged branch"
 msgstr ""
 
-#: builtin/branch.c:778
+#: builtin/branch.c:822
 msgid "delete branch (even if not merged)"
 msgstr ""
 
-#: builtin/branch.c:779
+#: builtin/branch.c:823
 msgid "move/rename a branch and its reflog"
 msgstr ""
 
-#: builtin/branch.c:780
+#: builtin/branch.c:824
 msgid "move/rename a branch, even if target exists"
 msgstr ""
 
-#: builtin/branch.c:781
+#: builtin/branch.c:825
 msgid "list branch names"
 msgstr ""
 
-#: builtin/branch.c:782
+#: builtin/branch.c:826
 msgid "create the branch's reflog"
 msgstr ""
 
-#: builtin/branch.c:784
+#: builtin/branch.c:828
 msgid "edit the description for the branch"
 msgstr ""
 
-#: builtin/branch.c:785
+#: builtin/branch.c:829
 msgid "force creation (when already exists)"
 msgstr ""
 
-#: builtin/branch.c:788
+#: builtin/branch.c:832
 msgid "print only not merged branches"
 msgstr ""
 
-#: builtin/branch.c:794
+#: builtin/branch.c:838
 msgid "print only merged branches"
 msgstr ""
 
-#: builtin/branch.c:798
+#: builtin/branch.c:842
 msgid "list branches in columns"
 msgstr ""
 
-#: builtin/branch.c:811
+#: builtin/branch.c:855
 msgid "Failed to resolve HEAD as a valid ref."
 msgstr ""
 
-#: builtin/branch.c:816 builtin/clone.c:561
+#: builtin/branch.c:860 builtin/clone.c:630
 msgid "HEAD not found below refs/heads!"
 msgstr ""
 
-#: builtin/branch.c:839
+#: builtin/branch.c:883
 msgid "--column and --verbose are incompatible"
 msgstr ""
 
-#: builtin/branch.c:845
+#: builtin/branch.c:889 builtin/branch.c:928
 msgid "branch name required"
 msgstr ""
 
-#: builtin/branch.c:860
+#: builtin/branch.c:904
 msgid "Cannot give description to detached HEAD"
 msgstr ""
 
-#: builtin/branch.c:865
+#: builtin/branch.c:909
 msgid "cannot edit description of more than one branch"
 msgstr ""
 
-#: builtin/branch.c:872
+#: builtin/branch.c:916
 #, c-format
 msgid "No commit on branch '%s' yet."
 msgstr ""
 
-#: builtin/branch.c:875
+#: builtin/branch.c:919
 #, c-format
 msgid "No branch named '%s'."
 msgstr ""
 
-#: builtin/branch.c:888
+#: builtin/branch.c:934
 msgid "too many branches for a rename operation"
 msgstr ""
 
-#: builtin/branch.c:893
+#: builtin/branch.c:939
+msgid "too many branches to set new upstream"
+msgstr ""
+
+#: builtin/branch.c:943
+#, c-format
+msgid ""
+"could not set upstream of HEAD to %s when it does not point to any branch."
+msgstr ""
+
+#: builtin/branch.c:946 builtin/branch.c:968 builtin/branch.c:990
+#, c-format
+msgid "no such branch '%s'"
+msgstr ""
+
+#: builtin/branch.c:950
 #, c-format
 msgid "branch '%s' does not exist"
 msgstr ""
 
-#: builtin/branch.c:905
+#: builtin/branch.c:962
+msgid "too many branches to unset upstream"
+msgstr ""
+
+#: builtin/branch.c:966
+msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgstr ""
+
+#: builtin/branch.c:972
 #, c-format
 msgid "Branch '%s' has no upstream information"
 msgstr ""
 
-#: builtin/branch.c:920
+#: builtin/branch.c:987
+msgid "it does not make sense to create 'HEAD' manually"
+msgstr ""
+
+#: builtin/branch.c:993
 msgid "-a and -r options to 'git branch' do not make sense with a branch name"
 msgstr ""
 
-#: builtin/branch.c:923
+#: builtin/branch.c:996
 #, c-format
 msgid ""
 "The --set-upstream flag is deprecated and will be removed. Consider using --"
 "track or --set-upstream-to\n"
 msgstr ""
 
-#: builtin/branch.c:940
+#: builtin/branch.c:1013
 #, c-format
 msgid ""
 "\n"
@@ -2505,12 +2790,12 @@ msgid ""
 "\n"
 msgstr ""
 
-#: builtin/branch.c:941
+#: builtin/branch.c:1014
 #, c-format
 msgid "    git branch -d %s\n"
 msgstr ""
 
-#: builtin/branch.c:942
+#: builtin/branch.c:1015
 #, c-format
 msgid "    git branch --set-upstream-to %s\n"
 msgstr ""
@@ -2528,43 +2813,43 @@ msgstr ""
 msgid "Need a repository to unbundle."
 msgstr ""
 
-#: builtin/cat-file.c:247
+#: builtin/cat-file.c:285
 msgid "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"
 msgstr ""
 
-#: builtin/cat-file.c:248
+#: builtin/cat-file.c:286
 msgid "git cat-file (--batch|--batch-check) < <list_of_objects>"
 msgstr ""
 
-#: builtin/cat-file.c:266
+#: builtin/cat-file.c:323
 msgid "<type> can be one of: blob, tree, commit, tag"
 msgstr ""
 
-#: builtin/cat-file.c:267
+#: builtin/cat-file.c:324
 msgid "show object type"
 msgstr ""
 
-#: builtin/cat-file.c:268
+#: builtin/cat-file.c:325
 msgid "show object size"
 msgstr ""
 
-#: builtin/cat-file.c:270
+#: builtin/cat-file.c:327
 msgid "exit with zero when there's no error"
 msgstr ""
 
-#: builtin/cat-file.c:271
+#: builtin/cat-file.c:328
 msgid "pretty-print object's content"
 msgstr ""
 
-#: builtin/cat-file.c:273
+#: builtin/cat-file.c:330
 msgid "for blob objects, run textconv on object's content"
 msgstr ""
 
-#: builtin/cat-file.c:275
+#: builtin/cat-file.c:332
 msgid "show info and content of objects fed from the standard input"
 msgstr ""
 
-#: builtin/cat-file.c:278
+#: builtin/cat-file.c:335
 msgid "show info about objects fed from the standard input"
 msgstr ""
 
@@ -2592,30 +2877,55 @@ msgstr ""
 msgid "input paths are terminated by a null character"
 msgstr ""
 
-#: builtin/check-ignore.c:18 builtin/checkout.c:1012 builtin/gc.c:177
+#: builtin/check-ignore.c:18 builtin/checkout.c:1054 builtin/gc.c:177
 msgid "suppress progress reporting"
 msgstr ""
 
-#: builtin/check-ignore.c:151
+#: builtin/check-ignore.c:26
+msgid "show non-matching input paths"
+msgstr ""
+
+#: builtin/check-ignore.c:143
 msgid "cannot specify pathnames with --stdin"
 msgstr ""
 
-#: builtin/check-ignore.c:154
+#: builtin/check-ignore.c:146
 msgid "-z only makes sense with --stdin"
 msgstr ""
 
-#: builtin/check-ignore.c:156
+#: builtin/check-ignore.c:148
 msgid "no path specified"
 msgstr ""
 
-#: builtin/check-ignore.c:160
+#: builtin/check-ignore.c:152
 msgid "--quiet is only valid with a single pathname"
 msgstr ""
 
-#: builtin/check-ignore.c:162
+#: builtin/check-ignore.c:154
 msgid "cannot have both --quiet and --verbose"
 msgstr ""
 
+#: builtin/check-ignore.c:157
+msgid "--non-matching is only valid with --verbose"
+msgstr ""
+
+#: builtin/check-mailmap.c:8
+msgid "git check-mailmap [options] <contact>..."
+msgstr ""
+
+#: builtin/check-mailmap.c:13
+msgid "also read contacts from stdin"
+msgstr ""
+
+#: builtin/check-mailmap.c:24
+#, c-format
+msgid "unable to parse contact: %s"
+msgstr ""
+
+#: builtin/check-mailmap.c:47
+msgid "no contacts specified"
+msgstr ""
+
 #: builtin/checkout-index.c:126
 msgid "git checkout-index [options] [--] [<file>...]"
 msgstr ""
@@ -2668,106 +2978,106 @@ msgstr ""
 msgid "git checkout [options] [<branch>] -- <file>..."
 msgstr ""
 
-#: builtin/checkout.c:116 builtin/checkout.c:149
+#: builtin/checkout.c:117 builtin/checkout.c:150
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr ""
 
-#: builtin/checkout.c:118 builtin/checkout.c:151
+#: builtin/checkout.c:119 builtin/checkout.c:152
 #, c-format
 msgid "path '%s' does not have their version"
 msgstr ""
 
-#: builtin/checkout.c:134
+#: builtin/checkout.c:135
 #, c-format
 msgid "path '%s' does not have all necessary versions"
 msgstr ""
 
-#: builtin/checkout.c:178
+#: builtin/checkout.c:179
 #, c-format
 msgid "path '%s' does not have necessary versions"
 msgstr ""
 
-#: builtin/checkout.c:195
+#: builtin/checkout.c:196
 #, c-format
 msgid "path '%s': cannot merge"
 msgstr ""
 
-#: builtin/checkout.c:212
+#: builtin/checkout.c:213
 #, c-format
 msgid "Unable to add merge result for '%s'"
 msgstr ""
 
-#: builtin/checkout.c:236 builtin/checkout.c:239 builtin/checkout.c:242
-#: builtin/checkout.c:245
+#: builtin/checkout.c:237 builtin/checkout.c:240 builtin/checkout.c:243
+#: builtin/checkout.c:246
 #, c-format
 msgid "'%s' cannot be used with updating paths"
 msgstr ""
 
-#: builtin/checkout.c:248 builtin/checkout.c:251
+#: builtin/checkout.c:249 builtin/checkout.c:252
 #, c-format
 msgid "'%s' cannot be used with %s"
 msgstr ""
 
-#: builtin/checkout.c:254
+#: builtin/checkout.c:255
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
 msgstr ""
 
-#: builtin/checkout.c:265 builtin/checkout.c:426
+#: builtin/checkout.c:266 builtin/checkout.c:458
 msgid "corrupt index file"
 msgstr ""
 
-#: builtin/checkout.c:295 builtin/checkout.c:302
+#: builtin/checkout.c:329 builtin/checkout.c:336
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr ""
 
-#: builtin/checkout.c:448
+#: builtin/checkout.c:480
 msgid "you need to resolve your current index first"
 msgstr ""
 
-#: builtin/checkout.c:569
+#: builtin/checkout.c:601
 #, c-format
 msgid "Can not do reflog for '%s'\n"
 msgstr ""
 
-#: builtin/checkout.c:602
+#: builtin/checkout.c:639
 msgid "HEAD is now at"
 msgstr ""
 
-#: builtin/checkout.c:609
+#: builtin/checkout.c:646
 #, c-format
 msgid "Reset branch '%s'\n"
 msgstr ""
 
-#: builtin/checkout.c:612
+#: builtin/checkout.c:649
 #, c-format
 msgid "Already on '%s'\n"
 msgstr ""
 
-#: builtin/checkout.c:616
+#: builtin/checkout.c:653
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
 msgstr ""
 
-#: builtin/checkout.c:618 builtin/checkout.c:955
+#: builtin/checkout.c:655 builtin/checkout.c:997
 #, c-format
 msgid "Switched to a new branch '%s'\n"
 msgstr ""
 
-#: builtin/checkout.c:620
+#: builtin/checkout.c:657
 #, c-format
 msgid "Switched to branch '%s'\n"
 msgstr ""
 
-#: builtin/checkout.c:676
+#: builtin/checkout.c:713
 #, c-format
 msgid " ... and %d more.\n"
 msgstr ""
 
 #. The singular version
-#: builtin/checkout.c:682
+#: builtin/checkout.c:719
 #, c-format
 msgid ""
 "Warning: you are leaving %d commit behind, not connected to\n"
@@ -2782,7 +3092,7 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/checkout.c:700
+#: builtin/checkout.c:737
 #, c-format
 msgid ""
 "If you want to keep them by creating a new branch, this may be a good time\n"
@@ -2792,228 +3102,315 @@ msgid ""
 "\n"
 msgstr ""
 
-#: builtin/checkout.c:730
+#: builtin/checkout.c:767
 msgid "internal error in revision walk"
 msgstr ""
 
-#: builtin/checkout.c:734
+#: builtin/checkout.c:771
 msgid "Previous HEAD position was"
 msgstr ""
 
-#: builtin/checkout.c:761 builtin/checkout.c:950
+#: builtin/checkout.c:798 builtin/checkout.c:992
 msgid "You are on a branch yet to be born"
 msgstr ""
 
 #. case (1)
-#: builtin/checkout.c:886
+#: builtin/checkout.c:928
 #, c-format
 msgid "invalid reference: %s"
 msgstr ""
 
 #. case (1): want a tree
-#: builtin/checkout.c:925
+#: builtin/checkout.c:967
 #, c-format
 msgid "reference is not a tree: %s"
 msgstr ""
 
-#: builtin/checkout.c:964
+#: builtin/checkout.c:1006
 msgid "paths cannot be used with switching branches"
 msgstr ""
 
-#: builtin/checkout.c:967 builtin/checkout.c:971
+#: builtin/checkout.c:1009 builtin/checkout.c:1013
 #, c-format
 msgid "'%s' cannot be used with switching branches"
 msgstr ""
 
-#: builtin/checkout.c:975 builtin/checkout.c:978 builtin/checkout.c:983
-#: builtin/checkout.c:986
+#: builtin/checkout.c:1017 builtin/checkout.c:1020 builtin/checkout.c:1025
+#: builtin/checkout.c:1028
 #, c-format
 msgid "'%s' cannot be used with '%s'"
 msgstr ""
 
-#: builtin/checkout.c:991
+#: builtin/checkout.c:1033
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
 msgstr ""
 
-#: builtin/checkout.c:1013 builtin/checkout.c:1015 builtin/clone.c:89
+#: builtin/checkout.c:1055 builtin/checkout.c:1057 builtin/clone.c:89
 #: builtin/remote.c:169 builtin/remote.c:171
 msgid "branch"
 msgstr ""
 
-#: builtin/checkout.c:1014
+#: builtin/checkout.c:1056
 msgid "create and checkout a new branch"
 msgstr ""
 
-#: builtin/checkout.c:1016
+#: builtin/checkout.c:1058
 msgid "create/reset and checkout a branch"
 msgstr ""
 
-#: builtin/checkout.c:1017
+#: builtin/checkout.c:1059
 msgid "create reflog for new branch"
 msgstr ""
 
-#: builtin/checkout.c:1018
+#: builtin/checkout.c:1060
 msgid "detach the HEAD at named commit"
 msgstr ""
 
-#: builtin/checkout.c:1019
+#: builtin/checkout.c:1061
 msgid "set upstream info for new branch"
 msgstr ""
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new branch"
 msgstr ""
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new unparented branch"
 msgstr ""
 
-#: builtin/checkout.c:1022
+#: builtin/checkout.c:1064
 msgid "checkout our version for unmerged files"
 msgstr ""
 
-#: builtin/checkout.c:1024
+#: builtin/checkout.c:1066
 msgid "checkout their version for unmerged files"
 msgstr ""
 
-#: builtin/checkout.c:1026
+#: builtin/checkout.c:1068
 msgid "force checkout (throw away local modifications)"
 msgstr ""
 
-#: builtin/checkout.c:1027
+#: builtin/checkout.c:1069
 msgid "perform a 3-way merge with the new branch"
 msgstr ""
 
-#: builtin/checkout.c:1028 builtin/merge.c:215
+#: builtin/checkout.c:1070 builtin/merge.c:232
 msgid "update ignored files (default)"
 msgstr ""
 
-#: builtin/checkout.c:1029 builtin/log.c:1147 parse-options.h:245
+#: builtin/checkout.c:1071 builtin/log.c:1208 parse-options.h:249
 msgid "style"
 msgstr ""
 
-#: builtin/checkout.c:1030
+#: builtin/checkout.c:1072
 msgid "conflict style (merge or diff3)"
 msgstr ""
 
-#: builtin/checkout.c:1033
+#: builtin/checkout.c:1075
+msgid "do not limit pathspecs to sparse entries only"
+msgstr ""
+
+#: builtin/checkout.c:1077
 msgid "second guess 'git checkout no-such-branch'"
 msgstr ""
 
-#: builtin/checkout.c:1057
+#: builtin/checkout.c:1101
 msgid "-b, -B and --orphan are mutually exclusive"
 msgstr ""
 
-#: builtin/checkout.c:1074
+#: builtin/checkout.c:1118
 msgid "--track needs a branch name"
 msgstr ""
 
-#: builtin/checkout.c:1081
+#: builtin/checkout.c:1125
 msgid "Missing branch name; try -b"
 msgstr ""
 
-#: builtin/checkout.c:1116
+#: builtin/checkout.c:1160
 msgid "invalid path specification"
 msgstr ""
 
-#: builtin/checkout.c:1123
+#: builtin/checkout.c:1167
 #, 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:1128
+#: builtin/checkout.c:1172
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
 msgstr ""
 
-#: builtin/checkout.c:1132
+#: builtin/checkout.c:1176
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
 msgstr ""
 
-#: builtin/clean.c:20
-msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+#: builtin/clean.c:25
+msgid ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
 msgstr ""
 
-#: builtin/clean.c:24
+#: builtin/clean.c:29
 #, c-format
 msgid "Removing %s\n"
 msgstr ""
 
-#: builtin/clean.c:25
+#: builtin/clean.c:30
 #, c-format
 msgid "Would remove %s\n"
 msgstr ""
 
-#: builtin/clean.c:26
+#: builtin/clean.c:31
 #, c-format
 msgid "Skipping repository %s\n"
 msgstr ""
 
-#: builtin/clean.c:27
+#: builtin/clean.c:32
 #, c-format
 msgid "Would skip repository %s\n"
 msgstr ""
 
-#: builtin/clean.c:28
+#: builtin/clean.c:33
 #, c-format
 msgid "failed to remove %s"
 msgstr ""
 
-#: builtin/clean.c:160
+#: builtin/clean.c:293
+msgid ""
+"Prompt help:\n"
+"1          - select a numbered item\n"
+"foo        - select item based on unique prefix\n"
+"           - (empty) select nothing"
+msgstr ""
+
+#: builtin/clean.c:297
+msgid ""
+"Prompt help:\n"
+"1          - select a single item\n"
+"3-5        - select a range of items\n"
+"2-3,6-9    - select multiple ranges\n"
+"foo        - select item based on unique prefix\n"
+"-...       - unselect specified items\n"
+"*          - choose all items\n"
+"           - (empty) finish selecting"
+msgstr ""
+
+#: builtin/clean.c:515
+#, c-format
+msgid "Huh (%s)?"
+msgstr ""
+
+#: builtin/clean.c:658
+#, c-format
+msgid "Input ignore patterns>> "
+msgstr ""
+
+#: builtin/clean.c:695
+#, c-format
+msgid "WARNING: Cannot find items matched by: %s"
+msgstr ""
+
+#: builtin/clean.c:716
+msgid "Select items to delete"
+msgstr ""
+
+#: builtin/clean.c:756
+#, c-format
+msgid "remove %s? "
+msgstr ""
+
+#: builtin/clean.c:781
+msgid "Bye."
+msgstr ""
+
+#: builtin/clean.c:789
+msgid ""
+"clean               - start cleaning\n"
+"filter by pattern   - exclude items from deletion\n"
+"select by numbers   - select items to be deleted by numbers\n"
+"ask each            - confirm each deletion (like \"rm -i\")\n"
+"quit                - stop cleaning\n"
+"help                - this screen\n"
+"?                   - help for prompt selection"
+msgstr ""
+
+#: builtin/clean.c:816
+msgid "*** Commands ***"
+msgstr ""
+
+#: builtin/clean.c:817
+msgid "What now"
+msgstr ""
+
+#: builtin/clean.c:825
+msgid "Would remove the following item:"
+msgid_plural "Would remove the following items:"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/clean.c:842
+msgid "No more files to clean, exiting."
+msgstr ""
+
+#: builtin/clean.c:874
 msgid "do not print names of files removed"
 msgstr ""
 
-#: builtin/clean.c:162
+#: builtin/clean.c:876
 msgid "force"
 msgstr ""
 
-#: builtin/clean.c:164
+#: builtin/clean.c:877
+msgid "interactive cleaning"
+msgstr ""
+
+#: builtin/clean.c:879
 msgid "remove whole directories"
 msgstr ""
 
-#: builtin/clean.c:165 builtin/describe.c:413 builtin/grep.c:717
-#: builtin/ls-files.c:494 builtin/name-rev.c:231 builtin/show-ref.c:182
+#: builtin/clean.c:880 builtin/describe.c:420 builtin/grep.c:716
+#: builtin/ls-files.c:493 builtin/name-rev.c:315 builtin/show-ref.c:186
 msgid "pattern"
 msgstr ""
 
-#: builtin/clean.c:166
+#: builtin/clean.c:881
 msgid "add <pattern> to ignore rules"
 msgstr ""
 
-#: builtin/clean.c:167
+#: builtin/clean.c:882
 msgid "remove ignored files, too"
 msgstr ""
 
-#: builtin/clean.c:169
+#: builtin/clean.c:884
 msgid "remove only ignored files"
 msgstr ""
 
-#: builtin/clean.c:187
+#: builtin/clean.c:902
 msgid "-x and -X cannot be used together"
 msgstr ""
 
-#: builtin/clean.c:191
+#: builtin/clean.c:906
 msgid ""
-"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+"clean.requireForce set to true and neither -i, -n nor -f given; refusing to "
+"clean"
 msgstr ""
 
-#: builtin/clean.c:194
+#: builtin/clean.c:909
 msgid ""
-"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
-"clean"
+"clean.requireForce defaults to true and neither -i, -n nor -f given; "
+"refusing to clean"
 msgstr ""
 
 #: builtin/clone.c:36
 msgid "git clone [options] [--] <repo> [<dir>]"
 msgstr ""
 
-#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:212
-#: builtin/push.c:436
+#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:229
+#: builtin/push.c:462
 msgid "force progress reporting"
 msgstr ""
 
@@ -3073,7 +3470,7 @@ msgstr ""
 msgid "path to git-upload-pack on the remote"
 msgstr ""
 
-#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:662
+#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:661
 msgid "depth"
 msgstr ""
 
@@ -3101,122 +3498,151 @@ msgstr ""
 msgid "set config inside the new repository"
 msgstr ""
 
-#: builtin/clone.c:243
+#: builtin/clone.c:253
 #, c-format
-msgid "reference repository '%s' is not a local directory."
+msgid "reference repository '%s' is not a local repository."
 msgstr ""
 
-#: builtin/clone.c:306
+#: builtin/clone.c:316
 #, c-format
 msgid "failed to create directory '%s'"
 msgstr ""
 
-#: builtin/clone.c:308 builtin/diff.c:77
+#: builtin/clone.c:318 builtin/diff.c:77
 #, c-format
 msgid "failed to stat '%s'"
 msgstr ""
 
-#: builtin/clone.c:310
+#: builtin/clone.c:320
 #, c-format
 msgid "%s exists and is not a directory"
 msgstr ""
 
-#: builtin/clone.c:324
+#: builtin/clone.c:334
 #, c-format
 msgid "failed to stat %s\n"
 msgstr ""
 
-#: builtin/clone.c:346
+#: builtin/clone.c:356
 #, c-format
 msgid "failed to create link '%s'"
 msgstr ""
 
-#: builtin/clone.c:350
+#: builtin/clone.c:360
 #, c-format
 msgid "failed to copy file to '%s'"
 msgstr ""
 
-#: builtin/clone.c:373
+#: builtin/clone.c:383
 #, c-format
 msgid "done.\n"
 msgstr ""
 
-#: builtin/clone.c:443
+#: builtin/clone.c:396
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry the checkout with 'git checkout -f HEAD'\n"
+msgstr ""
+
+#: builtin/clone.c:475
 #, c-format
 msgid "Could not find remote branch %s to clone."
 msgstr ""
 
-#: builtin/clone.c:552
+#: builtin/clone.c:555
+#, c-format
+msgid "Checking connectivity... "
+msgstr ""
+
+#: builtin/clone.c:558
+msgid "remote did not send all necessary objects"
+msgstr ""
+
+#: builtin/clone.c:560
+#, c-format
+msgid "done\n"
+msgstr ""
+
+#: builtin/clone.c:621
 msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
 msgstr ""
 
-#: builtin/clone.c:690
+#: builtin/clone.c:652
+msgid "unable to checkout working tree"
+msgstr ""
+
+#: builtin/clone.c:760
 msgid "Too many arguments."
 msgstr ""
 
-#: builtin/clone.c:694
+#: builtin/clone.c:764
 msgid "You must specify a repository to clone."
 msgstr ""
 
-#: builtin/clone.c:705
+#: builtin/clone.c:775
 #, c-format
 msgid "--bare and --origin %s options are incompatible."
 msgstr ""
 
-#: builtin/clone.c:708
+#: builtin/clone.c:778
 msgid "--bare and --separate-git-dir are incompatible."
 msgstr ""
 
-#: builtin/clone.c:721
+#: builtin/clone.c:791
 #, c-format
 msgid "repository '%s' does not exist"
 msgstr ""
 
-#: builtin/clone.c:726
+#: builtin/clone.c:796
 msgid "--depth is ignored in local clones; use file:// instead."
 msgstr ""
 
-#: builtin/clone.c:736
+#: builtin/clone.c:798
+msgid "--local is ignored"
+msgstr ""
+
+#: builtin/clone.c:808
 #, c-format
 msgid "destination path '%s' already exists and is not an empty directory."
 msgstr ""
 
-#: builtin/clone.c:746
+#: builtin/clone.c:818
 #, c-format
 msgid "working tree '%s' already exists."
 msgstr ""
 
-#: builtin/clone.c:759 builtin/clone.c:771
+#: builtin/clone.c:831 builtin/clone.c:843
 #, c-format
 msgid "could not create leading directories of '%s'"
 msgstr ""
 
-#: builtin/clone.c:762
+#: builtin/clone.c:834
 #, c-format
 msgid "could not create work tree dir '%s'."
 msgstr ""
 
-#: builtin/clone.c:781
+#: builtin/clone.c:853
 #, c-format
 msgid "Cloning into bare repository '%s'...\n"
 msgstr ""
 
-#: builtin/clone.c:783
+#: builtin/clone.c:855
 #, c-format
 msgid "Cloning into '%s'...\n"
 msgstr ""
 
-#: builtin/clone.c:818
+#: builtin/clone.c:890
 #, c-format
 msgid "Don't know how to clone %s"
 msgstr ""
 
-#: builtin/clone.c:867
+#: builtin/clone.c:942
 #, c-format
 msgid "Remote branch %s not found in upstream %s"
 msgstr ""
 
-#: builtin/clone.c:874
+#: builtin/clone.c:949
 msgid "You appear to have cloned an empty repository."
 msgstr ""
 
@@ -3252,15 +3678,15 @@ msgstr ""
 msgid "--command must be the first argument"
 msgstr ""
 
-#: builtin/commit.c:34
+#: builtin/commit.c:35
 msgid "git commit [options] [--] <pathspec>..."
 msgstr ""
 
-#: builtin/commit.c:39
+#: builtin/commit.c:40
 msgid "git status [options] [--] <pathspec>..."
 msgstr ""
 
-#: builtin/commit.c:44
+#: builtin/commit.c:45
 msgid ""
 "Your name and email address were configured automatically based\n"
 "on your username and hostname. Please check that they are accurate.\n"
@@ -3274,110 +3700,123 @@ msgid ""
 "    git commit --amend --reset-author\n"
 msgstr ""
 
-#: builtin/commit.c:56
+#: builtin/commit.c:57
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
 "remove the commit entirely with \"git reset HEAD^\".\n"
 msgstr ""
 
-#: builtin/commit.c:61
+#: builtin/commit.c:62
 msgid ""
 "The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
 "If you wish to commit it anyway, use:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"Otherwise, please use 'git reset'\n"
 msgstr ""
 
-#: builtin/commit.c:258
+#: builtin/commit.c:69
+msgid "Otherwise, please use 'git reset'\n"
+msgstr ""
+
+#: builtin/commit.c:72
+msgid ""
+"If you wish to skip this commit, use:\n"
+"\n"
+"    git reset\n"
+"\n"
+"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
+"the remaining commits.\n"
+msgstr ""
+
+#: builtin/commit.c:279
 msgid "failed to unpack HEAD tree object"
 msgstr ""
 
-#: builtin/commit.c:300
+#: builtin/commit.c:321
 msgid "unable to create temporary index"
 msgstr ""
 
-#: builtin/commit.c:306
+#: builtin/commit.c:327
 msgid "interactive add failed"
 msgstr ""
 
-#: builtin/commit.c:339 builtin/commit.c:360 builtin/commit.c:410
+#: builtin/commit.c:360 builtin/commit.c:381 builtin/commit.c:431
 msgid "unable to write new_index file"
 msgstr ""
 
-#: builtin/commit.c:391
+#: builtin/commit.c:412
 msgid "cannot do a partial commit during a merge."
 msgstr ""
 
-#: builtin/commit.c:393
+#: builtin/commit.c:414
 msgid "cannot do a partial commit during a cherry-pick."
 msgstr ""
 
-#: builtin/commit.c:403
+#: builtin/commit.c:424
 msgid "cannot read the index"
 msgstr ""
 
-#: builtin/commit.c:423
+#: builtin/commit.c:444
 msgid "unable to write temporary index file"
 msgstr ""
 
-#: builtin/commit.c:511 builtin/commit.c:517
+#: builtin/commit.c:535 builtin/commit.c:541
 #, c-format
 msgid "invalid commit: %s"
 msgstr ""
 
-#: builtin/commit.c:540
+#: builtin/commit.c:563
 msgid "malformed --author parameter"
 msgstr ""
 
-#: builtin/commit.c:560
+#: builtin/commit.c:583
 #, c-format
 msgid "Malformed ident string: '%s'"
 msgstr ""
 
-#: builtin/commit.c:598 builtin/commit.c:631 builtin/commit.c:954
+#: builtin/commit.c:621 builtin/commit.c:654 builtin/commit.c:982
 #, c-format
 msgid "could not lookup commit %s"
 msgstr ""
 
-#: builtin/commit.c:610 builtin/shortlog.c:272
+#: builtin/commit.c:633 builtin/shortlog.c:271
 #, c-format
 msgid "(reading log message from standard input)\n"
 msgstr ""
 
-#: builtin/commit.c:612
+#: builtin/commit.c:635
 msgid "could not read log from standard input"
 msgstr ""
 
-#: builtin/commit.c:616
+#: builtin/commit.c:639
 #, c-format
 msgid "could not read log file '%s'"
 msgstr ""
 
-#: builtin/commit.c:622
+#: builtin/commit.c:645
 msgid "commit has empty message"
 msgstr ""
 
-#: builtin/commit.c:638
+#: builtin/commit.c:661
 msgid "could not read MERGE_MSG"
 msgstr ""
 
-#: builtin/commit.c:642
+#: builtin/commit.c:665
 msgid "could not read SQUASH_MSG"
 msgstr ""
 
-#: builtin/commit.c:646
+#: builtin/commit.c:669
 #, c-format
 msgid "could not read '%s'"
 msgstr ""
 
-#: builtin/commit.c:707
+#: builtin/commit.c:730
 msgid "could not write commit template"
 msgstr ""
 
-#: builtin/commit.c:718
+#: builtin/commit.c:741
 #, c-format
 msgid ""
 "\n"
@@ -3387,7 +3826,7 @@ msgid ""
 "and try again.\n"
 msgstr ""
 
-#: builtin/commit.c:723
+#: builtin/commit.c:746
 #, c-format
 msgid ""
 "\n"
@@ -3397,14 +3836,14 @@ msgid ""
 "and try again.\n"
 msgstr ""
 
-#: builtin/commit.c:735
+#: builtin/commit.c:758
 #, 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:740
+#: builtin/commit.c:763
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
@@ -3412,351 +3851,351 @@ msgid ""
 "An empty message aborts the commit.\n"
 msgstr ""
 
-#: builtin/commit.c:753
+#: builtin/commit.c:776
 #, c-format
 msgid "%sAuthor:    %s"
 msgstr ""
 
-#: builtin/commit.c:760
+#: builtin/commit.c:783
 #, c-format
 msgid "%sCommitter: %s"
 msgstr ""
 
-#: builtin/commit.c:780
+#: builtin/commit.c:803
 msgid "Cannot read index"
 msgstr ""
 
-#: builtin/commit.c:817
+#: builtin/commit.c:845
 msgid "Error building trees"
 msgstr ""
 
-#: builtin/commit.c:832 builtin/tag.c:359
+#: builtin/commit.c:860 builtin/tag.c:359
 #, c-format
 msgid "Please supply the message using either -m or -F option.\n"
 msgstr ""
 
-#: builtin/commit.c:929
+#: builtin/commit.c:957
 #, c-format
 msgid "No existing author found with '%s'"
 msgstr ""
 
-#: builtin/commit.c:944 builtin/commit.c:1138
+#: builtin/commit.c:972 builtin/commit.c:1208
 #, c-format
 msgid "Invalid untracked files mode '%s'"
 msgstr ""
 
-#: builtin/commit.c:974
+#: builtin/commit.c:1009
+msgid "--long and -z are incompatible"
+msgstr ""
+
+#: builtin/commit.c:1039
 msgid "Using both --reset-author and --author does not make sense"
 msgstr ""
 
-#: builtin/commit.c:985
+#: builtin/commit.c:1050
 msgid "You have nothing to amend."
 msgstr ""
 
-#: builtin/commit.c:988
+#: builtin/commit.c:1053
 msgid "You are in the middle of a merge -- cannot amend."
 msgstr ""
 
-#: builtin/commit.c:990
+#: builtin/commit.c:1055
 msgid "You are in the middle of a cherry-pick -- cannot amend."
 msgstr ""
 
-#: builtin/commit.c:993
+#: builtin/commit.c:1058
 msgid "Options --squash and --fixup cannot be used together"
 msgstr ""
 
-#: builtin/commit.c:1003
+#: builtin/commit.c:1068
 msgid "Only one of -c/-C/-F/--fixup can be used."
 msgstr ""
 
-#: builtin/commit.c:1005
+#: builtin/commit.c:1070
 msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
 msgstr ""
 
-#: builtin/commit.c:1013
+#: builtin/commit.c:1078
 msgid "--reset-author can be used only with -C, -c or --amend."
 msgstr ""
 
-#: builtin/commit.c:1030
+#: builtin/commit.c:1095
 msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
 msgstr ""
 
-#: builtin/commit.c:1032
+#: builtin/commit.c:1097
 msgid "No paths with --include/--only does not make sense."
 msgstr ""
 
-#: builtin/commit.c:1034
+#: builtin/commit.c:1099
 msgid "Clever... amending the last one with dirty index."
 msgstr ""
 
-#: builtin/commit.c:1036
+#: builtin/commit.c:1101
 msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
 msgstr ""
 
-#: builtin/commit.c:1046 builtin/tag.c:575
+#: builtin/commit.c:1111 builtin/tag.c:575
 #, c-format
 msgid "Invalid cleanup mode %s"
 msgstr ""
 
-#: builtin/commit.c:1051
+#: builtin/commit.c:1116
 msgid "Paths with -a does not make sense."
 msgstr ""
 
-#: builtin/commit.c:1057 builtin/commit.c:1192
-msgid "--long and -z are incompatible"
-msgstr ""
-
-#: builtin/commit.c:1152 builtin/commit.c:1388
+#: builtin/commit.c:1222 builtin/commit.c:1455
 msgid "show status concisely"
 msgstr ""
 
-#: builtin/commit.c:1154 builtin/commit.c:1390
+#: builtin/commit.c:1224 builtin/commit.c:1457
 msgid "show branch information"
 msgstr ""
 
-#: builtin/commit.c:1156 builtin/commit.c:1392 builtin/push.c:426
+#: builtin/commit.c:1226 builtin/commit.c:1459 builtin/push.c:452
 msgid "machine-readable output"
 msgstr ""
 
-#: builtin/commit.c:1159 builtin/commit.c:1394
+#: builtin/commit.c:1229 builtin/commit.c:1461
 msgid "show status in long format (default)"
 msgstr ""
 
-#: builtin/commit.c:1162 builtin/commit.c:1397
+#: builtin/commit.c:1232 builtin/commit.c:1464
 msgid "terminate entries with NUL"
 msgstr ""
 
-#: builtin/commit.c:1164 builtin/commit.c:1400 builtin/fast-export.c:647
-#: builtin/fast-export.c:650 builtin/tag.c:459
+#: builtin/commit.c:1234 builtin/commit.c:1467 builtin/fast-export.c:667
+#: builtin/fast-export.c:670 builtin/tag.c:459
 msgid "mode"
 msgstr ""
 
-#: builtin/commit.c:1165 builtin/commit.c:1400
+#: builtin/commit.c:1235 builtin/commit.c:1467
 msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
 msgstr ""
 
-#: builtin/commit.c:1168
+#: builtin/commit.c:1238
 msgid "show ignored files"
 msgstr ""
 
-#: builtin/commit.c:1169 parse-options.h:151
+#: builtin/commit.c:1239 parse-options.h:154
 msgid "when"
 msgstr ""
 
-#: builtin/commit.c:1170
+#: builtin/commit.c:1240
 msgid ""
 "ignore changes to submodules, optional when: all, dirty, untracked. "
 "(Default: all)"
 msgstr ""
 
-#: builtin/commit.c:1172
+#: builtin/commit.c:1242
 msgid "list untracked files in columns"
 msgstr ""
 
-#: builtin/commit.c:1246
+#: builtin/commit.c:1313
 msgid "couldn't look up newly created commit"
 msgstr ""
 
-#: builtin/commit.c:1248
+#: builtin/commit.c:1315
 msgid "could not parse newly created commit"
 msgstr ""
 
-#: builtin/commit.c:1289
+#: builtin/commit.c:1356
 msgid "detached HEAD"
 msgstr ""
 
-#: builtin/commit.c:1291
+#: builtin/commit.c:1358
 msgid " (root-commit)"
 msgstr ""
 
-#: builtin/commit.c:1358
+#: builtin/commit.c:1425
 msgid "suppress summary after successful commit"
 msgstr ""
 
-#: builtin/commit.c:1359
+#: builtin/commit.c:1426
 msgid "show diff in commit message template"
 msgstr ""
 
-#: builtin/commit.c:1361
+#: builtin/commit.c:1428
 msgid "Commit message options"
 msgstr ""
 
-#: builtin/commit.c:1362 builtin/tag.c:457
+#: builtin/commit.c:1429 builtin/tag.c:457
 msgid "read message from file"
 msgstr ""
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "author"
 msgstr ""
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "override author for commit"
 msgstr ""
 
-#: builtin/commit.c:1364 builtin/gc.c:178
+#: builtin/commit.c:1431 builtin/gc.c:178
 msgid "date"
 msgstr ""
 
-#: builtin/commit.c:1364
+#: builtin/commit.c:1431
 msgid "override date for commit"
 msgstr ""
 
-#: builtin/commit.c:1365 builtin/merge.c:206 builtin/notes.c:533
-#: builtin/notes.c:690 builtin/tag.c:455
+#: builtin/commit.c:1432 builtin/merge.c:223 builtin/notes.c:405
+#: builtin/notes.c:562 builtin/tag.c:455
 msgid "message"
 msgstr ""
 
-#: builtin/commit.c:1365
+#: builtin/commit.c:1432
 msgid "commit message"
 msgstr ""
 
-#: builtin/commit.c:1366
+#: builtin/commit.c:1433
 msgid "reuse and edit message from specified commit"
 msgstr ""
 
-#: builtin/commit.c:1367
+#: builtin/commit.c:1434
 msgid "reuse message from specified commit"
 msgstr ""
 
-#: builtin/commit.c:1368
+#: builtin/commit.c:1435
 msgid "use autosquash formatted message to fixup specified commit"
 msgstr ""
 
-#: builtin/commit.c:1369
+#: builtin/commit.c:1436
 msgid "use autosquash formatted message to squash specified commit"
 msgstr ""
 
-#: builtin/commit.c:1370
+#: builtin/commit.c:1437
 msgid "the commit is authored by me now (used with -C/-c/--amend)"
 msgstr ""
 
-#: builtin/commit.c:1371 builtin/log.c:1102 builtin/revert.c:109
+#: builtin/commit.c:1438 builtin/log.c:1160 builtin/revert.c:111
 msgid "add Signed-off-by:"
 msgstr ""
 
-#: builtin/commit.c:1372
+#: builtin/commit.c:1439
 msgid "use specified template file"
 msgstr ""
 
-#: builtin/commit.c:1373
+#: builtin/commit.c:1440
 msgid "force edit of commit"
 msgstr ""
 
-#: builtin/commit.c:1374
+#: builtin/commit.c:1441
 msgid "default"
 msgstr ""
 
-#: builtin/commit.c:1374 builtin/tag.c:460
+#: builtin/commit.c:1441 builtin/tag.c:460
 msgid "how to strip spaces and #comments from message"
 msgstr ""
 
-#: builtin/commit.c:1375
+#: builtin/commit.c:1442
 msgid "include status in commit message template"
 msgstr ""
 
-#: builtin/commit.c:1376 builtin/merge.c:213 builtin/tag.c:461
+#: builtin/commit.c:1443 builtin/merge.c:230 builtin/tag.c:461
 msgid "key id"
 msgstr ""
 
-#: builtin/commit.c:1377 builtin/merge.c:214
+#: builtin/commit.c:1444 builtin/merge.c:231
 msgid "GPG sign commit"
 msgstr ""
 
 #. end commit message options
-#: builtin/commit.c:1380
+#: builtin/commit.c:1447
 msgid "Commit contents options"
 msgstr ""
 
-#: builtin/commit.c:1381
+#: builtin/commit.c:1448
 msgid "commit all changed files"
 msgstr ""
 
-#: builtin/commit.c:1382
+#: builtin/commit.c:1449
 msgid "add specified files to index for commit"
 msgstr ""
 
-#: builtin/commit.c:1383
+#: builtin/commit.c:1450
 msgid "interactively add files"
 msgstr ""
 
-#: builtin/commit.c:1384
+#: builtin/commit.c:1451
 msgid "interactively add changes"
 msgstr ""
 
-#: builtin/commit.c:1385
+#: builtin/commit.c:1452
 msgid "commit only specified files"
 msgstr ""
 
-#: builtin/commit.c:1386
+#: builtin/commit.c:1453
 msgid "bypass pre-commit hook"
 msgstr ""
 
-#: builtin/commit.c:1387
+#: builtin/commit.c:1454
 msgid "show what would be committed"
 msgstr ""
 
-#: builtin/commit.c:1398
+#: builtin/commit.c:1465
 msgid "amend previous commit"
 msgstr ""
 
-#: builtin/commit.c:1399
+#: builtin/commit.c:1466
 msgid "bypass post-rewrite hook"
 msgstr ""
 
-#: builtin/commit.c:1404
+#: builtin/commit.c:1471
 msgid "ok to record an empty change"
 msgstr ""
 
-#: builtin/commit.c:1407
+#: builtin/commit.c:1474
 msgid "ok to record a change with an empty message"
 msgstr ""
 
-#: builtin/commit.c:1439
+#: builtin/commit.c:1507
 msgid "could not parse HEAD commit"
 msgstr ""
 
-#: builtin/commit.c:1477 builtin/merge.c:508
+#: builtin/commit.c:1545 builtin/merge.c:525
 #, c-format
 msgid "could not open '%s' for reading"
 msgstr ""
 
-#: builtin/commit.c:1484
+#: builtin/commit.c:1552
 #, c-format
 msgid "Corrupt MERGE_HEAD file (%s)"
 msgstr ""
 
-#: builtin/commit.c:1491
+#: builtin/commit.c:1559
 msgid "could not read MERGE_MODE"
 msgstr ""
 
-#: builtin/commit.c:1510
+#: builtin/commit.c:1578
 #, c-format
 msgid "could not read commit message: %s"
 msgstr ""
 
-#: builtin/commit.c:1524
+#: builtin/commit.c:1592
 #, c-format
 msgid "Aborting commit; you did not edit the message.\n"
 msgstr ""
 
-#: builtin/commit.c:1529
+#: builtin/commit.c:1597
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr ""
 
-#: builtin/commit.c:1544 builtin/merge.c:832 builtin/merge.c:857
+#: builtin/commit.c:1612 builtin/merge.c:861 builtin/merge.c:886
 msgid "failed to write commit object"
 msgstr ""
 
-#: builtin/commit.c:1565
+#: builtin/commit.c:1633
 msgid "cannot lock HEAD ref"
 msgstr ""
 
-#: builtin/commit.c:1569
+#: builtin/commit.c:1637
 msgid "cannot update HEAD ref"
 msgstr ""
 
-#: builtin/commit.c:1580
+#: builtin/commit.c:1648
 msgid ""
 "Repository has been updated, but unable to write\n"
 "new_index file. Check that disk is not full or quota is\n"
@@ -3767,249 +4206,265 @@ msgstr ""
 msgid "git config [options]"
 msgstr ""
 
-#: builtin/config.c:51
+#: builtin/config.c:52
 msgid "Config file location"
 msgstr ""
 
-#: builtin/config.c:52
+#: builtin/config.c:53
 msgid "use global config file"
 msgstr ""
 
-#: builtin/config.c:53
+#: builtin/config.c:54
 msgid "use system config file"
 msgstr ""
 
-#: builtin/config.c:54
+#: builtin/config.c:55
 msgid "use repository config file"
 msgstr ""
 
-#: builtin/config.c:55
+#: builtin/config.c:56
 msgid "use given config file"
 msgstr ""
 
-#: builtin/config.c:56
-msgid "Action"
+#: builtin/config.c:57
+msgid "blob-id"
 msgstr ""
 
 #: builtin/config.c:57
-msgid "get value: name [value-regex]"
+msgid "read config from given blob object"
 msgstr ""
 
 #: builtin/config.c:58
-msgid "get all values: key [value-regex]"
+msgid "Action"
 msgstr ""
 
 #: builtin/config.c:59
-msgid "get values for regexp: name-regex [value-regex]"
+msgid "get value: name [value-regex]"
 msgstr ""
 
 #: builtin/config.c:60
-msgid "replace all matching variables: name value [value_regex]"
+msgid "get all values: key [value-regex]"
 msgstr ""
 
 #: builtin/config.c:61
-msgid "add a new variable: name value"
+msgid "get values for regexp: name-regex [value-regex]"
 msgstr ""
 
 #: builtin/config.c:62
-msgid "remove a variable: name [value-regex]"
+msgid "replace all matching variables: name value [value_regex]"
 msgstr ""
 
 #: builtin/config.c:63
-msgid "remove all matches: name [value-regex]"
+msgid "add a new variable: name value"
 msgstr ""
 
 #: builtin/config.c:64
-msgid "rename section: old-name new-name"
+msgid "remove a variable: name [value-regex]"
 msgstr ""
 
 #: builtin/config.c:65
-msgid "remove a section: name"
+msgid "remove all matches: name [value-regex]"
 msgstr ""
 
 #: builtin/config.c:66
-msgid "list all"
+msgid "rename section: old-name new-name"
 msgstr ""
 
 #: builtin/config.c:67
+msgid "remove a section: name"
+msgstr ""
+
+#: builtin/config.c:68
+msgid "list all"
+msgstr ""
+
+#: builtin/config.c:69
 msgid "open an editor"
 msgstr ""
 
-#: builtin/config.c:68 builtin/config.c:69
+#: builtin/config.c:70 builtin/config.c:71
 msgid "slot"
 msgstr ""
 
-#: builtin/config.c:68
+#: builtin/config.c:70
 msgid "find the color configured: [default]"
 msgstr ""
 
-#: builtin/config.c:69
+#: builtin/config.c:71
 msgid "find the color setting: [stdout-is-tty]"
 msgstr ""
 
-#: builtin/config.c:70
+#: builtin/config.c:72
 msgid "Type"
 msgstr ""
 
-#: builtin/config.c:71
+#: builtin/config.c:73
 msgid "value is \"true\" or \"false\""
 msgstr ""
 
-#: builtin/config.c:72
+#: builtin/config.c:74
 msgid "value is decimal number"
 msgstr ""
 
-#: builtin/config.c:73
+#: builtin/config.c:75
 msgid "value is --bool or --int"
 msgstr ""
 
-#: builtin/config.c:74
+#: builtin/config.c:76
 msgid "value is a path (file or directory name)"
 msgstr ""
 
-#: builtin/config.c:75
+#: builtin/config.c:77
 msgid "Other"
 msgstr ""
 
-#: builtin/config.c:76
+#: builtin/config.c:78
 msgid "terminate values with NUL byte"
 msgstr ""
 
-#: builtin/config.c:77
+#: builtin/config.c:79
 msgid "respect include directives on lookup"
 msgstr ""
 
-#: builtin/count-objects.c:69
-msgid "git count-objects [-v]"
+#: builtin/count-objects.c:82
+msgid "git count-objects [-v] [-H | --human-readable]"
 msgstr ""
 
-#: builtin/describe.c:15
-msgid "git describe [options] <committish>*"
+#: builtin/count-objects.c:97
+msgid "print sizes in human readable format"
 msgstr ""
 
 #: builtin/describe.c:16
+msgid "git describe [options] <committish>*"
+msgstr ""
+
+#: builtin/describe.c:17
 msgid "git describe [options] --dirty"
 msgstr ""
 
-#: builtin/describe.c:234
+#: builtin/describe.c:237
 #, c-format
 msgid "annotated tag %s not available"
 msgstr ""
 
-#: builtin/describe.c:238
+#: builtin/describe.c:241
 #, c-format
 msgid "annotated tag %s has no embedded name"
 msgstr ""
 
-#: builtin/describe.c:240
+#: builtin/describe.c:243
 #, c-format
 msgid "tag '%s' is really '%s' here"
 msgstr ""
 
-#: builtin/describe.c:267
+#: builtin/describe.c:270
 #, c-format
 msgid "Not a valid object name %s"
 msgstr ""
 
-#: builtin/describe.c:270
+#: builtin/describe.c:273
 #, c-format
 msgid "%s is not a valid '%s' object"
 msgstr ""
 
-#: builtin/describe.c:287
+#: builtin/describe.c:290
 #, c-format
 msgid "no tag exactly matches '%s'"
 msgstr ""
 
-#: builtin/describe.c:289
+#: builtin/describe.c:292
 #, c-format
 msgid "searching to describe %s\n"
 msgstr ""
 
-#: builtin/describe.c:329
+#: builtin/describe.c:332
 #, c-format
 msgid "finished search at %s\n"
 msgstr ""
 
-#: builtin/describe.c:353
+#: builtin/describe.c:359
 #, c-format
 msgid ""
 "No annotated tags can describe '%s'.\n"
 "However, there were unannotated tags: try --tags."
 msgstr ""
 
-#: builtin/describe.c:357
+#: builtin/describe.c:363
 #, c-format
 msgid ""
 "No tags can describe '%s'.\n"
 "Try --always, or create some tags."
 msgstr ""
 
-#: builtin/describe.c:378
+#: builtin/describe.c:384
 #, c-format
 msgid "traversed %lu commits\n"
 msgstr ""
 
-#: builtin/describe.c:381
+#: builtin/describe.c:387
 #, c-format
 msgid ""
 "more than %i tags found; listed %i most recent\n"
 "gave up search at %s\n"
 msgstr ""
 
-#: builtin/describe.c:403
+#: builtin/describe.c:409
 msgid "find the tag that comes after the commit"
 msgstr ""
 
-#: builtin/describe.c:404
+#: builtin/describe.c:410
 msgid "debug search strategy on stderr"
 msgstr ""
 
-#: builtin/describe.c:405
-msgid "use any ref in .git/refs"
+#: builtin/describe.c:411
+msgid "use any ref"
 msgstr ""
 
-#: builtin/describe.c:406
-msgid "use any tag in .git/refs/tags"
+#: builtin/describe.c:412
+msgid "use any tag, even unannotated"
 msgstr ""
 
-#: builtin/describe.c:407
+#: builtin/describe.c:413
 msgid "always use long format"
 msgstr ""
 
-#: builtin/describe.c:410
+#: builtin/describe.c:414
+msgid "only follow first parent"
+msgstr ""
+
+#: builtin/describe.c:417
 msgid "only output exact matches"
 msgstr ""
 
-#: builtin/describe.c:412
+#: builtin/describe.c:419
 msgid "consider <n> most recent tags (default: 10)"
 msgstr ""
 
-#: builtin/describe.c:414
+#: builtin/describe.c:421
 msgid "only consider tags matching <pattern>"
 msgstr ""
 
-#: builtin/describe.c:416 builtin/name-rev.c:238
+#: builtin/describe.c:423 builtin/name-rev.c:322
 msgid "show abbreviated commit object as fallback"
 msgstr ""
 
-#: builtin/describe.c:417
+#: builtin/describe.c:424
 msgid "mark"
 msgstr ""
 
-#: builtin/describe.c:418
+#: builtin/describe.c:425
 msgid "append <mark> on dirty working tree (default: \"-dirty\")"
 msgstr ""
 
-#: builtin/describe.c:436
+#: builtin/describe.c:443
 msgid "--long is incompatible with --abbrev=0"
 msgstr ""
 
-#: builtin/describe.c:462
+#: builtin/describe.c:469
 msgid "No names found, cannot describe anything."
 msgstr ""
 
-#: builtin/describe.c:482
+#: builtin/describe.c:489
 msgid "--dirty is incompatible with committishes"
 msgstr ""
 
@@ -4018,31 +4473,26 @@ msgstr ""
 msgid "'%s': not a regular file or symlink"
 msgstr ""
 
-#: builtin/diff.c:228
+#: builtin/diff.c:230
 #, c-format
 msgid "invalid option: %s"
 msgstr ""
 
-#: builtin/diff.c:305
+#: builtin/diff.c:307
 msgid "Not a git repository"
 msgstr ""
 
-#: builtin/diff.c:348
+#: builtin/diff.c:350
 #, c-format
 msgid "invalid object '%s' given."
 msgstr ""
 
-#: builtin/diff.c:353
-#, c-format
-msgid "more than %d trees given: '%s'"
-msgstr ""
-
-#: builtin/diff.c:363
+#: builtin/diff.c:359
 #, c-format
 msgid "more than two blobs given: '%s'"
 msgstr ""
 
-#: builtin/diff.c:371
+#: builtin/diff.c:366
 #, c-format
 msgid "unhandled object '%s' given."
 msgstr ""
@@ -4051,39 +4501,39 @@ msgstr ""
 msgid "git fast-export [rev-list-opts]"
 msgstr ""
 
-#: builtin/fast-export.c:646
+#: builtin/fast-export.c:666
 msgid "show progress after <n> objects"
 msgstr ""
 
-#: builtin/fast-export.c:648
+#: builtin/fast-export.c:668
 msgid "select handling of signed tags"
 msgstr ""
 
-#: builtin/fast-export.c:651
+#: builtin/fast-export.c:671
 msgid "select handling of tags that tag filtered objects"
 msgstr ""
 
-#: builtin/fast-export.c:654
+#: builtin/fast-export.c:674
 msgid "Dump marks to this file"
 msgstr ""
 
-#: builtin/fast-export.c:656
+#: builtin/fast-export.c:676
 msgid "Import marks from this file"
 msgstr ""
 
-#: builtin/fast-export.c:658
+#: builtin/fast-export.c:678
 msgid "Fake a tagger when tags lack one"
 msgstr ""
 
-#: builtin/fast-export.c:660
+#: builtin/fast-export.c:680
 msgid "Output full tree for each commit"
 msgstr ""
 
-#: builtin/fast-export.c:662
+#: builtin/fast-export.c:682
 msgid "Use the done feature to terminate the stream"
 msgstr ""
 
-#: builtin/fast-export.c:663
+#: builtin/fast-export.c:683
 msgid "Skip output of blob data"
 msgstr ""
 
@@ -4159,7 +4609,7 @@ msgstr ""
 msgid "convert to a complete repository"
 msgstr ""
 
-#: builtin/fetch.c:88 builtin/log.c:1119
+#: builtin/fetch.c:88 builtin/log.c:1177
 msgid "dir"
 msgstr ""
 
@@ -4171,162 +4621,162 @@ msgstr ""
 msgid "default mode for recursion"
 msgstr ""
 
-#: builtin/fetch.c:204
+#: builtin/fetch.c:220
 msgid "Couldn't find remote ref HEAD"
 msgstr ""
 
-#: builtin/fetch.c:257
+#: builtin/fetch.c:273
 #, c-format
 msgid "object %s not found"
 msgstr ""
 
-#: builtin/fetch.c:262
+#: builtin/fetch.c:278
 msgid "[up to date]"
 msgstr ""
 
-#: builtin/fetch.c:276
+#: builtin/fetch.c:292
 #, c-format
 msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
 msgstr ""
 
-#: builtin/fetch.c:277 builtin/fetch.c:363
+#: builtin/fetch.c:293 builtin/fetch.c:379
 msgid "[rejected]"
 msgstr ""
 
-#: builtin/fetch.c:288
+#: builtin/fetch.c:304
 msgid "[tag update]"
 msgstr ""
 
-#: builtin/fetch.c:290 builtin/fetch.c:325 builtin/fetch.c:343
+#: builtin/fetch.c:306 builtin/fetch.c:341 builtin/fetch.c:359
 msgid "  (unable to update local ref)"
 msgstr ""
 
-#: builtin/fetch.c:308
+#: builtin/fetch.c:324
 msgid "[new tag]"
 msgstr ""
 
-#: builtin/fetch.c:311
+#: builtin/fetch.c:327
 msgid "[new branch]"
 msgstr ""
 
-#: builtin/fetch.c:314
+#: builtin/fetch.c:330
 msgid "[new ref]"
 msgstr ""
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "unable to update local ref"
 msgstr ""
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "forced update"
 msgstr ""
 
-#: builtin/fetch.c:365
+#: builtin/fetch.c:381
 msgid "(non-fast-forward)"
 msgstr ""
 
-#: builtin/fetch.c:396 builtin/fetch.c:688
+#: builtin/fetch.c:412 builtin/fetch.c:718
 #, c-format
 msgid "cannot open %s: %s\n"
 msgstr ""
 
-#: builtin/fetch.c:405
+#: builtin/fetch.c:421
 #, c-format
 msgid "%s did not send all necessary objects\n"
 msgstr ""
 
-#: builtin/fetch.c:491
+#: builtin/fetch.c:520
 #, c-format
 msgid "From %.*s\n"
 msgstr ""
 
-#: builtin/fetch.c:502
+#: builtin/fetch.c:531
 #, 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:552
+#: builtin/fetch.c:581
 #, c-format
 msgid "   (%s will become dangling)"
 msgstr ""
 
-#: builtin/fetch.c:553
+#: builtin/fetch.c:582
 #, c-format
 msgid "   (%s has become dangling)"
 msgstr ""
 
-#: builtin/fetch.c:560
+#: builtin/fetch.c:589
 msgid "[deleted]"
 msgstr ""
 
-#: builtin/fetch.c:561 builtin/remote.c:1055
+#: builtin/fetch.c:590 builtin/remote.c:1055
 msgid "(none)"
 msgstr ""
 
-#: builtin/fetch.c:678
+#: builtin/fetch.c:708
 #, c-format
 msgid "Refusing to fetch into current branch %s of non-bare repository"
 msgstr ""
 
-#: builtin/fetch.c:712
+#: builtin/fetch.c:742
 #, c-format
 msgid "Don't know how to fetch from %s"
 msgstr ""
 
-#: builtin/fetch.c:789
+#: builtin/fetch.c:823
 #, c-format
 msgid "Option \"%s\" value \"%s\" is not valid for %s"
 msgstr ""
 
-#: builtin/fetch.c:792
+#: builtin/fetch.c:826
 #, c-format
 msgid "Option \"%s\" is ignored for %s\n"
 msgstr ""
 
-#: builtin/fetch.c:894
+#: builtin/fetch.c:928
 #, c-format
 msgid "Fetching %s\n"
 msgstr ""
 
-#: builtin/fetch.c:896 builtin/remote.c:100
+#: builtin/fetch.c:930 builtin/remote.c:100
 #, c-format
 msgid "Could not fetch %s"
 msgstr ""
 
-#: builtin/fetch.c:915
+#: builtin/fetch.c:949
 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:935
+#: builtin/fetch.c:969
 msgid "You need to specify a tag name."
 msgstr ""
 
-#: builtin/fetch.c:981
+#: builtin/fetch.c:1015
 msgid "--depth and --unshallow cannot be used together"
 msgstr ""
 
-#: builtin/fetch.c:983
+#: builtin/fetch.c:1017
 msgid "--unshallow on a complete repository does not make sense"
 msgstr ""
 
-#: builtin/fetch.c:1002
+#: builtin/fetch.c:1036
 msgid "fetch --all does not take a repository argument"
 msgstr ""
 
-#: builtin/fetch.c:1004
+#: builtin/fetch.c:1038
 msgid "fetch --all does not make sense with refspecs"
 msgstr ""
 
-#: builtin/fetch.c:1015
+#: builtin/fetch.c:1049
 #, c-format
 msgid "No such remote or remote group: %s"
 msgstr ""
 
-#: builtin/fetch.c:1023
+#: builtin/fetch.c:1057
 msgid "Fetching a group and specifying refspecs does not make sense"
 msgstr ""
 
@@ -4334,29 +4784,29 @@ msgstr ""
 msgid "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"
 msgstr ""
 
-#: builtin/fmt-merge-msg.c:659 builtin/fmt-merge-msg.c:662 builtin/grep.c:701
-#: builtin/merge.c:188 builtin/show-branch.c:656 builtin/show-ref.c:175
-#: builtin/tag.c:446 parse-options.h:133 parse-options.h:239
+#: builtin/fmt-merge-msg.c:663 builtin/fmt-merge-msg.c:666 builtin/grep.c:700
+#: builtin/merge.c:203 builtin/show-branch.c:655 builtin/show-ref.c:179
+#: builtin/tag.c:446 parse-options.h:133 parse-options.h:243
 msgid "n"
 msgstr ""
 
-#: builtin/fmt-merge-msg.c:660
+#: builtin/fmt-merge-msg.c:664
 msgid "populate log with at most <n> entries from shortlog"
 msgstr ""
 
-#: builtin/fmt-merge-msg.c:663
+#: builtin/fmt-merge-msg.c:667
 msgid "alias for --log (deprecated)"
 msgstr ""
 
-#: builtin/fmt-merge-msg.c:666
+#: builtin/fmt-merge-msg.c:670
 msgid "text"
 msgstr ""
 
-#: builtin/fmt-merge-msg.c:667
+#: builtin/fmt-merge-msg.c:671
 msgid "use <text> as start of message"
 msgstr ""
 
-#: builtin/fmt-merge-msg.c:668
+#: builtin/fmt-merge-msg.c:672
 msgid "file to read from"
 msgstr ""
 
@@ -4491,225 +4941,220 @@ msgstr ""
 msgid "grep: failed to create thread: %s"
 msgstr ""
 
-#: builtin/grep.c:365
+#: builtin/grep.c:364
 #, c-format
 msgid "Failed to chdir: %s"
 msgstr ""
 
-#: builtin/grep.c:443 builtin/grep.c:478
+#: builtin/grep.c:442 builtin/grep.c:477
 #, c-format
 msgid "unable to read tree (%s)"
 msgstr ""
 
-#: builtin/grep.c:493
+#: builtin/grep.c:492
 #, c-format
 msgid "unable to grep from object of type %s"
 msgstr ""
 
-#: builtin/grep.c:551
+#: builtin/grep.c:550
 #, c-format
 msgid "switch `%c' expects a numerical value"
 msgstr ""
 
-#: builtin/grep.c:568
+#: builtin/grep.c:567
 #, c-format
 msgid "cannot open '%s'"
 msgstr ""
 
-#: builtin/grep.c:643
+#: builtin/grep.c:642
 msgid "search in index instead of in the work tree"
 msgstr ""
 
-#: builtin/grep.c:645
+#: builtin/grep.c:644
 msgid "find in contents not managed by git"
 msgstr ""
 
-#: builtin/grep.c:647
+#: builtin/grep.c:646
 msgid "search in both tracked and untracked files"
 msgstr ""
 
-#: builtin/grep.c:649
+#: builtin/grep.c:648
 msgid "search also in ignored files"
 msgstr ""
 
-#: builtin/grep.c:652
+#: builtin/grep.c:651
 msgid "show non-matching lines"
 msgstr ""
 
-#: builtin/grep.c:654
+#: builtin/grep.c:653
 msgid "case insensitive matching"
 msgstr ""
 
-#: builtin/grep.c:656
+#: builtin/grep.c:655
 msgid "match patterns only at word boundaries"
 msgstr ""
 
-#: builtin/grep.c:658
+#: builtin/grep.c:657
 msgid "process binary files as text"
 msgstr ""
 
-#: builtin/grep.c:660
+#: builtin/grep.c:659
 msgid "don't match patterns in binary files"
 msgstr ""
 
-#: builtin/grep.c:663
+#: builtin/grep.c:662
 msgid "descend at most <depth> levels"
 msgstr ""
 
-#: builtin/grep.c:667
+#: builtin/grep.c:666
 msgid "use extended POSIX regular expressions"
 msgstr ""
 
-#: builtin/grep.c:670
+#: builtin/grep.c:669
 msgid "use basic POSIX regular expressions (default)"
 msgstr ""
 
-#: builtin/grep.c:673
+#: builtin/grep.c:672
 msgid "interpret patterns as fixed strings"
 msgstr ""
 
-#: builtin/grep.c:676
+#: builtin/grep.c:675
 msgid "use Perl-compatible regular expressions"
 msgstr ""
 
-#: builtin/grep.c:679
+#: builtin/grep.c:678
 msgid "show line numbers"
 msgstr ""
 
-#: builtin/grep.c:680
+#: builtin/grep.c:679
 msgid "don't show filenames"
 msgstr ""
 
-#: builtin/grep.c:681
+#: builtin/grep.c:680
 msgid "show filenames"
 msgstr ""
 
-#: builtin/grep.c:683
+#: builtin/grep.c:682
 msgid "show filenames relative to top directory"
 msgstr ""
 
-#: builtin/grep.c:685
+#: builtin/grep.c:684
 msgid "show only filenames instead of matching lines"
 msgstr ""
 
-#: builtin/grep.c:687
+#: builtin/grep.c:686
 msgid "synonym for --files-with-matches"
 msgstr ""
 
-#: builtin/grep.c:690
+#: builtin/grep.c:689
 msgid "show only the names of files without match"
 msgstr ""
 
-#: builtin/grep.c:692
+#: builtin/grep.c:691
 msgid "print NUL after filenames"
 msgstr ""
 
-#: builtin/grep.c:694
+#: builtin/grep.c:693
 msgid "show the number of matches instead of matching lines"
 msgstr ""
 
-#: builtin/grep.c:695
+#: builtin/grep.c:694
 msgid "highlight matches"
 msgstr ""
 
-#: builtin/grep.c:697
+#: builtin/grep.c:696
 msgid "print empty line between matches from different files"
 msgstr ""
 
-#: builtin/grep.c:699
+#: builtin/grep.c:698
 msgid "show filename only once above matches from same file"
 msgstr ""
 
-#: builtin/grep.c:702
+#: builtin/grep.c:701
 msgid "show <n> context lines before and after matches"
 msgstr ""
 
-#: builtin/grep.c:705
+#: builtin/grep.c:704
 msgid "show <n> context lines before matches"
 msgstr ""
 
-#: builtin/grep.c:707
+#: builtin/grep.c:706
 msgid "show <n> context lines after matches"
 msgstr ""
 
-#: builtin/grep.c:708
+#: builtin/grep.c:707
 msgid "shortcut for -C NUM"
 msgstr ""
 
-#: builtin/grep.c:711
+#: builtin/grep.c:710
 msgid "show a line with the function name before matches"
 msgstr ""
 
-#: builtin/grep.c:713
+#: builtin/grep.c:712
 msgid "show the surrounding function"
 msgstr ""
 
-#: builtin/grep.c:716
+#: builtin/grep.c:715
 msgid "read patterns from file"
 msgstr ""
 
-#: builtin/grep.c:718
+#: builtin/grep.c:717
 msgid "match <pattern>"
 msgstr ""
 
-#: builtin/grep.c:720
+#: builtin/grep.c:719
 msgid "combine patterns specified with -e"
 msgstr ""
 
-#: builtin/grep.c:732
+#: builtin/grep.c:731
 msgid "indicate hit with exit status without output"
 msgstr ""
 
-#: builtin/grep.c:734
+#: builtin/grep.c:733
 msgid "show only matches from files that match all patterns"
 msgstr ""
 
-#: builtin/grep.c:736
+#: builtin/grep.c:735
 msgid "show parse tree for grep expression"
 msgstr ""
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "pager"
 msgstr ""
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "show matching files in the pager"
 msgstr ""
 
-#: builtin/grep.c:743
+#: builtin/grep.c:742
 msgid "allow calling of grep(1) (ignored by this build)"
 msgstr ""
 
-#: builtin/grep.c:744 builtin/show-ref.c:184
+#: builtin/grep.c:743 builtin/show-ref.c:188
 msgid "show usage"
 msgstr ""
 
-#: builtin/grep.c:811
+#: builtin/grep.c:810
 msgid "no pattern given."
 msgstr ""
 
-#: builtin/grep.c:825
-#, c-format
-msgid "bad object %s"
-msgstr ""
-
-#: builtin/grep.c:868
+#: builtin/grep.c:865
 msgid "--open-files-in-pager only works on the worktree"
 msgstr ""
 
-#: builtin/grep.c:891
+#: builtin/grep.c:888
 msgid "--cached or --untracked cannot be used with --no-index."
 msgstr ""
 
-#: builtin/grep.c:896
+#: builtin/grep.c:893
 msgid "--no-index or --untracked cannot be used with revs."
 msgstr ""
 
-#: builtin/grep.c:899
+#: builtin/grep.c:896
 msgid "--[no-]exclude-standard cannot be used for tracked contents."
 msgstr ""
 
-#: builtin/grep.c:907
+#: builtin/grep.c:904
 msgid "both --cached and trees are given."
 msgstr ""
 
@@ -4747,8 +5192,12 @@ msgstr ""
 msgid "process file as it were from this path"
 msgstr ""
 
+#: builtin/help.c:41
+msgid "print all available commands"
+msgstr ""
+
 #: builtin/help.c:42
-msgid "print all available commands"
+msgid "print list of useful guides"
 msgstr ""
 
 #: builtin/help.c:43
@@ -4764,7 +5213,7 @@ msgid "show info page"
 msgstr ""
 
 #: builtin/help.c:52
-msgid "git help [--all] [--man|--web|--info] [command]"
+msgid "git help [--all] [--guides] [--man|--web|--info] [command]"
 msgstr ""
 
 #: builtin/help.c:64
@@ -4817,290 +5266,322 @@ msgstr ""
 msgid "no info viewer handled the request"
 msgstr ""
 
-#: builtin/help.c:429 builtin/help.c:436
+#: builtin/help.c:420
+msgid "Defining attributes per path"
+msgstr ""
+
+#: builtin/help.c:421
+msgid "A Git glossary"
+msgstr ""
+
+#: builtin/help.c:422
+msgid "Specifies intentionally untracked files to ignore"
+msgstr ""
+
+#: builtin/help.c:423
+msgid "Defining submodule properties"
+msgstr ""
+
+#: builtin/help.c:424
+msgid "Specifying revisions and ranges for Git"
+msgstr ""
+
+#: builtin/help.c:425
+msgid "A tutorial introduction to Git (for version 1.5.1 or newer)"
+msgstr ""
+
+#: builtin/help.c:426
+msgid "An overview of recommended workflows with Git"
+msgstr ""
+
+#: builtin/help.c:438
+msgid "The common Git guides are:\n"
+msgstr ""
+
+#: builtin/help.c:460 builtin/help.c:476
 #, c-format
 msgid "usage: %s%s"
 msgstr ""
 
-#: builtin/help.c:452
+#: builtin/help.c:492
 #, c-format
 msgid "`git %s' is aliased to `%s'"
 msgstr ""
 
-#: builtin/index-pack.c:170
+#: builtin/index-pack.c:184
 #, c-format
 msgid "object type mismatch at %s"
 msgstr ""
 
-#: builtin/index-pack.c:190
+#: builtin/index-pack.c:204
 msgid "object of unexpected type"
 msgstr ""
 
-#: builtin/index-pack.c:227
+#: builtin/index-pack.c:244
 #, c-format
 msgid "cannot fill %d byte"
 msgid_plural "cannot fill %d bytes"
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/index-pack.c:237
+#: builtin/index-pack.c:254
 msgid "early EOF"
 msgstr ""
 
-#: builtin/index-pack.c:238
+#: builtin/index-pack.c:255
 msgid "read error on input"
 msgstr ""
 
-#: builtin/index-pack.c:250
+#: builtin/index-pack.c:267
 msgid "used more bytes than were available"
 msgstr ""
 
-#: builtin/index-pack.c:257
+#: builtin/index-pack.c:274
 msgid "pack too large for current definition of off_t"
 msgstr ""
 
-#: builtin/index-pack.c:273
+#: builtin/index-pack.c:290
 #, c-format
 msgid "unable to create '%s'"
 msgstr ""
 
-#: builtin/index-pack.c:278
+#: builtin/index-pack.c:295
 #, c-format
 msgid "cannot open packfile '%s'"
 msgstr ""
 
-#: builtin/index-pack.c:292
+#: builtin/index-pack.c:309
 msgid "pack signature mismatch"
 msgstr ""
 
-#: builtin/index-pack.c:294
+#: builtin/index-pack.c:311
 #, c-format
 msgid "pack version %<PRIu32> unsupported"
 msgstr ""
 
-#: builtin/index-pack.c:312
+#: builtin/index-pack.c:329
 #, c-format
 msgid "pack has bad object at offset %lu: %s"
 msgstr ""
 
-#: builtin/index-pack.c:434
+#: builtin/index-pack.c:451
 #, c-format
 msgid "inflate returned %d"
 msgstr ""
 
-#: builtin/index-pack.c:483
+#: builtin/index-pack.c:500
 msgid "offset value overflow for delta base object"
 msgstr ""
 
-#: builtin/index-pack.c:491
+#: builtin/index-pack.c:508
 msgid "delta base offset is out of bound"
 msgstr ""
 
-#: builtin/index-pack.c:499
+#: builtin/index-pack.c:516
 #, c-format
 msgid "unknown object type %d"
 msgstr ""
 
-#: builtin/index-pack.c:530
+#: builtin/index-pack.c:547
 msgid "cannot pread pack file"
 msgstr ""
 
-#: builtin/index-pack.c:532
+#: builtin/index-pack.c:549
 #, 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:558
+#: builtin/index-pack.c:575
 msgid "serious inflate inconsistency"
 msgstr ""
 
-#: builtin/index-pack.c:649 builtin/index-pack.c:655 builtin/index-pack.c:678
-#: builtin/index-pack.c:712 builtin/index-pack.c:721
+#: 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
 #, c-format
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr ""
 
-#: builtin/index-pack.c:652 builtin/pack-objects.c:170
+#: builtin/index-pack.c:669 builtin/pack-objects.c:170
 #: builtin/pack-objects.c:262
 #, c-format
 msgid "unable to read %s"
 msgstr ""
 
-#: builtin/index-pack.c:718
+#: builtin/index-pack.c:735
 #, c-format
 msgid "cannot read existing object %s"
 msgstr ""
 
-#: builtin/index-pack.c:732
+#: builtin/index-pack.c:749
 #, c-format
 msgid "invalid blob object %s"
 msgstr ""
 
-#: builtin/index-pack.c:747
+#: builtin/index-pack.c:763
 #, c-format
 msgid "invalid %s"
 msgstr ""
 
-#: builtin/index-pack.c:749
+#: builtin/index-pack.c:766
 msgid "Error in object"
 msgstr ""
 
-#: builtin/index-pack.c:751
+#: builtin/index-pack.c:768
 #, c-format
 msgid "Not all child objects of %s are reachable"
 msgstr ""
 
-#: builtin/index-pack.c:821 builtin/index-pack.c:847
+#: builtin/index-pack.c:838 builtin/index-pack.c:868
 msgid "failed to apply delta"
 msgstr ""
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Receiving objects"
 msgstr ""
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Indexing objects"
 msgstr ""
 
-#: builtin/index-pack.c:1012
+#: builtin/index-pack.c:1035
 msgid "pack is corrupted (SHA1 mismatch)"
 msgstr ""
 
-#: builtin/index-pack.c:1017
+#: builtin/index-pack.c:1040
 msgid "cannot fstat packfile"
 msgstr ""
 
-#: builtin/index-pack.c:1020
+#: builtin/index-pack.c:1043
 msgid "pack has junk at the end"
 msgstr ""
 
-#: builtin/index-pack.c:1031
+#: builtin/index-pack.c:1054
 msgid "confusion beyond insanity in parse_pack_objects()"
 msgstr ""
 
-#: builtin/index-pack.c:1054
+#: builtin/index-pack.c:1077
 msgid "Resolving deltas"
 msgstr ""
 
-#: builtin/index-pack.c:1064
+#: builtin/index-pack.c:1087
 #, c-format
 msgid "unable to create thread: %s"
 msgstr ""
 
-#: builtin/index-pack.c:1106
+#: builtin/index-pack.c:1129
 msgid "confusion beyond insanity"
 msgstr ""
 
-#: builtin/index-pack.c:1112
+#: builtin/index-pack.c:1137
 #, c-format
 msgid "completed with %d local objects"
 msgstr ""
 
-#: builtin/index-pack.c:1121
+#: builtin/index-pack.c:1147
 #, c-format
 msgid "Unexpected tail checksum for %s (disk corruption?)"
 msgstr ""
 
-#: builtin/index-pack.c:1125
+#: builtin/index-pack.c:1151
 #, c-format
 msgid "pack has %d unresolved delta"
 msgid_plural "pack has %d unresolved deltas"
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/index-pack.c:1150
+#: builtin/index-pack.c:1176
 #, c-format
 msgid "unable to deflate appended object (%d)"
 msgstr ""
 
-#: builtin/index-pack.c:1229
+#: builtin/index-pack.c:1255
 #, c-format
 msgid "local object %s is corrupt"
 msgstr ""
 
-#: builtin/index-pack.c:1253
+#: builtin/index-pack.c:1279
 msgid "error while closing pack file"
 msgstr ""
 
-#: builtin/index-pack.c:1266
+#: builtin/index-pack.c:1292
 #, c-format
 msgid "cannot write keep file '%s'"
 msgstr ""
 
-#: builtin/index-pack.c:1274
+#: builtin/index-pack.c:1300
 #, c-format
 msgid "cannot close written keep file '%s'"
 msgstr ""
 
-#: builtin/index-pack.c:1287
+#: builtin/index-pack.c:1313
 msgid "cannot store pack file"
 msgstr ""
 
-#: builtin/index-pack.c:1298
+#: builtin/index-pack.c:1324
 msgid "cannot store index file"
 msgstr ""
 
-#: builtin/index-pack.c:1331
+#: builtin/index-pack.c:1357
 #, c-format
 msgid "bad pack.indexversion=%<PRIu32>"
 msgstr ""
 
-#: builtin/index-pack.c:1337
+#: builtin/index-pack.c:1363
 #, c-format
 msgid "invalid number of threads specified (%d)"
 msgstr ""
 
-#: builtin/index-pack.c:1341 builtin/index-pack.c:1514
+#: builtin/index-pack.c:1367 builtin/index-pack.c:1545
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr ""
 
-#: builtin/index-pack.c:1399
+#: builtin/index-pack.c:1425
 #, c-format
 msgid "Cannot open existing pack file '%s'"
 msgstr ""
 
-#: builtin/index-pack.c:1401
+#: builtin/index-pack.c:1427
 #, c-format
 msgid "Cannot open existing pack idx file for '%s'"
 msgstr ""
 
-#: builtin/index-pack.c:1448
+#: builtin/index-pack.c:1474
 #, c-format
 msgid "non delta: %d object"
 msgid_plural "non delta: %d objects"
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/index-pack.c:1455
+#: builtin/index-pack.c:1481
 #, c-format
 msgid "chain length = %d: %lu object"
 msgid_plural "chain length = %d: %lu objects"
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/index-pack.c:1482
+#: builtin/index-pack.c:1509
 msgid "Cannot come back to cwd"
 msgstr ""
 
-#: builtin/index-pack.c:1526 builtin/index-pack.c:1529
-#: builtin/index-pack.c:1541 builtin/index-pack.c:1545
+#: builtin/index-pack.c:1557 builtin/index-pack.c:1560
+#: builtin/index-pack.c:1572 builtin/index-pack.c:1576
 #, c-format
 msgid "bad %s"
 msgstr ""
 
-#: builtin/index-pack.c:1559
+#: builtin/index-pack.c:1590
 msgid "--fix-thin cannot be used without --stdin"
 msgstr ""
 
-#: builtin/index-pack.c:1563 builtin/index-pack.c:1573
+#: builtin/index-pack.c:1594 builtin/index-pack.c:1604
 #, c-format
 msgid "packfile name '%s' does not end with '.pack'"
 msgstr ""
 
-#: builtin/index-pack.c:1582
+#: builtin/index-pack.c:1613
 msgid "--verify with no packfile name given"
 msgstr ""
 
@@ -5234,7 +5715,7 @@ msgstr ""
 msgid "specify that the git repository is to be shared amongst several users"
 msgstr ""
 
-#: builtin/init-db.c:493 builtin/prune-packed.c:77
+#: builtin/init-db.c:493 builtin/prune-packed.c:75
 msgid "be quiet"
 msgstr ""
 
@@ -5264,345 +5745,353 @@ msgstr ""
 msgid "Cannot access work tree '%s'"
 msgstr ""
 
-#: builtin/log.c:39
-msgid "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
+#: builtin/log.c:41
+msgid "git log [<options>] [<revision range>] [[--] <path>...]\n"
 msgstr ""
 
-#: builtin/log.c:40
+#: builtin/log.c:42
 msgid "   or: git show [options] <object>..."
 msgstr ""
 
-#: builtin/log.c:102
+#: builtin/log.c:124
 msgid "suppress diff output"
 msgstr ""
 
-#: builtin/log.c:103
+#: builtin/log.c:125
 msgid "show source"
 msgstr ""
 
-#: builtin/log.c:104
+#: builtin/log.c:126
 msgid "Use mail map file"
 msgstr ""
 
-#: builtin/log.c:105
+#: builtin/log.c:127
 msgid "decorate options"
 msgstr ""
 
-#: builtin/log.c:198
+#: builtin/log.c:230
 #, c-format
 msgid "Final output: %d %s\n"
 msgstr ""
 
-#: builtin/log.c:419 builtin/log.c:511
+#: builtin/log.c:453 builtin/log.c:545
 #, c-format
 msgid "Could not read object %s"
 msgstr ""
 
-#: builtin/log.c:535
+#: builtin/log.c:569
 #, c-format
 msgid "Unknown type: %d"
 msgstr ""
 
-#: builtin/log.c:627
+#: builtin/log.c:669
 msgid "format.headers without value"
 msgstr ""
 
-#: builtin/log.c:701
+#: builtin/log.c:751
 msgid "name of output directory is too long"
 msgstr ""
 
-#: builtin/log.c:717
+#: builtin/log.c:767
 #, c-format
 msgid "Cannot open patch file %s"
 msgstr ""
 
-#: builtin/log.c:731
+#: builtin/log.c:781
 msgid "Need exactly one range."
 msgstr ""
 
-#: builtin/log.c:739
+#: builtin/log.c:789
 msgid "Not a range."
 msgstr ""
 
-#: builtin/log.c:812
+#: builtin/log.c:891
 msgid "Cover letter needs email format"
 msgstr ""
 
-#: builtin/log.c:885
+#: builtin/log.c:967
 #, c-format
 msgid "insane in-reply-to: %s"
 msgstr ""
 
-#: builtin/log.c:913
+#: builtin/log.c:995
 msgid "git format-patch [options] [<since> | <revision range>]"
 msgstr ""
 
-#: builtin/log.c:958
+#: builtin/log.c:1040
 msgid "Two output directories?"
 msgstr ""
 
-#: builtin/log.c:1097
+#: builtin/log.c:1155
 msgid "use [PATCH n/m] even with a single patch"
 msgstr ""
 
-#: builtin/log.c:1100
+#: builtin/log.c:1158
 msgid "use [PATCH] even with multiple patches"
 msgstr ""
 
-#: builtin/log.c:1104
+#: builtin/log.c:1162
 msgid "print patches to standard out"
 msgstr ""
 
-#: builtin/log.c:1106
+#: builtin/log.c:1164
 msgid "generate a cover letter"
 msgstr ""
 
-#: builtin/log.c:1108
+#: builtin/log.c:1166
 msgid "use simple number sequence for output file names"
 msgstr ""
 
-#: builtin/log.c:1109
+#: builtin/log.c:1167
 msgid "sfx"
 msgstr ""
 
-#: builtin/log.c:1110
+#: builtin/log.c:1168
 msgid "use <sfx> instead of '.patch'"
 msgstr ""
 
-#: builtin/log.c:1112
+#: builtin/log.c:1170
 msgid "start numbering patches at <n> instead of 1"
 msgstr ""
 
-#: builtin/log.c:1114
+#: builtin/log.c:1172
 msgid "mark the series as Nth re-roll"
 msgstr ""
 
-#: builtin/log.c:1116
+#: builtin/log.c:1174
 msgid "Use [<prefix>] instead of [PATCH]"
 msgstr ""
 
-#: builtin/log.c:1119
+#: builtin/log.c:1177
 msgid "store resulting files in <dir>"
 msgstr ""
 
-#: builtin/log.c:1122
+#: builtin/log.c:1180
 msgid "don't strip/add [PATCH]"
 msgstr ""
 
-#: builtin/log.c:1125
+#: builtin/log.c:1183
 msgid "don't output binary diffs"
 msgstr ""
 
-#: builtin/log.c:1127
+#: builtin/log.c:1185
 msgid "don't include a patch matching a commit upstream"
 msgstr ""
 
-#: builtin/log.c:1129
+#: builtin/log.c:1187
 msgid "show patch format instead of default (patch + stat)"
 msgstr ""
 
-#: builtin/log.c:1131
+#: builtin/log.c:1189
 msgid "Messaging"
 msgstr ""
 
-#: builtin/log.c:1132
+#: builtin/log.c:1190
 msgid "header"
 msgstr ""
 
-#: builtin/log.c:1133
+#: builtin/log.c:1191
 msgid "add email header"
 msgstr ""
 
-#: builtin/log.c:1134 builtin/log.c:1136
+#: builtin/log.c:1192 builtin/log.c:1194
 msgid "email"
 msgstr ""
 
-#: builtin/log.c:1134
+#: builtin/log.c:1192
 msgid "add To: header"
 msgstr ""
 
-#: builtin/log.c:1136
+#: builtin/log.c:1194
 msgid "add Cc: header"
 msgstr ""
 
-#: builtin/log.c:1138
+#: builtin/log.c:1196
+msgid "ident"
+msgstr ""
+
+#: builtin/log.c:1197
+msgid "set From address to <ident> (or committer ident if absent)"
+msgstr ""
+
+#: builtin/log.c:1199
 msgid "message-id"
 msgstr ""
 
-#: builtin/log.c:1139
+#: builtin/log.c:1200
 msgid "make first mail a reply to <message-id>"
 msgstr ""
 
-#: builtin/log.c:1140 builtin/log.c:1143
+#: builtin/log.c:1201 builtin/log.c:1204
 msgid "boundary"
 msgstr ""
 
-#: builtin/log.c:1141
+#: builtin/log.c:1202
 msgid "attach the patch"
 msgstr ""
 
-#: builtin/log.c:1144
+#: builtin/log.c:1205
 msgid "inline the patch"
 msgstr ""
 
-#: builtin/log.c:1148
+#: builtin/log.c:1209
 msgid "enable message threading, styles: shallow, deep"
 msgstr ""
 
-#: builtin/log.c:1150
+#: builtin/log.c:1211
 msgid "signature"
 msgstr ""
 
-#: builtin/log.c:1151
+#: builtin/log.c:1212
 msgid "add a signature"
 msgstr ""
 
-#: builtin/log.c:1153
+#: builtin/log.c:1214
 msgid "don't print the patch filenames"
 msgstr ""
 
-#: builtin/log.c:1202
+#: builtin/log.c:1288
 #, c-format
-msgid "bogus committer info %s"
+msgid "invalid ident line: %s"
 msgstr ""
 
-#: builtin/log.c:1247
+#: builtin/log.c:1303
 msgid "-n and -k are mutually exclusive."
 msgstr ""
 
-#: builtin/log.c:1249
+#: builtin/log.c:1305
 msgid "--subject-prefix and -k are mutually exclusive."
 msgstr ""
 
-#: builtin/log.c:1257
+#: builtin/log.c:1313
 msgid "--name-only does not make sense"
 msgstr ""
 
-#: builtin/log.c:1259
+#: builtin/log.c:1315
 msgid "--name-status does not make sense"
 msgstr ""
 
-#: builtin/log.c:1261
+#: builtin/log.c:1317
 msgid "--check does not make sense"
 msgstr ""
 
-#: builtin/log.c:1284
+#: builtin/log.c:1340
 msgid "standard output, or directory, which one?"
 msgstr ""
 
-#: builtin/log.c:1286
+#: builtin/log.c:1342
 #, c-format
 msgid "Could not create directory '%s'"
 msgstr ""
 
-#: builtin/log.c:1439
+#: builtin/log.c:1490
 msgid "Failed to create output files"
 msgstr ""
 
-#: builtin/log.c:1488
+#: builtin/log.c:1539
 msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
 msgstr ""
 
-#: builtin/log.c:1543
+#: builtin/log.c:1594
 #, c-format
 msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr ""
 
-#: builtin/log.c:1556 builtin/log.c:1558 builtin/log.c:1570
+#: builtin/log.c:1607 builtin/log.c:1609 builtin/log.c:1621
 #, c-format
 msgid "Unknown commit %s"
 msgstr ""
 
-#: builtin/ls-files.c:409
+#: builtin/ls-files.c:408
 msgid "git ls-files [options] [<file>...]"
 msgstr ""
 
-#: builtin/ls-files.c:466
+#: builtin/ls-files.c:465
 msgid "identify the file status with tags"
 msgstr ""
 
-#: builtin/ls-files.c:468
+#: builtin/ls-files.c:467
 msgid "use lowercase letters for 'assume unchanged' files"
 msgstr ""
 
-#: builtin/ls-files.c:470
+#: builtin/ls-files.c:469
 msgid "show cached files in the output (default)"
 msgstr ""
 
-#: builtin/ls-files.c:472
+#: builtin/ls-files.c:471
 msgid "show deleted files in the output"
 msgstr ""
 
-#: builtin/ls-files.c:474
+#: builtin/ls-files.c:473
 msgid "show modified files in the output"
 msgstr ""
 
-#: builtin/ls-files.c:476
+#: builtin/ls-files.c:475
 msgid "show other files in the output"
 msgstr ""
 
-#: builtin/ls-files.c:478
+#: builtin/ls-files.c:477
 msgid "show ignored files in the output"
 msgstr ""
 
-#: builtin/ls-files.c:481
+#: builtin/ls-files.c:480
 msgid "show staged contents' object name in the output"
 msgstr ""
 
-#: builtin/ls-files.c:483
+#: builtin/ls-files.c:482
 msgid "show files on the filesystem that need to be removed"
 msgstr ""
 
-#: builtin/ls-files.c:485
+#: builtin/ls-files.c:484
 msgid "show 'other' directories' name only"
 msgstr ""
 
-#: builtin/ls-files.c:488
+#: builtin/ls-files.c:487
 msgid "don't show empty directories"
 msgstr ""
 
-#: builtin/ls-files.c:491
+#: builtin/ls-files.c:490
 msgid "show unmerged files in the output"
 msgstr ""
 
-#: builtin/ls-files.c:493
+#: builtin/ls-files.c:492
 msgid "show resolve-undo information"
 msgstr ""
 
-#: builtin/ls-files.c:495
+#: builtin/ls-files.c:494
 msgid "skip files matching pattern"
 msgstr ""
 
-#: builtin/ls-files.c:498
+#: builtin/ls-files.c:497
 msgid "exclude patterns are read from <file>"
 msgstr ""
 
-#: builtin/ls-files.c:501
+#: builtin/ls-files.c:500
 msgid "read additional per-directory exclude patterns in <file>"
 msgstr ""
 
-#: builtin/ls-files.c:503
+#: builtin/ls-files.c:502
 msgid "add the standard git exclusions"
 msgstr ""
 
-#: builtin/ls-files.c:506
+#: builtin/ls-files.c:505
 msgid "make the output relative to the project top directory"
 msgstr ""
 
-#: builtin/ls-files.c:509
+#: builtin/ls-files.c:508
 msgid "if any <file> is not in the index, treat this as an error"
 msgstr ""
 
-#: builtin/ls-files.c:510
+#: builtin/ls-files.c:509
 msgid "tree-ish"
 msgstr ""
 
-#: builtin/ls-files.c:511
+#: builtin/ls-files.c:510
 msgid "pretend that paths removed since <tree-ish> are still present"
 msgstr ""
 
-#: builtin/ls-files.c:513
+#: builtin/ls-files.c:512
 msgid "show debugging data"
 msgstr ""
 
@@ -5654,163 +6143,167 @@ msgstr ""
 msgid "git merge --abort"
 msgstr ""
 
-#: builtin/merge.c:90
+#: builtin/merge.c:98
 msgid "switch `m' requires a value"
 msgstr ""
 
-#: builtin/merge.c:127
+#: builtin/merge.c:135
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
 msgstr ""
 
-#: builtin/merge.c:128
+#: builtin/merge.c:136
 #, c-format
 msgid "Available strategies are:"
 msgstr ""
 
-#: builtin/merge.c:133
+#: builtin/merge.c:141
 #, c-format
 msgid "Available custom strategies are:"
 msgstr ""
 
-#: builtin/merge.c:183
+#: builtin/merge.c:198
 msgid "do not show a diffstat at the end of the merge"
 msgstr ""
 
-#: builtin/merge.c:186
+#: builtin/merge.c:201
 msgid "show a diffstat at the end of the merge"
 msgstr ""
 
-#: builtin/merge.c:187
+#: builtin/merge.c:202
 msgid "(synonym to --stat)"
 msgstr ""
 
-#: builtin/merge.c:189
+#: builtin/merge.c:204
 msgid "add (at most <n>) entries from shortlog to merge commit message"
 msgstr ""
 
-#: builtin/merge.c:192
+#: builtin/merge.c:207
 msgid "create a single commit instead of doing a merge"
 msgstr ""
 
-#: builtin/merge.c:194
+#: builtin/merge.c:209
 msgid "perform a commit if the merge succeeds (default)"
 msgstr ""
 
-#: builtin/merge.c:196
+#: builtin/merge.c:211
 msgid "edit message before committing"
 msgstr ""
 
-#: builtin/merge.c:198
+#: builtin/merge.c:212
 msgid "allow fast-forward (default)"
 msgstr ""
 
-#: builtin/merge.c:200
+#: builtin/merge.c:214
 msgid "abort if fast-forward is not possible"
 msgstr ""
 
-#: builtin/merge.c:202 builtin/notes.c:866 builtin/revert.c:112
+#: builtin/merge.c:218
+msgid "Verify that the named commit has a valid GPG signature"
+msgstr ""
+
+#: builtin/merge.c:219 builtin/notes.c:738 builtin/revert.c:114
 msgid "strategy"
 msgstr ""
 
-#: builtin/merge.c:203
+#: builtin/merge.c:220
 msgid "merge strategy to use"
 msgstr ""
 
-#: builtin/merge.c:204
+#: builtin/merge.c:221
 msgid "option=value"
 msgstr ""
 
-#: builtin/merge.c:205
+#: builtin/merge.c:222
 msgid "option for selected merge strategy"
 msgstr ""
 
-#: builtin/merge.c:207
+#: builtin/merge.c:224
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
 
-#: builtin/merge.c:211
+#: builtin/merge.c:228
 msgid "abort the current in-progress merge"
 msgstr ""
 
-#: builtin/merge.c:240
+#: builtin/merge.c:257
 msgid "could not run stash."
 msgstr ""
 
-#: builtin/merge.c:245
+#: builtin/merge.c:262
 msgid "stash failed"
 msgstr ""
 
-#: builtin/merge.c:250
+#: builtin/merge.c:267
 #, c-format
 msgid "not a valid object: %s"
 msgstr ""
 
-#: builtin/merge.c:269 builtin/merge.c:286
+#: builtin/merge.c:286 builtin/merge.c:303
 msgid "read-tree failed"
 msgstr ""
 
-#: builtin/merge.c:316
+#: builtin/merge.c:333
 msgid " (nothing to squash)"
 msgstr ""
 
-#: builtin/merge.c:329
+#: builtin/merge.c:346
 #, c-format
 msgid "Squash commit -- not updating HEAD\n"
 msgstr ""
 
-#: builtin/merge.c:361
+#: builtin/merge.c:378
 msgid "Writing SQUASH_MSG"
 msgstr ""
 
-#: builtin/merge.c:363
+#: builtin/merge.c:380
 msgid "Finishing SQUASH_MSG"
 msgstr ""
 
-#: builtin/merge.c:386
+#: builtin/merge.c:403
 #, c-format
 msgid "No merge message -- not updating HEAD\n"
 msgstr ""
 
-#: builtin/merge.c:436
+#: builtin/merge.c:453
 #, c-format
 msgid "'%s' does not point to a commit"
 msgstr ""
 
-#: builtin/merge.c:535
+#: builtin/merge.c:565
 #, c-format
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr ""
 
-#: builtin/merge.c:628
+#: builtin/merge.c:657
 msgid "git write-tree failed to write a tree"
 msgstr ""
 
-#: builtin/merge.c:656
+#: builtin/merge.c:685
 msgid "Not handling anything other than two heads merge."
 msgstr ""
 
-#: builtin/merge.c:670
+#: builtin/merge.c:699
 #, c-format
 msgid "Unknown option for merge-recursive: -X%s"
 msgstr ""
 
-#: builtin/merge.c:684
+#: builtin/merge.c:713
 #, c-format
 msgid "unable to write %s"
 msgstr ""
 
-#: builtin/merge.c:773
+#: builtin/merge.c:802
 #, c-format
 msgid "Could not read from '%s'"
 msgstr ""
 
-#: builtin/merge.c:782
+#: builtin/merge.c:811
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr ""
 
-#: builtin/merge.c:788
+#: builtin/merge.c:817
 #, c-format
 msgid ""
 "Please enter a commit message to explain why this merge is necessary,\n"
@@ -5820,140 +6313,157 @@ msgid ""
 "the commit.\n"
 msgstr ""
 
-#: builtin/merge.c:812
+#: builtin/merge.c:841
 msgid "Empty commit message."
 msgstr ""
 
-#: builtin/merge.c:824
+#: builtin/merge.c:853
 #, c-format
 msgid "Wonderful.\n"
 msgstr ""
 
-#: builtin/merge.c:889
+#: builtin/merge.c:918
 #, c-format
 msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
 msgstr ""
 
-#: builtin/merge.c:905
+#: builtin/merge.c:934
 #, c-format
 msgid "'%s' is not a commit"
 msgstr ""
 
-#: builtin/merge.c:946
+#: builtin/merge.c:975
 msgid "No current branch."
 msgstr ""
 
-#: builtin/merge.c:948
+#: builtin/merge.c:977
 msgid "No remote for the current branch."
 msgstr ""
 
-#: builtin/merge.c:950
+#: builtin/merge.c:979
 msgid "No default upstream defined for the current branch."
 msgstr ""
 
-#: builtin/merge.c:955
-#, c-format
-msgid "No remote tracking branch for %s from %s"
-msgstr ""
-
-#: builtin/merge.c:1042 builtin/merge.c:1199
+#: builtin/merge.c:984
 #, c-format
-msgid "%s - not something we can merge"
+msgid "No remote-tracking branch for %s from %s"
 msgstr ""
 
-#: builtin/merge.c:1110
+#: builtin/merge.c:1140
 msgid "There is no merge to abort (MERGE_HEAD missing)."
 msgstr ""
 
-#: builtin/merge.c:1126 git-pull.sh:31
+#: builtin/merge.c:1156 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:1129 git-pull.sh:34
+#: builtin/merge.c:1159 git-pull.sh:34
 msgid "You have not concluded your merge (MERGE_HEAD exists)."
 msgstr ""
 
-#: builtin/merge.c:1133
+#: builtin/merge.c:1163
 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:1136
+#: builtin/merge.c:1166
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
 msgstr ""
 
-#: builtin/merge.c:1145
+#: builtin/merge.c:1175
 msgid "You cannot combine --squash with --no-ff."
 msgstr ""
 
-#: builtin/merge.c:1150
-msgid "You cannot combine --no-ff with --ff-only."
-msgstr ""
-
-#: builtin/merge.c:1157
+#: builtin/merge.c:1184
 msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr ""
 
-#: builtin/merge.c:1189
+#: builtin/merge.c:1216
 msgid "Can merge only exactly one commit into empty head"
 msgstr ""
 
-#: builtin/merge.c:1192
+#: builtin/merge.c:1219
 msgid "Squash commit into empty head not supported yet"
 msgstr ""
 
-#: builtin/merge.c:1194
+#: builtin/merge.c:1221
 msgid "Non-fast-forward commit does not make sense into an empty head"
 msgstr ""
 
-#: builtin/merge.c:1310
+#: builtin/merge.c:1226
+#, c-format
+msgid "%s - not something we can merge"
+msgstr ""
+
+#: builtin/merge.c:1277
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr ""
+
+#: builtin/merge.c:1280
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr ""
+
+#. 'N'
+#: builtin/merge.c:1283
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr ""
+
+#: builtin/merge.c:1286
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr ""
+
+#: builtin/merge.c:1370
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr ""
 
-#: builtin/merge.c:1349
+#: builtin/merge.c:1409
 #, c-format
 msgid "Trying really trivial in-index merge...\n"
 msgstr ""
 
-#: builtin/merge.c:1356
+#: builtin/merge.c:1416
 #, c-format
 msgid "Nope.\n"
 msgstr ""
 
-#: builtin/merge.c:1388
+#: builtin/merge.c:1448
 msgid "Not possible to fast-forward, aborting."
 msgstr ""
 
-#: builtin/merge.c:1411 builtin/merge.c:1490
+#: builtin/merge.c:1471 builtin/merge.c:1550
 #, c-format
 msgid "Rewinding the tree to pristine...\n"
 msgstr ""
 
-#: builtin/merge.c:1415
+#: builtin/merge.c:1475
 #, c-format
 msgid "Trying merge strategy %s...\n"
 msgstr ""
 
-#: builtin/merge.c:1481
+#: builtin/merge.c:1541
 #, c-format
 msgid "No merge strategy handled the merge.\n"
 msgstr ""
 
-#: builtin/merge.c:1483
+#: builtin/merge.c:1543
 #, c-format
 msgid "Merge with strategy %s failed.\n"
 msgstr ""
 
-#: builtin/merge.c:1492
+#: builtin/merge.c:1552
 #, c-format
 msgid "Using the %s to prepare resolving by hand.\n"
 msgstr ""
 
-#: builtin/merge.c:1504
+#: builtin/merge.c:1564
 #, c-format
 msgid "Automatic merge went well; stopped before committing as requested\n"
 msgstr ""
@@ -6118,369 +6628,352 @@ msgstr ""
 msgid "renaming '%s' failed"
 msgstr ""
 
-#: builtin/name-rev.c:175
+#: builtin/name-rev.c:259
 msgid "git name-rev [options] <commit>..."
 msgstr ""
 
-#: builtin/name-rev.c:176
+#: builtin/name-rev.c:260
 msgid "git name-rev [options] --all"
 msgstr ""
 
-#: builtin/name-rev.c:177
+#: builtin/name-rev.c:261
 msgid "git name-rev [options] --stdin"
 msgstr ""
 
-#: builtin/name-rev.c:229
+#: builtin/name-rev.c:313
 msgid "print only names (no SHA-1)"
 msgstr ""
 
-#: builtin/name-rev.c:230
+#: builtin/name-rev.c:314
 msgid "only use tags to name the commits"
 msgstr ""
 
-#: builtin/name-rev.c:232
+#: builtin/name-rev.c:316
 msgid "only use refs matching <pattern>"
 msgstr ""
 
-#: builtin/name-rev.c:234
+#: builtin/name-rev.c:318
 msgid "list all commits reachable from all refs"
 msgstr ""
 
-#: builtin/name-rev.c:235
+#: builtin/name-rev.c:319
 msgid "read from stdin"
 msgstr ""
 
-#: builtin/name-rev.c:236
+#: builtin/name-rev.c:320
 msgid "allow to print `undefined` names"
 msgstr ""
 
-#: builtin/notes.c:26
+#: builtin/name-rev.c:326
+msgid "dereference tags in the input (internal use)"
+msgstr ""
+
+#: builtin/notes.c:24
 msgid "git notes [--ref <notes_ref>] [list [<object>]]"
 msgstr ""
 
-#: builtin/notes.c:27
+#: builtin/notes.c:25
 msgid ""
 "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
 msgstr ""
 
-#: builtin/notes.c:28
+#: builtin/notes.c:26
 msgid "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 
-#: builtin/notes.c:29
+#: builtin/notes.c:27
 msgid ""
 "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
 msgstr ""
 
-#: builtin/notes.c:30
+#: builtin/notes.c:28
 msgid "git notes [--ref <notes_ref>] edit [<object>]"
 msgstr ""
 
-#: builtin/notes.c:31
+#: builtin/notes.c:29
 msgid "git notes [--ref <notes_ref>] show [<object>]"
 msgstr ""
 
-#: builtin/notes.c:32
+#: builtin/notes.c:30
 msgid ""
 "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"
 msgstr ""
 
-#: builtin/notes.c:33
+#: builtin/notes.c:31
 msgid "git notes merge --commit [-v | -q]"
 msgstr ""
 
-#: builtin/notes.c:34
+#: builtin/notes.c:32
 msgid "git notes merge --abort [-v | -q]"
 msgstr ""
 
-#: builtin/notes.c:35
+#: builtin/notes.c:33
 msgid "git notes [--ref <notes_ref>] remove [<object>...]"
 msgstr ""
 
-#: builtin/notes.c:36
+#: builtin/notes.c:34
 msgid "git notes [--ref <notes_ref>] prune [-n | -v]"
 msgstr ""
 
-#: builtin/notes.c:37
+#: builtin/notes.c:35
 msgid "git notes [--ref <notes_ref>] get-ref"
 msgstr ""
 
-#: builtin/notes.c:42
+#: builtin/notes.c:40
 msgid "git notes [list [<object>]]"
 msgstr ""
 
-#: builtin/notes.c:47
+#: builtin/notes.c:45
 msgid "git notes add [<options>] [<object>]"
 msgstr ""
 
-#: builtin/notes.c:52
+#: builtin/notes.c:50
 msgid "git notes copy [<options>] <from-object> <to-object>"
 msgstr ""
 
-#: builtin/notes.c:53
+#: builtin/notes.c:51
 msgid "git notes copy --stdin [<from-object> <to-object>]..."
 msgstr ""
 
-#: builtin/notes.c:58
+#: builtin/notes.c:56
 msgid "git notes append [<options>] [<object>]"
 msgstr ""
 
-#: builtin/notes.c:63
+#: builtin/notes.c:61
 msgid "git notes edit [<object>]"
 msgstr ""
 
-#: builtin/notes.c:68
+#: builtin/notes.c:66
 msgid "git notes show [<object>]"
 msgstr ""
 
-#: builtin/notes.c:73
+#: builtin/notes.c:71
 msgid "git notes merge [<options>] <notes_ref>"
 msgstr ""
 
-#: builtin/notes.c:74
+#: builtin/notes.c:72
 msgid "git notes merge --commit [<options>]"
 msgstr ""
 
-#: builtin/notes.c:75
+#: builtin/notes.c:73
 msgid "git notes merge --abort [<options>]"
 msgstr ""
 
-#: builtin/notes.c:80
+#: builtin/notes.c:78
 msgid "git notes remove [<object>]"
 msgstr ""
 
-#: builtin/notes.c:85
+#: builtin/notes.c:83
 msgid "git notes prune [<options>]"
 msgstr ""
 
-#: builtin/notes.c:90
+#: builtin/notes.c:88
 msgid "git notes get-ref"
 msgstr ""
 
-#: builtin/notes.c:139
+#: builtin/notes.c:137
 #, c-format
 msgid "unable to start 'show' for object '%s'"
 msgstr ""
 
-#: builtin/notes.c:143
+#: builtin/notes.c:141
 msgid "could not read 'show' output"
 msgstr ""
 
-#: builtin/notes.c:151
+#: builtin/notes.c:149
 #, c-format
 msgid "failed to finish 'show' for object '%s'"
 msgstr ""
 
-#: builtin/notes.c:169 builtin/tag.c:341
+#: builtin/notes.c:167 builtin/tag.c:341
 #, c-format
 msgid "could not create file '%s'"
 msgstr ""
 
-#: builtin/notes.c:188
+#: builtin/notes.c:186
 msgid "Please supply the note contents using either -m or -F option"
 msgstr ""
 
-#: builtin/notes.c:209 builtin/notes.c:972
+#: builtin/notes.c:207 builtin/notes.c:844
 #, c-format
 msgid "Removing note for object %s\n"
 msgstr ""
 
-#: builtin/notes.c:214
+#: builtin/notes.c:212
 msgid "unable to write note object"
 msgstr ""
 
-#: builtin/notes.c:216
+#: builtin/notes.c:214
 #, c-format
 msgid "The note contents has been left in %s"
 msgstr ""
 
-#: builtin/notes.c:250 builtin/tag.c:540
+#: builtin/notes.c:248 builtin/tag.c:540
 #, c-format
 msgid "cannot read '%s'"
 msgstr ""
 
-#: builtin/notes.c:252 builtin/tag.c:543
+#: builtin/notes.c:250 builtin/tag.c:543
 #, c-format
 msgid "could not open or read '%s'"
 msgstr ""
 
-#: builtin/notes.c:271 builtin/notes.c:444 builtin/notes.c:446
-#: builtin/notes.c:506 builtin/notes.c:560 builtin/notes.c:643
-#: builtin/notes.c:648 builtin/notes.c:723 builtin/notes.c:765
-#: builtin/notes.c:967 builtin/tag.c:556
+#: builtin/notes.c:269 builtin/notes.c:316 builtin/notes.c:318
+#: builtin/notes.c:378 builtin/notes.c:432 builtin/notes.c:515
+#: builtin/notes.c:520 builtin/notes.c:595 builtin/notes.c:637
+#: builtin/notes.c:839 builtin/tag.c:556
 #, c-format
 msgid "Failed to resolve '%s' as a valid ref."
 msgstr ""
 
-#: builtin/notes.c:274
+#: builtin/notes.c:272
 #, c-format
 msgid "Failed to read object '%s'."
 msgstr ""
 
-#: builtin/notes.c:298
-msgid "Cannot commit uninitialized/unreferenced notes tree"
-msgstr ""
-
-#: builtin/notes.c:339
-#, c-format
-msgid "Bad notes.rewriteMode value: '%s'"
-msgstr ""
-
-#: builtin/notes.c:349
-#, 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
-#: builtin/notes.c:376
-#, c-format
-msgid "Bad %s value: '%s'"
-msgstr ""
-
-#: builtin/notes.c:440
+#: builtin/notes.c:312
 #, c-format
 msgid "Malformed input line: '%s'."
 msgstr ""
 
-#: builtin/notes.c:455
+#: builtin/notes.c:327
 #, c-format
 msgid "Failed to copy notes from '%s' to '%s'"
 msgstr ""
 
-#: builtin/notes.c:499 builtin/notes.c:553 builtin/notes.c:626
-#: builtin/notes.c:638 builtin/notes.c:711 builtin/notes.c:758
-#: builtin/notes.c:1032
+#: builtin/notes.c:371 builtin/notes.c:425 builtin/notes.c:498
+#: builtin/notes.c:510 builtin/notes.c:583 builtin/notes.c:630
+#: builtin/notes.c:904
 msgid "too many parameters"
 msgstr ""
 
-#: builtin/notes.c:512 builtin/notes.c:771
+#: builtin/notes.c:384 builtin/notes.c:643
 #, c-format
 msgid "No note found for object %s."
 msgstr ""
 
-#: builtin/notes.c:534 builtin/notes.c:691
+#: builtin/notes.c:406 builtin/notes.c:563
 msgid "note contents as a string"
 msgstr ""
 
-#: builtin/notes.c:537 builtin/notes.c:694
+#: builtin/notes.c:409 builtin/notes.c:566
 msgid "note contents in a file"
 msgstr ""
 
-#: builtin/notes.c:539 builtin/notes.c:542 builtin/notes.c:696
-#: builtin/notes.c:699 builtin/tag.c:474
+#: builtin/notes.c:411 builtin/notes.c:414 builtin/notes.c:568
+#: builtin/notes.c:571 builtin/tag.c:474
 msgid "object"
 msgstr ""
 
-#: builtin/notes.c:540 builtin/notes.c:697
+#: builtin/notes.c:412 builtin/notes.c:569
 msgid "reuse and edit specified note object"
 msgstr ""
 
-#: builtin/notes.c:543 builtin/notes.c:700
+#: builtin/notes.c:415 builtin/notes.c:572
 msgid "reuse specified note object"
 msgstr ""
 
-#: builtin/notes.c:545 builtin/notes.c:613
+#: builtin/notes.c:417 builtin/notes.c:485
 msgid "replace existing notes"
 msgstr ""
 
-#: builtin/notes.c:579
+#: builtin/notes.c:451
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
 "existing notes"
 msgstr ""
 
-#: builtin/notes.c:584 builtin/notes.c:661
+#: builtin/notes.c:456 builtin/notes.c:533
 #, c-format
 msgid "Overwriting existing notes for object %s\n"
 msgstr ""
 
-#: builtin/notes.c:614
+#: builtin/notes.c:486
 msgid "read objects from stdin"
 msgstr ""
 
-#: builtin/notes.c:616
+#: builtin/notes.c:488
 msgid "load rewriting config for <command> (implies --stdin)"
 msgstr ""
 
-#: builtin/notes.c:634
+#: builtin/notes.c:506
 msgid "too few parameters"
 msgstr ""
 
-#: builtin/notes.c:655
+#: builtin/notes.c:527
 #, c-format
 msgid ""
 "Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
 "existing notes"
 msgstr ""
 
-#: builtin/notes.c:667
+#: builtin/notes.c:539
 #, c-format
 msgid "Missing notes on source object %s. Cannot copy."
 msgstr ""
 
-#: builtin/notes.c:716
+#: builtin/notes.c:588
 #, c-format
 msgid ""
 "The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
 "Please use 'git notes add -f -m/-F/-c/-C' instead.\n"
 msgstr ""
 
-#: builtin/notes.c:863
+#: builtin/notes.c:735
 msgid "General options"
 msgstr ""
 
-#: builtin/notes.c:865
+#: builtin/notes.c:737
 msgid "Merge options"
 msgstr ""
 
-#: builtin/notes.c:867
+#: builtin/notes.c:739
 msgid ""
 "resolve notes conflicts using the given strategy (manual/ours/theirs/union/"
 "cat_sort_uniq)"
 msgstr ""
 
-#: builtin/notes.c:869
+#: builtin/notes.c:741
 msgid "Committing unmerged notes"
 msgstr ""
 
-#: builtin/notes.c:871
+#: builtin/notes.c:743
 msgid "finalize notes merge by committing unmerged notes"
 msgstr ""
 
-#: builtin/notes.c:873
+#: builtin/notes.c:745
 msgid "Aborting notes merge resolution"
 msgstr ""
 
-#: builtin/notes.c:875
+#: builtin/notes.c:747
 msgid "abort notes merge"
 msgstr ""
 
-#: builtin/notes.c:970
+#: builtin/notes.c:842
 #, c-format
 msgid "Object %s has no note\n"
 msgstr ""
 
-#: builtin/notes.c:982
+#: builtin/notes.c:854
 msgid "attempt to remove non-existent note is not an error"
 msgstr ""
 
-#: builtin/notes.c:985
+#: builtin/notes.c:857
 msgid "read object names from the standard input"
 msgstr ""
 
-#: builtin/notes.c:1066
+#: builtin/notes.c:938
 msgid "notes_ref"
 msgstr ""
 
-#: builtin/notes.c:1067
+#: builtin/notes.c:939
 msgid "use notes from <notes_ref>"
 msgstr ""
 
-#: builtin/notes.c:1102 builtin/remote.c:1598
+#: builtin/notes.c:974 builtin/remote.c:1598
 #, c-format
 msgid "Unknown subcommand: %s"
 msgstr ""
@@ -6703,7 +7196,7 @@ msgid ""
 "%s"
 msgstr ""
 
-#: builtin/push.c:121
+#: builtin/push.c:117
 #, c-format
 msgid ""
 "You are not currently on a branch.\n"
@@ -6713,7 +7206,7 @@ msgid ""
 "    git push %s HEAD:<name-of-remote-branch>\n"
 msgstr ""
 
-#: builtin/push.c:128
+#: builtin/push.c:131
 #, c-format
 msgid ""
 "The current branch %s has no upstream branch.\n"
@@ -6722,12 +7215,12 @@ msgid ""
 "    git push --set-upstream %s %s\n"
 msgstr ""
 
-#: builtin/push.c:136
+#: builtin/push.c:139
 #, c-format
 msgid "The current branch %s has multiple upstream branches, refusing to push."
 msgstr ""
 
-#: builtin/push.c:139
+#: builtin/push.c:142
 #, c-format
 msgid ""
 "You are pushing to remote '%s', which is not the upstream of\n"
@@ -6735,7 +7228,7 @@ msgid ""
 "to update which remote branch."
 msgstr ""
 
-#: builtin/push.c:151
+#: builtin/push.c:165
 msgid ""
 "push.default is unset; its implicit value is changing in\n"
 "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -6753,20 +7246,20 @@ msgid ""
 "'current' instead of 'simple' if you sometimes use older versions of Git)"
 msgstr ""
 
-#: builtin/push.c:199
+#: builtin/push.c:225
 msgid ""
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
 
-#: builtin/push.c:206
+#: builtin/push.c:232
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
-"before pushing again.\n"
+"its remote counterpart. Integrate the remote changes (e.g.\n"
+"'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 
-#: builtin/push.c:212
+#: builtin/push.c:238
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
 "counterpart. If you did not intend to push that branch, you may want to\n"
@@ -6774,50 +7267,50 @@ msgid ""
 "to 'simple', 'current' or 'upstream' to push only the current branch."
 msgstr ""
 
-#: builtin/push.c:218
+#: builtin/push.c:244
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and merge the remote changes\n"
-"(e.g. 'git pull') before pushing again.\n"
+"counterpart. Check out this branch and integrate the remote changes\n"
+"(e.g. 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 
-#: builtin/push.c:224
+#: builtin/push.c:250
 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"
-"to the same ref. You may want to first merge the remote changes (e.g.,\n"
-"'git pull') before pushing again.\n"
+"to the same ref. You may want to first integrate the remote changes\n"
+"(e.g., 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 
-#: builtin/push.c:231
+#: builtin/push.c:257
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr ""
 
-#: builtin/push.c:234
+#: builtin/push.c:260
 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:294
+#: builtin/push.c:320
 #, c-format
 msgid "Pushing to %s\n"
 msgstr ""
 
-#: builtin/push.c:298
+#: builtin/push.c:324
 #, c-format
 msgid "failed to push some refs to '%s'"
 msgstr ""
 
-#: builtin/push.c:331
+#: builtin/push.c:357
 #, c-format
 msgid "bad repository '%s'"
 msgstr ""
 
-#: builtin/push.c:332
+#: builtin/push.c:358
 msgid ""
 "No configured push destination.\n"
 "Either specify the URL from the command-line or configure a remote "
@@ -6830,83 +7323,87 @@ msgid ""
 "    git push <name>\n"
 msgstr ""
 
-#: builtin/push.c:347
+#: builtin/push.c:373
 msgid "--all and --tags are incompatible"
 msgstr ""
 
-#: builtin/push.c:348
+#: builtin/push.c:374
 msgid "--all can't be combined with refspecs"
 msgstr ""
 
-#: builtin/push.c:353
+#: builtin/push.c:379
 msgid "--mirror and --tags are incompatible"
 msgstr ""
 
-#: builtin/push.c:354
+#: builtin/push.c:380
 msgid "--mirror can't be combined with refspecs"
 msgstr ""
 
-#: builtin/push.c:359
+#: builtin/push.c:385
 msgid "--all and --mirror are incompatible"
 msgstr ""
 
-#: builtin/push.c:419
+#: builtin/push.c:445
 msgid "repository"
 msgstr ""
 
-#: builtin/push.c:420
+#: builtin/push.c:446
 msgid "push all refs"
 msgstr ""
 
-#: builtin/push.c:421
+#: builtin/push.c:447
 msgid "mirror all refs"
 msgstr ""
 
-#: builtin/push.c:423
+#: builtin/push.c:449
 msgid "delete refs"
 msgstr ""
 
-#: builtin/push.c:424
+#: builtin/push.c:450
 msgid "push tags (can't be used with --all or --mirror)"
 msgstr ""
 
-#: builtin/push.c:427
+#: builtin/push.c:453
 msgid "force updates"
 msgstr ""
 
-#: builtin/push.c:428
+#: builtin/push.c:454
 msgid "check"
 msgstr ""
 
-#: builtin/push.c:429
+#: builtin/push.c:455
 msgid "control recursive pushing of submodules"
 msgstr ""
 
-#: builtin/push.c:431
+#: builtin/push.c:457
 msgid "use thin pack"
 msgstr ""
 
-#: builtin/push.c:432 builtin/push.c:433
+#: builtin/push.c:458 builtin/push.c:459
 msgid "receive pack program"
 msgstr ""
 
-#: builtin/push.c:434
+#: builtin/push.c:460
 msgid "set upstream for git pull/status"
 msgstr ""
 
-#: builtin/push.c:437
+#: builtin/push.c:463
 msgid "prune locally removed refs"
 msgstr ""
 
-#: builtin/push.c:439
+#: builtin/push.c:465
 msgid "bypass pre-push hook"
 msgstr ""
 
-#: builtin/push.c:448
+#: builtin/push.c:466
+msgid "push missing but relevant tags"
+msgstr ""
+
+#: builtin/push.c:476
 msgid "--delete is incompatible with --all, --mirror and --tags"
 msgstr ""
 
-#: builtin/push.c:450
+#: builtin/push.c:478
 msgid "--delete doesn't make sense without any refs"
 msgstr ""
 
@@ -6917,70 +7414,80 @@ msgid ""
 "index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"
 msgstr ""
 
-#: builtin/read-tree.c:108
+#: builtin/read-tree.c:109
 msgid "write resulting index to <file>"
 msgstr ""
 
-#: builtin/read-tree.c:111
+#: builtin/read-tree.c:112
 msgid "only empty the index"
 msgstr ""
 
-#: builtin/read-tree.c:113
+#: builtin/read-tree.c:114
 msgid "Merging"
 msgstr ""
 
-#: builtin/read-tree.c:115
+#: builtin/read-tree.c:116
 msgid "perform a merge in addition to a read"
 msgstr ""
 
-#: builtin/read-tree.c:117
+#: builtin/read-tree.c:118
 msgid "3-way merge if no file level merging required"
 msgstr ""
 
-#: builtin/read-tree.c:119
+#: builtin/read-tree.c:120
 msgid "3-way merge in presence of adds and removes"
 msgstr ""
 
-#: builtin/read-tree.c:121
+#: builtin/read-tree.c:122
 msgid "same as -m, but discard unmerged entries"
 msgstr ""
 
-#: builtin/read-tree.c:122
+#: builtin/read-tree.c:123
 msgid "<subdirectory>/"
 msgstr ""
 
-#: builtin/read-tree.c:123
+#: builtin/read-tree.c:124
 msgid "read the tree into the index under <subdirectory>/"
 msgstr ""
 
-#: builtin/read-tree.c:126
+#: builtin/read-tree.c:127
 msgid "update working tree with merge result"
 msgstr ""
 
-#: builtin/read-tree.c:128
+#: builtin/read-tree.c:129
 msgid "gitignore"
 msgstr ""
 
-#: builtin/read-tree.c:129
+#: builtin/read-tree.c:130
 msgid "allow explicitly ignored files to be overwritten"
 msgstr ""
 
-#: builtin/read-tree.c:132
+#: builtin/read-tree.c:133
 msgid "don't check the working tree after merging"
 msgstr ""
 
-#: builtin/read-tree.c:133
+#: builtin/read-tree.c:134
 msgid "don't update the index or the work tree"
 msgstr ""
 
-#: builtin/read-tree.c:135
+#: builtin/read-tree.c:136
 msgid "skip applying sparse checkout filter"
 msgstr ""
 
-#: builtin/read-tree.c:137
+#: builtin/read-tree.c:138
 msgid "debug unpack-trees"
 msgstr ""
 
+#: builtin/reflog.c:500
+#, c-format
+msgid "%s' for '%s' is not a valid timestamp"
+msgstr ""
+
+#: builtin/reflog.c:616 builtin/reflog.c:621
+#, c-format
+msgid "'%s' is not a valid timestamp"
+msgstr ""
+
 #: builtin/remote.c:11
 msgid "git remote [-v | --verbose]"
 msgstr ""
@@ -7541,95 +8048,95 @@ msgstr ""
 msgid "Failed to find tree of %s."
 msgstr ""
 
-#: builtin/reset.c:98
+#: builtin/reset.c:99
 #, c-format
 msgid "HEAD is now at %s"
 msgstr ""
 
-#: builtin/reset.c:169
+#: builtin/reset.c:172
 #, c-format
 msgid "Cannot do a %s reset in the middle of a merge."
 msgstr ""
 
-#: builtin/reset.c:248
+#: builtin/reset.c:251
 msgid "be quiet, only report errors"
 msgstr ""
 
-#: builtin/reset.c:250
+#: builtin/reset.c:253
 msgid "reset HEAD and index"
 msgstr ""
 
-#: builtin/reset.c:251
+#: builtin/reset.c:254
 msgid "reset only HEAD"
 msgstr ""
 
-#: builtin/reset.c:253 builtin/reset.c:255
+#: builtin/reset.c:256 builtin/reset.c:258
 msgid "reset HEAD, index and working tree"
 msgstr ""
 
-#: builtin/reset.c:257
+#: builtin/reset.c:260
 msgid "reset HEAD but keep local changes"
 msgstr ""
 
-#: builtin/reset.c:275
+#: builtin/reset.c:278
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
 msgstr ""
 
-#: builtin/reset.c:278 builtin/reset.c:286
+#: builtin/reset.c:281 builtin/reset.c:289
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr ""
 
-#: builtin/reset.c:283
+#: builtin/reset.c:286
 #, c-format
 msgid "Failed to resolve '%s' as a valid tree."
 msgstr ""
 
-#: builtin/reset.c:292
+#: builtin/reset.c:295
 msgid "--patch is incompatible with --{hard,mixed,soft}"
 msgstr ""
 
-#: builtin/reset.c:301
+#: builtin/reset.c:304
 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
 msgstr ""
 
-#: builtin/reset.c:303
+#: builtin/reset.c:306
 #, c-format
 msgid "Cannot do %s reset with paths."
 msgstr ""
 
-#: builtin/reset.c:313
+#: builtin/reset.c:316
 #, c-format
 msgid "%s reset is not allowed in a bare repository"
 msgstr ""
 
-#: builtin/reset.c:333
+#: builtin/reset.c:336
 #, c-format
 msgid "Could not reset index file to revision '%s'."
 msgstr ""
 
-#: builtin/reset.c:339
+#: builtin/reset.c:342
 msgid "Unstaged changes after reset:"
 msgstr ""
 
-#: builtin/reset.c:344
+#: builtin/reset.c:347
 msgid "Could not write new index file."
 msgstr ""
 
-#: builtin/rev-parse.c:339
+#: builtin/rev-parse.c:345
 msgid "git rev-parse --parseopt [options] -- [<args>...]"
 msgstr ""
 
-#: builtin/rev-parse.c:344
+#: builtin/rev-parse.c:350
 msgid "keep the `--` passed as an arg"
 msgstr ""
 
-#: builtin/rev-parse.c:346
+#: builtin/rev-parse.c:352
 msgid "stop parsing after the first non-option argument"
 msgstr ""
 
-#: builtin/rev-parse.c:464
+#: builtin/rev-parse.c:470
 msgid ""
 "git rev-parse --parseopt [options] -- [<args>...]\n"
 "   or: git rev-parse --sq-quote [<arg>...]\n"
@@ -7639,7 +8146,7 @@ msgid ""
 msgstr ""
 
 #: builtin/revert.c:22
-msgid "git revert [options] <commit-ish>"
+msgid "git revert [options] <commit-ish>..."
 msgstr ""
 
 #: builtin/revert.c:23
@@ -7647,174 +8154,196 @@ msgid "git revert <subcommand>"
 msgstr ""
 
 #: builtin/revert.c:28
-msgid "git cherry-pick [options] <commit-ish>"
+msgid "git cherry-pick [options] <commit-ish>..."
 msgstr ""
 
 #: builtin/revert.c:29
 msgid "git cherry-pick <subcommand>"
 msgstr ""
 
-#: builtin/revert.c:70 builtin/revert.c:92
+#: builtin/revert.c:71 builtin/revert.c:94
 #, c-format
 msgid "%s: %s cannot be used with %s"
 msgstr ""
 
-#: builtin/revert.c:103
+#: builtin/revert.c:105
 msgid "end revert or cherry-pick sequence"
 msgstr ""
 
-#: builtin/revert.c:104
+#: builtin/revert.c:106
 msgid "resume revert or cherry-pick sequence"
 msgstr ""
 
-#: builtin/revert.c:105
+#: builtin/revert.c:107
 msgid "cancel revert or cherry-pick sequence"
 msgstr ""
 
-#: builtin/revert.c:106
+#: builtin/revert.c:108
 msgid "don't automatically commit"
 msgstr ""
 
-#: builtin/revert.c:107
+#: builtin/revert.c:109
 msgid "edit the commit message"
 msgstr ""
 
-#: builtin/revert.c:110
+#: builtin/revert.c:112
 msgid "parent number"
 msgstr ""
 
-#: builtin/revert.c:112
+#: builtin/revert.c:114
 msgid "merge strategy"
 msgstr ""
 
-#: builtin/revert.c:113
+#: builtin/revert.c:115
 msgid "option"
 msgstr ""
 
-#: builtin/revert.c:114
+#: builtin/revert.c:116
 msgid "option for merge strategy"
 msgstr ""
 
-#: builtin/revert.c:125
+#: builtin/revert.c:127
 msgid "append commit name"
 msgstr ""
 
-#: builtin/revert.c:126
+#: builtin/revert.c:128
 msgid "allow fast-forward"
 msgstr ""
 
-#: builtin/revert.c:127
+#: builtin/revert.c:129
 msgid "preserve initially empty commits"
 msgstr ""
 
-#: builtin/revert.c:128
+#: builtin/revert.c:130
 msgid "allow commits with empty messages"
 msgstr ""
 
-#: builtin/revert.c:129
+#: builtin/revert.c:131
 msgid "keep redundant, empty commits"
 msgstr ""
 
-#: builtin/revert.c:133
+#: builtin/revert.c:135
 msgid "program error"
 msgstr ""
 
-#: builtin/revert.c:223
+#: builtin/revert.c:225
 msgid "revert failed"
 msgstr ""
 
-#: builtin/revert.c:238
+#: builtin/revert.c:240
 msgid "cherry-pick failed"
 msgstr ""
 
-#: builtin/rm.c:15
+#: builtin/rm.c:16
 msgid "git rm [options] [--] <file>..."
 msgstr ""
 
-#: builtin/rm.c:64 builtin/rm.c:186
-#, c-format
+#: builtin/rm.c:64
+msgid ""
+"the following submodule (or one of its nested submodules)\n"
+"uses a .git directory:"
+msgid_plural ""
+"the following submodules (or one of its nested submodules)\n"
+"use a .git directory:"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/rm.c:70
 msgid ""
-"submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+"\n"
 "(use 'rm -rf' if you really want to remove it including all of its history)"
 msgstr ""
 
-#: builtin/rm.c:174
-#, c-format
+#: builtin/rm.c:230
 msgid ""
-"'%s' has staged content different from both the file and the HEAD\n"
-"(use -f to force removal)"
-msgstr ""
+"the following file has staged content different from both the\n"
+"file and the HEAD:"
+msgid_plural ""
+"the following files have staged content different from both the\n"
+"file and the HEAD:"
+msgstr[0] ""
+msgstr[1] ""
 
-#: builtin/rm.c:180
-#, c-format
+#: builtin/rm.c:235
 msgid ""
-"'%s' has changes staged in the index\n"
-"(use --cached to keep the file, or -f to force removal)"
+"\n"
+"(use -f to force removal)"
 msgstr ""
 
-#: builtin/rm.c:191
-#, c-format
+#: builtin/rm.c:239
+msgid "the following file has changes staged in the index:"
+msgid_plural "the following files have changes staged in the index:"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/rm.c:243 builtin/rm.c:254
 msgid ""
-"'%s' has local modifications\n"
+"\n"
 "(use --cached to keep the file, or -f to force removal)"
 msgstr ""
 
-#: builtin/rm.c:207
+#: builtin/rm.c:251
+msgid "the following file has local modifications:"
+msgid_plural "the following files have local modifications:"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/rm.c:269
 msgid "do not list removed files"
 msgstr ""
 
-#: builtin/rm.c:208
+#: builtin/rm.c:270
 msgid "only remove from the index"
 msgstr ""
 
-#: builtin/rm.c:209
+#: builtin/rm.c:271
 msgid "override the up-to-date check"
 msgstr ""
 
-#: builtin/rm.c:210
+#: builtin/rm.c:272
 msgid "allow recursive removal"
 msgstr ""
 
-#: builtin/rm.c:212
+#: builtin/rm.c:274
 msgid "exit with a zero status even if nothing matched"
 msgstr ""
 
-#: builtin/rm.c:283
+#: builtin/rm.c:345
 #, c-format
 msgid "not removing '%s' recursively without -r"
 msgstr ""
 
-#: builtin/rm.c:322
+#: builtin/rm.c:384
 #, c-format
 msgid "git rm: unable to remove %s"
 msgstr ""
 
 #: builtin/shortlog.c:13
-msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
+msgid "git shortlog [<options>] [<revision range>] [[--] [<path>...]]"
 msgstr ""
 
-#: builtin/shortlog.c:133
+#: builtin/shortlog.c:131
 #, c-format
 msgid "Missing author: %s"
 msgstr ""
 
-#: builtin/shortlog.c:229
+#: builtin/shortlog.c:228
 msgid "sort output according to the number of commits per author"
 msgstr ""
 
-#: builtin/shortlog.c:231
+#: builtin/shortlog.c:230
 msgid "Suppress commit descriptions, only provides commit count"
 msgstr ""
 
-#: builtin/shortlog.c:233
+#: builtin/shortlog.c:232
 msgid "Show the email address of each author"
 msgstr ""
 
-#: builtin/shortlog.c:234
+#: builtin/shortlog.c:233
 msgid "w[,i1[,i2]]"
 msgstr ""
 
-#: builtin/shortlog.c:235
+#: builtin/shortlog.c:234
 msgid "Linewrap output"
 msgstr ""
 
@@ -7830,47 +8359,47 @@ msgstr ""
 msgid "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr ""
 
-#: builtin/show-branch.c:651
+#: builtin/show-branch.c:650
 msgid "show remote-tracking and local branches"
 msgstr ""
 
-#: builtin/show-branch.c:653
+#: builtin/show-branch.c:652
 msgid "show remote-tracking branches"
 msgstr ""
 
-#: builtin/show-branch.c:655
+#: builtin/show-branch.c:654
 msgid "color '*!+-' corresponding to the branch"
 msgstr ""
 
-#: builtin/show-branch.c:657
+#: builtin/show-branch.c:656
 msgid "show <n> more commits after the common ancestor"
 msgstr ""
 
-#: builtin/show-branch.c:659
+#: builtin/show-branch.c:658
 msgid "synonym to more=-1"
 msgstr ""
 
-#: builtin/show-branch.c:660
+#: builtin/show-branch.c:659
 msgid "suppress naming strings"
 msgstr ""
 
-#: builtin/show-branch.c:662
+#: builtin/show-branch.c:661
 msgid "include the current branch"
 msgstr ""
 
-#: builtin/show-branch.c:664
+#: builtin/show-branch.c:663
 msgid "name commits with their object names"
 msgstr ""
 
-#: builtin/show-branch.c:666
+#: builtin/show-branch.c:665
 msgid "show possible merge bases"
 msgstr ""
 
-#: builtin/show-branch.c:668
+#: builtin/show-branch.c:667
 msgid "show refs unreachable from any other ref"
 msgstr ""
 
-#: builtin/show-branch.c:670
+#: builtin/show-branch.c:669
 msgid "show commits in topological order"
 msgstr ""
 
@@ -7883,14 +8412,14 @@ msgid "show merges reachable from only one tip"
 msgstr ""
 
 #: builtin/show-branch.c:676
-msgid "show commits where no parent comes before its children"
+msgid "topologically sort, maintaining date order where possible"
 msgstr ""
 
-#: builtin/show-branch.c:678
+#: builtin/show-branch.c:679
 msgid "<n>[,<base>]"
 msgstr ""
 
-#: builtin/show-branch.c:679
+#: builtin/show-branch.c:680
 msgid "show <n> most recent ref-log entries starting at base"
 msgstr ""
 
@@ -7904,35 +8433,35 @@ msgstr ""
 msgid "git show-ref --exclude-existing[=pattern] < ref-list"
 msgstr ""
 
-#: builtin/show-ref.c:165
+#: builtin/show-ref.c:168
 msgid "only show tags (can be combined with heads)"
 msgstr ""
 
-#: builtin/show-ref.c:166
+#: builtin/show-ref.c:169
 msgid "only show heads (can be combined with tags)"
 msgstr ""
 
-#: builtin/show-ref.c:167
+#: builtin/show-ref.c:170
 msgid "stricter reference checking, requires exact ref path"
 msgstr ""
 
-#: builtin/show-ref.c:170 builtin/show-ref.c:172
-msgid "show the HEAD reference"
+#: builtin/show-ref.c:173 builtin/show-ref.c:176
+msgid "show the HEAD reference, even if it would be filtered out"
 msgstr ""
 
-#: builtin/show-ref.c:174
+#: builtin/show-ref.c:178
 msgid "dereference tags into object IDs"
 msgstr ""
 
-#: builtin/show-ref.c:176
+#: builtin/show-ref.c:180
 msgid "only show SHA1 hash using <n> digits"
 msgstr ""
 
-#: builtin/show-ref.c:180
+#: builtin/show-ref.c:184
 msgid "do not print results to stdout (useful with --verify)"
 msgstr ""
 
-#: builtin/show-ref.c:182
+#: builtin/show-ref.c:186
 msgid "show refs from stdin that aren't in local repository"
 msgstr ""
 
@@ -8336,23 +8865,30 @@ msgstr ""
 msgid "only useful for debugging"
 msgstr ""
 
-#: git.c:16
-msgid "See 'git help <command>' for more information on a specific command."
+#: git.c:17
+msgid ""
+"'git help -a' and 'git help -g' lists available subcommands and some\n"
+"concept guides. See 'git help <command>' or 'git help <concept>'\n"
+"to read about a specific subcommand or concept."
+msgstr ""
+
+#: parse-options.h:144
+msgid "expiry date"
 msgstr ""
 
-#: parse-options.h:156
+#: parse-options.h:159
 msgid "no-op (backward compatibility)"
 msgstr ""
 
-#: parse-options.h:232
+#: parse-options.h:236
 msgid "be more verbose"
 msgstr ""
 
-#: parse-options.h:234
+#: parse-options.h:238
 msgid "be more quiet"
 msgstr ""
 
-#: parse-options.h:240
+#: parse-options.h:244
 msgid "use <n> digits to display SHA-1s"
 msgstr ""
 
@@ -8409,7 +8945,7 @@ msgid "Move or rename a file, a directory, or a symlink"
 msgstr ""
 
 #: common-cmds.h:21
-msgid "Fetch from and merge with another repository or a local branch"
+msgid "Fetch from and integrate with another repository or a local branch"
 msgstr ""
 
 #: common-cmds.h:22
@@ -8453,7 +8989,7 @@ msgstr ""
 #: git-am.sh:105
 #, sh-format
 msgid ""
-"When you have resolved this problem, run \"$cmdline --resolved\".\n"
+"When you have resolved this problem, run \"$cmdline --continue\".\n"
 "If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n"
 "To restore the original branch and stop patching, run \"$cmdline --abort\"."
 msgstr ""
@@ -8503,25 +9039,32 @@ msgid ""
 "it will be removed. Please do not use it anymore."
 msgstr ""
 
-#: git-am.sh:477
+#: git-am.sh:479
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
 msgstr ""
 
-#: git-am.sh:482
+#: git-am.sh:484
 msgid "Please make up your mind. --skip or --abort?"
 msgstr ""
 
-#: git-am.sh:509
+#: git-am.sh:520
+#, sh-format
+msgid ""
+"Stray $dotest directory found.\n"
+"Use \"git am --abort\" to remove it."
+msgstr ""
+
+#: git-am.sh:528
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr ""
 
-#: git-am.sh:575
+#: git-am.sh:594
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
 msgstr ""
 
-#: git-am.sh:679
+#: git-am.sh:698
 #, sh-format
 msgid ""
 "Patch is empty.  Was it split wrong?\n"
@@ -8529,60 +9072,60 @@ msgid ""
 "To restore the original branch and stop patching run \"$cmdline --abort\"."
 msgstr ""
 
-#: git-am.sh:706
+#: git-am.sh:725
 msgid "Patch does not have a valid e-mail address."
 msgstr ""
 
-#: git-am.sh:753
+#: git-am.sh:772
 msgid "cannot be interactive without stdin connected to a terminal."
 msgstr ""
 
-#: git-am.sh:757
+#: git-am.sh:776
 msgid "Commit Body is:"
 msgstr ""
 
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
-#: git-am.sh:764
+#: git-am.sh:783
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 msgstr ""
 
-#: git-am.sh:800
+#: git-am.sh:819
 #, sh-format
 msgid "Applying: $FIRSTLINE"
 msgstr ""
 
-#: git-am.sh:821
+#: git-am.sh:840
 msgid ""
 "No changes - did you forget to use 'git add'?\n"
 "If there is nothing left to stage, chances are that something else\n"
 "already introduced the same changes; you might want to skip this patch."
 msgstr ""
 
-#: git-am.sh:829
+#: git-am.sh:848
 msgid ""
 "You still have unmerged paths in your index\n"
 "did you forget to use 'git add'?"
 msgstr ""
 
-#: git-am.sh:845
+#: git-am.sh:864
 msgid "No changes -- Patch already applied."
 msgstr ""
 
-#: git-am.sh:855
+#: git-am.sh:874
 #, sh-format
 msgid "Patch failed at $msgnum $FIRSTLINE"
 msgstr ""
 
-#: git-am.sh:858
+#: git-am.sh:877
 #, sh-format
 msgid ""
 "The copy of the patch that failed is found in:\n"
 "   $dotest/patch"
 msgstr ""
 
-#: git-am.sh:876
+#: git-am.sh:895
 msgid "applying to an empty history"
 msgstr ""
 
@@ -8674,59 +9217,59 @@ msgid ""
 "(You can use \"git bisect bad\" and \"git bisect good\" for that.)"
 msgstr ""
 
-#: git-bisect.sh:347 git-bisect.sh:474
+#: git-bisect.sh:363 git-bisect.sh:490
 msgid "We are not bisecting."
 msgstr ""
 
-#: git-bisect.sh:354
+#: git-bisect.sh:370
 #, sh-format
 msgid "'$invalid' is not a valid commit"
 msgstr ""
 
-#: git-bisect.sh:363
+#: git-bisect.sh:379
 #, sh-format
 msgid ""
 "Could not check out original HEAD '$branch'.\n"
 "Try 'git bisect reset <commit>'."
 msgstr ""
 
-#: git-bisect.sh:390
+#: git-bisect.sh:406
 msgid "No logfile given"
 msgstr ""
 
-#: git-bisect.sh:391
+#: git-bisect.sh:407
 #, sh-format
 msgid "cannot read $file for replaying"
 msgstr ""
 
-#: git-bisect.sh:408
+#: git-bisect.sh:424
 msgid "?? what are you talking about?"
 msgstr ""
 
-#: git-bisect.sh:420
+#: git-bisect.sh:436
 #, sh-format
 msgid "running $command"
 msgstr ""
 
-#: git-bisect.sh:427
+#: git-bisect.sh:443
 #, sh-format
 msgid ""
 "bisect run failed:\n"
 "exit code $res from '$command' is < 0 or >= 128"
 msgstr ""
 
-#: git-bisect.sh:453
+#: git-bisect.sh:469
 msgid "bisect run cannot continue any more"
 msgstr ""
 
-#: git-bisect.sh:459
+#: git-bisect.sh:475
 #, sh-format
 msgid ""
 "bisect run failed:\n"
 "'bisect_state $state' exited with error code $res"
 msgstr ""
 
-#: git-bisect.sh:466
+#: git-bisect.sh:482
 msgid "bisect run success"
 msgstr ""
 
@@ -8741,7 +9284,7 @@ msgstr ""
 msgid "Pull is not possible because you have unmerged files."
 msgstr ""
 
-#: git-pull.sh:197
+#: git-pull.sh:203
 msgid "updating an unborn branch with changes added to the index"
 msgstr ""
 
@@ -8749,7 +9292,7 @@ msgstr ""
 #. The working tree and the index file is still based on the
 #. $orig_head commit, but we are merging into $curr_head.
 #. First update the working tree to match $curr_head.
-#: git-pull.sh:229
+#: git-pull.sh:235
 #, sh-format
 msgid ""
 "Warning: fetch updated the current branch head.\n"
@@ -8757,15 +9300,15 @@ msgid ""
 "Warning: commit $orig_head."
 msgstr ""
 
-#: git-pull.sh:254
+#: git-pull.sh:260
 msgid "Cannot merge multiple branches into empty head"
 msgstr ""
 
-#: git-pull.sh:258
+#: git-pull.sh:264
 msgid "Cannot rebase onto multiple branches"
 msgstr ""
 
-#: git-rebase.sh:53
+#: git-rebase.sh:54
 msgid ""
 "When you have resolved this problem, run \"git rebase --continue\".\n"
 "If you prefer to skip this patch, run \"git rebase --skip\" instead.\n"
@@ -8773,42 +9316,58 @@ msgid ""
 "\"."
 msgstr ""
 
+#: git-rebase.sh:156
+msgid "Applied autostash."
+msgstr ""
+
+#: git-rebase.sh:159
+#, sh-format
+msgid "Cannot store $stash_sha1"
+msgstr ""
+
 #: git-rebase.sh:160
+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:190
 msgid "The pre-rebase hook refused to rebase."
 msgstr ""
 
-#: git-rebase.sh:165
+#: git-rebase.sh:195
 msgid "It looks like git-am is in progress. Cannot rebase."
 msgstr ""
 
-#: git-rebase.sh:296
+#: git-rebase.sh:329
 msgid "The --exec option must be used with the --interactive option"
 msgstr ""
 
-#: git-rebase.sh:301
+#: git-rebase.sh:334
 msgid "No rebase in progress?"
 msgstr ""
 
-#: git-rebase.sh:312
+#: git-rebase.sh:345
 msgid "The --edit-todo action can only be used during interactive rebase."
 msgstr ""
 
-#: git-rebase.sh:319
+#: git-rebase.sh:352
 msgid "Cannot read HEAD"
 msgstr ""
 
-#: git-rebase.sh:322
+#: git-rebase.sh:355
 msgid ""
 "You must edit all merge conflicts and then\n"
 "mark them as resolved using git add"
 msgstr ""
 
-#: git-rebase.sh:340
+#: git-rebase.sh:373
 #, sh-format
 msgid "Could not move back to $head_name"
 msgstr ""
 
-#: git-rebase.sh:359
+#: git-rebase.sh:392
 #, sh-format
 msgid ""
 "It seems that there is already a $state_dir_base directory, and\n"
@@ -8821,56 +9380,65 @@ msgid ""
 "valuable there."
 msgstr ""
 
-#: git-rebase.sh:404
+#: git-rebase.sh:437
 #, sh-format
 msgid "invalid upstream $upstream_name"
 msgstr ""
 
-#: git-rebase.sh:428
+#: git-rebase.sh:461
 #, sh-format
 msgid "$onto_name: there are more than one merge bases"
 msgstr ""
 
-#: git-rebase.sh:431 git-rebase.sh:435
+#: git-rebase.sh:464 git-rebase.sh:468
 #, sh-format
 msgid "$onto_name: there is no merge base"
 msgstr ""
 
-#: git-rebase.sh:440
+#: git-rebase.sh:473
 #, sh-format
 msgid "Does not point to a valid commit: $onto_name"
 msgstr ""
 
-#: git-rebase.sh:463
+#: git-rebase.sh:496
 #, sh-format
 msgid "fatal: no such branch: $branch_name"
 msgstr ""
 
-#: git-rebase.sh:483
+#: git-rebase.sh:519
+msgid "Cannot autostash"
+msgstr ""
+
+#: git-rebase.sh:524
+#, sh-format
+msgid "Created autostash: $stash_abbrev"
+msgstr ""
+
+#: git-rebase.sh:528
 msgid "Please commit or stash them."
 msgstr ""
 
-#: git-rebase.sh:501
+#: git-rebase.sh:548
 #, sh-format
 msgid "Current branch $branch_name is up to date."
 msgstr ""
 
-#: git-rebase.sh:504
+#: git-rebase.sh:552
 #, sh-format
 msgid "Current branch $branch_name is up to date, rebase forced."
 msgstr ""
 
-#: git-rebase.sh:515
+#: git-rebase.sh:563
 #, sh-format
 msgid "Changes from $mb to $onto:"
 msgstr ""
 
 #. Detach HEAD and reset the tree
-#: git-rebase.sh:524
+#: git-rebase.sh:572
 msgid "First, rewinding head to replay your work on top of it..."
 msgstr ""
 
-#: git-rebase.sh:532
+#: git-rebase.sh:582
 #, sh-format
 msgid "Fast-forwarded $branch_name to $onto_name."
 msgstr ""
@@ -8903,6 +9471,11 @@ msgstr ""
 msgid "Cannot record working tree state"
 msgstr ""
 
+#: git-stash.sh:190
+#, sh-format
+msgid "Cannot update $ref_stash with $w_commit"
+msgstr ""
+
 #. TRANSLATORS: $option is an invalid option, like
 #. `--blah-blah'. The 7 spaces at the beginning of the
 #. second line correspond to "error: ". So you should line
@@ -8913,126 +9486,141 @@ 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:202
+#: git-stash.sh:241
 #, sh-format
 msgid ""
 "error: unknown option for 'stash save': $option\n"
 "       To provide a message, use git stash save -- '$option'"
 msgstr ""
 
-#: git-stash.sh:223
+#: git-stash.sh:262
 msgid "No local changes to save"
 msgstr ""
 
-#: git-stash.sh:227
+#: git-stash.sh:268
+msgid ""
+"The following untracked files would NOT be saved but need to be removed by "
+"stash save:"
+msgstr ""
+
+#: git-stash.sh:270
+msgid ""
+"Aborting. Consider using either the --force or --include-untracked option."
+msgstr ""
+
+#: git-stash.sh:274
 msgid "Cannot initialize stash"
 msgstr ""
 
-#: git-stash.sh:235
+#: git-stash.sh:278
 msgid "Cannot save the current status"
 msgstr ""
 
-#: git-stash.sh:253
+#: git-stash.sh:296
 msgid "Cannot remove worktree changes"
 msgstr ""
 
-#: git-stash.sh:352
+#: git-stash.sh:395
 msgid "No stash found."
 msgstr ""
 
-#: git-stash.sh:359
+#: git-stash.sh:402
 #, sh-format
 msgid "Too many revisions specified: $REV"
 msgstr ""
 
-#: git-stash.sh:365
+#: git-stash.sh:408
 #, sh-format
 msgid "$reference is not valid reference"
 msgstr ""
 
-#: git-stash.sh:393
+#: git-stash.sh:436
 #, sh-format
 msgid "'$args' is not a stash-like commit"
 msgstr ""
 
-#: git-stash.sh:404
+#: git-stash.sh:447
 #, sh-format
 msgid "'$args' is not a stash reference"
 msgstr ""
 
-#: git-stash.sh:412
+#: git-stash.sh:455
 msgid "unable to refresh index"
 msgstr ""
 
-#: git-stash.sh:416
+#: git-stash.sh:459
 msgid "Cannot apply a stash in the middle of a merge"
 msgstr ""
 
-#: git-stash.sh:424
+#: git-stash.sh:467
 msgid "Conflicts in index. Try without --index."
 msgstr ""
 
-#: git-stash.sh:426
+#: git-stash.sh:469
 msgid "Could not save index tree"
 msgstr ""
 
-#: git-stash.sh:460
+#: git-stash.sh:503
 msgid "Cannot unstage modified files"
 msgstr ""
 
-#: git-stash.sh:475
+#: git-stash.sh:518
 msgid "Index was not unstashed."
 msgstr ""
 
-#: git-stash.sh:492
+#: git-stash.sh:535
 #, sh-format
 msgid "Dropped ${REV} ($s)"
 msgstr ""
 
-#: git-stash.sh:493
+#: git-stash.sh:536
 #, sh-format
 msgid "${REV}: Could not drop stash entry"
 msgstr ""
 
-#: git-stash.sh:500
+#: git-stash.sh:543
 msgid "No branch name specified"
 msgstr ""
 
-#: git-stash.sh:571
+#: git-stash.sh:615
 msgid "(To restore them type \"git stash apply\")"
 msgstr ""
 
-#: git-submodule.sh:90
+#: git-submodule.sh:95
 #, sh-format
 msgid "cannot strip one component off url '$remoteurl'"
 msgstr ""
 
-#: git-submodule.sh:195
+#: git-submodule.sh:237
 #, sh-format
 msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:238
+#: git-submodule.sh:281
 #, sh-format
 msgid "Clone of '$url' into submodule path '$sm_path' failed"
 msgstr ""
 
-#: git-submodule.sh:250
+#: git-submodule.sh:293
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
 msgstr ""
 
-#: git-submodule.sh:343
+#: git-submodule.sh:403
+msgid "Relative path can only be used from the toplevel of the working tree"
+msgstr ""
+
+#: git-submodule.sh:413
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
 msgstr ""
 
-#: git-submodule.sh:360
+#: git-submodule.sh:430
 #, sh-format
 msgid "'$sm_path' already exists in the index"
 msgstr ""
 
-#: git-submodule.sh:364
+#: git-submodule.sh:434
 #, sh-format
 msgid ""
 "The following path is ignored by one of your .gitignore files:\n"
@@ -9040,180 +9628,244 @@ msgid ""
 "Use -f if you really want to add it."
 msgstr ""
 
-#: git-submodule.sh:382
+#: git-submodule.sh:452
 #, sh-format
 msgid "Adding existing repo at '$sm_path' to the index"
 msgstr ""
 
-#: git-submodule.sh:384
+#: git-submodule.sh:454
 #, sh-format
 msgid "'$sm_path' already exists and is not a valid git repo"
 msgstr ""
 
-#: git-submodule.sh:392
+#: git-submodule.sh:462
 #, sh-format
 msgid "A git directory for '$sm_name' is found locally with remote(s):"
 msgstr ""
 
-#: git-submodule.sh:394
+#: git-submodule.sh:464
 #, sh-format
 msgid ""
 "If you want to reuse this local git directory instead of cloning again from"
 msgstr ""
 
-#: git-submodule.sh:396
+#: git-submodule.sh:466
 #, sh-format
 msgid ""
 "use the '--force' option. If the local git directory is not the correct repo"
 msgstr ""
 
-#: git-submodule.sh:397
+#: git-submodule.sh:467
 #, sh-format
 msgid ""
 "or you are unsure what this means choose another name with the '--name' "
 "option."
 msgstr ""
 
-#: git-submodule.sh:399
+#: git-submodule.sh:469
 #, sh-format
 msgid "Reactivating local git directory for submodule '$sm_name'."
 msgstr ""
 
-#: git-submodule.sh:411
+#: git-submodule.sh:481
 #, sh-format
 msgid "Unable to checkout submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:416
+#: git-submodule.sh:486
 #, sh-format
 msgid "Failed to add submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:425
+#: git-submodule.sh:495
 #, sh-format
 msgid "Failed to register submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:468
+#: git-submodule.sh:539
+#, sh-format
+msgid "Entering '$prefix$displaypath'"
+msgstr ""
+
+#: git-submodule.sh:554
+#, sh-format
+msgid "Stopping at '$prefix$displaypath'; script returned non-zero status."
+msgstr ""
+
+#: git-submodule.sh:600
+#, sh-format
+msgid "No url found for submodule path '$displaypath' in .gitmodules"
+msgstr ""
+
+#: git-submodule.sh:609
+#, sh-format
+msgid "Failed to register url for submodule path '$displaypath'"
+msgstr ""
+
+#: git-submodule.sh:611
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$displaypath'"
+msgstr ""
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Failed to register update mode for submodule path '$displaypath'"
+msgstr ""
+
+#: git-submodule.sh:656
 #, sh-format
-msgid "Entering '$prefix$sm_path'"
+msgid "Use '.' if you really want to deinitialize all submodules"
+msgstr ""
+
+#: git-submodule.sh:673
+#, sh-format
+msgid "Submodule work tree '$displaypath' contains a .git directory"
+msgstr ""
+
+#: git-submodule.sh:674
+#, sh-format
+msgid ""
+"(use 'rm -rf' if you really want to remove it including all of its history)"
 msgstr ""
 
-#: git-submodule.sh:482
+#: git-submodule.sh:680
 #, sh-format
-msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgid ""
+"Submodule work tree '$displaypath' contains local modifications; use '-f' to "
+"discard them"
 msgstr ""
 
-#: git-submodule.sh:526
+#: git-submodule.sh:683
 #, sh-format
-msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgid "Cleared directory '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:535
+#: git-submodule.sh:684
 #, sh-format
-msgid "Failed to register url for submodule path '$sm_path'"
+msgid "Could not remove submodule work tree '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:537
+#: git-submodule.sh:687
 #, sh-format
-msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgid "Could not create empty submodule directory '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:545
+#: git-submodule.sh:696
 #, sh-format
-msgid "Failed to register update mode for submodule path '$sm_path'"
+msgid "Submodule '$name' ($url) unregistered for path '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:649
+#: git-submodule.sh:811
 #, sh-format
 msgid ""
-"Submodule path '$sm_path' not initialized\n"
+"Submodule path '$displaypath' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
 
-#: git-submodule.sh:662
+#: git-submodule.sh:824
 #, sh-format
-msgid "Unable to find current revision in submodule path '$sm_path'"
+msgid "Unable to find current revision in submodule path '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:671 git-submodule.sh:695
+#: git-submodule.sh:833
 #, sh-format
 msgid "Unable to fetch in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:709
+#: git-submodule.sh:857
 #, sh-format
-msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgid "Unable to fetch in submodule path '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:710
+#: git-submodule.sh:871
 #, sh-format
-msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgid "Unable to rebase '$sha1' in submodule path '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:715
+#: git-submodule.sh:872
 #, sh-format
-msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgid "Submodule path '$displaypath': rebased into '$sha1'"
 msgstr ""
 
-#: git-submodule.sh:716
+#: git-submodule.sh:877
 #, sh-format
-msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgid "Unable to merge '$sha1' in submodule path '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:721
+#: git-submodule.sh:878
 #, sh-format
-msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgid "Submodule path '$displaypath': merged in '$sha1'"
 msgstr ""
 
-#: git-submodule.sh:722
+#: git-submodule.sh:883
 #, sh-format
-msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgid ""
+"Execution of '$command $sha1' failed in submodule  path '$prefix$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:744 git-submodule.sh:1066
+#: git-submodule.sh:884
 #, sh-format
-msgid "Failed to recurse into submodule path '$sm_path'"
+msgid "Submodule path '$prefix$sm_path': '$command $sha1'"
+msgstr ""
+
+#: git-submodule.sh:889
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$displaypath'"
+msgstr ""
+
+#: git-submodule.sh:890
+#, sh-format
+msgid "Submodule path '$displaypath': checked out '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:917
+#, sh-format
+msgid "Failed to recurse into submodule path '$displaypath'"
 msgstr ""
 
-#: git-submodule.sh:852
+#: git-submodule.sh:1025
 msgid "The --cached option cannot be used with the --files option"
 msgstr ""
 
 #. unexpected type
-#: git-submodule.sh:892
+#: git-submodule.sh:1066
 #, sh-format
 msgid "unexpected mode $mod_dst"
 msgstr ""
 
-#: git-submodule.sh:910
+#: git-submodule.sh:1086
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgid "  Warn: $display_name doesn't contain commit $sha1_src"
 msgstr ""
 
-#: git-submodule.sh:913
+#: git-submodule.sh:1089
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commit $sha1_dst"
 msgstr ""
 
-#: git-submodule.sh:916
+#: git-submodule.sh:1092
 #, sh-format
-msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commits $sha1_src and $sha1_dst"
 msgstr ""
 
-#: git-submodule.sh:941
+#: git-submodule.sh:1117
 msgid "blob"
 msgstr ""
 
-#: git-submodule.sh:979
+#: git-submodule.sh:1155
 msgid "Submodules changed but not updated:"
 msgstr ""
 
-#: git-submodule.sh:981
+#: git-submodule.sh:1157
 msgid "Submodule changes to be committed:"
 msgstr ""
 
-#: git-submodule.sh:1129
+#: git-submodule.sh:1242
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr ""
+
+#: git-submodule.sh:1306
 #, sh-format
-msgid "Synchronizing submodule url for '$prefix$sm_path'"
+msgid "Synchronizing submodule url for '$displaypath'"
 msgstr ""
index 15f1aa82a08ee6aab7263f57adb38bc38a01dd67..12dfca92f4a829247ef1e62f05cd43915b911e7b 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -5,10 +5,10 @@
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: git 1.8.2\n"
+"Project-Id-Version: git 1.8.4\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2013-03-05 12:36+0800\n"
-"PO-Revision-Date: 2013-03-05 09:17+0100\n"
+"POT-Creation-Date: 2013-08-06 14:10+0800\n"
+"PO-Revision-Date: 2013-08-06 12:37+0100\n"
 "Last-Translator: Peter Krefting <peter@softwolves.pp.se>\n"
 "Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
 "Language: sv\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: advice.c:49
+#: advice.c:57
 #, c-format
 msgid "hint: %.*s\n"
 msgstr "tips: %.*s\n"
@@ -26,7 +26,7 @@ msgstr "tips: %.*s\n"
 #. * Message used both when 'git commit' fails and when
 #. * other commands doing a merge do.
 #.
-#: advice.c:79
+#: advice.c:87
 msgid ""
 "Fix them up in the work tree,\n"
 "and then use 'git add/rm <file>' as\n"
@@ -65,7 +65,7 @@ msgstr "fmt"
 msgid "archive format"
 msgstr "arkivformat"
 
-#: archive.c:324 builtin/log.c:1115
+#: archive.c:324 builtin/log.c:1173
 msgid "prefix"
 msgstr "prefix"
 
@@ -73,15 +73,15 @@ msgstr "prefix"
 msgid "prepend prefix to each pathname in the archive"
 msgstr "lägg till prefix till varje sökväg i arkivet"
 
-#: archive.c:326 builtin/archive.c:91 builtin/blame.c:2366
-#: builtin/blame.c:2367 builtin/config.c:55 builtin/fast-export.c:653
-#: builtin/fast-export.c:655 builtin/grep.c:715 builtin/hash-object.c:77
-#: builtin/ls-files.c:497 builtin/ls-files.c:500 builtin/notes.c:536
-#: builtin/notes.c:693 builtin/read-tree.c:107 parse-options.h:149
+#: archive.c:326 builtin/archive.c:88 builtin/blame.c:2292
+#: builtin/blame.c:2293 builtin/config.c:56 builtin/fast-export.c:673
+#: builtin/fast-export.c:675 builtin/grep.c:714 builtin/hash-object.c:77
+#: builtin/ls-files.c:496 builtin/ls-files.c:499 builtin/notes.c:408
+#: builtin/notes.c:565 builtin/read-tree.c:108 parse-options.h:152
 msgid "file"
 msgstr "fil"
 
-#: archive.c:327 builtin/archive.c:92
+#: archive.c:327 builtin/archive.c:89
 msgid "write the archive to this file"
 msgstr "skriv arkivet till filen"
 
@@ -109,19 +109,19 @@ msgstr "komprimera bättre"
 msgid "list supported archive formats"
 msgstr "visa understödda arkivformat"
 
-#: archive.c:345 builtin/archive.c:93 builtin/clone.c:85
+#: archive.c:345 builtin/archive.c:90 builtin/clone.c:85
 msgid "repo"
 msgstr "arkiv"
 
-#: archive.c:346 builtin/archive.c:94
+#: archive.c:346 builtin/archive.c:91
 msgid "retrieve the archive from remote repository <repo>"
 msgstr "hämta arkivet från fjärrarkivet <arkiv>"
 
-#: archive.c:347 builtin/archive.c:95 builtin/notes.c:615
+#: archive.c:347 builtin/archive.c:92 builtin/notes.c:487
 msgid "command"
 msgstr "kommando"
 
-#: archive.c:348 builtin/archive.c:96
+#: archive.c:348 builtin/archive.c:93
 msgid "path to the remote git-upload-archive command"
 msgstr "sökväg till kommandot git-upload-archive på fjärren"
 
@@ -133,6 +133,130 @@ msgstr ""
 "Negativa mönster ignoreras i git-attribut\n"
 "Använd '\\!' för att inleda med ett utropstecken."
 
+#: branch.c:60
+#, c-format
+msgid "Not setting branch %s as its own upstream."
+msgstr "Ställer inte in grenen %s som sin egen uppströmsgren."
+
+#: branch.c:82
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s by rebasing."
+msgstr ""
+"Grenen %s ställdes in att spåra fjärrgrenen %s från %s genom ombasering."
+
+#: branch.c:83
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s."
+msgstr "Grenen %s ställdes in att spåra fjärrgrenen %s från %s."
+
+#: branch.c:87
+#, c-format
+msgid "Branch %s set up to track local branch %s by rebasing."
+msgstr "Grenen %s ställdes in att spåra den lokala grenen %s genom ombasering."
+
+#: branch.c:88
+#, c-format
+msgid "Branch %s set up to track local branch %s."
+msgstr "Grenen %s ställdes in att spåra den lokala grenen %s."
+
+#: branch.c:92
+#, c-format
+msgid "Branch %s set up to track remote ref %s by rebasing."
+msgstr "Grenen %s ställdes in att spåra fjärreferensen %s genom ombasering."
+
+#: branch.c:93
+#, c-format
+msgid "Branch %s set up to track remote ref %s."
+msgstr "Grenen %s ställdes in att spåra fjärreferensen %s."
+
+#: branch.c:97
+#, c-format
+msgid "Branch %s set up to track local ref %s by rebasing."
+msgstr ""
+"Grenen %s ställdes in att spåra den lokala referensen %s genom ombasering."
+
+#: branch.c:98
+#, c-format
+msgid "Branch %s set up to track local ref %s."
+msgstr "Grenen %s ställdes in att spåra den lokala referensen %s."
+
+#: branch.c:118
+#, c-format
+msgid "Tracking not set up: name too long: %s"
+msgstr "Spårning har inte ställts in: namnet för långt: %s"
+
+#: branch.c:137
+#, c-format
+msgid "Not tracking: ambiguous information for ref %s"
+msgstr "Spårar inte: tvetydig information för referensen %s"
+
+#: branch.c:182
+#, c-format
+msgid "'%s' is not a valid branch name."
+msgstr "\"%s\" är inte ett giltigt grennamn."
+
+#: branch.c:187
+#, c-format
+msgid "A branch named '%s' already exists."
+msgstr "Det finns redan en gren som heter \"%s\""
+
+#: branch.c:195
+msgid "Cannot force update the current branch."
+msgstr "Kan inte tvinga uppdatering av aktuell gren."
+
+#: branch.c:216
+#, c-format
+msgid "Cannot setup tracking information; starting point '%s' is not a branch."
+msgstr "Kan inte ställa in spårning; startpunkten \"%s\" är inte en gren."
+
+#: branch.c:218
+#, c-format
+msgid "the requested upstream branch '%s' does not exist"
+msgstr "den efterfrågade uppströmsgrenen \"%s\" finns inte"
+
+#: branch.c:220
+msgid ""
+"\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push."
+msgstr ""
+"\n"
+"Om du har tänkt basera ditt arbete på en uppströmsgren\n"
+"som redan finns på fjärren kan du behöva köra \"git fetch\"\n"
+"för att hämta den.\n"
+"\n"
+"Om du har tänkt sända in en ny lokal gren som skall\n"
+"spåra dess fjärrmotsvarighet kan du använda \"git push -u\"\n"
+"för att ställa in uppströmskonfigurationen när du sänder in."
+
+#: branch.c:265
+#, c-format
+msgid "Not a valid object name: '%s'."
+msgstr "Objektnamnet är inte giltigt: \"%s\"."
+
+#: branch.c:285
+#, c-format
+msgid "Ambiguous object name: '%s'."
+msgstr "Objektnamnet är tvetydigt: \"%s\"."
+
+#: branch.c:290
+#, c-format
+msgid "Not a valid branch point: '%s'."
+msgstr "Avgreningspunkten är inte giltig: \"%s\""
+
+#: branch.c:296
+msgid "Failed to lock ref for update"
+msgstr "Misslyckades låsa referens för uppdatering"
+
+#: branch.c:314
+msgid "Failed to write ref"
+msgstr "Misslyckades skriva referens"
+
 #: bundle.c:36
 #, c-format
 msgid "'%s' does not look like a v2 bundle file"
@@ -143,7 +267,7 @@ msgstr "'%s' ser inte ut som en v2-bundle-fil"
 msgid "unrecognized header: %s%s (%d)"
 msgstr "okänt huvud: %s%s (%d)"
 
-#: bundle.c:89 builtin/commit.c:674
+#: bundle.c:89 builtin/commit.c:697
 #, c-format
 msgid "could not open '%s'"
 msgstr "kunde inte öppna \"%s\""
@@ -152,35 +276,35 @@ msgstr "kunde inte öppna \"%s\""
 msgid "Repository lacks these prerequisite commits:"
 msgstr "Arkivet saknar dessa nödvändiga incheckningar:"
 
-#: bundle.c:164 sequencer.c:566 sequencer.c:998 builtin/log.c:299
-#: builtin/log.c:751 builtin/log.c:1358 builtin/log.c:1574 builtin/merge.c:347
-#: builtin/shortlog.c:157
+#: bundle.c:164 sequencer.c:661 sequencer.c:1111 builtin/log.c:331
+#: builtin/log.c:801 builtin/log.c:1399 builtin/log.c:1625 builtin/merge.c:364
+#: builtin/shortlog.c:156
 msgid "revision walk setup failed"
 msgstr "misslyckades skapa revisionstraversering"
 
 #: bundle.c:186
 #, c-format
-msgid "The bundle contains %d ref"
-msgid_plural "The bundle contains %d refs"
-msgstr[0] "Paketet (bundlen) innehåller %d referens"
-msgstr[1] "Paketet (bundlen) innehåller %d referenser"
+msgid "The bundle contains this ref:"
+msgid_plural "The bundle contains these %d refs:"
+msgstr[0] "Paketet (bundlen) denna referens:"
+msgstr[1] "Paketet (bundlen) dessa %d referenser:"
 
-#: bundle.c:192
+#: bundle.c:193
 msgid "The bundle records a complete history."
 msgstr "Paketet (bundlen) beskriver en komplett historik."
 
 #: bundle.c:195
 #, c-format
-msgid "The bundle requires this ref"
-msgid_plural "The bundle requires these %d refs"
-msgstr[0] "Paketet (bundlen) kräver denna referens"
-msgstr[1] "Paketet (bundlen) kräver dessa %d referenser"
+msgid "The bundle requires this ref:"
+msgid_plural "The bundle requires these %d refs:"
+msgstr[0] "Paketet (bundlen) kräver denna referens:"
+msgstr[1] "Paketet (bundlen) kräver dessa %d referenser:"
 
 #: bundle.c:294
 msgid "rev-list died"
 msgstr "rev-list dog"
 
-#: bundle.c:300 builtin/log.c:1254 builtin/shortlog.c:260
+#: bundle.c:300 builtin/log.c:1310 builtin/shortlog.c:259
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "okänt argument: %s"
@@ -211,12 +335,12 @@ msgstr "kan inte skapa \"%s\""
 msgid "index-pack died"
 msgstr "index-pack dog"
 
-#: commit.c:50
+#: commit.c:53
 #, c-format
 msgid "could not parse %s"
 msgstr "kunde inte tolka %s"
 
-#: commit.c:52
+#: commit.c:55
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s är inte en incheckning!"
@@ -225,16 +349,16 @@ msgstr "%s %s är inte en incheckning!"
 msgid "memory exhausted"
 msgstr "minnet slut"
 
-#: connected.c:39
+#: connected.c:60
 msgid "Could not run 'git rev-list'"
 msgstr "Kunde inte köra \"git rev-list\""
 
-#: connected.c:48
+#: connected.c:80
 #, c-format
 msgid "failed write to rev-list: %s"
 msgstr "kunde inte skriva till rev-list: %s"
 
-#: connected.c:56
+#: connected.c:88
 #, c-format
 msgid "failed to close rev-list's stdin: %s"
 msgstr "kunde inte stänga rev-list:s standard in: %s"
@@ -330,7 +454,7 @@ msgstr ""
 "Hittade fel i konfigurationsvariabeln \"diff.dirstat\":\n"
 "%s"
 
-#: diff.c:3468
+#: diff.c:3487
 #, c-format
 msgid ""
 "Failed to parse --dirstat/-X option parameter:\n"
@@ -339,12 +463,12 @@ msgstr ""
 "Misslyckades tolka argument till flaggan --dirstat/-X;\n"
 "%s"
 
-#: diff.c:3482
+#: diff.c:3501
 #, c-format
 msgid "Failed to parse --submodule option parameter: '%s'"
 msgstr "Misslyckades tolka argument till flaggan --submodule: \"%s\""
 
-#: gpg-interface.c:59 gpg-interface.c:127
+#: gpg-interface.c:59 gpg-interface.c:131
 msgid "could not run gpg."
 msgstr "kunde inte köra gpg."
 
@@ -356,45 +480,45 @@ msgstr "gpg godtog inte data"
 msgid "gpg failed to sign the data"
 msgstr "gpg misslyckades signera data"
 
-#: gpg-interface.c:112
+#: gpg-interface.c:115
 #, c-format
 msgid "could not create temporary file '%s': %s"
 msgstr "kunde inte skapa temporära filen \"%s\": %s"
 
-#: gpg-interface.c:115
+#: gpg-interface.c:118
 #, c-format
 msgid "failed writing detached signature to '%s': %s"
 msgstr "misslyckades skriva fristående signatur till \"%s\": %s"
 
-#: grep.c:1622
+#: grep.c:1623
 #, c-format
 msgid "'%s': unable to read %s"
 msgstr "\"%s\" kunde inte läsa %s"
 
-#: grep.c:1639
+#: grep.c:1640
 #, c-format
 msgid "'%s': %s"
 msgstr "\"%s\": %s"
 
-#: grep.c:1650
+#: grep.c:1651
 #, c-format
 msgid "'%s': short read %s"
 msgstr "\"%s\": kort läsning %s"
 
-#: help.c:212
+#: help.c:210
 #, c-format
 msgid "available git commands in '%s'"
 msgstr "git-kommandon tillgängliga i \"%s\""
 
-#: help.c:219
+#: help.c:217
 msgid "git commands available from elsewhere on your $PATH"
 msgstr "git-kommandon från andra platser i din $PATH"
 
-#: help.c:235
+#: help.c:233
 msgid "The most commonly used git commands are:"
 msgstr "De mest använda git-kommandona är:"
 
-#: help.c:292
+#: help.c:290
 #, c-format
 msgid ""
 "'%s' appears to be a git command, but we were not\n"
@@ -403,11 +527,11 @@ msgstr ""
 "\"%s\" verkar vara ett git-kommando, men vi kan inte\n"
 "köra det. Kanske git-%s är trasigt?"
 
-#: help.c:349
+#: help.c:347
 msgid "Uh oh. Your system reports no Git commands at all."
 msgstr "Oj då. Ditt system rapporterar inga Git-kommandon alls."
 
-#: help.c:371
+#: help.c:369
 #, c-format
 msgid ""
 "WARNING: You called a Git command named '%s', which does not exist.\n"
@@ -416,17 +540,17 @@ msgstr ""
 "VARNING: Du anropade ett Git-kommando vid namn \"%s\", som inte finns.\n"
 "Fortsätter under förutsättningen att du menade \"%s\""
 
-#: help.c:376
+#: help.c:374
 #, c-format
 msgid "in %0.1f seconds automatically..."
 msgstr "automatiskt om %0.1f sekunder..."
 
-#: help.c:383
+#: help.c:381
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
 msgstr "git: \"%s\" är inte ett git-kommando. Se \"git --help\"."
 
-#: help.c:387
+#: help.c:385 help.c:444
 msgid ""
 "\n"
 "Did you mean this?"
@@ -440,12 +564,17 @@ msgstr[1] ""
 "\n"
 "Menade du ett av dessa?"
 
+#: help.c:440
+#, c-format
+msgid "%s: %s - %s"
+msgstr "%s: %s - %s"
+
 #: merge.c:56
 msgid "failed to read the cache"
 msgstr "misslyckads läsa cachen"
 
-#: merge.c:110 builtin/checkout.c:333 builtin/checkout.c:534
-#: builtin/clone.c:586
+#: merge.c:110 builtin/checkout.c:365 builtin/checkout.c:566
+#: builtin/clone.c:656
 msgid "unable to write new index file"
 msgstr "kunde inte skriva ny indexfil"
 
@@ -459,70 +588,70 @@ msgstr "(felaktig incheckning)\n"
 msgid "addinfo_cache failed for path '%s'"
 msgstr "addinfo_cache misslyckades för sökvägen \"%s\""
 
-#: merge-recursive.c:268
+#: merge-recursive.c:269
 msgid "error building trees"
 msgstr "fel vid byggande av träd"
 
-#: merge-recursive.c:672
+#: merge-recursive.c:673
 #, c-format
 msgid "failed to create path '%s'%s"
 msgstr "misslyckades skapa sökvägen \"%s\"%s"
 
-#: merge-recursive.c:683
+#: merge-recursive.c:684
 #, c-format
 msgid "Removing %s to make room for subdirectory\n"
 msgstr "Tar bort %s för att göra plats för underkatalog\n"
 
 #. something else exists
 #. .. but not some other error (who really cares what?)
-#: merge-recursive.c:697 merge-recursive.c:718
+#: merge-recursive.c:698 merge-recursive.c:719
 msgid ": perhaps a D/F conflict?"
 msgstr ": kanske en K/F-konflikt?"
 
-#: merge-recursive.c:708
+#: merge-recursive.c:709
 #, c-format
 msgid "refusing to lose untracked file at '%s'"
 msgstr "vägrar förlora ospårad fil vid \"%s\""
 
-#: merge-recursive.c:748
+#: merge-recursive.c:749
 #, c-format
 msgid "cannot read object %s '%s'"
 msgstr "kan inte läsa objektet %s: \"%s\""
 
-#: merge-recursive.c:750
+#: merge-recursive.c:751
 #, c-format
 msgid "blob expected for %s '%s'"
 msgstr "blob förväntades för %s \"%s\""
 
-#: merge-recursive.c:773 builtin/clone.c:302
+#: merge-recursive.c:774 builtin/clone.c:312
 #, c-format
 msgid "failed to open '%s'"
 msgstr "misslyckades öppna \"%s\""
 
-#: merge-recursive.c:781
+#: merge-recursive.c:782
 #, c-format
 msgid "failed to symlink '%s'"
 msgstr "misslyckades ta skapa symboliska länken \"%s\""
 
-#: merge-recursive.c:784
+#: merge-recursive.c:785
 #, c-format
 msgid "do not know what to do with %06o %s '%s'"
 msgstr "vet inte hur %06o %s \"%s\" skall hanteras"
 
-#: merge-recursive.c:922
+#: merge-recursive.c:923
 msgid "Failed to execute internal merge"
 msgstr "Misslyckades exekvera intern sammanslagning"
 
-#: merge-recursive.c:926
+#: merge-recursive.c:927
 #, c-format
 msgid "Unable to add %s to database"
 msgstr "Kunde inte lägga till %s till databasen"
 
-#: merge-recursive.c:942
+#: merge-recursive.c:943
 msgid "unsupported object type in the tree"
 msgstr "objekttyp som ej stöds upptäcktes i trädet"
 
-#: merge-recursive.c:1021 merge-recursive.c:1035
+#: merge-recursive.c:1022 merge-recursive.c:1036
 #, c-format
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
@@ -531,7 +660,7 @@ msgstr ""
 "KONFLIKT (%s/radera): %s raderad i %s och %s i %s. Versionen %s av %s lämnad "
 "i trädet."
 
-#: merge-recursive.c:1027 merge-recursive.c:1040
+#: merge-recursive.c:1028 merge-recursive.c:1041
 #, c-format
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
@@ -540,20 +669,20 @@ msgstr ""
 "KONFLIKT (%s/radera): %s raderad i %s och %s i %s. Versionen %s av %s lämnad "
 "i trädet vid %s."
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "rename"
 msgstr "namnbyte"
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "renamed"
 msgstr "namnbytt"
 
-#: merge-recursive.c:1137
+#: merge-recursive.c:1138
 #, c-format
 msgid "%s is a directory in %s adding as %s instead"
 msgstr "%s är en katalog i %s lägger till som %s istället"
 
-#: merge-recursive.c:1159
+#: merge-recursive.c:1160
 #, c-format
 msgid ""
 "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
@@ -562,147 +691,173 @@ msgstr ""
 "KONFLIKT (namnbyte/namnbyte): Namnbyte \"%s\"->\"%s\" på grenen \"%s\" "
 "namnbyte \"%s\"->\"%s\" i \"%s\"%s"
 
-#: merge-recursive.c:1164
+#: merge-recursive.c:1165
 msgid " (left unresolved)"
 msgstr " (lämnad olöst)"
 
-#: merge-recursive.c:1218
+#: merge-recursive.c:1219
 #, c-format
 msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
 msgstr ""
 "KONFLIKT (namnbyte/namnbyte): Namnbyte %s->%s i %s. Namnbyte %s->%s i %s"
 
-#: merge-recursive.c:1248
+#: merge-recursive.c:1249
 #, c-format
 msgid "Renaming %s to %s and %s to %s instead"
 msgstr "Byter namn på %s till %s och %s till %s istället"
 
-#: merge-recursive.c:1447
+#: merge-recursive.c:1448
 #, c-format
 msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s"
 msgstr "KONFLIKT (namnbyte/tillägg): Namnbyte %s->%s i %s. %s tillagd i %s"
 
-#: merge-recursive.c:1457
+#: merge-recursive.c:1458
 #, c-format
 msgid "Adding merged %s"
 msgstr "Lägger till sammanslagen %s"
 
-#: merge-recursive.c:1462 merge-recursive.c:1660
+#: merge-recursive.c:1463 merge-recursive.c:1661
 #, c-format
 msgid "Adding as %s instead"
 msgstr "Lägger till som %s iställer"
 
-#: merge-recursive.c:1513
+#: merge-recursive.c:1514
 #, c-format
 msgid "cannot read object %s"
 msgstr "kan inte läsa objektet %s"
 
-#: merge-recursive.c:1516
+#: merge-recursive.c:1517
 #, c-format
 msgid "object %s is not a blob"
 msgstr "objektet %s är inte en blob"
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modify"
 msgstr "ändra"
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modified"
 msgstr "ändrad"
 
-#: merge-recursive.c:1574
+#: merge-recursive.c:1575
 msgid "content"
 msgstr "innehåll"
 
-#: merge-recursive.c:1581
+#: merge-recursive.c:1582
 msgid "add/add"
 msgstr "tillägg/tillägg"
 
-#: merge-recursive.c:1615
+#: merge-recursive.c:1616
 #, c-format
 msgid "Skipped %s (merged same as existing)"
 msgstr "Hoppade över %s (sammanslagen samma som befintlig)"
 
-#: merge-recursive.c:1629
+#: merge-recursive.c:1630
 #, c-format
 msgid "Auto-merging %s"
 msgstr "Slår ihop %s automatiskt"
 
-#: merge-recursive.c:1633 git-submodule.sh:942
+#: merge-recursive.c:1634 git-submodule.sh:1118
 msgid "submodule"
 msgstr "undermodul"
 
-#: merge-recursive.c:1634
+#: merge-recursive.c:1635
 #, c-format
 msgid "CONFLICT (%s): Merge conflict in %s"
 msgstr "KONFLIKT (%s): Sammanslagningskonflikt i %s"
 
-#: merge-recursive.c:1724
+#: merge-recursive.c:1725
 #, c-format
 msgid "Removing %s"
 msgstr "Tar bort %s"
 
-#: merge-recursive.c:1749
+#: merge-recursive.c:1750
 msgid "file/directory"
 msgstr "fil/katalog"
 
-#: merge-recursive.c:1755
+#: merge-recursive.c:1756
 msgid "directory/file"
 msgstr "katalog/fil"
 
-#: merge-recursive.c:1760
+#: merge-recursive.c:1761
 #, c-format
 msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
 msgstr ""
 "KONFLIKT (%s): Det finns en katalog med namnet %s i %s. Lägger till %s som %s"
 
-#: merge-recursive.c:1770
+#: merge-recursive.c:1771
 #, c-format
 msgid "Adding %s"
 msgstr "Lägger till %s"
 
-#: merge-recursive.c:1787
+#: merge-recursive.c:1788
 msgid "Fatal merge failure, shouldn't happen."
 msgstr "Ödesdigert sammanslagningsfel, borde inte inträffa."
 
-#: merge-recursive.c:1806
+#: merge-recursive.c:1807
 msgid "Already up-to-date!"
 msgstr "Redan à jour!"
 
-#: merge-recursive.c:1815
+#: merge-recursive.c:1816
 #, c-format
 msgid "merging of trees %s and %s failed"
 msgstr "sammanslagning av träden %s och %s misslyckades"
 
-#: merge-recursive.c:1845
+#: merge-recursive.c:1846
 #, c-format
 msgid "Unprocessed path??? %s"
 msgstr "Obehandlad sökväg??? %s"
 
-#: merge-recursive.c:1890
+#: merge-recursive.c:1891
 msgid "Merging:"
 msgstr "Slår ihop:"
 
-#: merge-recursive.c:1903
+#: merge-recursive.c:1904
 #, c-format
 msgid "found %u common ancestor:"
 msgid_plural "found %u common ancestors:"
 msgstr[0] "hittade %u gemensam förfader:"
 msgstr[1] "hittade %u gemensamma förfäder:"
 
-#: merge-recursive.c:1940
+#: merge-recursive.c:1941
 msgid "merge returned no commit"
 msgstr "sammanslagningen returnerade ingen incheckning"
 
-#: merge-recursive.c:1997
+#: merge-recursive.c:1998
 #, c-format
 msgid "Could not parse object '%s'"
 msgstr "Kunde inte tolka objektet \"%s\""
 
-#: merge-recursive.c:2009 builtin/merge.c:643
+#: merge-recursive.c:2010 builtin/merge.c:672
 msgid "Unable to write index."
 msgstr "Kunde inte skriva indexet."
 
+#: notes-utils.c:40
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "Kan inte checka in oinitierat/orefererat anteckningsträd"
+
+#: notes-utils.c:81
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Felaktigt värde för notes.rewriteMode: '%s'"
+
+#: notes-utils.c:91
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "Vägrar skriva över anteckningar i %s (utanför refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: notes-utils.c:118
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Felaktigt värde på %s: \"%s\""
+
+#: object.c:204
+#, c-format
+msgid "unable to parse object: %s"
+msgstr "kunde inte tolka objektet: %s"
+
 #: parse-options.c:489
 msgid "..."
 msgstr "..."
@@ -738,18 +893,18 @@ msgstr "Sökvägen \"%s\" är i undermodulen \"%.*s\""
 msgid "'%s' is beyond a symbolic link"
 msgstr "\"%s\" är på andra sidan av en symbolisk länk"
 
-#: remote.c:1653
+#: remote.c:1788
 #, 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] "Din gren ligger före \"%s\" med %d incheckning.\n"
 msgstr[1] "Din gren ligger före \"%s\" med %d incheckningar.\n"
 
-#: remote.c:1659
+#: remote.c:1794
 msgid "  (use \"git push\" to publish your local commits)\n"
 msgstr "  (använd \"git push\" för att publicera dina lokala incheckningar)\n"
 
-#: remote.c:1662
+#: remote.c:1797
 #, c-format
 msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
 msgid_plural ""
@@ -759,11 +914,11 @@ msgstr[0] ""
 msgstr[1] ""
 "Din gren ligger efter \"%s\" med %d incheckningar, och kan snabbspolas.\n"
 
-#: remote.c:1670
+#: remote.c:1805
 msgid "  (use \"git pull\" to update your local branch)\n"
 msgstr "  (använd \"git pull\" för att uppdatera din lokala gren)\n"
 
-#: remote.c:1673
+#: remote.c:1808
 #, c-format
 msgid ""
 "Your branch and '%s' have diverged,\n"
@@ -778,23 +933,32 @@ msgstr[1] ""
 "Din gren och \"%s\" har divergerat,\n"
 "och har %d respektive %d olika incheckningar.\n"
 
-#: remote.c:1683
+#: remote.c:1818
 msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
 msgstr "  (använd \"git pull\" för att slå ihop fjärrgrenen med din egen)\n"
 
-#: sequencer.c:123 builtin/merge.c:761 builtin/merge.c:874 builtin/merge.c:984
-#: builtin/merge.c:994
+#: run-command.c:80
+msgid "open /dev/null failed"
+msgstr "misslyckades öppna /dev/null"
+
+#: run-command.c:82
+#, c-format
+msgid "dup2(%d,%d) failed"
+msgstr "dup2(%d,%d) misslyckades"
+
+#: sequencer.c:206 builtin/merge.c:790 builtin/merge.c:903
+#: builtin/merge.c:1013 builtin/merge.c:1023
 #, c-format
 msgid "Could not open '%s' for writing"
 msgstr "Kunde inte öppna \"%s\" för skrivning"
 
-#: sequencer.c:125 builtin/merge.c:333 builtin/merge.c:764 builtin/merge.c:986
-#: builtin/merge.c:999
+#: sequencer.c:208 builtin/merge.c:350 builtin/merge.c:793
+#: builtin/merge.c:1015 builtin/merge.c:1028
 #, c-format
 msgid "Could not write to '%s'"
 msgstr "Kunde inte skriva till \"%s\""
 
-#: sequencer.c:146
+#: sequencer.c:229
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'"
@@ -802,7 +966,7 @@ msgstr ""
 "efter att ha löst konflikterna, markera de rättade sökvägarna\n"
 "med \"git add <sökvägar>\" eller \"git rm <sökvägar>\""
 
-#: sequencer.c:149
+#: sequencer.c:232
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'\n"
@@ -812,228 +976,260 @@ msgstr ""
 "med \"git add <sökvägar>\" eller \"git rm <sökvägar>\"\n"
 "och checka in resultatet med \"git commit\""
 
-#: sequencer.c:162 sequencer.c:774 sequencer.c:857
+#: sequencer.c:245 sequencer.c:869 sequencer.c:952
 #, c-format
 msgid "Could not write to %s"
 msgstr "Kunde inte skriva till %s"
 
-#: sequencer.c:165
+#: sequencer.c:248
 #, c-format
 msgid "Error wrapping up %s"
 msgstr "Fel vid ombrytning av %s"
 
-#: sequencer.c:180
+#: sequencer.c:263
 msgid "Your local changes would be overwritten by cherry-pick."
 msgstr "Dina lokala ändringar skulle skrivas över av \"cherry-pick\"."
 
-#: sequencer.c:182
+#: sequencer.c:265
 msgid "Your local changes would be overwritten by revert."
 msgstr "Dina lokala ändringar skulle skrivas över av \"revert\"."
 
-#: sequencer.c:185
+#: sequencer.c:268
 msgid "Commit your changes or stash them to proceed."
 msgstr "Checka in dina ändringar eller använd \"stash\" för att fortsätta."
 
 #. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:236
+#: sequencer.c:324
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr "%s: Kunde inte skriva ny indexfil"
 
-#: sequencer.c:267
+#: sequencer.c:355
 msgid "Could not resolve HEAD commit\n"
 msgstr "Kunde inte bestämma HEAD:s incheckning\n"
 
-#: sequencer.c:288
+#: sequencer.c:377
 msgid "Unable to update cache tree\n"
 msgstr "Kan inte uppdatera cacheträd\n"
 
-#: sequencer.c:333
+#: sequencer.c:422
 #, c-format
 msgid "Could not parse commit %s\n"
 msgstr "Kunde inte tolka incheckningen %s\n"
 
-#: sequencer.c:338
+#: sequencer.c:427
 #, c-format
 msgid "Could not parse parent commit %s\n"
 msgstr "Kunde inte tolka föräldraincheckningen %s\n"
 
-#: sequencer.c:404
+#: sequencer.c:493
 msgid "Your index file is unmerged."
 msgstr "Din indexfil har inte slagits ihop."
 
-#: sequencer.c:423
+#: sequencer.c:512
 #, c-format
 msgid "Commit %s is a merge but no -m option was given."
 msgstr "Incheckning %s är en sammanslagning, men flaggan -m angavs inte."
 
-#: sequencer.c:431
+#: sequencer.c:520
 #, c-format
 msgid "Commit %s does not have parent %d"
 msgstr "Incheckning %s har inte förälder %d"
 
-#: sequencer.c:435
+#: sequencer.c:524
 #, c-format
 msgid "Mainline was specified but commit %s is not a merge."
 msgstr "Huvudlinje angavs, men incheckningen %s är inte en sammanslagning"
 
 #. TRANSLATORS: The first %s will be "revert" or
 #. "cherry-pick", the second %s a SHA1
-#: sequencer.c:448
+#: sequencer.c:537
 #, c-format
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s: kan inte tolka föräldraincheckningen %s"
 
-#: sequencer.c:452
+#: sequencer.c:541
 #, c-format
 msgid "Cannot get commit message for %s"
 msgstr "Kan inte hämta incheckningsmeddelande för %s"
 
-#: sequencer.c:536
+#: sequencer.c:627
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "kunde inte ångra %s... %s"
 
-#: sequencer.c:537
+#: sequencer.c:628
 #, c-format
 msgid "could not apply %s... %s"
 msgstr "kunde inte tillämpa %s... %s"
 
-#: sequencer.c:569
+#: sequencer.c:664
 msgid "empty commit set passed"
 msgstr "den angivna uppsättningen incheckningar är tom"
 
-#: sequencer.c:577
+#: sequencer.c:672
 #, c-format
 msgid "git %s: failed to read the index"
 msgstr "git %s: misslyckades läsa indexet"
 
-#: sequencer.c:582
+#: sequencer.c:677
 #, c-format
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: misslyckades uppdatera indexet"
 
-#: sequencer.c:640
+#: sequencer.c:735
 #, c-format
 msgid "Cannot %s during a %s"
 msgstr "kan inte %s under en %s"
 
-#: sequencer.c:662
+#: sequencer.c:757
 #, c-format
 msgid "Could not parse line %d."
 msgstr "Kan inte tolka rad %d."
 
-#: sequencer.c:667
+#: sequencer.c:762
 msgid "No commits parsed."
 msgstr "Inga incheckningar lästes."
 
-#: sequencer.c:680
+#: sequencer.c:775
 #, c-format
 msgid "Could not open %s"
 msgstr "Kunde inte öppna %s"
 
-#: sequencer.c:684
+#: sequencer.c:779
 #, c-format
 msgid "Could not read %s."
 msgstr "kunde inte läsa %s."
 
-#: sequencer.c:691
+#: sequencer.c:786
 #, c-format
 msgid "Unusable instruction sheet: %s"
 msgstr "Oanvändbart manus: %s"
 
-#: sequencer.c:719
+#: sequencer.c:814
 #, c-format
 msgid "Invalid key: %s"
 msgstr "Felaktig nyckel: %s"
 
-#: sequencer.c:722
+#: sequencer.c:817
 #, c-format
 msgid "Invalid value for %s: %s"
 msgstr "Felaktigt värde för %s: %s"
 
-#: sequencer.c:734
+#: sequencer.c:829
 #, c-format
 msgid "Malformed options sheet: %s"
 msgstr "Trasigt manus: %s"
 
-#: sequencer.c:755
+#: sequencer.c:850
 msgid "a cherry-pick or revert is already in progress"
 msgstr "en \"cherry-pick\" eller \"revert\" pågår redan"
 
-#: sequencer.c:756
+#: sequencer.c:851
 msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
 msgstr "testa \"git cherry-pick (--continue | --quit | --abort)\""
 
-#: sequencer.c:760
+#: sequencer.c:855
 #, c-format
 msgid "Could not create sequencer directory %s"
 msgstr "Kunde inte skapa \"sequencer\"-katalogen \"%s\""
 
-#: sequencer.c:776 sequencer.c:861
+#: sequencer.c:871 sequencer.c:956
 #, c-format
 msgid "Error wrapping up %s."
 msgstr "Fel vid ombrytning av %s."
 
-#: sequencer.c:795 sequencer.c:929
+#: sequencer.c:890 sequencer.c:1024
 msgid "no cherry-pick or revert in progress"
 msgstr "ingen \"cherry-pick\" eller \"revert\" pågår"
 
-#: sequencer.c:797
+#: sequencer.c:892
 msgid "cannot resolve HEAD"
 msgstr "kan inte bestämma HEAD"
 
-#: sequencer.c:799
+#: sequencer.c:894
 msgid "cannot abort from a branch yet to be born"
 msgstr "kan inte avbryta från en gren som ännu inte är född"
 
-#: sequencer.c:821 builtin/apply.c:4056
+#: sequencer.c:916 builtin/apply.c:4061
 #, c-format
 msgid "cannot open %s: %s"
 msgstr "kan inte öppna %s: %s"
 
-#: sequencer.c:824
+#: sequencer.c:919
 #, c-format
 msgid "cannot read %s: %s"
 msgstr "kan inte läsa %s: %s"
 
-#: sequencer.c:825
+#: sequencer.c:920
 msgid "unexpected end of file"
 msgstr "oväntat filslut"
 
-#: sequencer.c:831
+#: sequencer.c:926
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
 msgstr "sparad HEAD-fil från före \"cherry-pick\", \"%s\", är trasig"
 
-#: sequencer.c:854
+#: sequencer.c:949
 #, c-format
 msgid "Could not format %s."
 msgstr "Kunde inte formatera %s."
 
-#: sequencer.c:1016
+#: sequencer.c:1092
+#, c-format
+msgid "%s: can't cherry-pick a %s"
+msgstr "%s: kan inte göra \"cherry-pick\" på typen \"%s\""
+
+#: sequencer.c:1095
+#, c-format
+msgid "%s: bad revision"
+msgstr "%s: felaktig revision"
+
+#: sequencer.c:1129
 msgid "Can't revert as initial commit"
 msgstr "Kan inte ångra som första incheckning"
 
-#: sequencer.c:1017
+#: sequencer.c:1130
 msgid "Can't cherry-pick into empty head"
 msgstr "Kan inte göra \"cherry-pick\" i ett tomt huvud"
 
-#: sha1_name.c:1044
+#: sha1_name.c:440
+msgid ""
+"Git normally never creates a ref that ends with 40 hex characters\n"
+"because it will be ignored when you just specify 40-hex. These refs\n"
+"may be created by mistake. For example,\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+"examine these refs and maybe delete them. Turn this message off by\n"
+"running \"git config advice.objectNameWarning false\""
+msgstr ""
+"Git skapar normalt aldrig referenser som slutar med 40 hexadecimala\n"
+"tecken, då detta ignoreras när du anger 40-hex enbart. Dessa\n"
+"referenser kan skapas av misstag. Till exempel, för\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"där \"$br\" på något sätt blivit tomt och en 40-hex-referens skapats.\n"
+"Undersök referenserna och ta kanske bort dem. Stäng av meddelandet\n"
+"genom att köra \"git config advice.objectNameWarning false\""
+
+#: sha1_name.c:1097
 msgid "HEAD does not point to a branch"
 msgstr "HEAD pekar inte på en gren"
 
-#: sha1_name.c:1047
+#: sha1_name.c:1100
 #, c-format
 msgid "No such branch: '%s'"
 msgstr "Okänd gren: \"%s\""
 
-#: sha1_name.c:1049
+#: sha1_name.c:1102
 #, c-format
 msgid "No upstream configured for branch '%s'"
 msgstr "Ingen standarduppström angiven för grenen \"%s\""
 
-#: sha1_name.c:1052
+#: sha1_name.c:1106
 #, c-format
 msgid "Upstream branch '%s' not stored as a remote-tracking branch"
 msgstr "Uppströmsgrenen \"%s\" är inte lagrad som en fjärrspårande gren"
@@ -1043,232 +1239,232 @@ msgstr "Uppströmsgrenen \"%s\" är inte lagrad som en fjärrspårande gren"
 msgid "unable to access '%s': %s"
 msgstr "kan inte komma åt \"%s\": %s"
 
-#: wrapper.c:423
+#: wrapper.c:429
 #, c-format
 msgid "unable to access '%s'"
 msgstr "kan inte komma åt \"%s\""
 
-#: wrapper.c:434
+#: wrapper.c:440
 #, c-format
 msgid "unable to look up current user in the passwd file: %s"
 msgstr "kan inte slå upp aktuell användare i passwd-filen: %s"
 
-#: wrapper.c:435
+#: wrapper.c:441
 msgid "no such user"
 msgstr "okänd användare"
 
-#: wt-status.c:140
+#: wt-status.c:141
 msgid "Unmerged paths:"
 msgstr "Ej sammanslagna sökvägar:"
 
-#: wt-status.c:167 wt-status.c:194
+#: wt-status.c:168 wt-status.c:195
 #, c-format
 msgid "  (use \"git reset %s <file>...\" to unstage)"
 msgstr "  (använd \"git reset %s <fil>...\" för att ta bort från kö)"
 
-#: wt-status.c:169 wt-status.c:196
+#: wt-status.c:170 wt-status.c:197
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
 msgstr "  (använd \"git rm --cached <fil>...\" för att ta bort från kö)"
 
-#: wt-status.c:173
+#: wt-status.c:174
 msgid "  (use \"git add <file>...\" to mark resolution)"
 msgstr "  (använd \"git add <fil>...\" för att ange lösning)"
 
-#: wt-status.c:175 wt-status.c:179
+#: wt-status.c:176 wt-status.c:180
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
 msgstr "  (använd \"git add/rm <fil>...\" som lämpligt för att ange lösning)"
 
-#: wt-status.c:177
+#: wt-status.c:178
 msgid "  (use \"git rm <file>...\" to mark resolution)"
 msgstr "  (använd \"git rm <fil>...\" för att ange lösning)"
 
-#: wt-status.c:188
+#: wt-status.c:189
 msgid "Changes to be committed:"
 msgstr "Ändringar att checka in:"
 
-#: wt-status.c:206
+#: wt-status.c:207
 msgid "Changes not staged for commit:"
 msgstr "Ändringar ej i incheckningskön:"
 
-#: wt-status.c:210
+#: wt-status.c:211
 msgid "  (use \"git add <file>...\" to update what will be committed)"
 msgstr ""
 "  (använd \"git add <fil>...\" för att uppdatera vad som skall checkas in)"
 
-#: wt-status.c:212
+#: wt-status.c:213
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
 msgstr ""
 "  (använd \"git add/rm <fil>...\" för att uppdatera vad som skall checkas in)"
 
-#: wt-status.c:213
+#: wt-status.c:214
 msgid ""
 "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
 msgstr ""
 "  (använd \"git checkout -- <fil>...\" för att förkasta ändringar i "
 "arbetskatalogen)"
 
-#: wt-status.c:215
+#: wt-status.c:216
 msgid "  (commit or discard the untracked or modified content in submodules)"
 msgstr ""
 "  (checka in eller förkasta ospårat eller ändrat innehåll i undermoduler)"
 
-#: wt-status.c:227
+#: wt-status.c:228
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
 msgstr ""
 "  (använd \"git %s <fil>...\" för att ta med i det som skall checkas in)"
 
-#: wt-status.c:244
+#: wt-status.c:245
 msgid "bug"
 msgstr "programfel"
 
-#: wt-status.c:249
+#: wt-status.c:250
 msgid "both deleted:"
 msgstr "borttaget av bägge:"
 
-#: wt-status.c:250
+#: wt-status.c:251
 msgid "added by us:"
 msgstr "tillagt av oss:"
 
-#: wt-status.c:251
+#: wt-status.c:252
 msgid "deleted by them:"
 msgstr "borttaget av dem:"
 
-#: wt-status.c:252
+#: wt-status.c:253
 msgid "added by them:"
 msgstr "tillagt av dem:"
 
-#: wt-status.c:253
+#: wt-status.c:254
 msgid "deleted by us:"
 msgstr "borttaget av oss:"
 
-#: wt-status.c:254
+#: wt-status.c:255
 msgid "both added:"
 msgstr "tillagt av bägge:"
 
-#: wt-status.c:255
+#: wt-status.c:256
 msgid "both modified:"
 msgstr "ändrat av bägge:"
 
-#: wt-status.c:285
+#: wt-status.c:286
 msgid "new commits, "
 msgstr "nya incheckningar, "
 
-#: wt-status.c:287
+#: wt-status.c:288
 msgid "modified content, "
 msgstr "ändrat innehåll, "
 
-#: wt-status.c:289
+#: wt-status.c:290
 msgid "untracked content, "
 msgstr "ospårat innehåll, "
 
-#: wt-status.c:303
+#: wt-status.c:307
 #, c-format
 msgid "new file:   %s"
 msgstr "ny fil:     %s"
 
-#: wt-status.c:306
+#: wt-status.c:310
 #, c-format
 msgid "copied:     %s -> %s"
 msgstr "kopierad:   %s -> %s"
 
-#: wt-status.c:309
+#: wt-status.c:313
 #, c-format
 msgid "deleted:    %s"
 msgstr "borttagen:  %s"
 
-#: wt-status.c:312
+#: wt-status.c:316
 #, c-format
 msgid "modified:   %s"
 msgstr "ändrad:     %s"
 
-#: wt-status.c:315
+#: wt-status.c:319
 #, c-format
 msgid "renamed:    %s -> %s"
 msgstr "namnbyte:   %s -> %s"
 
-#: wt-status.c:318
+#: wt-status.c:322
 #, c-format
 msgid "typechange: %s"
 msgstr "typbyte:    %s"
 
-#: wt-status.c:321
+#: wt-status.c:325
 #, c-format
 msgid "unknown:    %s"
 msgstr "okänd:      %s"
 
-#: wt-status.c:324
+#: wt-status.c:328
 #, c-format
 msgid "unmerged:   %s"
 msgstr "osammansl.: %s"
 
-#: wt-status.c:327
+#: wt-status.c:331
 #, c-format
 msgid "bug: unhandled diff status %c"
 msgstr "programfel: diff-status %c ej hanterad"
 
-#: wt-status.c:789
+#: wt-status.c:803
 msgid "You have unmerged paths."
 msgstr "Du har ej sammanslagna sökvägar."
 
-#: wt-status.c:792 wt-status.c:944
+#: wt-status.c:806
 msgid "  (fix conflicts and run \"git commit\")"
 msgstr "  (rätta konflikter och kör \"git commit\")"
 
-#: wt-status.c:795
+#: wt-status.c:809
 msgid "All conflicts fixed but you are still merging."
 msgstr "Alla konflikter har rättats men du är fortfarande i en sammanslagning."
 
-#: wt-status.c:798
+#: wt-status.c:812
 msgid "  (use \"git commit\" to conclude merge)"
 msgstr "  (använd \"git commit\" för att slutföra sammanslagningen)"
 
-#: wt-status.c:808
+#: wt-status.c:822
 msgid "You are in the middle of an am session."
 msgstr "Du är i mitten av en körning av \"git am\"."
 
-#: wt-status.c:811
+#: wt-status.c:825
 msgid "The current patch is empty."
 msgstr "Aktuell patch är tom."
 
-#: wt-status.c:815
-msgid "  (fix conflicts and then run \"git am --resolved\")"
-msgstr "  (rätta konflikter och kör sedan \"git am --resolved\")"
+#: wt-status.c:829
+msgid "  (fix conflicts and then run \"git am --continue\")"
+msgstr "  (rätta konflikter och kör sedan \"git am --continue\")"
 
-#: wt-status.c:817
+#: wt-status.c:831
 msgid "  (use \"git am --skip\" to skip this patch)"
 msgstr "  (använd \"git am --skip\" för att hoppa över patchen)"
 
-#: wt-status.c:819
+#: wt-status.c:833
 msgid "  (use \"git am --abort\" to restore the original branch)"
 msgstr "  (använd \"git am --abort\" för att återställa ursprungsgrenen)"
 
-#: wt-status.c:879 wt-status.c:896
+#: wt-status.c:893 wt-status.c:910
 #, c-format
 msgid "You are currently rebasing branch '%s' on '%s'."
 msgstr "Du håller på att ombasera grenen \"%s\" ovanpå \"%s\"."
 
-#: wt-status.c:884 wt-status.c:901
+#: wt-status.c:898 wt-status.c:915
 msgid "You are currently rebasing."
 msgstr "Du håller på med en ombasering."
 
-#: wt-status.c:887
+#: wt-status.c:901
 msgid "  (fix conflicts and then run \"git rebase --continue\")"
 msgstr "  (rätta konflikter och kör sedan \"git rebase --continue\")"
 
-#: wt-status.c:889
+#: wt-status.c:903
 msgid "  (use \"git rebase --skip\" to skip this patch)"
 msgstr "  (använd \"git rebase --skip\" för att hoppa över patchen)"
 
-#: wt-status.c:891
+#: wt-status.c:905
 msgid "  (use \"git rebase --abort\" to check out the original branch)"
 msgstr "  (använd \"git rebase --abort\" för att checka ut ursprungsgrenen)"
 
-#: wt-status.c:904
+#: wt-status.c:918
 msgid "  (all conflicts fixed: run \"git rebase --continue\")"
 msgstr "  (alla konflikter rättade: kör \"git rebase --continue\")"
 
-#: wt-status.c:908
+#: wt-status.c:922
 #, c-format
 msgid ""
 "You are currently splitting a commit while rebasing branch '%s' on '%s'."
@@ -1276,104 +1472,154 @@ msgstr ""
 "Du håller på att dela upp en incheckning medan du ombaserar grenen \"%s\" "
 "ovanpå \"%s\"."
 
-#: wt-status.c:913
+#: wt-status.c:927
 msgid "You are currently splitting a commit during a rebase."
 msgstr "Du håller på att dela upp en incheckning i en ombasering."
 
-#: wt-status.c:916
+#: wt-status.c:930
 msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
 msgstr "  (Så fort din arbetskatalog är ren, kör \"git rebase --continue\")"
 
-#: wt-status.c:920
+#: wt-status.c:934
 #, c-format
 msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
 msgstr ""
 "Du håller på att redigera en incheckning medan du ombaserar grenen \"%s\" "
 "ovanpå \"%s\"."
 
-#: wt-status.c:925
+#: wt-status.c:939
 msgid "You are currently editing a commit during a rebase."
 msgstr "Du håller på att redigera en incheckning under en ombasering."
 
-#: wt-status.c:928
+#: wt-status.c:942
 msgid "  (use \"git commit --amend\" to amend the current commit)"
 msgstr ""
 "  (använd \"git commit --amend\" för att lägga till på aktuell incheckning)"
 
-#: wt-status.c:930
+#: wt-status.c:944
 msgid ""
 "  (use \"git rebase --continue\" once you are satisfied with your changes)"
 msgstr "  (använd \"git rebase --continue\" när du är nöjd med dina ändringar)"
 
-#: wt-status.c:940
+#: wt-status.c:954
 msgid "You are currently cherry-picking."
 msgstr "Du håller på med en \"cherry-pick\"."
 
-#: wt-status.c:947
-msgid "  (all conflicts fixed: run \"git commit\")"
-msgstr "  (alla konflikter har rättats: kör \"git commit\")"
-
 #: wt-status.c:958
+msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
+msgstr "  (rätta konflikter och kör sedan \"git cherry-pick --continue\")"
+
+#: wt-status.c:961
+msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
+msgstr "  (alla konflikter rättade: kör \"git cherry-pick --continue\")"
+
+#: wt-status.c:963
+msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
+msgstr ""
+"  (använd \"git cherry-pick --abort\" för att avbryta \"cherry-pick\"-"
+"operationen)"
+
+#: wt-status.c:972
 #, c-format
-msgid "You are currently bisecting branch '%s'."
-msgstr "Du håller på med en \"bisect\" på grenen \"%s\"."
+msgid "You are currently reverting commit %s."
+msgstr "Du håller på med att ångra incheckningen %s."
+
+#: wt-status.c:977
+msgid "  (fix conflicts and run \"git revert --continue\")"
+msgstr "  (rätta konflikter och kör sedan \"git revert --continue\")"
+
+#: wt-status.c:980
+msgid "  (all conflicts fixed: run \"git revert --continue\")"
+msgstr "  (alla konflikter rättade: kör \"git revert --continue\")"
+
+#: wt-status.c:982
+msgid "  (use \"git revert --abort\" to cancel the revert operation)"
+msgstr "  (använd \"git revert --abort\" för att avbryta ångrandet)"
 
-#: wt-status.c:962
+#: wt-status.c:993
+#, c-format
+msgid "You are currently bisecting, started from branch '%s'."
+msgstr "Du håller på med en \"bisect\", startad från grenen \"%s\"."
+
+#: wt-status.c:997
 msgid "You are currently bisecting."
 msgstr "Du håller på med en \"bisect\"."
 
-#: wt-status.c:965
+#: wt-status.c:1000
 msgid "  (use \"git bisect reset\" to get back to the original branch)"
 msgstr ""
 "  (använd \"git bisect reset\" för att komma tillbaka till ursprungsgrenen)"
 
-#: wt-status.c:1064
+#: wt-status.c:1173
 msgid "On branch "
 msgstr "På grenen "
 
-#: wt-status.c:1071
+#: wt-status.c:1180
+msgid "rebase in progress; onto "
+msgstr "ombasering pågår; ovanpå"
+
+#: wt-status.c:1187
+msgid "HEAD detached at "
+msgstr "HEAD frånkopplad vid "
+
+#: wt-status.c:1189
+msgid "HEAD detached from "
+msgstr "HEAD frånkopplad från "
+
+#: wt-status.c:1192
 msgid "Not currently on any branch."
 msgstr "Inte på någon gren för närvarande."
 
-#: wt-status.c:1083
+#: wt-status.c:1209
 msgid "Initial commit"
 msgstr "Första incheckning"
 
-#: wt-status.c:1097
+#: wt-status.c:1223
 msgid "Untracked files"
 msgstr "Ospårade filer"
 
-#: wt-status.c:1099
+#: wt-status.c:1225
 msgid "Ignored files"
 msgstr "Ignorerade filer"
 
+#: wt-status.c:1229
+#, c-format
+msgid ""
+"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
+"may speed it up, but you have to be careful not to forget to add\n"
+"new files yourself (see 'git help status')."
+msgstr ""
+"Det tog %.2f sekunder att räkna upp ospårade filer. \"status -uno\"\n"
+"kan gå snabbare, men du måste vara försiktig så du inte glömmer\n"
+"lägga till nya filer själv (se \"git help status\")."
+
 # %s är nästa sträng eller tom.
-#: wt-status.c:1101
+#: wt-status.c:1235
 #, c-format
 msgid "Untracked files not listed%s"
 msgstr "Ospårade filer visas ej%s"
 
-#: wt-status.c:1103
+#: wt-status.c:1237
 msgid " (use -u option to show untracked files)"
 msgstr " (använd flaggan -u för att visa ospårade filer)"
 
-#: wt-status.c:1109
+#: wt-status.c:1243
 msgid "No changes"
 msgstr "Inga ändringar"
 
-#: wt-status.c:1114
+#: wt-status.c:1248
 #, c-format
 msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
 msgstr ""
 "inga ändringar att checka in (använd \"git add\" och/eller \"git commit -a"
 "\")\n"
 
-#: wt-status.c:1117
+#: wt-status.c:1251
 #, c-format
 msgid "no changes added to commit\n"
 msgstr "inga ändringar att checka in\n"
 
-#: wt-status.c:1120
+#: wt-status.c:1254
 #, c-format
 msgid ""
 "nothing added to commit but untracked files present (use \"git add\" to "
@@ -1382,52 +1628,52 @@ msgstr ""
 "inget köat för incheckning, men ospårade filer finns (spåra med \"git add"
 "\")\n"
 
-#: wt-status.c:1123
+#: wt-status.c:1257
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
 msgstr "inget köat för incheckning, men ospårade filer finns\n"
 
-#: wt-status.c:1126
+#: wt-status.c:1260
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
 msgstr "inget att checka in (skapa/kopiera filer och spåra med \"git add\")\n"
 
-#: wt-status.c:1129 wt-status.c:1134
+#: wt-status.c:1263 wt-status.c:1268
 #, c-format
 msgid "nothing to commit\n"
 msgstr "inget att checka in\n"
 
-#: wt-status.c:1132
+#: wt-status.c:1266
 #, c-format
 msgid "nothing to commit (use -u to show untracked files)\n"
 msgstr "inget att checka in (använd -u för att visa ospårade filer)\n"
 
-#: wt-status.c:1136
+#: wt-status.c:1270
 #, c-format
 msgid "nothing to commit, working directory clean\n"
 msgstr "inget att checka in, arbetskatalogen ren\n"
 
-#: wt-status.c:1244
+#: wt-status.c:1378
 msgid "HEAD (no branch)"
 msgstr "HEAD (ingen gren)"
 
-#: wt-status.c:1250
+#: wt-status.c:1384
 msgid "Initial commit on "
 msgstr "Första incheckning på "
 
-#: wt-status.c:1265
+#: wt-status.c:1399
 msgid "behind "
 msgstr "efter "
 
-#: wt-status.c:1268 wt-status.c:1271
+#: wt-status.c:1402 wt-status.c:1405
 msgid "ahead "
 msgstr "före "
 
-#: wt-status.c:1273
+#: wt-status.c:1407
 msgid ", behind "
 msgstr ", efter "
 
-#: compat/precompose_utf8.c:58 builtin/clone.c:341
+#: compat/precompose_utf8.c:58 builtin/clone.c:351
 #, c-format
 msgid "failed to unlink '%s'"
 msgstr "misslyckades ta bort länken \"%s\""
@@ -1436,193 +1682,226 @@ msgstr "misslyckades ta bort länken \"%s\""
 msgid "git add [options] [--] <pathspec>..."
 msgstr "git add [flaggor] [--] <sökväg>..."
 
-#: builtin/add.c:63
+#.
+#. * To be consistent with "git add -p" and most Git
+#. * commands, we should default to being tree-wide, but
+#. * this is not the original behavior and can't be
+#. * changed until users trained themselves not to type
+#. * "git add -u" or "git add -A". For now, we warn and
+#. * keep the old behavior. Later, the behavior can be changed
+#. * to tree-wide, keeping the warning for a while, and
+#. * eventually we can drop the warning.
+#.
+#: builtin/add.c:58
+#, c-format
+msgid ""
+"The behavior of 'git add %s (or %s)' with no path argument from a\n"
+"subdirectory of the tree will change in Git 2.0 and should not be used "
+"anymore.\n"
+"To add content for the whole tree, run:\n"
+"\n"
+"  git add %s :/\n"
+"  (or git add %s :/)\n"
+"\n"
+"To restrict the command to the current directory, run:\n"
+"\n"
+"  git add %s .\n"
+"  (or git add %s .)\n"
+"\n"
+"With the current Git version, the command is restricted to the current "
+"directory.\n"
+msgstr ""
+"Beteendet för \"git add %s (eller %s)\" utan sökvägsargument från en\n"
+"underkatalog i ett träd kommer ändras i Git 2.0 och bör inte längre "
+"användas.\n"
+"För att lägga till innehållet för hela trädet, använd:\n"
+"\n"
+"  git add %s :/\n"
+"  (eller git add %s :/)\n"
+"\n"
+"För att begränsa kommandot till aktuell katalog, använd:\n"
+"\n"
+"  git add %s .\n"
+"  (eller git add %s .)\n"
+"\n"
+"I nuvarande version av Git begränsas kommandot till aktuell katalog.\n"
+
+#: builtin/add.c:100
+#, c-format
+msgid ""
+"You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
+"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
+"Paths like '%s' that are\n"
+"removed from your working tree are ignored with this version of Git.\n"
+"\n"
+"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
+"  ignores paths you removed from your working tree.\n"
+"\n"
+"* 'git add --all <pathspec>' will let you also record the removals.\n"
+"\n"
+"Run 'git status' to check the paths you removed from your working tree.\n"
+msgstr ""
+"Du körde \"git add\" utan varken \"-A (--all)\" eller \"--ignore-removal\".\n"
+"Beteendet kommer ändras i Git 2.0 vad gäller sökvägar du tagit bort.\n"
+"Sökvägar som \"%s\", som har\n"
+"tagits bort från din arbetskatalog ignoreras i den här versionen av Git.\n"
+"\n"
+"* \"git add --ignore-removal <sökväg>\", som är förvalet just nu,\n"
+"  ignorerar sökvägar du har tagit bort från arbetskatalogen.\n"
+"\n"
+"* \"git add --all <sökväg>\" låter dig även registrera borttagningarna.\n"
+"\n"
+"Kör \"git status\" för att kontrollera sökvägarna du tagit bort från\n"
+"arbetskatalogen.\n"
+
+#: builtin/add.c:144
 #, c-format
 msgid "unexpected diff status %c"
 msgstr "diff-status %c förväntades inte"
 
-#: builtin/add.c:68 builtin/commit.c:231
+#: builtin/add.c:149 builtin/commit.c:252
 msgid "updating files failed"
 msgstr "misslyckades uppdatera filer"
 
-#: builtin/add.c:78
+#: builtin/add.c:163
 #, c-format
 msgid "remove '%s'\n"
 msgstr "ta bort \"%s\"\n"
 
-#: builtin/add.c:148
+#: builtin/add.c:253
 msgid "Unstaged changes after refreshing the index:"
 msgstr "Ospårade ändringar efter att ha uppdaterat indexet:"
 
-#: builtin/add.c:151 builtin/add.c:460 builtin/rm.c:275
+#: builtin/add.c:256 builtin/add.c:573 builtin/rm.c:337
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "sökvägsangivelsen \"%s\" motsvarade inte några filer"
 
-#: builtin/add.c:234
+#: builtin/add.c:339
 msgid "Could not read the index"
 msgstr "Kunde inte läsa indexet"
 
-#: builtin/add.c:244
+#: builtin/add.c:350
 #, c-format
 msgid "Could not open '%s' for writing."
 msgstr "Kunde inte öppna \"%s\" för skrivning"
 
-#: builtin/add.c:248
+#: builtin/add.c:354
 msgid "Could not write patch"
 msgstr "Kunde inte skriva patch"
 
-#: builtin/add.c:253
+#: builtin/add.c:359
 #, c-format
 msgid "Could not stat '%s'"
 msgstr "Kunde inte ta status på \"%s\""
 
-#: builtin/add.c:255
+#: builtin/add.c:361
 msgid "Empty patch. Aborted."
 msgstr "Tom patch. Avbryter."
 
-#: builtin/add.c:261
+#: builtin/add.c:367
 #, c-format
 msgid "Could not apply '%s'"
 msgstr "Kunde inte tillämpa \"%s\""
 
-#: builtin/add.c:271
+#: builtin/add.c:377
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr "Följande sökvägar ignoreras av en av dina .gitignore-filer:\n"
 
-#: builtin/add.c:277 builtin/clean.c:161 builtin/fetch.c:78 builtin/mv.c:63
-#: builtin/prune-packed.c:76 builtin/push.c:425 builtin/remote.c:1253
-#: builtin/rm.c:206
+#: builtin/add.c:394 builtin/clean.c:875 builtin/fetch.c:78 builtin/mv.c:63
+#: builtin/prune-packed.c:73 builtin/push.c:451 builtin/remote.c:1253
+#: builtin/rm.c:268
 msgid "dry run"
 msgstr "testkörning"
 
-#: builtin/add.c:278 builtin/apply.c:4405 builtin/check-ignore.c:19
-#: builtin/commit.c:1150 builtin/count-objects.c:82 builtin/fsck.c:613
-#: builtin/log.c:1522 builtin/mv.c:62 builtin/read-tree.c:112
+#: builtin/add.c:395 builtin/apply.c:4410 builtin/check-ignore.c:19
+#: builtin/commit.c:1220 builtin/count-objects.c:95 builtin/fsck.c:613
+#: builtin/log.c:1573 builtin/mv.c:62 builtin/read-tree.c:113
 msgid "be verbose"
 msgstr "var pratsam"
 
-#: builtin/add.c:280
+#: builtin/add.c:397
 msgid "interactive picking"
 msgstr "plocka interaktivt"
 
-#: builtin/add.c:281 builtin/checkout.c:1031 builtin/reset.c:258
+#: builtin/add.c:398 builtin/checkout.c:1073 builtin/reset.c:261
 msgid "select hunks interactively"
 msgstr "välj stycken interaktivt"
 
-#: builtin/add.c:282
+#: builtin/add.c:399
 msgid "edit current diff and apply"
 msgstr "redigera aktuell diff och applicera"
 
-#: builtin/add.c:283
+#: builtin/add.c:400
 msgid "allow adding otherwise ignored files"
 msgstr "tillåt lägga till annars ignorerade filer"
 
-#: builtin/add.c:284
+#: builtin/add.c:401
 msgid "update tracked files"
 msgstr "uppdatera spårade filer"
 
-#: builtin/add.c:285
+#: builtin/add.c:402
 msgid "record only the fact that the path will be added later"
 msgstr "registrera endast att sökvägen kommer läggas till senare"
 
-#: builtin/add.c:286
+#: builtin/add.c:403
 msgid "add changes from all tracked and untracked files"
 msgstr "lägg till ändringar från alla spårade och ospårade filer"
 
-#: builtin/add.c:287
+#. takes no arguments
+#: builtin/add.c:406
+msgid "ignore paths removed in the working tree (same as --no-all)"
+msgstr "ignorera sökvägar borttagna i arbetskatalogen (samma som --no-all)"
+
+#: builtin/add.c:408
 msgid "don't add, only refresh the index"
 msgstr "lägg inte till, uppdatera endast indexet"
 
-#: builtin/add.c:288
+#: builtin/add.c:409
 msgid "just skip files which cannot be added because of errors"
 msgstr "hoppa bara över filer som inte kan läggas till på grund av fel"
 
-#: builtin/add.c:289
+#: builtin/add.c:410
 msgid "check if - even missing - files are ignored in dry run"
 msgstr "se om - även saknade - filer ignoreras i testkörning"
 
-#: builtin/add.c:311
+#: builtin/add.c:432
 #, c-format
 msgid "Use -f if you really want to add them.\n"
 msgstr "Använd -f om du verkligen vill lägga till dem.\n"
 
-#: builtin/add.c:312
+#: builtin/add.c:433
 msgid "no files added"
 msgstr "inga filer har lagts till"
 
-#: builtin/add.c:318
+#: builtin/add.c:439
 msgid "adding files failed"
 msgstr "misslyckades lägga till filer"
 
-#.
-#. * To be consistent with "git add -p" and most Git
-#. * commands, we should default to being tree-wide, but
-#. * this is not the original behavior and can't be
-#. * changed until users trained themselves not to type
-#. * "git add -u" or "git add -A". For now, we warn and
-#. * keep the old behavior. Later, this warning can be
-#. * turned into a die(...), and eventually we may
-#. * reallow the command with a new behavior.
-#.
-#: builtin/add.c:335
-#, c-format
-msgid ""
-"The behavior of 'git add %s (or %s)' with no path argument from a\n"
-"subdirectory of the tree will change in Git 2.0 and should not be used "
-"anymore.\n"
-"To add content for the whole tree, run:\n"
-"\n"
-"  git add %s :/\n"
-"  (or git add %s :/)\n"
-"\n"
-"To restrict the command to the current directory, run:\n"
-"\n"
-"  git add %s .\n"
-"  (or git add %s .)\n"
-"\n"
-"With the current Git version, the command is restricted to the current "
-"directory."
-msgstr ""
-"Beteendet för \"git add %s (eller %s)\" utan sökvägsargument från en\n"
-"underkatalog i ett träd kommer ändras i Git 2.0 och bör inte längre "
-"användas.\n"
-"För att lägga till innehållet för hela trädet, använd:\n"
-"\n"
-"  git add %s :/\n"
-"  (eller git add %s :/)\n"
-"\n"
-"För att begränsa kommandot till aktuell katalog, använd:\n"
-"\n"
-"  git add %s .\n"
-"  (eller git add %s .)\n"
-"\n"
-"I nuvarande version av Git begränsas kommandot till aktuell katalog."
-
-#: builtin/add.c:381
+#: builtin/add.c:478
 msgid "-A and -u are mutually incompatible"
 msgstr "-A och -u är ömsesidigt inkompatibla"
 
-#: builtin/add.c:383
+#: builtin/add.c:496
 msgid "Option --ignore-missing can only be used together with --dry-run"
 msgstr "Flaggan --ignore-missing kan endast användas tillsammans med --dry-run"
 
-#: builtin/add.c:414
+#: builtin/add.c:526
 #, c-format
 msgid "Nothing specified, nothing added.\n"
 msgstr "Inget angivet, inget tillagt.\n"
 
-#: builtin/add.c:415
+#: builtin/add.c:527
 #, c-format
 msgid "Maybe you wanted to say 'git add .'?\n"
 msgstr "Kanske menade du att skriva \"git add .\"?\n"
 
-#: builtin/add.c:421 builtin/check-ignore.c:67 builtin/clean.c:204
-#: builtin/commit.c:291 builtin/mv.c:82 builtin/rm.c:235
+#: builtin/add.c:533 builtin/check-ignore.c:161 builtin/clean.c:919
+#: builtin/commit.c:312 builtin/mv.c:82 builtin/rm.c:297
 msgid "index file corrupt"
 msgstr "indexfilen trasig"
 
-#: builtin/add.c:481 builtin/apply.c:4501 builtin/mv.c:229 builtin/rm.c:370
+#: builtin/add.c:605 builtin/apply.c:4506 builtin/mv.c:229 builtin/rm.c:432
 msgid "Unable to write new index file"
 msgstr "Kunde inte skriva ny indexfil"
 
@@ -1755,24 +2034,24 @@ msgstr "kunde inte läsa symboliska länken %s"
 msgid "unable to open or read %s"
 msgstr "kunde inte öppna eller läsa %s"
 
-#: builtin/apply.c:2684
+#: builtin/apply.c:2688
 #, c-format
 msgid "invalid start of line: '%c'"
 msgstr "felaktig inledning på rad: \"%c\""
 
-#: builtin/apply.c:2802
+#: builtin/apply.c:2806
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
 msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
 msgstr[0] "Stycke %d lyckades på %d (offset %d rad)."
 msgstr[1] "Stycke %d lyckades på %d (offset %d rader)."
 
-#: builtin/apply.c:2814
+#: builtin/apply.c:2818
 #, c-format
 msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
 msgstr "Sammanhang reducerat till (%ld/%ld) för att tillämpa fragment vid %d"
 
-#: builtin/apply.c:2820
+#: builtin/apply.c:2824
 #, c-format
 msgid ""
 "while searching for:\n"
@@ -1781,318 +2060,318 @@ msgstr ""
 "vid sökning efter:\n"
 "%.*s"
 
-#: builtin/apply.c:2839
+#: builtin/apply.c:2843
 #, c-format
 msgid "missing binary patch data for '%s'"
 msgstr "saknar binära patchdata för \"%s\""
 
-#: builtin/apply.c:2942
+#: builtin/apply.c:2946
 #, c-format
 msgid "binary patch does not apply to '%s'"
 msgstr "binärpatchen kan inte tillämpas på \"%s\""
 
-#: builtin/apply.c:2948
+#: builtin/apply.c:2952
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
 msgstr "binärpatchen på \"%s\" ger felaktigt resultat (förväntade %s, fick %s)"
 
-#: builtin/apply.c:2969
+#: builtin/apply.c:2973
 #, c-format
 msgid "patch failed: %s:%ld"
 msgstr "patch misslyckades: %s:%ld"
 
-#: builtin/apply.c:3091
+#: builtin/apply.c:3095
 #, c-format
 msgid "cannot checkout %s"
 msgstr "kan inte checka ut %s"
 
-#: builtin/apply.c:3136 builtin/apply.c:3145 builtin/apply.c:3189
+#: builtin/apply.c:3140 builtin/apply.c:3149 builtin/apply.c:3194
 #, c-format
 msgid "read of %s failed"
 msgstr "misslyckades läsa %s"
 
-#: builtin/apply.c:3169 builtin/apply.c:3391
+#: builtin/apply.c:3174 builtin/apply.c:3396
 #, c-format
 msgid "path %s has been renamed/deleted"
 msgstr "sökvägen %s har ändrat namn/tagits bort"
 
-#: builtin/apply.c:3250 builtin/apply.c:3405
+#: builtin/apply.c:3255 builtin/apply.c:3410
 #, c-format
 msgid "%s: does not exist in index"
 msgstr "%s: finns inte i indexet"
 
-#: builtin/apply.c:3254 builtin/apply.c:3397 builtin/apply.c:3419
+#: builtin/apply.c:3259 builtin/apply.c:3402 builtin/apply.c:3424
 #, c-format
 msgid "%s: %s"
 msgstr "%s: %s"
 
-#: builtin/apply.c:3259 builtin/apply.c:3413
+#: builtin/apply.c:3264 builtin/apply.c:3418
 #, c-format
 msgid "%s: does not match index"
 msgstr "%s: motsvarar inte indexet"
 
-#: builtin/apply.c:3361
+#: builtin/apply.c:3366
 msgid "removal patch leaves file contents"
 msgstr "patch för borttagning lämnar kvar filinnehåll"
 
-#: builtin/apply.c:3430
+#: builtin/apply.c:3435
 #, c-format
 msgid "%s: wrong type"
 msgstr "%s: fel typ"
 
-#: builtin/apply.c:3432
+#: builtin/apply.c:3437
 #, c-format
 msgid "%s has type %o, expected %o"
 msgstr "%s har typen %o, förväntade %o"
 
-#: builtin/apply.c:3533
+#: builtin/apply.c:3538
 #, c-format
 msgid "%s: already exists in index"
 msgstr "%s: finns redan i indexet"
 
-#: builtin/apply.c:3536
+#: builtin/apply.c:3541
 #, c-format
 msgid "%s: already exists in working directory"
 msgstr "%s: finns redan i arbetskatalogen"
 
-#: builtin/apply.c:3556
+#: builtin/apply.c:3561
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o)"
 msgstr "nytt läge (%o) för %s motsvarar inte gammalt läge (%o)"
 
-#: builtin/apply.c:3561
+#: builtin/apply.c:3566
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
 msgstr "nytt läge (%o) för %s motsvarar inte gammalt läge (%o) för %s"
 
-#: builtin/apply.c:3569
+#: builtin/apply.c:3574
 #, c-format
 msgid "%s: patch does not apply"
 msgstr "%s: patchen kan inte tillämpas"
 
-#: builtin/apply.c:3582
+#: builtin/apply.c:3587
 #, c-format
 msgid "Checking patch %s..."
 msgstr "Kontrollerar patchen %s..."
 
-#: builtin/apply.c:3675 builtin/checkout.c:215 builtin/reset.c:124
+#: builtin/apply.c:3680 builtin/checkout.c:216 builtin/reset.c:127
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
 msgstr "make_cache_entry misslyckades för sökvägen \"%s\""
 
-#: builtin/apply.c:3818
+#: builtin/apply.c:3823
 #, c-format
 msgid "unable to remove %s from index"
 msgstr "kan inte ta bort %s från indexet"
 
-#: builtin/apply.c:3846
+#: builtin/apply.c:3851
 #, c-format
-msgid "corrupt patch for subproject %s"
-msgstr "trasig patch för underprojektet %s"
+msgid "corrupt patch for submodule %s"
+msgstr "trasig patch för undermodulen %s"
 
-#: builtin/apply.c:3850
+#: builtin/apply.c:3855
 #, c-format
 msgid "unable to stat newly created file '%s'"
 msgstr "kan inte ta status på nyligen skapade filen \"%s\""
 
-#: builtin/apply.c:3855
+#: builtin/apply.c:3860
 #, c-format
 msgid "unable to create backing store for newly created file %s"
 msgstr "kan inte skapa säkerhetsminne för nyligen skapade filen %s"
 
-#: builtin/apply.c:3858 builtin/apply.c:3966
+#: builtin/apply.c:3863 builtin/apply.c:3971
 #, c-format
 msgid "unable to add cache entry for %s"
 msgstr "kan inte lägga till cachepost för %s"
 
-#: builtin/apply.c:3891
+#: builtin/apply.c:3896
 #, c-format
 msgid "closing file '%s'"
 msgstr "stänger filen \"%s\""
 
-#: builtin/apply.c:3940
+#: builtin/apply.c:3945
 #, c-format
 msgid "unable to write file '%s' mode %o"
 msgstr "kan inte skriva filen \"%s\" läge %o"
 
-#: builtin/apply.c:4027
+#: builtin/apply.c:4032
 #, c-format
 msgid "Applied patch %s cleanly."
 msgstr "Tillämpade patchen %s rent."
 
-#: builtin/apply.c:4035
+#: builtin/apply.c:4040
 msgid "internal error"
 msgstr "internt fel"
 
 #. Say this even without --verbose
-#: builtin/apply.c:4038
+#: builtin/apply.c:4043
 #, c-format
 msgid "Applying patch %%s with %d reject..."
 msgid_plural "Applying patch %%s with %d rejects..."
 msgstr[0] "Tillämpade patchen %%s med %d refuserad..."
 msgstr[1] "Tillämpade patchen %%s med %d refuserade..."
 
-#: builtin/apply.c:4048
+#: builtin/apply.c:4053
 #, c-format
 msgid "truncating .rej filename to %.*s.rej"
 msgstr "trunkerar .rej-filnamnet till %.*s.rej"
 
-#: builtin/apply.c:4069
+#: builtin/apply.c:4074
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Stycke %d tillämpades rent."
 
-#: builtin/apply.c:4072
+#: builtin/apply.c:4077
 #, c-format
 msgid "Rejected hunk #%d."
 msgstr "Refuserar stycke %d."
 
-#: builtin/apply.c:4222
+#: builtin/apply.c:4227
 msgid "unrecognized input"
 msgstr "indata känns inte igen"
 
-#: builtin/apply.c:4233
+#: builtin/apply.c:4238
 msgid "unable to read index file"
 msgstr "kan inte läsa indexfilen"
 
-#: builtin/apply.c:4352 builtin/apply.c:4355 builtin/clone.c:91
+#: builtin/apply.c:4357 builtin/apply.c:4360 builtin/clone.c:91
 #: builtin/fetch.c:63
 msgid "path"
 msgstr "sökväg"
 
-#: builtin/apply.c:4353
+#: builtin/apply.c:4358
 msgid "don't apply changes matching the given path"
 msgstr "tillämpa inte ändringar som motsvarar given sökväg"
 
-#: builtin/apply.c:4356
+#: builtin/apply.c:4361
 msgid "apply changes matching the given path"
 msgstr "tillämpa ändringar som motsvarar given sökväg"
 
-#: builtin/apply.c:4358
+#: builtin/apply.c:4363
 msgid "num"
 msgstr "antal"
 
-#: builtin/apply.c:4359
+#: builtin/apply.c:4364
 msgid "remove <num> leading slashes from traditional diff paths"
 msgstr "ta bort <antal> inledande snedstreck från traditionella diff-sökvägar"
 
-#: builtin/apply.c:4362
+#: builtin/apply.c:4367
 msgid "ignore additions made by the patch"
 msgstr "ignorera tillägg gjorda av patchen"
 
-#: builtin/apply.c:4364
+#: builtin/apply.c:4369
 msgid "instead of applying the patch, output diffstat for the input"
 msgstr "istället för att tillämpa patchen, skriv ut diffstat för indata"
 
-#: builtin/apply.c:4368
+#: builtin/apply.c:4373
 msgid "show number of added and deleted lines in decimal notation"
 msgstr "visa antal tillagda och borttagna rader decimalt"
 
-#: builtin/apply.c:4370
+#: builtin/apply.c:4375
 msgid "instead of applying the patch, output a summary for the input"
 msgstr "istället för att tillämpa patchen, skriv ut en summering av indata"
 
-#: builtin/apply.c:4372
+#: builtin/apply.c:4377
 msgid "instead of applying the patch, see if the patch is applicable"
 msgstr "istället för att tillämpa patchen, se om patchen kan tillämpas"
 
-#: builtin/apply.c:4374
+#: builtin/apply.c:4379
 msgid "make sure the patch is applicable to the current index"
 msgstr "se till att patchen kan tillämpas på aktuellt index"
 
-#: builtin/apply.c:4376
+#: builtin/apply.c:4381
 msgid "apply a patch without touching the working tree"
 msgstr "tillämpa en patch utan att röra arbetskatalogen"
 
-#: builtin/apply.c:4378
+#: builtin/apply.c:4383
 msgid "also apply the patch (use with --stat/--summary/--check)"
 msgstr "tillämpa också patchen (använd med --stat/--summary/--check)"
 
-#: builtin/apply.c:4380
+#: builtin/apply.c:4385
 msgid "attempt three-way merge if a patch does not apply"
 msgstr "försök en trevägssammanslagning om patchen inte kan tillämpas"
 
-#: builtin/apply.c:4382
+#: builtin/apply.c:4387
 msgid "build a temporary index based on embedded index information"
 msgstr "bygg ett temporärt index baserat på inbyggd indexinformation"
 
-#: builtin/apply.c:4384 builtin/checkout-index.c:197 builtin/ls-files.c:463
+#: builtin/apply.c:4389 builtin/checkout-index.c:197 builtin/ls-files.c:462
 msgid "paths are separated with NUL character"
 msgstr "sökvägar avdelas med NUL-tecken"
 
-#: builtin/apply.c:4387
+#: builtin/apply.c:4392
 msgid "ensure at least <n> lines of context match"
 msgstr "se till att åtminstone <n> rader sammanhang är lika"
 
-#: builtin/apply.c:4388
+#: builtin/apply.c:4393
 msgid "action"
 msgstr "åtgärd"
 
-#: builtin/apply.c:4389
+#: builtin/apply.c:4394
 msgid "detect new or modified lines that have whitespace errors"
 msgstr "detektera nya eller ändrade rader som har fel i blanktecken"
 
-#: builtin/apply.c:4392 builtin/apply.c:4395
+#: builtin/apply.c:4397 builtin/apply.c:4400
 msgid "ignore changes in whitespace when finding context"
 msgstr "ignorera ändringar i blanktecken för sammanhang"
 
-#: builtin/apply.c:4398
+#: builtin/apply.c:4403
 msgid "apply the patch in reverse"
 msgstr "tillämpa patchen baklänges"
 
-#: builtin/apply.c:4400
+#: builtin/apply.c:4405
 msgid "don't expect at least one line of context"
 msgstr "förvänta inte minst en rad sammanhang"
 
-#: builtin/apply.c:4402
+#: builtin/apply.c:4407
 msgid "leave the rejected hunks in corresponding *.rej files"
 msgstr "lämna refuserade stycken i motsvarande *.rej-filer"
 
-#: builtin/apply.c:4404
+#: builtin/apply.c:4409
 msgid "allow overlapping hunks"
 msgstr "tillåt överlappande stycken"
 
-#: builtin/apply.c:4407
+#: builtin/apply.c:4412
 msgid "tolerate incorrectly detected missing new-line at the end of file"
 msgstr "tolerera felaktigt detekterade saknade nyradstecken vid filslut"
 
-#: builtin/apply.c:4410
+#: builtin/apply.c:4415
 msgid "do not trust the line counts in the hunk headers"
 msgstr "lite inte på antalet linjer i styckehuvuden"
 
-#: builtin/apply.c:4412
+#: builtin/apply.c:4417
 msgid "root"
 msgstr "rot"
 
-#: builtin/apply.c:4413
+#: builtin/apply.c:4418
 msgid "prepend <root> to all filenames"
 msgstr "lägg till <rot> i alla filnamn"
 
-#: builtin/apply.c:4435
+#: builtin/apply.c:4440
 msgid "--3way outside a repository"
 msgstr "--3way utanför arkiv"
 
-#: builtin/apply.c:4443
+#: builtin/apply.c:4448
 msgid "--index outside a repository"
 msgstr "--index utanför arkiv"
 
-#: builtin/apply.c:4446
+#: builtin/apply.c:4451
 msgid "--cached outside a repository"
 msgstr "--cached utanför arkiv"
 
-#: builtin/apply.c:4462
+#: builtin/apply.c:4467
 #, c-format
 msgid "can't open patch '%s'"
 msgstr "kan inte öppna patchen \"%s\""
 
-#: builtin/apply.c:4476
+#: builtin/apply.c:4481
 #, c-format
 msgid "squelched %d whitespace error"
 msgid_plural "squelched %d whitespace errors"
 msgstr[0] "undertryckte %d fel i blanksteg"
 msgstr[1] "undertryckte %d fel i blanksteg"
 
-#: builtin/apply.c:4482 builtin/apply.c:4492
+#: builtin/apply.c:4487 builtin/apply.c:4497
 #, c-format
 msgid "%d line adds whitespace errors."
 msgid_plural "%d lines add whitespace errors."
@@ -2116,21 +2395,21 @@ msgstr "git archive: Fjärr utan URL"
 msgid "git archive: expected ACK/NAK, got EOF"
 msgstr "git archive: förväntade ACK/NAK, fick EOF"
 
-#: builtin/archive.c:63
+#: builtin/archive.c:61
 #, c-format
 msgid "git archive: NACK %s"
 msgstr "git archive: NACK %s"
 
-#: builtin/archive.c:65
+#: builtin/archive.c:63
 #, c-format
 msgid "remote error: %s"
 msgstr "fjärrfel: %s"
 
-#: builtin/archive.c:66
+#: builtin/archive.c:64
 msgid "git archive: protocol error"
 msgstr "git archive: protokollfel"
 
-#: builtin/archive.c:71
+#: builtin/archive.c:68
 msgid "git archive: expected a flush"
 msgstr "git archive: förväntade en tömning (flush)"
 
@@ -2146,123 +2425,123 @@ msgstr "utför 'git bisect next'"
 msgid "update BISECT_HEAD instead of checking out the current commit"
 msgstr "uppdatera BISECT_HEAD istället för att checka ut aktuell incheckning"
 
-#: builtin/blame.c:25
+#: builtin/blame.c:26
 msgid "git blame [options] [rev-opts] [rev] [--] file"
 msgstr "git blame [flaggor] [rev-flaggor] [rev] [--] fil"
 
-#: builtin/blame.c:30 builtin/shortlog.c:15
+#: builtin/blame.c:31
 msgid "[rev-opts] are documented in git-rev-list(1)"
 msgstr "[rev-flaggor] dokumenteras i git-rev-list(1)"
 
-#: builtin/blame.c:2350
+#: builtin/blame.c:2276
 msgid "Show blame entries as we find them, incrementally"
 msgstr "Visa klandringsposter när vi hittar dem, interaktivt"
 
-#: builtin/blame.c:2351
+#: builtin/blame.c:2277
 msgid "Show blank SHA-1 for boundary commits (Default: off)"
 msgstr "Visa blank SHA-1 för gränsincheckningar (Standard: av)"
 
-#: builtin/blame.c:2352
+#: builtin/blame.c:2278
 msgid "Do not treat root commits as boundaries (Default: off)"
 msgstr "Behandla inte rotincheckningar som gränser (Standard: av)"
 
-#: builtin/blame.c:2353
+#: builtin/blame.c:2279
 msgid "Show work cost statistics"
 msgstr "Visa statistik över arbetskostnad"
 
-#: builtin/blame.c:2354
+#: builtin/blame.c:2280
 msgid "Show output score for blame entries"
 msgstr "Visa utdatapoäng för klandringsposter"
 
-#: builtin/blame.c:2355
+#: builtin/blame.c:2281
 msgid "Show original filename (Default: auto)"
 msgstr "Visa originalfilnamn (Standard: auto)"
 
-#: builtin/blame.c:2356
+#: builtin/blame.c:2282
 msgid "Show original linenumber (Default: off)"
 msgstr "Visa ursprungligt radnummer (Standard: av)"
 
-#: builtin/blame.c:2357
+#: builtin/blame.c:2283
 msgid "Show in a format designed for machine consumption"
 msgstr "Visa i ett format avsett för maskinkonsumtion"
 
-#: builtin/blame.c:2358
+#: builtin/blame.c:2284
 msgid "Show porcelain format with per-line commit information"
 msgstr "Visa porslinsformat med per-rad-incheckningsinformation"
 
-#: builtin/blame.c:2359
+#: builtin/blame.c:2285
 msgid "Use the same output mode as git-annotate (Default: off)"
 msgstr "Använd samma utdataläge som git-annotate (Standard: av)"
 
-#: builtin/blame.c:2360
+#: builtin/blame.c:2286
 msgid "Show raw timestamp (Default: off)"
 msgstr "Visa rå tidsstämpel (Standard: av)"
 
-#: builtin/blame.c:2361
+#: builtin/blame.c:2287
 msgid "Show long commit SHA1 (Default: off)"
 msgstr "Visa lång inchecknings-SHA1 (Standard: av)"
 
-#: builtin/blame.c:2362
+#: builtin/blame.c:2288
 msgid "Suppress author name and timestamp (Default: off)"
 msgstr "Undertryck författarnamn och tidsstämpel (Standard: av)"
 
-#: builtin/blame.c:2363
+#: builtin/blame.c:2289
 msgid "Show author email instead of name (Default: off)"
 msgstr "Visa författarens e-post istället för namn (Standard: av)"
 
-#: builtin/blame.c:2364
+#: builtin/blame.c:2290
 msgid "Ignore whitespace differences"
 msgstr "Ignorera ändringar i blanksteg"
 
-#: builtin/blame.c:2365
+#: builtin/blame.c:2291
 msgid "Spend extra cycles to find better match"
 msgstr "Slösa extra cykler med att hitta bättre träff"
 
-#: builtin/blame.c:2366
+#: builtin/blame.c:2292
 msgid "Use revisions from <file> instead of calling git-rev-list"
 msgstr "Använd revisioner från <fil> istället för att anropa git-rev-list"
 
-#: builtin/blame.c:2367
+#: builtin/blame.c:2293
 msgid "Use <file>'s contents as the final image"
 msgstr "Använd <fil>s innehåll som slutgiltig bild"
 
-#: builtin/blame.c:2368 builtin/blame.c:2369
+#: builtin/blame.c:2294 builtin/blame.c:2295
 msgid "score"
 msgstr "poäng"
 
-#: builtin/blame.c:2368
+#: builtin/blame.c:2294
 msgid "Find line copies within and across files"
 msgstr "Hitta kopierade rader inuti och mellan filer"
 
-#: builtin/blame.c:2369
+#: builtin/blame.c:2295
 msgid "Find line movements within and across files"
 msgstr "Hitta flyttade rader inuti och mellan filer"
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "n,m"
 msgstr "n,m"
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "Process only line range n,m, counting from 1"
 msgstr "Behandla endast radintervallet n,m, med början på 1"
 
-#: builtin/branch.c:23
+#: builtin/branch.c:24
 msgid "git branch [options] [-r | -a] [--merged | --no-merged]"
 msgstr "git branch [flaggor] [-r | -a] [--merged | --no-merged]"
 
-#: builtin/branch.c:24
+#: builtin/branch.c:25
 msgid "git branch [options] [-l] [-f] <branchname> [<start-point>]"
 msgstr "git branch [flaggor] [-l] [-f] <grennamn> [<startpunkt>]"
 
-#: builtin/branch.c:25
+#: builtin/branch.c:26
 msgid "git branch [options] [-r] (-d | -D) <branchname>..."
 msgstr "git branch [flaggor] [-r] (-d | -D) <grennamn>..."
 
-#: builtin/branch.c:26
+#: builtin/branch.c:27
 msgid "git branch [options] (-m | -M) [<oldbranch>] <newbranch>"
 msgstr "git branch [flaggor] (-m | -M) [<gammal_gren>] <ny_gren>"
 
-#: builtin/branch.c:145
+#: builtin/branch.c:150
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
@@ -2271,7 +2550,7 @@ msgstr ""
 "tar bort grenen \"%s\" som har slagits ihop med\n"
 "         \"%s\", men ännu inte slagits ihop med HEAD."
 
-#: builtin/branch.c:149
+#: builtin/branch.c:154
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
@@ -2280,12 +2559,12 @@ msgstr ""
 "tar inte bort grenen \"%s\" som inte har slagits ihop med\n"
 "         \"%s\", trots att den har slagits ihop med HEAD."
 
-#: builtin/branch.c:163
+#: builtin/branch.c:168
 #, c-format
 msgid "Couldn't look up commit object for '%s'"
 msgstr "Kunde inte slå upp incheckningsobjekt för \"%s\""
 
-#: builtin/branch.c:167
+#: builtin/branch.c:172
 #, c-format
 msgid ""
 "The branch '%s' is not fully merged.\n"
@@ -2294,288 +2573,332 @@ msgstr ""
 "Grenen \"%s\" har inte slagits samman i sin helhet.\n"
 "Om du är säker på att du vill ta bort den, kör \"git branch -D %s\"."
 
-#: builtin/branch.c:180
+#: builtin/branch.c:185
 msgid "Update of config-file failed"
 msgstr "Misslyckades uppdatera konfigurationsfil"
 
-#: builtin/branch.c:208
+#: builtin/branch.c:213
 msgid "cannot use -a with -d"
 msgstr "kan inte ange -a med -d"
 
-#: builtin/branch.c:214
+#: builtin/branch.c:219
 msgid "Couldn't look up commit object for HEAD"
 msgstr "Kunde inte slå upp incheckningsobjekt för HEAD"
 
-#: builtin/branch.c:222
+#: builtin/branch.c:227
 #, c-format
 msgid "Cannot delete the branch '%s' which you are currently on."
 msgstr "Kan inte ta bort grenen \"%s\" som du befinner dig på för närvarande."
 
-#: builtin/branch.c:235
+#: builtin/branch.c:240
 #, c-format
 msgid "remote branch '%s' not found."
 msgstr "fjärrgrenen \"%s\" hittades inte."
 
-#: builtin/branch.c:236
+#: builtin/branch.c:241
 #, c-format
 msgid "branch '%s' not found."
 msgstr "grenen \"%s\" hittades inte."
 
-#: builtin/branch.c:250
+#: builtin/branch.c:255
 #, c-format
 msgid "Error deleting remote branch '%s'"
 msgstr "Fel vid borttagning av fjärrgrenen \"%s\""
 
-#: builtin/branch.c:251
+#: builtin/branch.c:256
 #, c-format
 msgid "Error deleting branch '%s'"
 msgstr "Fel vid borttagning av grenen \"%s\""
 
-#: builtin/branch.c:258
+#: builtin/branch.c:263
 #, c-format
 msgid "Deleted remote branch %s (was %s).\n"
 msgstr "Tog bort fjärrgrenen %s (var %s).\n"
 
-#: builtin/branch.c:259
+#: builtin/branch.c:264
 #, c-format
 msgid "Deleted branch %s (was %s).\n"
 msgstr "Tog bort grenen %s (var %s).\n"
 
-#: builtin/branch.c:361
+#: builtin/branch.c:366
 #, c-format
 msgid "branch '%s' does not point at a commit"
 msgstr "grenen \"%s\" pekar inte på en incheckning"
 
-#: builtin/branch.c:433
+#: builtin/branch.c:453
 #, c-format
 msgid "[%s: behind %d]"
 msgstr "[%s: bakom %d] "
 
-#: builtin/branch.c:435
+#: builtin/branch.c:455
 #, c-format
 msgid "[behind %d]"
 msgstr "[bakom %d] "
 
-#: builtin/branch.c:439
+#: builtin/branch.c:459
 #, c-format
 msgid "[%s: ahead %d]"
 msgstr "[%s: före %d] "
 
-#: builtin/branch.c:441
+#: builtin/branch.c:461
 #, c-format
 msgid "[ahead %d]"
 msgstr "[före %d] "
 
-#: builtin/branch.c:444
+#: builtin/branch.c:464
 #, c-format
 msgid "[%s: ahead %d, behind %d]"
 msgstr "[%s: före %d, bakom %d] "
 
-#: builtin/branch.c:447
+#: builtin/branch.c:467
 #, c-format
 msgid "[ahead %d, behind %d]"
 msgstr "[före %d, bakom %d] "
 
-#: builtin/branch.c:469
+#: builtin/branch.c:490
 msgid " **** invalid ref ****"
 msgstr " **** ogiltig ref ****"
 
-#: builtin/branch.c:560
+#: builtin/branch.c:582
+#, c-format
+msgid "(no branch, rebasing %s)"
+msgstr "(ingen gren, ombaserar %s)"
+
+#: builtin/branch.c:585
+#, c-format
+msgid "(no branch, bisect started on %s)"
+msgstr "(ingen gren, \"bisect\" startad på %s)"
+
+#: builtin/branch.c:588
+#, c-format
+msgid "(detached from %s)"
+msgstr "(frånkopplad från %s)"
+
+#: builtin/branch.c:591
 msgid "(no branch)"
 msgstr "(ingen gren)"
 
-#: builtin/branch.c:593
+#: builtin/branch.c:637
 #, c-format
 msgid "object '%s' does not point to a commit"
 msgstr "objektet \"%s\" pekar på en incheckning"
 
-#: builtin/branch.c:625
+#: builtin/branch.c:669
 msgid "some refs could not be read"
 msgstr "vissa referenser kunde inte läsas"
 
-#: builtin/branch.c:638
+#: builtin/branch.c:682
 msgid "cannot rename the current branch while not on any."
 msgstr ""
 "kunde inte byta namn på aktuell gren när du inte befinner dig på någon."
 
-#: builtin/branch.c:648
+#: builtin/branch.c:692
 #, c-format
 msgid "Invalid branch name: '%s'"
 msgstr "Felaktigt namn på gren: \"%s\""
 
-#: builtin/branch.c:663
+#: builtin/branch.c:707
 msgid "Branch rename failed"
 msgstr "Misslyckades byta namn på gren"
 
-#: builtin/branch.c:667
+#: builtin/branch.c:711
 #, c-format
 msgid "Renamed a misnamed branch '%s' away"
 msgstr "Bytte bort namn på en felaktigt namngiven gren \"%s\""
 
-#: builtin/branch.c:671
+#: builtin/branch.c:715
 #, c-format
 msgid "Branch renamed to %s, but HEAD is not updated!"
 msgstr "Grenen namnbytt till %s, men HEAD har inte uppdaterats!"
 
-#: builtin/branch.c:678
+#: builtin/branch.c:722
 msgid "Branch is renamed, but update of config-file failed"
 msgstr "Grenen namnbytt, men misslyckades uppdatera konfigurationsfilen"
 
-#: builtin/branch.c:693
+#: builtin/branch.c:737
 #, c-format
 msgid "malformed object name %s"
 msgstr "felformat objektnamn %s"
 
-#: builtin/branch.c:717
+#: builtin/branch.c:761
 #, c-format
 msgid "could not write branch description template: %s"
 msgstr "kunde inte skriva grenbeskrivningsmall: %s"
 
-#: builtin/branch.c:747
+#: builtin/branch.c:791
 msgid "Generic options"
 msgstr "Allmänna flaggor"
 
-#: builtin/branch.c:749
+#: builtin/branch.c:793
 msgid "show hash and subject, give twice for upstream branch"
 msgstr "visa hash och ärenderad, ange två gånger för uppströmsgren"
 
-#: builtin/branch.c:750
+#: builtin/branch.c:794
 msgid "suppress informational messages"
 msgstr "undertryck informationsmeddelanden"
 
-#: builtin/branch.c:751
+#: builtin/branch.c:795
 msgid "set up tracking mode (see git-pull(1))"
 msgstr "ställ in spårningsläge (se git-pull(1))"
 
-#: builtin/branch.c:753
+#: builtin/branch.c:797
 msgid "change upstream info"
 msgstr "ändra uppströmsinformation"
 
-#: builtin/branch.c:757
+#: builtin/branch.c:801
 msgid "use colored output"
 msgstr "använd färgad utdata"
 
-#: builtin/branch.c:758
+#: builtin/branch.c:802
 msgid "act on remote-tracking branches"
 msgstr "arbeta på fjärrspårande grenar"
 
-#: builtin/branch.c:761 builtin/branch.c:767 builtin/branch.c:788
-#: builtin/branch.c:794 builtin/commit.c:1366 builtin/commit.c:1367
-#: builtin/commit.c:1368 builtin/commit.c:1369 builtin/tag.c:468
+#: builtin/branch.c:805 builtin/branch.c:811 builtin/branch.c:832
+#: builtin/branch.c:838 builtin/commit.c:1433 builtin/commit.c:1434
+#: builtin/commit.c:1435 builtin/commit.c:1436 builtin/tag.c:468
 msgid "commit"
 msgstr "incheckning"
 
-#: builtin/branch.c:762 builtin/branch.c:768
+#: builtin/branch.c:806 builtin/branch.c:812
 msgid "print only branches that contain the commit"
 msgstr "visa endast grenar som innehåller incheckningen"
 
-#: builtin/branch.c:774
+#: builtin/branch.c:818
 msgid "Specific git-branch actions:"
 msgstr "Specifika git-branch-åtgärder:"
 
-#: builtin/branch.c:775
+#: builtin/branch.c:819
 msgid "list both remote-tracking and local branches"
 msgstr "visa både fjärrspårande och lokala grenar"
 
-#: builtin/branch.c:777
+#: builtin/branch.c:821
 msgid "delete fully merged branch"
 msgstr "ta bort helt sammanslagen gren"
 
-#: builtin/branch.c:778
+#: builtin/branch.c:822
 msgid "delete branch (even if not merged)"
 msgstr "ta bort gren (även om inte helt sammanslagen)"
 
-#: builtin/branch.c:779
+#: builtin/branch.c:823
 msgid "move/rename a branch and its reflog"
 msgstr "flytta/ta bort en gren och dess reflogg"
 
-#: builtin/branch.c:780
+#: builtin/branch.c:824
 msgid "move/rename a branch, even if target exists"
 msgstr "flytta/ta bort en gren, även om målet finns"
 
-#: builtin/branch.c:781
+#: builtin/branch.c:825
 msgid "list branch names"
 msgstr "lista namn på grenar"
 
-#: builtin/branch.c:782
+#: builtin/branch.c:826
 msgid "create the branch's reflog"
 msgstr "skapa grenens reflogg"
 
-#: builtin/branch.c:784
+#: builtin/branch.c:828
 msgid "edit the description for the branch"
 msgstr "redigera beskrivning för grenen"
 
-#: builtin/branch.c:785
+#: builtin/branch.c:829
 msgid "force creation (when already exists)"
 msgstr "tvinga skapande (när den redan finns)"
 
-#: builtin/branch.c:788
+#: builtin/branch.c:832
 msgid "print only not merged branches"
 msgstr "visa endast ej sammanslagna grenar"
 
-#: builtin/branch.c:794
+#: builtin/branch.c:838
 msgid "print only merged branches"
 msgstr "visa endast sammanslagna grenar"
 
-#: builtin/branch.c:798
+#: builtin/branch.c:842
 msgid "list branches in columns"
 msgstr "visa grenar i spalter"
 
-#: builtin/branch.c:811
+#: builtin/branch.c:855
 msgid "Failed to resolve HEAD as a valid ref."
 msgstr "Misslyckades slå upp HEAD som giltig referens"
 
-#: builtin/branch.c:816 builtin/clone.c:561
+#: builtin/branch.c:860 builtin/clone.c:630
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD hittades inte under refs/heads!"
 
-#: builtin/branch.c:839
+#: builtin/branch.c:883
 msgid "--column and --verbose are incompatible"
 msgstr "--column och --verbose är inkompatibla"
 
-#: builtin/branch.c:845
+#: builtin/branch.c:889 builtin/branch.c:928
 msgid "branch name required"
 msgstr "grennamn krävs"
 
-#: builtin/branch.c:860
+#: builtin/branch.c:904
 msgid "Cannot give description to detached HEAD"
 msgstr "Kan inte beskriva frånkopplad HEAD"
 
-#: builtin/branch.c:865
+#: builtin/branch.c:909
 msgid "cannot edit description of more than one branch"
 msgstr "kan inte redigera beskrivning för mer än en gren"
 
-#: builtin/branch.c:872
+#: builtin/branch.c:916
 #, c-format
 msgid "No commit on branch '%s' yet."
 msgstr "Inga incheckningar på grenen \"%s\" ännu"
 
-#: builtin/branch.c:875
+#: builtin/branch.c:919
 #, c-format
 msgid "No branch named '%s'."
 msgstr "Ingen gren vid namnet \"%s\"."
 
-#: builtin/branch.c:888
+#: builtin/branch.c:934
 msgid "too many branches for a rename operation"
 msgstr "för många grenar för namnbyte"
 
-#: builtin/branch.c:893
+#: builtin/branch.c:939
+msgid "too many branches to set new upstream"
+msgstr "för många grenar för att byta uppström"
+
+#: builtin/branch.c:943
+#, c-format
+msgid ""
+"could not set upstream of HEAD to %s when it does not point to any branch."
+msgstr ""
+"kunde inte sätta uppström för HEAD till %s när det inte pekar mot någon gren."
+
+#: builtin/branch.c:946 builtin/branch.c:968 builtin/branch.c:990
+#, c-format
+msgid "no such branch '%s'"
+msgstr "okänd gren \"%s\""
+
+#: builtin/branch.c:950
 #, c-format
 msgid "branch '%s' does not exist"
 msgstr "grenen \"%s\" finns inte"
 
-#: builtin/branch.c:905
+#: builtin/branch.c:962
+msgid "too many branches to unset upstream"
+msgstr "för många grenar för att ta bort uppström"
+
+#: builtin/branch.c:966
+msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgstr ""
+"kunde inte ta bort uppström för HEAD när det inte pekar mot någon gren."
+
+#: builtin/branch.c:972
 #, c-format
 msgid "Branch '%s' has no upstream information"
 msgstr "Grenen \"%s\" har ingen uppströmsinformation"
 
-#: builtin/branch.c:920
+#: builtin/branch.c:987
+msgid "it does not make sense to create 'HEAD' manually"
+msgstr "kan inte skapa \"HEAD\" manuellt"
+
+#: builtin/branch.c:993
 msgid "-a and -r options to 'git branch' do not make sense with a branch name"
 msgstr ""
 "flaggorna -a och -r på \"git branch\" kan inte anges tillsammans med ett "
 "grennamn"
 
-#: builtin/branch.c:923
+#: builtin/branch.c:996
 #, c-format
 msgid ""
 "The --set-upstream flag is deprecated and will be removed. Consider using --"
@@ -2584,7 +2907,7 @@ msgstr ""
 "Flaggan --set-upstream rekommenderas ej och kommer tas bort. Använd --track "
 "eller --set-upstream-to\n"
 
-#: builtin/branch.c:940
+#: builtin/branch.c:1013
 #, c-format
 msgid ""
 "\n"
@@ -2595,12 +2918,12 @@ msgstr ""
 "Om du vill göra så att \"%s\" spårar \"%s\" gör du så här:\n"
 "\n"
 
-#: builtin/branch.c:941
+#: builtin/branch.c:1014
 #, c-format
 msgid "    git branch -d %s\n"
 msgstr "    git branch -d %s\n"
 
-#: builtin/branch.c:942
+#: builtin/branch.c:1015
 #, c-format
 msgid "    git branch --set-upstream-to %s\n"
 msgstr "    git branch --set-upstream-to %s\n"
@@ -2618,43 +2941,43 @@ msgstr "Behöver ett arkiv för att skapa ett paket (bundle)."
 msgid "Need a repository to unbundle."
 msgstr "Behöver ett arkiv för att packa upp ett paket (bundle)."
 
-#: builtin/cat-file.c:247
+#: builtin/cat-file.c:285
 msgid "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"
 msgstr "git cat-file (-t|-s|-e|-p|<typ>|--textconv) <objekt>"
 
-#: builtin/cat-file.c:248
+#: builtin/cat-file.c:286
 msgid "git cat-file (--batch|--batch-check) < <list_of_objects>"
 msgstr "git cat-file (--batch|--batch-check) < <objektlista>"
 
-#: builtin/cat-file.c:266
+#: builtin/cat-file.c:323
 msgid "<type> can be one of: blob, tree, commit, tag"
 msgstr "<typ> kan vara en av: blob, tree, commit, tag"
 
-#: builtin/cat-file.c:267
+#: builtin/cat-file.c:324
 msgid "show object type"
 msgstr "visa objekttyp"
 
-#: builtin/cat-file.c:268
+#: builtin/cat-file.c:325
 msgid "show object size"
 msgstr "visa objektstorlek"
 
-#: builtin/cat-file.c:270
+#: builtin/cat-file.c:327
 msgid "exit with zero when there's no error"
 msgstr "avsluta med noll när det inte uppstått något fel"
 
-#: builtin/cat-file.c:271
+#: builtin/cat-file.c:328
 msgid "pretty-print object's content"
 msgstr "visa objektets innehåll snyggt"
 
-#: builtin/cat-file.c:273
+#: builtin/cat-file.c:330
 msgid "for blob objects, run textconv on object's content"
 msgstr "för blob-objekt, kör textconv på objektets innehåll"
 
-#: builtin/cat-file.c:275
+#: builtin/cat-file.c:332
 msgid "show info and content of objects fed from the standard input"
 msgstr "visa information och innehåll för objekt som listas på standard in"
 
-#: builtin/cat-file.c:278
+#: builtin/cat-file.c:335
 msgid "show info about objects fed from the standard input"
 msgstr "visa information för objekt som listas på standard in"
 
@@ -2682,30 +3005,55 @@ msgstr "läs filnamn från standard in"
 msgid "input paths are terminated by a null character"
 msgstr "sökvägar avdelas med null-tecken"
 
-#: builtin/check-ignore.c:18 builtin/checkout.c:1012 builtin/gc.c:177
+#: builtin/check-ignore.c:18 builtin/checkout.c:1054 builtin/gc.c:177
 msgid "suppress progress reporting"
 msgstr "undertryck förloppsrapportering"
 
-#: builtin/check-ignore.c:151
+#: builtin/check-ignore.c:26
+msgid "show non-matching input paths"
+msgstr "visa indatasökvägar som inte träffas"
+
+#: builtin/check-ignore.c:143
 msgid "cannot specify pathnames with --stdin"
 msgstr "kan inte ange sökvägsnamn med --stdin"
 
-#: builtin/check-ignore.c:154
+#: builtin/check-ignore.c:146
 msgid "-z only makes sense with --stdin"
 msgstr "-z kan endast användas tillsammans med --stdin"
 
-#: builtin/check-ignore.c:156
+#: builtin/check-ignore.c:148
 msgid "no path specified"
 msgstr "ingen sökväg angavs"
 
-#: builtin/check-ignore.c:160
+#: builtin/check-ignore.c:152
 msgid "--quiet is only valid with a single pathname"
 msgstr "--quiet kan endast användas med ett enkelt sökvägsnamn"
 
-#: builtin/check-ignore.c:162
+#: builtin/check-ignore.c:154
 msgid "cannot have both --quiet and --verbose"
 msgstr "kan inte använda både --quiet och --verbose"
 
+#: builtin/check-ignore.c:157
+msgid "--non-matching is only valid with --verbose"
+msgstr "--non-matching är endast giltig med --verbose"
+
+#: builtin/check-mailmap.c:8
+msgid "git check-mailmap [options] <contact>..."
+msgstr "git check-mailmap [flaggor] <kontakt>..."
+
+#: builtin/check-mailmap.c:13
+msgid "also read contacts from stdin"
+msgstr "läs även kontakter från standard in"
+
+#: builtin/check-mailmap.c:24
+#, c-format
+msgid "unable to parse contact: %s"
+msgstr "kunde inte tolka kontakt: %s"
+
+#: builtin/check-mailmap.c:47
+msgid "no contacts specified"
+msgstr "inga kontakter angavs"
+
 #: builtin/checkout-index.c:126
 msgid "git checkout-index [options] [--] [<file>...]"
 msgstr "git checkout-index [flaggor] [--] [<fil>...]"
@@ -2758,106 +3106,106 @@ msgstr "git checkout [flaggor] <gren>"
 msgid "git checkout [options] [<branch>] -- <file>..."
 msgstr "git checkout [flaggor] [<gren>] -- <fil>..."
 
-#: builtin/checkout.c:116 builtin/checkout.c:149
+#: builtin/checkout.c:117 builtin/checkout.c:150
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "sökvägen \"%s\" har inte vår version"
 
-#: builtin/checkout.c:118 builtin/checkout.c:151
+#: builtin/checkout.c:119 builtin/checkout.c:152
 #, c-format
 msgid "path '%s' does not have their version"
 msgstr "sökvägen \"%s\" har inte deras version"
 
-#: builtin/checkout.c:134
+#: builtin/checkout.c:135
 #, c-format
 msgid "path '%s' does not have all necessary versions"
 msgstr "sökvägen \"%s\" innehåller inte alla nödvändiga versioner"
 
-#: builtin/checkout.c:178
+#: builtin/checkout.c:179
 #, c-format
 msgid "path '%s' does not have necessary versions"
 msgstr "sökvägen \"%s\" innehåller inte nödvändiga versioner"
 
-#: builtin/checkout.c:195
+#: builtin/checkout.c:196
 #, c-format
 msgid "path '%s': cannot merge"
 msgstr "sökväg \"%s\": kan inte slå ihop"
 
-#: builtin/checkout.c:212
+#: builtin/checkout.c:213
 #, c-format
 msgid "Unable to add merge result for '%s'"
 msgstr "Kunde inte lägga till sammanslagningsresultat för \"%s\""
 
-#: builtin/checkout.c:236 builtin/checkout.c:239 builtin/checkout.c:242
-#: builtin/checkout.c:245
+#: builtin/checkout.c:237 builtin/checkout.c:240 builtin/checkout.c:243
+#: builtin/checkout.c:246
 #, c-format
 msgid "'%s' cannot be used with updating paths"
 msgstr "\"%s\" kan inte användas vid uppdatering av sökvägar"
 
-#: builtin/checkout.c:248 builtin/checkout.c:251
+#: builtin/checkout.c:249 builtin/checkout.c:252
 #, c-format
 msgid "'%s' cannot be used with %s"
 msgstr "\"%s\" kan inte användas med %s"
 
-#: builtin/checkout.c:254
+#: builtin/checkout.c:255
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
 msgstr "Kan inte uppdatera sökvägar och växla till grenen \"%s\" samtidigt."
 
-#: builtin/checkout.c:265 builtin/checkout.c:426
+#: builtin/checkout.c:266 builtin/checkout.c:458
 msgid "corrupt index file"
 msgstr "indexfilen är trasig"
 
-#: builtin/checkout.c:295 builtin/checkout.c:302
+#: builtin/checkout.c:329 builtin/checkout.c:336
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "sökvägen \"%s\" har inte slagits ihop"
 
-#: builtin/checkout.c:448
+#: builtin/checkout.c:480
 msgid "you need to resolve your current index first"
 msgstr "du måste lösa ditt befintliga index först"
 
-#: builtin/checkout.c:569
+#: builtin/checkout.c:601
 #, c-format
 msgid "Can not do reflog for '%s'\n"
 msgstr "Kan inte skapa referenslog för \"%s\"\n"
 
-#: builtin/checkout.c:602
+#: builtin/checkout.c:639
 msgid "HEAD is now at"
 msgstr "HEAD är nu på"
 
-#: builtin/checkout.c:609
+#: builtin/checkout.c:646
 #, c-format
 msgid "Reset branch '%s'\n"
 msgstr "Återställ gren \"%s\"\n"
 
-#: builtin/checkout.c:612
+#: builtin/checkout.c:649
 #, c-format
 msgid "Already on '%s'\n"
 msgstr "Redan på \"%s\"\n"
 
-#: builtin/checkout.c:616
+#: builtin/checkout.c:653
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
 msgstr "Växlade till och nollställde grenen \"%s\"\n"
 
-#: builtin/checkout.c:618 builtin/checkout.c:955
+#: builtin/checkout.c:655 builtin/checkout.c:997
 #, c-format
 msgid "Switched to a new branch '%s'\n"
 msgstr "Växlade till en ny gren \"%s\"\n"
 
-#: builtin/checkout.c:620
+#: builtin/checkout.c:657
 #, c-format
 msgid "Switched to branch '%s'\n"
 msgstr "Växlade till grenen \"%s\"\n"
 
-#: builtin/checkout.c:676
+#: builtin/checkout.c:713
 #, c-format
 msgid " ... and %d more.\n"
 msgstr " ... och %d till.\n"
 
 #. The singular version
-#: builtin/checkout.c:682
+#: builtin/checkout.c:719
 #, c-format
 msgid ""
 "Warning: you are leaving %d commit behind, not connected to\n"
@@ -2880,7 +3228,7 @@ msgstr[1] ""
 "\n"
 "%s\n"
 
-#: builtin/checkout.c:700
+#: builtin/checkout.c:737
 #, c-format
 msgid ""
 "If you want to keep them by creating a new branch, this may be a good time\n"
@@ -2895,132 +3243,136 @@ msgstr ""
 " git branch nytt_grennamn %s\n"
 "\n"
 
-#: builtin/checkout.c:730
+#: builtin/checkout.c:767
 msgid "internal error in revision walk"
 msgstr "internt fel vid genomgång av revisioner (revision walk)"
 
-#: builtin/checkout.c:734
+#: builtin/checkout.c:771
 msgid "Previous HEAD position was"
 msgstr "Tidigare position för HEAD var"
 
-#: builtin/checkout.c:761 builtin/checkout.c:950
+#: builtin/checkout.c:798 builtin/checkout.c:992
 msgid "You are on a branch yet to be born"
 msgstr "Du är på en gren som ännu inte är född"
 
 #. case (1)
-#: builtin/checkout.c:886
+#: builtin/checkout.c:928
 #, c-format
 msgid "invalid reference: %s"
 msgstr "felaktig referens: %s"
 
 #. case (1): want a tree
-#: builtin/checkout.c:925
+#: builtin/checkout.c:967
 #, c-format
 msgid "reference is not a tree: %s"
 msgstr "referensen är inte ett träd: %s"
 
-#: builtin/checkout.c:964
+#: builtin/checkout.c:1006
 msgid "paths cannot be used with switching branches"
 msgstr "sökvägar kan inte användas vid byte av gren"
 
-#: builtin/checkout.c:967 builtin/checkout.c:971
+#: builtin/checkout.c:1009 builtin/checkout.c:1013
 #, c-format
 msgid "'%s' cannot be used with switching branches"
 msgstr "\"%s\" kan inte användas vid byte av gren"
 
-#: builtin/checkout.c:975 builtin/checkout.c:978 builtin/checkout.c:983
-#: builtin/checkout.c:986
+#: builtin/checkout.c:1017 builtin/checkout.c:1020 builtin/checkout.c:1025
+#: builtin/checkout.c:1028
 #, c-format
 msgid "'%s' cannot be used with '%s'"
 msgstr "\"%s\" kan inte användas med \"%s\""
 
-#: builtin/checkout.c:991
+#: builtin/checkout.c:1033
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
 msgstr "Kan inte växla gren till icke-incheckningen \"%s\""
 
-#: builtin/checkout.c:1013 builtin/checkout.c:1015 builtin/clone.c:89
+#: builtin/checkout.c:1055 builtin/checkout.c:1057 builtin/clone.c:89
 #: builtin/remote.c:169 builtin/remote.c:171
 msgid "branch"
 msgstr "gren"
 
-#: builtin/checkout.c:1014
+#: builtin/checkout.c:1056
 msgid "create and checkout a new branch"
 msgstr "skapa och checka ut en ny gren"
 
-#: builtin/checkout.c:1016
+#: builtin/checkout.c:1058
 msgid "create/reset and checkout a branch"
 msgstr "skapa/nollställ och checka ut en gren"
 
-#: builtin/checkout.c:1017
+#: builtin/checkout.c:1059
 msgid "create reflog for new branch"
 msgstr "skapa reflogg för ny gren"
 
-#: builtin/checkout.c:1018
+#: builtin/checkout.c:1060
 msgid "detach the HEAD at named commit"
 msgstr "koppla från HEAD vid namngiven incheckning"
 
-#: builtin/checkout.c:1019
+#: builtin/checkout.c:1061
 msgid "set upstream info for new branch"
 msgstr "sätt uppströmsinformation för ny gren"
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new branch"
 msgstr "ny gren"
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new unparented branch"
 msgstr "ny gren utan förälder"
 
-#: builtin/checkout.c:1022
+#: builtin/checkout.c:1064
 msgid "checkout our version for unmerged files"
 msgstr "checka ut vår version för ej sammanslagna filer"
 
-#: builtin/checkout.c:1024
+#: builtin/checkout.c:1066
 msgid "checkout their version for unmerged files"
 msgstr "checka ut deras version för ej sammanslagna filer"
 
-#: builtin/checkout.c:1026
+#: builtin/checkout.c:1068
 msgid "force checkout (throw away local modifications)"
 msgstr "tvinga utcheckning (kasta bort lokala ändringar)"
 
-#: builtin/checkout.c:1027
+#: builtin/checkout.c:1069
 msgid "perform a 3-way merge with the new branch"
 msgstr "utför en 3-vägssammanslagning för den nya grenen"
 
-#: builtin/checkout.c:1028 builtin/merge.c:215
+#: builtin/checkout.c:1070 builtin/merge.c:232
 msgid "update ignored files (default)"
 msgstr "uppdatera ignorerade filer (standard)"
 
-#: builtin/checkout.c:1029 builtin/log.c:1147 parse-options.h:245
+#: builtin/checkout.c:1071 builtin/log.c:1208 parse-options.h:249
 msgid "style"
 msgstr "stil"
 
-#: builtin/checkout.c:1030
+#: builtin/checkout.c:1072
 msgid "conflict style (merge or diff3)"
 msgstr "konfliktstil (merge eller diff3)"
 
-#: builtin/checkout.c:1033
+#: builtin/checkout.c:1075
+msgid "do not limit pathspecs to sparse entries only"
+msgstr "begränsa inte sökvägar till endast glesa poster"
+
+#: builtin/checkout.c:1077
 msgid "second guess 'git checkout no-such-branch'"
 msgstr "förutspå \"git checkout gren-saknas\""
 
-#: builtin/checkout.c:1057
+#: builtin/checkout.c:1101
 msgid "-b, -B and --orphan are mutually exclusive"
 msgstr "-b, -B och --orphan är ömsesidigt uteslutande"
 
-#: builtin/checkout.c:1074
+#: builtin/checkout.c:1118
 msgid "--track needs a branch name"
 msgstr "--track behöver ett namn på en gren"
 
-#: builtin/checkout.c:1081
+#: builtin/checkout.c:1125
 msgid "Missing branch name; try -b"
 msgstr "Grennamn saknas; försök med -b"
 
-#: builtin/checkout.c:1116
+#: builtin/checkout.c:1160
 msgid "invalid path specification"
 msgstr "felaktig sökvägsangivelse"
 
-#: builtin/checkout.c:1123
+#: builtin/checkout.c:1167
 #, c-format
 msgid ""
 "Cannot update paths and switch to branch '%s' at the same time.\n"
@@ -3029,12 +3381,12 @@ msgstr ""
 "Kan inte uppdatera sökvägar och växla till grenen \"%s\" samtidigt.\n"
 "Ville du checka ut \"%s\" som inte kan lösas som en utcheckning?"
 
-#: builtin/checkout.c:1128
+#: builtin/checkout.c:1172
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
 msgstr "git checkout: --detach tar inte en sökväg som argument \"%s\""
 
-#: builtin/checkout.c:1132
+#: builtin/checkout.c:1176
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
@@ -3042,90 +3394,193 @@ msgstr ""
 "git checkout: --ours/--theirs, --force och --merge är inkompatibla när\n"
 "du checkar ut från indexet."
 
-#: builtin/clean.c:20
-msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+#: builtin/clean.c:25
+msgid ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
 msgstr ""
-"git clean [-d] [-f] [-n] [-q] [-e <mönster>] [-x | -X] [--] <sökvägar>..."
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <mönster>] [-x | -X] [--] "
+"<sökvägar>..."
 
-#: builtin/clean.c:24
+#: builtin/clean.c:29
 #, c-format
 msgid "Removing %s\n"
 msgstr "Tar bort %s\n"
 
-#: builtin/clean.c:25
+#: builtin/clean.c:30
 #, c-format
 msgid "Would remove %s\n"
 msgstr "Skulle ta bort %s\n"
 
-#: builtin/clean.c:26
+#: builtin/clean.c:31
 #, c-format
 msgid "Skipping repository %s\n"
 msgstr "Hoppar över arkivet %s\n"
 
-#: builtin/clean.c:27
+#: builtin/clean.c:32
 #, c-format
 msgid "Would skip repository %s\n"
 msgstr "Skulle hoppa över arkivet %s\n"
 
-#: builtin/clean.c:28
+#: builtin/clean.c:33
 #, c-format
 msgid "failed to remove %s"
 msgstr "misslyckades ta bort %s"
 
-#: builtin/clean.c:160
+#: builtin/clean.c:293
+msgid ""
+"Prompt help:\n"
+"1          - select a numbered item\n"
+"foo        - select item based on unique prefix\n"
+"           - (empty) select nothing"
+msgstr ""
+"Kommandohjälp:\n"
+"1          - markera en numrerad post\n"
+"foo        - markera post baserad på unikt prefix\n"
+"           - (tomt) markera ingenting"
+
+#: builtin/clean.c:297
+msgid ""
+"Prompt help:\n"
+"1          - select a single item\n"
+"3-5        - select a range of items\n"
+"2-3,6-9    - select multiple ranges\n"
+"foo        - select item based on unique prefix\n"
+"-...       - unselect specified items\n"
+"*          - choose all items\n"
+"           - (empty) finish selecting"
+msgstr ""
+"Kommandohjälp:\n"
+"1          - markera en ensam post\n"
+"3-5        - markera ett intervall med poster\n"
+"2-3,6-9    - markera flera intervall\n"
+"foo        - markera post baserad på unikt prefix\n"
+"-...       - avmarkera specifika poster\n"
+"*          - välj alla poster\n"
+"           - (tomt) avsluta markering"
+
+#: builtin/clean.c:515
+#, c-format
+msgid "Huh (%s)?"
+msgstr "Vadå (%s)?"
+
+#: builtin/clean.c:658
+#, c-format
+msgid "Input ignore patterns>> "
+msgstr "Ange ignoreringsmönster>>"
+
+#: builtin/clean.c:695
+#, c-format
+msgid "WARNING: Cannot find items matched by: %s"
+msgstr "VARNING: Hittar inte poster som motsvarar: %s"
+
+#: builtin/clean.c:716
+msgid "Select items to delete"
+msgstr "Välj poster att ta bort"
+
+#: builtin/clean.c:756
+#, c-format
+msgid "remove %s? "
+msgstr "ta bort %s?"
+
+#: builtin/clean.c:781
+msgid "Bye."
+msgstr "Hej då."
+
+#: builtin/clean.c:789
+msgid ""
+"clean               - start cleaning\n"
+"filter by pattern   - exclude items from deletion\n"
+"select by numbers   - select items to be deleted by numbers\n"
+"ask each            - confirm each deletion (like \"rm -i\")\n"
+"quit                - stop cleaning\n"
+"help                - this screen\n"
+"?                   - help for prompt selection"
+msgstr ""
+"clean               - börja städa\n"
+"filter by pattern   - uteslut poster från borttagning\n"
+"select by numbers   - markera poster som ska tas bort med siffror\n"
+"ask each            - bekräfta varje borttagning (som \"rm -i\")\n"
+"quit                - sluta städa\n"
+"help                - denna skärm\n"
+"?                   - hjälp för kommandoval"
+
+#: builtin/clean.c:816
+msgid "*** Commands ***"
+msgstr "*** Kommandon ***"
+
+#: builtin/clean.c:817
+msgid "What now"
+msgstr "Vad nu"
+
+#: builtin/clean.c:825
+msgid "Would remove the following item:"
+msgid_plural "Would remove the following items:"
+msgstr[0] "Skulle ta bort följande post:"
+msgstr[1] "Skulle ta bort följande poster:"
+
+#: builtin/clean.c:842
+msgid "No more files to clean, exiting."
+msgstr "Inga fler filer att städa, avslutar."
+
+#: builtin/clean.c:874
 msgid "do not print names of files removed"
 msgstr "skriv inte ut namn på borttagna filer"
 
-#: builtin/clean.c:162
+#: builtin/clean.c:876
 msgid "force"
 msgstr "tvinga"
 
-#: builtin/clean.c:164
+#: builtin/clean.c:877
+msgid "interactive cleaning"
+msgstr "städa interaktivt"
+
+#: builtin/clean.c:879
 msgid "remove whole directories"
 msgstr "ta bort hela kataloger"
 
-#: builtin/clean.c:165 builtin/describe.c:413 builtin/grep.c:717
-#: builtin/ls-files.c:494 builtin/name-rev.c:231 builtin/show-ref.c:182
+#: builtin/clean.c:880 builtin/describe.c:420 builtin/grep.c:716
+#: builtin/ls-files.c:493 builtin/name-rev.c:315 builtin/show-ref.c:186
 msgid "pattern"
 msgstr "mönster"
 
-#: builtin/clean.c:166
+#: builtin/clean.c:881
 msgid "add <pattern> to ignore rules"
 msgstr "lägg till <mönster> till ignoreringsregler"
 
-#: builtin/clean.c:167
+#: builtin/clean.c:882
 msgid "remove ignored files, too"
 msgstr "ta även bort ignorerade filer"
 
-#: builtin/clean.c:169
+#: builtin/clean.c:884
 msgid "remove only ignored files"
 msgstr "ta endast bort ignorerade filer"
 
-#: builtin/clean.c:187
+#: builtin/clean.c:902
 msgid "-x and -X cannot be used together"
 msgstr "-x och -X kan inte användas samtidigt"
 
-#: builtin/clean.c:191
+#: builtin/clean.c:906
 msgid ""
-"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+"clean.requireForce set to true and neither -i, -n nor -f given; refusing to "
+"clean"
 msgstr ""
-"clean.requireForce satt till true, men varken -n eller -f angavs; vägrar "
+"clean.requireForce satt till true, men varken -i, -n eller -f angavs; vägrar "
 "städa"
 
-#: builtin/clean.c:194
+#: builtin/clean.c:909
 msgid ""
-"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
-"clean"
+"clean.requireForce defaults to true and neither -i, -n nor -f given; "
+"refusing to clean"
 msgstr ""
-"clean.requireForce har standardvärdet true, men varken -n eller -f angavs; "
-"vägrar städa"
+"clean.requireForce har standardvärdet true och varken -i, -n eller -f "
+"angavs; vägrar städa"
 
 #: builtin/clone.c:36
 msgid "git clone [options] [--] <repo> [<dir>]"
 msgstr "git clone [flaggor] [--] <arkiv> [<kat>]"
 
-#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:212
-#: builtin/push.c:436
+#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:229
+#: builtin/push.c:462
 msgid "force progress reporting"
 msgstr "tvinga förloppsrapportering"
 
@@ -3185,7 +3640,7 @@ msgstr "checka ut <gren> istället för fjärrens HEAD"
 msgid "path to git-upload-pack on the remote"
 msgstr "sökväg till git-upload-pack på fjärren"
 
-#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:662
+#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:661
 msgid "depth"
 msgstr "djup"
 
@@ -3213,123 +3668,155 @@ msgstr "nyckel=värde"
 msgid "set config inside the new repository"
 msgstr "ställ in konfiguration i det nya arkivet"
 
-#: builtin/clone.c:243
+#: builtin/clone.c:253
 #, c-format
-msgid "reference repository '%s' is not a local directory."
-msgstr "referensarkivet \"%s\" är inte en lokal katalog."
+msgid "reference repository '%s' is not a local repository."
+msgstr "referensarkivet \"%s\" är inte ett lokalt arkiv."
 
-#: builtin/clone.c:306
+#: builtin/clone.c:316
 #, c-format
 msgid "failed to create directory '%s'"
 msgstr "misslyckades skapa katalogen \"%s\""
 
-#: builtin/clone.c:308 builtin/diff.c:77
+#: builtin/clone.c:318 builtin/diff.c:77
 #, c-format
 msgid "failed to stat '%s'"
 msgstr "misslyckades ta status på \"%s\""
 
-#: builtin/clone.c:310
+#: builtin/clone.c:320
 #, c-format
 msgid "%s exists and is not a directory"
 msgstr "%s finns och är ingen katalog"
 
-#: builtin/clone.c:324
+#: builtin/clone.c:334
 #, c-format
 msgid "failed to stat %s\n"
 msgstr "misslyckades ta status på %s\n"
 
-#: builtin/clone.c:346
+#: builtin/clone.c:356
 #, c-format
 msgid "failed to create link '%s'"
 msgstr "misslyckades skapa länken \"%s\""
 
-#: builtin/clone.c:350
+#: builtin/clone.c:360
 #, c-format
 msgid "failed to copy file to '%s'"
 msgstr "misslyckades kopiera filen till \"%s\""
 
-#: builtin/clone.c:373
+#: builtin/clone.c:383
 #, c-format
 msgid "done.\n"
 msgstr "klart.\n"
 
-#: builtin/clone.c:443
+#: builtin/clone.c:396
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry the checkout with 'git checkout -f HEAD'\n"
+msgstr ""
+"Klonen lyckades, men utcheckningen misslyckades.\n"
+"Du kan inspektera det som checkades ut med \"git status\"\n"
+"och försöka checka ut igen med \"git checkout -f HEAD\"\n"
+
+#: builtin/clone.c:475
 #, c-format
 msgid "Could not find remote branch %s to clone."
 msgstr "Kunde inte hitta fjärrgrenen %s för att klona."
 
-#: builtin/clone.c:552
+#: builtin/clone.c:555
+#, c-format
+msgid "Checking connectivity... "
+msgstr "Kontrollerar anslutning..."
+
+#: builtin/clone.c:558
+msgid "remote did not send all necessary objects"
+msgstr "fjärren sände inte alla nödvändiga objekt"
+
+#: builtin/clone.c:560
+#, c-format
+msgid "done\n"
+msgstr "klart\n"
+
+#: builtin/clone.c:621
 msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
 msgstr ""
 "HEAD hos fjärren pekar på en obefintlig referens, kan inte checka ut.\n"
 
-#: builtin/clone.c:690
+#: builtin/clone.c:652
+msgid "unable to checkout working tree"
+msgstr "kunde inte checka ut arbetskatalogen"
+
+#: builtin/clone.c:760
 msgid "Too many arguments."
 msgstr "För många argument."
 
-#: builtin/clone.c:694
+#: builtin/clone.c:764
 msgid "You must specify a repository to clone."
 msgstr "Du måste ange ett arkiv att klona."
 
-#: builtin/clone.c:705
+#: builtin/clone.c:775
 #, c-format
 msgid "--bare and --origin %s options are incompatible."
 msgstr "flaggorna --bare och --origin %s är inkompatibla."
 
-#: builtin/clone.c:708
+#: builtin/clone.c:778
 msgid "--bare and --separate-git-dir are incompatible."
 msgstr "flaggorna --bare och --separate-git-dir är inkompatibla."
 
-#: builtin/clone.c:721
+#: builtin/clone.c:791
 #, c-format
 msgid "repository '%s' does not exist"
 msgstr "arkivet \"%s\" finns inte"
 
-#: builtin/clone.c:726
+#: builtin/clone.c:796
 msgid "--depth is ignored in local clones; use file:// instead."
 msgstr "--depth ignoreras i lokala kloningar; använd file:// istället"
 
-#: builtin/clone.c:736
+#: builtin/clone.c:798
+msgid "--local is ignored"
+msgstr "--local ignoreras"
+
+#: builtin/clone.c:808
 #, c-format
 msgid "destination path '%s' already exists and is not an empty directory."
 msgstr "destinationssökvägen \"%s\" finns redan och är inte en tom katalog."
 
-#: builtin/clone.c:746
+#: builtin/clone.c:818
 #, c-format
 msgid "working tree '%s' already exists."
 msgstr "arbetsträdet \"%s\" finns redan."
 
-#: builtin/clone.c:759 builtin/clone.c:771
+#: builtin/clone.c:831 builtin/clone.c:843
 #, c-format
 msgid "could not create leading directories of '%s'"
 msgstr "kunde inte skapa inledande kataloger för \"%s\""
 
-#: builtin/clone.c:762
+#: builtin/clone.c:834
 #, c-format
 msgid "could not create work tree dir '%s'."
 msgstr "kunde inte skapa arbetskatalogen \"%s\""
 
-#: builtin/clone.c:781
+#: builtin/clone.c:853
 #, c-format
 msgid "Cloning into bare repository '%s'...\n"
 msgstr "Klonar till ett naket arkiv \"%s\"...\n"
 
-#: builtin/clone.c:783
+#: builtin/clone.c:855
 #, c-format
 msgid "Cloning into '%s'...\n"
 msgstr "Klonar till \"%s\"...\n"
 
-#: builtin/clone.c:818
+#: builtin/clone.c:890
 #, c-format
 msgid "Don't know how to clone %s"
 msgstr "Vet inte hur man klonar %s"
 
-#: builtin/clone.c:867
+#: builtin/clone.c:942
 #, c-format
 msgid "Remote branch %s not found in upstream %s"
 msgstr "Fjärrgrenen %s hittades inte i uppströmsarkivet %s"
 
-#: builtin/clone.c:874
+#: builtin/clone.c:949
 msgid "You appear to have cloned an empty repository."
 msgstr "Du verkar ha klonat ett tomt arkiv."
 
@@ -3365,15 +3852,15 @@ msgstr "Spaltfyllnad mellan spalter"
 msgid "--command must be the first argument"
 msgstr "--command måste vara första argument"
 
-#: builtin/commit.c:34
+#: builtin/commit.c:35
 msgid "git commit [options] [--] <pathspec>..."
 msgstr "git commit [flaggor] [--] <sökväg>..."
 
-#: builtin/commit.c:39
+#: builtin/commit.c:40
 msgid "git status [options] [--] <pathspec>..."
 msgstr "git status [flaggor] [--] <sökväg>..."
 
-#: builtin/commit.c:44
+#: builtin/commit.c:45
 msgid ""
 "Your name and email address were configured automatically based\n"
 "on your username and hostname. Please check that they are accurate.\n"
@@ -3398,7 +3885,7 @@ msgstr ""
 "\n"
 "    git commit --amend --reset-author\n"
 
-#: builtin/commit.c:56
+#: builtin/commit.c:57
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -3408,109 +3895,127 @@ msgstr ""
 "blir den tom. Du kan köra kommandot på nytt med --allow-empty, eller\n"
 "så kan du ta bort incheckningen helt med \"git reset HEAD^\".\n"
 
-#: builtin/commit.c:61
+#: builtin/commit.c:62
 msgid ""
 "The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
 "If you wish to commit it anyway, use:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"Otherwise, please use 'git reset'\n"
 msgstr ""
 "Den tidigare \"cherry-pick\":en är nu tom, kanske på grund av en löst\n"
 "konflikt. Om du vill checka in den ändå använder du:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"Annars använder du \"git reset\"\n"
 
-#: builtin/commit.c:258
+#: builtin/commit.c:69
+msgid "Otherwise, please use 'git reset'\n"
+msgstr "Använd annars \"git reset\"\n"
+
+#: builtin/commit.c:72
+msgid ""
+"If you wish to skip this commit, use:\n"
+"\n"
+"    git reset\n"
+"\n"
+"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
+"the remaining commits.\n"
+msgstr ""
+"Om du vill hoppa över incheckningen, använd:\n"
+"\n"
+"    git reset\n"
+"\n"
+"\"git cherry-pick --continue\" kommer därefter att återuppta\n"
+"cherry-pick för återstående incheckningar.\n"
+
+#: builtin/commit.c:279
 msgid "failed to unpack HEAD tree object"
 msgstr "misslyckades packa upp HEAD:s trädobjekt"
 
-#: builtin/commit.c:300
+#: builtin/commit.c:321
 msgid "unable to create temporary index"
 msgstr "kunde inte skapa temporär indexfil"
 
-#: builtin/commit.c:306
+#: builtin/commit.c:327
 msgid "interactive add failed"
 msgstr "interaktiv tilläggning misslyckades"
 
-#: builtin/commit.c:339 builtin/commit.c:360 builtin/commit.c:410
+#: builtin/commit.c:360 builtin/commit.c:381 builtin/commit.c:431
 msgid "unable to write new_index file"
 msgstr "kunde inte skriva filen new_index"
 
-#: builtin/commit.c:391
+#: builtin/commit.c:412
 msgid "cannot do a partial commit during a merge."
 msgstr "kan inte utföra en delvis incheckning under en sammanslagning."
 
-#: builtin/commit.c:393
+#: builtin/commit.c:414
 msgid "cannot do a partial commit during a cherry-pick."
 msgstr "kan inte utföra en delvis incheckning under en cherry-pick."
 
-#: builtin/commit.c:403
+#: builtin/commit.c:424
 msgid "cannot read the index"
 msgstr "kan inte läsa indexet"
 
-#: builtin/commit.c:423
+#: builtin/commit.c:444
 msgid "unable to write temporary index file"
 msgstr "kunde inte skriva temporär indexfil"
 
-#: builtin/commit.c:511 builtin/commit.c:517
+#: builtin/commit.c:535 builtin/commit.c:541
 #, c-format
 msgid "invalid commit: %s"
 msgstr "felaktig incheckning: %s"
 
-#: builtin/commit.c:540
+#: builtin/commit.c:563
 msgid "malformed --author parameter"
 msgstr "felformad \"--author\"-flagga"
 
-#: builtin/commit.c:560
+#: builtin/commit.c:583
 #, c-format
 msgid "Malformed ident string: '%s'"
 msgstr "Felaktig indragningssträng: \"%s\""
 
-#: builtin/commit.c:598 builtin/commit.c:631 builtin/commit.c:954
+#: builtin/commit.c:621 builtin/commit.c:654 builtin/commit.c:982
 #, c-format
 msgid "could not lookup commit %s"
 msgstr "kunde inte slå upp incheckningen %s"
 
-#: builtin/commit.c:610 builtin/shortlog.c:272
+#: builtin/commit.c:633 builtin/shortlog.c:271
 #, c-format
 msgid "(reading log message from standard input)\n"
 msgstr "(läser loggmeddelande från standard in)\n"
 
-#: builtin/commit.c:612
+#: builtin/commit.c:635
 msgid "could not read log from standard input"
 msgstr "kunde inte läsa logg från standard in"
 
-#: builtin/commit.c:616
+#: builtin/commit.c:639
 #, c-format
 msgid "could not read log file '%s'"
 msgstr "kunde inte läsa loggfilen \"%s\""
 
-#: builtin/commit.c:622
+#: builtin/commit.c:645
 msgid "commit has empty message"
 msgstr "incheckningen har ett tomt meddelande"
 
-#: builtin/commit.c:638
+#: builtin/commit.c:661
 msgid "could not read MERGE_MSG"
 msgstr "kunde inte läsa MERGE_MSG"
 
-#: builtin/commit.c:642
+#: builtin/commit.c:665
 msgid "could not read SQUASH_MSG"
 msgstr "kunde inte läsa SQUASH_MSG"
 
-#: builtin/commit.c:646
+#: builtin/commit.c:669
 #, c-format
 msgid "could not read '%s'"
 msgstr "kunde inte läsa \"%s\""
 
-#: builtin/commit.c:707
+#: builtin/commit.c:730
 msgid "could not write commit template"
 msgstr "kunde inte skriva incheckningsmall"
 
-#: builtin/commit.c:718
+#: builtin/commit.c:741
 #, c-format
 msgid ""
 "\n"
@@ -3525,7 +4030,7 @@ msgstr ""
 "\t%s\n"
 "och försöker igen.\n"
 
-#: builtin/commit.c:723
+#: builtin/commit.c:746
 #, c-format
 msgid ""
 "\n"
@@ -3540,7 +4045,7 @@ msgstr ""
 "\t%s\n"
 "och försöker igen.\n"
 
-#: builtin/commit.c:735
+#: builtin/commit.c:758
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
@@ -3550,7 +4055,7 @@ msgstr ""
 "med \"%c\" kommer ignoreras, och ett tomt meddelande avbryter "
 "incheckningen.\n"
 
-#: builtin/commit.c:740
+#: builtin/commit.c:763
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
@@ -3561,139 +4066,139 @@ msgstr ""
 "med \"%c\" kommer behållas; du kan själv ta bort dem om du vill.\n"
 "Ett tomt meddelande avbryter incheckningen.\n"
 
-#: builtin/commit.c:753
+#: builtin/commit.c:776
 #, c-format
 msgid "%sAuthor:    %s"
 msgstr "%sFörfattare: %s"
 
-#: builtin/commit.c:760
+#: builtin/commit.c:783
 #, c-format
 msgid "%sCommitter: %s"
 msgstr "%sIncheckare: %s"
 
-#: builtin/commit.c:780
+#: builtin/commit.c:803
 msgid "Cannot read index"
 msgstr "Kan inte läsa indexet"
 
-#: builtin/commit.c:817
+#: builtin/commit.c:845
 msgid "Error building trees"
 msgstr "Fel vid byggande av träd"
 
-#: builtin/commit.c:832 builtin/tag.c:359
+#: builtin/commit.c:860 builtin/tag.c:359
 #, c-format
 msgid "Please supply the message using either -m or -F option.\n"
 msgstr "Ange meddelandet en av flaggorna -m eller -F.\n"
 
-#: builtin/commit.c:929
+#: builtin/commit.c:957
 #, c-format
 msgid "No existing author found with '%s'"
 msgstr "Hittade ingen befintlig författare med \"%s\""
 
-#: builtin/commit.c:944 builtin/commit.c:1138
+#: builtin/commit.c:972 builtin/commit.c:1208
 #, c-format
 msgid "Invalid untracked files mode '%s'"
 msgstr "Ogiltigt läge för ospårade filer: \"%s\""
 
-#: builtin/commit.c:974
+#: builtin/commit.c:1009
+msgid "--long and -z are incompatible"
+msgstr "--long och -z är inkompatibla"
+
+#: builtin/commit.c:1039
 msgid "Using both --reset-author and --author does not make sense"
 msgstr "Kan inte använda både --reset-author och --author"
 
-#: builtin/commit.c:985
+#: builtin/commit.c:1050
 msgid "You have nothing to amend."
 msgstr "Du har inget att utöka."
 
-#: builtin/commit.c:988
+#: builtin/commit.c:1053
 msgid "You are in the middle of a merge -- cannot amend."
 msgstr "Du är i mitten av en sammanslagning -- kan inte utöka."
 
-#: builtin/commit.c:990
+#: builtin/commit.c:1055
 msgid "You are in the middle of a cherry-pick -- cannot amend."
 msgstr "Du är i mitten av en cherry-pick -- kan inte utöka."
 
-#: builtin/commit.c:993
+#: builtin/commit.c:1058
 msgid "Options --squash and --fixup cannot be used together"
 msgstr "Flaggorna --squash och --fixup kan inte användas samtidigt"
 
-#: builtin/commit.c:1003
+#: builtin/commit.c:1068
 msgid "Only one of -c/-C/-F/--fixup can be used."
 msgstr "Endast en av -c/-C/-F/--fixup kan användas."
 
-#: builtin/commit.c:1005
+#: builtin/commit.c:1070
 msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
 msgstr "Flaggan -m kan inte kombineras med -c/-C/-F/--fixup."
 
-#: builtin/commit.c:1013
+#: builtin/commit.c:1078
 msgid "--reset-author can be used only with -C, -c or --amend."
 msgstr "--reset-author kan endast användas med -C, -c eller --amend."
 
-#: builtin/commit.c:1030
+#: builtin/commit.c:1095
 msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
 msgstr ""
 "Endast en av --include/--only/--all/--interactive/--patch kan användas."
 
-#: builtin/commit.c:1032
+#: builtin/commit.c:1097
 msgid "No paths with --include/--only does not make sense."
 msgstr "Du måste ange sökvägar tillsammans med --include/--only."
 
-#: builtin/commit.c:1034
+#: builtin/commit.c:1099
 msgid "Clever... amending the last one with dirty index."
 msgstr "Smart... utöka den senaste med smutsigt index."
 
-#: builtin/commit.c:1036
+#: builtin/commit.c:1101
 msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
 msgstr "Explicita sökvägar angavs utan -i eller -o; antar --only sökvägar..."
 
-#: builtin/commit.c:1046 builtin/tag.c:575
+#: builtin/commit.c:1111 builtin/tag.c:575
 #, c-format
 msgid "Invalid cleanup mode %s"
 msgstr "Felaktigt städningsläge %s"
 
-#: builtin/commit.c:1051
+#: builtin/commit.c:1116
 msgid "Paths with -a does not make sense."
 msgstr "Kan inte ange sökvägar med -a."
 
-#: builtin/commit.c:1057 builtin/commit.c:1192
-msgid "--long and -z are incompatible"
-msgstr "--long och -z är inkompatibla"
-
-#: builtin/commit.c:1152 builtin/commit.c:1388
+#: builtin/commit.c:1222 builtin/commit.c:1455
 msgid "show status concisely"
-msgstr "vis koncis status"
+msgstr "visa koncis status"
 
-#: builtin/commit.c:1154 builtin/commit.c:1390
+#: builtin/commit.c:1224 builtin/commit.c:1457
 msgid "show branch information"
 msgstr "visa information om gren"
 
-#: builtin/commit.c:1156 builtin/commit.c:1392 builtin/push.c:426
+#: builtin/commit.c:1226 builtin/commit.c:1459 builtin/push.c:452
 msgid "machine-readable output"
 msgstr "maskinläsbar utdata"
 
-#: builtin/commit.c:1159 builtin/commit.c:1394
+#: builtin/commit.c:1229 builtin/commit.c:1461
 msgid "show status in long format (default)"
 msgstr "visa status i långt format (standard)"
 
-#: builtin/commit.c:1162 builtin/commit.c:1397
+#: builtin/commit.c:1232 builtin/commit.c:1464
 msgid "terminate entries with NUL"
 msgstr "terminera poster med NUL"
 
-#: builtin/commit.c:1164 builtin/commit.c:1400 builtin/fast-export.c:647
-#: builtin/fast-export.c:650 builtin/tag.c:459
+#: builtin/commit.c:1234 builtin/commit.c:1467 builtin/fast-export.c:667
+#: builtin/fast-export.c:670 builtin/tag.c:459
 msgid "mode"
 msgstr "läge"
 
-#: builtin/commit.c:1165 builtin/commit.c:1400
+#: builtin/commit.c:1235 builtin/commit.c:1467
 msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
 msgstr "visa ospårade filer, valfria lägen: alla, normal, no. (Standard: all)"
 
-#: builtin/commit.c:1168
+#: builtin/commit.c:1238
 msgid "show ignored files"
 msgstr "visa ignorerade filer"
 
-#: builtin/commit.c:1169 parse-options.h:151
+#: builtin/commit.c:1239 parse-options.h:154
 msgid "when"
 msgstr "när"
 
-#: builtin/commit.c:1170
+#: builtin/commit.c:1240
 msgid ""
 "ignore changes to submodules, optional when: all, dirty, untracked. "
 "(Default: all)"
@@ -3701,217 +4206,217 @@ msgstr ""
 "ignorera ändringar i undermoduler, valfritt när: all, dirty, untracked. "
 "(Default: all)"
 
-#: builtin/commit.c:1172
+#: builtin/commit.c:1242
 msgid "list untracked files in columns"
 msgstr "visa ospårade filer i spalter"
 
-#: builtin/commit.c:1246
+#: builtin/commit.c:1313
 msgid "couldn't look up newly created commit"
 msgstr "kunde inte slå upp en precis skapad incheckning"
 
-#: builtin/commit.c:1248
+#: builtin/commit.c:1315
 msgid "could not parse newly created commit"
 msgstr "kunde inte tolka en precis skapad incheckning"
 
-#: builtin/commit.c:1289
+#: builtin/commit.c:1356
 msgid "detached HEAD"
 msgstr "frånkopplad HEAD"
 
-#: builtin/commit.c:1291
+#: builtin/commit.c:1358
 msgid " (root-commit)"
 msgstr " (rotincheckning)"
 
-#: builtin/commit.c:1358
+#: builtin/commit.c:1425
 msgid "suppress summary after successful commit"
 msgstr "undertryck sammanfattning efter framgångsrik incheckning"
 
-#: builtin/commit.c:1359
+#: builtin/commit.c:1426
 msgid "show diff in commit message template"
 msgstr "visa diff i mallen för incheckningsmeddelandet"
 
-#: builtin/commit.c:1361
+#: builtin/commit.c:1428
 msgid "Commit message options"
 msgstr "Alternativ för incheckningsmeddelande"
 
-#: builtin/commit.c:1362 builtin/tag.c:457
+#: builtin/commit.c:1429 builtin/tag.c:457
 msgid "read message from file"
 msgstr "läs meddelande från fil"
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "author"
 msgstr "författare"
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "override author for commit"
 msgstr "överstyr författare för incheckningen"
 
-#: builtin/commit.c:1364 builtin/gc.c:178
+#: builtin/commit.c:1431 builtin/gc.c:178
 msgid "date"
 msgstr "datum"
 
-#: builtin/commit.c:1364
+#: builtin/commit.c:1431
 msgid "override date for commit"
 msgstr "överstyr datum för inchecknignen"
 
-#: builtin/commit.c:1365 builtin/merge.c:206 builtin/notes.c:533
-#: builtin/notes.c:690 builtin/tag.c:455
+#: builtin/commit.c:1432 builtin/merge.c:223 builtin/notes.c:405
+#: builtin/notes.c:562 builtin/tag.c:455
 msgid "message"
 msgstr "meddelande"
 
-#: builtin/commit.c:1365
+#: builtin/commit.c:1432
 msgid "commit message"
 msgstr "incheckningsmeddelande"
 
-#: builtin/commit.c:1366
+#: builtin/commit.c:1433
 msgid "reuse and edit message from specified commit"
 msgstr "återanvänd och redigera meddelande från angiven incheckning"
 
-#: builtin/commit.c:1367
+#: builtin/commit.c:1434
 msgid "reuse message from specified commit"
 msgstr "återanvänd meddelande från angiven incheckning"
 
-#: builtin/commit.c:1368
+#: builtin/commit.c:1435
 msgid "use autosquash formatted message to fixup specified commit"
 msgstr ""
 "använd autosquash-formaterat meddelande för att fixa angiven incheckning"
 
-#: builtin/commit.c:1369
+#: builtin/commit.c:1436
 msgid "use autosquash formatted message to squash specified commit"
 msgstr ""
 "använd autosquash-formaterat meddelande för att slå ihop med angiven "
 "incheckning"
 
-#: builtin/commit.c:1370
+#: builtin/commit.c:1437
 msgid "the commit is authored by me now (used with -C/-c/--amend)"
 msgstr "jag är nu författare av incheckningen (används med -C/-c/--amend)"
 
-#: builtin/commit.c:1371 builtin/log.c:1102 builtin/revert.c:109
+#: builtin/commit.c:1438 builtin/log.c:1160 builtin/revert.c:111
 msgid "add Signed-off-by:"
 msgstr "lägg till Signed-off-by:"
 
-#: builtin/commit.c:1372
+#: builtin/commit.c:1439
 msgid "use specified template file"
 msgstr "använd angiven mallfil"
 
-#: builtin/commit.c:1373
+#: builtin/commit.c:1440
 msgid "force edit of commit"
 msgstr "tvinga redigering av incheckning"
 
-#: builtin/commit.c:1374
+#: builtin/commit.c:1441
 msgid "default"
 msgstr "standard"
 
-#: builtin/commit.c:1374 builtin/tag.c:460
+#: builtin/commit.c:1441 builtin/tag.c:460
 msgid "how to strip spaces and #comments from message"
 msgstr "hur blanksteg och #kommentarer skall tas bort från meddelande"
 
-#: builtin/commit.c:1375
+#: builtin/commit.c:1442
 msgid "include status in commit message template"
 msgstr "inkludera status i mallen för incheckningsmeddelandet"
 
-#: builtin/commit.c:1376 builtin/merge.c:213 builtin/tag.c:461
+#: builtin/commit.c:1443 builtin/merge.c:230 builtin/tag.c:461
 msgid "key id"
 msgstr "nyckel-id"
 
-#: builtin/commit.c:1377 builtin/merge.c:214
+#: builtin/commit.c:1444 builtin/merge.c:231
 msgid "GPG sign commit"
 msgstr "GPG-signera incheckning"
 
 #. end commit message options
-#: builtin/commit.c:1380
+#: builtin/commit.c:1447
 msgid "Commit contents options"
 msgstr "Alternativ för incheckningens innehåll"
 
-#: builtin/commit.c:1381
+#: builtin/commit.c:1448
 msgid "commit all changed files"
 msgstr "checka in alla ändrade filer"
 
-#: builtin/commit.c:1382
+#: builtin/commit.c:1449
 msgid "add specified files to index for commit"
 msgstr "lägg till angivna filer till indexet för incheckning"
 
-#: builtin/commit.c:1383
+#: builtin/commit.c:1450
 msgid "interactively add files"
 msgstr "lägg till filer interaktivt"
 
-#: builtin/commit.c:1384
+#: builtin/commit.c:1451
 msgid "interactively add changes"
 msgstr "lägg till ändringar interaktivt"
 
-#: builtin/commit.c:1385
+#: builtin/commit.c:1452
 msgid "commit only specified files"
 msgstr "checka endast in angivna filer"
 
-#: builtin/commit.c:1386
+#: builtin/commit.c:1453
 msgid "bypass pre-commit hook"
 msgstr "förbigå pre-commit-krok"
 
-#: builtin/commit.c:1387
+#: builtin/commit.c:1454
 msgid "show what would be committed"
 msgstr "visa vad som skulle checkas in"
 
-#: builtin/commit.c:1398
+#: builtin/commit.c:1465
 msgid "amend previous commit"
 msgstr "lägg till föregående incheckning"
 
-#: builtin/commit.c:1399
+#: builtin/commit.c:1466
 msgid "bypass post-rewrite hook"
 msgstr "förbigå post-rewrite-krok"
 
-#: builtin/commit.c:1404
+#: builtin/commit.c:1471
 msgid "ok to record an empty change"
 msgstr "ok att registrera en tom ändring"
 
-#: builtin/commit.c:1407
+#: builtin/commit.c:1474
 msgid "ok to record a change with an empty message"
 msgstr "ok att registrera en ändring med tomt meddelande"
 
-#: builtin/commit.c:1439
+#: builtin/commit.c:1507
 msgid "could not parse HEAD commit"
 msgstr "kunde inte tolka HEAD:s incheckning"
 
-#: builtin/commit.c:1477 builtin/merge.c:508
+#: builtin/commit.c:1545 builtin/merge.c:525
 #, c-format
 msgid "could not open '%s' for reading"
 msgstr "kunde inte öppna \"%s\" för läsning"
 
-#: builtin/commit.c:1484
+#: builtin/commit.c:1552
 #, c-format
 msgid "Corrupt MERGE_HEAD file (%s)"
 msgstr "Trasig MERGE_HEAD-fil (%s)"
 
-#: builtin/commit.c:1491
+#: builtin/commit.c:1559
 msgid "could not read MERGE_MODE"
 msgstr "kunde inte läsa MERGE_MODE"
 
-#: builtin/commit.c:1510
+#: builtin/commit.c:1578
 #, c-format
 msgid "could not read commit message: %s"
 msgstr "kunde inte läsa incheckningsmeddelande: %s"
 
-#: builtin/commit.c:1524
+#: builtin/commit.c:1592
 #, c-format
 msgid "Aborting commit; you did not edit the message.\n"
 msgstr "Avbryter incheckning; meddelandet inte redigerat.\n"
 
-#: builtin/commit.c:1529
+#: builtin/commit.c:1597
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr "Avbryter på grund av tomt incheckningsmeddelande.\n"
 
-#: builtin/commit.c:1544 builtin/merge.c:832 builtin/merge.c:857
+#: builtin/commit.c:1612 builtin/merge.c:861 builtin/merge.c:886
 msgid "failed to write commit object"
 msgstr "kunde inte skriva incheckningsobjekt"
 
-#: builtin/commit.c:1565
+#: builtin/commit.c:1633
 msgid "cannot lock HEAD ref"
 msgstr "kunde inte låsa HEAD-referens"
 
-#: builtin/commit.c:1569
+#: builtin/commit.c:1637
 msgid "cannot update HEAD ref"
 msgstr "kunde inte uppdatera HEAD-referens"
 
-#: builtin/commit.c:1580
+#: builtin/commit.c:1648
 msgid ""
 "Repository has been updated, but unable to write\n"
 "new_index file. Check that disk is not full or quota is\n"
@@ -3926,171 +4431,183 @@ msgstr ""
 msgid "git config [options]"
 msgstr "git config [flaggor]"
 
-#: builtin/config.c:51
+#: builtin/config.c:52
 msgid "Config file location"
 msgstr "Konfigurationsfilens plats"
 
-#: builtin/config.c:52
+#: builtin/config.c:53
 msgid "use global config file"
 msgstr "använd global konfigurationsfil"
 
-#: builtin/config.c:53
+#: builtin/config.c:54
 msgid "use system config file"
 msgstr "använd systemets konfigurationsfil"
 
-#: builtin/config.c:54
+#: builtin/config.c:55
 msgid "use repository config file"
 msgstr "använd arkivets konfigurationsfil"
 
-#: builtin/config.c:55
+#: builtin/config.c:56
 msgid "use given config file"
 msgstr "använd angiven konfigurationsil"
 
-#: builtin/config.c:56
+#: builtin/config.c:57
+msgid "blob-id"
+msgstr "blob-id"
+
+#: builtin/config.c:57
+msgid "read config from given blob object"
+msgstr "läs konfiguration från givet blob-objekt"
+
+#: builtin/config.c:58
 msgid "Action"
 msgstr "Åtgärd"
 
-#: builtin/config.c:57
+#: builtin/config.c:59
 msgid "get value: name [value-regex]"
 msgstr "hämta värde: namn [värde-reguttr]"
 
-#: builtin/config.c:58
+#: builtin/config.c:60
 msgid "get all values: key [value-regex]"
 msgstr "hämta alla värden: nyckel [värde-reguttr]"
 
-#: builtin/config.c:59
+#: builtin/config.c:61
 msgid "get values for regexp: name-regex [value-regex]"
 msgstr "hämta värden för reguttr: namn-reguttr [värde-reguttr]"
 
-#: builtin/config.c:60
+#: builtin/config.c:62
 msgid "replace all matching variables: name value [value_regex]"
 msgstr "ersätt alla motsvarande variabler: namn värde [värde-reguttr]"
 
-#: builtin/config.c:61
+#: builtin/config.c:63
 msgid "add a new variable: name value"
 msgstr "lägg till en ny variabel: namn värde"
 
-#: builtin/config.c:62
+#: builtin/config.c:64
 msgid "remove a variable: name [value-regex]"
 msgstr "ta bort en variabel: namn [värde-reguttr]"
 
-#: builtin/config.c:63
+#: builtin/config.c:65
 msgid "remove all matches: name [value-regex]"
 msgstr "ta bort alla träffar: namn [värde-reguttr]"
 
-#: builtin/config.c:64
+#: builtin/config.c:66
 msgid "rename section: old-name new-name"
 msgstr "byt namn på sektion: gammalt-namn nytt-namn"
 
-#: builtin/config.c:65
+#: builtin/config.c:67
 msgid "remove a section: name"
 msgstr "ta bort en sektion: namn"
 
-#: builtin/config.c:66
+#: builtin/config.c:68
 msgid "list all"
 msgstr "visa alla"
 
-#: builtin/config.c:67
+#: builtin/config.c:69
 msgid "open an editor"
 msgstr "öppna textredigeringsprogram"
 
-#: builtin/config.c:68 builtin/config.c:69
+#: builtin/config.c:70 builtin/config.c:71
 msgid "slot"
 msgstr "plats"
 
-#: builtin/config.c:68
+#: builtin/config.c:70
 msgid "find the color configured: [default]"
 msgstr "hitta den inställda färgen: [default]"
 
-#: builtin/config.c:69
+#: builtin/config.c:71
 msgid "find the color setting: [stdout-is-tty]"
 msgstr "hitta färginställningen: [stdout-is-tty]"
 
-#: builtin/config.c:70
+#: builtin/config.c:72
 msgid "Type"
 msgstr "Typ"
 
-#: builtin/config.c:71
+#: builtin/config.c:73
 msgid "value is \"true\" or \"false\""
 msgstr "värdet är \"true\" eller \"false\""
 
-#: builtin/config.c:72
+#: builtin/config.c:74
 msgid "value is decimal number"
 msgstr "värdet är ett decimalt tal"
 
-#: builtin/config.c:73
+#: builtin/config.c:75
 msgid "value is --bool or --int"
 msgstr "värdet är --bool eller --int"
 
-#: builtin/config.c:74
+#: builtin/config.c:76
 msgid "value is a path (file or directory name)"
 msgstr "värdet är en sökväg (fil- eller katalognamn)"
 
-#: builtin/config.c:75
+#: builtin/config.c:77
 msgid "Other"
 msgstr "Andra"
 
-#: builtin/config.c:76
+#: builtin/config.c:78
 msgid "terminate values with NUL byte"
 msgstr "terminera värden med NUL-byte"
 
-#: builtin/config.c:77
+#: builtin/config.c:79
 msgid "respect include directives on lookup"
 msgstr "respektera inkluderingsdirektiv vid uppslag"
 
-#: builtin/count-objects.c:69
-msgid "git count-objects [-v]"
-msgstr "git count-objects [-v]"
+#: builtin/count-objects.c:82
+msgid "git count-objects [-v] [-H | --human-readable]"
+msgstr "git count-objects [-v] [-H | --human-readable]"
 
-#: builtin/describe.c:15
+#: builtin/count-objects.c:97
+msgid "print sizes in human readable format"
+msgstr "skriv storlekar i människoläsbart format"
+
+#: builtin/describe.c:16
 msgid "git describe [options] <committish>*"
 msgstr "git describe [flaggor] <incheckning-igt>*"
 
-#: builtin/describe.c:16
+#: builtin/describe.c:17
 msgid "git describe [options] --dirty"
 msgstr "git describe [flaggor] --dirty"
 
-#: builtin/describe.c:234
+#: builtin/describe.c:237
 #, c-format
 msgid "annotated tag %s not available"
 msgstr "den annoterade taggen %s inte tillgänglig"
 
-#: builtin/describe.c:238
+#: builtin/describe.c:241
 #, c-format
 msgid "annotated tag %s has no embedded name"
 msgstr "den annoterade taggen %s har inget inbäddat namn"
 
-#: builtin/describe.c:240
+#: builtin/describe.c:243
 #, c-format
 msgid "tag '%s' is really '%s' here"
 msgstr "taggen \"%s\" är i verkligheten \"%s\" här"
 
-#: builtin/describe.c:267
+#: builtin/describe.c:270
 #, c-format
 msgid "Not a valid object name %s"
 msgstr "Objektnamnet är inte giltigt: %s"
 
-#: builtin/describe.c:270
+#: builtin/describe.c:273
 #, c-format
 msgid "%s is not a valid '%s' object"
 msgstr "%s är inte ett giltigt \"%s\"-objekt"
 
-#: builtin/describe.c:287
+#: builtin/describe.c:290
 #, c-format
 msgid "no tag exactly matches '%s'"
 msgstr "ingen tagg motsvarar \"%s\" exakt"
 
-#: builtin/describe.c:289
+#: builtin/describe.c:292
 #, c-format
 msgid "searching to describe %s\n"
 msgstr "söker för att beskriva %s\n"
 
-#: builtin/describe.c:329
+#: builtin/describe.c:332
 #, c-format
 msgid "finished search at %s\n"
 msgstr "avslutade sökning på %s\n"
 
-#: builtin/describe.c:353
+#: builtin/describe.c:359
 #, c-format
 msgid ""
 "No annotated tags can describe '%s'.\n"
@@ -4099,7 +4616,7 @@ msgstr ""
 "Inga annoterade taggar kan beskriva \"%s\".\n"
 "Det finns dock oannoterade taggar: testa --tags."
 
-#: builtin/describe.c:357
+#: builtin/describe.c:363
 #, c-format
 msgid ""
 "No tags can describe '%s'.\n"
@@ -4108,12 +4625,12 @@ msgstr ""
 "Inga taggar kan beskriva \"%s\".\n"
 "Testa --always, eller skapa några taggar."
 
-#: builtin/describe.c:378
+#: builtin/describe.c:384
 #, c-format
 msgid "traversed %lu commits\n"
 msgstr "traverserade %lu incheckningar\n"
 
-#: builtin/describe.c:381
+#: builtin/describe.c:387
 #, c-format
 msgid ""
 "more than %i tags found; listed %i most recent\n"
@@ -4122,59 +4639,63 @@ msgstr ""
 "mer än %i taggar hittades; listar de %i senaste\n"
 "gav upp sökningen vid %s\n"
 
-#: builtin/describe.c:403
+#: builtin/describe.c:409
 msgid "find the tag that comes after the commit"
 msgstr "hitta taggen som kommer efter incheckningen"
 
-#: builtin/describe.c:404
+#: builtin/describe.c:410
 msgid "debug search strategy on stderr"
 msgstr "felsök sökstrategin på standard fel"
 
-#: builtin/describe.c:405
-msgid "use any ref in .git/refs"
-msgstr "använd alla referenser i .git/refs"
+#: builtin/describe.c:411
+msgid "use any ref"
+msgstr "använd alla referenser"
 
-#: builtin/describe.c:406
-msgid "use any tag in .git/refs/tags"
-msgstr "använd alla taggar i .git/refs/tags"
+#: builtin/describe.c:412
+msgid "use any tag, even unannotated"
+msgstr "använd alla taggar, även oannoterade"
 
-#: builtin/describe.c:407
+#: builtin/describe.c:413
 msgid "always use long format"
 msgstr "använd alltid långt format"
 
-#: builtin/describe.c:410
+#: builtin/describe.c:414
+msgid "only follow first parent"
+msgstr "följ endast första föräldern"
+
+#: builtin/describe.c:417
 msgid "only output exact matches"
 msgstr "skriv endast ut exakta träffar"
 
-#: builtin/describe.c:412
+#: builtin/describe.c:419
 msgid "consider <n> most recent tags (default: 10)"
 msgstr "överväg de <n> nyaste taggarna (standard: 10)"
 
-#: builtin/describe.c:414
+#: builtin/describe.c:421
 msgid "only consider tags matching <pattern>"
 msgstr "överväg endast taggar som motsvarar <mönster>"
 
-#: builtin/describe.c:416 builtin/name-rev.c:238
+#: builtin/describe.c:423 builtin/name-rev.c:322
 msgid "show abbreviated commit object as fallback"
 msgstr "visa förkortade incheckningsobjekt som standard"
 
-#: builtin/describe.c:417
+#: builtin/describe.c:424
 msgid "mark"
 msgstr "märke"
 
-#: builtin/describe.c:418
+#: builtin/describe.c:425
 msgid "append <mark> on dirty working tree (default: \"-dirty\")"
 msgstr "lägg till <märke> på lortigt arbetsträd (standard: \"-dirty\")"
 
-#: builtin/describe.c:436
+#: builtin/describe.c:443
 msgid "--long is incompatible with --abbrev=0"
 msgstr "--long är inkompatibel med --abbrev=0"
 
-#: builtin/describe.c:462
+#: builtin/describe.c:469
 msgid "No names found, cannot describe anything."
 msgstr "Inga namn hittades, kan inte beskriva något."
 
-#: builtin/describe.c:482
+#: builtin/describe.c:489
 msgid "--dirty is incompatible with committishes"
 msgstr "--dirty är inkompatibelt med \"committish\"-värden"
 
@@ -4183,31 +4704,26 @@ msgstr "--dirty är inkompatibelt med \"committish\"-värden"
 msgid "'%s': not a regular file or symlink"
 msgstr "\"%s\": inte en normal fil eller symbolisk länk"
 
-#: builtin/diff.c:228
+#: builtin/diff.c:230
 #, c-format
 msgid "invalid option: %s"
 msgstr "ogiltig flagga: %s"
 
-#: builtin/diff.c:305
+#: builtin/diff.c:307
 msgid "Not a git repository"
 msgstr "Inte ett git-arkiv"
 
-#: builtin/diff.c:348
+#: builtin/diff.c:350
 #, c-format
 msgid "invalid object '%s' given."
 msgstr "objektet \"%s\" som angavs är felaktigt."
 
-#: builtin/diff.c:353
-#, c-format
-msgid "more than %d trees given: '%s'"
-msgstr "mer än %d träd angavs: \"%s\""
-
-#: builtin/diff.c:363
+#: builtin/diff.c:359
 #, c-format
 msgid "more than two blobs given: '%s'"
 msgstr "mer än två blobbar angavs: \"%s\""
 
-#: builtin/diff.c:371
+#: builtin/diff.c:366
 #, c-format
 msgid "unhandled object '%s' given."
 msgstr "ej hanterat objekt \"%s\" angavs."
@@ -4216,39 +4732,39 @@ msgstr "ej hanterat objekt \"%s\" angavs."
 msgid "git fast-export [rev-list-opts]"
 msgstr "git fast-export [rev-list-flaggor]"
 
-#: builtin/fast-export.c:646
+#: builtin/fast-export.c:666
 msgid "show progress after <n> objects"
 msgstr "visa förlopp efter <n> objekt"
 
-#: builtin/fast-export.c:648
+#: builtin/fast-export.c:668
 msgid "select handling of signed tags"
 msgstr "välj hantering av signerade taggar"
 
-#: builtin/fast-export.c:651
+#: builtin/fast-export.c:671
 msgid "select handling of tags that tag filtered objects"
 msgstr "välj hantering av taggar som har taggfiltrerade objekt"
 
-#: builtin/fast-export.c:654
+#: builtin/fast-export.c:674
 msgid "Dump marks to this file"
 msgstr "Dump märken till filen"
 
-#: builtin/fast-export.c:656
+#: builtin/fast-export.c:676
 msgid "Import marks from this file"
 msgstr "Importera märken från filen"
 
-#: builtin/fast-export.c:658
+#: builtin/fast-export.c:678
 msgid "Fake a tagger when tags lack one"
 msgstr "Fejka taggare när taggen saknar en"
 
-#: builtin/fast-export.c:660
+#: builtin/fast-export.c:680
 msgid "Output full tree for each commit"
 msgstr "Skriv ut hela trädet för varje incheckning"
 
-#: builtin/fast-export.c:662
+#: builtin/fast-export.c:682
 msgid "Use the done feature to terminate the stream"
 msgstr "Använd done-funktionen för att avsluta strömmen"
 
-#: builtin/fast-export.c:663
+#: builtin/fast-export.c:683
 msgid "Skip output of blob data"
 msgstr "Hoppa över skrivning av blob-data"
 
@@ -4324,7 +4840,7 @@ msgstr "fördjupa historik för grund klon"
 msgid "convert to a complete repository"
 msgstr "konvertera till komplett arkiv"
 
-#: builtin/fetch.c:88 builtin/log.c:1119
+#: builtin/fetch.c:88 builtin/log.c:1177
 msgid "dir"
 msgstr "kat"
 
@@ -4336,76 +4852,76 @@ msgstr "lägg till i början av undermodulens sökvägsutdata"
 msgid "default mode for recursion"
 msgstr "standardläge för rekursion"
 
-#: builtin/fetch.c:204
+#: builtin/fetch.c:220
 msgid "Couldn't find remote ref HEAD"
 msgstr "Kunde inte hitta fjärr-referensen HEAD"
 
-#: builtin/fetch.c:257
+#: builtin/fetch.c:273
 #, c-format
 msgid "object %s not found"
 msgstr "objektet %s hittades inte"
 
-#: builtin/fetch.c:262
+#: builtin/fetch.c:278
 msgid "[up to date]"
 msgstr "[àjour]"
 
-#: builtin/fetch.c:276
+#: builtin/fetch.c:292
 #, c-format
 msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
 msgstr "! %-*s %-*s -> %s  (kan inte hämta i aktuell gren)"
 
-#: builtin/fetch.c:277 builtin/fetch.c:363
+#: builtin/fetch.c:293 builtin/fetch.c:379
 msgid "[rejected]"
 msgstr "[refuserad]"
 
-#: builtin/fetch.c:288
+#: builtin/fetch.c:304
 msgid "[tag update]"
 msgstr "[uppdaterad tagg]"
 
-#: builtin/fetch.c:290 builtin/fetch.c:325 builtin/fetch.c:343
+#: builtin/fetch.c:306 builtin/fetch.c:341 builtin/fetch.c:359
 msgid "  (unable to update local ref)"
 msgstr "  (kunde inte uppdatera lokal ref)"
 
-#: builtin/fetch.c:308
+#: builtin/fetch.c:324
 msgid "[new tag]"
 msgstr "[ny tagg]"
 
-#: builtin/fetch.c:311
+#: builtin/fetch.c:327
 msgid "[new branch]"
 msgstr "[ny gren]"
 
-#: builtin/fetch.c:314
+#: builtin/fetch.c:330
 msgid "[new ref]"
 msgstr "[ny ref]"
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "unable to update local ref"
 msgstr "kunde inte uppdatera lokal ref"
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "forced update"
 msgstr "tvingad uppdatering"
 
-#: builtin/fetch.c:365
+#: builtin/fetch.c:381
 msgid "(non-fast-forward)"
 msgstr "(ej snabbspolad)"
 
-#: builtin/fetch.c:396 builtin/fetch.c:688
+#: builtin/fetch.c:412 builtin/fetch.c:718
 #, c-format
 msgid "cannot open %s: %s\n"
 msgstr "kan inte öppna %s: %s\n"
 
-#: builtin/fetch.c:405
+#: builtin/fetch.c:421
 #, c-format
 msgid "%s did not send all necessary objects\n"
 msgstr "%s sände inte alla nödvändiga objekt\n"
 
-#: builtin/fetch.c:491
+#: builtin/fetch.c:520
 #, c-format
 msgid "From %.*s\n"
 msgstr "Från %.*s\n"
 
-#: builtin/fetch.c:502
+#: builtin/fetch.c:531
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -4414,55 +4930,55 @@ msgstr ""
 "vissa lokala referenser kunde inte uppdateras; testa att köra\n"
 " \"git remote prune %s\" för att ta bort gamla grenar som står i konflikt"
 
-#: builtin/fetch.c:552
+#: builtin/fetch.c:581
 #, c-format
 msgid "   (%s will become dangling)"
 msgstr "   (%s kommer bli dinglande)"
 
-#: builtin/fetch.c:553
+#: builtin/fetch.c:582
 #, c-format
 msgid "   (%s has become dangling)"
 msgstr "   (%s har blivit dinglande)"
 
-#: builtin/fetch.c:560
+#: builtin/fetch.c:589
 msgid "[deleted]"
 msgstr "[borttagen]"
 
-#: builtin/fetch.c:561 builtin/remote.c:1055
+#: builtin/fetch.c:590 builtin/remote.c:1055
 msgid "(none)"
 msgstr "(ingen)"
 
-#: builtin/fetch.c:678
+#: builtin/fetch.c:708
 #, c-format
 msgid "Refusing to fetch into current branch %s of non-bare repository"
 msgstr "Vägrar hämta till aktuell gren %s i ett icke-naket arkiv"
 
-#: builtin/fetch.c:712
+#: builtin/fetch.c:742
 #, c-format
 msgid "Don't know how to fetch from %s"
 msgstr "Vet inte hur man hämtar från %s"
 
-#: builtin/fetch.c:789
+#: builtin/fetch.c:823
 #, c-format
 msgid "Option \"%s\" value \"%s\" is not valid for %s"
 msgstr "Flaggan \"%s\" och värdet \"%s\" är inte giltigt för %s"
 
-#: builtin/fetch.c:792
+#: builtin/fetch.c:826
 #, c-format
 msgid "Option \"%s\" is ignored for %s\n"
 msgstr "Flaggan \"%s\" ignoreras för %s\n"
 
-#: builtin/fetch.c:894
+#: builtin/fetch.c:928
 #, c-format
 msgid "Fetching %s\n"
 msgstr "Hämtar %s\n"
 
-#: builtin/fetch.c:896 builtin/remote.c:100
+#: builtin/fetch.c:930 builtin/remote.c:100
 #, c-format
 msgid "Could not fetch %s"
 msgstr "Kunde inte hämta %s"
 
-#: builtin/fetch.c:915
+#: builtin/fetch.c:949
 msgid ""
 "No remote repository specified.  Please, specify either a URL or a\n"
 "remote name from which new revisions should be fetched."
@@ -4470,32 +4986,32 @@ msgstr ""
 "Inget fjärrarkiv angavs. Ange antingen en URL eller namnet på ett\n"
 "fjärrarkiv som nya incheckningar skall hämtas från."
 
-#: builtin/fetch.c:935
+#: builtin/fetch.c:969
 msgid "You need to specify a tag name."
 msgstr "Du måste ange namnet på en tagg."
 
-#: builtin/fetch.c:981
+#: builtin/fetch.c:1015
 msgid "--depth and --unshallow cannot be used together"
 msgstr "--depth och --unshallow kan inte användas samtidigt"
 
-#: builtin/fetch.c:983
+#: builtin/fetch.c:1017
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "--unshallow kan inte användas på ett komplett arkiv"
 
-#: builtin/fetch.c:1002
+#: builtin/fetch.c:1036
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all tar inte namnet på ett arkiv som argument"
 
-#: builtin/fetch.c:1004
+#: builtin/fetch.c:1038
 msgid "fetch --all does not make sense with refspecs"
 msgstr "fetch --all kan inte anges med referensspecifikationer"
 
-#: builtin/fetch.c:1015
+#: builtin/fetch.c:1049
 #, c-format
 msgid "No such remote or remote group: %s"
 msgstr "Fjärren eller fjärrgruppen finns inte: %s"
 
-#: builtin/fetch.c:1023
+#: builtin/fetch.c:1057
 msgid "Fetching a group and specifying refspecs does not make sense"
 msgstr "Kan inte hämta från grupp och ange referensspecifikationer"
 
@@ -4504,29 +5020,29 @@ msgid "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"
 msgstr ""
 "git fmt-merge-msg [-m <meddelande>] [--log[=<n>]|--no-log] [--file <fil>]"
 
-#: builtin/fmt-merge-msg.c:659 builtin/fmt-merge-msg.c:662 builtin/grep.c:701
-#: builtin/merge.c:188 builtin/show-branch.c:656 builtin/show-ref.c:175
-#: builtin/tag.c:446 parse-options.h:133 parse-options.h:239
+#: builtin/fmt-merge-msg.c:663 builtin/fmt-merge-msg.c:666 builtin/grep.c:700
+#: builtin/merge.c:203 builtin/show-branch.c:655 builtin/show-ref.c:179
+#: builtin/tag.c:446 parse-options.h:133 parse-options.h:243
 msgid "n"
 msgstr "n"
 
-#: builtin/fmt-merge-msg.c:660
+#: builtin/fmt-merge-msg.c:664
 msgid "populate log with at most <n> entries from shortlog"
 msgstr "fyll i loggen med som mest <n> poster från shortlog"
 
-#: builtin/fmt-merge-msg.c:663
+#: builtin/fmt-merge-msg.c:667
 msgid "alias for --log (deprecated)"
 msgstr "alias för --log (avråds)"
 
-#: builtin/fmt-merge-msg.c:666
+#: builtin/fmt-merge-msg.c:670
 msgid "text"
 msgstr "text"
 
-#: builtin/fmt-merge-msg.c:667
+#: builtin/fmt-merge-msg.c:671
 msgid "use <text> as start of message"
 msgstr "inled meddelande med <text>"
 
-#: builtin/fmt-merge-msg.c:668
+#: builtin/fmt-merge-msg.c:672
 msgid "file to read from"
 msgstr "fil att läsa från"
 
@@ -4665,225 +5181,220 @@ msgstr "git grep [flaggor] [-e] <mönster> [<rev>...] [[--] <sökväg>...]"
 msgid "grep: failed to create thread: %s"
 msgstr "grep: misslyckades skapa tråd. %s"
 
-#: builtin/grep.c:365
+#: builtin/grep.c:364
 #, c-format
 msgid "Failed to chdir: %s"
 msgstr "Kunde inte byta katalog (chdir): %s"
 
-#: builtin/grep.c:443 builtin/grep.c:478
+#: builtin/grep.c:442 builtin/grep.c:477
 #, c-format
 msgid "unable to read tree (%s)"
 msgstr "kunde inte läsa träd (%s)"
 
-#: builtin/grep.c:493
+#: builtin/grep.c:492
 #, c-format
 msgid "unable to grep from object of type %s"
 msgstr "Kunde inte \"grep\" från objekt av typen %s"
 
-#: builtin/grep.c:551
+#: builtin/grep.c:550
 #, c-format
 msgid "switch `%c' expects a numerical value"
 msgstr "flaggan \"%c\" antar ett numeriskt värde"
 
-#: builtin/grep.c:568
+#: builtin/grep.c:567
 #, c-format
 msgid "cannot open '%s'"
 msgstr "kan inte öppna \"%s\""
 
-#: builtin/grep.c:643
+#: builtin/grep.c:642
 msgid "search in index instead of in the work tree"
 msgstr "sök i indexet istället för i arbetskatalogen"
 
-#: builtin/grep.c:645
+#: builtin/grep.c:644
 msgid "find in contents not managed by git"
 msgstr "sök i innehål som inte hanteras av git"
 
-#: builtin/grep.c:647
+#: builtin/grep.c:646
 msgid "search in both tracked and untracked files"
 msgstr "sök i både spårade och ospårade filer"
 
-#: builtin/grep.c:649
+#: builtin/grep.c:648
 msgid "search also in ignored files"
 msgstr "sök även i ignorerade filer"
 
-#: builtin/grep.c:652
+#: builtin/grep.c:651
 msgid "show non-matching lines"
-msgstr "vis rader som inte träffas"
+msgstr "visa rader som inte träffas"
 
-#: builtin/grep.c:654
+#: builtin/grep.c:653
 msgid "case insensitive matching"
 msgstr "skiftlägesokänslig sökning"
 
-#: builtin/grep.c:656
+#: builtin/grep.c:655
 msgid "match patterns only at word boundaries"
 msgstr "matcha endast mönster vid ordgränser"
 
-#: builtin/grep.c:658
+#: builtin/grep.c:657
 msgid "process binary files as text"
 msgstr "hantera binärfiler som text"
 
-#: builtin/grep.c:660
+#: builtin/grep.c:659
 msgid "don't match patterns in binary files"
 msgstr "träffa inte mönster i binärfiler"
 
-#: builtin/grep.c:663
+#: builtin/grep.c:662
 msgid "descend at most <depth> levels"
 msgstr "gå som mest ned <djup> nivåer"
 
-#: builtin/grep.c:667
+#: builtin/grep.c:666
 msgid "use extended POSIX regular expressions"
 msgstr "använd utökade POSIX-reguljära uttryck"
 
-#: builtin/grep.c:670
+#: builtin/grep.c:669
 msgid "use basic POSIX regular expressions (default)"
 msgstr "använd grundläggande POSIX-reguljära uttryck (standard)"
 
-#: builtin/grep.c:673
+#: builtin/grep.c:672
 msgid "interpret patterns as fixed strings"
 msgstr "tolka mönster som fixerade strängar"
 
-#: builtin/grep.c:676
+#: builtin/grep.c:675
 msgid "use Perl-compatible regular expressions"
 msgstr "använd Perlkompatibla reguljära uttryck"
 
-#: builtin/grep.c:679
+#: builtin/grep.c:678
 msgid "show line numbers"
 msgstr "visa radnummer"
 
-#: builtin/grep.c:680
+#: builtin/grep.c:679
 msgid "don't show filenames"
 msgstr "visa inte filnamn"
 
-#: builtin/grep.c:681
+#: builtin/grep.c:680
 msgid "show filenames"
 msgstr "visa filnamn"
 
-#: builtin/grep.c:683
+#: builtin/grep.c:682
 msgid "show filenames relative to top directory"
 msgstr "visa filnamn relativa till toppkatalogen"
 
-#: builtin/grep.c:685
+#: builtin/grep.c:684
 msgid "show only filenames instead of matching lines"
 msgstr "visa endast filnamn istället för träffade rader"
 
-#: builtin/grep.c:687
+#: builtin/grep.c:686
 msgid "synonym for --files-with-matches"
 msgstr "synonym för --files-with-matches"
 
-#: builtin/grep.c:690
+#: builtin/grep.c:689
 msgid "show only the names of files without match"
 msgstr "visa endast namn på filer utan träffar"
 
-#: builtin/grep.c:692
+#: builtin/grep.c:691
 msgid "print NUL after filenames"
 msgstr "skriv NUL efter filnamn"
 
-#: builtin/grep.c:694
+#: builtin/grep.c:693
 msgid "show the number of matches instead of matching lines"
 msgstr "visa antal träffar istället för träffade rader"
 
-#: builtin/grep.c:695
+#: builtin/grep.c:694
 msgid "highlight matches"
 msgstr "ljusmarkera träffar"
 
-#: builtin/grep.c:697
+#: builtin/grep.c:696
 msgid "print empty line between matches from different files"
 msgstr "skriv tomma rader mellan träffar från olika filer"
 
-#: builtin/grep.c:699
+#: builtin/grep.c:698
 msgid "show filename only once above matches from same file"
 msgstr "visa filnamn endast en gång ovanför träffar från samma fil"
 
-#: builtin/grep.c:702
+#: builtin/grep.c:701
 msgid "show <n> context lines before and after matches"
 msgstr "visa <n> rader sammanhang före och efter träffar"
 
-#: builtin/grep.c:705
+#: builtin/grep.c:704
 msgid "show <n> context lines before matches"
 msgstr "visa <n> rader sammanhang före träffar"
 
-#: builtin/grep.c:707
+#: builtin/grep.c:706
 msgid "show <n> context lines after matches"
 msgstr "visa <n> rader sammanhang efter träffar"
 
-#: builtin/grep.c:708
+#: builtin/grep.c:707
 msgid "shortcut for -C NUM"
 msgstr "genväg för -C NUM"
 
-#: builtin/grep.c:711
+#: builtin/grep.c:710
 msgid "show a line with the function name before matches"
 msgstr "visa en rad med funktionsnamnet före träffen"
 
-#: builtin/grep.c:713
+#: builtin/grep.c:712
 msgid "show the surrounding function"
 msgstr "visa den omkringliggande funktionen"
 
-#: builtin/grep.c:716
+#: builtin/grep.c:715
 msgid "read patterns from file"
 msgstr "läs mönster från fil"
 
-#: builtin/grep.c:718
+#: builtin/grep.c:717
 msgid "match <pattern>"
 msgstr "träffa <mönster>"
 
-#: builtin/grep.c:720
+#: builtin/grep.c:719
 msgid "combine patterns specified with -e"
 msgstr "kombinera mönster som anges med -e"
 
-#: builtin/grep.c:732
+#: builtin/grep.c:731
 msgid "indicate hit with exit status without output"
 msgstr "ange träff med slutstatuskod utan utdata"
 
-#: builtin/grep.c:734
+#: builtin/grep.c:733
 msgid "show only matches from files that match all patterns"
 msgstr "visa endast träffar från filer som träffar alla mönster"
 
-#: builtin/grep.c:736
+#: builtin/grep.c:735
 msgid "show parse tree for grep expression"
 msgstr "visa analysträd för grep-uttryck"
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "pager"
 msgstr "bläddrare"
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "show matching files in the pager"
 msgstr "visa träffade filer i filbläddraren"
 
-#: builtin/grep.c:743
+#: builtin/grep.c:742
 msgid "allow calling of grep(1) (ignored by this build)"
 msgstr "tillåt anropa grep(1) (ignoreras av detta bygge)"
 
-#: builtin/grep.c:744 builtin/show-ref.c:184
+#: builtin/grep.c:743 builtin/show-ref.c:188
 msgid "show usage"
 msgstr "visa användning"
 
-#: builtin/grep.c:811
+#: builtin/grep.c:810
 msgid "no pattern given."
 msgstr "inget mönster angavs."
 
-#: builtin/grep.c:825
-#, c-format
-msgid "bad object %s"
-msgstr "felaktigt objekt %s"
-
-#: builtin/grep.c:868
+#: builtin/grep.c:865
 msgid "--open-files-in-pager only works on the worktree"
 msgstr "--open-files-in-pager fungerar endast i arbetskatalogen"
 
-#: builtin/grep.c:891
+#: builtin/grep.c:888
 msgid "--cached or --untracked cannot be used with --no-index."
 msgstr "--cached och --untracked kan inte användas med --no-index."
 
-#: builtin/grep.c:896
+#: builtin/grep.c:893
 msgid "--no-index or --untracked cannot be used with revs."
 msgstr "--no-index och --untracked kan inte användas med revisioner."
 
-#: builtin/grep.c:899
+#: builtin/grep.c:896
 msgid "--[no-]exclude-standard cannot be used for tracked contents."
 msgstr "--[no-]exclude-standard kan inte användas för spårat innehåll."
 
-#: builtin/grep.c:907
+#: builtin/grep.c:904
 msgid "both --cached and trees are given."
 msgstr "både --cached och träd angavs."
 
@@ -4923,10 +5434,14 @@ msgstr "spara filen som den är utan filer"
 msgid "process file as it were from this path"
 msgstr "hantera filen som om den kom från sökvägen"
 
-#: builtin/help.c:42
+#: builtin/help.c:41
 msgid "print all available commands"
 msgstr "visa alla tillgängliga kommandon"
 
+#: builtin/help.c:42
+msgid "print list of useful guides"
+msgstr "lista användbara vägledningar"
+
 #: builtin/help.c:43
 msgid "show man page"
 msgstr "visa manualsida"
@@ -4940,8 +5455,8 @@ msgid "show info page"
 msgstr "visa info-sida"
 
 #: builtin/help.c:52
-msgid "git help [--all] [--man|--web|--info] [command]"
-msgstr "git help [--all] [--man|--web|--info] [kommando]"
+msgid "git help [--all] [--guides] [--man|--web|--info] [command]"
+msgstr "git help [--all] [--guides] [--man|--web|--info] [kommando]"
 
 #: builtin/help.c:64
 #, c-format
@@ -4997,290 +5512,322 @@ msgstr "ingen man-visare hanterade förfrågan"
 msgid "no info viewer handled the request"
 msgstr "ingen info-visare hanterade förfrågan"
 
-#: builtin/help.c:429 builtin/help.c:436
+#: builtin/help.c:420
+msgid "Defining attributes per path"
+msgstr "Definierar attribut per sökväg"
+
+#: builtin/help.c:421
+msgid "A Git glossary"
+msgstr "En Git-ordlista"
+
+#: builtin/help.c:422
+msgid "Specifies intentionally untracked files to ignore"
+msgstr "Ange avsiktligen ospårade filer att ignorera"
+
+#: builtin/help.c:423
+msgid "Defining submodule properties"
+msgstr "Ange egenskaper för undermoduler"
+
+#: builtin/help.c:424
+msgid "Specifying revisions and ranges for Git"
+msgstr "Ange versioner och intervall i Git"
+
+#: builtin/help.c:425
+msgid "A tutorial introduction to Git (for version 1.5.1 or newer)"
+msgstr "Introduktion till Git (för version 1.5.1 och senare)"
+
+#: builtin/help.c:426
+msgid "An overview of recommended workflows with Git"
+msgstr "Översikt över rekommenderade arbetsflöden med Git"
+
+#: builtin/help.c:438
+msgid "The common Git guides are:\n"
+msgstr "De vanliga Git-vägledningarna är:\n"
+
+#: builtin/help.c:460 builtin/help.c:476
 #, c-format
 msgid "usage: %s%s"
 msgstr "användning: %s%s"
 
-#: builtin/help.c:452
+#: builtin/help.c:492
 #, c-format
 msgid "`git %s' is aliased to `%s'"
 msgstr "\"git %s\" är ett alias för \"%s\""
 
-#: builtin/index-pack.c:170
+#: builtin/index-pack.c:184
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "objekttyp stämmer inte överens vid %s"
 
-#: builtin/index-pack.c:190
+#: builtin/index-pack.c:204
 msgid "object of unexpected type"
 msgstr "objekt av oväntad typ"
 
-#: builtin/index-pack.c:227
+#: builtin/index-pack.c:244
 #, c-format
 msgid "cannot fill %d byte"
 msgid_plural "cannot fill %d bytes"
 msgstr[0] "kan inte fylla %d byte"
 msgstr[1] "kan inte fylla %d byte"
 
-#: builtin/index-pack.c:237
+#: builtin/index-pack.c:254
 msgid "early EOF"
 msgstr "tidigt filslut"
 
-#: builtin/index-pack.c:238
+#: builtin/index-pack.c:255
 msgid "read error on input"
 msgstr "indataläsfel"
 
-#: builtin/index-pack.c:250
+#: builtin/index-pack.c:267
 msgid "used more bytes than were available"
 msgstr "använde fler byte än tillgängligt"
 
-#: builtin/index-pack.c:257
+#: builtin/index-pack.c:274
 msgid "pack too large for current definition of off_t"
 msgstr "paket för stort för nuvarande definition av off_t"
 
-#: builtin/index-pack.c:273
+#: builtin/index-pack.c:290
 #, c-format
 msgid "unable to create '%s'"
 msgstr "kunde inte skapa \"%s\""
 
-#: builtin/index-pack.c:278
+#: builtin/index-pack.c:295
 #, c-format
 msgid "cannot open packfile '%s'"
 msgstr "kan inte öppna paketfilen \"%s\""
 
-#: builtin/index-pack.c:292
+#: builtin/index-pack.c:309
 msgid "pack signature mismatch"
 msgstr "paketsignatur stämmer inte överens"
 
-#: builtin/index-pack.c:294
+#: builtin/index-pack.c:311
 #, c-format
 msgid "pack version %<PRIu32> unsupported"
 msgstr "paketversion %<PRIu32> stöds ej"
 
-#: builtin/index-pack.c:312
+#: builtin/index-pack.c:329
 #, c-format
 msgid "pack has bad object at offset %lu: %s"
 msgstr "paketet har felaktigt objekt vid index %lu: %s"
 
-#: builtin/index-pack.c:434
+#: builtin/index-pack.c:451
 #, c-format
 msgid "inflate returned %d"
 msgstr "inflate returnerade %d"
 
-#: builtin/index-pack.c:483
+#: builtin/index-pack.c:500
 msgid "offset value overflow for delta base object"
 msgstr "indexvärdespill för deltabasobjekt"
 
-#: builtin/index-pack.c:491
+#: builtin/index-pack.c:508
 msgid "delta base offset is out of bound"
 msgstr "deltabasindex utanför gränsen"
 
-#: builtin/index-pack.c:499
+#: builtin/index-pack.c:516
 #, c-format
 msgid "unknown object type %d"
 msgstr "okänd objekttyp %d"
 
-#: builtin/index-pack.c:530
+#: builtin/index-pack.c:547
 msgid "cannot pread pack file"
 msgstr "kan inte utföra \"pread\" på paketfil"
 
-#: builtin/index-pack.c:532
+#: builtin/index-pack.c:549
 #, c-format
 msgid "premature end of pack file, %lu byte missing"
 msgid_plural "premature end of pack file, %lu bytes missing"
 msgstr[0] "för tidigt slut på paketfilen, %lu byte saknas"
 msgstr[1] "för tidigt slut på paketfilen, %lu byte saknas"
 
-#: builtin/index-pack.c:558
+#: builtin/index-pack.c:575
 msgid "serious inflate inconsistency"
 msgstr "allvarlig inflate-inkonsekvens"
 
-#: builtin/index-pack.c:649 builtin/index-pack.c:655 builtin/index-pack.c:678
-#: builtin/index-pack.c:712 builtin/index-pack.c:721
+#: 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
 #, c-format
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "SHA1-KOLLISION UPPTÄCKT VID %s !"
 
-#: builtin/index-pack.c:652 builtin/pack-objects.c:170
+#: builtin/index-pack.c:669 builtin/pack-objects.c:170
 #: builtin/pack-objects.c:262
 #, c-format
 msgid "unable to read %s"
 msgstr "kunde inte läsa %s"
 
-#: builtin/index-pack.c:718
+#: builtin/index-pack.c:735
 #, c-format
 msgid "cannot read existing object %s"
 msgstr "kan inte läsa befintligt objekt %s"
 
-#: builtin/index-pack.c:732
+#: builtin/index-pack.c:749
 #, c-format
 msgid "invalid blob object %s"
 msgstr "ogiltigt blob-objekt %s"
 
-#: builtin/index-pack.c:747
+#: builtin/index-pack.c:763
 #, c-format
 msgid "invalid %s"
 msgstr "ogiltigt %s"
 
-#: builtin/index-pack.c:749
+#: builtin/index-pack.c:766
 msgid "Error in object"
 msgstr "Fel i objekt"
 
-#: builtin/index-pack.c:751
+#: builtin/index-pack.c:768
 #, c-format
 msgid "Not all child objects of %s are reachable"
 msgstr "Inte alla barnobjekt för %s kan nås"
 
-#: builtin/index-pack.c:821 builtin/index-pack.c:847
+#: builtin/index-pack.c:838 builtin/index-pack.c:868
 msgid "failed to apply delta"
 msgstr "misslyckades tillämpa delta"
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Receiving objects"
 msgstr "Tar emot objekt"
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Indexing objects"
 msgstr "Skapar index för objekt"
 
-#: builtin/index-pack.c:1012
+#: builtin/index-pack.c:1035
 msgid "pack is corrupted (SHA1 mismatch)"
 msgstr "paketet är trasigt (SHA1 stämmer inte)"
 
-#: builtin/index-pack.c:1017
+#: builtin/index-pack.c:1040
 msgid "cannot fstat packfile"
 msgstr "kan inte utföra \"fstat\" på paketfil"
 
-#: builtin/index-pack.c:1020
+#: builtin/index-pack.c:1043
 msgid "pack has junk at the end"
 msgstr "paket har skräp i slutet"
 
-#: builtin/index-pack.c:1031
+#: builtin/index-pack.c:1054
 msgid "confusion beyond insanity in parse_pack_objects()"
 msgstr "förvirrad bortom vanvett i parse_pack_objects()"
 
-#: builtin/index-pack.c:1054
+#: builtin/index-pack.c:1077
 msgid "Resolving deltas"
 msgstr "Analyserar delta"
 
-#: builtin/index-pack.c:1064
+#: builtin/index-pack.c:1087
 #, c-format
 msgid "unable to create thread: %s"
 msgstr "kunde inte skapa tråd: %s"
 
-#: builtin/index-pack.c:1106
+#: builtin/index-pack.c:1129
 msgid "confusion beyond insanity"
 msgstr "förvirrad bortom vanvett"
 
-#: builtin/index-pack.c:1112
+#: builtin/index-pack.c:1137
 #, c-format
 msgid "completed with %d local objects"
 msgstr "slutfördes med %d lokala objekt"
 
-#: builtin/index-pack.c:1121
+#: builtin/index-pack.c:1147
 #, c-format
 msgid "Unexpected tail checksum for %s (disk corruption?)"
 msgstr "Oväntad svanschecksumma för %s (trasig disk?)"
 
-#: builtin/index-pack.c:1125
+#: builtin/index-pack.c:1151
 #, c-format
 msgid "pack has %d unresolved delta"
 msgid_plural "pack has %d unresolved deltas"
 msgstr[0] "paketet har %d oanalyserat delta"
 msgstr[1] "paketet har %d oanalyserade delta"
 
-#: builtin/index-pack.c:1150
+#: builtin/index-pack.c:1176
 #, c-format
 msgid "unable to deflate appended object (%d)"
 msgstr "kunde inte utföra \"deflate\" på tillagt objekt (%d)"
 
-#: builtin/index-pack.c:1229
+#: builtin/index-pack.c:1255
 #, c-format
 msgid "local object %s is corrupt"
 msgstr "lokalt objekt %s är trasigt"
 
-#: builtin/index-pack.c:1253
+#: builtin/index-pack.c:1279
 msgid "error while closing pack file"
 msgstr "fel vid stängning av paketfil"
 
-#: builtin/index-pack.c:1266
+#: builtin/index-pack.c:1292
 #, c-format
 msgid "cannot write keep file '%s'"
 msgstr "kan inte ta skriva \"keep\"-fil \"%s\""
 
-#: builtin/index-pack.c:1274
+#: builtin/index-pack.c:1300
 #, c-format
 msgid "cannot close written keep file '%s'"
 msgstr "akn inte stänga skriven \"keep\"-fil \"%s\""
 
-#: builtin/index-pack.c:1287
+#: builtin/index-pack.c:1313
 msgid "cannot store pack file"
 msgstr "kan inte spara paketfil"
 
-#: builtin/index-pack.c:1298
+#: builtin/index-pack.c:1324
 msgid "cannot store index file"
 msgstr "kan inte spara indexfil"
 
-#: builtin/index-pack.c:1331
+#: builtin/index-pack.c:1357
 #, c-format
 msgid "bad pack.indexversion=%<PRIu32>"
 msgstr "felaktig pack.indexversion=%<PRIu32>"
 
-#: builtin/index-pack.c:1337
+#: builtin/index-pack.c:1363
 #, c-format
 msgid "invalid number of threads specified (%d)"
 msgstr "felaktigt antal trådar angivet (%d)"
 
-#: builtin/index-pack.c:1341 builtin/index-pack.c:1514
+#: builtin/index-pack.c:1367 builtin/index-pack.c:1545
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "trådstöd saknas, ignorerar %s"
 
-#: builtin/index-pack.c:1399
+#: builtin/index-pack.c:1425
 #, c-format
 msgid "Cannot open existing pack file '%s'"
 msgstr "Kan inte öppna befintlig paketfil \"%s\""
 
-#: builtin/index-pack.c:1401
+#: builtin/index-pack.c:1427
 #, c-format
 msgid "Cannot open existing pack idx file for '%s'"
 msgstr "Kan inte öppna befintligt paket-idx-fil för \"%s\""
 
-#: builtin/index-pack.c:1448
+#: builtin/index-pack.c:1474
 #, c-format
 msgid "non delta: %d object"
 msgid_plural "non delta: %d objects"
 msgstr[0] "icke-delta: %d objekt"
 msgstr[1] "icke-delta: %d objekt"
 
-#: builtin/index-pack.c:1455
+#: builtin/index-pack.c:1481
 #, c-format
 msgid "chain length = %d: %lu object"
 msgid_plural "chain length = %d: %lu objects"
 msgstr[0] "kedjelängd = %d: %lu objekt"
 msgstr[1] "kedjelängd = %d: %lu objekt"
 
-#: builtin/index-pack.c:1482
+#: builtin/index-pack.c:1509
 msgid "Cannot come back to cwd"
 msgstr "Kan inte gå tillbaka till arbetskatalogen (cwd)"
 
-#: builtin/index-pack.c:1526 builtin/index-pack.c:1529
-#: builtin/index-pack.c:1541 builtin/index-pack.c:1545
+#: builtin/index-pack.c:1557 builtin/index-pack.c:1560
+#: builtin/index-pack.c:1572 builtin/index-pack.c:1576
 #, c-format
 msgid "bad %s"
 msgstr "felaktig %s"
 
-#: builtin/index-pack.c:1559
+#: builtin/index-pack.c:1590
 msgid "--fix-thin cannot be used without --stdin"
 msgstr "--fix-thin kan inte användas med --stdin"
 
-#: builtin/index-pack.c:1563 builtin/index-pack.c:1573
+#: builtin/index-pack.c:1594 builtin/index-pack.c:1604
 #, c-format
 msgid "packfile name '%s' does not end with '.pack'"
 msgstr "paketfilnamnet \"%s\" slutar inte med \".pack\""
 
-#: builtin/index-pack.c:1582
+#: builtin/index-pack.c:1613
 msgid "--verify with no packfile name given"
 msgstr "--verify angavs utan paketfilnamn"
 
@@ -5416,7 +5963,7 @@ msgstr "behörigheter"
 msgid "specify that the git repository is to be shared amongst several users"
 msgstr "ange att git-arkivet skall delas bland flera användare"
 
-#: builtin/init-db.c:493 builtin/prune-packed.c:77
+#: builtin/init-db.c:493 builtin/prune-packed.c:75
 msgid "be quiet"
 msgstr "var tyst"
 
@@ -5448,345 +5995,353 @@ msgstr "Kan inte komma åt aktuell arbetskatalog"
 msgid "Cannot access work tree '%s'"
 msgstr "Kan inte komma åt arbetskatalogen \"%s\""
 
-#: builtin/log.c:39
-msgid "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
-msgstr "git log [<flaggor>] [<sedan>..<till>] [[--] <sökväg>...]\n"
+#: builtin/log.c:41
+msgid "git log [<options>] [<revision range>] [[--] <path>...]\n"
+msgstr "git log [<flaggor>] [<versionsintervall>] [[--] <sökväg>...]\n"
 
-#: builtin/log.c:40
+#: builtin/log.c:42
 msgid "   or: git show [options] <object>..."
 msgstr "     eller: git show [flaggor] <objekt>..."
 
-#: builtin/log.c:102
+#: builtin/log.c:124
 msgid "suppress diff output"
 msgstr "undertryck diff-utdata"
 
-#: builtin/log.c:103
+#: builtin/log.c:125
 msgid "show source"
 msgstr "visa källkod"
 
-#: builtin/log.c:104
+#: builtin/log.c:126
 msgid "Use mail map file"
 msgstr "Använd e-postmappningsfil"
 
-#: builtin/log.c:105
+#: builtin/log.c:127
 msgid "decorate options"
 msgstr "dekoreringsflaggor"
 
-#: builtin/log.c:198
+#: builtin/log.c:230
 #, c-format
 msgid "Final output: %d %s\n"
 msgstr "Slututdata: %d %s\n"
 
-#: builtin/log.c:419 builtin/log.c:511
+#: builtin/log.c:453 builtin/log.c:545
 #, c-format
 msgid "Could not read object %s"
 msgstr "Kunde inte läsa objektet %s"
 
-#: builtin/log.c:535
+#: builtin/log.c:569
 #, c-format
 msgid "Unknown type: %d"
 msgstr "Okänd typ: %d"
 
-#: builtin/log.c:627
+#: builtin/log.c:669
 msgid "format.headers without value"
 msgstr "format.headers utan värde"
 
-#: builtin/log.c:701
+#: builtin/log.c:751
 msgid "name of output directory is too long"
 msgstr "namnet på utdatakatalogen är för långt"
 
-#: builtin/log.c:717
+#: builtin/log.c:767
 #, c-format
 msgid "Cannot open patch file %s"
 msgstr "Kan inte öppna patchfilen %s"
 
-#: builtin/log.c:731
+#: builtin/log.c:781
 msgid "Need exactly one range."
 msgstr "Behöver precis ett intervall."
 
-#: builtin/log.c:739
+#: builtin/log.c:789
 msgid "Not a range."
 msgstr "Inte ett intervall."
 
-#: builtin/log.c:812
+#: builtin/log.c:891
 msgid "Cover letter needs email format"
 msgstr "Omslagsbrevet behöver e-postformat"
 
-#: builtin/log.c:885
+#: builtin/log.c:967
 #, c-format
 msgid "insane in-reply-to: %s"
 msgstr "tokigt in-reply-to: %s"
 
-#: builtin/log.c:913
+#: builtin/log.c:995
 msgid "git format-patch [options] [<since> | <revision range>]"
 msgstr "git format-patch [flaggor] [<sedan> | <revisionsintervall>]"
 
-#: builtin/log.c:958
+#: builtin/log.c:1040
 msgid "Two output directories?"
 msgstr "Två utdatakataloger?"
 
-#: builtin/log.c:1097
+#: builtin/log.c:1155
 msgid "use [PATCH n/m] even with a single patch"
 msgstr "använd [PATCH n/m] även för en ensam patch"
 
-#: builtin/log.c:1100
+#: builtin/log.c:1158
 msgid "use [PATCH] even with multiple patches"
 msgstr "använd [PATCH] även för flera patchar"
 
-#: builtin/log.c:1104
+#: builtin/log.c:1162
 msgid "print patches to standard out"
 msgstr "skriv patcharna på stnadard ut"
 
-#: builtin/log.c:1106
+#: builtin/log.c:1164
 msgid "generate a cover letter"
 msgstr "generera ett följebrev"
 
-#: builtin/log.c:1108
+#: builtin/log.c:1166
 msgid "use simple number sequence for output file names"
 msgstr "använd enkel nummersekvens för utdatafilnamn"
 
-#: builtin/log.c:1109
+#: builtin/log.c:1167
 msgid "sfx"
 msgstr "sfx"
 
-#: builtin/log.c:1110
+#: builtin/log.c:1168
 msgid "use <sfx> instead of '.patch'"
 msgstr "använd <sfx> istället för \".patch\""
 
-#: builtin/log.c:1112
+#: builtin/log.c:1170
 msgid "start numbering patches at <n> instead of 1"
 msgstr "börja numrera patchar på <n> istället för 1"
 
-#: builtin/log.c:1114
+#: builtin/log.c:1172
 msgid "mark the series as Nth re-roll"
 msgstr "markera serien som N:te försök"
 
-#: builtin/log.c:1116
+#: builtin/log.c:1174
 msgid "Use [<prefix>] instead of [PATCH]"
 msgstr "Använd [<prefix>] istället för [PATCH]"
 
-#: builtin/log.c:1119
+#: builtin/log.c:1177
 msgid "store resulting files in <dir>"
 msgstr "spara filerna i <katalog>"
 
-#: builtin/log.c:1122
+#: builtin/log.c:1180
 msgid "don't strip/add [PATCH]"
 msgstr "ta inte bort eller lägg till [PATCH]"
 
-#: builtin/log.c:1125
+#: builtin/log.c:1183
 msgid "don't output binary diffs"
 msgstr "skriv inte binära diffar"
 
-#: builtin/log.c:1127
+#: builtin/log.c:1185
 msgid "don't include a patch matching a commit upstream"
 msgstr "ta inte med patchar som motsvarar en uppströmsincheckning"
 
-#: builtin/log.c:1129
+#: builtin/log.c:1187
 msgid "show patch format instead of default (patch + stat)"
 msgstr "visa patchformat istället för standard (patch + stat)"
 
-#: builtin/log.c:1131
+#: builtin/log.c:1189
 msgid "Messaging"
 msgstr "E-post"
 
-#: builtin/log.c:1132
+#: builtin/log.c:1190
 msgid "header"
 msgstr "huvud"
 
-#: builtin/log.c:1133
+#: builtin/log.c:1191
 msgid "add email header"
 msgstr "lägg till e-posthuvud"
 
-#: builtin/log.c:1134 builtin/log.c:1136
+#: builtin/log.c:1192 builtin/log.c:1194
 msgid "email"
 msgstr "epost"
 
-#: builtin/log.c:1134
+#: builtin/log.c:1192
 msgid "add To: header"
 msgstr "Lägg till mottagarhuvud (\"To:\")"
 
-#: builtin/log.c:1136
+#: builtin/log.c:1194
 msgid "add Cc: header"
 msgstr "Lägg till kopiehuvud (\"Cc:\")"
 
-#: builtin/log.c:1138
+#: builtin/log.c:1196
+msgid "ident"
+msgstr "ident"
+
+#: builtin/log.c:1197
+msgid "set From address to <ident> (or committer ident if absent)"
+msgstr "sätt Från-adress till <ident> (eller incheckare om ident saknas)"
+
+#: builtin/log.c:1199
 msgid "message-id"
 msgstr "meddelande-id"
 
-#: builtin/log.c:1139
+#: builtin/log.c:1200
 msgid "make first mail a reply to <message-id>"
 msgstr "Gör det första brevet ett svar till <meddelande-id>"
 
-#: builtin/log.c:1140 builtin/log.c:1143
+#: builtin/log.c:1201 builtin/log.c:1204
 msgid "boundary"
 msgstr "gräns"
 
-#: builtin/log.c:1141
+#: builtin/log.c:1202
 msgid "attach the patch"
 msgstr "bifoga patchen"
 
-#: builtin/log.c:1144
+#: builtin/log.c:1205
 msgid "inline the patch"
 msgstr "gör patchen ett inline-objekt"
 
-#: builtin/log.c:1148
+#: builtin/log.c:1209
 msgid "enable message threading, styles: shallow, deep"
 msgstr "aktivera brevtrådning, typer: shallow, deep"
 
-#: builtin/log.c:1150
+#: builtin/log.c:1211
 msgid "signature"
 msgstr "signatur"
 
-#: builtin/log.c:1151
+#: builtin/log.c:1212
 msgid "add a signature"
 msgstr "lägg till signatur"
 
-#: builtin/log.c:1153
+#: builtin/log.c:1214
 msgid "don't print the patch filenames"
 msgstr "visa inte filnamn för patchar"
 
-#: builtin/log.c:1202
+#: builtin/log.c:1288
 #, c-format
-msgid "bogus committer info %s"
-msgstr "felaktig incheckarinformation %s"
+msgid "invalid ident line: %s"
+msgstr "ogiltig ident-rad: %s"
 
-#: builtin/log.c:1247
+#: builtin/log.c:1303
 msgid "-n and -k are mutually exclusive."
 msgstr "-n och -k kan inte användas samtidigt."
 
-#: builtin/log.c:1249
+#: builtin/log.c:1305
 msgid "--subject-prefix and -k are mutually exclusive."
 msgstr "--subject-prefix och -k kan inte användas samtidigt."
 
-#: builtin/log.c:1257
+#: builtin/log.c:1313
 msgid "--name-only does not make sense"
 msgstr "kan inte använda --name-only"
 
-#: builtin/log.c:1259
+#: builtin/log.c:1315
 msgid "--name-status does not make sense"
 msgstr "kan inte använda --name-status"
 
-#: builtin/log.c:1261
+#: builtin/log.c:1317
 msgid "--check does not make sense"
 msgstr "kan inte använda --check"
 
-#: builtin/log.c:1284
+#: builtin/log.c:1340
 msgid "standard output, or directory, which one?"
 msgstr "standard ut, eller katalog, vilken skall det vara?"
 
-#: builtin/log.c:1286
+#: builtin/log.c:1342
 #, c-format
 msgid "Could not create directory '%s'"
 msgstr "Kunde inte skapa katalogen \"%s\""
 
-#: builtin/log.c:1439
+#: builtin/log.c:1490
 msgid "Failed to create output files"
 msgstr "Misslyckades skapa utdatafiler"
 
-#: builtin/log.c:1488
+#: builtin/log.c:1539
 msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
 msgstr "git cherry [-v] [<uppström> [<huvud> [<gräns>]]]"
 
-#: builtin/log.c:1543
+#: builtin/log.c:1594
 #, c-format
 msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "Kunde inte hitta en spårad fjärrgren, ange <uppström> manuellt.\n"
 
-#: builtin/log.c:1556 builtin/log.c:1558 builtin/log.c:1570
+#: builtin/log.c:1607 builtin/log.c:1609 builtin/log.c:1621
 #, c-format
 msgid "Unknown commit %s"
 msgstr "Okänd incheckning %s"
 
-#: builtin/ls-files.c:409
+#: builtin/ls-files.c:408
 msgid "git ls-files [options] [<file>...]"
 msgstr "git ls-files [flaggor] [<fil>...]"
 
-#: builtin/ls-files.c:466
+#: builtin/ls-files.c:465
 msgid "identify the file status with tags"
 msgstr "identifiera filstatus med taggar"
 
-#: builtin/ls-files.c:468
+#: builtin/ls-files.c:467
 msgid "use lowercase letters for 'assume unchanged' files"
 msgstr "använd små bokstäver för \"anta oförändrade\"-filer"
 
-#: builtin/ls-files.c:470
+#: builtin/ls-files.c:469
 msgid "show cached files in the output (default)"
 msgstr "visa cachade filer i utdata (standard)"
 
-#: builtin/ls-files.c:472
+#: builtin/ls-files.c:471
 msgid "show deleted files in the output"
 msgstr "visa borttagna filer i utdata"
 
-#: builtin/ls-files.c:474
+#: builtin/ls-files.c:473
 msgid "show modified files in the output"
 msgstr "visa modifierade filer i utdata"
 
-#: builtin/ls-files.c:476
+#: builtin/ls-files.c:475
 msgid "show other files in the output"
 msgstr "visa andra filer i utdata"
 
-#: builtin/ls-files.c:478
+#: builtin/ls-files.c:477
 msgid "show ignored files in the output"
 msgstr "visa ignorerade filer i utdata"
 
-#: builtin/ls-files.c:481
+#: builtin/ls-files.c:480
 msgid "show staged contents' object name in the output"
 msgstr "visa köat innehålls objektnamn i utdata"
 
-#: builtin/ls-files.c:483
+#: builtin/ls-files.c:482
 msgid "show files on the filesystem that need to be removed"
 msgstr "visa filer i filsystemet som behöver tas bort"
 
-#: builtin/ls-files.c:485
+#: builtin/ls-files.c:484
 msgid "show 'other' directories' name only"
 msgstr "visa endast namn för \"andra\" kataloger"
 
-#: builtin/ls-files.c:488
+#: builtin/ls-files.c:487
 msgid "don't show empty directories"
 msgstr "visa inte tomma kataloger"
 
-#: builtin/ls-files.c:491
+#: builtin/ls-files.c:490
 msgid "show unmerged files in the output"
 msgstr "visa ej sammanslagna filer i utdata"
 
-#: builtin/ls-files.c:493
+#: builtin/ls-files.c:492
 msgid "show resolve-undo information"
 msgstr "visa \"resolve-undo\"-information"
 
-#: builtin/ls-files.c:495
+#: builtin/ls-files.c:494
 msgid "skip files matching pattern"
 msgstr "hoppa över filer som motsvarar mönster"
 
-#: builtin/ls-files.c:498
+#: builtin/ls-files.c:497
 msgid "exclude patterns are read from <file>"
 msgstr "exkludera mönster som läses från <fil>"
 
-#: builtin/ls-files.c:501
+#: builtin/ls-files.c:500
 msgid "read additional per-directory exclude patterns in <file>"
 msgstr "läs ytterligare per-katalog-exkluderingsmönster från <fil>"
 
-#: builtin/ls-files.c:503
+#: builtin/ls-files.c:502
 msgid "add the standard git exclusions"
 msgstr "lägg till git:s standardexkluderingar"
 
-#: builtin/ls-files.c:506
+#: builtin/ls-files.c:505
 msgid "make the output relative to the project top directory"
 msgstr "gör utdata relativ till projektets toppkatalog"
 
-#: builtin/ls-files.c:509
+#: builtin/ls-files.c:508
 msgid "if any <file> is not in the index, treat this as an error"
 msgstr "om en <fil> inte är indexet, betrakta det som ett fel"
 
-#: builtin/ls-files.c:510
+#: builtin/ls-files.c:509
 msgid "tree-ish"
 msgstr "träd-igt"
 
-#: builtin/ls-files.c:511
+#: builtin/ls-files.c:510
 msgid "pretend that paths removed since <tree-ish> are still present"
 msgstr "låtsas att sökvägar borttagna sedan <träd-igt> fortfarande finns"
 
-#: builtin/ls-files.c:513
+#: builtin/ls-files.c:512
 msgid "show debugging data"
 msgstr "visa felsökningsutdata"
 
@@ -5838,166 +6393,170 @@ msgstr "git merge [flaggor] <meddelande> HEAD <incheckning>"
 msgid "git merge --abort"
 msgstr "git merge --abort"
 
-#: builtin/merge.c:90
+#: builtin/merge.c:98
 msgid "switch `m' requires a value"
 msgstr "flaggan \"m\" behöver ett värde"
 
-#: builtin/merge.c:127
+#: builtin/merge.c:135
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
 msgstr "Kunde inte hitta sammanslagningsstrategin \"%s\".\n"
 
-#: builtin/merge.c:128
+#: builtin/merge.c:136
 #, c-format
 msgid "Available strategies are:"
 msgstr "Tillgängliga strategier är:"
 
-#: builtin/merge.c:133
+#: builtin/merge.c:141
 #, c-format
 msgid "Available custom strategies are:"
 msgstr "Tillgängliga skräddarsydda strategier är:"
 
-#: builtin/merge.c:183
+#: builtin/merge.c:198
 msgid "do not show a diffstat at the end of the merge"
 msgstr "visa inte en diffstat när sammanslagningen är färdig"
 
-#: builtin/merge.c:186
+#: builtin/merge.c:201
 msgid "show a diffstat at the end of the merge"
 msgstr "visa en diffstat när sammanslagningen är färdig"
 
-#: builtin/merge.c:187
+#: builtin/merge.c:202
 msgid "(synonym to --stat)"
 msgstr "(synonym till --stat)"
 
-#: builtin/merge.c:189
+#: builtin/merge.c:204
 msgid "add (at most <n>) entries from shortlog to merge commit message"
 msgstr ""
 "lägg till (som mest <n>) poster från shortlog till incheckningsmeddelandet"
 
-#: builtin/merge.c:192
+#: builtin/merge.c:207
 msgid "create a single commit instead of doing a merge"
 msgstr "skapa en ensam incheckning istället för en sammanslagning"
 
-#: builtin/merge.c:194
+#: builtin/merge.c:209
 msgid "perform a commit if the merge succeeds (default)"
 msgstr "utför en incheckning om sammanslagningen lyckades (standard)"
 
-#: builtin/merge.c:196
+#: builtin/merge.c:211
 msgid "edit message before committing"
 msgstr "redigera meddelande innan incheckning"
 
-#: builtin/merge.c:198
+#: builtin/merge.c:212
 msgid "allow fast-forward (default)"
 msgstr "tillåt snabbspolning (standard)"
 
-#: builtin/merge.c:200
+#: builtin/merge.c:214
 msgid "abort if fast-forward is not possible"
 msgstr "avbryt om snabbspolning inte är möjlig"
 
-#: builtin/merge.c:202 builtin/notes.c:866 builtin/revert.c:112
+#: builtin/merge.c:218
+msgid "Verify that the named commit has a valid GPG signature"
+msgstr "Bekräfta att den namngivna incheckningen har en giltig GPG-signatur"
+
+#: builtin/merge.c:219 builtin/notes.c:738 builtin/revert.c:114
 msgid "strategy"
 msgstr "strategi"
 
-#: builtin/merge.c:203
+#: builtin/merge.c:220
 msgid "merge strategy to use"
 msgstr "sammanslagninsstrategi att använda"
 
-#: builtin/merge.c:204
+#: builtin/merge.c:221
 msgid "option=value"
 msgstr "alternativ=värde"
 
-#: builtin/merge.c:205
+#: builtin/merge.c:222
 msgid "option for selected merge strategy"
 msgstr "alternativ för vald sammanslagningsstrategi"
 
-#: builtin/merge.c:207
+#: builtin/merge.c:224
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "incheckningsmeddelande för (icke snabbspolande) sammanslagning"
 
-#: builtin/merge.c:211
+#: builtin/merge.c:228
 msgid "abort the current in-progress merge"
 msgstr "avbryt den pågående sammanslagningen"
 
-#: builtin/merge.c:240
+#: builtin/merge.c:257
 msgid "could not run stash."
 msgstr "kunde köra stash."
 
-#: builtin/merge.c:245
+#: builtin/merge.c:262
 msgid "stash failed"
 msgstr "stash misslyckades"
 
-#: builtin/merge.c:250
+#: builtin/merge.c:267
 #, c-format
 msgid "not a valid object: %s"
 msgstr "inte ett giltigt objekt: %s"
 
-#: builtin/merge.c:269 builtin/merge.c:286
+#: builtin/merge.c:286 builtin/merge.c:303
 msgid "read-tree failed"
 msgstr "read-tree misslyckades"
 
-#: builtin/merge.c:316
+#: builtin/merge.c:333
 msgid " (nothing to squash)"
 msgstr " (inget att platta till)"
 
-#: builtin/merge.c:329
+#: builtin/merge.c:346
 #, c-format
 msgid "Squash commit -- not updating HEAD\n"
 msgstr "Tillplattningsincheckning -- uppdaterar inte HEAD\n"
 
-#: builtin/merge.c:361
+#: builtin/merge.c:378
 msgid "Writing SQUASH_MSG"
 msgstr "Skriver SQUASH_MSG"
 
-#: builtin/merge.c:363
+#: builtin/merge.c:380
 msgid "Finishing SQUASH_MSG"
 msgstr "Avslutar SQUASH_MSG"
 
-#: builtin/merge.c:386
+#: builtin/merge.c:403
 #, c-format
 msgid "No merge message -- not updating HEAD\n"
 msgstr "Inget sammanslagningsmeddelande -- uppdaterar inte HEAD\n"
 
-#: builtin/merge.c:436
+#: builtin/merge.c:453
 #, c-format
 msgid "'%s' does not point to a commit"
 msgstr "\"%s\" verkar inte peka på en incheckning"
 
-#: builtin/merge.c:535
+#: builtin/merge.c:565
 #, c-format
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr "Felaktig branch.%s.mergeoptions-sträng: %s"
 
-#: builtin/merge.c:628
+#: builtin/merge.c:657
 msgid "git write-tree failed to write a tree"
 msgstr "git write-tree misslyckades skriva ett träd"
 
-#: builtin/merge.c:656
+#: builtin/merge.c:685
 msgid "Not handling anything other than two heads merge."
 msgstr "Hanterar inte något annat än en sammanslagning av två huvuden."
 
-#: builtin/merge.c:670
+#: builtin/merge.c:699
 #, c-format
 msgid "Unknown option for merge-recursive: -X%s"
 msgstr "Felaktig flagga för merge-recursive: -X%s"
 
-#: builtin/merge.c:684
+#: builtin/merge.c:713
 #, c-format
 msgid "unable to write %s"
 msgstr "kunde inte skriva %s"
 
-#: builtin/merge.c:773
+#: builtin/merge.c:802
 #, c-format
 msgid "Could not read from '%s'"
 msgstr "Kunde inte läsa från \"%s\""
 
-#: builtin/merge.c:782
+#: builtin/merge.c:811
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr ""
 "Checkar inte in sammanslagningen; använd \"git commit\" för att slutföra "
 "den.\n"
 
-#: builtin/merge.c:788
+#: builtin/merge.c:817
 #, c-format
 msgid ""
 "Please enter a commit message to explain why this merge is necessary,\n"
@@ -6013,53 +6572,48 @@ msgstr ""
 "Rader som inleds med \"%c\" kommer ignoreras, och ett tomt meddelande\n"
 "avbryter incheckningen.\n"
 
-#: builtin/merge.c:812
+#: builtin/merge.c:841
 msgid "Empty commit message."
 msgstr "Tomt incheckningsmeddelande."
 
-#: builtin/merge.c:824
+#: builtin/merge.c:853
 #, c-format
 msgid "Wonderful.\n"
 msgstr "Underbart.\n"
 
-#: builtin/merge.c:889
+#: builtin/merge.c:918
 #, c-format
 msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
 msgstr ""
 "Kunde inte slå ihop automatiskt; fixa konflikter och checka in resultatet.\n"
 
-#: builtin/merge.c:905
+#: builtin/merge.c:934
 #, c-format
 msgid "'%s' is not a commit"
 msgstr "\"%s\" är inte en incheckning"
 
-#: builtin/merge.c:946
+#: builtin/merge.c:975
 msgid "No current branch."
 msgstr "Inte på någon gren."
 
-#: builtin/merge.c:948
+#: builtin/merge.c:977
 msgid "No remote for the current branch."
 msgstr "Ingen fjärr för aktuell gren."
 
-#: builtin/merge.c:950
+#: builtin/merge.c:979
 msgid "No default upstream defined for the current branch."
 msgstr "Ingen standarduppström angiven för aktuell gren."
 
-#: builtin/merge.c:955
+#: builtin/merge.c:984
 #, c-format
-msgid "No remote tracking branch for %s from %s"
+msgid "No remote-tracking branch for %s from %s"
 msgstr "Ingen fjärrspårande gren för %s från %s"
 
-#: builtin/merge.c:1042 builtin/merge.c:1199
-#, c-format
-msgid "%s - not something we can merge"
-msgstr "%s - inte något vi kan slå ihop"
-
-#: builtin/merge.c:1110
+#: builtin/merge.c:1140
 msgid "There is no merge to abort (MERGE_HEAD missing)."
 msgstr "Det finns ingen sammanslagning att avbryta (MERGE_HEAD saknas)."
 
-#: builtin/merge.c:1126 git-pull.sh:31
+#: builtin/merge.c:1156 git-pull.sh:31
 msgid ""
 "You have not concluded your merge (MERGE_HEAD exists).\n"
 "Please, commit your changes before you can merge."
@@ -6067,11 +6621,11 @@ msgstr ""
 "Du har inte avslutat sammanslagningen (MERGE_HEAD finns).\n"
 "Checka in dina ändringar innan du kan slå ihop."
 
-#: builtin/merge.c:1129 git-pull.sh:34
+#: builtin/merge.c:1159 git-pull.sh:34
 msgid "You have not concluded your merge (MERGE_HEAD exists)."
 msgstr "Du har inte avslutat sammanslagningen (MERGE_HEAD finns)."
 
-#: builtin/merge.c:1133
+#: builtin/merge.c:1163
 msgid ""
 "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 "Please, commit your changes before you can merge."
@@ -6079,79 +6633,103 @@ msgstr ""
 "Du har inte avslutat din \"cherry-pick\" (CHERRY_PICK_HEAD finns).\n"
 "Checka in dina ändringar innan du kan slå ihop."
 
-#: builtin/merge.c:1136
+#: builtin/merge.c:1166
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
 msgstr "Du har inte avslutat din \"cherry-pick\" (CHERRY_PICK_HEAD finns)."
 
-#: builtin/merge.c:1145
+#: builtin/merge.c:1175
 msgid "You cannot combine --squash with --no-ff."
 msgstr "Du kan inte kombinera --squash med --no-ff."
 
-#: builtin/merge.c:1150
-msgid "You cannot combine --no-ff with --ff-only."
-msgstr "Du kan inte kombinera --no-ff med --ff-only."
-
-#: builtin/merge.c:1157
+#: builtin/merge.c:1184
 msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr "Ingen incheckning angiven och merge.defaultToUpstream är ej satt."
 
-#: builtin/merge.c:1189
+#: builtin/merge.c:1216
 msgid "Can merge only exactly one commit into empty head"
 msgstr "Kan endast slå ihop en enda incheckning i ett tomt huvud."
 
-#: builtin/merge.c:1192
+#: builtin/merge.c:1219
 msgid "Squash commit into empty head not supported yet"
 msgstr "Stöder inte en tillplattningsincheckning på ett tomt huvud ännu"
 
-#: builtin/merge.c:1194
+#: builtin/merge.c:1221
 msgid "Non-fast-forward commit does not make sense into an empty head"
 msgstr "Icke-snabbspolad incheckning kan inte användas med ett tomt huvud"
 
-#: builtin/merge.c:1310
+#: builtin/merge.c:1226
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - inte något vi kan slå ihop"
+
+#: builtin/merge.c:1277
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr ""
+"Incheckningen %s har en obetrodd GPG-signatur som påstås vara gjord av %s."
+
+#: builtin/merge.c:1280
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr ""
+"Incheckningen %s har en felaktig GPG-signatur som påstås vara gjord av %s."
+
+#. 'N'
+#: builtin/merge.c:1283
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr "Incheckning %s har inte någon GPG-signatur."
+
+#: builtin/merge.c:1286
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr "Incheckningen %s har en korrekt GPG-signatur av %s\n"
+
+#: builtin/merge.c:1370
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr "Uppdaterar %s..%s\n"
 
-#: builtin/merge.c:1349
+#: builtin/merge.c:1409
 #, c-format
 msgid "Trying really trivial in-index merge...\n"
 msgstr "Försöker riktigt enkel sammanslagning i indexet...\n"
 
-#: builtin/merge.c:1356
+#: builtin/merge.c:1416
 #, c-format
 msgid "Nope.\n"
 msgstr "Nej.\n"
 
-#: builtin/merge.c:1388
+#: builtin/merge.c:1448
 msgid "Not possible to fast-forward, aborting."
 msgstr "Kan inte snabbspola, avbryter."
 
-#: builtin/merge.c:1411 builtin/merge.c:1490
+#: builtin/merge.c:1471 builtin/merge.c:1550
 #, c-format
 msgid "Rewinding the tree to pristine...\n"
 msgstr "Återspolar trädet till orört...\n"
 
-#: builtin/merge.c:1415
+#: builtin/merge.c:1475
 #, c-format
 msgid "Trying merge strategy %s...\n"
 msgstr "Försöker sammanslagninsstrategin %s...\n"
 
-#: builtin/merge.c:1481
+#: builtin/merge.c:1541
 #, c-format
 msgid "No merge strategy handled the merge.\n"
 msgstr "Ingen sammanslagningsstrategi hanterade sammanslagningen.\n"
 
-#: builtin/merge.c:1483
+#: builtin/merge.c:1543
 #, c-format
 msgid "Merge with strategy %s failed.\n"
 msgstr "Sammanslagning med strategin %s misslyckades.\n"
 
-#: builtin/merge.c:1492
+#: builtin/merge.c:1552
 #, c-format
 msgid "Using the %s to prepare resolving by hand.\n"
 msgstr "Använder %s för att förbereda lösning för hand.\n"
 
-#: builtin/merge.c:1504
+#: builtin/merge.c:1564
 #, c-format
 msgid "Automatic merge went well; stopped before committing as requested\n"
 msgstr ""
@@ -6318,47 +6896,51 @@ msgstr "Byter namn på %s till %s\n"
 msgid "renaming '%s' failed"
 msgstr "misslyckades byta namn på \"%s\""
 
-#: builtin/name-rev.c:175
+#: builtin/name-rev.c:259
 msgid "git name-rev [options] <commit>..."
 msgstr "git name-rev [flaggor] <incheckning>..."
 
-#: builtin/name-rev.c:176
+#: builtin/name-rev.c:260
 msgid "git name-rev [options] --all"
 msgstr "git name-rev [flaggor] --all"
 
-#: builtin/name-rev.c:177
+#: builtin/name-rev.c:261
 msgid "git name-rev [options] --stdin"
 msgstr "git name-rev [flaggor] --stdin"
 
-#: builtin/name-rev.c:229
+#: builtin/name-rev.c:313
 msgid "print only names (no SHA-1)"
 msgstr "skriv endast namn (ingen SHA-1)"
 
-#: builtin/name-rev.c:230
+#: builtin/name-rev.c:314
 msgid "only use tags to name the commits"
 msgstr "använd endast taggar för att namnge incheckningar"
 
-#: builtin/name-rev.c:232
+#: builtin/name-rev.c:316
 msgid "only use refs matching <pattern>"
 msgstr "använd endast referenser som motsvarar <mönster>"
 
-#: builtin/name-rev.c:234
+#: builtin/name-rev.c:318
 msgid "list all commits reachable from all refs"
 msgstr "lista alla incheckningar som kan nås alla referenser"
 
-#: builtin/name-rev.c:235
+#: builtin/name-rev.c:319
 msgid "read from stdin"
 msgstr "läs från standard in"
 
-#: builtin/name-rev.c:236
+#: builtin/name-rev.c:320
 msgid "allow to print `undefined` names"
 msgstr "tillåt att skriva \"odefinierade\" namn"
 
-#: builtin/notes.c:26
+#: builtin/name-rev.c:326
+msgid "dereference tags in the input (internal use)"
+msgstr "avreferera taggar i indata (används internt)"
+
+#: builtin/notes.c:24
 msgid "git notes [--ref <notes_ref>] [list [<object>]]"
 msgstr "git notes [--ref <anteckningsref>] [list [<objekt>]]"
 
-#: builtin/notes.c:27
+#: builtin/notes.c:25
 msgid ""
 "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
@@ -6366,12 +6948,12 @@ msgstr ""
 "git notes [--ref <anteckningsref>] add [-f] [-m <medd> | -F <fil> | (-c | -"
 "C) <objekt>] [<objekt>]"
 
-#: builtin/notes.c:28
+#: builtin/notes.c:26
 msgid "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"
 msgstr ""
 "git notes [--ref <anteckningsref>] copy [-f] <från-objekt> <till-objekt>"
 
-#: builtin/notes.c:29
+#: builtin/notes.c:27
 msgid ""
 "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
@@ -6379,221 +6961,200 @@ msgstr ""
 "git notes [--ref <anteckningsref>] append [-m <medd> | -F <fil> | (-c | -C) "
 "<objekt>] [<objekt>]"
 
-#: builtin/notes.c:30
+#: builtin/notes.c:28
 msgid "git notes [--ref <notes_ref>] edit [<object>]"
 msgstr "git notes [--ref <anteckningsref>] edit [<objekt>]"
 
-#: builtin/notes.c:31
+#: builtin/notes.c:29
 msgid "git notes [--ref <notes_ref>] show [<object>]"
 msgstr "git notes [--ref <anteckningsref>] show [<objekt>]"
 
-#: builtin/notes.c:32
+#: builtin/notes.c:30
 msgid ""
 "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"
 msgstr ""
 "git notes [--ref <anteckningsref>] merge [-v | -q] [-s <strategi> ] "
 "<anteckningsref>"
 
-#: builtin/notes.c:33
+#: builtin/notes.c:31
 msgid "git notes merge --commit [-v | -q]"
 msgstr "git notes merge --commit [-v | -q]"
 
-#: builtin/notes.c:34
+#: builtin/notes.c:32
 msgid "git notes merge --abort [-v | -q]"
 msgstr "git notes merge --abort [-v | -q]"
 
-#: builtin/notes.c:35
+#: builtin/notes.c:33
 msgid "git notes [--ref <notes_ref>] remove [<object>...]"
 msgstr "git notes [--ref <anteckningsref>] remove [<objekt>...]"
 
-#: builtin/notes.c:36
+#: builtin/notes.c:34
 msgid "git notes [--ref <notes_ref>] prune [-n | -v]"
 msgstr "git notes [--ref <anteckningsref>] prune [-n | -v]"
 
-#: builtin/notes.c:37
+#: builtin/notes.c:35
 msgid "git notes [--ref <notes_ref>] get-ref"
 msgstr "git notes [--ref <anteckningsref>] get-ref"
 
-#: builtin/notes.c:42
+#: builtin/notes.c:40
 msgid "git notes [list [<object>]]"
 msgstr "git notes [list [<objekt>]]"
 
-#: builtin/notes.c:47
+#: builtin/notes.c:45
 msgid "git notes add [<options>] [<object>]"
 msgstr "git notes add [<flaggor>] [<objekt>]"
 
-#: builtin/notes.c:52
+#: builtin/notes.c:50
 msgid "git notes copy [<options>] <from-object> <to-object>"
 msgstr "git notes copy [<flaggor>] <från-objekt> <till-objekt>"
 
-#: builtin/notes.c:53
+#: builtin/notes.c:51
 msgid "git notes copy --stdin [<from-object> <to-object>]..."
 msgstr "git notes copy --stdin [<från-objekt> <till-objekt>]..."
 
-#: builtin/notes.c:58
+#: builtin/notes.c:56
 msgid "git notes append [<options>] [<object>]"
 msgstr "git notes append [<flaggor>] [<objekt>]"
 
-#: builtin/notes.c:63
+#: builtin/notes.c:61
 msgid "git notes edit [<object>]"
 msgstr "git notes edit [<objekt>]"
 
-#: builtin/notes.c:68
+#: builtin/notes.c:66
 msgid "git notes show [<object>]"
 msgstr "git notes show [<objekt>]"
 
-#: builtin/notes.c:73
+#: builtin/notes.c:71
 msgid "git notes merge [<options>] <notes_ref>"
 msgstr "git notes merge [<flaggor>] <anteckningsref>"
 
-#: builtin/notes.c:74
+#: builtin/notes.c:72
 msgid "git notes merge --commit [<options>]"
 msgstr "git notes merge --commit [<flaggor>]"
 
-#: builtin/notes.c:75
+#: builtin/notes.c:73
 msgid "git notes merge --abort [<options>]"
 msgstr "git notes merge --abort [<flaggor>]"
 
-#: builtin/notes.c:80
+#: builtin/notes.c:78
 msgid "git notes remove [<object>]"
 msgstr "git notes remove [<objekt>]"
 
-#: builtin/notes.c:85
+#: builtin/notes.c:83
 msgid "git notes prune [<options>]"
 msgstr "git notes prune [<flaggor>]"
 
-#: builtin/notes.c:90
+#: builtin/notes.c:88
 msgid "git notes get-ref"
 msgstr "git notes get-ref"
 
-#: builtin/notes.c:139
+#: builtin/notes.c:137
 #, c-format
 msgid "unable to start 'show' for object '%s'"
 msgstr "kunde inte starta \"show\" för objektet \"%s\""
 
-#: builtin/notes.c:143
+#: builtin/notes.c:141
 msgid "could not read 'show' output"
 msgstr "kunde inte läsa utdata från \"show\""
 
-#: builtin/notes.c:151
+#: builtin/notes.c:149
 #, c-format
 msgid "failed to finish 'show' for object '%s'"
 msgstr "kunde inte avsluta \"show\" för objektet \"%s\""
 
-#: builtin/notes.c:169 builtin/tag.c:341
+#: builtin/notes.c:167 builtin/tag.c:341
 #, c-format
 msgid "could not create file '%s'"
 msgstr "kunde inte skapa filen \"%s\""
 
-#: builtin/notes.c:188
+#: builtin/notes.c:186
 msgid "Please supply the note contents using either -m or -F option"
 msgstr "Ange innehåll för anteckningen med antingen -m eller -F"
 
-#: builtin/notes.c:209 builtin/notes.c:972
+#: builtin/notes.c:207 builtin/notes.c:844
 #, c-format
 msgid "Removing note for object %s\n"
 msgstr "Tar bort anteckning för objektet %s\n"
 
-#: builtin/notes.c:214
+#: builtin/notes.c:212
 msgid "unable to write note object"
 msgstr "kunde inte skriva anteckningsobjekt"
 
-#: builtin/notes.c:216
+#: builtin/notes.c:214
 #, c-format
 msgid "The note contents has been left in %s"
 msgstr "Anteckningens innehåll har lämnats kvar i %s"
 
-#: builtin/notes.c:250 builtin/tag.c:540
+#: builtin/notes.c:248 builtin/tag.c:540
 #, c-format
 msgid "cannot read '%s'"
 msgstr "kunde inte läsa \"%s\""
 
-#: builtin/notes.c:252 builtin/tag.c:543
+#: builtin/notes.c:250 builtin/tag.c:543
 #, c-format
 msgid "could not open or read '%s'"
 msgstr "kunde inte öppna eller läsa \"%s\""
 
-#: builtin/notes.c:271 builtin/notes.c:444 builtin/notes.c:446
-#: builtin/notes.c:506 builtin/notes.c:560 builtin/notes.c:643
-#: builtin/notes.c:648 builtin/notes.c:723 builtin/notes.c:765
-#: builtin/notes.c:967 builtin/tag.c:556
+#: builtin/notes.c:269 builtin/notes.c:316 builtin/notes.c:318
+#: builtin/notes.c:378 builtin/notes.c:432 builtin/notes.c:515
+#: builtin/notes.c:520 builtin/notes.c:595 builtin/notes.c:637
+#: builtin/notes.c:839 builtin/tag.c:556
 #, c-format
 msgid "Failed to resolve '%s' as a valid ref."
 msgstr "Kunde inte slå upp \"%s\" som en giltig referens."
 
-#: builtin/notes.c:274
+#: builtin/notes.c:272
 #, c-format
 msgid "Failed to read object '%s'."
 msgstr "Kunde inte läsa objektet \"%s\"."
 
-#: builtin/notes.c:298
-msgid "Cannot commit uninitialized/unreferenced notes tree"
-msgstr "Kan inte checka in oinitierat/orefererat anteckningsträd"
-
-#: builtin/notes.c:339
-#, c-format
-msgid "Bad notes.rewriteMode value: '%s'"
-msgstr "Felaktigt värde för notes.rewriteMode: '%s'"
-
-#: builtin/notes.c:349
-#, c-format
-msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
-msgstr "Vägrar skriva över anteckningar i %s (utanför refs/notes/)"
-
-#. TRANSLATORS: The first %s is the name of the
-#. environment variable, the second %s is its value
-#: builtin/notes.c:376
-#, c-format
-msgid "Bad %s value: '%s'"
-msgstr "Felaktigt värde på %s: \"%s\""
-
-#: builtin/notes.c:440
+#: builtin/notes.c:312
 #, c-format
 msgid "Malformed input line: '%s'."
 msgstr "Felaktig indatarad: \"%s\"."
 
-#: builtin/notes.c:455
+#: builtin/notes.c:327
 #, c-format
 msgid "Failed to copy notes from '%s' to '%s'"
 msgstr "Misslyckades kopiera anteckningar från \"%s\" till \"%s\""
 
-#: builtin/notes.c:499 builtin/notes.c:553 builtin/notes.c:626
-#: builtin/notes.c:638 builtin/notes.c:711 builtin/notes.c:758
-#: builtin/notes.c:1032
+#: builtin/notes.c:371 builtin/notes.c:425 builtin/notes.c:498
+#: builtin/notes.c:510 builtin/notes.c:583 builtin/notes.c:630
+#: builtin/notes.c:904
 msgid "too many parameters"
 msgstr "för många parametrar"
 
-#: builtin/notes.c:512 builtin/notes.c:771
+#: builtin/notes.c:384 builtin/notes.c:643
 #, c-format
 msgid "No note found for object %s."
 msgstr "Inga anteckningar hittades för objektet %s."
 
-#: builtin/notes.c:534 builtin/notes.c:691
+#: builtin/notes.c:406 builtin/notes.c:563
 msgid "note contents as a string"
 msgstr "anteckningsinnehåll som sträng"
 
-#: builtin/notes.c:537 builtin/notes.c:694
+#: builtin/notes.c:409 builtin/notes.c:566
 msgid "note contents in a file"
 msgstr "anteckningsinnehåll i en fil"
 
-#: builtin/notes.c:539 builtin/notes.c:542 builtin/notes.c:696
-#: builtin/notes.c:699 builtin/tag.c:474
+#: builtin/notes.c:411 builtin/notes.c:414 builtin/notes.c:568
+#: builtin/notes.c:571 builtin/tag.c:474
 msgid "object"
 msgstr "objekt"
 
-#: builtin/notes.c:540 builtin/notes.c:697
+#: builtin/notes.c:412 builtin/notes.c:569
 msgid "reuse and edit specified note object"
 msgstr "återanvänd och redigera angivet anteckningsobjekt"
 
-#: builtin/notes.c:543 builtin/notes.c:700
+#: builtin/notes.c:415 builtin/notes.c:572
 msgid "reuse specified note object"
 msgstr "återanvänd angivet anteckningsobjekt"
 
-#: builtin/notes.c:545 builtin/notes.c:613
+#: builtin/notes.c:417 builtin/notes.c:485
 msgid "replace existing notes"
 msgstr "ersätt befintliga anteckningar"
 
-#: builtin/notes.c:579
+#: builtin/notes.c:451
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -6602,24 +7163,24 @@ msgstr ""
 "Kan inte lägga till anteckningar. Hittade befintliga anteckningar för "
 "objektet %s. Använd \"-f\" för att skriva över befintliga anteckningar"
 
-#: builtin/notes.c:584 builtin/notes.c:661
+#: builtin/notes.c:456 builtin/notes.c:533
 #, c-format
 msgid "Overwriting existing notes for object %s\n"
 msgstr "Skriver över befintliga anteckningar för objektet %s\n"
 
-#: builtin/notes.c:614
+#: builtin/notes.c:486
 msgid "read objects from stdin"
 msgstr "läs objekt från standard in"
 
-#: builtin/notes.c:616
+#: builtin/notes.c:488
 msgid "load rewriting config for <command> (implies --stdin)"
 msgstr "läs omskrivningsinställning för <kommando> (implicerar --stdin)"
 
-#: builtin/notes.c:634
+#: builtin/notes.c:506
 msgid "too few parameters"
 msgstr "för få parametrar"
 
-#: builtin/notes.c:655
+#: builtin/notes.c:527
 #, c-format
 msgid ""
 "Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -6628,12 +7189,12 @@ msgstr ""
 "Kan inte kopiera anteckningar. Hittade befintliga anteckningar för objektet "
 "%s. Använd \"-f\" för att skriva över befintliga anteckningar"
 
-#: builtin/notes.c:667
+#: builtin/notes.c:539
 #, c-format
 msgid "Missing notes on source object %s. Cannot copy."
 msgstr "Anteckningar på källobjektet %s saknas. Kan inte kopiera."
 
-#: builtin/notes.c:716
+#: builtin/notes.c:588
 #, c-format
 msgid ""
 "The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
@@ -6642,15 +7203,15 @@ msgstr ""
 "Flaggorna -m/-F/-c/-C rekommenderas inte för underkommandot \"edit\".\n"
 "Använd \"git notes add -f -m/-F/-c/-C\" istället.\n"
 
-#: builtin/notes.c:863
+#: builtin/notes.c:735
 msgid "General options"
 msgstr "Allmänna flaggor"
 
-#: builtin/notes.c:865
+#: builtin/notes.c:737
 msgid "Merge options"
 msgstr "Flaggor för sammanslagning"
 
-#: builtin/notes.c:867
+#: builtin/notes.c:739
 msgid ""
 "resolve notes conflicts using the given strategy (manual/ours/theirs/union/"
 "cat_sort_uniq)"
@@ -6658,46 +7219,46 @@ msgstr ""
 "läs konflikter i anteckningar med angiven strategi (manual/ours/theirs/union/"
 "cat_sort_uniq)"
 
-#: builtin/notes.c:869
+#: builtin/notes.c:741
 msgid "Committing unmerged notes"
 msgstr "Checkar in ej sammanslagna anteckningar"
 
-#: builtin/notes.c:871
+#: builtin/notes.c:743
 msgid "finalize notes merge by committing unmerged notes"
 msgstr ""
 "färdigställ sammanslagning av anteckningar genom att checka in ej "
 "sammanslagna anteckningar"
 
-#: builtin/notes.c:873
+#: builtin/notes.c:745
 msgid "Aborting notes merge resolution"
 msgstr "Avbryt lösning av sammanslagning av anteckningar"
 
-#: builtin/notes.c:875
+#: builtin/notes.c:747
 msgid "abort notes merge"
 msgstr "avbryt sammanslagning av anteckningar"
 
-#: builtin/notes.c:970
+#: builtin/notes.c:842
 #, c-format
 msgid "Object %s has no note\n"
 msgstr "Objektet %s har ingen anteckning\n"
 
-#: builtin/notes.c:982
+#: builtin/notes.c:854
 msgid "attempt to remove non-existent note is not an error"
 msgstr "försök att ta bort icke-existerande anteckningar är inte ett fel"
 
-#: builtin/notes.c:985
+#: builtin/notes.c:857
 msgid "read object names from the standard input"
 msgstr "läs objektnamn från standard in"
 
-#: builtin/notes.c:1066
+#: builtin/notes.c:938
 msgid "notes_ref"
 msgstr "anteckningar-ref"
 
-#: builtin/notes.c:1067
+#: builtin/notes.c:939
 msgid "use notes from <notes_ref>"
 msgstr "använd anteckningar från <anteckningsref>"
 
-#: builtin/notes.c:1102 builtin/remote.c:1598
+#: builtin/notes.c:974 builtin/remote.c:1598
 #, c-format
 msgid "Unknown subcommand: %s"
 msgstr "Okänt underkommando: %s"
@@ -6933,7 +7494,7 @@ msgstr ""
 "    git push %s %s\n"
 "%s"
 
-#: builtin/push.c:121
+#: builtin/push.c:117
 #, c-format
 msgid ""
 "You are not currently on a branch.\n"
@@ -6948,7 +7509,7 @@ msgstr ""
 "\n"
 "    git push %s HEAD:<namn-på-fjärrgren>\n"
 
-#: builtin/push.c:128
+#: builtin/push.c:131
 #, c-format
 msgid ""
 "The current branch %s has no upstream branch.\n"
@@ -6961,12 +7522,12 @@ msgstr ""
 "\n"
 "    git push --set-upstream %s %s\n"
 
-#: builtin/push.c:136
+#: builtin/push.c:139
 #, c-format
 msgid "The current branch %s has multiple upstream branches, refusing to push."
 msgstr "Den aktuella grenen %s har flera uppströmsgrenar, vägrar sända."
 
-#: builtin/push.c:139
+#: builtin/push.c:142
 #, c-format
 msgid ""
 "You are pushing to remote '%s', which is not the upstream of\n"
@@ -6977,7 +7538,7 @@ msgstr ""
 "aktuella grenen \"%s\", utan att tala om för mig vad som\n"
 "skall sändas för att uppdatera fjärrgrenen."
 
-#: builtin/push.c:151
+#: builtin/push.c:165
 msgid ""
 "push.default is unset; its implicit value is changing in\n"
 "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -7011,26 +7572,26 @@ msgstr ""
 "liknande läget \"current\" istället för \"simple\" om du ibland använder\n"
 "äldre versioner av Git.)"
 
-#: builtin/push.c:199
+#: builtin/push.c:225
 msgid ""
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
 "Du angav inga referensspecifikationer att sända, och push.default är "
 "\"nothing\"."
 
-#: builtin/push.c:206
+#: builtin/push.c:232
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
-"before pushing again.\n"
+"its remote counterpart. Integrate the remote changes (e.g.\n"
+"'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då änden på din befintliga gren är bakom\n"
-"dess fjärrmotsvarighet. Slå ihop fjärrändringarna (t.ex. \"git pull\")\n"
-"innan du sänder igen.\n"
+"dess fjärrmotsvarighet. Integrera fjärrändringarna (t.ex\n"
+"\"git pull ....\") innan du sänder igen.\n"
 "Se avsnittet \"Note about fast-forward\" i \"git push --help\" för detaljer."
 
-#: builtin/push.c:212
+#: builtin/push.c:238
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
 "counterpart. If you did not intend to push that branch, you may want to\n"
@@ -7044,37 +7605,37 @@ msgstr ""
 "endast\n"
 "sända aktuell gren."
 
-#: builtin/push.c:218
+#: builtin/push.c:244
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and merge the remote changes\n"
-"(e.g. 'git pull') before pushing again.\n"
+"counterpart. Check out this branch and integrate the remote changes\n"
+"(e.g. 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då änden på en gren som sänds in är bakom dess\n"
-"fjärrmotsvarighet. Checka ut grenen och slå ihop fjärrändringarna (t.ex.\n"
-"\"git pull\") innan du sänder igen.\n"
+"fjärrmotsvarighet. Checka ut grenen och integrera fjärrändringarna (t.ex.\n"
+"\"git pull ...\") innan du sänder igen.\n"
 "Se avsnittet \"Note about fast-forward\" i \"git push --help\" för detaljer."
 
-#: builtin/push.c:224
+#: builtin/push.c:250
 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"
-"to the same ref. You may want to first merge the remote changes (e.g.,\n"
-"'git pull') before pushing again.\n"
+"to the same ref. You may want to first integrate the remote changes\n"
+"(e.g., 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Uppdateringar avvisades då fjärren innehåller ändringar som du inte\n"
 "har lokalt. Det beror oftast på att ett annat arkiv har sänt in samma\n"
-"referenser. Det kan vara en idé att först slå ihop fjärrändringarna\n"
-"(t.ex. \"git pull\") innan du sänder igen.\n"
+"referenser. Det kan vara en idé att först integrera fjärrändringarna\n"
+"(t.ex. \"git pull ...\") innan du sänder igen.\n"
 "Se avsnittet \"Note about fast-forwards\" i \"git push --help\" för detaljer."
 
-#: builtin/push.c:231
+#: builtin/push.c:257
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr "Uppdateringarna avvisades eftersom taggen redan finns på fjärren."
 
-#: builtin/push.c:234
+#: builtin/push.c:260
 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"
@@ -7085,22 +7646,22 @@ msgstr ""
 "pekar på något som inte är en incheckning, utan att använda flaggan\n"
 "\"--force\".\n"
 
-#: builtin/push.c:294
+#: builtin/push.c:320
 #, c-format
 msgid "Pushing to %s\n"
 msgstr "Sänder till %s\n"
 
-#: builtin/push.c:298
+#: builtin/push.c:324
 #, c-format
 msgid "failed to push some refs to '%s'"
 msgstr "misslyckades sända vissa referenser till \"%s\""
 
-#: builtin/push.c:331
+#: builtin/push.c:357
 #, c-format
 msgid "bad repository '%s'"
 msgstr "felaktigt arkiv \"%s\""
 
-#: builtin/push.c:332
+#: builtin/push.c:358
 msgid ""
 "No configured push destination.\n"
 "Either specify the URL from the command-line or configure a remote "
@@ -7121,83 +7682,87 @@ msgstr ""
 "\n"
 "    git push <namn>\n"
 
-#: builtin/push.c:347
+#: builtin/push.c:373
 msgid "--all and --tags are incompatible"
 msgstr "--all och --tags är inkompatibla"
 
-#: builtin/push.c:348
+#: builtin/push.c:374
 msgid "--all can't be combined with refspecs"
 msgstr "--all kan inte kombineras med referensspecifikationer"
 
-#: builtin/push.c:353
+#: builtin/push.c:379
 msgid "--mirror and --tags are incompatible"
 msgstr "--mirror och --tags är inkompatibla"
 
-#: builtin/push.c:354
+#: builtin/push.c:380
 msgid "--mirror can't be combined with refspecs"
 msgstr "--mirror kan inte kombineras med referensspecifikationer"
 
-#: builtin/push.c:359
+#: builtin/push.c:385
 msgid "--all and --mirror are incompatible"
 msgstr "--all och --mirror är inkompatibla"
 
-#: builtin/push.c:419
+#: builtin/push.c:445
 msgid "repository"
 msgstr "arkiv"
 
-#: builtin/push.c:420
+#: builtin/push.c:446
 msgid "push all refs"
 msgstr "sänd alla referenser"
 
-#: builtin/push.c:421
+#: builtin/push.c:447
 msgid "mirror all refs"
 msgstr "spegla alla referenser"
 
-#: builtin/push.c:423
+#: builtin/push.c:449
 msgid "delete refs"
 msgstr "ta bort referenser"
 
-#: builtin/push.c:424
+#: builtin/push.c:450
 msgid "push tags (can't be used with --all or --mirror)"
 msgstr "sänd taggar (kan inte användas med --all eller --mirror)"
 
-#: builtin/push.c:427
+#: builtin/push.c:453
 msgid "force updates"
 msgstr "tvinga uppdateringar"
 
-#: builtin/push.c:428
+#: builtin/push.c:454
 msgid "check"
 msgstr "kontrollera"
 
-#: builtin/push.c:429
+#: builtin/push.c:455
 msgid "control recursive pushing of submodules"
 msgstr "styr rekursiv insändning av undermoduler"
 
-#: builtin/push.c:431
+#: builtin/push.c:457
 msgid "use thin pack"
 msgstr "använd tunna paket"
 
-#: builtin/push.c:432 builtin/push.c:433
+#: builtin/push.c:458 builtin/push.c:459
 msgid "receive pack program"
 msgstr "program för att ta emot paket"
 
-#: builtin/push.c:434
+#: builtin/push.c:460
 msgid "set upstream for git pull/status"
 msgstr "ställ in uppström för git pull/status"
 
-#: builtin/push.c:437
+#: builtin/push.c:463
 msgid "prune locally removed refs"
 msgstr "ta bort lokalt borttagna referenser"
 
-#: builtin/push.c:439
+#: builtin/push.c:465
 msgid "bypass pre-push hook"
 msgstr "förbigå pre-push-krok"
 
-#: builtin/push.c:448
+#: builtin/push.c:466
+msgid "push missing but relevant tags"
+msgstr "sänd in saknade men relevanta taggar"
+
+#: builtin/push.c:476
 msgid "--delete is incompatible with --all, --mirror and --tags"
 msgstr "--delete är imkompatibel med --all, --mirror och --tags"
 
-#: builtin/push.c:450
+#: builtin/push.c:478
 msgid "--delete doesn't make sense without any refs"
 msgstr "--delete kan inte användas utan referenser"
 
@@ -7211,70 +7776,80 @@ msgstr ""
 "[-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
 "index-output=<fil>] (--empty | <träd-igt1> [<träd-igt2> [<träd-igt3>]])"
 
-#: builtin/read-tree.c:108
+#: builtin/read-tree.c:109
 msgid "write resulting index to <file>"
 msgstr "skriv resulterande index till <fil>"
 
-#: builtin/read-tree.c:111
+#: builtin/read-tree.c:112
 msgid "only empty the index"
 msgstr "töm bara indexet"
 
-#: builtin/read-tree.c:113
+#: builtin/read-tree.c:114
 msgid "Merging"
 msgstr "Sammanslagning"
 
-#: builtin/read-tree.c:115
+#: builtin/read-tree.c:116
 msgid "perform a merge in addition to a read"
 msgstr "utför en sammanslagning i tillägg till en läsning"
 
-#: builtin/read-tree.c:117
+#: builtin/read-tree.c:118
 msgid "3-way merge if no file level merging required"
 msgstr "3-vägssammanslagning om sammanslagning på filnivå ej krävs"
 
-#: builtin/read-tree.c:119
+#: builtin/read-tree.c:120
 msgid "3-way merge in presence of adds and removes"
 msgstr "3-vägssammanslagning när det finns tillägg och borttagningar"
 
-#: builtin/read-tree.c:121
+#: builtin/read-tree.c:122
 msgid "same as -m, but discard unmerged entries"
 msgstr "som -m, men kasta bort ej sammanslagna poster"
 
-#: builtin/read-tree.c:122
+#: builtin/read-tree.c:123
 msgid "<subdirectory>/"
 msgstr "<underkatalog>/"
 
-#: builtin/read-tree.c:123
+#: builtin/read-tree.c:124
 msgid "read the tree into the index under <subdirectory>/"
 msgstr "läs in trädet i indexet under <underkatalog>/"
 
-#: builtin/read-tree.c:126
+#: builtin/read-tree.c:127
 msgid "update working tree with merge result"
 msgstr "uppdatera arbetskatalogen med resultatet från sammanslagningen"
 
-#: builtin/read-tree.c:128
+#: builtin/read-tree.c:129
 msgid "gitignore"
 msgstr "gitignore"
 
-#: builtin/read-tree.c:129
+#: builtin/read-tree.c:130
 msgid "allow explicitly ignored files to be overwritten"
 msgstr "tillåt explicit ignorerade filer att skrivas över"
 
-#: builtin/read-tree.c:132
+#: builtin/read-tree.c:133
 msgid "don't check the working tree after merging"
 msgstr "kontrollera inte arbetskatalogen efter sammanslagning"
 
-#: builtin/read-tree.c:133
+#: builtin/read-tree.c:134
 msgid "don't update the index or the work tree"
 msgstr "uppdatera inte indexet eller arbetskatalogen"
 
-#: builtin/read-tree.c:135
+#: builtin/read-tree.c:136
 msgid "skip applying sparse checkout filter"
 msgstr "hoppa över att applicera filter för gles utcheckning"
 
-#: builtin/read-tree.c:137
+#: builtin/read-tree.c:138
 msgid "debug unpack-trees"
 msgstr "felsök unpack-trees"
 
+#: builtin/reflog.c:500
+#, c-format
+msgid "%s' for '%s' is not a valid timestamp"
+msgstr "\"%s\" för \"%s\" är inte en giltig tidsstämpel"
+
+#: builtin/reflog.c:616 builtin/reflog.c:621
+#, c-format
+msgid "'%s' is not a valid timestamp"
+msgstr "\"%s\" är inte en giltig tidsstämpel"
+
 #: builtin/remote.c:11
 msgid "git remote [-v | --verbose]"
 msgstr "git remote [-v | --verbose]"
@@ -7848,96 +8423,96 @@ msgstr "Kunde inte hitta träder för HEAD."
 msgid "Failed to find tree of %s."
 msgstr "Kunde inte hitta träder för %s."
 
-#: builtin/reset.c:98
+#: builtin/reset.c:99
 #, c-format
 msgid "HEAD is now at %s"
 msgstr "HEAD är nu på %s"
 
-#: builtin/reset.c:169
+#: builtin/reset.c:172
 #, c-format
 msgid "Cannot do a %s reset in the middle of a merge."
 msgstr "Kan inte utföra en %s återställning mitt i en sammanslagning."
 
-#: builtin/reset.c:248
+#: builtin/reset.c:251
 msgid "be quiet, only report errors"
 msgstr "var tyst, rapportera endast fel"
 
-#: builtin/reset.c:250
+#: builtin/reset.c:253
 msgid "reset HEAD and index"
 msgstr "återställ HEAD och index"
 
-#: builtin/reset.c:251
+#: builtin/reset.c:254
 msgid "reset only HEAD"
 msgstr "återställ endast HEAD"
 
-#: builtin/reset.c:253 builtin/reset.c:255
+#: builtin/reset.c:256 builtin/reset.c:258
 msgid "reset HEAD, index and working tree"
 msgstr "återställ HEAD, index och arbetskatalog"
 
-#: builtin/reset.c:257
+#: builtin/reset.c:260
 msgid "reset HEAD but keep local changes"
 msgstr "återställ HEAD men behåll lokala ändringar"
 
-#: builtin/reset.c:275
+#: builtin/reset.c:278
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
 msgstr "Kunde inte slå upp \"%s\" som en giltig revision."
 
-#: builtin/reset.c:278 builtin/reset.c:286
+#: builtin/reset.c:281 builtin/reset.c:289
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "Kan inte tolka objektet \"%s\""
 
-#: builtin/reset.c:283
+#: builtin/reset.c:286
 #, c-format
 msgid "Failed to resolve '%s' as a valid tree."
 msgstr "Kunde inte slå upp \"%s\" som ett giltigt träd."
 
-#: builtin/reset.c:292
+#: builtin/reset.c:295
 msgid "--patch is incompatible with --{hard,mixed,soft}"
 msgstr "--patch är inkompatibel med --{hard,mixed,soft}"
 
-#: builtin/reset.c:301
+#: builtin/reset.c:304
 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
 msgstr ""
 "--mixed rekommenderas inte med sökvägar; använd \"git reset -- <sökvägar>\"."
 
-#: builtin/reset.c:303
+#: builtin/reset.c:306
 #, c-format
 msgid "Cannot do %s reset with paths."
 msgstr "Kan inte göra %s återställning med sökvägar."
 
-#: builtin/reset.c:313
+#: builtin/reset.c:316
 #, c-format
 msgid "%s reset is not allowed in a bare repository"
 msgstr "%s återställning tillåts inte i ett naket arkiv"
 
-#: builtin/reset.c:333
+#: builtin/reset.c:336
 #, c-format
 msgid "Could not reset index file to revision '%s'."
 msgstr "Kunde inte återställa indexfilen till versionen \"%s\"."
 
-#: builtin/reset.c:339
+#: builtin/reset.c:342
 msgid "Unstaged changes after reset:"
 msgstr "Oköade ändringar efter återställning:"
 
-#: builtin/reset.c:344
+#: builtin/reset.c:347
 msgid "Could not write new index file."
 msgstr "Kunde inte skriva ny indexfil."
 
-#: builtin/rev-parse.c:339
+#: builtin/rev-parse.c:345
 msgid "git rev-parse --parseopt [options] -- [<args>...]"
 msgstr "git rev-parse --parseopt [options] -- [<argument>...]"
 
-#: builtin/rev-parse.c:344
+#: builtin/rev-parse.c:350
 msgid "keep the `--` passed as an arg"
 msgstr "behåll \"--\" sänt som argument"
 
-#: builtin/rev-parse.c:346
+#: builtin/rev-parse.c:352
 msgid "stop parsing after the first non-option argument"
 msgstr "sluta tolka efter första argument som inte är flagga"
 
-#: builtin/rev-parse.c:464
+#: builtin/rev-parse.c:470
 msgid ""
 "git rev-parse --parseopt [options] -- [<args>...]\n"
 "   or: git rev-parse --sq-quote [<arg>...]\n"
@@ -7953,192 +8528,216 @@ msgstr ""
 "varianten."
 
 #: builtin/revert.c:22
-msgid "git revert [options] <commit-ish>"
-msgstr "git revert [flaggor] <incheckning-igt>"
+msgid "git revert [options] <commit-ish>..."
+msgstr "git revert [flaggor] <incheckning-igt>..."
 
 #: builtin/revert.c:23
 msgid "git revert <subcommand>"
 msgstr "git revert <underkommando>"
 
 #: builtin/revert.c:28
-msgid "git cherry-pick [options] <commit-ish>"
-msgstr "git cherry-pick [flaggor] <incheckning-igt>"
+msgid "git cherry-pick [options] <commit-ish>..."
+msgstr "git cherry-pick [flaggor] <incheckning-igt>..."
 
 #: builtin/revert.c:29
 msgid "git cherry-pick <subcommand>"
 msgstr "git cherry-pick <underkommando>"
 
-#: builtin/revert.c:70 builtin/revert.c:92
+#: builtin/revert.c:71 builtin/revert.c:94
 #, c-format
 msgid "%s: %s cannot be used with %s"
 msgstr "%s: %s kan inte användas med %s"
 
-#: builtin/revert.c:103
+#: builtin/revert.c:105
 msgid "end revert or cherry-pick sequence"
 msgstr "avsluta revert- eller cherry-pick-sekvens"
 
-#: builtin/revert.c:104
+#: builtin/revert.c:106
 msgid "resume revert or cherry-pick sequence"
 msgstr "återuppta revert- eller cherry-pick-sekvens"
 
-#: builtin/revert.c:105
+#: builtin/revert.c:107
 msgid "cancel revert or cherry-pick sequence"
 msgstr "avbryt revert- eller cherry-pick-sekvens"
 
-#: builtin/revert.c:106
+#: builtin/revert.c:108
 msgid "don't automatically commit"
 msgstr "checka inte in automatiskt"
 
-#: builtin/revert.c:107
+#: builtin/revert.c:109
 msgid "edit the commit message"
 msgstr "redigera incheckningsmeddelandet"
 
-#: builtin/revert.c:110
+#: builtin/revert.c:112
 msgid "parent number"
 msgstr "nummer på förälder"
 
-#: builtin/revert.c:112
+#: builtin/revert.c:114
 msgid "merge strategy"
 msgstr "sammanslagninsstrategi"
 
-#: builtin/revert.c:113
+#: builtin/revert.c:115
 msgid "option"
 msgstr "alternativ"
 
-#: builtin/revert.c:114
+#: builtin/revert.c:116
 msgid "option for merge strategy"
 msgstr "alternativ för sammanslagningsstrategi"
 
-#: builtin/revert.c:125
+#: builtin/revert.c:127
 msgid "append commit name"
 msgstr "lägg till incheckningsnamn"
 
-#: builtin/revert.c:126
+#: builtin/revert.c:128
 msgid "allow fast-forward"
 msgstr "tillåt snabbspolning"
 
-#: builtin/revert.c:127
+#: builtin/revert.c:129
 msgid "preserve initially empty commits"
 msgstr "behåll incheckningar som börjar som tomma"
 
-#: builtin/revert.c:128
+#: builtin/revert.c:130
 msgid "allow commits with empty messages"
 msgstr "tillåt incheckningar med tomt meddelande"
 
-#: builtin/revert.c:129
+#: builtin/revert.c:131
 msgid "keep redundant, empty commits"
 msgstr "behåll redundanta, tomma incheckningar"
 
-#: builtin/revert.c:133
+#: builtin/revert.c:135
 msgid "program error"
 msgstr "programfel"
 
-#: builtin/revert.c:223
+#: builtin/revert.c:225
 msgid "revert failed"
 msgstr "\"revert\" misslyckades"
 
-#: builtin/revert.c:238
+#: builtin/revert.c:240
 msgid "cherry-pick failed"
 msgstr "\"cherry-pick\" misslyckades"
 
-#: builtin/rm.c:15
+#: builtin/rm.c:16
 msgid "git rm [options] [--] <file>..."
 msgstr "git rm [flaggor] [--] <fil>..."
 
-#: builtin/rm.c:64 builtin/rm.c:186
-#, c-format
+#: builtin/rm.c:64
+msgid ""
+"the following submodule (or one of its nested submodules)\n"
+"uses a .git directory:"
+msgid_plural ""
+"the following submodules (or one of its nested submodules)\n"
+"use a .git directory:"
+msgstr[0] ""
+"följande undermodul (eller en av dess nästlade undermoduler)\n"
+"använder en .git-katalog:"
+msgstr[1] ""
+"följande undermoduler (eller en av deras nästlade undermoduler)\n"
+"använder en .git-katalog:"
+
+#: builtin/rm.c:70
 msgid ""
-"submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+"\n"
 "(use 'rm -rf' if you really want to remove it including all of its history)"
 msgstr ""
-"undermodulen \"%s\" (eller en av dess undermoduler) använder en .git-"
-"katalog\n"
+"\n"
 "(använd \"rm -rf\" om du verkligen vill ta bort den och all dess historik)"
 
-#: builtin/rm.c:174
-#, c-format
+#: builtin/rm.c:230
+msgid ""
+"the following file has staged content different from both the\n"
+"file and the HEAD:"
+msgid_plural ""
+"the following files have staged content different from both the\n"
+"file and the HEAD:"
+msgstr[0] ""
+"följande fil har köat innehåll som skiljer sig både från filen och HEAD:"
+msgstr[1] ""
+"följande filer har köat innehåll som skiljer sig både från filen och HEAD:"
+
+#: builtin/rm.c:235
 msgid ""
-"'%s' has staged content different from both the file and the HEAD\n"
+"\n"
 "(use -f to force removal)"
 msgstr ""
-"\"%s\" har köat ändringar som skiljer sig både från filen och HEAD\n"
+"\n"
 "(använd -f för att tvinga borttagning)"
 
-#: builtin/rm.c:180
-#, c-format
-msgid ""
-"'%s' has changes staged in the index\n"
-"(use --cached to keep the file, or -f to force removal)"
-msgstr ""
-"\"%s\" har köade ändringar i indexet\n"
-"(använd --cached för att behålla filen eller -f för att tvinga borttagning)"
+#: builtin/rm.c:239
+msgid "the following file has changes staged in the index:"
+msgid_plural "the following files have changes staged in the index:"
+msgstr[0] "följande fil har ändringar köade i indexet:"
+msgstr[1] "följande filer har ändringar köade i indexet:"
 
-#: builtin/rm.c:191
-#, c-format
+#: builtin/rm.c:243 builtin/rm.c:254
 msgid ""
-"'%s' has local modifications\n"
+"\n"
 "(use --cached to keep the file, or -f to force removal)"
 msgstr ""
-"\"%s\" har lokala ändringar\n"
+"\n"
 "(använd --cached för att behålla filen eller -f för att tvinga borttagning)"
 
-#: builtin/rm.c:207
+#: builtin/rm.c:251
+msgid "the following file has local modifications:"
+msgid_plural "the following files have local modifications:"
+msgstr[0] "följande fil har lokala ändringar:"
+msgstr[1] "följande filer har lokala ändringar:"
+
+#: builtin/rm.c:269
 msgid "do not list removed files"
 msgstr "lista inte borttagna filer"
 
-#: builtin/rm.c:208
+#: builtin/rm.c:270
 msgid "only remove from the index"
 msgstr "ta bara bort från indexet"
 
-#: builtin/rm.c:209
+#: builtin/rm.c:271
 msgid "override the up-to-date check"
 msgstr "överstyr àjour-testet"
 
-#: builtin/rm.c:210
+#: builtin/rm.c:272
 msgid "allow recursive removal"
 msgstr "tillåt rekursiv borttagning"
 
-#: builtin/rm.c:212
+#: builtin/rm.c:274
 msgid "exit with a zero status even if nothing matched"
 msgstr "avsluta med nollstatus även om inget träffades"
 
-#: builtin/rm.c:283
+#: builtin/rm.c:345
 #, c-format
 msgid "not removing '%s' recursively without -r"
 msgstr "tar inte bort \"%s\" rekursivt utan -r"
 
-#: builtin/rm.c:322
+#: builtin/rm.c:384
 #, c-format
 msgid "git rm: unable to remove %s"
 msgstr "git rm: kan inte ta bort %s"
 
 #: builtin/shortlog.c:13
-msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
-msgstr ""
-"git shortlog [-n] [-s] [-e] [-w] [rev-flaggor] [--] [<incheckning-id>... ]"
+msgid "git shortlog [<options>] [<revision range>] [[--] [<path>...]]"
+msgstr "git shortlog [<flaggor>] [<versionsintervall>] [[--] <sökväg>...]"
 
-#: builtin/shortlog.c:133
+#: builtin/shortlog.c:131
 #, c-format
 msgid "Missing author: %s"
 msgstr "Författare saknas: %s"
 
-#: builtin/shortlog.c:229
+#: builtin/shortlog.c:228
 msgid "sort output according to the number of commits per author"
 msgstr "sortera utdata enligt antal incheckningar per författare"
 
-#: builtin/shortlog.c:231
+#: builtin/shortlog.c:230
 msgid "Suppress commit descriptions, only provides commit count"
 msgstr "Undertryck beskrivningar, visa bara antal incheckningar"
 
-#: builtin/shortlog.c:233
+#: builtin/shortlog.c:232
 msgid "Show the email address of each author"
 msgstr "Visa e-postadress för varje författare"
 
-#: builtin/shortlog.c:234
+#: builtin/shortlog.c:233
 msgid "w[,i1[,i2]]"
 msgstr "w[,i1[,i2]]"
 
-#: builtin/shortlog.c:235
+#: builtin/shortlog.c:234
 msgid "Linewrap output"
 msgstr "Radbryt utdata"
 
@@ -8158,47 +8757,47 @@ msgstr ""
 msgid "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g|--reflog)[=<n>[,<bas>]] [--list] [<ref>]"
 
-#: builtin/show-branch.c:651
+#: builtin/show-branch.c:650
 msgid "show remote-tracking and local branches"
 msgstr "visa fjärrspårande och lokala grenar"
 
-#: builtin/show-branch.c:653
+#: builtin/show-branch.c:652
 msgid "show remote-tracking branches"
 msgstr "visa fjärrspårande grenar"
 
-#: builtin/show-branch.c:655
+#: builtin/show-branch.c:654
 msgid "color '*!+-' corresponding to the branch"
 msgstr "färga \"*!+-\" enligt grenen"
 
-#: builtin/show-branch.c:657
+#: builtin/show-branch.c:656
 msgid "show <n> more commits after the common ancestor"
 msgstr "visa <n> ytterligare incheckningar efter gemensam anfader"
 
-#: builtin/show-branch.c:659
+#: builtin/show-branch.c:658
 msgid "synonym to more=-1"
 msgstr "synonym till more=-1"
 
-#: builtin/show-branch.c:660
+#: builtin/show-branch.c:659
 msgid "suppress naming strings"
 msgstr "undertyck namnsträngar"
 
-#: builtin/show-branch.c:662
+#: builtin/show-branch.c:661
 msgid "include the current branch"
 msgstr "inkludera aktuell gren"
 
-#: builtin/show-branch.c:664
+#: builtin/show-branch.c:663
 msgid "name commits with their object names"
 msgstr "namnge incheckningar med deras objektnamn"
 
-#: builtin/show-branch.c:666
+#: builtin/show-branch.c:665
 msgid "show possible merge bases"
 msgstr "visa möjliga sammanslaningsbaser"
 
-#: builtin/show-branch.c:668
+#: builtin/show-branch.c:667
 msgid "show refs unreachable from any other ref"
 msgstr "visa referenser som inte kan nås från någon annan referens"
 
-#: builtin/show-branch.c:670
+#: builtin/show-branch.c:669
 msgid "show commits in topological order"
 msgstr "visa incheckningar i topologisk ordning"
 
@@ -8211,14 +8810,14 @@ msgid "show merges reachable from only one tip"
 msgstr "visa sammanslagningar som endast kan nås från en spets"
 
 #: builtin/show-branch.c:676
-msgid "show commits where no parent comes before its children"
-msgstr "visa incheckningar där ingen förälder kommer före dess barn"
+msgid "topologically sort, maintaining date order where possible"
+msgstr "sortera topologiskt, behåll datumordning när möjligt"
 
-#: builtin/show-branch.c:678
+#: builtin/show-branch.c:679
 msgid "<n>[,<base>]"
 msgstr "<n>[,<bas>]"
 
-#: builtin/show-branch.c:679
+#: builtin/show-branch.c:680
 msgid "show <n> most recent ref-log entries starting at base"
 msgstr "visa <n> nyaste refloggposter med början på bas"
 
@@ -8234,35 +8833,35 @@ msgstr ""
 msgid "git show-ref --exclude-existing[=pattern] < ref-list"
 msgstr "git show-ref --exclude-existing[=mönster] < reflista"
 
-#: builtin/show-ref.c:165
+#: builtin/show-ref.c:168
 msgid "only show tags (can be combined with heads)"
 msgstr "visa endast taggar (kan kombineras med huvuden)"
 
-#: builtin/show-ref.c:166
+#: builtin/show-ref.c:169
 msgid "only show heads (can be combined with tags)"
 msgstr "visa endast huvuden (kan kombineras med taggar)"
 
-#: builtin/show-ref.c:167
+#: builtin/show-ref.c:170
 msgid "stricter reference checking, requires exact ref path"
 msgstr "striktare referenskontroll, kräver exakt referenssökväg"
 
-#: builtin/show-ref.c:170 builtin/show-ref.c:172
-msgid "show the HEAD reference"
-msgstr "visa HEAD-referensen"
+#: builtin/show-ref.c:173 builtin/show-ref.c:176
+msgid "show the HEAD reference, even if it would be filtered out"
+msgstr "visa HEAD-refrens, även när den skulle filtreras ut"
 
-#: builtin/show-ref.c:174
+#: builtin/show-ref.c:178
 msgid "dereference tags into object IDs"
 msgstr "avreferera taggar till objekt-id"
 
-#: builtin/show-ref.c:176
+#: builtin/show-ref.c:180
 msgid "only show SHA1 hash using <n> digits"
 msgstr "visa SHA1-hash endast med <n> siffror"
 
-#: builtin/show-ref.c:180
+#: builtin/show-ref.c:184
 msgid "do not print results to stdout (useful with --verify)"
 msgstr "visa inte resultat på standard ut (användbart med --verify)"
 
-#: builtin/show-ref.c:182
+#: builtin/show-ref.c:186
 msgid "show refs from stdin that aren't in local repository"
 msgstr "visa referenser från standard in som inte finns i lokalt arkiv"
 
@@ -8677,24 +9276,33 @@ msgstr "visa trädobjekt för underkatalogen <prefix>"
 msgid "only useful for debugging"
 msgstr "endast användbart vid felsökning"
 
-#: git.c:16
-msgid "See 'git help <command>' for more information on a specific command."
+#: git.c:17
+msgid ""
+"'git help -a' and 'git help -g' lists available subcommands and some\n"
+"concept guides. See 'git help <command>' or 'git help <concept>'\n"
+"to read about a specific subcommand or concept."
 msgstr ""
-"Se \"git help <kommando>\" för mer information om ett specifikt kommando."
+"\"git help -a\" och \"git help -g\" visar tillgängliga underkommandon och\n"
+"några konceptvägledningar. Se \"git help <kommando>\" eller \"git help\n"
+"<koncept>\" för att läsa mer om specifika underkommandon och koncept."
+
+#: parse-options.h:144
+msgid "expiry date"
+msgstr "giltig till"
 
-#: parse-options.h:156
+#: parse-options.h:159
 msgid "no-op (backward compatibility)"
 msgstr "ingen funktion (bakåtkompatibilitet)"
 
-#: parse-options.h:232
+#: parse-options.h:236
 msgid "be more verbose"
 msgstr "var mer pratsam"
 
-#: parse-options.h:234
+#: parse-options.h:238
 msgid "be more quiet"
 msgstr "var mer tyst"
 
-#: parse-options.h:240
+#: parse-options.h:244
 msgid "use <n> digits to display SHA-1s"
 msgstr "använd <n> siffror för att visa SHA-1:or"
 
@@ -8751,8 +9359,8 @@ msgid "Move or rename a file, a directory, or a symlink"
 msgstr "Flytta eller byt namn på en fil, katalog eller symbolisk länk"
 
 #: common-cmds.h:21
-msgid "Fetch from and merge with another repository or a local branch"
-msgstr "Hämta från och slå ihop med annat arkiv eller en lokal gren"
+msgid "Fetch from and integrate with another repository or a local branch"
+msgstr "Hämta från och integrera med annat arkiv eller en lokal gren"
 
 #: common-cmds.h:22
 msgid "Update remote refs along with associated objects"
@@ -8797,11 +9405,11 @@ msgstr ""
 #: git-am.sh:105
 #, sh-format
 msgid ""
-"When you have resolved this problem, run \"$cmdline --resolved\".\n"
+"When you have resolved this problem, run \"$cmdline --continue\".\n"
 "If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n"
 "To restore the original branch and stop patching, run \"$cmdline --abort\"."
 msgstr ""
-"När du har löst problemet kör du \"$cmdline --resolved\".\n"
+"När du har löst problemet kör du \"$cmdline --continue\".\n"
 "Om du vill hoppa över patchen kör du istället \"$cmdline --skip\".\n"
 "För att återställa originalgrenen och avbryta kör du \"$cmdline --abort\"."
 
@@ -8857,25 +9465,34 @@ msgstr ""
 "Flaggan -b/--binary har varit utan funktion länge, och\n"
 "kommer tas bort. Vi ber dig att inte använda den längre."
 
-#: git-am.sh:477
+#: git-am.sh:479
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
 msgstr "tidigare rebase-katalog $dotest finns fortfarande, men mbox angavs."
 
-#: git-am.sh:482
+#: git-am.sh:484
 msgid "Please make up your mind. --skip or --abort?"
 msgstr "Bestäm dig. --skip eller --abort?"
 
-#: git-am.sh:509
+#: git-am.sh:520
+#, sh-format
+msgid ""
+"Stray $dotest directory found.\n"
+"Use \"git am --abort\" to remove it."
+msgstr ""
+"Kvarbliven katalog $dotest hittades.\n"
+"Använd \"git am --abort\" för att ta bort den."
+
+#: git-am.sh:528
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr "Lösningsoperation pågår inte, vi återupptar inte."
 
-#: git-am.sh:575
+#: git-am.sh:594
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
 msgstr "Smutsigt index: kan inte tillämpa patchar (smutsiga: $files)"
 
-#: git-am.sh:679
+#: git-am.sh:698
 #, sh-format
 msgid ""
 "Patch is empty.  Was it split wrong?\n"
@@ -8886,32 +9503,32 @@ msgstr ""
 "Om du vill hoppa över patchen kör du istället \"$cmdline --skip\".\n"
 "För att återställa originalgrenen och avbryta kör du \"$cmdline --abort\"."
 
-#: git-am.sh:706
+#: git-am.sh:725
 msgid "Patch does not have a valid e-mail address."
 msgstr "Patchen har inte någon giltig e-postadress."
 
-#: git-am.sh:753
+#: git-am.sh:772
 msgid "cannot be interactive without stdin connected to a terminal."
 msgstr ""
 "kan inte vara interaktiv om standard in inte är ansluten till en terminal."
 
-#: git-am.sh:757
+#: git-am.sh:776
 msgid "Commit Body is:"
 msgstr "Incheckningskroppen är:"
 
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
-#: git-am.sh:764
+#: git-am.sh:783
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 msgstr "Tillämpa? Y=ja/N=nej/E=redigera/V=visa patch/A=godta alla "
 
-#: git-am.sh:800
+#: git-am.sh:819
 #, sh-format
 msgid "Applying: $FIRSTLINE"
 msgstr "Tillämpar: $FIRSTLINE"
 
-#: git-am.sh:821
+#: git-am.sh:840
 msgid ""
 "No changes - did you forget to use 'git add'?\n"
 "If there is nothing left to stage, chances are that something else\n"
@@ -8921,7 +9538,7 @@ msgstr ""
 "Om det inte är något kvar att köa kan det hända att något annat redan\n"
 "introducerat samma ändringar; kanske du bör hoppa över patchen."
 
-#: git-am.sh:829
+#: git-am.sh:848
 msgid ""
 "You still have unmerged paths in your index\n"
 "did you forget to use 'git add'?"
@@ -8929,16 +9546,16 @@ msgstr ""
 "Du har fortfarande sökvägar som inte slagits samman i ditt index\n"
 "glömde du använda \"git add\"?"
 
-#: git-am.sh:845
+#: git-am.sh:864
 msgid "No changes -- Patch already applied."
 msgstr "Inga ändringar -- Patchen har redan tillämpats."
 
-#: git-am.sh:855
+#: git-am.sh:874
 #, sh-format
 msgid "Patch failed at $msgnum $FIRSTLINE"
 msgstr "Patchen misslyckades vid $msgnum $FIRSTLINE"
 
-#: git-am.sh:858
+#: git-am.sh:877
 #, sh-format
 msgid ""
 "The copy of the patch that failed is found in:\n"
@@ -8947,7 +9564,7 @@ msgstr ""
 "En kopia av patchen som misslyckades finns i:\n"
 "   $dotest/patch"
 
-#: git-am.sh:876
+#: git-am.sh:895
 msgid "applying to an empty history"
 msgstr "tillämpar på en tom historik"
 
@@ -9047,16 +9664,16 @@ msgstr ""
 "Du måste sedan ange åtminstone en bra och en dålig version.\n"
 "(Du kan använda \"git bisect bad\" och \"git bisect good\" för detta.)"
 
-#: git-bisect.sh:347 git-bisect.sh:474
+#: git-bisect.sh:363 git-bisect.sh:490
 msgid "We are not bisecting."
 msgstr "Vi utför ingen bisect för tillfället."
 
-#: git-bisect.sh:354
+#: git-bisect.sh:370
 #, sh-format
 msgid "'$invalid' is not a valid commit"
 msgstr "\"$invalid\" är inte en giltig incheckning"
 
-#: git-bisect.sh:363
+#: git-bisect.sh:379
 #, sh-format
 msgid ""
 "Could not check out original HEAD '$branch'.\n"
@@ -9065,25 +9682,25 @@ msgstr ""
 "Kunde inte checka ut original-HEAD \"$branch\".\n"
 "Försök \"git bisect reset <incheckning>\"."
 
-#: git-bisect.sh:390
+#: git-bisect.sh:406
 msgid "No logfile given"
 msgstr "Ingen loggfil angiven"
 
-#: git-bisect.sh:391
+#: git-bisect.sh:407
 #, sh-format
 msgid "cannot read $file for replaying"
 msgstr "kan inte läsa $file för uppspelning"
 
-#: git-bisect.sh:408
+#: git-bisect.sh:424
 msgid "?? what are you talking about?"
 msgstr "?? vad menar du?"
 
-#: git-bisect.sh:420
+#: git-bisect.sh:436
 #, sh-format
 msgid "running $command"
 msgstr "kör $command"
 
-#: git-bisect.sh:427
+#: git-bisect.sh:443
 #, sh-format
 msgid ""
 "bisect run failed:\n"
@@ -9092,11 +9709,11 @@ msgstr ""
 "\"bisect\"-körningen misslyckades:\n"
 "felkod $res från \"$command\" är < 0 eller >= 128"
 
-#: git-bisect.sh:453
+#: git-bisect.sh:469
 msgid "bisect run cannot continue any more"
 msgstr "\"bisect\"-körningen kan inte fortsätta längre"
 
-#: git-bisect.sh:459
+#: git-bisect.sh:475
 #, sh-format
 msgid ""
 "bisect run failed:\n"
@@ -9105,7 +9722,7 @@ msgstr ""
 "\"bisect\"-körningen misslyckades:\n"
 "\"bisect_state $state\" avslutades med felkoden $res"
 
-#: git-bisect.sh:466
+#: git-bisect.sh:482
 msgid "bisect run success"
 msgstr "\"bisect\"-körningen lyckades"
 
@@ -9123,7 +9740,7 @@ msgstr ""
 msgid "Pull is not possible because you have unmerged files."
 msgstr "Du kan inte göra en \"pull\" då du har ändringar som inte checkats in."
 
-#: git-pull.sh:197
+#: git-pull.sh:203
 msgid "updating an unborn branch with changes added to the index"
 msgstr "uppdaterar en ofödd gren med ändringar som lagts till i indexet"
 
@@ -9131,7 +9748,7 @@ msgstr "uppdaterar en ofödd gren med ändringar som lagts till i indexet"
 #. The working tree and the index file is still based on the
 #. $orig_head commit, but we are merging into $curr_head.
 #. First update the working tree to match $curr_head.
-#: git-pull.sh:229
+#: git-pull.sh:235
 #, sh-format
 msgid ""
 "Warning: fetch updated the current branch head.\n"
@@ -9142,15 +9759,15 @@ msgstr ""
 "Varning: snabbspolar din arbetskatalog från\n"
 "Varning: incheckningen $orig_head."
 
-#: git-pull.sh:254
+#: git-pull.sh:260
 msgid "Cannot merge multiple branches into empty head"
 msgstr "Kan inte slå ihop flera grenar i ett tomt huvud."
 
-#: git-pull.sh:258
+#: git-pull.sh:264
 msgid "Cannot rebase onto multiple branches"
 msgstr "Kan inte utföra en \"rebase\" ovanpå flera grenar"
 
-#: git-rebase.sh:53
+#: git-rebase.sh:54
 msgid ""
 "When you have resolved this problem, run \"git rebase --continue\".\n"
 "If you prefer to skip this patch, run \"git rebase --skip\" instead.\n"
@@ -9161,31 +9778,50 @@ msgstr ""
 "Om du vill hoppa över patchen kör du istället \"git rebase --skip\".\n"
 "För att återställa originalgrenen och avbryta kör du \"git rebase --abort\"."
 
+#: git-rebase.sh:156
+msgid "Applied autostash."
+msgstr "Tillämpade autostash."
+
+#: git-rebase.sh:159
+#, sh-format
+msgid "Cannot store $stash_sha1"
+msgstr "Kan inte spara $stash_sha1"
+
 #: git-rebase.sh:160
+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 ""
+"Tillämpning av autostash gav konflikter.\n"
+"Dina ändringar är säkra i stashen.\n"
+"Du kan när som helst använda \"git stash pop\" eller \"git stash drop\".\n"
+
+#: git-rebase.sh:190
 msgid "The pre-rebase hook refused to rebase."
 msgstr "Kroken pre-rebase vägrade ombaseringen."
 
-#: git-rebase.sh:165
+#: git-rebase.sh:195
 msgid "It looks like git-am is in progress. Cannot rebase."
 msgstr "Det verkar som en git-am körs. Kan inte ombasera."
 
-#: git-rebase.sh:296
+#: git-rebase.sh:329
 msgid "The --exec option must be used with the --interactive option"
 msgstr "Flaggan --exec måste användas tillsammans med flaggan --interactive"
 
-#: git-rebase.sh:301
+#: git-rebase.sh:334
 msgid "No rebase in progress?"
 msgstr "Ingen ombasering pågår?"
 
-#: git-rebase.sh:312
+#: git-rebase.sh:345
 msgid "The --edit-todo action can only be used during interactive rebase."
 msgstr "Åtgärden --edit-todo kan endast användas under interaktiv ombasering."
 
-#: git-rebase.sh:319
+#: git-rebase.sh:352
 msgid "Cannot read HEAD"
 msgstr "Kan inte läsa HEAD"
 
-#: git-rebase.sh:322
+#: git-rebase.sh:355
 msgid ""
 "You must edit all merge conflicts and then\n"
 "mark them as resolved using git add"
@@ -9193,12 +9829,12 @@ msgstr ""
 "Du måste redigera alla sammanslagningskonflikter och\n"
 "därefter markera dem som lösta med git add"
 
-#: git-rebase.sh:340
+#: git-rebase.sh:373
 #, sh-format
 msgid "Could not move back to $head_name"
 msgstr "Kunde inte flytta tillbaka till $head_name"
 
-#: git-rebase.sh:359
+#: git-rebase.sh:392
 #, sh-format
 msgid ""
 "It seems that there is already a $state_dir_base directory, and\n"
@@ -9219,57 +9855,66 @@ msgstr ""
 "och kör programmet igen. Jag avslutar ifall du fortfarande har\n"
 "något av värde där."
 
-#: git-rebase.sh:404
+#: git-rebase.sh:437
 #, sh-format
 msgid "invalid upstream $upstream_name"
 msgstr "ogiltig uppström $upstream_name"
 
-#: git-rebase.sh:428
+#: git-rebase.sh:461
 #, sh-format
 msgid "$onto_name: there are more than one merge bases"
 msgstr "$onto_name: mer än en sammanslagningsbas finns"
 
-#: git-rebase.sh:431 git-rebase.sh:435
+#: git-rebase.sh:464 git-rebase.sh:468
 #, sh-format
 msgid "$onto_name: there is no merge base"
 msgstr "$onto_name: ingen sammanslagningsbas finns"
 
-#: git-rebase.sh:440
+#: git-rebase.sh:473
 #, sh-format
 msgid "Does not point to a valid commit: $onto_name"
 msgstr "Peka på en giltig incheckning: $onto_name"
 
-#: git-rebase.sh:463
+#: git-rebase.sh:496
 #, sh-format
 msgid "fatal: no such branch: $branch_name"
 msgstr "ödesdigert: ingen sådan gren: $branch_name"
 
-#: git-rebase.sh:483
+#: git-rebase.sh:519
+msgid "Cannot autostash"
+msgstr "Kan inte utföra \"autostash\""
+
+#: git-rebase.sh:524
+#, sh-format
+msgid "Created autostash: $stash_abbrev"
+msgstr "Skapade autostash: $stash_abbrev"
+
+#: git-rebase.sh:528
 msgid "Please commit or stash them."
 msgstr "Checka in eller använd \"stash\" på dem."
 
-#: git-rebase.sh:501
+#: git-rebase.sh:548
 #, sh-format
 msgid "Current branch $branch_name is up to date."
 msgstr "Aktuell gren $branch_name är à jour."
 
-#: git-rebase.sh:504
+#: git-rebase.sh:552
 #, sh-format
 msgid "Current branch $branch_name is up to date, rebase forced."
 msgstr "Aktuell gren $branch_name är à jour, ombasering framtvingad."
 
-#: git-rebase.sh:515
+#: git-rebase.sh:563
 #, sh-format
 msgid "Changes from $mb to $onto:"
 msgstr "Ändringar från $mb till $onto:"
 
 #. Detach HEAD and reset the tree
-#: git-rebase.sh:524
+#: git-rebase.sh:572
 msgid "First, rewinding head to replay your work on top of it..."
 msgstr ""
 "Först, spolar tillbaka huvudet för att spela av ditt arbete ovanpå det..."
 
-#: git-rebase.sh:532
+#: git-rebase.sh:582
 #, sh-format
 msgid "Fast-forwarded $branch_name to $onto_name."
 msgstr "Snabbspolade $branch_name till $onto_name."
@@ -9302,6 +9947,11 @@ msgstr "Kan inte ta bort temporärt index (kan inte inträffa)"
 msgid "Cannot record working tree state"
 msgstr "Kan inte registrera tillstånd för arbetskatalog"
 
+#: git-stash.sh:190
+#, sh-format
+msgid "Cannot update $ref_stash with $w_commit"
+msgstr "Kan inte uppdatera $ref_stash med $w_commit"
+
 #. TRANSLATORS: $option is an invalid option, like
 #. `--blah-blah'. The 7 spaces at the beginning of the
 #. second line correspond to "error: ". So you should line
@@ -9312,7 +9962,7 @@ msgstr "Kan inte registrera tillstånd för arbetskatalog"
 #. $ 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:202
+#: git-stash.sh:241
 #, sh-format
 msgid ""
 "error: unknown option for 'stash save': $option\n"
@@ -9321,120 +9971,139 @@ msgstr ""
 "fel: felaktig flagga för \"stash save\": $option\n"
 "     För att ange ett meddelande, använd git stash save -- \"$option\""
 
-#: git-stash.sh:223
+#: git-stash.sh:262
 msgid "No local changes to save"
 msgstr "Inga lokala ändringar att spara"
 
-#: git-stash.sh:227
+#: git-stash.sh:268
+msgid ""
+"The following untracked files would NOT be saved but need to be removed by "
+"stash save:"
+msgstr ""
+"Följande ospårade ändringar skulle INTE sparas utan måste tas bort med "
+"\"stash save\":"
+
+#: git-stash.sh:270
+msgid ""
+"Aborting. Consider using either the --force or --include-untracked option."
+msgstr ""
+"Avbryter. Överväg att använda antingen flaggan --force eller --include-"
+"untracked."
+
+#: git-stash.sh:274
 msgid "Cannot initialize stash"
 msgstr "Kan inte initiera \"stash\""
 
-#: git-stash.sh:235
+#: git-stash.sh:278
 msgid "Cannot save the current status"
 msgstr "Kan inte spara aktuell status"
 
-#: git-stash.sh:253
+#: git-stash.sh:296
 msgid "Cannot remove worktree changes"
 msgstr "Kan inte ta bort ändringar i arbetskatalogen"
 
-#: git-stash.sh:352
+#: git-stash.sh:395
 msgid "No stash found."
 msgstr "Ingen \"stash\" hittades."
 
-#: git-stash.sh:359
+#: git-stash.sh:402
 #, sh-format
 msgid "Too many revisions specified: $REV"
 msgstr "För många revisioner angivna: $REV"
 
-#: git-stash.sh:365
+#: git-stash.sh:408
 #, sh-format
 msgid "$reference is not valid reference"
 msgstr "$reference är inte en giltig referens."
 
-#: git-stash.sh:393
+#: git-stash.sh:436
 #, sh-format
 msgid "'$args' is not a stash-like commit"
 msgstr "\"$args\" är inte en \"stash\"-liknande incheckning"
 
-#: git-stash.sh:404
+#: git-stash.sh:447
 #, sh-format
 msgid "'$args' is not a stash reference"
 msgstr "\"$args\" är inte en \"stash\"-referens"
 
-#: git-stash.sh:412
+#: git-stash.sh:455
 msgid "unable to refresh index"
 msgstr "kan inte uppdatera indexet"
 
-#: git-stash.sh:416
+#: git-stash.sh:459
 msgid "Cannot apply a stash in the middle of a merge"
 msgstr "Kan inte tillämpa en \"stash\" mitt i en sammanslagning"
 
-#: git-stash.sh:424
+#: git-stash.sh:467
 msgid "Conflicts in index. Try without --index."
 msgstr "Konflikter i indexet. Testa utan --index."
 
-#: git-stash.sh:426
+#: git-stash.sh:469
 msgid "Could not save index tree"
 msgstr "Kunde inte spara indexträd"
 
-#: git-stash.sh:460
+#: git-stash.sh:503
 msgid "Cannot unstage modified files"
 msgstr "Kan inte ta bort ändrade filer ur kön"
 
-#: git-stash.sh:475
+#: git-stash.sh:518
 msgid "Index was not unstashed."
 msgstr "Indexet har inte tagits ur kön."
 
-#: git-stash.sh:492
+#: git-stash.sh:535
 #, sh-format
 msgid "Dropped ${REV} ($s)"
 msgstr "Kastade ${REV} ($s)"
 
-#: git-stash.sh:493
+#: git-stash.sh:536
 #, sh-format
 msgid "${REV}: Could not drop stash entry"
 msgstr "${REV}: Kunde inte kasta \"stash\"-post"
 
-#: git-stash.sh:500
+#: git-stash.sh:543
 msgid "No branch name specified"
 msgstr "Inget grennamn angavs"
 
-#: git-stash.sh:571
+#: git-stash.sh:615
 msgid "(To restore them type \"git stash apply\")"
 msgstr "(För att återställa dem, skriv \"git stash apply\")"
 
-#: git-submodule.sh:90
+#: git-submodule.sh:95
 #, sh-format
 msgid "cannot strip one component off url '$remoteurl'"
 msgstr "kan inte ta bort en komponent från url:en \"$remoteurl\""
 
-#: git-submodule.sh:195
+#: git-submodule.sh:237
 #, sh-format
 msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
 msgstr ""
 "Hittade ingen undermodulmappning i .gitmodules för sökvägen \"$sm_path\""
 
-#: git-submodule.sh:238
+#: git-submodule.sh:281
 #, sh-format
 msgid "Clone of '$url' into submodule path '$sm_path' failed"
 msgstr "Misslyckades klona \"$url\" till undermodulsökvägen \"$sm_path\""
 
-#: git-submodule.sh:250
+#: git-submodule.sh:293
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
-msgstr "Gitkatalog \"$a\" ingår i underkatalogsökvägen \"$b\" eller omvänt"
+msgstr "Gitkatalog \"$a\" ingår i undermodulsökvägen \"$b\" eller omvänt"
+
+#: git-submodule.sh:403
+msgid "Relative path can only be used from the toplevel of the working tree"
+msgstr "Relativ sökväg kan enfast användas från arbetskatalogens toppnivå"
 
-#: git-submodule.sh:343
+#: git-submodule.sh:413
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
 msgstr "arkiv-URL: \"$repo\" måste vara absolut eller börja med ./|../"
 
-#: git-submodule.sh:360
+#: git-submodule.sh:430
 #, sh-format
 msgid "'$sm_path' already exists in the index"
 msgstr "\"$sm_path\" finns redan i indexet"
 
-#: git-submodule.sh:364
+#: git-submodule.sh:434
 #, sh-format
 msgid ""
 "The following path is ignored by one of your .gitignore files:\n"
@@ -9445,22 +10114,22 @@ msgstr ""
 "$sm_path\n"
 "Använd -f om du verkligen vill lägga till den"
 
-#: git-submodule.sh:382
+#: git-submodule.sh:452
 #, sh-format
 msgid "Adding existing repo at '$sm_path' to the index"
 msgstr "Lägger till befintligt arkiv i \"$sm_path\" i indexet"
 
-#: git-submodule.sh:384
+#: git-submodule.sh:454
 #, sh-format
 msgid "'$sm_path' already exists and is not a valid git repo"
 msgstr "\"$sm_path\" finns redan och är inte ett giltigt git-arkiv"
 
-#: git-submodule.sh:392
+#: git-submodule.sh:462
 #, sh-format
 msgid "A git directory for '$sm_name' is found locally with remote(s):"
 msgstr "En git-katalog för \"$sm_name\" hittades lokalt med fjärr(ar):"
 
-#: git-submodule.sh:394
+#: git-submodule.sh:464
 #, sh-format
 msgid ""
 "If you want to reuse this local git directory instead of cloning again from"
@@ -9468,14 +10137,14 @@ msgstr ""
 "För att återanvända den lokala git-katalogen istället för att på nytt klona "
 "från"
 
-#: git-submodule.sh:396
+#: git-submodule.sh:466
 #, sh-format
 msgid ""
 "use the '--force' option. If the local git directory is not the correct repo"
 msgstr ""
 "använd flaggan \"--force\". Om den lokala git-katalogen inte är riktigt arkiv"
 
-#: git-submodule.sh:397
+#: git-submodule.sh:467
 #, sh-format
 msgid ""
 "or you are unsure what this means choose another name with the '--name' "
@@ -9484,153 +10153,299 @@ msgstr ""
 "eller om du är osäker på vad det innebär, välj nytt namn med flaggan \"--name"
 "\"."
 
-#: git-submodule.sh:399
+#: git-submodule.sh:469
 #, sh-format
 msgid "Reactivating local git directory for submodule '$sm_name'."
 msgstr "Aktiverar lokal git-katalog för undermodulen \"$sm_name\" på nytt."
 
-#: git-submodule.sh:411
+#: git-submodule.sh:481
 #, sh-format
 msgid "Unable to checkout submodule '$sm_path'"
 msgstr "Kan inte checka ut undermodulen \"$sm_path\""
 
-#: git-submodule.sh:416
+#: git-submodule.sh:486
 #, sh-format
 msgid "Failed to add submodule '$sm_path'"
 msgstr "Misslyckades lägga till undermodulen \"$sm_path\""
 
-#: git-submodule.sh:425
+#: git-submodule.sh:495
 #, sh-format
 msgid "Failed to register submodule '$sm_path'"
 msgstr "Misslyckades registrera undermodulen \"$sm_path\""
 
-#: git-submodule.sh:468
+#: git-submodule.sh:539
+#, sh-format
+msgid "Entering '$prefix$displaypath'"
+msgstr "Går in i \"$prefix$displaypath\""
+
+#: git-submodule.sh:554
+#, sh-format
+msgid "Stopping at '$prefix$displaypath'; script returned non-zero status."
+msgstr ""
+"Stoppar på \"$prefix$displaypath\"; skriptet returnerade en status skild "
+"från noll."
+
+#: git-submodule.sh:600
+#, sh-format
+msgid "No url found for submodule path '$displaypath' in .gitmodules"
+msgstr ""
+"Hittade ingen url för undermodulsökvägen \"$displaypath\" i .gitmodules"
+
+#: git-submodule.sh:609
+#, sh-format
+msgid "Failed to register url for submodule path '$displaypath'"
+msgstr "Misslyckades registrera url för undermodulsökväg \"$displaypath\""
+
+#: git-submodule.sh:611
 #, sh-format
-msgid "Entering '$prefix$sm_path'"
-msgstr "Går in i \"$prefix$sm_path\""
+msgid "Submodule '$name' ($url) registered for path '$displaypath'"
+msgstr ""
+"Undermodulen \"$name\" ($url) registrerad för sökvägen \"$displaypath\""
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Failed to register update mode for submodule path '$displaypath'"
+msgstr ""
+"Misslyckades registrera uppdateringsläge för undermodulsökväg \"$displaypath"
+"\""
+
+#: git-submodule.sh:656
+#, sh-format
+msgid "Use '.' if you really want to deinitialize all submodules"
+msgstr "Använd \".\" om du verkligen vill avinitiera alla undermoduler"
+
+#: git-submodule.sh:673
+#, sh-format
+msgid "Submodule work tree '$displaypath' contains a .git directory"
+msgstr ""
+"Undermodulens arbetskatalog \"$displaypath\" innehåller katalogen \".git\""
+
+#: git-submodule.sh:674
+#, sh-format
+msgid ""
+"(use 'rm -rf' if you really want to remove it including all of its history)"
+msgstr ""
+"(använd \"rm -rf\" om du verkligen vill ta bort den och all dess historik)"
 
-#: git-submodule.sh:482
+#: git-submodule.sh:680
 #, sh-format
-msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgid ""
+"Submodule work tree '$displaypath' contains local modifications; use '-f' to "
+"discard them"
 msgstr ""
-"Stoppar på \"$sm_path\"; skriptet returnerade en status skild från noll."
+"Undermodulens arbetskatalog \"$displaypath\" har lokala ändringar; \"-f\" "
+"kastar bort dem"
 
-#: git-submodule.sh:526
+#: git-submodule.sh:683
 #, sh-format
-msgid "No url found for submodule path '$sm_path' in .gitmodules"
-msgstr "Hittade ingen url för undermodulsökvägen \"$sm_path\" i .gitmodules"
+msgid "Cleared directory '$displaypath'"
+msgstr "Rensade katalogen \"$displaypath\""
 
-#: git-submodule.sh:535
+#: git-submodule.sh:684
 #, sh-format
-msgid "Failed to register url for submodule path '$sm_path'"
-msgstr "Misslyckades registrera url för underkatalogsökväg \"$sm_path\""
+msgid "Could not remove submodule work tree '$displaypath'"
+msgstr "Kunde inte ta bort undermodulens arbetskatalog \"$displaypath\""
 
-#: git-submodule.sh:537
+#: git-submodule.sh:687
 #, sh-format
-msgid "Submodule '$name' ($url) registered for path '$sm_path'"
-msgstr "Undermodulen \"$name\" ($url) registrerad för sökvägen \"$sm_path\""
+msgid "Could not create empty submodule directory '$displaypath'"
+msgstr "Kunde inte skapa tom undermodulskatalog \"$displaypath\""
 
-#: git-submodule.sh:545
+#: git-submodule.sh:696
 #, sh-format
-msgid "Failed to register update mode for submodule path '$sm_path'"
+msgid "Submodule '$name' ($url) unregistered for path '$displaypath'"
 msgstr ""
-"Misslyckades registrera uppdateringsläge för undermodulsökväg \"$sm_path\""
+"Undermodulen \"$name\" ($url) avregistrerad för sökvägen \"$displaypath\""
 
-#: git-submodule.sh:649
+#: git-submodule.sh:811
 #, sh-format
 msgid ""
-"Submodule path '$sm_path' not initialized\n"
+"Submodule path '$displaypath' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
-"Undermodulen \"$sm_path\" har inte initierats\n"
+"Undermodulen \"$displaypath\" har inte initierats\n"
 "Kanske du vill köra \"update --init\"?"
 
-#: git-submodule.sh:662
+#: git-submodule.sh:824
 #, sh-format
-msgid "Unable to find current revision in submodule path '$sm_path'"
-msgstr "Kan inte hitta aktuell revision i undermodulsökväg \"$sm_path\""
+msgid "Unable to find current revision in submodule path '$displaypath'"
+msgstr "Kan inte hitta aktuell revision i undermodulsökvägen \"$displaypath\""
 
-#: git-submodule.sh:671 git-submodule.sh:695
+#: git-submodule.sh:833
 #, sh-format
 msgid "Unable to fetch in submodule path '$sm_path'"
 msgstr "Kan inte hämta i undermodulsökväg \"$sm_path\""
 
-#: git-submodule.sh:709
+#: git-submodule.sh:857
 #, sh-format
-msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
-msgstr "Kan inte ombasera \"$sha1\" i undermodulsökväg \"$sm_path\""
+msgid "Unable to fetch in submodule path '$displaypath'"
+msgstr "Kan inte hämta i undermodulsökväg \"$displaypath\""
 
-#: git-submodule.sh:710
+#: git-submodule.sh:871
 #, sh-format
-msgid "Submodule path '$sm_path': rebased into '$sha1'"
-msgstr "Undermodulsökvägen \"$sm_path\": ombaserade in i \"$sha1\""
+msgid "Unable to rebase '$sha1' in submodule path '$displaypath'"
+msgstr "Kan inte ombasera \"$sha1\" i undermodulsökvägen \"$displaypath\""
 
-#: git-submodule.sh:715
+#: git-submodule.sh:872
 #, sh-format
-msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
-msgstr "Kan inte slå ihop \"$sha1\" i undermodulsökvägen \"$sm_path\""
+msgid "Submodule path '$displaypath': rebased into '$sha1'"
+msgstr "Undermodulsökvägen \"$displaypath\": ombaserade in i \"$sha1\""
 
-#: git-submodule.sh:716
+#: git-submodule.sh:877
 #, sh-format
-msgid "Submodule path '$sm_path': merged in '$sha1'"
-msgstr "Undermodulsökvägen \"$sm_path\": sammanslagen i \"$sha1\""
+msgid "Unable to merge '$sha1' in submodule path '$displaypath'"
+msgstr "Kan inte slå ihop \"$sha1\" i undermodulsökvägen \"$displaypath\""
 
-#: git-submodule.sh:721
+#: git-submodule.sh:878
 #, sh-format
-msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
-msgstr "Kan inte checka ut \"$sha1\" i undermodulsökvägen \"$sm_path\""
+msgid "Submodule path '$displaypath': merged in '$sha1'"
+msgstr "Undermodulsökvägen \"$displaypath\": sammanslagen i \"$sha1\""
 
-#: git-submodule.sh:722
+#: git-submodule.sh:883
 #, sh-format
-msgid "Submodule path '$sm_path': checked out '$sha1'"
-msgstr "Undermodulsökvägen \"$sm_path\": checkade ut \"$sha1\""
+msgid ""
+"Execution of '$command $sha1' failed in submodule  path '$prefix$sm_path'"
+msgstr ""
+"Misslyckades köra \"$command $sha1\" i undermodulsökvägen \"$prefix$sm_path\""
 
-#: git-submodule.sh:744 git-submodule.sh:1066
+#: git-submodule.sh:884
 #, sh-format
-msgid "Failed to recurse into submodule path '$sm_path'"
-msgstr "Misslyckades rekursera in i undermodulsökvägen \"$sm_path\""
+msgid "Submodule path '$prefix$sm_path': '$command $sha1'"
+msgstr "Undermodulsökvägen \"$prefix$sm_path\": \"$command $sha1\""
+
+#: git-submodule.sh:889
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$displaypath'"
+msgstr "Kan inte checka ut \"$sha1\" i undermodulsökvägen \"$displaypath\""
+
+#: git-submodule.sh:890
+#, sh-format
+msgid "Submodule path '$displaypath': checked out '$sha1'"
+msgstr "Undermodulsökvägen \"$displaypath\": checkade ut \"$sha1\""
 
-#: git-submodule.sh:852
+#: git-submodule.sh:917
+#, sh-format
+msgid "Failed to recurse into submodule path '$displaypath'"
+msgstr "Misslyckades rekursera in i undermodulsökvägen \"$displaypath\""
+
+#: git-submodule.sh:1025
 msgid "The --cached option cannot be used with the --files option"
 msgstr "Flaggan --cached kan inte användas med flaggan --files"
 
 #. unexpected type
-#: git-submodule.sh:892
+#: git-submodule.sh:1066
 #, sh-format
 msgid "unexpected mode $mod_dst"
 msgstr "oväntat läge $mod_dst"
 
-#: git-submodule.sh:910
+#: git-submodule.sh:1086
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_src"
-msgstr "  Varning: $name innehåller inte incheckning $sha1_src"
+msgid "  Warn: $display_name doesn't contain commit $sha1_src"
+msgstr "  Varning: $display_name innehåller inte incheckningen $sha1_src"
 
-#: git-submodule.sh:913
+#: git-submodule.sh:1089
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_dst"
-msgstr "  Varning: $name innehåller inte incheckning $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commit $sha1_dst"
+msgstr "  Varning: $display_name innehåller inte incheckningen $sha1_dst"
 
-#: git-submodule.sh:916
+#: git-submodule.sh:1092
 #, sh-format
-msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
-msgstr "  Varning: $name innehåller inte incheckningar $sha1_src och $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr ""
+"  Varning: $display_name innehåller inte incheckningarna $sha1_src och "
+"$sha1_dst"
 
-#: git-submodule.sh:941
+#: git-submodule.sh:1117
 msgid "blob"
 msgstr "blob"
 
-#: git-submodule.sh:979
+#: git-submodule.sh:1155
 msgid "Submodules changed but not updated:"
 msgstr "Undermoduler ändrade men inte uppdaterade:"
 
-#: git-submodule.sh:981
+#: git-submodule.sh:1157
 msgid "Submodule changes to be committed:"
 msgstr "Undermodulers ändringar att checka in:"
 
-#: git-submodule.sh:1129
+#: git-submodule.sh:1242
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "Misslyckades rekursera in i undermodulsökvägen \"$sm_path\""
+
+#: git-submodule.sh:1306
 #, sh-format
-msgid "Synchronizing submodule url for '$prefix$sm_path'"
-msgstr "Synkroniserar undermodul-url för \"$prefix$sm_path\""
+msgid "Synchronizing submodule url for '$displaypath'"
+msgstr "Synkroniserar undermodul-url för \"$displaypath\""
+
+#~ msgid "  (fix conflicts and then run \"git am --resolved\")"
+#~ msgstr "  (rätta konflikter och kör sedan \"git am --resolved\")"
+
+#~ msgid "  (all conflicts fixed: run \"git commit\")"
+#~ msgstr "  (alla konflikter har rättats: kör \"git commit\")"
+
+#~ msgid "more than %d trees given: '%s'"
+#~ msgstr "mer än %d träd angavs: \"%s\""
+
+#~ msgid "You cannot combine --no-ff with --ff-only."
+#~ msgstr "Du kan inte kombinera --no-ff med --ff-only."
+
+#~ msgid ""
+#~ "submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+#~ "(use 'rm -rf' if you really want to remove it including all of its "
+#~ "history)"
+#~ msgstr ""
+#~ "undermodulen \"%s\" (eller en av dess undermoduler) använder en .git-"
+#~ "katalog\n"
+#~ "(använd \"rm -rf\" om du verkligen vill ta bort den och all dess historik)"
+
+#~ msgid ""
+#~ "'%s' has changes staged in the index\n"
+#~ "(use --cached to keep the file, or -f to force removal)"
+#~ msgstr ""
+#~ "\"%s\" har köade ändringar i indexet\n"
+#~ "(använd --cached för att behålla filen eller -f för att tvinga "
+#~ "borttagning)"
+
+#~ msgid "show commits where no parent comes before its children"
+#~ msgstr "visa incheckningar där ingen förälder kommer före dess barn"
+
+#~ msgid "show the HEAD reference"
+#~ msgstr "visa HEAD-referensen"
+
+#~ msgid "Unable to fetch in submodule path '$prefix$sm_path'"
+#~ msgstr "Kan inte hämta i undermodulsökväg \"$prefix$sm_path\""
+
+#~ msgid "Failed to recurse into submodule path '$prefix$sm_path'"
+#~ msgstr "Misslyckades rekursera in i undermodulsökvägen \"$prefix$sm_path\""
+
+#~ msgid "It took %.2f seconds to enumerate untracked files.  'status -uno'"
+#~ msgstr "Det tog %.2f sekunder att räkna ospårade filer. \"status -uno\""
+
+#~ msgid "may speed it up, but you have to be careful not to forget to add"
+#~ msgstr "kanske gör det snabbare, men du måste vara försiktig så att du"
+
+#~ msgid "new files yourself (see 'git help status')."
+#~ msgstr "inte glömmer lägga till filer själv (se \"git help status\")"
+
+#~ msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
+#~ msgstr ""
+#~ "git shortlog [-n] [-s] [-e] [-w] [rev-flaggor] [--] [<incheckning-id>... ]"
+
+#~ msgid "See 'git help <command>' for more information on a specific command."
+#~ msgstr ""
+#~ "Se \"git help <kommando>\" för mer information om ett specifikt kommando."
+
+#~ msgid "use any ref in .git/refs"
+#~ msgstr "använd alla referenser i .git/refs"
+
+#~ msgid "use any tag in .git/refs/tags"
+#~ msgstr "använd alla taggar i .git/refs/tags"
+
+#~ msgid "bad object %s"
+#~ msgstr "felaktigt objekt %s"
+
+#~ msgid "bogus committer info %s"
+#~ msgstr "felaktig incheckarinformation %s"
 
 #~ msgid "can't fdopen 'show' output fd"
 #~ msgstr "kunde inte öppna (fdopen) \"show\"-utdata-filhandtag"
@@ -9739,9 +10554,6 @@ msgstr "Synkroniserar undermodul-url för \"$prefix$sm_path\""
 #~ msgid "# Changed but not updated:"
 #~ msgstr "# Ändrade men inte uppdaterade:"
 
-#~ msgid "A branch named '%s' already exists."
-#~ msgstr "Det finns redan en gren som heter \"%s\""
-
 #~ msgid "path '%s' does not have all 3 versions"
 #~ msgstr "sökvägen \"%s\" har inte alla 3 versionerna"
 
@@ -9765,73 +10577,3 @@ msgstr "Synkroniserar undermodul-url för \"$prefix$sm_path\""
 
 #~ msgid "Failed to write current notes tree to database"
 #~ msgstr "Kunde inte skriva aktuellt anteckningsträd till databasen"
-
-# FIXME: Untranslatable!
-#
-#~ msgid "Refusing to %s notes in %s (outside of refs/notes/)"
-#~ msgstr "Vägrar %s anteckningar i %s (utanför refs/notes/)"
-
-#~ msgid "add"
-#~ msgstr "add"
-
-#~ msgid "copy"
-#~ msgstr "copy"
-
-#~ msgid "show"
-#~ msgstr "show"
-
-#~ msgid "remove"
-#~ msgstr "remove"
-
-#~ msgid "prune"
-#~ msgstr "prune"
-
-#~ msgid "The current branch %s is not tracking anything."
-#~ msgstr "Den aktuella grenen %s spårar ingenting."
-
-#~ msgid "No destination configured to push to."
-#~ msgstr "Har inte ställt in någon destination att sända till."
-
-#~ msgid "Reflog action message too long: %.*s..."
-#~ msgstr "Reflog-händelsemeddelande för långt: %.*s..."
-
-#~ msgid "Could not read commit message of %s"
-#~ msgstr "Kunde inte läsa incheckningsmeddelandet för %s"
-
-#~ msgid "Could not extract author email from %s"
-#~ msgstr "Kunde inte hämta författarens e-postadress från %s"
-
-#~ msgid "Could not extract author time from %s"
-#~ msgstr "Kunde inte hämta författartid från %s"
-
-#~ msgid "No author information found in %s"
-#~ msgstr "Hittade ingen författarinformation i %s"
-
-#~ msgid "cherry-pick --ff cannot be used with --signoff"
-#~ msgstr "cherry-pick --ff kan inte användas med --signoff"
-
-#~ msgid "cherry-pick --ff cannot be used with --no-commit"
-#~ msgstr "cherry-pick --ff kan inte användas med --no-commit"
-
-#~ msgid "cherry-pick --ff cannot be used with -x"
-#~ msgstr "cherry-pick --ff kan inte användas med -x"
-
-#~ msgid "cherry-pick --ff cannot be used with --edit"
-#~ msgstr "cherry-pick --ff kan inte användas med --edit"
-
-#~ msgid "committer info too long."
-#~ msgstr "incheckarinformation för lång."
-
-#~ msgid ""
-#~ "\n"
-#~ "#\n"
-#~ "# Write a tag message\n"
-#~ "#\n"
-#~ msgstr ""
-#~ "\n"
-#~ "#\n"
-#~ "# Skriv ett taggmeddelande\n"
-#~ "#\n"
-
-#~ msgid "signing key value too long (%.10s...)"
-#~ msgstr "signeringsnyckelvärdet för långt (%.10s...)"
index 7d9d05d6cb0c61b6cc6f6f274faf60a9479eab42..dd2d2a73193c1bcb47d53f713c426ef4f0a0233f 100644 (file)
--- a/po/vi.po
+++ b/po/vi.po
@@ -1,15 +1,14 @@
 # Vietnamese translation for GIT-CORE.
-# Copyright (C) 2012-2013 Trần Ngọc Quân.
 # This file is distributed under the same license as the git-core package.
 # First translated by Trần Ngọc Quân <vnwildman@gmail.com>, 2012-2013.
 # Nguyễn Thái Ngọc Duy <pclouds@gmail.com>, 2012.
 #
 msgid ""
 msgstr ""
-"Project-Id-Version: git-v1.8.2-rc2-4-g77995\n"
+"Project-Id-Version: git-v1.8.4-rc1-22-gb8ecf23\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2013-03-05 12:36+0800\n"
-"PO-Revision-Date: 2013-03-06 13:55+0700\n"
+"POT-Creation-Date: 2013-08-06 14:10+0800\n"
+"PO-Revision-Date: 2013-08-06 14:28+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"
@@ -20,8 +19,9 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=1;\n"
 "X-Poedit-SourceCharset: UTF-8\n"
 "X-Poedit-Basepath: ../\n"
+"X-Generator: Poedit 1.5.5\n"
 
-#: advice.c:49
+#: advice.c:57
 #, c-format
 msgid "hint: %.*s\n"
 msgstr "gợi ý: %.*s\n"
@@ -30,7 +30,7 @@ msgstr "gợi ý: %.*s\n"
 #. * Message used both when 'git commit' fails and when
 #. * other commands doing a merge do.
 #.
-#: advice.c:79
+#: advice.c:87
 msgid ""
 "Fix them up in the work tree,\n"
 "and then use 'git add/rm <file>' as\n"
@@ -38,9 +38,9 @@ msgid ""
 "or use 'git commit -a'."
 msgstr ""
 "Sửa chúng trong cây làm việc,\n"
-"và sau đó dùng lệnh “git add/rm <tập-tin>” dành riêng\n"
+"và sau đó dùng lệnh \"git add/rm <tập-tin>\" dành riêng\n"
 "cho việc đánh dấu tập tin cần giải quyết và tạo lần chuyển giao,\n"
-"hoặc là dùng lệnh “git commit -a”."
+"hoặc là dùng lệnh \"git commit -a\"."
 
 #: archive.c:10
 msgid "git archive [options] <tree-ish> [<path>...]"
@@ -69,7 +69,7 @@ msgstr "fmt"
 msgid "archive format"
 msgstr "định dạng lưu trữ"
 
-#: archive.c:324 builtin/log.c:1115
+#: archive.c:324 builtin/log.c:1173
 msgid "prefix"
 msgstr "tiền tố"
 
@@ -77,15 +77,15 @@ msgstr "tiền tố"
 msgid "prepend prefix to each pathname in the archive"
 msgstr "nối thêm tiền tố vào từng đường dẫn tập tin trong kho lưu"
 
-#: archive.c:326 builtin/archive.c:91 builtin/blame.c:2366
-#: builtin/blame.c:2367 builtin/config.c:55 builtin/fast-export.c:653
-#: builtin/fast-export.c:655 builtin/grep.c:715 builtin/hash-object.c:77
-#: builtin/ls-files.c:497 builtin/ls-files.c:500 builtin/notes.c:536
-#: builtin/notes.c:693 builtin/read-tree.c:107 parse-options.h:149
+#: archive.c:326 builtin/archive.c:88 builtin/blame.c:2292
+#: builtin/blame.c:2293 builtin/config.c:56 builtin/fast-export.c:673
+#: builtin/fast-export.c:675 builtin/grep.c:714 builtin/hash-object.c:77
+#: builtin/ls-files.c:496 builtin/ls-files.c:499 builtin/notes.c:408
+#: builtin/notes.c:565 builtin/read-tree.c:108 parse-options.h:152
 msgid "file"
 msgstr "tập-tin"
 
-#: archive.c:327 builtin/archive.c:92
+#: archive.c:327 builtin/archive.c:89
 msgid "write the archive to this file"
 msgstr "ghi kho lưu vào tập tin này"
 
@@ -95,7 +95,7 @@ msgstr "đọc .gitattributes trong thư mục làm việc"
 
 #: archive.c:330
 msgid "report archived files on stderr"
-msgstr "liệt kê các tập tin được lưu trữ vào stderr (đầu ra lỗi chuẩn)"
+msgstr "liệt kê các tập tin được lưu trữ vào stderr (đầu ra lỗi tiêu chuẩn)"
 
 #: archive.c:331
 msgid "store only"
@@ -113,19 +113,19 @@ msgstr "nén nhỏ hơn"
 msgid "list supported archive formats"
 msgstr "liệt kê các kiểu nén được hỗ trợ"
 
-#: archive.c:345 builtin/archive.c:93 builtin/clone.c:85
+#: archive.c:345 builtin/archive.c:90 builtin/clone.c:85
 msgid "repo"
 msgstr "kho"
 
-#: archive.c:346 builtin/archive.c:94
+#: archive.c:346 builtin/archive.c:91
 msgid "retrieve the archive from remote repository <repo>"
 msgstr "nhận kho lưu từ kho chứa <kho> trên máy chủ"
 
-#: archive.c:347 builtin/archive.c:95 builtin/notes.c:615
+#: archive.c:347 builtin/archive.c:92 builtin/notes.c:487
 msgid "command"
 msgstr "lệnh"
 
-#: archive.c:348 builtin/archive.c:96
+#: archive.c:348 builtin/archive.c:93
 msgid "path to the remote git-upload-archive command"
 msgstr "đường dẫn đến lệnh git-upload-pack trên máy chủ"
 
@@ -137,6 +137,132 @@ msgstr ""
 "Các mẫu dạng phủ định bị cấm dùng cho các thuộc tính của git\n"
 "Dùng “\\!” cho các chuỗi văn bản có dấu chấm than dẫn đầu."
 
+#: branch.c:60
+#, c-format
+msgid "Not setting branch %s as its own upstream."
+msgstr "Chưa cài đặt nhánh %s như là thượng nguồn (upstream) của nó."
+
+#: branch.c:82
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s by rebasing."
+msgstr ""
+"Nhánh %s cài đặt để theo dõi vết nhánh máy chủ %s từ %s bằng cách rebase."
+
+#: branch.c:83
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s."
+msgstr "Nhánh %s cài đặt để theo vết nhánh máy chủ %s từ %s."
+
+#: branch.c:87
+#, c-format
+msgid "Branch %s set up to track local branch %s by rebasing."
+msgstr "Nhánh %s cài đặt để theo vết nhánh nội bộ %s bằng cách rebase."
+
+#: branch.c:88
+#, c-format
+msgid "Branch %s set up to track local branch %s."
+msgstr "Nhánh %s cài đặt để theo vết nhánh nội bộ %s."
+
+#: branch.c:92
+#, c-format
+msgid "Branch %s set up to track remote ref %s by rebasing."
+msgstr "Nhánh %s cài đặt để theo vết nhánh máy chủ %s bằng cách rebase."
+
+#: branch.c:93
+#, c-format
+msgid "Branch %s set up to track remote ref %s."
+msgstr "Nhánh %s cài đặt để theo vết tham chiếu máy chủ %s."
+
+#: branch.c:97
+#, c-format
+msgid "Branch %s set up to track local ref %s by rebasing."
+msgstr ""
+"Nhánh %s cài đặt để theo dõi vết tham chiếu nội bộ %s bằng cách rebase."
+
+#: branch.c:98
+#, c-format
+msgid "Branch %s set up to track local ref %s."
+msgstr "Nhánh %s cài đặt để theo dõi tham chiếu nội bộ %s."
+
+#: branch.c:118
+#, c-format
+msgid "Tracking not set up: name too long: %s"
+msgstr "Theo vết chưa được cài đặt: tên quá dài: %s"
+
+#: branch.c:137
+#, c-format
+msgid "Not tracking: ambiguous information for ref %s"
+msgstr "Không theo vết: thông tin chưa rõ ràng cho tham chiếu %s"
+
+#: branch.c:182
+#, c-format
+msgid "'%s' is not a valid branch name."
+msgstr "“%s” không phải là một tên nhánh hợp lệ."
+
+#: branch.c:187
+#, c-format
+msgid "A branch named '%s' already exists."
+msgstr "Đã có nhánh mang tên “%s”."
+
+#: branch.c:195
+msgid "Cannot force update the current branch."
+msgstr "Không thể ép buộc cập nhật nhánh hiện hành."
+
+#: branch.c:216
+#, c-format
+msgid "Cannot setup tracking information; starting point '%s' is not a branch."
+msgstr ""
+"Không thể cài đặt thông tin theo vết; điểm bắt đầu “%s” không phải là một "
+"nhánh."
+
+#: branch.c:218
+#, c-format
+msgid "the requested upstream branch '%s' does not exist"
+msgstr "nhánh dòng ngược đã yêu cầu “%s” chưa sẵn có"
+
+#: branch.c:220
+msgid ""
+"\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push."
+msgstr ""
+"\n"
+"Nếu bạn có ý định trên cơ sở công việc của bạn trên nhánh dòng ngược\n"
+"(upstream) cái mà đã sẵn có trên máy chủ, bạn cần chạy\n"
+"lệnh \"git fetch\" để lấy nó về.\n"
+"\n"
+"Nếu bạn có ý định push lênh một nhánh nội bộ mới cái mà\n"
+"sẽ theo dõi bản đối chiếu máy chủ của nó, bạn cần dùng lệnh\n"
+"\"git push -u\" để đặt cấu hình dòng ngược bạn muốn push."
+
+#: branch.c:265
+#, c-format
+msgid "Not a valid object name: '%s'."
+msgstr "Không phải tên đối tượng hợp lệ: “%s”."
+
+#: branch.c:285
+#, c-format
+msgid "Ambiguous object name: '%s'."
+msgstr "Tên đối tượng chưa rõ ràng: “%s”."
+
+#: branch.c:290
+#, c-format
+msgid "Not a valid branch point: '%s'."
+msgstr "Nhánh không hợp lệ: “%s”."
+
+#: branch.c:296
+msgid "Failed to lock ref for update"
+msgstr "Gặp lỗi khi khóa tham chiếu (ref) để cập nhật"
+
+#: branch.c:314
+msgid "Failed to write ref"
+msgstr "Gặp lỗi khi ghi tham chiếu (ref)"
+
 #: bundle.c:36
 #, c-format
 msgid "'%s' does not look like a v2 bundle file"
@@ -147,7 +273,7 @@ msgstr "“%s” không giống như tập tin v2 bundle (định dạng dump c
 msgid "unrecognized header: %s%s (%d)"
 msgstr "phần đầu (header) không được thừa nhận: %s%s (%d)"
 
-#: bundle.c:89 builtin/commit.c:674
+#: bundle.c:89 builtin/commit.c:697
 #, c-format
 msgid "could not open '%s'"
 msgstr "không thể mở “%s”"
@@ -156,35 +282,35 @@ msgstr "không thể mở “%s”"
 msgid "Repository lacks these prerequisite commits:"
 msgstr "Kho chứa thiếu những lần chuyển giao (commit) cần trước hết này:"
 
-#: bundle.c:164 sequencer.c:566 sequencer.c:998 builtin/log.c:299
-#: builtin/log.c:751 builtin/log.c:1358 builtin/log.c:1574 builtin/merge.c:347
-#: builtin/shortlog.c:157
+#: bundle.c:164 sequencer.c:661 sequencer.c:1111 builtin/log.c:331
+#: builtin/log.c:801 builtin/log.c:1399 builtin/log.c:1625 builtin/merge.c:364
+#: builtin/shortlog.c:156
 msgid "revision walk setup failed"
-msgstr "cài đặt việc di chuyển qua các điểm xét lại gặp lỗi"
+msgstr "cài đặt việc di chuyển qua các điểm xét duyệt gặp lỗi"
 
 #: bundle.c:186
 #, c-format
-msgid "The bundle contains %d ref"
-msgid_plural "The bundle contains %d refs"
-msgstr[0] "Bundle chứa %d tham chiếu (refs)"
-msgstr[1] "Bundle chứa %d tham chiếu (refs)"
+msgid "The bundle contains this ref:"
+msgid_plural "The bundle contains these %d refs:"
+msgstr[0] "Bundle chứa tham chiếu (ref) này:"
+msgstr[1] "Bundle chứa %d tham chiếu (ref):"
 
-#: bundle.c:192
+#: bundle.c:193
 msgid "The bundle records a complete history."
 msgstr "Lệnh bundle ghi lại toàn bộ lịch sử."
 
 #: bundle.c:195
 #, c-format
-msgid "The bundle requires this ref"
-msgid_plural "The bundle requires these %d refs"
-msgstr[0] "Lệnh bundle yêu cầu tham chiếu (refs) này"
-msgstr[1] "Lệnh bundle yêu cầu %d tham chiếu (refs) này"
+msgid "The bundle requires this ref:"
+msgid_plural "The bundle requires these %d refs:"
+msgstr[0] "Lệnh bundle yêu cầu tham chiếu này:"
+msgstr[1] "Lệnh bundle yêu cầu %d tham chiếu (refs) này:"
 
 #: bundle.c:294
 msgid "rev-list died"
 msgstr "rev-list đã chết"
 
-#: bundle.c:300 builtin/log.c:1254 builtin/shortlog.c:260
+#: bundle.c:300 builtin/log.c:1310 builtin/shortlog.c:259
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "đối số không được thừa nhận: %s"
@@ -215,12 +341,12 @@ msgstr "không thể tạo “%s”"
 msgid "index-pack died"
 msgstr "index-pack đã chết"
 
-#: commit.c:50
+#: commit.c:53
 #, c-format
 msgid "could not parse %s"
-msgstr "không thể phân tích %s"
+msgstr "không thể phân tích cú pháp %s"
 
-#: commit.c:52
+#: commit.c:55
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s không phải là một lần commit!"
@@ -229,16 +355,16 @@ msgstr "%s %s không phải là một lần commit!"
 msgid "memory exhausted"
 msgstr "hết bộ nhớ"
 
-#: connected.c:39
+#: connected.c:60
 msgid "Could not run 'git rev-list'"
 msgstr "Không thể chạy “git rev-list”"
 
-#: connected.c:48
+#: connected.c:80
 #, c-format
 msgid "failed write to rev-list: %s"
 msgstr "gặp lỗi khi ghi vào rev-list: %s"
 
-#: connected.c:56
+#: connected.c:88
 #, c-format
 msgid "failed to close rev-list's stdin: %s"
 msgstr "gặp lỗi khi đóng đầu vào chuẩn stdin của rev-list: %s"
@@ -334,7 +460,7 @@ msgstr ""
 "Tìm thấy các lỗi trong biến cấu hình “diff.dirstat”:\n"
 "%s"
 
-#: diff.c:3468
+#: diff.c:3487
 #, c-format
 msgid ""
 "Failed to parse --dirstat/-X option parameter:\n"
@@ -343,12 +469,12 @@ msgstr ""
 "Gặp lỗi khi phân tích đối số tùy chọn --dirstat/-X:\n"
 "%s"
 
-#: diff.c:3482
+#: diff.c:3501
 #, c-format
 msgid "Failed to parse --submodule option parameter: '%s'"
 msgstr "Gặp lỗi khi phân tích đối số tùy chọn --submodule: “%s”"
 
-#: gpg-interface.c:59 gpg-interface.c:127
+#: gpg-interface.c:59 gpg-interface.c:131
 msgid "could not run gpg."
 msgstr "không thể chạy gpg."
 
@@ -360,45 +486,45 @@ msgstr "gpg đã không chấp nhận dữ liệu"
 msgid "gpg failed to sign the data"
 msgstr "gpg gặp lỗi khi ký dữ liệu"
 
-#: gpg-interface.c:112
+#: gpg-interface.c:115
 #, c-format
 msgid "could not create temporary file '%s': %s"
 msgstr "không thể tạo tập tin tạm thời “%s”: %s"
 
-#: gpg-interface.c:115
+#: gpg-interface.c:118
 #, c-format
 msgid "failed writing detached signature to '%s': %s"
 msgstr "gặp lỗi khi ghi chữ ký đính kèm vào “%s”: %s"
 
-#: grep.c:1622
+#: grep.c:1623
 #, c-format
 msgid "'%s': unable to read %s"
 msgstr "“%s”: không thể đọc %s"
 
-#: grep.c:1639
+#: grep.c:1640
 #, c-format
 msgid "'%s': %s"
 msgstr "“%s”: %s"
 
-#: grep.c:1650
+#: grep.c:1651
 #, c-format
 msgid "'%s': short read %s"
 msgstr "“%s”: đọc ngắn %s"
 
-#: help.c:212
+#: help.c:210
 #, c-format
 msgid "available git commands in '%s'"
-msgstr "các lệnh git sẵn sàng để dùng trong “%s”"
+msgstr "các lệnh git sẵn có trong thư mục “%s”:"
 
-#: help.c:219
+#: help.c:217
 msgid "git commands available from elsewhere on your $PATH"
 msgstr "các lệnh git sẵn sàng để dùng từ một nơi khác trong $PATH của bạn"
 
-#: help.c:235
+#: help.c:233
 msgid "The most commonly used git commands are:"
 msgstr "Những lệnh git hay được sử dụng nhất là:"
 
-#: help.c:292
+#: help.c:290
 #, c-format
 msgid ""
 "'%s' appears to be a git command, but we were not\n"
@@ -407,11 +533,11 @@ msgstr ""
 "“%s” trông như là một lệnh git, nhưng chúng tôi không\n"
 "thể thực thi nó. Có lẽ là lệnh git-%s đã bị hỏng?"
 
-#: help.c:349
+#: help.c:347
 msgid "Uh oh. Your system reports no Git commands at all."
 msgstr "Ối chà. Hệ thống của bạn báo rằng chẳng có lệnh Git nào cả."
 
-#: help.c:371
+#: help.c:369
 #, c-format
 msgid ""
 "WARNING: You called a Git command named '%s', which does not exist.\n"
@@ -420,17 +546,17 @@ msgstr ""
 "CẢNH BÁO: Bạn đã gọi lệnh Git có tên “%s”, mà nó lại không có sẵn.\n"
 "Giả định rằng ý bạn là “%s”"
 
-#: help.c:376
+#: help.c:374
 #, c-format
 msgid "in %0.1f seconds automatically..."
 msgstr "trong %0.1f giây một cách tự động..."
 
-#: help.c:383
+#: help.c:381
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
 msgstr "git: “%s” không phải là một lệnh của git. Xem “git --help”."
 
-#: help.c:387
+#: help.c:385 help.c:444
 msgid ""
 "\n"
 "Did you mean this?"
@@ -444,12 +570,17 @@ msgstr[1] ""
 "\n"
 "Có phải ý bạn là một trong số những cái này không?"
 
+#: help.c:440
+#, c-format
+msgid "%s: %s - %s"
+msgstr "%s: %s - %s"
+
 #: merge.c:56
 msgid "failed to read the cache"
 msgstr "gặp lỗi khi đọc bộ nhớ tạm"
 
-#: merge.c:110 builtin/checkout.c:333 builtin/checkout.c:534
-#: builtin/clone.c:586
+#: merge.c:110 builtin/checkout.c:365 builtin/checkout.c:566
+#: builtin/clone.c:656
 msgid "unable to write new index file"
 msgstr "không thể ghi tập tin lưu bảng mục lục mới"
 
@@ -463,70 +594,70 @@ msgstr "(commit sai)\n"
 msgid "addinfo_cache failed for path '%s'"
 msgstr "addinfo_cache gặp lỗi đối với đường dẫn “%s”"
 
-#: merge-recursive.c:268
+#: merge-recursive.c:269
 msgid "error building trees"
 msgstr "gặp lỗi khi xây dựng cây"
 
-#: merge-recursive.c:672
+#: merge-recursive.c:673
 #, c-format
 msgid "failed to create path '%s'%s"
 msgstr "gặp lỗi khi tạo đường dẫn “%s”%s"
 
-#: merge-recursive.c:683
+#: merge-recursive.c:684
 #, c-format
 msgid "Removing %s to make room for subdirectory\n"
 msgstr "Gỡ bỏ %s để tạo chỗ (room) cho thư mục con\n"
 
 #. something else exists
 #. .. but not some other error (who really cares what?)
-#: merge-recursive.c:697 merge-recursive.c:718
+#: merge-recursive.c:698 merge-recursive.c:719
 msgid ": perhaps a D/F conflict?"
 msgstr ": có lẽ là một xung đột D/F?"
 
-#: merge-recursive.c:708
+#: merge-recursive.c:709
 #, c-format
 msgid "refusing to lose untracked file at '%s'"
 msgstr "từ chối đóng tập tin không được theo vết tại “%s”"
 
-#: merge-recursive.c:748
+#: merge-recursive.c:749
 #, c-format
 msgid "cannot read object %s '%s'"
 msgstr "không thể đọc đối tượng %s “%s”"
 
-#: merge-recursive.c:750
+#: merge-recursive.c:751
 #, c-format
 msgid "blob expected for %s '%s'"
 msgstr "đối tượng blob được mong đợi cho %s “%s”"
 
-#: merge-recursive.c:773 builtin/clone.c:302
+#: merge-recursive.c:774 builtin/clone.c:312
 #, c-format
 msgid "failed to open '%s'"
 msgstr "gặp lỗi khi mở “%s”"
 
-#: merge-recursive.c:781
+#: merge-recursive.c:782
 #, c-format
 msgid "failed to symlink '%s'"
 msgstr "gặp lỗi khi tạo liên kết tượng trưng (symlink) “%s”"
 
-#: merge-recursive.c:784
+#: merge-recursive.c:785
 #, c-format
 msgid "do not know what to do with %06o %s '%s'"
 msgstr "không hiểu phải làm gì với %06o %s “%s”"
 
-#: merge-recursive.c:922
+#: merge-recursive.c:923
 msgid "Failed to execute internal merge"
 msgstr "Gặp lỗi khi thực hiện trộn nội bộ"
 
-#: merge-recursive.c:926
+#: merge-recursive.c:927
 #, c-format
 msgid "Unable to add %s to database"
 msgstr "Không thể thêm %s vào cơ sở dữ liệu"
 
-#: merge-recursive.c:942
+#: merge-recursive.c:943
 msgid "unsupported object type in the tree"
 msgstr "kiểu đối tượng không được hỗ trợ trong cây (tree)"
 
-#: merge-recursive.c:1021 merge-recursive.c:1035
+#: merge-recursive.c:1022 merge-recursive.c:1036
 #, c-format
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
@@ -535,7 +666,7 @@ msgstr ""
 "XUNG ĐỘT (%s/xóa): %s bị xóa trong %s và %s trong %s. Phiên bản %s của %s "
 "còn lại trong cây (tree)."
 
-#: merge-recursive.c:1027 merge-recursive.c:1040
+#: merge-recursive.c:1028 merge-recursive.c:1041
 #, c-format
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
@@ -544,20 +675,20 @@ msgstr ""
 "XUNG ĐỘT (%s/xóa): %s bị xóa trong %s và %s trong %s. Phiên bản %s của %s "
 "còn lại trong cây (tree) tại %s."
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "rename"
 msgstr "đổi tên"
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "renamed"
 msgstr "đã đổi tên"
 
-#: merge-recursive.c:1137
+#: merge-recursive.c:1138
 #, c-format
 msgid "%s is a directory in %s adding as %s instead"
 msgstr "%s là một thư mục trong %s thay vào đó thêm vào như là %s"
 
-#: merge-recursive.c:1159
+#: merge-recursive.c:1160
 #, c-format
 msgid ""
 "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
@@ -566,149 +697,177 @@ msgstr ""
 "XUNG ĐỘT (đổi-tên/đổi-tên): Đổi tên \"%s\"->\"%s\" trong nhánh \"%s\" đổi "
 "tên \"%s\"->\"%s\" trong \"%s\"%s"
 
-#: merge-recursive.c:1164
+#: merge-recursive.c:1165
 msgid " (left unresolved)"
 msgstr " (cần giải quyết)"
 
-#: merge-recursive.c:1218
+#: merge-recursive.c:1219
 #, c-format
 msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
 msgstr ""
 "XUNG ĐỘT (đổi-tên/đổi-tên): Đổi tên %s->%s trong %s. Đổi tên %s->%s trong %s"
 
-#: merge-recursive.c:1248
+#: merge-recursive.c:1249
 #, c-format
 msgid "Renaming %s to %s and %s to %s instead"
 msgstr "Đang đổi tên %s thành %s thay vì %s thành %s"
 
-#: merge-recursive.c:1447
+#: merge-recursive.c:1448
 #, c-format
 msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s"
 msgstr ""
 "XUNG ĐỘT (đổi-tên/thêm): Đổi tên %s->%s trong %s. %s được thêm vào trong %s"
 
-#: merge-recursive.c:1457
+#: merge-recursive.c:1458
 #, c-format
 msgid "Adding merged %s"
 msgstr "Thêm hòa trộn %s"
 
-#: merge-recursive.c:1462 merge-recursive.c:1660
+#: merge-recursive.c:1463 merge-recursive.c:1661
 #, c-format
 msgid "Adding as %s instead"
 msgstr "Thay vào đó thêm vào %s"
 
-#: merge-recursive.c:1513
+#: merge-recursive.c:1514
 #, c-format
 msgid "cannot read object %s"
 msgstr "không thể đọc đối tượng %s"
 
-#: merge-recursive.c:1516
+#: merge-recursive.c:1517
 #, c-format
 msgid "object %s is not a blob"
 msgstr "đối tượng %s không phải là một blob"
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modify"
 msgstr "sửa đổi"
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modified"
 msgstr "đã sửa"
 
-#: merge-recursive.c:1574
+#: merge-recursive.c:1575
 msgid "content"
 msgstr "nội dung"
 
-#: merge-recursive.c:1581
+#: merge-recursive.c:1582
 msgid "add/add"
 msgstr "thêm/thêm"
 
-#: merge-recursive.c:1615
+#: merge-recursive.c:1616
 #, c-format
 msgid "Skipped %s (merged same as existing)"
 msgstr "Đã bỏ qua %s (đã có sẵn lần hòa trộn này)"
 
-#: merge-recursive.c:1629
+#: merge-recursive.c:1630
 #, c-format
 msgid "Auto-merging %s"
 msgstr "Tự-động-hòa-trộn %s"
 
-#: merge-recursive.c:1633 git-submodule.sh:942
+#: merge-recursive.c:1634 git-submodule.sh:1118
 msgid "submodule"
 msgstr "mô-đun-con"
 
-#: merge-recursive.c:1634
+#: merge-recursive.c:1635
 #, c-format
 msgid "CONFLICT (%s): Merge conflict in %s"
 msgstr "XUNG ĐỘT (%s): Xung đột hòa trộn trong %s"
 
-#: merge-recursive.c:1724
+#: merge-recursive.c:1725
 #, c-format
 msgid "Removing %s"
 msgstr "Đang xóa %s"
 
-#: merge-recursive.c:1749
+#: merge-recursive.c:1750
 msgid "file/directory"
 msgstr "tập-tin/thư-mục"
 
-#: merge-recursive.c:1755
+#: merge-recursive.c:1756
 msgid "directory/file"
 msgstr "thư-mục/tập tin"
 
-#: merge-recursive.c:1760
+#: merge-recursive.c:1761
 #, c-format
 msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
 msgstr ""
 "XUNG ĐỘT (%s): Ở đây không có thư mục nào có tên %s trong %s. Thêm %s như là "
 "%s"
 
-#: merge-recursive.c:1770
+#: merge-recursive.c:1771
 #, c-format
 msgid "Adding %s"
 msgstr "Thêm \"%s\""
 
-#: merge-recursive.c:1787
+#: merge-recursive.c:1788
 msgid "Fatal merge failure, shouldn't happen."
 msgstr "Việc hòa trộn hỏng nghiêm trọng, không nên để xảy ra."
 
-#: merge-recursive.c:1806
+#: merge-recursive.c:1807
 msgid "Already up-to-date!"
 msgstr "Đã cập nhật rồi!"
 
-#: merge-recursive.c:1815
+#: merge-recursive.c:1816
 #, c-format
 msgid "merging of trees %s and %s failed"
 msgstr "hòa trộn cây (tree) %s và %s gặp lỗi"
 
-#: merge-recursive.c:1845
+#: merge-recursive.c:1846
 #, c-format
 msgid "Unprocessed path??? %s"
 msgstr "Đường dẫn chưa được xử lý??? %s"
 
-#: merge-recursive.c:1890
+#: merge-recursive.c:1891
 msgid "Merging:"
 msgstr "Đang trộn:"
 
-#: merge-recursive.c:1903
+#: merge-recursive.c:1904
 #, c-format
 msgid "found %u common ancestor:"
 msgid_plural "found %u common ancestors:"
 msgstr[0] "tìm thấy %u tổ tiên chung:"
 msgstr[1] "tìm thấy %u tổ tiên chung:"
 
-#: merge-recursive.c:1940
+#: merge-recursive.c:1941
 msgid "merge returned no commit"
 msgstr "hòa trộn không trả về lần chuyển giao (commit) nào"
 
-#: merge-recursive.c:1997
+#: merge-recursive.c:1998
 #, c-format
 msgid "Could not parse object '%s'"
 msgstr "Không thể phân tích đối tượng “%s”"
 
-#: merge-recursive.c:2009 builtin/merge.c:643
+#: merge-recursive.c:2010 builtin/merge.c:672
 msgid "Unable to write index."
 msgstr "Không thể ghi bảng mục lục"
 
+#: notes-utils.c:40
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+"Không thể chuyển giao (commit) chưa được khởi tạo hoặc không được tham chiếu "
+"cây ghi chú"
+
+#: notes-utils.c:81
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Giá trị notes.rewriteMode sai: “%s”"
+
+#: notes-utils.c:91
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "Từ chối ghi đè ghi chú trong %s (nằm ngoài của refs/notes/)"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: notes-utils.c:118
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Giá trị %s sai: “%s”"
+
+#: object.c:204
+#, c-format
+msgid "unable to parse object: %s"
+msgstr "không thể phân tích đối tượng: “%s”"
+
 #: parse-options.c:489
 msgid "..."
 msgstr "..."
@@ -744,18 +903,18 @@ msgstr "Đường dẫn “%s” thì ở trong mô-đun-con “%.*s”"
 msgid "'%s' is beyond a symbolic link"
 msgstr "“%s” nằm ngoài một liên kết tượng trưng"
 
-#: remote.c:1653
+#: remote.c:1788
 #, 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] "Nhánh của bạn là đầu của “%s” bởi %d lần chuyển giao (commit).\n"
 msgstr[1] "Nhánh của bạn là đầu của “%s” bởi %d lần chuyển giao (commit).\n"
 
-#: remote.c:1659
+#: remote.c:1794
 msgid "  (use \"git push\" to publish your local commits)\n"
 msgstr "  (dùng \"git push\" để xuất bản các lần chuyển giao nội bộ của bạn)\n"
 
-#: remote.c:1662
+#: remote.c:1797
 #, c-format
 msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
 msgid_plural ""
@@ -767,11 +926,11 @@ msgstr[1] ""
 "Nhánh của bạn thì ở đằng sau “%s” bởi %d lần chuyển giao (commit), và có thể "
 "được fast-forward.\n"
 
-#: remote.c:1670
+#: remote.c:1805
 msgid "  (use \"git pull\" to update your local branch)\n"
 msgstr "  (dùng \"git pull\" để cập nhật nhánh nội bộ của bạn)\n"
 
-#: remote.c:1673
+#: remote.c:1808
 #, c-format
 msgid ""
 "Your branch and '%s' have diverged,\n"
@@ -787,25 +946,34 @@ msgstr[1] ""
 "Your branch and “%s” have diverged,\n"
 "and have %d and %d different commit each, respectively.\n"
 
-#: remote.c:1683
+#: remote.c:1818
 msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
 msgstr ""
 "  (dùng \"git pull\" để hòa trộn nhánh trên máy chủ vào trong nhánh của "
 "bạn)\n"
 
-#: sequencer.c:123 builtin/merge.c:761 builtin/merge.c:874 builtin/merge.c:984
-#: builtin/merge.c:994
+#: run-command.c:80
+msgid "open /dev/null failed"
+msgstr "gặp lỗi khi mở /dev/null"
+
+#: run-command.c:82
+#, c-format
+msgid "dup2(%d,%d) failed"
+msgstr "dup2(%d,%d) gặp lỗi"
+
+#: sequencer.c:206 builtin/merge.c:790 builtin/merge.c:903
+#: builtin/merge.c:1013 builtin/merge.c:1023
 #, c-format
 msgid "Could not open '%s' for writing"
 msgstr "Không thể mở “%s” để ghi"
 
-#: sequencer.c:125 builtin/merge.c:333 builtin/merge.c:764 builtin/merge.c:986
-#: builtin/merge.c:999
+#: sequencer.c:208 builtin/merge.c:350 builtin/merge.c:793
+#: builtin/merge.c:1015 builtin/merge.c:1028
 #, c-format
 msgid "Could not write to '%s'"
 msgstr "Không thể ghi vào “%s”"
 
-#: sequencer.c:146
+#: sequencer.c:229
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'"
@@ -813,7 +981,7 @@ msgstr ""
 "sau khi giải quyết các xung đột, đánh dấu đường dẫn đã sửa\n"
 "với lệnh “git add <đường_dẫn>” hoặc “git rm <đường_dẫn>”"
 
-#: sequencer.c:149
+#: sequencer.c:232
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'\n"
@@ -823,232 +991,266 @@ msgstr ""
 "với lệnh “git add <đường_dẫn>” hoặc “git rm <đường_dẫn>”\n"
 "và chuyển giao (commit) kết quả bằng lệnh “git commit”"
 
-#: sequencer.c:162 sequencer.c:774 sequencer.c:857
+#: sequencer.c:245 sequencer.c:869 sequencer.c:952
 #, c-format
 msgid "Could not write to %s"
 msgstr "Không thể ghi vào %s"
 
-#: sequencer.c:165
+#: sequencer.c:248
 #, c-format
 msgid "Error wrapping up %s"
 msgstr "Lỗi bao bọc %s"
 
-#: sequencer.c:180
+#: sequencer.c:263
 msgid "Your local changes would be overwritten by cherry-pick."
 msgstr "Các thay đổi nội bộ của bạn có thể bị ghi đè bởi lệnh cherry-pick."
 
-#: sequencer.c:182
+#: sequencer.c:265
 msgid "Your local changes would be overwritten by revert."
 msgstr "Các thay đổi nội bộ của bạn có thể bị ghi đè bởi lệnh revert."
 
-#: sequencer.c:185
+#: sequencer.c:268
 msgid "Commit your changes or stash them to proceed."
 msgstr "Chuyển giao (commit) các thay đổi của bạn hay stash chúng để xử lý."
 
 #. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:236
+#: sequencer.c:324
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr "%s: Không thể ghi tập tin lưu bảng mục lục mới"
 
-#: sequencer.c:267
+#: sequencer.c:355
 msgid "Could not resolve HEAD commit\n"
 msgstr "Không thể phân giải commit (lần chuyển giao) HEAD\n"
 
-#: sequencer.c:288
+#: sequencer.c:377
 msgid "Unable to update cache tree\n"
 msgstr "Không thể cập nhật cây bộ nhớ đệm\n"
 
-#: sequencer.c:333
+#: sequencer.c:422
 #, c-format
 msgid "Could not parse commit %s\n"
 msgstr "Không thể phân tích commit (lần chuyển giao) %s\n"
 
-#: sequencer.c:338
+#: sequencer.c:427
 #, c-format
 msgid "Could not parse parent commit %s\n"
 msgstr "Không thể phân tích commit (lần chuyển giao) cha mẹ %s\n"
 
-#: sequencer.c:404
+#: sequencer.c:493
 msgid "Your index file is unmerged."
 msgstr "Tập tin lưu mục lục của bạn không được hòa trộn."
 
-#: sequencer.c:423
+#: sequencer.c:512
 #, c-format
 msgid "Commit %s is a merge but no -m option was given."
 msgstr ""
 "Lần chuyển giao (commit) %s là một lần hòa trộn nhưng không đưa ra tùy chọn -"
 "m."
 
-#: sequencer.c:431
+#: sequencer.c:520
 #, c-format
 msgid "Commit %s does not have parent %d"
 msgstr "Lần chuyển giao (commit) %s không có cha mẹ %d"
 
-#: sequencer.c:435
+#: sequencer.c:524
 #, c-format
 msgid "Mainline was specified but commit %s is not a merge."
 msgstr ""
-"Luồng chính được chỉ ra nhưng lần chuyển giao (commit) %s không phải là một "
-"lần hòa trộn."
+"Luồng chính đã được chỉ ra nhưng lần chuyển giao (commit) %s không phải là "
+"một lần hòa trộn."
 
 #. TRANSLATORS: The first %s will be "revert" or
 #. "cherry-pick", the second %s a SHA1
-#: sequencer.c:448
+#: sequencer.c:537
 #, c-format
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s: không thể phân tích lần chuyển giao mẹ của %s"
 
-#: sequencer.c:452
+#: sequencer.c:541
 #, c-format
 msgid "Cannot get commit message for %s"
 msgstr "Không thể lấy thông điệp lần chuyển giao (commit) cho %s"
 
-#: sequencer.c:536
+#: sequencer.c:627
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "không thể revert %s... %s"
 
-#: sequencer.c:537
+#: sequencer.c:628
 #, c-format
 msgid "could not apply %s... %s"
 msgstr "không thể apply (áp dụng miếng vá) %s... %s"
 
-#: sequencer.c:569
+#: sequencer.c:664
 msgid "empty commit set passed"
 msgstr "lần chuyển giao (commit) trống rỗng đặt là hợp quy cách"
 
-#: sequencer.c:577
+#: sequencer.c:672
 #, c-format
 msgid "git %s: failed to read the index"
 msgstr "git %s: gặp lỗi đọc bảng mục lục"
 
-#: sequencer.c:582
+#: sequencer.c:677
 #, c-format
 msgid "git %s: failed to refresh the index"
 msgstr "git %s: gặp lỗi khi làm tươi mới bảng mục lục"
 
-#: sequencer.c:640
+#: sequencer.c:735
 #, c-format
 msgid "Cannot %s during a %s"
 msgstr "Không thể %s trong khi %s"
 
-#: sequencer.c:662
+#: sequencer.c:757
 #, c-format
 msgid "Could not parse line %d."
 msgstr "Không phân tích được dòng %d."
 
-#: sequencer.c:667
+#: sequencer.c:762
 msgid "No commits parsed."
 msgstr "Không có lần chuyển giao (commit) nào được phân tích."
 
-#: sequencer.c:680
+#: sequencer.c:775
 #, c-format
 msgid "Could not open %s"
 msgstr "Không thể mở %s"
 
-#: sequencer.c:684
+#: sequencer.c:779
 #, c-format
 msgid "Could not read %s."
 msgstr "Không thể đọc %s."
 
-#: sequencer.c:691
+#: sequencer.c:786
 #, c-format
 msgid "Unusable instruction sheet: %s"
 msgstr "Bảng chỉ thị không thể dùng được: %s"
 
-#: sequencer.c:719
+#: sequencer.c:814
 #, c-format
 msgid "Invalid key: %s"
 msgstr "Khóa không đúng: %s"
 
-#: sequencer.c:722
+#: sequencer.c:817
 #, c-format
 msgid "Invalid value for %s: %s"
 msgstr "Giá trị không hợp lệ %s: %s"
 
-#: sequencer.c:734
+#: sequencer.c:829
 #, c-format
 msgid "Malformed options sheet: %s"
 msgstr "Bảng tùy chọn dị hình: %s"
 
-#: sequencer.c:755
+#: sequencer.c:850
 msgid "a cherry-pick or revert is already in progress"
 msgstr "một thao tác cherry-pick hoặc revert đang được thực hiện"
 
-#: sequencer.c:756
+#: sequencer.c:851
 msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
 msgstr "hãy thử \"git cherry-pick (--continue | --quit | --abort)\""
 
-#: sequencer.c:760
+#: sequencer.c:855
 #, c-format
 msgid "Could not create sequencer directory %s"
 msgstr "Không thể tạo thư mục xếp dãy %s"
 
-#: sequencer.c:776 sequencer.c:861
+#: sequencer.c:871 sequencer.c:956
 #, c-format
 msgid "Error wrapping up %s."
 msgstr "Lỗi bao bọc %s."
 
-#: sequencer.c:795 sequencer.c:929
+#: sequencer.c:890 sequencer.c:1024
 msgid "no cherry-pick or revert in progress"
 msgstr "không cherry-pick hay revert trong tiến trình"
 
-#: sequencer.c:797
+#: sequencer.c:892
 msgid "cannot resolve HEAD"
 msgstr "không thể phân giải HEAD"
 
-#: sequencer.c:799
+#: sequencer.c:894
 msgid "cannot abort from a branch yet to be born"
 msgstr "không thể hủy bỏ từ một nhánh mà nó còn chưa được tạo ra"
 
-#: sequencer.c:821 builtin/apply.c:4056
+#: sequencer.c:916 builtin/apply.c:4061
 #, c-format
 msgid "cannot open %s: %s"
 msgstr "không thể mở %s: %s"
 
-#: sequencer.c:824
+#: sequencer.c:919
 #, c-format
 msgid "cannot read %s: %s"
 msgstr "không thể đọc %s: %s"
 
-#: sequencer.c:825
+#: sequencer.c:920
 msgid "unexpected end of file"
 msgstr "kết thúc tập tin đột xuất"
 
-#: sequencer.c:831
+#: sequencer.c:926
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
 msgstr "tập tin HEAD “pre-cherry-pick” đã lưu “%s” bị hỏng"
 
-#: sequencer.c:854
+#: sequencer.c:949
 #, c-format
 msgid "Could not format %s."
 msgstr "Không thể định dạng %s."
 
-#: sequencer.c:1016
+#: sequencer.c:1092
+#, c-format
+msgid "%s: can't cherry-pick a %s"
+msgstr "%s: không thể cherry-pick một %s"
+
+#: sequencer.c:1095
+#, c-format
+msgid "%s: bad revision"
+msgstr "%s: điểm xét duyệt sai"
+
+#: sequencer.c:1129
 msgid "Can't revert as initial commit"
 msgstr "Không thể revert một lần chuyển giao (commit) khởi tạo"
 
-#: sequencer.c:1017
+#: sequencer.c:1130
 msgid "Can't cherry-pick into empty head"
 msgstr "Không thể cherry-pick vào một đầu (head) trống rỗng"
 
-#: sha1_name.c:1044
+#: sha1_name.c:440
+msgid ""
+"Git normally never creates a ref that ends with 40 hex characters\n"
+"because it will be ignored when you just specify 40-hex. These refs\n"
+"may be created by mistake. For example,\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+"examine these refs and maybe delete them. Turn this message off by\n"
+"running \"git config advice.objectNameWarning false\""
+msgstr ""
+"Git thường không bao giờ tạo tham chiếu mà nó kết thúc với 40 ký tự hex\n"
+"bởi vì nó sẽ bị bỏ qua khi bạn chỉ định 40-hex. Những tham chiếu này\n"
+"có lẽ được tạo ra bởi một sai sót nào đó. Ví dụ,\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"với \"$br\" không hiểu lý do vì sao trống rỗng và một tham chiếu 40-hex được "
+"tạo ra.\n"
+"Xin hãy kiểm tra những tham chiếu này và có thể xóa chúng đi. Tắt lời nhắn "
+"này\n"
+"bằng cách chạy lệnh \"git config advice.objectNameWarning false\""
+
+#: sha1_name.c:1097
 msgid "HEAD does not point to a branch"
 msgstr "HEAD không chỉ đến một nhánh nào cả"
 
-#: sha1_name.c:1047
+#: sha1_name.c:1100
 #, c-format
 msgid "No such branch: '%s'"
 msgstr "Không có nhánh nào như thế: “%s”"
 
-#: sha1_name.c:1049
+#: sha1_name.c:1102
 #, c-format
 msgid "No upstream configured for branch '%s'"
 msgstr "Không có thượng nguồn (upstream) được cấu hình cho nhánh “%s”"
 
-#: sha1_name.c:1052
+#: sha1_name.c:1106
 #, c-format
 msgid "Upstream branch '%s' not stored as a remote-tracking branch"
 msgstr ""
@@ -1060,239 +1262,239 @@ msgstr ""
 msgid "unable to access '%s': %s"
 msgstr "không thể truy cập “%s”: %s"
 
-#: wrapper.c:423
+#: wrapper.c:429
 #, c-format
 msgid "unable to access '%s'"
 msgstr "không thể truy cập “%s”"
 
-#: wrapper.c:434
+#: wrapper.c:440
 #, c-format
 msgid "unable to look up current user in the passwd file: %s"
 msgstr "không tìm thấy người dùng hiện tại trong tập tin passwd: %s"
 
-#: wrapper.c:435
+#: wrapper.c:441
 msgid "no such user"
 msgstr "không có người dùng như vậy"
 
-#: wt-status.c:140
+#: wt-status.c:141
 msgid "Unmerged paths:"
 msgstr "Những đường dẫn chưa được hòa trộn:"
 
-#: wt-status.c:167 wt-status.c:194
+#: wt-status.c:168 wt-status.c:195
 #, c-format
 msgid "  (use \"git reset %s <file>...\" to unstage)"
 msgstr "  (dùng \"git reset %s <tập-tin>...\" để bỏ một stage (trạng thái))"
 
-#: wt-status.c:169 wt-status.c:196
+#: wt-status.c:170 wt-status.c:197
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
 msgstr "  (dùng \"git rm --cached <tập-tin>...\" để bỏ trạng thái (stage))"
 
-#: wt-status.c:173
+#: wt-status.c:174
 msgid "  (use \"git add <file>...\" to mark resolution)"
 msgstr "  (dùng \"git add <tập-tin>...\" để đánh dấu là cần giải quyết)"
 
-#: wt-status.c:175 wt-status.c:179
+#: wt-status.c:176 wt-status.c:180
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
 msgstr ""
 "  (dùng \"git add/rm <tập-tin>...\" như là một cách  thích hợp để đánh dấu "
 "là cần được giải quyết)"
 
-#: wt-status.c:177
+#: wt-status.c:178
 msgid "  (use \"git rm <file>...\" to mark resolution)"
 msgstr "  (dùng \"git rm <tập-tin>...\" để đánh dấu là cần giải quyết)"
 
-#: wt-status.c:188
+#: wt-status.c:189
 msgid "Changes to be committed:"
 msgstr "Những thay đổi sẽ được chuyển giao:"
 
-#: wt-status.c:206
+#: wt-status.c:207
 msgid "Changes not staged for commit:"
 msgstr ""
 "Các thay đổi không được đặt trạng thái (stage) cho lần chuyển giao (commit):"
 
-#: wt-status.c:210
+#: wt-status.c:211
 msgid "  (use \"git add <file>...\" to update what will be committed)"
 msgstr ""
 "  (dùng \"git add <tập-tin>...\" để cập nhật những gì cần chuyển giao "
 "(commit))"
 
-#: wt-status.c:212
+#: wt-status.c:213
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
 msgstr ""
 "  (dùng \"git add/rm <tập-tin>...\" để cập nhật những gì sẽ được chuyển giao)"
 
-#: wt-status.c:213
+#: wt-status.c:214
 msgid ""
 "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
 msgstr ""
 "  (dùng \"git checkout -- <tập-tin>...\" để bỏ thay đổi trong thư mục làm "
 "việc)"
 
-#: wt-status.c:215
+#: wt-status.c:216
 msgid "  (commit or discard the untracked or modified content in submodules)"
 msgstr ""
 "  (chuyển giao (commit) hoặc là loại bỏ các nội dung không-bị-theo-vết hay "
 "đã bị chỉnh sửa trong mô-đun-con)"
 
-#: wt-status.c:227
+#: wt-status.c:228
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
 msgstr ""
 "  (dùng \"git %s <tập-tin>...\" để thêm vào những gì cần chuyển giao "
 "(commit))"
 
-#: wt-status.c:244
+#: wt-status.c:245
 msgid "bug"
 msgstr "lỗi"
 
-#: wt-status.c:249
+#: wt-status.c:250
 msgid "both deleted:"
 msgstr "bị xóa bởi cả hai:"
 
-#: wt-status.c:250
+#: wt-status.c:251
 msgid "added by us:"
 msgstr "được thêm vào bởi chúng ta:"
 
-#: wt-status.c:251
+#: wt-status.c:252
 msgid "deleted by them:"
 msgstr "bị xóa đi bởi họ:"
 
-#: wt-status.c:252
+#: wt-status.c:253
 msgid "added by them:"
 msgstr "được thêm vào bởi họ:"
 
-#: wt-status.c:253
+#: wt-status.c:254
 msgid "deleted by us:"
 msgstr "bị xóa bởi chúng ta:"
 
-#: wt-status.c:254
+#: wt-status.c:255
 msgid "both added:"
 msgstr "được thêm vào bởi cả hai:"
 
-#: wt-status.c:255
+#: wt-status.c:256
 msgid "both modified:"
 msgstr "bị sửa bởi cả hai:"
 
-#: wt-status.c:285
+#: wt-status.c:286
 msgid "new commits, "
 msgstr "lần chuyển giao (commit) mới, "
 
-#: wt-status.c:287
+#: wt-status.c:288
 msgid "modified content, "
 msgstr "nội dung bị sửa đổi, "
 
-#: wt-status.c:289
+#: wt-status.c:290
 msgid "untracked content, "
 msgstr "nội dung chưa được theo dõi, "
 
-#: wt-status.c:303
+#: wt-status.c:307
 #, c-format
 msgid "new file:   %s"
 msgstr "tập tin mới:   %s"
 
-#: wt-status.c:306
+#: wt-status.c:310
 #, c-format
 msgid "copied:     %s -> %s"
 msgstr "đã sao chép:     %s -> %s"
 
-#: wt-status.c:309
+#: wt-status.c:313
 #, c-format
 msgid "deleted:    %s"
 msgstr "đã xóa:    %s"
 
-#: wt-status.c:312
+#: wt-status.c:316
 #, c-format
 msgid "modified:   %s"
 msgstr "đã sửa đổi:   %s"
 
-#: wt-status.c:315
+#: wt-status.c:319
 #, c-format
 msgid "renamed:    %s -> %s"
 msgstr "đã đổi tên:    %s -> %s"
 
-#: wt-status.c:318
+#: wt-status.c:322
 #, c-format
 msgid "typechange: %s"
 msgstr "đổi-kiểu: %s"
 
-#: wt-status.c:321
+#: wt-status.c:325
 #, c-format
 msgid "unknown:    %s"
 msgstr "không hiểu:    %s"
 
-#: wt-status.c:324
+#: wt-status.c:328
 #, c-format
 msgid "unmerged:   %s"
 msgstr "chưa hòa trộn:   %s"
 
-#: wt-status.c:327
+#: wt-status.c:331
 #, c-format
 msgid "bug: unhandled diff status %c"
 msgstr "lỗi: không lấy được trạng thái lệnh diff %c"
 
-#: wt-status.c:789
+#: wt-status.c:803
 msgid "You have unmerged paths."
 msgstr "Bạn có những đường dẫn chưa được hòa trộn."
 
-#: wt-status.c:792 wt-status.c:944
+#: wt-status.c:806
 msgid "  (fix conflicts and run \"git commit\")"
 msgstr "  (sửa các xung đột sau đó chạy \"git commit\")"
 
-#: wt-status.c:795
+#: wt-status.c:809
 msgid "All conflicts fixed but you are still merging."
 msgstr "Tất cả các xung đột đã được giải quyết nhưng bạn vẫn đang hòa trộn."
 
-#: wt-status.c:798
+#: wt-status.c:812
 msgid "  (use \"git commit\" to conclude merge)"
 msgstr "  (dùng \"git commit\" để hoàn tất việc hòa trộn)"
 
-#: wt-status.c:808
+#: wt-status.c:822
 msgid "You are in the middle of an am session."
 msgstr "Bạn đang ở giữa của một phiên “am”."
 
-#: wt-status.c:811
+#: wt-status.c:825
 msgid "The current patch is empty."
 msgstr "Miếng vá hiện tại bị trống rỗng."
 
-#: wt-status.c:815
-msgid "  (fix conflicts and then run \"git am --resolved\")"
-msgstr "  (sửa các xung đột và sau đó chạy lệnh \"git am --resolved\")"
+#: wt-status.c:829
+msgid "  (fix conflicts and then run \"git am --continue\")"
+msgstr "  (sửa các xung đột và sau đó chạy lệnh \"git am --continue\")"
 
-#: wt-status.c:817
+#: wt-status.c:831
 msgid "  (use \"git am --skip\" to skip this patch)"
 msgstr "  (dùng \"git am --skip\" để bỏ qua miếng vá này)"
 
-#: wt-status.c:819
+#: wt-status.c:833
 msgid "  (use \"git am --abort\" to restore the original branch)"
 msgstr "  (dùng \"git am --abort\" để phục hồi lại nhánh nguyên thủy)"
 
-#: wt-status.c:879 wt-status.c:896
+#: wt-status.c:893 wt-status.c:910
 #, c-format
 msgid "You are currently rebasing branch '%s' on '%s'."
 msgstr "Bạn hiện nay đang thực hiện việc rebase nhánh “%s” trên “%s”."
 
-#: wt-status.c:884 wt-status.c:901
+#: wt-status.c:898 wt-status.c:915
 msgid "You are currently rebasing."
 msgstr "Bạn hiện nay đang thực hiện việc rebase (tái cấu trúc)."
 
-#: wt-status.c:887
+#: wt-status.c:901
 msgid "  (fix conflicts and then run \"git rebase --continue\")"
 msgstr "  (sửa các xung đột và sau đó chạy lệnh \"git rebase --continue\")"
 
-#: wt-status.c:889
+#: wt-status.c:903
 msgid "  (use \"git rebase --skip\" to skip this patch)"
 msgstr "  (dùng \"git rebase --skip\" để bỏ qua lần vá này)"
 
-#: wt-status.c:891
+#: wt-status.c:905
 msgid "  (use \"git rebase --abort\" to check out the original branch)"
 msgstr "  (dùng \"git rebase --abort\" để check-out nhánh nguyên thủy)"
 
-#: wt-status.c:904
+#: wt-status.c:918
 msgid "  (all conflicts fixed: run \"git rebase --continue\")"
 msgstr ""
 "  (khi tất cả các xung đột đã sửa xong: chạy lệnh \"git rebase --continue\")"
 
-#: wt-status.c:908
+#: wt-status.c:922
 #, c-format
 msgid ""
 "You are currently splitting a commit while rebasing branch '%s' on '%s'."
@@ -1300,110 +1502,163 @@ msgstr ""
 "Bạn hiện nay đang thực hiện việc chia tách một lần chuyển giao (commit) "
 "trong khi đang rebase nhánh “%s” trên “%s”."
 
-#: wt-status.c:913
+#: wt-status.c:927
 msgid "You are currently splitting a commit during a rebase."
 msgstr ""
 "Bạn hiện tại đang cắt đôi một lần chuyển giao trong khi đang thực hiện việc "
 "rebase."
 
-#: wt-status.c:916
+#: wt-status.c:930
 msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
 msgstr ""
 "  (Một khi thư mục làm việc của bạn đã gọn gàng, chạy \"git rebase --continue"
 "\")"
 
-#: wt-status.c:920
+#: wt-status.c:934
 #, c-format
 msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
 msgstr ""
 "Bạn hiện nay đang thực hiện việc sửa chữa một lần chuyển giao (commit) trong "
 "khi đang rebase nhánh “%s” trên “%s”."
 
-#: wt-status.c:925
+#: wt-status.c:939
 msgid "You are currently editing a commit during a rebase."
 msgstr "Bạn hiện đang sửa một lần chuyển giao trong khi bạn thực hiện rebase."
 
-#: wt-status.c:928
+#: wt-status.c:942
 msgid "  (use \"git commit --amend\" to amend the current commit)"
 msgstr ""
 "  (dùng \"git commit --amend\" để tu bổ lần chuyển giao (commit) hiện tại)"
 
-#: wt-status.c:930
+#: wt-status.c:944
 msgid ""
 "  (use \"git rebase --continue\" once you are satisfied with your changes)"
 msgstr ""
 "  (dùng \"git rebase --continue\" một khi bạn cảm thấy hài lòng về những "
 "thay đổi của mình)"
 
-#: wt-status.c:940
+#: wt-status.c:954
 msgid "You are currently cherry-picking."
 msgstr "Bạn hiện nay đang thực hiện việc cherry-pick."
 
-#: wt-status.c:947
-msgid "  (all conflicts fixed: run \"git commit\")"
-msgstr "  (khi tất cả các xung đột đã sửa xong: chạy lệnh \"git commit\")"
-
 #: wt-status.c:958
+msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
+msgstr ""
+"  (sửa các xung đột và sau đó chạy lệnh \"git cherry-pick --continue\")"
+
+#: wt-status.c:961
+msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
+msgstr ""
+"  (khi tất cả các xung đột đã sửa xong: chạy lệnh \"git cherry-pick --"
+"continue\")"
+
+#: wt-status.c:963
+msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
+msgstr "  (dùng \"git cherry-pick --abort\" để hủy bỏ thao tác cherry-pick)"
+
+#: wt-status.c:972
+#, c-format
+msgid "You are currently reverting commit %s."
+msgstr "Bạn hiện nay đang thực hiện thao tác revert lần chuyển giao “%s”."
+
+#: wt-status.c:977
+msgid "  (fix conflicts and run \"git revert --continue\")"
+msgstr "  (sửa các xung đột và sau đó chạy lệnh \"git revert --continue\")"
+
+#: wt-status.c:980
+msgid "  (all conflicts fixed: run \"git revert --continue\")"
+msgstr ""
+"  (khi tất cả các xung đột đã sửa xong: chạy lệnh \"git revert --continue\")"
+
+#: wt-status.c:982
+msgid "  (use \"git revert --abort\" to cancel the revert operation)"
+msgstr "  (dùng \"git revert --abort\" để hủy bỏ thao tác revert)"
+
+#: wt-status.c:993
 #, c-format
-msgid "You are currently bisecting branch '%s'."
+msgid "You are currently bisecting, started from branch '%s'."
 msgstr ""
-"Bạn hiện nay đang thực hiện thao tác di chuyển nửa bước (bisect) trên nhánh "
-"“%s”."
+"Bạn hiện nay đang thực hiện thao tác di chuyển nửa bước (bisect), bắt đầu từ "
+"nhánh “%s”."
 
-#: wt-status.c:962
+#: wt-status.c:997
 msgid "You are currently bisecting."
 msgstr "Bạn hiện tại đang thực hiện việc bisect (chia đôi)."
 
-#: wt-status.c:965
+#: wt-status.c:1000
 msgid "  (use \"git bisect reset\" to get back to the original branch)"
 msgstr "  (dùng \"git bisect reset\" để quay trở lại nhánh nguyên thủy)"
 
-#: wt-status.c:1064
+#: wt-status.c:1173
 msgid "On branch "
 msgstr "Trên nhánh "
 
-#: wt-status.c:1071
+#: wt-status.c:1180
+msgid "rebase in progress; onto "
+msgstr "rebase đang được thực hiện: lên trên"
+
+#: wt-status.c:1187
+msgid "HEAD detached at "
+msgstr "HEAD được tách rời tại"
+
+#: wt-status.c:1189
+msgid "HEAD detached from "
+msgstr "HEAD được tách rời từ"
+
+#: wt-status.c:1192
 msgid "Not currently on any branch."
 msgstr "Hiện tại chẳng ở nhánh nào cả."
 
-#: wt-status.c:1083
+#: wt-status.c:1209
 msgid "Initial commit"
 msgstr "Lần chuyển giao (commit) khởi đầu"
 
-#: wt-status.c:1097
+#: wt-status.c:1223
 msgid "Untracked files"
 msgstr "Những tập tin chưa được theo dõi"
 
-#: wt-status.c:1099
+#: wt-status.c:1225
 msgid "Ignored files"
 msgstr "Những tập tin bị lờ đi"
 
-#: wt-status.c:1101
+#: wt-status.c:1229
+#, c-format
+msgid ""
+"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
+"may speed it up, but you have to be careful not to forget to add\n"
+"new files yourself (see 'git help status')."
+msgstr ""
+"Cần %.2f giây để liệt kê tất cả các tập tin chưa được theo dõi. “status -"
+"uno”\n"
+"có lẽ làm nó nhanh hơn, nhưng bạn phải cẩn thận đừng quên mình phải\n"
+"tự thêm các tập tin mới (xem “git help status”.."
+
+#: wt-status.c:1235
 #, c-format
 msgid "Untracked files not listed%s"
 msgstr "Những tập tin không bị theo vết không được liệt kê ra %s"
 
-#: wt-status.c:1103
+#: wt-status.c:1237
 msgid " (use -u option to show untracked files)"
 msgstr " (dùng tùy chọn -u để hiển thị các tập tin chưa được theo dõi)"
 
-#: wt-status.c:1109
+#: wt-status.c:1243
 msgid "No changes"
 msgstr "Không có thay đổi nào"
 
-#: wt-status.c:1114
+#: wt-status.c:1248
 #, c-format
 msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
 msgstr ""
 "không có thay đổi nào được thêm vào commit (dùng \"git add\" và/hoặc \"git "
 "commit -a\")\n"
 
-#: wt-status.c:1117
+#: wt-status.c:1251
 #, c-format
 msgid "no changes added to commit\n"
 msgstr "không có thay đổi nào được thêm vào lần chuyển giao (commit)\n"
 
-#: wt-status.c:1120
+#: wt-status.c:1254
 #, c-format
 msgid ""
 "nothing added to commit but untracked files present (use \"git add\" to "
@@ -1412,58 +1667,58 @@ msgstr ""
 "không có gì được thêm vào lần chuyển giao (commit) nhưng có những tập tin "
 "không được theo dấu vết hiện diện (dùng \"git add\" để đưa vào theo dõi)\n"
 
-#: wt-status.c:1123
+#: wt-status.c:1257
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
 msgstr ""
 "không có gì được thêm vào lần chuyển giao (commit) nhưng có những tập tin "
 "không được theo dấu vết hiện diện\n"
 
-#: wt-status.c:1126
+#: wt-status.c:1260
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
 msgstr ""
 " không có gì để commit (tạo/sao-chép các tập tin và dùng \"git add\" để theo "
 "dõi dấu vết)\n"
 
-#: wt-status.c:1129 wt-status.c:1134
+#: wt-status.c:1263 wt-status.c:1268
 #, c-format
 msgid "nothing to commit\n"
 msgstr "không có gì để chuyển giao (commit)\n"
 
-#: wt-status.c:1132
+#: wt-status.c:1266
 #, c-format
 msgid "nothing to commit (use -u to show untracked files)\n"
 msgstr ""
 "không có gì để chuyển giao (commit) (dùng -u để bỏ các tập tin cần theo dấu "
 "vết)\n"
 
-#: wt-status.c:1136
+#: wt-status.c:1270
 #, c-format
 msgid "nothing to commit, working directory clean\n"
 msgstr "không có gì để chuyển giao (commit), thư mục làm việc sạch sẽ\n"
 
-#: wt-status.c:1244
+#: wt-status.c:1378
 msgid "HEAD (no branch)"
 msgstr "HEAD (không nhánh)"
 
-#: wt-status.c:1250
+#: wt-status.c:1384
 msgid "Initial commit on "
 msgstr "Lần chuyển giao (commit) khởi tạo trên "
 
-#: wt-status.c:1265
+#: wt-status.c:1399
 msgid "behind "
 msgstr "đằng sau "
 
-#: wt-status.c:1268 wt-status.c:1271
+#: wt-status.c:1402 wt-status.c:1405
 msgid "ahead "
 msgstr "phía trước "
 
-#: wt-status.c:1273
+#: wt-status.c:1407
 msgid ", behind "
 msgstr ", đằng sau "
 
-#: compat/precompose_utf8.c:58 builtin/clone.c:341
+#: compat/precompose_utf8.c:58 builtin/clone.c:351
 #, c-format
 msgid "failed to unlink '%s'"
 msgstr "bỏ liên kết (unlink) %s không thành công"
@@ -1472,200 +1727,235 @@ msgstr "bỏ liên kết (unlink) %s không thành công"
 msgid "git add [options] [--] <pathspec>..."
 msgstr "git add [các-tùy-chọn] [--]  <pathspec>..."
 
-#: builtin/add.c:63
+#.
+#. * To be consistent with "git add -p" and most Git
+#. * commands, we should default to being tree-wide, but
+#. * this is not the original behavior and can't be
+#. * changed until users trained themselves not to type
+#. * "git add -u" or "git add -A". For now, we warn and
+#. * keep the old behavior. Later, the behavior can be changed
+#. * to tree-wide, keeping the warning for a while, and
+#. * eventually we can drop the warning.
+#.
+#: builtin/add.c:58
+#, c-format
+msgid ""
+"The behavior of 'git add %s (or %s)' with no path argument from a\n"
+"subdirectory of the tree will change in Git 2.0 and should not be used "
+"anymore.\n"
+"To add content for the whole tree, run:\n"
+"\n"
+"  git add %s :/\n"
+"  (or git add %s :/)\n"
+"\n"
+"To restrict the command to the current directory, run:\n"
+"\n"
+"  git add %s .\n"
+"  (or git add %s .)\n"
+"\n"
+"With the current Git version, the command is restricted to the current "
+"directory.\n"
+msgstr ""
+"Cách ứng xử của lệnh “git add %s (hay %s)” khi không có tham số đường dẫn "
+"từ\n"
+"thư-mục con của cây sẽ thay đổi kể từ Git 2.0 và không thể sử dụng như thế "
+"nữa.\n"
+"Để thêm nội dung cho toàn bộ cây, chạy:\n"
+"\n"
+"  git add %s :/\n"
+"  (hay git add %s :/)\n"
+"\n"
+"Để hạn chế lệnh cho thư-mục hiện tại, chạy:\n"
+"\n"
+"  git add %s .\n"
+"  (hay git add %s .)\n"
+"\n"
+"Với phiên bản hiện tại của Git, lệnh bị hạn chế cho thư-mục hiện tại.\n"
+
+#: builtin/add.c:100
+#, c-format
+msgid ""
+"You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
+"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
+"Paths like '%s' that are\n"
+"removed from your working tree are ignored with this version of Git.\n"
+"\n"
+"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
+"  ignores paths you removed from your working tree.\n"
+"\n"
+"* 'git add --all <pathspec>' will let you also record the removals.\n"
+"\n"
+"Run 'git status' to check the paths you removed from your working tree.\n"
+msgstr ""
+"Bạn chạy “git add” mà không có “-A (--all)” cũng không “--ignore-removal”,\n"
+"cách ứng xử của nó sẽ thay đổi kể từ Git 2.0: nó quan tâm đến các đường dẫn "
+"mà\n"
+"bạn đã gỡ bỏ. Các đường dẫn như là “%s” cái mà\n"
+"bị gỡ bỏ từ cây làm việc của bạn thì bị bỏ qua với phiên bản này của Git.\n"
+"\n"
+"* “git add --ignore-removal <pathspec>”, cái hiện tại là mặc định,\n"
+"  bỏ qua các đường dẫn bạn đã gỡ bỏ từ cây làm việc của bạn.\n"
+"\n"
+"* “git add --all <pathspec>” sẽ đồng thời giúp bạn ghi lại việc dời đi.\n"
+"\n"
+"Chạy “git status” để kiểm tra các đường dẫn bạn đã gỡ bỏ từ cây làm việc của "
+"bạn.\n"
+
+#: builtin/add.c:144
 #, c-format
 msgid "unexpected diff status %c"
 msgstr "trạng thái lệnh diff không như mong đợi %c"
 
-#: builtin/add.c:68 builtin/commit.c:231
+#: builtin/add.c:149 builtin/commit.c:252
 msgid "updating files failed"
 msgstr "Cập nhật tập tin gặp lỗi"
 
-#: builtin/add.c:78
+#: builtin/add.c:163
 #, c-format
 msgid "remove '%s'\n"
 msgstr "gỡ bỏ “%s”\n"
 
-#: builtin/add.c:148
+#: builtin/add.c:253
 msgid "Unstaged changes after refreshing the index:"
 msgstr ""
 "Các thay đổi không được lưu trạng thái sau khi làm tươi mới lại bảng mục lục:"
 
-#: builtin/add.c:151 builtin/add.c:460 builtin/rm.c:275
+#: builtin/add.c:256 builtin/add.c:573 builtin/rm.c:337
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "pathspec “%s” không khớp với bất kỳ tập tin nào"
 
-#: builtin/add.c:234
+#: builtin/add.c:339
 msgid "Could not read the index"
 msgstr "Không thể đọc bảng mục lục"
 
-#: builtin/add.c:244
+#: builtin/add.c:350
 #, c-format
 msgid "Could not open '%s' for writing."
 msgstr "Không thể mở “%s” để ghi."
 
-#: builtin/add.c:248
+#: builtin/add.c:354
 msgid "Could not write patch"
 msgstr "Không thể ghi ra miếng vá"
 
-#: builtin/add.c:253
+#: builtin/add.c:359
 #, c-format
 msgid "Could not stat '%s'"
 msgstr "không thể lấy trạng thái về “%s”"
 
-#: builtin/add.c:255
+#: builtin/add.c:361
 msgid "Empty patch. Aborted."
 msgstr "Miếng vá trống rỗng. Đã bỏ qua."
 
-#: builtin/add.c:261
+#: builtin/add.c:367
 #, c-format
 msgid "Could not apply '%s'"
 msgstr "Không thể apply (áp dụng miếng vá) “%s”"
 
-#: builtin/add.c:271
+#: builtin/add.c:377
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr ""
 "Các đường dẫn theo sau đây sẽ bị lờ đi bởi một trong các tập tin .gitignore "
 "của bạn:\n"
 
-#: builtin/add.c:277 builtin/clean.c:161 builtin/fetch.c:78 builtin/mv.c:63
-#: builtin/prune-packed.c:76 builtin/push.c:425 builtin/remote.c:1253
-#: builtin/rm.c:206
+#: builtin/add.c:394 builtin/clean.c:875 builtin/fetch.c:78 builtin/mv.c:63
+#: builtin/prune-packed.c:73 builtin/push.c:451 builtin/remote.c:1253
+#: builtin/rm.c:268
 msgid "dry run"
 msgstr "chạy thử"
 
-#: builtin/add.c:278 builtin/apply.c:4405 builtin/check-ignore.c:19
-#: builtin/commit.c:1150 builtin/count-objects.c:82 builtin/fsck.c:613
-#: builtin/log.c:1522 builtin/mv.c:62 builtin/read-tree.c:112
+#: builtin/add.c:395 builtin/apply.c:4410 builtin/check-ignore.c:19
+#: builtin/commit.c:1220 builtin/count-objects.c:95 builtin/fsck.c:613
+#: builtin/log.c:1573 builtin/mv.c:62 builtin/read-tree.c:113
 msgid "be verbose"
 msgstr "chi tiết"
 
-#: builtin/add.c:280
+#: builtin/add.c:397
 msgid "interactive picking"
 msgstr "sửa bằng cách tương tác"
 
-#: builtin/add.c:281 builtin/checkout.c:1031 builtin/reset.c:258
+#: builtin/add.c:398 builtin/checkout.c:1073 builtin/reset.c:261
 msgid "select hunks interactively"
 msgstr "chọn “hunks” một cách tương tác"
 
-#: builtin/add.c:282
+#: builtin/add.c:399
 msgid "edit current diff and apply"
 msgstr "sửa diff hiện nay và áp dụng nó"
 
-#: builtin/add.c:283
+#: builtin/add.c:400
 msgid "allow adding otherwise ignored files"
 msgstr "cho phép thêm các tập tin bị bỏ qua khác"
 
-#: builtin/add.c:284
+#: builtin/add.c:401
 msgid "update tracked files"
 msgstr "cập nhật các tập tin được theo vết"
 
-#: builtin/add.c:285
+#: builtin/add.c:402
 msgid "record only the fact that the path will be added later"
 msgstr "chỉ ghi lại sự việc mà đường dẫn sẽ được thêm vào sau"
 
-#: builtin/add.c:286
+#: builtin/add.c:403
 msgid "add changes from all tracked and untracked files"
 msgstr ""
 "thêm các thay đổi từ tất cả các tập tin có cũng như không được theo dõi dấu "
 "vết"
 
-#: builtin/add.c:287
+#. takes no arguments
+#: builtin/add.c:406
+msgid "ignore paths removed in the working tree (same as --no-all)"
+msgstr ""
+"lờ đi các đường dẫn bị gỡ bỏ trong cây thư mục làm việc (giống với --no-all)"
+
+#: builtin/add.c:408
 msgid "don't add, only refresh the index"
 msgstr "không thêm, chỉ làm tươi mới bảng mục lục"
 
-#: builtin/add.c:288
+#: builtin/add.c:409
 msgid "just skip files which cannot be added because of errors"
 msgstr "chie bỏ qua những tập tin mà nó không thể được thêm vào bởi vì gặp lỗi"
 
-#: builtin/add.c:289
+#: builtin/add.c:410
 msgid "check if - even missing - files are ignored in dry run"
 msgstr ""
 "kiểm tra xem - thậm chí thiếu - tập tin bị bỏ qua trong quá trình chạy thử"
 
-#: builtin/add.c:311
+#: builtin/add.c:432
 #, c-format
 msgid "Use -f if you really want to add them.\n"
 msgstr "Sử dụng tùy chọn -f nếu bạn thực sự muốn thêm chúng vào.\n"
 
-#: builtin/add.c:312
+#: builtin/add.c:433
 msgid "no files added"
 msgstr "chưa có tập tin nào được thêm vào"
 
-#: builtin/add.c:318
+#: builtin/add.c:439
 msgid "adding files failed"
 msgstr "thêm tập tin gặp lỗi"
 
-#.
-#. * To be consistent with "git add -p" and most Git
-#. * commands, we should default to being tree-wide, but
-#. * this is not the original behavior and can't be
-#. * changed until users trained themselves not to type
-#. * "git add -u" or "git add -A". For now, we warn and
-#. * keep the old behavior. Later, this warning can be
-#. * turned into a die(...), and eventually we may
-#. * reallow the command with a new behavior.
-#.
-#: builtin/add.c:335
-#, c-format
-msgid ""
-"The behavior of 'git add %s (or %s)' with no path argument from a\n"
-"subdirectory of the tree will change in Git 2.0 and should not be used "
-"anymore.\n"
-"To add content for the whole tree, run:\n"
-"\n"
-"  git add %s :/\n"
-"  (or git add %s :/)\n"
-"\n"
-"To restrict the command to the current directory, run:\n"
-"\n"
-"  git add %s .\n"
-"  (or git add %s .)\n"
-"\n"
-"With the current Git version, the command is restricted to the current "
-"directory."
-msgstr ""
-"Cách ứng xử của lệnh “git add %s (hay %s)” khi không có tham số đường dẫn "
-"từ\n"
-"thư-mục con của cây sẽ thay đổi kể từ Git 2.0 và không thể sử dụng như thế "
-"nữa.\n"
-"Để thêm nội dung cho toàn bộ cây, chạy:\n"
-"\n"
-"  git add %s :/\n"
-"  (hay git add %s :/)\n"
-"\n"
-"Để hạn chế lệnh cho thư-mục hiện tại, chạy:\n"
-"\n"
-"  git add %s .\n"
-"  (hay git add %s .)\n"
-"\n"
-"Với phiên bản hiện tại của Git, lệnh bị hạn chế cho thư-mục hiện tại."
-
-#: builtin/add.c:381
+#: builtin/add.c:478
 msgid "-A and -u are mutually incompatible"
 msgstr "-A và -u xung khắc nhau"
 
-#: builtin/add.c:383
+#: builtin/add.c:496
 msgid "Option --ignore-missing can only be used together with --dry-run"
 msgstr "Tùy chọn --ignore-missing chỉ có thể được dùng cùng với --dry-run"
 
-#: builtin/add.c:414
+#: builtin/add.c:526
 #, c-format
 msgid "Nothing specified, nothing added.\n"
 msgstr "Không có gì được chỉ ra, không có gì được thêm vào.\n"
 
-#: builtin/add.c:415
+#: builtin/add.c:527
 #, c-format
 msgid "Maybe you wanted to say 'git add .'?\n"
 msgstr "Có lẽ bạn muốn là “git add .” phải không?\n"
 
-#: builtin/add.c:421 builtin/check-ignore.c:67 builtin/clean.c:204
-#: builtin/commit.c:291 builtin/mv.c:82 builtin/rm.c:235
+#: builtin/add.c:533 builtin/check-ignore.c:161 builtin/clean.c:919
+#: builtin/commit.c:312 builtin/mv.c:82 builtin/rm.c:297
 msgid "index file corrupt"
 msgstr "tập tin ghi bảng mục lục bị hỏng"
 
-#: builtin/add.c:481 builtin/apply.c:4501 builtin/mv.c:229 builtin/rm.c:370
+#: builtin/add.c:605 builtin/apply.c:4506 builtin/mv.c:229 builtin/rm.c:432
 msgid "Unable to write new index file"
 msgstr "Không thể ghi tập tin lưu bảng mục lục mới"
 
@@ -1799,24 +2089,24 @@ msgstr "không thể đọc liên kết tượng trưng %s"
 msgid "unable to open or read %s"
 msgstr "không thể mở hay đọc %s"
 
-#: builtin/apply.c:2684
+#: builtin/apply.c:2688
 #, c-format
 msgid "invalid start of line: '%c'"
 msgstr "sai khởi đầu dòng: “%c”"
 
-#: builtin/apply.c:2802
+#: builtin/apply.c:2806
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
 msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
 msgstr[0] "Khối dữ liệu #%d thành công tại %d (offset %d dòng)."
 msgstr[1] "Khối dữ liệu #%d thành công tại %d (offset %d dòng)."
 
-#: builtin/apply.c:2814
+#: builtin/apply.c:2818
 #, c-format
 msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
 msgstr "Nội dung bị giảm xuống (%ld/%ld) để áp dụng mảnh dữ liệu tại %d"
 
-#: builtin/apply.c:2820
+#: builtin/apply.c:2824
 #, c-format
 msgid ""
 "while searching for:\n"
@@ -1825,325 +2115,325 @@ msgstr ""
 "Trong khi đang tìm kiếm cho:\n"
 "%.*s"
 
-#: builtin/apply.c:2839
+#: builtin/apply.c:2843
 #, c-format
 msgid "missing binary patch data for '%s'"
 msgstr "thiếu dữ liệu của miếng vá định dạng nhị phân cho “%s”"
 
-#: builtin/apply.c:2942
+#: builtin/apply.c:2946
 #, c-format
 msgid "binary patch does not apply to '%s'"
 msgstr "miếng vá định dạng nhị phân không được áp dụng cho “%s”"
 
-#: builtin/apply.c:2948
+#: builtin/apply.c:2952
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
 msgstr ""
 "vá nhị phân cho “%s” tạo ra kết quả không chính xác (mong chờ %s, lại nhận "
 "%s)"
 
-#: builtin/apply.c:2969
+#: builtin/apply.c:2973
 #, c-format
 msgid "patch failed: %s:%ld"
 msgstr "vá gặp lỗi: %s:%ld"
 
-#: builtin/apply.c:3091
+#: builtin/apply.c:3095
 #, c-format
 msgid "cannot checkout %s"
 msgstr "không thể \"checkout\" %s"
 
-#: builtin/apply.c:3136 builtin/apply.c:3145 builtin/apply.c:3189
+#: builtin/apply.c:3140 builtin/apply.c:3149 builtin/apply.c:3194
 #, c-format
 msgid "read of %s failed"
 msgstr "đọc %s gặp lỗi"
 
-#: builtin/apply.c:3169 builtin/apply.c:3391
+#: builtin/apply.c:3174 builtin/apply.c:3396
 #, c-format
 msgid "path %s has been renamed/deleted"
 msgstr "đường dẫn %s đã bị xóa/đổi tên"
 
-#: builtin/apply.c:3250 builtin/apply.c:3405
+#: builtin/apply.c:3255 builtin/apply.c:3410
 #, c-format
 msgid "%s: does not exist in index"
 msgstr "%s: không tồn tại trong bảng mục lục"
 
-#: builtin/apply.c:3254 builtin/apply.c:3397 builtin/apply.c:3419
+#: builtin/apply.c:3259 builtin/apply.c:3402 builtin/apply.c:3424
 #, c-format
 msgid "%s: %s"
 msgstr "%s: %s"
 
-#: builtin/apply.c:3259 builtin/apply.c:3413
+#: builtin/apply.c:3264 builtin/apply.c:3418
 #, c-format
 msgid "%s: does not match index"
 msgstr "%s: không khớp trong mục lục"
 
-#: builtin/apply.c:3361
+#: builtin/apply.c:3366
 msgid "removal patch leaves file contents"
 msgstr "loại bỏ miếng vá để lại nội dung tập tin"
 
-#: builtin/apply.c:3430
+#: builtin/apply.c:3435
 #, c-format
 msgid "%s: wrong type"
 msgstr "%s: sai kiểu"
 
-#: builtin/apply.c:3432
+#: builtin/apply.c:3437
 #, c-format
 msgid "%s has type %o, expected %o"
 msgstr "%s có kiểu %o, mong chờ %o"
 
-#: builtin/apply.c:3533
+#: builtin/apply.c:3538
 #, c-format
 msgid "%s: already exists in index"
 msgstr "%s: đã có từ trước trong bảng mục lục"
 
-#: builtin/apply.c:3536
+#: builtin/apply.c:3541
 #, c-format
 msgid "%s: already exists in working directory"
 msgstr "%s: đã sẵn có trong thư mục đang làm việc"
 
-#: builtin/apply.c:3556
+#: builtin/apply.c:3561
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o)"
 msgstr "chế độ mới (%o) của %s không khớp với chế độ cũ (%o)"
 
-#: builtin/apply.c:3561
+#: builtin/apply.c:3566
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
 msgstr "chế độ mới (%o) của %s không khớp với chế độ cũ (%o) của %s"
 
-#: builtin/apply.c:3569
+#: builtin/apply.c:3574
 #, c-format
 msgid "%s: patch does not apply"
 msgstr "%s: miếng vá không được áp dụng"
 
-#: builtin/apply.c:3582
+#: builtin/apply.c:3587
 #, c-format
 msgid "Checking patch %s..."
 msgstr "Đang kiểm tra miếng vá %s..."
 
-#: builtin/apply.c:3675 builtin/checkout.c:215 builtin/reset.c:124
+#: builtin/apply.c:3680 builtin/checkout.c:216 builtin/reset.c:127
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
 msgstr "make_cache_entry gặp lỗi đối với đường dẫn “%s”"
 
-#: builtin/apply.c:3818
+#: builtin/apply.c:3823
 #, c-format
 msgid "unable to remove %s from index"
 msgstr "không thể gỡ bỏ %s từ mục lục"
 
-#: builtin/apply.c:3846
+#: builtin/apply.c:3851
 #, c-format
-msgid "corrupt patch for subproject %s"
-msgstr "miếng vá sai hỏng cho dự án con (subproject) %s"
+msgid "corrupt patch for submodule %s"
+msgstr "miếng vá sai hỏng cho mô-đun-con (subproject) %s"
 
-#: builtin/apply.c:3850
+#: builtin/apply.c:3855
 #, c-format
 msgid "unable to stat newly created file '%s'"
 msgstr "không thể lấy trạng thái về tập tin %s mới hơn đã được tạo"
 
-#: builtin/apply.c:3855
+#: builtin/apply.c:3860
 #, c-format
 msgid "unable to create backing store for newly created file %s"
 msgstr "không thể tạo “backing store” cho tập tin được tạo mới hơn %s"
 
-#: builtin/apply.c:3858 builtin/apply.c:3966
+#: builtin/apply.c:3863 builtin/apply.c:3971
 #, c-format
 msgid "unable to add cache entry for %s"
 msgstr "không thể thêm mục nhớ tạm cho %s"
 
-#: builtin/apply.c:3891
+#: builtin/apply.c:3896
 #, c-format
 msgid "closing file '%s'"
 msgstr "đang đóng tập tin “%s”"
 
-#: builtin/apply.c:3940
+#: builtin/apply.c:3945
 #, c-format
 msgid "unable to write file '%s' mode %o"
 msgstr "không thể ghi vào tập tin “%s” chế độ (mode) %o"
 
-#: builtin/apply.c:4027
+#: builtin/apply.c:4032
 #, c-format
 msgid "Applied patch %s cleanly."
 msgstr "Đã áp dụng miếng và %s một cách sạch sẽ."
 
-#: builtin/apply.c:4035
+#: builtin/apply.c:4040
 msgid "internal error"
 msgstr "lỗi nội bộ"
 
 #. Say this even without --verbose
-#: builtin/apply.c:4038
+#: builtin/apply.c:4043
 #, c-format
 msgid "Applying patch %%s with %d reject..."
 msgid_plural "Applying patch %%s with %d rejects..."
 msgstr[0] "Đang áp dụng miếng vá %%s với %d lần từ chối..."
 msgstr[1] "Đang áp dụng miếng vá %%s với %d lần từ chối..."
 
-#: builtin/apply.c:4048
+#: builtin/apply.c:4053
 #, c-format
 msgid "truncating .rej filename to %.*s.rej"
 msgstr "đang cắt cụt tên tập tin .rej thành %.*s.rej"
 
-#: builtin/apply.c:4069
+#: builtin/apply.c:4074
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "Khối nhớ #%d được áp dụng gọn gàng."
 
-#: builtin/apply.c:4072
+#: builtin/apply.c:4077
 #, c-format
 msgid "Rejected hunk #%d."
 msgstr "hunk #%d bị từ chối."
 
-#: builtin/apply.c:4222
+#: builtin/apply.c:4227
 msgid "unrecognized input"
 msgstr "không thừa nhận đầu vào"
 
-#: builtin/apply.c:4233
+#: builtin/apply.c:4238
 msgid "unable to read index file"
 msgstr "không thể đọc tập tin lưu bảng mục lục"
 
-#: builtin/apply.c:4352 builtin/apply.c:4355 builtin/clone.c:91
+#: builtin/apply.c:4357 builtin/apply.c:4360 builtin/clone.c:91
 #: builtin/fetch.c:63
 msgid "path"
 msgstr "đường-dẫn"
 
-#: builtin/apply.c:4353
+#: builtin/apply.c:4358
 msgid "don't apply changes matching the given path"
 msgstr "không áp dụng các thay đổi khớp với đường dẫn đã cho"
 
-#: builtin/apply.c:4356
+#: builtin/apply.c:4361
 msgid "apply changes matching the given path"
 msgstr "áp dụng các thay đổi khớp với đường dẫn đã cho"
 
-#: builtin/apply.c:4358
+#: builtin/apply.c:4363
 msgid "num"
 msgstr "số"
 
-#: builtin/apply.c:4359
+#: builtin/apply.c:4364
 msgid "remove <num> leading slashes from traditional diff paths"
 msgstr "gỡ bỏ <số> phần dẫn đầu (slashe) từ đường dẫn diff cổ điển"
 
-#: builtin/apply.c:4362
+#: builtin/apply.c:4367
 msgid "ignore additions made by the patch"
 msgstr "lờ đi phần phụ thêm tạo ra bởi miếng vá"
 
-#: builtin/apply.c:4364
+#: builtin/apply.c:4369
 msgid "instead of applying the patch, output diffstat for the input"
 msgstr ""
 "thay vì áp dụng một miếng vá, kết xuất kết quả từ lệnh diffstat cho đầu ra"
 
-#: builtin/apply.c:4368
+#: builtin/apply.c:4373
 msgid "show number of added and deleted lines in decimal notation"
 msgstr ""
 "hiển thị số lượng các dòng được thêm vào và xóa đi theo ký hiệu thập phân"
 
-#: builtin/apply.c:4370
+#: builtin/apply.c:4375
 msgid "instead of applying the patch, output a summary for the input"
 msgstr "thay vì áp dụng một miếng vá, kết xuất kết quả cho đầu vào"
 
-#: builtin/apply.c:4372
+#: builtin/apply.c:4377
 msgid "instead of applying the patch, see if the patch is applicable"
 msgstr "thay vì áp dụng miếng vá, hãy xem xem miếng vá có thích hợp không"
 
-#: builtin/apply.c:4374
+#: builtin/apply.c:4379
 msgid "make sure the patch is applicable to the current index"
 msgstr "hãy chắc chắn là miếng vá thích hợp với bảng mục lục hiện hành"
 
-#: builtin/apply.c:4376
+#: builtin/apply.c:4381
 msgid "apply a patch without touching the working tree"
 msgstr "áp dụng một miếng vá mà không động chạm đến cây làm việc"
 
-#: builtin/apply.c:4378
+#: builtin/apply.c:4383
 msgid "also apply the patch (use with --stat/--summary/--check)"
 msgstr ""
 "đồng thời áp dụng miếng vá (dùng với tùy chọn --stat/--summary/--check)"
 
-#: builtin/apply.c:4380
+#: builtin/apply.c:4385
 msgid "attempt three-way merge if a patch does not apply"
 msgstr "thử hòa trộn kiểu three-way nếu việc vá không thể thực hiện được"
 
-#: builtin/apply.c:4382
+#: builtin/apply.c:4387
 msgid "build a temporary index based on embedded index information"
 msgstr ""
 "xây dựng bảng mục lục tạm thời trên cơ sở thông tin bảng mục lục được nhúng"
 
-#: builtin/apply.c:4384 builtin/checkout-index.c:197 builtin/ls-files.c:463
+#: builtin/apply.c:4389 builtin/checkout-index.c:197 builtin/ls-files.c:462
 msgid "paths are separated with NUL character"
 msgstr "các đường dẫn bị ngăn cách bởi ký tự NULL"
 
-#: builtin/apply.c:4387
+#: builtin/apply.c:4392
 msgid "ensure at least <n> lines of context match"
 msgstr "đảm bảo rằng có ít nhất <n> dòng nội dung khớp"
 
-#: builtin/apply.c:4388
+#: builtin/apply.c:4393
 msgid "action"
 msgstr "hành động"
 
-#: builtin/apply.c:4389
+#: builtin/apply.c:4394
 msgid "detect new or modified lines that have whitespace errors"
 msgstr "tìm thấy một dòng mới hoặc bị sửa đổi mà nó có lỗi do khoảng trắng"
 
-#: builtin/apply.c:4392 builtin/apply.c:4395
+#: builtin/apply.c:4397 builtin/apply.c:4400
 msgid "ignore changes in whitespace when finding context"
 msgstr "lờ đi sự thay đổi do khoảng trắng khi quét nội dung"
 
-#: builtin/apply.c:4398
+#: builtin/apply.c:4403
 msgid "apply the patch in reverse"
 msgstr "áp dụng miếng vá theo chiều ngược"
 
-#: builtin/apply.c:4400
+#: builtin/apply.c:4405
 msgid "don't expect at least one line of context"
 msgstr "đừng hy vọng có ít nhất một dòng nội dung"
 
-#: builtin/apply.c:4402
+#: builtin/apply.c:4407
 msgid "leave the rejected hunks in corresponding *.rej files"
 msgstr "để lại khối dữ liệu bị từ chối trong các tập tin *.rej tương ứng"
 
-#: builtin/apply.c:4404
+#: builtin/apply.c:4409
 msgid "allow overlapping hunks"
 msgstr "cho phép chồng khối nhớ"
 
-#: builtin/apply.c:4407
+#: builtin/apply.c:4412
 msgid "tolerate incorrectly detected missing new-line at the end of file"
 msgstr ""
 "đã dò tìm thấy dung sai không chính xác thiếu dòng mới tại cuối tập tin"
 
-#: builtin/apply.c:4410
+#: builtin/apply.c:4415
 msgid "do not trust the line counts in the hunk headers"
 msgstr "không tin số lượng dòng trong phần đầu khối dữ liệu"
 
-#: builtin/apply.c:4412
+#: builtin/apply.c:4417
 msgid "root"
 msgstr "root"
 
-#: builtin/apply.c:4413
+#: builtin/apply.c:4418
 msgid "prepend <root> to all filenames"
 msgstr "treo thêm <root> vào tất cả các tên tập tin"
 
-#: builtin/apply.c:4435
+#: builtin/apply.c:4440
 msgid "--3way outside a repository"
 msgstr "--3way ở ngoài một kho chứa"
 
-#: builtin/apply.c:4443
+#: builtin/apply.c:4448
 msgid "--index outside a repository"
 msgstr "--index ở ngoài một kho chứa"
 
-#: builtin/apply.c:4446
+#: builtin/apply.c:4451
 msgid "--cached outside a repository"
 msgstr "--cached ở ngoài một kho chứa"
 
-#: builtin/apply.c:4462
+#: builtin/apply.c:4467
 #, c-format
 msgid "can't open patch '%s'"
 msgstr "không thể mở miếng vá “%s”"
 
-#: builtin/apply.c:4476
+#: builtin/apply.c:4481
 #, c-format
 msgid "squelched %d whitespace error"
 msgid_plural "squelched %d whitespace errors"
 msgstr[0] "đã chấm dứt %d lỗi khoảng trắng"
 msgstr[1] "đã chấm dứt %d lỗi khoảng trắng"
 
-#: builtin/apply.c:4482 builtin/apply.c:4492
+#: builtin/apply.c:4487 builtin/apply.c:4497
 #, c-format
 msgid "%d line adds whitespace errors."
 msgid_plural "%d lines add whitespace errors."
@@ -2167,21 +2457,21 @@ msgstr "git archive: Máy chủ không có địa chỉ URL"
 msgid "git archive: expected ACK/NAK, got EOF"
 msgstr "git archive: mong đợi ACK/NAK, nhận EOF"
 
-#: builtin/archive.c:63
+#: builtin/archive.c:61
 #, c-format
 msgid "git archive: NACK %s"
 msgstr "git archive: NACK %s"
 
-#: builtin/archive.c:65
+#: builtin/archive.c:63
 #, c-format
 msgid "remote error: %s"
 msgstr "lỗi máy chủ: %s"
 
-#: builtin/archive.c:66
+#: builtin/archive.c:64
 msgid "git archive: protocol error"
 msgstr "git archive: lỗi giao thức"
 
-#: builtin/archive.c:71
+#: builtin/archive.c:68
 msgid "git archive: expected a flush"
 msgstr "git archive: đã mong chờ một flush"
 
@@ -2199,125 +2489,125 @@ msgstr ""
 "cập nhật BISECT_HEAD thay vì lấy ra (checking out) lần chuyển giao (commit) "
 "hiện hành"
 
-#: builtin/blame.c:25
+#: builtin/blame.c:26
 msgid "git blame [options] [rev-opts] [rev] [--] file"
 msgstr "git blame [các-tùy-chọn] [rev-opts] [rev] [--] tập-tin"
 
-#: builtin/blame.c:30 builtin/shortlog.c:15
+#: builtin/blame.c:31
 msgid "[rev-opts] are documented in git-rev-list(1)"
 msgstr "[rev-opts] được mô tả trong git-rev-list(1)"
 
-#: builtin/blame.c:2350
+#: builtin/blame.c:2276
 msgid "Show blame entries as we find them, incrementally"
 msgstr "Hiển thị các mục “blame” như là chúng ta thấy chúng, tăng dần"
 
-#: builtin/blame.c:2351
+#: builtin/blame.c:2277
 msgid "Show blank SHA-1 for boundary commits (Default: off)"
 msgstr ""
 "Hiển thị SHA-1 trắng cho những lần chuyển giao biên giới (Mặc định: off)"
 
-#: builtin/blame.c:2352
+#: builtin/blame.c:2278
 msgid "Do not treat root commits as boundaries (Default: off)"
 msgstr "Không coi các lần chuyển giao gốc là giới hạn (Mặc định: off)"
 
-#: builtin/blame.c:2353
+#: builtin/blame.c:2279
 msgid "Show work cost statistics"
 msgstr "Hiển thị thống kê công sức làm việc"
 
-#: builtin/blame.c:2354
+#: builtin/blame.c:2280
 msgid "Show output score for blame entries"
 msgstr "Hiển thị kết xuất điểm số có các mục tin “blame”"
 
-#: builtin/blame.c:2355
+#: builtin/blame.c:2281
 msgid "Show original filename (Default: auto)"
 msgstr "Hiển thị tên tập tin gốc (Mặc định: auto)"
 
-#: builtin/blame.c:2356
+#: builtin/blame.c:2282
 msgid "Show original linenumber (Default: off)"
 msgstr "Hiển thị số dòng gốc (Mặc định: off)"
 
-#: builtin/blame.c:2357
+#: builtin/blame.c:2283
 msgid "Show in a format designed for machine consumption"
 msgstr "Hiển thị ở định dạng đã thiết kế cho sự tiêu dùng bằng máy"
 
-#: builtin/blame.c:2358
+#: builtin/blame.c:2284
 msgid "Show porcelain format with per-line commit information"
 msgstr "Hiển thị định dạng “porcelain” với thông tin chuyển giao mỗi dòng"
 
-#: builtin/blame.c:2359
+#: builtin/blame.c:2285
 msgid "Use the same output mode as git-annotate (Default: off)"
 msgstr "Dùng cùng chế độ xuất ra vóigit-annotate (Mặc định: off)"
 
-#: builtin/blame.c:2360
+#: builtin/blame.c:2286
 msgid "Show raw timestamp (Default: off)"
 msgstr "Hiển thị dấu vết thời gian dạng thô (Mặc định: off)"
 
-#: builtin/blame.c:2361
+#: builtin/blame.c:2287
 msgid "Show long commit SHA1 (Default: off)"
 msgstr "Hiển thị SHA1 của lần chuyển giao (commit) dạng dài (Mặc định: off)"
 
-#: builtin/blame.c:2362
+#: builtin/blame.c:2288
 msgid "Suppress author name and timestamp (Default: off)"
 msgstr "Không hiển thị tên tác giả và dấu vết thời gian (Mặc định: off)"
 
-#: builtin/blame.c:2363
+#: builtin/blame.c:2289
 msgid "Show author email instead of name (Default: off)"
 msgstr "Hiển thị thư điện tử của tác giả thay vì tên (Mặc định: off)"
 
-#: builtin/blame.c:2364
+#: builtin/blame.c:2290
 msgid "Ignore whitespace differences"
 msgstr "Bỏ qua các khác biệt do khoảng trắng gây ra"
 
-#: builtin/blame.c:2365
+#: builtin/blame.c:2291
 msgid "Spend extra cycles to find better match"
 msgstr "Tiêu thụ thêm năng tài nguyên máy móc để tìm kiếm tốt hơn nữa"
 
-#: builtin/blame.c:2366
+#: builtin/blame.c:2292
 msgid "Use revisions from <file> instead of calling git-rev-list"
 msgstr ""
 "Sử dụng điểm xét duyệt (revision) từ <tập tin> thay vì gọi “git-rev-list”"
 
-#: builtin/blame.c:2367
+#: builtin/blame.c:2293
 msgid "Use <file>'s contents as the final image"
 msgstr "Sử dụng nội dung của <tập tin> như là ảnh cuối cùng"
 
-#: builtin/blame.c:2368 builtin/blame.c:2369
+#: builtin/blame.c:2294 builtin/blame.c:2295
 msgid "score"
 msgstr "điểm số"
 
-#: builtin/blame.c:2368
+#: builtin/blame.c:2294
 msgid "Find line copies within and across files"
 msgstr "Tìm các bản sao chép dòng trong và ngang qua tập tin"
 
-#: builtin/blame.c:2369
+#: builtin/blame.c:2295
 msgid "Find line movements within and across files"
 msgstr "Tìm các di chuyển dòng trong và ngang qua tập tin"
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "n,m"
 msgstr "n,m"
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "Process only line range n,m, counting from 1"
 msgstr "Xử lý chỉ dòng vùng n,m, tính từ 1"
 
-#: builtin/branch.c:23
+#: builtin/branch.c:24
 msgid "git branch [options] [-r | -a] [--merged | --no-merged]"
 msgstr "git branch [các-tùy-chọn] [-r | -a] [--merged | --no-merged]"
 
-#: builtin/branch.c:24
+#: builtin/branch.c:25
 msgid "git branch [options] [-l] [-f] <branchname> [<start-point>]"
 msgstr "git branch [các-tùy-chọn] [-l] [-f] <tên-nhánh> [<điểm-đầu>]"
 
-#: builtin/branch.c:25
+#: builtin/branch.c:26
 msgid "git branch [options] [-r] (-d | -D) <branchname>..."
 msgstr "git branch [các-tùy-chọn] [-r] (-d | -D) <tên-nhánh> ..."
 
-#: builtin/branch.c:26
+#: builtin/branch.c:27
 msgid "git branch [options] (-m | -M) [<oldbranch>] <newbranch>"
 msgstr "git branch [các-tùy-chọn] (-m | -M) [<nhánh-cũ>] <nhánh-mới>"
 
-#: builtin/branch.c:145
+#: builtin/branch.c:150
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
@@ -2326,7 +2616,7 @@ msgstr ""
 "đang xóa nhánh “%s” mà nó lại đã được hòa trộn vào\n"
 "         “%s”, nhưng vẫn chưa được hòa trộn vào HEAD."
 
-#: builtin/branch.c:149
+#: builtin/branch.c:154
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
@@ -2335,12 +2625,12 @@ msgstr ""
 "không xóa nhánh “%s” cái mà chưa được hòa trộn vào\n"
 "         “%s”, cho dù là nó đã được hòa trộn vào HEAD."
 
-#: builtin/branch.c:163
+#: builtin/branch.c:168
 #, c-format
 msgid "Couldn't look up commit object for '%s'"
 msgstr "Không thể tìm kiếm đối tượng chuyển giao (commit) cho “%s”"
 
-#: builtin/branch.c:167
+#: builtin/branch.c:172
 #, c-format
 msgid ""
 "The branch '%s' is not fully merged.\n"
@@ -2349,287 +2639,333 @@ msgstr ""
 "Nhánh “%s” không được trộn một cách đầy đủ.\n"
 "Nếu bạn thực sự muốn xóa nó, thì chạy lệnh “git branch -D %s”."
 
-#: builtin/branch.c:180
+#: builtin/branch.c:185
 msgid "Update of config-file failed"
 msgstr "Cập nhật tập tin cấu hình gặp lỗi"
 
-#: builtin/branch.c:208
+#: builtin/branch.c:213
 msgid "cannot use -a with -d"
 msgstr "không thể dùng tùy chọn -a với -d"
 
-#: builtin/branch.c:214
+#: builtin/branch.c:219
 msgid "Couldn't look up commit object for HEAD"
 msgstr "Không thể tìm kiếm đối tượng chuyển giao (commit) cho HEAD"
 
-#: builtin/branch.c:222
+#: builtin/branch.c:227
 #, c-format
 msgid "Cannot delete the branch '%s' which you are currently on."
 msgstr "Không thể xóa nhánh “%s” cái mà bạn hiện nay đang ở."
 
-#: builtin/branch.c:235
+#: builtin/branch.c:240
 #, c-format
 msgid "remote branch '%s' not found."
 msgstr "không tìm thấy nhánh máy chủ “%s”."
 
-#: builtin/branch.c:236
+#: builtin/branch.c:241
 #, c-format
 msgid "branch '%s' not found."
 msgstr "không tìm thấy nhánh “%s”."
 
-#: builtin/branch.c:250
+#: builtin/branch.c:255
 #, c-format
 msgid "Error deleting remote branch '%s'"
 msgstr "Gặp lỗi khi đang xóa nhánh trên máy chủ “%s”"
 
-#: builtin/branch.c:251
+#: builtin/branch.c:256
 #, c-format
 msgid "Error deleting branch '%s'"
 msgstr "Lỗi khi xoá bỏ nhánh “%s”"
 
-#: builtin/branch.c:258
+#: builtin/branch.c:263
 #, c-format
 msgid "Deleted remote branch %s (was %s).\n"
 msgstr "Nhánh trên máy chủ \"%s\" đã bị xóa (từng là %s).\n"
 
-#: builtin/branch.c:259
+#: builtin/branch.c:264
 #, c-format
 msgid "Deleted branch %s (was %s).\n"
 msgstr "Nhánh “%s” đã bị xóa (từng là %s)\n"
 
-#: builtin/branch.c:361
+#: builtin/branch.c:366
 #, c-format
 msgid "branch '%s' does not point at a commit"
 msgstr "nhánh “%s” không chỉ đến một lần chuyển giao (commit) nào cả"
 
-#: builtin/branch.c:433
+#: builtin/branch.c:453
 #, c-format
 msgid "[%s: behind %d]"
 msgstr "[%s: đằng sau %d]"
 
-#: builtin/branch.c:435
+#: builtin/branch.c:455
 #, c-format
 msgid "[behind %d]"
 msgstr "[đằng sau %d]"
 
-#: builtin/branch.c:439
+#: builtin/branch.c:459
 #, c-format
 msgid "[%s: ahead %d]"
 msgstr "[%s: phía trước %d]"
 
-#: builtin/branch.c:441
+#: builtin/branch.c:461
 #, c-format
 msgid "[ahead %d]"
 msgstr "[phía trước %d]"
 
-#: builtin/branch.c:444
+#: builtin/branch.c:464
 #, c-format
 msgid "[%s: ahead %d, behind %d]"
 msgstr "[%s: trước %d, sau %d]"
 
-#: builtin/branch.c:447
+#: builtin/branch.c:467
 #, c-format
 msgid "[ahead %d, behind %d]"
 msgstr "[trước %d, sau %d]"
 
-#: builtin/branch.c:469
+#: builtin/branch.c:490
 msgid " **** invalid ref ****"
 msgstr " **** tham chiếu sai ****"
 
-#: builtin/branch.c:560
+#: builtin/branch.c:582
+#, c-format
+msgid "(no branch, rebasing %s)"
+msgstr "(không nhánh, đang rebase %s)"
+
+#: builtin/branch.c:585
+#, c-format
+msgid "(no branch, bisect started on %s)"
+msgstr "(không nhánh, bisect được bắt đầu tại %s)"
+
+#: builtin/branch.c:588
+#, c-format
+msgid "(detached from %s)"
+msgstr "(được tách rời từ %s)"
+
+#: builtin/branch.c:591
 msgid "(no branch)"
 msgstr "(không nhánh)"
 
-#: builtin/branch.c:593
+#: builtin/branch.c:637
 #, c-format
 msgid "object '%s' does not point to a commit"
 msgstr "đối tượng “%s” không chỉ đến một lần chuyển giao (commit) nào cả"
 
-#: builtin/branch.c:625
+#: builtin/branch.c:669
 msgid "some refs could not be read"
 msgstr "một số tham chiếu đã không thể đọc được"
 
-#: builtin/branch.c:638
+#: builtin/branch.c:682
 msgid "cannot rename the current branch while not on any."
 msgstr "không thể đổi tên nhánh hiện hành trong khi nó chẳng ở đâu cả."
 
-#: builtin/branch.c:648
+#: builtin/branch.c:692
 #, c-format
 msgid "Invalid branch name: '%s'"
 msgstr "Sai tên nhánh: “%s”"
 
-#: builtin/branch.c:663
+#: builtin/branch.c:707
 msgid "Branch rename failed"
 msgstr "Đổi tên nhánh gặp lỗi"
 
-#: builtin/branch.c:667
+#: builtin/branch.c:711
 #, c-format
 msgid "Renamed a misnamed branch '%s' away"
 msgstr "Đã đổi tên nhánh khuyết danh “%s” đi"
 
-#: builtin/branch.c:671
+#: builtin/branch.c:715
 #, c-format
 msgid "Branch renamed to %s, but HEAD is not updated!"
 msgstr "Nhánh bị đổi tên thành %s, nhưng HEAD lại không được cập nhật!"
 
-#: builtin/branch.c:678
+#: builtin/branch.c:722
 msgid "Branch is renamed, but update of config-file failed"
 msgstr "Nhánh bị đổi tên, nhưng cập nhật tập tin cấu hình gặp lỗi"
 
-#: builtin/branch.c:693
+#: builtin/branch.c:737
 #, c-format
 msgid "malformed object name %s"
 msgstr "tên đối tượng dị hình %s"
 
-#: builtin/branch.c:717
+#: builtin/branch.c:761
 #, c-format
 msgid "could not write branch description template: %s"
 msgstr "không thể ghi vào mẫu mô tả nhánh: %s"
 
-#: builtin/branch.c:747
+#: builtin/branch.c:791
 msgid "Generic options"
 msgstr "Tùy chọn chung"
 
-#: builtin/branch.c:749
+#: builtin/branch.c:793
 msgid "show hash and subject, give twice for upstream branch"
 msgstr "hiển thị mã băm và chủ đề, đưa ra hai lần cho nhánh thượng nguồn"
 
-#: builtin/branch.c:750
+#: builtin/branch.c:794
 msgid "suppress informational messages"
 msgstr "Thu hồi các thông điệp thông tin"
 
-#: builtin/branch.c:751
+#: builtin/branch.c:795
 msgid "set up tracking mode (see git-pull(1))"
 msgstr "cài đặt chế độ theo vết (xem git-pull(1))"
 
-#: builtin/branch.c:753
+#: builtin/branch.c:797
 msgid "change upstream info"
 msgstr "thay đổi thông tin thượng nguồn (upstream)"
 
-#: builtin/branch.c:757
+#: builtin/branch.c:801
 msgid "use colored output"
 msgstr "sử dụng kết xuất có tô màu"
 
-#: builtin/branch.c:758
+#: builtin/branch.c:802
 msgid "act on remote-tracking branches"
 msgstr "thao tác trên nhánh “remote-tracking”"
 
-#: builtin/branch.c:761 builtin/branch.c:767 builtin/branch.c:788
-#: builtin/branch.c:794 builtin/commit.c:1366 builtin/commit.c:1367
-#: builtin/commit.c:1368 builtin/commit.c:1369 builtin/tag.c:468
+#: builtin/branch.c:805 builtin/branch.c:811 builtin/branch.c:832
+#: builtin/branch.c:838 builtin/commit.c:1433 builtin/commit.c:1434
+#: builtin/commit.c:1435 builtin/commit.c:1436 builtin/tag.c:468
 msgid "commit"
 msgstr "commit"
 
-#: builtin/branch.c:762 builtin/branch.c:768
+#: builtin/branch.c:806 builtin/branch.c:812
 msgid "print only branches that contain the commit"
 msgstr "chỉ hiển thị những nhánh mà nó chứa lần chuyển giao"
 
-#: builtin/branch.c:774
+#: builtin/branch.c:818
 msgid "Specific git-branch actions:"
 msgstr "Hành động git-branch:"
 
-#: builtin/branch.c:775
+#: builtin/branch.c:819
 msgid "list both remote-tracking and local branches"
 msgstr "liệt kê cả nhánh “remote-tracking” và nội bộ"
 
-#: builtin/branch.c:777
+#: builtin/branch.c:821
 msgid "delete fully merged branch"
 msgstr "xóa một cách đầy đủ nhánh đã hòa trộn"
 
-#: builtin/branch.c:778
+#: builtin/branch.c:822
 msgid "delete branch (even if not merged)"
 msgstr "xoá nhánh (cho dù là chưa được hòa trộn)"
 
-#: builtin/branch.c:779
+#: builtin/branch.c:823
 msgid "move/rename a branch and its reflog"
 msgstr "di chuyển hay đổi tên một nhánh và reflog của nó"
 
-#: builtin/branch.c:780
+#: builtin/branch.c:824
 msgid "move/rename a branch, even if target exists"
 msgstr "di chuyển hoặc đổi tên một nhánh, thậm chí cả khi đích đã có sẵn"
 
-#: builtin/branch.c:781
+#: builtin/branch.c:825
 msgid "list branch names"
 msgstr "liệt kê các tên nhánh"
 
-#: builtin/branch.c:782
+#: builtin/branch.c:826
 msgid "create the branch's reflog"
 msgstr "tạo reflog của nhánh"
 
-#: builtin/branch.c:784
+#: builtin/branch.c:828
 msgid "edit the description for the branch"
 msgstr "sửa mô tả cho nhánh"
 
-#: builtin/branch.c:785
+#: builtin/branch.c:829
 msgid "force creation (when already exists)"
 msgstr "ép buộc tạo (khi đã sẵn tồn tại rồi)"
 
-#: builtin/branch.c:788
+#: builtin/branch.c:832
 msgid "print only not merged branches"
 msgstr "chỉ hiển thị các nhánh chưa được hòa trộn"
 
-#: builtin/branch.c:794
+#: builtin/branch.c:838
 msgid "print only merged branches"
 msgstr "chỉ hiển thị các nhánh được hòa trộn"
 
-#: builtin/branch.c:798
+#: builtin/branch.c:842
 msgid "list branches in columns"
 msgstr "liệt kê các nhánh trong các cột"
 
-#: builtin/branch.c:811
+#: builtin/branch.c:855
 msgid "Failed to resolve HEAD as a valid ref."
 msgstr "Gặp lỗi khi phân giải HEAD như là một tham chiếu (ref) hợp lệ."
 
-#: builtin/branch.c:816 builtin/clone.c:561
+#: builtin/branch.c:860 builtin/clone.c:630
 msgid "HEAD not found below refs/heads!"
 msgstr "không tìm thấy HEAD ở dưới refs/heads!"
 
-#: builtin/branch.c:839
+#: builtin/branch.c:883
 msgid "--column and --verbose are incompatible"
 msgstr "--column và --verbose xung khắc nhau"
 
-#: builtin/branch.c:845
+#: builtin/branch.c:889 builtin/branch.c:928
 msgid "branch name required"
 msgstr "cần tên nhánh"
 
-#: builtin/branch.c:860
+#: builtin/branch.c:904
 msgid "Cannot give description to detached HEAD"
 msgstr "Không thể đưa ra mô tả HEAD đã tách rời"
 
-#: builtin/branch.c:865
+#: builtin/branch.c:909
 msgid "cannot edit description of more than one branch"
 msgstr "không thể sửa mô tả cho nhiều hơn một nhánh"
 
-#: builtin/branch.c:872
+#: builtin/branch.c:916
 #, c-format
 msgid "No commit on branch '%s' yet."
 msgstr "Vẫn chưa chuyển giao trên nhánh “%s”."
 
-#: builtin/branch.c:875
+#: builtin/branch.c:919
 #, c-format
 msgid "No branch named '%s'."
 msgstr "Không có nhánh nào có tên “%s”."
 
-#: builtin/branch.c:888
+#: builtin/branch.c:934
 msgid "too many branches for a rename operation"
 msgstr "quá nhiều nhánh dành cho thao tác đổi tên"
 
-#: builtin/branch.c:893
+#: builtin/branch.c:939
+msgid "too many branches to set new upstream"
+msgstr "quá nhiều nhánh được đặt cho dòng ngược (upstream) mới"
+
+#: builtin/branch.c:943
+#, c-format
+msgid ""
+"could not set upstream of HEAD to %s when it does not point to any branch."
+msgstr ""
+"không thể đặt dòng ngược (upstream) của HEAD thành %s khi mà nó chẳng chỉ "
+"đến nhánh nào cả."
+
+#: builtin/branch.c:946 builtin/branch.c:968 builtin/branch.c:990
+#, c-format
+msgid "no such branch '%s'"
+msgstr "không có nhánh nào như thế “%s”"
+
+#: builtin/branch.c:950
 #, c-format
 msgid "branch '%s' does not exist"
 msgstr "nhánh “%s” chưa sẵn có"
 
-#: builtin/branch.c:905
+#: builtin/branch.c:962
+msgid "too many branches to unset upstream"
+msgstr "quá nhiều nhánh để bỏ đặt ngược dòng (upstream)"
+
+#: builtin/branch.c:966
+msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgstr ""
+"không thể bỏ đặt ngược dòng (upstream) của HEAD không chỉ đến một nhánh nào "
+"cả."
+
+#: builtin/branch.c:972
 #, c-format
 msgid "Branch '%s' has no upstream information"
 msgstr "Nhánh “%s” không có thông tin thượng nguồn (upstream)"
 
-#: builtin/branch.c:920
+#: builtin/branch.c:987
+msgid "it does not make sense to create 'HEAD' manually"
+msgstr "không hợp lý khi tạo “HEAD” thủ công "
+
+#: builtin/branch.c:993
 msgid "-a and -r options to 'git branch' do not make sense with a branch name"
 msgstr ""
 "hai tùy chọn -a và -r áp dụng cho lệnh “git branch” không hợp lý đối với tên "
 "nhánh"
 
-#: builtin/branch.c:923
+#: builtin/branch.c:996
 #, c-format
 msgid ""
 "The --set-upstream flag is deprecated and will be removed. Consider using --"
@@ -2638,7 +2974,7 @@ msgstr ""
 "Cờ --set-upstream bị phản đối và sẽ bị xóa bỏ. Nên dùng --track hoặc --set-"
 "upstream-to\n"
 
-#: builtin/branch.c:940
+#: builtin/branch.c:1013
 #, c-format
 msgid ""
 "\n"
@@ -2649,12 +2985,12 @@ msgstr ""
 "Nếu bạn muốn “%s” theo dõi “%s”, thực hiện lệnh sau:\n"
 "\n"
 
-#: builtin/branch.c:941
+#: builtin/branch.c:1014
 #, c-format
 msgid "    git branch -d %s\n"
 msgstr "    git branch -d %s\n"
 
-#: builtin/branch.c:942
+#: builtin/branch.c:1015
 #, c-format
 msgid "    git branch --set-upstream-to %s\n"
 msgstr "    git branch --set-upstream-to %s\n"
@@ -2672,44 +3008,44 @@ msgstr "Cần một kho chứa để mà tạo một bundle."
 msgid "Need a repository to unbundle."
 msgstr "Cần một kho chứa để mà bung một bundle."
 
-#: builtin/cat-file.c:247
+#: builtin/cat-file.c:285
 msgid "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"
 msgstr "git cat-file (-t|-s|-e|-p|<kiểu>|--textconv) <đối tượng>"
 
-#: builtin/cat-file.c:248
+#: builtin/cat-file.c:286
 msgid "git cat-file (--batch|--batch-check) < <list_of_objects>"
 msgstr "git cat-file (--batch|--batch-check) < <danh-sách-đối-tượng>"
 
-#: builtin/cat-file.c:266
+#: builtin/cat-file.c:323
 msgid "<type> can be one of: blob, tree, commit, tag"
 msgstr "<kiểu> là một trong số: blob, tree, commit, tag"
 
-#: builtin/cat-file.c:267
+#: builtin/cat-file.c:324
 msgid "show object type"
 msgstr "hiển thị kiểu đối tượng"
 
-#: builtin/cat-file.c:268
+#: builtin/cat-file.c:325
 msgid "show object size"
 msgstr "hiển thị kích thước đối tượng"
 
-#: builtin/cat-file.c:270
+#: builtin/cat-file.c:327
 msgid "exit with zero when there's no error"
 msgstr "thoát với 0 khi không có lỗi"
 
-#: builtin/cat-file.c:271
+#: builtin/cat-file.c:328
 msgid "pretty-print object's content"
 msgstr "in nội dung đối tượng dạng dễ đọc"
 
-#: builtin/cat-file.c:273
+#: builtin/cat-file.c:330
 msgid "for blob objects, run textconv on object's content"
 msgstr "với đối tượng blob, chạy lệnh textconv trên nội dung của đối tượng"
 
-#: builtin/cat-file.c:275
+#: builtin/cat-file.c:332
 msgid "show info and content of objects fed from the standard input"
 msgstr ""
 "hiển thị thông tin và nội dung của các đối tượng lấy từ đầu vào tiêu chuẩn"
 
-#: builtin/cat-file.c:278
+#: builtin/cat-file.c:335
 msgid "show info about objects fed from the standard input"
 msgstr "hiển thị các thông tin về đối tượng fed  từ đầu vào tiêu chuẩn"
 
@@ -2738,30 +3074,55 @@ msgstr "đọc tên tập tin từ đầu vào tiêu chuẩn"
 msgid "input paths are terminated by a null character"
 msgstr "các đường dẫn được  ngăn cách bởi ký tự null"
 
-#: builtin/check-ignore.c:18 builtin/checkout.c:1012 builtin/gc.c:177
+#: builtin/check-ignore.c:18 builtin/checkout.c:1054 builtin/gc.c:177
 msgid "suppress progress reporting"
 msgstr "chặn các báo cáo tiến trình hoạt động"
 
-#: builtin/check-ignore.c:151
+#: builtin/check-ignore.c:26
+msgid "show non-matching input paths"
+msgstr "hiển thị những đường dẫn đầu vào không khớp với mẫu"
+
+#: builtin/check-ignore.c:143
 msgid "cannot specify pathnames with --stdin"
 msgstr "không thể chỉ định các tên đường dẫn với --stdin"
 
-#: builtin/check-ignore.c:154
+#: builtin/check-ignore.c:146
 msgid "-z only makes sense with --stdin"
 msgstr "-z chỉ hợp lý với --stdin"
 
-#: builtin/check-ignore.c:156
+#: builtin/check-ignore.c:148
 msgid "no path specified"
 msgstr "chưa ghi rõ đường dẫn"
 
-#: builtin/check-ignore.c:160
+#: builtin/check-ignore.c:152
 msgid "--quiet is only valid with a single pathname"
 msgstr "--quiet chỉ hợp lệ với tên đường dẫn đơn"
 
-#: builtin/check-ignore.c:162
+#: builtin/check-ignore.c:154
 msgid "cannot have both --quiet and --verbose"
 msgstr "không thể dùng cả hai tùy chọn --quiet và --verbose"
 
+#: builtin/check-ignore.c:157
+msgid "--non-matching is only valid with --verbose"
+msgstr "tùy-chọn --non-matching chỉ hợp lệ khi dùng với --verbose"
+
+#: builtin/check-mailmap.c:8
+msgid "git check-mailmap [options] <contact>..."
+msgstr "git check-mailmap [các-tùy-chọn] <danh-bạ>..."
+
+#: builtin/check-mailmap.c:13
+msgid "also read contacts from stdin"
+msgstr "đồng thời đọc các danh bạ từ đầu vào tiêu chuẩn"
+
+#: builtin/check-mailmap.c:24
+#, c-format
+msgid "unable to parse contact: %s"
+msgstr "không thể phân tích danh bạ: “%s”"
+
+#: builtin/check-mailmap.c:47
+msgid "no contacts specified"
+msgstr "chưa chỉ ra danh bạ"
+
 #: builtin/checkout-index.c:126
 msgid "git checkout-index [options] [--] [<file>...]"
 msgstr "git checkout-index [các-tùy-chọn] [--] [<tập-tin>...]"
@@ -2815,107 +3176,107 @@ msgstr "git checkout [các-tùy-chọn] <nhánh>"
 msgid "git checkout [options] [<branch>] -- <file>..."
 msgstr "git checkout [các-tùy-chọn] [<nhánh>] -- <tập-tin>..."
 
-#: builtin/checkout.c:116 builtin/checkout.c:149
+#: builtin/checkout.c:117 builtin/checkout.c:150
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "đường dẫn “%s” không có các phiên bản của chúng ta"
 
-#: builtin/checkout.c:118 builtin/checkout.c:151
+#: builtin/checkout.c:119 builtin/checkout.c:152
 #, c-format
 msgid "path '%s' does not have their version"
 msgstr "đường dẫn “%s” không có các phiên bản của chúng"
 
-#: builtin/checkout.c:134
+#: builtin/checkout.c:135
 #, c-format
 msgid "path '%s' does not have all necessary versions"
 msgstr "đường dẫn “%s” không có tất cả các phiên bản cần thiết"
 
-#: builtin/checkout.c:178
+#: builtin/checkout.c:179
 #, c-format
 msgid "path '%s' does not have necessary versions"
 msgstr "đường dẫn “%s” không có các phiên bản cần thiết"
 
-#: builtin/checkout.c:195
+#: builtin/checkout.c:196
 #, c-format
 msgid "path '%s': cannot merge"
 msgstr "đường dẫn “%s”: không thể hòa trộn"
 
-#: builtin/checkout.c:212
+#: builtin/checkout.c:213
 #, c-format
 msgid "Unable to add merge result for '%s'"
 msgstr "Không thể thêm kết quả hòa trộn cho “%s”"
 
-#: builtin/checkout.c:236 builtin/checkout.c:239 builtin/checkout.c:242
-#: builtin/checkout.c:245
+#: builtin/checkout.c:237 builtin/checkout.c:240 builtin/checkout.c:243
+#: builtin/checkout.c:246
 #, c-format
 msgid "'%s' cannot be used with updating paths"
 msgstr "“%s” không thể được sử dụng với các đường dẫn cập nhật"
 
-#: builtin/checkout.c:248 builtin/checkout.c:251
+#: builtin/checkout.c:249 builtin/checkout.c:252
 #, c-format
 msgid "'%s' cannot be used with %s"
 msgstr "“%s” không thể được sử dụng với %s"
 
-#: builtin/checkout.c:254
+#: builtin/checkout.c:255
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
 msgstr ""
 "Không thể cập nhật các đường dẫn và chuyển đến nhánh “%s” cùng một lúc."
 
-#: builtin/checkout.c:265 builtin/checkout.c:426
+#: builtin/checkout.c:266 builtin/checkout.c:458
 msgid "corrupt index file"
 msgstr "tập tin ghi bảng mục lục bị hỏng"
 
-#: builtin/checkout.c:295 builtin/checkout.c:302
+#: builtin/checkout.c:329 builtin/checkout.c:336
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "đường dẫn “%s” không được hòa trộn"
 
-#: builtin/checkout.c:448
+#: builtin/checkout.c:480
 msgid "you need to resolve your current index first"
 msgstr "bạn cần phải phân giải bảng mục lục hiện tại của bạn trước đã"
 
-#: builtin/checkout.c:569
+#: builtin/checkout.c:601
 #, c-format
 msgid "Can not do reflog for '%s'\n"
 msgstr "Không thể thực hiện reflog cho “%s”\n"
 
-#: builtin/checkout.c:602
+#: builtin/checkout.c:639
 msgid "HEAD is now at"
 msgstr "HEAD hiện giờ tại"
 
-#: builtin/checkout.c:609
+#: builtin/checkout.c:646
 #, c-format
 msgid "Reset branch '%s'\n"
 msgstr "Đặt lại nhánh “%s”\n"
 
-#: builtin/checkout.c:612
+#: builtin/checkout.c:649
 #, c-format
 msgid "Already on '%s'\n"
 msgstr "Đã sẵn sàng trên “%s”\n"
 
-#: builtin/checkout.c:616
+#: builtin/checkout.c:653
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
 msgstr "Đã chuyển tới và reset nhánh “%s”\n"
 
-#: builtin/checkout.c:618 builtin/checkout.c:955
+#: builtin/checkout.c:655 builtin/checkout.c:997
 #, c-format
 msgid "Switched to a new branch '%s'\n"
 msgstr "Đã chuyển đến nhánh mới “%s”\n"
 
-#: builtin/checkout.c:620
+#: builtin/checkout.c:657
 #, c-format
 msgid "Switched to branch '%s'\n"
 msgstr "Đã chuyển đến nhánh “%s”\n"
 
-#: builtin/checkout.c:676
+#: builtin/checkout.c:713
 #, c-format
 msgid " ... and %d more.\n"
 msgstr " ... và nhiều hơn %d.\n"
 
 #. The singular version
-#: builtin/checkout.c:682
+#: builtin/checkout.c:719
 #, c-format
 msgid ""
 "Warning: you are leaving %d commit behind, not connected to\n"
@@ -2940,7 +3301,7 @@ msgstr[1] ""
 "\n"
 "%s\n"
 
-#: builtin/checkout.c:700
+#: builtin/checkout.c:737
 #, c-format
 msgid ""
 "If you want to keep them by creating a new branch, this may be a good time\n"
@@ -2956,134 +3317,138 @@ msgstr ""
 " git branch tên_nhánh_mới %s\n"
 "\n"
 
-#: builtin/checkout.c:730
+#: builtin/checkout.c:767
 msgid "internal error in revision walk"
-msgstr "lỗi nội bộ trong khi di chuyển qua các điểm xét lại"
+msgstr "lỗi nội bộ trong khi di chuyển qua các điểm xét duyệt"
 
-#: builtin/checkout.c:734
+#: builtin/checkout.c:771
 msgid "Previous HEAD position was"
 msgstr "Vị trí kế trước của HEAD là"
 
-#: builtin/checkout.c:761 builtin/checkout.c:950
+#: builtin/checkout.c:798 builtin/checkout.c:992
 msgid "You are on a branch yet to be born"
 msgstr "Bạn tại nhánh mà nó chưa hề được sinh ra"
 
 #. case (1)
-#: builtin/checkout.c:886
+#: builtin/checkout.c:928
 #, c-format
 msgid "invalid reference: %s"
 msgstr "tham chiếu sai: %s"
 
 #. case (1): want a tree
-#: builtin/checkout.c:925
+#: builtin/checkout.c:967
 #, c-format
 msgid "reference is not a tree: %s"
 msgstr "tham chiếu không phải là một cây (tree):%s"
 
-#: builtin/checkout.c:964
+#: builtin/checkout.c:1006
 msgid "paths cannot be used with switching branches"
 msgstr "các đường dẫn không thể dùng cùng với các nhánh chuyển"
 
-#: builtin/checkout.c:967 builtin/checkout.c:971
+#: builtin/checkout.c:1009 builtin/checkout.c:1013
 #, c-format
 msgid "'%s' cannot be used with switching branches"
 msgstr "“%s” không thể được sử dụng với các nhánh chuyển"
 
-#: builtin/checkout.c:975 builtin/checkout.c:978 builtin/checkout.c:983
-#: builtin/checkout.c:986
+#: builtin/checkout.c:1017 builtin/checkout.c:1020 builtin/checkout.c:1025
+#: builtin/checkout.c:1028
 #, c-format
 msgid "'%s' cannot be used with '%s'"
 msgstr "“%s” không thể được sử dụng với “%s”"
 
-#: builtin/checkout.c:991
+#: builtin/checkout.c:1033
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
 msgstr "Không thể chuyển nhánh đến một non-commit “%s”"
 
-#: builtin/checkout.c:1013 builtin/checkout.c:1015 builtin/clone.c:89
+#: builtin/checkout.c:1055 builtin/checkout.c:1057 builtin/clone.c:89
 #: builtin/remote.c:169 builtin/remote.c:171
 msgid "branch"
 msgstr "nhánh"
 
-#: builtin/checkout.c:1014
+#: builtin/checkout.c:1056
 msgid "create and checkout a new branch"
 msgstr "tạo và checkout một nhánh mới"
 
-#: builtin/checkout.c:1016
+#: builtin/checkout.c:1058
 msgid "create/reset and checkout a branch"
 msgstr "create/reset và checkout một nhánh"
 
-#: builtin/checkout.c:1017
+#: builtin/checkout.c:1059
 msgid "create reflog for new branch"
 msgstr "tạo reflog cho nhánh mới"
 
-#: builtin/checkout.c:1018
+#: builtin/checkout.c:1060
 msgid "detach the HEAD at named commit"
 msgstr "rời bỏ HEAD tại lần chuyển giao danh nghĩa"
 
-#: builtin/checkout.c:1019
+#: builtin/checkout.c:1061
 msgid "set upstream info for new branch"
 msgstr "đặt thông tin thượng nguồn (upstream) cho nhánh mới"
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new branch"
 msgstr "nhánh mới"
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new unparented branch"
 msgstr "nhánh mồ côi mới"
 
-#: builtin/checkout.c:1022
+#: builtin/checkout.c:1064
 msgid "checkout our version for unmerged files"
 msgstr ""
 "lấy ra (checkout) phiên bản của chúng ta cho các tập tin chưa được hòa trộn"
 
-#: builtin/checkout.c:1024
+#: builtin/checkout.c:1066
 msgid "checkout their version for unmerged files"
 msgstr ""
 "lấy ra (checkout) phiên bản của chúng họ cho các tập tin chưa được hòa trộn"
 
-#: builtin/checkout.c:1026
+#: builtin/checkout.c:1068
 msgid "force checkout (throw away local modifications)"
 msgstr "ép buộc lấy ra (checkout) (bỏ đi những thay đổi nội bộ)"
 
-#: builtin/checkout.c:1027
+#: builtin/checkout.c:1069
 msgid "perform a 3-way merge with the new branch"
 msgstr "thực hiện hòa trộn kiểu 3-way với nhánh mới"
 
-#: builtin/checkout.c:1028 builtin/merge.c:215
+#: builtin/checkout.c:1070 builtin/merge.c:232
 msgid "update ignored files (default)"
 msgstr "cập nhật các tập tin bị bỏ qua (mặc định)"
 
-#: builtin/checkout.c:1029 builtin/log.c:1147 parse-options.h:245
+#: builtin/checkout.c:1071 builtin/log.c:1208 parse-options.h:249
 msgid "style"
 msgstr "kiểu"
 
-#: builtin/checkout.c:1030
+#: builtin/checkout.c:1072
 msgid "conflict style (merge or diff3)"
 msgstr "xung đột kiểu (hòa trộn hay diff3)"
 
-#: builtin/checkout.c:1033
+#: builtin/checkout.c:1075
+msgid "do not limit pathspecs to sparse entries only"
+msgstr "không giới hạn pathspecs chỉ thành các mục thưa thớt"
+
+#: builtin/checkout.c:1077
 msgid "second guess 'git checkout no-such-branch'"
 msgstr "gợi ý thứ hai “git checkout không-nhánh-nào-như-vậy”"
 
-#: builtin/checkout.c:1057
+#: builtin/checkout.c:1101
 msgid "-b, -B and --orphan are mutually exclusive"
 msgstr "Tùy chọn -b|-B và --orphan loại từ lẫn nhau"
 
-#: builtin/checkout.c:1074
+#: builtin/checkout.c:1118
 msgid "--track needs a branch name"
 msgstr "--track cần tên một nhánh"
 
-#: builtin/checkout.c:1081
+#: builtin/checkout.c:1125
 msgid "Missing branch name; try -b"
 msgstr "Thiếu tên nhánh; hãy thử -b"
 
-#: builtin/checkout.c:1116
+#: builtin/checkout.c:1160
 msgid "invalid path specification"
 msgstr "đường dẫn đã cho không hợp lệ"
 
-#: builtin/checkout.c:1123
+#: builtin/checkout.c:1167
 #, c-format
 msgid ""
 "Cannot update paths and switch to branch '%s' at the same time.\n"
@@ -3093,12 +3458,12 @@ msgstr ""
 "Bạn đã có ý định checkout “%s” cái mà không thể được phân giải như là lần "
 "chuyển giao (commit)?"
 
-#: builtin/checkout.c:1128
+#: builtin/checkout.c:1172
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
 msgstr "git checkout: --detach không nhận một đối số đường dẫn “%s”"
 
-#: builtin/checkout.c:1132
+#: builtin/checkout.c:1176
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
@@ -3106,89 +3471,192 @@ msgstr ""
 "git checkout: --ours/--theirs, --force và --merge là xung khắc với nhau khi\n"
 "checkout bảng mục lục (index)."
 
-#: builtin/clean.c:20
-msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
-msgstr "git clean [-d] [-f] [-n] [-q] [-e <mẫu>] [-x | -X] [--] <đường-dẫn>..."
+#: builtin/clean.c:25
+msgid ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+msgstr ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <mẫu>] [-x | -X] [--] <đường-dẫn>..."
 
-#: builtin/clean.c:24
+#: builtin/clean.c:29
 #, c-format
 msgid "Removing %s\n"
 msgstr "Đang gỡ bỏ %s\n"
 
-#: builtin/clean.c:25
+#: builtin/clean.c:30
 #, c-format
 msgid "Would remove %s\n"
 msgstr "Có thể gỡ bỏ %s\n"
 
-#: builtin/clean.c:26
+#: builtin/clean.c:31
 #, c-format
 msgid "Skipping repository %s\n"
 msgstr "Đang bỏ qua kho chứa %s\n"
 
-#: builtin/clean.c:27
+#: builtin/clean.c:32
 #, c-format
 msgid "Would skip repository %s\n"
 msgstr "Nên bỏ qua kho chứa %s\n"
 
-#: builtin/clean.c:28
+#: builtin/clean.c:33
 #, c-format
 msgid "failed to remove %s"
 msgstr "gặp lỗi khi gỡ bỏ %s"
 
-#: builtin/clean.c:160
+#: builtin/clean.c:293
+msgid ""
+"Prompt help:\n"
+"1          - select a numbered item\n"
+"foo        - select item based on unique prefix\n"
+"           - (empty) select nothing"
+msgstr ""
+"Trợ giúp về nhắc:\n"
+"1          - chọn một mục được đánh số\n"
+"foo        - chọn mục trên cơ sở tiền tố duy nhất\n"
+"           - (trống rỗng) không chọn gì cả"
+
+#: builtin/clean.c:297
+msgid ""
+"Prompt help:\n"
+"1          - select a single item\n"
+"3-5        - select a range of items\n"
+"2-3,6-9    - select multiple ranges\n"
+"foo        - select item based on unique prefix\n"
+"-...       - unselect specified items\n"
+"*          - choose all items\n"
+"           - (empty) finish selecting"
+msgstr ""
+"Trợ giúp về nhắc:\n"
+"1          - chọn một mục đơn\n"
+"3-5        - chọn một vùng\n"
+"2-3,6-9    - chọn nhiều vùng\n"
+"foo        - chọn mục dựa trên tiền tố duy nhất\n"
+"-...       - không chọn các mục đã chỉ ra\n"
+"*          - chọn tất\n"
+"           - (trống rỗng) kết thúc việc chọn"
+
+#: builtin/clean.c:515
+#, c-format
+msgid "Huh (%s)?"
+msgstr "Hả (%s)?"
+
+#: builtin/clean.c:658
+#, c-format
+msgid "Input ignore patterns>> "
+msgstr "Mẫu để lọc các tập tin cần lờ đi đầu vào>>"
+
+#: builtin/clean.c:695
+#, c-format
+msgid "WARNING: Cannot find items matched by: %s"
+msgstr "CẢNH BÁO: Không tìm thấy các mục được khớp bởi: %s"
+
+#: builtin/clean.c:716
+msgid "Select items to delete"
+msgstr "Chọn mục muốn xóa"
+
+#: builtin/clean.c:756
+#, c-format
+msgid "remove %s? "
+msgstr "gỡ bỏ “%s”?"
+
+#: builtin/clean.c:781
+msgid "Bye."
+msgstr "Tạm biệt."
+
+#: builtin/clean.c:789
+msgid ""
+"clean               - start cleaning\n"
+"filter by pattern   - exclude items from deletion\n"
+"select by numbers   - select items to be deleted by numbers\n"
+"ask each            - confirm each deletion (like \"rm -i\")\n"
+"quit                - stop cleaning\n"
+"help                - this screen\n"
+"?                   - help for prompt selection"
+msgstr ""
+"clean               - bắt đầu dọn dẹp\n"
+"filter by pattern   - loại trừ các mục khỏi việc xóa\n"
+"select by numbers   - chọn các mục cần xóa bằng số\n"
+"ask each            - xác nhận trước mỗi lần xóa (giống như \"rm -i\")\n"
+"quit                - dừng việc dọn dẹp lại\n"
+"help                - hiển thị chính trợ giúp này\n"
+"?                   - trợ giúp dành cho chọn bằng cách nhắc"
+
+#: builtin/clean.c:816
+msgid "*** Commands ***"
+msgstr "*** Lệnh ***"
+
+#: builtin/clean.c:817
+msgid "What now"
+msgstr "Giờ thì sao"
+
+#: builtin/clean.c:825
+msgid "Would remove the following item:"
+msgid_plural "Would remove the following items:"
+msgstr[0] "Có muốn gỡ bỏ mục sau đây không:"
+msgstr[1] "Có muốn gỡ bỏ các mục sau đây không:"
+
+#: builtin/clean.c:842
+msgid "No more files to clean, exiting."
+msgstr "Không còn tập-tin nào để dọn dẹp, đang thoát ra."
+
+#: builtin/clean.c:874
 msgid "do not print names of files removed"
 msgstr "không hiển thị tên của các tập tin đã gỡ bỏ"
 
-#: builtin/clean.c:162
+#: builtin/clean.c:876
 msgid "force"
 msgstr "ép buộc"
 
-#: builtin/clean.c:164
+#: builtin/clean.c:877
+msgid "interactive cleaning"
+msgstr "sửa bằng cách tương tác"
+
+#: builtin/clean.c:879
 msgid "remove whole directories"
 msgstr "gỡ bỏ toàn bộ thư mục"
 
-#: builtin/clean.c:165 builtin/describe.c:413 builtin/grep.c:717
-#: builtin/ls-files.c:494 builtin/name-rev.c:231 builtin/show-ref.c:182
+#: builtin/clean.c:880 builtin/describe.c:420 builtin/grep.c:716
+#: builtin/ls-files.c:493 builtin/name-rev.c:315 builtin/show-ref.c:186
 msgid "pattern"
 msgstr "mẫu"
 
-#: builtin/clean.c:166
+#: builtin/clean.c:881
 msgid "add <pattern> to ignore rules"
 msgstr "thêm <mẫu> vào trong qui tắc bỏ qua"
 
-#: builtin/clean.c:167
+#: builtin/clean.c:882
 msgid "remove ignored files, too"
 msgstr "đồng thời gỡ bỏ cả các tập tin bị bỏ qua"
 
-#: builtin/clean.c:169
+#: builtin/clean.c:884
 msgid "remove only ignored files"
 msgstr "chỉ gỡ bỏ những tập tin bị bỏ qua"
 
-#: builtin/clean.c:187
+#: builtin/clean.c:902
 msgid "-x and -X cannot be used together"
 msgstr "-x và -X không thể dùng cùng một lúc với nhau"
 
-#: builtin/clean.c:191
+#: builtin/clean.c:906
 msgid ""
-"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+"clean.requireForce set to true and neither -i, -n nor -f given; refusing to "
+"clean"
 msgstr ""
-"clean.requireForce được đặt thành true và không đưa ra tùy chọn  -n mà cũng "
+"clean.requireForce được đặt thành true và không đưa ra tùy chọn -n mà cũng "
 "không -f; từ chối lệnh dọn dẹp (clean)"
 
-#: builtin/clean.c:194
+#: builtin/clean.c:909
 msgid ""
-"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
-"clean"
+"clean.requireForce defaults to true and neither -i, -n nor -f given; "
+"refusing to clean"
 msgstr ""
-"clean.requireForce mặc định được đặt thành true và không đưa ra tùy chọn  -n "
-"mà cũng không -f; từ chối lệnh dọn dẹp (clean)"
+"clean.requireForce mặc định được đặt là true và không đưa ra tùy chọn -n mà "
+"cũng không -f; từ chối lệnh dọn dẹp (clean)"
 
 #: builtin/clone.c:36
 msgid "git clone [options] [--] <repo> [<dir>]"
 msgstr "git clone [các-tùy-chọn] [--] <kho> [<t.mục>]"
 
-#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:212
-#: builtin/push.c:436
+#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:229
+#: builtin/push.c:462
 msgid "force progress reporting"
 msgstr "ép buộc báo cáo tiến trình"
 
@@ -3248,7 +3716,7 @@ msgstr "lấy ra nhánh (checkout <nhánh>) thay vì HEAD của máy chủ"
 msgid "path to git-upload-pack on the remote"
 msgstr "đường dẫn đến git-upload-pack trên máy chủ"
 
-#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:662
+#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:661
 msgid "depth"
 msgstr "độ sâu"
 
@@ -3276,122 +3744,154 @@ msgstr "khóa=giá trị"
 msgid "set config inside the new repository"
 msgstr "đặt cấu hình bên trong một kho chứa mới"
 
-#: builtin/clone.c:243
+#: builtin/clone.c:253
 #, c-format
-msgid "reference repository '%s' is not a local directory."
-msgstr "kho tham chiếu “%s” không phải là một thư mục nội bộ."
+msgid "reference repository '%s' is not a local repository."
+msgstr "kho tham chiếu “%s” không phải là một kho nội bộ."
 
-#: builtin/clone.c:306
+#: builtin/clone.c:316
 #, c-format
 msgid "failed to create directory '%s'"
 msgstr "tạo thư mục \"%s\" gặp lỗi"
 
-#: builtin/clone.c:308 builtin/diff.c:77
+#: builtin/clone.c:318 builtin/diff.c:77
 #, c-format
 msgid "failed to stat '%s'"
 msgstr "gặp lỗi stat (lấy trạng thái về) “%s”"
 
-#: builtin/clone.c:310
+#: builtin/clone.c:320
 #, c-format
 msgid "%s exists and is not a directory"
 msgstr "%s tồn tại nhưng không phải là một thư mục"
 
-#: builtin/clone.c:324
+#: builtin/clone.c:334
 #, c-format
 msgid "failed to stat %s\n"
 msgstr "lỗi stat (lấy trạng thái về) %s\n"
 
-#: builtin/clone.c:346
+#: builtin/clone.c:356
 #, c-format
 msgid "failed to create link '%s'"
 msgstr "gặp lỗi khi tạo được liên kết mềm %s"
 
-#: builtin/clone.c:350
+#: builtin/clone.c:360
 #, c-format
 msgid "failed to copy file to '%s'"
 msgstr "gặp lỗi khi chép tập tin tới “%s”"
 
-#: builtin/clone.c:373
+#: builtin/clone.c:383
 #, c-format
 msgid "done.\n"
 msgstr "hoàn tất.\n"
 
-#: builtin/clone.c:443
+#: builtin/clone.c:396
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry the checkout with 'git checkout -f HEAD'\n"
+msgstr ""
+"Việc nhân bản thành công, nhưng checkout gặp lỗi.\n"
+"Bạn kiểm tra kỹ xem cái gì được lấy ra bằng lệnh “git status”\n"
+"và thử checkout với lệnh “git checkout -f HEAD”\n"
+
+#: builtin/clone.c:475
 #, c-format
 msgid "Could not find remote branch %s to clone."
 msgstr "Không tìm thấy nhánh máy chủ %s để nhân bản (clone)."
 
-#: builtin/clone.c:552
+#: builtin/clone.c:555
+#, c-format
+msgid "Checking connectivity... "
+msgstr "Đang kiểm tra kết nối..."
+
+#: builtin/clone.c:558
+msgid "remote did not send all necessary objects"
+msgstr "máy chủ đã không gửi tất cả các đối tượng cần thiết"
+
+#: builtin/clone.c:560
+#, c-format
+msgid "done\n"
+msgstr "xong.\n"
+
+#: builtin/clone.c:621
 msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
 msgstr "refers HEAD máy chủ  chỉ đến ref không tồn tại, không thể checkout.\n"
 
-#: builtin/clone.c:690
+#: builtin/clone.c:652
+msgid "unable to checkout working tree"
+msgstr "không thể lấy ra (checkout) cây làm việc"
+
+#: builtin/clone.c:760
 msgid "Too many arguments."
 msgstr "Có quá nhiều đối số."
 
-#: builtin/clone.c:694
+#: builtin/clone.c:764
 msgid "You must specify a repository to clone."
 msgstr "Bạn phải chỉ định một kho để mà nhân bản (clone)."
 
-#: builtin/clone.c:705
+#: builtin/clone.c:775
 #, c-format
 msgid "--bare and --origin %s options are incompatible."
 msgstr "tùy chọn --bare và --origin %s xung khắc nhau."
 
-#: builtin/clone.c:708
+#: builtin/clone.c:778
 msgid "--bare and --separate-git-dir are incompatible."
 msgstr "tùy chọn --bare và --separate-git-dir xung khắc nhau."
 
-#: builtin/clone.c:721
+#: builtin/clone.c:791
 #, c-format
 msgid "repository '%s' does not exist"
 msgstr "kho chứa “%s” chưa tồn tại"
 
-#: builtin/clone.c:726
+#: builtin/clone.c:796
 msgid "--depth is ignored in local clones; use file:// instead."
 msgstr "--depth bị lờ đi khi nhân bản nội bộ; hãy sử dụng file:// để thay thế."
 
-#: builtin/clone.c:736
+#: builtin/clone.c:798
+msgid "--local is ignored"
+msgstr "--local bị lờ đi"
+
+#: builtin/clone.c:808
 #, c-format
 msgid "destination path '%s' already exists and is not an empty directory."
 msgstr "đường dẫn đích “%s” đã có từ trước và không phải là một thư mục rỗng."
 
-#: builtin/clone.c:746
+#: builtin/clone.c:818
 #, c-format
 msgid "working tree '%s' already exists."
 msgstr "cây làm việc “%s” đã sẵn tồn tại rồi."
 
-#: builtin/clone.c:759 builtin/clone.c:771
+#: builtin/clone.c:831 builtin/clone.c:843
 #, c-format
 msgid "could not create leading directories of '%s'"
 msgstr "không thể tạo các thư mục dẫn đầu của “%s”"
 
-#: builtin/clone.c:762
+#: builtin/clone.c:834
 #, c-format
 msgid "could not create work tree dir '%s'."
 msgstr "không thể tạo cây thư mục làm việc dir “%s”."
 
-#: builtin/clone.c:781
+#: builtin/clone.c:853
 #, c-format
 msgid "Cloning into bare repository '%s'...\n"
 msgstr "Đang nhân bản thành kho chứa bare “%s”...\n"
 
-#: builtin/clone.c:783
+#: builtin/clone.c:855
 #, c-format
 msgid "Cloning into '%s'...\n"
 msgstr "Đang nhân bản thành “%s”...\n"
 
-#: builtin/clone.c:818
+#: builtin/clone.c:890
 #, c-format
 msgid "Don't know how to clone %s"
 msgstr "Không biết làm cách nào để nhân bản (clone) %s"
 
-#: builtin/clone.c:867
+#: builtin/clone.c:942
 #, c-format
 msgid "Remote branch %s not found in upstream %s"
 msgstr "Nhánh máy chủ %s không tìm thấy trong thượng nguồn (upstream) %s"
 
-#: builtin/clone.c:874
+#: builtin/clone.c:949
 msgid "You appear to have cloned an empty repository."
 msgstr "Bạn hình như là đã nhân bản một kho trống rỗng."
 
@@ -3427,15 +3927,15 @@ msgstr "Chèn thêm khoảng trắng giữa các cột"
 msgid "--command must be the first argument"
 msgstr "--command phải là đối số đầu tiên"
 
-#: builtin/commit.c:34
+#: builtin/commit.c:35
 msgid "git commit [options] [--] <pathspec>..."
 msgstr "git commit [các-tùy-chọn] [--] <pathspec>..."
 
-#: builtin/commit.c:39
+#: builtin/commit.c:40
 msgid "git status [options] [--] <pathspec>..."
 msgstr "git status [các-tùy-chọn] [--] <pathspec>..."
 
-#: builtin/commit.c:44
+#: builtin/commit.c:45
 msgid ""
 "Your name and email address were configured automatically based\n"
 "on your username and hostname. Please check that they are accurate.\n"
@@ -3463,7 +3963,7 @@ msgstr ""
 "\n"
 "    git commit --amend --reset-author\n"
 
-#: builtin/commit.c:56
+#: builtin/commit.c:57
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -3476,114 +3976,132 @@ msgstr ""
 "hoặc là bạn gỡ bỏ các lần chuyển giao một cách hoàn toàn bằng lệnh:\n"
 "\"git reset HEAD^\".\n"
 
-#: builtin/commit.c:61
+#: builtin/commit.c:62
 msgid ""
 "The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
 "If you wish to commit it anyway, use:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"Otherwise, please use 'git reset'\n"
 msgstr ""
 "Lần cherry-pick trước hiện nay trống rỗng, có lẽ là bởi vì sự phân giải xung "
 "đột.\n"
-"Nếu bạn muốn chuyển giao nó cho dù thế nào đi nữa, sử dụng:\n"
+"Nếu bạn vẫn muốn chuyển giao nó cho dù thế nào đi nữa, sử dụng:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"Nếu không, hãy thử dùng “git reset”\n"
 
-#: builtin/commit.c:258
+#: builtin/commit.c:69
+msgid "Otherwise, please use 'git reset'\n"
+msgstr "Nếu không được thì dùng lệnh \"git reset\"\n"
+
+#: builtin/commit.c:72
+msgid ""
+"If you wish to skip this commit, use:\n"
+"\n"
+"    git reset\n"
+"\n"
+"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
+"the remaining commits.\n"
+msgstr ""
+"Nếu bạn muốn bỏ qua lần chuyển giao này thì dùng:\n"
+"\n"
+"    git reset\n"
+"\n"
+"Thế thì \"git cherry-pick --continue\" sẽ phục hồi lại việc cherry-pick\n"
+"những lần chuyển giao còn lại.\n"
+
+#: builtin/commit.c:279
 msgid "failed to unpack HEAD tree object"
 msgstr "gặp lỗi khi tháo dỡ HEAD đối tượng cây"
 
-#: builtin/commit.c:300
+#: builtin/commit.c:321
 msgid "unable to create temporary index"
 msgstr "không thể tạo bảng mục lục tạm thời"
 
-#: builtin/commit.c:306
+#: builtin/commit.c:327
 msgid "interactive add failed"
 msgstr "việc thêm tương tác gặp lỗi"
 
-#: builtin/commit.c:339 builtin/commit.c:360 builtin/commit.c:410
+#: builtin/commit.c:360 builtin/commit.c:381 builtin/commit.c:431
 msgid "unable to write new_index file"
 msgstr "không thể ghi tập tin lưu bảng mục lục mới (new_index)"
 
-#: builtin/commit.c:391
+#: builtin/commit.c:412
 msgid "cannot do a partial commit during a merge."
 msgstr ""
 "không thể thực hiện việc chuyển giao (commit) cục bộ trong khi đang được hòa "
 "trộn."
 
-#: builtin/commit.c:393
+#: builtin/commit.c:414
 msgid "cannot do a partial commit during a cherry-pick."
 msgstr ""
 "không thể thực hiện việc chuyển giao (commit) bộ phận trong khi đang cherry-"
 "pick."
 
-#: builtin/commit.c:403
+#: builtin/commit.c:424
 msgid "cannot read the index"
 msgstr "không đọc được bảng mục lục"
 
-#: builtin/commit.c:423
+#: builtin/commit.c:444
 msgid "unable to write temporary index file"
 msgstr "không thể ghi tập tin lưu bảng mục lục tạm thời"
 
-#: builtin/commit.c:511 builtin/commit.c:517
+#: builtin/commit.c:535 builtin/commit.c:541
 #, c-format
 msgid "invalid commit: %s"
 msgstr "lần chuyển giao (commit) không hợp lệ: %s"
 
-#: builtin/commit.c:540
+#: builtin/commit.c:563
 msgid "malformed --author parameter"
 msgstr "đối số --author bị dị hình"
 
-#: builtin/commit.c:560
+#: builtin/commit.c:583
 #, c-format
 msgid "Malformed ident string: '%s'"
 msgstr "Chuỗi thụt lề đầu dòng dị hình: “%s”"
 
-#: builtin/commit.c:598 builtin/commit.c:631 builtin/commit.c:954
+#: builtin/commit.c:621 builtin/commit.c:654 builtin/commit.c:982
 #, c-format
 msgid "could not lookup commit %s"
 msgstr "không thể tìm kiếm commit (lần chuyển giao) %s"
 
-#: builtin/commit.c:610 builtin/shortlog.c:272
+#: builtin/commit.c:633 builtin/shortlog.c:271
 #, c-format
 msgid "(reading log message from standard input)\n"
 msgstr "(đang đọc thông điệp nhật ký từ đầu vào tiêu chuẩn)\n"
 
-#: builtin/commit.c:612
+#: builtin/commit.c:635
 msgid "could not read log from standard input"
 msgstr "không thể đọc nhật ký từ đầu vào tiêu chuẩn"
 
-#: builtin/commit.c:616
+#: builtin/commit.c:639
 #, c-format
 msgid "could not read log file '%s'"
 msgstr "không đọc được tệp nhật ký “%s”"
 
-#: builtin/commit.c:622
+#: builtin/commit.c:645
 msgid "commit has empty message"
 msgstr "lần chuyển giao (commit) có ghi chú trống rỗng"
 
-#: builtin/commit.c:638
+#: builtin/commit.c:661
 msgid "could not read MERGE_MSG"
 msgstr "không thể đọc MERGE_MSG"
 
-#: builtin/commit.c:642
+#: builtin/commit.c:665
 msgid "could not read SQUASH_MSG"
 msgstr "không thể đọc SQUASH_MSG"
 
-#: builtin/commit.c:646
+#: builtin/commit.c:669
 #, c-format
 msgid "could not read '%s'"
 msgstr "Không thể đọc “%s”."
 
-#: builtin/commit.c:707
+#: builtin/commit.c:730
 msgid "could not write commit template"
 msgstr "không thể ghi mẫu commit"
 
-#: builtin/commit.c:718
+#: builtin/commit.c:741
 #, c-format
 msgid ""
 "\n"
@@ -3598,7 +4116,7 @@ msgstr ""
 "\t%s\n"
 "và thử lại.\n"
 
-#: builtin/commit.c:723
+#: builtin/commit.c:746
 #, c-format
 msgid ""
 "\n"
@@ -3613,7 +4131,7 @@ msgstr ""
 "\t%s\n"
 "và thử lại.\n"
 
-#: builtin/commit.c:735
+#: builtin/commit.c:758
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
@@ -3624,7 +4142,7 @@ msgstr ""
 "bắt đầu bằng “%c” sẽ được bỏ qua, nếu phần chú thích rỗng sẽ hủy bỏ lần "
 "chuyển giao (commit).\n"
 
-#: builtin/commit.c:740
+#: builtin/commit.c:763
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
@@ -3636,372 +4154,372 @@ msgstr ""
 "bắt đầu bằng “%c” sẽ được bỏ qua; bạn có thể xóa chúng đi nếu muốn thế.\n"
 "Phần chú thích này nếu trống rỗng sẽ hủy bỏ lần chuyển giao (commit).\n"
 
-#: builtin/commit.c:753
+#: builtin/commit.c:776
 #, c-format
 msgid "%sAuthor:    %s"
 msgstr "%sTác giả:    %s"
 
-#: builtin/commit.c:760
+#: builtin/commit.c:783
 #, c-format
 msgid "%sCommitter: %s"
 msgstr "%sNgười chuyển giao (commit): %s"
 
-#: builtin/commit.c:780
+#: builtin/commit.c:803
 msgid "Cannot read index"
 msgstr "không đọc được bảng mục lục"
 
-#: builtin/commit.c:817
+#: builtin/commit.c:845
 msgid "Error building trees"
 msgstr "Gặp lỗi khi xây dựng cây"
 
-#: builtin/commit.c:832 builtin/tag.c:359
+#: builtin/commit.c:860 builtin/tag.c:359
 #, c-format
 msgid "Please supply the message using either -m or -F option.\n"
 msgstr "Xin hãy áp dụng thông điệp sử dụng hoặc là tùy chọn -m hoặc là -F.\n"
 
-#: builtin/commit.c:929
+#: builtin/commit.c:957
 #, c-format
 msgid "No existing author found with '%s'"
 msgstr "Không tìm thấy tác giả có sẵn với “%s”"
 
-#: builtin/commit.c:944 builtin/commit.c:1138
+#: builtin/commit.c:972 builtin/commit.c:1208
 #, c-format
 msgid "Invalid untracked files mode '%s'"
 msgstr "Chế độ cho các tập tin không bị theo vết không hợp lệ “%s”"
 
-#: builtin/commit.c:974
+#: builtin/commit.c:1009
+msgid "--long and -z are incompatible"
+msgstr "hai tùy chọn -long và -z không tương thích với nhau"
+
+#: builtin/commit.c:1039
 msgid "Using both --reset-author and --author does not make sense"
 msgstr "Sử dụng cả hai tùy chọn --reset-author và --author không hợp lý"
 
-#: builtin/commit.c:985
+#: builtin/commit.c:1050
 msgid "You have nothing to amend."
 msgstr "Không có gì để amend (tu bổ) cả."
 
-#: builtin/commit.c:988
+#: builtin/commit.c:1053
 msgid "You are in the middle of a merge -- cannot amend."
 msgstr ""
 "Bạn đang ở giữa của quá trình hòa trộn -- không thể thực hiện amend (tu bổ)."
 
-#: builtin/commit.c:990
+#: builtin/commit.c:1055
 msgid "You are in the middle of a cherry-pick -- cannot amend."
 msgstr ""
 "Bạn đang ở giữa của quá trình cherry-pick -- không thể thực hiện amend (tu "
 "bổ)."
 
-#: builtin/commit.c:993
+#: builtin/commit.c:1058
 msgid "Options --squash and --fixup cannot be used together"
 msgstr "Các tùy chọn --squash và --fixup không thể sử dụng cùng với nhau"
 
-#: builtin/commit.c:1003
+#: builtin/commit.c:1068
 msgid "Only one of -c/-C/-F/--fixup can be used."
 msgstr "Chỉ một tùy chọn trong số -c/-C/-F/--fixup được sử dụng"
 
-#: builtin/commit.c:1005
+#: builtin/commit.c:1070
 msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
 msgstr "Tùy chọn -m không thể được tổ hợp cùng với -c/-C/-F/--fixup."
 
-#: builtin/commit.c:1013
+#: builtin/commit.c:1078
 msgid "--reset-author can be used only with -C, -c or --amend."
 msgstr ""
 "--reset-author chỉ có thể được sử dụng với tùy chọn -C, -c hay --amend."
 
-#: builtin/commit.c:1030
+#: builtin/commit.c:1095
 msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
 msgstr ""
 "Chỉ một trong các tùy chọn --include/--only/--all/--interactive/--patch được "
 "sử dụng."
 
-#: builtin/commit.c:1032
+#: builtin/commit.c:1097
 msgid "No paths with --include/--only does not make sense."
 msgstr "Không đường dẫn với các tùy chọn --include/--only không hợp lý."
 
-#: builtin/commit.c:1034
+#: builtin/commit.c:1099
 msgid "Clever... amending the last one with dirty index."
 msgstr "Giỏi...  tu bổ cái cuối với bảng mục lục phi nghĩa."
 
-#: builtin/commit.c:1036
+#: builtin/commit.c:1101
 msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
 msgstr ""
 "Những đường dẫn rõ ràng được chỉ ra không có tùy chọn -i cũng không -o; đang "
 "giả định --only những-đường-dẫn..."
 
-#: builtin/commit.c:1046 builtin/tag.c:575
+#: builtin/commit.c:1111 builtin/tag.c:575
 #, c-format
 msgid "Invalid cleanup mode %s"
 msgstr "Chế độ dọn dẹp không hợp lệ %s"
 
-#: builtin/commit.c:1051
+#: builtin/commit.c:1116
 msgid "Paths with -a does not make sense."
 msgstr "Các đường dẫn với tùy chọn -a không hợp lý."
 
-#: builtin/commit.c:1057 builtin/commit.c:1192
-msgid "--long and -z are incompatible"
-msgstr "hai tùy chọn -long và -z không tương thích với nhau"
-
-#: builtin/commit.c:1152 builtin/commit.c:1388
+#: builtin/commit.c:1222 builtin/commit.c:1455
 msgid "show status concisely"
 msgstr "hiển thị trạng thái ở dạng súc tích"
 
-#: builtin/commit.c:1154 builtin/commit.c:1390
+#: builtin/commit.c:1224 builtin/commit.c:1457
 msgid "show branch information"
 msgstr "hiển thị thông tin nhánh"
 
-#: builtin/commit.c:1156 builtin/commit.c:1392 builtin/push.c:426
+#: builtin/commit.c:1226 builtin/commit.c:1459 builtin/push.c:452
 msgid "machine-readable output"
 msgstr "kết xuất dạng máy-có-thể-đọc"
 
-#: builtin/commit.c:1159 builtin/commit.c:1394
+#: builtin/commit.c:1229 builtin/commit.c:1461
 msgid "show status in long format (default)"
 msgstr "hiển thị trạng thái ở định dạng dài (mặc định)"
 
-#: builtin/commit.c:1162 builtin/commit.c:1397
+#: builtin/commit.c:1232 builtin/commit.c:1464
 msgid "terminate entries with NUL"
 msgstr "chấm dứt các mục bằng NUL"
 
-#: builtin/commit.c:1164 builtin/commit.c:1400 builtin/fast-export.c:647
-#: builtin/fast-export.c:650 builtin/tag.c:459
+#: builtin/commit.c:1234 builtin/commit.c:1467 builtin/fast-export.c:667
+#: builtin/fast-export.c:670 builtin/tag.c:459
 msgid "mode"
 msgstr "chế độ"
 
-#: builtin/commit.c:1165 builtin/commit.c:1400
+#: builtin/commit.c:1235 builtin/commit.c:1467
 msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
 msgstr ""
 "hiển thị các tập tin chưa được theo dõi  dấu vết, các chế độ tùy chọn:  all, "
 "normal, no. (Mặc định: all)"
 
-#: builtin/commit.c:1168
+#: builtin/commit.c:1238
 msgid "show ignored files"
 msgstr "hiển thị các tập tin ẩn"
 
-#: builtin/commit.c:1169 parse-options.h:151
+#: builtin/commit.c:1239 parse-options.h:154
 msgid "when"
 msgstr "khi"
 
-#: builtin/commit.c:1170
+#: builtin/commit.c:1240
 msgid ""
 "ignore changes to submodules, optional when: all, dirty, untracked. "
 "(Default: all)"
 msgstr ""
-"bỏ qua các thay đổi trong mô-đun con, tùy chọn khi: all, dirty, untracked. "
+"bỏ qua các thay đổi trong mô-đun-con, tùy chọn khi: all, dirty, untracked. "
 "(Mặc định: all)"
 
-#: builtin/commit.c:1172
+#: builtin/commit.c:1242
 msgid "list untracked files in columns"
 msgstr "hiển thị danh sách các tập-tin chưa được theo dõi trong các cột"
 
-#: builtin/commit.c:1246
+#: builtin/commit.c:1313
 msgid "couldn't look up newly created commit"
 msgstr "không thể tìm thấy lần chuyển giao (commit) mới hơn đã được tạo"
 
-#: builtin/commit.c:1248
+#: builtin/commit.c:1315
 msgid "could not parse newly created commit"
 msgstr ""
 "không thể phân tích cú pháp của đối tượng chuyển giao mới hơn đã được tạo"
 
-#: builtin/commit.c:1289
+#: builtin/commit.c:1356
 msgid "detached HEAD"
 msgstr "đã rời khỏi HEAD"
 
-#: builtin/commit.c:1291
+#: builtin/commit.c:1358
 msgid " (root-commit)"
 msgstr " (root-commit)"
 
-#: builtin/commit.c:1358
+#: builtin/commit.c:1425
 msgid "suppress summary after successful commit"
 msgstr "không hiển thị tổng kết sau khi chuyển giao thành công"
 
-#: builtin/commit.c:1359
+#: builtin/commit.c:1426
 msgid "show diff in commit message template"
 msgstr "hiển thị sự khác biệt trong mẫu tin nhắn chuyển giao"
 
-#: builtin/commit.c:1361
+#: builtin/commit.c:1428
 msgid "Commit message options"
 msgstr "Các tùy chọn ghi chú commit"
 
-#: builtin/commit.c:1362 builtin/tag.c:457
+#: builtin/commit.c:1429 builtin/tag.c:457
 msgid "read message from file"
 msgstr "đọc chú thích từ tập tin"
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "author"
 msgstr "tác giả"
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "override author for commit"
 msgstr "ghi đè tác giả cho commit"
 
-#: builtin/commit.c:1364 builtin/gc.c:178
+#: builtin/commit.c:1431 builtin/gc.c:178
 msgid "date"
 msgstr "ngày tháng"
 
-#: builtin/commit.c:1364
+#: builtin/commit.c:1431
 msgid "override date for commit"
 msgstr "ghi đè ngày tháng cho commit"
 
-#: builtin/commit.c:1365 builtin/merge.c:206 builtin/notes.c:533
-#: builtin/notes.c:690 builtin/tag.c:455
+#: builtin/commit.c:1432 builtin/merge.c:223 builtin/notes.c:405
+#: builtin/notes.c:562 builtin/tag.c:455
 msgid "message"
 msgstr "thông điệp"
 
-#: builtin/commit.c:1365
+#: builtin/commit.c:1432
 msgid "commit message"
 msgstr "chú thích của lần commit"
 
-#: builtin/commit.c:1366
+#: builtin/commit.c:1433
 msgid "reuse and edit message from specified commit"
 msgstr ""
 "dùng lại các ghi chú từ lần chuyển giao (commit) đã cho nhưng có cho sửa chữa"
 
-#: builtin/commit.c:1367
+#: builtin/commit.c:1434
 msgid "reuse message from specified commit"
 msgstr "dùng lại các ghi chú từ lần chuyển giao (commit) đã cho"
 
-#: builtin/commit.c:1368
+#: builtin/commit.c:1435
 msgid "use autosquash formatted message to fixup specified commit"
 msgstr ""
 "dùng ghi chú có định dạng autosquash để sửa chữa lần chuyển giao đã chỉ ra"
 
-#: builtin/commit.c:1369
+#: builtin/commit.c:1436
 msgid "use autosquash formatted message to squash specified commit"
 msgstr ""
 "dùng lời nhắn có định dạng tự động nén để nén lại các lần chuyển giao đã chỉ "
 "ra"
 
-#: builtin/commit.c:1370
+#: builtin/commit.c:1437
 msgid "the commit is authored by me now (used with -C/-c/--amend)"
 msgstr ""
 "lần chuyển giao nhận tôi là tác giả (được dùng với tùy chọn -C/-c/--amend)"
 
-#: builtin/commit.c:1371 builtin/log.c:1102 builtin/revert.c:109
+#: builtin/commit.c:1438 builtin/log.c:1160 builtin/revert.c:111
 msgid "add Signed-off-by:"
 msgstr "thêm dòng Signed-off-by:"
 
-#: builtin/commit.c:1372
+#: builtin/commit.c:1439
 msgid "use specified template file"
 msgstr "sử dụng tập tin mẫu đã cho"
 
-#: builtin/commit.c:1373
+#: builtin/commit.c:1440
 msgid "force edit of commit"
 msgstr "ép buộc sửa lần commit"
 
-#: builtin/commit.c:1374
+#: builtin/commit.c:1441
 msgid "default"
 msgstr "mặc định"
 
-#: builtin/commit.c:1374 builtin/tag.c:460
+#: builtin/commit.c:1441 builtin/tag.c:460
 msgid "how to strip spaces and #comments from message"
 msgstr "làm thế nào để cắt bỏ khoảng trắng và #ghichú từ mẩu tin nhắn"
 
-#: builtin/commit.c:1375
+#: builtin/commit.c:1442
 msgid "include status in commit message template"
 msgstr "bao gồm các trạng thái ghi mẫu ghi chú chuyển giao (commit)"
 
-#: builtin/commit.c:1376 builtin/merge.c:213 builtin/tag.c:461
+#: builtin/commit.c:1443 builtin/merge.c:230 builtin/tag.c:461
 msgid "key id"
 msgstr "id khóa"
 
-#: builtin/commit.c:1377 builtin/merge.c:214
+#: builtin/commit.c:1444 builtin/merge.c:231
 msgid "GPG sign commit"
 msgstr "ký lần commit dùng GPG"
 
 #. end commit message options
-#: builtin/commit.c:1380
+#: builtin/commit.c:1447
 msgid "Commit contents options"
 msgstr "Các tùy nội dung ghi chú commit"
 
-#: builtin/commit.c:1381
+#: builtin/commit.c:1448
 msgid "commit all changed files"
 msgstr "chuyển giao tất cả các tập tin có thay đổi"
 
-#: builtin/commit.c:1382
+#: builtin/commit.c:1449
 msgid "add specified files to index for commit"
 msgstr "thêm các tập tin đã chỉ ra vào bảng mục lục để chuyển giao (commit)"
 
-#: builtin/commit.c:1383
+#: builtin/commit.c:1450
 msgid "interactively add files"
 msgstr "thêm các tập-tin bằng tương tác"
 
-#: builtin/commit.c:1384
+#: builtin/commit.c:1451
 msgid "interactively add changes"
 msgstr "thêm các thay đổi bằng tương tác"
 
-#: builtin/commit.c:1385
+#: builtin/commit.c:1452
 msgid "commit only specified files"
 msgstr "chỉ chuyển giao các tập tin đã chỉ ra"
 
-#: builtin/commit.c:1386
+#: builtin/commit.c:1453
 msgid "bypass pre-commit hook"
 msgstr "vòng qua móc (hook) pre-commit"
 
-#: builtin/commit.c:1387
+#: builtin/commit.c:1454
 msgid "show what would be committed"
 msgstr "hiển thị xem cái gì có thể được chuyển giao"
 
-#: builtin/commit.c:1398
+#: builtin/commit.c:1465
 msgid "amend previous commit"
 msgstr "tu bổ (amend) lần commit trước"
 
-#: builtin/commit.c:1399
+#: builtin/commit.c:1466
 msgid "bypass post-rewrite hook"
 msgstr "vòng qua móc (hook) post-rewrite"
 
-#: builtin/commit.c:1404
+#: builtin/commit.c:1471
 msgid "ok to record an empty change"
 msgstr "ok để ghi lại một thay đổi trống rỗng"
 
-#: builtin/commit.c:1407
+#: builtin/commit.c:1474
 msgid "ok to record a change with an empty message"
 msgstr "ok để ghi các thay đổi với lời nhắn trống rỗng"
 
-#: builtin/commit.c:1439
+#: builtin/commit.c:1507
 msgid "could not parse HEAD commit"
 msgstr "không thể phân tích commit (lần chuyển giao) HEAD"
 
-#: builtin/commit.c:1477 builtin/merge.c:508
+#: builtin/commit.c:1545 builtin/merge.c:525
 #, c-format
 msgid "could not open '%s' for reading"
 msgstr "không thể mở “%s” để đọc"
 
-#: builtin/commit.c:1484
+#: builtin/commit.c:1552
 #, c-format
 msgid "Corrupt MERGE_HEAD file (%s)"
 msgstr "Tập tin MERGE_HEAD sai hỏng (%s)"
 
-#: builtin/commit.c:1491
+#: builtin/commit.c:1559
 msgid "could not read MERGE_MODE"
 msgstr "không thể đọc MERGE_MODE"
 
-#: builtin/commit.c:1510
+#: builtin/commit.c:1578
 #, c-format
 msgid "could not read commit message: %s"
 msgstr "không thể đọc thông điệp (message) commit (lần chuyển giao): %s"
 
-#: builtin/commit.c:1524
+#: builtin/commit.c:1592
 #, c-format
 msgid "Aborting commit; you did not edit the message.\n"
 msgstr ""
 "Đang bỏ qua việc chuyển giao (commit); bạn đã không biên soạn thông điệp "
 "(message).\n"
 
-#: builtin/commit.c:1529
+#: builtin/commit.c:1597
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr ""
 "Đang bỏ qua lần chuyển giao (commit) bởi vì thông điệp của nó trống rỗng.\n"
 
-#: builtin/commit.c:1544 builtin/merge.c:832 builtin/merge.c:857
+#: builtin/commit.c:1612 builtin/merge.c:861 builtin/merge.c:886
 msgid "failed to write commit object"
 msgstr "gặp lỗi khi ghi đối tượng chuyển giao (commit)"
 
-#: builtin/commit.c:1565
+#: builtin/commit.c:1633
 msgid "cannot lock HEAD ref"
 msgstr "không thể khóa HEAD ref (tham chiếu)"
 
-#: builtin/commit.c:1569
+#: builtin/commit.c:1637
 msgid "cannot update HEAD ref"
 msgstr "không thể cập nhật ref (tham chiếu) HEAD"
 
-#: builtin/commit.c:1580
+#: builtin/commit.c:1648
 msgid ""
 "Repository has been updated, but unable to write\n"
 "new_index file. Check that disk is not full or quota is\n"
@@ -4016,171 +4534,183 @@ msgstr ""
 msgid "git config [options]"
 msgstr "git config [các-tùy-chọn]"
 
-#: builtin/config.c:51
+#: builtin/config.c:52
 msgid "Config file location"
 msgstr "Vị trí tập tin cấu hình"
 
-#: builtin/config.c:52
+#: builtin/config.c:53
 msgid "use global config file"
 msgstr "đặt tập tin cấu hình cần toàn cục"
 
-#: builtin/config.c:53
+#: builtin/config.c:54
 msgid "use system config file"
 msgstr "sử dụng tập tin cấu hình hệ thống"
 
-#: builtin/config.c:54
+#: builtin/config.c:55
 msgid "use repository config file"
 msgstr "dùng tập tin cấu hình của kho"
 
-#: builtin/config.c:55
+#: builtin/config.c:56
 msgid "use given config file"
 msgstr "sử dụng tập tin cấu hình đã cho"
 
-#: builtin/config.c:56
+#: builtin/config.c:57
+msgid "blob-id"
+msgstr "blob-id"
+
+#: builtin/config.c:57
+msgid "read config from given blob object"
+msgstr "đọc cấu hình từ đối tượng blob đã cho"
+
+#: builtin/config.c:58
 msgid "Action"
 msgstr "Hành động"
 
-#: builtin/config.c:57
+#: builtin/config.c:59
 msgid "get value: name [value-regex]"
 msgstr "lấy giá-trị: tên [value-regex]"
 
-#: builtin/config.c:58
+#: builtin/config.c:60
 msgid "get all values: key [value-regex]"
 msgstr "lấy tất cả giá-trị: khóa [value-regex]"
 
-#: builtin/config.c:59
+#: builtin/config.c:61
 msgid "get values for regexp: name-regex [value-regex]"
 msgstr "lấy giá trị cho regexp: name-regex [value-regex]"
 
-#: builtin/config.c:60
+#: builtin/config.c:62
 msgid "replace all matching variables: name value [value_regex]"
 msgstr "thay thế tất cả các biến khớp mẫu: tên giá-trị [value_regex]"
 
-#: builtin/config.c:61
+#: builtin/config.c:63
 msgid "add a new variable: name value"
 msgstr "thêm biến mới: tên giá-trị"
 
-#: builtin/config.c:62
+#: builtin/config.c:64
 msgid "remove a variable: name [value-regex]"
 msgstr "gỡ bỏ biến: tên [value-regex]"
 
-#: builtin/config.c:63
+#: builtin/config.c:65
 msgid "remove all matches: name [value-regex]"
 msgstr "gỡ bỏ mọi cái khớp: tên [value-regex]"
 
-#: builtin/config.c:64
+#: builtin/config.c:66
 msgid "rename section: old-name new-name"
 msgstr "đổi tên chương: tên-cũ tên-mới"
 
-#: builtin/config.c:65
+#: builtin/config.c:67
 msgid "remove a section: name"
 msgstr "gỡ bỏ chương: tên"
 
-#: builtin/config.c:66
+#: builtin/config.c:68
 msgid "list all"
 msgstr "liệt kê tất"
 
-#: builtin/config.c:67
+#: builtin/config.c:69
 msgid "open an editor"
 msgstr "mở một trình biên soạn"
 
-#: builtin/config.c:68 builtin/config.c:69
+#: builtin/config.c:70 builtin/config.c:71
 msgid "slot"
 msgstr "khe"
 
-#: builtin/config.c:68
+#: builtin/config.c:70
 msgid "find the color configured: [default]"
 msgstr "tìm cấu hình màu sắc: [mặc định]"
 
-#: builtin/config.c:69
+#: builtin/config.c:71
 msgid "find the color setting: [stdout-is-tty]"
 msgstr "tìm các cài đặt về màu sắc: [stdout-là-tty]"
 
-#: builtin/config.c:70
+#: builtin/config.c:72
 msgid "Type"
 msgstr "Kiểu"
 
-#: builtin/config.c:71
+#: builtin/config.c:73
 msgid "value is \"true\" or \"false\""
 msgstr "giá trị là \"true\" hoặc \"false\""
 
-#: builtin/config.c:72
+#: builtin/config.c:74
 msgid "value is decimal number"
 msgstr "giá trị ở dạng số thập phân"
 
-#: builtin/config.c:73
+#: builtin/config.c:75
 msgid "value is --bool or --int"
 msgstr "giá trị là --bool hoặc --int"
 
-#: builtin/config.c:74
+#: builtin/config.c:76
 msgid "value is a path (file or directory name)"
 msgstr "giá trị là đường dẫn (tên tập tin hay thư mục)"
 
-#: builtin/config.c:75
+#: builtin/config.c:77
 msgid "Other"
 msgstr "Khác"
 
-#: builtin/config.c:76
+#: builtin/config.c:78
 msgid "terminate values with NUL byte"
 msgstr "chấm dứt giá trị với byte NUL"
 
-#: builtin/config.c:77
+#: builtin/config.c:79
 msgid "respect include directives on lookup"
 msgstr "tôn trọng kể cà các hướng trong tìm kiếm"
 
-#: builtin/count-objects.c:69
-msgid "git count-objects [-v]"
-msgstr "git count-objects [-v]"
+#: builtin/count-objects.c:82
+msgid "git count-objects [-v] [-H | --human-readable]"
+msgstr "git count-objects [-v] [-H | --human-readable]"
 
-#: builtin/describe.c:15
+#: builtin/count-objects.c:97
+msgid "print sizes in human readable format"
+msgstr "hiển thị kích cỡ theo định dạng dành cho người đọc"
+
+#: builtin/describe.c:16
 msgid "git describe [options] <committish>*"
 msgstr "git describe [các-tùy-chọn] <committish>*"
 
-#: builtin/describe.c:16
+#: builtin/describe.c:17
 msgid "git describe [options] --dirty"
 msgstr "git describe [các-tùy-chọn] --dirty"
 
-#: builtin/describe.c:234
+#: builtin/describe.c:237
 #, c-format
 msgid "annotated tag %s not available"
 msgstr "thẻ đã được ghi chú %s không sẵn để dùng"
 
-#: builtin/describe.c:238
+#: builtin/describe.c:241
 #, c-format
 msgid "annotated tag %s has no embedded name"
 msgstr "thẻ được chú giải %s không có tên nhúng"
 
-#: builtin/describe.c:240
+#: builtin/describe.c:243
 #, c-format
 msgid "tag '%s' is really '%s' here"
 msgstr "thẻ “%s” đã thực sự ở đây “%s” rồi"
 
-#: builtin/describe.c:267
+#: builtin/describe.c:270
 #, c-format
 msgid "Not a valid object name %s"
 msgstr "Không phải tên đối tượng %s hợp lệ"
 
-#: builtin/describe.c:270
+#: builtin/describe.c:273
 #, c-format
 msgid "%s is not a valid '%s' object"
 msgstr "%s không phải là một đối tượng “%s” hợp lệ"
 
-#: builtin/describe.c:287
+#: builtin/describe.c:290
 #, c-format
 msgid "no tag exactly matches '%s'"
 msgstr "không có thẻ nào khớp chính xác với “%s”"
 
-#: builtin/describe.c:289
+#: builtin/describe.c:292
 #, c-format
 msgid "searching to describe %s\n"
 msgstr "Đang tìm kiếm để mô tả %s\n"
 
-#: builtin/describe.c:329
+#: builtin/describe.c:332
 #, c-format
 msgid "finished search at %s\n"
 msgstr "việc tìm kiếm đã kết thúc tại %s\n"
 
-#: builtin/describe.c:353
+#: builtin/describe.c:359
 #, c-format
 msgid ""
 "No annotated tags can describe '%s'.\n"
@@ -4189,7 +4719,7 @@ msgstr ""
 "Không có thẻ được chú giải nào được mô tả là “%s”.\n"
 "Tuy nhiên, ở đây có những thẻ không được chú giải: hãy thử --tags."
 
-#: builtin/describe.c:357
+#: builtin/describe.c:363
 #, c-format
 msgid ""
 "No tags can describe '%s'.\n"
@@ -4198,12 +4728,12 @@ msgstr ""
 "Không có thẻ (tag) có thể mô tả “%s”.\n"
 "Hãy thử --always, hoặt tạo một số thẻ."
 
-#: builtin/describe.c:378
+#: builtin/describe.c:384
 #, c-format
 msgid "traversed %lu commits\n"
 msgstr "đã xuyên %lu qua lần chuyển giao (commit)\n"
 
-#: builtin/describe.c:381
+#: builtin/describe.c:387
 #, c-format
 msgid ""
 "more than %i tags found; listed %i most recent\n"
@@ -4212,59 +4742,63 @@ msgstr ""
 "tìm thấy nhiều hơn %i thẻ (tag); đã liệt kê %i gần đây nhất\n"
 "bỏ đi tìm kiếm tại %s\n"
 
-#: builtin/describe.c:403
+#: builtin/describe.c:409
 msgid "find the tag that comes after the commit"
 msgstr "tìm các thẻ mà nó đến trước lần chuyển giao"
 
-#: builtin/describe.c:404
+#: builtin/describe.c:410
 msgid "debug search strategy on stderr"
 msgstr "chiến lược tìm kiếm trên đầu ra lỗi chuẩn stderr"
 
-#: builtin/describe.c:405
-msgid "use any ref in .git/refs"
-msgstr "sử dụng bất kỳ ref nào trong .git/refs"
+#: builtin/describe.c:411
+msgid "use any ref"
+msgstr "dùng ref bất kỳ"
 
-#: builtin/describe.c:406
-msgid "use any tag in .git/refs/tags"
-msgstr "sử dụng bất kỳ thẻ nào trong .git/refs/tags"
+#: builtin/describe.c:412
+msgid "use any tag, even unannotated"
+msgstr "dùng thẻ bất kỳ, cả khi “unannotated”"
 
-#: builtin/describe.c:407
+#: builtin/describe.c:413
 msgid "always use long format"
 msgstr "luôn dùng định dạng dài"
 
-#: builtin/describe.c:410
+#: builtin/describe.c:414
+msgid "only follow first parent"
+msgstr "chỉ theo cha mẹ đầu tiên"
+
+#: builtin/describe.c:417
 msgid "only output exact matches"
 msgstr "chỉ xuất những gì khớp chính xác"
 
-#: builtin/describe.c:412
+#: builtin/describe.c:419
 msgid "consider <n> most recent tags (default: 10)"
 msgstr "coi như  <n> thẻ gần đây nhất (mặc định: 10)"
 
-#: builtin/describe.c:414
+#: builtin/describe.c:421
 msgid "only consider tags matching <pattern>"
 msgstr "chỉ cân nhắc đến những thẻ khớp với  <mẫu>"
 
-#: builtin/describe.c:416 builtin/name-rev.c:238
+#: builtin/describe.c:423 builtin/name-rev.c:322
 msgid "show abbreviated commit object as fallback"
 msgstr "hiển thị đối tượng chuyển giao vắn tắt như là fallback"
 
-#: builtin/describe.c:417
+#: builtin/describe.c:424
 msgid "mark"
 msgstr "dấu"
 
-#: builtin/describe.c:418
+#: builtin/describe.c:425
 msgid "append <mark> on dirty working tree (default: \"-dirty\")"
 msgstr "thêm <dấu> trên cây thư mục làm việc bẩn (mặc định \"-dirty\")"
 
-#: builtin/describe.c:436
+#: builtin/describe.c:443
 msgid "--long is incompatible with --abbrev=0"
 msgstr "--long là xung khắc với tùy chọn --abbrev=0"
 
-#: builtin/describe.c:462
+#: builtin/describe.c:469
 msgid "No names found, cannot describe anything."
 msgstr "Không tìm thấy các tên, không thể mô tả gì cả."
 
-#: builtin/describe.c:482
+#: builtin/describe.c:489
 msgid "--dirty is incompatible with committishes"
 msgstr "--dirty là xung khắc với các tùy chọn dành cho chuyển giao (commit)"
 
@@ -4273,31 +4807,26 @@ msgstr "--dirty là xung khắc với các tùy chọn dành cho chuyển giao (
 msgid "'%s': not a regular file or symlink"
 msgstr "“%s”: không phải tập tin bình thường hay liên kết tượng trưng"
 
-#: builtin/diff.c:228
+#: builtin/diff.c:230
 #, c-format
 msgid "invalid option: %s"
 msgstr "tùy chọn sai: %s"
 
-#: builtin/diff.c:305
+#: builtin/diff.c:307
 msgid "Not a git repository"
 msgstr "Không phải là kho git"
 
-#: builtin/diff.c:348
+#: builtin/diff.c:350
 #, c-format
 msgid "invalid object '%s' given."
 msgstr "đối tượng đã cho “%s” không hợp lệ."
 
-#: builtin/diff.c:353
-#, c-format
-msgid "more than %d trees given: '%s'"
-msgstr "đã chỉ ra nhiều hơn %d cây (tree): “%s”"
-
-#: builtin/diff.c:363
+#: builtin/diff.c:359
 #, c-format
 msgid "more than two blobs given: '%s'"
 msgstr "đã cho nhiều hơn hai đối tượng blob: “%s”"
 
-#: builtin/diff.c:371
+#: builtin/diff.c:366
 #, c-format
 msgid "unhandled object '%s' given."
 msgstr "đã cho đối tượng không thể nắm giữ “%s”."
@@ -4306,39 +4835,39 @@ msgstr "đã cho đối tượng không thể nắm giữ “%s”."
 msgid "git fast-export [rev-list-opts]"
 msgstr "git fast-export [rev-list-opts]"
 
-#: builtin/fast-export.c:646
+#: builtin/fast-export.c:666
 msgid "show progress after <n> objects"
 msgstr "hiển thị tiến triển sau <n> đối tượng"
 
-#: builtin/fast-export.c:648
+#: builtin/fast-export.c:668
 msgid "select handling of signed tags"
 msgstr "chọn điều khiển của thẻ đã ký"
 
-#: builtin/fast-export.c:651
+#: builtin/fast-export.c:671
 msgid "select handling of tags that tag filtered objects"
 msgstr "chọn sự xử lý của các thẻ, cái mà đánh thẻ các đối tượng được lọc ra"
 
-#: builtin/fast-export.c:654
+#: builtin/fast-export.c:674
 msgid "Dump marks to this file"
 msgstr "Đổ các đánh dấu này vào tập-tin"
 
-#: builtin/fast-export.c:656
+#: builtin/fast-export.c:676
 msgid "Import marks from this file"
 msgstr "nhập vào đánh dấu từ tập tin này"
 
-#: builtin/fast-export.c:658
+#: builtin/fast-export.c:678
 msgid "Fake a tagger when tags lack one"
 msgstr "Làm giả một cái thẻ khi thẻ bị thiếu một cái"
 
-#: builtin/fast-export.c:660
+#: builtin/fast-export.c:680
 msgid "Output full tree for each commit"
 msgstr "Xuất ra toàn bộ cây cho mỗi lần chuyển giao"
 
-#: builtin/fast-export.c:662
+#: builtin/fast-export.c:682
 msgid "Use the done feature to terminate the stream"
 msgstr "Sử dụng tính năng done để chấm dứt luồng dữ liệu"
 
-#: builtin/fast-export.c:663
+#: builtin/fast-export.c:683
 msgid "Skip output of blob data"
 msgstr "Bỏ qua kết xuất của dữ liệu blob"
 
@@ -4360,7 +4889,7 @@ msgstr "git fetch --all [<các-tùy-chọn>]"
 
 #: builtin/fetch.c:60
 msgid "fetch from all remotes"
-msgstr "fetch từ tất cả các máy chủ"
+msgstr "lấy về từ tất cả các máy chủ"
 
 #: builtin/fetch.c:62
 msgid "append to .git/FETCH_HEAD instead of overwriting"
@@ -4376,15 +4905,15 @@ msgstr "ép buộc ghi đè lên nhánh nội bộ"
 
 #: builtin/fetch.c:67
 msgid "fetch from multiple remotes"
-msgstr "fetch từ nhiều máy chủ cùng lúc"
+msgstr "lấy từ nhiều máy chủ cùng lúc"
 
 #: builtin/fetch.c:69
 msgid "fetch all tags and associated objects"
-msgstr "lấy (fetch) tất cả các thẻ cùng với các đối tượng liên quan đến nó"
+msgstr "lấy tất cả các thẻ cùng với các đối tượng liên quan đến nó"
 
 #: builtin/fetch.c:71
 msgid "do not fetch all tags (--no-tags)"
-msgstr "không lấy (fetch) tất cả các thẻ (--no-tags)"
+msgstr "không lấy tất cả các thẻ (--no-tags)"
 
 #: builtin/fetch.c:73
 msgid "prune remote-tracking branches no longer on remote"
@@ -4398,7 +4927,7 @@ msgstr "khi-cần"
 
 #: builtin/fetch.c:75
 msgid "control recursive fetching of submodules"
-msgstr "điều khiển việc lấy về (fetch) đệ quy trong các mô-đun-con"
+msgstr "điều khiển việc lấy về đệ quy trong các mô-đun-con"
 
 #: builtin/fetch.c:79
 msgid "keep downloaded pack"
@@ -4406,7 +4935,7 @@ msgstr "giữ các gói đã tải về"
 
 #: builtin/fetch.c:81
 msgid "allow updating of HEAD ref"
-msgstr "cho phép cập nhật ref (tham chiếu) HEAD"
+msgstr "cho phép cập nhật th.chiếu HEAD"
 
 #: builtin/fetch.c:84
 msgid "deepen history of shallow clone"
@@ -4416,7 +4945,7 @@ msgstr "làm sâu hơn lịch sử của bản sao"
 msgid "convert to a complete repository"
 msgstr "chuyển đổi hoàn toàn sang kho git"
 
-#: builtin/fetch.c:88 builtin/log.c:1119
+#: builtin/fetch.c:88 builtin/log.c:1177
 msgid "dir"
 msgstr "tmục"
 
@@ -4428,76 +4957,76 @@ msgstr "soạn sẵn cái này cho kết xuất đường dẫn mô-đun-con"
 msgid "default mode for recursion"
 msgstr "chế độ mặc định cho đệ qui"
 
-#: builtin/fetch.c:204
+#: builtin/fetch.c:220
 msgid "Couldn't find remote ref HEAD"
 msgstr "Không thể tìm thấy máy chủ cho tham chiếu HEAD"
 
-#: builtin/fetch.c:257
+#: builtin/fetch.c:273
 #, c-format
 msgid "object %s not found"
 msgstr "Không tìm thấy đối tượng %s"
 
-#: builtin/fetch.c:262
+#: builtin/fetch.c:278
 msgid "[up to date]"
 msgstr "[đã cập nhật]"
 
-#: builtin/fetch.c:276
+#: builtin/fetch.c:292
 #, c-format
 msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
 msgstr "! %-*s %-*s -> %s  (không thể fetch (lấy) về nhánh hiện hành)"
 
-#: builtin/fetch.c:277 builtin/fetch.c:363
+#: builtin/fetch.c:293 builtin/fetch.c:379
 msgid "[rejected]"
 msgstr "[Bị từ chối]"
 
-#: builtin/fetch.c:288
+#: builtin/fetch.c:304
 msgid "[tag update]"
 msgstr "[cập nhật thẻ]"
 
-#: builtin/fetch.c:290 builtin/fetch.c:325 builtin/fetch.c:343
+#: builtin/fetch.c:306 builtin/fetch.c:341 builtin/fetch.c:359
 msgid "  (unable to update local ref)"
 msgstr "  (không thể cập nhật tham chiếu (ref) nội bộ)"
 
-#: builtin/fetch.c:308
+#: builtin/fetch.c:324
 msgid "[new tag]"
 msgstr "[thẻ mới]"
 
-#: builtin/fetch.c:311
+#: builtin/fetch.c:327
 msgid "[new branch]"
 msgstr "[nhánh mới]"
 
-#: builtin/fetch.c:314
+#: builtin/fetch.c:330
 msgid "[new ref]"
 msgstr "[ref (tham chiếu) mới]"
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "unable to update local ref"
 msgstr "không thể cập nhật tham chiếu (ref) nội bộ"
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "forced update"
 msgstr "cưỡng bức cập nhật"
 
-#: builtin/fetch.c:365
+#: builtin/fetch.c:381
 msgid "(non-fast-forward)"
 msgstr "(non-fast-forward)"
 
-#: builtin/fetch.c:396 builtin/fetch.c:688
+#: builtin/fetch.c:412 builtin/fetch.c:718
 #, c-format
 msgid "cannot open %s: %s\n"
 msgstr "không thể mở %s: %s\n"
 
-#: builtin/fetch.c:405
+#: builtin/fetch.c:421
 #, c-format
 msgid "%s did not send all necessary objects\n"
 msgstr "%s đã không gửi tất cả các đối tượng cần thiết\n"
 
-#: builtin/fetch.c:491
+#: builtin/fetch.c:520
 #, c-format
 msgid "From %.*s\n"
 msgstr "Từ %.*s\n"
 
-#: builtin/fetch.c:502
+#: builtin/fetch.c:531
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -4506,57 +5035,57 @@ msgstr ""
 "một số tham chiếu (refs) nội bộ không thể được cập nhật; hãy thử chạy\n"
 " “git remote prune %s” để bỏ đi những nhánh cũ, hay bị xung đột"
 
-#: builtin/fetch.c:552
+#: builtin/fetch.c:581
 #, c-format
 msgid "   (%s will become dangling)"
 msgstr "   (%s sẽ trở thành không đầu (không được quản lý))"
 
-#: builtin/fetch.c:553
+#: builtin/fetch.c:582
 #, c-format
 msgid "   (%s has become dangling)"
 msgstr "   (%s đã trở thành không đầu (không được quản lý))"
 
-#: builtin/fetch.c:560
+#: builtin/fetch.c:589
 msgid "[deleted]"
 msgstr "[đã xóa]"
 
-#: builtin/fetch.c:561 builtin/remote.c:1055
+#: builtin/fetch.c:590 builtin/remote.c:1055
 msgid "(none)"
 msgstr "(không)"
 
-#: builtin/fetch.c:678
+#: builtin/fetch.c:708
 #, c-format
 msgid "Refusing to fetch into current branch %s of non-bare repository"
 msgstr ""
 "Từ chối việc lấy (fetch) vào trong nhánh hiện tại %s của một kho chứa không "
 "phải kho trần (bare)"
 
-#: builtin/fetch.c:712
+#: builtin/fetch.c:742
 #, c-format
 msgid "Don't know how to fetch from %s"
-msgstr "Không biết làm cách nào để lấy về (fetch) từ %s"
+msgstr "Không biết làm cách nào để lấy về từ %s"
 
-#: builtin/fetch.c:789
+#: builtin/fetch.c:823
 #, c-format
 msgid "Option \"%s\" value \"%s\" is not valid for %s"
 msgstr "Tùy chọn \"%s\" có giá trị \"%s\" là không hợp lệ cho %s"
 
-#: builtin/fetch.c:792
+#: builtin/fetch.c:826
 #, c-format
 msgid "Option \"%s\" is ignored for %s\n"
 msgstr "Tùy chọn \"%s\" bị bỏ qua với %s\n"
 
-#: builtin/fetch.c:894
+#: builtin/fetch.c:928
 #, c-format
 msgid "Fetching %s\n"
-msgstr "Đang lấy (fetch) %s\n"
+msgstr "Đang lấy về %s\n"
 
-#: builtin/fetch.c:896 builtin/remote.c:100
+#: builtin/fetch.c:930 builtin/remote.c:100
 #, c-format
 msgid "Could not fetch %s"
-msgstr "không thể fetch (lấy) %s"
+msgstr "không thể lấy về %s"
 
-#: builtin/fetch.c:915
+#: builtin/fetch.c:949
 msgid ""
 "No remote repository specified.  Please, specify either a URL or a\n"
 "remote name from which new revisions should be fetched."
@@ -4564,32 +5093,32 @@ msgstr ""
 "Chưa chỉ ra kho chứa máy chủ.  Xin hãy chỉ định hoặc là URL hoặc\n"
 "tên máy chủ từ cái mà những điểm xét duyệt mới có thể được fetch (lấy về)."
 
-#: builtin/fetch.c:935
+#: builtin/fetch.c:969
 msgid "You need to specify a tag name."
 msgstr "Bạn phải định rõ tên thẻ."
 
-#: builtin/fetch.c:981
+#: builtin/fetch.c:1015
 msgid "--depth and --unshallow cannot be used together"
 msgstr "tùy chọn --depth và --unshallow không thể sử dụng cùng với nhau"
 
-#: builtin/fetch.c:983
+#: builtin/fetch.c:1017
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "--unshallow trên kho hoàn chỉnh là không hợp lý"
 
-#: builtin/fetch.c:1002
+#: builtin/fetch.c:1036
 msgid "fetch --all does not take a repository argument"
 msgstr "lệnh lấy về \"fetch --all\" không lấy đối số kho chứa"
 
-#: builtin/fetch.c:1004
+#: builtin/fetch.c:1038
 msgid "fetch --all does not make sense with refspecs"
 msgstr "lệnh lấy về \"fetch --all\" không hợp lý với refspecs"
 
-#: builtin/fetch.c:1015
+#: builtin/fetch.c:1049
 #, c-format
 msgid "No such remote or remote group: %s"
 msgstr "không có nhóm máy chủ hay máy chủ như thế: %s"
 
-#: builtin/fetch.c:1023
+#: builtin/fetch.c:1057
 msgid "Fetching a group and specifying refspecs does not make sense"
 msgstr "Việc lấy về cả một nhóm và chỉ định refspecs không hợp lý"
 
@@ -4598,29 +5127,29 @@ msgid "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"
 msgstr ""
 "git fmt-merge-msg [-m <thông điệp>] [--log[=<n>]|--no-log] [--file <tập-tin>]"
 
-#: builtin/fmt-merge-msg.c:659 builtin/fmt-merge-msg.c:662 builtin/grep.c:701
-#: builtin/merge.c:188 builtin/show-branch.c:656 builtin/show-ref.c:175
-#: builtin/tag.c:446 parse-options.h:133 parse-options.h:239
+#: builtin/fmt-merge-msg.c:663 builtin/fmt-merge-msg.c:666 builtin/grep.c:700
+#: builtin/merge.c:203 builtin/show-branch.c:655 builtin/show-ref.c:179
+#: builtin/tag.c:446 parse-options.h:133 parse-options.h:243
 msgid "n"
 msgstr "n"
 
-#: builtin/fmt-merge-msg.c:660
+#: builtin/fmt-merge-msg.c:664
 msgid "populate log with at most <n> entries from shortlog"
 msgstr "gắn nhật ký với ít nhất <n> mục từ lệnh “shortlog”"
 
-#: builtin/fmt-merge-msg.c:663
+#: builtin/fmt-merge-msg.c:667
 msgid "alias for --log (deprecated)"
 msgstr "bí danh cho --log (không được dùng)"
 
-#: builtin/fmt-merge-msg.c:666
+#: builtin/fmt-merge-msg.c:670
 msgid "text"
 msgstr "văn bản"
 
-#: builtin/fmt-merge-msg.c:667
+#: builtin/fmt-merge-msg.c:671
 msgid "use <text> as start of message"
 msgstr "dùng <văn bản thường> để bắt đầu ghi chú"
 
-#: builtin/fmt-merge-msg.c:668
+#: builtin/fmt-merge-msg.c:672
 msgid "file to read from"
 msgstr "tập tin để đọc dữ liệu từ đó"
 
@@ -4760,228 +5289,223 @@ msgstr "git grep [các-tùy-chọn] [-e] <mẫu> [<rev>...] [[--] <đường-d
 msgid "grep: failed to create thread: %s"
 msgstr "grep: gặp lỗi tạo tuyến (thread): %s"
 
-#: builtin/grep.c:365
+#: builtin/grep.c:364
 #, c-format
 msgid "Failed to chdir: %s"
 msgstr "Gặp lỗi với lệnh chdir: %s"
 
-#: builtin/grep.c:443 builtin/grep.c:478
+#: builtin/grep.c:442 builtin/grep.c:477
 #, c-format
 msgid "unable to read tree (%s)"
 msgstr "không thể đọc cây (%s)"
 
-#: builtin/grep.c:493
+#: builtin/grep.c:492
 #, c-format
 msgid "unable to grep from object of type %s"
 msgstr "không thể thực hiện lệnh grep (lọc tìm) từ đối tượng thuộc kiểu %s"
 
-#: builtin/grep.c:551
+#: builtin/grep.c:550
 #, c-format
 msgid "switch `%c' expects a numerical value"
 msgstr "chuyển đến “%c” cần một giá trị bằng số"
 
-#: builtin/grep.c:568
+#: builtin/grep.c:567
 #, c-format
 msgid "cannot open '%s'"
 msgstr "không mở được “%s”"
 
-#: builtin/grep.c:643
+#: builtin/grep.c:642
 msgid "search in index instead of in the work tree"
 msgstr "tìm trong bảng mục lục thay vì trong cây làm việc"
 
-#: builtin/grep.c:645
+#: builtin/grep.c:644
 msgid "find in contents not managed by git"
 msgstr "tìm trong nội dung không được quản lý bởi git"
 
-#: builtin/grep.c:647
+#: builtin/grep.c:646
 msgid "search in both tracked and untracked files"
 msgstr "tìm kiếm các tập tin được và chưa được theo dõi dấu vết"
 
-#: builtin/grep.c:649
+#: builtin/grep.c:648
 msgid "search also in ignored files"
 msgstr "tìm cả trong các tập tin đã bị lờ đi"
 
-#: builtin/grep.c:652
+#: builtin/grep.c:651
 msgid "show non-matching lines"
 msgstr "hiển thị  những dòng không khớp với mẫu"
 
-#: builtin/grep.c:654
+#: builtin/grep.c:653
 msgid "case insensitive matching"
 msgstr "phân biệt chữ hoa/thường"
 
-#: builtin/grep.c:656
+#: builtin/grep.c:655
 msgid "match patterns only at word boundaries"
 msgstr "chỉ khớp mẫu tại đường ranh giới từ"
 
-#: builtin/grep.c:658
+#: builtin/grep.c:657
 msgid "process binary files as text"
 msgstr "xử lý tập tin nhị phân như là dạng văn bản thường"
 
-#: builtin/grep.c:660
+#: builtin/grep.c:659
 msgid "don't match patterns in binary files"
 msgstr "không khớp mẫu trong các tập tin nhị phân"
 
-#: builtin/grep.c:663
+#: builtin/grep.c:662
 msgid "descend at most <depth> levels"
 msgstr "giảm xuống ít nhất mức <sâu>"
 
-#: builtin/grep.c:667
+#: builtin/grep.c:666
 msgid "use extended POSIX regular expressions"
 msgstr "dùng biểu thức chính qui POSIX có mở rộng"
 
-#: builtin/grep.c:670
+#: builtin/grep.c:669
 msgid "use basic POSIX regular expressions (default)"
 msgstr "sử dụng biểu thức chính quy kiểu POSIX (mặc định)"
 
-#: builtin/grep.c:673
+#: builtin/grep.c:672
 msgid "interpret patterns as fixed strings"
 msgstr "diễn dịch các mẫu như là chuỗi cố định"
 
-#: builtin/grep.c:676
+#: builtin/grep.c:675
 msgid "use Perl-compatible regular expressions"
 msgstr "sử dụng biểu thức chính quy tương thích Perl"
 
-#: builtin/grep.c:679
+#: builtin/grep.c:678
 msgid "show line numbers"
 msgstr "hiển thị số của dòng"
 
-#: builtin/grep.c:680
+#: builtin/grep.c:679
 msgid "don't show filenames"
 msgstr "không hiển thị tên tập tin"
 
-#: builtin/grep.c:681
+#: builtin/grep.c:680
 msgid "show filenames"
 msgstr "hiển thị các tên tập tin"
 
-#: builtin/grep.c:683
+#: builtin/grep.c:682
 msgid "show filenames relative to top directory"
 msgstr "hiển thị tên tập tin tương đối với thư mục đỉnh (top)"
 
-#: builtin/grep.c:685
+#: builtin/grep.c:684
 msgid "show only filenames instead of matching lines"
 msgstr "chỉ hiển thị tên tập tin thay vì những dòng khớp với mẫu"
 
-#: builtin/grep.c:687
+#: builtin/grep.c:686
 msgid "synonym for --files-with-matches"
 msgstr "đồng nghĩa với --files-with-matches"
 
-#: builtin/grep.c:690
+#: builtin/grep.c:689
 msgid "show only the names of files without match"
 msgstr "chỉ hiển thị tên cho những tập tin không khớp với mẫu"
 
-#: builtin/grep.c:692
+#: builtin/grep.c:691
 msgid "print NUL after filenames"
 msgstr "thêm NUL vào sau tên tập tin"
 
-#: builtin/grep.c:694
+#: builtin/grep.c:693
 msgid "show the number of matches instead of matching lines"
 msgstr "hiển thị số lượng khớp thay vì những dòng khớp với mẫu"
 
-#: builtin/grep.c:695
+#: builtin/grep.c:694
 msgid "highlight matches"
 msgstr "tô sáng cái khớp"
 
-#: builtin/grep.c:697
+#: builtin/grep.c:696
 msgid "print empty line between matches from different files"
 msgstr "hiển thị dòng trống giữa các lần khớp từ các tập tin khác biệt"
 
-#: builtin/grep.c:699
+#: builtin/grep.c:698
 msgid "show filename only once above matches from same file"
 msgstr ""
 "hiển thị tên tập tin một lần phía trên các lần khớp từ cùng một tập tin"
 
-#: builtin/grep.c:702
+#: builtin/grep.c:701
 msgid "show <n> context lines before and after matches"
 msgstr "hiển thị <n> dòng nội dung phía trước và sau các lần khớp"
 
-#: builtin/grep.c:705
+#: builtin/grep.c:704
 msgid "show <n> context lines before matches"
 msgstr "hiển thị <n> dòng nội dung trước khớp"
 
-#: builtin/grep.c:707
+#: builtin/grep.c:706
 msgid "show <n> context lines after matches"
 msgstr "hiển thị <n> dòng nội dung sau khớp"
 
-#: builtin/grep.c:708
+#: builtin/grep.c:707
 msgid "shortcut for -C NUM"
 msgstr "Dạng tắt cho -C SỐ"
 
-#: builtin/grep.c:711
+#: builtin/grep.c:710
 msgid "show a line with the function name before matches"
 msgstr "hiển thị dòng vói tên hàm trước các lần khớp"
 
-#: builtin/grep.c:713
+#: builtin/grep.c:712
 msgid "show the surrounding function"
 msgstr "hiển thị hàm bao quanh"
 
-#: builtin/grep.c:716
+#: builtin/grep.c:715
 msgid "read patterns from file"
 msgstr "đọc mẫu từ tập-tin"
 
-#: builtin/grep.c:718
+#: builtin/grep.c:717
 msgid "match <pattern>"
 msgstr "match <mẫu>"
 
-#: builtin/grep.c:720
+#: builtin/grep.c:719
 msgid "combine patterns specified with -e"
 msgstr "tổ hợp mẫu được chỉ ra với tùy chọn -e"
 
-#: builtin/grep.c:732
+#: builtin/grep.c:731
 msgid "indicate hit with exit status without output"
 msgstr "đưa ra gợi ý với trạng thái thoát mà không có kết xuất"
 
-#: builtin/grep.c:734
+#: builtin/grep.c:733
 msgid "show only matches from files that match all patterns"
 msgstr "chỉ hiển thị những cái khớp từ tập tin mà nó khớp toàn bộ các mẫu"
 
-#: builtin/grep.c:736
+#: builtin/grep.c:735
 msgid "show parse tree for grep expression"
 msgstr "hiển thị cây phân tích cú pháp cho biểu thức “grep” (tìm kiếm)"
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "pager"
 msgstr "trang giấy"
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "show matching files in the pager"
 msgstr "hiển thị các tập tin khớp trong trang giấy"
 
-#: builtin/grep.c:743
+#: builtin/grep.c:742
 msgid "allow calling of grep(1) (ignored by this build)"
 msgstr "cho phép gọi grep(1) (bị bỏ qua bởi lần dịch này)"
 
-#: builtin/grep.c:744 builtin/show-ref.c:184
+#: builtin/grep.c:743 builtin/show-ref.c:188
 msgid "show usage"
 msgstr "hiển thị cách dùng"
 
-#: builtin/grep.c:811
+#: builtin/grep.c:810
 msgid "no pattern given."
 msgstr "chưa chỉ ra mẫu."
 
-#: builtin/grep.c:825
-#, c-format
-msgid "bad object %s"
-msgstr "đối tượng sai %s"
-
-#: builtin/grep.c:868
+#: builtin/grep.c:865
 msgid "--open-files-in-pager only works on the worktree"
 msgstr "--open-files-in-pager chỉ làm việc trên cây-làm-việc"
 
-#: builtin/grep.c:891
+#: builtin/grep.c:888
 msgid "--cached or --untracked cannot be used with --no-index."
 msgstr "--cached hay --untracked không được sử dụng với --no-index."
 
-#: builtin/grep.c:896
+#: builtin/grep.c:893
 msgid "--no-index or --untracked cannot be used with revs."
 msgstr ""
 "--no-index hay --untracked không được sử dụng cùng với các tùy chọn liên "
 "quan đến revs."
 
-#: builtin/grep.c:899
+#: builtin/grep.c:896
 msgid "--[no-]exclude-standard cannot be used for tracked contents."
 msgstr "--[no-]exclude-standard không thể sử dụng cho nội dung lưu dấu vết."
 
-#: builtin/grep.c:907
+#: builtin/grep.c:904
 msgid "both --cached and trees are given."
 msgstr "cả hai --cached và các cây phải được chỉ ra."
 
@@ -5021,10 +5545,14 @@ msgstr "lưu các tập tin mà nó không có các bộ lọc"
 msgid "process file as it were from this path"
 msgstr "xử lý tập tin như là nó đang ở thư mục này"
 
-#: builtin/help.c:42
+#: builtin/help.c:41
 msgid "print all available commands"
 msgstr "hiển thị danh sách các câu lệnh người dùng có thể sử dụng"
 
+#: builtin/help.c:42
+msgid "print list of useful guides"
+msgstr "hiển thị danh sách các hướng dẫn hữu dụng"
+
 #: builtin/help.c:43
 msgid "show man page"
 msgstr "hiển thị trang man"
@@ -5038,8 +5566,8 @@ msgid "show info page"
 msgstr "hiện trang info"
 
 #: builtin/help.c:52
-msgid "git help [--all] [--man|--web|--info] [command]"
-msgstr "git help [--all] [--man|--web|--info] [lệnh]"
+msgid "git help [--all] [--guides] [--man|--web|--info] [command]"
+msgstr "git help [--all] [--guides] [--man|--web|--info] [lệnh]"
 
 #: builtin/help.c:64
 #, c-format
@@ -5091,294 +5619,326 @@ msgstr "“%s”: không rõ chương trình xem man."
 msgid "no man viewer handled the request"
 msgstr "không có trình xem trợ giúp dạng manpage tiếp hợp với yêu cầu"
 
-#: builtin/help.c:374
-msgid "no info viewer handled the request"
-msgstr "không có trình xem trợ giúp dạng info tiếp hợp với yêu cầu"
+#: builtin/help.c:374
+msgid "no info viewer handled the request"
+msgstr "không có trình xem trợ giúp dạng info tiếp hợp với yêu cầu"
+
+#: builtin/help.c:420
+msgid "Defining attributes per path"
+msgstr "Định nghĩa các thuộc tính cho mỗi đường dẫn"
+
+#: builtin/help.c:421
+msgid "A Git glossary"
+msgstr "Thuật ngữ chuyên môn Git"
+
+#: builtin/help.c:422
+msgid "Specifies intentionally untracked files to ignore"
+msgstr "Chỉ định các tập tin không cần theo dõi"
+
+#: builtin/help.c:423
+msgid "Defining submodule properties"
+msgstr "Định nghĩa thuộc tính mô-đun-con"
+
+#: builtin/help.c:424
+msgid "Specifying revisions and ranges for Git"
+msgstr "Chỉ định điểm xét duyệt và vùng cho Git"
+
+#: builtin/help.c:425
+msgid "A tutorial introduction to Git (for version 1.5.1 or newer)"
+msgstr "Hướng dẫn cách dùng Git ở mức cơ bản (bản 1.5.1 hay mới hơn)"
+
+#: builtin/help.c:426
+msgid "An overview of recommended workflows with Git"
+msgstr "Tổng quan về luồng công việc khuyến nghị nên dùng với Git."
+
+#: builtin/help.c:438
+msgid "The common Git guides are:\n"
+msgstr "Các chỉ dẫn chung về cách dùng Git là:\n"
 
-#: builtin/help.c:429 builtin/help.c:436
+#: builtin/help.c:460 builtin/help.c:476
 #, c-format
 msgid "usage: %s%s"
 msgstr "cách sử dụng: %s%s"
 
-#: builtin/help.c:452
+#: builtin/help.c:492
 #, c-format
 msgid "`git %s' is aliased to `%s'"
 msgstr "“git %s” được đặt bí danh thành “%s”"
 
-#: builtin/index-pack.c:170
+#: builtin/index-pack.c:184
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "kiểu đối tượng không khớp tại %s"
 
-#: builtin/index-pack.c:190
+#: builtin/index-pack.c:204
 msgid "object of unexpected type"
 msgstr "đối tượng của kiểu không mong đợi"
 
-#: builtin/index-pack.c:227
+#: builtin/index-pack.c:244
 #, c-format
 msgid "cannot fill %d byte"
 msgid_plural "cannot fill %d bytes"
 msgstr[0] "không thể điền vào %d byte"
 msgstr[1] "không thể điền vào %d byte"
 
-#: builtin/index-pack.c:237
+#: builtin/index-pack.c:254
 msgid "early EOF"
 msgstr "vừa đúng lúc EOF"
 
-#: builtin/index-pack.c:238
+#: builtin/index-pack.c:255
 msgid "read error on input"
 msgstr "lỗi đọc ở đầu vào"
 
-#: builtin/index-pack.c:250
+#: builtin/index-pack.c:267
 msgid "used more bytes than were available"
 msgstr "sử dụng nhiều hơn số lượng byte mà nó sẵn có"
 
-#: builtin/index-pack.c:257
+#: builtin/index-pack.c:274
 msgid "pack too large for current definition of off_t"
 msgstr "pack quá lớn so với định nghĩa hiện tại của kiểu off_t"
 
-#: builtin/index-pack.c:273
+#: builtin/index-pack.c:290
 #, c-format
 msgid "unable to create '%s'"
 msgstr "không thể tạo “%s”"
 
-#: builtin/index-pack.c:278
+#: builtin/index-pack.c:295
 #, c-format
 msgid "cannot open packfile '%s'"
 msgstr "không thể mở packfile “%s”"
 
-#: builtin/index-pack.c:292
+#: builtin/index-pack.c:309
 msgid "pack signature mismatch"
 msgstr "chữ ký cho pack không khớp"
 
-#: builtin/index-pack.c:294
+#: builtin/index-pack.c:311
 #, c-format
 msgid "pack version %<PRIu32> unsupported"
 msgstr "không hỗ trợ phiên bản pack %<PRIu32>"
 
-#: builtin/index-pack.c:312
+#: builtin/index-pack.c:329
 #, c-format
 msgid "pack has bad object at offset %lu: %s"
 msgstr "pack có đối tượng sai khoảng bù (offset) %lu: %s"
 
-#: builtin/index-pack.c:434
+#: builtin/index-pack.c:451
 #, c-format
 msgid "inflate returned %d"
 msgstr "xả nén trả về %d"
 
-#: builtin/index-pack.c:483
+#: builtin/index-pack.c:500
 msgid "offset value overflow for delta base object"
 msgstr "tràn giá trị khoảng bù cho đối tượng delta cơ sở"
 
-#: builtin/index-pack.c:491
+#: builtin/index-pack.c:508
 msgid "delta base offset is out of bound"
 msgstr "khoảng bù cơ sở cho delta nằm ngoài phạm vi"
 
-#: builtin/index-pack.c:499
+#: builtin/index-pack.c:516
 #, c-format
 msgid "unknown object type %d"
 msgstr "không hiểu kiểu đối tượng %d"
 
-#: builtin/index-pack.c:530
+#: builtin/index-pack.c:547
 msgid "cannot pread pack file"
 msgstr "không thể chạy hàm pread cho tập tin pack"
 
-#: builtin/index-pack.c:532
+#: builtin/index-pack.c:549
 #, c-format
 msgid "premature end of pack file, %lu byte missing"
 msgid_plural "premature end of pack file, %lu bytes missing"
 msgstr[0] "tập tin pack bị kết thúc sớm, %lu byte bị thiếu"
 msgstr[1] "tập tin pack bị kết thúc sớm, %lu byte bị thiếu"
 
-#: builtin/index-pack.c:558
+#: builtin/index-pack.c:575
 msgid "serious inflate inconsistency"
 msgstr "sự mâu thuẫn xả nén nghiêm trọng"
 
-#: builtin/index-pack.c:649 builtin/index-pack.c:655 builtin/index-pack.c:678
-#: builtin/index-pack.c:712 builtin/index-pack.c:721
+#: 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
 #, c-format
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "SỰ VA CHẠM SHA1 ĐàXẢY RA VỚI %s!"
 
-#: builtin/index-pack.c:652 builtin/pack-objects.c:170
+#: builtin/index-pack.c:669 builtin/pack-objects.c:170
 #: builtin/pack-objects.c:262
 #, c-format
 msgid "unable to read %s"
 msgstr "không thể đọc %s"
 
-#: builtin/index-pack.c:718
+#: builtin/index-pack.c:735
 #, c-format
 msgid "cannot read existing object %s"
 msgstr "không thể đọc đối tượng đã tồn tại %s"
 
-#: builtin/index-pack.c:732
+#: builtin/index-pack.c:749
 #, c-format
 msgid "invalid blob object %s"
 msgstr "đối tượng blob không hợp lệ %s"
 
-#: builtin/index-pack.c:747
+#: builtin/index-pack.c:763
 #, c-format
 msgid "invalid %s"
 msgstr "%s không hợp lệ"
 
-#: builtin/index-pack.c:749
+#: builtin/index-pack.c:766
 msgid "Error in object"
 msgstr "Lỗi trong đối tượng"
 
-#: builtin/index-pack.c:751
+#: builtin/index-pack.c:768
 #, c-format
 msgid "Not all child objects of %s are reachable"
 msgstr "Không phải tất cả các đối tượng con của %s là có thể với tới được"
 
-#: builtin/index-pack.c:821 builtin/index-pack.c:847
+#: builtin/index-pack.c:838 builtin/index-pack.c:868
 msgid "failed to apply delta"
 msgstr "gặp lỗi khi áp dụng delta"
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Receiving objects"
 msgstr "Đang nhận về các đối tượng"
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Indexing objects"
 msgstr "Các đối tượng bảng mục lục"
 
-#: builtin/index-pack.c:1012
+#: builtin/index-pack.c:1035
 msgid "pack is corrupted (SHA1 mismatch)"
 msgstr "pack bị sai hỏng (SHA1 không khớp)"
 
-#: builtin/index-pack.c:1017
+#: builtin/index-pack.c:1040
 msgid "cannot fstat packfile"
 msgstr "không thể fstat packfile"
 
-#: builtin/index-pack.c:1020
+#: builtin/index-pack.c:1043
 msgid "pack has junk at the end"
 msgstr "pack có phần thừa ở cuối"
 
-#: builtin/index-pack.c:1031
+#: builtin/index-pack.c:1054
 msgid "confusion beyond insanity in parse_pack_objects()"
 msgstr "lộn xộn hơn cả điên rồ khi chạy hàm parse_pack_objects()"
 
-#: builtin/index-pack.c:1054
+#: builtin/index-pack.c:1077
 msgid "Resolving deltas"
 msgstr "Đang phân giải các delta"
 
-#: builtin/index-pack.c:1064
+#: builtin/index-pack.c:1087
 #, c-format
 msgid "unable to create thread: %s"
 msgstr "không thể tạo tuyến: %s"
 
-#: builtin/index-pack.c:1106
+#: builtin/index-pack.c:1129
 msgid "confusion beyond insanity"
 msgstr "lộn xộn hơn cả điên rồ"
 
-#: builtin/index-pack.c:1112
+#: builtin/index-pack.c:1137
 #, c-format
 msgid "completed with %d local objects"
 msgstr "đầy đủ với %d đối tượng nội bộ"
 
-#: builtin/index-pack.c:1121
+#: builtin/index-pack.c:1147
 #, c-format
 msgid "Unexpected tail checksum for %s (disk corruption?)"
 msgstr "Tổng kiểm tra tail không như mong đợi cho %s (đĩa hỏng?)"
 
-#: builtin/index-pack.c:1125
+#: builtin/index-pack.c:1151
 #, c-format
 msgid "pack has %d unresolved delta"
 msgid_plural "pack has %d unresolved deltas"
 msgstr[0] "pack có %d delta chưa được giải quyết"
 msgstr[1] "pack có %d delta chưa được giải quyết"
 
-#: builtin/index-pack.c:1150
+#: builtin/index-pack.c:1176
 #, c-format
 msgid "unable to deflate appended object (%d)"
 msgstr "không thể xả đối tượng nối thêm (%d)"
 
-#: builtin/index-pack.c:1229
+#: builtin/index-pack.c:1255
 #, c-format
 msgid "local object %s is corrupt"
 msgstr "đối tượng nội bộ %s bị hỏng"
 
-#: builtin/index-pack.c:1253
+#: builtin/index-pack.c:1279
 msgid "error while closing pack file"
 msgstr "gặp lỗi trong khi đóng tập tin pack"
 
-#: builtin/index-pack.c:1266
+#: builtin/index-pack.c:1292
 #, c-format
 msgid "cannot write keep file '%s'"
 msgstr "không thể ghi tập tin giữ lại “%s”"
 
-#: builtin/index-pack.c:1274
+#: builtin/index-pack.c:1300
 #, c-format
 msgid "cannot close written keep file '%s'"
 msgstr "không thể đóng tập tin giữ lại đã được ghi “%s”"
 
-#: builtin/index-pack.c:1287
+#: builtin/index-pack.c:1313
 msgid "cannot store pack file"
 msgstr "không thể lưu tập tin pack"
 
-#: builtin/index-pack.c:1298
+#: builtin/index-pack.c:1324
 msgid "cannot store index file"
 msgstr "không thể lưu trữ tập tin ghi mục lục"
 
-#: builtin/index-pack.c:1331
+#: builtin/index-pack.c:1357
 #, c-format
 msgid "bad pack.indexversion=%<PRIu32>"
 msgstr "sai pack.indexversion=%<PRIu32>"
 
-#: builtin/index-pack.c:1337
+#: builtin/index-pack.c:1363
 #, c-format
 msgid "invalid number of threads specified (%d)"
 msgstr "số tuyến chỉ ra không hợp lệ (%d)"
 
-#: builtin/index-pack.c:1341 builtin/index-pack.c:1514
+#: builtin/index-pack.c:1367 builtin/index-pack.c:1545
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "không hỗ trợ đa tuyến, bỏ qua %s"
 
-#: builtin/index-pack.c:1399
+#: builtin/index-pack.c:1425
 #, c-format
 msgid "Cannot open existing pack file '%s'"
 msgstr "Không thể mở tập tin pack đã sẵn có “%s”"
 
-#: builtin/index-pack.c:1401
+#: builtin/index-pack.c:1427
 #, c-format
 msgid "Cannot open existing pack idx file for '%s'"
 msgstr "Không thể mở tập tin “pack idx” cho “%s”"
 
-#: builtin/index-pack.c:1448
+#: builtin/index-pack.c:1474
 #, c-format
 msgid "non delta: %d object"
 msgid_plural "non delta: %d objects"
 msgstr[0] "không delta: %d đối tượng"
 msgstr[1] "không delta: %d đối tượng"
 
-#: builtin/index-pack.c:1455
+#: builtin/index-pack.c:1481
 #, c-format
 msgid "chain length = %d: %lu object"
 msgid_plural "chain length = %d: %lu objects"
 msgstr[0] "chiều dài xích = %d: %lu đối tượng"
 msgstr[1] "chiều dài xích = %d: %lu đối tượng"
 
-#: builtin/index-pack.c:1482
+#: builtin/index-pack.c:1509
 msgid "Cannot come back to cwd"
 msgstr "Không thể quay lại cwd"
 
-#: builtin/index-pack.c:1526 builtin/index-pack.c:1529
-#: builtin/index-pack.c:1541 builtin/index-pack.c:1545
+#: builtin/index-pack.c:1557 builtin/index-pack.c:1560
+#: builtin/index-pack.c:1572 builtin/index-pack.c:1576
 #, c-format
 msgid "bad %s"
 msgstr "%s sai"
 
-#: builtin/index-pack.c:1559
+#: builtin/index-pack.c:1590
 msgid "--fix-thin cannot be used without --stdin"
 msgstr "--fix-thin không thể được dùng mà không có --stdin"
 
-#: builtin/index-pack.c:1563 builtin/index-pack.c:1573
+#: builtin/index-pack.c:1594 builtin/index-pack.c:1604
 #, c-format
 msgid "packfile name '%s' does not end with '.pack'"
 msgstr "tên tập tin packfile “%s” không được kết thúc bằng đuôi “.pack”"
 
-#: builtin/index-pack.c:1582
+#: builtin/index-pack.c:1613
 msgid "--verify with no packfile name given"
 msgstr "dùng tùy chọn --verify mà không đưa ra tên packfile"
 
@@ -5514,7 +6074,7 @@ msgstr "các quyền"
 msgid "specify that the git repository is to be shared amongst several users"
 msgstr "chỉ ra cái mà kho git được chia sẻ giữa nhiều người dùng"
 
-#: builtin/init-db.c:493 builtin/prune-packed.c:77
+#: builtin/init-db.c:493 builtin/prune-packed.c:75
 msgid "be quiet"
 msgstr "im lặng"
 
@@ -5546,246 +6106,255 @@ msgstr "Không thể truy cập thư mục làm việc hiện hành"
 msgid "Cannot access work tree '%s'"
 msgstr "không thể truy cập cây (tree) làm việc “%s”"
 
-#: builtin/log.c:39
-msgid "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
-msgstr "git log [<các-tùy-chọn>] [<kể-từ>..<cho-đến>] [[--] <đường-dẫn>...]\n"
+#: builtin/log.c:41
+msgid "git log [<options>] [<revision range>] [[--] <path>...]\n"
+msgstr "git log [<các-tùy-chọn>] [<vùng xem xét>] [[--] <đường-dẫn>...]\n"
 
-#: builtin/log.c:40
+#: builtin/log.c:42
 msgid "   or: git show [options] <object>..."
-msgstr "   or: git show [các-tùy-chọn] <đối-tượng>..."
+msgstr "  hay: git show [các-tùy-chọn] <đối-tượng>..."
 
-#: builtin/log.c:102
+#: builtin/log.c:124
 msgid "suppress diff output"
 msgstr "chặn mọi kết xuất từ diff"
 
-#: builtin/log.c:103
+#: builtin/log.c:125
 msgid "show source"
 msgstr "hiển thị mã nguồn"
 
-#: builtin/log.c:104
+#: builtin/log.c:126
 msgid "Use mail map file"
 msgstr "Sử dụng tập tin ánh xạ thư"
 
-#: builtin/log.c:105
+#: builtin/log.c:127
 msgid "decorate options"
 msgstr "các tùy chọn trang trí"
 
-#: builtin/log.c:198
+#: builtin/log.c:230
 #, c-format
 msgid "Final output: %d %s\n"
 msgstr "Kết xuất cuối cùng: %d %s\n"
 
-#: builtin/log.c:419 builtin/log.c:511
+#: builtin/log.c:453 builtin/log.c:545
 #, c-format
 msgid "Could not read object %s"
 msgstr "Không thể đọc đối tượng %s"
 
-#: builtin/log.c:535
+#: builtin/log.c:569
 #, c-format
 msgid "Unknown type: %d"
 msgstr "Không nhận ra kiểu: %d"
 
-#: builtin/log.c:627
+#: builtin/log.c:669
 msgid "format.headers without value"
 msgstr "format.headers không có giá trị cụ thể"
 
-#: builtin/log.c:701
+#: builtin/log.c:751
 msgid "name of output directory is too long"
 msgstr "tên của thư mục kết xuất quá dài"
 
-#: builtin/log.c:717
+#: builtin/log.c:767
 #, c-format
 msgid "Cannot open patch file %s"
 msgstr "Không thể mở tập tin miếng vá: %s"
 
-#: builtin/log.c:731
+#: builtin/log.c:781
 msgid "Need exactly one range."
 msgstr "Cần chính xác một vùng."
 
-#: builtin/log.c:739
+#: builtin/log.c:789
 msgid "Not a range."
 msgstr "Không phải là một vùng."
 
-#: builtin/log.c:812
+#: builtin/log.c:891
 msgid "Cover letter needs email format"
 msgstr "”Cover letter” cần cho định dạng thư"
 
-#: builtin/log.c:885
+#: builtin/log.c:967
 #, c-format
 msgid "insane in-reply-to: %s"
 msgstr "in-reply-to điên rồ: %s"
 
-#: builtin/log.c:913
+#: builtin/log.c:995
 msgid "git format-patch [options] [<since> | <revision range>]"
 msgstr "git format-patch [các-tùy-chọn] [<kể-từ> | <vùng-xem-xét>]"
 
-#: builtin/log.c:958
+#: builtin/log.c:1040
 msgid "Two output directories?"
 msgstr "Hai thư mục kết xuất?"
 
-#: builtin/log.c:1097
+#: builtin/log.c:1155
 msgid "use [PATCH n/m] even with a single patch"
 msgstr "dùng [PATCH n/m] ngay cả với miếng vá đơn"
 
-#: builtin/log.c:1100
+#: builtin/log.c:1158
 msgid "use [PATCH] even with multiple patches"
 msgstr "dùng [VÁ] ngay cả với các miếng vá phức tạp"
 
-#: builtin/log.c:1104
+#: builtin/log.c:1162
 msgid "print patches to standard out"
 msgstr "hiển thị miếng vá ra đầu ra chuẩn"
 
-#: builtin/log.c:1106
+#: builtin/log.c:1164
 msgid "generate a cover letter"
 msgstr "tạo bì thư"
 
-#: builtin/log.c:1108
+#: builtin/log.c:1166
 msgid "use simple number sequence for output file names"
 msgstr "sử dụng chỗi dãy số dạng đơn giản cho tên tập-tin xuất ra"
 
-#: builtin/log.c:1109
+#: builtin/log.c:1167
 msgid "sfx"
 msgstr "sfx"
 
-#: builtin/log.c:1110
+#: builtin/log.c:1168
 msgid "use <sfx> instead of '.patch'"
 msgstr "sử dụng <sfx> thay cho “.patch”"
 
-#: builtin/log.c:1112
+#: builtin/log.c:1170
 msgid "start numbering patches at <n> instead of 1"
 msgstr "bắt đầu đánh số miếng vá từ <n> thay vì 1"
 
-#: builtin/log.c:1114
+#: builtin/log.c:1172
 msgid "mark the series as Nth re-roll"
 msgstr "đánh dấu chuỗi nối tiếp dạng thứ-N re-roll"
 
-#: builtin/log.c:1116
+#: builtin/log.c:1174
 msgid "Use [<prefix>] instead of [PATCH]"
 msgstr "Dùng [<tiền-tố>] thay cho [VÁ]"
 
-#: builtin/log.c:1119
+#: builtin/log.c:1177
 msgid "store resulting files in <dir>"
 msgstr "lưu các tập tin kết quả trong <t.mục>"
 
-#: builtin/log.c:1122
+#: builtin/log.c:1180
 msgid "don't strip/add [PATCH]"
 msgstr "không strip/add [VÁ]"
 
-#: builtin/log.c:1125
+#: builtin/log.c:1183
 msgid "don't output binary diffs"
 msgstr "không kết xuất diff (những khác biệt) nhị phân"
 
-#: builtin/log.c:1127
+#: builtin/log.c:1185
 msgid "don't include a patch matching a commit upstream"
 msgstr "không bao gồm miếng vá khớp với một lần chuyển giao thượng nguồn"
 
-#: builtin/log.c:1129
+#: builtin/log.c:1187
 msgid "show patch format instead of default (patch + stat)"
 msgstr "hiển thị định dạng miếng vá thay vì mặc định (miếng vá + thống kê)"
 
-#: builtin/log.c:1131
+#: builtin/log.c:1189
 msgid "Messaging"
 msgstr "Lời nhắn"
 
-#: builtin/log.c:1132
+#: builtin/log.c:1190
 msgid "header"
 msgstr "đầu đề thư"
 
-#: builtin/log.c:1133
+#: builtin/log.c:1191
 msgid "add email header"
 msgstr "thêm đầu đề thư"
 
-#: builtin/log.c:1134 builtin/log.c:1136
+#: builtin/log.c:1192 builtin/log.c:1194
 msgid "email"
 msgstr "thư điện tử"
 
-#: builtin/log.c:1134
+#: builtin/log.c:1192
 msgid "add To: header"
 msgstr "thêm To: đầu đề thư"
 
-#: builtin/log.c:1136
+#: builtin/log.c:1194
 msgid "add Cc: header"
 msgstr "thêm Cc: đầu đề thư"
 
-#: builtin/log.c:1138
+#: builtin/log.c:1196
+msgid "ident"
+msgstr "thụt lề"
+
+#: builtin/log.c:1197
+msgid "set From address to <ident> (or committer ident if absent)"
+msgstr ""
+"đặt `Địa chỉ gửi' thành <thụ lề> (hoặc thụt lề người commit nếu bỏ quên)"
+
+#: builtin/log.c:1199
 msgid "message-id"
 msgstr "message-id"
 
-#: builtin/log.c:1139
+#: builtin/log.c:1200
 msgid "make first mail a reply to <message-id>"
 msgstr "dùng thư đầu tiên để trả lời <message-id>"
 
-#: builtin/log.c:1140 builtin/log.c:1143
+#: builtin/log.c:1201 builtin/log.c:1204
 msgid "boundary"
 msgstr "ranh giới"
 
-#: builtin/log.c:1141
+#: builtin/log.c:1202
 msgid "attach the patch"
 msgstr "đính kèm miếng vá"
 
-#: builtin/log.c:1144
+#: builtin/log.c:1205
 msgid "inline the patch"
 msgstr "dùng miếng vá làm nội dung"
 
-#: builtin/log.c:1148
+#: builtin/log.c:1209
 msgid "enable message threading, styles: shallow, deep"
 msgstr "cho phép luồng lời nhắn, kiểu: “shallow”, “deep”"
 
-#: builtin/log.c:1150
+#: builtin/log.c:1211
 msgid "signature"
 msgstr "chữ ký"
 
-#: builtin/log.c:1151
+#: builtin/log.c:1212
 msgid "add a signature"
 msgstr "thêm chữ ký"
 
-#: builtin/log.c:1153
+#: builtin/log.c:1214
 msgid "don't print the patch filenames"
 msgstr "không hiển thị các tên tập tin của miếng vá"
 
-#: builtin/log.c:1202
+#: builtin/log.c:1288
 #, c-format
-msgid "bogus committer info %s"
-msgstr "thông tin người chuyển giao không có thực %s"
+msgid "invalid ident line: %s"
+msgstr "dòng thụt lề không hợp lệ: %s"
 
-#: builtin/log.c:1247
+#: builtin/log.c:1303
 msgid "-n and -k are mutually exclusive."
 msgstr "-n và  -k loại từ lẫn nhau."
 
-#: builtin/log.c:1249
+#: builtin/log.c:1305
 msgid "--subject-prefix and -k are mutually exclusive."
 msgstr "--subject-prefix và -k xung khắc nhau."
 
-#: builtin/log.c:1257
+#: builtin/log.c:1313
 msgid "--name-only does not make sense"
 msgstr "--name-only không hợp lý"
 
-#: builtin/log.c:1259
+#: builtin/log.c:1315
 msgid "--name-status does not make sense"
 msgstr "--name-status không hợp lý"
 
-#: builtin/log.c:1261
+#: builtin/log.c:1317
 msgid "--check does not make sense"
 msgstr "--check không hợp lý"
 
-#: builtin/log.c:1284
+#: builtin/log.c:1340
 msgid "standard output, or directory, which one?"
 msgstr "đầu ra chuẩn, hay thư mục, chọn cái nào?"
 
-#: builtin/log.c:1286
+#: builtin/log.c:1342
 #, c-format
 msgid "Could not create directory '%s'"
 msgstr "Không thể tạo thư mục “%s”"
 
-#: builtin/log.c:1439
+#: builtin/log.c:1490
 msgid "Failed to create output files"
 msgstr "Gặp lỗi khi tạo các tập tin kết xuất"
 
-#: builtin/log.c:1488
+#: builtin/log.c:1539
 msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
 msgstr "git cherry [-v] [<thượng-nguồn> [<head> [<giới-hạn>]]]"
 
-#: builtin/log.c:1543
+#: builtin/log.c:1594
 #, c-format
 msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
@@ -5793,103 +6362,103 @@ msgstr ""
 "Không tìm thấy nhánh mạng bị theo vết, hãy chỉ định <thượng-nguồn> một cách "
 "thủ công.\n"
 
-#: builtin/log.c:1556 builtin/log.c:1558 builtin/log.c:1570
+#: builtin/log.c:1607 builtin/log.c:1609 builtin/log.c:1621
 #, c-format
 msgid "Unknown commit %s"
 msgstr "Không hiểu lần chuyển giao (commit) %s"
 
-#: builtin/ls-files.c:409
+#: builtin/ls-files.c:408
 msgid "git ls-files [options] [<file>...]"
 msgstr "git ls-files [các-tùy-chọn] [<tập-tin>...]"
 
-#: builtin/ls-files.c:466
+#: builtin/ls-files.c:465
 msgid "identify the file status with tags"
 msgstr "nhận dạng các trạng thái tập tin với thẻ"
 
-#: builtin/ls-files.c:468
+#: builtin/ls-files.c:467
 msgid "use lowercase letters for 'assume unchanged' files"
 msgstr ""
 "dùng chữ cái viết thường cho các tập tin “assume unchanged” (giả định không "
 "thay đổi)"
 
-#: builtin/ls-files.c:470
+#: builtin/ls-files.c:469
 msgid "show cached files in the output (default)"
 msgstr "hiển thị các tập tin được nhớ tạm vào đầu ra (mặc định)"
 
-#: builtin/ls-files.c:472
+#: builtin/ls-files.c:471
 msgid "show deleted files in the output"
 msgstr "hiển thị các tập tin đã xóa trong kết xuất"
 
-#: builtin/ls-files.c:474
+#: builtin/ls-files.c:473
 msgid "show modified files in the output"
 msgstr "hiển thị các tập tin đã bị sửa đổi ra kết xuất"
 
-#: builtin/ls-files.c:476
+#: builtin/ls-files.c:475
 msgid "show other files in the output"
 msgstr "hiển thị các tập tin khác trong kết xuất"
 
-#: builtin/ls-files.c:478
+#: builtin/ls-files.c:477
 msgid "show ignored files in the output"
 msgstr "hiển thị các tập tin bị bỏ qua trong kết xuất"
 
-#: builtin/ls-files.c:481
+#: builtin/ls-files.c:480
 msgid "show staged contents' object name in the output"
 msgstr "hiển thị tên đối tượng của nội dung được lưu trạng thái ở kết xuất"
 
-#: builtin/ls-files.c:483
+#: builtin/ls-files.c:482
 msgid "show files on the filesystem that need to be removed"
 msgstr "hiển thị các tập tin trên hệ thống tập tin mà nó cần được gỡ bỏ"
 
-#: builtin/ls-files.c:485
+#: builtin/ls-files.c:484
 msgid "show 'other' directories' name only"
 msgstr "chỉ hiển thị tên của các thư mục “khác”"
 
-#: builtin/ls-files.c:488
+#: builtin/ls-files.c:487
 msgid "don't show empty directories"
 msgstr "không hiển thị thư mục rỗng"
 
-#: builtin/ls-files.c:491
+#: builtin/ls-files.c:490
 msgid "show unmerged files in the output"
 msgstr "hiển thị các tập tin chưa hòa trộn trong kết xuất"
 
-#: builtin/ls-files.c:493
+#: builtin/ls-files.c:492
 msgid "show resolve-undo information"
 msgstr "hiển thị thông tin resolve-undo"
 
-#: builtin/ls-files.c:495
+#: builtin/ls-files.c:494
 msgid "skip files matching pattern"
 msgstr "bỏ qua những tập tin khớp với một mẫu"
 
-#: builtin/ls-files.c:498
+#: builtin/ls-files.c:497
 msgid "exclude patterns are read from <file>"
 msgstr "mẫu loại trừ được đọc từ <tập tin>"
 
-#: builtin/ls-files.c:501
+#: builtin/ls-files.c:500
 msgid "read additional per-directory exclude patterns in <file>"
 msgstr "đọc thêm các mẫu ngoại trừ mỗi thư mục trong <tập tin>"
 
-#: builtin/ls-files.c:503
+#: builtin/ls-files.c:502
 msgid "add the standard git exclusions"
 msgstr "thêm loại trừ tiêu chuẩn kiểu git"
 
-#: builtin/ls-files.c:506
+#: builtin/ls-files.c:505
 msgid "make the output relative to the project top directory"
 msgstr "làm cho kết xuất liên quan đến thư mục ở mức cao nhất (gốc) của dự án"
 
-#: builtin/ls-files.c:509
+#: builtin/ls-files.c:508
 msgid "if any <file> is not in the index, treat this as an error"
 msgstr "nếu <tập tin> bất kỳ không ở trong bảng mục lục, xử lý nó như một lỗi"
 
-#: builtin/ls-files.c:510
+#: builtin/ls-files.c:509
 msgid "tree-ish"
 msgstr "tree-ish"
 
-#: builtin/ls-files.c:511
+#: builtin/ls-files.c:510
 msgid "pretend that paths removed since <tree-ish> are still present"
 msgstr ""
 "giả định rằng các đường dẫn đã bị gỡ bỏ kể từ <tree-ish> nay vẫn hiện diện"
 
-#: builtin/ls-files.c:513
+#: builtin/ls-files.c:512
 msgid "show debugging data"
 msgstr "hiển thị dữ liệu gỡ lỗi"
 
@@ -5941,166 +6510,170 @@ msgstr "git merge [các-tùy-chọn] <tin-nhắn> HEAD <commit>"
 msgid "git merge --abort"
 msgstr "git merge --abort"
 
-#: builtin/merge.c:90
+#: builtin/merge.c:98
 msgid "switch `m' requires a value"
 msgstr "switch “m” yêu cầu một giá trị"
 
-#: builtin/merge.c:127
+#: builtin/merge.c:135
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
 msgstr "Không tìm thấy chiến lược hòa trộn “%s”.\n"
 
-#: builtin/merge.c:128
+#: builtin/merge.c:136
 #, c-format
 msgid "Available strategies are:"
 msgstr "Các chiến lược sẵn sàng là:"
 
-#: builtin/merge.c:133
+#: builtin/merge.c:141
 #, c-format
 msgid "Available custom strategies are:"
 msgstr "Các chiến lược tùy chỉnh sẵn sàng là:"
 
-#: builtin/merge.c:183
+#: builtin/merge.c:198
 msgid "do not show a diffstat at the end of the merge"
 msgstr "không hiển thị thống kê khác biệt tại cuối của lần hòa trộn"
 
-#: builtin/merge.c:186
+#: builtin/merge.c:201
 msgid "show a diffstat at the end of the merge"
 msgstr "hiển thị thống kê khác biệt tại cuối của hòa trộn"
 
-#: builtin/merge.c:187
+#: builtin/merge.c:202
 msgid "(synonym to --stat)"
 msgstr "(đồng nghĩa với --stat)"
 
-#: builtin/merge.c:189
+#: builtin/merge.c:204
 msgid "add (at most <n>) entries from shortlog to merge commit message"
 msgstr "thêm (ít nhất <n>) mục từ shortlog cho ghi chú chuyển giao hòa trộn"
 
-#: builtin/merge.c:192
+#: builtin/merge.c:207
 msgid "create a single commit instead of doing a merge"
 msgstr "tạo một lần chuyển giao (commit) đưon thay vì thực hiện việc hòa trộn"
 
-#: builtin/merge.c:194
+#: builtin/merge.c:209
 msgid "perform a commit if the merge succeeds (default)"
 msgstr "thực hiện chuyển giao nếu hòa trộn thành công (mặc định)"
 
-#: builtin/merge.c:196
+#: builtin/merge.c:211
 msgid "edit message before committing"
 msgstr "sửa chú thích trước khi chuyển giao (commit)"
 
-#: builtin/merge.c:198
+#: builtin/merge.c:212
 msgid "allow fast-forward (default)"
 msgstr "cho phép fast-forward (mặc định)"
 
-#: builtin/merge.c:200
+#: builtin/merge.c:214
 msgid "abort if fast-forward is not possible"
 msgstr "bỏ qua nếu fast-forward không thể được"
 
-#: builtin/merge.c:202 builtin/notes.c:866 builtin/revert.c:112
+#: builtin/merge.c:218
+msgid "Verify that the named commit has a valid GPG signature"
+msgstr "Thẩm tra xem lần chuyển giao có tên đó có chữ ký GPG hợp lệ hay không"
+
+#: builtin/merge.c:219 builtin/notes.c:738 builtin/revert.c:114
 msgid "strategy"
 msgstr "chiến lược"
 
-#: builtin/merge.c:203
+#: builtin/merge.c:220
 msgid "merge strategy to use"
 msgstr "chiến lược hòa trộn sẽ dùng"
 
-#: builtin/merge.c:204
+#: builtin/merge.c:221
 msgid "option=value"
 msgstr "tùy_chọn=giá_trị"
 
-#: builtin/merge.c:205
+#: builtin/merge.c:222
 msgid "option for selected merge strategy"
 msgstr "tùy chọn cho chiến lược hòa trộn đã chọn"
 
-#: builtin/merge.c:207
+#: builtin/merge.c:224
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr ""
 "hòa trộn ghi chú của lần chuyển giao (dành cho hòa trộn non-fast-forward)"
 
-#: builtin/merge.c:211
+#: builtin/merge.c:228
 msgid "abort the current in-progress merge"
 msgstr "bãi bỏ quá trình hòa trộn hiện tại đang thực hiện"
 
-#: builtin/merge.c:240
+#: builtin/merge.c:257
 msgid "could not run stash."
 msgstr "không thể chạy stash."
 
-#: builtin/merge.c:245
+#: builtin/merge.c:262
 msgid "stash failed"
 msgstr "stash gặp lỗi"
 
-#: builtin/merge.c:250
+#: builtin/merge.c:267
 #, c-format
 msgid "not a valid object: %s"
 msgstr "không phải là một đối tượng hợp lệ: %s"
 
-#: builtin/merge.c:269 builtin/merge.c:286
+#: builtin/merge.c:286 builtin/merge.c:303
 msgid "read-tree failed"
 msgstr "read-tree gặp lỗi"
 
-#: builtin/merge.c:316
+#: builtin/merge.c:333
 msgid " (nothing to squash)"
 msgstr " (không có gì để squash)"
 
-#: builtin/merge.c:329
+#: builtin/merge.c:346
 #, c-format
 msgid "Squash commit -- not updating HEAD\n"
 msgstr "Squash commit -- không cập nhật HEAD\n"
 
-#: builtin/merge.c:361
+#: builtin/merge.c:378
 msgid "Writing SQUASH_MSG"
 msgstr "Đang ghi SQUASH_MSG"
 
-#: builtin/merge.c:363
+#: builtin/merge.c:380
 msgid "Finishing SQUASH_MSG"
 msgstr "Hoàn thành SQUASH_MSG"
 
-#: builtin/merge.c:386
+#: builtin/merge.c:403
 #, c-format
 msgid "No merge message -- not updating HEAD\n"
 msgstr "Không thông điệp hòa trộn -- không cập nhật HEAD\n"
 
-#: builtin/merge.c:436
+#: builtin/merge.c:453
 #, c-format
 msgid "'%s' does not point to a commit"
 msgstr "“%s” không chỉ đến một lần chuyển giao (commit) nào cả"
 
-#: builtin/merge.c:535
+#: builtin/merge.c:565
 #, c-format
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr "Chuỗi branch.%s.mergeoptions sai: %s"
 
-#: builtin/merge.c:628
+#: builtin/merge.c:657
 msgid "git write-tree failed to write a tree"
 msgstr "lệnh git write-tree gặp lỗi khi ghi một cây"
 
-#: builtin/merge.c:656
+#: builtin/merge.c:685
 msgid "Not handling anything other than two heads merge."
 msgstr "Không cầm nắm gì ngoài hai head hòa trộn"
 
-#: builtin/merge.c:670
+#: builtin/merge.c:699
 #, c-format
 msgid "Unknown option for merge-recursive: -X%s"
 msgstr "Không hiểu tùy chọn cho merge-recursive: -X%s"
 
-#: builtin/merge.c:684
+#: builtin/merge.c:713
 #, c-format
 msgid "unable to write %s"
 msgstr "không thể ghi %s"
 
-#: builtin/merge.c:773
+#: builtin/merge.c:802
 #, c-format
 msgid "Could not read from '%s'"
 msgstr "Không thể đọc từ “%s”"
 
-#: builtin/merge.c:782
+#: builtin/merge.c:811
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr ""
 "Vẫn chưa hòa trộn các lần chuyển giao (commit); sử dụng lệnh “git commit” để "
 "hoàn tất việc hòa trộn.\n"
 
-#: builtin/merge.c:788
+#: builtin/merge.c:817
 #, c-format
 msgid ""
 "Please enter a commit message to explain why this merge is necessary,\n"
@@ -6118,55 +6691,50 @@ msgstr ""
 "rỗng\n"
 "sẽ hủy bỏ lần chuyển giao (commit).\n"
 
-#: builtin/merge.c:812
+#: builtin/merge.c:841
 msgid "Empty commit message."
 msgstr "Chú thích của lần commit (chuyển giao) bị trống rỗng."
 
-#: builtin/merge.c:824
+#: builtin/merge.c:853
 #, c-format
 msgid "Wonderful.\n"
 msgstr "Thần kỳ.\n"
 
-#: builtin/merge.c:889
+#: builtin/merge.c:918
 #, c-format
 msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
 msgstr ""
 "Việc tự động hòa trộn gặp lỗi; hãy sửa các xung đột sau đó chuyển giao "
 "(commit) kết quả.\n"
 
-#: builtin/merge.c:905
+#: builtin/merge.c:934
 #, c-format
 msgid "'%s' is not a commit"
 msgstr "%s không phải là một lần commit (chuyển giao)"
 
-#: builtin/merge.c:946
+#: builtin/merge.c:975
 msgid "No current branch."
 msgstr "không phải nhánh hiện hành"
 
-#: builtin/merge.c:948
+#: builtin/merge.c:977
 msgid "No remote for the current branch."
 msgstr "Không có máy chủ cho nhánh hiện hành."
 
-#: builtin/merge.c:950
+#: builtin/merge.c:979
 msgid "No default upstream defined for the current branch."
 msgstr "Không có thượng nguồn mặc định được định nghĩa cho nhánh hiện hành."
 
-#: builtin/merge.c:955
+#: builtin/merge.c:984
 #, c-format
-msgid "No remote tracking branch for %s from %s"
+msgid "No remote-tracking branch for %s from %s"
 msgstr "Không nhánh mạng theo vết cho %s từ %s"
 
-#: builtin/merge.c:1042 builtin/merge.c:1199
-#, c-format
-msgid "%s - not something we can merge"
-msgstr "%s - không phải là một số thứ chúng tôi có thể hòa trộn"
-
-#: builtin/merge.c:1110
+#: builtin/merge.c:1140
 msgid "There is no merge to abort (MERGE_HEAD missing)."
 msgstr ""
 "Ở đây không có lần hòa trộn nào được hủy bỏ giữa chừng cả (thiếu MERGE_HEAD)."
 
-#: builtin/merge.c:1126 git-pull.sh:31
+#: builtin/merge.c:1156 git-pull.sh:31
 msgid ""
 "You have not concluded your merge (MERGE_HEAD exists).\n"
 "Please, commit your changes before you can merge."
@@ -6174,11 +6742,11 @@ msgstr ""
 "Bạn chưa kết thúc việc hòa trộng (MERGE_HEAD vẫn tồn tại).\n"
 "Hãy chuyển giao (commit) các thay đổi trước khi bạn có thể hòa trộn."
 
-#: builtin/merge.c:1129 git-pull.sh:34
+#: builtin/merge.c:1159 git-pull.sh:34
 msgid "You have not concluded your merge (MERGE_HEAD exists)."
 msgstr "Bạn chưa kết thúc việc hòa trộng (MERGE_HEAD vẫn tồn tại)."
 
-#: builtin/merge.c:1133
+#: builtin/merge.c:1163
 msgid ""
 "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 "Please, commit your changes before you can merge."
@@ -6186,85 +6754,108 @@ msgstr ""
 "Bạn chưa kết thúc việc cherry-pick (CHERRY_PICK_HEAD vẫn tồn tại).\n"
 "Hãy chuyển giao (commit) các thay đổi trước khi bạn có thể hòa trộn."
 
-#: builtin/merge.c:1136
+#: builtin/merge.c:1166
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
 msgstr "Bạn chưa kết thúc việc cherry-pick (CHERRY_PICK_HEAD vẫn tồn tại)."
 
-#: builtin/merge.c:1145
+#: builtin/merge.c:1175
 msgid "You cannot combine --squash with --no-ff."
 msgstr "Bạn không thể kết hợp --squash với --no-ff."
 
-#: builtin/merge.c:1150
-msgid "You cannot combine --no-ff with --ff-only."
-msgstr "Bạn không thể kết hợp --no-ff với --ff-only."
-
-#: builtin/merge.c:1157
+#: builtin/merge.c:1184
 msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr ""
 "Không chỉ ra lần chuyển giao (commit) và merge.defaultToUpstream chưa được "
 "đặt."
 
-#: builtin/merge.c:1189
+#: builtin/merge.c:1216
 msgid "Can merge only exactly one commit into empty head"
 msgstr ""
 "Không thể hòa trộn một cách đúng đắn một lần chuyển giao (commit) vào một "
 "head rỗng"
 
-#: builtin/merge.c:1192
+#: builtin/merge.c:1219
 msgid "Squash commit into empty head not supported yet"
 msgstr "Squash commit vào một head trống rỗng vẫn chưa được hỗ trợ"
 
-#: builtin/merge.c:1194
+#: builtin/merge.c:1221
 msgid "Non-fast-forward commit does not make sense into an empty head"
 msgstr ""
 "Chuyển giao (commit) không-fast-forward không hợp lý ở trong một head trống "
 "rỗng"
 
-#: builtin/merge.c:1310
+#: builtin/merge.c:1226
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - không phải là một số thứ chúng tôi có thể hòa trộn"
+
+#: builtin/merge.c:1277
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr ""
+"Lần chuyển giao %s có một chữ ký GPG không đáng tin, được cho là bởi %s."
+
+#: builtin/merge.c:1280
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr "Lần chuyển giao %s có một chữ ký GPG sai, được cho là bởi %s."
+
+#. 'N'
+#: builtin/merge.c:1283
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr "Lần chuyển giao (commit) %s không có chữ ký GPG."
+
+#: builtin/merge.c:1286
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr "Lần chuyển giao %s có một chữ ký GPG tốt bởi %s\n"
+
+#: builtin/merge.c:1370
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr "Đang cập nhật %s..%s\n"
 
-#: builtin/merge.c:1349
+#: builtin/merge.c:1409
 #, c-format
 msgid "Trying really trivial in-index merge...\n"
 msgstr "Đang thử hòa trộn kiểu “trivial in-index”...\n"
 
-#: builtin/merge.c:1356
+#: builtin/merge.c:1416
 #, c-format
 msgid "Nope.\n"
 msgstr "Không.\n"
 
-#: builtin/merge.c:1388
+#: builtin/merge.c:1448
 msgid "Not possible to fast-forward, aborting."
 msgstr "Thực hiện lệnh fast-forward là không thể được, đang bỏ qua."
 
-#: builtin/merge.c:1411 builtin/merge.c:1490
+#: builtin/merge.c:1471 builtin/merge.c:1550
 #, c-format
 msgid "Rewinding the tree to pristine...\n"
 msgstr "Đang tua lại cây thành thời xa xưa...\n"
 
-#: builtin/merge.c:1415
+#: builtin/merge.c:1475
 #, c-format
 msgid "Trying merge strategy %s...\n"
 msgstr "Đang thử chiến lược hòa trộn %s...\n"
 
-#: builtin/merge.c:1481
+#: builtin/merge.c:1541
 #, c-format
 msgid "No merge strategy handled the merge.\n"
 msgstr "Không có chiến lược hòa trộn nào được nắm giữ (handle) sự hòa trộn.\n"
 
-#: builtin/merge.c:1483
+#: builtin/merge.c:1543
 #, c-format
 msgid "Merge with strategy %s failed.\n"
 msgstr "Hòa trộn với chiến lược %s gặp lỗi.\n"
 
-#: builtin/merge.c:1492
+#: builtin/merge.c:1552
 #, c-format
 msgid "Using the %s to prepare resolving by hand.\n"
 msgstr "Sử dụng %s để chuẩn bị giải quyết bằng tay.\n"
 
-#: builtin/merge.c:1504
+#: builtin/merge.c:1564
 #, c-format
 msgid "Automatic merge went well; stopped before committing as requested\n"
 msgstr ""
@@ -6433,48 +7024,52 @@ msgstr "Đổi tên %s thành %s\n"
 msgid "renaming '%s' failed"
 msgstr "đổi tên %s gặp lỗi"
 
-#: builtin/name-rev.c:175
+#: builtin/name-rev.c:259
 msgid "git name-rev [options] <commit>..."
 msgstr "git name-rev [các-tùy-chọn] <commit>..."
 
-#: builtin/name-rev.c:176
+#: builtin/name-rev.c:260
 msgid "git name-rev [options] --all"
 msgstr "git name-rev [các-tùy-chọn] --all"
 
-#: builtin/name-rev.c:177
+#: builtin/name-rev.c:261
 msgid "git name-rev [options] --stdin"
 msgstr "git name-rev [các-tùy-chọn] --stdin"
 
-#: builtin/name-rev.c:229
+#: builtin/name-rev.c:313
 msgid "print only names (no SHA-1)"
 msgstr "chỉ hiển thị tên (không SHA-1)"
 
-#: builtin/name-rev.c:230
+#: builtin/name-rev.c:314
 msgid "only use tags to name the commits"
 msgstr "chỉ dùng các thẻ để đặt tên cho các lần chuyển giao"
 
-#: builtin/name-rev.c:232
+#: builtin/name-rev.c:316
 msgid "only use refs matching <pattern>"
 msgstr "chỉ sử dụng các tham chiếu (refs) khớp với <mẫu>"
 
-#: builtin/name-rev.c:234
+#: builtin/name-rev.c:318
 msgid "list all commits reachable from all refs"
 msgstr ""
 "liệt kê tất cả các lần chuyển giao có thể đọc được từ tất cả các tham chiếu"
 
-#: builtin/name-rev.c:235
+#: builtin/name-rev.c:319
 msgid "read from stdin"
 msgstr "đọc từ đầu vào tiêu chuẩn"
 
-#: builtin/name-rev.c:236
+#: builtin/name-rev.c:320
 msgid "allow to print `undefined` names"
 msgstr "cho phép hiển thị các tên “chưa định nghĩa“"
 
-#: builtin/notes.c:26
+#: builtin/name-rev.c:326
+msgid "dereference tags in the input (internal use)"
+msgstr "bãi bỏ tham chiếu các thẻ trong đầu vào (dùng nội bộ)"
+
+#: builtin/notes.c:24
 msgid "git notes [--ref <notes_ref>] [list [<object>]]"
 msgstr "git notes [--ref <notes_ref>] [list [<đối-tượng>]]"
 
-#: builtin/notes.c:27
+#: builtin/notes.c:25
 msgid ""
 "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
@@ -6482,11 +7077,11 @@ msgstr ""
 "git notes [--ref <notes_ref>] add [-f] [-m <lời-nhắn> | -F <tập-tin> | (-c | "
 "-C) <đối-tượng>] [<đối-tượng>]"
 
-#: builtin/notes.c:28
+#: builtin/notes.c:26
 msgid "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"
 msgstr "git notes [--ref <notes_ref>] copy [-f] <từ-đối-tượng> <đến-đối-tượng>"
 
-#: builtin/notes.c:29
+#: builtin/notes.c:27
 msgid ""
 "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
@@ -6494,223 +7089,200 @@ msgstr ""
 "git notes [--ref <notes_ref>] append [-m <lời-nhắn> | -F <tập-tin> | (-c | -"
 "C) <đối-tượng>] [<đối-tượng>]"
 
-#: builtin/notes.c:30
+#: builtin/notes.c:28
 msgid "git notes [--ref <notes_ref>] edit [<object>]"
 msgstr "git notes [--ref <notes_ref>] edit [<đối-tượng>]"
 
-#: builtin/notes.c:31
+#: builtin/notes.c:29
 msgid "git notes [--ref <notes_ref>] show [<object>]"
 msgstr "git notes [--ref <notes_ref>] show [<đối-tượng>]"
 
-#: builtin/notes.c:32
+#: builtin/notes.c:30
 msgid ""
 "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"
 msgstr ""
 "git notes [--ref <notes_ref>] merge [-v | -q] [-s <chiến-lược> ] <notes_ref>"
 
-#: builtin/notes.c:33
+#: builtin/notes.c:31
 msgid "git notes merge --commit [-v | -q]"
 msgstr "git notes merge --commit [-v | -q]"
 
-#: builtin/notes.c:34
+#: builtin/notes.c:32
 msgid "git notes merge --abort [-v | -q]"
 msgstr "git notes merge --abort [-v | -q]"
 
-#: builtin/notes.c:35
+#: builtin/notes.c:33
 msgid "git notes [--ref <notes_ref>] remove [<object>...]"
 msgstr "git notes [--ref <notes_ref>] remove [<đối-tượng>...]"
 
-#: builtin/notes.c:36
+#: builtin/notes.c:34
 msgid "git notes [--ref <notes_ref>] prune [-n | -v]"
 msgstr "git notes [--ref <notes_ref>] prune [-n | -v]"
 
-#: builtin/notes.c:37
+#: builtin/notes.c:35
 msgid "git notes [--ref <notes_ref>] get-ref"
 msgstr "git notes [--ref <notes_ref>] get-ref"
 
-#: builtin/notes.c:42
+#: builtin/notes.c:40
 msgid "git notes [list [<object>]]"
 msgstr "git notes [list [<đối tượng>]]"
 
-#: builtin/notes.c:47
+#: builtin/notes.c:45
 msgid "git notes add [<options>] [<object>]"
 msgstr "git notes add [<các-tùy-chọn>] [<đối-tượng>]"
 
-#: builtin/notes.c:52
+#: builtin/notes.c:50
 msgid "git notes copy [<options>] <from-object> <to-object>"
 msgstr "git notes copy [<các-tùy-chọn>] <từ-đối-tượng> <đến-đối-tượng>"
 
-#: builtin/notes.c:53
+#: builtin/notes.c:51
 msgid "git notes copy --stdin [<from-object> <to-object>]..."
 msgstr "git notes copy --stdin [<từ-đối-tượng> <đến-đối-tượng>]..."
 
-#: builtin/notes.c:58
+#: builtin/notes.c:56
 msgid "git notes append [<options>] [<object>]"
 msgstr "git notes append [<các-tùy-chọn>] [<đối-tượng>]"
 
-#: builtin/notes.c:63
+#: builtin/notes.c:61
 msgid "git notes edit [<object>]"
 msgstr "git notes edit [<đối tượng>]"
 
-#: builtin/notes.c:68
+#: builtin/notes.c:66
 msgid "git notes show [<object>]"
 msgstr "git notes show [<đối tượng>]"
 
-#: builtin/notes.c:73
+#: builtin/notes.c:71
 msgid "git notes merge [<options>] <notes_ref>"
 msgstr "git notes merge [<các-tùy-chọn>] <notes_ref>"
 
-#: builtin/notes.c:74
+#: builtin/notes.c:72
 msgid "git notes merge --commit [<options>]"
 msgstr "git notes merge --commit [<các-tùy-chọn>]"
 
-#: builtin/notes.c:75
+#: builtin/notes.c:73
 msgid "git notes merge --abort [<options>]"
 msgstr "git notes merge --abort [<các-tùy-chọn>]"
 
-#: builtin/notes.c:80
+#: builtin/notes.c:78
 msgid "git notes remove [<object>]"
 msgstr "git notes remove [<đối tượng>]"
 
-#: builtin/notes.c:85
+#: builtin/notes.c:83
 msgid "git notes prune [<options>]"
 msgstr "git notes prune [<các-tùy-chọn>]"
 
-#: builtin/notes.c:90
+#: builtin/notes.c:88
 msgid "git notes get-ref"
 msgstr "git notes get-ref"
 
-#: builtin/notes.c:139
+#: builtin/notes.c:137
 #, c-format
 msgid "unable to start 'show' for object '%s'"
 msgstr "không thể khởi chạy “show” cho đối tượng “%s”"
 
-#: builtin/notes.c:143
+#: builtin/notes.c:141
 msgid "could not read 'show' output"
 msgstr "không thể đọc kết xuất “show”"
 
-#: builtin/notes.c:151
+#: builtin/notes.c:149
 #, c-format
 msgid "failed to finish 'show' for object '%s'"
 msgstr "gặp lỗi khi hoàn thành “show” cho đối tượng “%s”"
 
-#: builtin/notes.c:169 builtin/tag.c:341
+#: builtin/notes.c:167 builtin/tag.c:341
 #, c-format
 msgid "could not create file '%s'"
 msgstr "không thể tạo tập tin “%s”"
 
-#: builtin/notes.c:188
+#: builtin/notes.c:186
 msgid "Please supply the note contents using either -m or -F option"
 msgstr ""
 "Xin hãy áp dụng nội dung của ghi chú sử dụng hoặc là tùy chọn -m hoặc là -F"
 
-#: builtin/notes.c:209 builtin/notes.c:972
+#: builtin/notes.c:207 builtin/notes.c:844
 #, c-format
 msgid "Removing note for object %s\n"
 msgstr "Đang gỡ bỏ ghi chú (note) cho đối tượng %s\n"
 
-#: builtin/notes.c:214
+#: builtin/notes.c:212
 msgid "unable to write note object"
 msgstr "không thể ghi đối tượng ghi chú (note)"
 
-#: builtin/notes.c:216
+#: builtin/notes.c:214
 #, c-format
 msgid "The note contents has been left in %s"
 msgstr "Nội dung ghi chú còn lại %s"
 
-#: builtin/notes.c:250 builtin/tag.c:540
+#: builtin/notes.c:248 builtin/tag.c:540
 #, c-format
 msgid "cannot read '%s'"
 msgstr "không thể đọc “%s”"
 
-#: builtin/notes.c:252 builtin/tag.c:543
+#: builtin/notes.c:250 builtin/tag.c:543
 #, c-format
 msgid "could not open or read '%s'"
 msgstr "không thể mở hay đọc “%s”"
 
-#: builtin/notes.c:271 builtin/notes.c:444 builtin/notes.c:446
-#: builtin/notes.c:506 builtin/notes.c:560 builtin/notes.c:643
-#: builtin/notes.c:648 builtin/notes.c:723 builtin/notes.c:765
-#: builtin/notes.c:967 builtin/tag.c:556
+#: builtin/notes.c:269 builtin/notes.c:316 builtin/notes.c:318
+#: builtin/notes.c:378 builtin/notes.c:432 builtin/notes.c:515
+#: builtin/notes.c:520 builtin/notes.c:595 builtin/notes.c:637
+#: builtin/notes.c:839 builtin/tag.c:556
 #, c-format
 msgid "Failed to resolve '%s' as a valid ref."
 msgstr "Gặp lỗi khi phân giải “%s” như là một tham chiếu (ref) hợp lệ."
 
-#: builtin/notes.c:274
+#: builtin/notes.c:272
 #, c-format
 msgid "Failed to read object '%s'."
 msgstr "Gặp lỗi khi đọc đối tượng “%s”."
 
-#: builtin/notes.c:298
-msgid "Cannot commit uninitialized/unreferenced notes tree"
-msgstr ""
-"Không thể chuyển giao (commit) chưa được khởi tạo hoặc không được tham chiếu "
-"cây ghi chú"
-
-#: builtin/notes.c:339
-#, c-format
-msgid "Bad notes.rewriteMode value: '%s'"
-msgstr "Giá trị notes.rewriteMode sai: “%s”"
-
-#: builtin/notes.c:349
-#, c-format
-msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
-msgstr "Từ chối ghi đè ghi chú trong %s (nằm ngoài của refs/notes/)"
-
-#. TRANSLATORS: The first %s is the name of the
-#. environment variable, the second %s is its value
-#: builtin/notes.c:376
-#, c-format
-msgid "Bad %s value: '%s'"
-msgstr "Giá trị %s sai: “%s”"
-
-#: builtin/notes.c:440
+#: builtin/notes.c:312
 #, c-format
 msgid "Malformed input line: '%s'."
 msgstr "Dòng nhập vào dị hình: “%s”."
 
-#: builtin/notes.c:455
+#: builtin/notes.c:327
 #, c-format
 msgid "Failed to copy notes from '%s' to '%s'"
 msgstr "Gặp lỗi khi sao chép ghi chú (note) từ “%s” tới “%s”"
 
-#: builtin/notes.c:499 builtin/notes.c:553 builtin/notes.c:626
-#: builtin/notes.c:638 builtin/notes.c:711 builtin/notes.c:758
-#: builtin/notes.c:1032
+#: builtin/notes.c:371 builtin/notes.c:425 builtin/notes.c:498
+#: builtin/notes.c:510 builtin/notes.c:583 builtin/notes.c:630
+#: builtin/notes.c:904
 msgid "too many parameters"
 msgstr "quá nhiều đối số"
 
-#: builtin/notes.c:512 builtin/notes.c:771
+#: builtin/notes.c:384 builtin/notes.c:643
 #, c-format
 msgid "No note found for object %s."
 msgstr "không tìm thấy ghi chú cho đối tượng %s."
 
-#: builtin/notes.c:534 builtin/notes.c:691
+#: builtin/notes.c:406 builtin/notes.c:563
 msgid "note contents as a string"
 msgstr "nội dung ghi chú (note) nằm trong một chuỗi"
 
-#: builtin/notes.c:537 builtin/notes.c:694
+#: builtin/notes.c:409 builtin/notes.c:566
 msgid "note contents in a file"
 msgstr "nội dung ghi chú (note) nằm trong một tập tin"
 
-#: builtin/notes.c:539 builtin/notes.c:542 builtin/notes.c:696
-#: builtin/notes.c:699 builtin/tag.c:474
+#: builtin/notes.c:411 builtin/notes.c:414 builtin/notes.c:568
+#: builtin/notes.c:571 builtin/tag.c:474
 msgid "object"
 msgstr "đối tượng"
 
-#: builtin/notes.c:540 builtin/notes.c:697
+#: builtin/notes.c:412 builtin/notes.c:569
 msgid "reuse and edit specified note object"
 msgstr "dùng lại nhưng có sửa chữa đối tượng note đã chỉ ra"
 
-#: builtin/notes.c:543 builtin/notes.c:700
+#: builtin/notes.c:415 builtin/notes.c:572
 msgid "reuse specified note object"
 msgstr "dùng lại đối tượng ghi chú (note) đã chỉ ra"
 
-#: builtin/notes.c:545 builtin/notes.c:613
+#: builtin/notes.c:417 builtin/notes.c:485
 msgid "replace existing notes"
 msgstr "thay thế ghi chú trước"
 
-#: builtin/notes.c:579
+#: builtin/notes.c:451
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -6719,24 +7291,24 @@ msgstr ""
 "Không thể thêm các ghi chú. Đã tìm thấy các ghi chú đã có sẵn cho đối tượng "
 "%s. Sử dụng tùy chọn “-f” để ghi đè lên các ghi chú cũ"
 
-#: builtin/notes.c:584 builtin/notes.c:661
+#: builtin/notes.c:456 builtin/notes.c:533
 #, c-format
 msgid "Overwriting existing notes for object %s\n"
 msgstr "Đang ghi đè lên ghi chú cũ cho đối tượng %s\n"
 
-#: builtin/notes.c:614
+#: builtin/notes.c:486
 msgid "read objects from stdin"
 msgstr "đọc các đối tượng từ đầu vào tiêu chuẩn"
 
-#: builtin/notes.c:616
+#: builtin/notes.c:488
 msgid "load rewriting config for <command> (implies --stdin)"
 msgstr "tải cấu hình chép lại cho <lệnh> (ngầm định là --stdin)"
 
-#: builtin/notes.c:634
+#: builtin/notes.c:506
 msgid "too few parameters"
 msgstr "quá ít đối số"
 
-#: builtin/notes.c:655
+#: builtin/notes.c:527
 #, c-format
 msgid ""
 "Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
@@ -6745,12 +7317,12 @@ msgstr ""
 "Không thể sao chép các ghi chú. Đã tìm thấy các ghi chú đã có sẵn cho đối "
 "tượng %s. Sử dụng tùy chọn “-f” để ghi đè lên các ghi chú cũ"
 
-#: builtin/notes.c:667
+#: builtin/notes.c:539
 #, c-format
 msgid "Missing notes on source object %s. Cannot copy."
 msgstr "Thiếu ghi chú trên đối tượng nguốn %s. Không thể sao chép."
 
-#: builtin/notes.c:716
+#: builtin/notes.c:588
 #, c-format
 msgid ""
 "The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
@@ -6759,15 +7331,15 @@ msgstr ""
 "Các tùy chọn -m/-F/-c/-C đã cổ không còn dùng nữa cho lệnh con “edit”.\n"
 "Xin hãy sử dụng lệnh sau để thay thế: “git notes add -f -m/-F/-c/-C”.\n"
 
-#: builtin/notes.c:863
+#: builtin/notes.c:735
 msgid "General options"
 msgstr "Tùy chọn chung"
 
-#: builtin/notes.c:865
+#: builtin/notes.c:737
 msgid "Merge options"
 msgstr "Tùy chọn về hòa trộn"
 
-#: builtin/notes.c:867
+#: builtin/notes.c:739
 msgid ""
 "resolve notes conflicts using the given strategy (manual/ours/theirs/union/"
 "cat_sort_uniq)"
@@ -6775,46 +7347,46 @@ msgstr ""
 "phân giải các xung đột “notes” sử dụng chiến lược đã đưa ra (manual/ours/"
 "theirs/union/cat_sort_uniq)"
 
-#: builtin/notes.c:869
+#: builtin/notes.c:741
 msgid "Committing unmerged notes"
 msgstr "Chuyển giao các note chưa được hòa trộn"
 
-#: builtin/notes.c:871
+#: builtin/notes.c:743
 msgid "finalize notes merge by committing unmerged notes"
 msgstr ""
 "các note cuối cùng được hòa trộn bởi các note chưa hòa trộn của lần chuyển "
 "giao"
 
-#: builtin/notes.c:873
+#: builtin/notes.c:745
 msgid "Aborting notes merge resolution"
 msgstr "Hủy bỏ phân giải ghi chú (note) hòa trộn"
 
-#: builtin/notes.c:875
+#: builtin/notes.c:747
 msgid "abort notes merge"
 msgstr "bỏ qua hòa trộn các ghi chú (note)"
 
-#: builtin/notes.c:970
+#: builtin/notes.c:842
 #, c-format
 msgid "Object %s has no note\n"
 msgstr "Đối tượng %s không có ghi chú (note)\n"
 
-#: builtin/notes.c:982
+#: builtin/notes.c:854
 msgid "attempt to remove non-existent note is not an error"
 msgstr "cố gắng gỡ bỏ một note chưa từng tồn tại không phải là một lỗi"
 
-#: builtin/notes.c:985
+#: builtin/notes.c:857
 msgid "read object names from the standard input"
 msgstr "đọc tên đối tượng từ thiết bị nhập chuẩn"
 
-#: builtin/notes.c:1066
+#: builtin/notes.c:938
 msgid "notes_ref"
 msgstr "notes_ref"
 
-#: builtin/notes.c:1067
+#: builtin/notes.c:939
 msgid "use notes from <notes_ref>"
 msgstr "dùng “notes” từ <notes_ref>"
 
-#: builtin/notes.c:1102 builtin/remote.c:1598
+#: builtin/notes.c:974 builtin/remote.c:1598
 #, c-format
 msgid "Unknown subcommand: %s"
 msgstr "Không hiểu câu lệnh con: %s"
@@ -7055,7 +7627,7 @@ msgstr ""
 "    git push %s %s\n"
 "%s"
 
-#: builtin/push.c:121
+#: builtin/push.c:117
 #, c-format
 msgid ""
 "You are not currently on a branch.\n"
@@ -7070,7 +7642,7 @@ msgstr ""
 "\n"
 "    git push %s HEAD:<tên-của-nhánh-máy-chủ>\n"
 
-#: builtin/push.c:128
+#: builtin/push.c:131
 #, c-format
 msgid ""
 "The current branch %s has no upstream branch.\n"
@@ -7084,12 +7656,12 @@ msgstr ""
 "\n"
 "    git push --set-upstream %s %s\n"
 
-#: builtin/push.c:136
+#: builtin/push.c:139
 #, c-format
 msgid "The current branch %s has multiple upstream branches, refusing to push."
 msgstr "Nhánh hiện tại %s có đa nhánh thượng nguồn (upstream), từ chối push."
 
-#: builtin/push.c:139
+#: builtin/push.c:142
 #, c-format
 msgid ""
 "You are pushing to remote '%s', which is not the upstream of\n"
@@ -7101,7 +7673,7 @@ msgstr ""
 "nhánh hiện tại “%s” của bạn, mà không báo cho tôi biết là cái gì được push\n"
 "để cập nhật nhánh máy chủ nào."
 
-#: builtin/push.c:151
+#: builtin/push.c:165
 msgid ""
 "push.default is unset; its implicit value is changing in\n"
 "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -7134,28 +7706,28 @@ msgstr ""
 "tự\n"
 "“current” thay vì “simple” nếu bạn thỉnh thoảng phải sử dụng bản Git cũ)"
 
-#: builtin/push.c:199
+#: builtin/push.c:225
 msgid ""
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
 "Bạn đã không chỉ ra một refspecs nào để push, và push.default là \"không là "
 "gì cả\"."
 
-#: builtin/push.c:206
+#: builtin/push.c:232
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
-"before pushing again.\n"
+"its remote counterpart. Integrate the remote changes (e.g.\n"
+"'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Việc cập nhật bị từ chối bởi vì đầu mút của nhánh được push nằm đằng sau bộ\n"
 "phận tương ứng của máy chủ. Hòa trộn với các thay đổi từ máy chủ (v.d. “git "
-"pull”)\n"
+"pull...”)\n"
 "trước khi lại push lần nữa.\n"
-"Xem trong phần “Note about fast-forwards” từ lệnh “git push --help” để có "
-"thông tin chi tiết."
+"Xem “Note about fast-forwards” từ lệnh “git push --help” để có thông tin chi "
+"tiết."
 
-#: builtin/push.c:212
+#: builtin/push.c:238
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
 "counterpart. If you did not intend to push that branch, you may want to\n"
@@ -7170,41 +7742,40 @@ msgstr ""
 "thành “simple”, “current” hoặc “upstream” để chỉ push nhánh hiện hành mà "
 "thôi."
 
-#: builtin/push.c:218
+#: builtin/push.c:244
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and merge the remote changes\n"
-"(e.g. 'git pull') before pushing again.\n"
+"counterpart. Check out this branch and integrate the remote changes\n"
+"(e.g. 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Việc cập nhật bị từ chối bởi vì đầu mút của nhánh được push nằm đằng sau bộ\n"
 "phận tương ứng của máy chủ. Checkou nhánh này và hòa trộn với các thay đổi "
 "từ máy chủ\n"
-"(v.d. “git pull”) trước khi lại push lần nữa.\n"
-"Xem trong phần “Note about fast-forwards” từ lệnh “git push --help” để có "
-"thông tin chi tiết."
+"(v.d. “git pull ...”) trước khi lại push lần nữa.\n"
+"Xem “Note about fast-forwards” từ lệnh “git push --help” để có thông tin chi "
+"tiết."
 
-#: builtin/push.c:224
+#: builtin/push.c:250
 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"
-"to the same ref. You may want to first merge the remote changes (e.g.,\n"
-"'git pull') before pushing again.\n"
+"to the same ref. You may want to first integrate the remote changes\n"
+"(e.g., 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "Việc cập nhật bị từ chối bởi vì máy chủ có chứa công việc mà bạn không\n"
-"có ở máy nội bộ của mình. Lỗi này thường có nguyên nhân bởi kho khác đẩy dữ "
-"liệu lên\n"
-"cùng một tham chiếu. Bạn có lẽ muốn hòa trộn với các thay đổi từ máy chủ\n"
-"(v.d. “git pull”) trước khi lại push lần nữa.\n"
-"Xem trong phần “Note about fast-forwards” từ lệnh “git push --help” để có "
-"thông tin chi tiết."
-
-#: builtin/push.c:231
+"có ở máy nội bộ của mình. Lỗi này thường có nguyên nhân bởi kho khác đẩy\n"
+"dữ liệu lên cùng một tham chiếu. Bạn có lẽ muốn hòa trộn với các thay đổi\n"
+"từ máy chủ(v.d. “git pull...”) trước khi lại push lần nữa.\n"
+"Xem “Note about fast-forwards” từ lệnh “git push --help” để có thông tin chi "
+"tiết."
+
+#: builtin/push.c:257
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr "Việc cập nhật bị từ chối bởi vì thẻ đã sẵn có từ trước trên máy chủ."
 
-#: builtin/push.c:234
+#: builtin/push.c:260
 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"
@@ -7216,22 +7787,22 @@ msgstr ""
 "tượng\n"
 "không phải chuyển giao, mà không sử dụng tùy chọn “--force”.\n"
 
-#: builtin/push.c:294
+#: builtin/push.c:320
 #, c-format
 msgid "Pushing to %s\n"
 msgstr "Đang push (đẩy) lên %s\n"
 
-#: builtin/push.c:298
+#: builtin/push.c:324
 #, c-format
 msgid "failed to push some refs to '%s'"
 msgstr "gặp lỗi khi push (đẩy lên) một số tham chiếu (ref) đến “%s”"
 
-#: builtin/push.c:331
+#: builtin/push.c:357
 #, c-format
 msgid "bad repository '%s'"
 msgstr "repository (kho) sai “%s”"
 
-#: builtin/push.c:332
+#: builtin/push.c:358
 msgid ""
 "No configured push destination.\n"
 "Either specify the URL from the command-line or configure a remote "
@@ -7252,84 +7823,88 @@ msgstr ""
 "\n"
 "    git push <tên>\n"
 
-#: builtin/push.c:347
+#: builtin/push.c:373
 msgid "--all and --tags are incompatible"
 msgstr "--all và --tags xung khắc nhau"
 
-#: builtin/push.c:348
+#: builtin/push.c:374
 msgid "--all can't be combined with refspecs"
 msgstr "--all không thể được tổ hợp cùng với refspecs"
 
-#: builtin/push.c:353
+#: builtin/push.c:379
 msgid "--mirror and --tags are incompatible"
 msgstr "--mirror và --tags xung khắc nhau"
 
-#: builtin/push.c:354
+#: builtin/push.c:380
 msgid "--mirror can't be combined with refspecs"
 msgstr "--mirror không thể được tổ hợp cùng với refspecs"
 
-#: builtin/push.c:359
+#: builtin/push.c:385
 msgid "--all and --mirror are incompatible"
 msgstr "--all và --mirror xung khắc nhau"
 
-#: builtin/push.c:419
+#: builtin/push.c:445
 msgid "repository"
 msgstr "kho"
 
-#: builtin/push.c:420
+#: builtin/push.c:446
 msgid "push all refs"
 msgstr "push tất cả refs"
 
-#: builtin/push.c:421
+#: builtin/push.c:447
 msgid "mirror all refs"
 msgstr "mirror tất cả refs"
 
-#: builtin/push.c:423
+#: builtin/push.c:449
 msgid "delete refs"
 msgstr "xóa refs"
 
-#: builtin/push.c:424
+#: builtin/push.c:450
 msgid "push tags (can't be used with --all or --mirror)"
 msgstr ""
 "các thẻ push (không thể sử dụng cùng với các tùy chọn --all hay --mirror)"
 
-#: builtin/push.c:427
+#: builtin/push.c:453
 msgid "force updates"
 msgstr "ép buộc cập nhật"
 
-#: builtin/push.c:428
+#: builtin/push.c:454
 msgid "check"
 msgstr "kiểm tra"
 
-#: builtin/push.c:429
+#: builtin/push.c:455
 msgid "control recursive pushing of submodules"
 msgstr "điều khiển việc đẩy lên (push) đệ qui của mô-đun-con"
 
-#: builtin/push.c:431
+#: builtin/push.c:457
 msgid "use thin pack"
 msgstr "tạo gói nhẹ"
 
-#: builtin/push.c:432 builtin/push.c:433
+#: builtin/push.c:458 builtin/push.c:459
 msgid "receive pack program"
 msgstr "nhận về chương trình pack"
 
-#: builtin/push.c:434
+#: builtin/push.c:460
 msgid "set upstream for git pull/status"
 msgstr "đặt thượng nguồn (upstream) cho git pull/status"
 
-#: builtin/push.c:437
+#: builtin/push.c:463
 msgid "prune locally removed refs"
 msgstr "prune (cắt cụt) những tham chiếu (refs) bị gỡ bỏ"
 
-#: builtin/push.c:439
+#: builtin/push.c:465
 msgid "bypass pre-push hook"
 msgstr "vòng qua “pre-push hook”"
 
-#: builtin/push.c:448
+#: builtin/push.c:466
+msgid "push missing but relevant tags"
+msgstr "push bị trượt nhưng các thẻ thích hợp"
+
+#: builtin/push.c:476
 msgid "--delete is incompatible with --all, --mirror and --tags"
 msgstr "--delete là xung khắc với các tùy chọn --all, --mirror và --tags"
 
-#: builtin/push.c:450
+#: builtin/push.c:478
 msgid "--delete doesn't make sense without any refs"
 msgstr "--delete không hợp lý nếu không có bất kỳ tham chiếu (refs) nào"
 
@@ -7344,71 +7919,81 @@ msgstr ""
 "[--index-output=<tập-tin>] (--empty | <tree-ish1> [<tree-ish2> [<tree-"
 "ish3>]])"
 
-#: builtin/read-tree.c:108
+#: builtin/read-tree.c:109
 msgid "write resulting index to <file>"
 msgstr "ghi mục lục kết quả vào <tập-tin>"
 
-#: builtin/read-tree.c:111
+#: builtin/read-tree.c:112
 msgid "only empty the index"
 msgstr "chỉ với bảng mục lục trống rỗng"
 
-#: builtin/read-tree.c:113
+#: builtin/read-tree.c:114
 msgid "Merging"
 msgstr "Hòa trộn"
 
-#: builtin/read-tree.c:115
+#: builtin/read-tree.c:116
 msgid "perform a merge in addition to a read"
 msgstr "thực hiện một hòa trộn thêm vào việc đọc"
 
-#: builtin/read-tree.c:117
+#: builtin/read-tree.c:118
 msgid "3-way merge if no file level merging required"
 msgstr ""
 "hòa trộn kiểu “3-way” nếu không có tập tin mức hòa trộn nào được yêu cầu "
 
-#: builtin/read-tree.c:119
+#: builtin/read-tree.c:120
 msgid "3-way merge in presence of adds and removes"
 msgstr "hòa trộn 3-way trong sự hiện diện của “adds” và “removes”"
 
-#: builtin/read-tree.c:121
+#: builtin/read-tree.c:122
 msgid "same as -m, but discard unmerged entries"
 msgstr "giống với -m, nhưng bỏ qua các mục chưa được hòa trộn"
 
-#: builtin/read-tree.c:122
+#: builtin/read-tree.c:123
 msgid "<subdirectory>/"
 msgstr "<thư-mục-con>/"
 
-#: builtin/read-tree.c:123
+#: builtin/read-tree.c:124
 msgid "read the tree into the index under <subdirectory>/"
 msgstr "đọc cây vào trong bảng mục lục dưới <thư_mục_con>/"
 
-#: builtin/read-tree.c:126
+#: builtin/read-tree.c:127
 msgid "update working tree with merge result"
 msgstr "cập nhật cây làm việc với kết quả hòa trộn"
 
-#: builtin/read-tree.c:128
+#: builtin/read-tree.c:129
 msgid "gitignore"
 msgstr "gitignore"
 
-#: builtin/read-tree.c:129
+#: builtin/read-tree.c:130
 msgid "allow explicitly ignored files to be overwritten"
 msgstr "cho phép các tập tin rõ ràng bị lờ đi được ghi đè"
 
-#: builtin/read-tree.c:132
+#: builtin/read-tree.c:133
 msgid "don't check the working tree after merging"
 msgstr "không kiểm tra cây làm việc sau hòa trộn"
 
-#: builtin/read-tree.c:133
+#: builtin/read-tree.c:134
 msgid "don't update the index or the work tree"
 msgstr "không cập nhật bảng mục lục hay cây làm việc"
 
-#: builtin/read-tree.c:135
+#: builtin/read-tree.c:136
 msgid "skip applying sparse checkout filter"
 msgstr "bỏ qua áp dụng bộ lọc lấy ra (checkout) thưa thớt"
 
-#: builtin/read-tree.c:137
+#: builtin/read-tree.c:138
 msgid "debug unpack-trees"
 msgstr "gỡ lỗi unpack-trees"
 
+#: builtin/reflog.c:500
+#, c-format
+msgid "%s' for '%s' is not a valid timestamp"
+msgstr "%s' cho '%s' không phải là dấu thời gian hợp lệ"
+
+#: builtin/reflog.c:616 builtin/reflog.c:621
+#, c-format
+msgid "'%s' is not a valid timestamp"
+msgstr "“%s” không phải là dấu thời gian hợp lệ"
+
 #: builtin/remote.c:11
 msgid "git remote [-v | --verbose]"
 msgstr "git remote [-v | --verbose]"
@@ -7852,7 +8437,7 @@ msgstr " * [nên xén bớt] %s"
 #: builtin/remote.c:1298
 #, c-format
 msgid " * [pruned] %s"
-msgstr " *[đã xén bớ] %s"
+msgstr " *[đã bị xén] %s"
 
 #: builtin/remote.c:1321
 msgid "prune remotes after fetching"
@@ -7984,97 +8569,97 @@ msgstr "Gặp lỗi khi tìm cây của HEAD."
 msgid "Failed to find tree of %s."
 msgstr "Gặp lỗi khi tìm cây của %s."
 
-#: builtin/reset.c:98
+#: builtin/reset.c:99
 #, c-format
 msgid "HEAD is now at %s"
 msgstr "HEAD hiện giờ tại %s"
 
-#: builtin/reset.c:169
+#: builtin/reset.c:172
 #, c-format
 msgid "Cannot do a %s reset in the middle of a merge."
 msgstr "Không thể thực hiện một %s reset ở giữa của quá trình hòa trộn."
 
-#: builtin/reset.c:248
+#: builtin/reset.c:251
 msgid "be quiet, only report errors"
 msgstr "làm việc ở chế độ im lặng, chỉ hiển thị khi có lỗi"
 
-#: builtin/reset.c:250
+#: builtin/reset.c:253
 msgid "reset HEAD and index"
 msgstr "đặt lại (reset) HEAD và bảng mục lục"
 
-#: builtin/reset.c:251
+#: builtin/reset.c:254
 msgid "reset only HEAD"
 msgstr "chỉ đặt lại (reset) HEAD"
 
-#: builtin/reset.c:253 builtin/reset.c:255
+#: builtin/reset.c:256 builtin/reset.c:258
 msgid "reset HEAD, index and working tree"
 msgstr "đặt lại HEAD, bảng mục lục và cây làm việc"
 
-#: builtin/reset.c:257
+#: builtin/reset.c:260
 msgid "reset HEAD but keep local changes"
 msgstr "đặt lại HEAD nhưng giữ lại các thay đổi nội bộ"
 
-#: builtin/reset.c:275
+#: builtin/reset.c:278
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
 msgstr "Gặp lỗi khi phân giải “%s” như là điểm xét duyệt hợp lệ."
 
-#: builtin/reset.c:278 builtin/reset.c:286
+#: builtin/reset.c:281 builtin/reset.c:289
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "không thể phân tích đối tượng “%s”."
 
-#: builtin/reset.c:283
+#: builtin/reset.c:286
 #, c-format
 msgid "Failed to resolve '%s' as a valid tree."
 msgstr "Gặp lỗi khi phân giải “%s” như là một cây (tree) hợp lệ."
 
-#: builtin/reset.c:292
+#: builtin/reset.c:295
 msgid "--patch is incompatible with --{hard,mixed,soft}"
 msgstr "--patch xung khắc với --{hard,mixed,soft}"
 
-#: builtin/reset.c:301
+#: builtin/reset.c:304
 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
 msgstr ""
 "--mixed với các đường dẫn không còn dùng nữa; hãy thay thế bằng lệnh “git "
 "reset -- <đường_dẫn>”."
 
-#: builtin/reset.c:303
+#: builtin/reset.c:306
 #, c-format
 msgid "Cannot do %s reset with paths."
 msgstr "Không thể thực hiện lệnh %s reset với các đường dẫn."
 
-#: builtin/reset.c:313
+#: builtin/reset.c:316
 #, c-format
 msgid "%s reset is not allowed in a bare repository"
 msgstr "%s reset không được phép trên kho bare (trên máy chủ)"
 
-#: builtin/reset.c:333
+#: builtin/reset.c:336
 #, c-format
 msgid "Could not reset index file to revision '%s'."
-msgstr "Không thể đặt lại (reset) bảng mục lục thành điểm xét lại “%s”."
+msgstr "Không thể đặt lại (reset) bảng mục lục thành điểm xét duyệt “%s”."
 
-#: builtin/reset.c:339
+#: builtin/reset.c:342
 msgid "Unstaged changes after reset:"
 msgstr "Những thay đổi bị bỏ trạng thái (stage) sau khi reset:"
 
-#: builtin/reset.c:344
+#: builtin/reset.c:347
 msgid "Could not write new index file."
 msgstr "Không thể ghi tập tin lưu bảng mục lục mới."
 
-#: builtin/rev-parse.c:339
+#: builtin/rev-parse.c:345
 msgid "git rev-parse --parseopt [options] -- [<args>...]"
 msgstr "git rev-parse --parseopt [các-tùy-chọn] -- [<th.số>...]"
 
-#: builtin/rev-parse.c:344
+#: builtin/rev-parse.c:350
 msgid "keep the `--` passed as an arg"
 msgstr "giữ “--“ chuyển qua làm tham số"
 
-#: builtin/rev-parse.c:346
+#: builtin/rev-parse.c:352
 msgid "stop parsing after the first non-option argument"
 msgstr "dừng phân tích sau đối số đầu tiên không có tùy chọn"
 
-#: builtin/rev-parse.c:464
+#: builtin/rev-parse.c:470
 msgid ""
 "git rev-parse --parseopt [options] -- [<args>...]\n"
 "   or: git rev-parse --sq-quote [<arg>...]\n"
@@ -8089,191 +8674,219 @@ msgstr ""
 "Chạy lệnh \"git rev-parse --parseopt -h\" để có thêm thông tin về cách dùng."
 
 #: builtin/revert.c:22
-msgid "git revert [options] <commit-ish>"
-msgstr "git revert [các-tùy-chọn] <commit-ish>"
+msgid "git revert [options] <commit-ish>..."
+msgstr "git revert [các-tùy-chọn] <commit-ish>..."
 
 #: builtin/revert.c:23
 msgid "git revert <subcommand>"
 msgstr "git revert <lệnh-con>"
 
 #: builtin/revert.c:28
-msgid "git cherry-pick [options] <commit-ish>"
-msgstr "git cherry-pick [các-tùy-chọn] <commit-ish>"
+msgid "git cherry-pick [options] <commit-ish>..."
+msgstr "git cherry-pick [các-tùy-chọn] <commit-ish>..."
 
 #: builtin/revert.c:29
 msgid "git cherry-pick <subcommand>"
 msgstr "git cherry-pick <lệnh-con>"
 
-#: builtin/revert.c:70 builtin/revert.c:92
+#: builtin/revert.c:71 builtin/revert.c:94
 #, c-format
 msgid "%s: %s cannot be used with %s"
 msgstr "%s: %s không thể được sử dụng với %s"
 
-#: builtin/revert.c:103
+#: builtin/revert.c:105
 msgid "end revert or cherry-pick sequence"
 msgstr "kết thúc cherry-pick hay revert liên tiếp nhau"
 
-#: builtin/revert.c:104
+#: builtin/revert.c:106
 msgid "resume revert or cherry-pick sequence"
 msgstr "phục hồi lại cherry-pick hay revert liên tiếp nhau"
 
-#: builtin/revert.c:105
+#: builtin/revert.c:107
 msgid "cancel revert or cherry-pick sequence"
 msgstr "không cherry-pick hay revert liên tiếp nhau"
 
-#: builtin/revert.c:106
+#: builtin/revert.c:108
 msgid "don't automatically commit"
 msgstr "không commit một cách tự động."
 
-#: builtin/revert.c:107
+#: builtin/revert.c:109
 msgid "edit the commit message"
 msgstr "sửa lại chú thích cho commit"
 
-#: builtin/revert.c:110
+#: builtin/revert.c:112
 msgid "parent number"
 msgstr "số của cha mẹ"
 
-#: builtin/revert.c:112
+#: builtin/revert.c:114
 msgid "merge strategy"
 msgstr "chiến lược hòa trộn"
 
-#: builtin/revert.c:113
+#: builtin/revert.c:115
 msgid "option"
 msgstr "tùy chọn"
 
-#: builtin/revert.c:114
+#: builtin/revert.c:116
 msgid "option for merge strategy"
 msgstr "tùy chọn cho chiến lược hòa trộn"
 
-#: builtin/revert.c:125
+#: builtin/revert.c:127
 msgid "append commit name"
 msgstr "nối thêm tên lần chuyển giao (commit)"
 
-#: builtin/revert.c:126
+#: builtin/revert.c:128
 msgid "allow fast-forward"
 msgstr "cho phép fast-forward"
 
-#: builtin/revert.c:127
+#: builtin/revert.c:129
 msgid "preserve initially empty commits"
 msgstr "cấm khởi tạo lần chuyển giao trống rỗng"
 
-#: builtin/revert.c:128
+#: builtin/revert.c:130
 msgid "allow commits with empty messages"
 msgstr "chấp nhận chuyển giao (commit) mà không ghi chú gì"
 
-#: builtin/revert.c:129
+#: builtin/revert.c:131
 msgid "keep redundant, empty commits"
 msgstr "giữ lại các lần chuyển giao dư thừa, rỗng"
 
-#: builtin/revert.c:133
+#: builtin/revert.c:135
 msgid "program error"
 msgstr "lỗi chương trình"
 
-#: builtin/revert.c:223
+#: builtin/revert.c:225
 msgid "revert failed"
 msgstr "revert gặp lỗi"
 
-#: builtin/revert.c:238
+#: builtin/revert.c:240
 msgid "cherry-pick failed"
 msgstr "cherry-pick gặp lỗi"
 
-#: builtin/rm.c:15
+#: builtin/rm.c:16
 msgid "git rm [options] [--] <file>..."
 msgstr "git rm [các-tùy-chọn] [--] <tập-tin>..."
 
-#: builtin/rm.c:64 builtin/rm.c:186
-#, c-format
+#: builtin/rm.c:64
+msgid ""
+"the following submodule (or one of its nested submodules)\n"
+"uses a .git directory:"
+msgid_plural ""
+"the following submodules (or one of its nested submodules)\n"
+"use a .git directory:"
+msgstr[0] ""
+"mô-đun-con sau đây (hay một trong số mô-đun-con bên trong của nó)\n"
+"dùng một thư mục .git:"
+msgstr[1] ""
+"các mô-đun-con sau đây (hay một trong số mô-đun-con bên trong của nó)\n"
+"dùng một thư mục .git:"
+
+#: builtin/rm.c:70
 msgid ""
-"submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+"\n"
 "(use 'rm -rf' if you really want to remove it including all of its history)"
 msgstr ""
-"mô-đun-con “%s” (hoặc cái nằm trong các mô-đun-con) dùng thư mục .git\n"
-"(dùng “rm -rf” nếu bạn thực sự muốn gỡ bỏ nó cùng với tất cả lịch sử của "
+"\n"
+"(dùng /\"rm -rf/\" nếu bạn thực sự muốn gỡ bỏ nó cùng với tất cả lịch sử của "
 "chúng)"
 
-#: builtin/rm.c:174
-#, c-format
+#: builtin/rm.c:230
 msgid ""
-"'%s' has staged content different from both the file and the HEAD\n"
-"(use -f to force removal)"
-msgstr ""
-"“%s” có nội dung được lưu trạng thái khác biệt từ cả tập tin và cả HEAD\n"
-"(dùng tùy chọn -f để ép buộc gỡ bỏ)"
+"the following file has staged content different from both the\n"
+"file and the HEAD:"
+msgid_plural ""
+"the following files have staged content different from both the\n"
+"file and the HEAD:"
+msgstr[0] ""
+"tập tin sau đây có trạng thái khác biệt nội dung từ cả tập tin\n"
+"và cả HEAD:"
+msgstr[1] ""
+"các tập tin sau đây có trạng thái khác biệt nội dung từ cả tập tin\n"
+"và cả HEAD:"
 
-#: builtin/rm.c:180
-#, c-format
+#: builtin/rm.c:235
 msgid ""
-"'%s' has changes staged in the index\n"
-"(use --cached to keep the file, or -f to force removal)"
+"\n"
+"(use -f to force removal)"
 msgstr ""
-"“%s” có các thay đổi được lưu trạng thái trong bảng mục lục\n"
-"(dùng tùy chọn --cached để giữ tập tin, hoặc -f để ép buộc gỡ bỏ)"
+"\n"
+"(dùng -f để buộc gỡ bỏ)"
 
-#: builtin/rm.c:191
-#, c-format
+#: builtin/rm.c:239
+msgid "the following file has changes staged in the index:"
+msgid_plural "the following files have changes staged in the index:"
+msgstr[0] "tập tin sau đây có thay đổi trạng thái trong bảng mục lục:"
+msgstr[1] "các tập tin sau đây có thay đổi trạng thái trong bảng mục lục:"
+
+#: builtin/rm.c:243 builtin/rm.c:254
 msgid ""
-"'%s' has local modifications\n"
+"\n"
 "(use --cached to keep the file, or -f to force removal)"
 msgstr ""
-"“%s” có các thay đổi nội bộ\n"
+"\n"
 "(dùng tùy chọn --cached để giữ tập tin, hoặc -f để ép buộc gỡ bỏ)"
 
-#: builtin/rm.c:207
+#: builtin/rm.c:251
+msgid "the following file has local modifications:"
+msgid_plural "the following files have local modifications:"
+msgstr[0] "tập tin sau đây có những thay đổi nội bộ:"
+msgstr[1] "những tập tin sau đây có những thay đổi nội bộ:"
+
+#: builtin/rm.c:269
 msgid "do not list removed files"
 msgstr "không liệt kê các tập tin đã gỡ bỏ"
 
-#: builtin/rm.c:208
+#: builtin/rm.c:270
 msgid "only remove from the index"
 msgstr "chỉ gỡ bỏ từ mục lục"
 
-#: builtin/rm.c:209
+#: builtin/rm.c:271
 msgid "override the up-to-date check"
 msgstr "ghi đè lên kiểm tra cập nhật"
 
-#: builtin/rm.c:210
+#: builtin/rm.c:272
 msgid "allow recursive removal"
 msgstr "cho phép gỡ bỏ đệ qui"
 
-#: builtin/rm.c:212
+#: builtin/rm.c:274
 msgid "exit with a zero status even if nothing matched"
 msgstr "thoát ra với trạng thái khác không thậm chí nếu không có gì khớp"
 
-#: builtin/rm.c:283
+#: builtin/rm.c:345
 #, c-format
 msgid "not removing '%s' recursively without -r"
 msgstr "không thể gỡ bỏ “%s” một cách đệ qui mà không có tùy chọn -r"
 
-#: builtin/rm.c:322
+#: builtin/rm.c:384
 #, c-format
 msgid "git rm: unable to remove %s"
 msgstr "git rm: không thể gỡ bỏ %s"
 
 #: builtin/shortlog.c:13
-msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
-msgstr "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
+msgid "git shortlog [<options>] [<revision range>] [[--] [<path>...]]"
+msgstr "git shortlog [các-tùy-chọn] [<vùng-xét-duyệt>] [[--] [<đường-dẫn>...]]"
 
-#: builtin/shortlog.c:133
+#: builtin/shortlog.c:131
 #, c-format
 msgid "Missing author: %s"
 msgstr "Thiếu tên tác giả: %s"
 
-#: builtin/shortlog.c:229
+#: builtin/shortlog.c:228
 msgid "sort output according to the number of commits per author"
 msgstr "sắp xếp kết xuất tuân theo số lượng chuyển giao trên mỗi tác giả"
 
-#: builtin/shortlog.c:231
+#: builtin/shortlog.c:230
 msgid "Suppress commit descriptions, only provides commit count"
 msgstr "Chặn mọi mô tả lần chuyển giao, chỉ đưa ra số lượng lần chuyển giao"
 
-#: builtin/shortlog.c:233
+#: builtin/shortlog.c:232
 msgid "Show the email address of each author"
 msgstr "Hiển thị thư điện tử cho từng tác giả"
 
-#: builtin/shortlog.c:234
+#: builtin/shortlog.c:233
 msgid "w[,i1[,i2]]"
 msgstr "w[,i1[,i2]]"
 
-#: builtin/shortlog.c:235
+#: builtin/shortlog.c:234
 msgid "Linewrap output"
 msgstr "Ngắt dòng khi quá dài"
 
@@ -8293,47 +8906,47 @@ msgstr ""
 msgid "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g|--reflog)[=<n>[,<nền>]] [--list] [<ref>]"
 
-#: builtin/show-branch.c:651
+#: builtin/show-branch.c:650
 msgid "show remote-tracking and local branches"
 msgstr "hiển thị các nhánh remote-tracking và nội bộ"
 
-#: builtin/show-branch.c:653
+#: builtin/show-branch.c:652
 msgid "show remote-tracking branches"
 msgstr "hiển thị các nhánh remote-tracking"
 
-#: builtin/show-branch.c:655
+#: builtin/show-branch.c:654
 msgid "color '*!+-' corresponding to the branch"
 msgstr "màu “*!+-” tương ứng với nhánh"
 
-#: builtin/show-branch.c:657
+#: builtin/show-branch.c:656
 msgid "show <n> more commits after the common ancestor"
 msgstr "hiển thị thêm <n> lần chuyển giao sau cha mẹ chung"
 
-#: builtin/show-branch.c:659
+#: builtin/show-branch.c:658
 msgid "synonym to more=-1"
 msgstr "đồng nghĩa với more=-1"
 
-#: builtin/show-branch.c:660
+#: builtin/show-branch.c:659
 msgid "suppress naming strings"
 msgstr "chặn các chuỗi đặt tên"
 
-#: builtin/show-branch.c:662
+#: builtin/show-branch.c:661
 msgid "include the current branch"
 msgstr "bao gồm nhánh hiện hành"
 
-#: builtin/show-branch.c:664
+#: builtin/show-branch.c:663
 msgid "name commits with their object names"
 msgstr "đặt tên các lần chuyển giao bằng các tên của đối tượng của chúng"
 
-#: builtin/show-branch.c:666
+#: builtin/show-branch.c:665
 msgid "show possible merge bases"
 msgstr "hiển thị mọi cơ sở có thể dùng để hòa trộn"
 
-#: builtin/show-branch.c:668
+#: builtin/show-branch.c:667
 msgid "show refs unreachable from any other ref"
 msgstr "hiển thị các tham chiếu không thể được đọc bởi bất kỳ tham chiếu khác"
 
-#: builtin/show-branch.c:670
+#: builtin/show-branch.c:669
 msgid "show commits in topological order"
 msgstr "hiển thị các lần chuyển giao theo thứ tự tôpô"
 
@@ -8346,14 +8959,14 @@ msgid "show merges reachable from only one tip"
 msgstr "hiển thị các lần hòa trộn có thể đọc được chỉ từ một đầu mút"
 
 #: builtin/show-branch.c:676
-msgid "show commits where no parent comes before its children"
-msgstr "hiển thị các lần chuyển giao nơi mà cha mẹ đến trước con của nó"
+msgid "topologically sort, maintaining date order where possible"
+msgstr "sắp xếp hình thái học, bảo trì thứ tự ngày nếu có thể"
 
-#: builtin/show-branch.c:678
+#: builtin/show-branch.c:679
 msgid "<n>[,<base>]"
 msgstr "<n>[,<cơ sở>]"
 
-#: builtin/show-branch.c:679
+#: builtin/show-branch.c:680
 msgid "show <n> most recent ref-log entries starting at base"
 msgstr "hiển thị <n> các mục “ref-log” gần nhất kể từ nền (base)"
 
@@ -8369,38 +8982,38 @@ msgstr ""
 msgid "git show-ref --exclude-existing[=pattern] < ref-list"
 msgstr "git show-ref --exclude-existing[=mẫu] < ref-list"
 
-#: builtin/show-ref.c:165
+#: builtin/show-ref.c:168
 msgid "only show tags (can be combined with heads)"
 msgstr "chỉ hiển thị thẻ (có thể tổ hợp cùng với đầu)"
 
-#: builtin/show-ref.c:166
+#: builtin/show-ref.c:169
 msgid "only show heads (can be combined with tags)"
 msgstr "chỉ hiển thị đầu (có thể tổ hợp cùng với thẻ)"
 
-#: builtin/show-ref.c:167
+#: builtin/show-ref.c:170
 msgid "stricter reference checking, requires exact ref path"
 msgstr ""
 "việc kiểm tra tham chiếu chính xác, đòi hỏi chính xác đường dẫn tham chiếu "
 "(ref)"
 
-#: builtin/show-ref.c:170 builtin/show-ref.c:172
-msgid "show the HEAD reference"
-msgstr "hiển thị tham chiếu của HEAD"
+#: builtin/show-ref.c:173 builtin/show-ref.c:176
+msgid "show the HEAD reference, even if it would be filtered out"
+msgstr "hiển thị tham chiếu HEAD, ngay cả khi nó đã được lọc ra"
 
-#: builtin/show-ref.c:174
+#: builtin/show-ref.c:178
 msgid "dereference tags into object IDs"
 msgstr "bãi bỏ tham chiếu các thẻ thành ra các ID đối tượng"
 
-#: builtin/show-ref.c:176
+#: builtin/show-ref.c:180
 msgid "only show SHA1 hash using <n> digits"
 msgstr "chỉ hiển thị mã băm SHA1 sử dụng <n> chữ số"
 
-#: builtin/show-ref.c:180
+#: builtin/show-ref.c:184
 msgid "do not print results to stdout (useful with --verify)"
 msgstr ""
 "không hiển thị kết quả ra đầu ra chuẩn (stdout) (chỉ hữu dụng với --verify)"
 
-#: builtin/show-ref.c:182
+#: builtin/show-ref.c:186
 msgid "show refs from stdin that aren't in local repository"
 msgstr ""
 "hiển thị các tham chiếu (refs) từ đầu vào tiêu chuẩn (stdin) cái mà không ở "
@@ -8821,24 +9434,33 @@ msgstr "ghi đối tượng cây (tree) cho <tiền tố> thư mục con"
 msgid "only useful for debugging"
 msgstr "chỉ hữu ích khi cần gỡ lỗi"
 
-#: git.c:16
-msgid "See 'git help <command>' for more information on a specific command."
+#: git.c:17
+msgid ""
+"'git help -a' and 'git help -g' lists available subcommands and some\n"
+"concept guides. See 'git help <command>' or 'git help <concept>'\n"
+"to read about a specific subcommand or concept."
 msgstr ""
-"Chạy lệnh “git help <tên-lệnh>” để có thêm thông tin về lệnh được chỉ ra."
+"“git help -a” và “git help -g” liệt kê các câu lệnh con sẵn có và một số\n"
+"hướng dẫn về khái niệm. Xem “git help <lệnh>” hay “git help <khái-niệm>”\n"
+"để xem các đặc tả cho lệnh hay khái niệm cụ thể."
 
-#: parse-options.h:156
+#: parse-options.h:144
+msgid "expiry date"
+msgstr "ngày hết hạn"
+
+#: parse-options.h:159
 msgid "no-op (backward compatibility)"
 msgstr "no-op (tương thích ngược)"
 
-#: parse-options.h:232
+#: parse-options.h:236
 msgid "be more verbose"
 msgstr "chi tiết hơn nữa"
 
-#: parse-options.h:234
+#: parse-options.h:238
 msgid "be more quiet"
 msgstr "im lặng hơn nữa"
 
-#: parse-options.h:240
+#: parse-options.h:244
 msgid "use <n> digits to display SHA-1s"
 msgstr "sử dụng <n> chữ số để hiển thị SHA-1s"
 
@@ -8856,7 +9478,7 @@ msgstr "Liệt kê, tạo hay là xóa các nhánh"
 
 #: common-cmds.h:11
 msgid "Checkout a branch or paths to the working tree"
-msgstr "Lấy ra (checkout) một nhánh hay các đường dẫn tới cây làm việc"
+msgstr "Lấy ra một nhánh hay các đường dẫn tới cây làm việc"
 
 #: common-cmds.h:12
 msgid "Clone a repository into a new directory"
@@ -8869,12 +9491,12 @@ msgstr "Ghi các thay đổi vào kho chứa"
 #: common-cmds.h:14
 msgid "Show changes between commits, commit and working tree, etc"
 msgstr ""
-"Hiển thị các thay đổi giữa những lần chuyển giao (commit), commit và cây làm "
-"việc, v.v.."
+"Xem các thay đổi giữa những lần chuyển giao,\n"
+"               giữa một lần chuyển giao và cây làm việc, v.v.."
 
 #: common-cmds.h:15
 msgid "Download objects and refs from another repository"
-msgstr "Các đối tượng và tham chiếu được tải về từ kho chứa khác"
+msgstr "Tải về các đối tượng và tham chiếu từ kho chứa khác"
 
 #: common-cmds.h:16
 msgid "Print lines matching a pattern"
@@ -8882,40 +9504,37 @@ msgstr "In ra những dòng khớp với một mẫu"
 
 #: common-cmds.h:17
 msgid "Create an empty Git repository or reinitialize an existing one"
-msgstr ""
-"Tạo một kho git trống rỗng hay khởi tạo lại một kho đã tồn tại từ trước"
+msgstr "Tạo một kho git mới hay khởi tạo lại một kho đã tồn tại từ trước"
 
 #: common-cmds.h:18
 msgid "Show commit logs"
-msgstr "Hiển thị nhật ký các lần chuyển giao (commit)"
+msgstr "Hiển thị nhật ký các lần chuyển giao"
 
 #: common-cmds.h:19
 msgid "Join two or more development histories together"
-msgstr ""
-"Hợp nhất hai hay nhiều hơn lịch sử của các nhà phát triển phần mềm lại với "
-"nhau"
+msgstr "Hợp nhất hai hay nhiều hơn lịch sử của các nhà phát triển"
 
 #: common-cmds.h:20
 msgid "Move or rename a file, a directory, or a symlink"
-msgstr "Di chuyển, đổi tên một tập tin, thư mục hay liên kết tượng trưng"
+msgstr "Di chuyển hay đổi tên một tập tin, thư mục hoặc liên kết mềm"
 
 #: common-cmds.h:21
-msgid "Fetch from and merge with another repository or a local branch"
-msgstr "Lấy về (fetch) và hòa trộn với kho khác hay nhánh nội bộ"
+msgid "Fetch from and integrate with another repository or a local branch"
+msgstr "Lấy về và hợp nhất với kho khác hay một nhánh nội bộ"
 
 #: common-cmds.h:22
 msgid "Update remote refs along with associated objects"
-msgstr ""
-"Cập nhật tham chiếu (refs) máy chủ cùng với các đối tượng liên quan đến nó"
+msgstr "Cập nhật th.chiếu máy chủ cùng với các đối tượng liên quan đến nó"
 
 #: common-cmds.h:23
 msgid "Forward-port local commits to the updated upstream head"
 msgstr ""
-"Forward-port những lần chuyển giao nội bộ tới head thượng nguồn đã cập nhật"
+"Chuyển tiếp những lần chuyển giao nội bộ\n"
+"               tới head thượng nguồn đã cập nhật"
 
 #: common-cmds.h:24
 msgid "Reset current HEAD to the specified state"
-msgstr "Đặt lại HEAD hiện hành thành một trạng thái được chỉ ra"
+msgstr "Đặt lại HEAD hiện hành thành trạng thái đã cho"
 
 #: common-cmds.h:25
 msgid "Remove files from the working tree and from the index"
@@ -8931,9 +9550,7 @@ msgstr "Hiển thị trạng thái cây làm việc"
 
 #: common-cmds.h:28
 msgid "Create, list, delete or verify a tag object signed with GPG"
-msgstr ""
-"Tạo, liệt kê, xóa hay xác thực một đối tượng thẻ (tag) mà nó được ký sử dụng "
-"GPG"
+msgstr "Tạo, liệt kê, xóa hay xác thực một đối tượng thẻ được ký bằng GPG"
 
 #: git-am.sh:50
 msgid "You need to set your committer info first"
@@ -8950,11 +9567,11 @@ msgstr ""
 #: git-am.sh:105
 #, sh-format
 msgid ""
-"When you have resolved this problem, run \"$cmdline --resolved\".\n"
+"When you have resolved this problem, run \"$cmdline --continue\".\n"
 "If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n"
 "To restore the original branch and stop patching, run \"$cmdline --abort\"."
 msgstr ""
-"Khi bạn cần giải quyết vấn đề này hãy chạy lệnh \"$cmdline --resolved\".\n"
+"Khi bạn cần giải quyết vấn đề này hãy chạy lệnh \"$cmdline --continue\".\n"
 "Nếu bạn có ý định bỏ qua miếng vá, thay vào đó bạn chạy \"$cmdline --skip"
 "\".\n"
 "Để phục hồi lại thành nhánh nguyên thủy và dừng việc vá lại thì chạy "
@@ -9010,25 +9627,34 @@ msgstr ""
 "Tùy chọn -b/--binary đã không dùng từ lâu rồi, và\n"
 "nó sẽ được bỏ đi. Xin đừng sử dụng nó thêm nữa."
 
-#: git-am.sh:477
+#: git-am.sh:479
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
 msgstr "thư mục rebase trước $dotest vẫn chưa sẵn sàng nhưng mbox được đưa ra."
 
-#: git-am.sh:482
+#: git-am.sh:484
 msgid "Please make up your mind. --skip or --abort?"
 msgstr "Xin hãy rõ ràng. --skip hay --abort?"
 
-#: git-am.sh:509
+#: git-am.sh:520
+#, sh-format
+msgid ""
+"Stray $dotest directory found.\n"
+"Use \"git am --abort\" to remove it."
+msgstr ""
+"Tìm thấy thư mục lạc $dotest.\n"
+"Dùng \"git am --abort\" để loại bỏ nó đi."
+
+#: git-am.sh:528
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr "Thao tác phân giải không được tiến hành, chúng ta không phục hồi lại."
 
-#: git-am.sh:575
+#: git-am.sh:594
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
 msgstr "Bảng mục lục sai: không thể áp dụng các miếng vá (sai: $files)"
 
-#: git-am.sh:679
+#: git-am.sh:698
 #, sh-format
 msgid ""
 "Patch is empty.  Was it split wrong?\n"
@@ -9041,34 +9667,34 @@ msgstr ""
 "Để phục hồi lại nhánh nguyên thủy và dừng vá lại hãy chạy lệnh \"$cmdline --"
 "abort\"."
 
-#: git-am.sh:706
+#: git-am.sh:725
 msgid "Patch does not have a valid e-mail address."
 msgstr "Miếng vá không có địa chỉ e-mail hợp lệ."
 
-#: git-am.sh:753
+#: git-am.sh:772
 msgid "cannot be interactive without stdin connected to a terminal."
 msgstr ""
 "không thể được tương tác mà không có stdin kết nối với một thiết bị cuối"
 
-#: git-am.sh:757
+#: git-am.sh:776
 msgid "Commit Body is:"
 msgstr "Thân của lần chuyển giao (commit) là:"
 
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
-#: git-am.sh:764
+#: git-am.sh:783
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 msgstr ""
 "Áp dụng? đồng ý [y]/không [n]/chỉnh sửa [e]/hiển thị miếng [v]á/đồng ý tất "
 "cả [a] "
 
-#: git-am.sh:800
+#: git-am.sh:819
 #, sh-format
 msgid "Applying: $FIRSTLINE"
 msgstr "Đang áp dụng (miếng vá): $FIRSTLINE"
 
-#: git-am.sh:821
+#: git-am.sh:840
 msgid ""
 "No changes - did you forget to use 'git add'?\n"
 "If there is nothing left to stage, chances are that something else\n"
@@ -9079,7 +9705,7 @@ msgstr ""
 "đã sẵn được đưa vào với cùng nội dung thay đổi; bạn có lẽ muốn bỏ qua miếng "
 "vá này."
 
-#: git-am.sh:829
+#: git-am.sh:848
 msgid ""
 "You still have unmerged paths in your index\n"
 "did you forget to use 'git add'?"
@@ -9087,16 +9713,16 @@ msgstr ""
 "Bạn vẫn có những đường dẫn chưa được hòa trộn trong bảng mục lục của mình\n"
 "bạn đã quên sử dụng lệnh “git add” à?"
 
-#: git-am.sh:845
+#: git-am.sh:864
 msgid "No changes -- Patch already applied."
 msgstr "Không thay đổi gì cả -- Miếng vá đã được áp dụng rồi."
 
-#: git-am.sh:855
+#: git-am.sh:874
 #, sh-format
 msgid "Patch failed at $msgnum $FIRSTLINE"
 msgstr "Vá gặp lỗi tại $msgnum $FIRSTLINE"
 
-#: git-am.sh:858
+#: git-am.sh:877
 #, sh-format
 msgid ""
 "The copy of the patch that failed is found in:\n"
@@ -9105,7 +9731,7 @@ msgstr ""
 "Bản sao chép của miếng vá mà nó gặp lỗi thì được tìm thấy trong:\n"
 "   $dotest/patch"
 
-#: git-am.sh:876
+#: git-am.sh:895
 msgid "applying to an empty history"
 msgstr "áp dụng vào một lịch sử trống rỗng"
 
@@ -9128,7 +9754,7 @@ msgstr "không công nhận tùy chọn: “$arg”"
 #: git-bisect.sh:99
 #, sh-format
 msgid "'$arg' does not appear to be a valid revision"
-msgstr "”$arg” không có vẻ như là một sự xét lại hợp lệ"
+msgstr "”$arg” không có vẻ như là một điểm xét duyệt hợp lệ"
 
 #: git-bisect.sh:117
 msgid "Bad HEAD - I need a HEAD"
@@ -9206,16 +9832,16 @@ msgstr ""
 "sai.\n"
 "(Bạn có thể sử dụng \"git bisect bad\" và \"git bisect good\" cho chúng.)"
 
-#: git-bisect.sh:347 git-bisect.sh:474
+#: git-bisect.sh:363 git-bisect.sh:490
 msgid "We are not bisecting."
 msgstr "Chúng tôi không bisect."
 
-#: git-bisect.sh:354
+#: git-bisect.sh:370
 #, sh-format
 msgid "'$invalid' is not a valid commit"
 msgstr "”$invalid” không phải là lần chuyển giao (commit) hợp lệ"
 
-#: git-bisect.sh:363
+#: git-bisect.sh:379
 #, sh-format
 msgid ""
 "Could not check out original HEAD '$branch'.\n"
@@ -9224,25 +9850,25 @@ msgstr ""
 "Không thể check-out HEAD nguyên thủy của “$branch”.\n"
 "Hãy thử “git bisect reset <lần-chuyển-giao>”."
 
-#: git-bisect.sh:390
+#: git-bisect.sh:406
 msgid "No logfile given"
 msgstr "Chưa chỉ ra tập tin ghi nhật ký"
 
-#: git-bisect.sh:391
+#: git-bisect.sh:407
 #, sh-format
 msgid "cannot read $file for replaying"
 msgstr "không thể đọc $file để thao diễn lại"
 
-#: git-bisect.sh:408
+#: git-bisect.sh:424
 msgid "?? what are you talking about?"
 msgstr "?? bạn đang nói gì thế?"
 
-#: git-bisect.sh:420
+#: git-bisect.sh:436
 #, sh-format
 msgid "running $command"
 msgstr "đang chạy lệnh $command"
 
-#: git-bisect.sh:427
+#: git-bisect.sh:443
 #, sh-format
 msgid ""
 "bisect run failed:\n"
@@ -9251,11 +9877,11 @@ msgstr ""
 "chạy bisect gặp lỗi:\n"
 "mã trả về $res từ lệnh “$command” là < 0 hoặc >= 128"
 
-#: git-bisect.sh:453
+#: git-bisect.sh:469
 msgid "bisect run cannot continue any more"
 msgstr "bisect không thể tiếp tục thêm được nữa"
 
-#: git-bisect.sh:459
+#: git-bisect.sh:475
 #, sh-format
 msgid ""
 "bisect run failed:\n"
@@ -9264,7 +9890,7 @@ msgstr ""
 "chạy bisect gặp lỗi:\n"
 "”bisect_state $state” đã thoát ra với mã lỗi $res"
 
-#: git-bisect.sh:466
+#: git-bisect.sh:482
 msgid "bisect run success"
 msgstr "bisect chạy thành công"
 
@@ -9284,7 +9910,7 @@ msgid "Pull is not possible because you have unmerged files."
 msgstr ""
 "Full là không thể thực hiện bởi vì bạn có những tập tin chưa được hòa trộn."
 
-#: git-pull.sh:197
+#: git-pull.sh:203
 msgid "updating an unborn branch with changes added to the index"
 msgstr ""
 "đang cập nhật một nhánh chưa được sinh ra với các thay đổi được thêm vào "
@@ -9294,7 +9920,7 @@ msgstr ""
 #. The working tree and the index file is still based on the
 #. $orig_head commit, but we are merging into $curr_head.
 #. First update the working tree to match $curr_head.
-#: git-pull.sh:229
+#: git-pull.sh:235
 #, sh-format
 msgid ""
 "Warning: fetch updated the current branch head.\n"
@@ -9305,15 +9931,15 @@ msgstr ""
 "Cảnh báo: đang fast-forward cây làm việc của bạn từ\n"
 "Cảnh báo: commit $orig_head."
 
-#: git-pull.sh:254
+#: git-pull.sh:260
 msgid "Cannot merge multiple branches into empty head"
 msgstr "Không thể hòa trộn nhiều nhánh và trong một head trống rỗng"
 
-#: git-pull.sh:258
+#: git-pull.sh:264
 msgid "Cannot rebase onto multiple branches"
 msgstr "Không thể thực hiện lệnh rebase (cơ cấu lại) trên nhiều nhánh"
 
-#: git-rebase.sh:53
+#: git-rebase.sh:54
 msgid ""
 "When you have resolved this problem, run \"git rebase --continue\".\n"
 "If you prefer to skip this patch, run \"git rebase --skip\" instead.\n"
@@ -9326,35 +9952,55 @@ msgstr ""
 "Để phục hồi lại thành nhánh nguyên thủy và dừng việc vá lại thì chạy \"git "
 "rebase --abort\"."
 
+#: git-rebase.sh:156
+msgid "Applied autostash."
+msgstr "Đã áp dụng autostash."
+
+#: git-rebase.sh:159
+#, sh-format
+msgid "Cannot store $stash_sha1"
+msgstr "Không thể lưu $stash_sha1"
+
 #: git-rebase.sh:160
+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 ""
+"Áp dụng autostash có hiệu quả trong các xung đột.\n"
+"Các thay đổi của bạn an toàn trong stash (tạm cất đi).\n"
+"Bạn có thể chạy lệnh \"git stash pop\" hay \"git stash drop\" bất kỳ lúc "
+"nào.\n"
+
+#: git-rebase.sh:190
 msgid "The pre-rebase hook refused to rebase."
 msgstr "Móc (hook) pre-rebase từ chối rebase."
 
-#: git-rebase.sh:165
+#: git-rebase.sh:195
 msgid "It looks like git-am is in progress. Cannot rebase."
 msgstr ""
 "Hình như đang trong quá trình thực hiện lệnh git-am. Không thể chạy lệnh "
 "rebase."
 
-#: git-rebase.sh:296
+#: git-rebase.sh:329
 msgid "The --exec option must be used with the --interactive option"
 msgstr "Tùy chọn --exec phải được sử dụng cùng với tùy chọn --interactive"
 
-#: git-rebase.sh:301
+#: git-rebase.sh:334
 msgid "No rebase in progress?"
 msgstr "Không có tiến trình rebase nào phải không?"
 
-#: git-rebase.sh:312
+#: git-rebase.sh:345
 msgid "The --edit-todo action can only be used during interactive rebase."
 msgstr ""
 "Hành động “--edit-todo” chỉ có thể dùng trong quá trình “rebase” (sửa lịch "
 "sử) tương tác."
 
-#: git-rebase.sh:319
+#: git-rebase.sh:352
 msgid "Cannot read HEAD"
 msgstr "Không thể đọc HEAD"
 
-#: git-rebase.sh:322
+#: git-rebase.sh:355
 msgid ""
 "You must edit all merge conflicts and then\n"
 "mark them as resolved using git add"
@@ -9362,12 +10008,12 @@ msgstr ""
 "Bạn phải sửa tất cả các lần hòa trộn xung đột và sau\n"
 "đó đánh dấu chúng là cần xử lý sử dụng lệnh git add"
 
-#: git-rebase.sh:340
+#: git-rebase.sh:373
 #, sh-format
 msgid "Could not move back to $head_name"
 msgstr "Không thể quay trở lại $head_name"
 
-#: git-rebase.sh:359
+#: git-rebase.sh:392
 #, sh-format
 msgid ""
 "It seems that there is already a $state_dir_base directory, and\n"
@@ -9390,57 +10036,66 @@ msgstr ""
 "\n"
 "TÔI: là lệnh bạn vừa gọi!"
 
-#: git-rebase.sh:404
+#: git-rebase.sh:437
 #, sh-format
 msgid "invalid upstream $upstream_name"
 msgstr "thượng nguồn không hợp lệ $upstream_name"
 
-#: git-rebase.sh:428
+#: git-rebase.sh:461
 #, sh-format
 msgid "$onto_name: there are more than one merge bases"
 msgstr "$onto_name: ở đây có nhiều hơn một nền móng hòa trộn"
 
-#: git-rebase.sh:431 git-rebase.sh:435
+#: git-rebase.sh:464 git-rebase.sh:468
 #, sh-format
 msgid "$onto_name: there is no merge base"
 msgstr "$onto_name: ở đây không có nền móng hòa trộn nào"
 
-#: git-rebase.sh:440
+#: git-rebase.sh:473
 #, sh-format
 msgid "Does not point to a valid commit: $onto_name"
 msgstr "Không chỉ đến một lần chuyển giao (commit) không hợp lệ: $onto_name"
 
-#: git-rebase.sh:463
+#: git-rebase.sh:496
 #, sh-format
 msgid "fatal: no such branch: $branch_name"
 msgstr "nghiêm trọng: không có nhánh như thế: $branch_name"
 
-#: git-rebase.sh:483
+#: git-rebase.sh:519
+msgid "Cannot autostash"
+msgstr "Không thể autostash"
+
+#: git-rebase.sh:524
+#, sh-format
+msgid "Created autostash: $stash_abbrev"
+msgstr "Đã tạo autostash: $stash_abbrev"
+
+#: git-rebase.sh:528
 msgid "Please commit or stash them."
 msgstr "Xin hãy commit hoặc tạm cất (stash) chúng."
 
-#: git-rebase.sh:501
+#: git-rebase.sh:548
 #, sh-format
 msgid "Current branch $branch_name is up to date."
 msgstr "Nhánh hiện tại $branch_name đã được cập nhật rồi."
 
-#: git-rebase.sh:504
+#: git-rebase.sh:552
 #, sh-format
 msgid "Current branch $branch_name is up to date, rebase forced."
 msgstr "Nhánh hiện tại $branch_name đã được cập nhật rồi, lệnh rebase ép buộc."
 
-#: git-rebase.sh:515
+#: git-rebase.sh:563
 #, sh-format
 msgid "Changes from $mb to $onto:"
 msgstr "Thay đổi từ $mb thành $onto:"
 
 #. Detach HEAD and reset the tree
-#: git-rebase.sh:524
+#: git-rebase.sh:572
 msgid "First, rewinding head to replay your work on top of it..."
 msgstr ""
 "Trước tiên, di chuyển head để xem lại các công việc trên đỉnh của nó..."
 
-#: git-rebase.sh:532
+#: git-rebase.sh:582
 #, sh-format
 msgid "Fast-forwarded $branch_name to $onto_name."
 msgstr "Fast-forward $branch_name thành $onto_name."
@@ -9474,6 +10129,11 @@ msgstr "Không thể gỡ bỏ bảng mục lục tạm thời (không thể x
 msgid "Cannot record working tree state"
 msgstr "Không thể ghi lại trạng thái cây làm việc hiện hành"
 
+#: git-stash.sh:190
+#, sh-format
+msgid "Cannot update $ref_stash with $w_commit"
+msgstr "Không thể cập nhật $ref_stash với $w_commit"
+
 #. TRANSLATORS: $option is an invalid option, like
 #. `--blah-blah'. The 7 spaces at the beginning of the
 #. second line correspond to "error: ". So you should line
@@ -9484,132 +10144,153 @@ msgstr "Không thể ghi lại trạng thái cây làm việc hiện hành"
 #. $ 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:202
+#: git-stash.sh:241
 #, sh-format
 msgid ""
 "error: unknown option for 'stash save': $option\n"
 "       To provide a message, use git stash save -- '$option'"
 msgstr ""
 "lỗi: không hiểu tùy chọn cho “stash save”: $option\n"
-"       Để cung cấp một thông điệp, sử dụng git stash save -- “$option”"
+"     Để dùng thông điệp có chứa -- ở đầu, sử dụng git stash save -- \"$option"
+"\""
 
-#: git-stash.sh:223
+#: git-stash.sh:262
 msgid "No local changes to save"
 msgstr "Không có thay đổi nội bộ nào được ghi lại"
 
-#: git-stash.sh:227
+#: git-stash.sh:268
+msgid ""
+"The following untracked files would NOT be saved but need to be removed by "
+"stash save:"
+msgstr ""
+"Các tập tin chưa được theo dõi sau đây KHÔNG được ghi lại nhưng cần được gỡ "
+"bỏ bởi việc ghi lại stash:"
+
+#: git-stash.sh:270
+msgid ""
+"Aborting. Consider using either the --force or --include-untracked option."
+msgstr ""
+"Bãi bỏ. Cân nhắc dùng một trong hai tùy chọn --force và --include-untracked."
+
+#: git-stash.sh:274
 msgid "Cannot initialize stash"
 msgstr "Không thể khởi tạo stash"
 
-#: git-stash.sh:235
+#: git-stash.sh:278
 msgid "Cannot save the current status"
 msgstr "Không thể ghi lại trạng thái hiện hành"
 
-#: git-stash.sh:253
+#: git-stash.sh:296
 msgid "Cannot remove worktree changes"
 msgstr "Không thể gỡ bỏ các thay đổi cây-làm-việc"
 
-#: git-stash.sh:352
+#: git-stash.sh:395
 msgid "No stash found."
 msgstr "Không tìm thấy stash nào."
 
-#: git-stash.sh:359
+#: git-stash.sh:402
 #, sh-format
 msgid "Too many revisions specified: $REV"
-msgstr "Chỉ ra quá nhiều điểm xét lại: $REV"
+msgstr "Chỉ ra quá nhiều điểm xét duyệt: $REV"
 
-#: git-stash.sh:365
+#: git-stash.sh:408
 #, sh-format
 msgid "$reference is not valid reference"
 msgstr "$reference không phải là tham chiếu hợp lệ"
 
-#: git-stash.sh:393
+#: git-stash.sh:436
 #, sh-format
 msgid "'$args' is not a stash-like commit"
 msgstr "”$args” không phải là lần chuyển giao (commit) giống-stash"
 
-#: git-stash.sh:404
+#: git-stash.sh:447
 #, sh-format
 msgid "'$args' is not a stash reference"
 msgstr "”$args” không phải tham chiếu đến stash"
 
-#: git-stash.sh:412
+#: git-stash.sh:455
 msgid "unable to refresh index"
 msgstr "không thể làm tươi mới bảng mục lục"
 
-#: git-stash.sh:416
+#: git-stash.sh:459
 msgid "Cannot apply a stash in the middle of a merge"
 msgstr "Không thể áp dụng một stash ở giữa của quá trình hòa trộn"
 
-#: git-stash.sh:424
+#: git-stash.sh:467
 msgid "Conflicts in index. Try without --index."
 msgstr "Xung đột trong bảng mục lục. Hãy thử mà không dùng tùy chọn --index."
 
-#: git-stash.sh:426
+#: git-stash.sh:469
 msgid "Could not save index tree"
 msgstr "Không thể ghi lại cây chỉ mục"
 
-#: git-stash.sh:460
+#: git-stash.sh:503
 msgid "Cannot unstage modified files"
 msgstr "Không thể bỏ trạng thía của các tập tin đã được sửa chữa"
 
-#: git-stash.sh:475
+#: git-stash.sh:518
 msgid "Index was not unstashed."
 msgstr "Bảng mục lục đã không được bỏ stash."
 
-#: git-stash.sh:492
+#: git-stash.sh:535
 #, sh-format
 msgid "Dropped ${REV} ($s)"
-msgstr "Đã hạ xuống ${REV} ($s)"
+msgstr "Đã xóa ${REV} ($s)"
 
-#: git-stash.sh:493
+#: git-stash.sh:536
 #, sh-format
 msgid "${REV}: Could not drop stash entry"
 msgstr "${REV}: Không thể xóa bỏ mục stash"
 
-#: git-stash.sh:500
+#: git-stash.sh:543
 msgid "No branch name specified"
 msgstr "Chưa chỉ ra tên của nhánh"
 
-#: git-stash.sh:571
+#: git-stash.sh:615
 msgid "(To restore them type \"git stash apply\")"
 msgstr "(Để phục hồi lại chúng hãy gõ \"git stash apply\")"
 
-#: git-submodule.sh:90
+#: git-submodule.sh:95
 #, sh-format
 msgid "cannot strip one component off url '$remoteurl'"
 msgstr "không thể tháo bỏ một thành phần ra khỏi “$remoteurl” url"
 
-#: git-submodule.sh:195
+#: git-submodule.sh:237
 #, sh-format
 msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
 msgstr ""
 "Không tìm thấy ánh xạ (mapping) mô-đun-con trong .gitmodules cho đường dẫn "
 "“$sm_path”"
 
-#: git-submodule.sh:238
+#: git-submodule.sh:281
 #, sh-format
 msgid "Clone of '$url' into submodule path '$sm_path' failed"
 msgstr "Nhân bản “$url” vào đường dẫn mô-đun-con “$sm_path” gặp lỗi"
 
-#: git-submodule.sh:250
+#: git-submodule.sh:293
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
 msgstr ""
 "Gitdir “$a” là bộ phận của đường dẫn mô-đun-con “$b” hoặc \"vice versa\""
 
-#: git-submodule.sh:343
+#: git-submodule.sh:403
+msgid "Relative path can only be used from the toplevel of the working tree"
+msgstr ""
+"Đường dẫn tương đối chỉ có thể dùng từ thư mục ở mức cao nhất của cây làm "
+"việc"
+
+#: git-submodule.sh:413
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
 msgstr ""
 "repo URL: “$repo” phải là đường dẫn tuyệt đối hoặc là bắt đầu bằng ./|../"
 
-#: git-submodule.sh:360
+#: git-submodule.sh:430
 #, sh-format
 msgid "'$sm_path' already exists in the index"
 msgstr "”$sm_path” thực sự đã tồn tại ở bảng mục lục rồi"
 
-#: git-submodule.sh:364
+#: git-submodule.sh:434
 #, sh-format
 msgid ""
 "The following path is ignored by one of your .gitignore files:\n"
@@ -9621,36 +10302,36 @@ msgstr ""
 "$sm_path\n"
 "Sử dụng -f nếu bạn thực sự muốn thêm nó vào."
 
-#: git-submodule.sh:382
+#: git-submodule.sh:452
 #, sh-format
 msgid "Adding existing repo at '$sm_path' to the index"
 msgstr "Đang thêm repo có sẵn tại “$sm_path” vào bảng mục lục"
 
-#: git-submodule.sh:384
+#: git-submodule.sh:454
 #, sh-format
 msgid "'$sm_path' already exists and is not a valid git repo"
 msgstr "”$sm_path” đã tồn tại từ trước và không phải là một kho git hợp lệ"
 
-#: git-submodule.sh:392
+#: git-submodule.sh:462
 #, sh-format
 msgid "A git directory for '$sm_name' is found locally with remote(s):"
 msgstr ""
 "Thư mục git cho “$sm_name” được tìm thấy một cách cục bộ với các máy chủ:"
 
-#: git-submodule.sh:394
+#: git-submodule.sh:464
 #, sh-format
 msgid ""
 "If you want to reuse this local git directory instead of cloning again from"
 msgstr "Nếu bạn muốn dùng lại thư mục git nội bộ này thay vì nhân bản từ nó"
 
-#: git-submodule.sh:396
+#: git-submodule.sh:466
 #, sh-format
 msgid ""
 "use the '--force' option. If the local git directory is not the correct repo"
 msgstr ""
 "dùng tùy chọn “--force”. Nếu thư mục git nội bộ không phải là repo (kho) đúng"
 
-#: git-submodule.sh:397
+#: git-submodule.sh:467
 #, sh-format
 msgid ""
 "or you are unsure what this means choose another name with the '--name' "
@@ -9659,158 +10340,302 @@ msgstr ""
 "hay bạn không chắc chắn điều đó có nghĩa gì chọn tên khác với tùy chọn “--"
 "name”."
 
-#: git-submodule.sh:399
+#: git-submodule.sh:469
 #, sh-format
 msgid "Reactivating local git directory for submodule '$sm_name'."
 msgstr ""
 "Phục hồi sự hoạt động của thư mục git nội bộ cho mô-đun-con “$sm_name”."
 
-#: git-submodule.sh:411
+#: git-submodule.sh:481
 #, sh-format
 msgid "Unable to checkout submodule '$sm_path'"
-msgstr "Không thể checkout mô-đun con “$sm_path”"
+msgstr "Không thể checkout mô-đun-con “$sm_path”"
 
-#: git-submodule.sh:416
+#: git-submodule.sh:486
 #, sh-format
 msgid "Failed to add submodule '$sm_path'"
-msgstr "Gặp lỗi khi thêm mô-đun con “$sm_path”"
+msgstr "Gặp lỗi khi thêm mô-đun-con “$sm_path”"
 
-#: git-submodule.sh:425
+#: git-submodule.sh:495
 #, sh-format
 msgid "Failed to register submodule '$sm_path'"
-msgstr "Gặp lỗi khi đăng ký với hệ thống mô-đun con “$sm_path”"
+msgstr "Gặp lỗi khi đăng ký với hệ thống mô-đun-con “$sm_path”"
+
+#: git-submodule.sh:539
+#, sh-format
+msgid "Entering '$prefix$displaypath'"
+msgstr "Đang vào “$prefix$displaypath”"
 
-#: git-submodule.sh:468
+#: git-submodule.sh:554
 #, sh-format
-msgid "Entering '$prefix$sm_path'"
-msgstr "Đang nhập “$prefix$sm_path”"
+msgid "Stopping at '$prefix$displaypath'; script returned non-zero status."
+msgstr ""
+"Dừng lại tại “$prefix$displaypath”; script trả về trạng thái khác không."
+
+#: git-submodule.sh:600
+#, sh-format
+msgid "No url found for submodule path '$displaypath' in .gitmodules"
+msgstr ""
+"Không tìm thấy url cho đường dẫn mô-đun-con “$displaypath” trong .gitmodules"
 
-#: git-submodule.sh:482
+#: git-submodule.sh:609
 #, sh-format
-msgid "Stopping at '$sm_path'; script returned non-zero status."
-msgstr "Dừng lại tại “$sm_path”; script trả về trạng thái khác không."
+msgid "Failed to register url for submodule path '$displaypath'"
+msgstr "Gặp lỗi khi đăng ký url cho đường dẫn mô-đun-con “$displaypath”"
 
-#: git-submodule.sh:526
+#: git-submodule.sh:611
 #, sh-format
-msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgid "Submodule '$name' ($url) registered for path '$displaypath'"
+msgstr "Mô-đun-con “$name” ($url) được đăng ký cho đường dẫn “$displaypath”"
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Failed to register update mode for submodule path '$displaypath'"
 msgstr ""
-"Không tìm thấy url cho đường dẫn mô-đun-con “$sm_path” trong .gitmodules"
+"Gặp lỗi khi đăng ký chế độ cập nhật cho đường dẫn mô-đun-con “$displaypath”"
+
+#: git-submodule.sh:656
+#, sh-format
+msgid "Use '.' if you really want to deinitialize all submodules"
+msgstr "Dùng “.” nếu bạn thực sự muốn gỡ bỏ mọi mô-đun-con"
 
-#: git-submodule.sh:535
+#: git-submodule.sh:673
 #, sh-format
-msgid "Failed to register url for submodule path '$sm_path'"
-msgstr "Gặp lỗi khi đăng ký url cho đường dẫn mô-đun-con “$sm_path”"
+msgid "Submodule work tree '$displaypath' contains a .git directory"
+msgstr "Cây làm việc mô-đun-con “$displaypath” có chứa thư mục .git"
 
-#: git-submodule.sh:537
+#: git-submodule.sh:674
 #, sh-format
-msgid "Submodule '$name' ($url) registered for path '$sm_path'"
-msgstr "Mô-đun-con “$name” ($url) được đăng ký cho đường dẫn “$sm_path”"
+msgid ""
+"(use 'rm -rf' if you really want to remove it including all of its history)"
+msgstr ""
+"(dùng “rm -rf” nếu bạn thực sự muốn gỡ bỏ nó cùng với tất cả lịch sử của "
+"chúng)"
 
-#: git-submodule.sh:545
+#: git-submodule.sh:680
 #, sh-format
-msgid "Failed to register update mode for submodule path '$sm_path'"
+msgid ""
+"Submodule work tree '$displaypath' contains local modifications; use '-f' to "
+"discard them"
 msgstr ""
-"Gặp lỗi khi đăng ký chế độ cập nhật cho đường dẫn mô-đun-con “$sm_path”"
+"Cây làm việc mô-đun-con “$displaypath” chứa các thay đổi nội bộ; hãy dùng “-"
+"f” để loại bỏ chúng đi"
+
+#: git-submodule.sh:683
+#, sh-format
+msgid "Cleared directory '$displaypath'"
+msgstr "Đã tạo thư mục '$displaypath'"
 
-#: git-submodule.sh:649
+#: git-submodule.sh:684
+#, sh-format
+msgid "Could not remove submodule work tree '$displaypath'"
+msgstr "Không thể gỡ bỏ cây làm việc mô-đun-con “$displaypath”"
+
+#: git-submodule.sh:687
+#, sh-format
+msgid "Could not create empty submodule directory '$displaypath'"
+msgstr "Không thể tạo thư mục mô-đun-con rỗng “$displaypath”"
+
+#: git-submodule.sh:696
+#, sh-format
+msgid "Submodule '$name' ($url) unregistered for path '$displaypath'"
+msgstr "Mô-đun-con “$name” ($url) được bỏ đăng ký cho đường dẫn “$displaypath”"
+
+#: git-submodule.sh:811
 #, sh-format
 msgid ""
-"Submodule path '$sm_path' not initialized\n"
+"Submodule path '$displaypath' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
-"Đường dẫn mô-đun-con “$sm_path” chưa được khởi tạo\n"
+"Đường dẫn mô-đun-con “$displaypath” chưa được khởi tạo.\n"
 "Có lẽ bạn muốn sử dụng lệnh “update --init”?"
 
-#: git-submodule.sh:662
+#: git-submodule.sh:824
 #, sh-format
-msgid "Unable to find current revision in submodule path '$sm_path'"
+msgid "Unable to find current revision in submodule path '$displaypath'"
 msgstr ""
-"Không tìm thấy điểm xét lại hiện hành trong đường dẫn mô-đun-con “$sm_path”"
+"Không tìm thấy điểm xét duyệt hiện hành trong đường dẫn mô-đun-con "
+"“$displaypath”"
 
-#: git-submodule.sh:671 git-submodule.sh:695
+#: git-submodule.sh:833
 #, sh-format
 msgid "Unable to fetch in submodule path '$sm_path'"
 msgstr "Không thể lấy về (fetch) trong đường dẫn mô-đun-con “$sm_path”"
 
-#: git-submodule.sh:709
+#: git-submodule.sh:857
+#, sh-format
+msgid "Unable to fetch in submodule path '$displaypath'"
+msgstr "Không thể lấy về (fetch) trong đường dẫn mô-đun-con “$displaypath”"
+
+#: git-submodule.sh:871
 #, sh-format
-msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
-msgstr "Không thể rebase “$sha1” trong đường dẫn mô-đun-con “$sm_path”"
+msgid "Unable to rebase '$sha1' in submodule path '$displaypath'"
+msgstr "Không thể rebase “$sha1” trong đường dẫn mô-đun-con “$displaypath”"
 
-#: git-submodule.sh:710
+#: git-submodule.sh:872
 #, sh-format
-msgid "Submodule path '$sm_path': rebased into '$sha1'"
-msgstr "Đường dẫn mô-đun-con “$sm_path”: được rebase vào trong “$sha1”"
+msgid "Submodule path '$displaypath': rebased into '$sha1'"
+msgstr "Đường dẫn mô-đun-con “$displaypath”: được rebase vào trong “$sha1”"
 
-#: git-submodule.sh:715
+#: git-submodule.sh:877
 #, sh-format
-msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgid "Unable to merge '$sha1' in submodule path '$displaypath'"
 msgstr ""
-"Không thể hòa trộn (merge) “$sha1” trong đường dẫn mô-đun-con “$sm_path”"
+"Không thể hòa trộn (merge) “$sha1” trong đường dẫn mô-đun-con “$displaypath”"
 
-#: git-submodule.sh:716
+#: git-submodule.sh:878
 #, sh-format
-msgid "Submodule path '$sm_path': merged in '$sha1'"
-msgstr "Đường dẫn mô-đun-con “$sm_path”: được hòa trộn vào “$sha1”"
+msgid "Submodule path '$displaypath': merged in '$sha1'"
+msgstr "Đường dẫn mô-đun-con “$displaypath”: được hòa trộn vào “$sha1”"
 
-#: git-submodule.sh:721
+#: git-submodule.sh:883
 #, sh-format
-msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
-msgstr "Không thể checkout “$sha1” trong đường dẫn mô-đun-con “$sm_path”"
+msgid ""
+"Execution of '$command $sha1' failed in submodule  path '$prefix$sm_path'"
+msgstr ""
+"Thực hiện không thành công lệnh “$command $sha1” trong đường dẫn mô-đun-con "
+"“$prefix$sm_path”"
 
-#: git-submodule.sh:722
+#: git-submodule.sh:884
 #, sh-format
-msgid "Submodule path '$sm_path': checked out '$sha1'"
-msgstr "Đường dẫn mô-đun-con “$sm_path”: được checkout “$sha1”"
+msgid "Submodule path '$prefix$sm_path': '$command $sha1'"
+msgstr "Đường dẫn mô-đun-con “$prefix$sm_path”: “$command $sha1”"
 
-#: git-submodule.sh:744 git-submodule.sh:1066
+#: git-submodule.sh:889
 #, sh-format
-msgid "Failed to recurse into submodule path '$sm_path'"
-msgstr "Gặp lỗi khi đệ quy vào trong đường dẫn mô-đun-con “$sm_path”"
+msgid "Unable to checkout '$sha1' in submodule path '$displaypath'"
+msgstr "Không thể checkout “$sha1” trong đường dẫn mô-đun-con “$displaypath”"
+
+#: git-submodule.sh:890
+#, sh-format
+msgid "Submodule path '$displaypath': checked out '$sha1'"
+msgstr "Đường dẫn mô-đun-con “$displaypath”: đã checkout “$sha1”"
+
+#: git-submodule.sh:917
+#, sh-format
+msgid "Failed to recurse into submodule path '$displaypath'"
+msgstr "Gặp lỗi khi đệ quy vào trong đường dẫn mô-đun-con “$displaypath”"
 
-#: git-submodule.sh:852
+#: git-submodule.sh:1025
 msgid "The --cached option cannot be used with the --files option"
 msgstr "Tùy chọn --cached không thể dùng cùng với tùy chọn --files"
 
 #. unexpected type
-#: git-submodule.sh:892
+#: git-submodule.sh:1066
 #, sh-format
 msgid "unexpected mode $mod_dst"
 msgstr "chế độ không như mong chờ $mod_dst"
 
-#: git-submodule.sh:910
+#: git-submodule.sh:1086
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_src"
-msgstr "  Cảnh báo: $name không chứa lần chuyển giao (commit) $sha1_src"
+msgid "  Warn: $display_name doesn't contain commit $sha1_src"
+msgstr ""
+"  Cảnh báo: $display_name không chứa lần chuyển giao (commit) $sha1_src"
 
-#: git-submodule.sh:913
+#: git-submodule.sh:1089
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_dst"
-msgstr "  Cảnh báo: $name không chứa lần chuyển giao (commit) $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commit $sha1_dst"
+msgstr ""
+"  Cảnh báo: $display_name không chứa lần chuyển giao (commit) $sha1_dst"
 
-#: git-submodule.sh:916
+#: git-submodule.sh:1092
 #, sh-format
-msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commits $sha1_src and $sha1_dst"
 msgstr ""
-"  Cảnh báo: $name không chứa những lần chuyển giao (commit) $sha1_src và "
-"$sha1_dst"
+"  Cảnh báo: $display_name không chứa những lần chuyển giao (commit) "
+"$sha1_src và $sha1_dst"
 
-#: git-submodule.sh:941
+#: git-submodule.sh:1117
 msgid "blob"
 msgstr "blob"
 
-#: git-submodule.sh:979
+#: git-submodule.sh:1155
 msgid "Submodules changed but not updated:"
 msgstr "Những mô-đun-con đã bị thay đổi nhưng chưa được cập nhật:"
 
-#: git-submodule.sh:981
+#: git-submodule.sh:1157
 msgid "Submodule changes to be committed:"
 msgstr "Những mô-đun-con thay đổi đã được chuyển giao (commit):"
 
-#: git-submodule.sh:1129
+#: git-submodule.sh:1242
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "Gặp lỗi khi đệ quy vào trong đường dẫn mô-đun-con “$sm_path”"
+
+#: git-submodule.sh:1306
 #, sh-format
-msgid "Synchronizing submodule url for '$prefix$sm_path'"
-msgstr "Url Mô-đun-con đồng bộ hóa cho “$prefix$sm_path”"
+msgid "Synchronizing submodule url for '$displaypath'"
+msgstr "Url Mô-đun-con đồng bộ hóa cho “$displaypath”"
+
+#~ msgid "  (fix conflicts and then run \"git am --resolved\")"
+#~ msgstr "  (sửa các xung đột và sau đó chạy lệnh \"git am --resolved\")"
+
+#~ msgid "  (all conflicts fixed: run \"git commit\")"
+#~ msgstr "  (khi tất cả các xung đột đã sửa xong: chạy lệnh \"git commit\")"
+
+#~ msgid "more than %d trees given: '%s'"
+#~ msgstr "đã chỉ ra nhiều hơn %d cây (tree): “%s”"
+
+#~ msgid "You cannot combine --no-ff with --ff-only."
+#~ msgstr "Bạn không thể kết hợp --no-ff với --ff-only."
+
+#~ msgid ""
+#~ "submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+#~ "(use 'rm -rf' if you really want to remove it including all of its "
+#~ "history)"
+#~ msgstr ""
+#~ "mô-đun-con “%s” (hoặc cái nằm trong các mô-đun-con) dùng thư mục .git\n"
+#~ "(dùng “rm -rf” nếu bạn thực sự muốn gỡ bỏ nó cùng với tất cả lịch sử của "
+#~ "chúng)"
+
+#~ msgid ""
+#~ "'%s' has changes staged in the index\n"
+#~ "(use --cached to keep the file, or -f to force removal)"
+#~ msgstr ""
+#~ "“%s” có các thay đổi được lưu trạng thái trong bảng mục lục\n"
+#~ "(dùng tùy chọn --cached để giữ tập tin, hoặc -f để ép buộc gỡ bỏ)"
+
+#~ msgid "show commits where no parent comes before its children"
+#~ msgstr "hiển thị các lần chuyển giao nơi mà cha mẹ đến trước con của nó"
+
+#~ msgid "show the HEAD reference"
+#~ msgstr "hiển thị tham chiếu của HEAD"
+
+#~ msgid "Unable to fetch in submodule path '$prefix$sm_path'"
+#~ msgstr ""
+#~ "Không thể lấy về (fetch) trong đường dẫn mô-đun-con “$prefix$sm_path”"
+
+#~ msgid "Failed to recurse into submodule path '$prefix$sm_path'"
+#~ msgstr "Gặp lỗi khi đệ quy vào trong đường dẫn mô-đun-con “$prefix$sm_path”"
+
+#~ msgid "It took %.2f seconds to enumerate untracked files.  'status -uno'"
+#~ msgstr "Cần %.2f giây để đếm các tập tin chưa được theo vết.  “status -uno”"
+
+#~ msgid "may speed it up, but you have to be careful not to forget to add"
+#~ msgstr ""
+#~ "có thể làm nó nhanh lên, nhưng bạn phải cẩn trọng đừng quên thêm nó vào"
+
+#~ msgid "new files yourself (see 'git help status')."
+#~ msgstr "tập tin mới của chính bạn (xem “git help status”.."
+
+#~ msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
+#~ msgstr "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
+
+#~ msgid "See 'git help <command>' for more information on a specific command."
+#~ msgstr ""
+#~ "Chạy lệnh “git help <tên-lệnh>” để có thêm thông tin về lệnh được chỉ ra."
+
+#~ msgid "use any ref in .git/refs"
+#~ msgstr "sử dụng bất kỳ ref nào trong .git/refs"
+
+#~ msgid "use any tag in .git/refs/tags"
+#~ msgstr "sử dụng bất kỳ thẻ nào trong .git/refs/tags"
+
+#~ msgid "bad object %s"
+#~ msgstr "đối tượng sai %s"
+
+#~ msgid "bogus committer info %s"
+#~ msgstr "thông tin người chuyển giao không có thực %s"
 
 #~ msgid "can't fdopen 'show' output fd"
 #~ msgstr "không thể fdopen “show” (lệnh hiển thị) mô tả tập tin (fd) kết xuất"
index c48ae10d78595592fb6b44a2d5f338f588010423..b7b46f14f0d15f02e6c7e3166e860f89a4a9681f 100644 (file)
@@ -12,8 +12,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2013-03-05 12:36+0800\n"
-"PO-Revision-Date: 2013-03-05 13:07+0800\n"
+"POT-Creation-Date: 2013-08-06 14:10+0800\n"
+"PO-Revision-Date: 2013-08-06 14:45+0800\n"
 "Last-Translator: Jiang Xin <worldhello.net@gmail.com>\n"
 "Language-Team: GitHub <https://github.com/gotgit/git/>\n"
 "Language: zh_CN\n"
@@ -22,7 +22,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: advice.c:49
+#: advice.c:57
 #, c-format
 msgid "hint: %.*s\n"
 msgstr "提示:%.*s\n"
@@ -31,7 +31,7 @@ msgstr "提示:%.*s\n"
 #. * Message used both when 'git commit' fails and when
 #. * other commands doing a merge do.
 #.
-#: advice.c:79
+#: advice.c:87
 msgid ""
 "Fix them up in the work tree,\n"
 "and then use 'git add/rm <file>' as\n"
@@ -68,7 +68,7 @@ msgstr "格式"
 msgid "archive format"
 msgstr "归档格式"
 
-#: archive.c:324 builtin/log.c:1115
+#: archive.c:324 builtin/log.c:1173
 msgid "prefix"
 msgstr "前缀"
 
@@ -76,15 +76,15 @@ msgstr "前缀"
 msgid "prepend prefix to each pathname in the archive"
 msgstr "为归档中每个路径名加上前缀"
 
-#: archive.c:326 builtin/archive.c:91 builtin/blame.c:2366
-#: builtin/blame.c:2367 builtin/config.c:55 builtin/fast-export.c:653
-#: builtin/fast-export.c:655 builtin/grep.c:715 builtin/hash-object.c:77
-#: builtin/ls-files.c:497 builtin/ls-files.c:500 builtin/notes.c:536
-#: builtin/notes.c:693 builtin/read-tree.c:107 parse-options.h:149
+#: archive.c:326 builtin/archive.c:88 builtin/blame.c:2292
+#: builtin/blame.c:2293 builtin/config.c:56 builtin/fast-export.c:673
+#: builtin/fast-export.c:675 builtin/grep.c:714 builtin/hash-object.c:77
+#: builtin/ls-files.c:496 builtin/ls-files.c:499 builtin/notes.c:408
+#: builtin/notes.c:565 builtin/read-tree.c:108 parse-options.h:152
 msgid "file"
 msgstr "文件"
 
-#: archive.c:327 builtin/archive.c:92
+#: archive.c:327 builtin/archive.c:89
 msgid "write the archive to this file"
 msgstr "归档写入此文件"
 
@@ -112,19 +112,19 @@ msgstr "压缩效果更好"
 msgid "list supported archive formats"
 msgstr "列出支持的归档格式"
 
-#: archive.c:345 builtin/archive.c:93 builtin/clone.c:85
+#: archive.c:345 builtin/archive.c:90 builtin/clone.c:85
 msgid "repo"
 msgstr "版本库"
 
-#: archive.c:346 builtin/archive.c:94
+#: archive.c:346 builtin/archive.c:91
 msgid "retrieve the archive from remote repository <repo>"
 msgstr "从远程版本库(<版本库>)提取归档文件"
 
-#: archive.c:347 builtin/archive.c:95 builtin/notes.c:615
+#: archive.c:347 builtin/archive.c:92 builtin/notes.c:487
 msgid "command"
 msgstr "命令"
 
-#: archive.c:348 builtin/archive.c:96
+#: archive.c:348 builtin/archive.c:93
 msgid "path to the remote git-upload-archive command"
 msgstr "远程 git-upload-archive 命令的路径"
 
@@ -136,6 +136,126 @@ msgstr ""
 "负值模版在 git attributes 中被忽略\n"
 "当字符串确实要以感叹号开始时,使用 '\\!'。"
 
+#: branch.c:60
+#, c-format
+msgid "Not setting branch %s as its own upstream."
+msgstr "未设置分支 %s 作为它自己的上游。"
+
+#: branch.c:82
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s by rebasing."
+msgstr "分支 %1$s 设置为使用变基来跟踪来自 %3$s 的远程分支 %2$s。"
+
+#: branch.c:83
+#, c-format
+msgid "Branch %s set up to track remote branch %s from %s."
+msgstr "分支 %1$s 设置为跟踪来自 %3$s 的远程分支 %2$s。"
+
+#: branch.c:87
+#, c-format
+msgid "Branch %s set up to track local branch %s by rebasing."
+msgstr "分支 %s 设置为使用变基来跟踪本地分支 %s。"
+
+#: branch.c:88
+#, c-format
+msgid "Branch %s set up to track local branch %s."
+msgstr "分支 %s 设置为跟踪本地分支 %s。"
+
+#: branch.c:92
+#, c-format
+msgid "Branch %s set up to track remote ref %s by rebasing."
+msgstr "分支 %s 设置为使用变基来跟踪远程引用 %s。"
+
+#: branch.c:93
+#, c-format
+msgid "Branch %s set up to track remote ref %s."
+msgstr "分支 %s 设置为跟踪远程引用 %s。"
+
+#: branch.c:97
+#, c-format
+msgid "Branch %s set up to track local ref %s by rebasing."
+msgstr "分支 %s 设置为使用变基来跟踪本地引用 %s。"
+
+#: branch.c:98
+#, c-format
+msgid "Branch %s set up to track local ref %s."
+msgstr "分支 %s 设置为跟踪本地引用 %s。"
+
+#: branch.c:118
+#, c-format
+msgid "Tracking not set up: name too long: %s"
+msgstr "跟踪未设置:名字太长:%s"
+
+#: branch.c:137
+#, c-format
+msgid "Not tracking: ambiguous information for ref %s"
+msgstr "未跟踪:引用 %s 有歧义"
+
+#: branch.c:182
+#, c-format
+msgid "'%s' is not a valid branch name."
+msgstr "'%s' 不是一个有效的分支名称。"
+
+#: branch.c:187
+#, c-format
+msgid "A branch named '%s' already exists."
+msgstr "一个分支名 '%s' 已经存在。"
+
+#: branch.c:195
+msgid "Cannot force update the current branch."
+msgstr "无法强制更新当前分支。"
+
+#: branch.c:216
+#, c-format
+msgid "Cannot setup tracking information; starting point '%s' is not a branch."
+msgstr "无法设置跟踪信息;起始点 '%s' 不是一个分支。"
+
+#: branch.c:218
+#, c-format
+msgid "the requested upstream branch '%s' does not exist"
+msgstr "请求的上游分支 '%s' 不存在"
+
+#: branch.c:220
+msgid ""
+"\n"
+"If you are planning on basing your work on an upstream\n"
+"branch that already exists at the remote, you may need to\n"
+"run \"git fetch\" to retrieve it.\n"
+"\n"
+"If you are planning to push out a new local branch that\n"
+"will track its remote counterpart, you may want to use\n"
+"\"git push -u\" to set the upstream config as you push."
+msgstr ""
+"\n"
+"如果您正计划基于远程一个现存的上游分支开始你的工作,\n"
+"您可能需要执行 \"git fetch\" 来获取分支。\n"
+"\n"
+"如果您正计划推送一个能与对应远程分支建立跟踪的新的本地分支,\n"
+"您可能需要使用 \"git push -u\" 推送分支并配置和上游的关联。"
+
+#: branch.c:265
+#, c-format
+msgid "Not a valid object name: '%s'."
+msgstr "不是一个有效的对象名:'%s'。"
+
+#: branch.c:285
+#, c-format
+msgid "Ambiguous object name: '%s'."
+msgstr "歧义的对象名:'%s'。"
+
+#: branch.c:290
+#, c-format
+msgid "Not a valid branch point: '%s'."
+msgstr "无效的分支点:'%s'。"
+
+#: branch.c:296
+msgid "Failed to lock ref for update"
+msgstr "无法为更新锁定引用"
+
+#: branch.c:314
+msgid "Failed to write ref"
+msgstr "不能写引用"
+
 #: bundle.c:36
 #, c-format
 msgid "'%s' does not look like a v2 bundle file"
@@ -146,7 +266,7 @@ msgstr "'%s' 不像是一个 v2 版本的包文件"
 msgid "unrecognized header: %s%s (%d)"
 msgstr "未能识别的包头:%s%s (%d)"
 
-#: bundle.c:89 builtin/commit.c:674
+#: bundle.c:89 builtin/commit.c:697
 #, c-format
 msgid "could not open '%s'"
 msgstr "不能打开 '%s'"
@@ -155,35 +275,35 @@ msgstr "不能打开 '%s'"
 msgid "Repository lacks these prerequisite commits:"
 msgstr "版本库缺少这些必备的提交:"
 
-#: bundle.c:164 sequencer.c:566 sequencer.c:998 builtin/log.c:299
-#: builtin/log.c:751 builtin/log.c:1358 builtin/log.c:1574 builtin/merge.c:347
-#: builtin/shortlog.c:157
+#: bundle.c:164 sequencer.c:661 sequencer.c:1111 builtin/log.c:331
+#: builtin/log.c:801 builtin/log.c:1399 builtin/log.c:1625 builtin/merge.c:364
+#: builtin/shortlog.c:156
 msgid "revision walk setup failed"
 msgstr "版本遍历设置失败"
 
 #: bundle.c:186
 #, c-format
-msgid "The bundle contains %d ref"
-msgid_plural "The bundle contains %d refs"
-msgstr[0] "这个包中含有 %d 个引用"
-msgstr[1] "这个包中含有 %d 个引用"
+msgid "The bundle contains this ref:"
+msgid_plural "The bundle contains these %d refs:"
+msgstr[0] "这个包中含有这个引用:"
+msgstr[1] "这个包中含有 %d 个引用"
 
-#: bundle.c:192
+#: bundle.c:193
 msgid "The bundle records a complete history."
 msgstr "这个包记录一个完整历史。"
 
 #: bundle.c:195
 #, c-format
-msgid "The bundle requires this ref"
-msgid_plural "The bundle requires these %d refs"
-msgstr[0] "这个包需要这个引用"
-msgstr[1] "这个包需要 %d 个这些引用"
+msgid "The bundle requires this ref:"
+msgid_plural "The bundle requires these %d refs:"
+msgstr[0] "这个包需要这个引用"
+msgstr[1] "这个包需要 %d 个引用:"
 
 #: bundle.c:294
 msgid "rev-list died"
 msgstr "rev-list 终止"
 
-#: bundle.c:300 builtin/log.c:1254 builtin/shortlog.c:260
+#: bundle.c:300 builtin/log.c:1310 builtin/shortlog.c:259
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "未能识别的参数:%s"
@@ -214,12 +334,12 @@ msgstr "不能创建 '%s'"
 msgid "index-pack died"
 msgstr "index-pack 终止"
 
-#: commit.c:50
+#: commit.c:53
 #, c-format
 msgid "could not parse %s"
 msgstr "不能解析 %s"
 
-#: commit.c:52
+#: commit.c:55
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s 不是一个提交!"
@@ -228,16 +348,16 @@ msgstr "%s %s 不是一个提交!"
 msgid "memory exhausted"
 msgstr "内存耗尽"
 
-#: connected.c:39
+#: connected.c:60
 msgid "Could not run 'git rev-list'"
 msgstr "不能执行 'git rev-list'"
 
-#: connected.c:48
+#: connected.c:80
 #, c-format
 msgid "failed write to rev-list: %s"
 msgstr "无法写入 rev-list:%s"
 
-#: connected.c:56
+#: connected.c:88
 #, c-format
 msgid "failed to close rev-list's stdin: %s"
 msgstr "无法关闭 rev-list 的标准输入:%s"
@@ -335,7 +455,7 @@ msgstr ""
 "发现配置变量 'diff.dirstat' 中的错误:\n"
 "%s"
 
-#: diff.c:3468
+#: diff.c:3487
 #, c-format
 msgid ""
 "Failed to parse --dirstat/-X option parameter:\n"
@@ -344,12 +464,12 @@ msgstr ""
 "无法解析 --dirstat/-X 选项的参数:\n"
 "%s"
 
-#: diff.c:3482
+#: diff.c:3501
 #, c-format
 msgid "Failed to parse --submodule option parameter: '%s'"
 msgstr "无法解析 --submodule 选项的参数:'%s'"
 
-#: gpg-interface.c:59 gpg-interface.c:127
+#: gpg-interface.c:59 gpg-interface.c:131
 msgid "could not run gpg."
 msgstr "不能执行 gpg。"
 
@@ -361,45 +481,45 @@ msgstr "gpg 没有接受数据"
 msgid "gpg failed to sign the data"
 msgstr "gpg 无法为数据签名"
 
-#: gpg-interface.c:112
+#: gpg-interface.c:115
 #, c-format
 msgid "could not create temporary file '%s': %s"
 msgstr "不能创建临时文件 '%s':%s"
 
-#: gpg-interface.c:115
+#: gpg-interface.c:118
 #, c-format
 msgid "failed writing detached signature to '%s': %s"
 msgstr "无法将分离式签名写入 '%s':%s"
 
-#: grep.c:1622
+#: grep.c:1623
 #, c-format
 msgid "'%s': unable to read %s"
 msgstr "'%s':无法读取 %s"
 
-#: grep.c:1639
+#: grep.c:1640
 #, c-format
 msgid "'%s': %s"
 msgstr "'%s':%s"
 
-#: grep.c:1650
+#: grep.c:1651
 #, c-format
 msgid "'%s': short read %s"
 msgstr "'%s':读取不完整 %s"
 
-#: help.c:212
+#: help.c:210
 #, c-format
 msgid "available git commands in '%s'"
 msgstr "在 '%s' 下可用的 git 命令"
 
-#: help.c:219
+#: help.c:217
 msgid "git commands available from elsewhere on your $PATH"
 msgstr "在 $PATH 路径中的其他地方可用的 git 命令"
 
-#: help.c:235
+#: help.c:233
 msgid "The most commonly used git commands are:"
 msgstr "最常用的 git 命令有:"
 
-#: help.c:292
+#: help.c:290
 #, c-format
 msgid ""
 "'%s' appears to be a git command, but we were not\n"
@@ -408,11 +528,11 @@ msgstr ""
 "'%s' 像是一个 git 命令,但却无法运行。\n"
 "可能是 git-%s 受损?"
 
-#: help.c:349
+#: help.c:347
 msgid "Uh oh. Your system reports no Git commands at all."
 msgstr "唉呀,您的系统中未发现 Git 命令。"
 
-#: help.c:371
+#: help.c:369
 #, c-format
 msgid ""
 "WARNING: You called a Git command named '%s', which does not exist.\n"
@@ -421,17 +541,17 @@ msgstr ""
 "警告:您运行一个不存在的 Git 命令 '%s'。继续执行假定您要要运行的\n"
 "是 '%s'"
 
-#: help.c:376
+#: help.c:374
 #, c-format
 msgid "in %0.1f seconds automatically..."
 msgstr "在 %0.1f 秒钟后自动运行..."
 
-#: help.c:383
+#: help.c:381
 #, c-format
 msgid "git: '%s' is not a git command. See 'git --help'."
 msgstr "git:'%s' 不是一个 git 命令。参见 'git --help'。"
 
-#: help.c:387
+#: help.c:385 help.c:444
 msgid ""
 "\n"
 "Did you mean this?"
@@ -445,12 +565,17 @@ msgstr[1] ""
 "\n"
 "您指的是这其中的某一个么?"
 
+#: help.c:440
+#, c-format
+msgid "%s: %s - %s"
+msgstr "%s:%s - %s"
+
 #: merge.c:56
 msgid "failed to read the cache"
 msgstr "无法读取缓存"
 
-#: merge.c:110 builtin/checkout.c:333 builtin/checkout.c:534
-#: builtin/clone.c:586
+#: merge.c:110 builtin/checkout.c:365 builtin/checkout.c:566
+#: builtin/clone.c:656
 msgid "unable to write new index file"
 msgstr "无法写新的索引文件"
 
@@ -464,70 +589,70 @@ msgstr "(坏提交)\n"
 msgid "addinfo_cache failed for path '%s'"
 msgstr "为路径 '%s' addinfo_cache 失败"
 
-#: merge-recursive.c:268
+#: merge-recursive.c:269
 msgid "error building trees"
 msgstr "无法创建树"
 
-#: merge-recursive.c:672
+#: merge-recursive.c:673
 #, c-format
 msgid "failed to create path '%s'%s"
 msgstr "无法创建路径 '%s'%s"
 
-#: merge-recursive.c:683
+#: merge-recursive.c:684
 #, c-format
 msgid "Removing %s to make room for subdirectory\n"
 msgstr "删除 %s 以便为子目录留出空间\n"
 
 #. something else exists
 #. .. but not some other error (who really cares what?)
-#: merge-recursive.c:697 merge-recursive.c:718
+#: merge-recursive.c:698 merge-recursive.c:719
 msgid ": perhaps a D/F conflict?"
 msgstr ":可能是一个目录/文件冲突?"
 
-#: merge-recursive.c:708
+#: merge-recursive.c:709
 #, c-format
 msgid "refusing to lose untracked file at '%s'"
 msgstr "拒绝丢弃 '%s' 中的未跟踪文件"
 
-#: merge-recursive.c:748
+#: merge-recursive.c:749
 #, c-format
 msgid "cannot read object %s '%s'"
 msgstr "不能读取对象 %s '%s'"
 
-#: merge-recursive.c:750
+#: merge-recursive.c:751
 #, c-format
 msgid "blob expected for %s '%s'"
 msgstr "%s '%s' 应为数据(blob)对象"
 
-#: merge-recursive.c:773 builtin/clone.c:302
+#: merge-recursive.c:774 builtin/clone.c:312
 #, c-format
 msgid "failed to open '%s'"
 msgstr "无法打开 '%s'"
 
-#: merge-recursive.c:781
+#: merge-recursive.c:782
 #, c-format
 msgid "failed to symlink '%s'"
 msgstr "无法创建符号链接 '%s'"
 
-#: merge-recursive.c:784
+#: merge-recursive.c:785
 #, c-format
 msgid "do not know what to do with %06o %s '%s'"
 msgstr "不知道如何处理 %06o %s '%s'"
 
-#: merge-recursive.c:922
+#: merge-recursive.c:923
 msgid "Failed to execute internal merge"
 msgstr "无法执行内部合并"
 
-#: merge-recursive.c:926
+#: merge-recursive.c:927
 #, c-format
 msgid "Unable to add %s to database"
 msgstr "不能添加 %s 至对象库"
 
-#: merge-recursive.c:942
+#: merge-recursive.c:943
 msgid "unsupported object type in the tree"
 msgstr "在树中有不支持的对象类型"
 
-#: merge-recursive.c:1021 merge-recursive.c:1035
+#: merge-recursive.c:1022 merge-recursive.c:1036
 #, c-format
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
@@ -536,7 +661,7 @@ msgstr ""
 "冲突(%1$s/删除):%2$s 在 %3$s 中被删除,在 %5$s 中被 %4$s。%7$s 在 %6$s 中"
 "的版本被保留。"
 
-#: merge-recursive.c:1027 merge-recursive.c:1040
+#: merge-recursive.c:1028 merge-recursive.c:1041
 #, c-format
 msgid ""
 "CONFLICT (%s/delete): %s deleted in %s and %s in %s. Version %s of %s left "
@@ -545,20 +670,20 @@ msgstr ""
 "冲突(%1$s/删除):%2$s 在 %3$s 中被删除,在 %5$s 中被 %4$s。%7$s 在 %6$s 中"
 "的版本保留于 %8$s 中。"
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "rename"
 msgstr "重命名"
 
-#: merge-recursive.c:1081
+#: merge-recursive.c:1082
 msgid "renamed"
 msgstr "重命名"
 
-#: merge-recursive.c:1137
+#: merge-recursive.c:1138
 #, c-format
 msgid "%s is a directory in %s adding as %s instead"
 msgstr "%s 是 %s 中的一个目录而以 %s 为名被添加"
 
-#: merge-recursive.c:1159
+#: merge-recursive.c:1160
 #, c-format
 msgid ""
 "CONFLICT (rename/rename): Rename \"%s\"->\"%s\" in branch \"%s\" rename \"%s"
@@ -567,147 +692,173 @@ msgstr ""
 "冲突(重命名/重命名):在分支 \"%3$s\" 中重命名 \"%1$s\"->\"%2$s\",在分支 "
 "\"%6$s\" 中重命名 \"%4$s\"->\"%5$s\"%7$s"
 
-#: merge-recursive.c:1164
+#: merge-recursive.c:1165
 msgid " (left unresolved)"
 msgstr "(留下未解决)"
 
-#: merge-recursive.c:1218
+#: merge-recursive.c:1219
 #, c-format
 msgid "CONFLICT (rename/rename): Rename %s->%s in %s. Rename %s->%s in %s"
 msgstr ""
 "冲突(重命名/重命名):在 %3$s 中重命名 %1$s->%2$s,在 %6$s 中重命名 %4$s->"
 "%5$s"
 
-#: merge-recursive.c:1248
+#: merge-recursive.c:1249
 #, c-format
 msgid "Renaming %s to %s and %s to %s instead"
 msgstr "而是重命名 %s 至 %s,以及 %s 至 %s"
 
-#: merge-recursive.c:1447
+#: merge-recursive.c:1448
 #, c-format
 msgid "CONFLICT (rename/add): Rename %s->%s in %s. %s added in %s"
 msgstr "冲突(重命名/添加):在 %3$s 中重命名 %1$s->%2$s。在 %5$s 中添加 %4$s"
 
-#: merge-recursive.c:1457
+#: merge-recursive.c:1458
 #, c-format
 msgid "Adding merged %s"
 msgstr "添加合并后的 %s"
 
-#: merge-recursive.c:1462 merge-recursive.c:1660
+#: merge-recursive.c:1463 merge-recursive.c:1661
 #, c-format
 msgid "Adding as %s instead"
 msgstr "而是以 %s 为名添加"
 
-#: merge-recursive.c:1513
+#: merge-recursive.c:1514
 #, c-format
 msgid "cannot read object %s"
 msgstr "不能读取对象 %s"
 
-#: merge-recursive.c:1516
+#: merge-recursive.c:1517
 #, c-format
 msgid "object %s is not a blob"
 msgstr "对象 %s 不是一个数据(blob)对象"
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modify"
 msgstr "修改"
 
-#: merge-recursive.c:1564
+#: merge-recursive.c:1565
 msgid "modified"
 msgstr "修改"
 
-#: merge-recursive.c:1574
+#: merge-recursive.c:1575
 msgid "content"
 msgstr "内容"
 
-#: merge-recursive.c:1581
+#: merge-recursive.c:1582
 msgid "add/add"
 msgstr "添加/添加"
 
-#: merge-recursive.c:1615
+#: merge-recursive.c:1616
 #, c-format
 msgid "Skipped %s (merged same as existing)"
 msgstr "略过 %s(已经做过相同合并)"
 
-#: merge-recursive.c:1629
+#: merge-recursive.c:1630
 #, c-format
 msgid "Auto-merging %s"
 msgstr "自动合并 %s"
 
-#: merge-recursive.c:1633 git-submodule.sh:942
+#: merge-recursive.c:1634 git-submodule.sh:1118
 msgid "submodule"
 msgstr "子模组"
 
-#: merge-recursive.c:1634
+#: merge-recursive.c:1635
 #, c-format
 msgid "CONFLICT (%s): Merge conflict in %s"
 msgstr "冲突(%s):合并冲突于 %s"
 
-#: merge-recursive.c:1724
+#: merge-recursive.c:1725
 #, c-format
 msgid "Removing %s"
 msgstr "删除 %s"
 
-#: merge-recursive.c:1749
+#: merge-recursive.c:1750
 msgid "file/directory"
 msgstr "文件/目录"
 
-#: merge-recursive.c:1755
+#: merge-recursive.c:1756
 msgid "directory/file"
 msgstr "目录/文件"
 
-#: merge-recursive.c:1760
+#: merge-recursive.c:1761
 #, c-format
 msgid "CONFLICT (%s): There is a directory with name %s in %s. Adding %s as %s"
 msgstr "冲突(%1$s):在 %3$s 中有一个名为 %2$s 的目录。以 %5$s 为名添加 %4$s"
 
-#: merge-recursive.c:1770
+#: merge-recursive.c:1771
 #, c-format
 msgid "Adding %s"
 msgstr "添加 %s"
 
-#: merge-recursive.c:1787
+#: merge-recursive.c:1788
 msgid "Fatal merge failure, shouldn't happen."
 msgstr "严重的合并错误,不应发生。"
 
-#: merge-recursive.c:1806
+#: merge-recursive.c:1807
 msgid "Already up-to-date!"
 msgstr "已经是最新的!"
 
-#: merge-recursive.c:1815
+#: merge-recursive.c:1816
 #, c-format
 msgid "merging of trees %s and %s failed"
 msgstr "无法合并树 %s 和 %s"
 
-#: merge-recursive.c:1845
+#: merge-recursive.c:1846
 #, c-format
 msgid "Unprocessed path??? %s"
 msgstr "未处理的路径??? %s"
 
-#: merge-recursive.c:1890
+#: merge-recursive.c:1891
 msgid "Merging:"
 msgstr "合并:"
 
-#: merge-recursive.c:1903
+#: merge-recursive.c:1904
 #, c-format
 msgid "found %u common ancestor:"
 msgid_plural "found %u common ancestors:"
 msgstr[0] "发现 %u 个共同祖先:"
 msgstr[1] "发现 %u 个共同祖先:"
 
-#: merge-recursive.c:1940
+#: merge-recursive.c:1941
 msgid "merge returned no commit"
 msgstr "合并未返回提交"
 
-#: merge-recursive.c:1997
+#: merge-recursive.c:1998
 #, c-format
 msgid "Could not parse object '%s'"
 msgstr "不能解析对象 '%s'"
 
-#: merge-recursive.c:2009 builtin/merge.c:643
+#: merge-recursive.c:2010 builtin/merge.c:672
 msgid "Unable to write index."
 msgstr "不能写入索引。"
 
+#: notes-utils.c:40
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "不能提交未初始化/未引用的注解树"
+
+#: notes-utils.c:81
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "坏的 notes.rewriteMode 值:'%s'"
+
+#: notes-utils.c:91
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr "拒绝向 %s(在 refs/notes/ 之外)写入注解"
+
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: notes-utils.c:118
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "坏的 %s 值:'%s'"
+
+#: object.c:204
+#, c-format
+msgid "unable to parse object: %s"
+msgstr "不能解析对象:%s"
+
 #: parse-options.c:489
 msgid "..."
 msgstr "..."
@@ -744,18 +895,18 @@ msgstr "路径 '%s' 属于模组 '%.*s'"
 msgid "'%s' is beyond a symbolic link"
 msgstr "'%s' 位于符号链接中"
 
-#: remote.c:1653
+#: remote.c:1788
 #, 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] "您的分支领先 '%s' 共 %d 个提交。\n"
 msgstr[1] "您的分支领先 '%s' 共 %d 个提交。\n"
 
-#: remote.c:1659
+#: remote.c:1794
 msgid "  (use \"git push\" to publish your local commits)\n"
 msgstr "  (使用 \"git push\" 来发布您的本地提交)\n"
 
-#: remote.c:1662
+#: remote.c:1797
 #, c-format
 msgid "Your branch is behind '%s' by %d commit, and can be fast-forwarded.\n"
 msgid_plural ""
@@ -764,11 +915,11 @@ msgstr[0] "您的分支落后 '%s' 共 %d 个提交,并且可以快进。\n"
 msgstr[1] "您的分支落后 '%s' 共 %d 个提交,并且可以快进。\n"
 
 #  译者:注意保持前导空格
-#: remote.c:1670
+#: remote.c:1805
 msgid "  (use \"git pull\" to update your local branch)\n"
 msgstr "  (使用 \"git pull\" 来更新您的本地分支)\n"
 
-#: remote.c:1673
+#: remote.c:1808
 #, c-format
 msgid ""
 "Your branch and '%s' have diverged,\n"
@@ -784,23 +935,32 @@ msgstr[1] ""
 "并且分别有 %d 和 %d 处不同的提交。\n"
 
 #  译者:注意保持前导空格
-#: remote.c:1683
+#: remote.c:1818
 msgid "  (use \"git pull\" to merge the remote branch into yours)\n"
 msgstr "  (使用 \"git pull\" 来合并远程分支)\n"
 
-#: sequencer.c:123 builtin/merge.c:761 builtin/merge.c:874 builtin/merge.c:984
-#: builtin/merge.c:994
+#: run-command.c:80
+msgid "open /dev/null failed"
+msgstr "不能打开 /dev/null"
+
+#: run-command.c:82
+#, c-format
+msgid "dup2(%d,%d) failed"
+msgstr "不能调用 dup2(%d,%d)"
+
+#: sequencer.c:206 builtin/merge.c:790 builtin/merge.c:903
+#: builtin/merge.c:1013 builtin/merge.c:1023
 #, c-format
 msgid "Could not open '%s' for writing"
 msgstr "不能为写入打开 '%s'"
 
-#: sequencer.c:125 builtin/merge.c:333 builtin/merge.c:764 builtin/merge.c:986
-#: builtin/merge.c:999
+#: sequencer.c:208 builtin/merge.c:350 builtin/merge.c:793
+#: builtin/merge.c:1015 builtin/merge.c:1028
 #, c-format
 msgid "Could not write to '%s'"
 msgstr "不能写入 '%s'"
 
-#: sequencer.c:146
+#: sequencer.c:229
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'"
@@ -808,7 +968,7 @@ msgstr ""
 "冲突解决完毕后,用 'git add <paths>' 或 'git rm <paths>'\n"
 "命令标记修正后的文件"
 
-#: sequencer.c:149
+#: sequencer.c:232
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'\n"
@@ -817,228 +977,259 @@ msgstr ""
 "冲突解决完毕后,用 'git add <paths>' 或 'git rm <paths>'\n"
 "对修正后的文件做标记,然后用 'git commit' 提交"
 
-#: sequencer.c:162 sequencer.c:774 sequencer.c:857
+#: sequencer.c:245 sequencer.c:869 sequencer.c:952
 #, c-format
 msgid "Could not write to %s"
 msgstr "不能写入 %s"
 
-#: sequencer.c:165
+#: sequencer.c:248
 #, c-format
 msgid "Error wrapping up %s"
 msgstr "错误收尾 %s"
 
-#: sequencer.c:180
+#: sequencer.c:263
 msgid "Your local changes would be overwritten by cherry-pick."
 msgstr "您的本地修改将被拣选操作覆盖。"
 
-#: sequencer.c:182
+#: sequencer.c:265
 msgid "Your local changes would be overwritten by revert."
 msgstr "您的本地修改将被还原操作覆盖。"
 
-#: sequencer.c:185
+#: sequencer.c:268
 msgid "Commit your changes or stash them to proceed."
 msgstr "提交您的修改或保存进度后再继续。"
 
 #. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:236
+#: sequencer.c:324
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr "%s:无法写入新索引文件"
 
-#: sequencer.c:267
+#: sequencer.c:355
 msgid "Could not resolve HEAD commit\n"
 msgstr "不能解析 HEAD 提交\n"
 
-#: sequencer.c:288
+#: sequencer.c:377
 msgid "Unable to update cache tree\n"
 msgstr "不能更新缓存\n"
 
-#: sequencer.c:333
+#: sequencer.c:422
 #, c-format
 msgid "Could not parse commit %s\n"
 msgstr "不能解析提交 %s\n"
 
-#: sequencer.c:338
+#: sequencer.c:427
 #, c-format
 msgid "Could not parse parent commit %s\n"
 msgstr "不能解析父提交 %s\n"
 
-#: sequencer.c:404
+#: sequencer.c:493
 msgid "Your index file is unmerged."
 msgstr "您的索引文件未完成合并。"
 
-#: sequencer.c:423
+#: sequencer.c:512
 #, c-format
 msgid "Commit %s is a merge but no -m option was given."
 msgstr "提交 %s 是一个合并提交但未提供 -m 选项。"
 
-#: sequencer.c:431
+#: sequencer.c:520
 #, c-format
 msgid "Commit %s does not have parent %d"
 msgstr "提交 %s 没有父提交 %d"
 
-#: sequencer.c:435
+#: sequencer.c:524
 #, c-format
 msgid "Mainline was specified but commit %s is not a merge."
 msgstr "指定了主线但提交 %s 不是一个合并。"
 
 #. TRANSLATORS: The first %s will be "revert" or
 #. "cherry-pick", the second %s a SHA1
-#: sequencer.c:448
+#: sequencer.c:537
 #, c-format
 msgid "%s: cannot parse parent commit %s"
 msgstr "%s:不能解析父提交 %s"
 
-#: sequencer.c:452
+#: sequencer.c:541
 #, c-format
 msgid "Cannot get commit message for %s"
 msgstr "不能得到 %s 的提交说明"
 
-#: sequencer.c:536
+#: sequencer.c:627
 #, c-format
 msgid "could not revert %s... %s"
 msgstr "不能还原 %s... %s"
 
-#: sequencer.c:537
+#: sequencer.c:628
 #, c-format
 msgid "could not apply %s... %s"
 msgstr "不能应用 %s... %s"
 
-#: sequencer.c:569
+#: sequencer.c:664
 msgid "empty commit set passed"
 msgstr "提供了空的提交集"
 
-#: sequencer.c:577
+#: sequencer.c:672
 #, c-format
 msgid "git %s: failed to read the index"
 msgstr "git %s:无法读取索引"
 
-#: sequencer.c:582
+#: sequencer.c:677
 #, c-format
 msgid "git %s: failed to refresh the index"
 msgstr "git %s:无法刷新索引"
 
-#: sequencer.c:640
+#: sequencer.c:735
 #, c-format
 msgid "Cannot %s during a %s"
 msgstr "无法 %s 在一个 %s 过程中"
 
-#: sequencer.c:662
+#: sequencer.c:757
 #, c-format
 msgid "Could not parse line %d."
 msgstr "不能解析第 %d 行。"
 
-#: sequencer.c:667
+#: sequencer.c:762
 msgid "No commits parsed."
 msgstr "没有提交被解析。"
 
-#: sequencer.c:680
+#: sequencer.c:775
 #, c-format
 msgid "Could not open %s"
 msgstr "不能打开 %s"
 
-#: sequencer.c:684
+#: sequencer.c:779
 #, c-format
 msgid "Could not read %s."
 msgstr "不能读取 %s。"
 
-#: sequencer.c:691
+#: sequencer.c:786
 #, c-format
 msgid "Unusable instruction sheet: %s"
 msgstr "无用的指令表单:%s"
 
-#: sequencer.c:719
+#: sequencer.c:814
 #, c-format
 msgid "Invalid key: %s"
 msgstr "无效键名:%s"
 
-#: sequencer.c:722
+#: sequencer.c:817
 #, c-format
 msgid "Invalid value for %s: %s"
 msgstr "%s 的值无效:%s"
 
-#: sequencer.c:734
+#: sequencer.c:829
 #, c-format
 msgid "Malformed options sheet: %s"
 msgstr "非法的选项表单:%s"
 
-#: sequencer.c:755
+#: sequencer.c:850
 msgid "a cherry-pick or revert is already in progress"
 msgstr "一个拣选或还原操作已在进行"
 
-#: sequencer.c:756
+#: sequencer.c:851
 msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
 msgstr "尝试 \"git cherry-pick (--continue | --quit | --abort)\""
 
-#: sequencer.c:760
+#: sequencer.c:855
 #, c-format
 msgid "Could not create sequencer directory %s"
 msgstr "不能创建序列目录 %s"
 
-#: sequencer.c:776 sequencer.c:861
+#: sequencer.c:871 sequencer.c:956
 #, c-format
 msgid "Error wrapping up %s."
 msgstr "错误收尾 %s。"
 
-#: sequencer.c:795 sequencer.c:929
+#: sequencer.c:890 sequencer.c:1024
 msgid "no cherry-pick or revert in progress"
 msgstr "拣选或还原操作并未进行"
 
-#: sequencer.c:797
+#: sequencer.c:892
 msgid "cannot resolve HEAD"
 msgstr "不能解析 HEAD"
 
-#: sequencer.c:799
+#: sequencer.c:894
 msgid "cannot abort from a branch yet to be born"
 msgstr "不能从尚未建立的分支终止"
 
-#: sequencer.c:821 builtin/apply.c:4056
+#: sequencer.c:916 builtin/apply.c:4061
 #, c-format
 msgid "cannot open %s: %s"
 msgstr "不能打开 %s:%s"
 
-#: sequencer.c:824
+#: sequencer.c:919
 #, c-format
 msgid "cannot read %s: %s"
 msgstr "不能读取 %s:%s"
 
-#: sequencer.c:825
+#: sequencer.c:920
 msgid "unexpected end of file"
 msgstr "意外的文件结束"
 
-#: sequencer.c:831
+#: sequencer.c:926
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
 msgstr "保存拣选提交前的 HEAD 文件 '%s' 损坏"
 
-#: sequencer.c:854
+#: sequencer.c:949
 #, c-format
 msgid "Could not format %s."
 msgstr "不能格式化 %s。"
 
-#: sequencer.c:1016
+#: sequencer.c:1092
+#, c-format
+msgid "%s: can't cherry-pick a %s"
+msgstr "%s:不能拣选一个%s"
+
+#: sequencer.c:1095
+#, c-format
+msgid "%s: bad revision"
+msgstr "%s:错误的版本"
+
+#: sequencer.c:1129
 msgid "Can't revert as initial commit"
 msgstr "不能作为初始提交还原"
 
-#: sequencer.c:1017
+#: sequencer.c:1130
 msgid "Can't cherry-pick into empty head"
 msgstr "不能拣选到空分支"
 
-#: sha1_name.c:1044
+#: sha1_name.c:440
+msgid ""
+"Git normally never creates a ref that ends with 40 hex characters\n"
+"because it will be ignored when you just specify 40-hex. These refs\n"
+"may be created by mistake. For example,\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+"examine these refs and maybe delete them. Turn this message off by\n"
+"running \"git config advice.objectNameWarning false\""
+msgstr ""
+"Git 通常不会创建一个以40位十六进制字符命名的引用,因为当你提供40位\n"
+"十六进制字符时将被忽略。不过这些引用也可能被错误地创建。例如:\n"
+"\n"
+"  git checkout -b $br $(git rev-parse ...)\n"
+"\n"
+"当 \"$br\" 空白时一个40位十六进制的引用将被创建。请检查这些引用,\n"
+"可能需要删除它们。用 \"git config advice.objectNameWarning false\"\n"
+"命令关闭本消息通知。"
+
+#: sha1_name.c:1097
 msgid "HEAD does not point to a branch"
 msgstr "HEAD 没有指向一个分支"
 
-#: sha1_name.c:1047
+#: sha1_name.c:1100
 #, c-format
 msgid "No such branch: '%s'"
 msgstr "没有此分支:'%s'"
 
-#: sha1_name.c:1049
+#: sha1_name.c:1102
 #, c-format
 msgid "No upstream configured for branch '%s'"
 msgstr "尚未给分支 '%s' 设置上游"
 
-#: sha1_name.c:1052
+#: sha1_name.c:1106
 #, c-format
 msgid "Upstream branch '%s' not stored as a remote-tracking branch"
 msgstr "上游分支 '%s' 没有存储为一个远程跟踪分支"
@@ -1048,418 +1239,469 @@ msgstr "上游分支 '%s' 没有存储为一个远程跟踪分支"
 msgid "unable to access '%s': %s"
 msgstr "不能访问 '%s':%s"
 
-#: wrapper.c:423
+#: wrapper.c:429
 #, c-format
 msgid "unable to access '%s'"
 msgstr "不能访问 '%s'"
 
-#: wrapper.c:434
+#: wrapper.c:440
 #, c-format
 msgid "unable to look up current user in the passwd file: %s"
 msgstr "无法在口令文件中查询到当前用户:%s"
 
-#: wrapper.c:435
+#: wrapper.c:441
 msgid "no such user"
 msgstr "无此用户"
 
-#: wt-status.c:140
+#: wt-status.c:141
 msgid "Unmerged paths:"
 msgstr "未合并的路径:"
 
 #  译者:注意保持前导空格
-#: wt-status.c:167 wt-status.c:194
+#: wt-status.c:168 wt-status.c:195
 #, c-format
 msgid "  (use \"git reset %s <file>...\" to unstage)"
 msgstr "  (使用 \"git reset %s <file>...\" 撤出暂存区)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:169 wt-status.c:196
+#: wt-status.c:170 wt-status.c:197
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
 msgstr "  (使用 \"git rm --cached <file>...\" 撤出暂存区)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:173
+#: wt-status.c:174
 msgid "  (use \"git add <file>...\" to mark resolution)"
 msgstr "  (使用 \"git add <file>...\" 标记解决方案)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:175 wt-status.c:179
+#: wt-status.c:176 wt-status.c:180
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
 msgstr "  (酌情使用 \"git add/rm <file>...\" 标记解决方案)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:177
+#: wt-status.c:178
 msgid "  (use \"git rm <file>...\" to mark resolution)"
 msgstr "  (使用 \"git rm <file>...\" 标记解决方案)"
 
-#: wt-status.c:188
+#: wt-status.c:189
 msgid "Changes to be committed:"
 msgstr "要提交的变更:"
 
-#: wt-status.c:206
+#: wt-status.c:207
 msgid "Changes not staged for commit:"
 msgstr "尚未暂存以备提交的变更:"
 
 #  译者:注意保持前导空格
-#: wt-status.c:210
+#: wt-status.c:211
 msgid "  (use \"git add <file>...\" to update what will be committed)"
 msgstr "  (使用 \"git add <file>...\" 更新要提交的内容)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:212
+#: wt-status.c:213
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
 msgstr "  (使用 \"git add/rm <file>...\" 更新要提交的内容)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:213
+#: wt-status.c:214
 msgid ""
 "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
 msgstr "  (使用 \"git checkout -- <file>...\" 丢弃工作区的改动)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:215
+#: wt-status.c:216
 msgid "  (commit or discard the untracked or modified content in submodules)"
 msgstr "  (提交或丢弃子模组中未跟踪或修改的内容)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:227
+#: wt-status.c:228
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
 msgstr "  (使用 \"git %s <file>...\" 以包含要提交的内容)"
 
-#: wt-status.c:244
+#: wt-status.c:245
 msgid "bug"
 msgstr "bug"
 
-#: wt-status.c:249
+#: wt-status.c:250
 msgid "both deleted:"
 msgstr "双方删除:"
 
-#: wt-status.c:250
+#: wt-status.c:251
 msgid "added by us:"
 msgstr "由我们添加:"
 
-#: wt-status.c:251
+#: wt-status.c:252
 msgid "deleted by them:"
 msgstr "由他们删除:"
 
-#: wt-status.c:252
+#: wt-status.c:253
 msgid "added by them:"
 msgstr "由他们添加:"
 
-#: wt-status.c:253
+#: wt-status.c:254
 msgid "deleted by us:"
 msgstr "由我们删除:"
 
-#: wt-status.c:254
+#: wt-status.c:255
 msgid "both added:"
 msgstr "双方添加:"
 
-#: wt-status.c:255
+#: wt-status.c:256
 msgid "both modified:"
 msgstr "双方修改:"
 
 #  译者:末尾两个字节可能被删减,如果翻译为中文标点会出现半个汉字
-#: wt-status.c:285
+#: wt-status.c:286
 msgid "new commits, "
 msgstr "新提交, "
 
 #  译者:末尾两个字节可能被删减,如果翻译为中文标点会出现半个汉字
-#: wt-status.c:287
+#: wt-status.c:288
 msgid "modified content, "
 msgstr "修改的内容, "
 
 #  译者:末尾两个字节可能被删减,如果翻译为中文标点会出现半个汉字
-#: wt-status.c:289
+#: wt-status.c:290
 msgid "untracked content, "
 msgstr "未跟踪的内容, "
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: wt-status.c:303
+#: wt-status.c:307
 #, c-format
 msgid "new file:   %s"
 msgstr "新文件:    %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: wt-status.c:306
+#: wt-status.c:310
 #, c-format
 msgid "copied:     %s -> %s"
 msgstr "拷贝:      %s -> %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: wt-status.c:309
+#: wt-status.c:313
 #, c-format
 msgid "deleted:    %s"
 msgstr "删除:      %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: wt-status.c:312
+#: wt-status.c:316
 #, c-format
 msgid "modified:   %s"
 msgstr "修改:      %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: wt-status.c:315
+#: wt-status.c:319
 #, c-format
 msgid "renamed:    %s -> %s"
 msgstr "重命名:    %s -> %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: wt-status.c:318
+#: wt-status.c:322
 #, c-format
 msgid "typechange: %s"
 msgstr "类型变更:  %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: wt-status.c:321
+#: wt-status.c:325
 #, c-format
 msgid "unknown:    %s"
 msgstr "未知:      %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: wt-status.c:324
+#: wt-status.c:328
 #, c-format
 msgid "unmerged:   %s"
 msgstr "未合并:    %s"
 
-#: wt-status.c:327
+#: wt-status.c:331
 #, c-format
 msgid "bug: unhandled diff status %c"
 msgstr "bug:未处理的差异状态 %c"
 
-#: wt-status.c:789
+#: wt-status.c:803
 msgid "You have unmerged paths."
 msgstr "您有尚未合并的路径。"
 
 #  译者:注意保持前导空格
-#: wt-status.c:792 wt-status.c:944
+#: wt-status.c:806
 msgid "  (fix conflicts and run \"git commit\")"
 msgstr "  (解决冲突并运行 \"git commit\")"
 
-#: wt-status.c:795
+#: wt-status.c:809
 msgid "All conflicts fixed but you are still merging."
 msgstr "所有冲突已解决但您仍处于合并中。"
 
 #  译者:注意保持前导空格
-#: wt-status.c:798
+#: wt-status.c:812
 msgid "  (use \"git commit\" to conclude merge)"
 msgstr "  (使用 \"git commit\" 结束合并)"
 
-#: wt-status.c:808
+#: wt-status.c:822
 msgid "You are in the middle of an am session."
 msgstr "您正处于一个 am 过程中。"
 
-#: wt-status.c:811
+#: wt-status.c:825
 msgid "The current patch is empty."
 msgstr "当前的补丁为空。"
 
 #  译者:注意保持前导空格
-#: wt-status.c:815
-msgid "  (fix conflicts and then run \"git am --resolved\")"
-msgstr "  (解决冲突,然后运行 \"git am --resolved\")"
+#: wt-status.c:829
+msgid "  (fix conflicts and then run \"git am --continue\")"
+msgstr "  (解决冲突,然后运行 \"git am --continue\")"
 
 #  译者:注意保持前导空格
-#: wt-status.c:817
+#: wt-status.c:831
 msgid "  (use \"git am --skip\" to skip this patch)"
 msgstr "  (使用 \"git am --skip\" 跳过此补丁)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:819
+#: wt-status.c:833
 msgid "  (use \"git am --abort\" to restore the original branch)"
 msgstr "  (使用 \"git am --abort\" 恢复原有分支)"
 
-#: wt-status.c:879 wt-status.c:896
+#: wt-status.c:893 wt-status.c:910
 #, c-format
 msgid "You are currently rebasing branch '%s' on '%s'."
 msgstr "您正在将分支 '%s' 变基到 '%s'。"
 
-#: wt-status.c:884 wt-status.c:901
+#: wt-status.c:898 wt-status.c:915
 msgid "You are currently rebasing."
 msgstr "您正在变基。"
 
 #  译者:注意保持前导空格
-#: wt-status.c:887
+#: wt-status.c:901
 msgid "  (fix conflicts and then run \"git rebase --continue\")"
 msgstr "  (解决冲突,然后运行 \"git rebase --continue\")"
 
 #  译者:注意保持前导空格
-#: wt-status.c:889
+#: wt-status.c:903
 msgid "  (use \"git rebase --skip\" to skip this patch)"
 msgstr "  (使用 \"git rebase --skip\" 跳过此补丁)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:891
+#: wt-status.c:905
 msgid "  (use \"git rebase --abort\" to check out the original branch)"
 msgstr "  (使用 \"git rebase --abort\" 以检出原有分支)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:904
+#: wt-status.c:918
 msgid "  (all conflicts fixed: run \"git rebase --continue\")"
 msgstr "  (所有冲突已解决:运行 \"git rebase --continue\")"
 
-#: wt-status.c:908
+#: wt-status.c:922
 #, c-format
 msgid ""
 "You are currently splitting a commit while rebasing branch '%s' on '%s'."
 msgstr "您正在将分支 '%s' 变基到 '%s' 过程中拆分一个提交。"
 
-#: wt-status.c:913
+#: wt-status.c:927
 msgid "You are currently splitting a commit during a rebase."
 msgstr "您正在变基过程中拆分一个提交。"
 
 #  译者:注意保持前导空格
-#: wt-status.c:916
+#: wt-status.c:930
 msgid "  (Once your working directory is clean, run \"git rebase --continue\")"
 msgstr "  (一旦您工作目录提交干净后,运行 \"git rebase --continue\")"
 
-#: wt-status.c:920
+#: wt-status.c:934
 #, c-format
 msgid "You are currently editing a commit while rebasing branch '%s' on '%s'."
 msgstr "您正在将分支 '%s' 变基到 '%s' 过程中编辑一个提交。"
 
-#: wt-status.c:925
+#: wt-status.c:939
 msgid "You are currently editing a commit during a rebase."
 msgstr "您正在变基过程中编辑一个提交。"
 
 #  译者:注意保持前导空格
-#: wt-status.c:928
+#: wt-status.c:942
 msgid "  (use \"git commit --amend\" to amend the current commit)"
 msgstr "  (使用 \"git commit --amend\" 修补当前提交)"
 
 #  译者:注意保持前导空格
-#: wt-status.c:930
+#: wt-status.c:944
 msgid ""
 "  (use \"git rebase --continue\" once you are satisfied with your changes)"
 msgstr "  (当您对您的修改满意后执行 \"git rebase --continue\")"
 
-#: wt-status.c:940
+#: wt-status.c:954
 msgid "You are currently cherry-picking."
 msgstr "您正在做拣选操作。"
 
 #  译者:注意保持前导空格
-#: wt-status.c:947
-msgid "  (all conflicts fixed: run \"git commit\")"
-msgstr "  (解决所有冲突后,执行 \"git commit\")"
-
 #: wt-status.c:958
+msgid "  (fix conflicts and run \"git cherry-pick --continue\")"
+msgstr "  (解决冲突并运行 \"git cherry-pick --continue\")"
+
+#  译者:注意保持前导空格
+#: wt-status.c:961
+msgid "  (all conflicts fixed: run \"git cherry-pick --continue\")"
+msgstr "  (所有冲突已解决:运行 \"git cherry-pick --continue\")"
+
+#  译者:注意保持前导空格
+#: wt-status.c:963
+msgid "  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"
+msgstr "  (使用 \"git cherry-pick --abort\" 以取消拣选操作)"
+
+#: wt-status.c:972
 #, c-format
-msgid "You are currently bisecting branch '%s'."
-msgstr "您正在分支 '%s' 中做二分查找。"
+msgid "You are currently reverting commit %s."
+msgstr "您正在反转提交 %s 。"
+
+#  译者:注意保持前导空格
+#: wt-status.c:977
+msgid "  (fix conflicts and run \"git revert --continue\")"
+msgstr "  (解决冲突并运行 \"git revert --continue\")"
+
+#  译者:注意保持前导空格
+#: wt-status.c:980
+msgid "  (all conflicts fixed: run \"git revert --continue\")"
+msgstr "  (所有冲突已解决:运行 \"git revert --continue\")"
+
+#  译者:注意保持前导空格
+#: wt-status.c:982
+msgid "  (use \"git revert --abort\" to cancel the revert operation)"
+msgstr "  (使用 \"git revert --abort\" 以取消反转提交操作)"
 
-#: wt-status.c:962
+#: wt-status.c:993
+#, c-format
+msgid "You are currently bisecting, started from branch '%s'."
+msgstr "您正在从分支 '%s' 开始做二分查找。"
+
+#: wt-status.c:997
 msgid "You are currently bisecting."
 msgstr "您正在做二分查找。"
 
 #  译者:注意保持前导空格
-#: wt-status.c:965
+#: wt-status.c:1000
 msgid "  (use \"git bisect reset\" to get back to the original branch)"
 msgstr "  (使用 \"git bisect reset\" 以回到原有分支)"
 
-#: wt-status.c:1064
+#: wt-status.c:1173
 msgid "On branch "
 msgstr "位于分支 "
 
-#: wt-status.c:1071
+#: wt-status.c:1180
+msgid "rebase in progress; onto "
+msgstr "变基正在进行中;至 "
+
+#: wt-status.c:1187
+msgid "HEAD detached at "
+msgstr "头指针分离于 "
+
+#: wt-status.c:1189
+msgid "HEAD detached from "
+msgstr "头指针分离自 "
+
+#: wt-status.c:1192
 msgid "Not currently on any branch."
 msgstr "当前不在任何分支上。"
 
-#: wt-status.c:1083
+#: wt-status.c:1209
 msgid "Initial commit"
 msgstr "初始提交"
 
-#: wt-status.c:1097
+#: wt-status.c:1223
 msgid "Untracked files"
 msgstr "未跟踪的文件"
 
-#: wt-status.c:1099
+#: wt-status.c:1225
 msgid "Ignored files"
 msgstr "忽略的文件"
 
-#: wt-status.c:1101
+#: wt-status.c:1229
+#, c-format
+msgid ""
+"It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
+"may speed it up, but you have to be careful not to forget to add\n"
+"new files yourself (see 'git help status')."
+msgstr ""
+"耗费了 %.2f 秒以枚举未跟踪的文件。'status -uno' 也许能提高速度,\n"
+"但您需要小心不要忘了添加新文件(参见 'git help status')。"
+
+#: wt-status.c:1235
 #, c-format
 msgid "Untracked files not listed%s"
 msgstr "未跟踪的文件没有列出%s"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: wt-status.c:1103
+#: wt-status.c:1237
 msgid " (use -u option to show untracked files)"
 msgstr "(使用 -u 参数显示未跟踪的文件)"
 
-#: wt-status.c:1109
+#: wt-status.c:1243
 msgid "No changes"
 msgstr "没有修改"
 
-#  译者:中文字符串拼接,可删除前导空格
-#: wt-status.c:1114
+#: wt-status.c:1248
 #, c-format
 msgid "no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
 msgstr "修改尚未加入提交(使用 \"git add\" 和/或 \"git commit -a\")\n"
 
-#: wt-status.c:1117
+#: wt-status.c:1251
 #, c-format
 msgid "no changes added to commit\n"
 msgstr "修改尚未加入提交\n"
 
-#: wt-status.c:1120
+#: wt-status.c:1254
 #, c-format
 msgid ""
 "nothing added to commit but untracked files present (use \"git add\" to "
 "track)\n"
 msgstr "提交为空,但是存在尚未跟踪的文件(使用 \"git add\" 建立跟踪)\n"
 
-#: wt-status.c:1123
+#: wt-status.c:1257
 #, c-format
 msgid "nothing added to commit but untracked files present\n"
 msgstr "提交为空,但是存在尚未跟踪的文件\n"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: wt-status.c:1126
+#: wt-status.c:1260
 #, c-format
 msgid "nothing to commit (create/copy files and use \"git add\" to track)\n"
 msgstr "无文件要提交(创建/拷贝文件并使用 \"git add\" 建立跟踪)\n"
 
-#: wt-status.c:1129 wt-status.c:1134
+#: wt-status.c:1263 wt-status.c:1268
 #, c-format
 msgid "nothing to commit\n"
 msgstr "无文件要提交\n"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: wt-status.c:1132
+#: wt-status.c:1266
 #, c-format
 msgid "nothing to commit (use -u to show untracked files)\n"
 msgstr "无文件要提交(使用 -u 显示未跟踪的文件)\n"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: wt-status.c:1136
+#: wt-status.c:1270
 #, c-format
 msgid "nothing to commit, working directory clean\n"
 msgstr "无文件要提交,干净的工作区\n"
 
-#: wt-status.c:1244
+#: wt-status.c:1378
 msgid "HEAD (no branch)"
 msgstr "HEAD(非分支)"
 
 #  译者:注意保持句尾空格
-#: wt-status.c:1250
+#: wt-status.c:1384
 msgid "Initial commit on "
 msgstr "初始提交于 "
 
 #  译者:注意保持句尾空格
-#: wt-status.c:1265
+#: wt-status.c:1399
 msgid "behind "
 msgstr "落后 "
 
 #  译者:注意保持句尾空格
-#: wt-status.c:1268 wt-status.c:1271
+#: wt-status.c:1402 wt-status.c:1405
 msgid "ahead "
 msgstr "领先 "
 
 #  译者:注意保持句尾空格
-#: wt-status.c:1273
+#: wt-status.c:1407
 msgid ", behind "
 msgstr ",落后 "
 
-#: compat/precompose_utf8.c:58 builtin/clone.c:341
+#: compat/precompose_utf8.c:58 builtin/clone.c:351
 #, c-format
 msgid "failed to unlink '%s'"
 msgstr "无法删除 '%s'"
@@ -1468,193 +1710,225 @@ msgstr "无法删除 '%s'"
 msgid "git add [options] [--] <pathspec>..."
 msgstr "git add [选项] [--] <路径匹配>..."
 
-#: builtin/add.c:63
+#  译者:字符串首行行首要添加“warning: ”字串,故此首行要较其余行短
+#.
+#. * To be consistent with "git add -p" and most Git
+#. * commands, we should default to being tree-wide, but
+#. * this is not the original behavior and can't be
+#. * changed until users trained themselves not to type
+#. * "git add -u" or "git add -A". For now, we warn and
+#. * keep the old behavior. Later, the behavior can be changed
+#. * to tree-wide, keeping the warning for a while, and
+#. * eventually we can drop the warning.
+#.
+#: builtin/add.c:58
+#, c-format
+msgid ""
+"The behavior of 'git add %s (or %s)' with no path argument from a\n"
+"subdirectory of the tree will change in Git 2.0 and should not be used "
+"anymore.\n"
+"To add content for the whole tree, run:\n"
+"\n"
+"  git add %s :/\n"
+"  (or git add %s :/)\n"
+"\n"
+"To restrict the command to the current directory, run:\n"
+"\n"
+"  git add %s .\n"
+"  (or git add %s .)\n"
+"\n"
+"With the current Git version, the command is restricted to the current "
+"directory.\n"
+msgstr ""
+"在 Git 2.0 版本,位于一个子目录下不带任何路径参数地执行命令\n"
+"'git add %s (或 %s)' 的行为将被改变,不要再继续使用了。\n"
+"如果要添加整个目录树的内容,执行:\n"
+"\n"
+"  git add %s :/\n"
+"  (或 git add %s :/)\n"
+"\n"
+"如果要限制该命令只作用于当前目录,执行:\n"
+"\n"
+"  git add %s .\n"
+"  (或 git add %s .)\n"
+"\n"
+"对于当前版本的 Git,这条命令只作用于当前目录。\n"
+
+#: builtin/add.c:100
+#, c-format
+msgid ""
+"You ran 'git add' with neither '-A (--all)' or '--ignore-removal',\n"
+"whose behaviour will change in Git 2.0 with respect to paths you removed.\n"
+"Paths like '%s' that are\n"
+"removed from your working tree are ignored with this version of Git.\n"
+"\n"
+"* 'git add --ignore-removal <pathspec>', which is the current default,\n"
+"  ignores paths you removed from your working tree.\n"
+"\n"
+"* 'git add --all <pathspec>' will let you also record the removals.\n"
+"\n"
+"Run 'git status' to check the paths you removed from your working tree.\n"
+msgstr ""
+"您在运行 'git add' 时没有指定 '-A (--all)' 或 '--ignore-removal',\n"
+"针对其中本地移除路径的行为将在 Git 2.0 版本库发生变化。\n"
+"像本地工作区移除的路径 '%s'\n"
+"在此版本的 Git 中被忽略。\n"
+"\n"
+"* 'git add --ignore-removal <pathspec>',是当前版本的默认操作,\n"
+"  忽略您本地工作区中移除的文件。\n"
+"\n"
+"* 'git add --all <pathspec>' 将让您同时对删除操作进行记录。\n"
+"\n"
+"运行 'git status' 来检查您本地工作区中移除的路径。\n"
+
+#: builtin/add.c:144
 #, c-format
 msgid "unexpected diff status %c"
 msgstr "意外的差异状态 %c"
 
-#: builtin/add.c:68 builtin/commit.c:231
+#: builtin/add.c:149 builtin/commit.c:252
 msgid "updating files failed"
 msgstr "更新文件失败"
 
-#: builtin/add.c:78
+#: builtin/add.c:163
 #, c-format
 msgid "remove '%s'\n"
 msgstr "删除 '%s'\n"
 
-#: builtin/add.c:148
+#: builtin/add.c:253
 msgid "Unstaged changes after refreshing the index:"
 msgstr "刷新索引之后尚未被暂存的变更:"
 
-#: builtin/add.c:151 builtin/add.c:460 builtin/rm.c:275
+#: builtin/add.c:256 builtin/add.c:573 builtin/rm.c:337
 #, c-format
 msgid "pathspec '%s' did not match any files"
 msgstr "路径 '%s' 未匹配任何文件"
 
-#: builtin/add.c:234
+#: builtin/add.c:339
 msgid "Could not read the index"
 msgstr "不能读取索引"
 
-#: builtin/add.c:244
+#: builtin/add.c:350
 #, c-format
 msgid "Could not open '%s' for writing."
 msgstr "不能打开 '%s' 以写入。"
 
-#: builtin/add.c:248
+#: builtin/add.c:354
 msgid "Could not write patch"
 msgstr "不能生成补丁"
 
-#: builtin/add.c:253
+#: builtin/add.c:359
 #, c-format
 msgid "Could not stat '%s'"
 msgstr "不能查看文件状态 '%s'"
 
-#: builtin/add.c:255
+#: builtin/add.c:361
 msgid "Empty patch. Aborted."
 msgstr "空补丁。异常终止。"
 
-#: builtin/add.c:261
+#: builtin/add.c:367
 #, c-format
 msgid "Could not apply '%s'"
 msgstr "不能应用 '%s'"
 
-#: builtin/add.c:271
+#: builtin/add.c:377
 msgid "The following paths are ignored by one of your .gitignore files:\n"
 msgstr "下列路径根据您的一个 .gitignore 文件而被忽略:\n"
 
-#: builtin/add.c:277 builtin/clean.c:161 builtin/fetch.c:78 builtin/mv.c:63
-#: builtin/prune-packed.c:76 builtin/push.c:425 builtin/remote.c:1253
-#: builtin/rm.c:206
+#: builtin/add.c:394 builtin/clean.c:875 builtin/fetch.c:78 builtin/mv.c:63
+#: builtin/prune-packed.c:73 builtin/push.c:451 builtin/remote.c:1253
+#: builtin/rm.c:268
 msgid "dry run"
 msgstr "演习"
 
-#: builtin/add.c:278 builtin/apply.c:4405 builtin/check-ignore.c:19
-#: builtin/commit.c:1150 builtin/count-objects.c:82 builtin/fsck.c:613
-#: builtin/log.c:1522 builtin/mv.c:62 builtin/read-tree.c:112
+#: builtin/add.c:395 builtin/apply.c:4410 builtin/check-ignore.c:19
+#: builtin/commit.c:1220 builtin/count-objects.c:95 builtin/fsck.c:613
+#: builtin/log.c:1573 builtin/mv.c:62 builtin/read-tree.c:113
 msgid "be verbose"
 msgstr "冗长输出"
 
-#: builtin/add.c:280
+#: builtin/add.c:397
 msgid "interactive picking"
 msgstr "交互式拣选"
 
-#: builtin/add.c:281 builtin/checkout.c:1031 builtin/reset.c:258
+#: builtin/add.c:398 builtin/checkout.c:1073 builtin/reset.c:261
 msgid "select hunks interactively"
 msgstr "交互式挑选数据块"
 
-#: builtin/add.c:282
+#: builtin/add.c:399
 msgid "edit current diff and apply"
 msgstr "编辑当前差异并应用"
 
-#: builtin/add.c:283
+#: builtin/add.c:400
 msgid "allow adding otherwise ignored files"
 msgstr "允许添加忽略的文件"
 
-#: builtin/add.c:284
+#: builtin/add.c:401
 msgid "update tracked files"
 msgstr "更新已跟踪的文件"
 
-#: builtin/add.c:285
+#: builtin/add.c:402
 msgid "record only the fact that the path will be added later"
 msgstr "只记录,该路径稍后再添加"
 
-#: builtin/add.c:286
+#: builtin/add.c:403
 msgid "add changes from all tracked and untracked files"
 msgstr "添加所有改变的已跟踪文件和未跟踪文件"
 
-#: builtin/add.c:287
+#. takes no arguments
+#: builtin/add.c:406
+msgid "ignore paths removed in the working tree (same as --no-all)"
+msgstr "忽略工作区中移除的路径(和 --no-all 相同)"
+
+#: builtin/add.c:408
 msgid "don't add, only refresh the index"
 msgstr "不添加,只刷新索引"
 
-#: builtin/add.c:288
+#: builtin/add.c:409
 msgid "just skip files which cannot be added because of errors"
 msgstr "跳过因出错不能添加的文件"
 
-#: builtin/add.c:289
+#: builtin/add.c:410
 msgid "check if - even missing - files are ignored in dry run"
 msgstr "检查在演习模式下文件(即使不存在)是否被忽略"
 
-#: builtin/add.c:311
+#: builtin/add.c:432
 #, c-format
 msgid "Use -f if you really want to add them.\n"
 msgstr "使用 -f 参数如果您确实要添加它们。\n"
 
-#: builtin/add.c:312
+#: builtin/add.c:433
 msgid "no files added"
 msgstr "没有文件被添加"
 
-#: builtin/add.c:318
+#: builtin/add.c:439
 msgid "adding files failed"
 msgstr "添加文件失败"
 
-#  译者:字符串首行行首要添加“warning: ”字串,故此首行要较其余行短
-#.
-#. * To be consistent with "git add -p" and most Git
-#. * commands, we should default to being tree-wide, but
-#. * this is not the original behavior and can't be
-#. * changed until users trained themselves not to type
-#. * "git add -u" or "git add -A". For now, we warn and
-#. * keep the old behavior. Later, this warning can be
-#. * turned into a die(...), and eventually we may
-#. * reallow the command with a new behavior.
-#.
-#: builtin/add.c:335
-#, c-format
-msgid ""
-"The behavior of 'git add %s (or %s)' with no path argument from a\n"
-"subdirectory of the tree will change in Git 2.0 and should not be used "
-"anymore.\n"
-"To add content for the whole tree, run:\n"
-"\n"
-"  git add %s :/\n"
-"  (or git add %s :/)\n"
-"\n"
-"To restrict the command to the current directory, run:\n"
-"\n"
-"  git add %s .\n"
-"  (or git add %s .)\n"
-"\n"
-"With the current Git version, the command is restricted to the current "
-"directory."
-msgstr ""
-"在 Git 2.0 版本,将不再允许在一个子目录下不带任何路径参数地执行\n"
-"命令 'git add %s (或 %s)'。所以不要再继续使用了。\n"
-"如果要添加整个目录树的内容,执行:\n"
-"\n"
-"  git add %s :/\n"
-"  (或 git add %s :/)\n"
-"\n"
-"如果要限制该命令只作用于当前目录,执行:\n"
-"\n"
-"  git add %s .\n"
-"  (或 git add %s .)\n"
-"\n"
-"对于当前版本的 Git,这条命令只作用于当前目录。"
-
-#: builtin/add.c:381
+#: builtin/add.c:478
 msgid "-A and -u are mutually incompatible"
 msgstr "-A 和 -u 选项互斥"
 
-#: builtin/add.c:383
+#: builtin/add.c:496
 msgid "Option --ignore-missing can only be used together with --dry-run"
 msgstr "选项 --ignore-missing 只能和 --dry-run 同时使用"
 
-#: builtin/add.c:414
+#: builtin/add.c:526
 #, c-format
 msgid "Nothing specified, nothing added.\n"
 msgstr "没有指定文件,也没有文件被添加。\n"
 
-#: builtin/add.c:415
+#: builtin/add.c:527
 #, c-format
 msgid "Maybe you wanted to say 'git add .'?\n"
 msgstr "也许您想要执行 'git add .'?\n"
 
-#: builtin/add.c:421 builtin/check-ignore.c:67 builtin/clean.c:204
-#: builtin/commit.c:291 builtin/mv.c:82 builtin/rm.c:235
+#: builtin/add.c:533 builtin/check-ignore.c:161 builtin/clean.c:919
+#: builtin/commit.c:312 builtin/mv.c:82 builtin/rm.c:297
 msgid "index file corrupt"
 msgstr "索引文件损坏"
 
-#: builtin/add.c:481 builtin/apply.c:4501 builtin/mv.c:229 builtin/rm.c:370
+#: builtin/add.c:605 builtin/apply.c:4506 builtin/mv.c:229 builtin/rm.c:432
 msgid "Unable to write new index file"
 msgstr "无法写入新索引文件"
 
@@ -1782,24 +2056,24 @@ msgstr "无法读取符号链接 %s"
 msgid "unable to open or read %s"
 msgstr "不能打开或读取 %s"
 
-#: builtin/apply.c:2684
+#: builtin/apply.c:2688
 #, c-format
 msgid "invalid start of line: '%c'"
 msgstr "无效的行首字符:'%c'"
 
-#: builtin/apply.c:2802
+#: builtin/apply.c:2806
 #, c-format
 msgid "Hunk #%d succeeded at %d (offset %d line)."
 msgid_plural "Hunk #%d succeeded at %d (offset %d lines)."
 msgstr[0] "块 #%d 成功应用于 %d(偏移 %d 行)"
 msgstr[1] "块 #%d 成功应用于 %d(偏移 %d 行)"
 
-#: builtin/apply.c:2814
+#: builtin/apply.c:2818
 #, c-format
 msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
 msgstr "上下文减少到(%ld/%ld)以在第 %d 行应用补丁片段"
 
-#: builtin/apply.c:2820
+#: builtin/apply.c:2824
 #, c-format
 msgid ""
 "while searching for:\n"
@@ -1808,318 +2082,318 @@ msgstr ""
 "当查询:\n"
 "%.*s"
 
-#: builtin/apply.c:2839
+#: builtin/apply.c:2843
 #, c-format
 msgid "missing binary patch data for '%s'"
 msgstr "缺失 '%s' 的二进制补丁数据"
 
-#: builtin/apply.c:2942
+#: builtin/apply.c:2946
 #, c-format
 msgid "binary patch does not apply to '%s'"
 msgstr "二进制补丁未应用到 '%s'"
 
-#: builtin/apply.c:2948
+#: builtin/apply.c:2952
 #, c-format
 msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
 msgstr "到 '%s' 的二进制补丁产生了不正确的结果(应为 %s,却为 %s)"
 
-#: builtin/apply.c:2969
+#: builtin/apply.c:2973
 #, c-format
 msgid "patch failed: %s:%ld"
 msgstr "打补丁失败:%s:%ld"
 
-#: builtin/apply.c:3091
+#: builtin/apply.c:3095
 #, c-format
 msgid "cannot checkout %s"
 msgstr "不能检出 %s"
 
-#: builtin/apply.c:3136 builtin/apply.c:3145 builtin/apply.c:3189
+#: builtin/apply.c:3140 builtin/apply.c:3149 builtin/apply.c:3194
 #, c-format
 msgid "read of %s failed"
 msgstr "读取 %s 失败"
 
-#: builtin/apply.c:3169 builtin/apply.c:3391
+#: builtin/apply.c:3174 builtin/apply.c:3396
 #, c-format
 msgid "path %s has been renamed/deleted"
 msgstr "路径 %s 已经被重命名/删除"
 
-#: builtin/apply.c:3250 builtin/apply.c:3405
+#: builtin/apply.c:3255 builtin/apply.c:3410
 #, c-format
 msgid "%s: does not exist in index"
 msgstr "%s:不存在于索引中"
 
-#: builtin/apply.c:3254 builtin/apply.c:3397 builtin/apply.c:3419
+#: builtin/apply.c:3259 builtin/apply.c:3402 builtin/apply.c:3424
 #, c-format
 msgid "%s: %s"
 msgstr "%s:%s"
 
-#: builtin/apply.c:3259 builtin/apply.c:3413
+#: builtin/apply.c:3264 builtin/apply.c:3418
 #, c-format
 msgid "%s: does not match index"
 msgstr "%s:和索引不匹配"
 
-#: builtin/apply.c:3361
+#: builtin/apply.c:3366
 msgid "removal patch leaves file contents"
 msgstr "移除补丁仍留下了文件内容"
 
-#: builtin/apply.c:3430
+#: builtin/apply.c:3435
 #, c-format
 msgid "%s: wrong type"
 msgstr "%s:错误类型"
 
-#: builtin/apply.c:3432
+#: builtin/apply.c:3437
 #, c-format
 msgid "%s has type %o, expected %o"
 msgstr "%s 的类型是 %o,应为 %o"
 
-#: builtin/apply.c:3533
+#: builtin/apply.c:3538
 #, c-format
 msgid "%s: already exists in index"
 msgstr "%s:已经存在于索引中"
 
-#: builtin/apply.c:3536
+#: builtin/apply.c:3541
 #, c-format
 msgid "%s: already exists in working directory"
 msgstr "%s:已经存在于工作区中"
 
-#: builtin/apply.c:3556
+#: builtin/apply.c:3561
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o)"
 msgstr "%2$s 的新模式(%1$o)和旧模式(%3$o)不匹配"
 
-#: builtin/apply.c:3561
+#: builtin/apply.c:3566
 #, c-format
 msgid "new mode (%o) of %s does not match old mode (%o) of %s"
 msgstr "%2$s 的新模式(%1$o)和 %4$s 的旧模式(%3$o)不匹配"
 
-#: builtin/apply.c:3569
+#: builtin/apply.c:3574
 #, c-format
 msgid "%s: patch does not apply"
 msgstr "%s:补丁未应用"
 
-#: builtin/apply.c:3582
+#: builtin/apply.c:3587
 #, c-format
 msgid "Checking patch %s..."
 msgstr "检查补丁 %s..."
 
-#: builtin/apply.c:3675 builtin/checkout.c:215 builtin/reset.c:124
+#: builtin/apply.c:3680 builtin/checkout.c:216 builtin/reset.c:127
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
 msgstr "对路径 '%s' 的 make_cache_entry 操作失败"
 
-#: builtin/apply.c:3818
+#: builtin/apply.c:3823
 #, c-format
 msgid "unable to remove %s from index"
 msgstr "不能从索引中移除 %s"
 
-#: builtin/apply.c:3846
+#: builtin/apply.c:3851
 #, c-format
-msgid "corrupt patch for subproject %s"
-msgstr "子项目 %s 损坏的补丁"
+msgid "corrupt patch for submodule %s"
+msgstr "子模组 %s 损坏的补丁"
 
-#: builtin/apply.c:3850
+#: builtin/apply.c:3855
 #, c-format
 msgid "unable to stat newly created file '%s'"
 msgstr "不能枚举新建文件 '%s' 的状态"
 
-#: builtin/apply.c:3855
+#: builtin/apply.c:3860
 #, c-format
 msgid "unable to create backing store for newly created file %s"
 msgstr "不能为新建文件 %s 创建后端存储"
 
-#: builtin/apply.c:3858 builtin/apply.c:3966
+#: builtin/apply.c:3863 builtin/apply.c:3971
 #, c-format
 msgid "unable to add cache entry for %s"
 msgstr "无法为 %s 添加缓存条目"
 
-#: builtin/apply.c:3891
+#: builtin/apply.c:3896
 #, c-format
 msgid "closing file '%s'"
 msgstr "关闭文件 '%s'"
 
-#: builtin/apply.c:3940
+#: builtin/apply.c:3945
 #, c-format
 msgid "unable to write file '%s' mode %o"
 msgstr "不能写文件 '%s' 权限 %o"
 
-#: builtin/apply.c:4027
+#: builtin/apply.c:4032
 #, c-format
 msgid "Applied patch %s cleanly."
 msgstr "成功应用补丁 %s。"
 
-#: builtin/apply.c:4035
+#: builtin/apply.c:4040
 msgid "internal error"
 msgstr "内部错误"
 
 #. Say this even without --verbose
-#: builtin/apply.c:4038
+#: builtin/apply.c:4043
 #, c-format
 msgid "Applying patch %%s with %d reject..."
 msgid_plural "Applying patch %%s with %d rejects..."
 msgstr[0] "应用 %%s 个补丁,其中 %d 个被拒绝..."
 msgstr[1] "应用 %%s 个补丁,其中 %d 个被拒绝..."
 
-#: builtin/apply.c:4048
+#: builtin/apply.c:4053
 #, c-format
 msgid "truncating .rej filename to %.*s.rej"
 msgstr "截短 .rej 文件名为 %.*s.rej"
 
-#: builtin/apply.c:4069
+#: builtin/apply.c:4074
 #, c-format
 msgid "Hunk #%d applied cleanly."
 msgstr "第 #%d 个片段成功应用。"
 
-#: builtin/apply.c:4072
+#: builtin/apply.c:4077
 #, c-format
 msgid "Rejected hunk #%d."
 msgstr "拒绝第 #%d 个片段。"
 
-#: builtin/apply.c:4222
+#: builtin/apply.c:4227
 msgid "unrecognized input"
 msgstr "未能识别的输入"
 
-#: builtin/apply.c:4233
+#: builtin/apply.c:4238
 msgid "unable to read index file"
 msgstr "无法读取索引文件"
 
-#: builtin/apply.c:4352 builtin/apply.c:4355 builtin/clone.c:91
+#: builtin/apply.c:4357 builtin/apply.c:4360 builtin/clone.c:91
 #: builtin/fetch.c:63
 msgid "path"
 msgstr "路径"
 
-#: builtin/apply.c:4353
+#: builtin/apply.c:4358
 msgid "don't apply changes matching the given path"
 msgstr "不要应用与给出路径向匹配的变更"
 
-#: builtin/apply.c:4356
+#: builtin/apply.c:4361
 msgid "apply changes matching the given path"
 msgstr "应用与给出路径向匹配的变更"
 
-#: builtin/apply.c:4358
+#: builtin/apply.c:4363
 msgid "num"
 msgstr "数字"
 
-#: builtin/apply.c:4359
+#: builtin/apply.c:4364
 msgid "remove <num> leading slashes from traditional diff paths"
 msgstr "从传统的 diff 路径中移除指定数量的前导斜线"
 
-#: builtin/apply.c:4362
+#: builtin/apply.c:4367
 msgid "ignore additions made by the patch"
 msgstr "忽略补丁中的添加的文件"
 
-#: builtin/apply.c:4364
+#: builtin/apply.c:4369
 msgid "instead of applying the patch, output diffstat for the input"
 msgstr "不应用补丁,而是显示输入的差异统计(diffstat)"
 
-#: builtin/apply.c:4368
+#: builtin/apply.c:4373
 msgid "show number of added and deleted lines in decimal notation"
 msgstr "以十进制数显示添加和删除的行数"
 
-#: builtin/apply.c:4370
+#: builtin/apply.c:4375
 msgid "instead of applying the patch, output a summary for the input"
 msgstr "不应用补丁,而是显示输入的概要"
 
-#: builtin/apply.c:4372
+#: builtin/apply.c:4377
 msgid "instead of applying the patch, see if the patch is applicable"
 msgstr "不应用补丁,而是查看补丁是否可应用"
 
-#: builtin/apply.c:4374
+#: builtin/apply.c:4379
 msgid "make sure the patch is applicable to the current index"
 msgstr "确认补丁可以应用到当前索引"
 
-#: builtin/apply.c:4376
+#: builtin/apply.c:4381
 msgid "apply a patch without touching the working tree"
 msgstr "应用补丁而不修改工作区"
 
-#: builtin/apply.c:4378
+#: builtin/apply.c:4383
 msgid "also apply the patch (use with --stat/--summary/--check)"
 msgstr "还应用此补丁(与 --stat/--summary/--check 选项同时使用)"
 
-#: builtin/apply.c:4380
+#: builtin/apply.c:4385
 msgid "attempt three-way merge if a patch does not apply"
 msgstr "如果一个补丁不能应用则尝试三路合并"
 
-#: builtin/apply.c:4382
+#: builtin/apply.c:4387
 msgid "build a temporary index based on embedded index information"
 msgstr "创建一个临时索引基于嵌入的索引信息"
 
-#: builtin/apply.c:4384 builtin/checkout-index.c:197 builtin/ls-files.c:463
+#: builtin/apply.c:4389 builtin/checkout-index.c:197 builtin/ls-files.c:462
 msgid "paths are separated with NUL character"
 msgstr "路径以 NUL 字符分隔"
 
-#: builtin/apply.c:4387
+#: builtin/apply.c:4392
 msgid "ensure at least <n> lines of context match"
 msgstr "确保至少匹配 <n> 行上下文"
 
-#: builtin/apply.c:4388
+#: builtin/apply.c:4393
 msgid "action"
 msgstr "动作"
 
-#: builtin/apply.c:4389
+#: builtin/apply.c:4394
 msgid "detect new or modified lines that have whitespace errors"
 msgstr "检查新增和修改的行中间的空白字符滥用"
 
-#: builtin/apply.c:4392 builtin/apply.c:4395
+#: builtin/apply.c:4397 builtin/apply.c:4400
 msgid "ignore changes in whitespace when finding context"
 msgstr "查找上下文时忽略空白字符的变更"
 
-#: builtin/apply.c:4398
+#: builtin/apply.c:4403
 msgid "apply the patch in reverse"
 msgstr "反向应用补丁"
 
-#: builtin/apply.c:4400
+#: builtin/apply.c:4405
 msgid "don't expect at least one line of context"
 msgstr "无需至少一行上下文"
 
-#: builtin/apply.c:4402
+#: builtin/apply.c:4407
 msgid "leave the rejected hunks in corresponding *.rej files"
 msgstr "将拒绝的补丁片段保存在对应的 *.rej 文件中"
 
-#: builtin/apply.c:4404
+#: builtin/apply.c:4409
 msgid "allow overlapping hunks"
 msgstr "允许重叠的补丁片段"
 
-#: builtin/apply.c:4407
+#: builtin/apply.c:4412
 msgid "tolerate incorrectly detected missing new-line at the end of file"
 msgstr "允许不正确的文件末尾换行符"
 
-#: builtin/apply.c:4410
+#: builtin/apply.c:4415
 msgid "do not trust the line counts in the hunk headers"
 msgstr "不信任补丁片段的头信息中的行号"
 
-#: builtin/apply.c:4412
+#: builtin/apply.c:4417
 msgid "root"
 msgstr "根目录"
 
-#: builtin/apply.c:4413
+#: builtin/apply.c:4418
 msgid "prepend <root> to all filenames"
 msgstr "为所有文件名前添加 <根目录>"
 
-#: builtin/apply.c:4435
+#: builtin/apply.c:4440
 msgid "--3way outside a repository"
 msgstr "--3way 在一个版本库之外"
 
-#: builtin/apply.c:4443
+#: builtin/apply.c:4448
 msgid "--index outside a repository"
 msgstr "--index 在一个版本库之外"
 
-#: builtin/apply.c:4446
+#: builtin/apply.c:4451
 msgid "--cached outside a repository"
 msgstr "--cached 在一个版本库之外"
 
-#: builtin/apply.c:4462
+#: builtin/apply.c:4467
 #, c-format
 msgid "can't open patch '%s'"
 msgstr "不能打开补丁 '%s'"
 
-#: builtin/apply.c:4476
+#: builtin/apply.c:4481
 #, c-format
 msgid "squelched %d whitespace error"
 msgid_plural "squelched %d whitespace errors"
 msgstr[0] "抑制下仍有 %d 个空白字符误用"
 msgstr[1] "抑制下仍有 %d 个空白字符误用"
 
-#: builtin/apply.c:4482 builtin/apply.c:4492
+#: builtin/apply.c:4487 builtin/apply.c:4497
 #, c-format
 msgid "%d line adds whitespace errors."
 msgid_plural "%d lines add whitespace errors."
@@ -2143,21 +2417,21 @@ msgstr "git archive:未提供远程URL"
 msgid "git archive: expected ACK/NAK, got EOF"
 msgstr "git archive:应为ACK/NACK,却得到EOF"
 
-#: builtin/archive.c:63
+#: builtin/archive.c:61
 #, c-format
 msgid "git archive: NACK %s"
 msgstr "git archive:NACK %s"
 
-#: builtin/archive.c:65
+#: builtin/archive.c:63
 #, c-format
 msgid "remote error: %s"
 msgstr "远程错误:%s"
 
-#: builtin/archive.c:66
+#: builtin/archive.c:64
 msgid "git archive: protocol error"
 msgstr "git archive:协议错误"
 
-#: builtin/archive.c:71
+#: builtin/archive.c:68
 msgid "git archive: expected a flush"
 msgstr "git archive:应为刷新"
 
@@ -2173,124 +2447,124 @@ msgstr "执行 'git bisect next'"
 msgid "update BISECT_HEAD instead of checking out the current commit"
 msgstr "更新 BISECT_HEAD 而非检出当前提交"
 
-#: builtin/blame.c:25
+#: builtin/blame.c:26
 msgid "git blame [options] [rev-opts] [rev] [--] file"
 msgstr "git blame [选项] [版本选项] [版本] [--] 文件"
 
-#: builtin/blame.c:30 builtin/shortlog.c:15
+#: builtin/blame.c:31
 msgid "[rev-opts] are documented in git-rev-list(1)"
 msgstr "[版本选项] 的文档记录在 git-rev-list(1) 中"
 
-#: builtin/blame.c:2350
+#: builtin/blame.c:2276
 msgid "Show blame entries as we find them, incrementally"
 msgstr "增量式地显示发现的 blame 条目"
 
-#: builtin/blame.c:2351
+#: builtin/blame.c:2277
 msgid "Show blank SHA-1 for boundary commits (Default: off)"
 msgstr "边界提交显示空的 SHA-1(默认:关闭)"
 
-#: builtin/blame.c:2352
+#: builtin/blame.c:2278
 msgid "Do not treat root commits as boundaries (Default: off)"
 msgstr "不把根提交作为边界(默认:关闭)"
 
-#: builtin/blame.c:2353
+#: builtin/blame.c:2279
 msgid "Show work cost statistics"
 msgstr "显示命令消耗统计"
 
-#: builtin/blame.c:2354
+#: builtin/blame.c:2280
 msgid "Show output score for blame entries"
 msgstr "显示判断 blame 条目位移的得分诊断信息"
 
-#: builtin/blame.c:2355
+#: builtin/blame.c:2281
 msgid "Show original filename (Default: auto)"
 msgstr "显示原始文件名(默认:自动)"
 
-#: builtin/blame.c:2356
+#: builtin/blame.c:2282
 msgid "Show original linenumber (Default: off)"
 msgstr "显示原始的行号(默认:关闭)"
 
-#: builtin/blame.c:2357
+#: builtin/blame.c:2283
 msgid "Show in a format designed for machine consumption"
 msgstr "显示为一个适合机器读取的格式"
 
-#: builtin/blame.c:2358
+#: builtin/blame.c:2284
 msgid "Show porcelain format with per-line commit information"
 msgstr "为每一行显示机器适用的提交信息"
 
-#: builtin/blame.c:2359
+#: builtin/blame.c:2285
 msgid "Use the same output mode as git-annotate (Default: off)"
 msgstr "使用和 git-annotate 相同的输出模式(默认:关闭)"
 
-#: builtin/blame.c:2360
+#: builtin/blame.c:2286
 msgid "Show raw timestamp (Default: off)"
 msgstr "显示原始时间戳(默认:关闭)"
 
-#: builtin/blame.c:2361
+#: builtin/blame.c:2287
 msgid "Show long commit SHA1 (Default: off)"
 msgstr "显示长的SHA1提交号(默认:关闭)"
 
-#: builtin/blame.c:2362
+#: builtin/blame.c:2288
 msgid "Suppress author name and timestamp (Default: off)"
 msgstr "隐藏作者名字和时间戳(默认:关闭)"
 
-#: builtin/blame.c:2363
+#: builtin/blame.c:2289
 msgid "Show author email instead of name (Default: off)"
 msgstr "显示作者的邮箱而不是名字(默认:关闭)"
 
-#: builtin/blame.c:2364
+#: builtin/blame.c:2290
 msgid "Ignore whitespace differences"
 msgstr "忽略空白差异"
 
-#: builtin/blame.c:2365
+#: builtin/blame.c:2291
 msgid "Spend extra cycles to find better match"
 msgstr "花费额外的循环来找到更好的匹配"
 
-#: builtin/blame.c:2366
+#: builtin/blame.c:2292
 msgid "Use revisions from <file> instead of calling git-rev-list"
 msgstr "使用来自 <file> 的修订集而不是调用 git-rev-list"
 
-#: builtin/blame.c:2367
+#: builtin/blame.c:2293
 msgid "Use <file>'s contents as the final image"
 msgstr "使用 <file> 的内容作为最终的图片"
 
-#: builtin/blame.c:2368 builtin/blame.c:2369
+#: builtin/blame.c:2294 builtin/blame.c:2295
 msgid "score"
 msgstr "得分"
 
-#: builtin/blame.c:2368
+#: builtin/blame.c:2294
 msgid "Find line copies within and across files"
 msgstr "找到文件内及跨文件的行拷贝"
 
-#: builtin/blame.c:2369
+#: builtin/blame.c:2295
 msgid "Find line movements within and across files"
 msgstr "找到文件内及跨文件的行移动"
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "n,m"
 msgstr "n,m"
 
-#: builtin/blame.c:2370
+#: builtin/blame.c:2296
 msgid "Process only line range n,m, counting from 1"
 msgstr "只处理行范围在 n 和 m 之间的,从 1 开始"
 
-#: builtin/branch.c:23
+#: builtin/branch.c:24
 msgid "git branch [options] [-r | -a] [--merged | --no-merged]"
 msgstr "git branch [选项] [-r | -a] [--merged | --no-merged]"
 
-#: builtin/branch.c:24
+#: builtin/branch.c:25
 msgid "git branch [options] [-l] [-f] <branchname> [<start-point>]"
 msgstr "git branch [选项] [-l] [-f] <分支名> [<起始点>]"
 
-#: builtin/branch.c:25
+#: builtin/branch.c:26
 msgid "git branch [options] [-r] (-d | -D) <branchname>..."
 msgstr "git branch [选项] [-r] (-d | -D) <分支名>..."
 
-#: builtin/branch.c:26
+#: builtin/branch.c:27
 msgid "git branch [options] (-m | -M) [<oldbranch>] <newbranch>"
 msgstr "git branch [选项] (-m | -M) [<旧分支>] <新分支>"
 
 #  译者:保持原换行格式,在输出时 %s 的替代内容会让字符串变长
-#: builtin/branch.c:145
+#: builtin/branch.c:150
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
@@ -2300,7 +2574,7 @@ msgstr ""
 "         '%s',但未合并到 HEAD。"
 
 #  译者:保持原换行格式,在输出时 %s 的替代内容会让字符串变长
-#: builtin/branch.c:149
+#: builtin/branch.c:154
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
@@ -2309,12 +2583,12 @@ msgstr ""
 "并未删除分支 '%s', 虽然它已经合并到 HEAD,\n"
 "         然而却尚未被合并到分支 '%s' 。"
 
-#: builtin/branch.c:163
+#: builtin/branch.c:168
 #, c-format
 msgid "Couldn't look up commit object for '%s'"
 msgstr "无法查询 '%s' 指向的提交对象"
 
-#: builtin/branch.c:167
+#: builtin/branch.c:172
 #, c-format
 msgid ""
 "The branch '%s' is not fully merged.\n"
@@ -2323,285 +2597,327 @@ msgstr ""
 "分支 '%s' 没有完全合并。\n"
 "如果您确认要删除它,执行 'git branch -D %s'。"
 
-#: builtin/branch.c:180
+#: builtin/branch.c:185
 msgid "Update of config-file failed"
 msgstr "无法更新 config 文件"
 
-#: builtin/branch.c:208
+#: builtin/branch.c:213
 msgid "cannot use -a with -d"
 msgstr "不能将 -a 和 -d 同时使用"
 
-#: builtin/branch.c:214
+#: builtin/branch.c:219
 msgid "Couldn't look up commit object for HEAD"
 msgstr "无法查询 HEAD 指向的提交对象"
 
-#: builtin/branch.c:222
+#: builtin/branch.c:227
 #, c-format
 msgid "Cannot delete the branch '%s' which you are currently on."
 msgstr "无法删除您当前所在的分支 '%s'。"
 
-#: builtin/branch.c:235
+#: builtin/branch.c:240
 #, c-format
 msgid "remote branch '%s' not found."
 msgstr "远程分支 '%s' 未发现。"
 
-#: builtin/branch.c:236
+#: builtin/branch.c:241
 #, c-format
 msgid "branch '%s' not found."
 msgstr "分支 '%s' 未发现。"
 
-#: builtin/branch.c:250
+#: builtin/branch.c:255
 #, c-format
 msgid "Error deleting remote branch '%s'"
 msgstr "删除远程分支 '%s' 时出错"
 
-#: builtin/branch.c:251
+#: builtin/branch.c:256
 #, c-format
 msgid "Error deleting branch '%s'"
 msgstr "删除分支 '%s' 时出错"
 
-#: builtin/branch.c:258
+#: builtin/branch.c:263
 #, c-format
 msgid "Deleted remote branch %s (was %s).\n"
 msgstr "已删除远程分支 %s(曾为 %s)。\n"
 
-#: builtin/branch.c:259
+#: builtin/branch.c:264
 #, c-format
 msgid "Deleted branch %s (was %s).\n"
 msgstr "已删除分支 %s(曾为 %s)。\n"
 
-#: builtin/branch.c:361
+#: builtin/branch.c:366
 #, c-format
 msgid "branch '%s' does not point at a commit"
 msgstr "分支 '%s' 未指向一个提交"
 
-#: builtin/branch.c:433
+#: builtin/branch.c:453
 #, c-format
 msgid "[%s: behind %d]"
 msgstr "[%s:落后 %d]"
 
-#: builtin/branch.c:435
+#: builtin/branch.c:455
 #, c-format
 msgid "[behind %d]"
 msgstr "[落后 %d]"
 
-#: builtin/branch.c:439
+#: builtin/branch.c:459
 #, c-format
 msgid "[%s: ahead %d]"
 msgstr "[%s:领先 %d]"
 
-#: builtin/branch.c:441
+#: builtin/branch.c:461
 #, c-format
 msgid "[ahead %d]"
 msgstr "[领先 %d]"
 
-#: builtin/branch.c:444
+#: builtin/branch.c:464
 #, c-format
 msgid "[%s: ahead %d, behind %d]"
 msgstr "[%s:领先 %d,落后 %d]"
 
-#: builtin/branch.c:447
+#: builtin/branch.c:467
 #, c-format
 msgid "[ahead %d, behind %d]"
 msgstr "[领先 %d,落后 %d]"
 
-#: builtin/branch.c:469
+#: builtin/branch.c:490
 msgid " **** invalid ref ****"
 msgstr " **** 无效引用 ****"
 
-#: builtin/branch.c:560
+#: builtin/branch.c:582
+#, c-format
+msgid "(no branch, rebasing %s)"
+msgstr "(非分支,正变基 %s)"
+
+#: builtin/branch.c:585
+#, c-format
+msgid "(no branch, bisect started on %s)"
+msgstr "(非分支,二分查找开始于 %s)"
+
+#: builtin/branch.c:588
+#, c-format
+msgid "(detached from %s)"
+msgstr "(分离自 %s)"
+
+#: builtin/branch.c:591
 msgid "(no branch)"
 msgstr "(非分支)"
 
-#: builtin/branch.c:593
+#: builtin/branch.c:637
 #, c-format
 msgid "object '%s' does not point to a commit"
 msgstr "对象 '%s' 没有指向一个提交"
 
-#: builtin/branch.c:625
+#: builtin/branch.c:669
 msgid "some refs could not be read"
 msgstr "一些引用不能读取"
 
-#: builtin/branch.c:638
+#: builtin/branch.c:682
 msgid "cannot rename the current branch while not on any."
 msgstr "无法重命名当前分支因为不处于任何分支上。"
 
-#: builtin/branch.c:648
+#: builtin/branch.c:692
 #, c-format
 msgid "Invalid branch name: '%s'"
 msgstr "无效的分支名:'%s'"
 
-#: builtin/branch.c:663
+#: builtin/branch.c:707
 msgid "Branch rename failed"
 msgstr "分支重命名失败"
 
-#: builtin/branch.c:667
+#: builtin/branch.c:711
 #, c-format
 msgid "Renamed a misnamed branch '%s' away"
 msgstr "重命名掉一个错误命名的旧分支 '%s'"
 
-#: builtin/branch.c:671
+#: builtin/branch.c:715
 #, c-format
 msgid "Branch renamed to %s, but HEAD is not updated!"
 msgstr "分支重命名为 %s,但 HEAD 没有更新!"
 
-#: builtin/branch.c:678
+#: builtin/branch.c:722
 msgid "Branch is renamed, but update of config-file failed"
 msgstr "分支被重命名,但更新 config 文件失败"
 
-#: builtin/branch.c:693
+#: builtin/branch.c:737
 #, c-format
 msgid "malformed object name %s"
 msgstr "非法的对象名 %s"
 
-#: builtin/branch.c:717
+#: builtin/branch.c:761
 #, c-format
 msgid "could not write branch description template: %s"
 msgstr "不能写分支描述模版:%s"
 
-#: builtin/branch.c:747
+#: builtin/branch.c:791
 msgid "Generic options"
 msgstr "通用选项"
 
-#: builtin/branch.c:749
+#: builtin/branch.c:793
 msgid "show hash and subject, give twice for upstream branch"
 msgstr "显示哈希值和主题,若参数出现两次则显示上游分支"
 
-#: builtin/branch.c:750
+#: builtin/branch.c:794
 msgid "suppress informational messages"
 msgstr "不显示信息"
 
-#: builtin/branch.c:751
+#: builtin/branch.c:795
 msgid "set up tracking mode (see git-pull(1))"
 msgstr "设置跟踪模式(参见 git-pull(1))"
 
-#: builtin/branch.c:753
+#: builtin/branch.c:797
 msgid "change upstream info"
 msgstr "改变上游信息"
 
-#: builtin/branch.c:757
+#: builtin/branch.c:801
 msgid "use colored output"
 msgstr "使用彩色输出"
 
-#: builtin/branch.c:758
+#: builtin/branch.c:802
 msgid "act on remote-tracking branches"
 msgstr "作用于远程跟踪分支"
 
-#: builtin/branch.c:761 builtin/branch.c:767 builtin/branch.c:788
-#: builtin/branch.c:794 builtin/commit.c:1366 builtin/commit.c:1367
-#: builtin/commit.c:1368 builtin/commit.c:1369 builtin/tag.c:468
+#: builtin/branch.c:805 builtin/branch.c:811 builtin/branch.c:832
+#: builtin/branch.c:838 builtin/commit.c:1433 builtin/commit.c:1434
+#: builtin/commit.c:1435 builtin/commit.c:1436 builtin/tag.c:468
 msgid "commit"
 msgstr "提交"
 
-#: builtin/branch.c:762 builtin/branch.c:768
+#: builtin/branch.c:806 builtin/branch.c:812
 msgid "print only branches that contain the commit"
 msgstr "只打印包含该提交的分支"
 
-#: builtin/branch.c:774
+#: builtin/branch.c:818
 msgid "Specific git-branch actions:"
 msgstr "具体的 git-branch 动作:"
 
-#: builtin/branch.c:775
+#: builtin/branch.c:819
 msgid "list both remote-tracking and local branches"
 msgstr "列出远程跟踪及本地分支"
 
-#: builtin/branch.c:777
+#: builtin/branch.c:821
 msgid "delete fully merged branch"
 msgstr "删除完全合并的分支"
 
-#: builtin/branch.c:778
+#: builtin/branch.c:822
 msgid "delete branch (even if not merged)"
 msgstr "删除分支(即使没有合并)"
 
-#: builtin/branch.c:779
+#: builtin/branch.c:823
 msgid "move/rename a branch and its reflog"
 msgstr "移动/重命名一个分支,以及它的引用日志"
 
-#: builtin/branch.c:780
+#: builtin/branch.c:824
 msgid "move/rename a branch, even if target exists"
 msgstr "移动/重命名一个分支,即使目标已存在"
 
-#: builtin/branch.c:781
+#: builtin/branch.c:825
 msgid "list branch names"
 msgstr "列出分支名"
 
-#: builtin/branch.c:782
+#: builtin/branch.c:826
 msgid "create the branch's reflog"
 msgstr "创建分支的引用日志"
 
-#: builtin/branch.c:784
+#: builtin/branch.c:828
 msgid "edit the description for the branch"
 msgstr "标记分支的描述"
 
-#: builtin/branch.c:785
+#: builtin/branch.c:829
 msgid "force creation (when already exists)"
 msgstr "强制创建(当已经存在)"
 
-#: builtin/branch.c:788
+#: builtin/branch.c:832
 msgid "print only not merged branches"
 msgstr "只打印没有合并的分支"
 
-#: builtin/branch.c:794
+#: builtin/branch.c:838
 msgid "print only merged branches"
 msgstr "只打印合并的分支"
 
-#: builtin/branch.c:798
+#: builtin/branch.c:842
 msgid "list branches in columns"
 msgstr "以列的方式显示分支"
 
-#: builtin/branch.c:811
+#: builtin/branch.c:855
 msgid "Failed to resolve HEAD as a valid ref."
 msgstr "无法将 HEAD 解析为有效引用。"
 
-#: builtin/branch.c:816 builtin/clone.c:561
+#: builtin/branch.c:860 builtin/clone.c:630
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD 没有位于 /refs/heads 之下!"
 
-#: builtin/branch.c:839
+#: builtin/branch.c:883
 msgid "--column and --verbose are incompatible"
 msgstr "--column 和 --verbose 不兼容"
 
-#: builtin/branch.c:845
+#: builtin/branch.c:889 builtin/branch.c:928
 msgid "branch name required"
 msgstr "必须提供分支名"
 
-#: builtin/branch.c:860
+#: builtin/branch.c:904
 msgid "Cannot give description to detached HEAD"
 msgstr "不能向分离头指针提供描述"
 
-#: builtin/branch.c:865
+#: builtin/branch.c:909
 msgid "cannot edit description of more than one branch"
 msgstr "不能为一个以上的分支编辑描述"
 
-#: builtin/branch.c:872
+#: builtin/branch.c:916
 #, c-format
 msgid "No commit on branch '%s' yet."
 msgstr "分支 '%s' 尚无提交。"
 
-#: builtin/branch.c:875
+#: builtin/branch.c:919
 #, c-format
 msgid "No branch named '%s'."
 msgstr "没有分支 '%s'。"
 
-#: builtin/branch.c:888
+#: builtin/branch.c:934
 msgid "too many branches for a rename operation"
 msgstr "为重命名操作提供了太多的分支名"
 
-#: builtin/branch.c:893
+#: builtin/branch.c:939
+msgid "too many branches to set new upstream"
+msgstr "为设置新上游提供了太多的分支名"
+
+#: builtin/branch.c:943
+#, c-format
+msgid ""
+"could not set upstream of HEAD to %s when it does not point to any branch."
+msgstr "无法设置 HEAD 的上游为 %s,因为 HEAD 没有指向任何分支。"
+
+#: builtin/branch.c:946 builtin/branch.c:968 builtin/branch.c:990
+#, c-format
+msgid "no such branch '%s'"
+msgstr "没有此分支 '%s'"
+
+#: builtin/branch.c:950
 #, c-format
 msgid "branch '%s' does not exist"
 msgstr "分支 '%s' 不存在"
 
-#: builtin/branch.c:905
+#: builtin/branch.c:962
+msgid "too many branches to unset upstream"
+msgstr "为取消上游设置操作提供了太多的分支名"
+
+#: builtin/branch.c:966
+msgid "could not unset upstream of HEAD when it does not point to any branch."
+msgstr "无法取消 HEAD 的上游设置因为它没有指向一个分支"
+
+#: builtin/branch.c:972
 #, c-format
 msgid "Branch '%s' has no upstream information"
 msgstr "分支 '%s' 没有上游信息"
 
-#: builtin/branch.c:920
+#: builtin/branch.c:987
+msgid "it does not make sense to create 'HEAD' manually"
+msgstr "手工创建 'HEAD' 没有意义"
+
+#: builtin/branch.c:993
 msgid "-a and -r options to 'git branch' do not make sense with a branch name"
 msgstr "'git branch' 的 -a 和 -r 选项带一个分支名参数没有意义"
 
-#: builtin/branch.c:923
+#: builtin/branch.c:996
 #, c-format
 msgid ""
 "The --set-upstream flag is deprecated and will be removed. Consider using --"
@@ -2609,7 +2925,7 @@ msgid ""
 msgstr ""
 "选项 --set-upstream 已弃用并将被移除。考虑使用 --track 或 --set-upstream-to\n"
 
-#: builtin/branch.c:940
+#: builtin/branch.c:1013
 #, c-format
 msgid ""
 "\n"
@@ -2620,12 +2936,12 @@ msgstr ""
 "如果你想用 '%s' 跟踪 '%s', 这么做:\n"
 "\n"
 
-#: builtin/branch.c:941
+#: builtin/branch.c:1014
 #, c-format
 msgid "    git branch -d %s\n"
 msgstr "    git branch -d %s\n"
 
-#: builtin/branch.c:942
+#: builtin/branch.c:1015
 #, c-format
 msgid "    git branch --set-upstream-to %s\n"
 msgstr "    git branch --set-upstream-to %s\n"
@@ -2643,43 +2959,43 @@ msgstr "需要一个版本库来创建包。"
 msgid "Need a repository to unbundle."
 msgstr "需要一个版本库来解包。"
 
-#: builtin/cat-file.c:247
+#: builtin/cat-file.c:285
 msgid "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"
 msgstr "git cat-file (-t|-s|-e|-p|<类型>|--textconv) <对象>"
 
-#: builtin/cat-file.c:248
+#: builtin/cat-file.c:286
 msgid "git cat-file (--batch|--batch-check) < <list_of_objects>"
 msgstr "git cat-file (--batch|--batch-check) < <对象列表>"
 
-#: builtin/cat-file.c:266
+#: builtin/cat-file.c:323
 msgid "<type> can be one of: blob, tree, commit, tag"
 msgstr "<类型> 可以是其中之一:blob、tree、commit、tag"
 
-#: builtin/cat-file.c:267
+#: builtin/cat-file.c:324
 msgid "show object type"
 msgstr "显示对象类型"
 
-#: builtin/cat-file.c:268
+#: builtin/cat-file.c:325
 msgid "show object size"
 msgstr "显示对象大小"
 
-#: builtin/cat-file.c:270
+#: builtin/cat-file.c:327
 msgid "exit with zero when there's no error"
 msgstr "当没有错误时退出并返回零"
 
-#: builtin/cat-file.c:271
+#: builtin/cat-file.c:328
 msgid "pretty-print object's content"
 msgstr "美观地打印对象的内容"
 
-#: builtin/cat-file.c:273
+#: builtin/cat-file.c:330
 msgid "for blob objects, run textconv on object's content"
 msgstr "对于数据(blob)对象,对其内容执行 textconv"
 
-#: builtin/cat-file.c:275
+#: builtin/cat-file.c:332
 msgid "show info and content of objects fed from the standard input"
 msgstr "显示从标准输入提供的对象的信息和内容"
 
-#: builtin/cat-file.c:278
+#: builtin/cat-file.c:335
 msgid "show info about objects fed from the standard input"
 msgstr "显示从标准输入提供的对象的信息"
 
@@ -2707,30 +3023,55 @@ msgstr "从标准输入读出文件名"
 msgid "input paths are terminated by a null character"
 msgstr "输入路径以null字符终止"
 
-#: builtin/check-ignore.c:18 builtin/checkout.c:1012 builtin/gc.c:177
+#: builtin/check-ignore.c:18 builtin/checkout.c:1054 builtin/gc.c:177
 msgid "suppress progress reporting"
 msgstr "不显示进度报告"
 
-#: builtin/check-ignore.c:151
+#: builtin/check-ignore.c:26
+msgid "show non-matching input paths"
+msgstr "显示未匹配的输入路径"
+
+#: builtin/check-ignore.c:143
 msgid "cannot specify pathnames with --stdin"
 msgstr "不能同时提供路径及 --stdin 参数"
 
-#: builtin/check-ignore.c:154
+#: builtin/check-ignore.c:146
 msgid "-z only makes sense with --stdin"
 msgstr "-z 需要和 --stdin 参数共用才有意义"
 
-#: builtin/check-ignore.c:156
+#: builtin/check-ignore.c:148
 msgid "no path specified"
 msgstr "未指定路径"
 
-#: builtin/check-ignore.c:160
+#: builtin/check-ignore.c:152
 msgid "--quiet is only valid with a single pathname"
 msgstr "参数 --quiet 只在提供一个路径名时有效"
 
-#: builtin/check-ignore.c:162
+#: builtin/check-ignore.c:154
 msgid "cannot have both --quiet and --verbose"
 msgstr "不能同时提供 --quiet 和 --verbose 参数"
 
+#: builtin/check-ignore.c:157
+msgid "--non-matching is only valid with --verbose"
+msgstr "--non-matching 选项只在使用 --verbose 时有效"
+
+#: builtin/check-mailmap.c:8
+msgid "git check-mailmap [options] <contact>..."
+msgstr "git check-mailmap [选项] <联系地址>..."
+
+#: builtin/check-mailmap.c:13
+msgid "also read contacts from stdin"
+msgstr "还从标准输入读取联系地址"
+
+#: builtin/check-mailmap.c:24
+#, c-format
+msgid "unable to parse contact: %s"
+msgstr "不能解析联系地址:%s"
+
+#: builtin/check-mailmap.c:47
+msgid "no contacts specified"
+msgstr "未指定联系地址"
+
 #: builtin/checkout-index.c:126
 msgid "git checkout-index [options] [--] [<file>...]"
 msgstr "git checkout-index [选项] [--] [<文件>...]"
@@ -2783,107 +3124,107 @@ msgstr "git checkout [选项] <分支>"
 msgid "git checkout [options] [<branch>] -- <file>..."
 msgstr "git checkout [选项] [<分支>] -- <文件>..."
 
-#: builtin/checkout.c:116 builtin/checkout.c:149
+#: builtin/checkout.c:117 builtin/checkout.c:150
 #, c-format
 msgid "path '%s' does not have our version"
 msgstr "路径 '%s' 没有我们的版本"
 
-#: builtin/checkout.c:118 builtin/checkout.c:151
+#: builtin/checkout.c:119 builtin/checkout.c:152
 #, c-format
 msgid "path '%s' does not have their version"
 msgstr "路径 '%s' 没有他们的版本"
 
-#: builtin/checkout.c:134
+#: builtin/checkout.c:135
 #, c-format
 msgid "path '%s' does not have all necessary versions"
 msgstr "路径 '%s' 没有全部必须的版本"
 
-#: builtin/checkout.c:178
+#: builtin/checkout.c:179
 #, c-format
 msgid "path '%s' does not have necessary versions"
 msgstr "路径 '%s' 没有必须的版本"
 
-#: builtin/checkout.c:195
+#: builtin/checkout.c:196
 #, c-format
 msgid "path '%s': cannot merge"
 msgstr "path '%s':无法合并"
 
-#: builtin/checkout.c:212
+#: builtin/checkout.c:213
 #, c-format
 msgid "Unable to add merge result for '%s'"
 msgstr "无法为 '%s' 添加合并结果"
 
-#: builtin/checkout.c:236 builtin/checkout.c:239 builtin/checkout.c:242
-#: builtin/checkout.c:245
+#: builtin/checkout.c:237 builtin/checkout.c:240 builtin/checkout.c:243
+#: builtin/checkout.c:246
 #, c-format
 msgid "'%s' cannot be used with updating paths"
 msgstr "'%s' 不能在更新路径时使用"
 
-#: builtin/checkout.c:248 builtin/checkout.c:251
+#: builtin/checkout.c:249 builtin/checkout.c:252
 #, c-format
 msgid "'%s' cannot be used with %s"
 msgstr "'%s' 不能和 %s 同时使用"
 
-#: builtin/checkout.c:254
+#: builtin/checkout.c:255
 #, c-format
 msgid "Cannot update paths and switch to branch '%s' at the same time."
 msgstr "不能同时更新路径并切换到分支'%s'。"
 
-#: builtin/checkout.c:265 builtin/checkout.c:426
+#: builtin/checkout.c:266 builtin/checkout.c:458
 msgid "corrupt index file"
 msgstr "损坏的索引文件"
 
-#: builtin/checkout.c:295 builtin/checkout.c:302
+#: builtin/checkout.c:329 builtin/checkout.c:336
 #, c-format
 msgid "path '%s' is unmerged"
 msgstr "路径 '%s' 未合并"
 
-#: builtin/checkout.c:448
+#: builtin/checkout.c:480
 msgid "you need to resolve your current index first"
 msgstr "您需要先解决当前索引的冲突"
 
-#: builtin/checkout.c:569
+#: builtin/checkout.c:601
 #, c-format
 msgid "Can not do reflog for '%s'\n"
 msgstr "不能对 '%s' 执行 reflog 操作\n"
 
-#: builtin/checkout.c:602
+#: builtin/checkout.c:639
 msgid "HEAD is now at"
 msgstr "HEAD 目前位于"
 
-#: builtin/checkout.c:609
+#: builtin/checkout.c:646
 #, c-format
 msgid "Reset branch '%s'\n"
 msgstr "重置分支 '%s'\n"
 
-#: builtin/checkout.c:612
+#: builtin/checkout.c:649
 #, c-format
 msgid "Already on '%s'\n"
 msgstr "已经位于 '%s'\n"
 
-#: builtin/checkout.c:616
+#: builtin/checkout.c:653
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
 msgstr "切换并重置分支 '%s'\n"
 
-#: builtin/checkout.c:618 builtin/checkout.c:955
+#: builtin/checkout.c:655 builtin/checkout.c:997
 #, c-format
 msgid "Switched to a new branch '%s'\n"
 msgstr "切换到一个新分支 '%s'\n"
 
-#: builtin/checkout.c:620
+#: builtin/checkout.c:657
 #, c-format
 msgid "Switched to branch '%s'\n"
 msgstr "切换到分支 '%s'\n"
 
 #  译者:注意保持前导空格
-#: builtin/checkout.c:676
+#: builtin/checkout.c:713
 #, c-format
 msgid " ... and %d more.\n"
 msgstr " ... 及其它 %d 个。\n"
 
 #. The singular version
-#: builtin/checkout.c:682
+#: builtin/checkout.c:719
 #, c-format
 msgid ""
 "Warning: you are leaving %d commit behind, not connected to\n"
@@ -2904,7 +3245,7 @@ msgstr[1] ""
 "\n"
 "%s\n"
 
-#: builtin/checkout.c:700
+#: builtin/checkout.c:737
 #, c-format
 msgid ""
 "If you want to keep them by creating a new branch, this may be a good time\n"
@@ -2919,132 +3260,136 @@ msgstr ""
 " git branch new_branch_name %s\n"
 "\n"
 
-#: builtin/checkout.c:730
+#: builtin/checkout.c:767
 msgid "internal error in revision walk"
 msgstr "在版本遍历时遇到内部错误"
 
-#: builtin/checkout.c:734
+#: builtin/checkout.c:771
 msgid "Previous HEAD position was"
 msgstr "之前的 HEAD 位置是"
 
-#: builtin/checkout.c:761 builtin/checkout.c:950
+#: builtin/checkout.c:798 builtin/checkout.c:992
 msgid "You are on a branch yet to be born"
 msgstr "您位于一个尚未初始化的分支"
 
 #. case (1)
-#: builtin/checkout.c:886
+#: builtin/checkout.c:928
 #, c-format
 msgid "invalid reference: %s"
 msgstr "无效引用:%s"
 
 #. case (1): want a tree
-#: builtin/checkout.c:925
+#: builtin/checkout.c:967
 #, c-format
 msgid "reference is not a tree: %s"
 msgstr "引用不是一个树:%s"
 
-#: builtin/checkout.c:964
+#: builtin/checkout.c:1006
 msgid "paths cannot be used with switching branches"
 msgstr "路径不能和切换分支同时使用"
 
-#: builtin/checkout.c:967 builtin/checkout.c:971
+#: builtin/checkout.c:1009 builtin/checkout.c:1013
 #, c-format
 msgid "'%s' cannot be used with switching branches"
 msgstr "'%s' 不能和切换分支同时使用"
 
-#: builtin/checkout.c:975 builtin/checkout.c:978 builtin/checkout.c:983
-#: builtin/checkout.c:986
+#: builtin/checkout.c:1017 builtin/checkout.c:1020 builtin/checkout.c:1025
+#: builtin/checkout.c:1028
 #, c-format
 msgid "'%s' cannot be used with '%s'"
 msgstr "'%s' 不能和 '%s' 同时使用"
 
-#: builtin/checkout.c:991
+#: builtin/checkout.c:1033
 #, c-format
 msgid "Cannot switch branch to a non-commit '%s'"
 msgstr "不能切换分支到一个非提交 '%s'"
 
-#: builtin/checkout.c:1013 builtin/checkout.c:1015 builtin/clone.c:89
+#: builtin/checkout.c:1055 builtin/checkout.c:1057 builtin/clone.c:89
 #: builtin/remote.c:169 builtin/remote.c:171
 msgid "branch"
 msgstr "分支"
 
-#: builtin/checkout.c:1014
+#: builtin/checkout.c:1056
 msgid "create and checkout a new branch"
 msgstr "创建并检出一个新的分支"
 
-#: builtin/checkout.c:1016
+#: builtin/checkout.c:1058
 msgid "create/reset and checkout a branch"
 msgstr "创建/重置并检出一个分支"
 
-#: builtin/checkout.c:1017
+#: builtin/checkout.c:1059
 msgid "create reflog for new branch"
 msgstr "为新的分支创建引用日志"
 
-#: builtin/checkout.c:1018
+#: builtin/checkout.c:1060
 msgid "detach the HEAD at named commit"
 msgstr "成为指向该提交的分离头指针"
 
-#: builtin/checkout.c:1019
+#: builtin/checkout.c:1061
 msgid "set upstream info for new branch"
 msgstr "为新的分支设置上游信息"
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new branch"
 msgstr "新分支"
 
-#: builtin/checkout.c:1021
+#: builtin/checkout.c:1063
 msgid "new unparented branch"
 msgstr "新的没有父提交的分支"
 
-#: builtin/checkout.c:1022
+#: builtin/checkout.c:1064
 msgid "checkout our version for unmerged files"
 msgstr "对尚未合并的文件检出我们的版本"
 
-#: builtin/checkout.c:1024
+#: builtin/checkout.c:1066
 msgid "checkout their version for unmerged files"
 msgstr "对尚未合并的文件检出他们的版本"
 
-#: builtin/checkout.c:1026
+#: builtin/checkout.c:1068
 msgid "force checkout (throw away local modifications)"
 msgstr "强制检出(丢弃本地修改)"
 
-#: builtin/checkout.c:1027
+#: builtin/checkout.c:1069
 msgid "perform a 3-way merge with the new branch"
 msgstr "和新的分支执行三路合并"
 
-#: builtin/checkout.c:1028 builtin/merge.c:215
+#: builtin/checkout.c:1070 builtin/merge.c:232
 msgid "update ignored files (default)"
 msgstr "更新忽略的文件(默认)"
 
-#: builtin/checkout.c:1029 builtin/log.c:1147 parse-options.h:245
+#: builtin/checkout.c:1071 builtin/log.c:1208 parse-options.h:249
 msgid "style"
 msgstr "风格"
 
-#: builtin/checkout.c:1030
+#: builtin/checkout.c:1072
 msgid "conflict style (merge or diff3)"
 msgstr "冲突输出风格(merge 或 diff3)"
 
-#: builtin/checkout.c:1033
+#: builtin/checkout.c:1075
+msgid "do not limit pathspecs to sparse entries only"
+msgstr "对路径不做稀疏检出的限制"
+
+#: builtin/checkout.c:1077
 msgid "second guess 'git checkout no-such-branch'"
 msgstr "再者猜测'git checkout no-such-branch'"
 
-#: builtin/checkout.c:1057
+#: builtin/checkout.c:1101
 msgid "-b, -B and --orphan are mutually exclusive"
 msgstr "-b、-B 和 --orphan 是互斥的"
 
-#: builtin/checkout.c:1074
+#: builtin/checkout.c:1118
 msgid "--track needs a branch name"
 msgstr "--track 需要一个分支名"
 
-#: builtin/checkout.c:1081
+#: builtin/checkout.c:1125
 msgid "Missing branch name; try -b"
 msgstr "缺少分支名;尝试 -b"
 
-#: builtin/checkout.c:1116
+#: builtin/checkout.c:1160
 msgid "invalid path specification"
 msgstr "无效的路径规格"
 
-#: builtin/checkout.c:1123
+#: builtin/checkout.c:1167
 #, c-format
 msgid ""
 "Cannot update paths and switch to branch '%s' at the same time.\n"
@@ -3053,99 +3398,202 @@ msgstr ""
 "不能同时更新路径并切换到分支'%s'。\n"
 "您是想要检出 '%s' 但其未能解析为提交么?"
 
-#: builtin/checkout.c:1128
+#: builtin/checkout.c:1172
 #, c-format
 msgid "git checkout: --detach does not take a path argument '%s'"
 msgstr "git checkout:--detach 不能接收路径参数 '%s'"
 
-#: builtin/checkout.c:1132
+#: builtin/checkout.c:1176
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
 msgstr ""
 "git checkout:在从索引检出时,--ours/--theirs、--force 和 --merge 不兼容。"
 
-#: builtin/clean.c:20
-msgid "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
-msgstr "git clean [-d] [-f] [-n] [-q] [-e <模式>] [-x | -X] [--] <路径>..."
+#: builtin/clean.c:25
+msgid ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."
+msgstr ""
+"git clean [-d] [-f] [-i] [-n] [-q] [-e <模式>] [-x | -X] [--] <路径>..."
 
-#: builtin/clean.c:24
+#: builtin/clean.c:29
 #, c-format
 msgid "Removing %s\n"
 msgstr "正删除 %s\n"
 
-#: builtin/clean.c:25
+#: builtin/clean.c:30
 #, c-format
 msgid "Would remove %s\n"
 msgstr "将删除 %s\n"
 
-#: builtin/clean.c:26
+#: builtin/clean.c:31
 #, c-format
 msgid "Skipping repository %s\n"
 msgstr "忽略版本库 %s\n"
 
-#: builtin/clean.c:27
+#: builtin/clean.c:32
 #, c-format
 msgid "Would skip repository %s\n"
 msgstr "将忽略版本库 %s\n"
 
-#: builtin/clean.c:28
+#: builtin/clean.c:33
 #, c-format
 msgid "failed to remove %s"
 msgstr "无法删除 %s"
 
-#: builtin/clean.c:160
+#: builtin/clean.c:293
+msgid ""
+"Prompt help:\n"
+"1          - select a numbered item\n"
+"foo        - select item based on unique prefix\n"
+"           - (empty) select nothing"
+msgstr ""
+"帮助:\n"
+"1          - 通过编号选择一个选项\n"
+"foo        - 通过标题选择一个选项\n"
+"           - (空)什么也不选择"
+
+#: builtin/clean.c:297
+msgid ""
+"Prompt help:\n"
+"1          - select a single item\n"
+"3-5        - select a range of items\n"
+"2-3,6-9    - select multiple ranges\n"
+"foo        - select item based on unique prefix\n"
+"-...       - unselect specified items\n"
+"*          - choose all items\n"
+"           - (empty) finish selecting"
+msgstr ""
+"帮助:\n"
+"1          - 选择一个选项\n"
+"3-5        - 选择一个范围内的所有选项\n"
+"2-3,6-9    - 选择多个范围内的所有选项\n"
+"foo        - 通过标题选择一个选项\n"
+"-...       - 反选特定的选项\n"
+"*          - 选择所有选项\n"
+"           - (空)结束选择"
+
+#: builtin/clean.c:515
+#, c-format
+msgid "Huh (%s)?"
+msgstr "嗯(%s)?"
+
+#: builtin/clean.c:658
+#, c-format
+msgid "Input ignore patterns>> "
+msgstr "输入模版以排除条目>> "
+
+#: builtin/clean.c:695
+#, c-format
+msgid "WARNING: Cannot find items matched by: %s"
+msgstr "警告:无法找到和 %s 匹配的条目"
+
+#: builtin/clean.c:716
+msgid "Select items to delete"
+msgstr "选择要删除的条目"
+
+#: builtin/clean.c:756
+#, c-format
+msgid "remove %s? "
+msgstr "删除 %s?"
+
+#: builtin/clean.c:781
+msgid "Bye."
+msgstr "再见。"
+
+#: builtin/clean.c:789
+msgid ""
+"clean               - start cleaning\n"
+"filter by pattern   - exclude items from deletion\n"
+"select by numbers   - select items to be deleted by numbers\n"
+"ask each            - confirm each deletion (like \"rm -i\")\n"
+"quit                - stop cleaning\n"
+"help                - this screen\n"
+"?                   - help for prompt selection"
+msgstr ""
+"clean               - 开始清理\n"
+"filter by pattern   - 通过模版排除要删除的条目\n"
+"select by numbers   - 通过数字选择要删除的条目\n"
+"ask each            - 针对删除逐一询问(就像 \"rm -i\")\n"
+"quit                - 停止删除并退出\n"
+"help                - 显示本帮助\n"
+"?                   - 显示如何在提示符下选择的帮助"
+
+#: builtin/clean.c:816
+msgid "*** Commands ***"
+msgstr "*** 命令 ***"
+
+#: builtin/clean.c:817
+msgid "What now"
+msgstr "请选择"
+
+#: builtin/clean.c:825
+msgid "Would remove the following item:"
+msgid_plural "Would remove the following items:"
+msgstr[0] "将删除如下条目:"
+msgstr[1] "将删除如下条目:"
+
+#: builtin/clean.c:842
+msgid "No more files to clean, exiting."
+msgstr "没有要清理的文件,退出。"
+
+#: builtin/clean.c:874
 msgid "do not print names of files removed"
 msgstr "不打印删除文件的名称"
 
-#: builtin/clean.c:162
+#: builtin/clean.c:876
 msgid "force"
 msgstr "强制"
 
-#: builtin/clean.c:164
+#: builtin/clean.c:877
+msgid "interactive cleaning"
+msgstr "交互式清除"
+
+#: builtin/clean.c:879
 msgid "remove whole directories"
 msgstr "删除整个目录"
 
-#: builtin/clean.c:165 builtin/describe.c:413 builtin/grep.c:717
-#: builtin/ls-files.c:494 builtin/name-rev.c:231 builtin/show-ref.c:182
+#: builtin/clean.c:880 builtin/describe.c:420 builtin/grep.c:716
+#: builtin/ls-files.c:493 builtin/name-rev.c:315 builtin/show-ref.c:186
 msgid "pattern"
 msgstr "模式"
 
-#: builtin/clean.c:166
+#: builtin/clean.c:881
 msgid "add <pattern> to ignore rules"
 msgstr "添加<模式>到忽略规则"
 
-#: builtin/clean.c:167
+#: builtin/clean.c:882
 msgid "remove ignored files, too"
 msgstr "也删除忽略的文件"
 
-#: builtin/clean.c:169
+#: builtin/clean.c:884
 msgid "remove only ignored files"
 msgstr "只删除忽略的文件"
 
-#: builtin/clean.c:187
+#: builtin/clean.c:902
 msgid "-x and -X cannot be used together"
 msgstr "-x 和 -X 不能同时使用"
 
-#: builtin/clean.c:191
+#: builtin/clean.c:906
 msgid ""
-"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+"clean.requireForce set to true and neither -i, -n nor -f given; refusing to "
+"clean"
 msgstr ""
-"clean.requireForce 设置为 true 且未提供 -n 或 -f 选项,拒绝执行清理动作"
+"clean.requireForce 设置为 true 且未提供 -i、-n 或 -f 选项,拒绝执行清理动作"
 
-#: builtin/clean.c:194
+#: builtin/clean.c:909
 msgid ""
-"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
-"clean"
+"clean.requireForce defaults to true and neither -i, -n nor -f given; "
+"refusing to clean"
 msgstr ""
-"clean.requireForce 默认为 true 且未提供 -n 或 -f 选项,拒绝执行清理动作"
+"clean.requireForce 默认为 true 且未提供 -i、-n 或 -f 选项,拒绝执行清理动作"
 
 #: builtin/clone.c:36
 msgid "git clone [options] [--] <repo> [<dir>]"
 msgstr "git clone [选项] [--] <版本库> [<路径>]"
 
-#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:212
-#: builtin/push.c:436
+#: builtin/clone.c:64 builtin/fetch.c:82 builtin/merge.c:229
+#: builtin/push.c:462
 msgid "force progress reporting"
 msgstr "强制显示进度报告"
 
@@ -3205,7 +3653,7 @@ msgstr "检出<分支>而不是远程HEAD"
 msgid "path to git-upload-pack on the remote"
 msgstr "远程 git-upload-pack 路径"
 
-#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:662
+#: builtin/clone.c:93 builtin/fetch.c:83 builtin/grep.c:661
 msgid "depth"
 msgstr "深度"
 
@@ -3233,122 +3681,154 @@ msgstr "key=value"
 msgid "set config inside the new repository"
 msgstr "在新版本库中设置配置信息"
 
-#: builtin/clone.c:243
+#: builtin/clone.c:253
 #, c-format
-msgid "reference repository '%s' is not a local directory."
-msgstr "å¼\95ç\94¨ç\89\88æ\9c¬åº\93 '%s' ä¸\8dæ\98¯ä¸\80个æ\9c¬å\9c°ç\9b®å½\95。"
+msgid "reference repository '%s' is not a local repository."
+msgstr "å¼\95ç\94¨ç\89\88æ\9c¬åº\93 '%s' ä¸\8dæ\98¯ä¸\80个æ\9c¬å\9c°ç\89\88æ\9c¬åº\93。"
 
-#: builtin/clone.c:306
+#: builtin/clone.c:316
 #, c-format
 msgid "failed to create directory '%s'"
 msgstr "无法创建目录 '%s'"
 
-#: builtin/clone.c:308 builtin/diff.c:77
+#: builtin/clone.c:318 builtin/diff.c:77
 #, c-format
 msgid "failed to stat '%s'"
 msgstr "无法枚举 '%s' 状态"
 
-#: builtin/clone.c:310
+#: builtin/clone.c:320
 #, c-format
 msgid "%s exists and is not a directory"
 msgstr "%s 存在且不是一个目录"
 
-#: builtin/clone.c:324
+#: builtin/clone.c:334
 #, c-format
 msgid "failed to stat %s\n"
 msgstr "无法枚举 %s 状态\n"
 
-#: builtin/clone.c:346
+#: builtin/clone.c:356
 #, c-format
 msgid "failed to create link '%s'"
 msgstr "无法创建链接 '%s'"
 
-#: builtin/clone.c:350
+#: builtin/clone.c:360
 #, c-format
 msgid "failed to copy file to '%s'"
 msgstr "无法拷贝文件至 '%s'"
 
-#: builtin/clone.c:373
+#: builtin/clone.c:383
 #, c-format
 msgid "done.\n"
 msgstr "完成。\n"
 
-#: builtin/clone.c:443
+#: builtin/clone.c:396
+msgid ""
+"Clone succeeded, but checkout failed.\n"
+"You can inspect what was checked out with 'git status'\n"
+"and retry the checkout with 'git checkout -f HEAD'\n"
+msgstr ""
+"克隆成功,但是检出失败。\n"
+"您可以通过 'git status' 检查哪些已被检出,然后使用命令\n"
+"'git checkout -f HEAD' 重试\n"
+
+#: builtin/clone.c:475
 #, c-format
 msgid "Could not find remote branch %s to clone."
 msgstr "不能发现要克隆的远程分支 %s。"
 
-#: builtin/clone.c:552
+#: builtin/clone.c:555
+#, c-format
+msgid "Checking connectivity... "
+msgstr "检查连接... "
+
+#: builtin/clone.c:558
+msgid "remote did not send all necessary objects"
+msgstr "远程没有发送所有必须的对象"
+
+#: builtin/clone.c:560
+#, c-format
+msgid "done\n"
+msgstr "完成\n"
+
+#: builtin/clone.c:621
 msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
 msgstr "远程 HEAD 指向一个不存在的引用,无法检出。\n"
 
-#: builtin/clone.c:690
+#: builtin/clone.c:652
+msgid "unable to checkout working tree"
+msgstr "不能检出工作区"
+
+#: builtin/clone.c:760
 msgid "Too many arguments."
 msgstr "太多参数。"
 
-#: builtin/clone.c:694
+#: builtin/clone.c:764
 msgid "You must specify a repository to clone."
 msgstr "您必须指定一个版本库来克隆。"
 
-#: builtin/clone.c:705
+#: builtin/clone.c:775
 #, c-format
 msgid "--bare and --origin %s options are incompatible."
 msgstr "--bare 和 --origin %s 选项不兼容。"
 
-#: builtin/clone.c:708
+#: builtin/clone.c:778
 msgid "--bare and --separate-git-dir are incompatible."
 msgstr "--bare 和 --separate-git-dir 选项不兼容。"
 
-#: builtin/clone.c:721
+#: builtin/clone.c:791
 #, c-format
 msgid "repository '%s' does not exist"
 msgstr "版本库 '%s' 不存在"
 
-#: builtin/clone.c:726
+#: builtin/clone.c:796
 msgid "--depth is ignored in local clones; use file:// instead."
 msgstr "--depth 在本地克隆被忽略,改为 file:// 协议试试。"
 
-#: builtin/clone.c:736
+#: builtin/clone.c:798
+msgid "--local is ignored"
+msgstr "--local 被忽略"
+
+#: builtin/clone.c:808
 #, c-format
 msgid "destination path '%s' already exists and is not an empty directory."
 msgstr "目标路径 '%s' 已经存在,并且不是一个空目录。"
 
-#: builtin/clone.c:746
+#: builtin/clone.c:818
 #, c-format
 msgid "working tree '%s' already exists."
 msgstr "工作区 '%s' 已经存在。"
 
-#: builtin/clone.c:759 builtin/clone.c:771
+#: builtin/clone.c:831 builtin/clone.c:843
 #, c-format
 msgid "could not create leading directories of '%s'"
 msgstr "不能为 '%s' 创建先导目录"
 
-#: builtin/clone.c:762
+#: builtin/clone.c:834
 #, c-format
 msgid "could not create work tree dir '%s'."
 msgstr "不能为 '%s' 创建工作区目录。"
 
-#: builtin/clone.c:781
+#: builtin/clone.c:853
 #, c-format
 msgid "Cloning into bare repository '%s'...\n"
 msgstr "克隆到裸版本库 '%s'...\n"
 
-#: builtin/clone.c:783
+#: builtin/clone.c:855
 #, c-format
 msgid "Cloning into '%s'...\n"
 msgstr "正克隆到 '%s'...\n"
 
-#: builtin/clone.c:818
+#: builtin/clone.c:890
 #, c-format
 msgid "Don't know how to clone %s"
 msgstr "不知道如何克隆 %s"
 
-#: builtin/clone.c:867
+#: builtin/clone.c:942
 #, c-format
 msgid "Remote branch %s not found in upstream %s"
 msgstr "远程分支 %s 在上游 %s 未发现"
 
-#: builtin/clone.c:874
+#: builtin/clone.c:949
 msgid "You appear to have cloned an empty repository."
 msgstr "您似乎克隆了一个空版本库。"
 
@@ -3384,15 +3864,15 @@ msgstr "两列之间的填充空间"
 msgid "--command must be the first argument"
 msgstr "--command 必须是第一个参数"
 
-#: builtin/commit.c:34
+#: builtin/commit.c:35
 msgid "git commit [options] [--] <pathspec>..."
 msgstr "git commit [选项] [--] <路径匹配>..."
 
-#: builtin/commit.c:39
+#: builtin/commit.c:40
 msgid "git status [options] [--] <pathspec>..."
 msgstr "git status [选项] [--] <路径匹配>..."
 
-#: builtin/commit.c:44
+#: builtin/commit.c:45
 msgid ""
 "Your name and email address were configured automatically based\n"
 "on your username and hostname. Please check that they are accurate.\n"
@@ -3415,7 +3895,7 @@ msgstr ""
 "\n"
 "    git commit --amend --reset-author\n"
 
-#: builtin/commit.c:56
+#: builtin/commit.c:57
 msgid ""
 "You asked to amend the most recent commit, but doing so would make\n"
 "it empty. You can repeat your command with --allow-empty, or you can\n"
@@ -3424,109 +3904,127 @@ msgstr ""
 "您要修补最近的提交,但这么做会让它成为空提交。您可以重复您的命令并带上\n"
 "--allow-empty 选项,或者您可用命令 \"git reset HEAD^\" 整个删除该提交。\n"
 
-#: builtin/commit.c:61
+#: builtin/commit.c:62
 msgid ""
 "The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
 "If you wish to commit it anyway, use:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"Otherwise, please use 'git reset'\n"
 msgstr ""
 "之前的拣选操作现在是一个空提交,可能是由冲突解决导致的。如果您无论如何\n"
 "也要提交,使用命令:\n"
 "\n"
 "    git commit --allow-empty\n"
 "\n"
-"否则,请使用命令 'git reset'\n"
 
-#: builtin/commit.c:258
+#: builtin/commit.c:69
+msgid "Otherwise, please use 'git reset'\n"
+msgstr "否则,请使用命令 'git reset'\n"
+
+#: builtin/commit.c:72
+msgid ""
+"If you wish to skip this commit, use:\n"
+"\n"
+"    git reset\n"
+"\n"
+"Then \"git cherry-pick --continue\" will resume cherry-picking\n"
+"the remaining commits.\n"
+msgstr ""
+"如果您想跳过这个提交,使用命令:\n"
+"\n"
+"    git reset\n"
+"\n"
+"然后执行 \"git cherry-pick --continue\" 继续对其余提交执行拣选\n"
+"操作。\n"
+
+#: builtin/commit.c:279
 msgid "failed to unpack HEAD tree object"
 msgstr "无法解包 HEAD 树对象"
 
-#: builtin/commit.c:300
+#: builtin/commit.c:321
 msgid "unable to create temporary index"
 msgstr "不能创建临时索引"
 
-#: builtin/commit.c:306
+#: builtin/commit.c:327
 msgid "interactive add failed"
 msgstr "交互式添加失败"
 
-#: builtin/commit.c:339 builtin/commit.c:360 builtin/commit.c:410
+#: builtin/commit.c:360 builtin/commit.c:381 builtin/commit.c:431
 msgid "unable to write new_index file"
 msgstr "无法写 new_index 文件"
 
-#: builtin/commit.c:391
+#: builtin/commit.c:412
 msgid "cannot do a partial commit during a merge."
 msgstr "在合并过程中不能做部分提交。"
 
-#: builtin/commit.c:393
+#: builtin/commit.c:414
 msgid "cannot do a partial commit during a cherry-pick."
 msgstr "在拣选过程中不能做部分提交。"
 
-#: builtin/commit.c:403
+#: builtin/commit.c:424
 msgid "cannot read the index"
 msgstr "无法读取索引"
 
-#: builtin/commit.c:423
+#: builtin/commit.c:444
 msgid "unable to write temporary index file"
 msgstr "无法写临时索引文件"
 
-#: builtin/commit.c:511 builtin/commit.c:517
+#: builtin/commit.c:535 builtin/commit.c:541
 #, c-format
 msgid "invalid commit: %s"
 msgstr "无效的提交:%s"
 
-#: builtin/commit.c:540
+#: builtin/commit.c:563
 msgid "malformed --author parameter"
 msgstr "非法的 --author 参数"
 
-#: builtin/commit.c:560
+#: builtin/commit.c:583
 #, c-format
 msgid "Malformed ident string: '%s'"
 msgstr "非法的身份字符串:'%s'"
 
-#: builtin/commit.c:598 builtin/commit.c:631 builtin/commit.c:954
+#: builtin/commit.c:621 builtin/commit.c:654 builtin/commit.c:982
 #, c-format
 msgid "could not lookup commit %s"
 msgstr "不能查询提交 %s"
 
-#: builtin/commit.c:610 builtin/shortlog.c:272
+#: builtin/commit.c:633 builtin/shortlog.c:271
 #, c-format
 msgid "(reading log message from standard input)\n"
 msgstr "(正从标准输入中读取日志信息)\n"
 
-#: builtin/commit.c:612
+#: builtin/commit.c:635
 msgid "could not read log from standard input"
 msgstr "不能从标准输入中读取日志信息"
 
-#: builtin/commit.c:616
+#: builtin/commit.c:639
 #, c-format
 msgid "could not read log file '%s'"
 msgstr "不能读取日志文件 '%s'"
 
-#: builtin/commit.c:622
+#: builtin/commit.c:645
 msgid "commit has empty message"
 msgstr "提交说明为空"
 
-#: builtin/commit.c:638
+#: builtin/commit.c:661
 msgid "could not read MERGE_MSG"
 msgstr "不能读取 MERGE_MSG"
 
-#: builtin/commit.c:642
+#: builtin/commit.c:665
 msgid "could not read SQUASH_MSG"
 msgstr "不能读取 SQUASH_MSG"
 
-#: builtin/commit.c:646
+#: builtin/commit.c:669
 #, c-format
 msgid "could not read '%s'"
 msgstr "不能读取 '%s'"
 
-#: builtin/commit.c:707
+#: builtin/commit.c:730
 msgid "could not write commit template"
 msgstr "不能写提交模版"
 
-#: builtin/commit.c:718
+#: builtin/commit.c:741
 #, c-format
 msgid ""
 "\n"
@@ -3540,7 +4038,7 @@ msgstr ""
 "\t%s\n"
 "然后重试。\n"
 
-#: builtin/commit.c:723
+#: builtin/commit.c:746
 #, c-format
 msgid ""
 "\n"
@@ -3554,7 +4052,7 @@ msgstr ""
 "\t%s\n"
 "然后重试。\n"
 
-#: builtin/commit.c:735
+#: builtin/commit.c:758
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
@@ -3563,7 +4061,7 @@ msgstr ""
 "请为您的变更输入提交说明。以 '%c' 开始的行将被忽略,而一个空的提交\n"
 "说明将会终止提交。\n"
 
-#: builtin/commit.c:740
+#: builtin/commit.c:763
 #, c-format
 msgid ""
 "Please enter the commit message for your changes. Lines starting\n"
@@ -3574,355 +4072,355 @@ msgstr ""
 "也可以删除它们。一个空的提交说明将会终止提交。\n"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: builtin/commit.c:753
+#: builtin/commit.c:776
 #, c-format
 msgid "%sAuthor:    %s"
 msgstr "%s作者:     %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: builtin/commit.c:760
+#: builtin/commit.c:783
 #, c-format
 msgid "%sCommitter: %s"
 msgstr "%s提交者:   %s"
 
-#: builtin/commit.c:780
+#: builtin/commit.c:803
 msgid "Cannot read index"
 msgstr "无法读取索引"
 
-#: builtin/commit.c:817
+#: builtin/commit.c:845
 msgid "Error building trees"
 msgstr "无法创建树对象"
 
-#: builtin/commit.c:832 builtin/tag.c:359
+#: builtin/commit.c:860 builtin/tag.c:359
 #, c-format
 msgid "Please supply the message using either -m or -F option.\n"
 msgstr "请使用 -m 或 -F 选项提供提交说明。\n"
 
-#: builtin/commit.c:929
+#: builtin/commit.c:957
 #, c-format
 msgid "No existing author found with '%s'"
 msgstr "没有找到匹配 '%s' 的作者"
 
-#: builtin/commit.c:944 builtin/commit.c:1138
+#: builtin/commit.c:972 builtin/commit.c:1208
 #, c-format
 msgid "Invalid untracked files mode '%s'"
 msgstr "无效的未追踪文件参数 '%s'"
 
-#: builtin/commit.c:974
+#: builtin/commit.c:1009
+msgid "--long and -z are incompatible"
+msgstr "--long 和 -z 选项不兼容"
+
+#: builtin/commit.c:1039
 msgid "Using both --reset-author and --author does not make sense"
 msgstr "同时使用 --reset-author 和 --author 没有意义"
 
-#: builtin/commit.c:985
+#: builtin/commit.c:1050
 msgid "You have nothing to amend."
 msgstr "您没有可修补的提交。"
 
-#: builtin/commit.c:988
+#: builtin/commit.c:1053
 msgid "You are in the middle of a merge -- cannot amend."
 msgstr "您正处于一个合并过程中 -- 无法修补提交。"
 
-#: builtin/commit.c:990
+#: builtin/commit.c:1055
 msgid "You are in the middle of a cherry-pick -- cannot amend."
 msgstr "您正处于一个拣选过程中 -- 无法修补提交。"
 
-#: builtin/commit.c:993
+#: builtin/commit.c:1058
 msgid "Options --squash and --fixup cannot be used together"
 msgstr "选项 --squash 和 --fixup 不能同时使用"
 
-#: builtin/commit.c:1003
+#: builtin/commit.c:1068
 msgid "Only one of -c/-C/-F/--fixup can be used."
 msgstr "只能用一个 -c/-C/-F/--fixup 选项。"
 
-#: builtin/commit.c:1005
+#: builtin/commit.c:1070
 msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
 msgstr "选项 -m 不能和 -c/-C/-F/--fixup 同时使用。"
 
-#: builtin/commit.c:1013
+#: builtin/commit.c:1078
 msgid "--reset-author can be used only with -C, -c or --amend."
 msgstr "--reset-author 只能和 -C、-c 或 --amend 同时使用。"
 
-#: builtin/commit.c:1030
+#: builtin/commit.c:1095
 msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
 msgstr "只能用一个 --include/--only/--all/--interactive/--patch 选项。"
 
-#: builtin/commit.c:1032
+#: builtin/commit.c:1097
 msgid "No paths with --include/--only does not make sense."
 msgstr "参数 --include/--only 不跟路径没有意义。"
 
-#: builtin/commit.c:1034
+#: builtin/commit.c:1099
 msgid "Clever... amending the last one with dirty index."
 msgstr "聪明... 在索引不干净下修补最后的提交。"
 
-#: builtin/commit.c:1036
+#: builtin/commit.c:1101
 msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
 msgstr "指定了明确的路径而没有使用 -i 或 -o 选项;认为是 --only paths..."
 
-#: builtin/commit.c:1046 builtin/tag.c:575
+#: builtin/commit.c:1111 builtin/tag.c:575
 #, c-format
 msgid "Invalid cleanup mode %s"
 msgstr "无效的清理模式 %s"
 
-#: builtin/commit.c:1051
+#: builtin/commit.c:1116
 msgid "Paths with -a does not make sense."
 msgstr "路径和 -a 选项同时使用没有意义。"
 
-#: builtin/commit.c:1057 builtin/commit.c:1192
-msgid "--long and -z are incompatible"
-msgstr "--long 和 -z 选项不兼容"
-
-#: builtin/commit.c:1152 builtin/commit.c:1388
+#: builtin/commit.c:1222 builtin/commit.c:1455
 msgid "show status concisely"
 msgstr "以简洁的格式显示状态"
 
-#: builtin/commit.c:1154 builtin/commit.c:1390
+#: builtin/commit.c:1224 builtin/commit.c:1457
 msgid "show branch information"
 msgstr "显示分支信息"
 
-#: builtin/commit.c:1156 builtin/commit.c:1392 builtin/push.c:426
+#: builtin/commit.c:1226 builtin/commit.c:1459 builtin/push.c:452
 msgid "machine-readable output"
 msgstr "机器可读的输出"
 
-#: builtin/commit.c:1159 builtin/commit.c:1394
+#: builtin/commit.c:1229 builtin/commit.c:1461
 msgid "show status in long format (default)"
 msgstr "以长格式显示状态(默认)"
 
-#: builtin/commit.c:1162 builtin/commit.c:1397
+#: builtin/commit.c:1232 builtin/commit.c:1464
 msgid "terminate entries with NUL"
 msgstr "条目以NUL字符结尾"
 
-#: builtin/commit.c:1164 builtin/commit.c:1400 builtin/fast-export.c:647
-#: builtin/fast-export.c:650 builtin/tag.c:459
+#: builtin/commit.c:1234 builtin/commit.c:1467 builtin/fast-export.c:667
+#: builtin/fast-export.c:670 builtin/tag.c:459
 msgid "mode"
 msgstr "模式"
 
-#: builtin/commit.c:1165 builtin/commit.c:1400
+#: builtin/commit.c:1235 builtin/commit.c:1467
 msgid "show untracked files, optional modes: all, normal, no. (Default: all)"
 msgstr "显示未跟踪的文件,“模式”的可选参数:all、normal、no。(默认:all)"
 
-#: builtin/commit.c:1168
+#: builtin/commit.c:1238
 msgid "show ignored files"
 msgstr "显示忽略的文件"
 
-#: builtin/commit.c:1169 parse-options.h:151
+#: builtin/commit.c:1239 parse-options.h:154
 msgid "when"
 msgstr "何时"
 
-#: builtin/commit.c:1170
+#: builtin/commit.c:1240
 msgid ""
 "ignore changes to submodules, optional when: all, dirty, untracked. "
 "(Default: all)"
 msgstr ""
 "忽略子模组的更改,“何时”的可选参数:all、dirty、untracked。(默认:all)"
 
-#: builtin/commit.c:1172
+#: builtin/commit.c:1242
 msgid "list untracked files in columns"
 msgstr "以列的方式显示未跟踪的文件"
 
-#: builtin/commit.c:1246
+#: builtin/commit.c:1313
 msgid "couldn't look up newly created commit"
 msgstr "无法找到新创建的提交"
 
-#: builtin/commit.c:1248
+#: builtin/commit.c:1315
 msgid "could not parse newly created commit"
 msgstr "不能解析新创建的提交"
 
-#: builtin/commit.c:1289
+#: builtin/commit.c:1356
 msgid "detached HEAD"
 msgstr "分离头指针"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: builtin/commit.c:1291
+#: builtin/commit.c:1358
 msgid " (root-commit)"
 msgstr "(根提交)"
 
-#: builtin/commit.c:1358
+#: builtin/commit.c:1425
 msgid "suppress summary after successful commit"
 msgstr "提交成功后不显示概述信息"
 
-#: builtin/commit.c:1359
+#: builtin/commit.c:1426
 msgid "show diff in commit message template"
 msgstr "在提交说明模板里显示差异"
 
-#: builtin/commit.c:1361
+#: builtin/commit.c:1428
 msgid "Commit message options"
 msgstr "提交说明选项"
 
-#: builtin/commit.c:1362 builtin/tag.c:457
+#: builtin/commit.c:1429 builtin/tag.c:457
 msgid "read message from file"
 msgstr "从文件中读取提交说明"
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "author"
 msgstr "作者"
 
-#: builtin/commit.c:1363
+#: builtin/commit.c:1430
 msgid "override author for commit"
 msgstr "提交时覆盖作者"
 
-#: builtin/commit.c:1364 builtin/gc.c:178
+#: builtin/commit.c:1431 builtin/gc.c:178
 msgid "date"
 msgstr "日期"
 
-#: builtin/commit.c:1364
+#: builtin/commit.c:1431
 msgid "override date for commit"
 msgstr "提交时覆盖日期"
 
-#: builtin/commit.c:1365 builtin/merge.c:206 builtin/notes.c:533
-#: builtin/notes.c:690 builtin/tag.c:455
+#: builtin/commit.c:1432 builtin/merge.c:223 builtin/notes.c:405
+#: builtin/notes.c:562 builtin/tag.c:455
 msgid "message"
 msgstr "说明"
 
-#: builtin/commit.c:1365
+#: builtin/commit.c:1432
 msgid "commit message"
 msgstr "提交说明"
 
-#: builtin/commit.c:1366
+#: builtin/commit.c:1433
 msgid "reuse and edit message from specified commit"
 msgstr "重用并编辑指定提交的提交说明"
 
-#: builtin/commit.c:1367
+#: builtin/commit.c:1434
 msgid "reuse message from specified commit"
 msgstr "重用指定提交的提交说明"
 
-#: builtin/commit.c:1368
+#: builtin/commit.c:1435
 msgid "use autosquash formatted message to fixup specified commit"
 msgstr "使用 autosquash 格式的提交说明用以修正指定的提交"
 
-#: builtin/commit.c:1369
+#: builtin/commit.c:1436
 msgid "use autosquash formatted message to squash specified commit"
 msgstr "使用 autosquash 格式的提交说明用以压缩至指定的提交"
 
-#: builtin/commit.c:1370
+#: builtin/commit.c:1437
 msgid "the commit is authored by me now (used with -C/-c/--amend)"
 msgstr "现在将该提交的作者改为我(和 -C/-c/--amend 参数共用)"
 
-#: builtin/commit.c:1371 builtin/log.c:1102 builtin/revert.c:109
+#: builtin/commit.c:1438 builtin/log.c:1160 builtin/revert.c:111
 msgid "add Signed-off-by:"
 msgstr "添加 Signed-off-by: 签名"
 
-#: builtin/commit.c:1372
+#: builtin/commit.c:1439
 msgid "use specified template file"
 msgstr "使用指定的模板文件"
 
-#: builtin/commit.c:1373
+#: builtin/commit.c:1440
 msgid "force edit of commit"
 msgstr "强制编辑提交"
 
 #  译者:可选值,不能翻译(或是原文中笔误,应为 mode)
-#: builtin/commit.c:1374
+#: builtin/commit.c:1441
 msgid "default"
 msgstr "default"
 
-#: builtin/commit.c:1374 builtin/tag.c:460
+#: builtin/commit.c:1441 builtin/tag.c:460
 msgid "how to strip spaces and #comments from message"
 msgstr "设置如何删除提交说明里的空格和#注释"
 
-#: builtin/commit.c:1375
+#: builtin/commit.c:1442
 msgid "include status in commit message template"
 msgstr "在提交说明模板里包含状态信息"
 
-#: builtin/commit.c:1376 builtin/merge.c:213 builtin/tag.c:461
+#: builtin/commit.c:1443 builtin/merge.c:230 builtin/tag.c:461
 msgid "key id"
 msgstr "key id"
 
-#: builtin/commit.c:1377 builtin/merge.c:214
+#: builtin/commit.c:1444 builtin/merge.c:231
 msgid "GPG sign commit"
 msgstr "GPG 提交签名"
 
 #. end commit message options
-#: builtin/commit.c:1380
+#: builtin/commit.c:1447
 msgid "Commit contents options"
 msgstr "提交内容选项"
 
-#: builtin/commit.c:1381
+#: builtin/commit.c:1448
 msgid "commit all changed files"
 msgstr "提交所有改动的文件"
 
-#: builtin/commit.c:1382
+#: builtin/commit.c:1449
 msgid "add specified files to index for commit"
 msgstr "添加指定的文件到索引区等待提交"
 
-#: builtin/commit.c:1383
+#: builtin/commit.c:1450
 msgid "interactively add files"
 msgstr "交互式添加文件"
 
-#: builtin/commit.c:1384
+#: builtin/commit.c:1451
 msgid "interactively add changes"
 msgstr "交互式添加变更"
 
-#: builtin/commit.c:1385
+#: builtin/commit.c:1452
 msgid "commit only specified files"
 msgstr "只提交指定的文件"
 
-#: builtin/commit.c:1386
+#: builtin/commit.c:1453
 msgid "bypass pre-commit hook"
 msgstr "绕过 pre-commit 钩子"
 
-#: builtin/commit.c:1387
+#: builtin/commit.c:1454
 msgid "show what would be committed"
 msgstr "显示将要提交的内容"
 
-#: builtin/commit.c:1398
+#: builtin/commit.c:1465
 msgid "amend previous commit"
 msgstr "修改先前的提交"
 
-#: builtin/commit.c:1399
+#: builtin/commit.c:1466
 msgid "bypass post-rewrite hook"
 msgstr "绕过 post-rewrite 钩子"
 
-#: builtin/commit.c:1404
+#: builtin/commit.c:1471
 msgid "ok to record an empty change"
 msgstr "允许一个空提交"
 
-#: builtin/commit.c:1407
+#: builtin/commit.c:1474
 msgid "ok to record a change with an empty message"
 msgstr "允许空的提交说明"
 
-#: builtin/commit.c:1439
+#: builtin/commit.c:1507
 msgid "could not parse HEAD commit"
 msgstr "不能解析 HEAD 提交"
 
-#: builtin/commit.c:1477 builtin/merge.c:508
+#: builtin/commit.c:1545 builtin/merge.c:525
 #, c-format
 msgid "could not open '%s' for reading"
 msgstr "不能为读入打开 '%s'"
 
-#: builtin/commit.c:1484
+#: builtin/commit.c:1552
 #, c-format
 msgid "Corrupt MERGE_HEAD file (%s)"
 msgstr "损坏的 MERGE_HEAD 文件(%s)"
 
-#: builtin/commit.c:1491
+#: builtin/commit.c:1559
 msgid "could not read MERGE_MODE"
 msgstr "不能读取 MERGE_MODE"
 
-#: builtin/commit.c:1510
+#: builtin/commit.c:1578
 #, c-format
 msgid "could not read commit message: %s"
 msgstr "不能读取提交说明:%s"
 
-#: builtin/commit.c:1524
+#: builtin/commit.c:1592
 #, c-format
 msgid "Aborting commit; you did not edit the message.\n"
 msgstr "终止提交;您未更改来自模版的提交说明。\n"
 
-#: builtin/commit.c:1529
+#: builtin/commit.c:1597
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr "终止提交因为提交说明为空。\n"
 
-#: builtin/commit.c:1544 builtin/merge.c:832 builtin/merge.c:857
+#: builtin/commit.c:1612 builtin/merge.c:861 builtin/merge.c:886
 msgid "failed to write commit object"
 msgstr "无法写提交对象"
 
-#: builtin/commit.c:1565
+#: builtin/commit.c:1633
 msgid "cannot lock HEAD ref"
 msgstr "无法锁定 HEAD 引用"
 
-#: builtin/commit.c:1569
+#: builtin/commit.c:1637
 msgid "cannot update HEAD ref"
 msgstr "无法更新 HEAD 引用"
 
-#: builtin/commit.c:1580
+#: builtin/commit.c:1648
 msgid ""
 "Repository has been updated, but unable to write\n"
 "new_index file. Check that disk is not full or quota is\n"
@@ -3935,171 +4433,183 @@ msgstr ""
 msgid "git config [options]"
 msgstr "git config [选项]"
 
-#: builtin/config.c:51
+#: builtin/config.c:52
 msgid "Config file location"
 msgstr "配置文件位置"
 
-#: builtin/config.c:52
+#: builtin/config.c:53
 msgid "use global config file"
 msgstr "使用全局配置文件"
 
-#: builtin/config.c:53
+#: builtin/config.c:54
 msgid "use system config file"
 msgstr "使用系统级配置文件"
 
-#: builtin/config.c:54
+#: builtin/config.c:55
 msgid "use repository config file"
 msgstr "使用版本库级配置文件"
 
-#: builtin/config.c:55
+#: builtin/config.c:56
 msgid "use given config file"
 msgstr "使用指定的配置文件"
 
-#: builtin/config.c:56
+#: builtin/config.c:57
+msgid "blob-id"
+msgstr "数据对象ID"
+
+#: builtin/config.c:57
+msgid "read config from given blob object"
+msgstr "从给定的数据对象读取配置"
+
+#: builtin/config.c:58
 msgid "Action"
 msgstr "操作"
 
-#: builtin/config.c:57
+#: builtin/config.c:59
 msgid "get value: name [value-regex]"
 msgstr "获取值:name [value-regex]"
 
-#: builtin/config.c:58
+#: builtin/config.c:60
 msgid "get all values: key [value-regex]"
 msgstr "获得所有的值:key [value-regex]"
 
-#: builtin/config.c:59
+#: builtin/config.c:61
 msgid "get values for regexp: name-regex [value-regex]"
 msgstr "根据正则表达式获得值:name-regex [value-regex]"
 
-#: builtin/config.c:60
+#: builtin/config.c:62
 msgid "replace all matching variables: name value [value_regex]"
 msgstr "替换所有匹配的变量:name value [value_regex]"
 
-#: builtin/config.c:61
+#: builtin/config.c:63
 msgid "add a new variable: name value"
 msgstr "添加一个新的变量:name value"
 
-#: builtin/config.c:62
+#: builtin/config.c:64
 msgid "remove a variable: name [value-regex]"
 msgstr "删除一个变量:name [value-regex]"
 
-#: builtin/config.c:63
+#: builtin/config.c:65
 msgid "remove all matches: name [value-regex]"
 msgstr "删除所有匹配项:name [value-regex]"
 
-#: builtin/config.c:64
+#: builtin/config.c:66
 msgid "rename section: old-name new-name"
 msgstr "重命名小节:old-name new-name"
 
-#: builtin/config.c:65
+#: builtin/config.c:67
 msgid "remove a section: name"
 msgstr "删除一个小节:name"
 
-#: builtin/config.c:66
+#: builtin/config.c:68
 msgid "list all"
 msgstr "列出所有"
 
-#: builtin/config.c:67
+#: builtin/config.c:69
 msgid "open an editor"
 msgstr "打开一个编辑器"
 
-#: builtin/config.c:68 builtin/config.c:69
+#: builtin/config.c:70 builtin/config.c:71
 msgid "slot"
 msgstr "slot"
 
-#: builtin/config.c:68
+#: builtin/config.c:70
 msgid "find the color configured: [default]"
 msgstr "找到配置的颜色:[默认]"
 
-#: builtin/config.c:69
+#: builtin/config.c:71
 msgid "find the color setting: [stdout-is-tty]"
 msgstr "找到颜色设置:[stdout-is-tty]"
 
-#: builtin/config.c:70
+#: builtin/config.c:72
 msgid "Type"
 msgstr "类型"
 
-#: builtin/config.c:71
+#: builtin/config.c:73
 msgid "value is \"true\" or \"false\""
 msgstr "值是 \"true\" 或 \"false\""
 
-#: builtin/config.c:72
+#: builtin/config.c:74
 msgid "value is decimal number"
 msgstr "值是十进制数"
 
-#: builtin/config.c:73
+#: builtin/config.c:75
 msgid "value is --bool or --int"
 msgstr "值是 --bool or --int"
 
-#: builtin/config.c:74
+#: builtin/config.c:76
 msgid "value is a path (file or directory name)"
 msgstr "值是一个路径(文件或目录名)"
 
-#: builtin/config.c:75
+#: builtin/config.c:77
 msgid "Other"
 msgstr "其它"
 
-#: builtin/config.c:76
+#: builtin/config.c:78
 msgid "terminate values with NUL byte"
 msgstr "终止值是NUL字节"
 
-#: builtin/config.c:77
+#: builtin/config.c:79
 msgid "respect include directives on lookup"
 msgstr "查询时参照 include 指令递归查找"
 
-#: builtin/count-objects.c:69
-msgid "git count-objects [-v]"
-msgstr "git count-objects [-v]"
+#: builtin/count-objects.c:82
+msgid "git count-objects [-v] [-H | --human-readable]"
+msgstr "git count-objects [-v] [-H | --human-readable]"
 
-#: builtin/describe.c:15
+#: builtin/count-objects.c:97
+msgid "print sizes in human readable format"
+msgstr "以用户可读的格式显示大小"
+
+#: builtin/describe.c:16
 msgid "git describe [options] <committish>*"
 msgstr "git describe [选项] <提交号>*"
 
-#: builtin/describe.c:16
+#: builtin/describe.c:17
 msgid "git describe [options] --dirty"
 msgstr "git describe [选项] --dirty"
 
-#: builtin/describe.c:234
+#: builtin/describe.c:237
 #, c-format
 msgid "annotated tag %s not available"
 msgstr "注释 tag %s 无效"
 
-#: builtin/describe.c:238
+#: builtin/describe.c:241
 #, c-format
 msgid "annotated tag %s has no embedded name"
 msgstr "注释 tag %s 没有嵌入名称"
 
-#: builtin/describe.c:240
+#: builtin/describe.c:243
 #, c-format
 msgid "tag '%s' is really '%s' here"
 msgstr "tag '%s' 的确是在 '%s'"
 
-#: builtin/describe.c:267
+#: builtin/describe.c:270
 #, c-format
 msgid "Not a valid object name %s"
 msgstr "不是一个有效的对象名 %s"
 
-#: builtin/describe.c:270
+#: builtin/describe.c:273
 #, c-format
 msgid "%s is not a valid '%s' object"
 msgstr "%s 不是一个有效的 '%s' 对象"
 
-#: builtin/describe.c:287
+#: builtin/describe.c:290
 #, c-format
 msgid "no tag exactly matches '%s'"
 msgstr "没有 tag 准确匹配 '%s'"
 
-#: builtin/describe.c:289
+#: builtin/describe.c:292
 #, c-format
 msgid "searching to describe %s\n"
 msgstr "搜索描述 %s\n"
 
-#: builtin/describe.c:329
+#: builtin/describe.c:332
 #, c-format
 msgid "finished search at %s\n"
 msgstr "完成搜索 %s\n"
 
-#: builtin/describe.c:353
+#: builtin/describe.c:359
 #, c-format
 msgid ""
 "No annotated tags can describe '%s'.\n"
@@ -4108,7 +4618,7 @@ msgstr ""
 "没有注释 tag 能描述 '%s'。\n"
 "然而,有非注释 tag:尝试 --tags。"
 
-#: builtin/describe.c:357
+#: builtin/describe.c:363
 #, c-format
 msgid ""
 "No tags can describe '%s'.\n"
@@ -4117,12 +4627,12 @@ msgstr ""
 "没有注释 tag 能描述 '%s'。\n"
 "尝试 --always,或者创建一些 tag。"
 
-#: builtin/describe.c:378
+#: builtin/describe.c:384
 #, c-format
 msgid "traversed %lu commits\n"
 msgstr "已遍历 %lu 个提交\n"
 
-#: builtin/describe.c:381
+#: builtin/describe.c:387
 #, c-format
 msgid ""
 "more than %i tags found; listed %i most recent\n"
@@ -4131,59 +4641,63 @@ msgstr ""
 "发现多于 %i 个 tag,列出最近的 %i 个\n"
 "在 %s 放弃搜索\n"
 
-#: builtin/describe.c:403
+#: builtin/describe.c:409
 msgid "find the tag that comes after the commit"
 msgstr "寻找提交之后的 tag(用于描述提交)"
 
-#: builtin/describe.c:404
+#: builtin/describe.c:410
 msgid "debug search strategy on stderr"
 msgstr "在标准错误上调试搜索策略"
 
-#: builtin/describe.c:405
-msgid "use any ref in .git/refs"
-msgstr "使用 .git/refs 里的任意引用"
+#: builtin/describe.c:411
+msgid "use any ref"
+msgstr "使用任意引用"
 
-#: builtin/describe.c:406
-msgid "use any tag in .git/refs/tags"
-msgstr "使用 .git/refs/tags 里的任意 tag"
+#: builtin/describe.c:412
+msgid "use any tag, even unannotated"
+msgstr "使用任意 tag,即使未带注解"
 
-#: builtin/describe.c:407
+#: builtin/describe.c:413
 msgid "always use long format"
 msgstr "始终使用长提交号格式"
 
-#: builtin/describe.c:410
+#: builtin/describe.c:414
+msgid "only follow first parent"
+msgstr "只跟随第一个父提交"
+
+#: builtin/describe.c:417
 msgid "only output exact matches"
 msgstr "只输出精确匹配"
 
-#: builtin/describe.c:412
+#: builtin/describe.c:419
 msgid "consider <n> most recent tags (default: 10)"
 msgstr "考虑最近 <n> 个 tags(默认:10)"
 
-#: builtin/describe.c:414
+#: builtin/describe.c:421
 msgid "only consider tags matching <pattern>"
 msgstr "只考虑匹配 <模式> 的 tags"
 
-#: builtin/describe.c:416 builtin/name-rev.c:238
+#: builtin/describe.c:423 builtin/name-rev.c:322
 msgid "show abbreviated commit object as fallback"
 msgstr "显示简写的提交号作为后备"
 
-#: builtin/describe.c:417
+#: builtin/describe.c:424
 msgid "mark"
 msgstr "标记"
 
-#: builtin/describe.c:418
+#: builtin/describe.c:425
 msgid "append <mark> on dirty working tree (default: \"-dirty\")"
 msgstr "若工作区脏(有变更)在结尾添加 <标记>(默认:\"-dirty\")"
 
-#: builtin/describe.c:436
+#: builtin/describe.c:443
 msgid "--long is incompatible with --abbrev=0"
 msgstr "--long 与 --abbrev=0 不兼容"
 
-#: builtin/describe.c:462
+#: builtin/describe.c:469
 msgid "No names found, cannot describe anything."
 msgstr "没有发现名称,无法描述任何东西。"
 
-#: builtin/describe.c:482
+#: builtin/describe.c:489
 msgid "--dirty is incompatible with committishes"
 msgstr "--dirty 不能与提交同时使用"
 
@@ -4192,31 +4706,26 @@ msgstr "--dirty 不能与提交同时使用"
 msgid "'%s': not a regular file or symlink"
 msgstr "'%s':不是一个正规文件或符号链接"
 
-#: builtin/diff.c:228
+#: builtin/diff.c:230
 #, c-format
 msgid "invalid option: %s"
 msgstr "无效选项:%s"
 
-#: builtin/diff.c:305
+#: builtin/diff.c:307
 msgid "Not a git repository"
 msgstr "不是一个 git 版本库"
 
-#: builtin/diff.c:348
+#: builtin/diff.c:350
 #, c-format
 msgid "invalid object '%s' given."
 msgstr "提供了无效对象 '%s'。"
 
-#: builtin/diff.c:353
-#, c-format
-msgid "more than %d trees given: '%s'"
-msgstr "提供了超过 %d 个树对象:'%s'"
-
-#: builtin/diff.c:363
+#: builtin/diff.c:359
 #, c-format
 msgid "more than two blobs given: '%s'"
 msgstr "提供了超过两个数据(blob)对象:'%s'"
 
-#: builtin/diff.c:371
+#: builtin/diff.c:366
 #, c-format
 msgid "unhandled object '%s' given."
 msgstr "提供了无法处理的对象 '%s'。"
@@ -4225,39 +4734,39 @@ msgstr "提供了无法处理的对象 '%s'。"
 msgid "git fast-export [rev-list-opts]"
 msgstr "git fast-export [rev-list-opts]"
 
-#: builtin/fast-export.c:646
+#: builtin/fast-export.c:666
 msgid "show progress after <n> objects"
 msgstr "在 <n> 个对象之后显示进度"
 
-#: builtin/fast-export.c:648
+#: builtin/fast-export.c:668
 msgid "select handling of signed tags"
 msgstr "选择如何处理签名 tags"
 
-#: builtin/fast-export.c:651
+#: builtin/fast-export.c:671
 msgid "select handling of tags that tag filtered objects"
 msgstr "选择当 tag 指向被过滤时 tags 的处理方式"
 
-#: builtin/fast-export.c:654
+#: builtin/fast-export.c:674
 msgid "Dump marks to this file"
 msgstr "把标记存储到这个文件"
 
-#: builtin/fast-export.c:656
+#: builtin/fast-export.c:676
 msgid "Import marks from this file"
 msgstr "从这个文件导入标记"
 
-#: builtin/fast-export.c:658
+#: builtin/fast-export.c:678
 msgid "Fake a tagger when tags lack one"
 msgstr "当 tags 缺少标记者字段时,假装提供一个"
 
-#: builtin/fast-export.c:660
+#: builtin/fast-export.c:680
 msgid "Output full tree for each commit"
 msgstr "每次提交都输出整个树"
 
-#: builtin/fast-export.c:662
+#: builtin/fast-export.c:682
 msgid "Use the done feature to terminate the stream"
 msgstr "使用 done 功能来终止流"
 
-#: builtin/fast-export.c:663
+#: builtin/fast-export.c:683
 msgid "Skip output of blob data"
 msgstr "跳过数据对象的输出"
 
@@ -4334,7 +4843,7 @@ msgstr "深化浅克隆的历史"
 msgid "convert to a complete repository"
 msgstr "转换为一个完整的版本库"
 
-#: builtin/fetch.c:88 builtin/log.c:1119
+#: builtin/fetch.c:88 builtin/log.c:1177
 msgid "dir"
 msgstr "目录"
 
@@ -4346,77 +4855,77 @@ msgstr "在子模组路径输出的前面加上此目录"
 msgid "default mode for recursion"
 msgstr "递归的默认模式"
 
-#: builtin/fetch.c:204
+#: builtin/fetch.c:220
 msgid "Couldn't find remote ref HEAD"
 msgstr "无法发现远程 HEAD 引用"
 
-#: builtin/fetch.c:257
+#: builtin/fetch.c:273
 #, c-format
 msgid "object %s not found"
 msgstr "对象 %s 未发现"
 
-#: builtin/fetch.c:262
+#: builtin/fetch.c:278
 msgid "[up to date]"
 msgstr "[最新]"
 
-#: builtin/fetch.c:276
+#: builtin/fetch.c:292
 #, c-format
 msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
 msgstr "! %-*s %-*s -> %s  (在当前分支下不能获取)"
 
-#: builtin/fetch.c:277 builtin/fetch.c:363
+#: builtin/fetch.c:293 builtin/fetch.c:379
 msgid "[rejected]"
 msgstr "[已拒绝]"
 
-#: builtin/fetch.c:288
+#: builtin/fetch.c:304
 msgid "[tag update]"
 msgstr "[tag更新]"
 
 #  译者:注意保持前导空格
-#: builtin/fetch.c:290 builtin/fetch.c:325 builtin/fetch.c:343
+#: builtin/fetch.c:306 builtin/fetch.c:341 builtin/fetch.c:359
 msgid "  (unable to update local ref)"
 msgstr "  (不能更新本地引用)"
 
-#: builtin/fetch.c:308
+#: builtin/fetch.c:324
 msgid "[new tag]"
 msgstr "[新tag]"
 
-#: builtin/fetch.c:311
+#: builtin/fetch.c:327
 msgid "[new branch]"
 msgstr "[新分支]"
 
-#: builtin/fetch.c:314
+#: builtin/fetch.c:330
 msgid "[new ref]"
 msgstr "[新引用]"
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "unable to update local ref"
 msgstr "不能更新本地引用"
 
-#: builtin/fetch.c:359
+#: builtin/fetch.c:375
 msgid "forced update"
 msgstr "强制更新"
 
-#: builtin/fetch.c:365
+#: builtin/fetch.c:381
 msgid "(non-fast-forward)"
 msgstr "(非快进式)"
 
-#: builtin/fetch.c:396 builtin/fetch.c:688
+#: builtin/fetch.c:412 builtin/fetch.c:718
 #, c-format
 msgid "cannot open %s: %s\n"
 msgstr "无法打开 %s:%s\n"
 
-#: builtin/fetch.c:405
+#: builtin/fetch.c:421
 #, c-format
 msgid "%s did not send all necessary objects\n"
 msgstr "%s 未发送所有必须的对象\n"
 
-#: builtin/fetch.c:491
+#: builtin/fetch.c:520
 #, c-format
 msgid "From %.*s\n"
 msgstr "来自 %.*s\n"
 
-#: builtin/fetch.c:502
+#: builtin/fetch.c:531
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -4426,87 +4935,87 @@ msgstr ""
 " 'git remote prune %s' 来删除旧的、有冲突的分支"
 
 #  译者:注意保持前导空格
-#: builtin/fetch.c:552
+#: builtin/fetch.c:581
 #, c-format
 msgid "   (%s will become dangling)"
 msgstr "   (%s 将成为摇摆状态)"
 
 #  译者:注意保持前导空格
-#: builtin/fetch.c:553
+#: builtin/fetch.c:582
 #, c-format
 msgid "   (%s has become dangling)"
 msgstr "   (%s 已成为摇摆状态)"
 
-#: builtin/fetch.c:560
+#: builtin/fetch.c:589
 msgid "[deleted]"
 msgstr "[已删除]"
 
-#: builtin/fetch.c:561 builtin/remote.c:1055
+#: builtin/fetch.c:590 builtin/remote.c:1055
 msgid "(none)"
 msgstr "(无)"
 
-#: builtin/fetch.c:678
+#: builtin/fetch.c:708
 #, c-format
 msgid "Refusing to fetch into current branch %s of non-bare repository"
 msgstr "拒绝获取到非裸版本库的当前分支 %s"
 
-#: builtin/fetch.c:712
+#: builtin/fetch.c:742
 #, c-format
 msgid "Don't know how to fetch from %s"
 msgstr "不知道如何从 %s 获取"
 
-#: builtin/fetch.c:789
+#: builtin/fetch.c:823
 #, c-format
 msgid "Option \"%s\" value \"%s\" is not valid for %s"
 msgstr "选项 \"%s\" 的值 \"%s\" 对于 %s 是无效的"
 
-#: builtin/fetch.c:792
+#: builtin/fetch.c:826
 #, c-format
 msgid "Option \"%s\" is ignored for %s\n"
 msgstr "选项 \"%s\" 为 %s 所忽略\n"
 
-#: builtin/fetch.c:894
+#: builtin/fetch.c:928
 #, c-format
 msgid "Fetching %s\n"
 msgstr "正在获取 %s\n"
 
-#: builtin/fetch.c:896 builtin/remote.c:100
+#: builtin/fetch.c:930 builtin/remote.c:100
 #, c-format
 msgid "Could not fetch %s"
 msgstr "不能获取 %s"
 
-#: builtin/fetch.c:915
+#: builtin/fetch.c:949
 msgid ""
 "No remote repository specified.  Please, specify either a URL or a\n"
 "remote name from which new revisions should be fetched."
 msgstr "未指定远程版本库。请通过一个URL或远程版本库名指定,用以获取新提交。"
 
-#: builtin/fetch.c:935
+#: builtin/fetch.c:969
 msgid "You need to specify a tag name."
 msgstr "您需要指定一个 tag 名称。"
 
-#: builtin/fetch.c:981
+#: builtin/fetch.c:1015
 msgid "--depth and --unshallow cannot be used together"
 msgstr "--depth 和 --unshallow 不能同时使用"
 
-#: builtin/fetch.c:983
+#: builtin/fetch.c:1017
 msgid "--unshallow on a complete repository does not make sense"
 msgstr "对于一个完整的版本库,参数 --unshallow 没有意义"
 
-#: builtin/fetch.c:1002
+#: builtin/fetch.c:1036
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all 不能带一个版本库参数"
 
-#: builtin/fetch.c:1004
+#: builtin/fetch.c:1038
 msgid "fetch --all does not make sense with refspecs"
 msgstr "fetch --all 带引用表达式没有任何意义"
 
-#: builtin/fetch.c:1015
+#: builtin/fetch.c:1049
 #, c-format
 msgid "No such remote or remote group: %s"
 msgstr "没有这样的远程或远程组:%s"
 
-#: builtin/fetch.c:1023
+#: builtin/fetch.c:1057
 msgid "Fetching a group and specifying refspecs does not make sense"
 msgstr "获取组并指定引用表达式没有意义"
 
@@ -4515,29 +5024,29 @@ msgid "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"
 msgstr ""
 "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"
 
-#: builtin/fmt-merge-msg.c:659 builtin/fmt-merge-msg.c:662 builtin/grep.c:701
-#: builtin/merge.c:188 builtin/show-branch.c:656 builtin/show-ref.c:175
-#: builtin/tag.c:446 parse-options.h:133 parse-options.h:239
+#: builtin/fmt-merge-msg.c:663 builtin/fmt-merge-msg.c:666 builtin/grep.c:700
+#: builtin/merge.c:203 builtin/show-branch.c:655 builtin/show-ref.c:179
+#: builtin/tag.c:446 parse-options.h:133 parse-options.h:243
 msgid "n"
 msgstr "n"
 
-#: builtin/fmt-merge-msg.c:660
+#: builtin/fmt-merge-msg.c:664
 msgid "populate log with at most <n> entries from shortlog"
 msgstr "向提交说明中最多复制指定条目(合并而来的提交)的简短说明"
 
-#: builtin/fmt-merge-msg.c:663
+#: builtin/fmt-merge-msg.c:667
 msgid "alias for --log (deprecated)"
 msgstr "参数 --log 的别名(已弃用)"
 
-#: builtin/fmt-merge-msg.c:666
+#: builtin/fmt-merge-msg.c:670
 msgid "text"
 msgstr "文本"
 
-#: builtin/fmt-merge-msg.c:667
+#: builtin/fmt-merge-msg.c:671
 msgid "use <text> as start of message"
 msgstr "使用 <文本> 作为提交说明的开始"
 
-#: builtin/fmt-merge-msg.c:668
+#: builtin/fmt-merge-msg.c:672
 msgid "file to read from"
 msgstr "从文件中读取"
 
@@ -4674,226 +5183,221 @@ msgstr "git grep [选项] [-e] <模式> [<修订>...] [[--] <路径>...]"
 msgid "grep: failed to create thread: %s"
 msgstr "grep:无法创建线程:%s"
 
-#: builtin/grep.c:365
+#: builtin/grep.c:364
 #, c-format
 msgid "Failed to chdir: %s"
 msgstr "无法切换目录:%s"
 
-#: builtin/grep.c:443 builtin/grep.c:478
+#: builtin/grep.c:442 builtin/grep.c:477
 #, c-format
 msgid "unable to read tree (%s)"
 msgstr "无法读取树(%s)"
 
-#: builtin/grep.c:493
+#: builtin/grep.c:492
 #, c-format
 msgid "unable to grep from object of type %s"
 msgstr "无法抓取来自于 %s 类型的对象"
 
-#: builtin/grep.c:551
+#: builtin/grep.c:550
 #, c-format
 msgid "switch `%c' expects a numerical value"
 msgstr "开关 `%c' 期望一个数字值"
 
-#: builtin/grep.c:568
+#: builtin/grep.c:567
 #, c-format
 msgid "cannot open '%s'"
 msgstr "不能打开 '%s'"
 
-#: builtin/grep.c:643
+#: builtin/grep.c:642
 msgid "search in index instead of in the work tree"
 msgstr "在索引区搜索而不是在工作区"
 
-#: builtin/grep.c:645
+#: builtin/grep.c:644
 msgid "find in contents not managed by git"
 msgstr "在未被 git 管理的内容中查找"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: builtin/grep.c:647
+#: builtin/grep.c:646
 msgid "search in both tracked and untracked files"
 msgstr "在跟踪和未跟踪的文件中搜索"
 
-#: builtin/grep.c:649
+#: builtin/grep.c:648
 msgid "search also in ignored files"
 msgstr "也在忽略的文件中搜索"
 
-#: builtin/grep.c:652
+#: builtin/grep.c:651
 msgid "show non-matching lines"
 msgstr "显示未匹配的行"
 
-#: builtin/grep.c:654
+#: builtin/grep.c:653
 msgid "case insensitive matching"
 msgstr "不区分大小写匹配"
 
-#: builtin/grep.c:656
+#: builtin/grep.c:655
 msgid "match patterns only at word boundaries"
 msgstr "只在单词边界匹配模式"
 
-#: builtin/grep.c:658
+#: builtin/grep.c:657
 msgid "process binary files as text"
 msgstr "把二进制文件当做文本处理"
 
-#: builtin/grep.c:660
+#: builtin/grep.c:659
 msgid "don't match patterns in binary files"
 msgstr "不在二进制文件中匹配模式"
 
-#: builtin/grep.c:663
+#: builtin/grep.c:662
 msgid "descend at most <depth> levels"
 msgstr "最多以指定的深度向下寻找"
 
-#: builtin/grep.c:667
+#: builtin/grep.c:666
 msgid "use extended POSIX regular expressions"
 msgstr "使用扩展的 POSIX 正则表达式"
 
-#: builtin/grep.c:670
+#: builtin/grep.c:669
 msgid "use basic POSIX regular expressions (default)"
 msgstr "使用基本的 POSIX 正则表达式(默认)"
 
-#: builtin/grep.c:673
+#: builtin/grep.c:672
 msgid "interpret patterns as fixed strings"
 msgstr "把模式解析为固定的字符串"
 
-#: builtin/grep.c:676
+#: builtin/grep.c:675
 msgid "use Perl-compatible regular expressions"
 msgstr "使用 Perl 兼容的正则表达式"
 
-#: builtin/grep.c:679
+#: builtin/grep.c:678
 msgid "show line numbers"
 msgstr "显示行号"
 
-#: builtin/grep.c:680
+#: builtin/grep.c:679
 msgid "don't show filenames"
 msgstr "不显示文件名"
 
-#: builtin/grep.c:681
+#: builtin/grep.c:680
 msgid "show filenames"
 msgstr "显示文件名"
 
-#: builtin/grep.c:683
+#: builtin/grep.c:682
 msgid "show filenames relative to top directory"
 msgstr "显示相对于顶级目录的文件名"
 
-#: builtin/grep.c:685
+#: builtin/grep.c:684
 msgid "show only filenames instead of matching lines"
 msgstr "只显示文件名而不显示匹配的行"
 
-#: builtin/grep.c:687
+#: builtin/grep.c:686
 msgid "synonym for --files-with-matches"
 msgstr "和 --files-with-matches 同义"
 
-#: builtin/grep.c:690
+#: builtin/grep.c:689
 msgid "show only the names of files without match"
 msgstr "只显示未匹配的文件名"
 
-#: builtin/grep.c:692
+#: builtin/grep.c:691
 msgid "print NUL after filenames"
 msgstr "在文件名后输出 NUL 字符"
 
-#: builtin/grep.c:694
+#: builtin/grep.c:693
 msgid "show the number of matches instead of matching lines"
 msgstr "显示总匹配行数,而不显示匹配的行"
 
-#: builtin/grep.c:695
+#: builtin/grep.c:694
 msgid "highlight matches"
 msgstr "高亮显示匹配项"
 
-#: builtin/grep.c:697
+#: builtin/grep.c:696
 msgid "print empty line between matches from different files"
 msgstr "在不同文件的匹配项之间打印空行"
 
-#: builtin/grep.c:699
+#: builtin/grep.c:698
 msgid "show filename only once above matches from same file"
 msgstr "只在同一文件的匹配项的上面显示一次文件名"
 
-#: builtin/grep.c:702
+#: builtin/grep.c:701
 msgid "show <n> context lines before and after matches"
 msgstr "显示匹配项前后的 <n> 行上下文"
 
-#: builtin/grep.c:705
+#: builtin/grep.c:704
 msgid "show <n> context lines before matches"
 msgstr "显示匹配项前 <n> 行上下文"
 
-#: builtin/grep.c:707
+#: builtin/grep.c:706
 msgid "show <n> context lines after matches"
 msgstr "显示匹配项后 <n> 行上下文"
 
-#: builtin/grep.c:708
+#: builtin/grep.c:707
 msgid "shortcut for -C NUM"
 msgstr "快捷键 -C 数字"
 
-#: builtin/grep.c:711
+#: builtin/grep.c:710
 msgid "show a line with the function name before matches"
 msgstr "在匹配的前面显示一行函数名"
 
-#: builtin/grep.c:713
+#: builtin/grep.c:712
 msgid "show the surrounding function"
 msgstr "显示所在函数的前后内容"
 
-#: builtin/grep.c:716
+#: builtin/grep.c:715
 msgid "read patterns from file"
 msgstr "从文件读取模式"
 
-#: builtin/grep.c:718
+#: builtin/grep.c:717
 msgid "match <pattern>"
 msgstr "匹配 <模式>"
 
-#: builtin/grep.c:720
+#: builtin/grep.c:719
 msgid "combine patterns specified with -e"
 msgstr "组合用 -e 参数设定的模式"
 
-#: builtin/grep.c:732
+#: builtin/grep.c:731
 msgid "indicate hit with exit status without output"
 msgstr "不输出,而用退出码标识命中状态"
 
-#: builtin/grep.c:734
+#: builtin/grep.c:733
 msgid "show only matches from files that match all patterns"
 msgstr "只显示匹配所有模式的文件中的匹配"
 
-#: builtin/grep.c:736
+#: builtin/grep.c:735
 msgid "show parse tree for grep expression"
 msgstr "显示 grep 表达式的解析树"
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "pager"
 msgstr "分页"
 
-#: builtin/grep.c:740
+#: builtin/grep.c:739
 msgid "show matching files in the pager"
 msgstr "分页显示匹配的文件"
 
-#: builtin/grep.c:743
+#: builtin/grep.c:742
 msgid "allow calling of grep(1) (ignored by this build)"
 msgstr "允许调用 grep(1)(本次构建忽略)"
 
-#: builtin/grep.c:744 builtin/show-ref.c:184
+#: builtin/grep.c:743 builtin/show-ref.c:188
 msgid "show usage"
 msgstr "显示用法"
 
-#: builtin/grep.c:811
+#: builtin/grep.c:810
 msgid "no pattern given."
 msgstr "未提供模式匹配。"
 
-#: builtin/grep.c:825
-#, c-format
-msgid "bad object %s"
-msgstr "坏对象 %s"
-
-#: builtin/grep.c:868
+#: builtin/grep.c:865
 msgid "--open-files-in-pager only works on the worktree"
 msgstr "--open-files-in-pager 仅用于工作区"
 
-#: builtin/grep.c:891
+#: builtin/grep.c:888
 msgid "--cached or --untracked cannot be used with --no-index."
 msgstr "--cached 或 --untracked 不能与 --no-index 同时使用。"
 
-#: builtin/grep.c:896
+#: builtin/grep.c:893
 msgid "--no-index or --untracked cannot be used with revs."
 msgstr "--no-index 或 --untracked 不能和版本同时使用。"
 
-#: builtin/grep.c:899
+#: builtin/grep.c:896
 msgid "--[no-]exclude-standard cannot be used for tracked contents."
 msgstr "--[no-]exclude-standard 不能用于已跟踪内容。"
 
-#: builtin/grep.c:907
+#: builtin/grep.c:904
 msgid "both --cached and trees are given."
 msgstr "同时给出了 --cached 和树对象。"
 
@@ -4933,10 +5437,14 @@ msgstr "原样存储文件不使用过滤器"
 msgid "process file as it were from this path"
 msgstr "处理文件并假设其来自于此路径"
 
-#: builtin/help.c:42
+#: builtin/help.c:41
 msgid "print all available commands"
 msgstr "打印所有可用的命令"
 
+#: builtin/help.c:42
+msgid "print list of useful guides"
+msgstr "显示有用的指南列表"
+
 #: builtin/help.c:43
 msgid "show man page"
 msgstr "显示 man 手册"
@@ -4950,8 +5458,8 @@ msgid "show info page"
 msgstr "显示 info 手册"
 
 #: builtin/help.c:52
-msgid "git help [--all] [--man|--web|--info] [command]"
-msgstr "git help [--all] [--man|--web|--info] [命令]"
+msgid "git help [--all] [--guides] [--man|--web|--info] [command]"
+msgstr "git help [--all] [--guides] [--man|--web|--info] [命令]"
 
 #: builtin/help.c:64
 #, c-format
@@ -5007,290 +5515,322 @@ msgstr "没有 man 查看器处理此请求"
 msgid "no info viewer handled the request"
 msgstr "没有 info 查看器处理此请求"
 
-#: builtin/help.c:429 builtin/help.c:436
+#: builtin/help.c:420
+msgid "Defining attributes per path"
+msgstr "定义路径的属性"
+
+#: builtin/help.c:421
+msgid "A Git glossary"
+msgstr "Git 词汇表"
+
+#: builtin/help.c:422
+msgid "Specifies intentionally untracked files to ignore"
+msgstr "忽略指定的未跟踪文件"
+
+#: builtin/help.c:423
+msgid "Defining submodule properties"
+msgstr "定义子模组属性"
+
+#: builtin/help.c:424
+msgid "Specifying revisions and ranges for Git"
+msgstr "指定 Git 的版本和版本范围"
+
+#: builtin/help.c:425
+msgid "A tutorial introduction to Git (for version 1.5.1 or newer)"
+msgstr "一个 Git 教程(针对 1.5.1 或更新版本)"
+
+#: builtin/help.c:426
+msgid "An overview of recommended workflows with Git"
+msgstr "Git 推荐的工作流概览"
+
+#: builtin/help.c:438
+msgid "The common Git guides are:\n"
+msgstr "最常用的 Git 向导有:\n"
+
+#: builtin/help.c:460 builtin/help.c:476
 #, c-format
 msgid "usage: %s%s"
 msgstr "用法:%s%s"
 
-#: builtin/help.c:452
+#: builtin/help.c:492
 #, c-format
 msgid "`git %s' is aliased to `%s'"
 msgstr "`git %s' 是 `%s' 的别名"
 
-#: builtin/index-pack.c:170
+#: builtin/index-pack.c:184
 #, c-format
 msgid "object type mismatch at %s"
 msgstr "%s 的对象类型不匹配"
 
-#: builtin/index-pack.c:190
+#: builtin/index-pack.c:204
 msgid "object of unexpected type"
 msgstr "意外的类型的对象"
 
-#: builtin/index-pack.c:227
+#: builtin/index-pack.c:244
 #, c-format
 msgid "cannot fill %d byte"
 msgid_plural "cannot fill %d bytes"
 msgstr[0] "无法填充 %d 字节"
 msgstr[1] "无法填充 %d 字节"
 
-#: builtin/index-pack.c:237
+#: builtin/index-pack.c:254
 msgid "early EOF"
 msgstr "过早的文件结束符(EOF)"
 
-#: builtin/index-pack.c:238
+#: builtin/index-pack.c:255
 msgid "read error on input"
 msgstr "输入上的读错误"
 
-#: builtin/index-pack.c:250
+#: builtin/index-pack.c:267
 msgid "used more bytes than were available"
 msgstr "用掉了超过可用的字节"
 
-#: builtin/index-pack.c:257
+#: builtin/index-pack.c:274
 msgid "pack too large for current definition of off_t"
 msgstr "包太大超过了当前 off_t 的定义"
 
-#: builtin/index-pack.c:273
+#: builtin/index-pack.c:290
 #, c-format
 msgid "unable to create '%s'"
 msgstr "不能创建 '%s'"
 
-#: builtin/index-pack.c:278
+#: builtin/index-pack.c:295
 #, c-format
 msgid "cannot open packfile '%s'"
 msgstr "无法打开包文件 '%s'"
 
-#: builtin/index-pack.c:292
+#: builtin/index-pack.c:309
 msgid "pack signature mismatch"
 msgstr "包签名不匹配"
 
-#: builtin/index-pack.c:294
+#: builtin/index-pack.c:311
 #, c-format
 msgid "pack version %<PRIu32> unsupported"
 msgstr "不支持包版本 %<PRIu32>"
 
-#: builtin/index-pack.c:312
+#: builtin/index-pack.c:329
 #, c-format
 msgid "pack has bad object at offset %lu: %s"
 msgstr "包中有错误的对象位于 %lu:%s"
 
-#: builtin/index-pack.c:434
+#: builtin/index-pack.c:451
 #, c-format
 msgid "inflate returned %d"
 msgstr "解压缩返回 %d"
 
-#: builtin/index-pack.c:483
+#: builtin/index-pack.c:500
 msgid "offset value overflow for delta base object"
 msgstr "偏移值覆盖了 delta 基准对象"
 
-#: builtin/index-pack.c:491
+#: builtin/index-pack.c:508
 msgid "delta base offset is out of bound"
 msgstr "delta 基准偏移越界"
 
-#: builtin/index-pack.c:499
+#: builtin/index-pack.c:516
 #, c-format
 msgid "unknown object type %d"
 msgstr "未知对象类型 %d"
 
-#: builtin/index-pack.c:530
+#: builtin/index-pack.c:547
 msgid "cannot pread pack file"
 msgstr "无法读取包文件"
 
-#: builtin/index-pack.c:532
+#: builtin/index-pack.c:549
 #, c-format
 msgid "premature end of pack file, %lu byte missing"
 msgid_plural "premature end of pack file, %lu bytes missing"
 msgstr[0] "包文件过早结束,缺少 %lu 字节"
 msgstr[1] "包文件过早结束,缺少 %lu 字节"
 
-#: builtin/index-pack.c:558
+#: builtin/index-pack.c:575
 msgid "serious inflate inconsistency"
 msgstr "解压缩严重的不一致"
 
-#: builtin/index-pack.c:649 builtin/index-pack.c:655 builtin/index-pack.c:678
-#: builtin/index-pack.c:712 builtin/index-pack.c:721
+#: 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
 #, c-format
 msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr "发现 %s 出现 SHA1 冲突!"
 
-#: builtin/index-pack.c:652 builtin/pack-objects.c:170
+#: builtin/index-pack.c:669 builtin/pack-objects.c:170
 #: builtin/pack-objects.c:262
 #, c-format
 msgid "unable to read %s"
 msgstr "不能读 %s"
 
-#: builtin/index-pack.c:718
+#: builtin/index-pack.c:735
 #, c-format
 msgid "cannot read existing object %s"
 msgstr "不能读取现存对象 %s"
 
-#: builtin/index-pack.c:732
+#: builtin/index-pack.c:749
 #, c-format
 msgid "invalid blob object %s"
 msgstr "无效的数据(blob)对象 %s"
 
-#: builtin/index-pack.c:747
+#: builtin/index-pack.c:763
 #, c-format
 msgid "invalid %s"
 msgstr "无效的 %s"
 
-#: builtin/index-pack.c:749
+#: builtin/index-pack.c:766
 msgid "Error in object"
 msgstr "对象中出错"
 
-#: builtin/index-pack.c:751
+#: builtin/index-pack.c:768
 #, c-format
 msgid "Not all child objects of %s are reachable"
 msgstr "%s 的所有子对象并非都可达"
 
-#: builtin/index-pack.c:821 builtin/index-pack.c:847
+#: builtin/index-pack.c:838 builtin/index-pack.c:868
 msgid "failed to apply delta"
 msgstr "无法应用 delta"
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Receiving objects"
 msgstr "接收对象中"
 
-#: builtin/index-pack.c:986
+#: builtin/index-pack.c:1009
 msgid "Indexing objects"
 msgstr "索引对象中"
 
-#: builtin/index-pack.c:1012
+#: builtin/index-pack.c:1035
 msgid "pack is corrupted (SHA1 mismatch)"
 msgstr "包冲突(SHA1 不匹配)"
 
-#: builtin/index-pack.c:1017
+#: builtin/index-pack.c:1040
 msgid "cannot fstat packfile"
 msgstr "不能枚举包文件状态"
 
-#: builtin/index-pack.c:1020
+#: builtin/index-pack.c:1043
 msgid "pack has junk at the end"
 msgstr "包的结尾有垃圾数据"
 
-#: builtin/index-pack.c:1031
+#: builtin/index-pack.c:1054
 msgid "confusion beyond insanity in parse_pack_objects()"
 msgstr "parse_pack_objects() 中遇到不可理喻的问题"
 
-#: builtin/index-pack.c:1054
+#: builtin/index-pack.c:1077
 msgid "Resolving deltas"
 msgstr "处理 delta 中"
 
-#: builtin/index-pack.c:1064
+#: builtin/index-pack.c:1087
 #, c-format
 msgid "unable to create thread: %s"
 msgstr "不能创建线程:%s"
 
-#: builtin/index-pack.c:1106
+#: builtin/index-pack.c:1129
 msgid "confusion beyond insanity"
 msgstr "不可理喻"
 
-#: builtin/index-pack.c:1112
+#: builtin/index-pack.c:1137
 #, c-format
 msgid "completed with %d local objects"
 msgstr "完成 %d 个本地对象"
 
-#: builtin/index-pack.c:1121
+#: builtin/index-pack.c:1147
 #, c-format
 msgid "Unexpected tail checksum for %s (disk corruption?)"
 msgstr "对 %s 的尾部校验出现意外(磁盘损坏?)"
 
-#: builtin/index-pack.c:1125
+#: builtin/index-pack.c:1151
 #, c-format
 msgid "pack has %d unresolved delta"
 msgid_plural "pack has %d unresolved deltas"
 msgstr[0] "包有 %d 个未解决的 delta"
 msgstr[1] "包有 %d 个未解决的 delta"
 
-#: builtin/index-pack.c:1150
+#: builtin/index-pack.c:1176
 #, c-format
 msgid "unable to deflate appended object (%d)"
 msgstr "不能压缩附加对象(%d)"
 
-#: builtin/index-pack.c:1229
+#: builtin/index-pack.c:1255
 #, c-format
 msgid "local object %s is corrupt"
 msgstr "本地对象 %s 已损坏"
 
-#: builtin/index-pack.c:1253
+#: builtin/index-pack.c:1279
 msgid "error while closing pack file"
 msgstr "关闭包文件时出错"
 
-#: builtin/index-pack.c:1266
+#: builtin/index-pack.c:1292
 #, c-format
 msgid "cannot write keep file '%s'"
 msgstr "无法写保留文件 '%s'"
 
-#: builtin/index-pack.c:1274
+#: builtin/index-pack.c:1300
 #, c-format
 msgid "cannot close written keep file '%s'"
 msgstr "无法关闭保留文件 '%s'"
 
-#: builtin/index-pack.c:1287
+#: builtin/index-pack.c:1313
 msgid "cannot store pack file"
 msgstr "无法存储包文件"
 
-#: builtin/index-pack.c:1298
+#: builtin/index-pack.c:1324
 msgid "cannot store index file"
 msgstr "无法存储索引文件"
 
-#: builtin/index-pack.c:1331
+#: builtin/index-pack.c:1357
 #, c-format
 msgid "bad pack.indexversion=%<PRIu32>"
 msgstr "坏的 pack.indexversion=%<PRIu32>"
 
-#: builtin/index-pack.c:1337
+#: builtin/index-pack.c:1363
 #, c-format
 msgid "invalid number of threads specified (%d)"
 msgstr "指定的线程数无效(%d)"
 
-#: builtin/index-pack.c:1341 builtin/index-pack.c:1514
+#: builtin/index-pack.c:1367 builtin/index-pack.c:1545
 #, c-format
 msgid "no threads support, ignoring %s"
 msgstr "没有线程支持,忽略 %s"
 
-#: builtin/index-pack.c:1399
+#: builtin/index-pack.c:1425
 #, c-format
 msgid "Cannot open existing pack file '%s'"
 msgstr "无法打开现存包文件 '%s'"
 
-#: builtin/index-pack.c:1401
+#: builtin/index-pack.c:1427
 #, c-format
 msgid "Cannot open existing pack idx file for '%s'"
 msgstr "无法为 %s 打开包索引文件"
 
-#: builtin/index-pack.c:1448
+#: builtin/index-pack.c:1474
 #, c-format
 msgid "non delta: %d object"
 msgid_plural "non delta: %d objects"
 msgstr[0] "非 delta:%d 个对象"
 msgstr[1] "非 delta:%d 个对象"
 
-#: builtin/index-pack.c:1455
+#: builtin/index-pack.c:1481
 #, c-format
 msgid "chain length = %d: %lu object"
 msgid_plural "chain length = %d: %lu objects"
 msgstr[0] "链长 = %d: %lu 对象"
 msgstr[1] "链长 = %d: %lu 对象"
 
-#: builtin/index-pack.c:1482
+#: builtin/index-pack.c:1509
 msgid "Cannot come back to cwd"
 msgstr "无法返回当前工作目录"
 
-#: builtin/index-pack.c:1526 builtin/index-pack.c:1529
-#: builtin/index-pack.c:1541 builtin/index-pack.c:1545
+#: builtin/index-pack.c:1557 builtin/index-pack.c:1560
+#: builtin/index-pack.c:1572 builtin/index-pack.c:1576
 #, c-format
 msgid "bad %s"
 msgstr "错误选项 %s"
 
-#: builtin/index-pack.c:1559
+#: builtin/index-pack.c:1590
 msgid "--fix-thin cannot be used without --stdin"
 msgstr "--fix-thin 不能和 --stdin 同时使用"
 
-#: builtin/index-pack.c:1563 builtin/index-pack.c:1573
+#: builtin/index-pack.c:1594 builtin/index-pack.c:1604
 #, c-format
 msgid "packfile name '%s' does not end with '.pack'"
 msgstr "包名 '%s' 没有以 '.pack' 结尾"
 
-#: builtin/index-pack.c:1582
+#: builtin/index-pack.c:1613
 msgid "--verify with no packfile name given"
 msgstr "--verify 没有提供包名参数"
 
@@ -5427,7 +5967,7 @@ msgstr "权限"
 msgid "specify that the git repository is to be shared amongst several users"
 msgstr "指定 git 版本库是多个用户之间共享的"
 
-#: builtin/init-db.c:493 builtin/prune-packed.c:77
+#: builtin/init-db.c:493 builtin/prune-packed.c:75
 msgid "be quiet"
 msgstr "保持安静"
 
@@ -5459,345 +5999,353 @@ msgstr "不能访问当前工作目录"
 msgid "Cannot access work tree '%s'"
 msgstr "不能访问工作区 '%s'"
 
-#: builtin/log.c:39
-msgid "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
-msgstr "git log [<选项>] [<从>..<到>] [[--] <路径>...]\n"
+#: builtin/log.c:41
+msgid "git log [<options>] [<revision range>] [[--] <path>...]\n"
+msgstr "git log [<选项>] [<版本范围>] [[--] <路径>...]\n"
 
-#: builtin/log.c:40
+#: builtin/log.c:42
 msgid "   or: git show [options] <object>..."
 msgstr "   或者:git show [选项] <对象>..."
 
-#: builtin/log.c:102
+#: builtin/log.c:124
 msgid "suppress diff output"
 msgstr "不显示差异输出"
 
-#: builtin/log.c:103
+#: builtin/log.c:125
 msgid "show source"
 msgstr "显示源"
 
-#: builtin/log.c:104
+#: builtin/log.c:126
 msgid "Use mail map file"
 msgstr "使用邮件映射文件"
 
-#: builtin/log.c:105
+#: builtin/log.c:127
 msgid "decorate options"
 msgstr "修饰选项"
 
-#: builtin/log.c:198
+#: builtin/log.c:230
 #, c-format
 msgid "Final output: %d %s\n"
 msgstr "最终输出:%d %s\n"
 
-#: builtin/log.c:419 builtin/log.c:511
+#: builtin/log.c:453 builtin/log.c:545
 #, c-format
 msgid "Could not read object %s"
 msgstr "不能读取对象 %s"
 
-#: builtin/log.c:535
+#: builtin/log.c:569
 #, c-format
 msgid "Unknown type: %d"
 msgstr "未知类型:%d"
 
-#: builtin/log.c:627
+#: builtin/log.c:669
 msgid "format.headers without value"
 msgstr "format.headers 没有值"
 
-#: builtin/log.c:701
+#: builtin/log.c:751
 msgid "name of output directory is too long"
 msgstr "输出目录名太长"
 
-#: builtin/log.c:717
+#: builtin/log.c:767
 #, c-format
 msgid "Cannot open patch file %s"
 msgstr "无法打开补丁文件 %s"
 
-#: builtin/log.c:731
+#: builtin/log.c:781
 msgid "Need exactly one range."
 msgstr "只需要一个范围。"
 
-#: builtin/log.c:739
+#: builtin/log.c:789
 msgid "Not a range."
 msgstr "不是一个范围。"
 
-#: builtin/log.c:812
+#: builtin/log.c:891
 msgid "Cover letter needs email format"
 msgstr "信封需要邮件地址格式"
 
-#: builtin/log.c:885
+#: builtin/log.c:967
 #, c-format
 msgid "insane in-reply-to: %s"
 msgstr "不正常的 in-reply-to:%s"
 
-#: builtin/log.c:913
+#: builtin/log.c:995
 msgid "git format-patch [options] [<since> | <revision range>]"
 msgstr "git format-patch [选项] [<从> | <修订集范围>]"
 
-#: builtin/log.c:958
+#: builtin/log.c:1040
 msgid "Two output directories?"
 msgstr "两个输出目录?"
 
-#: builtin/log.c:1097
+#: builtin/log.c:1155
 msgid "use [PATCH n/m] even with a single patch"
 msgstr "使用 [PATCH n/m],即使只有一个补丁"
 
-#: builtin/log.c:1100
+#: builtin/log.c:1158
 msgid "use [PATCH] even with multiple patches"
 msgstr "使用 [PATCH],即使有多个补丁"
 
-#: builtin/log.c:1104
+#: builtin/log.c:1162
 msgid "print patches to standard out"
 msgstr "打印补丁到标准输出"
 
-#: builtin/log.c:1106
+#: builtin/log.c:1164
 msgid "generate a cover letter"
 msgstr "生成一封附信"
 
-#: builtin/log.c:1108
+#: builtin/log.c:1166
 msgid "use simple number sequence for output file names"
 msgstr "使用简单的数字序列作为输出文件名"
 
-#: builtin/log.c:1109
+#: builtin/log.c:1167
 msgid "sfx"
 msgstr "后缀"
 
-#: builtin/log.c:1110
+#: builtin/log.c:1168
 msgid "use <sfx> instead of '.patch'"
 msgstr "使用 <后缀> 代替 '.patch'"
 
-#: builtin/log.c:1112
+#: builtin/log.c:1170
 msgid "start numbering patches at <n> instead of 1"
 msgstr "补丁以 <n> 开始编号,而不是1"
 
-#: builtin/log.c:1114
+#: builtin/log.c:1172
 msgid "mark the series as Nth re-roll"
 msgstr "标记补丁系列是第几次重制"
 
-#: builtin/log.c:1116
+#: builtin/log.c:1174
 msgid "Use [<prefix>] instead of [PATCH]"
 msgstr "使用 [<前缀>] 代替 [PATCH]"
 
-#: builtin/log.c:1119
+#: builtin/log.c:1177
 msgid "store resulting files in <dir>"
 msgstr "把结果文件存储在 <dir>"
 
-#: builtin/log.c:1122
+#: builtin/log.c:1180
 msgid "don't strip/add [PATCH]"
 msgstr "不删除/添加 [PATCH]"
 
-#: builtin/log.c:1125
+#: builtin/log.c:1183
 msgid "don't output binary diffs"
 msgstr "不输出二进制差异"
 
-#: builtin/log.c:1127
+#: builtin/log.c:1185
 msgid "don't include a patch matching a commit upstream"
 msgstr "不包含已在上游提交中的补丁"
 
-#: builtin/log.c:1129
+#: builtin/log.c:1187
 msgid "show patch format instead of default (patch + stat)"
 msgstr "显示纯补丁格式而非默认的(补丁+状态)"
 
-#: builtin/log.c:1131
+#: builtin/log.c:1189
 msgid "Messaging"
 msgstr "邮件发送"
 
-#: builtin/log.c:1132
+#: builtin/log.c:1190
 msgid "header"
 msgstr "header"
 
-#: builtin/log.c:1133
+#: builtin/log.c:1191
 msgid "add email header"
 msgstr "添加邮件头"
 
-#: builtin/log.c:1134 builtin/log.c:1136
+#: builtin/log.c:1192 builtin/log.c:1194
 msgid "email"
 msgstr "邮件地址"
 
-#: builtin/log.c:1134
+#: builtin/log.c:1192
 msgid "add To: header"
 msgstr "添加收件人"
 
-#: builtin/log.c:1136
+#: builtin/log.c:1194
 msgid "add Cc: header"
 msgstr "添加抄送"
 
-#: builtin/log.c:1138
+#: builtin/log.c:1196
+msgid "ident"
+msgstr "ident"
+
+#: builtin/log.c:1197
+msgid "set From address to <ident> (or committer ident if absent)"
+msgstr "设置 From 地址为 <ident>(如若不提供,用提交者ID做为地址)"
+
+#: builtin/log.c:1199
 msgid "message-id"
 msgstr "message-id"
 
-#: builtin/log.c:1139
+#: builtin/log.c:1200
 msgid "make first mail a reply to <message-id>"
 msgstr "使第一封邮件作为对 <message-id> 的回复"
 
-#: builtin/log.c:1140 builtin/log.c:1143
+#: builtin/log.c:1201 builtin/log.c:1204
 msgid "boundary"
 msgstr "边界"
 
-#: builtin/log.c:1141
+#: builtin/log.c:1202
 msgid "attach the patch"
 msgstr "附件方式添加补丁"
 
-#: builtin/log.c:1144
+#: builtin/log.c:1205
 msgid "inline the patch"
 msgstr "内联显示补丁"
 
-#: builtin/log.c:1148
+#: builtin/log.c:1209
 msgid "enable message threading, styles: shallow, deep"
 msgstr "启用邮件线索,风格:浅,深"
 
-#: builtin/log.c:1150
+#: builtin/log.c:1211
 msgid "signature"
 msgstr "签名"
 
-#: builtin/log.c:1151
+#: builtin/log.c:1212
 msgid "add a signature"
 msgstr "添加一个签名"
 
-#: builtin/log.c:1153
+#: builtin/log.c:1214
 msgid "don't print the patch filenames"
 msgstr "不要打印补丁文件名"
 
-#: builtin/log.c:1202
+#: builtin/log.c:1288
 #, c-format
-msgid "bogus committer info %s"
-msgstr "虚假的提交者信息 %s"
+msgid "invalid ident line: %s"
+msgstr "包含无效的身份标识:%s"
 
-#: builtin/log.c:1247
+#: builtin/log.c:1303
 msgid "-n and -k are mutually exclusive."
 msgstr "-n 和 -k 互斥。"
 
-#: builtin/log.c:1249
+#: builtin/log.c:1305
 msgid "--subject-prefix and -k are mutually exclusive."
 msgstr "--subject-prefix 和 -k 互斥。"
 
-#: builtin/log.c:1257
+#: builtin/log.c:1313
 msgid "--name-only does not make sense"
 msgstr "--name-only 无意义"
 
-#: builtin/log.c:1259
+#: builtin/log.c:1315
 msgid "--name-status does not make sense"
 msgstr "--name-status 无意义"
 
-#: builtin/log.c:1261
+#: builtin/log.c:1317
 msgid "--check does not make sense"
 msgstr "--check 无意义"
 
-#: builtin/log.c:1284
+#: builtin/log.c:1340
 msgid "standard output, or directory, which one?"
 msgstr "标准输出或目录,哪一个?"
 
-#: builtin/log.c:1286
+#: builtin/log.c:1342
 #, c-format
 msgid "Could not create directory '%s'"
 msgstr "不能创建目录 '%s'"
 
-#: builtin/log.c:1439
+#: builtin/log.c:1490
 msgid "Failed to create output files"
 msgstr "无法创建输出文件"
 
-#: builtin/log.c:1488
+#: builtin/log.c:1539
 msgid "git cherry [-v] [<upstream> [<head> [<limit>]]]"
 msgstr "git cherry [-v] [<上游> [<头> [<限制>]]]"
 
-#: builtin/log.c:1543
+#: builtin/log.c:1594
 #, c-format
 msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "不能找到跟踪的远程分支,请手工指定 <upstream>。\n"
 
-#: builtin/log.c:1556 builtin/log.c:1558 builtin/log.c:1570
+#: builtin/log.c:1607 builtin/log.c:1609 builtin/log.c:1621
 #, c-format
 msgid "Unknown commit %s"
 msgstr "未知提交 %s"
 
-#: builtin/ls-files.c:409
+#: builtin/ls-files.c:408
 msgid "git ls-files [options] [<file>...]"
 msgstr "git ls-files [选项] [<文件>...]"
 
-#: builtin/ls-files.c:466
+#: builtin/ls-files.c:465
 msgid "identify the file status with tags"
 msgstr "用标签标识文件的状态"
 
-#: builtin/ls-files.c:468
+#: builtin/ls-files.c:467
 msgid "use lowercase letters for 'assume unchanged' files"
 msgstr "使用小写字母表示 '假设未改变的' 文件"
 
-#: builtin/ls-files.c:470
+#: builtin/ls-files.c:469
 msgid "show cached files in the output (default)"
 msgstr "显示缓存的文件(默认)"
 
-#: builtin/ls-files.c:472
+#: builtin/ls-files.c:471
 msgid "show deleted files in the output"
 msgstr "显示已删除的文件"
 
-#: builtin/ls-files.c:474
+#: builtin/ls-files.c:473
 msgid "show modified files in the output"
 msgstr "显示已修改的文件"
 
-#: builtin/ls-files.c:476
+#: builtin/ls-files.c:475
 msgid "show other files in the output"
 msgstr "显示其它文件"
 
-#: builtin/ls-files.c:478
+#: builtin/ls-files.c:477
 msgid "show ignored files in the output"
 msgstr "显示忽略的文件"
 
-#: builtin/ls-files.c:481
+#: builtin/ls-files.c:480
 msgid "show staged contents' object name in the output"
 msgstr "显示暂存区内容的对象名称"
 
-#: builtin/ls-files.c:483
+#: builtin/ls-files.c:482
 msgid "show files on the filesystem that need to be removed"
 msgstr "显示文件系统需要删除的文件"
 
-#: builtin/ls-files.c:485
+#: builtin/ls-files.c:484
 msgid "show 'other' directories' name only"
 msgstr "只显示“其他”目录的名称"
 
-#: builtin/ls-files.c:488
+#: builtin/ls-files.c:487
 msgid "don't show empty directories"
 msgstr "不显示空目录"
 
-#: builtin/ls-files.c:491
+#: builtin/ls-files.c:490
 msgid "show unmerged files in the output"
 msgstr "显示未合并的文件"
 
-#: builtin/ls-files.c:493
+#: builtin/ls-files.c:492
 msgid "show resolve-undo information"
 msgstr "显示 resolve-undo 信息"
 
-#: builtin/ls-files.c:495
+#: builtin/ls-files.c:494
 msgid "skip files matching pattern"
 msgstr "匹配排除文件的模式"
 
-#: builtin/ls-files.c:498
+#: builtin/ls-files.c:497
 msgid "exclude patterns are read from <file>"
 msgstr "从 <文件> 中读取排除模式"
 
-#: builtin/ls-files.c:501
+#: builtin/ls-files.c:500
 msgid "read additional per-directory exclude patterns in <file>"
 msgstr "从 <文件> 读取额外的每个目录的排除模式"
 
-#: builtin/ls-files.c:503
+#: builtin/ls-files.c:502
 msgid "add the standard git exclusions"
 msgstr "添加标准的 git 排除"
 
-#: builtin/ls-files.c:506
+#: builtin/ls-files.c:505
 msgid "make the output relative to the project top directory"
 msgstr "显示相对于顶级目录的文件名"
 
-#: builtin/ls-files.c:509
+#: builtin/ls-files.c:508
 msgid "if any <file> is not in the index, treat this as an error"
 msgstr "如果任何 <文件> 都不在索引区,视为错误"
 
-#: builtin/ls-files.c:510
+#: builtin/ls-files.c:509
 msgid "tree-ish"
 msgstr "树或提交"
 
-#: builtin/ls-files.c:511
+#: builtin/ls-files.c:510
 msgid "pretend that paths removed since <tree-ish> are still present"
 msgstr "假装自从 <树或提交> 之后删除的路径仍然存在"
 
-#: builtin/ls-files.c:513
+#: builtin/ls-files.c:512
 msgid "show debugging data"
 msgstr "显示调试数据"
 
@@ -5849,164 +6397,168 @@ msgstr "git merge [选项] <说明> HEAD <提交>"
 msgid "git merge --abort"
 msgstr "git merge --abort"
 
-#: builtin/merge.c:90
+#: builtin/merge.c:98
 msgid "switch `m' requires a value"
 msgstr "开关 `m' 需要一个值"
 
-#: builtin/merge.c:127
+#: builtin/merge.c:135
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
 msgstr "不能找到合并策略 '%s'。\n"
 
-#: builtin/merge.c:128
+#: builtin/merge.c:136
 #, c-format
 msgid "Available strategies are:"
 msgstr "可用的策略有:"
 
-#: builtin/merge.c:133
+#: builtin/merge.c:141
 #, c-format
 msgid "Available custom strategies are:"
 msgstr "可用的自定义策略有:"
 
-#: builtin/merge.c:183
+#: builtin/merge.c:198
 msgid "do not show a diffstat at the end of the merge"
 msgstr "在合并的最后不显示差异统计"
 
-#: builtin/merge.c:186
+#: builtin/merge.c:201
 msgid "show a diffstat at the end of the merge"
 msgstr "在合并的最后显示差异统计"
 
-#: builtin/merge.c:187
+#: builtin/merge.c:202
 msgid "(synonym to --stat)"
 msgstr "(和 --stat 同义)"
 
-#: builtin/merge.c:189
+#: builtin/merge.c:204
 msgid "add (at most <n>) entries from shortlog to merge commit message"
 msgstr "在合并提交信息中添加(最多 <n> 条)精简提交记录"
 
-#: builtin/merge.c:192
+#: builtin/merge.c:207
 msgid "create a single commit instead of doing a merge"
 msgstr "创建一个单独的提交而不是做一次合并"
 
-#: builtin/merge.c:194
+#: builtin/merge.c:209
 msgid "perform a commit if the merge succeeds (default)"
 msgstr "如果合并成功,执行一次提交(默认)"
 
-#: builtin/merge.c:196
+#: builtin/merge.c:211
 msgid "edit message before committing"
 msgstr "在提交前编辑提交说明"
 
-#: builtin/merge.c:198
+#: builtin/merge.c:212
 msgid "allow fast-forward (default)"
 msgstr "允许快进(默认)"
 
-#: builtin/merge.c:200
+#: builtin/merge.c:214
 msgid "abort if fast-forward is not possible"
 msgstr "如果不能快进就放弃合并"
 
-#: builtin/merge.c:202 builtin/notes.c:866 builtin/revert.c:112
+#: builtin/merge.c:218
+msgid "Verify that the named commit has a valid GPG signature"
+msgstr "验证指定的提交是否包含一个有效的 GPG 签名"
+
+#: builtin/merge.c:219 builtin/notes.c:738 builtin/revert.c:114
 msgid "strategy"
 msgstr "策略"
 
-#: builtin/merge.c:203
+#: builtin/merge.c:220
 msgid "merge strategy to use"
 msgstr "要使用的合并策略"
 
-#: builtin/merge.c:204
+#: builtin/merge.c:221
 msgid "option=value"
 msgstr "option=value"
 
-#: builtin/merge.c:205
+#: builtin/merge.c:222
 msgid "option for selected merge strategy"
 msgstr "所选的合并策略的选项"
 
-#: builtin/merge.c:207
+#: builtin/merge.c:224
 msgid "merge commit message (for a non-fast-forward merge)"
 msgstr "合并的提交说明(针对非快进式合并)"
 
-#: builtin/merge.c:211
+#: builtin/merge.c:228
 msgid "abort the current in-progress merge"
 msgstr "放弃当前正在进行的合并"
 
-#: builtin/merge.c:240
+#: builtin/merge.c:257
 msgid "could not run stash."
 msgstr "不能进行进度保存。"
 
-#: builtin/merge.c:245
+#: builtin/merge.c:262
 msgid "stash failed"
 msgstr "进度保存失败"
 
-#: builtin/merge.c:250
+#: builtin/merge.c:267
 #, c-format
 msgid "not a valid object: %s"
 msgstr "不是一个有效对象:%s"
 
-#: builtin/merge.c:269 builtin/merge.c:286
+#: builtin/merge.c:286 builtin/merge.c:303
 msgid "read-tree failed"
 msgstr "读取树失败"
 
 #  译者:注意保持前导空格
-#: builtin/merge.c:316
+#: builtin/merge.c:333
 msgid " (nothing to squash)"
 msgstr " (无可压缩)"
 
-#: builtin/merge.c:329
+#: builtin/merge.c:346
 #, c-format
 msgid "Squash commit -- not updating HEAD\n"
 msgstr "压缩提交 -- 未更新 HEAD\n"
 
-#: builtin/merge.c:361
+#: builtin/merge.c:378
 msgid "Writing SQUASH_MSG"
 msgstr "写入 SQUASH_MSG"
 
-#: builtin/merge.c:363
+#: builtin/merge.c:380
 msgid "Finishing SQUASH_MSG"
 msgstr "完成 SQUASH_MSG"
 
-#: builtin/merge.c:386
+#: builtin/merge.c:403
 #, c-format
 msgid "No merge message -- not updating HEAD\n"
 msgstr "无合并信息 -- 未更新 HEAD\n"
 
-#: builtin/merge.c:436
+#: builtin/merge.c:453
 #, c-format
 msgid "'%s' does not point to a commit"
 msgstr "'%s' 没有指向一个提交"
 
-#: builtin/merge.c:535
+#: builtin/merge.c:565
 #, c-format
 msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr "坏的 branch.%s.mergeoptions 字符串:%s"
 
-#: builtin/merge.c:628
+#: builtin/merge.c:657
 msgid "git write-tree failed to write a tree"
 msgstr "git write-tree 无法写入一树对象"
 
-#: builtin/merge.c:656
+#: builtin/merge.c:685
 msgid "Not handling anything other than two heads merge."
 msgstr "不能处理两个头合并之外的任何操作。"
 
-#: builtin/merge.c:670
+#: builtin/merge.c:699
 #, c-format
 msgid "Unknown option for merge-recursive: -X%s"
 msgstr "merge-recursive 的未知选项:-X%s"
 
-#: builtin/merge.c:684
+#: builtin/merge.c:713
 #, c-format
 msgid "unable to write %s"
 msgstr "不能写 %s"
 
-#: builtin/merge.c:773
+#: builtin/merge.c:802
 #, c-format
 msgid "Could not read from '%s'"
 msgstr "不能从 '%s' 读取"
 
-#: builtin/merge.c:782
+#: builtin/merge.c:811
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr "未提交合并,使用 'git commit' 完成此次合并。\n"
 
-#: builtin/merge.c:788
+#: builtin/merge.c:817
 #, c-format
 msgid ""
 "Please enter a commit message to explain why this merge is necessary,\n"
@@ -6020,52 +6572,47 @@ msgstr ""
 "\n"
 "以 '%c' 开头的行将被忽略,而且空提交说明将会终止提交。\n"
 
-#: builtin/merge.c:812
+#: builtin/merge.c:841
 msgid "Empty commit message."
 msgstr "空提交信息。"
 
-#: builtin/merge.c:824
+#: builtin/merge.c:853
 #, c-format
 msgid "Wonderful.\n"
 msgstr "太棒了。\n"
 
-#: builtin/merge.c:889
+#: builtin/merge.c:918
 #, c-format
 msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
 msgstr "自动合并失败,修正冲突然后提交修正的结果。\n"
 
-#: builtin/merge.c:905
+#: builtin/merge.c:934
 #, c-format
 msgid "'%s' is not a commit"
 msgstr "'%s' 不是一个提交"
 
-#: builtin/merge.c:946
+#: builtin/merge.c:975
 msgid "No current branch."
 msgstr "没有当前分支。"
 
-#: builtin/merge.c:948
+#: builtin/merge.c:977
 msgid "No remote for the current branch."
 msgstr "当前分支没有对应的远程版本库。"
 
-#: builtin/merge.c:950
+#: builtin/merge.c:979
 msgid "No default upstream defined for the current branch."
 msgstr "当前分支没有定义默认的上游分支。"
 
-#: builtin/merge.c:955
-#, c-format
-msgid "No remote tracking branch for %s from %s"
-msgstr "%s 没有来自 %s 的远程跟踪分支"
-
-#: builtin/merge.c:1042 builtin/merge.c:1199
+#: builtin/merge.c:984
 #, c-format
-msgid "%s - not something we can merge"
-msgstr "%s - 不能被合并"
+msgid "No remote-tracking branch for %s from %s"
+msgstr "对于 %s 没有来自 %s 的远程跟踪分支"
 
-#: builtin/merge.c:1110
+#: builtin/merge.c:1140
 msgid "There is no merge to abort (MERGE_HEAD missing)."
 msgstr "没有要终止的合并(MERGE_HEAD 丢失)。"
 
-#: builtin/merge.c:1126 git-pull.sh:31
+#: builtin/merge.c:1156 git-pull.sh:31
 msgid ""
 "You have not concluded your merge (MERGE_HEAD exists).\n"
 "Please, commit your changes before you can merge."
@@ -6073,11 +6620,11 @@ msgstr ""
 "您尚未结束您的合并(存在 MERGE_HEAD)。\n"
 "请在合并前先提交您的修改。"
 
-#: builtin/merge.c:1129 git-pull.sh:34
+#: builtin/merge.c:1159 git-pull.sh:34
 msgid "You have not concluded your merge (MERGE_HEAD exists)."
 msgstr "您尚未结束您的合并(存在 MERGE_HEAD)。"
 
-#: builtin/merge.c:1133
+#: builtin/merge.c:1163
 msgid ""
 "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 "Please, commit your changes before you can merge."
@@ -6085,79 +6632,101 @@ msgstr ""
 "您尚未结束您的拣选(存在 CHERRY_PICK_HEAD)。\n"
 "请在合并前先提交您的修改。"
 
-#: builtin/merge.c:1136
+#: builtin/merge.c:1166
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
 msgstr "您尚未结束您的拣选(存在 CHERRY_PICK_HEAD)。"
 
-#: builtin/merge.c:1145
+#: builtin/merge.c:1175
 msgid "You cannot combine --squash with --no-ff."
 msgstr "您不能将 --squash 与 --no-ff 同时使用。"
 
-#: builtin/merge.c:1150
-msgid "You cannot combine --no-ff with --ff-only."
-msgstr "您不能将 --no-ff 与 --ff-only 同时使用。"
-
-#: builtin/merge.c:1157
+#: builtin/merge.c:1184
 msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr "未指定提交并且 merge.defaultToUpstream 未设置。"
 
-#: builtin/merge.c:1189
+#: builtin/merge.c:1216
 msgid "Can merge only exactly one commit into empty head"
 msgstr "只能将一个提交合并到空分支上"
 
-#: builtin/merge.c:1192
+#: builtin/merge.c:1219
 msgid "Squash commit into empty head not supported yet"
 msgstr "尚不支持到空分支的压缩提交"
 
-#: builtin/merge.c:1194
+#: builtin/merge.c:1221
 msgid "Non-fast-forward commit does not make sense into an empty head"
 msgstr "到空分支的非快进式提交没有意义"
 
-#: builtin/merge.c:1310
+#: builtin/merge.c:1226
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - 不能被合并"
+
+#: builtin/merge.c:1277
+#, c-format
+msgid "Commit %s has an untrusted GPG signature, allegedly by %s."
+msgstr "提交 %s 有一个非可信的声称来自 %s 的 GPG 签名。"
+
+#: builtin/merge.c:1280
+#, c-format
+msgid "Commit %s has a bad GPG signature allegedly by %s."
+msgstr "提交 %s 有一个错误的声称来自 %s 的 GPG 签名。"
+
+#. 'N'
+#: builtin/merge.c:1283
+#, c-format
+msgid "Commit %s does not have a GPG signature."
+msgstr "提交 %s 没有一个 GPG 签名。"
+
+#: builtin/merge.c:1286
+#, c-format
+msgid "Commit %s has a good GPG signature by %s\n"
+msgstr "提交 %s 有一个来自 %s 的好的 GPG 签名。\n"
+
+#: builtin/merge.c:1370
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr "更新 %s..%s\n"
 
-#: builtin/merge.c:1349
+#: builtin/merge.c:1409
 #, c-format
 msgid "Trying really trivial in-index merge...\n"
 msgstr "尝试非常小的索引内合并...\n"
 
-#: builtin/merge.c:1356
+#: builtin/merge.c:1416
 #, c-format
 msgid "Nope.\n"
 msgstr "无。\n"
 
-#: builtin/merge.c:1388
+#: builtin/merge.c:1448
 msgid "Not possible to fast-forward, aborting."
 msgstr "无法快进,终止。"
 
-#: builtin/merge.c:1411 builtin/merge.c:1490
+#: builtin/merge.c:1471 builtin/merge.c:1550
 #, c-format
 msgid "Rewinding the tree to pristine...\n"
 msgstr "将树回滚至原始状态...\n"
 
-#: builtin/merge.c:1415
+#: builtin/merge.c:1475
 #, c-format
 msgid "Trying merge strategy %s...\n"
 msgstr "尝试合并策略 %s...\n"
 
-#: builtin/merge.c:1481
+#: builtin/merge.c:1541
 #, c-format
 msgid "No merge strategy handled the merge.\n"
 msgstr "没有合并策略处理此合并。\n"
 
-#: builtin/merge.c:1483
+#: builtin/merge.c:1543
 #, c-format
 msgid "Merge with strategy %s failed.\n"
 msgstr "使用策略 %s 合并失败。\n"
 
-#: builtin/merge.c:1492
+#: builtin/merge.c:1552
 #, c-format
 msgid "Using the %s to prepare resolving by hand.\n"
 msgstr "使用 %s 以准备手工解决。\n"
 
-#: builtin/merge.c:1504
+#: builtin/merge.c:1564
 #, c-format
 msgid "Automatic merge went well; stopped before committing as requested\n"
 msgstr "自动合并进展顺利,按要求在提交前停止\n"
@@ -6323,47 +6892,51 @@ msgstr "重命名 %s 至 %s\n"
 msgid "renaming '%s' failed"
 msgstr "重命名 '%s' 失败"
 
-#: builtin/name-rev.c:175
+#: builtin/name-rev.c:259
 msgid "git name-rev [options] <commit>..."
 msgstr "git name-rev [选项] <提交>..."
 
-#: builtin/name-rev.c:176
+#: builtin/name-rev.c:260
 msgid "git name-rev [options] --all"
 msgstr "git name-rev [选项] --all"
 
-#: builtin/name-rev.c:177
+#: builtin/name-rev.c:261
 msgid "git name-rev [options] --stdin"
 msgstr "git name-rev [选项] --stdin"
 
-#: builtin/name-rev.c:229
+#: builtin/name-rev.c:313
 msgid "print only names (no SHA-1)"
 msgstr "只打印名称(无 SHA-1)"
 
-#: builtin/name-rev.c:230
+#: builtin/name-rev.c:314
 msgid "only use tags to name the commits"
 msgstr "只使用 tags 来命名提交"
 
-#: builtin/name-rev.c:232
+#: builtin/name-rev.c:316
 msgid "only use refs matching <pattern>"
 msgstr "只使用和 <模式> 相匹配的引用"
 
-#: builtin/name-rev.c:234
+#: builtin/name-rev.c:318
 msgid "list all commits reachable from all refs"
 msgstr "列出可以从所有引用访问的提交"
 
-#: builtin/name-rev.c:235
+#: builtin/name-rev.c:319
 msgid "read from stdin"
 msgstr "从标准输入读取"
 
-#: builtin/name-rev.c:236
+#: builtin/name-rev.c:320
 msgid "allow to print `undefined` names"
 msgstr "允许打印 `未定义` 的名称"
 
-#: builtin/notes.c:26
+#: builtin/name-rev.c:326
+msgid "dereference tags in the input (internal use)"
+msgstr "反向解析输入中的 tags(内部使用)"
+
+#: builtin/notes.c:24
 msgid "git notes [--ref <notes_ref>] [list [<object>]]"
 msgstr "git notes [--ref <注解引用>] [list [<对象>]]"
 
-#: builtin/notes.c:27
+#: builtin/notes.c:25
 msgid ""
 "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
@@ -6371,11 +6944,11 @@ msgstr ""
 "git notes [--ref <注解引用>] add [-f] [-m <说明> | -F <文件> | (-c | -C) <对"
 "象>] [<对象>]"
 
-#: builtin/notes.c:28
+#: builtin/notes.c:26
 msgid "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"
 msgstr "git notes [--ref <注解引用>] copy [-f] <源对象> <目标对象>"
 
-#: builtin/notes.c:29
+#: builtin/notes.c:27
 msgid ""
 "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) "
 "<object>] [<object>]"
@@ -6383,255 +6956,234 @@ msgstr ""
 "git notes [--ref <注解引用>] append [-m <说明> | -F <文件> | (-c | -C) <对象"
 ">] [<对象>]"
 
-#: builtin/notes.c:30
+#: builtin/notes.c:28
 msgid "git notes [--ref <notes_ref>] edit [<object>]"
 msgstr "git notes [--ref <注解引用>] edit [<对象>]"
 
-#: builtin/notes.c:31
+#: builtin/notes.c:29
 msgid "git notes [--ref <notes_ref>] show [<object>]"
 msgstr "git notes [--ref <注解引用>] show [<对象>]"
 
-#: builtin/notes.c:32
+#: builtin/notes.c:30
 msgid ""
 "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"
 msgstr "git notes [--ref <注解引用>] merge [-v | -q] [-s <策略> ] <注解引用>"
 
-#: builtin/notes.c:33
+#: builtin/notes.c:31
 msgid "git notes merge --commit [-v | -q]"
 msgstr "git notes merge --commit [-v | -q]"
 
-#: builtin/notes.c:34
+#: builtin/notes.c:32
 msgid "git notes merge --abort [-v | -q]"
 msgstr "git notes merge --abort [-v | -q]"
 
-#: builtin/notes.c:35
+#: builtin/notes.c:33
 msgid "git notes [--ref <notes_ref>] remove [<object>...]"
 msgstr "git notes [--ref <注解引用>] remove [<对象>...]"
 
-#: builtin/notes.c:36
+#: builtin/notes.c:34
 msgid "git notes [--ref <notes_ref>] prune [-n | -v]"
 msgstr "git notes [--ref <注解引用>] prune [-n | -v]"
 
-#: builtin/notes.c:37
+#: builtin/notes.c:35
 msgid "git notes [--ref <notes_ref>] get-ref"
 msgstr "git notes [--ref <注解引用>] get-ref"
 
-#: builtin/notes.c:42
+#: builtin/notes.c:40
 msgid "git notes [list [<object>]]"
 msgstr "git notes [list [<对象>]]"
 
-#: builtin/notes.c:47
+#: builtin/notes.c:45
 msgid "git notes add [<options>] [<object>]"
 msgstr "git notes add [<选项>] [<对象>]"
 
-#: builtin/notes.c:52
+#: builtin/notes.c:50
 msgid "git notes copy [<options>] <from-object> <to-object>"
 msgstr "git notes copy [<选项>] <源对象> <目标对象>"
 
-#: builtin/notes.c:53
+#: builtin/notes.c:51
 msgid "git notes copy --stdin [<from-object> <to-object>]..."
 msgstr "git notes copy --stdin [<源对象> <目标对象>]..."
 
-#: builtin/notes.c:58
+#: builtin/notes.c:56
 msgid "git notes append [<options>] [<object>]"
 msgstr "git notes append [<选项>] [<对象>]"
 
-#: builtin/notes.c:63
+#: builtin/notes.c:61
 msgid "git notes edit [<object>]"
 msgstr "git notes edit [<对象>]"
 
-#: builtin/notes.c:68
+#: builtin/notes.c:66
 msgid "git notes show [<object>]"
 msgstr "git notes show [<对象>]"
 
-#: builtin/notes.c:73
+#: builtin/notes.c:71
 msgid "git notes merge [<options>] <notes_ref>"
 msgstr "git notes merge [<选项>] <注解引用>"
 
-#: builtin/notes.c:74
+#: builtin/notes.c:72
 msgid "git notes merge --commit [<options>]"
 msgstr "git notes merge --commit [<选项>]"
 
-#: builtin/notes.c:75
+#: builtin/notes.c:73
 msgid "git notes merge --abort [<options>]"
 msgstr "git notes merge --abort [<选项>]"
 
-#: builtin/notes.c:80
+#: builtin/notes.c:78
 msgid "git notes remove [<object>]"
 msgstr "git notes remove [<对象>]"
 
-#: builtin/notes.c:85
+#: builtin/notes.c:83
 msgid "git notes prune [<options>]"
 msgstr "git notes prune [<选项>]"
 
-#: builtin/notes.c:90
+#: builtin/notes.c:88
 msgid "git notes get-ref"
 msgstr "git notes get-ref"
 
-#: builtin/notes.c:139
+#: builtin/notes.c:137
 #, c-format
 msgid "unable to start 'show' for object '%s'"
 msgstr "不能为对象 '%s' 开始 'show'"
 
-#: builtin/notes.c:143
+#: builtin/notes.c:141
 msgid "could not read 'show' output"
 msgstr "不能读取 'show' 的输出"
 
-#: builtin/notes.c:151
+#: builtin/notes.c:149
 #, c-format
 msgid "failed to finish 'show' for object '%s'"
 msgstr "无法为对象 '%s' 完成 'show'"
 
-#: builtin/notes.c:169 builtin/tag.c:341
+#: builtin/notes.c:167 builtin/tag.c:341
 #, c-format
 msgid "could not create file '%s'"
 msgstr "不能创建文件 '%s'"
 
-#: builtin/notes.c:188
+#: builtin/notes.c:186
 msgid "Please supply the note contents using either -m or -F option"
 msgstr "请通过 -m 或 -F 选项为注解提供内容"
 
-#: builtin/notes.c:209 builtin/notes.c:972
+#: builtin/notes.c:207 builtin/notes.c:844
 #, c-format
 msgid "Removing note for object %s\n"
 msgstr "删除对象 %s 的注解\n"
 
-#: builtin/notes.c:214
+#: builtin/notes.c:212
 msgid "unable to write note object"
 msgstr "不能写注解对象"
 
-#: builtin/notes.c:216
+#: builtin/notes.c:214
 #, c-format
 msgid "The note contents has been left in %s"
 msgstr "注解内容被留在文件 %s 中"
 
-#: builtin/notes.c:250 builtin/tag.c:540
+#: builtin/notes.c:248 builtin/tag.c:540
 #, c-format
 msgid "cannot read '%s'"
 msgstr "不能读取 '%s'"
 
-#: builtin/notes.c:252 builtin/tag.c:543
+#: builtin/notes.c:250 builtin/tag.c:543
 #, c-format
 msgid "could not open or read '%s'"
 msgstr "不能打开或读取 '%s'"
 
-#: builtin/notes.c:271 builtin/notes.c:444 builtin/notes.c:446
-#: builtin/notes.c:506 builtin/notes.c:560 builtin/notes.c:643
-#: builtin/notes.c:648 builtin/notes.c:723 builtin/notes.c:765
-#: builtin/notes.c:967 builtin/tag.c:556
+#: builtin/notes.c:269 builtin/notes.c:316 builtin/notes.c:318
+#: builtin/notes.c:378 builtin/notes.c:432 builtin/notes.c:515
+#: builtin/notes.c:520 builtin/notes.c:595 builtin/notes.c:637
+#: builtin/notes.c:839 builtin/tag.c:556
 #, c-format
 msgid "Failed to resolve '%s' as a valid ref."
 msgstr "无法解析 '%s' 为一个有效引用。"
 
-#: builtin/notes.c:274
+#: builtin/notes.c:272
 #, c-format
 msgid "Failed to read object '%s'."
 msgstr "无法读取对象 '%s'。"
 
-#: builtin/notes.c:298
-msgid "Cannot commit uninitialized/unreferenced notes tree"
-msgstr "不能提交未初始化/未引用的注解树"
-
-#: builtin/notes.c:339
-#, c-format
-msgid "Bad notes.rewriteMode value: '%s'"
-msgstr "坏的 notes.rewriteMode 值:'%s'"
-
-#: builtin/notes.c:349
-#, c-format
-msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
-msgstr "拒绝向 %s(在 refs/notes/ 之外)写入注解"
-
-#. TRANSLATORS: The first %s is the name of the
-#. environment variable, the second %s is its value
-#: builtin/notes.c:376
-#, c-format
-msgid "Bad %s value: '%s'"
-msgstr "坏的 %s 值:'%s'"
-
-#: builtin/notes.c:440
+#: builtin/notes.c:312
 #, c-format
 msgid "Malformed input line: '%s'."
 msgstr "非法的输入行:'%s'。"
 
-#: builtin/notes.c:455
+#: builtin/notes.c:327
 #, c-format
 msgid "Failed to copy notes from '%s' to '%s'"
 msgstr "无法从 '%s' 到 '%s' 拷贝注解"
 
-#: builtin/notes.c:499 builtin/notes.c:553 builtin/notes.c:626
-#: builtin/notes.c:638 builtin/notes.c:711 builtin/notes.c:758
-#: builtin/notes.c:1032
+#: builtin/notes.c:371 builtin/notes.c:425 builtin/notes.c:498
+#: builtin/notes.c:510 builtin/notes.c:583 builtin/notes.c:630
+#: builtin/notes.c:904
 msgid "too many parameters"
 msgstr "参数太多"
 
-#: builtin/notes.c:512 builtin/notes.c:771
+#: builtin/notes.c:384 builtin/notes.c:643
 #, c-format
 msgid "No note found for object %s."
 msgstr "未发现对象 %s 的注解。"
 
-#: builtin/notes.c:534 builtin/notes.c:691
+#: builtin/notes.c:406 builtin/notes.c:563
 msgid "note contents as a string"
 msgstr "注解内容作为一个字符串"
 
-#: builtin/notes.c:537 builtin/notes.c:694
+#: builtin/notes.c:409 builtin/notes.c:566
 msgid "note contents in a file"
 msgstr "注解内容到一个文件中"
 
-#: builtin/notes.c:539 builtin/notes.c:542 builtin/notes.c:696
-#: builtin/notes.c:699 builtin/tag.c:474
+#: builtin/notes.c:411 builtin/notes.c:414 builtin/notes.c:568
+#: builtin/notes.c:571 builtin/tag.c:474
 msgid "object"
 msgstr "对象"
 
-#: builtin/notes.c:540 builtin/notes.c:697
+#: builtin/notes.c:412 builtin/notes.c:569
 msgid "reuse and edit specified note object"
 msgstr "重用和编辑指定的注解对象"
 
-#: builtin/notes.c:543 builtin/notes.c:700
+#: builtin/notes.c:415 builtin/notes.c:572
 msgid "reuse specified note object"
 msgstr "重用指定的注解对象"
 
-#: builtin/notes.c:545 builtin/notes.c:613
+#: builtin/notes.c:417 builtin/notes.c:485
 msgid "replace existing notes"
 msgstr "替换已存在的注解"
 
-#: builtin/notes.c:579
+#: builtin/notes.c:451
 #, c-format
 msgid ""
 "Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
 "existing notes"
 msgstr "不能添加注解。发现对象 %s 已存在注解。使用 '-f' 覆盖现存注解"
 
-#: builtin/notes.c:584 builtin/notes.c:661
+#: builtin/notes.c:456 builtin/notes.c:533
 #, c-format
 msgid "Overwriting existing notes for object %s\n"
 msgstr "覆盖对象 %s 现存注解\n"
 
-#: builtin/notes.c:614
+#: builtin/notes.c:486
 msgid "read objects from stdin"
 msgstr "从标准输入读取对象"
 
-#: builtin/notes.c:616
+#: builtin/notes.c:488
 msgid "load rewriting config for <command> (implies --stdin)"
 msgstr "重新加载 <命令> 的配置(隐含 --stdin)"
 
-#: builtin/notes.c:634
+#: builtin/notes.c:506
 msgid "too few parameters"
 msgstr "参数太少"
 
-#: builtin/notes.c:655
+#: builtin/notes.c:527
 #, c-format
 msgid ""
 "Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
 "existing notes"
 msgstr "不能拷贝注解。发现对象 %s 已存在注解。使用 '-f' 覆盖现存注解"
 
-#: builtin/notes.c:667
+#: builtin/notes.c:539
 #, c-format
 msgid "Missing notes on source object %s. Cannot copy."
 msgstr "源对象 %s 缺少注解。不能拷贝。"
 
-#: builtin/notes.c:716
+#: builtin/notes.c:588
 #, c-format
 msgid ""
 "The -m/-F/-c/-C options have been deprecated for the 'edit' subcommand.\n"
@@ -6640,58 +7192,58 @@ msgstr ""
 "子命令 'edit' 的选项 -m/-F/-c/-C 已弃用。\n"
 "请换用 'git notes add -f -m/-F/-c/-C'。\n"
 
-#: builtin/notes.c:863
+#: builtin/notes.c:735
 msgid "General options"
 msgstr "通用选项"
 
-#: builtin/notes.c:865
+#: builtin/notes.c:737
 msgid "Merge options"
 msgstr "合并选项"
 
-#: builtin/notes.c:867
+#: builtin/notes.c:739
 msgid ""
 "resolve notes conflicts using the given strategy (manual/ours/theirs/union/"
 "cat_sort_uniq)"
 msgstr "使用指定的策略解决注解冲突 (manual/ours/theirs/union/cat_sort_uniq)"
 
-#: builtin/notes.c:869
+#: builtin/notes.c:741
 msgid "Committing unmerged notes"
 msgstr "提交未合并的注解"
 
-#: builtin/notes.c:871
+#: builtin/notes.c:743
 msgid "finalize notes merge by committing unmerged notes"
 msgstr "通过提交未合并的注解来完成注解合并"
 
-#: builtin/notes.c:873
+#: builtin/notes.c:745
 msgid "Aborting notes merge resolution"
 msgstr "中止注解合并的方案"
 
-#: builtin/notes.c:875
+#: builtin/notes.c:747
 msgid "abort notes merge"
 msgstr "中止注解合并"
 
-#: builtin/notes.c:970
+#: builtin/notes.c:842
 #, c-format
 msgid "Object %s has no note\n"
 msgstr "对象 %s 没有注解\n"
 
-#: builtin/notes.c:982
+#: builtin/notes.c:854
 msgid "attempt to remove non-existent note is not an error"
 msgstr "尝试删除不存在的注解不是一个错误"
 
-#: builtin/notes.c:985
+#: builtin/notes.c:857
 msgid "read object names from the standard input"
 msgstr "从标准输入读取对象名称"
 
-#: builtin/notes.c:1066
+#: builtin/notes.c:938
 msgid "notes_ref"
 msgstr "注解引用"
 
-#: builtin/notes.c:1067
+#: builtin/notes.c:939
 msgid "use notes from <notes_ref>"
 msgstr "从 <注解引用> 使用注解"
 
-#: builtin/notes.c:1102 builtin/remote.c:1598
+#: builtin/notes.c:974 builtin/remote.c:1598
 #, c-format
 msgid "Unknown subcommand: %s"
 msgstr "未知子命令:%s"
@@ -6925,7 +7477,7 @@ msgstr ""
 "    git push %s %s\n"
 "%s"
 
-#: builtin/push.c:121
+#: builtin/push.c:117
 #, c-format
 msgid ""
 "You are not currently on a branch.\n"
@@ -6939,7 +7491,7 @@ msgstr ""
 "\n"
 "    git push %s HEAD:<name-of-remote-branch>\n"
 
-#: builtin/push.c:128
+#: builtin/push.c:131
 #, c-format
 msgid ""
 "The current branch %s has no upstream branch.\n"
@@ -6952,12 +7504,12 @@ msgstr ""
 "\n"
 "    git push --set-upstream %s %s\n"
 
-#: builtin/push.c:136
+#: builtin/push.c:139
 #, c-format
 msgid "The current branch %s has multiple upstream branches, refusing to push."
 msgstr "当前分支 %s 有多个上游分支,拒绝推送。"
 
-#: builtin/push.c:139
+#: builtin/push.c:142
 #, c-format
 msgid ""
 "You are pushing to remote '%s', which is not the upstream of\n"
@@ -6968,7 +7520,7 @@ msgstr ""
 "而没有告诉我要推送什么、更新哪个远程分支。"
 
 #  译者:字符串首行行首要添加“warning: ”字串,故此首行要较其余行短
-#: builtin/push.c:151
+#: builtin/push.c:165
 msgid ""
 "push.default is unset; its implicit value is changing in\n"
 "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -6999,23 +7551,23 @@ msgstr ""
 "('simple' 模式由 Git 1.7.11 版本引入。如果您有时要使用老版本的 Git,\n"
 "为保持兼容,请用 'current' 代替 'simple' 模式)"
 
-#: builtin/push.c:199
+#: builtin/push.c:225
 msgid ""
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr "您没有为推送指定任何引用表达式,并且 push.default 为 \"nothing\"。"
 
-#: builtin/push.c:206
+#: builtin/push.c:232
 msgid ""
 "Updates were rejected because the tip of your current branch is behind\n"
-"its remote counterpart. Merge the remote changes (e.g. 'git pull')\n"
-"before pushing again.\n"
+"its remote counterpart. Integrate the remote changes (e.g.\n"
+"'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为您当前分支的最新提交落后于其对应的远程分支。\n"
-"再次推送前,先与远程变更合并(如 'git pull')。详见\n"
+"再次推送前,先与远程变更合并(如 'git pull ...')。详见\n"
 "'git push --help' 中的 'Note about fast-forwards' 小节。"
 
-#: builtin/push.c:212
+#: builtin/push.c:238
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
 "counterpart. If you did not intend to push that branch, you may want to\n"
@@ -7027,35 +7579,35 @@ msgstr ""
 "配置变量 'push.default' 设置为 'simple'、'current' 或 'upstream'\n"
 "以便只推送当前分支。"
 
-#: builtin/push.c:218
+#: builtin/push.c:244
 msgid ""
 "Updates were rejected because a pushed branch tip is behind its remote\n"
-"counterpart. Check out this branch and merge the remote changes\n"
-"(e.g. 'git pull') before pushing again.\n"
+"counterpart. Check out this branch and integrate the remote changes\n"
+"(e.g. 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为推送的一个分支的最新提交落后于其对应的远程分支。\n"
-"检出该分支并与远程变更合并(如 'git pull'),然后再推送。详见\n"
+"检出该分支并整合远程变更(如 'git pull ...'),然后再推送。详见\n"
 "'git push --help' 中的 'Note about fast-forwards' 小节。"
 
-#: builtin/push.c:224
+#: builtin/push.c:250
 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"
-"to the same ref. You may want to first merge the remote changes (e.g.,\n"
-"'git pull') before pushing again.\n"
+"to the same ref. You may want to first integrate the remote changes\n"
+"(e.g., 'git pull ...') before pushing again.\n"
 "See the 'Note about fast-forwards' in 'git push --help' for details."
 msgstr ""
 "更新被拒绝,因为远程版本库包含您本地尚不存在的提交。这通常是因为另外\n"
-"一个版本库已推送了相同的引用。再次推送前,您可能需要先合并远程变更\n"
-"(如 'git pull')。\n"
+"一个版本库已向该引用进行了推送。再次推送前,您可能需要先整合远程变更\n"
+"(如 'git pull ...')。\n"
 "详见 'git push --help' 中的 'Note about fast-forwards' 小节。"
 
-#: builtin/push.c:231
+#: builtin/push.c:257
 msgid "Updates were rejected because the tag already exists in the remote."
 msgstr "更新被拒绝因为 tag 在远程已经存在。"
 
-#: builtin/push.c:234
+#: builtin/push.c:260
 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"
@@ -7064,22 +7616,22 @@ msgstr ""
 "如果不使用 '--force' 参数,您不能更新一个指向非提交对象的远程引用,\n"
 "也不能更新远程引用让其指向一个非提交对象。\n"
 
-#: builtin/push.c:294
+#: builtin/push.c:320
 #, c-format
 msgid "Pushing to %s\n"
 msgstr "推送到 %s\n"
 
-#: builtin/push.c:298
+#: builtin/push.c:324
 #, c-format
 msgid "failed to push some refs to '%s'"
 msgstr "无法推送一些引用到 '%s'"
 
-#: builtin/push.c:331
+#: builtin/push.c:357
 #, c-format
 msgid "bad repository '%s'"
 msgstr "坏的版本库 '%s'"
 
-#: builtin/push.c:332
+#: builtin/push.c:358
 msgid ""
 "No configured push destination.\n"
 "Either specify the URL from the command-line or configure a remote "
@@ -7100,83 +7652,87 @@ msgstr ""
 "\n"
 "    git push <name>\n"
 
-#: builtin/push.c:347
+#: builtin/push.c:373
 msgid "--all and --tags are incompatible"
 msgstr "--all 和 --tags 不兼容"
 
-#: builtin/push.c:348
+#: builtin/push.c:374
 msgid "--all can't be combined with refspecs"
 msgstr "--all 不能和引用表达式同时使用"
 
-#: builtin/push.c:353
+#: builtin/push.c:379
 msgid "--mirror and --tags are incompatible"
 msgstr "--mirror 和 --tags 不兼容"
 
-#: builtin/push.c:354
+#: builtin/push.c:380
 msgid "--mirror can't be combined with refspecs"
 msgstr "--mirror 不能和引用表达式同时使用"
 
-#: builtin/push.c:359
+#: builtin/push.c:385
 msgid "--all and --mirror are incompatible"
 msgstr "--all 和 --mirror 不兼容"
 
-#: builtin/push.c:419
+#: builtin/push.c:445
 msgid "repository"
 msgstr "版本库"
 
-#: builtin/push.c:420
+#: builtin/push.c:446
 msgid "push all refs"
 msgstr "推送所有引用"
 
-#: builtin/push.c:421
+#: builtin/push.c:447
 msgid "mirror all refs"
 msgstr "镜像所有引用"
 
-#: builtin/push.c:423
+#: builtin/push.c:449
 msgid "delete refs"
 msgstr "删除引用"
 
-#: builtin/push.c:424
+#: builtin/push.c:450
 msgid "push tags (can't be used with --all or --mirror)"
 msgstr "推送 tags(不能使用 --all or --mirror)"
 
-#: builtin/push.c:427
+#: builtin/push.c:453
 msgid "force updates"
 msgstr "强制更新"
 
-#: builtin/push.c:428
+#: builtin/push.c:454
 msgid "check"
 msgstr "检查"
 
-#: builtin/push.c:429
+#: builtin/push.c:455
 msgid "control recursive pushing of submodules"
 msgstr "控制子模组的递归推送"
 
-#: builtin/push.c:431
+#: builtin/push.c:457
 msgid "use thin pack"
 msgstr "使用精简打包"
 
-#: builtin/push.c:432 builtin/push.c:433
+#: builtin/push.c:458 builtin/push.c:459
 msgid "receive pack program"
 msgstr "接收包程序"
 
-#: builtin/push.c:434
+#: builtin/push.c:460
 msgid "set upstream for git pull/status"
 msgstr "设置 git pull/status 的上游"
 
-#: builtin/push.c:437
+#: builtin/push.c:463
 msgid "prune locally removed refs"
 msgstr "清除本地删除的引用"
 
-#: builtin/push.c:439
+#: builtin/push.c:465
 msgid "bypass pre-push hook"
 msgstr "绕过 pre-push 钩子"
 
-#: builtin/push.c:448
+#: builtin/push.c:466
+msgid "push missing but relevant tags"
+msgstr "推送缺失的有关的 tags"
+
+#: builtin/push.c:476
 msgid "--delete is incompatible with --all, --mirror and --tags"
 msgstr "--delete 与 --all、--mirror 及 --tags 不兼容"
 
-#: builtin/push.c:450
+#: builtin/push.c:478
 msgid "--delete doesn't make sense without any refs"
 msgstr "--delete 未接任何引用没有意义"
 
@@ -7190,70 +7746,80 @@ msgstr ""
 "u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--"
 "index-output=<文件>] (--empty | <树或提交1> [<树或提交2> [<树或提交3>]])"
 
-#: builtin/read-tree.c:108
+#: builtin/read-tree.c:109
 msgid "write resulting index to <file>"
 msgstr "将索引结果写入 <file>"
 
-#: builtin/read-tree.c:111
+#: builtin/read-tree.c:112
 msgid "only empty the index"
 msgstr "只是清空索引"
 
-#: builtin/read-tree.c:113
+#: builtin/read-tree.c:114
 msgid "Merging"
 msgstr "合并"
 
-#: builtin/read-tree.c:115
+#: builtin/read-tree.c:116
 msgid "perform a merge in addition to a read"
 msgstr "读取之余再执行一个合并"
 
-#: builtin/read-tree.c:117
+#: builtin/read-tree.c:118
 msgid "3-way merge if no file level merging required"
 msgstr "如果没有文件级合并需要,执行三路合并"
 
-#: builtin/read-tree.c:119
+#: builtin/read-tree.c:120
 msgid "3-way merge in presence of adds and removes"
 msgstr "存在添加和删除时,也执行三路合并"
 
-#: builtin/read-tree.c:121
+#: builtin/read-tree.c:122
 msgid "same as -m, but discard unmerged entries"
 msgstr "类似于 -m,但丢弃未合并的条目"
 
-#: builtin/read-tree.c:122
+#: builtin/read-tree.c:123
 msgid "<subdirectory>/"
 msgstr "<子目录>/"
 
-#: builtin/read-tree.c:123
+#: builtin/read-tree.c:124
 msgid "read the tree into the index under <subdirectory>/"
 msgstr "读取树对象到索引的 <子目录>/ 下"
 
-#: builtin/read-tree.c:126
+#: builtin/read-tree.c:127
 msgid "update working tree with merge result"
 msgstr "用合并的结果更新工作区"
 
-#: builtin/read-tree.c:128
+#: builtin/read-tree.c:129
 msgid "gitignore"
 msgstr "gitignore"
 
-#: builtin/read-tree.c:129
+#: builtin/read-tree.c:130
 msgid "allow explicitly ignored files to be overwritten"
 msgstr "允许忽略文件中设定的文件可以被覆盖"
 
-#: builtin/read-tree.c:132
+#: builtin/read-tree.c:133
 msgid "don't check the working tree after merging"
 msgstr "合并后不检查工作区"
 
-#: builtin/read-tree.c:133
+#: builtin/read-tree.c:134
 msgid "don't update the index or the work tree"
 msgstr "不更新索引区和工作区"
 
-#: builtin/read-tree.c:135
+#: builtin/read-tree.c:136
 msgid "skip applying sparse checkout filter"
 msgstr "跳过应用稀疏检出过滤器"
 
-#: builtin/read-tree.c:137
+#: builtin/read-tree.c:138
 msgid "debug unpack-trees"
 msgstr "调试 unpack-trees"
 
+#: builtin/reflog.c:500
+#, c-format
+msgid "%s' for '%s' is not a valid timestamp"
+msgstr "'%2$s' 的值 '%1$s' 不是一个有效的时间戳"
+
+#: builtin/reflog.c:616 builtin/reflog.c:621
+#, c-format
+msgid "'%s' is not a valid timestamp"
+msgstr "'%s' 不是一个有效的时间戳"
+
 #: builtin/remote.c:11
 msgid "git remote [-v | --verbose]"
 msgstr "git remote [-v | --verbose]"
@@ -7824,98 +8390,98 @@ msgstr "无法找到 HEAD 指向的树。"
 msgid "Failed to find tree of %s."
 msgstr "无法找到 %s 指向的树。"
 
-#: builtin/reset.c:98
+#: builtin/reset.c:99
 #, c-format
 msgid "HEAD is now at %s"
 msgstr "HEAD 现在位于 %s"
 
 #  译者:汉字之间无空格,故删除%s前后空格
-#: builtin/reset.c:169
+#: builtin/reset.c:172
 #, c-format
 msgid "Cannot do a %s reset in the middle of a merge."
 msgstr "在合并过程中不能做%s重置操作。"
 
-#: builtin/reset.c:248
+#: builtin/reset.c:251
 msgid "be quiet, only report errors"
 msgstr "安静模式,只报告错误"
 
-#: builtin/reset.c:250
+#: builtin/reset.c:253
 msgid "reset HEAD and index"
 msgstr "重置 HEAD 和索引"
 
-#: builtin/reset.c:251
+#: builtin/reset.c:254
 msgid "reset only HEAD"
 msgstr "只重置 HEAD"
 
-#: builtin/reset.c:253 builtin/reset.c:255
+#: builtin/reset.c:256 builtin/reset.c:258
 msgid "reset HEAD, index and working tree"
 msgstr "重置 HEAD、索引和工作区"
 
-#: builtin/reset.c:257
+#: builtin/reset.c:260
 msgid "reset HEAD but keep local changes"
 msgstr "重置 HEAD 但保存本地变更"
 
-#: builtin/reset.c:275
+#: builtin/reset.c:278
 #, c-format
 msgid "Failed to resolve '%s' as a valid revision."
 msgstr "无法将 '%s' 解析为一个有效的版本。"
 
-#: builtin/reset.c:278 builtin/reset.c:286
+#: builtin/reset.c:281 builtin/reset.c:289
 #, c-format
 msgid "Could not parse object '%s'."
 msgstr "不能解析对象 '%s'。"
 
-#: builtin/reset.c:283
+#: builtin/reset.c:286
 #, c-format
 msgid "Failed to resolve '%s' as a valid tree."
 msgstr "无法将 '%s' 解析为一个有效的树对象。"
 
-#: builtin/reset.c:292
+#: builtin/reset.c:295
 msgid "--patch is incompatible with --{hard,mixed,soft}"
 msgstr "--patch 与 --{hard、mixed、soft} 选项不兼容"
 
-#: builtin/reset.c:301
+#: builtin/reset.c:304
 msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
 msgstr "--mixed 带路径已弃用,而是用 'git reset -- <paths>'。"
 
 #  译者:汉字之间无空格,故删除%s前后空格
-#: builtin/reset.c:303
+#: builtin/reset.c:306
 #, c-format
 msgid "Cannot do %s reset with paths."
 msgstr "不能带路径进行%s重置。"
 
 #  译者:汉字之间无空格,故删除%s前后空格
-#: builtin/reset.c:313
+#: builtin/reset.c:316
 #, c-format
 msgid "%s reset is not allowed in a bare repository"
 msgstr "不能对裸版本库进行%s重置"
 
-#: builtin/reset.c:333
+#: builtin/reset.c:336
 #, c-format
 msgid "Could not reset index file to revision '%s'."
 msgstr "不能重置索引文件至版本 '%s'。"
 
-#: builtin/reset.c:339
+#: builtin/reset.c:342
 msgid "Unstaged changes after reset:"
 msgstr "重置后撤出暂存区的变更:"
 
-#: builtin/reset.c:344
+#: builtin/reset.c:347
 msgid "Could not write new index file."
 msgstr "不能写入新的索引文件。"
 
-#: builtin/rev-parse.c:339
+#: builtin/rev-parse.c:345
 msgid "git rev-parse --parseopt [options] -- [<args>...]"
 msgstr "git rev-parse --parseopt [选项] -- [<参数>...]"
 
-#: builtin/rev-parse.c:344
+#: builtin/rev-parse.c:350
 msgid "keep the `--` passed as an arg"
 msgstr "保持 `--` 作为一个参数传递"
 
-#: builtin/rev-parse.c:346
+#: builtin/rev-parse.c:352
 msgid "stop parsing after the first non-option argument"
 msgstr "遇到第一个非选项参数后停止解析"
 
-#: builtin/rev-parse.c:464
+#: builtin/rev-parse.c:470
 msgid ""
 "git rev-parse --parseopt [options] -- [<args>...]\n"
 "   or: git rev-parse --sq-quote [<arg>...]\n"
@@ -7930,190 +8496,210 @@ msgstr ""
 "初次使用时执行 \"git rev-parse --parseopt -h\" 来获得更多信息。"
 
 #: builtin/revert.c:22
-msgid "git revert [options] <commit-ish>"
-msgstr "git revert [选项] <提交号>"
+msgid "git revert [options] <commit-ish>..."
+msgstr "git revert [选项] <提交号>..."
 
 #: builtin/revert.c:23
 msgid "git revert <subcommand>"
 msgstr "git revert <子命令>"
 
 #: builtin/revert.c:28
-msgid "git cherry-pick [options] <commit-ish>"
-msgstr "git cherry-pick [选项] <提交号>"
+msgid "git cherry-pick [options] <commit-ish>..."
+msgstr "git cherry-pick [选项] <提交号>..."
 
 #: builtin/revert.c:29
 msgid "git cherry-pick <subcommand>"
 msgstr "git cherry-pick <子命令>"
 
-#: builtin/revert.c:70 builtin/revert.c:92
+#: builtin/revert.c:71 builtin/revert.c:94
 #, c-format
 msgid "%s: %s cannot be used with %s"
 msgstr "%s:%s 不能和 %s 同时使用"
 
-#: builtin/revert.c:103
+#: builtin/revert.c:105
 msgid "end revert or cherry-pick sequence"
 msgstr "终止反转或拣选操作"
 
-#: builtin/revert.c:104
+#: builtin/revert.c:106
 msgid "resume revert or cherry-pick sequence"
 msgstr "继续反转或拣选操作"
 
-#: builtin/revert.c:105
+#: builtin/revert.c:107
 msgid "cancel revert or cherry-pick sequence"
 msgstr "取消反转或拣选操作"
 
-#: builtin/revert.c:106
+#: builtin/revert.c:108
 msgid "don't automatically commit"
 msgstr "不要自动提交"
 
-#: builtin/revert.c:107
+#: builtin/revert.c:109
 msgid "edit the commit message"
 msgstr "编辑提交说明"
 
-#: builtin/revert.c:110
+#: builtin/revert.c:112
 msgid "parent number"
 msgstr "父编号"
 
-#: builtin/revert.c:112
+#: builtin/revert.c:114
 msgid "merge strategy"
 msgstr "合并策略"
 
-#: builtin/revert.c:113
+#: builtin/revert.c:115
 msgid "option"
 msgstr "选项"
 
-#: builtin/revert.c:114
+#: builtin/revert.c:116
 msgid "option for merge strategy"
 msgstr "合并策略的选项"
 
-#: builtin/revert.c:125
+#: builtin/revert.c:127
 msgid "append commit name"
 msgstr "追加提交名称"
 
-#: builtin/revert.c:126
+#: builtin/revert.c:128
 msgid "allow fast-forward"
 msgstr "允许快进式"
 
-#: builtin/revert.c:127
+#: builtin/revert.c:129
 msgid "preserve initially empty commits"
 msgstr "保留初始化的空提交"
 
-#: builtin/revert.c:128
+#: builtin/revert.c:130
 msgid "allow commits with empty messages"
 msgstr "允许提交说明为空"
 
-#: builtin/revert.c:129
+#: builtin/revert.c:131
 msgid "keep redundant, empty commits"
 msgstr "保持多余的、空的提交"
 
-#: builtin/revert.c:133
+#: builtin/revert.c:135
 msgid "program error"
 msgstr "程序错误"
 
-#: builtin/revert.c:223
+#: builtin/revert.c:225
 msgid "revert failed"
 msgstr "还原失败"
 
-#: builtin/revert.c:238
+#: builtin/revert.c:240
 msgid "cherry-pick failed"
 msgstr "拣选失败"
 
-#: builtin/rm.c:15
+#: builtin/rm.c:16
 msgid "git rm [options] [--] <file>..."
 msgstr "git rm [选项] [--] <文件>..."
 
-#: builtin/rm.c:64 builtin/rm.c:186
-#, c-format
+#: builtin/rm.c:64
+msgid ""
+"the following submodule (or one of its nested submodules)\n"
+"uses a .git directory:"
+msgid_plural ""
+"the following submodules (or one of its nested submodules)\n"
+"use a .git directory:"
+msgstr[0] "如下子模组(或一个其嵌套子模组)使用了一个 .git 目录:"
+msgstr[1] "如下子模组(或一个其嵌套子模组)使用了一个 .git 目录:"
+
+#: builtin/rm.c:70
 msgid ""
-"submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+"\n"
 "(use 'rm -rf' if you really want to remove it including all of its history)"
 msgstr ""
-"子模组 '%s'(或者它的一个嵌套子模组)使用一个 .git 目录\n"
+"\n"
 "(使用 'rm -rf' 命令如果您真的想删除它及其全部历史)"
 
-#: builtin/rm.c:174
-#, c-format
+#: builtin/rm.c:230
+msgid ""
+"the following file has staged content different from both the\n"
+"file and the HEAD:"
+msgid_plural ""
+"the following files have staged content different from both the\n"
+"file and the HEAD:"
+msgstr[0] "如下文件其暂存的内容和工作区及 HEAD 中的都不一样:"
+msgstr[1] "如下文件其暂存的内容和工作区及 HEAD 中的都不一样:"
+
+#: builtin/rm.c:235
 msgid ""
-"'%s' has staged content different from both the file and the HEAD\n"
+"\n"
 "(use -f to force removal)"
 msgstr ""
-"'%s' 暂存的内容和工作区及 HEAD 中的都不一样\n"
+"\n"
 "(使用 -f 强制删除)"
 
-#: builtin/rm.c:180
-#, c-format
-msgid ""
-"'%s' has changes staged in the index\n"
-"(use --cached to keep the file, or -f to force removal)"
-msgstr ""
-"'%s' 有变更已暂存至索引中\n"
-"(使用 --cached 保存文件,或用 -f 强制删除)"
+#: builtin/rm.c:239
+msgid "the following file has changes staged in the index:"
+msgid_plural "the following files have changes staged in the index:"
+msgstr[0] "如下文件相对索引区的内容有修改"
+msgstr[1] "如下文件相对索引区的内容有修改"
 
-#: builtin/rm.c:191
-#, c-format
+#: builtin/rm.c:243 builtin/rm.c:254
 msgid ""
-"'%s' has local modifications\n"
+"\n"
 "(use --cached to keep the file, or -f to force removal)"
 msgstr ""
-"'%s' 有本地修改\n"
-"(使用 --cached 保存文件,或用 -f 强制删除)"
+"\n"
+"(使用 --cached 保留本地文件,或用 -f 强制删除)"
+
+#: builtin/rm.c:251
+msgid "the following file has local modifications:"
+msgid_plural "the following files have local modifications:"
+msgstr[0] "如下文件有本地修改:"
+msgstr[1] "如下文件有本地修改:"
 
-#: builtin/rm.c:207
+#: builtin/rm.c:269
 msgid "do not list removed files"
 msgstr "不列出删除的文件"
 
-#: builtin/rm.c:208
+#: builtin/rm.c:270
 msgid "only remove from the index"
 msgstr "只从索引区删除"
 
-#: builtin/rm.c:209
+#: builtin/rm.c:271
 msgid "override the up-to-date check"
 msgstr "忽略文件更新状态检查"
 
-#: builtin/rm.c:210
+#: builtin/rm.c:272
 msgid "allow recursive removal"
 msgstr "允许递归删除"
 
-#: builtin/rm.c:212
+#: builtin/rm.c:274
 msgid "exit with a zero status even if nothing matched"
 msgstr "即使没有匹配,也以零状态退出"
 
-#: builtin/rm.c:283
+#: builtin/rm.c:345
 #, c-format
 msgid "not removing '%s' recursively without -r"
 msgstr "未提供 -r 选项不会递归删除 '%s'"
 
-#: builtin/rm.c:322
+#: builtin/rm.c:384
 #, c-format
 msgid "git rm: unable to remove %s"
 msgstr "git rm:不能删除 %s"
 
 #: builtin/shortlog.c:13
-msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
-msgstr "git shortlog [-n] [-s] [-e] [-w] [版本选项] [--] [<提交号>... ]"
+msgid "git shortlog [<options>] [<revision range>] [[--] [<path>...]]"
+msgstr "git shortlog [<选项>] [<版本范围>] [[--] [<路径>...]]"
 
-#: builtin/shortlog.c:133
+#: builtin/shortlog.c:131
 #, c-format
 msgid "Missing author: %s"
 msgstr "缺少作者:%s"
 
-#: builtin/shortlog.c:229
+#: builtin/shortlog.c:228
 msgid "sort output according to the number of commits per author"
 msgstr "根据每个作者的提交数量排序"
 
-#: builtin/shortlog.c:231
+#: builtin/shortlog.c:230
 msgid "Suppress commit descriptions, only provides commit count"
 msgstr "隐藏提交说明,只提供提交数量"
 
-#: builtin/shortlog.c:233
+#: builtin/shortlog.c:232
 msgid "Show the email address of each author"
 msgstr "显示每个作者的电子邮件地址"
 
-#: builtin/shortlog.c:234
+#: builtin/shortlog.c:233
 msgid "w[,i1[,i2]]"
 msgstr "w[,i1[,i2]]"
 
-#: builtin/shortlog.c:235
+#: builtin/shortlog.c:234
 msgid "Linewrap output"
 msgstr "折行输出"
 
@@ -8133,47 +8719,47 @@ msgstr ""
 msgid "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"
 msgstr "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"
 
-#: builtin/show-branch.c:651
+#: builtin/show-branch.c:650
 msgid "show remote-tracking and local branches"
 msgstr "显示远程跟踪的和本地的分支"
 
-#: builtin/show-branch.c:653
+#: builtin/show-branch.c:652
 msgid "show remote-tracking branches"
 msgstr "显示远程跟踪的分支"
 
-#: builtin/show-branch.c:655
+#: builtin/show-branch.c:654
 msgid "color '*!+-' corresponding to the branch"
 msgstr "着色 '*!+-' 到相应的分支"
 
-#: builtin/show-branch.c:657
+#: builtin/show-branch.c:656
 msgid "show <n> more commits after the common ancestor"
 msgstr "显示共同祖先后的 <n> 个提交"
 
-#: builtin/show-branch.c:659
+#: builtin/show-branch.c:658
 msgid "synonym to more=-1"
 msgstr "和 more=-1 同义"
 
-#: builtin/show-branch.c:660
+#: builtin/show-branch.c:659
 msgid "suppress naming strings"
 msgstr "不显示字符串命名"
 
-#: builtin/show-branch.c:662
+#: builtin/show-branch.c:661
 msgid "include the current branch"
 msgstr "包括当前分支"
 
-#: builtin/show-branch.c:664
+#: builtin/show-branch.c:663
 msgid "name commits with their object names"
 msgstr "以对象名字命名提交"
 
-#: builtin/show-branch.c:666
+#: builtin/show-branch.c:665
 msgid "show possible merge bases"
 msgstr "显示可能合并的基线"
 
-#: builtin/show-branch.c:668
+#: builtin/show-branch.c:667
 msgid "show refs unreachable from any other ref"
 msgstr "显示没有任何引用的的引用"
 
-#: builtin/show-branch.c:670
+#: builtin/show-branch.c:669
 msgid "show commits in topological order"
 msgstr "以拓扑顺序显示提交"
 
@@ -8186,14 +8772,14 @@ msgid "show merges reachable from only one tip"
 msgstr "显示仅一个分支可访问的合并提交"
 
 #: builtin/show-branch.c:676
-msgid "show commits where no parent comes before its children"
-msgstr "æ\98¾ç¤ºæ\8f\90交æ\97¶ä»¥æ\97¶é\97´ä¸ºåº\8fä¸\94ç\88¶æ\8f\90交ä¸\8dè\83½é¢\86å\85\88å­\90æ\8f\90交"
+msgid "topologically sort, maintaining date order where possible"
+msgstr "æ\8b\93æ\89\91æ\96¹å¼\8fæ\8e\92åº\8fï¼\8c并尽å\8f¯è\83½å\9c°ä¿\9dæ\8c\81æ\97¥æ\9c\9f顺åº\8f"
 
-#: builtin/show-branch.c:678
+#: builtin/show-branch.c:679
 msgid "<n>[,<base>]"
 msgstr "<n>[,<base>]"
 
-#: builtin/show-branch.c:679
+#: builtin/show-branch.c:680
 msgid "show <n> most recent ref-log entries starting at base"
 msgstr "显示从 base 开始的 <n> 条最近的引用日志记录"
 
@@ -8209,35 +8795,35 @@ msgstr ""
 msgid "git show-ref --exclude-existing[=pattern] < ref-list"
 msgstr "git show-ref --exclude-existing[=模式] < 引用列表"
 
-#: builtin/show-ref.c:165
+#: builtin/show-ref.c:168
 msgid "only show tags (can be combined with heads)"
 msgstr "只显示 tags(可以和头共用)"
 
-#: builtin/show-ref.c:166
+#: builtin/show-ref.c:169
 msgid "only show heads (can be combined with tags)"
 msgstr "只显示头(可以和 tags 共用)"
 
-#: builtin/show-ref.c:167
+#: builtin/show-ref.c:170
 msgid "stricter reference checking, requires exact ref path"
 msgstr "更严格的引用检测,需要精确的引用路径"
 
-#: builtin/show-ref.c:170 builtin/show-ref.c:172
-msgid "show the HEAD reference"
-msgstr "显示 HEAD 引用"
+#: builtin/show-ref.c:173 builtin/show-ref.c:176
+msgid "show the HEAD reference, even if it would be filtered out"
+msgstr "显示 HEAD 引用,即使被过滤掉"
 
-#: builtin/show-ref.c:174
+#: builtin/show-ref.c:178
 msgid "dereference tags into object IDs"
 msgstr "转换 tags 到对象ID"
 
-#: builtin/show-ref.c:176
+#: builtin/show-ref.c:180
 msgid "only show SHA1 hash using <n> digits"
 msgstr "只显示使用 <n> 个数字的 SHA1 哈希"
 
-#: builtin/show-ref.c:180
+#: builtin/show-ref.c:184
 msgid "do not print results to stdout (useful with --verify)"
 msgstr "不打印结果到标准输出(例如与 --verify 参数共用)"
 
-#: builtin/show-ref.c:182
+#: builtin/show-ref.c:186
 msgid "show refs from stdin that aren't in local repository"
 msgstr "显示从标准输入中读入的不在本地版本库中的引用"
 
@@ -8649,23 +9235,32 @@ msgstr "将 <前缀> 子目录内容写到一个树对象"
 msgid "only useful for debugging"
 msgstr "只对调试有用"
 
-#: git.c:16
-msgid "See 'git help <command>' for more information on a specific command."
-msgstr "参见 'git help <command>' 以获得该特定命令的详细信息。"
+#: git.c:17
+msgid ""
+"'git help -a' and 'git help -g' lists available subcommands and some\n"
+"concept guides. See 'git help <command>' or 'git help <concept>'\n"
+"to read about a specific subcommand or concept."
+msgstr ""
+"命令 'git help -a' 和 'git help -g' 显示可用的子命令和一些指南。参见\n"
+"'git help <命令>' 或 'git help <指南>' 来查看给定的子命令帮助或指南。"
+
+#: parse-options.h:144
+msgid "expiry date"
+msgstr "逾期日期"
 
-#: parse-options.h:156
+#: parse-options.h:159
 msgid "no-op (backward compatibility)"
 msgstr "空操作(向后兼容)"
 
-#: parse-options.h:232
+#: parse-options.h:236
 msgid "be more verbose"
 msgstr "更加详细"
 
-#: parse-options.h:234
+#: parse-options.h:238
 msgid "be more quiet"
 msgstr "更加安静"
 
-#: parse-options.h:240
+#: parse-options.h:244
 msgid "use <n> digits to display SHA-1s"
 msgstr "用 <n> 位数字显示 SHA-1 哈希值"
 
@@ -8722,8 +9317,8 @@ msgid "Move or rename a file, a directory, or a symlink"
 msgstr "移动或重命名一个文件、目录或符号链接"
 
 #: common-cmds.h:21
-msgid "Fetch from and merge with another repository or a local branch"
-msgstr "获取并合并另外的版本库或一个本地分支"
+msgid "Fetch from and integrate with another repository or a local branch"
+msgstr "获取并整合另外的版本库或一个本地分支"
 
 #: common-cmds.h:22
 msgid "Update remote refs along with associated objects"
@@ -8766,11 +9361,11 @@ msgstr "您好像在上一次 'am' 失败后移动了 HEAD。未回退至 ORIG_H
 #: git-am.sh:105
 #, sh-format
 msgid ""
-"When you have resolved this problem, run \"$cmdline --resolved\".\n"
+"When you have resolved this problem, run \"$cmdline --continue\".\n"
 "If you prefer to skip this patch, run \"$cmdline --skip\" instead.\n"
 "To restore the original branch and stop patching, run \"$cmdline --abort\"."
 msgstr ""
-"当您解决了此问题后,执行 \"$cmdline --resolved\"。\n"
+"当您解决了此问题后,执行 \"$cmdline --continue\"。\n"
 "如果您想跳过此补丁,则执行 \"$cmdline --skip\"。\n"
 "要恢复原分支并停止打补丁,执行 \"$cmdline --abort\"。"
 
@@ -8823,25 +9418,34 @@ msgstr ""
 "参数 -b/--binary 已经很长时间不做任何实质操作了,并且将被删除。\n"
 "请不要再使用它了。"
 
-#: git-am.sh:477
+#: git-am.sh:479
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
-msgstr "之前的变基目录 $dotest 仍然存在但给出了mbox。"
+msgstr "之前的变基目录 $dotest 仍然存在,但却提供了 mbox。"
 
-#: git-am.sh:482
+#: git-am.sh:484
 msgid "Please make up your mind. --skip or --abort?"
 msgstr "请下决心。--skip 或是 --abort ?"
 
-#: git-am.sh:509
+#: git-am.sh:520
+#, sh-format
+msgid ""
+"Stray $dotest directory found.\n"
+"Use \"git am --abort\" to remove it."
+msgstr ""
+"发现了错误的 $dotest 目录。\n"
+"使用 \"git am --abort\" 删除它。"
+
+#: git-am.sh:528
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr "解决操作未进行,我们不会继续。"
 
-#: git-am.sh:575
+#: git-am.sh:594
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
 msgstr "脏的索引:不能应用补丁(脏文件:$files)"
 
-#: git-am.sh:679
+#: git-am.sh:698
 #, sh-format
 msgid ""
 "Patch is empty.  Was it split wrong?\n"
@@ -8852,15 +9456,15 @@ msgstr ""
 "如果您想要跳过这个补丁,执行 \"$cmdline --skip\"。\n"
 "要恢复原分支并停止打补丁,执行 \"$cmdline --abort\"。"
 
-#: git-am.sh:706
+#: git-am.sh:725
 msgid "Patch does not have a valid e-mail address."
 msgstr "补丁中没有一个有效的邮件地址。"
 
-#: git-am.sh:753
+#: git-am.sh:772
 msgid "cannot be interactive without stdin connected to a terminal."
 msgstr "标准输入没有和终端关联,不能进行交互式操作。"
 
-#: git-am.sh:757
+#: git-am.sh:776
 msgid "Commit Body is:"
 msgstr "提交内容为:"
 
@@ -8868,16 +9472,16 @@ msgstr "提交内容为:"
 #. TRANSLATORS: Make sure to include [y], [n], [e], [v] and [a]
 #. in your translation. The program will only accept English
 #. input at this point.
-#: git-am.sh:764
+#: git-am.sh:783
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 msgstr "应用?[y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 
-#: git-am.sh:800
+#: git-am.sh:819
 #, sh-format
 msgid "Applying: $FIRSTLINE"
 msgstr "正应用:$FIRSTLINE"
 
-#: git-am.sh:821
+#: git-am.sh:840
 msgid ""
 "No changes - did you forget to use 'git add'?\n"
 "If there is nothing left to stage, chances are that something else\n"
@@ -8887,22 +9491,22 @@ msgstr ""
 "如果没有什么要添加到暂存区的,则很可能是其它提交已经引入了相同的变更。\n"
 "您也许想要跳过这个补丁。"
 
-#: git-am.sh:829
+#: git-am.sh:848
 msgid ""
 "You still have unmerged paths in your index\n"
 "did you forget to use 'git add'?"
 msgstr "您的索引中仍有未合并的路径。您是否忘了执行 'git add'?"
 
-#: git-am.sh:845
+#: git-am.sh:864
 msgid "No changes -- Patch already applied."
 msgstr "没有变更 —— 补丁已经应用过。"
 
-#: git-am.sh:855
+#: git-am.sh:874
 #, sh-format
 msgid "Patch failed at $msgnum $FIRSTLINE"
 msgstr "补丁失败于 $msgnum $FIRSTLINE"
 
-#: git-am.sh:858
+#: git-am.sh:877
 #, sh-format
 msgid ""
 "The copy of the patch that failed is found in:\n"
@@ -8911,7 +9515,7 @@ msgstr ""
 "失败的补丁文件副本位于:\n"
 "   $dotest/patch"
 
-#: git-am.sh:876
+#: git-am.sh:895
 msgid "applying to an empty history"
 msgstr "正应用到一个空历史上"
 
@@ -9010,16 +9614,16 @@ msgstr ""
 "然后需要提供我至少一个好版本和一个坏版本。\n"
 "(您可以用 \"git bisect bad\" 和 \"git bisect good\" 命令来标识。)"
 
-#: git-bisect.sh:347 git-bisect.sh:474
+#: git-bisect.sh:363 git-bisect.sh:490
 msgid "We are not bisecting."
 msgstr "我们没有在二分查找。"
 
-#: git-bisect.sh:354
+#: git-bisect.sh:370
 #, sh-format
 msgid "'$invalid' is not a valid commit"
 msgstr "'$invalid' 不是一个有效的提交"
 
-#: git-bisect.sh:363
+#: git-bisect.sh:379
 #, sh-format
 msgid ""
 "Could not check out original HEAD '$branch'.\n"
@@ -9028,25 +9632,25 @@ msgstr ""
 "不能检出原始 HEAD '$branch'。\n"
 "尝试 'git bisect reset <提交>'。"
 
-#: git-bisect.sh:390
+#: git-bisect.sh:406
 msgid "No logfile given"
 msgstr "未提供日志文件"
 
-#: git-bisect.sh:391
+#: git-bisect.sh:407
 #, sh-format
 msgid "cannot read $file for replaying"
 msgstr "不能读取 $file 来重放"
 
-#: git-bisect.sh:408
+#: git-bisect.sh:424
 msgid "?? what are you talking about?"
 msgstr "?? 您在说什么?"
 
-#: git-bisect.sh:420
+#: git-bisect.sh:436
 #, sh-format
 msgid "running $command"
 msgstr "运行 $command"
 
-#: git-bisect.sh:427
+#: git-bisect.sh:443
 #, sh-format
 msgid ""
 "bisect run failed:\n"
@@ -9055,11 +9659,11 @@ msgstr ""
 "二分查找运行失败:\n"
 "命令 '$command' 的退出码 $res 小于 0 或大于等于 128"
 
-#: git-bisect.sh:453
+#: git-bisect.sh:469
 msgid "bisect run cannot continue any more"
 msgstr "二分查找不能继续运行"
 
-#: git-bisect.sh:459
+#: git-bisect.sh:475
 #, sh-format
 msgid ""
 "bisect run failed:\n"
@@ -9068,7 +9672,7 @@ msgstr ""
 "二分查找运行失败:\n"
 "'bisect_state $state' 退出码为 $res"
 
-#: git-bisect.sh:466
+#: git-bisect.sh:482
 msgid "bisect run success"
 msgstr "二分查找运行成功"
 
@@ -9086,7 +9690,7 @@ msgstr ""
 msgid "Pull is not possible because you have unmerged files."
 msgstr "Pull 不可用,因为您尚有未合并的文件。"
 
-#: git-pull.sh:197
+#: git-pull.sh:203
 msgid "updating an unborn branch with changes added to the index"
 msgstr "更新尚未诞生的分支,变更添加至索引"
 
@@ -9094,7 +9698,7 @@ msgstr "更新尚未诞生的分支,变更添加至索引"
 #. The working tree and the index file is still based on the
 #. $orig_head commit, but we are merging into $curr_head.
 #. First update the working tree to match $curr_head.
-#: git-pull.sh:229
+#: git-pull.sh:235
 #, sh-format
 msgid ""
 "Warning: fetch updated the current branch head.\n"
@@ -9104,15 +9708,15 @@ msgstr ""
 "警告:fetch 更新了当前的分支。您的工作区\n"
 "警告:从原提交 $orig_head 快进。"
 
-#: git-pull.sh:254
+#: git-pull.sh:260
 msgid "Cannot merge multiple branches into empty head"
 msgstr "无法将多个分支合并到空分支"
 
-#: git-pull.sh:258
+#: git-pull.sh:264
 msgid "Cannot rebase onto multiple branches"
 msgstr "无法变基到多个分支"
 
-#: git-rebase.sh:53
+#: git-rebase.sh:54
 msgid ""
 "When you have resolved this problem, run \"git rebase --continue\".\n"
 "If you prefer to skip this patch, run \"git rebase --skip\" instead.\n"
@@ -9123,31 +9727,50 @@ msgstr ""
 "如果您想跳过此补丁,则执行 \"git rebase --skip\"。\n"
 "要恢复原分支并停止变基,执行 \"git rebase --abort\"。"
 
+#: git-rebase.sh:156
+msgid "Applied autostash."
+msgstr "成功应用 autostash。"
+
+#: git-rebase.sh:159
+#, sh-format
+msgid "Cannot store $stash_sha1"
+msgstr "无法保存 $stash_sha1"
+
 #: git-rebase.sh:160
+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 ""
+"应用 autostash 导致冲突。\n"
+"您的修改安全地保存在 stash 中。\n"
+"您可以在任何时候运行 \"git stash pop\" 或 \"git stash drop\"。\n"
+
+#: git-rebase.sh:190
 msgid "The pre-rebase hook refused to rebase."
 msgstr "钩子 pre-rebase 拒绝变基。"
 
-#: git-rebase.sh:165
+#: git-rebase.sh:195
 msgid "It looks like git-am is in progress. Cannot rebase."
 msgstr "似乎正处于在 git-am 的执行过程中。无法变基。"
 
-#: git-rebase.sh:296
+#: git-rebase.sh:329
 msgid "The --exec option must be used with the --interactive option"
 msgstr "选项 --exec 必须和选项 --interactive 同时使用"
 
-#: git-rebase.sh:301
+#: git-rebase.sh:334
 msgid "No rebase in progress?"
 msgstr "没有正在进行的变基?"
 
-#: git-rebase.sh:312
+#: git-rebase.sh:345
 msgid "The --edit-todo action can only be used during interactive rebase."
 msgstr "动作 --edit-todo 只能用在交互式变基过程中。"
 
-#: git-rebase.sh:319
+#: git-rebase.sh:352
 msgid "Cannot read HEAD"
 msgstr "不能读取 HEAD"
 
-#: git-rebase.sh:322
+#: git-rebase.sh:355
 msgid ""
 "You must edit all merge conflicts and then\n"
 "mark them as resolved using git add"
@@ -9155,12 +9778,12 @@ msgstr ""
 "您必须编辑所有的合并冲突,然后通过 git add\n"
 "命令将它们标记为已解决"
 
-#: git-rebase.sh:340
+#: git-rebase.sh:373
 #, sh-format
 msgid "Could not move back to $head_name"
 msgstr "无法移回 $head_name"
 
-#: git-rebase.sh:359
+#: git-rebase.sh:392
 #, sh-format
 msgid ""
 "It seems that there is already a $state_dir_base directory, and\n"
@@ -9179,56 +9802,65 @@ msgstr ""
 "\t$cmd_clear_stale_rebase\n"
 "然后再重新执行变基。为避免您丢失重要数据,我已经停止当前操作。"
 
-#: git-rebase.sh:404
+#: git-rebase.sh:437
 #, sh-format
 msgid "invalid upstream $upstream_name"
 msgstr "无效的上游 $upstream_name"
 
-#: git-rebase.sh:428
+#: git-rebase.sh:461
 #, sh-format
 msgid "$onto_name: there are more than one merge bases"
 msgstr "$onto_name: 有一个以上的合并基准"
 
-#: git-rebase.sh:431 git-rebase.sh:435
+#: git-rebase.sh:464 git-rebase.sh:468
 #, sh-format
 msgid "$onto_name: there is no merge base"
 msgstr "$onto_name: 没有合并基准"
 
-#: git-rebase.sh:440
+#: git-rebase.sh:473
 #, sh-format
 msgid "Does not point to a valid commit: $onto_name"
 msgstr "没有指向一个有效的提交:$onto_name"
 
-#: git-rebase.sh:463
+#: git-rebase.sh:496
 #, sh-format
 msgid "fatal: no such branch: $branch_name"
 msgstr "严重错误:无此分支:$branch_name"
 
-#: git-rebase.sh:483
+#: git-rebase.sh:519
+msgid "Cannot autostash"
+msgstr "无法 autostash"
+
+#: git-rebase.sh:524
+#, sh-format
+msgid "Created autostash: $stash_abbrev"
+msgstr "创建了 autostash: $stash_abbrev"
+
+#: git-rebase.sh:528
 msgid "Please commit or stash them."
 msgstr "请提交或为它们保存进度。"
 
-#: git-rebase.sh:501
+#: git-rebase.sh:548
 #, sh-format
 msgid "Current branch $branch_name is up to date."
 msgstr "当前分支 $branch_name 是最新的。"
 
-#: git-rebase.sh:504
+#: git-rebase.sh:552
 #, sh-format
 msgid "Current branch $branch_name is up to date, rebase forced."
 msgstr "当前分支 $branch_name 是最新的,强制变基。"
 
-#: git-rebase.sh:515
+#: git-rebase.sh:563
 #, sh-format
 msgid "Changes from $mb to $onto:"
 msgstr "变更从 $mb 到 $onto:"
 
 #. Detach HEAD and reset the tree
-#: git-rebase.sh:524
+#: git-rebase.sh:572
 msgid "First, rewinding head to replay your work on top of it..."
 msgstr "首先,重置头指针以便在上面重放您的工作..."
 
-#: git-rebase.sh:532
+#: git-rebase.sh:582
 #, sh-format
 msgid "Fast-forwarded $branch_name to $onto_name."
 msgstr "快进 $branch_name 至 $onto_name。"
@@ -9261,6 +9893,11 @@ msgstr "无法删除临时索引(不应发生)"
 msgid "Cannot record working tree state"
 msgstr "不能记录工作区状态"
 
+#: git-stash.sh:190
+#, sh-format
+msgid "Cannot update $ref_stash with $w_commit"
+msgstr "无法用 $w_commit 更新 $ref_stash"
+
 #. TRANSLATORS: $option is an invalid option, like
 #. `--blah-blah'. The 7 spaces at the beginning of the
 #. second line correspond to "error: ". So you should line
@@ -9271,7 +9908,7 @@ 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:202
+#: git-stash.sh:241
 #, sh-format
 msgid ""
 "error: unknown option for 'stash save': $option\n"
@@ -9280,119 +9917,134 @@ msgstr ""
 "错误:'stash save' 的未知选项:$option\n"
 "      要提供一个描述信息,使用 git stash save -- '$option'"
 
-#: git-stash.sh:223
+#: git-stash.sh:262
 msgid "No local changes to save"
 msgstr "没有要保存的本地修改"
 
-#: git-stash.sh:227
+#: git-stash.sh:268
+msgid ""
+"The following untracked files would NOT be saved but need to be removed by "
+"stash save:"
+msgstr "如下未跟踪文件将不会被保存但是将会因为进度保存而需要被删除:"
+
+#: git-stash.sh:270
+msgid ""
+"Aborting. Consider using either the --force or --include-untracked option."
+msgstr "终止。考虑使用 --force 或者 --include-untracked 选项。"
+
+#: git-stash.sh:274
 msgid "Cannot initialize stash"
 msgstr "无法初始化 stash"
 
-#: git-stash.sh:235
+#: git-stash.sh:278
 msgid "Cannot save the current status"
 msgstr "无法保存当前状态"
 
-#: git-stash.sh:253
+#: git-stash.sh:296
 msgid "Cannot remove worktree changes"
 msgstr "无法删除工作区变更"
 
-#: git-stash.sh:352
+#: git-stash.sh:395
 msgid "No stash found."
 msgstr "未发现 stash。"
 
-#: git-stash.sh:359
+#: git-stash.sh:402
 #, sh-format
 msgid "Too many revisions specified: $REV"
 msgstr "指定了太多的版本:$REV"
 
-#: git-stash.sh:365
+#: git-stash.sh:408
 #, sh-format
 msgid "$reference is not valid reference"
 msgstr "$reference 不是有效的引用"
 
-#: git-stash.sh:393
+#: git-stash.sh:436
 #, sh-format
 msgid "'$args' is not a stash-like commit"
 msgstr "'$args' 不是 stash 样提交"
 
-#: git-stash.sh:404
+#: git-stash.sh:447
 #, sh-format
 msgid "'$args' is not a stash reference"
 msgstr "'$args' 不是一个 stash 引用"
 
-#: git-stash.sh:412
+#: git-stash.sh:455
 msgid "unable to refresh index"
 msgstr "无法刷新索引"
 
-#: git-stash.sh:416
+#: git-stash.sh:459
 msgid "Cannot apply a stash in the middle of a merge"
 msgstr "无法在合并过程中恢复进度"
 
-#: git-stash.sh:424
+#: git-stash.sh:467
 msgid "Conflicts in index. Try without --index."
 msgstr "索引中有冲突。尝试不使用 --index。"
 
-#: git-stash.sh:426
+#: git-stash.sh:469
 msgid "Could not save index tree"
 msgstr "不能保存索引树"
 
-#: git-stash.sh:460
+#: git-stash.sh:503
 msgid "Cannot unstage modified files"
 msgstr "无法还原修改的文件"
 
-#: git-stash.sh:475
+#: git-stash.sh:518
 msgid "Index was not unstashed."
 msgstr "索引的进度没有被恢复。"
 
-#: git-stash.sh:492
+#: git-stash.sh:535
 #, sh-format
 msgid "Dropped ${REV} ($s)"
 msgstr "丢弃了 ${REV} ($s)"
 
-#: git-stash.sh:493
+#: git-stash.sh:536
 #, sh-format
 msgid "${REV}: Could not drop stash entry"
 msgstr "${REV}:不能丢弃进度条目"
 
-#: git-stash.sh:500
+#: git-stash.sh:543
 msgid "No branch name specified"
 msgstr "未指定分支名"
 
-#: git-stash.sh:571
+#: git-stash.sh:615
 msgid "(To restore them type \"git stash apply\")"
 msgstr "(为恢复数据输入 \"git stash apply\")"
 
-#: git-submodule.sh:90
+#: git-submodule.sh:95
 #, sh-format
 msgid "cannot strip one component off url '$remoteurl'"
 msgstr "无法从 url '$remoteurl' 剥离一个组件"
 
-#: git-submodule.sh:195
+#: git-submodule.sh:237
 #, sh-format
 msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
 msgstr "未在 .gitmodules 中发现路径 '$sm_path' 的子模组映射"
 
-#: git-submodule.sh:238
+#: git-submodule.sh:281
 #, sh-format
 msgid "Clone of '$url' into submodule path '$sm_path' failed"
 msgstr "无法克隆 '$url' 到子模组路径 '$sm_path'"
 
-#: git-submodule.sh:250
+#: git-submodule.sh:293
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
 msgstr "Gitdir '$a' 在子模组路径 '$b' 之下或相反"
 
-#: git-submodule.sh:343
+#: git-submodule.sh:403
+msgid "Relative path can only be used from the toplevel of the working tree"
+msgstr "只能在工作区的顶级目录中使用相对路径"
+
+#: git-submodule.sh:413
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
 msgstr "版本库URL:'$repo' 必须是绝对路径或以 ./|../ 起始"
 
-#: git-submodule.sh:360
+#: git-submodule.sh:430
 #, sh-format
 msgid "'$sm_path' already exists in the index"
 msgstr "'$sm_path' 已经存在于索引中"
 
-#: git-submodule.sh:364
+#: git-submodule.sh:434
 #, sh-format
 msgid ""
 "The following path is ignored by one of your .gitignore files:\n"
@@ -9403,188 +10055,377 @@ msgstr ""
 "$sm_path\n"
 "如果您确实想添加它,使用 -f 参数。"
 
-#: git-submodule.sh:382
+#: git-submodule.sh:452
 #, sh-format
 msgid "Adding existing repo at '$sm_path' to the index"
 msgstr "添加位于 '$sm_path' 的现存版本库到索引"
 
-#: git-submodule.sh:384
+#: git-submodule.sh:454
 #, sh-format
 msgid "'$sm_path' already exists and is not a valid git repo"
 msgstr "'$sm_path' 已存在且不是一个有效的 git 版本库"
 
-#: git-submodule.sh:392
+#: git-submodule.sh:462
 #, sh-format
 msgid "A git directory for '$sm_name' is found locally with remote(s):"
 msgstr "本地发现 '$sm_name' 的一个 git 目录,与其对应的远程版本库:"
 
-#: git-submodule.sh:394
+#: git-submodule.sh:464
 #, sh-format
 msgid ""
 "If you want to reuse this local git directory instead of cloning again from"
 msgstr "如果您想重用此本地 git 目录而不是重新克隆自"
 
-#: git-submodule.sh:396
+#: git-submodule.sh:466
 #, sh-format
 msgid ""
 "use the '--force' option. If the local git directory is not the correct repo"
 msgstr "使用 '--force' 参数。如果本地 git 目录不是正确的版本库"
 
-#: git-submodule.sh:397
+#: git-submodule.sh:467
 #, sh-format
 msgid ""
 "or you are unsure what this means choose another name with the '--name' "
 "option."
 msgstr "或者您不确定其中含义使用 '--name' 参数选择另外一个名称。"
 
-#: git-submodule.sh:399
+#: git-submodule.sh:469
 #, sh-format
 msgid "Reactivating local git directory for submodule '$sm_name'."
 msgstr "激活本地 git 目录到子模组 '$sm_name'。"
 
-#: git-submodule.sh:411
+#: git-submodule.sh:481
 #, sh-format
 msgid "Unable to checkout submodule '$sm_path'"
 msgstr "不能检出子模组 '$sm_path'"
 
-#: git-submodule.sh:416
+#: git-submodule.sh:486
 #, sh-format
 msgid "Failed to add submodule '$sm_path'"
 msgstr "无法添加子模组 '$sm_path'"
 
-#: git-submodule.sh:425
+#: git-submodule.sh:495
 #, sh-format
 msgid "Failed to register submodule '$sm_path'"
 msgstr "无法注册子模组 '$sm_path'"
 
-#: git-submodule.sh:468
+#: git-submodule.sh:539
+#, sh-format
+msgid "Entering '$prefix$displaypath'"
+msgstr "正在进入 '$prefix$displaypath'"
+
+#: git-submodule.sh:554
+#, sh-format
+msgid "Stopping at '$prefix$displaypath'; script returned non-zero status."
+msgstr "停止于 '$prefix$displaypath',脚本返回非零值。"
+
+#: git-submodule.sh:600
+#, sh-format
+msgid "No url found for submodule path '$displaypath' in .gitmodules"
+msgstr "在 .gitmodules 中未找到子模组路径 '$displaypath' 的 url"
+
+#: git-submodule.sh:609
+#, sh-format
+msgid "Failed to register url for submodule path '$displaypath'"
+msgstr "无法为子模组路径 '$displaypath' 注册 url"
+
+#: git-submodule.sh:611
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$displaypath'"
+msgstr "子模组 '$name' ($url) 未对路径 '$displaypath' 注册"
+
+#: git-submodule.sh:619
+#, sh-format
+msgid "Failed to register update mode for submodule path '$displaypath'"
+msgstr "无法为子模组路径 '$displaypath' 注册更新模式"
+
+#: git-submodule.sh:656
+#, sh-format
+msgid "Use '.' if you really want to deinitialize all submodules"
+msgstr "使用 '.' 如果您真的想要对所有子模组取消初始化"
+
+#: git-submodule.sh:673
+#, sh-format
+msgid "Submodule work tree '$displaypath' contains a .git directory"
+msgstr "子模组工作区 '$displaypath' 包含一个 .git 目录"
+
+#: git-submodule.sh:674
 #, sh-format
-msgid "Entering '$prefix$sm_path'"
-msgstr "正在进入 '$prefix$sm_path'"
+msgid ""
+"(use 'rm -rf' if you really want to remove it including all of its history)"
+msgstr "(使用 'rm -rf' 命令如果您真的想删除它及其全部历史)"
 
-#: git-submodule.sh:482
+#: git-submodule.sh:680
 #, sh-format
-msgid "Stopping at '$sm_path'; script returned non-zero status."
-msgstr "停止于 '$sm_path',脚本返回非零值。"
+msgid ""
+"Submodule work tree '$displaypath' contains local modifications; use '-f' to "
+"discard them"
+msgstr "子模组工作区 '$displaypath' 包含本地修改;使用 '-f' 丢弃它们"
 
-#: git-submodule.sh:526
+#: git-submodule.sh:683
 #, sh-format
-msgid "No url found for submodule path '$sm_path' in .gitmodules"
-msgstr "å\9c¨ .gitmodules ä¸­æ\9cªæ\89¾å\88°å­\90模ç»\84è·¯å¾\84 '$sm_path' ç\9a\84 url"
+msgid "Cleared directory '$displaypath'"
+msgstr "å·²æ¸\85é\99¤ç\9b®å½\95 '$displaypath'"
 
-#: git-submodule.sh:535
+#: git-submodule.sh:684
 #, sh-format
-msgid "Failed to register url for submodule path '$sm_path'"
-msgstr "无法为子模组路径 '$sm_path' 注册 url"
+msgid "Could not remove submodule work tree '$displaypath'"
+msgstr "无法移除子模组工作区 '$displaypath'"
 
-#: git-submodule.sh:537
+#: git-submodule.sh:687
 #, sh-format
-msgid "Submodule '$name' ($url) registered for path '$sm_path'"
-msgstr "子模组 '$name' ($url) 已为路径 '$sm_path' 注册"
+msgid "Could not create empty submodule directory '$displaypath'"
+msgstr "不能创建空的子模组目录 '$displaypath'"
 
-#: git-submodule.sh:545
+#: git-submodule.sh:696
 #, sh-format
-msgid "Failed to register update mode for submodule path '$sm_path'"
-msgstr "无法为子模组路径 '$sm_path' 注册更新模式"
+msgid "Submodule '$name' ($url) unregistered for path '$displaypath'"
+msgstr "子模组 '$name' ($url) 未对路径 '$displaypath' 注册"
 
-#: git-submodule.sh:649
+#: git-submodule.sh:811
 #, sh-format
 msgid ""
-"Submodule path '$sm_path' not initialized\n"
+"Submodule path '$displaypath' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
-"子模组路径 '$sm_path' 没有初始化\n"
+"子模组路径 '$displaypath' 没有初始化\n"
 "也许您想用 'update --init'?"
 
-#: git-submodule.sh:662
+#: git-submodule.sh:824
 #, sh-format
-msgid "Unable to find current revision in submodule path '$sm_path'"
-msgstr "无法在子模组路径 '$sm_path' 中找到当前版本"
+msgid "Unable to find current revision in submodule path '$displaypath'"
+msgstr "无法在子模组路径 '$displaypath' 中找到当前版本"
 
-#: git-submodule.sh:671 git-submodule.sh:695
+#: git-submodule.sh:833
 #, sh-format
 msgid "Unable to fetch in submodule path '$sm_path'"
 msgstr "无法在子模组路径 '$sm_path' 中获取"
 
-#: git-submodule.sh:709
+#: git-submodule.sh:857
 #, sh-format
-msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
-msgstr "无法在子模组路径 '$sm_path' 中变基 '$sha1'"
+msgid "Unable to fetch in submodule path '$displaypath'"
+msgstr "无法在子模组路径 '$displaypath' 中获取"
 
-#: git-submodule.sh:710
+#: git-submodule.sh:871
 #, sh-format
-msgid "Submodule path '$sm_path': rebased into '$sha1'"
-msgstr "子模组路径 '$sm_path':变基至 '$sha1'"
+msgid "Unable to rebase '$sha1' in submodule path '$displaypath'"
+msgstr "无法在子模组路径 '$displaypath' 中变基 '$sha1'"
 
-#: git-submodule.sh:715
+#: git-submodule.sh:872
 #, sh-format
-msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
-msgstr "无法合并 '$sha1' 到子模组路径 '$sm_path' 中"
+msgid "Submodule path '$displaypath': rebased into '$sha1'"
+msgstr "子模组路径 '$displaypath':变基至 '$sha1'"
 
-#: git-submodule.sh:716
+#: git-submodule.sh:877
 #, sh-format
-msgid "Submodule path '$sm_path': merged in '$sha1'"
-msgstr "子模组路径 '$sm_path':已合并入 '$sha1'"
+msgid "Unable to merge '$sha1' in submodule path '$displaypath'"
+msgstr "无法合并 '$sha1' 到子模组路径 '$displaypath' 中"
 
-#: git-submodule.sh:721
+#: git-submodule.sh:878
 #, sh-format
-msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
-msgstr "无法在子模组路径 '$sm_path' 中检出 '$sha1'"
+msgid "Submodule path '$displaypath': merged in '$sha1'"
+msgstr "子模组路径 '$displaypath':已合并入 '$sha1'"
 
-#: git-submodule.sh:722
+#: git-submodule.sh:883
 #, sh-format
-msgid "Submodule path '$sm_path': checked out '$sha1'"
-msgstr "子模组路径 '$sm_path':检出 '$sha1'"
+msgid ""
+"Execution of '$command $sha1' failed in submodule  path '$prefix$sm_path'"
+msgstr "在子模组路径 '$prefix$sm_path' 中执行 '$command $sha1' 失败"
 
-#: git-submodule.sh:744 git-submodule.sh:1066
+#: git-submodule.sh:884
 #, sh-format
-msgid "Failed to recurse into submodule path '$sm_path'"
-msgstr "无法递归进子模组路径 '$sm_path'"
+msgid "Submodule path '$prefix$sm_path': '$command $sha1'"
+msgstr "子模组路径 '$prefix$sm_path': '$command $sha1'"
+
+#: git-submodule.sh:889
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$displaypath'"
+msgstr "无法在子模组路径 '$displaypath' 中检出 '$sha1'"
+
+#: git-submodule.sh:890
+#, sh-format
+msgid "Submodule path '$displaypath': checked out '$sha1'"
+msgstr "子模组路径 '$displaypath':检出 '$sha1'"
+
+#: git-submodule.sh:917
+#, sh-format
+msgid "Failed to recurse into submodule path '$displaypath'"
+msgstr "无法递归进子模组路径 '$displaypath'"
 
-#: git-submodule.sh:852
+#: git-submodule.sh:1025
 msgid "The --cached option cannot be used with the --files option"
 msgstr "选项 --cached 不能和选项 --files 同时使用"
 
 #. unexpected type
-#: git-submodule.sh:892
+#: git-submodule.sh:1066
 #, sh-format
 msgid "unexpected mode $mod_dst"
 msgstr "意外的模式 $mod_dst"
 
 #  译者:注意保持前导空格
-#: git-submodule.sh:910
+#: git-submodule.sh:1086
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_src"
-msgstr "  警告:$name 未包含提交 $sha1_src"
+msgid "  Warn: $display_name doesn't contain commit $sha1_src"
+msgstr "  警告:$display_name 未包含提交 $sha1_src"
 
 #  译者:注意保持前导空格
-#: git-submodule.sh:913
+#: git-submodule.sh:1089
 #, sh-format
-msgid "  Warn: $name doesn't contain commit $sha1_dst"
-msgstr "  警告:$name 未包含提交 $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commit $sha1_dst"
+msgstr "  警告:$display_name 未包含提交 $sha1_dst"
 
 #  译者:注意保持前导空格
-#: git-submodule.sh:916
+#: git-submodule.sh:1092
 #, sh-format
-msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
-msgstr "  警告:$name 未包含提交 $sha1_src 和 $sha1_dst"
+msgid "  Warn: $display_name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr "  警告:$display_name 未包含提交 $sha1_src 和 $sha1_dst"
 
-#: git-submodule.sh:941
+#: git-submodule.sh:1117
 msgid "blob"
 msgstr "数据对象"
 
-#: git-submodule.sh:979
+#: git-submodule.sh:1155
 msgid "Submodules changed but not updated:"
 msgstr "子模组已修改但尚未更新:"
 
-#: git-submodule.sh:981
+#: git-submodule.sh:1157
 msgid "Submodule changes to be committed:"
 msgstr "要提交的子模组变更:"
 
-#: git-submodule.sh:1129
+#: git-submodule.sh:1242
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "无法递归进子模组路径 '$sm_path'"
+
+#: git-submodule.sh:1306
 #, sh-format
-msgid "Synchronizing submodule url for '$prefix$sm_path'"
-msgstr "为 '$prefix$sm_path' 同步子模组 url"
+msgid "Synchronizing submodule url for '$displaypath'"
+msgstr "为 '$displaypath' 同步子模组 url"
+
+#~ msgid ""
+#~ "the following submodule (or one of its nested submodule)\n"
+#~ "uses a .git directory:"
+#~ msgid_plural ""
+#~ "the following submodules (or one of its nested submodule)\n"
+#~ "use a .git directory:"
+#~ msgstr[0] "如下子模组(或一个其嵌套的子模组)使用了一个 .git 目录:"
+#~ msgstr[1] "如下子模组(或一个其嵌套的子模组)使用了一个 .git 目录:"
+
+#~ msgid "Cannot change unmerged .gitmodules, resolve merge conflicts first"
+#~ msgstr "无法修改未合并的 .gitmodules,先解决合并冲突"
+
+#~ msgid "Could not find section in .gitmodules where path=%s"
+#~ msgstr "无法在 .gitmodules 中找到 path=%s 的小节"
+
+#~ msgid "Could not update .gitmodules entry %s"
+#~ msgstr "不能更新 .gitmodules 条目 %s"
+
+#~ msgid "Could not remove .gitmodules entry for %s"
+#~ msgstr "无法移除 %s 的 .gitmodules 条目"
+
+#~ msgid "could not find .gitmodules in index"
+#~ msgstr "无法在索引中找到 .gitmodules"
+
+#~ msgid "reading updated .gitmodules failed"
+#~ msgstr "读取更新后的 .gitmodules 失败"
+
+#~ msgid "unable to stat updated .gitmodules"
+#~ msgstr "不能枚举更新后的 .gitmodules"
+
+#~ msgid "unable to remove .gitmodules from index"
+#~ msgstr "不能从索引中移除 .gitmodules"
+
+#~ msgid "adding updated .gitmodules failed"
+#~ msgstr "添加更新后的 .gitmodules 失败"
+
+#~ msgid "staging updated .gitmodules failed"
+#~ msgstr "将更新后 .gitmodules 添加暂存区失败"
+
+#~ msgid "Could not set core.worktree in %s"
+#~ msgstr "不能在 %s 中设置 core.worktree"
+
+#  译者:注意保持前导空格
+#~ msgid "  (fix conflicts and then run \"git am --resolved\")"
+#~ msgstr "  (解决冲突,然后运行 \"git am --resolved\")"
+
+#  译者:注意保持前导空格
+#~ msgid "  (all conflicts fixed: run \"git commit\")"
+#~ msgstr "  (解决所有冲突后,执行 \"git commit\")"
+
+#~ msgid "more than %d trees given: '%s'"
+#~ msgstr "提供了超过 %d 个树对象:'%s'"
+
+#~ msgid "You cannot combine --no-ff with --ff-only."
+#~ msgstr "您不能将 --no-ff 与 --ff-only 同时使用。"
+
+#~ msgid "Huh? Directory %s is in index and no submodule?"
+#~ msgstr "嗯?目录 %s 在索引中并且不是子模组?"
+
+#~ msgid ""
+#~ "submodule '%s' (or one of its nested submodules) uses a .git directory\n"
+#~ "(use 'rm -rf' if you really want to remove it including all of its "
+#~ "history)"
+#~ msgstr ""
+#~ "子模组 '%s'(或者它的一个嵌套子模组)使用一个 .git 目录\n"
+#~ "(使用 'rm -rf' 命令如果您真的想删除它及其全部历史)"
+
+#~ msgid ""
+#~ "'%s' has changes staged in the index\n"
+#~ "(use --cached to keep the file, or -f to force removal)"
+#~ msgstr ""
+#~ "'%s' 有变更已暂存至索引中\n"
+#~ "(使用 --cached 保存文件,或用 -f 强制删除)"
+
+#~ msgid "show commits where no parent comes before its children"
+#~ msgstr "显示提交时以时间为序且父提交不能领先子提交"
+
+#~ msgid "show the HEAD reference"
+#~ msgstr "显示 HEAD 引用"
+
+#~ msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+#~ msgstr "子模组 '$name' ($url) 已为路径 '$sm_path' 注册"
+
+#~ msgid "Change flags"
+#~ msgstr "修改参数"
+
+#~ msgid ""
+#~ "NOTE: no more files to clean; press \"t\" to toggle flags of git-clean."
+#~ msgstr "注意:没有要清理的文件;尝试按 \"t\" 切换 git-clean 的选项。"
+
+#~ msgid "Failed to recurse into submodule path '$prefix$sm_path'"
+#~ msgstr "无法递归进子模组路径 '$prefix$sm_path'"
+
+#~ msgid "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"
+#~ msgstr "git shortlog [-n] [-s] [-e] [-w] [版本选项] [--] [<提交号>... ]"
+
+#~ msgid "See 'git help <command>' for more information on a specific command."
+#~ msgstr "参见 'git help <command>' 以获得该特定命令的详细信息。"
+
+#~ msgid "BUG: impossible combination of %d and %p"
+#~ msgstr "BUG: 无法组合 %d 和 %p"
+
+#  译者:以下三条实际为一个段落
+#~ msgid "It took %.2f seconds to enumerate untracked files.  'status -uno'"
+#~ msgstr "耗费了 %.2f 秒以枚举未跟踪的文件。'status -uno'"
+
+#  译者:为对齐,下面两句内容有调整
+#~ msgid "may speed it up, but you have to be careful not to forget to add"
+#~ msgstr "也许能提高速度,但您需要小心不要忘了添加新文件"
+
+#~ msgid "new files yourself (see 'git help status')."
+#~ msgstr "(参见 'git help status')。"
+
+#~ msgid "use any ref in .git/refs"
+#~ msgstr "使用 .git/refs 里的任意引用"
+
+#~ msgid "use any tag in .git/refs/tags"
+#~ msgstr "使用 .git/refs/tags 里的任意 tag"
+
+#~ msgid "bad object %s"
+#~ msgstr "坏对象 %s"
+
+#~ msgid "bogus committer info %s"
+#~ msgstr "虚假的提交者信息 %s"
 
 #~ msgid "can't fdopen 'show' output fd"
 #~ msgstr "不能打开 'show' 输出文件句柄"
index d3a82d22d398ae7234f8917f4d8b0770ba4d0c47..74563c92b4cdce8e947a2816a8b56355b11439e6 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -393,36 +393,36 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
        strbuf_addstr(sb, "?=");
 }
 
-void pp_user_info(const struct pretty_print_context *pp,
+static const char *show_ident_date(const struct ident_split *ident,
+                                  enum date_mode mode)
+{
+       unsigned long date = 0;
+       int tz = 0;
+
+       if (ident->date_begin && ident->date_end)
+               date = strtoul(ident->date_begin, NULL, 10);
+       if (ident->tz_begin && ident->tz_end)
+               tz = strtol(ident->tz_begin, NULL, 10);
+       return show_date(date, tz, mode);
+}
+
+void pp_user_info(struct pretty_print_context *pp,
                  const char *what, struct strbuf *sb,
                  const char *line, const char *encoding)
 {
-       struct strbuf name;
-       struct strbuf mail;
        struct ident_split ident;
-       int linelen;
-       char *line_end, *date;
+       char *line_end;
        const char *mailbuf, *namebuf;
        size_t namelen, maillen;
        int max_length = 78; /* per rfc2822 */
-       unsigned long time;
-       int tz;
 
        if (pp->fmt == CMIT_FMT_ONELINE)
                return;
 
-       line_end = strchr(line, '\n');
-       if (!line_end) {
-               line_end = strchr(line, '\0');
-               if (!line_end)
-                       return;
-       }
-
-       linelen = ++line_end - line;
-       if (split_ident_line(&ident, line, linelen))
+       line_end = strchrnul(line, '\n');
+       if (split_ident_line(&ident, line, line_end - line))
                return;
 
-
        mailbuf = ident.mail_begin;
        maillen = ident.mail_end - ident.mail_begin;
        namebuf = ident.name_begin;
@@ -431,54 +431,62 @@ void pp_user_info(const struct pretty_print_context *pp,
        if (pp->mailmap)
                map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
 
-       strbuf_init(&mail, 0);
-       strbuf_init(&name, 0);
-
-       strbuf_add(&mail, mailbuf, maillen);
-       strbuf_add(&name, namebuf, namelen);
-
-       namelen = name.len + mail.len + 3; /* ' ' + '<' + '>' */
-       time = strtoul(ident.date_begin, &date, 10);
-       tz = strtol(date, NULL, 10);
-
        if (pp->fmt == CMIT_FMT_EMAIL) {
+               if (pp->from_ident) {
+                       struct strbuf buf = STRBUF_INIT;
+
+                       strbuf_addstr(&buf, "From: ");
+                       strbuf_add(&buf, namebuf, namelen);
+                       strbuf_addstr(&buf, " <");
+                       strbuf_add(&buf, mailbuf, maillen);
+                       strbuf_addstr(&buf, ">\n");
+                       string_list_append(&pp->in_body_headers,
+                                          strbuf_detach(&buf, NULL));
+
+                       mailbuf = pp->from_ident->mail_begin;
+                       maillen = pp->from_ident->mail_end - mailbuf;
+                       namebuf = pp->from_ident->name_begin;
+                       namelen = pp->from_ident->name_end - namebuf;
+               }
+
                strbuf_addstr(sb, "From: ");
-               if (needs_rfc2047_encoding(name.buf, name.len, RFC2047_ADDRESS)) {
-                       add_rfc2047(sb, name.buf, name.len,
+               if (needs_rfc2047_encoding(namebuf, namelen, RFC2047_ADDRESS)) {
+                       add_rfc2047(sb, namebuf, namelen,
                                    encoding, RFC2047_ADDRESS);
                        max_length = 76; /* per rfc2047 */
-               } else if (needs_rfc822_quoting(name.buf, name.len)) {
+               } else if (needs_rfc822_quoting(namebuf, namelen)) {
                        struct strbuf quoted = STRBUF_INIT;
-                       add_rfc822_quoted(&quoted, name.buf, name.len);
+                       add_rfc822_quoted(&quoted, namebuf, namelen);
                        strbuf_add_wrapped_bytes(sb, quoted.buf, quoted.len,
                                                        -6, 1, max_length);
                        strbuf_release(&quoted);
                } else {
-                       strbuf_add_wrapped_bytes(sb, name.buf, name.len,
+                       strbuf_add_wrapped_bytes(sb, namebuf, namelen,
                                                 -6, 1, max_length);
                }
-               if (namelen - name.len + last_line_length(sb) > max_length)
-                       strbuf_addch(sb, '\n');
 
-               strbuf_addf(sb, " <%s>\n", mail.buf);
+               if (max_length <
+                   last_line_length(sb) + strlen(" <") + maillen + strlen(">"))
+                       strbuf_addch(sb, '\n');
+               strbuf_addf(sb, " <%.*s>\n", (int)maillen, mailbuf);
        } else {
-               strbuf_addf(sb, "%s: %.*s%s <%s>\n", what,
-                             (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0,
-                             "    ", name.buf, mail.buf);
+               strbuf_addf(sb, "%s: %.*s%.*s <%.*s>\n", what,
+                           (pp->fmt == CMIT_FMT_FULLER) ? 4 : 0, "    ",
+                           (int)namelen, namebuf, (int)maillen, mailbuf);
        }
 
-       strbuf_release(&mail);
-       strbuf_release(&name);
-
        switch (pp->fmt) {
        case CMIT_FMT_MEDIUM:
-               strbuf_addf(sb, "Date:   %s\n", show_date(time, tz, pp->date_mode));
+               strbuf_addf(sb, "Date:   %s\n",
+                           show_ident_date(&ident, pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
-               strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822));
+               strbuf_addf(sb, "Date: %s\n",
+                           show_ident_date(&ident, DATE_RFC2822));
                break;
        case CMIT_FMT_FULLER:
-               strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, pp->date_mode));
+               strbuf_addf(sb, "%sDate: %s\n", what,
+                           show_ident_date(&ident, pp->date_mode));
                break;
        default:
                /* notin' */
@@ -594,6 +602,7 @@ static char *replace_encoding_header(char *buf, const char *encoding)
 }
 
 char *logmsg_reencode(const struct commit *commit,
+                     char **commit_encoding,
                      const char *output_encoding)
 {
        static const char *utf8 = "UTF-8";
@@ -615,9 +624,15 @@ char *logmsg_reencode(const struct commit *commit,
                            sha1_to_hex(commit->object.sha1), typename(type));
        }
 
-       if (!output_encoding || !*output_encoding)
+       if (!output_encoding || !*output_encoding) {
+               if (commit_encoding)
+                       *commit_encoding =
+                               get_header(commit, msg, "encoding");
                return msg;
+       }
        encoding = get_header(commit, msg, "encoding");
+       if (commit_encoding)
+               *commit_encoding = encoding;
        use_encoding = encoding ? encoding : utf8;
        if (same_encoding(use_encoding, output_encoding)) {
                /*
@@ -658,7 +673,8 @@ char *logmsg_reencode(const struct commit *commit,
        if (out)
                out = replace_encoding_header(out, output_encoding);
 
-       free(encoding);
+       if (!commit_encoding)
+               free(encoding);
        /*
         * If the re-encoding failed, out might be NULL here; in that
         * case we just return the commit message verbatim.
@@ -688,8 +704,6 @@ static size_t format_person_part(struct strbuf *sb, char part,
 {
        /* currently all placeholders have same length */
        const int placeholder_len = 2;
-       int tz;
-       unsigned long date = 0;
        struct ident_split s;
        const char *name, *mail;
        size_t maillen, namelen;
@@ -716,30 +730,23 @@ static size_t format_person_part(struct strbuf *sb, char part,
        if (!s.date_begin)
                goto skip;
 
-       date = strtoul(s.date_begin, NULL, 10);
-
        if (part == 't') {      /* date, UNIX timestamp */
                strbuf_add(sb, s.date_begin, s.date_end - s.date_begin);
                return placeholder_len;
        }
 
-       /* parse tz */
-       tz = strtoul(s.tz_begin + 1, NULL, 10);
-       if (*s.tz_begin == '-')
-               tz = -tz;
-
        switch (part) {
        case 'd':       /* date */
-               strbuf_addstr(sb, show_date(date, tz, dmode));
+               strbuf_addstr(sb, show_ident_date(&s, dmode));
                return placeholder_len;
        case 'D':       /* date, RFC2822 style */
-               strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_RFC2822));
                return placeholder_len;
        case 'r':       /* date, relative */
-               strbuf_addstr(sb, show_date(date, tz, DATE_RELATIVE));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE));
                return placeholder_len;
        case 'i':       /* date, ISO 8601 */
-               strbuf_addstr(sb, show_date(date, tz, DATE_ISO8601));
+               strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
                return placeholder_len;
        }
 
@@ -761,19 +768,38 @@ struct chunk {
        size_t len;
 };
 
+enum flush_type {
+       no_flush,
+       flush_right,
+       flush_left,
+       flush_left_and_steal,
+       flush_both
+};
+
+enum trunc_type {
+       trunc_none,
+       trunc_left,
+       trunc_middle,
+       trunc_right
+};
+
 struct format_commit_context {
        const struct commit *commit;
        const struct pretty_print_context *pretty_ctx;
        unsigned commit_header_parsed:1;
        unsigned commit_message_parsed:1;
        struct signature_check signature_check;
+       enum flush_type flush_type;
+       enum trunc_type truncate;
        char *message;
+       char *commit_encoding;
        size_t width, indent1, indent2;
+       int auto_color;
+       int padding;
 
        /* These offsets are relative to the start of the commit message. */
        struct chunk author;
        struct chunk committer;
-       struct chunk encoding;
        size_t message_off;
        size_t subject_off;
        size_t body_off;
@@ -820,9 +846,6 @@ static void parse_commit_header(struct format_commit_context *context)
                } else if (!prefixcmp(msg + i, "committer ")) {
                        context->committer.off = i + 10;
                        context->committer.len = eol - i - 10;
-               } else if (!prefixcmp(msg + i, "encoding ")) {
-                       context->encoding.off = i + 9;
-                       context->encoding.len = eol - i - 9;
                }
                i = eol;
        }
@@ -903,23 +926,6 @@ static void parse_commit_message(struct format_commit_context *c)
        c->commit_message_parsed = 1;
 }
 
-static void format_decoration(struct strbuf *sb, const struct commit *commit)
-{
-       struct name_decoration *d;
-       const char *prefix = " (";
-
-       load_ref_decorations(DECORATE_SHORT_REFS);
-       d = lookup_decoration(&name_decoration, &commit->object);
-       while (d) {
-               strbuf_addstr(sb, prefix);
-               prefix = ", ";
-               strbuf_addstr(sb, d->name);
-               d = d->next;
-       }
-       if (prefix[0] == ',')
-               strbuf_addch(sb, ')');
-}
-
 static void strbuf_wrap(struct strbuf *sb, size_t pos,
                        size_t width, size_t indent1, size_t indent2)
 {
@@ -966,7 +972,112 @@ static int format_reflog_person(struct strbuf *sb,
        return format_person_part(sb, part, ident, strlen(ident), dmode);
 }
 
-static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
+static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
+                         const char *placeholder,
+                         struct format_commit_context *c)
+{
+       if (placeholder[1] == '(') {
+               const char *begin = placeholder + 2;
+               const char *end = strchr(begin, ')');
+               char color[COLOR_MAXLEN];
+
+               if (!end)
+                       return 0;
+               if (!prefixcmp(begin, "auto,")) {
+                       if (!want_color(c->pretty_ctx->color))
+                               return end - placeholder + 1;
+                       begin += 5;
+               }
+               color_parse_mem(begin,
+                               end - begin,
+                               "--pretty format", color);
+               strbuf_addstr(sb, color);
+               return end - placeholder + 1;
+       }
+       if (!prefixcmp(placeholder + 1, "red")) {
+               strbuf_addstr(sb, GIT_COLOR_RED);
+               return 4;
+       } else if (!prefixcmp(placeholder + 1, "green")) {
+               strbuf_addstr(sb, GIT_COLOR_GREEN);
+               return 6;
+       } else if (!prefixcmp(placeholder + 1, "blue")) {
+               strbuf_addstr(sb, GIT_COLOR_BLUE);
+               return 5;
+       } else if (!prefixcmp(placeholder + 1, "reset")) {
+               strbuf_addstr(sb, GIT_COLOR_RESET);
+               return 6;
+       } else
+               return 0;
+}
+
+static size_t parse_padding_placeholder(struct strbuf *sb,
+                                       const char *placeholder,
+                                       struct format_commit_context *c)
+{
+       const char *ch = placeholder;
+       enum flush_type flush_type;
+       int to_column = 0;
+
+       switch (*ch++) {
+       case '<':
+               flush_type = flush_right;
+               break;
+       case '>':
+               if (*ch == '<') {
+                       flush_type = flush_both;
+                       ch++;
+               } else if (*ch == '>') {
+                       flush_type = flush_left_and_steal;
+                       ch++;
+               } else
+                       flush_type = flush_left;
+               break;
+       default:
+               return 0;
+       }
+
+       /* the next value means "wide enough to that column" */
+       if (*ch == '|') {
+               to_column = 1;
+               ch++;
+       }
+
+       if (*ch == '(') {
+               const char *start = ch + 1;
+               const char *end = start + strcspn(start, ",)");
+               char *next;
+               int width;
+               if (!end || end == start)
+                       return 0;
+               width = strtoul(start, &next, 10);
+               if (next == start || width == 0)
+                       return 0;
+               c->padding = to_column ? -width : width;
+               c->flush_type = flush_type;
+
+               if (*end == ',') {
+                       start = end + 1;
+                       end = strchr(start, ')');
+                       if (!end || end == start)
+                               return 0;
+                       if (!prefixcmp(start, "trunc)"))
+                               c->truncate = trunc_right;
+                       else if (!prefixcmp(start, "ltrunc)"))
+                               c->truncate = trunc_left;
+                       else if (!prefixcmp(start, "mtrunc)"))
+                               c->truncate = trunc_middle;
+                       else
+                               return 0;
+               } else
+                       c->truncate = trunc_none;
+
+               return end - placeholder + 1;
+       }
+       return 0;
+}
+
+static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
+                               const char *placeholder,
                                void *context)
 {
        struct format_commit_context *c = context;
@@ -978,38 +1089,20 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
        /* these are independent of the commit */
        switch (placeholder[0]) {
        case 'C':
-               if (placeholder[1] == '(') {
-                       const char *begin = placeholder + 2;
-                       const char *end = strchr(begin, ')');
-                       char color[COLOR_MAXLEN];
-
-                       if (!end)
-                               return 0;
-                       if (!prefixcmp(begin, "auto,")) {
-                               if (!want_color(c->pretty_ctx->color))
-                                       return end - placeholder + 1;
-                               begin += 5;
-                       }
-                       color_parse_mem(begin,
-                                       end - begin,
-                                       "--pretty format", color);
-                       strbuf_addstr(sb, color);
-                       return end - placeholder + 1;
+               if (!prefixcmp(placeholder + 1, "(auto)")) {
+                       c->auto_color = 1;
+                       return 7; /* consumed 7 bytes, "C(auto)" */
+               } else {
+                       int ret = parse_color(sb, placeholder, c);
+                       if (ret)
+                               c->auto_color = 0;
+                       /*
+                        * Otherwise, we decided to treat %C<unknown>
+                        * as a literal string, and the previous
+                        * %C(auto) is still valid.
+                        */
+                       return ret;
                }
-               if (!prefixcmp(placeholder + 1, "red")) {
-                       strbuf_addstr(sb, GIT_COLOR_RED);
-                       return 4;
-               } else if (!prefixcmp(placeholder + 1, "green")) {
-                       strbuf_addstr(sb, GIT_COLOR_GREEN);
-                       return 6;
-               } else if (!prefixcmp(placeholder + 1, "blue")) {
-                       strbuf_addstr(sb, GIT_COLOR_BLUE);
-                       return 5;
-               } else if (!prefixcmp(placeholder + 1, "reset")) {
-                       strbuf_addstr(sb, GIT_COLOR_RESET);
-                       return 6;
-               } else
-                       return 0;
        case 'n':               /* newline */
                strbuf_addch(sb, '\n');
                return 1;
@@ -1047,6 +1140,10 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                        return end - placeholder + 1;
                } else
                        return 0;
+
+       case '<':
+       case '>':
+               return parse_padding_placeholder(sb, placeholder, c);
        }
 
        /* these depend on the commit */
@@ -1055,13 +1152,19 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
 
        switch (placeholder[0]) {
        case 'H':               /* commit hash */
+               strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));
                strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
+               strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                return 1;
        case 'h':               /* abbreviated commit hash */
-               if (add_again(sb, &c->abbrev_commit_hash))
+               strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));
+               if (add_again(sb, &c->abbrev_commit_hash)) {
+                       strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                        return 1;
+               }
                strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
                                                     c->pretty_ctx->abbrev));
+               strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
                return 1;
        case 'T':               /* tree hash */
@@ -1098,7 +1201,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                strbuf_addstr(sb, get_revision_mark(NULL, commit));
                return 1;
        case 'd':
-               format_decoration(sb, commit);
+               load_ref_decorations(DECORATE_SHORT_REFS);
+               format_decorations(sb, commit, c->auto_color);
                return 1;
        case 'g':               /* reflog info */
                switch(placeholder[1]) {
@@ -1177,7 +1281,8 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                                   msg + c->committer.off, c->committer.len,
                                   c->pretty_ctx->date_mode);
        case 'e':       /* encoding */
-               strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
+               if (c->commit_encoding)
+                       strbuf_addstr(sb, c->commit_encoding);
                return 1;
        case 'B':       /* raw body */
                /* message_off is always left at the initial newline */
@@ -1203,7 +1308,111 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
        return 0;       /* unknown placeholder */
 }
 
-static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
+static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */
+                                   const char *placeholder,
+                                   struct format_commit_context *c)
+{
+       struct strbuf local_sb = STRBUF_INIT;
+       int total_consumed = 0, len, padding = c->padding;
+       if (padding < 0) {
+               const char *start = strrchr(sb->buf, '\n');
+               int occupied;
+               if (!start)
+                       start = sb->buf;
+               occupied = utf8_strnwidth(start, -1, 1);
+               padding = (-padding) - occupied;
+       }
+       while (1) {
+               int modifier = *placeholder == 'C';
+               int consumed = format_commit_one(&local_sb, placeholder, c);
+               total_consumed += consumed;
+
+               if (!modifier)
+                       break;
+
+               placeholder += consumed;
+               if (*placeholder != '%')
+                       break;
+               placeholder++;
+               total_consumed++;
+       }
+       len = utf8_strnwidth(local_sb.buf, -1, 1);
+
+       if (c->flush_type == flush_left_and_steal) {
+               const char *ch = sb->buf + sb->len - 1;
+               while (len > padding && ch > sb->buf) {
+                       const char *p;
+                       if (*ch == ' ') {
+                               ch--;
+                               padding++;
+                               continue;
+                       }
+                       /* check for trailing ansi sequences */
+                       if (*ch != 'm')
+                               break;
+                       p = ch - 1;
+                       while (ch - p < 10 && *p != '\033')
+                               p--;
+                       if (*p != '\033' ||
+                           ch + 1 - p != display_mode_esc_sequence_len(p))
+                               break;
+                       /*
+                        * got a good ansi sequence, put it back to
+                        * local_sb as we're cutting sb
+                        */
+                       strbuf_insert(&local_sb, 0, p, ch + 1 - p);
+                       ch = p - 1;
+               }
+               strbuf_setlen(sb, ch + 1 - sb->buf);
+               c->flush_type = flush_left;
+       }
+
+       if (len > padding) {
+               switch (c->truncate) {
+               case trunc_left:
+                       strbuf_utf8_replace(&local_sb,
+                                           0, len - (padding - 2),
+                                           "..");
+                       break;
+               case trunc_middle:
+                       strbuf_utf8_replace(&local_sb,
+                                           padding / 2 - 1,
+                                           len - (padding - 2),
+                                           "..");
+                       break;
+               case trunc_right:
+                       strbuf_utf8_replace(&local_sb,
+                                           padding - 2, len - (padding - 2),
+                                           "..");
+                       break;
+               case trunc_none:
+                       break;
+               }
+               strbuf_addstr(sb, local_sb.buf);
+       } else {
+               int sb_len = sb->len, offset = 0;
+               if (c->flush_type == flush_left)
+                       offset = padding - len;
+               else if (c->flush_type == flush_both)
+                       offset = (padding - len) / 2;
+               /*
+                * we calculate padding in columns, now
+                * convert it back to chars
+                */
+               padding = padding - len + local_sb.len;
+               strbuf_grow(sb, padding);
+               strbuf_setlen(sb, sb_len + padding);
+               memset(sb->buf + sb_len, ' ', sb->len - sb_len);
+               memcpy(sb->buf + sb_len + offset, local_sb.buf,
+                      local_sb.len);
+       }
+       strbuf_release(&local_sb);
+       c->flush_type = no_flush;
+       return total_consumed;
+}
+
+static size_t format_commit_item(struct strbuf *sb, /* in UTF-8 */
+                                const char *placeholder,
                                 void *context)
 {
        int consumed;
@@ -1232,7 +1441,10 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                placeholder++;
 
        orig_len = sb->len;
-       consumed = format_commit_one(sb, placeholder, context);
+       if (((struct format_commit_context *)context)->flush_type != no_flush)
+               consumed = format_and_pad_commit(sb, placeholder, context);
+       else
+               consumed = format_commit_one(sb, placeholder, context);
        if (magic == NO_MAGIC)
                return consumed;
 
@@ -1283,22 +1495,43 @@ void format_commit_message(const struct commit *commit,
 {
        struct format_commit_context context;
        const char *output_enc = pretty_ctx->output_encoding;
+       const char *utf8 = "UTF-8";
 
        memset(&context, 0, sizeof(context));
        context.commit = commit;
        context.pretty_ctx = pretty_ctx;
        context.wrap_start = sb->len;
-       context.message = logmsg_reencode(commit, output_enc);
+       context.message = logmsg_reencode(commit,
+                                         &context.commit_encoding,
+                                         output_enc);
 
        strbuf_expand(sb, format, format_commit_item, &context);
        rewrap_message_tail(sb, &context, 0, 0, 0);
 
+       if (output_enc) {
+               if (same_encoding(utf8, output_enc))
+                       output_enc = NULL;
+       } else {
+               if (context.commit_encoding &&
+                   !same_encoding(context.commit_encoding, utf8))
+                       output_enc = context.commit_encoding;
+       }
+
+       if (output_enc) {
+               int outsz;
+               char *out = reencode_string_len(sb->buf, sb->len,
+                                               output_enc, utf8, &outsz);
+               if (out)
+                       strbuf_attach(sb, out, outsz, outsz + 1);
+       }
+
+       free(context.commit_encoding);
        logmsg_free(context.message, commit);
        free(context.signature_check.gpg_output);
        free(context.signature_check.signer);
 }
 
-static void pp_header(const struct pretty_print_context *pp,
+static void pp_header(struct pretty_print_context *pp,
                      const char *encoding,
                      const struct commit *commit,
                      const char **msg_p,
@@ -1359,7 +1592,7 @@ static void pp_header(const struct pretty_print_context *pp,
        }
 }
 
-void pp_title_line(const struct pretty_print_context *pp,
+void pp_title_line(struct pretty_print_context *pp,
                   const char **msg_p,
                   struct strbuf *sb,
                   const char *encoding,
@@ -1386,6 +1619,16 @@ void pp_title_line(const struct pretty_print_context *pp,
        }
        strbuf_addch(sb, '\n');
 
+       if (need_8bit_cte == 0) {
+               int i;
+               for (i = 0; i < pp->in_body_headers.nr; i++) {
+                       if (has_non_ascii(pp->in_body_headers.items[i].string)) {
+                               need_8bit_cte = 1;
+                               break;
+                       }
+               }
+       }
+
        if (need_8bit_cte > 0) {
                const char *header_fmt =
                        "MIME-Version: 1.0\n"
@@ -1399,10 +1642,21 @@ void pp_title_line(const struct pretty_print_context *pp,
        if (pp->fmt == CMIT_FMT_EMAIL) {
                strbuf_addch(sb, '\n');
        }
+
+       if (pp->in_body_headers.nr) {
+               int i;
+               for (i = 0; i < pp->in_body_headers.nr; i++) {
+                       strbuf_addstr(sb, pp->in_body_headers.items[i].string);
+                       free(pp->in_body_headers.items[i].string);
+               }
+               string_list_clear(&pp->in_body_headers, 0);
+               strbuf_addch(sb, '\n');
+       }
+
        strbuf_release(&title);
 }
 
-void pp_remainder(const struct pretty_print_context *pp,
+void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
                  int indent)
@@ -1434,7 +1688,7 @@ void pp_remainder(const struct pretty_print_context *pp,
        }
 }
 
-void pretty_print_commit(const struct pretty_print_context *pp,
+void pretty_print_commit(struct pretty_print_context *pp,
                         const struct commit *commit,
                         struct strbuf *sb)
 {
@@ -1451,7 +1705,7 @@ void pretty_print_commit(const struct pretty_print_context *pp,
        }
 
        encoding = get_log_output_encoding();
-       msg = reencoded = logmsg_reencode(commit, encoding);
+       msg = reencoded = logmsg_reencode(commit, NULL, encoding);
 
        if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
                indent = 0;
diff --git a/prio-queue.c b/prio-queue.c
new file mode 100644 (file)
index 0000000..c9f8c6d
--- /dev/null
@@ -0,0 +1,84 @@
+#include "cache.h"
+#include "commit.h"
+#include "prio-queue.h"
+
+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;
+       }
+}
+
+void clear_prio_queue(struct prio_queue *queue)
+{
+       free(queue->array);
+       queue->nr = 0;
+       queue->alloc = 0;
+       queue->array = NULL;
+}
+
+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)
+               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)
+                       break;
+
+               thing = queue->array[parent];
+               queue->array[parent] = queue->array[ix];
+               queue->array[ix] = thing;
+       }
+}
+
+void *prio_queue_get(struct prio_queue *queue)
+{
+       void *result, *swap;
+       int ix, child;
+       prio_queue_compare_fn compare = queue->compare;
+
+       if (!queue->nr)
+               return NULL;
+       if (!compare)
+               return queue->array[--queue->nr]; /* LIFO */
+
+       result = queue->array[0];
+       if (!--queue->nr)
+               return result;
+
+       queue->array[0] = queue->array[queue->nr];
+
+       /* 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))
+                       child++; /* use right child */
+
+               if (compare(queue->array[ix], queue->array[child],
+                           queue->cb_data) <= 0)
+                       break;
+
+               swap = queue->array[child];
+               queue->array[child] = queue->array[ix];
+               queue->array[ix] = swap;
+       }
+       return result;
+}
diff --git a/prio-queue.h b/prio-queue.h
new file mode 100644 (file)
index 0000000..9c3cd1f
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef PRIO_QUEUE_H
+#define PRIO_QUEUE_H
+
+/*
+ * A priority queue implementation, primarily for keeping track of
+ * commits in the 'date-order' so that we process them from new to old
+ * as they are discovered, but can be used to hold any pointer to
+ * struct.  The caller is responsible for supplying a function to
+ * compare two "things".
+ *
+ * Alternatively, this data structure can also be used as a LIFO stack
+ * by specifying NULL as the comparison function.
+ */
+
+/*
+ * Compare two "things", one and two; the third parameter is cb_data
+ * in the prio_queue structure.  The result is returned as a sign of
+ * the return value, being the same as the sign of the result of
+ * subtracting "two" from "one" (i.e. negative if "one" sorts earlier
+ * than "two").
+ */
+typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data);
+
+struct prio_queue {
+       prio_queue_compare_fn compare;
+       void *cb_data;
+       int alloc, nr;
+       void **array;
+};
+
+/*
+ * Add the "thing" to the queue.
+ */
+extern void prio_queue_put(struct prio_queue *, void *thing);
+
+/*
+ * Extract the "thing" that compares the smallest out of the queue,
+ * or NULL.  If compare function is NULL, the queue acts as a LIFO
+ * stack.
+ */
+extern void *prio_queue_get(struct prio_queue *);
+
+extern void clear_prio_queue(struct prio_queue *);
+
+/* Reverse the LIFO elements */
+extern void prio_queue_reverse(struct prio_queue *);
+
+#endif /* PRIO_QUEUE_H */
diff --git a/quote.c b/quote.c
index 911229fdf3caffe29a87aea76f38dc50863797d4..5c8808160e700af0c055a84f8527eb3ac76962d9 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -312,82 +312,27 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen,
        fputc(terminator, fp);
 }
 
-static const char *path_relative(const char *in, int len,
-                                struct strbuf *sb, const char *prefix,
-                                int prefix_len);
-
-void write_name_quoted_relative(const char *name, size_t len,
-                               const char *prefix, size_t prefix_len,
+void write_name_quoted_relative(const char *name, const char *prefix,
                                FILE *fp, int terminator)
 {
        struct strbuf sb = STRBUF_INIT;
 
-       name = path_relative(name, len, &sb, prefix, prefix_len);
+       name = relative_path(name, prefix, &sb);
        write_name_quoted(name, fp, terminator);
 
        strbuf_release(&sb);
 }
 
-/*
- * Give path as relative to prefix.
- *
- * The strbuf may or may not be used, so do not assume it contains the
- * returned path.
- */
-static const char *path_relative(const char *in, int len,
-                                struct strbuf *sb, const char *prefix,
-                                int prefix_len)
-{
-       int off, i;
-
-       if (len < 0)
-               len = strlen(in);
-       if (prefix_len < 0) {
-               if (prefix)
-                       prefix_len = strlen(prefix);
-               else
-                       prefix_len = 0;
-       }
-
-       off = 0;
-       i = 0;
-       while (i < prefix_len && i < len && prefix[i] == in[i]) {
-               if (prefix[i] == '/')
-                       off = i + 1;
-               i++;
-       }
-       in += off;
-       len -= off;
-
-       if (i >= prefix_len)
-               return in;
-
-       strbuf_reset(sb);
-       strbuf_grow(sb, len);
-
-       while (i < prefix_len) {
-               if (prefix[i] == '/')
-                       strbuf_addstr(sb, "../");
-               i++;
-       }
-       strbuf_add(sb, in, len);
-
-       return sb->buf;
-}
-
 /* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
-                         struct strbuf *out, const char *prefix)
+char *quote_path_relative(const char *in, const char *prefix,
+                         struct strbuf *out)
 {
        struct strbuf sb = STRBUF_INIT;
-       const char *rel = path_relative(in, len, &sb, prefix, -1);
+       const char *rel = relative_path(in, prefix, &sb);
        strbuf_reset(out);
        quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
        strbuf_release(&sb);
 
-       if (!out->len)
-               strbuf_addstr(out, "./");
-
        return out->buf;
 }
 
diff --git a/quote.h b/quote.h
index 133155a48b4baa56cd70c07af8a477771f373105..ed110a5d8d9e98a680c67379c7c39e4eda5b25f5 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -60,13 +60,12 @@ extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
 extern void write_name_quoted(const char *name, FILE *, int terminator);
 extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
                                  const char *name, FILE *, int terminator);
-extern void write_name_quoted_relative(const char *name, size_t len,
-               const char *prefix, size_t prefix_len,
+extern void write_name_quoted_relative(const char *name, const char *prefix,
                FILE *fp, int terminator);
 
 /* quote path as relative to the given prefix */
-extern char *quote_path_relative(const char *in, int len,
-                         struct strbuf *out, const char *prefix);
+extern char *quote_path_relative(const char *in, const char *prefix,
+                         struct strbuf *out);
 
 /* quoting as a string literal for other languages */
 extern void perl_quote_print(FILE *stream, const char *src);
index 5a9704f4e5b974a46bc5486373a26816c5b3b4bd..c3d5e3543fae75c81a7a0a00c48bb0076675a2f1 100644 (file)
@@ -67,6 +67,61 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
        add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
+void fill_stat_data(struct stat_data *sd, struct stat *st)
+{
+       sd->sd_ctime.sec = (unsigned int)st->st_ctime;
+       sd->sd_mtime.sec = (unsigned int)st->st_mtime;
+       sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
+       sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
+       sd->sd_dev = st->st_dev;
+       sd->sd_ino = st->st_ino;
+       sd->sd_uid = st->st_uid;
+       sd->sd_gid = st->st_gid;
+       sd->sd_size = st->st_size;
+}
+
+int match_stat_data(const struct stat_data *sd, struct stat *st)
+{
+       int changed = 0;
+
+       if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
+               changed |= MTIME_CHANGED;
+       if (trust_ctime && check_stat &&
+           sd->sd_ctime.sec != (unsigned int)st->st_ctime)
+               changed |= CTIME_CHANGED;
+
+#ifdef USE_NSEC
+       if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+               changed |= MTIME_CHANGED;
+       if (trust_ctime && check_stat &&
+           sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+               changed |= CTIME_CHANGED;
+#endif
+
+       if (check_stat) {
+               if (sd->sd_uid != (unsigned int) st->st_uid ||
+                       sd->sd_gid != (unsigned int) st->st_gid)
+                       changed |= OWNER_CHANGED;
+               if (sd->sd_ino != (unsigned int) st->st_ino)
+                       changed |= INODE_CHANGED;
+       }
+
+#ifdef USE_STDEV
+       /*
+        * st_dev breaks on network filesystems where different
+        * clients will have different views of what "device"
+        * the filesystem is on
+        */
+       if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
+                       changed |= INODE_CHANGED;
+#endif
+
+       if (sd->sd_size != (unsigned int) st->st_size)
+               changed |= DATA_CHANGED;
+
+       return changed;
+}
+
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -74,15 +129,7 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
  */
 void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 {
-       ce->ce_ctime.sec = (unsigned int)st->st_ctime;
-       ce->ce_mtime.sec = (unsigned int)st->st_mtime;
-       ce->ce_ctime.nsec = ST_CTIME_NSEC(*st);
-       ce->ce_mtime.nsec = ST_MTIME_NSEC(*st);
-       ce->ce_dev = st->st_dev;
-       ce->ce_ino = st->st_ino;
-       ce->ce_uid = st->st_uid;
-       ce->ce_gid = st->st_gid;
-       ce->ce_size = st->st_size;
+       fill_stat_data(&ce->ce_stat_data, st);
 
        if (assume_unchanged)
                ce->ce_flags |= CE_VALID;
@@ -91,7 +138,7 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
                ce_mark_uptodate(ce);
 }
 
-static int ce_compare_data(struct cache_entry *ce, struct stat *st)
+static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
 {
        int match = -1;
        int fd = open(ce->name, O_RDONLY);
@@ -105,7 +152,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)
        return match;
 }
 
-static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
+static int ce_compare_link(const struct cache_entry *ce, size_t expected_size)
 {
        int match = -1;
        void *buffer;
@@ -126,7 +173,7 @@ static int ce_compare_link(struct cache_entry *ce, size_t expected_size)
        return match;
 }
 
-static int ce_compare_gitlink(struct cache_entry *ce)
+static int ce_compare_gitlink(const struct cache_entry *ce)
 {
        unsigned char sha1[20];
 
@@ -143,7 +190,7 @@ static int ce_compare_gitlink(struct cache_entry *ce)
        return hashcmp(sha1, ce->sha1);
 }
 
-static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
+static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
 {
        switch (st->st_mode & S_IFMT) {
        case S_IFREG:
@@ -163,7 +210,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
        return 0;
 }
 
-static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
+static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
 {
        unsigned int changed = 0;
 
@@ -195,43 +242,11 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
        default:
                die("internal error: ce_mode is %o", ce->ce_mode);
        }
-       if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
-               changed |= MTIME_CHANGED;
-       if (trust_ctime && check_stat &&
-           ce->ce_ctime.sec != (unsigned int)st->st_ctime)
-               changed |= CTIME_CHANGED;
-
-#ifdef USE_NSEC
-       if (check_stat && ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
-               changed |= MTIME_CHANGED;
-       if (trust_ctime && check_stat &&
-           ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
-               changed |= CTIME_CHANGED;
-#endif
-
-       if (check_stat) {
-               if (ce->ce_uid != (unsigned int) st->st_uid ||
-                       ce->ce_gid != (unsigned int) st->st_gid)
-                       changed |= OWNER_CHANGED;
-               if (ce->ce_ino != (unsigned int) st->st_ino)
-                       changed |= INODE_CHANGED;
-       }
-
-#ifdef USE_STDEV
-       /*
-        * st_dev breaks on network filesystems where different
-        * clients will have different views of what "device"
-        * the filesystem is on
-        */
-       if (check_stat && ce->ce_dev != (unsigned int) st->st_dev)
-                       changed |= INODE_CHANGED;
-#endif
 
-       if (ce->ce_size != (unsigned int) st->st_size)
-               changed |= DATA_CHANGED;
+       changed |= match_stat_data(&ce->ce_stat_data, st);
 
        /* Racily smudged entry? */
-       if (!ce->ce_size) {
+       if (!ce->ce_stat_data.sd_size) {
                if (!is_empty_blob_sha1(ce->sha1))
                        changed |= DATA_CHANGED;
        }
@@ -239,23 +254,24 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
        return changed;
 }
 
-static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
+static int is_racy_timestamp(const struct index_state *istate,
+                            const struct cache_entry *ce)
 {
        return (!S_ISGITLINK(ce->ce_mode) &&
                istate->timestamp.sec &&
 #ifdef USE_NSEC
                 /* nanosecond timestamped files can also be racy! */
-               (istate->timestamp.sec < ce->ce_mtime.sec ||
-                (istate->timestamp.sec == ce->ce_mtime.sec &&
-                 istate->timestamp.nsec <= ce->ce_mtime.nsec))
+               (istate->timestamp.sec < ce->ce_stat_data.sd_mtime.sec ||
+                (istate->timestamp.sec == ce->ce_stat_data.sd_mtime.sec &&
+                 istate->timestamp.nsec <= ce->ce_stat_data.sd_mtime.nsec))
 #else
-               istate->timestamp.sec <= ce->ce_mtime.sec
+               istate->timestamp.sec <= ce->ce_stat_data.sd_mtime.sec
 #endif
                 );
 }
 
 int ie_match_stat(const struct index_state *istate,
-                 struct cache_entry *ce, struct stat *st,
+                 const struct cache_entry *ce, struct stat *st,
                  unsigned int options)
 {
        unsigned int changed;
@@ -311,7 +327,8 @@ int ie_match_stat(const struct index_state *istate,
 }
 
 int ie_modified(const struct index_state *istate,
-               struct cache_entry *ce, struct stat *st, unsigned int options)
+               const struct cache_entry *ce,
+               struct stat *st, unsigned int options)
 {
        int changed, changed_fs;
 
@@ -340,7 +357,7 @@ int ie_modified(const struct index_state *istate,
         * then we know it is.
         */
        if ((changed & DATA_CHANGED) &&
-           (S_ISGITLINK(ce->ce_mode) || ce->ce_size != 0))
+           (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
                return changed;
 
        changed_fs = ce_modified_check_fs(ce, st);
@@ -472,7 +489,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 }
 
 /*
- * Remove all cache ententries marked for removal, that is where
+ * Remove all cache entries marked for removal, that is where
  * CE_REMOVE is set in ce_flags.  This is much more effective than
  * calling remove_index_entry_at() for each entry to be removed.
  */
@@ -626,7 +643,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
                        if (*ptr == '/') {
                                struct cache_entry *foundce;
                                ++ptr;
-                               foundce = index_name_exists(&the_index, ce->name, ptr - ce->name, ignore_case);
+                               foundce = index_name_exists(istate, ce->name, ptr - ce->name, ignore_case);
                                if (foundce) {
                                        memcpy((void *)startPtr, foundce->name + (startPtr - ce->name), ptr - startPtr);
                                        startPtr = ptr;
@@ -705,7 +722,7 @@ struct cache_entry *make_cache_entry(unsigned int mode,
        return ce;
 }
 
-int ce_same_name(struct cache_entry *a, struct cache_entry *b)
+int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
 {
        int len = ce_namelen(a);
        return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
@@ -979,7 +996,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
        if (istate->cache_nr == istate->cache_alloc) {
                istate->cache_alloc = alloc_nr(istate->cache_alloc);
                istate->cache = xrealloc(istate->cache,
-                                       istate->cache_alloc * sizeof(struct cache_entry *));
+                                       istate->cache_alloc * sizeof(*istate->cache));
        }
 
        /* Add it in.. */
@@ -1322,16 +1339,16 @@ static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *on
 {
        struct cache_entry *ce = xmalloc(cache_entry_size(len));
 
-       ce->ce_ctime.sec = ntoh_l(ondisk->ctime.sec);
-       ce->ce_mtime.sec = ntoh_l(ondisk->mtime.sec);
-       ce->ce_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
-       ce->ce_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
-       ce->ce_dev   = ntoh_l(ondisk->dev);
-       ce->ce_ino   = ntoh_l(ondisk->ino);
+       ce->ce_stat_data.sd_ctime.sec = ntoh_l(ondisk->ctime.sec);
+       ce->ce_stat_data.sd_mtime.sec = ntoh_l(ondisk->mtime.sec);
+       ce->ce_stat_data.sd_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
+       ce->ce_stat_data.sd_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
+       ce->ce_stat_data.sd_dev   = ntoh_l(ondisk->dev);
+       ce->ce_stat_data.sd_ino   = ntoh_l(ondisk->ino);
        ce->ce_mode  = ntoh_l(ondisk->mode);
-       ce->ce_uid   = ntoh_l(ondisk->uid);
-       ce->ce_gid   = ntoh_l(ondisk->gid);
-       ce->ce_size  = ntoh_l(ondisk->size);
+       ce->ce_stat_data.sd_uid   = ntoh_l(ondisk->uid);
+       ce->ce_stat_data.sd_gid   = ntoh_l(ondisk->gid);
+       ce->ce_stat_data.sd_size  = ntoh_l(ondisk->size);
        ce->ce_flags = flags & ~CE_NAMEMASK;
        ce->ce_namelen = len;
        hashcpy(ce->sha1, ondisk->sha1);
@@ -1449,7 +1466,7 @@ int read_index_from(struct index_state *istate, const char *path)
        istate->version = ntohl(hdr->hdr_version);
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
-       istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *));
+       istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache));
        istate->initialized = 1;
 
        if (istate->version == 4)
@@ -1518,8 +1535,9 @@ int discard_index(struct index_state *istate)
        free_name_hash(istate);
        cache_tree_free(&(istate->cache_tree));
        istate->initialized = 0;
-
-       /* no need to throw away allocated active_cache */
+       free(istate->cache);
+       istate->cache = NULL;
+       istate->cache_alloc = 0;
        return 0;
 }
 
@@ -1608,7 +1626,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
         * The only thing we care about in this function is to smudge the
         * falsely clean entry due to touch-update-touch race, so we leave
         * everything else as they are.  We are called for entries whose
-        * ce_mtime match the index file mtime.
+        * ce_stat_data.sd_mtime match the index file mtime.
         *
         * Note that this actually does not do much for gitlinks, for
         * which ce_match_stat_basic() always goes to the actual
@@ -1647,7 +1665,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
                 * file, and never calls us, so the cached size information
                 * for "frotz" stays 6 which does not match the filesystem.
                 */
-               ce->ce_size = 0;
+               ce->ce_stat_data.sd_size = 0;
        }
 }
 
@@ -1657,16 +1675,16 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
 {
        short flags;
 
-       ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
-       ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
-       ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
-       ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
-       ondisk->dev  = htonl(ce->ce_dev);
-       ondisk->ino  = htonl(ce->ce_ino);
+       ondisk->ctime.sec = htonl(ce->ce_stat_data.sd_ctime.sec);
+       ondisk->mtime.sec = htonl(ce->ce_stat_data.sd_mtime.sec);
+       ondisk->ctime.nsec = htonl(ce->ce_stat_data.sd_ctime.nsec);
+       ondisk->mtime.nsec = htonl(ce->ce_stat_data.sd_mtime.nsec);
+       ondisk->dev  = htonl(ce->ce_stat_data.sd_dev);
+       ondisk->ino  = htonl(ce->ce_stat_data.sd_ino);
        ondisk->mode = htonl(ce->ce_mode);
-       ondisk->uid  = htonl(ce->ce_uid);
-       ondisk->gid  = htonl(ce->ce_gid);
-       ondisk->size = htonl(ce->ce_size);
+       ondisk->uid  = htonl(ce->ce_stat_data.sd_uid);
+       ondisk->gid  = htonl(ce->ce_stat_data.sd_gid);
+       ondisk->size = htonl(ce->ce_stat_data.sd_size);
        hashcpy(ondisk->sha1, ce->sha1);
 
        flags = ce->ce_flags;
@@ -1742,7 +1760,7 @@ static int has_racy_timestamp(struct index_state *istate)
 }
 
 /*
- * Opportunisticly update the index but do not complain if we can't
+ * Opportunistically update the index but do not complain if we can't
  */
 void update_index_if_able(struct index_state *istate, struct lock_file *lockfile)
 {
@@ -1899,3 +1917,67 @@ int index_name_is_other(const struct index_state *istate, const char *name,
        }
        return 1;
 }
+
+void *read_blob_data_from_index(struct index_state *istate, const char *path, unsigned long *size)
+{
+       int pos, len;
+       unsigned long sz;
+       enum object_type type;
+       void *data;
+
+       len = strlen(path);
+       pos = index_name_pos(istate, path, len);
+       if (pos < 0) {
+               /*
+                * We might be in the middle of a merge, in which
+                * case we would read stage #2 (ours).
+                */
+               int i;
+               for (i = -pos - 1;
+                    (pos < 0 && i < istate->cache_nr &&
+                     !strcmp(istate->cache[i]->name, path));
+                    i++)
+                       if (ce_stage(istate->cache[i]) == 2)
+                               pos = i;
+       }
+       if (pos < 0)
+               return NULL;
+       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+       if (!data || type != OBJ_BLOB) {
+               free(data);
+               return NULL;
+       }
+       if (size)
+               *size = sz;
+       return data;
+}
+
+void stat_validity_clear(struct stat_validity *sv)
+{
+       free(sv->sd);
+       sv->sd = NULL;
+}
+
+int stat_validity_check(struct stat_validity *sv, const char *path)
+{
+       struct stat st;
+
+       if (stat(path, &st) < 0)
+               return sv->sd == NULL;
+       if (!sv->sd)
+               return 0;
+       return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
+}
+
+void stat_validity_update(struct stat_validity *sv, int fd)
+{
+       struct stat st;
+
+       if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+               stat_validity_clear(sv);
+       else {
+               if (!sv->sd)
+                       sv->sd = xcalloc(1, sizeof(struct stat_data));
+               fill_stat_data(sv->sd, &st);
+       }
+}
diff --git a/refs.c b/refs.c
index de2d8eb866062649a0d0f23a77e350bca1a182cd..7922261580515859bc89005c4c69e8623c50c728 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -109,7 +109,20 @@ struct ref_entry;
  * (ref_entry->flag & REF_DIR) is zero.
  */
 struct ref_value {
+       /*
+        * The name of the object to which this reference resolves
+        * (which may be a tag object).  If REF_ISBROKEN, this is
+        * null.  If REF_ISSYMREF, then this is the name of the object
+        * referred to by the last reference in the symlink chain.
+        */
        unsigned char sha1[20];
+
+       /*
+        * If REF_KNOWS_PEELED, then this field holds the peeled value
+        * of this reference, or null if the reference is known not to
+        * be peelable.  See the documentation for peel_ref() for an
+        * exact definition of "peelable".
+        */
        unsigned char peeled[20];
 };
 
@@ -158,7 +171,17 @@ struct ref_dir {
        struct ref_entry **entries;
 };
 
-/* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */
+/*
+ * Bit values for ref_entry::flag.  REF_ISSYMREF=0x01,
+ * REF_ISPACKED=0x02, and REF_ISBROKEN=0x04 are public values; see
+ * refs.h.
+ */
+
+/*
+ * The field ref_entry->u.value.peeled of this value entry contains
+ * the correct peeled value for the reference, which might be
+ * null_sha1 if the reference is not a tag or if it is broken.
+ */
 #define REF_KNOWS_PEELED 0x08
 
 /* ref_entry represents a directory of references */
@@ -343,18 +366,17 @@ static int ref_entry_cmp_sslice(const void *key_, const void *ent_)
 }
 
 /*
- * Return the entry with the given refname from the ref_dir
- * (non-recursively), sorting dir if necessary.  Return NULL if no
- * such entry is found.  dir must already be complete.
+ * Return the index of the entry with the given refname from the
+ * ref_dir (non-recursively), sorting dir if necessary.  Return -1 if
+ * no such entry is found.  dir must already be complete.
  */
-static struct ref_entry *search_ref_dir(struct ref_dir *dir,
-                                       const char *refname, size_t len)
+static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len)
 {
        struct ref_entry **r;
        struct string_slice key;
 
        if (refname == NULL || !dir->nr)
-               return NULL;
+               return -1;
 
        sort_ref_dir(dir);
        key.len = len;
@@ -363,9 +385,9 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir,
                    ref_entry_cmp_sslice);
 
        if (r == NULL)
-               return NULL;
+               return -1;
 
-       return *r;
+       return r - dir->entries;
 }
 
 /*
@@ -379,8 +401,9 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
                                         const char *subdirname, size_t len,
                                         int mkdir)
 {
-       struct ref_entry *entry = search_ref_dir(dir, subdirname, len);
-       if (!entry) {
+       int entry_index = search_ref_dir(dir, subdirname, len);
+       struct ref_entry *entry;
+       if (entry_index == -1) {
                if (!mkdir)
                        return NULL;
                /*
@@ -391,6 +414,8 @@ static struct ref_dir *search_for_subdir(struct ref_dir *dir,
                 */
                entry = create_dir_entry(dir->ref_cache, subdirname, len, 0);
                add_entry_to_dir(dir, entry);
+       } else {
+               entry = dir->entries[entry_index];
        }
        return get_ref_dir(entry);
 }
@@ -429,12 +454,67 @@ static struct ref_dir *find_containing_dir(struct ref_dir *dir,
  */
 static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
 {
+       int entry_index;
        struct ref_entry *entry;
        dir = find_containing_dir(dir, refname, 0);
        if (!dir)
                return NULL;
-       entry = search_ref_dir(dir, refname, strlen(refname));
-       return (entry && !(entry->flag & REF_DIR)) ? entry : NULL;
+       entry_index = search_ref_dir(dir, refname, strlen(refname));
+       if (entry_index == -1)
+               return NULL;
+       entry = dir->entries[entry_index];
+       return (entry->flag & REF_DIR) ? NULL : entry;
+}
+
+/*
+ * Remove the entry with the given name from dir, recursing into
+ * subdirectories as necessary.  If refname is the name of a directory
+ * (i.e., ends with '/'), then remove the directory and its contents.
+ * If the removal was successful, return the number of entries
+ * remaining in the directory entry that contained the deleted entry.
+ * If the name was not found, return -1.  Please note that this
+ * function only deletes the entry from the cache; it does not delete
+ * it from the filesystem or ensure that other cache entries (which
+ * might be symbolic references to the removed entry) are updated.
+ * Nor does it remove any containing dir entries that might be made
+ * empty by the removal.  dir must represent the top-level directory
+ * and must already be complete.
+ */
+static int remove_entry(struct ref_dir *dir, const char *refname)
+{
+       int refname_len = strlen(refname);
+       int entry_index;
+       struct ref_entry *entry;
+       int is_dir = refname[refname_len - 1] == '/';
+       if (is_dir) {
+               /*
+                * refname represents a reference directory.  Remove
+                * the trailing slash; otherwise we will get the
+                * directory *representing* refname rather than the
+                * one *containing* it.
+                */
+               char *dirname = xmemdupz(refname, refname_len - 1);
+               dir = find_containing_dir(dir, dirname, 0);
+               free(dirname);
+       } else {
+               dir = find_containing_dir(dir, refname, 0);
+       }
+       if (!dir)
+               return -1;
+       entry_index = search_ref_dir(dir, refname, refname_len);
+       if (entry_index == -1)
+               return -1;
+       entry = dir->entries[entry_index];
+
+       memmove(&dir->entries[entry_index],
+               &dir->entries[entry_index + 1],
+               (dir->nr - entry_index - 1) * sizeof(*dir->entries)
+               );
+       dir->nr--;
+       if (dir->sorted > entry_index)
+               dir->sorted--;
+       free_ref_entry(entry);
+       return dir->nr;
 }
 
 /*
@@ -503,28 +583,69 @@ static void sort_ref_dir(struct ref_dir *dir)
        dir->sorted = dir->nr = i;
 }
 
-#define DO_FOR_EACH_INCLUDE_BROKEN 01
+/* Include broken references in a do_for_each_ref*() iteration: */
+#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
 
+/*
+ * Return true iff the reference described by entry can be resolved to
+ * an object in the database.  Emit a warning if the referred-to
+ * object does not exist.
+ */
+static int ref_resolves_to_object(struct ref_entry *entry)
+{
+       if (entry->flag & REF_ISBROKEN)
+               return 0;
+       if (!has_sha1_file(entry->u.value.sha1)) {
+               error("%s does not point to a valid object!", entry->name);
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * current_ref is a performance hack: when iterating over references
+ * using the for_each_ref*() functions, current_ref is set to the
+ * current reference's entry before calling the callback function.  If
+ * the callback function calls peel_ref(), then peel_ref() first
+ * checks whether the reference to be peeled is the current reference
+ * (it usually is) and if so, returns that reference's peeled version
+ * if it is available.  This avoids a refname lookup in a common case.
+ */
 static struct ref_entry *current_ref;
 
-static int do_one_ref(const char *base, each_ref_fn fn, int trim,
-                     int flags, void *cb_data, struct ref_entry *entry)
+typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data);
+
+struct ref_entry_cb {
+       const char *base;
+       int trim;
+       int flags;
+       each_ref_fn *fn;
+       void *cb_data;
+};
+
+/*
+ * Handle one reference in a do_for_each_ref*()-style iteration,
+ * calling an each_ref_fn for each entry.
+ */
+static int do_one_ref(struct ref_entry *entry, void *cb_data)
 {
+       struct ref_entry_cb *data = cb_data;
+       struct ref_entry *old_current_ref;
        int retval;
-       if (prefixcmp(entry->name, base))
+
+       if (prefixcmp(entry->name, data->base))
                return 0;
 
-       if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
-               if (entry->flag & REF_ISBROKEN)
-                       return 0; /* ignore broken refs e.g. dangling symref */
-               if (!has_sha1_file(entry->u.value.sha1)) {
-                       error("%s does not point to a valid object!", entry->name);
-                       return 0;
-               }
-       }
+       if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+             !ref_resolves_to_object(entry))
+               return 0;
+
+       /* Store the old value, in case this is a recursive call: */
+       old_current_ref = current_ref;
        current_ref = entry;
-       retval = fn(entry->name + trim, entry->u.value.sha1, entry->flag, cb_data);
-       current_ref = NULL;
+       retval = data->fn(entry->name + data->trim, entry->u.value.sha1,
+                         entry->flag, data->cb_data);
+       current_ref = old_current_ref;
        return retval;
 }
 
@@ -532,11 +653,11 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
  * Call fn for each reference in dir that has index in the range
  * offset <= index < dir->nr.  Recurse into subdirectories that are in
  * that index range, sorting them before iterating.  This function
- * does not sort dir itself; it should be sorted beforehand.
+ * does not sort dir itself; it should be sorted beforehand.  fn is
+ * called for all references, including broken ones.
  */
-static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
-                                 const char *base,
-                                 each_ref_fn fn, int trim, int flags, void *cb_data)
+static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset,
+                                   each_ref_entry_fn fn, void *cb_data)
 {
        int i;
        assert(dir->sorted == dir->nr);
@@ -546,10 +667,9 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
                if (entry->flag & REF_DIR) {
                        struct ref_dir *subdir = get_ref_dir(entry);
                        sort_ref_dir(subdir);
-                       retval = do_for_each_ref_in_dir(subdir, 0,
-                                                       base, fn, trim, flags, cb_data);
+                       retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data);
                } else {
-                       retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
+                       retval = fn(entry, cb_data);
                }
                if (retval)
                        return retval;
@@ -562,12 +682,12 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
  * by refname.  Recurse into subdirectories.  If a value entry appears
  * in both dir1 and dir2, then only process the version that is in
  * dir2.  The input dirs must already be sorted, but subdirs will be
- * sorted as needed.
+ * sorted as needed.  fn is called for all references, including
+ * broken ones.
  */
-static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
-                                  struct ref_dir *dir2,
-                                  const char *base, each_ref_fn fn, int trim,
-                                  int flags, void *cb_data)
+static int do_for_each_entry_in_dirs(struct ref_dir *dir1,
+                                    struct ref_dir *dir2,
+                                    each_ref_entry_fn fn, void *cb_data)
 {
        int retval;
        int i1 = 0, i2 = 0;
@@ -578,12 +698,10 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
                struct ref_entry *e1, *e2;
                int cmp;
                if (i1 == dir1->nr) {
-                       return do_for_each_ref_in_dir(dir2, i2,
-                                                     base, fn, trim, flags, cb_data);
+                       return do_for_each_entry_in_dir(dir2, i2, fn, cb_data);
                }
                if (i2 == dir2->nr) {
-                       return do_for_each_ref_in_dir(dir1, i1,
-                                                     base, fn, trim, flags, cb_data);
+                       return do_for_each_entry_in_dir(dir1, i1, fn, cb_data);
                }
                e1 = dir1->entries[i1];
                e2 = dir2->entries[i2];
@@ -595,14 +713,13 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
                                struct ref_dir *subdir2 = get_ref_dir(e2);
                                sort_ref_dir(subdir1);
                                sort_ref_dir(subdir2);
-                               retval = do_for_each_ref_in_dirs(
-                                               subdir1, subdir2,
-                                               base, fn, trim, flags, cb_data);
+                               retval = do_for_each_entry_in_dirs(
+                                               subdir1, subdir2, fn, cb_data);
                                i1++;
                                i2++;
                        } else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) {
                                /* Both are references; ignore the one from dir1. */
-                               retval = do_one_ref(base, fn, trim, flags, cb_data, e2);
+                               retval = fn(e2, cb_data);
                                i1++;
                                i2++;
                        } else {
@@ -621,25 +738,32 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
                        if (e->flag & REF_DIR) {
                                struct ref_dir *subdir = get_ref_dir(e);
                                sort_ref_dir(subdir);
-                               retval = do_for_each_ref_in_dir(
-                                               subdir, 0,
-                                               base, fn, trim, flags, cb_data);
+                               retval = do_for_each_entry_in_dir(
+                                               subdir, 0, fn, cb_data);
                        } else {
-                               retval = do_one_ref(base, fn, trim, flags, cb_data, e);
+                               retval = fn(e, cb_data);
                        }
                }
                if (retval)
                        return retval;
        }
-       if (i1 < dir1->nr)
-               return do_for_each_ref_in_dir(dir1, i1,
-                                             base, fn, trim, flags, cb_data);
-       if (i2 < dir2->nr)
-               return do_for_each_ref_in_dir(dir2, i2,
-                                             base, fn, trim, flags, cb_data);
-       return 0;
 }
 
+/*
+ * Load all of the refs from the dir into our in-memory cache. The hard work
+ * of loading loose refs is done by get_ref_dir(), so we just need to recurse
+ * through all of the sub-directories. We do not even need to care about
+ * sorting, as traversal order does not matter to us.
+ */
+static void prime_ref_dir(struct ref_dir *dir)
+{
+       int i;
+       for (i = 0; i < dir->nr; i++) {
+               struct ref_entry *entry = dir->entries[i];
+               if (entry->flag & REF_DIR)
+                       prime_ref_dir(get_ref_dir(entry));
+       }
+}
 /*
  * Return true iff refname1 and refname2 conflict with each other.
  * Two reference names conflict if one of them exactly matches the
@@ -661,14 +785,13 @@ struct name_conflict_cb {
        const char *conflicting_refname;
 };
 
-static int name_conflict_fn(const char *existingrefname, const unsigned char *sha1,
-                           int flags, void *cb_data)
+static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
 {
        struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
-       if (data->oldrefname && !strcmp(data->oldrefname, existingrefname))
+       if (data->oldrefname && !strcmp(data->oldrefname, entry->name))
                return 0;
-       if (names_conflict(data->refname, existingrefname)) {
-               data->conflicting_refname = existingrefname;
+       if (names_conflict(data->refname, entry->name)) {
+               data->conflicting_refname = entry->name;
                return 1;
        }
        return 0;
@@ -676,7 +799,7 @@ static int name_conflict_fn(const char *existingrefname, const unsigned char *sh
 
 /*
  * Return true iff a reference named refname could be created without
- * conflicting with the name of an existing reference in array.  If
+ * conflicting with the name of an existing reference in dir.  If
  * oldrefname is non-NULL, ignore potential conflicts with oldrefname
  * (e.g., because oldrefname is scheduled for deletion in the same
  * operation).
@@ -690,9 +813,7 @@ static int is_refname_available(const char *refname, const char *oldrefname,
        data.conflicting_refname = NULL;
 
        sort_ref_dir(dir);
-       if (do_for_each_ref_in_dir(dir, 0, "", name_conflict_fn,
-                                  0, DO_FOR_EACH_INCLUDE_BROKEN,
-                                  &data)) {
+       if (do_for_each_entry_in_dir(dir, 0, name_conflict_fn, &data)) {
                error("'%s' exists; cannot create '%s'",
                      data.conflicting_refname, refname);
                return 0;
@@ -700,6 +821,30 @@ static int is_refname_available(const char *refname, const char *oldrefname,
        return 1;
 }
 
+struct packed_ref_cache {
+       struct ref_entry *root;
+
+       /*
+        * Count of references to the data structure in this instance,
+        * including the pointer from ref_cache::packed if any.  The
+        * data will not be freed as long as the reference count is
+        * nonzero.
+        */
+       unsigned int referrers;
+
+       /*
+        * Iff the packed-refs file associated with this instance is
+        * currently locked for writing, this points at the associated
+        * lock (which is owned by somebody else).  The referrer count
+        * is also incremented when the file is locked and decremented
+        * when it is unlocked.
+        */
+       struct lock_file *lock;
+
+       /* The metadata from when this packed-refs cache was read */
+       struct stat_validity validity;
+};
+
 /*
  * Future: need to be in "struct repository"
  * when doing a full libification.
@@ -707,16 +852,51 @@ static int is_refname_available(const char *refname, const char *oldrefname,
 static struct ref_cache {
        struct ref_cache *next;
        struct ref_entry *loose;
-       struct ref_entry *packed;
-       /* The submodule name, or "" for the main repo. */
-       char name[FLEX_ARRAY];
-} *ref_cache;
+       struct packed_ref_cache *packed;
+       /*
+        * The submodule name, or "" for the main repo.  We allocate
+        * length 1 rather than FLEX_ARRAY so that the main ref_cache
+        * is initialized correctly.
+        */
+       char name[1];
+} ref_cache, *submodule_ref_caches;
+
+/* Lock used for the main packed-refs file: */
+static struct lock_file packlock;
+
+/*
+ * Increment the reference count of *packed_refs.
+ */
+static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+       packed_refs->referrers++;
+}
+
+/*
+ * Decrease the reference count of *packed_refs.  If it goes to zero,
+ * free *packed_refs and return true; otherwise return false.
+ */
+static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+       if (!--packed_refs->referrers) {
+               free_ref_entry(packed_refs->root);
+               stat_validity_clear(&packed_refs->validity);
+               free(packed_refs);
+               return 1;
+       } else {
+               return 0;
+       }
+}
 
 static void clear_packed_ref_cache(struct ref_cache *refs)
 {
        if (refs->packed) {
-               free_ref_entry(refs->packed);
+               struct packed_ref_cache *packed_refs = refs->packed;
+
+               if (packed_refs->lock)
+                       die("internal error: packed-ref cache cleared while locked");
                refs->packed = NULL;
+               release_packed_ref_cache(packed_refs);
        }
 }
 
@@ -748,18 +928,18 @@ static struct ref_cache *create_ref_cache(const char *submodule)
  */
 static struct ref_cache *get_ref_cache(const char *submodule)
 {
-       struct ref_cache *refs = ref_cache;
-       if (!submodule)
-               submodule = "";
-       while (refs) {
+       struct ref_cache *refs;
+
+       if (!submodule || !*submodule)
+               return &ref_cache;
+
+       for (refs = submodule_ref_caches; refs; refs = refs->next)
                if (!strcmp(submodule, refs->name))
                        return refs;
-               refs = refs->next;
-       }
 
        refs = create_ref_cache(submodule);
-       refs->next = ref_cache;
-       ref_cache = refs;
+       refs->next = submodule_ref_caches;
+       submodule_ref_caches = refs;
        return refs;
 }
 
@@ -770,6 +950,16 @@ void invalidate_ref_cache(const char *submodule)
        clear_loose_ref_cache(refs);
 }
 
+/* The length of a peeled reference line in packed-refs, including EOL: */
+#define PEELED_LINE_LENGTH 42
+
+/*
+ * The packed-refs header line that we write out.  Perhaps other
+ * traits will be added later.  The trailing space is required.
+ */
+static const char PACKED_REFS_HEADER[] =
+       "# pack-refs with: peeled fully-peeled \n";
+
 /*
  * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
  * Return a pointer to the refname within the line (null-terminated),
@@ -862,8 +1052,8 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                }
                if (last &&
                    refline[0] == '^' &&
-                   strlen(refline) == 42 &&
-                   refline[41] == '\n' &&
+                   strlen(refline) == PEELED_LINE_LENGTH &&
+                   refline[PEELED_LINE_LENGTH - 1] == '\n' &&
                    !get_sha1_hex(refline + 1, sha1)) {
                        hashcpy(last->u.value.peeled, sha1);
                        /*
@@ -876,30 +1066,58 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
        }
 }
 
-static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+/*
+ * Get the packed_ref_cache for the specified ref_cache, creating it
+ * if necessary.
+ */
+static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
 {
+       const char *packed_refs_file;
+
+       if (*refs->name)
+               packed_refs_file = git_path_submodule(refs->name, "packed-refs");
+       else
+               packed_refs_file = git_path("packed-refs");
+
+       if (refs->packed &&
+           !stat_validity_check(&refs->packed->validity, packed_refs_file))
+               clear_packed_ref_cache(refs);
+
        if (!refs->packed) {
-               const char *packed_refs_file;
                FILE *f;
 
-               refs->packed = create_dir_entry(refs, "", 0, 0);
-               if (*refs->name)
-                       packed_refs_file = git_path_submodule(refs->name, "packed-refs");
-               else
-                       packed_refs_file = git_path("packed-refs");
+               refs->packed = xcalloc(1, sizeof(*refs->packed));
+               acquire_packed_ref_cache(refs->packed);
+               refs->packed->root = create_dir_entry(refs, "", 0, 0);
                f = fopen(packed_refs_file, "r");
                if (f) {
-                       read_packed_refs(f, get_ref_dir(refs->packed));
+                       stat_validity_update(&refs->packed->validity, fileno(f));
+                       read_packed_refs(f, get_ref_dir(refs->packed->root));
                        fclose(f);
                }
        }
-       return get_ref_dir(refs->packed);
+       return refs->packed;
+}
+
+static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
+{
+       return get_ref_dir(packed_ref_cache->root);
+}
+
+static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+{
+       return get_packed_ref_dir(get_packed_ref_cache(refs));
 }
 
 void add_packed_ref(const char *refname, const unsigned char *sha1)
 {
-       add_ref(get_packed_refs(get_ref_cache(NULL)),
-                       create_ref_entry(refname, sha1, REF_ISPACKED, 1));
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed refs not locked");
+       add_ref(get_packed_ref_dir(packed_ref_cache),
+               create_ref_entry(refname, sha1, REF_ISPACKED, 1));
 }
 
 /*
@@ -1069,18 +1287,43 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sh
 }
 
 /*
- * Try to read ref from the packed references.  On success, set sha1
- * and return 0; otherwise, return -1.
+ * Return the ref_entry for the given refname from the packed
+ * references.  If it does not exist, return NULL.
  */
-static int get_packed_ref(const char *refname, unsigned char *sha1)
+static struct ref_entry *get_packed_ref(const char *refname)
 {
-       struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));
-       struct ref_entry *entry = find_ref(packed, refname);
+       return find_ref(get_packed_refs(&ref_cache), refname);
+}
+
+/*
+ * A loose ref file doesn't exist; check for a packed ref.  The
+ * options are forwarded from resolve_safe_unsafe().
+ */
+static const char *handle_missing_loose_ref(const char *refname,
+                                           unsigned char *sha1,
+                                           int reading,
+                                           int *flag)
+{
+       struct ref_entry *entry;
+
+       /*
+        * The loose reference file does not exist; check for a packed
+        * reference.
+        */
+       entry = get_packed_ref(refname);
        if (entry) {
                hashcpy(sha1, entry->u.value.sha1);
-               return 0;
+               if (flag)
+                       *flag |= REF_ISPACKED;
+               return refname;
+       }
+       /* The reference is not a packed reference, either. */
+       if (reading) {
+               return NULL;
+       } else {
+               hashclr(sha1);
+               return refname;
        }
-       return -1;
 }
 
 const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
@@ -1107,32 +1350,34 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
 
                git_snpath(path, sizeof(path), "%s", refname);
 
+               /*
+                * We might have to loop back here to avoid a race
+                * condition: first we lstat() the file, then we try
+                * to read it as a link or as a file.  But if somebody
+                * changes the type of the file (file <-> directory
+                * <-> symlink) between the lstat() and reading, then
+                * we don't want to report that as an error but rather
+                * try again starting with the lstat().
+                */
+       stat_ref:
                if (lstat(path, &st) < 0) {
-                       if (errno != ENOENT)
+                       if (errno == ENOENT)
+                               return handle_missing_loose_ref(refname, sha1,
+                                                               reading, flag);
+                       else
                                return NULL;
-                       /*
-                        * The loose reference file does not exist;
-                        * check for a packed reference.
-                        */
-                       if (!get_packed_ref(refname, sha1)) {
-                               if (flag)
-                                       *flag |= REF_ISPACKED;
-                               return refname;
-                       }
-                       /* The reference is not a packed reference, either. */
-                       if (reading) {
-                               return NULL;
-                       } else {
-                               hashclr(sha1);
-                               return refname;
-                       }
                }
 
                /* Follow "normalized" - ie "refs/.." symlinks by hand */
                if (S_ISLNK(st.st_mode)) {
                        len = readlink(path, buffer, sizeof(buffer)-1);
-                       if (len < 0)
-                               return NULL;
+                       if (len < 0) {
+                               if (errno == ENOENT || errno == EINVAL)
+                                       /* inconsistent with lstat; retry */
+                                       goto stat_ref;
+                               else
+                                       return NULL;
+                       }
                        buffer[len] = 0;
                        if (!prefixcmp(buffer, "refs/") &&
                                        !check_refname_format(buffer, 0)) {
@@ -1155,8 +1400,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                 * a ref
                 */
                fd = open(path, O_RDONLY);
-               if (fd < 0)
-                       return NULL;
+               if (fd < 0) {
+                       if (errno == ENOENT)
+                               /* inconsistent with lstat; retry */
+                               goto stat_ref;
+                       else
+                               return NULL;
+               }
                len = read_in_full(fd, buffer, sizeof(buffer)-1);
                close(fd);
                if (len < 0)
@@ -1168,8 +1418,19 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                /*
                 * Is it a symbolic ref?
                 */
-               if (prefixcmp(buffer, "ref:"))
-                       break;
+               if (prefixcmp(buffer, "ref:")) {
+                       /*
+                        * Please note that FETCH_HEAD has a second
+                        * line containing other data.
+                        */
+                       if (get_sha1_hex(buffer, sha1) ||
+                           (buffer[40] != '\0' && !isspace(buffer[40]))) {
+                               if (flag)
+                                       *flag |= REF_ISBROKEN;
+                               return NULL;
+                       }
+                       return refname;
+               }
                if (flag)
                        *flag |= REF_ISSYMREF;
                buf = buffer + 4;
@@ -1182,13 +1443,6 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
                }
                refname = strcpy(refname_buffer, buf);
        }
-       /* Please note that FETCH_HEAD has a second line containing other data. */
-       if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
-               if (flag)
-                       *flag |= REF_ISBROKEN;
-               return NULL;
-       }
-       return refname;
 }
 
 char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
@@ -1231,54 +1485,130 @@ static int filter_refs(const char *refname, const unsigned char *sha1, int flags
        return filter->fn(refname, sha1, flags, filter->cb_data);
 }
 
+enum peel_status {
+       /* object was peeled successfully: */
+       PEEL_PEELED = 0,
+
+       /*
+        * object cannot be peeled because the named object (or an
+        * object referred to by a tag in the peel chain), does not
+        * exist.
+        */
+       PEEL_INVALID = -1,
+
+       /* object cannot be peeled because it is not a tag: */
+       PEEL_NON_TAG = -2,
+
+       /* ref_entry contains no peeled value because it is a symref: */
+       PEEL_IS_SYMREF = -3,
+
+       /*
+        * ref_entry cannot be peeled because it is broken (i.e., the
+        * symbolic reference cannot even be resolved to an object
+        * name):
+        */
+       PEEL_BROKEN = -4
+};
+
+/*
+ * Peel the named object; i.e., if the object is a tag, resolve the
+ * tag recursively until a non-tag is found.  If successful, store the
+ * result to sha1 and return PEEL_PEELED.  If the object is not a tag
+ * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
+ * and leave sha1 unchanged.
+ */
+static enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)
+{
+       struct object *o = lookup_unknown_object(name);
+
+       if (o->type == OBJ_NONE) {
+               int type = sha1_object_info(name, NULL);
+               if (type < 0)
+                       return PEEL_INVALID;
+               o->type = type;
+       }
+
+       if (o->type != OBJ_TAG)
+               return PEEL_NON_TAG;
+
+       o = deref_tag_noverify(o);
+       if (!o)
+               return PEEL_INVALID;
+
+       hashcpy(sha1, o->sha1);
+       return PEEL_PEELED;
+}
+
+/*
+ * Peel the entry (if possible) and return its new peel_status.  If
+ * repeel is true, re-peel the entry even if there is an old peeled
+ * value that is already stored in it.
+ *
+ * It is OK to call this function with a packed reference entry that
+ * might be stale and might even refer to an object that has since
+ * been garbage-collected.  In such a case, if the entry has
+ * REF_KNOWS_PEELED then leave the status unchanged and return
+ * PEEL_PEELED or PEEL_NON_TAG; otherwise, return PEEL_INVALID.
+ */
+static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
+{
+       enum peel_status status;
+
+       if (entry->flag & REF_KNOWS_PEELED) {
+               if (repeel) {
+                       entry->flag &= ~REF_KNOWS_PEELED;
+                       hashclr(entry->u.value.peeled);
+               } else {
+                       return is_null_sha1(entry->u.value.peeled) ?
+                               PEEL_NON_TAG : PEEL_PEELED;
+               }
+       }
+       if (entry->flag & REF_ISBROKEN)
+               return PEEL_BROKEN;
+       if (entry->flag & REF_ISSYMREF)
+               return PEEL_IS_SYMREF;
+
+       status = peel_object(entry->u.value.sha1, entry->u.value.peeled);
+       if (status == PEEL_PEELED || status == PEEL_NON_TAG)
+               entry->flag |= REF_KNOWS_PEELED;
+       return status;
+}
+
 int peel_ref(const char *refname, unsigned char *sha1)
 {
        int flag;
        unsigned char base[20];
-       struct object *o;
 
        if (current_ref && (current_ref->name == refname
-               || !strcmp(current_ref->name, refname))) {
-               if (current_ref->flag & REF_KNOWS_PEELED) {
-                       if (is_null_sha1(current_ref->u.value.peeled))
-                           return -1;
-                       hashcpy(sha1, current_ref->u.value.peeled);
-                       return 0;
-               }
-               hashcpy(base, current_ref->u.value.sha1);
-               goto fallback;
+                           || !strcmp(current_ref->name, refname))) {
+               if (peel_entry(current_ref, 0))
+                       return -1;
+               hashcpy(sha1, current_ref->u.value.peeled);
+               return 0;
        }
 
        if (read_ref_full(refname, base, 1, &flag))
                return -1;
 
-       if ((flag & REF_ISPACKED)) {
-               struct ref_dir *dir = get_packed_refs(get_ref_cache(NULL));
-               struct ref_entry *r = find_ref(dir, refname);
-
-               if (r != NULL && r->flag & REF_KNOWS_PEELED) {
+       /*
+        * If the reference is packed, read its ref_entry from the
+        * cache in the hope that we already know its peeled value.
+        * We only try this optimization on packed references because
+        * (a) forcing the filling of the loose reference cache could
+        * be expensive and (b) loose references anyway usually do not
+        * have REF_KNOWS_PEELED.
+        */
+       if (flag & REF_ISPACKED) {
+               struct ref_entry *r = get_packed_ref(refname);
+               if (r) {
+                       if (peel_entry(r, 0))
+                               return -1;
                        hashcpy(sha1, r->u.value.peeled);
                        return 0;
                }
        }
 
-fallback:
-       o = lookup_unknown_object(base);
-       if (o->type == OBJ_NONE) {
-               int type = sha1_object_info(base, NULL);
-               if (type < 0)
-                       return -1;
-               o->type = type;
-       }
-
-       if (o->type == OBJ_TAG) {
-               o = deref_tag_noverify(o);
-               if (o) {
-                       hashcpy(sha1, o->sha1);
-                       return 0;
-               }
-       }
-       return -1;
+       return peel_object(base, sha1);
 }
 
 struct warn_if_dangling_data {
@@ -1316,40 +1646,84 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
        for_each_rawref(warn_if_dangling_symref, &data);
 }
 
-static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
-                          int trim, int flags, void *cb_data)
+/*
+ * Call fn for each reference in the specified ref_cache, omitting
+ * references not in the containing_dir of base.  fn is called for all
+ * references, including broken ones.  If fn ever returns a non-zero
+ * value, stop the iteration and return that value; otherwise, return
+ * 0.
+ */
+static int do_for_each_entry(struct ref_cache *refs, const char *base,
+                            each_ref_entry_fn fn, void *cb_data)
 {
-       struct ref_cache *refs = get_ref_cache(submodule);
-       struct ref_dir *packed_dir = get_packed_refs(refs);
-       struct ref_dir *loose_dir = get_loose_refs(refs);
+       struct packed_ref_cache *packed_ref_cache;
+       struct ref_dir *loose_dir;
+       struct ref_dir *packed_dir;
        int retval = 0;
 
+       /*
+        * We must make sure that all loose refs are read before accessing the
+        * packed-refs file; this avoids a race condition in which loose refs
+        * are migrated to the packed-refs file by a simultaneous process, but
+        * our in-memory view is from before the migration. get_packed_ref_cache()
+        * takes care of making sure our view is up to date with what is on
+        * disk.
+        */
+       loose_dir = get_loose_refs(refs);
        if (base && *base) {
-               packed_dir = find_containing_dir(packed_dir, base, 0);
                loose_dir = find_containing_dir(loose_dir, base, 0);
        }
+       if (loose_dir)
+               prime_ref_dir(loose_dir);
+
+       packed_ref_cache = get_packed_ref_cache(refs);
+       acquire_packed_ref_cache(packed_ref_cache);
+       packed_dir = get_packed_ref_dir(packed_ref_cache);
+       if (base && *base) {
+               packed_dir = find_containing_dir(packed_dir, base, 0);
+       }
 
        if (packed_dir && loose_dir) {
                sort_ref_dir(packed_dir);
                sort_ref_dir(loose_dir);
-               retval = do_for_each_ref_in_dirs(
-                               packed_dir, loose_dir,
-                               base, fn, trim, flags, cb_data);
+               retval = do_for_each_entry_in_dirs(
+                               packed_dir, loose_dir, fn, cb_data);
        } else if (packed_dir) {
                sort_ref_dir(packed_dir);
-               retval = do_for_each_ref_in_dir(
-                               packed_dir, 0,
-                               base, fn, trim, flags, cb_data);
+               retval = do_for_each_entry_in_dir(
+                               packed_dir, 0, fn, cb_data);
        } else if (loose_dir) {
                sort_ref_dir(loose_dir);
-               retval = do_for_each_ref_in_dir(
-                               loose_dir, 0,
-                               base, fn, trim, flags, cb_data);
+               retval = do_for_each_entry_in_dir(
+                               loose_dir, 0, fn, cb_data);
        }
 
+       release_packed_ref_cache(packed_ref_cache);
        return retval;
 }
 
+/*
+ * Call fn for each reference in the specified ref_cache for which the
+ * refname begins with base.  If trim is non-zero, then trim that many
+ * characters off the beginning of each refname before passing the
+ * refname to fn.  flags can be DO_FOR_EACH_INCLUDE_BROKEN to include
+ * broken references in the iteration.  If fn ever returns a non-zero
+ * value, stop the iteration and return that value; otherwise, return
+ * 0.
+ */
+static int do_for_each_ref(struct ref_cache *refs, const char *base,
+                          each_ref_fn fn, int trim, int flags, void *cb_data)
+{
+       struct ref_entry_cb data;
+       data.base = base;
+       data.trim = trim;
+       data.flags = flags;
+       data.fn = fn;
+       data.cb_data = cb_data;
+
+       return do_for_each_entry(refs, base, do_one_ref, &data);
+}
+
 static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
        unsigned char sha1[20];
@@ -1380,23 +1754,23 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
+       return do_for_each_ref(&ref_cache, "", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
+       return do_for_each_ref(get_ref_cache(submodule), "", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+       return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_ref_in_submodule(const char *submodule, const char *prefix,
                each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
+       return do_for_each_ref(get_ref_cache(submodule), prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
@@ -1431,7 +1805,7 @@ int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *c
 
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
+       return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data);
 }
 
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@ -1454,7 +1828,7 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
        struct strbuf buf = STRBUF_INIT;
        int ret;
        strbuf_addf(&buf, "%srefs/", get_git_namespace());
-       ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+       ret = do_for_each_ref(&ref_cache, buf.buf, fn, 0, 0, cb_data);
        strbuf_release(&buf);
        return ret;
 }
@@ -1496,7 +1870,7 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "", fn, 0,
+       return do_for_each_ref(&ref_cache, "", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
@@ -1702,7 +2076,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
         * name is a proper prefix of our refname.
         */
        if (missing &&
-            !is_refname_available(refname, NULL, get_packed_refs(get_ref_cache(NULL)))) {
+            !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
                last_errno = ENOTDIR;
                goto error_return;
        }
@@ -1754,48 +2128,325 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
        return lock_ref_sha1_basic(refname, old_sha1, flags, NULL);
 }
 
-struct repack_without_ref_sb {
-       const char *refname;
-       int fd;
-};
-
-static int repack_without_ref_fn(const char *refname, const unsigned char *sha1,
-                                int flags, void *cb_data)
+/*
+ * Write an entry to the packed-refs file for the specified refname.
+ * If peeled is non-NULL, write it as the entry's peeled value.
+ */
+static void write_packed_entry(int fd, char *refname, unsigned char *sha1,
+                              unsigned char *peeled)
 {
-       struct repack_without_ref_sb *data = cb_data;
        char line[PATH_MAX + 100];
        int len;
 
-       if (!strcmp(data->refname, refname))
-               return 0;
        len = snprintf(line, sizeof(line), "%s %s\n",
                       sha1_to_hex(sha1), refname);
        /* this should not happen but just being defensive */
        if (len > sizeof(line))
                die("too long a refname '%s'", refname);
-       write_or_die(data->fd, line, len);
+       write_or_die(fd, line, len);
+
+       if (peeled) {
+               if (snprintf(line, sizeof(line), "^%s\n",
+                            sha1_to_hex(peeled)) != PEELED_LINE_LENGTH)
+                       die("internal error");
+               write_or_die(fd, line, PEELED_LINE_LENGTH);
+       }
+}
+
+/*
+ * An each_ref_entry_fn that writes the entry to a packed-refs file.
+ */
+static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
+{
+       int *fd = cb_data;
+       enum peel_status peel_status = peel_entry(entry, 0);
+
+       if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
+               error("internal error: %s is not a valid packed reference!",
+                     entry->name);
+       write_packed_entry(*fd, entry->name, entry->u.value.sha1,
+                          peel_status == PEEL_PEELED ?
+                          entry->u.value.peeled : NULL);
        return 0;
 }
 
-static struct lock_file packlock;
+int lock_packed_refs(int flags)
+{
+       struct packed_ref_cache *packed_ref_cache;
 
-static int repack_without_ref(const char *refname)
+       if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
+               return -1;
+       /*
+        * Get the current packed-refs while holding the lock.  If the
+        * packed-refs file has been modified since we last read it,
+        * this will automatically invalidate the cache and re-read
+        * the packed-refs file.
+        */
+       packed_ref_cache = get_packed_ref_cache(&ref_cache);
+       packed_ref_cache->lock = &packlock;
+       /* Increment the reference count to prevent it from being freed: */
+       acquire_packed_ref_cache(packed_ref_cache);
+       return 0;
+}
+
+int commit_packed_refs(void)
+{
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+       int error = 0;
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed-refs not locked");
+       write_or_die(packed_ref_cache->lock->fd,
+                    PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
+
+       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))
+               error = -1;
+       packed_ref_cache->lock = NULL;
+       release_packed_ref_cache(packed_ref_cache);
+       return error;
+}
+
+void rollback_packed_refs(void)
+{
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed-refs not locked");
+       rollback_lock_file(packed_ref_cache->lock);
+       packed_ref_cache->lock = NULL;
+       release_packed_ref_cache(packed_ref_cache);
+       clear_packed_ref_cache(&ref_cache);
+}
+
+struct ref_to_prune {
+       struct ref_to_prune *next;
+       unsigned char sha1[20];
+       char name[FLEX_ARRAY];
+};
+
+struct pack_refs_cb_data {
+       unsigned int flags;
+       struct ref_dir *packed_refs;
+       struct ref_to_prune *ref_to_prune;
+};
+
+/*
+ * An each_ref_entry_fn that is run over loose references only.  If
+ * the loose reference can be packed, add an entry in the packed ref
+ * cache.  If the reference should be pruned, also add it to
+ * ref_to_prune in the pack_refs_cb_data.
+ */
+static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
 {
-       struct repack_without_ref_sb data;
-       struct ref_cache *refs = get_ref_cache(NULL);
-       struct ref_dir *packed = get_packed_refs(refs);
-       if (find_ref(packed, refname) == NULL)
+       struct pack_refs_cb_data *cb = cb_data;
+       enum peel_status peel_status;
+       struct ref_entry *packed_entry;
+       int is_tag_ref = !prefixcmp(entry->name, "refs/tags/");
+
+       /* ALWAYS pack tags */
+       if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
                return 0;
-       data.refname = refname;
-       data.fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
-       if (data.fd < 0) {
+
+       /* Do not pack symbolic or broken refs: */
+       if ((entry->flag & REF_ISSYMREF) || !ref_resolves_to_object(entry))
+               return 0;
+
+       /* Add a packed ref cache entry equivalent to the loose entry. */
+       peel_status = peel_entry(entry, 1);
+       if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
+               die("internal error peeling reference %s (%s)",
+                   entry->name, sha1_to_hex(entry->u.value.sha1));
+       packed_entry = find_ref(cb->packed_refs, entry->name);
+       if (packed_entry) {
+               /* Overwrite existing packed entry with info from loose entry */
+               packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
+               hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1);
+       } else {
+               packed_entry = create_ref_entry(entry->name, entry->u.value.sha1,
+                                               REF_ISPACKED | REF_KNOWS_PEELED, 0);
+               add_ref(cb->packed_refs, packed_entry);
+       }
+       hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled);
+
+       /* Schedule the loose reference for pruning if requested. */
+       if ((cb->flags & PACK_REFS_PRUNE)) {
+               int namelen = strlen(entry->name) + 1;
+               struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+               hashcpy(n->sha1, entry->u.value.sha1);
+               strcpy(n->name, entry->name);
+               n->next = cb->ref_to_prune;
+               cb->ref_to_prune = n;
+       }
+       return 0;
+}
+
+/*
+ * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Note: munges *name.
+ */
+static void try_remove_empty_parents(char *name)
+{
+       char *p, *q;
+       int i;
+       p = name;
+       for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
+               while (*p && *p != '/')
+                       p++;
+               /* tolerate duplicate slashes; see check_refname_format() */
+               while (*p == '/')
+                       p++;
+       }
+       for (q = p; *q; q++)
+               ;
+       while (1) {
+               while (q > p && *q != '/')
+                       q--;
+               while (q > p && *(q-1) == '/')
+                       q--;
+               if (q == p)
+                       break;
+               *q = '\0';
+               if (rmdir(git_path("%s", name)))
+                       break;
+       }
+}
+
+/* make sure nobody touched the ref, and unlink */
+static void prune_ref(struct ref_to_prune *r)
+{
+       struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1);
+
+       if (lock) {
+               unlink_or_warn(git_path("%s", r->name));
+               unlock_ref(lock);
+               try_remove_empty_parents(r->name);
+       }
+}
+
+static void prune_refs(struct ref_to_prune *r)
+{
+       while (r) {
+               prune_ref(r);
+               r = r->next;
+       }
+}
+
+int pack_refs(unsigned int flags)
+{
+       struct pack_refs_cb_data cbdata;
+
+       memset(&cbdata, 0, sizeof(cbdata));
+       cbdata.flags = flags;
+
+       lock_packed_refs(LOCK_DIE_ON_ERROR);
+       cbdata.packed_refs = get_packed_refs(&ref_cache);
+
+       do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
+                                pack_if_possible_fn, &cbdata);
+
+       if (commit_packed_refs())
+               die_errno("unable to overwrite old ref-pack file");
+
+       prune_refs(cbdata.ref_to_prune);
+       return 0;
+}
+
+/*
+ * If entry is no longer needed in packed-refs, add it to the string
+ * list pointed to by cb_data.  Reasons for deleting entries:
+ *
+ * - Entry is broken.
+ * - Entry is overridden by a loose ref.
+ * - Entry does not point at a valid object.
+ *
+ * In the first and third cases, also emit an error message because these
+ * are indications of repository corruption.
+ */
+static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
+{
+       struct string_list *refs_to_delete = cb_data;
+
+       if (entry->flag & REF_ISBROKEN) {
+               /* This shouldn't happen to packed refs. */
+               error("%s is broken!", entry->name);
+               string_list_append(refs_to_delete, entry->name);
+               return 0;
+       }
+       if (!has_sha1_file(entry->u.value.sha1)) {
+               unsigned char sha1[20];
+               int flags;
+
+               if (read_ref_full(entry->name, sha1, 0, &flags))
+                       /* We should at least have found the packed ref. */
+                       die("Internal error");
+               if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
+                       /*
+                        * This packed reference is overridden by a
+                        * loose reference, so it is OK that its value
+                        * is no longer valid; for example, it might
+                        * refer to an object that has been garbage
+                        * collected.  For this purpose we don't even
+                        * care whether the loose reference itself is
+                        * invalid, broken, symbolic, etc.  Silently
+                        * remove the packed reference.
+                        */
+                       string_list_append(refs_to_delete, entry->name);
+                       return 0;
+               }
+               /*
+                * There is no overriding loose reference, so the fact
+                * that this reference doesn't refer to a valid object
+                * indicates some kind of repository corruption.
+                * Report the problem, then omit the reference from
+                * the output.
+                */
+               error("%s does not point to a valid object!", entry->name);
+               string_list_append(refs_to_delete, entry->name);
+               return 0;
+       }
+
+       return 0;
+}
+
+static int repack_without_ref(const char *refname)
+{
+       struct ref_dir *packed;
+       struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+       struct string_list_item *ref_to_delete;
+
+       if (!get_packed_ref(refname))
+               return 0; /* refname does not exist in packed refs */
+
+       if (lock_packed_refs(0)) {
                unable_to_lock_error(git_path("packed-refs"), errno);
                return error("cannot delete '%s' from packed refs", refname);
        }
-       clear_packed_ref_cache(refs);
-       packed = get_packed_refs(refs);
-       do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data);
-       return commit_lock_file(&packlock);
+       packed = get_packed_refs(&ref_cache);
+
+       /* Remove refname from the cache: */
+       if (remove_entry(packed, refname) == -1) {
+               /*
+                * The packed entry disappeared while we were
+                * acquiring the lock.
+                */
+               rollback_packed_refs();
+               return 0;
+       }
+
+       /* Remove any other accumulated cruft: */
+       do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete);
+       for_each_string_list_item(ref_to_delete, &refs_to_delete) {
+               if (remove_entry(packed, ref_to_delete->string) == -1)
+                       die("internal error");
+       }
+
+       /* Write what remains: */
+       return commit_packed_refs();
 }
 
 int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
@@ -1823,7 +2474,7 @@ int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
        ret |= repack_without_ref(lock->ref_name);
 
        unlink_or_warn(git_path("logs/%s", lock->ref_name));
-       invalidate_ref_cache(NULL);
+       clear_loose_ref_cache(&ref_cache);
        unlock_ref(lock);
        return ret;
 }
@@ -1845,7 +2496,6 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
        struct stat loginfo;
        int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
        const char *symref = NULL;
-       struct ref_cache *refs = get_ref_cache(NULL);
 
        if (log && S_ISLNK(loginfo.st_mode))
                return error("reflog for %s is a symlink", oldrefname);
@@ -1857,10 +2507,10 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
        if (!symref)
                return error("refname %s not found", oldrefname);
 
-       if (!is_refname_available(newrefname, oldrefname, get_packed_refs(refs)))
+       if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
                return 1;
 
-       if (!is_refname_available(newrefname, oldrefname, get_loose_refs(refs)))
+       if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
                return 1;
 
        if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
@@ -2116,7 +2766,7 @@ int write_ref_sha1(struct ref_lock *lock,
                unlock_ref(lock);
                return -1;
        }
-       clear_loose_ref_cache(get_ref_cache(NULL));
+       clear_loose_ref_cache(&ref_cache);
        if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
            (strcmp(lock->ref_name, lock->orig_ref_name) &&
             log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) {
diff --git a/refs.h b/refs.h
index a35eafc4ee15493c1473d71b5c2e83a1f9137c1a..9e5db3ae26ec7898d8ee9083c37562d3af05c0ab 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -10,18 +10,41 @@ struct ref_lock {
        int force_write;
 };
 
+/*
+ * Bit values set in the flags argument passed to each_ref_fn():
+ */
+
+/* Reference is a symbolic reference. */
 #define REF_ISSYMREF 0x01
+
+/* Reference is a packed reference. */
 #define REF_ISPACKED 0x02
+
+/*
+ * Reference cannot be resolved to an object name: dangling symbolic
+ * reference (directly or indirectly), corrupt reference file, or
+ * symbolic reference refers to ill-formatted reference name.
+ */
 #define REF_ISBROKEN 0x04
 
 /*
- * Calls the specified function for each ref file until it returns
- * nonzero, and returns the value.  Please note that it is not safe to
- * modify references while an iteration is in progress, unless the
- * same callback function invocation that modifies the reference also
- * returns a nonzero value to immediately stop the iteration.
+ * The signature for the callback function for the for_each_*()
+ * functions below.  The memory pointed to by the refname and sha1
+ * arguments is only guaranteed to be valid for the duration of a
+ * single callback invocation.
+ */
+typedef int each_ref_fn(const char *refname,
+                       const unsigned char *sha1, int flags, void *cb_data);
+
+/*
+ * The following functions invoke the specified callback function for
+ * each reference indicated.  If the function ever returns a nonzero
+ * value, stop the iteration and return that value.  Please note that
+ * it is not safe to modify references while an iteration is in
+ * progress, unless the same callback function invocation that
+ * modifies the reference also returns a nonzero value to immediately
+ * stop the iteration.
  */
-typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
 extern int head_ref(each_ref_fn, void *);
 extern int for_each_ref(each_ref_fn, void *);
 extern int for_each_ref_in(const char *, each_ref_fn, void *);
@@ -54,13 +77,57 @@ extern int for_each_rawref(each_ref_fn, void *);
 extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
 
 /*
- * Add a reference to the in-memory packed reference cache.  To actually
- * write the reference to the packed-refs file, call pack_refs().
+ * Lock the packed-refs file for writing.  Flags is passed to
+ * hold_lock_file_for_update().  Return 0 on success.
+ */
+extern int lock_packed_refs(int flags);
+
+/*
+ * Add a reference to the in-memory packed reference cache.  This may
+ * only be called while the packed-refs file is locked (see
+ * lock_packed_refs()).  To actually write the packed-refs file, call
+ * commit_packed_refs().
  */
 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.
+ */
+extern int commit_packed_refs(void);
+
+/*
+ * Rollback the lockfile for the packed-refs file, and discard the
+ * in-memory packed reference cache.  (The packed-refs file will be
+ * read anew if it is needed again after this function is called.)
+ */
+extern void rollback_packed_refs(void);
+
+/*
+ * Flags for controlling behaviour of pack_refs()
+ * PACK_REFS_PRUNE: Prune loose refs after packing
+ * PACK_REFS_ALL:   Pack _all_ refs, not just tags and already packed refs
+ */
+#define PACK_REFS_PRUNE 0x0001
+#define PACK_REFS_ALL   0x0002
+
+/*
+ * Write a packed-refs file for the current repository.
+ * flags: Combination of the above PACK_REFS_* flags.
+ */
+int pack_refs(unsigned int flags);
+
 extern int ref_exists(const char *);
 
+/*
+ * 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,
+ * store the SHA1 of the referred-to object to sha1 and return 0.  If
+ * any of these conditions are not met, return a non-zero value.
+ * Symbolic references are considered unpeelable, even if they
+ * ultimately resolve to a peelable tag.
+ */
 extern int peel_ref(const char *refname, unsigned char *sha1);
 
 /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/
index 60eda6308197ad3d0b5957b9ae34e2dc15dfe5cc..5b3ce9eed299e312c132b590ea4926cfb019a271 100644 (file)
@@ -7,6 +7,7 @@
 #include "run-command.h"
 #include "pkt-line.h"
 #include "sideband.h"
+#include "argv-array.h"
 
 static struct remote *remote;
 static const char *url; /* always ends with a trailing slash */
@@ -787,36 +788,35 @@ static int push_dav(int nr_spec, char **specs)
 static int push_git(struct discovery *heads, int nr_spec, char **specs)
 {
        struct rpc_state rpc;
-       const char **argv;
-       int argc = 0, i, err;
+       int i, err;
+       struct argv_array args;
+
+       argv_array_init(&args);
+       argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
+                        NULL);
 
-       argv = xmalloc((10 + nr_spec) * sizeof(char*));
-       argv[argc++] = "send-pack";
-       argv[argc++] = "--stateless-rpc";
-       argv[argc++] = "--helper-status";
        if (options.thin)
-               argv[argc++] = "--thin";
+               argv_array_push(&args, "--thin");
        if (options.dry_run)
-               argv[argc++] = "--dry-run";
+               argv_array_push(&args, "--dry-run");
        if (options.verbosity == 0)
-               argv[argc++] = "--quiet";
+               argv_array_push(&args, "--quiet");
        else if (options.verbosity > 1)
-               argv[argc++] = "--verbose";
-       argv[argc++] = options.progress ? "--progress" : "--no-progress";
-       argv[argc++] = url;
+               argv_array_push(&args, "--verbose");
+       argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
+       argv_array_push(&args, url);
        for (i = 0; i < nr_spec; i++)
-               argv[argc++] = specs[i];
-       argv[argc++] = NULL;
+               argv_array_push(&args, specs[i]);
 
        memset(&rpc, 0, sizeof(rpc));
        rpc.service_name = "git-receive-pack",
-       rpc.argv = argv;
+       rpc.argv = args.argv;
 
        err = rpc_service(&rpc, heads);
        if (rpc.result.len)
                write_or_die(1, rpc.result.buf, rpc.result.len);
        strbuf_release(&rpc.result);
-       free(argv);
+       argv_array_clear(&args);
        return err;
 }
 
index 5ddf11cc61e22e39f28b3cbe0dbd1075c0881556..d7cd5d272fd23624223f37ec8bda7d4fa8c39bce 100644 (file)
@@ -286,7 +286,7 @@ static int do_command(struct strbuf *line)
        return 0;
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        struct strbuf buf = STRBUF_INIT, url_sb = STRBUF_INIT,
                        private_ref_sb = STRBUF_INIT, marksfilename_sb = STRBUF_INIT,
index 68eb99bdf0ca3fedd02ab1bc93db7549b21b62d4..efcba931eca963bd6a5fd13f01a4859e0ae9e14d 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -276,10 +276,9 @@ static void read_remotes_file(struct remote *remote)
 
 static void read_branches_file(struct remote *remote)
 {
-       const char *slash = strchr(remote->name, '/');
        char *frag;
        struct strbuf branch = STRBUF_INIT;
-       int n = slash ? slash - remote->name : 1000;
+       int n = 1000;
        FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
        char *s, *p;
        int len;
@@ -299,21 +298,11 @@ static void read_branches_file(struct remote *remote)
        while (isspace(p[-1]))
                *--p = 0;
        len = p - s;
-       if (slash)
-               len += strlen(slash);
        p = xmalloc(len + 1);
        strcpy(p, s);
-       if (slash)
-               strcat(p, slash);
 
        /*
-        * With "slash", e.g. "git fetch jgarzik/netdev-2.6" when
-        * reading from $GIT_DIR/branches/jgarzik fetches "HEAD" from
-        * the partial URL obtained from the branches file plus
-        * "/netdev-2.6" and does not store it in any tracking ref.
-        * #branch specifier in the file is ignored.
-        *
-        * Otherwise, the branches file would have URL and optionally
+        * The branches file would have URL and optionally
         * #branch specified.  The "master" (or specified) branch is
         * fetched and stored in the local branch of the same name.
         */
@@ -323,12 +312,8 @@ static void read_branches_file(struct remote *remote)
                strbuf_addf(&branch, "refs/heads/%s", frag);
        } else
                strbuf_addstr(&branch, "refs/heads/master");
-       if (!slash) {
-               strbuf_addf(&branch, ":refs/heads/%s", remote->name);
-       } else {
-               strbuf_reset(&branch);
-               strbuf_addstr(&branch, "HEAD:");
-       }
+
+       strbuf_addf(&branch, ":refs/heads/%s", remote->name);
        add_url_alias(remote, p);
        add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
        /*
@@ -1317,6 +1302,14 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
        free(sent_tips.tip);
 }
 
+static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
+{
+       for ( ; ref; ref = ref->next)
+               string_list_append_nodup(ref_index, ref->name)->util = ref;
+
+       sort_string_list(ref_index);
+}
+
 /*
  * Given the set of refs the local repository has, the set of refs the
  * remote repository has, and the refspec used for push, determine
@@ -1335,6 +1328,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
        int errs;
        static const char *default_refspec[] = { ":", NULL };
        struct ref *ref, **dst_tail = tail_ref(dst);
+       struct string_list dst_ref_index = STRING_LIST_INIT_NODUP;
 
        if (!nr_refspec) {
                nr_refspec = 1;
@@ -1345,6 +1339,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
 
        /* pick the remainder */
        for (ref = src; ref; ref = ref->next) {
+               struct string_list_item *dst_item;
                struct ref *dst_peer;
                const struct refspec *pat = NULL;
                char *dst_name;
@@ -1353,7 +1348,11 @@ int match_push_refs(struct ref *src, struct ref **dst,
                if (!dst_name)
                        continue;
 
-               dst_peer = find_ref_by_name(*dst, dst_name);
+               if (!dst_ref_index.nr)
+                       prepare_ref_index(&dst_ref_index, *dst);
+
+               dst_item = string_list_lookup(&dst_ref_index, dst_name);
+               dst_peer = dst_item ? dst_item->util : NULL;
                if (dst_peer) {
                        if (dst_peer->peer_ref)
                                /* We're already sending something to this ref. */
@@ -1370,6 +1369,8 @@ int match_push_refs(struct ref *src, struct ref **dst,
                        /* Create a new one and link it */
                        dst_peer = make_linked_ref(dst_name, &dst_tail);
                        hashcpy(dst_peer->new_sha1, ref->new_sha1);
+                       string_list_insert(&dst_ref_index,
+                               dst_peer->name)->util = dst_peer;
                }
                dst_peer->peer_ref = copy_ref(ref);
                dst_peer->force = pat->force;
@@ -1377,10 +1378,13 @@ int match_push_refs(struct ref *src, struct ref **dst,
                free(dst_name);
        }
 
+       string_list_clear(&dst_ref_index, 0);
+
        if (flags & MATCH_REFS_FOLLOW_TAGS)
                add_missing_tags(src, dst, &dst_tail);
 
        if (send_prune) {
+               struct string_list src_ref_index = STRING_LIST_INIT_NODUP;
                /* check for missing refs on the remote */
                for (ref = *dst; ref; ref = ref->next) {
                        char *src_name;
@@ -1391,11 +1395,15 @@ int match_push_refs(struct ref *src, struct ref **dst,
 
                        src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL);
                        if (src_name) {
-                               if (!find_ref_by_name(src, src_name))
+                               if (!src_ref_index.nr)
+                                       prepare_ref_index(&src_ref_index, src);
+                               if (!string_list_has_string(&src_ref_index,
+                                           src_name))
                                        ref->peer_ref = alloc_delete_ref();
                                free(src_name);
                        }
                }
+               string_list_clear(&src_ref_index, 0);
        }
        if (errs)
                return -1;
@@ -1474,8 +1482,7 @@ struct branch *branch_get(const char *name)
                ret->remote = remote_get(ret->remote_name);
                if (ret->merge_nr) {
                        int i;
-                       ret->merge = xcalloc(sizeof(*ret->merge),
-                                            ret->merge_nr);
+                       ret->merge = xcalloc(ret->merge_nr, sizeof(*ret->merge));
                        for (i = 0; i < ret->merge_nr; i++) {
                                ret->merge[i] = xcalloc(1, sizeof(**ret->merge));
                                ret->merge[i]->src = xstrdup(ret->merge_name[i]);
index 98e3e294d0dc295290b7b891a834186c0791e43a..6fde8f94c27d5a3a196098af0f4716e27566dae5 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -299,7 +299,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
 {
        mmfile_t mmfile[3] = {{NULL}};
        mmbuffer_t result = {NULL, 0};
-       struct cache_entry *ce;
+       const struct cache_entry *ce;
        int pos, len, i, hunk_no;
        struct rerere_io_mem io;
        int marker_size = ll_merge_marker_size(path);
@@ -359,7 +359,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
 
 static int check_one_conflict(int i, int *type)
 {
-       struct cache_entry *e = active_cache[i];
+       const struct cache_entry *e = active_cache[i];
 
        if (!ce_stage(e)) {
                *type = RESOLVED;
@@ -374,8 +374,8 @@ static int check_one_conflict(int i, int *type)
 
        /* Only handle regular files with both stages #2 and #3 */
        if (i + 1 < active_nr) {
-               struct cache_entry *e2 = active_cache[i];
-               struct cache_entry *e3 = active_cache[i + 1];
+               const struct cache_entry *e2 = active_cache[i];
+               const struct cache_entry *e3 = active_cache[i + 1];
                if (ce_stage(e2) == 2 &&
                    ce_stage(e3) == 3 &&
                    ce_same_name(e, e3) &&
@@ -398,7 +398,7 @@ static int find_conflict(struct string_list *conflict)
 
        for (i = 0; i < active_nr;) {
                int conflict_type;
-               struct cache_entry *e = active_cache[i];
+               const struct cache_entry *e = active_cache[i];
                i = check_one_conflict(i, &conflict_type);
                if (conflict_type == THREE_STAGED)
                        string_list_insert(conflict, (const char *)e->name);
@@ -414,7 +414,7 @@ int rerere_remaining(struct string_list *merge_rr)
 
        for (i = 0; i < active_nr;) {
                int conflict_type;
-               struct cache_entry *e = active_cache[i];
+               const struct cache_entry *e = active_cache[i];
                i = check_one_conflict(i, &conflict_type);
                if (conflict_type == PUNTED)
                        string_list_insert(merge_rr, (const char *)e->name);
index 639eb9c59f355e46bdd53cf13fd94e8a6a9537da..77101f51c153ab014ecb80a02ca52daaa16cb10d 100644 (file)
@@ -115,7 +115,7 @@ void resolve_undo_clear_index(struct index_state *istate)
 
 int unmerge_index_entry_at(struct index_state *istate, int pos)
 {
-       struct cache_entry *ce;
+       const struct cache_entry *ce;
        struct string_list_item *item;
        struct resolve_undo_info *ru;
        int i, err = 0, matched;
@@ -167,7 +167,7 @@ void unmerge_marked_index(struct index_state *istate)
                return;
 
        for (i = 0; i < istate->cache_nr; i++) {
-               struct cache_entry *ce = istate->cache[i];
+               const struct cache_entry *ce = istate->cache[i];
                if (ce->ce_flags & CE_MATCHED)
                        i = unmerge_index_entry_at(istate, i);
        }
@@ -181,7 +181,7 @@ void unmerge_index(struct index_state *istate, const char **pathspec)
                return;
 
        for (i = 0; i < istate->cache_nr; i++) {
-               struct cache_entry *ce = istate->cache[i];
+               const struct cache_entry *ce = istate->cache[i];
                if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))
                        continue;
                i = unmerge_index_entry_at(istate, i);
index 56d666d69a8a961d33651c0658f7009967e0b7ab..ac20d1aaed7b5e1a52682dfd196f87e2f94e87c7 100644 (file)
@@ -13,7 +13,9 @@
 #include "decorate.h"
 #include "log-tree.h"
 #include "string-list.h"
+#include "line-log.h"
 #include "mailmap.h"
+#include "commit-slab.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -70,7 +72,8 @@ static int show_path_truncated(FILE *out, const struct name_path *path)
        return ours || emitted;
 }
 
-void show_object_with_name(FILE *out, struct object *obj, const struct name_path *path, const char *component)
+void show_object_with_name(FILE *out, struct object *obj,
+                          const struct name_path *path, const char *component)
 {
        struct name_path leaf;
        leaf.up = (struct name_path *)path;
@@ -87,7 +90,9 @@ void add_object(struct object *obj,
                struct name_path *path,
                const char *name)
 {
-       add_object_array(obj, path_name(path, name), p);
+       char *pn = path_name(path, name);
+       add_object_array(obj, pn, p);
+       free(pn);
 }
 
 static void mark_blob_uninteresting(struct blob *blob)
@@ -186,7 +191,9 @@ void mark_parents_uninteresting(struct commit *commit)
        }
 }
 
-static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
+static void add_pending_object_with_mode(struct rev_info *revs,
+                                        struct object *obj,
+                                        const char *name, unsigned mode)
 {
        if (!obj)
                return;
@@ -209,7 +216,8 @@ static void add_pending_object_with_mode(struct rev_info *revs, struct object *o
        add_object_array_with_mode(obj, name, &revs->pending, mode);
 }
 
-void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+void add_pending_object(struct rev_info *revs,
+                       struct object *obj, const char *name)
 {
        add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
 }
@@ -226,7 +234,9 @@ void add_head_to_pending(struct rev_info *revs)
        add_pending_object(revs, obj, "HEAD");
 }
 
-static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
+static struct object *get_reference(struct rev_info *revs, const char *name,
+                                   const unsigned char *sha1,
+                                   unsigned int flags)
 {
        struct object *object;
 
@@ -247,7 +257,8 @@ void add_pending_sha1(struct rev_info *revs, const char *name,
        add_pending_object(revs, object, name);
 }
 
-static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name)
+static struct commit *handle_commit(struct rev_info *revs,
+                                   struct object *object, const char *name)
 {
        unsigned long flags = object->flags;
 
@@ -332,6 +343,80 @@ static int everybody_uninteresting(struct commit_list *orig)
        return 1;
 }
 
+/*
+ * A definition of "relevant" commit that we can use to simplify limited graphs
+ * by eliminating side branches.
+ *
+ * A "relevant" commit is one that is !UNINTERESTING (ie we are including it
+ * in our list), or that is a specified BOTTOM commit. Then after computing
+ * a limited list, during processing we can generally ignore boundary merges
+ * coming from outside the graph, (ie from irrelevant parents), and treat
+ * those merges as if they were single-parent. TREESAME is defined to consider
+ * only relevant parents, if any. If we are TREESAME to our on-graph parents,
+ * we don't care if we were !TREESAME to non-graph parents.
+ *
+ * Treating bottom commits as relevant ensures that a limited graph's
+ * connection to the actual bottom commit is not viewed as a side branch, but
+ * treated as part of the graph. For example:
+ *
+ *   ....Z...A---X---o---o---B
+ *        .     /
+ *         W---Y
+ *
+ * When computing "A..B", the A-X connection is at least as important as
+ * Y-X, despite A being flagged UNINTERESTING.
+ *
+ * And when computing --ancestry-path "A..B", the A-X connection is more
+ * important than Y-X, despite both A and Y being flagged UNINTERESTING.
+ */
+static inline int relevant_commit(struct commit *commit)
+{
+       return (commit->object.flags & (UNINTERESTING | BOTTOM)) != UNINTERESTING;
+}
+
+/*
+ * Return a single relevant commit from a parent list. If we are a TREESAME
+ * commit, and this selects one of our parents, then we can safely simplify to
+ * that parent.
+ */
+static struct commit *one_relevant_parent(const struct rev_info *revs,
+                                         struct commit_list *orig)
+{
+       struct commit_list *list = orig;
+       struct commit *relevant = NULL;
+
+       if (!orig)
+               return NULL;
+
+       /*
+        * For 1-parent commits, or if first-parent-only, then return that
+        * first parent (even if not "relevant" by the above definition).
+        * TREESAME will have been set purely on that parent.
+        */
+       if (revs->first_parent_only || !orig->next)
+               return orig->item;
+
+       /*
+        * For multi-parent commits, identify a sole relevant parent, if any.
+        * If we have only one relevant parent, then TREESAME will be set purely
+        * with regard to that parent, and we can simplify accordingly.
+        *
+        * If we have more than one relevant parent, or no relevant parents
+        * (and multiple irrelevant ones), then we can't select a parent here
+        * and return NULL.
+        */
+       while (list) {
+               struct commit *commit = list->item;
+               list = list->next;
+               if (relevant_commit(commit)) {
+                       if (relevant)
+                               return NULL;
+                       relevant = commit;
+               }
+       }
+       return relevant;
+}
+
 /*
  * The goal is to get REV_TREE_NEW as the result only if the
  * diff consists of all '+' (and no other changes), REV_TREE_OLD
@@ -368,7 +453,8 @@ static void file_change(struct diff_options *options,
        DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
-static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct commit *commit)
+static int rev_compare_tree(struct rev_info *revs,
+                           struct commit *parent, struct commit *commit)
 {
        struct tree *t1 = parent->tree;
        struct tree *t2 = commit->tree;
@@ -429,10 +515,125 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
 }
 
+struct treesame_state {
+       unsigned int nparents;
+       unsigned char treesame[FLEX_ARRAY];
+};
+
+static struct treesame_state *initialise_treesame(struct rev_info *revs, struct commit *commit)
+{
+       unsigned n = commit_list_count(commit->parents);
+       struct treesame_state *st = xcalloc(1, sizeof(*st) + n);
+       st->nparents = n;
+       add_decoration(&revs->treesame, &commit->object, st);
+       return st;
+}
+
+/*
+ * Must be called immediately after removing the nth_parent from a commit's
+ * parent list, if we are maintaining the per-parent treesame[] decoration.
+ * This does not recalculate the master TREESAME flag - update_treesame()
+ * should be called to update it after a sequence of treesame[] modifications
+ * that may have affected it.
+ */
+static int compact_treesame(struct rev_info *revs, struct commit *commit, unsigned nth_parent)
+{
+       struct treesame_state *st;
+       int old_same;
+
+       if (!commit->parents) {
+               /*
+                * Have just removed the only parent from a non-merge.
+                * Different handling, as we lack decoration.
+                */
+               if (nth_parent != 0)
+                       die("compact_treesame %u", nth_parent);
+               old_same = !!(commit->object.flags & TREESAME);
+               if (rev_same_tree_as_empty(revs, commit))
+                       commit->object.flags |= TREESAME;
+               else
+                       commit->object.flags &= ~TREESAME;
+               return old_same;
+       }
+
+       st = lookup_decoration(&revs->treesame, &commit->object);
+       if (!st || nth_parent >= st->nparents)
+               die("compact_treesame %u", nth_parent);
+
+       old_same = st->treesame[nth_parent];
+       memmove(st->treesame + nth_parent,
+               st->treesame + nth_parent + 1,
+               st->nparents - nth_parent - 1);
+
+       /*
+        * If we've just become a non-merge commit, update TREESAME
+        * immediately, and remove the no-longer-needed decoration.
+        * If still a merge, defer update until update_treesame().
+        */
+       if (--st->nparents == 1) {
+               if (commit->parents->next)
+                       die("compact_treesame parents mismatch");
+               if (st->treesame[0] && revs->dense)
+                       commit->object.flags |= TREESAME;
+               else
+                       commit->object.flags &= ~TREESAME;
+               free(add_decoration(&revs->treesame, &commit->object, NULL));
+       }
+
+       return old_same;
+}
+
+static unsigned update_treesame(struct rev_info *revs, struct commit *commit)
+{
+       if (commit->parents && commit->parents->next) {
+               unsigned n;
+               struct treesame_state *st;
+               struct commit_list *p;
+               unsigned relevant_parents;
+               unsigned relevant_change, irrelevant_change;
+
+               st = lookup_decoration(&revs->treesame, &commit->object);
+               if (!st)
+                       die("update_treesame %s", sha1_to_hex(commit->object.sha1));
+               relevant_parents = 0;
+               relevant_change = irrelevant_change = 0;
+               for (p = commit->parents, n = 0; p; n++, p = p->next) {
+                       if (relevant_commit(p->item)) {
+                               relevant_change |= !st->treesame[n];
+                               relevant_parents++;
+                       } else
+                               irrelevant_change |= !st->treesame[n];
+               }
+               if (relevant_parents ? relevant_change : irrelevant_change)
+                       commit->object.flags &= ~TREESAME;
+               else
+                       commit->object.flags |= TREESAME;
+       }
+
+       return commit->object.flags & TREESAME;
+}
+
+static inline int limiting_can_increase_treesame(const struct rev_info *revs)
+{
+       /*
+        * TREESAME is irrelevant unless prune && dense;
+        * if simplify_history is set, we can't have a mixture of TREESAME and
+        *    !TREESAME INTERESTING parents (and we don't have treesame[]
+        *    decoration anyway);
+        * if first_parent_only is set, then the TREESAME flag is locked
+        *    against the first parent (and again we lack treesame[] decoration).
+        */
+       return revs->prune && revs->dense &&
+              !revs->simplify_history &&
+              !revs->first_parent_only;
+}
+
 static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
 {
        struct commit_list **pp, *parent;
-       int tree_changed = 0, tree_same = 0, nth_parent = 0;
+       struct treesame_state *ts = NULL;
+       int relevant_change = 0, irrelevant_change = 0;
+       int relevant_parents, nth_parent;
 
        /*
         * If we don't do pruning, everything is interesting
@@ -456,33 +657,54 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
        if (!revs->dense && !commit->parents->next)
                return;
 
-       pp = &commit->parents;
-       while ((parent = *pp) != NULL) {
+       for (pp = &commit->parents, nth_parent = 0, relevant_parents = 0;
+            (parent = *pp) != NULL;
+            pp = &parent->next, nth_parent++) {
                struct commit *p = parent->item;
+               if (relevant_commit(p))
+                       relevant_parents++;
 
-               /*
-                * Do not compare with later parents when we care only about
-                * the first parent chain, in order to avoid derailing the
-                * traversal to follow a side branch that brought everything
-                * in the path we are limited to by the pathspec.
-                */
-               if (revs->first_parent_only && nth_parent++)
-                       break;
+               if (nth_parent == 1) {
+                       /*
+                        * This our second loop iteration - so we now know
+                        * we're dealing with a merge.
+                        *
+                        * Do not compare with later parents when we care only about
+                        * the first parent chain, in order to avoid derailing the
+                        * traversal to follow a side branch that brought everything
+                        * in the path we are limited to by the pathspec.
+                        */
+                       if (revs->first_parent_only)
+                               break;
+                       /*
+                        * If this will remain a potentially-simplifiable
+                        * merge, remember per-parent treesame if needed.
+                        * Initialise the array with the comparison from our
+                        * first iteration.
+                        */
+                       if (revs->treesame.name &&
+                           !revs->simplify_history &&
+                           !(commit->object.flags & UNINTERESTING)) {
+                               ts = initialise_treesame(revs, commit);
+                               if (!(irrelevant_change || relevant_change))
+                                       ts->treesame[0] = 1;
+                       }
+               }
                if (parse_commit(p) < 0)
                        die("cannot simplify commit %s (because of %s)",
                            sha1_to_hex(commit->object.sha1),
                            sha1_to_hex(p->object.sha1));
                switch (rev_compare_tree(revs, p, commit)) {
                case REV_TREE_SAME:
-                       tree_same = 1;
-                       if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
+                       if (!revs->simplify_history || !relevant_commit(p)) {
                                /* Even if a merge with an uninteresting
                                 * side branch brought the entire change
                                 * we are interested in, we do not want
                                 * to lose the other branches of this
                                 * merge, so we just keep going.
                                 */
-                               pp = &parent->next;
+                               if (ts)
+                                       ts->treesame[nth_parent] = 1;
                                continue;
                        }
                        parent->next = NULL;
@@ -510,15 +732,27 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                /* fallthrough */
                case REV_TREE_OLD:
                case REV_TREE_DIFFERENT:
-                       tree_changed = 1;
-                       pp = &parent->next;
+                       if (relevant_commit(p))
+                               relevant_change = 1;
+                       else
+                               irrelevant_change = 1;
                        continue;
                }
                die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
        }
-       if (tree_changed && !tree_same)
-               return;
-       commit->object.flags |= TREESAME;
+
+       /*
+        * TREESAME is straightforward for single-parent commits. For merge
+        * commits, it is most useful to define it so that "irrelevant"
+        * parents cannot make us !TREESAME - if we have any relevant
+        * parents, then we only consider TREESAMEness with respect to them,
+        * allowing irrelevant merges from uninteresting branches to be
+        * simplified away. Only if we have only irrelevant parents do we
+        * base TREESAME on them. Note that this logic is replicated in
+        * update_treesame, which should be kept in sync.
+        */
+       if (relevant_parents ? !relevant_change : !irrelevant_change)
+               commit->object.flags |= TREESAME;
 }
 
 static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
@@ -801,16 +1035,12 @@ static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *li
  * to filter the result of "A..B" further to the ones that can actually
  * reach A.
  */
-static struct commit_list *collect_bottom_commits(struct rev_info *revs)
+static struct commit_list *collect_bottom_commits(struct commit_list *list)
 {
-       struct commit_list *bottom = NULL;
-       int i;
-       for (i = 0; i < revs->cmdline.nr; i++) {
-               struct rev_cmdline_entry *elem = &revs->cmdline.rev[i];
-               if ((elem->flags & UNINTERESTING) &&
-                   elem->item->type == OBJ_COMMIT)
-                       commit_list_insert((struct commit *)elem->item, &bottom);
-       }
+       struct commit_list *elem, *bottom = NULL;
+       for (elem = list; elem; elem = elem->next)
+               if (elem->item->object.flags & BOTTOM)
+                       commit_list_insert(elem->item, &bottom);
        return bottom;
 }
 
@@ -841,7 +1071,7 @@ static int limit_list(struct rev_info *revs)
        struct commit_list *bottom = NULL;
 
        if (revs->ancestry_path) {
-               bottom = collect_bottom_commits(revs);
+               bottom = collect_bottom_commits(list);
                if (!bottom)
                        die("--ancestry-path given but there are no bottom commits");
        }
@@ -894,10 +1124,26 @@ static int limit_list(struct rev_info *revs)
                free_commit_list(bottom);
        }
 
+       /*
+        * Check if any commits have become TREESAME by some of their parents
+        * becoming UNINTERESTING.
+        */
+       if (limiting_can_increase_treesame(revs))
+               for (list = newlist; list; list = list->next) {
+                       struct commit *c = list->item;
+                       if (c->object.flags & (UNINTERESTING | TREESAME))
+                               continue;
+                       update_treesame(revs, c);
+               }
+
        revs->commits = newlist;
        return 0;
 }
 
+/*
+ * Add an entry to refs->cmdline with the specified information.
+ * *name is copied.
+ */
 static void add_rev_cmdline(struct rev_info *revs,
                            struct object *item,
                            const char *name,
@@ -909,12 +1155,25 @@ static void add_rev_cmdline(struct rev_info *revs,
 
        ALLOC_GROW(info->rev, nr + 1, info->alloc);
        info->rev[nr].item = item;
-       info->rev[nr].name = name;
+       info->rev[nr].name = xstrdup(name);
        info->rev[nr].whence = whence;
        info->rev[nr].flags = flags;
        info->nr++;
 }
 
+static void add_rev_cmdline_list(struct rev_info *revs,
+                                struct commit_list *commit_list,
+                                int whence,
+                                unsigned flags)
+{
+       while (commit_list) {
+               struct object *object = &commit_list->item->object;
+               add_rev_cmdline(revs, object, sha1_to_hex(object->sha1),
+                               whence, flags);
+               commit_list = commit_list->next;
+       }
+}
+
 struct all_refs_cb {
        int all_flags;
        int warned_bad_reflog;
@@ -1000,7 +1259,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
        const char *arg = arg_;
 
        if (*arg == '^') {
-               flags ^= UNINTERESTING;
+               flags ^= UNINTERESTING | BOTTOM;
                arg++;
        }
        if (get_sha1_committish(arg, sha1))
@@ -1038,7 +1297,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        DIFF_OPT_SET(&revs->pruning, QUICK);
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
-       revs->lifo = 1;
+       revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
        revs->dense = 1;
        revs->prefix = prefix;
        revs->max_age = -1;
@@ -1092,14 +1351,15 @@ static void prepare_show_merge(struct rev_info *revs)
        add_pending_object(revs, &head->object, "HEAD");
        add_pending_object(revs, &other->object, "MERGE_HEAD");
        bases = get_merge_bases(head, other, 1);
-       add_pending_commit_list(revs, bases, UNINTERESTING);
+       add_rev_cmdline_list(revs, bases, REV_CMD_MERGE_BASE, UNINTERESTING | BOTTOM);
+       add_pending_commit_list(revs, bases, UNINTERESTING | BOTTOM);
        free_commit_list(bases);
        head->object.flags |= SYMMETRIC_LEFT;
 
        if (!active_nr)
                read_cache();
        for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
                if (ce_path_match(ce, &revs->prune_data)) {
@@ -1128,13 +1388,15 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
        int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
        unsigned get_sha1_flags = 0;
 
+       flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
+
        dotdot = strstr(arg, "..");
        if (dotdot) {
                unsigned char from_sha1[20];
                const char *next = dotdot + 2;
                const char *this = arg;
                int symmetric = *next == '.';
-               unsigned int flags_exclude = flags ^ UNINTERESTING;
+               unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
                static const char head_by_default[] = "HEAD";
                unsigned int a_flags;
 
@@ -1179,6 +1441,9 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
 
                        if (symmetric) {
                                exclude = get_merge_bases(a, b, 1);
+                               add_rev_cmdline_list(revs, exclude,
+                                                    REV_CMD_MERGE_BASE,
+                                                    flags_exclude);
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
@@ -1207,13 +1472,13 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
        dotdot = strstr(arg, "^!");
        if (dotdot && !dotdot[2]) {
                *dotdot = 0;
-               if (!add_parents_only(revs, arg, flags ^ UNINTERESTING))
+               if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM)))
                        *dotdot = '^';
        }
 
        local_flags = 0;
        if (*arg == '^') {
-               local_flags = UNINTERESTING;
+               local_flags = UNINTERESTING | BOTTOM;
                arg++;
        }
 
@@ -1276,7 +1541,7 @@ static void read_revisions_from_stdin(struct rev_info *revs,
                        }
                        die("options not supported in --stdin mode");
                }
-               if (handle_revision_arg(xstrdup(sb.buf), revs, 0,
+               if (handle_revision_arg(sb.buf, revs, 0,
                                        REVARG_CANNOT_BE_FILENAME))
                        die("bad revision '%s'", sb.buf);
        }
@@ -1374,7 +1639,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--merge")) {
                revs->show_merge = 1;
        } else if (!strcmp(arg, "--topo-order")) {
-               revs->lifo = 1;
+               revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
                revs->topo_order = 1;
        } else if (!strcmp(arg, "--simplify-merges")) {
                revs->simplify_merges = 1;
@@ -1392,7 +1657,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->prune = 1;
                load_ref_decorations(DECORATE_SHORT_REFS);
        } else if (!strcmp(arg, "--date-order")) {
-               revs->lifo = 0;
+               revs->sort_order = REV_SORT_BY_COMMIT_DATE;
+               revs->topo_order = 1;
+       } else if (!strcmp(arg, "--author-date-order")) {
+               revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
                revs->topo_order = 1;
        } else if (!prefixcmp(arg, "--early-output")) {
                int count = 100;
@@ -1690,7 +1958,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
                handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
        } else if (!strcmp(arg, "--bisect")) {
                handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
-               handle_refs(submodule, revs, *flags ^ UNINTERESTING, for_each_good_bisect_ref);
+               handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
                handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
@@ -1716,7 +1984,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
        } else if (!strcmp(arg, "--reflog")) {
                handle_reflog(revs, *flags);
        } else if (!strcmp(arg, "--not")) {
-               *flags ^= UNINTERESTING;
+               *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
                revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
        } else if (!prefixcmp(arg, "--no-walk=")) {
@@ -1897,6 +2165,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->combine_merges)
                revs->ignore_merges = 0;
        revs->diffopt.abbrev = revs->abbrev;
+
+       if (revs->line_level_traverse) {
+               revs->limited = 1;
+               revs->topo_order = 1;
+       }
+
        diff_setup_done(&revs->diffopt);
 
        grep_commit_pattern_type(GREP_PATTERN_TYPE_UNSPECIFIED,
@@ -1930,28 +2204,32 @@ static void add_child(struct rev_info *revs, struct commit *parent, struct commi
        l->next = add_decoration(&revs->children, &parent->object, l);
 }
 
-static int remove_duplicate_parents(struct commit *commit)
+static int remove_duplicate_parents(struct rev_info *revs, struct commit *commit)
 {
+       struct treesame_state *ts = lookup_decoration(&revs->treesame, &commit->object);
        struct commit_list **pp, *p;
        int surviving_parents;
 
        /* Examine existing parents while marking ones we have seen... */
        pp = &commit->parents;
+       surviving_parents = 0;
        while ((p = *pp) != NULL) {
                struct commit *parent = p->item;
                if (parent->object.flags & TMP_MARK) {
                        *pp = p->next;
+                       if (ts)
+                               compact_treesame(revs, commit, surviving_parents);
                        continue;
                }
                parent->object.flags |= TMP_MARK;
+               surviving_parents++;
                pp = &p->next;
        }
-       /* count them while clearing the temporary mark */
-       surviving_parents = 0;
+       /* clear the temporary mark */
        for (p = commit->parents; p; p = p->next) {
                p->item->object.flags &= ~TMP_MARK;
-               surviving_parents++;
        }
+       /* no update_treesame() - removing duplicates can't affect TREESAME */
        return surviving_parents;
 }
 
@@ -1971,9 +2249,157 @@ static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs,
        return st;
 }
 
+static int mark_redundant_parents(struct rev_info *revs, struct commit *commit)
+{
+       struct commit_list *h = reduce_heads(commit->parents);
+       int i = 0, marked = 0;
+       struct commit_list *po, *pn;
+
+       /* Want these for sanity-checking only */
+       int orig_cnt = commit_list_count(commit->parents);
+       int cnt = commit_list_count(h);
+
+       /*
+        * Not ready to remove items yet, just mark them for now, based
+        * on the output of reduce_heads(). reduce_heads outputs the reduced
+        * set in its original order, so this isn't too hard.
+        */
+       po = commit->parents;
+       pn = h;
+       while (po) {
+               if (pn && po->item == pn->item) {
+                       pn = pn->next;
+                       i++;
+               } else {
+                       po->item->object.flags |= TMP_MARK;
+                       marked++;
+               }
+               po=po->next;
+       }
+
+       if (i != cnt || cnt+marked != orig_cnt)
+               die("mark_redundant_parents %d %d %d %d", orig_cnt, cnt, i, marked);
+
+       free_commit_list(h);
+
+       return marked;
+}
+
+static int mark_treesame_root_parents(struct rev_info *revs, struct commit *commit)
+{
+       struct commit_list *p;
+       int marked = 0;
+
+       for (p = commit->parents; p; p = p->next) {
+               struct commit *parent = p->item;
+               if (!parent->parents && (parent->object.flags & TREESAME)) {
+                       parent->object.flags |= TMP_MARK;
+                       marked++;
+               }
+       }
+
+       return marked;
+}
+
+/*
+ * Awkward naming - this means one parent we are TREESAME to.
+ * cf mark_treesame_root_parents: root parents that are TREESAME (to an
+ * empty tree). Better name suggestions?
+ */
+static int leave_one_treesame_to_parent(struct rev_info *revs, struct commit *commit)
+{
+       struct treesame_state *ts = lookup_decoration(&revs->treesame, &commit->object);
+       struct commit *unmarked = NULL, *marked = NULL;
+       struct commit_list *p;
+       unsigned n;
+
+       for (p = commit->parents, n = 0; p; p = p->next, n++) {
+               if (ts->treesame[n]) {
+                       if (p->item->object.flags & TMP_MARK) {
+                               if (!marked)
+                                       marked = p->item;
+                       } else {
+                               if (!unmarked) {
+                                       unmarked = p->item;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       /*
+        * If we are TREESAME to a marked-for-deletion parent, but not to any
+        * unmarked parents, unmark the first TREESAME parent. This is the
+        * parent that the default simplify_history==1 scan would have followed,
+        * and it doesn't make sense to omit that path when asking for a
+        * simplified full history. Retaining it improves the chances of
+        * understanding odd missed merges that took an old version of a file.
+        *
+        * Example:
+        *
+        *   I--------*X       A modified the file, but mainline merge X used
+        *    \       /        "-s ours", so took the version from I. X is
+        *     `-*A--'         TREESAME to I and !TREESAME to A.
+        *
+        * Default log from X would produce "I". Without this check,
+        * --full-history --simplify-merges would produce "I-A-X", showing
+        * the merge commit X and that it changed A, but not making clear that
+        * it had just taken the I version. With this check, the topology above
+        * is retained.
+        *
+        * Note that it is possible that the simplification chooses a different
+        * TREESAME parent from the default, in which case this test doesn't
+        * activate, and we _do_ drop the default parent. Example:
+        *
+        *   I------X         A modified the file, but it was reverted in B,
+        *    \    /          meaning mainline merge X is TREESAME to both
+        *    *A-*B           parents.
+        *
+        * Default log would produce "I" by following the first parent;
+        * --full-history --simplify-merges will produce "I-A-B". But this is a
+        * reasonable result - it presents a logical full history leading from
+        * I to X, and X is not an important merge.
+        */
+       if (!unmarked && marked) {
+               marked->object.flags &= ~TMP_MARK;
+               return 1;
+       }
+
+       return 0;
+}
+
+static int remove_marked_parents(struct rev_info *revs, struct commit *commit)
+{
+       struct commit_list **pp, *p;
+       int nth_parent, removed = 0;
+
+       pp = &commit->parents;
+       nth_parent = 0;
+       while ((p = *pp) != NULL) {
+               struct commit *parent = p->item;
+               if (parent->object.flags & TMP_MARK) {
+                       parent->object.flags &= ~TMP_MARK;
+                       *pp = p->next;
+                       free(p);
+                       removed++;
+                       compact_treesame(revs, commit, nth_parent);
+                       continue;
+               }
+               pp = &p->next;
+               nth_parent++;
+       }
+
+       /* Removing parents can only increase TREESAMEness */
+       if (removed && !(commit->object.flags & TREESAME))
+               update_treesame(revs, commit);
+
+       return nth_parent;
+}
+
 static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail)
 {
        struct commit_list *p;
+       struct commit *parent;
        struct merge_simplify_state *st, *pst;
        int cnt;
 
@@ -2015,7 +2441,9 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c
        }
 
        /*
-        * Rewrite our list of parents.
+        * Rewrite our list of parents. Note that this cannot
+        * affect our TREESAME flags in any way - a commit is
+        * always TREESAME to its simplification.
         */
        for (p = commit->parents; p; p = p->next) {
                pst = locate_simplify_state(revs, p->item);
@@ -2027,43 +2455,53 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c
        if (revs->first_parent_only)
                cnt = 1;
        else
-               cnt = remove_duplicate_parents(commit);
+               cnt = remove_duplicate_parents(revs, commit);
 
        /*
         * It is possible that we are a merge and one side branch
         * does not have any commit that touches the given paths;
-        * in such a case, the immediate parents will be rewritten
-        * to different commits.
+        * in such a case, the immediate parent from that branch
+        * will be rewritten to be the merge base.
         *
         *      o----X          X: the commit we are looking at;
         *     /    /           o: a commit that touches the paths;
         * ---o----'
         *
-        * Further reduce the parents by removing redundant parents.
+        * Further, a merge of an independent branch that doesn't
+        * touch the path will reduce to a treesame root parent:
+        *
+        *  ----o----X          X: the commit we are looking at;
+        *          /           o: a commit that touches the paths;
+        *         r            r: a root commit not touching the paths
+        *
+        * Detect and simplify both cases.
         */
        if (1 < cnt) {
-               struct commit_list *h = reduce_heads(commit->parents);
-               cnt = commit_list_count(h);
-               free_commit_list(commit->parents);
-               commit->parents = h;
+               int marked = mark_redundant_parents(revs, commit);
+               marked += mark_treesame_root_parents(revs, commit);
+               if (marked)
+                       marked -= leave_one_treesame_to_parent(revs, commit);
+               if (marked)
+                       cnt = remove_marked_parents(revs, commit);
        }
 
        /*
         * A commit simplifies to itself if it is a root, if it is
         * UNINTERESTING, if it touches the given paths, or if it is a
-        * merge and its parents simplifies to more than one commits
+        * merge and its parents don't simplify to one relevant commit
         * (the first two cases are already handled at the beginning of
         * this function).
         *
-        * Otherwise, it simplifies to what its sole parent simplifies to.
+        * Otherwise, it simplifies to what its sole relevant parent
+        * simplifies to.
         */
        if (!cnt ||
            (commit->object.flags & UNINTERESTING) ||
            !(commit->object.flags & TREESAME) ||
-           (1 < cnt))
+           (parent = one_relevant_parent(revs, commit->parents)) == NULL)
                st->simplified = commit;
        else {
-               pst = locate_simplify_state(revs, commit->parents->item);
+               pst = locate_simplify_state(revs, parent);
                st->simplified = pst->simplified;
        }
        return tail;
@@ -2159,6 +2597,11 @@ int prepare_revision_walk(struct rev_info *revs)
        if (!revs->leak_pending)
                free(list);
 
+       /* Signal whether we need per-parent treesame decoration */
+       if (revs->simplify_merges ||
+           (revs->limited && limiting_can_increase_treesame(revs)))
+               revs->treesame.name = "treesame";
+
        if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
                commit_list_sort_by_date(&revs->commits);
        if (revs->no_walk)
@@ -2167,7 +2610,9 @@ int prepare_revision_walk(struct rev_info *revs)
                if (limit_list(revs) < 0)
                        return -1;
        if (revs->topo_order)
-               sort_in_topological_order(&revs->commits, revs->lifo);
+               sort_in_topological_order(&revs->commits, revs->sort_order);
+       if (revs->line_level_traverse)
+               line_log_filter(revs);
        if (revs->simplify_merges)
                simplify_merges(revs);
        if (revs->children.name)
@@ -2175,12 +2620,6 @@ int prepare_revision_walk(struct rev_info *revs)
        return 0;
 }
 
-enum rewrite_result {
-       rewrite_one_ok,
-       rewrite_one_noparents,
-       rewrite_one_error
-};
-
 static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
 {
        struct commit_list *cache = NULL;
@@ -2190,24 +2629,25 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp
                if (!revs->limited)
                        if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0)
                                return rewrite_one_error;
-               if (p->parents && p->parents->next)
-                       return rewrite_one_ok;
                if (p->object.flags & UNINTERESTING)
                        return rewrite_one_ok;
                if (!(p->object.flags & TREESAME))
                        return rewrite_one_ok;
                if (!p->parents)
                        return rewrite_one_noparents;
-               *pp = p->parents->item;
+               if ((p = one_relevant_parent(revs, p->parents)) == NULL)
+                       return rewrite_one_ok;
+               *pp = p;
        }
 }
 
-static int rewrite_parents(struct rev_info *revs, struct commit *commit)
+int rewrite_parents(struct rev_info *revs, struct commit *commit,
+       rewrite_parent_fn_t rewrite_parent)
 {
        struct commit_list **pp = &commit->parents;
        while (*pp) {
                struct commit_list *parent = *pp;
-               switch (rewrite_one(revs, &parent->item)) {
+               switch (rewrite_parent(revs, &parent->item)) {
                case rewrite_one_ok:
                        break;
                case rewrite_one_noparents:
@@ -2218,7 +2658,7 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
                }
                pp = &parent->next;
        }
-       remove_duplicate_parents(commit);
+       remove_duplicate_parents(revs, commit);
        return 0;
 }
 
@@ -2292,7 +2732,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
         * in it.
         */
        encoding = get_log_output_encoding();
-       message = logmsg_reencode(commit, encoding);
+       message = logmsg_reencode(commit, NULL, encoding);
 
        /* Copy the commit to temporary if we are using "fake" headers */
        if (buf.len)
@@ -2324,7 +2764,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
        return retval;
 }
 
-static inline int want_ancestry(struct rev_info *revs)
+static inline int want_ancestry(const struct rev_info *revs)
 {
        return (revs->rewrite_parents || revs->children.name);
 }
@@ -2342,10 +2782,7 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
        if (revs->min_age != -1 && (commit->date > revs->min_age))
                return commit_ignore;
        if (revs->min_parents || (revs->max_parents >= 0)) {
-               int n = 0;
-               struct commit_list *p;
-               for (p = commit->parents; p; p = p->next)
-                       n++;
+               int n = commit_list_count(commit->parents);
                if ((n < revs->min_parents) ||
                    ((revs->max_parents >= 0) && (n > revs->max_parents)))
                        return commit_ignore;
@@ -2355,12 +2792,23 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
        if (revs->prune && revs->dense) {
                /* Commit without changes? */
                if (commit->object.flags & TREESAME) {
+                       int n;
+                       struct commit_list *p;
                        /* drop merges unless we want parenthood */
                        if (!want_ancestry(revs))
                                return commit_ignore;
-                       /* non-merge - always ignore it */
-                       if (!commit->parents || !commit->parents->next)
-                               return commit_ignore;
+                       /*
+                        * If we want ancestry, then need to keep any merges
+                        * between relevant commits to tie together topology.
+                        * For consistency with TREESAME and simplification
+                        * use "relevant" here rather than just INTERESTING,
+                        * to treat bottom commit(s) as part of the topology.
+                        */
+                       for (n = 0, p = commit->parents; p; p = p->next)
+                               if (relevant_commit(p->item))
+                                       if (++n >= 2)
+                                               return commit_show;
+                       return commit_ignore;
                }
        }
        return commit_show;
@@ -2373,7 +2821,15 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
        if (action == commit_show &&
            !revs->show_all &&
            revs->prune && revs->dense && want_ancestry(revs)) {
-               if (rewrite_parents(revs, commit) < 0)
+               /*
+                * --full-diff on simplified parents is no good: it
+                * will show spurious changes from the commits that
+                * were elided.  So we save the parents on the side
+                * when --full-diff is in effect.
+                */
+               if (revs->full_diff)
+                       save_parents(revs, commit);
+               if (rewrite_parents(revs, commit, rewrite_one) < 0)
                        return commit_error;
        }
        return action;
@@ -2392,6 +2848,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
                free(entry);
 
                if (revs->reflog_info) {
+                       save_parents(revs, commit);
                        fake_reflog_parent(revs->reflog_info, commit);
                        commit->object.flags &= ~(ADDED | SEEN | SHOWN);
                }
@@ -2423,25 +2880,23 @@ static struct commit *get_revision_1(struct rev_info *revs)
        return NULL;
 }
 
-static void gc_boundary(struct object_array *array)
+/*
+ * Return true for entries that have not yet been shown.  (This is an
+ * object_array_each_func_t.)
+ */
+static int entry_unshown(struct object_array_entry *entry, void *cb_data_unused)
 {
-       unsigned nr = array->nr;
-       unsigned alloc = array->alloc;
-       struct object_array_entry *objects = array->objects;
+       return !(entry->item->flags & SHOWN);
+}
 
-       if (alloc <= nr) {
-               unsigned i, j;
-               for (i = j = 0; i < nr; i++) {
-                       if (objects[i].item->flags & SHOWN)
-                               continue;
-                       if (i != j)
-                               objects[j] = objects[i];
-                       j++;
-               }
-               for (i = j; i < nr; i++)
-                       objects[i].item = NULL;
-               array->nr = j;
-       }
+/*
+ * If array is on the verge of a realloc, garbage-collect any entries
+ * that have already been shown to try to free up some space.
+ */
+static void gc_boundary(struct object_array *array)
+{
+       if (array->nr == array->alloc)
+               object_array_filter(array, entry_unshown, NULL);
 }
 
 static void create_boundary_commit_list(struct rev_info *revs)
@@ -2482,7 +2937,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
         * If revs->topo_order is set, sort the boundary commits
         * in topological order
         */
-       sort_in_topological_order(&revs->commits, revs->lifo);
+       sort_in_topological_order(&revs->commits, revs->sort_order);
 }
 
 static struct commit *get_revision_internal(struct rev_info *revs)
@@ -2593,6 +3048,8 @@ struct commit *get_revision(struct rev_info *revs)
        c = get_revision_internal(revs);
        if (c && revs->graph)
                graph_update(revs->graph, c);
+       if (!c)
+               free_saved_parents(revs);
        return c;
 }
 
@@ -2624,3 +3081,54 @@ void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
        fputs(mark, stdout);
        putchar(' ');
 }
+
+define_commit_slab(saved_parents, struct commit_list *);
+
+#define EMPTY_PARENT_LIST ((struct commit_list *)-1)
+
+void save_parents(struct rev_info *revs, struct commit *commit)
+{
+       struct commit_list **pp;
+
+       if (!revs->saved_parents_slab) {
+               revs->saved_parents_slab = xmalloc(sizeof(struct saved_parents));
+               init_saved_parents(revs->saved_parents_slab);
+       }
+
+       pp = saved_parents_at(revs->saved_parents_slab, commit);
+
+       /*
+        * When walking with reflogs, we may visit the same commit
+        * several times: once for each appearance in the reflog.
+        *
+        * In this case, save_parents() will be called multiple times.
+        * We want to keep only the first set of parents.  We need to
+        * store a sentinel value for an empty (i.e., NULL) parent
+        * list to distinguish it from a not-yet-saved list, however.
+        */
+       if (*pp)
+               return;
+       if (commit->parents)
+               *pp = copy_commit_list(commit->parents);
+       else
+               *pp = EMPTY_PARENT_LIST;
+}
+
+struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit)
+{
+       struct commit_list *parents;
+
+       if (!revs->saved_parents_slab)
+               return commit->parents;
+
+       parents = *saved_parents_at(revs->saved_parents_slab, commit);
+       if (parents == EMPTY_PARENT_LIST)
+               return NULL;
+       return parents;
+}
+
+void free_saved_parents(struct rev_info *revs)
+{
+       if (revs->saved_parents_slab)
+               clear_saved_parents(revs->saved_parents_slab);
+}
index 01bd2b7c07719c9628bba13e34b581dc1fdbc0af..e7f1d211bf0a203978a3024bcbae5c98f25c60cf 100644 (file)
@@ -4,6 +4,7 @@
 #include "parse-options.h"
 #include "grep.h"
 #include "notes.h"
+#include "commit.h"
 
 #define SEEN           (1u<<0)
 #define UNINTERESTING   (1u<<1)
@@ -15,7 +16,8 @@
 #define ADDED          (1u<<7) /* Parents already parsed and added? */
 #define SYMMETRIC_LEFT (1u<<8)
 #define PATCHSAME      (1u<<9)
-#define ALL_REV_FLAGS  ((1u<<10)-1)
+#define BOTTOM         (1u<<10)
+#define ALL_REV_FLAGS  ((1u<<11)-1)
 
 #define DECORATE_SHORT_REFS    1
 #define DECORATE_FULL_REFS     2
@@ -23,6 +25,7 @@
 struct rev_info;
 struct log_info;
 struct string_list;
+struct saved_parents;
 
 struct rev_cmdline_info {
        unsigned int nr;
@@ -35,6 +38,7 @@ struct rev_cmdline_info {
                        REV_CMD_PARENTS_ONLY,
                        REV_CMD_LEFT,
                        REV_CMD_RIGHT,
+                       REV_CMD_MERGE_BASE,
                        REV_CMD_REV
                } whence;
                unsigned flags;
@@ -60,6 +64,10 @@ struct rev_info {
        const char *prefix;
        const char *def;
        struct pathspec prune_data;
+
+       /* topo-sort */
+       enum rev_sort_order sort_order;
+
        unsigned int    early_output:1,
                        ignore_missing:1;
 
@@ -70,7 +78,6 @@ struct rev_info {
                        show_all:1,
                        remove_empty_trees:1,
                        simplify_history:1,
-                       lifo:1,
                        topo_order:1,
                        simplify_merges:1,
                        simplify_by_decoration:1,
@@ -96,7 +103,8 @@ struct rev_info {
                        cherry_mark:1,
                        bisect:1,
                        ancestry_path:1,
-                       first_parent_only:1;
+                       first_parent_only:1,
+                       line_level_traverse:1;
 
        /* Diff flags */
        unsigned int    diff:1,
@@ -137,6 +145,7 @@ struct rev_info {
        int             numbered_files;
        int             reroll_count;
        char            *message_id;
+       struct ident_split from_ident;
        struct string_list *ref_message_ids;
        int             add_signoff;
        const char      *extra_headers;
@@ -167,6 +176,7 @@ struct rev_info {
        struct reflog_walk_info *reflog_info;
        struct decoration children;
        struct decoration merge_simplification;
+       struct decoration treesame;
 
        /* notes-specific options: which refs to show */
        struct display_notes_opt notes_opt;
@@ -175,6 +185,12 @@ struct rev_info {
        int count_left;
        int count_right;
        int count_same;
+
+       /* line level range that we are chasing */
+       struct decoration line_log_data;
+
+       /* copies of the parent lists, for --full-diff display */
+       struct saved_parents *saved_parents_slab;
 };
 
 #define REV_TREE_SAME          0
@@ -195,19 +211,23 @@ struct setup_revision_opt {
 };
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
-extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *);
+extern int setup_revisions(int argc, const char **argv, struct rev_info *revs,
+                          struct setup_revision_opt *);
 extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
-                                const struct option *options,
-                                const char * const usagestr[]);
+                              const struct option *options,
+                              const char * const usagestr[]);
 #define REVARG_CANNOT_BE_FILENAME 01
 #define REVARG_COMMITTISH 02
-extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
+extern int handle_revision_arg(const char *arg, struct rev_info *revs,
+                              int flags, unsigned revarg_opt);
 
 extern void reset_revision_walk(void);
 extern int prepare_revision_walk(struct rev_info *revs);
 extern struct commit *get_revision(struct rev_info *revs);
-extern char *get_revision_mark(const struct rev_info *revs, const struct commit *commit);
-extern void put_revision_mark(const struct rev_info *revs, const struct commit *commit);
+extern char *get_revision_mark(const struct rev_info *revs,
+                              const struct commit *commit);
+extern void put_revision_mark(const struct rev_info *revs,
+                             const struct commit *commit);
 
 extern void mark_parents_uninteresting(struct commit *commit);
 extern void mark_tree_uninteresting(struct tree *tree);
@@ -220,15 +240,19 @@ struct name_path {
 
 char *path_name(const struct name_path *path, const char *name);
 
-extern void show_object_with_name(FILE *, struct object *, const struct name_path *, const char *);
+extern void show_object_with_name(FILE *, struct object *,
+                                 const struct name_path *, const char *);
 
 extern void add_object(struct object *obj,
                       struct object_array *p,
                       struct name_path *path,
                       const char *name);
 
-extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
-extern void add_pending_sha1(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags);
+extern void add_pending_object(struct rev_info *revs,
+                              struct object *obj, const char *name);
+extern void add_pending_sha1(struct rev_info *revs,
+                            const char *name, const unsigned char *sha1,
+                            unsigned int flags);
 
 extern void add_head_to_pending(struct rev_info *);
 
@@ -238,7 +262,35 @@ enum commit_action {
        commit_error
 };
 
-extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
-extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
+extern enum commit_action get_commit_action(struct rev_info *revs,
+                                           struct commit *commit);
+extern enum commit_action simplify_commit(struct rev_info *revs,
+                                         struct commit *commit);
+
+enum rewrite_result {
+       rewrite_one_ok,
+       rewrite_one_noparents,
+       rewrite_one_error
+};
+
+typedef enum rewrite_result (*rewrite_parent_fn_t)(struct rev_info *revs, struct commit **pp);
+
+extern int rewrite_parents(struct rev_info *revs, struct commit *commit,
+       rewrite_parent_fn_t rewrite_parent);
+
+/*
+ * Save a copy of the parent list, and return the saved copy.  This is
+ * used by the log machinery to retrieve the original parents when
+ * commit->parents has been modified by history simpification.
+ *
+ * You may only call save_parents() once per commit (this is checked
+ * for non-root commits).
+ *
+ * get_saved_parents() will transparently return commit->parents if
+ * history simplification is off.
+ */
+extern void save_parents(struct rev_info *revs, struct commit *commit);
+extern struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);
+extern void free_saved_parents(struct rev_info *revs);
 
 #endif
index 1b32a12a29b64fcc7d8b2401b00fb8a70ff81bad..1b7f88eeb1f1971f1568d5991978192c2d00645e 100644 (file)
@@ -72,11 +72,14 @@ static inline void close_pair(int fd[2])
        close(fd[1]);
 }
 
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 static inline void dup_devnull(int to)
 {
        int fd = open("/dev/null", O_RDWR);
-       dup2(fd, to);
+       if (fd < 0)
+               die_errno(_("open /dev/null failed"));
+       if (dup2(fd, to) < 0)
+               die_errno(_("dup2(%d,%d) failed"), fd, to);
        close(fd);
 }
 #endif
@@ -159,7 +162,7 @@ static const char **prepare_shell_cmd(const char **argv)
                die("BUG: shell command is empty");
 
        if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
                nargv[nargc++] = SHELL_PATH;
 #else
                nargv[nargc++] = "sh";
@@ -182,7 +185,7 @@ static const char **prepare_shell_cmd(const char **argv)
        return nargv;
 }
 
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 static int execv_shell_cmd(const char **argv)
 {
        const char **nargv = prepare_shell_cmd(argv);
@@ -193,7 +196,7 @@ static int execv_shell_cmd(const char **argv)
 }
 #endif
 
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 static int child_err = 2;
 static int child_notifier = -1;
 
@@ -334,7 +337,7 @@ fail_pipe:
        trace_argv_printf(cmd->argv, "trace: run_command:");
        fflush(NULL);
 
-#ifndef WIN32
+#ifndef GIT_WINDOWS_NATIVE
 {
        int notify_pipe[2];
        if (pipe(notify_pipe))
index 221ce331405933f6f097b7e2b51401b70097e4e2..6b985afd07a5b58ecadad8859ae90b3430ccb4c2 100644 (file)
@@ -46,6 +46,7 @@ int finish_command(struct child_process *);
 int run_command(struct child_process *);
 
 extern char *find_hook(const char *name);
+LAST_ARG_MUST_BE_NULL
 extern int run_hook(const char *index_file, const char *name, ...);
 
 #define RUN_COMMAND_NO_STDIN 1
index cf8fbeb8d5ac2b6ca3b498f2115c02eeea22910a..351548f57db61e62b47584020464aee877b48d8d 100644 (file)
@@ -270,15 +270,20 @@ static int error_dirty_index(struct replay_opts *opts)
 }
 
 static int fast_forward_to(const unsigned char *to, const unsigned char *from,
-                          int unborn)
+                       int unborn, struct replay_opts *opts)
 {
        struct ref_lock *ref_lock;
+       struct strbuf sb = STRBUF_INIT;
+       int ret;
 
        read_cache();
        if (checkout_fast_forward(from, to, 1))
                exit(1); /* the callee should have complained already */
        ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from, 0);
-       return write_ref_sha1(ref_lock, to, "cherry-pick");
+       strbuf_addf(&sb, "%s: fast-forward", action_name(opts));
+       ret = write_ref_sha1(ref_lock, to, sb.buf);
+       strbuf_release(&sb);
+       return ret;
 }
 
 static int do_recursive_merge(struct commit *base, struct commit *next,
@@ -326,7 +331,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
                int i;
                strbuf_addstr(msgbuf, "\nConflicts:\n");
                for (i = 0; i < active_nr;) {
-                       struct cache_entry *ce = active_cache[i++];
+                       const struct cache_entry *ce = active_cache[i++];
                        if (ce_stage(ce)) {
                                strbuf_addch(msgbuf, '\t');
                                strbuf_addstr(msgbuf, ce->name);
@@ -366,8 +371,9 @@ 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, active_cache,
-                                 active_nr, 0))
+               if (cache_tree_update(active_cache_tree,
+                                     (const struct cache_entry * const *)active_cache,
+                                     active_nr, 0))
                        return error(_("Unable to update cache tree\n"));
 
        return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
@@ -474,7 +480,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
-       int res, unborn = 0;
+       int res, unborn = 0, allow;
 
        if (opts->no_commit) {
                /*
@@ -523,7 +529,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        if (opts->allow_ff &&
            ((parent && !hashcmp(parent->object.sha1, head)) ||
             (!parent && unborn)))
-            return fast_forward_to(commit->object.sha1, head, unborn);
+               return fast_forward_to(commit->object.sha1, head, unborn, opts);
 
        if (parent && parse_commit(parent) < 0)
                /* TRANSLATORS: The first %s will be "revert" or
@@ -624,14 +630,18 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                      msg.subject);
                print_advice(res == 1, opts);
                rerere(opts->allow_rerere_auto);
-       } else {
-               int allow = allow_empty(opts, commit);
-               if (allow < 0)
-                       return allow;
-               if (!opts->no_commit)
-                       res = run_git_commit(defmsg, opts, allow);
+               goto leave;
+       }
+
+       allow = allow_empty(opts, commit);
+       if (allow < 0) {
+               res = allow;
+               goto leave;
        }
+       if (!opts->no_commit)
+               res = run_git_commit(defmsg, opts, allow);
 
+leave:
        free_message(&msg);
        free(defmsg);
 
@@ -1077,10 +1087,10 @@ int sequencer_pick_revisions(struct replay_opts *opts)
                        continue;
 
                if (!get_sha1(name, sha1)) {
-                       enum object_type type = sha1_object_info(sha1, NULL);
-
-                       if (type > 0 && type != OBJ_COMMIT)
+                       if (!lookup_commit_reference_gently(sha1, 1)) {
+                               enum object_type type = sha1_object_info(sha1, NULL);
                                die(_("%s: can't cherry-pick a %s"), name, typename(type));
+                       }
                } else
                        die(_("%s: bad revision"), name);
        }
diff --git a/setup.c b/setup.c
index 94c1e61bda747ed5c664bbbf8431234dc8276d29..5262319be69b3bf3fd2492dea3d30e7353d66a23 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -360,6 +360,7 @@ int is_inside_work_tree(void)
 
 void setup_work_tree(void)
 {
+       struct strbuf sb = STRBUF_INIT;
        const char *work_tree, *git_dir;
        static int initialized = 0;
 
@@ -379,8 +380,10 @@ void setup_work_tree(void)
        if (getenv(GIT_WORK_TREE_ENVIRONMENT))
                setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
 
-       set_git_dir(relative_path(git_dir, work_tree));
+       set_git_dir(relative_path(git_dir, work_tree, &sb));
        initialized = 1;
+
+       strbuf_release(&sb);
 }
 
 static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
@@ -908,3 +911,15 @@ const char *resolve_gitdir(const char *suspect)
                return suspect;
        return read_gitfile(suspect);
 }
+
+/* if any standard file descriptor is missing open it to /dev/null */
+void sanitize_stdfds(void)
+{
+       int fd = open("/dev/null", O_RDWR, 0);
+       while (fd != -1 && fd < 2)
+               fd = dup(fd);
+       if (fd == -1)
+               die_errno("open /dev/null or dup failed");
+       if (fd > 2)
+               close(fd);
+}
index 64228a26d0be873040ebde37e95e62d20f5ba957..06784fb95af2ddbdbfc6ed97e6ccf2ac920393b3 100644 (file)
@@ -36,6 +36,9 @@ 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
@@ -1303,6 +1306,26 @@ static int git_open_noatime(const char *name)
        }
 }
 
+static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
+{
+       char *name = sha1_file_name(sha1);
+       struct alternate_object_database *alt;
+
+       if (!lstat(name, st))
+               return 0;
+
+       prepare_alt_odb();
+       errno = ENOENT;
+       for (alt = alt_odb_list; alt; alt = alt->next) {
+               name = alt->name;
+               fill_sha1_path(name, sha1);
+               if (!lstat(alt->base, st))
+                       return 0;
+       }
+
+       return -1;
+}
+
 static int open_sha1_file(const unsigned char *sha1)
 {
        int fd;
@@ -1690,46 +1713,21 @@ static int retry_bad_packed_offset(struct packed_git *p, off_t obj_offset)
        return type;
 }
 
-
 #define POI_STACK_PREALLOC 64
 
-static int packed_object_info(struct packed_git *p, off_t obj_offset,
-                             unsigned long *sizep, int *rtype)
+static enum object_type packed_to_object_type(struct packed_git *p,
+                                             off_t obj_offset,
+                                             enum object_type type,
+                                             struct pack_window **w_curs,
+                                             off_t curpos)
 {
-       struct pack_window *w_curs = NULL;
-       unsigned long size;
-       off_t curpos = obj_offset;
-       enum object_type type;
        off_t small_poi_stack[POI_STACK_PREALLOC];
        off_t *poi_stack = small_poi_stack;
        int poi_stack_nr = 0, poi_stack_alloc = POI_STACK_PREALLOC;
 
-       type = unpack_object_header(p, &w_curs, &curpos, &size);
-
-       if (rtype)
-               *rtype = type; /* representation type */
-
-       if (sizep) {
-               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
-                       off_t tmp_pos = curpos;
-                       off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
-                                                          type, obj_offset);
-                       if (!base_offset) {
-                               type = OBJ_BAD;
-                               goto out;
-                       }
-                       *sizep = get_size_from_delta(p, &w_curs, tmp_pos);
-                       if (*sizep == 0) {
-                               type = OBJ_BAD;
-                               goto out;
-                       }
-               } else {
-                       *sizep = size;
-               }
-       }
-
        while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
                off_t base_offset;
+               unsigned long size;
                /* Push the object we're going to leave behind */
                if (poi_stack_nr >= poi_stack_alloc && poi_stack == small_poi_stack) {
                        poi_stack_alloc = alloc_nr(poi_stack_nr);
@@ -1740,11 +1738,11 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
                }
                poi_stack[poi_stack_nr++] = obj_offset;
                /* If parsing the base offset fails, just unwind */
-               base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
+               base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
                if (!base_offset)
                        goto unwind;
                curpos = obj_offset = base_offset;
-               type = unpack_object_header(p, &w_curs, &curpos, &size);
+               type = unpack_object_header(p, w_curs, &curpos, &size);
                if (type <= OBJ_NONE) {
                        /* If getting the base itself fails, we first
                         * retry the base, otherwise unwind */
@@ -1771,7 +1769,6 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
 out:
        if (poi_stack != small_poi_stack)
                free(poi_stack);
-       unuse_pack(&w_curs);
        return type;
 
 unwind:
@@ -1785,6 +1782,57 @@ unwind:
        goto out;
 }
 
+static int packed_object_info(struct packed_git *p, off_t obj_offset,
+                             struct object_info *oi)
+{
+       struct pack_window *w_curs = NULL;
+       unsigned long size;
+       off_t curpos = obj_offset;
+       enum object_type type;
+
+       /*
+        * We always get the representation type, but only convert it to
+        * a "real" type later if the caller is interested.
+        */
+       type = unpack_object_header(p, &w_curs, &curpos, &size);
+
+       if (oi->sizep) {
+               if (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) {
+                       off_t tmp_pos = curpos;
+                       off_t base_offset = get_delta_base(p, &w_curs, &tmp_pos,
+                                                          type, obj_offset);
+                       if (!base_offset) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+                       *oi->sizep = get_size_from_delta(p, &w_curs, tmp_pos);
+                       if (*oi->sizep == 0) {
+                               type = OBJ_BAD;
+                               goto out;
+                       }
+               } else {
+                       *oi->sizep = size;
+               }
+       }
+
+       if (oi->disk_sizep) {
+               struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+               *oi->disk_sizep = revidx[1].offset - obj_offset;
+       }
+
+       if (oi->typep) {
+               *oi->typep = packed_to_object_type(p, obj_offset, type, &w_curs, curpos);
+               if (*oi->typep < 0) {
+                       type = OBJ_BAD;
+                       goto out;
+               }
+       }
+
+out:
+       unuse_pack(&w_curs);
+       return type;
+}
+
 static void *unpack_compressed_entry(struct packed_git *p,
                                    struct pack_window **w_curs,
                                    off_t curpos,
@@ -1956,12 +2004,19 @@ 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 = NULL;
+                       log_pack_access = no_log_pack_access;
                        return;
                }
        }
@@ -1992,7 +2047,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)
+       if (log_pack_access != no_log_pack_access)
                write_pack_access_log(p, obj_offset);
 
        /* PHASE 1: drill down to the innermost base object */
@@ -2128,7 +2183,6 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
                        error("failed to unpack compressed delta "
                              "at offset %"PRIuMAX" from %s",
                              (uintmax_t)curpos, p->pack_name);
-                       free(base);
                        data = NULL;
                        continue;
                }
@@ -2136,10 +2190,19 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
                data = patch_delta(base, base_size,
                                   delta_data, delta_size,
                                   &size);
+
+               /*
+                * We could not apply the delta; warn the user, but keep going.
+                * Our failure will be noticed either in the next iteration of
+                * the loop, or if this is the final delta, in the caller when
+                * we return NULL. Those code paths will take care of making
+                * a more explicit warning and retrying with another copy of
+                * the object.
+                */
                if (!data)
-                       die("failed to apply delta");
+                       error("failed to apply delta");
 
-               free (delta_data);
+               free(delta_data);
        }
 
        *final_type = type;
@@ -2339,7 +2402,8 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
 
 }
 
-static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *sizep)
+static int sha1_loose_object_info(const unsigned char *sha1,
+                                 struct object_info *oi)
 {
        int status;
        unsigned long mapsize, size;
@@ -2347,19 +2411,37 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size
        git_zstream stream;
        char hdr[32];
 
+       /*
+        * If we don't care about type or size, then we don't
+        * need to look inside the object at all.
+        */
+       if (!oi->typep && !oi->sizep) {
+               if (oi->disk_sizep) {
+                       struct stat st;
+                       if (stat_sha1_file(sha1, &st) < 0)
+                               return -1;
+                       *oi->disk_sizep = st.st_size;
+               }
+               return 0;
+       }
+
        map = map_sha1_file(sha1, &mapsize);
        if (!map)
-               return error("unable to find %s", sha1_to_hex(sha1));
+               return -1;
+       if (oi->disk_sizep)
+               *oi->disk_sizep = mapsize;
        if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
                status = error("unable to unpack %s header",
                               sha1_to_hex(sha1));
        else if ((status = parse_sha1_header(hdr, &size)) < 0)
                status = error("unable to parse %s header", sha1_to_hex(sha1));
-       else if (sizep)
-               *sizep = size;
+       else if (oi->sizep)
+               *oi->sizep = size;
        git_inflate_end(&stream);
        munmap(map, mapsize);
-       return status;
+       if (oi->typep)
+               *oi->typep = status;
+       return 0;
 }
 
 /* returns enum object_type or negative */
@@ -2367,34 +2449,37 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
 {
        struct cached_object *co;
        struct pack_entry e;
-       int status, rtype;
+       int rtype;
 
        co = find_cached_object(sha1);
        if (co) {
+               if (oi->typep)
+                       *(oi->typep) = co->type;
                if (oi->sizep)
                        *(oi->sizep) = co->size;
+               if (oi->disk_sizep)
+                       *(oi->disk_sizep) = 0;
                oi->whence = OI_CACHED;
-               return co->type;
+               return 0;
        }
 
        if (!find_pack_entry(sha1, &e)) {
                /* Most likely it's a loose object. */
-               status = sha1_loose_object_info(sha1, oi->sizep);
-               if (status >= 0) {
+               if (!sha1_loose_object_info(sha1, oi)) {
                        oi->whence = OI_LOOSE;
-                       return status;
+                       return 0;
                }
 
                /* Not a loose object; someone else may have just packed it. */
                reprepare_packed_git();
                if (!find_pack_entry(sha1, &e))
-                       return status;
+                       return -1;
        }
 
-       status = packed_object_info(e.p, e.offset, oi->sizep, &rtype);
-       if (status < 0) {
+       rtype = packed_object_info(e.p, e.offset, oi);
+       if (rtype < 0) {
                mark_bad_packed_object(e.p, sha1);
-               status = sha1_object_info_extended(sha1, oi);
+               return sha1_object_info_extended(sha1, oi);
        } else if (in_delta_base_cache(e.p, e.offset)) {
                oi->whence = OI_DBCACHED;
        } else {
@@ -2405,15 +2490,19 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
                                         rtype == OBJ_OFS_DELTA);
        }
 
-       return status;
+       return 0;
 }
 
 int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 {
-       struct object_info oi;
+       enum object_type type;
+       struct object_info oi = {NULL};
 
+       oi.typep = &type;
        oi.sizep = sizep;
-       return sha1_object_info_extended(sha1, &oi);
+       if (sha1_object_info_extended(sha1, &oi) < 0)
+               return -1;
+       return type;
 }
 
 static void *read_packed_sha1(const unsigned char *sha1,
@@ -2836,7 +2925,10 @@ int has_sha1_file(const unsigned char *sha1)
 
        if (find_pack_entry(sha1, &e))
                return 1;
-       return has_loose_object(sha1);
+       if (has_loose_object(sha1))
+               return 1;
+       reprepare_packed_git();
+       return find_pack_entry(sha1, &e);
 }
 
 static void check_tree(const void *buf, size_t size)
index 3820f28ae757cce54a95014629ade4f7feb56efc..65ad066d9bb4efd9b61142b40d790bac9543de1d 100644 (file)
@@ -52,7 +52,7 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char
        }
 
        if (!ds->candidate_ok) {
-               /* discard the candidate; we know it does not satisify fn */
+               /* discard the candidate; we know it does not satisfy fn */
                hashcpy(ds->candidate, current);
                ds->candidate_checked = 0;
                return;
@@ -241,7 +241,7 @@ static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(lookup_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(sha1), NULL, 0);
        if (obj && obj->type == OBJ_COMMIT)
                return 1;
        return 0;
@@ -431,22 +431,51 @@ static inline int upstream_mark(const char *string, int len)
 }
 
 static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
+static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf);
 
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
        static const char *warn_msg = "refname '%.*s' is ambiguous.";
+       static const char *object_name_msg = N_(
+       "Git normally never creates a ref that ends with 40 hex characters\n"
+       "because it will be ignored when you just specify 40-hex. These refs\n"
+       "may be created by mistake. For example,\n"
+       "\n"
+       "  git checkout -b $br $(git rev-parse ...)\n"
+       "\n"
+       "where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
+       "examine these refs and maybe delete them. Turn this message off by\n"
+       "running \"git config advice.objectNameWarning false\"");
+       unsigned char tmp_sha1[20];
        char *real_ref = NULL;
        int refs_found = 0;
-       int at, reflog_len;
-
-       if (len == 40 && !get_sha1_hex(str, sha1))
+       int at, reflog_len, nth_prior = 0;
+
+       if (len == 40 && !get_sha1_hex(str, sha1)) {
+               if (warn_on_object_refname_ambiguity) {
+                       refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
+                       if (refs_found > 0 && warn_ambiguous_refs) {
+                               warning(warn_msg, len, str);
+                               if (advice_object_name_warning)
+                                       fprintf(stderr, "%s\n", _(object_name_msg));
+                       }
+                       free(real_ref);
+               }
                return 0;
+       }
 
        /* basic@{time or number or -number} format to query ref-log */
        reflog_len = at = 0;
        if (len && str[len-1] == '}') {
-               for (at = len-2; at >= 0; at--) {
+               for (at = len-4; at >= 0; at--) {
                        if (str[at] == '@' && str[at+1] == '{') {
+                               if (str[at+2] == '-') {
+                                       if (at != 0)
+                                               /* @{-N} not at start */
+                                               return -1;
+                                       nth_prior = 1;
+                                       continue;
+                               }
                                if (!upstream_mark(str + at, len - at)) {
                                        reflog_len = (len-1) - (at+2);
                                        len = at;
@@ -460,20 +489,22 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
        if (len && ambiguous_path(str, len))
                return -1;
 
-       if (!len && reflog_len) {
+       if (nth_prior) {
                struct strbuf buf = STRBUF_INIT;
-               int ret;
-               /* try the @{-N} syntax for n-th checkout */
-               ret = interpret_branch_name(str+at, &buf);
-               if (ret > 0) {
-                       /* substitute this branch name and restart */
-                       return get_sha1_1(buf.buf, buf.len, sha1, 0);
-               } else if (ret == 0) {
-                       return -1;
+               int detached;
+
+               if (interpret_nth_prior_checkout(str, &buf) > 0) {
+                       detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1));
+                       strbuf_release(&buf);
+                       if (detached)
+                               return 0;
                }
+       }
+
+       if (!len && reflog_len)
                /* allow "@{...}" to mean the current branch reflog */
                refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
-       else if (reflog_len)
+       else if (reflog_len)
                refs_found = dwim_log(str, len, sha1, &real_ref);
        else
                refs_found = dwim_ref(str, len, sha1, &real_ref);
@@ -481,7 +512,9 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
        if (!refs_found)
                return -1;
 
-       if (warn_ambiguous_refs && refs_found > 1)
+       if (warn_ambiguous_refs &&
+           (refs_found > 1 ||
+            !get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
                warning(warn_msg, len, str);
 
        if (reflog_len) {
@@ -490,10 +523,6 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                unsigned long co_time;
                int co_tz, co_cnt;
 
-               /* a @{-N} placed anywhere except the start is an error */
-               if (str[at+2] == '-')
-                       return -1;
-
                /* Is it asking for N-th entry, or approxidate? */
                for (i = nth = 0; 0 <= nth && i < reflog_len; i++) {
                        char ch = str[at+2+i];
@@ -517,12 +546,21 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                }
                if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
+                       if (!len) {
+                               if (!prefixcmp(real_ref, "refs/heads/")) {
+                                       str = real_ref + 11;
+                                       len = strlen(real_ref + 11);
+                               } else {
+                                       /* detached HEAD */
+                                       str = "HEAD";
+                                       len = 4;
+                               }
+                       }
                        if (at_time)
                                warning("Log for '%.*s' only goes "
                                        "back to %s.", len, str,
                                        show_date(co_time, co_tz, DATE_RFC2822));
                        else {
-                               free(real_ref);
                                die("Log for '%.*s' only has %d entries.",
                                    len, str, co_cnt);
                        }
@@ -966,6 +1004,27 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
        return st;
 }
 
+static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
+{
+       /* we have extra data, which might need further processing */
+       struct strbuf tmp = STRBUF_INIT;
+       int used = buf->len;
+       int ret;
+
+       strbuf_add(buf, name + len, namelen - len);
+       ret = interpret_branch_name(buf->buf, &tmp);
+       /* that data was not interpreted, remove our cruft */
+       if (ret < 0) {
+               strbuf_setlen(buf, used);
+               return len;
+       }
+       strbuf_reset(buf);
+       strbuf_addbuf(buf, &tmp);
+       strbuf_release(&tmp);
+       /* tweak for size of {-N} versus expanded ref name */
+       return ret - used + len;
+}
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -995,28 +1054,13 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
        int len = interpret_nth_prior_checkout(name, buf);
        int tmp_len;
 
-       if (!len)
+       if (!len) {
                return len; /* syntax Ok, not enough switches */
-       if (0 < len && len == namelen)
-               return len; /* consumed all */
-       else if (0 < len) {
-               /* we have extra data, which might need further processing */
-               struct strbuf tmp = STRBUF_INIT;
-               int used = buf->len;
-               int ret;
-
-               strbuf_add(buf, name + len, namelen - len);
-               ret = interpret_branch_name(buf->buf, &tmp);
-               /* that data was not interpreted, remove our cruft */
-               if (ret < 0) {
-                       strbuf_setlen(buf, used);
-                       return len;
-               }
-               strbuf_reset(buf);
-               strbuf_addbuf(buf, &tmp);
-               strbuf_release(&tmp);
-               /* tweak for size of {-N} versus expanded ref name */
-               return ret - used + len;
+       } else if (len > 0) {
+               if (len == namelen)
+                       return len; /* consumed all */
+               else
+                       return reinterpret(name, namelen, len, buf);
        }
 
        cp = strchr(name, '@');
@@ -1033,14 +1077,15 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
         * points to something different than a branch.
         */
        if (!upstream)
-               return error(_("HEAD does not point to a branch"));
+               die(_("HEAD does not point to a branch"));
        if (!upstream->merge || !upstream->merge[0]->dst) {
                if (!ref_exists(upstream->refname))
-                       return error(_("No such branch: '%s'"), cp);
-               if (!upstream->merge)
-                       return error(_("No upstream configured for branch '%s'"),
-                                    upstream->name);
-               return error(
+                       die(_("No such branch: '%s'"), cp);
+               if (!upstream->merge) {
+                       die(_("No upstream configured for branch '%s'"),
+                               upstream->name);
+               }
+               die(
                        _("Upstream branch '%s' not stored as a remote-tracking branch"),
                        upstream->merge[0]->src);
        }
@@ -1055,9 +1100,13 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
 int strbuf_branchname(struct strbuf *sb, const char *name)
 {
        int len = strlen(name);
-       if (interpret_branch_name(name, sb) == len)
+       int used = interpret_branch_name(name, sb);
+
+       if (used == len)
                return 0;
-       strbuf_add(sb, name, len);
+       if (used < 0)
+               used = 0;
+       strbuf_add(sb, name + used, len - used);
        return len;
 }
 
@@ -1170,7 +1219,7 @@ static void diagnose_invalid_index_path(int stage,
                                        const char *filename)
 {
        struct stat st;
-       struct cache_entry *ce;
+       const struct cache_entry *ce;
        int pos;
        unsigned namelen = strlen(filename);
        unsigned fullnamelen;
@@ -1264,7 +1313,7 @@ static int get_sha1_with_context_1(const char *name,
         */
        if (name[0] == ':') {
                int stage = 0;
-               struct cache_entry *ce;
+               const struct cache_entry *ce;
                char *new_path = NULL;
                int pos;
                if (!only_to_die && namelen > 2 && name[1] == '/') {
index 6be915f38f1fe8dbe0a22c4cd8ae2569331f483f..cdf37d694de175adcce10fa2e2a79c6579603b0b 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -1,8 +1,19 @@
 #include "cache.h"
 #include "commit.h"
 #include "tag.h"
+#include "pkt-line.h"
 
 static int is_shallow = -1;
+static struct stat shallow_stat;
+static char *alternate_shallow_file;
+
+void set_alternate_shallow_file(const char *path)
+{
+       if (is_shallow != -1)
+               die("BUG: is_repository_shallow must not be called before set_alternate_shallow_file");
+       free(alternate_shallow_file);
+       alternate_shallow_file = path ? xstrdup(path) : NULL;
+}
 
 int register_shallow(const unsigned char *sha1)
 {
@@ -21,12 +32,21 @@ int is_repository_shallow(void)
 {
        FILE *fp;
        char buf[1024];
+       const char *path = alternate_shallow_file;
 
        if (is_shallow >= 0)
                return is_shallow;
 
-       fp = fopen(git_path("shallow"), "r");
-       if (!fp) {
+       if (!path)
+               path = git_path("shallow");
+       /*
+        * fetch-pack sets '--shallow-file ""' as an indicator that no
+        * shallow file should be used. We could just open it and it
+        * will likely fail. But let's do an explicit check instead.
+        */
+       if (!*path ||
+           stat(path, &shallow_stat) ||
+           (fp = fopen(path, "r")) == NULL) {
                is_shallow = 0;
                return is_shallow;
        }
@@ -91,20 +111,112 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
                                        continue;
                                *pointer = cur_depth;
                        }
-                       if (cur_depth < depth) {
-                               if (p->next)
-                                       add_object_array(&p->item->object,
-                                                       NULL, &stack);
-                               else {
-                                       commit = p->item;
-                                       cur_depth = *(int *)commit->util;
-                               }
-                       } else {
-                               commit_list_insert(p->item, &result);
-                               p->item->object.flags |= shallow_flag;
+                       if (p->next)
+                               add_object_array(&p->item->object,
+                                               NULL, &stack);
+                       else {
+                               commit = p->item;
+                               cur_depth = *(int *)commit->util;
                        }
                }
        }
 
        return result;
 }
+
+void check_shallow_file_for_update(void)
+{
+       struct stat st;
+
+       if (!is_shallow)
+               return;
+       else if (is_shallow == -1)
+               die("BUG: shallow must be initialized by now");
+
+       if (stat(git_path("shallow"), &st))
+               die("shallow file was removed during fetch");
+       else if (st.st_mtime != shallow_stat.st_mtime
+#ifdef USE_NSEC
+                || ST_MTIME_NSEC(st) != ST_MTIME_NSEC(shallow_stat)
+#endif
+                  )
+               die("shallow file was changed during fetch");
+}
+
+struct write_shallow_data {
+       struct strbuf *out;
+       int use_pack_protocol;
+       int count;
+};
+
+static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
+{
+       struct write_shallow_data *data = cb_data;
+       const char *hex = sha1_to_hex(graft->sha1);
+       if (graft->nr_parent != -1)
+               return 0;
+       data->count++;
+       if (data->use_pack_protocol)
+               packet_buf_write(data->out, "shallow %s", hex);
+       else {
+               strbuf_addstr(data->out, hex);
+               strbuf_addch(data->out, '\n');
+       }
+       return 0;
+}
+
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
+{
+       struct write_shallow_data data;
+       data.out = out;
+       data.use_pack_protocol = use_pack_protocol;
+       data.count = 0;
+       for_each_commit_graft(write_one_shallow, &data);
+       return data.count;
+}
+
+char *setup_temporary_shallow(void)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int fd;
+
+       if (write_shallow_commits(&sb, 0)) {
+               struct strbuf path = STRBUF_INIT;
+               strbuf_addstr(&path, git_path("shallow_XXXXXX"));
+               fd = xmkstemp(path.buf);
+               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+                       die_errno("failed to write to %s",
+                                 path.buf);
+               close(fd);
+               strbuf_release(&sb);
+               return strbuf_detach(&path, NULL);
+       }
+       /*
+        * is_repository_shallow() sees empty string as "no shallow
+        * file".
+        */
+       return xstrdup("");
+}
+
+void setup_alternate_shallow(struct lock_file *shallow_lock,
+                            const char **alternate_shallow_file)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int fd;
+
+       check_shallow_file_for_update();
+       fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
+                                      LOCK_DIE_ON_ERROR);
+       if (write_shallow_commits(&sb, 0)) {
+               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+                       die_errno("failed to write to %s",
+                                 shallow_lock->filename);
+               *alternate_shallow_file = shallow_lock->filename;
+       } else
+               /*
+                * is_repository_shallow() sees empty string as "no
+                * shallow file".
+                */
+               *alternate_shallow_file = "";
+       strbuf_release(&sb);
+}
diff --git a/shell.c b/shell.c
index 1429870a8f29d3d8b8e4d1d7ba3e228d15a73759..66350b220cc74abd383e10770f8325ba4e658e87 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -147,7 +147,6 @@ int main(int argc, char **argv)
        char *prog;
        const char **user_argv;
        struct commands *cmd;
-       int devnull_fd;
        int count;
 
        git_setup_gettext();
@@ -156,15 +155,10 @@ int main(int argc, char **argv)
 
        /*
         * Always open file descriptors 0/1/2 to avoid clobbering files
-        * in die().  It also avoids not messing up when the pipes are
-        * dup'ed onto stdin/stdout/stderr in the child processes we spawn.
+        * in die().  It also avoids messing up when the pipes are dup'ed
+        * onto stdin/stdout/stderr in the child processes we spawn.
         */
-       devnull_fd = open("/dev/null", O_RDWR);
-       while (devnull_fd >= 0 && devnull_fd <= 2)
-               devnull_fd = dup(devnull_fd);
-       if (devnull_fd == -1)
-               die_errno("opening /dev/null failed");
-       close (devnull_fd);
+       sanitize_stdfds();
 
        /*
         * Special hack to pretend to be a CVS server
index cabcd9d1577d89c5e944a4c12e3a8e6af901078c..debe904523252ae4fd2e18784641575c3f828dd2 100644 (file)
@@ -111,11 +111,11 @@ static enum input_source istream_source(const unsigned char *sha1,
        unsigned long size;
        int status;
 
+       oi->typep = type;
        oi->sizep = &size;
        status = sha1_object_info_extended(sha1, oi);
        if (status < 0)
                return stream_error;
-       *type = status;
 
        switch (oi->whence) {
        case OI_LOOSE:
@@ -135,7 +135,7 @@ struct git_istream *open_istream(const unsigned char *sha1,
                                 struct stream_filter *filter)
 {
        struct git_istream *st;
-       struct object_info oi;
+       struct object_info oi = {NULL};
        const unsigned char *real = lookup_replace_object(sha1);
        enum input_source src = istream_source(real, type, &oi);
 
@@ -149,7 +149,7 @@ struct git_istream *open_istream(const unsigned char *sha1,
                        return NULL;
                }
        }
-       if (st && filter) {
+       if (filter) {
                /* Add "&& !is_null_stream_filter(filter)" for performance */
                struct git_istream *nst = attach_stream_filter(st, filter);
                if (!nst)
index e728025f60dbf7f3df87b7104af6410832ddb653..c0f93c208c11b2dad0d508fcb7adf14873484385 100644 (file)
@@ -134,6 +134,9 @@ int parse_submodule_config_option(const char *var, const char *value)
                return 0;
 
        if (!strcmp(key, "path")) {
+               if (!value)
+                       return config_error_nonbool(var);
+
                config = unsorted_string_list_lookup(&config_name_for_path, value);
                if (config)
                        free(config->util);
@@ -151,6 +154,9 @@ int parse_submodule_config_option(const char *var, const char *value)
        } else if (!strcmp(key, "ignore")) {
                char *name_cstr;
 
+               if (!value)
+                       return config_error_nonbool(var);
+
                if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
                    strcmp(value, "all") && strcmp(value, "none")) {
                        warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
@@ -226,6 +232,7 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
        while ((commit = get_revision(rev))) {
                struct pretty_print_context ctx = {0};
                ctx.date_mode = rev->date_mode;
+               ctx.output_encoding = get_log_output_encoding();
                strbuf_setlen(&sb, 0);
                strbuf_addstr(&sb, line_prefix);
                if (commit->object.flags & SYMMETRIC_LEFT) {
@@ -603,9 +610,8 @@ int fetch_populated_submodules(const struct argv_array *options,
        if (!work_tree)
                goto out;
 
-       if (!the_index.initialized)
-               if (read_cache() < 0)
-                       die("index file corrupt");
+       if (read_cache() < 0)
+               die("index file corrupt");
 
        argv_array_push(&argv, "fetch");
        for (i = 0; i < options->argc; i++)
@@ -624,7 +630,7 @@ int fetch_populated_submodules(const struct argv_array *options,
                struct strbuf submodule_path = STRBUF_INIT;
                struct strbuf submodule_git_dir = STRBUF_INIT;
                struct strbuf submodule_prefix = STRBUF_INIT;
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                const char *git_dir, *name, *default_argv;
 
                if (!S_ISGITLINK(ce->ce_mode))
@@ -846,7 +852,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                struct commit *a, struct commit *b)
 {
        int i, j;
-       struct object_array merges;
+       struct object_array merges = OBJECT_ARRAY_INIT;
        struct commit *commit;
        int contains_another;
 
@@ -856,7 +862,6 @@ static int find_first_merges(struct object_array *result, const char *path,
        struct rev_info revs;
        struct setup_revision_opt rev_opts;
 
-       memset(&merges, 0, sizeof(merges));
        memset(result, 0, sizeof(struct object_array));
        memset(&rev_opts, 0, sizeof(rev_opts));
 
@@ -894,8 +899,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                }
 
                if (!contains_another)
-                       add_object_array(merges.objects[i].item,
-                                        merges.objects[i].name, result);
+                       add_object_array(merges.objects[i].item, NULL, result);
        }
 
        free(merges.objects);
index 1923cc104bc71e5a696676232d6f42b47a19de25..2373a04f7aecab62b8575f8564020b10fb874843 100644 (file)
@@ -15,9 +15,16 @@ PROVE ?= prove
 DEFAULT_TEST_TARGET ?= test
 TEST_LINT ?= test-lint-duplicates test-lint-executable
 
+ifdef TEST_OUTPUT_DIRECTORY
+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
+else
+TEST_RESULTS_DIRECTORY = test-results
+endif
+
 # Shell quote;
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+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))
@@ -36,10 +43,10 @@ $(T):
        @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 pre-clean:
-       $(RM) -r test-results
+       $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
 
 clean-except-prove-cache:
-       $(RM) -r 'trash directory'.* test-results
+       $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
        $(RM) -r valgrind/bin
 
 clean: clean-except-prove-cache
@@ -65,7 +72,7 @@ aggregate-results-and-cleanup: $(T)
        $(MAKE) clean
 
 aggregate-results:
-       for f in test-results/t*-*.counts; do \
+       for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
                echo "$$f"; \
        done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
 
@@ -83,42 +90,4 @@ valgrind:
 perf:
        $(MAKE) -C perf/ all
 
-# Smoke testing targets
--include ../GIT-VERSION-FILE
-uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo unknown')
-uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo unknown')
-
-test-results:
-       mkdir -p test-results
-
-test-results/git-smoke.tar.gz: test-results
-       '$(PERL_PATH_SQ)' ./harness \
-               --archive="test-results/git-smoke.tar.gz" \
-               $(T)
-
-smoke: test-results/git-smoke.tar.gz
-
-SMOKE_UPLOAD_FLAGS =
-ifdef SMOKE_USERNAME
-       SMOKE_UPLOAD_FLAGS += -F username="$(SMOKE_USERNAME)" -F password="$(SMOKE_PASSWORD)"
-endif
-ifdef SMOKE_COMMENT
-       SMOKE_UPLOAD_FLAGS += -F comments="$(SMOKE_COMMENT)"
-endif
-ifdef SMOKE_TAGS
-       SMOKE_UPLOAD_FLAGS += -F tags="$(SMOKE_TAGS)"
-endif
-
-smoke_report: smoke
-       curl \
-               -H "Expect: " \
-               -F project=Git \
-               -F architecture="$(uname_M)" \
-               -F platform="$(uname_S)" \
-               -F revision="$(GIT_VERSION)" \
-               -F report_file=@test-results/git-smoke.tar.gz \
-               $(SMOKE_UPLOAD_FLAGS) \
-               http://smoke.git.nix.is/app/projects/process_add_report/1 \
-       | grep -v ^Redirecting
-
 .PHONY: pre-clean $(T) aggregate-results clean valgrind perf
index e669bb31b9aa2513a6f71de9fd066a21a0a3bde7..2167125008db5e3e1c7d13310c8da3a4243e8718 100644 (file)
--- a/t/README
+++ b/t/README
@@ -76,6 +76,11 @@ appropriately before running "make".
        command being run and their output if any are also
        output.
 
+--verbose-only=<pattern>::
+       Like --verbose, but the effect is limited to tests with
+       numbers matching <pattern>.  The number matched against is
+       simply the running count of the test within the file.
+
 --debug::
        This may help the person who is developing a new test.
        It causes the command defined with test_debug to run.
@@ -121,6 +126,11 @@ appropriately before running "make".
        the 't/valgrind/' directory and use the commands under
        't/valgrind/bin/'.
 
+--valgrind-only=<pattern>::
+       Like --valgrind, but the effect is limited to tests with
+       numbers matching <pattern>.  The number matched against is
+       simply the running count of the test within the file.
+
 --tee::
        In addition to printing the test output to the terminal,
        write it to files named 't/test-results/$TEST_NAME.out'.
@@ -324,6 +334,9 @@ Don't:
    use 'test_must_fail git cmd'.  This will signal a failure if git
    dies in an unexpected way (e.g. segfault).
 
+   On the other hand, don't use test_must_fail for running regular
+   platform commands; just use '! cmd'.
+
  - use perl without spelling it as "$PERL_PATH". This is to help our
    friends on Windows where the platform Perl often adds CR before
    the end of line, and they bundle Git with a version of Perl that
@@ -592,6 +605,20 @@ library for your script to use.
                test_cmp expected actual
        '
 
+ - test_ln_s_add <path1> <path2>
+
+   This function helps systems whose filesystem does not support symbolic
+   links. Use it to add a symbolic link entry to the index when it is not
+   important that the file system entry is a symbolic link, i.e., instead
+   of the sequence
+
+       ln -s foo bar &&
+       git add bar
+
+   Sometimes it is possible to split a test in a part that does not need
+   the symbolic link in the file system and a part that does; then only
+   the latter part need be protected by a SYMLINKS prerequisite (see below).
+
 Prerequisites
 -------------
 
index c56a77d2378117006694d4b575445f89446ae80c..d4e7f4736f74e58da504a04be3898b0a4078fedb 100644 (file)
@@ -2,11 +2,21 @@
 # sourced from t8001-annotate.sh and t8002-blame.sh.
 
 check_count () {
-       head=
-       case "$1" in -h) head="$2"; shift; shift ;; esac
-       echo "$PROG file $head" >&4
-       $PROG file $head >.result || return 1
-       cat .result | perl -e '
+       head= &&
+       file='file' &&
+       options= &&
+       while :
+       do
+               case "$1" in
+               -h) head="$2"; shift; shift ;;
+               -f) file="$2"; shift; shift ;;
+               -*) options="$options $1"; shift ;;
+               *) break ;;
+               esac
+       done &&
+       echo "$PROG $options $file $head" >&4 &&
+       $PROG $options $file $head >actual &&
+       perl -e '
                my %expect = (@ARGV);
                my %count = map { $_ => 0 } keys %expect;
                while (<STDIN>) {
@@ -31,107 +41,246 @@ check_count () {
                        print STDERR "Author $author (expected $value, attributed $count) $ok\n";
                }
                exit($bad);
-       ' "$@"
+       ' "$@" <actual
 }
 
-test_expect_success \
-    'prepare reference tree' \
-    'echo "1A quick brown fox jumps over the" >file &&
-     echo "lazy dog" >>file &&
-     git add file &&
-     GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" git commit -a -m "Initial."'
-
-test_expect_success \
-    'check all lines blamed on A' \
-    'check_count A 2'
-
-test_expect_success \
-    'Setup new lines blamed on B' \
-    'echo "2A quick brown fox jumps over the" >>file &&
-     echo "lazy dog" >> file &&
-     GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" git commit -a -m "Second."'
-
-test_expect_success \
-    'Two lines blamed on A, two on B' \
-    'check_count A 2 B 2'
-
-test_expect_success \
-    'merge-setup part 1' \
-    'git checkout -b branch1 master &&
-     echo "3A slow green fox jumps into the" >> file &&
-     echo "well." >> file &&
-     GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" git commit -a -m "Branch1-1"'
-
-test_expect_success \
-    'Two lines blamed on A, two on B, two on B1' \
-    'check_count A 2 B 2 B1 2'
-
-test_expect_success \
-    'merge-setup part 2' \
-    'git checkout -b branch2 master &&
-     sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
-     mv file.new file &&
-     GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" git commit -a -m "Branch2-1"'
-
-test_expect_success \
-    'Two lines blamed on A, one on B, one on B2' \
-    'check_count A 2 B 1 B2 1'
-
-test_expect_success \
-    'merge-setup part 3' \
-    'git pull . branch1'
-
-test_expect_success \
-    'Two lines blamed on A, one on B, two on B1, one on B2' \
-    'check_count A 2 B 1 B1 2 B2 1'
-
-test_expect_success \
-    'Annotating an old revision works' \
-    'check_count -h master A 2 B 2'
-
-test_expect_success \
-    'Annotating an old revision works' \
-    'check_count -h master^ A 2'
-
-test_expect_success \
-    'merge-setup part 4' \
-    'echo "evil merge." >>file &&
-     git commit -a --amend'
-
-test_expect_success \
-    'Two lines blamed on A, one on B, two on B1, one on B2, one on A U Thor' \
-    'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1'
-
-test_expect_success \
-    'an incomplete line added' \
-    'echo "incomplete" | tr -d "\\012" >>file &&
-    GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" git commit -a -m "Incomplete"'
-
-test_expect_success \
-    'With incomplete lines.' \
-    'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1'
-
-test_expect_success \
-    'some edit' \
-    'mv file file.orig &&
-    {
-       cat file.orig &&
-       echo
-    } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file &&
-    echo "incomplete" | tr -d "\\012" >>file &&
-    GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" git commit -a -m "edit"'
-
-test_expect_success \
-    'some edit' \
-    'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1'
-
-test_expect_success \
-    'an obfuscated email added' \
-    'echo "No robots allowed" > file.new &&
-     cat file >> file.new &&
-     mv file.new file &&
-     GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" git commit -a -m "norobots"'
-
-test_expect_success \
-    'obfuscated email parsed' \
-    'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1'
+test_expect_success 'setup A lines' '
+       echo "1A quick brown fox jumps over the" >file &&
+       echo "lazy dog" >>file &&
+       git add file &&
+       GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
+       git commit -a -m "Initial."
+'
+
+test_expect_success 'blame 1 author' '
+       check_count A 2
+'
+
+test_expect_success 'setup B lines' '
+       echo "2A quick brown fox jumps over the" >>file &&
+       echo "lazy dog" >>file &&
+       GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
+       git commit -a -m "Second."
+'
+
+test_expect_success 'blame 2 authors' '
+       check_count A 2 B 2
+'
+
+test_expect_success 'setup B1 lines (branch1)' '
+       git checkout -b branch1 master &&
+       echo "3A slow green fox jumps into the" >>file &&
+       echo "well." >>file &&
+       GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" \
+       git commit -a -m "Branch1-1"
+'
+
+test_expect_success 'blame 2 authors + 1 branch1 author' '
+       check_count A 2 B 2 B1 2
+'
+
+test_expect_success 'setup B2 lines (branch2)' '
+       git checkout -b branch2 master &&
+       sed -e "s/2A quick brown/4A quick brown lazy dog/" <file >file.new &&
+       mv file.new file &&
+       GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" \
+       git commit -a -m "Branch2-1"
+'
+
+test_expect_success 'blame 2 authors + 1 branch2 author' '
+       check_count A 2 B 1 B2 1
+'
+
+test_expect_success 'merge branch1 & branch2' '
+       git pull . branch1
+'
+
+test_expect_success 'blame 2 authors + 2 merged-in authors' '
+       check_count A 2 B 1 B1 2 B2 1
+'
+
+test_expect_success 'blame ancestor' '
+       check_count -h master A 2 B 2
+'
+
+test_expect_success 'blame great-ancestor' '
+       check_count -h master^ A 2
+'
+
+test_expect_success 'setup evil merge' '
+       echo "evil merge." >>file &&
+       git commit -a --amend
+'
+
+test_expect_success 'blame evil merge' '
+       check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1
+'
+
+test_expect_success 'setup incomplete line' '
+       echo "incomplete" | tr -d "\\012" >>file &&
+       GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \
+       git commit -a -m "Incomplete"
+'
+
+test_expect_success 'blame incomplete line' '
+       check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1
+'
+
+test_expect_success 'setup edits' '
+       mv file file.orig &&
+       {
+               cat file.orig &&
+               echo
+       } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" >file &&
+       echo "incomplete" | tr -d "\\012" >>file &&
+       GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" \
+       git commit -a -m "edit"
+'
+
+test_expect_success 'blame edits' '
+       check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1
+'
+
+test_expect_success 'setup obfuscated email' '
+       echo "No robots allowed" >file.new &&
+       cat file >>file.new &&
+       mv file.new file &&
+       GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" \
+       git commit -a -m "norobots"
+'
+
+test_expect_success 'blame obfuscated email' '
+       check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L 1 (all)' '
+       check_count -L1 A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L , (all)' '
+       check_count -L, A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L X (X to end)' '
+       check_count -L5 B1 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L X, (X to end)' '
+       check_count -L5, B1 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L ,Y (up to Y)' '
+       check_count -L,3 A 1 B2 1 E 1
+'
+
+test_expect_success 'blame -L X,X' '
+       check_count -L3,3 B2 1
+'
+
+test_expect_success 'blame -L X,Y' '
+       check_count -L3,6 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L Y,X (undocumented)' '
+       check_count -L6,3 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X,+1' '
+       check_count -L3,+1 B2 1
+'
+
+test_expect_success 'blame -L X,+N' '
+       check_count -L3,+4 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X,-1' '
+       check_count -L3,-1 B2 1
+'
+
+test_expect_success 'blame -L X,-N' '
+       check_count -L6,-4 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L /RE/ (RE to end)' '
+       check_count -L/evil/ C 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,/RE2/' '
+       check_count -L/robot/,/green/ A 1 B 1 B2 1 D 1 E 1
+'
+
+test_expect_success 'blame -L X,/RE/' '
+       check_count -L5,/evil/ B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,Y' '
+       check_count -L/99/,7 B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,+N' '
+       check_count -L/99/,+3 B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,-N' '
+       check_count -L/99/,-3 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X (X > nlines)' '
+       test_must_fail $PROG -L12345 file
+'
+
+test_expect_success 'blame -L ,Y (Y > nlines)' '
+       test_must_fail $PROG -L,12345 file
+'
+
+test_expect_success 'setup -L :regex' '
+       tr Q "\\t" >hello.c <<-\EOF &&
+       int main(int argc, const char *argv[])
+       {
+       Qputs("hello");
+       }
+       EOF
+       git add hello.c &&
+       GIT_AUTHOR_NAME="F" GIT_AUTHOR_EMAIL="F@test.git" \
+       git commit -m "hello" &&
+
+       mv hello.c hello.orig &&
+       sed -e "/}/ {x; s/$/Qputs(\"goodbye\");/; G;}" <hello.orig |
+       tr Q "\\t" >hello.c &&
+       GIT_AUTHOR_NAME="G" GIT_AUTHOR_EMAIL="G@test.git" \
+       git commit -a -m "goodbye" &&
+
+       mv hello.c hello.orig &&
+       echo "#include <stdio.h>" >hello.c &&
+       cat hello.orig >>hello.c &&
+       tr Q "\\t" >>hello.c <<-\EOF
+       void mail()
+       {
+       Qputs("mail");
+       }
+       EOF
+       GIT_AUTHOR_NAME="H" GIT_AUTHOR_EMAIL="H@test.git" \
+       git commit -a -m "mail"
+'
+
+test_expect_success 'blame -L :literal' '
+       check_count -f hello.c -L:main F 4 G 1
+'
+
+test_expect_success 'blame -L :regex' '
+       check_count -f hello.c "-L:m[a-z][a-z]l" H 4
+'
+
+test_expect_success 'blame -L :nomatch' '
+       test_must_fail $PROG -L:nomatch hello.c
+'
+
+test_expect_success 'blame -L bogus' '
+       test_must_fail $PROG -L file &&
+       test_must_fail $PROG -L1,+ file &&
+       test_must_fail $PROG -L1,- file &&
+       test_must_fail $PROG -LX file &&
+       test_must_fail $PROG -L1,X file &&
+       test_must_fail $PROG -L1,+N file &&
+       test_must_fail $PROG -L1,-N file
+'
index 8b5a71dc05216dc0acfb7761a4b9ca789c716ce0..45971f43b77142fe1bb90262974c83c786cfe43d 100755 (executable)
@@ -21,6 +21,7 @@ while (<>) {
        /^\s*declare\s+/ and err 'arrays/declare not portable';
        /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
        /test\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
+       /^\s*export\s+[^=]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)';
        # this resets our $. for each file
        close ARGV if eof;
 }
index ae2dc4604f708d320d88d4f99ca9d575d88b6124..9e381e000f6cbece882dec43d2a3dbab3d18a983 100644 (file)
@@ -36,7 +36,7 @@ EOF
 
        # You can set the GITWEB_TEST_INSTALLED environment variable to
        # the gitwebdir (the directory where gitweb is installed / deployed to)
-       # of an existing gitweb instalation to test that installation,
+       # of an existing gitweb installation to test that installation,
        # or simply to pathname of installed gitweb script.
        if test -n "$GITWEB_TEST_INSTALLED" ; then
                if test -d $GITWEB_TEST_INSTALLED; then
index ad8553719a4f476bd24aa0294d4c8103d55f9d90..dd17e3a09d728a08c8038c5ce8187ea63112cdf3 100644 (file)
@@ -1,5 +1,4 @@
 ServerName dummy
-LockFile accept.lock
 PidFile httpd.pid
 DocumentRoot www
 LogFormat "%h %l %u %t \"%r\" %>s %b" common
@@ -24,6 +23,10 @@ ErrorLog error.log
        LoadModule version_module modules/mod_version.so
 </IfModule>
 
+<IfVersion < 2.4>
+LockFile accept.lock
+</IfVersion>
+
 <IfVersion < 2.1>
 <IfModule !mod_auth.c>
        LoadModule auth_module modules/mod_auth.so
@@ -40,6 +43,24 @@ ErrorLog error.log
 <IfModule !mod_authz_user.c>
        LoadModule authz_user_module modules/mod_authz_user.so
 </IfModule>
+<IfModule !mod_authz_host.c>
+       LoadModule authz_host_module modules/mod_authz_host.so
+</IfModule>
+</IfVersion>
+
+<IfVersion >= 2.4>
+<IfModule !mod_authn_core.c>
+       LoadModule authn_core_module modules/mod_authn_core.so
+</IfModule>
+<IfModule !mod_authz_core.c>
+       LoadModule authz_core_module modules/mod_authz_core.so
+</IfModule>
+<IfModule !mod_access_compat.c>
+       LoadModule access_compat_module modules/mod_access_compat.so
+</IfModule>
+<IfModule !mod_mpm_prefork.c>
+       LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
+</IfModule>
 </IfVersion>
 
 PassEnv GIT_VALGRIND
@@ -115,6 +136,21 @@ SSLEngine On
        Require valid-user
 </LocationMatch>
 
+RewriteCond %{QUERY_STRING} service=git-receive-pack [OR]
+RewriteCond %{REQUEST_URI} /git-receive-pack$
+RewriteRule ^/half-auth-complete/ - [E=AUTHREQUIRED:yes]
+
+<Location /half-auth-complete/>
+  Order Deny,Allow
+  Deny from env=AUTHREQUIRED
+
+  AuthType Basic
+  AuthName "Git Access"
+  AuthUserFile passwd
+  Require valid-user
+  Satisfy Any
+</Location>
+
 <IfDefine DAV>
        LoadModule dav_module modules/mod_dav.so
        LoadModule dav_fs_module modules/mod_dav_fs.so
index 6ccf7970916b58748aedcce7e583eed2dee782d3..8ff87fb3f9bee1c6955f84b66027037100ebbbd3 100644 (file)
 #       ("squash", "fixup", "edit", or "reword") and the SHA1 taken
 #       from the specified line.
 #
+#   "exec_cmd_with_args" -- add an "exec cmd with args" line.
+#
 #   "#" -- Add a comment line.
 #
 #   ">" -- Add a blank line.
 
 set_fake_editor () {
-       echo "#!$SHELL_PATH" >fake-editor.sh
-       cat >> fake-editor.sh <<\EOF
-case "$1" in
-*/COMMIT_EDITMSG)
-       test -z "$EXPECT_HEADER_COUNT" ||
-               test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+       write_script fake-editor.sh <<-\EOF
+       case "$1" in
+       */COMMIT_EDITMSG)
+               test -z "$EXPECT_HEADER_COUNT" ||
+                       test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+                       exit
+               test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+               test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
                exit
-       test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
-       test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
-       exit
-       ;;
-esac
-test -z "$EXPECT_COUNT" ||
-       test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
-       exit
-test -z "$FAKE_LINES" && exit
-grep -v '^#' < "$1" > "$1".tmp
-rm -f "$1"
-echo 'rebase -i script before editing:'
-cat "$1".tmp
-action=pick
-for line in $FAKE_LINES; do
-       case $line in
-       squash|fixup|edit|reword)
-               action="$line";;
-       exec*)
-               echo "$line" | sed 's/_/ /g' >> "$1";;
-       "#")
-               echo '# comment' >> "$1";;
-       ">")
-               echo >> "$1";;
-       *)
-               sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
-               action=pick;;
+               ;;
        esac
-done
-echo 'rebase -i script after editing:'
-cat "$1"
-EOF
+       test -z "$EXPECT_COUNT" ||
+               test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+               exit
+       test -z "$FAKE_LINES" && exit
+       grep -v '^#' < "$1" > "$1".tmp
+       rm -f "$1"
+       echo 'rebase -i script before editing:'
+       cat "$1".tmp
+       action=pick
+       for line in $FAKE_LINES; do
+               case $line in
+               squash|fixup|edit|reword)
+                       action="$line";;
+               exec*)
+                       echo "$line" | sed 's/_/ /g' >> "$1";;
+               "#")
+                       echo '# comment' >> "$1";;
+               ">")
+                       echo >> "$1";;
+               *)
+                       sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+                       action=pick;;
+               esac
+       done
+       echo 'rebase -i script after editing:'
+       cat "$1"
+       EOF
+
+       test_set_editor "$(pwd)/fake-editor.sh"
+}
+
+# After set_cat_todo_editor, rebase -i will write the todo list (ignoring
+# blank lines and comments) to stdout, and exit failure (so you should run
+# it with test_must_fail).  This can be used to verify the expected user
+# experience, for todo list changes that do not affect the outcome of
+# rebase; or as an extra check in addition to checking the outcome.
 
+set_cat_todo_editor () {
+       write_script fake-editor.sh <<-\EOF
+       grep "^[^#]" "$1"
+       exit 1
+       EOF
        test_set_editor "$(pwd)/fake-editor.sh"
-       chmod a+x fake-editor.sh
+}
+
+# checks that the revisions in "$2" represent a linear range with the
+# subjects in "$1"
+test_linear_range () {
+       revlist_merges=$(git rev-list --merges "$2") &&
+       test -z "$revlist_merges" &&
+       expected=$1
+       set -- $(git log --reverse --format=%s "$2")
+       test "$expected" = "$*"
+}
+
+reset_rebase () {
+       test_might_fail git rebase --abort &&
+       git reset --hard &&
+       git clean -f
+}
+
+cherry_pick () {
+       git cherry-pick -n "$2" &&
+       git commit -m "$1" &&
+       git tag "$1"
+}
+
+revert () {
+       git revert -n "$2" &&
+       git commit -m "$1" &&
+       git tag "$1"
+}
+
+make_empty () {
+       git commit --allow-empty -m "$1" &&
+       git tag "$1"
 }
index ea25dd89e505525507c16c62d91fb07c092b064a..3f2d873fec9ed9e98b787cd708cd9e8e816e7573 100644 (file)
@@ -1,55 +1,50 @@
 : included from 6002 and others
 
-[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+mkdir -p .git/refs/tags
 
-:> sed.script
+>sed.script
 
-# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
-tag()
-{
+# Answer the sha1 has associated with the tag. The tag must exist in .git/refs/tags
+tag () {
        _tag=$1
-       [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
-       cat .git/refs/tags/$_tag
+       test -f ".git/refs/tags/$_tag" || error "tag: \"$_tag\" does not exist"
+       cat ".git/refs/tags/$_tag"
 }
 
 # Generate a commit using the text specified to make it unique and the tree
 # named by the tag specified.
-unique_commit()
-{
+unique_commit () {
        _text=$1
-        _tree=$2
+       _tree=$2
        shift 2
-       echo $_text | git commit-tree $(tag $_tree) "$@"
+       echo "$_text" | git commit-tree $(tag "$_tree") "$@"
 }
 
 # Save the output of a command into the tag specified. Prepend
 # a substitution script for the tag onto the front of sed.script
-save_tag()
-{
+save_tag () {
        _tag=$1
-       [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+       test -n "$_tag" || error "usage: save_tag tag commit-args ..."
        shift 1
-       "$@" >.git/refs/tags/$_tag
+       "$@" >".git/refs/tags/$_tag"
 
-        echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
-       cat sed.script >> sed.script.tmp
+       echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp
+       cat sed.script >>sed.script.tmp
        rm sed.script
        mv sed.script.tmp sed.script
 }
 
-# Replace unhelpful sha1 hashses with their symbolic equivalents
-entag()
-{
+# Replace unhelpful sha1 hashes with their symbolic equivalents
+entag () {
        sed -f sed.script
 }
 
 # Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
 # tag to a specified value. Restore the original value on return.
-as_author()
-{
+as_author () {
        _author=$1
        shift 1
-        _save=$GIT_AUTHOR_EMAIL
+       _save=$GIT_AUTHOR_EMAIL
 
        GIT_AUTHOR_EMAIL="$_author"
        export GIT_AUTHOR_EMAIL
@@ -63,45 +58,58 @@ as_author()
        fi
 }
 
-commit_date()
-{
-        _commit=$1
-       git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+commit_date () {
+       _commit=$1
+       git cat-file commit $_commit |
+       sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
 }
 
-on_committer_date()
-{
-    _date=$1
-    shift 1
-    GIT_COMMITTER_DATE="$_date"
-    export GIT_COMMITTER_DATE
-    "$@"
-    unset GIT_COMMITTER_DATE
+# Assign the value of fake date to a variable, but
+# allow fairly common "1971-08-16 00:00" to be omittd
+assign_fake_date () {
+       case "$2" in
+       ??:??:??)       eval "$1='1971-08-16 $2'" ;;
+       ??:??)          eval "$1='1971-08-16 00:$2'" ;;
+       ??)             eval "$1='1971-08-16 00:00:$2'" ;;
+       *)              eval "$1='$2'" ;;
+       esac
+}
+
+on_committer_date () {
+       assign_fake_date GIT_COMMITTER_DATE "$1"
+       export GIT_COMMITTER_DATE
+       shift 1
+       "$@"
+}
+
+on_dates () {
+       assign_fake_date GIT_COMMITTER_DATE "$1"
+       assign_fake_date GIT_AUTHOR_DATE "$2"
+       export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+       shift 2
+       "$@"
 }
 
 # Execute a command and suppress any error output.
-hide_error()
-{
+hide_error () {
        "$@" 2>/dev/null
 }
 
-check_output()
-{
+check_output () {
        _name=$1
        shift 1
-       if eval "$*" | entag > $_name.actual
+       if eval "$*" | entag >"$_name.actual"
        then
-               test_cmp $_name.expected $_name.actual
+               test_cmp "$_name.expected" "$_name.actual"
        else
-               return 1;
+               return 1
        fi
 }
 
 # Turn a reasonable test description into a reasonable test name.
 # All alphanums translated into -'s which are then compressed and stripped
 # from front and back.
-name_from_description()
-{
+name_from_description () {
        perl -pe '
                s/[^A-Za-z0-9.]/-/g;
                s/-+/-/g;
@@ -119,9 +127,11 @@ name_from_description()
 test_output_expect_success()
 {
        _description=$1
-        _test=$2
-        [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
-        _name=$(echo $_description | name_from_description)
-       cat > $_name.expected
+       _test=$2
+       test $# -eq 2 ||
+       error "usage: test_output_expect_success description test <<EOF ... EOF"
+
+       _name=$(echo $_description | name_from_description)
+       cat >"$_name.expected"
        test_expect_success "$_description" "check_output $_name \"$_test\""
 }
index c552f561bffd9800d53064772ea640677b20a3ae..8848c1461909199a5c0bd587df8d9ef652c650ac 100644 (file)
@@ -66,7 +66,7 @@ You can set the following variables (also in your config.mak):
     GIT_PERF_LARGE_REPO
        Repositories to copy for the performance tests.  The normal
        repo should be at least git.git size.  The large repo should
-       probably be about linux-2.6.git size for optimal results.
+       probably be about linux.git size for optimal results.
        Both default to the git.git you are running from.
 
 You can also pass the options taken by ordinary git tests; the most
diff --git a/t/perf/p0002-read-cache.sh b/t/perf/p0002-read-cache.sh
new file mode 100755 (executable)
index 0000000..9180ae9
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+test_description="Tests performance of reading the index"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+count=1000
+test_perf "read_cache/discard_cache $count times" "
+       test-read-cache $count
+"
+
+test_done
diff --git a/t/perf/p4211-line-log.sh b/t/perf/p4211-line-log.sh
new file mode 100755 (executable)
index 0000000..3d074b0
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='Tests log -L performance'
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# Pick a file to log pseudo-randomly.  The sort key is the blob hash,
+# so it is stable.
+test_expect_success 'select a file' '
+       git ls-tree HEAD | grep ^100644 |
+       sort -k 3 | head -1 | cut -f 2 >filelist
+'
+
+file=$(cat filelist)
+export file
+
+test_perf 'git rev-list --topo-order (baseline)' '
+       git rev-list --topo-order HEAD >/dev/null
+'
+
+test_perf 'git log --follow (baseline for -M)' '
+       git log --oneline --follow -- "$file" >/dev/null
+'
+
+test_perf 'git log -L' '
+       git log -L 1:"$file" >/dev/null
+'
+
+test_perf 'git log -M -L' '
+       git log -M -L 1:"$file" >/dev/null
+'
+
+test_done
index a816fbcb7618bb8cdbf1f74a4fd2295923c02ea3..f4eecaa17110c2974cfdedab90db4604b6923443 100644 (file)
@@ -150,6 +150,7 @@ exit $ret' >&3 2>&4
 
 
 test_perf () {
+       test_start_
        test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-success"
@@ -160,7 +161,7 @@ test_perf () {
                echo "$test_count" >>"$perf_results_dir"/$base.subtests
                echo "$1" >"$perf_results_dir"/$base.$test_count.descr
                if test -z "$verbose"; then
-                       echo -n "perf $test_count - $1:"
+                       printf "%s" "perf $test_count - $1:"
                else
                        echo "perf $test_count - $1:"
                fi
@@ -169,7 +170,7 @@ test_perf () {
                        if test_run_perf_ "$2"
                        then
                                if test -z "$verbose"; then
-                                       echo -n " $i"
+                                       printf " %s" "$i"
                                else
                                        echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:"
                                fi
@@ -187,7 +188,7 @@ test_perf () {
                base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
                "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
        fi
-       echo >&3 ""
+       test_finish_
 }
 
 # We extend test_done to print timings at the end (./run disables this
index cefe33d6d1976e4017569507a6f76ad89c83e4ff..10be52beedb4058318555e096d51517f1c30ad9f 100755 (executable)
@@ -47,8 +47,14 @@ test_expect_failure 'pretend we have a known breakage' '
 
 run_sub_test_lib_test () {
        name="$1" descr="$2" # stdin is the body of the test code
+       shift 2
        mkdir "$name" &&
        (
+               # Pretend we're a test harness.  This prevents
+               # test-lib from writing the counts to a file that will
+               # later be summarized, showing spurious "failed" tests
+               HARNESS_ACTIVE=t &&
+               export HARNESS_ACTIVE &&
                cd "$name" &&
                cat >"$name.sh" <<-EOF &&
                #!$SHELL_PATH
@@ -65,7 +71,7 @@ run_sub_test_lib_test () {
                cat >>"$name.sh" &&
                chmod +x "$name.sh" &&
                export TEST_DIRECTORY &&
-               ./"$name.sh" >out 2>err
+               ./"$name.sh" "$@" >out 2>err
        )
 }
 
@@ -215,6 +221,60 @@ test_expect_success 'pretend we have a mix of all possible results' "
        EOF
 "
 
+test_expect_success 'test --verbose' '
+       test_must_fail run_sub_test_lib_test \
+               test-verbose "test verbose" --verbose <<-\EOF &&
+       test_expect_success "passing test" true
+       test_expect_success "test with output" "echo foo"
+       test_expect_success "failing test" false
+       test_done
+       EOF
+       mv test-verbose/out test-verbose/out+
+       grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
+       check_sub_test_lib_test test-verbose <<-\EOF
+       > expecting success: true
+       > Z
+       > ok 1 - passing test
+       > Z
+       > expecting success: echo foo
+       > foo
+       > Z
+       > ok 2 - test with output
+       > Z
+       > expecting success: false
+       > Z
+       > not ok 3 - failing test
+       > #     false
+       > Z
+       > # failed 1 among 3 test(s)
+       > 1..3
+       EOF
+'
+
+test_expect_success 'test --verbose-only' '
+       test_must_fail run_sub_test_lib_test \
+               test-verbose-only-2 "test verbose-only=2" \
+               --verbose-only=2 <<-\EOF &&
+       test_expect_success "passing test" true
+       test_expect_success "test with output" "echo foo"
+       test_expect_success "failing test" false
+       test_done
+       EOF
+       check_sub_test_lib_test test-verbose-only-2 <<-\EOF
+       > ok 1 - passing test
+       > Z
+       > expecting success: echo foo
+       > foo
+       > Z
+       > ok 2 - test with output
+       > Z
+       > not ok 3 - failing test
+       > #     false
+       > # failed 1 among 3 test(s)
+       > 1..3
+       EOF
+'
+
 test_set_prereq HAVEIT
 haveit=no
 test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
@@ -367,22 +427,6 @@ test_expect_success 'validate object ID of a known tree' '
 
 # Various types of objects
 
-# Some filesystems do not support symblic links; on such systems
-# some expected values are different
-if test_have_prereq SYMLINKS
-then
-       expectfilter=cat
-       expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b
-       expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3
-       expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2
-else
-       expectfilter='grep -v sym'
-       expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46
-       expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325
-       expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f
-fi
-
-
 test_expect_success 'adding various types of objects with git update-index --add' '
        mkdir path2 path3 path3/subp3 &&
        paths="path0 path2/file2 path3/file3 path3/subp3/file3" &&
@@ -390,10 +434,7 @@ test_expect_success 'adding various types of objects with git update-index --add
                for p in $paths
                do
                        echo "hello $p" >$p || exit 1
-                       if test_have_prereq SYMLINKS
-                       then
-                               ln -s "hello $p" ${p}sym || exit 1
-                       fi
+                       test_ln_s_add "hello $p" ${p}sym || exit 1
                done
        ) &&
        find path* ! -type d -print | xargs git update-index --add
@@ -405,7 +446,7 @@ test_expect_success 'showing stage with git ls-files --stage' '
 '
 
 test_expect_success 'validate git ls-files output for a known tree' '
-       $expectfilter >expected <<-\EOF &&
+       cat >expected <<-\EOF &&
        100644 f87290f8eb2cbbea7857214459a0739927eab154 0       path0
        120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0       path0sym
        100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0       path2/file2
@@ -423,14 +464,14 @@ test_expect_success 'writing tree out with git write-tree' '
 '
 
 test_expect_success 'validate object ID for a known tree' '
-       test "$tree" = "$expectedtree"
+       test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b
 '
 
 test_expect_success 'showing tree with git ls-tree' '
     git ls-tree $tree >current
 '
 
-test_expect_success SYMLINKS 'git ls-tree output for a known tree' '
+test_expect_success 'git ls-tree output for a known tree' '
        cat >expected <<-\EOF &&
        100644 blob f87290f8eb2cbbea7857214459a0739927eab154    path0
        120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01    path0sym
@@ -447,7 +488,7 @@ test_expect_success 'showing tree with git ls-tree -r' '
 '
 
 test_expect_success 'git ls-tree -r output for a known tree' '
-       $expectfilter >expected <<-\EOF &&
+       cat >expected <<-\EOF &&
        100644 blob f87290f8eb2cbbea7857214459a0739927eab154    path0
        120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01    path0sym
        100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7    path2/file2
@@ -465,7 +506,7 @@ test_expect_success 'showing tree with git ls-tree -r -t' '
        git ls-tree -r -t $tree >current
 '
 
-test_expect_success SYMLINKS 'git ls-tree -r output for a known tree' '
+test_expect_success 'git ls-tree -r output for a known tree' '
        cat >expected <<-\EOF &&
        100644 blob f87290f8eb2cbbea7857214459a0739927eab154    path0
        120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01    path0sym
@@ -487,7 +528,7 @@ test_expect_success 'writing partial tree out with git write-tree --prefix' '
 '
 
 test_expect_success 'validate object ID for a known tree' '
-       test "$ptree" = "$expectedptree1"
+       test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3
 '
 
 test_expect_success 'writing partial tree out with git write-tree --prefix' '
@@ -495,7 +536,7 @@ test_expect_success 'writing partial tree out with git write-tree --prefix' '
 '
 
 test_expect_success 'validate object ID for a known tree' '
-       test "$ptree" = "$expectedptree2"
+       test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2
 '
 
 test_expect_success 'put invalid objects into the index' '
@@ -529,7 +570,7 @@ test_expect_success 'git read-tree followed by write-tree should be idempotent'
 '
 
 test_expect_success 'validate git diff-files output for a know cache/work tree state' '
-       $expectfilter >expected <<\EOF &&
+       cat >expected <<\EOF &&
 :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M     path0
 :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M     path0sym
 :100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M     path2/file2
@@ -553,7 +594,7 @@ test_expect_success 'no diff after checkout and git update-index --refresh' '
 '
 
 ################################################################
-P=$expectedtree
+P=087704a96baf1c2d1c869a8b084481e121c88b5b
 
 test_expect_success 'git commit-tree records the correct tree in a commit' '
        commit0=$(echo NO | git commit-tree $P) &&
index ad664105646d4579a4e31ea5d230268acc33c0f5..9fb582b192e7d44856d3687234de922217e511f4 100755 (executable)
@@ -379,6 +379,10 @@ test_expect_success 'init with separate gitdir' '
        test -d realgitdir/refs
 '
 
+test_expect_success 're-init on .git file' '
+       ( cd newdir && git init )
+'
+
 test_expect_success 're-init to update git link' '
        (
        cd newdir &&
index 93e58c00e886db22b5ebf86af103efc4e65ec832..981437b3a88b86ee1ddd26f842a0048b8dc2b57f 100755 (executable)
@@ -20,4 +20,11 @@ test_expect_success 'sigchain works' '
        test_cmp expect actual
 '
 
+test_expect_success !MINGW 'signals are propagated using shell convention' '
+       # we use exec here to avoid any sub-shell interpretation
+       # of the exit code
+       git config alias.sigterm "!exec test-sigchain" &&
+       test_expect_code 143 git sigterm
+'
+
 test_done
index 9c1bde1fd6e6424f7af732556656a9daf9be350b..c29342d6bcd005ecc0eaee0ad5e17f65cdf45c3d 100755 (executable)
@@ -66,16 +66,23 @@ test_check_ignore () {
 
        init_vars &&
        rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" &&
-       echo git $global_args check-ignore $quiet_opt $verbose_opt $args \
+       echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
                >"$HOME/cmd" &&
+       echo "$expect_code" >"$HOME/expected-exit-code" &&
        test_expect_code "$expect_code" \
-               git $global_args check-ignore $quiet_opt $verbose_opt $args \
+               git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
                >"$HOME/stdout" 2>"$HOME/stderr" &&
        test_cmp "$HOME/expected-stdout" "$HOME/stdout" &&
        stderr_empty_on_success "$expect_code"
 }
 
-# Runs the same code with 3 different levels of output verbosity,
+# Runs the same code with 4 different levels of output verbosity:
+#
+#   1. with -q / --quiet
+#   2. with default verbosity
+#   3. with -v / --verbose
+#   4. with -v / --verbose, *and* -n / --non-matching
+#
 # expecting success each time.  Takes advantage of the fact that
 # check-ignore --verbose output is the same as normal output except
 # for the extra first column.
@@ -83,7 +90,9 @@ test_check_ignore () {
 # Arguments:
 #   - (optional) prereqs for this test, e.g. 'SYMLINKS'
 #   - test name
-#   - output to expect from -v / --verbose mode
+#   - output to expect from the fourth verbosity mode (the output
+#     from the other verbosity modes is automatically inferred
+#     from this value)
 #   - code to run (should invoke test_check_ignore)
 test_expect_success_multi () {
        prereq=
@@ -92,8 +101,9 @@ test_expect_success_multi () {
                prereq=$1
                shift
        fi
-       testname="$1" expect_verbose="$2" code="$3"
+       testname="$1" expect_all="$2" code="$3"
 
+       expect_verbose=$( echo "$expect_all" | grep -v '^::     ' )
        expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
 
        test_expect_success $prereq "$testname" '
@@ -101,23 +111,40 @@ test_expect_success_multi () {
                eval "$code"
        '
 
-       for quiet_opt in '-q' '--quiet'
-       do
-               test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
+       # --quiet is only valid when a single pattern is passed
+       if test $( echo "$expect_all" | wc -l ) = 1
+       then
+               for quiet_opt in '-q' '--quiet'
+               do
+                       test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
                        expect '' &&
                        $code
                "
-       done
-       quiet_opt=
+               done
+               quiet_opt=
+       fi
 
        for verbose_opt in '-v' '--verbose'
        do
-               test_expect_success $prereq "$testname${verbose_opt:+ with $verbose_opt}" "
-                       expect '$expect_verbose' &&
-                       $code
-               "
+               for non_matching_opt in '' ' -n' ' --non-matching'
+               do
+                       if test -n "$non_matching_opt"
+                       then
+                               my_expect="$expect_all"
+                       else
+                               my_expect="$expect_verbose"
+                       fi
+
+                       test_code="
+                               expect '$my_expect' &&
+                               $code
+                       "
+                       opts="$verbose_opt$non_matching_opt"
+                       test_expect_success $prereq "$testname${opts:+ with $opts}" "$test_code"
+               done
        done
        verbose_opt=
+       non_matching_opt=
 }
 
 test_expect_success 'setup' '
@@ -178,7 +205,7 @@ test_expect_success 'setup' '
 #
 # test invalid inputs
 
-test_expect_success_multi '. corner-case' '' '
+test_expect_success_multi '. corner-case' '::  .' '
        test_check_ignore . 1
 '
 
@@ -189,11 +216,7 @@ test_expect_success_multi 'empty command line' '' '
 
 test_expect_success_multi '--stdin with empty STDIN' '' '
        test_check_ignore "--stdin" 1 </dev/null &&
-       if test -n "$quiet_opt"; then
-               test_stderr ""
-       else
-               test_stderr "no pathspec given."
-       fi
+       test_stderr ""
 '
 
 test_expect_success '-q with multiple args' '
@@ -276,27 +299,39 @@ do
                where="in subdir $subdir"
        fi
 
-       test_expect_success_multi "non-existent file $where not ignored" '' "
-               test_check_ignore '${subdir}non-existent' 1
-       "
+       test_expect_success_multi "non-existent file $where not ignored" \
+               "::     ${subdir}non-existent" \
+               "test_check_ignore '${subdir}non-existent' 1"
 
        test_expect_success_multi "non-existent file $where ignored" \
-               ".gitignore:1:one       ${subdir}one" "
-               test_check_ignore '${subdir}one'
-       "
+               ".gitignore:1:one       ${subdir}one" \
+               "test_check_ignore '${subdir}one'"
 
-       test_expect_success_multi "existing untracked file $where not ignored" '' "
-               test_check_ignore '${subdir}not-ignored' 1
-       "
+       test_expect_success_multi "existing untracked file $where not ignored" \
+               "::     ${subdir}not-ignored" \
+               "test_check_ignore '${subdir}not-ignored' 1"
 
-       test_expect_success_multi "existing tracked file $where not ignored" '' "
-               test_check_ignore '${subdir}ignored-but-in-index' 1
-       "
+       test_expect_success_multi "existing tracked file $where not ignored" \
+               "::     ${subdir}ignored-but-in-index" \
+               "test_check_ignore '${subdir}ignored-but-in-index' 1"
 
        test_expect_success_multi "existing untracked file $where ignored" \
-               ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" "
-               test_check_ignore '${subdir}ignored-and-untracked'
-       "
+               ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
+               "test_check_ignore '${subdir}ignored-and-untracked'"
+
+       test_expect_success_multi "mix of file types $where" \
+"::    ${subdir}non-existent
+.gitignore:1:one       ${subdir}one
+::     ${subdir}not-ignored
+::     ${subdir}ignored-but-in-index
+.gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
+               "test_check_ignore '
+                       ${subdir}non-existent
+                       ${subdir}one
+                       ${subdir}not-ignored
+                       ${subdir}ignored-but-in-index
+                       ${subdir}ignored-and-untracked'
+               "
 done
 
 # Having established the above, from now on we mostly test against
@@ -391,7 +426,7 @@ test_expect_success 'cd to ignored sub-directory with -v' '
 #
 # test handling of symlinks
 
-test_expect_success_multi SYMLINKS 'symlink' '' '
+test_expect_success_multi SYMLINKS 'symlink' '::       a/symlink' '
        test_check_ignore "a/symlink" 1
 '
 
@@ -574,37 +609,34 @@ cat <<-\EOF >stdin
        globaltwo
        b/globaltwo
        ../b/globaltwo
+       c/not-ignored
 EOF
-cat <<-\EOF >expected-default
-       ../one
-       one
-       b/on
-       b/one
-       b/one one
-       b/one two
-       "b/one\"three"
-       b/two
-       b/twooo
-       ../globaltwo
-       globaltwo
-       b/globaltwo
-       ../b/globaltwo
-EOF
-cat <<-EOF >expected-verbose
+# N.B. we deliberately end STDIN with a non-matching pattern in order
+# to test that the exit code indicates that one or more of the
+# provided paths is ignored - in other words, that it represents an
+# aggregation of all the results, not just the final result.
+
+cat <<-EOF >expected-all
        .gitignore:1:one        ../one
+       ::      ../not-ignored
        .gitignore:1:one        one
+       ::      not-ignored
        a/b/.gitignore:8:!on*   b/on
        a/b/.gitignore:8:!on*   b/one
        a/b/.gitignore:8:!on*   b/one one
        a/b/.gitignore:8:!on*   b/one two
        a/b/.gitignore:8:!on*   "b/one\"three"
        a/b/.gitignore:9:!two   b/two
+       ::      b/not-ignored
        a/.gitignore:1:two*     b/twooo
        $global_excludes:2:!globaltwo   ../globaltwo
        $global_excludes:2:!globaltwo   globaltwo
        $global_excludes:2:!globaltwo   b/globaltwo
        $global_excludes:2:!globaltwo   ../b/globaltwo
+       ::      c/not-ignored
 EOF
+grep -v '^::   ' expected-all >expected-verbose
+sed -e 's/.*   //' expected-verbose >expected-default
 
 sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
        tr "\n" "\0" >stdin0
@@ -629,6 +661,14 @@ test_expect_success '--stdin from subdirectory with -v' '
        )
 '
 
+test_expect_success '--stdin from subdirectory with -v -n' '
+       expect_from_stdin <expected-all &&
+       (
+               cd a &&
+               test_check_ignore "--stdin -v -n" <../stdin
+       )
+'
+
 for opts in '--stdin -z' '-z --stdin'
 do
        test_expect_success "$opts from subdirectory" '
@@ -648,5 +688,31 @@ do
        '
 done
 
+test_expect_success PIPE 'streaming support for --stdin' '
+       mkfifo in out &&
+       (git check-ignore -n -v --stdin <in >out &) &&
+
+       # We cannot just "echo >in" because check-ignore would get EOF
+       # after echo exited; instead we open the descriptor in our
+       # shell, and then echo to the fd. We make sure to close it at
+       # the end, so that the subprocess does get EOF and dies
+       # properly.
+       #
+       # Similarly, we must keep "out" open so that check-ignore does
+       # not ever get SIGPIPE trying to write to us. Not only would that
+       # produce incorrect results, but then there would be no writer on the
+       # other end of the pipe, and we would potentially block forever trying
+       # to open it.
+       exec 9>in &&
+       exec 8<out &&
+       test_when_finished "exec 9>&-" &&
+       test_when_finished "exec 8<&-" &&
+       echo >&9 one &&
+       read response <&8 &&
+       echo "$response" | grep "^\.gitignore:1:one     one" &&
+       echo >&9 two &&
+       read response <&8 &&
+       echo "$response" | grep "^::    two"
+'
 
 test_done
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
new file mode 100755 (executable)
index 0000000..94045c3
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='basic tests for priority queue implementation'
+. ./test-lib.sh
+
+cat >expect <<'EOF'
+1
+2
+3
+4
+5
+5
+6
+7
+8
+9
+10
+EOF
+test_expect_success 'basic ordering' '
+       test-prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+2
+3
+4
+1
+5
+6
+EOF
+test_expect_success 'mixed put and get' '
+       test-prio-queue 6 2 4 get 5 3 get get 1 dump >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+1
+2
+NULL
+1
+2
+NULL
+EOF
+test_expect_success 'notice empty queue' '
+       test-prio-queue 1 2 get get get 1 2 get get get >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 1a8f44c44ca3cce70990ec56fec61894be9b3086..e526184a0bfa3c8ad2171d0c41a7bee22cf9a04e 100755 (executable)
@@ -81,6 +81,14 @@ test_expect_success 'safecrlf: print warning only once' '
        test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1
 '
 
+
+test_expect_success 'safecrlf: git diff demotes safecrlf=true to warn' '
+       git config core.autocrlf input &&
+       git config core.safecrlf true &&
+       git diff HEAD
+'
+
+
 test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
        git config core.autocrlf false &&
        git config core.safecrlf false &&
index e50f0f742fdc4dca766e3f236cd32e388f0c89aa..b92e6cb0469ebb2e328c490cba9b8aaa7c1dbb84 100755 (executable)
@@ -190,4 +190,18 @@ test_expect_success 'required filter clean failure' '
        test_must_fail git add test.fc
 '
 
+test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
+
+test_expect_success EXPENSIVE 'filter large file' '
+       git config filter.largefile.smudge cat &&
+       git config filter.largefile.clean cat &&
+       for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB &&
+       echo "2GB filter=largefile" >.gitattributes &&
+       git add 2GB 2>err &&
+       ! test -s err &&
+       rm -f 2GB &&
+       git checkout -- 2GB 2>err &&
+       ! test -s err
+'
+
 test_done
index 244a43c9201a3d81d0931f5f127bd2dcfd068eba..65606df3ed8b87256273dc85056f57a404b71c99 100755 (executable)
@@ -50,7 +50,7 @@ EOF
 
 test_expect_success 'test help' '
        test_must_fail test-parse-options -h > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_i18ncmp expect output
 '
 
@@ -75,7 +75,7 @@ check() {
        shift &&
        sed "s/^$what .*/$what $expect/" <expect.template >expect &&
        test-parse-options $* >output 2>output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 }
 
@@ -86,7 +86,7 @@ check_i18n() {
        shift &&
        sed "s/^$what .*/$what $expect/" <expect.template >expect &&
        test-parse-options $* >output 2>output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_i18ncmp expect output
 }
 
@@ -99,7 +99,7 @@ check_unknown() {
        esac &&
        cat expect.err >>expect &&
        test_must_fail test-parse-options $* >output 2>output.err &&
-       test ! -s output &&
+       test_must_be_empty output &&
        test_cmp expect output.err
 }
 
@@ -112,7 +112,7 @@ check_unknown_i18n() {
        esac &&
        cat expect.err >>expect &&
        test_must_fail test-parse-options $* >output 2>output.err &&
-       test ! -s output &&
+       test_must_be_empty output &&
        test_i18ncmp expect output.err
 }
 
@@ -149,7 +149,7 @@ test_expect_success 'short options' '
        test-parse-options -s123 -b -i 1729 -b -vv -n -F my.file \
        > output 2> output.err &&
        test_cmp expect output &&
-       test ! -s output.err
+       test_must_be_empty output.err
 '
 
 cat > expect << EOF
@@ -168,7 +168,7 @@ test_expect_success 'long options' '
        test-parse-options --boolean --integer 1729 --boolean --string2=321 \
                --verbose --verbose --no-dry-run --abbrev=10 --file fi.le\
                --obsolete > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -199,7 +199,7 @@ EOF
 test_expect_success 'intermingled arguments' '
        test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \
                > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -217,13 +217,13 @@ EOF
 
 test_expect_success 'unambiguously abbreviated option' '
        test-parse-options --int 2 --boolean --no-bo > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'unambiguously abbreviated option with "="' '
        test-parse-options --int=2 > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -246,7 +246,7 @@ EOF
 
 test_expect_success 'non ambiguous option (after two options it abbreviates)' '
        test-parse-options --st 123 > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -256,7 +256,7 @@ EOF
 
 test_expect_success 'detect possible typos' '
        test_must_fail test-parse-options -boolean > output 2> output.err &&
-       test ! -s output &&
+       test_must_be_empty output &&
        test_cmp typo.err output.err
 '
 
@@ -266,7 +266,7 @@ EOF
 
 test_expect_success 'detect possible typos' '
        test_must_fail test-parse-options -ambiguous > output 2> output.err &&
-       test ! -s output &&
+       test_must_be_empty output &&
        test_cmp typo.err output.err
 '
 
@@ -285,7 +285,7 @@ EOF
 
 test_expect_success 'keep some options as arguments' '
        test-parse-options --quux > output 2> output.err &&
-        test ! -s output.err &&
+       test_must_be_empty output.err &&
         test_cmp expect output
 '
 
@@ -305,7 +305,7 @@ EOF
 test_expect_success 'OPT_DATE() and OPT_SET_PTR() work' '
        test-parse-options -t "1970-01-01 00:00:01 +0000" --default-string \
                foo -q > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -324,7 +324,7 @@ EOF
 
 test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
        test-parse-options --length=four -b -4 > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -352,13 +352,13 @@ EOF
 
 test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
        test-parse-options --set23 -bbbbb --no-or4 > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' '
        test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -376,19 +376,19 @@ EOF
 
 test_expect_success 'OPT_BIT() works' '
        test-parse-options -bb --or4 > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_NEGBIT() works' '
        test-parse-options -bb --no-neg-or4 > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' '
        test-parse-options + + + + + + > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -406,7 +406,7 @@ EOF
 
 test_expect_success 'OPT_NUMBER_CALLBACK() works' '
        test-parse-options -12345 > output 2> output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
@@ -424,7 +424,7 @@ EOF
 
 test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
        test-parse-options --no-ambig >output 2>output.err &&
-       test ! -s output.err &&
+       test_must_be_empty output.err &&
        test_cmp expect output
 '
 
index 09a42a428e1d286c5ab9e1003d49481ef6df3e44..3a48de20d8e0d63a6901cb8df037805e97c5c3f3 100755 (executable)
@@ -8,8 +8,15 @@ test_description='Test various path utilities'
 . ./test-lib.sh
 
 norm_path() {
+       expected=$(test-path-utils mingw_path "$2")
        test_expect_success $3 "normalize path: $1 => $2" \
-       "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
+       "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'"
+}
+
+relative_path() {
+       expected=$(test-path-utils mingw_path "$3")
+       test_expect_success $4 "relative path: $1 $2 => $3" \
+       "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
 }
 
 # On Windows, we are using MSYS's bash, which mangles the paths.
@@ -34,8 +41,8 @@ ancestor() {
         test \"\$actual\" = '$expected'"
 }
 
-# Absolute path tests must be skipped on Windows because due to path mangling
-# the test program never sees a POSIX-style absolute path
+# Some absolute path tests should be skipped on Windows due to path mangling
+# on POSIX-style absolute paths
 case $(uname -s) in
 *MINGW*)
        ;;
@@ -68,30 +75,30 @@ norm_path d1/s1//../s2/../../d2 d2
 norm_path d1/.../d2 d1/.../d2
 norm_path d1/..././../d2 d1/d2
 
-norm_path / / POSIX
+norm_path / /
 norm_path // / POSIX
 norm_path /// / POSIX
-norm_path /. / POSIX
+norm_path /. /
 norm_path /./ / POSIX
 norm_path /./.. ++failed++ POSIX
-norm_path /../. ++failed++ POSIX
+norm_path /../. ++failed++
 norm_path /./../.// ++failed++ POSIX
 norm_path /dir/.. / POSIX
 norm_path /dir/sub/../.. / POSIX
 norm_path /dir/sub/../../.. ++failed++ POSIX
-norm_path /dir /dir POSIX
-norm_path /dir// /dir/ POSIX
-norm_path /./dir /dir POSIX
-norm_path /dir/. /dir/ POSIX
-norm_path /dir///./ /dir/ POSIX
-norm_path /dir//sub/.. /dir/ POSIX
-norm_path /dir/sub/../ /dir/ POSIX
+norm_path /dir /dir
+norm_path /dir// /dir/
+norm_path /./dir /dir
+norm_path /dir/. /dir/
+norm_path /dir///./ /dir/
+norm_path /dir//sub/.. /dir/
+norm_path /dir/sub/../ /dir/
 norm_path //dir/sub/../. /dir/ POSIX
-norm_path /dir/s1/../s2/ /dir/s2/ POSIX
-norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
-norm_path /d1/s1//../s2/../../d2 /d2 POSIX
-norm_path /d1/.../d2 /d1/.../d2 POSIX
-norm_path /d1/..././../d2 /d1/d2 POSIX
+norm_path /dir/s1/../s2/ /dir/s2/
+norm_path /d1/s1///s2/..//../s3/ /d1/s3/
+norm_path /d1/s1//../s2/../../d2 /d2
+norm_path /d1/.../d2 /d1/.../d2
+norm_path /d1/..././../d2 /d1/d2
 
 ancestor / / -1
 ancestor /foo / 0
@@ -183,4 +190,33 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
        test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
 '
 
+relative_path /a/b/c/  /a/b/           c/
+relative_path /a/b/c/  /a/b            c/
+relative_path /a//b//c/        //a/b//         c/      POSIX
+relative_path /a/b     /a/b            ./
+relative_path /a/b/    /a/b            ./
+relative_path /a       /a/b            ../
+relative_path /                /a/b/           ../../
+relative_path /a/c     /a/b/           ../c
+relative_path /a/c     /a/b            ../c
+relative_path /x/y     /a/b/           ../../x/y
+relative_path /a/b     "<empty>"       /a/b
+relative_path /a/b     "<null>"        /a/b
+relative_path a/b/c/   a/b/            c/
+relative_path a/b/c/   a/b             c/
+relative_path a/b//c   a//b            c
+relative_path a/b/     a/b/            ./
+relative_path a/b/     a/b             ./
+relative_path a                a/b             ../
+relative_path x/y      a/b             ../../x/y
+relative_path a/c      a/b             ../c
+relative_path a/b      "<empty>"       a/b
+relative_path a/b      "<null>"        a/b
+relative_path "<empty>"        /a/b            ./
+relative_path "<empty>"        "<empty>"       ./
+relative_path "<empty>"        "<null>"        ./
+relative_path "<null>" "<empty>"       ./
+relative_path "<null>" "<null>"        ./
+relative_path "<null>" /a/b            ./
+
 test_done
index da2c504e53e37840f71790d6b262106b7b57d697..986b2a8f2664b1734964049a5b24fc50cf938cfe 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success 'mktemp to nonexistent directory prints filename' '
        grep "doesnotexist/test" err
 '
 
-test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' '
+test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints filename' '
        mkdir cannotwrite &&
        chmod -w cannotwrite &&
        test_when_finished "chmod +w cannotwrite" &&
index 315b9b3f10cfae0186afca6f8024d73cce0a19e2..e0a69402324fed3c2fd7ca9495f5735742766108 100755 (executable)
@@ -27,6 +27,7 @@ test_expect_success 'merge @{-1}' '
        test_commit B &&
        git checkout A &&
        test_commit C &&
+       test_commit D &&
        git branch -f master B &&
        git branch -f other &&
        git checkout other &&
@@ -35,14 +36,24 @@ test_expect_success 'merge @{-1}' '
        git cat-file commit HEAD | grep "Merge branch '\''other'\''"
 '
 
-test_expect_success 'merge @{-1} when there is not enough switches yet' '
+test_expect_success 'merge @{-1}~1' '
+       git checkout master &&
+       git reset --hard B &&
+       git checkout other &&
+       git checkout master &&
+       git merge @{-1}~1 &&
+       git cat-file commit HEAD >actual &&
+       grep "Merge branch '\''other'\''" actual
+'
+
+test_expect_success 'merge @{-100} before checking out that many branches yet' '
        git reflog expire --expire=now &&
        git checkout -f master &&
        git reset --hard B &&
        git branch -f other C &&
        git checkout other &&
        git checkout master &&
-       test_must_fail git merge @{-12}
+       test_must_fail git merge @{-100}
 '
 
 test_done
index b3ae7d52c6c76895434c1d2e8dfb9a31b12ebc62..3e72aff470f8d9e5e79689de459904d1f3271d0d 100755 (executable)
@@ -158,7 +158,7 @@ test_expect_success '3-way not overwriting local changes (their side)' '
 
 '
 
-test_expect_success SYMLINKS 'funny symlink in work tree' '
+test_expect_success 'funny symlink in work tree' '
 
        git reset --hard &&
        git checkout -b sym-b side-b &&
@@ -170,15 +170,14 @@ test_expect_success SYMLINKS 'funny symlink in work tree' '
        rm -fr a &&
        git checkout -b sym-a side-a &&
        mkdir -p a &&
-       ln -s ../b a/b &&
-       git add a/b &&
+       test_ln_s_add ../b a/b &&
        git commit -m "we add a/b" &&
 
        read_tree_u_must_succeed -m -u sym-a sym-a sym-b
 
 '
 
-test_expect_success SYMLINKS,SANITY 'funny symlink in work tree, un-unlink-able' '
+test_expect_success SANITY 'funny symlink in work tree, un-unlink-able' '
 
        rm -fr a b &&
        git reset --hard &&
index 9820f70c84d93b9461ae6663e6ab5b3cd735e3d5..4e911fb43d8ea2c699b3a836547b618274db6e05 100755 (executable)
@@ -36,66 +36,47 @@ $content"
     '
 
     test_expect_success "Type of $type is correct" '
-        test $type = "$(git cat-file -t $sha1)"
+       echo $type >expect &&
+       git cat-file -t $sha1 >actual &&
+       test_cmp expect actual
     '
 
     test_expect_success "Size of $type is correct" '
-        test $size = "$(git cat-file -s $sha1)"
+       echo $size >expect &&
+       git cat-file -s $sha1 >actual &&
+       test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "Content of $type is correct" '
-       expect="$(maybe_remove_timestamp "$content" $no_ts)"
-       actual="$(maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts)"
-
-        if test "z$expect" = "z$actual"
-       then
-               : happy
-       else
-               echo "Oops: expected $expect"
-               echo "but got $actual"
-               false
-        fi
+       maybe_remove_timestamp "$content" $no_ts >expect &&
+       maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual &&
+       test_cmp expect actual
     '
 
     test_expect_success "Pretty content of $type is correct" '
-       expect="$(maybe_remove_timestamp "$pretty_content" $no_ts)"
-       actual="$(maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts)"
-        if test "z$expect" = "z$actual"
-       then
-               : happy
-       else
-               echo "Oops: expected $expect"
-               echo "but got $actual"
-               false
-        fi
+       maybe_remove_timestamp "$pretty_content" $no_ts >expect &&
+       maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual &&
+       test_cmp expect actual
     '
 
     test -z "$content" ||
     test_expect_success "--batch output of $type is correct" '
-       expect="$(maybe_remove_timestamp "$batch_output" $no_ts)"
-       actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts)"
-        if test "z$expect" = "z$actual"
-       then
-               : happy
-       else
-               echo "Oops: expected $expect"
-               echo "but got $actual"
-               false
-        fi
+       maybe_remove_timestamp "$batch_output" $no_ts >expect &&
+       maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual &&
+       test_cmp expect actual
     '
 
     test_expect_success "--batch-check output of $type is correct" '
-       expect="$sha1 $type $size"
-       actual="$(echo_without_newline $sha1 | git cat-file --batch-check)"
-        if test "z$expect" = "z$actual"
-       then
-               : happy
-       else
-               echo "Oops: expected $expect"
-               echo "but got $actual"
-               false
-        fi
+       echo "$sha1 $type $size" >expect &&
+       echo_without_newline $sha1 | git cat-file --batch-check >actual &&
+       test_cmp expect actual
+    '
+
+    test_expect_success "custom --batch-check format" '
+       echo "$type $sha1" >expect &&
+       echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual &&
+       test_cmp expect actual
     '
 }
 
@@ -134,15 +115,12 @@ tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 tag_description="This is a tag"
 tag_content="$tag_header_without_timestamp 0000000000 +0000
 
-$tag_description"
-tag_pretty_content="$tag_header_without_timestamp Thu Jan 1 00:00:00 1970 +0000
-
 $tag_description"
 
 tag_sha1=$(echo_without_newline "$tag_content" | git mktag)
 tag_size=$(strlen "$tag_content")
 
-run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_pretty_content" 1
+run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1
 
 test_expect_success \
     "Reach a blob from a tag pointing to it" \
index 5c0053a20bf2fbf1dd466ace5dc933e38ce3544d..0c74beedd22600e8dfe852393a5a9a36350cfeb5 100755 (executable)
@@ -250,4 +250,28 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'checkout without --ignore-skip-worktree-bits' '
+       echo "*" >.git/info/sparse-checkout &&
+       git checkout -f top &&
+       test_path_is_file init.t &&
+       echo sub >.git/info/sparse-checkout &&
+       git checkout &&
+       echo modified >> sub/added &&
+       git checkout . &&
+       test_path_is_missing init.t &&
+       git diff --exit-code HEAD
+'
+
+test_expect_success 'checkout with --ignore-skip-worktree-bits' '
+       echo "*" >.git/info/sparse-checkout &&
+       git checkout -f top &&
+       test_path_is_file init.t &&
+       echo sub >.git/info/sparse-checkout &&
+       git checkout &&
+       echo modified >> sub/added &&
+       git checkout --ignore-skip-worktree-bits . &&
+       test_path_is_file init.t &&
+       git diff --exit-code HEAD
+'
+
 test_done
diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh
new file mode 100755 (executable)
index 0000000..fdc257e
--- /dev/null
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='support for reading config from a blob'
+. ./test-lib.sh
+
+test_expect_success 'create config blob' '
+       cat >config <<-\EOF &&
+       [some]
+               value = 1
+       EOF
+       git add config &&
+       git commit -m foo
+'
+
+test_expect_success 'list config blob contents' '
+       echo some.value=1 >expect &&
+       git config --blob=HEAD:config --list >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fetch value from blob' '
+       echo true >expect &&
+       git config --blob=HEAD:config --bool some.value >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'reading non-existing value from blob is an error' '
+       test_must_fail git config --blob=HEAD:config non.existing
+'
+
+test_expect_success 'reading from blob and file is an error' '
+       test_must_fail git config --blob=HEAD:config --system --list
+'
+
+test_expect_success 'reading from missing ref is an error' '
+       test_must_fail git config --blob=HEAD:doesnotexist --list
+'
+
+test_expect_success 'reading from non-blob is an error' '
+       test_must_fail git config --blob=HEAD --list
+'
+
+test_expect_success 'setting a value in a blob is an error' '
+       test_must_fail git config --blob=HEAD:config some.value foo
+'
+
+test_expect_success 'deleting a value in a blob is an error' '
+       test_must_fail git config --blob=HEAD:config --unset some.value
+'
+
+test_expect_success 'editing a blob is an error' '
+       test_must_fail git config --blob=HEAD:config --edit
+'
+
+test_expect_success 'parse errors in blobs are properly attributed' '
+       cat >config <<-\EOF &&
+       [some]
+               value = "
+       EOF
+       git add config &&
+       git commit -m broken &&
+
+       test_must_fail git config --blob=HEAD:config some.value 2>err &&
+
+       # just grep for our token as the exact error message is likely to
+       # change or be internationalized
+       grep "HEAD:config" err
+'
+
+test_done
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
new file mode 100755 (executable)
index 0000000..3e500ed
--- /dev/null
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+test_description='show-ref'
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_commit A &&
+       git tag -f -a -m "annotated A" A &&
+       git checkout -b side &&
+       test_commit B &&
+       git tag -f -a -m "annotated B" B &&
+       git checkout master &&
+       test_commit C &&
+       git branch B A^0
+'
+
+test_expect_success 'show-ref' '
+       echo $(git rev-parse refs/tags/A) refs/tags/A >expect &&
+
+       git show-ref A >actual &&
+       test_cmp expect actual &&
+
+       git show-ref tags/A >actual &&
+       test_cmp expect actual &&
+
+       git show-ref refs/tags/A >actual &&
+       test_cmp expect actual &&
+
+       >expect &&
+
+       test_must_fail git show-ref D >actual
+       test_cmp expect actual
+'
+
+test_expect_success 'show-ref -q' '
+       >expect &&
+
+       git show-ref -q A >actual &&
+       test_cmp expect actual &&
+
+       git show-ref -q tags/A >actual &&
+       test_cmp expect actual &&
+
+       git show-ref -q refs/tags/A >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail git show-ref -q D >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'show-ref --verify' '
+       echo $(git rev-parse refs/tags/A) refs/tags/A >expect &&
+
+       git show-ref --verify refs/tags/A >actual &&
+       test_cmp expect actual &&
+
+       >expect &&
+
+       test_must_fail git show-ref --verify A >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail git show-ref --verify tags/A >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail git show-ref --verify D >actual
+       test_cmp expect actual
+'
+
+test_expect_success 'show-ref --verify -q' '
+       >expect &&
+
+       git show-ref --verify -q refs/tags/A >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail git show-ref --verify -q A >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail git show-ref --verify -q tags/A >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail git show-ref --verify -q D >actual
+       test_cmp expect actual
+'
+
+test_expect_success 'show-ref -d' '
+       {
+               echo $(git rev-parse refs/tags/A) refs/tags/A &&
+               echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}"
+               echo $(git rev-parse refs/tags/C) refs/tags/C
+       } >expect &&
+       git show-ref -d A C >actual &&
+       test_cmp expect actual &&
+
+       git show-ref -d tags/A tags/C >actual &&
+       test_cmp expect actual &&
+
+       git show-ref -d refs/tags/A refs/tags/C >actual &&
+       test_cmp expect actual &&
+
+       echo $(git rev-parse refs/heads/master) refs/heads/master >expect &&
+       git show-ref -d master >actual &&
+       test_cmp expect actual &&
+
+       git show-ref -d heads/master >actual &&
+       test_cmp expect actual &&
+
+       git show-ref -d refs/heads/master >actual &&
+       test_cmp expect actual
+
+       git show-ref -d --verify refs/heads/master >actual &&
+       test_cmp expect actual
+
+       >expect &&
+
+       test_must_fail git show-ref -d --verify master >actual &&
+       test_cmp expect actual &&
+
+       test_must_fail git show-ref -d --verify heads/master >actual &&
+       test_cmp expect actual
+
+'
+
+test_expect_success 'show-ref --heads, --tags, --head, pattern' '
+       for branch in B master side
+       do
+               echo $(git rev-parse refs/heads/$branch) refs/heads/$branch
+       done >expect.heads &&
+       git show-ref --heads >actual &&
+       test_cmp expect.heads actual &&
+
+       for tag in A B C
+       do
+               echo $(git rev-parse refs/tags/$tag) refs/tags/$tag
+       done >expect.tags &&
+       git show-ref --tags >actual &&
+       test_cmp expect.tags actual &&
+
+       cat expect.heads expect.tags >expect &&
+       git show-ref --heads --tags >actual &&
+       test_cmp expect actual &&
+
+       {
+               echo $(git rev-parse HEAD) HEAD &&
+               cat expect.heads expect.tags
+       } >expect &&
+       git show-ref --heads --tags --head >actual &&
+       test_cmp expect actual &&
+
+       {
+               echo $(git rev-parse HEAD) HEAD &&
+               echo $(git rev-parse refs/heads/B) refs/heads/B
+               echo $(git rev-parse refs/tags/B) refs/tags/B
+       } >expect &&
+       git show-ref --head B >actual &&
+       test_cmp expect actual &&
+
+       {
+               echo $(git rev-parse HEAD) HEAD &&
+               echo $(git rev-parse refs/heads/B) refs/heads/B
+               echo $(git rev-parse refs/tags/B) refs/tags/B
+               echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}"
+       } >expect &&
+       git show-ref --head -d B >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 9a105fe21f0da2851fc0f2aae4ff10e2417c9842..6f47c0dd0ec9b5e59205ca98eea9bd729605e004 100755 (executable)
@@ -144,4 +144,26 @@ test_expect_success 'empty reflog file' '
        test_cmp expect actual
 '
 
+# This guards against the alternative of showing the diffs vs. the
+# reflog ancestor.  The reflog used is designed to list the commits
+# more than once, so as to exercise the corresponding logic.
+test_expect_success 'git log -g -p shows diffs vs. parents' '
+       test_commit two &&
+       git branch flipflop &&
+       git update-ref refs/heads/flipflop -m flip1 HEAD^ &&
+       git update-ref refs/heads/flipflop -m flop1 HEAD &&
+       git update-ref refs/heads/flipflop -m flip2 HEAD^ &&
+       git log -g -p flipflop >reflog &&
+       grep -v ^Reflog reflog >actual &&
+       git log -1 -p HEAD^ >log.one &&
+       git log -1 -p HEAD >log.two &&
+       (
+               cat log.one; echo
+               cat log.two; echo
+               cat log.one; echo
+               cat log.two
+       ) >expect &&
+       test_cmp expect actual
+'
+
 test_done
index b27a7209f7401706f3b0a37b495fc0cb3a07c29e..2a19e797eb7cffba28deb4e99b3708c5b26602ae 100755 (executable)
@@ -129,8 +129,7 @@ test_expect_success 'branch@{u} works when tracking a local branch' '
 
 test_expect_success 'branch@{u} error message when no upstream' '
        cat >expect <<-EOF &&
-       error: No upstream configured for branch ${sq}non-tracking${sq}
-       fatal: Needed a single revision
+       fatal: No upstream configured for branch ${sq}non-tracking${sq}
        EOF
        error_message non-tracking@{u} 2>actual &&
        test_i18ncmp expect actual
@@ -138,8 +137,7 @@ test_expect_success 'branch@{u} error message when no upstream' '
 
 test_expect_success '@{u} error message when no upstream' '
        cat >expect <<-EOF &&
-       error: No upstream configured for branch ${sq}master${sq}
-       fatal: Needed a single revision
+       fatal: No upstream configured for branch ${sq}master${sq}
        EOF
        test_must_fail git rev-parse --verify @{u} 2>actual &&
        test_i18ncmp expect actual
@@ -147,8 +145,7 @@ test_expect_success '@{u} error message when no upstream' '
 
 test_expect_success 'branch@{u} error message with misspelt branch' '
        cat >expect <<-EOF &&
-       error: No such branch: ${sq}no-such-branch${sq}
-       fatal: Needed a single revision
+       fatal: No such branch: ${sq}no-such-branch${sq}
        EOF
        error_message no-such-branch@{u} 2>actual &&
        test_i18ncmp expect actual
@@ -156,8 +153,7 @@ test_expect_success 'branch@{u} error message with misspelt branch' '
 
 test_expect_success '@{u} error message when not on a branch' '
        cat >expect <<-EOF &&
-       error: HEAD does not point to a branch
-       fatal: Needed a single revision
+       fatal: HEAD does not point to a branch
        EOF
        git checkout HEAD^0 &&
        test_must_fail git rev-parse --verify @{u} 2>actual &&
@@ -166,8 +162,7 @@ test_expect_success '@{u} error message when not on a branch' '
 
 test_expect_success 'branch@{u} error message if upstream branch not fetched' '
        cat >expect <<-EOF &&
-       error: Upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
-       fatal: Needed a single revision
+       fatal: Upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch
        EOF
        error_message bad-upstream@{u} 2>actual &&
        test_i18ncmp expect actual
index d5d6244178137cddc985ad4fc32ee3e78c7c5746..e5aea3b89602fdaf5ed3a9a230a604dd3f48cda0 100755 (executable)
@@ -4,17 +4,24 @@ test_description='test various @{X} syntax combinations together'
 . ./test-lib.sh
 
 check() {
-test_expect_${3:-success} "$1 = $2" "
-       echo '$2' >expect &&
-       git log -1 --format=%s '$1' >actual &&
-       test_cmp expect actual
-"
+       test_expect_${4:-success} "$1 = $3" "
+               echo '$3' >expect &&
+               if test '$2' = 'commit'
+               then
+                       git log -1 --format=%s '$1' >actual
+               else
+                       git rev-parse --symbolic-full-name '$1' >actual
+               fi &&
+               test_cmp expect actual
+       "
 }
+
 nonsense() {
-test_expect_${2:-success} "$1 is nonsensical" "
-       test_must_fail git log -1 '$1'
-"
+       test_expect_${2:-success} "$1 is nonsensical" "
+               test_must_fail git rev-parse --verify '$1'
+       "
 }
+
 fail() {
        "$@" failure
 }
@@ -31,21 +38,40 @@ test_expect_success 'setup' '
        git checkout -b new-branch &&
        test_commit new-one &&
        test_commit new-two &&
-       git config branch.old-branch.remote . &&
-       git config branch.old-branch.merge refs/heads/master &&
-       git config branch.new-branch.remote . &&
-       git config branch.new-branch.merge refs/heads/upstream-branch
+       git branch -u master old-branch &&
+       git branch -u upstream-branch new-branch
 '
 
-check HEAD new-two
-check "@{1}" new-one
-check "@{-1}" old-two
-check "@{-1}@{1}" old-one
-check "@{u}" upstream-two
-check "@{u}@{1}" upstream-one
-check "@{-1}@{u}" master-two
-check "@{-1}@{u}@{1}" master-one
+check HEAD ref refs/heads/new-branch
+check "@{1}" commit new-one
+check "HEAD@{1}" commit new-one
+check "@{now}" commit new-two
+check "HEAD@{now}" commit new-two
+check "@{-1}" ref refs/heads/old-branch
+check "@{-1}@{0}" commit old-two
+check "@{-1}@{1}" commit old-one
+check "@{u}" ref refs/heads/upstream-branch
+check "HEAD@{u}" ref refs/heads/upstream-branch
+check "@{u}@{1}" commit upstream-one
+check "@{-1}@{u}" ref refs/heads/master
+check "@{-1}@{u}@{1}" commit master-one
 nonsense "@{u}@{-1}"
+nonsense "@{0}@{0}"
 nonsense "@{1}@{u}"
+nonsense "HEAD@{-1}"
+nonsense "@{-1}@{-1}"
+
+# @{N} versus HEAD@{N}
+
+check "HEAD@{3}" commit old-two
+nonsense "@{3}"
+
+test_expect_success 'switch to old-branch' '
+       git checkout old-branch
+'
+
+check HEAD ref refs/heads/old-branch
+check "HEAD@{1}" commit new-two
+check "@{1}" commit old-one
 
 test_done
index 6b3d797ceabad069aefd1337ffe366b86b523412..4a155c8d095fb8ca2c2f88802d3501e01d07ce28 100755 (executable)
@@ -77,6 +77,7 @@ test_expect_success 'disambiguate blob' '
 
 test_expect_success 'disambiguate tree' '
        commit=$(echo "d7xm" | git commit-tree 000000000) &&
+       # this commit is fffff2e and not ambiguous with the 00000* objects
        test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc)
 '
 
@@ -99,10 +100,14 @@ test_expect_success 'disambiguate commit-ish' '
 
 test_expect_success 'disambiguate commit' '
        commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) &&
+       # this commit is ffffffd8 and not ambiguous with the 00000* objects
        test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f)
 '
 
 test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
+       # These are underspecified from the prefix-length point of view
+       # to disambiguate the commit with other objects, but there is only
+       # one commit that has 00000* prefix at this point.
        git log 000000000..000000000 &&
        git log ..000000000 &&
        git log 000000000.. &&
@@ -112,16 +117,19 @@ test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
 '
 
 test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' '
+       # Likewise.
        git rev-parse 000000000..000000000 &&
        git rev-parse ..000000000 &&
        git rev-parse 000000000..
 '
 
 test_expect_success 'git log takes only commit-ish' '
+       # Likewise.
        git log 000000000
 '
 
 test_expect_success 'git reset takes only commit-ish' '
+       # Likewise.
        git reset 000000000
 '
 
@@ -131,26 +139,30 @@ test_expect_success 'first tag' '
 '
 
 test_expect_failure 'two semi-ambiguous commit-ish' '
+       # At this point, we have a tag 0000000000f8f that points
+       # at a commit 0000000000e4f, and a tree and a blob that
+       # share 0000000000 prefix with these tag and commit.
+       #
        # Once the parser becomes ultra-smart, it could notice that
-       # 110282 before ^{commit} name many different objects, but
+       # 0000000000 before ^{commit} name many different objects, but
        # that only two (HEAD and v1.0.0 tag) can be peeled to commit,
        # and that peeling them down to commit yield the same commit
        # without ambiguity.
-       git rev-parse --verify 110282^{commit} &&
+       git rev-parse --verify 0000000000^{commit} &&
 
        # likewise
-       git log 000000000..000000000 &&
-       git log ..000000000 &&
-       git log 000000000.. &&
-       git log 000000000...000000000 &&
-       git log ...000000000 &&
-       git log 000000000...
+       git log 0000000000..0000000000 &&
+       git log ..0000000000 &&
+       git log 0000000000.. &&
+       git log 0000000000...0000000000 &&
+       git log ...0000000000 &&
+       git log 0000000000...
 '
 
 test_expect_failure 'three semi-ambiguous tree-ish' '
        # Likewise for tree-ish.  HEAD, v1.0.0 and HEAD^{tree} share
        # the prefix but peeling them to tree yields the same thing
-       git rev-parse --verify 000000000^{tree}
+       git rev-parse --verify 0000000000^{tree}
 '
 
 test_expect_success 'parse describe name' '
@@ -241,7 +253,7 @@ test_expect_success 'ambiguous commit-ish' '
        # Now there are many commits that begin with the
        # common prefix, none of these should pick one at
        # random.  They all should result in ambiguity errors.
-       test_must_fail git rev-parse --verify 110282^{commit} &&
+       test_must_fail git rev-parse --verify 00000000^{commit} &&
 
        # likewise
        test_must_fail git log 000000000..000000000 &&
@@ -261,4 +273,22 @@ test_expect_success 'rev-parse --disambiguate' '
        test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
 '
 
+test_expect_success 'ambiguous 40-hex ref' '
+       TREE=$(git mktree </dev/null) &&
+       REF=`git rev-parse HEAD` &&
+       VAL=$(git commit-tree $TREE </dev/null) &&
+       git update-ref refs/heads/$REF $VAL &&
+       test `git rev-parse $REF 2>err` = $REF &&
+       grep "refname.*${REF}.*ambiguous" err
+'
+
+test_expect_success 'ambiguous short sha1 ref' '
+       TREE=$(git mktree </dev/null) &&
+       REF=`git rev-parse --short HEAD` &&
+       VAL=$(git commit-tree $TREE </dev/null) &&
+       git update-ref refs/heads/$REF $VAL &&
+       test `git rev-parse $REF 2>err` = $VAL &&
+       grep "refname.*${REF}.*ambiguous" err
+'
+
 test_done
diff --git a/t/t1513-rev-parse-prefix.sh b/t/t1513-rev-parse-prefix.sh
new file mode 100755 (executable)
index 0000000..87ec3ae
--- /dev/null
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='Tests for rev-parse --prefix'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir -p sub1/sub2 &&
+       echo top >top &&
+       echo file1 >sub1/file1 &&
+       echo file2 >sub1/sub2/file2 &&
+       git add top sub1/file1 sub1/sub2/file2 &&
+       git commit -m commit
+'
+
+test_expect_success 'empty prefix -- file' '
+       git rev-parse --prefix "" -- top sub1/file1 >actual &&
+       cat <<-\EOF >expected &&
+       --
+       top
+       sub1/file1
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- file' '
+       git rev-parse --prefix sub1/ -- file1 sub2/file2 >actual &&
+       cat <<-\EOF >expected &&
+       --
+       sub1/file1
+       sub1/sub2/file2
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- ../file' '
+       git rev-parse --prefix sub1/ -- ../top sub2/file2 >actual &&
+       cat <<-\EOF >expected &&
+       --
+       sub1/../top
+       sub1/sub2/file2
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'empty prefix HEAD:./path' '
+       git rev-parse --prefix "" HEAD:./top >actual &&
+       git rev-parse HEAD:top >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:./path' '
+       git rev-parse --prefix sub1/ HEAD:./file1 >actual &&
+       git rev-parse HEAD:sub1/file1 >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:../path' '
+       git rev-parse --prefix sub1/ HEAD:../top >actual &&
+       git rev-parse HEAD:top >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'prefix ignored with HEAD:top' '
+       git rev-parse --prefix sub1/ HEAD:top >actual &&
+       git rev-parse HEAD:top >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'disambiguate path with valid prefix' '
+       git rev-parse --prefix sub1/ file1 >actual &&
+       cat <<-\EOF >expected &&
+       sub1/file1
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'file and refs with prefix' '
+       git rev-parse --prefix sub1/ master file1 >actual &&
+       cat <<-EOF >expected &&
+       $(git rev-parse master)
+       sub1/file1
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'two-levels deep' '
+       git rev-parse --prefix sub1/sub2/ -- file2 >actual &&
+       cat <<-\EOF >expected &&
+       --
+       sub1/sub2/file2
+       EOF
+       test_cmp expected actual
+'
+
+test_done
index 98aa73e8239355eba098253edfd156d4ea254be2..1fc8e634b737db766dcaf195af9cb8a5d14ef04f 100755 (executable)
@@ -59,10 +59,9 @@ test_expect_success \
     'git read-tree -m $tree1 && git checkout-index -f -a'
 test_debug 'show_files $tree1'
 
-test_expect_success SYMLINKS \
-    'git update-index --add a symlink.' \
-    'ln -s path0 path1 &&
-     git update-index --add path1'
+test_expect_success \
+    'add a symlink' \
+    'test_ln_s_add path0 path1'
 test_expect_success \
     'writing tree out with git write-tree' \
     'tree3=$(git write-tree)'
index 0f4b2896af8b73edcd5bd60631405a430803a40f..f171a5578b6b81e4d7cb03ef0b6b9272ef610ffc 100755 (executable)
@@ -194,11 +194,10 @@ test_expect_success \
  test $(cat ../$s1) = tree1asubdir/path5)
 )'
 
-test_expect_success SYMLINKS \
+test_expect_success \
 'checkout --temp symlink' '
 rm -f path* .merge_* out .git/index &&
-ln -s b a &&
-git update-index --add a &&
+test_ln_s_add b a &&
 t4=$(git write-tree) &&
 rm -f .git/index &&
 git read-tree $t4 &&
index e6f59f1914667f0001fe990656a66bb76e14a41d..fc9aad530e9f9078e95d99da5c1d0849e489d4cf 100755 (executable)
@@ -6,7 +6,7 @@ test_description='git checkout to switch between branches with symlink<->dir'
 
 . ./test-lib.sh
 
-test_expect_success SYMLINKS setup '
+test_expect_success setup '
 
        mkdir frotz &&
        echo hello >frotz/filfre &&
@@ -25,25 +25,25 @@ test_expect_success SYMLINKS setup '
 
        git rm --cached frotz/filfre &&
        mv frotz xyzzy &&
-       ln -s xyzzy frotz &&
-       git add xyzzy/filfre frotz &&
+       test_ln_s_add xyzzy frotz &&
+       git add xyzzy/filfre &&
        test_tick &&
        git commit -m "side moves frotz/ to xyzzy/ and adds frotz->xyzzy/"
 
 '
 
-test_expect_success SYMLINKS 'switch from symlink to dir' '
+test_expect_success 'switch from symlink to dir' '
 
        git checkout master
 
 '
 
-test_expect_success SYMLINKS 'Remove temporary directories & switch to master' '
+test_expect_success 'Remove temporary directories & switch to master' '
        rm -fr frotz xyzzy nitfol &&
        git checkout -f master
 '
 
-test_expect_success SYMLINKS 'switch from dir to symlink' '
+test_expect_success 'switch from dir to symlink' '
 
        git checkout side
 
index b44de9dc623fc923941b61719f445170602e21be..e7ba8c505f579ab5cd53ee713eaf6ca894e471a1 100755 (executable)
@@ -116,4 +116,38 @@ test_expect_success 'master...' '
        test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
 '
 
+test_expect_success '"checkout -" works after a rebase A' '
+       git checkout master &&
+       git checkout other &&
+       git rebase master &&
+       git checkout - &&
+       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase A B' '
+       git branch moodle master~1 &&
+       git checkout master &&
+       git checkout other &&
+       git rebase master moodle &&
+       git checkout - &&
+       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A' '
+       git checkout master &&
+       git checkout other &&
+       git rebase -i master &&
+       git checkout - &&
+       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A B' '
+       git branch foodle master~1 &&
+       git checkout master &&
+       git checkout other &&
+       git rebase master foodle &&
+       git checkout - &&
+       test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
 test_done
index 5da63e9fa267af4517024307f4bdeef73caeccf2..c2ada7de37312bffd4b05ff7ac7a9c9e0c4da395 100755 (executable)
@@ -29,21 +29,25 @@ test_expect_success 'checkout commit with dir must not remove untracked a/b' '
        test -f a/b
 '
 
-test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' '
+test_expect_success 'create a commit where dir a/b changed to symlink' '
 
        rm -rf a/b &&   # cleanup if previous test failed
        git checkout -f -b symlink start &&
        rm -rf a/b &&
-       ln -s foo a/b &&
        git add -A &&
+       test_ln_s_add foo a/b &&
        git commit -m "dir to symlink"
 '
 
-test_expect_success SYMLINKS 'checkout commit with dir must not remove untracked a/b' '
+test_expect_success 'checkout commit with dir must not remove untracked a/b' '
 
        git rm --cached a/b &&
        git commit -m "un-track the symlink" &&
-       test_must_fail git checkout start &&
+       test_must_fail git checkout start
+'
+
+test_expect_success SYMLINKS 'the symlink remained' '
+
        test -h a/b
 '
 
index c317254b9a82befd3400d300f85ecfd661d1a948..9bf2bdffd24b773a2eec636efdd534269f45e74b 100755 (executable)
@@ -96,11 +96,10 @@ test_expect_success 'non-limited update in subdir leaves root alone' '
        test_cmp expect actual
 '
 
-test_expect_success SYMLINKS 'replace a file with a symlink' '
+test_expect_success 'replace a file with a symlink' '
 
        rm foo &&
-       ln -s top foo &&
-       git add -u -- foo
+       test_ln_s_add top foo
 
 '
 
@@ -166,9 +165,9 @@ test_expect_success 'add -u resolves unmerged paths' '
        echo 2 >path3 &&
        echo 2 >path5 &&
 
-       # Explicit resolving by adding removed paths should fail
-       test_must_fail git add path4 &&
-       test_must_fail git add path6 &&
+       # Fail to explicitly resolve removed paths with "git add"
+       test_must_fail git add --no-all path4 &&
+       test_must_fail git add --no-all path6 &&
 
        # "add -u" should notice removals no matter what stages
        # the index entries are in.
index 6a8151064c8256bd03a90460eb1bd6970428f2f0..fc8b59e7f7796c075b7fd03145e5f9c69625403b 100755 (executable)
@@ -41,4 +41,14 @@ test_expect_success 'git add --all' '
        test_cmp expect actual
 '
 
+test_expect_success 'Just "git add" is a no-op' '
+       git reset --hard &&
+       echo >will-remove &&
+       >will-not-be-added &&
+       git add &&
+       git diff-index --name-status --cached HEAD >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
 test_done
index efb7ebc91f2f46b38e0c02f4dd11e2224678baea..f0421c09c700bc1203d2ef563c54157e94c83b33 100755 (executable)
@@ -103,7 +103,7 @@ test_expect_success \
      test_cmp expect output'
 
 test_expect_success 'restore gitignore' '
-       git checkout $allignores &&
+       git checkout --ignore-skip-worktree-bits $allignores &&
        rm .git/index
 '
 
@@ -175,6 +175,24 @@ test_expect_success 'negated exclude matches can override previous ones' '
        grep "^a.1" output
 '
 
+test_expect_success 'excluded directory overrides content patterns' '
+
+       git ls-files --others --exclude="one" --exclude="!one/a.1" >output &&
+       if grep "^one/a.1" output
+       then
+               false
+       fi
+'
+
+test_expect_success 'negated directory doesn'\''t affect content patterns' '
+
+       git ls-files --others --exclude="!one" --exclude="one/a.1" >output &&
+       if grep "^one/a.1" output
+       then
+               false
+       fi
+'
+
 test_expect_success 'subdirectory ignore (setup)' '
        mkdir -p top/l1/l2 &&
        (
@@ -214,6 +232,55 @@ test_expect_success 'subdirectory ignore (l1)' '
        test_cmp expect actual
 '
 
+test_expect_success 'show/hide empty ignored directory (setup)' '
+       rm top/l1/l2/l1 &&
+       rm top/l1/.gitignore
+'
+
+test_expect_success 'show empty ignored directory with --directory' '
+       (
+               cd top &&
+               git ls-files -o -i --exclude l1 --directory
+       ) >actual &&
+       echo l1/ >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'hide empty ignored directory with --no-empty-directory' '
+       (
+               cd top &&
+               git ls-files -o -i --exclude l1 --directory --no-empty-directory
+       ) >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'show/hide empty ignored sub-directory (setup)' '
+       > top/l1/tracked &&
+       (
+               cd top &&
+               git add -f l1/tracked
+       )
+'
+
+test_expect_success 'show empty ignored sub-directory with --directory' '
+       (
+               cd top &&
+               git ls-files -o -i --exclude l1 --directory
+       ) >actual &&
+       echo l1/l2/ >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'hide empty ignored sub-directory with --no-empty-directory' '
+       (
+               cd top &&
+               git ls-files -o -i --exclude l1 --directory --no-empty-directory
+       ) >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'pattern matches prefix completely' '
        : >expect &&
        git ls-files -i -o --exclude "/three/a.3[abc]" >actual &&
index 95671c205364a12bea02173b33d0d427d5c546fe..f611d799b690457e306571adf16813b238a27dc9 100755 (executable)
@@ -11,6 +11,8 @@ This test prepares the following in the cache:
     path1       - a symlink
     path2/file2 - a file in a directory
     path3/file3 - a file in a directory
+    submod1/   - a submodule
+    submod2/   - another submodule
 
 and the following on the filesystem:
 
@@ -21,9 +23,11 @@ and the following on the filesystem:
     path4      - a file
     path5      - a symlink
     path6/file6 - a file in a directory
+    submod1/   - a submodule (modified from the cache)
+    submod2/   - a submodule (matches the cache)
 
-git ls-files -k should report that existing filesystem
-objects except path4, path5 and path6/file6 to be killed.
+git ls-files -k should report that existing filesystem objects
+path0/*, path1/*, path2 and path3 to be killed.
 
 Also for modification test, the cache and working tree have:
 
@@ -33,75 +37,82 @@ Also for modification test, the cache and working tree have:
     path10     - a non-empty file, cache dirtied.
 
 We should report path0, path1, path2/file2, path3/file3, path7 and path8
-modified without reporting path9 and path10.
+modified without reporting path9 and path10.  submod1 is also modified.
 '
 . ./test-lib.sh
 
-date >path0
-if test_have_prereq SYMLINKS
-then
-       ln -s xyzzy path1
-else
-       date > path1
-fi
-mkdir path2 path3
-date >path2/file2
-date >path3/file3
-: >path7
-date >path8
-: >path9
-date >path10
-test_expect_success \
-    'git update-index --add to add various paths.' \
-    "git update-index --add -- path0 path1 path?/file? path7 path8 path9 path10"
-
-rm -fr path? ;# leave path10 alone
-date >path2
-if test_have_prereq SYMLINKS
-then
-       ln -s frotz path3
-       ln -s nitfol path5
-else
-       date > path3
-       date > path5
-fi
-mkdir path0 path1 path6
-date >path0/file0
-date >path1/file1
-date >path6/file6
-date >path7
-: >path8
-: >path9
-touch path10
+test_expect_success 'git update-index --add to add various paths.' '
+       date >path0 &&
+       test_ln_s_add xyzzy path1 &&
+       mkdir path2 path3 &&
+       date >path2/file2 &&
+       date >path3/file3 &&
+       : >path7 &&
+       date >path8 &&
+       : >path9 &&
+       date >path10 &&
+       git update-index --add -- path0 path?/file? path7 path8 path9 path10 &&
+       for i in 1 2
+       do
+               git init submod$i &&
+               (
+                       cd submod$i && git commit --allow-empty -m "empty $i"
+               ) || break
+       done &&
+       git update-index --add submod[12]
+       (
+               cd submod1 &&
+               git commit --allow-empty -m "empty 1 (updated)"
+       ) &&
+       rm -fr path?    # leave path10 alone
+'
 
-test_expect_success \
-    'git ls-files -k to show killed files.' \
-    'git ls-files -k >.output'
-cat >.expected <<EOF
-path0/file0
-path1/file1
-path2
-path3
-EOF
+test_expect_success 'git ls-files -k to show killed files.' '
+       date >path2 &&
+       if test_have_prereq SYMLINKS
+       then
+               ln -s frotz path3 &&
+               ln -s nitfol path5
+       else
+               date >path3 &&
+               date >path5
+       fi &&
+       mkdir path0 path1 path6 &&
+       date >path0/file0 &&
+       date >path1/file1 &&
+       date >path6/file6 &&
+       date >path7 &&
+       : >path8 &&
+       : >path9 &&
+       touch path10 &&
+       git ls-files -k >.output
+'
 
-test_expect_success \
-    'validate git ls-files -k output.' \
-    'test_cmp .expected .output'
+test_expect_success 'validate git ls-files -k output.' '
+       cat >.expected <<-\EOF &&
+       path0/file0
+       path1/file1
+       path2
+       path3
+       EOF
+       test_cmp .expected .output
+'
 
-test_expect_success \
-    'git ls-files -m to show modified files.' \
-    'git ls-files -m >.output'
-cat >.expected <<EOF
-path0
-path1
-path2/file2
-path3/file3
-path7
-path8
-EOF
+test_expect_success 'git ls-files -m to show modified files.' '
+       git ls-files -m >.output
+'
 
-test_expect_success \
-    'validate git ls-files -m output.' \
-    'test_cmp .expected .output'
+test_expect_success 'validate git ls-files -m output.' '
+       cat >.expected <<-\EOF &&
+       path0
+       path1
+       path2/file2
+       path3/file3
+       path7
+       path8
+       submod1
+       EOF
+       test_cmp .expected .output
+'
 
 test_done
index a5e3da7e419e6f13ea0960722b4c1c712a995112..2f96100a5f655bbea859bf596ff9835c9abb11a2 100755 (executable)
@@ -25,10 +25,7 @@ test_expect_success 'setup 1' '
        git branch submod &&
        git branch copy &&
        git branch rename &&
-       if test_have_prereq SYMLINKS
-       then
-               git branch rename-ln
-       fi &&
+       git branch rename-ln &&
 
        echo hello >>a &&
        cp a d/e &&
@@ -260,16 +257,12 @@ test_expect_success 'setup 8' '
        git add e &&
        test_tick &&
        git commit -m "rename a->e" &&
-       if test_have_prereq SYMLINKS
-       then
-               git checkout rename-ln &&
-               git mv a e &&
-               ln -s e a &&
-               git add a e &&
-               test_tick &&
-               git commit -m "rename a->e, symlink a->e" &&
-               oln=`printf e | git hash-object --stdin`
-       fi
+       git checkout rename-ln &&
+       git mv a e &&
+       test_ln_s_add e a &&
+       test_tick &&
+       git commit -m "rename a->e, symlink a->e" &&
+       oln=`printf e | git hash-object --stdin`
 '
 
 test_expect_success 'setup 9' '
@@ -569,28 +562,25 @@ test_expect_success 'merge-recursive copy vs. rename' '
        test_cmp expected actual
 '
 
-if test_have_prereq SYMLINKS
-then
-       test_expect_failure 'merge-recursive rename vs. rename/symlink' '
-
-               git checkout -f rename &&
-               git merge rename-ln &&
-               ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
-               (
-                       echo "120000 blob $oln  a"
-                       echo "100644 blob $o0   b"
-                       echo "100644 blob $o0   c"
-                       echo "100644 blob $o0   d/e"
-                       echo "100644 blob $o0   e"
-                       echo "120000 $oln 0     a"
-                       echo "100644 $o0 0      b"
-                       echo "100644 $o0 0      c"
-                       echo "100644 $o0 0      d/e"
-                       echo "100644 $o0 0      e"
-               ) >expected &&
-               test_cmp expected actual
-       '
-fi
+test_expect_failure 'merge-recursive rename vs. rename/symlink' '
+
+       git checkout -f rename &&
+       git merge rename-ln &&
+       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       (
+               echo "120000 blob $oln  a"
+               echo "100644 blob $o0   b"
+               echo "100644 blob $o0   c"
+               echo "100644 blob $o0   d/e"
+               echo "100644 blob $o0   e"
+               echo "120000 $oln 0     a"
+               echo "100644 $o0 0      b"
+               echo "100644 $o0 0      c"
+               echo "100644 $o0 0      d/e"
+               echo "100644 $o0 0      e"
+       ) >expected &&
+       test_cmp expected actual
+'
 
 
 test_done
index 2b17311cb0870ea210d9b5cbe167363d13641d67..5fd7bbb65244bdf6470508b6bc79aef58e79e86d 100755 (executable)
@@ -14,7 +14,7 @@ test_description='merge-recursive options
 . ./test-lib.sh
 
 test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b
-test_have_prereq MINGW && export GREP_OPTIONS=-U
+test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U
 
 test_expect_success 'setup' '
        conflict_hunks () {
index 4c37057ddf4a6796c88dba45f0123da1a44fb4af..38446a0e872e90fb008d6d42784a1892bab214e9 100755 (executable)
@@ -6,20 +6,20 @@ test_description='wildmatch tests'
 
 match() {
     if [ $1 = 1 ]; then
-       test_expect_success "wildmatch:    match '$3' '$4'" "
+       test_expect_success "wildmatch:     match '$3' '$4'" "
            test-wildmatch wildmatch '$3' '$4'
        "
     else
-       test_expect_success "wildmatch: no match '$3' '$4'" "
+       test_expect_success "wildmatch:  no match '$3' '$4'" "
            ! test-wildmatch wildmatch '$3' '$4'
        "
     fi
     if [ $2 = 1 ]; then
-       test_expect_success "fnmatch:      match '$3' '$4'" "
+       test_expect_success "fnmatch:       match '$3' '$4'" "
            test-wildmatch fnmatch '$3' '$4'
        "
     elif [ $2 = 0 ]; then
-       test_expect_success "fnmatch:   no match '$3' '$4'" "
+       test_expect_success "fnmatch:    no match '$3' '$4'" "
            ! test-wildmatch fnmatch '$3' '$4'
        "
 #    else
@@ -29,13 +29,25 @@ match() {
     fi
 }
 
+imatch() {
+    if [ $1 = 1 ]; then
+       test_expect_success "iwildmatch:    match '$2' '$3'" "
+           test-wildmatch iwildmatch '$2' '$3'
+       "
+    else
+       test_expect_success "iwildmatch: no match '$2' '$3'" "
+           ! test-wildmatch iwildmatch '$2' '$3'
+       "
+    fi
+}
+
 pathmatch() {
     if [ $1 = 1 ]; then
-       test_expect_success "pathmatch:    match '$2' '$3'" "
+       test_expect_success "pathmatch:     match '$2' '$3'" "
            test-wildmatch pathmatch '$2' '$3'
        "
     else
-       test_expect_success "pathmatch: no match '$2' '$3'" "
+       test_expect_success "pathmatch:  no match '$2' '$3'" "
            ! test-wildmatch pathmatch '$2' '$3'
        "
     fi
@@ -235,4 +247,35 @@ pathmatch 1 abcXdefXghi '*X*i'
 pathmatch 1 ab/cXd/efXg/hi '*/*X*/*/*i'
 pathmatch 1 ab/cXd/efXg/hi '*Xg*i'
 
+# Case-sensitivy features
+match 0 x 'a' '[A-Z]'
+match 1 x 'A' '[A-Z]'
+match 0 x 'A' '[a-z]'
+match 1 x 'a' '[a-z]'
+match 0 x 'a' '[[:upper:]]'
+match 1 x 'A' '[[:upper:]]'
+match 0 x 'A' '[[:lower:]]'
+match 1 x 'a' '[[:lower:]]'
+match 0 x 'A' '[B-Za]'
+match 1 x 'a' '[B-Za]'
+match 0 x 'A' '[B-a]'
+match 1 x 'a' '[B-a]'
+match 0 x 'z' '[Z-y]'
+match 1 x 'Z' '[Z-y]'
+
+imatch 1 'a' '[A-Z]'
+imatch 1 'A' '[A-Z]'
+imatch 1 'A' '[a-z]'
+imatch 1 'a' '[a-z]'
+imatch 1 'a' '[[:upper:]]'
+imatch 1 'A' '[[:upper:]]'
+imatch 1 'A' '[[:lower:]]'
+imatch 1 'a' '[[:lower:]]'
+imatch 1 'A' '[B-Za]'
+imatch 1 'a' '[B-Za]'
+imatch 1 'A' '[B-a]'
+imatch 1 'a' '[B-a]'
+imatch 1 'z' '[Z-y]'
+imatch 1 'Z' '[Z-y]'
+
 test_done
index 81d90b66c50e75323a44aaf5e2e067d5a3569e6a..eb73c06a4e1ee826684ee84efa134ee5517023a6 100755 (executable)
@@ -22,20 +22,8 @@ test_expect_success \
     'setup' \
     'mkdir path2 path2/baz &&
      echo Hi >path0 &&
-     if test_have_prereq SYMLINKS
-     then
-       ln -s path0 path1 &&
-       ln -s ../path1 path2/bazbo
-       make_expected () {
-               cat >expected
-       }
-     else
-       printf path0 > path1 &&
-       printf ../path1 > path2/bazbo
-       make_expected () {
-               sed -e "s/120000 /100644 /" >expected
-       }
-     fi &&
+     test_ln_s_add path0 path1 &&
+     test_ln_s_add ../path1 path2/bazbo &&
      echo Lo >path2/foo &&
      echo Mi >path2/baz/b &&
      find path? \( -type f -o -type l \) -print |
@@ -51,7 +39,7 @@ test_output () {
 test_expect_success \
     'ls-tree plain' \
     'git ls-tree $tree >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
 040000 tree X  path2
@@ -61,7 +49,7 @@ EOF
 test_expect_success \
     'ls-tree recursive' \
     'git ls-tree -r $tree >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
 100644 blob X  path2/baz/b
@@ -73,7 +61,7 @@ EOF
 test_expect_success \
     'ls-tree recursive with -t' \
     'git ls-tree -r -t $tree >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
 040000 tree X  path2
@@ -87,7 +75,7 @@ EOF
 test_expect_success \
     'ls-tree recursive with -d' \
     'git ls-tree -r -d $tree >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X  path2
 040000 tree X  path2/baz
 EOF
@@ -96,7 +84,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path' \
     'git ls-tree $tree path >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 EOF
      test_output'
 
@@ -106,7 +94,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path1 path0' \
     'git ls-tree $tree path1 path0 >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 100644 blob X  path0
 120000 blob X  path1
 EOF
@@ -115,7 +103,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path0/' \
     'git ls-tree $tree path0/ >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 EOF
      test_output'
 
@@ -124,7 +112,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path2' \
     'git ls-tree $tree path2 >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X  path2
 EOF
      test_output'
@@ -133,7 +121,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path2/' \
     'git ls-tree $tree path2/ >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X  path2/baz
 120000 blob X  path2/bazbo
 100644 blob X  path2/foo
@@ -145,7 +133,7 @@ EOF
 test_expect_success \
     'ls-tree filtered with path2/baz' \
     'git ls-tree $tree path2/baz >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X  path2/baz
 EOF
      test_output'
@@ -153,14 +141,14 @@ EOF
 test_expect_success \
     'ls-tree filtered with path2/bak' \
     'git ls-tree $tree path2/bak >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 EOF
      test_output'
 
 test_expect_success \
     'ls-tree -t filtered with path2/bak' \
     'git ls-tree -t $tree path2/bak >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X  path2
 EOF
      test_output'
@@ -168,7 +156,7 @@ EOF
 test_expect_success \
     'ls-tree with one path a prefix of the other' \
     'git ls-tree $tree path2/baz path2/bazbo >current &&
-     make_expected <<\EOF &&
+     cat >expected <<\EOF &&
 040000 tree X  path2/baz
 120000 blob X  path2/bazbo
 EOF
index cd04361df811d329e269ac1d5eb0b0f03baa74d8..1a2080e3dca272b6ed40739a7539f01f4f6ae65c 100755 (executable)
@@ -118,4 +118,37 @@ test_expect_success 'pack, prune and repack' '
        test_cmp all-of-them again
 '
 
+test_expect_success 'explicit pack-refs with dangling packed reference' '
+       git commit --allow-empty -m "soon to be garbage-collected" &&
+       git pack-refs --all &&
+       git reset --hard HEAD^ &&
+       git reflog expire --expire=all --all &&
+       git prune --expire=all &&
+       git pack-refs --all 2>result &&
+       test_cmp /dev/null result
+'
+
+test_expect_success 'delete ref with dangling packed version' '
+       git checkout -b lamb &&
+       git commit --allow-empty -m "future garbage" &&
+       git pack-refs --all &&
+       git reset --hard HEAD^ &&
+       git checkout master &&
+       git reflog expire --expire=all --all &&
+       git prune --expire=all &&
+       git branch -d lamb 2>result &&
+       test_cmp /dev/null result
+'
+
+test_expect_success 'delete ref while another dangling packed ref' '
+       git branch lamb &&
+       git commit --allow-empty -m "future garbage" &&
+       git pack-refs --all &&
+       git reset --hard HEAD^ &&
+       git reflog expire --expire=all --all &&
+       git prune --expire=all &&
+       git branch -d lamb 2>result &&
+       test_cmp /dev/null result
+'
+
 test_done
index d4d7792eae845096f8f89ef4d31171d55dac5659..3b7caca4212ea8ee99e8dba8955eab73f0e2f3df 100755 (executable)
@@ -61,4 +61,13 @@ test_expect_success 'refs are peeled outside of refs/tags (old packed)' '
        test_cmp expect actual
 '
 
+test_expect_success 'peeled refs survive deletion of packed ref' '
+       git pack-refs --all &&
+       cp .git/packed-refs fully-peeled &&
+       git branch yadda &&
+       git pack-refs --all &&
+       git branch -d yadda &&
+       test_cmp fully-peeled .git/packed-refs
+'
+
 test_done
index f6cc10265700730999174347704a0df6989c7f1b..ebf93b0695dfac37fc276e1f07377d858eb3e1da 100755 (executable)
@@ -40,13 +40,6 @@ test_expect_success 'prepare repository with topic branches' '
        echo Side >>C &&
        git add C &&
        git commit -m "Add C" &&
-       git checkout -b nonlinear my-topic-branch &&
-       echo Edit >>B &&
-       git add B &&
-       git commit -m "Modify B" &&
-       git merge side &&
-       git checkout -b upstream-merged-nonlinear &&
-       git merge master &&
        git checkout -f my-topic-branch &&
        git tag topic
 '
@@ -66,26 +59,15 @@ test_expect_success 'rebase against master' '
        git rebase master
 '
 
-test_expect_success 'rebase against master twice' '
-       git rebase master >out &&
-       test_i18ngrep "Current branch my-topic-branch is up to date" out
-'
-
-test_expect_success 'rebase against master twice with --force' '
-       git rebase --force-rebase master >out &&
-       test_i18ngrep "Current branch my-topic-branch is up to date, rebase forced" out
-'
-
-test_expect_success 'rebase against master twice from another branch' '
-       git checkout my-topic-branch^ &&
-       git rebase master my-topic-branch >out &&
-       test_i18ngrep "Current branch my-topic-branch is up to date" out
-'
-
-test_expect_success 'rebase fast-forward to master' '
-       git checkout my-topic-branch^ &&
-       git rebase my-topic-branch >out &&
-       test_i18ngrep "Fast-forwarded HEAD to my-topic-branch" out
+test_expect_success 'rebase, with <onto> and <upstream> specified as :/quuxery' '
+       test_when_finished "git branch -D torebase" &&
+       git checkout -b torebase my-topic-branch^ &&
+       upstream=$(git rev-parse ":/Add B") &&
+       onto=$(git rev-parse ":/Add A") &&
+       git rebase --onto $onto $upstream &&
+       git reset --hard my-topic-branch^ &&
+       git rebase --onto ":/Add A" ":/Add B" &&
+       git checkout my-topic-branch
 '
 
 test_expect_success 'the rebase operation should not have destroyed author information' '
@@ -101,29 +83,14 @@ test_expect_success 'HEAD was detached during rebase' '
        test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
 '
 
-test_expect_success 'rebase after merge master' '
-       git reset --hard topic &&
-       git merge master &&
-       git rebase master &&
-       ! (git show | grep "^Merge:")
-'
-
-test_expect_success 'rebase of history with merges is linearized' '
-       git checkout nonlinear &&
-       test 4 = $(git rev-list master.. | wc -l) &&
-       git rebase master &&
-       test 3 = $(git rev-list master.. | wc -l)
-'
-
-test_expect_success 'rebase of history with merges after upstream merge is linearized' '
-       git checkout upstream-merged-nonlinear &&
-       test 5 = $(git rev-list master.. | wc -l) &&
-       git rebase master &&
-       test 3 = $(git rev-list master.. | wc -l)
+test_expect_success 'rebase from ambiguous branch name' '
+       git checkout -b topic side &&
+       git rebase master
 '
 
 test_expect_success 'rebase a single mode change' '
        git checkout master &&
+       git branch -D topic &&
        echo 1 >X &&
        git add X &&
        test_tick &&
@@ -178,7 +145,7 @@ test_expect_success 'default to @{upstream} when upstream arg is missing' '
 test_expect_success 'rebase -q is quiet' '
        git checkout -b quiet topic &&
        git rebase -q master >output.out 2>&1 &&
-       test ! -s output.out
+       test_must_be_empty output.out
 '
 
 test_expect_success 'Rebase a commit that sprinkles CRs in' '
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
deleted file mode 100755 (executable)
index 58f4823..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
-#
-
-test_description='git rebase should detect patches integrated upstream
-
-This test cherry-picks one local change of two into master branch, and
-checks that git rebase succeeds with only the second patch in the
-local branch.
-'
-. ./test-lib.sh
-
-test_expect_success 'prepare repository with topic branch' '
-       test_commit A &&
-       git checkout -b my-topic-branch &&
-       test_commit B &&
-       test_commit C &&
-       git checkout -f master &&
-       test_commit A2 A.t
-'
-
-test_expect_success 'pick top patch from topic branch into master' '
-       git cherry-pick C &&
-       git checkout -f my-topic-branch
-'
-
-test_debug '
-       git cherry master &&
-       git format-patch -k --stdout --full-index master >/dev/null &&
-       gitk --all & sleep 1
-'
-
-test_expect_success 'rebase topic branch against new master and check git am did not get halted' '
-       git rebase master &&
-       test_path_is_missing .git/rebase-apply
-'
-
-test_expect_success 'rebase --merge topic branch that was partially merged upstream' '
-       git reset --hard C &&
-       git rebase --merge master &&
-       test_path_is_missing .git/rebase-merge
-'
-
-test_expect_success 'rebase ignores empty commit' '
-       git reset --hard A &&
-       git commit --allow-empty -m empty &&
-       test_commit D &&
-       git rebase C &&
-       test "$(git log --format=%s C..)" = "D"
-'
-
-test_expect_success 'rebase --keep-empty' '
-       git reset --hard D &&
-       git rebase --keep-empty C &&
-       test "$(git log --format=%s C..)" = "D
-empty"
-'
-
-test_expect_success 'rebase --keep-empty keeps empty even if already in upstream' '
-       git reset --hard A &&
-       git commit --allow-empty -m also-empty &&
-       git rebase --keep-empty D &&
-       test "$(git log --format=%s A..)" = "also-empty
-D
-empty"
-'
-
-test_done
index 826500bd18a520a37e3490b9deeca94fb9e14405..3968020e648091e907fde6efe9aeb5ce66753670 100755 (executable)
@@ -64,10 +64,11 @@ test_expect_success 'rebase with --merge' '
 
 test_expect_success 'rebase --skip with --merge' '
        git rebase --skip
-       '
+'
 
-test_expect_success 'merge and reference trees equal' \
-       'test -z "`git diff-tree skip-merge skip-reference`"'
+test_expect_success 'merge and reference trees equal' '
+       test -z "`git diff-tree skip-merge skip-reference`"
+'
 
 test_expect_success 'moved back to branch correctly' '
        test refs/heads/skip-merge = $(git symbolic-ref HEAD)
index a58406d12fc8dabf8f2e73e721d2f622a781cc9a..50e22b1cadff4252dfb552a4db930f49544952aa 100755 (executable)
@@ -29,8 +29,6 @@ Initial setup:
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
-set_fake_editor
-
 # WARNING: Modifications to the initial repository can change the SHA ID used
 # in the expect2 file for the 'stop on conflicting pick' test.
 
@@ -72,6 +70,7 @@ export SHELL
 test_expect_success 'rebase -i with the exec command' '
        git checkout master &&
        (
+       set_fake_editor &&
        FAKE_LINES="1 exec_>touch-one
                2 exec_>touch-two exec_false exec_>touch-three
                3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" &&
@@ -93,6 +92,7 @@ test_expect_success 'rebase -i with the exec command' '
 test_expect_success 'rebase -i with the exec command runs from tree root' '
        git checkout master &&
        mkdir subdir && (cd subdir &&
+       set_fake_editor &&
        FAKE_LINES="1 exec_>touch-subdir" \
                git rebase -i HEAD^
        ) &&
@@ -103,6 +103,7 @@ test_expect_success 'rebase -i with the exec command runs from tree root' '
 test_expect_success 'rebase -i with the exec command checks tree cleanness' '
        git checkout master &&
        (
+       set_fake_editor &&
        FAKE_LINES="exec_echo_foo_>file1 1" &&
        export FAKE_LINES &&
        test_must_fail git rebase -i HEAD^
@@ -116,6 +117,7 @@ test_expect_success 'rebase -i with exec of inexistent command' '
        git checkout master &&
        test_when_finished "git rebase --abort" &&
        (
+       set_fake_editor &&
        FAKE_LINES="exec_this-command-does-not-exist 1" &&
        export FAKE_LINES &&
        test_must_fail git rebase -i HEAD^ >actual 2>&1
@@ -125,6 +127,7 @@ test_expect_success 'rebase -i with exec of inexistent command' '
 
 test_expect_success 'no changes are a nop' '
        git checkout branch2 &&
+       set_fake_editor &&
        git rebase -i F &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
        test $(git rev-parse I) = $(git rev-parse HEAD)
@@ -134,6 +137,7 @@ test_expect_success 'test the [branch] option' '
        git checkout -b dead-end &&
        git rm file6 &&
        git commit -m "stop here" &&
+       set_fake_editor &&
        git rebase -i F branch2 &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
        test $(git rev-parse I) = $(git rev-parse branch2) &&
@@ -142,6 +146,7 @@ test_expect_success 'test the [branch] option' '
 
 test_expect_success 'test --onto <branch>' '
        git checkout -b test-onto branch2 &&
+       set_fake_editor &&
        git rebase -i --onto branch1 F &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-onto" &&
        test $(git rev-parse HEAD^) = $(git rev-parse branch1) &&
@@ -151,6 +156,7 @@ test_expect_success 'test --onto <branch>' '
 test_expect_success 'rebase on top of a non-conflicting commit' '
        git checkout branch1 &&
        git tag original-branch1 &&
+       set_fake_editor &&
        git rebase -i branch2 &&
        test file6 = $(git diff --name-only original-branch1) &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
@@ -163,6 +169,7 @@ test_expect_success 'reflog for the branch shows state before rebase' '
 '
 
 test_expect_success 'exchange two commits' '
+       set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i HEAD~2 &&
        test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test G = $(git cat-file commit HEAD | sed -ne \$p)
@@ -188,6 +195,7 @@ EOF
 
 test_expect_success 'stop on conflicting pick' '
        git tag new-branch1 &&
+       set_fake_editor &&
        test_must_fail git rebase -i master &&
        test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" &&
        test_cmp expect .git/rebase-merge/patch &&
@@ -208,6 +216,7 @@ test_expect_success 'abort' '
 test_expect_success 'abort with error when new base cannot be checked out' '
        git rm --cached file1 &&
        git commit -m "remove file in base" &&
+       set_fake_editor &&
        test_must_fail git rebase -i master > output 2>&1 &&
        grep "The following untracked working tree files would be overwritten by checkout:" \
                output &&
@@ -222,6 +231,7 @@ test_expect_success 'retain authorship' '
        test_tick &&
        GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
        git tag twerp &&
+       set_fake_editor &&
        git rebase -i --onto master HEAD^ &&
        git show HEAD | grep "^Author: Twerp Snog"
 '
@@ -232,6 +242,7 @@ test_expect_success 'squash' '
        test_tick &&
        GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
        echo "******************************" &&
+       set_fake_editor &&
        FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
                git rebase -i --onto master HEAD~2 &&
        test B = $(cat file7) &&
@@ -244,6 +255,7 @@ test_expect_success 'retain authorship when squashing' '
 
 test_expect_success '-p handles "no changes" gracefully' '
        HEAD=$(git rev-parse HEAD) &&
+       set_fake_editor &&
        git rebase -i -p HEAD^ &&
        git update-index --refresh &&
        git diff-files --quiet &&
@@ -253,6 +265,7 @@ test_expect_success '-p handles "no changes" gracefully' '
 
 test_expect_failure 'exchange two commits with -p' '
        git checkout H &&
+       set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i -p HEAD~2 &&
        test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
        test G = $(git cat-file commit HEAD | sed -ne \$p)
@@ -287,6 +300,7 @@ test_expect_success 'preserve merges with -p' '
        git commit -m M file1 &&
        git checkout -b to-be-rebased &&
        test_tick &&
+       set_fake_editor &&
        git rebase -i -p --onto branch1 master &&
        git update-index --refresh &&
        git diff-files --quiet &&
@@ -301,6 +315,7 @@ test_expect_success 'preserve merges with -p' '
 '
 
 test_expect_success 'edit ancestor with -p' '
+       set_fake_editor &&
        FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 &&
        echo 2 > unrelated-file &&
        test_tick &&
@@ -314,6 +329,7 @@ test_expect_success 'edit ancestor with -p' '
 
 test_expect_success '--continue tries to commit' '
        test_tick &&
+       set_fake_editor &&
        test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
        echo resolved > file1 &&
        git add file1 &&
@@ -325,6 +341,7 @@ test_expect_success '--continue tries to commit' '
 test_expect_success 'verbose flag is heeded, even after --continue' '
        git reset --hard master@{1} &&
        test_tick &&
+       set_fake_editor &&
        test_must_fail git rebase -v -i --onto new-branch1 HEAD^ &&
        echo resolved > file1 &&
        git add file1 &&
@@ -334,6 +351,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
 
 test_expect_success 'multi-squash only fires up editor once' '
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
                EXPECT_HEADER_COUNT=4 \
                git rebase -i $base &&
@@ -344,6 +362,7 @@ test_expect_success 'multi-squash only fires up editor once' '
 test_expect_success 'multi-fixup does not fire up editor' '
        git checkout -b multi-fixup E &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
                git rebase -i $base &&
        test $base = $(git rev-parse HEAD^) &&
@@ -355,6 +374,7 @@ test_expect_success 'multi-fixup does not fire up editor' '
 test_expect_success 'commit message used after conflict' '
        git checkout -b conflict-fixup conflict-branch &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        (
                FAKE_LINES="1 fixup 3 fixup 4" &&
                export FAKE_LINES &&
@@ -373,6 +393,7 @@ test_expect_success 'commit message used after conflict' '
 test_expect_success 'commit message retained after conflict' '
        git checkout -b conflict-squash conflict-branch &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        (
                FAKE_LINES="1 fixup 3 squash 4" &&
                export FAKE_LINES &&
@@ -399,6 +420,7 @@ EOF
 test_expect_success 'squash and fixup generate correct log messages' '
        git checkout -b squash-fixup E &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
                EXPECT_HEADER_COUNT=4 \
                git rebase -i $base &&
@@ -411,6 +433,7 @@ test_expect_success 'squash and fixup generate correct log messages' '
 test_expect_success 'squash ignores comments' '
        git checkout -b skip-comments E &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
                EXPECT_HEADER_COUNT=4 \
                git rebase -i $base &&
@@ -423,6 +446,7 @@ test_expect_success 'squash ignores comments' '
 test_expect_success 'squash ignores blank lines' '
        git checkout -b skip-blank-lines E &&
        base=$(git rev-parse HEAD~4) &&
+       set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
                EXPECT_HEADER_COUNT=4 \
                git rebase -i $base &&
@@ -435,6 +459,7 @@ test_expect_success 'squash ignores blank lines' '
 test_expect_success 'squash works as expected' '
        git checkout -b squash-works no-conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
+       set_fake_editor &&
        FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \
                git rebase -i HEAD~3 &&
        test $one = $(git rev-parse HEAD~2)
@@ -443,6 +468,7 @@ test_expect_success 'squash works as expected' '
 test_expect_success 'interrupted squash works as expected' '
        git checkout -b interrupted-squash conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
+       set_fake_editor &&
        (
                FAKE_LINES="1 squash 3 2" &&
                export FAKE_LINES &&
@@ -460,6 +486,7 @@ test_expect_success 'interrupted squash works as expected' '
 test_expect_success 'interrupted squash works as expected (case 2)' '
        git checkout -b interrupted-squash2 conflict-branch &&
        one=$(git rev-parse HEAD~3) &&
+       set_fake_editor &&
        (
                FAKE_LINES="3 squash 1 2" &&
                export FAKE_LINES &&
@@ -477,21 +504,14 @@ test_expect_success 'interrupted squash works as expected (case 2)' '
        test $one = $(git rev-parse HEAD~2)
 '
 
-test_expect_success 'ignore patch if in upstream' '
-       HEAD=$(git rev-parse HEAD) &&
-       git checkout -b has-cherry-picked HEAD^ &&
+test_expect_success '--continue tries to commit, even for "edit"' '
        echo unrelated > file7 &&
        git add file7 &&
        test_tick &&
        git commit -m "unrelated change" &&
-       git cherry-pick $HEAD &&
-       EXPECT_COUNT=1 git rebase -i $HEAD &&
-       test $HEAD = $(git rev-parse HEAD^)
-'
-
-test_expect_success '--continue tries to commit, even for "edit"' '
        parent=$(git rev-parse HEAD^) &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo edited > file7 &&
        git add file7 &&
@@ -504,6 +524,7 @@ test_expect_success '--continue tries to commit, even for "edit"' '
 test_expect_success 'aborted --continue does not squash commits after "edit"' '
        old=$(git rev-parse HEAD) &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo "edited again" > file7 &&
        git add file7 &&
@@ -518,6 +539,7 @@ test_expect_success 'aborted --continue does not squash commits after "edit"' '
 
 test_expect_success 'auto-amend only edited commits after "edit"' '
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo "edited again" > file7 &&
        git add file7 &&
@@ -536,6 +558,7 @@ test_expect_success 'auto-amend only edited commits after "edit"' '
 test_expect_success 'clean error after failed "exec"' '
        test_tick &&
        test_when_finished "git rebase --abort || :" &&
+       set_fake_editor &&
        (
                FAKE_LINES="1 exec_false" &&
                export FAKE_LINES &&
@@ -551,6 +574,7 @@ test_expect_success 'rebase a detached HEAD' '
        grandparent=$(git rev-parse HEAD~2) &&
        git checkout $(git rev-parse HEAD) &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i HEAD~2 &&
        test $grandparent = $(git rev-parse HEAD~2)
 '
@@ -567,6 +591,7 @@ test_expect_success 'rebase a commit violating pre-commit' '
        test_must_fail git commit -m doesnt-verify file1 &&
        git commit -m doesnt-verify --no-verify file1 &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES=2 git rebase -i HEAD~2
 
 '
@@ -588,6 +613,7 @@ test_expect_success 'rebase with a file named HEAD in worktree' '
                git commit -m "Add body"
        ) &&
 
+       set_fake_editor &&
        FAKE_LINES="1 squash 2" git rebase -i to-be-rebased &&
        test "$(git show -s --pretty=format:%an)" = "Squashed Away"
 
@@ -599,6 +625,7 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' '
        GIT_EDITOR=: git commit --amend \
                --author="Somebody else <somebody@else.com>" &&
        test $(git rev-parse branch3) != $(git rev-parse branch4) &&
+       set_fake_editor &&
        git rebase -i branch3 &&
        test $(git rev-parse branch3) = $(git rev-parse branch4)
 
@@ -623,10 +650,12 @@ test_expect_success 'submodule rebase setup' '
                git commit -a -m "submodule second"
        ) &&
        test_tick &&
+       set_fake_editor &&
        git commit -a -m "Three changes submodule"
 '
 
 test_expect_success 'submodule rebase -i' '
+       set_fake_editor &&
        FAKE_LINES="1 squash 2 3" git rebase -i A
 '
 
@@ -644,6 +673,7 @@ test_expect_success 'submodule conflict setup' '
 '
 
 test_expect_success 'rebase -i continue with only submodule staged' '
+       set_fake_editor &&
        test_must_fail git rebase -i submodule-base &&
        git add sub &&
        git rebase --continue &&
@@ -653,6 +683,7 @@ test_expect_success 'rebase -i continue with only submodule staged' '
 test_expect_success 'rebase -i continue with unstaged submodule' '
        git checkout submodule-topic &&
        git reset --hard &&
+       set_fake_editor &&
        test_must_fail git rebase -i submodule-base &&
        git reset &&
        git rebase --continue &&
@@ -665,6 +696,7 @@ test_expect_success 'avoid unnecessary reset' '
        test-chmtime =123456789 file3 &&
        git update-index --refresh &&
        HEAD=$(git rev-parse HEAD) &&
+       set_fake_editor &&
        git rebase -i HEAD~4 &&
        test $HEAD = $(git rev-parse HEAD) &&
        MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') &&
@@ -673,6 +705,7 @@ test_expect_success 'avoid unnecessary reset' '
 
 test_expect_success 'reword' '
        git checkout -b reword-branch master &&
+       set_fake_editor &&
        FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A &&
        git show HEAD | grep "E changed" &&
        test $(git rev-parse master) != $(git rev-parse HEAD) &&
@@ -692,7 +725,8 @@ test_expect_success 'rebase -i can copy notes' '
        test_commit n2 &&
        test_commit n3 &&
        git notes add -m"a note" n3 &&
-       git rebase --onto n1 n2 &&
+       set_fake_editor &&
+       git rebase -i --onto n1 n2 &&
        test "a note" = "$(git notes show HEAD)"
 '
 
@@ -705,6 +739,7 @@ EOF
 test_expect_success 'rebase -i can copy notes over a fixup' '
        git reset --hard n3 &&
        git notes add -m"an earlier note" n2 &&
+       set_fake_editor &&
        GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 &&
        git notes show > output &&
        test_cmp expect output
@@ -714,6 +749,7 @@ test_expect_success 'rebase while detaching HEAD' '
        git symbolic-ref HEAD &&
        grandparent=$(git rev-parse HEAD~2) &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 &&
        test $grandparent = $(git rev-parse HEAD~2) &&
        test_must_fail git symbolic-ref HEAD
@@ -723,6 +759,7 @@ test_tick # Ensure that the rebased commits get a different timestamp.
 test_expect_success 'always cherry-pick with --no-ff' '
        git checkout no-ff-branch &&
        git tag original-no-ff-branch &&
+       set_fake_editor &&
        git rebase -i --no-ff A &&
        touch empty &&
        for p in 0 1 2
@@ -755,6 +792,7 @@ test_expect_success 'set up commits with funny messages' '
 test_expect_success 'rebase-i history with funny messages' '
        git rev-list A..funny >expect &&
        test_tick &&
+       set_fake_editor &&
        FAKE_LINES="1 2 3 4" git rebase -i A &&
        git rev-list A.. >actual &&
        test_cmp expect actual
@@ -771,6 +809,7 @@ test_expect_success 'prepare for rebase -i --exec' '
 
 
 test_expect_success 'running "git rebase -i --exec git show HEAD"' '
+       set_fake_editor &&
        git rebase -i --exec "git show HEAD" HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -784,6 +823,7 @@ test_expect_success 'running "git rebase -i --exec git show HEAD"' '
 
 test_expect_success 'running "git rebase --exec git show HEAD -i"' '
        git reset --hard execute &&
+       set_fake_editor &&
        git rebase --exec "git show HEAD" -i HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -797,6 +837,7 @@ test_expect_success 'running "git rebase --exec git show HEAD -i"' '
 
 test_expect_success 'running "git rebase -ix git show HEAD"' '
        git reset --hard execute &&
+       set_fake_editor &&
        git rebase -ix "git show HEAD" HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" &&
@@ -810,6 +851,7 @@ test_expect_success 'running "git rebase -ix git show HEAD"' '
 
 test_expect_success 'rebase -ix with several <CMD>' '
        git reset --hard execute &&
+       set_fake_editor &&
        git rebase -ix "git show HEAD; pwd" HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" &&
@@ -823,6 +865,7 @@ test_expect_success 'rebase -ix with several <CMD>' '
 
 test_expect_success 'rebase -ix with several instances of --exec' '
        git reset --hard execute &&
+       set_fake_editor &&
        git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual &&
        (
                FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2
@@ -844,6 +887,7 @@ test_expect_success 'rebase -ix with --autosquash' '
        echo bis >bis.txt &&
        git add bis.txt &&
        git commit -m "fixup! two_exec" &&
+       set_fake_editor &&
        (
                git checkout -b autosquash_actual &&
                git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual
@@ -862,6 +906,7 @@ test_expect_success 'rebase -ix with --autosquash' '
 
 test_expect_success 'rebase --exec without -i shows error message' '
        git reset --hard execute &&
+       set_fake_editor &&
        test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual &&
        echo "The --exec option must be used with the --interactive option" >expected &&
        test_i18ncmp expected actual
@@ -870,6 +915,7 @@ test_expect_success 'rebase --exec without -i shows error message' '
 
 test_expect_success 'rebase -i --exec without <CMD>' '
        git reset --hard execute &&
+       set_fake_editor &&
        test_must_fail git rebase -i --exec 2>tmp &&
        sed -e "1d" tmp >actual &&
        test_must_fail git rebase -h >expected &&
@@ -879,6 +925,7 @@ test_expect_success 'rebase -i --exec without <CMD>' '
 
 test_expect_success 'rebase -i --root re-order and drop commits' '
        git checkout E &&
+       set_fake_editor &&
        FAKE_LINES="3 1 2 5" git rebase -i --root &&
        test E = $(git cat-file commit HEAD | sed -ne \$p) &&
        test B = $(git cat-file commit HEAD^ | sed -ne \$p) &&
@@ -892,6 +939,7 @@ test_expect_success 'rebase -i --root retain root commit author and message' '
        echo B >file7 &&
        git add file7 &&
        GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
+       set_fake_editor &&
        FAKE_LINES="2" git rebase -i --root &&
        git cat-file commit HEAD | grep -q "^author Twerp Snog" &&
        git cat-file commit HEAD | grep -q "^different author$"
@@ -900,6 +948,7 @@ test_expect_success 'rebase -i --root retain root commit author and message' '
 test_expect_success 'rebase -i --root temporary sentinel commit' '
        git checkout B &&
        (
+               set_fake_editor &&
                FAKE_LINES="2" &&
                export FAKE_LINES &&
                test_must_fail git rebase -i --root
@@ -910,6 +959,7 @@ test_expect_success 'rebase -i --root temporary sentinel commit' '
 
 test_expect_success 'rebase -i --root fixup root commit' '
        git checkout B &&
+       set_fake_editor &&
        FAKE_LINES="1 fixup 2" git rebase -i --root &&
        test A = $(git cat-file commit HEAD | sed -ne \$p) &&
        test B = $(git show HEAD:file1) &&
@@ -919,6 +969,7 @@ test_expect_success 'rebase -i --root fixup root commit' '
 test_expect_success 'rebase --edit-todo does not works on non-interactive rebase' '
        git reset --hard &&
        git checkout conflict-branch &&
+       set_fake_editor &&
        test_must_fail git rebase --onto HEAD~2 HEAD~ &&
        test_must_fail git rebase --edit-todo &&
        git rebase --abort
@@ -927,6 +978,7 @@ test_expect_success 'rebase --edit-todo does not works on non-interactive rebase
 test_expect_success 'rebase --edit-todo can be used to modify todo' '
        git reset --hard &&
        git checkout no-conflict-branch^0 &&
+       set_fake_editor &&
        FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 &&
        FAKE_LINES="2 1" git rebase --edit-todo &&
        git rebase --continue
@@ -934,6 +986,22 @@ test_expect_success 'rebase --edit-todo can be used to modify todo' '
        test L = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
+test_expect_success 'rebase -i produces readable reflog' '
+       git reset --hard &&
+       git branch -f branch-reflog-test H &&
+       set_fake_editor &&
+       git rebase -i --onto I F branch-reflog-test &&
+       cat >expect <<-\EOF &&
+       rebase -i (start): checkout I
+       rebase -i (pick): G
+       rebase -i (pick): H
+       rebase -i (finish): returning to refs/heads/branch-reflog-test
+       EOF
+       tail -n 4 .git/logs/HEAD |
+       sed -e "s/.*    //" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'rebase -i respects core.commentchar' '
        git reset --hard &&
        git checkout E^0 &&
@@ -947,4 +1015,63 @@ test_expect_success 'rebase -i respects core.commentchar' '
        test B = $(git cat-file commit HEAD^ | sed -ne \$p)
 '
 
+test_expect_success 'rebase -i, with <onto> and <upstream> specified as :/quuxery' '
+       test_when_finished "git branch -D torebase" &&
+       git checkout -b torebase branch1 &&
+       upstream=$(git rev-parse ":/J") &&
+       onto=$(git rev-parse ":/A") &&
+       git rebase --onto $onto $upstream &&
+       git reset --hard branch1 &&
+       git rebase --onto ":/A" ":/J" &&
+       git checkout branch1
+'
+
+test_expect_success 'rebase -i with --strategy and -X' '
+       git checkout -b conflict-merge-use-theirs conflict-branch &&
+       git reset --hard HEAD^ &&
+       echo five >conflict &&
+       echo Z >file1 &&
+       git commit -a -m "one file conflict" &&
+       EDITOR=true git rebase -i --strategy=recursive -Xours conflict-branch &&
+       test $(git show conflict-branch:conflict) = $(cat conflict) &&
+       test $(cat file1) = Z
+'
+
+test_expect_success 'rebase -i error on commits with \ in message' '
+       current_head=$(git rev-parse HEAD)
+       test_when_finished "git rebase --abort; git reset --hard $current_head; rm -f error" &&
+       test_commit TO-REMOVE will-conflict old-content &&
+       test_commit "\temp" will-conflict new-content dummy &&
+       (
+       EDITOR=true &&
+       export EDITOR &&
+       test_must_fail git rebase -i HEAD^ --onto HEAD^^ 2>error
+       ) &&
+       test_expect_code 1 grep  "      emp" error
+'
+
+test_expect_success 'short SHA-1 setup' '
+       test_when_finished "git checkout master" &&
+       git checkout --orphan collide &&
+       git rm -rf . &&
+       (
+       unset test_tick &&
+       test_commit collide1 collide &&
+       test_commit --notick collide2 collide &&
+       test_commit --notick collide3 collide
+       )
+'
+
+test_expect_success 'short SHA-1 collide' '
+       test_when_finished "reset_rebase && git checkout master" &&
+       git checkout collide &&
+       (
+       unset test_tick &&
+       test_tick &&
+       set_fake_editor &&
+       FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \
+       FAKE_LINES="reword 1 2" git rebase -i HEAD~2
+       )
+'
+
 test_done
index e6a9a0d436f3a28a7de6eaadd7f1f223ae740c64..0392e36d2364c8149f2a628a0901f113b7a5875f 100755 (executable)
@@ -4,27 +4,17 @@ test_description='messages from rebase operation'
 
 . ./test-lib.sh
 
-quick_one () {
-       echo "$1" >"file$1" &&
-       git add "file$1" &&
-       test_tick &&
-       git commit -m "$1"
-}
+test_expect_success 'setup' '
+       test_commit O fileO &&
+       test_commit X fileX &&
+       test_commit A fileA &&
+       test_commit B fileB &&
+       test_commit Y fileY &&
 
-test_expect_success setup '
-       quick_one O &&
-       git branch topic &&
-       quick_one X &&
-       quick_one A &&
-       quick_one B &&
-       quick_one Y &&
-
-       git checkout topic &&
-       quick_one A &&
-       quick_one B &&
-       quick_one Z &&
+       git checkout -b topic O &&
+       git cherry-pick A B &&
+       test_commit Z fileZ &&
        git tag start
-
 '
 
 cat >expect <<\EOF
@@ -34,12 +24,32 @@ Committed: 0003 Z
 EOF
 
 test_expect_success 'rebase -m' '
-
        git rebase -m master >report &&
        sed -n -e "/^Already applied: /p" \
                -e "/^Committed: /p" report >actual &&
        test_cmp expect actual
+'
+
+test_expect_success 'rebase against master twice' '
+       git rebase master >out &&
+       test_i18ngrep "Current branch topic is up to date" out
+'
+
+test_expect_success 'rebase against master twice with --force' '
+       git rebase --force-rebase master >out &&
+       test_i18ngrep "Current branch topic is up to date, rebase forced" out
+'
+
+test_expect_success 'rebase against master twice from another branch' '
+       git checkout topic^ &&
+       git rebase master topic >out &&
+       test_i18ngrep "Current branch topic is up to date" out
+'
 
+test_expect_success 'rebase fast-forward to master' '
+       git checkout topic^ &&
+       git rebase topic >out &&
+       test_i18ngrep "Fast-forwarded HEAD to topic" out
 '
 
 test_expect_success 'rebase --stat' '
index 6de4e2263f9ec65d2de2e28af6a12d5c1065f686..8c251c57a6827317a9c17fac2a0e99df3c9becf8 100755 (executable)
@@ -11,14 +11,6 @@ Run "git rebase -p" and check that merges are properly carried along
 GIT_AUTHOR_EMAIL=bogus_email_address
 export GIT_AUTHOR_EMAIL
 
-# Clone 1 (trivial merge):
-#
-# A1--A2  <-- origin/master
-#  \   \
-#   B1--M  <-- topic
-#    \
-#     B2  <-- origin/topic
-#
 # Clone 2 (conflicting merge):
 #
 # A1--A2--B3   <-- origin/master
@@ -37,15 +29,7 @@ export GIT_AUTHOR_EMAIL
 #      \
 #       B2     <-- origin/topic
 #
-# Clone 4 (merge using second parent as base):
-#
-# A1--A2--B3   <-- origin/master
-#  \
-#   B1--A3--M  <-- topic
-#    \     /
-#     \--A4    <-- topic2
-#      \
-#       B2     <-- origin/topic
+# Clone 4 (same as Clone 3)
 
 test_expect_success 'setup for merge-preserving rebase' \
        'echo First > A &&
@@ -58,20 +42,6 @@ test_expect_success 'setup for merge-preserving rebase' \
        git checkout -f master &&
        echo Third >> A &&
        git commit -a -m "Modify A2" &&
-
-       git clone ./. clone1 &&
-       (cd clone1 &&
-       git checkout -b topic origin/topic &&
-       git merge origin/master
-       ) &&
-
-       git clone ./. clone4 &&
-       (
-               cd clone4 &&
-               git checkout -b topic origin/topic &&
-               git merge origin/master
-       ) &&
-
        echo Fifth > B &&
        git add B &&
        git commit -m "Add different B" &&
@@ -96,21 +66,21 @@ test_expect_success 'setup for merge-preserving rebase' \
                git merge --no-ff topic2
        ) &&
 
+       git clone ./. clone4 &&
+       (
+               cd clone4 &&
+               git checkout -b topic2 origin/topic &&
+               echo Sixth > A &&
+               git commit -a -m "Modify A3" &&
+               git checkout -b topic origin/topic &&
+               git merge --no-ff topic2
+       ) &&
+
        git checkout topic &&
        echo Fourth >> B &&
        git commit -a -m "Modify B2"
 '
 
-test_expect_success 'rebase -p fakes interactive rebase' '
-       (
-       cd clone1 &&
-       git fetch &&
-       git rebase -p origin/topic &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
-       )
-'
-
 test_expect_success '--continue works after a conflict' '
        (
        cd clone2 &&
@@ -138,14 +108,14 @@ test_expect_success 'rebase -p preserves no-ff merges' '
        )
 '
 
-test_expect_success 'rebase -p works when base inside second parent' '
+test_expect_success 'rebase -p ignores merge.log config' '
        (
        cd clone4 &&
        git fetch &&
-       git rebase -p HEAD^2 &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
+       git -c merge.log=1 rebase -p origin/topic &&
+       echo >expected &&
+       git log --format="%b" -1 >current &&
+       test_cmp expected current
        )
 '
 
index a1e86c4097ba096245b11a991c39e79886ca2b91..41370ab998c72e14f269a257304b3e0cc4aebc8c 100755 (executable)
@@ -4,6 +4,8 @@ test_description='auto squash'
 
 . ./test-lib.sh
 
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
 test_expect_success setup '
        echo 0 >file0 &&
        git add . &&
@@ -193,4 +195,59 @@ test_expect_success 'use commit --squash' '
        test_auto_commit_flags squash 2
 '
 
+test_auto_fixup_fixup () {
+       git reset --hard base &&
+       echo 1 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit -m "$1! first" &&
+       echo 2 >file1 &&
+       git add -u &&
+       test_tick &&
+       git commit -m "$1! $2! first" &&
+       git tag "final-$1-$2" &&
+       test_tick &&
+       (
+               set_cat_todo_editor &&
+               test_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&
+               cat >expected <<-EOF &&
+               pick $(git rev-parse --short HEAD^^^) first commit
+               $1 $(git rev-parse --short HEAD^) $1! first
+               $1 $(git rev-parse --short HEAD) $1! $2! first
+               pick $(git rev-parse --short HEAD^^) second commit
+               EOF
+               test_cmp expected actual
+       ) &&
+       git rebase --autosquash -i HEAD^^^^ &&
+       git log --oneline >actual &&
+       test_line_count = 3 actual
+       git diff --exit-code "final-$1-$2" &&
+       test 2 = "$(git cat-file blob HEAD^:file1)" &&
+       if test "$1" = "fixup"
+       then
+               test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+       elif test "$1" = "squash"
+       then
+               test 3 = $(git cat-file commit HEAD^ | grep first | wc -l)
+       else
+               false
+       fi
+}
+
+test_expect_success 'fixup! fixup!' '
+       test_auto_fixup_fixup fixup fixup
+'
+
+test_expect_success 'fixup! squash!' '
+       test_auto_fixup_fixup fixup squash
+'
+
+test_expect_success 'squash! squash!' '
+       test_auto_fixup_fixup squash squash
+'
+
+test_expect_success 'squash! fixup!' '
+       test_auto_fixup_fixup squash fixup
+'
+
 test_done
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
new file mode 100755 (executable)
index 0000000..90eb264
--- /dev/null
@@ -0,0 +1,170 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Ramkumar Ramachandra
+#
+
+test_description='git rebase --autostash tests'
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo hello-world >file0 &&
+       git add . &&
+       test_tick &&
+       git commit -m "initial commit" &&
+       git checkout -b feature-branch &&
+       echo another-hello >file1 &&
+       echo goodbye >file2 &&
+       git add . &&
+       test_tick &&
+       git commit -m "second commit" &&
+       echo final-goodbye >file3 &&
+       git add . &&
+       test_tick &&
+       git commit -m "third commit" &&
+       git checkout -b unrelated-onto-branch master &&
+       echo unrelated >file4 &&
+       git add . &&
+       test_tick &&
+       git commit -m "unrelated commit" &&
+       git checkout -b related-onto-branch master &&
+       echo conflicting-change >file2 &&
+       git add . &&
+       test_tick &&
+       git commit -m "related commit"
+'
+
+testrebase() {
+       type=$1
+       dotest=$2
+
+       test_expect_success "rebase$type: dirty worktree, non-conflicting rebase" '
+               test_config rebase.autostash true &&
+               git reset --hard &&
+               git checkout -b rebased-feature-branch feature-branch &&
+               test_when_finished git branch -D rebased-feature-branch &&
+               echo dirty >>file3 &&
+               git rebase$type unrelated-onto-branch &&
+               grep unrelated file4 &&
+               grep dirty file3 &&
+               git checkout feature-branch
+       '
+
+       test_expect_success "rebase$type: dirty index, non-conflicting rebase" '
+               test_config rebase.autostash true &&
+               git reset --hard &&
+               git checkout -b rebased-feature-branch feature-branch &&
+               test_when_finished git branch -D rebased-feature-branch &&
+               echo dirty >>file3 &&
+               git add file3 &&
+               git rebase$type unrelated-onto-branch &&
+               grep unrelated file4 &&
+               grep dirty file3 &&
+               git checkout feature-branch
+       '
+
+       test_expect_success "rebase$type: conflicting rebase" '
+               test_config rebase.autostash true &&
+               git reset --hard &&
+               git checkout -b rebased-feature-branch feature-branch &&
+               test_when_finished git branch -D rebased-feature-branch &&
+               echo dirty >>file3 &&
+               test_must_fail git rebase$type related-onto-branch &&
+               test_path_is_file $dotest/autostash &&
+               ! grep dirty file3 &&
+               rm -rf $dotest &&
+               git reset --hard &&
+               git checkout feature-branch
+       '
+
+       test_expect_success "rebase$type: --continue" '
+               test_config rebase.autostash true &&
+               git reset --hard &&
+               git checkout -b rebased-feature-branch feature-branch &&
+               test_when_finished git branch -D rebased-feature-branch &&
+               echo dirty >>file3 &&
+               test_must_fail git rebase$type related-onto-branch &&
+               test_path_is_file $dotest/autostash &&
+               ! grep dirty file3 &&
+               echo "conflicting-plus-goodbye" >file2 &&
+               git add file2 &&
+               git rebase --continue &&
+               test_path_is_missing $dotest/autostash &&
+               grep dirty file3 &&
+               git checkout feature-branch
+       '
+
+       test_expect_success "rebase$type: --skip" '
+               test_config rebase.autostash true &&
+               git reset --hard &&
+               git checkout -b rebased-feature-branch feature-branch &&
+               test_when_finished git branch -D rebased-feature-branch &&
+               echo dirty >>file3 &&
+               test_must_fail git rebase$type related-onto-branch &&
+               test_path_is_file $dotest/autostash &&
+               ! grep dirty file3 &&
+               git rebase --skip &&
+               test_path_is_missing $dotest/autostash &&
+               grep dirty file3 &&
+               git checkout feature-branch
+       '
+
+       test_expect_success "rebase$type: --abort" '
+               test_config rebase.autostash true &&
+               git reset --hard &&
+               git checkout -b rebased-feature-branch feature-branch &&
+               test_when_finished git branch -D rebased-feature-branch &&
+               echo dirty >>file3 &&
+               test_must_fail git rebase$type related-onto-branch &&
+               test_path_is_file $dotest/autostash &&
+               ! grep dirty file3 &&
+               git rebase --abort &&
+               test_path_is_missing $dotest/autostash &&
+               grep dirty file3 &&
+               git checkout feature-branch
+       '
+
+       test_expect_success "rebase$type: non-conflicting rebase, conflicting stash" '
+               test_config rebase.autostash true &&
+               git reset --hard &&
+               git checkout -b rebased-feature-branch feature-branch &&
+               test_when_finished git branch -D rebased-feature-branch &&
+               echo dirty >file4 &&
+               git add file4 &&
+               git rebase$type unrelated-onto-branch &&
+               test_path_is_missing $dotest &&
+               git reset --hard &&
+               grep unrelated file4 &&
+               ! grep dirty file4 &&
+               git checkout feature-branch &&
+               git stash pop &&
+               grep dirty file4
+       '
+}
+
+test_expect_success "rebase: fast-forward rebase" '
+       test_config rebase.autostash true &&
+       git reset --hard &&
+       git checkout -b behind-feature-branch feature-branch~1 &&
+       test_when_finished git branch -D behind-feature-branch &&
+       echo dirty >>file1 &&
+       git rebase feature-branch &&
+       grep dirty file1 &&
+       git checkout feature-branch
+'
+
+test_expect_success "rebase: noop rebase" '
+       test_config rebase.autostash true &&
+       git reset --hard &&
+       git checkout -b same-feature-branch feature-branch &&
+       test_when_finished git branch -D same-feature-branch &&
+       echo dirty >>file1 &&
+       git rebase feature-branch &&
+       grep dirty file1 &&
+       git checkout feature-branch
+'
+
+testrebase "" .git/rebase-apply
+testrebase " --merge" .git/rebase-merge
+testrebase " --interactive" .git/rebase-merge
+
+test_done
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
new file mode 100755 (executable)
index 0000000..9c55cba
--- /dev/null
@@ -0,0 +1,350 @@
+#!/bin/sh
+
+test_description='basic rebase topology tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+# a---b---c
+#      \
+#       d---e
+test_expect_success 'setup' '
+       test_commit a &&
+       test_commit b &&
+       test_commit c &&
+       git checkout b &&
+       test_commit d &&
+       test_commit e
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "simple rebase $*" "
+               reset_rebase &&
+               git rebase $* c e &&
+               test_cmp_rev c HEAD~2 &&
+               test_linear_range 'd e' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* is no-op if upstream is an ancestor" "
+               reset_rebase &&
+               git rebase $* b e &&
+               test_cmp_rev e HEAD
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* -f rewrites even if upstream is an ancestor" "
+               reset_rebase &&
+               git rebase $* -f b e &&
+               ! test_cmp_rev e HEAD &&
+               test_cmp_rev b HEAD~2 &&
+               test_linear_range 'd e' b..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* fast-forwards from ancestor of upstream" "
+               reset_rebase &&
+               git rebase $* e b &&
+               test_cmp_rev e HEAD
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+#       f
+#      /
+# a---b---c---g---h
+#      \
+#       d---gp--i
+#
+# gp = cherry-picked g
+# h = reverted g
+#
+# Reverted patches are there for tests to be able to check if a commit
+# that introduced the same change as another commit is
+# dropped. Without reverted commits, we could get false positives
+# because applying the patch succeeds, but simply results in no
+# changes.
+test_expect_success 'setup of linear history for range selection tests' '
+       git checkout c &&
+       test_commit g &&
+       revert h g &&
+       git checkout d &&
+       cherry_pick gp g &&
+       test_commit i &&
+       git checkout b &&
+       test_commit f
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* drops patches in upstream" "
+               reset_rebase &&
+               git rebase $* h i &&
+               test_cmp_rev h HEAD~2 &&
+               test_linear_range 'd i' h..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* can drop last patch if in upstream" "
+               reset_rebase &&
+               git rebase $* h gp &&
+               test_cmp_rev h HEAD^ &&
+               test_linear_range 'd' h..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto drops patches in upstream" "
+               reset_rebase &&
+               git rebase $* --onto f h i &&
+               test_cmp_rev f HEAD~2 &&
+               test_linear_range 'd i' f..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto does not drop patches in onto" "
+               reset_rebase &&
+               git rebase $* --onto h f i &&
+               test_cmp_rev h HEAD~3 &&
+               test_linear_range 'd gp i' h..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+# a---b---c---j!
+#      \
+#       d---k!--l
+#
+# ! = empty
+test_expect_success 'setup of linear history for empty commit tests' '
+       git checkout c &&
+       make_empty j &&
+       git checkout d &&
+       make_empty k &&
+       test_commit l
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* drops empty commit" "
+               reset_rebase &&
+               git rebase $* c l &&
+               test_cmp_rev c HEAD~2 &&
+               test_linear_range 'd l' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --keep-empty" "
+               reset_rebase &&
+               git rebase $* --keep-empty c l &&
+               test_cmp_rev c HEAD~3 &&
+               test_linear_range 'd k l' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --keep-empty keeps empty even if already in upstream" "
+               reset_rebase &&
+               git rebase $* --keep-empty j l &&
+               test_cmp_rev j HEAD~3 &&
+               test_linear_range 'd k l' j..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase failure -i
+test_run_rebase failure -p
+
+#       m
+#      /
+# a---b---c---g
+#
+# x---y---bp
+#
+# bp = cherry-picked b
+# m = reverted b
+#
+# Reverted patches are there for tests to be able to check if a commit
+# that introduced the same change as another commit is
+# dropped. Without reverted commits, we could get false positives
+# because applying the patch succeeds, but simply results in no
+# changes.
+test_expect_success 'setup of linear history for test involving root' '
+       git checkout b &&
+       revert m b &&
+       git checkout --orphan disjoint &&
+       git rm -rf . &&
+       test_commit x &&
+       test_commit y &&
+       cherry_pick bp b
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto --root" "
+               reset_rebase &&
+               git rebase $* --onto c --root y &&
+               test_cmp_rev c HEAD~2 &&
+               test_linear_range 'x y' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* without --onto --root with disjoint history" "
+               reset_rebase &&
+               git rebase $* c y &&
+               test_cmp_rev c HEAD~2 &&
+               test_linear_range 'x y' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto --root drops patch in onto" "
+               reset_rebase &&
+               git rebase $* --onto m --root bp &&
+               test_cmp_rev m HEAD~2 &&
+               test_linear_range 'x y' m..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto --root with merge-base does not go to root" "
+               reset_rebase &&
+               git rebase $* --onto m --root g &&
+               test_cmp_rev m HEAD~2 &&
+               test_linear_range 'c g' m..
+       "
+}
+
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* without --onto --root with disjoint history drops patch in onto" "
+               reset_rebase &&
+               git rebase $* m bp &&
+               test_cmp_rev m HEAD~2 &&
+               test_linear_range 'x y' m..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --root on linear history is a no-op" "
+               reset_rebase &&
+               git rebase $* --root c &&
+               test_cmp_rev c HEAD
+       "
+}
+test_run_rebase failure ''
+test_run_rebase failure -m
+test_run_rebase failure -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* -f --root on linear history causes re-write" "
+               reset_rebase &&
+               git rebase $* -f --root c &&
+               ! test_cmp_rev a HEAD~2 &&
+               test_linear_range 'a b c' HEAD
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_done
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
new file mode 100755 (executable)
index 0000000..1d195fb
--- /dev/null
@@ -0,0 +1,258 @@
+#!/bin/sh
+
+test_description='rebase topology tests with merges'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_revision_subjects () {
+       expected="$1"
+       shift
+       set -- $(git log --format=%s --no-walk=unsorted "$@")
+       test "$expected" = "$*"
+}
+
+# a---b-----------c
+#      \           \
+#       d-------e   \
+#        \       \   \
+#         n---o---w---v
+#              \
+#               z
+test_expect_success 'setup of non-linear-history' '
+       test_commit a &&
+       test_commit b &&
+       test_commit c &&
+       git checkout b &&
+       test_commit d &&
+       test_commit e
+
+       git checkout c &&
+       test_commit g &&
+       revert h g &&
+       git checkout d &&
+       cherry_pick gp g &&
+       test_commit i &&
+       git checkout b &&
+       test_commit f
+
+       git checkout d &&
+       test_commit n &&
+       test_commit o &&
+       test_merge w e &&
+       test_merge v c &&
+       git checkout o &&
+       test_commit z
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* after merge from upstream" "
+               reset_rebase &&
+               git rebase $* e w &&
+               test_cmp_rev e HEAD~2 &&
+               test_linear_range 'n o' e..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+
+test_run_rebase () {
+       result=$1
+       shift
+       expected=$1
+       shift
+       test_expect_$result "rebase $* of non-linear history is linearized in place" "
+               reset_rebase &&
+               git rebase $* d w &&
+               test_cmp_rev d HEAD~3 &&
+               test_linear_range "\'"$expected"\'" d..
+       "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'e n o' ''
+test_run_rebase success 'e n o' -m
+test_run_rebase success 'n o e' -i
+
+test_run_rebase () {
+       result=$1
+       shift
+       expected=$1
+       shift
+       test_expect_$result "rebase $* of non-linear history is linearized upstream" "
+               reset_rebase &&
+               git rebase $* c w &&
+               test_cmp_rev c HEAD~4 &&
+               test_linear_range "\'"$expected"\'" c..
+       "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'd e n o' ''
+test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' -i
+
+test_run_rebase () {
+       result=$1
+       shift
+       expected=$1
+       shift
+       test_expect_$result "rebase $* of non-linear history with merges after upstream merge is linearized" "
+               reset_rebase &&
+               git rebase $* c v &&
+               test_cmp_rev c HEAD~4 &&
+               test_linear_range "\'"$expected"\'" c..
+       "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'd e n o' ''
+test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' -i
+
+test_expect_success "rebase -p is no-op in non-linear history" "
+       reset_rebase &&
+       git rebase -p d w &&
+       test_cmp_rev w HEAD
+"
+
+test_expect_success "rebase -p is no-op when base inside second parent" "
+       reset_rebase &&
+       git rebase -p e w &&
+       test_cmp_rev w HEAD
+"
+
+test_expect_failure "rebase -p --root on non-linear history is a no-op" "
+       reset_rebase &&
+       git rebase -p --root w &&
+       test_cmp_rev w HEAD
+"
+
+test_expect_success "rebase -p re-creates merge from side branch" "
+       reset_rebase &&
+       git rebase -p z w &&
+       test_cmp_rev z HEAD^ &&
+       test_cmp_rev w^2 HEAD^2
+"
+
+test_expect_success "rebase -p re-creates internal merge" "
+       reset_rebase &&
+       git rebase -p c w &&
+       test_cmp_rev c HEAD~4 &&
+       test_cmp_rev HEAD^2^ HEAD~3 &&
+       test_revision_subjects 'd n e o w' HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p can re-create two branches on onto" "
+       reset_rebase &&
+       git rebase -p --onto c d w &&
+       test_cmp_rev c HEAD~3 &&
+       test_cmp_rev c HEAD^2^ &&
+       test_revision_subjects 'n e o w' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+#       f
+#      /
+# a---b---c---g---h
+#      \
+#       d---gp--i
+#        \       \
+#         e-------u
+#
+# gp = cherry-picked g
+# h = reverted g
+test_expect_success 'setup of non-linear-history for patch-equivalence tests' '
+       git checkout e &&
+       test_merge u i
+'
+
+test_expect_success "rebase -p re-creates history around dropped commit matching upstream" "
+       reset_rebase &&
+       git rebase -p h u &&
+       test_cmp_rev h HEAD~3 &&
+       test_cmp_rev HEAD^2^ HEAD~2 &&
+       test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p --onto in merged history drops patches in upstream" "
+       reset_rebase &&
+       git rebase -p --onto f h u &&
+       test_cmp_rev f HEAD~3 &&
+       test_cmp_rev HEAD^2^ HEAD~2 &&
+       test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p --onto in merged history does not drop patches in onto" "
+       reset_rebase &&
+       git rebase -p --onto h f u &&
+       test_cmp_rev h HEAD~3 &&
+       test_cmp_rev HEAD^2~2 HEAD~2 &&
+       test_revision_subjects 'd gp i e u' HEAD~2 HEAD^2^ HEAD^2 HEAD^ HEAD
+"
+
+# a---b---c---g---h
+#      \
+#       d---gp--s
+#        \   \ /
+#         \   X
+#          \ / \
+#           e---t
+#
+# gp = cherry-picked g
+# h = reverted g
+test_expect_success 'setup of non-linear-history for dropping whole side' '
+       git checkout gp &&
+       test_merge s e &&
+       git checkout e &&
+       test_merge t gp
+'
+
+test_expect_failure "rebase -p drops merge commit when entire first-parent side is dropped" "
+       reset_rebase &&
+       git rebase -p h s &&
+       test_cmp_rev h HEAD~2 &&
+       test_linear_range 'd e' h..
+"
+
+test_expect_success "rebase -p drops merge commit when entire second-parent side is dropped" "
+       reset_rebase &&
+       git rebase -p h t &&
+       test_cmp_rev h HEAD~2 &&
+       test_linear_range 'd e' h..
+"
+
+# a---b---c
+#      \
+#       d---e
+#        \   \
+#         n---r
+#          \
+#           o
+#
+# r = tree-same with n
+test_expect_success 'setup of non-linear-history for empty commits' '
+       git checkout n &&
+       git merge --no-commit e &&
+       git reset n . &&
+       git commit -m r &&
+       git reset --hard &&
+       git clean -f &&
+       git tag r
+'
+
+test_expect_success "rebase -p re-creates empty internal merge commit" "
+       reset_rebase &&
+       git rebase -p c r &&
+       test_cmp_rev c HEAD~3 &&
+       test_cmp_rev HEAD^2^ HEAD~2 &&
+       test_revision_subjects 'd e n r' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p re-creates empty merge commit" "
+       reset_rebase &&
+       git rebase -p o r &&
+       test_cmp_rev e HEAD^2 &&
+       test_cmp_rev o HEAD^ &&
+       test_revision_subjects 'r' HEAD
+"
+
+test_done
index a0c6e30d805e6e71b9fec4311a6ede298b6265b4..fbdc47cfbdae6e3cec7d6762faa4a8e13554d803 100755 (executable)
@@ -28,29 +28,21 @@ test_expect_success setup '
 '
 
 test_expect_success 'cherry-pick an empty commit' '
-       git checkout master && {
-               git cherry-pick empty-branch^
-               test "$?" = 1
-       }
+       git checkout master &&
+       test_expect_code 1 git cherry-pick empty-branch^
 '
 
 test_expect_success 'index lockfile was removed' '
-
        test ! -f .git/index.lock
-
 '
 
 test_expect_success 'cherry-pick a commit with an empty message' '
-       git checkout master && {
-               git cherry-pick empty-branch
-               test "$?" = 1
-       }
+       git checkout master &&
+       test_expect_code 1 git cherry-pick empty-branch
 '
 
 test_expect_success 'index lockfile was removed' '
-
        test ! -f .git/index.lock
-
 '
 
 test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' '
@@ -101,7 +93,7 @@ test_expect_success 'cherry-pick a no-op with --keep-redundant' '
        git reset --hard &&
        git checkout fork^0 &&
        git cherry-pick --keep-redundant-commits master &&
-       git show -s --format='%s' >actual &&
+       git show -s --format=%s >actual &&
        echo "add file2 on master" >expect &&
        test_cmp expect actual
 '
index df921d1f33df34dd2b2631580f8d53b18270662a..a5b6a5f3310316d289aee0961fcb6a2ae26fca99 100755 (executable)
@@ -10,17 +10,15 @@ test_expect_success 'Initialize repository' '
        git commit -m a
 '
 
-test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' '
+test_expect_success 'Setup rename across paths each below D/F conflicts' '
        mkdir b &&
-       ln -s ../a b/a &&
-       git add b &&
+       test_ln_s_add ../a b/a &&
        git commit -m b &&
 
        git checkout -b branch &&
        rm b/a &&
-       mv a b/a &&
-       ln -s b/a a &&
-       git add . &&
+       git mv a b/a &&
+       test_ln_s_add b/a a &&
        git commit -m swap &&
 
        >f1 &&
@@ -28,7 +26,7 @@ test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts
        git commit -m f1
 '
 
-test_expect_success SYMLINKS 'Cherry-pick succeeds with rename across D/F conflicts' '
+test_expect_success 'Cherry-pick succeeds with rename across D/F conflicts' '
        git reset --hard &&
        git checkout master^0 &&
        git cherry-pick branch
index 0c44e9f5d04af8d038a1e78ed5fdf892ce4dd5b5..5c87b55645e249929585c2ed0e7bcf98a1b62da3 100755 (executable)
@@ -687,4 +687,100 @@ test_expect_failure SYMLINKS 'rm across a symlinked leading path (w/ index)' '
        test_path_is_file e/f
 '
 
+test_expect_success 'setup for testing rm messages' '
+       >bar.txt &&
+       >foo.txt &&
+       git add bar.txt foo.txt
+'
+
+test_expect_success 'rm files with different staged content' '
+       cat >expect <<-\EOF &&
+       error: the following files have staged content different from both the
+       file and the HEAD:
+           bar.txt
+           foo.txt
+       (use -f to force removal)
+       EOF
+       echo content1 >foo.txt &&
+       echo content1 >bar.txt &&
+       test_must_fail git rm foo.txt bar.txt 2>actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'rm files with different staged content without hints' '
+       cat >expect <<-\EOF &&
+       error: the following files have staged content different from both the
+       file and the HEAD:
+           bar.txt
+           foo.txt
+       EOF
+       echo content2 >foo.txt &&
+       echo content2 >bar.txt &&
+       test_must_fail git -c advice.rmhints=false rm foo.txt bar.txt 2>actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with local modification' '
+       cat >expect <<-\EOF &&
+       error: the following file has local modifications:
+           foo.txt
+       (use --cached to keep the file, or -f to force removal)
+       EOF
+       git commit -m "testing rm 3" &&
+       echo content3 >foo.txt &&
+       test_must_fail git rm foo.txt 2>actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with local modification without hints' '
+       cat >expect <<-\EOF &&
+       error: the following file has local modifications:
+           bar.txt
+       EOF
+       echo content4 >bar.txt &&
+       test_must_fail git -c advice.rmhints=false rm bar.txt 2>actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with changes in the index' '
+       cat >expect <<-\EOF &&
+       error: the following file has changes staged in the index:
+           foo.txt
+       (use --cached to keep the file, or -f to force removal)
+       EOF
+       git reset --hard &&
+       echo content5 >foo.txt &&
+       git add foo.txt &&
+       test_must_fail git rm foo.txt 2>actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'rm file with changes in the index without hints' '
+       cat >expect <<-\EOF &&
+       error: the following file has changes staged in the index:
+           foo.txt
+       EOF
+       test_must_fail git -c advice.rmhints=false rm foo.txt 2>actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'rm files with two different errors' '
+       cat >expect <<-\EOF &&
+       error: the following file has staged content different from both the
+       file and the HEAD:
+           foo1.txt
+       (use -f to force removal)
+       error: the following file has changes staged in the index:
+           bar1.txt
+       (use --cached to keep the file, or -f to force removal)
+       EOF
+       echo content >foo1.txt &&
+       git add foo1.txt &&
+       echo content6 >foo1.txt &&
+       echo content6 >bar1.txt &&
+       git add bar1.txt &&
+       test_must_fail git rm bar1.txt foo1.txt 2>actual &&
+       test_i18ncmp expect actual
+'
+
 test_done
index 874b3a6444ac64182eecf2b6aca4411fc90097e2..aab86e838b806f9bb2289f536f4ba9324d75da0a 100755 (executable)
@@ -30,10 +30,9 @@ test_expect_success \
         *) echo fail; git ls-files --stage xfoo1; (exit 1);;
         esac'
 
-test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
+test_expect_success 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo1 &&
-       ln -s foo xfoo1 &&
-       git add xfoo1 &&
+       test_ln_s_add foo xfoo1 &&
        case "`git ls-files --stage xfoo1`" in
        120000" "*xfoo1) echo pass;;
        *) echo fail; git ls-files --stage xfoo1; (exit 1);;
@@ -51,21 +50,19 @@ test_expect_success \
         *) echo fail; git ls-files --stage xfoo2; (exit 1);;
         esac'
 
-test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
+test_expect_success 'git add: filemode=0 should not get confused by symlink' '
        rm -f xfoo2 &&
-       ln -s foo xfoo2 &&
-       git update-index --add xfoo2 &&
+       test_ln_s_add foo xfoo2 &&
        case "`git ls-files --stage xfoo2`" in
        120000" "*xfoo2) echo pass;;
        *) echo fail; git ls-files --stage xfoo2; (exit 1);;
        esac
 '
 
-test_expect_success SYMLINKS \
+test_expect_success \
        'git update-index --add: Test that executable bit is not used...' \
        'git config core.filemode 0 &&
-        ln -s xfoo2 xfoo3 &&
-        git update-index --add xfoo3 &&
+        test_ln_s_add xfoo2 xfoo3 &&   # runs git update-index --add
         case "`git ls-files --stage xfoo3`" in
         120000" "*xfoo3) echo pass;;
         *) echo fail; git ls-files --stage xfoo3; (exit 1);;
index 37ddabba2d60956a4d8da63585bec5a622e12a5d..4bf1dbe9c9f3ffeb1151be07bf75dcd872167916 100755 (executable)
@@ -34,11 +34,47 @@ test_expect_success 'no encoding header for base case' '
        test z = "z$E"
 '
 
-test_expect_failure 'UTF-16 refused because of NULs' '
+test_expect_success 'UTF-16 refused because of NULs' '
        echo UTF-16 >F &&
-       git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt
+       test_must_fail git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt
 '
 
+test_expect_success 'UTF-8 invalid characters refused' '
+       test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+       echo "UTF-8 characters" >F &&
+       printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \
+               >"$HOME/invalid" &&
+       git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+       grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 overlong sequences rejected' '
+       test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+       rm -f "$HOME/stderr" "$HOME/invalid" &&
+       echo "UTF-8 overlong" >F &&
+       printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \
+               >"$HOME/invalid" &&
+       git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+       grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 non-characters refused' '
+       test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+       echo "UTF-8 non-character 1" >F &&
+       printf "Commit message\n\nNon-character:\364\217\277\276\n" \
+               >"$HOME/invalid" &&
+       git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+       grep "did not conform" "$HOME"/stderr
+'
+
+test_expect_success 'UTF-8 non-characters refused' '
+       test_when_finished "rm -f $HOME/stderr $HOME/invalid" &&
+       echo "UTF-8 non-character 2." >F &&
+       printf "Commit message\n\nNon-character:\357\267\220\n" \
+               >"$HOME/invalid" &&
+       git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr &&
+       grep "did not conform" "$HOME"/stderr
+'
 
 for H in ISO8859-1 eucJP ISO-2022-JP
 do
diff --git a/t/t3900/UTF-16.txt b/t/t3900/UTF-16.txt
new file mode 100644 (file)
index 0000000..2257f05
Binary files /dev/null and b/t/t3900/UTF-16.txt differ
index 5dfbda7491aceaa64215ac94bdd416f9b93f55d3..debda7a678277e1198ba6620210b66984caf03a2 100755 (executable)
@@ -200,17 +200,17 @@ test_expect_success 'apply -q is quiet' '
        echo foo > file &&
        git stash &&
        git stash apply -q > output.out 2>&1 &&
-       test ! -s output.out
+       test_must_be_empty output.out
 '
 
 test_expect_success 'save -q is quiet' '
        git stash save --quiet > output.out 2>&1 &&
-       test ! -s output.out
+       test_must_be_empty output.out
 '
 
 test_expect_success 'pop -q is quiet' '
        git stash pop -q > output.out 2>&1 &&
-       test ! -s output.out
+       test_must_be_empty output.out
 '
 
 test_expect_success 'pop -q --index works and is quiet' '
@@ -219,13 +219,13 @@ test_expect_success 'pop -q --index works and is quiet' '
        git stash save --quiet &&
        git stash pop -q --index > output.out 2>&1 &&
        test foo = "$(git show :file)" &&
-       test ! -s output.out
+       test_must_be_empty output.out
 '
 
 test_expect_success 'drop -q is quiet' '
        git stash &&
        git stash drop -q > output.out 2>&1 &&
-       test ! -s output.out
+       test_must_be_empty output.out
 '
 
 test_expect_success 'stash -k' '
@@ -336,41 +336,58 @@ test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
 
 # This test creates a commit with a symlink used for the following tests
 
-test_expect_success SYMLINKS 'stash symlink to file' '
+test_expect_success 'stash symlink to file' '
        git reset --hard &&
-       ln -s file filelink &&
-       git add filelink &&
+       test_ln_s_add file filelink &&
        git commit -m "Add symlink" &&
        rm filelink &&
        cp file filelink &&
-       git stash save "symlink to file" &&
+       git stash save "symlink to file"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
        test -h filelink &&
-       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
        git stash apply &&
        ! test -h filelink &&
        test bar = "$(cat file)"
 '
 
-test_expect_success SYMLINKS 'stash symlink to file (stage rm)' '
+test_expect_success 'stash symlink to file (stage rm)' '
        git reset --hard &&
        git rm filelink &&
        cp file filelink &&
-       git stash save "symlink to file (stage rm)" &&
+       git stash save "symlink to file (stage rm)"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
        test -h filelink &&
-       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
        git stash apply &&
        ! test -h filelink &&
        test bar = "$(cat file)"
 '
 
-test_expect_success SYMLINKS 'stash symlink to file (full stage)' '
+test_expect_success 'stash symlink to file (full stage)' '
        git reset --hard &&
        rm filelink &&
        cp file filelink &&
        git add filelink &&
-       git stash save "symlink to file (full stage)" &&
+       git stash save "symlink to file (full stage)"
+'
+
+test_expect_success SYMLINKS 'this must have re-created the symlink' '
        test -h filelink &&
-       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+       case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac
+'
+
+test_expect_success 'unstash must re-create the file' '
        git stash apply &&
        ! test -h filelink &&
        test bar = "$(cat file)"
@@ -637,4 +654,23 @@ test_expect_success 'stash where working directory contains "HEAD" file' '
        test_cmp output expect
 '
 
+test_expect_success 'store called with invalid commit' '
+       test_must_fail git stash store foo
+'
+
+test_expect_success 'store updates stash ref and reflog' '
+       git stash clear &&
+       git reset --hard &&
+       echo quux >bazzy &&
+       git add bazzy &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       ! grep quux bazzy &&
+       git stash store -m quuxery $STASH_ID &&
+       test $(cat .git/refs/stash) = $STASH_ID &&
+       grep $STASH_ID .git/logs/refs/stash &&
+       git stash pop &&
+       grep quux bazzy
+'
+
 test_done
index 6ddd46915d2757bb5a40057e6850a4f72cd4dafb..8de36b7d12227a38769f658983363a5ebd52fd68 100755 (executable)
@@ -15,17 +15,17 @@ line 3'
 cat path0 >path1
 chmod +x path1
 
-test_expect_success \
-    'update-index --add two files with and without +x.' \
-    'git update-index --add path0 path1'
+test_expect_success 'update-index --add two files with and without +x.' '
+       git update-index --add path0 path1
+'
 
 mv path0 path0-
 sed -e 's/line/Line/' <path0- >path0
 chmod +x path0
 rm -f path1
-test_expect_success \
-    'git diff-files -p after editing work tree.' \
-    'git diff-files -p >current'
+test_expect_success 'git diff-files -p after editing work tree.' '
+       git diff-files -p >actual
+'
 
 # that's as far as it comes
 if [ "$(git config --get core.filemode)" = false ]
@@ -55,8 +55,38 @@ deleted file mode 100755
 -line 3
 EOF
 
-test_expect_success \
-    'validate git diff-files -p output.' \
-    'compare_diff_patch current expected'
+test_expect_success 'validate git diff-files -p output.' '
+       compare_diff_patch expected actual
+'
+
+test_expect_success 'git diff-files -s after editing work tree' '
+       git diff-files -s >actual 2>err &&
+       test_must_be_empty actual &&
+       test_must_be_empty err
+'
+
+test_expect_success 'git diff-files --no-patch as synonym for -s' '
+       git diff-files --no-patch >actual 2>err &&
+       test_must_be_empty actual &&
+       test_must_be_empty err
+'
+
+test_expect_success 'git diff-files --no-patch --patch shows the patch' '
+       git diff-files --no-patch --patch >actual &&
+       compare_diff_patch expected actual
+'
+
+test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' '
+       git diff-files --no-patch --patch-with-raw >actual &&
+       grep -q "^:100644 100755 .* 0000000000000000000000000000000000000000 M  path0\$" actual &&
+       tail -n +4 actual >actual-patch &&
+       compare_diff_patch expected actual-patch
+'
+
+test_expect_success 'git diff-files --patch --no-patch does not show the patch' '
+       git diff-files --patch --no-patch >actual 2>err &&
+       test_must_be_empty actual &&
+       test_must_be_empty err
+'
 
 test_done
index 73b4a24f5ef676b8fa9fb3dc83183437710853c3..27e98a8f9d6c858468904542c500236b9e9f99c2 100755 (executable)
@@ -99,11 +99,11 @@ test_expect_success \
     'validate result of -B -M (#4)' \
     'compare_diff_raw expected current'
 
-test_expect_success SYMLINKS \
+test_expect_success \
     'make file0 into something completely different' \
     'rm -f file0 &&
-     ln -s frotz file0 &&
-     git update-index file0 file1'
+     test_ln_s_add frotz file0 &&
+     git update-index file1'
 
 test_expect_success \
     'run diff with -B' \
@@ -114,7 +114,7 @@ cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100  file1
 EOF
 
-test_expect_success SYMLINKS \
+test_expect_success \
     'validate result of -B (#5)' \
     'compare_diff_raw expected current'
 
@@ -129,7 +129,7 @@ cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R     file0   file1
 EOF
 
-test_expect_success SYMLINKS \
+test_expect_success \
     'validate result of -B -M (#6)' \
     'compare_diff_raw expected current'
 
@@ -144,7 +144,7 @@ cat >expected <<\EOF
 :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M     file1
 EOF
 
-test_expect_success SYMLINKS \
+test_expect_success \
     'validate result of -M (#7)' \
     'compare_diff_raw expected current'
 
index f0d5041c11581ec2d711c0ea3bfd479019814b78..13e7f621ab79f95cc7c3057d9de5710813049102 100755 (executable)
@@ -9,7 +9,7 @@ test_description='Test diff of symlinks.
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/diff-lib.sh
 
-test_expect_success SYMLINKS 'diff new symlink and file' '
+test_expect_success 'diff new symlink and file' '
        cat >expected <<-\EOF &&
        diff --git a/frotz b/frotz
        new file mode 120000
@@ -27,22 +27,25 @@ test_expect_success SYMLINKS 'diff new symlink and file' '
        @@ -0,0 +1 @@
        +xyzzy
        EOF
-       ln -s xyzzy frotz &&
-       echo xyzzy >nitfol &&
+
+       # the empty tree
        git update-index &&
        tree=$(git write-tree) &&
-       git update-index --add frotz nitfol &&
+
+       test_ln_s_add xyzzy frotz &&
+       echo xyzzy >nitfol &&
+       git update-index --add nitfol &&
        GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current &&
        compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff unchanged symlink and file'  '
+test_expect_success 'diff unchanged symlink and file'  '
        tree=$(git write-tree) &&
        git update-index frotz nitfol &&
        test -z "$(git diff-index --name-only $tree)"
 '
 
-test_expect_success SYMLINKS 'diff removed symlink and file' '
+test_expect_success 'diff removed symlink and file' '
        cat >expected <<-\EOF &&
        diff --git a/frotz b/frotz
        deleted file mode 120000
@@ -66,12 +69,18 @@ test_expect_success SYMLINKS 'diff removed symlink and file' '
        compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff identical, but newly created symlink and file' '
+test_expect_success 'diff identical, but newly created symlink and file' '
        >expected &&
        rm -f frotz nitfol &&
        echo xyzzy >nitfol &&
        test-chmtime +10 nitfol &&
-       ln -s xyzzy frotz &&
+       if test_have_prereq SYMLINKS
+       then
+               ln -s xyzzy frotz
+       else
+               printf xyzzy >frotz
+               # the symlink property propagates from the index
+       fi &&
        git diff-index -M -p $tree >current &&
        compare_diff_patch expected current &&
 
@@ -80,7 +89,7 @@ test_expect_success SYMLINKS 'diff identical, but newly created symlink and file
        compare_diff_patch expected current
 '
 
-test_expect_success SYMLINKS 'diff different symlink and file' '
+test_expect_success 'diff different symlink and file' '
        cat >expected <<-\EOF &&
        diff --git a/frotz b/frotz
        index 7c465af..df1db54 120000
@@ -100,7 +109,13 @@ test_expect_success SYMLINKS 'diff different symlink and file' '
        +yxyyz
        EOF
        rm -f frotz &&
-       ln -s yxyyz frotz &&
+       if test_have_prereq SYMLINKS
+       then
+               ln -s yxyyz frotz
+       else
+               printf yxyyz >frotz
+               # the symlink property propagates from the index
+       fi &&
        echo yxyyz >nitfol &&
        git diff-index -M -p $tree >current &&
        compare_diff_patch expected current
index 58d418098d793b284bb4a7222cf9709c45b26c90..668933bfb2c906ce9f8c74ab498f1986b56aced0 100755 (executable)
@@ -972,6 +972,49 @@ test_expect_success 'empty subject prefix does not have extra space' '
        test_cmp expect actual
 '
 
+test_expect_success '--from=ident notices bogus ident' '
+       test_must_fail git format-patch -1 --stdout --from=foo >patch
+'
+
+test_expect_success '--from=ident replaces author' '
+       git format-patch -1 --stdout --from="Me <me@example.com>" >patch &&
+       cat >expect <<-\EOF &&
+       From: Me <me@example.com>
+
+       From: A U Thor <author@example.com>
+
+       EOF
+       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       test_cmp expect patch.head
+'
+
+test_expect_success '--from uses committer ident' '
+       git format-patch -1 --stdout --from >patch &&
+       cat >expect <<-\EOF &&
+       From: C O Mitter <committer@example.com>
+
+       From: A U Thor <author@example.com>
+
+       EOF
+       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       test_cmp expect patch.head
+'
+
+test_expect_success 'in-body headers trigger content encoding' '
+       GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
+       test_when_finished "git reset --hard HEAD^" &&
+       git format-patch -1 --stdout --from >patch &&
+       cat >expect <<-\EOF &&
+       From: C O Mitter <committer@example.com>
+       Content-Type: text/plain; charset=UTF-8
+
+       From: éxötìc <author@example.com>
+
+       EOF
+       sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
+       test_cmp expect patch.head
+'
+
 append_signoff()
 {
        C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
index cc3db1304ef202a376e84360986568bb7408c701..3fb4b976a277e69cdbf73172caf335748be609a3 100755 (executable)
@@ -142,6 +142,351 @@ EOF
 git diff --ignore-space-at-eol > out
 test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
 
+test_expect_success 'ignore-blank-lines: only new lines' '
+       test_seq 5 >x &&
+       git update-index x &&
+       test_seq 5 | sed "/3/i \\
+" >x &&
+       git diff --ignore-blank-lines >out &&
+       >expect &&
+       test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: only new lines with space' '
+       test_seq 5 >x &&
+       git update-index x &&
+       test_seq 5 | sed "/3/i \ " >x &&
+       git diff -w --ignore-blank-lines >out &&
+       >expect &&
+       test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: after change' '
+       cat <<-\EOF >x &&
+       1
+       2
+
+       3
+       4
+       5
+
+       6
+       7
+       EOF
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+
+       1
+       2
+       3
+       4
+       5
+       6
+
+       7
+       EOF
+       git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,6 +1,7 @@
+       +change
+       +
+        1
+        2
+       -
+        3
+        4
+        5
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: before change' '
+       cat <<-\EOF >x &&
+       1
+       2
+
+       3
+       4
+       5
+       6
+       7
+       EOF
+       git update-index x &&
+       cat <<-\EOF >x &&
+
+       1
+       2
+       3
+       4
+       5
+
+       6
+       7
+       change
+       EOF
+       git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -4,5 +4,7 @@
+        3
+        4
+        5
+       +
+        6
+        7
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes' '
+       cat <<-\EOF >x &&
+       1
+       2
+       3
+       4
+       5
+
+
+       6
+       7
+       8
+       9
+       10
+       EOF
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+
+       3
+       4
+       5
+       6
+       7
+       8
+
+       9
+       10
+       change
+       EOF
+       git diff --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,5 +1,7 @@
+       +change
+        1
+        2
+       +
+        3
+        4
+        5
+       @@ -8,5 +8,7 @@
+        6
+        7
+        8
+       +
+        9
+        10
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '
+       test_seq 10 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+
+       3
+       4
+       5
+
+       6
+       7
+       8
+       9
+
+       10
+       change
+       EOF
+       git diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,10 +1,15 @@
+       +change
+        1
+        2
+       +
+        3
+        4
+        5
+       +
+        6
+        7
+        8
+        9
+       +
+        10
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: scattered spaces' '
+       test_seq 10 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+       3
+
+       4
+
+       5
+
+       6
+
+
+       7
+
+       8
+       9
+       10
+       change
+       EOF
+       git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,3 +1,4 @@
+       +change
+        1
+        2
+        3
+       @@ -8,3 +15,4 @@
+        8
+        9
+        10
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: spaces coalesce' '
+       test_seq 6 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+       3
+
+       4
+
+       5
+
+       6
+       change
+       EOF
+       git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,6 +1,11 @@
+       +change
+        1
+        2
+        3
+       +
+        4
+       +
+        5
+       +
+        6
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: mix changes and blank lines' '
+       test_seq 16 >x &&
+       git update-index x &&
+       cat <<-\EOF >x &&
+       change
+       1
+       2
+
+       3
+       4
+       5
+       change
+       6
+       7
+       8
+
+       9
+       10
+       11
+       change
+       12
+       13
+       14
+
+       15
+       16
+       change
+       EOF
+       git diff --ignore-blank-lines >out.tmp &&
+       cat <<-\EOF >expected &&
+       diff --git a/x b/x
+       --- a/x
+       +++ b/x
+       @@ -1,8 +1,11 @@
+       +change
+        1
+        2
+       +
+        3
+        4
+        5
+       +change
+        6
+        7
+        8
+       @@ -9,8 +13,11 @@
+        9
+        10
+        11
+       +change
+        12
+        13
+        14
+       +
+        15
+        16
+       +change
+       EOF
+       compare_diff_patch expected out.tmp
+'
+
 test_expect_success 'check mixed spaces and tabs in indent' '
 
        # This is indented with SP HT SP.
index 5d20acf436558da6c11214f431beb503bc89459a..55d549fcf441be927bc43b2c41107a90aa614c2a 100755 (executable)
@@ -4,44 +4,44 @@ test_description='typechange rename detection'
 
 . ./test-lib.sh
 
-test_expect_success SYMLINKS setup '
+test_expect_success setup '
 
        rm -f foo bar &&
        cat "$TEST_DIRECTORY"/../COPYING >foo &&
-       ln -s linklink bar &&
-       git add foo bar &&
+       test_ln_s_add linklink bar &&
+       git add foo &&
        git commit -a -m Initial &&
        git tag one &&
 
-       rm -f foo bar &&
+       git rm -f foo bar &&
        cat "$TEST_DIRECTORY"/../COPYING >bar &&
-       ln -s linklink foo &&
-       git add foo bar &&
+       test_ln_s_add linklink foo &&
+       git add bar &&
        git commit -a -m Second &&
        git tag two &&
 
-       rm -f foo bar &&
+       git rm -f foo bar &&
        cat "$TEST_DIRECTORY"/../COPYING >foo &&
        git add foo &&
        git commit -a -m Third &&
        git tag three &&
 
        mv foo bar &&
-       ln -s linklink foo &&
-       git add foo bar &&
+       test_ln_s_add linklink foo &&
+       git add bar &&
        git commit -a -m Fourth &&
        git tag four &&
 
        # This is purely for sanity check
 
-       rm -f foo bar &&
+       git rm -f foo bar &&
        cat "$TEST_DIRECTORY"/../COPYING >foo &&
        cat "$TEST_DIRECTORY"/../Makefile >bar &&
        git add foo bar &&
        git commit -a -m Fifth &&
        git tag five &&
 
-       rm -f foo bar &&
+       git rm -f foo bar &&
        cat "$TEST_DIRECTORY"/../Makefile >foo &&
        cat "$TEST_DIRECTORY"/../COPYING >bar &&
        git add foo bar &&
@@ -50,7 +50,7 @@ test_expect_success SYMLINKS setup '
 
 '
 
-test_expect_success SYMLINKS 'cross renames to be detected for regular files' '
+test_expect_success 'cross renames to be detected for regular files' '
 
        git diff-tree five six -r --name-status -B -M | sort >actual &&
        {
@@ -61,7 +61,7 @@ test_expect_success SYMLINKS 'cross renames to be detected for regular files' '
 
 '
 
-test_expect_success SYMLINKS 'cross renames to be detected for typechange' '
+test_expect_success 'cross renames to be detected for typechange' '
 
        git diff-tree one two -r --name-status -B -M | sort >actual &&
        {
@@ -72,7 +72,7 @@ test_expect_success SYMLINKS 'cross renames to be detected for typechange' '
 
 '
 
-test_expect_success SYMLINKS 'moves and renames' '
+test_expect_success 'moves and renames' '
 
        git diff-tree three four -r --name-status -B -M | sort >actual &&
        {
index 53ec330ce8a09cfde5b3b5e7661bb6dd457ba0db..f75f46f92d22451522d4676ff4ed709b49419481 100755 (executable)
@@ -139,12 +139,10 @@ index 0000000..67be421
 +frotz
 \ No newline at end of file
 EOF
-# make a symlink the hard way that works on symlink-challenged file systems
+
 test_expect_success 'textconv does not act on symlinks' '
-       printf frotz > file &&
-       git add file &&
-       git ls-files -s | sed -e s/100644/120000/ |
-               git update-index --index-info &&
+       rm -f file &&
+       test_ln_s_add frotz file &&
        git commit -m typechange &&
        git show >diff &&
        find_diff <diff >actual &&
index 1261dbbdf5869b671e36cd7128bfde61fd67b8de..1019d7b35fcb350761a44cf4ccd5ae156ce8d577 100755 (executable)
@@ -353,4 +353,52 @@ test_expect_failure 'combine diff coalesce three parents' '
        compare_diff_patch expected actual
 '
 
+# Test for a bug reported at
+# http://thread.gmane.org/gmane.comp.version-control.git/224410
+# where a delete lines were missing from combined diff output when they
+# occurred exactly before the context lines of a later change.
+test_expect_success 'combine diff missing delete bug' '
+       git commit -m initial --allow-empty &&
+       cat <<-\EOF >test &&
+       1
+       2
+       3
+       4
+       EOF
+       git add test &&
+       git commit -a -m side1 &&
+       git checkout -B side1 &&
+       git checkout HEAD^ &&
+       cat <<-\EOF >test &&
+       0
+       1
+       2
+       3
+       4modified
+       EOF
+       git add test &&
+       git commit -m side2 &&
+       git branch -f side2 &&
+       test_must_fail git merge --no-commit side1 &&
+       cat <<-\EOF >test &&
+       1
+       2
+       3
+       4modified
+       EOF
+       git add test &&
+       git commit -a -m merge &&
+       git diff-tree -c -p HEAD >actual.tmp &&
+       sed -e "1,/^@@@/d" < actual.tmp >actual &&
+       tr -d Q <<-\EOF >expected &&
+       - 0
+         1
+         2
+         3
+        -4
+        +4modified
+       EOF
+       compare_diff_patch expected actual
+'
+
 test_done
index 32d4a604250d55e9221a416638c8c8c4fdd6ff87..1751c8330709c56a4b7019513e30434741847b95 100755 (executable)
@@ -1,6 +1,7 @@
 #!/bin/sh
 #
 # Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
 #
 
 test_description='Support for verbose submodule differences in git diff
@@ -10,6 +11,9 @@ This test tries to verify the sanity of the --submodule option of git diff.
 
 . ./test-lib.sh
 
+# String "added" in German (translated with Google Translate), encoded in UTF-8,
+# used in sample commit log messages in add_file() function below.
+added=$(printf "hinzugef\303\274gt")
 add_file () {
        (
                cd "$1" &&
@@ -19,7 +23,8 @@ add_file () {
                        echo "$name" >"$name" &&
                        git add "$name" &&
                        test_tick &&
-                       git commit -m "Add $name" || exit
+                       msg_added_iso88591=$(echo "Add $name ($added $name)" | iconv -f utf-8 -t iso8859-1) &&
+                       git -c 'i18n.commitEncoding=iso8859-1' commit -m "$msg_added_iso88591"
                done >/dev/null &&
                git rev-parse --short --verify HEAD
        )
@@ -93,7 +98,7 @@ test_expect_success 'modified submodule(forward)' '
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head1..$head2:
-         > Add foo3
+         > Add foo3 ($added foo3)
        EOF
        test_cmp expected actual
 '
@@ -102,7 +107,7 @@ test_expect_success 'modified submodule(forward)' '
        git diff --submodule=log >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head1..$head2:
-         > Add foo3
+         > Add foo3 ($added foo3)
        EOF
        test_cmp expected actual
 '
@@ -111,7 +116,7 @@ test_expect_success 'modified submodule(forward) --submodule' '
        git diff --submodule >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head1..$head2:
-         > Add foo3
+         > Add foo3 ($added foo3)
        EOF
        test_cmp expected actual
 '
@@ -142,8 +147,8 @@ test_expect_success 'modified submodule(backward)' '
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head2..$head3 (rewind):
-         < Add foo3
-         < Add foo2
+         < Add foo3 ($added foo3)
+         < Add foo2 ($added foo2)
        EOF
        test_cmp expected actual
 '
@@ -153,10 +158,10 @@ test_expect_success 'modified submodule(backward and forward)' '
        git diff-index -p --submodule=log HEAD >actual &&
        cat >expected <<-EOF &&
        Submodule sm1 $head2...$head4:
-         > Add foo5
-         > Add foo4
-         < Add foo3
-         < Add foo2
+         > Add foo5 ($added foo5)
+         > Add foo4 ($added foo4)
+         < Add foo3 ($added foo3)
+         < Add foo2 ($added foo2)
        EOF
        test_cmp expected actual
 '
index 7c398432bad761c1b38c6d15548bed0c389221fd..1618a6dbc7c718677cf669b5f6fc96f2682992fb 100755 (executable)
@@ -86,6 +86,20 @@ test_expect_success 'apply --index from subdir of toplevel' '
        test_cmp expected sub/dir/file
 '
 
+test_expect_success 'apply half-broken patch from subdir of toplevel' '
+       (
+               cd sub/dir &&
+               test_must_fail git apply <<-EOF
+               --- sub/dir/file
+               +++ sub/dir/file
+               @@ -1,0 +1,0 @@
+               --- file_in_root
+               +++ file_in_root
+               @@ -1,0 +1,0 @@
+               EOF
+       )
+'
+
 test_expect_success 'apply from .git dir' '
        cp postimage expected &&
        cp preimage .git/file &&
index f12826fb09729d5753f3f6c9511dab5062f0aae1..ebadbc347fc4fd9d435a366e46e3e1cebba81b83 100755 (executable)
@@ -9,20 +9,19 @@ test_description='git apply should not get confused with type changes.
 
 . ./test-lib.sh
 
-test_expect_success SYMLINKS 'setup repository and commits' '
+test_expect_success 'setup repository and commits' '
        echo "hello world" > foo &&
        echo "hi planet" > bar &&
        git update-index --add foo bar &&
        git commit -m initial &&
        git branch initial &&
        rm -f foo &&
-       ln -s bar foo &&
-       git update-index foo &&
+       test_ln_s_add bar foo &&
        git commit -m "foo symlinked to bar" &&
        git branch foo-symlinked-to-bar &&
-       rm -f foo &&
+       git rm -f foo &&
        echo "how far is the sun?" > foo &&
-       git update-index foo &&
+       git update-index --add foo &&
        git commit -m "foo back to file" &&
        git branch foo-back-to-file &&
        printf "\0" > foo &&
@@ -42,7 +41,7 @@ test_expect_success SYMLINKS 'setup repository and commits' '
        git branch foo-baz-renamed-from-foo
        '
 
-test_expect_success SYMLINKS 'file renamed from foo to foo/baz' '
+test_expect_success 'file renamed from foo to foo/baz' '
        git checkout -f initial &&
        git diff-tree -M -p HEAD foo-baz-renamed-from-foo > patch &&
        git apply --index < patch
@@ -50,7 +49,7 @@ test_expect_success SYMLINKS 'file renamed from foo to foo/baz' '
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'file renamed from foo/baz to foo' '
+test_expect_success 'file renamed from foo/baz to foo' '
        git checkout -f foo-baz-renamed-from-foo &&
        git diff-tree -M -p HEAD initial > patch &&
        git apply --index < patch
@@ -58,7 +57,7 @@ test_expect_success SYMLINKS 'file renamed from foo/baz to foo' '
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'directory becomes file' '
+test_expect_success 'directory becomes file' '
        git checkout -f foo-becomes-a-directory &&
        git diff-tree -p HEAD initial > patch &&
        git apply --index < patch
@@ -66,7 +65,7 @@ test_expect_success SYMLINKS 'directory becomes file' '
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'file becomes directory' '
+test_expect_success 'file becomes directory' '
        git checkout -f initial &&
        git diff-tree -p HEAD foo-becomes-a-directory > patch &&
        git apply --index < patch
@@ -74,7 +73,7 @@ test_expect_success SYMLINKS 'file becomes directory' '
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'file becomes symlink' '
+test_expect_success 'file becomes symlink' '
        git checkout -f initial &&
        git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
        git apply --index < patch
@@ -82,21 +81,21 @@ test_expect_success SYMLINKS 'file becomes symlink' '
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'symlink becomes file' '
+test_expect_success 'symlink becomes file' '
        git checkout -f foo-symlinked-to-bar &&
        git diff-tree -p HEAD foo-back-to-file > patch &&
        git apply --index < patch
        '
 test_debug 'cat patch'
 
-test_expect_success SYMLINKS 'binary file becomes symlink' '
+test_expect_success 'binary file becomes symlink' '
        git checkout -f foo-becomes-binary &&
        git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch &&
        git apply --index < patch
        '
 test_debug 'cat patch'
 
-test_expect_success SYMLINKS 'symlink becomes binary file' '
+test_expect_success 'symlink becomes binary file' '
        git checkout -f foo-symlinked-to-bar &&
        git diff-tree -p --binary HEAD foo-becomes-binary > patch &&
        git apply --index < patch
@@ -104,7 +103,7 @@ test_expect_success SYMLINKS 'symlink becomes binary file' '
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'symlink becomes directory' '
+test_expect_success 'symlink becomes directory' '
        git checkout -f foo-symlinked-to-bar &&
        git diff-tree -p HEAD foo-becomes-a-directory > patch &&
        git apply --index < patch
@@ -112,7 +111,7 @@ test_expect_success SYMLINKS 'symlink becomes directory' '
 test_debug 'cat patch'
 
 
-test_expect_success SYMLINKS 'directory becomes symlink' '
+test_expect_success 'directory becomes symlink' '
        git checkout -f foo-becomes-a-directory &&
        git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
        git apply --index < patch
index 7674dd2ec9d6f14a0f2181ebf415378521f90fa4..872fcda6cb6dce98ec360c41cb6ae1220193ca48 100755 (executable)
@@ -9,18 +9,16 @@ test_description='git apply symlinks and partial files
 
 . ./test-lib.sh
 
-test_expect_success SYMLINKS setup '
+test_expect_success setup '
 
-       ln -s path1/path2/path3/path4/path5 link1 &&
-       git add link? &&
+       test_ln_s_add path1/path2/path3/path4/path5 link1 &&
        git commit -m initial &&
 
        git branch side &&
 
        rm -f link? &&
 
-       ln -s htap6 link1 &&
-       git update-index link? &&
+       test_ln_s_add htap6 link1 &&
        git commit -m second &&
 
        git diff-tree -p HEAD^ HEAD >patch  &&
@@ -37,7 +35,7 @@ test_expect_success SYMLINKS 'apply symlink patch' '
 
 '
 
-test_expect_success SYMLINKS 'apply --index symlink patch' '
+test_expect_success 'apply --index symlink patch' '
 
        git checkout -f side &&
        git apply --index patch &&
index 39407376ba7da1cb8256bcd3041a83147cd9eee5..70b3a06e1dc7a92ac5a0806854e100af02c2346b 100755 (executable)
@@ -10,11 +10,11 @@ lecho () {
        done
 }
 
-test_expect_success SYMLINKS setup '
+test_expect_success setup '
 
        mkdir -p arch/i386/boot arch/x86_64 &&
        lecho 1 2 3 4 5 >arch/i386/boot/Makefile &&
-       ln -s ../i386/boot arch/x86_64/boot &&
+       test_ln_s_add ../i386/boot arch/x86_64/boot &&
        git add . &&
        test_tick &&
        git commit -m initial &&
@@ -31,7 +31,7 @@ test_expect_success SYMLINKS setup '
 
 '
 
-test_expect_success SYMLINKS apply '
+test_expect_success apply '
 
        git checkout test &&
        git diff --exit-code test &&
@@ -40,7 +40,7 @@ test_expect_success SYMLINKS apply '
 
 '
 
-test_expect_success SYMLINKS 'check result' '
+test_expect_success 'check result' '
 
        git diff --exit-code master &&
        git diff --exit-code --cached master &&
index 12f6b027acbccae3fe524906970ff139ddccfed0..5edb79a058f407f47353041afb8e7851b066a517 100755 (executable)
@@ -147,7 +147,7 @@ test_expect_success 'am applies patch correctly' '
        git checkout first &&
        test_tick &&
        git am <patch1 &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git diff --exit-code second &&
        test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@ -158,7 +158,7 @@ test_expect_success 'am applies patch e-mail not in a mbox' '
        git reset --hard &&
        git checkout first &&
        git am patch1.eml &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git diff --exit-code second &&
        test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@ -169,7 +169,7 @@ test_expect_success 'am applies patch e-mail not in a mbox with CRLF' '
        git reset --hard &&
        git checkout first &&
        git am patch1-crlf.eml &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git diff --exit-code second &&
        test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@ -180,7 +180,7 @@ test_expect_success 'am applies patch e-mail with preceding whitespace' '
        git reset --hard &&
        git checkout first &&
        git am patch1-ws.eml &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git diff --exit-code second &&
        test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
@@ -206,7 +206,7 @@ test_expect_success 'am changes committer and keeps author' '
        git reset --hard &&
        git checkout first &&
        git am patch2 &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
        git diff --exit-code master..HEAD &&
        git diff --exit-code master^..HEAD^ &&
@@ -258,7 +258,7 @@ test_expect_success 'am --keep really keeps the subject' '
        git reset --hard &&
        git checkout HEAD^ &&
        git am --keep patch4 &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git cat-file commit HEAD >actual &&
        grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual
 '
@@ -268,7 +268,7 @@ test_expect_success 'am --keep-non-patch really keeps the non-patch part' '
        git reset --hard &&
        git checkout HEAD^ &&
        git am --keep-non-patch patch4 &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git cat-file commit HEAD >actual &&
        grep "^\[foo\] third" actual
 '
@@ -283,7 +283,7 @@ test_expect_success 'am -3 falls back to 3-way merge' '
        test_tick &&
        git commit -m "copied stuff" &&
        git am -3 lorem-move.patch &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git diff --exit-code lorem
 '
 
@@ -297,7 +297,7 @@ test_expect_success 'am -3 -p0 can read --no-prefix patch' '
        test_tick &&
        git commit -m "copied stuff" &&
        git am -3 -p0 lorem-zero.patch &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git diff --exit-code lorem
 '
 
@@ -307,7 +307,7 @@ test_expect_success 'am can rename a file' '
        git reset --hard &&
        git checkout lorem^0 &&
        git am rename.patch &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git update-index --refresh &&
        git diff --exit-code rename
 '
@@ -318,7 +318,7 @@ test_expect_success 'am -3 can rename a file' '
        git reset --hard &&
        git checkout lorem^0 &&
        git am -3 rename.patch &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git update-index --refresh &&
        git diff --exit-code rename
 '
@@ -329,7 +329,7 @@ test_expect_success 'am -3 can rename a file after falling back to 3-way merge'
        git reset --hard &&
        git checkout lorem^0 &&
        git am -3 rename-add.patch &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git update-index --refresh &&
        git diff --exit-code rename
 '
@@ -358,11 +358,17 @@ test_expect_success 'am pauses on conflict' '
 test_expect_success 'am --skip works' '
        echo goodbye >expected &&
        git am --skip &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git diff --exit-code lorem2^^ -- file &&
        test_cmp expected another
 '
 
+test_expect_success 'am --abort removes a stray directory' '
+       mkdir .git/rebase-apply &&
+       git am --abort &&
+       test_path_is_missing .git/rebase-apply
+'
+
 test_expect_success 'am --resolved works' '
        echo goodbye >expected &&
        rm -fr .git/rebase-apply &&
@@ -373,7 +379,7 @@ test_expect_success 'am --resolved works' '
        echo resolved >>file &&
        git add file &&
        git am --resolved &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        test_cmp expected another
 '
 
@@ -382,7 +388,7 @@ test_expect_success 'am takes patches from a Pine mailbox' '
        git reset --hard &&
        git checkout first &&
        cat pine patch1 | git am &&
-       ! test -d .git/rebase-apply &&
+       test_path_is_missing .git/rebase-apply &&
        git diff --exit-code master^..HEAD
 '
 
@@ -391,7 +397,7 @@ test_expect_success 'am fails on mail without patch' '
        git reset --hard &&
        test_must_fail git am <failmail &&
        git am --abort &&
-       ! test -d .git/rebase-apply
+       test_path_is_missing .git/rebase-apply
 '
 
 test_expect_success 'am fails on empty patch' '
@@ -400,7 +406,7 @@ test_expect_success 'am fails on empty patch' '
        echo "---" >>failmail &&
        test_must_fail git am <failmail &&
        git am --skip &&
-       ! test -d .git/rebase-apply
+       test_path_is_missing .git/rebase-apply
 '
 
 test_expect_success 'am works from stdin in subdirectory' '
index 9243a979933997286bb6004bd7b224bad1bbc5cc..cb03d287698f110b66e69636348caccdbe6a94f8 100755 (executable)
@@ -530,6 +530,20 @@ test_expect_success 'show added path under "--follow -M"' '
        )
 '
 
+test_expect_success 'git log -c --follow' '
+       test_create_repo follow-c &&
+       (
+               cd follow-c &&
+               test_commit initial file original &&
+               git rm file &&
+               test_commit rename file2 original &&
+               git reset --hard initial &&
+               test_commit modify file foo &&
+               git merge -m merge rename &&
+               git log -c --follow file2
+       )
+'
+
 cat >expect <<\EOF
 *   commit COMMIT_OBJECT_NAME
 |\  Merge: MERGE_PARENTS
index 842b7549ec3ed6b1fa268a96d085048d2f0b7793..0dd8b65d7cdec2ada739d7e0b524fd3c66dd5ef4 100755 (executable)
@@ -13,6 +13,11 @@ fuzz_blame () {
 }
 
 test_expect_success setup '
+       cat >contacts <<-\EOF &&
+       A U Thor <author@example.com>
+       nick1 <bugs@company.xx>
+       EOF
+
        echo one >one &&
        git add one &&
        test_tick &&
@@ -23,6 +28,44 @@ test_expect_success setup '
        git commit --author "nick1 <bugs@company.xx>" -m second
 '
 
+test_expect_success 'check-mailmap no arguments' '
+       test_must_fail git check-mailmap
+'
+
+test_expect_success 'check-mailmap arguments' '
+       cat >expect <<-\EOF &&
+       A U Thor <author@example.com>
+       nick1 <bugs@company.xx>
+       EOF
+       git check-mailmap \
+               "A U Thor <author@example.com>" \
+               "nick1 <bugs@company.xx>" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin' '
+       cat >expect <<-\EOF &&
+       A U Thor <author@example.com>
+       nick1 <bugs@company.xx>
+       EOF
+       git check-mailmap --stdin <contacts >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin arguments' '
+       cat >expect <<-\EOF &&
+       Internal Guy <bugs@company.xy>
+       EOF
+       cat <contacts >>expect &&
+       git check-mailmap --stdin "Internal Guy <bugs@company.xy>" \
+               <contacts >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap bogus contact' '
+       test_must_fail git check-mailmap bogus
+'
+
 cat >expect <<\EOF
 A U Thor (1):
       initial
@@ -159,7 +202,8 @@ test_expect_success 'setup mailmap blob tests' '
        Blob Guy <author@example.com>
        Blob Guy <bugs@company.xx>
        EOF
-       git add just-bugs both &&
+       printf "Tricky Guy <author@example.com>" >no-newline &&
+       git add just-bugs both no-newline &&
        git commit -m "my mailmaps" &&
        echo "Repo Guy <author@example.com>" >.mailmap &&
        echo "Internal Guy <author@example.com>" >internal.map
@@ -243,10 +287,41 @@ test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' '
        )
 '
 
+test_expect_success 'mailmap.blob can handle blobs without trailing newline' '
+       cat >expect <<-\EOF &&
+       Tricky Guy (1):
+             initial
+
+       nick1 (1):
+             second
+
+       EOF
+       git -c mailmap.blob=map:no-newline shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'cleanup after mailmap.blob tests' '
        rm -f .mailmap
 '
 
+test_expect_success 'single-character name' '
+       echo "     1    A <author@example.com>" >expect &&
+       echo "     1    nick1 <bugs@company.xx>" >>expect &&
+       echo "A <author@example.com>" >.mailmap &&
+       test_when_finished "rm .mailmap" &&
+       git shortlog -es HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'preserve canonical email case' '
+       echo "     1    A U Thor <AUTHOR@example.com>" >expect &&
+       echo "     1    nick1 <bugs@company.xx>" >>expect &&
+       echo "<AUTHOR@example.com> <author@example.com>" >.mailmap &&
+       test_when_finished "rm .mailmap" &&
+       git shortlog -es HEAD >actual &&
+       test_cmp expect actual
+'
+
 # Extended mailmap configurations should give us the following output for shortlog
 cat >expect <<\EOF
 A U Thor <author@example.com> (1):
@@ -409,4 +484,15 @@ test_expect_success 'Blame output (complex mapping)' '
        test_cmp expect actual.fuzz
 '
 
+cat >expect <<\EOF
+Some Dude <some@dude.xx>
+EOF
+
+test_expect_success 'commit --author honors mailmap' '
+       test_must_fail git commit --author "nick" --allow-empty -meight &&
+       git commit --author "Some Dude" --allow-empty -meight &&
+       git show --pretty=format:"%an <%ae>%n" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 98a43d457a3de96fd9aa728610db7d8b3fdd241d..fb000411395d50639334c3c0395b2b975aa4bc12 100755 (executable)
@@ -1,20 +1,38 @@
 #!/bin/sh
 #
 # Copyright (c) 2010, Will Palmer
+# Copyright (c) 2011, Alexey Shumkin (+ non-UTF-8 commit encoding tests)
 #
 
 test_description='Test pretty formats'
 . ./test-lib.sh
 
+sample_utf8_part=$(printf "f\303\244ng")
+
+commit_msg () {
+       # String "initial. initial" partly in German
+       # (translated with Google Translate),
+       # encoded in UTF-8, used as a commit log message below.
+       msg="initial. an${sample_utf8_part}lich\n"
+       if test -n "$1"
+       then
+               printf "$msg" | iconv -f utf-8 -t "$1"
+       else
+               printf "$msg"
+       fi
+}
+
 test_expect_success 'set up basic repos' '
        >foo &&
        >bar &&
        git add foo &&
        test_tick &&
-       git commit -m initial &&
+       git config i18n.commitEncoding iso8859-1 &&
+       git commit -m "$(commit_msg iso8859-1)" &&
        git add bar &&
        test_tick &&
-       git commit -m "add bar"
+       git commit -m "add bar" &&
+       git config --unset i18n.commitEncoding
 '
 
 test_expect_success 'alias builtin format' '
@@ -38,6 +56,20 @@ test_expect_success 'alias user-defined format' '
        test_cmp expected actual
 '
 
+test_expect_success 'alias user-defined tformat with %s (iso8859-1 encoding)' '
+       git config i18n.logOutputEncoding iso8859-1 &&
+       git log --oneline >expected-s &&
+       git log --pretty="tformat:%h %s" >actual-s &&
+       git config --unset i18n.logOutputEncoding &&
+       test_cmp expected-s actual-s
+'
+
+test_expect_success 'alias user-defined tformat with %s (utf-8 encoding)' '
+       git log --oneline >expected-s &&
+       git log --pretty="tformat:%h %s" >actual-s &&
+       test_cmp expected-s actual-s
+'
+
 test_expect_success 'alias user-defined tformat' '
        git log --pretty="tformat:%h" >expected &&
        git config pretty.test-alias "tformat:%h" &&
@@ -72,13 +104,13 @@ test_expect_success 'alias loop' '
 '
 
 test_expect_success 'NUL separation' '
-       printf "add bar\0initial" >expected &&
+       printf "add bar\0$(commit_msg)" >expected &&
        git log -z --pretty="format:%s" >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'NUL termination' '
-       printf "add bar\0initial\0" >expected &&
+       printf "add bar\0$(commit_msg)\0" >expected &&
        git log -z --pretty="tformat:%s" >actual &&
        test_cmp expected actual
 '
@@ -86,7 +118,7 @@ test_expect_success 'NUL termination' '
 test_expect_success 'NUL separation with --stat' '
        stat0_part=$(git diff --stat HEAD^ HEAD) &&
        stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
-       printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n" >expected &&
+       printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n" >expected &&
        git log -z --stat --pretty="format:%s" >actual &&
        test_i18ncmp expected actual
 '
@@ -94,9 +126,188 @@ test_expect_success 'NUL separation with --stat' '
 test_expect_failure 'NUL termination with --stat' '
        stat0_part=$(git diff --stat HEAD^ HEAD) &&
        stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
-       printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n\0" >expected &&
+       printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected &&
        git log -z --stat --pretty="tformat:%s" >actual &&
        test_i18ncmp expected actual
 '
 
+test_expect_success 'setup more commits' '
+       test_commit "message one" one one message-one &&
+       test_commit "message two" two two message-two &&
+       head1=$(git rev-parse --verify --short HEAD~0) &&
+       head2=$(git rev-parse --verify --short HEAD~1) &&
+       head3=$(git rev-parse --verify --short HEAD~2) &&
+       head4=$(git rev-parse --verify --short HEAD~3)
+'
+
+test_expect_success 'left alignment formatting' '
+       git log --pretty="format:%<(40)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+message two                            Z
+message one                            Z
+add bar                                Z
+$(commit_msg)                    Z
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting at the nth column' '
+       git log --pretty="format:%h %<|(40)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+$head1 message two                    Z
+$head2 message one                    Z
+$head3 add bar                        Z
+$head4 $(commit_msg)            Z
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with no padding' '
+       git log --pretty="format:%<(1)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       cat <<EOF >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with trunc' '
+       git log --pretty="format:%<(10,trunc)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+message ..
+message ..
+add bar  Z
+initial...
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with ltrunc' '
+       git log --pretty="format:%<(10,ltrunc)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+..sage two
+..sage one
+add bar  Z
+..${sample_utf8_part}lich
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'left alignment formatting with mtrunc' '
+       git log --pretty="format:%<(10,mtrunc)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+mess.. two
+mess.. one
+add bar  Z
+init..lich
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting' '
+       git log --pretty="format:%>(40)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+Z                            message two
+Z                            message one
+Z                                add bar
+Z                    $(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting at the nth column' '
+       git log --pretty="format:%h %>|(40)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+$head1                      message two
+$head2                      message one
+$head3                          add bar
+$head4              $(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'right alignment formatting with no padding' '
+       git log --pretty="format:%>(1)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       cat <<EOF >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'center alignment formatting' '
+       git log --pretty="format:%><(40)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+Z             message two              Z
+Z             message one              Z
+Z               add bar                Z
+Z         $(commit_msg)          Z
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'center alignment formatting at the nth column' '
+       git log --pretty="format:%h %><|(40)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       qz_to_tab_space <<EOF >expected &&
+$head1           message two          Z
+$head2           message one          Z
+$head3             add bar            Z
+$head4       $(commit_msg)      Z
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'center alignment formatting with no padding' '
+       git log --pretty="format:%><(1)%s" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       cat <<EOF >expected &&
+message two
+message one
+add bar
+$(commit_msg)
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'left/right alignment formatting with stealing' '
+       git commit --amend -m short --author "long long long <long@me.com>" &&
+       git log --pretty="format:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual &&
+       # complete the incomplete line at the end
+       echo >>actual &&
+       cat <<EOF >expected &&
+short long  long long
+message ..   A U Thor
+add bar      A U Thor
+initial...   A U Thor
+EOF
+       test_cmp expected actual
+'
+
 test_done
index bbde31b0196ae9c5ba3da33bfdb6f8c4f9c8f4e9..925f577a3c4652bed253787d5f540a415b16fccf 100755 (executable)
@@ -44,15 +44,15 @@ test_expect_success setup '
 '
 
 cat >expected <<EOF
-${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\
  ${c_tag}tag: v1.0${c_reset}${c_commit},\
  ${c_tag}tag: B${c_reset}${c_commit},\
  ${c_branch}master${c_reset}${c_commit})${c_reset} B
-${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\
  ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1
-${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
  On master: Changes to A.t
-${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
 EOF
 
 # We want log to show all, but the second parent to refs/stash is irrelevant
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
new file mode 100755 (executable)
index 0000000..7665d67
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='test log -L'
+. ./test-lib.sh
+
+test_expect_success 'setup (import history)' '
+       git fast-import < "$TEST_DIRECTORY"/t4211/history.export &&
+       git reset --hard
+'
+
+canned_test_1 () {
+       test_expect_$1 "$2" "
+               git log $2 >actual &&
+               test_cmp \"\$TEST_DIRECTORY\"/t4211/expect.$3 actual
+       "
+}
+
+canned_test () {
+       canned_test_1 success "$@"
+}
+canned_test_failure () {
+       canned_test_1 failure "$@"
+}
+
+test_bad_opts () {
+       test_expect_success "invalid args: $1" "
+               test_must_fail git log $1 2>errors &&
+               grep '$2' errors
+       "
+}
+
+canned_test "-L 4,12:a.c simple" simple-f
+canned_test "-L 4,+9:a.c simple" simple-f
+canned_test "-L '/long f/,/^}/:a.c' simple" simple-f
+canned_test "-L :f:a.c simple" simple-f-to-main
+
+canned_test "-L '/main/,/^}/:a.c' simple" simple-main
+canned_test "-L :main:a.c simple" simple-main-to-end
+
+canned_test "-L 1,+4:a.c simple" beginning-of-file
+
+canned_test "-L 20:a.c simple" end-of-file
+
+canned_test "-L '/long f/',/^}/:a.c -L /main/,/^}/:a.c simple" two-ranges
+canned_test "-L 24,+1:a.c simple" vanishes-early
+
+canned_test "-M -L '/long f/,/^}/:b.c' move-support" move-support-f
+canned_test "-M -L ':f:b.c' parallel-change" parallel-change-f-to-main
+
+canned_test "-L 4,12:a.c -L :main:a.c simple" multiple
+canned_test "-L 4,18:a.c -L :main:a.c simple" multiple-overlapping
+canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping
+canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset
+canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset
+
+test_bad_opts "-L" "switch.*requires a value"
+test_bad_opts "-L b.c" "argument.*not of the form"
+test_bad_opts "-L 1:" "argument.*not of the form"
+test_bad_opts "-L 1:nonexistent" "There is no path"
+test_bad_opts "-L 1:simple" "There is no path"
+test_bad_opts "-L '/foo:b.c'" "argument.*not of the form"
+test_bad_opts "-L 1000:b.c" "has only.*lines"
+test_bad_opts "-L 1,1000:b.c" "has only.*lines"
+test_bad_opts "-L :b.c" "argument.*not of the form"
+test_bad_opts "-L :foo:b.c" "no match"
+
+# There is a separate bug when an empty -L range is the first -L encountered,
+# thus to demonstrate this particular bug, the empty -L range must follow a
+# non-empty -L range.
+test_expect_success '-L {empty-range} (any -L)' '
+       n=$(expr $(wc -l <b.c) + 1) &&
+       git log -L1,1:b.c -L$n:b.c
+'
+
+test_expect_success '-L {empty-range} (first -L)' '
+       n=$(expr $(wc -l <b.c) + 1) &&
+       git log -L$n:b.c
+'
+
+test_done
diff --git a/t/t4211/expect.beginning-of-file b/t/t4211/expect.beginning-of-file
new file mode 100644 (file)
index 0000000..91b4054
--- /dev/null
@@ -0,0 +1,43 @@
+commit 4a23ae5c98d59a58c6da036156959f2dc9f472ad
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:47:40 2013 +0100
+
+    change at very beginning
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -1,3 +1,4 @@
++#include <unistd.h>
+ #include <stdio.h>
+ long f(long x)
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -1,3 +1,3 @@
+ #include <stdio.h>
+-int f(int x)
++long f(long x)
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +1,3 @@
++#include <stdio.h>
++
++int f(int x)
diff --git a/t/t4211/expect.end-of-file b/t/t4211/expect.end-of-file
new file mode 100644 (file)
index 0000000..bd25bb2
--- /dev/null
@@ -0,0 +1,62 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -20,3 +20,5 @@
+       printf("%ld\n", f(15));
+       return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -20,3 +20,3 @@
+       printf("%ld\n", f(15));
+       return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -19,3 +19,3 @@
+-      printf("%d\n", f(15));
++      printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +18,3 @@
++      printf("%d\n", f(15));
++      return 0;
++}
diff --git a/t/t4211/expect.move-support-f b/t/t4211/expect.move-support-f
new file mode 100644 (file)
index 0000000..c905e01
--- /dev/null
@@ -0,0 +1,80 @@
+commit 6ce3c4ff690136099bb17e1a8766b75764726ea7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:49:50 2013 +0100
+
+    another simple change
+
+diff --git a/b.c b/b.c
+--- a/b.c
++++ b/b.c
+@@ -4,9 +4,9 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+-              x >>= 1;
++              x /= 2;
+               s++;
+       }
+       return s;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
++      return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++      int s = 0;
++      while (x) {
++              x >>= 1;
++              s++;
++      }
++}
diff --git a/t/t4211/expect.multiple b/t/t4211/expect.multiple
new file mode 100644 (file)
index 0000000..76ad5b5
--- /dev/null
@@ -0,0 +1,104 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,7 @@
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+-      printf("%d\n", f(15));
++      printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
++      return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++      int s = 0;
++      while (x) {
++              x >>= 1;
++              s++;
++      }
++}
diff --git a/t/t4211/expect.multiple-overlapping b/t/t4211/expect.multiple-overlapping
new file mode 100644 (file)
index 0000000..d930b6e
--- /dev/null
@@ -0,0 +1,187 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,21 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,19 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
++}
+\ No newline at end of file
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+- * A comment.
++ * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+-int f(int x)
++long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * A comment.
+  */
+ int main ()
+ {
+-      printf("%d\n", f(15));
++      printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,18 +3,19 @@
+ int f(int x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
++      return s;
+ }
+ /*
+  * A comment.
+  */
+ int main ()
+ {
+       printf("%d\n", f(15));
+       return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,18 @@
++int f(int x)
++{
++      int s = 0;
++      while (x) {
++              x >>= 1;
++              s++;
++      }
++}
++
++/*
++ * A comment.
++ */
++
++int main ()
++{
++      printf("%d\n", f(15));
++      return 0;
++}
diff --git a/t/t4211/expect.multiple-superset b/t/t4211/expect.multiple-superset
new file mode 100644 (file)
index 0000000..d930b6e
--- /dev/null
@@ -0,0 +1,187 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,21 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,19 +4,19 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
++}
+\ No newline at end of file
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+- * A comment.
++ * This is only an example!
+  */
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,19 +3,19 @@
+-int f(int x)
++long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * A comment.
+  */
+ int main ()
+ {
+-      printf("%d\n", f(15));
++      printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,18 +3,19 @@
+ int f(int x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
++      return s;
+ }
+ /*
+  * A comment.
+  */
+ int main ()
+ {
+       printf("%d\n", f(15));
+       return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,18 @@
++int f(int x)
++{
++      int s = 0;
++      while (x) {
++              x >>= 1;
++              s++;
++      }
++}
++
++/*
++ * A comment.
++ */
++
++int main ()
++{
++      printf("%d\n", f(15));
++      return 0;
++}
diff --git a/t/t4211/expect.parallel-change-f-to-main b/t/t4211/expect.parallel-change-f-to-main
new file mode 100644 (file)
index 0000000..052def8
--- /dev/null
@@ -0,0 +1,160 @@
+commit 0469c60bc4837d52d97b1f081dec5f98dea20fed
+Merge: ba227c6 6ce3c4f
+Author: Thomas Rast <trast@inf.ethz.ch>
+Date:   Fri Apr 12 16:16:24 2013 +0200
+
+    Merge across the rename
+
+
+commit 6ce3c4ff690136099bb17e1a8766b75764726ea7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:49:50 2013 +0100
+
+    another simple change
+
+diff --git a/b.c b/b.c
+--- a/b.c
++++ b/b.c
+@@ -4,14 +4,14 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+-              x >>= 1;
++              x /= 2;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * This is only an example!
+  */
+
+commit ba227c6632349700fbb957dec2b50f5e2358be3f
+Author: Thomas Rast <trast@inf.ethz.ch>
+Date:   Fri Apr 12 16:15:57 2013 +0200
+
+    change on another line of history while rename happens
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -4,14 +4,14 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+- * This is only an example!
++ * This is only a short example!
+  */
+
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+- * A comment.
++ * This is only an example!
+  */
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+-int f(int x)
++long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * A comment.
+  */
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,13 +3,14 @@
+ int f(int x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
++      return s;
+ }
+ /*
+  * A comment.
+  */
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,13 @@
++int f(int x)
++{
++      int s = 0;
++      while (x) {
++              x >>= 1;
++              s++;
++      }
++}
++
++/*
++ * A comment.
++ */
++
diff --git a/t/t4211/expect.simple-f b/t/t4211/expect.simple-f
new file mode 100644 (file)
index 0000000..a1f5bc4
--- /dev/null
@@ -0,0 +1,59 @@
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
++      return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++      int s = 0;
++      while (x) {
++              x >>= 1;
++              s++;
++      }
++}
diff --git a/t/t4211/expect.simple-f-to-main b/t/t4211/expect.simple-f-to-main
new file mode 100644 (file)
index 0000000..a475768
--- /dev/null
@@ -0,0 +1,100 @@
+commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:41 2013 +0100
+
+    touch comment
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+ long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+- * A comment.
++ * This is only an example!
+  */
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,14 +3,14 @@
+-int f(int x)
++long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+ /*
+  * A comment.
+  */
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,13 +3,14 @@
+ int f(int x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
++      return s;
+ }
+ /*
+  * A comment.
+  */
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,13 @@
++int f(int x)
++{
++      int s = 0;
++      while (x) {
++              x >>= 1;
++              s++;
++      }
++}
++
++/*
++ * A comment.
++ */
++
diff --git a/t/t4211/expect.simple-main b/t/t4211/expect.simple-main
new file mode 100644 (file)
index 0000000..39ce39b
--- /dev/null
@@ -0,0 +1,68 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
+\ No newline at end of file
++}
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+-      printf("%d\n", f(15));
++      printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +16,5 @@
++int main ()
++{
++      printf("%d\n", f(15));
++      return 0;
++}
diff --git a/t/t4211/expect.simple-main-to-end b/t/t4211/expect.simple-main-to-end
new file mode 100644 (file)
index 0000000..8480bd9
--- /dev/null
@@ -0,0 +1,70 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,7 @@
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
+\ No newline at end of file
++}
++
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+-      printf("%d\n", f(15));
++      printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +16,5 @@
++int main ()
++{
++      printf("%d\n", f(15));
++      return 0;
++}
diff --git a/t/t4211/expect.two-ranges b/t/t4211/expect.two-ranges
new file mode 100644 (file)
index 0000000..6109aa0
--- /dev/null
@@ -0,0 +1,102 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
+\ No newline at end of file
++}
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -18,5 +18,5 @@
+ int main ()
+ {
+       printf("%ld\n", f(15));
+       return 0;
+-}
++}
+\ No newline at end of file
+
+commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:45:16 2013 +0100
+
+    touch both functions
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,9 +3,9 @@
+-int f(int x)
++long f(long x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+ }
+@@ -17,5 +17,5 @@
+ int main ()
+ {
+-      printf("%d\n", f(15));
++      printf("%ld\n", f(15));
+       return 0;
+ }
+
+commit f04fb20f2c77850996cba739709acc6faecc58f7
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:55 2013 +0100
+
+    change f()
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -3,8 +3,9 @@
+ int f(int x)
+ {
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
++      return s;
+ }
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +3,8 @@
++int f(int x)
++{
++      int s = 0;
++      while (x) {
++              x >>= 1;
++              s++;
++      }
++}
diff --git a/t/t4211/expect.vanishes-early b/t/t4211/expect.vanishes-early
new file mode 100644 (file)
index 0000000..1f7cd06
--- /dev/null
@@ -0,0 +1,39 @@
+commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:43 2013 +0100
+
+    change back to complete line
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -22,1 +24,1 @@
+-}
+\ No newline at end of file
++/* incomplete lines are bad! */
+
+commit 100b61a6f2f720f812620a9d10afb3a960ccb73c
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:48:10 2013 +0100
+
+    change to an incomplete line at end
+
+diff --git a/a.c b/a.c
+--- a/a.c
++++ b/a.c
+@@ -22,1 +22,1 @@
+-}
++}
+\ No newline at end of file
+
+commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a
+Author: Thomas Rast <trast@student.ethz.ch>
+Date:   Thu Feb 28 10:44:48 2013 +0100
+
+    initial
+
+diff --git a/a.c b/a.c
+--- /dev/null
++++ b/a.c
+@@ -0,0 +20,1 @@
++}
diff --git a/t/t4211/history.export b/t/t4211/history.export
new file mode 100644 (file)
index 0000000..f9f41e2
--- /dev/null
@@ -0,0 +1,406 @@
+blob
+mark :1
+data 157
+#include <stdio.h>
+
+int f(int x)
+{
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+       printf("%d\n", f(15));
+       return 0;
+}
+
+reset refs/tags/simple
+commit refs/tags/simple
+mark :2
+author Thomas Rast <trast@student.ethz.ch> 1362044688 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044688 +0100
+data 8
+initial
+M 100644 :1 a.c
+
+blob
+mark :3
+data 168
+#include <stdio.h>
+
+int f(int x)
+{
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+       printf("%d\n", f(15));
+       return 0;
+}
+
+commit refs/tags/simple
+mark :4
+author Thomas Rast <trast@student.ethz.ch> 1362044695 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044695 +0100
+data 11
+change f()
+from :2
+M 100644 :3 a.c
+
+blob
+mark :5
+data 171
+#include <stdio.h>
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * A comment.
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+
+commit refs/tags/simple
+mark :6
+author Thomas Rast <trast@student.ethz.ch> 1362044716 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044716 +0100
+data 21
+touch both functions
+from :4
+M 100644 :5 a.c
+
+blob
+mark :7
+data 185
+#include <stdio.h>
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+
+commit refs/tags/simple
+mark :8
+author Thomas Rast <trast@student.ethz.ch> 1362044741 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044741 +0100
+data 14
+touch comment
+from :6
+M 100644 :7 a.c
+
+blob
+mark :9
+data 205
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+
+commit refs/tags/simple
+mark :10
+author Thomas Rast <trast@student.ethz.ch> 1362044860 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044860 +0100
+data 25
+change at very beginning
+from :8
+M 100644 :9 a.c
+
+blob
+mark :11
+data 204
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+commit refs/tags/simple
+mark :12
+author Thomas Rast <trast@student.ethz.ch> 1362044890 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044890 +0100
+data 36
+change to an incomplete line at end
+from :10
+M 100644 :11 a.c
+
+blob
+mark :13
+data 238
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/tags/simple
+mark :14
+author Thomas Rast <trast@student.ethz.ch> 1362044923 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044923 +0100
+data 29
+change back to complete line
+from :12
+M 100644 :13 a.c
+
+commit refs/tags/move-support
+mark :15
+author Thomas Rast <trast@student.ethz.ch> 1362044968 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044968 +0100
+data 10
+move file
+from :14
+D a.c
+M 100644 :13 b.c
+
+blob
+mark :16
+data 237
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x /= 2;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/tags/move-support
+mark :17
+author Thomas Rast <trast@student.ethz.ch> 1362044990 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362044990 +0100
+data 22
+another simple change
+from :15
+M 100644 :16 b.c
+
+blob
+mark :18
+data 254
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x);
+
+/*
+ * This is only an example!
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+
+/* incomplete lines are bad! */
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x /= 2;
+               s++;
+       }
+       return s;
+}
+
+commit refs/heads/master
+mark :19
+author Thomas Rast <trast@student.ethz.ch> 1362045024 +0100
+committer Thomas Rast <trast@student.ethz.ch> 1362045024 +0100
+data 21
+move within the file
+from :17
+M 100644 :18 b.c
+
+blob
+mark :20
+data 243
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x >>= 1;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * This is only a short example!
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/heads/parallel-change
+mark :21
+author Thomas Rast <trast@inf.ethz.ch> 1365776157 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1365776157 +0200
+data 55
+change on another line of history while rename happens
+from :14
+M 100644 :20 a.c
+
+blob
+mark :22
+data 242
+#include <unistd.h>
+#include <stdio.h>
+
+long f(long x)
+{
+       int s = 0;
+       while (x) {
+               x /= 2;
+               s++;
+       }
+       return s;
+}
+
+/*
+ * This is only a short example!
+ */
+
+int main ()
+{
+       printf("%ld\n", f(15));
+       return 0;
+}
+
+/* incomplete lines are bad! */
+
+commit refs/heads/parallel-change
+mark :23
+author Thomas Rast <trast@inf.ethz.ch> 1365776184 +0200
+committer Thomas Rast <trast@inf.ethz.ch> 1365776191 +0200
+data 24
+Merge across the rename
+from :21
+merge :17
+D a.c
+M 100644 :22 b.c
+
+reset refs/heads/parallel-change
+from :23
+
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
new file mode 100755 (executable)
index 0000000..ec5099b
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='git log with invalid commit headers'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit foo &&
+
+       git cat-file commit HEAD |
+       sed "/^author /s/>/>-<>/" >broken_email.commit &&
+       git hash-object -w -t commit broken_email.commit >broken_email.hash &&
+       git update-ref refs/heads/broken_email $(cat broken_email.hash)
+'
+
+test_expect_success 'git log with broken author email' '
+       {
+               echo commit $(cat broken_email.hash)
+               echo "Author: A U Thor <author@example.com>"
+               echo "Date:   Thu Jan 1 00:00:00 1970 +0000"
+               echo
+               echo "    foo"
+       } >expect.out &&
+       : >expect.err &&
+
+       git log broken_email >actual.out 2>actual.err &&
+
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_expect_success 'git log --format with broken author email' '
+       echo "A U Thor+author@example.com+" >expect.out &&
+       : >expect.err &&
+
+       git log --format="%an+%ae+%ad" broken_email >actual.out 2>actual.err &&
+
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+'
+
+test_done
index b7da95fac578b58b7405386eb16f4288a253b95f..85716dd6ec566f34f7c4e0b30477bb1531c6030e 100755 (executable)
@@ -3,20 +3,19 @@
 test_description='git am with corrupt input'
 . ./test-lib.sh
 
-# Note the missing "+++" line:
-cat > bad-patch.diff <<'EOF'
-From: A U Thor <au.thor@example.com>
-diff --git a/f b/f
-index 7898192..6178079 100644
---- a/f
-@@ -1 +1 @@
--a
-+b
-EOF
-
 test_expect_success setup '
-       test $? = 0 &&
-       echo a > f &&
+       # Note the missing "+++" line:
+       cat >bad-patch.diff <<-\EOF &&
+       From: A U Thor <au.thor@example.com>
+       diff --git a/f b/f
+       index 7898192..6178079 100644
+       --- a/f
+       @@ -1 +1 @@
+       -a
+       +b
+       EOF
+
+       echo a >f &&
        git add f &&
        test_tick &&
        git commit -m initial
@@ -26,17 +25,12 @@ test_expect_success setup '
 #   fatal: unable to write file '(null)' mode 100644: Bad address
 # Also, it had the unwanted side-effect of deleting f.
 test_expect_success 'try to apply corrupted patch' '
-       git am bad-patch.diff 2> actual
-       test $? = 1
+       test_must_fail git am bad-patch.diff 2>actual
 '
 
-cat > expected <<EOF
-fatal: git diff header lacks filename information (line 4)
-EOF
-
 test_expect_success 'compare diagnostic; ensure file is still here' '
-       test $? = 0 &&
-       test -f f &&
+       echo "fatal: git diff header lacks filename information (line 4)" >expected &&
+       test_path_is_file f &&
        test_cmp expected actual
 '
 
index bd43b3d8ef8b3e71f6985cfa96a940dccb4a0a54..9015e476541dafdd6a82efcd4ace6038461d8522 100755 (executable)
@@ -205,6 +205,19 @@ EXPECTED
        test_cmp expected actual
 '
 
+test_expect_success 'file remove A, B (same)' '
+       cat >expected <<\EXPECTED &&
+EXPECTED
+
+       git reset --hard initial &&
+       test_commit "rm-a-b-base" "ONE" "AAA" &&
+       git rm ONE &&
+       git commit -m "rm-a-b" &&
+       git tag "rm-a-b" &&
+       git merge-tree rm-a-b-base rm-a-b rm-a-b >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'file change A, remove B' '
        cat >expected <<\EXPECTED &&
 removed in remote
@@ -246,6 +259,57 @@ EXPECTED
        test_cmp expected actual
 '
 
+test_expect_success 'tree add A, B (same)' '
+       cat >expect <<-\EOF &&
+       EOF
+       git reset --hard initial &&
+       mkdir sub &&
+       test_commit "add sub/file" "sub/file" "file" add-tree-A &&
+       git merge-tree initial add-tree-A add-tree-A >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'tree add A, B (different)' '
+       cat >expect <<-\EOF &&
+       added in both
+         our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
+         their  100644 ba629238ca89489f2b350e196ca445e09d8bb834 sub/file
+       @@ -1 +1,5 @@
+       +<<<<<<< .our
+        AAA
+       +=======
+       +BBB
+       +>>>>>>> .their
+       EOF
+       git reset --hard initial &&
+       mkdir sub &&
+       test_commit "add sub/file" "sub/file" "AAA" add-tree-a-b-A &&
+       git reset --hard initial &&
+       mkdir sub &&
+       test_commit "add sub/file" "sub/file" "BBB" add-tree-a-b-B &&
+       git merge-tree initial add-tree-a-b-A add-tree-a-b-B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'tree unchanged A, removed B' '
+       cat >expect <<-\EOF &&
+       removed in remote
+         base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
+         our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
+       @@ -1 +0,0 @@
+       -AAA
+       EOF
+       git reset --hard initial &&
+       mkdir sub &&
+       test_commit "add sub/file" "sub/file" "AAA" tree-remove-b-initial &&
+       git rm sub/file &&
+       test_tick &&
+       git commit -m "remove sub/file" &&
+       git tag tree-remove-b-B &&
+       git merge-tree tree-remove-b-initial tree-remove-b-initial tree-remove-b-B >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'turn file to tree' '
        git reset --hard initial &&
        rm initial-file &&
index 3fbd366ec3bf95020959041462721291d6b92cf2..c2023b1a3d78a7f100207a76678957d2659f8a8d 100755 (executable)
@@ -30,10 +30,76 @@ GUNZIP=${GUNZIP:-gzip -d}
 
 SUBSTFORMAT=%H%n
 
+test_lazy_prereq TAR_NEEDS_PAX_FALLBACK '
+       (
+               mkdir pax &&
+               cd pax &&
+               "$TAR" xf "$TEST_DIRECTORY"/t5000/pax.tar &&
+               test -f PaxHeaders.1791/file
+       )
+'
+
+get_pax_header() {
+       file=$1
+       header=$2=
+
+       while read len rest
+       do
+               if test "$len" = $(echo "$len $rest" | wc -c)
+               then
+                       case "$rest" in
+                       $header*)
+                               echo "${rest#$header}"
+                               ;;
+                       esac
+               fi
+       done <"$file"
+}
+
+check_tar() {
+       tarfile=$1.tar
+       listfile=$1.lst
+       dir=$1
+       dir_with_prefix=$dir/$2
+
+       test_expect_success ' extract tar archive' '
+               (mkdir $dir && cd $dir && "$TAR" xf -) <$tarfile
+       '
+
+       test_expect_success TAR_NEEDS_PAX_FALLBACK ' interpret pax headers' '
+               (
+                       cd $dir &&
+                       for header in *.paxheader
+                       do
+                               data=${header%.paxheader}.data &&
+                               if test -h $data -o -e $data
+                               then
+                                       path=$(get_pax_header $header path) &&
+                                       if test -n "$path"
+                                       then
+                                               mv "$data" "$path"
+                                       fi
+                               fi
+                       done
+               )
+       '
+
+       test_expect_success ' validate filenames' '
+               (cd ${dir_with_prefix}a && find .) | sort >$listfile &&
+               test_cmp a.lst $listfile
+       '
+
+       test_expect_success ' validate file contents' '
+               diff -r a ${dir_with_prefix}a
+       '
+}
+
 test_expect_success \
     'populate workdir' \
-    'mkdir a b c &&
+    'mkdir a &&
      echo simple textfile >a/a &&
+     ten=0123456789 && hundred=$ten$ten$ten$ten$ten$ten$ten$ten$ten$ten &&
+     echo long filename >a/four$hundred &&
      mkdir a/bin &&
      cp /bin/sh a/bin &&
      printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
@@ -62,6 +128,12 @@ test_expect_success \
      git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
      git commit-tree $treeid </dev/null)'
 
+test_expect_success 'setup export-subst' '
+       echo "substfile?" export-subst >>.git/info/attributes &&
+       git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+               >a/substfile1
+'
+
 test_expect_success \
     'create bare clone' \
     'git clone --bare . bare.git &&
@@ -75,13 +147,19 @@ test_expect_success \
     'git archive' \
     'git archive HEAD >b.tar'
 
-test_expect_success \
-    'git tar-tree' \
-    'git tar-tree HEAD >b2.tar'
+check_tar b
 
-test_expect_success \
-    'git archive vs. git tar-tree' \
-    'test_cmp b.tar b2.tar'
+test_expect_success 'git archive --prefix=prefix/' '
+       git archive --prefix=prefix/ HEAD >with_prefix.tar
+'
+
+check_tar with_prefix prefix/
+
+test_expect_success 'git-archive --prefix=olde-' '
+       git archive --prefix=olde- HEAD >with_olde-prefix.tar
+'
+
+check_tar with_olde-prefix olde-
 
 test_expect_success 'git archive on large files' '
     test_config core.bigfilethreshold 1 &&
@@ -118,66 +196,14 @@ test_expect_success \
     'git get-tar-commit-id <b.tar >b.commitid &&
      test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
 
-test_expect_success \
-    'extract tar archive' \
-    '(cd b && "$TAR" xf -) <b.tar'
-
-test_expect_success \
-    'validate filenames' \
-    '(cd b/a && find .) | sort >b.lst &&
-     test_cmp a.lst b.lst'
-
-test_expect_success \
-    'validate file contents' \
-    'diff -r a b/a'
-
-test_expect_success \
-    'git tar-tree with prefix' \
-    'git tar-tree HEAD prefix >c.tar'
-
-test_expect_success \
-    'extract tar archive with prefix' \
-    '(cd c && "$TAR" xf -) <c.tar'
-
-test_expect_success \
-    'validate filenames with prefix' \
-    '(cd c/prefix/a && find .) | sort >c.lst &&
-     test_cmp a.lst c.lst'
-
-test_expect_success \
-    'validate file contents with prefix' \
-    'diff -r a c/prefix/a'
-
-test_expect_success \
-    'create archives with substfiles' \
-    'cp .git/info/attributes .git/info/attributes.before &&
-     echo "substfile?" export-subst >>.git/info/attributes &&
-     git archive HEAD >f.tar &&
-     git archive --prefix=prefix/ HEAD >g.tar &&
-     mv .git/info/attributes.before .git/info/attributes'
-
-test_expect_success \
-    'extract substfiles' \
-    '(mkdir f && cd f && "$TAR" xf -) <f.tar'
-
-test_expect_success \
-     'validate substfile contents' \
-     'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
-      >f/a/substfile1.expected &&
-      test_cmp f/a/substfile1.expected f/a/substfile1 &&
-      test_cmp a/substfile2 f/a/substfile2
+test_expect_success 'git tar-tree' '
+       git tar-tree HEAD >tar-tree.tar &&
+       test_cmp b.tar tar-tree.tar
 '
 
-test_expect_success \
-    'extract substfiles from archive with prefix' \
-    '(mkdir g && cd g && "$TAR" xf -) <g.tar'
-
-test_expect_success \
-     'validate substfile contents from archive with prefix' \
-     'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
-      >g/prefix/a/substfile1.expected &&
-      test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
-      test_cmp a/substfile2 g/prefix/a/substfile2
+test_expect_success 'git tar-tree with prefix' '
+       git tar-tree HEAD prefix >tar-tree_with_prefix.tar &&
+       test_cmp with_prefix.tar tar-tree_with_prefix.tar
 '
 
 test_expect_success 'git archive with --output, override inferred format' '
@@ -197,18 +223,6 @@ test_expect_success 'clients cannot access unreachable commits' '
        test_must_fail git archive --remote=. $sha1 >remote.tar
 '
 
-test_expect_success 'git-archive --prefix=olde-' '
-       git archive --prefix=olde- >h.tar HEAD &&
-       (
-               mkdir h &&
-               cd h &&
-               "$TAR" xf - <../h.tar
-       ) &&
-       test -d h/olde-a &&
-       test -d h/olde-a/bin &&
-       test -f h/olde-a/bin/sh
-'
-
 test_expect_success 'setup tar filters' '
        git config tar.tar.foo.command "tr ab ba" &&
        git config tar.bar.command "tr ab ba" &&
diff --git a/t/t5000/pax.tar b/t/t5000/pax.tar
new file mode 100644 (file)
index 0000000..d911737
Binary files /dev/null and b/t/t5000/pax.tar differ
index 4e7b05dd2391125b1c9e58404c552647107099cd..c72f71eb18ee90b7885ea2af574e1115154a5549 100755 (executable)
@@ -37,7 +37,7 @@ check_zip() {
 
 test_expect_success \
     'populate workdir' \
-    'mkdir a b c &&
+    'mkdir a &&
      echo simple textfile >a/a &&
      mkdir a/bin &&
      cp /bin/sh a/bin &&
index bfdb56a0694db0c6ed07aaa568c648909ac62660..67f3b54bed3545417d1f6186f6f42452cb55dc87 100755 (executable)
@@ -23,17 +23,31 @@ check_dir() {
                        echo "$dir/$i"
                done
        } | sort >expect &&
-       find "$dir" -print | sort >actual &&
+       find "$dir" ! -name pax_global_header -print | sort >actual &&
        test_cmp expect actual
 }
 
-test_expect_success 'tar archive of empty tree is empty' '
-       git archive --format=tar HEAD: >empty.tar &&
+
+# bsdtar/libarchive versions before 3.1.3 consider a tar file with a
+# global pax header that is not followed by a file record as corrupt.
+if "$TAR" tf "$TEST_DIRECTORY"/t5004/empty-with-pax-header.tar >/dev/null 2>&1
+then
+       test_set_prereq HEADER_ONLY_TAR_OK
+fi
+
+test_expect_success HEADER_ONLY_TAR_OK 'tar archive of commit with empty tree' '
+       git archive --format=tar HEAD >empty-with-pax-header.tar &&
        make_dir extract &&
-       "$TAR" xf empty.tar -C extract &&
+       "$TAR" xf empty-with-pax-header.tar -C extract &&
        check_dir extract
 '
 
+test_expect_success 'tar archive of empty tree is empty' '
+       git archive --format=tar HEAD: >empty.tar &&
+       perl -e "print \"\\0\" x 10240" >10knuls.tar &&
+       test_cmp 10knuls.tar empty.tar
+'
+
 test_expect_success 'tar archive of empty tree with prefix' '
        git archive --format=tar --prefix=foo/ HEAD >prefix.tar &&
        make_dir extract &&
diff --git a/t/t5004/empty-with-pax-header.tar b/t/t5004/empty-with-pax-header.tar
new file mode 100644 (file)
index 0000000..da9e39e
Binary files /dev/null and b/t/t5004/empty-with-pax-header.tar differ
index 432f98c357601057cb89f9dd6bfbe1ab02e9477a..1afa0d5c44e00a401cb32ec4bca13c4c6882fd07 100755 (executable)
@@ -80,12 +80,12 @@ test_expect_success 'setup: two scripts for reading pull requests' '
 
        cat <<-EOT >fuzz.sed
        #!/bin/sed -nf
+       s/$downstream_url_for_sed/URL/g
        s/$_x40/OBJECT_NAME/g
        s/A U Thor/AUTHOR/g
        s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
        s/        [^ ].*/        SUBJECT/g
        s/  [^ ].* (DATE)/  SUBJECT (DATE)/g
-       s/$downstream_url_for_sed/URL/g
        s/for-upstream/BRANCH/g
        s/mnemonic.txt/FILENAME/g
        s/^version [0-9]/VERSION/
index 5b1250f0d2d69369fccacbefa3fa24488e9c4b4e..35926debe3df81dc997e99f5edbf74ab5f9ec479 100755 (executable)
@@ -51,7 +51,7 @@ do_corrupt_object() {
     ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` &&
     ofs=$(($ofs + $2)) &&
     chmod +w ${pack}.pack &&
-    dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
+    dd of=${pack}.pack bs=1 conv=notrunc seek=$ofs &&
     test_must_fail git verify-pack ${pack}.pack
 }
 
@@ -275,6 +275,33 @@ test_expect_success \
      git cat-file blob $blob_2 > /dev/null &&
      git cat-file blob $blob_3 > /dev/null'
 
+test_expect_success \
+    'corruption of delta base reference pointing to wrong object' \
+    'create_new_pack --delta-base-offset &&
+     git prune-packed &&
+     printf "\220\033" | do_corrupt_object $blob_3 2 &&
+     git cat-file blob $blob_1 >/dev/null &&
+     git cat-file blob $blob_2 >/dev/null &&
+     test_must_fail git cat-file blob $blob_3 >/dev/null'
+
+test_expect_success \
+    '... but having a loose copy allows for full recovery' \
+    'mv ${pack}.idx tmp &&
+     git hash-object -t blob -w file_3 &&
+     mv tmp ${pack}.idx &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack --delta-base-offset --no-reuse-delta &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
 test_expect_success \
     'corrupting header to have too small output buffer fails unpack' \
     'create_new_pack &&
index baa670cea5d8a9d8b4b4578de69917fe76f71d46..ea2e0d4b489a9e079c93b36b013d4bc919755c02 100755 (executable)
@@ -31,8 +31,8 @@ clear_hook_input () {
 }
 
 verify_hook_input () {
-       test_cmp "$TRASH_DIRECTORY"/post-rewrite.args expected.args &&
-       test_cmp "$TRASH_DIRECTORY"/post-rewrite.data expected.data
+       test_cmp expected.args "$TRASH_DIRECTORY"/post-rewrite.args &&
+       test_cmp expected.data "$TRASH_DIRECTORY"/post-rewrite.data
 }
 
 test_expect_success 'git commit --amend' '
index d574085696e81522669119c9b7480ba4c682c968..d87ddf73b7127bc624ca739653db9389831b817e 100755 (executable)
@@ -135,6 +135,13 @@ test_expect_success 'clone shallow depth 1' '
        test "`git --git-dir=shallow0/.git rev-list --count HEAD`" = 1
 '
 
+test_expect_success 'clone shallow depth 1 with fsck' '
+       git config --global fetch.fsckobjects true &&
+       git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0fsck &&
+       test "`git --git-dir=shallow0fsck/.git rev-list --count HEAD`" = 1 &&
+       git config --global --unset fetch.fsckobjects
+'
+
 test_expect_success 'clone shallow' '
        git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow
 '
@@ -373,6 +380,31 @@ test_expect_success 'clone shallow with packed refs' '
        test_cmp count8.expected count8.actual
 '
 
+test_expect_success 'fetch in shallow repo unreachable shallow objects' '
+       (
+               git clone --bare --branch B --single-branch "file://$(pwd)/." no-reflog &&
+               git clone --depth 1 "file://$(pwd)/no-reflog" shallow9 &&
+               cd no-reflog &&
+               git tag -d TAGB1 TAGB2 &&
+               git update-ref refs/heads/B B~~ &&
+               git gc --prune=now &&
+               cd ../shallow9 &&
+               git fetch origin &&
+               git fsck --no-dangling
+       )
+'
+test_expect_success 'fetch creating new shallow root' '
+       (
+               git clone "file://$(pwd)/." shallow10 &&
+               git commit --allow-empty -m empty &&
+               cd shallow10 &&
+               git fetch --depth=1 --progress 2>actual &&
+               # This should fetch only the empty commit, no tree or
+               # blob objects
+               grep "remote: Total 1" actual
+       )
+'
+
 test_expect_success 'setup tests for the --stdin parameter' '
        for head in C D E F
        do
@@ -484,4 +516,20 @@ test_expect_success 'test --all, --depth, and explicit tag' '
        ) >out-adt 2>error-adt
 '
 
+test_expect_success 'shallow fetch with tags does not break the repository' '
+       mkdir repo1 &&
+       (
+               cd repo1 &&
+               git init &&
+               test_commit 1 &&
+               test_commit 2 &&
+               test_commit 3 &&
+               mkdir repo2 &&
+               cd repo2 &&
+               git init &&
+               git fetch --depth=2 ../.git master:branch &&
+               git fsck
+       )
+'
+
 test_done
index 6579a86724cec01b8c731ead2d4083dfe44293dd..8f6e3922dce2b676221610d83a87c5b36d3bc5b6 100755 (executable)
@@ -42,107 +42,104 @@ check_tracking_branch () {
 }
 
 test_expect_success setup '
-
        setup_repository one &&
        setup_repository two &&
        (
-               cd two && git branch another
+               cd two &&
+               git branch another
        ) &&
        git clone one test
-
 '
 
 test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
-(
-       cd test &&
-       tokens_match origin "$(git remote)" &&
-       check_remote_track origin master side &&
-       check_tracking_branch origin HEAD master side
-)
+       (
+               cd test &&
+               tokens_match origin "$(git remote)" &&
+               check_remote_track origin master side &&
+               check_tracking_branch origin HEAD master side
+       )
 '
 
 test_expect_success 'add another remote' '
-(
-       cd test &&
-       git remote add -f second ../two &&
-       tokens_match "origin second" "$(git remote)" &&
-       check_tracking_branch second master side another &&
-       git for-each-ref "--format=%(refname)" refs/remotes |
-       sed -e "/^refs\/remotes\/origin\//d" \
-           -e "/^refs\/remotes\/second\//d" >actual &&
-       >expect &&
-       test_cmp expect actual
-)
+       (
+               cd test &&
+               git remote add -f second ../two &&
+               tokens_match "origin second" "$(git remote)" &&
+               check_tracking_branch second master side another &&
+               git for-each-ref "--format=%(refname)" refs/remotes |
+               sed -e "/^refs\/remotes\/origin\//d" \
+                   -e "/^refs\/remotes\/second\//d" >actual &&
+               >expect &&
+               test_cmp expect actual
+       )
 '
 
-test_expect_success C_LOCALE_OUTPUT 'check remote tracking' '
-(
-       cd test &&
-       check_remote_track origin master side &&
-       check_remote_track second master side another
-)
+test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '
+       (
+               cd test &&
+               check_remote_track origin master side &&
+               check_remote_track second master side another
+       )
 '
 
 test_expect_success 'remote forces tracking branches' '
-(
-       cd test &&
-       case `git config remote.second.fetch` in
-       +*) true ;;
-        *) false ;;
-       esac
-)
+       (
+               cd test &&
+               case `git config remote.second.fetch` in
+               +*) true ;;
+                *) false ;;
+               esac
+       )
 '
 
 test_expect_success 'remove remote' '
-(
-       cd test &&
-       git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
-       git remote rm second
-)
+       (
+               cd test &&
+               git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
+               git remote rm second
+       )
 '
 
 test_expect_success C_LOCALE_OUTPUT 'remove remote' '
-(
-       cd test &&
-       tokens_match origin "$(git remote)" &&
-       check_remote_track origin master side &&
-       git for-each-ref "--format=%(refname)" refs/remotes |
-       sed -e "/^refs\/remotes\/origin\//d" >actual &&
-       >expect &&
-       test_cmp expect actual
-)
+       (
+               cd test &&
+               tokens_match origin "$(git remote)" &&
+               check_remote_track origin master side &&
+               git for-each-ref "--format=%(refname)" refs/remotes |
+               sed -e "/^refs\/remotes\/origin\//d" >actual &&
+               >expect &&
+               test_cmp expect actual
+       )
 '
 
 test_expect_success 'remove remote protects local branches' '
-(
-       cd test &&
-       { cat >expect1 <<EOF
-Note: A branch outside the refs/remotes/ hierarchy was not removed;
-to delete it, use:
-  git branch -d master
-EOF
-       } &&
-       { cat >expect2 <<EOF
-Note: Some branches outside the refs/remotes/ hierarchy were not removed;
-to delete them, use:
-  git branch -d foobranch
-  git branch -d master
-EOF
-       } &&
-       git tag footag &&
-       git config --add remote.oops.fetch "+refs/*:refs/*" &&
-       git remote remove oops 2>actual1 &&
-       git branch foobranch &&
-       git config --add remote.oops.fetch "+refs/*:refs/*" &&
-       git remote rm oops 2>actual2 &&
-       git branch -d foobranch &&
-       git tag -d footag &&
-       test_i18ncmp expect1 actual1 &&
-       test_i18ncmp expect2 actual2
-)
-'
-
-cat > test/expect << EOF
+       (
+               cd test &&
+               cat >expect1 <<-\EOF &&
+               Note: A branch outside the refs/remotes/ hierarchy was not removed;
+               to delete it, use:
+                 git branch -d master
+               EOF
+               cat >expect2 <<-\EOF &&
+               Note: Some branches outside the refs/remotes/ hierarchy were not removed;
+               to delete them, use:
+                 git branch -d foobranch
+                 git branch -d master
+               EOF
+               git tag footag &&
+               git config --add remote.oops.fetch "+refs/*:refs/*" &&
+               git remote remove oops 2>actual1 &&
+               git branch foobranch &&
+               git config --add remote.oops.fetch "+refs/*:refs/*" &&
+               git remote rm oops 2>actual2 &&
+               git branch -d foobranch &&
+               git tag -d footag &&
+               test_i18ncmp expect1 actual1 &&
+               test_i18ncmp expect2 actual2
+       )
+'
+
+cat >test/expect <<EOF
 * remote origin
   Fetch URL: $(pwd)/one
   Push  URL: $(pwd)/one
@@ -172,36 +169,40 @@ cat > test/expect << EOF
 EOF
 
 test_expect_success 'show' '
-       (cd test &&
-        git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
-        git fetch &&
-        git checkout -b ahead origin/master &&
-        echo 1 >> file &&
-        test_tick &&
-        git commit -m update file &&
-        git checkout master &&
-        git branch --track octopus origin/master &&
-        git branch --track rebase origin/master &&
-        git branch -d -r origin/master &&
-        git config --add remote.two.url ../two &&
-        git config --add remote.two.pushurl ../three &&
-        git config branch.rebase.rebase true &&
-        git config branch.octopus.merge "topic-a topic-b topic-c" &&
-        (cd ../one &&
-         echo 1 > file &&
-         test_tick &&
-         git commit -m update file) &&
-        git config --add remote.origin.push : &&
-        git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
-        git config --add remote.origin.push +refs/tags/lastbackup &&
-        git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
-        git config --add remote.two.push refs/heads/master:refs/heads/another &&
-        git remote show origin two > output &&
-        git branch -d rebase octopus &&
-        test_i18ncmp expect output)
-'
-
-cat > test/expect << EOF
+       (
+               cd test &&
+               git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
+               git fetch &&
+               git checkout -b ahead origin/master &&
+               echo 1 >>file &&
+               test_tick &&
+               git commit -m update file &&
+               git checkout master &&
+               git branch --track octopus origin/master &&
+               git branch --track rebase origin/master &&
+               git branch -d -r origin/master &&
+               git config --add remote.two.url ../two &&
+               git config --add remote.two.pushurl ../three &&
+               git config branch.rebase.rebase true &&
+               git config branch.octopus.merge "topic-a topic-b topic-c" &&
+               (
+                       cd ../one &&
+                       echo 1 >file &&
+                       test_tick &&
+                       git commit -m update file
+               ) &&
+               git config --add remote.origin.push : &&
+               git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
+               git config --add remote.origin.push +refs/tags/lastbackup &&
+               git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
+               git config --add remote.two.push refs/heads/master:refs/heads/another &&
+               git remote show origin two >output &&
+               git branch -d rebase octopus &&
+               test_i18ncmp expect output
+       )
+'
+
+cat >test/expect <<EOF
 * remote origin
   Fetch URL: $(pwd)/one
   Push  URL: $(pwd)/one
@@ -219,152 +220,187 @@ cat > test/expect << EOF
 EOF
 
 test_expect_success 'show -n' '
-       (mv one one.unreachable &&
-        cd test &&
-        git remote show -n origin > output &&
-        mv ../one.unreachable ../one &&
-        test_i18ncmp expect output)
+       mv one one.unreachable &&
+       (
+               cd test &&
+               git remote show -n origin >output &&
+               mv ../one.unreachable ../one &&
+               test_i18ncmp expect output
+       )
 '
 
 test_expect_success 'prune' '
-       (cd one &&
-        git branch -m side side2) &&
-       (cd test &&
-        git fetch origin &&
-        git remote prune origin &&
-        git rev-parse refs/remotes/origin/side2 &&
-        test_must_fail git rev-parse refs/remotes/origin/side)
+       (
+               cd one &&
+               git branch -m side side2
+       ) &&
+       (
+               cd test &&
+               git fetch origin &&
+               git remote prune origin &&
+               git rev-parse refs/remotes/origin/side2 &&
+               test_must_fail git rev-parse refs/remotes/origin/side
+       )
 '
 
 test_expect_success 'set-head --delete' '
-       (cd test &&
-        git symbolic-ref refs/remotes/origin/HEAD &&
-        git remote set-head --delete origin &&
-        test_must_fail git symbolic-ref refs/remotes/origin/HEAD)
+       (
+               cd test &&
+               git symbolic-ref refs/remotes/origin/HEAD &&
+               git remote set-head --delete origin &&
+               test_must_fail git symbolic-ref refs/remotes/origin/HEAD
+       )
 '
 
 test_expect_success 'set-head --auto' '
-       (cd test &&
-        git remote set-head --auto origin &&
-        echo refs/remotes/origin/master >expect &&
-        git symbolic-ref refs/remotes/origin/HEAD >output &&
-        test_cmp expect output
+       (
+               cd test &&
+               git remote set-head --auto origin &&
+               echo refs/remotes/origin/master >expect &&
+               git symbolic-ref refs/remotes/origin/HEAD >output &&
+               test_cmp expect output
        )
 '
 
-cat >test/expect <<EOF
+cat >test/expect <<\EOF
 error: Multiple remote HEAD branches. Please choose one explicitly with:
   git remote set-head two another
   git remote set-head two master
 EOF
 
 test_expect_success 'set-head --auto fails w/multiple HEADs' '
-       (cd test &&
-        test_must_fail git remote set-head --auto two >output 2>&1 &&
-       test_i18ncmp expect output)
+       (
+               cd test &&
+               test_must_fail git remote set-head --auto two >output 2>&1 &&
+               test_i18ncmp expect output
+       )
 '
 
-cat >test/expect <<EOF
+cat >test/expect <<\EOF
 refs/remotes/origin/side2
 EOF
 
 test_expect_success 'set-head explicit' '
-       (cd test &&
-        git remote set-head origin side2 &&
-        git symbolic-ref refs/remotes/origin/HEAD >output &&
-        git remote set-head origin master &&
-        test_cmp expect output)
+       (
+               cd test &&
+               git remote set-head origin side2 &&
+               git symbolic-ref refs/remotes/origin/HEAD >output &&
+               git remote set-head origin master &&
+               test_cmp expect output
+       )
 '
 
-cat > test/expect << EOF
+cat >test/expect <<EOF
 Pruning origin
 URL: $(pwd)/one
  * [would prune] origin/side2
 EOF
 
 test_expect_success 'prune --dry-run' '
-       (cd one &&
-        git branch -m side2 side) &&
-       (cd test &&
-        git remote prune --dry-run origin > output &&
-        git rev-parse refs/remotes/origin/side2 &&
-        test_must_fail git rev-parse refs/remotes/origin/side &&
-       (cd ../one &&
-        git branch -m side side2) &&
-        test_i18ncmp expect output)
+       (
+               cd one &&
+               git branch -m side2 side) &&
+       (
+               cd test &&
+               git remote prune --dry-run origin >output &&
+               git rev-parse refs/remotes/origin/side2 &&
+               test_must_fail git rev-parse refs/remotes/origin/side &&
+       (
+               cd ../one &&
+               git branch -m side side2) &&
+               test_i18ncmp expect output
+       )
 '
 
 test_expect_success 'add --mirror && prune' '
-       (mkdir mirror &&
-        cd mirror &&
-        git init --bare &&
-        git remote add --mirror -f origin ../one) &&
-       (cd one &&
-        git branch -m side2 side) &&
-       (cd mirror &&
-        git rev-parse --verify refs/heads/side2 &&
-        test_must_fail git rev-parse --verify refs/heads/side &&
-        git fetch origin &&
-        git remote prune origin &&
-        test_must_fail git rev-parse --verify refs/heads/side2 &&
-        git rev-parse --verify refs/heads/side)
+       mkdir mirror &&
+       (
+               cd mirror &&
+               git init --bare &&
+               git remote add --mirror -f origin ../one
+       ) &&
+       (
+               cd one &&
+               git branch -m side2 side
+       ) &&
+       (
+               cd mirror &&
+               git rev-parse --verify refs/heads/side2 &&
+               test_must_fail git rev-parse --verify refs/heads/side &&
+               git fetch origin &&
+               git remote prune origin &&
+               test_must_fail git rev-parse --verify refs/heads/side2 &&
+               git rev-parse --verify refs/heads/side
+       )
 '
 
 test_expect_success 'add --mirror=fetch' '
        mkdir mirror-fetch &&
        git init mirror-fetch/parent &&
-       (cd mirror-fetch/parent &&
-        test_commit one) &&
+       (
+               cd mirror-fetch/parent &&
+               test_commit one
+       ) &&
        git init --bare mirror-fetch/child &&
-       (cd mirror-fetch/child &&
-        git remote add --mirror=fetch -f parent ../parent)
+       (
+               cd mirror-fetch/child &&
+               git remote add --mirror=fetch -f parent ../parent
+       )
 '
 
 test_expect_success 'fetch mirrors act as mirrors during fetch' '
-       (cd mirror-fetch/parent &&
-        git branch new &&
-        git branch -m master renamed
+       (
+               cd mirror-fetch/parent &&
+               git branch new &&
+               git branch -m master renamed
        ) &&
-       (cd mirror-fetch/child &&
-        git fetch parent &&
-        git rev-parse --verify refs/heads/new &&
-        git rev-parse --verify refs/heads/renamed
+       (
+               cd mirror-fetch/child &&
+               git fetch parent &&
+               git rev-parse --verify refs/heads/new &&
+               git rev-parse --verify refs/heads/renamed
        )
 '
 
 test_expect_success 'fetch mirrors can prune' '
-       (cd mirror-fetch/child &&
-        git remote prune parent &&
-        test_must_fail git rev-parse --verify refs/heads/master
+       (
+               cd mirror-fetch/child &&
+               git remote prune parent &&
+               test_must_fail git rev-parse --verify refs/heads/master
        )
 '
 
 test_expect_success 'fetch mirrors do not act as mirrors during push' '
-       (cd mirror-fetch/parent &&
-        git checkout HEAD^0
+       (
+               cd mirror-fetch/parent &&
+               git checkout HEAD^0
        ) &&
-       (cd mirror-fetch/child &&
-        git branch -m renamed renamed2 &&
-        git push parent :
+       (
+               cd mirror-fetch/child &&
+               git branch -m renamed renamed2 &&
+               git push parent :
        ) &&
-       (cd mirror-fetch/parent &&
-        git rev-parse --verify renamed &&
-        test_must_fail git rev-parse --verify refs/heads/renamed2
+       (
+               cd mirror-fetch/parent &&
+               git rev-parse --verify renamed &&
+               test_must_fail git rev-parse --verify refs/heads/renamed2
        )
 '
 
 test_expect_success 'add fetch mirror with specific branches' '
        git init --bare mirror-fetch/track &&
-       (cd mirror-fetch/track &&
-        git remote add --mirror=fetch -t heads/new parent ../parent
+       (
+               cd mirror-fetch/track &&
+               git remote add --mirror=fetch -t heads/new parent ../parent
        )
 '
 
 test_expect_success 'fetch mirror respects specific branches' '
-       (cd mirror-fetch/track &&
-        git fetch parent &&
-        git rev-parse --verify refs/heads/new &&
-        test_must_fail git rev-parse --verify refs/heads/renamed
+       (
+               cd mirror-fetch/track &&
+               git fetch parent &&
+               git rev-parse --verify refs/heads/new &&
+               test_must_fail git rev-parse --verify refs/heads/renamed
        )
 '
 
@@ -372,60 +408,72 @@ test_expect_success 'add --mirror=push' '
        mkdir mirror-push &&
        git init --bare mirror-push/public &&
        git init mirror-push/private &&
-       (cd mirror-push/private &&
-        test_commit one &&
-        git remote add --mirror=push public ../public
+       (
+               cd mirror-push/private &&
+               test_commit one &&
+               git remote add --mirror=push public ../public
        )
 '
 
 test_expect_success 'push mirrors act as mirrors during push' '
-       (cd mirror-push/private &&
-        git branch new &&
-        git branch -m master renamed &&
-        git push public
+       (
+               cd mirror-push/private &&
+               git branch new &&
+               git branch -m master renamed &&
+               git push public
        ) &&
-       (cd mirror-push/private &&
-        git rev-parse --verify refs/heads/new &&
-        git rev-parse --verify refs/heads/renamed &&
-        test_must_fail git rev-parse --verify refs/heads/master
+       (
+               cd mirror-push/private &&
+               git rev-parse --verify refs/heads/new &&
+               git rev-parse --verify refs/heads/renamed &&
+               test_must_fail git rev-parse --verify refs/heads/master
        )
 '
 
 test_expect_success 'push mirrors do not act as mirrors during fetch' '
-       (cd mirror-push/public &&
-        git branch -m renamed renamed2 &&
-        git symbolic-ref HEAD refs/heads/renamed2
+       (
+               cd mirror-push/public &&
+               git branch -m renamed renamed2 &&
+               git symbolic-ref HEAD refs/heads/renamed2
        ) &&
-       (cd mirror-push/private &&
-        git fetch public &&
-        git rev-parse --verify refs/heads/renamed &&
-        test_must_fail git rev-parse --verify refs/heads/renamed2
+       (
+               cd mirror-push/private &&
+               git fetch public &&
+               git rev-parse --verify refs/heads/renamed &&
+               test_must_fail git rev-parse --verify refs/heads/renamed2
        )
 '
 
 test_expect_success 'push mirrors do not allow you to specify refs' '
        git init mirror-push/track &&
-       (cd mirror-push/track &&
-        test_must_fail git remote add --mirror=push -t new public ../public
+       (
+               cd mirror-push/track &&
+               test_must_fail git remote add --mirror=push -t new public ../public
        )
 '
 
 test_expect_success 'add alt && prune' '
-       (mkdir alttst &&
-        cd alttst &&
-        git init &&
-        git remote add -f origin ../one &&
-        git config remote.alt.url ../one &&
-        git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") &&
-       (cd one &&
-        git branch -m side side2) &&
-       (cd alttst &&
-        git rev-parse --verify refs/remotes/origin/side &&
-        test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
-        git fetch alt &&
-        git remote prune alt &&
-        test_must_fail git rev-parse --verify refs/remotes/origin/side &&
-        git rev-parse --verify refs/remotes/origin/side2)
+       mkdir alttst &&
+       (
+               cd alttst &&
+               git init &&
+               git remote add -f origin ../one &&
+               git config remote.alt.url ../one &&
+               git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*"
+       ) &&
+       (
+               cd one &&
+               git branch -m side side2
+       ) &&
+       (
+               cd alttst &&
+               git rev-parse --verify refs/remotes/origin/side &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
+               git fetch alt &&
+               git remote prune alt &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/side &&
+               git rev-parse --verify refs/remotes/origin/side2
+       )
 '
 
 cat >test/expect <<\EOF
@@ -433,20 +481,24 @@ some-tag
 EOF
 
 test_expect_success 'add with reachable tags (default)' '
-       (cd one &&
-        >foobar &&
-        git add foobar &&
-        git commit -m "Foobar" &&
-        git tag -a -m "Foobar tag" foobar-tag &&
-        git reset --hard HEAD~1 &&
-        git tag -a -m "Some tag" some-tag) &&
-       (mkdir add-tags &&
-        cd add-tags &&
-        git init &&
-        git remote add -f origin ../one &&
-        git tag -l some-tag >../test/output &&
-        git tag -l foobar-tag >>../test/output &&
-        test_must_fail git config remote.origin.tagopt) &&
+       (
+               cd one &&
+               >foobar &&
+               git add foobar &&
+               git commit -m "Foobar" &&
+               git tag -a -m "Foobar tag" foobar-tag &&
+               git reset --hard HEAD~1 &&
+               git tag -a -m "Some tag" some-tag
+       ) &&
+       mkdir add-tags &&
+       (
+               cd add-tags &&
+               git init &&
+               git remote add -f origin ../one &&
+               git tag -l some-tag >../test/output &&
+               git tag -l foobar-tag >>../test/output &&
+               test_must_fail git config remote.origin.tagopt
+       ) &&
        test_cmp test/expect test/output
 '
 
@@ -457,14 +509,16 @@ foobar-tag
 EOF
 
 test_expect_success 'add --tags' '
-       (rm -rf add-tags &&
-        mkdir add-tags &&
-        cd add-tags &&
-        git init &&
-        git remote add -f --tags origin ../one &&
-        git tag -l some-tag >../test/output &&
-        git tag -l foobar-tag >>../test/output &&
-        git config remote.origin.tagopt >>../test/output) &&
+       rm -rf add-tags &&
+       (
+               mkdir add-tags &&
+               cd add-tags &&
+               git init &&
+               git remote add -f --tags origin ../one &&
+               git tag -l some-tag >../test/output &&
+               git tag -l foobar-tag >>../test/output &&
+               git config remote.origin.tagopt >>../test/output
+       ) &&
        test_cmp test/expect test/output
 '
 
@@ -473,25 +527,31 @@ cat >test/expect <<\EOF
 EOF
 
 test_expect_success 'add --no-tags' '
-       (rm -rf add-tags &&
-        mkdir add-no-tags &&
-        cd add-no-tags &&
-        git init &&
-        git remote add -f --no-tags origin ../one &&
-        git tag -l some-tag >../test/output &&
-        git tag -l foobar-tag >../test/output &&
-        git config remote.origin.tagopt >>../test/output) &&
-       (cd one &&
-        git tag -d some-tag foobar-tag) &&
+       rm -rf add-tags &&
+       (
+               mkdir add-no-tags &&
+               cd add-no-tags &&
+               git init &&
+               git remote add -f --no-tags origin ../one &&
+               git tag -l some-tag >../test/output &&
+               git tag -l foobar-tag >../test/output &&
+               git config remote.origin.tagopt >>../test/output
+       ) &&
+       (
+               cd one &&
+               git tag -d some-tag foobar-tag
+       ) &&
        test_cmp test/expect test/output
 '
 
 test_expect_success 'reject --no-no-tags' '
-       (cd add-no-tags &&
-        test_must_fail git remote add -f --no-no-tags neworigin ../one)
+       (
+               cd add-no-tags &&
+               test_must_fail git remote add -f --no-no-tags neworigin ../one
+       )
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   apis/master
   apis/side
   drosophila/another
@@ -500,17 +560,17 @@ cat > one/expect << EOF
 EOF
 
 test_expect_success 'update' '
-
-       (cd one &&
-        git remote add drosophila ../two &&
-        git remote add apis ../mirror &&
-        git remote update &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+       (
+               cd one &&
+               git remote add drosophila ../two &&
+               git remote add apis ../mirror &&
+               git remote update &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   drosophila/another
   drosophila/master
   drosophila/side
@@ -521,34 +581,40 @@ cat > one/expect << EOF
 EOF
 
 test_expect_success 'update with arguments' '
-
-       (cd one &&
-        for b in $(git branch -r)
-        do
+       (
+               cd one &&
+               for b in $(git branch -r)
+               do
                git branch -r -d $b || break
-        done &&
-        git remote add manduca ../mirror &&
-        git remote add megaloprepus ../mirror &&
-        git config remotes.phobaeticus "drosophila megaloprepus" &&
-        git config remotes.titanus manduca &&
-        git remote update phobaeticus titanus &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+               done &&
+               git remote add manduca ../mirror &&
+               git remote add megaloprepus ../mirror &&
+               git config remotes.phobaeticus "drosophila megaloprepus" &&
+               git config remotes.titanus manduca &&
+               git remote update phobaeticus titanus &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
 test_expect_success 'update --prune' '
-
-       (cd one &&
-        git branch -m side2 side3) &&
-       (cd test &&
-        git remote update --prune &&
-        (cd ../one && git branch -m side3 side2) &&
-        git rev-parse refs/remotes/origin/side3 &&
-        test_must_fail git rev-parse refs/remotes/origin/side2)
+       (
+               cd one &&
+               git branch -m side2 side3
+       ) &&
+       (
+               cd test &&
+               git remote update --prune &&
+               (
+                       cd ../one &&
+                       git branch -m side3 side2
+               ) &&
+               git rev-parse refs/remotes/origin/side3 &&
+               test_must_fail git rev-parse refs/remotes/origin/side2
+       )
 '
 
-cat > one/expect << EOF
+cat >one/expect <<-\EOF
   apis/master
   apis/side
   manduca/master
@@ -558,176 +624,204 @@ cat > one/expect << EOF
 EOF
 
 test_expect_success 'update default' '
-
-       (cd one &&
-        for b in $(git branch -r)
-        do
+       (
+               cd one &&
+               for b in $(git branch -r)
+               do
                git branch -r -d $b || break
-        done &&
-        git config remote.drosophila.skipDefaultUpdate true &&
-        git remote update default &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+               done &&
+               git config remote.drosophila.skipDefaultUpdate true &&
+               git remote update default &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
-cat > one/expect << EOF
+cat >one/expect <<\EOF
   drosophila/another
   drosophila/master
   drosophila/side
 EOF
 
 test_expect_success 'update default (overridden, with funny whitespace)' '
-
-       (cd one &&
-        for b in $(git branch -r)
-        do
+       (
+               cd one &&
+               for b in $(git branch -r)
+               do
                git branch -r -d $b || break
-        done &&
-        git config remotes.default "$(printf "\t drosophila  \n")" &&
-        git remote update default &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+               done &&
+               git config remotes.default "$(printf "\t drosophila  \n")" &&
+               git remote update default &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
 test_expect_success 'update (with remotes.default defined)' '
-
-       (cd one &&
-        for b in $(git branch -r)
-        do
+       (
+               cd one &&
+               for b in $(git branch -r)
+               do
                git branch -r -d $b || break
-        done &&
-        git config remotes.default "drosophila" &&
-        git remote update &&
-        git branch -r > output &&
-        test_cmp expect output)
-
+               done &&
+               git config remotes.default "drosophila" &&
+               git remote update &&
+               git branch -r >output &&
+               test_cmp expect output
+       )
 '
 
 test_expect_success '"remote show" does not show symbolic refs' '
-
        git clone one three &&
-       (cd three &&
-        git remote show origin > output &&
-        ! grep "^ *HEAD$" < output &&
-        ! grep -i stale < output)
-
+       (
+               cd three &&
+               git remote show origin >output &&
+               ! grep "^ *HEAD$" < output &&
+               ! grep -i stale < output
+       )
 '
 
 test_expect_success 'reject adding remote with an invalid name' '
-
        test_must_fail git remote add some:url desired-name
-
 '
 
 # The first three test if the tracking branches are properly renamed,
 # the last two ones check if the config is updated.
 
 test_expect_success 'rename a remote' '
-
        git clone one four &&
-       (cd four &&
-        git remote rename origin upstream &&
-        rmdir .git/refs/remotes/origin &&
-        test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
-        test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
-        test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
-        test "$(git config branch.master.remote)" = "upstream")
-
+       (
+               cd four &&
+               git remote rename origin upstream &&
+               rmdir .git/refs/remotes/origin &&
+               test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
+               test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
+               test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
+               test "$(git config branch.master.remote)" = "upstream"
+       )
 '
 
 test_expect_success 'rename does not update a non-default fetch refspec' '
-
        git clone one four.one &&
-       (cd four.one &&
-        git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
-        git remote rename origin upstream &&
-        test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
-        git rev-parse -q origin/master)
-
+       (
+               cd four.one &&
+               git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
+               git remote rename origin upstream &&
+               test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
+               git rev-parse -q origin/master
+       )
 '
 
 test_expect_success 'rename a remote with name part of fetch spec' '
-
        git clone one four.two &&
-       (cd four.two &&
-        git remote rename origin remote &&
-        git remote rename remote upstream &&
-        test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*")
-
+       (
+               cd four.two &&
+               git remote rename origin remote &&
+               git remote rename remote upstream &&
+               test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*"
+       )
 '
 
 test_expect_success 'rename a remote with name prefix of other remote' '
-
        git clone one four.three &&
-       (cd four.three &&
-        git remote add o git://example.com/repo.git &&
-        git remote rename o upstream &&
-        test "$(git rev-parse origin/master)" = "$(git rev-parse master)")
-
+       (
+               cd four.three &&
+               git remote add o git://example.com/repo.git &&
+               git remote rename o upstream &&
+               test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+       )
 '
 
-cat > remotes_origin << EOF
+cat >remotes_origin <<EOF
 URL: $(pwd)/one
 Push: refs/heads/master:refs/heads/upstream
+Push: refs/heads/next:refs/heads/upstream2
 Pull: refs/heads/master:refs/heads/origin
+Pull: refs/heads/next:refs/heads/origin2
 EOF
 
 test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
        git clone one five &&
        origin_url=$(pwd)/one &&
-       (cd five &&
-        git remote remove origin &&
-        mkdir -p .git/remotes &&
-        cat ../remotes_origin > .git/remotes/origin &&
-        git remote rename origin origin &&
-        ! test -f .git/remotes/origin &&
-        test "$(git config remote.origin.url)" = "$origin_url" &&
-        test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" &&
-        test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+       (
+               cd five &&
+               git remote remove origin &&
+               mkdir -p .git/remotes &&
+               cat ../remotes_origin >.git/remotes/origin &&
+               git remote rename origin origin &&
+               test_path_is_missing .git/remotes/origin &&
+               test "$(git config remote.origin.url)" = "$origin_url" &&
+               cat >push_expected <<-\EOF &&
+               refs/heads/master:refs/heads/upstream
+               refs/heads/next:refs/heads/upstream2
+               EOF
+               cat >fetch_expected <<-\EOF &&
+               refs/heads/master:refs/heads/origin
+               refs/heads/next:refs/heads/origin2
+               EOF
+               git config --get-all remote.origin.push >push_actual &&
+               git config --get-all remote.origin.fetch >fetch_actual &&
+               test_cmp push_expected push_actual &&
+               test_cmp fetch_expected fetch_actual
+       )
 '
 
 test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
        git clone one six &&
        origin_url=$(pwd)/one &&
-       (cd six &&
-        git remote rm origin &&
-        echo "$origin_url" > .git/branches/origin &&
-        git remote rename origin origin &&
-        ! test -f .git/branches/origin &&
-        test "$(git config remote.origin.url)" = "$origin_url" &&
-        test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+       (
+               cd six &&
+               git remote rm origin &&
+               echo "$origin_url" >.git/branches/origin &&
+               git remote rename origin origin &&
+               test_path_is_missing .git/branches/origin &&
+               test "$(git config remote.origin.url)" = "$origin_url" &&
+               test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin" &&
+               test "$(git config remote.origin.push)" = "HEAD:refs/heads/master"
+       )
 '
 
-test_expect_success 'remote prune to cause a dangling symref' '
+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
        git clone one seven &&
+       (
+               cd seven &&
+               git remote rm origin &&
+               echo "quux#foom" > .git/branches/origin &&
+               git remote rename origin origin &&
+               test_path_is_missing .git/branches/origin &&
+               test "$(git config remote.origin.url)" = "quux" &&
+               test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin"
+               test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
+       )
+'
+
+test_expect_success 'remote prune to cause a dangling symref' '
+       git clone one eight &&
        (
                cd one &&
                git checkout side2 &&
                git branch -D master
        ) &&
        (
-               cd seven &&
+               cd eight &&
                git remote prune origin
        ) >err 2>&1 &&
        test_i18ngrep "has become dangling" err &&
 
        : And the dangling symref will not cause other annoying errors &&
        (
-               cd seven &&
+               cd eight &&
                git branch -a
        ) 2>err &&
        ! grep "points nowhere" err &&
        (
-               cd seven &&
+               cd eight &&
                test_must_fail git branch nomore origin
        ) 2>err &&
        grep "dangling symref" err
 '
 
 test_expect_success 'show empty remote' '
-
        test_create_repo empty &&
        git clone empty empty-clone &&
        (
@@ -1003,4 +1097,26 @@ test_expect_success 'remote set-url --delete baz' '
        cmp expect actual
 '
 
+test_expect_success 'extra args: setup' '
+       # add a dummy origin so that this does not trigger failure
+       git remote add origin .
+'
+
+test_extra_arg () {
+       test_expect_success "extra args: $*" "
+               test_must_fail git remote $* bogus_extra_arg 2>actual &&
+               grep '^usage:' actual
+       "
+}
+
+test_extra_arg add nick url
+test_extra_arg rename origin newname
+test_extra_arg remove origin
+test_extra_arg set-head origin master
+# set-branches takes any number of args
+test_extra_arg set-url origin newurl oldurl
+# show takes any number of args
+# prune takes any number of args
+# update takes any number of args
+
 test_done
index d7a19a182941c7ab54b81bb8013835cfb8c5919f..fde689166a5dcb419e0519580d628f91f1f29f83 100755 (executable)
@@ -370,30 +370,39 @@ test_expect_success 'bundle should record HEAD correctly' '
 
 '
 
-test_expect_success 'explicit fetch should not update tracking' '
+test_expect_success 'mark initial state of origin/master' '
+       (
+               cd three &&
+               git tag base-origin-master refs/remotes/origin/master
+       )
+'
+
+test_expect_success 'explicit fetch should update tracking' '
 
        cd "$D" &&
        git branch -f side &&
        (
                cd three &&
+               git update-ref refs/remotes/origin/master base-origin-master &&
                o=$(git rev-parse --verify refs/remotes/origin/master) &&
                git fetch origin master &&
                n=$(git rev-parse --verify refs/remotes/origin/master) &&
-               test "$o" = "$n" &&
+               test "$o" != "$n" &&
                test_must_fail git rev-parse --verify refs/remotes/origin/side
        )
 '
 
-test_expect_success 'explicit pull should not update tracking' '
+test_expect_success 'explicit pull should update tracking' '
 
        cd "$D" &&
        git branch -f side &&
        (
                cd three &&
+               git update-ref refs/remotes/origin/master base-origin-master &&
                o=$(git rev-parse --verify refs/remotes/origin/master) &&
                git pull origin master &&
                n=$(git rev-parse --verify refs/remotes/origin/master) &&
-               test "$o" = "$n" &&
+               test "$o" != "$n" &&
                test_must_fail git rev-parse --verify refs/remotes/origin/side
        )
 '
@@ -404,6 +413,7 @@ test_expect_success 'configured fetch updates tracking' '
        git branch -f side &&
        (
                cd three &&
+               git update-ref refs/remotes/origin/master base-origin-master &&
                o=$(git rev-parse --verify refs/remotes/origin/master) &&
                git fetch origin &&
                n=$(git rev-parse --verify refs/remotes/origin/master) &&
@@ -412,6 +422,22 @@ test_expect_success 'configured fetch updates tracking' '
        )
 '
 
+test_expect_success 'non-matching refspecs do not confuse tracking update' '
+       cd "$D" &&
+       git update-ref refs/odd/location HEAD &&
+       (
+               cd three &&
+               git update-ref refs/remotes/origin/master base-origin-master &&
+               git config --add remote.origin.fetch \
+                       refs/odd/location:refs/remotes/origin/odd &&
+               o=$(git rev-parse --verify refs/remotes/origin/master) &&
+               git fetch origin master &&
+               n=$(git rev-parse --verify refs/remotes/origin/master) &&
+               test "$o" != "$n" &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/odd
+       )
+'
+
 test_expect_success 'pushing nonexistent branch by mistake should not segv' '
 
        cd "$D" &&
index 6af6c63350383e049082e61d3c8133af79d5a749..ed4d9c83181bc45456074f053ed5a0f137fc7e27 100755 (executable)
@@ -57,6 +57,35 @@ test_expect_success 'pulling into void does not overwrite untracked files' '
        )
 '
 
+test_expect_success 'pulling into void does not overwrite staged files' '
+       git init cloned-staged-colliding &&
+       (
+               cd cloned-staged-colliding &&
+               echo "alternate content" >file &&
+               git add file &&
+               test_must_fail git pull .. master &&
+               echo "alternate content" >expect &&
+               test_cmp expect file &&
+               git cat-file blob :file >file.index &&
+               test_cmp expect file.index
+       )
+'
+
+
+test_expect_success 'pulling into void does not remove new staged files' '
+       git init cloned-staged-new &&
+       (
+               cd cloned-staged-new &&
+               echo "new tracked file" >newfile &&
+               git add newfile &&
+               git pull .. master &&
+               echo "new tracked file" >expect &&
+               test_cmp expect newfile &&
+               git cat-file blob :newfile >newfile.index &&
+               test_cmp expect newfile.index
+       )
+'
+
 test_expect_success 'test . as a remote' '
 
        git branch copy master &&
index aa31abe32b3abc1b43d8111f6503313aa8237455..453aba53f45e34f6b17bcad7628f4224d6c2de4c 100755 (executable)
@@ -15,19 +15,19 @@ test_expect_success 'git pull -q' '
        mkdir clonedq &&
        (cd clonedq && git init &&
        git pull -q "../parent" >out 2>err &&
-       test ! -s err &&
-       test ! -s out)
+       test_must_be_empty err &&
+       test_must_be_empty out)
 '
 
 test_expect_success 'git pull -q --rebase' '
        mkdir clonedqrb &&
        (cd clonedqrb && git init &&
        git pull -q --rebase "../parent" >out 2>err &&
-       test ! -s err &&
-       test ! -s out &&
+       test_must_be_empty err &&
+       test_must_be_empty out &&
        git pull -q --rebase "../parent" >out 2>err &&
-       test ! -s err &&
-       test ! -s out)
+       test_must_be_empty err &&
+       test_must_be_empty out)
 '
 
 test_expect_success 'git pull' '
@@ -35,7 +35,7 @@ test_expect_success 'git pull' '
        (cd cloned && git init &&
        git pull "../parent" >out 2>err &&
        test -s err &&
-       test ! -s out)
+       test_must_be_empty out)
 '
 
 test_expect_success 'git pull --rebase' '
@@ -43,7 +43,7 @@ test_expect_success 'git pull --rebase' '
        (cd clonedrb && git init &&
        git pull --rebase "../parent" >out 2>err &&
        test -s err &&
-       test ! -s out)
+       test_must_be_empty out)
 '
 
 test_expect_success 'git pull -v' '
@@ -51,7 +51,7 @@ test_expect_success 'git pull -v' '
        (cd clonedv && git init &&
        git pull -v "../parent" >out 2>err &&
        test -s err &&
-       test ! -s out)
+       test_must_be_empty out)
 '
 
 test_expect_success 'git pull -v --rebase' '
@@ -59,22 +59,22 @@ test_expect_success 'git pull -v --rebase' '
        (cd clonedvrb && git init &&
        git pull -v --rebase "../parent" >out 2>err &&
        test -s err &&
-       test ! -s out)
+       test_must_be_empty out)
 '
 
 test_expect_success 'git pull -v -q' '
        mkdir clonedvq &&
        (cd clonedvq && git init &&
        git pull -v -q "../parent" >out 2>err &&
-       test ! -s out &&
-       test ! -s err)
+       test_must_be_empty out &&
+       test_must_be_empty err)
 '
 
 test_expect_success 'git pull -q -v' '
        mkdir clonedqv &&
        (cd clonedqv && git init &&
        git pull -q -v "../parent" >out 2>err &&
-       test ! -s out &&
+       test_must_be_empty out &&
        test -s err)
 '
 
index 4736da8f36fd4e69fea0008829f4304020ea338d..6a5ac3add47399918737168e60f75d2d7cec1557 100755 (executable)
@@ -15,17 +15,19 @@ test_expect_success 'setup bare remotes' '
 
 # $1 = local revision
 # $2 = remote revision (tested to be equal to the local one)
+# $3 = [optional] repo to check for actual output (repo1 by default)
 check_pushed_commit () {
        git log -1 --format='%h %s' "$1" >expect &&
-       git --git-dir=repo1 log -1 --format='%h %s' "$2" >actual &&
+       git --git-dir="${3:-repo1}" log -1 --format='%h %s' "$2" >actual &&
        test_cmp expect actual
 }
 
 # $1 = push.default value
 # $2 = expected target branch for the push
+# $3 = [optional] repo to check for actual output (repo1 by default)
 test_push_success () {
        git -c push.default="$1" push &&
-       check_pushed_commit HEAD "$2"
+       check_pushed_commit HEAD "$2" "$3"
 }
 
 # $1 = push.default value
@@ -37,6 +39,26 @@ test_push_failure () {
        test_cmp expect actual
 }
 
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+test_pushdefault_workflow () {
+       workflow=central
+       pushdefault=parent1
+       if test -n "${4-}"; then
+               workflow=triangular
+               pushdefault=parent2
+       fi
+       test_expect_success "push.default = $2 $1 in $workflow workflows" "
+               test_config branch.master.remote parent1 &&
+               test_config branch.master.merge refs/heads/foo &&
+               test_config remote.pushdefault $pushdefault &&
+               test_commit commit-for-$2${4+-triangular} &&
+               test_push_$1 $2 $3 ${4+repo2}
+       "
+}
+
 test_expect_success '"upstream" pushes to configured upstream' '
        git checkout master &&
        test_config branch.master.remote parent1 &&
@@ -48,7 +70,6 @@ test_expect_success '"upstream" pushes to configured upstream' '
 test_expect_success '"upstream" does not push on unconfigured remote' '
        git checkout master &&
        test_unconfig branch.master.remote &&
-       test_config push.default upstream &&
        test_commit three &&
        test_push_failure upstream
 '
@@ -57,7 +78,6 @@ test_expect_success '"upstream" does not push on unconfigured branch' '
        git checkout master &&
        test_config branch.master.remote parent1 &&
        test_unconfig branch.master.merge &&
-       test_config push.default upstream
        test_commit four &&
        test_push_failure upstream
 '
@@ -115,4 +135,41 @@ test_expect_success 'push to existing branch, upstream configured with different
        test_cmp expect-other-name actual-other-name
 '
 
+# We are on 'master', which integrates with 'foo' from parent1
+# remote (set in test_pushdefault_workflow helper).  Push to
+# parent1 in centralized, and push to parent2 in triangular workflow.
+# The parent1 repository has 'master' and 'foo' branches, while
+# the parent2 repository has only 'master' branch.
+#
+# test_pushdefault_workflow() arguments:
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+
+# update parent1's master (which is not our upstream)
+test_pushdefault_workflow success current master
+
+# update parent1's foo (which is our upstream)
+test_pushdefault_workflow success upstream foo
+
+# upsream is foo which is not the name of the current branch
+test_pushdefault_workflow failure simple master
+
+# master and foo are updated
+test_pushdefault_workflow success matching master
+
+# master is updated
+test_pushdefault_workflow success current master triangular
+
+# upstream mode cannot be used in triangular
+test_pushdefault_workflow failure upstream foo triangular
+
+# in triangular, 'simple' works as 'current' and update the branch
+# with the same name.
+test_pushdefault_workflow success simple master triangular
+
+# master is updated (parent2 does not have foo)
+test_pushdefault_workflow success matching master triangular
+
 test_done
index c983d3694c74cf6de6b7bd9a74c28d41462171f0..3932e797f7f60545e6e8c140824a5ca5ee16832a 100755 (executable)
@@ -54,9 +54,6 @@ test_expect_success 'upload-pack fails due to error in rev-list' '
        printf "0032want %s\n0034shallow %s00000009done\n0000" \
                $(git rev-parse HEAD) $(git rev-parse HEAD^) >input &&
        test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
-       # pack-objects survived
-       grep "Total.*, reused" output.err &&
-       # but there was an error, which must have been in rev-list
        grep "bad tree object" output.err
 '
 
index 4086f02bc129b3628c724c7037fe60509fbef46c..beb00be4b1593a058308d8897fed81a7cf1556d7 100755 (executable)
@@ -294,5 +294,35 @@ test_expect_success 'push to auth-only-for-push repo' '
        test_cmp expect actual
 '
 
+test_expect_success 'create repo without http.receivepack set' '
+       cd "$ROOT_PATH" &&
+       git init half-auth &&
+       (
+               cd half-auth &&
+               test_commit one
+       ) &&
+       git clone --bare half-auth "$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git"
+'
+
+test_expect_success 'clone via half-auth-complete does not need password' '
+       cd "$ROOT_PATH" &&
+       set_askpass wrong &&
+       git clone "$HTTPD_URL"/half-auth-complete/smart/half-auth.git \
+               half-auth-clone &&
+       expect_askpass none
+'
+
+test_expect_success 'push into half-auth-complete requires password' '
+       cd "$ROOT_PATH/half-auth-clone" &&
+       echo two >expect &&
+       test_commit two &&
+       set_askpass user@host &&
+       git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \
+               log -1 --format=%s >actual &&
+       expect_askpass both user@host &&
+       test_cmp expect actual
+'
+
 stop_httpd
 test_done
index b23efbbfd9586670f1eeee114a1335b9dad4d97f..55a866af803452e164e2c11c421b1e63f62d2e1e 100755 (executable)
@@ -209,13 +209,17 @@ test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
 
        # now assign tags to all the dangling commits we created above
        tag=$("$PERL_PATH" -e "print \"bla\" x 30") &&
-       sed -e "s/^:\(.\+\) \(.\+\)$/\2 refs\/tags\/$tag-\1/" <marks >>packed-refs
+       sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs
        )
 '
 
 test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' '
        git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err &&
-       test_line_count = 0 err
+       test_line_count = 0 err &&
+       (
+               cd too-many-refs &&
+               test $(git for-each-ref refs/tags | wc -l) = 50000
+       )
 '
 
 stop_httpd
index ef98d95e00d7d25ee408b311339cf4f10c44a5ad..9be9ae3436c929921bc0a2cd2a458e4e4500c154 100755 (executable)
@@ -5,7 +5,7 @@ test_description='test git-http-backend-noserver'
 
 HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
 
-test_have_prereq MINGW && export GREP_OPTIONS=-U
+test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U
 
 run_backend() {
        echo "$2" |
index 67869b4813dd354f4376ead1470ecb0e58929302..0629149eddcff4e6e724ab82479504deda141141 100755 (executable)
@@ -280,4 +280,9 @@ test_expect_success 'clone checking out a tag' '
        test_cmp fetch.expected fetch.actual
 '
 
+test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' '
+       cp -R src "foo:bar" &&
+       git clone "./foo:bar" foobar
+'
+
 test_done
index 60f1552adef417b8d37ca4215be1e66627c37258..6537911a4300656706c995226b2f0444a760bae0 100755 (executable)
@@ -185,4 +185,17 @@ test_expect_success 'fetch with incomplete alternates' '
        ! grep " want $tag_object" "$U.K"
 '
 
+test_expect_success 'clone using repo with gitfile as a reference' '
+       git clone --separate-git-dir=L A M &&
+       git clone --reference=M A N &&
+       echo "$base_dir/L/objects" >expected &&
+       test_cmp expected "$base_dir/N/.git/objects/info/alternates"
+'
+
+test_expect_success 'clone using repo pointed at by gitfile as reference' '
+       git clone --reference=M/.git A O &&
+       echo "$base_dir/L/objects" >expected &&
+       test_cmp expected "$base_dir/O/.git/objects/info/alternates"
+'
+
 test_done
index 02cb02472322e8fd6c31548d30950c937e301e5f..85cadfad6d8aeee79e0f05530376ca7dc6f085f0 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success 'clone -o' '
 test_expect_success 'redirected clone' '
 
        git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
-       test ! -s err
+       test_must_be_empty err
 
 '
 test_expect_success 'redirected clone -v' '
index 56be67e07e3c20f029a0bdeb2179e219ebc0bda1..6e7a7be052276613bf5f83551d93b4d90536ea4e 100755 (executable)
@@ -20,7 +20,9 @@ test_expect_success 'setup' '
         echo one >file && git add file && git commit -m one &&
         git checkout -b two &&
         echo two >file && git add file && git commit -m two &&
-        git checkout master)
+        git checkout master) &&
+       mkdir empty &&
+       (cd empty && git init)
 '
 
 test_expect_success 'vanilla clone chooses HEAD' '
@@ -61,4 +63,8 @@ test_expect_success 'clone -b with bogus branch' '
        test_must_fail git clone -b bogus parent clone-bogus
 '
 
+test_expect_success 'clone -b not allowed with empty repos' '
+       test_must_fail git clone -b branch empty clone-branch-empty
+'
+
 test_done
index 8956c21617410863660bff0bc22ec8e81903e81a..5a6e49d18d6f9e6e06409093d9b9f426d70de475 100755 (executable)
@@ -58,7 +58,13 @@ test_expect_success 'creating too deep nesting' \
 git clone -l -s D E &&
 git clone -l -s E F &&
 git clone -l -s F G &&
-test_must_fail git clone --bare -l -s G H'
+git clone --bare -l -s G H'
+
+test_expect_success 'invalidity of deepest repository' \
+'cd H && {
+       test_valid_repo
+       test $? -ne 0
+}'
 
 cd "$base_dir"
 
index f387027c05cf502c6892c7694598bef34376be92..8c4c5396a8447fc39d6f0c697af761631b08b3f7 100755 (executable)
@@ -6,11 +6,7 @@
 test_description='Test remote-helper import and export commands'
 
 . ./test-lib.sh
-
-if ! type "${BASH-bash}" >/dev/null 2>&1; then
-       skip_all='skipping remote-testgit tests, bash not available'
-       test_done
-fi
+. "$TEST_DIRECTORY"/lib-gpg.sh
 
 compare_refs() {
        git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
@@ -100,39 +96,28 @@ test_expect_failure 'push new branch with old:new refspec' '
 
 test_expect_success 'cloning without refspec' '
        GIT_REMOTE_TESTGIT_REFSPEC="" \
-       git clone "testgit::${PWD}/server" local2 &&
+       git clone "testgit::${PWD}/server" local2 2>error &&
+       grep "This remote helper should implement refspec capability" error &&
        compare_refs local2 HEAD server HEAD
 '
 
 test_expect_success 'pulling without refspecs' '
        (cd local2 &&
        git reset --hard &&
-       GIT_REMOTE_TESTGIT_REFSPEC="" git pull) &&
+       GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) &&
+       grep "This remote helper should implement refspec capability" error &&
        compare_refs local2 HEAD server HEAD
 '
 
-test_expect_failure 'pushing without refspecs' '
+test_expect_success 'pushing without refspecs' '
        test_when_finished "(cd local2 && git reset --hard origin)" &&
        (cd local2 &&
        echo content >>file &&
        git commit -a -m ten &&
-       GIT_REMOTE_TESTGIT_REFSPEC="" git push) &&
-       compare_refs local2 HEAD server HEAD
-'
-
-test_expect_success 'pulling with straight refspec' '
-       (cd local2 &&
-       GIT_REMOTE_TESTGIT_REFSPEC="*:*" git pull) &&
-       compare_refs local2 HEAD server HEAD
-'
-
-test_expect_failure 'pushing with straight refspec' '
-       test_when_finished "(cd local2 && git reset --hard origin)" &&
-       (cd local2 &&
-       echo content >>file &&
-       git commit -a -m eleven &&
-       GIT_REMOTE_TESTGIT_REFSPEC="*:*" git push) &&
-       compare_refs local2 HEAD server HEAD
+       GIT_REMOTE_TESTGIT_REFSPEC="" &&
+       export GIT_REMOTE_TESTGIT_REFSPEC &&
+       test_must_fail git push 2>../error) &&
+       grep "remote-helper doesn.t support push; refspec needed" error
 '
 
 test_expect_success 'pulling without marks' '
@@ -166,4 +151,81 @@ test_expect_success 'push ref with existing object' '
        compare_refs local dup server dup
 '
 
+test_expect_success GPG 'push signed tag' '
+       (cd local &&
+       git checkout master &&
+       git tag -s -m signed-tag signed-tag &&
+       git push origin signed-tag
+       ) &&
+       compare_refs local signed-tag^{} server signed-tag^{} &&
+       test_must_fail compare_refs local signed-tag server signed-tag
+'
+
+test_expect_success GPG 'push signed tag with signed-tags capability' '
+       (cd local &&
+       git checkout master &&
+       git tag -s -m signed-tag signed-tag-2 &&
+       GIT_REMOTE_TESTGIT_SIGNED_TAGS=1 git push origin signed-tag-2
+       ) &&
+       compare_refs local signed-tag-2 server signed-tag-2
+'
+
+test_expect_success 'push update refs' '
+       (cd local &&
+       git checkout -b update master &&
+       echo update >>file &&
+       git commit -a -m update &&
+       git push origin update &&
+       git rev-parse --verify remotes/origin/update >expect &&
+       git rev-parse --verify testgit/origin/heads/update >actual &&
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'push update refs failure' '
+       (cd local &&
+       git checkout update &&
+       echo "update fail" >>file &&
+       git commit -a -m "update fail" &&
+       git rev-parse --verify testgit/origin/heads/update >expect &&
+       GIT_REMOTE_TESTGIT_PUSH_ERROR="non-fast forward" &&
+       export GIT_REMOTE_TESTGIT_PUSH_ERROR &&
+       test_expect_code 1 git push origin update &&
+       git rev-parse --verify testgit/origin/heads/update >actual &&
+       test_cmp expect actual
+       )
+'
+
+test_expect_success 'proper failure checks for fetching' '
+       (GIT_REMOTE_TESTGIT_FAILURE=1 &&
+       export GIT_REMOTE_TESTGIT_FAILURE &&
+       cd local &&
+       test_must_fail git fetch 2> error &&
+       cat error &&
+       grep -q "Error while running fast-import" error
+       )
+'
+
+test_expect_success 'proper failure checks for pushing' '
+       (GIT_REMOTE_TESTGIT_FAILURE=1 &&
+       export GIT_REMOTE_TESTGIT_FAILURE &&
+       cd local &&
+       test_must_fail git push --all
+       )
+'
+
+test_expect_success 'push messages' '
+       (cd local &&
+       git checkout -b new_branch master &&
+       echo new >>file &&
+       git commit -a -m new &&
+       git push origin new_branch &&
+       git fetch origin &&
+       echo new >>file &&
+       git commit -a -m new &&
+       git push origin new_branch 2> msg &&
+       ! grep "\[new branch\]" msg
+       )
+'
+
 test_done
diff --git a/t/t5802-connect-helper.sh b/t/t5802-connect-helper.sh
new file mode 100755 (executable)
index 0000000..878faf2
--- /dev/null
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='ext::cmd remote "connect" helper'
+. ./test-lib.sh
+
+test_expect_success setup '
+       test_tick &&
+       git commit --allow-empty -m initial &&
+       test_tick &&
+       git commit --allow-empty -m second &&
+       test_tick &&
+       git commit --allow-empty -m third &&
+       test_tick &&
+       git tag -a -m "tip three" three &&
+
+       test_tick &&
+       git commit --allow-empty -m fourth
+'
+
+test_expect_success clone '
+       cmd=$(echo "echo >&2 ext::sh invoked && %S .." | sed -e "s/ /% /g") &&
+       git clone "ext::sh -c %S% ." dst &&
+       git for-each-ref refs/heads/ refs/tags/ >expect &&
+       (
+               cd dst &&
+               git config remote.origin.url "ext::sh -c $cmd" &&
+               git for-each-ref refs/heads/ refs/tags/
+       ) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'update following tag' '
+       test_tick &&
+       git commit --allow-empty -m fifth &&
+       test_tick &&
+       git tag -a -m "tip five" five &&
+       git for-each-ref refs/heads/ refs/tags/ >expect &&
+       (
+               cd dst &&
+               git pull &&
+               git for-each-ref refs/heads/ refs/tags/ >../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success 'update backfilled tag' '
+       test_tick &&
+       git commit --allow-empty -m sixth &&
+       test_tick &&
+       git tag -a -m "tip two" two three^1 &&
+       git for-each-ref refs/heads/ refs/tags/ >expect &&
+       (
+               cd dst &&
+               git pull &&
+               git for-each-ref refs/heads/ refs/tags/ >../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success 'update backfilled tag without primary transfer' '
+       test_tick &&
+       git tag -a -m "tip one " one two^1 &&
+       git for-each-ref refs/heads/ refs/tags/ >expect &&
+       (
+               cd dst &&
+               git pull &&
+               git for-each-ref refs/heads/ refs/tags/ >../actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_done
index fb07536a0f5b7e1000cf4945f55be3305df88e90..43ad7724845cd0e1523aadd60845f048a6b92ce5 100755 (executable)
@@ -39,25 +39,25 @@ test_bisection_diff()
 date >path0
 git update-index --add path0
 save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+on_committer_date "00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "00:08" save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "00:09" save_tag b3 unique_commit b2 tree -p b2
+on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4
 git update-ref HEAD $(tag l5)
 
 
@@ -90,29 +90,29 @@ git update-ref HEAD $(tag l5)
 #     F
 
 
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag F unique_commit F tree
-on_committer_date "1971-08-16 00:00:01" save_tag e8 unique_commit e8 tree -p F
-on_committer_date "1971-08-16 00:00:02" save_tag e7 unique_commit e7 tree -p e8
-on_committer_date "1971-08-16 00:00:03" save_tag e6 unique_commit e6 tree -p e7
-on_committer_date "1971-08-16 00:00:04" save_tag e5 unique_commit e5 tree -p e6
-on_committer_date "1971-08-16 00:00:05" save_tag f4 unique_commit f4 tree -p F
-on_committer_date "1971-08-16 00:00:06" save_tag f3 unique_commit f3 tree -p f4
-on_committer_date "1971-08-16 00:00:07" save_tag f2 unique_commit f2 tree -p f3
-on_committer_date "1971-08-16 00:00:08" save_tag f1 unique_commit f1 tree -p f2
-on_committer_date "1971-08-16 00:00:09" save_tag e4 unique_commit e4 tree -p e5
-on_committer_date "1971-08-16 00:00:10" save_tag e3 unique_commit e3 tree -p e4
-on_committer_date "1971-08-16 00:00:11" save_tag e2 unique_commit e2 tree -p e3
-on_committer_date "1971-08-16 00:00:12" save_tag e1 unique_commit e1 tree -p e2
-on_committer_date "1971-08-16 00:00:13" save_tag E unique_commit E tree -p e1 -p f1
-
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag U unique_commit U tree
-on_committer_date "1971-08-16 00:00:01" save_tag u0 unique_commit u0 tree -p U
-on_committer_date "1971-08-16 00:00:01" save_tag u1 unique_commit u1 tree -p u0
-on_committer_date "1971-08-16 00:00:02" save_tag u2 unique_commit u2 tree -p u0
-on_committer_date "1971-08-16 00:00:03" save_tag u3 unique_commit u3 tree -p u0
-on_committer_date "1971-08-16 00:00:04" save_tag u4 unique_commit u4 tree -p u0
-on_committer_date "1971-08-16 00:00:05" save_tag u5 unique_commit u5 tree -p u0
-on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
+on_committer_date "00:00" hide_error save_tag F unique_commit F tree
+on_committer_date "00:01" save_tag e8 unique_commit e8 tree -p F
+on_committer_date "00:02" save_tag e7 unique_commit e7 tree -p e8
+on_committer_date "00:03" save_tag e6 unique_commit e6 tree -p e7
+on_committer_date "00:04" save_tag e5 unique_commit e5 tree -p e6
+on_committer_date "00:05" save_tag f4 unique_commit f4 tree -p F
+on_committer_date "00:06" save_tag f3 unique_commit f3 tree -p f4
+on_committer_date "00:07" save_tag f2 unique_commit f2 tree -p f3
+on_committer_date "00:08" save_tag f1 unique_commit f1 tree -p f2
+on_committer_date "00:09" save_tag e4 unique_commit e4 tree -p e5
+on_committer_date "00:10" save_tag e3 unique_commit e3 tree -p e4
+on_committer_date "00:11" save_tag e2 unique_commit e2 tree -p e3
+on_committer_date "00:12" save_tag e1 unique_commit e1 tree -p e2
+on_committer_date "00:13" save_tag E unique_commit E tree -p e1 -p f1
+
+on_committer_date "00:00" hide_error save_tag U unique_commit U tree
+on_committer_date "00:01" save_tag u0 unique_commit u0 tree -p U
+on_committer_date "00:01" save_tag u1 unique_commit u1 tree -p u0
+on_committer_date "00:02" save_tag u2 unique_commit u2 tree -p u0
+on_committer_date "00:03" save_tag u3 unique_commit u3 tree -p u0
+on_committer_date "00:04" save_tag u4 unique_commit u4 tree -p u0
+on_committer_date "00:05" save_tag u5 unique_commit u5 tree -p u0
+on_committer_date "00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
 
 test_sequence()
 {
index e4c52b0214b5028e8c3db035dc96ca3285e61506..24d1836f417d67a224f0d9bef619952a39ed28eb 100755 (executable)
@@ -16,39 +16,34 @@ list_duplicates()
 date >path0
 git update-index --add path0
 save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b3 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
-on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
-on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
-on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree
-on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root
-on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0
-on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
-on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
+on_dates "00:00" "00:00" hide_error save_tag root unique_commit root tree
+on_dates "00:01" "00:01" save_tag l0 unique_commit l0 tree -p root
+on_dates "00:02" "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_dates "00:03" "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_dates "00:04" "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_dates "00:05" "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_dates "00:06" "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_dates "00:07" "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_dates "00:08" "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
+on_dates "00:09" "00:09" save_tag b3 unique_commit b3 tree -p b2
+on_dates "00:10" "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_dates "00:11" "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_dates "00:12" "00:00" save_tag a2 unique_commit a2 tree -p a1
+on_dates "00:13" "00:01" save_tag a3 unique_commit a3 tree -p a2
+on_dates "00:14" "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_dates "00:15" "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_dates "00:16" "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_dates "00:17" "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_dates "00:18" "00:18" save_tag l5 unique_commit l5 tree -p l4
+on_dates "00:19" "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
+on_dates "00:20" "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
+on_dates "00:21" "00:21" hide_error save_tag alt_root unique_commit alt_root tree
+on_dates "00:22" "00:22" save_tag r0 unique_commit r0 tree -p alt_root
+on_dates "00:23" "00:23" save_tag r1 unique_commit r1 tree -p r0
+on_dates "00:24" "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
+on_dates "00:25" "00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
 
 
-#
-# note: as of 20/6, it isn't possible to create duplicate parents, so this
-# can't be tested.
-#
-#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3
 hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
 save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
 save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
@@ -105,6 +100,50 @@ l0
 root
 EOF
 
+test_output_expect_success 'simple date order' 'git rev-list --date-order  HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+c2
+b3
+b2
+c1
+b1
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'simple author-date order' 'git rev-list --author-date-order  HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+c3
+c2
+b3
+b2
+c1
+b1
+a3
+a2
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
 test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order  g4' <<EOF
 g4
 h2
index 3fc3b74c8efa4e5092e36e6a6fbd2a15bc862d77..98744038eccb396c752e6a4b6ced702733c0d396 100755 (executable)
@@ -1,20 +1,45 @@
 #!/bin/sh
 
+# Copyright (c) 2009 Jens Lehmann
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
+
 test_description='git rev-list --pretty=format test'
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_tick
+# String "added" in German
+# (translated with Google Translate),
+# encoded in UTF-8, used as a commit log message below.
+added=$(printf "added (hinzugef\303\274gt) foo")
+added_iso88591=$(echo "$added" | iconv -f utf-8 -t iso8859-1)
+# same but "changed"
+changed=$(printf "changed (ge\303\244ndert) foo")
+changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t iso8859-1)
+
 test_expect_success 'setup' '
-touch foo && git add foo && git commit -m "added foo" &&
-  echo changed >foo && git commit -a -m "changed foo"
+       : >foo &&
+       git add foo &&
+       git config i18n.commitEncoding iso8859-1 &&
+       git commit -m "$added_iso88591" &&
+       head1=$(git rev-parse --verify HEAD) &&
+       head1_short=$(git rev-parse --verify --short $head1) &&
+       tree1=$(git rev-parse --verify HEAD:) &&
+       tree1_short=$(git rev-parse --verify --short $tree1) &&
+       echo "$changed" > foo &&
+       git commit -a -m "$changed_iso88591" &&
+       head2=$(git rev-parse --verify HEAD) &&
+       head2_short=$(git rev-parse --verify --short $head2) &&
+       tree2=$(git rev-parse --verify HEAD:) &&
+       tree2_short=$(git rev-parse --verify --short $tree2)
+       git config --unset i18n.commitEncoding
 '
 
-# usage: test_format name format_string <expected_output
+# usage: test_format name format_string [failure] <expected_output
 test_format () {
        cat >expect.$1
-       test_expect_success "format $1" "
+       test_expect_${3:-success} "format $1" "
                git rev-list --pretty=format:'$2' master >output.$1 &&
                test_cmp expect.$1 output.$1
        "
@@ -32,49 +57,49 @@ has_no_color () {
        test_cmp expect "$1"
 }
 
-test_format percent %%h <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format percent %%h <<EOF
+commit $head2
 %h
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 %h
 EOF
 
-test_format hash %H%n%h <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-131a310eb913d107dd3c09a65d1651175898735d
-131a310
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cf
+test_format hash %H%n%h <<EOF
+commit $head2
+$head2
+$head2_short
+commit $head1
+$head1
+$head1_short
 EOF
 
-test_format tree %T%n%t <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-fe722612f26da5064c32ca3843aa154bdb0b08a0
-fe72261
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-4d5fcadc293a348e88f777dc0920f11e7d71441c
-4d5fcad
+test_format tree %T%n%t <<EOF
+commit $head2
+$tree2
+$tree2_short
+commit $head1
+$tree1
+$tree1_short
 EOF
 
-test_format parents %P%n%p <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-86c75cf
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format parents %P%n%p <<EOF
+commit $head2
+$head1
+$head1_short
+commit $head1
 
 
 EOF
 
 # we don't test relative here
-test_format author %an%n%ae%n%ad%n%aD%n%at <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format author %an%n%ae%n%ad%n%aD%n%at <<EOF
+commit $head2
 A U Thor
 author@example.com
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 A U Thor
 author@example.com
 Thu Apr 7 15:13:13 2005 -0700
@@ -82,14 +107,14 @@ Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
 EOF
 
-test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<EOF
+commit $head2
 C O Mitter
 committer@example.com
 Thu Apr 7 15:13:13 2005 -0700
 Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 C O Mitter
 committer@example.com
 Thu Apr 7 15:13:13 2005 -0700
@@ -97,43 +122,45 @@ Thu, 7 Apr 2005 15:13:13 -0700
 1112911993
 EOF
 
-test_format encoding %e <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format encoding %e <<EOF
+commit $head2
+iso8859-1
+commit $head1
+iso8859-1
 EOF
 
-test_format subject %s <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+test_format subject %s <<EOF
+commit $head2
+$changed
+commit $head1
+$added
 EOF
 
-test_format body %b <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+test_format body %b <<EOF
+commit $head2
+commit $head1
 EOF
 
-test_format raw-body %B <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
+test_format raw-body %B <<EOF
+commit $head2
+$changed
 
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+commit $head1
+$added
 
 EOF
 
-test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<EOF
+commit $head2
 \e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 \e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
 EOF
 
-test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<'EOF'
-commit 131a310eb913d107dd3c09a65d1651175898735d
+test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<EOF
+commit $head2
 \e[1;31;43mfoo\e[m
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+commit $head1
 \e[1;31;43mfoo\e[m
 EOF
 
@@ -179,46 +206,71 @@ test_expect_success '%C(auto) respects --color=auto (stdout not tty)' '
        )
 '
 
-cat >commit-msg <<'EOF'
+iconv -f utf-8 -t iso8859-1 > commit-msg <<EOF
 Test printing of complex bodies
 
 This commit message is much longer than the others,
 and it will be encoded in iso8859-1. We should therefore
 include an iso8859 character: ¡bueno!
 EOF
+
 test_expect_success 'setup complex body' '
-git config i18n.commitencoding iso8859-1 &&
-  echo change2 >foo && git commit -a -F commit-msg
+       git config i18n.commitencoding iso8859-1 &&
+       echo change2 >foo && git commit -a -F commit-msg &&
+       head3=$(git rev-parse --verify HEAD) &&
+       head3_short=$(git rev-parse --short $head3)
 '
 
-test_format complex-encoding %e <<'EOF'
-commit f58db70b055c5718631e5c61528b28b12090cdea
+test_format complex-encoding %e <<EOF
+commit $head3
+iso8859-1
+commit $head2
+iso8859-1
+commit $head1
 iso8859-1
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
 EOF
 
-test_format complex-subject %s <<'EOF'
-commit f58db70b055c5718631e5c61528b28b12090cdea
+test_format complex-subject %s <<EOF
+commit $head3
 Test printing of complex bodies
-commit 131a310eb913d107dd3c09a65d1651175898735d
-changed foo
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
-added foo
+commit $head2
+$changed_iso88591
+commit $head1
+$added_iso88591
 EOF
 
-test_format complex-body %b <<'EOF'
-commit f58db70b055c5718631e5c61528b28b12090cdea
-This commit message is much longer than the others,
-and it will be encoded in iso8859-1. We should therefore
-include an iso8859 character: ¡bueno!
+test_expect_success 'prepare expected messages (for test %b)' '
+       cat <<-EOF >expected.utf-8 &&
+       commit $head3
+       This commit message is much longer than the others,
+       and it will be encoded in iso8859-1. We should therefore
+       include an iso8859 character: ¡bueno!
+
+       commit $head2
+       commit $head1
+       EOF
+       iconv -f utf-8 -t iso8859-1 expected.utf-8 >expected.iso8859-1
+'
+
+test_format complex-body %b <expected.iso8859-1
 
-commit 131a310eb913d107dd3c09a65d1651175898735d
-commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+# Git uses i18n.commitEncoding if no i18n.logOutputEncoding set
+# so unset i18n.commitEncoding to test encoding conversion
+git config --unset i18n.commitEncoding
+
+test_format complex-subject-commitencoding-unset %s <<EOF
+commit $head3
+Test printing of complex bodies
+commit $head2
+$changed
+commit $head1
+$added
 EOF
 
+test_format complex-body-commitencoding-unset %b <expected.utf-8
+
 test_expect_success '%x00 shows NUL' '
-       echo  >expect commit f58db70b055c5718631e5c61528b28b12090cdea &&
+       echo  >expect commit $head3 &&
        echo >>expect fooQbar &&
        git rev-list -1 --format=foo%x00bar HEAD >actual.nul &&
        nul_to_q <actual.nul >actual &&
@@ -265,12 +317,12 @@ test_expect_success 'add LF before non-empty (2)' '
 
 test_expect_success 'add SP before non-empty (1)' '
        git show -s --pretty=format:"%s% bThanks" HEAD^^ >actual &&
-       test $(wc -w <actual) = 2
+       test $(wc -w <actual) = 3
 '
 
 test_expect_success 'add SP before non-empty (2)' '
        git show -s --pretty=format:"%s% sThanks" HEAD^^ >actual &&
-       test $(wc -w <actual) = 4
+       test $(wc -w <actual) = 6
 '
 
 test_expect_success '--abbrev' '
index dd6dc844e787549ab7622fb4fbec4f89b52e22ab..fde5e712eb512791c893de7b393fe36e382a3f6d 100755 (executable)
@@ -14,21 +14,24 @@ unnote () {
 
 test_expect_success setup '
        echo "Hi there" >file &&
-       git add file &&
-       test_tick && git commit -m "Initial file" &&
+       echo "initial" >lost &&
+       git add file lost &&
+       test_tick && git commit -m "Initial file and lost" &&
        note A &&
 
        git branch other-branch &&
 
        echo "Hello" >file &&
-       git add file &&
-       test_tick && git commit -m "Modified file" &&
+       echo "second" >lost &&
+       git add file lost &&
+       test_tick && git commit -m "Modified file and lost" &&
        note B &&
 
        git checkout other-branch &&
 
        echo "Hello" >file &&
-       git add file &&
+       >lost &&
+       git add file lost &&
        test_tick && git commit -m "Modified the file identically" &&
        note C &&
 
@@ -37,7 +40,9 @@ test_expect_success setup '
        test_tick && git commit -m "Add another file" &&
        note D &&
 
-       test_tick && git merge -m "merge" master &&
+       test_tick &&
+       test_must_fail git merge -m "merge" master &&
+       >lost && git commit -a -m "merge" &&
        note E &&
 
        echo "Yet another" >elif &&
@@ -105,9 +110,27 @@ check_result 'L K J I H G F E D C B A' --full-history
 check_result 'K I H E C B A' --full-history -- file
 check_result 'K I H E C B A' --full-history --topo-order -- file
 check_result 'K I H E C B A' --full-history --date-order -- file
-check_outcome failure 'I E C B A' --simplify-merges -- file
+check_result 'I E C B A' --simplify-merges -- file
 check_result 'I B A' -- file
 check_result 'I B A' --topo-order -- file
 check_result 'H' --first-parent -- another-file
 
+check_result 'E C B A' --full-history E -- lost
+test_expect_success 'full history simplification without parent' '
+       printf "%s\n" E C B A >expect &&
+       git log --pretty="$FMT" --full-history E -- lost |
+       unnote >actual &&
+       sed -e "s/^.*   \([^ ]*\) .*/\1/" >check <actual &&
+       test_cmp expect check || {
+               cat actual
+               false
+       }
+'
+
+test_expect_success '--full-diff is not affected by --parents' '
+       git log -p --pretty="%H" --full-diff -- file >expected &&
+       git log -p --pretty="%H" --full-diff --parents -- file >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 39b4cb0ecdc8801b355b815a0566c6e2df5c5a39..dabebaee0b7b474387d4bc9b466b969624822d6d 100755 (executable)
@@ -13,6 +13,13 @@ test_description='--ancestry-path'
 #
 #  D..M -- M.t                 == M
 #  --ancestry-path D..M -- M.t == M
+#
+#  F...I                 == F G H I
+#  --ancestry-path F...I == F H I
+#
+#  G..M -- G.t                 == [nothing - was dropped in "-s ours" merge L]
+#  --ancestry-path G..M -- G.t == L
+#  --ancestry-path --simplify-merges G^..M -- G.t == G L
 
 . ./test-lib.sh
 
@@ -63,13 +70,52 @@ test_expect_success 'rev-list D..M -- M.t' '
        test_cmp expect actual
 '
 
-test_expect_success 'rev-list --ancestry-patch D..M -- M.t' '
+test_expect_success 'rev-list --ancestry-path D..M -- M.t' '
        echo M >expect &&
        git rev-list --ancestry-path --format=%s D..M -- M.t |
        sed -e "/^commit /d" >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'rev-list F...I' '
+       for c in F G H I; do echo $c; done >expect &&
+       git rev-list --format=%s F...I |
+       sed -e "/^commit /d" |
+       sort >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path F...I' '
+       for c in F H I; do echo $c; done >expect &&
+       git rev-list --ancestry-path --format=%s F...I |
+       sed -e "/^commit /d" |
+       sort >actual &&
+       test_cmp expect actual
+'
+
+# G.t is dropped in an "-s ours" merge
+test_expect_success 'rev-list G..M -- G.t' '
+       >expect &&
+       git rev-list --format=%s G..M -- G.t |
+       sed -e "/^commit /d" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path G..M -- G.t' '
+       echo L >expect &&
+       git rev-list --ancestry-path --format=%s G..M -- G.t |
+       sed -e "/^commit /d" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' '
+       for c in G L; do echo $c; done >expect &&
+       git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t |
+       sed -e "/^commit /d" |
+       sort >actual &&
+       test_cmp expect actual
+'
+
 #   b---bc
 #  / \ /
 # a   X
index 331b9b07d4eedb07377de605ebb87691427b7bb4..d15b313d4b5e65bc583b7c1f0e4efbb5ac56cb00 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Fredrik Kuivinen
 #
 
-# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
+# See http://marc.info/?l=git&m=111463358500362&w=2 for a
 # nice description of what this is about.
 
 
index 8bf53de3ef6bc7cc5de14b70e93ffe42b9b3f094..064f5cefeb4cf7e3bfa2c104384094492dc54f91 100755 (executable)
@@ -741,4 +741,42 @@ test_expect_success 'bisect: demonstrate identification of damage boundary' "
        git bisect reset
 "
 
+cat > expected.bisect-log <<EOF
+# bad: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>.
+# good: [7b7f204a749c3125d5224ed61ea2ae1187ad046f] Add <2: A new day for git> into <hello>.
+git bisect start '32a594a3fdac2d57cf6d02987e30eec68511498c' '7b7f204a749c3125d5224ed61ea2ae1187ad046f'
+# good: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>.
+git bisect good 3de952f2416b6084f557ec417709eac740c6818c
+# first bad commit: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>.
+EOF
+
+test_expect_success 'bisect log: successfull result' '
+       git bisect reset &&
+       git bisect start $HASH4 $HASH2 &&
+       git bisect good &&
+       git bisect log >bisect-log.txt &&
+       test_cmp expected.bisect-log bisect-log.txt &&
+       git bisect reset
+'
+
+cat > expected.bisect-skip-log <<EOF
+# bad: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>.
+# good: [7b7f204a749c3125d5224ed61ea2ae1187ad046f] Add <2: A new day for git> into <hello>.
+git bisect start '32a594a3fdac2d57cf6d02987e30eec68511498c' '7b7f204a749c3125d5224ed61ea2ae1187ad046f'
+# skip: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>.
+git bisect skip 3de952f2416b6084f557ec417709eac740c6818c
+# only skipped commits left to test
+# possible first bad commit: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>.
+# possible first bad commit: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>.
+EOF
+
+test_expect_success 'bisect log: only skip commits left' '
+       git bisect reset &&
+       git bisect start $HASH4 $HASH2 &&
+       test_must_fail git bisect skip &&
+       git bisect log >bisect-skip-log.txt &&
+       test_cmp expected.bisect-skip-log bisect-skip-log.txt &&
+       git bisect reset
+'
+
 test_done
index 2599ae50eb94051f66b4dc1de603aaf8cd1f79eb..9324ea44162130c0eea4dcc08d21866354e3c1da 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='merging when a directory was replaced with a symlink'
 . ./test-lib.sh
 
-test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' '
+test_expect_success 'create a commit where dir a/b changed to symlink' '
        mkdir -p a/b/c a/b-2/c &&
        > a/b/c/d &&
        > a/b-2/c/d &&
@@ -12,12 +12,12 @@ test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink'
        git commit -m base &&
        git tag start &&
        rm -rf a/b &&
-       ln -s b-2 a/b &&
        git add -A &&
+       test_ln_s_add b-2 a/b &&
        git commit -m "dir to symlink"
 '
 
-test_expect_success SYMLINKS 'checkout does not clobber untracked symlink' '
+test_expect_success 'checkout does not clobber untracked symlink' '
        git checkout HEAD^0 &&
        git reset --hard master &&
        git rm --cached a/b &&
@@ -25,7 +25,7 @@ test_expect_success SYMLINKS 'checkout does not clobber untracked symlink' '
        test_must_fail git checkout start^0
 '
 
-test_expect_success SYMLINKS 'a/b-2/c/d is kept when clobbering symlink b' '
+test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' '
        git checkout HEAD^0 &&
        git reset --hard master &&
        git rm --cached a/b &&
@@ -34,14 +34,14 @@ test_expect_success SYMLINKS 'a/b-2/c/d is kept when clobbering symlink b' '
        test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'checkout should not have deleted a/b-2/c/d' '
+test_expect_success 'checkout should not have deleted a/b-2/c/d' '
        git checkout HEAD^0 &&
        git reset --hard master &&
         git checkout start^0 &&
         test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'setup for merge test' '
+test_expect_success 'setup for merge test' '
        git reset --hard &&
        test -f a/b-2/c/d &&
        echo x > a/x &&
@@ -50,39 +50,51 @@ test_expect_success SYMLINKS 'setup for merge test' '
        git tag baseline
 '
 
-test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s resolve master &&
-       test -h a/b &&
        test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+       test -h a/b
+'
+
+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s recursive master &&
-       test -h a/b &&
        test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+       test -h a/b
+'
+
+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
        git reset --hard &&
        git checkout master^0 &&
        git merge -s resolve baseline^0 &&
-       test -h a/b &&
        test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+       test -h a/b
+'
+
+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
        git reset --hard &&
        git checkout master^0 &&
        git merge -s recursive baseline^0 &&
-       test -h a/b &&
        test -f a/b-2/c/d
 '
 
-test_expect_failure SYMLINKS 'do not lose untracked in merge (resolve)' '
+test_expect_success SYMLINKS 'a/b was resolved as symlink' '
+       test -h a/b
+'
+
+test_expect_failure 'do not lose untracked in merge (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        >a/b/c/e &&
@@ -91,7 +103,7 @@ test_expect_failure SYMLINKS 'do not lose untracked in merge (resolve)' '
        test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'do not lose untracked in merge (recursive)' '
+test_expect_success 'do not lose untracked in merge (recursive)' '
        git reset --hard &&
        git checkout baseline^0 &&
        >a/b/c/e &&
@@ -100,52 +112,61 @@ test_expect_success SYMLINKS 'do not lose untracked in merge (recursive)' '
        test -f a/b-2/c/d
 '
 
-test_expect_success SYMLINKS 'do not lose modifications in merge (resolve)' '
+test_expect_success 'do not lose modifications in merge (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        echo more content >>a/b/c/d &&
        test_must_fail git merge -s resolve master
 '
 
-test_expect_success SYMLINKS 'do not lose modifications in merge (recursive)' '
+test_expect_success 'do not lose modifications in merge (recursive)' '
        git reset --hard &&
        git checkout baseline^0 &&
        echo more content >>a/b/c/d &&
        test_must_fail git merge -s recursive master
 '
 
-test_expect_success SYMLINKS 'setup a merge where dir a/b-2 changed to symlink' '
+test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
        git reset --hard &&
        git checkout start^0 &&
        rm -rf a/b-2 &&
-       ln -s b a/b-2 &&
        git add -A &&
+       test_ln_s_add b a/b-2 &&
        git commit -m "dir a/b-2 to symlink" &&
        git tag test2
 '
 
-test_expect_success SYMLINKS 'merge should not have D/F conflicts (resolve)' '
+test_expect_success 'merge should not have D/F conflicts (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s resolve test2 &&
-       test -h a/b-2 &&
        test -f a/b/c/d
 '
 
-test_expect_success SYMLINKS 'merge should not have D/F conflicts (recursive)' '
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+       test -h a/b-2
+'
+
+test_expect_success 'merge should not have D/F conflicts (recursive)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s recursive test2 &&
-       test -h a/b-2 &&
        test -f a/b/c/d
 '
 
-test_expect_success SYMLINKS 'merge should not have F/D conflicts (recursive)' '
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+       test -h a/b-2
+'
+
+test_expect_success 'merge should not have F/D conflicts (recursive)' '
        git reset --hard &&
        git checkout -b foo test2 &&
        git merge -s recursive baseline^0 &&
-       test -h a/b-2 &&
        test -f a/b/c/d
 '
 
+test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' '
+       test -h a/b-2
+'
+
 test_done
diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh
new file mode 100755 (executable)
index 0000000..88b84df
--- /dev/null
@@ -0,0 +1,196 @@
+#!/bin/sh
+#
+#        ,---E--.   *H----------.             * marks !TREESAME parent paths
+#       /        \ /             \*
+# *A--*B---D--*F-*G---------K-*L-*M
+#   \     /*       \       /
+#    `-C-'          `-*I-*J
+#
+# A creates "file", B and F change it.
+# Odd merge G takes the old version from B.
+# I changes it, but J reverts it, so K is TREESAME to both parents.
+# H and L both change "file", and M merges those changes.
+
+test_description='TREESAME and limiting'
+
+. ./test-lib.sh
+
+note () {
+       git tag "$1"
+}
+
+unnote () {
+       git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\))\([       ]\)|\1\2|g"
+}
+
+test_expect_success setup '
+       test_commit "Initial file" file "Hi there" A &&
+       git branch other-branch &&
+
+       test_commit "file=Hello" file "Hello" B &&
+       git branch third-branch &&
+
+       git checkout other-branch &&
+       test_commit "Added other" other "Hello" C &&
+
+       git checkout master &&
+       test_merge D other-branch &&
+
+       git checkout third-branch &&
+       test_commit "Third file" third "Nothing" E &&
+
+       git checkout master &&
+       test_commit "file=Blah" file "Blah" F &&
+
+       test_tick && git merge --no-commit third-branch &&
+       git checkout third-branch file &&
+       git commit &&
+       note G &&
+       git branch fiddler-branch &&
+
+       git checkout -b part2-branch &&
+       test_commit "file=Part 2" file "Part 2" H &&
+
+       git checkout fiddler-branch &&
+       test_commit "Bad commit" file "Silly" I &&
+
+       test_tick && git revert I && note J &&
+
+       git checkout master &&
+       test_tick && git merge --no-ff fiddler-branch &&
+       note K
+
+       test_commit "file=Part 1" file "Part 1" L &&
+
+       test_tick && test_must_fail git merge part2-branch &&
+       test_commit M file "Parts 1+2"
+'
+
+check_outcome () {
+       outcome=$1
+       shift
+
+       case "$1" in
+       *"("*)
+               FMT="%P %H | %s"
+               munge_actual="
+                       s/^\([^ ]*\)    \([^ ]*\) .*/(\1)\2/
+                       s/ //g
+                       s/()//
+               "
+               ;;
+       *)
+               FMT="%H | %s"
+               munge_actual="s/^\([^ ]*\) .*/\1/"
+               ;;
+       esac &&
+       printf "%s\n" $1 >expect &&
+       shift
+
+       param="$*" &&
+       test_expect_$outcome "log $param" '
+               git log --format="$FMT" $param |
+               unnote >actual &&
+               sed -e "$munge_actual" <actual >check &&
+               test_cmp expect check || {
+                       cat actual
+                       false
+               }
+       '
+}
+
+check_result () {
+       check_outcome success "$@"
+}
+
+# Odd merge G drops a change in F. Important that G is listed in all
+# except the most basic list. Achieving this means normal merge D will also be
+# shown in normal full-history, as we can't distinguish unless we do a
+# simplification pass. After simplification, D is dropped but G remains.
+# Also, merge simplification of G should not drop the parent B that the default
+# simple history follows.
+check_result 'M L K J I H G F E D C B A'
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FE)G (D)F (B)E (BC)D (A)C (A)B A'
+check_result 'M H L K J I G E F D C B A' --topo-order
+check_result 'M L H B A' -- file
+check_result '(LH)M (B)L (B)H (A)B A' --parents -- file
+check_result 'M L J I H G F D B A' --full-history -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FB)G (D)F (BA)D (A)B A' --full-history --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FB)G (B)F (A)B A' --simplify-merges -- file
+check_result 'M L K G F D B A' --first-parent
+check_result 'M L G F B A' --first-parent -- file
+
+# Check that odd merge G remains shown when F is the bottom.
+check_result 'M L K J I H G E' F..M
+check_result 'M H L K J I G E' F..M --topo-order
+check_result 'M L H' F..M -- file
+check_result '(LH)M (B)L (B)H' --parents F..M -- file
+check_result 'M L J I H G' F..M --full-history -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FB)G' F..M --full-history --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FB)G' F..M --simplify-merges -- file
+check_result 'M L K J I H G' F..M --ancestry-path
+check_result 'M L J I H G' F..M --ancestry-path -- file
+check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FE)G' F..M --ancestry-path --parents -- file
+check_result '(LH)M (G)H (J)L (I)J (G)I (FE)G' F..M --ancestry-path --simplify-merges -- file
+check_result 'M L K G' F..M --first-parent
+check_result 'M L G' F..M --first-parent -- file
+
+# Note that G is pruned when E is the bottom, even if it's the same commit list
+# If we want history since E, then we're quite happy to ignore G that took E.
+check_result 'M L K J I H G' E..M --ancestry-path
+check_result 'M L J I H' E..M --ancestry-path -- file
+check_result '(LH)M (K)L (EJ)K (I)J (E)I (E)H' E..M --ancestry-path --parents -- file
+check_result '(LH)M (E)H (J)L (I)J (E)I' E..M --ancestry-path --simplify-merges -- file
+
+# Should still be able to ignore I-J branch in simple log, despite limiting
+# to G.
+check_result 'M L K J I H' G..M
+check_result 'M H L K J I' G..M --topo-order
+check_result 'M L H' G..M -- file
+check_result '(LH)M (G)L (G)H' G..M --parents -- file
+check_result 'M L J I H' G..M --full-history -- file
+check_result 'M L K J I H' G..M --full-history --parents -- file
+check_result 'M H L J I' G..M --simplify-merges -- file
+check_result 'M L K J I H' G..M --ancestry-path
+check_result 'M L J I H' G..M --ancestry-path -- file
+check_result 'M L K J I H' G..M --ancestry-path --parents -- file
+check_result 'M H L J I' G..M --ancestry-path --simplify-merges -- file
+
+# B..F should be able to simplify the merge D from irrelevant side branch C.
+# Default log should also be free to follow B-D, and ignore C.
+# But --full-history shouldn't drop D on its own - without simplification,
+# we can't decide if the merge from INTERESTING commit C was sensible.
+check_result 'F D C' B..F
+check_result 'F' B..F -- file
+check_result '(B)F' B..F --parents -- file
+check_result 'F D' B..F --full-history -- file
+check_result '(D)F (BA)D' B..F --full-history --parents -- file
+check_result '(B)F' B..F --simplify-merges -- file
+check_result 'F D' B..F --ancestry-path
+check_result 'F' B..F --ancestry-path -- file
+check_result 'F' B..F --ancestry-path --parents -- file
+check_result 'F' B..F --ancestry-path --simplify-merges -- file
+check_result 'F D' B..F --first-parent
+check_result 'F' B..F --first-parent -- file
+
+# E...F should be equivalent to E F ^B, and be able to drop D as above.
+check_result 'F' E F ^B -- file # includes D
+check_result 'F' E...F -- file # includes D
+
+# Any sort of full history of C..F should show D, as it's the connection to C,
+# and it differs from it.
+check_result 'F D B' C..F
+check_result 'F B' C..F -- file
+check_result '(B)F (A)B' C..F --parents -- file
+check_result 'F D B' C..F --full-history -- file
+check_result '(D)F (BC)D (A)B' C..F --full-history --parents -- file
+check_result '(D)F (BC)D (A)B' C..F --simplify-merges -- file
+check_result 'F D' C..F --ancestry-path
+check_result 'F D' C..F --ancestry-path -- file
+check_result 'F D' C..F --ancestry-path --parents -- file
+check_result 'F D' C..F --ancestry-path --simplify-merges -- file
+check_result 'F D B' C..F --first-parent
+check_result 'F B' C..F --first-parent -- file
+
+
+test_done
index f67aa6ff6a3c2af2d0c1999e8203da3a922b0555..c0e5b2a6275df2d92172327b2d629cc73720423e 100755 (executable)
@@ -110,6 +110,9 @@ check_describe tags/e --all HEAD^^^
 check_describe B-0-* --long HEAD^^2^
 check_describe A-3-* --long HEAD^^2
 
+check_describe c-7-* --tags
+check_describe e-3-* --first-parent --tags
+
 : >err.expect
 check_describe A --all A^0
 test_expect_success 'no warning was displayed for A' '
@@ -171,4 +174,28 @@ check_describe "test2-lightweight-*" --tags --match="test2-*"
 
 check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
 
+test_expect_success 'name-rev with exact tags' '
+       echo A >expect &&
+       tag_object=$(git rev-parse refs/tags/A) &&
+       git name-rev --tags --name-only $tag_object >actual &&
+       test_cmp expect actual &&
+
+       echo "A^0" >expect &&
+       tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+       git name-rev --tags --name-only $tagged_commit >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'describe --contains with the exact tags' '
+       echo "A^0" >expect &&
+       tag_object=$(git rev-parse refs/tags/A) &&
+       git describe --contains $tag_object >actual &&
+       test_cmp expect actual &&
+
+       echo "A^0" >expect &&
+       tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+       git describe --contains $tagged_commit >actual &&
+       test_cmp expect actual
+'
+
 test_done
index a845b154e4db50b52eb67eaefefcc42403c52c0e..101816e718d6149a0e8ee500026455f04a8384ed 100755 (executable)
@@ -218,13 +218,13 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' '
 
 rm -f dirty dirty2
 
-test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' '
+test_expect_success 'git mv should overwrite symlink to a file' '
 
        rm -fr .git &&
        git init &&
        echo 1 >moved &&
-       ln -s moved symlink &&
-       git add moved symlink &&
+       test_ln_s_add moved symlink &&
+       git add moved &&
        test_must_fail git mv moved symlink &&
        git mv -f moved symlink &&
        ! test -e moved &&
@@ -237,22 +237,26 @@ test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' '
 
 rm -f moved symlink
 
-test_expect_success SYMLINKS 'git mv should overwrite file with a symlink' '
+test_expect_success 'git mv should overwrite file with a symlink' '
 
        rm -fr .git &&
        git init &&
        echo 1 >moved &&
-       ln -s moved symlink &&
-       git add moved symlink &&
+       test_ln_s_add moved symlink &&
+       git add moved &&
        test_must_fail git mv symlink moved &&
        git mv -f symlink moved &&
        ! test -e symlink &&
-       test -h moved &&
        git update-index --refresh &&
        git diff-files --quiet
 
 '
 
+test_expect_success SYMLINKS 'check moved symlink' '
+
+       test -h moved
+'
+
 rm -f moved symlink
 
 test_done
index 8f3b54d826f5a9c8e7b08df7f6a58398b5a62480..88d60c1ce2933141c0800a76fdebca9037803258 100755 (executable)
@@ -91,12 +91,12 @@ test_expect_success 'update-index --remove' '
        test_cmp expected 1
 '
 
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
        setup_absent &&
        test -z "$(git ls-files -d)"
 '
 
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
        setup_dirty &&
        test -z "$(git ls-files -d)"
 '
index 0da1214bcca439c126a0e19c664ed9230a1001fe..460789b4d85241eb51c0c1a5a1f4be7d4a0d7154 100755 (executable)
@@ -32,6 +32,25 @@ test_expect_success 'status untracked directory with --ignored -u' '
        git status --porcelain --ignored -u >actual &&
        test_cmp expected actual
 '
+cat >expected <<\EOF
+?? untracked/uncommitted
+!! untracked/ignored
+EOF
+
+test_expect_success 'status prefixed untracked directory with --ignored' '
+       git status --porcelain --ignored untracked/ >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? untracked/uncommitted
+!! untracked/ignored
+EOF
+
+test_expect_success 'status prefixed untracked sub-directory with --ignored -u' '
+       git status --porcelain --ignored -u untracked/ >actual &&
+       test_cmp expected actual
+'
 
 cat >expected <<\EOF
 ?? .gitignore
@@ -64,13 +83,35 @@ cat >expected <<\EOF
 ?? .gitignore
 ?? actual
 ?? expected
-!! untracked-ignored/
 EOF
 
-test_expect_success 'status untracked directory with ignored files with --ignore' '
+test_expect_success 'status empty untracked directory with --ignore' '
        rm -rf ignored &&
        mkdir untracked-ignored &&
        mkdir untracked-ignored/test &&
+       git status --porcelain --ignored >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+EOF
+
+test_expect_success 'status empty untracked directory with --ignore -u' '
+       git status --porcelain --ignored -u >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! untracked-ignored/
+EOF
+
+test_expect_success 'status untracked directory with ignored files with --ignore' '
        : >untracked-ignored/ignored &&
        : >untracked-ignored/test/ignored &&
        git status --porcelain --ignored >actual &&
@@ -122,10 +163,34 @@ cat >expected <<\EOF
 ?? .gitignore
 ?? actual
 ?? expected
-!! tracked/
+EOF
+
+test_expect_success 'status ignored tracked directory and ignored file with --ignore' '
+       echo "committed" >>.gitignore &&
+       git status --porcelain --ignored >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+EOF
+
+test_expect_success 'status ignored tracked directory and ignored file with --ignore -u' '
+       git status --porcelain --ignored -u >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/uncommitted
 EOF
 
 test_expect_success 'status ignored tracked directory and uncommitted file with --ignore' '
+       echo "tracked" >.gitignore &&
        : >tracked/uncommitted &&
        git status --porcelain --ignored >actual &&
        test_cmp expected actual
@@ -143,4 +208,58 @@ test_expect_success 'status ignored tracked directory and uncommitted file with
        test_cmp expected actual
 '
 
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/ignored/
+EOF
+
+test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore' '
+       rm -rf tracked/uncommitted &&
+       mkdir tracked/ignored &&
+       : >tracked/ignored/uncommitted &&
+       git status --porcelain --ignored >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/ignored/uncommitted
+EOF
+
+test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore -u' '
+       git status --porcelain --ignored -u >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/ignored/uncommitted
+EOF
+
+test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore' '
+       : >tracked/ignored/committed &&
+       git add -f tracked/ignored/committed &&
+       git commit -m. &&
+       git status --porcelain --ignored >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+?? .gitignore
+?? actual
+?? expected
+!! tracked/ignored/uncommitted
+EOF
+
+test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore -u' '
+       git status --porcelain --ignored -u >actual &&
+       test_cmp expected actual
+'
+
 test_done
index df82ec9ddae30acc5cad23886cbf6ff2de287e7f..8d4b50d1b5816d69ab165119773a7ffc0c360c0a 100755 (executable)
@@ -9,6 +9,19 @@ Documented tests for git reset'
 
 . ./test-lib.sh
 
+commit_msg () {
+       # String "modify 2nd file (changed)" partly in German
+       # (translated with Google Translate),
+       # encoded in UTF-8, used as a commit log message below.
+       msg="modify 2nd file (ge\303\244ndert)\n"
+       if test -n "$1"
+       then
+               printf "$msg" | iconv -f utf-8 -t "$1"
+       else
+               printf "$msg"
+       fi
+}
+
 test_expect_success 'creating initial files and commits' '
        test_tick &&
        echo "1st file" >first &&
@@ -28,7 +41,7 @@ test_expect_success 'creating initial files and commits' '
 
        echo "1st line 2nd file" >secondfile &&
        echo "2nd line 2nd file" >>secondfile &&
-       git commit -a -m "modify 2nd file" &&
+       git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" &&
        head5=$(git rev-parse --verify HEAD)
 '
 # git log --pretty=oneline # to see those SHA1 involved
@@ -44,6 +57,20 @@ check_changes () {
        done | test_cmp .cat_expect -
 }
 
+test_expect_success 'reset --hard message' '
+       hex=$(git log -1 --format="%h") &&
+       git reset --hard > .actual &&
+       echo HEAD is now at $hex $(commit_msg) > .expected &&
+       test_cmp .expected .actual
+'
+
+test_expect_success 'reset --hard message (iso8859-1 logoutputencoding)' '
+       hex=$(git log -1 --format="%h") &&
+       git -c "i18n.logOutputEncoding=iso8859-1" reset --hard > .actual &&
+       echo HEAD is now at $hex $(commit_msg iso8859-1) > .expected &&
+       test_cmp .expected .actual
+'
+
 >.diff_expect
 >.cached_expect
 cat >.cat_expect <<EOF
@@ -192,7 +219,8 @@ test_expect_success \
        'changing files and redo the last commit should succeed' '
        echo "3rd line 2nd file" >>secondfile &&
        git commit -a -C ORIG_HEAD &&
-       check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d &&
+       head4=$(git rev-parse --verify HEAD) &&
+       check_changes $head4 &&
        test "$(git rev-parse ORIG_HEAD)" = \
                        $head5
 '
@@ -211,7 +239,7 @@ test_expect_success \
        git reset --hard HEAD~2 &&
        check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
        test "$(git rev-parse ORIG_HEAD)" = \
-                       3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d
+                       $head4
 '
 
 >.diff_expect
@@ -303,7 +331,7 @@ test_expect_success 'redoing the last two commits should succeed' '
 
        echo "1st line 2nd file" >secondfile &&
        echo "2nd line 2nd file" >>secondfile &&
-       git commit -a -m "modify 2nd file" &&
+       git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" &&
        check_changes $head5
 '
 
@@ -326,10 +354,11 @@ test_expect_success '--hard reset to HEAD should clear a failed merge' '
        git checkout branch2 &&
        echo "3rd line in branch2" >>secondfile &&
        git commit -a -m "change in branch2" &&
+       head3=$(git rev-parse --verify HEAD) &&
 
        test_must_fail git pull . branch1 &&
        git reset --hard &&
-       check_changes 77abb337073fb4369a7ad69ff6f5ec0e4d6b54bb
+       check_changes $head3
 '
 
 >.diff_expect
@@ -457,7 +486,7 @@ test_expect_success 'disambiguation (1)' '
        test_must_fail git diff --quiet -- secondfile &&
        test -z "$(git diff --cached --name-only)" &&
        test -f secondfile &&
-       test ! -s secondfile
+       test_must_be_empty secondfile
 
 '
 
index ccfb54de7ad9473221390d019b109bcb010a2c76..710be90489b2fd5d6ca1a98201c1ec635efcae9f 100755 (executable)
@@ -298,6 +298,23 @@ test_expect_success 'git clean -d -x' '
 
 '
 
+test_expect_success 'git clean -d -x with ignored tracked directory' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       git clean -d -x -e src &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test -f src/part3.c &&
+       test ! -d docs &&
+       test ! -f obj.o &&
+       test ! -d build
+
+'
+
 test_expect_success 'git clean -X' '
 
        mkdir -p build docs &&
@@ -332,6 +349,23 @@ test_expect_success 'git clean -d -X' '
 
 '
 
+test_expect_success 'git clean -d -X with ignored tracked directory' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       git clean -d -X -e src &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test ! -f src/part3.c &&
+       test -f docs/manual.txt &&
+       test ! -f obj.o &&
+       test ! -d build
+
+'
+
 test_expect_success 'clean.requireForce defaults to true' '
 
        git config --unset clean.requireForce &&
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
new file mode 100755 (executable)
index 0000000..3ae394e
--- /dev/null
@@ -0,0 +1,475 @@
+#!/bin/sh
+
+test_description='git clean -i basic tests'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+       mkdir -p src &&
+       touch src/part1.c Makefile &&
+       echo build >.gitignore &&
+       echo \*.o >>.gitignore &&
+       git add . &&
+       git commit -m setup &&
+       touch src/part2.c README &&
+       git add .
+
+'
+
+test_expect_success 'git clean -i (c: clean hotkey)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       echo c | git clean -i &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test ! -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (cl: clean prefix)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       echo cl | git clean -i &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test ! -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (quit)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       echo quit | git clean -i &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (Ctrl+D)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       echo "\04" | git clean -i &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter all)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo f; echo "*"; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo f; echo "part3.* *.out"; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test ! -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test ! -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns 2)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo f; echo "* !*.out"; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - all)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo s; echo "*"; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test ! -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test ! -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - none)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo s; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo s; echo 3; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 2)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo s; echo 2 3; echo 5; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test ! -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 3)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo s; echo 3,4 5; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - filenames)' '
+
+       mkdir -p build docs &&
+       touch a.out foo.txt bar.txt baz.txt &&
+       (echo s; echo a.out fo ba bar; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test ! -f a.out &&
+       test ! -f foo.txt &&
+       test ! -f bar.txt &&
+       test -f baz.txt &&
+       rm baz.txt
+
+'
+
+test_expect_success 'git clean -id (select - range)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo s; echo 1,3-4; echo 2; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test ! -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test ! -f docs/manual.txt &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - range 2)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo s; echo 4- 1; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test ! -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (inverse select)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test ! -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask - Ctrl+D)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (echo a; echo Y; echo no; echo yes; echo "\04") | \
+       git clean -id &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (filter)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (cd build/ && \
+        (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \
+        git clean -id ..) &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (select by name)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (cd build/ && \
+        (echo s; echo "../docs/"; echo "../src/part3.c"; \
+         echo "../src/part4.c";  echo; echo c) | \
+        git clean -id ..) &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test -f a.out &&
+       test ! -f docs/manual.txt &&
+       test ! -f src/part3.c &&
+       test -f src/part3.h &&
+       test ! -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (ask)' '
+
+       mkdir -p build docs &&
+       touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+       docs/manual.txt obj.o build/lib.so &&
+       (cd build/ && \
+        (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+        git clean -id ..) &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test ! -f docs/manual.txt &&
+       test -f src/part3.c &&
+       test ! -f src/part3.h &&
+       test -f src/part4.c &&
+       test -f src/part4.h &&
+       test -f obj.o &&
+       test -f build/lib.so
+
+'
+
+test_done
index ff265353a375d02bb578fed29ea00f07dc08a6fc..a39d07446508598dded68561a946e8385a7830c3 100755 (executable)
@@ -18,6 +18,16 @@ test_expect_success 'setup - initial commit' '
        git branch initial
 '
 
+test_expect_success 'configuration parsing' '
+       test_when_finished "rm -f .gitmodules" &&
+       cat >.gitmodules <<-\EOF &&
+       [submodule "s"]
+               path
+               ignore
+       EOF
+       test_must_fail git status
+'
+
 test_expect_success 'setup - repository in init subdirectory' '
        mkdir init &&
        (
@@ -78,7 +88,7 @@ test_expect_success 'submodule add' '
        (
                cd addtest &&
                git submodule add -q "$submodurl" submod >actual &&
-               test ! -s actual &&
+               test_must_be_empty actual &&
                echo "gitdir: ../.git/modules/submod" >expect &&
                test_cmp expect submod/.git &&
                (
@@ -212,6 +222,32 @@ test_expect_success 'submodule add with ./, /.. and // in path' '
        test_cmp empty untracked
 '
 
+test_expect_success 'submodule add in subdirectory' '
+       echo "refs/heads/master" >expect &&
+       >empty &&
+
+       mkdir addtest/sub &&
+       (
+               cd addtest/sub &&
+               git submodule add "$submodurl" ../realsubmod3 &&
+               git submodule init
+       ) &&
+
+       rm -f heads head untracked &&
+       inspect addtest/realsubmod3 ../.. &&
+       test_cmp expect heads &&
+       test_cmp expect head &&
+       test_cmp empty untracked
+'
+
+test_expect_success 'submodule add in subdirectory with relative path should fail' '
+       (
+               cd addtest/sub &&
+               test_must_fail git submodule add ../../ submod3 2>../../output.err
+       ) &&
+       test_i18ngrep toplevel output.err
+'
+
 test_expect_success 'setup - add an example entry to .gitmodules' '
        GIT_CONFIG=.gitmodules \
        git config submodule.example.url git://example.com/init.git
@@ -308,7 +344,7 @@ test_expect_success 'update should work when path is an empty dir' '
 
        mkdir init &&
        git submodule update -q >update.out &&
-       test ! -s update.out &&
+       test_must_be_empty update.out &&
 
        inspect init &&
        test_cmp expect head-sha1
@@ -319,6 +355,26 @@ test_expect_success 'status should be "up-to-date" after update' '
        grep "^ $rev1" list
 '
 
+test_expect_success 'status "up-to-date" from subdirectory' '
+       mkdir -p sub &&
+       (
+               cd sub &&
+               git submodule status >../list
+       ) &&
+       grep "^ $rev1" list &&
+       grep "\\.\\./init" list
+'
+
+test_expect_success 'status "up-to-date" from subdirectory with path' '
+       mkdir -p sub &&
+       (
+               cd sub &&
+               git submodule status ../init >../list
+       ) &&
+       grep "^ $rev1" list &&
+       grep "\\.\\./init" list
+'
+
 test_expect_success 'status should be "modified" after submodule commit' '
        (
                cd init &&
@@ -399,6 +455,25 @@ test_expect_success 'update --init' '
        git rev-parse --resolve-git-dir init/.git
 '
 
+test_expect_success 'update --init from subdirectory' '
+       mv init init2 &&
+       git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+       git config --remove-section submodule.example &&
+       test_must_fail git config submodule.example.url &&
+
+       mkdir -p sub &&
+       (
+               cd sub &&
+               git submodule update ../init >update.out &&
+               cat update.out &&
+               test_i18ngrep "not initialized" update.out &&
+               test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
+
+               git submodule update --init ../init
+       ) &&
+       git rev-parse --resolve-git-dir init/.git
+'
+
 test_expect_success 'do not add files from a submodule' '
 
        git reset --hard &&
@@ -696,7 +771,7 @@ test_expect_success 'submodule add --name allows to replace a submodule with ano
                rm -rf repo &&
                git rm repo &&
                git submodule add -q --name repo_new "$submodurl/bare.git" repo >actual &&
-               test ! -s actual &&
+               test_must_be_empty actual &&
                echo "gitdir: ../.git/modules/submod" >expect &&
                test_cmp expect submod/.git &&
                (
@@ -772,6 +847,21 @@ test_expect_success 'submodule deinit should remove the whole submodule section
        rmdir init
 '
 
+test_expect_success 'submodule deinit from subdirectory' '
+       git submodule update --init &&
+       git config submodule.example.foo bar &&
+       mkdir -p sub &&
+       (
+               cd sub &&
+               git submodule deinit ../init >../output
+       ) &&
+       grep "\\.\\./init" output &&
+       test -z "$(git config --get-regexp "submodule\.example\.")" &&
+       test -n "$(git config --get-regexp "submodule\.example2\.")" &&
+       test -f example2/.git &&
+       rmdir init
+'
+
 test_expect_success 'submodule deinit . deinits all initialized submodules' '
        git submodule update --init &&
        git config submodule.example.foo bar &&
@@ -868,4 +958,35 @@ test_expect_success 'submodule deinit fails when submodule has a .git directory
        test -n "$(git config --get-regexp "submodule\.example\.")"
 '
 
+test_expect_success 'submodule with UTF-8 name' '
+       svname=$(printf "\303\245 \303\244\303\266") &&
+       mkdir "$svname" &&
+       (
+               cd "$svname" &&
+               git init &&
+               >sub &&
+               git add sub &&
+               git commit -m "init sub"
+       ) &&
+       test_config core.precomposeunicode true &&
+       git submodule add ./"$svname" &&
+       git submodule >&2 &&
+       test -n "$(git submodule | grep "$svname")"
+'
+
+test_expect_success 'submodule add clone shallow submodule' '
+       mkdir super &&
+       pwd=$(pwd)
+       (
+               cd super &&
+               git init &&
+               git submodule add --depth=1 file://"$pwd"/example2 submodule &&
+               (
+                       cd submodule &&
+                       test 1 = $(git log --oneline | wc -l)
+               )
+       )
+'
+
+
 test_done
index 30b429e7dcbcd56af21ded67128aa18901133275..ac2434c0dbdaf837d8dd7f896e680fbb7b6ab954 100755 (executable)
@@ -45,6 +45,42 @@ EOF
        test_cmp expected actual
 "
 
+test_expect_success 'added submodule (subdirectory)' "
+       mkdir sub &&
+       (
+               cd sub &&
+               git submodule summary >../actual
+       ) &&
+       cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+       test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory only)' "
+       (
+               cd sub &&
+               git submodule summary . >../actual
+       ) &&
+       >expected &&
+       test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory with explicit path)' "
+       (
+               cd sub &&
+               git submodule summary ../sm1 >../actual
+       ) &&
+       cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+  > Add foo2
+
+EOF
+       test_cmp expected actual
+"
+
 commit_file sm1 &&
 head2=$(add_file sm1 foo3)
 
@@ -76,8 +112,8 @@ head3=$(
 )
 
 test_expect_success 'modified submodule(backward)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head2...$head3 (2):
   < Add foo3
   < Add foo2
@@ -89,8 +125,8 @@ EOF
 head4=$(add_file sm1 foo4 foo5) &&
 head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
 test_expect_success 'modified submodule(backward and forward)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
@@ -102,15 +138,15 @@ EOF
 "
 
 test_expect_success '--summary-limit' "
-    git submodule summary -n 3 >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary -n 3 >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
   < Add foo3
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -122,8 +158,8 @@ rm -f sm1 &&
 mv sm1-bak sm1
 
 test_expect_success 'typechanged submodule(submodule->blob), --cached' "
-    git submodule summary --cached >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary --cached >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob) (3):
   < Add foo5
 
@@ -132,59 +168,59 @@ EOF
 "
 
 test_expect_success 'typechanged submodule(submodule->blob), --files' "
-    git submodule summary --files >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary --files >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head4(submodule) (3):
   > Add foo5
 
 EOF
-    test_i18ncmp actual expected
+       test_i18ncmp actual expected
 "
 
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob):
 
 EOF
-    test_i18ncmp actual expected
+       test_i18ncmp actual expected
 "
 
 rm -f sm1 &&
 test_create_repo sm1 &&
 head6=$(add_file sm1 foo6 foo7)
 test_expect_success 'nonexistent commit' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head4...$head6:
   Warn: sm1 doesn't contain commit $head4_full
 
 EOF
-    test_i18ncmp actual expected
+       test_i18ncmp actual expected
 "
 
 commit_file
 test_expect_success 'typechanged submodule(blob->submodule)' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head6(submodule) (2):
   > Add foo7
 
 EOF
-    test_i18ncmp expected actual
+       test_i18ncmp expected actual
 "
 
 commit_file sm1 &&
 rm -rf sm1
 test_expect_success 'deleted submodule' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 test_create_repo sm2 &&
@@ -192,43 +228,43 @@ head7=$(add_file sm2 foo8 foo9) &&
 git add sm2
 
 test_expect_success 'multiple submodules' "
-    git submodule summary >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 test_expect_success 'path filter' "
-    git submodule summary sm2 >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary sm2 >actual &&
+       cat >expected <<-EOF &&
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 commit_file sm2
 test_expect_success 'given commit' "
-    git submodule summary HEAD^ >actual &&
-    cat >expected <<-EOF &&
+       git submodule summary HEAD^ >actual &&
+       cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
-    test_cmp expected actual
+       test_cmp expected actual
 "
 
 test_expect_success '--for-status' "
-    git submodule summary --for-status HEAD^ >actual &&
-    test_i18ncmp actual - <<EOF
+       git submodule summary --for-status HEAD^ >actual &&
+       test_i18ncmp actual - <<EOF
 # Submodule changes to be committed:
 #
 # * sm1 $head6...0000000:
@@ -240,14 +276,14 @@ EOF
 "
 
 test_expect_success 'fail when using --files together with --cached' "
-    test_must_fail git submodule summary --files --cached
+       test_must_fail git submodule summary --files --cached
 "
 
 test_expect_success 'should not fail in an empty repo' "
-    git init xyzzy &&
-    cd xyzzy &&
-    git submodule summary >output 2>&1 &&
-    test_cmp output /dev/null
+       git init xyzzy &&
+       cd xyzzy &&
+       git submodule summary >output 2>&1 &&
+       test_cmp output /dev/null
 "
 
 test_done
index 94e26c47eae37062ca0d7d24d30104fb46d06743..79bc135bf69ea5813f0a372db6bbefa0f9a92753 100755 (executable)
@@ -11,216 +11,338 @@ These tests exercise the "git submodule sync" subcommand.
 . ./test-lib.sh
 
 test_expect_success setup '
-       echo file > file &&
+       echo file >file &&
        git add file &&
        test_tick &&
        git commit -m upstream &&
        git clone . super &&
        git clone super submodule &&
-       (cd submodule &&
-        git submodule add ../submodule sub-submodule &&
-        test_tick &&
-        git commit -m "sub-submodule"
+       (
+               cd submodule &&
+               git submodule add ../submodule sub-submodule &&
+               test_tick &&
+               git commit -m "sub-submodule"
        ) &&
-       (cd super &&
-        git submodule add ../submodule submodule &&
-        test_tick &&
-        git commit -m "submodule"
+       (
+               cd super &&
+               git submodule add ../submodule submodule &&
+               test_tick &&
+               git commit -m "submodule"
        ) &&
        git clone super super-clone &&
-       (cd super-clone && git submodule update --init --recursive) &&
+       (
+               cd super-clone &&
+               git submodule update --init --recursive
+       ) &&
        git clone super empty-clone &&
-       (cd empty-clone && git submodule init) &&
+       (
+               cd empty-clone &&
+               git submodule init
+       ) &&
        git clone super top-only-clone &&
        git clone super relative-clone &&
-       (cd relative-clone && git submodule update --init --recursive) &&
+       (
+               cd relative-clone &&
+               git submodule update --init --recursive
+       ) &&
        git clone super recursive-clone &&
-       (cd recursive-clone && git submodule update --init --recursive)
+       (
+               cd recursive-clone &&
+               git submodule update --init --recursive
+       )
 '
 
 test_expect_success 'change submodule' '
-       (cd submodule &&
-        echo second line >> file &&
-        test_tick &&
-        git commit -a -m "change submodule"
+       (
+               cd submodule &&
+               echo second line >>file &&
+               test_tick &&
+               git commit -a -m "change submodule"
        )
 '
 
+reset_submodule_urls () {
+       local root
+       root=$(pwd) &&
+       (
+               cd super-clone/submodule &&
+               git config remote.origin.url "$root/submodule"
+       ) &&
+       (
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url "$root/submodule"
+       )
+}
+
 test_expect_success 'change submodule url' '
-       (cd super &&
-        cd submodule &&
-        git checkout master &&
-        git pull
+       (
+               cd super &&
+               cd submodule &&
+               git checkout master &&
+               git pull
        ) &&
        mv submodule moved-submodule &&
-       (cd moved-submodule &&
-        git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
-        test_tick &&
-        git commit -a -m moved-sub-submodule
+       (
+               cd moved-submodule &&
+               git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
+               test_tick &&
+               git commit -a -m moved-sub-submodule
        ) &&
-       (cd super &&
-        git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
-        test_tick &&
-        git commit -a -m moved-submodule
+       (
+               cd super &&
+               git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
+               test_tick &&
+               git commit -a -m moved-submodule
        )
 '
 
 test_expect_success '"git submodule sync" should update submodule URLs' '
-       (cd super-clone &&
-        git pull --no-recurse-submodules &&
-        git submodule sync
+       (
+               cd super-clone &&
+               git pull --no-recurse-submodules &&
+               git submodule sync
        ) &&
-       test -d "$(cd super-clone/submodule &&
-        git config remote.origin.url
+       test -d "$(
+               cd super-clone/submodule &&
+               git config remote.origin.url
        )" &&
-       test ! -d "$(cd super-clone/submodule/sub-submodule &&
-        git config remote.origin.url
+       test ! -d "$(
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url
        )" &&
-       (cd super-clone/submodule &&
-        git checkout master &&
-        git pull
+       (
+               cd super-clone/submodule &&
+               git checkout master &&
+               git pull
        ) &&
-       (cd super-clone &&
-        test -d "$(git config submodule.submodule.url)"
+       (
+               cd super-clone &&
+               test -d "$(git config submodule.submodule.url)"
        )
 '
 
 test_expect_success '"git submodule sync --recursive" should update all submodule URLs' '
-       (cd super-clone &&
-        (cd submodule &&
-         git pull --no-recurse-submodules
-        ) &&
-        git submodule sync --recursive
+       (
+               cd super-clone &&
+               (
+                       cd submodule &&
+                       git pull --no-recurse-submodules
+               ) &&
+               git submodule sync --recursive
+       ) &&
+       test -d "$(
+               cd super-clone/submodule &&
+               git config remote.origin.url
+       )" &&
+       test -d "$(
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url
+       )" &&
+       (
+               cd super-clone/submodule/sub-submodule &&
+               git checkout master &&
+               git pull
+       )
+'
+
+test_expect_success 'reset submodule URLs' '
+       reset_submodule_urls super-clone
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs - subdirectory' '
+       (
+               cd super-clone &&
+               git pull --no-recurse-submodules &&
+               mkdir -p sub &&
+               cd sub &&
+               git submodule sync >../../output
+       ) &&
+       grep "\\.\\./submodule" output &&
+       test -d "$(
+               cd super-clone/submodule &&
+               git config remote.origin.url
+       )" &&
+       test ! -d "$(
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url
+       )" &&
+       (
+               cd super-clone/submodule &&
+               git checkout master &&
+               git pull
+       ) &&
+       (
+               cd super-clone &&
+               test -d "$(git config submodule.submodule.url)"
+       )
+'
+
+test_expect_success '"git submodule sync --recursive" should update all submodule URLs - subdirectory' '
+       (
+               cd super-clone &&
+               (
+                       cd submodule &&
+                       git pull --no-recurse-submodules
+               ) &&
+               mkdir -p sub &&
+               cd sub &&
+               git submodule sync --recursive >../../output
        ) &&
-       test -d "$(cd super-clone/submodule &&
-        git config remote.origin.url
+       grep "\\.\\./submodule/sub-submodule" output &&
+       test -d "$(
+               cd super-clone/submodule &&
+               git config remote.origin.url
        )" &&
-       test -d "$(cd super-clone/submodule/sub-submodule &&
-        git config remote.origin.url
+       test -d "$(
+               cd super-clone/submodule/sub-submodule &&
+               git config remote.origin.url
        )" &&
-       (cd super-clone/submodule/sub-submodule &&
-        git checkout master &&
-        git pull
+       (
+               cd super-clone/submodule/sub-submodule &&
+               git checkout master &&
+               git pull
        )
 '
 
 test_expect_success '"git submodule sync" should update known submodule URLs' '
-       (cd empty-clone &&
-        git pull &&
-        git submodule sync &&
-        test -d "$(git config submodule.submodule.url)"
+       (
+               cd empty-clone &&
+               git pull &&
+               git submodule sync &&
+               test -d "$(git config submodule.submodule.url)"
        )
 '
 
 test_expect_success '"git submodule sync" should not vivify uninteresting submodule' '
-       (cd top-only-clone &&
-        git pull &&
-        git submodule sync &&
-        test -z "$(git config submodule.submodule.url)" &&
-        git submodule sync submodule &&
-        test -z "$(git config submodule.submodule.url)"
+       (
+               cd top-only-clone &&
+               git pull &&
+               git submodule sync &&
+               test -z "$(git config submodule.submodule.url)" &&
+               git submodule sync submodule &&
+               test -z "$(git config submodule.submodule.url)"
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form foo' '
-       (cd relative-clone &&
-        git remote set-url origin foo &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual fails with: "cannot strip off url foo
-        test "$(git config remote.origin.url)" = "../submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin foo &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual fails with: "cannot strip off url foo
+                       test "$(git config remote.origin.url)" = "../submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form foo/bar' '
-       (cd relative-clone &&
-        git remote set-url origin foo/bar &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual foo/submodule
-        test "$(git config remote.origin.url)" = "../foo/submodule"
-       )
-       (cd submodule/sub-submodule &&
-        test "$(git config remote.origin.url)" != "../../foo/submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin foo/bar &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual foo/submodule
+                       test "$(git config remote.origin.url)" = "../foo/submodule"
+               ) &&
+               (
+                       cd submodule/sub-submodule &&
+                       test "$(git config remote.origin.url)" != "../../foo/submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync --recursive" propagates changes in origin' '
-       (cd recursive-clone &&
-        git remote set-url origin foo/bar &&
-        git submodule sync --recursive &&
-       (cd submodule &&
-        #actual foo/submodule
-        test "$(git config remote.origin.url)" = "../foo/submodule"
-       )
-       (cd submodule/sub-submodule &&
-        test "$(git config remote.origin.url)" = "../../foo/submodule"
-       )
+       (
+               cd recursive-clone &&
+               git remote set-url origin foo/bar &&
+               git submodule sync --recursive &&
+               (
+                       cd submodule &&
+                       #actual foo/submodule
+                       test "$(git config remote.origin.url)" = "../foo/submodule"
+               ) &&
+               (
+                       cd submodule/sub-submodule &&
+                       test "$(git config remote.origin.url)" = "../../foo/submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ./foo' '
-       (cd relative-clone &&
-        git remote set-url origin ./foo &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual ./submodule
-        test "$(git config remote.origin.url)" = "../submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ./foo &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual ./submodule
+                       test "$(git config remote.origin.url)" = "../submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ./foo/bar' '
-       (cd relative-clone &&
-        git remote set-url origin ./foo/bar &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual ./foo/submodule
-        test "$(git config remote.origin.url)" = "../foo/submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ./foo/bar &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual ./foo/submodule
+                       test "$(git config remote.origin.url)" = "../foo/submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo' '
-       (cd relative-clone &&
-        git remote set-url origin ../foo &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual ../submodule
-        test "$(git config remote.origin.url)" = "../../submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ../foo &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual ../submodule
+                       test "$(git config remote.origin.url)" = "../../submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar' '
-       (cd relative-clone &&
-        git remote set-url origin ../foo/bar &&
-        git submodule sync &&
-       (cd submodule &&
-        #actual ../foo/submodule
-        test "$(git config remote.origin.url)" = "../../foo/submodule"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ../foo/bar &&
+               git submodule sync &&
+               (
+                       cd submodule &&
+                       #actual ../foo/submodule
+                       test "$(git config remote.origin.url)" = "../../foo/submodule"
+               )
        )
 '
 
 test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar with deeply nested submodule' '
-       (cd relative-clone &&
-        git remote set-url origin ../foo/bar &&
-        mkdir -p a/b/c &&
-        ( cd a/b/c &&
-          git init &&
-          :> .gitignore &&
-          git add .gitignore &&
-          test_tick &&
-          git commit -m "initial commit" ) &&
-        git submodule add ../bar/a/b/c ./a/b/c &&
-        git submodule sync &&
-       (cd a/b/c &&
-        #actual ../foo/bar/a/b/c
-        test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
-       )
+       (
+               cd relative-clone &&
+               git remote set-url origin ../foo/bar &&
+               mkdir -p a/b/c &&
+               (
+                       cd a/b/c &&
+                       git init &&
+                       >.gitignore &&
+                       git add .gitignore &&
+                       test_tick &&
+                       git commit -m "initial commit"
+               ) &&
+               git submodule add ../bar/a/b/c ./a/b/c &&
+               git submodule sync &&
+               (
+                       cd a/b/c &&
+                       #actual ../foo/bar/a/b/c
+                       test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
+               )
        )
 '
 
index a4ffea0dbe743a1bbe4c5512037a6b1ffde1c313..f0b33053ab01c692a4dda15c0f631aaa45fb81b9 100755 (executable)
@@ -58,7 +58,7 @@ test_expect_success 'setup a submodule tree' '
         git submodule add ../merging merging &&
         test_tick &&
         git commit -m "rebasing"
-       )
+       ) &&
        (cd super &&
         git submodule add ../none none &&
         test_tick &&
@@ -80,6 +80,21 @@ test_expect_success 'submodule update detaching the HEAD ' '
        )
 '
 
+test_expect_success 'submodule update from subdirectory' '
+       (cd super/submodule &&
+        git reset --hard HEAD~1
+       ) &&
+       mkdir super/sub &&
+       (cd super/sub &&
+        (cd ../submodule &&
+         compare_head
+        ) &&
+        git submodule update ../submodule &&
+        cd ../submodule &&
+        ! compare_head
+       )
+'
+
 apos="'";
 test_expect_success 'submodule update does not fetch already present commits' '
        (cd submodule &&
@@ -279,6 +294,35 @@ test_expect_success 'submodule update - checkout in .git/config' '
        )
 '
 
+test_expect_success 'submodule update - command in .git/config' '
+       (cd super &&
+        git config submodule.submodule.update "!git checkout"
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD^
+       ) &&
+       (cd super &&
+        (cd submodule &&
+         compare_head
+        ) &&
+        git submodule update submodule &&
+        cd submodule &&
+        ! compare_head
+       )
+'
+
+test_expect_success 'submodule update - command in .git/config catches failure' '
+       (cd super &&
+        git config submodule.submodule.update "!false"
+       ) &&
+       (cd super/submodule &&
+         git reset --hard HEAD^
+       ) &&
+       (cd super &&
+        test_must_fail git submodule update submodule
+       )
+'
+
 test_expect_success 'submodule init picks up rebase' '
        (cd super &&
         git config -f .gitmodules submodule.rebasing.update rebase &&
@@ -685,14 +729,24 @@ test_expect_success 'submodule update properly revives a moved submodule' '
 test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd' '
        mkdir -p linked/dir &&
        ln -s linked/dir linkto &&
-       (
-               cd linkto &&
-               git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
-               (
-                       cd super &&
-                       git submodule update --init --recursive
-               )
+       (cd linkto &&
+        git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
+        (cd super &&
+         git submodule update --init --recursive
+        )
        )
 '
 
+test_expect_success 'submodule update clone shallow submodule' '
+       git clone cloned super3 &&
+       pwd=$(pwd)
+       (cd super3 &&
+        sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
+        mv -f .gitmodules.tmp .gitmodules &&
+        git submodule update --init --depth=3
+        (cd submodule &&
+         test 1 = $(git log --oneline | wc -l)
+        )
+       )
+'
 test_done
index 107b4b7c455dc0be6400eb1d462868b940b1247d..be93f10cf02bc77e7c78be41bea69b4268305e66 100755 (executable)
@@ -80,6 +80,22 @@ test_expect_success 'test basic "submodule foreach" usage' '
        test_i18ncmp expect actual
 '
 
+cat >expect <<EOF
+Entering '../sub1'
+$pwd/clone-foo1-../sub1-$sub1sha1
+Entering '../sub3'
+$pwd/clone-foo3-../sub3-$sub3sha1
+EOF
+
+test_expect_success 'test "submodule foreach" from subdirectory' '
+       mkdir clone/sub &&
+       (
+               cd clone/sub &&
+               git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$sha1" >../../actual
+       ) &&
+       test_i18ncmp expect actual
+'
+
 test_expect_success 'setup nested submodules' '
        git clone submodule nested1 &&
        git clone submodule nested2 &&
@@ -129,7 +145,7 @@ test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
                git rev-parse --resolve-git-dir nested1/.git &&
                test_must_fail git rev-parse --resolve-git-dir nested1/nested2/.git &&
                git submodule foreach "git submodule update --init" &&
-               git rev-parse --resolve-git-dir nested1/nested1/nested2/.git
+               git rev-parse --resolve-git-dir nested1/nested2/.git &&
                test_must_fail git rev-parse --resolve-git-dir nested1/nested2/nested3/.git
        )
 '
index 2fec13dcd32a551d4fb82d0bea185ba6b77c9e14..c20717181e95fc74738bdf53aa3e0b0797d77d6a 100755 (executable)
@@ -23,7 +23,9 @@ test_expect_success 'submodule on detached working tree' '
        mkdir home &&
        (
                cd home &&
-               export GIT_WORK_TREE="$(pwd)" GIT_DIR="$(pwd)/.dotfiles" &&
+               GIT_WORK_TREE="$(pwd)" &&
+               GIT_DIR="$(pwd)/.dotfiles" &&
+               export GIT_WORK_TREE GIT_DIR &&
                git clone --bare ../remote .dotfiles &&
                git submodule add ../bundle1 .vim/bundle/sogood &&
                test_commit "sogood" &&
@@ -39,7 +41,9 @@ test_expect_success 'submodule on detached working tree' '
        (
                cd home2 &&
                git clone --bare ../remote .dotfiles &&
-               export GIT_WORK_TREE="$(pwd)" GIT_DIR="$(pwd)/.dotfiles" &&
+               GIT_WORK_TREE="$(pwd)" &&
+               GIT_DIR="$(pwd)/.dotfiles" &&
+               export GIT_WORK_TREE GIT_DIR &&
                git checkout master &&
                git submodule update --init &&
                (
@@ -55,7 +59,8 @@ test_expect_success 'submodule on detached working pointed by core.worktree' '
        mkdir home3 &&
        (
                cd home3 &&
-               export GIT_DIR="$(pwd)/.dotfiles" &&
+               GIT_DIR="$(pwd)/.dotfiles" &&
+               export GIT_DIR &&
                git clone --bare ../remote "$GIT_DIR" &&
                git config core.bare false &&
                git config core.worktree .. &&
@@ -66,7 +71,8 @@ test_expect_success 'submodule on detached working pointed by core.worktree' '
        ) &&
        (
                cd home &&
-               export GIT_DIR="$(pwd)/.dotfiles" &&
+               GIT_DIR="$(pwd)/.dotfiles" &&
+               export GIT_DIR &&
                git config core.bare false &&
                git config core.worktree .. &&
                git pull &&
index 436b7b606e3eab09a0dd8c1b67ab90e43c24dffc..bdc1f295030b61fd6d4fc0ef672dffa99c027d8b 100755 (executable)
@@ -13,9 +13,9 @@ commit_msg_is () {
        expect=commit_msg_is.expect
        actual=commit_msg_is.actual
 
-       printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect &&
-       printf "%s" "$1" >$actual &&
-       test_i18ncmp $expect $actual
+       printf "%s" "$(git log --pretty=format:%s%b -1)" >"$actual" &&
+       printf "%s" "$1" >"$expect" &&
+       test_i18ncmp "$expect" "$actual"
 }
 
 # A sanity check to see if commit is working at all.
index 195e7477d883f39d51f5740b5b309a489dd92d3d..99ce36f5ef91ae91c0f2ebd02ae70c4cba4c937c 100755 (executable)
@@ -524,4 +524,17 @@ test_expect_success 'commit a file whose name is a dash' '
        test_i18ngrep " changed, 5 insertions" output
 '
 
+test_expect_success '--only works on to-be-born branch' '
+       # This test relies on having something in the index, as it
+       # would not otherwise actually prove much.  So check this.
+       test -n "$(git ls-files)" &&
+       git checkout --orphan orphan &&
+       echo foo >newfile &&
+       git add newfile &&
+       git commit --only newfile -m"--only on unborn branch" &&
+       echo newfile >expected &&
+       git ls-tree -r --name-only HEAD >actual &&
+       test_cmp expected actual
+'
+
 test_done
index a4938b1e4549d5082362ec3f5513ffe91f210d39..6313da2cdd979c6264e7a255711c6e444b768b96 100755 (executable)
@@ -361,6 +361,23 @@ test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' '
        test_cmp expect .git/result
 '
 
+test_expect_success 'do not fire editor if -m <msg> was given' '
+       echo tick >file &&
+       git add file &&
+       echo "editor not started" >.git/result &&
+       (GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" git commit -m tick) &&
+       test "$(cat .git/result)" = "editor not started"
+'
+
+test_expect_success 'do not fire editor if -m "" was given' '
+       echo tock >file &&
+       git add file &&
+       echo "editor not started" >.git/result &&
+       (GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" \
+        git commit -m "" --allow-empty-message) &&
+       test "$(cat .git/result)" = "editor not started"
+'
+
 test_expect_success 'do not fire editor in the presence of conflicts' '
 
        git clean -f &&
index e2ffdacc267c22e369247791e24338a20badf94d..ac3d0fe44507328f6363edc1d614bc9d8369899b 100755 (executable)
@@ -1335,4 +1335,66 @@ test_expect_failure '.git/config ignore=all suppresses submodule summary' '
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
+test_expect_success 'setup of test environment' '
+       git config status.showUntrackedFiles no &&
+       git status -s >expected_short &&
+       git status --no-short >expected_noshort
+'
+
+test_expect_success '"status.short=true" same as "-s"' '
+       git -c status.short=true status >actual &&
+       test_cmp expected_short actual
+'
+
+test_expect_success '"status.short=true" weaker than "--no-short"' '
+       git -c status.short=true status --no-short >actual &&
+       test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" same as "--no-short"' '
+       git -c status.short=false status >actual &&
+       test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" weaker than "-s"' '
+       git -c status.short=false status -s >actual &&
+       test_cmp expected_short actual
+'
+
+test_expect_success '"status.branch=true" same as "-b"' '
+       git status -sb >expected_branch &&
+       git -c status.branch=true status -s >actual &&
+       test_cmp expected_branch actual
+'
+
+test_expect_success '"status.branch=true" different from "--no-branch"' '
+       git status -s --no-branch  >expected_nobranch &&
+       git -c status.branch=true status -s >actual &&
+       test_must_fail test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--no-branch"' '
+       git -c status.branch=true status -s --no-branch >actual &&
+       test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--porcelain"' '
+       git -c status.branch=true status --porcelain >actual &&
+       test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" same as "--no-branch"' '
+       git -c status.branch=false status -s >actual &&
+       test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" weaker than "-b"' '
+       git -c status.branch=false status -sb >actual &&
+       test_cmp expected_branch actual
+'
+
+test_expect_success 'Restore default test environment' '
+       git config --unset status.showUntrackedFiles
+'
+
 test_done
index bf08d4e098f1bdc3adc5ec3df37b90d90b0e5c8f..31a798fda21f178cbbedf47ee92dbd997f9c4e42 100755 (executable)
@@ -77,7 +77,7 @@ test_expect_success 'status when rebase in progress before resolving conflicts'
        ONTO=$(git rev-parse --short HEAD^^) &&
        test_must_fail git rebase HEAD^ --onto HEAD^^ &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
        #   (fix conflicts and then run "git rebase --continue")
        #   (use "git rebase --skip" to skip this patch)
@@ -104,7 +104,7 @@ test_expect_success 'status when rebase in progress before rebase --continue' '
        echo three >main.txt &&
        git add main.txt &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
        #   (all conflicts fixed: run "git rebase --continue")
        #
@@ -136,7 +136,7 @@ test_expect_success 'status during rebase -i when conflicts unresolved' '
        ONTO=$(git rev-parse --short rebase_i_conflicts) &&
        test_must_fail git rebase -i rebase_i_conflicts &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
        #   (fix conflicts and then run "git rebase --continue")
        #   (use "git rebase --skip" to skip this patch)
@@ -162,7 +162,7 @@ test_expect_success 'status during rebase -i after resolving conflicts' '
        test_must_fail git rebase -i rebase_i_conflicts &&
        git add main.txt &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
        #   (all conflicts fixed: run "git rebase --continue")
        #
@@ -188,10 +188,9 @@ test_expect_success 'status when rebasing -i in edit mode' '
        export FAKE_LINES &&
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD~2) &&
-       TGT=$(git rev-parse --short two_rebase_i) &&
        git rebase -i HEAD~2 &&
        cat >expected <<-EOF &&
-       # HEAD detached from $TGT
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -216,9 +215,8 @@ test_expect_success 'status when splitting a commit' '
        ONTO=$(git rev-parse --short HEAD~3) &&
        git rebase -i HEAD~3 &&
        git reset HEAD^ &&
-       TGT=$(git rev-parse --short HEAD) &&
        cat >expected <<-EOF &&
-       # HEAD detached at $TGT
+       # rebase in progress; onto $ONTO
        # You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -246,11 +244,10 @@ test_expect_success 'status after editing the last commit with --amend during a
        export FAKE_LINES &&
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD~3) &&
-       TGT=$(git rev-parse --short three_amend) &&
        git rebase -i HEAD~3 &&
        git commit --amend -m "foo" &&
        cat >expected <<-EOF &&
-       # HEAD detached from $TGT
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -280,7 +277,7 @@ test_expect_success 'status: (continue first edit) second edit' '
        git rebase -i HEAD~3 &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -302,7 +299,7 @@ test_expect_success 'status: (continue first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -329,7 +326,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "foo" &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -351,7 +348,7 @@ test_expect_success 'status: (amend first edit) second edit' '
        git commit --amend -m "a" &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -374,7 +371,7 @@ test_expect_success 'status: (amend first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -402,7 +399,7 @@ test_expect_success 'status: (amend first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "d" &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -426,7 +423,7 @@ test_expect_success 'status: (split first edit) second edit' '
        git commit -m "e" &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -451,7 +448,7 @@ test_expect_success 'status: (split first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -481,7 +478,7 @@ test_expect_success 'status: (split first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "h" &&
        cat >expected <<-EOF &&
-       # HEAD detached from $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -510,7 +507,7 @@ test_expect_success 'status in an am session: file already exists' '
        cat >expected <<-\EOF &&
        # On branch am_already_exists
        # You are in the middle of an am session.
-       #   (fix conflicts and then run "git am --resolved")
+       #   (fix conflicts and then run "git am --continue")
        #   (use "git am --skip" to skip this patch)
        #   (use "git am --abort" to restore the original branch)
        #
@@ -532,7 +529,7 @@ test_expect_success 'status in an am session: file does not exist' '
        cat >expected <<-\EOF &&
        # On branch am_not_exists
        # You are in the middle of an am session.
-       #   (fix conflicts and then run "git am --resolved")
+       #   (fix conflicts and then run "git am --continue")
        #   (use "git am --skip" to skip this patch)
        #   (use "git am --abort" to restore the original branch)
        #
@@ -601,7 +598,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
        ONTO=$(git rev-parse --short HEAD^^) &&
        test_must_fail git rebase HEAD^ --onto HEAD^^ &&
        cat >expected <<-EOF &&
-       # HEAD detached at $ONTO
+       # rebase in progress; onto $ONTO
        # You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
        #
        # Unmerged paths:
@@ -632,7 +629,8 @@ test_expect_success 'status when cherry-picking before resolving conflicts' '
        cat >expected <<-\EOF &&
        # On branch cherry_branch
        # You are currently cherry-picking.
-       #   (fix conflicts and run "git commit")
+       #   (fix conflicts and run "git cherry-pick --continue")
+       #   (use "git cherry-pick --abort" to cancel the cherry-pick operation)
        #
        # Unmerged paths:
        #   (use "git add <file>..." to mark resolution)
@@ -655,7 +653,8 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '
        cat >expected <<-\EOF &&
        # On branch cherry_branch
        # You are currently cherry-picking.
-       #   (all conflicts fixed: run "git commit")
+       #   (all conflicts fixed: run "git cherry-pick --continue")
+       #   (use "git cherry-pick --abort" to cancel the cherry-pick operation)
        #
        # Changes to be committed:
        #
@@ -667,7 +666,7 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '
        test_i18ncmp expected actual
 '
 
-test_expect_success 'status showing detached from a tag' '
+test_expect_success 'status showing detached at and from a tag' '
        test_commit atag tagging &&
        git checkout atag &&
        cat >expected <<-\EOF
@@ -675,6 +674,14 @@ test_expect_success 'status showing detached from a tag' '
        nothing to commit (use -u to show untracked files)
        EOF
        git status --untracked-files=no >actual &&
+       test_i18ncmp expected actual &&
+
+       git reset --hard HEAD^ &&
+       cat >expected <<-\EOF
+       # HEAD detached from atag
+       nothing to commit (use -u to show untracked files)
+       EOF
+       git status --untracked-files=no >actual &&
        test_i18ncmp expected actual
 '
 
index 2f70433568e3134b09d7e1eb10cc5c8995c1a106..10aa028017833479c43bbb551741dfd36d9741aa 100755 (executable)
@@ -316,7 +316,7 @@ test_expect_success 'merge c1 with c2 (squash)' '
 
 test_debug 'git log --graph --decorate --oneline --all'
 
-test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
+test_expect_success 'unsuccessful merge of c1 with c2 (squash, ff-only)' '
        git reset --hard c1 &&
        test_must_fail git merge --squash --ff-only c2
 '
@@ -497,9 +497,15 @@ test_expect_success 'combining --squash and --no-ff is refused' '
        test_must_fail git merge --no-ff --squash c1
 '
 
-test_expect_success 'combining --ff-only and --no-ff is refused' '
-       test_must_fail git merge --ff-only --no-ff c1 &&
-       test_must_fail git merge --no-ff --ff-only c1
+test_expect_success 'option --ff-only overwrites --no-ff' '
+       git merge --no-ff --ff-only c1 &&
+       test_must_fail git merge --no-ff --ff-only c2
+'
+
+test_expect_success 'option --no-ff overrides merge.ff=only config' '
+       git reset --hard c0 &&
+       test_config merge.ff only &&
+       git merge --no-ff c1
 '
 
 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
index 25dac798894f6a3cb4b62c8b9ba944bfc1d82d9f..830a4c3e9dffc7963dda03139be6c4e66fddf10f 100755 (executable)
@@ -109,7 +109,7 @@ test_expect_success 'setup conflicted merge' '
 '
 
 # First do the merge with resolve and recursive then verify that
-# recusive is chosen.
+# recursive is chosen.
 
 test_expect_success 'merge picks up the best result' '
        git config --unset-all pull.twohead &&
index 6547eb8f5459d4d95113469d338e1879f86b79ea..758a623cdbb5f7e367bcfce5c239d79258c46e84 100755 (executable)
@@ -141,11 +141,10 @@ test_expect_success SYMLINKS 'will not overwrite untracked symlink in leading pa
        test_path_is_missing .git/MERGE_HEAD
 '
 
-test_expect_success SYMLINKS 'will not be confused by symlink in leading path' '
+test_expect_success 'will not be confused by symlink in leading path' '
        git reset --hard c0 &&
        rm -rf sub &&
-       ln -s sub2 sub &&
-       git add sub &&
+       test_ln_s_add sub2 sub &&
        git commit -m ln &&
        git checkout sub
 '
index a6bd99eaf51943a899aa5e5801479b0a84b6b692..2418528487624bf5497af71c9958a84e0a50328f 100755 (executable)
@@ -356,6 +356,13 @@ run_dir_diff_test 'difftool --dir-diff from subdirectory' '
        )
 '
 
+run_dir_diff_test 'difftool --dir-diff when worktree file is missing' '
+       test_when_finished git reset --hard &&
+       rm file2 &&
+       git difftool --dir-diff $symlinks --extcmd ls branch master >output &&
+       grep file2 output
+'
+
 write_script .git/CHECK_SYMLINKS <<\EOF
 for f in file file2 sub/sub
 do
@@ -378,6 +385,25 @@ test_expect_success PERL,SYMLINKS 'difftool --dir-diff --symlink without unstage
        test_cmp actual expect
 '
 
+write_script modify-right-file <<\EOF
+echo "new content" >"$2/file"
+EOF
+
+run_dir_diff_test 'difftool --dir-diff syncs worktree with unstaged change' '
+       test_when_finished git reset --hard &&
+       echo "orig content" >file &&
+       git difftool -d $symlinks --extcmd "$(pwd)/modify-right-file" branch &&
+       echo "new content" >expect &&
+       test_cmp expect file
+'
+
+run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' '
+       test_when_finished git reset --hard &&
+       git difftool -d $symlinks --extcmd "$(pwd)/modify-right-file" branch &&
+       echo "new content" >expect &&
+       test_cmp expect file
+'
+
 write_script modify-file <<\EOF
 echo "new content" >file
 EOF
index 41962f04a715ea5250360766dc64986c4ecf7981..72176e42c1d188f03f319b5a9acd954c5fe8ab6a 100755 (executable)
@@ -6,9 +6,9 @@ test_description='git annotate'
 PROG='git annotate'
 . "$TEST_DIRECTORY"/annotate-tests.sh
 
-test_expect_success 'Annotating an old revision works' '
-       git annotate file master >result &&
-       awk "{ print \$3; }" <result >authors &&
+test_expect_success 'annotate old revision' '
+       git annotate file master >actual &&
+       awk "{ print \$3; }" <actual >authors &&
        test 2 = $(grep A <authors | wc -l) &&
        test 2 = $(grep B <authors | wc -l)
 '
index e2896cffc17c519d208c26f215e4605d85edb918..5cdf3f178e8b94481370bcc8385248ec88e46f2c 100755 (executable)
@@ -7,8 +7,16 @@ PROG='git blame -c'
 . "$TEST_DIRECTORY"/annotate-tests.sh
 
 PROG='git blame -c -e'
-test_expect_success 'Blame --show-email works' '
-    check_count "<A@test.git>" 1 "<B@test.git>" 1 "<B1@test.git>" 1 "<B2@test.git>" 1 "<author@example.com>" 1 "<C@test.git>" 1 "<D@test.git>" 1 "<E at test dot git>" 1
+test_expect_success 'blame --show-email' '
+       check_count \
+               "<A@test.git>" 1 \
+               "<B@test.git>" 1 \
+               "<B1@test.git>" 1 \
+               "<B2@test.git>" 1 \
+               "<author@example.com>" 1 \
+               "<C@test.git>" 1 \
+               "<D@test.git>" 1 \
+               "<E at test dot git>" 1
 '
 
 test_done
index 230143cf318705fb01e61f10072a096e86186934..e7cac1db55113188c7149686399292bded9c07dd 100755 (executable)
@@ -175,6 +175,12 @@ test_expect_success 'blame -L with invalid end' '
        grep "has only 2 lines" errors
 '
 
+test_expect_success 'blame parses <end> part of -L' '
+       git blame -L1,1 tres >out &&
+       cat out &&
+       test $(wc -l < out) -eq 1
+'
+
 test_expect_success 'indent of line numbers, nine lines' '
        git blame nine_lines >actual &&
        test $(grep -c "  " actual) = 0
index bf6caa4dc3d42230757526dd215ab777f77ae369..7683515155497d9303f319b31c3a49fb6610706f 100755 (executable)
@@ -18,17 +18,13 @@ test_expect_success 'setup ' '
        echo "bin: test number 0" >zero.bin &&
        echo "bin: test 1" >one.bin &&
        echo "bin: test number 2" >two.bin &&
-       if test_have_prereq SYMLINKS; then
-               ln -s one.bin symlink.bin
-       fi &&
+       test_ln_s_add one.bin symlink.bin &&
        git add . &&
        GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
        echo "bin: test 1 version 2" >one.bin &&
        echo "bin: test number 2 version 2" >>two.bin &&
-       if test_have_prereq SYMLINKS; then
-               rm symlink.bin &&
-               ln -s two.bin symlink.bin
-       fi &&
+       rm -f symlink.bin &&
+       test_ln_s_add two.bin symlink.bin &&
        GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
 '
 
@@ -135,7 +131,7 @@ test_expect_success SYMLINKS 'blame --textconv (on symlink)' '
 
 # cp two.bin three.bin  and make small tweak
 # (this will direct blame -C -C three.bin to consider two.bin and symlink.bin)
-test_expect_success SYMLINKS 'make another new commit' '
+test_expect_success 'make another new commit' '
        cat >three.bin <<\EOF &&
 bin: test number 2
 bin: test number 2 version 2
@@ -146,7 +142,7 @@ EOF
        GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00"
 '
 
-test_expect_success SYMLINKS 'blame on last commit (-C -C, symlink)' '
+test_expect_success 'blame on last commit (-C -C, symlink)' '
        git blame -C -C three.bin >blame &&
        find_blame <blame >result &&
        cat >expected <<\EOF &&
index 78a0085e648b8fa6773b47e86959853cf29ccdf9..b95e102891db65c184a2ee3137f1b3e20cdab2db 100755 (executable)
@@ -12,9 +12,7 @@ chmod +x helper
 
 test_expect_success 'setup ' '
        echo "bin: test" >one.bin &&
-       if test_have_prereq SYMLINKS; then
-               ln -s one.bin symlink.bin
-       fi &&
+       test_ln_s_add one.bin symlink.bin &&
        git add . &&
        GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
        echo "bin: test version 2" >one.bin &&
@@ -72,14 +70,14 @@ test_expect_success 'cat-file --textconv on previous commit' '
        test_cmp expected result
 '
 
-test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' '
+test_expect_success 'cat-file without --textconv (symlink)' '
        git cat-file blob :symlink.bin >result &&
        printf "%s" "one.bin" >expected
        test_cmp expected result
 '
 
 
-test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' '
+test_expect_success 'cat-file --textconv on index (symlink)' '
        ! git cat-file --textconv :symlink.bin 2>result &&
        cat >expected <<\EOF &&
 fatal: git cat-file --textconv: unable to run textconv on :symlink.bin
@@ -87,7 +85,7 @@ EOF
        test_cmp expected result
 '
 
-test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' '
+test_expect_success 'cat-file --textconv on HEAD (symlink)' '
        ! git cat-file --textconv HEAD:symlink.bin 2>result &&
        cat >expected <<EOF &&
 fatal: git cat-file --textconv: unable to run textconv on HEAD:symlink.bin
index ebd5c5db45c8816f39b2d8c60775f455ab9c48aa..2813aa9a61dc52d6d72f684c51fe6894e623754d 100755 (executable)
@@ -171,6 +171,81 @@ Result: OK
 EOF
 "
 
+test_suppress_self () {
+       test_commit $3 &&
+       test_when_finished "git reset --hard HEAD^" &&
+
+       write_script cccmd-sed <<-EOF &&
+               sed -n -e s/^cccmd--//p "\$1"
+       EOF
+
+       git commit --amend --author="$1 <$2>" -F - &&
+       clean_fake_sendmail &&
+       git format-patch --stdout -1 >"suppress-self-$3.patch" &&
+
+       git send-email --from="$1 <$2>" \
+               --to=nobody@example.com \
+               --cc-cmd=./cccmd-sed \
+               --suppress-cc=self \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               suppress-self-$3.patch &&
+
+       mv msgtxt1 msgtxt1-$3 &&
+       sed -e '/^$/q' msgtxt1-$3 >"msghdr1-$3" &&
+       >"expected-no-cc-$3" &&
+
+       (grep '^Cc:' msghdr1-$3 >"actual-no-cc-$3";
+        test_cmp expected-no-cc-$3 actual-no-cc-$3)
+}
+
+test_suppress_self_unquoted () {
+       test_suppress_self "$1" "$2" "unquoted-$3" <<-EOF
+               test suppress-cc.self unquoted-$3 with name $1 email $2
+
+               unquoted-$3
+
+               cccmd--$1 <$2>
+
+               Cc: $1 <$2>
+               Signed-off-by: $1 <$2>
+       EOF
+}
+
+test_suppress_self_quoted () {
+       test_suppress_self "$1" "$2" "quoted-$3" <<-EOF
+               test suppress-cc.self quoted-$3 with name $1 email $2
+
+               quoted-$3
+
+               cccmd--"$1" <$2>
+
+               Cc: $1 <$2>
+               Cc: "$1" <$2>
+               Signed-off-by: $1 <$2>
+               Signed-off-by: "$1" <$2>
+       EOF
+}
+
+test_expect_success $PREREQ 'self name is suppressed' "
+       test_suppress_self_unquoted 'A U Thor' 'author@example.com' \
+               'self_name_suppressed'
+"
+
+test_expect_success $PREREQ 'self name with dot is suppressed' "
+       test_suppress_self_quoted 'A U. Thor' 'author@example.com' \
+               'self_name_dot_suppressed'
+"
+
+test_expect_success $PREREQ 'non-ascii self name is suppressed' "
+       test_suppress_self_quoted 'Füñný Nâmé' 'odd_?=mail@example.com' \
+               'non_ascii_self_suppressed'
+"
+
+test_expect_success $PREREQ 'sanitized self name is suppressed' "
+       test_suppress_self_unquoted '\"A U. Thor\"' 'author@example.com' \
+               'self_name_sanitized_suppressed'
+"
+
 test_expect_success $PREREQ 'Show all headers' '
        git send-email \
                --dry-run \
@@ -881,6 +956,20 @@ test_expect_success $PREREQ 'utf8 author is correctly passed on' '
        grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1
 '
 
+test_expect_success $PREREQ 'utf8 sender is not duplicated' '
+       clean_fake_sendmail &&
+       test_commit weird_sender &&
+       test_when_finished "git reset --hard HEAD^" &&
+       git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" &&
+       git format-patch --stdout -1 >funny_name.patch &&
+       git send-email --from="Füñný Nâmé <odd_?=mail@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         funny_name.patch &&
+       grep "^From: " msgtxt1 >msgfrom &&
+       test_line_count = 1 msgfrom
+'
+
 test_expect_success $PREREQ 'sendemail.composeencoding works' '
        clean_fake_sendmail &&
        git config sendemail.composeencoding iso-8859-1 &&
@@ -1003,55 +1092,6 @@ test_expect_success $PREREQ 'threading but no chain-reply-to' '
        grep "In-Reply-To: " stdout
 '
 
-test_expect_success $PREREQ 'warning with an implicit --chain-reply-to' '
-       git send-email \
-       --dry-run \
-       --from="Example <nobody@example.com>" \
-       --to=nobody@example.com \
-       outdir/000?-*.patch 2>errors >out &&
-       grep "no-chain-reply-to" errors
-'
-
-test_expect_success $PREREQ 'no warning with an explicit --chain-reply-to' '
-       git send-email \
-       --dry-run \
-       --from="Example <nobody@example.com>" \
-       --to=nobody@example.com \
-       --chain-reply-to \
-       outdir/000?-*.patch 2>errors >out &&
-       ! grep "no-chain-reply-to" errors
-'
-
-test_expect_success $PREREQ 'no warning with an explicit --no-chain-reply-to' '
-       git send-email \
-       --dry-run \
-       --from="Example <nobody@example.com>" \
-       --to=nobody@example.com \
-       --nochain-reply-to \
-       outdir/000?-*.patch 2>errors >out &&
-       ! grep "no-chain-reply-to" errors
-'
-
-test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = false' '
-       git config sendemail.chainreplyto false &&
-       git send-email \
-       --dry-run \
-       --from="Example <nobody@example.com>" \
-       --to=nobody@example.com \
-       outdir/000?-*.patch 2>errors >out &&
-       ! grep "no-chain-reply-to" errors
-'
-
-test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = true' '
-       git config sendemail.chainreplyto true &&
-       git send-email \
-       --dry-run \
-       --from="Example <nobody@example.com>" \
-       --to=nobody@example.com \
-       outdir/000?-*.patch 2>errors >out &&
-       ! grep "no-chain-reply-to" errors
-'
-
 test_expect_success $PREREQ 'sendemail.to works' '
        git config --replace-all sendemail.to "Somebody <somebody@ex.com>" &&
        git send-email \
index 2d2f016f6f8b151f10b08e4419dc6d3bdef3d0a8..4d81ba1c2c43bfe14cb329967ccf2c97b90a4848 100755 (executable)
@@ -24,7 +24,7 @@ init_git () {
        rm -fr .git &&
        git init &&
        #git remote add svnsim testsvn::sim:///$TEST_DIRECTORY/t9020/example.svnrdump
-       # let's reuse an exisiting dump file!?
+       # let's reuse an existing dump file!?
        git remote add svnsim testsvn::sim://$TEST_DIRECTORY/t9154/svn.dump
        git remote add svnfile testsvn::file://$TEST_DIRECTORY/t9154/svn.dump
 }
@@ -74,7 +74,8 @@ test_expect_success REMOTE_SVN 'mark-file regeneration' '
 '
 
 test_expect_success REMOTE_SVN 'incremental imports must lead to the same head' '
-       export SVNRMAX=3 &&
+       SVNRMAX=3 &&
+       export SVNRMAX &&
        init_git &&
        git fetch svnsim &&
        test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master  &&
index a61d6716d294a460c195ca36d33a9bed17aa5e33..9861c719f8c5e3fbe5b626e5fdf1ebf83156086c 100755 (executable)
@@ -7,7 +7,7 @@ test_description='test that git handles an svn repository with missing md5sums'
 # Loading a node from a svn dumpfile without a Text-Content-Length
 # field causes svn to neglect to store or report an md5sum.  (it will
 # calculate one if you had put Text-Content-Length: 0).  This showed
-# up in a repository creted with cvs2svn.
+# up in a repository created with cvs2svn.
 
 cat > dumpfile.svn <<EOF
 SVN-fs-dump-format-version: 1
diff --git a/t/t9147-git-svn-include-paths.sh b/t/t9147-git-svn-include-paths.sh
new file mode 100755 (executable)
index 0000000..a90ff58
--- /dev/null
@@ -0,0 +1,149 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Paul Walmsley - based on t9134 by Vitaly Shukela
+#
+
+test_description='git svn property tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+       svn_cmd co "$svnrepo" s &&
+       (
+               cd s &&
+               mkdir qqq www xxx &&
+               echo test_qqq > qqq/test_qqq.txt &&
+               echo test_www > www/test_www.txt &&
+               echo test_xxx > xxx/test_xxx.txt &&
+               svn_cmd add qqq &&
+               svn_cmd add www &&
+               svn_cmd add xxx &&
+               svn_cmd commit -m "create some files" &&
+               svn_cmd up &&
+               echo hi >> www/test_www.txt &&
+               svn_cmd commit -m "modify www/test_www.txt" &&
+               svn_cmd up
+       )
+'
+
+test_expect_success 'clone an SVN repository with filter to include qqq directory' '
+       git svn clone --include-paths="qqq" "$svnrepo" g &&
+       echo test_qqq > expect &&
+       for i in g/*/*.txt; do cat $i >> expect2; done &&
+       test_cmp expect expect2
+'
+
+
+test_expect_success 'init+fetch an SVN repository with included qqq directory' '
+       git svn init "$svnrepo" c &&
+       ( cd c && git svn fetch --include-paths="qqq" ) &&
+       rm expect2 &&
+       echo test_qqq > expect &&
+       for i in c/*/*.txt; do cat $i >> expect2; done &&
+       test_cmp expect expect2
+'
+
+test_expect_success 'verify include-paths config saved by clone' '
+       (
+           cd g &&
+           git config --get svn-remote.svn.include-paths | fgrep "qqq"
+       )
+'
+
+test_expect_success 'SVN-side change outside of www' '
+       (
+               cd s &&
+               echo b >> qqq/test_qqq.txt &&
+               svn_cmd commit -m "SVN-side change outside of www" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change outside of www"
+       )
+'
+
+test_expect_success 'update git svn-cloned repo (config include)' '
+       (
+               cd g &&
+               git svn rebase &&
+               printf "test_qqq\nb\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_expect_success 'update git svn-cloned repo (option include)' '
+       (
+               cd c &&
+               git svn rebase --include-paths="qqq" &&
+               printf "test_qqq\nb\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_expect_success 'SVN-side change inside of ignored www' '
+       (
+               cd s &&
+               echo zaq >> www/test_www.txt
+               svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+       )
+'
+
+test_expect_success 'update git svn-cloned repo (config include)' '
+       (
+               cd g &&
+               git svn rebase &&
+               printf "test_qqq\nb\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_expect_success 'update git svn-cloned repo (option include)' '
+       (
+               cd c &&
+               git svn rebase --include-paths="qqq" &&
+               printf "test_qqq\nb\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_expect_success 'SVN-side change in and out of included qqq' '
+       (
+               cd s &&
+               echo cvf >> www/test_www.txt
+               echo ygg >> qqq/test_qqq.txt
+               svn_cmd commit -m "SVN-side change in and out of ignored www" &&
+               svn_cmd up &&
+               svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+       )
+'
+
+test_expect_success 'update git svn-cloned repo again (config include)' '
+       (
+               cd g &&
+               git svn rebase &&
+               printf "test_qqq\nb\nygg\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_expect_success 'update git svn-cloned repo again (option include)' '
+       (
+               cd c &&
+               git svn rebase --include-paths="qqq" &&
+               printf "test_qqq\nb\nygg\n" > expect &&
+               for i in */*.txt; do cat $i >> expect2; done &&
+               test_cmp expect2 expect &&
+               rm expect expect2
+       )
+'
+
+test_done
index 6ef0c0bde368d10ca9739ecbcd157b862b00b0c8..1eab7015c7fce6fb9a41b52dc4bb1a173455a4d6 100755 (executable)
@@ -88,7 +88,6 @@ test_expect_success 'check reintegration mergeinfo' '
        test "$mergeinfo" = "/branches/svnb1:2-4,7-9,13-18
 /branches/svnb2:3,8,16-17
 /branches/svnb3:4,9
-/branches/svnb4:5-6,10-12
 /branches/svnb5:6,11"
        '
 
diff --git a/t/t9167-git-svn-cmd-branch-subproject.sh b/t/t9167-git-svn-cmd-branch-subproject.sh
new file mode 100755 (executable)
index 0000000..53def87
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# Copyright (c) 2013 Tobias Schulte
+#
+
+test_description='git svn branch for subproject clones'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize svnrepo' '
+       mkdir import &&
+       (
+               cd import &&
+               mkdir -p trunk/project branches tags &&
+               (
+                       cd trunk/project &&
+                       echo foo > foo
+               ) &&
+               svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null
+       ) &&
+       rm -rf import &&
+       svn_cmd co "$svnrepo"/trunk/project trunk/project &&
+       (
+               cd trunk/project &&
+               echo bar >> foo &&
+               svn_cmd ci -m "updated trunk"
+       ) &&
+       rm -rf trunk
+'
+
+test_expect_success 'import into git' '
+       git svn init --trunk=trunk/project --branches=branches/*/project \
+               --tags=tags/*/project "$svnrepo" &&
+       git svn fetch &&
+       git checkout remotes/trunk
+'
+
+test_expect_success 'git svn branch tests' '
+       test_must_fail git svn branch a &&
+       git svn branch --parents a &&
+       test_must_fail git svn branch -t tag1 &&
+       git svn branch --parents -t tag1 &&
+       test_must_fail git svn branch --tag tag2 &&
+       git svn branch --parents --tag tag2 &&
+       test_must_fail git svn tag tag3 &&
+       git svn tag --parents tag3
+'
+
+test_done
index 9320b4f94c25bf376a004c2c0d4451c0fd96f969..34c2d8f49ab6fd02a19e4b9f95e31838c8de3ece 100755 (executable)
@@ -146,6 +146,12 @@ test_expect_success 'signed-tags=strip' '
 
 '
 
+test_expect_success 'signed-tags=warn-strip' '
+       git fast-export --signed-tags=warn-strip sign-your-name >output 2>err &&
+       ! grep PGP output &&
+       test -s err
+'
+
 test_expect_success 'setup submodule' '
 
        git checkout -f master &&
@@ -390,7 +396,7 @@ test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'
 test_expect_success 'tag-obj_tag'     'git fast-export tag-obj_tag'
 test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
 
-test_expect_success SYMLINKS 'directory becomes symlink'        '
+test_expect_success 'directory becomes symlink'        '
        git init dirtosymlink &&
        git init result &&
        (
@@ -402,8 +408,7 @@ test_expect_success SYMLINKS 'directory becomes symlink'        '
                git add foo/world bar/world &&
                git commit -q -mone &&
                git rm -r foo &&
-               ln -s bar foo &&
-               git add foo &&
+               test_ln_s_add bar foo &&
                git commit -q -mtwo
        ) &&
        (
index 735a018eccf934ef1e0bc96b905142d4823a2814..db69af2cff72733bda515446a48b5ec8dcee23fd 100755 (executable)
@@ -330,7 +330,7 @@ test_expect_success 'validate result of edits [cvswork2]' '
 
 test_expect_success 'validate basic diffs saved during above cvswork2 edits' '
        test $(grep Index: cvsEdit1.diff | wc -l) = 1 &&
-       test ! -s cvsEdit2-empty.diff &&
+       test_must_be_empty cvsEdit2-empty.diff &&
        test $(grep Index: cvsEdit2-N.diff | wc -l) = 1 &&
        test $(grep Index: cvsEdit3.diff | wc -l) = 3 &&
        rm -rf diffSandbox &&
@@ -456,20 +456,20 @@ test_expect_success 'cvs up -r $(git rev-parse v1)' '
 
 test_expect_success 'cvs diff -r v1 -u' '
        ( cd cvswork && cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log &&
-       test ! -s cvsDiff.out &&
-       test ! -s cvs.log
+       test_must_be_empty cvsDiff.out &&
+       test_must_be_empty cvs.log
 '
 
 test_expect_success 'cvs diff -N -r v2 -u' '
        ( cd cvswork && ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log &&
-       test ! -s cvs.log &&
+       test_must_be_empty cvs.log &&
        test -s cvsDiff.out &&
        check_diff cvsDiff.out v2 v1 >check_diff.out 2>&1
 '
 
 test_expect_success 'cvs diff -N -r v2 -r v1.2' '
        ( cd cvswork && ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log &&
-       test ! -s cvs.log &&
+       test_must_be_empty cvs.log &&
        test -s cvsDiff.out &&
        check_diff cvsDiff.out v2 v1.2 >check_diff.out 2>&1
 '
@@ -488,7 +488,7 @@ test_expect_success 'apply early [cvswork3] diff to b3' '
 
 test_expect_success 'check [cvswork3] diff' '
        ( cd cvswork3 && ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log &&
-       test ! -s cvs.log &&
+       test_must_be_empty cvs.log &&
        test -s cvsDiff.out &&
        test $(grep Index: cvsDiff.out | wc -l) = 3 &&
        test_cmp cvsDiff.out cvswork3edit.diff &&
index 6783c14c1ad9af0f5aa3d16e13250e9a1a292b28..6fca19353d7308f88e63806f7187d9d379412d18 100755 (executable)
@@ -156,10 +156,10 @@ test_expect_success \
         git commit -a -m "File renamed." &&
         gitweb_run "p=.git;a=commitdiff"'
 
-test_expect_success SYMLINKS \
+test_expect_success \
        'commitdiff(0): file to symlink' \
        'rm renamed_file &&
-        ln -s file renamed_file &&
+        test_ln_s_add file renamed_file &&
         git commit -a -m "File to symlink." &&
         gitweb_run "p=.git;a=commitdiff"'
 
@@ -212,15 +212,14 @@ test_expect_success \
 # ----------------------------------------------------------------------
 # commitdiff testing (taken from t4114-apply-typechange.sh)
 
-test_expect_success SYMLINKS 'setup typechange commits' '
+test_expect_success 'setup typechange commits' '
        echo "hello world" > foo &&
        echo "hi planet" > bar &&
        git update-index --add foo bar &&
        git commit -m initial &&
        git branch initial &&
        rm -f foo &&
-       ln -s bar foo &&
-       git update-index foo &&
+       test_ln_s_add bar foo &&
        git commit -m "foo symlinked to bar" &&
        git branch foo-symlinked-to-bar &&
        rm -f foo &&
@@ -361,11 +360,7 @@ test_expect_success \
         echo "Changed" >> 04-rename-to &&
         test_chmod +x 05-mode-change &&
         rm -f 06-file-or-symlink &&
-        if test_have_prereq SYMLINKS; then
-               ln -s 01-change 06-file-or-symlink
-        else
-               printf %s 01-change > 06-file-or-symlink
-        fi &&
+        test_ln_s_add 01-change 06-file-or-symlink &&
         echo "Changed and have mode changed" > 07-change-mode-change   &&
         test_chmod +x 07-change-mode-change &&
         git commit -a -m "Large commit" &&
index ef86948d21c3e62eed14ed6cb326d5d2fd9273cb..d3a5bac754fd8a80a0df0c091fe0769f74f16a4d 100755 (executable)
@@ -130,7 +130,8 @@ test_expect_success DATE_PARSER 'modification: feed last-modified' '
 test_debug 'cat gitweb.headers'
 
 test_expect_success DATE_PARSER 'modification: feed if-modified-since (modified)' '
-       export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       export HTTP_IF_MODIFIED_SINCE &&
        test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
        gitweb_run "p=.git;a=atom;h=master" &&
        grep "Status: 200 OK" gitweb.headers
@@ -138,7 +139,8 @@ test_expect_success DATE_PARSER 'modification: feed if-modified-since (modified)
 test_debug 'cat gitweb.headers'
 
 test_expect_success DATE_PARSER 'modification: feed if-modified-since (unmodified)' '
-       export HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+       HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+       export HTTP_IF_MODIFIED_SINCE &&
        test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
        gitweb_run "p=.git;a=atom;h=master" &&
        grep "Status: 304 Not Modified" gitweb.headers
@@ -153,7 +155,8 @@ test_expect_success DATE_PARSER 'modification: snapshot last-modified' '
 test_debug 'cat gitweb.headers'
 
 test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (modified)' '
-       export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       export HTTP_IF_MODIFIED_SINCE &&
        test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
        gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
        grep "Status: 200 OK" gitweb.headers
@@ -161,7 +164,8 @@ test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (modif
 test_debug 'cat gitweb.headers'
 
 test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (unmodified)' '
-       export HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+       HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+       export HTTP_IF_MODIFIED_SINCE &&
        test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
        gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
        grep "Status: 304 Not Modified" gitweb.headers
@@ -170,7 +174,8 @@ test_debug 'cat gitweb.headers'
 
 test_expect_success DATE_PARSER 'modification: tree snapshot' '
        ID=`git rev-parse --verify HEAD^{tree}` &&
-       export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       export HTTP_IF_MODIFIED_SINCE &&
        test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
        gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
        grep "Status: 200 OK" gitweb.headers &&
index 9730821c3032c7d3251e2ce0d260df15f6c24f21..2bf142d09c784a0c2aac8d814674684a20a7c93b 100755 (executable)
@@ -469,9 +469,11 @@ test_expect_success 'use-client-spec detect-branches skips branches setup' '
                View: //depot/usecs/b1/... //depot/usecs/b3/...
                EOF
 
-               echo b3/b3-file3 >b3/b3-file3 &&
-               p4 add b3/b3-file3 &&
-               p4 submit -d "b3/b3-file3"
+               echo b3/b3-file3_1 >b3/b3-file3_1 &&
+               echo b3/b3-file3_2 >b3/b3-file3_2 &&
+               p4 add b3/b3-file3_1 &&
+               p4 add b3/b3-file3_2 &&
+               p4 submit -d "b3/b3-file3_1 b3/b3-file3_2"
        )
 '
 
@@ -487,6 +489,21 @@ test_expect_success 'use-client-spec detect-branches skips branches' '
        )
 '
 
+test_expect_success 'use-client-spec detect-branches skips files in branches' '
+       client_view "//depot/usecs/... //client/..." \
+                   "-//depot/usecs/b3/b3-file3_1 //client/b3/b3-file3_1" &&
+       test_when_finished cleanup_git &&
+       test_create_repo "$git" &&
+       (
+               cd "$git" &&
+               git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
+               git checkout -b master p4/usecs/b3 &&
+               test_path_is_file b1-file1 &&
+               test_path_is_file b3-file3_2 &&
+               test_path_is_missing b3-file3_1
+       )
+'
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index eeefa6790454d83bfb4ce740f7b1843c6a71c79c..a82744bab072114675dea66fa94f21f661aa8dbd 100755 (executable)
@@ -28,7 +28,7 @@ test_expect_success 'p4 client newlines, unix' '
                test_cmp f-unix-orig f-unix &&
 
                # make sure stored in repo as unix newlines
-               # use sed to eat python-appened newline
+               # use sed to eat python-appended newline
                p4 -G print //depot/f-unix | marshal_dump data 2 |\
                    sed \$d >f-unix-p4-print &&
                test_cmp f-unix-orig f-unix-p4-print &&
@@ -95,7 +95,7 @@ test_expect_success 'gitattributes setting eol=lf produces lf newlines' '
                git init &&
                echo "* eol=lf" >.gitattributes &&
                git p4 sync //depot@all &&
-               git checkout master &&
+               git checkout -b master p4/master &&
                test_cmp "$cli"/f-unix-orig f-unix &&
                test_cmp "$cli"/f-win-as-lf f-win
        )
@@ -109,7 +109,7 @@ test_expect_success 'gitattributes setting eol=crlf produces crlf newlines' '
                git init &&
                echo "* eol=crlf" >.gitattributes &&
                git p4 sync //depot@all &&
-               git checkout master &&
+               git checkout -b master p4/master &&
                test_cmp "$cli"/f-unix-as-crlf f-unix &&
                test_cmp "$cli"/f-win-orig f-win
        )
index 6d9d1418a041bd8b3dddb598ac5479c999047a06..2d4beb5e50c257fed2f4b6475cb5834425b5a64f 100755 (executable)
@@ -69,7 +69,7 @@ run_completion ()
        local -a COMPREPLY _words
        local _cword
        _words=( $1 )
-       test "${1: -1}" == ' ' && _words+=('')
+       test "${1: -1}" = ' ' && _words[${#_words[@]}+1]=''
        (( _cword = ${#_words[@]} - 1 ))
        __git_wrap__git_main && print_comp
 }
@@ -122,6 +122,140 @@ test_gitcomp_nl ()
 
 invalid_variable_name='${foo.bar}'
 
+actual="$TRASH_DIRECTORY/actual"
+
+test_expect_success 'setup for __gitdir tests' '
+       mkdir -p subdir/subsubdir &&
+       git init otherrepo
+'
+
+test_expect_success '__gitdir - from command line (through $__git_dir)' '
+       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+       (
+               __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - repo as argument' '
+       echo "otherrepo/.git" >expected &&
+       __gitdir "otherrepo" >"$actual" &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - remote as argument' '
+       echo "remote" >expected &&
+       __gitdir "remote" >"$actual" &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in cwd' '
+       echo ".git" >expected &&
+       __gitdir >"$actual" &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in parent' '
+       echo "$(pwd -P)/.git" >expected &&
+       (
+               cd subdir/subsubdir &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - cwd is a .git directory' '
+       echo "." >expected &&
+       (
+               cd .git &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - parent is a .git directory' '
+       echo "$(pwd -P)/.git" >expected &&
+       (
+               cd .git/refs/heads &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' '
+       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+       (
+               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+               export GIT_DIR &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
+       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+       (
+               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+               export GIT_DIR &&
+               cd subdir &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - non-existing $GIT_DIR' '
+       (
+               GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
+               export GIT_DIR &&
+               test_must_fail __gitdir
+       )
+'
+
+test_expect_success '__gitdir - gitfile in cwd' '
+       echo "$(pwd -P)/otherrepo/.git" >expected &&
+       echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git &&
+       test_when_finished "rm -f subdir/.git" &&
+       (
+               cd subdir &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - gitfile in parent' '
+       echo "$(pwd -P)/otherrepo/.git" >expected &&
+       echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git &&
+       test_when_finished "rm -f subdir/.git" &&
+       (
+               cd subdir/subsubdir &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' '
+       echo "$(pwd -P)/otherrepo/.git" >expected &&
+       mkdir otherrepo/dir &&
+       test_when_finished "rm -rf otherrepo/dir" &&
+       ln -s otherrepo/dir link &&
+       test_when_finished "rm -f link" &&
+       (
+               cd link &&
+               __gitdir >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - not a git repository' '
+       (
+               cd subdir/subsubdir &&
+               GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
+               export GIT_CEILING_DIRECTORIES &&
+               test_must_fail __gitdir
+       )
+'
+
 test_expect_success '__gitcomp - trailing space - options' '
        test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message=
                --reset-author" <<-EOF
@@ -231,6 +365,7 @@ test_expect_success 'double dash "git" itself' '
        --exec-path Z
        --exec-path=
        --html-path Z
+       --man-path Z
        --info-path Z
        --work-tree=
        --namespace=
@@ -347,4 +482,81 @@ test_expect_success 'send-email' '
        test_completion "git send-email ma" "master "
 '
 
+test_expect_success 'complete files' '
+       git init tmp && cd tmp &&
+       test_when_finished "cd .. && rm -rf tmp" &&
+
+       echo "expected" > .gitignore &&
+       echo "out" >> .gitignore &&
+
+       git add .gitignore &&
+       test_completion "git commit " ".gitignore" &&
+
+       git commit -m ignore &&
+
+       touch new &&
+       test_completion "git add " "new" &&
+
+       git add new &&
+       git commit -a -m new &&
+       test_completion "git add " "" &&
+
+       git mv new modified &&
+       echo modify > modified &&
+       test_completion "git add " "modified" &&
+
+       touch untracked &&
+
+       : TODO .gitignore should not be here &&
+       test_completion "git rm " <<-\EOF &&
+       .gitignore
+       modified
+       EOF
+
+       test_completion "git clean " "untracked" &&
+
+       : TODO .gitignore should not be here &&
+       test_completion "git mv " <<-\EOF &&
+       .gitignore
+       modified
+       EOF
+
+       mkdir dir &&
+       touch dir/file-in-dir &&
+       git add dir/file-in-dir &&
+       git commit -m dir &&
+
+       mkdir untracked-dir &&
+
+       : TODO .gitignore should not be here &&
+       test_completion "git mv modified " <<-\EOF &&
+       .gitignore
+       dir
+       modified
+       untracked
+       untracked-dir
+       EOF
+
+       test_completion "git commit " "modified" &&
+
+       : TODO .gitignore should not be here &&
+       test_completion "git ls-files " <<-\EOF
+       .gitignore
+       dir
+       modified
+       EOF
+
+       touch momified &&
+       test_completion "git add mom" "momified"
+'
+
+test_expect_failure 'complete with tilde expansion' '
+       git init tmp && cd tmp &&
+       test_when_finished "cd .. && rm -rf tmp" &&
+
+       touch ~/tmp/file &&
+
+       test_completion "git add ~/tmp/" "~/tmp/file"
+'
+
 test_done
index e147a8d277af7948cf165ea93899e9020a961d7a..3c3e4e8c38023595978368e0b1665e122e0bdfab 100755 (executable)
@@ -10,522 +10,558 @@ test_description='test git-specific bash prompt functions'
 . "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
 
 actual="$TRASH_DIRECTORY/actual"
+c_red='\\[\\e[31m\\]'
+c_green='\\[\\e[32m\\]'
+c_lblue='\\[\\e[1;34m\\]'
+c_clear='\\[\\e[0m\\]'
 
 test_expect_success 'setup for prompt tests' '
-       mkdir -p subdir/subsubdir &&
        git init otherrepo &&
-       echo 1 > file &&
+       echo 1 >file &&
        git add file &&
        test_tick &&
        git commit -m initial &&
        git tag -a -m msg1 t1 &&
        git checkout -b b1 &&
-       echo 2 > file &&
+       echo 2 >file &&
        git commit -m "second b1" file &&
-       echo 3 > file &&
+       echo 3 >file &&
        git commit -m "third b1" file &&
        git tag -a -m msg2 t2 &&
        git checkout -b b2 master &&
-       echo 0 > file &&
+       echo 0 >file &&
        git commit -m "second b2" file &&
+       echo 00 >file &&
+       git commit -m "another b2" file &&
+       echo 000 >file &&
+       git commit -m "yet another b2" file &&
        git checkout master
 '
 
-test_expect_success 'gitdir - from command line (through $__git_dir)' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-       (
-               __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - repo as argument' '
-       echo "otherrepo/.git" > expected &&
-       __gitdir "otherrepo" > "$actual" &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - remote as argument' '
-       echo "remote" > expected &&
-       __gitdir "remote" > "$actual" &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - .git directory in cwd' '
-       echo ".git" > expected &&
-       __gitdir > "$actual" &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - .git directory in parent' '
-       echo "$(pwd -P)/.git" > expected &&
-       (
-               cd subdir/subsubdir &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - cwd is a .git directory' '
-       echo "." > expected &&
-       (
-               cd .git &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - parent is a .git directory' '
-       echo "$(pwd -P)/.git" > expected &&
-       (
-               cd .git/refs/heads &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - $GIT_DIR set while .git directory in cwd' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-       (
-               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
-               export GIT_DIR &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - $GIT_DIR set while .git directory in parent' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
-       (
-               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
-               export GIT_DIR &&
-               cd subdir &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - non-existing $GIT_DIR' '
-       (
-               GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
-               export GIT_DIR &&
-               test_must_fail __gitdir
-       )
-'
-
-test_expect_success 'gitdir - gitfile in cwd' '
-       echo "$(pwd -P)/otherrepo/.git" > expected &&
-       echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
-       test_when_finished "rm -f subdir/.git" &&
-       (
-               cd subdir &&
-               __gitdir > "$actual"
-       ) &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - gitfile in parent' '
-       echo "$(pwd -P)/otherrepo/.git" > expected &&
-       echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
-       test_when_finished "rm -f subdir/.git" &&
-       (
-               cd subdir/subsubdir &&
-               __gitdir > "$actual"
-       ) &&
+test_expect_success 'prompt - branch name' '
+       printf " (master)" >expected &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
-test_expect_success SYMLINKS 'gitdir - resulting path avoids symlinks' '
-       echo "$(pwd -P)/otherrepo/.git" > expected &&
-       mkdir otherrepo/dir &&
-       test_when_finished "rm -rf otherrepo/dir" &&
-       ln -s otherrepo/dir link &&
-       test_when_finished "rm -f link" &&
-       (
-               cd link &&
-               __gitdir > "$actual"
-       ) &&
+test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
+       printf " (master)" >expected &&
+       test_when_finished "git checkout master" &&
+       test_config core.preferSymlinkRefs true &&
+       git checkout master &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
-test_expect_success 'gitdir - not a git repository' '
-       (
-               cd subdir/subsubdir &&
-               GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
-               export GIT_CEILING_DIRECTORIES &&
-               test_must_fail __gitdir
-       )
-'
-
-test_expect_success 'prompt - branch name' '
-       printf " (master)" > expected &&
-       __git_ps1 > "$actual" &&
+test_expect_success 'prompt - unborn branch' '
+       printf " (unborn)" >expected &&
+       git checkout --orphan unborn &&
+       test_when_finished "git checkout master" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - detached head' '
-       printf " ((%s...))" $(git log -1 --format="%h" b1^) > expected &&
+       printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+       test_config core.abbrev 13 &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - contains' '
-       printf " ((t2~1))" > expected &&
+       printf " ((t2~1))" >expected &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
        (
                GIT_PS1_DESCRIBE_STYLE=contains &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - branch' '
-       printf " ((b1~1))" > expected &&
+       printf " ((b1~1))" >expected &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
        (
                GIT_PS1_DESCRIBE_STYLE=branch &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - describe' '
-       printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) > expected &&
+       printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
        git checkout b1^ &&
        test_when_finished "git checkout master" &&
        (
                GIT_PS1_DESCRIBE_STYLE=describe &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - describe detached head - default' '
-       printf " ((t2))" > expected &&
+       printf " ((t2))" >expected &&
        git checkout --detach b1 &&
        test_when_finished "git checkout master" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
+       printf " (GIT_DIR!)" >expected &&
        (
                cd .git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - deep inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
+       printf " (GIT_DIR!)" >expected &&
        (
                cd .git/refs/heads &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - inside bare repository' '
-       printf " (BARE:master)" > expected &&
+       printf " (BARE:master)" >expected &&
        git init --bare bare.git &&
        test_when_finished "rm -rf bare.git" &&
        (
                cd bare.git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - interactive rebase' '
-       printf " (b1|REBASE-i)" > expected
-       echo "#!$SHELL_PATH" >fake_editor.sh &&
-       cat >>fake_editor.sh <<\EOF &&
-echo "edit $(git log -1 --format="%h")" > "$1"
-EOF
+       printf " (b1|REBASE-i 2/3)" >expected
+       write_script fake_editor.sh <<-\EOF &&
+               echo "exec echo" >"$1"
+               echo "edit $(git log -1 --format="%h")" >>"$1"
+               echo "exec echo" >>"$1"
+       EOF
        test_when_finished "rm -f fake_editor.sh" &&
-       chmod a+x fake_editor.sh &&
        test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
        git checkout b1 &&
        test_when_finished "git checkout master" &&
        git rebase -i HEAD^ &&
        test_when_finished "git rebase --abort"
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - rebase merge' '
-       printf " (b2|REBASE-m)" > expected &&
+       printf " (b2|REBASE-m 1/3)" >expected &&
        git checkout b2 &&
        test_when_finished "git checkout master" &&
        test_must_fail git rebase --merge b1 b2 &&
        test_when_finished "git rebase --abort" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - rebase' '
-       printf " ((t2)|REBASE)" > expected &&
+       printf " (b2|REBASE 1/3)" >expected &&
        git checkout b2 &&
        test_when_finished "git checkout master" &&
        test_must_fail git rebase b1 b2 &&
        test_when_finished "git rebase --abort" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - merge' '
-       printf " (b1|MERGING)" > expected &&
+       printf " (b1|MERGING)" >expected &&
        git checkout b1 &&
        test_when_finished "git checkout master" &&
        test_must_fail git merge b2 &&
        test_when_finished "git reset --hard" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - cherry-pick' '
-       printf " (master|CHERRY-PICKING)" > expected &&
+       printf " (master|CHERRY-PICKING)" >expected &&
        test_must_fail git cherry-pick b1 &&
        test_when_finished "git reset --hard" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - bisect' '
-       printf " (master|BISECTING)" > expected &&
+       printf " (master|BISECTING)" >expected &&
        git bisect start &&
        test_when_finished "git bisect reset" &&
-       __git_ps1 > "$actual" &&
+       __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - clean' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty worktree' '
-       printf " (master *)" > expected &&
-       echo "dirty" > file &&
+       printf " (master *)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty index' '
-       printf " (master +)" > expected &&
-       echo "dirty" > file &&
+       printf " (master +)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        git add -u &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
-       printf " (master *+)" > expected &&
-       echo "dirty index" > file &&
+       printf " (master *+)" >expected &&
+       echo "dirty index" >file &&
        test_when_finished "git reset --hard" &&
        git add -u &&
-       echo "dirty worktree" > file &&
+       echo "dirty worktree" >file &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - before root commit' '
-       printf " (master #)" > expected &&
+       printf " (master #)" >expected &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
                cd otherrepo &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' '
-       printf " (master)" > expected &&
-       echo "dirty" > file &&
+       printf " (master)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        test_config bash.showDirtyState false &&
        (
                sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' '
-       printf " (master)" > expected &&
-       echo "dirty" > file &&
+       printf " (master)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        test_config bash.showDirtyState true &&
        (
                sane_unset GIT_PS1_SHOWDIRTYSTATE &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' '
-       printf " (master)" > expected &&
-       echo "dirty" > file &&
+       printf " (master)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        test_config bash.showDirtyState false &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' '
-       printf " (master *)" > expected &&
-       echo "dirty" > file &&
+       printf " (master *)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        test_config bash.showDirtyState true &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
-       echo "dirty" > file &&
+       printf " (GIT_DIR!)" >expected &&
+       echo "dirty" >file &&
        test_when_finished "git reset --hard" &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
                cd .git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - no stash' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        (
                GIT_PS1_SHOWSTASHSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - stash' '
-       printf " (master $)" > expected &&
+       printf " (master $)" >expected &&
        echo 2 >file &&
        git stash &&
        test_when_finished "git stash drop" &&
+       git pack-refs --all &&
        (
                GIT_PS1_SHOWSTASHSTATE=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - stash status indicator - not shown inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
+       printf " (GIT_DIR!)" >expected &&
        echo 2 >file &&
        git stash &&
        test_when_finished "git stash drop" &&
        (
                GIT_PS1_SHOWSTASHSTATE=y &&
                cd .git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - no untracked files' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
                cd otherrepo &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - untracked files' '
-       printf " (master %%)" > expected &&
+       printf " (master %%)" >expected &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        test_config bash.showUntrackedFiles false &&
        (
                sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        test_config bash.showUntrackedFiles true &&
        (
                sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' '
-       printf " (master)" > expected &&
+       printf " (master)" >expected &&
        test_config bash.showUntrackedFiles false &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' '
-       printf " (master %%)" > expected &&
+       printf " (master %%)" >expected &&
        test_config bash.showUntrackedFiles true &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' '
-       printf " (GIT_DIR!)" > expected &&
+       printf " (GIT_DIR!)" >expected &&
        (
                GIT_PS1_SHOWUNTRACKEDFILES=y &&
                cd .git &&
-               __git_ps1 > "$actual"
+               __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
 test_expect_success 'prompt - format string starting with dash' '
-       printf -- "-master" > expected &&
-       __git_ps1 "-%s" > "$actual" &&
+       printf -- "-master" >expected &&
+       __git_ps1 "-%s" >"$actual" &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - pc mode' '
+       printf "BEFORE: (master):AFTER" >expected &&
+       printf "" >expected_output &&
+       (
+               __git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+               test_cmp expected_output "$actual" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - branch name' '
+       printf "BEFORE: (${c_green}master${c_clear}):AFTER" >expected &&
+       (
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - detached head' '
+       printf "BEFORE: (${c_red}(%s...)${c_clear}):AFTER" $(git log -1 --format="%h" b1^) >expected &&
+       git checkout b1^ &&
+       test_when_finished "git checkout master" &&
+       (
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_clear}):AFTER" >expected &&
+       echo "dirty" >file &&
+       test_when_finished "git reset --hard" &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_green}+${c_clear}):AFTER" >expected &&
+       echo "dirty" >file &&
+       test_when_finished "git reset --hard" &&
+       git add -u &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER" >expected &&
+       echo "dirty index" >file &&
+       test_when_finished "git reset --hard" &&
+       git add -u &&
+       echo "dirty worktree" >file &&
+       (
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_green}#${c_clear}):AFTER" >expected &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               cd otherrepo &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+       printf "BEFORE: (${c_green}GIT_DIR!${c_clear}):AFTER" >expected &&
+       echo "dirty" >file &&
+       test_when_finished "git reset --hard" &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               cd .git &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_lblue}\$${c_clear}):AFTER" >expected &&
+       echo 2 >file &&
+       git stash &&
+       test_when_finished "git stash drop" &&
+       (
+               GIT_PS1_SHOWSTASHSTATE=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+       printf "BEFORE: (${c_green}master${c_clear} ${c_red}%%${c_clear}):AFTER" >expected &&
+       (
+               GIT_PS1_SHOWUNTRACKEDFILES=y &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" &&
+               printf "%s" "$PS1" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - zsh color pc mode' '
+       printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
+       (
+               ZSH_VERSION=5.0.0 &&
+               GIT_PS1_SHOWCOLORHINTS=y &&
+               __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+               printf "%s" "$PS1" >"$actual"
+       ) &&
        test_cmp expected "$actual"
 '
 
index 52510094add59b508e1581ffebfa555e7249561c..a7e9aacbb2d9b05b0d5083a0fd70cb9287fcf454 100644 (file)
@@ -343,6 +343,7 @@ test_declared_prereq () {
 }
 
 test_expect_failure () {
+       test_start_
        test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
@@ -357,10 +358,11 @@ test_expect_failure () {
                        test_known_broken_failure_ "$1"
                fi
        fi
-       echo >&3 ""
+       test_finish_
 }
 
 test_expect_success () {
+       test_start_
        test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
        test "$#" = 2 ||
        error "bug in the test script: not 2 or 3 parameters to test-expect-success"
@@ -375,7 +377,7 @@ test_expect_success () {
                        test_failure_ "$@"
                fi
        fi
-       echo >&3 ""
+       test_finish_
 }
 
 # test_external runs external test scripts that provide continuous
@@ -609,6 +611,18 @@ test_cmp() {
        $GIT_TEST_CMP "$@"
 }
 
+# Check if the file expected to be empty is indeed empty, and barfs
+# otherwise.
+
+test_must_be_empty () {
+       if test -s "$1"
+       then
+               echo "'$1' is not empty, it contains:"
+               cat "$1"
+               return 1
+       fi
+}
+
 # Tests that its two parameters refer to the same revision
 test_cmp_rev () {
        git rev-parse --verify "$1" >expect.rev &&
@@ -679,3 +693,20 @@ test_create_repo () {
                mv .git/hooks .git/hooks-disabled
        ) || exit
 }
+
+# This function helps on symlink challenged file systems when it is not
+# important that the file system entry is a symbolic link.
+# Use test_ln_s_add instead of "ln -s x y && git add y" to add a
+# symbolic link entry y to the index.
+
+test_ln_s_add () {
+       if test_have_prereq SYMLINKS
+       then
+               ln -s "$1" "$2" &&
+               git update-index --add "$2"
+       else
+               printf '%s' "$1" >"$2" &&
+               ln_s_obj=$(git hash-object -w "$2") &&
+               git update-index --add --cacheinfo 120000 $ln_s_obj "$2"
+       fi
+}
index ca6bdef63d2ee9389729e5118f22461c887dc5ed..1aa27bdbbf39bd937a6bb66a7eafeda16bb76246 100644 (file)
@@ -54,8 +54,8 @@ done,*)
        # do not redirect again
        ;;
 *' --tee '*|*' --va'*)
-       mkdir -p test-results
-       BASE=test-results/$(basename "$0" .sh)
+       mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results"
+       BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)"
        (GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1;
         echo $? > $BASE.exit) | tee $BASE.out
        test "$(cat $BASE.exit)" = 0
@@ -92,6 +92,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
        print join("\n", @vars);
 ')
 unset XDG_CONFIG_HOME
+unset GITPERLLIB
 GIT_AUTHOR_EMAIL=author@example.com
 GIT_AUTHOR_NAME='A U Thor'
 GIT_COMMITTER_EMAIL=committer@example.com
@@ -184,6 +185,9 @@ do
                help=t; shift ;;
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
                verbose=t; shift ;;
+       --verbose-only=*)
+               verbose_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        -q|--q|--qu|--qui|--quie|--quiet)
                # Ignore --quiet under a TAP::Harness. Saying how many tests
                # passed without the ok/not ok details is always an error.
@@ -198,17 +202,39 @@ do
        --valgrind=*)
                valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)')
                shift ;;
+       --valgrind-only=*)
+               valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-parallel=*)
+               valgrind_parallel=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-only-stride=*)
+               valgrind_only_stride=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       --valgrind-only-offset=*)
+               valgrind_only_offset=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        --tee)
                shift ;; # was handled already
        --root=*)
                root=$(expr "z$1" : 'z[^=]*=\(.*\)')
                shift ;;
+       --statusprefix=*)
+               statusprefix=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        *)
                echo "error: unknown test option '$1'" >&2; exit 1 ;;
        esac
 done
 
-test -n "$valgrind" && verbose=t
+if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+then
+       test -z "$valgrind" && valgrind=memcheck
+       test -z "$verbose" && verbose_only="$valgrind_only"
+elif test -n "$valgrind"
+then
+       verbose=t
+fi
 
 if test -n "$color"
 then
@@ -303,12 +329,12 @@ trap 'die' EXIT
 
 test_ok_ () {
        test_success=$(($test_success + 1))
-       say_color "" "ok $test_count - $@"
+       say_color "" "${statusprefix}ok $test_count - $@"
 }
 
 test_failure_ () {
        test_failure=$(($test_failure + 1))
-       say_color error "not ok $test_count - $1"
+       say_color error "${statusprefix}not ok $test_count - $1"
        shift
        echo "$@" | sed -e 's/^/#       /'
        test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
@@ -316,18 +342,83 @@ test_failure_ () {
 
 test_known_broken_ok_ () {
        test_fixed=$(($test_fixed+1))
-       say_color error "ok $test_count - $@ # TODO known breakage vanished"
+       say_color error "${statusprefix}ok $test_count - $@ # TODO known breakage vanished"
 }
 
 test_known_broken_failure_ () {
        test_broken=$(($test_broken+1))
-       say_color warn "not ok $test_count - $@ # TODO known breakage"
+       say_color warn "${statusprefix}not ok $test_count - $@ # TODO known breakage"
 }
 
 test_debug () {
        test "$debug" = "" || eval "$1"
 }
 
+match_pattern_list () {
+       arg="$1"
+       shift
+       test -z "$*" && return 1
+       for pattern_
+       do
+               case "$arg" in
+               $pattern_)
+                       return 0
+               esac
+       done
+       return 1
+}
+
+maybe_teardown_verbose () {
+       test -z "$verbose_only" && return
+       exec 4>/dev/null 3>/dev/null
+       verbose=
+}
+
+last_verbose=t
+maybe_setup_verbose () {
+       test -z "$verbose_only" && return
+       if match_pattern_list $test_count $verbose_only ||
+               { test -n "$valgrind_only_stride" &&
+               expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null; }
+       then
+               exec 4>&2 3>&1
+               # Emit a delimiting blank line when going from
+               # non-verbose to verbose.  Within verbose mode the
+               # delimiter is printed by test_expect_*.  The choice
+               # of the initial $last_verbose is such that before
+               # test 1, we do not print it.
+               test -z "$last_verbose" && echo >&3 ""
+               verbose=t
+       else
+               exec 4>/dev/null 3>/dev/null
+               verbose=
+       fi
+       last_verbose=$verbose
+}
+
+maybe_teardown_valgrind () {
+       test -z "$GIT_VALGRIND" && return
+       GIT_VALGRIND_ENABLED=
+}
+
+maybe_setup_valgrind () {
+       test -z "$GIT_VALGRIND" && return
+       if test -z "$valgrind_only" && test -z "$valgrind_only_stride"
+       then
+               GIT_VALGRIND_ENABLED=t
+               return
+       fi
+       GIT_VALGRIND_ENABLED=
+       if match_pattern_list $test_count $valgrind_only
+       then
+               GIT_VALGRIND_ENABLED=t
+       elif test -n "$valgrind_only_stride" &&
+               expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null
+       then
+               GIT_VALGRIND_ENABLED=t
+       fi
+}
+
 test_eval_ () {
        # This is a separate function because some tests use
        # "return" to end a test_expect_success block early.
@@ -337,8 +428,10 @@ test_eval_ () {
 test_run_ () {
        test_cleanup=:
        expecting_failure=$2
+       setup_malloc_check
        test_eval_ "$1"
        eval_ret=$?
+       teardown_malloc_check
 
        if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
        then
@@ -353,17 +446,24 @@ test_run_ () {
        return "$eval_ret"
 }
 
-test_skip () {
+test_start_ () {
        test_count=$(($test_count+1))
+       maybe_setup_verbose
+       maybe_setup_valgrind
+}
+
+test_finish_ () {
+       echo >&3 ""
+       maybe_teardown_valgrind
+       maybe_teardown_verbose
+}
+
+test_skip () {
        to_skip=
-       for skp in $GIT_SKIP_TESTS
-       do
-               case $this_test.$test_count in
-               $skp)
-                       to_skip=t
-                       break
-               esac
-       done
+       if match_pattern_list $this_test.$test_count $GIT_SKIP_TESTS
+       then
+               to_skip=t
+       fi
        if test -z "$to_skip" && test -n "$test_prereq" &&
           ! test_have_prereq "$test_prereq"
        then
@@ -377,8 +477,8 @@ test_skip () {
                        of_prereq=" of $test_prereq"
                fi
 
-               say_color skip >&3 "skipping test: $@"
-               say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
+               say_color skip >&3 "${statusprefix}skipping test: $@"
+               say_color skip "${statusprefix}ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
                : true
                ;;
        *)
@@ -395,6 +495,8 @@ test_at_end_hook_ () {
 test_done () {
        GIT_EXIT_OK=t
 
+       # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts
+       # output file
        if test -z "$HARNESS_ACTIVE"
        then
                test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
@@ -414,11 +516,11 @@ test_done () {
 
        if test "$test_fixed" != 0
        then
-               say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
+               say_color error "${statusprefix}# $test_fixed known breakage(s) vanished; please update test(s)"
        fi
        if test "$test_broken" != 0
        then
-               say_color warn "# still have $test_broken known breakage(s)"
+               say_color warn "${statusprefix}# still have $test_broken known breakage(s)"
        fi
        if test "$test_broken" != 0 || test "$test_fixed" != 0
        then
@@ -441,9 +543,9 @@ test_done () {
                then
                        if test $test_remaining -gt 0
                        then
-                               say_color pass "# passed all $msg"
+                               say_color pass "${statusprefix}# passed all $msg"
                        fi
-                       say "1..$test_count$skip_all"
+                       say "${statusprefix}1..$test_count$skip_all"
                fi
 
                test -d "$remove_trash" &&
@@ -457,8 +559,8 @@ test_done () {
        *)
                if test $test_external_has_tap -eq 0
                then
-                       say_color error "# failed $test_failure among $msg"
-                       say "1..$test_count"
+                       say_color error "${statusprefix}# failed $test_failure among $msg"
+                       say "${statusprefix}1..$test_count"
                fi
 
                exit 1 ;;
@@ -466,6 +568,9 @@ test_done () {
        esac
 }
 
+
+# Set up a directory that we can put in PATH which redirects all git
+# calls to 'valgrind git ...'.
 if test -n "$valgrind"
 then
        make_symlink () {
@@ -513,31 +618,43 @@ then
                make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
        }
 
-       # override all git executables in TEST_DIRECTORY/..
-       GIT_VALGRIND=$TEST_DIRECTORY/valgrind
-       mkdir -p "$GIT_VALGRIND"/bin
-       for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
-       do
-               make_valgrind_symlink $file
-       done
-       # special-case the mergetools loadables
-       make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
-       OLDIFS=$IFS
-       IFS=:
-       for path in $PATH
-       do
-               ls "$path"/git-* 2> /dev/null |
-               while read file
+       # In the case of --valgrind-parallel, we only need to do the
+       # wrapping once, in the main script.  The worker children all
+       # have $valgrind_only_stride set, so we can skip based on that.
+       if test -z "$valgrind_only_stride"
+       then
+               # override all git executables in TEST_DIRECTORY/..
+               GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+               mkdir -p "$GIT_VALGRIND"/bin
+               for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
                do
-                       make_valgrind_symlink "$file"
+                       make_valgrind_symlink $file
                done
-       done
-       IFS=$OLDIFS
+               # special-case the mergetools loadables
+               make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
+               OLDIFS=$IFS
+               IFS=:
+               for path in $PATH
+               do
+                       ls "$path"/git-* 2> /dev/null |
+                       while read file
+                       do
+                               make_valgrind_symlink "$file"
+                       done
+               done
+               IFS=$OLDIFS
+       fi
        PATH=$GIT_VALGRIND/bin:$PATH
        GIT_EXEC_PATH=$GIT_VALGRIND/bin
        export GIT_VALGRIND
        GIT_VALGRIND_MODE="$valgrind"
        export GIT_VALGRIND_MODE
+       GIT_VALGRIND_ENABLED=t
+       if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+       then
+               GIT_VALGRIND_ENABLED=
+       fi
+       export GIT_VALGRIND_ENABLED
 elif test -n "$GIT_TEST_INSTALLED"
 then
        GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
@@ -622,21 +739,53 @@ then
 else
        mkdir -p "$TRASH_DIRECTORY"
 fi
+
+# Gross hack to spawn N sub-instances of the tests in parallel, and
+# summarize the results.  Note that if this is enabled, the script
+# terminates at the end of this 'if' block.
+if test -n "$valgrind_parallel"
+then
+       for i in $(test_seq 1 $valgrind_parallel)
+       do
+               root="$TRASH_DIRECTORY/vgparallel-$i"
+               mkdir "$root"
+               TEST_OUTPUT_DIRECTORY="$root" \
+                       ${SHELL_PATH} "$0" \
+                       --root="$root" --statusprefix="[$i] " \
+                       --valgrind="$valgrind" \
+                       --valgrind-only-stride="$valgrind_parallel" \
+                       --valgrind-only-offset="$i" &
+               pids="$pids $!"
+       done
+       trap "kill $pids" INT TERM HUP
+       wait $pids
+       trap - INT TERM HUP
+       for i in $(test_seq 1 $valgrind_parallel)
+       do
+               root="$TRASH_DIRECTORY/vgparallel-$i"
+               eval "$(cat "$root/test-results/$(basename "$0" .sh)"-*.counts |
+                       sed 's/^\([a-z][a-z]*\) \([0-9][0-9]*\)/inner_\1=\2/')"
+               test_count=$(expr $test_count + $inner_total)
+               test_success=$(expr $test_success + $inner_success)
+               test_fixed=$(expr $test_fixed + $inner_fixed)
+               test_broken=$(expr $test_broken + $inner_broken)
+               test_failure=$(expr $test_failure + $inner_failed)
+       done
+       test_done
+fi
+
 # Use -P to resolve symlinks in our working directory so that the cwd
 # in subprocesses like git equals our $PWD (for pathname comparisons).
 cd -P "$TRASH_DIRECTORY" || exit 1
 
 this_test=${0##*/}
 this_test=${this_test%%-*}
-for skp in $GIT_SKIP_TESTS
-do
-       case "$this_test" in
-       $skp)
-               say_color info >&3 "skipping test $this_test altogether"
-               skip_all="skip all tests in $this_test"
-               test_done
-       esac
-done
+if match_pattern_list "$this_test" $GIT_SKIP_TESTS
+then
+       say_color info >&3 "skipping test $this_test altogether"
+       skip_all="skip all tests in $this_test"
+       test_done
+fi
 
 # Provide an implementation of the 'yes' utility
 yes () {
@@ -676,6 +825,7 @@ case $(uname -s) in
        test_set_prereq MINGW
        test_set_prereq NOT_CYGWIN
        test_set_prereq SED_STRIPS_CR
+       test_set_prereq GREP_STRIPS_CR
        ;;
 *CYGWIN*)
        test_set_prereq POSIXPERM
@@ -683,6 +833,7 @@ case $(uname -s) in
        test_set_prereq NOT_MINGW
        test_set_prereq CYGWIN
        test_set_prereq SED_STRIPS_CR
+       test_set_prereq GREP_STRIPS_CR
        ;;
 *)
        test_set_prereq POSIXPERM
@@ -737,7 +888,14 @@ test_i18ngrep () {
 
 test_lazy_prereq PIPE '
        # test whether the filesystem supports FIFOs
-       rm -f testfifo && mkfifo testfifo
+       case $(uname -s) in
+       CYGWIN*)
+               false
+               ;;
+       *)
+               rm -f testfifo && mkfifo testfifo
+               ;;
+       esac
 '
 
 test_lazy_prereq SYMLINKS '
index d8105d9fabc3d0b0cb818d8c5504832f63c96b3e..2ffc80f72105b21efcd996002d1647b47f624f94 100755 (executable)
@@ -1,6 +1,10 @@
 #!/bin/sh
 
-out_prefix=$(dirname "$0")/../test-results/valgrind.out
+# Get TEST_OUTPUT_DIRECTORY from GIT-BUILD-OPTIONS if it's there...
+. "$(dirname "$0")/../../GIT-BUILD-OPTIONS"
+# ... otherwise set it to the default value.
+: ${TEST_OUTPUT_DIRECTORY=$(dirname "$0")/..}
+
 output=
 count=0
 total_count=0
@@ -115,7 +119,7 @@ handle_one () {
        finish_output
 }
 
-for test_script in "$(dirname "$0")"/../test-results/*.out
+for test_script in "$TEST_OUTPUT_DIRECTORY"/test-results/*.out
 do
        handle_one $test_script
 done
index 6b87c91b60cde4b19d6644227548f1ba6b226545..42153036dcfdee66e54fa4bd777ad3c36233d640 100755 (executable)
@@ -4,6 +4,9 @@ base=$(basename "$0")
 
 TOOL_OPTIONS='--leak-check=no'
 
+test -z "$GIT_VALGRIND_ENABLED" &&
+exec "$GIT_VALGRIND"/../../"$base" "$@"
+
 case "$GIT_VALGRIND_MODE" in
 memcheck-fast)
        ;;
index 18c48297652174ffae65b877dd131711a5746181..586e3bf94da1a2d69a061fe98aa1604f9223280b 100755 (executable)
@@ -15,13 +15,13 @@ else
        against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
 fi
 
-# If you want to allow non-ascii filenames set this variable to true.
+# If you want to allow non-ASCII filenames set this variable to true.
 allownonascii=$(git config hooks.allownonascii)
 
 # Redirect output to stderr.
 exec 1>&2
 
-# Cross platform projects tend to avoid non-ascii filenames; prevent
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
 # them from being added to the repository. We exploit the fact that the
 # printable range starts at the space character and ends with tilde.
 if [ "$allownonascii" != "true" ] &&
@@ -31,18 +31,17 @@ if [ "$allownonascii" != "true" ] &&
        test $(git diff --cached --name-only --diff-filter=A -z $against |
          LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
 then
-       echo "Error: Attempt to add a non-ascii file name."
-       echo
-       echo "This can cause problems if you want to work"
-       echo "with people on other platforms."
-       echo
-       echo "To be portable it is advisable to rename the file ..."
-       echo
-       echo "If you know what you are doing you can disable this"
-       echo "check using:"
-       echo
-       echo "  git config hooks.allownonascii true"
-       echo
+       cat <<\EOF
+Error: Attempt to add a non-ASCII file name.
+
+This can cause problems if you want to work with people on other platforms.
+
+To be portable it is advisable to rename the file.
+
+If you know what you are doing you can disable this check using:
+
+  git config hooks.allownonascii true
+EOF
        exit 1
 fi
 
old mode 100644 (file)
new mode 100755 (executable)
index 15ab6d8..1f3bceb
@@ -30,6 +30,7 @@ do
        if [ "$local_sha" = $z40 ]
        then
                # Handle delete
+               :
        else
                if [ "$remote_sha" = $z40 ]
                then
index 02b42badd550a4b775aab084b38a63c2f4f561a9..0df748784bb7f6b6f286dd37ada293df17cffeee 100644 (file)
@@ -56,7 +56,7 @@ static int timespec_arg(const char *arg, long int *set_time, int *set_eq)
        return 1;
 }
 
-int main(int argc, const char *argv[])
+int main(int argc, char *argv[])
 {
        static int verbose;
 
@@ -84,15 +84,15 @@ int main(int argc, const char *argv[])
                if (stat(argv[i], &sb) < 0) {
                        fprintf(stderr, "Failed to stat %s: %s\n",
                                argv[i], strerror(errno));
-                       return -1;
+                       return 1;
                }
 
-#ifdef WIN32
+#ifdef GIT_WINDOWS_NATIVE
                if (!(sb.st_mode & S_IWUSR) &&
                                chmod(argv[i], sb.st_mode | S_IWUSR)) {
                        fprintf(stderr, "Could not make user-writable %s: %s",
                                argv[i], strerror(errno));
-                       return -1;
+                       return 1;
                }
 #endif
 
@@ -107,7 +107,7 @@ int main(int argc, const char *argv[])
                if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
                        fprintf(stderr, "Failed to modify time on %s: %s\n",
                                argv[i], strerror(errno));
-                       return -1;
+                       return 1;
                }
        }
 
@@ -115,5 +115,5 @@ int main(int argc, const char *argv[])
 
 usage:
        fprintf(stderr, "usage: %s %s\n", argv[0], usage_str);
-       return -1;
+       return 1;
 }
index a6ffdf39d58bcb2b4469080e828dc8a5b0fbc675..47eab9765f5cd172ca630b2a632a92d123354442 100644 (file)
@@ -59,6 +59,8 @@ int main(int ac, char **av)
        struct cache_tree *another = cache_tree();
        if (read_cache() < 0)
                die("unable to read index file");
-       cache_tree_update(another, active_cache, active_nr, WRITE_TREE_DRY_RUN);
+       cache_tree_update(another,
+                         (const struct cache_entry * const *)active_cache,
+                         active_nr, WRITE_TREE_DRY_RUN);
        return dump_cache_tree(active_cache_tree, another, "");
 }
index bfaad9e09e63a3f9e0d0d3575cce32859382398c..05d4699c4a6cf32b2b6291e275dafa2744fe19d6 100644 (file)
@@ -1,6 +1,6 @@
 #include "cache.h"
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        struct cache_header hdr;
        int version;
index 3f388b4ce0bde90ff5593b42948338d5b73e8da9..ea3b959e94ff6f53726d4fce955bca1181a3be07 100644 (file)
@@ -22,7 +22,7 @@ static int compare_strings(const void *a, const void *b)
        return strcmp(x->text, y->text);
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        struct line *line, *p = NULL, *lines = NULL;
        struct strbuf sb = STRBUF_INIT;
index 3c9510a70107eed584916d61707002390d7d246a..434e8b892981e3b052437d1c7eb2668a70cf2dfa 100644 (file)
@@ -29,7 +29,7 @@ static int number_callback(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        const char *prefix = "prefix/";
        const char *usage[] = {
@@ -81,7 +81,7 @@ int main(int argc, const char **argv)
        };
        int i;
 
-       argc = parse_options(argc, argv, prefix, options, usage, 0);
+       argc = parse_options(argc, (const char **)argv, prefix, options, usage, 0);
 
        printf("boolean: %d\n", boolean);
        printf("integer: %u\n", integer);
index 0092cbf3540e2fdfd2e882461ba39ba2a833b672..bb975e4d3eaf54bb162ecdad8b4b8995c84fb035 100644 (file)
@@ -28,6 +28,19 @@ static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
        return 1;
 }
 
+static void normalize_argv_string(const char **var, const char *input)
+{
+       if (!strcmp(input, "<null>"))
+               *var = NULL;
+       else if (!strcmp(input, "<empty>"))
+               *var = "";
+       else
+               *var = input;
+
+       if (*var && (**var == '<' || **var == '('))
+               die("Bad value: %s\n", input);
+}
+
 int main(int argc, char **argv)
 {
        if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@@ -103,6 +116,25 @@ int main(int argc, char **argv)
                return 0;
        }
 
+       if (argc == 3 && !strcmp(argv[1], "mingw_path")) {
+               puts(argv[2]);
+               return 0;
+       }
+
+       if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+               struct strbuf sb = STRBUF_INIT;
+               const char *in, *prefix, *rel;
+               normalize_argv_string(&in, argv[2]);
+               normalize_argv_string(&prefix, argv[3]);
+               rel = relative_path(in, prefix, &sb);
+               if (!rel)
+                       puts("(null)");
+               else
+                       puts(strlen(rel) > 0 ? rel : "(empty)");
+               strbuf_release(&sb);
+               return 0;
+       }
+
        fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
                argv[1] ? argv[1] : "(there was none)");
        return 1;
diff --git a/test-prio-queue.c b/test-prio-queue.c
new file mode 100644 (file)
index 0000000..7be72f0
--- /dev/null
@@ -0,0 +1,39 @@
+#include "cache.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data)
+{
+       const int *a = va, *b = vb;
+       return *a - *b;
+}
+
+static void show(int *v)
+{
+       if (!v)
+               printf("NULL\n");
+       else
+               printf("%d\n", *v);
+       free(v);
+}
+
+int main(int argc, char **argv)
+{
+       struct prio_queue pq = { intcmp };
+
+       while (*++argv) {
+               if (!strcmp(*argv, "get"))
+                       show(prio_queue_get(&pq));
+               else if (!strcmp(*argv, "dump")) {
+                       int *v;
+                       while ((v = prio_queue_get(&pq)))
+                              show(v);
+               }
+               else {
+                       int *v = malloc(sizeof(*v));
+                       *v = atoi(*argv);
+                       prio_queue_put(&pq, v);
+               }
+       }
+
+       return 0;
+}
diff --git a/test-read-cache.c b/test-read-cache.c
new file mode 100644 (file)
index 0000000..b25bcf1
--- /dev/null
@@ -0,0 +1,13 @@
+#include "cache.h"
+
+int main (int argc, char **argv)
+{
+       int i, cnt = 1;
+       if (argc == 2)
+               cnt = strtol(argv[1], NULL, 0);
+       for (i = 0; i < cnt; i++) {
+               read_cache();
+               discard_cache();
+       }
+       return 0;
+}
index f2d4c0d22bd418836eb47424a03d0e903246d92e..93525eb7be48eb11c242200827a7f6f0706ee241 100644 (file)
@@ -1,7 +1,7 @@
 #include "cache.h"
 #include "run-command.h"
 
-int main(int argc, const char **argv)
+int main(int argc, char **argv)
 {
        struct child_process cp;
        int nogit = 0;
@@ -15,6 +15,6 @@ int main(int argc, const char **argv)
        }
        memset(&cp, 0, sizeof(cp));
        cp.git_cmd = 1;
-       cp.argv = argv + 1;
+       cp.argv = (const char **)argv + 1;
        return run_command(&cp);
 }
index 7f4b76a95899cc28aa1d42598d1649f126980ed9..97396a75ae4fc3f118750dd687fba85e3fc526cc 100644 (file)
@@ -24,7 +24,7 @@ int online_cpus(void)
        long ncpus;
 #endif
 
-#ifdef _WIN32
+#ifdef GIT_WINDOWS_NATIVE
        SYSTEM_INFO info;
        GetSystemInfo(&info);
 
diff --git a/trace.c b/trace.c
index 5ec0e3bd16b5cfc59254c6e10d4555ef3029aa36..3d744d1d4d93e7d4770ce858a7d5aee1c955a82b 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -75,6 +75,7 @@ static void trace_vprintf(const char *key, const char *fmt, va_list ap)
        strbuf_release(&buf);
 }
 
+__attribute__((format (printf, 2, 3)))
 static void trace_printf_key(const char *key, const char *fmt, ...)
 {
        va_list ap;
index dcd8d97411dd15e01ba1480a44b2a998043b424a..63cabc37e37dd338d3d9ee0764f099840d937600 100644 (file)
@@ -11,6 +11,7 @@
 #include "thread-utils.h"
 #include "sigchain.h"
 #include "argv-array.h"
+#include "refs.h"
 
 static int debug;
 
@@ -25,6 +26,7 @@ struct helper_data {
                option : 1,
                push : 1,
                connect : 1,
+               signed_tags : 1,
                no_disconnect_req : 1;
        char *export_marks;
        char *import_marks;
@@ -46,7 +48,7 @@ static void sendline(struct helper_data *helper, struct strbuf *buffer)
                die_errno("Full write to remote helper failed");
 }
 
-static int recvline_fh(FILE *helper, struct strbuf *buffer)
+static int recvline_fh(FILE *helper, struct strbuf *buffer, const char *name)
 {
        strbuf_reset(buffer);
        if (debug)
@@ -64,7 +66,7 @@ static int recvline_fh(FILE *helper, struct strbuf *buffer)
 
 static int recvline(struct helper_data *helper, struct strbuf *buffer)
 {
-       return recvline_fh(helper->out, buffer);
+       return recvline_fh(helper->out, buffer, helper->name);
 }
 
 static void xchgline(struct helper_data *helper, struct strbuf *buffer)
@@ -191,6 +193,8 @@ static struct child_process *get_helper(struct transport *transport)
                        refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec "));
                } else if (!strcmp(capname, "connect")) {
                        data->connect = 1;
+               } else if (!strcmp(capname, "signed-tags")) {
+                       data->signed_tags = 1;
                } else if (!prefixcmp(capname, "export-marks ")) {
                        struct strbuf arg = STRBUF_INIT;
                        strbuf_addstr(&arg, "--export-marks=");
@@ -211,10 +215,11 @@ static struct child_process *get_helper(struct transport *transport)
                int i;
                data->refspec_nr = refspec_nr;
                data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
-               for (i = 0; i < refspec_nr; i++) {
+               for (i = 0; i < refspec_nr; i++)
                        free((char *)refspecs[i]);
-               }
                free(refspecs);
+       } else if (data->import || data->bidi_import || data->export) {
+               warning("This remote helper should implement refspec capability.");
        }
        strbuf_release(&buf);
        if (debug)
@@ -410,9 +415,11 @@ static int get_exporter(struct transport *transport,
        /* we need to duplicate helper->in because we want to use it after
         * fastexport is done with it. */
        fastexport->out = dup(helper->in);
-       fastexport->argv = xcalloc(5 + revlist_args->nr, sizeof(*fastexport->argv));
+       fastexport->argv = xcalloc(6 + revlist_args->nr, sizeof(*fastexport->argv));
        fastexport->argv[argc++] = "fast-export";
        fastexport->argv[argc++] = "--use-done-feature";
+       fastexport->argv[argc++] = data->signed_tags ?
+               "--signed-tags=verbatim" : "--signed-tags=warn-strip";
        if (data->export_marks)
                fastexport->argv[argc++] = data->export_marks;
        if (data->import_marks)
@@ -469,7 +476,7 @@ static int fetch_with_import(struct transport *transport,
         * were fetching.
         *
         * (If no "refspec" capability was specified, for historical
-        * reasons we default to *:*.)
+        * reasons we default to the equivalent of *:*.)
         *
         * Store the result in to_fetch[i].old_sha1.  Callers such
         * as "git fetch" can use the value to write feedback to the
@@ -536,7 +543,7 @@ static int process_connect_service(struct transport *transport,
                goto exit;
 
        sendline(data, &cmdbuf);
-       recvline_fh(input, &cmdbuf);
+       recvline_fh(input, &cmdbuf, name);
        if (!strcmp(cmdbuf.buf, "")) {
                data->no_disconnect_req = 1;
                if (debug)
@@ -618,7 +625,7 @@ static int fetch(struct transport *transport,
        return -1;
 }
 
-static void push_update_ref_status(struct strbuf *buf,
+static int push_update_ref_status(struct strbuf *buf,
                                   struct ref **ref,
                                   struct ref *remote_refs)
 {
@@ -684,7 +691,7 @@ static void push_update_ref_status(struct strbuf *buf,
                *ref = find_ref_by_name(remote_refs, refname);
        if (!*ref) {
                warning("helper reported unexpected status of %s", refname);
-               return;
+               return 1;
        }
 
        if ((*ref)->status != REF_STATUS_NONE) {
@@ -693,11 +700,12 @@ static void push_update_ref_status(struct strbuf *buf,
                 * status reported by the remote helper if the latter is 'no match'.
                 */
                if (status == REF_STATUS_NONE)
-                       return;
+                       return 1;
        }
 
        (*ref)->status = status;
        (*ref)->remote_status = msg;
+       return !(status == REF_STATUS_OK);
 }
 
 static void push_update_refs_status(struct helper_data *data,
@@ -706,11 +714,24 @@ static void push_update_refs_status(struct helper_data *data,
        struct strbuf buf = STRBUF_INIT;
        struct ref *ref = remote_refs;
        for (;;) {
+               char *private;
+
                recvline(data, &buf);
                if (!buf.len)
                        break;
 
-               push_update_ref_status(&buf, &ref, remote_refs);
+               if (push_update_ref_status(&buf, &ref, remote_refs))
+                       continue;
+
+               if (!data->refspecs)
+                       continue;
+
+               /* propagate back the update to the remote namespace */
+               private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
+               if (!private)
+                       continue;
+               update_ref("update by helper", private, ref->new_sha1, NULL, 0, 0);
+               free(private);
        }
        strbuf_release(&buf);
 }
@@ -785,6 +806,14 @@ static int push_refs_with_export(struct transport *transport,
        struct string_list revlist_args = STRING_LIST_INIT_NODUP;
        struct strbuf buf = STRBUF_INIT;
 
+       if (!data->refspecs)
+               die("remote-helper doesn't support push; refspec needed");
+
+       if (flags & TRANSPORT_PUSH_DRY_RUN) {
+               if (set_helper_option(transport, "dry-run", "true") != 0)
+                       die("helper %s does not support dry-run", data->name);
+       }
+
        helper = get_helper(transport);
 
        write_constant(helper->in, "export\n");
@@ -795,22 +824,25 @@ static int push_refs_with_export(struct transport *transport,
                char *private;
                unsigned char sha1[20];
 
-               if (!data->refspecs)
-                       continue;
+               if (ref->deletion)
+                       die("remote-helpers do not support ref deletion");
+
                private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
                if (private && !get_sha1(private, sha1)) {
                        strbuf_addf(&buf, "^%s", private);
                        string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
+                       hashcpy(ref->old_sha1, sha1);
                }
                free(private);
 
-               if (ref->deletion) {
+               if (ref->deletion)
                        die("remote-helpers do not support ref deletion");
-               }
 
-               if (ref->peer_ref)
+               if (ref->peer_ref) {
+                       if (strcmp(ref->peer_ref->name, ref->name))
+                               die("remote-helpers do not support old:new syntax");
                        string_list_append(&revlist_args, ref->peer_ref->name);
-
+               }
        }
 
        if (get_exporter(transport, &exporter, &revlist_args))
@@ -950,6 +982,7 @@ int transport_helper_init(struct transport *transport, const char *name)
 #define PBUFFERSIZE 8192
 
 /* Print bidirectional transfer loop debug message. */
+__attribute__((format (printf, 1, 2)))
 static void transfer_debug(const char *fmt, ...)
 {
        va_list args;
@@ -1035,7 +1068,7 @@ static int udt_do_read(struct unidirectional_transfer *t)
                return -1;
        } else if (bytes == 0) {
                transfer_debug("%s EOF (with %i bytes in buffer)",
-                       t->src_name, t->bufuse);
+                       t->src_name, (int)t->bufuse);
                t->state = SSTATE_FLUSHING;
        } else if (bytes > 0) {
                t->bufuse += bytes;
@@ -1099,7 +1132,7 @@ static void *udt_copy_task_routine(void *udt)
 #ifndef NO_PTHREADS
 
 /*
- * Join thread, with apporiate errors on failure. Name is name for the
+ * Join thread, with appropriate errors on failure. Name is name for the
  * thread (for error messages). Returns 0 on success, 1 on failure.
  */
 static int tloop_join(pthread_t thread, const char *name)
@@ -1165,7 +1198,7 @@ static void udt_kill_transfer(struct unidirectional_transfer *t)
 }
 
 /*
- * Join process, with apporiate errors on failure. Name is name for the
+ * Join process, with appropriate errors on failure. Name is name for the
  * process (for error messages). Returns 0 on success, 1 on failure.
  */
 static int tloop_join(pid_t pid, const char *name)
index ba5d8afb1b04ba9d331c721fd9f730184fff2f23..de255880a41248857990380d7622512fd18160b0 100644 (file)
@@ -534,6 +534,8 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.quiet = (transport->verbose < 0);
        args.no_progress = !transport->progress;
        args.depth = data->options.depth;
+       args.check_self_contained_and_connected =
+               data->options.check_self_contained_and_connected;
 
        if (!data->got_remote_heads) {
                connect_setup(transport, 0, 0);
@@ -551,6 +553,8 @@ static int fetch_refs_via_pack(struct transport *transport,
                refs = NULL;
        data->conn = NULL;
        data->got_remote_heads = 0;
+       data->options.self_contained_and_connected =
+               args.self_contained_and_connected;
 
        free_refs(refs_tmp);
 
@@ -871,6 +875,8 @@ void transport_take_over(struct transport *transport,
        transport->push_refs = git_transport_push;
        transport->disconnect = disconnect_git;
        transport->smart_options = &(data->options);
+
+       transport->cannot_reuse = 1;
 }
 
 static int is_local(const char *url)
@@ -1228,7 +1234,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
                 * then local and remote refs are likely to still be equal.
                 * Just feed them all to the fetch method in that case.
                 * This condition shouldn't be met in a non-deepening fetch
-                * (see builtin-fetch.c:quickfetch()).
+                * (see builtin/fetch.c:quickfetch()).
                 */
                heads = xmalloc(nr_refs * sizeof(*heads));
                for (rm = refs; rm; rm = rm->next)
index fcb1d25d96a750c171c4341a9c5f08992915fb6b..96e0ede89f6c98efe988425aecd2418538b202e5 100644 (file)
@@ -8,6 +8,8 @@ struct git_transport_options {
        unsigned thin : 1;
        unsigned keep : 1;
        unsigned followtags : 1;
+       unsigned check_self_contained_and_connected : 1;
+       unsigned self_contained_and_connected : 1;
        int depth;
        const char *uploadpack;
        const char *receivepack;
@@ -25,6 +27,12 @@ struct transport {
         */
        unsigned got_remote_refs : 1;
 
+       /*
+        * Transports that call take-over destroys the data specific to
+        * the transport type while doing so, and cannot be reused.
+        */
+       unsigned cannot_reuse : 1;
+
        /**
         * Returns 0 if successful, positive if the option is not
         * recognized or is inapplicable, and negative if the option
@@ -168,7 +176,7 @@ int transport_connect(struct transport *transport, const char *name,
 int transport_helper_init(struct transport *transport, const char *name);
 int bidirectional_transfer_loop(int input, int output);
 
-/* common methods used by transport.c and builtin-send-pack.c */
+/* common methods used by transport.c and builtin/send-pack.c */
 void transport_verify_remote_names(int nr_heads, const char **heads);
 
 void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int verbose);
index 6e30ef9d048c62c11a92aa5b0ee6df2d227776e6..c626135234f2fbf2d8711e32a020d2e228a307b2 100644 (file)
@@ -323,7 +323,6 @@ static inline int prune_traversal(struct name_entry *e,
 
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 {
-       int ret = 0;
        int error = 0;
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
        int i;
@@ -341,6 +340,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
                strbuf_setlen(&base, info->pathlen);
        }
        for (;;) {
+               int trees_used;
                unsigned long mask, dirmask;
                const char *first = NULL;
                int first_len = 0;
@@ -404,15 +404,14 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
                if (interesting < 0)
                        break;
                if (interesting) {
-                       ret = info->fn(n, mask, dirmask, entry, info);
-                       if (ret < 0) {
-                               error = ret;
+                       trees_used = info->fn(n, mask, dirmask, entry, info);
+                       if (trees_used < 0) {
+                               error = trees_used;
                                if (!info->show_all_errors)
                                        break;
                        }
-                       mask &= ret;
+                       mask &= trees_used;
                }
-               ret = 0;
                for (i = 0; i < n; i++)
                        if (mask & (1ul << i))
                                update_extended_entry(tx + i, entry + i);
index 2bf0db9814a5c9f77fe00c97fa4f723986a7aac1..ae04b6417de0ff06391cc7e2d98fcd1b7d5aa633 100644 (file)
@@ -46,7 +46,7 @@ struct traverse_info {
        int pathlen;
        struct pathspec *pathspec;
 
-       unsigned long conflicts;
+       unsigned long df_conflicts;
        traverse_callback_t fn;
        void *data;
        int show_all_errors;
diff --git a/tree.c b/tree.c
index 62fed632d8a1ade389dd645b5887155c21ae25ba..c0e568c08f670c45ab9615af2a41bda43569b5fd 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -159,7 +159,7 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match)
         * sort at the end.
         */
        for (i = 0; !fn && i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                if (ce_stage(ce) == stage)
                        fn = read_one_entry;
        }
index 09e53df3b2a39e7ec8c7d601c5b8b8ae4de39b9a..bf01717015b662cec594d2d3c73e57f409037533 100644 (file)
@@ -116,14 +116,20 @@ static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
                        ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 }
 
-static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
-       unsigned int set, unsigned int clear)
+static struct cache_entry *dup_entry(const struct cache_entry *ce)
 {
        unsigned int size = ce_size(ce);
        struct cache_entry *new = xmalloc(size);
 
        memcpy(new, ce, size);
-       do_add_entry(o, new, set, clear);
+       return new;
+}
+
+static void add_entry(struct unpack_trees_options *o,
+                     const struct cache_entry *ce,
+                     unsigned int set, unsigned int clear)
+{
+       do_add_entry(o, dup_entry(ce), set, clear);
 }
 
 /*
@@ -173,7 +179,7 @@ static void display_error_msgs(struct unpack_trees_options *o)
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
  */
-static void unlink_entry(struct cache_entry *ce)
+static void unlink_entry(const struct cache_entry *ce)
 {
        if (!check_leading_path(ce->name, ce_namelen(ce)))
                return;
@@ -193,7 +199,7 @@ static int check_updates(struct unpack_trees_options *o)
 
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
-                       struct cache_entry *ce = index->cache[cnt];
+                       const struct cache_entry *ce = index->cache[cnt];
                        if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
                                total++;
                }
@@ -206,7 +212,7 @@ static int check_updates(struct unpack_trees_options *o)
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
        for (i = 0; i < index->cache_nr; i++) {
-               struct cache_entry *ce = index->cache[i];
+               const struct cache_entry *ce = index->cache[i];
 
                if (ce->ce_flags & CE_WT_REMOVE) {
                        display_progress(progress, ++cnt);
@@ -235,8 +241,11 @@ static int check_updates(struct unpack_trees_options *o)
        return errs != 0;
 }
 
-static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
-static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);
+static int verify_uptodate_sparse(const struct cache_entry *ce,
+                                 struct unpack_trees_options *o);
+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)
 {
@@ -291,7 +300,8 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
        return 0;
 }
 
-static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
+static inline int call_unpack_fn(const struct cache_entry * const *src,
+                                struct unpack_trees_options *o)
 {
        int ret = o->fn(src, o);
        if (ret > 0)
@@ -320,7 +330,7 @@ static void mark_all_ce_unused(struct index_state *index)
                index->cache[i]->ce_flags &= ~(CE_UNPACKED | CE_ADDED | CE_NEW_SKIP_WORKTREE);
 }
 
-static int locate_in_src_index(struct cache_entry *ce,
+static int locate_in_src_index(const struct cache_entry *ce,
                               struct unpack_trees_options *o)
 {
        struct index_state *index = o->src_index;
@@ -366,7 +376,7 @@ static struct cache_entry *next_cache_entry(struct unpack_trees_options *o)
        return NULL;
 }
 
-static void add_same_unmerged(struct cache_entry *ce,
+static void add_same_unmerged(const struct cache_entry *ce,
                              struct unpack_trees_options *o)
 {
        struct index_state *index = o->src_index;
@@ -388,7 +398,7 @@ static void add_same_unmerged(struct cache_entry *ce,
 static int unpack_index_entry(struct cache_entry *ce,
                              struct unpack_trees_options *o)
 {
-       struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
+       const struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
        int ret;
 
        src[0] = ce;
@@ -454,7 +464,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
        newinfo.pathspec = info->pathspec;
        newinfo.name = *p;
        newinfo.pathlen += tree_entry_len(p) + 1;
-       newinfo.conflicts |= df_conflicts;
+       newinfo.df_conflicts |= df_conflicts;
 
        for (i = 0; i < n; i++, dirmask >>= 1) {
                const unsigned char *sha1 = NULL;
@@ -555,17 +565,12 @@ static int unpack_nondirectories(int n, unsigned long mask,
 {
        int i;
        struct unpack_trees_options *o = info->data;
-       unsigned long conflicts;
+       unsigned long conflicts = info->df_conflicts | dirmask;
 
        /* Do we have *only* directories? Nothing to do */
        if (mask == dirmask && !src[0])
                return 0;
 
-       conflicts = info->conflicts;
-       if (o->merge)
-               conflicts >>= 1;
-       conflicts |= dirmask;
-
        /*
         * Ok, we've filled in up to any potential index entry in src[0],
         * now do the rest.
@@ -590,8 +595,16 @@ static int unpack_nondirectories(int n, unsigned long mask,
                src[i + o->merge] = create_ce_entry(info, names + i, stage);
        }
 
-       if (o->merge)
-               return call_unpack_fn(src, o);
+       if (o->merge) {
+               int rc = call_unpack_fn((const struct cache_entry * const *)src,
+                                       o);
+               for (i = 0; i < n; i++) {
+                       struct cache_entry *ce = src[i + o->merge];
+                       if (ce != o->df_conflict_entry)
+                               free(ce);
+               }
+               return rc;
+       }
 
        for (i = 0; i < n; i++)
                if (src[i] && src[i] != o->df_conflict_entry)
@@ -637,7 +650,7 @@ static int find_cache_pos(struct traverse_info *info,
        int p_len = tree_entry_len(p);
 
        for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
-               struct cache_entry *ce = index->cache[pos];
+               const struct cache_entry *ce = index->cache[pos];
                const char *ce_name, *ce_slash;
                int cmp, ce_len;
 
@@ -789,13 +802,6 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
 
        /* Now handle any directories.. */
        if (dirmask) {
-               unsigned long conflicts = mask & ~dirmask;
-               if (o->merge) {
-                       conflicts <<= 1;
-                       if (src[0])
-                               conflicts |= 1;
-               }
-
                /* special case: "diff-index --cached" looking at a tree */
                if (o->diff_index_cached &&
                    n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {
@@ -814,7 +820,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
                        }
                }
 
-               if (traverse_trees_recursive(n, dirmask, conflicts,
+               if (traverse_trees_recursive(n, dirmask, mask & ~dirmask,
                                             names, info) < 0)
                        return -1;
                return mask;
@@ -995,7 +1001,9 @@ static void mark_new_skip_worktree(struct exclude_list *el,
                       select_flag, skip_wt_flag, el);
 }
 
-static int verify_absent(struct cache_entry *, enum unpack_trees_error_types, struct unpack_trees_options *);
+static int verify_absent(const struct cache_entry *,
+                        enum unpack_trees_error_types,
+                        struct unpack_trees_options *);
 /*
  * N-way merge "len" trees.  Returns 0 on success, -1 on failure to manipulate the
  * resulting index, -2 on failure to reflect the changes to the work tree.
@@ -1026,10 +1034,6 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                        o->el = &el;
        }
 
-       if (o->dir) {
-               o->path_exclude_check = xmalloc(sizeof(struct path_exclude_check));
-               path_exclude_check_init(o->path_exclude_check, o->dir);
-       }
        memset(&o->result, 0, sizeof(o->result));
        o->result.initialized = 1;
        o->result.timestamp.sec = o->src_index->timestamp.sec;
@@ -1155,10 +1159,6 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
 done:
        clear_exclude_list(&el);
-       if (o->path_exclude_check) {
-               path_exclude_check_clear(o->path_exclude_check);
-               free(o->path_exclude_check);
-       }
        return ret;
 
 return_failed:
@@ -1173,12 +1173,13 @@ return_failed:
 
 /* Here come the merge functions */
 
-static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
+static int reject_merge(const struct cache_entry *ce,
+                       struct unpack_trees_options *o)
 {
        return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 }
 
-static int same(struct cache_entry *a, struct cache_entry *b)
+static int same(const struct cache_entry *a, const struct cache_entry *b)
 {
        if (!!a != !!b)
                return 0;
@@ -1195,9 +1196,9 @@ static int same(struct cache_entry *a, struct cache_entry *b)
  * When a CE gets turned into an unmerged entry, we
  * want it to be up-to-date
  */
-static int verify_uptodate_1(struct cache_entry *ce,
-                                  struct unpack_trees_options *o,
-                                  enum unpack_trees_error_types error_type)
+static int verify_uptodate_1(const struct cache_entry *ce,
+                            struct unpack_trees_options *o,
+                            enum unpack_trees_error_types error_type)
 {
        struct stat st;
 
@@ -1236,7 +1237,7 @@ static int verify_uptodate_1(struct cache_entry *ce,
                add_rejected_path(o, error_type, ce->name);
 }
 
-static int verify_uptodate(struct cache_entry *ce,
+static int verify_uptodate(const struct cache_entry *ce,
                           struct unpack_trees_options *o)
 {
        if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
@@ -1244,13 +1245,14 @@ static int verify_uptodate(struct cache_entry *ce,
        return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
 }
 
-static int verify_uptodate_sparse(struct cache_entry *ce,
+static int verify_uptodate_sparse(const struct cache_entry *ce,
                                  struct unpack_trees_options *o)
 {
        return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
 }
 
-static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
+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);
@@ -1263,16 +1265,16 @@ static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_optio
  * Currently, git does not checkout subprojects during a superproject
  * checkout, so it is not going to overwrite anything.
  */
-static int verify_clean_submodule(struct cache_entry *ce,
-                                     enum unpack_trees_error_types error_type,
-                                     struct unpack_trees_options *o)
+static int verify_clean_submodule(const struct cache_entry *ce,
+                                 enum unpack_trees_error_types error_type,
+                                 struct unpack_trees_options *o)
 {
        return 0;
 }
 
-static int verify_clean_subdirectory(struct cache_entry *ce,
-                                     enum unpack_trees_error_types error_type,
-                                     struct unpack_trees_options *o)
+static int verify_clean_subdirectory(const struct cache_entry *ce,
+                                    enum unpack_trees_error_types error_type,
+                                    struct unpack_trees_options *o)
 {
        /*
         * we are about to extract "ce->name"; we would not want to lose
@@ -1351,18 +1353,18 @@ static int verify_clean_subdirectory(struct cache_entry *ce,
  */
 static int icase_exists(struct unpack_trees_options *o, const char *name, int len, struct stat *st)
 {
-       struct cache_entry *src;
+       const struct cache_entry *src;
 
        src = index_name_exists(o->src_index, name, len, 1);
        return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 }
 
 static int check_ok_to_remove(const char *name, int len, int dtype,
-                             struct cache_entry *ce, struct stat *st,
+                             const struct cache_entry *ce, struct stat *st,
                              enum unpack_trees_error_types error_type,
                              struct unpack_trees_options *o)
 {
-       struct cache_entry *result;
+       const struct cache_entry *result;
 
        /*
         * It may be that the 'lstat()' succeeded even though
@@ -1375,7 +1377,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
                return 0;
 
        if (o->dir &&
-           is_path_excluded(o->path_exclude_check, name, -1, &dtype))
+           is_excluded(o->dir, name, &dtype))
                /*
                 * ce->name is explicitly excluded, so it is Ok to
                 * overwrite it.
@@ -1413,9 +1415,9 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static int verify_absent_1(struct cache_entry *ce,
-                                enum unpack_trees_error_types error_type,
-                                struct unpack_trees_options *o)
+static int verify_absent_1(const struct cache_entry *ce,
+                          enum unpack_trees_error_types error_type,
+                          struct unpack_trees_options *o)
 {
        int len;
        struct stat st;
@@ -1448,7 +1450,7 @@ static int verify_absent_1(struct cache_entry *ce,
        }
 }
 
-static int verify_absent(struct cache_entry *ce,
+static int verify_absent(const struct cache_entry *ce,
                         enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
 {
@@ -1457,9 +1459,9 @@ static int verify_absent(struct cache_entry *ce,
        return verify_absent_1(ce, error_type, o);
 }
 
-static int verify_absent_sparse(struct cache_entry *ce,
-                        enum unpack_trees_error_types error_type,
-                        struct unpack_trees_options *o)
+static int verify_absent_sparse(const struct cache_entry *ce,
+                               enum unpack_trees_error_types error_type,
+                               struct unpack_trees_options *o)
 {
        enum unpack_trees_error_types orphaned_error = error_type;
        if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
@@ -1468,10 +1470,12 @@ static int verify_absent_sparse(struct cache_entry *ce,
        return verify_absent_1(ce, orphaned_error, o);
 }
 
-static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
-               struct unpack_trees_options *o)
+static int merged_entry(const struct cache_entry *ce,
+                       const struct cache_entry *old,
+                       struct unpack_trees_options *o)
 {
        int update = CE_UPDATE;
+       struct cache_entry *merge = dup_entry(ce);
 
        if (!old) {
                /*
@@ -1489,8 +1493,11 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                update |= CE_ADDED;
                merge->ce_flags |= CE_NEW_SKIP_WORKTREE;
 
-               if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
+               if (verify_absent(merge,
+                                 ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
+                       free(merge);
                        return -1;
+               }
                invalidate_ce_path(merge, o);
        } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*
@@ -1504,8 +1511,10 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                        copy_cache_entry(merge, old);
                        update = 0;
                } else {
-                       if (verify_uptodate(old, o))
+                       if (verify_uptodate(old, o)) {
+                               free(merge);
                                return -1;
+                       }
                        /* Migrate old flags over */
                        update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
                        invalidate_ce_path(old, o);
@@ -1518,12 +1527,13 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                invalidate_ce_path(old, o);
        }
 
-       add_entry(o, merge, update, CE_STAGEMASK);
+       do_add_entry(o, merge, update, CE_STAGEMASK);
        return 1;
 }
 
-static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
-               struct unpack_trees_options *o)
+static int deleted_entry(const struct cache_entry *ce,
+                        const struct cache_entry *old,
+                        struct unpack_trees_options *o)
 {
        /* Did it exist in the index? */
        if (!old) {
@@ -1538,7 +1548,8 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
        return 1;
 }
 
-static int keep_entry(struct cache_entry *ce, struct unpack_trees_options *o)
+static int keep_entry(const struct cache_entry *ce,
+                     struct unpack_trees_options *o)
 {
        add_entry(o, ce, 0, 0);
        return 1;
@@ -1560,11 +1571,12 @@ static void show_stage_entry(FILE *o,
 }
 #endif
 
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
+int threeway_merge(const struct cache_entry * const *stages,
+                  struct unpack_trees_options *o)
 {
-       struct cache_entry *index;
-       struct cache_entry *head;
-       struct cache_entry *remote = stages[o->head_idx + 1];
+       const struct cache_entry *index;
+       const struct cache_entry *head;
+       const struct cache_entry *remote = stages[o->head_idx + 1];
        int count;
        int head_match = 0;
        int remote_match = 0;
@@ -1649,7 +1661,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
        if (o->aggressive) {
                int head_deleted = !head;
                int remote_deleted = !remote;
-               struct cache_entry *ce = NULL;
+               const struct cache_entry *ce = NULL;
 
                if (index)
                        ce = index;
@@ -1732,11 +1744,12 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
  * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
  *
  */
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
+int twoway_merge(const struct cache_entry * const *src,
+                struct unpack_trees_options *o)
 {
-       struct cache_entry *current = src[0];
-       struct cache_entry *oldtree = src[1];
-       struct cache_entry *newtree = src[2];
+       const struct cache_entry *current = src[0];
+       const struct cache_entry *oldtree = src[1];
+       const struct cache_entry *newtree = src[2];
 
        if (o->merge_size != 2)
                return error("Cannot do a twoway merge of %d trees",
@@ -1798,11 +1811,11 @@ int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o)
  * Keep the index entries at stage0, collapse stage1 but make sure
  * stage0 does not have anything there.
  */
-int bind_merge(struct cache_entry **src,
-               struct unpack_trees_options *o)
+int bind_merge(const struct cache_entry * const *src,
+              struct unpack_trees_options *o)
 {
-       struct cache_entry *old = src[0];
-       struct cache_entry *a = src[1];
+       const struct cache_entry *old = src[0];
+       const struct cache_entry *a = src[1];
 
        if (o->merge_size != 1)
                return error("Cannot do a bind merge of %d trees",
@@ -1822,10 +1835,11 @@ int bind_merge(struct cache_entry **src,
  * The rule is:
  * - take the stat information from stage0, take the data from stage1
  */
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
+int oneway_merge(const struct cache_entry * const *src,
+                struct unpack_trees_options *o)
 {
-       struct cache_entry *old = src[0];
-       struct cache_entry *a = src[1];
+       const struct cache_entry *old = src[0];
+       const struct cache_entry *a = src[1];
 
        if (o->merge_size != 1)
                return error("Cannot do a oneway merge of %d trees",
index ec74a9f19a47c39de61def9709da6d4d6f1dcbdb..36a73a6d003b6906dfb402aa930b352f5b368c60 100644 (file)
@@ -8,7 +8,7 @@
 struct unpack_trees_options;
 struct exclude_list;
 
-typedef int (*merge_fn_t)(struct cache_entry **src,
+typedef int (*merge_fn_t)(const struct cache_entry * const *src,
                struct unpack_trees_options *options);
 
 enum unpack_trees_error_types {
@@ -52,7 +52,6 @@ struct unpack_trees_options {
        const char *prefix;
        int cache_bottom;
        struct dir_struct *dir;
-       struct path_exclude_check *path_exclude_check;
        struct pathspec *pathspec;
        merge_fn_t fn;
        const char *msgs[NB_UNPACK_TREES_ERROR_TYPES];
@@ -78,9 +77,13 @@ struct unpack_trees_options {
 extern int unpack_trees(unsigned n, struct tree_desc *t,
                struct unpack_trees_options *options);
 
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int threeway_merge(const struct cache_entry * const *stages,
+                  struct unpack_trees_options *o);
+int twoway_merge(const struct cache_entry * const *src,
+                struct unpack_trees_options *o);
+int bind_merge(const struct cache_entry * const *src,
+              struct unpack_trees_options *o);
+int oneway_merge(const struct cache_entry * const *src,
+                struct unpack_trees_options *o);
 
 #endif
index bfa6279cc418278079bd95854adfe8d8301b6788..8327dc0b7c80b61fc24342a1816e323009818729 100644 (file)
@@ -40,6 +40,7 @@ static struct object_array have_obj;
 static struct object_array want_obj;
 static struct object_array extra_edge_obj;
 static unsigned int timeout;
+static int keepalive = 5;
 /* 0 for no sideband,
  * otherwise maximum packet size (up to 65520 bytes).
  */
@@ -68,87 +69,28 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
        return sz;
 }
 
-static FILE *pack_pipe = NULL;
-static void show_commit(struct commit *commit, void *data)
-{
-       if (commit->object.flags & BOUNDARY)
-               fputc('-', pack_pipe);
-       if (fputs(sha1_to_hex(commit->object.sha1), pack_pipe) < 0)
-               die("broken output pipe");
-       fputc('\n', pack_pipe);
-       fflush(pack_pipe);
-       free(commit->buffer);
-       commit->buffer = NULL;
-}
-
-static void show_object(struct object *obj,
-                       const struct name_path *path, const char *component,
-                       void *cb_data)
-{
-       show_object_with_name(pack_pipe, obj, path, component);
-}
-
-static void show_edge(struct commit *commit)
-{
-       fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
-}
-
-static int do_rev_list(int in, int out, void *user_data)
-{
-       int i;
-       struct rev_info revs;
-
-       pack_pipe = xfdopen(out, "w");
-       init_revisions(&revs, NULL);
-       revs.tag_objects = 1;
-       revs.tree_objects = 1;
-       revs.blob_objects = 1;
-       if (use_thin_pack)
-               revs.edge_hint = 1;
-
-       for (i = 0; i < want_obj.nr; i++) {
-               struct object *o = want_obj.objects[i].item;
-               /* why??? */
-               o->flags &= ~UNINTERESTING;
-               add_pending_object(&revs, o, NULL);
-       }
-       for (i = 0; i < have_obj.nr; i++) {
-               struct object *o = have_obj.objects[i].item;
-               o->flags |= UNINTERESTING;
-               add_pending_object(&revs, o, NULL);
-       }
-       setup_revisions(0, NULL, &revs, NULL);
-       if (prepare_revision_walk(&revs))
-               die("revision walk setup failed");
-       mark_edges_uninteresting(revs.commits, &revs, show_edge);
-       if (use_thin_pack)
-               for (i = 0; i < extra_edge_obj.nr; i++)
-                       fprintf(pack_pipe, "-%s\n", sha1_to_hex(
-                                       extra_edge_obj.objects[i].item->sha1));
-       traverse_commit_list(&revs, show_commit, show_object, NULL);
-       fflush(pack_pipe);
-       fclose(pack_pipe);
-       return 0;
-}
-
 static void create_pack_file(void)
 {
-       struct async rev_list;
        struct child_process pack_objects;
        char data[8193], progress[128];
        char abort_msg[] = "aborting due to possible repository "
                "corruption on the remote side.";
        int buffered = -1;
        ssize_t sz;
-       const char *argv[10];
-       int arg = 0;
+       const char *argv[12];
+       int i, arg = 0;
+       FILE *pipe_fd;
+       char *shallow_file = NULL;
 
-       argv[arg++] = "pack-objects";
-       if (!shallow_nr) {
-               argv[arg++] = "--revs";
-               if (use_thin_pack)
-                       argv[arg++] = "--thin";
+       if (shallow_nr) {
+               shallow_file = setup_temporary_shallow();
+               argv[arg++] = "--shallow-file";
+               argv[arg++] = shallow_file;
        }
+       argv[arg++] = "pack-objects";
+       argv[arg++] = "--revs";
+       if (use_thin_pack)
+               argv[arg++] = "--thin";
 
        argv[arg++] = "--stdout";
        if (!no_progress)
@@ -169,29 +111,21 @@ static void create_pack_file(void)
        if (start_command(&pack_objects))
                die("git upload-pack: unable to fork git-pack-objects");
 
-       if (shallow_nr) {
-               memset(&rev_list, 0, sizeof(rev_list));
-               rev_list.proc = do_rev_list;
-               rev_list.out = pack_objects.in;
-               if (start_async(&rev_list))
-                       die("git upload-pack: unable to fork git-rev-list");
-       }
-       else {
-               FILE *pipe_fd = xfdopen(pack_objects.in, "w");
-               int i;
-
-               for (i = 0; i < want_obj.nr; i++)
-                       fprintf(pipe_fd, "%s\n",
-                               sha1_to_hex(want_obj.objects[i].item->sha1));
-               fprintf(pipe_fd, "--not\n");
-               for (i = 0; i < have_obj.nr; i++)
-                       fprintf(pipe_fd, "%s\n",
-                               sha1_to_hex(have_obj.objects[i].item->sha1));
-               fprintf(pipe_fd, "\n");
-               fflush(pipe_fd);
-               fclose(pipe_fd);
-       }
-
+       pipe_fd = xfdopen(pack_objects.in, "w");
+
+       for (i = 0; i < want_obj.nr; i++)
+               fprintf(pipe_fd, "%s\n",
+                       sha1_to_hex(want_obj.objects[i].item->sha1));
+       fprintf(pipe_fd, "--not\n");
+       for (i = 0; i < have_obj.nr; i++)
+               fprintf(pipe_fd, "%s\n",
+                       sha1_to_hex(have_obj.objects[i].item->sha1));
+       for (i = 0; i < extra_edge_obj.nr; i++)
+               fprintf(pipe_fd, "%s\n",
+                       sha1_to_hex(extra_edge_obj.objects[i].item->sha1));
+       fprintf(pipe_fd, "\n");
+       fflush(pipe_fd);
+       fclose(pipe_fd);
 
        /* We read from pack_objects.err to capture stderr output for
         * progress bar, and pack_objects.out to capture the pack data.
@@ -200,6 +134,7 @@ static void create_pack_file(void)
        while (1) {
                struct pollfd pfd[2];
                int pe, pu, pollsize;
+               int ret;
 
                reset_timeout();
 
@@ -222,7 +157,8 @@ static void create_pack_file(void)
                if (!pollsize)
                        break;
 
-               if (poll(pfd, pollsize, -1) < 0) {
+               ret = poll(pfd, pollsize, 1000 * keepalive);
+               if (ret < 0) {
                        if (errno != EINTR) {
                                error("poll failed, resuming: %s",
                                      strerror(errno));
@@ -284,14 +220,32 @@ static void create_pack_file(void)
                        if (sz < 0)
                                goto fail;
                }
+
+               /*
+                * We hit the keepalive timeout without saying anything; send
+                * an empty message on the data sideband just to let the other
+                * side know we're still working on it, but don't have any data
+                * yet.
+                *
+                * If we don't have a sideband channel, there's no room in the
+                * protocol to say anything, so those clients are just out of
+                * luck.
+                */
+               if (!ret && use_sideband) {
+                       static const char buf[] = "0005\1";
+                       write_or_die(1, buf, 5);
+               }
        }
 
        if (finish_command(&pack_objects)) {
                error("git upload-pack: git-pack-objects died with error.");
                goto fail;
        }
-       if (shallow_nr && finish_async(&rev_list))
-               goto fail;      /* error was already reported */
+       if (shallow_file) {
+               if (*shallow_file)
+                       unlink(shallow_file);
+               free(shallow_file);
+       }
 
        /* flush the data */
        if (0 <= buffered) {
@@ -592,7 +546,7 @@ static void receive_needs(void)
                                die("invalid shallow line: %s", line);
                        object = parse_object(sha1);
                        if (!object)
-                               die("did not find object for %s", line);
+                               continue;
                        if (object->type != OBJ_COMMIT)
                                die("invalid shallow object %s", sha1_to_hex(sha1));
                        if (!(object->flags & CLIENT_SHALLOW)) {
@@ -785,6 +739,11 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
 {
        if (!strcmp("uploadpack.allowtipsha1inwant", var))
                allow_tip_sha1_in_want = git_config_bool(var, value);
+       else if (!strcmp("uploadpack.keepalive", var)) {
+               keepalive = git_config_int(var, value);
+               if (!keepalive)
+                       keepalive = -1;
+       }
        return parse_hide_refs_config(var, value, "uploadpack");
 }
 
diff --git a/utf8.c b/utf8.c
index 7f648574a550432e3160fcef178f894faf0bdd54..0d20e0acb2b6fb2dd1d63abed676a036b2b7ec2f 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -9,6 +9,20 @@ struct interval {
   int last;
 };
 
+size_t display_mode_esc_sequence_len(const char *s)
+{
+       const char *p = s;
+       if (*p++ != '\033')
+               return 0;
+       if (*p++ != '[')
+               return 0;
+       while (isdigit(*p) || *p == ';')
+               p++;
+       if (*p++ != 'm')
+               return 0;
+       return p - s;
+}
+
 /* auxiliary function for binary search in interval table */
 static int bisearch(ucs_char_t ucs, const struct interval *table, int max)
 {
@@ -252,18 +266,26 @@ int utf8_width(const char **start, size_t *remainder_p)
  * string, assuming that the string is utf8.  Returns strlen() instead
  * if the string does not look like a valid utf8 string.
  */
-int utf8_strwidth(const char *string)
+int utf8_strnwidth(const char *string, int len, int skip_ansi)
 {
        int width = 0;
        const char *orig = string;
 
-       while (1) {
-               if (!string)
-                       return strlen(orig);
-               if (!*string)
-                       return width;
+       if (len == -1)
+               len = strlen(string);
+       while (string && string < orig + len) {
+               int skip;
+               while (skip_ansi &&
+                      (skip = display_mode_esc_sequence_len(string)) != 0)
+                       string += skip;
                width += utf8_width(&string, NULL);
        }
+       return string ? width : len;
+}
+
+int utf8_strwidth(const char *string)
+{
+       return utf8_strnwidth(string, -1, 0);
 }
 
 int is_utf8(const char *text)
@@ -303,20 +325,6 @@ static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
        }
 }
 
-static size_t display_mode_esc_sequence_len(const char *s)
-{
-       const char *p = s;
-       if (*p++ != '\033')
-               return 0;
-       if (*p++ != '[')
-               return 0;
-       while (isdigit(*p) || *p == ';')
-               p++;
-       if (*p++ != 'm')
-               return 0;
-       return p - s;
-}
-
 /*
  * Wrap the text, if necessary. The variable indent is the indent for the
  * first line, indent2 is the indent for all other lines.
@@ -413,6 +421,52 @@ void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
        free(tmp);
 }
 
+void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width,
+                        const char *subst)
+{
+       struct strbuf sb_dst = STRBUF_INIT;
+       char *src = sb_src->buf;
+       char *end = src + sb_src->len;
+       char *dst;
+       int w = 0, subst_len = 0;
+
+       if (subst)
+               subst_len = strlen(subst);
+       strbuf_grow(&sb_dst, sb_src->len + subst_len);
+       dst = sb_dst.buf;
+
+       while (src < end) {
+               char *old;
+               size_t n;
+
+               while ((n = display_mode_esc_sequence_len(src))) {
+                       memcpy(dst, src, n);
+                       src += n;
+                       dst += n;
+               }
+
+               old = src;
+               n = utf8_width((const char**)&src, NULL);
+               if (!src)       /* broken utf-8, do nothing */
+                       return;
+               if (n && w >= pos && w < pos + width) {
+                       if (subst) {
+                               memcpy(dst, subst, subst_len);
+                               dst += subst_len;
+                               subst = NULL;
+                       }
+                       w += n;
+                       continue;
+               }
+               memcpy(dst, old, src - old);
+               dst += src - old;
+               w += n;
+       }
+       strbuf_setlen(&sb_dst, dst - sb_dst.buf);
+       strbuf_swap(sb_src, &sb_dst);
+       strbuf_release(&sb_dst);
+}
+
 int is_encoding_utf8(const char *name)
 {
        if (!name)
@@ -460,7 +514,7 @@ int utf8_fprintf(FILE *stream, const char *format, ...)
 #else
        typedef char * iconv_ibp;
 #endif
-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)
+char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv, int *outsz_p)
 {
        size_t outsz, outalloc;
        char *out, *outpos;
@@ -494,13 +548,17 @@ char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv)
                }
                else {
                        *outpos = '\0';
+                       if (outsz_p)
+                               *outsz_p = outpos - out;
                        break;
                }
        }
        return out;
 }
 
-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding)
+char *reencode_string_len(const char *in, int insz,
+                         const char *out_encoding, const char *in_encoding,
+                         int *outsz)
 {
        iconv_t conv;
        char *out;
@@ -526,7 +584,7 @@ char *reencode_string(const char *in, const char *out_encoding, const char *in_e
                        return NULL;
        }
 
-       out = reencode_string_iconv(in, strlen(in), conv);
+       out = reencode_string_iconv(in, insz, conv, outsz);
        iconv_close(conv);
        return out;
 }
diff --git a/utf8.h b/utf8.h
index 1f8ecad1e88451c622de08ea14414021da64f402..65d0e42b96b5a55ecf7362c58d486b9693bcfc46 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -3,25 +3,43 @@
 
 typedef unsigned int ucs_char_t;  /* assuming 32bit int */
 
+size_t display_mode_esc_sequence_len(const char *s);
 int utf8_width(const char **start, size_t *remainder_p);
+int utf8_strnwidth(const char *string, int len, int skip_ansi);
 int utf8_strwidth(const char *string);
 int is_utf8(const char *text);
 int is_encoding_utf8(const char *name);
 int same_encoding(const char *, const char *);
+__attribute__((format (printf, 2, 3)))
 int utf8_fprintf(FILE *, const char *, ...);
 
 void strbuf_add_wrapped_text(struct strbuf *buf,
                const char *text, int indent, int indent2, int width);
 void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
                             int indent, int indent2, int width);
+void strbuf_utf8_replace(struct strbuf *sb, int pos, int width,
+                        const char *subst);
 
 #ifndef NO_ICONV
-char *reencode_string_iconv(const char *in, size_t insz, iconv_t conv);
-char *reencode_string(const char *in, const char *out_encoding, const char *in_encoding);
+char *reencode_string_iconv(const char *in, size_t insz,
+                           iconv_t conv, int *outsz);
+char *reencode_string_len(const char *in, int insz,
+                         const char *out_encoding,
+                         const char *in_encoding,
+                         int *outsz);
 #else
-#define reencode_string(a,b,c) NULL
+#define reencode_string_len(a,b,c,d,e) NULL
 #endif
 
+static inline char *reencode_string(const char *in,
+                                   const char *out_encoding,
+                                   const char *in_encoding)
+{
+       return reencode_string_len(in, strlen(in),
+                                  out_encoding, in_encoding,
+                                  NULL);
+}
+
 int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);
 
 #endif
index 7192bdc1b880728a81b33a38091cca8de6a30445..f91ba99f32c047e5f3238668ae83de647ab92df2 100644 (file)
@@ -196,6 +196,11 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                        }
                                        if (t_ch <= p_ch && t_ch >= prev_ch)
                                                matched = 1;
+                                       else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) {
+                                               uchar t_ch_upper = toupper(t_ch);
+                                               if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch)
+                                                       matched = 1;
+                                       }
                                        p_ch = 0; /* This makes "prev_ch" get set to 0. */
                                } else if (p_ch == '[' && p[1] == ':') {
                                        const uchar *s;
@@ -245,6 +250,8 @@ static int dowild(const uchar *p, const uchar *text, unsigned int flags)
                                        } else if (CC_EQ(s,i, "upper")) {
                                                if (ISUPPER(t_ch))
                                                        matched = 1;
+                                               else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch))
+                                                       matched = 1;
                                        } else if (CC_EQ(s,i, "xdigit")) {
                                                if (ISXDIGIT(t_ch))
                                                        matched = 1;
index 53a8dd0a3f9a123eae16415d6bcd50b1fbcba6a4..701d2339b9f5014d78ff1d5c88cfd21a1accd0c2 100644 (file)
@@ -14,7 +14,7 @@ else
        GIT_TEMPLATE_DIR='@@BUILD_DIR@@/templates/blt'
        export GIT_TEMPLATE_DIR
 fi
-GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'
+GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'"${GITPERLLIB:+:$GITPERLLIB}"
 GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale'
 PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
 export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR
index bac59d2c41bae7441038b30728c696b6280f4c2d..f92b14759833b1ae77070b4188d8ad8ea8d3799e 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -130,6 +130,14 @@ void *xcalloc(size_t nmemb, size_t size)
        return ret;
 }
 
+/*
+ * Limit size of IO chunks, because huge chunks only cause pain.  OS X
+ * 64-bit is buggy, returning EINVAL if len >= INT_MAX; and even in
+ * the absense of bugs, large chunks can result in bad latencies when
+ * you decide to kill the process.
+ */
+#define MAX_IO_SIZE (8*1024*1024)
+
 /*
  * xread() is the same a read(), but it automatically restarts read()
  * operations with a recoverable error (EAGAIN and EINTR). xread()
@@ -138,6 +146,8 @@ void *xcalloc(size_t nmemb, size_t size)
 ssize_t xread(int fd, void *buf, size_t len)
 {
        ssize_t nr;
+       if (len > MAX_IO_SIZE)
+           len = MAX_IO_SIZE;
        while (1) {
                nr = read(fd, buf, len);
                if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
@@ -154,6 +164,8 @@ ssize_t xread(int fd, void *buf, size_t len)
 ssize_t xwrite(int fd, const void *buf, size_t len)
 {
        ssize_t nr;
+       if (len > MAX_IO_SIZE)
+           len = MAX_IO_SIZE;
        while (1) {
                nr = write(fd, buf, len);
                if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
@@ -322,7 +334,7 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
                template[5] = letters[v % num_letters]; v /= num_letters;
 
                fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
-               if (fd > 0)
+               if (fd >= 0)
                        return fd;
                /*
                 * Fatal error (EPERM, ENOSPC etc).
@@ -408,18 +420,24 @@ void warn_on_inaccessible(const char *path)
        warning(_("unable to access '%s': %s"), path, strerror(errno));
 }
 
-int access_or_warn(const char *path, int mode)
+static int access_error_is_ok(int err, unsigned flag)
+{
+       return err == ENOENT || err == ENOTDIR ||
+               ((flag & ACCESS_EACCES_OK) && err == EACCES);
+}
+
+int access_or_warn(const char *path, int mode, unsigned flag)
 {
        int ret = access(path, mode);
-       if (ret && errno != ENOENT && errno != ENOTDIR)
+       if (ret && !access_error_is_ok(errno, flag))
                warn_on_inaccessible(path);
        return ret;
 }
 
-int access_or_die(const char *path, int mode)
+int access_or_die(const char *path, int mode, unsigned flag)
 {
        int ret = access(path, mode);
-       if (ret && errno != ENOENT && errno != ENOTDIR)
+       if (ret && !access_error_is_ok(errno, flag))
                die_errno(_("unable to access '%s'"), path);
        return ret;
 }
index ec5f27c5992e5fec5fb54384a04ffacf1745ce4a..cb24f1fa9b9f9632e5fd92bbcceb44008619fa14 100644 (file)
@@ -127,6 +127,7 @@ void wt_status_prepare(struct wt_status *s)
        s->change.strdup_strings = 1;
        s->untracked.strdup_strings = 1;
        s->ignored.strdup_strings = 1;
+       s->show_branch = -1;  /* unspecified */
 }
 
 static void wt_status_print_unmerged_header(struct wt_status *s)
@@ -243,7 +244,7 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
        struct strbuf onebuf = STRBUF_INIT;
        const char *one, *how = _("bug");
 
-       one = quote_path(it->string, -1, &onebuf, s->prefix);
+       one = quote_path(it->string, s->prefix, &onebuf);
        status_printf(s, color(WT_STATUS_HEADER, s), "\t");
        switch (d->stagemask) {
        case 1: how = _("both deleted:"); break;
@@ -297,8 +298,8 @@ static void wt_status_print_change_data(struct wt_status *s,
                    change_type);
        }
 
-       one = quote_path(one_name, -1, &onebuf, s->prefix);
-       two = quote_path(two_name, -1, &twobuf, s->prefix);
+       one = quote_path(one_name, s->prefix, &onebuf);
+       two = quote_path(two_name, s->prefix, &twobuf);
 
        status_printf(s, color(WT_STATUS_HEADER, s), "\t");
        switch (status) {
@@ -371,7 +372,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
 static int unmerged_mask(const char *path)
 {
        int pos, mask;
-       struct cache_entry *ce;
+       const struct cache_entry *ce;
 
        pos = cache_name_pos(path, strlen(path));
        if (0 <= pos)
@@ -475,7 +476,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
        for (i = 0; i < active_nr; i++) {
                struct string_list_item *it;
                struct wt_status_change_data *d;
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
 
                if (!ce_path_match(ce, &pathspec))
                        continue;
@@ -511,9 +512,12 @@ static void wt_status_collect_untracked(struct wt_status *s)
        if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
                dir.flags |=
                        DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
+       if (s->show_ignored_files)
+               dir.flags |= DIR_SHOW_IGNORED_TOO;
        setup_standard_excludes(&dir);
 
        fill_directory(&dir, s->pathspec);
+
        for (i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
                if (cache_name_is_other(ent->name, ent->len) &&
@@ -522,22 +526,17 @@ static void wt_status_collect_untracked(struct wt_status *s)
                free(ent);
        }
 
-       if (s->show_ignored_files) {
-               dir.nr = 0;
-               dir.flags = DIR_SHOW_IGNORED;
-               if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
-                       dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
-               fill_directory(&dir, s->pathspec);
-               for (i = 0; i < dir.nr; i++) {
-                       struct dir_entry *ent = dir.entries[i];
-                       if (cache_name_is_other(ent->name, ent->len) &&
-                           match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
-                               string_list_insert(&s->ignored, ent->name);
-                       free(ent);
-               }
+       for (i = 0; i < dir.ignored_nr; i++) {
+               struct dir_entry *ent = dir.ignored[i];
+               if (cache_name_is_other(ent->name, ent->len) &&
+                   match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
+                       string_list_insert(&s->ignored, ent->name);
+               free(ent);
        }
 
        free(dir.entries);
+       free(dir.ignored);
+       clear_directory(&dir);
 
        if (advice_status_u_option) {
                struct timeval t_end;
@@ -708,8 +707,7 @@ static void wt_status_print_other(struct wt_status *s,
                struct string_list_item *it;
                const char *path;
                it = &(l->items[i]);
-               path = quote_path(it->string, strlen(it->string),
-                                 &buf, s->prefix);
+               path = quote_path(it->string, s->prefix, &buf);
                if (column_active(s->colopts)) {
                        string_list_append(&output, path);
                        continue;
@@ -828,7 +826,7 @@ static void show_am_in_progress(struct wt_status *s,
        if (advice_status_hints) {
                if (!state->am_empty_patch)
                        status_printf_ln(s, color,
-                               _("  (fix conflicts and then run \"git am --resolved\")"));
+                               _("  (fix conflicts and then run \"git am --continue\")"));
                status_printf_ln(s, color,
                        _("  (use \"git am --skip\" to skip this patch)"));
                status_printf_ln(s, color,
@@ -957,10 +955,12 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
        if (advice_status_hints) {
                if (has_unmerged(s))
                        status_printf_ln(s, color,
-                               _("  (fix conflicts and run \"git commit\")"));
+                               _("  (fix conflicts and run \"git cherry-pick --continue\")"));
                else
                        status_printf_ln(s, color,
-                               _("  (all conflicts fixed: run \"git commit\")"));
+                               _("  (all conflicts fixed: run \"git cherry-pick --continue\")"));
+               status_printf_ln(s, color,
+                       _("  (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"));
        }
        wt_status_print_trailer(s);
 }
@@ -1037,7 +1037,6 @@ got_nothing:
 }
 
 struct grab_1st_switch_cbdata {
-       int found;
        struct strbuf buf;
        unsigned char nsha1[20];
 };
@@ -1061,7 +1060,6 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
        for (end = target; *end && *end != '\n'; end++)
                ;
        strbuf_add(&cb->buf, target, end - target);
-       cb->found = 1;
        return 1;
 }
 
@@ -1178,7 +1176,10 @@ void wt_status_print(struct wt_status *s)
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
-                       if (state.detached_from) {
+                       if (state.rebase_in_progress || state.rebase_interactive_in_progress) {
+                               on_what = _("rebase in progress; onto ");
+                               branch_name = state.onto;
+                       } else if (state.detached_from) {
                                unsigned char sha1[20];
                                branch_name = state.detached_from;
                                if (!get_sha1("HEAD", sha1) &&
@@ -1291,7 +1292,7 @@ static void wt_shortstatus_unmerged(struct string_list_item *it,
        } else {
                struct strbuf onebuf = STRBUF_INIT;
                const char *one;
-               one = quote_path(it->string, -1, &onebuf, s->prefix);
+               one = quote_path(it->string, s->prefix, &onebuf);
                printf(" %s\n", one);
                strbuf_release(&onebuf);
        }
@@ -1319,7 +1320,7 @@ static void wt_shortstatus_status(struct string_list_item *it,
                struct strbuf onebuf = STRBUF_INIT;
                const char *one;
                if (d->head_path) {
-                       one = quote_path(d->head_path, -1, &onebuf, s->prefix);
+                       one = quote_path(d->head_path, s->prefix, &onebuf);
                        if (*one != '"' && strchr(one, ' ') != NULL) {
                                putchar('"');
                                strbuf_addch(&onebuf, '"');
@@ -1328,7 +1329,7 @@ static void wt_shortstatus_status(struct string_list_item *it,
                        printf("%s -> ", one);
                        strbuf_release(&onebuf);
                }
-               one = quote_path(it->string, -1, &onebuf, s->prefix);
+               one = quote_path(it->string, s->prefix, &onebuf);
                if (*one != '"' && strchr(one, ' ') != NULL) {
                        putchar('"');
                        strbuf_addch(&onebuf, '"');
@@ -1347,7 +1348,7 @@ static void wt_shortstatus_other(struct string_list_item *it,
        } else {
                struct strbuf onebuf = STRBUF_INIT;
                const char *one;
-               one = quote_path(it->string, -1, &onebuf, s->prefix);
+               one = quote_path(it->string, s->prefix, &onebuf);
                color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
                printf(" %s\n", one);
                strbuf_release(&onebuf);
index 4121bc208db2fbe5e4eb00af10056a9d8c0bab40..fb7152e187a2fe7c080aab1d84b7fd2cbe6c47a1 100644 (file)
@@ -96,9 +96,9 @@ void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);
 
-void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...)
-       ;
-void status_printf(struct wt_status *s, const char *color, const char *fmt, ...)
-       ;
+__attribute__((format (printf, 3, 4)))
+void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...);
+__attribute__((format (printf, 3, 4)))
+void status_printf(struct wt_status *s, const char *color, const char *fmt, ...);
 
 #endif /* STATUS_H */
index 219a3bbca613192d8b127659e4bfc0d60f38cf10..c0339919ccf78c4fe7d78123f9c3e891075d0602 100644 (file)
@@ -39,6 +39,8 @@ extern "C" {
 #define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
 #define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
 
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
+
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_COMMON (1 << 1)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
index b2eb6db2c851aea7eb08b17d802155876bef211c..2358a2d6326e54308413cb8a5e6b61eba06324e9 100644 (file)
@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
        xch->i2 = i2;
        xch->chg1 = chg1;
        xch->chg2 = chg2;
+       xch->ignore = 0;
 
        return xch;
 }
@@ -544,7 +545,9 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        xdchange_t *xch, *xche;
 
        for (xch = xscr; xch; xch = xche->next) {
-               xche = xdl_get_hunk(xch, xecfg);
+               xche = xdl_get_hunk(&xch, xecfg);
+               if (!xch)
+                       break;
                if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
                                     xch->i2, xche->i2 + xche->chg2 - xch->i2,
                                     ecb->priv) < 0)
@@ -553,6 +556,27 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        return 0;
 }
 
+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
+{
+       xdchange_t *xch;
+
+       for (xch = xscr; xch; xch = xch->next) {
+               int ignore = 1;
+               xrecord_t **rec;
+               long i;
+
+               rec = &xe->xdf1.recs[xch->i1];
+               for (i = 0; i < xch->chg1 && ignore; i++)
+                       ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+               rec = &xe->xdf2.recs[xch->i2];
+               for (i = 0; i < xch->chg2 && ignore; i++)
+                       ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+               xch->ignore = ignore;
+       }
+}
+
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
             xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
        xdchange_t *xscr;
@@ -571,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                return -1;
        }
        if (xscr) {
+               if (xpp->flags & XDF_IGNORE_BLANK_LINES)
+                       xdl_mark_ignorable(xscr, &xe, xpp->flags);
+
                if (ef(&xe, xscr, ecb, xecfg) < 0) {
 
                        xdl_free_script(xscr);
index 7a92ea9c4d84a559ae1d0bd90ebe667828d8f9cb..8b81206c9af0767bd91c4b9e453f7c5c2bde47b1 100644 (file)
@@ -41,6 +41,7 @@ typedef struct s_xdchange {
        struct s_xdchange *next;
        long i1, i2;
        long chg1, chg2;
+       int ignore;
 } xdchange_t;
 
 
index d11dbf9f13c13db16fa05f7539170862d6da9795..4d8645867e7d0103d49882877a7e4a8abacd24ea 100644 (file)
@@ -56,16 +56,51 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
 /*
  * Starting at the passed change atom, find the latest change atom to be included
  * inside the differential hunk according to the specified configuration.
+ * Also advance xscr if the first changes must be discarded.
  */
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
-       xdchange_t *xch, *xchp;
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
+{
+       xdchange_t *xch, *xchp, *lxch;
        long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+       long max_ignorable = xecfg->ctxlen;
+       unsigned long ignored = 0; /* number of ignored blank lines */
+
+       /* remove ignorable changes that are too far before other changes */
+       for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
+               xch = xchp->next;
+
+               if (xch == NULL ||
+                   xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
+                       *xscr = xch;
+       }
+
+       if (*xscr == NULL)
+               return NULL;
+
+       lxch = *xscr;
 
-       for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
-               if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+       for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
+               long distance = xch->i1 - (xchp->i1 + xchp->chg1);
+               if (distance > max_common)
                        break;
 
-       return xchp;
+               if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
+                       lxch = xch;
+                       ignored = 0;
+               } else if (distance < max_ignorable && xch->ignore) {
+                       ignored += xch->chg2;
+               } else if (lxch != xchp &&
+                          xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) {
+                       break;
+               } else if (!xch->ignore) {
+                       lxch = xch;
+                       ignored = 0;
+               } else {
+                       ignored += xch->chg2;
+               }
+       }
+
+       return lxch;
 }
 
 
@@ -139,7 +174,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                return xdl_emit_common(xe, xscr, ecb, xecfg);
 
        for (xch = xscr; xch; xch = xche->next) {
-               xche = xdl_get_hunk(xch, xecfg);
+               xche = xdl_get_hunk(&xch, xecfg);
+               if (!xch)
+                       break;
 
                s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
                s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
index c2e2e830273782dc597606ddbb0401c04dce8f8f..d29710770ce40bafa6e9eb2b2ea7c9c8ba43c727 100644 (file)
@@ -27,7 +27,7 @@
 typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                           xdemitconf_t const *xecfg);
 
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                  xdemitconf_t const *xecfg);
 
index 9504eaecb8ac45248ea22b0779aa7492cd15d83e..62cb23dfd37743e4985655998ccabd56db160233 100644 (file)
@@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {
        return nl + 1;
 }
 
+int xdl_blankline(const char *line, long size, long flags)
+{
+       long i;
+
+       if (!(flags & XDF_WHITESPACE_FLAGS))
+               return (size <= 1);
+
+       for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
+               ;
+
+       return (i == size);
+}
+
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 {
        int i1, i2;
index ad1428ed699383bf6f736b28033aeb9dbe114b76..4646ce575251b07053f20285be99422d6576603e 100644 (file)
@@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);
 void xdl_cha_free(chastore_t *cha);
 void *xdl_cha_alloc(chastore_t *cha);
 long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_blankline(const char *line, long size, long flags);
 int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
 unsigned long xdl_hash_record(char const **data, char const *top, long flags);
 unsigned int xdl_hashbits(unsigned int size);
diff --git a/zlib.c b/zlib.c
index bbaa0815ed55a425e458773a58a0eb9aca8453eb..61e6df0fdce6dfaf38da7af996d7fe801db8f00c 100644 (file)
--- a/zlib.c
+++ b/zlib.c
@@ -189,7 +189,7 @@ void git_deflate_init_gzip(git_zstream *strm, int level)
         * Use default 15 bits, +16 is to generate gzip header/trailer
         * instead of the zlib wrapper.
         */
-       return do_git_deflate_init(strm, level, 15 + 16);
+       do_git_deflate_init(strm, level, 15 + 16);
 }
 
 void git_deflate_init_raw(git_zstream *strm, int level)
@@ -198,7 +198,7 @@ void git_deflate_init_raw(git_zstream *strm, int level)
         * Use default 15 bits, negate the value to get raw compressed
         * data without zlib header and trailer.
         */
-       return do_git_deflate_init(strm, level, -15);
+       do_git_deflate_init(strm, level, -15);
 }
 
 int git_deflate_abort(git_zstream *strm)