Merge branch 'mh/ref-api'
authorJunio C Hamano <gitster@pobox.com>
Fri, 25 May 2012 19:04:36 +0000 (12:04 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 25 May 2012 19:04:36 +0000 (12:04 -0700)
Fixes a performance regression in the earlier series.

432 files changed:
.gitignore
Documentation/Makefile
Documentation/RelNotes/1.7.10.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.10.2.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.10.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.11.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.7.7.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.8.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.9.7.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-generate-patch.txt
Documentation/diff-options.txt
Documentation/everyday.txt
Documentation/git-am.txt
Documentation/git-archive.txt
Documentation/git-blame.txt
Documentation/git-branch.txt
Documentation/git-bundle.txt
Documentation/git-check-ref-format.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-column.txt [new file with mode: 0644]
Documentation/git-commit.txt
Documentation/git-config.txt
Documentation/git-cvsserver.txt
Documentation/git-difftool.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-fetch-pack.txt
Documentation/git-filter-branch.txt
Documentation/git-format-patch.txt
Documentation/git-gc.txt
Documentation/git-grep.txt
Documentation/git-index-pack.txt
Documentation/git-log.txt
Documentation/git-notes.txt
Documentation/git-p4.txt
Documentation/git-pack-refs.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-reflog.txt
Documentation/git-remote-helpers.txt
Documentation/git-remote.txt
Documentation/git-rerere.txt
Documentation/git-reset.txt
Documentation/git-rev-parse.txt
Documentation/git-revert.txt
Documentation/git-rm.txt
Documentation/git-shortlog.txt
Documentation/git-show-ref.txt
Documentation/git-show.txt
Documentation/git-stash.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git-tar-tree.txt
Documentation/git-update-index.txt
Documentation/git-var.txt
Documentation/git-whatchanged.txt
Documentation/git.txt
Documentation/gitcli.txt
Documentation/gitcore-tutorial.txt
Documentation/gitcredentials.txt
Documentation/gitdiffcore.txt
Documentation/githooks.txt
Documentation/gitmodules.txt
Documentation/gitweb.conf.txt
Documentation/gitworkflows.txt
Documentation/pretty-formats.txt
Documentation/pull-fetch-param.txt
Documentation/rev-list-options.txt
Documentation/technical/api-argv-array.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/api-revision-walking.txt
Documentation/technical/index-format.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-common.txt
Documentation/user-manual.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
advice.c
advice.h
archive-tar.c
archive-zip.c
archive.c
archive.h
argv-array.c
argv-array.h
bisect.c
branch.c
branch.h
builtin.h
builtin/apply.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/column.c [new file with mode: 0644]
builtin/commit.c
builtin/diff.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/help.c
builtin/index-pack.c
builtin/log.c
builtin/merge-file.c
builtin/merge.c
builtin/pack-objects.c
builtin/push.c
builtin/remote.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/send-pack.c
builtin/tag.c
builtin/unpack-objects.c
builtin/update-index.c
builtin/update-server-info.c
bundle.c
cache.h
column.c [new file with mode: 0644]
column.h [new file with mode: 0644]
combine-diff.c
command-list.txt
commit.c
commit.h
compat/mingw.c
compat/mingw.h
compat/win32/pthread.h
compat/win32mmap.c
config.c
configure.ac
contrib/completion/git-completion.bash
contrib/credential/osxkeychain/Makefile
contrib/examples/builtin-fetch--tool.c
contrib/fast-import/git-p4 [deleted file]
contrib/fast-import/git-p4.README [new file with mode: 0644]
contrib/fast-import/git-p4.bat [deleted file]
contrib/rerere-train.sh
contrib/subtree/.gitignore [new file with mode: 0644]
contrib/subtree/COPYING [new file with mode: 0644]
contrib/subtree/INSTALL [new file with mode: 0644]
contrib/subtree/Makefile [new file with mode: 0644]
contrib/subtree/README [new file with mode: 0644]
contrib/subtree/git-subtree.sh [new file with mode: 0755]
contrib/subtree/git-subtree.txt [new file with mode: 0644]
contrib/subtree/t/Makefile [new file with mode: 0644]
contrib/subtree/t/t7900-subtree.sh [new file with mode: 0755]
contrib/subtree/todo [new file with mode: 0644]
date.c
diff-no-index.c
diff.c
diff.h
diffcore-rename.c
dir.c
dir.h
entry.c
environment.c
exec_cmd.c
fast-import.c
fetch-pack.h
fsck.c
generate-cmdlist.sh
git-add--interactive.perl
git-am.sh
git-difftool--helper.sh
git-difftool.perl
git-p4.py [new file with mode: 0755]
git-rebase--am.sh
git-rebase--interactive.sh
git-rebase.sh
git-relink.perl
git-remote-testgit.py
git-repack.sh
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git.c
gitweb/gitweb.perl
gitweb/static/gitweb.css
graph.c
grep.c
grep.h
help.c
help.h
http-backend.c
http-push.c
http.c
ident.c
imap-send.c
ll-merge.c
log-tree.c
merge-recursive.c
mergesort.c [new file with mode: 0644]
mergesort.h [new file with mode: 0644]
notes-merge.c
object.c
object.h
pager.c
parse-options.c
parse-options.h
po/TEAMS
po/da.po [new file with mode: 0644]
po/de.po [new file with mode: 0644]
po/git.pot
po/pt_PT.po
po/zh_CN.po
pretty.c
read-cache.c
reflog-walk.c
reflog-walk.h
refs.c
remote-curl.c
revision.c
revision.h
run-command.c
sequencer.c
sequencer.h
setup.c
sha1_file.c
sha1_name.c
strbuf.c
strbuf.h
streaming.c
streaming.h
submodule.c
submodule.h
t/gitweb-lib.sh
t/lib-git-daemon.sh
t/lib-git-p4.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/perf/p5302-pack-index.sh [new file with mode: 0755]
t/t0023-crlf-am.sh
t/t0061-run-command.sh
t/t0062-revision-walking.sh [new file with mode: 0755]
t/t0303-credential-external.sh
t/t1050-large.sh
t/t1200-tutorial.sh
t/t1300-repo-config.sh
t/t1305-config-include.sh
t/t1410-reflog.sh
t/t1411-reflog-show.sh
t/t1501-worktree.sh
t/t1507-rev-parse-upstream.sh
t/t2004-checkout-cache-temp.sh
t/t2015-checkout-unborn.sh
t/t2020-checkout-detach.sh
t/t2030-unresolve-info.sh
t/t3200-branch.sh
t/t3300-funny-names.sh
t/t3310-notes-merge-manual-resolve.sh
t/t3404-rebase-interactive.sh
t/t3411-rebase-preserve-around-merges.sh
t/t3415-rebase-autosquash.sh
t/t3505-cherry-pick-empty.sh
t/t3508-cherry-pick-many-commits.sh
t/t3701-add-interactive.sh
t/t3900-i18n-commit.sh
t/t3903-stash.sh
t/t4006-diff-mode.sh
t/t4012-diff-binary.sh
t/t4013-diff-various.sh
t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
t/t4013/diff.diff-tree_--cc_--stat_--summary_master
t/t4013/diff.diff-tree_--cc_--stat_--summary_side
t/t4013/diff.diff-tree_--cc_--stat_master
t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial
t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
t/t4013/diff.diff-tree_-c_--stat_--summary_master
t/t4013/diff.diff-tree_-c_--stat_--summary_side
t/t4013/diff.diff-tree_-c_--stat_master
t/t4013/diff.diff_--patch-with-stat_-r_initial..side
t/t4013/diff.diff_--patch-with-stat_initial..side
t/t4013/diff.diff_--stat_initial..side
t/t4013/diff.diff_-r_--stat_initial..side
t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
t/t4013/diff.format-patch_--attach_--stdout_initial..master
t/t4013/diff.format-patch_--attach_--stdout_initial..master^
t/t4013/diff.format-patch_--attach_--stdout_initial..side
t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
t/t4013/diff.format-patch_--inline_--stdout_initial..master
t/t4013/diff.format-patch_--inline_--stdout_initial..master^
t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
t/t4013/diff.format-patch_--inline_--stdout_initial..side
t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
t/t4013/diff.format-patch_--stdout_--numbered_initial..master
t/t4013/diff.format-patch_--stdout_initial..master
t/t4013/diff.format-patch_--stdout_initial..master^
t/t4013/diff.format-patch_--stdout_initial..side
t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
t/t4013/diff.log_--patch-with-stat_master
t/t4013/diff.log_--patch-with-stat_master_--_dir_
t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
t/t4013/diff.log_--root_--patch-with-stat_--summary_master
t/t4013/diff.log_--root_--patch-with-stat_master
t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
t/t4013/diff.show_--patch-with-stat_--summary_side
t/t4013/diff.show_--patch-with-stat_side
t/t4013/diff.show_--stat_--summary_side
t/t4013/diff.show_--stat_side
t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
t/t4013/diff.whatchanged_--patch-with-stat_master
t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
t/t4013/diff.whatchanged_--root_--patch-with-stat_master
t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
t/t4014-format-patch.sh
t/t4016-diff-quote.sh
t/t4030-diff-textconv.sh
t/t4031-diff-rewrite-binary.sh
t/t4034-diff-words.sh
t/t4035-diff-quiet.sh
t/t4041-diff-submodule-option.sh
t/t4043-diff-rename-binary.sh
t/t4045-diff-relative.sh
t/t4047-diff-dirstat.sh
t/t4049-diff-stat-count.sh
t/t4052-stat-output.sh
t/t4053-diff-no-index.sh [new file with mode: 0755]
t/t4100-apply-stat.sh
t/t4150-am.sh
t/t4202-log.sh
t/t4205-log-pretty-formats.sh
t/t5000-tar-tree.sh
t/t5100-mailinfo.sh
t/t5100/patch0001
t/t5100/patch0002
t/t5100/patch0003
t/t5100/patch0005
t/t5100/patch0006
t/t5100/patch0010
t/t5100/patch0011
t/t5100/patch0014
t/t5100/patch0014--scissors
t/t5100/sample.mbox
t/t5150-request-pull.sh
t/t5500-fetch-pack.sh
t/t5510-fetch.sh
t/t5528-push-default.sh [new file with mode: 0755]
t/t5531-deep-submodule-push.sh
t/t5541-http-push.sh
t/t5550-http-fetch.sh
t/t5551-http-fetch.sh
t/t5570-git-daemon.sh
t/t5700-clone-reference.sh
t/t5710-info-alternate.sh
t/t5800-remote-helpers.sh
t/t6006-rev-list-format.sh
t/t6022-merge-rename.sh
t/t6028-merge-up-to-date.sh
t/t6030-bisect-porcelain.sh
t/t6032-merge-large-rename.sh
t/t6040-tracking-info.sh
t/t6042-merge-rename-corner-cases.sh
t/t6200-fmt-merge-msg.sh
t/t7004-tag.sh
t/t7201-co.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7408-submodule-reference.sh
t/t7501-commit.sh
t/t7502-commit.sh
t/t7503-pre-commit-hook.sh
t/t7508-status.sh
t/t7602-merge-octopus-many.sh
t/t7603-merge-reduce-heads.sh
t/t7607-merge-overwrite.sh
t/t7701-repack-unpack-unreachable.sh
t/t7800-difftool.sh
t/t7810-grep.sh
t/t9002-column.sh [new file with mode: 0755]
t/t9300-fast-import.sh
t/t9350-fast-export.sh
t/t9400-git-cvsserver-server.sh
t/t9501-gitweb-standalone-http-status.sh
t/t9800-git-p4-basic.sh
t/t9801-git-p4-branch.sh
t/t9802-git-p4-filetype.sh
t/t9803-git-p4-shell-metachars.sh
t/t9804-git-p4-label.sh
t/t9805-git-p4-skip-submit-edit.sh
t/t9806-git-p4-options.sh
t/t9807-git-p4-submit.sh
t/t9808-git-p4-chdir.sh
t/t9809-git-p4-client-view.sh
t/t9810-git-p4-rcs.sh
t/t9811-git-p4-label-import.sh [new file with mode: 0755]
t/t9902-completion.sh [new file with mode: 0755]
t/test-lib.sh
test-date.c
test-mergesort.c [new file with mode: 0644]
test-revision-walking.c [new file with mode: 0644]
test-subprocess.c
transport-helper.c
transport.c
transport.h
unpack-trees.c
varint.c [new file with mode: 0644]
varint.h [new file with mode: 0644]
vcs-svn/svndump.c
wrapper.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
xdiff/xdiff.h
xdiff/xdiffi.c
xdiff/xhistogram.c
xdiff/xpatience.c
xdiff/xprepare.c
xdiff/xutils.c
xdiff/xutils.h
index 87fcc5f6ff2e180280ff767fd291247739c7d0fa..bf66648e2c5f59cb92470dbda546c5348bcc85ef 100644 (file)
@@ -26,6 +26,7 @@
 /git-cherry-pick
 /git-clean
 /git-clone
+/git-column
 /git-commit
 /git-commit-tree
 /git-config
@@ -92,6 +93,7 @@
 /git-name-rev
 /git-mv
 /git-notes
+/git-p4
 /git-pack-redundant
 /git-pack-objects
 /git-pack-refs
 /test-index-version
 /test-line-buffer
 /test-match-trees
+/test-mergesort
 /test-mktemp
 /test-parse-options
 /test-path-utils
+/test-revision-walking
 /test-run-command
 /test-sha1
 /test-sigchain
index d40e211f22dbe6c102a102041ac9f0779c7cb837..14286cb65761ae93cb34195c1ce60f85caa510c1 100644 (file)
@@ -82,7 +82,7 @@ endif
 #
 
 ifndef ASCIIDOC7
-ASCIIDOC_EXTRA += -a asciidoc7compatible -a no-inline-literal
+ASCIIDOC_EXTRA += -a asciidoc7compatible
 endif
 ifdef DOCBOOK_XSL_172
 ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
@@ -124,6 +124,16 @@ SHELL_PATH ?= $(SHELL)
 # Shell quote;
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 
+ifdef DEFAULT_PAGER
+DEFAULT_PAGER_SQ = $(subst ','\'',$(DEFAULT_PAGER))
+ASCIIDOC_EXTRA += -a 'git-default-pager=$(DEFAULT_PAGER_SQ)'
+endif
+
+ifdef DEFAULT_EDITOR
+DEFAULT_EDITOR_SQ = $(subst ','\'',$(DEFAULT_EDITOR))
+ASCIIDOC_EXTRA += -a 'git-default-editor=$(DEFAULT_EDITOR_SQ)'
+endif
+
 #
 # Please note that there is a minor bug in asciidoc.
 # The version after 6.0.3 _will_ include the patch found here:
diff --git a/Documentation/RelNotes/1.7.10.1.txt b/Documentation/RelNotes/1.7.10.1.txt
new file mode 100644 (file)
index 0000000..806a965
--- /dev/null
@@ -0,0 +1,78 @@
+Git v1.7.10.1 Release Notes
+===========================
+
+Additions since v1.7.10
+-----------------------
+
+Localization message files for Danish and German have been added.
+
+
+Fixes since v1.7.10
+-------------------
+
+ * "git add -p" is not designed to deal with unmerged paths but did
+   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
+   era.
+
+ * When PATH contains an unreadable directory, alias expansion code
+   did not kick in, and failed with an error that said "git-subcmd"
+   was not found.
+
+ * "git clean -d -f" (not "-d -f -f") is supposed to protect nested
+   working trees of independent git repositories that exist in the
+   current project working tree from getting removed, but the
+   protection applied only to such working trees that are at the
+   top-level of the current project by mistake.
+
+ * "git commit --author=$name" did not tell the name that was being
+   recorded in the resulting commit to hooks, even though it does do
+   so when the end user overrode the authorship via the
+   "GIT_AUTHOR_NAME" environment variable.
+
+ * When "git commit --template F" errors out because the user did not
+   touch the message, it claimed that it aborts due to "empty
+   message", which was utterly wrong.
+
+ * The regexp configured with diff.wordregex was incorrectly reused
+   across files.
+
+ * An age-old corner case bug in combine diff (only triggered with -U0
+   and the hunk at the beginning of the file needs to be shown) has
+   been fixed.
+
+ * Rename detection logic used to match two empty files as renames
+   during merge-recursive, leading to unnatural mismerges.
+
+ * The parser in "fast-import" did not diagnose ":9" style references
+   that is not followed by required SP/LF as an error.
+
+ * When "git fetch" encounters repositories with too many references,
+   the command line of "fetch-pack" that is run by a helper
+   e.g. remote-curl, may fail to hold all of them. Now such an
+   internal invocation can feed the references through the standard
+   input of "fetch-pack".
+
+ * "git fetch" that recurses into submodules on demand did not check
+   if it needs to go into submodules when non branches (most notably,
+   tags) are fetched.
+
+ * "log -p --graph" used with "--stat" had a few formatting error.
+
+ * Running "notes merge --commit" failed to perform correctly when run
+   from any directory inside $GIT_DIR/.  When "notes merge" stops with
+   conflicts, $GIT_DIR/NOTES_MERGE_WORKTREE is the place a user edits
+   to resolve it.
+
+ * The 'push to upstream' implementation was broken in some corner
+   cases. "git push $there" without refspec, when the current branch
+   is set to push to a remote different from $there, used to push to
+   $there using the upstream information to a remote unreleated to
+   $there.
+
+ * Giving "--continue" to a conflicted "rebase -i" session skipped a
+   commit that only results in changes to submodules.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.10.2.txt b/Documentation/RelNotes/1.7.10.2.txt
new file mode 100644 (file)
index 0000000..7a7e9d6
--- /dev/null
@@ -0,0 +1,85 @@
+Git v1.7.10.2 Release Notes
+===========================
+
+Fixes since v1.7.10.1
+---------------------
+
+ * The test scaffolding for git-daemon was flaky.
+
+ * The test scaffolding for fast-import was flaky.
+
+ * The filesystem boundary was not correctly reported when .git directory
+   discovery stopped at a mount point.
+
+ * HTTP transport that requires authentication did not work correctly when
+   multiple connections are used simultaneously.
+
+ * Minor memory leak during unpack_trees (hence "merge" and "checkout"
+   to check out another branch) has been plugged.
+
+ * In the older days, the header "Conflicts:" in "cherry-pick" and "merge"
+   was separated by a blank line from the list of paths that follow for
+   readability, but when "merge" was rewritten in C, we lost it by
+   mistake. Remove the newline from "cherry-pick" to make them match
+   again.
+
+ * The command line parser choked "git cherry-pick $name" when $name can
+   be both revision name and a pathname, even though $name can never be a
+   path in the context of the command.
+
+ * The "include.path" facility in the configuration mechanism added in
+   1.7.10 forgot to interpret "~/path" and "~user/path" as it should.
+
+ * "git config --rename-section" to rename an existing section into a
+   bogus one did not check the new name.
+
+ * The "diff --no-index" codepath used limited-length buffers, risking
+   pathnames getting truncated.  Update it to use the strbuf API.
+
+ * The report from "git fetch" said "new branch" even for a non branch
+   ref.
+
+ * The http-backend (the server side of the smart http transfer) used
+   to overwrite GIT_COMMITTER_NAME and GIT_COMMITTER_EMAIL with the
+   value obtained from REMOTE_USER unconditionally, making it
+   impossible for the server side site-specific customization to use
+   different identity sources to affect the names logged. It now uses
+   REMOTE_USER only as a fallback value.
+
+ * "log --graph" was not very friendly with "--stat" option and its
+   output had line breaks at wrong places.
+
+ * Octopus merge strategy did not reduce heads that are recorded in the
+   final commit correctly.
+
+ * "git push" over smart-http lost progress output a few releases ago;
+   this release resurrects it.
+
+ * The error and advice messages given by "git push" when it fails due
+   to non-ff were not very helpful to new users; it has been broken
+   into three cases, and each is given a separate advice message.
+
+ * The insn sheet given by "rebase -i" did not make it clear that the
+   insn lines can be re-ordered to affect the order of the commits in
+   the resulting history.
+
+ * "git repack" used to write out unreachable objects as loose objects
+   when repacking, even if such loose objects will immediately pruned
+   due to its age.
+
+ * A contrib script "rerere-train" did not work out of the box unless
+   user futzed with her $PATH.
+
+ * "git rev-parse --show-prefix" used to emit nothing when run at the
+   top-level of the working tree, but now it gives a blank line.
+
+ * The i18n of error message "git stash save" was not properly done.
+
+ * "git submodule" used a sed script that some platforms mishandled.
+
+ * When using a Perl script on a system where "perl" found on user's
+   $PATH could be ancient or otherwise broken, we allow builders to
+   specify the path to a good copy of Perl with $PERL_PATH.  The
+   gitweb test forgot to use that Perl when running its test.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.10.3.txt b/Documentation/RelNotes/1.7.10.3.txt
new file mode 100644 (file)
index 0000000..703fbf1
--- /dev/null
@@ -0,0 +1,43 @@
+Git v1.7.10.3 Release Notes
+===========================
+
+Fixes since v1.7.10.2
+---------------------
+
+ * The message file for German translation has been updated a bit.
+
+ * Running "git checkout" on an unborn branch used to corrupt HEAD.
+
+ * When checking out another commit from an already detached state, we
+   used to report all commits that are not reachable from any of the
+   refs as lossage, but some of them might be reachable from the new
+   HEAD, and there is no need to warn about them.
+
+ * Some time ago, "git clone" lost the progress output for its
+   "checkout" phase; when run without any "--quiet" option, it should
+   give progress to the lengthy operation.
+
+ * The directory path used in "git diff --no-index", when it recurses
+   down, was broken with a recent update after v1.7.10.1 release.
+
+ * "log -z --pretty=tformat:..." did not terminate each record with
+   NUL.  The fix is not entirely correct when the output also asks for
+   --patch and/or --stat, though.
+
+ * The DWIM behaviour for "log --pretty=format:%gd -g" was somewhat
+   broken and gave undue precedence to configured log.date, causing
+   "git stash list" to show "stash@{time stamp string}".
+
+ * "git status --porcelain" ignored "--branch" option by mistake.  The
+   output for "git status --branch -z" was also incorrect and did not
+   terminate the record for the current branch name with NUL as asked.
+
+ * When a submodule repository uses alternate object store mechanism,
+   some commands that were started from the superproject did not
+   notice it and failed with "No such object" errors.  The subcommands
+   of "git submodule" command that recursed into the submodule in a
+   separate process were OK; only the ones that cheated and peeked
+   directly into the submodule's repository from the primary process
+   were affected.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.11.txt b/Documentation/RelNotes/1.7.11.txt
new file mode 100644 (file)
index 0000000..3f4c63a
--- /dev/null
@@ -0,0 +1,143 @@
+Git v1.7.11 Release Notes
+=========================
+
+Updates since v1.7.10
+---------------------
+
+UI, Workflows & Features
+
+ * A new mode for push, "simple", which is a cross between "current"
+   and "upstream", has been introduced. "git push" without any refspec
+   will push the current branch out to the same name at the remote
+   repository only when it is set to track the branch with the same
+   name over there.  The plan is to make this mode the new default
+   value when push.default is not configured.
+
+ * A couple of commands learned the "--column" option to produce
+   columnar output.
+
+ * A third-party tool "git subtree" is distributed in contrib/
+
+ * Error messages given when @{u} is used for a branch without its
+   upstream configured have been clatified.
+
+ * Even with "-q"uiet option, "checkout" used to report setting up
+   tracking.  Also "branch" learned the "-q"uiet option to squelch
+   informational message.
+
+ * Your build platform may support hardlinks but you may prefer not to
+   use them, e.g. when installing to DESTDIR to make a tarball and
+   untarring on a filesystem that has poor support for hardlinks.
+   There is a Makefile option NO_INSTALL_HARDLINKS for you.
+
+ * The smart-http backend used to always override GIT_COMMITTER_*
+   variables with REMOTE_USER and REMOTE_ADDR, but these variables are
+   now preserved when set.
+
+ * "git am" learned the "--include" option, which is an opposite of
+   existing the "--exclude" option.
+
+ * When "git am -3" needs to fall back to an application to a
+   synthesized preimage followed by a 3-way merge, the paths that
+   needed such treatment are now reported to the end user, so that the
+   result in them can be eyeballed with extra care.
+
+ * The output from "diff/log --stat" used to always allocate 4 columns
+   to show the number of modified lines, but not anymore.
+
+ * "git difftool" learned the "--dir-diff" option to spawn external
+   diff tools that can compare two directory hierarchies at a time
+   after populating two temporary directories, instead of running an
+   instance of the external tool once per a file pair.
+
+ * The "fmt-merge-msg" command learns to list the primary contributors
+   involved in the side topic you are merging.
+
+ * "git rebase" learned to optionally keep commits that do not
+   introduce any change in the original history.
+
+ * "git push --recurse-submodules" learned to optionally look into the
+   histories of submodules bound to the superproject and push them
+   out.
+
+ * A 'snapshot' request to "gitweb" honors If-Modified-Since: header,
+   based on the commit date.
+
+ * "gitweb" learned to highlight the patch it outputs even more.
+
+Foreign Interface
+
+ * "git svn" used to die with unwanted SIGPIPE when talking with HTTP
+   server that uses keep-alive.
+
+ * "git svn" learned to use platform specific authentication
+   providers, e.g. gnome-keyring, kwallet, etc.
+
+ * "git p4" has been moved out of contrib/ area and has seen more work
+   on importing labels as tags from (and exporting tags as labels to)
+   p4.
+
+Performance and Internal Implementation (please report possible regressions)
+
+ * An experimental "version 4" format of the index file has been
+   introduced to reduce on-disk footprint and I/O overhead.
+
+ * "git archive" learned to produce its output without reading the
+   blob object it writes out in memory in its entirety.
+
+ * "git index-pack" that runs when fetching or pushing objects to
+   complete the packfile on the receiving end learned to use multiple
+   threads to do its job when available.
+
+ * The code to compute hash values for lines used by the internal diff
+   engine was optimized on little-endian machines, using the same
+   trick the kernel folks came up with.
+
+ * "git apply" had some memory leaks plugged.
+
+ * Setting up a revision traversal with many starting points was
+   inefficient as these were placed in a date-order priority queue
+   one-by-one.  Now they are collected in the queue unordered first,
+   and sorted immediately before getting used.
+
+ * More lower-level commands learned to use the streaming API to read
+   from the object store without keeping everything in core.
+
+ * Because "sh" on the user's PATH may be utterly broken on some
+   systems, run-command API now uses SHELL_PATH, not /bin/sh, when
+   spawning an external command (not applicable to Windows port).
+
+ * The API to iterate over refs/ hierarchy has been tweaked to allow
+   walking only a subset of it more efficiently.
+
+Also contains minor documentation updates and code clean-ups.
+
+
+Fixes since v1.7.10
+-------------------
+
+Unless otherwise noted, all the fixes since v1.7.10 in the maintenance
+releases are contained in this release (see release notes to them for
+details).
+
+ * When a submodule repository uses alternate object store mechanism,
+   some commands that were started from the superproject did not
+   notice it and failed with "No such object" errors.  The subcommands
+   of "git submodule" command that properly recursed into the
+   submodule in a separate process were OK; only the ones that cheated
+   and peeked directly into the submodule's repository from the
+   primary process were affected.
+   (merge 5e73633 hv/submodule-alt-odb later to maint).
+
+ * The directory path used in "git diff --no-index", when it recurses
+   down, was broken with a recent update after v1.7.10.1 release.
+   (merge 176a335 bp/diff-no-index-strbuf-fix later to maint).
+
+ * "git status --porcelain" ignored "--branch" option by mistake.  The
+   output for "git status --branch -z" was also incorrect and did not
+   terminate the record for the current branch name with NUL as asked.
+   (merge d4a6bf1 jk/maint-status-porcelain-z-b later to maint).
+
+ * "git diff --stat" used to fully count a binary file with modified
+   execution bits whose contents is unmodified, which was not quite
+   right.
diff --git a/Documentation/RelNotes/1.7.7.7.txt b/Documentation/RelNotes/1.7.7.7.txt
new file mode 100644 (file)
index 0000000..e79118d
--- /dev/null
@@ -0,0 +1,13 @@
+Git v1.7.7.7 Release Notes
+==========================
+
+Fixes since v1.7.7.6
+--------------------
+
+ * An error message from 'git bundle' had an unmatched single quote pair in it.
+
+ * 'git diff --histogram' option was not described.
+
+ * 'git imap-send' carried an unused dead code.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.8.6.txt b/Documentation/RelNotes/1.7.8.6.txt
new file mode 100644 (file)
index 0000000..d9bf2b7
--- /dev/null
@@ -0,0 +1,22 @@
+Git v1.7.8.6 Release Notes
+==========================
+
+Fixes since v1.7.8.5
+--------------------
+
+ * An error message from 'git bundle' had an unmatched single quote pair in it.
+
+ * 'git diff --histogram' option was not described.
+
+ * Documentation for 'git rev-list' had minor formatting errors.
+
+ * 'git imap-send' carried an unused dead code.
+
+ * The way 'git fetch' implemented its connectivity check over
+   received objects was overly pessimistic, and wasted a lot of
+   cycles.
+
+ * Various minor backports of fixes from the 'master' and the 'maint'
+   branch.
+
+Also contains minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.9.7.txt b/Documentation/RelNotes/1.7.9.7.txt
new file mode 100644 (file)
index 0000000..59667d0
--- /dev/null
@@ -0,0 +1,13 @@
+Git v1.7.9.7 Release Notes
+==========================
+
+Fixes since v1.7.9.6
+--------------------
+
+ * An error message from 'git bundle' had an unmatched single quote pair in it.
+
+ * The way 'git fetch' implemented its connectivity check over
+   received objects was overly pessimistic, and wasted a lot of
+   cycles.
+
+Also contains minor fixes and documentation updates.
index c081657be774a70b453f493be11fbfb670452e86..915cb5a547896966377e9f39059527e3142b27c8 100644 (file)
@@ -95,7 +95,9 @@ included file is expanded immediately, as if its contents had been
 found at the location of the include directive. If the value of the
 `include.path` variable is a relative path, the path is considered to be
 relative to the configuration file in which the include directive was
-found. See below for examples.
+found. The value of `include.path` is subject to tilde expansion: `~/`
+is expanded to the value of `$HOME`, and `~user/` to the specified
+user's home directory. See below for examples.
 
 Example
 ~~~~~~~
@@ -122,6 +124,7 @@ Example
        [include]
                path = /path/to/foo.inc ; include by absolute path
                path = foo ; expand "foo" relative to the current file
+               path = ~/foo ; expand "foo" in your $HOME directory
 
 Variables
 ~~~~~~~~~
@@ -138,8 +141,23 @@ advice.*::
 +
 --
        pushNonFastForward::
-               Advice shown when linkgit:git-push[1] refuses
-               non-fast-forward refs.
+               Set this variable to 'false' if you want to disable
+               'pushNonFFCurrent', 'pushNonFFDefault', and
+               'pushNonFFMatching' simultaneously.
+       pushNonFFCurrent::
+               Advice shown when linkgit:git-push[1] fails due to a
+               non-fast-forward update to the current branch.
+       pushNonFFDefault::
+               Advice to set 'push.default' to 'upstream' or 'current'
+               when you ran linkgit:git-push[1] and pushed 'matching
+               refs' by default (i.e. you did not provide an explicit
+               refspec, and no 'push.default' configuration was set)
+               and it resulted in a non-fast-forward error.
+       pushNonFFMatching::
+               Advice shown when you ran linkgit:git-push[1] and pushed
+               'matching refs' explicitly (i.e. you used ':', or
+               specified a refspec that isn't your current branch) and
+               it resulted in a non-fast-forward error.
        statusHints::
                Directions on how to stage/unstage/add shown in the
                output of linkgit:git-status[1] and the template shown
@@ -463,8 +481,8 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.
 core.excludesfile::
        In addition to '.gitignore' (per-directory) and
        '.git/info/exclude', git looks into this file for patterns
-       of files which are not meant to be tracked.  "{tilde}/" is expanded
-       to the value of `$HOME` and "{tilde}user/" to the specified user's
+       of files which are not meant to be tracked.  "`~/`" is expanded
+       to the value of `$HOME` and "`~user/`" to the specified user's
        home directory.  See linkgit:gitignore[5].
 
 core.askpass::
@@ -838,6 +856,44 @@ color.ui::
        `never` if you prefer git commands not to use color unless enabled
        explicitly with some other configuration or the `--color` option.
 
+column.ui::
+       Specify whether supported commands should output in columns.
+       This variable consists of a list of tokens separated by spaces
+       or commas:
++
+--
+`always`;;
+       always show in columns
+`never`;;
+       never show in columns
+`auto`;;
+       show in columns if the output is to the terminal
+`column`;;
+       fill columns before rows (default)
+`row`;;
+       fill rows before columns
+`plain`;;
+       show in one column
+`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.status::
+       Specify whether to output untracked files in `git status` in columns.
+       See `column.ui` for details.
+
+column.tag::
+       Specify whether to output tag listing in `git tag` in columns.
+       See `column.ui` for details.
+
 commit.status::
        A boolean to enable/disable inclusion of status information in the
        commit message template when using an editor to prepare the commit
@@ -845,7 +901,7 @@ commit.status::
 
 commit.template::
        Specify a file to use as the template for new commit messages.
-       "{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
+       "`~/`" is expanded to the value of `$HOME` and "`~user/`" to the
        specified user's home directory.
 
 credential.helper::
@@ -970,7 +1026,7 @@ format.thread::
        a boolean value, or `shallow` or `deep`.  `shallow` threading
        makes every mail a reply to the head of the series,
        where the head is chosen from the cover letter, the
-       `\--in-reply-to`, and the first patch mail, in this order.
+       `--in-reply-to`, and the first patch mail, in this order.
        `deep` threading makes every mail a reply to the previous one.
        A true boolean value is the same as `shallow`, and a false
        value disables threading.
@@ -1401,7 +1457,7 @@ instaweb.port::
 interactive.singlekey::
        In interactive commands, allow the user to provide one-letter
        input with a single key (i.e., without hitting enter).
-       Currently this is used by the `\--patch` mode of
+       Currently this is used by the `--patch` mode of
        linkgit:git-add[1], linkgit:git-checkout[1], linkgit:git-commit[1],
        linkgit:git-reset[1], and linkgit:git-stash[1]. Note that this
        setting is silently ignored if portable keystroke input
@@ -1409,13 +1465,13 @@ interactive.singlekey::
 
 log.abbrevCommit::
        If true, makes linkgit:git-log[1], linkgit:git-show[1], and
-       linkgit:git-whatchanged[1] assume `\--abbrev-commit`. You may
-       override this option with `\--no-abbrev-commit`.
+       linkgit:git-whatchanged[1] assume `--abbrev-commit`. You may
+       override this option with `--no-abbrev-commit`.
 
 log.date::
        Set the default date-time mode for the 'log' command.
        Setting a value for log.date is similar to using 'git log''s
-       `\--date` option.  Possible values are `relative`, `local`,
+       `--date` option.  Possible values are `relative`, `local`,
        `default`, `iso`, `rfc`, and `short`; see linkgit:git-log[1]
        for details.
 
@@ -1605,18 +1661,18 @@ pack.indexVersion::
        and this config option ignored whenever the corresponding pack is
        larger than 2 GB.
 +
-If you have an old git that does not understand the version 2 `{asterisk}.idx` file,
+If you have an old git that does not understand the version 2 `*.idx` file,
 cloning or fetching over a non native protocol (e.g. "http" and "rsync")
-that will copy both `{asterisk}.pack` file and corresponding `{asterisk}.idx` file from the
+that will copy both `*.pack` file and corresponding `*.idx` file from the
 other side may give you a repository that cannot be accessed with your
-older version of git. If the `{asterisk}.pack` file is smaller than 2 GB, however,
+older version of git. If the `*.pack` file is smaller than 2 GB, however,
 you can use linkgit:git-index-pack[1] on the *.pack file to regenerate
-the `{asterisk}.idx` file.
+the `*.idx` file.
 
 pack.packSizeLimit::
        The maximum size of a pack.  This setting only affects
        packing to a file when repacking, i.e. the git:// protocol
-       is unaffected.  It can be overridden by the `\--max-pack-size`
+       is unaffected.  It can be overridden by the `--max-pack-size`
        option of linkgit:git-repack[1]. The minimum size allowed is
        limited to 1 MiB. The default is unlimited.
        Common unit suffixes of 'k', 'm', or 'g' are
@@ -1626,8 +1682,8 @@ pager.<cmd>::
        If the value is boolean, turns on or off pagination of the
        output of a particular git subcommand when writing to a tty.
        Otherwise, turns on pagination for the subcommand using the
-       pager specified by the value of `pager.<cmd>`.  If `\--paginate`
-       or `\--no-pager` is specified on the command line, it takes
+       pager specified by the value of `pager.<cmd>`.  If `--paginate`
+       or `--no-pager` is specified on the command line, it takes
        precedence over this option.  To disable pagination for all
        commands, set `core.pager` or `GIT_PAGER` to `cat`.
 
@@ -1635,9 +1691,9 @@ pretty.<name>::
        Alias for a --pretty= format string, as specified in
        linkgit:git-log[1]. Any aliases defined here can be used just
        as the built-in pretty formats could. For example,
-       running `git config pretty.changelog "format:{asterisk} %H %s"`
+       running `git config pretty.changelog "format:* %H %s"`
        would cause the invocation `git log --pretty=changelog`
-       to be equivalent to running `git log "--pretty=format:{asterisk} %H %s"`.
+       to be equivalent to running `git log "--pretty=format:* %H %s"`.
        Note that an alias with the same name as a built-in format
        will be silently ignored.
 
@@ -1665,12 +1721,30 @@ push.default::
        line. Possible values are:
 +
 * `nothing` - do not push anything.
-* `matching` - push all matching branches.
-  All branches having the same name in both ends are considered to be
-  matching. This is the default.
+* `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` - deprecated synonym for `upstream`.
+  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.
+  +
+  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.
 
 rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
@@ -1750,7 +1824,7 @@ remote.<name>.push::
 
 remote.<name>.mirror::
        If true, pushing to this remote will automatically behave
-       as if the `\--mirror` option was given on the command line.
+       as if the `--mirror` option was given on the command line.
 
 remote.<name>.skipDefaultUpdate::
        If true, this remote will be skipped by default when updating
index c57460c03dd1512e1e3644d3321e87d38316befe..55f499a160094d3135ba2e09fc558ef186fdaef2 100644 (file)
@@ -175,7 +175,7 @@ In the above example output, the function signature was changed
 from both files (hence two `-` removals from both file1 and
 file2, plus `++` to mean one line that was added does not appear
 in either file1 nor file2).  Also eight other lines are the same
-from file1 but do not appear in file2 (hence prefixed with `{plus}`).
+from file1 but do not appear in file2 (hence prefixed with `+`).
 
 When shown by `git diff-tree -c`, it compares the parents of a
 merge commit with the merge result (i.e. file1..fileN are the
index 378f19f0e21d57378cd1913445552456e09b4e51..6cfedd85dc890809a08e0236925a04b4f0d4b26d 100644 (file)
@@ -74,7 +74,7 @@ These parameters can also be set individually with `--stat-width=<width>`,
 `--stat-name-width=<name-width>` and `--stat-count=<count>`.
 
 --numstat::
-       Similar to `\--stat`, but shows number of added and
+       Similar to `--stat`, but shows number of added and
        deleted lines in decimal notation and pathname without
        abbreviation, to make it more machine friendly.  For
        binary files, outputs two `-` instead of saying
index ae413e52a52618503aa8d569f72ae688ec95a8fa..048337b40f8ebdaf4adda90a243f8d89c0aa8a00 100644 (file)
@@ -98,8 +98,8 @@ you originally wrote.
 <9> switch to the master branch.
 <10> merge a topic branch into your master branch.
 <11> review commit logs; other forms to limit output can be
-combined and include `\--max-count=10` (show 10 commits),
-`\--until=2005-12-10`, etc.
+combined and include `--max-count=10` (show 10 commits),
+`--until=2005-12-10`, etc.
 <12> view only the changes that touch what's in `curses/`
 directory, since `v2.43` tag.
 
index ee6cca2e1333eb26b0913eb5c4eaf9f38e5855d9..19d57a80f572c221c6a4d678a0673996416c5208 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
         [--3way] [--interactive] [--committer-date-is-author-date]
         [--ignore-date] [--ignore-space-change | --ignore-whitespace]
         [--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
-        [--exclude=<path>] [--reject] [-q | --quiet]
+        [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
         [--scissors | --no-scissors]
         [(<mbox> | <Maildir>)...]
 'git am' (--continue | --skip | --abort)
@@ -92,6 +92,7 @@ default.   You can use `--no-utf8` to override this.
 -p<n>::
 --directory=<dir>::
 --exclude=<path>::
+--include=<path>::
 --reject::
        These flags are passed to the 'git apply' (see linkgit:git-apply[1])
        program that applies
index ac7006e6400d85c31bf31a3022a38225373fe3f7..59d73e532fc4eaaec0c9d6c01d36b1d2da0cda38 100644 (file)
@@ -160,7 +160,7 @@ EXAMPLES
 
        Same as above, but the format is inferred from the output file.
 
-`git archive --format=tar --prefix=git-1.4.0/ v1.4.0{caret}\{tree\} | gzip >git-1.4.0.tar.gz`::
+`git archive --format=tar --prefix=git-1.4.0/ v1.4.0^{tree} | gzip >git-1.4.0.tar.gz`::
 
        Create a compressed tarball for v1.4.0 release, but without a
        global extended pax header.
index 9516914236bbfa675006994620b1c8f61855de1d..7ee923629ecc0dbf12a845f18d02c317b6266066 100644 (file)
@@ -160,7 +160,7 @@ introduced the file with:
        git log --diff-filter=A --pretty=short -- foo
 
 and then annotate the change between the commit and its
-parents, using `commit{caret}!` notation:
+parents, using `commit^!` notation:
 
        git blame -C -C -f $commit^! -- foo
 
index 6410c3d34545ce9bf191ffe91bbf8fd77cfc9b7e..47235bea0403bf25fa73529a37a7c5f87d0d7eb9 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
        [--list] [-v [--abbrev=<length> | --no-abbrev]]
+       [--column[=<options>] | --no-column]
        [(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -107,6 +108,14 @@ OPTIONS
        default to color output.
        Same as `--color=never`.
 
+--column[=<options>]::
+--no-column::
+       Display branch listing in columns. See configuration variable
+       column.branch for option syntax.`--column` and `--no-column`
+       without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable in non-verbose mode.
+
 -r::
 --remotes::
        List or delete (if used with -d) the remote-tracking branches.
@@ -126,6 +135,11 @@ OPTIONS
        relationship to upstream branch (if any). If given twice, print
        the name of the upstream branch, as well.
 
+-q::
+--quiet::
+       Be more quiet when creating or deleting a branch, suppressing
+       non-error messages.
+
 --abbrev=<length>::
        Alter the sha1's minimum display length in the output listing.
        The default value is 7 and can be overridden by the `core.abbrev`
index 92b01ec25d147831afaa0ffa43e11549af69e18c..16a6b0acebc7e15bfae0850d6d6c716fd5a32eaa 100644 (file)
@@ -61,7 +61,7 @@ unbundle <file>::
        A list of arguments, acceptable to 'git rev-parse' and
        'git rev-list' (and containing a named ref, see SPECIFYING REFERENCES
        below), that specifies the specific objects and references
-       to transport.  For example, `master{tilde}10..master` causes the
+       to transport.  For example, `master~10..master` causes the
        current master reference to be packaged along with all objects
        added since its 10th ancestor commit.  There is no explicit
        limit to the number of references and objects that may be
@@ -80,12 +80,12 @@ SPECIFYING REFERENCES
 
 'git bundle' will only package references that are shown by
 'git show-ref': this includes heads, tags, and remote heads.  References
-such as `master{tilde}1` cannot be packaged, but are perfectly suitable for
+such as `master~1` cannot be packaged, but are perfectly suitable for
 defining the basis.  More than one reference may be packaged, and more
 than one basis can be specified.  The objects packaged are those not
 contained in the union of the given bases.  Each basis can be
-specified explicitly (e.g. `^master{tilde}10`), or implicitly (e.g.
-`master{tilde}10..master`, `--since=10.days.ago master`).
+specified explicitly (e.g. `^master~10`), or implicitly (e.g.
+`master~10..master`, `--since=10.days.ago master`).
 
 It is very important that the basis used be held by the destination.
 It is okay to err on the side of caution, causing the bundle file
index 103e7b128d3a0d2b1c06da89d4a577f9548ba4f4..98009d1bd50271945637e813cc1fe8ec4161f5a5 100644 (file)
@@ -40,9 +40,9 @@ git imposes the following rules on how references are named:
 
 . They cannot have ASCII control characters (i.e. bytes whose
   values are lower than \040, or \177 `DEL`), space, tilde `~`,
-  caret `{caret}`, or colon `:` anywhere.
+  caret `^`, or colon `:` anywhere.
 
-. They cannot have question-mark `?`, asterisk `{asterisk}`, or open
+. They cannot have question-mark `?`, asterisk `*`, or open
   bracket `[` anywhere.  See the `--refspec-pattern` option below for
   an exception to this rule.
 
@@ -62,10 +62,10 @@ unquoted (by mistake), and also avoids ambiguities in certain
 reference name expressions (see linkgit:gitrevisions[7]):
 
 . A double-dot `..` is often used as in `ref1..ref2`, and in some
-  contexts this notation means `{caret}ref1 ref2` (i.e. not in
+  contexts this notation means `^ref1 ref2` (i.e. not in
   `ref1` and in `ref2`).
 
-. A tilde `~` and caret `{caret}` are used to introduce the postfix
+. A tilde `~` and caret `^` are used to introduce the postfix
   'nth parent' and 'peel onion' operation.
 
 . A colon `:` is used as in `srcref:dstref` to mean "use srcref\'s
@@ -92,9 +92,9 @@ OPTIONS
 --refspec-pattern::
        Interpret <refname> as a reference name pattern for a refspec
        (as used with remote repositories).  If this option is
-       enabled, <refname> is allowed to contain a single `{asterisk}`
+       enabled, <refname> is allowed to contain a single `*`
        in place of a one full pathname component (e.g.,
-       `foo/{asterisk}/bar` but not `foo/bar{asterisk}`).
+       `foo/*/bar` but not `foo/bar*`).
 
 --normalize::
        Normalize 'refname' by removing any leading slash (`/`)
index c0a96e6c1eede3b511689f35e9130a26ea81e003..63a251612ab9a25b798c8cdbb3a69e8a76241e36 100644 (file)
@@ -184,7 +184,7 @@ the conflicted merge in the specified paths.
 +
 This means that you can use `git checkout -p` to selectively discard
 edits from your current working tree. See the ``Interactive Mode''
-section of linkgit:git-add[1] to learn how to operate the `\--patch` mode.
+section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 
 <branch>::
        Branch to checkout; if it refers to a branch (i.e., a name that,
@@ -193,11 +193,11 @@ section of linkgit:git-add[1] to learn how to operate the `\--patch` mode.
        commit, your HEAD becomes "detached" and you are no longer on
        any branch (see below for details).
 +
-As a special case, the `"@\{-N\}"` syntax for the N-th last branch
+As a special case, the `"@{-N}"` syntax for the N-th last branch
 checks out the branch (instead of detaching).  You may also specify
-`-` which is synonymous with `"@\{-1\}"`.
+`-` which is synonymous with `"@{-1}"`.
 +
-As a further special case, you may use `"A\...B"` as a shortcut for the
+As a further special case, you may use `"A...B"` as a shortcut for the
 merge base of `A` and `B` if there is exactly one merge base. You can
 leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
index fed5097e00b4a031c2992ac3d421f6df975e6152..9f3dae631e5437662e9fb3583b24f414c93bb140 100644 (file)
@@ -103,6 +103,25 @@ effect to your index in a row.
        cherry-pick'ed commit, then a fast forward to this commit will
        be performed.
 
+--allow-empty::
+       By default, cherry-picking an empty commit will fail,
+       indicating that an explicit invocation of `git commit
+       --allow-empty` is required. This option overrides that
+       behavior, allowing empty commits to be preserved automatically
+       in a cherry-pick. Note that when "--ff" is in effect, empty
+       commits that meet the "fast-forward" requirement will be kept
+       even without this option.  Note also, that use of this option only
+       keeps commits that were initially empty (i.e. the commit recorded the
+       same tree as its parent).  Commits which are made empty due to a
+       previous commit are dropped.  To force the inclusion of those commits
+       use `--keep-redundant-commits`.
+
+--keep-redundant-commits::
+       If a commit being cherry picked duplicates a commit already in the
+       current history, it will become empty.  By default these
+       redundant commits are ignored.  This option overrides that behavior and
+       creates an empty commit object.  Implies `--allow-empty`.
+
 --strategy=<strategy>::
        Use the given merge strategy.  Should only be used once.
        See the MERGE STRATEGIES section in linkgit:git-merge[1]
@@ -130,7 +149,7 @@ EXAMPLES
        Apply the changes introduced by all commits that are ancestors
        of master but not of HEAD to produce new commits.
 
-`git cherry-pick master{tilde}4 master{tilde}2`::
+`git cherry-pick master~4 master~2`::
 
        Apply the changes introduced by the fifth and third last
        commits pointed to by master and create 2 new commits with
@@ -151,7 +170,7 @@ EXAMPLES
        are in next but not HEAD to the current branch, creating a new
        commit for each new change.
 
-`git rev-list --reverse master \-- README | git cherry-pick -n --stdin`::
+`git rev-list --reverse master -- README | git cherry-pick -n --stdin`::
 
        Apply the changes introduced by all commits on the master
        branch that touched README to the working tree and index,
diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt
new file mode 100644 (file)
index 0000000..9be16ee
--- /dev/null
@@ -0,0 +1,53 @@
+git-column(1)
+=============
+
+NAME
+----
+git-column - Display data in columns
+
+SYNOPSIS
+--------
+[verse]
+'git column' [--command=<name>] [--[raw-]mode=<mode>] [--width=<width>]
+            [--indent=<string>] [--nl=<string>] [--pading=<n>]
+
+DESCRIPTION
+-----------
+This command formats its input into multiple columns.
+
+OPTIONS
+-------
+--command=<name>::
+       Look up layout mode using configuration variable column.<name> and
+       column.ui.
+
+--mode=<mode>::
+       Specify layout mode. See configuration variable column.ui for option
+       syntax.
+
+--raw-mode=<n>::
+       Same as --mode but take mode encoded as a number. This is mainly used
+       by other commands that have already parsed layout mode.
+
+--width=<width>::
+       Specify the terminal width. By default 'git column' will detect the
+       terminal width, or fall back to 80 if it is unable to do so.
+
+--indent=<string>::
+       String to be printed at the beginning of each line.
+
+--nl=<N>::
+       String to be printed at the end of each line,
+       including newline character.
+
+--padding=<N>::
+       The number of spaces between columns. One space by default.
+
+
+Author
+------
+Written by Nguyen Thai Ngoc Duy <pclouds@gmail.com>
+
+GIT
+---
+Part of the linkgit:git[1] suite
index 5cc84a139133dca2fdcb594007c8b0d6464d5ca8..2d695f619ceff358a3bc7c1e66532cc077ca3065 100644 (file)
@@ -42,7 +42,7 @@ The content to be added can be specified in several ways:
 
 5. by using the --interactive or --patch switches with the 'commit' command
    to decide one by one which files or hunks should be part of the commit,
-   before finalizing the operation. See the ``Interactive Mode`` section of
+   before finalizing the operation. See the ``Interactive Mode'' section of
    linkgit:git-add[1] to learn how to operate these modes.
 
 The `--dry-run` option can be used to obtain a
@@ -132,11 +132,14 @@ OPTIONS
 
 -t <file>::
 --template=<file>::
-       Use the contents of the given file as the initial version
-       of the commit message. The editor is invoked and you can
-       make subsequent changes. If a message is specified using
-       the `-m` or `-F` options, this option has no effect. This
-       overrides the `commit.template` configuration variable.
+       When editing the commit message, start the editor with the
+       contents in the given file.  The `commit.template` configuration
+       variable is often used to give this option implicitly to the
+       command.  This mechanism can be used by projects that want to
+       guide participants with some hints on what to write in the message
+       in what order.  If the user exits the editor without editing the
+       message, the commit is aborted.  This has no effect when a message
+       is given by other means, e.g. with the `-m` or `-F` options.
 
 -s::
 --signoff::
@@ -284,7 +287,7 @@ When recording your own work, the contents of modified files in
 your working tree are temporarily stored to a staging area
 called the "index" with 'git add'.  A file can be
 reverted back, only in the index but not in the working tree,
-to that of the last commit with `git reset HEAD \-- <file>`,
+to that of the last commit with `git reset HEAD -- <file>`,
 which effectively reverts 'git add' and prevents the changes to
 this file from participating in the next commit.  After building
 the state to be committed incrementally with these commands,
index 81b03982e372c98afaf944398e9a168554871447..d9463cb3874181456138d4ca54da4e3540bbeb4f 100644 (file)
@@ -44,11 +44,15 @@ a "true" or "false" string for bool), or '--path', which does some
 path expansion (see '--path' below).  If no type specifier is passed, no
 checks or transformations are performed on the value.
 
-The file-option can be one of '--system', '--global' or '--file'
-which specify where the values will be read from or written to.
-The default is to assume the config file of the current repository,
-.git/config unless defined otherwise with GIT_DIR and GIT_CONFIG
-(see <<FILES>>).
+When reading, the values are read from the system, global and
+repository local configuration files by default, and options
+'--system', '--global', '--local' and '--file <filename>' can be
+used to tell the command to read from only that location (see <<FILES>>).
+
+When writing, the new value is written to the repository local
+configuration file by default, and options '--system', '--global',
+'--file <filename>' can be used to tell the command to write to
+that location (you can say '--local' but that is the default).
 
 This command will fail (with exit code ret) if:
 
@@ -194,9 +198,7 @@ If not set explicitly with '--file', there are three files where
 'git config' will search for configuration options:
 
 $GIT_DIR/config::
-       Repository specific configuration file. (The filename is
-       of course relative to the repository root, not the working
-       directory.)
+       Repository specific configuration file.
 
 ~/.gitconfig::
        User-specific configuration file. Also called "global"
index 827bc988ed5562c2dbc6566d6732b01f20bb4f05..88d814af0e050eb3b31f5763cbf12f03a5f6cbc3 100644 (file)
@@ -252,7 +252,7 @@ Configuring database backend
 
 'git-cvsserver' uses the Perl DBI module. Please also read
 its documentation if changing these variables, especially
-about `DBI\->connect()`.
+about `DBI->connect()`.
 
 gitcvs.dbname::
        Database name. The exact meaning depends on the
index fe38f667f9cd232c3c7a8bc7653648995b3ce4aa..31fc2e3aed613acd8cb6e4a56ae66c378fb07a0d 100644 (file)
@@ -19,6 +19,12 @@ linkgit:git-diff[1].
 
 OPTIONS
 -------
+-d::
+--dir-diff::
+       Copy the modified files to a temporary location and perform
+       a directory diff on them. This mode never prompts before
+       launching the diff tool.
+
 -y::
 --no-prompt::
        Do not prompt before launching a diff tool.
@@ -30,11 +36,9 @@ OPTIONS
 
 -t <tool>::
 --tool=<tool>::
-       Use the diff tool specified by <tool>.
-       Valid diff tools are:
-       araxis, bc3, deltawalker, diffuse, emerge, ecmerge, gvimdiff,
-       kdiff3, kompare, meld, opendiff, p4merge, tkdiff, vimdiff and
-       xxdiff.
+       Use the diff tool specified by <tool>.  Valid values include
+       emerge, kompare, meld, and vimdiff. Run `git difftool --tool-help`
+       for the list of valid <tool> settings.
 +
 If a diff tool is not specified, 'git difftool'
 will use the configuration variable `diff.tool`.  If the
@@ -62,6 +66,9 @@ of the diff post-image.  `$MERGED` is the name of the file which is
 being compared. `$BASE` is provided for compatibility
 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`.
+
 -x <command>::
 --extcmd=<command>::
        Specify a custom command for viewing diffs.
index f37eada63a449995861f41e5020c04f26d40c6f2..d6487e1ce03a9c060ae41b7b7455c442c6ea58f3 100644 (file)
@@ -104,7 +104,7 @@ marks the same across runs.
 [<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{tilde}10..master` causes the
+       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.
 
index ec6ef3119792a9e66a3a46bf6f0754458ea6a061..2620d28b4b38ed3f57ee426e27a2115db8160343 100644 (file)
@@ -98,9 +98,10 @@ OPTIONS
        options.
 
 --cat-blob-fd=<fd>::
-       Specify the file descriptor that will be written to
-       when the `cat-blob` command is encountered in the stream.
-       The default behaviour is to write to `stdout`.
+       Write responses to `cat-blob` and `ls` queries to the
+       file descriptor <fd> instead of `stdout`.  Allows `progress`
+       output intended for the end-user to be separated from other
+       output.
 
 --done::
        Require a `done` command at the end of the stream.
@@ -478,9 +479,9 @@ current branch value should be written as:
 ----
        from refs/heads/branch^0
 ----
-The `{caret}0` suffix is necessary as fast-import does not permit a branch to
+The `^0` suffix is necessary as fast-import does not permit a branch to
 start from itself, and the branch is created in memory before the
-`from` command is even read from the input.  Adding `{caret}0` will force
+`from` command is even read from the input.  Adding `^0` will force
 fast-import to resolve the commit through Git's revision parsing library,
 rather than its internal branch table, thereby loading in the
 existing value of the branch.
@@ -942,6 +943,9 @@ This command can be used anywhere in the stream that comments are
 accepted.  In particular, the `cat-blob` command can be used in the
 middle of a commit but not in the middle of a `data` command.
 
+See ``Responses To Commands'' below for details about how to read
+this output safely.
+
 `ls`
 ~~~~
 Prints information about the object at a path to a file descriptor
@@ -975,7 +979,7 @@ Reading from a named tree::
 
 See `filemodify` above for a detailed description of `<path>`.
 
-Output uses the same format as `git ls-tree <tree> {litdd} <path>`:
+Output uses the same format as `git ls-tree <tree> -- <path>`:
 
 ====
        <mode> SP ('blob' | 'tree' | 'commit') SP <dataref> HT <path> LF
@@ -991,6 +995,9 @@ instead report
        missing SP <path> LF
 ====
 
+See ``Responses To Commands'' below for details about how to read
+this output safely.
+
 `feature`
 ~~~~~~~~~
 Require that fast-import supports the specified feature, or abort if
@@ -1079,6 +1086,35 @@ If the `--done` command line option or `feature done` command is
 in use, the `done` command is mandatory and marks the end of the
 stream.
 
+Responses To Commands
+---------------------
+New objects written by fast-import are not available immediately.
+Most fast-import commands have no visible effect until the next
+checkpoint (or completion).  The frontend can send commands to
+fill fast-import's input pipe without worrying about how quickly
+they will take effect, which improves performance by simplifying
+scheduling.
+
+For some frontends, though, it is useful to be able to read back
+data from the current repository as it is being updated (for
+example when the source material describes objects in terms of
+patches to be applied to previously imported objects).  This can
+be accomplished by connecting the frontend and fast-import via
+bidirectional pipes:
+
+====
+       mkfifo fast-import-output
+       frontend <fast-import-output |
+       git fast-import >fast-import-output
+====
+
+A frontend set up this way can use `progress`, `ls`, and `cat-blob`
+commands to read information from the import in progress.
+
+To avoid deadlock, such frontends must completely consume any
+pending output from `progress`, `ls`, and `cat-blob` before
+performing writes to fast-import that might block.
+
 Crash Reports
 -------------
 If fast-import is supplied invalid input it will terminate with a
index ed1bdaacd10788ab35a2ae1de870d5972e30d432..474fa307a093ed126ab4f2216103a042d4bb0930 100644 (file)
@@ -32,6 +32,16 @@ OPTIONS
 --all::
        Fetch all remote refs.
 
+--stdin::
+       Take the list of refs from stdin, one per line. If there
+       are refs specified on the command line in addition to this
+       option, then the refs from stdin are processed after those
+       on the command line.
++
+If '--stateless-rpc' is specified together with this option then
+the list of refs must be in packet format (pkt-line). Each ref must
+be in a separate packet, and the list must end with a flush packet.
+
 -q::
 --quiet::
        Pass '-q' flag to 'git unpack-objects'; this makes the
index 0f2f1173834bdee77bd1d8d77e070af07a7b3580..81f58234a79a94ef5e9b60e1985b096cff6fcb5c 100644 (file)
@@ -96,8 +96,8 @@ OPTIONS
 --index-filter <command>::
        This is the filter for rewriting the index.  It is similar to the
        tree filter but does not check out the tree, which makes it much
-       faster.  Frequently used with `git rm \--cached
-       \--ignore-unmatch ...`, see EXAMPLES below.  For hairy
+       faster.  Frequently used with `git rm --cached
+       --ignore-unmatch ...`, see EXAMPLES below.  For hairy
        cases, see linkgit:git-update-index[1].
 
 --parent-filter <command>::
@@ -222,11 +222,11 @@ However, if the file is absent from the tree of some commit,
 a simple `rm filename` will fail for that tree and commit.
 Thus you may instead want to use `rm -f filename` as the script.
 
-Using `\--index-filter` with 'git rm' yields a significantly faster
+Using `--index-filter` with 'git rm' yields a significantly faster
 version.  Like with using `rm filename`, `git rm --cached filename`
 will fail if the file is absent from the tree of a commit.  If you
 want to "completely forget" a file, it does not matter when it entered
-history, so we also add `\--ignore-unmatch`:
+history, so we also add `--ignore-unmatch`:
 
 --------------------------------------------------------------------------
 git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
@@ -242,8 +242,8 @@ git filter-branch --subdirectory-filter foodir -- --all
 -------------------------------------------------------
 
 Thus you can, e.g., turn a library subdirectory into a repository of
-its own.  Note the `\--` that separates 'filter-branch' options from
-revision options, and the `\--all` to rewrite all branches and tags.
+its own.  Note the `--` that separates 'filter-branch' options from
+revision options, and the `--all` to rewrite all branches and tags.
 
 To set a commit (which typically is at the tip of another
 history) to be the parent of the current initial commit, in
@@ -371,23 +371,23 @@ Checklist for Shrinking a Repository
 ------------------------------------
 
 git-filter-branch is often used to get rid of a subset of files,
-usually with some combination of `\--index-filter` and
-`\--subdirectory-filter`.  People expect the resulting repository to
+usually with some combination of `--index-filter` and
+`--subdirectory-filter`.  People expect the resulting repository to
 be smaller than the original, but you need a few more steps to
 actually make it smaller, because git tries hard not to lose your
 objects until you tell it to.  First make sure that:
 
 * You really removed all variants of a filename, if a blob was moved
-  over its lifetime.  `git log \--name-only \--follow \--all \--
-  filename` can help you find renames.
+  over its lifetime.  `git log --name-only --follow --all -- filename`
+  can help you find renames.
 
-* You really filtered all refs: use `\--tag-name-filter cat \--
-  \--all` when calling git-filter-branch.
+* You really filtered all refs: use `--tag-name-filter cat -- --all`
+  when calling git-filter-branch.
 
 Then there are two ways to get a smaller repository.  A safer way is
 to clone, that keeps your original intact.
 
-* Clone it with `git clone +++file:///path/to/repo+++`.  The clone
+* Clone it with `git clone file:///path/to/repo`.  The clone
   will not have the removed objects.  See linkgit:git-clone[1].  (Note
   that cloning with a plain path just hardlinks everything!)
 
@@ -397,14 +397,14 @@ approach, so *make a backup* or go back to cloning it.  You have been
 warned.
 
 * Remove the original refs backed up by git-filter-branch: say `git
-  for-each-ref \--format="%(refname)" refs/original/ | xargs -n 1 git
+  for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git
   update-ref -d`.
 
-* Expire all reflogs with `git reflog expire \--expire=now \--all`.
+* Expire all reflogs with `git reflog expire --expire=now --all`.
 
-* Garbage collect all unreferenced objects with `git gc \--prune=now`
+* Garbage collect all unreferenced objects with `git gc --prune=now`
   (or if your git-gc is not new enough to support arguments to
-  `\--prune`, use `git repack -ad; git prune` instead).
+  `--prune`, use `git repack -ad; git prune` instead).
 
 GIT
 ---
index 6ea9be775c09111c14668e0de390a04a301f3235..04c7346e3e8ba12465e71ad1197ed6a109a473b1 100644 (file)
@@ -45,7 +45,7 @@ There are two ways to specify which commits to operate on.
 The first rule takes precedence in the case of a single <commit>.  To
 apply the second rule, i.e., format everything since the beginning of
 history up until <commit>, use the '\--root' option: `git format-patch
-\--root <commit>`.  If you want to format only <commit> itself, you
+--root <commit>`.  If you want to format only <commit> itself, you
 can do this with `git format-patch -1 <commit>`.
 
 By default, each output file is numbered sequentially from 1, and uses the
@@ -134,7 +134,7 @@ include::diff-options.txt[]
 The optional <style> argument can be either `shallow` or `deep`.
 'shallow' threading makes every mail a reply to the head of the
 series, where the head is chosen from the cover letter, the
-`\--in-reply-to`, and the first patch mail, in this order.  'deep'
+`--in-reply-to`, and the first patch mail, in this order.  'deep'
 threading makes every mail a reply to the previous one.
 +
 The default is `--no-thread`, unless the 'format.thread' configuration
index 815afcb9228aada96a143a4e5dcdec0e0ee807e5..b370b025b89ffad0664171a03527b9a775172dee 100644 (file)
@@ -84,7 +84,7 @@ The optional configuration variable 'gc.reflogExpireUnreachable'
 can be set to indicate how long historical reflog entries which
 are not part of the current branch should remain available in
 this repository.  These types of entries are generally created as
-a result of using `git commit \--amend` or `git rebase` and are the
+a result of using `git commit --amend` or `git rebase` and are the
 commits prior to the amend or rebase occurring.  Since these changes
 are not part of the current project most users will want to expire
 them sooner.  This option defaults to '30 days'.
index 343eadd4076664cc66c9d22db22d800e92cc6dc6..3bec0368831a04bc3653f11d3a0b1bff5a335907 100644 (file)
@@ -31,7 +31,9 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Look for specified patterns in the tracked files in the work tree, blobs
-registered in the index file, or blobs in given tree objects.
+registered in the index file, or blobs in given tree objects.  Patterns
+are lists of one or more search expressions separated by newline
+characters.  An empty string as search expression matches all lines.
 
 
 CONFIGURATION
@@ -247,11 +249,11 @@ OPTIONS
 Examples
 --------
 
-`git grep {apostrophe}time_t{apostrophe} \-- {apostrophe}*.[ch]{apostrophe}`::
+`git grep 'time_t' -- '*.[ch]'`::
        Looks for `time_t` in all tracked .c and .h files in the working
        directory and its subdirectories.
 
-`git grep -e {apostrophe}#define{apostrophe} --and \( -e MAX_PATH -e PATH_MAX \)`::
+`git grep -e '#define' --and \( -e MAX_PATH -e PATH_MAX \)`::
        Looks for a line that has `#define` and either `MAX_PATH` or
        `PATH_MAX`.
 
index 909687fed4269d8ad2e02b90d5a1f56fbcfde40e..39e6d0ddd84eeeacf43f41e0807cc1e305089ed0 100644 (file)
@@ -74,6 +74,16 @@ OPTIONS
 --strict::
        Die, if the pack contains broken objects or links.
 
+--threads=<n>::
+       Specifies the number of threads to spawn when resolving
+       deltas. This requires that index-pack be compiled with
+       pthreads otherwise this option is ignored with a warning.
+       This is meant to reduce packing time on multiprocessor
+       machines. The required amount of memory for the delta search
+       window is however multiplied by the number of threads.
+       Specifying 0 will cause git to auto-detect the number of CPU's
+       and use maximum 3 threads.
+
 
 Note
 ----
index 249fc878ec2058f9fcfc6655ecaeb299ba5622a7..1f906208f9514180fe9f20293ab28ce8180b5357 100644 (file)
@@ -100,7 +100,7 @@ Examples
        Show all commits since version 'v2.6.12' that changed any file
        in the include/scsi or drivers/scsi subdirectories
 
-`git log --since="2 weeks ago" \-- gitk`::
+`git log --since="2 weeks ago" -- gitk`::
 
        Show the changes during the last two weeks to the file 'gitk'.
        The "--" is necessary to avoid confusion with the *branch* named
index e8319eac6928300d1eb12070874e705b9be060b6..b95aafae2d7540d86d1b804871ef26dfbc41664e 100644 (file)
@@ -70,7 +70,7 @@ copy::
        second object). This subcommand is equivalent to:
        `git notes add [-f] -C $(git notes list <from-object>) <to-object>`
 +
-In `\--stdin` mode, take lines in the format
+In `--stdin` mode, take lines in the format
 +
 ----------
 <from-object> SP <to-object> [ SP <rest> ] LF
index b7c7929716adbad2e27f2d38b83a3c8f74604a59..fe1f49bc6f26950987bc5520267663f4f335dab0 100644 (file)
@@ -31,13 +31,6 @@ the updated p4 remote branch.
 
 EXAMPLE
 -------
-* Create an alias for 'git p4', using the full path to the 'git-p4'
-  script if needed:
-+
-------------
-$ git config --global alias.p4 '!git-p4'
-------------
-
 * Clone a repository:
 +
 ------------
@@ -165,11 +158,14 @@ OPTIONS
 
 General options
 ~~~~~~~~~~~~~~~
-All commands except clone accept this option.
+All commands except clone accept these options.
 
 --git-dir <dir>::
        Set the 'GIT_DIR' environment variable.  See linkgit:git[1].
 
+--verbose::
+       Provide more progress information.
+
 Sync options
 ~~~~~~~~~~~~
 These options can be used in the initial 'clone' as well as in
@@ -183,6 +179,7 @@ subsequent 'sync' operations.
 +
 This example imports a new remote "p4/proj2" into an existing
 git repository:
++
 ----
     $ git init
     $ git p4 sync --branch=refs/remotes/p4/proj2 //depot/proj2
@@ -200,12 +197,13 @@ git repository:
 --silent::
        Do not print any progress information.
 
---verbose::
-       Provide more progress information.
-
 --detect-labels::
        Query p4 for labels associated with the depot paths, and add
-       them as tags in git.
+       them as tags in git. Limited usefulness as only imports labels
+       associated with new changelists. Deprecated.
+
+--import-labels::
+       Import labels from p4 into git.
 
 --import-local::
        By default, p4 branches are stored in 'refs/remotes/p4/',
@@ -252,9 +250,6 @@ Submit options
 ~~~~~~~~~~~~~~
 These options can be used to modify 'git p4 submit' behavior.
 
---verbose::
-       Provide more progress information.
-
 --origin <commit>::
        Upstream location from which commits are identified to submit to
        p4.  By default, this is the most recent p4 commit reachable
@@ -270,6 +265,16 @@ These options can be used to modify 'git p4 submit' behavior.
        Re-author p4 changes before submitting to p4.  This option
        requires p4 admin privileges.
 
+--export-labels::
+       Export tags from git as p4 labels. Tags found in git are applied
+       to the perforce working directory.
+
+Rebase options
+~~~~~~~~~~~~~~
+These options can be used to modify 'git p4 rebase' behavior.
+
+--import-labels::
+       Import p4 labels.
 
 DEPOT PATH SYNTAX
 -----------------
@@ -311,19 +316,19 @@ configuration file.  This allows future 'git p4 submit' commands to
 work properly; the submit command looks only at the variable and does
 not have a command-line option.
 
-The full syntax for a p4 view is documented in 'p4 help views'.  Git-p4
+The full syntax for a p4 view is documented in 'p4 help views'.  'Git p4'
 knows only a subset of the view syntax.  It understands multi-line
 mappings, overlays with '+', exclusions with '-' and double-quotes
-around whitespace.  Of the possible wildcards, git-p4 only handles
-'...', and only when it is at the end of the path.  Git-p4 will complain
+around whitespace.  Of the possible wildcards, 'git p4' only handles
+'...', and only when it is at the end of the path.  'Git p4' will complain
 if it encounters an unhandled wildcard.
 
 Bugs in the implementation of overlap mappings exist.  If multiple depot
 paths map through overlays to the same location in the repository,
-git-p4 can choose the wrong one.  This is hard to solve without
-dedicating a client spec just for git-p4.
+'git p4' can choose the wrong one.  This is hard to solve without
+dedicating a client spec just for 'git p4'.
 
-The name of the client can be given to git-p4 in multiple ways.  The
+The name of the client can be given to 'git p4' in multiple ways.  The
 variable 'git-p4.client' takes precedence if it exists.  Otherwise,
 normal p4 mechanisms of determining the client are used:  environment
 variable P4CLIENT, a file referenced by P4CONFIG, or the local host name.
@@ -434,11 +439,23 @@ git-p4.branchList::
        enabled.  Each entry should be a pair of branch names separated
        by a colon (:).  This example declares that both branchA and
        branchB were created from main:
++
 -------------
 git config       git-p4.branchList main:branchA
 git config --add git-p4.branchList main:branchB
 -------------
 
+git-p4.ignoredP4Labels::
+       List of p4 labels to ignore. This is built automatically as
+       unimportable labels are discovered.
+
+git-p4.importLabels::
+       Import p4 labels into git, as per --import-labels.
+
+git-p4.labelImportRegexp::
+       Only p4 labels matching this regular expression will be imported. The
+       default value is '[a-zA-Z0-9_\-.]+$'.
+
 git-p4.useClientSpec::
        Specify that the p4 client spec should be used to identify p4
        depot paths of interest.  This is equivalent to specifying the
@@ -487,11 +504,18 @@ git-p4.skipUserNameCheck::
        user map, 'git p4' exits.  This option can be used to force
        submission regardless.
 
-git-p4.attemptRCSCleanup:
-    If enabled, 'git p4 submit' will attempt to cleanup RCS keywords
-    ($Header$, etc). These would otherwise cause merge conflicts and prevent
-    the submit going ahead. This option should be considered experimental at
-    present.
+git-p4.attemptRCSCleanup::
+       If enabled, 'git p4 submit' will attempt to cleanup RCS keywords
+       ($Header$, etc). These would otherwise cause merge conflicts and prevent
+       the submit going ahead. This option should be considered experimental at
+       present.
+
+git-p4.exportLabels::
+       Export git tags to p4 labels, as per --export-labels.
+
+git-p4.labelExportRegexp::
+       Only p4 labels matching this regular expression will be exported. The
+       default value is '[a-zA-Z0-9_\-.]+$'.
 
 IMPLEMENTATION DETAILS
 ----------------------
index a3c6677bfaaf628410eb85ce06f58d3c2b4df966..10afd4edfe515013bd92ad18d842536a537142a5 100644 (file)
@@ -32,7 +32,7 @@ Subsequent updates to branches always create new files under
 
 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
+occasionally run `git pack-refs --prune`.  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 0f18ec891ac75effb102a445a93bdcf4432bb697..defb544ed0af407a2e6360355bad34b1cb43fed9 100644 (file)
@@ -110,7 +110,7 @@ include::merge-options.txt[]
 +
 See `pull.rebase`, `branch.<name>.rebase` and `branch.autosetuprebase` in
 linkgit:git-config[1] if you want to make `git pull` always use
-`{litdd}rebase` instead of merging.
+`--rebase` instead of merging.
 +
 [NOTE]
 This is a potentially _dangerous_ mode of operation.
index 48760db3371ef762fe6e0f099045c208206742f1..cb97cc1c3b44d2b5201af8593e593a08bd36855e 100644 (file)
@@ -34,7 +34,7 @@ OPTIONS[[OPTIONS]]
 
 <refspec>...::
        The format of a <refspec> parameter is an optional plus
-       `{plus}`, followed by the source ref <src>, followed
+       `+`, followed by the source ref <src>, followed
        by a colon `:`, followed by the destination ref <dst>.
        It is used to specify with what <src> object the <dst> ref
        in the remote repository is to be updated.
@@ -50,7 +50,7 @@ updated.
 +
 The object referenced by <src> is used to update the <dst> reference
 on the remote side, but by default this is only allowed if the
-update can fast-forward <dst>.  By having the optional leading `{plus}`,
+update can fast-forward <dst>.  By having the optional leading `+`,
 you can tell git to update the <dst> ref even when the update is not a
 fast-forward.  This does *not* attempt to merge <src> into <dst>.  See
 EXAMPLES below for details.
@@ -60,7 +60,7 @@ EXAMPLES below for details.
 Pushing an empty <src> allows you to delete the <dst> ref from
 the remote repository.
 +
-The special refspec `:` (or `{plus}:` to allow non-fast-forward updates)
+The special refspec `:` (or `+:` to allow non-fast-forward updates)
 directs git to push "matching" branches: for every branch that exists on
 the local side, the remote side is updated if a branch of the same name
 already exists on the remote side.  This is the default operation mode
@@ -75,7 +75,7 @@ nor in any Push line of the corresponding remotes file---see below).
        Remove remote branches that don't have a local counterpart. For example
        a remote branch `tmp` will be removed if a local branch with the same
        name doesn't exist any more. This also respects refspecs, e.g.
-       `git push --prune remote refs/heads/{asterisk}:refs/tmp/{asterisk}` would
+       `git push --prune remote refs/heads/*:refs/tmp/*` would
        make sure that remote `refs/tmp/foo` will be removed if `refs/heads/foo`
        doesn't exist.
 
@@ -170,10 +170,16 @@ useful if you write an alias or script around 'git push'.
        is specified. This flag forces progress status even if the
        standard error stream is not directed to a terminal.
 
---recurse-submodules=check::
-       Check whether all submodule commits used by the revisions to be
-       pushed are available on a remote tracking branch. Otherwise the
-       push will be aborted and the command will exit with non-zero status.
+--recurse-submodules=check|on-demand::
+       Make sure all submodule commits used by the revisions to be
+       pushed are available on a remote tracking branch. If 'check' is
+       used git will verify that all submodule commits that changed in
+       the revisions to be pushed are available on at least one remote
+       of the submodule. If any commits are missing the push will be
+       aborted and exit with non-zero status. If 'on-demand' is used
+       all submodules that changed in the revisions to be pushed will
+       be pushed. If on-demand was not able to push all necessary
+       revisions it will also be aborted and exit with non-zero status.
 
 
 include::urls-remotes.txt[]
@@ -204,7 +210,7 @@ option is used.
 flag::
        A single character indicating the status of the ref:
 (space);; for a successfully pushed fast-forward;
-`{plus}`;; for a successful forced update;
+`+`;; for a successful forced update;
 `-`;; for a successfully deleted ref;
 `*`;; for a successfully pushed new ref;
 `!`;; for a ref that was rejected or failed to push; and
@@ -214,7 +220,7 @@ summary::
        For a successfully pushed ref, the summary shows the old and new
        values of the ref in a form suitable for using as an argument to
        `git log` (this is `<old>..<new>` in most cases, and
-       `<old>\...<new>` for forced non-fast-forward updates).
+       `<old>...<new>` for forced non-fast-forward updates).
 +
 For a failed update, more details are given:
 +
@@ -396,7 +402,7 @@ the ones in the examples below) can be configured as the default for
        Find a ref that matches `experimental` in the `origin` repository
        (e.g. `refs/heads/experimental`), and delete it.
 
-`git push origin {plus}dev:master`::
+`git push origin +dev:master`::
        Update the origin repository's master branch with the dev branch,
        allowing non-fast-forward updates.  *This can leave unreferenced
        commits dangling in the origin repository.*  Consider the
index 520aaa94fb7b2be7c11e7eed013acf36055a41ab..147fa1a8e002daba2a465ed1f56c88838500cb53 100644 (file)
@@ -238,6 +238,10 @@ leave out at most one of A and B, in which case it defaults to HEAD.
        will be reset to where it was when the rebase operation was
        started.
 
+--keep-empty::
+       Keep the commits that do not change anything from its
+       parents in the result.
+
 --skip::
        Restart the rebasing process by skipping the current patch.
 
@@ -267,7 +271,7 @@ which makes little sense.
 -X <strategy-option>::
 --strategy-option=<strategy-option>::
        Pass the <strategy-option> through to the merge strategy.
-       This implies `\--merge` and, if no strategy has been
+       This implies `--merge` and, if no strategy has been
        specified, `-s recursive`.  Note the reversal of 'ours' and
        'theirs' as noted in above for the `-m` option.
 
@@ -611,8 +615,8 @@ Easy case: The changes are literally the same.::
 Hard case: The changes are not the same.::
 
        This happens if the 'subsystem' rebase had conflicts, or used
-       `\--interactive` to omit, edit, squash, or fixup commits; or
-       if the upstream used one of `commit \--amend`, `reset`, or
+       `--interactive` to omit, edit, squash, or fixup commits; or
+       if the upstream used one of `commit --amend`, `reset`, or
        `filter-branch`.
 
 
@@ -648,7 +652,7 @@ correspond to the ones before the rebase.
 NOTE: While an "easy case recovery" sometimes appears to be successful
       even in the hard case, it may have unintended consequences.  For
       example, a commit that was removed via `git rebase
-      \--interactive` will be **resurrected**!
+      --interactive` will be **resurrected**!
 
 The idea is to manually tell 'git rebase' "where the old 'subsystem'
 ended and your 'topic' began", that is, what the old merge-base
@@ -656,7 +660,7 @@ between them was.  You will have to find a way to name the last commit
 of the old 'subsystem', for example:
 
 * With the 'subsystem' reflog: after 'git fetch', the old tip of
-  'subsystem' is at `subsystem@\{1}`.  Subsequent fetches will
+  'subsystem' is at `subsystem@{1}`.  Subsequent fetches will
   increase the number.  (See linkgit:git-reflog[1].)
 
 * Relative to the tip of 'topic': knowing that your 'topic' has three
index 976dc1493799c46bc2a390fde36063ae4e7a0e11..7fe2d2247bf4197c6212b6cab56e7f14606ff3b5 100644 (file)
@@ -39,13 +39,13 @@ as well).  It is an alias for `git log -g --abbrev-commit --pretty=oneline`;
 see linkgit:git-log[1].
 
 The reflog is useful in various git commands, to specify the old value
-of a reference. For example, `HEAD@\{2\}` means "where HEAD used to be
-two moves ago", `master@\{one.week.ago\}` means "where master used to
+of a reference. For example, `HEAD@{2}` means "where HEAD used to be
+two moves ago", `master@{one.week.ago}` means "where master used to
 point to one week ago", and so on. See linkgit:gitrevisions[7] for
 more details.
 
 To delete single entries from the reflog, use the subcommand "delete"
-and specify the _exact_ entry (e.g. "`git reflog delete master@\{2\}`").
+and specify the _exact_ entry (e.g. "`git reflog delete master@{2}`").
 
 
 OPTIONS
index 674797cd8308801dc3e3b4f3d6f581005a0a0d2a..f5836e46d080d8f1f9931a8cd1aa22d0654341ba 100644 (file)
@@ -87,7 +87,7 @@ to the `capabilities` command (see COMMANDS, below).
        capability use this.
 +
 A helper advertising the capability
-`refspec refs/heads/{asterisk}:refs/svn/origin/branches/{asterisk}`
+`refspec refs/heads/*:refs/svn/origin/branches/*`
 is saying that, when it is asked to `import refs/heads/topic`, the
 stream it outputs will update the `refs/svn/origin/branches/topic`
 ref.
@@ -96,7 +96,7 @@ 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 no 'refspec' capability is advertised,
-there is an implied `refspec {asterisk}:{asterisk}`.
+there is an implied `refspec *:*`.
 
 Capabilities for Pushing
 ~~~~~~~~~~~~~~~~~~~~~~~~
@@ -148,7 +148,7 @@ Other frontends may have some other order of preference.
        This modifies the 'import' capability.
 +
 A helper advertising
-`refspec refs/heads/{asterisk}:refs/svn/origin/branches/{asterisk}`
+`refspec refs/heads/*:refs/svn/origin/branches/*`
 in its capabilities is saying that, when it handles
 `import refs/heads/topic`, the stream it outputs will update the
 `refs/svn/origin/branches/topic` ref.
@@ -157,7 +157,7 @@ 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 no 'refspec' capability is advertised,
-there is an implied `refspec {asterisk}:{asterisk}`.
+there is an implied `refspec *:*`.
 
 INVOCATION
 ----------
index d376d19ef79962c5755e958eec7a491c96479a1f..a308f4c79f1a879e124486ef6cffbb2e9f8e606c 100644 (file)
@@ -67,14 +67,14 @@ multiple branches without grabbing all branches.
 With `-m <master>` option, a symbolic-ref `refs/remotes/<name>/HEAD` is set
 up to point at remote's `<master>` branch. See also the set-head command.
 +
-When a fetch mirror is created with `\--mirror=fetch`, the refs will not
+When a fetch mirror is created with `--mirror=fetch`, the refs will not
 be stored in the 'refs/remotes/' namespace, but rather everything in
 'refs/' on the remote will be directly mirrored into 'refs/' in the
 local repository. This option only makes sense in bare repositories,
 because a fetch would overwrite any local commits.
 +
-When a push mirror is created with `\--mirror=push`, then `git push`
-will always behave as if `\--mirror` was passed.
+When a push mirror is created with `--mirror=push`, then `git push`
+will always behave as if `--mirror` was passed.
 
 'rename'::
 
index b43b7c8c0ed67fa37e20c41f35a703c28c4d9844..a62227f84e88a5fae13af18e66aec6452693d354 100644 (file)
@@ -101,15 +101,15 @@ One way to do it is to pull master into the topic branch:
 
 The commits marked with `*` touch the same area in the same
 file; you need to resolve the conflicts when creating the commit
-marked with `{plus}`.  Then you can test the result to make sure your
+marked with `+`.  Then you can test the result to make sure your
 work-in-progress still works with what is in the latest master.
 
 After this test merge, there are two ways to continue your work
 on the topic.  The easiest is to build on top of the test merge
-commit `{plus}`, and when your work in the topic branch is finally
+commit `+`, and when your work in the topic branch is finally
 ready, pull the topic branch into master, and/or ask the
 upstream to pull from you.  By that time, however, the master or
-the upstream might have been advanced since the test merge `{plus}`,
+the upstream might have been advanced since the test merge `+`,
 in which case the final commit graph would look like this:
 
 ------------
index b674866e6d166ccce096b58f7cecdbdb183c0194..117e3743a62f59a1b6b57f8a36f4ee938f6e1848 100644 (file)
@@ -41,7 +41,7 @@ working tree in one go.
 +
 This means that `git reset -p` is the opposite of `git add -p`, i.e.
 you can use it to selectively reset hunks. See the ``Interactive Mode''
-section of linkgit:git-add[1] to learn how to operate the `\--patch` mode.
+section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
 
 'git reset' --<mode> [<commit>]::
        This form resets the current branch head to <commit> and
index 8023dc086d044d0ac2cab685ac1c8b050adda95b..4cc3e9586fcfd79ddeb7f636141e1a878285b0b5 100644 (file)
@@ -113,15 +113,14 @@ OPTIONS
 +
 If a `pattern` is given, only refs matching the given shell glob are
 shown.  If the pattern does not contain a globbing character (`?`,
-`{asterisk}`, or `[`), it is turned into a prefix match by
-appending `/{asterisk}`.
+`*`, or `[`), it is turned into a prefix match by appending `/*`.
 
 --glob=pattern::
        Show all refs matching the shell glob pattern `pattern`. If
        the pattern does not start with `refs/`, this is automatically
        prepended.  If the pattern does not contain a globbing
-       character (`?`, `{asterisk}`, or `[`), it is turned into a prefix
-       match by appending `/{asterisk}`.
+       character (`?`, `*`, or `[`), it is turned into a prefix
+       match by appending `/*`.
 
 --show-toplevel::
        Show the absolute path of the top-level directory.
@@ -138,7 +137,8 @@ appending `/{asterisk}`.
 
 --git-dir::
        Show `$GIT_DIR` if defined. Otherwise show the path to
-       the .git directory, relative to the current directory.
+       the .git directory. The path shown, when relative, is
+       relative to the current working directory.
 +
 If `$GIT_DIR` is not defined and the current directory
 is not detected to lie in a git repository or work tree
index b699a3458eff439b05049dab94610a57ac62fc0a..70152e8b1eb31e5ed6e7da889a424bc65c1f16c2 100644 (file)
@@ -27,7 +27,7 @@ throw away all uncommitted changes in your working directory, you
 should see linkgit:git-reset[1], particularly the '--hard' option.  If
 you want to extract specific files as they were in another commit, you
 should see linkgit:git-checkout[1], specifically the `git checkout
-<commit> \-- <filename>` syntax.  Take care with these alternatives as
+<commit> -- <filename>` syntax.  Take care with these alternatives as
 both will discard uncommitted changes in your working directory.
 
 OPTIONS
@@ -105,7 +105,7 @@ EXAMPLES
        Revert the changes specified by the fourth last commit in HEAD
        and create a new commit with the reverted changes.
 
-`git revert -n master{tilde}5..master{tilde}2`::
+`git revert -n master~5..master~2`::
 
        Revert the changes done by commits from the fifth last commit
        in master (included) to the third last commit in master
index 665ad4ddab14da173142914407680605dad9dae6..5d31860eb1ddf57251d61d16b34693cebc4fd297 100644 (file)
@@ -79,8 +79,7 @@ a file that you have not told git about does not remove that file.
 
 File globbing matches across directory boundaries.  Thus, given
 two directories `d` and `d2`, there is a difference between
-using `git rm {apostrophe}d{asterisk}{apostrophe}` and
-`git rm {apostrophe}d/{asterisk}{apostrophe}`, as the former will
+using `git rm 'd*'` and `git rm 'd/*'`, as the former will
 also remove all of directory `d2`.
 
 REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
index ff3755b4c72a52595be805e92a9f3b62d1477af2..01d8417316e23bc74f2c86df9004091710437ba7 100644 (file)
@@ -47,7 +47,7 @@ OPTIONS
 --format[=<format>]::
        Instead of the commit subject, use some other information to
        describe each commit.  '<format>' can be any string accepted
-       by the `--format` option of 'git log', such as '{asterisk} [%h] %s'.
+       by the `--format` option of 'git log', such as '* [%h] %s'.
        (See the "PRETTY FORMATS" section of linkgit:git-log[1].)
 
        Each pretty-printed commit will be rewrapped before it is shown.
index fcee0008a9d868bb27c9eacbcfe72a3a3f8ed60f..5dbcd47fec3b605d0b4b3e6ed47c06cc57f1dd6d 100644 (file)
@@ -73,7 +73,7 @@ OPTIONS
 --exclude-existing[=<pattern>]::
 
        Make 'git show-ref' act as a filter that reads refs from stdin of the
-       form "`{caret}(?:<anything>\s)?<refname>(?:{backslash}{caret}{})?$`"
+       form "`^(?:<anything>\s)?<refname>(?:\^{})?$`"
        and performs the following actions on each:
        (1) strip "{caret}{}" at the end of line if any;
        (2) ignore if pattern is provided and does not head-match refname;
index 1e38819e67cda4282ec7ede004ee5668d0a478c8..ae4edcccfbc2c862f4a1f77a5d58c348fbee2def 100644 (file)
@@ -52,10 +52,10 @@ EXAMPLES
        Shows the tag `v1.0.0`, along with the object the tags
        points at.
 
-`git show v1.0.0^\{tree\}`::
+`git show v1.0.0^{tree}`::
        Shows the tree pointed to by the tag `v1.0.0`.
 
-`git show -s --format=%s v1.0.0^\{commit\}`::
+`git show -s --format=%s v1.0.0^{commit}`::
        Shows the subject of the commit pointed to by the
        tag `v1.0.0`.
 
index 43af38aa4bd7673bef2127058f171da039732c71..0aa4e20eaed524ee01ae4bee11873efaedf44124 100644 (file)
@@ -36,8 +36,8 @@ you create one.
 
 The latest stash you created is stored in `refs/stash`; older
 stashes are found in the reflog of this reference and can be named using
-the usual reflog syntax (e.g. `stash@\{0}` is the most recently
-created stash, `stash@\{1}` is the one before it, `stash@\{2.hours.ago}`
+the usual reflog syntax (e.g. `stash@{0}` is the most recently
+created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`
 is also possible).
 
 OPTIONS
@@ -66,7 +66,7 @@ constructed such that its index state is the same as the index state
 of your repository, and its worktree contains only the changes you
 selected interactively.  The selected changes are then rolled back
 from your worktree. See the ``Interactive Mode'' section of
-linkgit:git-add[1] to learn how to operate the `\--patch` mode.
+linkgit:git-add[1] to learn how to operate the `--patch` mode.
 +
 The `--patch` option implies `--keep-index`.  You can use
 `--no-keep-index` to override this.
@@ -74,7 +74,7 @@ The `--patch` option implies `--keep-index`.  You can use
 list [<options>]::
 
        List the stashes that you currently have.  Each 'stash' is listed
-       with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is
+       with its name (e.g. `stash@{0}` is the latest stash, `stash@{1}` is
        the one before, etc.), the name of the branch that was current when the
        stash was made, and a short description of the commit the stash was
        based on.
@@ -93,7 +93,7 @@ show [<stash>]::
        stashed state and its original parent. When no `<stash>` is given,
        shows the latest one. By default, the command shows the diffstat, but
        it will accept any format known to 'git diff' (e.g., `git stash show
-       -p stash@\{1}` to view the second most recent stash in patch form).
+       -p stash@{1}` to view the second most recent stash in patch form).
 
 pop [--index] [-q|--quiet] [<stash>]::
 
@@ -111,8 +111,8 @@ tree's changes, but also the index's ones. However, this can fail, when you
 have conflicts (which are stored in the index, where you therefore can no
 longer apply the changes as they were originally).
 +
-When no `<stash>` is given, `stash@\{0}` is assumed, otherwise `<stash>` must
-be a reference of the form `stash@\{<revision>}`.
+When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
+be a reference of the form `stash@{<revision>}`.
 
 apply [--index] [-q|--quiet] [<stash>]::
 
@@ -143,9 +143,9 @@ clear::
 drop [-q|--quiet] [<stash>]::
 
        Remove a single stashed state from the stash list. When no `<stash>`
-       is given, it removes the latest one. i.e. `stash@\{0}`, otherwise
+       is given, it removes the latest one. i.e. `stash@{0}`, otherwise
        `<stash>` must a valid stash log reference of the form
-       `stash@\{<revision>}`.
+       `stash@{<revision>}`.
 
 create::
 
index 3d51717bbe84d0201b1c7a38943b2e99643bd89f..67e5f53a9ef00991954b6bf95286a418c7e4af23 100644 (file)
@@ -77,6 +77,13 @@ configuration variable documented in linkgit:git-config[1].
        Terminate entries with NUL, instead of LF.  This implies
        the `--porcelain` output format if no other format is given.
 
+--column[=<options>]::
+--no-column::
+       Display untracked files in columns. See configuration variable
+       column.status for option syntax.`--column` and `--no-column`
+       without options are equivalent to 'always' and 'never'
+       respectively.
+
 
 OUTPUT
 ------
@@ -98,12 +105,12 @@ In the short-format, the status of each path is shown as
 
        XY PATH1 -> PATH2
 
-where `PATH1` is the path in the `HEAD`, and the ` \-> PATH2` part is
+where `PATH1` is the path in the `HEAD`, and the " `-> PATH2`" part is
 shown only when `PATH1` corresponds to a different path in the
 index/worktree (i.e. the file is renamed). The 'XY' is a two-letter
 status code.
 
-The fields (including the `\->`) are separated from each other by a
+The fields (including the `->`) are separated from each other by a
 single space. If a filename contains whitespace or other nonprintable
 characters, that field will be quoted in the manner of a C string
 literal: surrounded by ASCII double quote (34) characters, and with
@@ -177,7 +184,7 @@ order is reversed (e.g 'from \-> to' becomes 'to from'). Second, a NUL
 and the terminating newline (but a space still separates the status
 field from the first filename).  Third, filenames containing special
 characters are not specially formatted; no quoting or
-backslash-escaping is performed. Fourth, there is no branch line.
+backslash-escaping is performed.
 
 CONFIGURATION
 -------------
index c243ee552bb4bc715b8903163b0136b32097af04..9e488c0aad433f7478d1334e53f8c5e5dd1878ed 100644 (file)
@@ -43,9 +43,9 @@ if you choose to go that route.
 Submodules are composed from a so-called `gitlink` tree entry
 in the main repository that refers to a particular commit object
 within the inner repository that is completely separate.
-A record in the `.gitmodules` file at the root of the source
-tree assigns a logical name to the submodule and describes
-the default URL the submodule shall be cloned from.
+A record in the `.gitmodules` (see linkgit:gitmodules[5]) file at the
+root of the source tree assigns a logical name to the submodule and
+describes the default URL the submodule shall be cloned from.
 The logical name can be used for overriding this URL within your
 local repository configuration (see 'submodule init').
 
@@ -140,7 +140,8 @@ update::
        checkout the commit specified in the index of the containing repository.
        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`.
+       `rebase`, `merge` or `none`. `none` can be overriden by specifying
+       `--checkout`.
 +
 If the submodule is not yet initialized, and you just want to use the
 setting as stored in .gitmodules, you can automatically initialize the
@@ -148,10 +149,6 @@ submodule with the `--init` option.
 +
 If `--recursive` is specified, this command will recurse into the
 registered submodules, and update any nested submodules within.
-+
-If the configuration key `submodule.$name.update` is set to `none` the
-submodule with name `$name` will not be updated by default. This can be
-overriden by adding `--checkout` to the command.
 
 summary::
        Show commit summary between the given commit (defaults to HEAD) and
index 34ee785064128bce0feb2cfc86d3040b42a8f428..cfe8d2b5df78b85b7a675423f77a3996ebe24092 100644 (file)
@@ -189,18 +189,16 @@ and have no uncommitted changes.
        last fetched commit from the upstream SVN.
 
 'dcommit'::
-       Commit each diff from a specified head directly to the SVN
+       Commit each diff from the current branch directly to the SVN
        repository, and then rebase or reset (depending on whether or
        not there is a diff between SVN and head).  This will create
        a revision in SVN for each commit in git.
-       It is recommended that you run 'git svn' fetch and rebase (not
-       pull or merge) your commits against the latest changes in the
-       SVN repository.
-       An optional revision or branch argument may be specified, and
-       causes 'git svn' to do all work on that revision/branch
-       instead of HEAD.
-       This is advantageous over 'set-tree' (below) because it produces
-       cleaner, more linear history.
++
+When an optional git branch name (or a git commit object name)
+is specified as an argument, the subcommand works on the specified
+branch, not on the current branch.
++
+Use of 'dcommit' is preferred to 'set-tree' (below).
 +
 --no-rebase;;
        After committing, do not rebase or reset.
@@ -572,6 +570,8 @@ config key: svn.repackflags
 --merge::
 -s<strategy>::
 --strategy=<strategy>::
+-p::
+--preserve-merges::
        These are only used with the 'dcommit' and 'rebase' commands.
 +
 Passed directly to 'git rebase' when using 'dcommit' if a
@@ -800,18 +800,19 @@ have each person clone that repository with 'git clone':
 
 REBASE VS. PULL/MERGE
 ---------------------
-
-Originally, 'git svn' recommended that the 'remotes/git-svn' branch be
-pulled or merged from.  This is because the author favored
+Prefer to use 'git svn rebase' or 'git rebase', rather than
+'git pull' or 'git merge' to synchronize unintegrated commits with a 'git svn'
+branch. Doing so will keep the history of unintegrated commits linear with
+respect to the upstream SVN repository and allow the use of the preferred
+'git svn dcommit' subcommand to push unintegrated commits back into SVN.
+
+Originally, 'git svn' recommended that developers pulled or merged from
+the 'git svn' branch.  This was because the author favored
 `git svn set-tree B` to commit a single head rather than the
-`git svn set-tree A..B` notation to commit multiple commits.
-
-If you use `git svn set-tree A..B` to commit several diffs and you do
-not have the latest remotes/git-svn merged into my-branch, you should
-use `git svn rebase` to update your work branch instead of `git pull` or
-`git merge`.  `pull`/`merge` can cause non-linear history to be flattened
-when committing into SVN, which can lead to merge commits reversing
-previous commits in SVN.
+`git svn set-tree A..B` notation to commit multiple commits. Use of
+'git pull' or 'git merge' with `git svn set-tree A..B` will cause non-linear
+history to be flattened when committing into SVN and this can lead to merge
+commits unexpectedly reversing previous commits in SVN.
 
 MERGE TRACKING
 --------------
index 8d32b9a814675c9ebb58c25615a7f647dae20c93..e36a7c3d1e11e38950c74b32ab4f1ac9a4683a41 100644 (file)
@@ -13,6 +13,7 @@ SYNOPSIS
        <tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
+       [--column[=<options>] | --no-column] [<pattern>...]
        [<pattern>...]
 'git tag' -v <tagname>...
 
@@ -84,6 +85,14 @@ OPTIONS
        using fnmatch(3)).  Multiple patterns may be given; if any of
        them matches, the tag is shown.
 
+--column[=<options>]::
+--no-column::
+       Display tag listing in columns. See configuration variable
+       column.tag for option syntax.`--column` and `--no-column`
+       without options are equivalent to 'always' and 'never' respectively.
++
+This option is only applicable when listing tags without annotation lines.
+
 --contains <commit>::
        Only list tags which contain the specified commit.
 
index 346e7a20797c22a29733f09e5fe241c84a46bfa9..f7362dc2d1736a087bc897d870f22859e74dc8a6 100644 (file)
@@ -63,7 +63,7 @@ EXAMPLES
 
        Create a tarball for v1.4.0 release.
 
-`git tar-tree v1.4.0{caret}\{tree\} git-1.4.0 | gzip >git-1.4.0.tar.gz`::
+`git tar-tree v1.4.0^{tree} git-1.4.0 | gzip >git-1.4.0.tar.gz`::
 
        Create a tarball for v1.4.0 release, but without a
        global extended pax header.
index a3081f4e237747dc858fd105302cbb9a489de41b..9d0b1515c58dc825beef6b4507542572c51ca988 100644 (file)
@@ -19,7 +19,7 @@ SYNOPSIS
             [--ignore-submodules]
             [--really-refresh] [--unresolve] [--again | -g]
             [--info-only] [--index-info]
-            [-z] [--stdin]
+            [-z] [--stdin] [--index-version <n>]
             [--verbose]
             [--] [<file>...]
 
@@ -143,6 +143,10 @@ you will need to handle the situation manually.
 --verbose::
         Report what is being added and removed from index.
 
+--index-version <n>::
+       Write the resulting index out in the named on-disk format version.
+       The current default version is 2.
+
 -z::
        Only meaningful with `--stdin` or `--index-info`; paths are
        separated with NUL character instead of LF.
index 5317cc247454b1a080b2609139befeba487d5ff5..988a3234f435ccd55d20f9d0e48c146b55691167 100644 (file)
@@ -43,13 +43,21 @@ GIT_EDITOR::
     `$SOME_ENVIRONMENT_VARIABLE`, `"C:\Program Files\Vim\gvim.exe"
     --nofork`.  The order of preference is the `$GIT_EDITOR`
     environment variable, then `core.editor` configuration, then
-    `$VISUAL`, then `$EDITOR`, and then finally 'vi'.
+    `$VISUAL`, then `$EDITOR`, and then the default chosen at compile
+    time, which is usually 'vi'.
+ifdef::git-default-editor[]
+    The build you are using chose '{git-default-editor}' as the default.
+endif::git-default-editor[]
 
 GIT_PAGER::
     Text viewer for use by git commands (e.g., 'less').  The value
     is meant to be interpreted by the shell.  The order of preference
     is the `$GIT_PAGER` environment variable, then `core.pager`
-    configuration, then `$PAGER`, and then finally 'less'.
+    configuration, then `$PAGER`, and then the default chosen at
+    compile time (usually 'less').
+ifdef::git-default-pager[]
+    The build you are using chose '{git-default-pager}' as the default.
+endif::git-default-pager[]
 
 Diagnostics
 -----------
index 76c7f7eec5bb8ba01262762909939070b55ef5c9..6c8f510c3f6398630da248fd356a00f69a6d9471 100644 (file)
@@ -58,7 +58,7 @@ Examples
        Show as patches the commits since version 'v2.6.12' that changed
        any file in the include/scsi or drivers/scsi subdirectories
 
-`git whatchanged --since="2 weeks ago" \-- gitk`::
+`git whatchanged --since="2 weeks ago" -- gitk`::
 
        Show the changes during the last two weeks to the file 'gitk'.
        The "--" is necessary to avoid confusion with the *branch* named
index ca85d1d210dc6015fef205e543f34b6f25733fca..c54321312633030879ecf003ee0cc17fe95c0137 100644 (file)
@@ -44,14 +44,17 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.10/git.html[documentation for release 1.7.10]
+* link:v1.7.10.2/git.html[documentation for release 1.7.10.2]
 
 * release notes for
+  link:RelNotes/1.7.10.2.txt[1.7.10.2],
+  link:RelNotes/1.7.10.1.txt[1.7.10.1],
   link:RelNotes/1.7.10.txt[1.7.10].
 
-* link:v1.7.9.6/git.html[documentation for release 1.7.9.6]
+* link:v1.7.9.7/git.html[documentation for release 1.7.9.7]
 
 * release notes for
+  link:RelNotes/1.7.9.7.txt[1.7.9.7],
   link:RelNotes/1.7.9.6.txt[1.7.9.6],
   link:RelNotes/1.7.9.5.txt[1.7.9.5],
   link:RelNotes/1.7.9.4.txt[1.7.9.4],
@@ -60,9 +63,10 @@ Documentation for older releases are available here:
   link:RelNotes/1.7.9.1.txt[1.7.9.1],
   link:RelNotes/1.7.9.txt[1.7.9].
 
-* link:v1.7.8.5/git.html[documentation for release 1.7.8.5]
+* link:v1.7.8.6/git.html[documentation for release 1.7.8.6]
 
 * release notes for
+  link:RelNotes/1.7.8.6.txt[1.7.8.6],
   link:RelNotes/1.7.8.5.txt[1.7.8.5],
   link:RelNotes/1.7.8.4.txt[1.7.8.4],
   link:RelNotes/1.7.8.3.txt[1.7.8.3],
@@ -70,9 +74,10 @@ Documentation for older releases are available here:
   link:RelNotes/1.7.8.1.txt[1.7.8.1],
   link:RelNotes/1.7.8.txt[1.7.8].
 
-* link:v1.7.7.6/git.html[documentation for release 1.7.7.6]
+* link:v1.7.7.7/git.html[documentation for release 1.7.7.7]
 
 * release notes for
+  link:RelNotes/1.7.7.7.txt[1.7.7.7],
   link:RelNotes/1.7.7.6.txt[1.7.7.6],
   link:RelNotes/1.7.7.5.txt[1.7.7.5],
   link:RelNotes/1.7.7.4.txt[1.7.7.4],
index f734f97b8e1b64f2d3bfc926e92e9226ba716289..ea17f7a53b664d5e9e86ceb814c5cc7430035125 100644 (file)
@@ -25,22 +25,22 @@ arguments.  Here are the rules:
    are paths.
 
  * When an argument can be misunderstood as either a revision or a path,
-   they can be disambiguated by placing `\--` between them.
-   E.g. `git diff \-- HEAD` is, "I have a file called HEAD in my work
+   they can be disambiguated by placing `--` between them.
+   E.g. `git diff -- HEAD` is, "I have a file called HEAD in my work
    tree.  Please show changes between the version I staged in the index
    and what I have in the work tree for that file". not "show difference
    between the HEAD commit and the work tree as a whole".  You can say
-   `git diff HEAD \--` to ask for the latter.
+   `git diff HEAD --` to ask for the latter.
 
- * Without disambiguating `\--`, git makes a reasonable guess, but errors
+ * Without disambiguating `--`, git makes a reasonable guess, but errors
    out and asking you to disambiguate when ambiguous.  E.g. if you have a
    file called HEAD in your work tree, `git diff HEAD` is ambiguous, and
-   you have to say either `git diff HEAD \--` or `git diff \-- HEAD` to
+   you have to say either `git diff HEAD --` or `git diff -- HEAD` to
    disambiguate.
 
 When writing a script that is expected to handle random user-input, it is
 a good practice to make it explicit which arguments are which by placing
-disambiguating `\--` at appropriate places.
+disambiguating `--` at appropriate places.
 
 Here are the rules regarding the "flags" that you should follow when you are
 scripting git:
index fb0d5692a4d2e898da0dea9c346420467722badc..9d893369a00b6a924876fb234c1082db2021abf3 100644 (file)
@@ -151,8 +151,8 @@ to your working tree, you use the 'git update-index' program. That
 program normally just takes a list of filenames you want to update, but
 to avoid trivial mistakes, it refuses to add new entries to the index
 (or remove existing ones) unless you explicitly tell it that you're
-adding a new entry with the `\--add` flag (or removing an entry with the
-`\--remove`) flag.
+adding a new entry with the `--add` flag (or removing an entry with the
+`--remove`) flag.
 
 So to populate the index with the two files you just created, you can do
 
@@ -399,10 +399,10 @@ $ git diff HEAD
 which ends up doing the above for you.
 
 In other words, 'git diff-index' normally compares a tree against the
-working tree, but when given the `\--cached` flag, it is told to
+working tree, but when given the `--cached` flag, it is told to
 instead compare against just the index cache contents, and ignore the
 current working tree state entirely. Since we just wrote the index
-file to HEAD, doing `git diff-index \--cached -p HEAD` should thus return
+file to HEAD, doing `git diff-index --cached -p HEAD` should thus return
 an empty set of differences, and that's exactly what it does.
 
 [NOTE]
@@ -411,7 +411,7 @@ an empty set of differences, and that's exactly what it does.
 comparisons, and saying that it compares a tree against the working
 tree is thus not strictly accurate. In particular, the list of
 files to compare (the "meta-data") *always* comes from the index file,
-regardless of whether the `\--cached` flag is used or not. The `\--cached`
+regardless of whether the `--cached` flag is used or not. The `--cached`
 flag really only determines whether the file *contents* to be compared
 come from the working tree or not.
 
@@ -433,7 +433,7 @@ update the index cache:
 $ git update-index hello
 ------------------------------------------------
 
-(note how we didn't need the `\--add` flag this time, since git knew
+(note how we didn't need the `--add` flag this time, since git knew
 about the file already).
 
 Note what happens to the different 'git diff-{asterisk}' versions here.
@@ -560,7 +560,7 @@ short history.
 When using the above two commands, the initial commit will be shown.
 If this is a problem because it is huge, you can hide it by setting
 the log.showroot configuration variable to false. Having this, you
-can still show it for each command just adding the `\--root` option,
+can still show it for each command just adding the `--root` option,
 which is a flag for 'git diff-tree' accepted by both commands.
 
 With that, you should now be having some inkling of what git does, and
@@ -881,7 +881,7 @@ helps you view what's going on:
 $ gitk --all
 ----------------
 
-will show you graphically both of your branches (that's what the `\--all`
+will show you graphically both of your branches (that's what the `--all`
 means: normally it will just show you your current `HEAD`) and their
 histories. You can also see exactly how they came to be from a common
 source.
@@ -935,7 +935,7 @@ which will very loudly warn you that you're now committing a merge
 (which is correct, so never mind), and you can write a small merge
 message about your adventures in 'git merge'-land.
 
-After you're done, start up `gitk \--all` to see graphically what the
+After you're done, start up `gitk --all` to see graphically what the
 history looks like. Notice that `mybranch` still exists, and you can
 switch to it, and continue to work with it if you want to. The
 `mybranch` branch will not contain the merge, but next time you merge it
@@ -958,11 +958,11 @@ $ git show-branch --topo-order --more=1 master mybranch
 The first two lines indicate that it is showing the two branches
 and the first line of the commit log message from their
 top-of-the-tree commits, you are currently on `master` branch
-(notice the asterisk `{asterisk}` character), and the first column for
+(notice the asterisk `*` character), and the first column for
 the later output lines is used to show commits contained in the
 `master` branch, and the second column for the `mybranch`
 branch. Three commits are shown along with their log messages.
-All of them have non blank characters in the first column (`{asterisk}`
+All of them have non blank characters in the first column (`*`
 shows an ordinary commit on the current branch, `-` is a merge commit), which
 means they are now part of the `master` branch. Only the "Some
 work" commit has the plus `+` character in the second column,
@@ -1002,8 +1002,8 @@ would be different)
 ----------------
 Updating from ae3a2da... to a80b4aa....
 Fast-forward (no commit created; -m option ignored)
- example |    1 +
- hello   |    1 +
+ example | 1 +
+ hello   | 1 +
  2 files changed, 2 insertions(+)
 ----------------
 
@@ -1013,7 +1013,7 @@ not actually do a merge. Instead, it just updated the top of
 the tree of your branch to that of the `master` branch. This is
 often called 'fast-forward' merge.
 
-You can run `gitk \--all` again to see how the commit ancestry
+You can run `gitk --all` again to see how the commit ancestry
 looks like, or run 'show-branch', which tells you this.
 
 ------------------------------------------------
@@ -1257,7 +1257,7 @@ this 'collapsing' tends to trivially merge most of the paths
 fairly quickly, leaving only a handful of real changes in non-zero
 stages.
 
-To look at only non-zero stages, use `\--unmerged` flag:
+To look at only non-zero stages, use `--unmerged` flag:
 
 ------------
 $ git ls-files --unmerged
@@ -1420,7 +1420,7 @@ packed, and stores the packed file in `.git/objects/pack`
 directory.
 
 [NOTE]
-You will see two files, `pack-{asterisk}.pack` and `pack-{asterisk}.idx`,
+You will see two files, `pack-*.pack` and `pack-*.idx`,
 in `.git/objects/pack` directory. They are closely related to
 each other, and if you ever copy them by hand to a different
 repository for whatever reason, you should make sure you copy
index 066f825f2e116becb90404246c0bdadebc72c1f7..7dfffc00461856924625aa3af1951103da0b7367 100644 (file)
@@ -143,8 +143,8 @@ CONFIGURATION OPTIONS
 ---------------------
 
 Options for a credential context can be configured either in
-`credential.\*` (which applies to all credentials), or
-`credential.<url>.\*`, where <url> matches the context as described
+`credential.*` (which applies to all credentials), or
+`credential.<url>.*`, where <url> matches the context as described
 above.
 
 The following options are available in either location:
index 370624c17174ddc67d4d29fd54127096f4f5b070..daf1782a31cb2dee704a6895939afa638d3736fc 100644 (file)
@@ -168,11 +168,11 @@ a similarity score different from the default of 50% by giving a
 number after the "-M" or "-C" option (e.g. "-M8" to tell it to use
 8/10 = 80%).
 
-Note.  When the "-C" option is used with `\--find-copies-harder`
+Note.  When the "-C" option is used with `--find-copies-harder`
 option, 'git diff-{asterisk}' commands feed unmodified filepairs to
 diffcore mechanism as well as modified ones.  This lets the copy
 detector consider unmodified files as copy source candidates at
-the expense of making it slower.  Without `\--find-copies-harder`,
+the expense of making it slower.  Without `--find-copies-harder`,
 'git diff-{asterisk}' commands can detect copies only if the file that was
 copied happened to have been modified in the same changeset.
 
@@ -224,7 +224,7 @@ 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-{asterisk}'
+-S option and the `--pickaxe-all` option to the 'git diff-*'
 commands.
 
 When diffcore-pickaxe is in use, it checks if there are
@@ -233,9 +233,9 @@ 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
+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
+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
index 28edefa202fb0d1065f161f59787ceae39439eb3..b9003fed248651a7783e042c2e3668cf3ff90d0f 100644 (file)
@@ -73,7 +73,7 @@ pre-commit
 ~~~~~~~~~~
 
 This hook is invoked by 'git commit', and can be bypassed
-with `\--no-verify` option.  It takes no parameter, and is
+with `--no-verify` option.  It takes no parameter, and is
 invoked before obtaining the proposed commit log message and
 making a commit.  Exiting with non-zero status from this script
 causes the 'git commit' to abort.
@@ -99,12 +99,12 @@ 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 SHA1 (if a `-c`, `-C` or `--amend` option was given).
 
 If the exit status is non-zero, 'git commit' will abort.
 
 The purpose of the hook is to edit the message file in place, and
-it is not suppressed by the `\--no-verify` option.  A non-zero exit
+it is not suppressed by the `--no-verify` option.  A non-zero exit
 means a failure of the hook and aborts the commit.  It should not
 be used as replacement for pre-commit hook.
 
@@ -115,7 +115,7 @@ commit-msg
 ~~~~~~~~~~
 
 This hook is invoked by 'git commit', and can be bypassed
-with `\--no-verify` option.  It takes a single parameter, the
+with `--no-verify` option.  It takes a single parameter, the
 name of the file that holds the proposed commit log message.
 Exiting with non-zero status causes the 'git commit' to
 abort.
index 4e1fd52e7d230c8f5e55960e960a68ef5875fe76..4effd789026e48085d6b0834cbef990968bedbb2 100644 (file)
@@ -41,8 +41,11 @@ submodule.<name>.update::
        the commit specified in the superproject. If 'merge', the commit
        specified in the superproject will be merged into the current branch
        in the submodule.
+       If 'none', the submodule with name `$name` will not be updated
+       by default.
+
        This config option is overridden if 'git submodule update' is given
-       the '--merge' or '--rebase' options.
+       the '--merge', '--rebase' or '--checkout' options.
 
 submodule.<name>.fetchRecurseSubmodules::
        This option can be used to control recursive fetching of this
index 7aba497b74540499f45ca135bc094ecdffff240a..b9dd56753a5e45487d1dc0fde81f8a9aaef47e43 100644 (file)
@@ -499,6 +499,13 @@ $maxload::
 Set `$maxload` to undefined value (`undef`) to turn this feature off.
 The default value is 300.
 
+$omit_age_column::
+       If true, omit the column with date of the most current commit on the
+       projects list page. It can save a bit of I/O and a fork per repository.
+
+$omit_owner::
+       If true prevents displaying information about repository owner.
+
 $per_request_config::
        If this is set to code reference, it will be run once for each request.
        You can set parts of configuration that change per session this way.
@@ -749,14 +756,14 @@ Project specific override is not supported.
 forks::
        If this feature is enabled, gitweb considers projects in
        subdirectories of project root (basename) to be forks of existing
-       projects.  For each project `$projname.git`, projects in the
-       `$projname/` directory and its subdirectories will not be
-       shown in the main projects list.  Instead, a \'+' mark is shown
-       next to `$projname`, which links to a "forks" view that lists all
-       the forks (all projects in `$projname/` subdirectory).  Additionally
+       projects.  For each project +$projname.git+, projects in the
+       +$projname/+ directory and its subdirectories will not be
+       shown in the main projects list.  Instead, a \'\+' mark is shown
+       next to +$projname+, which links to a "forks" view that lists all
+       the forks (all projects in +$projname/+ subdirectory).  Additionally
        a "forks" view for a project is linked from project summary page.
 +
-If the project list is taken from a file (`$projects_list` points to a
+If the project list is taken from a file (+$projects_list+ points to a
 file), forks are only recognized if they are listed after the main project
 in that file.
 +
index 5e4f362ff8073bc5fc5f651f5219834d6173517b..8b8c6ae5d3f3a3a12b875b106a3bbb5642c8e172 100644 (file)
@@ -39,8 +39,8 @@ To achieve this, try to split your work into small steps from the very
 beginning. It is always easier to squash a few commits together than
 to split one big commit into several.  Don't be afraid of making too
 small or imperfect steps along the way. You can always go back later
-and edit the commits with `git rebase \--interactive` before you
-publish them.  You can use `git stash save \--keep-index` to run the
+and edit the commits with `git rebase --interactive` before you
+publish them.  You can use `git stash save --keep-index` to run the
 test suite independent of other uncommitted changes; see the EXAMPLES
 section of linkgit:git-stash[1].
 
index 880b6f2e6f946ab6fc0ff4850759908364d50854..e3d8a83b23aff51c8dd4281f18de8661514b62b1 100644 (file)
@@ -130,8 +130,8 @@ The placeholders are:
 - '%b': body
 - '%B': raw body (unwrapped subject and body)
 - '%N': commit notes
-- '%gD': reflog selector, e.g., `refs/stash@\{1\}`
-- '%gd': shortened reflog selector, e.g., `stash@\{1\}`
+- '%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])
 - '%ge': reflog identity email
@@ -155,7 +155,7 @@ insert an empty string unless we are traversing reflog entries (e.g., by
 `git log -g`). The `%d` placeholder will use the "short" decoration
 format if `--decorate` was not already provided on the command line.
 
-If you add a `{plus}` (plus sign) after '%' of a placeholder, a line-feed
+If you add a `+` (plus sign) after '%' of a placeholder, a line-feed
 is inserted immediately before the expansion if and only if the
 placeholder expands to a non-empty string.
 
index 5dd6e5a0c7e7f215d4de783a2751e98e41c69948..94a9d32f1d5bd66ac359103d21e671e08140b745 100644 (file)
@@ -13,7 +13,7 @@ endif::git-pull[]
 
 <refspec>::
        The format of a <refspec> parameter is an optional plus
-       `{plus}`, followed by the source ref <src>, followed
+       `+`, followed by the source ref <src>, followed
        by a colon `:`, followed by the destination ref <dst>.
 +
 The remote ref that matches <src>
index 6a4b6355ba91d60ebf2279903a25032256f2063b..1ae3c899ef4bb6d37bc5cbcb25ed927011378d90 100644 (file)
@@ -198,7 +198,7 @@ excluded from the output.
 +
 For example, `--cherry-pick --right-only A...B` omits those
 commits from `B` which are in `A` or are patch-equivalent to a commit in
-`A`. In other words, this lists the `{plus}` commits from `git cherry A B`.
+`A`. In other words, this lists the `+` commits from `git cherry A B`.
 More precisely, `--cherry-pick --right-only --no-merges` gives the exact
 list.
 
@@ -455,7 +455,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` and `P` over '--full-history':
 +
 --
 * `N`'s parent list had `I` removed, because it is an ancestor of the
@@ -494,7 +494,7 @@ of course).
 When we want to find out what commits in `M` are contaminated with the
 bug introduced by `D` and need fixing, however, we might want to view
 only the subset of 'D..M' that are actually descendants of `D`, i.e.
-excluding `C` and `K`. This is exactly what the '\--ancestry-path'
+excluding `C` and `K`. This is exactly what the '--ancestry-path'
 option does. Applied to the 'D..M' range, it results in:
 +
 -----------------------------------------------------------------------
index 49b3d529526ebc109b380c9cb203a38571c5b15c..1b7d8f140c27d76cfa460c0839c44c6742110df7 100644 (file)
@@ -37,6 +37,11 @@ Functions
 `argv_array_push`::
        Push a copy of a string onto the end of the array.
 
+`argv_array_pushl`::
+       Push a list of strings onto the end of the array. The arguments
+       should be a list of `const char *` strings, terminated by a NULL
+       argument.
+
 `argv_array_pushf`::
        Format a string and push it onto the end of the array. This is a
        convenience wrapper combining `strbuf_addf` and `argv_array_push`.
index 2527b7e8d7516285aee4a85bf145f391e89463a7..3062389404e2d4b5f45fa7f5a324e65f933e08b0 100644 (file)
@@ -21,7 +21,7 @@ that allow to change the behavior of a command.
 * There are basically two forms of options:
   'Short options' consist of one dash (`-`) and one alphanumeric
   character.
-  'Long options' begin with two dashes (`\--`) and some
+  'Long options' begin with two dashes (`--`) and some
   alphanumeric characters.
 
 * Options are case-sensitive.
@@ -31,7 +31,7 @@ The parse-options API allows:
 
 * 'sticked' and 'separate form' of options with arguments.
   `-oArg` is sticked, `-o Arg` is separate form.
-  `\--option=Arg` is sticked, `\--option Arg` is separate form.
+  `--option=Arg` is sticked, `--option Arg` is separate form.
 
 * Long options may be 'abbreviated', as long as the abbreviation
   is unambiguous.
@@ -39,12 +39,12 @@ The parse-options API allows:
 * Short options may be bundled, e.g. `-a -b` can be specified as `-ab`.
 
 * Boolean long options can be 'negated' (or 'unset') by prepending
-  `no-`, e.g. `\--no-abbrev` instead of `\--abbrev`. Conversely,
+  `no-`, e.g. `--no-abbrev` instead of `--abbrev`. Conversely,
   options that begin with `no-` can be 'negated' by removing it.
 
-* Options and non-option arguments can clearly be separated using the `\--`
-  option, e.g. `-a -b \--option \-- \--this-is-a-file` indicates that
-  `\--this-is-a-file` must not be processed as an option.
+* Options and non-option arguments can clearly be separated using the `--`
+  option, e.g. `-a -b --option -- --this-is-a-file` indicates that
+  `--this-is-a-file` must not be processed as an option.
 
 Steps to parse options
 ----------------------
@@ -76,7 +76,7 @@ before the full parser, which in turn shows the full help message.
 Flags are the bitwise-or of:
 
 `PARSE_OPT_KEEP_DASHDASH`::
-       Keep the `\--` that usually separates options from
+       Keep the `--` that usually separates options from
        non-option arguments.
 
 `PARSE_OPT_STOP_AT_NON_OPTION`::
@@ -114,22 +114,22 @@ say `static struct option builtin_add_options[]`.
 There are some macros to easily define options:
 
 `OPT__ABBREV(&int_var)`::
-       Add `\--abbrev[=<n>]`.
+       Add `--abbrev[=<n>]`.
 
 `OPT__COLOR(&int_var, description)`::
-       Add `\--color[=<when>]` and `--no-color`.
+       Add `--color[=<when>]` and `--no-color`.
 
 `OPT__DRY_RUN(&int_var, description)`::
-       Add `-n, \--dry-run`.
+       Add `-n, --dry-run`.
 
 `OPT__FORCE(&int_var, description)`::
-       Add `-f, \--force`.
+       Add `-f, --force`.
 
 `OPT__QUIET(&int_var, description)`::
-       Add `-q, \--quiet`.
+       Add `-q, --quiet`.
 
 `OPT__VERBOSE(&int_var, description)`::
-       Add `-v, \--verbose`.
+       Add `-v, --verbose`.
 
 `OPT_GROUP(description)`::
        Start an option group. `description` is a short string that
@@ -216,10 +216,10 @@ The last element of the array must be `OPT_END()`.
 If not stated otherwise, interpret the arguments as follows:
 
 * `short` is a character for the short option
-  (e.g. `{apostrophe}e{apostrophe}` for `-e`, use `0` to omit),
+  (e.g. `'e'` for `-e`, use `0` to omit),
 
 * `long` is a string for the long option
-  (e.g. `"example"` for `\--example`, use `NULL` to omit),
+  (e.g. `"example"` for `--example`, use `NULL` to omit),
 
 * `int_var` is an integer variable,
 
@@ -243,10 +243,10 @@ The function must be defined in this form:
 The callback mechanism is as follows:
 
 * Inside `func`, the only interesting member of the structure
-  given by `opt` is the void pointer `opt\->value`.
-  `\*opt\->value` will be the value that is saved into `var`, if you
+  given by `opt` is the void pointer `opt->value`.
+  `*opt->value` will be the value that is saved into `var`, if you
   use `OPT_CALLBACK()`.
-  For example, do `*(unsigned long *)opt\->value = 42;` to get 42
+  For example, do `*(unsigned long *)opt->value = 42;` to get 42
   into an `unsigned long` variable.
 
 * Return value `0` indicates success and non-zero return
index 996da0503acfa3e3a0ed0f57a951d0fbc1500fb8..b7d0d9a8a7b45f4988c0ee8170fec25c415cc918 100644 (file)
@@ -56,6 +56,11 @@ function.
        returning a `struct commit *` each time you call it. The end of the
        revision list is indicated by returning a NULL pointer.
 
+`reset_revision_walk`::
+
+       Reset the flags used by the revision walking api. You can use
+       this to do multiple sequencial revision walks.
+
 Data structures
 ---------------
 
index 8930b3fabceebfc7e03ddabdf5f7bfb228babf6b..9d25b30178e30458deded19a77f66e7dd0df1227 100644 (file)
@@ -113,9 +113,22 @@ GIT index format
     are encoded in 7-bit ASCII and the encoding cannot contain a NUL
     byte (iow, this is a UNIX pathname).
 
+  (Version 4) In version 4, the entry path name is prefix-compressed
+    relative to the path name for the previous entry (the very first
+    entry is encoded as if the path name for the previous entry is an
+    empty string).  At the beginning of an entry, an integer N in the
+    variable width encoding (the same encoding as the offset is encoded
+    for OFS_DELTA pack entries; see pack-format.txt) is stored, followed
+    by a NUL-terminated string S.  Removing N bytes from the end of the
+    path name for the previous entry, and replacing it with the string S
+    yields the path name for this entry.
+
   1-8 nul bytes as necessary to pad the entry to a multiple of eight bytes
   while keeping the name NUL-terminated.
 
+  (Version 4) In version 4, the padding after the pathname does not
+  exist.
+
 == Extensions
 
 === Cached tree
index 546980c0a41ce9ba6d09ad5038b4412b7ef42cc7..49cdc571cd7e276df2913b0ccf9d1e2320b31c9d 100644 (file)
@@ -351,7 +351,7 @@ Then the server will start sending its packfile data.
 A simple clone may look like this (with no 'have' lines):
 
 ----
-   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d\0multi_ack \
+   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \
      side-band-64k ofs-delta\n
    C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n
    C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n
@@ -367,7 +367,7 @@ A simple clone may look like this (with no 'have' lines):
 An incremental update (fetch) response might look like this:
 
 ----
-   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d\0multi_ack \
+   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \
      side-band-64k ofs-delta\n
    C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n
    C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n
index d30a1b9510c55403c94e7b9ecfe211df9b463832..fb7ff084f8e55a06b349db6f7e47c3f64a886a0e 100644 (file)
@@ -36,7 +36,7 @@ More specifically, they:
 
 . They cannot have ASCII control characters (i.e. bytes whose
   values are lower than \040, or \177 `DEL`), space, tilde `~`,
-  caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
+  caret `^`, colon `:`, question-mark `?`, asterisk `*`,
   or open bracket `[` anywhere.
 
 . They cannot end with a slash `/` nor a dot `.`.
index 6c7fee7ef7669c7e8789ad0c7273fd190f63c18c..1b942074b69fa3d23b96738bab952a3dbb9c9174 100644 (file)
@@ -1611,7 +1611,7 @@ Recovering lost changes
 Reflogs
 ^^^^^^^
 
-Say you modify a branch with `linkgit:git-reset[1] --hard`, and then
+Say you modify a branch with +linkgit:git-reset[1] \--hard+, and then
 realize that the branch was the only reference you had to that point in
 history.
 
@@ -4207,7 +4207,7 @@ commits one by one with the function `get_revision()`.
 
 If you are interested in more details of the revision walking process,
 just have a look at the first implementation of `cmd_log()`; call
-`git show v1.3.0{tilde}155^2{tilde}4` and scroll down to that function (note that you
+`git show v1.3.0~155^2~4` and scroll down to that function (note that you
 no longer need to call `setup_pager()` directly).
 
 Nowadays, `git log` is a builtin, which means that it is _contained_ in the
@@ -4270,9 +4270,9 @@ Two things are interesting here:
   negative numbers in case of different errors--and 0 on success.
 
 - the variable `sha1` in the function signature of `get_sha1()` is `unsigned
-  char {asterisk}`, but is actually expected to be a pointer to `unsigned
+  char *`, but is actually expected to be a pointer to `unsigned
   char[20]`.  This variable will contain the 160-bit SHA-1 of the given
-  commit.  Note that whenever a SHA-1 is passed as `unsigned char {asterisk}`, it
+  commit.  Note that whenever a SHA-1 is passed as `unsigned char *`, it
   is the binary representation, as opposed to the ASCII representation in
   hex characters, which is passed as `char *`.
 
index 1f55d3e90027a0e130b5a2c95f01894b15cf2c2b..c92dbed2ecac1061770400af9c338c92d68bd1e5 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.10
+DEF_VER=v1.7.10.GIT
 
 LF='
 '
@@ -12,7 +12,7 @@ if test -f version
 then
        VN=$(cat version) || VN="$DEF_VER"
 elif test -d .git -o -f .git &&
-       VN=$(git describe --match "v[0-9]*" --abbrev=4 HEAD 2>/dev/null) &&
+       VN=$(git describe --match "v[0-9]*" --abbrev=7 HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
        v[0-9]*)
diff --git a/INSTALL b/INSTALL
index 58b2b86ccf93d045d4c406fe422e94db75533607..87e03bbfd48c5bceea6d3f3bd61dc166a5e0a79f 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -131,6 +131,9 @@ Issues of note:
          use English. Under autoconf the configure script will do this
          automatically if it can't find libintl on the system.
 
+       - Python version 2.6 or later is needed to use the git-p4
+         interface to Perforce.
+
  - Some platform specific issues are dealt with Makefile rules,
    but depending on your specific installation, you may not
    have all the libraries/tools needed, or you may have
index be1957a5e986d2e0581123dfe4e0eeb6702c13dc..96ebcf9830326a61313cea792f807c6d770bbf0b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -247,6 +247,9 @@ all::
 # Define NO_CROSS_DIRECTORY_HARDLINKS if you plan to distribute the installed
 # programs as a tar, where bin/ and libexec/ might be on different file systems.
 #
+# Define NO_INSTALL_HARDLINKS if you prefer to use either symbolic links or
+# copies to install built-in git commands e.g. git-cat-file.
+#
 # Define USE_NED_ALLOCATOR if you want to replace the platforms default
 # memory allocators with the nedmalloc allocator written by Niall Douglas.
 #
@@ -288,6 +291,11 @@ all::
 # dependency rules.
 #
 # Define NATIVE_CRLF if your platform uses CRLF for line endings.
+#
+# Define XDL_FAST_HASH to use an alternative line-hashing method in
+# the diff algorithm.  It gives a nice speedup if your processor has
+# fast unaligned word loads.  Does NOT work on big-endian systems!
+# Enabled by default on x86_64.
 
 GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -386,6 +394,7 @@ XDIFF_OBJS =
 VCSSVN_H =
 VCSSVN_OBJS =
 VCSSVN_TEST_OBJS =
+MISC_H =
 EXTRA_CPPFLAGS =
 LIB_H =
 LIB_OBJS =
@@ -440,6 +449,7 @@ SCRIPT_PERL += git-send-email.perl
 SCRIPT_PERL += git-svn.perl
 
 SCRIPT_PYTHON += git-remote-testgit.py
+SCRIPT_PYTHON += git-p4.py
 
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
@@ -454,15 +464,15 @@ EXTRA_PROGRAMS =
 # ... and all the rest that could be moved out of bindir to gitexecdir
 PROGRAMS += $(EXTRA_PROGRAMS)
 
+PROGRAM_OBJS += credential-store.o
 PROGRAM_OBJS += daemon.o
 PROGRAM_OBJS += fast-import.o
+PROGRAM_OBJS += http-backend.o
 PROGRAM_OBJS += imap-send.o
+PROGRAM_OBJS += sh-i18n--envsubst.o
 PROGRAM_OBJS += shell.o
 PROGRAM_OBJS += show-index.o
 PROGRAM_OBJS += upload-pack.o
-PROGRAM_OBJS += http-backend.o
-PROGRAM_OBJS += sh-i18n--envsubst.o
-PROGRAM_OBJS += credential-store.o
 
 # Binary suffix, set to .exe for Windows builds
 X =
@@ -475,15 +485,17 @@ TEST_PROGRAMS_NEED_X += test-ctype
 TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
-TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
 TEST_PROGRAMS_NEED_X += test-genrandom
 TEST_PROGRAMS_NEED_X += test-index-version
 TEST_PROGRAMS_NEED_X += test-line-buffer
 TEST_PROGRAMS_NEED_X += test-match-trees
+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-revision-walking
 TEST_PROGRAMS_NEED_X += test-run-command
+TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
 TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
 TEST_PROGRAMS_NEED_X += test-subprocess
@@ -543,6 +555,36 @@ LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 VCSSVN_LIB=vcs-svn/lib.a
 
+XDIFF_H += xdiff/xinclude.h
+XDIFF_H += xdiff/xmacros.h
+XDIFF_H += xdiff/xdiff.h
+XDIFF_H += xdiff/xtypes.h
+XDIFF_H += xdiff/xutils.h
+XDIFF_H += xdiff/xprepare.h
+XDIFF_H += xdiff/xdiffi.h
+XDIFF_H += xdiff/xemit.h
+
+VCSSVN_H += vcs-svn/line_buffer.h
+VCSSVN_H += vcs-svn/sliding_window.h
+VCSSVN_H += vcs-svn/repo_tree.h
+VCSSVN_H += vcs-svn/fast_export.h
+VCSSVN_H += vcs-svn/svndiff.h
+VCSSVN_H += vcs-svn/svndump.h
+
+MISC_H += bisect.h
+MISC_H += branch.h
+MISC_H += bundle.h
+MISC_H += common-cmds.h
+MISC_H += fetch-pack.h
+MISC_H += reachable.h
+MISC_H += send-pack.h
+MISC_H += shortlog.h
+MISC_H += tar.h
+MISC_H += thread-utils.h
+MISC_H += url.h
+MISC_H += walker.h
+MISC_H += wt-status.h
+
 LIB_H += advice.h
 LIB_H += archive.h
 LIB_H += argv-array.h
@@ -559,18 +601,18 @@ LIB_H += compat/cygwin.h
 LIB_H += compat/mingw.h
 LIB_H += compat/obstack.h
 LIB_H += compat/terminal.h
+LIB_H += compat/win32/dirent.h
+LIB_H += compat/win32/poll.h
 LIB_H += compat/win32/pthread.h
 LIB_H += compat/win32/syslog.h
-LIB_H += compat/win32/poll.h
-LIB_H += compat/win32/dirent.h
 LIB_H += connected.h
 LIB_H += convert.h
 LIB_H += credential.h
 LIB_H += csum-file.h
 LIB_H += decorate.h
 LIB_H += delta.h
-LIB_H += diffcore.h
 LIB_H += diff.h
+LIB_H += diffcore.h
 LIB_H += dir.h
 LIB_H += exec_cmd.h
 LIB_H += fmt-merge-msg.h
@@ -590,6 +632,7 @@ LIB_H += log-tree.h
 LIB_H += mailmap.h
 LIB_H += merge-file.h
 LIB_H += merge-recursive.h
+LIB_H += mergesort.h
 LIB_H += notes.h
 LIB_H += notes-cache.h
 LIB_H += notes-merge.h
@@ -627,6 +670,7 @@ LIB_H += tree-walk.h
 LIB_H += unpack-trees.h
 LIB_H += userdiff.h
 LIB_H += utf8.h
+LIB_H += varint.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
 
@@ -647,6 +691,7 @@ LIB_OBJS += bulk-checkin.o
 LIB_OBJS += bundle.o
 LIB_OBJS += cache-tree.o
 LIB_OBJS += color.o
+LIB_OBJS += column.o
 LIB_OBJS += combine-diff.o
 LIB_OBJS += commit.o
 LIB_OBJS += compat/obstack.o
@@ -676,8 +721,8 @@ LIB_OBJS += entry.o
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
-LIB_OBJS += gpg-interface.o
 LIB_OBJS += gettext.o
+LIB_OBJS += gpg-interface.o
 LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
@@ -694,6 +739,7 @@ LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge-file.o
 LIB_OBJS += merge-recursive.o
+LIB_OBJS += mergesort.o
 LIB_OBJS += name-hash.o
 LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
@@ -725,9 +771,9 @@ LIB_OBJS += rerere.o
 LIB_OBJS += resolve-undo.o
 LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
+LIB_OBJS += sequencer.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
-LIB_OBJS += sequencer.o
 LIB_OBJS += sha1-array.o
 LIB_OBJS += sha1-lookup.o
 LIB_OBJS += sha1_file.o
@@ -752,6 +798,7 @@ LIB_OBJS += url.o
 LIB_OBJS += usage.o
 LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
+LIB_OBJS += varint.o
 LIB_OBJS += walker.o
 LIB_OBJS += wrapper.o
 LIB_OBJS += write_or_die.o
@@ -775,6 +822,7 @@ BUILTIN_OBJS += builtin/checkout-index.o
 BUILTIN_OBJS += builtin/checkout.o
 BUILTIN_OBJS += builtin/clean.o
 BUILTIN_OBJS += builtin/clone.o
+BUILTIN_OBJS += builtin/column.o
 BUILTIN_OBJS += builtin/commit-tree.o
 BUILTIN_OBJS += builtin/commit.o
 BUILTIN_OBJS += builtin/config.o
@@ -864,6 +912,9 @@ EXTLIBS =
 # because maintaining the nesting to match is a pain.  If
 # we had "elif" things would have been much nicer...
 
+ifeq ($(uname_M),x86_64)
+       XDL_FAST_HASH = YesPlease
+endif
 ifeq ($(uname_S),OSF1)
        # Need this for u_short definitions et al
        BASIC_CFLAGS += -D_OSF_SOURCE
@@ -1737,6 +1788,10 @@ ifndef NO_MSGFMT_EXTENDED_OPTIONS
        MSGFMT += --check --statistics
 endif
 
+ifneq (,$(XDL_FAST_HASH))
+       BASIC_CFLAGS += -DXDL_FAST_HASH
+endif
+
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
 endif
@@ -1783,6 +1838,10 @@ ifdef ASCIIDOC7
        export ASCIIDOC7
 endif
 
+ifdef NO_INSTALL_HARDLINKS
+       export NO_INSTALL_HARDLINKS
+endif
+
 ### profile feedback build
 #
 
@@ -1849,6 +1908,13 @@ DEFAULT_PAGER_CQ_SQ = $(subst ','\'',$(DEFAULT_PAGER_CQ))
 BASIC_CFLAGS += -DDEFAULT_PAGER='$(DEFAULT_PAGER_CQ_SQ)'
 endif
 
+ifdef SHELL_PATH
+SHELL_PATH_CQ = "$(subst ",\",$(subst \,\\,$(SHELL_PATH)))"
+SHELL_PATH_CQ_SQ = $(subst ','\'',$(SHELL_PATH_CQ))
+
+BASIC_CFLAGS += -DSHELL_PATH='$(SHELL_PATH_CQ_SQ)'
+endif
+
 ALL_CFLAGS += $(BASIC_CFLAGS)
 ALL_LDFLAGS += $(BASIC_LDFLAGS)
 
@@ -2160,34 +2226,19 @@ builtin/branch.o builtin/checkout.o builtin/clone.o builtin/reset.o branch.o tra
 builtin/bundle.o bundle.o transport.o: bundle.h
 builtin/bisect--helper.o builtin/rev-list.o bisect.o: bisect.h
 builtin/clone.o builtin/fetch-pack.o transport.o: fetch-pack.h
-builtin/grep.o builtin/pack-objects.o transport-helper.o thread-utils.o: thread-utils.h
+builtin/index-pack.o builtin/grep.o builtin/pack-objects.o transport-helper.o thread-utils.o: thread-utils.h
 builtin/send-pack.o transport.o: send-pack.h
 builtin/log.o builtin/shortlog.o: shortlog.h
 builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 connect.o transport.o url.o http-backend.o: url.h
+builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
 http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
 
-XDIFF_H += xdiff/xinclude.h
-XDIFF_H += xdiff/xmacros.h
-XDIFF_H += xdiff/xdiff.h
-XDIFF_H += xdiff/xtypes.h
-XDIFF_H += xdiff/xutils.h
-XDIFF_H += xdiff/xprepare.h
-XDIFF_H += xdiff/xdiffi.h
-XDIFF_H += xdiff/xemit.h
-
 xdiff-interface.o $(XDIFF_OBJS): $(XDIFF_H)
 
-VCSSVN_H += vcs-svn/line_buffer.h
-VCSSVN_H += vcs-svn/sliding_window.h
-VCSSVN_H += vcs-svn/repo_tree.h
-VCSSVN_H += vcs-svn/fast_export.h
-VCSSVN_H += vcs-svn/svndiff.h
-VCSSVN_H += vcs-svn/svndump.h
-
 $(VCSSVN_OBJS) $(VCSSVN_TEST_OBJS): $(LIB_H) $(VCSSVN_H)
 endif
 
@@ -2258,6 +2309,8 @@ $(XDIFF_LIB): $(XDIFF_OBJS)
 $(VCSSVN_LIB): $(VCSSVN_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
 
+export DEFAULT_EDITOR DEFAULT_PAGER
+
 doc:
        $(MAKE) -C Documentation all
 
@@ -2282,7 +2335,7 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
        --keyword=_ --keyword=N_ --keyword="Q_:1,2"
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
-LOCALIZED_C := $(C_OBJ:o=c)
+LOCALIZED_C := $(C_OBJ:o=c) $(LIB_H) $(XDIFF_H) $(VCSSVN_H) $(MISC_H)
 LOCALIZED_SH := $(SCRIPT_SH)
 LOCALIZED_PERL := $(SCRIPT_PERL)
 
@@ -2528,19 +2581,21 @@ endif
        { test "$$bindir/" = "$$execdir/" || \
          for p in git$X $(filter $(install_bindir_programs),$(ALL_PROGRAMS)); do \
                $(RM) "$$execdir/$$p" && \
-               test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
+               test -z "$(NO_INSTALL_HARDLINKS)$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
                ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
                cp "$$bindir/$$p" "$$execdir/$$p" || exit; \
          done; \
        } && \
        for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
                $(RM) "$$bindir/$$p" && \
+               test -z "$(NO_INSTALL_HARDLINKS)" && \
                ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
                ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
                cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
        done && \
        for p in $(BUILT_INS); do \
                $(RM) "$$execdir/$$p" && \
+               test -z "$(NO_INSTALL_HARDLINKS)" && \
                ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
                ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
@@ -2548,6 +2603,7 @@ endif
        remote_curl_aliases="$(REMOTE_CURL_ALIASES)" && \
        for p in $$remote_curl_aliases; do \
                $(RM) "$$execdir/$$p" && \
+               test -z "$(NO_INSTALL_HARDLINKS)" && \
                ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
                ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
index 2c2a16955519b3c314ebdb3d2f0c08d544666ca1..bcb4fb98ff925389797125283e40c6e397efc1fd 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.7.10.txt
\ No newline at end of file
+Documentation/RelNotes/1.7.11.txt
\ No newline at end of file
index 01130e54e7b270df7f535fb815dba25ddb72ec1a..a492eea24f71ad2d2082efce9d1d925a5766b111 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -1,6 +1,9 @@
 #include "cache.h"
 
 int advice_push_nonfastforward = 1;
+int advice_push_non_ff_current = 1;
+int advice_push_non_ff_default = 1;
+int advice_push_non_ff_matching = 1;
 int advice_status_hints = 1;
 int advice_commit_before_merge = 1;
 int advice_resolve_conflict = 1;
@@ -12,6 +15,9 @@ static struct {
        int *preference;
 } advice_config[] = {
        { "pushnonfastforward", &advice_push_nonfastforward },
+       { "pushnonffcurrent", &advice_push_non_ff_current },
+       { "pushnonffdefault", &advice_push_non_ff_default },
+       { "pushnonffmatching", &advice_push_non_ff_matching },
        { "statushints", &advice_status_hints },
        { "commitbeforemerge", &advice_commit_before_merge },
        { "resolveconflict", &advice_resolve_conflict },
index 7bda45b83e34b8417e5c20219c7424bb35b3d681..f3cdbbf29e570e151b2b6b329ee9a9940ae59a98 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -4,6 +4,9 @@
 #include "git-compat-util.h"
 
 extern int advice_push_nonfastforward;
+extern int advice_push_non_ff_current;
+extern int advice_push_non_ff_default;
+extern int advice_push_non_ff_matching;
 extern int advice_status_hints;
 extern int advice_commit_before_merge;
 extern int advice_resolve_conflict;
index 20af0051a334a1357b055ea10d74f5380117ab68..dc91c6b50d56ad8fd9921d0e47f5b45cafdcfc4d 100644 (file)
@@ -4,6 +4,7 @@
 #include "cache.h"
 #include "tar.h"
 #include "archive.h"
+#include "streaming.h"
 #include "run-command.h"
 
 #define RECORDSIZE     (512)
@@ -30,10 +31,9 @@ static void write_if_needed(void)
  * queues up writes, so that all our write(2) calls write exactly one
  * full block; pads writes to RECORDSIZE
  */
-static void write_blocked(const void *data, unsigned long size)
+static void do_write_blocked(const void *data, unsigned long size)
 {
        const char *buf = data;
-       unsigned long tail;
 
        if (offset) {
                unsigned long chunk = BLOCKSIZE - offset;
@@ -54,6 +54,11 @@ static void write_blocked(const void *data, unsigned long size)
                memcpy(block + offset, buf, size);
                offset += size;
        }
+}
+
+static void finish_record(void)
+{
+       unsigned long tail;
        tail = offset % RECORDSIZE;
        if (tail)  {
                memset(block + offset, 0, RECORDSIZE - tail);
@@ -62,6 +67,12 @@ static void write_blocked(const void *data, unsigned long size)
        write_if_needed();
 }
 
+static void write_blocked(const void *data, unsigned long size)
+{
+       do_write_blocked(data, size);
+       finish_record();
+}
+
 /*
  * The end of tar archives is marked by 2*512 nul bytes and after that
  * follows the rest of the block (if any).
@@ -77,6 +88,33 @@ static void write_trailer(void)
        }
 }
 
+/*
+ * queues up writes, so that all our write(2) calls write exactly one
+ * full block; pads writes to RECORDSIZE
+ */
+static int stream_blocked(const unsigned char *sha1)
+{
+       struct git_istream *st;
+       enum object_type type;
+       unsigned long sz;
+       char buf[BLOCKSIZE];
+       ssize_t readlen;
+
+       st = open_istream(sha1, &type, &sz, NULL);
+       if (!st)
+               return error("cannot stream blob %s", sha1_to_hex(sha1));
+       for (;;) {
+               readlen = read_istream(st, buf, sizeof(buf));
+               if (readlen <= 0)
+                       break;
+               do_write_blocked(buf, readlen);
+       }
+       close_istream(st);
+       if (!readlen)
+               finish_record();
+       return readlen;
+}
+
 /*
  * pax extended header records have the format "%u %s=%s\n".  %u contains
  * the size of the whole string (including the %u), the first %s is the
@@ -101,13 +139,13 @@ static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
 
 static unsigned int ustar_header_chksum(const struct ustar_header *header)
 {
-       char *p = (char *)header;
+       const char *p = (const char *)header;
        unsigned int chksum = 0;
        while (p < header->chksum)
                chksum += *p++;
        chksum += sizeof(header->chksum) * ' ';
        p += sizeof(header->chksum);
-       while (p < (char *)header + sizeof(struct ustar_header))
+       while (p < (const char *)header + sizeof(struct ustar_header))
                chksum += *p++;
        return chksum;
 }
@@ -123,56 +161,101 @@ static size_t get_path_prefix(const char *path, size_t pathlen, size_t maxlen)
        return i;
 }
 
+static void prepare_header(struct archiver_args *args,
+                          struct ustar_header *header,
+                          unsigned int mode, unsigned long size)
+{
+       sprintf(header->mode, "%07o", mode & 07777);
+       sprintf(header->size, "%011lo", S_ISREG(mode) ? size : 0);
+       sprintf(header->mtime, "%011lo", (unsigned long) args->time);
+
+       sprintf(header->uid, "%07o", 0);
+       sprintf(header->gid, "%07o", 0);
+       strlcpy(header->uname, "root", sizeof(header->uname));
+       strlcpy(header->gname, "root", sizeof(header->gname));
+       sprintf(header->devmajor, "%07o", 0);
+       sprintf(header->devminor, "%07o", 0);
+
+       memcpy(header->magic, "ustar", 6);
+       memcpy(header->version, "00", 2);
+
+       sprintf(header->chksum, "%07o", ustar_header_chksum(header));
+}
+
+static int write_extended_header(struct archiver_args *args,
+                                const unsigned char *sha1,
+                                const void *buffer, unsigned long size)
+{
+       struct ustar_header header;
+       unsigned int mode;
+       memset(&header, 0, sizeof(header));
+       *header.typeflag = TYPEFLAG_EXT_HEADER;
+       mode = 0100666;
+       sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+       prepare_header(args, &header, mode, size);
+       write_blocked(&header, sizeof(header));
+       write_blocked(buffer, size);
+       return 0;
+}
+
 static int write_tar_entry(struct archiver_args *args,
-               const unsigned char *sha1, const char *path, size_t pathlen,
-               unsigned int mode, void *buffer, unsigned long size)
+                          const unsigned char *sha1,
+                          const char *path, size_t pathlen,
+                          unsigned int mode)
 {
        struct ustar_header header;
        struct strbuf ext_header = STRBUF_INIT;
+       unsigned int old_mode = mode;
+       unsigned long size;
+       void *buffer;
        int err = 0;
 
        memset(&header, 0, sizeof(header));
 
-       if (!sha1) {
-               *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
-               mode = 0100666;
-               strcpy(header.name, "pax_global_header");
-       } else if (!path) {
-               *header.typeflag = TYPEFLAG_EXT_HEADER;
-               mode = 0100666;
-               sprintf(header.name, "%s.paxheader", sha1_to_hex(sha1));
+       if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
+               *header.typeflag = TYPEFLAG_DIR;
+               mode = (mode | 0777) & ~tar_umask;
+       } else if (S_ISLNK(mode)) {
+               *header.typeflag = TYPEFLAG_LNK;
+               mode |= 0777;
+       } else if (S_ISREG(mode)) {
+               *header.typeflag = TYPEFLAG_REG;
+               mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
        } else {
-               if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
-                       *header.typeflag = TYPEFLAG_DIR;
-                       mode = (mode | 0777) & ~tar_umask;
-               } else if (S_ISLNK(mode)) {
-                       *header.typeflag = TYPEFLAG_LNK;
-                       mode |= 0777;
-               } else if (S_ISREG(mode)) {
-                       *header.typeflag = TYPEFLAG_REG;
-                       mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
+               return error("unsupported file mode: 0%o (SHA1: %s)",
+                            mode, sha1_to_hex(sha1));
+       }
+       if (pathlen > sizeof(header.name)) {
+               size_t plen = get_path_prefix(path, pathlen,
+                                             sizeof(header.prefix));
+               size_t rest = pathlen - plen - 1;
+               if (plen > 0 && rest <= sizeof(header.name)) {
+                       memcpy(header.prefix, path, plen);
+                               memcpy(header.name, path + plen + 1, rest);
                } else {
-                       return error("unsupported file mode: 0%o (SHA1: %s)",
-                                       mode, sha1_to_hex(sha1));
+                       sprintf(header.name, "%s.data",
+                               sha1_to_hex(sha1));
+                       strbuf_append_ext_header(&ext_header, "path",
+                                                path, pathlen);
                }
-               if (pathlen > sizeof(header.name)) {
-                       size_t plen = get_path_prefix(path, pathlen,
-                                       sizeof(header.prefix));
-                       size_t rest = pathlen - plen - 1;
-                       if (plen > 0 && rest <= sizeof(header.name)) {
-                               memcpy(header.prefix, path, plen);
-                               memcpy(header.name, path + plen + 1, rest);
-                       } else {
-                               sprintf(header.name, "%s.data",
-                                       sha1_to_hex(sha1));
-                               strbuf_append_ext_header(&ext_header, "path",
-                                               path, pathlen);
-                       }
-               } else
-                       memcpy(header.name, path, pathlen);
+       } else
+               memcpy(header.name, path, pathlen);
+
+       if (S_ISREG(mode) && !args->convert &&
+           sha1_object_info(sha1, &size) == OBJ_BLOB &&
+           size > big_file_threshold)
+               buffer = NULL;
+       else if (S_ISLNK(mode) || S_ISREG(mode)) {
+               enum object_type type;
+               buffer = sha1_file_to_archive(args, path, sha1, old_mode, &type, &size);
+               if (!buffer)
+                       return error("cannot read %s", sha1_to_hex(sha1));
+       } else {
+               buffer = NULL;
+               size = 0;
        }
 
-       if (S_ISLNK(mode) && buffer) {
+       if (S_ISLNK(mode)) {
                if (size > sizeof(header.linkname)) {
                        sprintf(header.linkname, "see %s.paxheader",
                                sha1_to_hex(sha1));
@@ -182,32 +265,25 @@ static int write_tar_entry(struct archiver_args *args,
                        memcpy(header.linkname, buffer, size);
        }
 
-       sprintf(header.mode, "%07o", mode & 07777);
-       sprintf(header.size, "%011lo", S_ISREG(mode) ? size : 0);
-       sprintf(header.mtime, "%011lo", (unsigned long) args->time);
-
-       sprintf(header.uid, "%07o", 0);
-       sprintf(header.gid, "%07o", 0);
-       strlcpy(header.uname, "root", sizeof(header.uname));
-       strlcpy(header.gname, "root", sizeof(header.gname));
-       sprintf(header.devmajor, "%07o", 0);
-       sprintf(header.devminor, "%07o", 0);
-
-       memcpy(header.magic, "ustar", 6);
-       memcpy(header.version, "00", 2);
-
-       sprintf(header.chksum, "%07o", ustar_header_chksum(&header));
+       prepare_header(args, &header, mode, size);
 
        if (ext_header.len > 0) {
-               err = write_tar_entry(args, sha1, NULL, 0, 0, ext_header.buf,
-                               ext_header.len);
-               if (err)
+               err = write_extended_header(args, sha1, ext_header.buf,
+                                           ext_header.len);
+               if (err) {
+                       free(buffer);
                        return err;
+               }
        }
        strbuf_release(&ext_header);
        write_blocked(&header, sizeof(header));
-       if (S_ISREG(mode) && buffer && size > 0)
-               write_blocked(buffer, size);
+       if (S_ISREG(mode) && size > 0) {
+               if (buffer)
+                       write_blocked(buffer, size);
+               else
+                       err = stream_blocked(sha1);
+       }
+       free(buffer);
        return err;
 }
 
@@ -215,11 +291,18 @@ static int write_global_extended_header(struct archiver_args *args)
 {
        const unsigned char *sha1 = args->commit_sha1;
        struct strbuf ext_header = STRBUF_INIT;
-       int err;
+       struct ustar_header header;
+       unsigned int mode;
+       int err = 0;
 
        strbuf_append_ext_header(&ext_header, "comment", sha1_to_hex(sha1), 40);
-       err = write_tar_entry(args, NULL, NULL, 0, 0, ext_header.buf,
-                       ext_header.len);
+       memset(&header, 0, sizeof(header));
+       *header.typeflag = TYPEFLAG_GLOBAL_HEADER;
+       mode = 0100666;
+       strcpy(header.name, "pax_global_header");
+       prepare_header(args, &header, mode, ext_header.len);
+       write_blocked(&header, sizeof(header));
+       write_blocked(ext_header.buf, ext_header.len);
        strbuf_release(&ext_header);
        return err;
 }
index 02d1f3787acd8d6583073458ab7c8946684f9371..f5af81f904df081002dad46a71be2eca8e3bebab 100644 (file)
@@ -3,6 +3,7 @@
  */
 #include "cache.h"
 #include "archive.h"
+#include "streaming.h"
 
 static int zip_date;
 static int zip_time;
@@ -15,6 +16,7 @@ static unsigned int zip_dir_offset;
 static unsigned int zip_dir_entries;
 
 #define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
+#define ZIP_STREAM (8)
 
 struct zip_local_header {
        unsigned char magic[4];
@@ -31,6 +33,14 @@ struct zip_local_header {
        unsigned char _end[1];
 };
 
+struct zip_data_desc {
+       unsigned char magic[4];
+       unsigned char crc32[4];
+       unsigned char compressed_size[4];
+       unsigned char size[4];
+       unsigned char _end[1];
+};
+
 struct zip_dir_header {
        unsigned char magic[4];
        unsigned char creator_version[2];
@@ -70,6 +80,7 @@ struct zip_dir_trailer {
  * we're interested in.
  */
 #define ZIP_LOCAL_HEADER_SIZE  offsetof(struct zip_local_header, _end)
+#define ZIP_DATA_DESC_SIZE     offsetof(struct zip_data_desc, _end)
 #define ZIP_DIR_HEADER_SIZE    offsetof(struct zip_dir_header, _end)
 #define ZIP_DIR_TRAILER_SIZE   offsetof(struct zip_dir_trailer, _end)
 
@@ -120,20 +131,59 @@ static void *zlib_deflate(void *data, unsigned long size,
        return buffer;
 }
 
+static void write_zip_data_desc(unsigned long size,
+                               unsigned long compressed_size,
+                               unsigned long crc)
+{
+       struct zip_data_desc trailer;
+
+       copy_le32(trailer.magic, 0x08074b50);
+       copy_le32(trailer.crc32, crc);
+       copy_le32(trailer.compressed_size, compressed_size);
+       copy_le32(trailer.size, size);
+       write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
+}
+
+static void set_zip_dir_data_desc(struct zip_dir_header *header,
+                                 unsigned long size,
+                                 unsigned long compressed_size,
+                                 unsigned long crc)
+{
+       copy_le32(header->crc32, crc);
+       copy_le32(header->compressed_size, compressed_size);
+       copy_le32(header->size, size);
+}
+
+static void set_zip_header_data_desc(struct zip_local_header *header,
+                                    unsigned long size,
+                                    unsigned long compressed_size,
+                                    unsigned long crc)
+{
+       copy_le32(header->crc32, crc);
+       copy_le32(header->compressed_size, compressed_size);
+       copy_le32(header->size, size);
+}
+
+#define STREAM_BUFFER_SIZE (1024 * 16)
+
 static int write_zip_entry(struct archiver_args *args,
-               const unsigned char *sha1, const char *path, size_t pathlen,
-               unsigned int mode, void *buffer, unsigned long size)
+                          const unsigned char *sha1,
+                          const char *path, size_t pathlen,
+                          unsigned int mode)
 {
        struct zip_local_header header;
        struct zip_dir_header dirent;
        unsigned long attr2;
        unsigned long compressed_size;
-       unsigned long uncompressed_size;
        unsigned long crc;
        unsigned long direntsize;
        int method;
        unsigned char *out;
        void *deflated = NULL;
+       void *buffer;
+       struct git_istream *stream = NULL;
+       unsigned long flags = 0;
+       unsigned long size;
 
        crc = crc32(0, NULL, 0);
 
@@ -146,24 +196,43 @@ static int write_zip_entry(struct archiver_args *args,
                method = 0;
                attr2 = 16;
                out = NULL;
-               uncompressed_size = 0;
+               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);
+
                method = 0;
                attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
                        (mode & 0111) ? ((mode) << 16) : 0;
-               if (S_ISREG(mode) && args->compression_level != 0)
+               if (S_ISREG(mode) && args->compression_level != 0 && size > 0)
                        method = 8;
-               crc = crc32(crc, buffer, size);
-               out = buffer;
-               uncompressed_size = size;
                compressed_size = size;
+
+               if (S_ISREG(mode) && type == OBJ_BLOB && !args->convert &&
+                   size > big_file_threshold) {
+                       stream = open_istream(sha1, &type, &size, NULL);
+                       if (!stream)
+                               return error("cannot stream blob %s",
+                                            sha1_to_hex(sha1));
+                       flags |= ZIP_STREAM;
+                       out = buffer = NULL;
+               } else {
+                       buffer = sha1_file_to_archive(args, path, sha1, mode,
+                                                     &type, &size);
+                       if (!buffer)
+                               return error("cannot read %s",
+                                            sha1_to_hex(sha1));
+                       crc = crc32(crc, buffer, size);
+                       out = buffer;
+               }
        } else {
                return error("unsupported file mode: 0%o (SHA1: %s)", mode,
                                sha1_to_hex(sha1));
        }
 
-       if (method == 8) {
+       if (buffer && method == 8) {
                deflated = zlib_deflate(buffer, size, args->compression_level,
                                &compressed_size);
                if (deflated && compressed_size - 6 < size) {
@@ -188,13 +257,11 @@ static int write_zip_entry(struct archiver_args *args,
        copy_le16(dirent.creator_version,
                S_ISLNK(mode) || (S_ISREG(mode) && (mode & 0111)) ? 0x0317 : 0);
        copy_le16(dirent.version, 10);
-       copy_le16(dirent.flags, 0);
+       copy_le16(dirent.flags, flags);
        copy_le16(dirent.compression_method, method);
        copy_le16(dirent.mtime, zip_time);
        copy_le16(dirent.mdate, zip_date);
-       copy_le32(dirent.crc32, crc);
-       copy_le32(dirent.compressed_size, compressed_size);
-       copy_le32(dirent.size, uncompressed_size);
+       set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
        copy_le16(dirent.filename_length, pathlen);
        copy_le16(dirent.extra_length, 0);
        copy_le16(dirent.comment_length, 0);
@@ -202,33 +269,120 @@ static int write_zip_entry(struct archiver_args *args,
        copy_le16(dirent.attr1, 0);
        copy_le32(dirent.attr2, attr2);
        copy_le32(dirent.offset, zip_offset);
-       memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
-       zip_dir_offset += ZIP_DIR_HEADER_SIZE;
-       memcpy(zip_dir + zip_dir_offset, path, pathlen);
-       zip_dir_offset += pathlen;
-       zip_dir_entries++;
 
        copy_le32(header.magic, 0x04034b50);
        copy_le16(header.version, 10);
-       copy_le16(header.flags, 0);
+       copy_le16(header.flags, flags);
        copy_le16(header.compression_method, method);
        copy_le16(header.mtime, zip_time);
        copy_le16(header.mdate, zip_date);
-       copy_le32(header.crc32, crc);
-       copy_le32(header.compressed_size, compressed_size);
-       copy_le32(header.size, uncompressed_size);
+       if (flags & ZIP_STREAM)
+               set_zip_header_data_desc(&header, 0, 0, 0);
+       else
+               set_zip_header_data_desc(&header, size, compressed_size, crc);
        copy_le16(header.filename_length, pathlen);
        copy_le16(header.extra_length, 0);
        write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);
        zip_offset += ZIP_LOCAL_HEADER_SIZE;
        write_or_die(1, path, pathlen);
        zip_offset += pathlen;
-       if (compressed_size > 0) {
+       if (stream && method == 0) {
+               unsigned char buf[STREAM_BUFFER_SIZE];
+               ssize_t readlen;
+
+               for (;;) {
+                       readlen = read_istream(stream, buf, sizeof(buf));
+                       if (readlen <= 0)
+                               break;
+                       crc = crc32(crc, buf, readlen);
+                       write_or_die(1, buf, readlen);
+               }
+               close_istream(stream);
+               if (readlen)
+                       return readlen;
+
+               compressed_size = size;
+               zip_offset += compressed_size;
+
+               write_zip_data_desc(size, compressed_size, crc);
+               zip_offset += ZIP_DATA_DESC_SIZE;
+
+               set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
+       } else if (stream && method == 8) {
+               unsigned char buf[STREAM_BUFFER_SIZE];
+               ssize_t readlen;
+               git_zstream zstream;
+               int result;
+               size_t out_len;
+               unsigned char compressed[STREAM_BUFFER_SIZE * 2];
+
+               memset(&zstream, 0, sizeof(zstream));
+               git_deflate_init(&zstream, args->compression_level);
+
+               compressed_size = 0;
+               zstream.next_out = compressed;
+               zstream.avail_out = sizeof(compressed);
+
+               for (;;) {
+                       readlen = read_istream(stream, buf, sizeof(buf));
+                       if (readlen <= 0)
+                               break;
+                       crc = crc32(crc, buf, readlen);
+
+                       zstream.next_in = buf;
+                       zstream.avail_in = readlen;
+                       result = git_deflate(&zstream, 0);
+                       if (result != Z_OK)
+                               die("deflate error (%d)", result);
+                       out = compressed;
+                       if (!compressed_size)
+                               out += 2;
+                       out_len = zstream.next_out - out;
+
+                       if (out_len > 0) {
+                               write_or_die(1, out, out_len);
+                               compressed_size += out_len;
+                               zstream.next_out = compressed;
+                               zstream.avail_out = sizeof(compressed);
+                       }
+
+               }
+               close_istream(stream);
+               if (readlen)
+                       return readlen;
+
+               zstream.next_in = buf;
+               zstream.avail_in = 0;
+               result = git_deflate(&zstream, Z_FINISH);
+               if (result != Z_STREAM_END)
+                       die("deflate error (%d)", result);
+
+               git_deflate_end(&zstream);
+               out = compressed;
+               if (!compressed_size)
+                       out += 2;
+               out_len = zstream.next_out - out - 4;
+               write_or_die(1, out, out_len);
+               compressed_size += out_len;
+               zip_offset += compressed_size;
+
+               write_zip_data_desc(size, compressed_size, crc);
+               zip_offset += ZIP_DATA_DESC_SIZE;
+
+               set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
+       } else if (compressed_size > 0) {
                write_or_die(1, out, compressed_size);
                zip_offset += compressed_size;
        }
 
        free(deflated);
+       free(buffer);
+
+       memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
+       zip_dir_offset += ZIP_DIR_HEADER_SIZE;
+       memcpy(zip_dir + zip_dir_offset, path, pathlen);
+       zip_dir_offset += pathlen;
+       zip_dir_entries++;
 
        return 0;
 }
index 1ee837d7170cfa52da6725cfe7c5ae0d6d67462e..a4844330fb4da325aa364640dc80f052dc7b7ad7 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -59,12 +59,15 @@ static void format_subst(const struct commit *commit,
        free(to_free);
 }
 
-static void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
-               unsigned int mode, enum object_type *type,
-               unsigned long *sizep, const struct commit *commit)
+void *sha1_file_to_archive(const struct archiver_args *args,
+                          const char *path, const unsigned char *sha1,
+                          unsigned int mode, enum object_type *type,
+                          unsigned long *sizep)
 {
        void *buffer;
+       const struct commit *commit = args->convert ? args->commit : NULL;
 
+       path += args->baselen;
        buffer = read_sha1_file(sha1, type, sizep);
        if (buffer && S_ISREG(mode)) {
                struct strbuf buf = STRBUF_INIT;
@@ -109,12 +112,9 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
        write_archive_entry_fn_t write_entry = c->write_entry;
        struct git_attr_check check[2];
        const char *path_without_prefix;
-       int convert = 0;
        int err;
-       enum object_type type;
-       unsigned long size;
-       void *buffer;
 
+       args->convert = 0;
        strbuf_reset(&path);
        strbuf_grow(&path, PATH_MAX);
        strbuf_add(&path, args->base, args->baselen);
@@ -126,28 +126,22 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
        if (!git_check_attr(path_without_prefix, ARRAY_SIZE(check), check)) {
                if (ATTR_TRUE(check[0].value))
                        return 0;
-               convert = ATTR_TRUE(check[1].value);
+               args->convert = ATTR_TRUE(check[1].value);
        }
 
        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
                strbuf_addch(&path, '/');
                if (args->verbose)
                        fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
-               err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0);
+               err = write_entry(args, sha1, path.buf, path.len, mode);
                if (err)
                        return err;
                return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
        }
 
-       buffer = sha1_file_to_archive(path_without_prefix, sha1, mode,
-                       &type, &size, convert ? args->commit : NULL);
-       if (!buffer)
-               return error("cannot read %s", sha1_to_hex(sha1));
        if (args->verbose)
                fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
-       err = write_entry(args, sha1, path.buf, path.len, mode, buffer, size);
-       free(buffer);
-       return err;
+       return write_entry(args, sha1, path.buf, path.len, mode);
 }
 
 int write_archive_entries(struct archiver_args *args,
@@ -167,7 +161,7 @@ int write_archive_entries(struct archiver_args *args,
                if (args->verbose)
                        fprintf(stderr, "%.*s\n", (int)len, args->base);
                err = write_entry(args, args->tree->object.sha1, args->base,
-                               len, 040777, NULL, 0);
+                                 len, 040777);
                if (err)
                        return err;
        }
@@ -260,18 +254,11 @@ static void parse_treeish_arg(const char **argv,
        /* Remotes are only allowed to fetch actual refs */
        if (remote) {
                char *ref = NULL;
-               const char *refname, *colon = NULL;
-
-               colon = strchr(name, ':');
-               if (colon)
-                       refname = xstrndup(name, colon - name);
-               else
-                       refname = name;
-
-               if (!dwim_ref(refname, strlen(refname), sha1, &ref))
-                       die("no such ref: %s", refname);
-               if (refname != name)
-                       free((void *)refname);
+               const char *colon = strchr(name, ':');
+               int refnamelen = colon ? colon - name : strlen(name);
+
+               if (!dwim_ref(name, refnamelen, sha1, &ref))
+                       die("no such ref: %.*s", refnamelen, name);
                free(ref);
        }
 
index 2b0884f1ef3123f26d9a7b3ba03e3d14ae1ccd57..895afcdc7a00c5f9f16a21f2c9fe361a3e218d37 100644 (file)
--- a/archive.h
+++ b/archive.h
@@ -11,6 +11,7 @@ struct archiver_args {
        const char **pathspec;
        unsigned int verbose : 1;
        unsigned int worktree_attributes : 1;
+       unsigned int convert : 1;
        int compression_level;
 };
 
@@ -27,11 +28,18 @@ extern void register_archiver(struct archiver *);
 extern void init_tar_archiver(void);
 extern void init_zip_archiver(void);
 
-typedef int (*write_archive_entry_fn_t)(struct archiver_args *args, const unsigned char *sha1, const char *path, size_t pathlen, unsigned int mode, void *buffer, unsigned long size);
+typedef int (*write_archive_entry_fn_t)(struct archiver_args *args,
+                                       const unsigned char *sha1,
+                                       const char *path, size_t pathlen,
+                                       unsigned int mode);
 
 extern int write_archive_entries(struct archiver_args *args, write_archive_entry_fn_t write_entry);
 extern int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix, const char *name_hint, int remote);
 
 const char *archive_format_from_filename(const char *filename);
+extern void *sha1_file_to_archive(const struct archiver_args *args,
+                                 const char *path, const unsigned char *sha1,
+                                 unsigned int mode, enum object_type *type,
+                                 unsigned long *sizep);
 
 #endif /* ARCHIVE_H */
index a4e04201e61d0243dc225d85117341d993ca0c3b..0b5f8898a10f16df8a6273f8960f05b670ba94bc 100644 (file)
@@ -2,8 +2,7 @@
 #include "argv-array.h"
 #include "strbuf.h"
 
-static const char *empty_argv_storage = NULL;
-const char **empty_argv = &empty_argv_storage;
+const char *empty_argv[] = { NULL };
 
 void argv_array_init(struct argv_array *array)
 {
@@ -39,6 +38,17 @@ void argv_array_pushf(struct argv_array *array, const char *fmt, ...)
        argv_array_push_nodup(array, strbuf_detach(&v, NULL));
 }
 
+void argv_array_pushl(struct argv_array *array, ...)
+{
+       va_list ap;
+       const char *arg;
+
+       va_start(ap, array);
+       while((arg = va_arg(ap, const char *)))
+               argv_array_push(array, arg);
+       va_end(ap);
+}
+
 void argv_array_clear(struct argv_array *array)
 {
        if (array->argv != empty_argv) {
index 74dd2b1bc0487fb3493c1c2747ebe2bd8958616b..b93a69c36cb8d391c1d7de93f75b3d54c00e60ca 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef ARGV_ARRAY_H
 #define ARGV_ARRAY_H
 
-extern const char **empty_argv;
+extern const char *empty_argv[];
 
 struct argv_array {
        const char **argv;
@@ -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, ...);
+void argv_array_pushl(struct argv_array *, ...);
 void argv_array_clear(struct argv_array *);
 
 #endif /* ARGV_ARRAY_H */
index 6e186e29cc4a6a74944798e8c4248219e9c5997f..48acf73391271c1d9061b178a53653357f07f391 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -833,7 +833,7 @@ static int check_ancestors(const char *prefix)
  */
 static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 {
-       const char *filename = git_path("BISECT_ANCESTORS_OK");
+       char *filename = xstrdup(git_path("BISECT_ANCESTORS_OK"));
        struct stat st;
        int fd;
 
@@ -842,11 +842,11 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
 
        /* Check if file BISECT_ANCESTORS_OK exists. */
        if (!stat(filename, &st) && S_ISREG(st.st_mode))
-               return;
+               goto done;
 
        /* Bisecting with no good rev is ok. */
        if (good_revs.nr == 0)
-               return;
+               goto done;
 
        /* Check if all good revs are ancestor of the bad rev. */
        if (check_ancestors(prefix))
@@ -859,6 +859,8 @@ static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
                        filename, strerror(errno));
        else
                close(fd);
+ done:
+       free(filename);
 }
 
 /*
index 9971820a184d9713126c3c9f763dd8f6ec1b1a50..eccdaf93924334137833e5acf7541aa514588c2d 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -101,9 +101,10 @@ void install_branch_config(int flag, const char *local, const char *origin, cons
  * config.
  */
 static int setup_tracking(const char *new_ref, const char *orig_ref,
-                          enum branch_track track)
+                         enum branch_track track, int quiet)
 {
        struct tracking tracking;
+       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",
@@ -128,7 +129,7 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
                return error("Not tracking: ambiguous information for ref %s",
                                orig_ref);
 
-       install_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,
+       install_branch_config(config_flags, new_ref, tracking.remote,
                              tracking.src ? tracking.src : orig_ref);
 
        free(tracking.src);
@@ -191,7 +192,7 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
 void create_branch(const char *head,
                   const char *name, const char *start_name,
                   int force, int reflog, int clobber_head,
-                  enum branch_track track)
+                  int quiet, enum branch_track track)
 {
        struct ref_lock *lock = NULL;
        struct commit *commit;
@@ -260,7 +261,7 @@ void create_branch(const char *head,
                         start_name);
 
        if (real_ref && track)
-               setup_tracking(ref.buf+11, real_ref, track);
+               setup_tracking(ref.buf+11, real_ref, track, quiet);
 
        if (!dont_change_ref)
                if (write_ref_sha1(lock, sha1, msg) < 0)
index b99c5a369e31a85d1fff822460e69a79d8c6102b..64173abf4db65b0a8e71c1c8880f97a3350306f7 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -14,7 +14,7 @@
  */
 void create_branch(const char *head, const char *name, const char *start_name,
                   int force, int reflog,
-                  int clobber_head, enum branch_track track);
+                  int clobber_head, int quiet, enum branch_track track);
 
 /*
  * Validates that the requested branch may be created, returning the
index 857b9c8aa85fff5764b528485a880cd9dfa95b17..338f540e39af7093b39668a3bed16158a4483566 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -61,6 +61,7 @@ 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);
 extern int cmd_clone(int argc, const char **argv, const char *prefix);
 extern int cmd_clean(int argc, const char **argv, const char *prefix);
+extern int cmd_column(int argc, const char **argv, const char *prefix);
 extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
index 389898f13364eb640077c1d82fefea98d9d3755f..dda9ea09c91010f0a214abc97ea0d5d0798a5360 100644 (file)
@@ -50,7 +50,7 @@ static const char *fake_ancestor;
 static int line_termination = '\n';
 static unsigned int p_context = UINT_MAX;
 static const char * const apply_usage[] = {
-       "git apply [options] [<patch>...]",
+       N_("git apply [options] [<patch>...]"),
        NULL
 };
 
@@ -103,7 +103,7 @@ static void parse_whitespace_option(const char *option)
                ws_error_action = correct_ws_error;
                return;
        }
-       die("unrecognized whitespace option '%s'", option);
+       die(_("unrecognized whitespace option '%s'"), option);
 }
 
 static void parse_ignorewhitespace_option(const char *option)
@@ -118,7 +118,7 @@ static void parse_ignorewhitespace_option(const char *option)
                ws_ignore_action = ignore_ws_change;
                return;
        }
-       die("unrecognized whitespace ignore option '%s'", option);
+       die(_("unrecognized whitespace ignore option '%s'"), option);
 }
 
 static void set_default_whitespace_mode(const char *whitespace_option)
@@ -152,9 +152,14 @@ struct fragment {
        unsigned long leading, trailing;
        unsigned long oldpos, oldlines;
        unsigned long newpos, newlines;
+       /*
+        * 'patch' is usually borrowed from buf in apply_patch(),
+        * but some codepaths store an allocated buffer.
+        */
        const char *patch;
+       unsigned free_patch:1,
+               rejected:1;
        int size;
-       int rejected;
        int linenr;
        struct fragment *next;
 };
@@ -196,6 +201,36 @@ struct patch {
        struct patch *next;
 };
 
+static void free_fragment_list(struct fragment *list)
+{
+       while (list) {
+               struct fragment *next = list->next;
+               if (list->free_patch)
+                       free((char *)list->patch);
+               free(list);
+               list = next;
+       }
+}
+
+static void free_patch(struct patch *patch)
+{
+       free_fragment_list(patch->fragments);
+       free(patch->def_name);
+       free(patch->old_name);
+       free(patch->new_name);
+       free(patch->result);
+       free(patch);
+}
+
+static void free_patch_list(struct patch *list)
+{
+       while (list) {
+               struct patch *next = list->next;
+               free_patch(list);
+               list = next;
+       }
+}
+
 /*
  * A line in a file, len-bytes long (includes the terminating LF,
  * except for an incomplete line at the end if the file ends with
@@ -302,6 +337,11 @@ static void add_line_info(struct image *img, const char *bol, size_t len, unsign
        img->nr++;
 }
 
+/*
+ * "buf" has the file contents to be patched (read from various sources).
+ * attach it to "image" and add line-based index to it.
+ * "image" now owns the "buf".
+ */
 static void prepare_image(struct image *image, char *buf, size_t len,
                          int prepare_linetable)
 {
@@ -335,25 +375,27 @@ static void clear_image(struct image *image)
        image->len = 0;
 }
 
-static void say_patch_name(FILE *output, const char *pre,
-                          struct patch *patch, const char *post)
+/* fmt must contain _one_ %s and no other substitution */
+static void say_patch_name(FILE *output, const char *fmt, struct patch *patch)
 {
-       fputs(pre, output);
+       struct strbuf sb = STRBUF_INIT;
+
        if (patch->old_name && patch->new_name &&
            strcmp(patch->old_name, patch->new_name)) {
-               quote_c_style(patch->old_name, NULL, output, 0);
-               fputs(" => ", output);
-               quote_c_style(patch->new_name, NULL, output, 0);
+               quote_c_style(patch->old_name, &sb, NULL, 0);
+               strbuf_addstr(&sb, " => ");
+               quote_c_style(patch->new_name, &sb, NULL, 0);
        } else {
                const char *n = patch->new_name;
                if (!n)
                        n = patch->old_name;
-               quote_c_style(n, NULL, output, 0);
+               quote_c_style(n, &sb, NULL, 0);
        }
-       fputs(post, output);
+       fprintf(output, fmt, sb.buf);
+       fputc('\n', output);
+       strbuf_release(&sb);
 }
 
-#define CHUNKSIZE (8192)
 #define SLOP (16)
 
 static void read_patch_file(struct strbuf *sb, int fd)
@@ -416,7 +458,7 @@ static char *squash_slash(char *name)
        return name;
 }
 
-static char *find_name_gnu(const char *line, char *def, int p_value)
+static char *find_name_gnu(const char *line, const char *def, int p_value)
 {
        struct strbuf name = STRBUF_INIT;
        char *cp;
@@ -439,11 +481,7 @@ static char *find_name_gnu(const char *line, char *def, int p_value)
                cp++;
        }
 
-       /* name can later be freed, so we need
-        * to memmove, not just return cp
-        */
        strbuf_remove(&name, 0, cp - name.buf);
-       free(def);
        if (root)
                strbuf_insert(&name, 0, root, root_len);
        return squash_slash(strbuf_detach(&name, NULL));
@@ -608,8 +646,13 @@ static size_t diff_timestamp_len(const char *line, size_t len)
        return line + len - end;
 }
 
-static char *find_name_common(const char *line, char *def, int p_value,
-                               const char *end, int terminate)
+static char *null_strdup(const char *s)
+{
+       return s ? xstrdup(s) : NULL;
+}
+
+static char *find_name_common(const char *line, const char *def,
+                             int p_value, const char *end, int terminate)
 {
        int len;
        const char *start = NULL;
@@ -630,10 +673,10 @@ static char *find_name_common(const char *line, char *def, int p_value,
                        start = line;
        }
        if (!start)
-               return squash_slash(def);
+               return squash_slash(null_strdup(def));
        len = line - start;
        if (!len)
-               return squash_slash(def);
+               return squash_slash(null_strdup(def));
 
        /*
         * Generally we prefer the shorter name, especially
@@ -644,8 +687,7 @@ static char *find_name_common(const char *line, char *def, int p_value,
        if (def) {
                int deflen = strlen(def);
                if (deflen < len && !strncmp(start, def, deflen))
-                       return squash_slash(def);
-               free(def);
+                       return squash_slash(xstrdup(def));
        }
 
        if (root) {
@@ -770,7 +812,7 @@ static int has_epoch_timestamp(const char *nameline)
        if (!stamp) {
                stamp = xmalloc(sizeof(*stamp));
                if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
-                       warning("Cannot prepare timestamp regexp %s",
+                       warning(_("Cannot prepare timestamp regexp %s"),
                                stamp_regexp);
                        return 0;
                }
@@ -779,7 +821,7 @@ static int has_epoch_timestamp(const char *nameline)
        status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
        if (status) {
                if (status != REG_NOMATCH)
-                       warning("regexec returned %d for input: %s",
+                       warning(_("regexec returned %d for input: %s"),
                                status, timestamp);
                return 0;
        }
@@ -842,8 +884,10 @@ static void parse_traditional_patch(const char *first, const char *second, struc
                name = find_name_traditional(first, NULL, p_value);
                patch->old_name = name;
        } else {
-               name = find_name_traditional(first, NULL, p_value);
-               name = find_name_traditional(second, name, p_value);
+               char *first_name;
+               first_name = find_name_traditional(first, NULL, p_value);
+               name = find_name_traditional(second, first_name, p_value);
+               free(first_name);
                if (has_epoch_timestamp(first)) {
                        patch->is_new = 1;
                        patch->is_delete = 0;
@@ -853,11 +897,12 @@ static void parse_traditional_patch(const char *first, const char *second, struc
                        patch->is_delete = 1;
                        patch->old_name = name;
                } else {
-                       patch->old_name = patch->new_name = name;
+                       patch->old_name = name;
+                       patch->new_name = xstrdup(name);
                }
        }
        if (!name)
-               die("unable to find filename in patch at line %d", linenr);
+               die(_("unable to find filename in patch at line %d"), linenr);
 }
 
 static int gitdiff_hdrend(const char *line, struct patch *patch)
@@ -874,7 +919,10 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
  * their names against any previous information, just
  * to make sure..
  */
-static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
+#define DIFF_OLD_NAME 0
+#define DIFF_NEW_NAME 1
+
+static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side)
 {
        if (!orig_name && !isnull)
                return find_name(line, NULL, p_value, TERM_TAB);
@@ -886,30 +934,40 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
                name = orig_name;
                len = strlen(name);
                if (isnull)
-                       die("git apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
+                       die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr);
                another = find_name(line, NULL, p_value, TERM_TAB);
                if (!another || memcmp(another, name, len + 1))
-                       die("git apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
+                       die((side == DIFF_NEW_NAME) ?
+                           _("git apply: bad git-diff - inconsistent new filename on line %d") :
+                           _("git apply: bad git-diff - inconsistent old filename on line %d"), linenr);
                free(another);
                return orig_name;
        }
        else {
                /* expect "/dev/null" */
                if (memcmp("/dev/null", line, 9) || line[9] != '\n')
-                       die("git apply: bad git-diff - expected /dev/null on line %d", linenr);
+                       die(_("git apply: bad git-diff - expected /dev/null on line %d"), linenr);
                return NULL;
        }
 }
 
 static int gitdiff_oldname(const char *line, struct patch *patch)
 {
-       patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, "old");
+       char *orig = patch->old_name;
+       patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
+                                             DIFF_OLD_NAME);
+       if (orig != patch->old_name)
+               free(orig);
        return 0;
 }
 
 static int gitdiff_newname(const char *line, struct patch *patch)
 {
-       patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, "new");
+       char *orig = patch->new_name;
+       patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
+                                             DIFF_NEW_NAME);
+       if (orig != patch->new_name)
+               free(orig);
        return 0;
 }
 
@@ -928,20 +986,23 @@ static int gitdiff_newmode(const char *line, struct patch *patch)
 static int gitdiff_delete(const char *line, struct patch *patch)
 {
        patch->is_delete = 1;
-       patch->old_name = patch->def_name;
+       free(patch->old_name);
+       patch->old_name = null_strdup(patch->def_name);
        return gitdiff_oldmode(line, patch);
 }
 
 static int gitdiff_newfile(const char *line, struct patch *patch)
 {
        patch->is_new = 1;
-       patch->new_name = patch->def_name;
+       free(patch->new_name);
+       patch->new_name = null_strdup(patch->def_name);
        return gitdiff_newmode(line, patch);
 }
 
 static int gitdiff_copysrc(const char *line, struct patch *patch)
 {
        patch->is_copy = 1;
+       free(patch->old_name);
        patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
@@ -949,6 +1010,7 @@ static int gitdiff_copysrc(const char *line, struct patch *patch)
 static int gitdiff_copydst(const char *line, struct patch *patch)
 {
        patch->is_copy = 1;
+       free(patch->new_name);
        patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
@@ -956,6 +1018,7 @@ static int gitdiff_copydst(const char *line, struct patch *patch)
 static int gitdiff_renamesrc(const char *line, struct patch *patch)
 {
        patch->is_rename = 1;
+       free(patch->old_name);
        patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
@@ -963,6 +1026,7 @@ static int gitdiff_renamesrc(const char *line, struct patch *patch)
 static int gitdiff_renamedst(const char *line, struct patch *patch)
 {
        patch->is_rename = 1;
+       free(patch->new_name);
        patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
 }
@@ -1044,7 +1108,7 @@ static const char *stop_at_slash(const char *line, int llen)
  * creation or deletion of an empty file.  In any of these cases,
  * both sides are the same name under a/ and b/ respectively.
  */
-static char *git_header_name(char *line, int llen)
+static char *git_header_name(const char *line, int llen)
 {
        const char *name;
        const char *second = NULL;
@@ -1171,7 +1235,7 @@ static char *git_header_name(char *line, int llen)
 }
 
 /* Verify that we recognize the lines following a git header */
-static int parse_git_header(char *line, int len, unsigned int size, struct patch *patch)
+static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
 {
        unsigned long offset;
 
@@ -1287,7 +1351,7 @@ static int parse_range(const char *line, int len, int offset, const char *expect
        return offset + ex;
 }
 
-static void recount_diff(char *line, int size, struct fragment *fragment)
+static void recount_diff(const char *line, int size, struct fragment *fragment)
 {
        int oldlines = 0, newlines = 0, ret = 0;
 
@@ -1327,7 +1391,7 @@ static void recount_diff(char *line, int size, struct fragment *fragment)
                        break;
                }
                if (ret) {
-                       warning("recount: unexpected line: %.*s",
+                       warning(_("recount: unexpected line: %.*s"),
                                (int)linelen(line, size), line);
                        return;
                }
@@ -1341,7 +1405,7 @@ static void recount_diff(char *line, int size, struct fragment *fragment)
  * Parse a unified diff fragment header of the
  * form "@@ -a,b +c,d @@"
  */
-static int parse_fragment_header(char *line, int len, struct fragment *fragment)
+static int parse_fragment_header(const char *line, int len, struct fragment *fragment)
 {
        int offset;
 
@@ -1355,7 +1419,7 @@ static int parse_fragment_header(char *line, int len, struct fragment *fragment)
        return offset;
 }
 
-static int find_header(char *line, unsigned long size, int *hdrsize, struct patch *patch)
+static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
 {
        unsigned long offset, len;
 
@@ -1384,7 +1448,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
                        struct fragment dummy;
                        if (parse_fragment_header(line, len, &dummy) < 0)
                                continue;
-                       die("patch fragment without header at line %d: %.*s",
+                       die(_("patch fragment without header at line %d: %.*s"),
                            linenr, (int)len-1, line);
                }
 
@@ -1401,9 +1465,14 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
                                continue;
                        if (!patch->old_name && !patch->new_name) {
                                if (!patch->def_name)
-                                       die("git diff header lacks filename information when removing "
-                                           "%d leading pathname components (line %d)" , p_value, linenr);
-                               patch->old_name = patch->new_name = patch->def_name;
+                                       die(Q_("git diff header lacks filename information when removing "
+                                              "%d leading pathname component (line %d)",
+                                              "git diff header lacks filename information when removing "
+                                              "%d leading pathname components (line %d)",
+                                              p_value),
+                                           p_value, linenr);
+                               patch->old_name = xstrdup(patch->def_name);
+                               patch->new_name = xstrdup(patch->def_name);
                        }
                        if (!patch->is_delete && !patch->new_name)
                                die("git diff header lacks filename information "
@@ -1466,7 +1535,7 @@ static void check_whitespace(const char *line, int len, unsigned ws_rule)
  * between a "---" that is part of a patch, and a "---" that starts
  * the next patch is to look at the line counts..
  */
-static int parse_fragment(char *line, unsigned long size,
+static int parse_fragment(const char *line, unsigned long size,
                          struct patch *patch, struct fragment *fragment)
 {
        int added, deleted;
@@ -1556,13 +1625,21 @@ static int parse_fragment(char *line, unsigned long size,
        patch->lines_deleted += deleted;
 
        if (0 < patch->is_new && oldlines)
-               return error("new file depends on old contents");
+               return error(_("new file depends on old contents"));
        if (0 < patch->is_delete && newlines)
-               return error("deleted file still has contents");
+               return error(_("deleted file still has contents"));
        return offset;
 }
 
-static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
+/*
+ * We have seen "diff --git a/... b/..." header (or a traditional patch
+ * header).  Read hunks that belong to this patch into fragments and hang
+ * them to the given patch structure.
+ *
+ * The (fragment->patch, fragment->size) pair points into the memory given
+ * by the caller, not a copy, when we return.
+ */
+static int parse_single_patch(const char *line, unsigned long size, struct patch *patch)
 {
        unsigned long offset = 0;
        unsigned long oldlines = 0, newlines = 0, context = 0;
@@ -1576,7 +1653,7 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                fragment->linenr = linenr;
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
-                       die("corrupt patch at line %d", linenr);
+                       die(_("corrupt patch at line %d"), linenr);
                fragment->patch = line;
                fragment->size = len;
                oldlines += fragment->oldlines;
@@ -1612,12 +1689,14 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
                patch->is_delete = 0;
 
        if (0 < patch->is_new && oldlines)
-               die("new file %s depends on old contents", patch->new_name);
+               die(_("new file %s depends on old contents"), patch->new_name);
        if (0 < patch->is_delete && newlines)
-               die("deleted file %s still has contents", patch->old_name);
+               die(_("deleted file %s still has contents"), patch->old_name);
        if (!patch->is_delete && !newlines && context)
-               fprintf(stderr, "** warning: file %s becomes empty but "
-                       "is not deleted\n", patch->new_name);
+               fprintf_ln(stderr,
+                          _("** warning: "
+                            "file %s becomes empty but is not deleted"),
+                          patch->new_name);
 
        return offset;
 }
@@ -1655,6 +1734,11 @@ static char *inflate_it(const void *data, unsigned long size,
        return out;
 }
 
+/*
+ * Read a binary hunk and return a new fragment; fragment->patch
+ * points at an allocated memory that the caller must free, so
+ * it is marked as "->free_patch = 1".
+ */
 static struct fragment *parse_binary_hunk(char **buf_p,
                                          unsigned long *sz_p,
                                          int *status_p,
@@ -1742,6 +1826,7 @@ static struct fragment *parse_binary_hunk(char **buf_p,
 
        frag = xcalloc(1, sizeof(*frag));
        frag->patch = inflate_it(data, hunk_size, origlen);
+       frag->free_patch = 1;
        if (!frag->patch)
                goto corrupt;
        free(data);
@@ -1755,7 +1840,7 @@ static struct fragment *parse_binary_hunk(char **buf_p,
  corrupt:
        free(data);
        *status_p = -1;
-       error("corrupt binary patch at line %d: %.*s",
+       error(_("corrupt binary patch at line %d: %.*s"),
              linenr-1, llen-1, buffer);
        return NULL;
 }
@@ -1784,7 +1869,7 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
        forward = parse_binary_hunk(&buffer, &size, &status, &used);
        if (!forward && !status)
                /* there has to be one hunk (forward hunk) */
-               return error("unrecognized binary patch at line %d", linenr-1);
+               return error(_("unrecognized binary patch at line %d"), linenr-1);
        if (status)
                /* otherwise we already gave an error message */
                return status;
@@ -1807,6 +1892,13 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
        return used;
 }
 
+/*
+ * Read the patch text in "buffer" taht extends for "size" bytes; stop
+ * reading after seeing a single patch (i.e. changes to a single file).
+ * Create fragments (i.e. patch hunks) and hang them to the given patch.
+ * Return the number of bytes consumed, so that the caller can call us
+ * again for the next patch.
+ */
 static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
 {
        int hdrsize, patchsize;
@@ -1863,7 +1955,7 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
                 */
                if ((apply || check) &&
                    (!patch->is_binary && !metadata_changes(patch)))
-                       die("patch with only garbage at line %d", linenr);
+                       die(_("patch with only garbage at line %d"), linenr);
        }
 
        return offset + hdrsize + patchsize;
@@ -1953,11 +2045,11 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
-                       return error("unable to read symlink %s", path);
+                       return error(_("unable to read symlink %s"), path);
                return 0;
        case S_IFREG:
                if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
-                       return error("unable to open or read %s", path);
+                       return error(_("unable to open or read %s"), path);
                convert_to_git(path, buf->buf, buf->len, buf, 0);
                return 0;
        default:
@@ -2028,7 +2120,7 @@ static void update_pre_post_images(struct image *preimage,
                        ctx++;
                }
                if (preimage->nr <= ctx)
-                       die("oops");
+                       die(_("oops"));
 
                /* and copy it in, while fixing the line length */
                len = preimage->line[ctx].len;
@@ -2367,6 +2459,11 @@ static void remove_last_line(struct image *img)
        img->len -= img->line[--img->nr].len;
 }
 
+/*
+ * The change from "preimage" and "postimage" has been found to
+ * apply at applied_pos (counts in line numbers) in "img".
+ * Update "img" to remove "preimage" and replace it with "postimage".
+ */
 static void update_image(struct image *img,
                         int applied_pos,
                         struct image *preimage,
@@ -2438,6 +2535,11 @@ static void update_image(struct image *img,
        img->nr = nr;
 }
 
+/*
+ * Use the patch-hunk text in "frag" to prepare two images (preimage and
+ * postimage) for the hunk.  Find lines that match "preimage" in "img" and
+ * replace the part of "img" with "postimage" text.
+ */
 static int apply_one_fragment(struct image *img, struct fragment *frag,
                              int inaccurate_eof, unsigned ws_rule,
                              int nth_fragment)
@@ -2540,7 +2642,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        break;
                default:
                        if (apply_verbosely)
-                               error("invalid start of line: '%c'", first);
+                               error(_("invalid start of line: '%c'"), first);
                        return -1;
                }
                if (added_blank_line) {
@@ -2657,9 +2759,11 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                        int offset = applied_pos - pos;
                        if (apply_in_reverse)
                                offset = 0 - offset;
-                       fprintf(stderr,
-                               "Hunk #%d succeeded at %d (offset %d lines).\n",
-                               nth_fragment, applied_pos + 1, offset);
+                       fprintf_ln(stderr,
+                                  Q_("Hunk #%d succeeded at %d (offset %d line).",
+                                     "Hunk #%d succeeded at %d (offset %d lines).",
+                                     offset),
+                                  nth_fragment, applied_pos + 1, offset);
                }
 
                /*
@@ -2668,13 +2772,13 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                 */
                if ((leading != frag->leading) ||
                    (trailing != frag->trailing))
-                       fprintf(stderr, "Context reduced to (%ld/%ld)"
-                               " to apply fragment at %d\n",
-                               leading, trailing, applied_pos+1);
+                       fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
+                                            " to apply fragment at %d"),
+                                  leading, trailing, applied_pos+1);
                update_image(img, applied_pos, &preimage, &postimage);
        } else {
                if (apply_verbosely)
-                       error("while searching for:\n%.*s",
+                       error(_("while searching for:\n%.*s"),
                              (int)(old - oldlines), oldlines);
        }
 
@@ -2693,7 +2797,7 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
        void *dst;
 
        if (!fragment)
-               return error("missing binary patch data for '%s'",
+               return error(_("missing binary patch data for '%s'"),
                             patch->new_name ?
                             patch->new_name :
                             patch->old_name);
@@ -2728,6 +2832,12 @@ static int apply_binary_fragment(struct image *img, struct patch *patch)
        return -1;
 }
 
+/*
+ * Replace "img" with the result of applying the binary patch.
+ * The binary patch data itself in patch->fragment is still kept
+ * but the preimage prepared by the caller in "img" is freed here
+ * or in the helper function apply_binary_fragment() this calls.
+ */
 static int apply_binary(struct image *img, struct patch *patch)
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
@@ -2790,13 +2900,13 @@ static int apply_binary(struct image *img, struct patch *patch)
                 * in the patch->fragments->{patch,size}.
                 */
                if (apply_binary_fragment(img, patch))
-                       return error("binary patch does not apply to '%s'",
+                       return error(_("binary patch does not apply to '%s'"),
                                     name);
 
                /* verify that the result matches */
                hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
-                       return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
+                       return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
                                name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
 
@@ -2817,7 +2927,7 @@ static int apply_fragments(struct image *img, struct patch *patch)
        while (frag) {
                nth++;
                if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
-                       error("patch failed: %s:%ld", name, frag->oldpos);
+                       error(_("patch failed: %s:%ld"), name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
                        frag->rejected = 1;
@@ -2932,14 +3042,14 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        if (!(patch->is_copy || patch->is_rename) &&
            (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {
                if (was_deleted(tpatch)) {
-                       return error("patch %s has been renamed/deleted",
+                       return error(_("patch %s has been renamed/deleted"),
                                patch->old_name);
                }
-               /* We have a patched copy in memory use that */
+               /* We have a patched copy in memory; use that. */
                strbuf_add(&buf, tpatch->result, tpatch->resultsize);
        } else if (cached) {
                if (read_file_or_gitlink(ce, &buf))
-                       return error("read of %s failed", patch->old_name);
+                       return error(_("read of %s failed"), patch->old_name);
        } else if (patch->old_name) {
                if (S_ISGITLINK(patch->old_mode)) {
                        if (ce) {
@@ -2948,12 +3058,15 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
                                /*
                                 * There is no way to apply subproject
                                 * patch without looking at the index.
+                                * NEEDSWORK: shouldn't this be flagged
+                                * as an error???
                                 */
+                               free_fragment_list(patch->fragments);
                                patch->fragments = NULL;
                        }
                } else {
                        if (read_old_data(st, patch->old_name, &buf))
-                               return error("read of %s failed", patch->old_name);
+                               return error(_("read of %s failed"), patch->old_name);
                }
        }
 
@@ -2968,7 +3081,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
        free(image.line_allocated);
 
        if (0 < patch->is_delete && patch->resultsize)
-               return error("removal patch leaves file contents");
+               return error(_("removal patch leaves file contents"));
 
        return 0;
 }
@@ -2989,7 +3102,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
                if (has_symlink_leading_path(new_name, strlen(new_name)))
                        return 0;
 
-               return error("%s: already exists in working directory", new_name);
+               return error(_("%s: already exists in working directory"), new_name);
        }
        else if ((errno != ENOENT) && (errno != ENOTDIR))
                return error("%s: %s", new_name, strerror(errno));
@@ -3027,12 +3140,12 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
        if (!(patch->is_copy || patch->is_rename) &&
            (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {
                if (was_deleted(tpatch))
-                       return error("%s: has been deleted/renamed", old_name);
+                       return error(_("%s: has been deleted/renamed"), old_name);
                st_mode = tpatch->new_mode;
        } else if (!cached) {
                stat_ret = lstat(old_name, st);
                if (stat_ret && errno != ENOENT)
-                       return error("%s: %s", old_name, strerror(errno));
+                       return error(_("%s: %s"), old_name, strerror(errno));
        }
 
        if (to_be_deleted(tpatch))
@@ -3043,7 +3156,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                if (pos < 0) {
                        if (patch->is_new < 0)
                                goto is_new;
-                       return error("%s: does not exist in index", old_name);
+                       return error(_("%s: does not exist in index"), old_name);
                }
                *ce = active_cache[pos];
                if (stat_ret < 0) {
@@ -3057,13 +3170,13 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                                return -1;
                }
                if (!cached && verify_index_match(*ce, st))
-                       return error("%s: does not match index", old_name);
+                       return error(_("%s: does not match index"), old_name);
                if (cached)
                        st_mode = (*ce)->ce_mode;
        } else if (stat_ret < 0) {
                if (patch->is_new < 0)
                        goto is_new;
-               return error("%s: %s", old_name, strerror(errno));
+               return error(_("%s: %s"), old_name, strerror(errno));
        }
 
        if (!cached && !tpatch)
@@ -3074,9 +3187,9 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
        if (!patch->old_mode)
                patch->old_mode = st_mode;
        if ((st_mode ^ patch->old_mode) & S_IFMT)
-               return error("%s: wrong type", old_name);
+               return error(_("%s: wrong type"), old_name);
        if (st_mode != patch->old_mode)
-               warning("%s has type %o, expected %o",
+               warning(_("%s has type %o, expected %o"),
                        old_name, st_mode, patch->old_mode);
        if (!patch->new_mode && !patch->is_delete)
                patch->new_mode = st_mode;
@@ -3085,10 +3198,15 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
  is_new:
        patch->is_new = 1;
        patch->is_delete = 0;
+       free(patch->old_name);
        patch->old_name = NULL;
        return 0;
 }
 
+/*
+ * Check and apply the patch in-core; leave the result in patch->result
+ * for the caller to write it out to the final destination.
+ */
 static int check_patch(struct patch *patch)
 {
        struct stat st;
@@ -3126,7 +3244,7 @@ static int check_patch(struct patch *patch)
                if (check_index &&
                    cache_name_pos(new_name, strlen(new_name)) >= 0 &&
                    !ok_if_exists)
-                       return error("%s: already exists in index", new_name);
+                       return error(_("%s: already exists in index"), new_name);
                if (!cached) {
                        int err = check_to_create_blob(new_name, ok_if_exists);
                        if (err)
@@ -3145,13 +3263,13 @@ static int check_patch(struct patch *patch)
                if (!patch->new_mode)
                        patch->new_mode = patch->old_mode;
                if ((patch->old_mode ^ patch->new_mode) & S_IFMT)
-                       return error("new mode (%o) of %s does not match old mode (%o)%s%s",
+                       return error(_("new mode (%o) of %s does not match old mode (%o)%s%s"),
                                patch->new_mode, new_name, patch->old_mode,
                                same ? "" : " of ", same ? "" : old_name);
        }
 
        if (apply_data(patch, &st, ce) < 0)
-               return error("%s: patch does not apply", name);
+               return error(_("%s: patch does not apply"), name);
        patch->rejected = 0;
        return 0;
 }
@@ -3164,7 +3282,7 @@ static int check_patch_list(struct patch *patch)
        while (patch) {
                if (apply_verbosely)
                        say_patch_name(stderr,
-                                      "Checking patch ", patch, "...\n");
+                                      _("Checking patch %s..."), patch);
                err |= check_patch(patch);
                patch = patch->next;
        }
@@ -3219,7 +3337,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
 
                ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
                if (!ce)
-                       die("make_cache_entry failed for path '%s'", name);
+                       die(_("make_cache_entry failed for path '%s'"), name);
                if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
                        die ("Could not add %s to temporary index", name);
        }
@@ -3362,7 +3480,7 @@ static void remove_file(struct patch *patch, int rmdir_empty)
 {
        if (update_index) {
                if (remove_file_from_cache(patch->old_name) < 0)
-                       die("unable to remove %s from index", patch->old_name);
+                       die(_("unable to remove %s from index"), patch->old_name);
        }
        if (!cached) {
                if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
@@ -3389,19 +3507,19 @@ 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 subproject %s"), path);
        } else {
                if (!cached) {
                        if (lstat(path, &st) < 0)
-                               die_errno("unable to stat newly created file '%s'",
+                               die_errno(_("unable to stat newly created file '%s'"),
                                          path);
                        fill_stat_cache_info(ce, &st);
                }
                if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
-                       die("unable to create backing store for newly created file %s", path);
+                       die(_("unable to create backing store for newly created file %s"), path);
        }
        if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
-               die("unable to add cache entry for %s", path);
+               die(_("unable to add cache entry for %s"), path);
 }
 
 static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
@@ -3434,7 +3552,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        strbuf_release(&nbuf);
 
        if (close(fd) < 0)
-               die_errno("closing file '%s'", path);
+               die_errno(_("closing file '%s'"), path);
        return 0;
 }
 
@@ -3483,7 +3601,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                        ++nr;
                }
        }
-       die_errno("unable to write file '%s' mode %o", path, mode);
+       die_errno(_("unable to write file '%s' mode %o"), path, mode);
 }
 
 static void create_file(struct patch *patch)
@@ -3528,6 +3646,7 @@ static int write_out_one_reject(struct patch *patch)
        char namebuf[PATH_MAX];
        struct fragment *frag;
        int cnt = 0;
+       struct strbuf sb = STRBUF_INIT;
 
        for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
                if (!frag->rejected)
@@ -3538,7 +3657,7 @@ static int write_out_one_reject(struct patch *patch)
        if (!cnt) {
                if (apply_verbosely)
                        say_patch_name(stderr,
-                                      "Applied patch ", patch, " cleanly.\n");
+                                      _("Applied patch %s cleanly."), patch);
                return 0;
        }
 
@@ -3546,16 +3665,20 @@ static int write_out_one_reject(struct patch *patch)
         * contents are marked "rejected" at the patch level.
         */
        if (!patch->new_name)
-               die("internal error");
+               die(_("internal error"));
 
        /* Say this even without --verbose */
-       say_patch_name(stderr, "Applying patch ", patch, " with");
-       fprintf(stderr, " %d rejects...\n", cnt);
+       strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...",
+                           "Applying patch %%s with %d rejects...",
+                           cnt),
+                   cnt);
+       say_patch_name(stderr, sb.buf, patch);
+       strbuf_release(&sb);
 
        cnt = strlen(patch->new_name);
        if (ARRAY_SIZE(namebuf) <= cnt + 5) {
                cnt = ARRAY_SIZE(namebuf) - 5;
-               warning("truncating .rej filename to %.*s.rej",
+               warning(_("truncating .rej filename to %.*s.rej"),
                        cnt - 1, patch->new_name);
        }
        memcpy(namebuf, patch->new_name, cnt);
@@ -3563,7 +3686,7 @@ static int write_out_one_reject(struct patch *patch)
 
        rej = fopen(namebuf, "w");
        if (!rej)
-               return error("cannot open %s: %s", namebuf, strerror(errno));
+               return error(_("cannot open %s: %s"), namebuf, strerror(errno));
 
        /* Normal git tools never deal with .rej, so do not pretend
         * this is a git patch by saying --git nor give extended
@@ -3576,10 +3699,10 @@ static int write_out_one_reject(struct patch *patch)
             frag;
             cnt++, frag = frag->next) {
                if (!frag->rejected) {
-                       fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
+                       fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
                        continue;
                }
-               fprintf(stderr, "Rejected hunk #%d.\n", cnt);
+               fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
                fprintf(rej, "%.*s", frag->size, frag->patch);
                if (frag->patch[frag->size-1] != '\n')
                        fputc('\n', rej);
@@ -3665,15 +3788,8 @@ static void prefix_patches(struct patch *p)
        if (!prefix || p->is_toplevel_relative)
                return;
        for ( ; p; p = p->next) {
-               if (p->new_name == p->old_name) {
-                       char *prefixed = p->new_name;
-                       prefix_one(&prefixed);
-                       p->new_name = p->old_name = prefixed;
-               }
-               else {
-                       prefix_one(&p->new_name);
-                       prefix_one(&p->old_name);
-               }
+               prefix_one(&p->new_name);
+               prefix_one(&p->old_name);
        }
 }
 
@@ -3683,12 +3799,10 @@ static void prefix_patches(struct patch *p)
 static int apply_patch(int fd, const char *filename, int options)
 {
        size_t offset;
-       struct strbuf buf = STRBUF_INIT;
+       struct strbuf buf = STRBUF_INIT; /* owns the patch text */
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
 
-       /* FIXME - memory leak when using multiple patch files as inputs */
-       memset(&fn_table, 0, sizeof(struct string_list));
        patch_input_file = filename;
        read_patch_file(&buf, fd);
        offset = 0;
@@ -3712,15 +3826,14 @@ static int apply_patch(int fd, const char *filename, int options)
                        listp = &patch->next;
                }
                else {
-                       /* perhaps free it a bit better? */
-                       free(patch);
+                       free_patch(patch);
                        skipped_patch++;
                }
                offset += nr;
        }
 
        if (!list && !skipped_patch)
-               die("unrecognized input");
+               die(_("unrecognized input"));
 
        if (whitespace_error && (ws_error_action == die_on_ws_error))
                apply = 0;
@@ -3731,7 +3844,7 @@ static int apply_patch(int fd, const char *filename, int options)
 
        if (check_index) {
                if (read_cache() < 0)
-                       die("unable to read index file");
+                       die(_("unable to read index file"));
        }
 
        if ((check || apply) &&
@@ -3754,7 +3867,9 @@ static int apply_patch(int fd, const char *filename, int options)
        if (summary)
                summary_patch_list(list);
 
+       free_patch_list(list);
        strbuf_release(&buf);
+       string_list_clear(&fn_table, 0);
        return 0;
 }
 
@@ -3844,66 +3959,66 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
        const char *whitespace_option = NULL;
 
        struct option builtin_apply_options[] = {
-               { OPTION_CALLBACK, 0, "exclude", NULL, "path",
-                       "don't apply changes matching the given path",
+               { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
+                       N_("don't apply changes matching the given path"),
                        0, option_parse_exclude },
-               { OPTION_CALLBACK, 0, "include", NULL, "path",
-                       "apply changes matching the given path",
+               { OPTION_CALLBACK, 0, "include", NULL, N_("path"),
+                       N_("apply changes matching the given path"),
                        0, option_parse_include },
-               { OPTION_CALLBACK, 'p', NULL, NULL, "num",
-                       "remove <num> leading slashes from traditional diff paths",
+               { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
+                       N_("remove <num> leading slashes from traditional diff paths"),
                        0, option_parse_p },
                OPT_BOOLEAN(0, "no-add", &no_add,
-                       "ignore additions made by the patch"),
+                       N_("ignore additions made by the patch")),
                OPT_BOOLEAN(0, "stat", &diffstat,
-                       "instead of applying the patch, output diffstat for the input"),
+                       N_("instead of applying the patch, output diffstat for the input")),
                OPT_NOOP_NOARG(0, "allow-binary-replacement"),
                OPT_NOOP_NOARG(0, "binary"),
                OPT_BOOLEAN(0, "numstat", &numstat,
-                       "shows number of added and deleted lines in decimal notation"),
+                       N_("shows number of added and deleted lines in decimal notation")),
                OPT_BOOLEAN(0, "summary", &summary,
-                       "instead of applying the patch, output a summary for the input"),
+                       N_("instead of applying the patch, output a summary for the input")),
                OPT_BOOLEAN(0, "check", &check,
-                       "instead of applying the patch, see if the patch is applicable"),
+                       N_("instead of applying the patch, see if the patch is applicable")),
                OPT_BOOLEAN(0, "index", &check_index,
-                       "make sure the patch is applicable to the current index"),
+                       N_("make sure the patch is applicable to the current index")),
                OPT_BOOLEAN(0, "cached", &cached,
-                       "apply a patch without touching the working tree"),
+                       N_("apply a patch without touching the working tree")),
                OPT_BOOLEAN(0, "apply", &force_apply,
-                       "also apply the patch (use with --stat/--summary/--check)"),
+                       N_("also apply the patch (use with --stat/--summary/--check)")),
                OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
-                       "build a temporary index based on embedded index information"),
+                       N_("build a temporary index based on embedded index information")),
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
-                       "paths are separated with NUL character",
+                       N_("paths are separated with NUL character"),
                        PARSE_OPT_NOARG, option_parse_z },
                OPT_INTEGER('C', NULL, &p_context,
-                               "ensure at least <n> lines of context match"),
-               { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
-                       "detect new or modified lines that have whitespace errors",
+                               N_("ensure at least <n> lines of context match")),
+               { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
+                       N_("detect new or modified lines that have whitespace errors"),
                        0, option_parse_whitespace },
                { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
-                       "ignore changes in whitespace when finding context",
+                       N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
                { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
-                       "ignore changes in whitespace when finding context",
+                       N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
                OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
-                       "apply the patch in reverse"),
+                       N_("apply the patch in reverse")),
                OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
-                       "don't expect at least one line of context"),
+                       N_("don't expect at least one line of context")),
                OPT_BOOLEAN(0, "reject", &apply_with_reject,
-                       "leave the rejected hunks in corresponding *.rej files"),
+                       N_("leave the rejected hunks in corresponding *.rej files")),
                OPT_BOOLEAN(0, "allow-overlap", &allow_overlap,
-                       "allow overlapping hunks"),
-               OPT__VERBOSE(&apply_verbosely, "be verbose"),
+                       N_("allow overlapping hunks")),
+               OPT__VERBOSE(&apply_verbosely, N_("be verbose")),
                OPT_BIT(0, "inaccurate-eof", &options,
-                       "tolerate incorrectly detected missing new-line at the end of file",
+                       N_("tolerate incorrectly detected missing new-line at the end of file"),
                        INACCURATE_EOF),
                OPT_BIT(0, "recount", &options,
-                       "do not trust the line counts in the hunk headers",
+                       N_("do not trust the line counts in the hunk headers"),
                        RECOUNT),
-               { OPTION_CALLBACK, 0, "directory", NULL, "root",
-                       "prepend <root> to all filenames",
+               { OPTION_CALLBACK, 0, "directory", NULL, N_("root"),
+                       N_("prepend <root> to all filenames"),
                        0, option_parse_directory },
                OPT_END()
        };
@@ -3924,10 +4039,10 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
        if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
                apply = 0;
        if (check_index && is_not_gitdir)
-               die("--index outside a repository");
+               die(_("--index outside a repository"));
        if (cached) {
                if (is_not_gitdir)
-                       die("--cached outside a repository");
+                       die(_("--cached outside a repository"));
                check_index = 1;
        }
        for (i = 0; i < argc; i++) {
@@ -3943,7 +4058,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
 
                fd = open(arg, O_RDONLY);
                if (fd < 0)
-                       die_errno("can't open patch '%s'", arg);
+                       die_errno(_("can't open patch '%s'"), arg);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
                errs |= apply_patch(fd, arg, options);
@@ -3957,32 +4072,32 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                    squelch_whitespace_errors < whitespace_error) {
                        int squelched =
                                whitespace_error - squelch_whitespace_errors;
-                       warning("squelched %d "
-                               "whitespace error%s",
-                               squelched,
-                               squelched == 1 ? "" : "s");
+                       warning(Q_("squelched %d whitespace error",
+                                  "squelched %d whitespace errors",
+                                  squelched),
+                               squelched);
                }
                if (ws_error_action == die_on_ws_error)
-                       die("%d line%s add%s whitespace errors.",
-                           whitespace_error,
-                           whitespace_error == 1 ? "" : "s",
-                           whitespace_error == 1 ? "s" : "");
+                       die(Q_("%d line adds whitespace errors.",
+                              "%d lines add whitespace errors.",
+                              whitespace_error),
+                           whitespace_error);
                if (applied_after_fixing_ws && apply)
                        warning("%d line%s applied after"
                                " fixing whitespace errors.",
                                applied_after_fixing_ws,
                                applied_after_fixing_ws == 1 ? "" : "s");
                else if (whitespace_error)
-                       warning("%d line%s add%s whitespace errors.",
-                               whitespace_error,
-                               whitespace_error == 1 ? "" : "s",
-                               whitespace_error == 1 ? "s" : "");
+                       warning(Q_("%d line adds whitespace errors.",
+                                  "%d lines add whitespace errors.",
+                                  whitespace_error),
+                               whitespace_error);
        }
 
        if (update_index) {
                if (write_cache(newfd, active_cache, active_nr) ||
                    commit_locked_index(&lock_file))
-                       die("Unable to write new index file");
+                       die(_("Unable to write new index file"));
        }
 
        return !!errs;
index b35bd6249de66d02b7f33eb7aae4866193447156..24d3dd52920542b993d4945ccb2642a74da5f759 100644 (file)
@@ -88,6 +88,20 @@ struct origin {
        char path[FLEX_ARRAY];
 };
 
+static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
+                     xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
+{
+       xpparam_t xpp = {0};
+       xdemitconf_t xecfg = {0};
+       xdemitcb_t ecb = {NULL};
+
+       xpp.flags = xdl_opts;
+       xecfg.ctxlen = ctxlen;
+       xecfg.hunk_func = hunk_func;
+       ecb.priv = cb_data;
+       return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
+}
+
 /*
  * Prepare diff_filespec and convert it using diff textconv API
  * if the textconv driver exists.
@@ -759,12 +773,14 @@ struct blame_chunk_cb_data {
        long tlno;
 };
 
-static void blame_chunk_cb(void *data, long same, long p_next, long t_next)
+static int blame_chunk_cb(long start_a, long count_a,
+                         long start_b, long count_b, void *data)
 {
        struct blame_chunk_cb_data *d = data;
-       blame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);
-       d->plno = p_next;
-       d->tlno = t_next;
+       blame_chunk(d->sb, d->tlno, d->plno, start_b, d->target, d->parent);
+       d->plno = start_a + count_a;
+       d->tlno = start_b + count_b;
+       return 0;
 }
 
 /*
@@ -779,8 +795,7 @@ static int pass_blame_to_parent(struct scoreboard *sb,
        int last_in_target;
        mmfile_t file_p, file_o;
        struct blame_chunk_cb_data d;
-       xpparam_t xpp;
-       xdemitconf_t xecfg;
+
        memset(&d, 0, sizeof(d));
        d.sb = sb; d.target = target; d.parent = parent;
        last_in_target = find_last_in_target(sb, target);
@@ -791,11 +806,7 @@ static int pass_blame_to_parent(struct scoreboard *sb,
        fill_origin_blob(&sb->revs->diffopt, target, &file_o);
        num_get_patch++;
 
-       memset(&xpp, 0, sizeof(xpp));
-       xpp.flags = xdl_opts;
-       memset(&xecfg, 0, sizeof(xecfg));
-       xecfg.ctxlen = 0;
-       xdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);
+       diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d);
        /* The rest (i.e. anything after tlno) are the same as the parent */
        blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
 
@@ -899,12 +910,15 @@ struct handle_split_cb_data {
        long tlno;
 };
 
-static void handle_split_cb(void *data, long same, long p_next, long t_next)
+static int handle_split_cb(long start_a, long count_a,
+                          long start_b, long count_b, void *data)
 {
        struct handle_split_cb_data *d = data;
-       handle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);
-       d->plno = p_next;
-       d->tlno = t_next;
+       handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
+                    d->split);
+       d->plno = start_a + count_a;
+       d->tlno = start_b + count_b;
+       return 0;
 }
 
 /*
@@ -922,8 +936,7 @@ static void find_copy_in_blob(struct scoreboard *sb,
        int cnt;
        mmfile_t file_o;
        struct handle_split_cb_data d;
-       xpparam_t xpp;
-       xdemitconf_t xecfg;
+
        memset(&d, 0, sizeof(d));
        d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
        /*
@@ -943,12 +956,8 @@ static void find_copy_in_blob(struct scoreboard *sb,
         * file_o is a part of final image we are annotating.
         * file_p partially may match that image.
         */
-       memset(&xpp, 0, sizeof(xpp));
-       xpp.flags = xdl_opts;
-       memset(&xecfg, 0, sizeof(xecfg));
-       xecfg.ctxlen = 1;
        memset(split, 0, sizeof(struct blame_entry [3]));
-       xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);
+       diff_hunks(file_p, &file_o, 1, handle_split_cb, &d);
        /* remainder, if any, all match the preimage */
        handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
 }
@@ -2302,6 +2311,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
                OPT_BIT('e', "show-email", &output_option, "Show author email instead of name (Default: off)", OUTPUT_SHOW_EMAIL),
                OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
+               OPT_BIT(0, "minimal", &xdl_opts, "Spend extra cycles to find better match", XDF_NEED_MINIMAL),
                OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
                OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
                { OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
index d8cccf725d3fab24ad585a26629373fc987bb3f8..0e060f2e4a982efdda0f88d021e0a9d3f23c6b75 100644 (file)
@@ -15,6 +15,8 @@
 #include "branch.h"
 #include "diff.h"
 #include "revision.h"
+#include "string-list.h"
+#include "column.h"
 
 static const char * const builtin_branch_usage[] = {
        "git branch [options] [-r | -a] [--merged | --no-merged]",
@@ -53,6 +55,9 @@ static enum merge_filter {
 } merge_filter;
 static unsigned char merge_filter_ref[20];
 
+static struct string_list output = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
+
 static int parse_branch_color_slot(const char *var, int ofs)
 {
        if (!strcasecmp(var+ofs, "plain"))
@@ -70,6 +75,8 @@ static int parse_branch_color_slot(const char *var, int ofs)
 
 static int git_branch_config(const char *var, const char *value, void *cb)
 {
+       if (!prefixcmp(var, "column."))
+               return git_column_config(var, value, "branch", &colopts);
        if (!strcmp(var, "color.branch")) {
                branch_use_color = git_config_colorbool(var, value);
                return 0;
@@ -146,26 +153,28 @@ static int branch_merged(int kind, const char *name,
        return merged;
 }
 
-static int delete_branches(int argc, const char **argv, int force, int kinds)
+static int delete_branches(int argc, const char **argv, int force, int kinds,
+                          int quiet)
 {
        struct commit *rev, *head_rev = NULL;
        unsigned char sha1[20];
        char *name = NULL;
-       const char *fmt, *remote;
+       const char *fmt;
        int i;
        int ret = 0;
+       int remote_branch = 0;
        struct strbuf bname = STRBUF_INIT;
 
        switch (kinds) {
        case REF_REMOTE_BRANCH:
                fmt = "refs/remotes/%s";
-               /* TRANSLATORS: This is "remote " in "remote branch '%s' not found" */
-               remote = _("remote ");
+               /* For subsequent UI messages */
+               remote_branch = 1;
+
                force = 1;
                break;
        case REF_LOCAL_BRANCH:
                fmt = "refs/heads/%s";
-               remote = "";
                break;
        default:
                die(_("cannot use -a with -d"));
@@ -189,8 +198,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
 
                name = xstrdup(mkpath(fmt, bname.buf));
                if (read_ref(name, sha1)) {
-                       error(_("%sbranch '%s' not found."),
-                                       remote, bname.buf);
+                       error(remote_branch
+                             ? _("remote branch '%s' not found.")
+                             : _("branch '%s' not found."), bname.buf);
                        ret = 1;
                        continue;
                }
@@ -211,14 +221,19 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                }
 
                if (delete_ref(name, sha1, 0)) {
-                       error(_("Error deleting %sbranch '%s'"), remote,
+                       error(remote_branch
+                             ? _("Error deleting remote branch '%s'")
+                             : _("Error deleting branch '%s'"),
                              bname.buf);
                        ret = 1;
                } else {
                        struct strbuf buf = STRBUF_INIT;
-                       printf(_("Deleted %sbranch %s (was %s).\n"), remote,
-                              bname.buf,
-                              find_unique_abbrev(sha1, DEFAULT_ABBREV));
+                       if (!quiet)
+                               printf(remote_branch
+                                      ? _("Deleted remote branch %s (was %s).\n")
+                                      : _("Deleted branch %s (was %s).\n"),
+                                      bname.buf,
+                                      find_unique_abbrev(sha1, DEFAULT_ABBREV));
                        strbuf_addf(&buf, "branch.%s", bname.buf);
                        if (git_config_rename_section(buf.buf, NULL) < 0)
                                warning(_("Update of config-file failed"));
@@ -376,6 +391,7 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
                int show_upstream_ref)
 {
        int ours, theirs;
+       char *ref = NULL;
        struct branch *branch = branch_get(branch_name);
 
        if (!stat_tracking_info(branch, &ours, &theirs)) {
@@ -386,16 +402,29 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
                return;
        }
 
-       strbuf_addch(stat, '[');
        if (show_upstream_ref)
-               strbuf_addf(stat, "%s: ",
-                       shorten_unambiguous_ref(branch->merge[0]->dst, 0));
-       if (!ours)
-               strbuf_addf(stat, _("behind %d] "), theirs);
-       else if (!theirs)
-               strbuf_addf(stat, _("ahead %d] "), ours);
-       else
-               strbuf_addf(stat, _("ahead %d, behind %d] "), ours, theirs);
+               ref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);
+       if (!ours) {
+               if (ref)
+                       strbuf_addf(stat, _("[%s: behind %d]"), ref, theirs);
+               else
+                       strbuf_addf(stat, _("[behind %d]"), theirs);
+
+       } else if (!theirs) {
+               if (ref)
+                       strbuf_addf(stat, _("[%s: ahead %d]"), ref, ours);
+               else
+                       strbuf_addf(stat, _("[ahead %d]"), ours);
+       } else {
+               if (ref)
+                       strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
+                                   ref, ours, theirs);
+               else
+                       strbuf_addf(stat, _("[ahead %d, behind %d]"),
+                                   ours, theirs);
+       }
+       strbuf_addch(stat, ' ');
+       free(ref);
 }
 
 static int matches_merge_filter(struct commit *commit)
@@ -474,7 +503,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
        else if (verbose)
                /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
                add_verbose_info(&out, item, verbose, abbrev);
-       printf("%s\n", out.buf);
+       if (column_active(colopts)) {
+               assert(!verbose && "--column and --verbose are incompatible");
+               string_list_append(&output, out.buf);
+       } else {
+               printf("%s\n", out.buf);
+       }
        strbuf_release(&name);
        strbuf_release(&out);
 }
@@ -655,7 +689,7 @@ static int edit_branch_description(const char *branch_name)
        fp = fopen(git_path(edit_description), "w");
        if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
                strbuf_release(&buf);
-               return error(_("could not write branch description template: %s\n"),
+               return error(_("could not write branch description template: %s"),
                             strerror(errno));
        }
        strbuf_reset(&buf);
@@ -678,6 +712,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        int delete = 0, rename = 0, force_create = 0, list = 0;
        int verbose = 0, abbrev = -1, detached = 0;
        int reflog = 0, edit_description = 0;
+       int quiet = 0;
        enum branch_track track;
        int kinds = REF_LOCAL_BRANCH;
        struct commit_list *with_commit = NULL;
@@ -686,6 +721,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_GROUP("Generic options"),
                OPT__VERBOSE(&verbose,
                        "show hash and subject, give twice for upstream branch"),
+               OPT__QUIET(&quiet, "suppress informational messages"),
                OPT_SET_INT('t', "track",  &track, "set up tracking mode (see git-pull(1))",
                        BRANCH_TRACK_EXPLICIT),
                OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
@@ -731,6 +767,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
                        opt_parse_merge_filter, (intptr_t) "HEAD",
                },
+               OPT_COLUMN(0, "column", &colopts, "list branches in columns"),
                OPT_END(),
        };
 
@@ -753,6 +790,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        }
        hashcpy(merge_filter_ref, head_sha1);
 
+
        argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
                             0);
 
@@ -764,12 +802,22 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        if (abbrev == -1)
                abbrev = DEFAULT_ABBREV;
+       finalize_colopts(&colopts, -1);
+       if (verbose) {
+               if (explicitly_enable_column(colopts))
+                       die(_("--column and --verbose are incompatible"));
+               colopts = 0;
+       }
 
        if (delete)
-               return delete_branches(argc, argv, delete > 1, kinds);
-       else if (list)
-               return print_ref_list(kinds, detached, verbose, abbrev,
-                                     with_commit, argv);
+               return delete_branches(argc, argv, delete > 1, kinds, quiet);
+       else if (list) {
+               int ret = print_ref_list(kinds, detached, verbose, abbrev,
+                                        with_commit, argv);
+               print_columns(&output, colopts, NULL);
+               string_list_clear(&output, 0);
+               return ret;
+       }
        else if (edit_description) {
                const char *branch_name;
                struct strbuf branch_ref = STRBUF_INIT;
@@ -808,7 +856,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                if (kinds != REF_LOCAL_BRANCH)
                        die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
                create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
-                             force_create, reflog, 0, track);
+                             force_create, reflog, 0, quiet, track);
        } else
                usage_with_options(builtin_branch_usage, options);
 
index 8ed501f220424976cc30f4a4dbf3d59f979902be..36a9104433e23422aab39b1912e998a7f54cd3f4 100644 (file)
@@ -11,6 +11,7 @@
 #include "parse-options.h"
 #include "diff.h"
 #include "userdiff.h"
+#include "streaming.h"
 
 #define BATCH 1
 #define BATCH_CHECK 2
@@ -127,6 +128,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                        return cmd_ls_tree(2, ls_args, NULL);
                }
 
+               if (type == OBJ_BLOB)
+                       return stream_blob_to_fd(1, sha1, NULL, 0);
                buf = read_sha1_file(sha1, &type, &size);
                if (!buf)
                        die("Cannot read object %s", obj_name);
@@ -149,6 +152,28 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                break;
 
        case 0:
+               if (type_from_string(exp_type) == OBJ_BLOB) {
+                       unsigned char blob_sha1[20];
+                       if (sha1_object_info(sha1, NULL) == OBJ_TAG) {
+                               enum object_type type;
+                               unsigned long size;
+                               char *buffer = read_sha1_file(sha1, &type, &size);
+                               if (memcmp(buffer, "object ", 7) ||
+                                   get_sha1_hex(buffer + 7, blob_sha1))
+                                       die("%s not a valid tag", sha1_to_hex(sha1));
+                               free(buffer);
+                       } else
+                               hashcpy(blob_sha1, sha1);
+
+                       if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
+                               return stream_blob_to_fd(1, blob_sha1, NULL, 0);
+                       /*
+                        * we attempted to dereference a tag to a blob
+                        * and failed; there may be new dereference
+                        * mechanisms this code is not aware of.
+                        * fall-back to the usual case.
+                        */
+               }
                buf = read_object_with_reference(sha1, exp_type, &size, NULL);
                break;
 
index 6b9061f26f5f33ae1ded811891e933441c210fb0..3ddda34f7a96c56707614dbaac40631467a2b54b 100644 (file)
@@ -543,6 +543,7 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log,
                                      opts->new_branch_force ? 1 : 0,
+                                     opts->quiet,
                                      opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
@@ -671,10 +672,10 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
  * HEAD.  If it is not reachable from any ref, this is the last chance
  * for the user to do so without resorting to reflog.
  */
-static void orphaned_commit_warning(struct commit *commit)
+static void orphaned_commit_warning(struct commit *old, struct commit *new)
 {
        struct rev_info revs;
-       struct object *object = &commit->object;
+       struct object *object = &old->object;
        struct object_array refs;
 
        init_revisions(&revs, NULL);
@@ -684,16 +685,17 @@ static void orphaned_commit_warning(struct commit *commit)
        add_pending_object(&revs, object, sha1_to_hex(object->sha1));
 
        for_each_ref(add_pending_uninteresting_ref, &revs);
+       add_pending_sha1(&revs, "HEAD", new->object.sha1, UNINTERESTING);
 
        refs = revs.pending;
        revs.leak_pending = 1;
 
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
-       if (!(commit->object.flags & UNINTERESTING))
-               suggest_reattach(commit, &revs);
+       if (!(old->object.flags & UNINTERESTING))
+               suggest_reattach(old, &revs);
        else
-               describe_detached_head(_("Previous HEAD position was"), commit);
+               describe_detached_head(_("Previous HEAD position was"), old);
 
        clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
        free(refs.objects);
@@ -730,7 +732,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
        }
 
        if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
-               orphaned_commit_warning(old.commit);
+               orphaned_commit_warning(old.commit, new->commit);
 
        update_refs_for_switch(opts, &old, new);
 
@@ -1090,7 +1092,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        if (opts.writeout_stage)
                die(_("--ours/--theirs is incompatible with switching branches."));
 
-       if (!new.commit) {
+       if (!new.commit && opts.new_branch) {
                unsigned char rev[20];
                int flag;
 
index bbd5c96237fc332e159face6c8678d8ae3b9a3e9..a4d8d25ee319c2bbcfe5b450468cfb41d3fcd0d6 100644 (file)
@@ -569,7 +569,7 @@ static int checkout(void)
        opts.update = 1;
        opts.merge = 1;
        opts.fn = oneway_merge;
-       opts.verbose_update = (option_verbosity > 0);
+       opts.verbose_update = (option_verbosity >= 0);
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
 
diff --git a/builtin/column.c b/builtin/column.c
new file mode 100644 (file)
index 0000000..5ea798a
--- /dev/null
@@ -0,0 +1,59 @@
+#include "builtin.h"
+#include "cache.h"
+#include "strbuf.h"
+#include "parse-options.h"
+#include "string-list.h"
+#include "column.h"
+
+static const char * const builtin_column_usage[] = {
+       "git column [options]",
+       NULL
+};
+static unsigned int colopts;
+
+static int column_config(const char *var, const char *value, void *cb)
+{
+       return git_column_config(var, value, cb, &colopts);
+}
+
+int cmd_column(int argc, const char **argv, const char *prefix)
+{
+       struct string_list list = STRING_LIST_INIT_DUP;
+       struct strbuf sb = STRBUF_INIT;
+       struct column_options copts;
+       const char *command = NULL, *real_command = NULL;
+       struct option options[] = {
+               OPT_STRING(0, "command", &real_command, "name", "lookup config vars"),
+               OPT_COLUMN(0, "mode", &colopts, "layout to use"),
+               OPT_INTEGER(0, "raw-mode", &colopts, "layout to use"),
+               OPT_INTEGER(0, "width", &copts.width, "Maximum width"),
+               OPT_STRING(0, "indent", &copts.indent, "string", "Padding space on left border"),
+               OPT_INTEGER(0, "nl", &copts.nl, "Padding space on right border"),
+               OPT_INTEGER(0, "padding", &copts.padding, "Padding space between columns"),
+               OPT_END()
+       };
+
+       /* This one is special and must be the first one */
+       if (argc > 1 && !prefixcmp(argv[1], "--command=")) {
+               command = argv[1] + 10;
+               git_config(column_config, (void *)command);
+       } else
+               git_config(column_config, NULL);
+
+       memset(&copts, 0, sizeof(copts));
+       copts.width = term_columns();
+       copts.padding = 1;
+       argc = parse_options(argc, argv, "", options, builtin_column_usage, 0);
+       if (argc)
+               usage_with_options(builtin_column_usage, options);
+       if (real_command || command) {
+               if (!real_command || !command || strcmp(real_command, command))
+                       die(_("--command must be the first argument"));
+       }
+       finalize_colopts(&colopts, -1);
+       while (!strbuf_getline(&sb, stdin, '\n'))
+               string_list_append(&list, sb.buf);
+
+       print_columns(&list, colopts, &copts);
+       return 0;
+}
index 3714582e1988f7c286412afb779cbfefe4849657..a2ec73d7382b62b182e344baeef9e26488187818 100644 (file)
@@ -27,6 +27,7 @@
 #include "quote.h"
 #include "submodule.h"
 #include "gpg-interface.h"
+#include "column.h"
 
 static const char * const builtin_commit_usage[] = {
        "git commit [options] [--] <filepattern>...",
@@ -109,13 +110,11 @@ static int show_ignored_in_status;
 static const char *only_include_assumed;
 static struct strbuf message = STRBUF_INIT;
 
-static int null_termination;
 static enum {
        STATUS_FORMAT_LONG,
        STATUS_FORMAT_SHORT,
        STATUS_FORMAT_PORCELAIN
 } status_format = STATUS_FORMAT_LONG;
-static int status_show_branch;
 
 static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 {
@@ -129,59 +128,6 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
-static struct option builtin_commit_options[] = {
-       OPT__QUIET(&quiet, "suppress summary after successful commit"),
-       OPT__VERBOSE(&verbose, "show diff in commit message template"),
-
-       OPT_GROUP("Commit message options"),
-       OPT_FILENAME('F', "file", &logfile, "read message from file"),
-       OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
-       OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
-       OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
-       OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
-       OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
-       OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
-       OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
-       OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
-       OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
-       OPT_FILENAME('t', "template", &template_file, "use specified template file"),
-       OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
-       OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
-       OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
-       { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
-         "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-       /* end commit message options */
-
-       OPT_GROUP("Commit contents options"),
-       OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
-       OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
-       OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
-       OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"),
-       OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
-       OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
-       OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
-       OPT_SET_INT(0, "short", &status_format, "show status concisely",
-                   STATUS_FORMAT_SHORT),
-       OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
-       OPT_SET_INT(0, "porcelain", &status_format,
-                   "machine-readable output", STATUS_FORMAT_PORCELAIN),
-       OPT_BOOLEAN('z', "null", &null_termination,
-                   "terminate entries with NUL"),
-       OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
-       OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
-       { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
-       /* end commit contents options */
-
-       { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
-         "ok to record an empty change",
-         PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-       { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
-         "ok to record a change with an empty message",
-         PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-
-       OPT_END()
-};
-
 static void determine_whence(struct wt_status *s)
 {
        if (file_exists(git_path("MERGE_HEAD")))
@@ -194,24 +140,6 @@ static void determine_whence(struct wt_status *s)
                s->whence = whence;
 }
 
-static const char *whence_s(void)
-{
-       const char *s = "";
-
-       switch (whence) {
-       case FROM_COMMIT:
-               break;
-       case FROM_MERGE:
-               s = _("merge");
-               break;
-       case FROM_CHERRY_PICK:
-               s = _("cherry-pick");
-               break;
-       }
-
-       return s;
-}
-
 static void rollback_index_files(void)
 {
        switch (commit_style) {
@@ -453,8 +381,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         */
        commit_style = COMMIT_PARTIAL;
 
-       if (whence != FROM_COMMIT)
-               die(_("cannot do a partial commit during a %s."), whence_s());
+       if (whence != FROM_COMMIT) {
+               if (whence == FROM_MERGE)
+                       die(_("cannot do a partial commit during a merge."));
+               else if (whence == FROM_CHERRY_PICK)
+                       die(_("cannot do a partial commit during a cherry-pick."));
+       }
 
        memset(&partial, 0, sizeof(partial));
        partial.strdup_strings = 1;
@@ -513,10 +445,10 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
 
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
-               wt_shortstatus_print(s, null_termination, status_show_branch);
+               wt_shortstatus_print(s);
                break;
        case STATUS_FORMAT_PORCELAIN:
-               wt_porcelain_print(s, null_termination);
+               wt_porcelain_print(s);
                break;
        case STATUS_FORMAT_LONG:
                wt_status_print(s);
@@ -533,9 +465,20 @@ static int is_a_merge(const struct commit *current_head)
 
 static const char sign_off_header[] = "Signed-off-by: ";
 
+static void export_one(const char *var, const char *s, const char *e, int hack)
+{
+       struct strbuf buf = STRBUF_INIT;
+       if (hack)
+               strbuf_addch(&buf, hack);
+       strbuf_addf(&buf, "%.*s", (int)(e - s), s);
+       setenv(var, buf.buf, 1);
+       strbuf_release(&buf);
+}
+
 static void determine_author_info(struct strbuf *author_ident)
 {
        char *name, *email, *date;
+       struct ident_split author;
 
        name = getenv("GIT_AUTHOR_NAME");
        email = getenv("GIT_AUTHOR_EMAIL");
@@ -585,6 +528,11 @@ static void determine_author_info(struct strbuf *author_ident)
                date = force_date;
        strbuf_addstr(author_ident, fmt_ident(name, email, date,
                                              IDENT_ERROR_ON_NO_NAME));
+       if (!split_ident_line(&author, author_ident->buf, author_ident->len)) {
+               export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0);
+               export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
+               export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
+       }
 }
 
 static int ends_rfc2822_footer(struct strbuf *sb)
@@ -652,6 +600,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        int ident_shown = 0;
        int clean_message_contents = (cleanup_mode != CLEANUP_NONE);
 
+       /* This checks and barfs if author is badly specified */
+       determine_author_info(author_ident);
+
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
 
@@ -771,37 +722,37 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
        strbuf_release(&sb);
 
-       /* This checks and barfs if author is badly specified */
-       determine_author_info(author_ident);
-
        /* This checks if committer ident is explicitly given */
        strbuf_addstr(&committer_ident, git_committer_info(0));
        if (use_editor && include_status) {
                char *ai_tmp, *ci_tmp;
                if (whence != FROM_COMMIT)
                        status_printf_ln(s, GIT_COLOR_NORMAL,
-                               _("\n"
-                               "It looks like you may be committing a %s.\n"
-                               "If this is not correct, please remove the file\n"
-                               "       %s\n"
-                               "and try again.\n"
-                               ""),
-                               whence_s(),
+                           whence == FROM_MERGE
+                               ? _("\n"
+                                       "It looks like you may be committing a merge.\n"
+                                       "If this is not correct, please remove the file\n"
+                                       "       %s\n"
+                                       "and try again.\n")
+                               : _("\n"
+                                       "It looks like you may be committing a cherry-pick.\n"
+                                       "If this is not correct, please remove the file\n"
+                                       "       %s\n"
+                                       "and try again.\n"),
                                git_path(whence == FROM_MERGE
                                         ? "MERGE_HEAD"
                                         : "CHERRY_PICK_HEAD"));
 
                fprintf(s->fp, "\n");
-               status_printf(s, GIT_COLOR_NORMAL,
-                       _("Please enter the commit message for your changes."));
                if (cleanup_mode == CLEANUP_ALL)
-                       status_printf_more(s, GIT_COLOR_NORMAL,
-                               _(" Lines starting\n"
-                               "with '#' will be ignored, and an empty"
+                       status_printf(s, GIT_COLOR_NORMAL,
+                               _("Please enter the commit message for your changes."
+                               " Lines starting\nwith '#' will be ignored, and an empty"
                                " message aborts the commit.\n"));
                else /* CLEANUP_SPACE, that is. */
-                       status_printf_more(s, GIT_COLOR_NORMAL,
-                               _(" Lines starting\n"
+                       status_printf(s, GIT_COLOR_NORMAL,
+                               _("Please enter the commit message for your changes."
+                               " Lines starting\n"
                                "with '#' will be kept; you may remove them"
                                " yourself if you want to.\n"
                                "An empty message aborts the commit.\n"));
@@ -905,27 +856,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        return 1;
 }
 
-/*
- * Find out if the message in the strbuf contains only whitespace and
- * Signed-off-by lines.
- */
-static int message_is_empty(struct strbuf *sb)
+static int rest_is_empty(struct strbuf *sb, int start)
 {
-       struct strbuf tmpl = STRBUF_INIT;
+       int i, eol;
        const char *nl;
-       int eol, i, start = 0;
-
-       if (cleanup_mode == CLEANUP_NONE && sb->len)
-               return 0;
-
-       /* See if the template is just a prefix of the message. */
-       if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
-               stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-               if (start + tmpl.len <= sb->len &&
-                   memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
-                       start += tmpl.len;
-       }
-       strbuf_release(&tmpl);
 
        /* Check if the rest is just whitespace and Signed-of-by's. */
        for (i = start; i < sb->len; i++) {
@@ -948,6 +882,40 @@ static int message_is_empty(struct strbuf *sb)
        return 1;
 }
 
+/*
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
+ */
+static int message_is_empty(struct strbuf *sb)
+{
+       if (cleanup_mode == CLEANUP_NONE && sb->len)
+               return 0;
+       return rest_is_empty(sb, 0);
+}
+
+/*
+ * See if the user edited the message in the editor or left what
+ * was in the template intact
+ */
+static int template_untouched(struct strbuf *sb)
+{
+       struct strbuf tmpl = STRBUF_INIT;
+       char *start;
+
+       if (cleanup_mode == CLEANUP_NONE && sb->len)
+               return 0;
+
+       if (!template_file || strbuf_read_file(&tmpl, template_file, 0) <= 0)
+               return 0;
+
+       stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
+       start = (char *)skip_prefix(sb->buf, tmpl.buf);
+       if (!start)
+               start = sb->buf;
+       strbuf_release(&tmpl);
+       return rest_is_empty(sb, start - sb->buf);
+}
+
 static const char *find_author_by_nickname(const char *name)
 {
        struct rev_info revs;
@@ -1013,6 +981,7 @@ static const char *read_commit_message(const char *name)
 }
 
 static int parse_and_validate_options(int argc, const char *argv[],
+                                     const struct option *options,
                                      const char * const usage[],
                                      const char *prefix,
                                      struct commit *current_head,
@@ -1020,8 +989,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
 {
        int f = 0;
 
-       argc = parse_options(argc, argv, prefix, builtin_commit_options, usage,
-                            0);
+       argc = parse_options(argc, argv, prefix, options, usage, 0);
 
        if (force_author && !strchr(force_author, '>'))
                force_author = find_author_by_nickname(force_author);
@@ -1039,8 +1007,12 @@ static int parse_and_validate_options(int argc, const char *argv[],
        /* Sanity check options */
        if (amend && !current_head)
                die(_("You have nothing to amend."));
-       if (amend && whence != FROM_COMMIT)
-               die(_("You are in the middle of a %s -- cannot amend."), whence_s());
+       if (amend && whence != FROM_COMMIT) {
+               if (whence == FROM_MERGE)
+                       die(_("You are in the middle of a merge -- cannot amend."));
+               else if (whence == FROM_CHERRY_PICK)
+                       die(_("You are in the middle of a cherry-pick -- cannot amend."));
+       }
        if (fixup_message && squash_message)
                die(_("Options --squash and --fixup cannot be used together"));
        if (use_message)
@@ -1055,6 +1027,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
                die(_("Only one of -c/-C/-F/--fixup can be used."));
        if (message.len && f > 0)
                die((_("Option -m cannot be combined with -c/-C/-F/--fixup.")));
+       if (f || message.len)
+               template_file = NULL;
        if (edit_message)
                use_message = edit_message;
        if (amend && !use_message && !fixup_message)
@@ -1100,7 +1074,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
        if (all && argc > 0)
                die(_("Paths with -a does not make sense."));
 
-       if (null_termination && status_format == STATUS_FORMAT_LONG)
+       if (s->null_termination && status_format == STATUS_FORMAT_LONG)
                status_format = STATUS_FORMAT_PORCELAIN;
        if (status_format != STATUS_FORMAT_LONG)
                dry_run = 1;
@@ -1145,6 +1119,8 @@ static int git_status_config(const char *k, const char *v, void *cb)
 {
        struct wt_status *s = cb;
 
+       if (!prefixcmp(k, "column."))
+               return git_column_config(k, v, "status", &s->colopts);
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
                s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
@@ -1187,19 +1163,19 @@ static int git_status_config(const char *k, const char *v, void *cb)
 
 int cmd_status(int argc, const char **argv, const char *prefix)
 {
-       struct wt_status s;
+       static struct wt_status s;
        int fd;
        unsigned char sha1[20];
        static struct option builtin_status_options[] = {
                OPT__VERBOSE(&verbose, "be verbose"),
                OPT_SET_INT('s', "short", &status_format,
                            "show status concisely", STATUS_FORMAT_SHORT),
-               OPT_BOOLEAN('b', "branch", &status_show_branch,
+               OPT_BOOLEAN('b', "branch", &s.show_branch,
                            "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
                            "machine-readable output",
                            STATUS_FORMAT_PORCELAIN),
-               OPT_BOOLEAN('z', "null", &null_termination,
+               OPT_BOOLEAN('z', "null", &s.null_termination,
                            "terminate entries with NUL"),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
                  "mode",
@@ -1210,6 +1186,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
                  "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+               OPT_COLUMN(0, "column", &s.colopts, "list untracked files in columns"),
                OPT_END(),
        };
 
@@ -1223,8 +1200,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix,
                             builtin_status_options,
                             builtin_status_usage, 0);
+       finalize_colopts(&s.colopts, -1);
 
-       if (null_termination && status_format == STATUS_FORMAT_LONG)
+       if (s.null_termination && status_format == STATUS_FORMAT_LONG)
                status_format = STATUS_FORMAT_PORCELAIN;
 
        handle_untracked_files_arg(&s);
@@ -1249,10 +1227,10 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
-               wt_shortstatus_print(&s, null_termination, status_show_branch);
+               wt_shortstatus_print(&s);
                break;
        case STATUS_FORMAT_PORCELAIN:
-               wt_porcelain_print(&s, null_termination);
+               wt_porcelain_print(&s);
                break;
        case STATUS_FORMAT_LONG:
                s.verbose = verbose;
@@ -1387,6 +1365,60 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
 
 int cmd_commit(int argc, const char **argv, const char *prefix)
 {
+       static struct wt_status s;
+       static struct option builtin_commit_options[] = {
+               OPT__QUIET(&quiet, "suppress summary after successful commit"),
+               OPT__VERBOSE(&verbose, "show diff in commit message template"),
+
+               OPT_GROUP("Commit message options"),
+               OPT_FILENAME('F', "file", &logfile, "read message from file"),
+               OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
+               OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
+               OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
+               OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
+               OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
+               OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
+               OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
+               OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C/-c/--amend)"),
+               OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
+               OPT_FILENAME('t', "template", &template_file, "use specified template file"),
+               OPT_BOOL('e', "edit", &edit_flag, "force edit of commit"),
+               OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+               OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
+               { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
+                 "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+               /* end commit message options */
+
+               OPT_GROUP("Commit contents options"),
+               OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
+               OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
+               OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
+               OPT_BOOLEAN('p', "patch", &patch_interactive, "interactively add changes"),
+               OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
+               OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+               OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
+               OPT_SET_INT(0, "short", &status_format, "show status concisely",
+                           STATUS_FORMAT_SHORT),
+               OPT_BOOLEAN(0, "branch", &s.show_branch, "show branch information"),
+               OPT_SET_INT(0, "porcelain", &status_format,
+                           "machine-readable output", STATUS_FORMAT_PORCELAIN),
+               OPT_BOOLEAN('z', "null", &s.null_termination,
+                           "terminate entries with NUL"),
+               OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
+               OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
+               { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+               /* end commit contents options */
+
+               { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
+                 "ok to record an empty change",
+                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+               { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
+                 "ok to record a change with an empty message",
+                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+
+               OPT_END()
+       };
+
        struct strbuf sb = STRBUF_INIT;
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
@@ -1396,7 +1428,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct commit_list *parents = NULL, **pptr = &parents;
        struct stat statbuf;
        int allow_fast_forward = 1;
-       struct wt_status s;
        struct commit *current_head = NULL;
        struct commit_extra_header *extra = NULL;
 
@@ -1406,6 +1437,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        wt_status_prepare(&s);
        git_config(git_commit_config, &s);
        determine_whence(&s);
+       s.colopts = 0;
 
        if (get_sha1("HEAD", sha1))
                current_head = NULL;
@@ -1414,7 +1446,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                if (!current_head || parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
-       argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
+       argc = parse_and_validate_options(argc, argv, builtin_commit_options,
+                                         builtin_commit_usage,
                                          prefix, current_head, &s);
        if (dry_run)
                return dry_run_commit(argc, argv, prefix, current_head, &s);
@@ -1494,6 +1527,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
+       if (template_untouched(&sb) && !allow_empty_message) {
+               rollback_index_files();
+               fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
+               exit(1);
+       }
        if (message_is_empty(&sb) && !allow_empty_message) {
                rollback_index_files();
                fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
index 424c815f9bc2ca8f87eb4694d1375b949b635170..9069dc41be33a362ff04d52c00b4830eed272826 100644 (file)
@@ -327,7 +327,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                                add_head_to_pending(&rev);
                                if (!rev.pending.nr) {
                                        struct tree *tree;
-                                       tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN);
+                                       tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
                                        add_pending_object(&rev, &tree->object, "HEAD");
                                }
                                break;
index 7124c4b49cfba7985c5ba2046296f006de04e3bb..10db15b18403f01b9ee5db27b37d1e0cdbb2abb1 100644 (file)
@@ -23,7 +23,9 @@ static struct fetch_pack_args args = {
 };
 
 static const char fetch_pack_usage[] =
-"git fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
+"git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
+"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
+"[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
 
 #define COMPLETE       (1U << 0)
 #define COMMON         (1U << 1)
@@ -942,6 +944,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                                args.fetch_all = 1;
                                continue;
                        }
+                       if (!strcmp("--stdin", arg)) {
+                               args.stdin_refs = 1;
+                               continue;
+                       }
                        if (!strcmp("-v", arg)) {
                                args.verbose = 1;
                                continue;
@@ -973,6 +979,40 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        if (!dest)
                usage(fetch_pack_usage);
 
+       if (args.stdin_refs) {
+               /*
+                * Copy refs from cmdline to new growable list, then
+                * append the refs from the standard input.
+                */
+               int alloc_heads = nr_heads;
+               int size = nr_heads * sizeof(*heads);
+               heads = memcpy(xmalloc(size), heads, size);
+               if (args.stateless_rpc) {
+                       /* in stateless RPC mode we use pkt-line to read
+                        * from stdin, until we get a flush packet
+                        */
+                       static char line[1000];
+                       for (;;) {
+                               int n = packet_read_line(0, line, sizeof(line));
+                               if (!n)
+                                       break;
+                               if (line[n-1] == '\n')
+                                       n--;
+                               ALLOC_GROW(heads, nr_heads + 1, alloc_heads);
+                               heads[nr_heads++] = xmemdupz(line, n);
+                       }
+               }
+               else {
+                       /* read from stdin one ref per line, until EOF */
+                       struct strbuf line = STRBUF_INIT;
+                       while (strbuf_getline(&line, stdin, '\n') != EOF) {
+                               ALLOC_GROW(heads, nr_heads + 1, alloc_heads);
+                               heads[nr_heads++] = strbuf_detach(&line, NULL);
+                       }
+                       strbuf_release(&line);
+               }
+       }
+
        if (args.stateless_rpc) {
                conn = NULL;
                fd[0] = 0;
index 65f5f9b72f92ec64ac5ad1ad264f78199337aba6..bb9a0743ff565f3002eb0fede8e35b7f01a73b75 100644 (file)
@@ -240,6 +240,7 @@ static int s_update_ref(const char *action,
 
 static int update_local_ref(struct ref *ref,
                            const char *remote,
+                           const struct ref *remote_ref,
                            struct strbuf *display)
 {
        struct commit *current = NULL, *updated;
@@ -293,18 +294,26 @@ static int update_local_ref(struct ref *ref,
                const char *msg;
                const char *what;
                int r;
-               if (!strncmp(ref->name, "refs/tags/", 10)) {
+               /*
+                * Nicely describe the new ref we're fetching.
+                * Base this on the remote's ref name, as it's
+                * more likely to follow a standard layout.
+                */
+               const char *name = remote_ref ? remote_ref->name : "";
+               if (!prefixcmp(name, "refs/tags/")) {
                        msg = "storing tag";
                        what = _("[new tag]");
-               }
-               else {
+               } else if (!prefixcmp(name, "refs/heads/")) {
                        msg = "storing head";
                        what = _("[new branch]");
-                       if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
-                           (recurse_submodules != RECURSE_SUBMODULES_ON))
-                               check_for_new_submodule_commits(ref->new_sha1);
+               } else {
+                       msg = "storing ref";
+                       what = _("[new ref]");
                }
 
+               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+                   (recurse_submodules != RECURSE_SUBMODULES_ON))
+                       check_for_new_submodule_commits(ref->new_sha1);
                r = s_update_ref(msg, ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
                            r ? '!' : '*',
@@ -466,7 +475,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 
                        strbuf_reset(&note);
                        if (ref) {
-                               rc |= update_local_ref(ref, what, &note);
+                               rc |= update_local_ref(ref, what, rm, &note);
                                free(ref);
                        } else
                                strbuf_addf(&note, "* %-*s %-*s -> FETCH_HEAD",
@@ -537,8 +546,8 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
        int result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
        const char *dangling_msg = dry_run
-               ? _("   (%s will become dangling)\n")
-               : _("   (%s has become dangling)\n");
+               ? _("   (%s will become dangling)")
+               : _("   (%s has become dangling)");
 
        for (ref = stale_refs; ref; ref = ref->next) {
                if (!dry_run)
index c81a7fef2680620d521e118d60e8c59893d59234..d42015d8672d7a929758f69371eacbbc59c92888 100644 (file)
@@ -27,6 +27,8 @@ int fmt_merge_msg_config(const char *key, const char *value, void *cb)
                        merge_log_config = DEFAULT_MERGE_LOG_LEN;
        } else if (!strcmp(key, "merge.branchdesc")) {
                use_branch_desc = git_config_bool(key, value);
+       } else {
+               return git_default_config(key, value, cb);
        }
        return 0;
 }
@@ -53,7 +55,48 @@ static void init_src_data(struct src_data *data)
 static struct string_list srcs = STRING_LIST_INIT_DUP;
 static struct string_list origins = STRING_LIST_INIT_DUP;
 
-static int handle_line(char *line)
+struct merge_parents {
+       int alloc, nr;
+       struct merge_parent {
+               unsigned char given[20];
+               unsigned char commit[20];
+               unsigned char used;
+       } *item;
+};
+
+/*
+ * I know, I know, this is inefficient, but you won't be pulling and merging
+ * hundreds of heads at a time anyway.
+ */
+static struct merge_parent *find_merge_parent(struct merge_parents *table,
+                                             unsigned char *given,
+                                             unsigned char *commit)
+{
+       int i;
+       for (i = 0; i < table->nr; i++) {
+               if (given && hashcmp(table->item[i].given, given))
+                       continue;
+               if (commit && hashcmp(table->item[i].commit, commit))
+                       continue;
+               return &table->item[i];
+       }
+       return NULL;
+}
+
+static void add_merge_parent(struct merge_parents *table,
+                            unsigned char *given,
+                            unsigned char *commit)
+{
+       if (table->nr && find_merge_parent(table, given, commit))
+               return;
+       ALLOC_GROW(table->item, table->nr + 1, table->alloc);
+       hashcpy(table->item[table->nr].given, given);
+       hashcpy(table->item[table->nr].commit, commit);
+       table->item[table->nr].used = 0;
+       table->nr++;
+}
+
+static int handle_line(char *line, struct merge_parents *merge_parents)
 {
        int i, len = strlen(line);
        struct origin_data *origin_data;
@@ -61,6 +104,7 @@ static int handle_line(char *line)
        struct src_data *src_data;
        struct string_list_item *item;
        int pulling_head = 0;
+       unsigned char sha1[20];
 
        if (len < 43 || line[40] != '\t')
                return 1;
@@ -71,14 +115,15 @@ static int handle_line(char *line)
        if (line[41] != '\t')
                return 2;
 
-       line[40] = 0;
-       origin_data = xcalloc(1, sizeof(struct origin_data));
-       i = get_sha1(line, origin_data->sha1);
-       line[40] = '\t';
-       if (i) {
-               free(origin_data);
+       i = get_sha1_hex(line, sha1);
+       if (i)
                return 3;
-       }
+
+       if (!find_merge_parent(merge_parents, sha1, NULL))
+               return 0; /* subsumed by other parents */
+
+       origin_data = xcalloc(1, sizeof(struct origin_data));
+       hashcpy(origin_data->sha1, sha1);
 
        if (line[len - 1] == '\n')
                line[len - 1] = 0;
@@ -180,6 +225,101 @@ static void add_branch_desc(struct strbuf *out, const char *name)
        strbuf_release(&desc);
 }
 
+#define util_as_integral(elem) ((intptr_t)((elem)->util))
+
+static void record_person(int which, struct string_list *people,
+                         struct commit *commit)
+{
+       char name_buf[MAX_GITNAME], *name, *name_end;
+       struct string_list_item *elem;
+       const char *field = (which == 'a') ? "\nauthor " : "\ncommitter ";
+
+       name = strstr(commit->buffer, field);
+       if (!name)
+               return;
+       name += strlen(field);
+       name_end = strchrnul(name, '<');
+       if (*name_end)
+               name_end--;
+       while (isspace(*name_end) && name <= name_end)
+               name_end--;
+       if (name_end < name || name + MAX_GITNAME <= name_end)
+               return;
+       memcpy(name_buf, name, name_end - name + 1);
+       name_buf[name_end - name + 1] = '\0';
+
+       elem = string_list_lookup(people, name_buf);
+       if (!elem) {
+               elem = string_list_insert(people, name_buf);
+               elem->util = (void *)0;
+       }
+       elem->util = (void*)(util_as_integral(elem) + 1);
+}
+
+static int cmp_string_list_util_as_integral(const void *a_, const void *b_)
+{
+       const struct string_list_item *a = a_, *b = b_;
+       return util_as_integral(b) - util_as_integral(a);
+}
+
+static void add_people_count(struct strbuf *out, struct string_list *people)
+{
+       if (people->nr == 1)
+               strbuf_addf(out, "%s", people->items[0].string);
+       else if (people->nr == 2)
+               strbuf_addf(out, "%s (%d) and %s (%d)",
+                           people->items[0].string,
+                           (int)util_as_integral(&people->items[0]),
+                           people->items[1].string,
+                           (int)util_as_integral(&people->items[1]));
+       else if (people->nr)
+               strbuf_addf(out, "%s (%d) and others",
+                           people->items[0].string,
+                           (int)util_as_integral(&people->items[0]));
+}
+
+static void credit_people(struct strbuf *out,
+                         struct string_list *them,
+                         int kind)
+{
+       const char *label;
+       const char *me;
+
+       if (kind == 'a') {
+               label = "\nBy ";
+               me = git_author_info(IDENT_NO_DATE);
+       } else {
+               label = "\nvia ";
+               me = git_committer_info(IDENT_NO_DATE);
+       }
+
+       if (!them->nr ||
+           (them->nr == 1 &&
+            me &&
+            (me = skip_prefix(me, them->items->string)) != NULL &&
+            skip_prefix(me, " <")))
+               return;
+       strbuf_addstr(out, label);
+       add_people_count(out, them);
+}
+
+static void add_people_info(struct strbuf *out,
+                           struct string_list *authors,
+                           struct string_list *committers)
+{
+       if (authors->nr)
+               qsort(authors->items,
+                     authors->nr, sizeof(authors->items[0]),
+                     cmp_string_list_util_as_integral);
+       if (committers->nr)
+               qsort(committers->items,
+                     committers->nr, sizeof(committers->items[0]),
+                     cmp_string_list_util_as_integral);
+
+       credit_people(out, authors, 'a');
+       credit_people(out, committers, 'c');
+}
+
 static void shortlog(const char *name,
                     struct origin_data *origin_data,
                     struct commit *head,
@@ -190,6 +330,8 @@ static void shortlog(const char *name,
        struct commit *commit;
        struct object *branch;
        struct string_list subjects = STRING_LIST_INIT_DUP;
+       struct string_list authors = STRING_LIST_INIT_DUP;
+       struct string_list committers = STRING_LIST_INIT_DUP;
        int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
        struct strbuf sb = STRBUF_INIT;
        const unsigned char *sha1 = origin_data->sha1;
@@ -199,7 +341,6 @@ static void shortlog(const char *name,
                return;
 
        setup_revisions(0, NULL, rev, NULL);
-       rev->ignore_merges = 1;
        add_pending_object(rev, branch, name);
        add_pending_object(rev, &head->object, "^HEAD");
        head->object.flags |= UNINTERESTING;
@@ -208,10 +349,15 @@ static void shortlog(const char *name,
        while ((commit = get_revision(rev)) != NULL) {
                struct pretty_print_context ctx = {0};
 
-               /* ignore merges */
-               if (commit->parents && commit->parents->next)
+               if (commit->parents && commit->parents->next) {
+                       /* do not list a merge but count committer */
+                       record_person('c', &committers, commit);
                        continue;
-
+               }
+               if (!count)
+                       /* the 'tip' committer */
+                       record_person('c', &committers, commit);
+               record_person('a', &authors, commit);
                count++;
                if (subjects.nr > limit)
                        continue;
@@ -226,6 +372,7 @@ static void shortlog(const char *name,
                        string_list_append(&subjects, strbuf_detach(&sb, NULL));
        }
 
+       add_people_info(out, &authors, &committers);
        if (count > limit)
                strbuf_addf(out, "\n* %s: (%d commits)\n", name, count);
        else
@@ -246,6 +393,8 @@ static void shortlog(const char *name,
        rev->commits = NULL;
        rev->pending.nr = 0;
 
+       string_list_clear(&authors, 0);
+       string_list_clear(&committers, 0);
        string_list_clear(&subjects, 0);
 }
 
@@ -313,7 +462,10 @@ static void fmt_tag_signature(struct strbuf *tagbuf,
                strbuf_add(tagbuf, tag_body, buf + len - tag_body);
        }
        strbuf_complete_line(tagbuf);
-       strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
+       if (sig->len) {
+               strbuf_addch(tagbuf, '\n');
+               strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
+       }
 }
 
 static void fmt_merge_msg_sigs(struct strbuf *out)
@@ -366,6 +518,67 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
        strbuf_release(&tagbuf);
 }
 
+static void find_merge_parents(struct merge_parents *result,
+                              struct strbuf *in, unsigned char *head)
+{
+       struct commit_list *parents, *next;
+       struct commit *head_commit;
+       int pos = 0, i, j;
+
+       parents = NULL;
+       while (pos < in->len) {
+               int len;
+               char *p = in->buf + pos;
+               char *newline = strchr(p, '\n');
+               unsigned char sha1[20];
+               struct commit *parent;
+               struct object *obj;
+
+               len = newline ? newline - p : strlen(p);
+               pos += len + !!newline;
+
+               if (len < 43 ||
+                   get_sha1_hex(p, sha1) ||
+                   p[40] != '\t' ||
+                   p[41] != '\t')
+                       continue; /* skip not-for-merge */
+               /*
+                * Do not use get_merge_parent() here; we do not have
+                * "name" here and we do not want to contaminate its
+                * util field yet.
+                */
+               obj = parse_object(sha1);
+               parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
+               if (!parent)
+                       continue;
+               commit_list_insert(parent, &parents);
+               add_merge_parent(result, obj->sha1, parent->object.sha1);
+       }
+       head_commit = lookup_commit(head);
+       if (head_commit)
+               commit_list_insert(head_commit, &parents);
+       parents = reduce_heads(parents);
+
+       while (parents) {
+               for (i = 0; i < result->nr; i++)
+                       if (!hashcmp(result->item[i].commit,
+                                    parents->item->object.sha1))
+                               result->item[i].used = 1;
+               next = parents->next;
+               free(parents);
+               parents = next;
+       }
+
+       for (i = j = 0; i < result->nr; i++) {
+               if (result->item[i].used) {
+                       if (i != j)
+                               result->item[j] = result->item[i];
+                       j++;
+               }
+       }
+       result->nr = j;
+}
+
 int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                  struct fmt_merge_msg_opts *opts)
 {
@@ -373,6 +586,9 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
        unsigned char head_sha1[20];
        const char *current_branch;
        void *current_branch_to_free;
+       struct merge_parents merge_parents;
+
+       memset(&merge_parents, 0, sizeof(merge_parents));
 
        /* get current branch */
        current_branch = current_branch_to_free =
@@ -382,6 +598,8 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
        if (!prefixcmp(current_branch, "refs/heads/"))
                current_branch += 11;
 
+       find_merge_parents(&merge_parents, in, head_sha1);
+
        /* get a line */
        while (pos < in->len) {
                int len;
@@ -392,7 +610,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                pos += len + !!newline;
                i++;
                p[len] = 0;
-               if (handle_line(p))
+               if (handle_line(p, &merge_parents))
                        die ("Error in line %d: %.*s", i, len, p);
        }
 
@@ -412,8 +630,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                rev.ignore_merges = 1;
                rev.limited = 1;
 
-               if (suffixcmp(out->buf, "\n"))
-                       strbuf_addch(out, '\n');
+               strbuf_complete_line(out);
 
                for (i = 0; i < origins.nr; i++)
                        shortlog(origins.items[i].string,
@@ -423,6 +640,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 
        strbuf_complete_line(out);
        free(current_branch_to_free);
+       free(merge_parents.item);
        return 0;
 }
 
index 67eb553c7dc3d8ce62fbbefbe64a90c6431963c7..a710227a64a9862c0a70f3022f901fc65b0c7f90 100644 (file)
@@ -12,6 +12,7 @@
 #include "parse-options.h"
 #include "dir.h"
 #include "progress.h"
+#include "streaming.h"
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
@@ -238,13 +239,8 @@ static void check_unreachable_object(struct object *obj)
                        if (!(f = fopen(filename, "w")))
                                die_errno("Could not open '%s'", filename);
                        if (obj->type == OBJ_BLOB) {
-                               enum object_type type;
-                               unsigned long size;
-                               char *buf = read_sha1_file(obj->sha1,
-                                               &type, &size);
-                               if (buf && fwrite(buf, 1, size, f) != size)
+                               if (stream_blob_to_fd(fileno(f), obj->sha1, NULL, 1))
                                        die_errno("Could not write '%s'", filename);
-                               free(buf);
                        } else
                                fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
                        if (fclose(f))
index 271376d82b4391318fda9d5cfd5a0764d3768117..9b4232c8f30715327342c3d86c027d04c2a44c94 100644 (file)
@@ -14,6 +14,7 @@
 #include "cache.h"
 #include "parse-options.h"
 #include "run-command.h"
+#include "argv-array.h"
 
 #define FAILED_RUN "failed to run %s"
 
@@ -28,12 +29,11 @@ static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
 static const char *prune_expire = "2.weeks.ago";
 
-#define MAX_ADD 10
-static const char *argv_pack_refs[] = {"pack-refs", "--all", "--prune", NULL};
-static const char *argv_reflog[] = {"reflog", "expire", "--all", NULL};
-static const char *argv_repack[MAX_ADD] = {"repack", "-d", "-l", NULL};
-static const char *argv_prune[] = {"prune", "--expire", NULL, NULL, NULL};
-static const char *argv_rerere[] = {"rerere", "gc", NULL};
+static struct argv_array pack_refs_cmd = ARGV_ARRAY_INIT;
+static struct argv_array reflog = ARGV_ARRAY_INIT;
+static struct argv_array repack = ARGV_ARRAY_INIT;
+static struct argv_array prune = ARGV_ARRAY_INIT;
+static struct argv_array rerere = ARGV_ARRAY_INIT;
 
 static int gc_config(const char *var, const char *value, void *cb)
 {
@@ -67,19 +67,6 @@ static int gc_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
-static void append_option(const char **cmd, const char *opt, int max_length)
-{
-       int i;
-
-       for (i = 0; cmd[i]; i++)
-               ;
-
-       if (i + 2 >= max_length)
-               die(_("Too many options specified"));
-       cmd[i++] = opt;
-       cmd[i] = NULL;
-}
-
 static int too_many_loose_objects(void)
 {
        /*
@@ -144,6 +131,17 @@ static int too_many_packs(void)
        return gc_auto_pack_limit <= cnt;
 }
 
+static void add_repack_all_option(void)
+{
+       if (prune_expire && !strcmp(prune_expire, "now"))
+               argv_array_push(&repack, "-a");
+       else {
+               argv_array_push(&repack, "-A");
+               if (prune_expire)
+                       argv_array_pushf(&repack, "--unpack-unreachable=%s", prune_expire);
+       }
+}
+
 static int need_to_gc(void)
 {
        /*
@@ -160,10 +158,7 @@ static int need_to_gc(void)
         * there is no need.
         */
        if (too_many_packs())
-               append_option(argv_repack,
-                             prune_expire && !strcmp(prune_expire, "now") ?
-                             "-a" : "-A",
-                             MAX_ADD);
+               add_repack_all_option();
        else if (!too_many_loose_objects())
                return 0;
 
@@ -177,7 +172,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        int aggressive = 0;
        int auto_gc = 0;
        int quiet = 0;
-       char buf[80];
 
        struct option builtin_gc_options[] = {
                OPT__QUIET(&quiet, "suppress progress reporting"),
@@ -192,6 +186,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_gc_usage, builtin_gc_options);
 
+       argv_array_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
+       argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
+       argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
+       argv_array_pushl(&prune, "prune", "--expire", NULL );
+       argv_array_pushl(&rerere, "rerere", "gc", NULL);
+
        git_config(gc_config, NULL);
 
        if (pack_refs < 0)
@@ -203,15 +203,13 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                usage_with_options(builtin_gc_usage, builtin_gc_options);
 
        if (aggressive) {
-               append_option(argv_repack, "-f", MAX_ADD);
-               append_option(argv_repack, "--depth=250", MAX_ADD);
-               if (aggressive_window > 0) {
-                       sprintf(buf, "--window=%d", aggressive_window);
-                       append_option(argv_repack, buf, MAX_ADD);
-               }
+               argv_array_push(&repack, "-f");
+               argv_array_push(&repack, "--depth=250");
+               if (aggressive_window > 0)
+                       argv_array_pushf(&repack, "--window=%d", aggressive_window);
        }
        if (quiet)
-               append_option(argv_repack, "-q", MAX_ADD);
+               argv_array_push(&repack, "-q");
 
        if (auto_gc) {
                /*
@@ -227,30 +225,27 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                                        "run \"git gc\" manually. See "
                                        "\"git help gc\" for more information.\n"));
        } else
-               append_option(argv_repack,
-                             prune_expire && !strcmp(prune_expire, "now")
-                             ? "-a" : "-A",
-                             MAX_ADD);
+               add_repack_all_option();
 
-       if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
-               return error(FAILED_RUN, argv_pack_refs[0]);
+       if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
+               return error(FAILED_RUN, pack_refs_cmd.argv[0]);
 
-       if (run_command_v_opt(argv_reflog, RUN_GIT_CMD))
-               return error(FAILED_RUN, argv_reflog[0]);
+       if (run_command_v_opt(reflog.argv, RUN_GIT_CMD))
+               return error(FAILED_RUN, reflog.argv[0]);
 
-       if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
-               return error(FAILED_RUN, argv_repack[0]);
+       if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
+               return error(FAILED_RUN, repack.argv[0]);
 
        if (prune_expire) {
-               argv_prune[2] = prune_expire;
+               argv_array_push(&prune, prune_expire);
                if (quiet)
-                       argv_prune[3] = "--no-progress";
-               if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
-                       return error(FAILED_RUN, argv_prune[0]);
+                       argv_array_push(&prune, "--no-progress");
+               if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
+                       return error(FAILED_RUN, prune.argv[0]);
        }
 
-       if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
-               return error(FAILED_RUN, argv_rerere[0]);
+       if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
+               return error(FAILED_RUN, rerere.argv[0]);
 
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
index 643938d905fb134a1e65e3684e8e5feb06b01a59..fe1726f5ef60d054938eaa849f2ae6020a95f805 100644 (file)
@@ -600,15 +600,12 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
        if (!patterns)
                die_errno(_("cannot open '%s'"), arg);
        while (strbuf_getline(&sb, patterns, '\n') == 0) {
-               char *s;
-               size_t len;
-
                /* ignore empty line like grep does */
                if (sb.len == 0)
                        continue;
 
-               s = strbuf_detach(&sb, &len);
-               append_grep_pat(grep_opt, s, len, arg, ++lno, GREP_PATTERN);
+               append_grep_pat(grep_opt, sb.buf, sb.len, arg, ++lno,
+                               GREP_PATTERN);
        }
        if (!from_stdin)
                fclose(patterns);
index 61ff79839bc4778451425d0ae1cfd199247954a1..43d3c84449a57ec7028acb663d4ab4bea134c97e 100644 (file)
@@ -9,6 +9,7 @@
 #include "common-cmds.h"
 #include "parse-options.h"
 #include "run-command.h"
+#include "column.h"
 #include "help.h"
 
 static struct man_viewer_list {
@@ -30,6 +31,7 @@ enum help_format {
 };
 
 static int show_all = 0;
+static unsigned int colopts;
 static enum help_format help_format = HELP_FORMAT_NONE;
 static struct option builtin_help_options[] = {
        OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
@@ -54,7 +56,7 @@ static enum help_format parse_help_format(const char *format)
                return HELP_FORMAT_INFO;
        if (!strcmp(format, "web") || !strcmp(format, "html"))
                return HELP_FORMAT_WEB;
-       die("unrecognized help format '%s'", format);
+       die(_("unrecognized help format '%s'"), format);
 }
 
 static const char *get_man_viewer_info(const char *name)
@@ -82,7 +84,7 @@ static int check_emacsclient_version(void)
        ec_process.err = -1;
        ec_process.stdout_to_stderr = 1;
        if (start_command(&ec_process))
-               return error("Failed to start emacsclient.");
+               return error(_("Failed to start emacsclient."));
 
        strbuf_read(&buffer, ec_process.err, 20);
        close(ec_process.err);
@@ -95,7 +97,7 @@ static int check_emacsclient_version(void)
 
        if (prefixcmp(buffer.buf, "emacsclient")) {
                strbuf_release(&buffer);
-               return error("Failed to parse emacsclient version.");
+               return error(_("Failed to parse emacsclient version."));
        }
 
        strbuf_remove(&buffer, 0, strlen("emacsclient"));
@@ -103,7 +105,7 @@ static int check_emacsclient_version(void)
 
        if (version < 22) {
                strbuf_release(&buffer);
-               return error("emacsclient version '%d' too old (< 22).",
+               return error(_("emacsclient version '%d' too old (< 22)."),
                        version);
        }
 
@@ -121,7 +123,7 @@ static void exec_woman_emacs(const char *path, const char *page)
                        path = "emacsclient";
                strbuf_addf(&man_page, "(woman \"%s\")", page);
                execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
-               warning("failed to exec '%s': %s", path, strerror(errno));
+               warning(_("failed to exec '%s': %s"), path, strerror(errno));
        }
 }
 
@@ -149,7 +151,7 @@ static void exec_man_konqueror(const char *path, const char *page)
                        path = "kfmclient";
                strbuf_addf(&man_page, "man:%s(1)", page);
                execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
-               warning("failed to exec '%s': %s", path, strerror(errno));
+               warning(_("failed to exec '%s': %s"), path, strerror(errno));
        }
 }
 
@@ -158,7 +160,7 @@ static void exec_man_man(const char *path, const char *page)
        if (!path)
                path = "man";
        execlp(path, "man", page, (char *)NULL);
-       warning("failed to exec '%s': %s", path, strerror(errno));
+       warning(_("failed to exec '%s': %s"), path, strerror(errno));
 }
 
 static void exec_man_cmd(const char *cmd, const char *page)
@@ -166,7 +168,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
        struct strbuf shell_cmd = STRBUF_INIT;
        strbuf_addf(&shell_cmd, "%s %s", cmd, page);
        execl("/bin/sh", "sh", "-c", shell_cmd.buf, (char *)NULL);
-       warning("failed to exec '%s': %s", cmd, strerror(errno));
+       warning(_("failed to exec '%s': %s"), cmd, strerror(errno));
 }
 
 static void add_man_viewer(const char *name)
@@ -206,8 +208,8 @@ static int add_man_viewer_path(const char *name,
        if (supported_man_viewer(name, len))
                do_add_man_viewer_info(name, len, value);
        else
-               warning("'%s': path for unsupported man viewer.\n"
-                       "Please consider using 'man.<tool>.cmd' instead.",
+               warning(_("'%s': path for unsupported man viewer.\n"
+                         "Please consider using 'man.<tool>.cmd' instead."),
                        name);
 
        return 0;
@@ -218,8 +220,8 @@ static int add_man_viewer_cmd(const char *name,
                              const char *value)
 {
        if (supported_man_viewer(name, len))
-               warning("'%s': cmd for supported man viewer.\n"
-                       "Please consider using 'man.<tool>.path' instead.",
+               warning(_("'%s': cmd for supported man viewer.\n"
+                         "Please consider using 'man.<tool>.path' instead."),
                        name);
        else
                do_add_man_viewer_info(name, len, value);
@@ -251,6 +253,8 @@ static int add_man_viewer_info(const char *var, const char *value)
 
 static int git_help_config(const char *var, const char *value, void *cb)
 {
+       if (!prefixcmp(var, "column."))
+               return git_column_config(var, value, "help", &colopts);
        if (!strcmp(var, "help.format")) {
                if (!value)
                        return config_error_nonbool(var);
@@ -280,11 +284,11 @@ void list_common_cmds_help(void)
                        longest = strlen(common_cmds[i].name);
        }
 
-       puts("The most commonly used git commands are:");
+       puts(_("The most commonly used git commands are:"));
        for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
                printf("   %s   ", common_cmds[i].name);
                mput_char(' ', longest - strlen(common_cmds[i].name));
-               puts(common_cmds[i].help);
+               puts(_(common_cmds[i].help));
        }
 }
 
@@ -348,7 +352,7 @@ static void exec_viewer(const char *name, const char *page)
        else if (info)
                exec_man_cmd(info, page);
        else
-               warning("'%s': unknown man viewer.", name);
+               warning(_("'%s': unknown man viewer."), name);
 }
 
 static void show_man_page(const char *git_cmd)
@@ -365,7 +369,7 @@ static void show_man_page(const char *git_cmd)
        if (fallback)
                exec_viewer(fallback, page);
        exec_viewer("man", page);
-       die("no man viewer handled the request");
+       die(_("no man viewer handled the request"));
 }
 
 static void show_info_page(const char *git_cmd)
@@ -373,7 +377,7 @@ static void show_info_page(const char *git_cmd)
        const char *page = cmd_to_page(git_cmd);
        setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
        execlp("info", "info", "gitman", page, (char *)NULL);
-       die("no info viewer handled the request");
+       die(_("no info viewer handled the request"));
 }
 
 static void get_html_page_path(struct strbuf *page_path, const char *page)
@@ -384,7 +388,7 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
        /* Check that we have a git documentation directory. */
        if (stat(mkpath("%s/git.html", html_path), &st)
            || !S_ISREG(st.st_mode))
-               die("'%s': not a documentation directory.", html_path);
+               die(_("'%s': not a documentation directory."), html_path);
 
        strbuf_init(page_path, 0);
        strbuf_addf(page_path, "%s/%s.html", html_path, page);
@@ -424,16 +428,17 @@ int cmd_help(int argc, const char **argv, const char *prefix)
        parsed_help_format = help_format;
 
        if (show_all) {
-               printf("usage: %s\n\n", git_usage_string);
-               list_commands("git commands", &main_cmds, &other_cmds);
-               printf("%s\n", git_more_info_string);
+               git_config(git_help_config, NULL);
+               printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
+               list_commands(colopts, &main_cmds, &other_cmds);
+               printf("%s\n", _(git_more_info_string));
                return 0;
        }
 
        if (!argv[0]) {
-               printf("usage: %s\n\n", git_usage_string);
+               printf(_("usage: %s%s"), _(git_usage_string), "\n\n");
                list_common_cmds_help();
-               printf("\n%s\n", git_more_info_string);
+               printf("\n%s\n", _(git_more_info_string));
                return 0;
        }
 
@@ -445,7 +450,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
 
        alias = alias_lookup(argv[0]);
        if (alias && !is_git_command(argv[0])) {
-               printf("`git %s' is aliased to `%s'\n", argv[0], alias);
+               printf_ln(_("`git %s' is aliased to `%s'"), argv[0], alias);
                return 0;
        }
 
index dd1c5c961db087d1a9f649463d41c0ccbd7a5199..dc2cfe6e6f63b6628f8a358f2f3b3e65c14e3b8d 100644 (file)
@@ -9,6 +9,7 @@
 #include "progress.h"
 #include "fsck.h"
 #include "exec_cmd.h"
+#include "thread-utils.h"
 
 static const char index_pack_usage[] =
 "git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -38,6 +39,19 @@ struct base_data {
        int ofs_first, ofs_last;
 };
 
+#if !defined(NO_PTHREADS) && defined(NO_PREAD)
+/* NO_PREAD uses compat/pread.c, which is not thread-safe. Disable threading. */
+#define NO_PTHREADS
+#endif
+
+struct thread_local {
+#ifndef NO_PTHREADS
+       pthread_t thread;
+#endif
+       struct base_data *base_cache;
+       size_t base_cache_used;
+};
+
 /*
  * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
  * to memcmp() only the first 20 bytes.
@@ -54,11 +68,11 @@ struct delta_entry {
 
 static struct object_entry *objects;
 static struct delta_entry *deltas;
-static struct base_data *base_cache;
-static size_t base_cache_used;
+static struct thread_local nothread_data;
 static int nr_objects;
 static int nr_deltas;
 static int nr_resolved_deltas;
+static int nr_threads;
 
 static int from_stdin;
 static int strict;
@@ -75,13 +89,84 @@ static git_SHA_CTX input_ctx;
 static uint32_t input_crc32;
 static int input_fd, output_fd, pack_fd;
 
+#ifndef NO_PTHREADS
+
+static struct thread_local *thread_data;
+static int nr_dispatched;
+static int threads_active;
+
+static pthread_mutex_t read_mutex;
+#define read_lock()            lock_mutex(&read_mutex)
+#define read_unlock()          unlock_mutex(&read_mutex)
+
+static pthread_mutex_t counter_mutex;
+#define counter_lock()         lock_mutex(&counter_mutex)
+#define counter_unlock()       unlock_mutex(&counter_mutex)
+
+static pthread_mutex_t work_mutex;
+#define work_lock()            lock_mutex(&work_mutex)
+#define work_unlock()          unlock_mutex(&work_mutex)
+
+static pthread_key_t key;
+
+static inline void lock_mutex(pthread_mutex_t *mutex)
+{
+       if (threads_active)
+               pthread_mutex_lock(mutex);
+}
+
+static inline void unlock_mutex(pthread_mutex_t *mutex)
+{
+       if (threads_active)
+               pthread_mutex_unlock(mutex);
+}
+
+/*
+ * Mutex and conditional variable can't be statically-initialized on Windows.
+ */
+static void init_thread(void)
+{
+       init_recursive_mutex(&read_mutex);
+       pthread_mutex_init(&counter_mutex, NULL);
+       pthread_mutex_init(&work_mutex, NULL);
+       pthread_key_create(&key, NULL);
+       thread_data = xcalloc(nr_threads, sizeof(*thread_data));
+       threads_active = 1;
+}
+
+static void cleanup_thread(void)
+{
+       if (!threads_active)
+               return;
+       threads_active = 0;
+       pthread_mutex_destroy(&read_mutex);
+       pthread_mutex_destroy(&counter_mutex);
+       pthread_mutex_destroy(&work_mutex);
+       pthread_key_delete(key);
+       free(thread_data);
+}
+
+#else
+
+#define read_lock()
+#define read_unlock()
+
+#define counter_lock()
+#define counter_unlock()
+
+#define work_lock()
+#define work_unlock()
+
+#endif
+
+
 static int mark_link(struct object *obj, int type, void *data)
 {
        if (!obj)
                return -1;
 
        if (type != OBJ_ANY && obj->type != type)
-               die("object type mismatch at %s", sha1_to_hex(obj->sha1));
+               die(_("object type mismatch at %s"), sha1_to_hex(obj->sha1));
 
        obj->flags |= FLAG_LINK;
        return 0;
@@ -101,7 +186,7 @@ static void check_object(struct object *obj)
                unsigned long size;
                int type = sha1_object_info(obj->sha1, &size);
                if (type != obj->type || type <= 0)
-                       die("object of unexpected type");
+                       die(_("object of unexpected type"));
                obj->flags |= FLAG_CHECKED;
                return;
        }
@@ -138,15 +223,18 @@ static void *fill(int min)
        if (min <= input_len)
                return input_buffer + input_offset;
        if (min > sizeof(input_buffer))
-               die("cannot fill %d bytes", min);
+               die(Q_("cannot fill %d byte",
+                      "cannot fill %d bytes",
+                      min),
+                   min);
        flush();
        do {
                ssize_t ret = xread(input_fd, input_buffer + input_len,
                                sizeof(input_buffer) - input_len);
                if (ret <= 0) {
                        if (!ret)
-                               die("early EOF");
-                       die_errno("read error on input");
+                               die(_("early EOF"));
+                       die_errno(_("read error on input"));
                }
                input_len += ret;
                if (from_stdin)
@@ -158,14 +246,14 @@ static void *fill(int min)
 static void use(int bytes)
 {
        if (bytes > input_len)
-               die("used more bytes than were available");
+               die(_("used more bytes than were available"));
        input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes);
        input_len -= bytes;
        input_offset += bytes;
 
        /* make sure off_t is sufficiently large not to wrap */
        if (signed_add_overflows(consumed_bytes, bytes))
-               die("pack too large for current definition of off_t");
+               die(_("pack too large for current definition of off_t"));
        consumed_bytes += bytes;
 }
 
@@ -181,12 +269,12 @@ static const char *open_pack_file(const char *pack_name)
                } else
                        output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
                if (output_fd < 0)
-                       die_errno("unable to create '%s'", pack_name);
+                       die_errno(_("unable to create '%s'"), pack_name);
                pack_fd = output_fd;
        } else {
                input_fd = open(pack_name, O_RDONLY);
                if (input_fd < 0)
-                       die_errno("cannot open packfile '%s'", pack_name);
+                       die_errno(_("cannot open packfile '%s'"), pack_name);
                output_fd = -1;
                pack_fd = input_fd;
        }
@@ -200,7 +288,7 @@ static void parse_pack_header(void)
 
        /* Header consistency check */
        if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
-               die("pack signature mismatch");
+               die(_("pack signature mismatch"));
        if (!pack_version_ok(hdr->hdr_version))
                die("pack version %"PRIu32" unsupported",
                        ntohl(hdr->hdr_version));
@@ -220,8 +308,27 @@ static NORETURN void bad_object(unsigned long offset, const char *format, ...)
        va_start(params, format);
        vsnprintf(buf, sizeof(buf), format, params);
        va_end(params);
-       die("pack has bad object at offset %lu: %s", offset, buf);
+       die(_("pack has bad object at offset %lu: %s"), offset, buf);
+}
+
+static inline struct thread_local *get_thread_data(void)
+{
+#ifndef NO_PTHREADS
+       if (threads_active)
+               return pthread_getspecific(key);
+       assert(!threads_active &&
+              "This should only be reached when all threads are gone");
+#endif
+       return &nothread_data;
+}
+
+#ifndef NO_PTHREADS
+static void set_thread_data(struct thread_local *data)
+{
+       if (threads_active)
+               pthread_setspecific(key, data);
 }
+#endif
 
 static struct base_data *alloc_base_data(void)
 {
@@ -237,15 +344,16 @@ static void free_base_data(struct base_data *c)
        if (c->data) {
                free(c->data);
                c->data = NULL;
-               base_cache_used -= c->size;
+               get_thread_data()->base_cache_used -= c->size;
        }
 }
 
 static void prune_base_data(struct base_data *retain)
 {
        struct base_data *b;
-       for (b = base_cache;
-            base_cache_used > delta_base_cache_limit && b;
+       struct thread_local *data = get_thread_data();
+       for (b = data->base_cache;
+            data->base_cache_used > delta_base_cache_limit && b;
             b = b->child) {
                if (b->data && b != retain)
                        free_base_data(b);
@@ -257,12 +365,12 @@ static void link_base_data(struct base_data *base, struct base_data *c)
        if (base)
                base->child = c;
        else
-               base_cache = c;
+               get_thread_data()->base_cache = c;
 
        c->base = base;
        c->child = NULL;
        if (c->data)
-               base_cache_used += c->size;
+               get_thread_data()->base_cache_used += c->size;
        prune_base_data(c);
 }
 
@@ -272,7 +380,7 @@ static void unlink_base_data(struct base_data *c)
        if (base)
                base->child = NULL;
        else
-               base_cache = NULL;
+               get_thread_data()->base_cache = NULL;
        free_base_data(c);
 }
 
@@ -294,7 +402,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size)
                use(input_len - stream.avail_in);
        } while (status == Z_OK);
        if (stream.total_out != size || status != Z_STREAM_END)
-               bad_object(offset, "inflate returned %d", status);
+               bad_object(offset, _("inflate returned %d"), status);
        git_inflate_end(&stream);
        return buf;
 }
@@ -339,7 +447,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
                while (c & 128) {
                        base_offset += 1;
                        if (!base_offset || MSB(base_offset, 7))
-                               bad_object(obj->idx.offset, "offset value overflow for delta base object");
+                               bad_object(obj->idx.offset, _("offset value overflow for delta base object"));
                        p = fill(1);
                        c = *p;
                        use(1);
@@ -347,7 +455,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
                }
                delta_base->offset = obj->idx.offset - base_offset;
                if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
-                       bad_object(obj->idx.offset, "delta base offset is out of bound");
+                       bad_object(obj->idx.offset, _("delta base offset is out of bound"));
                break;
        case OBJ_COMMIT:
        case OBJ_TREE:
@@ -355,7 +463,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
        case OBJ_TAG:
                break;
        default:
-               bad_object(obj->idx.offset, "unknown object type %d", obj->type);
+               bad_object(obj->idx.offset, _("unknown object type %d"), obj->type);
        }
        obj->hdr_size = consumed_bytes - obj->idx.offset;
 
@@ -384,9 +492,12 @@ static void *get_data_from_pack(struct object_entry *obj)
                ssize_t n = (len < 64*1024) ? len : 64*1024;
                n = pread(pack_fd, inbuf, n, from);
                if (n < 0)
-                       die_errno("cannot pread pack file");
+                       die_errno(_("cannot pread pack file"));
                if (!n)
-                       die("premature end of pack file, %lu bytes missing", len);
+                       die(Q_("premature end of pack file, %lu byte missing",
+                              "premature end of pack file, %lu bytes missing",
+                              len),
+                           len);
                from += n;
                len -= n;
                stream.next_in = inbuf;
@@ -396,7 +507,7 @@ static void *get_data_from_pack(struct object_entry *obj)
 
        /* This has been inflated OK when first encountered, so... */
        if (status != Z_STREAM_END || stream.total_out != obj->size)
-               die("serious inflate inconsistency");
+               die(_("serious inflate inconsistency"));
 
        git_inflate_end(&stream);
        free(inbuf);
@@ -461,25 +572,30 @@ static void sha1_object(const void *data, unsigned long size,
                        enum object_type type, unsigned char *sha1)
 {
        hash_sha1_file(data, size, typename(type), sha1);
+       read_lock();
        if (has_sha1_file(sha1)) {
                void *has_data;
                enum object_type has_type;
                unsigned long has_size;
                has_data = read_sha1_file(sha1, &has_type, &has_size);
+               read_unlock();
                if (!has_data)
-                       die("cannot read existing object %s", sha1_to_hex(sha1));
+                       die(_("cannot read existing object %s"), sha1_to_hex(sha1));
                if (size != has_size || type != has_type ||
                    memcmp(data, has_data, size) != 0)
-                       die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
+                       die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1));
                free(has_data);
-       }
+       } else
+               read_unlock();
+
        if (strict) {
+               read_lock();
                if (type == OBJ_BLOB) {
                        struct blob *blob = lookup_blob(sha1);
                        if (blob)
                                blob->object.flags |= FLAG_CHECKED;
                        else
-                               die("invalid blob object %s", sha1_to_hex(sha1));
+                               die(_("invalid blob object %s"), sha1_to_hex(sha1));
                } else {
                        struct object *obj;
                        int eaten;
@@ -491,11 +607,11 @@ static void sha1_object(const void *data, unsigned long size,
                         */
                        obj = parse_object_buffer(sha1, type, size, buf, &eaten);
                        if (!obj)
-                               die("invalid %s", typename(type));
+                               die(_("invalid %s"), typename(type));
                        if (fsck_object(obj, 1, fsck_error_function))
-                               die("Error in object");
+                               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));
+                               die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
 
                        if (obj->type == OBJ_TREE) {
                                struct tree *item = (struct tree *) obj;
@@ -507,6 +623,7 @@ static void sha1_object(const void *data, unsigned long size,
                        }
                        obj->flags |= FLAG_CHECKED;
                }
+               read_unlock();
        }
 }
 
@@ -552,7 +669,7 @@ static void *get_base_data(struct base_data *c)
                if (!delta_nr) {
                        c->data = get_data_from_pack(obj);
                        c->size = obj->size;
-                       base_cache_used += c->size;
+                       get_thread_data()->base_cache_used += c->size;
                        prune_base_data(c);
                }
                for (; delta_nr > 0; delta_nr--) {
@@ -567,8 +684,8 @@ static void *get_base_data(struct base_data *c)
                                &c->size);
                        free(raw);
                        if (!c->data)
-                               bad_object(obj->idx.offset, "failed to apply delta");
-                       base_cache_used += c->size;
+                               bad_object(obj->idx.offset, _("failed to apply delta"));
+                       get_thread_data()->base_cache_used += c->size;
                        prune_base_data(c);
                }
                free(delta);
@@ -593,10 +710,12 @@ static void resolve_delta(struct object_entry *delta_obj,
                                   delta_data, delta_obj->size, &result->size);
        free(delta_data);
        if (!result->data)
-               bad_object(delta_obj->idx.offset, "failed to apply delta");
+               bad_object(delta_obj->idx.offset, _("failed to apply delta"));
        sha1_object(result->data, result->size, delta_obj->real_type,
                    delta_obj->idx.sha1);
+       counter_lock();
        nr_resolved_deltas++;
+       counter_unlock();
 }
 
 static struct base_data *find_unresolved_deltas_1(struct base_data *base,
@@ -682,22 +801,53 @@ static int compare_delta_entry(const void *a, const void *b)
                                   objects[delta_b->obj_no].type);
 }
 
-/* Parse all objects and return the pack content SHA1 hash */
+static void resolve_base(struct object_entry *obj)
+{
+       struct base_data *base_obj = alloc_base_data();
+       base_obj->obj = obj;
+       base_obj->data = NULL;
+       find_unresolved_deltas(base_obj);
+}
+
+#ifndef NO_PTHREADS
+static void *threaded_second_pass(void *data)
+{
+       set_thread_data(data);
+       for (;;) {
+               int i;
+               work_lock();
+               display_progress(progress, nr_resolved_deltas);
+               while (nr_dispatched < nr_objects &&
+                      is_delta_type(objects[nr_dispatched].type))
+                       nr_dispatched++;
+               if (nr_dispatched >= nr_objects) {
+                       work_unlock();
+                       break;
+               }
+               i = nr_dispatched++;
+               work_unlock();
+
+               resolve_base(&objects[i]);
+       }
+       return NULL;
+}
+#endif
+
+/*
+ * First pass:
+ * - find locations of all objects;
+ * - calculate SHA1 of all non-delta objects;
+ * - remember base (SHA1 or offset) for all deltas.
+ */
 static void parse_pack_objects(unsigned char *sha1)
 {
        int i;
        struct delta_entry *delta = deltas;
        struct stat st;
 
-       /*
-        * First pass:
-        * - find locations of all objects;
-        * - calculate SHA1 of all non-delta objects;
-        * - remember base (SHA1 or offset) for all deltas.
-        */
        if (verbose)
                progress = start_progress(
-                               from_stdin ? "Receiving objects" : "Indexing objects",
+                               from_stdin ? _("Receiving objects") : _("Indexing objects"),
                                nr_objects);
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *obj = &objects[i];
@@ -719,15 +869,28 @@ static void parse_pack_objects(unsigned char *sha1)
        flush();
        git_SHA1_Final(sha1, &input_ctx);
        if (hashcmp(fill(20), sha1))
-               die("pack is corrupted (SHA1 mismatch)");
+               die(_("pack is corrupted (SHA1 mismatch)"));
        use(20);
 
        /* If input_fd is a file, we should have reached its end now. */
        if (fstat(input_fd, &st))
-               die_errno("cannot fstat packfile");
+               die_errno(_("cannot fstat packfile"));
        if (S_ISREG(st.st_mode) &&
                        lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
-               die("pack has junk at the end");
+               die(_("pack has junk at the end"));
+}
+
+/*
+ * Second pass:
+ * - for all non-delta objects, look if it is used as a base for
+ *   deltas;
+ * - if used as a base, uncompress the object and apply all deltas,
+ *   recursively checking if the resulting object is used as a base
+ *   for some more deltas.
+ */
+static void resolve_deltas(void)
+{
+       int i;
 
        if (!nr_deltas)
                return;
@@ -736,29 +899,83 @@ static void parse_pack_objects(unsigned char *sha1)
        qsort(deltas, nr_deltas, sizeof(struct delta_entry),
              compare_delta_entry);
 
-       /*
-        * Second pass:
-        * - for all non-delta objects, look if it is used as a base for
-        *   deltas;
-        * - if used as a base, uncompress the object and apply all deltas,
-        *   recursively checking if the resulting object is used as a base
-        *   for some more deltas.
-        */
        if (verbose)
-               progress = start_progress("Resolving deltas", nr_deltas);
+               progress = start_progress(_("Resolving deltas"), nr_deltas);
+
+#ifndef NO_PTHREADS
+       nr_dispatched = 0;
+       if (nr_threads > 1 || getenv("GIT_FORCE_THREADS")) {
+               init_thread();
+               for (i = 0; i < nr_threads; i++) {
+                       int ret = pthread_create(&thread_data[i].thread, NULL,
+                                                threaded_second_pass, thread_data + i);
+                       if (ret)
+                               die("unable to create thread: %s", strerror(ret));
+               }
+               for (i = 0; i < nr_threads; i++)
+                       pthread_join(thread_data[i].thread, NULL);
+               cleanup_thread();
+               return;
+       }
+#endif
+
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *obj = &objects[i];
-               struct base_data *base_obj = alloc_base_data();
 
                if (is_delta_type(obj->type))
                        continue;
-               base_obj->obj = obj;
-               base_obj->data = NULL;
-               find_unresolved_deltas(base_obj);
+               resolve_base(obj);
                display_progress(progress, nr_resolved_deltas);
        }
 }
 
+/*
+ * Third pass:
+ * - append objects to convert thin pack to full pack if required
+ * - write the final 20-byte SHA-1
+ */
+static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved);
+static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned char *pack_sha1)
+{
+       if (nr_deltas == nr_resolved_deltas) {
+               stop_progress(&progress);
+               /* Flush remaining pack final 20-byte SHA1. */
+               flush();
+               return;
+       }
+
+       if (fix_thin_pack) {
+               struct sha1file *f;
+               unsigned char read_sha1[20], tail_sha1[20];
+               char msg[48];
+               int nr_unresolved = nr_deltas - nr_resolved_deltas;
+               int nr_objects_initial = nr_objects;
+               if (nr_unresolved <= 0)
+                       die(_("confusion beyond insanity"));
+               objects = xrealloc(objects,
+                                  (nr_objects + nr_unresolved + 1)
+                                  * sizeof(*objects));
+               f = sha1fd(output_fd, curr_pack);
+               fix_unresolved_deltas(f, nr_unresolved);
+               sprintf(msg, "completed with %d local objects",
+                       nr_objects - nr_objects_initial);
+               stop_progress_msg(&progress, msg);
+               sha1close(f, tail_sha1, 0);
+               hashcpy(read_sha1, pack_sha1);
+               fixup_pack_header_footer(output_fd, pack_sha1,
+                                        curr_pack, nr_objects,
+                                        read_sha1, consumed_bytes-20);
+               if (hashcmp(read_sha1, tail_sha1) != 0)
+                       die("Unexpected tail checksum for %s "
+                           "(disk corruption?)", curr_pack);
+       }
+       if (nr_deltas != nr_resolved_deltas)
+               die(Q_("pack has %d unresolved delta",
+                      "pack has %d unresolved deltas",
+                      nr_deltas - nr_resolved_deltas),
+                   nr_deltas - nr_resolved_deltas);
+}
+
 static int write_compressed(struct sha1file *f, void *in, unsigned int size)
 {
        git_zstream stream;
@@ -778,7 +995,7 @@ static int write_compressed(struct sha1file *f, void *in, unsigned int size)
        } while (status == Z_OK);
 
        if (status != Z_STREAM_END)
-               die("unable to deflate appended object (%d)", status);
+               die(_("unable to deflate appended object (%d)"), status);
        size = stream.total_out;
        git_deflate_end(&stream);
        return size;
@@ -857,7 +1074,7 @@ static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
 
                if (check_sha1_signature(d->base.sha1, base_obj->data,
                                base_obj->size, typename(type)))
-                       die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
+                       die(_("local object %s is corrupt"), sha1_to_hex(d->base.sha1));
                base_obj->obj = append_obj_to_pack(f, d->base.sha1,
                                        base_obj->data, base_obj->size, type);
                find_unresolved_deltas(base_obj);
@@ -881,7 +1098,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                fsync_or_die(output_fd, curr_pack_name);
                err = close(output_fd);
                if (err)
-                       die_errno("error while closing pack file");
+                       die_errno(_("error while closing pack file"));
        }
 
        if (keep_msg) {
@@ -894,7 +1111,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
 
                if (keep_fd < 0) {
                        if (errno != EEXIST)
-                               die_errno("cannot write keep file '%s'",
+                               die_errno(_("cannot write keep file '%s'"),
                                          keep_name);
                } else {
                        if (keep_msg_len > 0) {
@@ -902,7 +1119,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                                write_or_die(keep_fd, "\n", 1);
                        }
                        if (close(keep_fd) != 0)
-                               die_errno("cannot close written keep file '%s'",
+                               die_errno(_("cannot close written keep file '%s'"),
                                    keep_name);
                        report = "keep";
                }
@@ -915,7 +1132,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        final_pack_name = name;
                }
                if (move_temp_to_file(curr_pack_name, final_pack_name))
-                       die("cannot store pack file");
+                       die(_("cannot store pack file"));
        } else if (from_stdin)
                chmod(final_pack_name, 0444);
 
@@ -926,7 +1143,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        final_index_name = name;
                }
                if (move_temp_to_file(curr_index_name, final_index_name))
-                       die("cannot store index file");
+                       die(_("cannot store index file"));
        } else
                chmod(final_index_name, 0444);
 
@@ -962,6 +1179,18 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
                        die("bad pack.indexversion=%"PRIu32, opts->version);
                return 0;
        }
+       if (!strcmp(k, "pack.threads")) {
+               nr_threads = git_config_int(k, v);
+               if (nr_threads < 0)
+                       die("invalid number of threads specified (%d)",
+                           nr_threads);
+#ifdef NO_PTHREADS
+               if (nr_threads != 1)
+                       warning("no threads support, ignoring %s", k);
+               nr_threads = 1;
+#endif
+               return 0;
+       }
        return git_default_config(k, v, cb);
 }
 
@@ -1015,9 +1244,9 @@ static void read_idx_option(struct pack_idx_option *opts, const char *pack_name)
        struct packed_git *p = add_packed_git(pack_name, strlen(pack_name), 1);
 
        if (!p)
-               die("Cannot open existing pack file '%s'", pack_name);
+               die(_("Cannot open existing pack file '%s'"), pack_name);
        if (open_pack_index(p))
-               die("Cannot open existing pack idx file for '%s'", pack_name);
+               die(_("Cannot open existing pack idx file for '%s'"), pack_name);
 
        /* Read the attributes from the existing idx file */
        opts->version = p->index_version;
@@ -1064,15 +1293,18 @@ static void show_pack_info(int stat_only)
        }
 
        if (baseobjects)
-               printf("non delta: %d object%s\n",
-                      baseobjects, baseobjects > 1 ? "s" : "");
+               printf_ln(Q_("non delta: %d object",
+                            "non delta: %d objects",
+                            baseobjects),
+                         baseobjects);
        for (i = 0; i < deepest_delta; i++) {
                if (!chain_histogram[i])
                        continue;
-               printf("chain length = %d: %lu object%s\n",
-                      i + 1,
-                      chain_histogram[i],
-                      chain_histogram[i] > 1 ? "s" : "");
+               printf_ln(Q_("chain length = %d: %lu object",
+                            "chain length = %d: %lu objects",
+                            chain_histogram[i]),
+                         i + 1,
+                         chain_histogram[i]);
        }
 }
 
@@ -1095,7 +1327,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        reset_pack_idx_option(&opts);
        git_config(git_index_pack_config, &opts);
        if (prefix && chdir(prefix))
-               die("Cannot come back to cwd");
+               die(_("Cannot come back to cwd"));
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -1120,6 +1352,17 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                keep_msg = "";
                        } else if (!prefixcmp(arg, "--keep=")) {
                                keep_msg = arg + 7;
+                       } else if (!prefixcmp(arg, "--threads=")) {
+                               char *end;
+                               nr_threads = strtoul(arg+10, &end, 0);
+                               if (!arg[10] || *end || nr_threads < 0)
+                                       usage(index_pack_usage);
+#ifdef NO_PTHREADS
+                               if (nr_threads != 1)
+                                       warning("no threads support, "
+                                               "ignoring %s", arg);
+                               nr_threads = 1;
+#endif
                        } else if (!prefixcmp(arg, "--pack_header=")) {
                                struct pack_header *hdr;
                                char *c;
@@ -1128,10 +1371,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                hdr->hdr_signature = htonl(PACK_SIGNATURE);
                                hdr->hdr_version = htonl(strtoul(arg + 14, &c, 10));
                                if (*c != ',')
-                                       die("bad %s", arg);
+                                       die(_("bad %s"), arg);
                                hdr->hdr_entries = htonl(strtoul(c + 1, &c, 10));
                                if (*c)
-                                       die("bad %s", arg);
+                                       die(_("bad %s"), arg);
                                input_len = sizeof(*hdr);
                        } else if (!strcmp(arg, "-v")) {
                                verbose = 1;
@@ -1143,11 +1386,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                char *c;
                                opts.version = strtoul(arg + 16, &c, 10);
                                if (opts.version > 2)
-                                       die("bad %s", arg);
+                                       die(_("bad %s"), arg);
                                if (*c == ',')
                                        opts.off32_limit = strtoul(c+1, &c, 0);
                                if (*c || opts.off32_limit & 0x80000000)
-                                       die("bad %s", arg);
+                                       die(_("bad %s"), arg);
                        } else
                                usage(index_pack_usage);
                        continue;
@@ -1161,11 +1404,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (!pack_name && !from_stdin)
                usage(index_pack_usage);
        if (fix_thin_pack && !from_stdin)
-               die("--fix-thin cannot be used without --stdin");
+               die(_("--fix-thin cannot be used without --stdin"));
        if (!index_name && pack_name) {
                int len = strlen(pack_name);
                if (!has_extension(pack_name, ".pack"))
-                       die("packfile name '%s' does not end with '.pack'",
+                       die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
                index_name_buf = xmalloc(len);
                memcpy(index_name_buf, pack_name, len - 5);
@@ -1175,7 +1418,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (keep_msg && !keep_name && pack_name) {
                int len = strlen(pack_name);
                if (!has_extension(pack_name, ".pack"))
-                       die("packfile name '%s' does not end with '.pack'",
+                       die(_("packfile name '%s' does not end with '.pack'"),
                            pack_name);
                keep_name_buf = xmalloc(len);
                memcpy(keep_name_buf, pack_name, len - 5);
@@ -1184,52 +1427,29 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        }
        if (verify) {
                if (!index_name)
-                       die("--verify with no packfile name given");
+                       die(_("--verify with no packfile name given"));
                read_idx_option(&opts, index_name);
                opts.flags |= WRITE_IDX_VERIFY | WRITE_IDX_STRICT;
        }
        if (strict)
                opts.flags |= WRITE_IDX_STRICT;
 
+#ifndef NO_PTHREADS
+       if (!nr_threads) {
+               nr_threads = online_cpus();
+               /* An experiment showed that more threads does not mean faster */
+               if (nr_threads > 3)
+                       nr_threads = 3;
+       }
+#endif
+
        curr_pack = open_pack_file(pack_name);
        parse_pack_header();
        objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
        deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
        parse_pack_objects(pack_sha1);
-       if (nr_deltas == nr_resolved_deltas) {
-               stop_progress(&progress);
-               /* Flush remaining pack final 20-byte SHA1. */
-               flush();
-       } else {
-               if (fix_thin_pack) {
-                       struct sha1file *f;
-                       unsigned char read_sha1[20], tail_sha1[20];
-                       char msg[48];
-                       int nr_unresolved = nr_deltas - nr_resolved_deltas;
-                       int nr_objects_initial = nr_objects;
-                       if (nr_unresolved <= 0)
-                               die("confusion beyond insanity");
-                       objects = xrealloc(objects,
-                                          (nr_objects + nr_unresolved + 1)
-                                          * sizeof(*objects));
-                       f = sha1fd(output_fd, curr_pack);
-                       fix_unresolved_deltas(f, nr_unresolved);
-                       sprintf(msg, "completed with %d local objects",
-                               nr_objects - nr_objects_initial);
-                       stop_progress_msg(&progress, msg);
-                       sha1close(f, tail_sha1, 0);
-                       hashcpy(read_sha1, pack_sha1);
-                       fixup_pack_header_footer(output_fd, pack_sha1,
-                                                curr_pack, nr_objects,
-                                                read_sha1, consumed_bytes-20);
-                       if (hashcmp(read_sha1, tail_sha1) != 0)
-                               die("Unexpected tail checksum for %s "
-                                   "(disk corruption?)", curr_pack);
-               }
-               if (nr_deltas != nr_resolved_deltas)
-                       die("pack has %d unresolved deltas",
-                           nr_deltas - nr_resolved_deltas);
-       }
+       resolve_deltas();
+       conclude_pack(fix_thin_pack, curr_pack, pack_sha1);
        free(deltas);
        if (strict)
                check_objects();
index 8a47012b0bd2fefe616c44b918d16a18463b5d2a..690caa7830b2a4549012db5e46794118bc36e989 100644 (file)
@@ -20,6 +20,7 @@
 #include "string-list.h"
 #include "parse-options.h"
 #include "branch.h"
+#include "streaming.h"
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -383,8 +384,13 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)
        strbuf_release(&out);
 }
 
-static int show_object(const unsigned char *sha1, int show_tag_object,
-       struct rev_info *rev)
+static int show_blob_object(const unsigned char *sha1, struct rev_info *rev)
+{
+       fflush(stdout);
+       return stream_blob_to_fd(1, sha1, NULL, 0);
+}
+
+static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
 {
        unsigned long size;
        enum object_type type;
@@ -394,16 +400,16 @@ static int show_object(const unsigned char *sha1, int show_tag_object,
        if (!buf)
                return error(_("Could not read object %s"), sha1_to_hex(sha1));
 
-       if (show_tag_object)
-               while (offset < size && buf[offset] != '\n') {
-                       int new_offset = offset + 1;
-                       while (new_offset < size && buf[new_offset++] != '\n')
-                               ; /* do nothing */
-                       if (!prefixcmp(buf + offset, "tagger "))
-                               show_tagger(buf + offset + 7,
-                                           new_offset - offset - 7, rev);
-                       offset = new_offset;
-               }
+       assert(type == OBJ_TAG);
+       while (offset < size && buf[offset] != '\n') {
+               int new_offset = offset + 1;
+               while (new_offset < size && buf[new_offset++] != '\n')
+                       ; /* do nothing */
+               if (!prefixcmp(buf + offset, "tagger "))
+                       show_tagger(buf + offset + 7,
+                                   new_offset - offset - 7, rev);
+               offset = new_offset;
+       }
 
        if (offset < size)
                fwrite(buf + offset, size - offset, 1, stdout);
@@ -463,7 +469,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                const char *name = objects[i].name;
                switch (o->type) {
                case OBJ_BLOB:
-                       ret = show_object(o->sha1, 0, NULL);
+                       ret = show_blob_object(o->sha1, NULL);
                        break;
                case OBJ_TAG: {
                        struct tag *t = (struct tag *)o;
@@ -474,7 +480,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
-                       ret = show_object(o->sha1, 1, &rev);
+                       ret = show_tag_object(o->sha1, &rev);
                        rev.shown_one = 1;
                        if (ret)
                                break;
index 237abd3c0b27b601aca140bfb67a302666ff9cf2..6f0efef43c5b93c87fa8f586319cd03c8018bd55 100644 (file)
@@ -63,7 +63,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        if (quiet) {
                if (!freopen("/dev/null", "w", stderr))
                        return error("failed to redirect stderr to /dev/null: "
-                                    "%s\n", strerror(errno));
+                                    "%s", strerror(errno));
        }
 
        if (prefix)
@@ -76,7 +76,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                if (read_mmfile(mmfs + i, fname))
                        return -1;
                if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
-                       return error("Cannot merge binary files: %s\n",
+                       return error("Cannot merge binary files: %s",
                                        argv[i]);
        }
 
index 08e01e8a60d7d7f9c60e9e7a3a2f8b4d66c12ce3..470fc57c5d6ceabedbd866a5db35d3123a764af0 100644 (file)
@@ -52,7 +52,6 @@ static int fast_forward_only, option_edit = -1;
 static int allow_trivial = 1, have_message;
 static int overwrite_ignore = 1;
 static struct strbuf merge_msg = STRBUF_INIT;
-static struct commit_list *remoteheads;
 static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
 static const char **xopts;
@@ -318,7 +317,7 @@ static void finish_up_to_date(const char *msg)
        drop_save();
 }
 
-static void squash_message(struct commit *commit)
+static void squash_message(struct commit *commit, struct commit_list *remoteheads)
 {
        struct rev_info rev;
        struct strbuf out = STRBUF_INIT;
@@ -366,6 +365,7 @@ static void squash_message(struct commit *commit)
 }
 
 static void finish(struct commit *head_commit,
+                  struct commit_list *remoteheads,
                   const unsigned char *new_head, const char *msg)
 {
        struct strbuf reflog_message = STRBUF_INIT;
@@ -380,7 +380,7 @@ static void finish(struct commit *head_commit,
                        getenv("GIT_REFLOG_ACTION"), msg);
        }
        if (squash) {
-               squash_message(head_commit);
+               squash_message(head_commit, remoteheads);
        } else {
                if (verbosity >= 0 && !merge_msg.len)
                        printf(_("No merge message -- not updating HEAD\n"));
@@ -683,6 +683,7 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
 }
 
 static int try_merge_strategy(const char *strategy, struct commit_list *common,
+                             struct commit_list *remoteheads,
                              struct commit *head, const char *head_arg)
 {
        int index_fd;
@@ -876,14 +877,14 @@ static void read_merge_msg(struct strbuf *msg)
                die_errno(_("Could not read from '%s'"), filename);
 }
 
-static void write_merge_state(void);
-static void abort_commit(const char *err_msg)
+static void write_merge_state(struct commit_list *);
+static void abort_commit(struct commit_list *remoteheads, const char *err_msg)
 {
        if (err_msg)
                error("%s", err_msg);
        fprintf(stderr,
                _("Not committing merge; use 'git commit' to complete the merge.\n"));
-       write_merge_state();
+       write_merge_state(remoteheads);
        exit(1);
 }
 
@@ -894,7 +895,7 @@ N_("Please enter a commit message to explain why this merge is necessary,\n"
    "Lines starting with '#' will be ignored, and an empty message aborts\n"
    "the commit.\n");
 
-static void prepare_to_commit(void)
+static void prepare_to_commit(struct commit_list *remoteheads)
 {
        struct strbuf msg = STRBUF_INIT;
        const char *comment = _(merge_editor_comment);
@@ -907,18 +908,18 @@ static void prepare_to_commit(void)
                 git_path("MERGE_MSG"), "merge", NULL, NULL);
        if (0 < option_edit) {
                if (launch_editor(git_path("MERGE_MSG"), NULL, NULL))
-                       abort_commit(NULL);
+                       abort_commit(remoteheads, NULL);
        }
        read_merge_msg(&msg);
        stripspace(&msg, 0 < option_edit);
        if (!msg.len)
-               abort_commit(_("Empty commit message."));
+               abort_commit(remoteheads, _("Empty commit message."));
        strbuf_release(&merge_msg);
        strbuf_addbuf(&merge_msg, &msg);
        strbuf_release(&msg);
 }
 
-static int merge_trivial(struct commit *head)
+static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 {
        unsigned char result_tree[20], result_commit[20];
        struct commit_list *parent = xmalloc(sizeof(*parent));
@@ -929,45 +930,37 @@ static int merge_trivial(struct commit *head)
        parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
-       prepare_to_commit();
+       prepare_to_commit(remoteheads);
        if (commit_tree(&merge_msg, result_tree, parent, result_commit, NULL,
                        sign_commit))
                die(_("failed to write commit object"));
-       finish(head, result_commit, "In-index merge");
+       finish(head, remoteheads, result_commit, "In-index merge");
        drop_save();
        return 0;
 }
 
 static int finish_automerge(struct commit *head,
+                           int head_subsumed,
                            struct commit_list *common,
+                           struct commit_list *remoteheads,
                            unsigned char *result_tree,
                            const char *wt_strategy)
 {
-       struct commit_list *parents = NULL, *j;
+       struct commit_list *parents = NULL;
        struct strbuf buf = STRBUF_INIT;
        unsigned char result_commit[20];
 
        free_commit_list(common);
-       if (allow_fast_forward) {
-               parents = remoteheads;
+       parents = remoteheads;
+       if (!head_subsumed || !allow_fast_forward)
                commit_list_insert(head, &parents);
-               parents = reduce_heads(parents);
-       } else {
-               struct commit_list **pptr = &parents;
-
-               pptr = &commit_list_insert(head,
-                               pptr)->next;
-               for (j = remoteheads; j; j = j->next)
-                       pptr = &commit_list_insert(j->item, pptr)->next;
-       }
        strbuf_addch(&merge_msg, '\n');
-       prepare_to_commit();
-       free_commit_list(remoteheads);
+       prepare_to_commit(remoteheads);
        if (commit_tree(&merge_msg, result_tree, parents, result_commit,
                        NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
-       finish(head, result_commit, buf.buf);
+       finish(head, remoteheads, result_commit, buf.buf);
        strbuf_release(&buf);
        drop_save();
        return 0;
@@ -1072,7 +1065,7 @@ static int setup_with_upstream(const char ***argv)
        return i;
 }
 
-static void write_merge_state(void)
+static void write_merge_state(struct commit_list *remoteheads)
 {
        const char *filename;
        int fd;
@@ -1137,6 +1130,39 @@ static int default_edit_option(void)
                st_stdin.st_mode == st_stdout.st_mode);
 }
 
+static struct commit_list *collect_parents(struct commit *head_commit,
+                                          int *head_subsumed,
+                                          int argc, const char **argv)
+{
+       int i;
+       struct commit_list *remoteheads = NULL, *parents, *next;
+       struct commit_list **remotes = &remoteheads;
+
+       if (head_commit)
+               remotes = &commit_list_insert(head_commit, remotes)->next;
+       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]);
+               remotes = &commit_list_insert(commit, remotes)->next;
+       }
+       *remotes = NULL;
+
+       parents = reduce_heads(remoteheads);
+
+       *head_subsumed = 1; /* we will flip this to 0 when we find it */
+       for (remoteheads = NULL, remotes = &remoteheads;
+            parents;
+            parents = next) {
+               struct commit *commit = parents->item;
+               next = parents->next;
+               if (commit == head_commit)
+                       *head_subsumed = 0;
+               else
+                       remotes = &commit_list_insert(commit, remotes)->next;
+       }
+       return remoteheads;
+}
 
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
@@ -1146,11 +1172,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
-       int flag, i, ret = 0;
+       int flag, i, ret = 0, head_subsumed;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
        struct commit_list *common = NULL;
        const char *best_strategy = NULL, *wt_strategy = NULL;
-       struct commit_list **remotes = &remoteheads;
+       struct commit_list *remoteheads, *p;
        void *branch_to_free;
 
        if (argc == 2 && !strcmp(argv[1], "-h"))
@@ -1255,6 +1281,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                head_arg = argv[1];
                argv += 2;
                argc -= 2;
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
        } else if (!head_commit) {
                struct commit *remote_head;
                /*
@@ -1270,7 +1297,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                if (!allow_fast_forward)
                        die(_("Non-fast-forward commit does not make sense into "
                            "an empty head"));
-               remote_head = get_merge_parent(argv[0]);
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
+               remote_head = remoteheads->item;
                if (!remote_head)
                        die(_("%s - not something we can merge"), argv[0]);
                read_empty(remote_head->object.sha1, 0);
@@ -1288,8 +1316,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * the standard merge summary message to be appended
                 * to the given message.
                 */
-               for (i = 0; i < argc; i++)
-                       merge_name(argv[i], &merge_names);
+               remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
+               for (p = remoteheads; p; p = p->next)
+                       merge_name(merge_remote_util(p->item)->name, &merge_names);
 
                if (!have_message || shortlog_len) {
                        struct fmt_merge_msg_opts opts;
@@ -1308,19 +1337,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        builtin_merge_options);
 
        strbuf_addstr(&buf, "merge");
-       for (i = 0; i < argc; i++)
-               strbuf_addf(&buf, " %s", argv[i]);
+       for (p = remoteheads; p; p = p->next)
+               strbuf_addf(&buf, " %s", merge_remote_util(p->item)->name);
        setenv("GIT_REFLOG_ACTION", buf.buf, 0);
        strbuf_reset(&buf);
 
-       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]);
-               remotes = &commit_list_insert(commit, remotes)->next;
+       for (p = remoteheads; p; p = p->next) {
+               struct commit *commit = p->item;
                strbuf_addf(&buf, "GITHEAD_%s",
                            sha1_to_hex(commit->object.sha1));
-               setenv(buf.buf, argv[i], 1);
+               setenv(buf.buf, merge_remote_util(commit)->name, 1);
                strbuf_reset(&buf);
                if (!fast_forward_only &&
                    merge_remote_util(commit) &&
@@ -1333,7 +1359,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                option_edit = default_edit_option();
 
        if (!use_strategies) {
-               if (!remoteheads->next)
+               if (!remoteheads)
+                       ; /* already up-to-date */
+               else if (!remoteheads->next)
                        add_strategies(pull_twohead, DEFAULT_TWOHEAD);
                else
                        add_strategies(pull_octopus, DEFAULT_OCTOPUS);
@@ -1346,7 +1374,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        allow_trivial = 0;
        }
 
-       if (!remoteheads->next)
+       if (!remoteheads)
+               ; /* already up-to-date */
+       else if (!remoteheads->next)
                common = get_merge_bases(head_commit, remoteheads->item, 1);
        else {
                struct commit_list *list = remoteheads;
@@ -1358,10 +1388,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
                   NULL, 0, DIE_ON_ERR);
 
-       if (!common)
+       if (remoteheads && !common)
                ; /* No common ancestors found. We need a real merge. */
-       else if (!remoteheads->next && !common->next &&
-                       common->item == remoteheads->item) {
+       else if (!remoteheads ||
+                (!remoteheads->next && !common->next &&
+                 common->item == remoteheads->item)) {
                /*
                 * If head can reach all the merge then we are up to date.
                 * but first the most common case of merging one remote.
@@ -1399,7 +1430,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               finish(head_commit, commit->object.sha1, msg.buf);
+               finish(head_commit, remoteheads, commit->object.sha1, msg.buf);
                drop_save();
                goto done;
        } else if (!remoteheads->next && common->next)
@@ -1421,7 +1452,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        if (!read_tree_trivial(common->item->object.sha1,
                                               head_commit->object.sha1,
                                               remoteheads->item->object.sha1)) {
-                               ret = merge_trivial(head_commit);
+                               ret = merge_trivial(head_commit, remoteheads);
                                goto done;
                        }
                        printf(_("Nope.\n"));
@@ -1492,7 +1523,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                wt_strategy = use_strategies[i]->name;
 
                ret = try_merge_strategy(use_strategies[i]->name,
-                                        common, head_commit, head_arg);
+                                        common, remoteheads,
+                                        head_commit, head_arg);
                if (!option_commit && !ret) {
                        merge_was_ok = 1;
                        /*
@@ -1534,8 +1566,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * auto resolved the merge cleanly.
         */
        if (automerge_was_ok) {
-               ret = finish_automerge(head_commit, common, result_tree,
-                                      wt_strategy);
+               ret = finish_automerge(head_commit, head_subsumed,
+                                      common, remoteheads,
+                                      result_tree, wt_strategy);
                goto done;
        }
 
@@ -1560,13 +1593,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                restore_state(head_commit->object.sha1, stash);
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
-               try_merge_strategy(best_strategy, common, head_commit, head_arg);
+               try_merge_strategy(best_strategy, common, remoteheads,
+                                  head_commit, head_arg);
        }
 
        if (squash)
-               finish(head_commit, NULL, NULL);
+               finish(head_commit, remoteheads, NULL, NULL);
        else
-               write_merge_state();
+               write_merge_state(remoteheads);
 
        if (merge_was_ok)
                fprintf(stderr, _("Automatic merge went well; "
index 7b07c092cc5550d6784531c3137f05f54cfec258..1861093e9db4b951e4cb4c790d4792fe7bc70da7 100644 (file)
@@ -63,6 +63,7 @@ static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
 static int non_empty;
 static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
+static unsigned long unpack_unreachable_expiration;
 static int local;
 static int incremental;
 static int ignore_packed_keep;
@@ -2249,6 +2250,10 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
                if (!p->pack_local || p->pack_keep)
                        continue;
 
+               if (unpack_unreachable_expiration &&
+                   p->mtime < unpack_unreachable_expiration)
+                       continue;
+
                if (open_pack_index(p))
                        die("cannot open pack index");
 
@@ -2315,6 +2320,21 @@ static int option_parse_index_version(const struct option *opt,
        return 0;
 }
 
+static int option_parse_unpack_unreachable(const struct option *opt,
+                                          const char *arg, int unset)
+{
+       if (unset) {
+               unpack_unreachable = 0;
+               unpack_unreachable_expiration = 0;
+       }
+       else {
+               unpack_unreachable = 1;
+               if (arg)
+                       unpack_unreachable_expiration = approxidate(arg);
+       }
+       return 0;
+}
+
 static int option_parse_ulong(const struct option *opt,
                              const char *arg, int unset)
 {
@@ -2392,8 +2412,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                         "include tag objects that refer to objects to be packed"),
                OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
                         "keep unreachable objects"),
-               OPT_BOOL(0, "unpack-unreachable", &unpack_unreachable,
-                        "unpack unreachable objects"),
+               { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, "time",
+                 "unpack unreachable objects newer than <time>",
+                 PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
                OPT_BOOL(0, "thin", &thin,
                         "create thin packs"),
                OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep,
index d315475f16c96a831a11c2aebf00ada40b7c9663..fdfcc6c71607a7412666b7cc647db98e1b85c8c1 100644 (file)
@@ -24,6 +24,7 @@ static int progress = -1;
 static const char **refspec;
 static int refspec_nr;
 static int refspec_alloc;
+static int default_matching_used;
 
 static void add_refspec(const char *ref)
 {
@@ -65,7 +66,54 @@ static void set_refspecs(const char **refs, int nr)
        }
 }
 
-static void setup_push_upstream(struct remote *remote)
+static int push_url_of_remote(struct remote *remote, const char ***url_p)
+{
+       if (remote->pushurl_nr) {
+               *url_p = remote->pushurl;
+               return remote->pushurl_nr;
+       }
+       *url_p = remote->url;
+       return remote->url_nr;
+}
+
+static NORETURN int die_push_simple(struct branch *branch, struct remote *remote) {
+       /*
+        * There's no point in using shorten_unambiguous_ref here,
+        * as the ambiguity would be on the remote side, not what
+        * we have locally. Plus, this is supposed to be the simple
+        * mode. If the user is doing something crazy like setting
+        * upstream to a non-branch, we should probably be showing
+        * them the big ugly fully qualified ref.
+        */
+       const char *advice_maybe = "";
+       const char *short_upstream =
+               skip_prefix(branch->merge[0]->src, "refs/heads/");
+
+       if (!short_upstream)
+               short_upstream = branch->merge[0]->src;
+       /*
+        * Don't show advice for people who explicitely set
+        * push.default.
+        */
+       if (push_default == PUSH_DEFAULT_UNSPECIFIED)
+               advice_maybe = _("\n"
+                                "To choose either option permanently, "
+                                "see push.default in 'git help config'.");
+       die(_("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"),
+           remote->name, short_upstream,
+           remote->name, branch->name, advice_maybe);
+}
+
+static void setup_push_upstream(struct remote *remote, int simple)
 {
        struct strbuf refspec = STRBUF_INIT;
        struct branch *branch = branch_get(NULL);
@@ -76,7 +124,7 @@ static void setup_push_upstream(struct remote *remote)
                    "\n"
                    "    git push %s HEAD:<name-of-remote-branch>\n"),
                    remote->name);
-       if (!branch->merge_nr || !branch->merge)
+       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"
                    "\n"
@@ -87,6 +135,14 @@ static void setup_push_upstream(struct remote *remote)
        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))
+               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);
+
        strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
        add_refspec(refspec.buf);
 }
@@ -95,12 +151,19 @@ static void setup_default_push_refspecs(struct remote *remote)
 {
        switch (push_default) {
        default:
+       case PUSH_DEFAULT_UNSPECIFIED:
+               default_matching_used = 1;
+               /* fallthru */
        case PUSH_DEFAULT_MATCHING:
                add_refspec(":");
                break;
 
+       case PUSH_DEFAULT_SIMPLE:
+               setup_push_upstream(remote, 1);
+               break;
+
        case PUSH_DEFAULT_UPSTREAM:
-               setup_push_upstream(remote);
+               setup_push_upstream(remote, 0);
                break;
 
        case PUSH_DEFAULT_CURRENT:
@@ -114,6 +177,45 @@ 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"
+          "See the 'Note about fast-forwards' in 'git push --help' for details.");
+
+static const char message_advice_use_upstream[] =
+       N_("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\n"
+          "variable to 'current' or 'upstream' to push only the current branch.");
+
+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"
+          "See the 'Note about fast-forwards' in 'git push --help' for details.");
+
+static void advise_pull_before_push(void)
+{
+       if (!advice_push_non_ff_current || !advice_push_nonfastforward)
+               return;
+       advise(_(message_advice_pull_before_push));
+}
+
+static void advise_use_upstream(void)
+{
+       if (!advice_push_non_ff_default || !advice_push_nonfastforward)
+               return;
+       advise(_(message_advice_use_upstream));
+}
+
+static void advise_checkout_pull_push(void)
+{
+       if (!advice_push_non_ff_matching || !advice_push_nonfastforward)
+               return;
+       advise(_(message_advice_checkout_pull_push));
+}
+
 static int push_with_options(struct transport *transport, int flags)
 {
        int err;
@@ -135,14 +237,21 @@ static int push_with_options(struct transport *transport, int flags)
                error(_("failed to push some refs to '%s'"), transport->url);
 
        err |= transport_disconnect(transport);
-
        if (!err)
                return 0;
 
-       if (nonfastforward && advice_push_nonfastforward) {
-               fprintf(stderr, _("To prevent you from losing history, non-fast-forward updates were rejected\n"
-                               "Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
-                               "'Note about fast-forwards' section of 'git push --help' for details.\n"));
+       switch (nonfastforward) {
+       default:
+               break;
+       case NON_FF_HEAD:
+               advise_pull_before_push();
+               break;
+       case NON_FF_OTHER:
+               if (default_matching_used)
+                       advise_use_upstream();
+               else
+                       advise_checkout_pull_push();
+               break;
        }
 
        return 1;
@@ -196,13 +305,7 @@ static int do_push(const char *repo, int flags)
                        setup_default_push_refspecs(remote);
        }
        errs = 0;
-       if (remote->pushurl_nr) {
-               url = remote->pushurl;
-               url_nr = remote->pushurl_nr;
-       } else {
-               url = remote->url;
-               url_nr = remote->url_nr;
-       }
+       url_nr = push_url_of_remote(remote, &url);
        if (url_nr) {
                for (i = 0; i < url_nr; i++) {
                        struct transport *transport =
@@ -224,13 +327,21 @@ static int option_parse_recurse_submodules(const struct option *opt,
                                   const char *arg, int unset)
 {
        int *flags = opt->value;
+
+       if (*flags & (TRANSPORT_RECURSE_SUBMODULES_CHECK |
+                     TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND))
+               die("%s can only be used once.", opt->long_name);
+
        if (arg) {
                if (!strcmp(arg, "check"))
                        *flags |= TRANSPORT_RECURSE_SUBMODULES_CHECK;
+               else if (!strcmp(arg, "on-demand"))
+                       *flags |= TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND;
                else
                        die("bad %s argument: %s", opt->long_name, arg);
        } else
-               die("option %s needs an argument (check)", opt->long_name);
+               die("option %s needs an argument (check|on-demand)",
+                               opt->long_name);
 
        return 0;
 }
index fec92bc66e41b82f929e37b1935d00d6558c34a0..920262d76ec9bdc40a8ad7703cf1eb972a40eb9e 100644 (file)
@@ -9,7 +9,7 @@
 
 static const char * const builtin_remote_usage[] = {
        "git remote [-v | --verbose]",
-       "git remote add [-t <branch>] [-m <master>] [-f] [--mirror=<fetch|push>] <name> <url>",
+       "git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>",
        "git remote rename <old> <new>",
        "git remote rm <name>",
        "git remote set-head <name> (-a | -d | <branch>)",
@@ -17,7 +17,7 @@ static const char * const builtin_remote_usage[] = {
        "git remote prune [-n | --dry-run] <name>",
        "git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]",
        "git remote set-branches [--add] <name> <branch>...",
-       "git remote set-url <name> <newurl> [<oldurl>]",
+       "git remote set-url [--push] <name> <newurl> [<oldurl>]",
        "git remote set-url --add <name> <newurl>",
        "git remote set-url --delete <name> <url>",
        NULL
@@ -95,9 +95,9 @@ static int fetch_remote(const char *name)
                argv[1] = "-v";
                argv[2] = name;
        }
-       printf("Updating %s\n", name);
+       printf_ln(_("Updating %s"), name);
        if (run_command_v_opt(argv, RUN_GIT_CMD))
-               return error("Could not fetch %s", name);
+               return error(_("Could not fetch %s"), name);
        return 0;
 }
 
@@ -127,8 +127,8 @@ static int add_branch(const char *key, const char *branchname,
 }
 
 static const char mirror_advice[] =
-"--mirror is dangerous and deprecated; please\n"
-"\t use --mirror=fetch or --mirror=push instead";
+N_("--mirror is dangerous and deprecated; please\n"
+   "\t use --mirror=fetch or --mirror=push instead");
 
 static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
 {
@@ -136,7 +136,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
        if (not)
                *mirror = MIRROR_NONE;
        else if (!arg) {
-               warning("%s", mirror_advice);
+               warning("%s", _(mirror_advice));
                *mirror = MIRROR_BOTH;
        }
        else if (!strcmp(arg, "fetch"))
@@ -144,7 +144,7 @@ static int parse_mirror_opt(const struct option *opt, const char *arg, int not)
        else if (!strcmp(arg, "push"))
                *mirror = MIRROR_PUSH;
        else
-               return error("unknown mirror argument: %s", arg);
+               return error(_("unknown mirror argument: %s"), arg);
        return 0;
 }
 
@@ -182,9 +182,9 @@ static int add(int argc, const char **argv)
                usage_with_options(builtin_remote_add_usage, options);
 
        if (mirror && master)
-               die("specifying a master branch makes no sense with --mirror");
+               die(_("specifying a master branch makes no sense with --mirror"));
        if (mirror && !(mirror & MIRROR_FETCH) && track.nr)
-               die("specifying branches to track makes sense only with fetch mirrors");
+               die(_("specifying branches to track makes sense only with fetch mirrors"));
 
        name = argv[0];
        url = argv[1];
@@ -192,11 +192,11 @@ static int add(int argc, const char **argv)
        remote = remote_get(name);
        if (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||
                        remote->fetch_refspec_nr))
-               die("remote %s already exists.", name);
+               die(_("remote %s already exists."), name);
 
        strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
        if (!valid_fetch_refspec(buf2.buf))
-               die("'%s' is not a valid remote name", name);
+               die(_("'%s' is not a valid remote name"), name);
 
        strbuf_addf(&buf, "remote.%s.url", name);
        if (git_config_set(buf.buf, url))
@@ -240,7 +240,7 @@ static int add(int argc, const char **argv)
                strbuf_addf(&buf2, "refs/remotes/%s/%s", name, master);
 
                if (create_symref(buf.buf, buf2.buf, "remote add"))
-                       return error("Could not setup master '%s'", master);
+                       return error(_("Could not setup master '%s'"), master);
        }
 
        strbuf_release(&buf);
@@ -296,7 +296,7 @@ static int config_read_branches(const char *key, const char *value, void *cb)
                info = item->util;
                if (type == REMOTE) {
                        if (info->remote_name)
-                               warning("more than one %s", orig_key);
+                               warning(_("more than one %s"), orig_key);
                        info->remote_name = xstrdup(value);
                } else if (type == MERGE) {
                        char *space = strchr(value, ' ');
@@ -336,7 +336,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
 
        for (i = 0; i < states->remote->fetch_refspec_nr; i++)
                if (get_fetch_map(remote_refs, states->remote->fetch + i, &tail, 1))
-                       die("Could not get fetch map for refspec %s",
+                       die(_("Could not get fetch map for refspec %s"),
                                states->remote->fetch_refspec[i]);
 
        states->new.strdup_strings = 1;
@@ -437,7 +437,7 @@ static int get_push_ref_states_noquery(struct ref_states *states)
 
        states->push.strdup_strings = 1;
        if (!remote->push_refspec_nr) {
-               item = string_list_append(&states->push, "(matching)");
+               item = string_list_append(&states->push, _("(matching)"));
                info = item->util = xcalloc(sizeof(struct push_info), 1);
                info->status = PUSH_STATUS_NOTQUERIED;
                info->dest = xstrdup(item->string);
@@ -445,11 +445,11 @@ static int get_push_ref_states_noquery(struct ref_states *states)
        for (i = 0; i < remote->push_refspec_nr; i++) {
                struct refspec *spec = remote->push + i;
                if (spec->matching)
-                       item = string_list_append(&states->push, "(matching)");
+                       item = string_list_append(&states->push, _("(matching)"));
                else if (strlen(spec->src))
                        item = string_list_append(&states->push, spec->src);
                else
-                       item = string_list_append(&states->push, "(delete)");
+                       item = string_list_append(&states->push, _("(delete)"));
 
                info = item->util = xcalloc(sizeof(struct push_info), 1);
                info->forced = spec->force;
@@ -592,19 +592,19 @@ static int migrate_file(struct remote *remote)
        strbuf_addf(&buf, "remote.%s.url", remote->name);
        for (i = 0; i < remote->url_nr; i++)
                if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
-                       return error("Could not append '%s' to '%s'",
+                       return error(_("Could not append '%s' to '%s'"),
                                        remote->url[i], buf.buf);
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.push", remote->name);
        for (i = 0; i < remote->push_refspec_nr; i++)
                if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
-                       return error("Could not append '%s' to '%s'",
+                       return error(_("Could not append '%s' to '%s'"),
                                        remote->push_refspec[i], buf.buf);
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.fetch", remote->name);
        for (i = 0; i < remote->fetch_refspec_nr; i++)
                if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
-                       return error("Could not append '%s' to '%s'",
+                       return error(_("Could not append '%s' to '%s'"),
                                        remote->fetch_refspec[i], buf.buf);
        if (remote->origin == REMOTE_REMOTES)
                path = git_path("remotes/%s", remote->name);
@@ -636,30 +636,30 @@ static int mv(int argc, const char **argv)
 
        oldremote = remote_get(rename.old);
        if (!oldremote)
-               die("No such remote: %s", rename.old);
+               die(_("No such remote: %s"), rename.old);
 
        if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
 
        newremote = remote_get(rename.new);
        if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
-               die("remote %s already exists.", rename.new);
+               die(_("remote %s already exists."), rename.new);
 
        strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
        if (!valid_fetch_refspec(buf.buf))
-               die("'%s' is not a valid remote name", rename.new);
+               die(_("'%s' is not a valid remote name"), rename.new);
 
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s", rename.old);
        strbuf_addf(&buf2, "remote.%s", rename.new);
        if (git_config_rename_section(buf.buf, buf2.buf) < 1)
-               return error("Could not rename config section '%s' to '%s'",
+               return error(_("Could not rename config section '%s' to '%s'"),
                                buf.buf, buf2.buf);
 
        strbuf_reset(&buf);
        strbuf_addf(&buf, "remote.%s.fetch", rename.new);
        if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
-               return error("Could not remove config section '%s'", buf.buf);
+               return error(_("Could not remove config section '%s'"), buf.buf);
        strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
        for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
                char *ptr;
@@ -674,13 +674,13 @@ static int mv(int argc, const char **argv)
                                      strlen(rename.old), rename.new,
                                      strlen(rename.new));
                } else
-                       warning("Not updating non-default fetch respec\n"
-                               "\t%s\n"
-                               "\tPlease update the configuration manually if necessary.",
+                       warning(_("Not updating non-default fetch refspec\n"
+                                 "\t%s\n"
+                                 "\tPlease update the configuration manually if necessary."),
                                buf2.buf);
 
                if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
-                       return error("Could not append '%s'", buf.buf);
+                       return error(_("Could not append '%s'"), buf.buf);
        }
 
        read_branches();
@@ -691,7 +691,7 @@ static int mv(int argc, const char **argv)
                        strbuf_reset(&buf);
                        strbuf_addf(&buf, "branch.%s.remote", item->string);
                        if (git_config_set(buf.buf, rename.new)) {
-                               return error("Could not set '%s'", buf.buf);
+                               return error(_("Could not set '%s'"), buf.buf);
                        }
                }
        }
@@ -713,7 +713,7 @@ static int mv(int argc, const char **argv)
                if (!(flag & REF_ISSYMREF))
                        continue;
                if (delete_ref(item->string, NULL, REF_NODEREF))
-                       die("deleting '%s' failed", item->string);
+                       die(_("deleting '%s' failed"), item->string);
        }
        for (i = 0; i < remote_branches.nr; i++) {
                struct string_list_item *item = remote_branches.items + i;
@@ -728,7 +728,7 @@ static int mv(int argc, const char **argv)
                strbuf_addf(&buf2, "remote: renamed %s to %s",
                                item->string, buf.buf);
                if (rename_ref(item->string, buf.buf, buf2.buf))
-                       die("renaming '%s' failed", item->string);
+                       die(_("renaming '%s' failed"), item->string);
        }
        for (i = 0; i < remote_branches.nr; i++) {
                struct string_list_item *item = remote_branches.items + i;
@@ -747,7 +747,7 @@ static int mv(int argc, const char **argv)
                strbuf_addf(&buf3, "remote: renamed %s to %s",
                                item->string, buf.buf);
                if (create_symref(buf.buf, buf2.buf, buf3.buf))
-                       die("creating '%s' failed", buf.buf);
+                       die(_("creating '%s' failed"), buf.buf);
        }
        return 0;
 }
@@ -761,7 +761,7 @@ static int remove_branches(struct string_list *branches)
                unsigned char *sha1 = item->util;
 
                if (delete_ref(refname, sha1, 0))
-                       result |= error("Could not remove branch %s", refname);
+                       result |= error(_("Could not remove branch %s"), refname);
        }
        return result;
 }
@@ -789,14 +789,14 @@ static int rm(int argc, const char **argv)
 
        remote = remote_get(argv[1]);
        if (!remote)
-               die("No such remote: %s", argv[1]);
+               die(_("No such remote: %s"), argv[1]);
 
        known_remotes.to_delete = remote;
        for_each_remote(add_known_remote, &known_remotes);
 
        strbuf_addf(&buf, "remote.%s", remote->name);
        if (git_config_rename_section(buf.buf, NULL) < 1)
-               return error("Could not remove config section '%s'", buf.buf);
+               return error(_("Could not remove config section '%s'"), buf.buf);
 
        read_branches();
        for (i = 0; i < branch_list.nr; i++) {
@@ -830,11 +830,12 @@ static int rm(int argc, const char **argv)
        string_list_clear(&branches, 1);
 
        if (skipped.nr) {
-               fprintf(stderr, skipped.nr == 1 ?
-                       "Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
-                       "to delete it, use:\n" :
-                       "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
-                       "to delete them, use:\n");
+               fprintf_ln(stderr,
+                          Q_("Note: A branch outside the refs/remotes/ hierarchy was not removed;\n"
+                             "to delete it, use:",
+                             "Note: Some branches outside the refs/remotes/ hierarchy were not removed;\n"
+                             "to delete them, use:",
+                             skipped.nr));
                for (i = 0; i < skipped.nr; i++)
                        fprintf(stderr, "  git branch -d %s\n",
                                skipped.items[i].string);
@@ -886,7 +887,7 @@ static int get_remote_ref_states(const char *name,
 
        states->remote = remote_get(name);
        if (!states->remote)
-               return error("No such remote: %s", name);
+               return error(_("No such remote: %s"), name);
 
        read_branches();
 
@@ -939,14 +940,14 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
                const char *fmt = "%s";
                const char *arg = "";
                if (string_list_has_string(&states->new, name)) {
-                       fmt = " new (next fetch will store in remotes/%s)";
+                       fmt = _(" new (next fetch will store in remotes/%s)");
                        arg = states->remote->name;
                } else if (string_list_has_string(&states->tracked, name))
-                       arg = " tracked";
+                       arg = _(" tracked");
                else if (string_list_has_string(&states->stale, name))
-                       arg = " stale (use 'git remote prune' to remove)";
+                       arg = _(" stale (use 'git remote prune' to remove)");
                else
-                       arg = " ???";
+                       arg = _(" ???");
                printf("    %-*s", info->width, name);
                printf(fmt, arg);
                printf("\n");
@@ -987,21 +988,21 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
        int i;
 
        if (branch_info->rebase && branch_info->merge.nr > 1) {
-               error("invalid branch.%s.merge; cannot rebase onto > 1 branch",
+               error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"),
                        item->string);
                return 0;
        }
 
        printf("    %-*s ", show_info->width, item->string);
        if (branch_info->rebase) {
-               printf("rebases onto remote %s\n", merge->items[0].string);
+               printf_ln(_("rebases onto remote %s"), merge->items[0].string);
                return 0;
        } else if (show_info->any_rebase) {
-               printf(" merges with remote %s\n", merge->items[0].string);
-               also = "    and with remote";
+               printf_ln(_(" merges with remote %s"), merge->items[0].string);
+               also = _("    and with remote");
        } else {
-               printf("merges with remote %s\n", merge->items[0].string);
-               also = "   and with remote";
+               printf_ln(_("merges with remote %s"), merge->items[0].string);
+               also = _("   and with remote");
        }
        for (i = 1; i < merge->nr; i++)
                printf("    %-*s %s %s\n", show_info->width, "", also,
@@ -1043,36 +1044,43 @@ static int show_push_info_item(struct string_list_item *item, void *cb_data)
 {
        struct show_info *show_info = cb_data;
        struct push_info *push_info = item->util;
-       char *src = item->string, *status = NULL;
+       const char *src = item->string, *status = NULL;
 
        switch (push_info->status) {
        case PUSH_STATUS_CREATE:
-               status = "create";
+               status = _("create");
                break;
        case PUSH_STATUS_DELETE:
-               status = "delete";
-               src = "(none)";
+               status = _("delete");
+               src = _("(none)");
                break;
        case PUSH_STATUS_UPTODATE:
-               status = "up to date";
+               status = _("up to date");
                break;
        case PUSH_STATUS_FASTFORWARD:
-               status = "fast-forwardable";
+               status = _("fast-forwardable");
                break;
        case PUSH_STATUS_OUTOFDATE:
-               status = "local out of date";
+               status = _("local out of date");
                break;
        case PUSH_STATUS_NOTQUERIED:
                break;
        }
-       if (status)
-               printf("    %-*s %s to %-*s (%s)\n", show_info->width, src,
-                       push_info->forced ? "forces" : "pushes",
-                       show_info->width2, push_info->dest, status);
-       else
-               printf("    %-*s %s to %s\n", show_info->width, src,
-                       push_info->forced ? "forces" : "pushes",
-                       push_info->dest);
+       if (status) {
+               if (push_info->forced)
+                       printf_ln(_("    %-*s forces to %-*s (%s)"), show_info->width, src,
+                              show_info->width2, push_info->dest, status);
+               else
+                       printf_ln(_("    %-*s pushes to %-*s (%s)"), show_info->width, src,
+                              show_info->width2, push_info->dest, status);
+       } else {
+               if (push_info->forced)
+                       printf_ln(_("    %-*s forces to %s"), show_info->width, src,
+                              push_info->dest);
+               else
+                       printf_ln(_("    %-*s pushes to %s"), show_info->width, src,
+                              push_info->dest);
+       }
        return 0;
 }
 
@@ -1107,9 +1115,9 @@ static int show(int argc, const char **argv)
 
                get_remote_ref_states(*argv, &states, query_flag);
 
-               printf("* remote %s\n", *argv);
-               printf("  Fetch URL: %s\n", states.remote->url_nr > 0 ?
-                       states.remote->url[0] : "(no URL)");
+               printf_ln(_("* remote %s"), *argv);
+               printf_ln(_("  Fetch URL: %s"), states.remote->url_nr > 0 ?
+                      states.remote->url[0] : _("(no URL)"));
                if (states.remote->pushurl_nr) {
                        url = states.remote->pushurl;
                        url_nr = states.remote->pushurl_nr;
@@ -1118,18 +1126,18 @@ static int show(int argc, const char **argv)
                        url_nr = states.remote->url_nr;
                }
                for (i = 0; i < url_nr; i++)
-                       printf("  Push  URL: %s\n", url[i]);
+                       printf_ln(_("  Push  URL: %s"), url[i]);
                if (!i)
-                       printf("  Push  URL: %s\n", "(no URL)");
+                       printf_ln(_("  Push  URL: %s"), "(no URL)");
                if (no_query)
-                       printf("  HEAD branch: (not queried)\n");
+                       printf_ln(_("  HEAD branch: %s"), "(not queried)");
                else if (!states.heads.nr)
-                       printf("  HEAD branch: (unknown)\n");
+                       printf_ln(_("  HEAD branch: %s"), "(unknown)");
                else if (states.heads.nr == 1)
-                       printf("  HEAD branch: %s\n", states.heads.items[0].string);
+                       printf_ln(_("  HEAD branch: %s"), states.heads.items[0].string);
                else {
-                       printf("  HEAD branch (remote HEAD is ambiguous,"
-                              " may be one of the following):\n");
+                       printf(_("  HEAD branch (remote HEAD is ambiguous,"
+                                " may be one of the following):\n"));
                        for (i = 0; i < states.heads.nr; i++)
                                printf("    %s\n", states.heads.items[i].string);
                }
@@ -1140,9 +1148,10 @@ static int show(int argc, const char **argv)
                for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
                for_each_string_list(&states.stale, add_remote_to_show_info, &info);
                if (info.list->nr)
-                       printf("  Remote branch%s:%s\n",
-                              info.list->nr > 1 ? "es" : "",
-                               no_query ? " (status not queried)" : "");
+                       printf_ln(Q_("  Remote branch:%s",
+                                    "  Remote branches:%s",
+                                    info.list->nr),
+                                 no_query ? _(" (status not queried)") : "");
                for_each_string_list(info.list, show_remote_info_item, &info);
                string_list_clear(info.list, 0);
 
@@ -1151,23 +1160,25 @@ static int show(int argc, const char **argv)
                info.any_rebase = 0;
                for_each_string_list(&branch_list, add_local_to_show_info, &info);
                if (info.list->nr)
-                       printf("  Local branch%s configured for 'git pull':\n",
-                              info.list->nr > 1 ? "es" : "");
+                       printf_ln(Q_("  Local branch configured for 'git pull':",
+                                    "  Local branches configured for 'git pull':",
+                                    info.list->nr));
                for_each_string_list(info.list, show_local_info_item, &info);
                string_list_clear(info.list, 0);
 
                /* git push info */
                if (states.remote->mirror)
-                       printf("  Local refs will be mirrored by 'git push'\n");
+                       printf_ln(_("  Local refs will be mirrored by 'git push'"));
 
                info.width = info.width2 = 0;
                for_each_string_list(&states.push, add_push_to_show_info, &info);
                qsort(info.list->items, info.list->nr,
                        sizeof(*info.list->items), cmp_string_with_push);
                if (info.list->nr)
-                       printf("  Local ref%s configured for 'git push'%s:\n",
-                               info.list->nr > 1 ? "s" : "",
-                               no_query ? " (status not queried)" : "");
+                       printf_ln(Q_("  Local ref configured for 'git push'%s:",
+                                    "  Local refs configured for 'git push'%s:",
+                                    info.list->nr),
+                                 no_query ? _(" (status not queried)") : "");
                for_each_string_list(info.list, show_push_info_item, &info);
                string_list_clear(info.list, 0);
 
@@ -1202,10 +1213,10 @@ static int set_head(int argc, const char **argv)
                memset(&states, 0, sizeof(states));
                get_remote_ref_states(argv[0], &states, GET_HEAD_NAMES);
                if (!states.heads.nr)
-                       result |= error("Cannot determine remote HEAD");
+                       result |= error(_("Cannot determine remote HEAD"));
                else if (states.heads.nr > 1) {
-                       result |= error("Multiple remote HEAD branches. "
-                                       "Please choose one explicitly with:");
+                       result |= error(_("Multiple remote HEAD branches. "
+                                         "Please choose one explicitly with:"));
                        for (i = 0; i < states.heads.nr; i++)
                                fprintf(stderr, "  git remote set-head %s %s\n",
                                        argv[0], states.heads.items[i].string);
@@ -1214,7 +1225,7 @@ static int set_head(int argc, const char **argv)
                free_remote_ref_states(&states);
        } else if (opt_d && !opt_a && argc == 1) {
                if (delete_ref(buf.buf, NULL, REF_NODEREF))
-                       result |= error("Could not delete %s", buf.buf);
+                       result |= error(_("Could not delete %s"), buf.buf);
        } else
                usage_with_options(builtin_remote_sethead_usage, options);
 
@@ -1222,9 +1233,9 @@ static int set_head(int argc, const char **argv)
                strbuf_addf(&buf2, "refs/remotes/%s/%s", argv[0], head_name);
                /* make sure it's valid */
                if (!ref_exists(buf2.buf))
-                       result |= error("Not a valid ref: %s", buf2.buf);
+                       result |= error(_("Not a valid ref: %s"), buf2.buf);
                else if (create_symref(buf.buf, buf2.buf, "remote set-head"))
-                       result |= error("Could not setup %s", buf.buf);
+                       result |= error(_("Could not setup %s"), buf.buf);
                if (opt_a)
                        printf("%s/HEAD set to %s\n", argv[0], head_name);
                free(head_name);
@@ -1260,18 +1271,18 @@ static int prune_remote(const char *remote, int dry_run)
        int result = 0, i;
        struct ref_states states;
        const char *dangling_msg = dry_run
-               ? " %s will become dangling!\n"
-               : " %s has become dangling!\n";
+               ? _(" %s will become dangling!")
+               : _(" %s has become dangling!");
 
        memset(&states, 0, sizeof(states));
        get_remote_ref_states(remote, &states, GET_REF_STATES);
 
        if (states.stale.nr) {
-               printf("Pruning %s\n", remote);
-               printf("URL: %s\n",
+               printf_ln(_("Pruning %s"), remote);
+               printf_ln(_("URL: %s"),
                       states.remote->url_nr
                       ? states.remote->url[0]
-                      : "(no URL)");
+                      : _("(no URL)"));
        }
 
        for (i = 0; i < states.stale.nr; i++) {
@@ -1280,8 +1291,12 @@ static int prune_remote(const char *remote, int dry_run)
                if (!dry_run)
                        result |= delete_ref(refname, NULL, 0);
 
-               printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
-                      abbrev_ref(refname, "refs/remotes/"));
+               if (dry_run)
+                       printf_ln(_(" * [would prune] %s"),
+                              abbrev_ref(refname, "refs/remotes/"));
+               else
+                       printf_ln(_(" * [pruned] %s"),
+                              abbrev_ref(refname, "refs/remotes/"));
                warn_dangling_symref(stdout, dangling_msg, refname);
        }
 
@@ -1369,7 +1384,7 @@ static int set_remote_branches(const char *remotename, const char **branches,
        strbuf_addf(&key, "remote.%s.fetch", remotename);
 
        if (!remote_is_configured(remotename))
-               die("No such remote '%s'", remotename);
+               die(_("No such remote '%s'"), remotename);
        remote = remote_get(remotename);
 
        if (!add_mode && remove_all_fetch_refspecs(remotename, key.buf)) {
@@ -1396,7 +1411,7 @@ static int set_branches(int argc, const char **argv)
        argc = parse_options(argc, argv, NULL, options,
                             builtin_remote_setbranches_usage, 0);
        if (argc == 0) {
-               error("no remote specified");
+               error(_("no remote specified"));
                usage_with_options(builtin_remote_setbranches_usage, options);
        }
        argv[argc] = NULL;
@@ -1429,7 +1444,7 @@ static int set_url(int argc, const char **argv)
                             PARSE_OPT_KEEP_ARGV0);
 
        if (add_mode && delete_mode)
-               die("--add --delete doesn't make sense");
+               die(_("--add --delete doesn't make sense"));
 
        if (argc < 3 || argc > 4 || ((add_mode || delete_mode) && argc != 3))
                usage_with_options(builtin_remote_seturl_usage, options);
@@ -1443,7 +1458,7 @@ static int set_url(int argc, const char **argv)
                oldurl = newurl;
 
        if (!remote_is_configured(remotename))
-               die("No such remote '%s'", remotename);
+               die(_("No such remote '%s'"), remotename);
        remote = remote_get(remotename);
 
        if (push_mode) {
@@ -1469,7 +1484,7 @@ static int set_url(int argc, const char **argv)
 
        /* Old URL specified. Demand that one matches. */
        if (regcomp(&old_regex, oldurl, REG_EXTENDED))
-               die("Invalid old URL pattern: %s", oldurl);
+               die(_("Invalid old URL pattern: %s"), oldurl);
 
        for (i = 0; i < urlset_nr; i++)
                if (!regexec(&old_regex, urlset[i], 0, NULL, 0))
@@ -1477,9 +1492,9 @@ static int set_url(int argc, const char **argv)
                else
                        negative_matches++;
        if (!delete_mode && !matches)
-               die("No such URL found: %s", oldurl);
+               die(_("No such URL found: %s"), oldurl);
        if (delete_mode && !negative_matches && !push_mode)
-               die("Will not delete all non-push URLs");
+               die(_("Will not delete all non-push URLs"));
 
        regfree(&old_regex);
 
@@ -1580,7 +1595,7 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
        else if (!strcmp(argv[0], "update"))
                result = update(argc, argv);
        else {
-               error("Unknown subcommand: %s", argv[0]);
+               error(_("Unknown subcommand: %s"), argv[0]);
                usage_with_options(builtin_remote_usage, options);
        }
 
index 4c4d404afc7321a222d3dc136b570ada3d3c2317..ff5a38372d76cc50167a8f7e7862a67cc9d419ea 100644 (file)
@@ -109,6 +109,7 @@ static void show_commit(struct commit *commit, void *data)
                struct pretty_print_context ctx = {0};
                ctx.abbrev = revs->abbrev;
                ctx.date_mode = revs->date_mode;
+               ctx.date_mode_explicit = revs->date_mode_explicit;
                ctx.fmt = revs->commit_format;
                pretty_print_commit(&ctx, commit, &buf);
                if (revs->graph) {
index 98d1cbeccacc4b22dee88dab3d0154a3f70afe81..733f626f6c3e4ef54d54df923230f7ae4fbb2d7d 100644 (file)
@@ -634,6 +634,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        if (!strcmp(arg, "--show-prefix")) {
                                if (prefix)
                                        puts(prefix);
+                               else
+                                       putchar('\n');
                                continue;
                        }
                        if (!strcmp(arg, "--show-cdup")) {
index e6840f23dc9ee6d670bb6254bee074e58e818486..82d1bf844b996d5d121169f244358964fd36fd19 100644 (file)
@@ -86,6 +86,7 @@ static void verify_opt_mutually_compatible(const char *me, ...)
                                break;
                }
        }
+       va_end(ap);
 
        if (opt1 && opt2)
                die(_("%s: %s cannot be used with %s"), me, opt1, opt2);
@@ -114,12 +115,16 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
                OPT_END(),
                OPT_END(),
                OPT_END(),
+               OPT_END(),
+               OPT_END(),
        };
 
        if (opts->action == REPLAY_PICK) {
                struct option cp_extra[] = {
                        OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
                        OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
+                       OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, "preserve initially empty commits"),
+                       OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, "keep redundant, empty commits"),
                        OPT_END(),
                };
                if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
@@ -137,6 +142,10 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
                                "--abort", rollback,
                                NULL);
 
+       /* implies allow_empty */
+       if (opts->keep_redundant_commits)
+               opts->allow_empty = 1;
+
        /* Set the subcommand */
        if (remove_state)
                opts->subcommand = REPLAY_REMOVE_STATE;
@@ -181,12 +190,15 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
        if (opts->subcommand != REPLAY_NONE) {
                opts->revs = NULL;
        } else {
+               struct setup_revision_opt s_r_opt;
                opts->revs = xmalloc(sizeof(*opts->revs));
                init_revisions(opts->revs, NULL);
                opts->revs->no_walk = 1;
                if (argc < 2)
                        usage_with_options(usage_str, options);
-               argc = setup_revisions(argc, argv, opts->revs, NULL);
+               memset(&s_r_opt, 0, sizeof(s_r_opt));
+               s_r_opt.assume_dashdash = 1;
+               argc = setup_revisions(argc, argv, opts->revs, &s_r_opt);
        }
 
        if (argc > 1)
index 9df341c793d58eff215805bf5ca5da383f4a99d9..d5d7105ba2debde9fa6f2f33171d84cc7c25339a 100644 (file)
@@ -410,6 +410,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        const char *receivepack = "git-receive-pack";
        int flags;
        int nonfastforward = 0;
+       int progress = -1;
 
        argv++;
        for (i = 1; i < argc; i++, argv++) {
@@ -452,6 +453,14 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                args.verbose = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--progress")) {
+                               progress = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "--no-progress")) {
+                               progress = 0;
+                               continue;
+                       }
                        if (!strcmp(arg, "--thin")) {
                                args.use_thin_pack = 1;
                                continue;
@@ -492,6 +501,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                }
        }
 
+       if (progress == -1)
+               progress = !args.quiet && isatty(2);
+       args.progress = progress;
+
        if (args.stateless_rpc) {
                conn = NULL;
                fd[0] = 0;
index fe7e5e5b3d64dc8168ebe687ac5c6c2d1cbbdba1..4fb6bd7b3dd39dadbbb2eb3bfb8a8c3dc96e8484 100644 (file)
@@ -16,6 +16,7 @@
 #include "revision.h"
 #include "gpg-interface.h"
 #include "sha1-array.h"
+#include "column.h"
 
 static const char * const git_tag_usage[] = {
        "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@ -33,6 +34,7 @@ struct tag_filter {
 };
 
 static struct sha1_array points_at;
+static unsigned int colopts;
 
 static int match_pattern(const char **patterns, const char *ref)
 {
@@ -263,6 +265,8 @@ static int git_tag_config(const char *var, const char *value, void *cb)
        int status = git_gpg_config(var, value, cb);
        if (status)
                return status;
+       if (!prefixcmp(var, "column."))
+               return git_column_config(var, value, "tag", &colopts);
        return git_default_config(var, value, cb);
 }
 
@@ -459,6 +463,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_STRING('u', "local-user", &keyid, "key-id",
                                        "use another key to sign the tag"),
                OPT__FORCE(&force, "replace the tag if exists"),
+               OPT_COLUMN(0, "column", &colopts, "show tag list in columns"),
 
                OPT_GROUP("Tag listing options"),
                {
@@ -495,9 +500,25 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
        if (list + delete + verify > 1)
                usage_with_options(git_tag_usage, options);
-       if (list)
-               return list_tags(argv, lines == -1 ? 0 : lines,
-                                with_commit);
+       finalize_colopts(&colopts, -1);
+       if (list && lines != -1) {
+               if (explicitly_enable_column(colopts))
+                       die(_("--column and -n are incompatible"));
+               colopts = 0;
+       }
+       if (list) {
+               int ret;
+               if (column_active(colopts)) {
+                       struct column_options copts;
+                       memset(&copts, 0, sizeof(copts));
+                       copts.padding = 2;
+                       run_column_filter(colopts, &copts);
+               }
+               ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);
+               if (column_active(colopts))
+                       stop_column_filter();
+               return ret;
+       }
        if (lines != -1)
                die(_("-n option is only allowed with -l."));
        if (with_commit)
index 14e04e6795bd514fb1fec506073d8a9a71668cfa..2217d7b3ae57235fc25ac3f5bf8429588aa034c4 100644 (file)
@@ -107,7 +107,7 @@ static void *get_data(unsigned long size)
                if (stream.total_out == size && ret == Z_STREAM_END)
                        break;
                if (ret != Z_OK) {
-                       error("inflate returned %d\n", ret);
+                       error("inflate returned %d", ret);
                        free(buf);
                        buf = NULL;
                        if (!recover)
index a6a23fa1f3c7782566d7fcfe470dd424b876e4a5..5f038d64da38820ebaa73ff73d1082e17c3c80d2 100644 (file)
@@ -708,6 +708,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        int newfd, entries, has_errors = 0, line_termination = '\n';
        int read_from_stdin = 0;
        int prefix_length = prefix ? strlen(prefix) : 0;
+       int preferred_index_format = 0;
        char set_executable_bit = 0;
        struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
@@ -791,6 +792,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                        "(for porcelains) forget saved unresolved conflicts",
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        resolve_undo_clear_callback},
+               OPT_INTEGER(0, "index-version", &preferred_index_format,
+                           "write index in this format"),
                OPT_END()
        };
 
@@ -851,6 +854,17 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                }
        }
        argc = parse_options_end(&ctx);
+       if (preferred_index_format) {
+               if (preferred_index_format < INDEX_FORMAT_LB ||
+                   INDEX_FORMAT_UB < preferred_index_format)
+                       die("index-version %d not in range: %d..%d",
+                           preferred_index_format,
+                           INDEX_FORMAT_LB, INDEX_FORMAT_UB);
+
+               if (the_index.version != preferred_index_format)
+                       active_cache_changed = 1;
+               the_index.version = preferred_index_format;
+       }
 
        if (read_from_stdin) {
                struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
index b90dce6358153b274a1e26afde9cc89aad473d14..0d63c4498c0c10193846c020a2d76958bd12e1bd 100644 (file)
@@ -15,6 +15,7 @@ int cmd_update_server_info(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, options,
                             update_server_info_usage, 0);
        if (argc > 0)
index d9cfd90534b6b6b0659576d5a71d1fa216bfa6b5..8d31b98f58b9e9bf156615130ec80684f788fcaa 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -33,7 +33,7 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
        if (strbuf_getwholeline_fd(&buf, fd, '\n') ||
            strcmp(buf.buf, bundle_signature)) {
                if (report_path)
-                       error("'%s' does not look like a v2 bundle file",
+                       error(_("'%s' does not look like a v2 bundle file"),
                              report_path);
                status = -1;
                goto abort;
@@ -60,7 +60,7 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
                    (40 <= buf.len && !isspace(buf.buf[40])) ||
                    (!is_prereq && buf.len <= 40)) {
                        if (report_path)
-                               error("unrecognized header: %s%s (%d)",
+                               error(_("unrecognized header: %s%s (%d)"),
                                      (is_prereq ? "-" : ""), buf.buf, (int)buf.len);
                        status = -1;
                        break;
@@ -86,7 +86,7 @@ int read_bundle_header(const char *path, struct bundle_header *header)
        int fd = open(path, O_RDONLY);
 
        if (fd < 0)
-               return error("could not open '%s'", path);
+               return error(_("could not open '%s'"), path);
        return parse_bundle_header(fd, header, path);
 }
 
@@ -137,7 +137,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
        struct object_array refs;
        struct commit *commit;
        int i, ret = 0, req_nr;
-       const char *message = "Repository lacks these prerequisite commits:";
+       const char *message = _("Repository lacks these prerequisite commits:");
 
        init_revisions(&revs, NULL);
        for (i = 0; i < p->nr; i++) {
@@ -161,7 +161,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
        revs.leak_pending = 1;
 
        if (prepare_revision_walk(&revs))
-               die("revision walk setup failed");
+               die(_("revision walk setup failed"));
 
        i = req_nr;
        while (i && (commit = get_revision(&revs)))
@@ -183,12 +183,16 @@ int verify_bundle(struct bundle_header *header, int verbose)
                struct ref_list *r;
 
                r = &header->references;
-               printf("The bundle contains %d ref%s\n",
-                      r->nr, (1 < r->nr) ? "s" : "");
+               printf_ln(Q_("The bundle contains %d ref",
+                            "The bundle contains %d refs",
+                            r->nr),
+                         r->nr);
                list_refs(r, 0, NULL);
                r = &header->prerequisites;
-               printf("The bundle requires these %d ref%s\n",
-                      r->nr, (1 < r->nr) ? "s" : "");
+               printf_ln(Q_("The bundle requires this ref",
+                            "The bundle requires these %d refs",
+                            r->nr),
+                         r->nr);
                list_refs(r, 0, NULL);
        }
        return ret;
@@ -283,13 +287,13 @@ int create_bundle(struct bundle_header *header, const char *path,
        strbuf_release(&buf);
        fclose(rls_fout);
        if (finish_command(&rls))
-               return error("rev-list died");
+               return error(_("rev-list died"));
 
        /* write references */
        argc = setup_revisions(argc, argv, &revs, NULL);
 
        if (argc > 1)
-               return error("unrecognized argument: %s'", argv[1]);
+               return error(_("unrecognized argument: %s"), argv[1]);
 
        object_array_remove_duplicates(&revs.pending);
 
@@ -324,7 +328,7 @@ int create_bundle(struct bundle_header *header, const char *path,
                 * constraints.
                 */
                if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
-                       warning("ref '%s' is excluded by the rev-list options",
+                       warning(_("ref '%s' is excluded by the rev-list options"),
                                e->name);
                        free(ref);
                        continue;
@@ -369,7 +373,7 @@ int create_bundle(struct bundle_header *header, const char *path,
                free(ref);
        }
        if (!ref_count)
-               die ("Refusing to create empty bundle.");
+               die(_("Refusing to create empty bundle."));
 
        /* end header */
        write_or_die(bundle_fd, "\n", 1);
@@ -387,7 +391,7 @@ int create_bundle(struct bundle_header *header, const char *path,
        rls.out = bundle_fd;
        rls.git_cmd = 1;
        if (start_command(&rls))
-               return error("Could not spawn pack-objects");
+               return error(_("Could not spawn pack-objects"));
 
        /*
         * start_command closed bundle_fd if it was > 1
@@ -405,10 +409,10 @@ int create_bundle(struct bundle_header *header, const char *path,
        }
        close(rls.in);
        if (finish_command(&rls))
-               return error ("pack-objects died");
+               return error(_("pack-objects died"));
        if (!bundle_to_stdout) {
                if (commit_lock_file(&lock))
-                       die_errno("cannot create '%s'", path);
+                       die_errno(_("cannot create '%s'"), path);
        }
        return 0;
 }
@@ -430,6 +434,6 @@ int unbundle(struct bundle_header *header, int bundle_fd, int flags)
        ip.no_stdout = 1;
        ip.git_cmd = 1;
        if (run_command(&ip))
-               return error("index-pack died");
+               return error(_("index-pack died"));
        return 0;
 }
diff --git a/cache.h b/cache.h
index e5e1aa4e15a336927376c63651d88d63f02c44bf..cc5048c202b6799591e7121de91ee2c6cdce9997 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -105,6 +105,9 @@ struct cache_header {
        unsigned int hdr_entries;
 };
 
+#define INDEX_FORMAT_LB 2
+#define INDEX_FORMAT_UB 4
+
 /*
  * The "cache_time" is just the low 32 bits of the
  * time. It doesn't matter if it overflows - we only
@@ -115,48 +118,6 @@ struct cache_time {
        unsigned int nsec;
 };
 
-/*
- * dev/ino/uid/gid/size are also just tracked to the low 32 bits
- * Again - this is just a (very strong in practice) heuristic that
- * the inode hasn't changed.
- *
- * We save the fields in big-endian order to allow using the
- * index file over NFS transparently.
- */
-struct ondisk_cache_entry {
-       struct cache_time ctime;
-       struct cache_time mtime;
-       unsigned int dev;
-       unsigned int ino;
-       unsigned int mode;
-       unsigned int uid;
-       unsigned int gid;
-       unsigned int size;
-       unsigned char sha1[20];
-       unsigned short flags;
-       char name[FLEX_ARRAY]; /* more */
-};
-
-/*
- * This struct is used when CE_EXTENDED bit is 1
- * The struct must match ondisk_cache_entry exactly from
- * ctime till flags
- */
-struct ondisk_cache_entry_extended {
-       struct cache_time ctime;
-       struct cache_time mtime;
-       unsigned int dev;
-       unsigned int ino;
-       unsigned int mode;
-       unsigned int uid;
-       unsigned int gid;
-       unsigned int size;
-       unsigned char sha1[20];
-       unsigned short flags;
-       unsigned short flags2;
-       char name[FLEX_ARRAY]; /* more */
-};
-
 struct cache_entry {
        struct cache_time ce_ctime;
        struct cache_time ce_mtime;
@@ -253,9 +214,6 @@ static inline size_t ce_namelen(const struct cache_entry *ce)
 }
 
 #define ce_size(ce) cache_entry_size(ce_namelen(ce))
-#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
-                           ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
-                           ondisk_cache_entry_size(ce_namelen(ce)))
 #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
 #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
 #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
@@ -306,13 +264,11 @@ static inline unsigned int canon_mode(unsigned int mode)
        return S_IFGITLINK;
 }
 
-#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
 #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
-#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
-#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
 
 struct index_state {
        struct cache_entry **cache;
+       unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
@@ -624,8 +580,10 @@ enum rebase_setup_type {
 enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
+       PUSH_DEFAULT_SIMPLE,
        PUSH_DEFAULT_UPSTREAM,
-       PUSH_DEFAULT_CURRENT
+       PUSH_DEFAULT_CURRENT,
+       PUSH_DEFAULT_UNSPECIFIED
 };
 
 extern enum branch_track git_branch_track;
@@ -708,6 +666,19 @@ static inline void hashclr(unsigned char *hash)
 #define EMPTY_TREE_SHA1_BIN \
         ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
 
+#define EMPTY_BLOB_SHA1_HEX \
+       "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+#define EMPTY_BLOB_SHA1_BIN_LITERAL \
+       "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
+       "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
+#define EMPTY_BLOB_SHA1_BIN \
+       ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
+
+static inline int is_empty_blob_sha1(const unsigned char *sha1)
+{
+       return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
+}
+
 int git_mkstemp(char *path, size_t n, const char *template);
 
 int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
@@ -906,10 +877,8 @@ enum date_mode {
 };
 
 const char *show_date(unsigned long time, int timezone, enum date_mode mode);
-const char *show_date_relative(unsigned long time, int tz,
-                              const struct timeval *now,
-                              char *timebuf,
-                              size_t timebuf_size);
+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);
 void datestamp(char *buf, int bufsize);
@@ -928,6 +897,22 @@ extern const char *fmt_name(const char *name, const char *email);
 extern const char *git_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 
+struct ident_split {
+       const char *name_begin;
+       const char *name_end;
+       const char *mail_begin;
+       const char *mail_end;
+       const char *date_begin;
+       const char *date_end;
+       const char *tz_begin;
+       const char *tz_end;
+};
+/*
+ * Signals an success with 0, but time part of the result may be NULL
+ * if the input lacks timestamp and zone
+ */
+extern int split_ident_line(struct ident_split *, const char *, int);
+
 struct checkout {
        const char *base_dir;
        int base_dir_len;
@@ -962,6 +947,7 @@ extern struct alternate_object_database {
        char base[FLEX_ARRAY]; /* more */
 } *alt_odb_list;
 extern void prepare_alt_odb(void);
+extern void read_info_alternates(const char * relative_base, int depth);
 extern void add_to_alternates_file(const char *reference);
 typedef int alt_odb_fn(struct alternate_object_database *, void *);
 extern void foreach_alt_odb(alt_odb_fn, void*);
@@ -1276,4 +1262,6 @@ extern struct startup_info *startup_info;
 /* builtin/merge.c */
 int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
 
+int sane_execvp(const char *file, char *const argv[]);
+
 #endif /* CACHE_H */
diff --git a/column.c b/column.c
new file mode 100644 (file)
index 0000000..9367ba5
--- /dev/null
+++ b/column.c
@@ -0,0 +1,434 @@
+#include "cache.h"
+#include "column.h"
+#include "string-list.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "utf8.h"
+
+#define XY2LINEAR(d, x, y) (COL_LAYOUT((d)->colopts) == COL_COLUMN ? \
+                           (x) * (d)->rows + (y) : \
+                           (y) * (d)->cols + (x))
+
+struct column_data {
+       const struct string_list *list;
+       unsigned int colopts;
+       struct column_options opts;
+
+       int rows, cols;
+       int *len;               /* cell length */
+       int *width;           /* index to the longest row in column */
+};
+
+/* return length of 's' in letters, ANSI escapes stripped */
+static int item_length(unsigned int colopts, const char *s)
+{
+       int len, i = 0;
+       struct strbuf str = STRBUF_INIT;
+
+       strbuf_addstr(&str, s);
+       while ((s = strstr(str.buf + i, "\033[")) != NULL) {
+               int len = strspn(s + 2, "0123456789;");
+               i = s - str.buf;
+               strbuf_remove(&str, i, len + 3); /* \033[<len><func char> */
+       }
+       len = utf8_strwidth(str.buf);
+       strbuf_release(&str);
+       return len;
+}
+
+/*
+ * Calculate cell width, rows and cols for a table of equal cells, given
+ * table width and how many spaces between cells.
+ */
+static void layout(struct column_data *data, int *width)
+{
+       int i;
+
+       *width = 0;
+       for (i = 0; i < data->list->nr; i++)
+               if (*width < data->len[i])
+                       *width = data->len[i];
+
+       *width += data->opts.padding;
+
+       data->cols = (data->opts.width - strlen(data->opts.indent)) / *width;
+       if (data->cols == 0)
+               data->cols = 1;
+
+       data->rows = DIV_ROUND_UP(data->list->nr, data->cols);
+}
+
+static void compute_column_width(struct column_data *data)
+{
+       int i, x, y;
+       for (x = 0; x < data->cols; x++) {
+               data->width[x] = XY2LINEAR(data, x, 0);
+               for (y = 0; y < data->rows; y++) {
+                       i = XY2LINEAR(data, x, y);
+                       if (i < data->list->nr &&
+                           data->len[data->width[x]] < data->len[i])
+                               data->width[x] = i;
+               }
+       }
+}
+
+/*
+ * Shrink all columns by shortening them one row each time (and adding
+ * more columns along the way). Hopefully the longest cell will be
+ * moved to the next column, column is shrunk so we have more space
+ * for new columns. The process ends when the whole thing no longer
+ * fits in data->total_width.
+ */
+static void shrink_columns(struct column_data *data)
+{
+       data->width = xrealloc(data->width,
+                              sizeof(*data->width) * data->cols);
+       while (data->rows > 1) {
+               int x, total_width, cols, rows;
+               rows = data->rows;
+               cols = data->cols;
+
+               data->rows--;
+               data->cols = DIV_ROUND_UP(data->list->nr, data->rows);
+               if (data->cols != cols)
+                       data->width = xrealloc(data->width,
+                                              sizeof(*data->width) * data->cols);
+               compute_column_width(data);
+
+               total_width = strlen(data->opts.indent);
+               for (x = 0; x < data->cols; x++) {
+                       total_width += data->len[data->width[x]];
+                       total_width += data->opts.padding;
+               }
+               if (total_width > data->opts.width) {
+                       data->rows = rows;
+                       data->cols = cols;
+                       break;
+               }
+       }
+       compute_column_width(data);
+}
+
+/* Display without layout when not enabled */
+static void display_plain(const struct string_list *list,
+                         const char *indent, const char *nl)
+{
+       int i;
+
+       for (i = 0; i < list->nr; i++)
+               printf("%s%s%s", indent, list->items[i].string, nl);
+}
+
+/* Print a cell to stdout with all necessary leading/traling space */
+static int display_cell(struct column_data *data, int initial_width,
+                       const char *empty_cell, int x, int y)
+{
+       int i, len, newline;
+
+       i = XY2LINEAR(data, x, y);
+       if (i >= data->list->nr)
+               return -1;
+
+       len = data->len[i];
+       if (data->width && data->len[data->width[x]] < initial_width) {
+               /*
+                * empty_cell has initial_width chars, if real column
+                * is narrower, increase len a bit so we fill less
+                * space.
+                */
+               len += initial_width - data->len[data->width[x]];
+               len -= data->opts.padding;
+       }
+
+       if (COL_LAYOUT(data->colopts) == COL_COLUMN)
+               newline = i + data->rows >= data->list->nr;
+       else
+               newline = x == data->cols - 1 || i == data->list->nr - 1;
+
+       printf("%s%s%s",
+              x == 0 ? data->opts.indent : "",
+              data->list->items[i].string,
+              newline ? data->opts.nl : empty_cell + len);
+       return 0;
+}
+
+/* Display COL_COLUMN or COL_ROW */
+static void display_table(const struct string_list *list,
+                         unsigned int colopts,
+                         const struct column_options *opts)
+{
+       struct column_data data;
+       int x, y, i, initial_width;
+       char *empty_cell;
+
+       memset(&data, 0, sizeof(data));
+       data.list = list;
+       data.colopts = colopts;
+       data.opts = *opts;
+
+       data.len = xmalloc(sizeof(*data.len) * list->nr);
+       for (i = 0; i < list->nr; i++)
+               data.len[i] = item_length(colopts, list->items[i].string);
+
+       layout(&data, &initial_width);
+
+       if (colopts & COL_DENSE)
+               shrink_columns(&data);
+
+       empty_cell = xmalloc(initial_width + 1);
+       memset(empty_cell, ' ', initial_width);
+       empty_cell[initial_width] = '\0';
+       for (y = 0; y < data.rows; y++) {
+               for (x = 0; x < data.cols; x++)
+                       if (display_cell(&data, initial_width, empty_cell, x, y))
+                               break;
+       }
+
+       free(data.len);
+       free(data.width);
+       free(empty_cell);
+}
+
+void print_columns(const struct string_list *list, unsigned int colopts,
+                  const struct column_options *opts)
+{
+       struct column_options nopts;
+
+       if (!list->nr)
+               return;
+       assert((colopts & COL_ENABLE_MASK) != COL_AUTO);
+
+       memset(&nopts, 0, sizeof(nopts));
+       nopts.indent = opts && opts->indent ? opts->indent : "";
+       nopts.nl = opts && opts->nl ? opts->nl : "\n";
+       nopts.padding = opts ? opts->padding : 1;
+       nopts.width = opts && opts->width ? opts->width : term_columns() - 1;
+       if (!column_active(colopts)) {
+               display_plain(list, "", "\n");
+               return;
+       }
+       switch (COL_LAYOUT(colopts)) {
+       case COL_PLAIN:
+               display_plain(list, nopts.indent, nopts.nl);
+               break;
+       case COL_ROW:
+       case COL_COLUMN:
+               display_table(list, colopts, &nopts);
+               break;
+       default:
+               die("BUG: invalid layout mode %d", COL_LAYOUT(colopts));
+       }
+}
+
+int finalize_colopts(unsigned int *colopts, int stdout_is_tty)
+{
+       if ((*colopts & COL_ENABLE_MASK) == COL_AUTO) {
+               if (stdout_is_tty < 0)
+                       stdout_is_tty = isatty(1);
+               *colopts &= ~COL_ENABLE_MASK;
+               if (stdout_is_tty)
+                       *colopts |= COL_ENABLED;
+       }
+       return 0;
+}
+
+struct colopt {
+       const char *name;
+       unsigned int value;
+       unsigned int mask;
+};
+
+#define LAYOUT_SET 1
+#define ENABLE_SET 2
+
+static int parse_option(const char *arg, int len, unsigned int *colopts,
+                       int *group_set)
+{
+       struct colopt opts[] = {
+               { "always", COL_ENABLED,  COL_ENABLE_MASK },
+               { "never",  COL_DISABLED, COL_ENABLE_MASK },
+               { "auto",   COL_AUTO,     COL_ENABLE_MASK },
+               { "plain",  COL_PLAIN,    COL_LAYOUT_MASK },
+               { "column", COL_COLUMN,   COL_LAYOUT_MASK },
+               { "row",    COL_ROW,      COL_LAYOUT_MASK },
+               { "dense",  COL_DENSE,    0 },
+       };
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(opts); i++) {
+               int set = 1, arg_len = len, name_len;
+               const char *arg_str = arg;
+
+               if (!opts[i].mask) {
+                       if (arg_len > 2 && !strncmp(arg_str, "no", 2)) {
+                               arg_str += 2;
+                               arg_len -= 2;
+                               set = 0;
+                       }
+               }
+
+               name_len = strlen(opts[i].name);
+               if (arg_len != name_len ||
+                   strncmp(arg_str, opts[i].name, name_len))
+                       continue;
+
+               switch (opts[i].mask) {
+               case COL_ENABLE_MASK:
+                       *group_set |= ENABLE_SET;
+                       break;
+               case COL_LAYOUT_MASK:
+                       *group_set |= LAYOUT_SET;
+                       break;
+               }
+
+               if (opts[i].mask)
+                       *colopts = (*colopts & ~opts[i].mask) | opts[i].value;
+               else {
+                       if (set)
+                               *colopts |= opts[i].value;
+                       else
+                               *colopts &= ~opts[i].value;
+               }
+               return 0;
+       }
+
+       return error("unsupported option '%s'", arg);
+}
+
+static int parse_config(unsigned int *colopts, const char *value)
+{
+       const char *sep = " ,";
+       int group_set = 0;
+
+       while (*value) {
+               int len = strcspn(value, sep);
+               if (len) {
+                       if (parse_option(value, len, colopts, &group_set))
+                               return -1;
+
+                       value += len;
+               }
+               value += strspn(value, sep);
+       }
+       /*
+        * Setting layout implies "always" if neither always, never
+        * nor auto is specified.
+        *
+        * Current value in COL_ENABLE_MASK is disregarded. This means if
+        * you set column.ui = auto and pass --column=row, then "auto"
+        * will become "always".
+        */
+       if ((group_set & LAYOUT_SET) && !(group_set & ENABLE_SET))
+               *colopts = (*colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
+       return 0;
+}
+
+static int column_config(const char *var, const char *value,
+                        const char *key, unsigned int *colopts)
+{
+       if (!value)
+               return config_error_nonbool(var);
+       if (parse_config(colopts, value))
+               return error("invalid column.%s mode %s", key, value);
+       return 0;
+}
+
+int git_column_config(const char *var, const char *value,
+                     const char *command, unsigned int *colopts)
+{
+       const char *it = skip_prefix(var, "column.");
+       if (!it)
+               return 0;
+
+       if (!strcmp(it, "ui"))
+               return column_config(var, value, "ui", colopts);
+
+       if (command && !strcmp(it, command))
+               return column_config(var, value, it, colopts);
+
+       return 0;
+}
+
+int parseopt_column_callback(const struct option *opt,
+                            const char *arg, int unset)
+{
+       unsigned int *colopts = opt->value;
+       *colopts |= COL_PARSEOPT;
+       *colopts &= ~COL_ENABLE_MASK;
+       if (unset)              /* --no-column == never */
+               return 0;
+       /* --column == always unless "arg" states otherwise */
+       *colopts |= COL_ENABLED;
+       if (arg)
+               return parse_config(colopts, arg);
+
+       return 0;
+}
+
+static int fd_out = -1;
+static struct child_process column_process;
+
+int run_column_filter(int colopts, const struct column_options *opts)
+{
+       const char *av[10];
+       int ret, ac = 0;
+       struct strbuf sb_colopt  = STRBUF_INIT;
+       struct strbuf sb_width   = STRBUF_INIT;
+       struct strbuf sb_padding = STRBUF_INIT;
+
+       if (fd_out != -1)
+               return -1;
+
+       av[ac++] = "column";
+       strbuf_addf(&sb_colopt, "--raw-mode=%d", colopts);
+       av[ac++] = sb_colopt.buf;
+       if (opts && opts->width) {
+               strbuf_addf(&sb_width, "--width=%d", opts->width);
+               av[ac++] = sb_width.buf;
+       }
+       if (opts && opts->indent) {
+               av[ac++] = "--indent";
+               av[ac++] = opts->indent;
+       }
+       if (opts && opts->padding) {
+               strbuf_addf(&sb_padding, "--padding=%d", opts->padding);
+               av[ac++] = sb_padding.buf;
+       }
+       av[ac] = NULL;
+
+       fflush(stdout);
+       memset(&column_process, 0, sizeof(column_process));
+       column_process.in = -1;
+       column_process.out = dup(1);
+       column_process.git_cmd = 1;
+       column_process.argv = av;
+
+       ret = start_command(&column_process);
+
+       strbuf_release(&sb_colopt);
+       strbuf_release(&sb_width);
+       strbuf_release(&sb_padding);
+
+       if (ret)
+               return -2;
+
+       fd_out = dup(1);
+       close(1);
+       dup2(column_process.in, 1);
+       close(column_process.in);
+       return 0;
+}
+
+int stop_column_filter(void)
+{
+       if (fd_out == -1)
+               return -1;
+
+       fflush(stdout);
+       close(1);
+       finish_command(&column_process);
+       dup2(fd_out, 1);
+       close(fd_out);
+       fd_out = -1;
+       return 0;
+}
diff --git a/column.h b/column.h
new file mode 100644 (file)
index 0000000..0a61917
--- /dev/null
+++ b/column.h
@@ -0,0 +1,45 @@
+#ifndef COLUMN_H
+#define COLUMN_H
+
+#define COL_LAYOUT_MASK   0x000F
+#define COL_ENABLE_MASK   0x0030   /* always, never or auto */
+#define COL_PARSEOPT      0x0040   /* --column is given from cmdline */
+#define COL_DENSE         0x0080   /* Shrink columns when possible,
+                                     making space for more columns */
+
+#define COL_DISABLED      0x0000   /* must be zero */
+#define COL_ENABLED       0x0010
+#define COL_AUTO          0x0020
+
+#define COL_LAYOUT(c) ((c) & COL_LAYOUT_MASK)
+#define COL_COLUMN             0   /* Fill columns before rows */
+#define COL_ROW                1   /* Fill rows before columns */
+#define COL_PLAIN             15   /* one column */
+
+#define explicitly_enable_column(c) \
+       (((c) & COL_PARSEOPT) && column_active(c))
+
+struct column_options {
+       int width;
+       int padding;
+       const char *indent;
+       const char *nl;
+};
+
+struct option;
+extern int parseopt_column_callback(const struct option *, const char *, int);
+extern int git_column_config(const char *var, const char *value,
+                            const char *command, unsigned int *colopts);
+extern int finalize_colopts(unsigned int *colopts, int stdout_is_tty);
+static inline int column_active(unsigned int colopts)
+{
+       return (colopts & COL_ENABLE_MASK) == COL_ENABLED;
+}
+
+extern void print_columns(const struct string_list *list, unsigned int colopts,
+                         const struct column_options *opts);
+
+extern int run_column_filter(int colopts, const struct column_options *);
+extern int stop_column_filter(void);
+
+#endif
index a2e8dcf8553ff15d7cfed8f8ec4735185ec162bb..978668036835e16df4b6bfd37a7b1e9f8494cf07 100644 (file)
@@ -423,7 +423,7 @@ static int make_hunks(struct sline *sline, unsigned long cnt,
                                                     hunk_begin, j);
                                la = (la + context < cnt + 1) ?
                                        (la + context) : cnt + 1;
-                               while (j <= --la) {
+                               while (la && j <= --la) {
                                        if (sline[la].flag & mark) {
                                                contin = 1;
                                                break;
index a36ee9b0150278e8fc4b61e7226df18409b334be..14ea67af038fbfce388dd20b8adcda503106911d 100644 (file)
@@ -20,6 +20,7 @@ git-cherry-pick                         mainporcelain
 git-citool                              mainporcelain
 git-clean                               mainporcelain
 git-clone                               mainporcelain common
+git-column                              purehelpers
 git-commit                              mainporcelain common
 git-commit-tree                         plumbingmanipulators
 git-config                              ancillarymanipulators
@@ -76,6 +77,7 @@ git-mktree                              plumbingmanipulators
 git-mv                                  mainporcelain common
 git-name-rev                            plumbinginterrogators
 git-notes                               mainporcelain
+git-p4                                  foreignscminterface
 git-pack-objects                        plumbingmanipulators
 git-pack-redundant                      plumbinginterrogators
 git-pack-refs                           ancillarymanipulators
index 4b39c19123c7fa8584a67d5fd91e11f89e5816e4..9ed36c7db5ee0d2e820a01b27507c4ab0afe9663 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -7,6 +7,7 @@
 #include "revision.h"
 #include "notes.h"
 #include "gpg-interface.h"
+#include "mergesort.h"
 
 int save_commit_buffer = 1;
 
@@ -390,15 +391,31 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
        return commit_list_insert(item, pp);
 }
 
+static int commit_list_compare_by_date(const void *a, const void *b)
+{
+       unsigned long a_date = ((const struct commit_list *)a)->item->date;
+       unsigned long b_date = ((const struct commit_list *)b)->item->date;
+       if (a_date < b_date)
+               return 1;
+       if (a_date > b_date)
+               return -1;
+       return 0;
+}
+
+static void *commit_list_get_next(const void *a)
+{
+       return ((const struct commit_list *)a)->next;
+}
+
+static void commit_list_set_next(void *a, void *next)
+{
+       ((struct commit_list *)a)->next = next;
+}
 
 void commit_list_sort_by_date(struct commit_list **list)
 {
-       struct commit_list *ret = NULL;
-       while (*list) {
-               commit_list_insert_by_date((*list)->item, &ret);
-               *list = (*list)->next;
-       }
-       *list = ret;
+       *list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
+                               commit_list_compare_by_date);
 }
 
 struct commit *pop_most_recent_commit(struct commit_list **list,
@@ -1182,3 +1199,30 @@ struct commit *get_merge_parent(const char *name)
        }
        return commit;
 }
+
+/*
+ * Append a commit to the end of the commit_list.
+ *
+ * next starts by pointing to the variable that holds the head of an
+ * empty commit_list, and is updated to point to the "next" field of
+ * the last item on the list as new commits are appended.
+ *
+ * Usage example:
+ *
+ *     struct commit_list *list;
+ *     struct commit_list **next = &list;
+ *
+ *     next = commit_list_append(c1, next);
+ *     next = commit_list_append(c2, next);
+ *     assert(commit_list_count(list) == 2);
+ *     return list;
+ */
+struct commit_list **commit_list_append(struct commit *commit,
+                                       struct commit_list **next)
+{
+       struct commit_list *new = xmalloc(sizeof(struct commit_list));
+       new->item = commit;
+       *next = new;
+       new->next = NULL;
+       return &new->next;
+}
index 154c0e34ff7d2dbaddcfb66b74d26697ffba6381..d617fa3f28d9fecf0bbf8e87c373f8701c286227 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -53,6 +53,8 @@ int find_commit_subject(const char *commit_buffer, const char **subject);
 
 struct commit_list *commit_list_insert(struct commit *item,
                                        struct commit_list **list);
+struct commit_list **commit_list_append(struct commit *commit,
+                                       struct commit_list **next);
 unsigned commit_list_count(const struct commit_list *l);
 struct commit_list *commit_list_insert_by_date(struct commit *item,
                                    struct commit_list **list);
@@ -82,6 +84,7 @@ struct pretty_print_context {
        const char *after_subject;
        int preserve_subject;
        enum date_mode date_mode;
+       unsigned date_mode_explicit:1;
        int need_8bit_cte;
        int show_notes;
        struct reflog_walk_info *reflog_info;
index a0ac487c0c12ea0e7c81485b5784e28f2115a2ea..afc892d6b1837d807490f42790bf29686d074a10 100644 (file)
@@ -1003,7 +1003,7 @@ static void mingw_execve(const char *cmd, char *const *argv, char *const *env)
        }
 }
 
-void mingw_execvp(const char *cmd, char *const *argv)
+int mingw_execvp(const char *cmd, char *const *argv)
 {
        char **path = get_path_split();
        char *prog = path_lookup(cmd, path, 0);
@@ -1015,11 +1015,13 @@ void mingw_execvp(const char *cmd, char *const *argv)
                errno = ENOENT;
 
        free_path_split(path);
+       return -1;
 }
 
-void mingw_execv(const char *cmd, char *const *argv)
+int mingw_execv(const char *cmd, char *const *argv)
 {
        mingw_execve(cmd, argv, environ);
+       return -1;
 }
 
 int mingw_kill(pid_t pid, int sig)
index 0ff1e04812ef2491f15b1d40bb8e1c2977b26d98..61a652138ac7b7ef412bbc15e6ce479db158b181 100644 (file)
@@ -22,9 +22,10 @@ typedef int socklen_t;
 #define S_IWOTH 0
 #define S_IXOTH 0
 #define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
-#define S_ISUID 0
-#define S_ISGID 0
-#define S_ISVTX 0
+
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
 
 #define WIFEXITED(x) 1
 #define WIFSIGNALED(x) 0
@@ -274,9 +275,9 @@ int mingw_utime(const char *file_name, const struct utimbuf *times);
 pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
                     const char *dir,
                     int fhin, int fhout, int fherr);
-void mingw_execvp(const char *cmd, char *const *argv);
+int mingw_execvp(const char *cmd, char *const *argv);
 #define execvp mingw_execvp
-void mingw_execv(const char *cmd, char *const *argv);
+int mingw_execv(const char *cmd, char *const *argv);
 #define execv mingw_execv
 
 static inline unsigned int git_ntohl(unsigned int x)
index 2e205485570bf62a11112c665624203207c724a9..8ad187344fd93e894a2f1cbfaa0fb59fd832de5b 100644 (file)
@@ -86,6 +86,11 @@ static inline int pthread_key_create(pthread_key_t *keyp, void (*destructor)(voi
        return (*keyp = TlsAlloc()) == TLS_OUT_OF_INDEXES ? EAGAIN : 0;
 }
 
+static inline int pthread_key_delete(pthread_key_t key)
+{
+       return TlsFree(key) ? 0 : EINVAL;
+}
+
 static inline int pthread_setspecific(pthread_key_t key, const void *value)
 {
        return TlsSetValue(key, (void *)value) ? 0 : EINVAL;
index b58aa69fa0609dad7f591024f9da31dfa58496fb..61d2ef8e46084ef14ea22690a68c6c99e2644455 100644 (file)
@@ -30,7 +30,7 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
        temp = MapViewOfFileEx(hmap, FILE_MAP_COPY, h, l, length, start);
 
        if (!CloseHandle(hmap))
-               warning("unable to close file mapping handle\n");
+               warning("unable to close file mapping handle");
 
        return temp ? temp : MAP_FAILED;
 }
index 68d32940f3f8f1512fa36702beb98c2457ca5d46..eeee986022fc8f235c43a2d45eb942fc0095f8b1 100644 (file)
--- a/config.c
+++ b/config.c
@@ -37,6 +37,11 @@ static int handle_path_include(const char *path, struct config_include_data *inc
 {
        int ret = 0;
        struct strbuf buf = STRBUF_INIT;
+       char *expanded = expand_user_path(path);
+
+       if (!expanded)
+               return error("Could not expand include path '%s'", path);
+       path = expanded;
 
        /*
         * Use an absolute path as-is, but interpret relative paths
@@ -63,6 +68,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
                inc->depth--;
        }
        strbuf_release(&buf);
+       free(expanded);
        return ret;
 }
 
@@ -829,6 +835,8 @@ static int git_default_push_config(const char *var, const char *value)
                        push_default = PUSH_DEFAULT_NOTHING;
                else if (!strcmp(value, "matching"))
                        push_default = PUSH_DEFAULT_MATCHING;
+               else if (!strcmp(value, "simple"))
+                       push_default = PUSH_DEFAULT_SIMPLE;
                else if (!strcmp(value, "upstream"))
                        push_default = PUSH_DEFAULT_UPSTREAM;
                else if (!strcmp(value, "tracking")) /* deprecated */
@@ -837,8 +845,8 @@ static int git_default_push_config(const char *var, const char *value)
                        push_default = PUSH_DEFAULT_CURRENT;
                else {
                        error("Malformed value for %s: %s", var, value);
-                       return error("Must be one of nothing, matching, "
-                                    "tracking or current.");
+                       return error("Must be one of nothing, matching, simple, "
+                                    "upstream or current.");
                }
                return 0;
        }
@@ -1552,20 +1560,42 @@ static int section_name_match (const char *buf, const char *name)
        return 0;
 }
 
+static int section_name_is_ok(const char *name)
+{
+       /* Empty section names are bogus. */
+       if (!*name)
+               return 0;
+
+       /*
+        * Before a dot, we must be alphanumeric or dash. After the first dot,
+        * anything goes, so we can stop checking.
+        */
+       for (; *name && *name != '.'; name++)
+               if (*name != '-' && !isalnum(*name))
+                       return 0;
+       return 1;
+}
+
 /* if new_name == NULL, the section is removed instead */
 int git_config_rename_section_in_file(const char *config_filename,
                                      const char *old_name, const char *new_name)
 {
        int ret = 0, remove = 0;
        char *filename_buf = NULL;
-       struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
+       struct lock_file *lock;
        int out_fd;
        char buf[1024];
        FILE *config_file;
 
+       if (new_name && !section_name_is_ok(new_name)) {
+               ret = error("invalid section name: %s", new_name);
+               goto out;
+       }
+
        if (!config_filename)
                config_filename = filename_buf = git_pathdup("config");
 
+       lock = xcalloc(sizeof(struct lock_file), 1);
        out_fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (out_fd < 0) {
                ret = error("could not lock config file %s", config_filename);
index 72f7958824dad94eb50abf7d99b264ff92b2ae88..e1255506a636722031c58398cb33056c43cffed3 100644 (file)
@@ -1,65 +1,55 @@
 #                                               -*- Autoconf -*-
 # Process this file with autoconf to produce a configure script.
 
-AC_PREREQ(2.59)
-AC_INIT([git], [@@GIT_VERSION@@], [git@vger.kernel.org])
-
-AC_CONFIG_SRCDIR([git.c])
-
-config_file=config.mak.autogen
-config_append=config.mak.append
-config_in=config.mak.in
-
-echo "# ${config_append}.  Generated by configure." > "${config_append}"
-
+## Definitions of private macros.
 
-## Definitions of macros
 # GIT_CONF_APPEND_LINE(LINE)
 # --------------------------
 # Append LINE to file ${config_append}
 AC_DEFUN([GIT_CONF_APPEND_LINE],
-[echo "$1" >> "${config_append}"])# GIT_CONF_APPEND_LINE
-#
+         [echo "$1" >> "${config_append}"])
+
 # GIT_ARG_SET_PATH(PROGRAM)
 # -------------------------
 # Provide --with-PROGRAM=PATH option to set PATH to PROGRAM
 # Optional second argument allows setting NO_PROGRAM=YesPlease if
 # --without-PROGRAM version used.
 AC_DEFUN([GIT_ARG_SET_PATH],
-[AC_ARG_WITH([$1],
- [AS_HELP_STRING([--with-$1=PATH],
-                 [provide PATH to $1])],
- [GIT_CONF_APPEND_PATH($1,$2)],[])
-])# GIT_ARG_SET_PATH
-#
+    [AC_ARG_WITH([$1],
       [AS_HELP_STRING([--with-$1=PATH],
+                        [provide PATH to $1])],
+        [GIT_CONF_APPEND_PATH([$1], [$2])],
+        [])])
+
 # GIT_CONF_APPEND_PATH(PROGRAM)
-# ------------------------------
+# -----------------------------
 # Parse --with-PROGRAM=PATH option to set PROGRAM_PATH=PATH
 # Used by GIT_ARG_SET_PATH(PROGRAM)
 # Optional second argument allows setting NO_PROGRAM=YesPlease if
 # --without-PROGRAM is used.
 AC_DEFUN([GIT_CONF_APPEND_PATH],
-[PROGRAM=m4_toupper($1); \
-if test "$withval" = "no"; then \
-       if test -n "$2"; then \
-               m4_toupper($1)_PATH=$withval; \
-               AC_MSG_NOTICE([Disabling use of ${PROGRAM}]); \
-               GIT_CONF_APPEND_LINE(NO_${PROGRAM}=YesPlease); \
-               GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=); \
-       else \
-               AC_MSG_ERROR([You cannot use git without $1]); \
-       fi; \
-else \
-       if test "$withval" = "yes"; then \
-               AC_MSG_WARN([You should provide path for --with-$1=PATH]); \
-       else \
-               m4_toupper($1)_PATH=$withval; \
-               AC_MSG_NOTICE([Setting m4_toupper($1)_PATH to $withval]); \
-               GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=$withval); \
-       fi; \
-fi; \
-]) # GIT_CONF_APPEND_PATH
-#
+    [m4_pushdef([GIT_UC_PROGRAM], m4_toupper([$1]))dnl
+    PROGRAM=GIT_UC_PROGRAM
+    if test "$withval" = "no"; then
+       if test -n "$2"; then
+               GIT_UC_PROGRAM[]_PATH=$withval
+               AC_MSG_NOTICE([Disabling use of ${PROGRAM}])
+               GIT_CONF_APPEND_LINE(NO_${PROGRAM}=YesPlease)
+               GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=)
+       else
+               AC_MSG_ERROR([You cannot use git without $1])
+       fi
+    else
+       if test "$withval" = "yes"; then
+               AC_MSG_WARN([You should provide path for --with-$1=PATH])
+       else
+               GIT_UC_PROGRAM[]_PATH=$withval
+               AC_MSG_NOTICE([Setting GIT_UC_PROGRAM[]_PATH to $withval])
+               GIT_CONF_APPEND_LINE(${PROGRAM}_PATH=$withval)
+       fi
+    fi
+    m4_popdef([GIT_UC_PROGRAM])])
+
 # GIT_PARSE_WITH(PACKAGE)
 # -----------------------
 # For use in AC_ARG_WITH action-if-found, for packages default ON.
@@ -67,21 +57,22 @@ fi; \
 # * Set PACKAGEDIR=PATH for --with-PACKAGE=PATH
 # * Unset NO_PACKAGE for --with-PACKAGE without ARG
 AC_DEFUN([GIT_PARSE_WITH],
-[PACKAGE=m4_toupper($1); \
-if test "$withval" = "no"; then \
-       m4_toupper(NO_$1)=YesPlease; \
-elif test "$withval" = "yes"; then \
-       m4_toupper(NO_$1)=; \
-else \
-       m4_toupper(NO_$1)=; \
-       m4_toupper($1)DIR=$withval; \
-       AC_MSG_NOTICE([Setting m4_toupper($1)DIR to $withval]); \
-       GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \
-fi \
-])# GIT_PARSE_WITH
-#
+    [m4_pushdef([GIT_UC_PACKAGE], m4_toupper([$1]))dnl
+    PACKAGE=GIT_UC_PACKAGE
+    if test "$withval" = "no"; then
+       NO_[]GIT_UC_PACKAGE=YesPlease
+    elif test "$withval" = "yes"; then
+       NO_[]GIT_UC_PACKAGE=
+    else
+       NO_[]GIT_UC_PACKAGE=
+       GIT_UC_PACKAGE[]DIR=$withval
+       AC_MSG_NOTICE([Setting GIT_UC_PACKAGE[]DIR to $withval])
+       GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval)
+    fi
+    m4_popdef([GIT_UC_PACKAGE])])
+
 # GIT_PARSE_WITH_SET_MAKE_VAR(WITHNAME, VAR, HELP_TEXT)
-# ---------------------
+# -----------------------------------------------------
 # Set VAR to the value specied by --with-WITHNAME.
 # No verification of arguments is performed, but warnings are issued
 # if either 'yes' or 'no' is specified.
@@ -90,33 +81,32 @@ fi \
 AC_DEFUN([GIT_PARSE_WITH_SET_MAKE_VAR],
 [AC_ARG_WITH([$1],
  [AS_HELP_STRING([--with-$1=VALUE], $3)],
- if test -n "$withval"; then \
-  if test "$withval" = "yes" -o "$withval" = "no"; then \
+ if test -n "$withval"; then
+  if test "$withval" = "yes" -o "$withval" = "no"; then
     AC_MSG_WARN([You likely do not want either 'yes' or 'no' as]
-                    [a value for $1 ($2).  Maybe you do...?]); \
-  fi; \
-  \
-  AC_MSG_NOTICE([Setting $2 to $withval]); \
-  GIT_CONF_APPEND_LINE($2=$withval); \
+                    [a value for $1 ($2).  Maybe you do...?])
+  fi
+  AC_MSG_NOTICE([Setting $2 to $withval])
+  GIT_CONF_APPEND_LINE($2=$withval)
  fi)])# GIT_PARSE_WITH_SET_MAKE_VAR
 
-dnl
-dnl GIT_CHECK_FUNC(FUNCTION, IFTRUE, IFFALSE)
-dnl -----------------------------------------
-dnl Similar to AC_CHECK_FUNC, but on systems that do not generate
-dnl warnings for missing prototypes (e.g. FreeBSD when compiling without
-dnl -Wall), it does not work.  By looking for function definition in
-dnl libraries, this problem can be worked around.
+#
+# GIT_CHECK_FUNC(FUNCTION, IFTRUE, IFFALSE)
+# -----------------------------------------
+# Similar to AC_CHECK_FUNC, but on systems that do not generate
+# warnings for missing prototypes (e.g. FreeBSD when compiling without
+# -Wall), it does not work.  By looking for function definition in
+# libraries, this problem can be worked around.
 AC_DEFUN([GIT_CHECK_FUNC],[AC_CHECK_FUNC([$1],[
   AC_SEARCH_LIBS([$1],,
   [$2],[$3])
 ],[$3])])
 
-dnl
-dnl GIT_STASH_FLAGS(BASEPATH_VAR)
-dnl -----------------------------
-dnl Allow for easy stashing of LDFLAGS and CPPFLAGS before running
-dnl tests that may want to take user settings into account.
+#
+# GIT_STASH_FLAGS(BASEPATH_VAR)
+# -----------------------------
+# Allow for easy stashing of LDFLAGS and CPPFLAGS before running
+# tests that may want to take user settings into account.
 AC_DEFUN([GIT_STASH_FLAGS],[
 if test -n "$1"; then
    old_CPPFLAGS="$CPPFLAGS"
@@ -137,6 +127,19 @@ if test -n "$1"; then
 fi
 ])
 
+## Configure body starts here.
+
+AC_PREREQ(2.59)
+AC_INIT([git], [@@GIT_VERSION@@], [git@vger.kernel.org])
+
+AC_CONFIG_SRCDIR([git.c])
+
+config_file=config.mak.autogen
+config_append=config.mak.append
+config_in=config.mak.in
+
+echo "# ${config_append}.  Generated by configure." > "${config_append}"
+
 # Directories holding "saner" versions of common or POSIX binaries.
 AC_ARG_WITH([sane-tool-path],
   [AS_HELP_STRING(
@@ -161,14 +164,13 @@ AC_ARG_WITH([sane-tool-path],
 AC_ARG_WITH([lib],
  [AS_HELP_STRING([--with-lib=ARG],
                  [ARG specifies alternative name for lib directory])],
- [if test "$withval" = "no" || test "$withval" = "yes"; then \
-       AC_MSG_WARN([You should provide name for --with-lib=ARG]); \
-else \
-       lib=$withval; \
-       AC_MSG_NOTICE([Setting lib to '$lib']); \
-       GIT_CONF_APPEND_LINE(lib=$withval); \
-fi; \
-],[])
+ [if test "$withval" = "no" || test "$withval" = "yes"; then
+       AC_MSG_WARN([You should provide name for --with-lib=ARG])
+  else
+       lib=$withval
+       AC_MSG_NOTICE([Setting lib to '$lib'])
+       GIT_CONF_APPEND_LINE(lib=$withval)
+  fi])
 
 if test -z "$lib"; then
    AC_MSG_NOTICE([Setting lib to 'lib' (the default)])
@@ -234,9 +236,9 @@ AC_MSG_NOTICE([CHECKS for site configuration])
 # /foo/bar/include and /foo/bar/lib directories.
 AC_ARG_WITH(openssl,
 AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
-AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),\
-GIT_PARSE_WITH(openssl))
-#
+AS_HELP_STRING([],              [ARG can be prefix for openssl library and headers]),
+GIT_PARSE_WITH([openssl]))
+
 # Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
 # able to use Perl-compatible regular expressions.
 #
@@ -246,17 +248,16 @@ GIT_PARSE_WITH(openssl))
 AC_ARG_WITH(libpcre,
 AS_HELP_STRING([--with-libpcre],[support Perl-compatible regexes (default is NO)])
 AS_HELP_STRING([],           [ARG can be also prefix for libpcre library and headers]),
-if test "$withval" = "no"; then \
-       USE_LIBPCRE=; \
-elif test "$withval" = "yes"; then \
-       USE_LIBPCRE=YesPlease; \
-else
-       USE_LIBPCRE=YesPlease; \
-       LIBPCREDIR=$withval; \
-       AC_MSG_NOTICE([Setting LIBPCREDIR to $withval]); \
-       GIT_CONF_APPEND_LINE(LIBPCREDIR=$withval); \
-fi \
-)
+    if test "$withval" = "no"; then
+       USE_LIBPCRE=
+    elif test "$withval" = "yes"; then
+       USE_LIBPCRE=YesPlease
+    else
+       USE_LIBPCRE=YesPlease
+       LIBPCREDIR=$withval
+       AC_MSG_NOTICE([Setting LIBPCREDIR to $withval])
+       GIT_CONF_APPEND_LINE(LIBPCREDIR=$withval)
+    fi)
 #
 # Define NO_CURL if you do not have curl installed.  git-http-pull and
 # git-http-push are not built, and you cannot use http:// and https://
@@ -364,7 +365,7 @@ AC_ARG_WITH(tcltk,
 AS_HELP_STRING([--with-tcltk],[use Tcl/Tk GUI (default is YES)])
 AS_HELP_STRING([],[ARG is the full path to the Tcl/Tk interpreter.])
 AS_HELP_STRING([],[Bare --with-tcltk will make the GUI part only if])
-AS_HELP_STRING([],[Tcl/Tk interpreter will be found in a system.]),\
+AS_HELP_STRING([],[Tcl/Tk interpreter will be found in a system.]),
 GIT_PARSE_WITH(tcltk))
 #
 
index 31f714da927511fb5b2b7ac11dd7af1e3f98ae76..cd92322161a89a23d3f494a7c7b7aaf5867a5781 100755 (executable)
@@ -304,16 +304,16 @@ __git_ps1 ()
        fi
 }
 
-# __gitcomp_1 requires 2 arguments
 __gitcomp_1 ()
 {
-       local c IFS=' '$'\t'$'\n'
+       local c IFS=$' \t\n'
        for c in $1; do
-               case "$c$2" in
-               --*=*) printf %s$'\n' "$c$2" ;;
-               *.)    printf %s$'\n' "$c$2" ;;
-               *)     printf %s$'\n' "$c$2 " ;;
+               c="$c$2"
+               case $c in
+               --*=*|*.) ;;
+               *) c="$c " ;;
                esac
+               printf '%s\n' "$c"
        done
 }
 
@@ -676,9 +676,7 @@ __git_complete_revlist_file ()
                *)   pfx="$ref:$pfx" ;;
                esac
 
-               local IFS=$'\n'
-               COMPREPLY=($(compgen -P "$pfx" \
-                       -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
+               __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
                                | sed '/^100... blob /{
                                           s,^.*        ,,
                                           s,$, ,
@@ -692,7 +690,7 @@ __git_complete_revlist_file ()
                                           s,$,/,
                                       }
                                       s/^.*    //')" \
-                       -- "$cur_"))
+                       "$pfx" "$cur_" ""
                ;;
        *...*)
                pfx="${cur_%...*}..."
@@ -1658,7 +1656,7 @@ _git_notes ()
                __gitcomp '--ref'
                ;;
        ,*)
-               case "${words[cword-1]}" in
+               case "$prev" in
                --ref)
                        __gitcomp_nl "$(__git_refs)"
                        ;;
@@ -1684,7 +1682,7 @@ _git_notes ()
        prune,*)
                ;;
        *)
-               case "${words[cword-1]}" in
+               case "$prev" in
                -m|-F)
                        ;;
                *)
@@ -2603,28 +2601,14 @@ _git ()
 {
        local i c=1 command __git_dir
 
-       if [[ -n ${ZSH_VERSION-} ]]; then
-               emulate -L bash
-               setopt KSH_TYPESET
-
-               # workaround zsh's bug that leaves 'words' as a special
-               # variable in versions < 4.3.12
-               typeset -h words
-
-               # workaround zsh's bug that quotes spaces in the COMPREPLY
-               # array if IFS doesn't contain spaces.
-               typeset -h IFS
-       fi
-
-       local cur words cword prev
-       _get_comp_words_by_ref -n =: cur words cword prev
        while [ $c -lt $cword ]; do
                i="${words[c]}"
                case "$i" in
                --git-dir=*) __git_dir="${i#--git-dir=}" ;;
                --bare)      __git_dir="." ;;
-               --version|-p|--paginate) ;;
                --help) command="help"; break ;;
+               -c) c=$((++c)) ;;
+               -*) ;;
                *) command="$i"; break ;;
                esac
                ((c++))
@@ -2639,9 +2623,12 @@ _git ()
                        --bare
                        --version
                        --exec-path
+                       --exec-path=
                        --html-path
+                       --info-path
                        --work-tree=
                        --namespace=
+                       --no-replace-objects
                        --help
                        "
                        ;;
@@ -2663,22 +2650,6 @@ _git ()
 
 _gitk ()
 {
-       if [[ -n ${ZSH_VERSION-} ]]; then
-               emulate -L bash
-               setopt KSH_TYPESET
-
-               # workaround zsh's bug that leaves 'words' as a special
-               # variable in versions < 4.3.12
-               typeset -h words
-
-               # workaround zsh's bug that quotes spaces in the COMPREPLY
-               # array if IFS doesn't contain spaces.
-               typeset -h IFS
-       fi
-
-       local cur words cword prev
-       _get_comp_words_by_ref -n =: cur words cword prev
-
        __git_has_doubledash && return
 
        local g="$(__gitdir)"
@@ -2699,16 +2670,43 @@ _gitk ()
        __git_complete_revlist
 }
 
-complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
-       || complete -o default -o nospace -F _git git
-complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
-       || complete -o default -o nospace -F _gitk gitk
+__git_func_wrap ()
+{
+       if [[ -n ${ZSH_VERSION-} ]]; then
+               emulate -L bash
+               setopt KSH_TYPESET
+
+               # workaround zsh's bug that leaves 'words' as a special
+               # variable in versions < 4.3.12
+               typeset -h words
+
+               # workaround zsh's bug that quotes spaces in the COMPREPLY
+               # array if IFS doesn't contain spaces.
+               typeset -h IFS
+       fi
+       local cur words cword prev
+       _get_comp_words_by_ref -n =: cur words cword prev
+       $1
+}
+
+# Setup completion for certain functions defined above by setting common
+# variables and workarounds.
+# This is NOT a public function; use at your own risk.
+__git_complete ()
+{
+       local wrapper="__git_wrap${2}"
+       eval "$wrapper () { __git_func_wrap $2 ; }"
+       complete -o bashdefault -o default -o nospace -F $wrapper $1 2>/dev/null \
+               || complete -o default -o nospace -F $wrapper $1
+}
+
+__git_complete git _git
+__git_complete gitk _gitk
 
 # The following are necessary only for Cygwin, and only are needed
 # when the user has tab-completed the executable name and consequently
 # included the '.exe' suffix.
 #
 if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
-complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
-       || complete -o default -o nospace -F _git git.exe
+__git_complete git.exe _git
 fi
index 75c07f8be4ae6ece8adb5c7190e4de38891f8787..4b3a08a2bac441f3841367d98f425b9b6a5a3598 100644 (file)
@@ -2,10 +2,13 @@ all:: git-credential-osxkeychain
 
 CC = gcc
 RM = rm -f
-CFLAGS = -g -Wall
+CFLAGS = -g -O2 -Wall
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
 
 git-credential-osxkeychain: git-credential-osxkeychain.o
-       $(CC) -o $@ $< -Wl,-framework -Wl,Security
+       $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) -Wl,-framework -Wl,Security
 
 git-credential-osxkeychain.o: git-credential-osxkeychain.c
        $(CC) -c $(CFLAGS) $<
index 3140e405fa130a0fa20b2f6ee4171da70935de66..0d54aa7061e780dd0000b8c2a48e266ad5b8ce53 100644 (file)
@@ -518,7 +518,7 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
                filename = git_path("FETCH_HEAD");
                fp = fopen(filename, "a");
                if (!fp)
-                       return error("cannot open %s: %s\n", filename, strerror(errno));
+                       return error("cannot open %s: %s", filename, strerror(errno));
                result = append_fetch_head(fp, argv[2], argv[3],
                                           argv[4], argv[5],
                                           argv[6], !!argv[7][0],
@@ -536,7 +536,7 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
                filename = git_path("FETCH_HEAD");
                fp = fopen(filename, "a");
                if (!fp)
-                       return error("cannot open %s: %s\n", filename, strerror(errno));
+                       return error("cannot open %s: %s", filename, strerror(errno));
                result = fetch_native_store(fp, argv[2], argv[3], argv[4],
                                            verbose, force);
                fclose(fp);
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
deleted file mode 100755 (executable)
index c5362c4..0000000
+++ /dev/null
@@ -1,2758 +0,0 @@
-#!/usr/bin/env python
-#
-# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
-#
-# Author: Simon Hausmann <simon@lst.de>
-# Copyright: 2007 Simon Hausmann <simon@lst.de>
-#            2007 Trolltech ASA
-# License: MIT <http://www.opensource.org/licenses/mit-license.php>
-#
-
-import optparse, sys, os, marshal, subprocess, shelve
-import tempfile, getopt, os.path, time, platform
-import re, shutil
-
-verbose = False
-
-
-def p4_build_cmd(cmd):
-    """Build a suitable p4 command line.
-
-    This consolidates building and returning a p4 command line into one
-    location. It means that hooking into the environment, or other configuration
-    can be done more easily.
-    """
-    real_cmd = ["p4"]
-
-    user = gitConfig("git-p4.user")
-    if len(user) > 0:
-        real_cmd += ["-u",user]
-
-    password = gitConfig("git-p4.password")
-    if len(password) > 0:
-        real_cmd += ["-P", password]
-
-    port = gitConfig("git-p4.port")
-    if len(port) > 0:
-        real_cmd += ["-p", port]
-
-    host = gitConfig("git-p4.host")
-    if len(host) > 0:
-        real_cmd += ["-H", host]
-
-    client = gitConfig("git-p4.client")
-    if len(client) > 0:
-        real_cmd += ["-c", client]
-
-
-    if isinstance(cmd,basestring):
-        real_cmd = ' '.join(real_cmd) + ' ' + cmd
-    else:
-        real_cmd += cmd
-    return real_cmd
-
-def chdir(dir):
-    # P4 uses the PWD environment variable rather than getcwd(). Since we're
-    # not using the shell, we have to set it ourselves.  This path could
-    # be relative, so go there first, then figure out where we ended up.
-    os.chdir(dir)
-    os.environ['PWD'] = os.getcwd()
-
-def die(msg):
-    if verbose:
-        raise Exception(msg)
-    else:
-        sys.stderr.write(msg + "\n")
-        sys.exit(1)
-
-def write_pipe(c, stdin):
-    if verbose:
-        sys.stderr.write('Writing pipe: %s\n' % str(c))
-
-    expand = isinstance(c,basestring)
-    p = subprocess.Popen(c, stdin=subprocess.PIPE, shell=expand)
-    pipe = p.stdin
-    val = pipe.write(stdin)
-    pipe.close()
-    if p.wait():
-        die('Command failed: %s' % str(c))
-
-    return val
-
-def p4_write_pipe(c, stdin):
-    real_cmd = p4_build_cmd(c)
-    return write_pipe(real_cmd, stdin)
-
-def read_pipe(c, ignore_error=False):
-    if verbose:
-        sys.stderr.write('Reading pipe: %s\n' % str(c))
-
-    expand = isinstance(c,basestring)
-    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
-    pipe = p.stdout
-    val = pipe.read()
-    if p.wait() and not ignore_error:
-        die('Command failed: %s' % str(c))
-
-    return val
-
-def p4_read_pipe(c, ignore_error=False):
-    real_cmd = p4_build_cmd(c)
-    return read_pipe(real_cmd, ignore_error)
-
-def read_pipe_lines(c):
-    if verbose:
-        sys.stderr.write('Reading pipe: %s\n' % str(c))
-
-    expand = isinstance(c, basestring)
-    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
-    pipe = p.stdout
-    val = pipe.readlines()
-    if pipe.close() or p.wait():
-        die('Command failed: %s' % str(c))
-
-    return val
-
-def p4_read_pipe_lines(c):
-    """Specifically invoke p4 on the command supplied. """
-    real_cmd = p4_build_cmd(c)
-    return read_pipe_lines(real_cmd)
-
-def system(cmd):
-    expand = isinstance(cmd,basestring)
-    if verbose:
-        sys.stderr.write("executing %s\n" % str(cmd))
-    subprocess.check_call(cmd, shell=expand)
-
-def p4_system(cmd):
-    """Specifically invoke p4 as the system command. """
-    real_cmd = p4_build_cmd(cmd)
-    expand = isinstance(real_cmd, basestring)
-    subprocess.check_call(real_cmd, shell=expand)
-
-def p4_integrate(src, dest):
-    p4_system(["integrate", "-Dt", src, dest])
-
-def p4_sync(path):
-    p4_system(["sync", path])
-
-def p4_add(f):
-    p4_system(["add", f])
-
-def p4_delete(f):
-    p4_system(["delete", f])
-
-def p4_edit(f):
-    p4_system(["edit", f])
-
-def p4_revert(f):
-    p4_system(["revert", f])
-
-def p4_reopen(type, file):
-    p4_system(["reopen", "-t", type, file])
-
-#
-# Canonicalize the p4 type and return a tuple of the
-# base type, plus any modifiers.  See "p4 help filetypes"
-# for a list and explanation.
-#
-def split_p4_type(p4type):
-
-    p4_filetypes_historical = {
-        "ctempobj": "binary+Sw",
-        "ctext": "text+C",
-        "cxtext": "text+Cx",
-        "ktext": "text+k",
-        "kxtext": "text+kx",
-        "ltext": "text+F",
-        "tempobj": "binary+FSw",
-        "ubinary": "binary+F",
-        "uresource": "resource+F",
-        "uxbinary": "binary+Fx",
-        "xbinary": "binary+x",
-        "xltext": "text+Fx",
-        "xtempobj": "binary+Swx",
-        "xtext": "text+x",
-        "xunicode": "unicode+x",
-        "xutf16": "utf16+x",
-    }
-    if p4type in p4_filetypes_historical:
-        p4type = p4_filetypes_historical[p4type]
-    mods = ""
-    s = p4type.split("+")
-    base = s[0]
-    mods = ""
-    if len(s) > 1:
-        mods = s[1]
-    return (base, mods)
-
-#
-# return the raw p4 type of a file (text, text+ko, etc)
-#
-def p4_type(file):
-    results = p4CmdList(["fstat", "-T", "headType", file])
-    return results[0]['headType']
-
-#
-# Given a type base and modifier, return a regexp matching
-# the keywords that can be expanded in the file
-#
-def p4_keywords_regexp_for_type(base, type_mods):
-    if base in ("text", "unicode", "binary"):
-        kwords = None
-        if "ko" in type_mods:
-            kwords = 'Id|Header'
-        elif "k" in type_mods:
-            kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision'
-        else:
-            return None
-        pattern = r"""
-            \$              # Starts with a dollar, followed by...
-            (%s)            # one of the keywords, followed by...
-            (:[^$]+)?       # possibly an old expansion, followed by...
-            \$              # another dollar
-            """ % kwords
-        return pattern
-    else:
-        return None
-
-#
-# Given a file, return a regexp matching the possible
-# RCS keywords that will be expanded, or None for files
-# with kw expansion turned off.
-#
-def p4_keywords_regexp_for_file(file):
-    if not os.path.exists(file):
-        return None
-    else:
-        (type_base, type_mods) = split_p4_type(p4_type(file))
-        return p4_keywords_regexp_for_type(type_base, type_mods)
-
-def setP4ExecBit(file, mode):
-    # Reopens an already open file and changes the execute bit to match
-    # the execute bit setting in the passed in mode.
-
-    p4Type = "+x"
-
-    if not isModeExec(mode):
-        p4Type = getP4OpenedType(file)
-        p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
-        p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
-        if p4Type[-1] == "+":
-            p4Type = p4Type[0:-1]
-
-    p4_reopen(p4Type, file)
-
-def getP4OpenedType(file):
-    # Returns the perforce file type for the given file.
-
-    result = p4_read_pipe(["opened", file])
-    match = re.match(".*\((.+)\)\r?$", result)
-    if match:
-        return match.group(1)
-    else:
-        die("Could not determine file type for %s (result: '%s')" % (file, result))
-
-def diffTreePattern():
-    # This is a simple generator for the diff tree regex pattern. This could be
-    # a class variable if this and parseDiffTreeEntry were a part of a class.
-    pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
-    while True:
-        yield pattern
-
-def parseDiffTreeEntry(entry):
-    """Parses a single diff tree entry into its component elements.
-
-    See git-diff-tree(1) manpage for details about the format of the diff
-    output. This method returns a dictionary with the following elements:
-
-    src_mode - The mode of the source file
-    dst_mode - The mode of the destination file
-    src_sha1 - The sha1 for the source file
-    dst_sha1 - The sha1 fr the destination file
-    status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc)
-    status_score - The score for the status (applicable for 'C' and 'R'
-                   statuses). This is None if there is no score.
-    src - The path for the source file.
-    dst - The path for the destination file. This is only present for
-          copy or renames. If it is not present, this is None.
-
-    If the pattern is not matched, None is returned."""
-
-    match = diffTreePattern().next().match(entry)
-    if match:
-        return {
-            'src_mode': match.group(1),
-            'dst_mode': match.group(2),
-            'src_sha1': match.group(3),
-            'dst_sha1': match.group(4),
-            'status': match.group(5),
-            'status_score': match.group(6),
-            'src': match.group(7),
-            'dst': match.group(10)
-        }
-    return None
-
-def isModeExec(mode):
-    # Returns True if the given git mode represents an executable file,
-    # otherwise False.
-    return mode[-3:] == "755"
-
-def isModeExecChanged(src_mode, dst_mode):
-    return isModeExec(src_mode) != isModeExec(dst_mode)
-
-def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
-
-    if isinstance(cmd,basestring):
-        cmd = "-G " + cmd
-        expand = True
-    else:
-        cmd = ["-G"] + cmd
-        expand = False
-
-    cmd = p4_build_cmd(cmd)
-    if verbose:
-        sys.stderr.write("Opening pipe: %s\n" % str(cmd))
-
-    # Use a temporary file to avoid deadlocks without
-    # subprocess.communicate(), which would put another copy
-    # of stdout into memory.
-    stdin_file = None
-    if stdin is not None:
-        stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
-        if isinstance(stdin,basestring):
-            stdin_file.write(stdin)
-        else:
-            for i in stdin:
-                stdin_file.write(i + '\n')
-        stdin_file.flush()
-        stdin_file.seek(0)
-
-    p4 = subprocess.Popen(cmd,
-                          shell=expand,
-                          stdin=stdin_file,
-                          stdout=subprocess.PIPE)
-
-    result = []
-    try:
-        while True:
-            entry = marshal.load(p4.stdout)
-            if cb is not None:
-                cb(entry)
-            else:
-                result.append(entry)
-    except EOFError:
-        pass
-    exitCode = p4.wait()
-    if exitCode != 0:
-        entry = {}
-        entry["p4ExitCode"] = exitCode
-        result.append(entry)
-
-    return result
-
-def p4Cmd(cmd):
-    list = p4CmdList(cmd)
-    result = {}
-    for entry in list:
-        result.update(entry)
-    return result;
-
-def p4Where(depotPath):
-    if not depotPath.endswith("/"):
-        depotPath += "/"
-    depotPath = depotPath + "..."
-    outputList = p4CmdList(["where", depotPath])
-    output = None
-    for entry in outputList:
-        if "depotFile" in entry:
-            if entry["depotFile"] == depotPath:
-                output = entry
-                break
-        elif "data" in entry:
-            data = entry.get("data")
-            space = data.find(" ")
-            if data[:space] == depotPath:
-                output = entry
-                break
-    if output == None:
-        return ""
-    if output["code"] == "error":
-        return ""
-    clientPath = ""
-    if "path" in output:
-        clientPath = output.get("path")
-    elif "data" in output:
-        data = output.get("data")
-        lastSpace = data.rfind(" ")
-        clientPath = data[lastSpace + 1:]
-
-    if clientPath.endswith("..."):
-        clientPath = clientPath[:-3]
-    return clientPath
-
-def currentGitBranch():
-    return read_pipe("git name-rev HEAD").split(" ")[1].strip()
-
-def isValidGitDir(path):
-    if (os.path.exists(path + "/HEAD")
-        and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
-        return True;
-    return False
-
-def parseRevision(ref):
-    return read_pipe("git rev-parse %s" % ref).strip()
-
-def branchExists(ref):
-    rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref],
-                     ignore_error=True)
-    return len(rev) > 0
-
-def extractLogMessageFromGitCommit(commit):
-    logMessage = ""
-
-    ## fixme: title is first line of commit, not 1st paragraph.
-    foundTitle = False
-    for log in read_pipe_lines("git cat-file commit %s" % commit):
-       if not foundTitle:
-           if len(log) == 1:
-               foundTitle = True
-           continue
-
-       logMessage += log
-    return logMessage
-
-def extractSettingsGitLog(log):
-    values = {}
-    for line in log.split("\n"):
-        line = line.strip()
-        m = re.search (r"^ *\[git-p4: (.*)\]$", line)
-        if not m:
-            continue
-
-        assignments = m.group(1).split (':')
-        for a in assignments:
-            vals = a.split ('=')
-            key = vals[0].strip()
-            val = ('='.join (vals[1:])).strip()
-            if val.endswith ('\"') and val.startswith('"'):
-                val = val[1:-1]
-
-            values[key] = val
-
-    paths = values.get("depot-paths")
-    if not paths:
-        paths = values.get("depot-path")
-    if paths:
-        values['depot-paths'] = paths.split(',')
-    return values
-
-def gitBranchExists(branch):
-    proc = subprocess.Popen(["git", "rev-parse", branch],
-                            stderr=subprocess.PIPE, stdout=subprocess.PIPE);
-    return proc.wait() == 0;
-
-_gitConfig = {}
-def gitConfig(key, args = None): # set args to "--bool", for instance
-    if not _gitConfig.has_key(key):
-        argsFilter = ""
-        if args != None:
-            argsFilter = "%s " % args
-        cmd = "git config %s%s" % (argsFilter, key)
-        _gitConfig[key] = read_pipe(cmd, ignore_error=True).strip()
-    return _gitConfig[key]
-
-def gitConfigList(key):
-    if not _gitConfig.has_key(key):
-        _gitConfig[key] = read_pipe("git config --get-all %s" % key, ignore_error=True).strip().split(os.linesep)
-    return _gitConfig[key]
-
-def p4BranchesInGit(branchesAreInRemotes = True):
-    branches = {}
-
-    cmdline = "git rev-parse --symbolic "
-    if branchesAreInRemotes:
-        cmdline += " --remotes"
-    else:
-        cmdline += " --branches"
-
-    for line in read_pipe_lines(cmdline):
-        line = line.strip()
-
-        ## only import to p4/
-        if not line.startswith('p4/') or line == "p4/HEAD":
-            continue
-        branch = line
-
-        # strip off p4
-        branch = re.sub ("^p4/", "", line)
-
-        branches[branch] = parseRevision(line)
-    return branches
-
-def findUpstreamBranchPoint(head = "HEAD"):
-    branches = p4BranchesInGit()
-    # map from depot-path to branch name
-    branchByDepotPath = {}
-    for branch in branches.keys():
-        tip = branches[branch]
-        log = extractLogMessageFromGitCommit(tip)
-        settings = extractSettingsGitLog(log)
-        if settings.has_key("depot-paths"):
-            paths = ",".join(settings["depot-paths"])
-            branchByDepotPath[paths] = "remotes/p4/" + branch
-
-    settings = None
-    parent = 0
-    while parent < 65535:
-        commit = head + "~%s" % parent
-        log = extractLogMessageFromGitCommit(commit)
-        settings = extractSettingsGitLog(log)
-        if settings.has_key("depot-paths"):
-            paths = ",".join(settings["depot-paths"])
-            if branchByDepotPath.has_key(paths):
-                return [branchByDepotPath[paths], settings]
-
-        parent = parent + 1
-
-    return ["", settings]
-
-def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
-    if not silent:
-        print ("Creating/updating branch(es) in %s based on origin branch(es)"
-               % localRefPrefix)
-
-    originPrefix = "origin/p4/"
-
-    for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
-        line = line.strip()
-        if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
-            continue
-
-        headName = line[len(originPrefix):]
-        remoteHead = localRefPrefix + headName
-        originHead = line
-
-        original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
-        if (not original.has_key('depot-paths')
-            or not original.has_key('change')):
-            continue
-
-        update = False
-        if not gitBranchExists(remoteHead):
-            if verbose:
-                print "creating %s" % remoteHead
-            update = True
-        else:
-            settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
-            if settings.has_key('change') > 0:
-                if settings['depot-paths'] == original['depot-paths']:
-                    originP4Change = int(original['change'])
-                    p4Change = int(settings['change'])
-                    if originP4Change > p4Change:
-                        print ("%s (%s) is newer than %s (%s). "
-                               "Updating p4 branch from origin."
-                               % (originHead, originP4Change,
-                                  remoteHead, p4Change))
-                        update = True
-                else:
-                    print ("Ignoring: %s was imported from %s while "
-                           "%s was imported from %s"
-                           % (originHead, ','.join(original['depot-paths']),
-                              remoteHead, ','.join(settings['depot-paths'])))
-
-        if update:
-            system("git update-ref %s %s" % (remoteHead, originHead))
-
-def originP4BranchesExist():
-        return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
-
-def p4ChangesForPaths(depotPaths, changeRange):
-    assert depotPaths
-    cmd = ['changes']
-    for p in depotPaths:
-        cmd += ["%s...%s" % (p, changeRange)]
-    output = p4_read_pipe_lines(cmd)
-
-    changes = {}
-    for line in output:
-        changeNum = int(line.split(" ")[1])
-        changes[changeNum] = True
-
-    changelist = changes.keys()
-    changelist.sort()
-    return changelist
-
-def p4PathStartsWith(path, prefix):
-    # This method tries to remedy a potential mixed-case issue:
-    #
-    # If UserA adds  //depot/DirA/file1
-    # and UserB adds //depot/dira/file2
-    #
-    # we may or may not have a problem. If you have core.ignorecase=true,
-    # we treat DirA and dira as the same directory
-    ignorecase = gitConfig("core.ignorecase", "--bool") == "true"
-    if ignorecase:
-        return path.lower().startswith(prefix.lower())
-    return path.startswith(prefix)
-
-def getClientSpec():
-    """Look at the p4 client spec, create a View() object that contains
-       all the mappings, and return it."""
-
-    specList = p4CmdList("client -o")
-    if len(specList) != 1:
-        die('Output from "client -o" is %d lines, expecting 1' %
-            len(specList))
-
-    # dictionary of all client parameters
-    entry = specList[0]
-
-    # just the keys that start with "View"
-    view_keys = [ k for k in entry.keys() if k.startswith("View") ]
-
-    # hold this new View
-    view = View()
-
-    # append the lines, in order, to the view
-    for view_num in range(len(view_keys)):
-        k = "View%d" % view_num
-        if k not in view_keys:
-            die("Expected view key %s missing" % k)
-        view.append(entry[k])
-
-    return view
-
-def getClientRoot():
-    """Grab the client directory."""
-
-    output = p4CmdList("client -o")
-    if len(output) != 1:
-        die('Output from "client -o" is %d lines, expecting 1' % len(output))
-
-    entry = output[0]
-    if "Root" not in entry:
-        die('Client has no "Root"')
-
-    return entry["Root"]
-
-class Command:
-    def __init__(self):
-        self.usage = "usage: %prog [options]"
-        self.needsGit = True
-
-class P4UserMap:
-    def __init__(self):
-        self.userMapFromPerforceServer = False
-        self.myP4UserId = None
-
-    def p4UserId(self):
-        if self.myP4UserId:
-            return self.myP4UserId
-
-        results = p4CmdList("user -o")
-        for r in results:
-            if r.has_key('User'):
-                self.myP4UserId = r['User']
-                return r['User']
-        die("Could not find your p4 user id")
-
-    def p4UserIsMe(self, p4User):
-        # return True if the given p4 user is actually me
-        me = self.p4UserId()
-        if not p4User or p4User != me:
-            return False
-        else:
-            return True
-
-    def getUserCacheFilename(self):
-        home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
-        return home + "/.gitp4-usercache.txt"
-
-    def getUserMapFromPerforceServer(self):
-        if self.userMapFromPerforceServer:
-            return
-        self.users = {}
-        self.emails = {}
-
-        for output in p4CmdList("users"):
-            if not output.has_key("User"):
-                continue
-            self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
-            self.emails[output["Email"]] = output["User"]
-
-
-        s = ''
-        for (key, val) in self.users.items():
-            s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
-
-        open(self.getUserCacheFilename(), "wb").write(s)
-        self.userMapFromPerforceServer = True
-
-    def loadUserMapFromCache(self):
-        self.users = {}
-        self.userMapFromPerforceServer = False
-        try:
-            cache = open(self.getUserCacheFilename(), "rb")
-            lines = cache.readlines()
-            cache.close()
-            for line in lines:
-                entry = line.strip().split("\t")
-                self.users[entry[0]] = entry[1]
-        except IOError:
-            self.getUserMapFromPerforceServer()
-
-class P4Debug(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [
-            optparse.make_option("--verbose", dest="verbose", action="store_true",
-                                 default=False),
-            ]
-        self.description = "A tool to debug the output of p4 -G."
-        self.needsGit = False
-        self.verbose = False
-
-    def run(self, args):
-        j = 0
-        for output in p4CmdList(args):
-            print 'Element: %d' % j
-            j += 1
-            print output
-        return True
-
-class P4RollBack(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [
-            optparse.make_option("--verbose", dest="verbose", action="store_true"),
-            optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
-        ]
-        self.description = "A tool to debug the multi-branch import. Don't use :)"
-        self.verbose = False
-        self.rollbackLocalBranches = False
-
-    def run(self, args):
-        if len(args) != 1:
-            return False
-        maxChange = int(args[0])
-
-        if "p4ExitCode" in p4Cmd("changes -m 1"):
-            die("Problems executing p4");
-
-        if self.rollbackLocalBranches:
-            refPrefix = "refs/heads/"
-            lines = read_pipe_lines("git rev-parse --symbolic --branches")
-        else:
-            refPrefix = "refs/remotes/"
-            lines = read_pipe_lines("git rev-parse --symbolic --remotes")
-
-        for line in lines:
-            if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
-                line = line.strip()
-                ref = refPrefix + line
-                log = extractLogMessageFromGitCommit(ref)
-                settings = extractSettingsGitLog(log)
-
-                depotPaths = settings['depot-paths']
-                change = settings['change']
-
-                changed = False
-
-                if len(p4Cmd("changes -m 1 "  + ' '.join (['%s...@%s' % (p, maxChange)
-                                                           for p in depotPaths]))) == 0:
-                    print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
-                    system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
-                    continue
-
-                while change and int(change) > maxChange:
-                    changed = True
-                    if self.verbose:
-                        print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
-                    system("git update-ref %s \"%s^\"" % (ref, ref))
-                    log = extractLogMessageFromGitCommit(ref)
-                    settings =  extractSettingsGitLog(log)
-
-
-                    depotPaths = settings['depot-paths']
-                    change = settings['change']
-
-                if changed:
-                    print "%s rewound to %s" % (ref, change)
-
-        return True
-
-class P4Submit(Command, P4UserMap):
-    def __init__(self):
-        Command.__init__(self)
-        P4UserMap.__init__(self)
-        self.options = [
-                optparse.make_option("--verbose", dest="verbose", action="store_true"),
-                optparse.make_option("--origin", dest="origin"),
-                optparse.make_option("-M", dest="detectRenames", action="store_true"),
-                # preserve the user, requires relevant p4 permissions
-                optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"),
-        ]
-        self.description = "Submit changes from git to the perforce depot."
-        self.usage += " [name of git branch to submit into perforce depot]"
-        self.interactive = True
-        self.origin = ""
-        self.detectRenames = False
-        self.verbose = False
-        self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
-        self.isWindows = (platform.system() == "Windows")
-
-    def check(self):
-        if len(p4CmdList("opened ...")) > 0:
-            die("You have files opened with perforce! Close them before starting the sync.")
-
-    # replaces everything between 'Description:' and the next P4 submit template field with the
-    # commit message
-    def prepareLogMessage(self, template, message):
-        result = ""
-
-        inDescriptionSection = False
-
-        for line in template.split("\n"):
-            if line.startswith("#"):
-                result += line + "\n"
-                continue
-
-            if inDescriptionSection:
-                if line.startswith("Files:") or line.startswith("Jobs:"):
-                    inDescriptionSection = False
-                else:
-                    continue
-            else:
-                if line.startswith("Description:"):
-                    inDescriptionSection = True
-                    line += "\n"
-                    for messageLine in message.split("\n"):
-                        line += "\t" + messageLine + "\n"
-
-            result += line + "\n"
-
-        return result
-
-    def patchRCSKeywords(self, file, pattern):
-        # Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern
-        (handle, outFileName) = tempfile.mkstemp(dir='.')
-        try:
-            outFile = os.fdopen(handle, "w+")
-            inFile = open(file, "r")
-            regexp = re.compile(pattern, re.VERBOSE)
-            for line in inFile.readlines():
-                line = regexp.sub(r'$\1$', line)
-                outFile.write(line)
-            inFile.close()
-            outFile.close()
-            # Forcibly overwrite the original file
-            os.unlink(file)
-            shutil.move(outFileName, file)
-        except:
-            # cleanup our temporary file
-            os.unlink(outFileName)
-            print "Failed to strip RCS keywords in %s" % file
-            raise
-
-        print "Patched up RCS keywords in %s" % file
-
-    def p4UserForCommit(self,id):
-        # Return the tuple (perforce user,git email) for a given git commit id
-        self.getUserMapFromPerforceServer()
-        gitEmail = read_pipe("git log --max-count=1 --format='%%ae' %s" % id)
-        gitEmail = gitEmail.strip()
-        if not self.emails.has_key(gitEmail):
-            return (None,gitEmail)
-        else:
-            return (self.emails[gitEmail],gitEmail)
-
-    def checkValidP4Users(self,commits):
-        # check if any git authors cannot be mapped to p4 users
-        for id in commits:
-            (user,email) = self.p4UserForCommit(id)
-            if not user:
-                msg = "Cannot find p4 user for email %s in commit %s." % (email, id)
-                if gitConfig('git-p4.allowMissingP4Users').lower() == "true":
-                    print "%s" % msg
-                else:
-                    die("Error: %s\nSet git-p4.allowMissingP4Users to true to allow this." % msg)
-
-    def lastP4Changelist(self):
-        # Get back the last changelist number submitted in this client spec. This
-        # then gets used to patch up the username in the change. If the same
-        # client spec is being used by multiple processes then this might go
-        # wrong.
-        results = p4CmdList("client -o")        # find the current client
-        client = None
-        for r in results:
-            if r.has_key('Client'):
-                client = r['Client']
-                break
-        if not client:
-            die("could not get client spec")
-        results = p4CmdList(["changes", "-c", client, "-m", "1"])
-        for r in results:
-            if r.has_key('change'):
-                return r['change']
-        die("Could not get changelist number for last submit - cannot patch up user details")
-
-    def modifyChangelistUser(self, changelist, newUser):
-        # fixup the user field of a changelist after it has been submitted.
-        changes = p4CmdList("change -o %s" % changelist)
-        if len(changes) != 1:
-            die("Bad output from p4 change modifying %s to user %s" %
-                (changelist, newUser))
-
-        c = changes[0]
-        if c['User'] == newUser: return   # nothing to do
-        c['User'] = newUser
-        input = marshal.dumps(c)
-
-        result = p4CmdList("change -f -i", stdin=input)
-        for r in result:
-            if r.has_key('code'):
-                if r['code'] == 'error':
-                    die("Could not modify user field of changelist %s to %s:%s" % (changelist, newUser, r['data']))
-            if r.has_key('data'):
-                print("Updated user field for changelist %s to %s" % (changelist, newUser))
-                return
-        die("Could not modify user field of changelist %s to %s" % (changelist, newUser))
-
-    def canChangeChangelists(self):
-        # check to see if we have p4 admin or super-user permissions, either of
-        # which are required to modify changelists.
-        results = p4CmdList(["protects", self.depotPath])
-        for r in results:
-            if r.has_key('perm'):
-                if r['perm'] == 'admin':
-                    return 1
-                if r['perm'] == 'super':
-                    return 1
-        return 0
-
-    def prepareSubmitTemplate(self):
-        # remove lines in the Files section that show changes to files outside the depot path we're committing into
-        template = ""
-        inFilesSection = False
-        for line in p4_read_pipe_lines(['change', '-o']):
-            if line.endswith("\r\n"):
-                line = line[:-2] + "\n"
-            if inFilesSection:
-                if line.startswith("\t"):
-                    # path starts and ends with a tab
-                    path = line[1:]
-                    lastTab = path.rfind("\t")
-                    if lastTab != -1:
-                        path = path[:lastTab]
-                        if not p4PathStartsWith(path, self.depotPath):
-                            continue
-                else:
-                    inFilesSection = False
-            else:
-                if line.startswith("Files:"):
-                    inFilesSection = True
-
-            template += line
-
-        return template
-
-    def edit_template(self, template_file):
-        """Invoke the editor to let the user change the submission
-           message.  Return true if okay to continue with the submit."""
-
-        # if configured to skip the editing part, just submit
-        if gitConfig("git-p4.skipSubmitEdit") == "true":
-            return True
-
-        # look at the modification time, to check later if the user saved
-        # the file
-        mtime = os.stat(template_file).st_mtime
-
-        # invoke the editor
-        if os.environ.has_key("P4EDITOR"):
-            editor = os.environ.get("P4EDITOR")
-        else:
-            editor = read_pipe("git var GIT_EDITOR").strip()
-        system(editor + " " + template_file)
-
-        # If the file was not saved, prompt to see if this patch should
-        # be skipped.  But skip this verification step if configured so.
-        if gitConfig("git-p4.skipSubmitEditCheck") == "true":
-            return True
-
-        # modification time updated means user saved the file
-        if os.stat(template_file).st_mtime > mtime:
-            return True
-
-        while True:
-            response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
-            if response == 'y':
-                return True
-            if response == 'n':
-                return False
-
-    def applyCommit(self, id):
-        print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
-
-        (p4User, gitEmail) = self.p4UserForCommit(id)
-
-        if not self.detectRenames:
-            # If not explicitly set check the config variable
-            self.detectRenames = gitConfig("git-p4.detectRenames")
-
-        if self.detectRenames.lower() == "false" or self.detectRenames == "":
-            diffOpts = ""
-        elif self.detectRenames.lower() == "true":
-            diffOpts = "-M"
-        else:
-            diffOpts = "-M%s" % self.detectRenames
-
-        detectCopies = gitConfig("git-p4.detectCopies")
-        if detectCopies.lower() == "true":
-            diffOpts += " -C"
-        elif detectCopies != "" and detectCopies.lower() != "false":
-            diffOpts += " -C%s" % detectCopies
-
-        if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true":
-            diffOpts += " --find-copies-harder"
-
-        diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
-        filesToAdd = set()
-        filesToDelete = set()
-        editedFiles = set()
-        filesToChangeExecBit = {}
-
-        for line in diff:
-            diff = parseDiffTreeEntry(line)
-            modifier = diff['status']
-            path = diff['src']
-            if modifier == "M":
-                p4_edit(path)
-                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
-                    filesToChangeExecBit[path] = diff['dst_mode']
-                editedFiles.add(path)
-            elif modifier == "A":
-                filesToAdd.add(path)
-                filesToChangeExecBit[path] = diff['dst_mode']
-                if path in filesToDelete:
-                    filesToDelete.remove(path)
-            elif modifier == "D":
-                filesToDelete.add(path)
-                if path in filesToAdd:
-                    filesToAdd.remove(path)
-            elif modifier == "C":
-                src, dest = diff['src'], diff['dst']
-                p4_integrate(src, dest)
-                if diff['src_sha1'] != diff['dst_sha1']:
-                    p4_edit(dest)
-                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
-                    p4_edit(dest)
-                    filesToChangeExecBit[dest] = diff['dst_mode']
-                os.unlink(dest)
-                editedFiles.add(dest)
-            elif modifier == "R":
-                src, dest = diff['src'], diff['dst']
-                p4_integrate(src, dest)
-                if diff['src_sha1'] != diff['dst_sha1']:
-                    p4_edit(dest)
-                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
-                    p4_edit(dest)
-                    filesToChangeExecBit[dest] = diff['dst_mode']
-                os.unlink(dest)
-                editedFiles.add(dest)
-                filesToDelete.add(src)
-            else:
-                die("unknown modifier %s for %s" % (modifier, path))
-
-        diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
-        patchcmd = diffcmd + " | git apply "
-        tryPatchCmd = patchcmd + "--check -"
-        applyPatchCmd = patchcmd + "--check --apply -"
-        patch_succeeded = True
-
-        if os.system(tryPatchCmd) != 0:
-            fixed_rcs_keywords = False
-            patch_succeeded = False
-            print "Unfortunately applying the change failed!"
-
-            # Patch failed, maybe it's just RCS keyword woes. Look through
-            # the patch to see if that's possible.
-            if gitConfig("git-p4.attemptRCSCleanup","--bool") == "true":
-                file = None
-                pattern = None
-                kwfiles = {}
-                for file in editedFiles | filesToDelete:
-                    # did this file's delta contain RCS keywords?
-                    pattern = p4_keywords_regexp_for_file(file)
-
-                    if pattern:
-                        # this file is a possibility...look for RCS keywords.
-                        regexp = re.compile(pattern, re.VERBOSE)
-                        for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
-                            if regexp.search(line):
-                                if verbose:
-                                    print "got keyword match on %s in %s in %s" % (pattern, line, file)
-                                kwfiles[file] = pattern
-                                break
-
-                for file in kwfiles:
-                    if verbose:
-                        print "zapping %s with %s" % (line,pattern)
-                    self.patchRCSKeywords(file, kwfiles[file])
-                    fixed_rcs_keywords = True
-
-            if fixed_rcs_keywords:
-                print "Retrying the patch with RCS keywords cleaned up"
-                if os.system(tryPatchCmd) == 0:
-                    patch_succeeded = True
-
-        if not patch_succeeded:
-            print "What do you want to do?"
-            response = "x"
-            while response != "s" and response != "a" and response != "w":
-                response = raw_input("[s]kip this patch / [a]pply the patch forcibly "
-                                     "and with .rej files / [w]rite the patch to a file (patch.txt) ")
-            if response == "s":
-                print "Skipping! Good luck with the next patches..."
-                for f in editedFiles:
-                    p4_revert(f)
-                for f in filesToAdd:
-                    os.remove(f)
-                return
-            elif response == "a":
-                os.system(applyPatchCmd)
-                if len(filesToAdd) > 0:
-                    print "You may also want to call p4 add on the following files:"
-                    print " ".join(filesToAdd)
-                if len(filesToDelete):
-                    print "The following files should be scheduled for deletion with p4 delete:"
-                    print " ".join(filesToDelete)
-                die("Please resolve and submit the conflict manually and "
-                    + "continue afterwards with git-p4 submit --continue")
-            elif response == "w":
-                system(diffcmd + " > patch.txt")
-                print "Patch saved to patch.txt in %s !" % self.clientPath
-                die("Please resolve and submit the conflict manually and "
-                    "continue afterwards with git-p4 submit --continue")
-
-        system(applyPatchCmd)
-
-        for f in filesToAdd:
-            p4_add(f)
-        for f in filesToDelete:
-            p4_revert(f)
-            p4_delete(f)
-
-        # Set/clear executable bits
-        for f in filesToChangeExecBit.keys():
-            mode = filesToChangeExecBit[f]
-            setP4ExecBit(f, mode)
-
-        logMessage = extractLogMessageFromGitCommit(id)
-        logMessage = logMessage.strip()
-
-        template = self.prepareSubmitTemplate()
-
-        if self.interactive:
-            submitTemplate = self.prepareLogMessage(template, logMessage)
-
-            if self.preserveUser:
-               submitTemplate = submitTemplate + ("\n######## Actual user %s, modified after commit\n" % p4User)
-
-            if os.environ.has_key("P4DIFF"):
-                del(os.environ["P4DIFF"])
-            diff = ""
-            for editedFile in editedFiles:
-                diff += p4_read_pipe(['diff', '-du', editedFile])
-
-            newdiff = ""
-            for newFile in filesToAdd:
-                newdiff += "==== new file ====\n"
-                newdiff += "--- /dev/null\n"
-                newdiff += "+++ %s\n" % newFile
-                f = open(newFile, "r")
-                for line in f.readlines():
-                    newdiff += "+" + line
-                f.close()
-
-            if self.checkAuthorship and not self.p4UserIsMe(p4User):
-                submitTemplate += "######## git author %s does not match your p4 account.\n" % gitEmail
-                submitTemplate += "######## Use git-p4 option --preserve-user to modify authorship\n"
-                submitTemplate += "######## Use git-p4 config git-p4.skipUserNameCheck hides this message.\n"
-
-            separatorLine = "######## everything below this line is just the diff #######\n"
-
-            (handle, fileName) = tempfile.mkstemp()
-            tmpFile = os.fdopen(handle, "w+")
-            if self.isWindows:
-                submitTemplate = submitTemplate.replace("\n", "\r\n")
-                separatorLine = separatorLine.replace("\n", "\r\n")
-                newdiff = newdiff.replace("\n", "\r\n")
-            tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
-            tmpFile.close()
-
-            if self.edit_template(fileName):
-                # read the edited message and submit
-                tmpFile = open(fileName, "rb")
-                message = tmpFile.read()
-                tmpFile.close()
-                submitTemplate = message[:message.index(separatorLine)]
-                if self.isWindows:
-                    submitTemplate = submitTemplate.replace("\r\n", "\n")
-                p4_write_pipe(['submit', '-i'], submitTemplate)
-
-                if self.preserveUser:
-                    if p4User:
-                        # Get last changelist number. Cannot easily get it from
-                        # the submit command output as the output is
-                        # unmarshalled.
-                        changelist = self.lastP4Changelist()
-                        self.modifyChangelistUser(changelist, p4User)
-            else:
-                # skip this patch
-                print "Submission cancelled, undoing p4 changes."
-                for f in editedFiles:
-                    p4_revert(f)
-                for f in filesToAdd:
-                    p4_revert(f)
-                    os.remove(f)
-
-            os.remove(fileName)
-        else:
-            fileName = "submit.txt"
-            file = open(fileName, "w+")
-            file.write(self.prepareLogMessage(template, logMessage))
-            file.close()
-            print ("Perforce submit template written as %s. "
-                   + "Please review/edit and then use p4 submit -i < %s to submit directly!"
-                   % (fileName, fileName))
-
-    def run(self, args):
-        if len(args) == 0:
-            self.master = currentGitBranch()
-            if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
-                die("Detecting current git branch failed!")
-        elif len(args) == 1:
-            self.master = args[0]
-            if not branchExists(self.master):
-                die("Branch %s does not exist" % self.master)
-        else:
-            return False
-
-        allowSubmit = gitConfig("git-p4.allowSubmit")
-        if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
-            die("%s is not in git-p4.allowSubmit" % self.master)
-
-        [upstream, settings] = findUpstreamBranchPoint()
-        self.depotPath = settings['depot-paths'][0]
-        if len(self.origin) == 0:
-            self.origin = upstream
-
-        if self.preserveUser:
-            if not self.canChangeChangelists():
-                die("Cannot preserve user names without p4 super-user or admin permissions")
-
-        if self.verbose:
-            print "Origin branch is " + self.origin
-
-        if len(self.depotPath) == 0:
-            print "Internal error: cannot locate perforce depot path from existing branches"
-            sys.exit(128)
-
-        self.useClientSpec = False
-        if gitConfig("git-p4.useclientspec", "--bool") == "true":
-            self.useClientSpec = True
-        if self.useClientSpec:
-            self.clientSpecDirs = getClientSpec()
-
-        if self.useClientSpec:
-            # all files are relative to the client spec
-            self.clientPath = getClientRoot()
-        else:
-            self.clientPath = p4Where(self.depotPath)
-
-        if self.clientPath == "":
-            die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath)
-
-        print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
-        self.oldWorkingDirectory = os.getcwd()
-
-        # ensure the clientPath exists
-        if not os.path.exists(self.clientPath):
-            os.makedirs(self.clientPath)
-
-        chdir(self.clientPath)
-        print "Synchronizing p4 checkout..."
-        p4_sync("...")
-        self.check()
-
-        commits = []
-        for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
-            commits.append(line.strip())
-        commits.reverse()
-
-        if self.preserveUser or (gitConfig("git-p4.skipUserNameCheck") == "true"):
-            self.checkAuthorship = False
-        else:
-            self.checkAuthorship = True
-
-        if self.preserveUser:
-            self.checkValidP4Users(commits)
-
-        while len(commits) > 0:
-            commit = commits[0]
-            commits = commits[1:]
-            self.applyCommit(commit)
-            if not self.interactive:
-                break
-
-        if len(commits) == 0:
-            print "All changes applied!"
-            chdir(self.oldWorkingDirectory)
-
-            sync = P4Sync()
-            sync.run([])
-
-            rebase = P4Rebase()
-            rebase.rebase()
-
-        return True
-
-class View(object):
-    """Represent a p4 view ("p4 help views"), and map files in a
-       repo according to the view."""
-
-    class Path(object):
-        """A depot or client path, possibly containing wildcards.
-           The only one supported is ... at the end, currently.
-           Initialize with the full path, with //depot or //client."""
-
-        def __init__(self, path, is_depot):
-            self.path = path
-            self.is_depot = is_depot
-            self.find_wildcards()
-            # remember the prefix bit, useful for relative mappings
-            m = re.match("(//[^/]+/)", self.path)
-            if not m:
-                die("Path %s does not start with //prefix/" % self.path)
-            prefix = m.group(1)
-            if not self.is_depot:
-                # strip //client/ on client paths
-                self.path = self.path[len(prefix):]
-
-        def find_wildcards(self):
-            """Make sure wildcards are valid, and set up internal
-               variables."""
-
-            self.ends_triple_dot = False
-            # There are three wildcards allowed in p4 views
-            # (see "p4 help views").  This code knows how to
-            # handle "..." (only at the end), but cannot deal with
-            # "%%n" or "*".  Only check the depot_side, as p4 should
-            # validate that the client_side matches too.
-            if re.search(r'%%[1-9]', self.path):
-                die("Can't handle %%n wildcards in view: %s" % self.path)
-            if self.path.find("*") >= 0:
-                die("Can't handle * wildcards in view: %s" % self.path)
-            triple_dot_index = self.path.find("...")
-            if triple_dot_index >= 0:
-                if triple_dot_index != len(self.path) - 3:
-                    die("Can handle only single ... wildcard, at end: %s" %
-                        self.path)
-                self.ends_triple_dot = True
-
-        def ensure_compatible(self, other_path):
-            """Make sure the wildcards agree."""
-            if self.ends_triple_dot != other_path.ends_triple_dot:
-                 die("Both paths must end with ... if either does;\n" +
-                     "paths: %s %s" % (self.path, other_path.path))
-
-        def match_wildcards(self, test_path):
-            """See if this test_path matches us, and fill in the value
-               of the wildcards if so.  Returns a tuple of
-               (True|False, wildcards[]).  For now, only the ... at end
-               is supported, so at most one wildcard."""
-            if self.ends_triple_dot:
-                dotless = self.path[:-3]
-                if test_path.startswith(dotless):
-                    wildcard = test_path[len(dotless):]
-                    return (True, [ wildcard ])
-            else:
-                if test_path == self.path:
-                    return (True, [])
-            return (False, [])
-
-        def match(self, test_path):
-            """Just return if it matches; don't bother with the wildcards."""
-            b, _ = self.match_wildcards(test_path)
-            return b
-
-        def fill_in_wildcards(self, wildcards):
-            """Return the relative path, with the wildcards filled in
-               if there are any."""
-            if self.ends_triple_dot:
-                return self.path[:-3] + wildcards[0]
-            else:
-                return self.path
-
-    class Mapping(object):
-        def __init__(self, depot_side, client_side, overlay, exclude):
-            # depot_side is without the trailing /... if it had one
-            self.depot_side = View.Path(depot_side, is_depot=True)
-            self.client_side = View.Path(client_side, is_depot=False)
-            self.overlay = overlay  # started with "+"
-            self.exclude = exclude  # started with "-"
-            assert not (self.overlay and self.exclude)
-            self.depot_side.ensure_compatible(self.client_side)
-
-        def __str__(self):
-            c = " "
-            if self.overlay:
-                c = "+"
-            if self.exclude:
-                c = "-"
-            return "View.Mapping: %s%s -> %s" % \
-                   (c, self.depot_side.path, self.client_side.path)
-
-        def map_depot_to_client(self, depot_path):
-            """Calculate the client path if using this mapping on the
-               given depot path; does not consider the effect of other
-               mappings in a view.  Even excluded mappings are returned."""
-            matches, wildcards = self.depot_side.match_wildcards(depot_path)
-            if not matches:
-                return ""
-            client_path = self.client_side.fill_in_wildcards(wildcards)
-            return client_path
-
-    #
-    # View methods
-    #
-    def __init__(self):
-        self.mappings = []
-
-    def append(self, view_line):
-        """Parse a view line, splitting it into depot and client
-           sides.  Append to self.mappings, preserving order."""
-
-        # Split the view line into exactly two words.  P4 enforces
-        # structure on these lines that simplifies this quite a bit.
-        #
-        # Either or both words may be double-quoted.
-        # Single quotes do not matter.
-        # Double-quote marks cannot occur inside the words.
-        # A + or - prefix is also inside the quotes.
-        # There are no quotes unless they contain a space.
-        # The line is already white-space stripped.
-        # The two words are separated by a single space.
-        #
-        if view_line[0] == '"':
-            # First word is double quoted.  Find its end.
-            close_quote_index = view_line.find('"', 1)
-            if close_quote_index <= 0:
-                die("No first-word closing quote found: %s" % view_line)
-            depot_side = view_line[1:close_quote_index]
-            # skip closing quote and space
-            rhs_index = close_quote_index + 1 + 1
-        else:
-            space_index = view_line.find(" ")
-            if space_index <= 0:
-                die("No word-splitting space found: %s" % view_line)
-            depot_side = view_line[0:space_index]
-            rhs_index = space_index + 1
-
-        if view_line[rhs_index] == '"':
-            # Second word is double quoted.  Make sure there is a
-            # double quote at the end too.
-            if not view_line.endswith('"'):
-                die("View line with rhs quote should end with one: %s" %
-                    view_line)
-            # skip the quotes
-            client_side = view_line[rhs_index+1:-1]
-        else:
-            client_side = view_line[rhs_index:]
-
-        # prefix + means overlay on previous mapping
-        overlay = False
-        if depot_side.startswith("+"):
-            overlay = True
-            depot_side = depot_side[1:]
-
-        # prefix - means exclude this path
-        exclude = False
-        if depot_side.startswith("-"):
-            exclude = True
-            depot_side = depot_side[1:]
-
-        m = View.Mapping(depot_side, client_side, overlay, exclude)
-        self.mappings.append(m)
-
-    def map_in_client(self, depot_path):
-        """Return the relative location in the client where this
-           depot file should live.  Returns "" if the file should
-           not be mapped in the client."""
-
-        paths_filled = []
-        client_path = ""
-
-        # look at later entries first
-        for m in self.mappings[::-1]:
-
-            # see where will this path end up in the client
-            p = m.map_depot_to_client(depot_path)
-
-            if p == "":
-                # Depot path does not belong in client.  Must remember
-                # this, as previous items should not cause files to
-                # exist in this path either.  Remember that the list is
-                # being walked from the end, which has higher precedence.
-                # Overlap mappings do not exclude previous mappings.
-                if not m.overlay:
-                    paths_filled.append(m.client_side)
-
-            else:
-                # This mapping matched; no need to search any further.
-                # But, the mapping could be rejected if the client path
-                # has already been claimed by an earlier mapping (i.e.
-                # one later in the list, which we are walking backwards).
-                already_mapped_in_client = False
-                for f in paths_filled:
-                    # this is View.Path.match
-                    if f.match(p):
-                        already_mapped_in_client = True
-                        break
-                if not already_mapped_in_client:
-                    # Include this file, unless it is from a line that
-                    # explicitly said to exclude it.
-                    if not m.exclude:
-                        client_path = p
-
-                # a match, even if rejected, always stops the search
-                break
-
-        return client_path
-
-class P4Sync(Command, P4UserMap):
-    delete_actions = ( "delete", "move/delete", "purge" )
-
-    def __init__(self):
-        Command.__init__(self)
-        P4UserMap.__init__(self)
-        self.options = [
-                optparse.make_option("--branch", dest="branch"),
-                optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
-                optparse.make_option("--changesfile", dest="changesFile"),
-                optparse.make_option("--silent", dest="silent", action="store_true"),
-                optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
-                optparse.make_option("--verbose", dest="verbose", action="store_true"),
-                optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
-                                     help="Import into refs/heads/ , not refs/remotes"),
-                optparse.make_option("--max-changes", dest="maxChanges"),
-                optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
-                                     help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
-                optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
-                                     help="Only sync files that are included in the Perforce Client Spec")
-        ]
-        self.description = """Imports from Perforce into a git repository.\n
-    example:
-    //depot/my/project/ -- to import the current head
-    //depot/my/project/@all -- to import everything
-    //depot/my/project/@1,6 -- to import only from revision 1 to 6
-
-    (a ... is not needed in the path p4 specification, it's added implicitly)"""
-
-        self.usage += " //depot/path[@revRange]"
-        self.silent = False
-        self.createdBranches = set()
-        self.committedChanges = set()
-        self.branch = ""
-        self.detectBranches = False
-        self.detectLabels = False
-        self.changesFile = ""
-        self.syncWithOrigin = True
-        self.verbose = False
-        self.importIntoRemotes = True
-        self.maxChanges = ""
-        self.isWindows = (platform.system() == "Windows")
-        self.keepRepoPath = False
-        self.depotPaths = None
-        self.p4BranchesInGit = []
-        self.cloneExclude = []
-        self.useClientSpec = False
-        self.useClientSpec_from_options = False
-        self.clientSpecDirs = None
-        self.tempBranches = []
-        self.tempBranchLocation = "git-p4-tmp"
-
-        if gitConfig("git-p4.syncFromOrigin") == "false":
-            self.syncWithOrigin = False
-
-    #
-    # P4 wildcards are not allowed in filenames.  P4 complains
-    # if you simply add them, but you can force it with "-f", in
-    # which case it translates them into %xx encoding internally.
-    # Search for and fix just these four characters.  Do % last so
-    # that fixing it does not inadvertently create new %-escapes.
-    #
-    def wildcard_decode(self, path):
-        # Cannot have * in a filename in windows; untested as to
-        # what p4 would do in such a case.
-        if not self.isWindows:
-            path = path.replace("%2A", "*")
-        path = path.replace("%23", "#") \
-                   .replace("%40", "@") \
-                   .replace("%25", "%")
-        return path
-
-    # Force a checkpoint in fast-import and wait for it to finish
-    def checkpoint(self):
-        self.gitStream.write("checkpoint\n\n")
-        self.gitStream.write("progress checkpoint\n\n")
-        out = self.gitOutput.readline()
-        if self.verbose:
-            print "checkpoint finished: " + out
-
-    def extractFilesFromCommit(self, commit):
-        self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
-                             for path in self.cloneExclude]
-        files = []
-        fnum = 0
-        while commit.has_key("depotFile%s" % fnum):
-            path =  commit["depotFile%s" % fnum]
-
-            if [p for p in self.cloneExclude
-                if p4PathStartsWith(path, p)]:
-                found = False
-            else:
-                found = [p for p in self.depotPaths
-                         if p4PathStartsWith(path, p)]
-            if not found:
-                fnum = fnum + 1
-                continue
-
-            file = {}
-            file["path"] = path
-            file["rev"] = commit["rev%s" % fnum]
-            file["action"] = commit["action%s" % fnum]
-            file["type"] = commit["type%s" % fnum]
-            files.append(file)
-            fnum = fnum + 1
-        return files
-
-    def stripRepoPath(self, path, prefixes):
-        if self.useClientSpec:
-            return self.clientSpecDirs.map_in_client(path)
-
-        if self.keepRepoPath:
-            prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
-
-        for p in prefixes:
-            if p4PathStartsWith(path, p):
-                path = path[len(p):]
-
-        return path
-
-    def splitFilesIntoBranches(self, commit):
-        branches = {}
-        fnum = 0
-        while commit.has_key("depotFile%s" % fnum):
-            path =  commit["depotFile%s" % fnum]
-            found = [p for p in self.depotPaths
-                     if p4PathStartsWith(path, p)]
-            if not found:
-                fnum = fnum + 1
-                continue
-
-            file = {}
-            file["path"] = path
-            file["rev"] = commit["rev%s" % fnum]
-            file["action"] = commit["action%s" % fnum]
-            file["type"] = commit["type%s" % fnum]
-            fnum = fnum + 1
-
-            relPath = self.stripRepoPath(path, self.depotPaths)
-
-            for branch in self.knownBranches.keys():
-
-                # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
-                if relPath.startswith(branch + "/"):
-                    if branch not in branches:
-                        branches[branch] = []
-                    branches[branch].append(file)
-                    break
-
-        return branches
-
-    # output one file from the P4 stream
-    # - helper for streamP4Files
-
-    def streamOneP4File(self, file, contents):
-        relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
-        relPath = self.wildcard_decode(relPath)
-        if verbose:
-            sys.stderr.write("%s\n" % relPath)
-
-        (type_base, type_mods) = split_p4_type(file["type"])
-
-        git_mode = "100644"
-        if "x" in type_mods:
-            git_mode = "100755"
-        if type_base == "symlink":
-            git_mode = "120000"
-            # p4 print on a symlink contains "target\n"; remove the newline
-            data = ''.join(contents)
-            contents = [data[:-1]]
-
-        if type_base == "utf16":
-            # p4 delivers different text in the python output to -G
-            # than it does when using "print -o", or normal p4 client
-            # operations.  utf16 is converted to ascii or utf8, perhaps.
-            # But ascii text saved as -t utf16 is completely mangled.
-            # Invoke print -o to get the real contents.
-            text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']])
-            contents = [ text ]
-
-        if type_base == "apple":
-            # Apple filetype files will be streamed as a concatenation of
-            # its appledouble header and the contents.  This is useless
-            # on both macs and non-macs.  If using "print -q -o xx", it
-            # will create "xx" with the data, and "%xx" with the header.
-            # This is also not very useful.
-            #
-            # Ideally, someday, this script can learn how to generate
-            # appledouble files directly and import those to git, but
-            # non-mac machines can never find a use for apple filetype.
-            print "\nIgnoring apple filetype file %s" % file['depotFile']
-            return
-
-        # Perhaps windows wants unicode, utf16 newlines translated too;
-        # but this is not doing it.
-        if self.isWindows and type_base == "text":
-            mangled = []
-            for data in contents:
-                data = data.replace("\r\n", "\n")
-                mangled.append(data)
-            contents = mangled
-
-        # Note that we do not try to de-mangle keywords on utf16 files,
-        # even though in theory somebody may want that.
-        pattern = p4_keywords_regexp_for_type(type_base, type_mods)
-        if pattern:
-            regexp = re.compile(pattern, re.VERBOSE)
-            text = ''.join(contents)
-            text = regexp.sub(r'$\1$', text)
-            contents = [ text ]
-
-        self.gitStream.write("M %s inline %s\n" % (git_mode, relPath))
-
-        # total length...
-        length = 0
-        for d in contents:
-            length = length + len(d)
-
-        self.gitStream.write("data %d\n" % length)
-        for d in contents:
-            self.gitStream.write(d)
-        self.gitStream.write("\n")
-
-    def streamOneP4Deletion(self, file):
-        relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
-        if verbose:
-            sys.stderr.write("delete %s\n" % relPath)
-        self.gitStream.write("D %s\n" % relPath)
-
-    # handle another chunk of streaming data
-    def streamP4FilesCb(self, marshalled):
-
-        if marshalled.has_key('depotFile') and self.stream_have_file_info:
-            # start of a new file - output the old one first
-            self.streamOneP4File(self.stream_file, self.stream_contents)
-            self.stream_file = {}
-            self.stream_contents = []
-            self.stream_have_file_info = False
-
-        # pick up the new file information... for the
-        # 'data' field we need to append to our array
-        for k in marshalled.keys():
-            if k == 'data':
-                self.stream_contents.append(marshalled['data'])
-            else:
-                self.stream_file[k] = marshalled[k]
-
-        self.stream_have_file_info = True
-
-    # Stream directly from "p4 files" into "git fast-import"
-    def streamP4Files(self, files):
-        filesForCommit = []
-        filesToRead = []
-        filesToDelete = []
-
-        for f in files:
-            # if using a client spec, only add the files that have
-            # a path in the client
-            if self.clientSpecDirs:
-                if self.clientSpecDirs.map_in_client(f['path']) == "":
-                    continue
-
-            filesForCommit.append(f)
-            if f['action'] in self.delete_actions:
-                filesToDelete.append(f)
-            else:
-                filesToRead.append(f)
-
-        # deleted files...
-        for f in filesToDelete:
-            self.streamOneP4Deletion(f)
-
-        if len(filesToRead) > 0:
-            self.stream_file = {}
-            self.stream_contents = []
-            self.stream_have_file_info = False
-
-            # curry self argument
-            def streamP4FilesCbSelf(entry):
-                self.streamP4FilesCb(entry)
-
-            fileArgs = ['%s#%s' % (f['path'], f['rev']) for f in filesToRead]
-
-            p4CmdList(["-x", "-", "print"],
-                      stdin=fileArgs,
-                      cb=streamP4FilesCbSelf)
-
-            # do the last chunk
-            if self.stream_file.has_key('depotFile'):
-                self.streamOneP4File(self.stream_file, self.stream_contents)
-
-    def make_email(self, userid):
-        if userid in self.users:
-            return self.users[userid]
-        else:
-            return "%s <a@b>" % userid
-
-    def commit(self, details, files, branch, branchPrefixes, parent = ""):
-        epoch = details["time"]
-        author = details["user"]
-        self.branchPrefixes = branchPrefixes
-
-        if self.verbose:
-            print "commit into %s" % branch
-
-        # start with reading files; if that fails, we should not
-        # create a commit.
-        new_files = []
-        for f in files:
-            if [p for p in branchPrefixes if p4PathStartsWith(f['path'], p)]:
-                new_files.append (f)
-            else:
-                sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])
-
-        self.gitStream.write("commit %s\n" % branch)
-#        gitStream.write("mark :%s\n" % details["change"])
-        self.committedChanges.add(int(details["change"]))
-        committer = ""
-        if author not in self.users:
-            self.getUserMapFromPerforceServer()
-        committer = "%s %s %s" % (self.make_email(author), epoch, self.tz)
-
-        self.gitStream.write("committer %s\n" % committer)
-
-        self.gitStream.write("data <<EOT\n")
-        self.gitStream.write(details["desc"])
-        self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s"
-                             % (','.join (branchPrefixes), details["change"]))
-        if len(details['options']) > 0:
-            self.gitStream.write(": options = %s" % details['options'])
-        self.gitStream.write("]\nEOT\n\n")
-
-        if len(parent) > 0:
-            if self.verbose:
-                print "parent %s" % parent
-            self.gitStream.write("from %s\n" % parent)
-
-        self.streamP4Files(new_files)
-        self.gitStream.write("\n")
-
-        change = int(details["change"])
-
-        if self.labels.has_key(change):
-            label = self.labels[change]
-            labelDetails = label[0]
-            labelRevisions = label[1]
-            if self.verbose:
-                print "Change %s is labelled %s" % (change, labelDetails)
-
-            files = p4CmdList(["files"] + ["%s...@%s" % (p, change)
-                                                    for p in branchPrefixes])
-
-            if len(files) == len(labelRevisions):
-
-                cleanedFiles = {}
-                for info in files:
-                    if info["action"] in self.delete_actions:
-                        continue
-                    cleanedFiles[info["depotFile"]] = info["rev"]
-
-                if cleanedFiles == labelRevisions:
-                    self.gitStream.write("tag tag_%s\n" % labelDetails["label"])
-                    self.gitStream.write("from %s\n" % branch)
-
-                    owner = labelDetails["Owner"]
-
-                    # Try to use the owner of the p4 label, or failing that,
-                    # the current p4 user id.
-                    if owner:
-                        email = self.make_email(owner)
-                    else:
-                        email = self.make_email(self.p4UserId())
-                    tagger = "%s %s %s" % (email, epoch, self.tz)
-
-                    self.gitStream.write("tagger %s\n" % tagger)
-
-                    description = labelDetails["Description"]
-                    self.gitStream.write("data %d\n" % len(description))
-                    self.gitStream.write(description)
-                    self.gitStream.write("\n")
-
-                else:
-                    if not self.silent:
-                        print ("Tag %s does not match with change %s: files do not match."
-                               % (labelDetails["label"], change))
-
-            else:
-                if not self.silent:
-                    print ("Tag %s does not match with change %s: file count is different."
-                           % (labelDetails["label"], change))
-
-    def getLabels(self):
-        self.labels = {}
-
-        l = p4CmdList(["labels"] + ["%s..." % p for p in self.depotPaths])
-        if len(l) > 0 and not self.silent:
-            print "Finding files belonging to labels in %s" % `self.depotPaths`
-
-        for output in l:
-            label = output["label"]
-            revisions = {}
-            newestChange = 0
-            if self.verbose:
-                print "Querying files for label %s" % label
-            for file in p4CmdList(["files"] +
-                                      ["%s...@%s" % (p, label)
-                                          for p in self.depotPaths]):
-                revisions[file["depotFile"]] = file["rev"]
-                change = int(file["change"])
-                if change > newestChange:
-                    newestChange = change
-
-            self.labels[newestChange] = [output, revisions]
-
-        if self.verbose:
-            print "Label changes: %s" % self.labels.keys()
-
-    def guessProjectName(self):
-        for p in self.depotPaths:
-            if p.endswith("/"):
-                p = p[:-1]
-            p = p[p.strip().rfind("/") + 1:]
-            if not p.endswith("/"):
-               p += "/"
-            return p
-
-    def getBranchMapping(self):
-        lostAndFoundBranches = set()
-
-        user = gitConfig("git-p4.branchUser")
-        if len(user) > 0:
-            command = "branches -u %s" % user
-        else:
-            command = "branches"
-
-        for info in p4CmdList(command):
-            details = p4Cmd(["branch", "-o", info["branch"]])
-            viewIdx = 0
-            while details.has_key("View%s" % viewIdx):
-                paths = details["View%s" % viewIdx].split(" ")
-                viewIdx = viewIdx + 1
-                # require standard //depot/foo/... //depot/bar/... mapping
-                if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."):
-                    continue
-                source = paths[0]
-                destination = paths[1]
-                ## HACK
-                if p4PathStartsWith(source, self.depotPaths[0]) and p4PathStartsWith(destination, self.depotPaths[0]):
-                    source = source[len(self.depotPaths[0]):-4]
-                    destination = destination[len(self.depotPaths[0]):-4]
-
-                    if destination in self.knownBranches:
-                        if not self.silent:
-                            print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
-                            print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
-                        continue
-
-                    self.knownBranches[destination] = source
-
-                    lostAndFoundBranches.discard(destination)
-
-                    if source not in self.knownBranches:
-                        lostAndFoundBranches.add(source)
-
-        # Perforce does not strictly require branches to be defined, so we also
-        # check git config for a branch list.
-        #
-        # Example of branch definition in git config file:
-        # [git-p4]
-        #   branchList=main:branchA
-        #   branchList=main:branchB
-        #   branchList=branchA:branchC
-        configBranches = gitConfigList("git-p4.branchList")
-        for branch in configBranches:
-            if branch:
-                (source, destination) = branch.split(":")
-                self.knownBranches[destination] = source
-
-                lostAndFoundBranches.discard(destination)
-
-                if source not in self.knownBranches:
-                    lostAndFoundBranches.add(source)
-
-
-        for branch in lostAndFoundBranches:
-            self.knownBranches[branch] = branch
-
-    def getBranchMappingFromGitBranches(self):
-        branches = p4BranchesInGit(self.importIntoRemotes)
-        for branch in branches.keys():
-            if branch == "master":
-                branch = "main"
-            else:
-                branch = branch[len(self.projectName):]
-            self.knownBranches[branch] = branch
-
-    def listExistingP4GitBranches(self):
-        # branches holds mapping from name to commit
-        branches = p4BranchesInGit(self.importIntoRemotes)
-        self.p4BranchesInGit = branches.keys()
-        for branch in branches.keys():
-            self.initialParents[self.refPrefix + branch] = branches[branch]
-
-    def updateOptionDict(self, d):
-        option_keys = {}
-        if self.keepRepoPath:
-            option_keys['keepRepoPath'] = 1
-
-        d["options"] = ' '.join(sorted(option_keys.keys()))
-
-    def readOptions(self, d):
-        self.keepRepoPath = (d.has_key('options')
-                             and ('keepRepoPath' in d['options']))
-
-    def gitRefForBranch(self, branch):
-        if branch == "main":
-            return self.refPrefix + "master"
-
-        if len(branch) <= 0:
-            return branch
-
-        return self.refPrefix + self.projectName + branch
-
-    def gitCommitByP4Change(self, ref, change):
-        if self.verbose:
-            print "looking in ref " + ref + " for change %s using bisect..." % change
-
-        earliestCommit = ""
-        latestCommit = parseRevision(ref)
-
-        while True:
-            if self.verbose:
-                print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
-            next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
-            if len(next) == 0:
-                if self.verbose:
-                    print "argh"
-                return ""
-            log = extractLogMessageFromGitCommit(next)
-            settings = extractSettingsGitLog(log)
-            currentChange = int(settings['change'])
-            if self.verbose:
-                print "current change %s" % currentChange
-
-            if currentChange == change:
-                if self.verbose:
-                    print "found %s" % next
-                return next
-
-            if currentChange < change:
-                earliestCommit = "^%s" % next
-            else:
-                latestCommit = "%s" % next
-
-        return ""
-
-    def importNewBranch(self, branch, maxChange):
-        # make fast-import flush all changes to disk and update the refs using the checkpoint
-        # command so that we can try to find the branch parent in the git history
-        self.gitStream.write("checkpoint\n\n");
-        self.gitStream.flush();
-        branchPrefix = self.depotPaths[0] + branch + "/"
-        range = "@1,%s" % maxChange
-        #print "prefix" + branchPrefix
-        changes = p4ChangesForPaths([branchPrefix], range)
-        if len(changes) <= 0:
-            return False
-        firstChange = changes[0]
-        #print "first change in branch: %s" % firstChange
-        sourceBranch = self.knownBranches[branch]
-        sourceDepotPath = self.depotPaths[0] + sourceBranch
-        sourceRef = self.gitRefForBranch(sourceBranch)
-        #print "source " + sourceBranch
-
-        branchParentChange = int(p4Cmd(["changes", "-m", "1", "%s...@1,%s" % (sourceDepotPath, firstChange)])["change"])
-        #print "branch parent: %s" % branchParentChange
-        gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
-        if len(gitParent) > 0:
-            self.initialParents[self.gitRefForBranch(branch)] = gitParent
-            #print "parent git commit: %s" % gitParent
-
-        self.importChanges(changes)
-        return True
-
-    def searchParent(self, parent, branch, target):
-        parentFound = False
-        for blob in read_pipe_lines(["git", "rev-list", "--reverse", "--no-merges", parent]):
-            blob = blob.strip()
-            if len(read_pipe(["git", "diff-tree", blob, target])) == 0:
-                parentFound = True
-                if self.verbose:
-                    print "Found parent of %s in commit %s" % (branch, blob)
-                break
-        if parentFound:
-            return blob
-        else:
-            return None
-
-    def importChanges(self, changes):
-        cnt = 1
-        for change in changes:
-            description = p4Cmd(["describe", str(change)])
-            self.updateOptionDict(description)
-
-            if not self.silent:
-                sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
-                sys.stdout.flush()
-            cnt = cnt + 1
-
-            try:
-                if self.detectBranches:
-                    branches = self.splitFilesIntoBranches(description)
-                    for branch in branches.keys():
-                        ## HACK  --hwn
-                        branchPrefix = self.depotPaths[0] + branch + "/"
-
-                        parent = ""
-
-                        filesForCommit = branches[branch]
-
-                        if self.verbose:
-                            print "branch is %s" % branch
-
-                        self.updatedBranches.add(branch)
-
-                        if branch not in self.createdBranches:
-                            self.createdBranches.add(branch)
-                            parent = self.knownBranches[branch]
-                            if parent == branch:
-                                parent = ""
-                            else:
-                                fullBranch = self.projectName + branch
-                                if fullBranch not in self.p4BranchesInGit:
-                                    if not self.silent:
-                                        print("\n    Importing new branch %s" % fullBranch);
-                                    if self.importNewBranch(branch, change - 1):
-                                        parent = ""
-                                        self.p4BranchesInGit.append(fullBranch)
-                                    if not self.silent:
-                                        print("\n    Resuming with change %s" % change);
-
-                                if self.verbose:
-                                    print "parent determined through known branches: %s" % parent
-
-                        branch = self.gitRefForBranch(branch)
-                        parent = self.gitRefForBranch(parent)
-
-                        if self.verbose:
-                            print "looking for initial parent for %s; current parent is %s" % (branch, parent)
-
-                        if len(parent) == 0 and branch in self.initialParents:
-                            parent = self.initialParents[branch]
-                            del self.initialParents[branch]
-
-                        blob = None
-                        if len(parent) > 0:
-                            tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change))
-                            if self.verbose:
-                                print "Creating temporary branch: " + tempBranch
-                            self.commit(description, filesForCommit, tempBranch, [branchPrefix])
-                            self.tempBranches.append(tempBranch)
-                            self.checkpoint()
-                            blob = self.searchParent(parent, branch, tempBranch)
-                        if blob:
-                            self.commit(description, filesForCommit, branch, [branchPrefix], blob)
-                        else:
-                            if self.verbose:
-                                print "Parent of %s not found. Committing into head of %s" % (branch, parent)
-                            self.commit(description, filesForCommit, branch, [branchPrefix], parent)
-                else:
-                    files = self.extractFilesFromCommit(description)
-                    self.commit(description, files, self.branch, self.depotPaths,
-                                self.initialParent)
-                    self.initialParent = ""
-            except IOError:
-                print self.gitError.read()
-                sys.exit(1)
-
-    def importHeadRevision(self, revision):
-        print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
-
-        details = {}
-        details["user"] = "git perforce import user"
-        details["desc"] = ("Initial import of %s from the state at revision %s\n"
-                           % (' '.join(self.depotPaths), revision))
-        details["change"] = revision
-        newestRevision = 0
-
-        fileCnt = 0
-        fileArgs = ["%s...%s" % (p,revision) for p in self.depotPaths]
-
-        for info in p4CmdList(["files"] + fileArgs):
-
-            if 'code' in info and info['code'] == 'error':
-                sys.stderr.write("p4 returned an error: %s\n"
-                                 % info['data'])
-                if info['data'].find("must refer to client") >= 0:
-                    sys.stderr.write("This particular p4 error is misleading.\n")
-                    sys.stderr.write("Perhaps the depot path was misspelled.\n");
-                    sys.stderr.write("Depot path:  %s\n" % " ".join(self.depotPaths))
-                sys.exit(1)
-            if 'p4ExitCode' in info:
-                sys.stderr.write("p4 exitcode: %s\n" % info['p4ExitCode'])
-                sys.exit(1)
-
-
-            change = int(info["change"])
-            if change > newestRevision:
-                newestRevision = change
-
-            if info["action"] in self.delete_actions:
-                # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
-                #fileCnt = fileCnt + 1
-                continue
-
-            for prop in ["depotFile", "rev", "action", "type" ]:
-                details["%s%s" % (prop, fileCnt)] = info[prop]
-
-            fileCnt = fileCnt + 1
-
-        details["change"] = newestRevision
-
-        # Use time from top-most change so that all git-p4 clones of
-        # the same p4 repo have the same commit SHA1s.
-        res = p4CmdList("describe -s %d" % newestRevision)
-        newestTime = None
-        for r in res:
-            if r.has_key('time'):
-                newestTime = int(r['time'])
-        if newestTime is None:
-            die("\"describe -s\" on newest change %d did not give a time")
-        details["time"] = newestTime
-
-        self.updateOptionDict(details)
-        try:
-            self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
-        except IOError:
-            print "IO error with git fast-import. Is your git version recent enough?"
-            print self.gitError.read()
-
-
-    def run(self, args):
-        self.depotPaths = []
-        self.changeRange = ""
-        self.initialParent = ""
-        self.previousDepotPaths = []
-
-        # map from branch depot path to parent branch
-        self.knownBranches = {}
-        self.initialParents = {}
-        self.hasOrigin = originP4BranchesExist()
-        if not self.syncWithOrigin:
-            self.hasOrigin = False
-
-        if self.importIntoRemotes:
-            self.refPrefix = "refs/remotes/p4/"
-        else:
-            self.refPrefix = "refs/heads/p4/"
-
-        if self.syncWithOrigin and self.hasOrigin:
-            if not self.silent:
-                print "Syncing with origin first by calling git fetch origin"
-            system("git fetch origin")
-
-        if len(self.branch) == 0:
-            self.branch = self.refPrefix + "master"
-            if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
-                system("git update-ref %s refs/heads/p4" % self.branch)
-                system("git branch -D p4");
-            # create it /after/ importing, when master exists
-            if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
-                system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
-
-        # accept either the command-line option, or the configuration variable
-        if self.useClientSpec:
-            # will use this after clone to set the variable
-            self.useClientSpec_from_options = True
-        else:
-            if gitConfig("git-p4.useclientspec", "--bool") == "true":
-                self.useClientSpec = True
-        if self.useClientSpec:
-            self.clientSpecDirs = getClientSpec()
-
-        # TODO: should always look at previous commits,
-        # merge with previous imports, if possible.
-        if args == []:
-            if self.hasOrigin:
-                createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent)
-            self.listExistingP4GitBranches()
-
-            if len(self.p4BranchesInGit) > 1:
-                if not self.silent:
-                    print "Importing from/into multiple branches"
-                self.detectBranches = True
-
-            if self.verbose:
-                print "branches: %s" % self.p4BranchesInGit
-
-            p4Change = 0
-            for branch in self.p4BranchesInGit:
-                logMsg =  extractLogMessageFromGitCommit(self.refPrefix + branch)
-
-                settings = extractSettingsGitLog(logMsg)
-
-                self.readOptions(settings)
-                if (settings.has_key('depot-paths')
-                    and settings.has_key ('change')):
-                    change = int(settings['change']) + 1
-                    p4Change = max(p4Change, change)
-
-                    depotPaths = sorted(settings['depot-paths'])
-                    if self.previousDepotPaths == []:
-                        self.previousDepotPaths = depotPaths
-                    else:
-                        paths = []
-                        for (prev, cur) in zip(self.previousDepotPaths, depotPaths):
-                            prev_list = prev.split("/")
-                            cur_list = cur.split("/")
-                            for i in range(0, min(len(cur_list), len(prev_list))):
-                                if cur_list[i] <> prev_list[i]:
-                                    i = i - 1
-                                    break
-
-                            paths.append ("/".join(cur_list[:i + 1]))
-
-                        self.previousDepotPaths = paths
-
-            if p4Change > 0:
-                self.depotPaths = sorted(self.previousDepotPaths)
-                self.changeRange = "@%s,#head" % p4Change
-                if not self.detectBranches:
-                    self.initialParent = parseRevision(self.branch)
-                if not self.silent and not self.detectBranches:
-                    print "Performing incremental import into %s git branch" % self.branch
-
-        if not self.branch.startswith("refs/"):
-            self.branch = "refs/heads/" + self.branch
-
-        if len(args) == 0 and self.depotPaths:
-            if not self.silent:
-                print "Depot paths: %s" % ' '.join(self.depotPaths)
-        else:
-            if self.depotPaths and self.depotPaths != args:
-                print ("previous import used depot path %s and now %s was specified. "
-                       "This doesn't work!" % (' '.join (self.depotPaths),
-                                               ' '.join (args)))
-                sys.exit(1)
-
-            self.depotPaths = sorted(args)
-
-        revision = ""
-        self.users = {}
-
-        # Make sure no revision specifiers are used when --changesfile
-        # is specified.
-        bad_changesfile = False
-        if len(self.changesFile) > 0:
-            for p in self.depotPaths:
-                if p.find("@") >= 0 or p.find("#") >= 0:
-                    bad_changesfile = True
-                    break
-        if bad_changesfile:
-            die("Option --changesfile is incompatible with revision specifiers")
-
-        newPaths = []
-        for p in self.depotPaths:
-            if p.find("@") != -1:
-                atIdx = p.index("@")
-                self.changeRange = p[atIdx:]
-                if self.changeRange == "@all":
-                    self.changeRange = ""
-                elif ',' not in self.changeRange:
-                    revision = self.changeRange
-                    self.changeRange = ""
-                p = p[:atIdx]
-            elif p.find("#") != -1:
-                hashIdx = p.index("#")
-                revision = p[hashIdx:]
-                p = p[:hashIdx]
-            elif self.previousDepotPaths == []:
-                # pay attention to changesfile, if given, else import
-                # the entire p4 tree at the head revision
-                if len(self.changesFile) == 0:
-                    revision = "#head"
-
-            p = re.sub ("\.\.\.$", "", p)
-            if not p.endswith("/"):
-                p += "/"
-
-            newPaths.append(p)
-
-        self.depotPaths = newPaths
-
-
-        self.loadUserMapFromCache()
-        self.labels = {}
-        if self.detectLabels:
-            self.getLabels();
-
-        if self.detectBranches:
-            ## FIXME - what's a P4 projectName ?
-            self.projectName = self.guessProjectName()
-
-            if self.hasOrigin:
-                self.getBranchMappingFromGitBranches()
-            else:
-                self.getBranchMapping()
-            if self.verbose:
-                print "p4-git branches: %s" % self.p4BranchesInGit
-                print "initial parents: %s" % self.initialParents
-            for b in self.p4BranchesInGit:
-                if b != "master":
-
-                    ## FIXME
-                    b = b[len(self.projectName):]
-                self.createdBranches.add(b)
-
-        self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
-
-        importProcess = subprocess.Popen(["git", "fast-import"],
-                                         stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-                                         stderr=subprocess.PIPE);
-        self.gitOutput = importProcess.stdout
-        self.gitStream = importProcess.stdin
-        self.gitError = importProcess.stderr
-
-        if revision:
-            self.importHeadRevision(revision)
-        else:
-            changes = []
-
-            if len(self.changesFile) > 0:
-                output = open(self.changesFile).readlines()
-                changeSet = set()
-                for line in output:
-                    changeSet.add(int(line))
-
-                for change in changeSet:
-                    changes.append(change)
-
-                changes.sort()
-            else:
-                # catch "git-p4 sync" with no new branches, in a repo that
-                # does not have any existing git-p4 branches
-                if len(args) == 0 and not self.p4BranchesInGit:
-                    die("No remote p4 branches.  Perhaps you never did \"git p4 clone\" in here.");
-                if self.verbose:
-                    print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
-                                                              self.changeRange)
-                changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
-
-                if len(self.maxChanges) > 0:
-                    changes = changes[:min(int(self.maxChanges), len(changes))]
-
-            if len(changes) == 0:
-                if not self.silent:
-                    print "No changes to import!"
-                return True
-
-            if not self.silent and not self.detectBranches:
-                print "Import destination: %s" % self.branch
-
-            self.updatedBranches = set()
-
-            self.importChanges(changes)
-
-            if not self.silent:
-                print ""
-                if len(self.updatedBranches) > 0:
-                    sys.stdout.write("Updated branches: ")
-                    for b in self.updatedBranches:
-                        sys.stdout.write("%s " % b)
-                    sys.stdout.write("\n")
-
-        self.gitStream.close()
-        if importProcess.wait() != 0:
-            die("fast-import failed: %s" % self.gitError.read())
-        self.gitOutput.close()
-        self.gitError.close()
-
-        # Cleanup temporary branches created during import
-        if self.tempBranches != []:
-            for branch in self.tempBranches:
-                read_pipe("git update-ref -d %s" % branch)
-            os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
-
-        return True
-
-class P4Rebase(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [ ]
-        self.description = ("Fetches the latest revision from perforce and "
-                            + "rebases the current work (branch) against it")
-        self.verbose = False
-
-    def run(self, args):
-        sync = P4Sync()
-        sync.run([])
-
-        return self.rebase()
-
-    def rebase(self):
-        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.");
-
-        [upstream, settings] = findUpstreamBranchPoint()
-        if len(upstream) == 0:
-            die("Cannot find upstream branchpoint for rebase")
-
-        # the branchpoint may be p4/foo~3, so strip off the parent
-        upstream = re.sub("~[0-9]+$", "", upstream)
-
-        print "Rebasing the current branch onto %s" % upstream
-        oldHead = read_pipe("git rev-parse HEAD").strip()
-        system("git rebase %s" % upstream)
-        system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
-        return True
-
-class P4Clone(P4Sync):
-    def __init__(self):
-        P4Sync.__init__(self)
-        self.description = "Creates a new git repository and imports from Perforce into it"
-        self.usage = "usage: %prog [options] //depot/path[@revRange]"
-        self.options += [
-            optparse.make_option("--destination", dest="cloneDestination",
-                                 action='store', default=None,
-                                 help="where to leave result of the clone"),
-            optparse.make_option("-/", dest="cloneExclude",
-                                 action="append", type="string",
-                                 help="exclude depot path"),
-            optparse.make_option("--bare", dest="cloneBare",
-                                 action="store_true", default=False),
-        ]
-        self.cloneDestination = None
-        self.needsGit = False
-        self.cloneBare = False
-
-    # This is required for the "append" cloneExclude action
-    def ensure_value(self, attr, value):
-        if not hasattr(self, attr) or getattr(self, attr) is None:
-            setattr(self, attr, value)
-        return getattr(self, attr)
-
-    def defaultDestination(self, args):
-        ## TODO: use common prefix of args?
-        depotPath = args[0]
-        depotDir = re.sub("(@[^@]*)$", "", depotPath)
-        depotDir = re.sub("(#[^#]*)$", "", depotDir)
-        depotDir = re.sub(r"\.\.\.$", "", depotDir)
-        depotDir = re.sub(r"/$", "", depotDir)
-        return os.path.split(depotDir)[1]
-
-    def run(self, args):
-        if len(args) < 1:
-            return False
-
-        if self.keepRepoPath and not self.cloneDestination:
-            sys.stderr.write("Must specify destination for --keep-path\n")
-            sys.exit(1)
-
-        depotPaths = args
-
-        if not self.cloneDestination and len(depotPaths) > 1:
-            self.cloneDestination = depotPaths[-1]
-            depotPaths = depotPaths[:-1]
-
-        self.cloneExclude = ["/"+p for p in self.cloneExclude]
-        for p in depotPaths:
-            if not p.startswith("//"):
-                return False
-
-        if not self.cloneDestination:
-            self.cloneDestination = self.defaultDestination(args)
-
-        print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
-
-        if not os.path.exists(self.cloneDestination):
-            os.makedirs(self.cloneDestination)
-        chdir(self.cloneDestination)
-
-        init_cmd = [ "git", "init" ]
-        if self.cloneBare:
-            init_cmd.append("--bare")
-        subprocess.check_call(init_cmd)
-
-        if not P4Sync.run(self, depotPaths):
-            return False
-        if self.branch != "master":
-            if self.importIntoRemotes:
-                masterbranch = "refs/remotes/p4/master"
-            else:
-                masterbranch = "refs/heads/p4/master"
-            if gitBranchExists(masterbranch):
-                system("git branch master %s" % masterbranch)
-                if not self.cloneBare:
-                    system("git checkout -f")
-            else:
-                print "Could not detect main branch. No checkout/master branch created."
-
-        # auto-set this variable if invoked with --use-client-spec
-        if self.useClientSpec_from_options:
-            system("git config --bool git-p4.useclientspec true")
-
-        return True
-
-class P4Branches(Command):
-    def __init__(self):
-        Command.__init__(self)
-        self.options = [ ]
-        self.description = ("Shows the git branches that hold imports and their "
-                            + "corresponding perforce depot paths")
-        self.verbose = False
-
-    def run(self, args):
-        if originP4BranchesExist():
-            createOrUpdateBranchesFromOrigin()
-
-        cmdline = "git rev-parse --symbolic "
-        cmdline += " --remotes"
-
-        for line in read_pipe_lines(cmdline):
-            line = line.strip()
-
-            if not line.startswith('p4/') or line == "p4/HEAD":
-                continue
-            branch = line
-
-            log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
-            settings = extractSettingsGitLog(log)
-
-            print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
-        return True
-
-class HelpFormatter(optparse.IndentedHelpFormatter):
-    def __init__(self):
-        optparse.IndentedHelpFormatter.__init__(self)
-
-    def format_description(self, description):
-        if description:
-            return description + "\n"
-        else:
-            return ""
-
-def printUsage(commands):
-    print "usage: %s <command> [options]" % sys.argv[0]
-    print ""
-    print "valid commands: %s" % ", ".join(commands)
-    print ""
-    print "Try %s <command> --help for command specific help." % sys.argv[0]
-    print ""
-
-commands = {
-    "debug" : P4Debug,
-    "submit" : P4Submit,
-    "commit" : P4Submit,
-    "sync" : P4Sync,
-    "rebase" : P4Rebase,
-    "clone" : P4Clone,
-    "rollback" : P4RollBack,
-    "branches" : P4Branches
-}
-
-
-def main():
-    if len(sys.argv[1:]) == 0:
-        printUsage(commands.keys())
-        sys.exit(2)
-
-    cmd = ""
-    cmdName = sys.argv[1]
-    try:
-        klass = commands[cmdName]
-        cmd = klass()
-    except KeyError:
-        print "unknown command %s" % cmdName
-        print ""
-        printUsage(commands.keys())
-        sys.exit(2)
-
-    options = cmd.options
-    cmd.gitdir = os.environ.get("GIT_DIR", None)
-
-    args = sys.argv[2:]
-
-    if len(options) > 0:
-        if cmd.needsGit:
-            options.append(optparse.make_option("--git-dir", dest="gitdir"))
-
-        parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
-                                       options,
-                                       description = cmd.description,
-                                       formatter = HelpFormatter())
-
-        (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
-    global verbose
-    verbose = cmd.verbose
-    if cmd.needsGit:
-        if cmd.gitdir == None:
-            cmd.gitdir = os.path.abspath(".git")
-            if not isValidGitDir(cmd.gitdir):
-                cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
-                if os.path.exists(cmd.gitdir):
-                    cdup = read_pipe("git rev-parse --show-cdup").strip()
-                    if len(cdup) > 0:
-                        chdir(cdup);
-
-        if not isValidGitDir(cmd.gitdir):
-            if isValidGitDir(cmd.gitdir + "/.git"):
-                cmd.gitdir += "/.git"
-            else:
-                die("fatal: cannot locate git repository at %s" % cmd.gitdir)
-
-        os.environ["GIT_DIR"] = cmd.gitdir
-
-    if not cmd.run(args):
-        parser.print_help()
-        sys.exit(2)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/contrib/fast-import/git-p4.README b/contrib/fast-import/git-p4.README
new file mode 100644 (file)
index 0000000..cec5ecf
--- /dev/null
@@ -0,0 +1,12 @@
+The git-p4 script moved to the top-level of the git source directory.
+
+Invoke it as any other git command, like "git p4 clone", for instance.
+
+Note that the top-level git-p4.py script is now the source.  It is
+built using make to git-p4, which will be installed.
+
+Windows users can copy the git-p4.py source script directly, possibly
+invoking it through a batch file called "git-p4.bat" in the same folder.
+It should contain just one line:
+
+    @python "%~d0%~p0git-p4.py" %*
diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat
deleted file mode 100644 (file)
index 9f97e88..0000000
+++ /dev/null
@@ -1 +0,0 @@
-@python "%~d0%~p0git-p4" %*
index 2cfe1b936b0feef1bd40947ce6ab249f62a6ad55..36b6feebe00060e4d522c506f12d2848673dbee6 100755 (executable)
@@ -7,7 +7,7 @@ USAGE="$me rev-list-args"
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
-. git-sh-setup
+. $(git --exec-path)/git-sh-setup
 require_work_tree
 cd_to_toplevel
 
diff --git a/contrib/subtree/.gitignore b/contrib/subtree/.gitignore
new file mode 100644 (file)
index 0000000..7e77c9d
--- /dev/null
@@ -0,0 +1,5 @@
+*~
+git-subtree.xml
+git-subtree.1
+mainline
+subproj
diff --git a/contrib/subtree/COPYING b/contrib/subtree/COPYING
new file mode 100644 (file)
index 0000000..d511905
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    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, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/contrib/subtree/INSTALL b/contrib/subtree/INSTALL
new file mode 100644 (file)
index 0000000..7ab0cf4
--- /dev/null
@@ -0,0 +1,28 @@
+HOW TO INSTALL git-subtree
+==========================
+
+First, build from the top source directory.
+
+Then, in contrib/subtree, run:
+
+  make
+  make install
+  make install-doc
+
+If you used configure to do the main build the git-subtree build will
+pick up those settings.  If not, you will likely have to provide a
+value for prefix:
+
+  make prefix=<some dir>
+  make prefix=<some dir> install
+  make prefix=<some dir> install-doc
+
+To run tests first copy git-subtree to the main build area so the
+newly-built git can find it:
+
+  cp git-subtree ../..
+
+Then:
+
+  make test
+
diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile
new file mode 100644 (file)
index 0000000..05cdd5c
--- /dev/null
@@ -0,0 +1,52 @@
+-include ../../config.mak.autogen
+-include ../../config.mak
+
+prefix ?= /usr/local
+mandir ?= $(prefix)/share/man
+libexecdir ?= $(prefix)/libexec/git-core
+gitdir ?= $(shell git --exec-path)
+man1dir ?= $(mandir)/man1
+
+gitver ?= $(word 3,$(shell git --version))
+
+# this should be set to a 'standard' bsd-type install program
+INSTALL ?= install
+
+ASCIIDOC_CONF      = ../../Documentation/asciidoc.conf
+MANPAGE_NORMAL_XSL =  ../../Documentation/manpage-normal.xsl
+
+GIT_SUBTREE_SH := git-subtree.sh
+GIT_SUBTREE    := git-subtree
+
+GIT_SUBTREE_DOC := git-subtree.1
+GIT_SUBTREE_XML := git-subtree.xml
+GIT_SUBTREE_TXT := git-subtree.txt
+
+all: $(GIT_SUBTREE)
+
+$(GIT_SUBTREE): $(GIT_SUBTREE_SH)
+       cp $< $@ && chmod +x $@
+
+doc: $(GIT_SUBTREE_DOC)
+
+install: $(GIT_SUBTREE)
+       $(INSTALL) -m 755 $(GIT_SUBTREE) $(libexecdir)
+
+install-doc: install-man
+
+install-man: $(GIT_SUBTREE_DOC)
+       $(INSTALL) -m 644 $^ $(man1dir)
+
+$(GIT_SUBTREE_DOC): $(GIT_SUBTREE_XML)
+       xmlto -m $(MANPAGE_NORMAL_XSL)  man $^
+
+$(GIT_SUBTREE_XML): $(GIT_SUBTREE_TXT)
+       asciidoc -b docbook -d manpage -f $(ASCIIDOC_CONF) \
+               -agit_version=$(gitver) $^
+
+test:
+       $(MAKE) -C t/ test
+
+clean:
+       rm -f *~ *.xml *.html *.1
+       rm -rf subproj mainline
diff --git a/contrib/subtree/README b/contrib/subtree/README
new file mode 100644 (file)
index 0000000..c686b4a
--- /dev/null
@@ -0,0 +1,8 @@
+
+Please read git-subtree.txt for documentation.
+
+Please don't contact me using github mail; it's slow, ugly, and worst of
+all, redundant. Email me instead at apenwarr@gmail.com and I'll be happy to
+help.
+
+Avery
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
new file mode 100755 (executable)
index 0000000..920c664
--- /dev/null
@@ -0,0 +1,712 @@
+#!/bin/bash
+#
+# git-subtree.sh: split/join git repositories in subdirectories of this one
+#
+# Copyright (C) 2009 Avery Pennarun <apenwarr@gmail.com>
+#
+if [ $# -eq 0 ]; then
+    set -- -h
+fi
+OPTS_SPEC="\
+git subtree add   --prefix=<prefix> <commit>
+git subtree merge --prefix=<prefix> <commit>
+git subtree pull  --prefix=<prefix> <repository> <refspec...>
+git subtree push  --prefix=<prefix> <repository> <refspec...>
+git subtree split --prefix=<prefix> <commit...>
+--
+h,help        show the help
+q             quiet
+d             show debug messages
+P,prefix=     the name of the subdir to split out
+m,message=    use the given message as the commit message for the merge commit
+ options for 'split'
+annotate=     add a prefix to commit message of new commits
+b,branch=     create a new branch from the split subtree
+ignore-joins  ignore prior --rejoin commits
+onto=         try connecting new tree to an existing one
+rejoin        merge the new branch back into HEAD
+ options for 'add', 'merge', 'pull' and 'push'
+squash        merge subtree changes as a single commit
+"
+eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
+
+PATH=$PATH:$(git --exec-path)
+. git-sh-setup
+
+require_work_tree
+
+quiet=
+branch=
+debug=
+command=
+onto=
+rejoin=
+ignore_joins=
+annotate=
+squash=
+message=
+
+debug()
+{
+       if [ -n "$debug" ]; then
+               echo "$@" >&2
+       fi
+}
+
+say()
+{
+       if [ -z "$quiet" ]; then
+               echo "$@" >&2
+       fi
+}
+
+assert()
+{
+       if "$@"; then
+               :
+       else
+               die "assertion failed: " "$@"
+       fi
+}
+
+
+#echo "Options: $*"
+
+while [ $# -gt 0 ]; do
+       opt="$1"
+       shift
+       case "$opt" in
+               -q) quiet=1 ;;
+               -d) debug=1 ;;
+               --annotate) annotate="$1"; shift ;;
+               --no-annotate) annotate= ;;
+               -b) branch="$1"; shift ;;
+               -P) prefix="$1"; shift ;;
+               -m) message="$1"; shift ;;
+               --no-prefix) prefix= ;;
+               --onto) onto="$1"; shift ;;
+               --no-onto) onto= ;;
+               --rejoin) rejoin=1 ;;
+               --no-rejoin) rejoin= ;;
+               --ignore-joins) ignore_joins=1 ;;
+               --no-ignore-joins) ignore_joins= ;;
+               --squash) squash=1 ;;
+               --no-squash) squash= ;;
+               --) break ;;
+               *) die "Unexpected option: $opt" ;;
+       esac
+done
+
+command="$1"
+shift
+case "$command" in
+       add|merge|pull) default= ;;
+       split|push) default="--default HEAD" ;;
+       *) die "Unknown command '$command'" ;;
+esac
+
+if [ -z "$prefix" ]; then
+       die "You must provide the --prefix option."
+fi
+
+case "$command" in
+       add) [ -e "$prefix" ] && 
+               die "prefix '$prefix' already exists." ;;
+       *)   [ -e "$prefix" ] || 
+               die "'$prefix' does not exist; use 'git subtree add'" ;;
+esac
+
+dir="$(dirname "$prefix/.")"
+
+if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" ]; then
+       revs=$(git rev-parse $default --revs-only "$@") || exit $?
+       dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $?
+       if [ -n "$dirs" ]; then
+               die "Error: Use --prefix instead of bare filenames."
+       fi
+fi
+
+debug "command: {$command}"
+debug "quiet: {$quiet}"
+debug "revs: {$revs}"
+debug "dir: {$dir}"
+debug "opts: {$*}"
+debug
+
+cache_setup()
+{
+       cachedir="$GIT_DIR/subtree-cache/$$"
+       rm -rf "$cachedir" || die "Can't delete old cachedir: $cachedir"
+       mkdir -p "$cachedir" || die "Can't create new cachedir: $cachedir"
+       mkdir -p "$cachedir/notree" || die "Can't create new cachedir: $cachedir/notree"
+       debug "Using cachedir: $cachedir" >&2
+}
+
+cache_get()
+{
+       for oldrev in $*; do
+               if [ -r "$cachedir/$oldrev" ]; then
+                       read newrev <"$cachedir/$oldrev"
+                       echo $newrev
+               fi
+       done
+}
+
+cache_miss()
+{
+       for oldrev in $*; do
+               if [ ! -r "$cachedir/$oldrev" ]; then
+                       echo $oldrev
+               fi
+       done
+}
+
+check_parents()
+{
+       missed=$(cache_miss $*)
+       for miss in $missed; do
+               if [ ! -r "$cachedir/notree/$miss" ]; then
+                       debug "  incorrect order: $miss"
+               fi
+       done
+}
+
+set_notree()
+{
+       echo "1" > "$cachedir/notree/$1"
+}
+
+cache_set()
+{
+       oldrev="$1"
+       newrev="$2"
+       if [ "$oldrev" != "latest_old" \
+            -a "$oldrev" != "latest_new" \
+            -a -e "$cachedir/$oldrev" ]; then
+               die "cache for $oldrev already exists!"
+       fi
+       echo "$newrev" >"$cachedir/$oldrev"
+}
+
+rev_exists()
+{
+       if git rev-parse "$1" >/dev/null 2>&1; then
+               return 0
+       else
+               return 1
+       fi
+}
+
+rev_is_descendant_of_branch()
+{
+       newrev="$1"
+       branch="$2"
+       branch_hash=$(git rev-parse $branch)
+       match=$(git rev-list -1 $branch_hash ^$newrev)
+
+       if [ -z "$match" ]; then
+               return 0
+       else
+               return 1
+       fi
+}
+
+# if a commit doesn't have a parent, this might not work.  But we only want
+# to remove the parent from the rev-list, and since it doesn't exist, it won't
+# be there anyway, so do nothing in that case.
+try_remove_previous()
+{
+       if rev_exists "$1^"; then
+               echo "^$1^"
+       fi
+}
+
+find_latest_squash()
+{
+       debug "Looking for latest squash ($dir)..."
+       dir="$1"
+       sq=
+       main=
+       sub=
+       git log --grep="^git-subtree-dir: $dir/*\$" \
+               --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD |
+       while read a b junk; do
+               debug "$a $b $junk"
+               debug "{{$sq/$main/$sub}}"
+               case "$a" in
+                       START) sq="$b" ;;
+                       git-subtree-mainline:) main="$b" ;;
+                       git-subtree-split:) sub="$b" ;;
+                       END)
+                               if [ -n "$sub" ]; then
+                                       if [ -n "$main" ]; then
+                                               # a rejoin commit?
+                                               # Pretend its sub was a squash.
+                                               sq="$sub"
+                                       fi
+                                       debug "Squash found: $sq $sub"
+                                       echo "$sq" "$sub"
+                                       break
+                               fi
+                               sq=
+                               main=
+                               sub=
+                               ;;
+               esac
+       done
+}
+
+find_existing_splits()
+{
+       debug "Looking for prior splits..."
+       dir="$1"
+       revs="$2"
+       main=
+       sub=
+       git log --grep="^git-subtree-dir: $dir/*\$" \
+               --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
+       while read a b junk; do
+               case "$a" in
+                       START) sq="$b" ;;
+                       git-subtree-mainline:) main="$b" ;;
+                       git-subtree-split:) sub="$b" ;;
+                       END)
+                               debug "  Main is: '$main'"
+                               if [ -z "$main" -a -n "$sub" ]; then
+                                       # squash commits refer to a subtree
+                                       debug "  Squash: $sq from $sub"
+                                       cache_set "$sq" "$sub"
+                               fi
+                               if [ -n "$main" -a -n "$sub" ]; then
+                                       debug "  Prior: $main -> $sub"
+                                       cache_set $main $sub
+                                       cache_set $sub $sub
+                                       try_remove_previous "$main"
+                                       try_remove_previous "$sub"
+                               fi
+                               main=
+                               sub=
+                               ;;
+               esac
+       done
+}
+
+copy_commit()
+{
+       # We're going to set some environment vars here, so
+       # do it in a subshell to get rid of them safely later
+       debug copy_commit "{$1}" "{$2}" "{$3}"
+       git log -1 --pretty=format:'%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b' "$1" |
+       (
+               read GIT_AUTHOR_NAME
+               read GIT_AUTHOR_EMAIL
+               read GIT_AUTHOR_DATE
+               read GIT_COMMITTER_NAME
+               read GIT_COMMITTER_EMAIL
+               read GIT_COMMITTER_DATE
+               export  GIT_AUTHOR_NAME \
+                       GIT_AUTHOR_EMAIL \
+                       GIT_AUTHOR_DATE \
+                       GIT_COMMITTER_NAME \
+                       GIT_COMMITTER_EMAIL \
+                       GIT_COMMITTER_DATE
+               (echo -n "$annotate"; cat ) |
+               git commit-tree "$2" $3  # reads the rest of stdin
+       ) || die "Can't copy commit $1"
+}
+
+add_msg()
+{
+       dir="$1"
+       latest_old="$2"
+       latest_new="$3"
+       if [ -n "$message" ]; then
+               commit_message="$message"
+       else
+               commit_message="Add '$dir/' from commit '$latest_new'"
+       fi
+       cat <<-EOF
+               $commit_message
+               
+               git-subtree-dir: $dir
+               git-subtree-mainline: $latest_old
+               git-subtree-split: $latest_new
+       EOF
+}
+
+add_squashed_msg()
+{
+       if [ -n "$message" ]; then
+               echo "$message"
+       else
+               echo "Merge commit '$1' as '$2'"
+       fi
+}
+
+rejoin_msg()
+{
+       dir="$1"
+       latest_old="$2"
+       latest_new="$3"
+       if [ -n "$message" ]; then
+               commit_message="$message"
+       else
+               commit_message="Split '$dir/' into commit '$latest_new'"
+       fi
+       cat <<-EOF
+               $commit_message
+               
+               git-subtree-dir: $dir
+               git-subtree-mainline: $latest_old
+               git-subtree-split: $latest_new
+       EOF
+}
+
+squash_msg()
+{
+       dir="$1"
+       oldsub="$2"
+       newsub="$3"
+       newsub_short=$(git rev-parse --short "$newsub")
+       
+       if [ -n "$oldsub" ]; then
+               oldsub_short=$(git rev-parse --short "$oldsub")
+               echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short"
+               echo
+               git log --pretty=tformat:'%h %s' "$oldsub..$newsub"
+               git log --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub"
+       else
+               echo "Squashed '$dir/' content from commit $newsub_short"
+       fi
+       
+       echo
+       echo "git-subtree-dir: $dir"
+       echo "git-subtree-split: $newsub"
+}
+
+toptree_for_commit()
+{
+       commit="$1"
+       git log -1 --pretty=format:'%T' "$commit" -- || exit $?
+}
+
+subtree_for_commit()
+{
+       commit="$1"
+       dir="$2"
+       git ls-tree "$commit" -- "$dir" |
+       while read mode type tree name; do
+               assert [ "$name" = "$dir" ]
+               assert [ "$type" = "tree" -o "$type" = "commit" ]
+               [ "$type" = "commit" ] && continue  # ignore submodules
+               echo $tree
+               break
+       done
+}
+
+tree_changed()
+{
+       tree=$1
+       shift
+       if [ $# -ne 1 ]; then
+               return 0   # weird parents, consider it changed
+       else
+               ptree=$(toptree_for_commit $1)
+               if [ "$ptree" != "$tree" ]; then
+                       return 0   # changed
+               else
+                       return 1   # not changed
+               fi
+       fi
+}
+
+new_squash_commit()
+{
+       old="$1"
+       oldsub="$2"
+       newsub="$3"
+       tree=$(toptree_for_commit $newsub) || exit $?
+       if [ -n "$old" ]; then
+               squash_msg "$dir" "$oldsub" "$newsub" | 
+                       git commit-tree "$tree" -p "$old" || exit $?
+       else
+               squash_msg "$dir" "" "$newsub" |
+                       git commit-tree "$tree" || exit $?
+       fi
+}
+
+copy_or_skip()
+{
+       rev="$1"
+       tree="$2"
+       newparents="$3"
+       assert [ -n "$tree" ]
+
+       identical=
+       nonidentical=
+       p=
+       gotparents=
+       for parent in $newparents; do
+               ptree=$(toptree_for_commit $parent) || exit $?
+               [ -z "$ptree" ] && continue
+               if [ "$ptree" = "$tree" ]; then
+                       # an identical parent could be used in place of this rev.
+                       identical="$parent"
+               else
+                       nonidentical="$parent"
+               fi
+               
+               # sometimes both old parents map to the same newparent;
+               # eliminate duplicates
+               is_new=1
+               for gp in $gotparents; do
+                       if [ "$gp" = "$parent" ]; then
+                               is_new=
+                               break
+                       fi
+               done
+               if [ -n "$is_new" ]; then
+                       gotparents="$gotparents $parent"
+                       p="$p -p $parent"
+               fi
+       done
+       
+       if [ -n "$identical" ]; then
+               echo $identical
+       else
+               copy_commit $rev $tree "$p" || exit $?
+       fi
+}
+
+ensure_clean()
+{
+       if ! git diff-index HEAD --exit-code --quiet 2>&1; then
+               die "Working tree has modifications.  Cannot add."
+       fi
+       if ! git diff-index --cached HEAD --exit-code --quiet 2>&1; then
+               die "Index has modifications.  Cannot add."
+       fi
+}
+
+cmd_add()
+{
+       if [ -e "$dir" ]; then
+               die "'$dir' already exists.  Cannot add."
+       fi
+
+       ensure_clean
+       
+       if [ $# -eq 1 ]; then
+               "cmd_add_commit" "$@"
+       elif [ $# -eq 2 ]; then
+               "cmd_add_repository" "$@"
+       else
+           say "error: parameters were '$@'"
+           die "Provide either a refspec or a repository and refspec."
+       fi
+}
+
+cmd_add_repository()
+{
+       echo "git fetch" "$@"
+       repository=$1
+       refspec=$2
+       git fetch "$@" || exit $?
+       revs=FETCH_HEAD
+       set -- $revs
+       cmd_add_commit "$@"
+}
+
+cmd_add_commit()
+{
+       revs=$(git rev-parse $default --revs-only "$@") || exit $?
+       set -- $revs
+       rev="$1"
+       
+       debug "Adding $dir as '$rev'..."
+       git read-tree --prefix="$dir" $rev || exit $?
+       git checkout -- "$dir" || exit $?
+       tree=$(git write-tree) || exit $?
+       
+       headrev=$(git rev-parse HEAD) || exit $?
+       if [ -n "$headrev" -a "$headrev" != "$rev" ]; then
+               headp="-p $headrev"
+       else
+               headp=
+       fi
+       
+       if [ -n "$squash" ]; then
+               rev=$(new_squash_commit "" "" "$rev") || exit $?
+               commit=$(add_squashed_msg "$rev" "$dir" |
+                        git commit-tree $tree $headp -p "$rev") || exit $?
+       else
+               commit=$(add_msg "$dir" "$headrev" "$rev" |
+                        git commit-tree $tree $headp -p "$rev") || exit $?
+       fi
+       git reset "$commit" || exit $?
+       
+       say "Added dir '$dir'"
+}
+
+cmd_split()
+{
+       debug "Splitting $dir..."
+       cache_setup || exit $?
+       
+       if [ -n "$onto" ]; then
+               debug "Reading history for --onto=$onto..."
+               git rev-list $onto |
+               while read rev; do
+                       # the 'onto' history is already just the subdir, so
+                       # any parent we find there can be used verbatim
+                       debug "  cache: $rev"
+                       cache_set $rev $rev
+               done
+       fi
+       
+       if [ -n "$ignore_joins" ]; then
+               unrevs=
+       else
+               unrevs="$(find_existing_splits "$dir" "$revs")"
+       fi
+       
+       # We can't restrict rev-list to only $dir here, because some of our
+       # parents have the $dir contents the root, and those won't match.
+       # (and rev-list --follow doesn't seem to solve this)
+       grl='git rev-list --topo-order --reverse --parents $revs $unrevs'
+       revmax=$(eval "$grl" | wc -l)
+       revcount=0
+       createcount=0
+       eval "$grl" |
+       while read rev parents; do
+               revcount=$(($revcount + 1))
+               say -n "$revcount/$revmax ($createcount)\r"
+               debug "Processing commit: $rev"
+               exists=$(cache_get $rev)
+               if [ -n "$exists" ]; then
+                       debug "  prior: $exists"
+                       continue
+               fi
+               createcount=$(($createcount + 1))
+               debug "  parents: $parents"
+               newparents=$(cache_get $parents)
+               debug "  newparents: $newparents"
+               
+               tree=$(subtree_for_commit $rev "$dir")
+               debug "  tree is: $tree"
+
+               check_parents $parents
+               
+               # ugly.  is there no better way to tell if this is a subtree
+               # vs. a mainline commit?  Does it matter?
+               if [ -z $tree ]; then
+                       set_notree $rev
+                       if [ -n "$newparents" ]; then
+                               cache_set $rev $rev
+                       fi
+                       continue
+               fi
+
+               newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
+               debug "  newrev is: $newrev"
+               cache_set $rev $newrev
+               cache_set latest_new $newrev
+               cache_set latest_old $rev
+       done || exit $?
+       latest_new=$(cache_get latest_new)
+       if [ -z "$latest_new" ]; then
+               die "No new revisions were found"
+       fi
+       
+       if [ -n "$rejoin" ]; then
+               debug "Merging split branch into HEAD..."
+               latest_old=$(cache_get latest_old)
+               git merge -s ours \
+                       -m "$(rejoin_msg $dir $latest_old $latest_new)" \
+                       $latest_new >&2 || exit $?
+       fi
+       if [ -n "$branch" ]; then
+               if rev_exists "refs/heads/$branch"; then
+                       if ! rev_is_descendant_of_branch $latest_new $branch; then
+                               die "Branch '$branch' is not an ancestor of commit '$latest_new'."
+                       fi
+                       action='Updated'
+               else
+                       action='Created'
+               fi
+               git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $?
+               say "$action branch '$branch'"
+       fi
+       echo $latest_new
+       exit 0
+}
+
+cmd_merge()
+{
+       revs=$(git rev-parse $default --revs-only "$@") || exit $?
+       ensure_clean
+       
+       set -- $revs
+       if [ $# -ne 1 ]; then
+               die "You must provide exactly one revision.  Got: '$revs'"
+       fi
+       rev="$1"
+       
+       if [ -n "$squash" ]; then
+               first_split="$(find_latest_squash "$dir")"
+               if [ -z "$first_split" ]; then
+                       die "Can't squash-merge: '$dir' was never added."
+               fi
+               set $first_split
+               old=$1
+               sub=$2
+               if [ "$sub" = "$rev" ]; then
+                       say "Subtree is already at commit $rev."
+                       exit 0
+               fi
+               new=$(new_squash_commit "$old" "$sub" "$rev") || exit $?
+               debug "New squash commit: $new"
+               rev="$new"
+       fi
+
+       version=$(git version)
+       if [ "$version" \< "git version 1.7" ]; then
+               if [ -n "$message" ]; then
+                       git merge -s subtree --message="$message" $rev
+               else
+                       git merge -s subtree $rev
+               fi
+       else
+               if [ -n "$message" ]; then
+                       git merge -Xsubtree="$prefix" --message="$message" $rev
+               else
+                       git merge -Xsubtree="$prefix" $rev
+               fi
+       fi
+}
+
+cmd_pull()
+{
+       ensure_clean
+       git fetch "$@" || exit $?
+       revs=FETCH_HEAD
+       set -- $revs
+       cmd_merge "$@"
+}
+
+cmd_push()
+{
+       if [ $# -ne 2 ]; then
+           die "You must provide <repository> <refspec>"
+       fi
+       if [ -e "$dir" ]; then
+           repository=$1
+           refspec=$2
+           echo "git push using: " $repository $refspec
+           git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec
+       else
+           die "'$dir' must already exist. Try 'git subtree add'."
+       fi
+}
+
+"cmd_$command" "$@"
diff --git a/contrib/subtree/git-subtree.txt b/contrib/subtree/git-subtree.txt
new file mode 100644 (file)
index 0000000..0c44fda
--- /dev/null
@@ -0,0 +1,366 @@
+git-subtree(1)
+==============
+
+NAME
+----
+git-subtree - Merge subtrees together and split repository into subtrees
+
+
+SYNOPSIS
+--------
+[verse]
+'git subtree' add   -P <prefix> <commit>
+'git subtree' pull  -P <prefix> <repository> <refspec...>
+'git subtree' push  -P <prefix> <repository> <refspec...>
+'git subtree' merge -P <prefix> <commit>
+'git subtree' split -P <prefix> [OPTIONS] [<commit>]
+
+
+DESCRIPTION
+-----------
+Subtrees allow subprojects to be included within a subdirectory
+of the main project, optionally including the subproject's
+entire history.
+
+For example, you could include the source code for a library
+as a subdirectory of your application.
+
+Subtrees are not to be confused with submodules, which are meant for
+the same task. Unlike submodules, subtrees do not need any special
+constructions (like .gitmodule files or gitlinks) be present in
+your repository, and do not force end-users of your
+repository to do anything special or to understand how subtrees
+work. A subtree is just a subdirectory that can be
+committed to, branched, and merged along with your project in
+any way you want.
+
+They are also not to be confused with using the subtree merge
+strategy. The main difference is that, besides merging
+the other project as a subdirectory, you can also extract the
+entire history of a subdirectory from your project and make it
+into a standalone project. Unlike the subtree merge strategy
+you can alternate back and forth between these
+two operations. If the standalone library gets updated, you can
+automatically merge the changes into your project; if you
+update the library inside your project, you can "split" the
+changes back out again and merge them back into the library
+project.
+
+For example, if a library you made for one application ends up being
+useful elsewhere, you can extract its entire history and publish
+that as its own git repository, without accidentally
+intermingling the history of your application project.
+
+[TIP]
+In order to keep your commit messages clean, we recommend that
+people split their commits between the subtrees and the main
+project as much as possible.  That is, if you make a change that
+affects both the library and the main application, commit it in
+two pieces.  That way, when you split the library commits out
+later, their descriptions will still make sense.  But if this
+isn't important to you, it's not *necessary*.  git subtree will
+simply leave out the non-library-related parts of the commit
+when it splits it out into the subproject later.
+
+
+COMMANDS
+--------
+add::
+       Create the <prefix> subtree by importing its contents
+       from the given <refspec> or <repository> and remote <refspec>.
+       A new commit is created automatically, joining the imported
+       project's history with your own.  With '--squash', imports
+       only a single commit from the subproject, rather than its
+       entire history.
+
+merge::
+       Merge recent changes up to <commit> into the <prefix>
+       subtree.  As with normal 'git merge', this doesn't
+       remove your own local changes; it just merges those
+       changes into the latest <commit>.  With '--squash',
+       creates only one commit that contains all the changes,
+       rather than merging in the entire history.
+
+       If you use '--squash', the merge direction doesn't
+       always have to be forward; you can use this command to
+       go back in time from v2.5 to v2.4, for example.  If your
+       merge introduces a conflict, you can resolve it in the
+       usual ways.
+       
+pull::
+       Exactly like 'merge', but parallels 'git pull' in that
+       it fetches the given commit from the specified remote
+       repository.
+       
+push::
+       Does a 'split' (see above) using the <prefix> supplied
+       and then does a 'git push' to push the result to the 
+       repository and refspec. This can be used to push your
+       subtree to different branches of the remote repository.
+
+split::
+       Extract a new, synthetic project history from the
+       history of the <prefix> subtree.  The new history
+       includes only the commits (including merges) that
+       affected <prefix>, and each of those commits now has the
+       contents of <prefix> at the root of the project instead
+       of in a subdirectory.  Thus, the newly created history
+       is suitable for export as a separate git repository.
+       
+       After splitting successfully, a single commit id is
+       printed to stdout.  This corresponds to the HEAD of the
+       newly created tree, which you can manipulate however you
+       want.
+       
+       Repeated splits of exactly the same history are
+       guaranteed to be identical (ie. to produce the same
+       commit ids).  Because of this, if you add new commits
+       and then re-split, the new commits will be attached as
+       commits on top of the history you generated last time,
+       so 'git merge' and friends will work as expected.
+       
+       Note that if you use '--squash' when you merge, you
+       should usually not just '--rejoin' when you split.
+
+
+OPTIONS
+-------
+-q::
+--quiet::
+       Suppress unnecessary output messages on stderr.
+
+-d::
+--debug::
+       Produce even more unnecessary output messages on stderr.
+
+-P <prefix>::
+--prefix=<prefix>::
+       Specify the path in the repository to the subtree you
+       want to manipulate.  This option is mandatory
+       for all commands.
+
+-m <message>::
+--message=<message>::
+       This option is only valid for add, merge and pull (unsure).
+       Specify <message> as the commit message for the merge commit.
+
+
+OPTIONS FOR add, merge, push, pull
+----------------------------------
+--squash::
+       This option is only valid for add, merge, push and pull
+       commands.
+
+       Instead of merging the entire history from the subtree
+       project, produce only a single commit that contains all
+       the differences you want to merge, and then merge that
+       new commit into your project.
+       
+       Using this option helps to reduce log clutter. People
+       rarely want to see every change that happened between
+       v1.0 and v1.1 of the library they're using, since none of the
+       interim versions were ever included in their application.
+       
+       Using '--squash' also helps avoid problems when the same
+       subproject is included multiple times in the same
+       project, or is removed and then re-added.  In such a
+       case, it doesn't make sense to combine the histories
+       anyway, since it's unclear which part of the history
+       belongs to which subtree.
+       
+       Furthermore, with '--squash', you can switch back and
+       forth between different versions of a subtree, rather
+       than strictly forward.  'git subtree merge --squash'
+       always adjusts the subtree to match the exactly
+       specified commit, even if getting to that commit would
+       require undoing some changes that were added earlier.
+       
+       Whether or not you use '--squash', changes made in your
+       local repository remain intact and can be later split
+       and send upstream to the subproject.
+
+
+OPTIONS FOR split
+-----------------
+--annotate=<annotation>::
+       This option is only valid for the split command.
+
+       When generating synthetic history, add <annotation> as a
+       prefix to each commit message.  Since we're creating new
+       commits with the same commit message, but possibly
+       different content, from the original commits, this can help
+       to differentiate them and avoid confusion.
+       
+       Whenever you split, you need to use the same
+       <annotation>, or else you don't have a guarantee that
+       the new re-created history will be identical to the old
+       one.  That will prevent merging from working correctly. 
+       git subtree tries to make it work anyway, particularly
+       if you use --rejoin, but it may not always be effective.
+
+-b <branch>::
+--branch=<branch>::
+       This option is only valid for the split command.
+
+       After generating the synthetic history, create a new
+       branch called <branch> that contains the new history. 
+       This is suitable for immediate pushing upstream. 
+       <branch> must not already exist.
+
+--ignore-joins::
+       This option is only valid for the split command.
+
+       If you use '--rejoin', git subtree attempts to optimize
+       its history reconstruction to generate only the new
+       commits since the last '--rejoin'.  '--ignore-join'
+       disables this behaviour, forcing it to regenerate the
+       entire history.  In a large project, this can take a
+       long time.
+
+--onto=<onto>::
+       This option is only valid for the split command.
+
+       If your subtree was originally imported using something
+       other than git subtree, its history may not match what
+       git subtree is expecting.  In that case, you can specify
+       the commit id <onto> that corresponds to the first
+       revision of the subproject's history that was imported
+       into your project, and git subtree will attempt to build
+       its history from there.
+       
+       If you used 'git subtree add', you should never need
+       this option.
+
+--rejoin::
+       This option is only valid for the split command.
+
+       After splitting, merge the newly created synthetic
+       history back into your main project.  That way, future
+       splits can search only the part of history that has
+       been added since the most recent --rejoin.
+       
+       If your split commits end up merged into the upstream
+       subproject, and then you want to get the latest upstream
+       version, this will allow git's merge algorithm to more
+       intelligently avoid conflicts (since it knows these
+       synthetic commits are already part of the upstream
+       repository).
+       
+       Unfortunately, using this option results in 'git log'
+       showing an extra copy of every new commit that was
+       created (the original, and the synthetic one).
+       
+       If you do all your merges with '--squash', don't use
+       '--rejoin' when you split, because you don't want the
+       subproject's history to be part of your project anyway.
+
+
+EXAMPLE 1. Add command
+----------------------
+Let's assume that you have a local repository that you would like
+to add an external vendor library to. In this case we will add the
+git-subtree repository as a subdirectory of your already existing
+git-extensions repository in ~/git-extensions/:
+
+       $ git subtree add --prefix=git-subtree --squash \
+               git://github.com/apenwarr/git-subtree.git master
+
+'master' needs to be a valid remote ref and can be a different branch
+name
+
+You can omit the --squash flag, but doing so will increase the number
+of commits that are incldued 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
+in our git-extensions repository.
+
+EXAMPLE 2. Extract a subtree using commit, merge and pull
+---------------------------------------------------------
+Let's use the repository for the git source code as an example.
+First, get your own copy of the git.git repository:
+
+       $ git clone git://git.kernel.org/pub/scm/git/git.git test-git
+       $ cd test-git
+
+gitweb (commit 1130ef3) was merged into git as of commit
+0a8f4f0, after which it was no longer maintained separately. 
+But imagine it had been maintained separately, and we wanted to
+extract git's changes to gitweb since that time, to share with
+the upstream.  You could do this:
+
+       $ git subtree split --prefix=gitweb --annotate='(split) ' \
+               0a8f4f0^.. --onto=1130ef3 --rejoin \
+               --branch gitweb-latest
+        $ gitk gitweb-latest
+        $ git push git@github.com:whatever/gitweb.git gitweb-latest:master
+        
+(We use '0a8f4f0^..' because that means "all the changes from
+0a8f4f0 to the current version, including 0a8f4f0 itself.")
+
+If gitweb had originally been merged using 'git subtree add' (or
+a previous split had already been done with --rejoin specified)
+then you can do all your splits without having to remember any
+weird commit ids:
+
+       $ git subtree split --prefix=gitweb --annotate='(split) ' --rejoin \
+               --branch gitweb-latest2
+
+And you can merge changes back in from the upstream project just
+as easily:
+
+       $ git subtree pull --prefix=gitweb \
+               git@github.com:whatever/gitweb.git master
+
+Or, using '--squash', you can actually rewind to an earlier
+version of gitweb:
+
+       $ git subtree merge --prefix=gitweb --squash gitweb-latest~10
+
+Then make some changes:
+
+       $ date >gitweb/myfile
+       $ git add gitweb/myfile
+       $ git commit -m 'created myfile'
+
+And fast forward again:
+
+       $ git subtree merge --prefix=gitweb --squash gitweb-latest
+
+And notice that your change is still intact:
+       
+       $ ls -l gitweb/myfile
+
+And you can split it out and look at your changes versus
+the standard gitweb:
+
+       git log gitweb-latest..$(git subtree split --prefix=gitweb)
+
+EXAMPLE 3. Extract a subtree using branch
+-----------------------------------------
+Suppose you have a source directory with many files and
+subdirectories, and you want to extract the lib directory to its own
+git project. Here's a short way to do it:
+
+First, make the new repository wherever you want:
+
+       $ <go to the new location>
+       $ git init --bare
+
+Back in your original directory:
+
+       $ git subtree split --prefix=lib --annotate="(split)" -b split
+
+Then push the new branch onto the new empty repository:
+
+       $ git push <new-repo> split:master
+
+
+AUTHOR
+------
+Written by Avery Pennarun <apenwarr@gmail.com>
+
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/contrib/subtree/t/Makefile b/contrib/subtree/t/Makefile
new file mode 100644 (file)
index 0000000..c864810
--- /dev/null
@@ -0,0 +1,69 @@
+# Run tests
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+#GIT_TEST_OPTS=--verbose --debug
+SHELL_PATH ?= $(SHELL)
+PERL_PATH ?= /usr/bin/perl
+TAR ?= $(TAR)
+RM ?= rm -f
+PROVE ?= prove
+DEFAULT_TEST_TARGET ?= test
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+
+all: $(DEFAULT_TEST_TARGET)
+
+test: pre-clean $(TEST_LINT)
+       $(MAKE) aggregate-results-and-cleanup
+
+prove: pre-clean $(TEST_LINT)
+       @echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+       $(MAKE) clean
+
+$(T):
+       @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+pre-clean:
+       $(RM) -r test-results
+
+clean:
+       $(RM) -r 'trash directory'.* test-results
+       $(RM) -r valgrind/bin
+       $(RM) .prove
+
+test-lint: test-lint-duplicates test-lint-executable
+
+test-lint-duplicates:
+       @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
+               test -z "$$dups" || { \
+               echo >&2 "duplicate test numbers:" $$dups; exit 1; }
+
+test-lint-executable:
+       @bad=`for i in $(T); do test -x "$$i" || echo $$i; done` && \
+               test -z "$$bad" || { \
+               echo >&2 "non-executable tests:" $$bad; exit 1; }
+
+aggregate-results-and-cleanup: $(T)
+       $(MAKE) aggregate-results
+       $(MAKE) clean
+
+aggregate-results:
+       for f in ../../../t/test-results/t*-*.counts; do \
+               echo "$$f"; \
+       done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
+
+valgrind:
+       $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind"
+
+test-results:
+       mkdir -p test-results
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind
diff --git a/contrib/subtree/t/t7900-subtree.sh b/contrib/subtree/t/t7900-subtree.sh
new file mode 100755 (executable)
index 0000000..bc2eeb0
--- /dev/null
@@ -0,0 +1,508 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Avery Pennaraum
+#
+test_description='Basic porcelain support for subtrees
+
+This test verifies the basic operation of the merge, pull, add
+and split subcommands of git subtree.
+'
+
+export TEST_DIRECTORY=$(pwd)/../../../t
+
+. ../../../t/test-lib.sh
+
+create()
+{
+       echo "$1" >"$1"
+       git add "$1"
+}
+
+
+check_equal()
+{
+       test_debug 'echo'
+       test_debug "echo \"check a:\" \"{$1}\""
+       test_debug "echo \"      b:\" \"{$2}\""
+       if [ "$1" = "$2" ]; then
+               return 0
+       else
+               return 1
+       fi
+}
+
+fixnl()
+{
+       t=""
+       while read x; do
+               t="$t$x "
+       done
+       echo $t
+}
+
+multiline()
+{
+       while read x; do
+               set -- $x
+               for d in "$@"; do
+                       echo "$d"
+               done
+       done
+}
+
+undo()
+{
+       git reset --hard HEAD~
+}
+
+last_commit_message()
+{
+       git log --pretty=format:%s -1
+}
+
+# 1
+test_expect_success 'init subproj' '
+        test_create_repo subproj
+'
+
+# To the subproject!
+cd subproj
+
+# 2
+test_expect_success 'add sub1' '
+        create sub1 &&
+        git commit -m "sub1" &&
+        git branch sub1 &&
+        git branch -m master subproj
+'
+
+# 3
+test_expect_success 'add sub2' '
+        create sub2 &&
+        git commit -m "sub2" &&
+        git branch sub2
+'
+
+# 4
+test_expect_success 'add sub3' '
+        create sub3 &&
+        git commit -m "sub3" &&
+        git branch sub3
+'
+
+# Back to mainline
+cd ..
+
+# 5
+test_expect_success 'add main4' '
+        create main4 &&
+        git commit -m "main4" &&
+        git branch -m master mainline &&
+        git branch subdir
+'
+
+# 6
+test_expect_success 'fetch subproj history' '
+        git fetch ./subproj sub1 &&
+        git branch sub1 FETCH_HEAD
+'
+
+# 7
+test_expect_success 'no subtree exists in main tree' '
+        test_must_fail git subtree merge --prefix=subdir sub1
+'
+
+# 8
+test_expect_success 'no pull from non-existant subtree' '
+        test_must_fail git subtree pull --prefix=subdir ./subproj sub1
+'
+
+# 9
+test_expect_success 'check if --message works for add' '
+        git subtree add --prefix=subdir --message="Added subproject" sub1 &&
+        check_equal ''"$(last_commit_message)"'' "Added subproject" &&
+        undo
+'
+
+# 10
+test_expect_success 'check if --message works as -m and --prefix as -P' '
+        git subtree add -P subdir -m "Added subproject using git subtree" sub1 &&
+        check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
+        undo
+'
+
+# 11
+test_expect_success 'check if --message works with squash too' '
+        git subtree add -P subdir -m "Added subproject with squash" --squash sub1 &&
+        check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
+        undo
+'
+
+# 12
+test_expect_success 'add subproj to mainline' '
+        git subtree add --prefix=subdir/ FETCH_HEAD &&
+        check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
+'
+
+# 13
+# this shouldn't actually do anything, since FETCH_HEAD is already a parent
+test_expect_success 'merge fetched subproj' '
+        git merge -m "merge -s -ours" -s ours FETCH_HEAD
+'
+
+# 14
+test_expect_success 'add main-sub5' '
+        create subdir/main-sub5 &&
+        git commit -m "main-sub5"
+'
+
+# 15
+test_expect_success 'add main6' '
+        create main6 &&
+        git commit -m "main6 boring"
+'
+
+# 16
+test_expect_success 'add main-sub7' '
+        create subdir/main-sub7 &&
+        git commit -m "main-sub7"
+'
+
+# 17
+test_expect_success 'fetch new subproj history' '
+        git fetch ./subproj sub2 &&
+        git branch sub2 FETCH_HEAD
+'
+
+# 18
+test_expect_success 'check if --message works for merge' '
+        git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 &&
+        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
+        undo
+'
+
+# 19
+test_expect_success 'check if --message for merge works with squash too' '
+        git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 &&
+        check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
+        undo
+'
+
+# 20
+test_expect_success 'merge new subproj history into subdir' '
+        git subtree merge --prefix=subdir FETCH_HEAD &&
+        git branch pre-split &&
+        check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline"
+'
+
+# 21
+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 "cat expected" &&
+        test_debug "echo -n actual: " &&
+        test_debug "cat actual" &&
+        test_cmp expected actual &&
+        rm -f expected actual
+'
+
+# 22
+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 "cat expected" &&
+        test_debug "echo -n actual: " &&
+        test_debug "cat actual" &&
+        test_cmp expected actual
+#        rm -f expected actual
+'
+
+# 23
+test_expect_success 'check if --message works for split+rejoin' '
+        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+        git branch spl1 "$spl1" &&
+        check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
+        undo
+'
+
+# 24
+test_expect_success 'check split with --branch' '
+        spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
+        undo &&
+        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 &&
+        check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
+'
+
+# 25
+test_expect_success 'check split with --branch for an existing branch' '
+        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+        undo &&
+        git branch splitbr2 sub1 &&
+        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 &&
+        check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
+'
+
+# 26
+test_expect_success 'check split with --branch for an incompatible branch' '
+        test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir
+'
+
+
+# 27
+test_expect_success 'check split+rejoin' '
+        spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
+        undo &&
+        git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin &&
+        check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'"
+'
+
+# 28
+test_expect_success 'add main-sub8' '
+        create subdir/main-sub8 &&
+        git commit -m "main-sub8"
+'
+
+# To the subproject!
+cd ./subproj
+
+# 29
+test_expect_success 'merge split into subproj' '
+        git fetch .. spl1 &&
+        git branch spl1 FETCH_HEAD &&
+        git merge FETCH_HEAD
+'
+
+# 30
+test_expect_success 'add sub9' '
+        create sub9 &&
+        git commit -m "sub9"
+'
+
+# Back to mainline
+cd ..
+
+# 31
+test_expect_success 'split for sub8' '
+        split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"''
+        git branch split2 "$split2"
+'
+
+# 32
+test_expect_success 'add main-sub10' '
+        create subdir/main-sub10 &&
+        git commit -m "main-sub10"
+'
+
+# 33
+test_expect_success 'split for sub10' '
+        spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' &&
+        git branch spl3 "$spl3"
+'
+
+# To the subproject!
+cd ./subproj
+
+# 34
+test_expect_success 'merge split into subproj' '
+        git fetch .. spl3 &&
+        git branch spl3 FETCH_HEAD &&
+        git merge FETCH_HEAD &&
+        git branch subproj-merge-spl3
+'
+
+chkm="main4 main6"
+chkms="main-sub10 main-sub5 main-sub7 main-sub8"
+chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl)
+chks="sub1 sub2 sub3 sub9"
+chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl)
+
+# 35
+test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
+        subfiles=''"$(git ls-files | fixnl)"'' &&
+        check_equal "$subfiles" "$chkms $chks"
+'
+
+# 36
+test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
+        allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+        check_equal "$allchanges" "$chkms $chks"
+'
+
+# Back to mainline
+cd ..
+
+# 37
+test_expect_success 'pull from subproj' '
+        git fetch ./subproj subproj-merge-spl3 &&
+        git branch subproj-merge-spl3 FETCH_HEAD &&
+        git subtree pull --prefix=subdir ./subproj subproj-merge-spl3
+'
+
+# 38
+test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
+        mainfiles=''"$(git ls-files | fixnl)"'' &&
+        check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub"
+'
+
+# 39
+test_expect_success 'make sure each filename changed exactly once in the entire history' '
+        # main-sub?? and /subdir/main-sub?? both change, because those are the
+        # changes that were split into their own history.  And subdir/sub?? never
+        # change, since they were *only* changed in the subtree branch.
+        allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' &&
+        check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"''
+'
+
+# 40
+test_expect_success 'make sure the --rejoin commits never make it into subproj' '
+        check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
+'
+
+# 41
+test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
+        # They are meaningless to subproj since one side of the merge refers to the mainline
+        check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
+'
+
+# prepare second pair of repositories
+mkdir test2
+cd test2
+
+# 42
+test_expect_success 'init main' '
+        test_create_repo main
+'
+
+cd main
+
+# 43
+test_expect_success 'add main1' '
+        create main1 &&
+        git commit -m "main1"
+'
+
+cd ..
+
+# 44
+test_expect_success 'init sub' '
+        test_create_repo sub
+'
+
+cd sub
+
+# 45
+test_expect_success 'add sub2' '
+        create sub2 &&
+        git commit -m "sub2"
+'
+
+cd ../main
+
+# check if split can find proper base without --onto
+
+# 46
+test_expect_success 'add sub as subdir in main' '
+        git fetch ../sub master &&
+        git branch sub2 FETCH_HEAD &&
+        git subtree add --prefix subdir sub2
+'
+
+cd ../sub
+
+# 47
+test_expect_success 'add sub3' '
+        create sub3 &&
+        git commit -m "sub3"
+'
+
+cd ../main
+
+# 48
+test_expect_success 'merge from sub' '
+        git fetch ../sub master &&
+        git branch sub3 FETCH_HEAD &&
+        git subtree merge --prefix subdir sub3
+'
+
+# 49
+test_expect_success 'add main-sub4' '
+        create subdir/main-sub4 &&
+        git commit -m "main-sub4"
+'
+
+# 50
+test_expect_success 'split for main-sub4 without --onto' '
+        git subtree split --prefix subdir --branch mainsub4
+'
+
+# at this point, the new commit parent should be sub3 if it is not,
+# something went wrong (the "newparent" of "master~" commit should
+# have been sub3, but it was not, because its cache was not set to
+# itself)
+
+# 51
+test_expect_success 'check that the commit parent is sub3' '
+        check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
+'
+
+# 52
+test_expect_success 'add main-sub5' '
+        mkdir subdir2 &&
+        create subdir2/main-sub5 &&
+        git commit -m "main-sub5"
+'
+
+# 53
+test_expect_success 'split for main-sub5 without --onto' '
+        # also test that we still can split out an entirely new subtree
+        # if the parent of the first commit in the tree is not empty,
+        # then the new subtree has accidently been attached to something
+        git subtree split --prefix subdir2 --branch mainsub5 &&
+        check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
+'
+
+# make sure no patch changes more than one file.  The original set of commits
+# changed only one file each.  A multi-file change would imply that we pruned
+# commits too aggressively.
+joincommits()
+{
+       commit=
+       all=
+       while read x y; do
+               #echo "{$x}" >&2
+               if [ -z "$x" ]; then
+                       continue
+               elif [ "$x" = "commit:" ]; then
+                       if [ -n "$commit" ]; then
+                               echo "$commit $all"
+                               all=
+                       fi
+                       commit="$y"
+               else
+                       all="$all $y"
+               fi
+       done
+       echo "$commit $all"
+}
+
+# 54
+test_expect_success 'verify one file change per commit' '
+        x= &&
+        list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
+#        test_debug "echo HERE" &&
+#        test_debug "echo ''"$list"''" &&
+        (git log --pretty=format:'"'commit: %H'"' | joincommits |
+        (       while read commit a b; do
+                       test_debug "echo Verifying commit "''"$commit"''
+                       test_debug "echo a: "''"$a"''
+                       test_debug "echo b: "''"$b"''
+                       check_equal "$b" ""
+                       x=1
+               done
+               check_equal "$x" 1
+        ))
+'
+
+test_done
diff --git a/contrib/subtree/todo b/contrib/subtree/todo
new file mode 100644 (file)
index 0000000..7e44b00
--- /dev/null
@@ -0,0 +1,50 @@
+
+       delete tempdir
+
+       'git subtree rejoin' option to do the same as --rejoin, eg. after a
+         rebase
+
+       --prefix doesn't force the subtree correctly in merge/pull:
+       "-s subtree" should be given an explicit subtree option?
+               There doesn't seem to be a way to do this.  We'd have to
+               patch git-merge-subtree.  Ugh.
+               (but we could avoid this problem by generating squashes with
+               exactly the right subtree structure, rather than using
+               subtree merge...)
+
+       add a 'push' subcommand to parallel 'pull'
+       
+       add a 'log' subcommand to see what's new in a subtree?
+
+       add to-submodule and from-submodule commands
+
+       automated tests for --squash stuff
+
+       "add" command non-obviously requires a commitid; would be easier if
+               it had a "pull" sort of mode instead
+
+       "pull" and "merge" commands should fail if you've never merged
+               that --prefix before
+               
+       docs should provide an example of "add"
+       
+       note that the initial split doesn't *have* to have a commitid
+               specified... that's just an optimization
+
+       if you try to add (or maybe merge?) with an invalid commitid, you
+               get a misleading "prefix must end with /" message from
+               one of the other git tools that git-subtree calls.  Should
+               detect this situation and print the *real* problem.
+       
+       "pull --squash" should do fetch-synthesize-merge, but instead just
+               does "pull" directly, which doesn't work at all.
+
+       make a 'force-update' that does what 'add' does even if the subtree
+               already exists.  That way we can help people who imported
+               subtrees "incorrectly" (eg. by just copying in the files) in
+               the past.
+
+       guess --prefix automatically if possible based on pwd
+
+       make a 'git subtree grafts' that automatically expands --squash'd
+               commits so you can see the full history if you want it.
diff --git a/date.c b/date.c
index a5055ca09dc1fafce2b9434c4fda02ad4f8e117f..1fdcf7c6eae0386552c5d7e069f5242fca3c2e0e 100644 (file)
--- a/date.c
+++ b/date.c
@@ -86,83 +86,98 @@ static int local_tzoffset(unsigned long time)
        return offset * eastwest;
 }
 
-const char *show_date_relative(unsigned long time, int tz,
+void show_date_relative(unsigned long time, int tz,
                               const struct timeval *now,
-                              char *timebuf,
-                              size_t timebuf_size)
+                              struct strbuf *timebuf)
 {
        unsigned long diff;
-       if (now->tv_sec < time)
-               return "in the future";
+       if (now->tv_sec < time) {
+               strbuf_addstr(timebuf, _("in the future"));
+               return;
+       }
        diff = now->tv_sec - time;
        if (diff < 90) {
-               snprintf(timebuf, timebuf_size, "%lu seconds ago", diff);
-               return timebuf;
+               strbuf_addf(timebuf,
+                        Q_("%lu second ago", "%lu seconds ago", diff), diff);
+               return;
        }
        /* Turn it into minutes */
        diff = (diff + 30) / 60;
        if (diff < 90) {
-               snprintf(timebuf, timebuf_size, "%lu minutes ago", diff);
-               return timebuf;
+               strbuf_addf(timebuf,
+                        Q_("%lu minute ago", "%lu minutes ago", diff), diff);
+               return;
        }
        /* Turn it into hours */
        diff = (diff + 30) / 60;
        if (diff < 36) {
-               snprintf(timebuf, timebuf_size, "%lu hours ago", diff);
-               return timebuf;
+               strbuf_addf(timebuf,
+                        Q_("%lu hour ago", "%lu hours ago", diff), diff);
+               return;
        }
        /* We deal with number of days from here on */
        diff = (diff + 12) / 24;
        if (diff < 14) {
-               snprintf(timebuf, timebuf_size, "%lu days ago", diff);
-               return timebuf;
+               strbuf_addf(timebuf,
+                        Q_("%lu day ago", "%lu days ago", diff), diff);
+               return;
        }
        /* Say weeks for the past 10 weeks or so */
        if (diff < 70) {
-               snprintf(timebuf, timebuf_size, "%lu weeks ago", (diff + 3) / 7);
-               return timebuf;
+               strbuf_addf(timebuf,
+                        Q_("%lu week ago", "%lu weeks ago", (diff + 3) / 7),
+                        (diff + 3) / 7);
+               return;
        }
        /* Say months for the past 12 months or so */
        if (diff < 365) {
-               snprintf(timebuf, timebuf_size, "%lu months ago", (diff + 15) / 30);
-               return timebuf;
+               strbuf_addf(timebuf,
+                        Q_("%lu month ago", "%lu months ago", (diff + 15) / 30),
+                        (diff + 15) / 30);
+               return;
        }
        /* Give years and months for 5 years or so */
        if (diff < 1825) {
                unsigned long totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
                unsigned long years = totalmonths / 12;
                unsigned long months = totalmonths % 12;
-               int n;
-               n = snprintf(timebuf, timebuf_size, "%lu year%s",
-                               years, (years > 1 ? "s" : ""));
-               if (months)
-                       snprintf(timebuf + n, timebuf_size - n,
-                                       ", %lu month%s ago",
-                                       months, (months > 1 ? "s" : ""));
-               else
-                       snprintf(timebuf + n, timebuf_size - n, " ago");
-               return timebuf;
+               if (months) {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addf(&sb, Q_("%lu year", "%lu years", years), years);
+                       /* TRANSLATORS: "%s" is "<n> years" */
+                       strbuf_addf(timebuf,
+                                Q_("%s, %lu month ago", "%s, %lu months ago", months),
+                                sb.buf, months);
+                       strbuf_release(&sb);
+               } else
+                       strbuf_addf(timebuf,
+                                Q_("%lu year ago", "%lu years ago", years), years);
+               return;
        }
        /* Otherwise, just years. Centuries is probably overkill. */
-       snprintf(timebuf, timebuf_size, "%lu years ago", (diff + 183) / 365);
-       return timebuf;
+       strbuf_addf(timebuf,
+                Q_("%lu year ago", "%lu years ago", (diff + 183) / 365),
+                (diff + 183) / 365);
 }
 
 const char *show_date(unsigned long time, int tz, enum date_mode mode)
 {
        struct tm *tm;
-       static char timebuf[200];
+       static struct strbuf timebuf = STRBUF_INIT;
 
        if (mode == DATE_RAW) {
-               snprintf(timebuf, sizeof(timebuf), "%lu %+05d", time, tz);
-               return timebuf;
+               strbuf_reset(&timebuf);
+               strbuf_addf(&timebuf, "%lu %+05d", time, tz);
+               return timebuf.buf;
        }
 
        if (mode == DATE_RELATIVE) {
                struct timeval now;
+
+               strbuf_reset(&timebuf);
                gettimeofday(&now, NULL);
-               return show_date_relative(time, tz, &now,
-                                         timebuf, sizeof(timebuf));
+               show_date_relative(time, tz, &now, &timebuf);
+               return timebuf.buf;
        }
 
        if (mode == DATE_LOCAL)
@@ -171,23 +186,25 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
        tm = time_to_tm(time, tz);
        if (!tm)
                return NULL;
+
+       strbuf_reset(&timebuf);
        if (mode == DATE_SHORT)
-               sprintf(timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
+               strbuf_addf(&timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
                                tm->tm_mon + 1, tm->tm_mday);
        else if (mode == DATE_ISO8601)
-               sprintf(timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
+               strbuf_addf(&timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+05d",
                                tm->tm_year + 1900,
                                tm->tm_mon + 1,
                                tm->tm_mday,
                                tm->tm_hour, tm->tm_min, tm->tm_sec,
                                tz);
        else if (mode == DATE_RFC2822)
-               sprintf(timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
+               strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
                        weekday_names[tm->tm_wday], tm->tm_mday,
                        month_names[tm->tm_mon], tm->tm_year + 1900,
                        tm->tm_hour, tm->tm_min, tm->tm_sec, tz);
        else
-               sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
+               strbuf_addf(&timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
                                weekday_names[tm->tm_wday],
                                month_names[tm->tm_mon],
                                tm->tm_mday,
@@ -195,7 +212,7 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
                                tm->tm_year + 1900,
                                (mode == DATE_LOCAL) ? 0 : ' ',
                                tz);
-       return timebuf;
+       return timebuf.buf;
 }
 
 /*
index 3a36144687ae2f5bf7bb3afc914ddbada8d5ff93..f0b0010aedd67f9b34f43426c47e837dfe2a522b 100644 (file)
@@ -52,7 +52,7 @@ static int get_mode(const char *path, int *mode)
 }
 
 static int queue_diff(struct diff_options *o,
-               const char *name1, const char *name2)
+                     const char *name1, const char *name2)
 {
        int mode1 = 0, mode2 = 0;
 
@@ -63,10 +63,12 @@ static int queue_diff(struct diff_options *o,
                return error("file/directory conflict: %s, %s", name1, name2);
 
        if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
-               char buffer1[PATH_MAX], buffer2[PATH_MAX];
+               struct strbuf buffer1 = STRBUF_INIT;
+               struct strbuf buffer2 = STRBUF_INIT;
                struct string_list p1 = STRING_LIST_INIT_DUP;
                struct string_list p2 = STRING_LIST_INIT_DUP;
-               int len1 = 0, len2 = 0, i1, i2, ret = 0;
+               int i1, i2, ret = 0;
+               size_t len1 = 0, len2 = 0;
 
                if (name1 && read_directory(name1, &p1))
                        return -1;
@@ -76,53 +78,53 @@ static int queue_diff(struct diff_options *o,
                }
 
                if (name1) {
-                       len1 = strlen(name1);
-                       if (len1 > 0 && name1[len1 - 1] == '/')
-                               len1--;
-                       memcpy(buffer1, name1, len1);
-                       buffer1[len1++] = '/';
+                       strbuf_addstr(&buffer1, name1);
+                       if (buffer1.len && buffer1.buf[buffer1.len - 1] != '/')
+                               strbuf_addch(&buffer1, '/');
+                       len1 = buffer1.len;
                }
 
                if (name2) {
-                       len2 = strlen(name2);
-                       if (len2 > 0 && name2[len2 - 1] == '/')
-                               len2--;
-                       memcpy(buffer2, name2, len2);
-                       buffer2[len2++] = '/';
+                       strbuf_addstr(&buffer2, name2);
+                       if (buffer2.len && buffer2.buf[buffer2.len - 1] != '/')
+                               strbuf_addch(&buffer2, '/');
+                       len2 = buffer2.len;
                }
 
                for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
                        const char *n1, *n2;
                        int comp;
 
+                       strbuf_setlen(&buffer1, len1);
+                       strbuf_setlen(&buffer2, len2);
+
                        if (i1 == p1.nr)
                                comp = 1;
                        else if (i2 == p2.nr)
                                comp = -1;
                        else
-                               comp = strcmp(p1.items[i1].string,
-                                       p2.items[i2].string);
+                               comp = strcmp(p1.items[i1].string, p2.items[i2].string);
 
                        if (comp > 0)
                                n1 = NULL;
                        else {
-                               n1 = buffer1;
-                               strncpy(buffer1 + len1, p1.items[i1++].string,
-                                               PATH_MAX - len1);
+                               strbuf_addstr(&buffer1, p1.items[i1++].string);
+                               n1 = buffer1.buf;
                        }
 
                        if (comp < 0)
                                n2 = NULL;
                        else {
-                               n2 = buffer2;
-                               strncpy(buffer2 + len2, p2.items[i2++].string,
-                                               PATH_MAX - len2);
+                               strbuf_addstr(&buffer2, p2.items[i2++].string);
+                               n2 = buffer2.buf;
                        }
 
                        ret = queue_diff(o, n1, n2);
                }
                string_list_clear(&p1, 0);
                string_list_clear(&p2, 0);
+               strbuf_release(&buffer1);
+               strbuf_release(&buffer2);
 
                return ret;
        } else {
diff --git a/diff.c b/diff.c
index 377ec1ea4cd90524f7c7525846fc95c3a9e66920..77edd5086fff30af36e92a151377c285de21aca3 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -989,10 +989,74 @@ static void diff_words_flush(struct emit_callback *ecbdata)
                diff_words_show(ecbdata->diff_words);
 }
 
+static void diff_filespec_load_driver(struct diff_filespec *one)
+{
+       /* Use already-loaded driver */
+       if (one->driver)
+               return;
+
+       if (S_ISREG(one->mode))
+               one->driver = userdiff_find_by_path(one->path);
+
+       /* Fallback to default settings */
+       if (!one->driver)
+               one->driver = userdiff_find_by_name("default");
+}
+
+static const char *userdiff_word_regex(struct diff_filespec *one)
+{
+       diff_filespec_load_driver(one);
+       return one->driver->word_regex;
+}
+
+static void init_diff_words_data(struct emit_callback *ecbdata,
+                                struct diff_options *orig_opts,
+                                struct diff_filespec *one,
+                                struct diff_filespec *two)
+{
+       int i;
+       struct diff_options *o = xmalloc(sizeof(struct diff_options));
+       memcpy(o, orig_opts, sizeof(struct diff_options));
+
+       ecbdata->diff_words =
+               xcalloc(1, sizeof(struct diff_words_data));
+       ecbdata->diff_words->type = o->word_diff;
+       ecbdata->diff_words->opt = o;
+       if (!o->word_regex)
+               o->word_regex = userdiff_word_regex(one);
+       if (!o->word_regex)
+               o->word_regex = userdiff_word_regex(two);
+       if (!o->word_regex)
+               o->word_regex = diff_word_regex_cfg;
+       if (o->word_regex) {
+               ecbdata->diff_words->word_regex = (regex_t *)
+                       xmalloc(sizeof(regex_t));
+               if (regcomp(ecbdata->diff_words->word_regex,
+                           o->word_regex,
+                           REG_EXTENDED | REG_NEWLINE))
+                       die ("Invalid regular expression: %s",
+                            o->word_regex);
+       }
+       for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
+               if (o->word_diff == diff_words_styles[i].type) {
+                       ecbdata->diff_words->style =
+                               &diff_words_styles[i];
+                       break;
+               }
+       }
+       if (want_color(o->use_color)) {
+               struct diff_words_style *st = ecbdata->diff_words->style;
+               st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
+               st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
+               st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
+       }
+}
+
 static void free_diff_words_data(struct emit_callback *ecbdata)
 {
        if (ecbdata->diff_words) {
                diff_words_flush(ecbdata);
+               free (ecbdata->diff_words->opt);
                free (ecbdata->diff_words->minus.text.ptr);
                free (ecbdata->diff_words->minus.orig);
                free (ecbdata->diff_words->plus.text.ptr);
@@ -1379,8 +1443,8 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
 {
        int i, len, add, del, adds = 0, dels = 0;
        uintmax_t max_change = 0, max_len = 0;
-       int total_files = data->nr;
-       int width, name_width, graph_width, number_width = 4, count;
+       int total_files = data->nr, count;
+       int width, name_width, graph_width, number_width = 0, bin_width = 0;
        const char *reset, *add_c, *del_c;
        const char *line_prefix = "";
        int extra_shown = 0;
@@ -1416,8 +1480,21 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                if (max_len < len)
                        max_len = len;
 
-               if (file->is_binary || file->is_unmerged)
+               if (file->is_unmerged) {
+                       /* "Unmerged" is 8 characters */
+                       bin_width = bin_width < 8 ? 8 : bin_width;
                        continue;
+               }
+               if (file->is_binary) {
+                       /* "Bin XXX -> YYY bytes" */
+                       int w = 14 + decimal_width(file->added)
+                               + decimal_width(file->deleted);
+                       bin_width = bin_width < w ? w : bin_width;
+                       /* Display change counts aligned with "Bin" */
+                       number_width = 3;
+                       continue;
+               }
+
                if (max_change < change)
                        max_change = change;
        }
@@ -1442,12 +1519,22 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
         * stat_name_width fixes the maximum width of the filename,
         * and is also used to divide available columns if there
         * aren't enough.
+        *
+        * Binary files are displayed with "Bin XXX -> YYY bytes"
+        * instead of the change count and graph. This part is treated
+        * similarly to the graph part, except that it is not
+        * "scaled". If total width is too small to accomodate the
+        * guaranteed minimum width of the filename part and the
+        * separators and this message, this message will "overflow"
+        * making the line longer than the maximum width.
         */
 
        if (options->stat_width == -1)
-               width = term_columns();
+               width = term_columns() - options->output_prefix_length;
        else
                width = options->stat_width ? options->stat_width : 80;
+       number_width = decimal_width(max_change) > number_width ?
+               decimal_width(max_change) : number_width;
 
        if (options->stat_graph_width == -1)
                options->stat_graph_width = diff_stat_graph_width;
@@ -1461,10 +1548,14 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
 
        /*
         * First assign sizes that are wanted, ignoring available width.
+        * strlen("Bin XXX -> YYY bytes") == bin_width, and the part
+        * starting from "XXX" should fit in graph_width.
         */
-       graph_width = (options->stat_graph_width &&
-                      options->stat_graph_width < max_change) ?
-               options->stat_graph_width : max_change;
+       graph_width = max_change + 4 > bin_width ? max_change : bin_width - 4;
+       if (options->stat_graph_width &&
+           options->stat_graph_width < graph_width)
+               graph_width = options->stat_graph_width;
+
        name_width = (options->stat_name_width > 0 &&
                      options->stat_name_width < max_len) ?
                options->stat_name_width : max_len;
@@ -1473,8 +1564,12 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
         * Adjust adjustable widths not to exceed maximum width
         */
        if (name_width + number_width + 6 + graph_width > width) {
-               if (graph_width > width * 3/8 - number_width - 6)
+               if (graph_width > width * 3/8 - number_width - 6) {
                        graph_width = width * 3/8 - number_width - 6;
+                       if (graph_width < 6)
+                               graph_width = 6;
+               }
+
                if (options->stat_graph_width &&
                    graph_width > options->stat_graph_width)
                        graph_width = options->stat_graph_width;
@@ -1519,8 +1614,12 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                if (data->files[i]->is_binary) {
                        fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
-                       fprintf(options->file, "  Bin ");
-                       fprintf(options->file, "%s%"PRIuMAX"%s",
+                       fprintf(options->file, " %*s", number_width, "Bin");
+                       if (!added && !deleted) {
+                               putc('\n', options->file);
+                               continue;
+                       }
+                       fprintf(options->file, " %s%"PRIuMAX"%s",
                                del_c, deleted, reset);
                        fprintf(options->file, " -> ");
                        fprintf(options->file, "%s%"PRIuMAX"%s",
@@ -1532,7 +1631,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                else if (data->files[i]->is_unmerged) {
                        fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
-                       fprintf(options->file, "  Unmerged\n");
+                       fprintf(options->file, " Unmerged\n");
                        continue;
                }
 
@@ -1561,8 +1660,9 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                }
                fprintf(options->file, "%s", line_prefix);
                show_name(options->file, prefix, name, len);
-               fprintf(options->file, "%5"PRIuMAX"%s", added + deleted,
-                               added + deleted ? " " : "");
+               fprintf(options->file, " %*"PRIuMAX"%s",
+                       number_width, added + deleted,
+                       added + deleted ? " " : "");
                show_graph(options->file, '+', add, add_c, reset);
                show_graph(options->file, '-', del, del_c, reset);
                fprintf(options->file, "\n");
@@ -1593,17 +1693,16 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
                return;
 
        for (i = 0; i < data->nr; i++) {
-               if (!data->files[i]->is_binary &&
-                   !data->files[i]->is_unmerged) {
-                       int added = data->files[i]->added;
-                       int deleted= data->files[i]->deleted;
-                       if (!data->files[i]->is_renamed &&
-                           (added + deleted == 0)) {
-                               total_files--;
-                       } else {
-                               adds += added;
-                               dels += deleted;
-                       }
+               int added = data->files[i]->added;
+               int deleted= data->files[i]->deleted;
+
+               if (data->files[i]->is_unmerged)
+                       continue;
+               if (!data->files[i]->is_renamed && (added + deleted == 0)) {
+                       total_files--;
+               } else {
+                       adds += added;
+                       dels += deleted;
                }
        }
        if (options->output_prefix) {
@@ -2061,20 +2160,6 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two, char *pre
        emit_binary_diff_body(file, two, one, prefix);
 }
 
-static void diff_filespec_load_driver(struct diff_filespec *one)
-{
-       /* Use already-loaded driver */
-       if (one->driver)
-               return;
-
-       if (S_ISREG(one->mode))
-               one->driver = userdiff_find_by_path(one->path);
-
-       /* Fallback to default settings */
-       if (!one->driver)
-               one->driver = userdiff_find_by_name("default");
-}
-
 int diff_filespec_is_binary(struct diff_filespec *one)
 {
        if (one->is_binary == -1) {
@@ -2100,12 +2185,6 @@ static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespe
        return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
 }
 
-static const char *userdiff_word_regex(struct diff_filespec *one)
-{
-       diff_filespec_load_driver(one);
-       return one->driver->word_regex;
-}
-
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
 {
        if (!options->a_prefix)
@@ -2292,42 +2371,8 @@ static void builtin_diff(const char *name_a,
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
                else if (!prefixcmp(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
-               if (o->word_diff) {
-                       int i;
-
-                       ecbdata.diff_words =
-                               xcalloc(1, sizeof(struct diff_words_data));
-                       ecbdata.diff_words->type = o->word_diff;
-                       ecbdata.diff_words->opt = o;
-                       if (!o->word_regex)
-                               o->word_regex = userdiff_word_regex(one);
-                       if (!o->word_regex)
-                               o->word_regex = userdiff_word_regex(two);
-                       if (!o->word_regex)
-                               o->word_regex = diff_word_regex_cfg;
-                       if (o->word_regex) {
-                               ecbdata.diff_words->word_regex = (regex_t *)
-                                       xmalloc(sizeof(regex_t));
-                               if (regcomp(ecbdata.diff_words->word_regex,
-                                               o->word_regex,
-                                               REG_EXTENDED | REG_NEWLINE))
-                                       die ("Invalid regular expression: %s",
-                                                       o->word_regex);
-                       }
-                       for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
-                               if (o->word_diff == diff_words_styles[i].type) {
-                                       ecbdata.diff_words->style =
-                                               &diff_words_styles[i];
-                                       break;
-                               }
-                       }
-                       if (want_color(o->use_color)) {
-                               struct diff_words_style *st = ecbdata.diff_words->style;
-                               st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
-                               st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
-                               st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
-                       }
-               }
+               if (o->word_diff)
+                       init_diff_words_data(&ecbdata, o, one, two);
                xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
                              &xpp, &xecfg);
                if (o->word_diff)
@@ -2357,6 +2402,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
 {
        mmfile_t mf1, mf2;
        struct diffstat_file *data;
+       int same_contents;
 
        data = diffstat_add(diffstat, name_a, name_b);
 
@@ -2365,10 +2411,17 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
                return;
        }
 
+       same_contents = !hashcmp(one->sha1, two->sha1);
+
        if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
                data->is_binary = 1;
-               data->added = diff_filespec_size(two);
-               data->deleted = diff_filespec_size(one);
+               if (same_contents) {
+                       data->added = 0;
+                       data->deleted = 0;
+               } else {
+                       data->added = diff_filespec_size(two);
+                       data->deleted = diff_filespec_size(one);
+               }
        }
 
        else if (complete_rewrite) {
@@ -2378,7 +2431,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
                data->added = count_lines(two->data, two->size);
        }
 
-       else {
+       else if (!same_contents) {
                /* Crazy xdl interfaces.. */
                xpparam_t xpp;
                xdemitconf_t xecfg;
@@ -3136,6 +3189,7 @@ void diff_setup(struct diff_options *options)
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
        options->context = 3;
+       DIFF_OPT_SET(options, RENAME_EMPTY);
 
        options->change = diff_change;
        options->add_remove = diff_addremove;
@@ -3506,6 +3560,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
+       else if (!strcmp(arg, "--rename-empty"))
+               DIFF_OPT_SET(options, RENAME_EMPTY);
+       else if (!strcmp(arg, "--no-rename-empty"))
+               DIFF_OPT_CLR(options, RENAME_EMPTY);
        else if (!strcmp(arg, "--relative"))
                DIFF_OPT_SET(options, RELATIVE_NAME);
        else if (!prefixcmp(arg, "--relative=")) {
@@ -3525,9 +3583,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--ignore-space-at-eol"))
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--patience"))
-               DIFF_XDL_SET(options, PATIENCE_DIFF);
+               options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
-               DIFF_XDL_SET(options, HISTOGRAM_DIFF);
+               options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
 
        /* flags options */
        else if (!strcmp(arg, "--binary")) {
@@ -4399,6 +4457,12 @@ void diff_flush(struct diff_options *options)
 
        if (output_format & DIFF_FORMAT_PATCH) {
                if (separator) {
+                       if (options->output_prefix) {
+                               struct strbuf *msg = NULL;
+                               msg = options->output_prefix(options,
+                                       options->output_prefix_data);
+                               fwrite(msg->buf, msg->len, 1, stdout);
+                       }
                        putc(options->line_termination, options->file);
                        if (options->stat_sep) {
                                /* attach patch instead of inline */
diff --git a/diff.h b/diff.h
index cb687436a0ddb9a08fc1a9e9cec569233284e01f..e027650cb0ff2651e2e890e7f00753c15f5b3cff 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -60,7 +60,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_OPT_SILENT_ON_REMOVE    (1 <<  5)
 #define DIFF_OPT_FIND_COPIES_HARDER  (1 <<  6)
 #define DIFF_OPT_FOLLOW_RENAMES      (1 <<  7)
-/* (1 <<  8) unused */
+#define DIFF_OPT_RENAME_EMPTY        (1 <<  8)
 /* (1 <<  9) unused */
 #define DIFF_OPT_HAS_CHANGES         (1 << 10)
 #define DIFF_OPT_QUICK               (1 << 11)
@@ -91,6 +91,8 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_XDL_SET(opts, flag)    ((opts)->xdl_opts |= XDF_##flag)
 #define DIFF_XDL_CLR(opts, flag)    ((opts)->xdl_opts &= ~XDF_##flag)
 
+#define DIFF_WITH_ALG(opts, flag)   (((opts)->xdl_opts & ~XDF_DIFF_ALGORITHM_MASK) | XDF_##flag)
+
 enum diff_words_type {
        DIFF_WORDS_NONE = 0,
        DIFF_WORDS_PORCELAIN,
@@ -150,6 +152,7 @@ struct diff_options {
        diff_format_fn_t format_callback;
        void *format_callback_data;
        diff_prefix_fn_t output_prefix;
+       int output_prefix_length;
        void *output_prefix_data;
 };
 
index f639601c762ebbd12374fa739d1d63efaf265e2a..216a7a4bbcab189b5c3d1b7f58728b94b8d6aec8 100644 (file)
@@ -512,9 +512,15 @@ void diffcore_rename(struct diff_options *options)
                        else if (options->single_follow &&
                                 strcmp(options->single_follow, p->two->path))
                                continue; /* not interested */
+                       else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+                                is_empty_blob_sha1(p->two->sha1))
+                               continue;
                        else
                                locate_rename_dst(p->two, 1);
                }
+               else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
+                        is_empty_blob_sha1(p->one->sha1))
+                       continue;
                else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
                        /*
                         * If the source is a broken "delete", and
diff --git a/dir.c b/dir.c
index 0a78d00b545ac4f302ea89b6393773669907599e..c6a98cc8dd69609bc2bdbe202973ecbd79bcfa32 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -873,14 +873,14 @@ enum path_treatment {
 };
 
 static enum path_treatment treat_one_path(struct dir_struct *dir,
-                                         char *path, int *len,
+                                         struct strbuf *path,
                                          const struct path_simplify *simplify,
                                          int dtype, struct dirent *de)
 {
-       int exclude = excluded(dir, path, &dtype);
+       int exclude = excluded(dir, path->buf, &dtype);
        if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
-           && exclude_matches_pathspec(path, *len, simplify))
-               dir_add_ignored(dir, path, *len);
+           && exclude_matches_pathspec(path->buf, path->len, simplify))
+               dir_add_ignored(dir, path->buf, path->len);
 
        /*
         * Excluded? If we don't explicitly want to show
@@ -890,7 +890,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
                return path_ignored;
 
        if (dtype == DT_UNKNOWN)
-               dtype = get_dtype(de, path, *len);
+               dtype = get_dtype(de, path->buf, path->len);
 
        /*
         * Do we want to see just the ignored files?
@@ -907,9 +907,8 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
        default:
                return path_ignored;
        case DT_DIR:
-               memcpy(path + *len, "/", 2);
-               (*len)++;
-               switch (treat_directory(dir, path, *len, simplify)) {
+               strbuf_addch(path, '/');
+               switch (treat_directory(dir, path->buf, path->len, simplify)) {
                case show_directory:
                        if (exclude != !!(dir->flags
                                          & DIR_SHOW_IGNORED))
@@ -930,26 +929,21 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
 
 static enum path_treatment treat_path(struct dir_struct *dir,
                                      struct dirent *de,
-                                     char *path, int path_max,
+                                     struct strbuf *path,
                                      int baselen,
-                                     const struct path_simplify *simplify,
-                                     int *len)
+                                     const struct path_simplify *simplify)
 {
        int dtype;
 
        if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
                return path_ignored;
-       *len = strlen(de->d_name);
-       /* Ignore overly long pathnames! */
-       if (*len + baselen + 8 > path_max)
-               return path_ignored;
-       memcpy(path + baselen, de->d_name, *len + 1);
-       *len += baselen;
-       if (simplify_away(path, *len, simplify))
+       strbuf_setlen(path, baselen);
+       strbuf_addstr(path, de->d_name);
+       if (simplify_away(path->buf, path->len, simplify))
                return path_ignored;
 
        dtype = DTYPE(de);
-       return treat_one_path(dir, path, len, simplify, dtype, de);
+       return treat_one_path(dir, path, simplify, dtype, de);
 }
 
 /*
@@ -969,19 +963,19 @@ static int read_directory_recursive(struct dir_struct *dir,
        DIR *fdir = opendir(*base ? base : ".");
        int contents = 0;
        struct dirent *de;
-       char path[PATH_MAX + 1];
+       struct strbuf path = STRBUF_INIT;
 
        if (!fdir)
                return 0;
 
-       memcpy(path, base, baselen);
+       strbuf_add(&path, base, baselen);
 
        while ((de = readdir(fdir)) != NULL) {
-               int len;
-               switch (treat_path(dir, de, path, sizeof(path),
-                                  baselen, simplify, &len)) {
+               switch (treat_path(dir, de, &path, baselen, simplify)) {
                case path_recurse:
-                       contents += read_directory_recursive(dir, path, len, 0, simplify);
+                       contents += read_directory_recursive(dir, path.buf,
+                                                            path.len, 0,
+                                                            simplify);
                        continue;
                case path_ignored:
                        continue;
@@ -992,10 +986,11 @@ static int read_directory_recursive(struct dir_struct *dir,
                if (check_only)
                        goto exit_early;
                else
-                       dir_add_name(dir, pathlen);
+                       dir_add_name(dir, path.buf, path.len);
        }
 exit_early:
        closedir(fdir);
+       strbuf_release(&path);
 
        return contents;
 }
@@ -1058,8 +1053,8 @@ static int treat_leading_path(struct dir_struct *dir,
                              const char *path, int len,
                              const struct path_simplify *simplify)
 {
-       char pathbuf[PATH_MAX];
-       int baselen, blen;
+       struct strbuf sb = STRBUF_INIT;
+       int baselen, rc = 0;
        const char *cp;
 
        while (len && path[len - 1] == '/')
@@ -1074,19 +1069,22 @@ static int treat_leading_path(struct dir_struct *dir,
                        baselen = len;
                else
                        baselen = cp - path;
-               memcpy(pathbuf, path, baselen);
-               pathbuf[baselen] = '\0';
-               if (!is_directory(pathbuf))
-                       return 0;
-               if (simplify_away(pathbuf, baselen, simplify))
-                       return 0;
-               blen = baselen;
-               if (treat_one_path(dir, pathbuf, &blen, simplify,
+               strbuf_setlen(&sb, 0);
+               strbuf_add(&sb, path, baselen);
+               if (!is_directory(sb.buf))
+                       break;
+               if (simplify_away(sb.buf, sb.len, simplify))
+                       break;
+               if (treat_one_path(dir, &sb, simplify,
                                   DT_DIR, NULL) == path_ignored)
-                       return 0; /* do not recurse into it */
-               if (len <= baselen)
-                       return 1; /* finished checking */
+                       break; /* do not recurse into it */
+               if (len <= baselen) {
+                       rc = 1;
+                       break; /* finished checking */
+               }
        }
+       strbuf_release(&sb);
+       return rc;
 }
 
 int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
@@ -1172,22 +1170,32 @@ int is_empty_dir(const char *path)
        return ret;
 }
 
-int remove_dir_recursively(struct strbuf *path, int flag)
+static int remove_dir_recurse(struct strbuf *path, int flag, int *kept_up)
 {
        DIR *dir;
        struct dirent *e;
-       int ret = 0, original_len = path->len, len;
+       int ret = 0, original_len = path->len, len, kept_down = 0;
        int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
+       int keep_toplevel = (flag & REMOVE_DIR_KEEP_TOPLEVEL);
        unsigned char submodule_head[20];
 
        if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
-           !resolve_gitlink_ref(path->buf, "HEAD", submodule_head))
+           !resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
                /* Do not descend and nuke a nested git work tree. */
+               if (kept_up)
+                       *kept_up = 1;
                return 0;
+       }
 
+       flag &= ~REMOVE_DIR_KEEP_TOPLEVEL;
        dir = opendir(path->buf);
-       if (!dir)
-               return rmdir(path->buf);
+       if (!dir) {
+               /* an empty dir could be removed even if it is unreadble */
+               if (!keep_toplevel)
+                       return rmdir(path->buf);
+               else
+                       return -1;
+       }
        if (path->buf[original_len - 1] != '/')
                strbuf_addch(path, '/');
 
@@ -1202,7 +1210,7 @@ int remove_dir_recursively(struct strbuf *path, int flag)
                if (lstat(path->buf, &st))
                        ; /* fall thru */
                else if (S_ISDIR(st.st_mode)) {
-                       if (!remove_dir_recursively(path, only_empty))
+                       if (!remove_dir_recurse(path, flag, &kept_down))
                                continue; /* happy */
                } else if (!only_empty && !unlink(path->buf))
                        continue; /* happy, too */
@@ -1214,11 +1222,22 @@ int remove_dir_recursively(struct strbuf *path, int flag)
        closedir(dir);
 
        strbuf_setlen(path, original_len);
-       if (!ret)
+       if (!ret && !keep_toplevel && !kept_down)
                ret = rmdir(path->buf);
+       else if (kept_up)
+               /*
+                * report the uplevel that it is not an error that we
+                * did not rmdir() our directory.
+                */
+               *kept_up = !ret;
        return ret;
 }
 
+int remove_dir_recursively(struct strbuf *path, int flag)
+{
+       return remove_dir_recurse(path, flag, NULL);
+}
+
 void setup_standard_excludes(struct dir_struct *dir)
 {
        const char *path;
diff --git a/dir.h b/dir.h
index dd6947e1d46098732ff1d8c3f23087c1e7163fe9..58b6fc7c86df1bd5ac6f072672027a1474732ac6 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -102,6 +102,7 @@ extern void setup_standard_excludes(struct dir_struct *dir);
 
 #define REMOVE_DIR_EMPTY_ONLY 01
 #define REMOVE_DIR_KEEP_NESTED_GIT 02
+#define REMOVE_DIR_KEEP_TOPLEVEL 04
 extern int remove_dir_recursively(struct strbuf *path, int flag);
 
 /* tries to remove the path with empty directories along it, ignores ENOENT */
diff --git a/entry.c b/entry.c
index 852fea13955475c1e2fda9cfc25a63a54a1f61c7..17a6bccec64e0e523aacc124611c43bd818372e3 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -120,58 +120,15 @@ static int streaming_write_entry(struct cache_entry *ce, char *path,
                                 const struct checkout *state, int to_tempfile,
                                 int *fstat_done, struct stat *statbuf)
 {
-       struct git_istream *st;
-       enum object_type type;
-       unsigned long sz;
        int result = -1;
-       ssize_t kept = 0;
-       int fd = -1;
-
-       st = open_istream(ce->sha1, &type, &sz, filter);
-       if (!st)
-               return -1;
-       if (type != OBJ_BLOB)
-               goto close_and_exit;
+       int fd;
 
        fd = open_output_fd(path, ce, to_tempfile);
-       if (fd < 0)
-               goto close_and_exit;
-
-       for (;;) {
-               char buf[1024 * 16];
-               ssize_t wrote, holeto;
-               ssize_t readlen = read_istream(st, buf, sizeof(buf));
-
-               if (!readlen)
-                       break;
-               if (sizeof(buf) == readlen) {
-                       for (holeto = 0; holeto < readlen; holeto++)
-                               if (buf[holeto])
-                                       break;
-                       if (readlen == holeto) {
-                               kept += holeto;
-                               continue;
-                       }
-               }
-
-               if (kept && lseek(fd, kept, SEEK_CUR) == (off_t) -1)
-                       goto close_and_exit;
-               else
-                       kept = 0;
-               wrote = write_in_full(fd, buf, readlen);
-
-               if (wrote != readlen)
-                       goto close_and_exit;
-       }
-       if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
-                    write(fd, "", 1) != 1))
-               goto close_and_exit;
-       *fstat_done = fstat_output(fd, state, statbuf);
-
-close_and_exit:
-       close_istream(st);
-       if (0 <= fd)
+       if (0 <= fd) {
+               result = stream_blob_to_fd(fd, ce->sha1, filter, 1);
+               *fstat_done = fstat_output(fd, state, statbuf);
                result = close(fd);
+       }
        if (result && 0 <= fd)
                unlink(path);
        return result;
index c93b8f44df0171a0f923546813d00e8b8e837af1..d7e6c657631f05553250a1705ea6c77c375c4bf4 100644 (file)
@@ -52,7 +52,7 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
-enum push_default_type push_default = PUSH_DEFAULT_MATCHING;
+enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 #ifndef OBJECT_CREATION_MODE
 #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
 #endif
index 171e841531de7fd5b51aa26f639104382395b854..125fa6fabf503d29cb82b2ccbc92359abf95b0e8 100644 (file)
@@ -134,7 +134,7 @@ int execv_git_cmd(const char **argv) {
        trace_argv_printf(nargv, "trace: exec:");
 
        /* execvp() can only ever return if it fails */
-       execvp("git", (char **)nargv);
+       sane_execvp("git", (char **)nargv);
 
        trace_printf("trace: exec failed: %s\n", strerror(errno));
 
index a85275dc682d2bb8068f003b8281c3933488f010..eed97c8fa9f3e1624f69443e28f76d995e589b34 100644 (file)
@@ -2207,6 +2207,59 @@ static uintmax_t change_note_fanout(struct tree_entry *root,
        return do_change_note_fanout(root, root, hex_sha1, 0, path, 0, fanout);
 }
 
+/*
+ * Given a pointer into a string, parse a mark reference:
+ *
+ *   idnum ::= ':' bigint;
+ *
+ * Return the first character after the value in *endptr.
+ *
+ * Complain if the following character is not what is expected,
+ * either a space or end of the string.
+ */
+static uintmax_t parse_mark_ref(const char *p, char **endptr)
+{
+       uintmax_t mark;
+
+       assert(*p == ':');
+       p++;
+       mark = strtoumax(p, endptr, 10);
+       if (*endptr == p)
+               die("No value after ':' in mark: %s", command_buf.buf);
+       return mark;
+}
+
+/*
+ * Parse the mark reference, and complain if this is not the end of
+ * the string.
+ */
+static uintmax_t parse_mark_ref_eol(const char *p)
+{
+       char *end;
+       uintmax_t mark;
+
+       mark = parse_mark_ref(p, &end);
+       if (*end != '\0')
+               die("Garbage after mark: %s", command_buf.buf);
+       return mark;
+}
+
+/*
+ * Parse the mark reference, demanding a trailing space.  Return a
+ * pointer to the space.
+ */
+static uintmax_t parse_mark_ref_space(const char **p)
+{
+       uintmax_t mark;
+       char *end;
+
+       mark = parse_mark_ref(*p, &end);
+       if (*end != ' ')
+               die("Missing space after mark: %s", command_buf.buf);
+       *p = end;
+       return mark;
+}
+
 static void file_change_m(struct branch *b)
 {
        const char *p = command_buf.buf + 2;
@@ -2235,21 +2288,21 @@ static void file_change_m(struct branch *b)
        }
 
        if (*p == ':') {
-               char *x;
-               oe = find_mark(strtoumax(p + 1, &x, 10));
+               oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-               p = x;
-       } else if (!prefixcmp(p, "inline")) {
+       } else if (!prefixcmp(p, "inline ")) {
                inline_data = 1;
-               p += 6;
+               p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
-                       die("Invalid SHA1: %s", command_buf.buf);
+                       die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
+               if (*p != ' ')
+                       die("Missing space after SHA1: %s", command_buf.buf);
        }
-       if (*p++ != ' ')
-               die("Missing space after SHA1: %s", command_buf.buf);
+       assert(*p == ' ');
+       p++;  /* skip space */
 
        strbuf_reset(&uq);
        if (!unquote_c_style(&uq, p, &endp)) {
@@ -2407,21 +2460,21 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
        /* Now parse the notemodify command. */
        /* <dataref> or 'inline' */
        if (*p == ':') {
-               char *x;
-               oe = find_mark(strtoumax(p + 1, &x, 10));
+               oe = find_mark(parse_mark_ref_space(&p));
                hashcpy(sha1, oe->idx.sha1);
-               p = x;
-       } else if (!prefixcmp(p, "inline")) {
+       } else if (!prefixcmp(p, "inline ")) {
                inline_data = 1;
-               p += 6;
+               p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
-                       die("Invalid SHA1: %s", command_buf.buf);
+                       die("Invalid dataref: %s", command_buf.buf);
                oe = find_object(sha1);
                p += 40;
+               if (*p != ' ')
+                       die("Missing space after SHA1: %s", command_buf.buf);
        }
-       if (*p++ != ' ')
-               die("Missing space after SHA1: %s", command_buf.buf);
+       assert(*p == ' ');
+       p++;  /* skip space */
 
        /* <committish> */
        s = lookup_branch(p);
@@ -2430,7 +2483,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
                        die("Can't add a note on empty branch.");
                hashcpy(commit_sha1, s->sha1);
        } else if (*p == ':') {
-               uintmax_t commit_mark = strtoumax(p + 1, NULL, 10);
+               uintmax_t commit_mark = parse_mark_ref_eol(p);
                struct object_entry *commit_oe = find_mark(commit_mark);
                if (commit_oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", commit_mark);
@@ -2537,7 +2590,7 @@ static int parse_from(struct branch *b)
                hashcpy(b->branch_tree.versions[0].sha1, t);
                hashcpy(b->branch_tree.versions[1].sha1, t);
        } else if (*from == ':') {
-               uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+               uintmax_t idnum = parse_mark_ref_eol(from);
                struct object_entry *oe = find_mark(idnum);
                if (oe->type != OBJ_COMMIT)
                        die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2572,7 +2625,7 @@ static struct hash_list *parse_merge(unsigned int *count)
                if (s)
                        hashcpy(n->sha1, s->sha1);
                else if (*from == ':') {
-                       uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+                       uintmax_t idnum = parse_mark_ref_eol(from);
                        struct object_entry *oe = find_mark(idnum);
                        if (oe->type != OBJ_COMMIT)
                                die("Mark :%" PRIuMAX " not a commit", idnum);
@@ -2735,7 +2788,7 @@ static void parse_new_tag(void)
                type = OBJ_COMMIT;
        } else if (*from == ':') {
                struct object_entry *oe;
-               from_mark = strtoumax(from + 1, NULL, 10);
+               from_mark = parse_mark_ref_eol(from);
                oe = find_mark(from_mark);
                type = oe->type;
                hashcpy(sha1, oe->idx.sha1);
@@ -2867,18 +2920,13 @@ static void parse_cat_blob(void)
        /* cat-blob SP <object> LF */
        p = command_buf.buf + strlen("cat-blob ");
        if (*p == ':') {
-               char *x;
-               oe = find_mark(strtoumax(p + 1, &x, 10));
-               if (x == p + 1)
-                       die("Invalid mark: %s", command_buf.buf);
+               oe = find_mark(parse_mark_ref_eol(p));
                if (!oe)
                        die("Unknown mark: %s", command_buf.buf);
-               if (*x)
-                       die("Garbage after mark: %s", command_buf.buf);
                hashcpy(sha1, oe->idx.sha1);
        } else {
                if (get_sha1_hex(p, sha1))
-                       die("Invalid SHA1: %s", command_buf.buf);
+                       die("Invalid dataref: %s", command_buf.buf);
                if (p[40])
                        die("Garbage after SHA1: %s", command_buf.buf);
                oe = find_object(sha1);
@@ -2944,17 +2992,13 @@ static struct object_entry *parse_treeish_dataref(const char **p)
        struct object_entry *e;
 
        if (**p == ':') {       /* <mark> */
-               char *endptr;
-               e = find_mark(strtoumax(*p + 1, &endptr, 10));
-               if (endptr == *p + 1)
-                       die("Invalid mark: %s", command_buf.buf);
+               e = find_mark(parse_mark_ref_space(p));
                if (!e)
                        die("Unknown mark: %s", command_buf.buf);
-               *p = endptr;
                hashcpy(sha1, e->idx.sha1);
        } else {        /* <sha1> */
                if (get_sha1_hex(*p, sha1))
-                       die("Invalid SHA1: %s", command_buf.buf);
+                       die("Invalid dataref: %s", command_buf.buf);
                e = find_object(sha1);
                *p += 40;
        }
index 0608edae3fe7b07b852b30281fea978806798c99..7c2069c97234453cb6a0a774c1859b7055b791d8 100644 (file)
@@ -10,6 +10,7 @@ struct fetch_pack_args {
                lock_pack:1,
                use_thin_pack:1,
                fetch_all:1,
+               stdin_refs:1,
                verbose:1,
                no_progress:1,
                include_tag:1,
diff --git a/fsck.c b/fsck.c
index 6c855f84f01c19678399d85181da1094bd61b371..4c63b2cc41eec4f568ee6f0d18a51c97304d5d96 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -27,7 +27,7 @@ static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)
                else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode))
                        result = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data);
                else {
-                       result = error("in tree %s: entry %s has bad mode %.6o\n",
+                       result = error("in tree %s: entry %s has bad mode %.6o",
                                        sha1_to_hex(tree->object.sha1), entry.path, entry.mode);
                }
                if (result < 0)
index 1093ef4ad6b9793fb829403c41391a09a07e57a2..9a4c9b94e61c6a310b69e546c7890a676a3fb96f 100755 (executable)
@@ -16,7 +16,7 @@ do
      /^NAME/,/git-'"$cmd"'/H
      ${
            x
-           s/.*git-'"$cmd"' - \(.*\)/  {"'"$cmd"'", "\1"},/
+           s/.*git-'"$cmd"' - \(.*\)/  {"'"$cmd"'", N_("\1")},/
            p
      }' "Documentation/git-$cmd.txt"
 done
index 8f0839d205e0c4010e256bb5cf81c73cc2f438ab..d948aa88dba11d1d7d87f6a523c698cf4f4848f1 100755 (executable)
@@ -268,6 +268,7 @@ sub get_empty_tree {
 # FILE:                is file different from index?
 # INDEX_ADDDEL:        is it add/delete between HEAD and index?
 # FILE_ADDDEL: is it add/delete between index and file?
+# UNMERGED:    is the path unmerged
 
 sub list_modified {
        my ($only) = @_;
@@ -318,16 +319,10 @@ sub list_modified {
                }
        }
 
-       for (run_cmd_pipe(qw(git diff-files --numstat --summary --), @tracked)) {
+       for (run_cmd_pipe(qw(git diff-files --numstat --summary --raw --), @tracked)) {
                if (($add, $del, $file) =
                    /^([-\d]+)  ([-\d]+)        (.*)/) {
                        $file = unquote_path($file);
-                       if (!exists $data{$file}) {
-                               $data{$file} = +{
-                                       INDEX => 'unchanged',
-                                       BINARY => 0,
-                               };
-                       }
                        my ($change, $bin);
                        if ($add eq '-' && $del eq '-') {
                                $change = 'binary';
@@ -346,6 +341,18 @@ sub list_modified {
                        $file = unquote_path($file);
                        $data{$file}{FILE_ADDDEL} = $adddel;
                }
+               elsif (/^:[0-7]+ [0-7]+ [0-9a-f]+ [0-9a-f]+ (.) (.*)$/) {
+                       $file = unquote_path($2);
+                       if (!exists $data{$file}) {
+                               $data{$file} = +{
+                                       INDEX => 'unchanged',
+                                       BINARY => 0,
+                               };
+                       }
+                       if ($1 eq 'U') {
+                               $data{$file}{UNMERGED} = 1;
+                       }
+               }
        }
 
        for (sort keys %data) {
@@ -1190,6 +1197,10 @@ sub apply_patch_for_checkout_commit {
 
 sub patch_update_cmd {
        my @all_mods = list_modified($patch_mode_flavour{FILTER});
+       error_msg "ignoring unmerged: $_->{VALUE}\n"
+               for grep { $_->{UNMERGED} } @all_mods;
+       @all_mods = grep { !$_->{UNMERGED} } @all_mods;
+
        my @mods = grep { !($_->{BINARY}) } @all_mods;
        my @them;
 
index 4da0ddafc4bf2029823148e7285085661c134497..f8b7a0cb602d2d2425f68f8c27338cc003b70f6b 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -24,6 +24,7 @@ ignore-space-change pass it through git-apply
 ignore-whitespace pass it through git-apply
 directory=      pass it through git-apply
 exclude=        pass it through git-apply
+include=        pass it through git-apply
 C=              pass it through git-apply
 p=              pass it through git-apply
 patch-format=   format the patch(es) are in
@@ -138,6 +139,12 @@ fall_back_3way () {
     say Using index info to reconstruct a base tree...
 
     cmd='GIT_INDEX_FILE="$dotest/patch-merge-tmp-index"'
+
+    if test -z "$GIT_QUIET"
+    then
+       eval "$cmd git diff-index --cached --diff-filter=AM --name-status HEAD"
+    fi
+
     cmd="$cmd git apply --cached $git_apply_opt"' <"$dotest/patch"'
     if eval "$cmd"
     then
@@ -412,7 +419,7 @@ do
                ;;
        --resolvemsg)
                shift; resolvemsg=$1 ;;
-       --whitespace|--directory|--exclude)
+       --whitespace|--directory|--exclude|--include)
                git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
        -C|-p)
                git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
index e6558d101062929241cc30cd0272556c3164310b..3d0fe0cd93b393a5f376aa10505e927fbed37f8f 100755 (executable)
@@ -73,9 +73,16 @@ then
        fi
 fi
 
-# Launch the merge tool on each path provided by 'git diff'
-while test $# -gt 6
-do
-       launch_merge_tool "$1" "$2" "$5"
-       shift 7
-done
+if test -n "$GIT_DIFFTOOL_DIRDIFF"
+then
+       LOCAL="$1"
+       REMOTE="$2"
+       run_merge_tool "$merge_tool" false
+else
+       # Launch the merge tool on each path provided by 'git diff'
+       while test $# -gt 6
+       do
+               launch_merge_tool "$1" "$2" "$5"
+               shift 7
+       done
+fi
index 09b65f1770c09824df5543d208d1df0e9a60d831..ae1e0525d89181f8adbe288b62e107b8dadb8ca1 100755 (executable)
-#!/usr/bin/env perl
+#!/usr/bin/perl
 # Copyright (c) 2009, 2010 David Aguilar
+# Copyright (c) 2012 Tim Henigan
 #
 # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
 # git-difftool--helper script.
 #
 # This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
-# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, and GIT_DIFF_TOOL
-# are exported for use by git-difftool--helper.
+# The GIT_DIFF* variables are exported for use by git-difftool--helper.
 #
 # Any arguments that are unknown to this script are forwarded to 'git diff'.
 
 use 5.008;
 use strict;
 use warnings;
-use Cwd qw(abs_path);
 use File::Basename qw(dirname);
+use File::Copy;
+use File::Find;
+use File::stat;
+use File::Path qw(mkpath);
+use File::Temp qw(tempdir);
+use Getopt::Long qw(:config pass_through);
+use Git;
 
-require Git;
-
-my $DIR = abs_path(dirname($0));
-
+my @tools;
+my @working_tree;
+my $rc;
+my $repo = Git->repository();
+my $repo_path = $repo->repo_path();
 
 sub usage
 {
+       my $exitcode = shift;
        print << 'USAGE';
-usage: git difftool [-t|--tool=<tool>] [-x|--extcmd=<cmd>]
-                    [-y|--no-prompt]   [-g|--gui]
+usage: git difftool [-t|--tool=<tool>] [--tool-help]
+                    [-x|--extcmd=<cmd>]
+                    [-g|--gui] [--no-gui]
+                    [--prompt] [-y|--no-prompt]
+                    [-d|--dir-diff]
                     ['git diff' options]
 USAGE
-       exit 1;
+       exit($exitcode);
 }
 
-sub setup_environment
+sub find_worktree
 {
-       $ENV{PATH} = "$DIR:$ENV{PATH}";
-       $ENV{GIT_PAGER} = '';
-       $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
+       # Git->repository->wc_path() does not honor changes to the working
+       # tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'
+       # config variable.
+       my $worktree;
+       my $env_worktree = $ENV{GIT_WORK_TREE};
+       my $core_worktree = Git::config('core.worktree');
+
+       if (defined($env_worktree) and (length($env_worktree) > 0)) {
+               $worktree = $env_worktree;
+       } elsif (defined($core_worktree) and (length($core_worktree) > 0)) {
+               $worktree = $core_worktree;
+       } else {
+               $worktree = $repo->wc_path();
+       }
+
+       return $worktree;
 }
 
-sub exe
+my $workdir = find_worktree();
+
+sub filter_tool_scripts
 {
-       my $exe = shift;
-       if ($^O eq 'MSWin32' || $^O eq 'msys') {
-               return "$exe.exe";
+       if (-d $_) {
+               if ($_ ne ".") {
+                       # Ignore files in subdirectories
+                       $File::Find::prune = 1;
+               }
+       } else {
+               if ((-f $_) && ($_ ne "defaults")) {
+                       push(@tools, $_);
+               }
        }
-       return $exe;
 }
 
-sub generate_command
+sub print_tool_help
 {
-       my @command = (exe('git'), 'diff');
-       my $skip_next = 0;
-       my $idx = -1;
-       my $prompt = '';
-       for my $arg (@ARGV) {
-               $idx++;
-               if ($skip_next) {
-                       $skip_next = 0;
-                       next;
+       my ($cmd, @found, @notfound);
+       my $gitpath = Git::exec_path();
+
+       find(\&filter_tool_scripts, "$gitpath/mergetools");
+
+       foreach my $tool (@tools) {
+               $cmd  = "TOOL_MODE=diff";
+               $cmd .= ' && . "$(git --exec-path)/git-mergetool--lib"';
+               $cmd .= " && get_merge_tool_path $tool >/dev/null 2>&1";
+               $cmd .= " && can_diff >/dev/null 2>&1";
+               if (system('sh', '-c', $cmd) == 0) {
+                       push(@found, $tool);
+               } else {
+                       push(@notfound, $tool);
                }
-               if ($arg eq '-t' || $arg eq '--tool') {
-                       usage() if $#ARGV <= $idx;
-                       $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];
-                       $skip_next = 1;
-                       next;
+       }
+
+       print "'git difftool --tool=<tool>' may be set to one of the following:\n";
+       print "\t$_\n" for (sort(@found));
+
+       print "\nThe following tools are valid, but not currently available:\n";
+       print "\t$_\n" for (sort(@notfound));
+
+       print "\nNOTE: Some of the tools listed above only work in a windowed\n";
+       print "environment. If run in a terminal-only session, they will fail.\n";
+
+       exit(0);
+}
+
+sub setup_dir_diff
+{
+       # Run the diff; exit immediately if no diff found
+       # 'Repository' and 'WorkingCopy' must be explicitly set to insure that
+       # if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used
+       # by Git->repository->command*.
+       my $diffrepo = Git->repository(Repository => $repo_path, WorkingCopy => $workdir);
+       my $diffrtn = $diffrepo->command_oneline('diff', '--raw', '--no-abbrev', '-z', @ARGV);
+       exit(0) if (length($diffrtn) == 0);
+
+       # Setup temp directories
+       my $tmpdir = tempdir('git-diffall.XXXXX', CLEANUP => 1, TMPDIR => 1);
+       my $ldir = "$tmpdir/left";
+       my $rdir = "$tmpdir/right";
+       mkpath($ldir) or die $!;
+       mkpath($rdir) or die $!;
+
+       # Build index info for left and right sides of the diff
+       my $submodule_mode = '160000';
+       my $symlink_mode = '120000';
+       my $null_mode = '0' x 6;
+       my $null_sha1 = '0' x 40;
+       my $lindex = '';
+       my $rindex = '';
+       my %submodule;
+       my %symlink;
+       my @rawdiff = split('\0', $diffrtn);
+
+       my $i = 0;
+       while ($i < $#rawdiff) {
+               if ($rawdiff[$i] =~ /^::/) {
+                       print "Combined diff formats ('-c' and '--cc') are not supported in directory diff mode.\n";
+                       exit(1);
                }
-               if ($arg =~ /^--tool=/) {
-                       $ENV{GIT_DIFF_TOOL} = substr($arg, 7);
-                       next;
+
+               my ($lmode, $rmode, $lsha1, $rsha1, $status) = split(' ', substr($rawdiff[$i], 1));
+               my $src_path = $rawdiff[$i + 1];
+               my $dst_path;
+
+               if ($status =~ /^[CR]/) {
+                       $dst_path = $rawdiff[$i + 2];
+                       $i += 3;
+               } else {
+                       $dst_path = $src_path;
+                       $i += 2;
                }
-               if ($arg eq '-x' || $arg eq '--extcmd') {
-                       usage() if $#ARGV <= $idx;
-                       $ENV{GIT_DIFFTOOL_EXTCMD} = $ARGV[$idx + 1];
-                       $skip_next = 1;
+
+               if (($lmode eq $submodule_mode) or ($rmode eq $submodule_mode)) {
+                       $submodule{$src_path}{left} = $lsha1;
+                       if ($lsha1 ne $rsha1) {
+                               $submodule{$dst_path}{right} = $rsha1;
+                       } else {
+                               $submodule{$dst_path}{right} = "$rsha1-dirty";
+                       }
                        next;
                }
-               if ($arg =~ /^--extcmd=/) {
-                       $ENV{GIT_DIFFTOOL_EXTCMD} = substr($arg, 9);
-                       next;
+
+               if ($lmode eq $symlink_mode) {
+                       $symlink{$src_path}{left} = $diffrepo->command_oneline('show', "$lsha1");
                }
-               if ($arg eq '-g' || $arg eq '--gui') {
-                       eval {
-                               my $tool = Git::command_oneline('config',
-                                                               'diff.guitool');
-                               if (length($tool)) {
-                                       $ENV{GIT_DIFF_TOOL} = $tool;
-                               }
-                       };
-                       next;
+
+               if ($rmode eq $symlink_mode) {
+                       $symlink{$dst_path}{right} = $diffrepo->command_oneline('show', "$rsha1");
                }
-               if ($arg eq '-y' || $arg eq '--no-prompt') {
-                       $prompt = 'no';
-                       next;
+
+               if (($lmode ne $null_mode) and ($status !~ /^C/)) {
+                       $lindex .= "$lmode $lsha1\t$src_path\0";
                }
-               if ($arg eq '--prompt') {
-                       $prompt = 'yes';
-                       next;
+
+               if ($rmode ne $null_mode) {
+                       if ($rsha1 ne $null_sha1) {
+                               $rindex .= "$rmode $rsha1\t$dst_path\0";
+                       } else {
+                               push(@working_tree, $dst_path);
+                       }
                }
-               if ($arg eq '-h') {
-                       usage();
+       }
+
+       # If $GIT_DIR is not set prior to calling 'git update-index' and
+       # 'git checkout-index', then those commands will fail if difftool
+       # is called from a directory other than the repo root.
+       my $must_unset_git_dir = 0;
+       if (not defined($ENV{GIT_DIR})) {
+               $must_unset_git_dir = 1;
+               $ENV{GIT_DIR} = $repo_path;
+       }
+
+       # Populate the left and right directories based on each index file
+       my ($inpipe, $ctx);
+       $ENV{GIT_INDEX_FILE} = "$tmpdir/lindex";
+       ($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);
+       print($inpipe $lindex);
+       $repo->command_close_pipe($inpipe, $ctx);
+       $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/");
+       exit($rc | ($rc >> 8)) if ($rc != 0);
+
+       $ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
+       ($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);
+       print($inpipe $rindex);
+       $repo->command_close_pipe($inpipe, $ctx);
+       $rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
+       exit($rc | ($rc >> 8)) if ($rc != 0);
+
+       # If $GIT_DIR was explicitly set just for the update/checkout
+       # commands, then it should be unset before continuing.
+       delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
+       delete($ENV{GIT_INDEX_FILE});
+
+       # Changes in the working tree need special treatment since they are
+       # not part of the index
+       for my $file (@working_tree) {
+               my $dir = dirname($file);
+               unless (-d "$rdir/$dir") {
+                       mkpath("$rdir/$dir") or die $!;
+               }
+               copy("$workdir/$file", "$rdir/$file") or die $!;
+               chmod(stat("$workdir/$file")->mode, "$rdir/$file") or die $!;
+       }
+
+       # Changes to submodules require special treatment. This loop writes a
+       # temporary file to both the left and right directories to show the
+       # change in the recorded SHA1 for the submodule.
+       for my $path (keys %submodule) {
+               if (defined($submodule{$path}{left})) {
+                       write_to_file("$ldir/$path", "Subproject commit $submodule{$path}{left}");
+               }
+               if (defined($submodule{$path}{right})) {
+                       write_to_file("$rdir/$path", "Subproject commit $submodule{$path}{right}");
+               }
+       }
+
+       # Symbolic links require special treatment. The standard "git diff"
+       # shows only the link itself, not the contents of the link target.
+       # This loop replicates that behavior.
+       for my $path (keys %symlink) {
+               if (defined($symlink{$path}{left})) {
+                       write_to_file("$ldir/$path", $symlink{$path}{left});
+               }
+               if (defined($symlink{$path}{right})) {
+                       write_to_file("$rdir/$path", $symlink{$path}{right});
                }
-               push @command, $arg;
        }
-       if ($prompt eq 'yes') {
-               $ENV{GIT_DIFFTOOL_PROMPT} = 'true';
-       } elsif ($prompt eq 'no') {
-               $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+
+       return ($ldir, $rdir);
+}
+
+sub write_to_file
+{
+       my $path = shift;
+       my $value = shift;
+
+       # Make sure the path to the file exists
+       my $dir = dirname($path);
+       unless (-d "$dir") {
+               mkpath("$dir") or die $!;
        }
-       return @command
+
+       # If the file already exists in that location, delete it.  This
+       # is required in the case of symbolic links.
+       unlink("$path");
+
+       open(my $fh, '>', "$path") or die $!;
+       print($fh $value);
+       close($fh);
 }
 
-setup_environment();
+# parse command-line options. all unrecognized options and arguments
+# are passed through to the 'git diff' command.
+my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt, $tool_help);
+GetOptions('g|gui!' => \$gui,
+       'd|dir-diff' => \$dirdiff,
+       'h' => \$help,
+       'prompt!' => \$prompt,
+       'y' => sub { $prompt = 0; },
+       't|tool:s' => \$difftool_cmd,
+       'tool-help' => \$tool_help,
+       'x|extcmd:s' => \$extcmd);
 
-# ActiveState Perl for Win32 does not implement POSIX semantics of
-# exec* system call. It just spawns the given executable and finishes
-# the starting program, exiting with code 0.
-# system will at least catch the errors returned by git diff,
-# allowing the caller of git difftool better handling of failures.
-my $rc = system(generate_command());
-exit($rc | ($rc >> 8));
+if (defined($help)) {
+       usage(0);
+}
+if (defined($tool_help)) {
+       print_tool_help();
+}
+if (defined($difftool_cmd)) {
+       if (length($difftool_cmd) > 0) {
+               $ENV{GIT_DIFF_TOOL} = $difftool_cmd;
+       } else {
+               print "No <tool> given for --tool=<tool>\n";
+               usage(1);
+       }
+}
+if (defined($extcmd)) {
+       if (length($extcmd) > 0) {
+               $ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;
+       } else {
+               print "No <cmd> given for --extcmd=<cmd>\n";
+               usage(1);
+       }
+}
+if ($gui) {
+       my $guitool = '';
+       $guitool = Git::config('diff.guitool');
+       if (length($guitool) > 0) {
+               $ENV{GIT_DIFF_TOOL} = $guitool;
+       }
+}
+
+# In directory diff mode, 'git-difftool--helper' is called once
+# to compare the a/b directories.  In file diff mode, 'git diff'
+# will invoke a separate instance of 'git-difftool--helper' for
+# each file that changed.
+if (defined($dirdiff)) {
+       my ($a, $b) = setup_dir_diff();
+       if (defined($extcmd)) {
+               $rc = system($extcmd, $a, $b);
+       } else {
+               $ENV{GIT_DIFFTOOL_DIRDIFF} = 'true';
+               $rc = system('git', 'difftool--helper', $a, $b);
+       }
+
+       exit($rc | ($rc >> 8)) if ($rc != 0);
+
+       # If the diff including working copy files and those
+       # files were modified during the diff, then the changes
+       # should be copied back to the working tree
+       for my $file (@working_tree) {
+               copy("$b/$file", "$workdir/$file") or die $!;
+               chmod(stat("$b/$file")->mode, "$workdir/$file") or die $!;
+       }
+} else {
+       if (defined($prompt)) {
+               if ($prompt) {
+                       $ENV{GIT_DIFFTOOL_PROMPT} = 'true';
+               } else {
+                       $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+               }
+       }
+
+       $ENV{GIT_PAGER} = '';
+       $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
+
+       # ActiveState Perl for Win32 does not implement POSIX semantics of
+       # exec* system call. It just spawns the given executable and finishes
+       # the starting program, exiting with code 0.
+       # system will at least catch the errors returned by git diff,
+       # allowing the caller of git difftool better handling of failures.
+       my $rc = system('git', 'diff', @ARGV);
+       exit($rc | ($rc >> 8));
+}
diff --git a/git-p4.py b/git-p4.py
new file mode 100755 (executable)
index 0000000..f895a24
--- /dev/null
+++ b/git-p4.py
@@ -0,0 +1,2975 @@
+#!/usr/bin/env python
+#
+# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git.
+#
+# Author: Simon Hausmann <simon@lst.de>
+# Copyright: 2007 Simon Hausmann <simon@lst.de>
+#            2007 Trolltech ASA
+# License: MIT <http://www.opensource.org/licenses/mit-license.php>
+#
+
+import optparse, sys, os, marshal, subprocess, shelve
+import tempfile, getopt, os.path, time, platform
+import re, shutil
+
+verbose = False
+
+# Only labels/tags matching this will be imported/exported
+defaultLabelRegexp = r'[a-zA-Z0-9_\-.]+$'
+
+def p4_build_cmd(cmd):
+    """Build a suitable p4 command line.
+
+    This consolidates building and returning a p4 command line into one
+    location. It means that hooking into the environment, or other configuration
+    can be done more easily.
+    """
+    real_cmd = ["p4"]
+
+    user = gitConfig("git-p4.user")
+    if len(user) > 0:
+        real_cmd += ["-u",user]
+
+    password = gitConfig("git-p4.password")
+    if len(password) > 0:
+        real_cmd += ["-P", password]
+
+    port = gitConfig("git-p4.port")
+    if len(port) > 0:
+        real_cmd += ["-p", port]
+
+    host = gitConfig("git-p4.host")
+    if len(host) > 0:
+        real_cmd += ["-H", host]
+
+    client = gitConfig("git-p4.client")
+    if len(client) > 0:
+        real_cmd += ["-c", client]
+
+
+    if isinstance(cmd,basestring):
+        real_cmd = ' '.join(real_cmd) + ' ' + cmd
+    else:
+        real_cmd += cmd
+    return real_cmd
+
+def chdir(dir):
+    # P4 uses the PWD environment variable rather than getcwd(). Since we're
+    # not using the shell, we have to set it ourselves.  This path could
+    # be relative, so go there first, then figure out where we ended up.
+    os.chdir(dir)
+    os.environ['PWD'] = os.getcwd()
+
+def die(msg):
+    if verbose:
+        raise Exception(msg)
+    else:
+        sys.stderr.write(msg + "\n")
+        sys.exit(1)
+
+def write_pipe(c, stdin):
+    if verbose:
+        sys.stderr.write('Writing pipe: %s\n' % str(c))
+
+    expand = isinstance(c,basestring)
+    p = subprocess.Popen(c, stdin=subprocess.PIPE, shell=expand)
+    pipe = p.stdin
+    val = pipe.write(stdin)
+    pipe.close()
+    if p.wait():
+        die('Command failed: %s' % str(c))
+
+    return val
+
+def p4_write_pipe(c, stdin):
+    real_cmd = p4_build_cmd(c)
+    return write_pipe(real_cmd, stdin)
+
+def read_pipe(c, ignore_error=False):
+    if verbose:
+        sys.stderr.write('Reading pipe: %s\n' % str(c))
+
+    expand = isinstance(c,basestring)
+    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
+    pipe = p.stdout
+    val = pipe.read()
+    if p.wait() and not ignore_error:
+        die('Command failed: %s' % str(c))
+
+    return val
+
+def p4_read_pipe(c, ignore_error=False):
+    real_cmd = p4_build_cmd(c)
+    return read_pipe(real_cmd, ignore_error)
+
+def read_pipe_lines(c):
+    if verbose:
+        sys.stderr.write('Reading pipe: %s\n' % str(c))
+
+    expand = isinstance(c, basestring)
+    p = subprocess.Popen(c, stdout=subprocess.PIPE, shell=expand)
+    pipe = p.stdout
+    val = pipe.readlines()
+    if pipe.close() or p.wait():
+        die('Command failed: %s' % str(c))
+
+    return val
+
+def p4_read_pipe_lines(c):
+    """Specifically invoke p4 on the command supplied. """
+    real_cmd = p4_build_cmd(c)
+    return read_pipe_lines(real_cmd)
+
+def system(cmd):
+    expand = isinstance(cmd,basestring)
+    if verbose:
+        sys.stderr.write("executing %s\n" % str(cmd))
+    subprocess.check_call(cmd, shell=expand)
+
+def p4_system(cmd):
+    """Specifically invoke p4 as the system command. """
+    real_cmd = p4_build_cmd(cmd)
+    expand = isinstance(real_cmd, basestring)
+    subprocess.check_call(real_cmd, shell=expand)
+
+def p4_integrate(src, dest):
+    p4_system(["integrate", "-Dt", wildcard_encode(src), wildcard_encode(dest)])
+
+def p4_sync(f, *options):
+    p4_system(["sync"] + list(options) + [wildcard_encode(f)])
+
+def p4_add(f):
+    # forcibly add file names with wildcards
+    if wildcard_present(f):
+        p4_system(["add", "-f", f])
+    else:
+        p4_system(["add", f])
+
+def p4_delete(f):
+    p4_system(["delete", wildcard_encode(f)])
+
+def p4_edit(f):
+    p4_system(["edit", wildcard_encode(f)])
+
+def p4_revert(f):
+    p4_system(["revert", wildcard_encode(f)])
+
+def p4_reopen(type, f):
+    p4_system(["reopen", "-t", type, wildcard_encode(f)])
+
+#
+# Canonicalize the p4 type and return a tuple of the
+# base type, plus any modifiers.  See "p4 help filetypes"
+# for a list and explanation.
+#
+def split_p4_type(p4type):
+
+    p4_filetypes_historical = {
+        "ctempobj": "binary+Sw",
+        "ctext": "text+C",
+        "cxtext": "text+Cx",
+        "ktext": "text+k",
+        "kxtext": "text+kx",
+        "ltext": "text+F",
+        "tempobj": "binary+FSw",
+        "ubinary": "binary+F",
+        "uresource": "resource+F",
+        "uxbinary": "binary+Fx",
+        "xbinary": "binary+x",
+        "xltext": "text+Fx",
+        "xtempobj": "binary+Swx",
+        "xtext": "text+x",
+        "xunicode": "unicode+x",
+        "xutf16": "utf16+x",
+    }
+    if p4type in p4_filetypes_historical:
+        p4type = p4_filetypes_historical[p4type]
+    mods = ""
+    s = p4type.split("+")
+    base = s[0]
+    mods = ""
+    if len(s) > 1:
+        mods = s[1]
+    return (base, mods)
+
+#
+# return the raw p4 type of a file (text, text+ko, etc)
+#
+def p4_type(file):
+    results = p4CmdList(["fstat", "-T", "headType", file])
+    return results[0]['headType']
+
+#
+# Given a type base and modifier, return a regexp matching
+# the keywords that can be expanded in the file
+#
+def p4_keywords_regexp_for_type(base, type_mods):
+    if base in ("text", "unicode", "binary"):
+        kwords = None
+        if "ko" in type_mods:
+            kwords = 'Id|Header'
+        elif "k" in type_mods:
+            kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision'
+        else:
+            return None
+        pattern = r"""
+            \$              # Starts with a dollar, followed by...
+            (%s)            # one of the keywords, followed by...
+            (:[^$]+)?       # possibly an old expansion, followed by...
+            \$              # another dollar
+            """ % kwords
+        return pattern
+    else:
+        return None
+
+#
+# Given a file, return a regexp matching the possible
+# RCS keywords that will be expanded, or None for files
+# with kw expansion turned off.
+#
+def p4_keywords_regexp_for_file(file):
+    if not os.path.exists(file):
+        return None
+    else:
+        (type_base, type_mods) = split_p4_type(p4_type(file))
+        return p4_keywords_regexp_for_type(type_base, type_mods)
+
+def setP4ExecBit(file, mode):
+    # Reopens an already open file and changes the execute bit to match
+    # the execute bit setting in the passed in mode.
+
+    p4Type = "+x"
+
+    if not isModeExec(mode):
+        p4Type = getP4OpenedType(file)
+        p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type)
+        p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type)
+        if p4Type[-1] == "+":
+            p4Type = p4Type[0:-1]
+
+    p4_reopen(p4Type, file)
+
+def getP4OpenedType(file):
+    # Returns the perforce file type for the given file.
+
+    result = p4_read_pipe(["opened", wildcard_encode(file)])
+    match = re.match(".*\((.+)\)\r?$", result)
+    if match:
+        return match.group(1)
+    else:
+        die("Could not determine file type for %s (result: '%s')" % (file, result))
+
+# Return the set of all p4 labels
+def getP4Labels(depotPaths):
+    labels = set()
+    if isinstance(depotPaths,basestring):
+        depotPaths = [depotPaths]
+
+    for l in p4CmdList(["labels"] + ["%s..." % p for p in depotPaths]):
+        label = l['label']
+        labels.add(label)
+
+    return labels
+
+# Return the set of all git tags
+def getGitTags():
+    gitTags = set()
+    for line in read_pipe_lines(["git", "tag"]):
+        tag = line.strip()
+        gitTags.add(tag)
+    return gitTags
+
+def diffTreePattern():
+    # This is a simple generator for the diff tree regex pattern. This could be
+    # a class variable if this and parseDiffTreeEntry were a part of a class.
+    pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)')
+    while True:
+        yield pattern
+
+def parseDiffTreeEntry(entry):
+    """Parses a single diff tree entry into its component elements.
+
+    See git-diff-tree(1) manpage for details about the format of the diff
+    output. This method returns a dictionary with the following elements:
+
+    src_mode - The mode of the source file
+    dst_mode - The mode of the destination file
+    src_sha1 - The sha1 for the source file
+    dst_sha1 - The sha1 fr the destination file
+    status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc)
+    status_score - The score for the status (applicable for 'C' and 'R'
+                   statuses). This is None if there is no score.
+    src - The path for the source file.
+    dst - The path for the destination file. This is only present for
+          copy or renames. If it is not present, this is None.
+
+    If the pattern is not matched, None is returned."""
+
+    match = diffTreePattern().next().match(entry)
+    if match:
+        return {
+            'src_mode': match.group(1),
+            'dst_mode': match.group(2),
+            'src_sha1': match.group(3),
+            'dst_sha1': match.group(4),
+            'status': match.group(5),
+            'status_score': match.group(6),
+            'src': match.group(7),
+            'dst': match.group(10)
+        }
+    return None
+
+def isModeExec(mode):
+    # Returns True if the given git mode represents an executable file,
+    # otherwise False.
+    return mode[-3:] == "755"
+
+def isModeExecChanged(src_mode, dst_mode):
+    return isModeExec(src_mode) != isModeExec(dst_mode)
+
+def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
+
+    if isinstance(cmd,basestring):
+        cmd = "-G " + cmd
+        expand = True
+    else:
+        cmd = ["-G"] + cmd
+        expand = False
+
+    cmd = p4_build_cmd(cmd)
+    if verbose:
+        sys.stderr.write("Opening pipe: %s\n" % str(cmd))
+
+    # Use a temporary file to avoid deadlocks without
+    # subprocess.communicate(), which would put another copy
+    # of stdout into memory.
+    stdin_file = None
+    if stdin is not None:
+        stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode)
+        if isinstance(stdin,basestring):
+            stdin_file.write(stdin)
+        else:
+            for i in stdin:
+                stdin_file.write(i + '\n')
+        stdin_file.flush()
+        stdin_file.seek(0)
+
+    p4 = subprocess.Popen(cmd,
+                          shell=expand,
+                          stdin=stdin_file,
+                          stdout=subprocess.PIPE)
+
+    result = []
+    try:
+        while True:
+            entry = marshal.load(p4.stdout)
+            if cb is not None:
+                cb(entry)
+            else:
+                result.append(entry)
+    except EOFError:
+        pass
+    exitCode = p4.wait()
+    if exitCode != 0:
+        entry = {}
+        entry["p4ExitCode"] = exitCode
+        result.append(entry)
+
+    return result
+
+def p4Cmd(cmd):
+    list = p4CmdList(cmd)
+    result = {}
+    for entry in list:
+        result.update(entry)
+    return result;
+
+def p4Where(depotPath):
+    if not depotPath.endswith("/"):
+        depotPath += "/"
+    depotPath = depotPath + "..."
+    outputList = p4CmdList(["where", depotPath])
+    output = None
+    for entry in outputList:
+        if "depotFile" in entry:
+            if entry["depotFile"] == depotPath:
+                output = entry
+                break
+        elif "data" in entry:
+            data = entry.get("data")
+            space = data.find(" ")
+            if data[:space] == depotPath:
+                output = entry
+                break
+    if output == None:
+        return ""
+    if output["code"] == "error":
+        return ""
+    clientPath = ""
+    if "path" in output:
+        clientPath = output.get("path")
+    elif "data" in output:
+        data = output.get("data")
+        lastSpace = data.rfind(" ")
+        clientPath = data[lastSpace + 1:]
+
+    if clientPath.endswith("..."):
+        clientPath = clientPath[:-3]
+    return clientPath
+
+def currentGitBranch():
+    return read_pipe("git name-rev HEAD").split(" ")[1].strip()
+
+def isValidGitDir(path):
+    if (os.path.exists(path + "/HEAD")
+        and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
+        return True;
+    return False
+
+def parseRevision(ref):
+    return read_pipe("git rev-parse %s" % ref).strip()
+
+def branchExists(ref):
+    rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref],
+                     ignore_error=True)
+    return len(rev) > 0
+
+def extractLogMessageFromGitCommit(commit):
+    logMessage = ""
+
+    ## fixme: title is first line of commit, not 1st paragraph.
+    foundTitle = False
+    for log in read_pipe_lines("git cat-file commit %s" % commit):
+       if not foundTitle:
+           if len(log) == 1:
+               foundTitle = True
+           continue
+
+       logMessage += log
+    return logMessage
+
+def extractSettingsGitLog(log):
+    values = {}
+    for line in log.split("\n"):
+        line = line.strip()
+        m = re.search (r"^ *\[git-p4: (.*)\]$", line)
+        if not m:
+            continue
+
+        assignments = m.group(1).split (':')
+        for a in assignments:
+            vals = a.split ('=')
+            key = vals[0].strip()
+            val = ('='.join (vals[1:])).strip()
+            if val.endswith ('\"') and val.startswith('"'):
+                val = val[1:-1]
+
+            values[key] = val
+
+    paths = values.get("depot-paths")
+    if not paths:
+        paths = values.get("depot-path")
+    if paths:
+        values['depot-paths'] = paths.split(',')
+    return values
+
+def gitBranchExists(branch):
+    proc = subprocess.Popen(["git", "rev-parse", branch],
+                            stderr=subprocess.PIPE, stdout=subprocess.PIPE);
+    return proc.wait() == 0;
+
+_gitConfig = {}
+def gitConfig(key, args = None): # set args to "--bool", for instance
+    if not _gitConfig.has_key(key):
+        argsFilter = ""
+        if args != None:
+            argsFilter = "%s " % args
+        cmd = "git config %s%s" % (argsFilter, key)
+        _gitConfig[key] = read_pipe(cmd, ignore_error=True).strip()
+    return _gitConfig[key]
+
+def gitConfigList(key):
+    if not _gitConfig.has_key(key):
+        _gitConfig[key] = read_pipe("git config --get-all %s" % key, ignore_error=True).strip().split(os.linesep)
+    return _gitConfig[key]
+
+def p4BranchesInGit(branchesAreInRemotes = True):
+    branches = {}
+
+    cmdline = "git rev-parse --symbolic "
+    if branchesAreInRemotes:
+        cmdline += " --remotes"
+    else:
+        cmdline += " --branches"
+
+    for line in read_pipe_lines(cmdline):
+        line = line.strip()
+
+        ## only import to p4/
+        if not line.startswith('p4/') or line == "p4/HEAD":
+            continue
+        branch = line
+
+        # strip off p4
+        branch = re.sub ("^p4/", "", line)
+
+        branches[branch] = parseRevision(line)
+    return branches
+
+def findUpstreamBranchPoint(head = "HEAD"):
+    branches = p4BranchesInGit()
+    # map from depot-path to branch name
+    branchByDepotPath = {}
+    for branch in branches.keys():
+        tip = branches[branch]
+        log = extractLogMessageFromGitCommit(tip)
+        settings = extractSettingsGitLog(log)
+        if settings.has_key("depot-paths"):
+            paths = ",".join(settings["depot-paths"])
+            branchByDepotPath[paths] = "remotes/p4/" + branch
+
+    settings = None
+    parent = 0
+    while parent < 65535:
+        commit = head + "~%s" % parent
+        log = extractLogMessageFromGitCommit(commit)
+        settings = extractSettingsGitLog(log)
+        if settings.has_key("depot-paths"):
+            paths = ",".join(settings["depot-paths"])
+            if branchByDepotPath.has_key(paths):
+                return [branchByDepotPath[paths], settings]
+
+        parent = parent + 1
+
+    return ["", settings]
+
+def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
+    if not silent:
+        print ("Creating/updating branch(es) in %s based on origin branch(es)"
+               % localRefPrefix)
+
+    originPrefix = "origin/p4/"
+
+    for line in read_pipe_lines("git rev-parse --symbolic --remotes"):
+        line = line.strip()
+        if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
+            continue
+
+        headName = line[len(originPrefix):]
+        remoteHead = localRefPrefix + headName
+        originHead = line
+
+        original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
+        if (not original.has_key('depot-paths')
+            or not original.has_key('change')):
+            continue
+
+        update = False
+        if not gitBranchExists(remoteHead):
+            if verbose:
+                print "creating %s" % remoteHead
+            update = True
+        else:
+            settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
+            if settings.has_key('change') > 0:
+                if settings['depot-paths'] == original['depot-paths']:
+                    originP4Change = int(original['change'])
+                    p4Change = int(settings['change'])
+                    if originP4Change > p4Change:
+                        print ("%s (%s) is newer than %s (%s). "
+                               "Updating p4 branch from origin."
+                               % (originHead, originP4Change,
+                                  remoteHead, p4Change))
+                        update = True
+                else:
+                    print ("Ignoring: %s was imported from %s while "
+                           "%s was imported from %s"
+                           % (originHead, ','.join(original['depot-paths']),
+                              remoteHead, ','.join(settings['depot-paths'])))
+
+        if update:
+            system("git update-ref %s %s" % (remoteHead, originHead))
+
+def originP4BranchesExist():
+        return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
+
+def p4ChangesForPaths(depotPaths, changeRange):
+    assert depotPaths
+    cmd = ['changes']
+    for p in depotPaths:
+        cmd += ["%s...%s" % (p, changeRange)]
+    output = p4_read_pipe_lines(cmd)
+
+    changes = {}
+    for line in output:
+        changeNum = int(line.split(" ")[1])
+        changes[changeNum] = True
+
+    changelist = changes.keys()
+    changelist.sort()
+    return changelist
+
+def p4PathStartsWith(path, prefix):
+    # This method tries to remedy a potential mixed-case issue:
+    #
+    # If UserA adds  //depot/DirA/file1
+    # and UserB adds //depot/dira/file2
+    #
+    # we may or may not have a problem. If you have core.ignorecase=true,
+    # we treat DirA and dira as the same directory
+    ignorecase = gitConfig("core.ignorecase", "--bool") == "true"
+    if ignorecase:
+        return path.lower().startswith(prefix.lower())
+    return path.startswith(prefix)
+
+def getClientSpec():
+    """Look at the p4 client spec, create a View() object that contains
+       all the mappings, and return it."""
+
+    specList = p4CmdList("client -o")
+    if len(specList) != 1:
+        die('Output from "client -o" is %d lines, expecting 1' %
+            len(specList))
+
+    # dictionary of all client parameters
+    entry = specList[0]
+
+    # just the keys that start with "View"
+    view_keys = [ k for k in entry.keys() if k.startswith("View") ]
+
+    # hold this new View
+    view = View()
+
+    # append the lines, in order, to the view
+    for view_num in range(len(view_keys)):
+        k = "View%d" % view_num
+        if k not in view_keys:
+            die("Expected view key %s missing" % k)
+        view.append(entry[k])
+
+    return view
+
+def getClientRoot():
+    """Grab the client directory."""
+
+    output = p4CmdList("client -o")
+    if len(output) != 1:
+        die('Output from "client -o" is %d lines, expecting 1' % len(output))
+
+    entry = output[0]
+    if "Root" not in entry:
+        die('Client has no "Root"')
+
+    return entry["Root"]
+
+#
+# P4 wildcards are not allowed in filenames.  P4 complains
+# if you simply add them, but you can force it with "-f", in
+# which case it translates them into %xx encoding internally.
+#
+def wildcard_decode(path):
+    # Search for and fix just these four characters.  Do % last so
+    # that fixing it does not inadvertently create new %-escapes.
+    # Cannot have * in a filename in windows; untested as to
+    # what p4 would do in such a case.
+    if not platform.system() == "Windows":
+        path = path.replace("%2A", "*")
+    path = path.replace("%23", "#") \
+               .replace("%40", "@") \
+               .replace("%25", "%")
+    return path
+
+def wildcard_encode(path):
+    # do % first to avoid double-encoding the %s introduced here
+    path = path.replace("%", "%25") \
+               .replace("*", "%2A") \
+               .replace("#", "%23") \
+               .replace("@", "%40")
+    return path
+
+def wildcard_present(path):
+    return path.translate(None, "*#@%") != path
+
+class Command:
+    def __init__(self):
+        self.usage = "usage: %prog [options]"
+        self.needsGit = True
+        self.verbose = False
+
+class P4UserMap:
+    def __init__(self):
+        self.userMapFromPerforceServer = False
+        self.myP4UserId = None
+
+    def p4UserId(self):
+        if self.myP4UserId:
+            return self.myP4UserId
+
+        results = p4CmdList("user -o")
+        for r in results:
+            if r.has_key('User'):
+                self.myP4UserId = r['User']
+                return r['User']
+        die("Could not find your p4 user id")
+
+    def p4UserIsMe(self, p4User):
+        # return True if the given p4 user is actually me
+        me = self.p4UserId()
+        if not p4User or p4User != me:
+            return False
+        else:
+            return True
+
+    def getUserCacheFilename(self):
+        home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
+        return home + "/.gitp4-usercache.txt"
+
+    def getUserMapFromPerforceServer(self):
+        if self.userMapFromPerforceServer:
+            return
+        self.users = {}
+        self.emails = {}
+
+        for output in p4CmdList("users"):
+            if not output.has_key("User"):
+                continue
+            self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
+            self.emails[output["Email"]] = output["User"]
+
+
+        s = ''
+        for (key, val) in self.users.items():
+            s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1))
+
+        open(self.getUserCacheFilename(), "wb").write(s)
+        self.userMapFromPerforceServer = True
+
+    def loadUserMapFromCache(self):
+        self.users = {}
+        self.userMapFromPerforceServer = False
+        try:
+            cache = open(self.getUserCacheFilename(), "rb")
+            lines = cache.readlines()
+            cache.close()
+            for line in lines:
+                entry = line.strip().split("\t")
+                self.users[entry[0]] = entry[1]
+        except IOError:
+            self.getUserMapFromPerforceServer()
+
+class P4Debug(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = []
+        self.description = "A tool to debug the output of p4 -G."
+        self.needsGit = False
+
+    def run(self, args):
+        j = 0
+        for output in p4CmdList(args):
+            print 'Element: %d' % j
+            j += 1
+            print output
+        return True
+
+class P4RollBack(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = [
+            optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true")
+        ]
+        self.description = "A tool to debug the multi-branch import. Don't use :)"
+        self.rollbackLocalBranches = False
+
+    def run(self, args):
+        if len(args) != 1:
+            return False
+        maxChange = int(args[0])
+
+        if "p4ExitCode" in p4Cmd("changes -m 1"):
+            die("Problems executing p4");
+
+        if self.rollbackLocalBranches:
+            refPrefix = "refs/heads/"
+            lines = read_pipe_lines("git rev-parse --symbolic --branches")
+        else:
+            refPrefix = "refs/remotes/"
+            lines = read_pipe_lines("git rev-parse --symbolic --remotes")
+
+        for line in lines:
+            if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"):
+                line = line.strip()
+                ref = refPrefix + line
+                log = extractLogMessageFromGitCommit(ref)
+                settings = extractSettingsGitLog(log)
+
+                depotPaths = settings['depot-paths']
+                change = settings['change']
+
+                changed = False
+
+                if len(p4Cmd("changes -m 1 "  + ' '.join (['%s...@%s' % (p, maxChange)
+                                                           for p in depotPaths]))) == 0:
+                    print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
+                    system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
+                    continue
+
+                while change and int(change) > maxChange:
+                    changed = True
+                    if self.verbose:
+                        print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
+                    system("git update-ref %s \"%s^\"" % (ref, ref))
+                    log = extractLogMessageFromGitCommit(ref)
+                    settings =  extractSettingsGitLog(log)
+
+
+                    depotPaths = settings['depot-paths']
+                    change = settings['change']
+
+                if changed:
+                    print "%s rewound to %s" % (ref, change)
+
+        return True
+
+class P4Submit(Command, P4UserMap):
+    def __init__(self):
+        Command.__init__(self)
+        P4UserMap.__init__(self)
+        self.options = [
+                optparse.make_option("--origin", dest="origin"),
+                optparse.make_option("-M", dest="detectRenames", action="store_true"),
+                # preserve the user, requires relevant p4 permissions
+                optparse.make_option("--preserve-user", dest="preserveUser", action="store_true"),
+                optparse.make_option("--export-labels", dest="exportLabels", action="store_true"),
+        ]
+        self.description = "Submit changes from git to the perforce depot."
+        self.usage += " [name of git branch to submit into perforce depot]"
+        self.interactive = True
+        self.origin = ""
+        self.detectRenames = False
+        self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
+        self.isWindows = (platform.system() == "Windows")
+        self.exportLabels = False
+
+    def check(self):
+        if len(p4CmdList("opened ...")) > 0:
+            die("You have files opened with perforce! Close them before starting the sync.")
+
+    # replaces everything between 'Description:' and the next P4 submit template field with the
+    # commit message
+    def prepareLogMessage(self, template, message):
+        result = ""
+
+        inDescriptionSection = False
+
+        for line in template.split("\n"):
+            if line.startswith("#"):
+                result += line + "\n"
+                continue
+
+            if inDescriptionSection:
+                if line.startswith("Files:") or line.startswith("Jobs:"):
+                    inDescriptionSection = False
+                else:
+                    continue
+            else:
+                if line.startswith("Description:"):
+                    inDescriptionSection = True
+                    line += "\n"
+                    for messageLine in message.split("\n"):
+                        line += "\t" + messageLine + "\n"
+
+            result += line + "\n"
+
+        return result
+
+    def patchRCSKeywords(self, file, pattern):
+        # Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern
+        (handle, outFileName) = tempfile.mkstemp(dir='.')
+        try:
+            outFile = os.fdopen(handle, "w+")
+            inFile = open(file, "r")
+            regexp = re.compile(pattern, re.VERBOSE)
+            for line in inFile.readlines():
+                line = regexp.sub(r'$\1$', line)
+                outFile.write(line)
+            inFile.close()
+            outFile.close()
+            # Forcibly overwrite the original file
+            os.unlink(file)
+            shutil.move(outFileName, file)
+        except:
+            # cleanup our temporary file
+            os.unlink(outFileName)
+            print "Failed to strip RCS keywords in %s" % file
+            raise
+
+        print "Patched up RCS keywords in %s" % file
+
+    def p4UserForCommit(self,id):
+        # Return the tuple (perforce user,git email) for a given git commit id
+        self.getUserMapFromPerforceServer()
+        gitEmail = read_pipe("git log --max-count=1 --format='%%ae' %s" % id)
+        gitEmail = gitEmail.strip()
+        if not self.emails.has_key(gitEmail):
+            return (None,gitEmail)
+        else:
+            return (self.emails[gitEmail],gitEmail)
+
+    def checkValidP4Users(self,commits):
+        # check if any git authors cannot be mapped to p4 users
+        for id in commits:
+            (user,email) = self.p4UserForCommit(id)
+            if not user:
+                msg = "Cannot find p4 user for email %s in commit %s." % (email, id)
+                if gitConfig('git-p4.allowMissingP4Users').lower() == "true":
+                    print "%s" % msg
+                else:
+                    die("Error: %s\nSet git-p4.allowMissingP4Users to true to allow this." % msg)
+
+    def lastP4Changelist(self):
+        # Get back the last changelist number submitted in this client spec. This
+        # then gets used to patch up the username in the change. If the same
+        # client spec is being used by multiple processes then this might go
+        # wrong.
+        results = p4CmdList("client -o")        # find the current client
+        client = None
+        for r in results:
+            if r.has_key('Client'):
+                client = r['Client']
+                break
+        if not client:
+            die("could not get client spec")
+        results = p4CmdList(["changes", "-c", client, "-m", "1"])
+        for r in results:
+            if r.has_key('change'):
+                return r['change']
+        die("Could not get changelist number for last submit - cannot patch up user details")
+
+    def modifyChangelistUser(self, changelist, newUser):
+        # fixup the user field of a changelist after it has been submitted.
+        changes = p4CmdList("change -o %s" % changelist)
+        if len(changes) != 1:
+            die("Bad output from p4 change modifying %s to user %s" %
+                (changelist, newUser))
+
+        c = changes[0]
+        if c['User'] == newUser: return   # nothing to do
+        c['User'] = newUser
+        input = marshal.dumps(c)
+
+        result = p4CmdList("change -f -i", stdin=input)
+        for r in result:
+            if r.has_key('code'):
+                if r['code'] == 'error':
+                    die("Could not modify user field of changelist %s to %s:%s" % (changelist, newUser, r['data']))
+            if r.has_key('data'):
+                print("Updated user field for changelist %s to %s" % (changelist, newUser))
+                return
+        die("Could not modify user field of changelist %s to %s" % (changelist, newUser))
+
+    def canChangeChangelists(self):
+        # check to see if we have p4 admin or super-user permissions, either of
+        # which are required to modify changelists.
+        results = p4CmdList(["protects", self.depotPath])
+        for r in results:
+            if r.has_key('perm'):
+                if r['perm'] == 'admin':
+                    return 1
+                if r['perm'] == 'super':
+                    return 1
+        return 0
+
+    def prepareSubmitTemplate(self):
+        # remove lines in the Files section that show changes to files outside the depot path we're committing into
+        template = ""
+        inFilesSection = False
+        for line in p4_read_pipe_lines(['change', '-o']):
+            if line.endswith("\r\n"):
+                line = line[:-2] + "\n"
+            if inFilesSection:
+                if line.startswith("\t"):
+                    # path starts and ends with a tab
+                    path = line[1:]
+                    lastTab = path.rfind("\t")
+                    if lastTab != -1:
+                        path = path[:lastTab]
+                        if not p4PathStartsWith(path, self.depotPath):
+                            continue
+                else:
+                    inFilesSection = False
+            else:
+                if line.startswith("Files:"):
+                    inFilesSection = True
+
+            template += line
+
+        return template
+
+    def edit_template(self, template_file):
+        """Invoke the editor to let the user change the submission
+           message.  Return true if okay to continue with the submit."""
+
+        # if configured to skip the editing part, just submit
+        if gitConfig("git-p4.skipSubmitEdit") == "true":
+            return True
+
+        # look at the modification time, to check later if the user saved
+        # the file
+        mtime = os.stat(template_file).st_mtime
+
+        # invoke the editor
+        if os.environ.has_key("P4EDITOR") and (os.environ.get("P4EDITOR") != ""):
+            editor = os.environ.get("P4EDITOR")
+        else:
+            editor = read_pipe("git var GIT_EDITOR").strip()
+        system(editor + " " + template_file)
+
+        # If the file was not saved, prompt to see if this patch should
+        # be skipped.  But skip this verification step if configured so.
+        if gitConfig("git-p4.skipSubmitEditCheck") == "true":
+            return True
+
+        # modification time updated means user saved the file
+        if os.stat(template_file).st_mtime > mtime:
+            return True
+
+        while True:
+            response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
+            if response == 'y':
+                return True
+            if response == 'n':
+                return False
+
+    def applyCommit(self, id):
+        print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
+
+        (p4User, gitEmail) = self.p4UserForCommit(id)
+
+        if not self.detectRenames:
+            # If not explicitly set check the config variable
+            self.detectRenames = gitConfig("git-p4.detectRenames")
+
+        if self.detectRenames.lower() == "false" or self.detectRenames == "":
+            diffOpts = ""
+        elif self.detectRenames.lower() == "true":
+            diffOpts = "-M"
+        else:
+            diffOpts = "-M%s" % self.detectRenames
+
+        detectCopies = gitConfig("git-p4.detectCopies")
+        if detectCopies.lower() == "true":
+            diffOpts += " -C"
+        elif detectCopies != "" and detectCopies.lower() != "false":
+            diffOpts += " -C%s" % detectCopies
+
+        if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true":
+            diffOpts += " --find-copies-harder"
+
+        diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
+        filesToAdd = set()
+        filesToDelete = set()
+        editedFiles = set()
+        pureRenameCopy = set()
+        filesToChangeExecBit = {}
+
+        for line in diff:
+            diff = parseDiffTreeEntry(line)
+            modifier = diff['status']
+            path = diff['src']
+            if modifier == "M":
+                p4_edit(path)
+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
+                    filesToChangeExecBit[path] = diff['dst_mode']
+                editedFiles.add(path)
+            elif modifier == "A":
+                filesToAdd.add(path)
+                filesToChangeExecBit[path] = diff['dst_mode']
+                if path in filesToDelete:
+                    filesToDelete.remove(path)
+            elif modifier == "D":
+                filesToDelete.add(path)
+                if path in filesToAdd:
+                    filesToAdd.remove(path)
+            elif modifier == "C":
+                src, dest = diff['src'], diff['dst']
+                p4_integrate(src, dest)
+                pureRenameCopy.add(dest)
+                if diff['src_sha1'] != diff['dst_sha1']:
+                    p4_edit(dest)
+                    pureRenameCopy.discard(dest)
+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
+                    p4_edit(dest)
+                    pureRenameCopy.discard(dest)
+                    filesToChangeExecBit[dest] = diff['dst_mode']
+                os.unlink(dest)
+                editedFiles.add(dest)
+            elif modifier == "R":
+                src, dest = diff['src'], diff['dst']
+                p4_integrate(src, dest)
+                if diff['src_sha1'] != diff['dst_sha1']:
+                    p4_edit(dest)
+                else:
+                    pureRenameCopy.add(dest)
+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
+                    p4_edit(dest)
+                    filesToChangeExecBit[dest] = diff['dst_mode']
+                os.unlink(dest)
+                editedFiles.add(dest)
+                filesToDelete.add(src)
+            else:
+                die("unknown modifier %s for %s" % (modifier, path))
+
+        diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id)
+        patchcmd = diffcmd + " | git apply "
+        tryPatchCmd = patchcmd + "--check -"
+        applyPatchCmd = patchcmd + "--check --apply -"
+        patch_succeeded = True
+
+        if os.system(tryPatchCmd) != 0:
+            fixed_rcs_keywords = False
+            patch_succeeded = False
+            print "Unfortunately applying the change failed!"
+
+            # Patch failed, maybe it's just RCS keyword woes. Look through
+            # the patch to see if that's possible.
+            if gitConfig("git-p4.attemptRCSCleanup","--bool") == "true":
+                file = None
+                pattern = None
+                kwfiles = {}
+                for file in editedFiles | filesToDelete:
+                    # did this file's delta contain RCS keywords?
+                    pattern = p4_keywords_regexp_for_file(file)
+
+                    if pattern:
+                        # this file is a possibility...look for RCS keywords.
+                        regexp = re.compile(pattern, re.VERBOSE)
+                        for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
+                            if regexp.search(line):
+                                if verbose:
+                                    print "got keyword match on %s in %s in %s" % (pattern, line, file)
+                                kwfiles[file] = pattern
+                                break
+
+                for file in kwfiles:
+                    if verbose:
+                        print "zapping %s with %s" % (line,pattern)
+                    self.patchRCSKeywords(file, kwfiles[file])
+                    fixed_rcs_keywords = True
+
+            if fixed_rcs_keywords:
+                print "Retrying the patch with RCS keywords cleaned up"
+                if os.system(tryPatchCmd) == 0:
+                    patch_succeeded = True
+
+        if not patch_succeeded:
+            print "What do you want to do?"
+            response = "x"
+            while response != "s" and response != "a" and response != "w":
+                response = raw_input("[s]kip this patch / [a]pply the patch forcibly "
+                                     "and with .rej files / [w]rite the patch to a file (patch.txt) ")
+            if response == "s":
+                print "Skipping! Good luck with the next patches..."
+                for f in editedFiles:
+                    p4_revert(f)
+                for f in filesToAdd:
+                    os.remove(f)
+                return
+            elif response == "a":
+                os.system(applyPatchCmd)
+                if len(filesToAdd) > 0:
+                    print "You may also want to call p4 add on the following files:"
+                    print " ".join(filesToAdd)
+                if len(filesToDelete):
+                    print "The following files should be scheduled for deletion with p4 delete:"
+                    print " ".join(filesToDelete)
+                die("Please resolve and submit the conflict manually and "
+                    + "continue afterwards with git p4 submit --continue")
+            elif response == "w":
+                system(diffcmd + " > patch.txt")
+                print "Patch saved to patch.txt in %s !" % self.clientPath
+                die("Please resolve and submit the conflict manually and "
+                    "continue afterwards with git p4 submit --continue")
+
+        system(applyPatchCmd)
+
+        for f in filesToAdd:
+            p4_add(f)
+        for f in filesToDelete:
+            p4_revert(f)
+            p4_delete(f)
+
+        # Set/clear executable bits
+        for f in filesToChangeExecBit.keys():
+            mode = filesToChangeExecBit[f]
+            setP4ExecBit(f, mode)
+
+        logMessage = extractLogMessageFromGitCommit(id)
+        logMessage = logMessage.strip()
+
+        template = self.prepareSubmitTemplate()
+
+        if self.interactive:
+            submitTemplate = self.prepareLogMessage(template, logMessage)
+
+            if self.preserveUser:
+               submitTemplate = submitTemplate + ("\n######## Actual user %s, modified after commit\n" % p4User)
+
+            if os.environ.has_key("P4DIFF"):
+                del(os.environ["P4DIFF"])
+            diff = ""
+            for editedFile in editedFiles:
+                diff += p4_read_pipe(['diff', '-du',
+                                      wildcard_encode(editedFile)])
+
+            newdiff = ""
+            for newFile in filesToAdd:
+                newdiff += "==== new file ====\n"
+                newdiff += "--- /dev/null\n"
+                newdiff += "+++ %s\n" % newFile
+                f = open(newFile, "r")
+                for line in f.readlines():
+                    newdiff += "+" + line
+                f.close()
+
+            if self.checkAuthorship and not self.p4UserIsMe(p4User):
+                submitTemplate += "######## git author %s does not match your p4 account.\n" % gitEmail
+                submitTemplate += "######## Use option --preserve-user to modify authorship.\n"
+                submitTemplate += "######## Variable git-p4.skipUserNameCheck hides this message.\n"
+
+            separatorLine = "######## everything below this line is just the diff #######\n"
+
+            (handle, fileName) = tempfile.mkstemp()
+            tmpFile = os.fdopen(handle, "w+")
+            if self.isWindows:
+                submitTemplate = submitTemplate.replace("\n", "\r\n")
+                separatorLine = separatorLine.replace("\n", "\r\n")
+                newdiff = newdiff.replace("\n", "\r\n")
+            tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
+            tmpFile.close()
+
+            if self.edit_template(fileName):
+                # read the edited message and submit
+                tmpFile = open(fileName, "rb")
+                message = tmpFile.read()
+                tmpFile.close()
+                submitTemplate = message[:message.index(separatorLine)]
+                if self.isWindows:
+                    submitTemplate = submitTemplate.replace("\r\n", "\n")
+                p4_write_pipe(['submit', '-i'], submitTemplate)
+
+                if self.preserveUser:
+                    if p4User:
+                        # Get last changelist number. Cannot easily get it from
+                        # the submit command output as the output is
+                        # unmarshalled.
+                        changelist = self.lastP4Changelist()
+                        self.modifyChangelistUser(changelist, p4User)
+
+                # The rename/copy happened by applying a patch that created a
+                # new file.  This leaves it writable, which confuses p4.
+                for f in pureRenameCopy:
+                    p4_sync(f, "-f")
+
+            else:
+                # skip this patch
+                print "Submission cancelled, undoing p4 changes."
+                for f in editedFiles:
+                    p4_revert(f)
+                for f in filesToAdd:
+                    p4_revert(f)
+                    os.remove(f)
+
+            os.remove(fileName)
+        else:
+            fileName = "submit.txt"
+            file = open(fileName, "w+")
+            file.write(self.prepareLogMessage(template, logMessage))
+            file.close()
+            print ("Perforce submit template written as %s. "
+                   + "Please review/edit and then use p4 submit -i < %s to submit directly!"
+                   % (fileName, fileName))
+
+    # Export git tags as p4 labels. Create a p4 label and then tag
+    # with that.
+    def exportGitTags(self, gitTags):
+        validLabelRegexp = gitConfig("git-p4.labelExportRegexp")
+        if len(validLabelRegexp) == 0:
+            validLabelRegexp = defaultLabelRegexp
+        m = re.compile(validLabelRegexp)
+
+        for name in gitTags:
+
+            if not m.match(name):
+                if verbose:
+                    print "tag %s does not match regexp %s" % (name, validLabelRegexp)
+                continue
+
+            # Get the p4 commit this corresponds to
+            logMessage = extractLogMessageFromGitCommit(name)
+            values = extractSettingsGitLog(logMessage)
+
+            if not values.has_key('change'):
+                # a tag pointing to something not sent to p4; ignore
+                if verbose:
+                    print "git tag %s does not give a p4 commit" % name
+                continue
+            else:
+                changelist = values['change']
+
+            # Get the tag details.
+            inHeader = True
+            isAnnotated = False
+            body = []
+            for l in read_pipe_lines(["git", "cat-file", "-p", name]):
+                l = l.strip()
+                if inHeader:
+                    if re.match(r'tag\s+', l):
+                        isAnnotated = True
+                    elif re.match(r'\s*$', l):
+                        inHeader = False
+                        continue
+                else:
+                    body.append(l)
+
+            if not isAnnotated:
+                body = ["lightweight tag imported by git p4\n"]
+
+            # Create the label - use the same view as the client spec we are using
+            clientSpec = getClientSpec()
+
+            labelTemplate  = "Label: %s\n" % name
+            labelTemplate += "Description:\n"
+            for b in body:
+                labelTemplate += "\t" + b + "\n"
+            labelTemplate += "View:\n"
+            for mapping in clientSpec.mappings:
+                labelTemplate += "\t%s\n" % mapping.depot_side.path
+
+            p4_write_pipe(["label", "-i"], labelTemplate)
+
+            # Use the label
+            p4_system(["tag", "-l", name] +
+                      ["%s@%s" % (mapping.depot_side.path, changelist) for mapping in clientSpec.mappings])
+
+            if verbose:
+                print "created p4 label for tag %s" % name
+
+    def run(self, args):
+        if len(args) == 0:
+            self.master = currentGitBranch()
+            if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
+                die("Detecting current git branch failed!")
+        elif len(args) == 1:
+            self.master = args[0]
+            if not branchExists(self.master):
+                die("Branch %s does not exist" % self.master)
+        else:
+            return False
+
+        allowSubmit = gitConfig("git-p4.allowSubmit")
+        if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
+            die("%s is not in git-p4.allowSubmit" % self.master)
+
+        [upstream, settings] = findUpstreamBranchPoint()
+        self.depotPath = settings['depot-paths'][0]
+        if len(self.origin) == 0:
+            self.origin = upstream
+
+        if self.preserveUser:
+            if not self.canChangeChangelists():
+                die("Cannot preserve user names without p4 super-user or admin permissions")
+
+        if self.verbose:
+            print "Origin branch is " + self.origin
+
+        if len(self.depotPath) == 0:
+            print "Internal error: cannot locate perforce depot path from existing branches"
+            sys.exit(128)
+
+        self.useClientSpec = False
+        if gitConfig("git-p4.useclientspec", "--bool") == "true":
+            self.useClientSpec = True
+        if self.useClientSpec:
+            self.clientSpecDirs = getClientSpec()
+
+        if self.useClientSpec:
+            # all files are relative to the client spec
+            self.clientPath = getClientRoot()
+        else:
+            self.clientPath = p4Where(self.depotPath)
+
+        if self.clientPath == "":
+            die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath)
+
+        print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
+        self.oldWorkingDirectory = os.getcwd()
+
+        # ensure the clientPath exists
+        new_client_dir = False
+        if not os.path.exists(self.clientPath):
+            new_client_dir = True
+            os.makedirs(self.clientPath)
+
+        chdir(self.clientPath)
+        print "Synchronizing p4 checkout..."
+        if new_client_dir:
+            # old one was destroyed, and maybe nobody told p4
+            p4_sync("...", "-f")
+        else:
+            p4_sync("...")
+        self.check()
+
+        commits = []
+        for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)):
+            commits.append(line.strip())
+        commits.reverse()
+
+        if self.preserveUser or (gitConfig("git-p4.skipUserNameCheck") == "true"):
+            self.checkAuthorship = False
+        else:
+            self.checkAuthorship = True
+
+        if self.preserveUser:
+            self.checkValidP4Users(commits)
+
+        while len(commits) > 0:
+            commit = commits[0]
+            commits = commits[1:]
+            self.applyCommit(commit)
+            if not self.interactive:
+                break
+
+        if len(commits) == 0:
+            print "All changes applied!"
+            chdir(self.oldWorkingDirectory)
+
+            sync = P4Sync()
+            sync.run([])
+
+            rebase = P4Rebase()
+            rebase.rebase()
+
+        if gitConfig("git-p4.exportLabels", "--bool") == "true":
+            self.exportLabels = True
+
+        if self.exportLabels:
+            p4Labels = getP4Labels(self.depotPath)
+            gitTags = getGitTags()
+
+            missingGitTags = gitTags - p4Labels
+            self.exportGitTags(missingGitTags)
+
+        return True
+
+class View(object):
+    """Represent a p4 view ("p4 help views"), and map files in a
+       repo according to the view."""
+
+    class Path(object):
+        """A depot or client path, possibly containing wildcards.
+           The only one supported is ... at the end, currently.
+           Initialize with the full path, with //depot or //client."""
+
+        def __init__(self, path, is_depot):
+            self.path = path
+            self.is_depot = is_depot
+            self.find_wildcards()
+            # remember the prefix bit, useful for relative mappings
+            m = re.match("(//[^/]+/)", self.path)
+            if not m:
+                die("Path %s does not start with //prefix/" % self.path)
+            prefix = m.group(1)
+            if not self.is_depot:
+                # strip //client/ on client paths
+                self.path = self.path[len(prefix):]
+
+        def find_wildcards(self):
+            """Make sure wildcards are valid, and set up internal
+               variables."""
+
+            self.ends_triple_dot = False
+            # There are three wildcards allowed in p4 views
+            # (see "p4 help views").  This code knows how to
+            # handle "..." (only at the end), but cannot deal with
+            # "%%n" or "*".  Only check the depot_side, as p4 should
+            # validate that the client_side matches too.
+            if re.search(r'%%[1-9]', self.path):
+                die("Can't handle %%n wildcards in view: %s" % self.path)
+            if self.path.find("*") >= 0:
+                die("Can't handle * wildcards in view: %s" % self.path)
+            triple_dot_index = self.path.find("...")
+            if triple_dot_index >= 0:
+                if triple_dot_index != len(self.path) - 3:
+                    die("Can handle only single ... wildcard, at end: %s" %
+                        self.path)
+                self.ends_triple_dot = True
+
+        def ensure_compatible(self, other_path):
+            """Make sure the wildcards agree."""
+            if self.ends_triple_dot != other_path.ends_triple_dot:
+                 die("Both paths must end with ... if either does;\n" +
+                     "paths: %s %s" % (self.path, other_path.path))
+
+        def match_wildcards(self, test_path):
+            """See if this test_path matches us, and fill in the value
+               of the wildcards if so.  Returns a tuple of
+               (True|False, wildcards[]).  For now, only the ... at end
+               is supported, so at most one wildcard."""
+            if self.ends_triple_dot:
+                dotless = self.path[:-3]
+                if test_path.startswith(dotless):
+                    wildcard = test_path[len(dotless):]
+                    return (True, [ wildcard ])
+            else:
+                if test_path == self.path:
+                    return (True, [])
+            return (False, [])
+
+        def match(self, test_path):
+            """Just return if it matches; don't bother with the wildcards."""
+            b, _ = self.match_wildcards(test_path)
+            return b
+
+        def fill_in_wildcards(self, wildcards):
+            """Return the relative path, with the wildcards filled in
+               if there are any."""
+            if self.ends_triple_dot:
+                return self.path[:-3] + wildcards[0]
+            else:
+                return self.path
+
+    class Mapping(object):
+        def __init__(self, depot_side, client_side, overlay, exclude):
+            # depot_side is without the trailing /... if it had one
+            self.depot_side = View.Path(depot_side, is_depot=True)
+            self.client_side = View.Path(client_side, is_depot=False)
+            self.overlay = overlay  # started with "+"
+            self.exclude = exclude  # started with "-"
+            assert not (self.overlay and self.exclude)
+            self.depot_side.ensure_compatible(self.client_side)
+
+        def __str__(self):
+            c = " "
+            if self.overlay:
+                c = "+"
+            if self.exclude:
+                c = "-"
+            return "View.Mapping: %s%s -> %s" % \
+                   (c, self.depot_side.path, self.client_side.path)
+
+        def map_depot_to_client(self, depot_path):
+            """Calculate the client path if using this mapping on the
+               given depot path; does not consider the effect of other
+               mappings in a view.  Even excluded mappings are returned."""
+            matches, wildcards = self.depot_side.match_wildcards(depot_path)
+            if not matches:
+                return ""
+            client_path = self.client_side.fill_in_wildcards(wildcards)
+            return client_path
+
+    #
+    # View methods
+    #
+    def __init__(self):
+        self.mappings = []
+
+    def append(self, view_line):
+        """Parse a view line, splitting it into depot and client
+           sides.  Append to self.mappings, preserving order."""
+
+        # Split the view line into exactly two words.  P4 enforces
+        # structure on these lines that simplifies this quite a bit.
+        #
+        # Either or both words may be double-quoted.
+        # Single quotes do not matter.
+        # Double-quote marks cannot occur inside the words.
+        # A + or - prefix is also inside the quotes.
+        # There are no quotes unless they contain a space.
+        # The line is already white-space stripped.
+        # The two words are separated by a single space.
+        #
+        if view_line[0] == '"':
+            # First word is double quoted.  Find its end.
+            close_quote_index = view_line.find('"', 1)
+            if close_quote_index <= 0:
+                die("No first-word closing quote found: %s" % view_line)
+            depot_side = view_line[1:close_quote_index]
+            # skip closing quote and space
+            rhs_index = close_quote_index + 1 + 1
+        else:
+            space_index = view_line.find(" ")
+            if space_index <= 0:
+                die("No word-splitting space found: %s" % view_line)
+            depot_side = view_line[0:space_index]
+            rhs_index = space_index + 1
+
+        if view_line[rhs_index] == '"':
+            # Second word is double quoted.  Make sure there is a
+            # double quote at the end too.
+            if not view_line.endswith('"'):
+                die("View line with rhs quote should end with one: %s" %
+                    view_line)
+            # skip the quotes
+            client_side = view_line[rhs_index+1:-1]
+        else:
+            client_side = view_line[rhs_index:]
+
+        # prefix + means overlay on previous mapping
+        overlay = False
+        if depot_side.startswith("+"):
+            overlay = True
+            depot_side = depot_side[1:]
+
+        # prefix - means exclude this path
+        exclude = False
+        if depot_side.startswith("-"):
+            exclude = True
+            depot_side = depot_side[1:]
+
+        m = View.Mapping(depot_side, client_side, overlay, exclude)
+        self.mappings.append(m)
+
+    def map_in_client(self, depot_path):
+        """Return the relative location in the client where this
+           depot file should live.  Returns "" if the file should
+           not be mapped in the client."""
+
+        paths_filled = []
+        client_path = ""
+
+        # look at later entries first
+        for m in self.mappings[::-1]:
+
+            # see where will this path end up in the client
+            p = m.map_depot_to_client(depot_path)
+
+            if p == "":
+                # Depot path does not belong in client.  Must remember
+                # this, as previous items should not cause files to
+                # exist in this path either.  Remember that the list is
+                # being walked from the end, which has higher precedence.
+                # Overlap mappings do not exclude previous mappings.
+                if not m.overlay:
+                    paths_filled.append(m.client_side)
+
+            else:
+                # This mapping matched; no need to search any further.
+                # But, the mapping could be rejected if the client path
+                # has already been claimed by an earlier mapping (i.e.
+                # one later in the list, which we are walking backwards).
+                already_mapped_in_client = False
+                for f in paths_filled:
+                    # this is View.Path.match
+                    if f.match(p):
+                        already_mapped_in_client = True
+                        break
+                if not already_mapped_in_client:
+                    # Include this file, unless it is from a line that
+                    # explicitly said to exclude it.
+                    if not m.exclude:
+                        client_path = p
+
+                # a match, even if rejected, always stops the search
+                break
+
+        return client_path
+
+class P4Sync(Command, P4UserMap):
+    delete_actions = ( "delete", "move/delete", "purge" )
+
+    def __init__(self):
+        Command.__init__(self)
+        P4UserMap.__init__(self)
+        self.options = [
+                optparse.make_option("--branch", dest="branch"),
+                optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"),
+                optparse.make_option("--changesfile", dest="changesFile"),
+                optparse.make_option("--silent", dest="silent", action="store_true"),
+                optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"),
+                optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
+                optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false",
+                                     help="Import into refs/heads/ , not refs/remotes"),
+                optparse.make_option("--max-changes", dest="maxChanges"),
+                optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true',
+                                     help="Keep entire BRANCH/DIR/SUBDIR prefix during import"),
+                optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true',
+                                     help="Only sync files that are included in the Perforce Client Spec")
+        ]
+        self.description = """Imports from Perforce into a git repository.\n
+    example:
+    //depot/my/project/ -- to import the current head
+    //depot/my/project/@all -- to import everything
+    //depot/my/project/@1,6 -- to import only from revision 1 to 6
+
+    (a ... is not needed in the path p4 specification, it's added implicitly)"""
+
+        self.usage += " //depot/path[@revRange]"
+        self.silent = False
+        self.createdBranches = set()
+        self.committedChanges = set()
+        self.branch = ""
+        self.detectBranches = False
+        self.detectLabels = False
+        self.importLabels = False
+        self.changesFile = ""
+        self.syncWithOrigin = True
+        self.importIntoRemotes = True
+        self.maxChanges = ""
+        self.isWindows = (platform.system() == "Windows")
+        self.keepRepoPath = False
+        self.depotPaths = None
+        self.p4BranchesInGit = []
+        self.cloneExclude = []
+        self.useClientSpec = False
+        self.useClientSpec_from_options = False
+        self.clientSpecDirs = None
+        self.tempBranches = []
+        self.tempBranchLocation = "git-p4-tmp"
+
+        if gitConfig("git-p4.syncFromOrigin") == "false":
+            self.syncWithOrigin = False
+
+    # Force a checkpoint in fast-import and wait for it to finish
+    def checkpoint(self):
+        self.gitStream.write("checkpoint\n\n")
+        self.gitStream.write("progress checkpoint\n\n")
+        out = self.gitOutput.readline()
+        if self.verbose:
+            print "checkpoint finished: " + out
+
+    def extractFilesFromCommit(self, commit):
+        self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
+                             for path in self.cloneExclude]
+        files = []
+        fnum = 0
+        while commit.has_key("depotFile%s" % fnum):
+            path =  commit["depotFile%s" % fnum]
+
+            if [p for p in self.cloneExclude
+                if p4PathStartsWith(path, p)]:
+                found = False
+            else:
+                found = [p for p in self.depotPaths
+                         if p4PathStartsWith(path, p)]
+            if not found:
+                fnum = fnum + 1
+                continue
+
+            file = {}
+            file["path"] = path
+            file["rev"] = commit["rev%s" % fnum]
+            file["action"] = commit["action%s" % fnum]
+            file["type"] = commit["type%s" % fnum]
+            files.append(file)
+            fnum = fnum + 1
+        return files
+
+    def stripRepoPath(self, path, prefixes):
+        if self.useClientSpec:
+            return self.clientSpecDirs.map_in_client(path)
+
+        if self.keepRepoPath:
+            prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
+
+        for p in prefixes:
+            if p4PathStartsWith(path, p):
+                path = path[len(p):]
+
+        return path
+
+    def splitFilesIntoBranches(self, commit):
+        branches = {}
+        fnum = 0
+        while commit.has_key("depotFile%s" % fnum):
+            path =  commit["depotFile%s" % fnum]
+            found = [p for p in self.depotPaths
+                     if p4PathStartsWith(path, p)]
+            if not found:
+                fnum = fnum + 1
+                continue
+
+            file = {}
+            file["path"] = path
+            file["rev"] = commit["rev%s" % fnum]
+            file["action"] = commit["action%s" % fnum]
+            file["type"] = commit["type%s" % fnum]
+            fnum = fnum + 1
+
+            relPath = self.stripRepoPath(path, self.depotPaths)
+            relPath = wildcard_decode(relPath)
+
+            for branch in self.knownBranches.keys():
+
+                # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2
+                if relPath.startswith(branch + "/"):
+                    if branch not in branches:
+                        branches[branch] = []
+                    branches[branch].append(file)
+                    break
+
+        return branches
+
+    # output one file from the P4 stream
+    # - helper for streamP4Files
+
+    def streamOneP4File(self, file, contents):
+        relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
+        relPath = wildcard_decode(relPath)
+        if verbose:
+            sys.stderr.write("%s\n" % relPath)
+
+        (type_base, type_mods) = split_p4_type(file["type"])
+
+        git_mode = "100644"
+        if "x" in type_mods:
+            git_mode = "100755"
+        if type_base == "symlink":
+            git_mode = "120000"
+            # p4 print on a symlink contains "target\n"; remove the newline
+            data = ''.join(contents)
+            contents = [data[:-1]]
+
+        if type_base == "utf16":
+            # p4 delivers different text in the python output to -G
+            # than it does when using "print -o", or normal p4 client
+            # operations.  utf16 is converted to ascii or utf8, perhaps.
+            # But ascii text saved as -t utf16 is completely mangled.
+            # Invoke print -o to get the real contents.
+            text = p4_read_pipe(['print', '-q', '-o', '-', file['depotFile']])
+            contents = [ text ]
+
+        if type_base == "apple":
+            # Apple filetype files will be streamed as a concatenation of
+            # its appledouble header and the contents.  This is useless
+            # on both macs and non-macs.  If using "print -q -o xx", it
+            # will create "xx" with the data, and "%xx" with the header.
+            # This is also not very useful.
+            #
+            # Ideally, someday, this script can learn how to generate
+            # appledouble files directly and import those to git, but
+            # non-mac machines can never find a use for apple filetype.
+            print "\nIgnoring apple filetype file %s" % file['depotFile']
+            return
+
+        # Perhaps windows wants unicode, utf16 newlines translated too;
+        # but this is not doing it.
+        if self.isWindows and type_base == "text":
+            mangled = []
+            for data in contents:
+                data = data.replace("\r\n", "\n")
+                mangled.append(data)
+            contents = mangled
+
+        # Note that we do not try to de-mangle keywords on utf16 files,
+        # even though in theory somebody may want that.
+        pattern = p4_keywords_regexp_for_type(type_base, type_mods)
+        if pattern:
+            regexp = re.compile(pattern, re.VERBOSE)
+            text = ''.join(contents)
+            text = regexp.sub(r'$\1$', text)
+            contents = [ text ]
+
+        self.gitStream.write("M %s inline %s\n" % (git_mode, relPath))
+
+        # total length...
+        length = 0
+        for d in contents:
+            length = length + len(d)
+
+        self.gitStream.write("data %d\n" % length)
+        for d in contents:
+            self.gitStream.write(d)
+        self.gitStream.write("\n")
+
+    def streamOneP4Deletion(self, file):
+        relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
+        relPath = wildcard_decode(relPath)
+        if verbose:
+            sys.stderr.write("delete %s\n" % relPath)
+        self.gitStream.write("D %s\n" % relPath)
+
+    # handle another chunk of streaming data
+    def streamP4FilesCb(self, marshalled):
+
+        if marshalled.has_key('depotFile') and self.stream_have_file_info:
+            # start of a new file - output the old one first
+            self.streamOneP4File(self.stream_file, self.stream_contents)
+            self.stream_file = {}
+            self.stream_contents = []
+            self.stream_have_file_info = False
+
+        # pick up the new file information... for the
+        # 'data' field we need to append to our array
+        for k in marshalled.keys():
+            if k == 'data':
+                self.stream_contents.append(marshalled['data'])
+            else:
+                self.stream_file[k] = marshalled[k]
+
+        self.stream_have_file_info = True
+
+    # Stream directly from "p4 files" into "git fast-import"
+    def streamP4Files(self, files):
+        filesForCommit = []
+        filesToRead = []
+        filesToDelete = []
+
+        for f in files:
+            # if using a client spec, only add the files that have
+            # a path in the client
+            if self.clientSpecDirs:
+                if self.clientSpecDirs.map_in_client(f['path']) == "":
+                    continue
+
+            filesForCommit.append(f)
+            if f['action'] in self.delete_actions:
+                filesToDelete.append(f)
+            else:
+                filesToRead.append(f)
+
+        # deleted files...
+        for f in filesToDelete:
+            self.streamOneP4Deletion(f)
+
+        if len(filesToRead) > 0:
+            self.stream_file = {}
+            self.stream_contents = []
+            self.stream_have_file_info = False
+
+            # curry self argument
+            def streamP4FilesCbSelf(entry):
+                self.streamP4FilesCb(entry)
+
+            fileArgs = ['%s#%s' % (f['path'], f['rev']) for f in filesToRead]
+
+            p4CmdList(["-x", "-", "print"],
+                      stdin=fileArgs,
+                      cb=streamP4FilesCbSelf)
+
+            # do the last chunk
+            if self.stream_file.has_key('depotFile'):
+                self.streamOneP4File(self.stream_file, self.stream_contents)
+
+    def make_email(self, userid):
+        if userid in self.users:
+            return self.users[userid]
+        else:
+            return "%s <a@b>" % userid
+
+    # Stream a p4 tag
+    def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
+        if verbose:
+            print "writing tag %s for commit %s" % (labelName, commit)
+        gitStream.write("tag %s\n" % labelName)
+        gitStream.write("from %s\n" % commit)
+
+        if labelDetails.has_key('Owner'):
+            owner = labelDetails["Owner"]
+        else:
+            owner = None
+
+        # Try to use the owner of the p4 label, or failing that,
+        # the current p4 user id.
+        if owner:
+            email = self.make_email(owner)
+        else:
+            email = self.make_email(self.p4UserId())
+        tagger = "%s %s %s" % (email, epoch, self.tz)
+
+        gitStream.write("tagger %s\n" % tagger)
+
+        print "labelDetails=",labelDetails
+        if labelDetails.has_key('Description'):
+            description = labelDetails['Description']
+        else:
+            description = 'Label from git p4'
+
+        gitStream.write("data %d\n" % len(description))
+        gitStream.write(description)
+        gitStream.write("\n")
+
+    def commit(self, details, files, branch, branchPrefixes, parent = ""):
+        epoch = details["time"]
+        author = details["user"]
+        self.branchPrefixes = branchPrefixes
+
+        if self.verbose:
+            print "commit into %s" % branch
+
+        # start with reading files; if that fails, we should not
+        # create a commit.
+        new_files = []
+        for f in files:
+            if [p for p in branchPrefixes if p4PathStartsWith(f['path'], p)]:
+                new_files.append (f)
+            else:
+                sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])
+
+        self.gitStream.write("commit %s\n" % branch)
+#        gitStream.write("mark :%s\n" % details["change"])
+        self.committedChanges.add(int(details["change"]))
+        committer = ""
+        if author not in self.users:
+            self.getUserMapFromPerforceServer()
+        committer = "%s %s %s" % (self.make_email(author), epoch, self.tz)
+
+        self.gitStream.write("committer %s\n" % committer)
+
+        self.gitStream.write("data <<EOT\n")
+        self.gitStream.write(details["desc"])
+        self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s"
+                             % (','.join (branchPrefixes), details["change"]))
+        if len(details['options']) > 0:
+            self.gitStream.write(": options = %s" % details['options'])
+        self.gitStream.write("]\nEOT\n\n")
+
+        if len(parent) > 0:
+            if self.verbose:
+                print "parent %s" % parent
+            self.gitStream.write("from %s\n" % parent)
+
+        self.streamP4Files(new_files)
+        self.gitStream.write("\n")
+
+        change = int(details["change"])
+
+        if self.labels.has_key(change):
+            label = self.labels[change]
+            labelDetails = label[0]
+            labelRevisions = label[1]
+            if self.verbose:
+                print "Change %s is labelled %s" % (change, labelDetails)
+
+            files = p4CmdList(["files"] + ["%s...@%s" % (p, change)
+                                                    for p in branchPrefixes])
+
+            if len(files) == len(labelRevisions):
+
+                cleanedFiles = {}
+                for info in files:
+                    if info["action"] in self.delete_actions:
+                        continue
+                    cleanedFiles[info["depotFile"]] = info["rev"]
+
+                if cleanedFiles == labelRevisions:
+                    self.streamTag(self.gitStream, 'tag_%s' % labelDetails['label'], labelDetails, branch, epoch)
+
+                else:
+                    if not self.silent:
+                        print ("Tag %s does not match with change %s: files do not match."
+                               % (labelDetails["label"], change))
+
+            else:
+                if not self.silent:
+                    print ("Tag %s does not match with change %s: file count is different."
+                           % (labelDetails["label"], change))
+
+    # Build a dictionary of changelists and labels, for "detect-labels" option.
+    def getLabels(self):
+        self.labels = {}
+
+        l = p4CmdList(["labels"] + ["%s..." % p for p in self.depotPaths])
+        if len(l) > 0 and not self.silent:
+            print "Finding files belonging to labels in %s" % `self.depotPaths`
+
+        for output in l:
+            label = output["label"]
+            revisions = {}
+            newestChange = 0
+            if self.verbose:
+                print "Querying files for label %s" % label
+            for file in p4CmdList(["files"] +
+                                      ["%s...@%s" % (p, label)
+                                          for p in self.depotPaths]):
+                revisions[file["depotFile"]] = file["rev"]
+                change = int(file["change"])
+                if change > newestChange:
+                    newestChange = change
+
+            self.labels[newestChange] = [output, revisions]
+
+        if self.verbose:
+            print "Label changes: %s" % self.labels.keys()
+
+    # Import p4 labels as git tags. A direct mapping does not
+    # exist, so assume that if all the files are at the same revision
+    # then we can use that, or it's something more complicated we should
+    # just ignore.
+    def importP4Labels(self, stream, p4Labels):
+        if verbose:
+            print "import p4 labels: " + ' '.join(p4Labels)
+
+        ignoredP4Labels = gitConfigList("git-p4.ignoredP4Labels")
+        validLabelRegexp = gitConfig("git-p4.labelImportRegexp")
+        if len(validLabelRegexp) == 0:
+            validLabelRegexp = defaultLabelRegexp
+        m = re.compile(validLabelRegexp)
+
+        for name in p4Labels:
+            commitFound = False
+
+            if not m.match(name):
+                if verbose:
+                    print "label %s does not match regexp %s" % (name,validLabelRegexp)
+                continue
+
+            if name in ignoredP4Labels:
+                continue
+
+            labelDetails = p4CmdList(['label', "-o", name])[0]
+
+            # get the most recent changelist for each file in this label
+            change = p4Cmd(["changes", "-m", "1"] + ["%s...@%s" % (p, name)
+                                for p in self.depotPaths])
+
+            if change.has_key('change'):
+                # find the corresponding git commit; take the oldest commit
+                changelist = int(change['change'])
+                gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
+                     "--reverse", ":/\[git-p4:.*change = %d\]" % changelist])
+                if len(gitCommit) == 0:
+                    print "could not find git commit for changelist %d" % changelist
+                else:
+                    gitCommit = gitCommit.strip()
+                    commitFound = True
+                    # Convert from p4 time format
+                    try:
+                        tmwhen = time.strptime(labelDetails['Update'], "%Y/%m/%d %H:%M:%S")
+                    except ValueError:
+                        print "Could not convert label time %s" % labelDetail['Update']
+                        tmwhen = 1
+
+                    when = int(time.mktime(tmwhen))
+                    self.streamTag(stream, name, labelDetails, gitCommit, when)
+                    if verbose:
+                        print "p4 label %s mapped to git commit %s" % (name, gitCommit)
+            else:
+                if verbose:
+                    print "Label %s has no changelists - possibly deleted?" % name
+
+            if not commitFound:
+                # We can't import this label; don't try again as it will get very
+                # expensive repeatedly fetching all the files for labels that will
+                # never be imported. If the label is moved in the future, the
+                # ignore will need to be removed manually.
+                system(["git", "config", "--add", "git-p4.ignoredP4Labels", name])
+
+    def guessProjectName(self):
+        for p in self.depotPaths:
+            if p.endswith("/"):
+                p = p[:-1]
+            p = p[p.strip().rfind("/") + 1:]
+            if not p.endswith("/"):
+               p += "/"
+            return p
+
+    def getBranchMapping(self):
+        lostAndFoundBranches = set()
+
+        user = gitConfig("git-p4.branchUser")
+        if len(user) > 0:
+            command = "branches -u %s" % user
+        else:
+            command = "branches"
+
+        for info in p4CmdList(command):
+            details = p4Cmd(["branch", "-o", info["branch"]])
+            viewIdx = 0
+            while details.has_key("View%s" % viewIdx):
+                paths = details["View%s" % viewIdx].split(" ")
+                viewIdx = viewIdx + 1
+                # require standard //depot/foo/... //depot/bar/... mapping
+                if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."):
+                    continue
+                source = paths[0]
+                destination = paths[1]
+                ## HACK
+                if p4PathStartsWith(source, self.depotPaths[0]) and p4PathStartsWith(destination, self.depotPaths[0]):
+                    source = source[len(self.depotPaths[0]):-4]
+                    destination = destination[len(self.depotPaths[0]):-4]
+
+                    if destination in self.knownBranches:
+                        if not self.silent:
+                            print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
+                            print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
+                        continue
+
+                    self.knownBranches[destination] = source
+
+                    lostAndFoundBranches.discard(destination)
+
+                    if source not in self.knownBranches:
+                        lostAndFoundBranches.add(source)
+
+        # Perforce does not strictly require branches to be defined, so we also
+        # check git config for a branch list.
+        #
+        # Example of branch definition in git config file:
+        # [git-p4]
+        #   branchList=main:branchA
+        #   branchList=main:branchB
+        #   branchList=branchA:branchC
+        configBranches = gitConfigList("git-p4.branchList")
+        for branch in configBranches:
+            if branch:
+                (source, destination) = branch.split(":")
+                self.knownBranches[destination] = source
+
+                lostAndFoundBranches.discard(destination)
+
+                if source not in self.knownBranches:
+                    lostAndFoundBranches.add(source)
+
+
+        for branch in lostAndFoundBranches:
+            self.knownBranches[branch] = branch
+
+    def getBranchMappingFromGitBranches(self):
+        branches = p4BranchesInGit(self.importIntoRemotes)
+        for branch in branches.keys():
+            if branch == "master":
+                branch = "main"
+            else:
+                branch = branch[len(self.projectName):]
+            self.knownBranches[branch] = branch
+
+    def listExistingP4GitBranches(self):
+        # branches holds mapping from name to commit
+        branches = p4BranchesInGit(self.importIntoRemotes)
+        self.p4BranchesInGit = branches.keys()
+        for branch in branches.keys():
+            self.initialParents[self.refPrefix + branch] = branches[branch]
+
+    def updateOptionDict(self, d):
+        option_keys = {}
+        if self.keepRepoPath:
+            option_keys['keepRepoPath'] = 1
+
+        d["options"] = ' '.join(sorted(option_keys.keys()))
+
+    def readOptions(self, d):
+        self.keepRepoPath = (d.has_key('options')
+                             and ('keepRepoPath' in d['options']))
+
+    def gitRefForBranch(self, branch):
+        if branch == "main":
+            return self.refPrefix + "master"
+
+        if len(branch) <= 0:
+            return branch
+
+        return self.refPrefix + self.projectName + branch
+
+    def gitCommitByP4Change(self, ref, change):
+        if self.verbose:
+            print "looking in ref " + ref + " for change %s using bisect..." % change
+
+        earliestCommit = ""
+        latestCommit = parseRevision(ref)
+
+        while True:
+            if self.verbose:
+                print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
+            next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
+            if len(next) == 0:
+                if self.verbose:
+                    print "argh"
+                return ""
+            log = extractLogMessageFromGitCommit(next)
+            settings = extractSettingsGitLog(log)
+            currentChange = int(settings['change'])
+            if self.verbose:
+                print "current change %s" % currentChange
+
+            if currentChange == change:
+                if self.verbose:
+                    print "found %s" % next
+                return next
+
+            if currentChange < change:
+                earliestCommit = "^%s" % next
+            else:
+                latestCommit = "%s" % next
+
+        return ""
+
+    def importNewBranch(self, branch, maxChange):
+        # make fast-import flush all changes to disk and update the refs using the checkpoint
+        # command so that we can try to find the branch parent in the git history
+        self.gitStream.write("checkpoint\n\n");
+        self.gitStream.flush();
+        branchPrefix = self.depotPaths[0] + branch + "/"
+        range = "@1,%s" % maxChange
+        #print "prefix" + branchPrefix
+        changes = p4ChangesForPaths([branchPrefix], range)
+        if len(changes) <= 0:
+            return False
+        firstChange = changes[0]
+        #print "first change in branch: %s" % firstChange
+        sourceBranch = self.knownBranches[branch]
+        sourceDepotPath = self.depotPaths[0] + sourceBranch
+        sourceRef = self.gitRefForBranch(sourceBranch)
+        #print "source " + sourceBranch
+
+        branchParentChange = int(p4Cmd(["changes", "-m", "1", "%s...@1,%s" % (sourceDepotPath, firstChange)])["change"])
+        #print "branch parent: %s" % branchParentChange
+        gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
+        if len(gitParent) > 0:
+            self.initialParents[self.gitRefForBranch(branch)] = gitParent
+            #print "parent git commit: %s" % gitParent
+
+        self.importChanges(changes)
+        return True
+
+    def searchParent(self, parent, branch, target):
+        parentFound = False
+        for blob in read_pipe_lines(["git", "rev-list", "--reverse", "--no-merges", parent]):
+            blob = blob.strip()
+            if len(read_pipe(["git", "diff-tree", blob, target])) == 0:
+                parentFound = True
+                if self.verbose:
+                    print "Found parent of %s in commit %s" % (branch, blob)
+                break
+        if parentFound:
+            return blob
+        else:
+            return None
+
+    def importChanges(self, changes):
+        cnt = 1
+        for change in changes:
+            description = p4Cmd(["describe", str(change)])
+            self.updateOptionDict(description)
+
+            if not self.silent:
+                sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+                sys.stdout.flush()
+            cnt = cnt + 1
+
+            try:
+                if self.detectBranches:
+                    branches = self.splitFilesIntoBranches(description)
+                    for branch in branches.keys():
+                        ## HACK  --hwn
+                        branchPrefix = self.depotPaths[0] + branch + "/"
+
+                        parent = ""
+
+                        filesForCommit = branches[branch]
+
+                        if self.verbose:
+                            print "branch is %s" % branch
+
+                        self.updatedBranches.add(branch)
+
+                        if branch not in self.createdBranches:
+                            self.createdBranches.add(branch)
+                            parent = self.knownBranches[branch]
+                            if parent == branch:
+                                parent = ""
+                            else:
+                                fullBranch = self.projectName + branch
+                                if fullBranch not in self.p4BranchesInGit:
+                                    if not self.silent:
+                                        print("\n    Importing new branch %s" % fullBranch);
+                                    if self.importNewBranch(branch, change - 1):
+                                        parent = ""
+                                        self.p4BranchesInGit.append(fullBranch)
+                                    if not self.silent:
+                                        print("\n    Resuming with change %s" % change);
+
+                                if self.verbose:
+                                    print "parent determined through known branches: %s" % parent
+
+                        branch = self.gitRefForBranch(branch)
+                        parent = self.gitRefForBranch(parent)
+
+                        if self.verbose:
+                            print "looking for initial parent for %s; current parent is %s" % (branch, parent)
+
+                        if len(parent) == 0 and branch in self.initialParents:
+                            parent = self.initialParents[branch]
+                            del self.initialParents[branch]
+
+                        blob = None
+                        if len(parent) > 0:
+                            tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change))
+                            if self.verbose:
+                                print "Creating temporary branch: " + tempBranch
+                            self.commit(description, filesForCommit, tempBranch, [branchPrefix])
+                            self.tempBranches.append(tempBranch)
+                            self.checkpoint()
+                            blob = self.searchParent(parent, branch, tempBranch)
+                        if blob:
+                            self.commit(description, filesForCommit, branch, [branchPrefix], blob)
+                        else:
+                            if self.verbose:
+                                print "Parent of %s not found. Committing into head of %s" % (branch, parent)
+                            self.commit(description, filesForCommit, branch, [branchPrefix], parent)
+                else:
+                    files = self.extractFilesFromCommit(description)
+                    self.commit(description, files, self.branch, self.depotPaths,
+                                self.initialParent)
+                    self.initialParent = ""
+            except IOError:
+                print self.gitError.read()
+                sys.exit(1)
+
+    def importHeadRevision(self, revision):
+        print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
+
+        details = {}
+        details["user"] = "git perforce import user"
+        details["desc"] = ("Initial import of %s from the state at revision %s\n"
+                           % (' '.join(self.depotPaths), revision))
+        details["change"] = revision
+        newestRevision = 0
+
+        fileCnt = 0
+        fileArgs = ["%s...%s" % (p,revision) for p in self.depotPaths]
+
+        for info in p4CmdList(["files"] + fileArgs):
+
+            if 'code' in info and info['code'] == 'error':
+                sys.stderr.write("p4 returned an error: %s\n"
+                                 % info['data'])
+                if info['data'].find("must refer to client") >= 0:
+                    sys.stderr.write("This particular p4 error is misleading.\n")
+                    sys.stderr.write("Perhaps the depot path was misspelled.\n");
+                    sys.stderr.write("Depot path:  %s\n" % " ".join(self.depotPaths))
+                sys.exit(1)
+            if 'p4ExitCode' in info:
+                sys.stderr.write("p4 exitcode: %s\n" % info['p4ExitCode'])
+                sys.exit(1)
+
+
+            change = int(info["change"])
+            if change > newestRevision:
+                newestRevision = change
+
+            if info["action"] in self.delete_actions:
+                # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
+                #fileCnt = fileCnt + 1
+                continue
+
+            for prop in ["depotFile", "rev", "action", "type" ]:
+                details["%s%s" % (prop, fileCnt)] = info[prop]
+
+            fileCnt = fileCnt + 1
+
+        details["change"] = newestRevision
+
+        # Use time from top-most change so that all git p4 clones of
+        # the same p4 repo have the same commit SHA1s.
+        res = p4CmdList("describe -s %d" % newestRevision)
+        newestTime = None
+        for r in res:
+            if r.has_key('time'):
+                newestTime = int(r['time'])
+        if newestTime is None:
+            die("\"describe -s\" on newest change %d did not give a time")
+        details["time"] = newestTime
+
+        self.updateOptionDict(details)
+        try:
+            self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
+        except IOError:
+            print "IO error with git fast-import. Is your git version recent enough?"
+            print self.gitError.read()
+
+
+    def run(self, args):
+        self.depotPaths = []
+        self.changeRange = ""
+        self.initialParent = ""
+        self.previousDepotPaths = []
+
+        # map from branch depot path to parent branch
+        self.knownBranches = {}
+        self.initialParents = {}
+        self.hasOrigin = originP4BranchesExist()
+        if not self.syncWithOrigin:
+            self.hasOrigin = False
+
+        if self.importIntoRemotes:
+            self.refPrefix = "refs/remotes/p4/"
+        else:
+            self.refPrefix = "refs/heads/p4/"
+
+        if self.syncWithOrigin and self.hasOrigin:
+            if not self.silent:
+                print "Syncing with origin first by calling git fetch origin"
+            system("git fetch origin")
+
+        if len(self.branch) == 0:
+            self.branch = self.refPrefix + "master"
+            if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
+                system("git update-ref %s refs/heads/p4" % self.branch)
+                system("git branch -D p4");
+            # create it /after/ importing, when master exists
+            if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
+                system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
+
+        # accept either the command-line option, or the configuration variable
+        if self.useClientSpec:
+            # will use this after clone to set the variable
+            self.useClientSpec_from_options = True
+        else:
+            if gitConfig("git-p4.useclientspec", "--bool") == "true":
+                self.useClientSpec = True
+        if self.useClientSpec:
+            self.clientSpecDirs = getClientSpec()
+
+        # TODO: should always look at previous commits,
+        # merge with previous imports, if possible.
+        if args == []:
+            if self.hasOrigin:
+                createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent)
+            self.listExistingP4GitBranches()
+
+            if len(self.p4BranchesInGit) > 1:
+                if not self.silent:
+                    print "Importing from/into multiple branches"
+                self.detectBranches = True
+
+            if self.verbose:
+                print "branches: %s" % self.p4BranchesInGit
+
+            p4Change = 0
+            for branch in self.p4BranchesInGit:
+                logMsg =  extractLogMessageFromGitCommit(self.refPrefix + branch)
+
+                settings = extractSettingsGitLog(logMsg)
+
+                self.readOptions(settings)
+                if (settings.has_key('depot-paths')
+                    and settings.has_key ('change')):
+                    change = int(settings['change']) + 1
+                    p4Change = max(p4Change, change)
+
+                    depotPaths = sorted(settings['depot-paths'])
+                    if self.previousDepotPaths == []:
+                        self.previousDepotPaths = depotPaths
+                    else:
+                        paths = []
+                        for (prev, cur) in zip(self.previousDepotPaths, depotPaths):
+                            prev_list = prev.split("/")
+                            cur_list = cur.split("/")
+                            for i in range(0, min(len(cur_list), len(prev_list))):
+                                if cur_list[i] <> prev_list[i]:
+                                    i = i - 1
+                                    break
+
+                            paths.append ("/".join(cur_list[:i + 1]))
+
+                        self.previousDepotPaths = paths
+
+            if p4Change > 0:
+                self.depotPaths = sorted(self.previousDepotPaths)
+                self.changeRange = "@%s,#head" % p4Change
+                if not self.detectBranches:
+                    self.initialParent = parseRevision(self.branch)
+                if not self.silent and not self.detectBranches:
+                    print "Performing incremental import into %s git branch" % self.branch
+
+        if not self.branch.startswith("refs/"):
+            self.branch = "refs/heads/" + self.branch
+
+        if len(args) == 0 and self.depotPaths:
+            if not self.silent:
+                print "Depot paths: %s" % ' '.join(self.depotPaths)
+        else:
+            if self.depotPaths and self.depotPaths != args:
+                print ("previous import used depot path %s and now %s was specified. "
+                       "This doesn't work!" % (' '.join (self.depotPaths),
+                                               ' '.join (args)))
+                sys.exit(1)
+
+            self.depotPaths = sorted(args)
+
+        revision = ""
+        self.users = {}
+
+        # Make sure no revision specifiers are used when --changesfile
+        # is specified.
+        bad_changesfile = False
+        if len(self.changesFile) > 0:
+            for p in self.depotPaths:
+                if p.find("@") >= 0 or p.find("#") >= 0:
+                    bad_changesfile = True
+                    break
+        if bad_changesfile:
+            die("Option --changesfile is incompatible with revision specifiers")
+
+        newPaths = []
+        for p in self.depotPaths:
+            if p.find("@") != -1:
+                atIdx = p.index("@")
+                self.changeRange = p[atIdx:]
+                if self.changeRange == "@all":
+                    self.changeRange = ""
+                elif ',' not in self.changeRange:
+                    revision = self.changeRange
+                    self.changeRange = ""
+                p = p[:atIdx]
+            elif p.find("#") != -1:
+                hashIdx = p.index("#")
+                revision = p[hashIdx:]
+                p = p[:hashIdx]
+            elif self.previousDepotPaths == []:
+                # pay attention to changesfile, if given, else import
+                # the entire p4 tree at the head revision
+                if len(self.changesFile) == 0:
+                    revision = "#head"
+
+            p = re.sub ("\.\.\.$", "", p)
+            if not p.endswith("/"):
+                p += "/"
+
+            newPaths.append(p)
+
+        self.depotPaths = newPaths
+
+        self.loadUserMapFromCache()
+        self.labels = {}
+        if self.detectLabels:
+            self.getLabels();
+
+        if self.detectBranches:
+            ## FIXME - what's a P4 projectName ?
+            self.projectName = self.guessProjectName()
+
+            if self.hasOrigin:
+                self.getBranchMappingFromGitBranches()
+            else:
+                self.getBranchMapping()
+            if self.verbose:
+                print "p4-git branches: %s" % self.p4BranchesInGit
+                print "initial parents: %s" % self.initialParents
+            for b in self.p4BranchesInGit:
+                if b != "master":
+
+                    ## FIXME
+                    b = b[len(self.projectName):]
+                self.createdBranches.add(b)
+
+        self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60))
+
+        importProcess = subprocess.Popen(["git", "fast-import"],
+                                         stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                                         stderr=subprocess.PIPE);
+        self.gitOutput = importProcess.stdout
+        self.gitStream = importProcess.stdin
+        self.gitError = importProcess.stderr
+
+        if revision:
+            self.importHeadRevision(revision)
+        else:
+            changes = []
+
+            if len(self.changesFile) > 0:
+                output = open(self.changesFile).readlines()
+                changeSet = set()
+                for line in output:
+                    changeSet.add(int(line))
+
+                for change in changeSet:
+                    changes.append(change)
+
+                changes.sort()
+            else:
+                # catch "git p4 sync" with no new branches, in a repo that
+                # does not have any existing p4 branches
+                if len(args) == 0 and not self.p4BranchesInGit:
+                    die("No remote p4 branches.  Perhaps you never did \"git p4 clone\" in here.");
+                if self.verbose:
+                    print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
+                                                              self.changeRange)
+                changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
+
+                if len(self.maxChanges) > 0:
+                    changes = changes[:min(int(self.maxChanges), len(changes))]
+
+            if len(changes) == 0:
+                if not self.silent:
+                    print "No changes to import!"
+            else:
+                if not self.silent and not self.detectBranches:
+                    print "Import destination: %s" % self.branch
+
+                self.updatedBranches = set()
+
+                self.importChanges(changes)
+
+                if not self.silent:
+                    print ""
+                    if len(self.updatedBranches) > 0:
+                        sys.stdout.write("Updated branches: ")
+                        for b in self.updatedBranches:
+                            sys.stdout.write("%s " % b)
+                        sys.stdout.write("\n")
+
+        if gitConfig("git-p4.importLabels", "--bool") == "true":
+            self.importLabels = True
+
+        if self.importLabels:
+            p4Labels = getP4Labels(self.depotPaths)
+            gitTags = getGitTags()
+
+            missingP4Labels = p4Labels - gitTags
+            self.importP4Labels(self.gitStream, missingP4Labels)
+
+        self.gitStream.close()
+        if importProcess.wait() != 0:
+            die("fast-import failed: %s" % self.gitError.read())
+        self.gitOutput.close()
+        self.gitError.close()
+
+        # Cleanup temporary branches created during import
+        if self.tempBranches != []:
+            for branch in self.tempBranches:
+                read_pipe("git update-ref -d %s" % branch)
+            os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
+
+        return True
+
+class P4Rebase(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = [
+                optparse.make_option("--import-labels", dest="importLabels", action="store_true"),
+        ]
+        self.importLabels = False
+        self.description = ("Fetches the latest revision from perforce and "
+                            + "rebases the current work (branch) against it")
+
+    def run(self, args):
+        sync = P4Sync()
+        sync.importLabels = self.importLabels
+        sync.run([])
+
+        return self.rebase()
+
+    def rebase(self):
+        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.");
+
+        [upstream, settings] = findUpstreamBranchPoint()
+        if len(upstream) == 0:
+            die("Cannot find upstream branchpoint for rebase")
+
+        # the branchpoint may be p4/foo~3, so strip off the parent
+        upstream = re.sub("~[0-9]+$", "", upstream)
+
+        print "Rebasing the current branch onto %s" % upstream
+        oldHead = read_pipe("git rev-parse HEAD").strip()
+        system("git rebase %s" % upstream)
+        system("git diff-tree --stat --summary -M %s HEAD" % oldHead)
+        return True
+
+class P4Clone(P4Sync):
+    def __init__(self):
+        P4Sync.__init__(self)
+        self.description = "Creates a new git repository and imports from Perforce into it"
+        self.usage = "usage: %prog [options] //depot/path[@revRange]"
+        self.options += [
+            optparse.make_option("--destination", dest="cloneDestination",
+                                 action='store', default=None,
+                                 help="where to leave result of the clone"),
+            optparse.make_option("-/", dest="cloneExclude",
+                                 action="append", type="string",
+                                 help="exclude depot path"),
+            optparse.make_option("--bare", dest="cloneBare",
+                                 action="store_true", default=False),
+        ]
+        self.cloneDestination = None
+        self.needsGit = False
+        self.cloneBare = False
+
+    # This is required for the "append" cloneExclude action
+    def ensure_value(self, attr, value):
+        if not hasattr(self, attr) or getattr(self, attr) is None:
+            setattr(self, attr, value)
+        return getattr(self, attr)
+
+    def defaultDestination(self, args):
+        ## TODO: use common prefix of args?
+        depotPath = args[0]
+        depotDir = re.sub("(@[^@]*)$", "", depotPath)
+        depotDir = re.sub("(#[^#]*)$", "", depotDir)
+        depotDir = re.sub(r"\.\.\.$", "", depotDir)
+        depotDir = re.sub(r"/$", "", depotDir)
+        return os.path.split(depotDir)[1]
+
+    def run(self, args):
+        if len(args) < 1:
+            return False
+
+        if self.keepRepoPath and not self.cloneDestination:
+            sys.stderr.write("Must specify destination for --keep-path\n")
+            sys.exit(1)
+
+        depotPaths = args
+
+        if not self.cloneDestination and len(depotPaths) > 1:
+            self.cloneDestination = depotPaths[-1]
+            depotPaths = depotPaths[:-1]
+
+        self.cloneExclude = ["/"+p for p in self.cloneExclude]
+        for p in depotPaths:
+            if not p.startswith("//"):
+                return False
+
+        if not self.cloneDestination:
+            self.cloneDestination = self.defaultDestination(args)
+
+        print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
+
+        if not os.path.exists(self.cloneDestination):
+            os.makedirs(self.cloneDestination)
+        chdir(self.cloneDestination)
+
+        init_cmd = [ "git", "init" ]
+        if self.cloneBare:
+            init_cmd.append("--bare")
+        subprocess.check_call(init_cmd)
+
+        if not P4Sync.run(self, depotPaths):
+            return False
+        if self.branch != "master":
+            if self.importIntoRemotes:
+                masterbranch = "refs/remotes/p4/master"
+            else:
+                masterbranch = "refs/heads/p4/master"
+            if gitBranchExists(masterbranch):
+                system("git branch master %s" % masterbranch)
+                if not self.cloneBare:
+                    system("git checkout -f")
+            else:
+                print "Could not detect main branch. No checkout/master branch created."
+
+        # auto-set this variable if invoked with --use-client-spec
+        if self.useClientSpec_from_options:
+            system("git config --bool git-p4.useclientspec true")
+
+        return True
+
+class P4Branches(Command):
+    def __init__(self):
+        Command.__init__(self)
+        self.options = [ ]
+        self.description = ("Shows the git branches that hold imports and their "
+                            + "corresponding perforce depot paths")
+        self.verbose = False
+
+    def run(self, args):
+        if originP4BranchesExist():
+            createOrUpdateBranchesFromOrigin()
+
+        cmdline = "git rev-parse --symbolic "
+        cmdline += " --remotes"
+
+        for line in read_pipe_lines(cmdline):
+            line = line.strip()
+
+            if not line.startswith('p4/') or line == "p4/HEAD":
+                continue
+            branch = line
+
+            log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
+            settings = extractSettingsGitLog(log)
+
+            print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
+        return True
+
+class HelpFormatter(optparse.IndentedHelpFormatter):
+    def __init__(self):
+        optparse.IndentedHelpFormatter.__init__(self)
+
+    def format_description(self, description):
+        if description:
+            return description + "\n"
+        else:
+            return ""
+
+def printUsage(commands):
+    print "usage: %s <command> [options]" % sys.argv[0]
+    print ""
+    print "valid commands: %s" % ", ".join(commands)
+    print ""
+    print "Try %s <command> --help for command specific help." % sys.argv[0]
+    print ""
+
+commands = {
+    "debug" : P4Debug,
+    "submit" : P4Submit,
+    "commit" : P4Submit,
+    "sync" : P4Sync,
+    "rebase" : P4Rebase,
+    "clone" : P4Clone,
+    "rollback" : P4RollBack,
+    "branches" : P4Branches
+}
+
+
+def main():
+    if len(sys.argv[1:]) == 0:
+        printUsage(commands.keys())
+        sys.exit(2)
+
+    cmd = ""
+    cmdName = sys.argv[1]
+    try:
+        klass = commands[cmdName]
+        cmd = klass()
+    except KeyError:
+        print "unknown command %s" % cmdName
+        print ""
+        printUsage(commands.keys())
+        sys.exit(2)
+
+    options = cmd.options
+    cmd.gitdir = os.environ.get("GIT_DIR", None)
+
+    args = sys.argv[2:]
+
+    options.append(optparse.make_option("--verbose", dest="verbose", action="store_true"))
+    if cmd.needsGit:
+        options.append(optparse.make_option("--git-dir", dest="gitdir"))
+
+    parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName),
+                                   options,
+                                   description = cmd.description,
+                                   formatter = HelpFormatter())
+
+    (cmd, args) = parser.parse_args(sys.argv[2:], cmd);
+    global verbose
+    verbose = cmd.verbose
+    if cmd.needsGit:
+        if cmd.gitdir == None:
+            cmd.gitdir = os.path.abspath(".git")
+            if not isValidGitDir(cmd.gitdir):
+                cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
+                if os.path.exists(cmd.gitdir):
+                    cdup = read_pipe("git rev-parse --show-cdup").strip()
+                    if len(cdup) > 0:
+                        chdir(cdup);
+
+        if not isValidGitDir(cmd.gitdir):
+            if isValidGitDir(cmd.gitdir + "/.git"):
+                cmd.gitdir += "/.git"
+            else:
+                die("fatal: cannot locate git repository at %s" % cmd.gitdir)
+
+        os.environ["GIT_DIR"] = cmd.gitdir
+
+    if not cmd.run(args):
+        parser.print_help()
+        sys.exit(2)
+
+
+if __name__ == '__main__':
+    main()
index c815a2412c2f33556ad712d328eaa80241c07bda..04d89415fe8ca1634fa362c66ea9569794dcadd5 100644 (file)
@@ -20,11 +20,20 @@ esac
 
 test -n "$rebase_root" && root_flag=--root
 
-git format-patch -k --stdout --full-index --ignore-if-in-upstream \
-       --src-prefix=a/ --dst-prefix=b/ \
-       --no-renames $root_flag "$revisions" |
-git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" &&
-move_to_original_branch
+if test -n "$keep_empty"
+then
+       # we have to do this the hard way.  git format-patch completely squashes
+       # empty commits and even if it didn't the format doesn't really lend
+       # itself well to recording empty patches.  fortunately, cherry-pick
+       # makes this easy
+       git cherry-pick --allow-empty "$revisions"
+else
+       git format-patch -k --stdout --full-index --ignore-if-in-upstream \
+               --src-prefix=a/ --dst-prefix=b/ \
+               --no-renames $root_flag "$revisions" |
+       git am $git_am_opt --rebasing --resolvemsg="$resolvemsg"
+fi && move_to_original_branch
+
 ret=$?
 test 0 != $ret -a -d "$state_dir" && write_basic_state
 exit $ret
index 5812222eb9afa2b2903040d7cf32ab0fb33c3508..0c19b7c7539192851d5bd43df686baa97dc0df73 100644 (file)
@@ -167,6 +167,14 @@ has_action () {
        sane_grep '^[^#]' "$1" >/dev/null
 }
 
+is_empty_commit() {
+       tree=$(git rev-parse -q --verify "$1"^{tree} 2>/dev/null ||
+               die "$1: not a commit that can be picked")
+       ptree=$(git rev-parse -q --verify "$1"^^{tree} 2>/dev/null ||
+               ptree=4b825dc642cb6eb9a060e54bf8d69288fbee4904)
+       test "$tree" = "$ptree"
+}
+
 # Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
 # GIT_AUTHOR_DATE exported from the current environment.
 do_with_author () {
@@ -191,12 +199,19 @@ git_sequence_editor () {
 
 pick_one () {
        ff=--ff
+
        case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
        case "$force_rebase" in '') ;; ?*) ff= ;; esac
        output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
+
+       if is_empty_commit "$sha1"
+       then
+               empty_args="--allow-empty"
+       fi
+
        test -d "$rewritten" &&
                pick_one_preserving_merges "$@" && return
-       output git cherry-pick $ff "$@"
+       output git cherry-pick $empty_args $ff "$@"
 }
 
 pick_one_preserving_merges () {
@@ -672,7 +687,7 @@ rearrange_squash () {
 case "$action" in
 continue)
        # do we have anything to commit?
-       if git diff-index --cached --quiet --ignore-submodules HEAD --
+       if git diff-index --cached --quiet HEAD --
        then
                : Nothing to commit -- skip this
        else
@@ -780,9 +795,17 @@ git rev-list $merges_option --pretty=oneline --abbrev-commit \
        sed -n "s/^>//p" |
 while read -r shortsha1 rest
 do
+
+       if test -z "$keep_empty" && is_empty_commit $shortsha1
+       then
+               comment_out="# "
+       else
+               comment_out=
+       fi
+
        if test t != "$preserve_merges"
        then
-               printf '%s\n' "pick $shortsha1 $rest" >> "$todo"
+               printf '%s\n' "${comment_out}pick $shortsha1 $rest" >>"$todo"
        else
                sha1=$(git rev-parse $shortsha1)
                if test -z "$rebase_root"
@@ -801,7 +824,7 @@ do
                if test f = "$preserve"
                then
                        touch "$rewritten"/$sha1
-                       printf '%s\n' "pick $shortsha1 $rest" >> "$todo"
+                       printf '%s\n' "${comment_out}pick $shortsha1 $rest" >>"$todo"
                fi
        fi
 done
@@ -846,11 +869,19 @@ cat >> "$todo" << EOF
 #  f, fixup = like "squash", but discard this commit's log message
 #  x, exec = run command (the rest of the line) using shell
 #
+# These lines can be re-ordered; they are executed from top to bottom.
+#
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
 #
 EOF
 
+if test -z "$keep_empty"
+then
+       echo "# Note that empty commits are commented out" >>"$todo"
+fi
+
+
 has_action "$todo" ||
        die_abort "Nothing to do"
 
index 69c1374823084804662dbd6b6d510ec2ec3428e9..24a2840033c175ff5399de82a1a777c75c0e85dd 100755 (executable)
@@ -43,6 +43,7 @@ s,strategy=!       use the given merge strategy
 no-ff!             cherry-pick all commits, even if unchanged
 m,merge!           use merging strategies to rebase
 i,interactive!     let the user edit the list of commits to rebase
+k,keep-empty      preserve empty commits during rebase
 f,force-rebase!    force rebase even if branch is up to date
 X,strategy-option=! pass the argument through to the merge strategy
 stat!              display a diffstat of what changed upstream
@@ -97,6 +98,7 @@ state_dir=
 action=
 preserve_merges=
 autosquash=
+keep_empty=
 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 
 read_basic_state () {
@@ -220,6 +222,9 @@ do
        -i)
                interactive_rebase=explicit
                ;;
+       -k)
+               keep_empty=yes
+               ;;
        -p)
                preserve_merges=t
                test -z "$interactive_rebase" && interactive_rebase=implied
index e136732cea80c1594ac02c93cf246e8c77ff89b8..f29285c411a6dc9b58cffb47b91577bb2d023bc6 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env perl
+#!/usr/bin/perl
 # Copyright 2005, Ryan Anderson <ryan@michonline.com>
 # Distribution permitted under the GPL v2, as distributed
 # by the Free Software Foundation.
index 3dc4851cfc70a2804b23a8eca4dac7702ae93c87..5f3ebd244dac3ca28298ece9df914102b8155660 100644 (file)
@@ -22,6 +22,7 @@
     _digest = sha.new
 import sys
 import os
+import time
 sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
 
 from git_remote_helpers.util import die, debug, warn
@@ -204,6 +205,11 @@ def read_one_line(repo):
     """Reads and processes one command.
     """
 
+    sleepy = os.environ.get("GIT_REMOTE_TESTGIT_SLEEPY")
+    if sleepy:
+        debug("Sleeping %d sec before readline" % int(sleepy))
+        time.sleep(int(sleepy))
+
     line = sys.stdin.readline()
 
     cmdline = line
@@ -258,6 +264,7 @@ def main(args):
 
     more = True
 
+    sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0)
     while (more):
         more = read_one_line(repo)
 
index 624feec26f66b15926ffa11767c146222bc9a185..757933174e4c17136f14a1fdd7c8c67c0a8bca50 100755 (executable)
@@ -15,6 +15,7 @@ F               pass --no-reuse-object to git-pack-objects
 n               do not run git-update-server-info
 q,quiet         be quiet
 l               pass --local to git-pack-objects
+unpack-unreachable=  with -A, do not loosen objects older than this
  Packing constraints
 window=         size of the window used for delta compression
 window-memory=  same as the above, but limit memory size instead of entries count
@@ -33,6 +34,8 @@ do
        -a)     all_into_one=t ;;
        -A)     all_into_one=t
                unpack_unreachable=--unpack-unreachable ;;
+       --unpack-unreachable)
+               unpack_unreachable="--unpack-unreachable=$2"; shift ;;
        -d)     remove_redundant=t ;;
        -q)     GIT_QUIET=t ;;
        -f)     no_reuse=--no-reuse-delta ;;
@@ -76,7 +79,12 @@ case ",$all_into_one," in
                if test -n "$existing" -a -n "$unpack_unreachable" -a \
                        -n "$remove_redundant"
                then
-                       args="$args $unpack_unreachable"
+                       # This may have arbitrary user arguments, so we
+                       # have to protect it against whitespace splitting
+                       # when it gets run as "pack-objects $args" later.
+                       # Fortunately, we know it's an approxidate, so we
+                       # can just use dots instead.
+                       args="$args $(echo "$unpack_unreachable" | tr ' ' .)"
                fi
        fi
        ;;
index 5d8e4e6c89f0471567a7aa1f07b054d8ac502e15..7b3ae75d7a631fb07404ab85fa6d43ada74d8512 100644 (file)
@@ -248,6 +248,10 @@ case $(uname -s) in
        find () {
                /usr/bin/find "$@"
        }
+       # git sees Windows-style pwd
+       pwd () {
+               builtin pwd -W
+       }
        is_absolute_path () {
                case "$1" in
                [/\\]* | [A-Za-z]:*)
index fe4ab28b2e10f85b20ff7892663cbb141fad4723..4e2c7f83314954de6655b0d1bff773c31dfe3ed1 100755 (executable)
@@ -199,8 +199,8 @@ save_stash () {
                        #    $ 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'
-                       eval_gettextln "$("error: unknown option for 'stash save': \$option
-       To provide a message, use git stash save -- '\$option'")"
+                       eval_gettextln "error: unknown option for 'stash save': \$option
+       To provide a message, use git stash save -- '\$option'"
                        usage
                        ;;
                *)
index efc86ad4e0b90edbbf5a927aa87e7ad4b1a1949e..64a70d621aa5b81547ff63740b6876009677aae5 100755 (executable)
@@ -101,11 +101,12 @@ module_list()
 module_name()
 {
        # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
+       sm_path="$1"
        re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
        name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
        test -z "$name" &&
-       die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$path'")"
+       die "$(eval_gettext "No submodule mapping found in .gitmodules for path '\$sm_path'")"
        echo "$name"
 }
 
@@ -119,7 +120,7 @@ module_name()
 #
 module_clone()
 {
-       path=$1
+       sm_path=$1
        url=$2
        reference="$3"
        quiet=
@@ -130,8 +131,8 @@ module_clone()
 
        gitdir=
        gitdir_base=
-       name=$(module_name "$path" 2>/dev/null)
-       test -n "$name" || name="$path"
+       name=$(module_name "$sm_path" 2>/dev/null)
+       test -n "$name" || name="$sm_path"
        base_name=$(dirname "$name")
 
        gitdir=$(git rev-parse --git-dir)
@@ -140,17 +141,17 @@ module_clone()
 
        if test -d "$gitdir"
        then
-               mkdir -p "$path"
+               mkdir -p "$sm_path"
                rm -f "$gitdir/index"
        else
                mkdir -p "$gitdir_base"
                git clone $quiet -n ${reference:+"$reference"} \
-                       --separate-git-dir "$gitdir" "$url" "$path" ||
-               die "$(eval_gettext "Clone of '\$url' into submodule path '\$path' failed")"
+                       --separate-git-dir "$gitdir" "$url" "$sm_path" ||
+               die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
        fi
 
        a=$(cd "$gitdir" && pwd)/
-       b=$(cd "$path" && pwd)/
+       b=$(cd "$sm_path" && pwd)/
        # normalize Windows-style absolute paths to POSIX-style absolute paths
        case $a in [a-zA-Z]:/*) a=/${a%%:*}${a#*:} ;; esac
        case $b in [a-zA-Z]:/*) b=/${b%%:*}${b#*:} ;; esac
@@ -167,11 +168,12 @@ module_clone()
        a=${a%/}
        b=${b%/}
 
-       rel=$(echo $b | sed -e 's|[^/]*|..|g')
-       echo "gitdir: $rel/$a" >"$path/.git"
+       # Turn each leading "*/" component into "../"
+       rel=$(echo $b | sed -e 's|[^/][^/]*|..|g')
+       echo "gitdir: $rel/$a" >"$sm_path/.git"
 
-       rel=$(echo $a | sed -e 's|[^/]*|..|g')
-       (clear_local_git_env; cd "$path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
+       rel=$(echo $a | sed -e 's|[^/][^/]*|..|g')
+       (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
 }
 
 #
@@ -222,14 +224,14 @@ cmd_add()
        done
 
        repo=$1
-       path=$2
+       sm_path=$2
 
-       if test -z "$path"; then
-               path=$(echo "$repo" |
+       if test -z "$sm_path"; then
+               sm_path=$(echo "$repo" |
                        sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
        fi
 
-       if test -z "$repo" -o -z "$path"; then
+       if test -z "$repo" -o -z "$sm_path"; then
                usage
        fi
 
@@ -250,7 +252,7 @@ cmd_add()
 
        # normalize path:
        # multiple //; leading ./; /./; /../; trailing /
-       path=$(printf '%s/\n' "$path" |
+       sm_path=$(printf '%s/\n' "$sm_path" |
                sed -e '
                        s|//*|/|g
                        s|^\(\./\)*||
@@ -260,49 +262,49 @@ cmd_add()
                        tstart
                        s|/*$||
                ')
-       git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
-       die "$(eval_gettext "'\$path' already exists in the index")"
+       git ls-files --error-unmatch "$sm_path" > /dev/null 2>&1 &&
+       die "$(eval_gettext "'\$sm_path' already exists in the index")"
 
-       if test -z "$force" && ! git add --dry-run --ignore-missing "$path" > /dev/null 2>&1
+       if test -z "$force" && ! git add --dry-run --ignore-missing "$sm_path" > /dev/null 2>&1
        then
                eval_gettextln "The following path is ignored by one of your .gitignore files:
-\$path
+\$sm_path
 Use -f if you really want to add it." >&2
                exit 1
        fi
 
        # perhaps the path exists and is already a git repo, else clone it
-       if test -e "$path"
+       if test -e "$sm_path"
        then
-               if test -d "$path"/.git -o -f "$path"/.git
+               if test -d "$sm_path"/.git -o -f "$sm_path"/.git
                then
-                       eval_gettextln "Adding existing repo at '\$path' to the index"
+                       eval_gettextln "Adding existing repo at '\$sm_path' to the index"
                else
-                       die "$(eval_gettext "'\$path' already exists and is not a valid git repo")"
+                       die "$(eval_gettext "'\$sm_path' already exists and is not a valid git repo")"
                fi
 
        else
 
-               module_clone "$path" "$realrepo" "$reference" || exit
+               module_clone "$sm_path" "$realrepo" "$reference" || exit
                (
                        clear_local_git_env
-                       cd "$path" &&
+                       cd "$sm_path" &&
                        # ash fails to wordsplit ${branch:+-b "$branch"...}
                        case "$branch" in
                        '') git checkout -f -q ;;
                        ?*) git checkout -f -q -B "$branch" "origin/$branch" ;;
                        esac
-               ) || die "$(eval_gettext "Unable to checkout submodule '\$path'")"
+               ) || die "$(eval_gettext "Unable to checkout submodule '\$sm_path'")"
        fi
-       git config submodule."$path".url "$realrepo"
+       git config submodule."$sm_path".url "$realrepo"
 
-       git add $force "$path" ||
-       die "$(eval_gettext "Failed to add submodule '\$path'")"
+       git add $force "$sm_path" ||
+       die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
 
-       git config -f .gitmodules submodule."$path".path "$path" &&
-       git config -f .gitmodules submodule."$path".url "$repo" &&
+       git config -f .gitmodules submodule."$sm_path".path "$sm_path" &&
+       git config -f .gitmodules submodule."$sm_path".url "$repo" &&
        git add --force .gitmodules ||
-       die "$(eval_gettext "Failed to register submodule '\$path'")"
+       die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
 }
 
 #
@@ -340,23 +342,25 @@ cmd_foreach()
        exec 3<&0
 
        module_list |
-       while read mode sha1 stage path
+       while read mode sha1 stage sm_path
        do
-               if test -e "$path"/.git
+               if test -e "$sm_path"/.git
                then
-                       say "$(eval_gettext "Entering '\$prefix\$path'")"
-                       name=$(module_name "$path")
+                       say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
+                       name=$(module_name "$sm_path")
                        (
-                               prefix="$prefix$path/"
+                               prefix="$prefix$sm_path/"
                                clear_local_git_env
-                               cd "$path" &&
+                               # we make $path available to scripts ...
+                               path=$sm_path
+                               cd "$sm_path" &&
                                eval "$@" &&
                                if test -n "$recursive"
                                then
                                        cmd_foreach "--recursive" "$@"
                                fi
                        ) <&3 3<&- ||
-                       die "$(eval_gettext "Stopping at '\$path'; script returned non-zero status.")"
+                       die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")"
                fi
        done
 }
@@ -390,15 +394,15 @@ cmd_init()
        done
 
        module_list "$@" |
-       while read mode sha1 stage path
+       while read mode sha1 stage sm_path
        do
                # Skip already registered paths
-               name=$(module_name "$path") || exit
+               name=$(module_name "$sm_path") || exit
                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 '\$path' in .gitmodules")"
+                       die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
 
                        # Possibly a url relative to parent
                        case "$url" in
@@ -407,7 +411,7 @@ cmd_init()
                                ;;
                        esac
                        git config submodule."$name".url "$url" ||
-                       die "$(eval_gettext "Failed to register url for submodule path '\$path'")"
+                       die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
                fi
 
                # Copy "update" setting when it is not set yet
@@ -415,9 +419,9 @@ 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 '\$path'")"
+               die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")"
 
-               say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$path'")"
+               say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")"
        done
 }
 
@@ -489,14 +493,14 @@ cmd_update()
        cloned_modules=
        module_list "$@" | {
        err=
-       while read mode sha1 stage path
+       while read mode sha1 stage sm_path
        do
                if test "$stage" = U
                then
-                       echo >&2 "Skipping unmerged submodule $path"
+                       echo >&2 "Skipping unmerged submodule $sm_path"
                        continue
                fi
-               name=$(module_name "$path") || exit
+               name=$(module_name "$sm_path") || exit
                url=$(git config submodule."$name".url)
                if ! test -z "$update"
                then
@@ -507,7 +511,7 @@ cmd_update()
 
                if test "$update_module" = "none"
                then
-                       echo "Skipping submodule '$path'"
+                       echo "Skipping submodule '$sm_path'"
                        continue
                fi
 
@@ -516,20 +520,20 @@ cmd_update()
                        # Only mention uninitialized submodules when its
                        # path have been specified
                        test "$#" != "0" &&
-                       say "$(eval_gettext "Submodule path '\$path' not initialized
+                       say "$(eval_gettext "Submodule path '\$sm_path' not initialized
 Maybe you want to use 'update --init'?")"
                        continue
                fi
 
-               if ! test -d "$path"/.git -o -f "$path"/.git
+               if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
                then
-                       module_clone "$path" "$url" "$reference"|| exit
+                       module_clone "$sm_path" "$url" "$reference"|| exit
                        cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
-                       subsha1=$(clear_local_git_env; cd "$path" &&
+                       subsha1=$(clear_local_git_env; cd "$sm_path" &&
                                git rev-parse --verify HEAD) ||
-                       die "$(eval_gettext "Unable to find current revision in submodule path '\$path'")"
+                       die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")"
                fi
 
                if test "$subsha1" != "$sha1"
@@ -545,10 +549,10 @@ Maybe you want to use 'update --init'?")"
                        then
                                # Run fetch only if $sha1 isn't present or it
                                # is not reachable from a ref.
-                               (clear_local_git_env; cd "$path" &&
+                               (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 '\$path'")"
+                               die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
                        fi
 
                        # Is this something we just cloned?
@@ -562,24 +566,24 @@ 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 '\$path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$path': rebased into '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$sm_path'")"
+                               say_msg="$(eval_gettext "Submodule path '\$sm_path': rebased into '\$sha1'")"
                                must_die_on_failure=yes
                                ;;
                        merge)
                                command="git merge"
-                               die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$path': merged in '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$sm_path'")"
+                               say_msg="$(eval_gettext "Submodule path '\$sm_path': merged in '\$sha1'")"
                                must_die_on_failure=yes
                                ;;
                        *)
                                command="git checkout $subforce -q"
-                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$path'")"
-                               say_msg="$(eval_gettext "Submodule path '\$path': checked out '\$sha1'")"
+                               die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$sm_path'")"
+                               say_msg="$(eval_gettext "Submodule path '\$sm_path': checked out '\$sha1'")"
                                ;;
                        esac
 
-                       if (clear_local_git_env; cd "$path" && $command "$sha1")
+                       if (clear_local_git_env; cd "$sm_path" && $command "$sha1")
                        then
                                say "$say_msg"
                        elif test -n "$must_die_on_failure"
@@ -593,11 +597,11 @@ Maybe you want to use 'update --init'?")"
 
                if test -n "$recursive"
                then
-                       (clear_local_git_env; cd "$path" && eval cmd_update "$orig_flags")
+                       (clear_local_git_env; cd "$sm_path" && eval cmd_update "$orig_flags")
                        res=$?
                        if test $res -gt 0
                        then
-                               die_msg="$(eval_gettext "Failed to recurse into submodule path '\$path'")"
+                               die_msg="$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
                                if test $res -eq 1
                                then
                                        err="${err};$die_msg"
@@ -884,30 +888,30 @@ cmd_status()
        done
 
        module_list "$@" |
-       while read mode sha1 stage path
+       while read mode sha1 stage sm_path
        do
-               name=$(module_name "$path") || exit
+               name=$(module_name "$sm_path") || exit
                url=$(git config submodule."$name".url)
-               displaypath="$prefix$path"
+               displaypath="$prefix$sm_path"
                if test "$stage" = U
                then
                        say "U$sha1 $displaypath"
                        continue
                fi
-               if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
+               if test -z "$url" || ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
                then
                        say "-$sha1 $displaypath"
                        continue;
                fi
-               set_name_rev "$path" "$sha1"
-               if git diff-files --ignore-submodules=dirty --quiet -- "$path"
+               set_name_rev "$sm_path" "$sha1"
+               if git diff-files --ignore-submodules=dirty --quiet -- "$sm_path"
                then
                        say " $sha1 $displaypath$revname"
                else
                        if test -z "$cached"
                        then
-                               sha1=$(clear_local_git_env; cd "$path" && git rev-parse --verify HEAD)
-                               set_name_rev "$path" "$sha1"
+                               sha1=$(clear_local_git_env; cd "$sm_path" && git rev-parse --verify HEAD)
+                               set_name_rev "$sm_path" "$sha1"
                        fi
                        say "+$sha1 $displaypath$revname"
                fi
@@ -917,10 +921,10 @@ cmd_status()
                        (
                                prefix="$displaypath/"
                                clear_local_git_env
-                               cd "$path" &&
+                               cd "$sm_path" &&
                                eval cmd_status "$orig_args"
                        ) ||
-                       die "$(eval_gettext "Failed to recurse into submodule path '\$path'")"
+                       die "$(eval_gettext "Failed to recurse into submodule path '\$sm_path'")"
                fi
        done
 }
@@ -952,9 +956,9 @@ cmd_sync()
        done
        cd_to_toplevel
        module_list "$@" |
-       while read mode sha1 stage path
+       while read mode sha1 stage sm_path
        do
-               name=$(module_name "$path")
+               name=$(module_name "$sm_path")
                url=$(git config -f .gitmodules --get submodule."$name".url)
 
                # Possibly a url relative to parent
@@ -969,11 +973,11 @@ cmd_sync()
                        say "$(eval_gettext "Synchronizing submodule url for '\$name'")"
                        git config submodule."$name".url "$url"
 
-                       if test -e "$path"/.git
+                       if test -e "$sm_path"/.git
                        then
                        (
                                clear_local_git_env
-                               cd "$path"
+                               cd "$sm_path"
                                remote=$(get_default_remote)
                                git config remote."$remote".url "$url"
                        )
index 4334b95f70fab5c3cb9e446a9d6c418748b95bca..c84842ff0383c6929d8169834944ec1ad2bb7346 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env perl
+#!/usr/bin/perl
 # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
 # License: GPL v2 or later
 use 5.008;
 $| = 1; # unbuffer STDOUT
 
 sub fatal (@) { print STDERR "@_\n"; exit 1 }
+
+# All SVN commands do it.  Otherwise we may die on SIGPIPE when the remote
+# repository decides to close the connection which we expect to be kept alive.
+$SIG{PIPE} = 'IGNORE';
+
+# Given a dot separated version number, "subtract" it from
+# the SVN::Core::VERSION; non-negaitive return means the SVN::Core
+# is at least at the version the caller asked for.
+sub compare_svn_version {
+       my (@ours) = split(/\./, $SVN::Core::VERSION);
+       my (@theirs) = split(/\./, $_[0]);
+       my ($i, $diff);
+
+       for ($i = 0; $i < @ours && $i < @theirs; $i++) {
+               $diff = $ours[$i] - $theirs[$i];
+               return $diff if ($diff);
+       }
+       return 1 if ($i < @ours);
+       return -1 if ($i < @theirs);
+       return 0;
+}
+
 sub _req_svn {
        require SVN::Core; # use()-ing this causes segfaults for me... *shrug*
        require SVN::Ra;
        require SVN::Delta;
-       if ($SVN::Core::VERSION lt '1.1.0') {
+       if (::compare_svn_version('1.1.0') < 0) {
                fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)";
        }
 }
@@ -84,7 +106,7 @@ BEGIN
        $_message, $_file, $_branch_dest,
        $_template, $_shared,
        $_version, $_fetch_all, $_no_rebase, $_fetch_parent,
-       $_merge, $_strategy, $_dry_run, $_local,
+       $_merge, $_strategy, $_preserve_merges, $_dry_run, $_local,
        $_prefix, $_no_checkout, $_url, $_verbose,
        $_git_format, $_commit_url, $_tag, $_merge_info, $_interactive);
 $Git::SVN::_follow_parent = 1;
@@ -233,6 +255,7 @@ BEGIN
                          'local|l' => \$_local,
                          'fetch-all|all' => \$_fetch_all,
                          'dry-run|n' => \$_dry_run,
+                         'preserve-merges|p' => \$_preserve_merges,
                          %fc_opts } ],
        'commit-diff' => [ \&cmd_commit_diff,
                           'Commit a diff between two trees',
@@ -1469,7 +1492,7 @@ sub cmd_info {
        }
        ::_req_svn();
        $result .= "Repository UUID: $uuid\n" unless $diff_status eq "A" &&
-               ($SVN::Core::VERSION le '1.5.4' || $file_type ne "dir");
+               (::compare_svn_version('1.5.4') <= 0 || $file_type ne "dir");
        $result .= "Revision: " . ($diff_status eq "A" ? 0 : $rev) . "\n";
 
        $result .= "Node Kind: " .
@@ -1570,6 +1593,7 @@ sub rebase_cmd {
        push @cmd, '-v' if $_verbose;
        push @cmd, qw/--merge/ if $_merge;
        push @cmd, "--strategy=$_strategy" if $_strategy;
+       push @cmd, "--preserve-merges" if $_preserve_merges;
        @cmd;
 }
 
@@ -2031,6 +2055,7 @@ package Git::SVN;
 use Time::Local;
 use Memoize;  # core since 5.8.0, Jul 2002
 use Memoize::Storable;
+use POSIX qw(:signal_h);
 
 my ($_gc_nr, $_gc_period);
 
@@ -4059,11 +4084,14 @@ sub rev_map_set {
        length $commit == 40 or die "arg3 must be a full SHA1 hexsum\n";
        my $db = $self->map_path($uuid);
        my $db_lock = "$db.lock";
-       my $sig;
+       my $sigmask;
        $update_ref ||= 0;
        if ($update_ref) {
-               $SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
-                           $SIG{USR1} = $SIG{USR2} = sub { $sig = $_[0] };
+               $sigmask = POSIX::SigSet->new();
+               my $signew = POSIX::SigSet->new(SIGINT, SIGHUP, SIGTERM,
+                       SIGALRM, SIGUSR1, SIGUSR2);
+               sigprocmask(SIG_BLOCK, $signew, $sigmask) or
+                       croak "Can't block signals: $!";
        }
        mkfile($db);
 
@@ -4102,9 +4130,8 @@ sub rev_map_set {
                                    "$db_lock => $db ($!)\n";
        delete $LOCKFILES{$db_lock};
        if ($update_ref) {
-               $SIG{INT} = $SIG{HUP} = $SIG{TERM} = $SIG{ALRM} = $SIG{PIPE} =
-                           $SIG{USR1} = $SIG{USR2} = 'DEFAULT';
-               kill $sig, $$ if defined $sig;
+               sigprocmask(SIG_SETMASK, $sigmask) or
+                       croak "Can't restore signal mask: $!";
        }
 }
 
@@ -5436,7 +5463,7 @@ BEGIN
 }
 
 sub _auth_providers () {
-       [
+       my @rv = (
          SVN::Client::get_simple_provider(),
          SVN::Client::get_ssl_server_trust_file_provider(),
          SVN::Client::get_simple_prompt_provider(
@@ -5452,7 +5479,23 @@ ()
            \&Git::SVN::Prompt::ssl_server_trust),
          SVN::Client::get_username_prompt_provider(
            \&Git::SVN::Prompt::username, 2)
-       ]
+       );
+
+       # earlier 1.6.x versions would segfault, and <= 1.5.x didn't have
+       # this function
+       if (::compare_svn_version('1.6.12') > 0) {
+               my $config = SVN::Core::config_get_config($config_dir);
+               my ($p, @a);
+               # config_get_config returns all config files from
+               # ~/.subversion, auth_get_platform_specific_client_providers
+               # just wants the config "file".
+               @a = ($config->{'config'}, undef);
+               $p = SVN::Core::auth_get_platform_specific_client_providers(@a);
+               # Insert the return value from
+               # auth_get_platform_specific_providers
+               unshift @rv, @$p;
+       }
+       \@rv;
 }
 
 sub escape_uri_only {
@@ -5599,7 +5642,7 @@ sub get_log {
        # drop it.  Therefore, the receiver callback passed to it
        # is made aware of this limitation by being wrapped if
        # the limit passed to is being wrapped.
-       if ($SVN::Core::VERSION le '1.2.0') {
+       if (::compare_svn_version('1.2.0') <= 0) {
                my $limit = splice(@args, 3, 1);
                if ($limit > 0) {
                        my $receiver = pop @args;
@@ -5631,7 +5674,8 @@ sub trees_match {
 
 sub get_commit_editor {
        my ($self, $log, $cb, $pool) = @_;
-       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
+
+       my @lock = (::compare_svn_version('1.2.0') >= 0) ? (undef, 0) : ();
        $self->SUPER::get_commit_editor($log, $cb, @lock, $pool);
 }
 
@@ -5649,7 +5693,7 @@ sub gs_do_update {
        my (@pc) = split m#/#, $path;
        my $reporter = $self->do_update($rev_b, (@pc ? shift @pc : ''),
                                        1, $editor, $pool);
-       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+       my @lock = (::compare_svn_version('1.2.0') >= 0) ? (undef) : ();
 
        # Since we can't rely on svn_ra_reparent being available, we'll
        # just have to do some magic with set_path to make it so
@@ -5699,7 +5743,7 @@ sub gs_do_switch {
        $ra ||= $self;
        $url_b = escape_url($url_b);
        my $reporter = $ra->do_switch($rev_b, '', 1, $url_b, $editor, $pool);
-       my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+       my @lock = (::compare_svn_version('1.2.0') >= 0) ? (undef) : ();
        $reporter->set_path('', $rev_a, 0, @lock, $pool);
        $reporter->finish_report($pool);
 
diff --git a/git.c b/git.c
index 380561663061f011189de883a865fbd59a922190..d232de92e496bd5750015edce22d3b67f236b605 100644 (file)
--- a/git.c
+++ b/git.c
@@ -13,7 +13,7 @@ const char git_usage_string[] =
        "           <command> [<args>]";
 
 const char git_more_info_string[] =
-       "See 'git help <command>' for more information on a specific command.";
+       N_("See 'git help <command>' for more information on a specific command.");
 
 static struct startup_info git_startup_info;
 static int use_pager = -1;
@@ -348,6 +348,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
                { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
                { "clone", cmd_clone },
+               { "column", cmd_column, RUN_SETUP_GENTLY },
                { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "config", cmd_config, RUN_SETUP_GENTLY },
index a8b5fad26631207fcc17a6339ba735877c0902bd..55e0e9ea38b3080e32467b6faf56f40d45386b96 100755 (executable)
@@ -133,6 +133,12 @@ sub evaluate_uri {
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_EXPORT_OK++";
 
+# don't generate age column on the projects list page
+our $omit_age_column = 0;
+
+# don't generate information about owners of repositories
+our $omit_owner=0;
+
 # show repository only if this subroutine returns true
 # when given the path to the project, for example:
 #    sub { return -e "$_[0]/git-daemon-export-ok"; }
@@ -1732,20 +1738,29 @@ sub chop_and_escape_str {
 # '<span class="mark">foo</span>bar'
 sub esc_html_hl_regions {
        my ($str, $css_class, @sel) = @_;
-       return esc_html($str) unless @sel;
+       my %opts = grep { ref($_) ne 'ARRAY' } @sel;
+       @sel     = grep { ref($_) eq 'ARRAY' } @sel;
+       return esc_html($str, %opts) unless @sel;
 
        my $out = '';
        my $pos = 0;
 
        for my $s (@sel) {
-               $out .= esc_html(substr($str, $pos, $s->[0] - $pos))
-                       if ($s->[0] - $pos > 0);
-               $out .= $cgi->span({-class => $css_class},
-                                  esc_html(substr($str, $s->[0], $s->[1] - $s->[0])));
+               my ($begin, $end) = @$s;
+
+               # Don't create empty <span> elements.
+               next if $end <= $begin;
+
+               my $escaped = esc_html(substr($str, $begin, $end - $begin),
+                                      %opts);
 
-               $pos = $s->[1];
+               $out .= esc_html(substr($str, $pos, $begin - $pos), %opts)
+                       if ($begin - $pos > 0);
+               $out .= $cgi->span({-class => $css_class}, $escaped);
+
+               $pos = $end;
        }
-       $out .= esc_html(substr($str, $pos))
+       $out .= esc_html(substr($str, $pos), %opts)
                if ($pos < length($str));
 
        return $out;
@@ -2421,26 +2436,32 @@ sub format_cc_diff_chunk_header {
 }
 
 # process patch (diff) line (not to be used for diff headers),
-# returning class and HTML-formatted (but not wrapped) line
-sub process_diff_line {
-       my $line = shift;
-       my ($from, $to) = @_;
-
-       my $diff_class = diff_line_class($line, $from, $to);
-
-       chomp $line;
-       $line = untabify($line);
+# returning HTML-formatted (but not wrapped) line.
+# If the line is passed as a reference, it is treated as HTML and not
+# esc_html()'ed.
+sub format_diff_line {
+       my ($line, $diff_class, $from, $to) = @_;
+
+       if (ref($line)) {
+               $line = $$line;
+       } else {
+               chomp $line;
+               $line = untabify($line);
 
-       if ($from && $to && $line =~ m/^\@{2} /) {
-               $line = format_unidiff_chunk_header($line, $from, $to);
-               return $diff_class, $line;
+               if ($from && $to && $line =~ m/^\@{2} /) {
+                       $line = format_unidiff_chunk_header($line, $from, $to);
+               } elsif ($from && $to && $line =~ m/^\@{3}/) {
+                       $line = format_cc_diff_chunk_header($line, $from, $to);
+               } else {
+                       $line = esc_html($line, -nbsp=>1);
+               }
+       }
 
-       } elsif ($from && $to && $line =~ m/^\@{3}/) {
-               $line = format_cc_diff_chunk_header($line, $from, $to);
-               return $diff_class, $line;
+       my $diff_classes = "diff";
+       $diff_classes .= " $diff_class" if ($diff_class);
+       $line = "<div class=\"$diff_classes\">$line</div>\n";
 
-       }
-       return $diff_class, esc_html($line, -nbsp=>1);
+       return $line;
 }
 
 # Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
@@ -2997,9 +3018,11 @@ sub git_get_projects_list {
                        }
                        if (check_export_ok("$projectroot/$path")) {
                                my $pr = {
-                                       path => $path,
-                                       owner => to_utf8($owner),
+                                       path => $path
                                };
+                               if ($owner) {
+                                       $pr->{'owner'} = to_utf8($owner);
+                               }
                                push @list, $pr;
                        }
                }
@@ -3886,6 +3909,7 @@ sub print_feed_meta {
                                '-type' => "application/$type+xml"
                        );
 
+                       $href_params{'extra_options'} = undef;
                        $href_params{'action'} = $type;
                        $link_attr{'-href'} = href(%href_params);
                        print "<link ".
@@ -4993,10 +5017,186 @@ sub git_difftree_body {
        print "</table>\n";
 }
 
-sub print_sidebyside_diff_chunk {
-       my @chunk = @_;
+# Print context lines and then rem/add lines in a side-by-side manner.
+sub print_sidebyside_diff_lines {
+       my ($ctx, $rem, $add) = @_;
+
+       # print context block before add/rem block
+       if (@$ctx) {
+               print join '',
+                       '<div class="chunk_block ctx">',
+                               '<div class="old">',
+                               @$ctx,
+                               '</div>',
+                               '<div class="new">',
+                               @$ctx,
+                               '</div>',
+                       '</div>';
+       }
+
+       if (!@$add) {
+               # pure removal
+               print join '',
+                       '<div class="chunk_block rem">',
+                               '<div class="old">',
+                               @$rem,
+                               '</div>',
+                       '</div>';
+       } elsif (!@$rem) {
+               # pure addition
+               print join '',
+                       '<div class="chunk_block add">',
+                               '<div class="new">',
+                               @$add,
+                               '</div>',
+                       '</div>';
+       } else {
+               print join '',
+                       '<div class="chunk_block chg">',
+                               '<div class="old">',
+                               @$rem,
+                               '</div>',
+                               '<div class="new">',
+                               @$add,
+                               '</div>',
+                       '</div>';
+       }
+}
+
+# Print context lines and then rem/add lines in inline manner.
+sub print_inline_diff_lines {
+       my ($ctx, $rem, $add) = @_;
+
+       print @$ctx, @$rem, @$add;
+}
+
+# Format removed and added line, mark changed part and HTML-format them.
+# Implementation is based on contrib/diff-highlight
+sub format_rem_add_lines_pair {
+       my ($rem, $add, $num_parents) = @_;
+
+       # We need to untabify lines before split()'ing them;
+       # otherwise offsets would be invalid.
+       chomp $rem;
+       chomp $add;
+       $rem = untabify($rem);
+       $add = untabify($add);
+
+       my @rem = split(//, $rem);
+       my @add = split(//, $add);
+       my ($esc_rem, $esc_add);
+       # Ignore leading +/- characters for each parent.
+       my ($prefix_len, $suffix_len) = ($num_parents, 0);
+       my ($prefix_has_nonspace, $suffix_has_nonspace);
+
+       my $shorter = (@rem < @add) ? @rem : @add;
+       while ($prefix_len < $shorter) {
+               last if ($rem[$prefix_len] ne $add[$prefix_len]);
+
+               $prefix_has_nonspace = 1 if ($rem[$prefix_len] !~ /\s/);
+               $prefix_len++;
+       }
+
+       while ($prefix_len + $suffix_len < $shorter) {
+               last if ($rem[-1 - $suffix_len] ne $add[-1 - $suffix_len]);
+
+               $suffix_has_nonspace = 1 if ($rem[-1 - $suffix_len] !~ /\s/);
+               $suffix_len++;
+       }
+
+       # Mark lines that are different from each other, but have some common
+       # part that isn't whitespace.  If lines are completely different, don't
+       # mark them because that would make output unreadable, especially if
+       # diff consists of multiple lines.
+       if ($prefix_has_nonspace || $suffix_has_nonspace) {
+               $esc_rem = esc_html_hl_regions($rem, 'marked',
+                       [$prefix_len, @rem - $suffix_len], -nbsp=>1);
+               $esc_add = esc_html_hl_regions($add, 'marked',
+                       [$prefix_len, @add - $suffix_len], -nbsp=>1);
+       } else {
+               $esc_rem = esc_html($rem, -nbsp=>1);
+               $esc_add = esc_html($add, -nbsp=>1);
+       }
+
+       return format_diff_line(\$esc_rem, 'rem'),
+              format_diff_line(\$esc_add, 'add');
+}
+
+# HTML-format diff context, removed and added lines.
+sub format_ctx_rem_add_lines {
+       my ($ctx, $rem, $add, $num_parents) = @_;
+       my (@new_ctx, @new_rem, @new_add);
+       my $can_highlight = 0;
+       my $is_combined = ($num_parents > 1);
+
+       # Highlight if every removed line has a corresponding added line.
+       if (@$add > 0 && @$add == @$rem) {
+               $can_highlight = 1;
+
+               # Highlight lines in combined diff only if the chunk contains
+               # diff between the same version, e.g.
+               #
+               #    - a
+               #   -  b
+               #    + c
+               #   +  d
+               #
+               # Otherwise the highlightling would be confusing.
+               if ($is_combined) {
+                       for (my $i = 0; $i < @$add; $i++) {
+                               my $prefix_rem = substr($rem->[$i], 0, $num_parents);
+                               my $prefix_add = substr($add->[$i], 0, $num_parents);
+
+                               $prefix_rem =~ s/-/+/g;
+
+                               if ($prefix_rem ne $prefix_add) {
+                                       $can_highlight = 0;
+                                       last;
+                               }
+                       }
+               }
+       }
+
+       if ($can_highlight) {
+               for (my $i = 0; $i < @$add; $i++) {
+                       my ($line_rem, $line_add) = format_rem_add_lines_pair(
+                               $rem->[$i], $add->[$i], $num_parents);
+                       push @new_rem, $line_rem;
+                       push @new_add, $line_add;
+               }
+       } else {
+               @new_rem = map { format_diff_line($_, 'rem') } @$rem;
+               @new_add = map { format_diff_line($_, 'add') } @$add;
+       }
+
+       @new_ctx = map { format_diff_line($_, 'ctx') } @$ctx;
+
+       return (\@new_ctx, \@new_rem, \@new_add);
+}
+
+# Print context lines and then rem/add lines.
+sub print_diff_lines {
+       my ($ctx, $rem, $add, $diff_style, $num_parents) = @_;
+       my $is_combined = $num_parents > 1;
+
+       ($ctx, $rem, $add) = format_ctx_rem_add_lines($ctx, $rem, $add,
+               $num_parents);
+
+       if ($diff_style eq 'sidebyside' && !$is_combined) {
+               print_sidebyside_diff_lines($ctx, $rem, $add);
+       } else {
+               # default 'inline' style and unknown styles
+               print_inline_diff_lines($ctx, $rem, $add);
+       }
+}
+
+sub print_diff_chunk {
+       my ($diff_style, $num_parents, $from, $to, @chunk) = @_;
        my (@ctx, @rem, @add);
 
+       # The class of the previous line.
+       my $prev_class = '';
+
        return unless @chunk;
 
        # incomplete last line might be among removed or added lines,
@@ -5015,55 +5215,19 @@ sub print_sidebyside_diff_chunk {
 
                # print chunk headers
                if ($class && $class eq 'chunk_header') {
-                       print $line;
+                       print format_diff_line($line, $class, $from, $to);
                        next;
                }
 
-               ## print from accumulator when type of class of lines change
-               # empty contents block on start rem/add block, or end of chunk
-               if (@ctx && (!$class || $class eq 'rem' || $class eq 'add')) {
-                       print join '',
-                               '<div class="chunk_block ctx">',
-                                       '<div class="old">',
-                                       @ctx,
-                                       '</div>',
-                                       '<div class="new">',
-                                       @ctx,
-                                       '</div>',
-                               '</div>';
-                       @ctx = ();
-               }
-               # empty add/rem block on start context block, or end of chunk
-               if ((@rem || @add) && (!$class || $class eq 'ctx')) {
-                       if (!@add) {
-                               # pure removal
-                               print join '',
-                                       '<div class="chunk_block rem">',
-                                               '<div class="old">',
-                                               @rem,
-                                               '</div>',
-                                       '</div>';
-                       } elsif (!@rem) {
-                               # pure addition
-                               print join '',
-                                       '<div class="chunk_block add">',
-                                               '<div class="new">',
-                                               @add,
-                                               '</div>',
-                                       '</div>';
-                       } else {
-                               # assume that it is change
-                               print join '',
-                                       '<div class="chunk_block chg">',
-                                               '<div class="old">',
-                                               @rem,
-                                               '</div>',
-                                               '<div class="new">',
-                                               @add,
-                                               '</div>',
-                                       '</div>';
-                       }
-                       @rem = @add = ();
+               ## print from accumulator when have some add/rem lines or end
+               # of chunk (flush context lines), or when have add and rem
+               # lines and new block is reached (otherwise add/rem lines could
+               # be reordered)
+               if (!$class || ((@rem || @add) && $class eq 'ctx') ||
+                   (@rem && @add && $class ne $prev_class)) {
+                       print_diff_lines(\@ctx, \@rem, \@add,
+                                        $diff_style, $num_parents);
+                       @ctx = @rem = @add = ();
                }
 
                ## adding lines to accumulator
@@ -5079,6 +5243,8 @@ sub print_sidebyside_diff_chunk {
                if ($class eq 'ctx') {
                        push @ctx, $line;
                }
+
+               $prev_class = $class;
        }
 }
 
@@ -5200,27 +5366,19 @@ sub git_patchset_body {
 
                        next PATCH if ($patch_line =~ m/^diff /);
 
-                       my ($class, $line) = process_diff_line($patch_line, \%from, \%to);
-                       my $diff_classes = "diff";
-                       $diff_classes .= " $class" if ($class);
-                       $line = "<div class=\"$diff_classes\">$line</div>\n";
+                       my $class = diff_line_class($patch_line, \%from, \%to);
 
-                       if ($diff_style eq 'sidebyside' && !$is_combined) {
-                               if ($class eq 'chunk_header') {
-                                       print_sidebyside_diff_chunk(@chunk);
-                                       @chunk = ( [ $class, $line ] );
-                               } else {
-                                       push @chunk, [ $class, $line ];
-                               }
-                       } else {
-                               # default 'inline' style and unknown styles
-                               print $line;
+                       if ($class eq 'chunk_header') {
+                               print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
+                               @chunk = ();
                        }
+
+                       push @chunk, [ $class, $patch_line ];
                }
 
        } continue {
                if (@chunk) {
-                       print_sidebyside_diff_chunk(@chunk);
+                       print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
                        @chunk = ();
                }
                print "</div>\n"; # class="patch"
@@ -5460,11 +5618,15 @@ sub git_project_list_rows {
                                        ? esc_html_match_hl_chopped($pr->{'descr_long'},
                                                                    $pr->{'descr'}, $search_regexp)
                                        : esc_html($pr->{'descr'})) .
-                     "</td>\n" .
-                     "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
-               print "<td class=\"". age_class($pr->{'age'}) . "\">" .
-                     (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n" .
-                     "<td class=\"link\">" .
+                     "</td>\n";
+               unless ($omit_owner) {
+                       print "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
+               }
+               unless ($omit_age_column) {
+                       print "<td class=\"". age_class($pr->{'age'}) . "\">" .
+                           (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n";
+               }
+               print"<td class=\"link\">" .
                      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
                      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
                      $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
@@ -5495,7 +5657,10 @@ sub git_project_list_body {
                                         'tagfilter'  => $tagfilter)
                if ($tagfilter || $search_regexp);
        # fill the rest
-       @projects = fill_project_list_info(\@projects);
+       my @all_fields = ('descr', 'descr_long', 'ctags', 'category');
+       push @all_fields, ('age', 'age_string') unless($omit_age_column);
+       push @all_fields, 'owner' unless($omit_owner);
+       @projects = fill_project_list_info(\@projects, @all_fields);
 
        $order ||= $default_projects_order;
        $from = 0 unless defined $from;
@@ -5526,8 +5691,8 @@ sub git_project_list_body {
                }
                print_sort_th('project', $order, 'Project');
                print_sort_th('descr', $order, 'Description');
-               print_sort_th('owner', $order, 'Owner');
-               print_sort_th('age', $order, 'Last Change');
+               print_sort_th('owner', $order, 'Owner') unless $omit_owner;
+               print_sort_th('age', $order, 'Last Change') unless $omit_age_column;
                print "<th></th>\n" . # for links
                      "</tr>\n";
        }
@@ -6280,8 +6445,10 @@ sub git_summary {
 
        print "<div class=\"title\">&nbsp;</div>\n";
        print "<table class=\"projects_list\">\n" .
-             "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n" .
-             "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+             "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n";
+        unless ($omit_owner) {
+               print  "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+        }
        if (defined $cd{'rfc2822'}) {
                print "<tr id=\"metadata_lchange\"><td>last change</td>" .
                      "<td>".format_timestamp_html(\%cd)."</td></tr>\n";
@@ -7003,6 +7170,28 @@ sub snapshot_name {
        return wantarray ? ($name, $name) : $name;
 }
 
+sub exit_if_unmodified_since {
+       my ($latest_epoch) = @_;
+       our $cgi;
+
+       my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
+       if (defined $if_modified) {
+               my $since;
+               if (eval { require HTTP::Date; 1; }) {
+                       $since = HTTP::Date::str2time($if_modified);
+               } elsif (eval { require Time::ParseDate; 1; }) {
+                       $since = Time::ParseDate::parsedate($if_modified, GMT => 1);
+               }
+               if (defined $since && $latest_epoch <= $since) {
+                       my %latest_date = parse_date($latest_epoch);
+                       print $cgi->header(
+                               -last_modified => $latest_date{'rfc2822'},
+                               -status => '304 Not Modified');
+                       goto DONE_GITWEB;
+               }
+       }
+}
+
 sub git_snapshot {
        my $format = $input_params{'snapshot_format'};
        if (!@snapshot_fmts) {
@@ -7029,6 +7218,10 @@ sub git_snapshot {
 
        my ($name, $prefix) = snapshot_name($project, $hash);
        my $filename = "$name$known_snapshot_formats{$format}{'suffix'}";
+
+       my %co = parse_commit($hash);
+       exit_if_unmodified_since($co{'committer_epoch'}) if %co;
+
        my $cmd = quote_command(
                git_cmd(), 'archive',
                "--format=$known_snapshot_formats{$format}{'format'}",
@@ -7038,9 +7231,15 @@ sub git_snapshot {
        }
 
        $filename =~ s/(["\\])/\\$1/g;
+       my %latest_date;
+       if (%co) {
+               %latest_date = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
+       }
+
        print $cgi->header(
                -type => $known_snapshot_formats{$format}{'type'},
                -content_disposition => 'inline; filename="' . $filename . '"',
+               %co ? (-last_modified => $latest_date{'rfc2822'}) : (),
                -status => '200 OK');
 
        open my $fd, "-|", $cmd
@@ -7820,33 +8019,14 @@ sub git_feed {
        if (defined($commitlist[0])) {
                %latest_commit = %{$commitlist[0]};
                my $latest_epoch = $latest_commit{'committer_epoch'};
-               %latest_date   = parse_date($latest_epoch, $latest_commit{'comitter_tz'});
-               my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
-               if (defined $if_modified) {
-                       my $since;
-                       if (eval { require HTTP::Date; 1; }) {
-                               $since = HTTP::Date::str2time($if_modified);
-                       } elsif (eval { require Time::ParseDate; 1; }) {
-                               $since = Time::ParseDate::parsedate($if_modified, GMT => 1);
-                       }
-                       if (defined $since && $latest_epoch <= $since) {
-                               print $cgi->header(
-                                       -type => $content_type,
-                                       -charset => 'utf-8',
-                                       -last_modified => $latest_date{'rfc2822'},
-                                       -status => '304 Not Modified');
-                               return;
-                       }
-               }
-               print $cgi->header(
-                       -type => $content_type,
-                       -charset => 'utf-8',
-                       -last_modified => $latest_date{'rfc2822'});
-       } else {
-               print $cgi->header(
-                       -type => $content_type,
-                       -charset => 'utf-8');
+               exit_if_unmodified_since($latest_epoch);
+               %latest_date = parse_date($latest_epoch, $latest_commit{'comitter_tz'});
        }
+       print $cgi->header(
+               -type => $content_type,
+               -charset => 'utf-8',
+               %latest_date ? (-last_modified => $latest_date{'rfc2822'}) : (),
+               -status => '200 OK');
 
        # Optimization: skip generating the body if client asks only
        # for Last-Modified date.
index c530355a7cefefe694f8cbdcc3a7acc5725bec3a..cb86d2d029637ec64f70cc9e1cdbeeea972e8771 100644 (file)
@@ -438,6 +438,10 @@ div.diff.add {
        color: #008800;
 }
 
+div.diff.add span.marked {
+       background-color: #aaffaa;
+}
+
 div.diff.from_file a.path,
 div.diff.from_file {
        color: #aa0000;
@@ -447,6 +451,10 @@ div.diff.rem {
        color: #cc0000;
 }
 
+div.diff.rem span.marked {
+       background-color: #ffaaaa;
+}
+
 div.diff.chunk_header a,
 div.diff.chunk_header {
        color: #990099;
diff --git a/graph.c b/graph.c
index 7358416a72e855b406e026036cf61bcdd15e5142..7e0a099f8329757b24770ec6788bf83a8398d76d 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -194,8 +194,10 @@ static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void
        struct git_graph *graph = data;
        static struct strbuf msgbuf = STRBUF_INIT;
 
+       assert(opt);
        assert(graph);
 
+       opt->output_prefix_length = graph->width;
        strbuf_reset(&msgbuf);
        graph_padding_line(graph, &msgbuf);
        return &msgbuf;
@@ -245,6 +247,7 @@ struct git_graph *graph_init(struct rev_info *opt)
         */
        opt->diffopt.output_prefix = diff_output_prefix_callback;
        opt->diffopt.output_prefix_data = graph;
+       opt->diffopt.output_prefix_length = 0;
 
        return graph;
 }
diff --git a/grep.c b/grep.c
index 190139cfcd0d61f364a88010fe1dd6e71e26f9e2..04e3ec6c6e90e8bd96495c7b40bcb014384b07d8 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -3,18 +3,64 @@
 #include "userdiff.h"
 #include "xdiff-interface.h"
 
-void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)
+static struct grep_pat *create_grep_pat(const char *pat, size_t patlen,
+                                       const char *origin, int no,
+                                       enum grep_pat_token t,
+                                       enum grep_header_field field)
 {
        struct grep_pat *p = xcalloc(1, sizeof(*p));
-       p->pattern = pat;
-       p->patternlen = strlen(pat);
-       p->origin = "header";
-       p->no = 0;
-       p->token = GREP_PATTERN_HEAD;
+       p->pattern = xmemdupz(pat, patlen);
+       p->patternlen = patlen;
+       p->origin = origin;
+       p->no = no;
+       p->token = t;
        p->field = field;
-       *opt->header_tail = p;
-       opt->header_tail = &p->next;
+       return p;
+}
+
+static void do_append_grep_pat(struct grep_pat ***tail, struct grep_pat *p)
+{
+       **tail = p;
+       *tail = &p->next;
        p->next = NULL;
+
+       switch (p->token) {
+       case GREP_PATTERN: /* atom */
+       case GREP_PATTERN_HEAD:
+       case GREP_PATTERN_BODY:
+               for (;;) {
+                       struct grep_pat *new_pat;
+                       size_t len = 0;
+                       char *cp = p->pattern + p->patternlen, *nl = NULL;
+                       while (++len <= p->patternlen) {
+                               if (*(--cp) == '\n') {
+                                       nl = cp;
+                                       break;
+                               }
+                       }
+                       if (!nl)
+                               break;
+                       new_pat = create_grep_pat(nl + 1, len - 1, p->origin,
+                                                 p->no, p->token, p->field);
+                       new_pat->next = p->next;
+                       if (!p->next)
+                               *tail = &new_pat->next;
+                       p->next = new_pat;
+                       *nl = '\0';
+                       p->patternlen -= len;
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+void append_header_grep_pattern(struct grep_opt *opt,
+                               enum grep_header_field field, const char *pat)
+{
+       struct grep_pat *p = create_grep_pat(pat, strlen(pat), "header", 0,
+                                            GREP_PATTERN_HEAD, field);
+       do_append_grep_pat(&opt->header_tail, p);
 }
 
 void append_grep_pattern(struct grep_opt *opt, const char *pat,
@@ -26,15 +72,8 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat,
 void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen,
                     const char *origin, int no, enum grep_pat_token t)
 {
-       struct grep_pat *p = xcalloc(1, sizeof(*p));
-       p->pattern = pat;
-       p->patternlen = patlen;
-       p->origin = origin;
-       p->no = no;
-       p->token = t;
-       *opt->pattern_tail = p;
-       opt->pattern_tail = &p->next;
-       p->next = NULL;
+       struct grep_pat *p = create_grep_pat(pat, patlen, origin, no, t, 0);
+       do_append_grep_pat(&opt->pattern_tail, p);
 }
 
 struct grep_opt *grep_opt_dup(const struct grep_opt *opt)
@@ -318,7 +357,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
 
        if (!opt->header_list)
                return NULL;
-       p = opt->header_list;
+
        for (p = opt->header_list; p; p = p->next) {
                if (p->token != GREP_PATTERN_HEAD)
                        die("bug: a non-header pattern in grep header list.");
@@ -430,6 +469,7 @@ void free_grep_patterns(struct grep_opt *opt)
                                free_pcre_regexp(p);
                        else
                                regfree(&p->regexp);
+                       free(p->pattern);
                        break;
                default:
                        break;
diff --git a/grep.h b/grep.h
index 36e49d8255e2952828f367a0e35d4fcb027d9772..ed7de6bec8e604a7b3dcb9f9a19053696b7c3fdf 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -38,7 +38,7 @@ struct grep_pat {
        const char *origin;
        int no;
        enum grep_pat_token token;
-       const char *pattern;
+       char *pattern;
        size_t patternlen;
        enum grep_header_field field;
        regex_t regexp;
diff --git a/help.c b/help.c
index 14eefc91ced3890975d0833ffc83971c7986858b..69d483d8d3b7e109345b1e8124df6b023ed312fb 100644 (file)
--- a/help.c
+++ b/help.c
@@ -4,6 +4,8 @@
 #include "levenshtein.h"
 #include "help.h"
 #include "common-cmds.h"
+#include "string-list.h"
+#include "column.h"
 
 void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
@@ -70,31 +72,25 @@ void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
        cmds->cnt = cj;
 }
 
-static void pretty_print_string_list(struct cmdnames *cmds, int longest)
+static void pretty_print_string_list(struct cmdnames *cmds,
+                                    unsigned int colopts)
 {
-       int cols = 1, rows;
-       int space = longest + 1; /* min 1 SP between words */
-       int max_cols = term_columns() - 1; /* don't print *on* the edge */
-       int i, j;
-
-       if (space < max_cols)
-               cols = max_cols / space;
-       rows = DIV_ROUND_UP(cmds->cnt, cols);
-
-       for (i = 0; i < rows; i++) {
-               printf("  ");
+       struct string_list list = STRING_LIST_INIT_NODUP;
+       struct column_options copts;
+       int i;
 
-               for (j = 0; j < cols; j++) {
-                       int n = j * rows + i;
-                       int size = space;
-                       if (n >= cmds->cnt)
-                               break;
-                       if (j == cols-1 || n + rows >= cmds->cnt)
-                               size = 1;
-                       printf("%-*s", size, cmds->names[n]->name);
-               }
-               putchar('\n');
-       }
+       for (i = 0; i < cmds->cnt; i++)
+               string_list_append(&list, cmds->names[i]->name);
+       /*
+        * 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);
+       string_list_clear(&list, 0);
 }
 
 static int is_executable(const char *name)
@@ -203,34 +199,21 @@ void load_command_list(const char *prefix,
        exclude_cmds(other_cmds, main_cmds);
 }
 
-void list_commands(const char *title, struct cmdnames *main_cmds,
-                  struct cmdnames *other_cmds)
+void list_commands(unsigned int colopts,
+                  struct cmdnames *main_cmds, struct cmdnames *other_cmds)
 {
-       int i, longest = 0;
-
-       for (i = 0; i < main_cmds->cnt; i++)
-               if (longest < main_cmds->names[i]->len)
-                       longest = main_cmds->names[i]->len;
-       for (i = 0; i < other_cmds->cnt; i++)
-               if (longest < other_cmds->names[i]->len)
-                       longest = other_cmds->names[i]->len;
-
        if (main_cmds->cnt) {
                const char *exec_path = git_exec_path();
-               printf("available %s in '%s'\n", title, exec_path);
-               printf("----------------");
-               mput_char('-', strlen(title) + strlen(exec_path));
+               printf_ln(_("available git commands in '%s'"), exec_path);
                putchar('\n');
-               pretty_print_string_list(main_cmds, longest);
+               pretty_print_string_list(main_cmds, colopts);
                putchar('\n');
        }
 
        if (other_cmds->cnt) {
-               printf("%s available from elsewhere on your $PATH\n", title);
-               printf("---------------------------------------");
-               mput_char('-', strlen(title));
+               printf_ln(_("git commands available from elsewhere on your $PATH"));
                putchar('\n');
-               pretty_print_string_list(other_cmds, longest);
+               pretty_print_string_list(other_cmds, colopts);
                putchar('\n');
        }
 }
@@ -341,7 +324,7 @@ const char *help_unknown_cmd(const char *cmd)
              sizeof(*main_cmds.names), levenshtein_compare);
 
        if (!main_cmds.cnt)
-               die ("Uh oh. Your system reports no Git commands at all.");
+               die(_("Uh oh. Your system reports no Git commands at all."));
 
        /* skip and count prefix matches */
        for (n = 0; n < main_cmds.cnt && !main_cmds.names[n]->len; n++)
@@ -362,23 +345,26 @@ const char *help_unknown_cmd(const char *cmd)
                const char *assumed = main_cmds.names[0]->name;
                main_cmds.names[0] = NULL;
                clean_cmdnames(&main_cmds);
-               fprintf(stderr, "WARNING: You called a Git command named '%s', "
-                       "which does not exist.\n"
-                       "Continuing under the assumption that you meant '%s'\n",
+               fprintf_ln(stderr,
+                          _("WARNING: You called a Git command named '%s', "
+                            "which does not exist.\n"
+                            "Continuing under the assumption that you meant '%s'"),
                        cmd, assumed);
                if (autocorrect > 0) {
-                       fprintf(stderr, "in %0.1f seconds automatically...\n",
+                       fprintf_ln(stderr, _("in %0.1f seconds automatically..."),
                                (float)autocorrect/10.0);
                        poll(NULL, 0, autocorrect * 100);
                }
                return assumed;
        }
 
-       fprintf(stderr, "git: '%s' is not a git command. See 'git --help'.\n", cmd);
+       fprintf_ln(stderr, _("git: '%s' is not a git command. See 'git --help'."), cmd);
 
        if (SIMILAR_ENOUGH(best_similarity)) {
-               fprintf(stderr, "\nDid you mean %s?\n",
-                       n < 2 ? "this": "one of these");
+               fprintf_ln(stderr,
+                          Q_("\nDid you mean this?",
+                             "\nDid you mean one of these?",
+                          n));
 
                for (i = 0; i < n; i++)
                        fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
diff --git a/help.h b/help.h
index b6b12d5754888438085bb4e574b034951ffec473..0ae5a124a3af9912d551caed909aac77acc59b0b 100644 (file)
--- a/help.h
+++ b/help.h
@@ -25,8 +25,6 @@ extern void add_cmdname(struct cmdnames *cmds, const char *name, int len);
 /* Here we require that excludes is a sorted list. */
 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(const char *title,
-                         struct cmdnames *main_cmds,
-                         struct cmdnames *other_cmds);
+extern void list_commands(unsigned int colopts, struct cmdnames *main_cmds, struct cmdnames *other_cmds);
 
 #endif /* HELP_H */
index 869d515383b5fc9c562ea7987b1cd485cce12032..f50e77fb2890207bd7385b59bdd561133991c910 100644 (file)
@@ -7,6 +7,7 @@
 #include "run-command.h"
 #include "string-list.h"
 #include "url.h"
+#include "argv-array.h"
 
 static const char content_type[] = "Content-Type";
 static const char content_length[] = "Content-Length";
@@ -317,8 +318,7 @@ static void run_service(const char **argv)
        const char *encoding = getenv("HTTP_CONTENT_ENCODING");
        const char *user = getenv("REMOTE_USER");
        const char *host = getenv("REMOTE_ADDR");
-       char *env[3];
-       struct strbuf buf = STRBUF_INIT;
+       struct argv_array env = ARGV_ARRAY_INIT;
        int gzipped_request = 0;
        struct child_process cld;
 
@@ -332,17 +332,15 @@ static void run_service(const char **argv)
        if (!host || !*host)
                host = "(none)";
 
-       memset(&env, 0, sizeof(env));
-       strbuf_addf(&buf, "GIT_COMMITTER_NAME=%s", user);
-       env[0] = strbuf_detach(&buf, NULL);
-
-       strbuf_addf(&buf, "GIT_COMMITTER_EMAIL=%s@http.%s", user, host);
-       env[1] = strbuf_detach(&buf, NULL);
-       env[2] = NULL;
+       if (!getenv("GIT_COMMITTER_NAME"))
+               argv_array_pushf(&env, "GIT_COMMITTER_NAME=%s", user);
+       if (!getenv("GIT_COMMITTER_EMAIL"))
+               argv_array_pushf(&env, "GIT_COMMITTER_EMAIL=%s@http.%s",
+                                user, host);
 
        memset(&cld, 0, sizeof(cld));
        cld.argv = argv;
-       cld.env = (const char *const *)env;
+       cld.env = env.argv;
        if (gzipped_request)
                cld.in = -1;
        cld.git_cmd = 1;
@@ -357,9 +355,7 @@ static void run_service(const char **argv)
 
        if (finish_command(&cld))
                exit(1);
-       free(env[0]);
-       free(env[1]);
-       strbuf_release(&buf);
+       argv_array_clear(&env);
 }
 
 static int show_text_ref(const char *name, const unsigned char *sha1,
index f22f7e43caa3e804c5c8275ba0312d58611d7da3..1df7ab5670d0ba2823da14083934925b0f05c926 100644 (file)
@@ -1108,7 +1108,7 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
                                if (repo->path)
                                        url = repo->path;
                                if (strncmp(path, url, repo->path_len))
-                                       error("Parsed path '%s' does not match url: '%s'\n",
+                                       error("Parsed path '%s' does not match url: '%s'",
                                              path, url);
                                else {
                                        path += repo->path_len;
@@ -1702,7 +1702,7 @@ static int delete_remote_branch(const char *pattern, int force)
                run_active_slot(slot);
                free(url);
                if (results.curl_result != CURLE_OK)
-                       return error("DELETE request failed (%d/%ld)\n",
+                       return error("DELETE request failed (%d/%ld)",
                                     results.curl_result, results.http_code);
        } else {
                free(url);
diff --git a/http.c b/http.c
index f3f83d70cd2e13a774d208206af20181dd39d8af..5cb87f16f25fe3d32e2594c8c6325d562eddc9ca 100644 (file)
--- a/http.c
+++ b/http.c
@@ -210,14 +210,23 @@ static int http_options(const char *var, const char *value, void *cb)
 
 static void init_curl_http_auth(CURL *result)
 {
-       if (http_auth.username) {
-               struct strbuf up = STRBUF_INIT;
-               credential_fill(&http_auth);
+       if (!http_auth.username)
+               return;
+
+       credential_fill(&http_auth);
+
+#if LIBCURL_VERSION_NUM >= 0x071301
+       curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
+       curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+#else
+       {
+               static struct strbuf up = STRBUF_INIT;
+               strbuf_reset(&up);
                strbuf_addf(&up, "%s:%s",
                            http_auth.username, http_auth.password);
-               curl_easy_setopt(result, CURLOPT_USERPWD,
-                                strbuf_detach(&up, NULL));
+               curl_easy_setopt(result, CURLOPT_USERPWD, up.buf);
        }
+#endif
 }
 
 static int has_cert_password(void)
@@ -494,6 +503,8 @@ struct active_request_slot *get_active_slot(void)
        curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
+       if (http_auth.password)
+               init_curl_http_auth(slot->curl);
 
        return slot;
 }
@@ -906,7 +917,7 @@ static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
        tmp = strbuf_detach(&buf, NULL);
 
        if (http_get_file(url, tmp, 0) != HTTP_OK) {
-               error("Unable to get pack index %s\n", url);
+               error("Unable to get pack index %s", url);
                free(tmp);
                tmp = NULL;
        }
diff --git a/ident.c b/ident.c
index f619619b82379c2d205c7d7ea101049e373ab90a..87c697c2b09692ec8a36d557aa0c73de38223492 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -220,6 +220,74 @@ static int copy(char *buf, size_t size, int offset, const char *src)
        return offset;
 }
 
+/*
+ * Reverse of fmt_ident(); given an ident line, split the fields
+ * to allow the caller to parse it.
+ * Signal a success by returning 0, but date/tz fields of the result
+ * can still be NULL if the input line only has the name/email part
+ * (e.g. reading from a reflog entry).
+ */
+int split_ident_line(struct ident_split *split, const char *line, int len)
+{
+       const char *cp;
+       size_t span;
+       int status = -1;
+
+       memset(split, 0, sizeof(*split));
+
+       split->name_begin = line;
+       for (cp = line; *cp && cp < line + len; cp++)
+               if (*cp == '<') {
+                       split->mail_begin = cp + 1;
+                       break;
+               }
+       if (!split->mail_begin)
+               return status;
+
+       for (cp = split->mail_begin - 2; line < cp; cp--)
+               if (!isspace(*cp)) {
+                       split->name_end = cp + 1;
+                       break;
+               }
+       if (!split->name_end)
+               return status;
+
+       for (cp = split->mail_begin; cp < line + len; cp++)
+               if (*cp == '>') {
+                       split->mail_end = cp;
+                       break;
+               }
+       if (!split->mail_end)
+               return status;
+
+       for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
+               ;
+       if (line + len <= cp)
+               goto person_only;
+       split->date_begin = cp;
+       span = strspn(cp, "0123456789");
+       if (!span)
+               goto person_only;
+       split->date_end = split->date_begin + span;
+       for (cp = split->date_end; cp < line + len && isspace(*cp); cp++)
+               ;
+       if (line + len <= cp || (*cp != '+' && *cp != '-'))
+               goto person_only;
+       split->tz_begin = cp;
+       span = strspn(cp + 1, "0123456789");
+       if (!span)
+               goto person_only;
+       split->tz_end = split->tz_begin + 1 + span;
+       return 0;
+
+person_only:
+       split->date_begin = NULL;
+       split->date_end = NULL;
+       split->tz_begin = NULL;
+       split->tz_end = NULL;
+       return 0;
+}
+
 static const char *env_hint =
 "\n"
 "*** Please tell me who you are.\n"
index 972ad62cd92ff2b09fb5caa21b24f471009ef19b..d42e4712972794f055aec6630ba86797d7e5343c 100644 (file)
@@ -1022,7 +1022,7 @@ static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const cha
 
        ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
        if (ret != strlen(response))
-               return error("IMAP error: sending response failed\n");
+               return error("IMAP error: sending response failed");
 
        free(response);
 
index da59738c9b787ae082ad062a91345a60628e704a..f3f7692158666ffd2ab6f65f4040462e4a7d2d00 100644 (file)
@@ -73,7 +73,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
        if (buffer_is_binary(orig->ptr, orig->size) ||
            buffer_is_binary(src1->ptr, src1->size) ||
            buffer_is_binary(src2->ptr, src2->size)) {
-               warning("Cannot merge binary files: %s (%s vs. %s)\n",
+               warning("Cannot merge binary files: %s (%s vs. %s)",
                        path, name1, name2);
                return ll_binary_merge(drv_unused, result,
                                       path,
index cea8756866e5ab86f09f3fadb0fe33e92b04b4bd..376d97317674dfe6dcffec3772c899c4fc96f82f 100644 (file)
@@ -629,10 +629,9 @@ void show_log(struct rev_info *opt)
                         * graph info here.
                         */
                        show_reflog_message(opt->reflog_info,
-                                   opt->commit_format == CMIT_FMT_ONELINE,
-                                   opt->date_mode_explicit ?
-                                       opt->date_mode :
-                                       DATE_NORMAL);
+                                           opt->commit_format == CMIT_FMT_ONELINE,
+                                           opt->date_mode,
+                                           opt->date_mode_explicit);
                        if (opt->commit_format == CMIT_FMT_ONELINE)
                                return;
                }
@@ -652,6 +651,7 @@ void show_log(struct rev_info *opt)
        if (ctx.need_8bit_cte >= 0)
                ctx.need_8bit_cte = has_non_ascii(opt->add_signoff);
        ctx.date_mode = opt->date_mode;
+       ctx.date_mode_explicit = opt->date_mode_explicit;
        ctx.abbrev = opt->diffopt.abbrev;
        ctx.after_subject = extra_headers;
        ctx.preserve_subject = opt->preserve_subject;
@@ -682,7 +682,7 @@ void show_log(struct rev_info *opt)
        if (opt->use_terminator) {
                if (!opt->missing_newline)
                        graph_show_padding(opt->graph);
-               putchar('\n');
+               putchar(opt->diffopt.line_termination);
        }
 
        strbuf_release(&msgbuf);
@@ -711,14 +711,15 @@ int log_tree_diff_flush(struct rev_info *opt)
                    opt->verbose_header &&
                    opt->commit_format != CMIT_FMT_ONELINE) {
                        int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
-                       if ((pch & opt->diffopt.output_format) == pch)
-                               printf("---");
                        if (opt->diffopt.output_prefix) {
                                struct strbuf *msg = NULL;
                                msg = opt->diffopt.output_prefix(&opt->diffopt,
                                        opt->diffopt.output_prefix_data);
                                fwrite(msg->buf, msg->len, 1, stdout);
                        }
+                       if ((pch & opt->diffopt.output_format) == pch) {
+                               printf("---");
+                       }
                        putchar('\n');
                }
        }
index 6479a60cd112c5b06b354b1a251c60bb4bce972a..680937c39e2dacb7aaa008ee77b34eb9f7c208cb 100644 (file)
@@ -485,6 +485,7 @@ static struct string_list *get_renames(struct merge_options *o,
        renames = xcalloc(1, sizeof(struct string_list));
        diff_setup(&opts);
        DIFF_OPT_SET(&opts, RECURSIVE);
+       DIFF_OPT_CLR(&opts, RENAME_EMPTY);
        opts.detect_rename = DIFF_DETECT_RENAME;
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
@@ -1914,7 +1915,7 @@ int merge_recursive(struct merge_options *o,
                /* if there is no common ancestor, use an empty tree */
                struct tree *tree;
 
-               tree = lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
+               tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
                merged_common_ancestors = make_virtual_commit(tree, "ancestor");
        }
 
@@ -2068,9 +2069,9 @@ int parse_merge_opt(struct merge_options *o, const char *s)
        else if (!prefixcmp(s, "subtree="))
                o->subtree_shift = s + strlen("subtree=");
        else if (!strcmp(s, "patience"))
-               o->xdl_opts |= XDF_PATIENCE_DIFF;
+               o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
-               o->xdl_opts |= XDF_HISTOGRAM_DIFF;
+               o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
        else if (!strcmp(s, "ignore-space-change"))
                o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
        else if (!strcmp(s, "ignore-all-space"))
diff --git a/mergesort.c b/mergesort.c
new file mode 100644 (file)
index 0000000..e5fdf2e
--- /dev/null
@@ -0,0 +1,73 @@
+#include "cache.h"
+#include "mergesort.h"
+
+struct mergesort_sublist {
+       void *ptr;
+       unsigned long len;
+};
+
+static void *get_nth_next(void *list, unsigned long n,
+                         void *(*get_next_fn)(const void *))
+{
+       while (n-- && list)
+               list = get_next_fn(list);
+       return list;
+}
+
+static void *pop_item(struct mergesort_sublist *l,
+                     void *(*get_next_fn)(const void *))
+{
+       void *p = l->ptr;
+       l->ptr = get_next_fn(l->ptr);
+       l->len = l->ptr ? (l->len - 1) : 0;
+       return p;
+}
+
+void *llist_mergesort(void *list,
+                     void *(*get_next_fn)(const void *),
+                     void (*set_next_fn)(void *, void *),
+                     int (*compare_fn)(const void *, const void *))
+{
+       unsigned long l;
+
+       if (!list)
+               return NULL;
+       for (l = 1; ; l *= 2) {
+               void *curr;
+               struct mergesort_sublist p, q;
+
+               p.ptr = list;
+               q.ptr = get_nth_next(p.ptr, l, get_next_fn);
+               if (!q.ptr)
+                       break;
+               p.len = q.len = l;
+
+               if (compare_fn(p.ptr, q.ptr) > 0)
+                       list = curr = pop_item(&q, get_next_fn);
+               else
+                       list = curr = pop_item(&p, get_next_fn);
+
+               while (p.ptr) {
+                       while (p.len || q.len) {
+                               void *prev = curr;
+
+                               if (!p.len)
+                                       curr = pop_item(&q, get_next_fn);
+                               else if (!q.len)
+                                       curr = pop_item(&p, get_next_fn);
+                               else if (compare_fn(p.ptr, q.ptr) > 0)
+                                       curr = pop_item(&q, get_next_fn);
+                               else
+                                       curr = pop_item(&p, get_next_fn);
+                               set_next_fn(prev, curr);
+                       }
+                       p.ptr = q.ptr;
+                       p.len = l;
+                       q.ptr = get_nth_next(p.ptr, l, get_next_fn);
+                       q.len = q.ptr ? l : 0;
+
+               }
+               set_next_fn(curr, NULL);
+       }
+       return list;
+}
diff --git a/mergesort.h b/mergesort.h
new file mode 100644 (file)
index 0000000..644cff1
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef MERGESORT_H
+#define MERGESORT_H
+
+/*
+ * Sort linked list in place.
+ * - get_next_fn() returns the next element given an element of a linked list.
+ * - set_next_fn() takes two elements A and B, and makes B the "next" element
+ *   of A on the list.
+ * - compare_fn() takes two elements A and B, and returns negative, 0, positive
+ *   as the same sign as "subtracting" B from A.
+ */
+void *llist_mergesort(void *list,
+                     void *(*get_next_fn)(const void *),
+                     void (*set_next_fn)(void *, void *),
+                     int (*compare_fn)(const void *, const void *));
+
+#endif
index fb0832f97d218ecd1812361721800d6288935c06..74aa77ce4be2bf23387a32e42cbdc72154c529c2 100644 (file)
@@ -267,7 +267,8 @@ static void check_notes_merge_worktree(struct notes_merge_options *o)
                 * Must establish NOTES_MERGE_WORKTREE.
                 * Abort if NOTES_MERGE_WORKTREE already exists
                 */
-               if (file_exists(git_path(NOTES_MERGE_WORKTREE))) {
+               if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
+                   !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
                        if (advice_resolve_conflict)
                                die("You have not concluded your previous "
                                    "notes merge (%s exists).\nPlease, use "
@@ -687,51 +688,60 @@ int notes_merge_commit(struct notes_merge_options *o,
 {
        /*
         * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all
-        * found notes to 'partial_tree'. Write the updates notes tree to
+        * found notes to 'partial_tree'. Write the updated notes tree to
         * the DB, and commit the resulting tree object while reusing the
         * commit message and parents from 'partial_commit'.
         * Finally store the new commit object SHA1 into 'result_sha1'.
         */
-       struct dir_struct dir;
-       char *path = xstrdup(git_path(NOTES_MERGE_WORKTREE "/"));
-       int path_len = strlen(path), i;
+       DIR *dir;
+       struct dirent *e;
+       struct strbuf path = STRBUF_INIT;
        char *msg = strstr(partial_commit->buffer, "\n\n");
        struct strbuf sb_msg = STRBUF_INIT;
+       int baselen;
 
+       strbuf_addstr(&path, git_path(NOTES_MERGE_WORKTREE));
        if (o->verbosity >= 3)
-               printf("Committing notes in notes merge worktree at %.*s\n",
-                       path_len - 1, path);
+               printf("Committing notes in notes merge worktree at %s\n",
+                       path.buf);
 
        if (!msg || msg[2] == '\0')
                die("partial notes commit has empty message");
        msg += 2;
 
-       memset(&dir, 0, sizeof(dir));
-       read_directory(&dir, path, path_len, NULL);
-       for (i = 0; i < dir.nr; i++) {
-               struct dir_entry *ent = dir.entries[i];
+       dir = opendir(path.buf);
+       if (!dir)
+               die_errno("could not open %s", path.buf);
+
+       strbuf_addch(&path, '/');
+       baselen = path.len;
+       while ((e = readdir(dir)) != NULL) {
                struct stat st;
-               const char *relpath = ent->name + path_len;
                unsigned char obj_sha1[20], blob_sha1[20];
 
-               if (ent->len - path_len != 40 || get_sha1_hex(relpath, obj_sha1)) {
+               if (is_dot_or_dotdot(e->d_name))
+                       continue;
+
+               if (strlen(e->d_name) != 40 || get_sha1_hex(e->d_name, obj_sha1)) {
                        if (o->verbosity >= 3)
-                               printf("Skipping non-SHA1 entry '%s'\n",
-                                                               ent->name);
+                               printf("Skipping non-SHA1 entry '%s%s'\n",
+                                       path.buf, e->d_name);
                        continue;
                }
 
+               strbuf_addstr(&path, e->d_name);
                /* write file as blob, and add to partial_tree */
-               if (stat(ent->name, &st))
-                       die_errno("Failed to stat '%s'", ent->name);
-               if (index_path(blob_sha1, ent->name, &st, HASH_WRITE_OBJECT))
-                       die("Failed to write blob object from '%s'", ent->name);
+               if (stat(path.buf, &st))
+                       die_errno("Failed to stat '%s'", path.buf);
+               if (index_path(blob_sha1, path.buf, &st, HASH_WRITE_OBJECT))
+                       die("Failed to write blob object from '%s'", path.buf);
                if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
                        die("Failed to add resolved note '%s' to notes tree",
-                           ent->name);
+                           path.buf);
                if (o->verbosity >= 4)
                        printf("Added resolved note for object %s: %s\n",
                                sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
+               strbuf_setlen(&path, baselen);
        }
 
        strbuf_attach(&sb_msg, msg, strlen(msg), strlen(msg) + 1);
@@ -740,20 +750,25 @@ int notes_merge_commit(struct notes_merge_options *o,
        if (o->verbosity >= 4)
                printf("Finalized notes merge commit: %s\n",
                        sha1_to_hex(result_sha1));
-       free(path);
+       strbuf_release(&path);
+       closedir(dir);
        return 0;
 }
 
 int notes_merge_abort(struct notes_merge_options *o)
 {
-       /* Remove .git/NOTES_MERGE_WORKTREE directory and all files within */
+       /*
+        * Remove all files within .git/NOTES_MERGE_WORKTREE. We do not remove
+        * the .git/NOTES_MERGE_WORKTREE directory itself, since it might be
+        * the current working directory of the user.
+        */
        struct strbuf buf = STRBUF_INIT;
        int ret;
 
        strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
        if (o->verbosity >= 3)
-               printf("Removing notes merge worktree at %s\n", buf.buf);
-       ret = remove_dir_recursively(&buf, 0);
+               printf("Removing notes merge worktree at %s/*\n", buf.buf);
+       ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
        strbuf_release(&buf);
        return ret;
 }
index 6b06297a5f06cc35cb266d6dd36c92df75a82de7..4af3451bf89471a0ab7a148aad4924a8ac8ae053 100644 (file)
--- a/object.c
+++ b/object.c
@@ -176,7 +176,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
                        obj = &tag->object;
                }
        } else {
-               warning("object %s has unknown type id %d\n", sha1_to_hex(sha1), type);
+               warning("object %s has unknown type id %d", sha1_to_hex(sha1), type);
                obj = NULL;
        }
        if (obj && obj->type == OBJ_NONE)
@@ -198,11 +198,22 @@ struct object *parse_object(const unsigned char *sha1)
        if (obj && obj->parsed)
                return obj;
 
+       if ((obj && obj->type == OBJ_BLOB) ||
+           (!obj && has_sha1_file(sha1) &&
+            sha1_object_info(sha1, NULL) == OBJ_BLOB)) {
+               if (check_sha1_signature(repl, NULL, 0, NULL) < 0) {
+                       error("sha1 mismatch %s", sha1_to_hex(repl));
+                       return NULL;
+               }
+               parse_blob_buffer(lookup_blob(sha1), NULL, 0);
+               return lookup_object(sha1);
+       }
+
        buffer = read_sha1_file(sha1, &type, &size);
        if (buffer) {
                if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) {
                        free(buffer);
-                       error("sha1 mismatch %s\n", sha1_to_hex(repl));
+                       error("sha1 mismatch %s", sha1_to_hex(repl));
                        return NULL;
                }
 
@@ -275,3 +286,14 @@ void object_array_remove_duplicates(struct object_array *array)
                array->nr = dst;
        }
 }
+
+void clear_object_flags(unsigned flags)
+{
+       int i;
+
+       for (i=0; i < obj_hash_size; i++) {
+               struct object *obj = obj_hash[i];
+               if (obj)
+                       obj->flags &= ~flags;
+       }
+}
index b6618d92bf04d549350d83b6237770c48734686f..6a97b6ba1a43e1d38eb07515ad298e0067628127 100644 (file)
--- a/object.h
+++ b/object.h
@@ -76,4 +76,6 @@ void add_object_array(struct object *obj, const char *name, struct object_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 *);
 
+void clear_object_flags(unsigned flags);
+
 #endif /* OBJECT_H */
diff --git a/pager.c b/pager.c
index 05584dead6728ceff818630fbccaa91bb6c6b686..4dcb08d9674c252cbe2c2725f92eca2a0350dc93 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -73,7 +73,7 @@ void setup_pager(void)
 {
        const char *pager = git_pager(isatty(1));
 
-       if (!pager)
+       if (!pager || pager_in_use())
                return;
 
        /*
index 850cfa78c9b7bbcd1cb8504453f60fd15b8ceebb..ab70c29c49b28eac1de3d7dd32814fa1e8544059 100644 (file)
@@ -490,7 +490,7 @@ static int usage_argh(const struct option *opts, FILE *outfile)
                        s = literal ? "[%s]" : "[<%s>]";
        else
                s = literal ? " %s" : " <%s>";
-       return fprintf(outfile, s, opts->argh ? opts->argh : "...");
+       return fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
 }
 
 #define USAGE_OPTS_WIDTH 24
@@ -508,13 +508,16 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
        if (!err && ctx && ctx->flags & PARSE_OPT_SHELL_EVAL)
                fprintf(outfile, "cat <<\\EOF\n");
 
-       fprintf(outfile, "usage: %s\n", *usagestr++);
+       fprintf_ln(outfile, _("usage: %s"), _(*usagestr++));
        while (*usagestr && **usagestr)
-               fprintf(outfile, "   or: %s\n", *usagestr++);
+               /* TRANSLATORS: the colon here should align with the
+                  one in "usage: %s" translation */
+               fprintf_ln(outfile, _("   or: %s"), _(*usagestr++));
        while (*usagestr) {
-               fprintf(outfile, "%s%s\n",
-                               **usagestr ? "    " : "",
-                               *usagestr);
+               if (**usagestr)
+                       fprintf_ln(outfile, _("    %s"), _(*usagestr));
+               else
+                       putchar('\n');
                usagestr++;
        }
 
@@ -528,7 +531,7 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                if (opts->type == OPTION_GROUP) {
                        fputc('\n', outfile);
                        if (*opts->help)
-                               fprintf(outfile, "%s\n", opts->help);
+                               fprintf(outfile, "%s\n", _(opts->help));
                        continue;
                }
                if (!full && (opts->flags & PARSE_OPT_HIDDEN))
@@ -558,7 +561,7 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                        fputc('\n', outfile);
                        pad = USAGE_OPTS_WIDTH;
                }
-               fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+               fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help));
        }
        fputc('\n', outfile);
 
index def9ced739b675cbcdac7598c3baebf5423239a0..77a4a8b2e6d2f6bc08be74b39ee6dd4964828763 100644 (file)
@@ -66,12 +66,14 @@ typedef int parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
  *
  * `argh`::
  *   token to explain the kind of argument this option wants. Keep it
- *   homogeneous across the repository.
+ *   homogeneous across the repository. Should be wrapped by N_() for
+ *   translation.
  *
  * `help`::
  *   the short help associated to what the option does.
  *   Must never be NULL (except for OPTION_END).
  *   OPTION_GROUP uses this pointer to store the group header.
+ *   Should be wrapped by N_() for translation.
  *
  * `flags`::
  *   mask of parse_opt_option_flags.
@@ -128,7 +130,7 @@ struct option {
 #define OPT_BOOL(s, l, v, h)        OPT_SET_INT(s, l, v, h, 1)
 #define OPT_SET_PTR(s, l, v, h, p)  { OPTION_SET_PTR, (s), (l), (v), NULL, \
                                      (h), PARSE_OPT_NOARG, NULL, (p) }
-#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), "n", (h) }
+#define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), N_("n"), (h) }
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
 #define OPT_STRING_LIST(s, l, v, a, h) \
                                    { OPTION_CALLBACK, (s), (l), (v), (a), \
@@ -136,7 +138,7 @@ struct option {
 #define OPT_UYN(s, l, v, h)         { OPTION_CALLBACK, (s), (l), (v), NULL, \
                                      (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
 #define OPT_DATE(s, l, v, h) \
-       { OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \
+       { OPTION_CALLBACK, (s), (l), (v), N_("time"),(h), 0,    \
          parse_opt_approxidate_cb }
 #define OPT_CALLBACK(s, l, v, a, h, f) \
        { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) }
@@ -144,21 +146,22 @@ struct option {
        { OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
          PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
 #define OPT_FILENAME(s, l, v, h)    { OPTION_FILENAME, (s), (l), (v), \
-                                      "file", (h) }
+                                      N_("file"), (h) }
 #define OPT_COLOR_FLAG(s, l, v, h) \
-       { OPTION_CALLBACK, (s), (l), (v), "when", (h), PARSE_OPT_OPTARG, \
+       { OPTION_CALLBACK, (s), (l), (v), N_("when"), (h), PARSE_OPT_OPTARG, \
                parse_opt_color_flag_cb, (intptr_t)"always" }
 
 #define OPT_NOOP_NOARG(s, l) \
        { OPTION_CALLBACK, (s), (l), NULL, NULL, \
-         "no-op (backward compatibility)", \
+         N_("no-op (backward compatibility)"),         \
          PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, parse_opt_noop_cb }
 
 /* Deprecated synonym */
 #define OPT_BOOLEAN OPT_COUNTUP
 
 /* parse_options() will filter out the processed options and leave the
- * non-option arguments in argv[].
+ * non-option arguments in argv[]. usagestr strings should be marked
+ * for translation with N_().
  * Returns the number of arguments left in argv[].
  */
 extern int parse_options(int argc, const char **argv, const char *prefix,
@@ -222,17 +225,19 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);
 #define OPT__VERBOSE(var, h)  OPT_BOOLEAN('v', "verbose", (var), (h))
 #define OPT__QUIET(var, h)    OPT_BOOLEAN('q', "quiet",   (var), (h))
 #define OPT__VERBOSITY(var) \
-       { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
+       { OPTION_CALLBACK, 'v', "verbose", (var), NULL, N_("be more verbose"), \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
-       { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
+       { OPTION_CALLBACK, 'q', "quiet", (var), NULL, N_("be more quiet"), \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
 #define OPT__DRY_RUN(var, h)  OPT_BOOLEAN('n', "dry-run", (var), (h))
 #define OPT__FORCE(var, h)    OPT_BOOLEAN('f', "force",   (var), (h))
 #define OPT__ABBREV(var)  \
-       { OPTION_CALLBACK, 0, "abbrev", (var), "n", \
-         "use <n> digits to display SHA-1s", \
+       { OPTION_CALLBACK, 0, "abbrev", (var), N_("n"), \
+         N_("use <n> digits to display SHA-1s"),       \
          PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 }
 #define OPT__COLOR(var, h) \
        OPT_COLOR_FLAG(0, "color", (var), (h))
+#define OPT_COLUMN(s, l, v, h) \
+       { OPTION_CALLBACK, (s), (l), (v), "style", (h), PARSE_OPT_OPTARG, parseopt_column_callback }
 
 #endif
index 7fcf1ec119e52a131ec873b1c3348fb0472fa1ef..cd3440ef5948de39245919fa8514e97471c1e3ad 100644 (file)
--- a/po/TEAMS
+++ b/po/TEAMS
@@ -1,6 +1,17 @@
 Core Git translation language teams
 (please keep the list sorted alphabetically on language field)
 
+Language:      da (Danish)
+Repository:    https://github.com/git-da/git-po/
+Leader:                Byrial Jensen <byrial@vip.cybercity.dk>
+
+Language:      de (German)
+Repository:    https://github.com/ralfth/git-po-de
+Leader:                Ralf Thielow <ralf.thielow@googlemail.com>
+Members:       Thomas Rast <trast@student.ethz.ch>
+               Jan Krüger <jk@jk.gs>
+               Christian Stimming <stimming@tuhh.de>
+
 Language:      is (Icelandic)
 Leader:                Ævar Arnfjörð Bjarmason <avarab@gmail.com>
 
diff --git a/po/da.po b/po/da.po
new file mode 100644 (file)
index 0000000..20a88ea
--- /dev/null
+++ b/po/da.po
@@ -0,0 +1,3503 @@
+# Danish translations for Git.
+# This file is distributed under the same license as the PACKAGE package.
+# Byrial Jensen <byrial@vip.cybercity.dk>, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Git\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-03-16 20:18+0800\n"
+"PO-Revision-Date: 2012-04-10 18:41+0200\n"
+"Last-Translator: Byrial Jensen <byrial@vip.cybercity.dk>\n"
+"Language-Team: Danish <dansk@dansk-gruppen.dk>\n"
+"Language: da\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);\n"
+
+#: advice.c:34
+#, c-format
+msgid "hint: %.*s\n"
+msgstr ""
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:64
+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 ""
+
+#: commit.c:47
+#, c-format
+msgid "could not parse %s"
+msgstr ""
+
+#: commit.c:49
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr ""
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr ""
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr ""
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr ""
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr ""
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr ""
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr ""
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+
+#: diff.c:1336
+msgid " 0 files changed\n"
+msgstr ""
+
+#: diff.c:1340
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:1357
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:1368
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ""
+msgstr[1] ""
+
+#: diff.c:3424
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr ""
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr ""
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr ""
+
+#: grep.c:1280
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr ""
+
+#: grep.c:1297
+#, c-format
+msgid "'%s': %s"
+msgstr ""
+
+#: grep.c:1308
+#, c-format
+msgid "'%s': short read %s"
+msgstr ""
+
+#: help.c:287
+#, 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 ""
+
+#: remote.c:1607
+#, 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:1613
+#, 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] ""
+msgstr[1] ""
+
+#: remote.c:1621
+#, 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] ""
+msgstr[1] ""
+
+#: sequencer.c:120 builtin/merge.c:864 builtin/merge.c:985
+#: builtin/merge.c:1095 builtin/merge.c:1105
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr ""
+
+#: sequencer.c:122 builtin/merge.c:334 builtin/merge.c:867
+#: builtin/merge.c:1097 builtin/merge.c:1110
+#, c-format
+msgid "Could not write to '%s'"
+msgstr ""
+
+#: sequencer.c:143
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+
+#: sequencer.c:146
+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:159 sequencer.c:685 sequencer.c:768
+#, c-format
+msgid "Could not write to %s"
+msgstr ""
+
+#: sequencer.c:162
+#, c-format
+msgid "Error wrapping up %s"
+msgstr ""
+
+#: sequencer.c:177
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr ""
+
+#: sequencer.c:179
+msgid "Your local changes would be overwritten by revert."
+msgstr ""
+
+#: sequencer.c:182
+msgid "Commit your changes or stash them to proceed."
+msgstr ""
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:232
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr ""
+
+#: sequencer.c:298
+msgid "Your index file is unmerged."
+msgstr ""
+
+#: sequencer.c:301
+msgid "You do not have a valid HEAD"
+msgstr ""
+
+#: sequencer.c:316
+#, c-format
+msgid "Commit %s is a merge but no -m option was given."
+msgstr ""
+
+#: sequencer.c:324
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr ""
+
+#: sequencer.c:328
+#, 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:339
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr ""
+
+#: sequencer.c:343
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr ""
+
+#: sequencer.c:427
+#, c-format
+msgid "could not revert %s... %s"
+msgstr ""
+
+#: sequencer.c:428
+#, c-format
+msgid "could not apply %s... %s"
+msgstr ""
+
+#: sequencer.c:450 sequencer.c:909 builtin/log.c:288 builtin/log.c:713
+#: builtin/log.c:1329 builtin/log.c:1548 builtin/merge.c:348
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr ""
+
+#: sequencer.c:453
+msgid "empty commit set passed"
+msgstr ""
+
+#: sequencer.c:461
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr ""
+
+#: sequencer.c:466
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr ""
+
+#: sequencer.c:551
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr ""
+
+#: sequencer.c:573
+#, c-format
+msgid "Could not parse line %d."
+msgstr ""
+
+#: sequencer.c:578
+msgid "No commits parsed."
+msgstr ""
+
+#: sequencer.c:591
+#, c-format
+msgid "Could not open %s"
+msgstr ""
+
+#: sequencer.c:595
+#, c-format
+msgid "Could not read %s."
+msgstr ""
+
+#: sequencer.c:602
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr ""
+
+#: sequencer.c:630
+#, c-format
+msgid "Invalid key: %s"
+msgstr ""
+
+#: sequencer.c:633
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr ""
+
+#: sequencer.c:645
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr ""
+
+#: sequencer.c:666
+msgid "a cherry-pick or revert is already in progress"
+msgstr ""
+
+#: sequencer.c:667
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr ""
+
+#: sequencer.c:671
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr ""
+
+#: sequencer.c:687 sequencer.c:772
+#, c-format
+msgid "Error wrapping up %s."
+msgstr ""
+
+#: sequencer.c:706 sequencer.c:840
+msgid "no cherry-pick or revert in progress"
+msgstr ""
+
+#: sequencer.c:708
+msgid "cannot resolve HEAD"
+msgstr ""
+
+#: sequencer.c:710
+msgid "cannot abort from a branch yet to be born"
+msgstr ""
+
+#: sequencer.c:732
+#, c-format
+msgid "cannot open %s: %s"
+msgstr ""
+
+#: sequencer.c:735
+#, c-format
+msgid "cannot read %s: %s"
+msgstr ""
+
+#: sequencer.c:736
+msgid "unexpected end of file"
+msgstr ""
+
+#: sequencer.c:742
+#, c-format
+msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
+msgstr ""
+
+#: sequencer.c:765
+#, c-format
+msgid "Could not format %s."
+msgstr ""
+
+#: sequencer.c:927
+msgid "Can't revert as initial commit"
+msgstr ""
+
+#: sequencer.c:928
+msgid "Can't cherry-pick into empty head"
+msgstr ""
+
+#: wt-status.c:134
+msgid "Unmerged paths:"
+msgstr ""
+
+#: wt-status.c:140 wt-status.c:157
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:142 wt-status.c:159
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+
+#: wt-status.c:143
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+
+#: wt-status.c:151
+msgid "Changes to be committed:"
+msgstr ""
+
+#: wt-status.c:169
+msgid "Changes not staged for commit:"
+msgstr ""
+
+#: wt-status.c:173
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:175
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr ""
+
+#: wt-status.c:176
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+
+#: wt-status.c:178
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+
+#: wt-status.c:187
+#, c-format
+msgid "%s files:"
+msgstr ""
+
+#: wt-status.c:190
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr ""
+
+#: wt-status.c:207
+msgid "bug"
+msgstr ""
+
+#: wt-status.c:212
+msgid "both deleted:"
+msgstr ""
+
+#: wt-status.c:213
+msgid "added by us:"
+msgstr ""
+
+#: wt-status.c:214
+msgid "deleted by them:"
+msgstr ""
+
+#: wt-status.c:215
+msgid "added by them:"
+msgstr ""
+
+#: wt-status.c:216
+msgid "deleted by us:"
+msgstr ""
+
+#: wt-status.c:217
+msgid "both added:"
+msgstr ""
+
+#: wt-status.c:218
+msgid "both modified:"
+msgstr ""
+
+#: wt-status.c:248
+msgid "new commits, "
+msgstr ""
+
+#: wt-status.c:250
+msgid "modified content, "
+msgstr ""
+
+#: wt-status.c:252
+msgid "untracked content, "
+msgstr ""
+
+#: wt-status.c:266
+#, c-format
+msgid "new file:   %s"
+msgstr ""
+
+#: wt-status.c:269
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr ""
+
+#: wt-status.c:272
+#, c-format
+msgid "deleted:    %s"
+msgstr ""
+
+#: wt-status.c:275
+#, c-format
+msgid "modified:   %s"
+msgstr ""
+
+#: wt-status.c:278
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr ""
+
+#: wt-status.c:281
+#, c-format
+msgid "typechange: %s"
+msgstr ""
+
+#: wt-status.c:284
+#, c-format
+msgid "unknown:    %s"
+msgstr ""
+
+#: wt-status.c:287
+#, c-format
+msgid "unmerged:   %s"
+msgstr ""
+
+#: wt-status.c:290
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr ""
+
+#: wt-status.c:713
+msgid "On branch "
+msgstr ""
+
+#: wt-status.c:720
+msgid "Not currently on any branch."
+msgstr ""
+
+#: wt-status.c:731
+msgid "Initial commit"
+msgstr ""
+
+#: wt-status.c:745
+msgid "Untracked"
+msgstr ""
+
+#: wt-status.c:747
+msgid "Ignored"
+msgstr ""
+
+#: wt-status.c:749
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr ""
+
+#: wt-status.c:751
+msgid " (use -u option to show untracked files)"
+msgstr ""
+
+#: wt-status.c:757
+msgid "No changes"
+msgstr ""
+
+#: wt-status.c:761
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr ""
+
+#: wt-status.c:763
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr ""
+
+#: wt-status.c:765
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+
+#: wt-status.c:767
+msgid " (use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:769 wt-status.c:772 wt-status.c:775
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr ""
+
+#: wt-status.c:770
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr ""
+
+#: wt-status.c:773
+msgid " (use -u to show untracked files)"
+msgstr ""
+
+#: wt-status.c:776
+msgid " (working directory clean)"
+msgstr ""
+
+#: wt-status.c:884
+msgid "HEAD (no branch)"
+msgstr ""
+
+#: wt-status.c:890
+msgid "Initial commit on "
+msgstr ""
+
+#: wt-status.c:905
+msgid "behind "
+msgstr ""
+
+#: wt-status.c:908 wt-status.c:911
+msgid "ahead "
+msgstr ""
+
+#: wt-status.c:913
+msgid ", behind "
+msgstr ""
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr ""
+
+#: builtin/add.c:67 builtin/commit.c:298
+msgid "updating files failed"
+msgstr ""
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr ""
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr ""
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr ""
+
+#: builtin/add.c:195 builtin/add.c:456 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr ""
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr ""
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr ""
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr ""
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr ""
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr ""
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr ""
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr ""
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr ""
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr ""
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr ""
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr ""
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr ""
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr ""
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr ""
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr ""
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:358 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr ""
+
+#: builtin/add.c:476 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr ""
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr ""
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr ""
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr ""
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr ""
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr ""
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr ""
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr ""
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr ""
+
+#: builtin/branch.c:137
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
+msgstr ""
+
+#: builtin/branch.c:141
+#, c-format
+msgid ""
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
+msgstr ""
+
+#. TRANSLATORS: This is "remote " in "remote branch '%s' not found"
+#: builtin/branch.c:163
+msgid "remote "
+msgstr ""
+
+#: builtin/branch.c:171
+msgid "cannot use -a with -d"
+msgstr ""
+
+#: builtin/branch.c:177
+msgid "Couldn't look up commit object for HEAD"
+msgstr ""
+
+#: builtin/branch.c:182
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr ""
+
+#: builtin/branch.c:192
+#, c-format
+msgid "%sbranch '%s' not found."
+msgstr ""
+
+#: builtin/branch.c:200
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr ""
+
+#: builtin/branch.c:206
+#, 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:214
+#, c-format
+msgid "Error deleting %sbranch '%s'"
+msgstr ""
+
+#: builtin/branch.c:219
+#, c-format
+msgid "Deleted %sbranch %s (was %s).\n"
+msgstr ""
+
+#: builtin/branch.c:224
+msgid "Update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:322
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr ""
+
+#: builtin/branch.c:394
+#, c-format
+msgid "behind %d] "
+msgstr ""
+
+#: builtin/branch.c:396
+#, c-format
+msgid "ahead %d] "
+msgstr ""
+
+#: builtin/branch.c:398
+#, c-format
+msgid "ahead %d, behind %d] "
+msgstr ""
+
+#: builtin/branch.c:501
+msgid "(no branch)"
+msgstr ""
+
+#: builtin/branch.c:566
+msgid "some refs could not be read"
+msgstr ""
+
+#: builtin/branch.c:579
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+
+#: builtin/branch.c:589
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr ""
+
+#: builtin/branch.c:604
+msgid "Branch rename failed"
+msgstr ""
+
+#: builtin/branch.c:608
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr ""
+
+#: builtin/branch.c:612
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr ""
+
+#: builtin/branch.c:619
+msgid "Branch is renamed, but update of config-file failed"
+msgstr ""
+
+#: builtin/branch.c:634
+#, c-format
+msgid "malformed object name %s"
+msgstr ""
+
+#: builtin/branch.c:658
+#, c-format
+msgid "could not write branch description template: %s\n"
+msgstr ""
+
+#: builtin/branch.c:746
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr ""
+
+#: builtin/branch.c:751 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr ""
+
+#: builtin/branch.c:809
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+msgstr ""
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+msgstr ""
+
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
+msgstr ""
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr ""
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr ""
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr ""
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr ""
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr ""
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr ""
+
+#: builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr ""
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr ""
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr ""
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:811
+msgid "unable to write new index file"
+msgstr ""
+
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr ""
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr ""
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:565
+msgid "HEAD is now at"
+msgstr ""
+
+#: builtin/checkout.c:572
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:575
+#, c-format
+msgid "Already on '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:579
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:581
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:583
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:639
+#, c-format
+msgid " ... and %d more.\n"
+msgstr ""
+
+#. The singular version
+#: builtin/checkout.c:645
+#, 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] ""
+msgstr[1] ""
+
+#: builtin/checkout.c:663
+#, 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 ""
+
+#: builtin/checkout.c:692
+msgid "internal error in revision walk"
+msgstr ""
+
+#: builtin/checkout.c:696
+msgid "Previous HEAD position was"
+msgstr ""
+
+#: builtin/checkout.c:722
+msgid "You are on a branch yet to be born"
+msgstr ""
+
+#. case (1)
+#: builtin/checkout.c:853
+#, c-format
+msgid "invalid reference: %s"
+msgstr ""
+
+#. case (1): want a tree
+#: builtin/checkout.c:892
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr ""
+
+#: builtin/checkout.c:972
+msgid "-B cannot be used with -b"
+msgstr ""
+
+#: builtin/checkout.c:981
+msgid "--patch is incompatible with all other options"
+msgstr ""
+
+#: builtin/checkout.c:984
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr ""
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:992
+msgid "--track needs a branch name"
+msgstr ""
+
+#: builtin/checkout.c:999
+msgid "Missing branch name; try -b"
+msgstr ""
+
+#: builtin/checkout.c:1005
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr ""
+
+#: builtin/checkout.c:1007
+msgid "--orphan cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:1017
+msgid "git checkout: -f and -m are incompatible"
+msgstr ""
+
+#: builtin/checkout.c:1051
+msgid "invalid path specification"
+msgstr ""
+
+#: builtin/checkout.c:1059
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+
+#: builtin/checkout.c:1061
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+
+#: builtin/checkout.c:1066
+msgid "git checkout: --detach does not take a path argument"
+msgstr ""
+
+#: builtin/checkout.c:1069
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+
+#: builtin/checkout.c:1088
+msgid "Cannot switch branch to a non-commit."
+msgstr ""
+
+#: builtin/checkout.c:1091
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr ""
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr ""
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr ""
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr ""
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr ""
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr ""
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr ""
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr ""
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr ""
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr ""
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr ""
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr ""
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr ""
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr ""
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr ""
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr ""
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr ""
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr ""
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr ""
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr ""
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr ""
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr ""
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr ""
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr ""
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr ""
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr ""
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr ""
+
+#: builtin/commit.c:42
+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 ""
+
+#: builtin/commit.c:54
+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:59
+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:205 builtin/reset.c:33
+msgid "merge"
+msgstr ""
+
+#: builtin/commit.c:208
+msgid "cherry-pick"
+msgstr ""
+
+#: builtin/commit.c:325
+msgid "failed to unpack HEAD tree object"
+msgstr ""
+
+#: builtin/commit.c:367
+msgid "unable to create temporary index"
+msgstr ""
+
+#: builtin/commit.c:373
+msgid "interactive add failed"
+msgstr ""
+
+#: builtin/commit.c:406 builtin/commit.c:427 builtin/commit.c:473
+msgid "unable to write new_index file"
+msgstr ""
+
+#: builtin/commit.c:457
+#, c-format
+msgid "cannot do a partial commit during a %s."
+msgstr ""
+
+#: builtin/commit.c:466
+msgid "cannot read the index"
+msgstr ""
+
+#: builtin/commit.c:486
+msgid "unable to write temporary index file"
+msgstr ""
+
+#: builtin/commit.c:550 builtin/commit.c:556
+#, c-format
+msgid "invalid commit: %s"
+msgstr ""
+
+#: builtin/commit.c:579
+msgid "malformed --author parameter"
+msgstr ""
+
+#: builtin/commit.c:635
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr ""
+
+#: builtin/commit.c:670 builtin/commit.c:703 builtin/commit.c:1000
+#, c-format
+msgid "could not lookup commit %s"
+msgstr ""
+
+#: builtin/commit.c:682 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr ""
+
+#: builtin/commit.c:684
+msgid "could not read log from standard input"
+msgstr ""
+
+#: builtin/commit.c:688
+#, c-format
+msgid "could not read log file '%s'"
+msgstr ""
+
+#: builtin/commit.c:694
+msgid "commit has empty message"
+msgstr ""
+
+#: builtin/commit.c:710
+msgid "could not read MERGE_MSG"
+msgstr ""
+
+#: builtin/commit.c:714
+msgid "could not read SQUASH_MSG"
+msgstr ""
+
+#: builtin/commit.c:718
+#, c-format
+msgid "could not read '%s'"
+msgstr ""
+
+#: builtin/commit.c:746
+#, c-format
+msgid "could not open '%s'"
+msgstr ""
+
+#: builtin/commit.c:770
+msgid "could not write commit template"
+msgstr ""
+
+#: builtin/commit.c:783
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a %s.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+
+#: builtin/commit.c:796
+msgid "Please enter the commit message for your changes."
+msgstr ""
+
+#: builtin/commit.c:799
+msgid ""
+" Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:804
+msgid ""
+" Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:816
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr ""
+
+#: builtin/commit.c:823
+#, c-format
+msgid "%sCommitter: %s"
+msgstr ""
+
+#: builtin/commit.c:843
+msgid "Cannot read index"
+msgstr ""
+
+#: builtin/commit.c:880
+msgid "Error building trees"
+msgstr ""
+
+#: builtin/commit.c:895 builtin/tag.c:357
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr ""
+
+#: builtin/commit.c:975
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr ""
+
+#: builtin/commit.c:990 builtin/commit.c:1182
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr ""
+
+#: builtin/commit.c:1030
+msgid "Using both --reset-author and --author does not make sense"
+msgstr ""
+
+#: builtin/commit.c:1041
+msgid "You have nothing to amend."
+msgstr ""
+
+#: builtin/commit.c:1043
+#, c-format
+msgid "You are in the middle of a %s -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1045
+msgid "Options --squash and --fixup cannot be used together"
+msgstr ""
+
+#: builtin/commit.c:1055
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr ""
+
+#: builtin/commit.c:1057
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr ""
+
+#: builtin/commit.c:1063
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr ""
+
+#: builtin/commit.c:1080
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+
+#: builtin/commit.c:1082
+msgid "No paths with --include/--only does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1084
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+
+#: builtin/commit.c:1086
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+
+#: builtin/commit.c:1096 builtin/tag.c:556
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr ""
+
+#: builtin/commit.c:1101
+msgid "Paths with -a does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1280
+msgid "couldn't look up newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1282
+msgid "could not parse newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1323
+msgid "detached HEAD"
+msgstr ""
+
+#: builtin/commit.c:1325
+msgid " (root-commit)"
+msgstr ""
+
+#: builtin/commit.c:1415
+msgid "could not parse HEAD commit"
+msgstr ""
+
+#: builtin/commit.c:1452 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr ""
+
+#: builtin/commit.c:1459
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr ""
+
+#: builtin/commit.c:1466
+msgid "could not read MERGE_MODE"
+msgstr ""
+
+#: builtin/commit.c:1485
+#, c-format
+msgid "could not read commit message: %s"
+msgstr ""
+
+#: builtin/commit.c:1499
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr ""
+
+#: builtin/commit.c:1514 builtin/merge.c:935 builtin/merge.c:968
+msgid "failed to write commit object"
+msgstr ""
+
+#: builtin/commit.c:1535
+msgid "cannot lock HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1539
+msgid "cannot update HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1550
+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/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr ""
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr ""
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr ""
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr ""
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr ""
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr ""
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr ""
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr ""
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr ""
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr ""
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr ""
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr ""
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr ""
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr ""
+
+#: builtin/diff.c:347
+#, c-format
+msgid "invalid object '%s' given."
+msgstr ""
+
+#: builtin/diff.c:352
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:362
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:370
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr ""
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr ""
+
+#: builtin/fetch.c:252
+#, c-format
+msgid "object %s not found"
+msgstr ""
+
+#: builtin/fetch.c:258
+msgid "[up to date]"
+msgstr ""
+
+#: builtin/fetch.c:272
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr ""
+
+#: builtin/fetch.c:273 builtin/fetch.c:351
+msgid "[rejected]"
+msgstr ""
+
+#: builtin/fetch.c:284
+msgid "[tag update]"
+msgstr ""
+
+#: builtin/fetch.c:286 builtin/fetch.c:313 builtin/fetch.c:331
+msgid "  (unable to update local ref)"
+msgstr ""
+
+#: builtin/fetch.c:298
+msgid "[new tag]"
+msgstr ""
+
+#: builtin/fetch.c:302
+msgid "[new branch]"
+msgstr ""
+
+#: builtin/fetch.c:347
+msgid "unable to update local ref"
+msgstr ""
+
+#: builtin/fetch.c:347
+msgid "forced update"
+msgstr ""
+
+#: builtin/fetch.c:353
+msgid "(non-fast-forward)"
+msgstr ""
+
+#: builtin/fetch.c:384 builtin/fetch.c:676
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr ""
+
+#: builtin/fetch.c:393
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr ""
+
+#: builtin/fetch.c:479
+#, c-format
+msgid "From %.*s\n"
+msgstr ""
+
+#: builtin/fetch.c:490
+#, 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:540
+#, c-format
+msgid "   (%s will become dangling)\n"
+msgstr ""
+
+#: builtin/fetch.c:541
+#, c-format
+msgid "   (%s has become dangling)\n"
+msgstr ""
+
+#: builtin/fetch.c:548
+msgid "[deleted]"
+msgstr ""
+
+#: builtin/fetch.c:549
+msgid "(none)"
+msgstr ""
+
+#: builtin/fetch.c:666
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+
+#: builtin/fetch.c:700
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr ""
+
+#: builtin/fetch.c:777
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#: builtin/fetch.c:780
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr ""
+
+#: builtin/fetch.c:879
+#, c-format
+msgid "Fetching %s\n"
+msgstr ""
+
+#: builtin/fetch.c:881
+#, c-format
+msgid "Could not fetch %s"
+msgstr ""
+
+#: builtin/fetch.c:898
+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:918
+msgid "You need to specify a tag name."
+msgstr ""
+
+#: builtin/fetch.c:970
+msgid "fetch --all does not take a repository argument"
+msgstr ""
+
+#: builtin/fetch.c:972
+msgid "fetch --all does not make sense with refspecs"
+msgstr ""
+
+#: builtin/fetch.c:983
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr ""
+
+#: builtin/fetch.c:991
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr ""
+
+#: builtin/gc.c:78
+msgid "Too many options specified"
+msgstr ""
+
+#: builtin/gc.c:103
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr ""
+
+#: builtin/gc.c:223
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+
+#: builtin/gc.c:226
+#, 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:256
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr ""
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr ""
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr ""
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr ""
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr ""
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr ""
+
+#: builtin/grep.c:888
+msgid "no pattern given."
+msgstr ""
+
+#: builtin/grep.c:902
+#, c-format
+msgid "bad object %s"
+msgstr ""
+
+#: builtin/grep.c:943
+msgid "--open-files-in-pager only works on the worktree"
+msgstr ""
+
+#: builtin/grep.c:966
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr ""
+
+#: builtin/grep.c:971
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr ""
+
+#: builtin/grep.c:974
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+
+#: builtin/grep.c:982
+msgid "both --cached and trees are 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:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr ""
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr ""
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr ""
+
+#: builtin/init-db.c:362
+#, 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:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr ""
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr ""
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr ""
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr ""
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr ""
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr ""
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr ""
+
+#: builtin/log.c:187
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr ""
+
+#: builtin/log.c:395 builtin/log.c:483
+#, c-format
+msgid "Could not read object %s"
+msgstr ""
+
+#: builtin/log.c:507
+#, c-format
+msgid "Unknown type: %d"
+msgstr ""
+
+#: builtin/log.c:596
+msgid "format.headers without value"
+msgstr ""
+
+#: builtin/log.c:669
+msgid "name of output directory is too long"
+msgstr ""
+
+#: builtin/log.c:680
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr ""
+
+#: builtin/log.c:694
+msgid "Need exactly one range."
+msgstr ""
+
+#: builtin/log.c:702
+msgid "Not a range."
+msgstr ""
+
+#: builtin/log.c:739
+msgid "Could not extract email from committer identity."
+msgstr ""
+
+#: builtin/log.c:785
+msgid "Cover letter needs email format"
+msgstr ""
+
+#: builtin/log.c:879
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr ""
+
+#: builtin/log.c:952
+msgid "Two output directories?"
+msgstr ""
+
+#: builtin/log.c:1173
+#, c-format
+msgid "bogus committer info %s"
+msgstr ""
+
+#: builtin/log.c:1218
+msgid "-n and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1220
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr ""
+
+#: builtin/log.c:1225 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr ""
+
+#: builtin/log.c:1228
+msgid "--name-only does not make sense"
+msgstr ""
+
+#: builtin/log.c:1230
+msgid "--name-status does not make sense"
+msgstr ""
+
+#: builtin/log.c:1232
+msgid "--check does not make sense"
+msgstr ""
+
+#: builtin/log.c:1255
+msgid "standard output, or directory, which one?"
+msgstr ""
+
+#: builtin/log.c:1257
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr ""
+
+#: builtin/log.c:1410
+msgid "Failed to create output files"
+msgstr ""
+
+#: builtin/log.c:1514
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+
+#: builtin/log.c:1530 builtin/log.c:1532 builtin/log.c:1544
+#, c-format
+msgid "Unknown commit %s"
+msgstr ""
+
+#: builtin/merge.c:91
+msgid "switch `m' requires a value"
+msgstr ""
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr ""
+
+#: builtin/merge.c:129
+#, c-format
+msgid "Available strategies are:"
+msgstr ""
+
+#: builtin/merge.c:134
+#, c-format
+msgid "Available custom strategies are:"
+msgstr ""
+
+#: builtin/merge.c:241
+msgid "could not run stash."
+msgstr ""
+
+#: builtin/merge.c:246
+msgid "stash failed"
+msgstr ""
+
+#: builtin/merge.c:251
+#, c-format
+msgid "not a valid object: %s"
+msgstr ""
+
+#: builtin/merge.c:270 builtin/merge.c:287
+msgid "read-tree failed"
+msgstr ""
+
+#: builtin/merge.c:317
+msgid " (nothing to squash)"
+msgstr ""
+
+#: builtin/merge.c:330
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:362
+msgid "Writing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:364
+msgid "Finishing SQUASH_MSG"
+msgstr ""
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr ""
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr ""
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr ""
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr ""
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr ""
+
+#: builtin/merge.c:696
+msgid "Unable to write index."
+msgstr ""
+
+#: builtin/merge.c:709
+msgid "Not handling anything other than two heads merge."
+msgstr ""
+
+#: builtin/merge.c:723
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr ""
+
+#: builtin/merge.c:737
+#, c-format
+msgid "unable to write %s"
+msgstr ""
+
+#: builtin/merge.c:876
+#, c-format
+msgid "Could not read from '%s'"
+msgstr ""
+
+#: builtin/merge.c:885
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:891
+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 '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+
+#: builtin/merge.c:915
+msgid "Empty commit message."
+msgstr ""
+
+#: builtin/merge.c:927
+#, c-format
+msgid "Wonderful.\n"
+msgstr ""
+
+#: builtin/merge.c:1000
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+
+#: builtin/merge.c:1016
+#, c-format
+msgid "'%s' is not a commit"
+msgstr ""
+
+#: builtin/merge.c:1057
+msgid "No current branch."
+msgstr ""
+
+#: builtin/merge.c:1059
+msgid "No remote for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1061
+msgid "No default upstream defined for the current branch."
+msgstr ""
+
+#: builtin/merge.c:1066
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr ""
+
+#: builtin/merge.c:1188
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr ""
+
+#: builtin/merge.c:1204 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:1207 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1211
+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:1214
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr ""
+
+#: builtin/merge.c:1223
+msgid "You cannot combine --squash with --no-ff."
+msgstr ""
+
+#: builtin/merge.c:1228
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr ""
+
+#: builtin/merge.c:1235
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr ""
+
+#: builtin/merge.c:1266
+msgid "Can merge only exactly one commit into empty head"
+msgstr ""
+
+#: builtin/merge.c:1269
+msgid "Squash commit into empty head not supported yet"
+msgstr ""
+
+#: builtin/merge.c:1271
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr ""
+
+#: builtin/merge.c:1275 builtin/merge.c:1319
+#, c-format
+msgid "%s - not something we can merge"
+msgstr ""
+
+#: builtin/merge.c:1385
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr ""
+
+#: builtin/merge.c:1423
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr ""
+
+#: builtin/merge.c:1430
+#, c-format
+msgid "Nope.\n"
+msgstr ""
+
+#: builtin/merge.c:1462
+msgid "Not possible to fast-forward, aborting."
+msgstr ""
+
+#: builtin/merge.c:1485 builtin/merge.c:1562
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr ""
+
+#: builtin/merge.c:1489
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr ""
+
+#: builtin/merge.c:1553
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr ""
+
+#: builtin/merge.c:1555
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr ""
+
+#: builtin/merge.c:1564
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr ""
+
+#: builtin/merge.c:1575
+#, c-format
+msgid "Automatic merge went well; stopped before committing as requested\n"
+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 ""
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr ""
+
+#: builtin/mv.c:215
+#, c-format
+msgid "renaming '%s' failed"
+msgstr ""
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr ""
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr ""
+
+#: builtin/notes.c:175 builtin/tag.c:343
+#, c-format
+msgid "could not create file '%s'"
+msgstr ""
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr ""
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr ""
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr ""
+
+#: builtin/notes.c:251 builtin/tag.c:521
+#, c-format
+msgid "cannot read '%s'"
+msgstr ""
+
+#: builtin/notes.c:253 builtin/tag.c:524
+#, c-format
+msgid "could not open or read '%s'"
+msgstr ""
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:537
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr ""
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr ""
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr ""
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:350
+#, 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:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr ""
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr ""
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr ""
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr ""
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr ""
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr ""
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr ""
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
+msgstr ""
+
+#: builtin/notes.c:717
+#, 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:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr ""
+
+#: builtin/notes.c:1103
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2310
+#, c-format
+msgid "unsupported index version %s"
+msgstr ""
+
+#: builtin/pack-objects.c:2314
+#, c-format
+msgid "bad index version '%s'"
+msgstr ""
+
+#: builtin/pack-objects.c:2322
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr ""
+
+#: builtin/pack-objects.c:2326
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr ""
+
+#: builtin/push.c:44
+msgid "tag shorthand without <tag>"
+msgstr ""
+
+#: builtin/push.c:63
+msgid "--delete only accepts plain target ref names"
+msgstr ""
+
+#: builtin/push.c:73
+#, 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:80
+#, 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:88
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
+msgstr ""
+
+#: builtin/push.c:111
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+
+#: builtin/push.c:131
+#, c-format
+msgid "Pushing to %s\n"
+msgstr ""
+
+#: builtin/push.c:135
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr ""
+
+#: builtin/push.c:143
+#, c-format
+msgid ""
+"To prevent you from losing history, non-fast-forward updates were rejected\n"
+"Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
+"'Note about fast-forwards' section of 'git push --help' for details.\n"
+msgstr ""
+
+#: builtin/push.c:160
+#, c-format
+msgid "bad repository '%s'"
+msgstr ""
+
+#: builtin/push.c:161
+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:176
+msgid "--all and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:177
+msgid "--all can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:182
+msgid "--mirror and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:183
+msgid "--mirror can't be combined with refspecs"
+msgstr ""
+
+#: builtin/push.c:188
+msgid "--all and --mirror are incompatible"
+msgstr ""
+
+#: builtin/push.c:274
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr ""
+
+#: builtin/push.c:276
+msgid "--delete doesn't make sense without any refs"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "mixed"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "soft"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "hard"
+msgstr ""
+
+#: builtin/reset.c:33
+msgid "keep"
+msgstr ""
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr ""
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr ""
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr ""
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr ""
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr ""
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr ""
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr ""
+
+#: builtin/reset.c:223
+#, c-format
+msgid "Cannot do a %s reset in the middle of a merge."
+msgstr ""
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr ""
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr ""
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr ""
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr ""
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr ""
+
+#: builtin/revert.c:70 builtin/revert.c:91
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr ""
+
+#: builtin/revert.c:126
+msgid "program error"
+msgstr ""
+
+#: builtin/revert.c:209
+msgid "revert failed"
+msgstr ""
+
+#: builtin/revert.c:224
+msgid "cherry-pick failed"
+msgstr ""
+
+#: builtin/rm.c:109
+#, c-format
+msgid ""
+"'%s' has staged content different from both the file and the HEAD\n"
+"(use -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr ""
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr ""
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr ""
+
+#: builtin/tag.c:58
+#, c-format
+msgid "malformed object at '%s'"
+msgstr ""
+
+#: builtin/tag.c:205
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr ""
+
+#: builtin/tag.c:210
+#, c-format
+msgid "tag '%s' not found."
+msgstr ""
+
+#: builtin/tag.c:225
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr ""
+
+#: builtin/tag.c:237
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr ""
+
+#: builtin/tag.c:247
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:254
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+
+#: builtin/tag.c:294
+msgid "unable to sign the tag"
+msgstr ""
+
+#: builtin/tag.c:296
+msgid "unable to write tag file"
+msgstr ""
+
+#: builtin/tag.c:321
+msgid "bad object type."
+msgstr ""
+
+#: builtin/tag.c:334
+msgid "tag header too big."
+msgstr ""
+
+#: builtin/tag.c:366
+msgid "no tag message?"
+msgstr ""
+
+#: builtin/tag.c:372
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr ""
+
+#: builtin/tag.c:421
+msgid "switch 'points-at' requires an object"
+msgstr ""
+
+#: builtin/tag.c:423
+#, c-format
+msgid "malformed object name '%s'"
+msgstr ""
+
+#: builtin/tag.c:502
+msgid "-n option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:504
+msgid "--contains option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:506
+msgid "--points-at option is only allowed with -l."
+msgstr ""
+
+#: builtin/tag.c:514
+msgid "only one -F or -m option is allowed."
+msgstr ""
+
+#: builtin/tag.c:534
+msgid "too many params"
+msgstr ""
+
+#: builtin/tag.c:540
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr ""
+
+#: builtin/tag.c:545
+#, c-format
+msgid "tag '%s' already exists"
+msgstr ""
+
+#: builtin/tag.c:563
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr ""
+
+#: builtin/tag.c:565
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr ""
+
+#: builtin/tag.c:567
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr ""
+
+#: git-am.sh:49
+msgid "You need to set your committer info first"
+msgstr ""
+
+#: git-am.sh:136
+msgid "Repository lacks necessary blobs to fall back on 3-way merge."
+msgstr ""
+
+#: git-am.sh:147
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+
+#: git-am.sh:156
+msgid "Falling back to patching base and 3-way merge..."
+msgstr ""
+
+#: git-am.sh:268
+msgid "Only one StGIT patch series can be applied at once"
+msgstr ""
+
+#: git-am.sh:355
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr ""
+
+#: git-am.sh:357
+msgid "Patch format detection failed."
+msgstr ""
+
+#: git-am.sh:411
+msgid "-d option is no longer supported.  Do not use."
+msgstr ""
+
+#: git-am.sh:474
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr ""
+
+#: git-am.sh:479
+msgid "Please make up your mind. --skip or --abort?"
+msgstr ""
+
+#: git-am.sh:506
+msgid "Resolve operation not in progress, we are not resuming."
+msgstr ""
+
+#: git-am.sh:572
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr ""
+
+#: git-am.sh:748
+msgid "cannot be interactive without stdin connected to a terminal."
+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:759
+msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
+msgstr ""
+
+#: git-am.sh:795
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr ""
+
+#: git-am.sh:840
+msgid "No changes -- Patch already applied."
+msgstr ""
+
+#: git-am.sh:866
+msgid "applying to an empty history"
+msgstr ""
+
+#. 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 ""
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+msgstr ""
+
+#: git-bisect.sh:99
+#, sh-format
+msgid "'$arg' does not appear to be a valid revision"
+msgstr ""
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr ""
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr ""
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr ""
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr ""
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr ""
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr ""
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr ""
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr ""
+
+#. 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 ""
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr ""
+
+#: git-bisect.sh:363
+#, sh-format
+msgid ""
+"Could not check out original HEAD '$branch'.\n"
+"Try 'git bisect reset <commit>'."
+msgstr ""
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr ""
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr ""
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr ""
+
+#: git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr ""
+
+#: 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 ""
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr ""
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr ""
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr ""
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr ""
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr ""
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr ""
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr ""
+
+#: git-stash.sh:123 git-stash.sh:136
+msgid "Cannot save the current worktree state"
+msgstr ""
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr ""
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr ""
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr ""
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr ""
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr ""
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr ""
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr ""
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr ""
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr ""
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr ""
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr ""
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr ""
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr ""
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr ""
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr ""
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr ""
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr ""
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr ""
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr ""
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr ""
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr ""
+
+#: git-submodule.sh:56
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr ""
+
+#: git-submodule.sh:108
+#, sh-format
+msgid "No submodule mapping found in .gitmodules for path '$path'"
+msgstr ""
+
+#: git-submodule.sh:149
+#, sh-format
+msgid "Clone of '$url' into submodule path '$path' failed"
+msgstr ""
+
+#: git-submodule.sh:159
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr ""
+
+#: git-submodule.sh:247
+#, sh-format
+msgid "repo URL: '$repo' must be absolute or begin with ./|../"
+msgstr ""
+
+#: git-submodule.sh:264
+#, sh-format
+msgid "'$path' already exists in the index"
+msgstr ""
+
+#: git-submodule.sh:281
+#, sh-format
+msgid "'$path' already exists and is not a valid git repo"
+msgstr ""
+
+#: git-submodule.sh:295
+#, sh-format
+msgid "Unable to checkout submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:300
+#, sh-format
+msgid "Failed to add submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:305
+#, sh-format
+msgid "Failed to register submodule '$path'"
+msgstr ""
+
+#: git-submodule.sh:347
+#, sh-format
+msgid "Entering '$prefix$path'"
+msgstr ""
+
+#: git-submodule.sh:359
+#, sh-format
+msgid "Stopping at '$path'; script returned non-zero status."
+msgstr ""
+
+#: git-submodule.sh:401
+#, sh-format
+msgid "No url found for submodule path '$path' in .gitmodules"
+msgstr ""
+
+#: git-submodule.sh:410
+#, sh-format
+msgid "Failed to register url for submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:418
+#, sh-format
+msgid "Failed to register update mode for submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:420
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$path'"
+msgstr ""
+
+#: git-submodule.sh:519
+#, sh-format
+msgid ""
+"Submodule path '$path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+
+#: git-submodule.sh:532
+#, sh-format
+msgid "Unable to find current revision in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:551
+#, sh-format
+msgid "Unable to fetch in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:565
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:566
+#, sh-format
+msgid "Submodule path '$path': rebased into '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:571
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:572
+#, sh-format
+msgid "Submodule path '$path': merged in '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:577
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:578
+#, sh-format
+msgid "Submodule path '$path': checked out '$sha1'"
+msgstr ""
+
+#: git-submodule.sh:600 git-submodule.sh:923
+#, sh-format
+msgid "Failed to recurse into submodule path '$path'"
+msgstr ""
+
+#: git-submodule.sh:708
+msgid "--"
+msgstr ""
+
+#: git-submodule.sh:766
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr ""
+
+#: git-submodule.sh:769
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:772
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr ""
+
+#: git-submodule.sh:797
+msgid "blob"
+msgstr ""
+
+#: git-submodule.sh:798
+msgid "submodule"
+msgstr ""
+
+#: git-submodule.sh:969
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr ""
diff --git a/po/de.po b/po/de.po
new file mode 100644 (file)
index 0000000..0edce30
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,3828 @@
+# German translations for Git.
+# Copyright (C) 2010-2012 Ralf Thielow <ralf.thielow@googlemail.com>
+# This file is distributed under the same license as the Git package.
+# Ralf Thielow <ralf.thielow@googlemail.com>, 2010, 2011, 2012.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: git 1.7.10\n"
+"Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
+"POT-Creation-Date: 2012-05-15 06:31+0800\n"
+"PO-Revision-Date: 2012-03-28 18:46+0200\n"
+"Last-Translator: Ralf Thielow <ralf.thielow@googlemail.com>\n"
+"Language-Team: German\n"
+"Language: de\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);\n"
+
+#: advice.c:40
+#, c-format
+msgid "hint: %.*s\n"
+msgstr "Hinweis: %.*s\n"
+
+#.
+#. * Message used both when 'git commit' fails and when
+#. * other commands doing a merge do.
+#.
+#: advice.c:70
+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 ""
+"Korrigiere dies im Arbeitsbaum,\n"
+"und benutze dann 'git add/rm <Datei>'\n"
+"um die Auflösung entsprechend zu markieren und einzutragen,\n"
+"oder benutze 'git commit -a'."
+
+#: commit.c:48
+#, c-format
+msgid "could not parse %s"
+msgstr "konnte %s nicht parsen"
+
+#: commit.c:50
+#, c-format
+msgid "%s %s is not a commit!"
+msgstr "%s %s ist keine Version!"
+
+#: compat/obstack.c:406 compat/obstack.c:408
+msgid "memory exhausted"
+msgstr "Speicher verbraucht"
+
+#: connected.c:39
+msgid "Could not run 'git rev-list'"
+msgstr "Konnte 'git rev-list' nicht ausführen"
+
+#: connected.c:48
+#, c-format
+msgid "failed write to rev-list: %s"
+msgstr "Fehler beim Schreiben nach rev-list: %s"
+
+#: connected.c:56
+#, c-format
+msgid "failed to close rev-list's stdin: %s"
+msgstr "Fehler beim Schließen von rev-list's Standard-Eingabe: %s"
+
+#: diff.c:105
+#, c-format
+msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
+msgstr ""
+"  Fehler beim Parsen des abgeschnittenen \"dirstat\" Prozentsatzes '%.*s'\n"
+
+#: diff.c:110
+#, c-format
+msgid "  Unknown dirstat parameter '%.*s'\n"
+msgstr "  Unbekannter \"dirstat\" Parameter '%.*s'\n"
+
+#: diff.c:210
+#, c-format
+msgid ""
+"Found errors in 'diff.dirstat' config variable:\n"
+"%s"
+msgstr ""
+"Fehler in 'diff.dirstat' Konfigurationsvariable gefunden:\n"
+"%s"
+
+#: diff.c:1400
+msgid " 0 files changed\n"
+msgstr " 0 Dateien geändert\n"
+
+#: diff.c:1404
+#, c-format
+msgid " %d file changed"
+msgid_plural " %d files changed"
+msgstr[0] " %d Datei geändert"
+msgstr[1] " %d Dateien geändert"
+
+#: diff.c:1421
+#, c-format
+msgid ", %d insertion(+)"
+msgid_plural ", %d insertions(+)"
+msgstr[0] ", %d Zeile hinzugefügt(+)"
+msgstr[1] ", %d Zeilen hinzugefügt(+)"
+
+#: diff.c:1432
+#, c-format
+msgid ", %d deletion(-)"
+msgid_plural ", %d deletions(-)"
+msgstr[0] ", %d Zeile entfernt(-)"
+msgstr[1] ", %d Zeilen entfernt(-)"
+
+#: diff.c:3439
+#, c-format
+msgid ""
+"Failed to parse --dirstat/-X option parameter:\n"
+"%s"
+msgstr ""
+"Fehler beim Parsen des --dirstat/-X Optionsparameters:\n"
+"%s"
+
+#: gpg-interface.c:59
+msgid "could not run gpg."
+msgstr "konnte gpg nicht ausführen"
+
+#: gpg-interface.c:71
+msgid "gpg did not accept the data"
+msgstr "gpg hat die Daten nicht akzeptiert"
+
+#: gpg-interface.c:82
+msgid "gpg failed to sign the data"
+msgstr "gpg beim Signieren der Daten fehlgeschlagen"
+
+#: grep.c:1280
+#, c-format
+msgid "'%s': unable to read %s"
+msgstr "'%s': konnte nicht lesen %s"
+
+#: grep.c:1297
+#, c-format
+msgid "'%s': %s"
+msgstr "'%s': %s"
+
+#: grep.c:1308
+#, c-format
+msgid "'%s': short read %s"
+msgstr "'%s': read() zu kurz %s"
+
+#: help.c:287
+#, 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' scheint ein git-Kommando zu sein, konnte aber\n"
+"nicht ausgeführt werden. Vielleicht ist git-%s fehlerhaft?"
+
+#: remote.c:1607
+#, 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] "Dein Zweig ist vor '%s' um %d Version.\n"
+msgstr[1] "Dein Zweig ist vor '%s' um %d Versionen.\n"
+
+#: remote.c:1613
+#, 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] ""
+"Dein Zweig ist zu '%s' um %d Version hinterher, und kann vorgespult werden.\n"
+msgstr[1] ""
+"Dein Zweig ist zu '%s' um %d Versionen hinterher, und kann vorgespult "
+"werden.\n"
+
+#: remote.c:1621
+#, 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] ""
+"Dein Zweig und '%s' sind divergiert,\n"
+"und haben jeweils %d und %d unterschiedliche Versionen.\n"
+msgstr[1] ""
+"Dein Zweig und '%s' sind divergiert,\n"
+"und haben jeweils %d und %d unterschiedliche Versionen.\n"
+
+#: sequencer.c:120 builtin/merge.c:865 builtin/merge.c:978
+#: builtin/merge.c:1088 builtin/merge.c:1098
+#, c-format
+msgid "Could not open '%s' for writing"
+msgstr "Konnte '%s' nicht zum Schreiben öffnen."
+
+#: sequencer.c:122 builtin/merge.c:333 builtin/merge.c:868
+#: builtin/merge.c:1090 builtin/merge.c:1103
+#, c-format
+msgid "Could not write to '%s'"
+msgstr "Konnte nicht nach '%s' schreiben."
+
+#: sequencer.c:143
+msgid ""
+"after resolving the conflicts, mark the corrected paths\n"
+"with 'git add <paths>' or 'git rm <paths>'"
+msgstr ""
+"nach Auflösung der Konflikte, markiere die korrigierten Pfade\n"
+"mit 'git add <Pfade>' oder 'git rm <Pfade>'"
+
+#: sequencer.c:146
+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 ""
+"nach Auflösung der Konflikte, markiere die korrigierten Pfade\n"
+"mit 'git add <Pfade>' oder 'git rm <Pfade>'und trage das Ergebnis ein mit "
+"'git commit'"
+
+#: sequencer.c:159 sequencer.c:685 sequencer.c:768
+#, c-format
+msgid "Could not write to %s"
+msgstr "Konnte nicht nach %s schreiben"
+
+#: sequencer.c:162
+#, c-format
+msgid "Error wrapping up %s"
+msgstr "Fehler bei Nachbereitung von %s"
+
+#: sequencer.c:177
+msgid "Your local changes would be overwritten by cherry-pick."
+msgstr ""
+"Deine lokalen Änderungen würden von \"cherry-pick\" überschrieben werden."
+
+#: sequencer.c:179
+msgid "Your local changes would be overwritten by revert."
+msgstr "Deine lokalen Änderungen würden von \"revert\" überschrieben werden."
+
+#: sequencer.c:182
+msgid "Commit your changes or stash them to proceed."
+msgstr "Trage deine Änderungen ein oder benutze \"stash\" um fortzufahren."
+
+#. TRANSLATORS: %s will be "revert" or "cherry-pick"
+#: sequencer.c:232
+#, c-format
+msgid "%s: Unable to write new index file"
+msgstr "%s: Konnte neue Bereitstellungsdatei nicht schreiben"
+
+#: sequencer.c:298
+msgid "Your index file is unmerged."
+msgstr "Deine Bereitstellungsdatei ist nicht zusammengeführt."
+
+#: sequencer.c:301
+msgid "You do not have a valid HEAD"
+msgstr "Du hast keine gültige Zweigspitze (HEAD)"
+
+#: sequencer.c:316
+#, 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."
+
+#: sequencer.c:324
+#, c-format
+msgid "Commit %s does not have parent %d"
+msgstr "Version %s hat keinen Elternteil %d"
+
+#: sequencer.c:328
+#, c-format
+msgid "Mainline was specified but commit %s is not a merge."
+msgstr ""
+"Hauptlinie wurde spezifiziert, aber Version %s ist keine Zusammenführung."
+
+#. TRANSLATORS: The first %s will be "revert" or
+#. "cherry-pick", the second %s a SHA1
+#: sequencer.c:339
+#, c-format
+msgid "%s: cannot parse parent commit %s"
+msgstr "%s: kann Elternversion %s nicht parsen"
+
+#: sequencer.c:343
+#, c-format
+msgid "Cannot get commit message for %s"
+msgstr "Kann keine Versionsbeschreibung für %s bekommen"
+
+#: sequencer.c:427
+#, c-format
+msgid "could not revert %s... %s"
+msgstr "Konnte %s nicht zurücksetzen... %s"
+
+#: sequencer.c:428
+#, c-format
+msgid "could not apply %s... %s"
+msgstr "Konnte %s nicht anwenden... %s"
+
+#: sequencer.c:450 sequencer.c:909 builtin/log.c:288 builtin/log.c:713
+#: builtin/log.c:1329 builtin/log.c:1548 builtin/merge.c:347
+#: builtin/shortlog.c:181
+msgid "revision walk setup failed"
+msgstr "Einrichtung des Revisionsgangs fehlgeschlagen"
+
+#: sequencer.c:453
+msgid "empty commit set passed"
+msgstr "leere Menge von Versionen übergeben"
+
+#: sequencer.c:461
+#, c-format
+msgid "git %s: failed to read the index"
+msgstr "git %s: Fehler beim Lesen der Bereitstellung"
+
+#: sequencer.c:466
+#, c-format
+msgid "git %s: failed to refresh the index"
+msgstr "git %s: Fehler beim Aktualisieren der Bereitstellung"
+
+#: sequencer.c:551
+#, c-format
+msgid "Cannot %s during a %s"
+msgstr "Kann %s nicht während eines %s durchführen"
+
+#: sequencer.c:573
+#, c-format
+msgid "Could not parse line %d."
+msgstr "Konnte Zeile %d nicht parsen."
+
+#: sequencer.c:578
+msgid "No commits parsed."
+msgstr "Keine Versionen geparst."
+
+#: sequencer.c:591
+#, c-format
+msgid "Could not open %s"
+msgstr "Konnte %s nicht öffnen"
+
+#: sequencer.c:595
+#, c-format
+msgid "Could not read %s."
+msgstr "Konnte %s nicht lesen."
+
+#: sequencer.c:602
+#, c-format
+msgid "Unusable instruction sheet: %s"
+msgstr "Unbenutzbares Instruktionsblatt: %s"
+
+#: sequencer.c:630
+#, c-format
+msgid "Invalid key: %s"
+msgstr "Ungültiger Schlüssel: %s"
+
+#: sequencer.c:633
+#, c-format
+msgid "Invalid value for %s: %s"
+msgstr "Ungültiger Wert für %s: %s"
+
+#: sequencer.c:645
+#, c-format
+msgid "Malformed options sheet: %s"
+msgstr "Fehlerhaftes Optionsblatt: %s"
+
+#: sequencer.c:666
+msgid "a cherry-pick or revert is already in progress"
+msgstr "\"cherry-pick\" oder \"revert\" ist bereits im Gang"
+
+#: sequencer.c:667
+msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
+msgstr "versuche \"git cherry-pick (--continue | --quit | --abort)\""
+
+#: sequencer.c:671
+#, c-format
+msgid "Could not create sequencer directory %s"
+msgstr "Konnte \"sequencer\"-Verzeichnis %s nicht erstellen"
+
+#: sequencer.c:687 sequencer.c:772
+#, c-format
+msgid "Error wrapping up %s."
+msgstr "Fehler beim Einpacken von %s."
+
+#: sequencer.c:706 sequencer.c:840
+msgid "no cherry-pick or revert in progress"
+msgstr "kein \"cherry-pick\" oder \"revert\" im Gang"
+
+#: sequencer.c:708
+msgid "cannot resolve HEAD"
+msgstr "kann Zweigspitze (HEAD) nicht auflösen"
+
+#: sequencer.c:710
+msgid "cannot abort from a branch yet to be born"
+msgstr "kann nicht abbrechen: bin auf einem Zweig, der noch geboren wird"
+
+#: sequencer.c:732
+#, c-format
+msgid "cannot open %s: %s"
+msgstr "Kann %s nicht öffnen: %s"
+
+#: sequencer.c:735
+#, c-format
+msgid "cannot read %s: %s"
+msgstr "Kann %s nicht lesen: %s"
+
+#: sequencer.c:736
+msgid "unexpected end of file"
+msgstr "Unerwartetes Dateiende"
+
+#: sequencer.c:742
+#, 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"
+
+#: sequencer.c:765
+#, c-format
+msgid "Could not format %s."
+msgstr "Konnte %s nicht formatieren."
+
+#: sequencer.c:927
+msgid "Can't revert as initial commit"
+msgstr "Kann nicht zu initialer Version zurücksetzen."
+
+#: sequencer.c:928
+msgid "Can't cherry-pick into empty head"
+msgstr "Kann \"cherry-pick\" nicht in einem leerem Kopf ausführen."
+
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr "Zweigspitze (HEAD) zeigt auf keinen Zweig"
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "Kein solcher Zweig '%s'"
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr "Kein entferntes Projektarchiv für Zweig '%s' konfiguriert."
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr "Zweig '%s' des entfernten Projektarchivs ist nicht als entfernter "
+"Übernahmezweig gespeichert"
+
+#: wt-status.c:134
+msgid "Unmerged paths:"
+msgstr "Nicht zusammengeführte Pfade:"
+
+#: wt-status.c:140 wt-status.c:157
+#, c-format
+msgid "  (use \"git reset %s <file>...\" to unstage)"
+msgstr ""
+"  (benutze \"git reset %s <Datei>...\" zum Herausnehmen aus der "
+"Bereitstellung)"
+
+#: wt-status.c:142 wt-status.c:159
+msgid "  (use \"git rm --cached <file>...\" to unstage)"
+msgstr ""
+"  (benutze \"git rm --cached <Datei>...\" zum Herausnehmen aus der "
+"Bereitstellung)"
+
+#: wt-status.c:143
+msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
+msgstr ""
+"  (benutze \"git add/rm <Datei>...\" um die Auflösung entsprechend zu "
+"markieren)"
+
+#: wt-status.c:151
+msgid "Changes to be committed:"
+msgstr "zum Eintragen bereitgestellte Änderungen:"
+
+#: wt-status.c:169
+msgid "Changes not staged for commit:"
+msgstr "Änderungen, die nicht zum Eintragen bereitgestellt sind:"
+
+#: wt-status.c:173
+msgid "  (use \"git add <file>...\" to update what will be committed)"
+msgstr "  (benutze \"git add <Datei>...\" zum Bereitstellen)"
+
+#: wt-status.c:175
+msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
+msgstr "  (benutze \"git add/rm <Datei>...\" zum Bereitstellen)"
+
+#: wt-status.c:176
+msgid ""
+"  (use \"git checkout -- <file>...\" to discard changes in working directory)"
+msgstr ""
+"  (benutze \"git checkout -- <Datei>...\" um die Änderungen im "
+"Arbeitsverzeichnis zu verwerfen)"
+
+#: wt-status.c:178
+msgid "  (commit or discard the untracked or modified content in submodules)"
+msgstr ""
+"  (trage ein oder verwerfe den unbeobachteten oder geänderten Inhalt in den "
+"Unterprojekten)"
+
+#: wt-status.c:187
+#, c-format
+msgid "%s files:"
+msgstr "%s Dateien:"
+
+#: wt-status.c:190
+#, c-format
+msgid "  (use \"git %s <file>...\" to include in what will be committed)"
+msgstr "  (benutze \"git %s <Datei>...\" zum Einfügen in die Eintragung)"
+
+#: wt-status.c:207
+msgid "bug"
+msgstr "Fehler"
+
+#: wt-status.c:212
+msgid "both deleted:"
+msgstr "beide gelöscht:"
+
+#: wt-status.c:213
+msgid "added by us:"
+msgstr "von uns hinzugefügt:"
+
+#: wt-status.c:214
+msgid "deleted by them:"
+msgstr "von denen gelöscht:"
+
+#: wt-status.c:215
+msgid "added by them:"
+msgstr "von denen hinzugefügt:"
+
+#: wt-status.c:216
+msgid "deleted by us:"
+msgstr "von uns gelöscht:"
+
+#: wt-status.c:217
+msgid "both added:"
+msgstr "von beiden hinzugefügt:"
+
+#: wt-status.c:218
+msgid "both modified:"
+msgstr "von beiden geändert:"
+
+#: wt-status.c:248
+msgid "new commits, "
+msgstr "neue Versionen, "
+
+#: wt-status.c:250
+msgid "modified content, "
+msgstr "geänderter Inhalt, "
+
+#: wt-status.c:252
+msgid "untracked content, "
+msgstr "unbeobachteter Inhalt, "
+
+#: wt-status.c:266
+#, c-format
+msgid "new file:   %s"
+msgstr "neue Datei:   %s"
+
+#: wt-status.c:269
+#, c-format
+msgid "copied:     %s -> %s"
+msgstr "kopiert:     %s -> %s"
+
+#: wt-status.c:272
+#, c-format
+msgid "deleted:    %s"
+msgstr "gelöscht:    %s"
+
+#: wt-status.c:275
+#, c-format
+msgid "modified:   %s"
+msgstr "geändert:   %s"
+
+#: wt-status.c:278
+#, c-format
+msgid "renamed:    %s -> %s"
+msgstr "umbenannt:    %s -> %s"
+
+#: wt-status.c:281
+#, c-format
+msgid "typechange: %s"
+msgstr "Typänderung: %s"
+
+#: wt-status.c:284
+#, c-format
+msgid "unknown:    %s"
+msgstr "unbekannt:    %s"
+
+#: wt-status.c:287
+#, c-format
+msgid "unmerged:   %s"
+msgstr "nicht zusammengeführt:   %s"
+
+#: wt-status.c:290
+#, c-format
+msgid "bug: unhandled diff status %c"
+msgstr "Fehler: unbehandelter Differenz-Status %c"
+
+#: wt-status.c:713
+msgid "On branch "
+msgstr "Auf Zweig "
+
+#: wt-status.c:720
+msgid "Not currently on any branch."
+msgstr "Im Moment auf keinem Zweig."
+
+#: wt-status.c:731
+msgid "Initial commit"
+msgstr "Initiale Version"
+
+#: wt-status.c:745
+msgid "Untracked"
+msgstr "Unbeobachtete"
+
+#: wt-status.c:747
+msgid "Ignored"
+msgstr "Ignorierte"
+
+#: wt-status.c:749
+#, c-format
+msgid "Untracked files not listed%s"
+msgstr "Unbeobachtete Dateien nicht aufgelistet%s"
+
+#: wt-status.c:751
+msgid " (use -u option to show untracked files)"
+msgstr " (benutze die Option -u um unbeobachteten Dateien anzuzeigen)"
+
+#: wt-status.c:757
+msgid "No changes"
+msgstr "Keine Änderungen"
+
+#: wt-status.c:761
+#, c-format
+msgid "no changes added to commit%s\n"
+msgstr "keine Änderungen zum Eintragen hinzugefügt%s\n"
+
+#: wt-status.c:763
+msgid " (use \"git add\" and/or \"git commit -a\")"
+msgstr " (benutze \"git add\" und/oder \"git commit -a\")"
+
+#: wt-status.c:765
+#, c-format
+msgid "nothing added to commit but untracked files present%s\n"
+msgstr ""
+"nichts zum Eintragen hinzugefügt, aber es gibt unbeobachtete Dateien%s\n"
+
+#: wt-status.c:767
+msgid " (use \"git add\" to track)"
+msgstr " (benutze \"git add\" zum Beobachten)"
+
+#: wt-status.c:769 wt-status.c:772 wt-status.c:775
+#, c-format
+msgid "nothing to commit%s\n"
+msgstr "nichts zum Eintragen%s\n"
+
+#: wt-status.c:770
+msgid " (create/copy files and use \"git add\" to track)"
+msgstr " (Erstelle/Kopiere Dateien und benutze \"git add\" zum Beobachten)"
+
+#: wt-status.c:773
+msgid " (use -u to show untracked files)"
+msgstr " (benutze die Option -u um unbeobachtete Dateien anzuzeigen)"
+
+#: wt-status.c:776
+msgid " (working directory clean)"
+msgstr " (Arbeitsverzeichnis sauber)"
+
+#: wt-status.c:884
+msgid "HEAD (no branch)"
+msgstr "HEAD (kein Zweig)"
+
+#: wt-status.c:890
+msgid "Initial commit on "
+msgstr "Initiale Version auf "
+
+#: wt-status.c:905
+msgid "behind "
+msgstr "hinterher "
+
+#: wt-status.c:908 wt-status.c:911
+msgid "ahead "
+msgstr "voraus "
+
+#: wt-status.c:913
+msgid ", behind "
+msgstr ", hinterher "
+
+#: builtin/add.c:62
+#, c-format
+msgid "unexpected diff status %c"
+msgstr "unerwarteter Differenz-Status %c"
+
+#: builtin/add.c:67 builtin/commit.c:298
+msgid "updating files failed"
+msgstr "Aktualisierung der Dateien fehlgeschlagen"
+
+#: builtin/add.c:77
+#, c-format
+msgid "remove '%s'\n"
+msgstr "entferne '%s'\n"
+
+#: builtin/add.c:176
+#, c-format
+msgid "Path '%s' is in submodule '%.*s'"
+msgstr "Pfad '%s' befindet sich in Unterprojekt '%.*s'"
+
+#: builtin/add.c:192
+msgid "Unstaged changes after refreshing the index:"
+msgstr ""
+"Nicht bereitgestellte Änderungen nach Aktualisierung der Bereitstellung:"
+
+#: builtin/add.c:195 builtin/add.c:456 builtin/rm.c:186
+#, c-format
+msgid "pathspec '%s' did not match any files"
+msgstr "Pfadspezifikation '%s' stimmt mit keinen Dateien überein"
+
+#: builtin/add.c:209
+#, c-format
+msgid "'%s' is beyond a symbolic link"
+msgstr "'%s' ist über einem symbolischen Link"
+
+#: builtin/add.c:276
+msgid "Could not read the index"
+msgstr "Konnte die Bereitstellung nicht lesen"
+
+#: builtin/add.c:286
+#, c-format
+msgid "Could not open '%s' for writing."
+msgstr "Konnte '%s' nicht zum Schreiben öffnen."
+
+#: builtin/add.c:290
+msgid "Could not write patch"
+msgstr "Konnte Patch nicht schreiben"
+
+#: builtin/add.c:295
+#, c-format
+msgid "Could not stat '%s'"
+msgstr "Konnte Verzeichnis '%s' nicht lesen"
+
+#: builtin/add.c:297
+msgid "Empty patch. Aborted."
+msgstr "Leerer Patch. Abgebrochen."
+
+#: builtin/add.c:303
+#, c-format
+msgid "Could not apply '%s'"
+msgstr "Konnte '%s' nicht anwenden."
+
+#: builtin/add.c:312
+msgid "The following paths are ignored by one of your .gitignore files:\n"
+msgstr ""
+"Die folgenden Pfade werden durch eine deiner \".gitignore\" Dateien "
+"ignoriert:\n"
+
+#: builtin/add.c:352
+#, c-format
+msgid "Use -f if you really want to add them.\n"
+msgstr "Verwende -f wenn du diese wirklich hinzufügen möchtest.\n"
+
+#: builtin/add.c:353
+msgid "no files added"
+msgstr "keine Dateien hinzugefügt"
+
+#: builtin/add.c:359
+msgid "adding files failed"
+msgstr "Hinzufügen von Dateien fehlgeschlagen"
+
+#: builtin/add.c:391
+msgid "-A and -u are mutually incompatible"
+msgstr "-A und -u sind zueinander inkompatibel"
+
+#: builtin/add.c:393
+msgid "Option --ignore-missing can only be used together with --dry-run"
+msgstr ""
+"Die Option --ignore-missing kann nur zusammen mit --dry-run benutzt werden."
+
+#: builtin/add.c:413
+#, c-format
+msgid "Nothing specified, nothing added.\n"
+msgstr "Nichts spezifiziert, nichts hinzugefügt.\n"
+
+#: builtin/add.c:414
+#, c-format
+msgid "Maybe you wanted to say 'git add .'?\n"
+msgstr "Wolltest du vielleicht 'git add .' sagen?\n"
+
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:358 builtin/mv.c:82
+#: builtin/rm.c:162
+msgid "index file corrupt"
+msgstr "Bereitstellungsdatei beschädigt"
+
+#: builtin/add.c:476 builtin/mv.c:229 builtin/rm.c:260
+msgid "Unable to write new index file"
+msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
+msgstr "Konnte Archiv-Datei '%s' nicht erstellen."
+
+#: builtin/archive.c:20
+msgid "could not redirect output"
+msgstr "Konnte Ausgabe nicht umleiten."
+
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
+msgstr "git archive: Externes Archiv ohne URL"
+
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
+msgstr "git archive: habe ACK/NAK erwartet, aber EOF bekommen"
+
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
+msgstr "git archive: NACK %s"
+
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
+msgstr "Fehler am anderen Ende: %s"
+
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
+msgstr "git archive: Protokollfehler"
+
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
+msgstr "git archive: erwartete eine Spülung (flush)"
+
+#: builtin/branch.c:137
+#, 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."
+
+#: builtin/branch.c:141
+#, 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."
+
+#. TRANSLATORS: This is "remote " in "remote branch '%s' not found"
+#: builtin/branch.c:164
+msgid "remote "
+msgstr "externer "
+
+#: builtin/branch.c:172
+msgid "cannot use -a with -d"
+msgstr "kann -a nicht mit -d benutzen"
+
+#: builtin/branch.c:178
+msgid "Couldn't look up commit object for HEAD"
+msgstr "Konnte Versionsobjekt für Zweigspitze (HEAD) nicht nachschlagen."
+
+#: builtin/branch.c:183
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
+msgstr ""
+"Kann Zweig '%s' nicht entfernen, da du dich gerade auf diesem befindest."
+
+#: builtin/branch.c:193
+#, c-format
+msgid "%sbranch '%s' not found."
+msgstr "%sZweig '%s' nicht gefunden."
+
+#: builtin/branch.c:201
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
+msgstr "Konnte Versionsobjekt für '%s' nicht nachschlagen."
+
+#: builtin/branch.c:207
+#, 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 du sicher bist diesen Zweig zu entfernen, führe 'git branch -D %s' aus."
+
+#: builtin/branch.c:215
+#, c-format
+msgid "Error deleting %sbranch '%s'"
+msgstr "Fehler beim Löschen von %sZweig '%s'"
+
+#: builtin/branch.c:221
+#, c-format
+msgid "Deleted %sbranch %s (was %s).\n"
+msgstr "Entferne %sZweig %s (war %s).\n"
+
+#: builtin/branch.c:226
+msgid "Update of config-file failed"
+msgstr "Aktualisierung der Konfigurationsdatei fehlgeschlagen."
+
+#: builtin/branch.c:324
+#, c-format
+msgid "branch '%s' does not point at a commit"
+msgstr "Zweig '%s' zeigt auf keine Version"
+
+#: builtin/branch.c:396
+#, c-format
+msgid "behind %d] "
+msgstr "%d hinterher] "
+
+#: builtin/branch.c:398
+#, c-format
+msgid "ahead %d] "
+msgstr "%d voraus] "
+
+#: builtin/branch.c:400
+#, c-format
+msgid "ahead %d, behind %d] "
+msgstr "%d voraus, %d hinterher] "
+
+#: builtin/branch.c:503
+msgid "(no branch)"
+msgstr "(kein Zweig)"
+
+#: builtin/branch.c:568
+msgid "some refs could not be read"
+msgstr "Konnte einige Referenzen nicht lesen"
+
+#: builtin/branch.c:581
+msgid "cannot rename the current branch while not on any."
+msgstr ""
+"Kann aktuellen Zweig nicht umbennen, solange du dich auf keinem befindest."
+
+#: builtin/branch.c:591
+#, c-format
+msgid "Invalid branch name: '%s'"
+msgstr "Ungültiger Zweig-Name: '%s'"
+
+#: builtin/branch.c:606
+msgid "Branch rename failed"
+msgstr "Umbenennung des Zweiges fehlgeschlagen"
+
+#: builtin/branch.c:610
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
+msgstr "falsch benannten Zweig '%s' umbenannt"
+
+#: builtin/branch.c:614
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
+msgstr "Zweig umbenannt zu %s, aber Zweigspitze (HEAD) ist nicht aktualisiert!"
+
+#: builtin/branch.c:621
+msgid "Branch is renamed, but update of config-file failed"
+msgstr ""
+"Zweig ist umbenannt, aber die Aktualisierung der Konfigurationsdatei ist "
+"fehlgeschlagen."
+
+#: builtin/branch.c:636
+#, c-format
+msgid "malformed object name %s"
+msgstr "Missgebildeter Objektname %s"
+
+#: builtin/branch.c:660
+#, c-format
+msgid "could not write branch description template: %s\n"
+msgstr "Konnte Beschreibungsvorlage für Zweig nicht schreiben: %s\n"
+
+#: builtin/branch.c:750
+msgid "Failed to resolve HEAD as a valid ref."
+msgstr "Konnte Zweigspitze (HEAD) nicht als gültige Referenz auflösen."
+
+#: builtin/branch.c:755 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
+msgstr "Zweigspitze (HEAD) wurde nicht unter \"refs/heads\" gefunden!"
+
+#: builtin/branch.c:813
+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' machen mit einem Zweignamen keinen "
+"Sinn."
+
+#: builtin/bundle.c:47
+#, c-format
+msgid "%s is okay\n"
+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."
+
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr "Zum Entpacken wird ein Projektarchiv benötigt."
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
+#, c-format
+msgid "path '%s' does not have our version"
+msgstr "Pfad '%s' hat nicht unsere Version."
+
+#: builtin/checkout.c:115 builtin/checkout.c:148
+#, c-format
+msgid "path '%s' does not have their version"
+msgstr "Pfad '%s' hat nicht deren Version."
+
+#: builtin/checkout.c:131
+#, c-format
+msgid "path '%s' does not have all necessary versions"
+msgstr "Pfad '%s' hat nicht alle notwendigen Versionen."
+
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
+msgstr "Pfad '%s' hat nicht die notwendigen Versionen."
+
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
+msgstr "Pfad '%s': kann nicht zusammenführen"
+
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
+msgstr "Konnte Ergebnis der Zusammenführung von '%s' nicht hinzufügen."
+
+#: builtin/checkout.c:212 builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr "make_cache_entry für Pfad '%s' fehlgeschlagen"
+
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
+msgstr "beschädigte Bereitstellungsdatei"
+
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
+msgstr "Pfad '%s' ist nicht zusammengeführt."
+
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
+msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
+msgstr "diff_setup_done fehlgeschlagen"
+
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr "Du musst zuerst deine aktuelle Bereitstellung auflösen."
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr "Konnte \"reflog\" für '%s' nicht durchführen\n"
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr "Zweigspitze (HEAD) ist jetzt bei"
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr "Setze Zweig '%s' zurück\n"
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr "Bereits auf '%s'\n"
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr "Gewechselt zu zurückgesetztem Zweig '%s'\n"
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr "Gewechselt zu einem neuen Zweig '%s'\n"
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr "Gewechselt zu Zweig '%s'\n"
+
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr " ... und %d weitere.\n"
+
+#. The singular version
+#: builtin/checkout.c:646
+#, 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] ""
+"Warnung: Du bist um %d Version hinterher, nicht verbunden zu\n"
+"einem deiner Zweige:\n"
+"\n"
+"%s\n"
+msgstr[1] ""
+"Warnung: Du bist um %d Versionen hinterher, nicht verbunden zu\n"
+"einem deiner Zweige:\n"
+"\n"
+"%s\n"
+
+#: builtin/checkout.c:664
+#, 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 ""
+"Wenn du diese durch einen neuen Zweig behalten möchtest, dann könnte jetzt\n"
+"ein guter Zeitpunkt sein dies zu tun mit:\n"
+"\n"
+" git branch neuer_zweig_name %s\n"
+"\n"
+
+#: builtin/checkout.c:693
+msgid "internal error in revision walk"
+msgstr "interner Fehler im Revisionsgang"
+
+#: builtin/checkout.c:697
+msgid "Previous HEAD position was"
+msgstr "Vorherige Position der Zweigspitze (HEAD) war"
+
+#: builtin/checkout.c:723
+msgid "You are on a branch yet to be born"
+msgstr "du bist auf einem Zweig, der noch geboren wird"
+
+#. case (1)
+#: builtin/checkout.c:854
+#, c-format
+msgid "invalid reference: %s"
+msgstr "Ungültige Referenz: %s"
+
+#. case (1): want a tree
+#: builtin/checkout.c:893
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr "Referenz ist kein Baum: %s"
+
+#: builtin/checkout.c:973
+msgid "-B cannot be used with -b"
+msgstr "-B kann nicht mit -b benutzt werden"
+
+#: builtin/checkout.c:982
+msgid "--patch is incompatible with all other options"
+msgstr "--patch ist inkompatibel mit allen anderen Optionen"
+
+#: builtin/checkout.c:985
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr "--detach kann nicht mit -b/-B/--orphan benutzt werden"
+
+#: builtin/checkout.c:987
+msgid "--detach cannot be used with -t"
+msgstr "--detach kann nicht mit -t benutzt werden"
+
+#: builtin/checkout.c:993
+msgid "--track needs a branch name"
+msgstr "--track benötigt einen Zweignamen"
+
+#: builtin/checkout.c:1000
+msgid "Missing branch name; try -b"
+msgstr "Vermisse Zweignamen; versuche -b"
+
+#: builtin/checkout.c:1006
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr "--orphan und -b|-B sind gegenseitig exklusiv"
+
+#: builtin/checkout.c:1008
+msgid "--orphan cannot be used with -t"
+msgstr "--orphan kann nicht mit -t benutzt werden"
+
+#: builtin/checkout.c:1018
+msgid "git checkout: -f and -m are incompatible"
+msgstr "git checkout: -f und -m sind inkompatibel"
+
+#: builtin/checkout.c:1052
+msgid "invalid path specification"
+msgstr "ungültige Pfadspezifikation"
+
+#: builtin/checkout.c:1060
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+"git checkout: Die Aktualisierung von Pfaden ist inkompatibel mit dem Wechsel "
+"von Zweigen.\n"
+"Hast du beabsichtigt '%s' auszuchecken, welcher nicht als Version aufgelöst "
+"werden kann?"
+
+#: builtin/checkout.c:1062
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+"git checkout: Die Aktualisierung von Pfaden ist inkompatibel mit dem Wechsel "
+"von Zweigen."
+
+#: builtin/checkout.c:1067
+msgid "git checkout: --detach does not take a path argument"
+msgstr "git checkout: --detach nimmt kein Pfad-Argument"
+
+#: builtin/checkout.c:1070
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+"git checkout: --ours/--theirs, --force and --merge sind inkompatibel wenn\n"
+"du aus der Bereitstellung auscheckst."
+
+#: builtin/checkout.c:1089
+msgid "Cannot switch branch to a non-commit."
+msgstr "Kann Zweig nur zu einer Version wechseln."
+
+#: builtin/checkout.c:1092
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr "--ours/--theirs ist inkompatibel mit den Wechseln von Zweigen."
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr "-x und -X können nicht zusammen benutzt werden"
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+"clean.requireForce auf \"true\" gesetzt und weder -n noch -f gegeben; "
+"Säuberung verweigert"
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+"clean.requireForce standardmäßig auf \"true\" gesetzt und weder -n noch -f "
+"gegeben; Säuberung verweigert"
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr "Würde %s entfernen\n"
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr "Entferne %s\n"
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr "Fehler beim Entfernen von %s"
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr "Würde nicht entfernen %s\n"
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr "Entferne nicht %s\n"
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr "Referenziertes Projektarchiv '%s' ist kein lokales Verzeichnis."
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr "Fehler beim Öffnen von '%s'"
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr "Fehler beim Erstellen von Verzeichnis '%s'"
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr "Konnte '%s' nicht lesen"
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr "%s existiert und ist kein Verzeichnis"
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr "Konnte %s nicht lesen\n"
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr "Konnte '%s' nicht entfernen"
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr "Konnte Verknüpfung '%s' nicht erstellen"
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr "Konnte Datei nicht nach '%s' kopieren"
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr "Fertig.\n"
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr "Konnte zu klonenden externer Zweig %s nicht finden."
+
+#: builtin/clone.c:549
+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"
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr "Zu viele Argumente."
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr "Du musst ein Projektarchiv zum Klonen angeben."
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr "--bare und --origin %s Optionen sind inkompatibel."
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr "Projektarchiv '%s' existiert nicht."
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr "--depth wird in lokalen Klonen ignoriert; benutze stattdessen file://."
+
+#: builtin/clone.c:683
+#, 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:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr "Arbeitsbaum '%s' existiert bereits."
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr "Konnte führende Verzeichnisse von '%s' nicht erstellen."
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr "Konnte Arbeitsverzeichnis '%s' nicht erstellen."
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr "Klone in bloßes Projektarchiv '%s'...\n"
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr "Klone nach '%s'...\n"
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr "Weiß nicht wie %s zu klonen ist."
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr "externer Zweig %s nicht im anderen Projektarchiv %s gefunden"
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr "Du scheinst ein leeres Projektarchiv geklont zu haben."
+
+#: builtin/commit.c:42
+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 ""
+"Dein Name und E-Mail Adresse wurden automatisch auf Basis\n"
+"deines Benutzer- und Rechnernamens konfiguriert. Bitte prüfe, dass diese\n"
+"zutreffend sind. Du kannst diese Meldung unterdrücken, indem du diese\n"
+"explizit setzt:\n"
+"\n"
+"    git config --global user.name \"Dein Name\"\n"
+"    git config --global user.email deine@emailadresse.de\n"
+"\n"
+"Nachdem du das getan hast, kannst du deine Identität für diese Version "
+"ändern mit:\n"
+"\n"
+"    git commit --amend --reset-author\n"
+
+#: builtin/commit.c:54
+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 ""
+"Du fragtest die jüngste Version nachzubessern, aber das würde diese leer\n"
+"machen. Du kannst Dein Kommando mit --allow-empty wiederholen, oder die\n"
+"Version mit \"git reset HEAD^\" vollständig entfernen.\n"
+
+#: builtin/commit.c:59
+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 du dies trotzdem eintragen willst, benutze:\n"
+"\n"
+"    git commit --allow-empty\n"
+"\n"
+"Andernfalls benutze bitte 'git reset'\n"
+
+#: builtin/commit.c:205 builtin/reset.c:33
+msgid "merge"
+msgstr "zusammenführen"
+
+#: builtin/commit.c:208
+msgid "cherry-pick"
+msgstr "cherry-pick"
+
+#: builtin/commit.c:325
+msgid "failed to unpack HEAD tree object"
+msgstr "Fehler beim Entpacken des Baum-Objektes der Zweigspitze (HEAD)."
+
+#: builtin/commit.c:367
+msgid "unable to create temporary index"
+msgstr "Konnte temporäre Bereitstellung nicht erstellen."
+
+#: builtin/commit.c:373
+msgid "interactive add failed"
+msgstr "interaktives Hinzufügen fehlgeschlagen"
+
+#: builtin/commit.c:406 builtin/commit.c:427 builtin/commit.c:473
+msgid "unable to write new_index file"
+msgstr "Konnte new_index Datei nicht schreiben"
+
+#: builtin/commit.c:457
+#, c-format
+msgid "cannot do a partial commit during a %s."
+msgstr "Kann keine partielle Eintragung durchführen, während %s im Gange ist."
+
+#: builtin/commit.c:466
+msgid "cannot read the index"
+msgstr "Kann Bereitstellung nicht lesen"
+
+#: builtin/commit.c:486
+msgid "unable to write temporary index file"
+msgstr "Konnte temporäre Bereitstellungsdatei nicht schreiben."
+
+#: builtin/commit.c:561 builtin/commit.c:567
+#, c-format
+msgid "invalid commit: %s"
+msgstr "Ungültige Version: %s"
+
+#: builtin/commit.c:590
+msgid "malformed --author parameter"
+msgstr "Fehlerhafter --author Parameter"
+
+#: builtin/commit.c:651
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr "Fehlerhafte Identifikations-String: '%s'"
+
+#: builtin/commit.c:689 builtin/commit.c:722 builtin/commit.c:1033
+#, c-format
+msgid "could not lookup commit %s"
+msgstr "Konnte Version %s nicht nachschlagen"
+
+#: builtin/commit.c:701 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr "(lese Log-Nachricht von Standard-Eingabe)\n"
+
+#: builtin/commit.c:703
+msgid "could not read log from standard input"
+msgstr "Konnte Log nicht von Standard-Eingabe lesen."
+
+#: builtin/commit.c:707
+#, c-format
+msgid "could not read log file '%s'"
+msgstr "Konnte Log-Datei '%s' nicht lesen"
+
+#: builtin/commit.c:713
+msgid "commit has empty message"
+msgstr "Version hat eine leere Beschreibung"
+
+#: builtin/commit.c:729
+msgid "could not read MERGE_MSG"
+msgstr "Konnte MERGE_MSG nicht lesen"
+
+#: builtin/commit.c:733
+msgid "could not read SQUASH_MSG"
+msgstr "Konnte SQUASH_MSG nicht lesen"
+
+#: builtin/commit.c:737
+#, c-format
+msgid "could not read '%s'"
+msgstr "Konnte '%s' nicht lesen"
+
+#: builtin/commit.c:765
+#, c-format
+msgid "could not open '%s'"
+msgstr "Konnte '%s' nicht öffnen"
+
+#: builtin/commit.c:789
+msgid "could not write commit template"
+msgstr "Konnte Versionsvorlage nicht schreiben"
+
+#: builtin/commit.c:799
+#, c-format
+msgid ""
+"\n"
+"It looks like you may be committing a %s.\n"
+"If this is not correct, please remove the file\n"
+"\t%s\n"
+"and try again.\n"
+msgstr ""
+"\n"
+"Es sieht so aus, als trägst du ein '%s' ein.\n"
+"Falls das nicht korrekt ist, entferne bitte die Datei\n"
+"\t%s\n"
+"und versuche es erneut.\n"
+
+#: builtin/commit.c:812
+msgid "Please enter the commit message for your changes."
+msgstr "Bitte gebe die Versionsbeschreibung für deine Änderungen ein."
+
+#: builtin/commit.c:815
+msgid ""
+" Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+" Zeilen, die mit '#'\n"
+"beginnen, werden ignoriert, und eine leere Versionsbeschreibung bricht die "
+"Eintragung ab.\n"
+
+#: builtin/commit.c:820
+msgid ""
+" Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+" Zeilen, die mit '#'\n"
+"beginnen, werden beibehalten; wenn du möchtest, kannst du diese entfernen.\n"
+"Eine leere Versionsbeschreibung bricht die Eintragung ab.\n"
+
+#: builtin/commit.c:832
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr "%sAutor:    %s"
+
+#: builtin/commit.c:839
+#, c-format
+msgid "%sCommitter: %s"
+msgstr "%sEintragender: %s"
+
+#: builtin/commit.c:859
+msgid "Cannot read index"
+msgstr "Kann Bereitstellung nicht lesen"
+
+#: builtin/commit.c:896
+msgid "Error building trees"
+msgstr "Fehler beim Erzeugen der Zweige"
+
+#: builtin/commit.c:911 builtin/tag.c:357
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr "Bitte liefere die Beschreibung entweder mit der Option -m oder -F.\n"
+
+#: builtin/commit.c:1008
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr "Kein existierender Autor mit '%s' gefunden."
+
+#: builtin/commit.c:1023 builtin/commit.c:1217
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr "Ungültiger Modus '%s' für unbeobachtete Dateien"
+
+#: builtin/commit.c:1063
+msgid "Using both --reset-author and --author does not make sense"
+msgstr "Verwendung von --reset-author und --author macht keinen Sinn."
+
+#: builtin/commit.c:1074
+msgid "You have nothing to amend."
+msgstr "Du hast nichts zum nachbessern."
+
+#: builtin/commit.c:1076
+#, c-format
+msgid "You are in the middle of a %s -- cannot amend."
+msgstr "%s ist im Gange -- kann nicht nachbessern."
+
+#: builtin/commit.c:1078
+msgid "Options --squash and --fixup cannot be used together"
+msgstr ""
+"Die Optionen --squash und --fixup können nicht gemeinsam benutzt werden."
+
+#: builtin/commit.c:1088
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr "Nur eines von -c/-C/-F/--fixup kann benutzt werden."
+
+#: builtin/commit.c:1090
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr "Option -m kann nicht mit -c/-C/-F/--fixup kombiniert werden"
+
+#: builtin/commit.c:1098
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr "--reset--author kann nur mit -C, -c oder --amend benutzt werden"
+
+#: builtin/commit.c:1115
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+"Nur eines von --include/--only/--all/--interactive/--patch kann benutzt "
+"werden."
+
+#: builtin/commit.c:1117
+msgid "No paths with --include/--only does not make sense."
+msgstr "--include/--only machen ohne Pfade keinen Sinn."
+
+#: builtin/commit.c:1119
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+"Klug... die letzte Version mit einer unsauberen Bereitstellung nachbessern."
+
+#: builtin/commit.c:1121
+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:1131 builtin/tag.c:556
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr "Ungültiger \"cleanup\" Modus %s"
+
+#: builtin/commit.c:1136
+msgid "Paths with -a does not make sense."
+msgstr "Pfade mit -a machen keinen Sinn."
+
+#: builtin/commit.c:1315
+msgid "couldn't look up newly created commit"
+msgstr "Konnte neu erstellte Version nicht nachschlagen."
+
+#: builtin/commit.c:1317
+msgid "could not parse newly created commit"
+msgstr "Konnte neulich erstellte Version nicht analysieren."
+
+#: builtin/commit.c:1358
+msgid "detached HEAD"
+msgstr "losgelöste Zweigspitze (HEAD)"
+
+#: builtin/commit.c:1360
+msgid " (root-commit)"
+msgstr " (Basis-Version)"
+
+#: builtin/commit.c:1450
+msgid "could not parse HEAD commit"
+msgstr "Konnte Version der Zweigspitze (HEAD) nicht analysieren."
+
+#: builtin/commit.c:1487 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr "Konnte '%s' nicht zum Lesen öffnen."
+
+#: builtin/commit.c:1494
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr "Beschädigte MERGE_HEAD-Datei (%s)"
+
+#: builtin/commit.c:1501
+msgid "could not read MERGE_MODE"
+msgstr "Konnte MERGE_MODE nicht lesen"
+
+#: builtin/commit.c:1520
+#, c-format
+msgid "could not read commit message: %s"
+msgstr "Konnte Versionsbeschreibung nicht lesen: %s"
+
+#: builtin/commit.c:1534
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "Eintragung abgebrochen; du hast die Beschreibung nicht editiert.\n"
+
+#: builtin/commit.c:1539
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr "Eintragung aufgrund leerer Versionsbeschreibung abgebrochen.\n"
+
+#: builtin/commit.c:1554 builtin/merge.c:936 builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr "Fehler beim Schreiben des Versionsobjektes."
+
+#: builtin/commit.c:1575
+msgid "cannot lock HEAD ref"
+msgstr "Kann Referenz der Zweigspitze (HEAD) nicht sperren."
+
+#: builtin/commit.c:1579
+msgid "cannot update HEAD ref"
+msgstr "Kann Referenz der Zweigspitze (HEAD) nicht aktualisieren."
+
+#: builtin/commit.c:1590
+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"
+"konnte nicht geschrieben werden. Prüfe, dass dein Speicher nicht\n"
+"voll und Dein Kontingent nicht aufgebraucht ist und führe\n"
+"anschließend \"git reset HEAD\" zu Wiederherstellung aus."
+
+#: builtin/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr "annotierte Markierung %s ist nicht verfügbar"
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr "annotierte Markierung %s hat keinen eingebetteten Namen"
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr "Markierung '%s' ist eigentlich '%s' hier"
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr "%s ist kein gültiger Objekt-Name"
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr "%s ist kein gültiges '%s' Objekt"
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr "kein Markierung entspricht exakt '%s'"
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr "suche zur Beschreibung von %s\n"
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr "beendete Suche bei %s\n"
+
+#: builtin/describe.c:353
+#, 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: versuche --tags."
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+"Keine Markierungen können '%s' beschreiben.\n"
+"Versuche --always oder erstelle einige Markierungen."
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr "%lu Versionen durchlaufen\n"
+
+#: builtin/describe.c:381
+#, 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"
+"Suche bei %s aufgegeben\n"
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr "--long ist inkompatibel mit --abbrev=0"
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr "Keine Namen gefunden, kann nichts beschreiben."
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr "--dirty ist inkompatibel mit Versionen"
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr "'%s': keine reguläre Datei oder symbolischer Link"
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr "Ungültige Option: %s"
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr "Kein Git-Projektarchiv"
+
+#: builtin/diff.c:347
+#, c-format
+msgid "invalid object '%s' given."
+msgstr "Objekt '%s' ist ungültig."
+
+#: builtin/diff.c:352
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr "Mehr als %d Zweige angegeben: '%s'"
+
+#: builtin/diff.c:362
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr "Mehr als zwei Blobs angegeben: '%s'"
+
+#: builtin/diff.c:370
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr "unbehandeltes Objekt '%s' angegeben"
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr "Konnte externe Referenz der Zweigspitze (HEAD) nicht finden."
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr "Objekt %s nicht gefunden"
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr "[aktuell]"
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr "! %-*s %-*s -> %s  (kann nicht im aktuellen Zweig anfordern)"
+
+#: builtin/fetch.c:274 builtin/fetch.c:360
+msgid "[rejected]"
+msgstr "[zurückgewiesen]"
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr "[Markierungsaktualisierung]"
+
+#: builtin/fetch.c:287 builtin/fetch.c:322 builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr "  (kann lokale Referenz nicht aktualisieren)"
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr "[neue Markierung]"
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr "[neuer Zweig]"
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[neue Referenz]"
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr "kann lokale Referenz nicht aktualisieren"
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr "Aktualisierung erzwungen"
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr "(kein Vorspulen)"
+
+#: builtin/fetch.c:393 builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr "kann %s nicht öffnen: %s\n"
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr "%s hat nicht alle erforderlichen Objekte gesendet\n"
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr "Von %.*s\n"
+
+#: builtin/fetch.c:499
+#, 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; versuche\n"
+"'git remote prune %s' um jeden älteren, widersprüchlichen Zweig zu entfernen."
+
+#: builtin/fetch.c:549
+#, c-format
+msgid "   (%s will become dangling)\n"
+msgstr "   (%s wird unreferenziert)\n"
+
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)\n"
+msgstr "   (%s wurde unreferenziert)\n"
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr "[gelöscht]"
+
+#: builtin/fetch.c:558
+msgid "(none)"
+msgstr "(nichts)"
+
+#: builtin/fetch.c:675
+#, 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 wurde verweigert."
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr "Weiß nicht wie von %s angefordert wird."
+
+#: builtin/fetch.c:786
+#, 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:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr "Option \"%s\" wird ignoriert für %s\n"
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr "Fordere an von %s\n"
+
+#: builtin/fetch.c:890
+#, c-format
+msgid "Could not fetch %s"
+msgstr "Konnte nicht von %s anfordern"
+
+#: builtin/fetch.c:907
+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 gebe entweder eine URL\n"
+"oder den Namen des externen Archivs an, von welchem neue\n"
+"Versionen angefordert werden sollen."
+
+#: builtin/fetch.c:927
+msgid "You need to specify a tag name."
+msgstr "Du musst den Namen der Markierung angeben."
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr "fetch --all akzeptiert kein Projektarchiv als Argument"
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr "fetch --all macht keinen Sinn mit Referenzspezifikationen"
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr "Kein externes Archiv (einzeln oder Gruppe): %s"
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+msgstr ""
+"Abholen einer Gruppe und Spezifizieren von Referenzspezifikationen macht "
+"keinen Sinn."
+
+#: builtin/gc.c:63
+#, c-format
+msgid "Invalid %s: '%s'"
+msgstr "Ungültiger %s: '%s'"
+
+#: builtin/gc.c:90
+#, c-format
+msgid "insanely long object directory %.*s"
+msgstr "zu langes Objekt-Verzeichnis %.*s"
+
+#: builtin/gc.c:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+"Die Datenbank des Projektarchivs wird für eine optimale Performance "
+"komprimiert.\n"
+
+#: builtin/gc.c:224
+#, 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 ""
+"Die Datenbank des Projektarchivs wird für eine optimale Performance\n"
+"komprimiert. Du kannst auch \"git gc\" manuell ausführen. Siehe \"git help gc"
+"\" für weitere Informationen.\n"
+
+#: builtin/gc.c:251
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+"Es gibt zu viele unerreichbare lose Objekte; führe 'git prune' aus, um diese "
+"zu entfernen."
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr "grep: Fehler beim Erzeugen eines Thread: %s"
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr "Fehler beim Verzeichniswechsel: %s"
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr "konnte Zweig (%s) nicht lesen"
+
+#: builtin/grep.c:526
+#, 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:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr "Schalter '%c' erwartet einen numerischen Wert"
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr "kann '%s' nicht öffnen"
+
+#: builtin/grep.c:888
+msgid "no pattern given."
+msgstr "keine Muster gegeben"
+
+#: builtin/grep.c:902
+#, c-format
+msgid "bad object %s"
+msgstr "ungültiges Objekt %s"
+
+#: builtin/grep.c:943
+msgid "--open-files-in-pager only works on the worktree"
+msgstr "--open-files-in-pager arbeitet nur auf dem Arbeitsbaum"
+
+#: builtin/grep.c:966
+msgid "--cached or --untracked cannot be used with --no-index."
+msgstr "--cached oder --untracked kann nicht mit --no-index benutzt werden"
+
+#: builtin/grep.c:971
+msgid "--no-index or --untracked cannot be used with revs."
+msgstr "--no-index oder --untracked kann nicht mit Revisionen benutzt werden"
+
+#: builtin/grep.c:974
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
+msgstr ""
+"--[no-]exlude-standard kann nicht mit beobachteten Inhalten benutzt werden"
+
+#: builtin/grep.c:982
+msgid "both --cached and trees are given."
+msgstr "sowohl --cached als auch Zweige gegeben"
+
+#: builtin/init-db.c:35
+#, c-format
+msgid "Could not make %s writable by group"
+msgstr "Konnte %s nicht schreibbar für Gruppen machen"
+
+#: builtin/init-db.c:62
+#, c-format
+msgid "insanely long template name %s"
+msgstr "zu langer Vorlagen-Name %s"
+
+#: builtin/init-db.c:67
+#, c-format
+msgid "cannot stat '%s'"
+msgstr "Kann '%s' nicht lesen"
+
+#: builtin/init-db.c:73
+#, c-format
+msgid "cannot stat template '%s'"
+msgstr "kann Vorlage '%s' nicht lesen"
+
+#: builtin/init-db.c:80
+#, c-format
+msgid "cannot opendir '%s'"
+msgstr "kann Verzeichnis '%s' nicht öffnen"
+
+#: builtin/init-db.c:97
+#, c-format
+msgid "cannot readlink '%s'"
+msgstr "kann Verknüfpung '%s' nicht lesen"
+
+#: builtin/init-db.c:99
+#, c-format
+msgid "insanely long symlink %s"
+msgstr "zu lange symbolische Verknüpfung %s"
+
+#: builtin/init-db.c:102
+#, c-format
+msgid "cannot symlink '%s' '%s'"
+msgstr "kann '%s' nicht mit '%s' symbolisch verknüpfen"
+
+#: builtin/init-db.c:106
+#, c-format
+msgid "cannot copy '%s' to '%s'"
+msgstr "kann '%s' nicht nach '%s' kopieren"
+
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
+msgstr "ignoriere Vorlage %s"
+
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
+msgstr "zu langer Vorlagen-Pfad %s"
+
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
+msgstr "keine Vorlagen in '%s' gefunden"
+
+#: builtin/init-db.c:154
+#, c-format
+msgid "not copying templates of a wrong format version %d from '%s'"
+msgstr "kopiere keine Vorlagen mit einer falschen Formatversion %d von '%s'"
+
+#: builtin/init-db.c:192
+#, c-format
+msgid "insane git directory %s"
+msgstr "ungültiges git Verzeichnis %s"
+
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
+msgstr "%s existiert bereits"
+
+#: builtin/init-db.c:354
+#, c-format
+msgid "unable to handle file type %d"
+msgstr "kann nicht mit Dateityp %d umgehen"
+
+#: builtin/init-db.c:357
+#, c-format
+msgid "unable to move %s to %s"
+msgstr "Konnte %s nicht nach %s verschieben"
+
+#: builtin/init-db.c:362
+#, c-format
+msgid "Could not create git link %s"
+msgstr "Konnte git-Verknüfung %s nicht erstellen"
+
+#.
+#. * 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:419
+#, c-format
+msgid "%s%s Git repository in %s%s\n"
+msgstr "%s%s Git-Projektarchiv in %s%s\n"
+
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr "Reinitialisierte existierendes"
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr "Initialisierte leeres"
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr " geteiltes"
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
+msgstr "kann aktuelles Verzeichnis nicht bestimmen"
+
+#: builtin/init-db.c:521 builtin/init-db.c:528
+#, c-format
+msgid "cannot mkdir %s"
+msgstr "kann Verzeichnis %s nicht erstellen"
+
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
+msgstr "kann nicht in Verzeichnis %s wechseln"
+
+#: builtin/init-db.c:554
+#, c-format
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
+msgstr ""
+"%s (oder --work-tree=<Verzeichnis>) nicht erlaubt ohne Spezifizierung von %s "
+"(oder --git-dir=<Verzeichnis>)"
+
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
+msgstr "Kann nicht auf aktuelles Arbeitsverzeichnis zugreifen."
+
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr "Kann nicht auf Arbeitsbaum '%s' zugreifen."
+
+#: builtin/log.c:188
+#, c-format
+msgid "Final output: %d %s\n"
+msgstr "letzte Ausgabe: %d %s\n"
+
+#: builtin/log.c:401 builtin/log.c:489
+#, c-format
+msgid "Could not read object %s"
+msgstr "Kann Objekt %s nicht lesen."
+
+#: builtin/log.c:513
+#, c-format
+msgid "Unknown type: %d"
+msgstr "Unbekannter Typ: %d"
+
+#: builtin/log.c:602
+msgid "format.headers without value"
+msgstr "format.headers ohne Wert"
+
+#: builtin/log.c:675
+msgid "name of output directory is too long"
+msgstr "Name des Ausgabeverzeichnisses ist zu lang."
+
+#: builtin/log.c:686
+#, c-format
+msgid "Cannot open patch file %s"
+msgstr "Kann Patch-Datei %s nicht öffnen"
+
+#: builtin/log.c:700
+msgid "Need exactly one range."
+msgstr "Brauche genau einen Versionsbereich."
+
+#: builtin/log.c:708
+msgid "Not a range."
+msgstr "Kein Versionsbereich."
+
+#: builtin/log.c:745
+msgid "Could not extract email from committer identity."
+msgstr "Konnte E-Mail-Adresse des Einreichers nicht extrahieren."
+
+#: builtin/log.c:791
+msgid "Cover letter needs email format"
+msgstr "Anschreiben benötigt E-Mail-Format"
+
+#: builtin/log.c:885
+#, c-format
+msgid "insane in-reply-to: %s"
+msgstr "ungültiges in-reply-to: %s"
+
+#: builtin/log.c:958
+msgid "Two output directories?"
+msgstr "Zwei Ausgabeverzeichnisse?"
+
+#: builtin/log.c:1179
+#, c-format
+msgid "bogus committer info %s"
+msgstr "unechte Einreicher-Informationen %s"
+
+#: builtin/log.c:1224
+msgid "-n and -k are mutually exclusive."
+msgstr "-n und -k schliessen sich gegenseitig aus"
+
+#: builtin/log.c:1226
+msgid "--subject-prefix and -k are mutually exclusive."
+msgstr "--subject-prefix und -k schliessen sich gegenseitig aus"
+
+#: builtin/log.c:1231 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "nicht erkanntes Argument: %s"
+
+#: builtin/log.c:1234
+msgid "--name-only does not make sense"
+msgstr "--name-only macht keinen Sinn"
+
+#: builtin/log.c:1236
+msgid "--name-status does not make sense"
+msgstr "--name-status macht keinen Sinn"
+
+#: builtin/log.c:1238
+msgid "--check does not make sense"
+msgstr "--check macht keinen Sinn"
+
+#: builtin/log.c:1261
+msgid "standard output, or directory, which one?"
+msgstr "Standard-Ausgabe oder Verzeichnis, welches von beidem?"
+
+#: builtin/log.c:1263
+#, c-format
+msgid "Could not create directory '%s'"
+msgstr "Konnte Verzeichnis '%s' nicht erstellen."
+
+#: builtin/log.c:1416
+msgid "Failed to create output files"
+msgstr "Fehler beim Erstellen der Ausgabedateien."
+
+#: builtin/log.c:1520
+#, c-format
+msgid ""
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgstr ""
+"Konnte gefolgten, externen Zweig nicht finden, bitte gebe <upstream> manuell "
+"an.\n"
+
+#: builtin/log.c:1536 builtin/log.c:1538 builtin/log.c:1550
+#, c-format
+msgid "Unknown commit %s"
+msgstr "Unbekannte Version %s"
+
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
+msgstr "Schalter 'm' erfordert einen Wert."
+
+#: builtin/merge.c:127
+#, c-format
+msgid "Could not find merge strategy '%s'.\n"
+msgstr "Konnte Zusammenführungsstrategie '%s' nicht finden.\n"
+
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
+msgstr "Verfügbare Strategien sind:"
+
+#: builtin/merge.c:133
+#, c-format
+msgid "Available custom strategies are:"
+msgstr "Verfügbare benutzerdefinierte Strategien sind:"
+
+#: builtin/merge.c:240
+msgid "could not run stash."
+msgstr "Konnte \"stash\" nicht ausführen."
+
+#: builtin/merge.c:245
+msgid "stash failed"
+msgstr "\"stash\" fehlgeschlagen"
+
+#: builtin/merge.c:250
+#, c-format
+msgid "not a valid object: %s"
+msgstr "kein gültiges Objekt: %s"
+
+#: builtin/merge.c:269 builtin/merge.c:286
+msgid "read-tree failed"
+msgstr "read-tree fehlgeschlagen"
+
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
+msgstr " (nichts zu quetschen)"
+
+#: builtin/merge.c:329
+#, c-format
+msgid "Squash commit -- not updating HEAD\n"
+msgstr "Quetsche Version -- Zweigspitze (HEAD) wird nicht aktualisiert\n"
+
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
+msgstr "Schreibe SQUASH_MSG"
+
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
+msgstr "Schließe SQUASH_MSG ab"
+
+#: builtin/merge.c:386
+#, c-format
+msgid "No merge message -- not updating HEAD\n"
+msgstr ""
+"Keine Zusammenführungsbeschreibung -- Zweigspitze (HEAD) wird nicht "
+"aktualisiert\n"
+
+#: builtin/merge.c:437
+#, c-format
+msgid "'%s' does not point to a commit"
+msgstr "'%s' zeigt auf keine Version"
+
+#: builtin/merge.c:536
+#, c-format
+msgid "Bad branch.%s.mergeoptions string: %s"
+msgstr "Ungültiger branch.%s.mergeoptions String: %s"
+
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
+msgstr "\"git write-tree\" schlug beim Schreiben eines Baumes fehl"
+
+#: builtin/merge.c:679
+msgid "failed to read the cache"
+msgstr "Lesen des Zwischenspeichers fehlgeschlagen"
+
+#: builtin/merge.c:697
+msgid "Unable to write index."
+msgstr "Konnte Bereitstellung nicht schreiben."
+
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
+msgstr "Es wird nur die Zusammenführung von zwei Zweigen behandelt."
+
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
+msgstr "Unbekannte Option für merge-recursive: -X%s"
+
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
+msgstr "konnte %s nicht schreiben"
+
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
+msgstr "konnte nicht von '%s' lesen"
+
+#: builtin/merge.c:886
+#, c-format
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgstr ""
+"Zusammenführung wurde nicht eingetragen; benutze 'git commit' um die "
+"Zusammenführung abzuschließen.\n"
+
+#: builtin/merge.c:892
+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 '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
+msgstr ""
+"Bitte gebe 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"
+"\n"
+"Zeilen beginnend mit '#' werden ignoriert, und eine leere Beschreibung "
+"bricht die Eintragung ab.\n"
+
+#: builtin/merge.c:916
+msgid "Empty commit message."
+msgstr "Leere Versionsbeschreibung"
+
+#: builtin/merge.c:928
+#, c-format
+msgid "Wonderful.\n"
+msgstr "Wunderbar.\n"
+
+#: builtin/merge.c:993
+#, c-format
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+msgstr ""
+"Automatische Zusammenführung fehlgeschlagen; behebe die Konflikte und trage "
+"dann das Ergebnis ein.\n"
+
+#: builtin/merge.c:1009
+#, c-format
+msgid "'%s' is not a commit"
+msgstr "'%s' ist keine Version"
+
+#: builtin/merge.c:1050
+msgid "No current branch."
+msgstr "Du befindest dich auf keinem Zweig."
+
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
+msgstr "Kein externes Archiv für den aktuellen Zweig."
+
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
+msgstr ""
+"Es ist kein externes Standard-Projektarchiv für den aktuellen Zweig "
+"definiert."
+
+#: builtin/merge.c:1059
+#, c-format
+msgid "No remote tracking branch for %s from %s"
+msgstr "Kein externer Übernahmezweig für %s von %s"
+
+#: builtin/merge.c:1146 builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - nichts was wir zusammenführen können"
+
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
+msgstr "Es gibt keine Zusammenführung zum Abbrechen (vermisse MERGE_HEAD)"
+
+#: builtin/merge.c:1230 git-pull.sh:31
+msgid ""
+"You have not concluded your merge (MERGE_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Du hast deine Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert).\n"
+"Bitte trage deine Änderungen ein, bevor du zusammenführen kannst."
+
+#: builtin/merge.c:1233 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
+msgstr ""
+"Du hast deine Zusammenführung nicht abgeschlossen (MERGE_HEAD existiert)."
+
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
+msgstr ""
+"Du hast \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert).\n"
+"Bitte trage deine Änderungen ein, bevor du zusammenführen kannst."
+
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+msgstr ""
+"Du hast \"cherry-pick\" nicht abgeschlossen (CHERRY_PICK_HEAD existiert)."
+
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
+msgstr "Du kannst --squash nicht mit --no-ff kombinieren."
+
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
+msgstr "Du kannst --no-ff nicht mit --ff--only kombinieren."
+
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
+msgstr "Keine Version angegeben und merge.defaultToUpstream ist nicht gesetzt."
+
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
+msgstr "Kann nur exakt eine Version in einem leeren Zweig zusammenführen."
+
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
+msgstr "Bin auf einem Zweig, der noch geboren wird; kann nicht quetschen."
+
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
+msgstr "nicht vorzuspulende Version macht in einem leeren Zweig keinen Sinn"
+
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
+msgstr "Aktualisiere %s..%s\n"
+
+#: builtin/merge.c:1451
+#, c-format
+msgid "Trying really trivial in-index merge...\n"
+msgstr "Probiere wirklich triviale \"in-index\"-Zusammenführung...\n"
+
+#: builtin/merge.c:1458
+#, c-format
+msgid "Nope.\n"
+msgstr "Nein.\n"
+
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr "Vorspulen nicht möglich, breche ab."
+
+#: builtin/merge.c:1513 builtin/merge.c:1592
+#, c-format
+msgid "Rewinding the tree to pristine...\n"
+msgstr "Rücklauf des Zweiges bis zum Ursprung...\n"
+
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr "Probiere Zusammenführungsstrategie %s...\n"
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
+msgstr "Keine Zusammenführungsstrategie behandelt diese Zusammenführung.\n"
+
+#: builtin/merge.c:1585
+#, c-format
+msgid "Merge with strategy %s failed.\n"
+msgstr "Zusammenführung mit Strategie %s fehlgeschlagen.\n"
+
+#: builtin/merge.c:1594
+#, c-format
+msgid "Using the %s to prepare resolving by hand.\n"
+msgstr "Benutze \"%s\" um die Auflösung per Hand vorzubereiten.\n"
+
+#: builtin/merge.c:1606
+#, 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"
+
+#: builtin/mv.c:108
+#, c-format
+msgid "Checking rename of '%s' to '%s'\n"
+msgstr "Prüfe Umbenennung von '%s' nach '%s'\n"
+
+#: builtin/mv.c:112
+msgid "bad source"
+msgstr "ungültige Quelle"
+
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
+msgstr "kann Verzeichnis nicht in sich selbst verschieben"
+
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
+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?"
+
+#: builtin/mv.c:140
+msgid "source directory is empty"
+msgstr "Quellverzeichnis ist leer"
+
+#: builtin/mv.c:171
+msgid "not under version control"
+msgstr "nicht unter Versionskontrolle"
+
+#: builtin/mv.c:173
+msgid "destination exists"
+msgstr "Ziel existiert bereits"
+
+#: builtin/mv.c:181
+#, c-format
+msgid "overwriting '%s'"
+msgstr "überschreibe '%s'"
+
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
+msgstr "Kann nicht überschreiben"
+
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
+msgstr "mehrere Quellen für das selbe Ziel"
+
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
+msgstr "%s, Quelle=%s, Ziel=%s"
+
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
+msgstr "Benenne %s nach %s um\n"
+
+#: builtin/mv.c:215
+#, c-format
+msgid "renaming '%s' failed"
+msgstr "Umbenennung von '%s' fehlgeschlagen"
+
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
+msgstr "konnte 'show' für Objekt '%s' nicht starten"
+
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
+msgstr "konnte Datei-Deskriptor für Ausgabe von 'show' nicht öffnen"
+
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
+msgstr "Schließen der Verbindung zu 'show' ist für Objekt '%s' fehlgeschlagen."
+
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
+msgstr "konnte 'show' für Objekt '%s' nicht abschließen"
+
+#: builtin/notes.c:175 builtin/tag.c:343
+#, c-format
+msgid "could not create file '%s'"
+msgstr "konnte Datei '%s' nicht erstellen"
+
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
+msgstr "Bitte liefere den Notiz-Inhalt unter Verwendung der Option -m oder -F."
+
+#: builtin/notes.c:210 builtin/notes.c:973
+#, c-format
+msgid "Removing note for object %s\n"
+msgstr "Entferne Notiz für Objekt %s\n"
+
+#: builtin/notes.c:215
+msgid "unable to write note object"
+msgstr "Konnte Notiz-Objekt nicht schreiben"
+
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
+msgstr "Die Notiz-Inhalte wurden in %s belassen"
+
+#: builtin/notes.c:251 builtin/tag.c:521
+#, c-format
+msgid "cannot read '%s'"
+msgstr "kann '%s' nicht lesen"
+
+#: builtin/notes.c:253 builtin/tag.c:524
+#, c-format
+msgid "could not open or read '%s'"
+msgstr "konnte '%s' nicht öffnen oder lesen"
+
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:537
+#, c-format
+msgid "Failed to resolve '%s' as a valid ref."
+msgstr "Konnte '%s' nicht als gültige Referenz auflösen."
+
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
+msgstr "Fehler beim Lesen des Objektes '%s'."
+
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
+msgstr "Kann uninitialisierten/unreferenzierten Notiz-Baum nicht eintragen."
+
+#: builtin/notes.c:340
+#, c-format
+msgid "Bad notes.rewriteMode value: '%s'"
+msgstr "Ungültiger notes.rewriteMode Wert: '%s'"
+
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+msgstr ""
+"Neuschreiben 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:377
+#, c-format
+msgid "Bad %s value: '%s'"
+msgstr "Ungültiger %s Wert: '%s'"
+
+#: builtin/notes.c:441
+#, c-format
+msgid "Malformed input line: '%s'."
+msgstr "Fehlerhafte Eingabezeile: '%s'."
+
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
+msgstr "Fehler beim Kopieren der Notizen von '%s' nach '%s'"
+
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
+msgstr "zu viele Parameter"
+
+#: builtin/notes.c:513 builtin/notes.c:772
+#, c-format
+msgid "No note found for object %s."
+msgstr "Kein Notiz für Objekt %s gefunden."
+
+#: builtin/notes.c:580
+#, c-format
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Konnte Notizen nicht hinzufügen. Existierende Notizen für Objekt %s "
+"gefunden. Verwende '-f' um die existierenden Notizen zu überschreiben."
+
+#: builtin/notes.c:585 builtin/notes.c:662
+#, c-format
+msgid "Overwriting existing notes for object %s\n"
+msgstr "Überschreibe existierende Notizen für Objekt %s\n"
+
+#: builtin/notes.c:635
+msgid "too few parameters"
+msgstr "zu wenig Parameter"
+
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
+msgstr ""
+"Kann Notizen nicht kopieren. Existierende Notizen für Objekt %s gefunden. "
+"Verwende '-f' um die existierenden Notizen zu überschreiben."
+
+#: builtin/notes.c:668
+#, 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:717
+#, 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 ""
+"Die Optionen -m/-F/-c/-C sind für das Unterkommando 'edit' veraltet.\n"
+"Bitte benutze stattdessen 'git notes add -f -m/-F/-c/-C'.\n"
+
+#: builtin/notes.c:971
+#, c-format
+msgid "Object %s has no note\n"
+msgstr "Objekt %s hat keine Notiz\n"
+
+#: builtin/notes.c:1103
+#, c-format
+msgid "Unknown subcommand: %s"
+msgstr "Unbekanntes Unterkommando: %s"
+
+#: builtin/pack-objects.c:2315
+#, c-format
+msgid "unsupported index version %s"
+msgstr "Nicht unterstützte Bereitstellungsversion %s"
+
+#: builtin/pack-objects.c:2319
+#, c-format
+msgid "bad index version '%s'"
+msgstr "Ungültige Bereitstellungsversion '%s'"
+
+#: builtin/pack-objects.c:2342
+#, c-format
+msgid "option %s does not accept negative form"
+msgstr "Option %s akzeptiert keine negative Form"
+
+#: builtin/pack-objects.c:2346
+#, c-format
+msgid "unable to parse value '%s' for option %s"
+msgstr "konnte Wert '%s' für Option %s nicht parsen"
+
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
+msgstr "Kurzschrift für Markierung ohne <Markierung>"
+
+#: builtin/push.c:64
+msgid "--delete only accepts plain target ref names"
+msgstr "--delete akzeptiert nur reine Referenz-Namen als Ziel"
+
+#: builtin/push.c:84
+#, 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 ""
+"Du befindest dich sich im Moment auf keinem Zweig.\n"
+"Um die Historie, führend zum aktuellen (freistehende Zweigspitze (HEAD))\n"
+"Status zu versenden, benutze\n"
+"\n"
+"    git push %s HEAD:<Name-des-externen-Zweiges>\n"
+
+#: builtin/push.c:91
+#, 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 ""
+"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, benutze\n"
+"\n"
+"    git push --set-upstream %s %s\n"
+
+#: builtin/push.c:99
+#, 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."
+
+#: builtin/push.c:102
+#, 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 ""
+"Du versendest nach '%s', welches kein externes Projektarchiv deines\n"
+"aktuellen Zweiges '%s' ist, ohne mir mitzuteilen, was ich versenden\n"
+"soll, um welchen externen Zweig zu aktualisieren."
+
+#: builtin/push.c:131
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgstr ""
+"Du hast keine Referenzspezifikationen zum Versenden angegeben, und "
+"push.default ist \"nothing\"."
+
+#: builtin/push.c:138
+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 ""
+"Aktualisierungen wurden zurückgewiesen, weil die Spitze deines aktuellen\n"
+"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Führe die\n"
+"externen Änderungen zusammen (z.B. 'git pull') bevor du erneut versendest.\n"
+"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help' für\n"
+"weitere Details."
+
+#: builtin/push.c:144
+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\n"
+"variable to '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 du nicht\n"
+"beabsichtigt hast, diesen Zweig zu versenden, kannst du auch den zu versendenden\n"
+"Zweig spezifizieren oder die Konfigurationsvariable 'push.default' zu 'current'\n"
+"oder 'upstream' setzen, um nur den aktuellen Zweig zu versenden."
+
+#: builtin/push.c:150
+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 ""
+"Aktualisierungen wurden zurückgewiesen, weil die Spitze eines versendeten\n"
+"Zweiges hinter seinem externen Gegenstück zurückgefallen ist. Checke diesen\n"
+"Zweig aus und führe die externen Änderungen zusammen (z.B. 'git pull')\n"
+"bevor du erneut versendest.\n"
+"Siehe auch die Sektion 'Note about fast-forwards' in 'git push --help'\n"
+"für weitere Details."
+
+#: builtin/push.c:190
+#, c-format
+msgid "Pushing to %s\n"
+msgstr "Versende nach %s\n"
+
+#: builtin/push.c:194
+#, c-format
+msgid "failed to push some refs to '%s'"
+msgstr "Fehler beim Versenden einiger Referenzen nach '%s'"
+
+#: builtin/push.c:226
+#, c-format
+msgid "bad repository '%s'"
+msgstr "ungültiges Projektarchiv '%s'"
+
+#: builtin/push.c:227
+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 ""
+"Kein Ziel zum Versenden konfiguriert.\n"
+"Entweder spezifizierst du die URL von der Kommandozeile oder konfigurierst "
+"ein externes Projektarchiv unter Benutzung von\n"
+"\n"
+"    git remote add <Name> <URL>\n"
+"\n"
+"und versendest dann unter Benutzung dieses Namens\n"
+"\n"
+"    git push <Name>\n"
+
+#: builtin/push.c:242
+msgid "--all and --tags are incompatible"
+msgstr "--all und --tags sind inkompatibel"
+
+#: builtin/push.c:243
+msgid "--all can't be combined with refspecs"
+msgstr "--all kann nicht mit Referenzspezifikationen kombiniert werden"
+
+#: builtin/push.c:248
+msgid "--mirror and --tags are incompatible"
+msgstr "--mirror und --tags sind inkompatibel"
+
+#: builtin/push.c:249
+msgid "--mirror can't be combined with refspecs"
+msgstr "--mirror kann nicht mit Referenzspezifikationen kombiniert werden"
+
+#: builtin/push.c:254
+msgid "--all and --mirror are incompatible"
+msgstr "--all und --mirror sind inkompatibel"
+
+#: builtin/push.c:334
+msgid "--delete is incompatible with --all, --mirror and --tags"
+msgstr "--delete ist inkompatibel mit --all, --mirror und --tags"
+
+#: builtin/push.c:336
+msgid "--delete doesn't make sense without any refs"
+msgstr "--delete macht ohne irgendeine Referenz keinen Sinn"
+
+#: 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 "keep"
+msgstr "keep"
+
+#: builtin/reset.c:77
+msgid "You do not have a valid HEAD."
+msgstr "Du hast keine gültige Zweigspitze (HEAD)."
+
+#: builtin/reset.c:79
+msgid "Failed to find tree of HEAD."
+msgstr "Fehler beim Finden des Baumes der Zweigspitze (HEAD)."
+
+#: builtin/reset.c:85
+#, c-format
+msgid "Failed to find tree of %s."
+msgstr "Fehler beim Finden des Baumes von %s."
+
+#: builtin/reset.c:96
+msgid "Could not write new index file."
+msgstr "Konnte neue Bereitstellungsdatei nicht schreiben."
+
+#: builtin/reset.c:106
+#, c-format
+msgid "HEAD is now at %s"
+msgstr "Zweigspitze (HEAD) ist jetzt bei %s"
+
+#: builtin/reset.c:130
+msgid "Could not read index"
+msgstr "Konnte Bereitstellung nicht lesen"
+
+#: builtin/reset.c:133
+msgid "Unstaged changes after reset:"
+msgstr "Nicht bereitgestellte Änderungen nach Zurücksetzung:"
+
+#: builtin/reset.c:223
+#, 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."
+
+#: builtin/reset.c:297
+#, c-format
+msgid "Could not parse object '%s'."
+msgstr "Konnte Objekt '%s' nicht parsen."
+
+#: builtin/reset.c:302
+msgid "--patch is incompatible with --{hard,mixed,soft}"
+msgstr "--patch ist inkompatibel mit --{hard,mixed,soft}"
+
+#: builtin/reset.c:311
+msgid "--mixed with paths is deprecated; use 'git reset -- <paths>' instead."
+msgstr ""
+"--mixed mit Pfaden ist veraltet; benutze stattdessen 'git reset -- <Pfade>'."
+
+#: builtin/reset.c:313
+#, c-format
+msgid "Cannot do %s reset with paths."
+msgstr "Eine '%s' Zurücksetzung mit Pfaden ist nicht möglich."
+
+#: builtin/reset.c:325
+#, c-format
+msgid "%s reset is not allowed in a bare repository"
+msgstr "'%s' Zurücksetzung ist in einem bloßen Projektarchiv nicht erlaubt"
+
+#: builtin/reset.c:341
+#, c-format
+msgid "Could not reset index file to revision '%s'."
+msgstr "Konnte Bereitstellungsdatei nicht zu Version '%s' zurücksetzen."
+
+#: builtin/revert.c:70 builtin/revert.c:92
+#, c-format
+msgid "%s: %s cannot be used with %s"
+msgstr "%s: %s kann nicht mit %s benutzt werden"
+
+#: builtin/revert.c:127
+msgid "program error"
+msgstr "Programmfehler"
+
+#: builtin/revert.c:213
+msgid "revert failed"
+msgstr "\"revert\" fehlgeschlagen"
+
+#: builtin/revert.c:228
+msgid "cherry-pick failed"
+msgstr "\"cherry-pick\" fehlgeschlagen"
+
+#: builtin/rm.c:109
+#, c-format
+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) (benutze -f um die Entfernung zu erzwingen)"
+
+#: builtin/rm.c:115
+#, c-format
+msgid ""
+"'%s' has changes staged in the index\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' hat Änderungen in der Bereitstellung\n"
+"(benutze --cached um die Datei zu behalten, oder -f um die Entfernung zu "
+"erzwingen)"
+
+#: builtin/rm.c:119
+#, c-format
+msgid ""
+"'%s' has local modifications\n"
+"(use --cached to keep the file, or -f to force removal)"
+msgstr ""
+"'%s' hat lokale Modifikationen\n"
+"(benutze --cached um die Datei zu behalten, oder -f um die Entfernung zu "
+"erzwingen)"
+
+#: builtin/rm.c:194
+#, c-format
+msgid "not removing '%s' recursively without -r"
+msgstr "'%s' wird nicht ohne -r rekursiv entfernt"
+
+#: builtin/rm.c:230
+#, c-format
+msgid "git rm: unable to remove %s"
+msgstr "git rm: konnte %s nicht entfernen"
+
+#: builtin/shortlog.c:157
+#, c-format
+msgid "Missing author: %s"
+msgstr "fehlender Autor: %s"
+
+#: builtin/tag.c:58
+#, c-format
+msgid "malformed object at '%s'"
+msgstr "fehlerhaftes Objekt bei '%s'"
+
+#: builtin/tag.c:205
+#, c-format
+msgid "tag name too long: %.*s..."
+msgstr "Markierungsname zu lang: %.*s..."
+
+#: builtin/tag.c:210
+#, c-format
+msgid "tag '%s' not found."
+msgstr "Markierung '%s' nicht gefunden."
+
+#: builtin/tag.c:225
+#, c-format
+msgid "Deleted tag '%s' (was %s)\n"
+msgstr "Gelöschte Markierung '%s' (war %s)\n"
+
+#: builtin/tag.c:237
+#, c-format
+msgid "could not verify the tag '%s'"
+msgstr "Konnte Markierung '%s' nicht verifizieren"
+
+#: builtin/tag.c:247
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be ignored.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Gebe eine Markierungsbeschreibung ein\n"
+"# Zeilen, die mit '#' beginnen, werden ignoriert.\n"
+"#\n"
+
+#: builtin/tag.c:254
+msgid ""
+"\n"
+"#\n"
+"# Write a tag message\n"
+"# Lines starting with '#' will be kept; you may remove them yourself if you "
+"want to.\n"
+"#\n"
+msgstr ""
+"\n"
+"#\n"
+"# Gebe eine Markierungsbeschreibung ein\n"
+"# Zeilen, die mit '#' beginnen, werden behalten; du darfst diese\n"
+"# selbst entfernen wenn du möchtest.\n"
+"#\n"
+
+#: builtin/tag.c:294
+msgid "unable to sign the tag"
+msgstr "konnte Markierung nicht signieren"
+
+#: builtin/tag.c:296
+msgid "unable to write tag file"
+msgstr "konnte Markierungsdatei nicht schreiben"
+
+#: builtin/tag.c:321
+msgid "bad object type."
+msgstr "ungültiger Objekt-Typ"
+
+#: builtin/tag.c:334
+msgid "tag header too big."
+msgstr "Markierungskopf zu groß."
+
+#: builtin/tag.c:366
+msgid "no tag message?"
+msgstr "keine Markierungsbeschreibung?"
+
+#: builtin/tag.c:372
+#, c-format
+msgid "The tag message has been left in %s\n"
+msgstr "Die Markierungsbeschreibung wurde gelassen in %s\n"
+
+#: builtin/tag.c:421
+msgid "switch 'points-at' requires an object"
+msgstr "Option 'points-at' erfordert ein Objekt"
+
+#: builtin/tag.c:423
+#, c-format
+msgid "malformed object name '%s'"
+msgstr "fehlerhafter Objekt-Name '%s'"
+
+#: builtin/tag.c:502
+msgid "-n option is only allowed with -l."
+msgstr "-n Option ist nur erlaubt mit -l."
+
+#: builtin/tag.c:504
+msgid "--contains option is only allowed with -l."
+msgstr "--contains Option ist nur erlaubt mit -l."
+
+#: builtin/tag.c:506
+msgid "--points-at option is only allowed with -l."
+msgstr "--points-at Option ist nur erlaubt mit -l."
+
+#: builtin/tag.c:514
+msgid "only one -F or -m option is allowed."
+msgstr "nur eine -F oder -m Option ist erlaubt."
+
+#: builtin/tag.c:534
+msgid "too many params"
+msgstr "zu viele Parameter"
+
+#: builtin/tag.c:540
+#, c-format
+msgid "'%s' is not a valid tag name."
+msgstr "'%s' ist kein gültiger Markierungsname."
+
+#: builtin/tag.c:545
+#, c-format
+msgid "tag '%s' already exists"
+msgstr "Markierung '%s' existiert bereits"
+
+#: builtin/tag.c:563
+#, c-format
+msgid "%s: cannot lock the ref"
+msgstr "%s: kann Referenz nicht sperren"
+
+#: builtin/tag.c:565
+#, c-format
+msgid "%s: cannot update the ref"
+msgstr "%s: kann Referenz nicht aktualisieren"
+
+#: builtin/tag.c:567
+#, c-format
+msgid "Updated tag '%s' (was %s)\n"
+msgstr "Aktualisierte Markierung '%s' (war %s)\n"
+
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "Du musst zuerst die Informationen des Eintragenden setzen."
+
+#: 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 "
+"zurückzufallen."
+
+#: git-am.sh:154
+msgid ""
+"Did you hand edit your patch?\n"
+"It does not apply to blobs recorded in its index."
+msgstr ""
+"Hast du den Patch per Hand editiert?\n"
+"Er kann nicht auf die Blobs in seiner 'index' Zeile angewendet werden."
+
+#: 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..."
+
+#: git-am.sh:275
+msgid "Only one StGIT patch series can be applied at once"
+msgstr "Es kann nur eine StGIT Patch-Serie auf einmal angewendet werden."
+
+#: git-am.sh:362
+#, sh-format
+msgid "Patch format $patch_format is not supported."
+msgstr "Patch-Format $patch_format wird nicht unterstützt."
+
+#: git-am.sh:364
+msgid "Patch format detection failed."
+msgstr "Patch-Formaterkennung fehlgeschlagen."
+
+#: git-am.sh:418
+msgid "-d option is no longer supported.  Do not use."
+msgstr "-d Option wird nicht länger unterstützt. Nicht benutzen."
+
+#: git-am.sh:481
+#, sh-format
+msgid "previous rebase directory $dotest still exists but mbox given."
+msgstr ""
+"Vorheriges Verzeichnis des Neuaufbaus $dotest existiert noch, aber mbox "
+"gegeben."
+
+#: git-am.sh:486
+msgid "Please make up your mind. --skip or --abort?"
+msgstr "Bitte werde dir klar. --skip oder --abort?"
+
+#: git-am.sh:513
+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:579
+#, sh-format
+msgid "Dirty index: cannot apply patches (dirty: $files)"
+msgstr ""
+"Unsaubere Bereitstellung: kann Patches nicht anwenden (unsauber: $files)"
+
+#: git-am.sh:755
+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."
+
+#. 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:766
+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:802
+#, sh-format
+msgid "Applying: $FIRSTLINE"
+msgstr "Wende an: $FIRSTLINE"
+
+#: git-am.sh:847
+msgid "No changes -- Patch already applied."
+msgstr "Keine Änderungen -- Patches bereits angewendet."
+
+#: git-am.sh:873
+msgid "applying to an empty history"
+msgstr "wende zu leerer Historie an"
+
+#. 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 "Willst du, dass ich es für dich mache [Y/n]? "
+
+#: git-bisect.sh:95
+#, sh-format
+msgid "unrecognised option: '$arg'"
+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 Version zu sein"
+
+#: git-bisect.sh:117
+msgid "Bad HEAD - I need a HEAD"
+msgstr "Ungültige Zweigspitze (HEAD) - Zweigspitze (HEAD) wird benötigt"
+
+#: git-bisect.sh:130
+#, sh-format
+msgid ""
+"Checking out '$start_head' failed. Try 'git bisect reset <validbranch>'."
+msgstr ""
+"Auschecken von '$start_head' fehlgeschlagen. Versuche 'git bisect reset "
+"<gueltigerzweig>'."
+
+#: git-bisect.sh:140
+msgid "won't bisect on seeked tree"
+msgstr "\"bisect\" auf gesuchtem Zweig nicht möglich"
+
+#: git-bisect.sh:144
+msgid "Bad HEAD - strange symbolic ref"
+msgstr "Ungültige Zweigspitze (HEAD) - merkwürdige symbolische Referenz"
+
+#: git-bisect.sh:189
+#, sh-format
+msgid "Bad bisect_write argument: $state"
+msgstr "Ungültiges \"bisect_write\" Argument: $state"
+
+#: git-bisect.sh:218
+#, sh-format
+msgid "Bad rev input: $arg"
+msgstr "Ungültige Referenz-Eingabe: $arg"
+
+#: git-bisect.sh:232
+msgid "Please call 'bisect_state' with at least one argument."
+msgstr "Bitte rufe 'bisect_state' mit mindestens einem Argument auf."
+
+#: git-bisect.sh:244
+#, sh-format
+msgid "Bad rev input: $rev"
+msgstr "Ungültige Referenz-Eingabe: $rev"
+
+#: git-bisect.sh:250
+msgid "'git bisect bad' can take only one argument."
+msgstr "'git bisect bad' kann nur ein Argument entgegennehmen."
+
+#. 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 "Bist du sicher [Y/n]? "
+
+#: git-bisect.sh:354
+#, sh-format
+msgid "'$invalid' is not a valid commit"
+msgstr "'$invalid' ist keine gültige Version"
+
+#: git-bisect.sh:363
+#, 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"
+"Versuche 'git bisect reset <Version>'."
+
+#: git-bisect.sh:390
+msgid "No logfile given"
+msgstr "Keine Log-Datei gegeben"
+
+#: git-bisect.sh:391
+#, sh-format
+msgid "cannot read $file for replaying"
+msgstr "kann $file nicht für das Abspielen lesen"
+
+#: git-bisect.sh:408
+msgid "?? what are you talking about?"
+msgstr "?? Was redest du da?"
+
+#: git-bisect.sh:474
+msgid "We are not bisecting."
+msgstr "Wir sind nicht beim Halbieren."
+
+#: 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 ""
+"\"pull\" ist nicht möglich, weil du nicht zusammengeführte Dateien hast.\n"
+"Bitte korrigiere dies im Arbeitsbaum und benutze dann 'git add/rm <Datei>'\n"
+"um die Auflösung entsprechend zu markieren, oder benutze 'git commit -a'."
+
+#: git-pull.sh:25
+msgid "Pull is not possible because you have unmerged files."
+msgstr ""
+"\"pull\" ist nicht möglich, weil du nicht zusammengeführte Dateien hast."
+
+#: git-pull.sh:197
+msgid "updating an unborn branch with changes added to the index"
+msgstr ""
+"Aktualisiere eine ungeborenen Zweig mit Änderungen, die zur Bereitstellung "
+"hinzugefügt wurden"
+
+#: git-pull.sh:253
+msgid "Cannot merge multiple branches into empty head"
+msgstr "Kann nicht mehrere Zweige in einen ungeborenen Zweig zusammenführen"
+
+#: git-pull.sh:257
+msgid "Cannot rebase onto multiple branches"
+msgstr "kann nicht auf mehrere Zweige neu aufbauen"
+
+#: git-stash.sh:51
+msgid "git stash clear with parameters is unimplemented"
+msgstr "git stash clear mit Parametern ist nicht implementiert"
+
+#: git-stash.sh:74
+msgid "You do not have the initial commit yet"
+msgstr "Du hast bisher noch keine initiale Version"
+
+#: git-stash.sh:89
+msgid "Cannot save the current index state"
+msgstr "Kann den aktuellen Zustand der Bereitstellung 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"
+
+#: git-stash.sh:140
+msgid "No changes selected"
+msgstr "Keine Änderungen ausgewählt"
+
+#: git-stash.sh:143
+msgid "Cannot remove temporary index (can't happen)"
+msgstr "Kann temporäre Bereitstellung nicht entfernen (kann nicht passieren)"
+
+#: git-stash.sh:156
+msgid "Cannot record working tree state"
+msgstr "Kann Zustand des Arbeitsbaumes nicht aufzeichnen"
+
+#: git-stash.sh:223
+msgid "No local changes to save"
+msgstr "Keine lokalen Änderungen zum Speichern"
+
+#: git-stash.sh:227
+msgid "Cannot initialize stash"
+msgstr "Kann \"stash\" nicht initialisieren"
+
+#: git-stash.sh:235
+msgid "Cannot save the current status"
+msgstr "Kann den aktuellen Status nicht speichern"
+
+#: git-stash.sh:253
+msgid "Cannot remove worktree changes"
+msgstr "Kann Änderungen am Arbeitsbaum nicht entfernen"
+
+#: git-stash.sh:352
+msgid "No stash found."
+msgstr "Kein \"stash\" gefunden."
+
+#: git-stash.sh:359
+#, sh-format
+msgid "Too many revisions specified: $REV"
+msgstr "Zu viele Revisionen angegeben: $REV"
+
+#: git-stash.sh:365
+#, sh-format
+msgid "$reference is not valid reference"
+msgstr "$reference ist keine gültige Referenz"
+
+#: git-stash.sh:393
+#, sh-format
+msgid "'$args' is not a stash-like commit"
+msgstr "'$args' ist keine \"stash\"-artige Version"
+
+#: git-stash.sh:404
+#, sh-format
+msgid "'$args' is not a stash reference"
+msgstr "'$args' ist keine \"stash\"-Referenz"
+
+#: git-stash.sh:412
+msgid "unable to refresh index"
+msgstr "unfähig die Bereitstellung zu aktualisieren"
+
+#: git-stash.sh:416
+msgid "Cannot apply a stash in the middle of a merge"
+msgstr ""
+"Kann \"stash\" nicht anwenden, solang eine Zusammenführung im Gange ist"
+
+#: git-stash.sh:424
+msgid "Conflicts in index. Try without --index."
+msgstr "Konflikte in der Bereitstellung. Versuche es ohne --index."
+
+#: git-stash.sh:426
+msgid "Could not save index tree"
+msgstr "Konnte Bereitstellungsbaum nicht speichern"
+
+#: git-stash.sh:460
+msgid "Cannot unstage modified files"
+msgstr "Kann geänderte Dateien nicht aus der Bereitstellung herausnehmen"
+
+#: git-stash.sh:491
+#, sh-format
+msgid "Dropped ${REV} ($s)"
+msgstr "Gelöscht ${REV} ($s)"
+
+#: git-stash.sh:492
+#, sh-format
+msgid "${REV}: Could not drop stash entry"
+msgstr "${REV}: Konnte \"stash\"-Eintrag nicht löschen"
+
+#: git-stash.sh:499
+msgid "No branch name specified"
+msgstr "Kein Zweigname spezifiziert"
+
+#: git-stash.sh:570
+msgid "(To restore them type \"git stash apply\")"
+msgstr "(Zur Wiederherstellung gebe \"git stash apply\" ein)"
+
+#: git-submodule.sh:56
+#, sh-format
+msgid "cannot strip one component off url '$remoteurl'"
+msgstr "Kann eine Komponente von URL '$remoteurl' nicht extrahieren"
+
+#: git-submodule.sh:109
+#, 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"
+
+#: git-submodule.sh:150
+#, sh-format
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr "Klonen von '$url' in Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:160
+#, sh-format
+msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
+msgstr ""
+"Git-Verzeichnis '$a' ist Teil des Unterprojekt-Pfades '$b', oder umgekehrt"
+
+#: git-submodule.sh:248
+#, 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:265
+#, sh-format
+msgid "'$path' already exists in the index"
+msgstr "'$path' existiert bereits in der Bereitstellung"
+
+#: git-submodule.sh:282
+#, 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"
+
+#: git-submodule.sh:296
+#, sh-format
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr "Unfähig Unterprojekt '$sm_path' auszuchecken"
+
+#: git-submodule.sh:301
+#, sh-format
+msgid "Failed to add submodule '$sm_path'"
+msgstr "Hinzufügen von Unterprojekt '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:306
+#, sh-format
+msgid "Failed to register submodule '$sm_path'"
+msgstr "Registierung von Unterprojekt '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:348
+#, sh-format
+msgid "Entering '$prefix$sm_path'"
+msgstr "Betrete '$prefix$sm_path'"
+
+#: git-submodule.sh:360
+#, sh-format
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr "Stoppe bei '$sm_path'; Skript gab nicht-Null Status zurück."
+
+#: git-submodule.sh:402
+#, 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"
+
+#: git-submodule.sh:411
+#, sh-format
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr "Registrierung der URL für Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:419
+#, sh-format
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr "Registrierung des Aktualisierungsmodus für Unterprojekt-Pfad "
+"'$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:421
+#, sh-format
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr "Unterprojekt '$name' ($url) ist für Pfad '$sm_path' registriert"
+
+#: git-submodule.sh:520
+#, sh-format
+msgid ""
+"Submodule path '$sm_path' not initialized\n"
+"Maybe you want to use 'update --init'?"
+msgstr ""
+"Unterprojekt-Pfad '$sm_path' ist nicht initialisiert\n"
+"Vielleicht möchtest du 'update --init' benutzen?"
+
+#: git-submodule.sh:533
+#, sh-format
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr "Konnte aktuelle Version in Unterprojekt-Pfad '$sm_path' nicht finden"
+
+#: git-submodule.sh:552
+#, sh-format
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr "Konnte in Unterprojekt-Pfad '$sm_path' nicht anfordern"
+
+#: git-submodule.sh:566
+#, sh-format
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr "Neuaufbau von '$sha1' in Unterprojekt-Pfad '$sm_path' nicht möglich"
+
+#: git-submodule.sh:567
+#, sh-format
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr "Unterprojekt-Pfad '$sm_path': neu aufgebaut in '$sha1'"
+
+#: git-submodule.sh:572
+#, sh-format
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr ""
+"Zusammenführung von '$sha1' in Unterprojekt-Pfad '$sm_path' fehlgeschlagen"
+
+#: git-submodule.sh:573
+#, sh-format
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr "Unterprojekt-Pfad '$sm_path': zusammengeführt in '$sha1'"
+
+#: git-submodule.sh:578
+#, sh-format
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr "Konnte '$sha1' in Unterprojekt-Pfad '$sm_path' nicht auschecken."
+
+#: git-submodule.sh:579
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr "Unterprojekt-Pfad: '$sm_path': '$sha1' ausgecheckt"
+
+#: git-submodule.sh:601 git-submodule.sh:924
+#, sh-format
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "Fehler bei Rekursion in Unterprojekt-Pfad '$sm_path'"
+
+#: git-submodule.sh:709
+msgid "--"
+msgstr "--"
+
+#: git-submodule.sh:767
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_src"
+msgstr "  Warnung: $name beinhaltet nicht Version $sha1_src"
+
+#: git-submodule.sh:770
+#, sh-format
+msgid "  Warn: $name doesn't contain commit $sha1_dst"
+msgstr "  Warnung: $name beinhaltet nicht Version $sha1_dst"
+
+#: git-submodule.sh:773
+#, sh-format
+msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+msgstr ""
+"  Warnung: $name beinhaltet nicht die Versionen $sha1_src und $sha1_dst"
+
+#: git-submodule.sh:798
+msgid "blob"
+msgstr "Blob"
+
+#: git-submodule.sh:799
+msgid "submodule"
+msgstr "Unterprojekt"
+
+#: git-submodule.sh:970
+#, sh-format
+msgid "Synchronizing submodule url for '$name'"
+msgstr "Synchronisiere Unterprojekt-URL für '$name'"
+
+#~ msgid "Too many options specified"
+#~ msgstr "Zu viele Optionen angegeben"
index 566c7fdda64a21178ed364f24199f67eaf3bf8ff..decd3d59e569cfbba98a4cb1984108125f691478 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: 2012-03-16 20:18+0800\n"
+"POT-Creation-Date: 2012-05-15 06:42+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:34
+#: advice.c:40
 #, 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:64
+#: advice.c:70
 msgid ""
 "Fix them up in the work tree,\n"
 "and then use 'git add/rm <file>' as\n"
@@ -35,12 +35,86 @@ msgid ""
 "or use 'git commit -a'."
 msgstr ""
 
-#: commit.c:47
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr ""
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr ""
+
+#: bundle.c:89 builtin/commit.c:697
+#, c-format
+msgid "could not open '%s'"
+msgstr ""
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr ""
+
+#: bundle.c:164 sequencer.c:533 sequencer.c:965 builtin/log.c:289
+#: builtin/log.c:719 builtin/log.c:1335 builtin/log.c:1554 builtin/merge.c:347
+#: builtin/shortlog.c:181
+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] ""
+msgstr[1] ""
+
+#: bundle.c:192
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bundle.c:290
+msgid "rev-list died"
+msgstr ""
+
+#: bundle.c:296 builtin/log.c:1231 builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr ""
+
+#: bundle.c:331
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr ""
+
+#: bundle.c:376
+msgid "Refusing to create empty bundle."
+msgstr ""
+
+#: bundle.c:394
+msgid "Could not spawn pack-objects"
+msgstr ""
+
+#: bundle.c:412
+msgid "pack-objects died"
+msgstr ""
+
+#: bundle.c:415
+#, c-format
+msgid "cannot create '%s'"
+msgstr ""
+
+#: bundle.c:437
+msgid "index-pack died"
+msgstr ""
+
+#: commit.c:48
 #, c-format
 msgid "could not parse %s"
 msgstr ""
 
-#: commit.c:49
+#: commit.c:50
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr ""
@@ -63,6 +137,73 @@ msgstr ""
 msgid "failed to close rev-list's stdin: %s"
 msgstr ""
 
+#: date.c:95
+msgid "in the future"
+msgstr ""
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:154 date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] ""
+msgstr[1] ""
+
 #: diff.c:105
 #, c-format
 msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
@@ -80,32 +221,32 @@ msgid ""
 "%s"
 msgstr ""
 
-#: diff.c:1336
+#: diff.c:1400
 msgid " 0 files changed\n"
 msgstr ""
 
-#: diff.c:1340
+#: diff.c:1404
 #, c-format
 msgid " %d file changed"
 msgid_plural " %d files changed"
 msgstr[0] ""
 msgstr[1] ""
 
-#: diff.c:1357
+#: diff.c:1421
 #, c-format
 msgid ", %d insertion(+)"
 msgid_plural ", %d insertions(+)"
 msgstr[0] ""
 msgstr[1] ""
 
-#: diff.c:1368
+#: diff.c:1432
 #, c-format
 msgid ", %d deletion(-)"
 msgid_plural ", %d deletions(-)"
 msgstr[0] ""
 msgstr[1] ""
 
-#: diff.c:3424
+#: diff.c:3478
 #, c-format
 msgid ""
 "Failed to parse --dirstat/-X option parameter:\n"
@@ -139,13 +280,53 @@ msgstr ""
 msgid "'%s': short read %s"
 msgstr ""
 
-#: help.c:287
+#: help.c:207
+#, c-format
+msgid "available git commands in '%s'"
+msgstr ""
+
+#: help.c:214
+msgid "git commands available from elsewhere on your $PATH"
+msgstr ""
+
+#: help.c:270
 #, 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:327
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr ""
+
+#: help.c:349
+#, 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:354
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr ""
+
+#: help.c:361
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr ""
+
+#: help.c:365
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+msgstr[1] ""
+
 #: remote.c:1607
 #, c-format
 msgid "Your branch is ahead of '%s' by %d commit.\n"
@@ -172,456 +353,487 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: sequencer.c:120 builtin/merge.c:864 builtin/merge.c:985
-#: builtin/merge.c:1095 builtin/merge.c:1105
+#: sequencer.c:121 builtin/merge.c:865 builtin/merge.c:978
+#: builtin/merge.c:1088 builtin/merge.c:1098
 #, c-format
 msgid "Could not open '%s' for writing"
 msgstr ""
 
-#: sequencer.c:122 builtin/merge.c:334 builtin/merge.c:867
-#: builtin/merge.c:1097 builtin/merge.c:1110
+#: sequencer.c:123 builtin/merge.c:333 builtin/merge.c:868
+#: builtin/merge.c:1090 builtin/merge.c:1103
 #, c-format
 msgid "Could not write to '%s'"
 msgstr ""
 
-#: sequencer.c:143
+#: sequencer.c:144
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'"
 msgstr ""
 
-#: sequencer.c:146
+#: sequencer.c:147
 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:159 sequencer.c:685 sequencer.c:768
+#: sequencer.c:160 sequencer.c:741 sequencer.c:824
 #, c-format
 msgid "Could not write to %s"
 msgstr ""
 
-#: sequencer.c:162
+#: sequencer.c:163
 #, c-format
 msgid "Error wrapping up %s"
 msgstr ""
 
-#: sequencer.c:177
+#: sequencer.c:178
 msgid "Your local changes would be overwritten by cherry-pick."
 msgstr ""
 
-#: sequencer.c:179
+#: sequencer.c:180
 msgid "Your local changes would be overwritten by revert."
 msgstr ""
 
-#: sequencer.c:182
+#: sequencer.c:183
 msgid "Commit your changes or stash them to proceed."
 msgstr ""
 
 #. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:232
+#: sequencer.c:233
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr ""
 
-#: sequencer.c:298
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
+msgstr ""
+
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
+msgstr ""
+
+#: sequencer.c:323
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr ""
+
+#: sequencer.c:328
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr ""
+
+#: sequencer.c:358
 msgid "Your index file is unmerged."
 msgstr ""
 
-#: sequencer.c:301
+#: sequencer.c:361
 msgid "You do not have a valid HEAD"
 msgstr ""
 
-#: sequencer.c:316
+#: sequencer.c:376
 #, c-format
 msgid "Commit %s is a merge but no -m option was given."
 msgstr ""
 
-#: sequencer.c:324
+#: sequencer.c:384
 #, c-format
 msgid "Commit %s does not have parent %d"
 msgstr ""
 
-#: sequencer.c:328
+#: sequencer.c:388
 #, 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:339
+#: sequencer.c:399
 #, c-format
 msgid "%s: cannot parse parent commit %s"
 msgstr ""
 
-#: sequencer.c:343
+#: sequencer.c:403
 #, c-format
 msgid "Cannot get commit message for %s"
 msgstr ""
 
-#: sequencer.c:427
+#: sequencer.c:491
 #, c-format
 msgid "could not revert %s... %s"
 msgstr ""
 
-#: sequencer.c:428
+#: sequencer.c:492
 #, c-format
 msgid "could not apply %s... %s"
 msgstr ""
 
-#: sequencer.c:450 sequencer.c:909 builtin/log.c:288 builtin/log.c:713
-#: builtin/log.c:1329 builtin/log.c:1548 builtin/merge.c:348
-#: builtin/shortlog.c:181
-msgid "revision walk setup failed"
-msgstr ""
-
-#: sequencer.c:453
+#: sequencer.c:536
 msgid "empty commit set passed"
 msgstr ""
 
-#: sequencer.c:461
+#: sequencer.c:544
 #, c-format
 msgid "git %s: failed to read the index"
 msgstr ""
 
-#: sequencer.c:466
+#: sequencer.c:549
 #, c-format
 msgid "git %s: failed to refresh the index"
 msgstr ""
 
-#: sequencer.c:551
+#: sequencer.c:607
 #, c-format
 msgid "Cannot %s during a %s"
 msgstr ""
 
-#: sequencer.c:573
+#: sequencer.c:629
 #, c-format
 msgid "Could not parse line %d."
 msgstr ""
 
-#: sequencer.c:578
+#: sequencer.c:634
 msgid "No commits parsed."
 msgstr ""
 
-#: sequencer.c:591
+#: sequencer.c:647
 #, c-format
 msgid "Could not open %s"
 msgstr ""
 
-#: sequencer.c:595
+#: sequencer.c:651
 #, c-format
 msgid "Could not read %s."
 msgstr ""
 
-#: sequencer.c:602
+#: sequencer.c:658
 #, c-format
 msgid "Unusable instruction sheet: %s"
 msgstr ""
 
-#: sequencer.c:630
+#: sequencer.c:686
 #, c-format
 msgid "Invalid key: %s"
 msgstr ""
 
-#: sequencer.c:633
+#: sequencer.c:689
 #, c-format
 msgid "Invalid value for %s: %s"
 msgstr ""
 
-#: sequencer.c:645
+#: sequencer.c:701
 #, c-format
 msgid "Malformed options sheet: %s"
 msgstr ""
 
-#: sequencer.c:666
+#: sequencer.c:722
 msgid "a cherry-pick or revert is already in progress"
 msgstr ""
 
-#: sequencer.c:667
+#: sequencer.c:723
 msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
 msgstr ""
 
-#: sequencer.c:671
+#: sequencer.c:727
 #, c-format
 msgid "Could not create sequencer directory %s"
 msgstr ""
 
-#: sequencer.c:687 sequencer.c:772
+#: sequencer.c:743 sequencer.c:828
 #, c-format
 msgid "Error wrapping up %s."
 msgstr ""
 
-#: sequencer.c:706 sequencer.c:840
+#: sequencer.c:762 sequencer.c:896
 msgid "no cherry-pick or revert in progress"
 msgstr ""
 
-#: sequencer.c:708
+#: sequencer.c:764
 msgid "cannot resolve HEAD"
 msgstr ""
 
-#: sequencer.c:710
+#: sequencer.c:766
 msgid "cannot abort from a branch yet to be born"
 msgstr ""
 
-#: sequencer.c:732
+#: sequencer.c:788 builtin/apply.c:3689
 #, c-format
 msgid "cannot open %s: %s"
 msgstr ""
 
-#: sequencer.c:735
+#: sequencer.c:791
 #, c-format
 msgid "cannot read %s: %s"
 msgstr ""
 
-#: sequencer.c:736
+#: sequencer.c:792
 msgid "unexpected end of file"
 msgstr ""
 
-#: sequencer.c:742
+#: sequencer.c:798
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
 msgstr ""
 
-#: sequencer.c:765
+#: sequencer.c:821
 #, c-format
 msgid "Could not format %s."
 msgstr ""
 
-#: sequencer.c:927
+#: sequencer.c:983
 msgid "Can't revert as initial commit"
 msgstr ""
 
-#: sequencer.c:928
+#: sequencer.c:984
 msgid "Can't cherry-pick into empty head"
 msgstr ""
 
-#: wt-status.c:134
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr ""
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr ""
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr ""
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr ""
+
+#: wt-status.c:135
 msgid "Unmerged paths:"
 msgstr ""
 
-#: wt-status.c:140 wt-status.c:157
+#: wt-status.c:141 wt-status.c:158
 #, c-format
 msgid "  (use \"git reset %s <file>...\" to unstage)"
 msgstr ""
 
-#: wt-status.c:142 wt-status.c:159
+#: wt-status.c:143 wt-status.c:160
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
 msgstr ""
 
-#: wt-status.c:143
+#: wt-status.c:144
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
 msgstr ""
 
-#: wt-status.c:151
+#: wt-status.c:152
 msgid "Changes to be committed:"
 msgstr ""
 
-#: wt-status.c:169
+#: wt-status.c:170
 msgid "Changes not staged for commit:"
 msgstr ""
 
-#: wt-status.c:173
+#: wt-status.c:174
 msgid "  (use \"git add <file>...\" to update what will be committed)"
 msgstr ""
 
-#: wt-status.c:175
+#: wt-status.c:176
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
 msgstr ""
 
-#: wt-status.c:176
+#: wt-status.c:177
 msgid ""
 "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
 msgstr ""
 
-#: wt-status.c:178
+#: wt-status.c:179
 msgid "  (commit or discard the untracked or modified content in submodules)"
 msgstr ""
 
-#: wt-status.c:187
+#: wt-status.c:188
 #, c-format
 msgid "%s files:"
 msgstr ""
 
-#: wt-status.c:190
+#: wt-status.c:191
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
 msgstr ""
 
-#: wt-status.c:207
+#: wt-status.c:208
 msgid "bug"
 msgstr ""
 
-#: wt-status.c:212
+#: wt-status.c:213
 msgid "both deleted:"
 msgstr ""
 
-#: wt-status.c:213
+#: wt-status.c:214
 msgid "added by us:"
 msgstr ""
 
-#: wt-status.c:214
+#: wt-status.c:215
 msgid "deleted by them:"
 msgstr ""
 
-#: wt-status.c:215
+#: wt-status.c:216
 msgid "added by them:"
 msgstr ""
 
-#: wt-status.c:216
+#: wt-status.c:217
 msgid "deleted by us:"
 msgstr ""
 
-#: wt-status.c:217
+#: wt-status.c:218
 msgid "both added:"
 msgstr ""
 
-#: wt-status.c:218
+#: wt-status.c:219
 msgid "both modified:"
 msgstr ""
 
-#: wt-status.c:248
+#: wt-status.c:249
 msgid "new commits, "
 msgstr ""
 
-#: wt-status.c:250
+#: wt-status.c:251
 msgid "modified content, "
 msgstr ""
 
-#: wt-status.c:252
+#: wt-status.c:253
 msgid "untracked content, "
 msgstr ""
 
-#: wt-status.c:266
+#: wt-status.c:267
 #, c-format
 msgid "new file:   %s"
 msgstr ""
 
-#: wt-status.c:269
+#: wt-status.c:270
 #, c-format
 msgid "copied:     %s -> %s"
 msgstr ""
 
-#: wt-status.c:272
+#: wt-status.c:273
 #, c-format
 msgid "deleted:    %s"
 msgstr ""
 
-#: wt-status.c:275
+#: wt-status.c:276
 #, c-format
 msgid "modified:   %s"
 msgstr ""
 
-#: wt-status.c:278
+#: wt-status.c:279
 #, c-format
 msgid "renamed:    %s -> %s"
 msgstr ""
 
-#: wt-status.c:281
+#: wt-status.c:282
 #, c-format
 msgid "typechange: %s"
 msgstr ""
 
-#: wt-status.c:284
+#: wt-status.c:285
 #, c-format
 msgid "unknown:    %s"
 msgstr ""
 
-#: wt-status.c:287
+#: wt-status.c:288
 #, c-format
 msgid "unmerged:   %s"
 msgstr ""
 
-#: wt-status.c:290
+#: wt-status.c:291
 #, c-format
 msgid "bug: unhandled diff status %c"
 msgstr ""
 
-#: wt-status.c:713
+#: wt-status.c:737
 msgid "On branch "
 msgstr ""
 
-#: wt-status.c:720
+#: wt-status.c:744
 msgid "Not currently on any branch."
 msgstr ""
 
-#: wt-status.c:731
+#: wt-status.c:755
 msgid "Initial commit"
 msgstr ""
 
-#: wt-status.c:745
+#: wt-status.c:769
 msgid "Untracked"
 msgstr ""
 
-#: wt-status.c:747
+#: wt-status.c:771
 msgid "Ignored"
 msgstr ""
 
-#: wt-status.c:749
+#: wt-status.c:773
 #, c-format
 msgid "Untracked files not listed%s"
 msgstr ""
 
-#: wt-status.c:751
+#: wt-status.c:775
 msgid " (use -u option to show untracked files)"
 msgstr ""
 
-#: wt-status.c:757
+#: wt-status.c:781
 msgid "No changes"
 msgstr ""
 
-#: wt-status.c:761
+#: wt-status.c:785
 #, c-format
 msgid "no changes added to commit%s\n"
 msgstr ""
 
-#: wt-status.c:763
+#: wt-status.c:787
 msgid " (use \"git add\" and/or \"git commit -a\")"
 msgstr ""
 
-#: wt-status.c:765
+#: wt-status.c:789
 #, c-format
 msgid "nothing added to commit but untracked files present%s\n"
 msgstr ""
 
-#: wt-status.c:767
+#: wt-status.c:791
 msgid " (use \"git add\" to track)"
 msgstr ""
 
-#: wt-status.c:769 wt-status.c:772 wt-status.c:775
+#: wt-status.c:793 wt-status.c:796 wt-status.c:799
 #, c-format
 msgid "nothing to commit%s\n"
 msgstr ""
 
-#: wt-status.c:770
+#: wt-status.c:794
 msgid " (create/copy files and use \"git add\" to track)"
 msgstr ""
 
-#: wt-status.c:773
+#: wt-status.c:797
 msgid " (use -u to show untracked files)"
 msgstr ""
 
-#: wt-status.c:776
+#: wt-status.c:800
 msgid " (working directory clean)"
 msgstr ""
 
-#: wt-status.c:884
+#: wt-status.c:908
 msgid "HEAD (no branch)"
 msgstr ""
 
-#: wt-status.c:890
+#: wt-status.c:914
 msgid "Initial commit on "
 msgstr ""
 
-#: wt-status.c:905
+#: wt-status.c:929
 msgid "behind "
 msgstr ""
 
-#: wt-status.c:908 wt-status.c:911
+#: wt-status.c:932 wt-status.c:935
 msgid "ahead "
 msgstr ""
 
-#: wt-status.c:913
+#: wt-status.c:937
 msgid ", behind "
 msgstr ""
 
@@ -630,7 +842,7 @@ msgstr ""
 msgid "unexpected diff status %c"
 msgstr ""
 
-#: builtin/add.c:67 builtin/commit.c:298
+#: builtin/add.c:67 builtin/commit.c:226
 msgid "updating files failed"
 msgstr ""
 
@@ -720,2069 +932,3128 @@ msgstr ""
 msgid "Maybe you wanted to say 'git add .'?\n"
 msgstr ""
 
-#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:358 builtin/mv.c:82
+#: builtin/add.c:420 builtin/clean.c:95 builtin/commit.c:286 builtin/mv.c:82
 #: builtin/rm.c:162
 msgid "index file corrupt"
 msgstr ""
 
-#: builtin/add.c:476 builtin/mv.c:229 builtin/rm.c:260
+#: builtin/add.c:476 builtin/apply.c:4100 builtin/mv.c:229 builtin/rm.c:260
 msgid "Unable to write new index file"
 msgstr ""
 
-#: builtin/archive.c:17
+#: builtin/apply.c:106
 #, c-format
-msgid "could not create archive file '%s'"
+msgid "unrecognized whitespace option '%s'"
 msgstr ""
 
-#: builtin/archive.c:20
-msgid "could not redirect output"
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
 msgstr ""
 
-#: builtin/archive.c:37
-msgid "git archive: Remote with no URL"
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
 msgstr ""
 
-#: builtin/archive.c:58
-msgid "git archive: expected ACK/NAK, got EOF"
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
 msgstr ""
 
-#: builtin/archive.c:63
+#: builtin/apply.c:905
 #, c-format
-msgid "git archive: NACK %s"
+msgid "unable to find filename in patch at line %d"
 msgstr ""
 
-#: builtin/archive.c:65
+#: builtin/apply.c:937
 #, c-format
-msgid "remote error: %s"
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
 msgstr ""
 
-#: builtin/archive.c:66
-msgid "git archive: protocol error"
+#: builtin/apply.c:941
+#, c-format
+msgid "git apply: bad git-diff - inconsistent new filename on line %d"
 msgstr ""
 
-#: builtin/archive.c:71
-msgid "git archive: expected a flush"
+#: builtin/apply.c:942
+#, c-format
+msgid "git apply: bad git-diff - inconsistent old filename on line %d"
 msgstr ""
 
-#: builtin/branch.c:137
+#: builtin/apply.c:949
 #, c-format
-msgid ""
-"deleting branch '%s' that has been merged to\n"
-"         '%s', but not yet merged to HEAD."
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
 msgstr ""
 
-#: builtin/branch.c:141
+#: builtin/apply.c:1394
 #, c-format
-msgid ""
-"not deleting branch '%s' that is not yet merged to\n"
-"         '%s', even though it is merged to HEAD."
+msgid "recount: unexpected line: %.*s"
 msgstr ""
 
-#. TRANSLATORS: This is "remote " in "remote branch '%s' not found"
-#: builtin/branch.c:163
-msgid "remote "
+#: builtin/apply.c:1451
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
 msgstr ""
 
-#: builtin/branch.c:171
-msgid "cannot use -a with -d"
-msgstr ""
+#: builtin/apply.c:1468
+#, 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] ""
+msgstr[1] ""
 
-#: builtin/branch.c:177
-msgid "Couldn't look up commit object for HEAD"
+#: builtin/apply.c:1628
+msgid "new file depends on old contents"
 msgstr ""
 
-#: builtin/branch.c:182
-#, c-format
-msgid "Cannot delete the branch '%s' which you are currently on."
+#: builtin/apply.c:1630
+msgid "deleted file still has contents"
 msgstr ""
 
-#: builtin/branch.c:192
+#: builtin/apply.c:1656
 #, c-format
-msgid "%sbranch '%s' not found."
+msgid "corrupt patch at line %d"
 msgstr ""
 
-#: builtin/branch.c:200
+#: builtin/apply.c:1692
 #, c-format
-msgid "Couldn't look up commit object for '%s'"
+msgid "new file %s depends on old contents"
 msgstr ""
 
-#: builtin/branch.c:206
+#: builtin/apply.c:1694
 #, 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'."
+msgid "deleted file %s still has contents"
 msgstr ""
 
-#: builtin/branch.c:214
+#: builtin/apply.c:1697
 #, c-format
-msgid "Error deleting %sbranch '%s'"
+msgid "** warning: file %s becomes empty but is not deleted"
 msgstr ""
 
-#: builtin/branch.c:219
+#: builtin/apply.c:1843
 #, c-format
-msgid "Deleted %sbranch %s (was %s).\n"
-msgstr ""
-
-#: builtin/branch.c:224
-msgid "Update of config-file failed"
+msgid "corrupt binary patch at line %d: %.*s"
 msgstr ""
 
-#: builtin/branch.c:322
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1872
 #, c-format
-msgid "branch '%s' does not point at a commit"
+msgid "unrecognized binary patch at line %d"
 msgstr ""
 
-#: builtin/branch.c:394
+#: builtin/apply.c:1958
 #, c-format
-msgid "behind %d] "
+msgid "patch with only garbage at line %d"
 msgstr ""
 
-#: builtin/branch.c:396
+#: builtin/apply.c:2048
 #, c-format
-msgid "ahead %d] "
+msgid "unable to read symlink %s"
 msgstr ""
 
-#: builtin/branch.c:398
+#: builtin/apply.c:2052
 #, c-format
-msgid "ahead %d, behind %d] "
+msgid "unable to open or read %s"
 msgstr ""
 
-#: builtin/branch.c:501
-msgid "(no branch)"
+#: builtin/apply.c:2123
+msgid "oops"
 msgstr ""
 
-#: builtin/branch.c:566
-msgid "some refs could not be read"
+#: builtin/apply.c:2645
+#, c-format
+msgid "invalid start of line: '%c'"
 msgstr ""
 
-#: builtin/branch.c:579
-msgid "cannot rename the current branch while not on any."
+#: builtin/apply.c:2763
+#, 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:2775
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
 msgstr ""
 
-#: builtin/branch.c:589
+#: builtin/apply.c:2781
 #, c-format
-msgid "Invalid branch name: '%s'"
+msgid ""
+"while searching for:\n"
+"%.*s"
 msgstr ""
 
-#: builtin/branch.c:604
-msgid "Branch rename failed"
+#: builtin/apply.c:2800
+#, c-format
+msgid "missing binary patch data for '%s'"
 msgstr ""
 
-#: builtin/branch.c:608
+#: builtin/apply.c:2903
 #, c-format
-msgid "Renamed a misnamed branch '%s' away"
+msgid "binary patch does not apply to '%s'"
 msgstr ""
 
-#: builtin/branch.c:612
+#: builtin/apply.c:2909
 #, c-format
-msgid "Branch renamed to %s, but HEAD is not updated!"
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
 msgstr ""
 
-#: builtin/branch.c:619
-msgid "Branch is renamed, but update of config-file failed"
+#: builtin/apply.c:2930
+#, c-format
+msgid "patch failed: %s:%ld"
 msgstr ""
 
-#: builtin/branch.c:634
+#: builtin/apply.c:3045
 #, c-format
-msgid "malformed object name %s"
+msgid "patch %s has been renamed/deleted"
 msgstr ""
 
-#: builtin/branch.c:658
+#: builtin/apply.c:3052 builtin/apply.c:3069
 #, c-format
-msgid "could not write branch description template: %s\n"
+msgid "read of %s failed"
 msgstr ""
 
-#: builtin/branch.c:746
-msgid "Failed to resolve HEAD as a valid ref."
+#: builtin/apply.c:3084
+msgid "removal patch leaves file contents"
 msgstr ""
 
-#: builtin/branch.c:751 builtin/clone.c:558
-msgid "HEAD not found below refs/heads!"
+#: builtin/apply.c:3105
+#, c-format
+msgid "%s: already exists in working directory"
 msgstr ""
 
-#: builtin/branch.c:809
-msgid "-a and -r options to 'git branch' do not make sense with a branch name"
+#: builtin/apply.c:3143
+#, c-format
+msgid "%s: has been deleted/renamed"
 msgstr ""
 
-#: builtin/bundle.c:47
+#: builtin/apply.c:3148 builtin/apply.c:3179
 #, c-format
-msgid "%s is okay\n"
+msgid "%s: %s"
 msgstr ""
 
-#: builtin/bundle.c:56
-msgid "Need a repository to create a bundle."
+#: builtin/apply.c:3159
+#, c-format
+msgid "%s: does not exist in index"
 msgstr ""
 
-#: builtin/bundle.c:60
-msgid "Need a repository to unbundle."
+#: builtin/apply.c:3173
+#, c-format
+msgid "%s: does not match index"
 msgstr ""
 
-#: builtin/checkout.c:113 builtin/checkout.c:146
+#: builtin/apply.c:3190
 #, c-format
-msgid "path '%s' does not have our version"
+msgid "%s: wrong type"
 msgstr ""
 
-#: builtin/checkout.c:115 builtin/checkout.c:148
+#: builtin/apply.c:3192
 #, c-format
-msgid "path '%s' does not have their version"
+msgid "%s has type %o, expected %o"
 msgstr ""
 
-#: builtin/checkout.c:131
+#: builtin/apply.c:3247
 #, c-format
-msgid "path '%s' does not have all necessary versions"
+msgid "%s: already exists in index"
 msgstr ""
 
-#: builtin/checkout.c:175
+#: builtin/apply.c:3266
 #, c-format
-msgid "path '%s' does not have necessary versions"
+msgid "new mode (%o) of %s does not match old mode (%o)%s%s"
 msgstr ""
 
-#: builtin/checkout.c:192
+#: builtin/apply.c:3272
 #, c-format
-msgid "path '%s': cannot merge"
+msgid "%s: patch does not apply"
 msgstr ""
 
-#: builtin/checkout.c:209
+#: builtin/apply.c:3285
 #, c-format
-msgid "Unable to add merge result for '%s'"
+msgid "Checking patch %s..."
 msgstr ""
 
-#: builtin/checkout.c:212 builtin/reset.c:158
+#: builtin/apply.c:3340 builtin/checkout.c:212 builtin/reset.c:158
 #, c-format
 msgid "make_cache_entry failed for path '%s'"
 msgstr ""
 
-#: builtin/checkout.c:234 builtin/checkout.c:392
-msgid "corrupt index file"
+#: builtin/apply.c:3483
+#, c-format
+msgid "unable to remove %s from index"
 msgstr ""
 
-#: builtin/checkout.c:264 builtin/checkout.c:271
+#: builtin/apply.c:3510
 #, c-format
-msgid "path '%s' is unmerged"
+msgid "corrupt patch for subproject %s"
 msgstr ""
 
-#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
-#: builtin/merge.c:811
-msgid "unable to write new index file"
+#: builtin/apply.c:3514
+#, c-format
+msgid "unable to stat newly created file '%s'"
 msgstr ""
 
-#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
-msgid "diff_setup_done failed"
+#: builtin/apply.c:3519
+#, c-format
+msgid "unable to create backing store for newly created file %s"
 msgstr ""
 
-#: builtin/checkout.c:414
-msgid "you need to resolve your current index first"
+#: builtin/apply.c:3522
+#, c-format
+msgid "unable to add cache entry for %s"
 msgstr ""
 
-#: builtin/checkout.c:533
+#: builtin/apply.c:3555
 #, c-format
-msgid "Can not do reflog for '%s'\n"
+msgid "closing file '%s'"
 msgstr ""
 
-#: builtin/checkout.c:565
-msgid "HEAD is now at"
+#: builtin/apply.c:3604
+#, c-format
+msgid "unable to write file '%s' mode %o"
 msgstr ""
 
-#: builtin/checkout.c:572
+#: builtin/apply.c:3660
 #, c-format
-msgid "Reset branch '%s'\n"
+msgid "Applied patch %s cleanly."
 msgstr ""
 
-#: builtin/checkout.c:575
-#, c-format
-msgid "Already on '%s'\n"
+#: builtin/apply.c:3668
+msgid "internal error"
 msgstr ""
 
-#: builtin/checkout.c:579
+#. Say this even without --verbose
+#: builtin/apply.c:3671
 #, c-format
-msgid "Switched to and reset branch '%s'\n"
-msgstr ""
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] ""
+msgstr[1] ""
 
-#: builtin/checkout.c:581
+#: builtin/apply.c:3681
 #, c-format
-msgid "Switched to a new branch '%s'\n"
+msgid "truncating .rej filename to %.*s.rej"
 msgstr ""
 
-#: builtin/checkout.c:583
+#: builtin/apply.c:3702
 #, c-format
-msgid "Switched to branch '%s'\n"
+msgid "Hunk #%d applied cleanly."
 msgstr ""
 
-#: builtin/checkout.c:639
+#: builtin/apply.c:3705
 #, c-format
-msgid " ... and %d more.\n"
+msgid "Rejected hunk #%d."
 msgstr ""
 
-#. The singular version
-#: builtin/checkout.c:645
-#, 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] ""
-msgstr[1] ""
-
-#: builtin/checkout.c:663
-#, 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"
+#: builtin/apply.c:3836
+msgid "unrecognized input"
 msgstr ""
 
-#: builtin/checkout.c:692
-msgid "internal error in revision walk"
+#: builtin/apply.c:3847
+msgid "unable to read index file"
 msgstr ""
 
-#: builtin/checkout.c:696
-msgid "Previous HEAD position was"
+#: builtin/apply.c:4042
+msgid "--index outside a repository"
 msgstr ""
 
-#: builtin/checkout.c:722
-msgid "You are on a branch yet to be born"
+#: builtin/apply.c:4045
+msgid "--cached outside a repository"
 msgstr ""
 
-#. case (1)
-#: builtin/checkout.c:853
+#: builtin/apply.c:4061
 #, c-format
-msgid "invalid reference: %s"
+msgid "can't open patch '%s'"
 msgstr ""
 
-#. case (1): want a tree
-#: builtin/checkout.c:892
+#: builtin/apply.c:4075
 #, c-format
-msgid "reference is not a tree: %s"
-msgstr ""
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] ""
+msgstr[1] ""
 
-#: builtin/checkout.c:972
-msgid "-B cannot be used with -b"
-msgstr ""
+#: builtin/apply.c:4081 builtin/apply.c:4091
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] ""
+msgstr[1] ""
 
-#: builtin/checkout.c:981
-msgid "--patch is incompatible with all other options"
+#: builtin/archive.c:17
+#, c-format
+msgid "could not create archive file '%s'"
 msgstr ""
 
-#: builtin/checkout.c:984
-msgid "--detach cannot be used with -b/-B/--orphan"
+#: builtin/archive.c:20
+msgid "could not redirect output"
 msgstr ""
 
-#: builtin/checkout.c:986
-msgid "--detach cannot be used with -t"
+#: builtin/archive.c:37
+msgid "git archive: Remote with no URL"
 msgstr ""
 
-#: builtin/checkout.c:992
-msgid "--track needs a branch name"
+#: builtin/archive.c:58
+msgid "git archive: expected ACK/NAK, got EOF"
 msgstr ""
 
-#: builtin/checkout.c:999
-msgid "Missing branch name; try -b"
+#: builtin/archive.c:63
+#, c-format
+msgid "git archive: NACK %s"
 msgstr ""
 
-#: builtin/checkout.c:1005
-msgid "--orphan and -b|-B are mutually exclusive"
+#: builtin/archive.c:65
+#, c-format
+msgid "remote error: %s"
 msgstr ""
 
-#: builtin/checkout.c:1007
-msgid "--orphan cannot be used with -t"
+#: builtin/archive.c:66
+msgid "git archive: protocol error"
 msgstr ""
 
-#: builtin/checkout.c:1017
-msgid "git checkout: -f and -m are incompatible"
+#: builtin/archive.c:71
+msgid "git archive: expected a flush"
 msgstr ""
 
-#: builtin/checkout.c:1051
-msgid "invalid path specification"
+#: builtin/branch.c:144
+#, c-format
+msgid ""
+"deleting branch '%s' that has been merged to\n"
+"         '%s', but not yet merged to HEAD."
 msgstr ""
 
-#: builtin/checkout.c:1059
+#: builtin/branch.c:148
 #, c-format
 msgid ""
-"git checkout: updating paths is incompatible with switching branches.\n"
-"Did you intend to checkout '%s' which can not be resolved as commit?"
+"not deleting branch '%s' that is not yet merged to\n"
+"         '%s', even though it is merged to HEAD."
 msgstr ""
 
-#: builtin/checkout.c:1061
-msgid "git checkout: updating paths is incompatible with switching branches."
+#: builtin/branch.c:180
+msgid "cannot use -a with -d"
 msgstr ""
 
-#: builtin/checkout.c:1066
-msgid "git checkout: --detach does not take a path argument"
+#: builtin/branch.c:186
+msgid "Couldn't look up commit object for HEAD"
 msgstr ""
 
-#: builtin/checkout.c:1069
-msgid ""
-"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
-"checking out of the index."
+#: builtin/branch.c:191
+#, c-format
+msgid "Cannot delete the branch '%s' which you are currently on."
 msgstr ""
 
-#: builtin/checkout.c:1088
-msgid "Cannot switch branch to a non-commit."
+#: builtin/branch.c:202
+#, c-format
+msgid "remote branch '%s' not found."
 msgstr ""
 
-#: builtin/checkout.c:1091
-msgid "--ours/--theirs is incompatible with switching branches."
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
 msgstr ""
 
-#: builtin/clean.c:78
-msgid "-x and -X cannot be used together"
+#: builtin/branch.c:210
+#, c-format
+msgid "Couldn't look up commit object for '%s'"
 msgstr ""
 
-#: builtin/clean.c:82
+#: builtin/branch.c:216
+#, c-format
 msgid ""
-"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+"The branch '%s' is not fully merged.\n"
+"If you are sure you want to delete it, run 'git branch -D %s'."
 msgstr ""
 
-#: builtin/clean.c:85
-msgid ""
-"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
-"clean"
+#: builtin/branch.c:225
+#, c-format
+msgid "Error deleting remote branch '%s'"
 msgstr ""
 
-#: builtin/clean.c:155 builtin/clean.c:176
+#: builtin/branch.c:226
 #, c-format
-msgid "Would remove %s\n"
+msgid "Error deleting branch '%s'"
 msgstr ""
 
-#: builtin/clean.c:159 builtin/clean.c:179
+#: builtin/branch.c:233
 #, c-format
-msgid "Removing %s\n"
+msgid "Deleted remote branch %s (was %s).\n"
 msgstr ""
 
-#: builtin/clean.c:162 builtin/clean.c:182
+#: builtin/branch.c:234
 #, c-format
-msgid "failed to remove %s"
+msgid "Deleted branch %s (was %s).\n"
 msgstr ""
 
-#: builtin/clean.c:166
-#, c-format
-msgid "Would not remove %s\n"
+#: builtin/branch.c:239
+msgid "Update of config-file failed"
 msgstr ""
 
-#: builtin/clean.c:168
+#: builtin/branch.c:337
 #, c-format
-msgid "Not removing %s\n"
+msgid "branch '%s' does not point at a commit"
 msgstr ""
 
-#: builtin/clone.c:243
+#: builtin/branch.c:409
 #, c-format
-msgid "reference repository '%s' is not a local directory."
+msgid "[%s: behind %d]"
 msgstr ""
 
-#: builtin/clone.c:302
+#: builtin/branch.c:411
 #, c-format
-msgid "failed to open '%s'"
+msgid "[behind %d]"
 msgstr ""
 
-#: builtin/clone.c:306
+#: builtin/branch.c:415
 #, c-format
-msgid "failed to create directory '%s'"
+msgid "[%s: ahead %d]"
 msgstr ""
 
-#: builtin/clone.c:308 builtin/diff.c:75
+#: builtin/branch.c:417
 #, c-format
-msgid "failed to stat '%s'"
+msgid "[ahead %d]"
 msgstr ""
 
-#: builtin/clone.c:310
+#: builtin/branch.c:420
 #, c-format
-msgid "%s exists and is not a directory"
+msgid "[%s: ahead %d, behind %d]"
 msgstr ""
 
-#: builtin/clone.c:324
+#: builtin/branch.c:423
 #, c-format
-msgid "failed to stat %s\n"
+msgid "[ahead %d, behind %d]"
 msgstr ""
 
-#: builtin/clone.c:341
-#, c-format
-msgid "failed to unlink '%s'"
+#: builtin/branch.c:535
+msgid "(no branch)"
 msgstr ""
 
-#: builtin/clone.c:346
-#, c-format
-msgid "failed to create link '%s'"
+#: builtin/branch.c:600
+msgid "some refs could not be read"
 msgstr ""
 
-#: builtin/clone.c:350
-#, c-format
-msgid "failed to copy file to '%s'"
+#: builtin/branch.c:613
+msgid "cannot rename the current branch while not on any."
 msgstr ""
 
-#: builtin/clone.c:373
+#: builtin/branch.c:623
 #, c-format
-msgid "done.\n"
+msgid "Invalid branch name: '%s'"
 msgstr ""
 
-#: builtin/clone.c:440
-#, c-format
-msgid "Could not find remote branch %s to clone."
+#: builtin/branch.c:638
+msgid "Branch rename failed"
 msgstr ""
 
-#: builtin/clone.c:549
-msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+#: builtin/branch.c:642
+#, c-format
+msgid "Renamed a misnamed branch '%s' away"
 msgstr ""
 
-#: builtin/clone.c:639
-msgid "Too many arguments."
+#: builtin/branch.c:646
+#, c-format
+msgid "Branch renamed to %s, but HEAD is not updated!"
 msgstr ""
 
-#: builtin/clone.c:643
-msgid "You must specify a repository to clone."
+#: builtin/branch.c:653
+msgid "Branch is renamed, but update of config-file failed"
 msgstr ""
 
-#: builtin/clone.c:654
+#: builtin/branch.c:668
 #, c-format
-msgid "--bare and --origin %s options are incompatible."
+msgid "malformed object name %s"
 msgstr ""
 
-#: builtin/clone.c:668
+#: builtin/branch.c:692
 #, c-format
-msgid "repository '%s' does not exist"
+msgid "could not write branch description template: %s"
 msgstr ""
 
-#: builtin/clone.c:673
-msgid "--depth is ignored in local clones; use file:// instead."
+#: builtin/branch.c:783
+msgid "Failed to resolve HEAD as a valid ref."
 msgstr ""
 
-#: builtin/clone.c:683
-#, c-format
-msgid "destination path '%s' already exists and is not an empty directory."
+#: builtin/branch.c:788 builtin/clone.c:558
+msgid "HEAD not found below refs/heads!"
 msgstr ""
 
-#: builtin/clone.c:693
-#, c-format
-msgid "working tree '%s' already exists."
+#: builtin/branch.c:808
+msgid "--column and --verbose are incompatible"
 msgstr ""
 
-#: builtin/clone.c:706 builtin/clone.c:720
-#, c-format
-msgid "could not create leading directories of '%s'"
+#: builtin/branch.c:857
+msgid "-a and -r options to 'git branch' do not make sense with a branch name"
 msgstr ""
 
-#: builtin/clone.c:709
+#: builtin/bundle.c:47
 #, c-format
-msgid "could not create work tree dir '%s'."
+msgid "%s is okay\n"
 msgstr ""
 
-#: builtin/clone.c:728
-#, c-format
-msgid "Cloning into bare repository '%s'...\n"
+#: builtin/bundle.c:56
+msgid "Need a repository to create a bundle."
 msgstr ""
 
-#: builtin/clone.c:730
+#: builtin/bundle.c:60
+msgid "Need a repository to unbundle."
+msgstr ""
+
+#: builtin/checkout.c:113 builtin/checkout.c:146
 #, c-format
-msgid "Cloning into '%s'...\n"
+msgid "path '%s' does not have our version"
 msgstr ""
 
-#: builtin/clone.c:786
+#: builtin/checkout.c:115 builtin/checkout.c:148
 #, c-format
-msgid "Don't know how to clone %s"
+msgid "path '%s' does not have their version"
 msgstr ""
 
-#: builtin/clone.c:835
+#: builtin/checkout.c:131
 #, c-format
-msgid "Remote branch %s not found in upstream %s"
+msgid "path '%s' does not have all necessary versions"
 msgstr ""
 
-#: builtin/clone.c:842
-msgid "You appear to have cloned an empty repository."
+#: builtin/checkout.c:175
+#, c-format
+msgid "path '%s' does not have necessary versions"
 msgstr ""
 
-#: builtin/commit.c:42
-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"
+#: builtin/checkout.c:192
+#, c-format
+msgid "path '%s': cannot merge"
 msgstr ""
 
-#: builtin/commit.c:54
-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"
+#: builtin/checkout.c:209
+#, c-format
+msgid "Unable to add merge result for '%s'"
 msgstr ""
 
-#: builtin/commit.c:59
-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"
+#: builtin/checkout.c:234 builtin/checkout.c:392
+msgid "corrupt index file"
 msgstr ""
 
-#: builtin/commit.c:205 builtin/reset.c:33
-msgid "merge"
+#: builtin/checkout.c:264 builtin/checkout.c:271
+#, c-format
+msgid "path '%s' is unmerged"
 msgstr ""
 
-#: builtin/commit.c:208
-msgid "cherry-pick"
+#: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
+#: builtin/merge.c:812
+msgid "unable to write new index file"
 msgstr ""
 
-#: builtin/commit.c:325
-msgid "failed to unpack HEAD tree object"
+#: builtin/checkout.c:319 builtin/diff.c:302 builtin/merge.c:408
+msgid "diff_setup_done failed"
 msgstr ""
 
-#: builtin/commit.c:367
+#: builtin/checkout.c:414
+msgid "you need to resolve your current index first"
+msgstr ""
+
+#: builtin/checkout.c:533
+#, c-format
+msgid "Can not do reflog for '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:566
+msgid "HEAD is now at"
+msgstr ""
+
+#: builtin/checkout.c:573
+#, c-format
+msgid "Reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:576
+#, c-format
+msgid "Already on '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:580
+#, c-format
+msgid "Switched to and reset branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:582
+#, c-format
+msgid "Switched to a new branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:584
+#, c-format
+msgid "Switched to branch '%s'\n"
+msgstr ""
+
+#: builtin/checkout.c:640
+#, c-format
+msgid " ... and %d more.\n"
+msgstr ""
+
+#. The singular version
+#: builtin/checkout.c:646
+#, 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] ""
+msgstr[1] ""
+
+#: builtin/checkout.c:664
+#, 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 ""
+
+#: builtin/checkout.c:694
+msgid "internal error in revision walk"
+msgstr ""
+
+#: builtin/checkout.c:698
+msgid "Previous HEAD position was"
+msgstr ""
+
+#: builtin/checkout.c:724
+msgid "You are on a branch yet to be born"
+msgstr ""
+
+#. case (1)
+#: builtin/checkout.c:855
+#, c-format
+msgid "invalid reference: %s"
+msgstr ""
+
+#. case (1): want a tree
+#: builtin/checkout.c:894
+#, c-format
+msgid "reference is not a tree: %s"
+msgstr ""
+
+#: builtin/checkout.c:974
+msgid "-B cannot be used with -b"
+msgstr ""
+
+#: builtin/checkout.c:983
+msgid "--patch is incompatible with all other options"
+msgstr ""
+
+#: builtin/checkout.c:986
+msgid "--detach cannot be used with -b/-B/--orphan"
+msgstr ""
+
+#: builtin/checkout.c:988
+msgid "--detach cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:994
+msgid "--track needs a branch name"
+msgstr ""
+
+#: builtin/checkout.c:1001
+msgid "Missing branch name; try -b"
+msgstr ""
+
+#: builtin/checkout.c:1007
+msgid "--orphan and -b|-B are mutually exclusive"
+msgstr ""
+
+#: builtin/checkout.c:1009
+msgid "--orphan cannot be used with -t"
+msgstr ""
+
+#: builtin/checkout.c:1019
+msgid "git checkout: -f and -m are incompatible"
+msgstr ""
+
+#: builtin/checkout.c:1053
+msgid "invalid path specification"
+msgstr ""
+
+#: builtin/checkout.c:1061
+#, c-format
+msgid ""
+"git checkout: updating paths is incompatible with switching branches.\n"
+"Did you intend to checkout '%s' which can not be resolved as commit?"
+msgstr ""
+
+#: builtin/checkout.c:1063
+msgid "git checkout: updating paths is incompatible with switching branches."
+msgstr ""
+
+#: builtin/checkout.c:1068
+msgid "git checkout: --detach does not take a path argument"
+msgstr ""
+
+#: builtin/checkout.c:1071
+msgid ""
+"git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
+"checking out of the index."
+msgstr ""
+
+#: builtin/checkout.c:1090
+msgid "Cannot switch branch to a non-commit."
+msgstr ""
+
+#: builtin/checkout.c:1093
+msgid "--ours/--theirs is incompatible with switching branches."
+msgstr ""
+
+#: builtin/clean.c:78
+msgid "-x and -X cannot be used together"
+msgstr ""
+
+#: builtin/clean.c:82
+msgid ""
+"clean.requireForce set to true and neither -n nor -f given; refusing to clean"
+msgstr ""
+
+#: builtin/clean.c:85
+msgid ""
+"clean.requireForce defaults to true and neither -n nor -f given; refusing to "
+"clean"
+msgstr ""
+
+#: builtin/clean.c:155 builtin/clean.c:176
+#, c-format
+msgid "Would remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:159 builtin/clean.c:179
+#, c-format
+msgid "Removing %s\n"
+msgstr ""
+
+#: builtin/clean.c:162 builtin/clean.c:182
+#, c-format
+msgid "failed to remove %s"
+msgstr ""
+
+#: builtin/clean.c:166
+#, c-format
+msgid "Would not remove %s\n"
+msgstr ""
+
+#: builtin/clean.c:168
+#, c-format
+msgid "Not removing %s\n"
+msgstr ""
+
+#: builtin/clone.c:243
+#, c-format
+msgid "reference repository '%s' is not a local directory."
+msgstr ""
+
+#: builtin/clone.c:302
+#, c-format
+msgid "failed to open '%s'"
+msgstr ""
+
+#: builtin/clone.c:306
+#, c-format
+msgid "failed to create directory '%s'"
+msgstr ""
+
+#: builtin/clone.c:308 builtin/diff.c:75
+#, c-format
+msgid "failed to stat '%s'"
+msgstr ""
+
+#: builtin/clone.c:310
+#, c-format
+msgid "%s exists and is not a directory"
+msgstr ""
+
+#: builtin/clone.c:324
+#, c-format
+msgid "failed to stat %s\n"
+msgstr ""
+
+#: builtin/clone.c:341
+#, c-format
+msgid "failed to unlink '%s'"
+msgstr ""
+
+#: builtin/clone.c:346
+#, c-format
+msgid "failed to create link '%s'"
+msgstr ""
+
+#: builtin/clone.c:350
+#, c-format
+msgid "failed to copy file to '%s'"
+msgstr ""
+
+#: builtin/clone.c:373
+#, c-format
+msgid "done.\n"
+msgstr ""
+
+#: builtin/clone.c:440
+#, c-format
+msgid "Could not find remote branch %s to clone."
+msgstr ""
+
+#: builtin/clone.c:549
+msgid "remote HEAD refers to nonexistent ref, unable to checkout.\n"
+msgstr ""
+
+#: builtin/clone.c:639
+msgid "Too many arguments."
+msgstr ""
+
+#: builtin/clone.c:643
+msgid "You must specify a repository to clone."
+msgstr ""
+
+#: builtin/clone.c:654
+#, c-format
+msgid "--bare and --origin %s options are incompatible."
+msgstr ""
+
+#: builtin/clone.c:668
+#, c-format
+msgid "repository '%s' does not exist"
+msgstr ""
+
+#: builtin/clone.c:673
+msgid "--depth is ignored in local clones; use file:// instead."
+msgstr ""
+
+#: builtin/clone.c:683
+#, c-format
+msgid "destination path '%s' already exists and is not an empty directory."
+msgstr ""
+
+#: builtin/clone.c:693
+#, c-format
+msgid "working tree '%s' already exists."
+msgstr ""
+
+#: builtin/clone.c:706 builtin/clone.c:720
+#, c-format
+msgid "could not create leading directories of '%s'"
+msgstr ""
+
+#: builtin/clone.c:709
+#, c-format
+msgid "could not create work tree dir '%s'."
+msgstr ""
+
+#: builtin/clone.c:728
+#, c-format
+msgid "Cloning into bare repository '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:730
+#, c-format
+msgid "Cloning into '%s'...\n"
+msgstr ""
+
+#: builtin/clone.c:786
+#, c-format
+msgid "Don't know how to clone %s"
+msgstr ""
+
+#: builtin/clone.c:835
+#, c-format
+msgid "Remote branch %s not found in upstream %s"
+msgstr ""
+
+#: builtin/clone.c:842
+msgid "You appear to have cloned an empty repository."
+msgstr ""
+
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr ""
+
+#: builtin/commit.c:43
+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 ""
+
+#: builtin/commit.c:55
+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:60
+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:253
+msgid "failed to unpack HEAD tree object"
+msgstr ""
+
+#: builtin/commit.c:295
 msgid "unable to create temporary index"
 msgstr ""
 
-#: builtin/commit.c:373
-msgid "interactive add failed"
+#: builtin/commit.c:301
+msgid "interactive add failed"
+msgstr ""
+
+#: builtin/commit.c:334 builtin/commit.c:355 builtin/commit.c:405
+msgid "unable to write new_index file"
+msgstr ""
+
+#: builtin/commit.c:386
+msgid "cannot do a partial commit during a merge."
+msgstr ""
+
+#: builtin/commit.c:388
+msgid "cannot do a partial commit during a cherry-pick."
+msgstr ""
+
+#: builtin/commit.c:398
+msgid "cannot read the index"
+msgstr ""
+
+#: builtin/commit.c:418
+msgid "unable to write temporary index file"
+msgstr ""
+
+#: builtin/commit.c:493 builtin/commit.c:499
+#, c-format
+msgid "invalid commit: %s"
+msgstr ""
+
+#: builtin/commit.c:522
+msgid "malformed --author parameter"
+msgstr ""
+
+#: builtin/commit.c:583
+#, c-format
+msgid "Malformed ident string: '%s'"
+msgstr ""
+
+#: builtin/commit.c:621 builtin/commit.c:654 builtin/commit.c:968
+#, c-format
+msgid "could not lookup commit %s"
+msgstr ""
+
+#: builtin/commit.c:633 builtin/shortlog.c:296
+#, c-format
+msgid "(reading log message from standard input)\n"
+msgstr ""
+
+#: builtin/commit.c:635
+msgid "could not read log from standard input"
+msgstr ""
+
+#: builtin/commit.c:639
+#, c-format
+msgid "could not read log file '%s'"
+msgstr ""
+
+#: builtin/commit.c:645
+msgid "commit has empty message"
+msgstr ""
+
+#: builtin/commit.c:661
+msgid "could not read MERGE_MSG"
+msgstr ""
+
+#: builtin/commit.c:665
+msgid "could not read SQUASH_MSG"
+msgstr ""
+
+#: builtin/commit.c:669
+#, c-format
+msgid "could not read '%s'"
+msgstr ""
+
+#: builtin/commit.c:721
+msgid "could not write commit template"
+msgstr ""
+
+#: builtin/commit.c:732
+#, 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 ""
+
+#: builtin/commit.c:737
+#, 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 ""
+
+#: builtin/commit.c:749
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be ignored, and an empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:754
+msgid ""
+"Please enter the commit message for your changes. Lines starting\n"
+"with '#' will be kept; you may remove them yourself if you want to.\n"
+"An empty message aborts the commit.\n"
+msgstr ""
+
+#: builtin/commit.c:767
+#, c-format
+msgid "%sAuthor:    %s"
+msgstr ""
+
+#: builtin/commit.c:774
+#, c-format
+msgid "%sCommitter: %s"
+msgstr ""
+
+#: builtin/commit.c:794
+msgid "Cannot read index"
+msgstr ""
+
+#: builtin/commit.c:831
+msgid "Error building trees"
+msgstr ""
+
+#: builtin/commit.c:846 builtin/tag.c:361
+#, c-format
+msgid "Please supply the message using either -m or -F option.\n"
+msgstr ""
+
+#: builtin/commit.c:943
+#, c-format
+msgid "No existing author found with '%s'"
+msgstr ""
+
+#: builtin/commit.c:958 builtin/commit.c:1158
+#, c-format
+msgid "Invalid untracked files mode '%s'"
+msgstr ""
+
+#: builtin/commit.c:998
+msgid "Using both --reset-author and --author does not make sense"
+msgstr ""
+
+#: builtin/commit.c:1009
+msgid "You have nothing to amend."
+msgstr ""
+
+#: builtin/commit.c:1012
+msgid "You are in the middle of a merge -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1014
+msgid "You are in the middle of a cherry-pick -- cannot amend."
+msgstr ""
+
+#: builtin/commit.c:1017
+msgid "Options --squash and --fixup cannot be used together"
+msgstr ""
+
+#: builtin/commit.c:1027
+msgid "Only one of -c/-C/-F/--fixup can be used."
+msgstr ""
+
+#: builtin/commit.c:1029
+msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+msgstr ""
+
+#: builtin/commit.c:1037
+msgid "--reset-author can be used only with -C, -c or --amend."
+msgstr ""
+
+#: builtin/commit.c:1054
+msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+msgstr ""
+
+#: builtin/commit.c:1056
+msgid "No paths with --include/--only does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1058
+msgid "Clever... amending the last one with dirty index."
+msgstr ""
+
+#: builtin/commit.c:1060
+msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+msgstr ""
+
+#: builtin/commit.c:1070 builtin/tag.c:577
+#, c-format
+msgid "Invalid cleanup mode %s"
+msgstr ""
+
+#: builtin/commit.c:1075
+msgid "Paths with -a does not make sense."
+msgstr ""
+
+#: builtin/commit.c:1258
+msgid "couldn't look up newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1260
+msgid "could not parse newly created commit"
+msgstr ""
+
+#: builtin/commit.c:1301
+msgid "detached HEAD"
+msgstr ""
+
+#: builtin/commit.c:1303
+msgid " (root-commit)"
+msgstr ""
+
+#: builtin/commit.c:1447
+msgid "could not parse HEAD commit"
+msgstr ""
+
+#: builtin/commit.c:1485 builtin/merge.c:509
+#, c-format
+msgid "could not open '%s' for reading"
+msgstr ""
+
+#: builtin/commit.c:1492
+#, c-format
+msgid "Corrupt MERGE_HEAD file (%s)"
+msgstr ""
+
+#: builtin/commit.c:1499
+msgid "could not read MERGE_MODE"
+msgstr ""
+
+#: builtin/commit.c:1518
+#, c-format
+msgid "could not read commit message: %s"
+msgstr ""
+
+#: builtin/commit.c:1532
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr ""
+
+#: builtin/commit.c:1537
+#, c-format
+msgid "Aborting commit due to empty commit message.\n"
+msgstr ""
+
+#: builtin/commit.c:1552 builtin/merge.c:936 builtin/merge.c:961
+msgid "failed to write commit object"
+msgstr ""
+
+#: builtin/commit.c:1573
+msgid "cannot lock HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1577
+msgid "cannot update HEAD ref"
+msgstr ""
+
+#: builtin/commit.c:1588
+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/describe.c:234
+#, c-format
+msgid "annotated tag %s not available"
+msgstr ""
+
+#: builtin/describe.c:238
+#, c-format
+msgid "annotated tag %s has no embedded name"
+msgstr ""
+
+#: builtin/describe.c:240
+#, c-format
+msgid "tag '%s' is really '%s' here"
+msgstr ""
+
+#: builtin/describe.c:267
+#, c-format
+msgid "Not a valid object name %s"
+msgstr ""
+
+#: builtin/describe.c:270
+#, c-format
+msgid "%s is not a valid '%s' object"
+msgstr ""
+
+#: builtin/describe.c:287
+#, c-format
+msgid "no tag exactly matches '%s'"
+msgstr ""
+
+#: builtin/describe.c:289
+#, c-format
+msgid "searching to describe %s\n"
+msgstr ""
+
+#: builtin/describe.c:329
+#, c-format
+msgid "finished search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:353
+#, c-format
+msgid ""
+"No annotated tags can describe '%s'.\n"
+"However, there were unannotated tags: try --tags."
+msgstr ""
+
+#: builtin/describe.c:357
+#, c-format
+msgid ""
+"No tags can describe '%s'.\n"
+"Try --always, or create some tags."
+msgstr ""
+
+#: builtin/describe.c:378
+#, c-format
+msgid "traversed %lu commits\n"
+msgstr ""
+
+#: builtin/describe.c:381
+#, c-format
+msgid ""
+"more than %i tags found; listed %i most recent\n"
+"gave up search at %s\n"
+msgstr ""
+
+#: builtin/describe.c:436
+msgid "--long is incompatible with --abbrev=0"
+msgstr ""
+
+#: builtin/describe.c:462
+msgid "No names found, cannot describe anything."
+msgstr ""
+
+#: builtin/describe.c:482
+msgid "--dirty is incompatible with committishes"
+msgstr ""
+
+#: builtin/diff.c:77
+#, c-format
+msgid "'%s': not a regular file or symlink"
+msgstr ""
+
+#: builtin/diff.c:220
+#, c-format
+msgid "invalid option: %s"
+msgstr ""
+
+#: builtin/diff.c:297
+msgid "Not a git repository"
+msgstr ""
+
+#: builtin/diff.c:347
+#, c-format
+msgid "invalid object '%s' given."
+msgstr ""
+
+#: builtin/diff.c:352
+#, c-format
+msgid "more than %d trees given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:362
+#, c-format
+msgid "more than two blobs given: '%s'"
+msgstr ""
+
+#: builtin/diff.c:370
+#, c-format
+msgid "unhandled object '%s' given."
+msgstr ""
+
+#: builtin/fetch.c:200
+msgid "Couldn't find remote ref HEAD"
+msgstr ""
+
+#: builtin/fetch.c:253
+#, c-format
+msgid "object %s not found"
+msgstr ""
+
+#: builtin/fetch.c:259
+msgid "[up to date]"
+msgstr ""
+
+#: builtin/fetch.c:273
+#, c-format
+msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgstr ""
+
+#: builtin/fetch.c:274 builtin/fetch.c:360
+msgid "[rejected]"
+msgstr ""
+
+#: builtin/fetch.c:285
+msgid "[tag update]"
+msgstr ""
+
+#: builtin/fetch.c:287 builtin/fetch.c:322 builtin/fetch.c:340
+msgid "  (unable to update local ref)"
+msgstr ""
+
+#: builtin/fetch.c:305
+msgid "[new tag]"
+msgstr ""
+
+#: builtin/fetch.c:308
+msgid "[new branch]"
+msgstr ""
+
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr ""
+
+#: builtin/fetch.c:356
+msgid "unable to update local ref"
+msgstr ""
+
+#: builtin/fetch.c:356
+msgid "forced update"
+msgstr ""
+
+#: builtin/fetch.c:362
+msgid "(non-fast-forward)"
+msgstr ""
+
+#: builtin/fetch.c:393 builtin/fetch.c:685
+#, c-format
+msgid "cannot open %s: %s\n"
+msgstr ""
+
+#: builtin/fetch.c:402
+#, c-format
+msgid "%s did not send all necessary objects\n"
+msgstr ""
+
+#: builtin/fetch.c:488
+#, c-format
+msgid "From %.*s\n"
+msgstr ""
+
+#: builtin/fetch.c:499
+#, 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:549
+#, c-format
+msgid "   (%s will become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:550
+#, c-format
+msgid "   (%s has become dangling)"
+msgstr ""
+
+#: builtin/fetch.c:557
+msgid "[deleted]"
+msgstr ""
+
+#: builtin/fetch.c:558 builtin/remote.c:1055
+msgid "(none)"
+msgstr ""
+
+#: builtin/fetch.c:675
+#, c-format
+msgid "Refusing to fetch into current branch %s of non-bare repository"
+msgstr ""
+
+#: builtin/fetch.c:709
+#, c-format
+msgid "Don't know how to fetch from %s"
+msgstr ""
+
+#: builtin/fetch.c:786
+#, c-format
+msgid "Option \"%s\" value \"%s\" is not valid for %s"
+msgstr ""
+
+#: builtin/fetch.c:789
+#, c-format
+msgid "Option \"%s\" is ignored for %s\n"
+msgstr ""
+
+#: builtin/fetch.c:888
+#, c-format
+msgid "Fetching %s\n"
+msgstr ""
+
+#: builtin/fetch.c:890 builtin/remote.c:100
+#, c-format
+msgid "Could not fetch %s"
+msgstr ""
+
+#: builtin/fetch.c:907
+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:927
+msgid "You need to specify a tag name."
+msgstr ""
+
+#: builtin/fetch.c:979
+msgid "fetch --all does not take a repository argument"
+msgstr ""
+
+#: builtin/fetch.c:981
+msgid "fetch --all does not make sense with refspecs"
+msgstr ""
+
+#: builtin/fetch.c:992
+#, c-format
+msgid "No such remote or remote group: %s"
+msgstr ""
+
+#: builtin/fetch.c:1000
+msgid "Fetching a group and specifying refspecs does not make sense"
+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:221
+#, c-format
+msgid "Auto packing the repository for optimum performance.\n"
+msgstr ""
+
+#: builtin/gc.c:224
+#, 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:251
+msgid ""
+"There are too many unreachable loose objects; run 'git prune' to remove them."
+msgstr ""
+
+#: builtin/grep.c:216
+#, c-format
+msgid "grep: failed to create thread: %s"
+msgstr ""
+
+#: builtin/grep.c:402
+#, c-format
+msgid "Failed to chdir: %s"
+msgstr ""
+
+#: builtin/grep.c:478 builtin/grep.c:512
+#, c-format
+msgid "unable to read tree (%s)"
+msgstr ""
+
+#: builtin/grep.c:526
+#, c-format
+msgid "unable to grep from object of type %s"
+msgstr ""
+
+#: builtin/grep.c:584
+#, c-format
+msgid "switch `%c' expects a numerical value"
+msgstr ""
+
+#: builtin/grep.c:601
+#, c-format
+msgid "cannot open '%s'"
+msgstr ""
+
+#: builtin/grep.c:888
+msgid "no pattern given."
+msgstr ""
+
+#: builtin/grep.c:902
+#, c-format
+msgid "bad object %s"
+msgstr ""
+
+#: builtin/grep.c:943
+msgid "--open-files-in-pager only works on the worktree"
 msgstr ""
 
-#: builtin/commit.c:406 builtin/commit.c:427 builtin/commit.c:473
-msgid "unable to write new_index file"
+#: builtin/grep.c:966
+msgid "--cached or --untracked cannot be used with --no-index."
 msgstr ""
 
-#: builtin/commit.c:457
-#, c-format
-msgid "cannot do a partial commit during a %s."
+#: builtin/grep.c:971
+msgid "--no-index or --untracked cannot be used with revs."
 msgstr ""
 
-#: builtin/commit.c:466
-msgid "cannot read the index"
+#: builtin/grep.c:974
+msgid "--[no-]exclude-standard cannot be used for tracked contents."
 msgstr ""
 
-#: builtin/commit.c:486
-msgid "unable to write temporary index file"
+#: builtin/grep.c:982
+msgid "both --cached and trees are given."
 msgstr ""
 
-#: builtin/commit.c:550 builtin/commit.c:556
+#: builtin/help.c:59
 #, c-format
-msgid "invalid commit: %s"
+msgid "unrecognized help format '%s'"
 msgstr ""
 
-#: builtin/commit.c:579
-msgid "malformed --author parameter"
+#: builtin/help.c:87
+msgid "Failed to start emacsclient."
 msgstr ""
 
-#: builtin/commit.c:635
+#: builtin/help.c:100
+msgid "Failed to parse emacsclient version."
+msgstr ""
+
+#: builtin/help.c:108
 #, c-format
-msgid "Malformed ident string: '%s'"
+msgid "emacsclient version '%d' too old (< 22)."
 msgstr ""
 
-#: builtin/commit.c:670 builtin/commit.c:703 builtin/commit.c:1000
+#: builtin/help.c:126 builtin/help.c:154 builtin/help.c:163 builtin/help.c:171
 #, c-format
-msgid "could not lookup commit %s"
+msgid "failed to exec '%s': %s"
 msgstr ""
 
-#: builtin/commit.c:682 builtin/shortlog.c:296
+#: builtin/help.c:211
 #, c-format
-msgid "(reading log message from standard input)\n"
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
 msgstr ""
 
-#: builtin/commit.c:684
-msgid "could not read log from standard input"
+#: builtin/help.c:223
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+
+#: builtin/help.c:287
+msgid "The most commonly used git commands are:"
 msgstr ""
 
-#: builtin/commit.c:688
+#: builtin/help.c:355
 #, c-format
-msgid "could not read log file '%s'"
+msgid "'%s': unknown man viewer."
 msgstr ""
 
-#: builtin/commit.c:694
-msgid "commit has empty message"
+#: builtin/help.c:372
+msgid "no man viewer handled the request"
 msgstr ""
 
-#: builtin/commit.c:710
-msgid "could not read MERGE_MSG"
+#: builtin/help.c:380
+msgid "no info viewer handled the request"
 msgstr ""
 
-#: builtin/commit.c:714
-msgid "could not read SQUASH_MSG"
+#: builtin/help.c:391
+#, c-format
+msgid "'%s': not a documentation directory."
 msgstr ""
 
-#: builtin/commit.c:718
+#: builtin/help.c:432 builtin/help.c:439
 #, c-format
-msgid "could not read '%s'"
+msgid "usage: %s%s"
 msgstr ""
 
-#: builtin/commit.c:746
+#: builtin/help.c:453
 #, c-format
-msgid "could not open '%s'"
+msgid "`git %s' is aliased to `%s'"
 msgstr ""
 
-#: builtin/commit.c:770
-msgid "could not write commit template"
+#: builtin/index-pack.c:169
+#, c-format
+msgid "object type mismatch at %s"
+msgstr ""
+
+#: builtin/index-pack.c:189
+msgid "object of unexpected type"
 msgstr ""
 
-#: builtin/commit.c:783
+#: builtin/index-pack.c:226
 #, c-format
-msgid ""
-"\n"
-"It looks like you may be committing a %s.\n"
-"If this is not correct, please remove the file\n"
-"\t%s\n"
-"and try again.\n"
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:236
+msgid "early EOF"
 msgstr ""
 
-#: builtin/commit.c:796
-msgid "Please enter the commit message for your changes."
+#: builtin/index-pack.c:237
+msgid "read error on input"
 msgstr ""
 
-#: builtin/commit.c:799
-msgid ""
-" Lines starting\n"
-"with '#' will be ignored, and an empty message aborts the commit.\n"
+#: builtin/index-pack.c:249
+msgid "used more bytes than were available"
 msgstr ""
 
-#: builtin/commit.c:804
-msgid ""
-" Lines starting\n"
-"with '#' will be kept; you may remove them yourself if you want to.\n"
-"An empty message aborts the commit.\n"
+#: builtin/index-pack.c:256
+msgid "pack too large for current definition of off_t"
 msgstr ""
 
-#: builtin/commit.c:816
+#: builtin/index-pack.c:272
 #, c-format
-msgid "%sAuthor:    %s"
+msgid "unable to create '%s'"
 msgstr ""
 
-#: builtin/commit.c:823
+#: builtin/index-pack.c:277
 #, c-format
-msgid "%sCommitter: %s"
+msgid "cannot open packfile '%s'"
 msgstr ""
 
-#: builtin/commit.c:843
-msgid "Cannot read index"
+#: builtin/index-pack.c:291
+msgid "pack signature mismatch"
 msgstr ""
 
-#: builtin/commit.c:880
-msgid "Error building trees"
+#: builtin/index-pack.c:311
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
 msgstr ""
 
-#: builtin/commit.c:895 builtin/tag.c:357
+#: builtin/index-pack.c:405
 #, c-format
-msgid "Please supply the message using either -m or -F option.\n"
+msgid "inflate returned %d"
 msgstr ""
 
-#: builtin/commit.c:975
-#, c-format
-msgid "No existing author found with '%s'"
+#: builtin/index-pack.c:450
+msgid "offset value overflow for delta base object"
 msgstr ""
 
-#: builtin/commit.c:990 builtin/commit.c:1182
-#, c-format
-msgid "Invalid untracked files mode '%s'"
+#: builtin/index-pack.c:458
+msgid "delta base offset is out of bound"
 msgstr ""
 
-#: builtin/commit.c:1030
-msgid "Using both --reset-author and --author does not make sense"
+#: builtin/index-pack.c:466
+#, c-format
+msgid "unknown object type %d"
 msgstr ""
 
-#: builtin/commit.c:1041
-msgid "You have nothing to amend."
+#: builtin/index-pack.c:495
+msgid "cannot pread pack file"
 msgstr ""
 
-#: builtin/commit.c:1043
+#: builtin/index-pack.c:497
 #, c-format
-msgid "You are in the middle of a %s -- cannot amend."
-msgstr ""
+msgid "premature end of pack file, %lu byte missing"
+msgid_plural "premature end of pack file, %lu bytes missing"
+msgstr[0] ""
+msgstr[1] ""
 
-#: builtin/commit.c:1045
-msgid "Options --squash and --fixup cannot be used together"
+#: builtin/index-pack.c:510
+msgid "serious inflate inconsistency"
 msgstr ""
 
-#: builtin/commit.c:1055
-msgid "Only one of -c/-C/-F/--fixup can be used."
+#: builtin/index-pack.c:583
+#, c-format
+msgid "cannot read existing object %s"
 msgstr ""
 
-#: builtin/commit.c:1057
-msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
+#: builtin/index-pack.c:586
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
 msgstr ""
 
-#: builtin/commit.c:1063
-msgid "--reset-author can be used only with -C, -c or --amend."
+#: builtin/index-pack.c:598
+#, c-format
+msgid "invalid blob object %s"
 msgstr ""
 
-#: builtin/commit.c:1080
-msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
+#: builtin/index-pack.c:610
+#, c-format
+msgid "invalid %s"
 msgstr ""
 
-#: builtin/commit.c:1082
-msgid "No paths with --include/--only does not make sense."
+#: builtin/index-pack.c:612
+msgid "Error in object"
 msgstr ""
 
-#: builtin/commit.c:1084
-msgid "Clever... amending the last one with dirty index."
+#: builtin/index-pack.c:614
+#, c-format
+msgid "Not all child objects of %s are reachable"
 msgstr ""
 
-#: builtin/commit.c:1086
-msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
+#: builtin/index-pack.c:687 builtin/index-pack.c:713
+msgid "failed to apply delta"
 msgstr ""
 
-#: builtin/commit.c:1096 builtin/tag.c:556
-#, c-format
-msgid "Invalid cleanup mode %s"
+#: builtin/index-pack.c:850
+msgid "Receiving objects"
 msgstr ""
 
-#: builtin/commit.c:1101
-msgid "Paths with -a does not make sense."
+#: builtin/index-pack.c:850
+msgid "Indexing objects"
 msgstr ""
 
-#: builtin/commit.c:1280
-msgid "couldn't look up newly created commit"
+#: builtin/index-pack.c:872
+msgid "pack is corrupted (SHA1 mismatch)"
 msgstr ""
 
-#: builtin/commit.c:1282
-msgid "could not parse newly created commit"
+#: builtin/index-pack.c:877
+msgid "cannot fstat packfile"
 msgstr ""
 
-#: builtin/commit.c:1323
-msgid "detached HEAD"
+#: builtin/index-pack.c:880
+msgid "pack has junk at the end"
 msgstr ""
 
-#: builtin/commit.c:1325
-msgid " (root-commit)"
+#: builtin/index-pack.c:903
+msgid "Resolving deltas"
 msgstr ""
 
-#: builtin/commit.c:1415
-msgid "could not parse HEAD commit"
+#: builtin/index-pack.c:954
+msgid "confusion beyond insanity"
 msgstr ""
 
-#: builtin/commit.c:1452 builtin/merge.c:509
+#: builtin/index-pack.c:973
 #, c-format
-msgid "could not open '%s' for reading"
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:998
+#, c-format
+msgid "unable to deflate appended object (%d)"
 msgstr ""
 
-#: builtin/commit.c:1459
+#: builtin/index-pack.c:1077
 #, c-format
-msgid "Corrupt MERGE_HEAD file (%s)"
+msgid "local object %s is corrupt"
 msgstr ""
 
-#: builtin/commit.c:1466
-msgid "could not read MERGE_MODE"
+#: builtin/index-pack.c:1101
+msgid "error while closing pack file"
 msgstr ""
 
-#: builtin/commit.c:1485
+#: builtin/index-pack.c:1114
 #, c-format
-msgid "could not read commit message: %s"
+msgid "cannot write keep file '%s'"
 msgstr ""
 
-#: builtin/commit.c:1499
+#: builtin/index-pack.c:1122
 #, c-format
-msgid "Aborting commit due to empty commit message.\n"
+msgid "cannot close written keep file '%s'"
 msgstr ""
 
-#: builtin/commit.c:1514 builtin/merge.c:935 builtin/merge.c:968
-msgid "failed to write commit object"
+#: builtin/index-pack.c:1135
+msgid "cannot store pack file"
 msgstr ""
 
-#: builtin/commit.c:1535
-msgid "cannot lock HEAD ref"
+#: builtin/index-pack.c:1146
+msgid "cannot store index file"
 msgstr ""
 
-#: builtin/commit.c:1539
-msgid "cannot update HEAD ref"
+#: builtin/index-pack.c:1247
+#, c-format
+msgid "Cannot open existing pack file '%s'"
 msgstr ""
 
-#: builtin/commit.c:1550
-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."
+#: builtin/index-pack.c:1249
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
 msgstr ""
 
-#: builtin/describe.c:234
+#: builtin/index-pack.c:1296
 #, c-format
-msgid "annotated tag %s not available"
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1303
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1330
+msgid "Cannot come back to cwd"
 msgstr ""
 
-#: builtin/describe.c:238
+#: builtin/index-pack.c:1374 builtin/index-pack.c:1377
+#: builtin/index-pack.c:1389 builtin/index-pack.c:1393
 #, c-format
-msgid "annotated tag %s has no embedded name"
+msgid "bad %s"
 msgstr ""
 
-#: builtin/describe.c:240
+#: builtin/index-pack.c:1407
+msgid "--fix-thin cannot be used without --stdin"
+msgstr ""
+
+#: builtin/index-pack.c:1411 builtin/index-pack.c:1421
 #, c-format
-msgid "tag '%s' is really '%s' here"
+msgid "packfile name '%s' does not end with '.pack'"
 msgstr ""
 
-#: builtin/describe.c:267
+#: builtin/index-pack.c:1430
+msgid "--verify with no packfile name given"
+msgstr ""
+
+#: builtin/init-db.c:35
 #, c-format
-msgid "Not a valid object name %s"
+msgid "Could not make %s writable by group"
 msgstr ""
 
-#: builtin/describe.c:270
+#: builtin/init-db.c:62
 #, c-format
-msgid "%s is not a valid '%s' object"
+msgid "insanely long template name %s"
 msgstr ""
 
-#: builtin/describe.c:287
+#: builtin/init-db.c:67
 #, c-format
-msgid "no tag exactly matches '%s'"
+msgid "cannot stat '%s'"
 msgstr ""
 
-#: builtin/describe.c:289
+#: builtin/init-db.c:73
 #, c-format
-msgid "searching to describe %s\n"
+msgid "cannot stat template '%s'"
 msgstr ""
 
-#: builtin/describe.c:329
+#: builtin/init-db.c:80
 #, c-format
-msgid "finished search at %s\n"
+msgid "cannot opendir '%s'"
 msgstr ""
 
-#: builtin/describe.c:353
+#: builtin/init-db.c:97
 #, c-format
-msgid ""
-"No annotated tags can describe '%s'.\n"
-"However, there were unannotated tags: try --tags."
+msgid "cannot readlink '%s'"
 msgstr ""
 
-#: builtin/describe.c:357
+#: builtin/init-db.c:99
 #, c-format
-msgid ""
-"No tags can describe '%s'.\n"
-"Try --always, or create some tags."
+msgid "insanely long symlink %s"
 msgstr ""
 
-#: builtin/describe.c:378
+#: builtin/init-db.c:102
 #, c-format
-msgid "traversed %lu commits\n"
+msgid "cannot symlink '%s' '%s'"
 msgstr ""
 
-#: builtin/describe.c:381
+#: builtin/init-db.c:106
 #, c-format
-msgid ""
-"more than %i tags found; listed %i most recent\n"
-"gave up search at %s\n"
+msgid "cannot copy '%s' to '%s'"
 msgstr ""
 
-#: builtin/describe.c:436
-msgid "--long is incompatible with --abbrev=0"
+#: builtin/init-db.c:110
+#, c-format
+msgid "ignoring template %s"
 msgstr ""
 
-#: builtin/describe.c:462
-msgid "No names found, cannot describe anything."
+#: builtin/init-db.c:133
+#, c-format
+msgid "insanely long template path %s"
 msgstr ""
 
-#: builtin/describe.c:482
-msgid "--dirty is incompatible with committishes"
+#: builtin/init-db.c:141
+#, c-format
+msgid "templates not found %s"
 msgstr ""
 
-#: builtin/diff.c:77
+#: builtin/init-db.c:154
 #, c-format
-msgid "'%s': not a regular file or symlink"
+msgid "not copying templates of a wrong format version %d from '%s'"
 msgstr ""
 
-#: builtin/diff.c:220
+#: builtin/init-db.c:192
 #, c-format
-msgid "invalid option: %s"
+msgid "insane git directory %s"
 msgstr ""
 
-#: builtin/diff.c:297
-msgid "Not a git repository"
+#: builtin/init-db.c:322 builtin/init-db.c:325
+#, c-format
+msgid "%s already exists"
 msgstr ""
 
-#: builtin/diff.c:347
+#: builtin/init-db.c:354
 #, c-format
-msgid "invalid object '%s' given."
+msgid "unable to handle file type %d"
 msgstr ""
 
-#: builtin/diff.c:352
+#: builtin/init-db.c:357
 #, c-format
-msgid "more than %d trees given: '%s'"
+msgid "unable to move %s to %s"
 msgstr ""
 
-#: builtin/diff.c:362
+#: builtin/init-db.c:362
 #, c-format
-msgid "more than two blobs given: '%s'"
+msgid "Could not create git link %s"
 msgstr ""
 
-#: builtin/diff.c:370
+#.
+#. * 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:419
 #, c-format
-msgid "unhandled object '%s' given."
+msgid "%s%s Git repository in %s%s\n"
 msgstr ""
 
-#: builtin/fetch.c:200
-msgid "Couldn't find remote ref HEAD"
+#: builtin/init-db.c:420
+msgid "Reinitialized existing"
+msgstr ""
+
+#: builtin/init-db.c:420
+msgid "Initialized empty"
+msgstr ""
+
+#: builtin/init-db.c:421
+msgid " shared"
+msgstr ""
+
+#: builtin/init-db.c:440
+msgid "cannot tell cwd"
 msgstr ""
 
-#: builtin/fetch.c:252
+#: builtin/init-db.c:521 builtin/init-db.c:528
 #, c-format
-msgid "object %s not found"
+msgid "cannot mkdir %s"
 msgstr ""
 
-#: builtin/fetch.c:258
-msgid "[up to date]"
+#: builtin/init-db.c:532
+#, c-format
+msgid "cannot chdir to %s"
 msgstr ""
 
-#: builtin/fetch.c:272
+#: builtin/init-db.c:554
 #, c-format
-msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
+msgid ""
+"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
+"dir=<directory>)"
 msgstr ""
 
-#: builtin/fetch.c:273 builtin/fetch.c:351
-msgid "[rejected]"
+#: builtin/init-db.c:578
+msgid "Cannot access current working directory"
 msgstr ""
 
-#: builtin/fetch.c:284
-msgid "[tag update]"
+#: builtin/init-db.c:585
+#, c-format
+msgid "Cannot access work tree '%s'"
+msgstr ""
+
+#: builtin/log.c:188
+#, c-format
+msgid "Final output: %d %s\n"
 msgstr ""
 
-#: builtin/fetch.c:286 builtin/fetch.c:313 builtin/fetch.c:331
-msgid "  (unable to update local ref)"
+#: builtin/log.c:401 builtin/log.c:489
+#, c-format
+msgid "Could not read object %s"
 msgstr ""
 
-#: builtin/fetch.c:298
-msgid "[new tag]"
+#: builtin/log.c:513
+#, c-format
+msgid "Unknown type: %d"
 msgstr ""
 
-#: builtin/fetch.c:302
-msgid "[new branch]"
+#: builtin/log.c:602
+msgid "format.headers without value"
 msgstr ""
 
-#: builtin/fetch.c:347
-msgid "unable to update local ref"
+#: builtin/log.c:675
+msgid "name of output directory is too long"
 msgstr ""
 
-#: builtin/fetch.c:347
-msgid "forced update"
+#: builtin/log.c:686
+#, c-format
+msgid "Cannot open patch file %s"
 msgstr ""
 
-#: builtin/fetch.c:353
-msgid "(non-fast-forward)"
+#: builtin/log.c:700
+msgid "Need exactly one range."
 msgstr ""
 
-#: builtin/fetch.c:384 builtin/fetch.c:676
-#, c-format
-msgid "cannot open %s: %s\n"
+#: builtin/log.c:708
+msgid "Not a range."
 msgstr ""
 
-#: builtin/fetch.c:393
-#, c-format
-msgid "%s did not send all necessary objects\n"
+#: builtin/log.c:745
+msgid "Could not extract email from committer identity."
 msgstr ""
 
-#: builtin/fetch.c:479
-#, c-format
-msgid "From %.*s\n"
+#: builtin/log.c:791
+msgid "Cover letter needs email format"
 msgstr ""
 
-#: builtin/fetch.c:490
+#: builtin/log.c:885
 #, c-format
-msgid ""
-"some local refs could not be updated; try running\n"
-" 'git remote prune %s' to remove any old, conflicting branches"
+msgid "insane in-reply-to: %s"
 msgstr ""
 
-#: builtin/fetch.c:540
-#, c-format
-msgid "   (%s will become dangling)\n"
+#: builtin/log.c:958
+msgid "Two output directories?"
 msgstr ""
 
-#: builtin/fetch.c:541
+#: builtin/log.c:1179
 #, c-format
-msgid "   (%s has become dangling)\n"
+msgid "bogus committer info %s"
 msgstr ""
 
-#: builtin/fetch.c:548
-msgid "[deleted]"
+#: builtin/log.c:1224
+msgid "-n and -k are mutually exclusive."
 msgstr ""
 
-#: builtin/fetch.c:549
-msgid "(none)"
+#: builtin/log.c:1226
+msgid "--subject-prefix and -k are mutually exclusive."
 msgstr ""
 
-#: builtin/fetch.c:666
-#, c-format
-msgid "Refusing to fetch into current branch %s of non-bare repository"
+#: builtin/log.c:1234
+msgid "--name-only does not make sense"
 msgstr ""
 
-#: builtin/fetch.c:700
-#, c-format
-msgid "Don't know how to fetch from %s"
+#: builtin/log.c:1236
+msgid "--name-status does not make sense"
 msgstr ""
 
-#: builtin/fetch.c:777
-#, c-format
-msgid "Option \"%s\" value \"%s\" is not valid for %s"
+#: builtin/log.c:1238
+msgid "--check does not make sense"
 msgstr ""
 
-#: builtin/fetch.c:780
-#, c-format
-msgid "Option \"%s\" is ignored for %s\n"
+#: builtin/log.c:1261
+msgid "standard output, or directory, which one?"
 msgstr ""
 
-#: builtin/fetch.c:879
+#: builtin/log.c:1263
 #, c-format
-msgid "Fetching %s\n"
+msgid "Could not create directory '%s'"
 msgstr ""
 
-#: builtin/fetch.c:881
-#, c-format
-msgid "Could not fetch %s"
+#: builtin/log.c:1416
+msgid "Failed to create output files"
 msgstr ""
 
-#: builtin/fetch.c:898
+#: builtin/log.c:1520
+#, c-format
 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:918
-msgid "You need to specify a tag name."
+"Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr ""
 
-#: builtin/fetch.c:970
-msgid "fetch --all does not take a repository argument"
+#: builtin/log.c:1536 builtin/log.c:1538 builtin/log.c:1550
+#, c-format
+msgid "Unknown commit %s"
 msgstr ""
 
-#: builtin/fetch.c:972
-msgid "fetch --all does not make sense with refspecs"
+#: builtin/merge.c:90
+msgid "switch `m' requires a value"
 msgstr ""
 
-#: builtin/fetch.c:983
+#: builtin/merge.c:127
 #, c-format
-msgid "No such remote or remote group: %s"
+msgid "Could not find merge strategy '%s'.\n"
 msgstr ""
 
-#: builtin/fetch.c:991
-msgid "Fetching a group and specifying refspecs does not make sense"
+#: builtin/merge.c:128
+#, c-format
+msgid "Available strategies are:"
 msgstr ""
 
-#: builtin/gc.c:63
+#: builtin/merge.c:133
 #, c-format
-msgid "Invalid %s: '%s'"
+msgid "Available custom strategies are:"
 msgstr ""
 
-#: builtin/gc.c:78
-msgid "Too many options specified"
+#: builtin/merge.c:240
+msgid "could not run stash."
 msgstr ""
 
-#: builtin/gc.c:103
-#, c-format
-msgid "insanely long object directory %.*s"
+#: builtin/merge.c:245
+msgid "stash failed"
 msgstr ""
 
-#: builtin/gc.c:223
+#: builtin/merge.c:250
 #, c-format
-msgid "Auto packing the repository for optimum performance.\n"
+msgid "not a valid object: %s"
 msgstr ""
 
-#: builtin/gc.c:226
-#, 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"
+#: builtin/merge.c:269 builtin/merge.c:286
+msgid "read-tree failed"
 msgstr ""
 
-#: builtin/gc.c:256
-msgid ""
-"There are too many unreachable loose objects; run 'git prune' to remove them."
+#: builtin/merge.c:316
+msgid " (nothing to squash)"
 msgstr ""
 
-#: builtin/grep.c:216
+#: builtin/merge.c:329
 #, c-format
-msgid "grep: failed to create thread: %s"
+msgid "Squash commit -- not updating HEAD\n"
 msgstr ""
 
-#: builtin/grep.c:402
-#, c-format
-msgid "Failed to chdir: %s"
+#: builtin/merge.c:361
+msgid "Writing SQUASH_MSG"
 msgstr ""
 
-#: builtin/grep.c:478 builtin/grep.c:512
-#, c-format
-msgid "unable to read tree (%s)"
+#: builtin/merge.c:363
+msgid "Finishing SQUASH_MSG"
 msgstr ""
 
-#: builtin/grep.c:526
+#: builtin/merge.c:386
 #, c-format
-msgid "unable to grep from object of type %s"
+msgid "No merge message -- not updating HEAD\n"
 msgstr ""
 
-#: builtin/grep.c:584
+#: builtin/merge.c:437
 #, c-format
-msgid "switch `%c' expects a numerical value"
+msgid "'%s' does not point to a commit"
 msgstr ""
 
-#: builtin/grep.c:601
+#: builtin/merge.c:536
 #, c-format
-msgid "cannot open '%s'"
+msgid "Bad branch.%s.mergeoptions string: %s"
 msgstr ""
 
-#: builtin/grep.c:888
-msgid "no pattern given."
+#: builtin/merge.c:629
+msgid "git write-tree failed to write a tree"
 msgstr ""
 
-#: builtin/grep.c:902
-#, c-format
-msgid "bad object %s"
+#: builtin/merge.c:679
+msgid "failed to read the cache"
 msgstr ""
 
-#: builtin/grep.c:943
-msgid "--open-files-in-pager only works on the worktree"
+#: builtin/merge.c:697
+msgid "Unable to write index."
 msgstr ""
 
-#: builtin/grep.c:966
-msgid "--cached or --untracked cannot be used with --no-index."
+#: builtin/merge.c:710
+msgid "Not handling anything other than two heads merge."
 msgstr ""
 
-#: builtin/grep.c:971
-msgid "--no-index or --untracked cannot be used with revs."
+#: builtin/merge.c:724
+#, c-format
+msgid "Unknown option for merge-recursive: -X%s"
 msgstr ""
 
-#: builtin/grep.c:974
-msgid "--[no-]exclude-standard cannot be used for tracked contents."
+#: builtin/merge.c:738
+#, c-format
+msgid "unable to write %s"
 msgstr ""
 
-#: builtin/grep.c:982
-msgid "both --cached and trees are given."
+#: builtin/merge.c:877
+#, c-format
+msgid "Could not read from '%s'"
 msgstr ""
 
-#: builtin/init-db.c:35
+#: builtin/merge.c:886
 #, c-format
-msgid "Could not make %s writable by group"
+msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr ""
 
-#: builtin/init-db.c:62
-#, c-format
-msgid "insanely long template name %s"
+#: builtin/merge.c:892
+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 '#' will be ignored, and an empty message aborts\n"
+"the commit.\n"
 msgstr ""
 
-#: builtin/init-db.c:67
-#, c-format
-msgid "cannot stat '%s'"
+#: builtin/merge.c:916
+msgid "Empty commit message."
 msgstr ""
 
-#: builtin/init-db.c:73
+#: builtin/merge.c:928
 #, c-format
-msgid "cannot stat template '%s'"
+msgid "Wonderful.\n"
 msgstr ""
 
-#: builtin/init-db.c:80
+#: builtin/merge.c:993
 #, c-format
-msgid "cannot opendir '%s'"
+msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
 msgstr ""
 
-#: builtin/init-db.c:97
+#: builtin/merge.c:1009
 #, c-format
-msgid "cannot readlink '%s'"
+msgid "'%s' is not a commit"
 msgstr ""
 
-#: builtin/init-db.c:99
-#, c-format
-msgid "insanely long symlink %s"
+#: builtin/merge.c:1050
+msgid "No current branch."
 msgstr ""
 
-#: builtin/init-db.c:102
-#, c-format
-msgid "cannot symlink '%s' '%s'"
+#: builtin/merge.c:1052
+msgid "No remote for the current branch."
 msgstr ""
 
-#: builtin/init-db.c:106
-#, c-format
-msgid "cannot copy '%s' to '%s'"
+#: builtin/merge.c:1054
+msgid "No default upstream defined for the current branch."
 msgstr ""
 
-#: builtin/init-db.c:110
+#: builtin/merge.c:1059
 #, c-format
-msgid "ignoring template %s"
+msgid "No remote tracking branch for %s from %s"
 msgstr ""
 
-#: builtin/init-db.c:133
+#: builtin/merge.c:1146 builtin/merge.c:1303
 #, c-format
-msgid "insanely long template path %s"
+msgid "%s - not something we can merge"
 msgstr ""
 
-#: builtin/init-db.c:141
-#, c-format
-msgid "templates not found %s"
+#: builtin/merge.c:1214
+msgid "There is no merge to abort (MERGE_HEAD missing)."
 msgstr ""
 
-#: builtin/init-db.c:154
-#, c-format
-msgid "not copying templates of a wrong format version %d from '%s'"
+#: builtin/merge.c:1230 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/init-db.c:192
-#, c-format
-msgid "insane git directory %s"
+#: builtin/merge.c:1233 git-pull.sh:34
+msgid "You have not concluded your merge (MERGE_HEAD exists)."
 msgstr ""
 
-#: builtin/init-db.c:322 builtin/init-db.c:325
-#, c-format
-msgid "%s already exists"
+#: builtin/merge.c:1237
+msgid ""
+"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
+"Please, commit your changes before you can merge."
 msgstr ""
 
-#: builtin/init-db.c:354
-#, c-format
-msgid "unable to handle file type %d"
+#: builtin/merge.c:1240
+msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
 msgstr ""
 
-#: builtin/init-db.c:357
-#, c-format
-msgid "unable to move %s to %s"
+#: builtin/merge.c:1249
+msgid "You cannot combine --squash with --no-ff."
 msgstr ""
 
-#: builtin/init-db.c:362
-#, c-format
-msgid "Could not create git link %s"
+#: builtin/merge.c:1254
+msgid "You cannot combine --no-ff with --ff-only."
 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:419
-#, c-format
-msgid "%s%s Git repository in %s%s\n"
+#: builtin/merge.c:1261
+msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr ""
 
-#: builtin/init-db.c:420
-msgid "Reinitialized existing"
+#: builtin/merge.c:1293
+msgid "Can merge only exactly one commit into empty head"
 msgstr ""
 
-#: builtin/init-db.c:420
-msgid "Initialized empty"
+#: builtin/merge.c:1296
+msgid "Squash commit into empty head not supported yet"
 msgstr ""
 
-#: builtin/init-db.c:421
-msgid " shared"
+#: builtin/merge.c:1298
+msgid "Non-fast-forward commit does not make sense into an empty head"
 msgstr ""
 
-#: builtin/init-db.c:440
-msgid "cannot tell cwd"
+#: builtin/merge.c:1413
+#, c-format
+msgid "Updating %s..%s\n"
 msgstr ""
 
-#: builtin/init-db.c:521 builtin/init-db.c:528
+#: builtin/merge.c:1451
 #, c-format
-msgid "cannot mkdir %s"
+msgid "Trying really trivial in-index merge...\n"
 msgstr ""
 
-#: builtin/init-db.c:532
+#: builtin/merge.c:1458
 #, c-format
-msgid "cannot chdir to %s"
+msgid "Nope.\n"
 msgstr ""
 
-#: builtin/init-db.c:554
+#: builtin/merge.c:1490
+msgid "Not possible to fast-forward, aborting."
+msgstr ""
+
+#: builtin/merge.c:1513 builtin/merge.c:1592
 #, c-format
-msgid ""
-"%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
-"dir=<directory>)"
+msgid "Rewinding the tree to pristine...\n"
 msgstr ""
 
-#: builtin/init-db.c:578
-msgid "Cannot access current working directory"
+#: builtin/merge.c:1517
+#, c-format
+msgid "Trying merge strategy %s...\n"
+msgstr ""
+
+#: builtin/merge.c:1583
+#, c-format
+msgid "No merge strategy handled the merge.\n"
 msgstr ""
 
-#: builtin/init-db.c:585
+#: builtin/merge.c:1585
 #, c-format
-msgid "Cannot access work tree '%s'"
+msgid "Merge with strategy %s failed.\n"
 msgstr ""
 
-#: builtin/log.c:187
+#: builtin/merge.c:1594
 #, c-format
-msgid "Final output: %d %s\n"
+msgid "Using the %s to prepare resolving by hand.\n"
 msgstr ""
 
-#: builtin/log.c:395 builtin/log.c:483
+#: builtin/merge.c:1606
 #, c-format
-msgid "Could not read object %s"
+msgid "Automatic merge went well; stopped before committing as requested\n"
 msgstr ""
 
-#: builtin/log.c:507
+#: builtin/mv.c:108
 #, c-format
-msgid "Unknown type: %d"
+msgid "Checking rename of '%s' to '%s'\n"
 msgstr ""
 
-#: builtin/log.c:596
-msgid "format.headers without value"
+#: builtin/mv.c:112
+msgid "bad source"
 msgstr ""
 
-#: builtin/log.c:669
-msgid "name of output directory is too long"
+#: builtin/mv.c:115
+msgid "can not move directory into itself"
 msgstr ""
 
-#: builtin/log.c:680
-#, c-format
-msgid "Cannot open patch file %s"
+#: builtin/mv.c:118
+msgid "cannot move directory over file"
 msgstr ""
 
-#: builtin/log.c:694
-msgid "Need exactly one range."
+#: builtin/mv.c:128
+#, c-format
+msgid "Huh? %.*s is in index?"
 msgstr ""
 
-#: builtin/log.c:702
-msgid "Not a range."
+#: builtin/mv.c:140
+msgid "source directory is empty"
 msgstr ""
 
-#: builtin/log.c:739
-msgid "Could not extract email from committer identity."
+#: builtin/mv.c:171
+msgid "not under version control"
 msgstr ""
 
-#: builtin/log.c:785
-msgid "Cover letter needs email format"
+#: builtin/mv.c:173
+msgid "destination exists"
 msgstr ""
 
-#: builtin/log.c:879
+#: builtin/mv.c:181
 #, c-format
-msgid "insane in-reply-to: %s"
+msgid "overwriting '%s'"
 msgstr ""
 
-#: builtin/log.c:952
-msgid "Two output directories?"
+#: builtin/mv.c:184
+msgid "Cannot overwrite"
 msgstr ""
 
-#: builtin/log.c:1173
-#, c-format
-msgid "bogus committer info %s"
+#: builtin/mv.c:187
+msgid "multiple sources for the same target"
 msgstr ""
 
-#: builtin/log.c:1218
-msgid "-n and -k are mutually exclusive."
+#: builtin/mv.c:202
+#, c-format
+msgid "%s, source=%s, destination=%s"
 msgstr ""
 
-#: builtin/log.c:1220
-msgid "--subject-prefix and -k are mutually exclusive."
+#: builtin/mv.c:212
+#, c-format
+msgid "Renaming %s to %s\n"
 msgstr ""
 
-#: builtin/log.c:1225 builtin/shortlog.c:284
+#: builtin/mv.c:215 builtin/remote.c:731
 #, c-format
-msgid "unrecognized argument: %s"
+msgid "renaming '%s' failed"
 msgstr ""
 
-#: builtin/log.c:1228
-msgid "--name-only does not make sense"
+#: builtin/notes.c:139
+#, c-format
+msgid "unable to start 'show' for object '%s'"
 msgstr ""
 
-#: builtin/log.c:1230
-msgid "--name-status does not make sense"
+#: builtin/notes.c:145
+msgid "can't fdopen 'show' output fd"
 msgstr ""
 
-#: builtin/log.c:1232
-msgid "--check does not make sense"
+#: builtin/notes.c:155
+#, c-format
+msgid "failed to close pipe to 'show' for object '%s'"
 msgstr ""
 
-#: builtin/log.c:1255
-msgid "standard output, or directory, which one?"
+#: builtin/notes.c:158
+#, c-format
+msgid "failed to finish 'show' for object '%s'"
 msgstr ""
 
-#: builtin/log.c:1257
+#: builtin/notes.c:175 builtin/tag.c:347
 #, c-format
-msgid "Could not create directory '%s'"
+msgid "could not create file '%s'"
 msgstr ""
 
-#: builtin/log.c:1410
-msgid "Failed to create output files"
+#: builtin/notes.c:189
+msgid "Please supply the note contents using either -m or -F option"
 msgstr ""
 
-#: builtin/log.c:1514
+#: builtin/notes.c:210 builtin/notes.c:973
 #, c-format
-msgid ""
-"Could not find a tracked remote branch, please specify <upstream> manually.\n"
+msgid "Removing note for object %s\n"
 msgstr ""
 
-#: builtin/log.c:1530 builtin/log.c:1532 builtin/log.c:1544
-#, c-format
-msgid "Unknown commit %s"
+#: builtin/notes.c:215
+msgid "unable to write note object"
 msgstr ""
 
-#: builtin/merge.c:91
-msgid "switch `m' requires a value"
+#: builtin/notes.c:217
+#, c-format
+msgid "The note contents has been left in %s"
 msgstr ""
 
-#: builtin/merge.c:128
+#: builtin/notes.c:251 builtin/tag.c:542
 #, c-format
-msgid "Could not find merge strategy '%s'.\n"
+msgid "cannot read '%s'"
 msgstr ""
 
-#: builtin/merge.c:129
+#: builtin/notes.c:253 builtin/tag.c:545
 #, c-format
-msgid "Available strategies are:"
+msgid "could not open or read '%s'"
 msgstr ""
 
-#: builtin/merge.c:134
+#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
+#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
+#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
+#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:558
 #, c-format
-msgid "Available custom strategies are:"
+msgid "Failed to resolve '%s' as a valid ref."
 msgstr ""
 
-#: builtin/merge.c:241
-msgid "could not run stash."
+#: builtin/notes.c:275
+#, c-format
+msgid "Failed to read object '%s'."
 msgstr ""
 
-#: builtin/merge.c:246
-msgid "stash failed"
+#: builtin/notes.c:299
+msgid "Cannot commit uninitialized/unreferenced notes tree"
 msgstr ""
 
-#: builtin/merge.c:251
+#: builtin/notes.c:340
 #, c-format
-msgid "not a valid object: %s"
+msgid "Bad notes.rewriteMode value: '%s'"
 msgstr ""
 
-#: builtin/merge.c:270 builtin/merge.c:287
-msgid "read-tree failed"
+#: builtin/notes.c:350
+#, c-format
+msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
 msgstr ""
 
-#: builtin/merge.c:317
-msgid " (nothing to squash)"
+#. TRANSLATORS: The first %s is the name of the
+#. environment variable, the second %s is its value
+#: builtin/notes.c:377
+#, c-format
+msgid "Bad %s value: '%s'"
 msgstr ""
 
-#: builtin/merge.c:330
+#: builtin/notes.c:441
 #, c-format
-msgid "Squash commit -- not updating HEAD\n"
+msgid "Malformed input line: '%s'."
 msgstr ""
 
-#: builtin/merge.c:362
-msgid "Writing SQUASH_MSG"
+#: builtin/notes.c:456
+#, c-format
+msgid "Failed to copy notes from '%s' to '%s'"
 msgstr ""
 
-#: builtin/merge.c:364
-msgid "Finishing SQUASH_MSG"
+#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
+#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
+#: builtin/notes.c:1033
+msgid "too many parameters"
 msgstr ""
 
-#: builtin/merge.c:386
+#: builtin/notes.c:513 builtin/notes.c:772
 #, c-format
-msgid "No merge message -- not updating HEAD\n"
+msgid "No note found for object %s."
 msgstr ""
 
-#: builtin/merge.c:437
+#: builtin/notes.c:580
 #, c-format
-msgid "'%s' does not point to a commit"
+msgid ""
+"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
 msgstr ""
 
-#: builtin/merge.c:536
+#: builtin/notes.c:585 builtin/notes.c:662
 #, c-format
-msgid "Bad branch.%s.mergeoptions string: %s"
+msgid "Overwriting existing notes for object %s\n"
 msgstr ""
 
-#: builtin/merge.c:629
-msgid "git write-tree failed to write a tree"
+#: builtin/notes.c:635
+msgid "too few parameters"
 msgstr ""
 
-#: builtin/merge.c:679
-msgid "failed to read the cache"
+#: builtin/notes.c:656
+#, c-format
+msgid ""
+"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
+"existing notes"
 msgstr ""
 
-#: builtin/merge.c:696
-msgid "Unable to write index."
+#: builtin/notes.c:668
+#, c-format
+msgid "Missing notes on source object %s. Cannot copy."
 msgstr ""
 
-#: builtin/merge.c:709
-msgid "Not handling anything other than two heads merge."
+#: builtin/notes.c:717
+#, 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/merge.c:723
+#: builtin/notes.c:971
 #, c-format
-msgid "Unknown option for merge-recursive: -X%s"
+msgid "Object %s has no note\n"
 msgstr ""
 
-#: builtin/merge.c:737
+#: builtin/notes.c:1103 builtin/remote.c:1598
 #, c-format
-msgid "unable to write %s"
+msgid "Unknown subcommand: %s"
 msgstr ""
 
-#: builtin/merge.c:876
+#: builtin/pack-objects.c:2315
 #, c-format
-msgid "Could not read from '%s'"
+msgid "unsupported index version %s"
 msgstr ""
 
-#: builtin/merge.c:885
+#: builtin/pack-objects.c:2319
 #, c-format
-msgid "Not committing merge; use 'git commit' to complete the merge.\n"
+msgid "bad index version '%s'"
 msgstr ""
 
-#: builtin/merge.c:891
-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 '#' will be ignored, and an empty message aborts\n"
-"the commit.\n"
+#: builtin/pack-objects.c:2342
+#, c-format
+msgid "option %s does not accept negative form"
 msgstr ""
 
-#: builtin/merge.c:915
-msgid "Empty commit message."
+#: builtin/pack-objects.c:2346
+#, c-format
+msgid "unable to parse value '%s' for option %s"
 msgstr ""
 
-#: builtin/merge.c:927
-#, c-format
-msgid "Wonderful.\n"
+#: builtin/push.c:45
+msgid "tag shorthand without <tag>"
 msgstr ""
 
-#: builtin/merge.c:1000
-#, c-format
-msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
+#: 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/merge.c:1016
+#: builtin/push.c:102
 #, c-format
-msgid "'%s' is not a commit"
+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/merge.c:1057
-msgid "No current branch."
+#: 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/merge.c:1059
-msgid "No remote for the current branch."
+#: 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/merge.c:1061
-msgid "No default upstream defined for the current branch."
+#: builtin/push.c:136
+#, c-format
+msgid "The current branch %s has multiple upstream branches, refusing to push."
 msgstr ""
 
-#: builtin/merge.c:1066
+#: builtin/push.c:139
 #, c-format
-msgid "No remote tracking branch for %s from %s"
+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/merge.c:1188
-msgid "There is no merge to abort (MERGE_HEAD missing)."
+#: builtin/push.c:174
+msgid ""
+"You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
 
-#: builtin/merge.c:1204 git-pull.sh:31
+#: builtin/push.c:181
 msgid ""
-"You have not concluded your merge (MERGE_HEAD exists).\n"
-"Please, commit your changes before you can merge."
+"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/merge.c:1207 git-pull.sh:34
-msgid "You have not concluded your merge (MERGE_HEAD exists)."
+#: builtin/push.c:187
+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\n"
+"variable to 'current' or 'upstream' to push only the current branch."
 msgstr ""
 
-#: builtin/merge.c:1211
+#: builtin/push.c:193
 msgid ""
-"You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
-"Please, commit your changes before you can merge."
+"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/merge.c:1214
-msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
+#: builtin/push.c:233
+#, c-format
+msgid "Pushing to %s\n"
 msgstr ""
 
-#: builtin/merge.c:1223
-msgid "You cannot combine --squash with --no-ff."
+#: builtin/push.c:237
+#, c-format
+msgid "failed to push some refs to '%s'"
 msgstr ""
 
-#: builtin/merge.c:1228
-msgid "You cannot combine --no-ff with --ff-only."
+#: builtin/push.c:269
+#, c-format
+msgid "bad repository '%s'"
 msgstr ""
 
-#: builtin/merge.c:1235
-msgid "No commit specified and merge.defaultToUpstream not set."
+#: builtin/push.c:270
+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/merge.c:1266
-msgid "Can merge only exactly one commit into empty head"
+#: builtin/push.c:285
+msgid "--all and --tags are incompatible"
+msgstr ""
+
+#: builtin/push.c:286
+msgid "--all can't be combined with refspecs"
 msgstr ""
 
-#: builtin/merge.c:1269
-msgid "Squash commit into empty head not supported yet"
+#: builtin/push.c:291
+msgid "--mirror and --tags are incompatible"
 msgstr ""
 
-#: builtin/merge.c:1271
-msgid "Non-fast-forward commit does not make sense into an empty head"
+#: builtin/push.c:292
+msgid "--mirror can't be combined with refspecs"
 msgstr ""
 
-#: builtin/merge.c:1275 builtin/merge.c:1319
-#, c-format
-msgid "%s - not something we can merge"
+#: builtin/push.c:297
+msgid "--all and --mirror are incompatible"
 msgstr ""
 
-#: builtin/merge.c:1385
-#, c-format
-msgid "Updating %s..%s\n"
+#: builtin/push.c:385
+msgid "--delete is incompatible with --all, --mirror and --tags"
 msgstr ""
 
-#: builtin/merge.c:1423
-#, c-format
-msgid "Trying really trivial in-index merge...\n"
+#: builtin/push.c:387
+msgid "--delete doesn't make sense without any refs"
 msgstr ""
 
-#: builtin/merge.c:1430
+#: builtin/remote.c:98
 #, c-format
-msgid "Nope.\n"
+msgid "Updating %s"
 msgstr ""
 
-#: builtin/merge.c:1462
-msgid "Not possible to fast-forward, aborting."
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
 msgstr ""
 
-#: builtin/merge.c:1485 builtin/merge.c:1562
+#: builtin/remote.c:147
 #, c-format
-msgid "Rewinding the tree to pristine...\n"
+msgid "unknown mirror argument: %s"
 msgstr ""
 
-#: builtin/merge.c:1489
-#, c-format
-msgid "Trying merge strategy %s...\n"
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
 msgstr ""
 
-#: builtin/merge.c:1553
-#, c-format
-msgid "No merge strategy handled the merge.\n"
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
 msgstr ""
 
-#: builtin/merge.c:1555
+#: builtin/remote.c:195 builtin/remote.c:646
 #, c-format
-msgid "Merge with strategy %s failed.\n"
+msgid "remote %s already exists."
 msgstr ""
 
-#: builtin/merge.c:1564
+#: builtin/remote.c:199 builtin/remote.c:650
 #, c-format
-msgid "Using the %s to prepare resolving by hand.\n"
+msgid "'%s' is not a valid remote name"
 msgstr ""
 
-#: builtin/merge.c:1575
+#: builtin/remote.c:243
 #, c-format
-msgid "Automatic merge went well; stopped before committing as requested\n"
+msgid "Could not setup master '%s'"
 msgstr ""
 
-#: builtin/mv.c:108
+#: builtin/remote.c:299
 #, c-format
-msgid "Checking rename of '%s' to '%s'\n"
+msgid "more than one %s"
 msgstr ""
 
-#: builtin/mv.c:112
-msgid "bad source"
+#: builtin/remote.c:339
+#, c-format
+msgid "Could not get fetch map for refspec %s"
 msgstr ""
 
-#: builtin/mv.c:115
-msgid "can not move directory into itself"
+#: builtin/remote.c:440 builtin/remote.c:448
+msgid "(matching)"
 msgstr ""
 
-#: builtin/mv.c:118
-msgid "cannot move directory over file"
+#: builtin/remote.c:452
+msgid "(delete)"
 msgstr ""
 
-#: builtin/mv.c:128
+#: builtin/remote.c:595 builtin/remote.c:601 builtin/remote.c:607
 #, c-format
-msgid "Huh? %.*s is in index?"
-msgstr ""
-
-#: builtin/mv.c:140
-msgid "source directory is empty"
+msgid "Could not append '%s' to '%s'"
 msgstr ""
 
-#: builtin/mv.c:171
-msgid "not under version control"
+#: builtin/remote.c:639 builtin/remote.c:792 builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
 msgstr ""
 
-#: builtin/mv.c:173
-msgid "destination exists"
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
 msgstr ""
 
-#: builtin/mv.c:181
+#: builtin/remote.c:662 builtin/remote.c:799
 #, c-format
-msgid "overwriting '%s'"
+msgid "Could not remove config section '%s'"
 msgstr ""
 
-#: builtin/mv.c:184
-msgid "Cannot overwrite"
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch respec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
 msgstr ""
 
-#: builtin/mv.c:187
-msgid "multiple sources for the same target"
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
 msgstr ""
 
-#: builtin/mv.c:202
+#: builtin/remote.c:694
 #, c-format
-msgid "%s, source=%s, destination=%s"
+msgid "Could not set '%s'"
 msgstr ""
 
-#: builtin/mv.c:212
+#: builtin/remote.c:716
 #, c-format
-msgid "Renaming %s to %s\n"
+msgid "deleting '%s' failed"
 msgstr ""
 
-#: builtin/mv.c:215
+#: builtin/remote.c:750
 #, c-format
-msgid "renaming '%s' failed"
+msgid "creating '%s' failed"
 msgstr ""
 
-#: builtin/notes.c:139
+#: builtin/remote.c:764
 #, c-format
-msgid "unable to start 'show' for object '%s'"
+msgid "Could not remove branch %s"
 msgstr ""
 
-#: builtin/notes.c:145
-msgid "can't fdopen 'show' output fd"
-msgstr ""
+#: 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] ""
+msgstr[1] ""
 
-#: builtin/notes.c:155
+#: builtin/remote.c:943
 #, c-format
-msgid "failed to close pipe to 'show' for object '%s'"
+msgid " new (next fetch will store in remotes/%s)"
 msgstr ""
 
-#: builtin/notes.c:158
-#, c-format
-msgid "failed to finish 'show' for object '%s'"
+#: builtin/remote.c:946
+msgid " tracked"
 msgstr ""
 
-#: builtin/notes.c:175 builtin/tag.c:343
-#, c-format
-msgid "could not create file '%s'"
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
 msgstr ""
 
-#: builtin/notes.c:189
-msgid "Please supply the note contents using either -m or -F option"
+#: builtin/remote.c:950
+msgid " ???"
 msgstr ""
 
-#: builtin/notes.c:210 builtin/notes.c:973
+#: builtin/remote.c:991
 #, c-format
-msgid "Removing note for object %s\n"
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
 msgstr ""
 
-#: builtin/notes.c:215
-msgid "unable to write note object"
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
 msgstr ""
 
-#: builtin/notes.c:217
+#: builtin/remote.c:1001
 #, c-format
-msgid "The note contents has been left in %s"
+msgid " merges with remote %s"
 msgstr ""
 
-#: builtin/notes.c:251 builtin/tag.c:521
-#, c-format
-msgid "cannot read '%s'"
+#: builtin/remote.c:1002
+msgid "    and with remote"
 msgstr ""
 
-#: builtin/notes.c:253 builtin/tag.c:524
+#: builtin/remote.c:1004
 #, c-format
-msgid "could not open or read '%s'"
+msgid "merges with remote %s"
 msgstr ""
 
-#: builtin/notes.c:272 builtin/notes.c:445 builtin/notes.c:447
-#: builtin/notes.c:507 builtin/notes.c:561 builtin/notes.c:644
-#: builtin/notes.c:649 builtin/notes.c:724 builtin/notes.c:766
-#: builtin/notes.c:968 builtin/reset.c:293 builtin/tag.c:537
-#, c-format
-msgid "Failed to resolve '%s' as a valid ref."
+#: builtin/remote.c:1005
+msgid "   and with remote"
 msgstr ""
 
-#: builtin/notes.c:275
-#, c-format
-msgid "Failed to read object '%s'."
+#: builtin/remote.c:1051
+msgid "create"
 msgstr ""
 
-#: builtin/notes.c:299
-msgid "Cannot commit uninitialized/unreferenced notes tree"
+#: builtin/remote.c:1054
+msgid "delete"
 msgstr ""
 
-#: builtin/notes.c:340
-#, c-format
-msgid "Bad notes.rewriteMode value: '%s'"
+#: builtin/remote.c:1058
+msgid "up to date"
 msgstr ""
 
-#: builtin/notes.c:350
-#, c-format
-msgid "Refusing to rewrite notes in %s (outside of refs/notes/)"
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
 msgstr ""
 
-#. TRANSLATORS: The first %s is the name of the
-#. environment variable, the second %s is its value
-#: builtin/notes.c:377
-#, c-format
-msgid "Bad %s value: '%s'"
+#: builtin/remote.c:1064
+msgid "local out of date"
 msgstr ""
 
-#: builtin/notes.c:441
+#: builtin/remote.c:1071
 #, c-format
-msgid "Malformed input line: '%s'."
+msgid "    %-*s forces to %-*s (%s)"
 msgstr ""
 
-#: builtin/notes.c:456
+#: builtin/remote.c:1074
 #, c-format
-msgid "Failed to copy notes from '%s' to '%s'"
+msgid "    %-*s pushes to %-*s (%s)"
 msgstr ""
 
-#: builtin/notes.c:500 builtin/notes.c:554 builtin/notes.c:627
-#: builtin/notes.c:639 builtin/notes.c:712 builtin/notes.c:759
-#: builtin/notes.c:1033
-msgid "too many parameters"
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
 msgstr ""
 
-#: builtin/notes.c:513 builtin/notes.c:772
+#: builtin/remote.c:1081
 #, c-format
-msgid "No note found for object %s."
+msgid "    %-*s pushes to %s"
 msgstr ""
 
-#: builtin/notes.c:580
+#: builtin/remote.c:1118
 #, c-format
-msgid ""
-"Cannot add notes. Found existing notes for object %s. Use '-f' to overwrite "
-"existing notes"
+msgid "* remote %s"
 msgstr ""
 
-#: builtin/notes.c:585 builtin/notes.c:662
+#: builtin/remote.c:1119
 #, c-format
-msgid "Overwriting existing notes for object %s\n"
+msgid "  Fetch URL: %s"
 msgstr ""
 
-#: builtin/notes.c:635
-msgid "too few parameters"
+#: builtin/remote.c:1120 builtin/remote.c:1285
+msgid "(no URL)"
 msgstr ""
 
-#: builtin/notes.c:656
+#: builtin/remote.c:1129 builtin/remote.c:1131
 #, c-format
-msgid ""
-"Cannot copy notes. Found existing notes for object %s. Use '-f' to overwrite "
-"existing notes"
+msgid "  Push  URL: %s"
 msgstr ""
 
-#: builtin/notes.c:668
+#: builtin/remote.c:1133 builtin/remote.c:1135 builtin/remote.c:1137
 #, c-format
-msgid "Missing notes on source object %s. Cannot copy."
+msgid "  HEAD branch: %s"
 msgstr ""
 
-#: builtin/notes.c:717
+#: builtin/remote.c:1139
 #, 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:971
-#, c-format
-msgid "Object %s has no note\n"
+"  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
 msgstr ""
 
-#: builtin/notes.c:1103
+#: builtin/remote.c:1151
 #, c-format
-msgid "Unknown subcommand: %s"
-msgstr ""
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] ""
+msgstr[1] ""
 
-#: builtin/pack-objects.c:2310
-#, c-format
-msgid "unsupported index version %s"
+#: builtin/remote.c:1154 builtin/remote.c:1181
+msgid " (status not queried)"
 msgstr ""
 
-#: builtin/pack-objects.c:2314
-#, c-format
-msgid "bad index version '%s'"
-msgstr ""
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] ""
+msgstr[1] ""
 
-#: builtin/pack-objects.c:2322
-#, c-format
-msgid "option %s does not accept negative form"
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
 msgstr ""
 
-#: builtin/pack-objects.c:2326
+#: builtin/remote.c:1178
 #, c-format
-msgid "unable to parse value '%s' for option %s"
-msgstr ""
+msgid "  Local ref configured for 'git push'%s:"
+msgid_plural "  Local refs configured for 'git push'%s:"
+msgstr[0] ""
+msgstr[1] ""
 
-#: builtin/push.c:44
-msgid "tag shorthand without <tag>"
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
 msgstr ""
 
-#: builtin/push.c:63
-msgid "--delete only accepts plain target ref names"
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
 msgstr ""
 
-#: builtin/push.c:73
+#: builtin/remote.c:1228
 #, 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"
+msgid "Could not delete %s"
 msgstr ""
 
-#: builtin/push.c:80
+#: builtin/remote.c:1236
 #, 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"
+msgid "Not a valid ref: %s"
 msgstr ""
 
-#: builtin/push.c:88
+#: builtin/remote.c:1238
 #, c-format
-msgid "The current branch %s has multiple upstream branches, refusing to push."
-msgstr ""
-
-#: builtin/push.c:111
-msgid ""
-"You didn't specify any refspecs to push, and push.default is \"nothing\"."
+msgid "Could not setup %s"
 msgstr ""
 
-#: builtin/push.c:131
+#: builtin/remote.c:1274
 #, c-format
-msgid "Pushing to %s\n"
+msgid " %s will become dangling!"
 msgstr ""
 
-#: builtin/push.c:135
+#: builtin/remote.c:1275
 #, c-format
-msgid "failed to push some refs to '%s'"
+msgid " %s has become dangling!"
 msgstr ""
 
-#: builtin/push.c:143
+#: builtin/remote.c:1281
 #, c-format
-msgid ""
-"To prevent you from losing history, non-fast-forward updates were rejected\n"
-"Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
-"'Note about fast-forwards' section of 'git push --help' for details.\n"
+msgid "Pruning %s"
 msgstr ""
 
-#: builtin/push.c:160
+#: builtin/remote.c:1282
 #, c-format
-msgid "bad repository '%s'"
+msgid "URL: %s"
 msgstr ""
 
-#: builtin/push.c:161
-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"
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
 msgstr ""
 
-#: builtin/push.c:176
-msgid "--all and --tags are incompatible"
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
 msgstr ""
 
-#: builtin/push.c:177
-msgid "--all can't be combined with refspecs"
+#: builtin/remote.c:1387 builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
 msgstr ""
 
-#: builtin/push.c:182
-msgid "--mirror and --tags are incompatible"
+#: builtin/remote.c:1414
+msgid "no remote specified"
 msgstr ""
 
-#: builtin/push.c:183
-msgid "--mirror can't be combined with refspecs"
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
 msgstr ""
 
-#: builtin/push.c:188
-msgid "--all and --mirror are incompatible"
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
 msgstr ""
 
-#: builtin/push.c:274
-msgid "--delete is incompatible with --all, --mirror and --tags"
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
 msgstr ""
 
-#: builtin/push.c:276
-msgid "--delete doesn't make sense without any refs"
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
 msgstr ""
 
 #: builtin/reset.c:33
@@ -2797,6 +4068,10 @@ msgstr ""
 msgid "hard"
 msgstr ""
 
+#: builtin/reset.c:33
+msgid "merge"
+msgstr ""
+
 #: builtin/reset.c:33
 msgid "keep"
 msgstr ""
@@ -2864,20 +4139,20 @@ msgstr ""
 msgid "Could not reset index file to revision '%s'."
 msgstr ""
 
-#: builtin/revert.c:70 builtin/revert.c:91
+#: builtin/revert.c:70 builtin/revert.c:92
 #, c-format
 msgid "%s: %s cannot be used with %s"
 msgstr ""
 
-#: builtin/revert.c:126
+#: builtin/revert.c:131
 msgid "program error"
 msgstr ""
 
-#: builtin/revert.c:209
+#: builtin/revert.c:221
 msgid "revert failed"
 msgstr ""
 
-#: builtin/revert.c:224
+#: builtin/revert.c:236
 msgid "cherry-pick failed"
 msgstr ""
 
@@ -2917,32 +4192,32 @@ msgstr ""
 msgid "Missing author: %s"
 msgstr ""
 
-#: builtin/tag.c:58
+#: builtin/tag.c:60
 #, c-format
 msgid "malformed object at '%s'"
 msgstr ""
 
-#: builtin/tag.c:205
+#: builtin/tag.c:207
 #, c-format
 msgid "tag name too long: %.*s..."
 msgstr ""
 
-#: builtin/tag.c:210
+#: builtin/tag.c:212
 #, c-format
 msgid "tag '%s' not found."
 msgstr ""
 
-#: builtin/tag.c:225
+#: builtin/tag.c:227
 #, c-format
 msgid "Deleted tag '%s' (was %s)\n"
 msgstr ""
 
-#: builtin/tag.c:237
+#: builtin/tag.c:239
 #, c-format
 msgid "could not verify the tag '%s'"
 msgstr ""
 
-#: builtin/tag.c:247
+#: builtin/tag.c:249
 msgid ""
 "\n"
 "#\n"
@@ -2951,7 +4226,7 @@ msgid ""
 "#\n"
 msgstr ""
 
-#: builtin/tag.c:254
+#: builtin/tag.c:256
 msgid ""
 "\n"
 "#\n"
@@ -2961,159 +4236,251 @@ msgid ""
 "#\n"
 msgstr ""
 
-#: builtin/tag.c:294
+#: builtin/tag.c:298
 msgid "unable to sign the tag"
 msgstr ""
 
-#: builtin/tag.c:296
+#: builtin/tag.c:300
 msgid "unable to write tag file"
 msgstr ""
 
-#: builtin/tag.c:321
+#: builtin/tag.c:325
 msgid "bad object type."
 msgstr ""
 
-#: builtin/tag.c:334
+#: builtin/tag.c:338
 msgid "tag header too big."
 msgstr ""
 
-#: builtin/tag.c:366
+#: builtin/tag.c:370
 msgid "no tag message?"
 msgstr ""
 
-#: builtin/tag.c:372
+#: builtin/tag.c:376
 #, c-format
 msgid "The tag message has been left in %s\n"
 msgstr ""
 
-#: builtin/tag.c:421
+#: builtin/tag.c:425
 msgid "switch 'points-at' requires an object"
 msgstr ""
 
-#: builtin/tag.c:423
+#: builtin/tag.c:427
 #, c-format
 msgid "malformed object name '%s'"
 msgstr ""
 
-#: builtin/tag.c:502
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr ""
+
+#: builtin/tag.c:523
 msgid "-n option is only allowed with -l."
 msgstr ""
 
-#: builtin/tag.c:504
+#: builtin/tag.c:525
 msgid "--contains option is only allowed with -l."
 msgstr ""
 
-#: builtin/tag.c:506
+#: builtin/tag.c:527
 msgid "--points-at option is only allowed with -l."
 msgstr ""
 
-#: builtin/tag.c:514
+#: builtin/tag.c:535
 msgid "only one -F or -m option is allowed."
 msgstr ""
 
-#: builtin/tag.c:534
+#: builtin/tag.c:555
 msgid "too many params"
 msgstr ""
 
-#: builtin/tag.c:540
+#: builtin/tag.c:561
 #, c-format
 msgid "'%s' is not a valid tag name."
 msgstr ""
 
-#: builtin/tag.c:545
+#: builtin/tag.c:566
 #, c-format
 msgid "tag '%s' already exists"
 msgstr ""
 
-#: builtin/tag.c:563
+#: builtin/tag.c:584
 #, c-format
 msgid "%s: cannot lock the ref"
 msgstr ""
 
-#: builtin/tag.c:565
+#: builtin/tag.c:586
 #, c-format
 msgid "%s: cannot update the ref"
 msgstr ""
 
-#: builtin/tag.c:567
+#: builtin/tag.c:588
 #, c-format
 msgid "Updated tag '%s' (was %s)\n"
 msgstr ""
 
-#: git-am.sh:49
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr ""
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr ""
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr ""
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr ""
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr ""
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr ""
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr ""
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr ""
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr ""
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr ""
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr ""
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr ""
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr ""
+
+#: common-cmds.h:20
+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 ""
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr ""
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr ""
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr ""
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr ""
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr ""
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr ""
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
+msgstr ""
+
+#: git-am.sh:50
 msgid "You need to set your committer info first"
 msgstr ""
 
-#: git-am.sh:136
+#: git-am.sh:137
 msgid "Repository lacks necessary blobs to fall back on 3-way merge."
 msgstr ""
 
-#: git-am.sh:147
+#: git-am.sh:154
 msgid ""
 "Did you hand edit your patch?\n"
 "It does not apply to blobs recorded in its index."
 msgstr ""
 
-#: git-am.sh:156
+#: git-am.sh:163
 msgid "Falling back to patching base and 3-way merge..."
 msgstr ""
 
-#: git-am.sh:268
+#: git-am.sh:275
 msgid "Only one StGIT patch series can be applied at once"
 msgstr ""
 
-#: git-am.sh:355
+#: git-am.sh:362
 #, sh-format
 msgid "Patch format $patch_format is not supported."
 msgstr ""
 
-#: git-am.sh:357
+#: git-am.sh:364
 msgid "Patch format detection failed."
 msgstr ""
 
-#: git-am.sh:411
+#: git-am.sh:418
 msgid "-d option is no longer supported.  Do not use."
 msgstr ""
 
-#: git-am.sh:474
+#: git-am.sh:481
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
 msgstr ""
 
-#: git-am.sh:479
+#: git-am.sh:486
 msgid "Please make up your mind. --skip or --abort?"
 msgstr ""
 
-#: git-am.sh:506
+#: git-am.sh:513
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr ""
 
-#: git-am.sh:572
+#: git-am.sh:579
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
 msgstr ""
 
-#: git-am.sh:748
+#: git-am.sh:755
 msgid "cannot be interactive without stdin connected to a terminal."
 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:759
+#: git-am.sh:766
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 msgstr ""
 
-#: git-am.sh:795
+#: git-am.sh:802
 #, sh-format
 msgid "Applying: $FIRSTLINE"
 msgstr ""
 
-#: git-am.sh:840
+#: git-am.sh:847
 msgid "No changes -- Patch already applied."
 msgstr ""
 
-#: git-am.sh:866
+#: git-am.sh:873
 msgid "applying to an empty history"
 msgstr ""
 
@@ -3345,161 +4712,161 @@ msgstr ""
 msgid "cannot strip one component off url '$remoteurl'"
 msgstr ""
 
-#: git-submodule.sh:108
+#: git-submodule.sh:109
 #, sh-format
-msgid "No submodule mapping found in .gitmodules for path '$path'"
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:149
+#: git-submodule.sh:150
 #, sh-format
-msgid "Clone of '$url' into submodule path '$path' failed"
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
 msgstr ""
 
-#: git-submodule.sh:159
+#: git-submodule.sh:160
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
 msgstr ""
 
-#: git-submodule.sh:247
+#: git-submodule.sh:249
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
 msgstr ""
 
-#: git-submodule.sh:264
+#: git-submodule.sh:266
 #, sh-format
-msgid "'$path' already exists in the index"
+msgid "'$sm_path' already exists in the index"
 msgstr ""
 
-#: git-submodule.sh:281
+#: git-submodule.sh:283
 #, sh-format
-msgid "'$path' already exists and is not a valid git repo"
+msgid "'$sm_path' already exists and is not a valid git repo"
 msgstr ""
 
-#: git-submodule.sh:295
+#: git-submodule.sh:297
 #, sh-format
-msgid "Unable to checkout submodule '$path'"
+msgid "Unable to checkout submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:300
+#: git-submodule.sh:302
 #, sh-format
-msgid "Failed to add submodule '$path'"
+msgid "Failed to add submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:305
+#: git-submodule.sh:307
 #, sh-format
-msgid "Failed to register submodule '$path'"
+msgid "Failed to register submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:347
+#: git-submodule.sh:349
 #, sh-format
-msgid "Entering '$prefix$path'"
+msgid "Entering '$prefix$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:359
+#: git-submodule.sh:363
 #, sh-format
-msgid "Stopping at '$path'; script returned non-zero status."
+msgid "Stopping at '$sm_path'; script returned non-zero status."
 msgstr ""
 
-#: git-submodule.sh:401
+#: git-submodule.sh:405
 #, sh-format
-msgid "No url found for submodule path '$path' in .gitmodules"
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
 msgstr ""
 
-#: git-submodule.sh:410
+#: git-submodule.sh:414
 #, sh-format
-msgid "Failed to register url for submodule path '$path'"
+msgid "Failed to register url for submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:418
+#: git-submodule.sh:422
 #, sh-format
-msgid "Failed to register update mode for submodule path '$path'"
+msgid "Failed to register update mode for submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:420
+#: git-submodule.sh:424
 #, sh-format
-msgid "Submodule '$name' ($url) registered for path '$path'"
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:519
+#: git-submodule.sh:523
 #, sh-format
 msgid ""
-"Submodule path '$path' not initialized\n"
+"Submodule path '$sm_path' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
 
-#: git-submodule.sh:532
+#: git-submodule.sh:536
 #, sh-format
-msgid "Unable to find current revision in submodule path '$path'"
+msgid "Unable to find current revision in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:551
+#: git-submodule.sh:555
 #, sh-format
-msgid "Unable to fetch in submodule path '$path'"
+msgid "Unable to fetch in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:565
+#: git-submodule.sh:569
 #, sh-format
-msgid "Unable to rebase '$sha1' in submodule path '$path'"
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:566
+#: git-submodule.sh:570
 #, sh-format
-msgid "Submodule path '$path': rebased into '$sha1'"
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
 msgstr ""
 
-#: git-submodule.sh:571
+#: git-submodule.sh:575
 #, sh-format
-msgid "Unable to merge '$sha1' in submodule path '$path'"
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:572
+#: git-submodule.sh:576
 #, sh-format
-msgid "Submodule path '$path': merged in '$sha1'"
+msgid "Submodule path '$sm_path': merged in '$sha1'"
 msgstr ""
 
-#: git-submodule.sh:577
+#: git-submodule.sh:581
 #, sh-format
-msgid "Unable to checkout '$sha1' in submodule path '$path'"
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:578
+#: git-submodule.sh:582
 #, sh-format
-msgid "Submodule path '$path': checked out '$sha1'"
+msgid "Submodule path '$sm_path': checked out '$sha1'"
 msgstr ""
 
-#: git-submodule.sh:600 git-submodule.sh:923
+#: git-submodule.sh:604 git-submodule.sh:927
 #, sh-format
-msgid "Failed to recurse into submodule path '$path'"
+msgid "Failed to recurse into submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:708
+#: git-submodule.sh:712
 msgid "--"
 msgstr ""
 
-#: git-submodule.sh:766
+#: git-submodule.sh:770
 #, sh-format
 msgid "  Warn: $name doesn't contain commit $sha1_src"
 msgstr ""
 
-#: git-submodule.sh:769
+#: git-submodule.sh:773
 #, sh-format
 msgid "  Warn: $name doesn't contain commit $sha1_dst"
 msgstr ""
 
-#: git-submodule.sh:772
+#: git-submodule.sh:776
 #, sh-format
 msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
 msgstr ""
 
-#: git-submodule.sh:797
+#: git-submodule.sh:801
 msgid "blob"
 msgstr ""
 
-#: git-submodule.sh:798
+#: git-submodule.sh:802
 msgid "submodule"
 msgstr ""
 
-#: git-submodule.sh:969
+#: git-submodule.sh:973
 #, sh-format
 msgid "Synchronizing submodule url for '$name'"
 msgstr ""
index a0e9b0cf40b83f78cc9251b7b99f34eb650e48b5..517ec29a4a3f6337e07ed411e7d86db160398fe9 100644 (file)
@@ -8,17 +8,17 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2012-03-16 20:18+0800\n"
-"PO-Revision-Date: 2012-04-01 11:26+0100\n"
+"POT-Creation-Date: 2012-05-08 16:06+0800\n"
+"PO-Revision-Date: 2012-05-14 21:17+0100\n"
 "Last-Translator: Marco Sousa <marcomsousa AT gmail.com>\n"
 "Language-Team: Portuguese\n"
-"Language: pt\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
+"Language: pt\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: advice.c:34
+#: advice.c:40
 #, c-format
 msgid "hint: %.*s\n"
 msgstr "dica: %.*s\n"
@@ -27,7 +27,7 @@ msgstr "dica: %.*s\n"
 #. * Message used both when 'git commit' fails and when
 #. * other commands doing a merge do.
 #.
-#: advice.c:64
+#: advice.c:70
 msgid ""
 "Fix them up in the work tree,\n"
 "and then use 'git add/rm <file>' as\n"
@@ -35,12 +35,95 @@ msgid ""
 "or use 'git commit -a'."
 msgstr ""
 
-#: commit.c:47
+#: bundle.c:36
+#, c-format
+msgid "'%s' does not look like a v2 bundle file"
+msgstr ""
+
+#: bundle.c:63
+#, c-format
+msgid "unrecognized header: %s%s (%d)"
+msgstr "cabeçalho não reconhecido: %s%s (%d)"
+
+#: bundle.c:89
+#: builtin/commit.c:753
+#, c-format
+msgid "could not open '%s'"
+msgstr "não é possivel abrir '%s'"
+
+#: bundle.c:140
+msgid "Repository lacks these prerequisite commits:"
+msgstr ""
+
+#: bundle.c:164
+#: sequencer.c:533
+#: sequencer.c:965
+#: builtin/log.c:289
+#: builtin/log.c:719
+#: builtin/log.c:1335
+#: builtin/log.c:1554
+#: builtin/merge.c:347
+#: builtin/shortlog.c:181
+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] ""
+msgstr[1] ""
+
+#: bundle.c:192
+#, c-format
+msgid "The bundle requires this ref"
+msgid_plural "The bundle requires these %d refs"
+msgstr[0] ""
+msgstr[1] ""
+
+#: bundle.c:290
+msgid "rev-list died"
+msgstr "rev-list morreu"
+
+#: bundle.c:296
+#: builtin/log.c:1231
+#: builtin/shortlog.c:284
+#, c-format
+msgid "unrecognized argument: %s"
+msgstr "argumento não reconhecido: %s"
+
+#: bundle.c:331
+#, c-format
+msgid "ref '%s' is excluded by the rev-list options"
+msgstr ""
+
+#: bundle.c:376
+msgid "Refusing to create empty bundle."
+msgstr ""
+
+#: bundle.c:394
+msgid "Could not spawn pack-objects"
+msgstr "Não foi possível pawn pack-objects"
+
+#: bundle.c:412
+msgid "pack-objects died"
+msgstr ""
+
+#: bundle.c:415
+#, c-format
+msgid "cannot create '%s'"
+msgstr "não consegue crear '%s'"
+
+#: bundle.c:437
+msgid "index-pack died"
+msgstr ""
+
+#: commit.c:48
 #, c-format
 msgid "could not parse %s"
 msgstr "não consigo parsear %s"
 
-#: commit.c:49
+#: commit.c:50
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s não é um commit!"
@@ -48,11 +131,11 @@ msgstr "%s %s não é um commit!"
 #: compat/obstack.c:406
 #: compat/obstack.c:408
 msgid "memory exhausted"
-msgstr "memoria exausta"
+msgstr "memória esgotada"
 
 #: connected.c:39
 msgid "Could not run 'git rev-list'"
-msgstr ""
+msgstr "Não foi possível executar 'git rev-list'"
 
 #: connected.c:48
 #, c-format
@@ -64,6 +147,74 @@ msgstr ""
 msgid "failed to close rev-list's stdin: %s"
 msgstr ""
 
+#: date.c:95
+msgid "in the future"
+msgstr ""
+
+#: date.c:101
+#, c-format
+msgid "%lu second ago"
+msgid_plural "%lu seconds ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:108
+#, c-format
+msgid "%lu minute ago"
+msgid_plural "%lu minutes ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:115
+#, c-format
+msgid "%lu hour ago"
+msgid_plural "%lu hours ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:122
+#, c-format
+msgid "%lu day ago"
+msgid_plural "%lu days ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:128
+#, c-format
+msgid "%lu week ago"
+msgid_plural "%lu weeks ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:135
+#, c-format
+msgid "%lu month ago"
+msgid_plural "%lu months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:146
+#, c-format
+msgid "%lu year"
+msgid_plural "%lu years"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:149
+#, c-format
+msgid "%s, %lu month ago"
+msgid_plural "%s, %lu months ago"
+msgstr[0] ""
+msgstr[1] ""
+
+#: date.c:154
+#: date.c:159
+#, c-format
+msgid "%lu year ago"
+msgid_plural "%lu years ago"
+msgstr[0] ""
+msgstr[1] ""
+
 #: diff.c:105
 #, c-format
 msgid "  Failed to parse dirstat cut-off percentage '%.*s'\n"
@@ -81,32 +232,32 @@ msgid ""
 "%s"
 msgstr ""
 
-#: diff.c:1336
+#: diff.c:1400
 msgid " 0 files changed\n"
 msgstr " 0 ficheros modificados\n"
 
-#: diff.c:1340
+#: diff.c:1404
 #, c-format
 msgid " %d file changed"
 msgid_plural " %d files changed"
 msgstr[0] " %d ficheiro modificado"
 msgstr[1] " %d ficheiros modificados"
 
-#: diff.c:1357
+#: diff.c:1421
 #, c-format
 msgid ", %d insertion(+)"
 msgid_plural ", %d insertions(+)"
 msgstr[0] ", %d adição(+)"
 msgstr[1] ", %d adições(+)"
 
-#: diff.c:1368
+#: diff.c:1432
 #, c-format
 msgid ", %d deletion(-)"
 msgid_plural ", %d deletions(-)"
 msgstr[0] ", %d eliminado(-)"
 msgstr[1] ", %d eliminados(-)"
 
-#: diff.c:3424
+#: diff.c:3478
 #, c-format
 msgid ""
 "Failed to parse --dirstat/-X option parameter:\n"
@@ -140,19 +291,59 @@ msgstr "'%s': %s"
 msgid "'%s': short read %s"
 msgstr ""
 
-#: help.c:287
+#: help.c:207
+#, c-format
+msgid "available git commands in '%s'"
+msgstr ""
+
+#: help.c:214
+msgid "git commands available from elsewhere on your $PATH"
+msgstr ""
+
+#: help.c:270
 #, 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:327
+msgid "Uh oh. Your system reports no Git commands at all."
+msgstr ""
+
+#: help.c:349
+#, 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:354
+#, c-format
+msgid "in %0.1f seconds automatically..."
+msgstr ""
+
+#: help.c:361
+#, c-format
+msgid "git: '%s' is not a git command. See 'git --help'."
+msgstr ""
+
+#: help.c:365
+msgid ""
+"\n"
+"Did you mean this?"
+msgid_plural ""
+"\n"
+"Did you mean one of these?"
+msgstr[0] ""
+msgstr[1] ""
+
 #: remote.c:1607
 #, 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] ""
+msgstr[0] "A sua rama está à frente de '%s' pelo commit %d.\n"
+msgstr[1] "A sua rama está à frente de '%s' pelos commites %d.\n"
 
 #: remote.c:1613
 #, c-format
@@ -172,475 +363,502 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: sequencer.c:120
-#: builtin/merge.c:864
-#: builtin/merge.c:985
-#: builtin/merge.c:1095
-#: builtin/merge.c:1105
+#: sequencer.c:121
+#: builtin/merge.c:865
+#: builtin/merge.c:978
+#: builtin/merge.c:1088
+#: builtin/merge.c:1098
 #, c-format
 msgid "Could not open '%s' for writing"
 msgstr ""
 
-#: sequencer.c:122
-#: builtin/merge.c:334
-#: builtin/merge.c:867
-#: builtin/merge.c:1097
-#: builtin/merge.c:1110
+#: sequencer.c:123
+#: builtin/merge.c:333
+#: builtin/merge.c:868
+#: builtin/merge.c:1090
+#: builtin/merge.c:1103
 #, c-format
 msgid "Could not write to '%s'"
-msgstr ""
+msgstr "Não foi possível escrever para '%s'"
 
-#: sequencer.c:143
+#: sequencer.c:144
 msgid ""
 "after resolving the conflicts, mark the corrected paths\n"
 "with 'git add <paths>' or 'git rm <paths>'"
 msgstr ""
 
-#: sequencer.c:146
+#: sequencer.c:147
 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:159
-#: sequencer.c:685
-#: sequencer.c:768
+#: sequencer.c:160
+#: sequencer.c:741
+#: sequencer.c:824
 #, c-format
 msgid "Could not write to %s"
-msgstr ""
+msgstr "Não foi possível gravar para %s"
 
-#: sequencer.c:162
+#: sequencer.c:163
 #, c-format
 msgid "Error wrapping up %s"
 msgstr ""
 
-#: sequencer.c:177
+#: sequencer.c:178
 msgid "Your local changes would be overwritten by cherry-pick."
 msgstr ""
 
-#: sequencer.c:179
+#: sequencer.c:180
 msgid "Your local changes would be overwritten by revert."
 msgstr ""
 
-#: sequencer.c:182
+#: sequencer.c:183
 msgid "Commit your changes or stash them to proceed."
 msgstr ""
 
 #. TRANSLATORS: %s will be "revert" or "cherry-pick"
-#: sequencer.c:232
+#: sequencer.c:233
 #, c-format
 msgid "%s: Unable to write new index file"
 msgstr ""
 
-#: sequencer.c:298
-msgid "Your index file is unmerged."
+#: sequencer.c:261
+msgid "Could not resolve HEAD commit\n"
 msgstr ""
 
-#: sequencer.c:301
-msgid "You do not have a valid HEAD"
+#: sequencer.c:282
+msgid "Unable to update cache tree\n"
 msgstr ""
 
-#: sequencer.c:316
+#: sequencer.c:323
+#, c-format
+msgid "Could not parse commit %s\n"
+msgstr "Não foi possível analisar commit %s\n"
+
+#: sequencer.c:328
+#, c-format
+msgid "Could not parse parent commit %s\n"
+msgstr "Não foi possível analisar commit parent %s\n"
+
+#: sequencer.c:358
+msgid "Your index file is unmerged."
+msgstr "O seu ficheiro de índice é não fundido."
+
+#: sequencer.c:361
+msgid "You do not have a valid HEAD"
+msgstr "Você não tem uma HEAD válida"
+
+#: sequencer.c:376
 #, c-format
 msgid "Commit %s is a merge but no -m option was given."
 msgstr ""
 
-#: sequencer.c:324
+#: sequencer.c:384
 #, c-format
 msgid "Commit %s does not have parent %d"
 msgstr ""
 
-#: sequencer.c:328
+#: sequencer.c:388
 #, 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:339
+#: sequencer.c:399
 #, c-format
 msgid "%s: cannot parse parent commit %s"
 msgstr ""
 
-#: sequencer.c:343
+#: sequencer.c:403
 #, c-format
 msgid "Cannot get commit message for %s"
-msgstr ""
+msgstr "Não é possível obter mensagem commit para %s"
 
-#: sequencer.c:427
+#: sequencer.c:491
 #, c-format
 msgid "could not revert %s... %s"
 msgstr ""
 
-#: sequencer.c:428
+#: sequencer.c:492
 #, c-format
 msgid "could not apply %s... %s"
 msgstr ""
 
-#: sequencer.c:450
-#: sequencer.c:909
-#: builtin/log.c:288
-#: builtin/log.c:713
-#: builtin/log.c:1329
-#: builtin/log.c:1548
-#: builtin/merge.c:348
-#: builtin/shortlog.c:181
-msgid "revision walk setup failed"
-msgstr ""
-
-#: sequencer.c:453
+#: sequencer.c:536
 msgid "empty commit set passed"
-msgstr ""
+msgstr "passado commit com o set vazio"
 
-#: sequencer.c:461
+#: sequencer.c:544
 #, c-format
 msgid "git %s: failed to read the index"
 msgstr ""
 
-#: sequencer.c:466
+#: sequencer.c:549
 #, c-format
 msgid "git %s: failed to refresh the index"
 msgstr ""
 
-#: sequencer.c:551
+#: sequencer.c:607
 #, c-format
 msgid "Cannot %s during a %s"
-msgstr ""
+msgstr "Não foi possível abrir %s durante um %s"
 
-#: sequencer.c:573
+#: sequencer.c:629
 #, c-format
 msgid "Could not parse line %d."
-msgstr ""
+msgstr "Não foi possível parsear linha %d."
 
-#: sequencer.c:578
+#: sequencer.c:634
 msgid "No commits parsed."
 msgstr "Nenhum commit parseado."
 
-#: sequencer.c:591
+#: sequencer.c:647
 #, c-format
 msgid "Could not open %s"
-msgstr ""
+msgstr "Não foi possível abrir %s"
 
-#: sequencer.c:595
+#: sequencer.c:651
 #, c-format
 msgid "Could not read %s."
-msgstr ""
+msgstr "Não foi possível ler %s."
 
-#: sequencer.c:602
+#: sequencer.c:658
 #, c-format
 msgid "Unusable instruction sheet: %s"
 msgstr ""
 
-#: sequencer.c:630
+#: sequencer.c:686
 #, c-format
 msgid "Invalid key: %s"
 msgstr ""
 
-#: sequencer.c:633
+#: sequencer.c:689
 #, c-format
 msgid "Invalid value for %s: %s"
 msgstr "Valor inválido para %s: %s"
 
-#: sequencer.c:645
+#: sequencer.c:701
 #, c-format
 msgid "Malformed options sheet: %s"
 msgstr ""
 
-#: sequencer.c:666
+#: sequencer.c:722
 msgid "a cherry-pick or revert is already in progress"
 msgstr ""
 
-#: sequencer.c:667
+#: sequencer.c:723
 msgid "try \"git cherry-pick (--continue | --quit | --abort)\""
 msgstr ""
 
-#: sequencer.c:671
+#: sequencer.c:727
 #, c-format
 msgid "Could not create sequencer directory %s"
 msgstr ""
 
-#: sequencer.c:687
-#: sequencer.c:772
+#: sequencer.c:743
+#: sequencer.c:828
 #, c-format
 msgid "Error wrapping up %s."
 msgstr ""
 
-#: sequencer.c:706
-#: sequencer.c:840
+#: sequencer.c:762
+#: sequencer.c:896
 msgid "no cherry-pick or revert in progress"
 msgstr ""
 
-#: sequencer.c:708
+#: sequencer.c:764
 msgid "cannot resolve HEAD"
 msgstr ""
 
-#: sequencer.c:710
+#: sequencer.c:766
 msgid "cannot abort from a branch yet to be born"
 msgstr ""
 
-#: sequencer.c:732
+#: sequencer.c:788
+#: builtin/apply.c:3682
 #, c-format
 msgid "cannot open %s: %s"
-msgstr ""
+msgstr "não foi possível abrir %s: %s"
 
-#: sequencer.c:735
+#: sequencer.c:791
 #, c-format
 msgid "cannot read %s: %s"
-msgstr ""
+msgstr "não foi possível ler %s: %s"
 
-#: sequencer.c:736
+#: sequencer.c:792
 msgid "unexpected end of file"
 msgstr ""
 
-#: sequencer.c:742
+#: sequencer.c:798
 #, c-format
 msgid "stored pre-cherry-pick HEAD file '%s' is corrupt"
 msgstr ""
 
-#: sequencer.c:765
+#: sequencer.c:821
 #, c-format
 msgid "Could not format %s."
-msgstr ""
+msgstr "Não foi possível formatear %s."
 
-#: sequencer.c:927
+#: sequencer.c:983
 msgid "Can't revert as initial commit"
 msgstr ""
 
-#: sequencer.c:928
+#: sequencer.c:984
 msgid "Can't cherry-pick into empty head"
 msgstr ""
 
-#: wt-status.c:134
-msgid "Unmerged paths:"
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
 msgstr ""
 
-#: wt-status.c:140
-#: wt-status.c:157
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "Não existe rama '%s'"
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr ""
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr ""
+
+#: wt-status.c:135
+msgid "Unmerged paths:"
+msgstr "caminhos não fundidos:"
+
+#: wt-status.c:141
+#: wt-status.c:158
 #, c-format
 msgid "  (use \"git reset %s <file>...\" to unstage)"
 msgstr ""
 
-#: wt-status.c:142
-#: wt-status.c:159
+#: wt-status.c:143
+#: wt-status.c:160
 msgid "  (use \"git rm --cached <file>...\" to unstage)"
 msgstr ""
 
-#: wt-status.c:143
+#: wt-status.c:144
 msgid "  (use \"git add/rm <file>...\" as appropriate to mark resolution)"
 msgstr "  (usa \"git add/rm <ficheiro>...\" para marcar como resolvido)"
 
-#: wt-status.c:151
+#: wt-status.c:152
 msgid "Changes to be committed:"
 msgstr "Mudanças a serem commitadas"
 
-#: wt-status.c:169
+#: wt-status.c:170
 msgid "Changes not staged for commit:"
 msgstr ""
 
-#: wt-status.c:173
+#: wt-status.c:174
 msgid "  (use \"git add <file>...\" to update what will be committed)"
 msgstr "  (usa \"git add <ficheiro>...\" para actualizar o que vai ser commitado)"
 
-#: wt-status.c:175
+#: wt-status.c:176
 msgid "  (use \"git add/rm <file>...\" to update what will be committed)"
 msgstr "  (usa \"git add/rm <ficheiro>...\" para actualizar o que vai ser commitado)"
 
-#: wt-status.c:176
+#: wt-status.c:177
 msgid "  (use \"git checkout -- <file>...\" to discard changes in working directory)"
 msgstr ""
 
-#: wt-status.c:178
+#: wt-status.c:179
 msgid "  (commit or discard the untracked or modified content in submodules)"
 msgstr ""
 
-#: wt-status.c:187
+#: wt-status.c:188
 #, c-format
 msgid "%s files:"
 msgstr "%s ficheros:"
 
-#: wt-status.c:190
+#: wt-status.c:191
 #, c-format
 msgid "  (use \"git %s <file>...\" to include in what will be committed)"
 msgstr ""
 
-#: wt-status.c:207
+#: wt-status.c:208
 msgid "bug"
 msgstr "erro"
 
-#: wt-status.c:212
+#: wt-status.c:213
 msgid "both deleted:"
-msgstr ""
+msgstr "eliminados em ambos:"
 
-#: wt-status.c:213
+#: wt-status.c:214
 msgid "added by us:"
-msgstr ""
+msgstr "adicionado por nós:"
 
-#: wt-status.c:214
+#: wt-status.c:215
 msgid "deleted by them:"
-msgstr ""
+msgstr "eliminados por eles:"
 
-#: wt-status.c:215
+#: wt-status.c:216
 msgid "added by them:"
-msgstr ""
+msgstr "adicionados por eles:"
 
-#: wt-status.c:216
+#: wt-status.c:217
 msgid "deleted by us:"
-msgstr ""
+msgstr "eliminados por nós:"
 
-#: wt-status.c:217
+#: wt-status.c:218
 msgid "both added:"
-msgstr ""
+msgstr "adicionados em ambos:"
 
-#: wt-status.c:218
+#: wt-status.c:219
 msgid "both modified:"
-msgstr ""
+msgstr "modificados em ambos:"
 
-#: wt-status.c:248
+#: wt-status.c:249
 msgid "new commits, "
 msgstr "novos commits, "
 
-#: wt-status.c:250
+#: wt-status.c:251
 msgid "modified content, "
-msgstr ""
+msgstr "conteúdo modificado, "
 
-#: wt-status.c:252
+#: wt-status.c:253
 msgid "untracked content, "
-msgstr ""
+msgstr "conteúdo não seguido"
 
-#: wt-status.c:266
+#: wt-status.c:267
 #, c-format
 msgid "new file:   %s"
 msgstr "novo ficheiro:   %s"
 
-#: wt-status.c:269
+#: wt-status.c:270
 #, c-format
 msgid "copied:     %s -> %s"
 msgstr "copiado:     %s -> %s"
 
-#: wt-status.c:272
+#: wt-status.c:273
 #, c-format
 msgid "deleted:    %s"
 msgstr "eliminado:    %s"
 
-#: wt-status.c:275
+#: wt-status.c:276
 #, c-format
 msgid "modified:   %s"
 msgstr "modificado:   %s"
 
-#: wt-status.c:278
+#: wt-status.c:279
 #, c-format
 msgid "renamed:    %s -> %s"
 msgstr "mudado de nome:    %s -> %s"
 
-#: wt-status.c:281
+#: wt-status.c:282
 #, c-format
 msgid "typechange: %s"
 msgstr ""
 
-#: wt-status.c:284
+#: wt-status.c:285
 #, c-format
 msgid "unknown:    %s"
 msgstr "desconhecido:    %s"
 
-#: wt-status.c:287
+#: wt-status.c:288
 #, c-format
 msgid "unmerged:   %s"
-msgstr ""
+msgstr "não fundidos:   %s"
 
-#: wt-status.c:290
+#: wt-status.c:291
 #, c-format
 msgid "bug: unhandled diff status %c"
 msgstr ""
 
-#: wt-status.c:713
+#: wt-status.c:737
 msgid "On branch "
 msgstr "Na rama"
 
-#: wt-status.c:720
+#: wt-status.c:744
 msgid "Not currently on any branch."
-msgstr ""
+msgstr "Não está em nenhuma rama."
 
-#: wt-status.c:731
+#: wt-status.c:755
 msgid "Initial commit"
 msgstr "Commit inicial"
 
-#: wt-status.c:745
+#: wt-status.c:769
 msgid "Untracked"
-msgstr ""
+msgstr "Não seguido"
 
-#: wt-status.c:747
+#: wt-status.c:771
 msgid "Ignored"
 msgstr "Ignorado"
 
-#: wt-status.c:749
+#: wt-status.c:773
 #, c-format
 msgid "Untracked files not listed%s"
 msgstr ""
 
-#: wt-status.c:751
+#: wt-status.c:775
 msgid " (use -u option to show untracked files)"
 msgstr ""
 
-#: wt-status.c:757
+#: wt-status.c:781
 msgid "No changes"
 msgstr "Sem mudanças"
 
-#: wt-status.c:761
+#: wt-status.c:785
 #, c-format
 msgid "no changes added to commit%s\n"
-msgstr ""
+msgstr "nenhuma alteração adicionado ao commit%s\n"
 
-#: wt-status.c:763
+#: wt-status.c:787
 msgid " (use \"git add\" and/or \"git commit -a\")"
 msgstr " (usa \"git add\" e/ou \"git commit -a\")"
 
-#: wt-status.c:765
+#: wt-status.c:789
 #, c-format
 msgid "nothing added to commit but untracked files present%s\n"
 msgstr ""
 
-#: wt-status.c:767
+#: wt-status.c:791
 msgid " (use \"git add\" to track)"
 msgstr " (usa \"git add\" para seguir)"
 
-#: wt-status.c:769
-#: wt-status.c:772
-#: wt-status.c:775
+#: wt-status.c:793
+#: wt-status.c:796
+#: wt-status.c:799
 #, c-format
 msgid "nothing to commit%s\n"
 msgstr "nada para fazer commit%s\n"
 
-#: wt-status.c:770
+#: wt-status.c:794
 msgid " (create/copy files and use \"git add\" to track)"
 msgstr ""
 
-#: wt-status.c:773
+#: wt-status.c:797
 msgid " (use -u to show untracked files)"
 msgstr ""
 
-#: wt-status.c:776
+#: wt-status.c:800
 msgid " (working directory clean)"
 msgstr " (directório de trabalho vacio)"
 
-#: wt-status.c:884
+#: wt-status.c:908
 msgid "HEAD (no branch)"
 msgstr "HEAD (Não é rama)"
 
-#: wt-status.c:890
+#: wt-status.c:914
 msgid "Initial commit on "
 msgstr "Commit inicial em "
 
-#: wt-status.c:905
+#: wt-status.c:929
 msgid "behind "
 msgstr "atrás "
 
-#: wt-status.c:908
-#: wt-status.c:911
+#: wt-status.c:932
+#: wt-status.c:935
 msgid "ahead "
 msgstr "a frente "
 
-#: wt-status.c:913
+#: wt-status.c:937
 msgid ", behind "
 msgstr ", atrás "
 
@@ -650,9 +868,9 @@ msgid "unexpected diff status %c"
 msgstr ""
 
 #: builtin/add.c:67
-#: builtin/commit.c:298
+#: builtin/commit.c:282
 msgid "updating files failed"
-msgstr ""
+msgstr "Falou a atualização dos ficheiros"
 
 #: builtin/add.c:77
 #, c-format
@@ -682,16 +900,16 @@ msgstr ""
 
 #: builtin/add.c:276
 msgid "Could not read the index"
-msgstr ""
+msgstr "Não foi possível ler o indíce"
 
 #: builtin/add.c:286
 #, c-format
 msgid "Could not open '%s' for writing."
-msgstr ""
+msgstr "Não foi possível abrir '%s' para escrever."
 
 #: builtin/add.c:290
 msgid "Could not write patch"
-msgstr ""
+msgstr "Não consegue escrever patch"
 
 #: builtin/add.c:295
 #, c-format
@@ -700,12 +918,12 @@ msgstr ""
 
 #: builtin/add.c:297
 msgid "Empty patch. Aborted."
-msgstr ""
+msgstr "Patch vazio. Aborted."
 
 #: builtin/add.c:303
 #, c-format
 msgid "Could not apply '%s'"
-msgstr ""
+msgstr "Não foi possível aplicar o '%s'"
 
 #: builtin/add.c:312
 msgid "The following paths are ignored by one of your .gitignore files:\n"
@@ -722,7 +940,7 @@ msgstr "nenhum ficheiros adicionado"
 
 #: builtin/add.c:359
 msgid "adding files failed"
-msgstr ""
+msgstr "falhou a adicionar ficheiros"
 
 #: builtin/add.c:391
 msgid "-A and -u are mutually incompatible"
@@ -744,18 +962,359 @@ msgstr ""
 
 #: builtin/add.c:420
 #: builtin/clean.c:95
-#: builtin/commit.c:358
+#: builtin/commit.c:342
 #: builtin/mv.c:82
 #: builtin/rm.c:162
 msgid "index file corrupt"
-msgstr ""
+msgstr "ficheiro index corrupto"
 
 #: builtin/add.c:476
+#: builtin/apply.c:4093
 #: builtin/mv.c:229
 #: builtin/rm.c:260
 msgid "Unable to write new index file"
 msgstr ""
 
+#: builtin/apply.c:106
+#, c-format
+msgid "unrecognized whitespace option '%s'"
+msgstr "espaço em braco não reconhecido: '%s'"
+
+#: builtin/apply.c:121
+#, c-format
+msgid "unrecognized whitespace ignore option '%s'"
+msgstr ""
+
+#: builtin/apply.c:815
+#, c-format
+msgid "Cannot prepare timestamp regexp %s"
+msgstr ""
+
+#: builtin/apply.c:824
+#, c-format
+msgid "regexec returned %d for input: %s"
+msgstr ""
+
+#: builtin/apply.c:905
+#, c-format
+msgid "unable to find filename in patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:934
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null, got %s on line %d"
+msgstr ""
+
+#: builtin/apply.c:937
+#, c-format
+msgid "git apply: bad git-diff - inconsistent %s filename on line %d"
+msgstr ""
+
+#: builtin/apply.c:944
+#, c-format
+msgid "git apply: bad git-diff - expected /dev/null on line %d"
+msgstr ""
+
+#: builtin/apply.c:1387
+#, c-format
+msgid "recount: unexpected line: %.*s"
+msgstr ""
+
+#: builtin/apply.c:1444
+#, c-format
+msgid "patch fragment without header at line %d: %.*s"
+msgstr ""
+
+#: builtin/apply.c:1461
+#, 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] ""
+msgstr[1] ""
+
+#: builtin/apply.c:1621
+msgid "new file depends on old contents"
+msgstr ""
+
+#: builtin/apply.c:1623
+msgid "deleted file still has contents"
+msgstr ""
+
+#: builtin/apply.c:1649
+#, c-format
+msgid "corrupt patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:1685
+#, c-format
+msgid "new file %s depends on old contents"
+msgstr ""
+
+#: builtin/apply.c:1687
+#, c-format
+msgid "deleted file %s still has contents"
+msgstr ""
+
+#: builtin/apply.c:1690
+#, c-format
+msgid "** warning: file %s becomes empty but is not deleted"
+msgstr ""
+
+#: builtin/apply.c:1836
+#, c-format
+msgid "corrupt binary patch at line %d: %.*s"
+msgstr ""
+
+#. there has to be one hunk (forward hunk)
+#: builtin/apply.c:1865
+#, c-format
+msgid "unrecognized binary patch at line %d"
+msgstr ""
+
+#: builtin/apply.c:1951
+#, c-format
+msgid "patch with only garbage at line %d"
+msgstr ""
+
+#: builtin/apply.c:2041
+#, c-format
+msgid "unable to read symlink %s"
+msgstr ""
+
+#: builtin/apply.c:2045
+#, c-format
+msgid "unable to open or read %s"
+msgstr "Não foi possível abrir o ler %s"
+
+#: builtin/apply.c:2116
+msgid "oops"
+msgstr ""
+
+#: builtin/apply.c:2638
+#, c-format
+msgid "invalid start of line: '%c'"
+msgstr "começo de linha inválido: '%c'"
+
+#: builtin/apply.c:2756
+#, 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:2768
+#, c-format
+msgid "Context reduced to (%ld/%ld) to apply fragment at %d"
+msgstr ""
+
+#: builtin/apply.c:2774
+#, c-format
+msgid ""
+"while searching for:\n"
+"%.*s"
+msgstr ""
+
+#: builtin/apply.c:2793
+#, c-format
+msgid "missing binary patch data for '%s'"
+msgstr ""
+
+#: builtin/apply.c:2896
+#, c-format
+msgid "binary patch does not apply to '%s'"
+msgstr ""
+
+#: builtin/apply.c:2902
+#, c-format
+msgid "binary patch to '%s' creates incorrect result (expecting %s, got %s)"
+msgstr ""
+
+#: builtin/apply.c:2923
+#, c-format
+msgid "patch failed: %s:%ld"
+msgstr ""
+
+#: builtin/apply.c:3038
+#, c-format
+msgid "patch %s has been renamed/deleted"
+msgstr ""
+
+#: builtin/apply.c:3045
+#: builtin/apply.c:3062
+#, c-format
+msgid "read of %s failed"
+msgstr "ler %s falhou"
+
+#: builtin/apply.c:3077
+msgid "removal patch leaves file contents"
+msgstr ""
+
+#: builtin/apply.c:3098
+#, c-format
+msgid "%s: already exists in working directory"
+msgstr "%s: já existe no espaço de trabalho"
+
+#: builtin/apply.c:3136
+#, c-format
+msgid "%s: has been deleted/renamed"
+msgstr ""
+
+#: builtin/apply.c:3141
+#: builtin/apply.c:3172
+#, c-format
+msgid "%s: %s"
+msgstr "%s: %s"
+
+#: builtin/apply.c:3152
+#, c-format
+msgid "%s: does not exist in index"
+msgstr ""
+
+#: builtin/apply.c:3166
+#, c-format
+msgid "%s: does not match index"
+msgstr "%s: não tem correspondencia ao index"
+
+#: builtin/apply.c:3183
+#, c-format
+msgid "%s: wrong type"
+msgstr ""
+
+#: builtin/apply.c:3185
+#, c-format
+msgid "%s has type %o, expected %o"
+msgstr ""
+
+#: builtin/apply.c:3240
+#, c-format
+msgid "%s: already exists in index"
+msgstr "%s: já existe no indíce"
+
+#: builtin/apply.c:3259
+#, c-format
+msgid "new mode (%o) of %s does not match old mode (%o)%s%s"
+msgstr ""
+
+#: builtin/apply.c:3265
+#, c-format
+msgid "%s: patch does not apply"
+msgstr ""
+
+#: builtin/apply.c:3278
+#, c-format
+msgid "Checking patch %s..."
+msgstr ""
+
+#: builtin/apply.c:3333
+#: builtin/checkout.c:212
+#: builtin/reset.c:158
+#, c-format
+msgid "make_cache_entry failed for path '%s'"
+msgstr ""
+
+#: builtin/apply.c:3476
+#, c-format
+msgid "unable to remove %s from index"
+msgstr ""
+
+#: builtin/apply.c:3503
+#, c-format
+msgid "corrupt patch for subproject %s"
+msgstr ""
+
+#: builtin/apply.c:3507
+#, c-format
+msgid "unable to stat newly created file '%s'"
+msgstr "não é possivel 'stat' o novo ficheiro creado '%s'"
+
+#: builtin/apply.c:3512
+#, c-format
+msgid "unable to create backing store for newly created file %s"
+msgstr ""
+
+#: builtin/apply.c:3515
+#, c-format
+msgid "unable to add cache entry for %s"
+msgstr ""
+
+#: builtin/apply.c:3548
+#, c-format
+msgid "closing file '%s'"
+msgstr "fechar fichero '%s'"
+
+#: builtin/apply.c:3597
+#, c-format
+msgid "unable to write file '%s' mode %o"
+msgstr ""
+
+#: builtin/apply.c:3653
+#, c-format
+msgid "Applied patch %s cleanly."
+msgstr ""
+
+#: builtin/apply.c:3661
+msgid "internal error"
+msgstr ""
+
+#. Say this even without --verbose
+#: builtin/apply.c:3664
+#, c-format
+msgid "Applying patch %%s with %d reject..."
+msgid_plural "Applying patch %%s with %d rejects..."
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:3674
+#, c-format
+msgid "truncating .rej filename to %.*s.rej"
+msgstr ""
+
+#: builtin/apply.c:3695
+#, c-format
+msgid "Hunk #%d applied cleanly."
+msgstr ""
+
+#: builtin/apply.c:3698
+#, c-format
+msgid "Rejected hunk #%d."
+msgstr ""
+
+#: builtin/apply.c:3829
+msgid "unrecognized input"
+msgstr "entrada não reconhecida"
+
+#: builtin/apply.c:3840
+msgid "unable to read index file"
+msgstr "Não foi possível ler o fichero indíce"
+
+#: builtin/apply.c:4035
+msgid "--index outside a repository"
+msgstr "--index fora de um repositorio"
+
+#: builtin/apply.c:4038
+msgid "--cached outside a repository"
+msgstr "--cached fora de um repositorio"
+
+#: builtin/apply.c:4054
+#, c-format
+msgid "can't open patch '%s'"
+msgstr "não é possivel abrir patch '%s'"
+
+#: builtin/apply.c:4068
+#, c-format
+msgid "squelched %d whitespace error"
+msgid_plural "squelched %d whitespace errors"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/apply.c:4074
+#: builtin/apply.c:4084
+#, c-format
+msgid "%d line adds whitespace errors."
+msgid_plural "%d lines add whitespace errors."
+msgstr[0] ""
+msgstr[1] ""
+
 #: builtin/archive.c:17
 #, c-format
 msgid "could not create archive file '%s'"
@@ -781,7 +1340,7 @@ msgstr ""
 #: builtin/archive.c:65
 #, c-format
 msgid "remote error: %s"
-msgstr ""
+msgstr "erro remoto: %s"
 
 #: builtin/archive.c:66
 msgid "git archive: protocol error"
@@ -791,144 +1350,158 @@ msgstr ""
 msgid "git archive: expected a flush"
 msgstr ""
 
-#: builtin/branch.c:137
+#: builtin/branch.c:144
 #, c-format
 msgid ""
 "deleting branch '%s' that has been merged to\n"
 "         '%s', but not yet merged to HEAD."
 msgstr ""
 
-#: builtin/branch.c:141
+#: builtin/branch.c:148
 #, c-format
 msgid ""
 "not deleting branch '%s' that is not yet merged to\n"
 "         '%s', even though it is merged to HEAD."
 msgstr ""
 
-#. TRANSLATORS: This is "remote " in "remote branch '%s' not found"
-#: builtin/branch.c:163
-msgid "remote "
-msgstr "remota"
-
-#: builtin/branch.c:171
+#: builtin/branch.c:180
 msgid "cannot use -a with -d"
-msgstr ""
+msgstr "Não é possível usar -a com um -d"
 
-#: builtin/branch.c:177
+#: builtin/branch.c:186
 msgid "Couldn't look up commit object for HEAD"
 msgstr ""
 
-#: builtin/branch.c:182
+#: builtin/branch.c:191
 #, c-format
 msgid "Cannot delete the branch '%s' which you are currently on."
 msgstr ""
 
-#: builtin/branch.c:192
+#: builtin/branch.c:202
 #, c-format
-msgid "%sbranch '%s' not found."
-msgstr ""
+msgid "remote branch '%s' not found."
+msgstr "rama remota '%s não encontrada."
+
+#: builtin/branch.c:203
+#, c-format
+msgid "branch '%s' not found."
+msgstr "rama '%s' não encontrado."
 
-#: builtin/branch.c:200
+#: builtin/branch.c:210
 #, c-format
 msgid "Couldn't look up commit object for '%s'"
 msgstr ""
 
-#: builtin/branch.c:206
+#: builtin/branch.c:216
 #, 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:214
+#: builtin/branch.c:225
 #, c-format
-msgid "Error deleting %sbranch '%s'"
+msgid "Error deleting remote branch '%s'"
 msgstr ""
 
-#: builtin/branch.c:219
+#: builtin/branch.c:226
 #, c-format
-msgid "Deleted %sbranch %s (was %s).\n"
+msgid "Error deleting branch '%s'"
+msgstr "Erro a eliminar rama '%s'"
+
+#: builtin/branch.c:233
+#, c-format
+msgid "Deleted remote branch %s (was %s).\n"
 msgstr ""
 
-#: builtin/branch.c:224
+#: builtin/branch.c:234
+#, c-format
+msgid "Deleted branch %s (was %s).\n"
+msgstr "Eliminar rama %s (era %s).\n"
+
+#: builtin/branch.c:239
 msgid "Update of config-file failed"
 msgstr ""
 
-#: builtin/branch.c:322
+#: builtin/branch.c:337
 #, c-format
 msgid "branch '%s' does not point at a commit"
 msgstr ""
 
-#: builtin/branch.c:394
+#: builtin/branch.c:409
 #, c-format
 msgid "behind %d] "
-msgstr ""
+msgstr "atrás %d] "
 
-#: builtin/branch.c:396
+#: builtin/branch.c:411
 #, c-format
 msgid "ahead %d] "
-msgstr ""
+msgstr "a frente %d] "
 
-#: builtin/branch.c:398
+#: builtin/branch.c:413
 #, c-format
 msgid "ahead %d, behind %d] "
-msgstr ""
+msgstr "a frente %d, atrás %d] "
 
-#: builtin/branch.c:501
+#: builtin/branch.c:521
 msgid "(no branch)"
 msgstr "(não é rama)"
 
-#: builtin/branch.c:566
+#: builtin/branch.c:586
 msgid "some refs could not be read"
 msgstr ""
 
-#: builtin/branch.c:579
+#: builtin/branch.c:599
 msgid "cannot rename the current branch while not on any."
 msgstr ""
 
-#: builtin/branch.c:589
+#: builtin/branch.c:609
 #, c-format
 msgid "Invalid branch name: '%s'"
-msgstr ""
+msgstr "Nome da rama inválida: '%s'"
 
-#: builtin/branch.c:604
+#: builtin/branch.c:624
 msgid "Branch rename failed"
-msgstr ""
+msgstr "Falhou renomeação da rama"
 
-#: builtin/branch.c:608
+#: builtin/branch.c:628
 #, c-format
 msgid "Renamed a misnamed branch '%s' away"
-msgstr ""
+msgstr "Renomeado uma rama erronea '%s'"
 
-#: builtin/branch.c:612
+#: builtin/branch.c:632
 #, c-format
 msgid "Branch renamed to %s, but HEAD is not updated!"
 msgstr ""
 
-#: builtin/branch.c:619
+#: builtin/branch.c:639
 msgid "Branch is renamed, but update of config-file failed"
 msgstr ""
 
-#: builtin/branch.c:634
+#: builtin/branch.c:654
 #, c-format
 msgid "malformed object name %s"
 msgstr ""
 
-#: builtin/branch.c:658
+#: builtin/branch.c:678
 #, c-format
-msgid "could not write branch description template: %s\n"
+msgid "could not write branch description template: %s"
 msgstr ""
 
-#: builtin/branch.c:746
+#: builtin/branch.c:769
 msgid "Failed to resolve HEAD as a valid ref."
 msgstr ""
 
-#: builtin/branch.c:751
+#: builtin/branch.c:774
 #: builtin/clone.c:558
 msgid "HEAD not found below refs/heads!"
 msgstr ""
 
-#: builtin/branch.c:809
+#: builtin/branch.c:794
+msgid "--column and --verbose are incompatible"
+msgstr "--column e --verbose são incompatíveis"
+
+#: builtin/branch.c:843
 msgid "-a and -r options to 'git branch' do not make sense with a branch name"
 msgstr ""
 
@@ -977,16 +1550,10 @@ msgstr ""
 msgid "Unable to add merge result for '%s'"
 msgstr ""
 
-#: builtin/checkout.c:212
-#: builtin/reset.c:158
-#, c-format
-msgid "make_cache_entry failed for path '%s'"
-msgstr ""
-
 #: builtin/checkout.c:234
 #: builtin/checkout.c:392
 msgid "corrupt index file"
-msgstr ""
+msgstr "ficheiro index corrupto"
 
 #: builtin/checkout.c:264
 #: builtin/checkout.c:271
@@ -997,7 +1564,7 @@ msgstr ""
 #: builtin/checkout.c:302
 #: builtin/checkout.c:498
 #: builtin/clone.c:583
-#: builtin/merge.c:811
+#: builtin/merge.c:812
 msgid "unable to write new index file"
 msgstr ""
 
@@ -1016,42 +1583,42 @@ msgstr ""
 msgid "Can not do reflog for '%s'\n"
 msgstr ""
 
-#: builtin/checkout.c:565
+#: builtin/checkout.c:566
 msgid "HEAD is now at"
 msgstr "HEAD é agora em "
 
-#: builtin/checkout.c:572
+#: builtin/checkout.c:573
 #, c-format
 msgid "Reset branch '%s'\n"
 msgstr "Reset rama '%s'\n"
 
-#: builtin/checkout.c:575
+#: builtin/checkout.c:576
 #, c-format
 msgid "Already on '%s'\n"
 msgstr "Já em '%s'\n"
 
-#: builtin/checkout.c:579
+#: builtin/checkout.c:580
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
 msgstr ""
 
-#: builtin/checkout.c:581
+#: builtin/checkout.c:582
 #, c-format
 msgid "Switched to a new branch '%s'\n"
-msgstr ""
+msgstr "Mudado para a nova rama '%s'\n"
 
-#: builtin/checkout.c:583
+#: builtin/checkout.c:584
 #, c-format
 msgid "Switched to branch '%s'\n"
-msgstr ""
+msgstr "Mudado para a rama '%s'\n"
 
-#: builtin/checkout.c:639
+#: builtin/checkout.c:640
 #, c-format
 msgid " ... and %d more.\n"
-msgstr ""
+msgstr " ... e %d mais.\n"
 
 #. The singular version
-#: builtin/checkout.c:645
+#: builtin/checkout.c:646
 #, c-format
 msgid ""
 "Warning: you are leaving %d commit behind, not connected to\n"
@@ -1066,7 +1633,7 @@ msgid_plural ""
 msgstr[0] ""
 msgstr[1] ""
 
-#: builtin/checkout.c:663
+#: builtin/checkout.c:664
 #, c-format
 msgid ""
 "If you want to keep them by creating a new branch, this may be a good time\n"
@@ -1076,96 +1643,96 @@ msgid ""
 "\n"
 msgstr ""
 
-#: builtin/checkout.c:692
+#: builtin/checkout.c:693
 msgid "internal error in revision walk"
 msgstr ""
 
-#: builtin/checkout.c:696
+#: builtin/checkout.c:697
 msgid "Previous HEAD position was"
 msgstr ""
 
-#: builtin/checkout.c:722
+#: builtin/checkout.c:723
 msgid "You are on a branch yet to be born"
 msgstr ""
 
 #. case (1)
-#: builtin/checkout.c:853
+#: builtin/checkout.c:854
 #, c-format
 msgid "invalid reference: %s"
 msgstr ""
 
 #. case (1): want a tree
-#: builtin/checkout.c:892
+#: builtin/checkout.c:893
 #, c-format
 msgid "reference is not a tree: %s"
 msgstr ""
 
-#: builtin/checkout.c:972
+#: builtin/checkout.c:973
 msgid "-B cannot be used with -b"
 msgstr ""
 
-#: builtin/checkout.c:981
+#: builtin/checkout.c:982
 msgid "--patch is incompatible with all other options"
 msgstr ""
 
-#: builtin/checkout.c:984
+#: builtin/checkout.c:985
 msgid "--detach cannot be used with -b/-B/--orphan"
 msgstr ""
 
-#: builtin/checkout.c:986
+#: builtin/checkout.c:987
 msgid "--detach cannot be used with -t"
 msgstr ""
 
-#: builtin/checkout.c:992
+#: builtin/checkout.c:993
 msgid "--track needs a branch name"
 msgstr ""
 
-#: builtin/checkout.c:999
+#: builtin/checkout.c:1000
 msgid "Missing branch name; try -b"
 msgstr ""
 
-#: builtin/checkout.c:1005
+#: builtin/checkout.c:1006
 msgid "--orphan and -b|-B are mutually exclusive"
 msgstr ""
 
-#: builtin/checkout.c:1007
+#: builtin/checkout.c:1008
 msgid "--orphan cannot be used with -t"
 msgstr ""
 
-#: builtin/checkout.c:1017
+#: builtin/checkout.c:1018
 msgid "git checkout: -f and -m are incompatible"
 msgstr ""
 
-#: builtin/checkout.c:1051
+#: builtin/checkout.c:1052
 msgid "invalid path specification"
 msgstr ""
 
-#: builtin/checkout.c:1059
+#: builtin/checkout.c:1060
 #, c-format
 msgid ""
 "git checkout: updating paths is incompatible with switching branches.\n"
 "Did you intend to checkout '%s' which can not be resolved as commit?"
 msgstr ""
 
-#: builtin/checkout.c:1061
+#: builtin/checkout.c:1062
 msgid "git checkout: updating paths is incompatible with switching branches."
 msgstr ""
 
-#: builtin/checkout.c:1066
+#: builtin/checkout.c:1067
 msgid "git checkout: --detach does not take a path argument"
 msgstr ""
 
-#: builtin/checkout.c:1069
+#: builtin/checkout.c:1070
 msgid ""
 "git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 "checking out of the index."
 msgstr ""
 
-#: builtin/checkout.c:1088
+#: builtin/checkout.c:1089
 msgid "Cannot switch branch to a non-commit."
 msgstr ""
 
-#: builtin/checkout.c:1091
+#: builtin/checkout.c:1092
 msgid "--ours/--theirs is incompatible with switching branches."
 msgstr ""
 
@@ -1191,7 +1758,7 @@ msgstr ""
 #: builtin/clean.c:179
 #, c-format
 msgid "Removing %s\n"
-msgstr ""
+msgstr "Eliminando %s\n"
 
 #: builtin/clean.c:162
 #: builtin/clean.c:182
@@ -1217,12 +1784,12 @@ msgstr ""
 #: builtin/clone.c:302
 #, c-format
 msgid "failed to open '%s'"
-msgstr ""
+msgstr "falhou a abrir '%s'"
 
 #: builtin/clone.c:306
 #, c-format
 msgid "failed to create directory '%s'"
-msgstr ""
+msgstr "falhou a criar o directório '%s'"
 
 #: builtin/clone.c:308
 #: builtin/diff.c:75
@@ -1253,7 +1820,7 @@ msgstr ""
 #: builtin/clone.c:350
 #, c-format
 msgid "failed to copy file to '%s'"
-msgstr ""
+msgstr "falhou a copiar o ficheiro para '%s'"
 
 #: builtin/clone.c:373
 #, c-format
@@ -1271,7 +1838,7 @@ msgstr ""
 
 #: builtin/clone.c:639
 msgid "Too many arguments."
-msgstr ""
+msgstr "Demasiados parametros."
 
 #: builtin/clone.c:643
 msgid "You must specify a repository to clone."
@@ -1315,17 +1882,17 @@ msgstr ""
 #: builtin/clone.c:728
 #, c-format
 msgid "Cloning into bare repository '%s'...\n"
-msgstr ""
+msgstr "Clonando em um repositorio nu (bare) '%s'...\n"
 
 #: builtin/clone.c:730
 #, c-format
 msgid "Cloning into '%s'...\n"
-msgstr ""
+msgstr "Clonar em '%s'...\n"
 
 #: builtin/clone.c:786
 #, c-format
 msgid "Don't know how to clone %s"
-msgstr ""
+msgstr "Não sei como clonar %s"
 
 #: builtin/clone.c:835
 #, c-format
@@ -1336,7 +1903,11 @@ msgstr ""
 msgid "You appear to have cloned an empty repository."
 msgstr ""
 
-#: builtin/commit.c:42
+#: builtin/column.c:51
+msgid "--command must be the first argument"
+msgstr ""
+
+#: builtin/commit.c:43
 msgid ""
 "Your name and email address were configured automatically based\n"
 "on your username and hostname. Please check that they are accurate.\n"
@@ -1349,15 +1920,25 @@ msgid ""
 "\n"
 "    git commit --amend --reset-author\n"
 msgstr ""
+"O seu nome e endereço de e-mail foram configurados automaticamente com base\n"
+"no o seu usuario e nome da maquina. Por favor, verifique se eles são precisos.\n"
+"Você pode suprimir esta mensagem, configurando-los explicitamente:\n"
+"\n"
+"    git config --global user.name \"O teu Nome\"\n"
+"    git config --global user.email tu@examplo.com\n"
+"\n"
+"Após fazer isso, você pode corregir a identidade usada em este commit com:\n"
+"\n"
+"    git commit --amend --reset-author\n"
 
-#: builtin/commit.c:54
+#: builtin/commit.c:55
 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:59
+#: builtin/commit.c:60
 msgid ""
 "The previous cherry-pick is now empty, possibly due to conflict resolution.\n"
 "If you wish to commit it anyway, use:\n"
@@ -1367,286 +1948,289 @@ msgid ""
 "Otherwise, please use 'git reset'\n"
 msgstr ""
 
-#: builtin/commit.c:205
-#: builtin/reset.c:33
-msgid "merge"
-msgstr "juntar"
-
-#: builtin/commit.c:208
-msgid "cherry-pick"
-msgstr "cherry-pick"
-
-#: builtin/commit.c:325
+#: builtin/commit.c:309
 msgid "failed to unpack HEAD tree object"
 msgstr ""
 
-#: builtin/commit.c:367
+#: builtin/commit.c:351
 msgid "unable to create temporary index"
 msgstr ""
 
-#: builtin/commit.c:373
+#: builtin/commit.c:357
 msgid "interactive add failed"
-msgstr ""
+msgstr "falhou adicionar interativo"
 
-#: builtin/commit.c:406
-#: builtin/commit.c:427
-#: builtin/commit.c:473
+#: builtin/commit.c:390
+#: builtin/commit.c:411
+#: builtin/commit.c:461
 msgid "unable to write new_index file"
 msgstr ""
 
-#: builtin/commit.c:457
-#, c-format
-msgid "cannot do a partial commit during a %s."
+#: builtin/commit.c:442
+msgid "cannot do a partial commit during a merge."
 msgstr ""
 
-#: builtin/commit.c:466
-msgid "cannot read the index"
+#: builtin/commit.c:444
+msgid "cannot do a partial commit during a cherry-pick."
 msgstr ""
 
-#: builtin/commit.c:486
+#: builtin/commit.c:454
+msgid "cannot read the index"
+msgstr "não foi possível ler o indíce"
+
+#: builtin/commit.c:474
 msgid "unable to write temporary index file"
 msgstr ""
 
-#: builtin/commit.c:550
-#: builtin/commit.c:556
+#: builtin/commit.c:549
+#: builtin/commit.c:555
 #, c-format
 msgid "invalid commit: %s"
 msgstr "commit inválido: %s"
 
-#: builtin/commit.c:579
+#: builtin/commit.c:578
 msgid "malformed --author parameter"
 msgstr ""
 
-#: builtin/commit.c:635
+#: builtin/commit.c:639
 #, c-format
 msgid "Malformed ident string: '%s'"
 msgstr ""
 
-#: builtin/commit.c:670
-#: builtin/commit.c:703
-#: builtin/commit.c:1000
+#: builtin/commit.c:677
+#: builtin/commit.c:710
+#: builtin/commit.c:1024
 #, c-format
 msgid "could not lookup commit %s"
 msgstr ""
 
-#: builtin/commit.c:682
+#: builtin/commit.c:689
 #: builtin/shortlog.c:296
 #, c-format
 msgid "(reading log message from standard input)\n"
 msgstr ""
 
-#: builtin/commit.c:684
+#: builtin/commit.c:691
 msgid "could not read log from standard input"
 msgstr ""
 
-#: builtin/commit.c:688
+#: builtin/commit.c:695
 #, c-format
 msgid "could not read log file '%s'"
-msgstr ""
+msgstr "não é possivel ler o ficheiro de log '%s'"
 
-#: builtin/commit.c:694
+#: builtin/commit.c:701
 msgid "commit has empty message"
-msgstr ""
+msgstr "a mensagem do commit está vazia"
 
-#: builtin/commit.c:710
+#: builtin/commit.c:717
 msgid "could not read MERGE_MSG"
-msgstr ""
+msgstr "não é possivel ler MERGE_MSG"
 
-#: builtin/commit.c:714
+#: builtin/commit.c:721
 msgid "could not read SQUASH_MSG"
-msgstr ""
+msgstr "não é possivel ler SQUASH_MSG"
 
-#: builtin/commit.c:718
+#: builtin/commit.c:725
 #, c-format
 msgid "could not read '%s'"
-msgstr ""
-
-#: builtin/commit.c:746
-#, c-format
-msgid "could not open '%s'"
-msgstr ""
+msgstr "não é possivel ler '%s'"
 
-#: builtin/commit.c:770
+#: builtin/commit.c:777
 msgid "could not write commit template"
 msgstr ""
 
-#: builtin/commit.c:783
+#: builtin/commit.c:788
 #, c-format
 msgid ""
 "\n"
-"It looks like you may be committing a %s.\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 ""
 
-#: builtin/commit.c:796
-msgid "Please enter the commit message for your changes."
+#: builtin/commit.c:793
+#, 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 ""
 
-#: builtin/commit.c:799
+#: builtin/commit.c:805
 msgid ""
-" Lines starting\n"
+"Please enter the commit message for your changes. Lines starting\n"
 "with '#' will be ignored, and an empty message aborts the commit.\n"
 msgstr ""
 
-#: builtin/commit.c:804
+#: builtin/commit.c:810
 msgid ""
-" Lines starting\n"
+"Please enter the commit message for your changes. Lines starting\n"
 "with '#' will be kept; you may remove them yourself if you want to.\n"
 "An empty message aborts the commit.\n"
 msgstr ""
 
-#: builtin/commit.c:816
+#: builtin/commit.c:823
 #, c-format
 msgid "%sAuthor:    %s"
 msgstr "%sAutor:    %s"
 
-#: builtin/commit.c:823
+#: builtin/commit.c:830
 #, c-format
 msgid "%sCommitter: %s"
 msgstr "%sCommitador: %s"
 
-#: builtin/commit.c:843
+#: builtin/commit.c:850
 msgid "Cannot read index"
 msgstr ""
 
-#: builtin/commit.c:880
+#: builtin/commit.c:887
 msgid "Error building trees"
 msgstr ""
 
-#: builtin/commit.c:895
-#: builtin/tag.c:357
+#: builtin/commit.c:902
+#: builtin/tag.c:361
 #, c-format
 msgid "Please supply the message using either -m or -F option.\n"
 msgstr ""
 
-#: builtin/commit.c:975
+#: builtin/commit.c:999
 #, c-format
 msgid "No existing author found with '%s'"
 msgstr ""
 
-#: builtin/commit.c:990
-#: builtin/commit.c:1182
+#: builtin/commit.c:1014
+#: builtin/commit.c:1214
 #, c-format
 msgid "Invalid untracked files mode '%s'"
 msgstr ""
 
-#: builtin/commit.c:1030
+#: builtin/commit.c:1054
 msgid "Using both --reset-author and --author does not make sense"
 msgstr ""
 
-#: builtin/commit.c:1041
+#: builtin/commit.c:1065
 msgid "You have nothing to amend."
+msgstr "Você não tem nada a corregir."
+
+#: builtin/commit.c:1068
+msgid "You are in the middle of a merge -- cannot amend."
 msgstr ""
 
-#: builtin/commit.c:1043
-#, c-format
-msgid "You are in the middle of a %s -- cannot amend."
+#: builtin/commit.c:1070
+msgid "You are in the middle of a cherry-pick -- cannot amend."
 msgstr ""
 
-#: builtin/commit.c:1045
+#: builtin/commit.c:1073
 msgid "Options --squash and --fixup cannot be used together"
 msgstr ""
 
-#: builtin/commit.c:1055
+#: builtin/commit.c:1083
 msgid "Only one of -c/-C/-F/--fixup can be used."
 msgstr ""
 
-#: builtin/commit.c:1057
+#: builtin/commit.c:1085
 msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
 msgstr ""
 
-#: builtin/commit.c:1063
+#: builtin/commit.c:1093
 msgid "--reset-author can be used only with -C, -c or --amend."
 msgstr ""
 
-#: builtin/commit.c:1080
+#: builtin/commit.c:1110
 msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
 msgstr ""
 
-#: builtin/commit.c:1082
+#: builtin/commit.c:1112
 msgid "No paths with --include/--only does not make sense."
 msgstr ""
 
-#: builtin/commit.c:1084
+#: builtin/commit.c:1114
 msgid "Clever... amending the last one with dirty index."
 msgstr ""
 
-#: builtin/commit.c:1086
+#: builtin/commit.c:1116
 msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
 msgstr ""
 
-#: builtin/commit.c:1096
-#: builtin/tag.c:556
+#: builtin/commit.c:1126
+#: builtin/tag.c:577
 #, c-format
 msgid "Invalid cleanup mode %s"
 msgstr ""
 
-#: builtin/commit.c:1101
+#: builtin/commit.c:1131
 msgid "Paths with -a does not make sense."
 msgstr ""
 
-#: builtin/commit.c:1280
+#: builtin/commit.c:1315
 msgid "couldn't look up newly created commit"
 msgstr ""
 
-#: builtin/commit.c:1282
+#: builtin/commit.c:1317
 msgid "could not parse newly created commit"
 msgstr ""
 
-#: builtin/commit.c:1323
+#: builtin/commit.c:1358
 msgid "detached HEAD"
 msgstr ""
 
-#: builtin/commit.c:1325
+#: builtin/commit.c:1360
 msgid " (root-commit)"
 msgstr " (root-commit)"
 
-#: builtin/commit.c:1415
+#: builtin/commit.c:1450
 msgid "could not parse HEAD commit"
 msgstr ""
 
-#: builtin/commit.c:1452
+#: builtin/commit.c:1487
 #: builtin/merge.c:509
 #, c-format
 msgid "could not open '%s' for reading"
 msgstr ""
 
-#: builtin/commit.c:1459
+#: builtin/commit.c:1494
 #, c-format
 msgid "Corrupt MERGE_HEAD file (%s)"
 msgstr ""
 
-#: builtin/commit.c:1466
+#: builtin/commit.c:1501
 msgid "could not read MERGE_MODE"
 msgstr ""
 
-#: builtin/commit.c:1485
+#: builtin/commit.c:1520
 #, c-format
 msgid "could not read commit message: %s"
 msgstr ""
 
-#: builtin/commit.c:1499
+#: builtin/commit.c:1534
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr ""
+
+#: builtin/commit.c:1539
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr ""
 
-#: builtin/commit.c:1514
-#: builtin/merge.c:935
-#: builtin/merge.c:968
+#: builtin/commit.c:1554
+#: builtin/merge.c:936
+#: builtin/merge.c:961
 msgid "failed to write commit object"
 msgstr ""
 
-#: builtin/commit.c:1535
+#: builtin/commit.c:1575
 msgid "cannot lock HEAD ref"
 msgstr ""
 
-#: builtin/commit.c:1539
+#: builtin/commit.c:1579
 msgid "cannot update HEAD ref"
 msgstr ""
 
-#: builtin/commit.c:1550
+#: builtin/commit.c:1590
 msgid ""
 "Repository has been updated, but unable to write\n"
 "new_index file. Check that disk is not full or quota is\n"
@@ -1725,7 +2309,7 @@ msgstr ""
 
 #: builtin/describe.c:462
 msgid "No names found, cannot describe anything."
-msgstr ""
+msgstr "Nenhum nome encontrado, não descreve nada."
 
 #: builtin/describe.c:482
 msgid "--dirty is incompatible with committishes"
@@ -1769,180 +2353,184 @@ msgstr ""
 msgid "Couldn't find remote ref HEAD"
 msgstr ""
 
-#: builtin/fetch.c:252
+#: builtin/fetch.c:253
 #, c-format
 msgid "object %s not found"
-msgstr ""
+msgstr "objecto %s não encontrado"
 
-#: builtin/fetch.c:258
+#: builtin/fetch.c:259
 msgid "[up to date]"
 msgstr "[Actualizada]"
 
-#: builtin/fetch.c:272
+#: builtin/fetch.c:273
 #, c-format
 msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
 msgstr ""
 
-#: builtin/fetch.c:273
-#: builtin/fetch.c:351
+#: builtin/fetch.c:274
+#: builtin/fetch.c:360
 msgid "[rejected]"
 msgstr "[rejeitado]"
 
-#: builtin/fetch.c:284
+#: builtin/fetch.c:285
 msgid "[tag update]"
 msgstr "[etiqueta actualizada]"
 
-#: builtin/fetch.c:286
-#: builtin/fetch.c:313
-#: builtin/fetch.c:331
+#: builtin/fetch.c:287
+#: builtin/fetch.c:322
+#: builtin/fetch.c:340
 msgid "  (unable to update local ref)"
 msgstr ""
 
-#: builtin/fetch.c:298
+#: builtin/fetch.c:305
 msgid "[new tag]"
 msgstr "[nova etiqueta]"
 
-#: builtin/fetch.c:302
+#: builtin/fetch.c:308
 msgid "[new branch]"
 msgstr "[nova rama]"
 
-#: builtin/fetch.c:347
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[nova ref]"
+
+#: builtin/fetch.c:356
 msgid "unable to update local ref"
 msgstr ""
 
-#: builtin/fetch.c:347
+#: builtin/fetch.c:356
 msgid "forced update"
 msgstr "actualização forçada"
 
-#: builtin/fetch.c:353
+#: builtin/fetch.c:362
 msgid "(non-fast-forward)"
-msgstr ""
+msgstr "(non-fast-forward)"
 
-#: builtin/fetch.c:384
-#: builtin/fetch.c:676
+#: builtin/fetch.c:393
+#: builtin/fetch.c:685
 #, c-format
 msgid "cannot open %s: %s\n"
-msgstr ""
+msgstr "não é possivel abrir %s: %s\n"
 
-#: builtin/fetch.c:393
+#: builtin/fetch.c:402
 #, c-format
 msgid "%s did not send all necessary objects\n"
 msgstr ""
 
-#: builtin/fetch.c:479
+#: builtin/fetch.c:488
 #, c-format
 msgid "From %.*s\n"
 msgstr "Para %.*s\n"
 
-#: builtin/fetch.c:490
+#: builtin/fetch.c:499
 #, 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:540
+#: builtin/fetch.c:549
 #, c-format
-msgid "   (%s will become dangling)\n"
+msgid "   (%s will become dangling)"
 msgstr ""
 
-#: builtin/fetch.c:541
+#: builtin/fetch.c:550
 #, c-format
-msgid "   (%s has become dangling)\n"
+msgid "   (%s has become dangling)"
 msgstr ""
 
-#: builtin/fetch.c:548
+#: builtin/fetch.c:557
 msgid "[deleted]"
 msgstr "[eliminado]"
 
-#: builtin/fetch.c:549
+#: builtin/fetch.c:558
+#: builtin/remote.c:1055
 msgid "(none)"
 msgstr "(nenhum)"
 
-#: builtin/fetch.c:666
+#: builtin/fetch.c:675
 #, c-format
 msgid "Refusing to fetch into current branch %s of non-bare repository"
 msgstr ""
 
-#: builtin/fetch.c:700
+#: builtin/fetch.c:709
 #, c-format
 msgid "Don't know how to fetch from %s"
 msgstr ""
 
-#: builtin/fetch.c:777
+#: builtin/fetch.c:786
 #, c-format
 msgid "Option \"%s\" value \"%s\" is not valid for %s"
 msgstr ""
 
-#: builtin/fetch.c:780
+#: builtin/fetch.c:789
 #, c-format
 msgid "Option \"%s\" is ignored for %s\n"
 msgstr ""
 
-#: builtin/fetch.c:879
+#: builtin/fetch.c:888
 #, c-format
 msgid "Fetching %s\n"
-msgstr ""
+msgstr "Baixando %s\n"
 
-#: builtin/fetch.c:881
+#: builtin/fetch.c:890
+#: builtin/remote.c:100
 #, c-format
 msgid "Could not fetch %s"
 msgstr ""
 
-#: builtin/fetch.c:898
+#: builtin/fetch.c:907
 msgid ""
 "No remote repository specified.  Please, specify either a URL or a\n"
 "remote name from which new revisions should be fetched."
 msgstr ""
+"Nenhum repositório remoto especificado. Por favor, especifique um URL ou o\n"
+"nome remoto a partir do qual novas revisões devem ser obtida."
 
-#: builtin/fetch.c:918
+#: builtin/fetch.c:927
 msgid "You need to specify a tag name."
-msgstr ""
+msgstr "Você precisa especificar um nome da etiqueta."
 
-#: builtin/fetch.c:970
+#: builtin/fetch.c:979
 msgid "fetch --all does not take a repository argument"
 msgstr ""
 
-#: builtin/fetch.c:972
+#: builtin/fetch.c:981
 msgid "fetch --all does not make sense with refspecs"
 msgstr ""
 
-#: builtin/fetch.c:983
+#: builtin/fetch.c:992
 #, c-format
 msgid "No such remote or remote group: %s"
 msgstr ""
 
-#: builtin/fetch.c:991
+#: builtin/fetch.c:1000
 msgid "Fetching a group and specifying refspecs does not make sense"
 msgstr ""
 
 #: builtin/gc.c:63
 #, c-format
 msgid "Invalid %s: '%s'"
-msgstr ""
+msgstr "Inválido %s: '%s'"
 
-#: builtin/gc.c:78
-msgid "Too many options specified"
-msgstr ""
-
-#: builtin/gc.c:103
+#: builtin/gc.c:90
 #, c-format
 msgid "insanely long object directory %.*s"
 msgstr ""
 
-#: builtin/gc.c:223
+#: builtin/gc.c:221
 #, c-format
 msgid "Auto packing the repository for optimum performance.\n"
 msgstr ""
 
-#: builtin/gc.c:226
+#: builtin/gc.c:224
 #, 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:256
+#: builtin/gc.c:251
 msgid "There are too many unreachable loose objects; run 'git prune' to remove them."
 msgstr ""
 
@@ -2006,6 +2594,314 @@ msgstr ""
 msgid "both --cached and trees are given."
 msgstr ""
 
+#: builtin/help.c:59
+#, c-format
+msgid "unrecognized help format '%s'"
+msgstr "formato ajuda não reconhecido '%s'"
+
+#: builtin/help.c:87
+msgid "Failed to start emacsclient."
+msgstr ""
+
+#: builtin/help.c:100
+msgid "Failed to parse emacsclient version."
+msgstr ""
+
+#: builtin/help.c:108
+#, c-format
+msgid "emacsclient version '%d' too old (< 22)."
+msgstr ""
+
+#: builtin/help.c:126
+#: builtin/help.c:154
+#: builtin/help.c:163
+#: builtin/help.c:171
+#, c-format
+msgid "failed to exec '%s': %s"
+msgstr ""
+
+#: builtin/help.c:211
+#, c-format
+msgid ""
+"'%s': path for unsupported man viewer.\n"
+"Please consider using 'man.<tool>.cmd' instead."
+msgstr ""
+
+#: builtin/help.c:223
+#, c-format
+msgid ""
+"'%s': cmd for supported man viewer.\n"
+"Please consider using 'man.<tool>.path' instead."
+msgstr ""
+
+#: builtin/help.c:287
+msgid "The most commonly used git commands are:"
+msgstr ""
+
+#: builtin/help.c:355
+#, c-format
+msgid "'%s': unknown man viewer."
+msgstr ""
+
+#: builtin/help.c:372
+msgid "no man viewer handled the request"
+msgstr ""
+
+#: builtin/help.c:380
+msgid "no info viewer handled the request"
+msgstr ""
+
+#: builtin/help.c:391
+#, c-format
+msgid "'%s': not a documentation directory."
+msgstr ""
+
+#: builtin/help.c:432
+#: builtin/help.c:439
+#, c-format
+msgid "usage: %s%s"
+msgstr ""
+
+#: builtin/help.c:453
+#, c-format
+msgid "`git %s' is aliased to `%s'"
+msgstr ""
+
+#: builtin/index-pack.c:84
+#, c-format
+msgid "object type mismatch at %s"
+msgstr ""
+
+#: builtin/index-pack.c:104
+msgid "object of unexpected type"
+msgstr ""
+
+#: builtin/index-pack.c:141
+#, c-format
+msgid "cannot fill %d byte"
+msgid_plural "cannot fill %d bytes"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:151
+msgid "early EOF"
+msgstr ""
+
+#: builtin/index-pack.c:152
+msgid "read error on input"
+msgstr ""
+
+#: builtin/index-pack.c:164
+msgid "used more bytes than were available"
+msgstr ""
+
+#: builtin/index-pack.c:171
+msgid "pack too large for current definition of off_t"
+msgstr ""
+
+#: builtin/index-pack.c:187
+#, c-format
+msgid "unable to create '%s'"
+msgstr "não é possivel crear '%s'"
+
+#: builtin/index-pack.c:192
+#, c-format
+msgid "cannot open packfile '%s'"
+msgstr "Não é possivel abrir o ficheiro packfile '%s'"
+
+#: builtin/index-pack.c:206
+msgid "pack signature mismatch"
+msgstr ""
+
+#: builtin/index-pack.c:226
+#, c-format
+msgid "pack has bad object at offset %lu: %s"
+msgstr ""
+
+#: builtin/index-pack.c:300
+#, c-format
+msgid "inflate returned %d"
+msgstr ""
+
+#: builtin/index-pack.c:345
+msgid "offset value overflow for delta base object"
+msgstr ""
+
+#: builtin/index-pack.c:353
+msgid "delta base offset is out of bound"
+msgstr ""
+
+#: builtin/index-pack.c:361
+#, c-format
+msgid "unknown object type %d"
+msgstr "ojecto com tipo desconhecido %d"
+
+#: builtin/index-pack.c:390
+msgid "cannot pread pack file"
+msgstr "Não é possivel pread pack file"
+
+#: builtin/index-pack.c:392
+#, 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:405
+msgid "serious inflate inconsistency"
+msgstr ""
+
+#: builtin/index-pack.c:476
+#, c-format
+msgid "cannot read existing object %s"
+msgstr "não foi possível ler objecto existente %s"
+
+#: builtin/index-pack.c:479
+#, c-format
+msgid "SHA1 COLLISION FOUND WITH %s !"
+msgstr ""
+
+#: builtin/index-pack.c:488
+#, c-format
+msgid "invalid blob object %s"
+msgstr "inválido objecto blob %s"
+
+#: builtin/index-pack.c:500
+#, c-format
+msgid "invalid %s"
+msgstr "inválido: %s"
+
+#: builtin/index-pack.c:502
+msgid "Error in object"
+msgstr ""
+
+#: builtin/index-pack.c:504
+#, c-format
+msgid "Not all child objects of %s are reachable"
+msgstr ""
+
+#: builtin/index-pack.c:576
+#: builtin/index-pack.c:602
+msgid "failed to apply delta"
+msgstr ""
+
+#: builtin/index-pack.c:706
+msgid "Receiving objects"
+msgstr ""
+
+#: builtin/index-pack.c:706
+msgid "Indexing objects"
+msgstr ""
+
+#: builtin/index-pack.c:728
+msgid "pack is corrupted (SHA1 mismatch)"
+msgstr ""
+
+#: builtin/index-pack.c:733
+msgid "cannot fstat packfile"
+msgstr "Não é possivel fstat packfile"
+
+#: builtin/index-pack.c:736
+msgid "pack has junk at the end"
+msgstr ""
+
+#: builtin/index-pack.c:754
+msgid "Resolving deltas"
+msgstr "Resolvendo deltas"
+
+#: builtin/index-pack.c:787
+#, c-format
+msgid "unable to deflate appended object (%d)"
+msgstr ""
+
+#: builtin/index-pack.c:866
+#, c-format
+msgid "local object %s is corrupt"
+msgstr ""
+
+#: builtin/index-pack.c:890
+msgid "error while closing pack file"
+msgstr ""
+
+#: builtin/index-pack.c:903
+#, c-format
+msgid "cannot write keep file '%s'"
+msgstr "não é possivel escrever o fichero kepp '%s'"
+
+#: builtin/index-pack.c:911
+#, c-format
+msgid "cannot close written keep file '%s'"
+msgstr "Não é possivel fechar o fichero escrito '%s'"
+
+#: builtin/index-pack.c:924
+msgid "cannot store pack file"
+msgstr "Não é possivel guardar o fichero pack"
+
+#: builtin/index-pack.c:935
+msgid "cannot store index file"
+msgstr "Não é possivel guardar fichero index"
+
+#: builtin/index-pack.c:1024
+#, c-format
+msgid "Cannot open existing pack file '%s'"
+msgstr "Não é possivel abrir o existente ficheiro pack %s"
+
+#: builtin/index-pack.c:1026
+#, c-format
+msgid "Cannot open existing pack idx file for '%s'"
+msgstr "Não é possivel abrir o ficheiro 'pack idx' para '%s'"
+
+#: builtin/index-pack.c:1073
+#, c-format
+msgid "non delta: %d object"
+msgid_plural "non delta: %d objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1080
+#, c-format
+msgid "chain length = %d: %lu object"
+msgid_plural "chain length = %d: %lu objects"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/index-pack.c:1107
+msgid "Cannot come back to cwd"
+msgstr ""
+
+#: builtin/index-pack.c:1140
+#: builtin/index-pack.c:1143
+#: builtin/index-pack.c:1155
+#: builtin/index-pack.c:1159
+#, c-format
+msgid "bad %s"
+msgstr "inválido %s"
+
+#: builtin/index-pack.c:1173
+msgid "--fix-thin cannot be used without --stdin"
+msgstr ""
+
+#: builtin/index-pack.c:1177
+#: builtin/index-pack.c:1187
+#, c-format
+msgid "packfile name '%s' does not end with '.pack'"
+msgstr ""
+
+#: builtin/index-pack.c:1196
+msgid "--verify with no packfile name given"
+msgstr ""
+
+#: builtin/index-pack.c:1220
+msgid "confusion beyond insanity"
+msgstr ""
+
+#: builtin/index-pack.c:1239
+#, c-format
+msgid "pack has %d unresolved delta"
+msgid_plural "pack has %d unresolved deltas"
+msgstr[0] ""
+msgstr[1] ""
+
 #: builtin/init-db.c:35
 #, c-format
 msgid "Could not make %s writable by group"
@@ -2148,167 +3044,161 @@ msgstr ""
 msgid "Cannot access work tree '%s'"
 msgstr ""
 
-#: builtin/log.c:187
+#: builtin/log.c:188
 #, c-format
 msgid "Final output: %d %s\n"
 msgstr ""
 
-#: builtin/log.c:395
-#: builtin/log.c:483
+#: builtin/log.c:401
+#: builtin/log.c:489
 #, c-format
 msgid "Could not read object %s"
 msgstr ""
 
-#: builtin/log.c:507
+#: builtin/log.c:513
 #, c-format
 msgid "Unknown type: %d"
-msgstr ""
+msgstr "Tipo desconhecido: %d"
 
-#: builtin/log.c:596
+#: builtin/log.c:602
 msgid "format.headers without value"
 msgstr ""
 
-#: builtin/log.c:669
+#: builtin/log.c:675
 msgid "name of output directory is too long"
-msgstr ""
+msgstr "nome do diretório de saída é demasiado longo"
 
-#: builtin/log.c:680
+#: builtin/log.c:686
 #, c-format
 msgid "Cannot open patch file %s"
-msgstr ""
+msgstr "Não é possivel abrir o ficheiro patch %s"
 
-#: builtin/log.c:694
+#: builtin/log.c:700
 msgid "Need exactly one range."
-msgstr ""
+msgstr "Necessita de exatamente um intervalo."
 
-#: builtin/log.c:702
+#: builtin/log.c:708
 msgid "Not a range."
-msgstr ""
+msgstr "Não é um intervalo."
 
-#: builtin/log.c:739
+#: builtin/log.c:745
 msgid "Could not extract email from committer identity."
-msgstr ""
+msgstr "Não foi possível extrair a identidade do committer do e-mail."
 
-#: builtin/log.c:785
+#: builtin/log.c:791
 msgid "Cover letter needs email format"
-msgstr ""
+msgstr "Carta de apresentação necessita um modelo de e-mail"
 
-#: builtin/log.c:879
+#: builtin/log.c:885
 #, c-format
 msgid "insane in-reply-to: %s"
 msgstr ""
 
-#: builtin/log.c:952
+#: builtin/log.c:958
 msgid "Two output directories?"
-msgstr ""
+msgstr "Dois diretórios de saída?"
 
-#: builtin/log.c:1173
+#: builtin/log.c:1179
 #, c-format
 msgid "bogus committer info %s"
 msgstr ""
 
-#: builtin/log.c:1218
+#: builtin/log.c:1224
 msgid "-n and -k are mutually exclusive."
 msgstr ""
 
-#: builtin/log.c:1220
+#: builtin/log.c:1226
 msgid "--subject-prefix and -k are mutually exclusive."
 msgstr ""
 
-#: builtin/log.c:1225
-#: builtin/shortlog.c:284
-#, c-format
-msgid "unrecognized argument: %s"
-msgstr ""
-
-#: builtin/log.c:1228
+#: builtin/log.c:1234
 msgid "--name-only does not make sense"
 msgstr ""
 
-#: builtin/log.c:1230
+#: builtin/log.c:1236
 msgid "--name-status does not make sense"
 msgstr ""
 
-#: builtin/log.c:1232
+#: builtin/log.c:1238
 msgid "--check does not make sense"
 msgstr ""
 
-#: builtin/log.c:1255
+#: builtin/log.c:1261
 msgid "standard output, or directory, which one?"
-msgstr ""
+msgstr "saída padrão, ou diretório, qual deles?"
 
-#: builtin/log.c:1257
+#: builtin/log.c:1263
 #, c-format
 msgid "Could not create directory '%s'"
 msgstr ""
 
-#: builtin/log.c:1410
+#: builtin/log.c:1416
 msgid "Failed to create output files"
-msgstr ""
+msgstr "Falhou ao criar ficheiros de saída"
 
-#: builtin/log.c:1514
+#: builtin/log.c:1520
 #, c-format
 msgid "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr ""
 
-#: builtin/log.c:1530
-#: builtin/log.c:1532
-#: builtin/log.c:1544
+#: builtin/log.c:1536
+#: builtin/log.c:1538
+#: builtin/log.c:1550
 #, c-format
 msgid "Unknown commit %s"
 msgstr "Commit desconhecido %s"
 
-#: builtin/merge.c:91
+#: builtin/merge.c:90
 msgid "switch `m' requires a value"
 msgstr ""
 
-#: builtin/merge.c:128
+#: builtin/merge.c:127
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
 msgstr ""
 
-#: builtin/merge.c:129
+#: builtin/merge.c:128
 #, c-format
 msgid "Available strategies are:"
-msgstr ""
+msgstr "As estratégias disponíveis são:"
 
-#: builtin/merge.c:134
+#: builtin/merge.c:133
 #, c-format
 msgid "Available custom strategies are:"
-msgstr ""
+msgstr "Estratégias personalizadas disponíveis são:"
 
-#: builtin/merge.c:241
+#: builtin/merge.c:240
 msgid "could not run stash."
 msgstr ""
 
-#: builtin/merge.c:246
+#: builtin/merge.c:245
 msgid "stash failed"
 msgstr "falhou o stash"
 
-#: builtin/merge.c:251
+#: builtin/merge.c:250
 #, c-format
 msgid "not a valid object: %s"
 msgstr ""
 
-#: builtin/merge.c:270
-#: builtin/merge.c:287
+#: builtin/merge.c:269
+#: builtin/merge.c:286
 msgid "read-tree failed"
 msgstr ""
 
-#: builtin/merge.c:317
+#: builtin/merge.c:316
 msgid " (nothing to squash)"
 msgstr " (nada para squash)"
 
-#: builtin/merge.c:330
+#: builtin/merge.c:329
 #, c-format
 msgid "Squash commit -- not updating HEAD\n"
 msgstr ""
 
-#: builtin/merge.c:362
+#: builtin/merge.c:361
 msgid "Writing SQUASH_MSG"
 msgstr "Escrevendo SQUASH_MSG"
 
-#: builtin/merge.c:364
+#: builtin/merge.c:363
 msgid "Finishing SQUASH_MSG"
 msgstr "Terminando SQUASH_MSG"
 
@@ -2335,35 +3225,35 @@ msgstr ""
 msgid "failed to read the cache"
 msgstr ""
 
-#: builtin/merge.c:696
+#: builtin/merge.c:697
 msgid "Unable to write index."
 msgstr ""
 
-#: builtin/merge.c:709
+#: builtin/merge.c:710
 msgid "Not handling anything other than two heads merge."
 msgstr ""
 
-#: builtin/merge.c:723
+#: builtin/merge.c:724
 #, c-format
 msgid "Unknown option for merge-recursive: -X%s"
 msgstr ""
 
-#: builtin/merge.c:737
+#: builtin/merge.c:738
 #, c-format
 msgid "unable to write %s"
 msgstr ""
 
-#: builtin/merge.c:876
+#: builtin/merge.c:877
 #, c-format
 msgid "Could not read from '%s'"
 msgstr ""
 
-#: builtin/merge.c:885
+#: builtin/merge.c:886
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
-msgstr ""
+msgstr "Não commitando um merge; usa 'git commit' para completar o merge.\n"
 
-#: builtin/merge.c:891
+#: builtin/merge.c:892
 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"
@@ -2372,144 +3262,144 @@ msgid ""
 "the commit.\n"
 msgstr ""
 
-#: builtin/merge.c:915
+#: builtin/merge.c:916
 msgid "Empty commit message."
-msgstr ""
+msgstr "Mensagem de commit vazia."
 
-#: builtin/merge.c:927
+#: builtin/merge.c:928
 #, c-format
 msgid "Wonderful.\n"
 msgstr "Fastastico.\n"
 
-#: builtin/merge.c:1000
+#: builtin/merge.c:993
 #, c-format
 msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
 msgstr ""
 
-#: builtin/merge.c:1016
+#: builtin/merge.c:1009
 #, c-format
 msgid "'%s' is not a commit"
 msgstr "'%s' não é um commit"
 
-#: builtin/merge.c:1057
+#: builtin/merge.c:1050
 msgid "No current branch."
 msgstr "Nenhuma rama actual"
 
-#: builtin/merge.c:1059
+#: builtin/merge.c:1052
 msgid "No remote for the current branch."
 msgstr ""
 
-#: builtin/merge.c:1061
+#: builtin/merge.c:1054
 msgid "No default upstream defined for the current branch."
 msgstr ""
 
-#: builtin/merge.c:1066
+#: builtin/merge.c:1059
 #, c-format
 msgid "No remote tracking branch for %s from %s"
 msgstr ""
 
-#: builtin/merge.c:1188
+#: builtin/merge.c:1146
+#: builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr ""
+
+#: builtin/merge.c:1214
 msgid "There is no merge to abort (MERGE_HEAD missing)."
 msgstr ""
 
-#: builtin/merge.c:1204
+#: builtin/merge.c:1230
 #: 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:1207
+#: builtin/merge.c:1233
 #: git-pull.sh:34
 msgid "You have not concluded your merge (MERGE_HEAD exists)."
 msgstr ""
 
-#: builtin/merge.c:1211
+#: builtin/merge.c:1237
 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:1214
+#: builtin/merge.c:1240
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
 msgstr ""
 
-#: builtin/merge.c:1223
+#: builtin/merge.c:1249
 msgid "You cannot combine --squash with --no-ff."
 msgstr ""
 
-#: builtin/merge.c:1228
+#: builtin/merge.c:1254
 msgid "You cannot combine --no-ff with --ff-only."
 msgstr ""
 
-#: builtin/merge.c:1235
+#: builtin/merge.c:1261
 msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr ""
 
-#: builtin/merge.c:1266
+#: builtin/merge.c:1293
 msgid "Can merge only exactly one commit into empty head"
 msgstr ""
 
-#: builtin/merge.c:1269
+#: builtin/merge.c:1296
 msgid "Squash commit into empty head not supported yet"
 msgstr ""
 
-#: builtin/merge.c:1271
+#: builtin/merge.c:1298
 msgid "Non-fast-forward commit does not make sense into an empty head"
 msgstr ""
 
-#: builtin/merge.c:1275
-#: builtin/merge.c:1319
-#, c-format
-msgid "%s - not something we can merge"
-msgstr ""
-
-#: builtin/merge.c:1385
+#: builtin/merge.c:1413
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr "Actualizando %s..%s\n"
 
-#: builtin/merge.c:1423
+#: builtin/merge.c:1451
 #, c-format
 msgid "Trying really trivial in-index merge...\n"
 msgstr ""
 
-#: builtin/merge.c:1430
+#: builtin/merge.c:1458
 #, c-format
 msgid "Nope.\n"
 msgstr "Não.\n"
 
-#: builtin/merge.c:1462
+#: builtin/merge.c:1490
 msgid "Not possible to fast-forward, aborting."
 msgstr ""
 
-#: builtin/merge.c:1485
-#: builtin/merge.c:1562
+#: builtin/merge.c:1513
+#: builtin/merge.c:1592
 #, c-format
 msgid "Rewinding the tree to pristine...\n"
 msgstr ""
 
-#: builtin/merge.c:1489
+#: builtin/merge.c:1517
 #, c-format
 msgid "Trying merge strategy %s...\n"
 msgstr ""
 
-#: builtin/merge.c:1553
+#: builtin/merge.c:1583
 #, c-format
 msgid "No merge strategy handled the merge.\n"
 msgstr ""
 
-#: builtin/merge.c:1555
+#: builtin/merge.c:1585
 #, c-format
 msgid "Merge with strategy %s failed.\n"
-msgstr ""
+msgstr "Fundir com a estratégia %s falhou.\n"
 
-#: builtin/merge.c:1564
+#: builtin/merge.c:1594
 #, c-format
 msgid "Using the %s to prepare resolving by hand.\n"
 msgstr ""
 
-#: builtin/merge.c:1575
+#: builtin/merge.c:1606
 #, c-format
 msgid "Automatic merge went well; stopped before committing as requested\n"
 msgstr ""
@@ -2538,15 +3428,15 @@ msgstr ""
 
 #: builtin/mv.c:140
 msgid "source directory is empty"
-msgstr ""
+msgstr "o directorio fonte está vazio"
 
 #: builtin/mv.c:171
 msgid "not under version control"
-msgstr ""
+msgstr "não está no controlo de versões"
 
 #: builtin/mv.c:173
 msgid "destination exists"
-msgstr ""
+msgstr "existe destino"
 
 #: builtin/mv.c:181
 #, c-format
@@ -2559,7 +3449,7 @@ msgstr "Não consegue subscrever"
 
 #: builtin/mv.c:187
 msgid "multiple sources for the same target"
-msgstr ""
+msgstr "múltiplas fontes para o mesmo alvo"
 
 #: builtin/mv.c:202
 #, c-format
@@ -2569,12 +3459,13 @@ msgstr ""
 #: builtin/mv.c:212
 #, c-format
 msgid "Renaming %s to %s\n"
-msgstr ""
+msgstr "Mudar de nome %s para %s\n"
 
 #: builtin/mv.c:215
+#: builtin/remote.c:731
 #, c-format
 msgid "renaming '%s' failed"
-msgstr ""
+msgstr "mudar de nome '%s' falhou"
 
 #: builtin/notes.c:139
 #, c-format
@@ -2596,7 +3487,7 @@ msgid "failed to finish 'show' for object '%s'"
 msgstr ""
 
 #: builtin/notes.c:175
-#: builtin/tag.c:343
+#: builtin/tag.c:347
 #, c-format
 msgid "could not create file '%s'"
 msgstr ""
@@ -2621,13 +3512,13 @@ msgid "The note contents has been left in %s"
 msgstr ""
 
 #: builtin/notes.c:251
-#: builtin/tag.c:521
+#: builtin/tag.c:542
 #, c-format
 msgid "cannot read '%s'"
 msgstr "não consegue ler '%s'"
 
 #: builtin/notes.c:253
-#: builtin/tag.c:524
+#: builtin/tag.c:545
 #, c-format
 msgid "could not open or read '%s'"
 msgstr ""
@@ -2643,7 +3534,7 @@ msgstr ""
 #: builtin/notes.c:766
 #: builtin/notes.c:968
 #: builtin/reset.c:293
-#: builtin/tag.c:537
+#: builtin/tag.c:558
 #, c-format
 msgid "Failed to resolve '%s' as a valid ref."
 msgstr ""
@@ -2672,7 +3563,7 @@ msgstr ""
 #: builtin/notes.c:377
 #, c-format
 msgid "Bad %s value: '%s'"
-msgstr ""
+msgstr "Inválido %s valor: '%s'"
 
 #: builtin/notes.c:441
 #, c-format
@@ -2692,13 +3583,13 @@ msgstr ""
 #: builtin/notes.c:759
 #: builtin/notes.c:1033
 msgid "too many parameters"
-msgstr ""
+msgstr "demasiado parametros"
 
 #: builtin/notes.c:513
 #: builtin/notes.c:772
 #, c-format
 msgid "No note found for object %s."
-msgstr ""
+msgstr "Nenhuma nota encontrada para o objecto %s."
 
 #: builtin/notes.c:580
 #, c-format
@@ -2738,39 +3629,61 @@ msgid "Object %s has no note\n"
 msgstr ""
 
 #: builtin/notes.c:1103
+#: builtin/remote.c:1598
 #, c-format
 msgid "Unknown subcommand: %s"
 msgstr ""
 
-#: builtin/pack-objects.c:2310
+#: builtin/pack-objects.c:2315
 #, c-format
 msgid "unsupported index version %s"
 msgstr ""
 
-#: builtin/pack-objects.c:2314
+#: builtin/pack-objects.c:2319
 #, c-format
 msgid "bad index version '%s'"
 msgstr ""
 
-#: builtin/pack-objects.c:2322
+#: builtin/pack-objects.c:2342
 #, c-format
 msgid "option %s does not accept negative form"
-msgstr ""
+msgstr "opção %s não aceita formato negativo"
 
-#: builtin/pack-objects.c:2326
+#: builtin/pack-objects.c:2346
 #, c-format
 msgid "unable to parse value '%s' for option %s"
 msgstr ""
 
-#: builtin/push.c:44
+#: builtin/push.c:45
 msgid "tag shorthand without <tag>"
 msgstr ""
 
-#: builtin/push.c:63
+#: builtin/push.c:64
 msgid "--delete only accepts plain target ref names"
+msgstr "--delete só aceita nomes simples para o ref de destino"
+
+#: 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:73
+#: builtin/push.c:121
 #, c-format
 msgid ""
 "You are not currently on a branch.\n"
@@ -2780,7 +3693,7 @@ msgid ""
 "    git push %s HEAD:<name-of-remote-branch>\n"
 msgstr ""
 
-#: builtin/push.c:80
+#: builtin/push.c:128
 #, c-format
 msgid ""
 "The current branch %s has no upstream branch.\n"
@@ -2789,39 +3702,63 @@ msgid ""
 "    git push --set-upstream %s %s\n"
 msgstr ""
 
-#: builtin/push.c:88
+#: builtin/push.c:136
 #, c-format
 msgid "The current branch %s has multiple upstream branches, refusing to push."
 msgstr ""
 
-#: builtin/push.c:111
+#: 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:174
 msgid "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr ""
 
-#: builtin/push.c:131
+#: builtin/push.c:181
+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:187
+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\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+
+#: builtin/push.c:193
+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:233
 #, c-format
 msgid "Pushing to %s\n"
 msgstr "Pushing para %s\n"
 
-#: builtin/push.c:135
+#: builtin/push.c:237
 #, c-format
 msgid "failed to push some refs to '%s'"
 msgstr ""
 
-#: builtin/push.c:143
-#, c-format
-msgid ""
-"To prevent you from losing history, non-fast-forward updates were rejected\n"
-"Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
-"'Note about fast-forwards' section of 'git push --help' for details.\n"
-msgstr ""
-
-#: builtin/push.c:160
+#: builtin/push.c:269
 #, c-format
 msgid "bad repository '%s'"
 msgstr "repositorio inválido '%s'"
 
-#: builtin/push.c:161
+#: builtin/push.c:270
 msgid ""
 "No configured push destination.\n"
 "Either specify the URL from the command-line or configure a remote repository using\n"
@@ -2833,34 +3770,390 @@ msgid ""
 "    git push <name>\n"
 msgstr ""
 
-#: builtin/push.c:176
+#: builtin/push.c:285
 msgid "--all and --tags are incompatible"
-msgstr ""
+msgstr "--all e --tags are são incompatíveis"
 
-#: builtin/push.c:177
+#: builtin/push.c:286
 msgid "--all can't be combined with refspecs"
 msgstr ""
 
-#: builtin/push.c:182
+#: builtin/push.c:291
 msgid "--mirror and --tags are incompatible"
 msgstr ""
 
-#: builtin/push.c:183
+#: builtin/push.c:292
 msgid "--mirror can't be combined with refspecs"
 msgstr ""
 
-#: builtin/push.c:188
+#: builtin/push.c:297
 msgid "--all and --mirror are incompatible"
 msgstr ""
 
-#: builtin/push.c:274
+#: builtin/push.c:385
 msgid "--delete is incompatible with --all, --mirror and --tags"
 msgstr ""
 
-#: builtin/push.c:276
+#: builtin/push.c:387
 msgid "--delete doesn't make sense without any refs"
 msgstr ""
 
+#: builtin/remote.c:98
+#, c-format
+msgid "Updating %s"
+msgstr "Actualizando %s"
+
+#: builtin/remote.c:130
+msgid ""
+"--mirror is dangerous and deprecated; please\n"
+"\t use --mirror=fetch or --mirror=push instead"
+msgstr ""
+
+#: builtin/remote.c:147
+#, c-format
+msgid "unknown mirror argument: %s"
+msgstr "argumento mirror não conhecido: %s"
+
+#: builtin/remote.c:185
+msgid "specifying a master branch makes no sense with --mirror"
+msgstr ""
+
+#: builtin/remote.c:187
+msgid "specifying branches to track makes sense only with fetch mirrors"
+msgstr ""
+
+#: builtin/remote.c:195
+#: builtin/remote.c:646
+#, c-format
+msgid "remote %s already exists."
+msgstr "o remoto %s já existe"
+
+#: builtin/remote.c:199
+#: builtin/remote.c:650
+#, c-format
+msgid "'%s' is not a valid remote name"
+msgstr "'%s' não é um nombe remoto valido"
+
+#: builtin/remote.c:243
+#, c-format
+msgid "Could not setup master '%s'"
+msgstr "Não foi possível configurar a rama master '%s'"
+
+#: builtin/remote.c:299
+#, c-format
+msgid "more than one %s"
+msgstr ""
+
+#: 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 ""
+
+#: builtin/remote.c:452
+msgid "(delete)"
+msgstr "(eliminado)"
+
+#: builtin/remote.c:595
+#: builtin/remote.c:601
+#: builtin/remote.c:607
+#, c-format
+msgid "Could not append '%s' to '%s'"
+msgstr "Não foi possível adicionar o '%s' para o '%s'"
+
+#: builtin/remote.c:639
+#: builtin/remote.c:792
+#: builtin/remote.c:890
+#, c-format
+msgid "No such remote: %s"
+msgstr ""
+
+#: builtin/remote.c:656
+#, c-format
+msgid "Could not rename config section '%s' to '%s'"
+msgstr "Não foi possível renombrar a secção da configuração de '%s' para '%s'"
+
+#: builtin/remote.c:662
+#: builtin/remote.c:799
+#, c-format
+msgid "Could not remove config section '%s'"
+msgstr "Não foi possível remover a secção da configuração '%s'"
+
+#: builtin/remote.c:677
+#, c-format
+msgid ""
+"Not updating non-default fetch respec\n"
+"\t%s\n"
+"\tPlease update the configuration manually if necessary."
+msgstr ""
+
+#: builtin/remote.c:683
+#, c-format
+msgid "Could not append '%s'"
+msgstr "Não foi possível adicionar '%s'"
+
+#: builtin/remote.c:694
+#, c-format
+msgid "Could not set '%s'"
+msgstr "Não foi possível atribuir '%s'"
+
+#: builtin/remote.c:716
+#, c-format
+msgid "deleting '%s' failed"
+msgstr "falhou eliminar '%s'"
+
+#: builtin/remote.c:750
+#, c-format
+msgid "creating '%s' failed"
+msgstr "falhou a criar '%s'"
+
+#: builtin/remote.c:764
+#, c-format
+msgid "Could not remove branch %s"
+msgstr "Não foi possível remover rama %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] ""
+msgstr[1] ""
+
+#: builtin/remote.c:943
+#, c-format
+msgid " new (next fetch will store in remotes/%s)"
+msgstr ""
+
+#: builtin/remote.c:946
+msgid " tracked"
+msgstr "seguido"
+
+#: builtin/remote.c:948
+msgid " stale (use 'git remote prune' to remove)"
+msgstr ""
+
+#: builtin/remote.c:950
+msgid " ???"
+msgstr " ???"
+
+#: builtin/remote.c:991
+#, c-format
+msgid "invalid branch.%s.merge; cannot rebase onto > 1 branch"
+msgstr ""
+
+#: builtin/remote.c:998
+#, c-format
+msgid "rebases onto remote %s"
+msgstr ""
+
+#: builtin/remote.c:1001
+#, c-format
+msgid " merges with remote %s"
+msgstr "Fundir com servidor remoto %s"
+
+#: builtin/remote.c:1002
+msgid "    and with remote"
+msgstr "    e com remoto"
+
+#: builtin/remote.c:1004
+#, c-format
+msgid "merges with remote %s"
+msgstr "Fundir com servidor remoto %s"
+
+#: builtin/remote.c:1005
+msgid "   and with remote"
+msgstr "   e com remoto"
+
+#: builtin/remote.c:1051
+msgid "create"
+msgstr "creado"
+
+#: builtin/remote.c:1054
+msgid "delete"
+msgstr "eliminado"
+
+#: builtin/remote.c:1058
+msgid "up to date"
+msgstr "actualizado"
+
+#: builtin/remote.c:1061
+msgid "fast-forwardable"
+msgstr "fast-forwardable"
+
+#: builtin/remote.c:1064
+msgid "local out of date"
+msgstr "local desatualizada"
+
+#: builtin/remote.c:1071
+#, c-format
+msgid "    %-*s forces to %-*s (%s)"
+msgstr ""
+
+#: builtin/remote.c:1074
+#, c-format
+msgid "    %-*s pushes to %-*s (%s)"
+msgstr ""
+
+#: builtin/remote.c:1078
+#, c-format
+msgid "    %-*s forces to %s"
+msgstr ""
+
+#: builtin/remote.c:1081
+#, c-format
+msgid "    %-*s pushes to %s"
+msgstr ""
+
+#: builtin/remote.c:1118
+#, c-format
+msgid "* remote %s"
+msgstr "* remota %s"
+
+#: builtin/remote.c:1119
+#, c-format
+msgid "  Fetch URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1120
+#: builtin/remote.c:1285
+msgid "(no URL)"
+msgstr "(nenhum URL)"
+
+#: builtin/remote.c:1129
+#: builtin/remote.c:1131
+#, c-format
+msgid "  Push  URL: %s"
+msgstr ""
+
+#: builtin/remote.c:1133
+#: builtin/remote.c:1135
+#: builtin/remote.c:1137
+#, c-format
+msgid "  HEAD branch: %s"
+msgstr "Rama HEAD: %s"
+
+#: builtin/remote.c:1139
+#, c-format
+msgid "  HEAD branch (remote HEAD is ambiguous, may be one of the following):\n"
+msgstr ""
+
+#: builtin/remote.c:1151
+#, c-format
+msgid "  Remote branch:%s"
+msgid_plural "  Remote branches:%s"
+msgstr[0] "Rama remota:%s"
+msgstr[1] "Ramas remotas:%s'"
+
+#: builtin/remote.c:1154
+#: builtin/remote.c:1181
+msgid " (status not queried)"
+msgstr ""
+
+#: builtin/remote.c:1163
+msgid "  Local branch configured for 'git pull':"
+msgid_plural "  Local branches configured for 'git pull':"
+msgstr[0] ""
+msgstr[1] ""
+
+#: builtin/remote.c:1171
+msgid "  Local refs will be mirrored by 'git push'"
+msgstr ""
+
+#: 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] ""
+msgstr[1] ""
+
+#: builtin/remote.c:1216
+msgid "Cannot determine remote HEAD"
+msgstr ""
+
+#: builtin/remote.c:1218
+msgid "Multiple remote HEAD branches. Please choose one explicitly with:"
+msgstr ""
+
+#: builtin/remote.c:1228
+#, c-format
+msgid "Could not delete %s"
+msgstr "Não foi possível abrir %s"
+
+#: builtin/remote.c:1236
+#, c-format
+msgid "Not a valid ref: %s"
+msgstr ""
+
+#: builtin/remote.c:1238
+#, c-format
+msgid "Could not setup %s"
+msgstr "Não foi possível configurar %s"
+
+#: builtin/remote.c:1274
+#, c-format
+msgid " %s will become dangling!"
+msgstr ""
+
+#: builtin/remote.c:1275
+#, c-format
+msgid " %s has become dangling!"
+msgstr ""
+
+#: builtin/remote.c:1281
+#, c-format
+msgid "Pruning %s"
+msgstr "Apagando %s"
+
+#: builtin/remote.c:1282
+#, c-format
+msgid "URL: %s"
+msgstr "URL: %s"
+
+#: builtin/remote.c:1295
+#, c-format
+msgid " * [would prune] %s"
+msgstr ""
+
+#: builtin/remote.c:1298
+#, c-format
+msgid " * [pruned] %s"
+msgstr ""
+
+#: builtin/remote.c:1387
+#: builtin/remote.c:1461
+#, c-format
+msgid "No such remote '%s'"
+msgstr "Não existe este remoto '%s'"
+
+#: builtin/remote.c:1414
+msgid "no remote specified"
+msgstr "Nenhum remoto especificado"
+
+#: builtin/remote.c:1447
+msgid "--add --delete doesn't make sense"
+msgstr ""
+
+#: builtin/remote.c:1487
+#, c-format
+msgid "Invalid old URL pattern: %s"
+msgstr ""
+
+#: builtin/remote.c:1495
+#, c-format
+msgid "No such URL found: %s"
+msgstr "Nenhum URL encontrado: %s"
+
+#: builtin/remote.c:1497
+msgid "Will not delete all non-push URLs"
+msgstr ""
+
 #: builtin/reset.c:33
 msgid "mixed"
 msgstr "mistura"
@@ -2873,13 +4166,17 @@ msgstr "leve"
 msgid "hard"
 msgstr "forte"
 
+#: builtin/reset.c:33
+msgid "merge"
+msgstr "juntar"
+
 #: builtin/reset.c:33
 msgid "keep"
 msgstr "manter"
 
 #: builtin/reset.c:77
 msgid "You do not have a valid HEAD."
-msgstr ""
+msgstr "Não tens a HEAD válida."
 
 #: builtin/reset.c:79
 msgid "Failed to find tree of HEAD."
@@ -2915,7 +4212,7 @@ msgstr ""
 #: builtin/reset.c:297
 #, c-format
 msgid "Could not parse object '%s'."
-msgstr ""
+msgstr "Não foi possível analisar objeto '%s'."
 
 #: builtin/reset.c:302
 msgid "--patch is incompatible with --{hard,mixed,soft}"
@@ -2941,20 +4238,20 @@ msgid "Could not reset index file to revision '%s'."
 msgstr ""
 
 #: builtin/revert.c:70
-#: builtin/revert.c:91
+#: builtin/revert.c:92
 #, c-format
 msgid "%s: %s cannot be used with %s"
 msgstr ""
 
-#: builtin/revert.c:126
+#: builtin/revert.c:131
 msgid "program error"
 msgstr "erro do programa"
 
-#: builtin/revert.c:209
+#: builtin/revert.c:221
 msgid "revert failed"
 msgstr "falhou o revert"
 
-#: builtin/revert.c:224
+#: builtin/revert.c:236
 msgid "cherry-pick failed"
 msgstr "cherry-pick falhou"
 
@@ -2994,32 +4291,32 @@ msgstr ""
 msgid "Missing author: %s"
 msgstr "Autor em falta: %s"
 
-#: builtin/tag.c:58
+#: builtin/tag.c:60
 #, c-format
 msgid "malformed object at '%s'"
 msgstr ""
 
-#: builtin/tag.c:205
+#: builtin/tag.c:207
 #, c-format
 msgid "tag name too long: %.*s..."
 msgstr ""
 
-#: builtin/tag.c:210
+#: builtin/tag.c:212
 #, c-format
 msgid "tag '%s' not found."
 msgstr "etiqueta '%s' não foi encontrada."
 
-#: builtin/tag.c:225
+#: builtin/tag.c:227
 #, c-format
 msgid "Deleted tag '%s' (was %s)\n"
 msgstr ""
 
-#: builtin/tag.c:237
+#: builtin/tag.c:239
 #, c-format
 msgid "could not verify the tag '%s'"
 msgstr ""
 
-#: builtin/tag.c:247
+#: builtin/tag.c:249
 msgid ""
 "\n"
 "#\n"
@@ -3028,7 +4325,7 @@ msgid ""
 "#\n"
 msgstr ""
 
-#: builtin/tag.c:254
+#: builtin/tag.c:256
 msgid ""
 "\n"
 "#\n"
@@ -3037,159 +4334,251 @@ msgid ""
 "#\n"
 msgstr ""
 
-#: builtin/tag.c:294
+#: builtin/tag.c:298
 msgid "unable to sign the tag"
 msgstr ""
 
-#: builtin/tag.c:296
+#: builtin/tag.c:300
 msgid "unable to write tag file"
 msgstr ""
 
-#: builtin/tag.c:321
+#: builtin/tag.c:325
 msgid "bad object type."
 msgstr ""
 
-#: builtin/tag.c:334
+#: builtin/tag.c:338
 msgid "tag header too big."
 msgstr ""
 
-#: builtin/tag.c:366
+#: builtin/tag.c:370
 msgid "no tag message?"
-msgstr ""
+msgstr "nenhuma mensaje para a etiqueta?"
 
-#: builtin/tag.c:372
+#: builtin/tag.c:376
 #, c-format
 msgid "The tag message has been left in %s\n"
 msgstr ""
 
-#: builtin/tag.c:421
+#: builtin/tag.c:425
 msgid "switch 'points-at' requires an object"
 msgstr ""
 
-#: builtin/tag.c:423
+#: builtin/tag.c:427
 #, c-format
 msgid "malformed object name '%s'"
 msgstr ""
 
-#: builtin/tag.c:502
+#: builtin/tag.c:506
+msgid "--column and -n are incompatible"
+msgstr "--column e -n are são incompatíveis"
+
+#: builtin/tag.c:523
 msgid "-n option is only allowed with -l."
 msgstr ""
 
-#: builtin/tag.c:504
+#: builtin/tag.c:525
 msgid "--contains option is only allowed with -l."
 msgstr ""
 
-#: builtin/tag.c:506
+#: builtin/tag.c:527
 msgid "--points-at option is only allowed with -l."
 msgstr ""
 
-#: builtin/tag.c:514
+#: builtin/tag.c:535
 msgid "only one -F or -m option is allowed."
 msgstr ""
 
-#: builtin/tag.c:534
+#: builtin/tag.c:555
 msgid "too many params"
 msgstr "demasiado parametros"
 
-#: builtin/tag.c:540
+#: builtin/tag.c:561
 #, c-format
 msgid "'%s' is not a valid tag name."
 msgstr ""
 
-#: builtin/tag.c:545
+#: builtin/tag.c:566
 #, c-format
 msgid "tag '%s' already exists"
-msgstr ""
+msgstr "etiqueta '%s' já existe"
 
-#: builtin/tag.c:563
+#: builtin/tag.c:584
 #, c-format
 msgid "%s: cannot lock the ref"
 msgstr ""
 
-#: builtin/tag.c:565
+#: builtin/tag.c:586
 #, c-format
 msgid "%s: cannot update the ref"
 msgstr ""
 
-#: builtin/tag.c:567
+#: builtin/tag.c:588
 #, c-format
 msgid "Updated tag '%s' (was %s)\n"
 msgstr ""
 
-#: git-am.sh:49
-msgid "You need to set your committer info first"
+#: git.c:16
+msgid "See 'git help <command>' for more information on a specific command."
+msgstr ""
+
+#: common-cmds.h:8
+msgid "Add file contents to the index"
+msgstr ""
+
+#: common-cmds.h:9
+msgid "Find by binary search the change that introduced a bug"
+msgstr ""
+
+#: common-cmds.h:10
+msgid "List, create, or delete branches"
+msgstr "Listar, criar ou apagar ramas"
+
+#: common-cmds.h:11
+msgid "Checkout a branch or paths to the working tree"
+msgstr ""
+
+#: common-cmds.h:12
+msgid "Clone a repository into a new directory"
+msgstr ""
+
+#: common-cmds.h:13
+msgid "Record changes to the repository"
+msgstr "Gravar alterações para o repositório"
+
+#: common-cmds.h:14
+msgid "Show changes between commits, commit and working tree, etc"
+msgstr ""
+
+#: common-cmds.h:15
+msgid "Download objects and refs from another repository"
+msgstr ""
+
+#: common-cmds.h:16
+msgid "Print lines matching a pattern"
+msgstr ""
+
+#: common-cmds.h:17
+msgid "Create an empty git repository or reinitialize an existing one"
+msgstr ""
+
+#: common-cmds.h:18
+msgid "Show commit logs"
+msgstr "Mostrado logs de commits"
+
+#: common-cmds.h:19
+msgid "Join two or more development histories together"
+msgstr ""
+
+#: common-cmds.h:20
+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 ""
+
+#: common-cmds.h:22
+msgid "Update remote refs along with associated objects"
+msgstr ""
+
+#: common-cmds.h:23
+msgid "Forward-port local commits to the updated upstream head"
+msgstr ""
+
+#: common-cmds.h:24
+msgid "Reset current HEAD to the specified state"
+msgstr ""
+
+#: common-cmds.h:25
+msgid "Remove files from the working tree and from the index"
+msgstr ""
+
+#: common-cmds.h:26
+msgid "Show various types of objects"
+msgstr ""
+
+#: common-cmds.h:27
+msgid "Show the working tree status"
+msgstr "Mostrar o estado los ramos das árvores de trabalho"
+
+#: common-cmds.h:28
+msgid "Create, list, delete or verify a tag object signed with GPG"
 msgstr ""
 
-#: git-am.sh:136
+#: git-am.sh:50
+msgid "You need to set your committer info first"
+msgstr "Necessitas primeiro de especificiar os teus dados de committer"
+
+#: git-am.sh:137
 msgid "Repository lacks necessary blobs to fall back on 3-way merge."
 msgstr ""
 
-#: git-am.sh:147
+#: git-am.sh:154
 msgid ""
 "Did you hand edit your patch?\n"
 "It does not apply to blobs recorded in its index."
 msgstr ""
 
-#: git-am.sh:156
+#: git-am.sh:163
 msgid "Falling back to patching base and 3-way merge..."
 msgstr ""
 
-#: git-am.sh:268
+#: git-am.sh:275
 msgid "Only one StGIT patch series can be applied at once"
 msgstr ""
 
-#: git-am.sh:355
+#: git-am.sh:362
 #, sh-format
 msgid "Patch format $patch_format is not supported."
 msgstr ""
 
-#: git-am.sh:357
+#: git-am.sh:364
 msgid "Patch format detection failed."
-msgstr ""
+msgstr "Falhou a detecção do formato do patch."
 
-#: git-am.sh:411
+#: git-am.sh:418
 msgid "-d option is no longer supported.  Do not use."
 msgstr ""
 
-#: git-am.sh:474
+#: git-am.sh:481
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
 msgstr ""
 
-#: git-am.sh:479
+#: git-am.sh:486
 msgid "Please make up your mind. --skip or --abort?"
 msgstr ""
 
-#: git-am.sh:506
+#: git-am.sh:513
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr ""
 
-#: git-am.sh:572
+#: git-am.sh:579
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
 msgstr ""
 
-#: git-am.sh:748
+#: git-am.sh:755
 msgid "cannot be interactive without stdin connected to a terminal."
 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:759
+#: git-am.sh:766
 msgid "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
 msgstr "Aplicar? Sim[y]/[n]ão/[e]ditar/[v]er patch/[a]ceitar todos "
 
-#: git-am.sh:795
+#: git-am.sh:802
 #, sh-format
 msgid "Applying: $FIRSTLINE"
 msgstr "Aplicando: $FIRSTLINE"
 
-#: git-am.sh:840
+#: git-am.sh:847
 msgid "No changes -- Patch already applied."
-msgstr ""
+msgstr "Nenhuma mudança -- Já foi aplicado o patch."
 
-#: git-am.sh:866
+#: git-am.sh:873
 msgid "applying to an empty history"
 msgstr ""
 
@@ -3284,7 +4673,7 @@ msgstr ""
 
 #: git-bisect.sh:474
 msgid "We are not bisecting."
-msgstr ""
+msgstr "Não estamos a bisseccionar."
 
 #: git-pull.sh:21
 msgid ""
@@ -3307,7 +4696,7 @@ msgstr ""
 
 #: git-pull.sh:257
 msgid "Cannot rebase onto multiple branches"
-msgstr ""
+msgstr "Não é possível fazer rebase com várias ramas"
 
 #: git-stash.sh:51
 msgid "git stash clear with parameters is unimplemented"
@@ -3328,7 +4717,7 @@ msgstr ""
 
 #: git-stash.sh:140
 msgid "No changes selected"
-msgstr ""
+msgstr "Não há alterações seleccionadas"
 
 #: git-stash.sh:143
 msgid "Cannot remove temporary index (can't happen)"
@@ -3340,11 +4729,11 @@ msgstr ""
 
 #: git-stash.sh:223
 msgid "No local changes to save"
-msgstr ""
+msgstr "Sem alterações locais para guardar"
 
 #: git-stash.sh:227
 msgid "Cannot initialize stash"
-msgstr ""
+msgstr "Não é possível inicializar o stash"
 
 #: git-stash.sh:235
 msgid "Cannot save the current status"
@@ -3401,7 +4790,7 @@ msgstr ""
 #: git-stash.sh:491
 #, sh-format
 msgid "Dropped ${REV} ($s)"
-msgstr ""
+msgstr "Deixado cair ${REV} ($s)"
 
 #: git-stash.sh:492
 #, sh-format
@@ -3410,7 +4799,7 @@ msgstr ""
 
 #: git-stash.sh:499
 msgid "No branch name specified"
-msgstr ""
+msgstr "Nenhum nome para a rama especificado"
 
 #: git-stash.sh:570
 msgid "(To restore them type \"git stash apply\")"
@@ -3421,163 +4810,171 @@ msgstr ""
 msgid "cannot strip one component off url '$remoteurl'"
 msgstr ""
 
-#: git-submodule.sh:108
+#: git-submodule.sh:109
 #, sh-format
-msgid "No submodule mapping found in .gitmodules for path '$path'"
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:149
+#: git-submodule.sh:150
 #, sh-format
-msgid "Clone of '$url' into submodule path '$path' failed"
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
 msgstr ""
 
-#: git-submodule.sh:159
+#: git-submodule.sh:160
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
 msgstr ""
 
-#: git-submodule.sh:247
+#: git-submodule.sh:249
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
 msgstr ""
 
-#: git-submodule.sh:264
+#: git-submodule.sh:266
 #, sh-format
-msgid "'$path' already exists in the index"
+msgid "'$sm_path' already exists in the index"
 msgstr ""
 
-#: git-submodule.sh:281
+#: git-submodule.sh:283
 #, sh-format
-msgid "'$path' already exists and is not a valid git repo"
+msgid "'$sm_path' already exists and is not a valid git repo"
 msgstr ""
 
-#: git-submodule.sh:295
+#: git-submodule.sh:297
 #, sh-format
-msgid "Unable to checkout submodule '$path'"
+msgid "Unable to checkout submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:300
+#: git-submodule.sh:302
 #, sh-format
-msgid "Failed to add submodule '$path'"
+msgid "Failed to add submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:305
+#: git-submodule.sh:307
 #, sh-format
-msgid "Failed to register submodule '$path'"
+msgid "Failed to register submodule '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:347
+#: git-submodule.sh:349
 #, sh-format
-msgid "Entering '$prefix$path'"
-msgstr ""
+msgid "Entering '$prefix$sm_path'"
+msgstr "Entrando '$prefix$sm_path'"
 
-#: git-submodule.sh:359
+#: git-submodule.sh:363
 #, sh-format
-msgid "Stopping at '$path'; script returned non-zero status."
+msgid "Stopping at '$sm_path'; script returned non-zero status."
 msgstr ""
 
-#: git-submodule.sh:401
+#: git-submodule.sh:405
 #, sh-format
-msgid "No url found for submodule path '$path' in .gitmodules"
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
 msgstr ""
 
-#: git-submodule.sh:410
+#: git-submodule.sh:414
 #, sh-format
-msgid "Failed to register url for submodule path '$path'"
+msgid "Failed to register url for submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:418
+#: git-submodule.sh:422
 #, sh-format
-msgid "Failed to register update mode for submodule path '$path'"
+msgid "Failed to register update mode for submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:420
+#: git-submodule.sh:424
 #, sh-format
-msgid "Submodule '$name' ($url) registered for path '$path'"
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:519
+#: git-submodule.sh:523
 #, sh-format
 msgid ""
-"Submodule path '$path' not initialized\n"
+"Submodule path '$sm_path' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
 
-#: git-submodule.sh:532
+#: git-submodule.sh:536
 #, sh-format
-msgid "Unable to find current revision in submodule path '$path'"
+msgid "Unable to find current revision in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:551
+#: git-submodule.sh:555
 #, sh-format
-msgid "Unable to fetch in submodule path '$path'"
+msgid "Unable to fetch in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:565
+#: git-submodule.sh:569
 #, sh-format
-msgid "Unable to rebase '$sha1' in submodule path '$path'"
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:566
+#: git-submodule.sh:570
 #, sh-format
-msgid "Submodule path '$path': rebased into '$sha1'"
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
 msgstr ""
 
-#: git-submodule.sh:571
+#: git-submodule.sh:575
 #, sh-format
-msgid "Unable to merge '$sha1' in submodule path '$path'"
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:572
+#: git-submodule.sh:576
 #, sh-format
-msgid "Submodule path '$path': merged in '$sha1'"
+msgid "Submodule path '$sm_path': merged in '$sha1'"
 msgstr ""
 
-#: git-submodule.sh:577
+#: git-submodule.sh:581
 #, sh-format
-msgid "Unable to checkout '$sha1' in submodule path '$path'"
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:578
+#: git-submodule.sh:582
 #, sh-format
-msgid "Submodule path '$path': checked out '$sha1'"
+msgid "Submodule path '$sm_path': checked out '$sha1'"
 msgstr ""
 
-#: git-submodule.sh:600
-#: git-submodule.sh:923
+#: git-submodule.sh:604
+#: git-submodule.sh:927
 #, sh-format
-msgid "Failed to recurse into submodule path '$path'"
+msgid "Failed to recurse into submodule path '$sm_path'"
 msgstr ""
 
-#: git-submodule.sh:708
+#: git-submodule.sh:712
 msgid "--"
 msgstr "--"
 
-#: git-submodule.sh:766
+#: git-submodule.sh:770
 #, sh-format
 msgid "  Warn: $name doesn't contain commit $sha1_src"
 msgstr ""
 
-#: git-submodule.sh:769
+#: git-submodule.sh:773
 #, sh-format
 msgid "  Warn: $name doesn't contain commit $sha1_dst"
 msgstr ""
 
-#: git-submodule.sh:772
+#: git-submodule.sh:776
 #, sh-format
 msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
 msgstr ""
 
-#: git-submodule.sh:797
+#: git-submodule.sh:801
 msgid "blob"
 msgstr "blob"
 
-#: git-submodule.sh:798
+#: git-submodule.sh:802
 msgid "submodule"
 msgstr "submódulos"
 
-#: git-submodule.sh:969
+#: git-submodule.sh:973
 #, sh-format
 msgid "Synchronizing submodule url for '$name'"
 msgstr ""
 
+#~ msgid "cherry-pick"
+#~ msgstr "cherry-pick"
+
+#~ msgid "Please enter the commit message for your changes."
+#~ msgstr "Por favor insira a mensagem de commit das suas alterações."
+
+#~ msgid "Too many options specified"
+#~ msgstr "Demasiadas opções especificadas"
index 64747394807b169e6dc9f64e101a03ee659c8075..155c75e57e07c54835560759a1e85985788d1e48 100644 (file)
@@ -12,7 +12,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Git\n"
 "Report-Msgid-Bugs-To: Git Mailing List <git@vger.kernel.org>\n"
-"POT-Creation-Date: 2012-03-16 20:18+0800\n"
+"POT-Creation-Date: 2012-05-15 06:31+0800\n"
 "PO-Revision-Date: 2012-01-30 00:00+0800\n"
 "Last-Translator: Jiang Xin <worldhello.net@gmail.com>\n"
 "Language-Team: GitHub <https://github.com/gotgit/git/>\n"
@@ -22,7 +22,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 
-#: advice.c:34
+#: advice.c:40
 #, 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:64
+#: advice.c:70
 msgid ""
 "Fix them up in the work tree,\n"
 "and then use 'git add/rm <file>' as\n"
@@ -42,12 +42,12 @@ msgstr ""
 "'git add/rm <file>' 标记解决方案,\n"
 "或使用 'git commit -a'。"
 
-#: commit.c:47
+#: commit.c:48
 #, c-format
 msgid "could not parse %s"
 msgstr "不能解析 %s"
 
-#: commit.c:49
+#: commit.c:50
 #, c-format
 msgid "%s %s is not a commit!"
 msgstr "%s %s 不是一个提交!"
@@ -91,29 +91,29 @@ msgstr ""
 "发现配置变量 'diff.dirstat' 中的错误:\n"
 "%s"
 
-#: diff.c:1336
+#: diff.c:1400
 msgid " 0 files changed\n"
 msgstr " 0 个文件被修改\n"
 
-#: diff.c:1340
+#: diff.c:1404
 #, c-format
 msgid " %d file changed"
 msgid_plural " %d files changed"
 msgstr[0] " %d 个文件被修改"
 
-#: diff.c:1357
+#: diff.c:1421
 #, c-format
 msgid ", %d insertion(+)"
 msgid_plural ", %d insertions(+)"
 msgstr[0] ",插入 %d 行(+)"
 
-#: diff.c:1368
+#: diff.c:1432
 #, c-format
 msgid ", %d deletion(-)"
 msgid_plural ", %d deletions(-)"
 msgstr[0] ",删除 %d 行(-)"
 
-#: diff.c:3424
+#: diff.c:3439
 #, c-format
 msgid ""
 "Failed to parse --dirstat/-X option parameter:\n"
@@ -183,14 +183,14 @@ msgstr[0] ""
 "您的分支和 '%s' 出现了偏离,\n"
 "并且各自分别有 %d 和 %d 处不同的提交。\n"
 
-#: sequencer.c:120 builtin/merge.c:864 builtin/merge.c:985
-#: builtin/merge.c:1095 builtin/merge.c:1105
+#: sequencer.c:120 builtin/merge.c:865 builtin/merge.c:978
+#: builtin/merge.c:1088 builtin/merge.c:1098
 #, c-format
 msgid "Could not open '%s' for writing"
 msgstr "不能为写入打开 '%s'"
 
-#: sequencer.c:122 builtin/merge.c:334 builtin/merge.c:867
-#: builtin/merge.c:1097 builtin/merge.c:1110
+#: sequencer.c:122 builtin/merge.c:333 builtin/merge.c:868
+#: builtin/merge.c:1090 builtin/merge.c:1103
 #, c-format
 msgid "Could not write to '%s'"
 msgstr "不能写入 '%s'"
@@ -285,8 +285,8 @@ msgstr "不能还原 %s... %s"
 msgid "could not apply %s... %s"
 msgstr "不能应用 %s... %s"
 
-#: sequencer.c:450 sequencer.c:909 builtin/log.c:288 builtin/log.c:713
-#: builtin/log.c:1329 builtin/log.c:1548 builtin/merge.c:348
+#: sequencer.c:450 sequencer.c:909 builtin/log.c:289 builtin/log.c:719
+#: builtin/log.c:1335 builtin/log.c:1554 builtin/merge.c:347
 #: builtin/shortlog.c:181
 msgid "revision walk setup failed"
 msgstr "版本遍历设置失败"
@@ -411,6 +411,25 @@ msgstr "不能作为初始提交还原"
 msgid "Can't cherry-pick into empty head"
 msgstr "不能拣选到空分支"
 
+#: sha1_name.c:864
+msgid "HEAD does not point to a branch"
+msgstr "HEAD 没有指向一个分支"
+
+#: sha1_name.c:867
+#, c-format
+msgid "No such branch: '%s'"
+msgstr "没有此分支:'%s'"
+
+#: sha1_name.c:869
+#, c-format
+msgid "No upstream configured for branch '%s'"
+msgstr "尚未给分支 '%s' 设置上游"
+
+#: sha1_name.c:872
+#, c-format
+msgid "Upstream branch '%s' not stored as a remote-tracking branch"
+msgstr "上游分支 '%s' 没有存储为一个远程跟踪分支"
+
 #: wt-status.c:134
 msgid "Unmerged paths:"
 msgstr "未合并的路径:"
@@ -830,34 +849,34 @@ msgstr ""
 
 #  译者:汉字之间无空格,故删除尾部空格
 #. TRANSLATORS: This is "remote " in "remote branch '%s' not found"
-#: builtin/branch.c:163
+#: builtin/branch.c:164
 msgid "remote "
 msgstr "远程"
 
-#: builtin/branch.c:171
+#: builtin/branch.c:172
 msgid "cannot use -a with -d"
 msgstr "不能将 -a 和 -d 共用"
 
-#: builtin/branch.c:177
+#: builtin/branch.c:178
 msgid "Couldn't look up commit object for HEAD"
 msgstr "无法查询 HEAD 指向的提交对象"
 
-#: builtin/branch.c:182
+#: builtin/branch.c:183
 #, c-format
 msgid "Cannot delete the branch '%s' which you are currently on."
 msgstr "无法删除您当前所在的分支 '%s'。"
 
-#: builtin/branch.c:192
+#: builtin/branch.c:193
 #, c-format
 msgid "%sbranch '%s' not found."
 msgstr "%s分支 '%s' 未发现。"
 
-#: builtin/branch.c:200
+#: builtin/branch.c:201
 #, c-format
 msgid "Couldn't look up commit object for '%s'"
 msgstr "无法查询 '%s' 指向的提交对象"
 
-#: builtin/branch.c:206
+#: builtin/branch.c:207
 #, c-format
 msgid ""
 "The branch '%s' is not fully merged.\n"
@@ -866,97 +885,97 @@ msgstr ""
 "分支 '%s' 没有完全合并。\n"
 "如果您确认要删除它,执行 'git branch -D %s'。"
 
-#: builtin/branch.c:214
+#: builtin/branch.c:215
 #, c-format
 msgid "Error deleting %sbranch '%s'"
 msgstr "删除 %s分支 '%s' 时出错"
 
-#: builtin/branch.c:219
+#: builtin/branch.c:221
 #, c-format
 msgid "Deleted %sbranch %s (was %s).\n"
 msgstr "已删除 %s分支 %s(曾为 %s)。\n"
 
-#: builtin/branch.c:224
+#: builtin/branch.c:226
 msgid "Update of config-file failed"
 msgstr "无法更新 config 文件"
 
-#: builtin/branch.c:322
+#: builtin/branch.c:324
 #, c-format
 msgid "branch '%s' does not point at a commit"
 msgstr "分支 '%s' 未指向一个提交"
 
 #  译者:注意保持句尾空格
-#: builtin/branch.c:394
+#: builtin/branch.c:396
 #, c-format
 msgid "behind %d] "
 msgstr "落后 %d] "
 
 #  译者:注意保持句尾空格
-#: builtin/branch.c:396
+#: builtin/branch.c:398
 #, c-format
 msgid "ahead %d] "
 msgstr "领先 %d] "
 
 #  译者:注意保持句尾空格
-#: builtin/branch.c:398
+#: builtin/branch.c:400
 #, c-format
 msgid "ahead %d, behind %d] "
 msgstr "领先 %d,落后 %d] "
 
-#: builtin/branch.c:501
+#: builtin/branch.c:503
 msgid "(no branch)"
 msgstr "(非分支)"
 
-#: builtin/branch.c:566
+#: builtin/branch.c:568
 msgid "some refs could not be read"
 msgstr "一些引用不能读取"
 
-#: builtin/branch.c:579
+#: builtin/branch.c:581
 msgid "cannot rename the current branch while not on any."
 msgstr "无法重命名当前分支因为不处于任何分支上。"
 
-#: builtin/branch.c:589
+#: builtin/branch.c:591
 #, c-format
 msgid "Invalid branch name: '%s'"
 msgstr "无效的分支名:'%s'"
 
-#: builtin/branch.c:604
+#: builtin/branch.c:606
 msgid "Branch rename failed"
 msgstr "分支重命名失败"
 
-#: builtin/branch.c:608
+#: builtin/branch.c:610
 #, c-format
 msgid "Renamed a misnamed branch '%s' away"
 msgstr "重命名掉一个错误命名的旧分支 '%s'"
 
-#: builtin/branch.c:612
+#: builtin/branch.c:614
 #, c-format
 msgid "Branch renamed to %s, but HEAD is not updated!"
 msgstr "分支重命名为 %s,但 HEAD 没有更新!"
 
-#: builtin/branch.c:619
+#: builtin/branch.c:621
 msgid "Branch is renamed, but update of config-file failed"
 msgstr "分支被重命名,但更新 config 文件失败"
 
-#: builtin/branch.c:634
+#: builtin/branch.c:636
 #, c-format
 msgid "malformed object name %s"
 msgstr "非法的对象名 %s"
 
-#: builtin/branch.c:658
+#: builtin/branch.c:660
 #, c-format
 msgid "could not write branch description template: %s\n"
 msgstr "不能写分支描述模版:%s\n"
 
-#: builtin/branch.c:746
+#: builtin/branch.c:750
 msgid "Failed to resolve HEAD as a valid ref."
 msgstr "无法将 HEAD 解析为有效引用。"
 
-#: builtin/branch.c:751 builtin/clone.c:558
+#: builtin/branch.c:755 builtin/clone.c:558
 msgid "HEAD not found below refs/heads!"
 msgstr "HEAD 没有位于 /refs/heads 之下!"
 
-#: builtin/branch.c:809
+#: builtin/branch.c:813
 msgid "-a and -r options to 'git branch' do not make sense with a branch name"
 msgstr "'git branch' 的 -a 和 -r 选项带一个分支名参数没有意义"
 
@@ -1018,7 +1037,7 @@ msgid "path '%s' is unmerged"
 msgstr "路径 '%s' 未合并"
 
 #: builtin/checkout.c:302 builtin/checkout.c:498 builtin/clone.c:583
-#: builtin/merge.c:811
+#: builtin/merge.c:812
 msgid "unable to write new index file"
 msgstr "无法写新的索引文件"
 
@@ -1035,43 +1054,43 @@ msgstr "您需要先解决当前索引的冲突"
 msgid "Can not do reflog for '%s'\n"
 msgstr "不能对 '%s' 执行 reflog 操作\n"
 
-#: builtin/checkout.c:565
+#: builtin/checkout.c:566
 msgid "HEAD is now at"
 msgstr "HEAD 目前位于"
 
-#: builtin/checkout.c:572
+#: builtin/checkout.c:573
 #, c-format
 msgid "Reset branch '%s'\n"
 msgstr "重置分支 '%s'\n"
 
-#: builtin/checkout.c:575
+#: builtin/checkout.c:576
 #, c-format
 msgid "Already on '%s'\n"
 msgstr "已经位于 '%s'\n"
 
-#: builtin/checkout.c:579
+#: builtin/checkout.c:580
 #, c-format
 msgid "Switched to and reset branch '%s'\n"
 msgstr "切换并重置分支 '%s'\n"
 
-#: builtin/checkout.c:581
+#: builtin/checkout.c:582
 #, c-format
 msgid "Switched to a new branch '%s'\n"
 msgstr "切换到一个新分支 '%s'\n"
 
-#: builtin/checkout.c:583
+#: builtin/checkout.c:584
 #, c-format
 msgid "Switched to branch '%s'\n"
 msgstr "切换到分支 '%s'\n"
 
 #  译者:注意保持前导空格
-#: builtin/checkout.c:639
+#: builtin/checkout.c:640
 #, c-format
 msgid " ... and %d more.\n"
 msgstr " ... 及其它 %d 个。\n"
 
 #. The singular version
-#: builtin/checkout.c:645
+#: builtin/checkout.c:646
 #, c-format
 msgid ""
 "Warning: you are leaving %d commit behind, not connected to\n"
@@ -1088,7 +1107,7 @@ msgstr[0] ""
 "\n"
 "%s\n"
 
-#: builtin/checkout.c:663
+#: builtin/checkout.c:664
 #, c-format
 msgid ""
 "If you want to keep them by creating a new branch, this may be a good time\n"
@@ -1103,71 +1122,71 @@ msgstr ""
 " git branch new_branch_name %s\n"
 "\n"
 
-#: builtin/checkout.c:692
+#: builtin/checkout.c:693
 msgid "internal error in revision walk"
 msgstr "在版本遍历时遇到内部错误"
 
-#: builtin/checkout.c:696
+#: builtin/checkout.c:697
 msgid "Previous HEAD position was"
 msgstr "之前的 HEAD 位置是"
 
-#: builtin/checkout.c:722
+#: builtin/checkout.c:723
 msgid "You are on a branch yet to be born"
 msgstr "您位于一个尚未初始化的分支"
 
 #. case (1)
-#: builtin/checkout.c:853
+#: builtin/checkout.c:854
 #, c-format
 msgid "invalid reference: %s"
 msgstr "无效引用:%s"
 
 #. case (1): want a tree
-#: builtin/checkout.c:892
+#: builtin/checkout.c:893
 #, c-format
 msgid "reference is not a tree: %s"
 msgstr "引用不是一个树:%s"
 
-#: builtin/checkout.c:972
+#: builtin/checkout.c:973
 msgid "-B cannot be used with -b"
 msgstr "-B 不能和 -b 共用"
 
-#: builtin/checkout.c:981
+#: builtin/checkout.c:982
 msgid "--patch is incompatible with all other options"
 msgstr "--patch 选项和其他选项不兼容"
 
-#: builtin/checkout.c:984
+#: builtin/checkout.c:985
 msgid "--detach cannot be used with -b/-B/--orphan"
 msgstr "--detach 不能和 -b/-B/--orphan 共用"
 
-#: builtin/checkout.c:986
+#: builtin/checkout.c:987
 msgid "--detach cannot be used with -t"
 msgstr "--detach 不能和 -t 共用"
 
-#: builtin/checkout.c:992
+#: builtin/checkout.c:993
 msgid "--track needs a branch name"
 msgstr "--track 需要一个分支名"
 
-#: builtin/checkout.c:999
+#: builtin/checkout.c:1000
 msgid "Missing branch name; try -b"
 msgstr "缺少分支名;尝试 -b"
 
-#: builtin/checkout.c:1005
+#: builtin/checkout.c:1006
 msgid "--orphan and -b|-B are mutually exclusive"
 msgstr "--orphan 和 -b|-B 互斥"
 
-#: builtin/checkout.c:1007
+#: builtin/checkout.c:1008
 msgid "--orphan cannot be used with -t"
 msgstr "--orphan 不能和 -t 共用"
 
-#: builtin/checkout.c:1017
+#: builtin/checkout.c:1018
 msgid "git checkout: -f and -m are incompatible"
 msgstr "git checkout:-f 和 -m 不兼容"
 
-#: builtin/checkout.c:1051
+#: builtin/checkout.c:1052
 msgid "invalid path specification"
 msgstr "无效的路径规格"
 
-#: builtin/checkout.c:1059
+#: builtin/checkout.c:1060
 #, c-format
 msgid ""
 "git checkout: updating paths is incompatible with switching branches.\n"
@@ -1176,26 +1195,26 @@ msgstr ""
 "git checkout:更新路径和切换分支不兼容。\n"
 "您是想要检出 '%s' 但未能将其解析为提交么?"
 
-#: builtin/checkout.c:1061
+#: builtin/checkout.c:1062
 msgid "git checkout: updating paths is incompatible with switching branches."
 msgstr "git checkout:更新路径和切换分支不兼容。"
 
-#: builtin/checkout.c:1066
+#: builtin/checkout.c:1067
 msgid "git checkout: --detach does not take a path argument"
 msgstr "git checkout:--detach 不跟路径参数"
 
-#: builtin/checkout.c:1069
+#: builtin/checkout.c:1070
 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/checkout.c:1088
+#: builtin/checkout.c:1089
 msgid "Cannot switch branch to a non-commit."
 msgstr "无法切换分支到一个非提交。"
 
-#: builtin/checkout.c:1091
+#: builtin/checkout.c:1092
 msgid "--ours/--theirs is incompatible with switching branches."
 msgstr "--ours/--theirs 和切换分支不兼容。"
 
@@ -1452,67 +1471,67 @@ msgstr "无法读取索引"
 msgid "unable to write temporary index file"
 msgstr "无法写临时索引文件"
 
-#: builtin/commit.c:550 builtin/commit.c:556
+#: builtin/commit.c:561 builtin/commit.c:567
 #, c-format
 msgid "invalid commit: %s"
 msgstr "无效的提交:%s"
 
-#: builtin/commit.c:579
+#: builtin/commit.c:590
 msgid "malformed --author parameter"
 msgstr "非法的 --author 参数"
 
-#: builtin/commit.c:635
+#: builtin/commit.c:651
 #, c-format
 msgid "Malformed ident string: '%s'"
 msgstr "非法的身份字符串:'%s'"
 
-#: builtin/commit.c:670 builtin/commit.c:703 builtin/commit.c:1000
+#: builtin/commit.c:689 builtin/commit.c:722 builtin/commit.c:1033
 #, c-format
 msgid "could not lookup commit %s"
 msgstr "不能查询提交 %s"
 
-#: builtin/commit.c:682 builtin/shortlog.c:296
+#: builtin/commit.c:701 builtin/shortlog.c:296
 #, c-format
 msgid "(reading log message from standard input)\n"
 msgstr "(正从标准输入中读取日志信息)\n"
 
-#: builtin/commit.c:684
+#: builtin/commit.c:703
 msgid "could not read log from standard input"
 msgstr "不能从标准输入中读取日志信息"
 
-#: builtin/commit.c:688
+#: builtin/commit.c:707
 #, c-format
 msgid "could not read log file '%s'"
 msgstr "不能读取日志文件 '%s'"
 
-#: builtin/commit.c:694
+#: builtin/commit.c:713
 msgid "commit has empty message"
 msgstr "提交说明为空"
 
-#: builtin/commit.c:710
+#: builtin/commit.c:729
 msgid "could not read MERGE_MSG"
 msgstr "不能读取 MERGE_MSG"
 
-#: builtin/commit.c:714
+#: builtin/commit.c:733
 msgid "could not read SQUASH_MSG"
 msgstr "不能读取 SQUASH_MSG"
 
-#: builtin/commit.c:718
+#: builtin/commit.c:737
 #, c-format
 msgid "could not read '%s'"
 msgstr "不能读取 '%s'"
 
-#: builtin/commit.c:746
+#: builtin/commit.c:765
 #, c-format
 msgid "could not open '%s'"
 msgstr "不能打开 '%s'"
 
-#: builtin/commit.c:770
+#: builtin/commit.c:789
 msgid "could not write commit template"
 msgstr "不能写提交模版"
 
 #  译者:%s若翻为中文,前后不需要空格
-#: builtin/commit.c:783
+#: builtin/commit.c:799
 #, c-format
 msgid ""
 "\n"
@@ -1527,12 +1546,12 @@ msgstr ""
 "\t%s\n"
 "然后重试。\n"
 
-#: builtin/commit.c:796
+#: builtin/commit.c:812
 msgid "Please enter the commit message for your changes."
 msgstr "请为您的修改输入提交说明。"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: builtin/commit.c:799
+#: builtin/commit.c:815
 msgid ""
 " Lines starting\n"
 "with '#' will be ignored, and an empty message aborts the commit.\n"
@@ -1541,7 +1560,7 @@ msgstr ""
 "的行将被忽略,并且空的提交说明将会中止提交。\n"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: builtin/commit.c:804
+#: builtin/commit.c:820
 msgid ""
 " Lines starting\n"
 "with '#' will be kept; you may remove them yourself if you want to.\n"
@@ -1552,153 +1571,158 @@ msgstr ""
 "中止提交。\n"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: builtin/commit.c:816
+#: builtin/commit.c:832
 #, c-format
 msgid "%sAuthor:    %s"
 msgstr "%s作者:     %s"
 
 #  译者:为保证在输出中对齐,注意调整句中空格!
-#: builtin/commit.c:823
+#: builtin/commit.c:839
 #, c-format
 msgid "%sCommitter: %s"
 msgstr "%s提交者:   %s"
 
-#: builtin/commit.c:843
+#: builtin/commit.c:859
 msgid "Cannot read index"
 msgstr "无法读取索引"
 
-#: builtin/commit.c:880
+#: builtin/commit.c:896
 msgid "Error building trees"
 msgstr "无法创建树对象"
 
-#: builtin/commit.c:895 builtin/tag.c:357
+#: builtin/commit.c:911 builtin/tag.c:357
 #, c-format
 msgid "Please supply the message using either -m or -F option.\n"
 msgstr "请使用 -m 或者 -F 选项提供提交说明。\n"
 
-#: builtin/commit.c:975
+#: builtin/commit.c:1008
 #, c-format
 msgid "No existing author found with '%s'"
 msgstr "没有找到匹配 '%s' 的作者"
 
-#: builtin/commit.c:990 builtin/commit.c:1182
+#: builtin/commit.c:1023 builtin/commit.c:1217
 #, c-format
 msgid "Invalid untracked files mode '%s'"
 msgstr "无效的未追踪文件参数 '%s'"
 
-#: builtin/commit.c:1030
+#: builtin/commit.c:1063
 msgid "Using both --reset-author and --author does not make sense"
 msgstr "同时使用 --reset-author 和 --author 没有意义"
 
-#: builtin/commit.c:1041
+#: builtin/commit.c:1074
 msgid "You have nothing to amend."
 msgstr "您没有可修补的提交。"
 
 #  译者:%s若翻为中文,前后不需要空格
-#: builtin/commit.c:1043
+#: builtin/commit.c:1076
 #, c-format
 msgid "You are in the middle of a %s -- cannot amend."
 msgstr "您正处于一个%s的过程中 -- 无法修补提交。"
 
-#: builtin/commit.c:1045
+#: builtin/commit.c:1078
 msgid "Options --squash and --fixup cannot be used together"
 msgstr "选项 --squash 和 --fixup 不能共用"
 
-#: builtin/commit.c:1055
+#: builtin/commit.c:1088
 msgid "Only one of -c/-C/-F/--fixup can be used."
 msgstr "只能用一个 -c/-C/-F/--fixup 选项。"
 
-#: builtin/commit.c:1057
+#: builtin/commit.c:1090
 msgid "Option -m cannot be combined with -c/-C/-F/--fixup."
 msgstr "选项 -m 不能和 -c/-C/-F/--fixup 共用。"
 
-#: builtin/commit.c:1063
+#: builtin/commit.c:1098
 msgid "--reset-author can be used only with -C, -c or --amend."
 msgstr "--reset-author 只能和 -C、-c 或 --amend 共用。"
 
-#: builtin/commit.c:1080
+#: builtin/commit.c:1115
 msgid "Only one of --include/--only/--all/--interactive/--patch can be used."
 msgstr "只能用一个 --include/--only/--all/--interactive/--patch 选项。"
 
-#: builtin/commit.c:1082
+#: builtin/commit.c:1117
 msgid "No paths with --include/--only does not make sense."
 msgstr "参数 --include/--only 不跟路径没有意义。"
 
-#: builtin/commit.c:1084
+#: builtin/commit.c:1119
 msgid "Clever... amending the last one with dirty index."
 msgstr "聪明... 在索引不干净下修补最后的提交。"
 
-#: builtin/commit.c:1086
+#: builtin/commit.c:1121
 msgid "Explicit paths specified without -i nor -o; assuming --only paths..."
 msgstr "指定了明确的路径而没有使用 -i 或 -o 选项;认为是 --only paths..."
 
-#: builtin/commit.c:1096 builtin/tag.c:556
+#: builtin/commit.c:1131 builtin/tag.c:556
 #, c-format
 msgid "Invalid cleanup mode %s"
 msgstr "无效的清理模式 %s"
 
-#: builtin/commit.c:1101
+#: builtin/commit.c:1136
 msgid "Paths with -a does not make sense."
 msgstr "路径和 -a 选项共用没有意义。"
 
-#: builtin/commit.c:1280
+#: builtin/commit.c:1315
 msgid "couldn't look up newly created commit"
 msgstr "无法找到新创建的提交"
 
-#: builtin/commit.c:1282
+#: builtin/commit.c:1317
 msgid "could not parse newly created commit"
 msgstr "不能解析新创建的提交"
 
-#: builtin/commit.c:1323
+#: builtin/commit.c:1358
 msgid "detached HEAD"
 msgstr "分离头指针"
 
 #  译者:中文字符串拼接,可删除前导空格
-#: builtin/commit.c:1325
+#: builtin/commit.c:1360
 msgid " (root-commit)"
 msgstr "(根提交)"
 
-#: builtin/commit.c:1415
+#: builtin/commit.c:1450
 msgid "could not parse HEAD commit"
 msgstr "不能解析 HEAD 提交"
 
-#: builtin/commit.c:1452 builtin/merge.c:509
+#: builtin/commit.c:1487 builtin/merge.c:509
 #, c-format
 msgid "could not open '%s' for reading"
 msgstr "不能为读入打开 '%s'"
 
-#: builtin/commit.c:1459
+#: builtin/commit.c:1494
 #, c-format
 msgid "Corrupt MERGE_HEAD file (%s)"
 msgstr "损坏的 MERGE_HEAD 文件(%s)"
 
-#: builtin/commit.c:1466
+#: builtin/commit.c:1501
 msgid "could not read MERGE_MODE"
 msgstr "不能读取 MERGE_MODE"
 
-#: builtin/commit.c:1485
+#: builtin/commit.c:1520
 #, c-format
 msgid "could not read commit message: %s"
 msgstr "不能读取提交说明:%s"
 
-#: builtin/commit.c:1499
+#: builtin/commit.c:1534
+#, c-format
+msgid "Aborting commit; you did not edit the message.\n"
+msgstr "终止提交;您未更改来自模版的提交说明。\n"
+
+#: builtin/commit.c:1539
 #, c-format
 msgid "Aborting commit due to empty commit message.\n"
 msgstr "终止提交因为提交说明为空。\n"
 
-#: builtin/commit.c:1514 builtin/merge.c:935 builtin/merge.c:968
+#: builtin/commit.c:1554 builtin/merge.c:936 builtin/merge.c:961
 msgid "failed to write commit object"
 msgstr "无法写提交对象"
 
-#: builtin/commit.c:1535
+#: builtin/commit.c:1575
 msgid "cannot lock HEAD ref"
 msgstr "无法锁定 HEAD 引用"
 
-#: builtin/commit.c:1539
+#: builtin/commit.c:1579
 msgid "cannot update HEAD ref"
 msgstr "无法更新 HEAD 引用"
 
-#: builtin/commit.c:1550
+#: builtin/commit.c:1590
 msgid ""
 "Repository has been updated, but unable to write\n"
 "new_index file. Check that disk is not full or quota is\n"
@@ -1829,69 +1853,73 @@ msgstr "提供了无法处理的对象 '%s'。"
 msgid "Couldn't find remote ref HEAD"
 msgstr "无法发现远程 HEAD 引用"
 
-#: builtin/fetch.c:252
+#: builtin/fetch.c:253
 #, c-format
 msgid "object %s not found"
 msgstr "对象 %s 未发现"
 
-#: builtin/fetch.c:258
+#: builtin/fetch.c:259
 msgid "[up to date]"
 msgstr "[最新]"
 
-#: builtin/fetch.c:272
+#: builtin/fetch.c:273
 #, c-format
 msgid "! %-*s %-*s -> %s  (can't fetch in current branch)"
 msgstr "! %-*s %-*s -> %s  (在当前分支下不能获取)"
 
-#: builtin/fetch.c:273 builtin/fetch.c:351
+#: builtin/fetch.c:274 builtin/fetch.c:360
 msgid "[rejected]"
 msgstr "[已拒绝]"
 
-#: builtin/fetch.c:284
+#: builtin/fetch.c:285
 msgid "[tag update]"
 msgstr "[tag更新]"
 
 #  译者:注意保持前导空格
-#: builtin/fetch.c:286 builtin/fetch.c:313 builtin/fetch.c:331
+#: builtin/fetch.c:287 builtin/fetch.c:322 builtin/fetch.c:340
 msgid "  (unable to update local ref)"
 msgstr "  (不能更新本地引用)"
 
-#: builtin/fetch.c:298
+#: builtin/fetch.c:305
 msgid "[new tag]"
 msgstr "[新tag]"
 
-#: builtin/fetch.c:302
+#: builtin/fetch.c:308
 msgid "[new branch]"
 msgstr "[新分支]"
 
-#: builtin/fetch.c:347
+#: builtin/fetch.c:311
+msgid "[new ref]"
+msgstr "[新引用]"
+
+#: builtin/fetch.c:356
 msgid "unable to update local ref"
 msgstr "不能更新本地引用"
 
-#: builtin/fetch.c:347
+#: builtin/fetch.c:356
 msgid "forced update"
 msgstr "强制更新"
 
-#: builtin/fetch.c:353
+#: builtin/fetch.c:362
 msgid "(non-fast-forward)"
 msgstr "(非快进式)"
 
-#: builtin/fetch.c:384 builtin/fetch.c:676
+#: builtin/fetch.c:393 builtin/fetch.c:685
 #, c-format
 msgid "cannot open %s: %s\n"
 msgstr "无法打开 %s:%s\n"
 
-#: builtin/fetch.c:393
+#: builtin/fetch.c:402
 #, c-format
 msgid "%s did not send all necessary objects\n"
 msgstr "%s 未发送所有必须的对象\n"
 
-#: builtin/fetch.c:479
+#: builtin/fetch.c:488
 #, c-format
 msgid "From %.*s\n"
 msgstr "来自 %.*s\n"
 
-#: builtin/fetch.c:490
+#: builtin/fetch.c:499
 #, c-format
 msgid ""
 "some local refs could not be updated; try running\n"
@@ -1901,79 +1929,79 @@ msgstr ""
 " 'git remote prune %s' 来删除旧的、有冲突的分支"
 
 #  译者:注意保持前导空格
-#: builtin/fetch.c:540
+#: builtin/fetch.c:549
 #, c-format
 msgid "   (%s will become dangling)\n"
 msgstr "   (%s 将成为悬空状态)\n"
 
 #  译者:注意保持前导空格
-#: builtin/fetch.c:541
+#: builtin/fetch.c:550
 #, c-format
 msgid "   (%s has become dangling)\n"
 msgstr "   (%s 已成为悬空状态)\n"
 
-#: builtin/fetch.c:548
+#: builtin/fetch.c:557
 msgid "[deleted]"
 msgstr "[已删除]"
 
-#: builtin/fetch.c:549
+#: builtin/fetch.c:558
 msgid "(none)"
 msgstr "(无)"
 
-#: builtin/fetch.c:666
+#: builtin/fetch.c:675
 #, c-format
 msgid "Refusing to fetch into current branch %s of non-bare repository"
 msgstr "拒绝获取到非裸版本库的当前分支 %s"
 
-#: builtin/fetch.c:700
+#: builtin/fetch.c:709
 #, c-format
 msgid "Don't know how to fetch from %s"
 msgstr "不知道如何从 %s 获取"
 
-#: builtin/fetch.c:777
+#: builtin/fetch.c:786
 #, c-format
 msgid "Option \"%s\" value \"%s\" is not valid for %s"
 msgstr "选项 \"%s\" 的值 \"%s\" 对于 %s 是无效的"
 
-#: builtin/fetch.c:780
+#: builtin/fetch.c:789
 #, c-format
 msgid "Option \"%s\" is ignored for %s\n"
 msgstr "选项 \"%s\" 对于 %s 被忽略\n"
 
-#: builtin/fetch.c:879
+#: builtin/fetch.c:888
 #, c-format
 msgid "Fetching %s\n"
 msgstr "正在获取 %s\n"
 
-#: builtin/fetch.c:881
+#: builtin/fetch.c:890
 #, c-format
 msgid "Could not fetch %s"
 msgstr "不能获取 %s"
 
-#: builtin/fetch.c:898
+#: builtin/fetch.c:907
 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:918
+#: builtin/fetch.c:927
 msgid "You need to specify a tag name."
 msgstr "您需要指定一个 tag 名称。"
 
-#: builtin/fetch.c:970
+#: builtin/fetch.c:979
 msgid "fetch --all does not take a repository argument"
 msgstr "fetch --all 不能带一个版本库参数"
 
-#: builtin/fetch.c:972
+#: builtin/fetch.c:981
 msgid "fetch --all does not make sense with refspecs"
 msgstr "fetch --all 带引用表达式没有任何意义"
 
-#: builtin/fetch.c:983
+#: builtin/fetch.c:992
 #, c-format
 msgid "No such remote or remote group: %s"
 msgstr "没有这样的远程或远程组:%s"
 
-#: builtin/fetch.c:991
+#: builtin/fetch.c:1000
 msgid "Fetching a group and specifying refspecs does not make sense"
 msgstr "获取组并指定引用表达式没有意义"
 
@@ -1982,21 +2010,17 @@ msgstr "获取组并指定引用表达式没有意义"
 msgid "Invalid %s: '%s'"
 msgstr "无效的 %s:'%s'"
 
-#: builtin/gc.c:78
-msgid "Too many options specified"
-msgstr "指定了太多的选项"
-
-#: builtin/gc.c:103
+#: builtin/gc.c:90
 #, c-format
 msgid "insanely long object directory %.*s"
 msgstr "不正常的长对象目录 %.*s"
 
-#: builtin/gc.c:223
+#: builtin/gc.c:221
 #, c-format
 msgid "Auto packing the repository for optimum performance.\n"
 msgstr "自动打包版本库以求最佳性能。\n"
 
-#: builtin/gc.c:226
+#: builtin/gc.c:224
 #, c-format
 msgid ""
 "Auto packing the repository for optimum performance. You may also\n"
@@ -2005,7 +2029,7 @@ msgstr ""
 "自动打包版本库以求最佳性能。您还可以手动运行 \"git gc\"。\n"
 "参见 \"git help gc\" 以获取更多信息。\n"
 
-#: builtin/gc.c:256
+#: builtin/gc.c:251
 msgid ""
 "There are too many unreachable loose objects; run 'git prune' to remove them."
 msgstr "有太多不可达的松散对象,运行 'git prune' 删除它们。"
@@ -2202,7 +2226,8 @@ msgid ""
 "%s (or --work-tree=<directory>) not allowed without specifying %s (or --git-"
 "dir=<directory>)"
 msgstr ""
-"不允许 %s(或 --work-tree=<directory>)而没有指定 %s(或 --git-dir=<directory>)"
+"不允许 %s(或 --work-tree=<directory>)而没有指定 %s(或 --git-"
+"dir=<directory>)"
 
 #: builtin/init-db.c:578
 msgid "Cannot access current working directory"
@@ -2213,164 +2238,164 @@ msgstr "不能访问当前工作目录"
 msgid "Cannot access work tree '%s'"
 msgstr "不能访问工作区 '%s'"
 
-#: builtin/log.c:187
+#: builtin/log.c:188
 #, c-format
 msgid "Final output: %d %s\n"
 msgstr "最终输出:%d %s\n"
 
-#: builtin/log.c:395 builtin/log.c:483
+#: builtin/log.c:401 builtin/log.c:489
 #, c-format
 msgid "Could not read object %s"
 msgstr "不能读取对象 %s"
 
-#: builtin/log.c:507
+#: builtin/log.c:513
 #, c-format
 msgid "Unknown type: %d"
 msgstr "未知类型:%d"
 
-#: builtin/log.c:596
+#: builtin/log.c:602
 msgid "format.headers without value"
 msgstr "format.headers 没有值"
 
-#: builtin/log.c:669
+#: builtin/log.c:675
 msgid "name of output directory is too long"
 msgstr "输出目录名太长"
 
-#: builtin/log.c:680
+#: builtin/log.c:686
 #, c-format
 msgid "Cannot open patch file %s"
 msgstr "无法打开补丁文件 %s"
 
-#: builtin/log.c:694
+#: builtin/log.c:700
 msgid "Need exactly one range."
 msgstr "只需要一个范围。"
 
-#: builtin/log.c:702
+#: builtin/log.c:708
 msgid "Not a range."
 msgstr "不是一个范围。"
 
-#: builtin/log.c:739
+#: builtin/log.c:745
 msgid "Could not extract email from committer identity."
 msgstr "不能从提交者身份中提取邮件地址。"
 
-#: builtin/log.c:785
+#: builtin/log.c:791
 msgid "Cover letter needs email format"
 msgstr "信封需要邮件地址格式"
 
-#: builtin/log.c:879
+#: builtin/log.c:885
 #, c-format
 msgid "insane in-reply-to: %s"
 msgstr "不正常的 in-reply-to:%s"
 
-#: builtin/log.c:952
+#: builtin/log.c:958
 msgid "Two output directories?"
 msgstr "两个输出目录?"
 
-#: builtin/log.c:1173
+#: builtin/log.c:1179
 #, c-format
 msgid "bogus committer info %s"
 msgstr "虚假的提交者信息 %s"
 
-#: builtin/log.c:1218
+#: builtin/log.c:1224
 msgid "-n and -k are mutually exclusive."
 msgstr "-n 和 -k 互斥。"
 
-#: builtin/log.c:1220
+#: builtin/log.c:1226
 msgid "--subject-prefix and -k are mutually exclusive."
 msgstr "--subject-prefix 和 -k 互斥。"
 
-#: builtin/log.c:1225 builtin/shortlog.c:284
+#: builtin/log.c:1231 builtin/shortlog.c:284
 #, c-format
 msgid "unrecognized argument: %s"
 msgstr "未识别的参数:%s"
 
-#: builtin/log.c:1228
+#: builtin/log.c:1234
 msgid "--name-only does not make sense"
 msgstr "--name-only 无意义"
 
-#: builtin/log.c:1230
+#: builtin/log.c:1236
 msgid "--name-status does not make sense"
 msgstr "--name-status 无意义"
 
-#: builtin/log.c:1232
+#: builtin/log.c:1238
 msgid "--check does not make sense"
 msgstr "--check 无意义"
 
-#: builtin/log.c:1255
+#: builtin/log.c:1261
 msgid "standard output, or directory, which one?"
 msgstr "标准输出或目录,哪一个?"
 
-#: builtin/log.c:1257
+#: builtin/log.c:1263
 #, c-format
 msgid "Could not create directory '%s'"
 msgstr "不能创建目录 '%s'"
 
-#: builtin/log.c:1410
+#: builtin/log.c:1416
 msgid "Failed to create output files"
 msgstr "无法创建输出文件"
 
-#: builtin/log.c:1514
+#: builtin/log.c:1520
 #, c-format
 msgid ""
 "Could not find a tracked remote branch, please specify <upstream> manually.\n"
 msgstr "不能找到跟踪的远程分支,请手工指定 <upstream>。\n"
 
-#: builtin/log.c:1530 builtin/log.c:1532 builtin/log.c:1544
+#: builtin/log.c:1536 builtin/log.c:1538 builtin/log.c:1550
 #, c-format
 msgid "Unknown commit %s"
 msgstr "未知提交 %s"
 
-#: builtin/merge.c:91
+#: builtin/merge.c:90
 msgid "switch `m' requires a value"
 msgstr "开关 `m' 需要一个值"
 
-#: builtin/merge.c:128
+#: builtin/merge.c:127
 #, c-format
 msgid "Could not find merge strategy '%s'.\n"
 msgstr "不能找到合并策略 '%s'。\n"
 
-#: builtin/merge.c:129
+#: builtin/merge.c:128
 #, c-format
 msgid "Available strategies are:"
 msgstr "可用的策略有:"
 
-#: builtin/merge.c:134
+#: builtin/merge.c:133
 #, c-format
 msgid "Available custom strategies are:"
 msgstr "可用的自定义策略有:"
 
-#: builtin/merge.c:241
+#: builtin/merge.c:240
 msgid "could not run stash."
 msgstr "不能进行进度保存。"
 
-#: builtin/merge.c:246
+#: builtin/merge.c:245
 msgid "stash failed"
 msgstr "进度保存失败"
 
-#: builtin/merge.c:251
+#: builtin/merge.c:250
 #, c-format
 msgid "not a valid object: %s"
 msgstr "不是一个有效对象:%s"
 
-#: builtin/merge.c:270 builtin/merge.c:287
+#: builtin/merge.c:269 builtin/merge.c:286
 msgid "read-tree failed"
 msgstr "读取树失败"
 
 #  译者:注意保持前导空格
-#: builtin/merge.c:317
+#: builtin/merge.c:316
 msgid " (nothing to squash)"
 msgstr " (无可压缩)"
 
-#: builtin/merge.c:330
+#: builtin/merge.c:329
 #, c-format
 msgid "Squash commit -- not updating HEAD\n"
 msgstr "压缩提交 -- 未更新 HEAD\n"
 
-#: builtin/merge.c:362
+#: builtin/merge.c:361
 msgid "Writing SQUASH_MSG"
 msgstr "写入 SQUASH_MSG"
 
-#: builtin/merge.c:364
+#: builtin/merge.c:363
 msgid "Finishing SQUASH_MSG"
 msgstr "完成 SQUASH_MSG"
 
@@ -2397,35 +2422,35 @@ msgstr "git write-tree 无法写入一树对象"
 msgid "failed to read the cache"
 msgstr "无法读取缓存"
 
-#: builtin/merge.c:696
+#: builtin/merge.c:697
 msgid "Unable to write index."
 msgstr "不能写索引。"
 
-#: builtin/merge.c:709
+#: builtin/merge.c:710
 msgid "Not handling anything other than two heads merge."
 msgstr "不能处理两个头合并之外的任何操作。"
 
-#: builtin/merge.c:723
+#: builtin/merge.c:724
 #, c-format
 msgid "Unknown option for merge-recursive: -X%s"
 msgstr "merge-recursive 的未知选项:-X%s"
 
-#: builtin/merge.c:737
+#: builtin/merge.c:738
 #, c-format
 msgid "unable to write %s"
 msgstr "不能写 %s"
 
-#: builtin/merge.c:876
+#: builtin/merge.c:877
 #, c-format
 msgid "Could not read from '%s'"
 msgstr "不能从 '%s' 读取"
 
-#: builtin/merge.c:885
+#: builtin/merge.c:886
 #, c-format
 msgid "Not committing merge; use 'git commit' to complete the merge.\n"
 msgstr "未提交合并,使用 'git commit' 完成此次合并。\n"
 
-#: builtin/merge.c:891
+#: builtin/merge.c:892
 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"
@@ -2438,47 +2463,52 @@ msgstr ""
 "\n"
 "以 '#' 开头的行将被忽略,而且空提交说明将会终止提交。\n"
 
-#: builtin/merge.c:915
+#: builtin/merge.c:916
 msgid "Empty commit message."
 msgstr "空提交信息。"
 
-#: builtin/merge.c:927
+#: builtin/merge.c:928
 #, c-format
 msgid "Wonderful.\n"
 msgstr "太棒了。\n"
 
-#: builtin/merge.c:1000
+#: builtin/merge.c:993
 #, c-format
 msgid "Automatic merge failed; fix conflicts and then commit the result.\n"
 msgstr "自动合并失败,修正冲突然后提交修正的结果。\n"
 
-#: builtin/merge.c:1016
+#: builtin/merge.c:1009
 #, c-format
 msgid "'%s' is not a commit"
 msgstr "'%s' 不是一个提交"
 
-#: builtin/merge.c:1057
+#: builtin/merge.c:1050
 msgid "No current branch."
 msgstr "没有当前分支。"
 
-#: builtin/merge.c:1059
+#: builtin/merge.c:1052
 msgid "No remote for the current branch."
 msgstr "当前分支没有对应的远程版本库。"
 
-#: builtin/merge.c:1061
+#: builtin/merge.c:1054
 msgid "No default upstream defined for the current branch."
 msgstr "当前分支没有定义默认的上游分支。"
 
-#: builtin/merge.c:1066
+#: builtin/merge.c:1059
 #, c-format
 msgid "No remote tracking branch for %s from %s"
 msgstr "%s 没有来自 %s 的远程跟踪分支"
 
-#: builtin/merge.c:1188
+#: builtin/merge.c:1146 builtin/merge.c:1303
+#, c-format
+msgid "%s - not something we can merge"
+msgstr "%s - 不能被合并"
+
+#: builtin/merge.c:1214
 msgid "There is no merge to abort (MERGE_HEAD missing)."
 msgstr "没有要终止的合并(MERGE_HEAD 丢失)。"
 
-#: builtin/merge.c:1204 git-pull.sh:31
+#: builtin/merge.c:1230 git-pull.sh:31
 msgid ""
 "You have not concluded your merge (MERGE_HEAD exists).\n"
 "Please, commit your changes before you can merge."
@@ -2486,11 +2516,11 @@ msgstr ""
 "您尚未结束您的合并(存在 MERGE_HEAD)。\n"
 "请在合并前先提交您的修改。"
 
-#: builtin/merge.c:1207 git-pull.sh:34
+#: builtin/merge.c:1233 git-pull.sh:34
 msgid "You have not concluded your merge (MERGE_HEAD exists)."
 msgstr "您尚未结束您的合并(存在 MERGE_HEAD)。"
 
-#: builtin/merge.c:1211
+#: builtin/merge.c:1237
 msgid ""
 "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
 "Please, commit your changes before you can merge."
@@ -2498,84 +2528,79 @@ msgstr ""
 "您尚未结束您的拣选(存在 CHERRY_PICK_HEAD)。\n"
 "请在合并前先提交您的修改。"
 
-#: builtin/merge.c:1214
+#: builtin/merge.c:1240
 msgid "You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."
 msgstr "您尚未结束您的拣选(存在 CHERRY_PICK_HEAD)。"
 
-#: builtin/merge.c:1223
+#: builtin/merge.c:1249
 msgid "You cannot combine --squash with --no-ff."
 msgstr "您不能将 --squash 与 --no-ff 共用。"
 
-#: builtin/merge.c:1228
+#: builtin/merge.c:1254
 msgid "You cannot combine --no-ff with --ff-only."
 msgstr "您不能将 --no-ff 与 --ff-only 共用。"
 
-#: builtin/merge.c:1235
+#: builtin/merge.c:1261
 msgid "No commit specified and merge.defaultToUpstream not set."
 msgstr "未指定提交并且 merge.defaultToUpstream 未设置。"
 
-#: builtin/merge.c:1266
+#: builtin/merge.c:1293
 msgid "Can merge only exactly one commit into empty head"
 msgstr "只能将一个提交合并到空分支上"
 
-#: builtin/merge.c:1269
+#: builtin/merge.c:1296
 msgid "Squash commit into empty head not supported yet"
 msgstr "尚不支持到空分支的压缩提交"
 
-#: builtin/merge.c:1271
+#: builtin/merge.c:1298
 msgid "Non-fast-forward commit does not make sense into an empty head"
 msgstr "到空分支的非快进式提交没有意义"
 
-#: builtin/merge.c:1275 builtin/merge.c:1319
-#, c-format
-msgid "%s - not something we can merge"
-msgstr "%s - 不能被合并"
-
-#: builtin/merge.c:1385
+#: builtin/merge.c:1413
 #, c-format
 msgid "Updating %s..%s\n"
 msgstr "更新 %s..%s\n"
 
-#: builtin/merge.c:1423
+#: builtin/merge.c:1451
 #, c-format
 msgid "Trying really trivial in-index merge...\n"
 msgstr "尝试非常小的索引内合并...\n"
 
-#: builtin/merge.c:1430
+#: builtin/merge.c:1458
 #, c-format
 msgid "Nope.\n"
 msgstr "无。\n"
 
-#: builtin/merge.c:1462
+#: builtin/merge.c:1490
 msgid "Not possible to fast-forward, aborting."
 msgstr "无法快进,终止。"
 
-#: builtin/merge.c:1485 builtin/merge.c:1562
+#: builtin/merge.c:1513 builtin/merge.c:1592
 #, c-format
 msgid "Rewinding the tree to pristine...\n"
 msgstr "将树回滚至原始状态...\n"
 
-#: builtin/merge.c:1489
+#: builtin/merge.c:1517
 #, c-format
 msgid "Trying merge strategy %s...\n"
 msgstr "尝试合并策略 %s...\n"
 
-#: builtin/merge.c:1553
+#: builtin/merge.c:1583
 #, c-format
 msgid "No merge strategy handled the merge.\n"
 msgstr "没有合并策略处理此合并。\n"
 
-#: builtin/merge.c:1555
+#: builtin/merge.c:1585
 #, c-format
 msgid "Merge with strategy %s failed.\n"
 msgstr "使用策略 %s 合并失败。\n"
 
-#: builtin/merge.c:1564
+#: builtin/merge.c:1594
 #, c-format
 msgid "Using the %s to prepare resolving by hand.\n"
 msgstr "使用 %s 以准备手工解决。\n"
 
-#: builtin/merge.c:1575
+#: builtin/merge.c:1606
 #, c-format
 msgid "Automatic merge went well; stopped before committing as requested\n"
 msgstr "自动合并进展顺利,按要求在提交前停止\n"
@@ -2796,35 +2821,35 @@ msgstr "对象 %s 没有注解\n"
 msgid "Unknown subcommand: %s"
 msgstr "未知子命令:%s"
 
-#: builtin/pack-objects.c:2310
+#: builtin/pack-objects.c:2315
 #, c-format
 msgid "unsupported index version %s"
 msgstr "不支持的索引版本 %s"
 
-#: builtin/pack-objects.c:2314
+#: builtin/pack-objects.c:2319
 #, c-format
 msgid "bad index version '%s'"
 msgstr "坏的索引版本 '%s'"
 
-#: builtin/pack-objects.c:2322
+#: builtin/pack-objects.c:2342
 #, c-format
 msgid "option %s does not accept negative form"
 msgstr "选项 %s 不接受否定格式"
 
-#: builtin/pack-objects.c:2326
+#: builtin/pack-objects.c:2346
 #, c-format
 msgid "unable to parse value '%s' for option %s"
 msgstr "不能解析值 '%s' 针对于选项 %s"
 
-#: builtin/push.c:44
+#: builtin/push.c:45
 msgid "tag shorthand without <tag>"
 msgstr "tag 简写没有跟 <tag> 参数"
 
-#: builtin/push.c:63
+#: builtin/push.c:64
 msgid "--delete only accepts plain target ref names"
 msgstr "--delete 只接受简单的目标引用名"
 
-#: builtin/push.c:73
+#: builtin/push.c:84
 #, c-format
 msgid ""
 "You are not currently on a branch.\n"
@@ -2838,7 +2863,7 @@ msgstr ""
 "\n"
 "    git push %s HEAD:<name-of-remote-branch>\n"
 
-#: builtin/push.c:80
+#: builtin/push.c:91
 #, c-format
 msgid ""
 "The current branch %s has no upstream branch.\n"
@@ -2851,43 +2876,76 @@ msgstr ""
 "\n"
 "    git push --set-upstream %s %s\n"
 
-#: builtin/push.c:88
+#: builtin/push.c:99
 #, c-format
 msgid "The current branch %s has multiple upstream branches, refusing to push."
 msgstr "当前分支 %s 有多个上游分支,拒绝推送。"
 
-#: builtin/push.c:111
+#: builtin/push.c:102
+#, 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 ""
+"您正推送至远程 '%s'(其并非当前分支 '%s' 的上游),\n"
+"而没有告诉我要推送什么、更新哪个远程分支。"
+
+#: builtin/push.c:131
 msgid ""
 "You didn't specify any refspecs to push, and push.default is \"nothing\"."
 msgstr "您没有为推送指定任何引用表达式,并且 push.default 为 \"nothing\"。"
 
-#: builtin/push.c:131
+#: builtin/push.c:138
+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 ""
+"更新被拒绝,因为您当前分支的最新提交落后于其对应的远程分支。\n"
+"再次推送前,先与远程变更合并(如 'git pull')。详见\n"
+"'git push --help' 中的 'Note about fast-forwards' 小节。"
+
+#: builtin/push.c:144
+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\n"
+"variable to 'current' or 'upstream' to push only the current branch."
+msgstr ""
+"更新被拒绝,因为推送的一个分支的最新提交落后于其对应的远程分支。\n"
+"如果您并非有意推送该分支,您可以在推送时指定要推送的分支,或者将\n"
+"配置变量 'push.default' 设置为 'current' 或 'upstream' 以便只推送"
+"当前分支。"
+
+#: builtin/push.c:150
+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 ""
+"更新被拒绝,因为推送的一个分支的最新提交落后于其对应的远程分支。\n"
+"检出该分支并与远程变更合并(如 'git pull'),然后再推送。详见\n"
+"'git push --help' 中的 'Note about fast-forwards' 小节。"
+
+#: builtin/push.c:190
 #, c-format
 msgid "Pushing to %s\n"
 msgstr "推送到 %s\n"
 
-#: builtin/push.c:135
+#: builtin/push.c:194
 #, c-format
 msgid "failed to push some refs to '%s'"
 msgstr "无法推送一些引用到 '%s'"
 
-#: builtin/push.c:143
-#, c-format
-msgid ""
-"To prevent you from losing history, non-fast-forward updates were rejected\n"
-"Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
-"'Note about fast-forwards' section of 'git push --help' for details.\n"
-msgstr ""
-"为了防止您丢失提交历史,非快进式更新被拒绝。\n"
-"再次推送前先与远程变更合并(如 'git pull')。详见\n"
-"'git push --help' 中的 'Note about fast-forwards' 小节。\n"
-
-#: builtin/push.c:160
+#: builtin/push.c:226
 #, c-format
 msgid "bad repository '%s'"
 msgstr "坏的版本库 '%s'"
 
-#: builtin/push.c:161
+#: builtin/push.c:227
 msgid ""
 "No configured push destination.\n"
 "Either specify the URL from the command-line or configure a remote "
@@ -2908,31 +2966,31 @@ msgstr ""
 "\n"
 "    git push <name>\n"
 
-#: builtin/push.c:176
+#: builtin/push.c:242
 msgid "--all and --tags are incompatible"
 msgstr "--all 和 --tags 不兼容"
 
-#: builtin/push.c:177
+#: builtin/push.c:243
 msgid "--all can't be combined with refspecs"
 msgstr "--all 不能和引用表达式共用"
 
-#: builtin/push.c:182
+#: builtin/push.c:248
 msgid "--mirror and --tags are incompatible"
 msgstr "--mirror 和 --tags 不兼容"
 
-#: builtin/push.c:183
+#: builtin/push.c:249
 msgid "--mirror can't be combined with refspecs"
 msgstr "--mirror 不能和引用表达式共用"
 
-#: builtin/push.c:188
+#: builtin/push.c:254
 msgid "--all and --mirror are incompatible"
 msgstr "--all 和 --mirror 不兼容"
 
-#: builtin/push.c:274
+#: builtin/push.c:334
 msgid "--delete is incompatible with --all, --mirror and --tags"
 msgstr "--delete 与 --all、--mirror 及 --tags 不兼容"
 
-#: builtin/push.c:276
+#: builtin/push.c:336
 msgid "--delete doesn't make sense without any refs"
 msgstr "--delete 未接任何引用没有意义"
 
@@ -3018,20 +3076,20 @@ msgstr "不能对裸版本库进行%s重置"
 msgid "Could not reset index file to revision '%s'."
 msgstr "不能重置索引文件至版本 '%s'。"
 
-#: builtin/revert.c:70 builtin/revert.c:91
+#: builtin/revert.c:70 builtin/revert.c:92
 #, c-format
 msgid "%s: %s cannot be used with %s"
 msgstr "%s:%s 不能和 %s 共用"
 
-#: builtin/revert.c:126
+#: builtin/revert.c:127
 msgid "program error"
 msgstr "程序错误"
 
-#: builtin/revert.c:209
+#: builtin/revert.c:213
 msgid "revert failed"
 msgstr "还原失败"
 
-#: builtin/revert.c:224
+#: builtin/revert.c:228
 msgid "cherry-pick failed"
 msgstr "拣选失败"
 
@@ -3210,15 +3268,15 @@ msgstr "%s:不能更新引用"
 msgid "Updated tag '%s' (was %s)\n"
 msgstr "已更新tag '%s'(曾为 %s)\n"
 
-#: git-am.sh:49
+#: git-am.sh:50
 msgid "You need to set your committer info first"
 msgstr "您需要先设置你的提交者信息"
 
-#: git-am.sh:136
+#: git-am.sh:137
 msgid "Repository lacks necessary blobs to fall back on 3-way merge."
 msgstr "版本库缺乏必要的 blob 数据以进行三路合并。"
 
-#: git-am.sh:147
+#: git-am.sh:154
 msgid ""
 "Did you hand edit your patch?\n"
 "It does not apply to blobs recorded in its index."
@@ -3226,46 +3284,46 @@ msgstr ""
 "您是否曾手动编辑过您的补丁?\n"
 "无法应用补丁到索引中的数据上。"
 
-#: git-am.sh:156
+#: git-am.sh:163
 msgid "Falling back to patching base and 3-way merge..."
 msgstr "回退到补丁基础版本并使用三路合并..."
 
-#: git-am.sh:268
+#: git-am.sh:275
 msgid "Only one StGIT patch series can be applied at once"
 msgstr "一次只能有一个 StGIT 补丁队列被应用"
 
-#: git-am.sh:355
+#: git-am.sh:362
 #, sh-format
 msgid "Patch format $patch_format is not supported."
 msgstr "不支持 $patch_format 补丁格式。"
 
-#: git-am.sh:357
+#: git-am.sh:364
 msgid "Patch format detection failed."
 msgstr "补丁格式检测失败。"
 
-#: git-am.sh:411
+#: git-am.sh:418
 msgid "-d option is no longer supported.  Do not use."
 msgstr "不再支持 -d 选项。不要使用。"
 
-#: git-am.sh:474
+#: git-am.sh:481
 #, sh-format
 msgid "previous rebase directory $dotest still exists but mbox given."
 msgstr "之前的变基目录 $dotest 仍然存在但给出了mbox。"
 
-#: git-am.sh:479
+#: git-am.sh:486
 msgid "Please make up your mind. --skip or --abort?"
 msgstr "请下决心。--skip 或是 --abort ?"
 
-#: git-am.sh:506
+#: git-am.sh:513
 msgid "Resolve operation not in progress, we are not resuming."
 msgstr "解决操作未进行,我们不会继续。"
 
-#: git-am.sh:572
+#: git-am.sh:579
 #, sh-format
 msgid "Dirty index: cannot apply patches (dirty: $files)"
 msgstr "脏的索引:不能应用补丁(脏文件:$files)"
 
-#: git-am.sh:748
+#: git-am.sh:755
 msgid "cannot be interactive without stdin connected to a terminal."
 msgstr "标准输入没有和终端关联,不能进行交互式操作。"
 
@@ -3273,20 +3331,20 @@ 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:759
+#: git-am.sh:766
 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:795
+#: git-am.sh:802
 #, sh-format
 msgid "Applying: $FIRSTLINE"
 msgstr "正应用:$FIRSTLINE"
 
-#: git-am.sh:840
+#: git-am.sh:847
 msgid "No changes -- Patch already applied."
 msgstr "没有变更 -- 补丁已经应用过。"
 
-#: git-am.sh:866
+#: git-am.sh:873
 msgid "applying to an empty history"
 msgstr "正应用到一个空历史上"
 
@@ -3525,166 +3583,169 @@ msgstr "(为恢复数据输入 \"git stash apply\")"
 msgid "cannot strip one component off url '$remoteurl'"
 msgstr "无法从 url '$remoteurl' 剥离一个组件"
 
-#: git-submodule.sh:108
+#: git-submodule.sh:109
 #, sh-format
-msgid "No submodule mapping found in .gitmodules for path '$path'"
-msgstr "未在 .gitmodules 中发现路径 '$path' 的子模组映射"
+msgid "No submodule mapping found in .gitmodules for path '$sm_path'"
+msgstr "未在 .gitmodules 中发现路径 '$sm_path' 的子模组映射"
 
-#: git-submodule.sh:149
+#: git-submodule.sh:150
 #, sh-format
-msgid "Clone of '$url' into submodule path '$path' failed"
-msgstr "无法克隆 '$url' 到子模组路径 '$path'"
+msgid "Clone of '$url' into submodule path '$sm_path' failed"
+msgstr "无法克隆 '$url' 到子模组路径 '$sm_path'"
 
-#: git-submodule.sh:159
+#: git-submodule.sh:160
 #, sh-format
 msgid "Gitdir '$a' is part of the submodule path '$b' or vice versa"
 msgstr "Gitdir '$a' 在子模组路径 '$b' 之下或者相反"
 
-#: git-submodule.sh:247
+#: git-submodule.sh:248
 #, sh-format
 msgid "repo URL: '$repo' must be absolute or begin with ./|../"
 msgstr "版本库URL:'$repo' 必须是绝对路径或以 ./|../ 起始"
 
-#: git-submodule.sh:264
+#: git-submodule.sh:265
 #, sh-format
-msgid "'$path' already exists in the index"
-msgstr "'$path' 已经存在于索引中"
+msgid "'$sm_path' already exists in the index"
+msgstr "'$sm_path' 已经存在于索引中"
 
-#: git-submodule.sh:281
+#: git-submodule.sh:282
 #, sh-format
-msgid "'$path' already exists and is not a valid git repo"
-msgstr "'$path' 已存在且不是一个有效的 git 版本库"
+msgid "'$sm_path' already exists and is not a valid git repo"
+msgstr "'$sm_path' 已存在且不是一个有效的 git 版本库"
 
-#: git-submodule.sh:295
+#: git-submodule.sh:296
 #, sh-format
-msgid "Unable to checkout submodule '$path'"
-msgstr "不能检出子模组 '$path'"
+msgid "Unable to checkout submodule '$sm_path'"
+msgstr "不能检出子模组 '$sm_path'"
 
-#: git-submodule.sh:300
+#: git-submodule.sh:301
 #, sh-format
-msgid "Failed to add submodule '$path'"
-msgstr "无法添加子模组 '$path'"
+msgid "Failed to add submodule '$sm_path'"
+msgstr "无法添加子模组 '$sm_path'"
 
-#: git-submodule.sh:305
+#: git-submodule.sh:306
 #, sh-format
-msgid "Failed to register submodule '$path'"
-msgstr "无法注册子模组 '$path'"
+msgid "Failed to register submodule '$sm_path'"
+msgstr "无法注册子模组 '$sm_path'"
 
-#: git-submodule.sh:347
+#: git-submodule.sh:348
 #, sh-format
-msgid "Entering '$prefix$path'"
-msgstr "正在进入 '$prefix$path'"
+msgid "Entering '$prefix$sm_path'"
+msgstr "正在进入 '$prefix$sm_path'"
 
-#: git-submodule.sh:359
+#: git-submodule.sh:360
 #, sh-format
-msgid "Stopping at '$path'; script returned non-zero status."
-msgstr "停止于 '$path',脚本返回非零值。"
+msgid "Stopping at '$sm_path'; script returned non-zero status."
+msgstr "停止于 '$sm_path',脚本返回非零值。"
 
-#: git-submodule.sh:401
+#: git-submodule.sh:402
 #, sh-format
-msgid "No url found for submodule path '$path' in .gitmodules"
-msgstr "在 .gitmodules 中未找到子模组路径 '$path' 的 url"
+msgid "No url found for submodule path '$sm_path' in .gitmodules"
+msgstr "在 .gitmodules 中未找到子模组路径 '$sm_path' 的 url"
 
-#: git-submodule.sh:410
+#: git-submodule.sh:411
 #, sh-format
-msgid "Failed to register url for submodule path '$path'"
-msgstr "无法为子模组路径 '$path' 注册 url"
+msgid "Failed to register url for submodule path '$sm_path'"
+msgstr "无法为子模组路径 '$sm_path' 注册 url"
 
-#: git-submodule.sh:418
+#: git-submodule.sh:419
 #, sh-format
-msgid "Failed to register update mode for submodule path '$path'"
-msgstr "无法为子模组路径 '$path' 注册更新模式"
+msgid "Failed to register update mode for submodule path '$sm_path'"
+msgstr "无法为子模组路径 '$sm_path' 注册更新模式"
 
-#: git-submodule.sh:420
+#: git-submodule.sh:421
 #, sh-format
-msgid "Submodule '$name' ($url) registered for path '$path'"
-msgstr "子模组 '$name' ($url) 已为路径 '$path' 注册"
+msgid "Submodule '$name' ($url) registered for path '$sm_path'"
+msgstr "子模组 '$name' ($url) 已为路径 '$sm_path' 注册"
 
-#: git-submodule.sh:519
+#: git-submodule.sh:520
 #, sh-format
 msgid ""
-"Submodule path '$path' not initialized\n"
+"Submodule path '$sm_path' not initialized\n"
 "Maybe you want to use 'update --init'?"
 msgstr ""
-"子模组路径 '$path' 没有初始化\n"
+"子模组路径 '$sm_path' 没有初始化\n"
 "也许您想用 'update --init'?"
 
-#: git-submodule.sh:532
+#: git-submodule.sh:533
 #, sh-format
-msgid "Unable to find current revision in submodule path '$path'"
-msgstr "无法在子模组路径 '$path' 中找到当前版本"
+msgid "Unable to find current revision in submodule path '$sm_path'"
+msgstr "无法在子模组路径 '$sm_path' 中找到当前版本"
 
-#: git-submodule.sh:551
+#: git-submodule.sh:552
 #, sh-format
-msgid "Unable to fetch in submodule path '$path'"
-msgstr "无法在子模组路径 '$path' 中获取"
-
-#: git-submodule.sh:565
-#, sh-format
-msgid "Unable to rebase '$sha1' in submodule path '$path'"
-msgstr "无法在子模组路径 '$path' 中变基 '$sha1'"
+msgid "Unable to fetch in submodule path '$sm_path'"
+msgstr "无法在子模组路径 '$sm_path' 中获取"
 
 #: git-submodule.sh:566
 #, sh-format
-msgid "Submodule path '$path': rebased into '$sha1'"
-msgstr "子模组路径 '$path':变基至 '$sha1'"
+msgid "Unable to rebase '$sha1' in submodule path '$sm_path'"
+msgstr "无法在子模组路径 '$sm_path' 中变基 '$sha1'"
 
-#: git-submodule.sh:571
+#: git-submodule.sh:567
 #, sh-format
-msgid "Unable to merge '$sha1' in submodule path '$path'"
-msgstr "无法合并 '$sha1' 到子模组路径 '$path' 中"
+msgid "Submodule path '$sm_path': rebased into '$sha1'"
+msgstr "子模组路径 '$sm_path':变基至 '$sha1'"
 
 #: git-submodule.sh:572
 #, sh-format
-msgid "Submodule path '$path': merged in '$sha1'"
-msgstr "子模组路径 '$path':已合并入 '$sha1'"
+msgid "Unable to merge '$sha1' in submodule path '$sm_path'"
+msgstr "无法合并 '$sha1' 到子模组路径 '$sm_path' 中"
 
-#: git-submodule.sh:577
+#: git-submodule.sh:573
 #, sh-format
-msgid "Unable to checkout '$sha1' in submodule path '$path'"
-msgstr "无法在子模组路径 '$path' 中检出 '$sha1'"
+msgid "Submodule path '$sm_path': merged in '$sha1'"
+msgstr "子模组路径 '$sm_path':已合并入 '$sha1'"
 
 #: git-submodule.sh:578
 #, sh-format
-msgid "Submodule path '$path': checked out '$sha1'"
-msgstr "子模组路径 '$path':检出 '$sha1'"
+msgid "Unable to checkout '$sha1' in submodule path '$sm_path'"
+msgstr "无法在子模组路径 '$sm_path' 中检出 '$sha1'"
+
+#: git-submodule.sh:579
+#, sh-format
+msgid "Submodule path '$sm_path': checked out '$sha1'"
+msgstr "子模组路径 '$sm_path':检出 '$sha1'"
 
-#: git-submodule.sh:600 git-submodule.sh:923
+#: git-submodule.sh:601 git-submodule.sh:924
 #, sh-format
-msgid "Failed to recurse into submodule path '$path'"
-msgstr "无法递归进子模组路径 '$path'"
+msgid "Failed to recurse into submodule path '$sm_path'"
+msgstr "无法递归进子模组路径 '$sm_path'"
 
-#: git-submodule.sh:708
+#: git-submodule.sh:709
 msgid "--"
 msgstr "--"
 
 #  译者:注意保持前导空格
-#: git-submodule.sh:766
+#: git-submodule.sh:767
 #, sh-format
 msgid "  Warn: $name doesn't contain commit $sha1_src"
 msgstr "  警告:$name 未包含提交 $sha1_src"
 
 #  译者:注意保持前导空格
-#: git-submodule.sh:769
+#: git-submodule.sh:770
 #, sh-format
 msgid "  Warn: $name doesn't contain commit $sha1_dst"
 msgstr "  警告:$name 未包含提交 $sha1_dst"
 
 #  译者:注意保持前导空格
-#: git-submodule.sh:772
+#: git-submodule.sh:773
 #, sh-format
 msgid "  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
 msgstr "  警告:$name 未包含提交 $sha1_src 和 $sha1_dst"
 
-#: git-submodule.sh:797
+#: git-submodule.sh:798
 msgid "blob"
 msgstr "blob"
 
-#: git-submodule.sh:798
+#: git-submodule.sh:799
 msgid "submodule"
 msgstr "子模组"
 
-#: git-submodule.sh:969
+#: git-submodule.sh:970
 #, sh-format
 msgid "Synchronizing submodule url for '$name'"
 msgstr "为 '$name' 同步子模组 url"
+
+#~ msgid "Too many options specified"
+#~ msgstr "指定了太多的选项"
index 8688b8f2d45a493aa8b51b29f7d46b2abff7f30e..02a0a2bb43570fec656c4152454fa67743111a2c 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -531,41 +531,24 @@ static size_t format_person_part(struct strbuf *sb, char part,
 {
        /* currently all placeholders have same length */
        const int placeholder_len = 2;
-       int start, end, tz = 0;
+       int tz;
        unsigned long date = 0;
-       char *ep;
-       const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
        char person_name[1024];
        char person_mail[1024];
+       struct ident_split s;
+       const char *name_start, *name_end, *mail_start, *mail_end;
 
-       /* advance 'end' to point to email start delimiter */
-       for (end = 0; end < len && msg[end] != '<'; end++)
-               ; /* do nothing */
-
-       /*
-        * When end points at the '<' that we found, it should have
-        * matching '>' later, which means 'end' must be strictly
-        * below len - 1.
-        */
-       if (end >= len - 2)
+       if (split_ident_line(&s, msg, len) < 0)
                goto skip;
 
-       /* Seek for both name and email part */
-       name_start = msg;
-       name_end = msg+end;
-       while (name_end > name_start && isspace(*(name_end-1)))
-               name_end--;
-       mail_start = msg+end+1;
-       mail_end = mail_start;
-       while (mail_end < msg_end && *mail_end != '>')
-               mail_end++;
-       if (mail_end == msg_end)
-               goto skip;
-       end = mail_end-msg;
+       name_start = s.name_begin;
+       name_end = s.name_end;
+       mail_start = s.mail_begin;
+       mail_end = s.mail_end;
 
        if (part == 'N' || part == 'E') { /* mailmap lookup */
-               strlcpy(person_name, name_start, name_end-name_start+1);
-               strlcpy(person_mail, mail_start, mail_end-mail_start+1);
+               strlcpy(person_name, name_start, name_end - name_start + 1);
+               strlcpy(person_mail, mail_start, mail_end - mail_start + 1);
                mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
                name_start = person_name;
                name_end = name_start + strlen(person_name);
@@ -581,28 +564,20 @@ static size_t format_person_part(struct strbuf *sb, char part,
                return placeholder_len;
        }
 
-       /* advance 'start' to point to date start delimiter */
-       for (start = end + 1; start < len && isspace(msg[start]); start++)
-               ; /* do nothing */
-       if (start >= len)
-               goto skip;
-       date = strtoul(msg + start, &ep, 10);
-       if (msg + start == ep)
+       if (!s.date_begin)
                goto skip;
 
+       date = strtoul(s.date_begin, NULL, 10);
+
        if (part == 't') {      /* date, UNIX timestamp */
-               strbuf_add(sb, msg + start, ep - (msg + start));
+               strbuf_add(sb, s.date_begin, s.date_end - s.date_begin);
                return placeholder_len;
        }
 
        /* parse tz */
-       for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
-               ; /* do nothing */
-       if (start + 1 < len) {
-               tz = strtoul(msg + start + 1, NULL, 10);
-               if (msg[start] == '-')
-                       tz = -tz;
-       }
+       tz = strtoul(s.tz_begin + 1, NULL, 10);
+       if (*s.tz_begin == '-')
+               tz = -tz;
 
        switch (part) {
        case 'd':       /* date */
@@ -621,8 +596,9 @@ static size_t format_person_part(struct strbuf *sb, char part,
 
 skip:
        /*
-        * bogus commit, 'sb' cannot be updated, but we still need to
-        * compute a valid return value.
+        * reading from either a bogus commit, or a reflog entry with
+        * %gn, %ge, etc.; 'sb' cannot be updated, but we still need
+        * to compute a valid return value.
         */
        if (part == 'n' || part == 'e' || part == 't' || part == 'd'
            || part == 'D' || part == 'r' || part == 'i')
@@ -1034,6 +1010,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
                                get_reflog_selector(sb,
                                                    c->pretty_ctx->reflog_info,
                                                    c->pretty_ctx->date_mode,
+                                                   c->pretty_ctx->date_mode_explicit,
                                                    (placeholder[1] == 'd'));
                        return 2;
                case 's':       /* reflog message */
index 274e54b4f31da69bf7c0721d4c8ba8e264db5dde..ef355cc9a89b948688756b8c3681ac607518492b 100644 (file)
@@ -12,6 +12,8 @@
 #include "commit.h"
 #include "blob.h"
 #include "resolve-undo.h"
+#include "strbuf.h"
+#include "varint.h"
 
 static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 
@@ -157,16 +159,6 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
        return 0;
 }
 
-static int is_empty_blob_sha1(const unsigned char *sha1)
-{
-       static const unsigned char empty_blob_sha1[20] = {
-               0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
-               0x29,0xae,0x77,0x5a,0xd8,0xc2,0xe4,0x8c,0x53,0x91
-       };
-
-       return !hashcmp(sha1, empty_blob_sha1);
-}
-
 static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 {
        unsigned int changed = 0;
@@ -1189,15 +1181,74 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int reall
        return refresh_cache_ent(&the_index, ce, really, NULL, NULL);
 }
 
+
+/*****************************************************************
+ * Index File I/O
+ *****************************************************************/
+
+#define INDEX_FORMAT_DEFAULT 3
+
+/*
+ * dev/ino/uid/gid/size are also just tracked to the low 32 bits
+ * Again - this is just a (very strong in practice) heuristic that
+ * the inode hasn't changed.
+ *
+ * We save the fields in big-endian order to allow using the
+ * index file over NFS transparently.
+ */
+struct ondisk_cache_entry {
+       struct cache_time ctime;
+       struct cache_time mtime;
+       unsigned int dev;
+       unsigned int ino;
+       unsigned int mode;
+       unsigned int uid;
+       unsigned int gid;
+       unsigned int size;
+       unsigned char sha1[20];
+       unsigned short flags;
+       char name[FLEX_ARRAY]; /* more */
+};
+
+/*
+ * This struct is used when CE_EXTENDED bit is 1
+ * The struct must match ondisk_cache_entry exactly from
+ * ctime till flags
+ */
+struct ondisk_cache_entry_extended {
+       struct cache_time ctime;
+       struct cache_time mtime;
+       unsigned int dev;
+       unsigned int ino;
+       unsigned int mode;
+       unsigned int uid;
+       unsigned int gid;
+       unsigned int size;
+       unsigned char sha1[20];
+       unsigned short flags;
+       unsigned short flags2;
+       char name[FLEX_ARRAY]; /* more */
+};
+
+/* These are only used for v3 or lower */
+#define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
+#define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
+#define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
+#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
+                           ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
+                           ondisk_cache_entry_size(ce_namelen(ce)))
+
 static int verify_hdr(struct cache_header *hdr, unsigned long size)
 {
        git_SHA_CTX c;
        unsigned char sha1[20];
+       int hdr_version;
 
        if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
                return error("bad signature");
-       if (hdr->hdr_version != htonl(2) && hdr->hdr_version != htonl(3))
-               return error("bad index version");
+       hdr_version = ntohl(hdr->hdr_version);
+       if (hdr_version < 2 || 4 < hdr_version)
+               return error("bad index version %d", hdr_version);
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, hdr, size - 20);
        git_SHA1_Final(sha1, &c);
@@ -1231,7 +1282,74 @@ int read_index(struct index_state *istate)
        return read_index_from(istate, get_index_file());
 }
 
-static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk)
+#ifndef NEEDS_ALIGNED_ACCESS
+#define ntoh_s(var) ntohs(var)
+#define ntoh_l(var) ntohl(var)
+#else
+static inline uint16_t ntoh_s_force_align(void *p)
+{
+       uint16_t x;
+       memcpy(&x, p, sizeof(x));
+       return ntohs(x);
+}
+static inline uint32_t ntoh_l_force_align(void *p)
+{
+       uint32_t x;
+       memcpy(&x, p, sizeof(x));
+       return ntohl(x);
+}
+#define ntoh_s(var) ntoh_s_force_align(&(var))
+#define ntoh_l(var) ntoh_l_force_align(&(var))
+#endif
+
+static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *ondisk,
+                                                  unsigned int flags,
+                                                  const char *name,
+                                                  size_t len)
+{
+       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_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_flags = flags;
+       hashcpy(ce->sha1, ondisk->sha1);
+       memcpy(ce->name, name, len);
+       ce->name[len] = '\0';
+       return ce;
+}
+
+/*
+ * Adjacent cache entries tend to share the leading paths, so it makes
+ * sense to only store the differences in later entries.  In the v4
+ * on-disk format of the index, each on-disk cache entry stores the
+ * number of bytes to be stripped from the end of the previous name,
+ * and the bytes to append to the result, to come up with its name.
+ */
+static unsigned long expand_name_field(struct strbuf *name, const char *cp_)
+{
+       const unsigned char *ep, *cp = (const unsigned char *)cp_;
+       size_t len = decode_varint(&cp);
+
+       if (name->len < len)
+               die("malformed name field in the index");
+       strbuf_remove(name, name->len - len, len);
+       for (ep = cp; *ep; ep++)
+               ; /* find the end */
+       strbuf_add(name, cp, ep - cp);
+       return (const char *)ep + 1 - cp_;
+}
+
+static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk,
+                                           unsigned long *ent_size,
+                                           struct strbuf *previous_name)
 {
        struct cache_entry *ce;
        size_t len;
@@ -1239,14 +1357,14 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk)
        unsigned int flags;
 
        /* On-disk flags are just 16 bits */
-       flags = ntohs(ondisk->flags);
+       flags = ntoh_s(ondisk->flags);
        len = flags & CE_NAMEMASK;
 
        if (flags & CE_EXTENDED) {
                struct ondisk_cache_entry_extended *ondisk2;
                int extended_flags;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
-               extended_flags = ntohs(ondisk2->flags2) << 16;
+               extended_flags = ntoh_s(ondisk2->flags2) << 16;
                /* We do not yet understand any bit out of CE_EXTENDED_FLAGS */
                if (extended_flags & ~CE_EXTENDED_FLAGS)
                        die("Unknown index entry format %08x", extended_flags);
@@ -1256,27 +1374,22 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk)
        else
                name = ondisk->name;
 
-       if (len == CE_NAMEMASK)
-               len = strlen(name);
-
-       ce = xmalloc(cache_entry_size(len));
-
-       ce->ce_ctime.sec = ntohl(ondisk->ctime.sec);
-       ce->ce_mtime.sec = ntohl(ondisk->mtime.sec);
-       ce->ce_ctime.nsec = ntohl(ondisk->ctime.nsec);
-       ce->ce_mtime.nsec = ntohl(ondisk->mtime.nsec);
-       ce->ce_dev   = ntohl(ondisk->dev);
-       ce->ce_ino   = ntohl(ondisk->ino);
-       ce->ce_mode  = ntohl(ondisk->mode);
-       ce->ce_uid   = ntohl(ondisk->uid);
-       ce->ce_gid   = ntohl(ondisk->gid);
-       ce->ce_size  = ntohl(ondisk->size);
-       ce->ce_flags = flags;
-
-       hashcpy(ce->sha1, ondisk->sha1);
-
-       memcpy(ce->name, name, len);
-       ce->name[len] = '\0';
+       if (!previous_name) {
+               /* v3 and earlier */
+               if (len == CE_NAMEMASK)
+                       len = strlen(name);
+               ce = cache_entry_from_ondisk(ondisk, flags, name, len);
+
+               *ent_size = ondisk_ce_size(ce);
+       } else {
+               unsigned long consumed;
+               consumed = expand_name_field(previous_name, name);
+               ce = cache_entry_from_ondisk(ondisk, flags,
+                                            previous_name->buf,
+                                            previous_name->len);
+
+               *ent_size = (name - ((char *)ondisk)) + consumed;
+       }
        return ce;
 }
 
@@ -1289,6 +1402,7 @@ int read_index_from(struct index_state *istate, const char *path)
        struct cache_header *hdr;
        void *mmap;
        size_t mmap_size;
+       struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 
        errno = EBUSY;
        if (istate->initialized)
@@ -1321,22 +1435,30 @@ int read_index_from(struct index_state *istate, const char *path)
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
 
+       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->initialized = 1;
 
+       if (istate->version == 4)
+               previous_name = &previous_name_buf;
+       else
+               previous_name = NULL;
+
        src_offset = sizeof(*hdr);
        for (i = 0; i < istate->cache_nr; i++) {
                struct ondisk_cache_entry *disk_ce;
                struct cache_entry *ce;
+               unsigned long consumed;
 
                disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
-               ce = create_from_disk(disk_ce);
+               ce = create_from_disk(disk_ce, &consumed, previous_name);
                set_index_entry(istate, i, ce);
 
-               src_offset += ondisk_ce_size(ce);
+               src_offset += consumed;
        }
+       strbuf_release(&previous_name_buf);
        istate->timestamp.sec = st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
 
@@ -1520,13 +1642,10 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
        }
 }
 
-static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
+/* Copy miscellaneous fields but not the name */
+static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
+                                      struct cache_entry *ce)
 {
-       int size = ondisk_ce_size(ce);
-       struct ondisk_cache_entry *ondisk = xcalloc(1, size);
-       char *name;
-       int result;
-
        ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
        ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
        ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
@@ -1543,11 +1662,52 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
                struct ondisk_cache_entry_extended *ondisk2;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
                ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
-               name = ondisk2->name;
+               return ondisk2->name;
+       }
+       else {
+               return ondisk->name;
+       }
+}
+
+static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
+                         struct strbuf *previous_name)
+{
+       int size;
+       struct ondisk_cache_entry *ondisk;
+       char *name;
+       int result;
+
+       if (!previous_name) {
+               size = ondisk_ce_size(ce);
+               ondisk = xcalloc(1, size);
+               name = copy_cache_entry_to_ondisk(ondisk, ce);
+               memcpy(name, ce->name, ce_namelen(ce));
+       } else {
+               int common, to_remove, prefix_size;
+               unsigned char to_remove_vi[16];
+               for (common = 0;
+                    (ce->name[common] &&
+                     common < previous_name->len &&
+                     ce->name[common] == previous_name->buf[common]);
+                    common++)
+                       ; /* still matching */
+               to_remove = previous_name->len - common;
+               prefix_size = encode_varint(to_remove, to_remove_vi);
+
+               if (ce->ce_flags & CE_EXTENDED)
+                       size = offsetof(struct ondisk_cache_entry_extended, name);
+               else
+                       size = offsetof(struct ondisk_cache_entry, name);
+               size += prefix_size + (ce_namelen(ce) - common + 1);
+
+               ondisk = xcalloc(1, size);
+               name = copy_cache_entry_to_ondisk(ondisk, ce);
+               memcpy(name, to_remove_vi, prefix_size);
+               memcpy(name + prefix_size, ce->name + common, ce_namelen(ce) - common);
+
+               strbuf_splice(previous_name, common, to_remove,
+                             ce->name + common, ce_namelen(ce) - common);
        }
-       else
-               name = ondisk->name;
-       memcpy(name, ce->name, ce_namelen(ce));
 
        result = ce_write(c, fd, ondisk, size);
        free(ondisk);
@@ -1583,10 +1743,11 @@ int write_index(struct index_state *istate, int newfd)
 {
        git_SHA_CTX c;
        struct cache_header hdr;
-       int i, err, removed, extended;
+       int i, err, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
        struct stat st;
+       struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 
        for (i = removed = extended = 0; i < entries; i++) {
                if (cache[i]->ce_flags & CE_REMOVE)
@@ -1600,24 +1761,34 @@ int write_index(struct index_state *istate, int newfd)
                }
        }
 
+       if (!istate->version)
+               istate->version = INDEX_FORMAT_DEFAULT;
+
+       /* demote version 3 to version 2 when the latter suffices */
+       if (istate->version == 3 || istate->version == 2)
+               istate->version = extended ? 3 : 2;
+
+       hdr_version = istate->version;
+
        hdr.hdr_signature = htonl(CACHE_SIGNATURE);
-       /* for extended format, increase version so older git won't try to read it */
-       hdr.hdr_version = htonl(extended ? 3 : 2);
+       hdr.hdr_version = htonl(hdr_version);
        hdr.hdr_entries = htonl(entries - removed);
 
        git_SHA1_Init(&c);
        if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
                return -1;
 
+       previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
        for (i = 0; i < entries; i++) {
                struct cache_entry *ce = cache[i];
                if (ce->ce_flags & CE_REMOVE)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
-               if (ce_write_entry(&c, newfd, ce) < 0)
+               if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
                        return -1;
        }
+       strbuf_release(&previous_name_buf);
 
        /* Write extension data here */
        if (istate->cache_tree) {
index 86d18843f52046d87741bffa9f865ec973a2ae73..b2fbdb2392f80a531d5f9a21630a036d45c0a827 100644 (file)
@@ -126,7 +126,12 @@ static void add_commit_info(struct commit *commit, void *util,
 }
 
 struct commit_reflog {
-       int flag, recno;
+       int recno;
+       enum selector_type {
+               SELECTOR_NONE,
+               SELECTOR_INDEX,
+               SELECTOR_DATE
+       } selector;
        struct complete_reflogs *reflogs;
 };
 
@@ -150,6 +155,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
        struct complete_reflogs *reflogs;
        char *branch, *at = strchr(name, '@');
        struct commit_reflog *commit_reflog;
+       enum selector_type selector = SELECTOR_NONE;
 
        if (commit->object.flags & UNINTERESTING)
                die ("Cannot walk reflogs for %s", name);
@@ -162,7 +168,10 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
                if (*ep != '}') {
                        recno = -1;
                        timestamp = approxidate(at + 2);
+                       selector = SELECTOR_DATE;
                }
+               else
+                       selector = SELECTOR_INDEX;
        } else
                recno = 0;
 
@@ -200,7 +209,6 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
 
        commit_reflog = xcalloc(sizeof(struct commit_reflog), 1);
        if (recno < 0) {
-               commit_reflog->flag = 1;
                commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
                if (commit_reflog->recno < 0) {
                        free(branch);
@@ -209,6 +217,7 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
                }
        } else
                commit_reflog->recno = reflogs->nr - recno - 1;
+       commit_reflog->selector = selector;
        commit_reflog->reflogs = reflogs;
 
        add_commit_info(commit, commit_reflog, &info->reflogs);
@@ -247,7 +256,7 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
 
 void get_reflog_selector(struct strbuf *sb,
                         struct reflog_walk_info *reflog_info,
-                        enum date_mode dmode,
+                        enum date_mode dmode, int force_date,
                         int shorten)
 {
        struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
@@ -267,7 +276,8 @@ void get_reflog_selector(struct strbuf *sb,
        }
 
        strbuf_addf(sb, "%s@{", printed_ref);
-       if (commit_reflog->flag || dmode) {
+       if (commit_reflog->selector == SELECTOR_DATE ||
+           (commit_reflog->selector == SELECTOR_NONE && force_date)) {
                info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
                strbuf_addstr(sb, show_date(info->timestamp, info->tz, dmode));
        } else {
@@ -308,7 +318,7 @@ const char *get_reflog_ident(struct reflog_walk_info *reflog_info)
 }
 
 void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
-       enum date_mode dmode)
+                        enum date_mode dmode, int force_date)
 {
        if (reflog_info && reflog_info->last_commit_reflog) {
                struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
@@ -316,7 +326,7 @@ void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
                struct strbuf selector = STRBUF_INIT;
 
                info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
-               get_reflog_selector(&selector, reflog_info, dmode, 0);
+               get_reflog_selector(&selector, reflog_info, dmode, force_date, 0);
                if (oneline) {
                        printf("%s: %s", selector.buf, info->message);
                }
index afb1ae3fde93a5c61def211be56b0554a25dd45e..50265f51c56431025b665abf34ac29fdce3d302b 100644 (file)
@@ -11,13 +11,13 @@ extern int add_reflog_for_walk(struct reflog_walk_info *info,
 extern void fake_reflog_parent(struct reflog_walk_info *info,
                struct commit *commit);
 extern void show_reflog_message(struct reflog_walk_info *info, int,
-               enum date_mode);
+                               enum date_mode, int force_date);
 extern void get_reflog_message(struct strbuf *sb,
                struct reflog_walk_info *reflog_info);
 extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
 extern void get_reflog_selector(struct strbuf *sb,
                struct reflog_walk_info *reflog_info,
-               enum date_mode dmode,
+               enum date_mode dmode, int force_date,
                int shorten);
 
 #endif
diff --git a/refs.c b/refs.c
index 98f6425907e02dcd0ec49dccf213e64d65ded19e..010ed07e33d90cbdae4511013319a129c1201ca9 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -101,11 +101,45 @@ int check_refname_format(const char *refname, int flags)
 
 struct ref_entry;
 
+/*
+ * Information used (along with the information in ref_entry) to
+ * describe a single cached reference.  This data structure only
+ * occurs embedded in a union in struct ref_entry, and only when
+ * (ref_entry->flag & REF_DIR) is zero.
+ */
 struct ref_value {
        unsigned char sha1[20];
        unsigned char peeled[20];
 };
 
+struct ref_cache;
+
+/*
+ * Information used (along with the information in ref_entry) to
+ * describe a level in the hierarchy of references.  This data
+ * structure only occurs embedded in a union in struct ref_entry, and
+ * only when (ref_entry.flag & REF_DIR) is set.  In that case,
+ * (ref_entry.flag & REF_INCOMPLETE) determines whether the references
+ * in the directory have already been read:
+ *
+ *     (ref_entry.flag & REF_INCOMPLETE) unset -- a directory of loose
+ *         or packed references, already read.
+ *
+ *     (ref_entry.flag & REF_INCOMPLETE) set -- a directory of loose
+ *         references that hasn't been read yet (nor has any of its
+ *         subdirectories).
+ *
+ * Entries within a directory are stored within a growable array of
+ * pointers to ref_entries (entries, nr, alloc).  Entries 0 <= i <
+ * sorted are sorted by their component name in strcmp() order and the
+ * remaining entries are unsorted.
+ *
+ * Loose references are read lazily, one directory at a time.  When a
+ * directory of loose references is read, then all of the references
+ * in that directory are stored, and REF_INCOMPLETE stubs are created
+ * for any subdirectories, but the subdirectories themselves are not
+ * read.  The reading is triggered by get_ref_dir().
+ */
 struct ref_dir {
        int nr, alloc;
 
@@ -117,24 +151,41 @@ struct ref_dir {
         */
        int sorted;
 
+       /* A pointer to the ref_cache that contains this ref_dir. */
+       struct ref_cache *ref_cache;
+
        struct ref_entry **entries;
 };
 
 /* ISSYMREF=0x01, ISPACKED=0x02, and ISBROKEN=0x04 are public interfaces */
 #define REF_KNOWS_PEELED 0x08
+
+/* ref_entry represents a directory of references */
 #define REF_DIR 0x10
 
+/*
+ * Entry has not yet been read from disk (used only for REF_DIR
+ * entries representing loose references)
+ */
+#define REF_INCOMPLETE 0x20
+
 /*
  * A ref_entry represents either a reference or a "subdirectory" of
- * references.  Each directory in the reference namespace is
- * represented by a ref_entry with (flags & REF_DIR) set and
- * containing a subdir member that holds the entries in that
- * directory.  References are represented by a ref_entry with (flags &
- * REF_DIR) unset and a value member that describes the reference's
- * value.  The flag member is at the ref_entry level, but it is also
- * needed to interpret the contents of the value field (in other
- * words, a ref_value object is not very much use without the
- * enclosing ref_entry).
+ * references.
+ *
+ * Each directory in the reference namespace is represented by a
+ * ref_entry with (flags & REF_DIR) set and containing a subdir member
+ * that holds the entries in that directory that have been read so
+ * far.  If (flags & REF_INCOMPLETE) is set, then the directory and
+ * its subdirectories haven't been read yet.  REF_INCOMPLETE is only
+ * used for loose reference directories.
+ *
+ * References are represented by a ref_entry with (flags & REF_DIR)
+ * unset and a value member that describes the reference's value.  The
+ * flag member is at the ref_entry level, but it is also needed to
+ * interpret the contents of the value field (in other words, a
+ * ref_value object is not very much use without the enclosing
+ * ref_entry).
  *
  * Reference names cannot end with slash and directories' names are
  * always stored with a trailing slash (except for the top-level
@@ -171,6 +222,20 @@ struct ref_entry {
        char name[FLEX_ARRAY];
 };
 
+static void read_loose_refs(const char *dirname, struct ref_dir *dir);
+
+static struct ref_dir *get_ref_dir(struct ref_entry *entry)
+{
+       struct ref_dir *dir;
+       assert(entry->flag & REF_DIR);
+       dir = &entry->u.subdir;
+       if (entry->flag & REF_INCOMPLETE) {
+               read_loose_refs(entry->name, dir);
+               entry->flag &= ~REF_INCOMPLETE;
+       }
+       return dir;
+}
+
 static struct ref_entry *create_ref_entry(const char *refname,
                                          const unsigned char *sha1, int flag,
                                          int check_name)
@@ -195,7 +260,7 @@ static void clear_ref_dir(struct ref_dir *dir);
 static void free_ref_entry(struct ref_entry *entry)
 {
        if (entry->flag & REF_DIR)
-               clear_ref_dir(&entry->u.subdir);
+               clear_ref_dir(get_ref_dir(entry));
        free(entry);
 }
 
@@ -234,13 +299,15 @@ static void clear_ref_dir(struct ref_dir *dir)
  * dirname is the name of the directory with a trailing slash (e.g.,
  * "refs/heads/") or "" for the top-level directory.
  */
-static struct ref_entry *create_dir_entry(const char *dirname)
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+                                         const char *dirname, int incomplete)
 {
        struct ref_entry *direntry;
        int len = strlen(dirname);
        direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
        memcpy(direntry->name, dirname, len + 1);
-       direntry->flag = REF_DIR;
+       direntry->u.subdir.ref_cache = ref_cache;
+       direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
        return direntry;
 }
 
@@ -256,7 +323,7 @@ static void sort_ref_dir(struct ref_dir *dir);
 /*
  * 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.
+ * such entry is found.  dir must already be complete.
  */
 static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname)
 {
@@ -282,39 +349,61 @@ static struct ref_entry *search_ref_dir(struct ref_dir *dir, const char *refname
        return *r;
 }
 
+/*
+ * Search for a directory entry directly within dir (without
+ * recursing).  Sort dir if necessary.  subdirname must be a directory
+ * name (i.e., end in '/').  If mkdir is set, then create the
+ * directory if it is missing; otherwise, return NULL if the desired
+ * directory cannot be found.  dir must already be complete.
+ */
+static struct ref_dir *search_for_subdir(struct ref_dir *dir,
+                                        const char *subdirname, int mkdir)
+{
+       struct ref_entry *entry = search_ref_dir(dir, subdirname);
+       if (!entry) {
+               if (!mkdir)
+                       return NULL;
+               /*
+                * Since dir is complete, the absence of a subdir
+                * means that the subdir really doesn't exist;
+                * therefore, create an empty record for it but mark
+                * the record complete.
+                */
+               entry = create_dir_entry(dir->ref_cache, subdirname, 0);
+               add_entry_to_dir(dir, entry);
+       }
+       return get_ref_dir(entry);
+}
+
 /*
  * If refname is a reference name, find the ref_dir within the dir
  * tree that should hold refname.  If refname is a directory name
  * (i.e., ends in '/'), then return that ref_dir itself.  dir must
- * represent the top-level directory.  Sort ref_dirs and recurse into
- * subdirectories as necessary.  If mkdir is set, then create any
- * missing directories; otherwise, return NULL if the desired
- * directory cannot be found.
+ * represent the top-level directory and must already be complete.
+ * Sort ref_dirs and recurse into subdirectories as necessary.  If
+ * mkdir is set, then create any missing directories; otherwise,
+ * return NULL if the desired directory cannot be found.
  */
 static struct ref_dir *find_containing_dir(struct ref_dir *dir,
                                           const char *refname, int mkdir)
 {
-       char *refname_copy = xstrdup(refname);
-       char *slash;
-       struct ref_entry *entry;
-       for (slash = strchr(refname_copy, '/'); slash; slash = strchr(slash + 1, '/')) {
-               char tmp = slash[1];
-               slash[1] = '\0';
-               entry = search_ref_dir(dir, refname_copy);
-               if (!entry) {
-                       if (!mkdir) {
-                               dir = NULL;
-                               break;
-                       }
-                       entry = create_dir_entry(refname_copy);
-                       add_entry_to_dir(dir, entry);
+       struct strbuf dirname;
+       const char *slash;
+       strbuf_init(&dirname, PATH_MAX);
+       for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+               struct ref_dir *subdir;
+               strbuf_add(&dirname,
+                          refname + dirname.len,
+                          (slash + 1) - (refname + dirname.len));
+               subdir = search_for_subdir(dir, dirname.buf, mkdir);
+               if (!subdir) {
+                       dir = NULL;
+                       break;
                }
-               slash[1] = tmp;
-               assert(entry->flag & REF_DIR);
-               dir = &entry->u.subdir;
+               dir = subdir;
        }
 
-       free(refname_copy);
+       strbuf_release(&dirname);
        return dir;
 }
 
@@ -440,8 +529,9 @@ static int do_for_each_ref_in_dir(struct ref_dir *dir, int offset,
                struct ref_entry *entry = dir->entries[i];
                int retval;
                if (entry->flag & REF_DIR) {
-                       sort_ref_dir(&entry->u.subdir);
-                       retval = do_for_each_ref_in_dir(&entry->u.subdir, 0,
+                       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);
                } else {
                        retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
@@ -486,10 +576,12 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
                if (cmp == 0) {
                        if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {
                                /* Both are directories; descend them in parallel. */
-                               sort_ref_dir(&e1->u.subdir);
-                               sort_ref_dir(&e2->u.subdir);
+                               struct ref_dir *subdir1 = get_ref_dir(e1);
+                               struct ref_dir *subdir2 = get_ref_dir(e2);
+                               sort_ref_dir(subdir1);
+                               sort_ref_dir(subdir2);
                                retval = do_for_each_ref_in_dirs(
-                                               &e1->u.subdir, &e2->u.subdir,
+                                               subdir1, subdir2,
                                                base, fn, trim, flags, cb_data);
                                i1++;
                                i2++;
@@ -512,9 +604,10 @@ static int do_for_each_ref_in_dirs(struct ref_dir *dir1,
                                i2++;
                        }
                        if (e->flag & REF_DIR) {
-                               sort_ref_dir(&e->u.subdir);
+                               struct ref_dir *subdir = get_ref_dir(e);
+                               sort_ref_dir(subdir);
                                retval = do_for_each_ref_in_dir(
-                                               &e->u.subdir, 0,
+                                               subdir, 0,
                                                base, fn, trim, flags, cb_data);
                        } else {
                                retval = do_one_ref(base, fn, trim, flags, cb_data, e);
@@ -598,26 +691,26 @@ static int is_refname_available(const char *refname, const char *oldrefname,
  */
 static struct ref_cache {
        struct ref_cache *next;
-       char did_loose;
-       char did_packed;
-       struct ref_dir loose;
-       struct ref_dir packed;
+       struct ref_entry *loose;
+       struct ref_entry *packed;
        /* The submodule name, or "" for the main repo. */
        char name[FLEX_ARRAY];
 } *ref_cache;
 
 static void clear_packed_ref_cache(struct ref_cache *refs)
 {
-       if (refs->did_packed)
-               clear_ref_dir(&refs->packed);
-       refs->did_packed = 0;
+       if (refs->packed) {
+               free_ref_entry(refs->packed);
+               refs->packed = NULL;
+       }
 }
 
 static void clear_loose_ref_cache(struct ref_cache *refs)
 {
-       if (refs->did_loose)
-               clear_ref_dir(&refs->loose);
-       refs->did_loose = 0;
+       if (refs->loose) {
+               free_ref_entry(refs->loose);
+               refs->loose = NULL;
+       }
 }
 
 static struct ref_cache *create_ref_cache(const char *submodule)
@@ -731,22 +824,22 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
 
 static struct ref_dir *get_packed_refs(struct ref_cache *refs)
 {
-       if (!refs->did_packed) {
+       if (!refs->packed) {
                const char *packed_refs_file;
                FILE *f;
 
+               refs->packed = create_dir_entry(refs, "", 0);
                if (*refs->name)
                        packed_refs_file = git_path_submodule(refs->name, "packed-refs");
                else
                        packed_refs_file = git_path("packed-refs");
                f = fopen(packed_refs_file, "r");
                if (f) {
-                       read_packed_refs(f, &refs->packed);
+                       read_packed_refs(f, get_ref_dir(refs->packed));
                        fclose(f);
                }
-               refs->did_packed = 1;
        }
-       return &refs->packed;
+       return get_ref_dir(refs->packed);
 }
 
 void add_packed_ref(const char *refname, const unsigned char *sha1)
@@ -755,76 +848,89 @@ void add_packed_ref(const char *refname, const unsigned char *sha1)
                        create_ref_entry(refname, sha1, REF_ISPACKED, 1));
 }
 
-static void get_ref_dir(struct ref_cache *refs, const char *base,
-                       struct ref_dir *dir)
+/*
+ * Read the loose references from the namespace dirname into dir
+ * (without recursing).  dirname must end with '/'.  dir must be the
+ * directory entry corresponding to dirname.
+ */
+static void read_loose_refs(const char *dirname, struct ref_dir *dir)
 {
+       struct ref_cache *refs = dir->ref_cache;
        DIR *d;
        const char *path;
+       struct dirent *de;
+       int dirnamelen = strlen(dirname);
+       struct strbuf refname;
 
        if (*refs->name)
-               path = git_path_submodule(refs->name, "%s", base);
+               path = git_path_submodule(refs->name, "%s", dirname);
        else
-               path = git_path("%s", base);
+               path = git_path("%s", dirname);
 
        d = opendir(path);
-       if (d) {
-               struct dirent *de;
-               int baselen = strlen(base);
-               char *refname = xmalloc(baselen + 257);
-
-               memcpy(refname, base, baselen);
-               if (baselen && base[baselen-1] != '/')
-                       refname[baselen++] = '/';
-
-               while ((de = readdir(d)) != NULL) {
-                       unsigned char sha1[20];
-                       struct stat st;
-                       int flag;
-                       int namelen;
-                       const char *refdir;
-
-                       if (de->d_name[0] == '.')
-                               continue;
-                       namelen = strlen(de->d_name);
-                       if (namelen > 255)
-                               continue;
-                       if (has_extension(de->d_name, ".lock"))
-                               continue;
-                       memcpy(refname + baselen, de->d_name, namelen+1);
-                       refdir = *refs->name
-                               ? git_path_submodule(refs->name, "%s", refname)
-                               : git_path("%s", refname);
-                       if (stat(refdir, &st) < 0)
-                               continue;
-                       if (S_ISDIR(st.st_mode)) {
-                               get_ref_dir(refs, refname, dir);
-                               continue;
-                       }
+       if (!d)
+               return;
+
+       strbuf_init(&refname, dirnamelen + 257);
+       strbuf_add(&refname, dirname, dirnamelen);
+
+       while ((de = readdir(d)) != NULL) {
+               unsigned char sha1[20];
+               struct stat st;
+               int flag;
+               const char *refdir;
+
+               if (de->d_name[0] == '.')
+                       continue;
+               if (has_extension(de->d_name, ".lock"))
+                       continue;
+               strbuf_addstr(&refname, de->d_name);
+               refdir = *refs->name
+                       ? git_path_submodule(refs->name, "%s", refname.buf)
+                       : git_path("%s", refname.buf);
+               if (stat(refdir, &st) < 0) {
+                       ; /* silently ignore */
+               } else if (S_ISDIR(st.st_mode)) {
+                       strbuf_addch(&refname, '/');
+                       add_entry_to_dir(dir,
+                                        create_dir_entry(refs, refname.buf, 1));
+               } else {
                        if (*refs->name) {
                                hashclr(sha1);
                                flag = 0;
-                               if (resolve_gitlink_ref(refs->name, refname, sha1) < 0) {
+                               if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) {
                                        hashclr(sha1);
                                        flag |= REF_ISBROKEN;
                                }
-                       } else if (read_ref_full(refname, sha1, 1, &flag)) {
+                       } else if (read_ref_full(refname.buf, sha1, 1, &flag)) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
                        }
-                       add_ref(dir, create_ref_entry(refname, sha1, flag, 1));
+                       add_entry_to_dir(dir,
+                                        create_ref_entry(refname.buf, sha1, flag, 1));
                }
-               free(refname);
-               closedir(d);
+               strbuf_setlen(&refname, dirnamelen);
        }
+       strbuf_release(&refname);
+       closedir(d);
 }
 
 static struct ref_dir *get_loose_refs(struct ref_cache *refs)
 {
-       if (!refs->did_loose) {
-               get_ref_dir(refs, "refs", &refs->loose);
-               refs->did_loose = 1;
+       if (!refs->loose) {
+               /*
+                * Mark the top-level directory complete because we
+                * are about to read the only subdirectory that can
+                * hold references:
+                */
+               refs->loose = create_dir_entry(refs, "", 0);
+               /*
+                * Create an incomplete entry for "refs/":
+                */
+               add_entry_to_dir(get_ref_dir(refs->loose),
+                                create_dir_entry(refs, "refs/", 1));
        }
-       return &refs->loose;
+       return get_ref_dir(refs->loose);
 }
 
 /* We allow "recursive" symbolic refs. Only within reason, though */
@@ -1132,6 +1238,7 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
                return 0;
 
        fprintf(d->fp, d->msg_fmt, refname);
+       fputc('\n', d->fp);
        return 0;
 }
 
@@ -2229,57 +2336,59 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat
        return for_each_recent_reflog_ent(refname, fn, 0, cb_data);
 }
 
-static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
+/*
+ * Call fn for each reflog in the namespace indicated by name.  name
+ * must be empty or end with '/'.  Name will be used as a scratch
+ * space, but its contents will be restored before return.
+ */
+static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data)
 {
-       DIR *d = opendir(git_path("logs/%s", base));
+       DIR *d = opendir(git_path("logs/%s", name->buf));
        int retval = 0;
+       struct dirent *de;
+       int oldlen = name->len;
 
-       if (d) {
-               struct dirent *de;
-               int baselen = strlen(base);
-               char *log = xmalloc(baselen + 257);
+       if (!d)
+               return name->len ? errno : 0;
 
-               memcpy(log, base, baselen);
-               if (baselen && base[baselen-1] != '/')
-                       log[baselen++] = '/';
-
-               while ((de = readdir(d)) != NULL) {
-                       struct stat st;
-                       int namelen;
+       while ((de = readdir(d)) != NULL) {
+               struct stat st;
 
-                       if (de->d_name[0] == '.')
-                               continue;
-                       namelen = strlen(de->d_name);
-                       if (namelen > 255)
-                               continue;
-                       if (has_extension(de->d_name, ".lock"))
-                               continue;
-                       memcpy(log + baselen, de->d_name, namelen+1);
-                       if (stat(git_path("logs/%s", log), &st) < 0)
-                               continue;
+               if (de->d_name[0] == '.')
+                       continue;
+               if (has_extension(de->d_name, ".lock"))
+                       continue;
+               strbuf_addstr(name, de->d_name);
+               if (stat(git_path("logs/%s", name->buf), &st) < 0) {
+                       ; /* silently ignore */
+               } else {
                        if (S_ISDIR(st.st_mode)) {
-                               retval = do_for_each_reflog(log, fn, cb_data);
+                               strbuf_addch(name, '/');
+                               retval = do_for_each_reflog(name, fn, cb_data);
                        } else {
                                unsigned char sha1[20];
-                               if (read_ref_full(log, sha1, 0, NULL))
-                                       retval = error("bad ref for %s", log);
+                               if (read_ref_full(name->buf, sha1, 0, NULL))
+                                       retval = error("bad ref for %s", name->buf);
                                else
-                                       retval = fn(log, sha1, 0, cb_data);
+                                       retval = fn(name->buf, sha1, 0, cb_data);
                        }
                        if (retval)
                                break;
                }
-               free(log);
-               closedir(d);
+               strbuf_setlen(name, oldlen);
        }
-       else if (*base)
-               return errno;
+       closedir(d);
        return retval;
 }
 
 int for_each_reflog(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_reflog("", fn, cb_data);
+       int retval;
+       struct strbuf name;
+       strbuf_init(&name, PATH_MAX);
+       retval = do_for_each_reflog(&name, fn, cb_data);
+       strbuf_release(&name);
+       return retval;
 }
 
 int update_ref(const char *action, const char *refname,
index d159fe7f3433ccf6e8c8908961736951e42b9c35..04a9d6277db8fac78cb223a906b12744aab32193 100644 (file)
@@ -290,6 +290,7 @@ static void output_refs(struct ref *refs)
 struct rpc_state {
        const char *service_name;
        const char **argv;
+       struct strbuf *stdin_preamble;
        char *service_url;
        char *hdr_content_type;
        char *hdr_accept;
@@ -535,6 +536,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
 {
        const char *svc = rpc->service_name;
        struct strbuf buf = STRBUF_INIT;
+       struct strbuf *preamble = rpc->stdin_preamble;
        struct child_process client;
        int err = 0;
 
@@ -545,6 +547,8 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
        client.argv = rpc->argv;
        if (start_command(&client))
                exit(1);
+       if (preamble)
+               write_or_die(client.in, preamble->buf, preamble->len);
        if (heads)
                write_or_die(client.in, heads->buf, heads->len);
 
@@ -626,13 +630,14 @@ static int fetch_git(struct discovery *heads,
        int nr_heads, struct ref **to_fetch)
 {
        struct rpc_state rpc;
+       struct strbuf preamble = STRBUF_INIT;
        char *depth_arg = NULL;
-       const char **argv;
        int argc = 0, i, err;
+       const char *argv[15];
 
-       argv = xmalloc((15 + nr_heads) * sizeof(char*));
        argv[argc++] = "fetch-pack";
        argv[argc++] = "--stateless-rpc";
+       argv[argc++] = "--stdin";
        argv[argc++] = "--lock-pack";
        if (options.followtags)
                argv[argc++] = "--include-tag";
@@ -651,24 +656,27 @@ static int fetch_git(struct discovery *heads,
                argv[argc++] = depth_arg;
        }
        argv[argc++] = url;
+       argv[argc++] = NULL;
+
        for (i = 0; i < nr_heads; i++) {
                struct ref *ref = to_fetch[i];
                if (!ref->name || !*ref->name)
                        die("cannot fetch by sha1 over smart http");
-               argv[argc++] = ref->name;
+               packet_buf_write(&preamble, "%s\n", ref->name);
        }
-       argv[argc++] = NULL;
+       packet_buf_flush(&preamble);
 
        memset(&rpc, 0, sizeof(rpc));
        rpc.service_name = "git-upload-pack",
        rpc.argv = argv;
+       rpc.stdin_preamble = &preamble;
        rpc.gzip_request = 1;
 
        err = rpc_service(&rpc, heads);
        if (rpc.result.len)
                safe_write(1, rpc.result.buf, rpc.result.len);
        strbuf_release(&rpc.result);
-       free(argv);
+       strbuf_release(&preamble);
        free(depth_arg);
        return err;
 }
@@ -774,6 +782,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
                argv[argc++] = "--quiet";
        else if (options.verbosity > 1)
                argv[argc++] = "--verbose";
+       argv[argc++] = options.progress ? "--progress" : "--no-progress";
        argv[argc++] = url;
        for (i = 0; i < nr_spec; i++)
                argv[argc++] = specs[i];
index b3554ed11b5c1a987c1d22b7851c91641bbf7f56..935e7a7ba413668c95a6c3e846b9058be07f0425 100644 (file)
@@ -1715,17 +1715,21 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                submodule = opt->submodule;
 
        /* First, search for "--" */
-       seen_dashdash = 0;
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (strcmp(arg, "--"))
-                       continue;
-               argv[i] = NULL;
-               argc = i;
-               if (argv[i + 1])
-                       append_prune_data(&prune_data, argv + i + 1);
+       if (opt && opt->assume_dashdash) {
                seen_dashdash = 1;
-               break;
+       } else {
+               seen_dashdash = 0;
+               for (i = 1; i < argc; i++) {
+                       const char *arg = argv[i];
+                       if (strcmp(arg, "--"))
+                               continue;
+                       argv[i] = NULL;
+                       argc = i;
+                       if (argv[i + 1])
+                               append_prune_data(&prune_data, argv + i + 1);
+                       seen_dashdash = 1;
+                       break;
+               }
        }
 
        /* Second, deal with arguments and options */
@@ -2062,10 +2066,16 @@ static void set_children(struct rev_info *revs)
        }
 }
 
+void reset_revision_walk(void)
+{
+       clear_object_flags(SEEN | ADDED | SHOWN);
+}
+
 int prepare_revision_walk(struct rev_info *revs)
 {
        int nr = revs->pending.nr;
        struct object_array_entry *e, *list;
+       struct commit_list **next = &revs->commits;
 
        e = list = revs->pending.objects;
        revs->pending.nr = 0;
@@ -2076,11 +2086,12 @@ int prepare_revision_walk(struct rev_info *revs)
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
-                               commit_list_insert_by_date(commit, &revs->commits);
+                               next = commit_list_append(commit, next);
                        }
                }
                e++;
        }
+       commit_list_sort_by_date(&revs->commits);
        if (!revs->leak_pending)
                free(list);
 
index b8e9223954a5d66e01bd1eb342a936495aa67ad1..863f4f64543bd9f9e830e7c9567def23e906c7d9 100644 (file)
@@ -183,6 +183,7 @@ struct setup_revision_opt {
        const char *def;
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
        const char *submodule;
+       int assume_dashdash;
 };
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
@@ -192,6 +193,7 @@ extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ct
                                 const char * const usagestr[]);
 extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
 
+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);
index 1db8abf9843516576f30f8105bbfdd66487db6e1..606791dc674a1d24459d85504f0c981634b52020 100644 (file)
@@ -4,6 +4,10 @@
 #include "sigchain.h"
 #include "argv-array.h"
 
+#ifndef SHELL_PATH
+# define SHELL_PATH "/bin/sh"
+#endif
+
 struct child_to_clean {
        pid_t pid;
        struct child_to_clean *next;
@@ -76,6 +80,68 @@ static inline void dup_devnull(int to)
 }
 #endif
 
+static char *locate_in_PATH(const char *file)
+{
+       const char *p = getenv("PATH");
+       struct strbuf buf = STRBUF_INIT;
+
+       if (!p || !*p)
+               return NULL;
+
+       while (1) {
+               const char *end = strchrnul(p, ':');
+
+               strbuf_reset(&buf);
+
+               /* POSIX specifies an empty entry as the current directory. */
+               if (end != p) {
+                       strbuf_add(&buf, p, end - p);
+                       strbuf_addch(&buf, '/');
+               }
+               strbuf_addstr(&buf, file);
+
+               if (!access(buf.buf, F_OK))
+                       return strbuf_detach(&buf, NULL);
+
+               if (!*end)
+                       break;
+               p = end + 1;
+       }
+
+       strbuf_release(&buf);
+       return NULL;
+}
+
+static int exists_in_PATH(const char *file)
+{
+       char *r = locate_in_PATH(file);
+       free(r);
+       return r != NULL;
+}
+
+int sane_execvp(const char *file, char * const argv[])
+{
+       if (!execvp(file, argv))
+               return 0; /* cannot happen ;-) */
+
+       /*
+        * When a command can't be found because one of the directories
+        * listed in $PATH is unsearchable, execvp reports EACCES, but
+        * careful usability testing (read: analysis of occasional bug
+        * reports) reveals that "No such file or directory" is more
+        * intuitive.
+        *
+        * We avoid commands with "/", because execvp will not do $PATH
+        * lookups in that case.
+        *
+        * The reassignment of EACCES to errno looks like a no-op below,
+        * but we need to protect against exists_in_PATH overwriting errno.
+        */
+       if (errno == EACCES && !strchr(file, '/'))
+               errno = exists_in_PATH(file) ? EACCES : ENOENT;
+       return -1;
+}
+
 static const char **prepare_shell_cmd(const char **argv)
 {
        int argc, nargc = 0;
@@ -90,7 +156,11 @@ 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
+               nargv[nargc++] = SHELL_PATH;
+#else
                nargv[nargc++] = "sh";
+#endif
                nargv[nargc++] = "-c";
 
                if (argc < 2)
@@ -114,7 +184,7 @@ static int execv_shell_cmd(const char **argv)
 {
        const char **nargv = prepare_shell_cmd(argv);
        trace_argv_printf(nargv, "trace: exec:");
-       execvp(nargv[0], (char **)nargv);
+       sane_execvp(nargv[0], (char **)nargv);
        free(nargv);
        return -1;
 }
@@ -339,7 +409,7 @@ int start_command(struct child_process *cmd)
                } else if (cmd->use_shell) {
                        execv_shell_cmd(cmd->argv);
                } else {
-                       execvp(cmd->argv[0], (char *const*) cmd->argv);
+                       sane_execvp(cmd->argv[0], (char *const*) cmd->argv);
                }
                if (errno == ENOENT) {
                        if (!cmd->silent_exec_failure)
index a37846a594f5a2d6acfb075ece1b5c30dda2329f..3c384b94d24e3099cb149c66347386d1430fadef 100644 (file)
@@ -13,6 +13,7 @@
 #include "rerere.h"
 #include "merge-recursive.h"
 #include "refs.h"
+#include "argv-array.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -164,7 +165,7 @@ static void write_message(struct strbuf *msgbuf, const char *filename)
 
 static struct tree *empty_tree(void)
 {
-       return lookup_tree((const unsigned char *)EMPTY_TREE_SHA1_BIN);
+       return lookup_tree(EMPTY_TREE_SHA1_BIN);
 }
 
 static int error_dirty_index(struct replay_opts *opts)
@@ -234,7 +235,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 
        if (!clean) {
                int i;
-               strbuf_addstr(msgbuf, "\nConflicts:\n\n");
+               strbuf_addstr(msgbuf, "\nConflicts:\n");
                for (i = 0; i < active_nr;) {
                        struct cache_entry *ce = active_cache[i++];
                        if (ce_stage(ce)) {
@@ -251,6 +252,38 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        return !clean;
 }
 
+static int is_index_unchanged(void)
+{
+       unsigned char head_sha1[20];
+       struct commit *head_commit;
+
+       if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+               return error(_("Could not resolve HEAD commit\n"));
+
+       head_commit = lookup_commit(head_sha1);
+
+       /*
+        * If head_commit is NULL, check_commit, called from
+        * lookup_commit, would have indicated that head_commit is not
+        * a commit object already.  parse_commit() will return failure
+        * without further complaints in such a case.  Otherwise, if
+        * the commit is invalid, parse_commit() will complain.  So
+        * there is nothing for us to say here.  Just return failure.
+        */
+       if (parse_commit(head_commit))
+               return -1;
+
+       if (!active_cache_tree)
+               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))
+                       return error(_("Unable to update cache tree\n"));
+
+       return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
+}
+
 /*
  * If we are cherry-pick, and if the merge did not result in
  * hand-editing, we will hit this commit and inherit the original
@@ -260,21 +293,46 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
  */
 static int run_git_commit(const char *defmsg, struct replay_opts *opts)
 {
-       /* 6 is max possible length of our args array including NULL */
-       const char *args[6];
-       int i = 0;
+       struct argv_array array;
+       int rc;
+
+       argv_array_init(&array);
+       argv_array_push(&array, "commit");
+       argv_array_push(&array, "-n");
 
-       args[i++] = "commit";
-       args[i++] = "-n";
        if (opts->signoff)
-               args[i++] = "-s";
+               argv_array_push(&array, "-s");
        if (!opts->edit) {
-               args[i++] = "-F";
-               args[i++] = defmsg;
+               argv_array_push(&array, "-F");
+               argv_array_push(&array, defmsg);
+       }
+
+       if (opts->allow_empty)
+               argv_array_push(&array, "--allow-empty");
+
+       rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
+       argv_array_clear(&array);
+       return rc;
+}
+
+static int is_original_commit_empty(struct commit *commit)
+{
+       const unsigned char *ptree_sha1;
+
+       if (parse_commit(commit))
+               return error(_("Could not parse commit %s\n"),
+                            sha1_to_hex(commit->object.sha1));
+       if (commit->parents) {
+               struct commit *parent = commit->parents->item;
+               if (parse_commit(parent))
+                       return error(_("Could not parse parent commit %s\n"),
+                               sha1_to_hex(parent->object.sha1));
+               ptree_sha1 = parent->tree->object.sha1;
+       } else {
+               ptree_sha1 = EMPTY_TREE_SHA1_BIN; /* commit is root */
        }
-       args[i] = NULL;
 
-       return run_command_v_opt(args, RUN_GIT_CMD);
+       return !hashcmp(ptree_sha1, commit->tree->object.sha1);
 }
 
 static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
@@ -286,6 +344,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
        int res;
+       int empty_commit;
+       int index_unchanged;
 
        if (opts->no_commit) {
                /*
@@ -411,6 +471,10 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                free_commit_list(remotes);
        }
 
+       empty_commit = is_original_commit_empty(commit);
+       if (empty_commit < 0)
+               return empty_commit;
+
        /*
         * If the merge was clean or if it failed due to conflict, we write
         * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
@@ -431,6 +495,25 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                print_advice(res == 1, opts);
                rerere(opts->allow_rerere_auto);
        } else {
+               index_unchanged = is_index_unchanged();
+               /*
+                * If index_unchanged is less than 0, that indicates we either
+                * couldn't parse HEAD or the index, so error out here.
+                */
+               if (index_unchanged < 0)
+                       return index_unchanged;
+
+               if (!empty_commit && !opts->keep_redundant_commits && index_unchanged)
+                       /*
+                        * The head tree and the index match
+                        * meaning the commit is empty.  Since it wasn't created
+                        * empty (based on the previous test), we can conclude
+                        * the commit has been made redundant.  Since we don't
+                        * want to keep redundant commits, we can just return
+                        * here, skipping this commit
+                        */
+                       return 0;
+
                if (!opts->no_commit)
                        res = run_git_commit(defmsg, opts);
        }
@@ -468,33 +551,6 @@ static void read_and_refresh_cache(struct replay_opts *opts)
        rollback_lock_file(&index_lock);
 }
 
-/*
- * Append a commit to the end of the commit_list.
- *
- * next starts by pointing to the variable that holds the head of an
- * empty commit_list, and is updated to point to the "next" field of
- * the last item on the list as new commits are appended.
- *
- * Usage example:
- *
- *     struct commit_list *list;
- *     struct commit_list **next = &list;
- *
- *     next = commit_list_append(c1, next);
- *     next = commit_list_append(c2, next);
- *     assert(commit_list_count(list) == 2);
- *     return list;
- */
-static struct commit_list **commit_list_append(struct commit *commit,
-                                              struct commit_list **next)
-{
-       struct commit_list *new = xmalloc(sizeof(struct commit_list));
-       new->item = commit;
-       *next = new;
-       new->next = NULL;
-       return &new->next;
-}
-
 static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
                struct replay_opts *opts)
 {
index bb4b13830e157dafb468f8256bab25110058ba9b..aa5f17cc3079f39000e9d7526d24354a027daabd 100644 (file)
@@ -29,6 +29,8 @@ struct replay_opts {
        int signoff;
        int allow_ff;
        int allow_rerere_auto;
+       int allow_empty;
+       int keep_redundant_commits;
 
        int mainline;
 
diff --git a/setup.c b/setup.c
index 7a3618fab774c0c26a99c6e23b4fcf726e5dc1ad..731851a4a85161af49a38c481672615a6ac0bbc9 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -569,13 +569,15 @@ static const char *setup_nongit(const char *cwd, int *nongit_ok)
        return NULL;
 }
 
-static dev_t get_device_or_die(const char *path, const char *prefix)
+static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
 {
        struct stat buf;
-       if (stat(path, &buf))
-               die_errno("failed to stat '%s%s%s'",
+       if (stat(path, &buf)) {
+               die_errno("failed to stat '%*s%s%s'",
+                               prefix_len,
                                prefix ? prefix : "",
                                prefix ? "/" : "", path);
+       }
        return buf.st_dev;
 }
 
@@ -589,7 +591,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
        static char cwd[PATH_MAX+1];
        const char *gitdirenv, *ret;
        char *gitfile;
-       int len, offset, ceil_offset;
+       int len, offset, offset_parent, ceil_offset;
        dev_t current_device = 0;
        int one_filesystem = 1;
 
@@ -631,7 +633,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
         */
        one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
        if (one_filesystem)
-               current_device = get_device_or_die(".", NULL);
+               current_device = get_device_or_die(".", NULL, 0);
        for (;;) {
                gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
                if (gitfile)
@@ -653,11 +655,12 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
                if (is_git_directory("."))
                        return setup_bare_git_dir(cwd, offset, len, nongit_ok);
 
-               while (--offset > ceil_offset && cwd[offset] != '/');
-               if (offset <= ceil_offset)
+               offset_parent = offset;
+               while (--offset_parent > ceil_offset && cwd[offset_parent] != '/');
+               if (offset_parent <= ceil_offset)
                        return setup_nongit(cwd, nongit_ok);
                if (one_filesystem) {
-                       dev_t parent_device = get_device_or_die("..", cwd);
+                       dev_t parent_device = get_device_or_die("..", cwd, offset);
                        if (parent_device != current_device) {
                                if (nongit_ok) {
                                        if (chdir(cwd))
@@ -666,7 +669,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
                                        return NULL;
                                }
                                cwd[offset] = '\0';
-                               die("Not a git repository (or any parent up to mount parent %s)\n"
+                               die("Not a git repository (or any parent up to mount point %s)\n"
                                "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
                        }
                }
@@ -674,6 +677,7 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
                        cwd[offset] = '\0';
                        die_errno("Cannot change to '%s/..'", cwd);
                }
+               offset = offset_parent;
        }
 }
 
index 4f06a0e450359744528d3b125fb09eacebf1eb4a..4ccaf7ac197c28400eddd496abcfc725528ca32b 100644 (file)
@@ -19,6 +19,7 @@
 #include "pack-revindex.h"
 #include "sha1-lookup.h"
 #include "bulk-checkin.h"
+#include "streaming.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -228,7 +229,6 @@ char *sha1_pack_index_name(const unsigned char *sha1)
 struct alternate_object_database *alt_odb_list;
 static struct alternate_object_database **alt_odb_tail;
 
-static void read_info_alternates(const char * alternates, int depth);
 static int git_open_noatime(const char *name);
 
 /*
@@ -353,7 +353,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
        }
 }
 
-static void read_info_alternates(const char * relative_base, int depth)
+void read_info_alternates(const char * relative_base, int depth)
 {
        char *map;
        size_t mapsz;
@@ -1146,10 +1146,47 @@ static const struct packed_git *has_packed_and_bad(const unsigned char *sha1)
        return NULL;
 }
 
-int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
+/*
+ * With an in-core object data in "map", rehash it to make sure the
+ * object name actually matches "sha1" to detect object corruption.
+ * With "map" == NULL, try reading the object named with "sha1" using
+ * the streaming interface and rehash it to do the same.
+ */
+int check_sha1_signature(const unsigned char *sha1, void *map,
+                        unsigned long size, const char *type)
 {
        unsigned char real_sha1[20];
-       hash_sha1_file(map, size, type, real_sha1);
+       enum object_type obj_type;
+       struct git_istream *st;
+       git_SHA_CTX c;
+       char hdr[32];
+       int hdrlen;
+
+       if (map) {
+               hash_sha1_file(map, size, type, real_sha1);
+               return hashcmp(sha1, real_sha1) ? -1 : 0;
+       }
+
+       st = open_istream(sha1, &obj_type, &size, NULL);
+       if (!st)
+               return -1;
+
+       /* Generate the header */
+       hdrlen = sprintf(hdr, "%s %lu", typename(obj_type), size) + 1;
+
+       /* Sha1.. */
+       git_SHA1_Init(&c);
+       git_SHA1_Update(&c, hdr, hdrlen);
+       for (;;) {
+               char buf[1024 * 16];
+               ssize_t readlen = read_istream(st, buf, sizeof(buf));
+
+               if (!readlen)
+                       break;
+               git_SHA1_Update(&c, buf, readlen);
+       }
+       git_SHA1_Final(real_sha1, &c);
+       close_istream(st);
        return hashcmp(sha1, real_sha1) ? -1 : 0;
 }
 
@@ -2379,7 +2416,7 @@ int move_temp_to_file(const char *tmpfile, const char *filename)
        unlink_or_warn(tmpfile);
        if (ret) {
                if (ret != EEXIST) {
-                       return error("unable to write sha1 filename %s: %s\n", filename, strerror(ret));
+                       return error("unable to write sha1 filename %s: %s", filename, strerror(ret));
                }
                /* FIXME!!! Collision check here ? */
        }
@@ -2471,9 +2508,9 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
        fd = create_tmpfile(tmp_file, sizeof(tmp_file), filename);
        if (fd < 0) {
                if (errno == EACCES)
-                       return error("insufficient permission for adding an object to repository database %s\n", get_object_directory());
+                       return error("insufficient permission for adding an object to repository database %s", get_object_directory());
                else
-                       return error("unable to create temporary sha1 filename %s: %s\n", tmp_file, strerror(errno));
+                       return error("unable to create temporary file: %s", strerror(errno));
        }
 
        /* Set it up */
index 03ffc2caaa6524a3361bc47a89e101ced2f0e987..c6331136d19c5224078fa78b6e5e794fcc587fe2 100644 (file)
@@ -856,10 +856,22 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
        len = cp + tmp_len - name;
        cp = xstrndup(name, cp - name);
        upstream = branch_get(*cp ? cp : NULL);
-       if (!upstream
-           || !upstream->merge
-           || !upstream->merge[0]->dst)
-               return error("No upstream branch found for '%s'", cp);
+       /*
+        * Upstream can be NULL only if cp refers to HEAD and HEAD
+        * points to something different than a branch.
+        */
+       if (!upstream)
+               return error(_("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(
+                       _("Upstream branch '%s' not stored as a remote-tracking branch"),
+                       upstream->merge[0]->src);
+       }
        free(cp);
        cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
        strbuf_reset(buf);
index 5135d5950d9a5ea3ce8064e5491e53da17645da9..ec88266718879ebcf89ab95b4c89f83004467869 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -464,3 +464,36 @@ void strbuf_addstr_urlencode(struct strbuf *sb, const char *s,
 {
        strbuf_add_urlencode(sb, s, strlen(s), reserved);
 }
+
+void strbuf_addf_ln(struct strbuf *sb, const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       strbuf_vaddf(sb, fmt, ap);
+       va_end(ap);
+       strbuf_addch(sb, '\n');
+}
+
+int printf_ln(const char *fmt, ...)
+{
+       int ret;
+       va_list ap;
+       va_start(ap, fmt);
+       ret = vprintf(fmt, ap);
+       va_end(ap);
+       if (ret < 0 || putchar('\n') == EOF)
+               return -1;
+       return ret + 1;
+}
+
+int fprintf_ln(FILE *fp, const char *fmt, ...)
+{
+       int ret;
+       va_list ap;
+       va_start(ap, fmt);
+       ret = vfprintf(fp, fmt, ap);
+       va_end(ap);
+       if (ret < 0 || putc('\n', fp) == EOF)
+               return -1;
+       return ret + 1;
+}
index 3effaa86b68f7b600dfd854558b7ee6f5d6f2f5e..b888d405db492c9851696cf6efe66e33d5e1dca8 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -99,6 +99,8 @@ __attribute__((format (printf,2,3)))
 extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);
 __attribute__((format (printf,2,0)))
 extern void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap);
+__attribute__((format (printf,2,3)))
+extern void strbuf_addf_ln(struct strbuf *sb, const char *fmt, ...);
 
 extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
 
@@ -129,4 +131,9 @@ extern void strbuf_add_urlencode(struct strbuf *, const char *, size_t,
 extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
                                    int reserved);
 
+__attribute__((format (printf,1,2)))
+extern int printf_ln(const char *fmt, ...);
+__attribute__((format (printf,2,3)))
+extern int fprintf_ln(FILE *fp, const char *fmt, ...);
+
 #endif /* STRBUF_H */
index 71072e1b1da670cdb4b048a3a6e83a4ae806bf5f..3a3cd1206af5465323760e952a836935ac271d32 100644 (file)
@@ -99,7 +99,7 @@ int close_istream(struct git_istream *st)
        return r;
 }
 
-ssize_t read_istream(struct git_istream *st, char *buf, size_t sz)
+ssize_t read_istream(struct git_istream *st, void *buf, size_t sz)
 {
        return st->vtbl->read(st, buf, sz);
 }
@@ -489,3 +489,58 @@ static open_method_decl(incore)
 
        return st->u.incore.buf ? 0 : -1;
 }
+
+
+/****************************************************************
+ * Users of streaming interface
+ ****************************************************************/
+
+int stream_blob_to_fd(int fd, unsigned const char *sha1, struct stream_filter *filter,
+                     int can_seek)
+{
+       struct git_istream *st;
+       enum object_type type;
+       unsigned long sz;
+       ssize_t kept = 0;
+       int result = -1;
+
+       st = open_istream(sha1, &type, &sz, filter);
+       if (!st)
+               return result;
+       if (type != OBJ_BLOB)
+               goto close_and_exit;
+       for (;;) {
+               char buf[1024 * 16];
+               ssize_t wrote, holeto;
+               ssize_t readlen = read_istream(st, buf, sizeof(buf));
+
+               if (!readlen)
+                       break;
+               if (can_seek && sizeof(buf) == readlen) {
+                       for (holeto = 0; holeto < readlen; holeto++)
+                               if (buf[holeto])
+                                       break;
+                       if (readlen == holeto) {
+                               kept += holeto;
+                               continue;
+                       }
+               }
+
+               if (kept && lseek(fd, kept, SEEK_CUR) == (off_t) -1)
+                       goto close_and_exit;
+               else
+                       kept = 0;
+               wrote = write_in_full(fd, buf, readlen);
+
+               if (wrote != readlen)
+                       goto close_and_exit;
+       }
+       if (kept && (lseek(fd, kept - 1, SEEK_CUR) == (off_t) -1 ||
+                    write(fd, "", 1) != 1))
+               goto close_and_exit;
+       result = 0;
+
+ close_and_exit:
+       close_istream(st);
+       return result;
+}
index 589e857b8c4ad68e30b91da2eb29a076b98ef903..1d05c2a465c2c26f26b15a1a07cce6282ee5c0c3 100644 (file)
@@ -10,6 +10,8 @@ struct git_istream;
 
 extern struct git_istream *open_istream(const unsigned char *, enum object_type *, unsigned long *, struct stream_filter *);
 extern int close_istream(struct git_istream *);
-extern ssize_t read_istream(struct git_istream *, char *, size_t);
+extern ssize_t read_istream(struct git_istream *, void *, size_t);
+
+extern int stream_blob_to_fd(int fd, const unsigned char *, struct stream_filter *, int can_seek);
 
 #endif /* STREAMING_H */
index 9a2806067954c55a27c068706b5bfe67a1189fd5..959d349ea7426344289020ee4216785fa65ec1e6 100644 (file)
@@ -63,6 +63,9 @@ static int add_submodule_odb(const char *path)
        alt_odb->name[40] = '\0';
        alt_odb->name[41] = '\0';
        alt_odb_list = alt_odb;
+
+       /* add possible alternates from the submodule */
+       read_info_alternates(objects_directory.buf, 0);
        prepare_alt_odb();
 done:
        strbuf_release(&objects_directory);
@@ -357,21 +360,19 @@ static void collect_submodules_from_diff(struct diff_queue_struct *q,
                                         void *data)
 {
        int i;
-       int *needs_pushing = data;
+       struct string_list *needs_pushing = data;
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                if (!S_ISGITLINK(p->two->mode))
                        continue;
-               if (submodule_needs_pushing(p->two->path, p->two->sha1)) {
-                       *needs_pushing = 1;
-                       break;
-               }
+               if (submodule_needs_pushing(p->two->path, p->two->sha1))
+                       string_list_insert(needs_pushing, p->two->path);
        }
 }
 
-
-static void commit_need_pushing(struct commit *commit, int *needs_pushing)
+static void find_unpushed_submodule_commits(struct commit *commit,
+               struct string_list *needs_pushing)
 {
        struct rev_info rev;
 
@@ -382,14 +383,15 @@ static void commit_need_pushing(struct commit *commit, int *needs_pushing)
        diff_tree_combined_merge(commit, 1, &rev);
 }
 
-int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name)
+int find_unpushed_submodules(unsigned char new_sha1[20],
+               const char *remotes_name, struct string_list *needs_pushing)
 {
        struct rev_info rev;
        struct commit *commit;
        const char *argv[] = {NULL, NULL, "--not", "NULL", NULL};
        int argc = ARRAY_SIZE(argv) - 1;
        char *sha1_copy;
-       int needs_pushing = 0;
+
        struct strbuf remotes_arg = STRBUF_INIT;
 
        strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name);
@@ -401,13 +403,62 @@ int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remote
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
 
-       while ((commit = get_revision(&rev)) && !needs_pushing)
-               commit_need_pushing(commit, &needs_pushing);
+       while ((commit = get_revision(&rev)) != NULL)
+               find_unpushed_submodule_commits(commit, needs_pushing);
 
+       reset_revision_walk();
        free(sha1_copy);
        strbuf_release(&remotes_arg);
 
-       return needs_pushing;
+       return needs_pushing->nr;
+}
+
+static int push_submodule(const char *path)
+{
+       if (add_submodule_odb(path))
+               return 1;
+
+       if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
+               struct child_process cp;
+               const char *argv[] = {"push", NULL};
+
+               memset(&cp, 0, sizeof(cp));
+               cp.argv = argv;
+               cp.env = local_repo_env;
+               cp.git_cmd = 1;
+               cp.no_stdin = 1;
+               cp.dir = path;
+               if (run_command(&cp))
+                       return 0;
+               close(cp.out);
+       }
+
+       return 1;
+}
+
+int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
+{
+       int i, ret = 1;
+       struct string_list needs_pushing;
+
+       memset(&needs_pushing, 0, sizeof(struct string_list));
+       needs_pushing.strdup_strings = 1;
+
+       if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
+               return 1;
+
+       for (i = 0; i < needs_pushing.nr; i++) {
+               const char *path = needs_pushing.items[i].string;
+               fprintf(stderr, "Pushing submodule '%s'\n", path);
+               if (!push_submodule(path)) {
+                       fprintf(stderr, "Unable to push submodule '%s'\n", path);
+                       ret = 0;
+               }
+       }
+
+       string_list_clear(&needs_pushing, 0);
+
+       return ret;
 }
 
 static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
@@ -741,6 +792,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                if (in_merge_bases(b, &commit, 1))
                        add_object_array(o, NULL, &merges);
        }
+       reset_revision_walk();
 
        /* Now we've got all merges that contain a and b. Prune all
         * merges that contain another found merge and save them in
index 80e04f3c8cfe9a49865ef54f61efaa3bc67dca5c..e105b0ebe6c06a03af7f82bdfbc9beb66377544f 100644 (file)
@@ -13,7 +13,7 @@ enum {
 void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                const char *path);
 int submodule_config(const char *var, const char *value, void *cb);
-void gitmodules_config();
+void gitmodules_config(void);
 int parse_submodule_config_option(const char *var, const char *value);
 void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
 int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
@@ -29,6 +29,8 @@ int fetch_populated_submodules(int num_options, const char **options,
 unsigned is_submodule_modified(const char *path, int ignore_untracked);
 int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
                    const unsigned char a[20], const unsigned char b[20], int search);
-int check_submodule_needs_pushing(unsigned char new_sha1[20], const char *remotes_name);
+int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
+               struct string_list *needs_pushing);
+int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
 
 #endif
index 21d11d6c2d65982d94f933b2a673b744cbc2e28a..ae2dc4604f708d320d88d4f99ca9d575d88b6124 100644 (file)
@@ -69,7 +69,7 @@ gitweb_run () {
        # written to web server logs, so we are not interested in that:
        # we are interested only in properly formatted errors/warnings
        rm -f gitweb.log &&
-       perl -- "$SCRIPT_NAME" \
+       "$PERL_PATH" -- "$SCRIPT_NAME" \
                >gitweb.output 2>gitweb.log &&
        perl -w -e '
                open O, ">gitweb.headers";
index ef2d01f36939199f9720faad06d2b3a01c7cd7f5..87f0ad8f4182b13903b9649308e36e34a6122494 100644 (file)
@@ -31,19 +31,19 @@ start_git_daemon() {
                >&3 2>git_daemon_output &
        GIT_DAEMON_PID=$!
        {
-               read line
+               read line <&7
                echo >&4 "$line"
-               cat >&4 &
+               cat <&7 >&4 &
+       } 7<git_daemon_output &&
 
-               # Check expected output
-               if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble"
-               then
-                       kill "$GIT_DAEMON_PID"
-                       wait "$GIT_DAEMON_PID"
-                       trap 'die' EXIT
-                       error "git daemon failed to start"
-               fi
-       } <git_daemon_output
+       # Check expected output
+       if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble"
+       then
+               kill "$GIT_DAEMON_PID"
+               wait "$GIT_DAEMON_PID"
+               trap 'die' EXIT
+               error "git daemon failed to start"
+       fi
 }
 
 stop_git_daemon() {
index a870f9a5d280e3809267b5970f8b9372b5d260b1..121e38002bc73807649ebcc5df2799f6f35a9c67 100644 (file)
@@ -1,20 +1,18 @@
 #
-# Library code for git-p4 tests
+# Library code for git p4 tests
 #
 
 . ./test-lib.sh
 
 if ! test_have_prereq PYTHON; then
-       skip_all='skipping git-p4 tests; python not available'
+       skip_all='skipping git p4 tests; python not available'
        test_done
 fi
 ( p4 -h && p4d -h ) >/dev/null 2>&1 || {
-       skip_all='skipping git-p4 tests; no p4 or p4d'
+       skip_all='skipping git p4 tests; no p4 or p4d'
        test_done
 }
 
-GITP4="$GIT_BUILD_DIR/contrib/fast-import/git-p4"
-
 # Try to pick a unique port: guess a large number, then hope
 # no more than one of each test is running.
 #
@@ -26,6 +24,7 @@ P4DPORT=$((10669 + ($testid - $git_p4_test_start)))
 
 export P4PORT=localhost:$P4DPORT
 export P4CLIENT=client
+export P4EDITOR=:
 
 db="$TRASH_DIRECTORY/db"
 cli="$TRASH_DIRECTORY/cli"
index f7dc0781d5d0ec5afd7f1d0898bffa17a9b1b00e..094d49089389c9e8dbf8c561ccc133065552fc81 100644 (file)
@@ -160,6 +160,6 @@ test_http_push_nonff() {
        '
 
        test_expect_success 'non-fast-forward push shows help message' '
-               test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" output
+               test_i18ngrep "Updates were rejected because" output
        '
 }
index 3c12b05d60849b4f3063527338140c717b720c5d..de3762e24762692733f7a42f58b139e279b29f3d 100644 (file)
@@ -52,8 +52,15 @@ Alias /auth/ www/auth/
 <Location /smart_noexport/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
 </Location>
+<Location /smart_custom_env/>
+       SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+       SetEnv GIT_HTTP_EXPORT_ALL
+       SetEnv GIT_COMMITTER_NAME "Custom User"
+       SetEnv GIT_COMMITTER_EMAIL custom@example.com
+</Location>
 ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/
 ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/
+ScriptAlias /smart_custom_env/ ${GIT_EXEC_PATH}/git-http-backend/
 <Directory ${GIT_EXEC_PATH}>
        Options None
 </Directory>
diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh
new file mode 100755 (executable)
index 0000000..6cb5b0d
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description="Tests index-pack performance"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+
+test_expect_success 'repack' '
+       git repack -ad &&
+       PACK=`ls .git/objects/pack/*.pack | head -n1` &&
+       test -f "$PACK" &&
+       export PACK
+'
+
+test_perf 'index-pack 0 threads' '
+       GIT_DIR=t1 git index-pack --threads=1 --stdin < $PACK
+'
+
+test_perf 'index-pack 1 thread ' '
+       GIT_DIR=t2 GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK
+'
+
+test_perf 'index-pack 2 threads' '
+       GIT_DIR=t3 git index-pack --threads=2 --stdin < $PACK
+'
+
+test_perf 'index-pack 4 threads' '
+       GIT_DIR=t4 git index-pack --threads=4 --stdin < $PACK
+'
+
+test_perf 'index-pack 8 threads' '
+       GIT_DIR=t5 git index-pack --threads=8 --stdin < $PACK
+'
+
+test_perf 'index-pack default number of threads' '
+       GIT_DIR=t6 git index-pack --stdin < $PACK
+'
+
+test_done
index aaed7254023b86a30c499db7c4b069c9d08b1085..f9bbb91f64e35d284a4ac79a6b2ecaa1d2913455 100755 (executable)
@@ -11,7 +11,7 @@ Date: Thu, 23 Aug 2007 13:00:00 +0200
 Subject: test1
 
 ---
- foo |    1 +
+ foo | 1 +
  1 files changed, 1 insertions(+), 0 deletions(-)
  create mode 100644 foo
 
index 8d4938f019ca406c0f248d19d046356dff1ac09a..17e969df609f71b0b4562cff8fda112632d27442 100755 (executable)
@@ -34,4 +34,17 @@ test_expect_success POSIXPERM 'run_command reports EACCES' '
        grep "fatal: cannot exec.*hello.sh" err
 '
 
+test_expect_success POSIXPERM 'unreadable directory in PATH' '
+       mkdir local-command &&
+       test_when_finished "chmod u+rwx local-command && rm -fr local-command" &&
+       git config alias.nitfol "!echo frotz" &&
+       chmod a-rx local-command &&
+       (
+               PATH=./local-command:$PATH &&
+               git nitfol >actual
+       ) &&
+       echo frotz >expect &&
+       test_cmp expect actual
+'
+
 test_done
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
new file mode 100755 (executable)
index 0000000..3d98eb8
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Heiko Voigt
+#
+
+test_description='Test revision walking api'
+
+. ./test-lib.sh
+
+cat >run_twice_expected <<-EOF
+1st
+ > add b
+ > add a
+2nd
+ > add b
+ > add a
+EOF
+
+test_expect_success 'setup' '
+       echo a > a &&
+       git add a &&
+       git commit -m "add a" &&
+       echo b > b &&
+       git add b &&
+       git commit -m "add b"
+'
+
+test_expect_success 'revision walking can be done twice' '
+       test-revision-walking run-twice > run_twice_actual
+       test_cmp run_twice_expected run_twice_actual
+'
+
+test_done
index 267f4c8ba3284d30492d8907b6fc3230f40e02fc..f028fd1418285107a90e170a6ea1cd7657e31bb8 100755 (executable)
@@ -1,39 +1,60 @@
 #!/bin/sh
 
-test_description='external credential helper tests'
-. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-credential.sh
+test_description='external credential helper tests
 
-pre_test() {
-       test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
-       eval "$GIT_TEST_CREDENTIAL_HELPER_SETUP"
+This is a tool for authors of external helper tools to sanity-check
+their helpers. If you have written the "git-credential-foo" helper,
+you check it with:
+
+  make GIT_TEST_CREDENTIAL_HELPER=foo t0303-credential-external.sh
+
+This assumes that your helper is capable of both storing and
+retrieving credentials (some helpers may be read-only, and they will
+fail these tests).
+
+Please note that the individual tests do not verify all of the
+preconditions themselves, but rather build on each other. A failing
+test means that tests later in the sequence can return false "OK"
+results.
+
+If your helper supports time-based expiration with a configurable
+timeout, you can test that feature with:
+
+  make GIT_TEST_CREDENTIAL_HELPER=foo \
+       GIT_TEST_CREDENTIAL_HELPER_TIMEOUT="foo --timeout=1" \
+       t0303-credential-external.sh
 
-       # clean before the test in case there is cruft left
-       # over from a previous run that would impact results
-       helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
-}
+If your helper requires additional setup before the tests are started,
+you can set GIT_TEST_CREDENTIAL_HELPER_SETUP to a sequence of shell
+commands.
+'
 
-post_test() {
-       # clean afterwards so that we are good citizens
-       # and don't leave cruft in the helper's storage, which
-       # might be long-term system storage
-       helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
-}
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-credential.sh
 
 if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then
-       say "# skipping external helper tests (set GIT_TEST_CREDENTIAL_HELPER)"
-else
-       pre_test
-       helper_test "$GIT_TEST_CREDENTIAL_HELPER"
-       post_test
+       skip_all="used to test external credential helpers"
+       test_done
 fi
 
+test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" ||
+       eval "$GIT_TEST_CREDENTIAL_HELPER_SETUP"
+
+# clean before the test in case there is cruft left
+# over from a previous run that would impact results
+helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+
+helper_test "$GIT_TEST_CREDENTIAL_HELPER"
+
 if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then
-       say "# skipping external helper timeout tests"
+       say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)"
 else
-       pre_test
        helper_test_timeout "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"
-       post_test
 fi
 
+# clean afterwards so that we are good citizens
+# and don't leave cruft in the helper's storage, which
+# might be long-term system storage
+helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER"
+
 test_done
index 29d6024b7f1b55c09cbd7e9ed682a3e745c550d6..55ed955ceffee9184b5822054697f58e7d0ef6a4 100755 (executable)
@@ -6,11 +6,15 @@ test_description='adding and checking out large blobs'
 . ./test-lib.sh
 
 test_expect_success setup '
-       git config core.bigfilethreshold 200k &&
+       # clone does not allow us to pass core.bigfilethreshold to
+       # new repos, so set core.bigfilethreshold globally
+       git config --global core.bigfilethreshold 200k &&
        echo X | dd of=large1 bs=1k seek=2000 &&
        echo X | dd of=large2 bs=1k seek=2000 &&
        echo X | dd of=large3 bs=1k seek=2000 &&
-       echo Y | dd of=huge bs=1k seek=2500
+       echo Y | dd of=huge bs=1k seek=2500 &&
+       GIT_ALLOC_LIMIT=1500 &&
+       export GIT_ALLOC_LIMIT
 '
 
 test_expect_success 'add a large file or two' '
@@ -100,4 +104,46 @@ test_expect_success 'packsize limit' '
        )
 '
 
+test_expect_success 'diff --raw' '
+       git commit -q -m initial &&
+       echo modified >>large1 &&
+       git add large1 &&
+       git commit -q -m modified &&
+       git diff --raw HEAD^
+'
+
+test_expect_success 'hash-object' '
+       git hash-object large1
+'
+
+test_expect_success 'cat-file a large file' '
+       git cat-file blob :large1 >/dev/null
+'
+
+test_expect_success 'cat-file a large file from a tag' '
+       git tag -m largefile largefiletag :large1 &&
+       git cat-file blob largefiletag >/dev/null
+'
+
+test_expect_success 'git-show a large file' '
+       git show :large1 >/dev/null
+
+'
+
+test_expect_success 'repack' '
+       git repack -ad
+'
+
+test_expect_success 'tar achiving' '
+       git archive --format=tar HEAD >/dev/null
+'
+
+test_expect_success 'zip achiving, store only' '
+       git archive --format=zip -0 HEAD >/dev/null
+'
+
+test_expect_success 'zip achiving, deflate' '
+       git archive --format=zip HEAD >/dev/null
+'
+
 test_done
index 9356beaf4be45c32d10209c5063b7a9c5aa06d42..397ccb690908dad2d45bab3d249f9c62f2dc91d1 100755 (executable)
@@ -154,8 +154,8 @@ test_expect_success 'git show-branch' '
 cat > resolve.expect << EOF
 Updating VARIABLE..VARIABLE
 FASTFORWARD (no commit created; -m option ignored)
- example |    1 +
- hello   |    1 +
+ example | 1 +
+ hello   | 1 +
  2 files changed, 2 insertions(+)
 EOF
 
index 36e227b3bb25cb17dabc5e205e63056e7fd0b370..a477453e2ef8ffedafb2dd05e00f507cd112bc97 100755 (executable)
@@ -550,6 +550,14 @@ EOF
 
 test_expect_success "rename succeeded" "test_cmp expect .git/config"
 
+test_expect_success 'renaming empty section name is rejected' '
+       test_must_fail git config --rename-section branch.zwei ""
+'
+
+test_expect_success 'renaming to bogus section is rejected' '
+       test_must_fail git config --rename-section branch.zwei "bogus name"
+'
+
 cat >> .git/config << EOF
   [branch "zwei"] a = 1 [branch "vier"]
 EOF
index 4b1cbaa0284f9b9cc3e29bf5f07b1a6a1f3a283c..a70707620f146d3fce69f77e08cae3a47253f157 100755 (executable)
@@ -29,6 +29,14 @@ test_expect_success 'chained relative paths' '
        test_cmp expect actual
 '
 
+test_expect_success 'include paths get tilde-expansion' '
+       echo "[test]one = 1" >one &&
+       echo "[include]path = ~/one" >.gitconfig &&
+       echo 1 >expect &&
+       git config test.one >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'include options can still be examined' '
        echo "[test]one = 1" >one &&
        echo "[include]path = one" >.gitconfig &&
index 252fc828374583cfb4c2346853bb87560efdf01d..236b13a3ab27f54808fa88574738747e3c3936b1 100755 (executable)
@@ -100,8 +100,7 @@ test_expect_success setup '
 
        check_fsck &&
 
-       loglen=$(wc -l <.git/logs/refs/heads/master) &&
-       test $loglen = 4
+       test_line_count = 4 .git/logs/refs/heads/master
 '
 
 test_expect_success rewind '
@@ -117,8 +116,7 @@ test_expect_success rewind '
 
        check_have A B C D E F G H I J K L &&
 
-       loglen=$(wc -l <.git/logs/refs/heads/master) &&
-       test $loglen = 5
+       test_line_count = 5 .git/logs/refs/heads/master
 '
 
 test_expect_success 'corrupt and check' '
@@ -136,8 +134,7 @@ test_expect_success 'reflog expire --dry-run should not touch reflog' '
                --stale-fix \
                --all &&
 
-       loglen=$(wc -l <.git/logs/refs/heads/master) &&
-       test $loglen = 5 &&
+       test_line_count = 5 .git/logs/refs/heads/master &&
 
        check_fsck "missing blob $F"
 '
@@ -150,8 +147,7 @@ test_expect_success 'reflog expire' '
                --stale-fix \
                --all &&
 
-       loglen=$(wc -l <.git/logs/refs/heads/master) &&
-       test $loglen = 2 &&
+       test_line_count = 2 .git/logs/refs/heads/master &&
 
        check_fsck "dangling commit $K"
 '
@@ -217,9 +213,7 @@ test_expect_success 'delete' '
 test_expect_success 'rewind2' '
 
        test_tick && git reset --hard HEAD~2 &&
-       loglen=$(wc -l <.git/logs/refs/heads/master) &&
-       test $loglen = 4
-
+       test_line_count = 4 .git/logs/refs/heads/master
 '
 
 test_expect_success '--expire=never' '
@@ -228,9 +222,7 @@ test_expect_success '--expire=never' '
                --expire=never \
                --expire-unreachable=never \
                --all &&
-       loglen=$(wc -l <.git/logs/refs/heads/master) &&
-       test $loglen = 4
-
+       test_line_count = 4 .git/logs/refs/heads/master
 '
 
 test_expect_success 'gc.reflogexpire=never' '
@@ -238,8 +230,7 @@ test_expect_success 'gc.reflogexpire=never' '
        git config gc.reflogexpire never &&
        git config gc.reflogexpireunreachable never &&
        git reflog expire --verbose --all &&
-       loglen=$(wc -l <.git/logs/refs/heads/master) &&
-       test $loglen = 4
+       test_line_count = 4 .git/logs/refs/heads/master
 '
 
 test_expect_success 'gc.reflogexpire=false' '
@@ -247,8 +238,7 @@ test_expect_success 'gc.reflogexpire=false' '
        git config gc.reflogexpire false &&
        git config gc.reflogexpireunreachable false &&
        git reflog expire --verbose --all &&
-       loglen=$(wc -l <.git/logs/refs/heads/master) &&
-       test $loglen = 4 &&
+       test_line_count = 4 .git/logs/refs/heads/master &&
 
        git config --unset gc.reflogexpire &&
        git config --unset gc.reflogexpireunreachable
index caa687b5b46cea65ed16c70c29cc11e9e8b771f1..9a105fe21f0da2851fc0f2aae4ff10e2417c9842 100755 (executable)
@@ -65,20 +65,73 @@ test_expect_success 'using @{now} syntax shows reflog date (oneline)' '
 '
 
 cat >expect <<'EOF'
-Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>)
+HEAD@{Thu Apr 7 15:13:13 2005 -0700}
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (format=%gd)' '
+       git log -g -1 --format=%gd HEAD@{now} >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>)
 Reflog message: commit (initial): one
 EOF
 test_expect_success 'using --date= shows reflog date (multiline)' '
-       git log -g -1 --date=raw >tmp &&
+       git log -g -1 --date=default >tmp &&
        grep ^Reflog <tmp >actual &&
        test_cmp expect actual
 '
 
 cat >expect <<'EOF'
-e46513e HEAD@{1112911993 -0700}: commit (initial): one
+e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one
 EOF
 test_expect_success 'using --date= shows reflog date (oneline)' '
-       git log -g -1 --oneline --date=raw >actual &&
+       git log -g -1 --oneline --date=default >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{1112911993 -0700}
+EOF
+test_expect_success 'using --date= shows reflog date (format=%gd)' '
+       git log -g -1 --format=%gd --date=raw >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (multiline)' '
+       test_config log.date raw &&
+       git log -g -1 >tmp &&
+       grep ^Reflog <tmp >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{0}: commit (initial): one
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (oneline)' '
+       test_config log.date raw &&
+       git log -g -1 --oneline >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{0}
+EOF
+test_expect_success 'log.date does not invoke "--date" magic (format=%gd)' '
+       test_config log.date raw &&
+       git log -g -1 --format=%gd >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+HEAD@{0}
+EOF
+test_expect_success '--date magic does not override explicit @{0} syntax' '
+       git log -g -1 --format=%gd --date=raw HEAD@{0} >actual &&
        test_cmp expect actual
 '
 
index e661147c573fa7312e8f533a4a4c8ea1eda7763f..8f36aa9fc4d2bdd8b590e3241eccf88609db4ee2 100755 (executable)
@@ -68,7 +68,7 @@ test_expect_success 'inside work tree' '
        )
 '
 
-test_expect_failure 'empty prefix is actually written out' '
+test_expect_success 'empty prefix is actually written out' '
        echo >expected &&
        (
                cd work &&
index a4555510c37276d36387d4c5c818503bb286dbd1..d6e576192fcd014ed5f570ceeab1152b93d91d1f 100755 (executable)
@@ -15,10 +15,18 @@ test_expect_success 'setup' '
        test_commit 3 &&
        (cd clone &&
         test_commit 4 &&
-        git branch --track my-side origin/side)
-
+        git branch --track my-side origin/side &&
+        git branch --track local-master master &&
+        git remote add -t master master-only .. &&
+        git fetch master-only &&
+        git branch bad-upstream &&
+        git config branch.bad-upstream.remote master-only &&
+        git config branch.bad-upstream.merge refs/heads/side
+       )
 '
 
+sq="'"
+
 full_name () {
        (cd clone &&
         git rev-parse --symbolic-full-name "$@")
@@ -29,6 +37,11 @@ commit_subject () {
         git show -s --pretty=format:%s "$@")
 }
 
+error_message () {
+       (cd clone &&
+        test_must_fail git rev-parse --verify "$@")
+}
+
 test_expect_success '@{upstream} resolves to correct full name' '
        test refs/remotes/origin/master = "$(full_name @{upstream})"
 '
@@ -78,7 +91,6 @@ test_expect_success 'checkout -b new my-side@{u} forks from the same' '
 
 test_expect_success 'merge my-side@{u} records the correct name' '
 (
-       sq="'\''" &&
        cd clone || exit
        git checkout master || exit
        git branch -D new ;# can fail but is ok
@@ -107,6 +119,69 @@ test_expect_success 'checkout other@{u}' '
        test_cmp expect actual
 '
 
+test_expect_success 'branch@{u} works when tracking a local branch' '
+       test refs/heads/master = "$(full_name local-master@{u})"
+'
+
+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
+       EOF
+       error_message non-tracking@{u} 2>actual &&
+       test_i18ncmp expect actual
+'
+
+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
+       EOF
+       test_must_fail git rev-parse --verify @{u} 2>actual &&
+       test_i18ncmp expect actual
+'
+
+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
+       EOF
+       error_message no-such-branch@{u} 2>actual &&
+       test_i18ncmp expect actual
+'
+
+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
+       EOF
+       git checkout HEAD^0 &&
+       test_must_fail git rev-parse --verify @{u} 2>actual &&
+       test_i18ncmp expect actual
+'
+
+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
+       EOF
+       error_message bad-upstream@{u} 2>actual &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'pull works when tracking a local branch' '
+(
+       cd clone &&
+       git checkout local-master &&
+       git pull
+)
+'
+
+# makes sense if the previous one succeeded
+test_expect_success '@{u} works when tracking a local branch' '
+       test refs/heads/master = "$(full_name @{u})"
+'
+
 cat >expect <<EOF
 commit 8f489d01d0cc65c3b0f09504ec50b5ed02a70bd5
 Reflog: master@{0} (C O Mitter <committer@example.com>)
index 36cca14d957f85733174d6ce514e22acfff3b1c9..0f4b2896af8b73edcd5bd60631405a430803a40f 100755 (executable)
@@ -40,7 +40,7 @@ test_expect_success \
 rm -f path* .merge_* out .git/index &&
 git read-tree $t1 &&
 git checkout-index --temp -- path1 >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d " -f2 out) = path1 &&
 p=$(cut "-d    " -f1 out) &&
 test -f $p &&
@@ -51,7 +51,7 @@ test_expect_success \
 rm -f path* .merge_* out .git/index &&
 git read-tree $t1 &&
 git checkout-index -a --temp >out &&
-test $(wc -l <out) = 5 &&
+test_line_count = 5 out &&
 for f in path0 path1 path3 path4 asubdir/path5
 do
        test $(grep $f out | cut "-d    " -f2) = $f &&
@@ -69,7 +69,7 @@ test_expect_success \
 'checkout one stage 2 to temporary file' '
 rm -f path* .merge_* out &&
 git checkout-index --stage=2 --temp -- path1 >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d " -f2 out) = path1 &&
 p=$(cut "-d    " -f1 out) &&
 test -f $p &&
@@ -79,7 +79,7 @@ test_expect_success \
 'checkout all stage 2 to temporary files' '
 rm -f path* .merge_* out &&
 git checkout-index --all --stage=2 --temp >out &&
-test $(wc -l <out) = 3 &&
+test_line_count = 3 out &&
 for f in path1 path2 path4
 do
        test $(grep $f out | cut "-d    " -f2) = $f &&
@@ -92,13 +92,13 @@ test_expect_success \
 'checkout all stages/one file to nothing' '
 rm -f path* .merge_* out &&
 git checkout-index --stage=all --temp -- path0 >out &&
-test $(wc -l <out) = 0'
+test_line_count = 0 out'
 
 test_expect_success \
 'checkout all stages/one file to temporary files' '
 rm -f path* .merge_* out &&
 git checkout-index --stage=all --temp -- path1 >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d " -f2 out) = path1 &&
 cut "-d        " -f1 out | (read s1 s2 s3 &&
 test -f $s1 &&
@@ -112,7 +112,7 @@ test_expect_success \
 'checkout some stages/one file to temporary files' '
 rm -f path* .merge_* out &&
 git checkout-index --stage=all --temp -- path2 >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d " -f2 out) = path2 &&
 cut "-d        " -f1 out | (read s1 s2 s3 &&
 test $s1 = . &&
@@ -125,7 +125,7 @@ test_expect_success \
 'checkout all stages/all files to temporary files' '
 rm -f path* .merge_* out &&
 git checkout-index -a --stage=all --temp >out &&
-test $(wc -l <out) = 5'
+test_line_count = 5 out'
 
 test_expect_success \
 '-- path0: no entry' '
@@ -185,7 +185,7 @@ test_expect_success \
 'checkout --temp within subdir' '
 (cd asubdir &&
  git checkout-index -a --stage=all >out &&
- test $(wc -l <out) = 1 &&
+ test_line_count = 1 out &&
  test $(grep path5 out | cut "-d       " -f2) = path5 &&
  grep path5 out | cut "-d      " -f1 | (read s1 s2 s3 &&
  test -f ../$s1 &&
@@ -203,7 +203,7 @@ t4=$(git write-tree) &&
 rm -f .git/index &&
 git read-tree $t4 &&
 git checkout-index --temp -a >out &&
-test $(wc -l <out) = 1 &&
+test_line_count = 1 out &&
 test $(cut "-d " -f2 out) = a &&
 p=$(cut "-d    " -f1 out) &&
 test -f $p &&
index 6352b74e2e54e5e08941d8d5d90ac30b202b56c1..37bdcedcc952083ba336cb9eaca5c67424d2cbb6 100755 (executable)
@@ -46,4 +46,15 @@ test_expect_success 'checking out another branch from unborn state' '
        test_cmp expect actual
 '
 
+test_expect_success 'checking out in a newly created repo' '
+       test_create_repo empty &&
+       (
+               cd empty &&
+               git symbolic-ref HEAD >expect &&
+               test_must_fail git checkout &&
+               git symbolic-ref HEAD >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index 068fba4c8e8706aa615d0401da1da47901113156..81005373d728f09e1a9289048cc721a0300f1cf9 100755 (executable)
@@ -11,14 +11,13 @@ check_not_detached () {
        git symbolic-ref -q HEAD >/dev/null
 }
 
-ORPHAN_WARNING='you are leaving .* commit.*behind'
 PREV_HEAD_DESC='Previous HEAD position was'
 check_orphan_warning() {
-       test_i18ngrep "$ORPHAN_WARNING" "$1" &&
+       test_i18ngrep "you are leaving $2 behind" "$1" &&
        test_i18ngrep ! "$PREV_HEAD_DESC" "$1"
 }
 check_no_orphan_warning() {
-       test_i18ngrep ! "$ORPHAN_WARNING" "$1" &&
+       test_i18ngrep ! "you are leaving .* commit.*behind" "$1" &&
        test_i18ngrep "$PREV_HEAD_DESC" "$1"
 }
 
@@ -110,12 +109,24 @@ test_expect_success 'checkout warns on orphan commits' '
        git checkout --detach two &&
        echo content >orphan &&
        git add orphan &&
-       git commit -a -m orphan &&
+       git commit -a -m orphan1 &&
+       echo new content >orphan &&
+       git commit -a -m orphan2 &&
+       orphan2=$(git rev-parse HEAD) &&
        git checkout master 2>stderr
 '
 
 test_expect_success 'checkout warns on orphan commits: output' '
-       check_orphan_warning stderr
+       check_orphan_warning stderr "2 commits"
+'
+
+test_expect_success 'checkout warns orphaning 1 of 2 commits' '
+       git checkout "$orphan2" &&
+       git checkout HEAD^ 2>stderr
+'
+
+test_expect_success 'checkout warns orphaning 1 of 2 commits: output' '
+       check_orphan_warning stderr "1 commit"
 '
 
 test_expect_success 'checkout does not warn leaving ref tip' '
@@ -148,7 +159,7 @@ test_expect_success 'tracking count is accurate after orphan check' '
        git config branch.child.merge refs/heads/master &&
        git checkout child^ &&
        git checkout child >stdout &&
-       test_cmp expect stdout
+       test_i18ncmp expect stdout
 '
 
 test_done
index cb7effe0a3e38eeba92b43682de9be68e677099e..f2620650ce1d25252210c07db20e54f99bd515c6 100755 (executable)
@@ -113,7 +113,7 @@ test_expect_success 'unmerge with plumbing' '
        prime_resolve_undo &&
        git update-index --unresolve fi/le &&
        git ls-files -u >actual &&
-       test $(wc -l <actual) = 3
+       test_line_count = 3 actual
 '
 
 test_expect_success 'rerere and rerere forget' '
index 9fe1d8feab419e1a8065b2ea5881f991edc68855..a17f8b2a407c2de2bf81d8a858957da409f1c980 100755 (executable)
@@ -160,6 +160,83 @@ test_expect_success 'git branch --list -d t should fail' '
        test_path_is_missing .git/refs/heads/t
 '
 
+test_expect_success 'git branch --column' '
+       COLUMNS=81 git branch --column=column >actual &&
+       cat >expected <<\EOF &&
+  a/b/c     bam       foo       l       * master    n         o/p       r
+  abc       bar       j/k       m/m       master2   o/o       q
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'git branch --column with an extremely long branch name' '
+       long=this/is/a/part/of/long/branch/name &&
+       long=z$long/$long/$long/$long &&
+       test_when_finished "git branch -d $long" &&
+       git branch $long &&
+       COLUMNS=80 git branch --column=column >actual &&
+       cat >expected <<EOF &&
+  a/b/c
+  abc
+  bam
+  bar
+  foo
+  j/k
+  l
+  m/m
+* master
+  master2
+  n
+  o/o
+  o/p
+  q
+  r
+  $long
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'git branch with column.*' '
+       git config column.ui column &&
+       git config column.branch "dense" &&
+       COLUMNS=80 git branch >actual &&
+       git config --unset column.branch &&
+       git config --unset column.ui &&
+       cat >expected <<\EOF &&
+  a/b/c   bam   foo   l   * master    n     o/p   r
+  abc     bar   j/k   m/m   master2   o/o   q
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'git branch --column -v should fail' '
+       test_must_fail git branch --column -v
+'
+
+test_expect_success 'git branch -v with column.ui ignored' '
+       git config column.ui column &&
+       COLUMNS=80 git branch -v | cut -c -10 | sed "s/ *$//" >actual &&
+       git config --unset column.ui &&
+       cat >expected <<\EOF &&
+  a/b/c
+  abc
+  bam
+  bar
+  foo
+  j/k
+  l
+  m/m
+* master
+  master2
+  n
+  o/o
+  o/p
+  q
+  r
+EOF
+       test_cmp expected actual
+'
+
 mv .git/config .git/config-saved
 
 test_expect_success 'git branch -m q q2 without config should succeed' '
index 9f00ada5f7776b2377993cc9392b89e5312a513f..c53c9f65ebd2824d4a0d528b25d85e1e0b26f4df 100755 (executable)
@@ -15,184 +15,204 @@ p0='no-funny'
 p1='tabs       ," (dq) and spaces'
 p2='just space'
 
-cat >"$p0" <<\EOF
-1. A quick brown fox jumps over the lazy cat, oops dog.
-2. A quick brown fox jumps over the lazy cat, oops dog.
-3. A quick brown fox jumps over the lazy cat, oops dog.
-EOF
-
-cat 2>/dev/null >"$p1" "$p0"
-echo 'Foo Bar Baz' >"$p2"
+test_expect_success 'setup' '
+       cat >"$p0" <<-\EOF &&
+       1. A quick brown fox jumps over the lazy cat, oops dog.
+       2. A quick brown fox jumps over the lazy cat, oops dog.
+       3. A quick brown fox jumps over the lazy cat, oops dog.
+       EOF
+
+       { cat "$p0" >"$p1" || :; } &&
+       { echo "Foo Bar Baz" >"$p2" || :; } &&
+
+       if test -f "$p1" && cmp "$p0" "$p1"
+       then
+               test_set_prereq TABS_IN_FILENAMES
+       fi
+'
 
-if test -f "$p1" && cmp "$p0" "$p1"
+if ! test_have_prereq TABS_IN_FILENAMES
 then
-    test_set_prereq TABS_IN_FILENAMES
-else
        # since FAT/NTFS does not allow tabs in filenames, skip this test
-       say 'Your filesystem does not allow tabs in filenames'
+       skip_all='Your filesystem does not allow tabs in filenames'
+       test_done
 fi
 
-test_expect_success TABS_IN_FILENAMES 'setup expect' "
-echo 'just space
-no-funny' >expected
-"
+test_expect_success 'setup: populate index and tree' '
+       git update-index --add "$p0" "$p2" &&
+       t0=$(git write-tree)
+'
 
-test_expect_success TABS_IN_FILENAMES 'git ls-files no-funny' \
-       'git update-index --add "$p0" "$p2" &&
+test_expect_success 'ls-files prints space in filename verbatim' '
+       printf "%s\n" "just space" no-funny >expected &&
        git ls-files >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-t0=`git write-tree` &&
-echo "$t0" >t0 &&
+       test_cmp expected current
+'
 
-cat > expected <<\EOF
-just space
-no-funny
-"tabs\t,\" (dq) and spaces"
-EOF
+test_expect_success 'setup: add funny filename' '
+       git update-index --add "$p1" &&
+       t1=$(git write-tree)
 '
 
-test_expect_success TABS_IN_FILENAMES 'git ls-files with-funny' \
-       'git update-index --add "$p1" &&
+test_expect_success 'ls-files quotes funny filename' '
+       cat >expected <<-\EOF &&
+       just space
+       no-funny
+       "tabs\t,\" (dq) and spaces"
+       EOF
        git ls-files >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' "
-echo 'just space
-no-funny
-tabs   ,\" (dq) and spaces' >expected
-"
-
-test_expect_success TABS_IN_FILENAMES 'git ls-files -z with-funny' \
-       'git ls-files -z | perl -pe y/\\000/\\012/ >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-t1=`git write-tree` &&
-echo "$t1" >t1 &&
-
-cat > expected <<\EOF
-just space
-no-funny
-"tabs\t,\" (dq) and spaces"
-EOF
-'
-
-test_expect_success TABS_IN_FILENAMES 'git ls-tree with funny' \
-       'git ls-tree -r $t1 | sed -e "s/^[^     ]*      //" >current &&
-        test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-cat > expected <<\EOF
-A      "tabs\t,\" (dq) and spaces"
-EOF
-'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-index with-funny' \
-       'git diff-index --name-status $t0 >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-tree with-funny' \
-       'git diff-tree --name-status $t0 $t1 >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' "
-echo 'A
-tabs   ,\" (dq) and spaces' >expected
-"
-
-test_expect_success TABS_IN_FILENAMES 'git diff-index -z with-funny' \
-       'git diff-index -z --name-status $t0 | perl -pe y/\\000/\\012/ >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-tree -z with-funny' \
-       'git diff-tree -z --name-status $t0 $t1 | perl -pe y/\\000/\\012/ >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-cat > expected <<\EOF
-CNUM   no-funny        "tabs\t,\" (dq) and spaces"
-EOF
-'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-tree -C with-funny' \
-       'git diff-tree -C --find-copies-harder --name-status \
-               $t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-cat > expected <<\EOF
-RNUM   no-funny        "tabs\t,\" (dq) and spaces"
-EOF
-'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny' \
-       'git update-index --force-remove "$p0" &&
-       git diff-index -M --name-status \
-               $t0 | sed -e 's/^R[0-9]*/RNUM/' >current &&
-       test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-cat > expected <<\EOF
-diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
-similarity index NUM%
-rename from no-funny
-rename to "tabs\t,\" (dq) and spaces"
-EOF
-'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny' \
-       'git diff-index -M -p $t0 |
-        sed -e "s/index [0-9]*%/index NUM%/" >current &&
-        test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-chmod +x "$p1" &&
-cat > expected <<\EOF
-diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
-old mode 100644
-new mode 100755
-similarity index NUM%
-rename from no-funny
-rename to "tabs\t,\" (dq) and spaces"
-EOF
-'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny' \
-       'git diff-index -M -p $t0 |
-        sed -e "s/index [0-9]*%/index NUM%/" >current &&
-        test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-cat >expected <<\EOF
- "tabs\t,\" (dq) and spaces"
- 1 file changed, 0 insertions(+), 0 deletions(-)
-EOF
-'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-tree rename with-funny applied' \
-       'git diff-index -M -p $t0 |
-        git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
-        test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'setup expect' '
-cat > expected <<\EOF
- no-funny
- "tabs\t,\" (dq) and spaces"
- 2 files changed, 3 insertions(+), 3 deletions(-)
-EOF
-'
-
-test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny applied' \
-       'git diff-index -p $t0 |
-        git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
-        test_cmp expected current'
-
-test_expect_success TABS_IN_FILENAMES 'git apply non-git diff' \
-       'git diff-index -p $t0 |
-        sed -ne "/^[-+@]/p" |
-        git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
-        test_cmp expected current'
+       test_cmp expected current
+'
+
+test_expect_success 'ls-files -z does not quote funny filename' '
+       cat >expected <<-\EOF &&
+       just space
+       no-funny
+       tabs    ," (dq) and spaces
+       EOF
+       git ls-files -z >ls-files.z &&
+       perl -pe "y/\000/\012/" <ls-files.z >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'ls-tree quotes funny filename' '
+       cat >expected <<-\EOF &&
+       just space
+       no-funny
+       "tabs\t,\" (dq) and spaces"
+       EOF
+       git ls-tree -r $t1 >ls-tree &&
+       sed -e "s/^[^   ]*      //" <ls-tree >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'diff-index --name-status quotes funny filename' '
+       cat >expected <<-\EOF &&
+       A       "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-index --name-status $t0 >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'diff-tree --name-status quotes funny filename' '
+       cat >expected <<-\EOF &&
+       A       "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-tree --name-status $t0 $t1 >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'diff-index -z does not quote funny filename' '
+       cat >expected <<-\EOF &&
+       A
+       tabs    ," (dq) and spaces
+       EOF
+       git diff-index -z --name-status $t0 >diff-index.z &&
+       perl -pe "y/\000/\012/" <diff-index.z >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'diff-tree -z does not quote funny filename' '
+       cat >expected <<-\EOF &&
+       A
+       tabs    ," (dq) and spaces
+       EOF
+       git diff-tree -z --name-status $t0 $t1 >diff-tree.z &&
+       perl -pe y/\\000/\\012/ <diff-tree.z >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'diff-tree --find-copies-harder quotes funny filename' '
+       cat >expected <<-\EOF &&
+       CNUM    no-funny        "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-tree -C --find-copies-harder --name-status $t0 $t1 >out &&
+       sed -e "s/^C[0-9]*/CNUM/" <out >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'setup: remove unfunny index entry' '
+       git update-index --force-remove "$p0"
+'
+
+test_expect_success 'diff-tree -M quotes funny filename' '
+       cat >expected <<-\EOF &&
+       RNUM    no-funny        "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-index -M --name-status $t0 >out &&
+       sed -e "s/^R[0-9]*/RNUM/" <out >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'diff-index -M -p quotes funny filename' '
+       cat >expected <<-\EOF &&
+       diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+       similarity index NUM%
+       rename from no-funny
+       rename to "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-index -M -p $t0 >diff &&
+       sed -e "s/index [0-9]*%/index NUM%/" <diff >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'setup: mode change' '
+       chmod +x "$p1"
+'
+
+test_expect_success 'diff-index -M -p with mode change quotes funny filename' '
+       cat >expected <<-\EOF &&
+       diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+       old mode 100644
+       new mode 100755
+       similarity index NUM%
+       rename from no-funny
+       rename to "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-index -M -p $t0 >diff &&
+       sed -e "s/index [0-9]*%/index NUM%/" <diff >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'diffstat for rename quotes funny filename' '
+       cat >expected <<-\EOF &&
+        "tabs\t,\" (dq) and spaces"
+        1 file changed, 0 insertions(+), 0 deletions(-)
+       EOF
+       git diff-index -M -p $t0 >diff &&
+       git apply --stat <diff >diffstat &&
+       sed -e "s/|.*//" -e "s/ *\$//" <diffstat >current &&
+       test_i18ncmp expected current
+'
+
+test_expect_success 'numstat for rename quotes funny filename' '
+       cat >expected <<-\EOF &&
+       0       0       "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-index -M -p $t0 >diff &&
+       git apply --numstat <diff >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'numstat without -M quotes funny filename' '
+       cat >expected <<-\EOF &&
+       0       3       no-funny
+       3       0       "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-index -p $t0 >diff &&
+       git apply --numstat <diff >current &&
+       test_cmp expected current
+'
+
+test_expect_success 'numstat for non-git rename diff quotes funny filename' '
+       cat >expected <<-\EOF &&
+       0       3       no-funny
+       3       0       "tabs\t,\" (dq) and spaces"
+       EOF
+       git diff-index -p $t0 >git-diff &&
+       sed -ne "/^[-+@]/p" <git-diff >diff &&
+       git apply --numstat <diff >current &&
+       test_cmp expected current
+'
 
 test_done
index 436719795376f78e3a32a441e9e7e0a4606ac2f5..195bb97f859d6a4990c292da46b9674be0f6153f 100755 (executable)
@@ -324,7 +324,7 @@ y and z notes on 4th commit
 EOF
        git notes merge --commit &&
        # No .git/NOTES_MERGE_* files left
-       test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+       test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
        test_cmp /dev/null output &&
        # Merge commit has pre-merge y and pre-merge z as parents
        test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
@@ -386,7 +386,7 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol
 test_expect_success 'abort notes merge' '
        git notes merge --abort &&
        # No .git/NOTES_MERGE_* files left
-       test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+       test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
        test_cmp /dev/null output &&
        # m has not moved (still == y)
        test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)" &&
@@ -453,7 +453,7 @@ EOF
        # Finalize merge
        git notes merge --commit &&
        # No .git/NOTES_MERGE_* files left
-       test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+       test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
        test_cmp /dev/null output &&
        # Merge commit has pre-merge y and pre-merge z as parents
        test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" &&
@@ -542,7 +542,7 @@ EOF
 test_expect_success 'resolve situation by aborting the notes merge' '
        git notes merge --abort &&
        # No .git/NOTES_MERGE_* files left
-       test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
+       test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null &&
        test_cmp /dev/null output &&
        # m has not moved (still == w)
        test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" &&
@@ -553,4 +553,23 @@ test_expect_success 'resolve situation by aborting the notes merge' '
        verify_notes z
 '
 
+cat >expect_notes <<EOF
+foo
+bar
+EOF
+
+test_expect_success 'switch cwd before committing notes merge' '
+       git notes add -m foo HEAD &&
+       git notes --ref=other add -m bar HEAD &&
+       test_must_fail git notes merge refs/notes/other &&
+       (
+               cd .git/NOTES_MERGE_WORKTREE &&
+               echo "foo" > $(git rev-parse HEAD) &&
+               echo "bar" >> $(git rev-parse HEAD) &&
+               git notes merge --commit
+       ) &&
+       git notes show HEAD > actual_notes &&
+       test_cmp expect_notes actual_notes
+'
+
 test_done
index b981572d736a1adf8da5281f31e580982e2059af..025c1c610efee9562343b34a9c815f34f51ed9ac 100755 (executable)
@@ -247,6 +247,7 @@ test_expect_success '-p handles "no changes" gracefully' '
 '
 
 test_expect_failure 'exchange two commits with -p' '
+       git checkout H &&
        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)
@@ -323,7 +324,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
        echo resolved > file1 &&
        git add file1 &&
        git rebase --continue > output &&
-       grep "^ file1 |    2 +-$" output
+       grep "^ file1 | 2 +-$" output
 '
 
 test_expect_success 'multi-squash only fires up editor once' '
@@ -624,8 +625,38 @@ test_expect_success 'submodule rebase -i' '
        FAKE_LINES="1 squash 2 3" git rebase -i A
 '
 
+test_expect_success 'submodule conflict setup' '
+       git tag submodule-base &&
+       git checkout HEAD^ &&
+       (
+               cd sub && git checkout HEAD^ && echo 4 >elif &&
+               git add elif && git commit -m "submodule conflict"
+       ) &&
+       git add sub &&
+       test_tick &&
+       git commit -m "Conflict in submodule" &&
+       git tag submodule-topic
+'
+
+test_expect_success 'rebase -i continue with only submodule staged' '
+       test_must_fail git rebase -i submodule-base &&
+       git add sub &&
+       git rebase --continue &&
+       test $(git rev-parse submodule-base) != $(git rev-parse HEAD)
+'
+
+test_expect_success 'rebase -i continue with unstaged submodule' '
+       git checkout submodule-topic &&
+       git reset --hard &&
+       test_must_fail git rebase -i submodule-base &&
+       git reset &&
+       git rebase --continue &&
+       test $(git rev-parse submodule-base) = $(git rev-parse HEAD)
+'
+
 test_expect_success 'avoid unnecessary reset' '
        git checkout master &&
+       git reset --hard &&
        test-chmtime =123456789 file3 &&
        git update-index --refresh &&
        HEAD=$(git rev-parse HEAD) &&
index ace8e54e9b374688751cc470eeb9003ca7f5c48f..dc81bf27eb736d32df0f0c3205de3254898d20b5 100755 (executable)
@@ -56,6 +56,7 @@ test_expect_success 'squash F1 into D1' '
 # And rebase G1..M1 onto E2
 
 test_expect_success 'rebase two levels of merge' '
+       git checkout A1 &&
        test_commit G1 &&
        test_commit H1 &&
        test_commit I1 &&
index b38be8e93723991d717b6b7fb690560efb58c36d..a1e86c4097ba096245b11a991c39e79886ca2b91 100755 (executable)
@@ -33,7 +33,7 @@ test_auto_fixup () {
        test_tick &&
        git rebase $2 -i HEAD^^^ &&
        git log --oneline >actual &&
-       test 3 = $(wc -l <actual) &&
+       test_line_count = 3 actual &&
        git diff --exit-code $1 &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
@@ -62,7 +62,7 @@ test_auto_squash () {
        test_tick &&
        git rebase $2 -i HEAD^^^ &&
        git log --oneline >actual &&
-       test 3 = $(wc -l <actual) &&
+       test_line_count = 3 actual &&
        git diff --exit-code $1 &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test 2 = $(git cat-file commit HEAD^ | grep first | wc -l)
@@ -90,7 +90,7 @@ test_expect_success 'misspelled auto squash' '
        test_tick &&
        git rebase --autosquash -i HEAD^^^ &&
        git log --oneline >actual &&
-       test 4 = $(wc -l <actual) &&
+       test_line_count = 4 actual &&
        git diff --exit-code final-missquash &&
        test 0 = $(git rev-list final-missquash...HEAD | wc -l)
 '
@@ -109,7 +109,7 @@ test_expect_success 'auto squash that matches 2 commits' '
        test_tick &&
        git rebase --autosquash -i HEAD~4 &&
        git log --oneline >actual &&
-       test 4 = $(wc -l <actual) &&
+       test_line_count = 4 actual &&
        git diff --exit-code final-multisquash &&
        test 1 = "$(git cat-file blob HEAD^^:file1)" &&
        test 2 = $(git cat-file commit HEAD^^ | grep first | wc -l) &&
@@ -130,7 +130,7 @@ test_expect_success 'auto squash that matches a commit after the squash' '
        test_tick &&
        git rebase --autosquash -i HEAD~4 &&
        git log --oneline >actual &&
-       test 5 = $(wc -l <actual) &&
+       test_line_count = 5 actual &&
        git diff --exit-code final-presquash &&
        test 0 = "$(git cat-file blob HEAD^^:file1)" &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
@@ -147,7 +147,7 @@ test_expect_success 'auto squash that matches a sha1' '
        test_tick &&
        git rebase --autosquash -i HEAD^^^ &&
        git log --oneline >actual &&
-       test 3 = $(wc -l <actual) &&
+       test_line_count = 3 actual &&
        git diff --exit-code final-shasquash &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l)
@@ -163,7 +163,7 @@ test_expect_success 'auto squash that matches longer sha1' '
        test_tick &&
        git rebase --autosquash -i HEAD^^^ &&
        git log --oneline >actual &&
-       test 3 = $(wc -l <actual) &&
+       test_line_count = 3 actual &&
        git diff --exit-code final-longshasquash &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l)
@@ -179,7 +179,7 @@ test_auto_commit_flags () {
        test_tick &&
        git rebase --autosquash -i HEAD^^^ &&
        git log --oneline >actual &&
-       test 3 = $(wc -l <actual) &&
+       test_line_count = 3 actual &&
        git diff --exit-code final-commit-$1 &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test $2 = $(git cat-file commit HEAD^ | grep first | wc -l)
index c10b28cf5731705b437793a58f5acd6c605ad579..92f00cdf84993da995f074efa12344c26b627d67 100755 (executable)
@@ -18,7 +18,12 @@ test_expect_success setup '
        echo third >> file1 &&
        git add file1 &&
        test_tick &&
-       git commit --allow-empty-message -m ""
+       git commit --allow-empty-message -m "" &&
+
+       git checkout master &&
+       git checkout -b empty-branch2 &&
+       test_tick &&
+       git commit --allow-empty -m "empty"
 
 '
 
@@ -48,4 +53,22 @@ test_expect_success 'index lockfile was removed' '
 
 '
 
+test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
+       git checkout master &&
+       echo fourth >>file2 &&
+       git add file2 &&
+       git commit -m "fourth" &&
+       test_must_fail git cherry-pick empty-branch2
+'
+
+test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' '
+       git checkout master &&
+       git cherry-pick --allow-empty empty-branch2
+'
+
+test_expect_success 'cherry pick with --keep-redundant-commits' '
+       git checkout master &&
+       git cherry-pick --keep-redundant-commits HEAD^
+'
+
 test_done
index 1b3a344158aa8077c1e5b47f9ab8bd6394e153ed..75f7ff4f2fe21e86e0a26fe5a6c2119bef38404c 100755 (executable)
@@ -35,6 +35,16 @@ test_expect_success setup '
 '
 
 test_expect_success 'cherry-pick first..fourth works' '
+       git checkout -f master &&
+       git reset --hard first &&
+       test_tick &&
+       git cherry-pick first..fourth &&
+       git diff --quiet other &&
+       git diff --quiet HEAD other &&
+       check_head_differs_from fourth
+'
+
+test_expect_success 'output to keep user entertained during multi-pick' '
        cat <<-\EOF >expected &&
        [master OBJID] second
         Author: A U Thor <author@example.com>
@@ -51,15 +61,22 @@ test_expect_success 'cherry-pick first..fourth works' '
        git reset --hard first &&
        test_tick &&
        git cherry-pick first..fourth >actual &&
+       sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+       test_line_count -ge 3 actual.fuzzy &&
+       test_i18ncmp expected actual.fuzzy
+'
+
+test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
+       git checkout -f master &&
+       git reset --hard first &&
+       test_tick &&
+       git cherry-pick --strategy resolve first..fourth &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-
-       sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
-       test_cmp expected actual.fuzzy &&
        check_head_differs_from fourth
 '
 
-test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
+test_expect_success 'output during multi-pick indicates merge strategy' '
        cat <<-\EOF >expected &&
        Trying simple merge.
        [master OBJID] second
@@ -79,11 +96,8 @@ test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
        git reset --hard first &&
        test_tick &&
        git cherry-pick --strategy resolve first..fourth >actual &&
-       git diff --quiet other &&
-       git diff --quiet HEAD other &&
        sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
-       test_cmp expected actual.fuzzy &&
-       check_head_differs_from fourth
+       test_i18ncmp expected actual.fuzzy
 '
 
 test_expect_success 'cherry-pick --ff first..fourth works' '
index 9e236f9cc0bf43155d377c1706c8142d843c417d..098a6ae4a086ccfeb8c658a80773eb07c9d66441 100755 (executable)
@@ -330,4 +330,30 @@ test_expect_success PERL 'split hunk "add -p (edit)"' '
        ! grep "^+15" actual
 '
 
+test_expect_success 'patch mode ignores unmerged entries' '
+       git reset --hard &&
+       test_commit conflict &&
+       test_commit non-conflict &&
+       git checkout -b side &&
+       test_commit side conflict.t &&
+       git checkout master &&
+       test_commit master conflict.t &&
+       test_must_fail git merge side &&
+       echo changed >non-conflict.t &&
+       echo y | git add -p >output &&
+       ! grep a/conflict.t output &&
+       cat >expected <<-\EOF &&
+       * Unmerged path conflict.t
+       diff --git a/non-conflict.t b/non-conflict.t
+       index f766221..5ea2ed4 100644
+       --- a/non-conflict.t
+       +++ b/non-conflict.t
+       @@ -1 +1 @@
+       -non-conflict
+       +changed
+       EOF
+       git diff --cached >diff &&
+       test_cmp expected diff
+'
+
 test_done
index d48a7c002d622ffac5087be9a7998f781a242731..37ddabba2d60956a4d8da63585bec5a622e12a5d 100755 (executable)
@@ -160,7 +160,7 @@ test_commit_autosquash_flags () {
                git config --unset-all i18n.commitencoding &&
                git rebase --autosquash -i HEAD^^^ &&
                git log --oneline >actual &&
-               test 3 = $(wc -l <actual)
+               test_line_count = 3 actual
        '
 }
 
index 663c60a12e82c96065e60fd448a6583c91e5a2cd..cd042633ba5ec36be89507d25254b32a8f0b98ae 100755 (executable)
@@ -432,7 +432,7 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '
        test $(git ls-files --modified | wc -l) -eq 1
 '
 
-test_expect_success 'stash show - stashes on stack, stash-like argument' '
+test_expect_success 'stash show format defaults to --stat' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
        git reset --hard &&
@@ -443,10 +443,25 @@ test_expect_success 'stash show - stashes on stack, stash-like argument' '
        STASH_ID=$(git stash create) &&
        git reset --hard &&
        cat >expected <<-EOF &&
-        file |    1 +
+        file | 1 +
         1 file changed, 1 insertion(+)
        EOF
        git stash show ${STASH_ID} >actual &&
+       test_i18ncmp expected actual
+'
+
+test_expect_success 'stash show - stashes on stack, stash-like argument' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       git reset --hard &&
+       echo foo >> file &&
+       git stash &&
+       test_when_finished "git stash drop" &&
+       echo bar >> file &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       echo "1 0       file" >expected &&
+       git stash show --numstat ${STASH_ID} >actual &&
        test_cmp expected actual
 '
 
@@ -480,11 +495,8 @@ test_expect_success 'stash show - no stashes on stack, stash-like argument' '
        echo foo >> file &&
        STASH_ID=$(git stash create) &&
        git reset --hard &&
-       cat >expected <<-EOF &&
-        file |    1 +
-        1 file changed, 1 insertion(+)
-       EOF
-       git stash show ${STASH_ID} >actual &&
+       echo "1 0       file" >expected &&
+       git stash show --numstat ${STASH_ID} >actual &&
        test_cmp expected actual
 '
 
index ff8c2f7532aa6fd748dbbdc0322384576f3a35ed..7a3e1f9a24e4be3759c29acc632fc461ede2b74b 100755 (executable)
@@ -8,23 +8,52 @@ test_description='Test mode change diffs.
 '
 . ./test-lib.sh
 
-test_expect_success \
-    'setup' \
-    'echo frotz >rezrov &&
-     git update-index --add rezrov &&
-     tree=`git write-tree` &&
-     echo $tree'
-
-test_expect_success \
-    'chmod' \
-    'test_chmod +x rezrov &&
-     git diff-index $tree >current'
-
-sed -e 's/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' <current >check
-echo ":100644 100755 X X M     rezrov" >expected
-
-test_expect_success \
-    'verify' \
-    'test_cmp expected check'
+sed_script='s/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /'
+
+test_expect_success 'setup' '
+       echo frotz >rezrov &&
+       git update-index --add rezrov &&
+       tree=`git write-tree` &&
+       echo $tree
+'
+
+test_expect_success 'chmod' '
+       test_chmod +x rezrov &&
+       git diff-index $tree >current &&
+       sed -e "$sed_script" <current >check &&
+       echo ":100644 100755 X X M      rezrov" >expected &&
+       test_cmp expected check
+'
+
+test_expect_success 'prepare binary file' '
+       git commit -m rezrov &&
+       printf "\00\01\02\03\04\05\06" >binbin &&
+       git add binbin &&
+       git commit -m binbin
+'
+
+test_expect_success '--stat output after text chmod' '
+       test_chmod -x rezrov &&
+       echo " 0 files changed" >expect &&
+       git diff HEAD --stat >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--shortstat output after text chmod' '
+       git diff HEAD --shortstat >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--stat output after binary chmod' '
+       test_chmod +x binbin &&
+       echo " 0 files changed" >expect &&
+       git diff HEAD --stat >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--shortstat output after binary chmod' '
+       git diff HEAD --shortstat >actual &&
+       test_cmp expect actual
+'
 
 test_done
index 2d9f9a0cf1555cab24af19f264d495e134c27352..8b4e80de9627d9a8fa90a31edbe05c2c70f1d4b3 100755 (executable)
@@ -8,6 +8,13 @@ test_description='Binary diff and apply
 
 . ./test-lib.sh
 
+cat >expect.binary-numstat <<\EOF
+1      1       a
+-      -       b
+1      1       c
+-      -       d
+EOF
+
 test_expect_success 'prepare repository' \
        'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
         git update-index --add a b c d &&
@@ -23,13 +30,23 @@ cat > expected <<\EOF
  d |  Bin
  4 files changed, 2 insertions(+), 2 deletions(-)
 EOF
-test_expect_success 'diff without --binary' \
-       'git diff | git apply --stat --summary >current &&
-        test_cmp expected current'
+test_expect_success '"apply --stat" output for binary file change' '
+       git diff >diff &&
+       git apply --stat --summary <diff >current &&
+       test_i18ncmp expected current
+'
+
+test_expect_success 'apply --numstat notices binary file change' '
+       git diff >diff &&
+       git apply --numstat <diff >current &&
+       test_cmp expect.binary-numstat current
+'
 
-test_expect_success 'diff with --binary' \
-       'git diff --binary | git apply --stat --summary >current &&
-        test_cmp expected current'
+test_expect_success 'apply --numstat understands diff --binary format' '
+       git diff --binary >diff &&
+       git apply --numstat <diff >current &&
+       test_cmp expect.binary-numstat current
+'
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
@@ -90,4 +107,23 @@ test_expect_success 'diff --no-index with binary creation' '
        test_cmp expected actual
 '
 
+cat >expect <<EOF
+ binfile  |   Bin 0 -> 1026 bytes
+ textfile | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
+
+test_expect_success 'diff --stat with binary files and big change count' '
+       echo X | dd of=binfile bs=1k seek=1 &&
+       git add binfile &&
+       i=0 &&
+       while test $i -lt 10000; do
+               echo $i &&
+               i=$(($i + 1))
+       done >textfile &&
+       git add textfile &&
+       git diff --cached --stat binfile textfile >output &&
+       grep " | " output >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 93a6f208710befc064b7b99bcd758bb8b6381918..e77c09c37eede2f039610199ba8e3c45e94213d4 100755 (executable)
@@ -128,7 +128,12 @@ do
                } >"$actual" &&
                if test -f "$expect"
                then
-                       test_cmp "$expect" "$actual" &&
+                       case $cmd in
+                       *format-patch* | *-stat*)
+                               test_i18ncmp "$expect" "$actual";;
+                       *)
+                               test_cmp "$expect" "$actual";;
+                       esac &&
                        rm -f "$actual"
                else
                        # this is to help developing new tests.
index 2f8560c369c01e72b8ec11d43e04df0a0e9ce60c..9951e3677d694304e3d52448e7a12d881c1943af 100644 (file)
@@ -1,7 +1,7 @@
 $ git diff-tree --cc --patch-with-stat --summary master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --cc dir/sub
index 72e03c14fb4edb2812202da8acd837b064131510..cec33fa3f02b2b8990b51b61c6e1f361e9c9b208 100644 (file)
@@ -1,8 +1,8 @@
 $ git diff-tree --cc --patch-with-stat --summary side
 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 8b357d964b4773c3078e2c32bd3e765f5749841c..db3c0a7b2cc5d98b85e22074df924f3cb666b840 100644 (file)
@@ -1,7 +1,7 @@
 $ git diff-tree --cc --patch-with-stat master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --cc dir/sub
index e0568d688311059ad04558f50bb3ddacd21fd403..d019867dd9415bc9fef748e4204c2376fa97b2f1 100644 (file)
@@ -1,6 +1,6 @@
 $ git diff-tree --cc --stat --summary master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 $
index 5afc8239a11f7f7fc20a3fabfad5213eb49fd153..12b2eee17ec04a40a10f0c7300379e01755c0114 100644 (file)
@@ -1,8 +1,8 @@
 $ git diff-tree --cc --stat --summary side
 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 $
index f48367a89a64945875a81024e444b35771800855..40b91796b34a1f22b51414d48c76152b4dc881ca 100644 (file)
@@ -1,6 +1,6 @@
 $ git diff-tree --cc --stat master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 $
index 590864c2d798472a3fed9fd6bf1fa41545fd572d..817ed06f822e28a0b15a71c2c8bb519e3cdcde67 100644 (file)
@@ -1,8 +1,8 @@
 $ git diff-tree --pretty=oneline --root --patch-with-stat initial
 444ac553ac7612cc88969031b02b3767fb8a353a Initial
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index e05e77875ccdaa8a258310f85f5753ae84b4c6c4..fe3f6b7c7e799cd3e99b46742feccec2cfce5634 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index 0e2c956633d28897e7bbddb58ad1cc053e55a8a6..06eb77e3861c321765bb77e701703db9400919a3 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index 384fa44ddd1ab9dbf0d38caf1f445fed1a0b5030..680eab5f27961e0a1ee6c26e0bd28f0aaa4a2c62 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
index 10384a83d31da363cec05f2fecb9def43ae158d7..9722d1b3a76ad89bedaab114a1800a88157bf8d1 100644 (file)
@@ -5,8 +5,8 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
 $
index f57062ea07ac63aa7c700e470aa43d1ede20e09b..ad69ffe647bbd692d307c0b7a8d047b8c79b3a2d 100644 (file)
@@ -1,8 +1,8 @@
 $ git diff-tree --root --patch-with-stat initial
 444ac553ac7612cc88969031b02b3767fb8a353a
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index 7088683444e54a026e201849ef5d635fab66fc3b..81c3021541bc61ec36557822d314c2e57e0a1265 100644 (file)
@@ -1,6 +1,6 @@
 $ git diff-tree -c --stat --summary master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 $
index ef216abb1dcaa91d1aa521599b25faf16f704124..e8dc12bfbf65577ad74bfe5bc9b2c8a838531eef 100644 (file)
@@ -1,8 +1,8 @@
 $ git diff-tree -c --stat --summary side
 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 $
index ad19f103eb3323d1c84719cec13a09885e3b47f4..89d59b15480f073d026cfbe6fca6ae31522cef22 100644 (file)
@@ -1,6 +1,6 @@
 $ git diff-tree -c --stat master
 59d314ad6f356dd08601a4cd5e530381da3e3c64
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 $
index ddad917ae80a5c80b2c7b7f65a85a19404b59945..be8d1ea1bd2d2b0b254b2483162df6d2735af8f8 100644 (file)
@@ -1,7 +1,7 @@
 $ git diff --patch-with-stat -r initial..side
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index bdbd114d8eaf5dd6f40502300c479839a61f0d6b..5424e6d5669ac11b55376f414439213643d6eade 100644 (file)
@@ -1,7 +1,7 @@
 $ git diff --patch-with-stat initial..side
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index 6d08f3d3550413a23c08e264b4707dafeba21acf..b7741e2b8367196c04366ac911c6837531b8a34d 100644 (file)
@@ -1,6 +1,6 @@
 $ git diff --stat initial..side
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 $
index 2ddb2540e640584532dd7b22d1618b35b322a20d..5d514f55b9382aa83f9415e65cc9039eb9b5f5c4 100644 (file)
@@ -1,6 +1,6 @@
 $ git diff -r --stat initial..side
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 $
index 3cab049f7d98bfe9988c9f19fe477d0d29c8c2d5..547ca065a59cd7ed4cb74dffe570db81e9fd9c0e 100644 (file)
@@ -12,9 +12,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 564a4d38f229946ea08e9c0110a5ad6a20acce0b..52fedc179e50de8f6dc61dbe069dc2737099f0b9 100644 (file)
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -121,9 +121,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 4f28460b83dd47a1446fc5c110025816611e2a54..1c3cde251b53cc8dfe476210411d22d61f24d473 100644 (file)
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
index b10cc2e251cf7e38592ae26279b9613758a47b35..4717bd8313ee7201d7c3be79439d658a73440a37 100644 (file)
@@ -12,9 +12,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index a976a8aaf42b2fa03fd35d215de9cafee99b946c..02c4db7ec582426bf447783d347592c1884e9f89 100644 (file)
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -121,9 +121,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index b4fd66477aa251c56da1e532d83b7d56ec79d25b..c7677c5951c3949ffa9789d68dff031f55247108 100644 (file)
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -121,9 +121,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 0d31036e7f573d70f0ec3c00b67b61291a663671..5b3e34e2c0c6e063ff03e32c07ed0435e9650348 100644 (file)
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -121,9 +121,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 18d47144231ab36e6f020071b1b8599dd2e76ace..d13f8a81280100a0ed18b547dfc7baf7bde0eb6e 100644 (file)
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
index 29e00ab8af8503e6da3b15c55147da550d4919f7..caec5537de3f158166aae80a99f68f99c619eccc 100644 (file)
@@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
index 3572f20b5d4a1834d1c60ca05eca11efd8b46375..d3a6762130cdd168733e7f510e84b02778411aca 100644 (file)
@@ -12,9 +12,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 54cdcdab40f321559038c954fab572e532801b8b..244d964fc67d3f2cec759eabdc2f245a72549563 100644 (file)
@@ -10,10 +10,10 @@ A U Thor (2):
   Second
   Third
 
- dir/sub |    4 ++++
- file0   |    3 +++
- file1   |    3 +++
- file2   |    3 ---
+ dir/sub | 4 ++++
+ file0   | 3 +++
+ file1   | 3 +++
+ file2   | 3 ---
  4 files changed, 10 insertions(+), 3 deletions(-)
  create mode 100644 file1
  delete mode 100644 file2
@@ -28,9 +28,9 @@ Subject: [DIFFERENT_PREFIX 1/2] Second
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -73,8 +73,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [DIFFERENT_PREFIX 2/2] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
index 23194ebdaa8defaca544a0f6d18d449a44fcd5f1..bfc287a147d25d37e5a0ae111e38182c1075778d 100644 (file)
@@ -6,9 +6,9 @@ Subject: [PATCH] Second
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -51,8 +51,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [PATCH] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -85,9 +85,9 @@ Date: Mon, 26 Jun 2006 00:03:00 +0000
 Subject: [PATCH] Side
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 78f1a80a976ac54cc53602f203cae96f12be65dc..568f6f584e63a4b711fd341b02a359270ef62b87 100644 (file)
@@ -6,9 +6,9 @@ Subject: [PATCH 1/3] Second
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -51,8 +51,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [PATCH 2/3] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -85,9 +85,9 @@ Date: Mon, 26 Jun 2006 00:03:00 +0000
 Subject: [PATCH 3/3] Side
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index a3dab7f773d71da1f1b1ade10b24cf9562ff7d1b..5f0352f9f70ceb2c40e2a24160c14887e4a6ca8f 100644 (file)
@@ -6,9 +6,9 @@ Subject: [PATCH 1/3] Second
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -51,8 +51,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [PATCH 2/3] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -85,9 +85,9 @@ Date: Mon, 26 Jun 2006 00:03:00 +0000
 Subject: [PATCH 3/3] Side
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 39f4a3f2d1f50ce49792f4fcc6373d003ebf971b..2ae454d807370143aacf2b6655d80361cb975c6f 100644 (file)
@@ -6,9 +6,9 @@ Subject: [PATCH 1/2] Second
 
 This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -51,8 +51,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [PATCH 2/2] Third
 
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
index 88109209db005188c9e70fdd40b906756119dd69..a7d52fbeeaf15b8146e431c90387ccd55754db59 100644 (file)
@@ -5,9 +5,9 @@ Date: Mon, 26 Jun 2006 00:03:00 +0000
 Subject: [PATCH] Side
 
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index 4085bbde870d0810c69e7d78ef94052df385826c..a18f1472a9a4b5b098a2c342a9f77fc68a3b6dc0 100644 (file)
@@ -12,7 +12,7 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -31,7 +31,7 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -53,7 +53,7 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index 458627953e29c80b8bed0e640db1c580946a0194..ae425c4672200ad73cb2db228bd7385757673ee3 100644 (file)
@@ -12,9 +12,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -54,8 +54,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -86,9 +86,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/dir/sub b/dir/sub
index 6e172cfadd04785269e0060fa55e86b3df117195..d5207cadf4483a7e63b6085a9007623c73b1a694 100644 (file)
@@ -12,7 +12,7 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -31,7 +31,7 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -53,7 +53,7 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index 48b0d4b91dfa427c1f0971bedbad61931e103bb7..0fc1e8cd71fee38125e77487da29c12c5863a054 100644 (file)
@@ -6,8 +6,8 @@ Date:   Mon Jun 26 00:04:00 2006 +0000
 
     Merge branch 'side'
 
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --cc dir/sub
@@ -44,9 +44,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
@@ -87,8 +87,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -120,9 +120,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -162,9 +162,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
index f9dc5122e2a436a62fa80bdf820b4d44cacdfe8e..dffc09dde9e03179f48cea86e04aac34f1d30504 100644 (file)
@@ -12,9 +12,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
@@ -55,8 +55,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -88,9 +88,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -130,9 +130,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
index 0807ece2347ac54f87c860904ef257c766da5dd0..55aa98012dece9e96d1092f442b6235d420ca42f 100644 (file)
@@ -12,9 +12,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -54,8 +54,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -86,9 +86,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/dir/sub b/dir/sub
@@ -127,9 +127,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index 84f5ef69119edf44e967fa09e3c6acd0bdc6b715..019d85f7de6c2d360ae2f9addd65bacd9bee318b 100644 (file)
@@ -6,8 +6,8 @@ Date:   Mon Jun 26 00:04:00 2006 +0000
 
     Merge branch 'side'
 
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --combined dir/sub
@@ -44,9 +44,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
@@ -87,8 +87,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -120,9 +120,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -162,9 +162,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
index e60384d24de317e9f9d6dac222d8123ae4d68e02..95a474ef1d54889bb8fd599a34c2efe101697b6e 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
index a3a3255fd3f508ddd47a92cb47552dde3ab8156a..974e99be820e40c68f5ca290519375a8a14cb0c6 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index d16f464aca8f83264f53f0355bb3be84eddf1bd8..a71492f9bf4e96e2017a3343cff97038f7ee6610 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 $
index 6300c0535fff7e4a82fdfb6511392b006f5dfa0c..9be712458f4494f0b5f79030ccc9252fc9f1525c 100644 (file)
@@ -5,8 +5,8 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 $
index 16ae54345f6a1e93fae3ad7269829b32eaab3739..c8b6af2f4381571b5168bc78ab57cd5f5adac4d1 100644 (file)
@@ -5,7 +5,7 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -24,7 +24,7 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -46,7 +46,7 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index f3e45ec270ace186d58f40a21a9763598d6dc0ac..1ac431ba9212d3ba8147ab44a657293e5b9b2558 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -47,8 +47,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -79,9 +79,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/dir/sub b/dir/sub
index c77f0bc320f0241550e9027f7ecb191d77f85b43..b30c28588f9ae44c9fcf2cf96feea9db949361e5 100644 (file)
@@ -5,7 +5,7 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -24,7 +24,7 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -46,7 +46,7 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
+ dir/sub | 2 ++
  1 file changed, 2 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index 8d03efea6ce33bb52018eeedc02629c6bd9253aa..30aae7817b952d1088c872453a6a38a681fcd946 100644 (file)
@@ -6,8 +6,8 @@ Date:   Mon Jun 26 00:04:00 2006 +0000
 
     Merge branch 'side'
 
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --cc dir/sub
@@ -44,9 +44,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
@@ -87,8 +87,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -120,9 +120,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -162,9 +162,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
index 1874d0616c4d6cf308a6fcc0f59a0d552664777f..db90e5152547bc92c4ff158e044aa3d18c764783 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
@@ -48,8 +48,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -81,9 +81,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -123,9 +123,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
index 5211ff2a757b8db234ec1f4b14ae33300c21296a..9a6cc92ce7de88b564c1d95d611ba1ae632571af 100644 (file)
@@ -5,9 +5,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -47,8 +47,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
@@ -79,9 +79,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
 
 diff --git a/dir/sub b/dir/sub
@@ -120,9 +120,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
 
 diff --git a/dir/sub b/dir/sub
index ad30245a59b94ed18bf9e1bfca3eb0077cd3a14a..d1d32bd34c333411661355685368130faec79764 100644 (file)
@@ -6,8 +6,8 @@ Date:   Mon Jun 26 00:04:00 2006 +0000
 
     Merge branch 'side'
 
- dir/sub |    2 ++
- file0   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
  2 files changed, 5 insertions(+)
 
 diff --combined dir/sub
@@ -44,9 +44,9 @@ Date:   Mon Jun 26 00:03:00 2006 +0000
 
     Side
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file3   |    4 ++++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file3   | 4 ++++
  3 files changed, 9 insertions(+)
  create mode 100644 file3
 
@@ -87,8 +87,8 @@ Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 ---
- dir/sub |    2 ++
- file1   |    3 +++
+ dir/sub | 2 ++
+ file1   | 3 +++
  2 files changed, 5 insertions(+)
  create mode 100644 file1
 
@@ -120,9 +120,9 @@ Date:   Mon Jun 26 00:01:00 2006 +0000
     
     This is the second commit.
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 ---
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 ---
  3 files changed, 5 insertions(+), 3 deletions(-)
  delete mode 100644 file2
 
@@ -162,9 +162,9 @@ Date:   Mon Jun 26 00:00:00 2006 +0000
 
     Initial
 ---
- dir/sub |    2 ++
- file0   |    3 +++
- file2   |    3 +++
+ dir/sub | 2 ++
+ file0   | 3 +++
+ file2   | 3 +++
  3 files changed, 8 insertions(+)
  create mode 100644 dir/sub
  create mode 100644 file0
index 7dfe716cf9ed63f08f512cfa123d9bf93fa92839..b473b6d6ebbf75f77be99085c26b68781b7f5fc6 100755 (executable)
@@ -518,11 +518,6 @@ test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
 '
 
 cat > expect << EOF
----
- file |   16 ++++++++++++++++
- 1 file changed, 16 insertions(+)
-
-diff --git a/file b/file
 index 40f36c6..2dc5c23 100644
 --- a/file
 +++ b/file
@@ -537,7 +532,9 @@ EOF
 test_expect_success 'format-patch respects -U' '
 
        git format-patch -U4 -2 &&
-       sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+       sed -e "1,/^diff/d" -e "/^+5/q" \
+               <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch \
+               >output &&
        test_cmp expect output
 
 '
index ab0c2f0574f915296b885a21c6151a6947bdae7e..97b81778cb8c9f3c06b551b1f7031c944ced70d0 100755 (executable)
@@ -57,22 +57,33 @@ test_expect_success TABS_IN_FILENAMES 'git diff --summary -M HEAD' '
        test_cmp expect actual
 '
 
-test_expect_success TABS_IN_FILENAMES 'setup expected files' '
-cat >expect <<\EOF
- pathname.1 => "Rpathname\twith HT.0"            |    0
- pathname.3 => "Rpathname\nwith LF.0"            |    0
- "pathname\twith HT.3" => "Rpathname\nwith LF.1" |    0
- pathname.2 => Rpathname with SP.0               |    0
- "pathname\twith HT.2" => Rpathname with SP.1    |    0
- pathname.0 => Rpathname.0                       |    0
- "pathname\twith HT.0" => Rpathname.1            |    0
- 7 files changed, 0 insertions(+), 0 deletions(-)
-EOF
+test_expect_success TABS_IN_FILENAMES 'git diff --numstat -M HEAD' '
+       cat >expect <<-\EOF &&
+       0       0       pathname.1 => "Rpathname\twith HT.0"
+       0       0       pathname.3 => "Rpathname\nwith LF.0"
+       0       0       "pathname\twith HT.3" => "Rpathname\nwith LF.1"
+       0       0       pathname.2 => Rpathname with SP.0
+       0       0       "pathname\twith HT.2" => Rpathname with SP.1
+       0       0       pathname.0 => Rpathname.0
+       0       0       "pathname\twith HT.0" => Rpathname.1
+       EOF
+       git diff --numstat -M HEAD >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success TABS_IN_FILENAMES 'git diff --stat -M HEAD' '
+       cat >expect <<-\EOF &&
+        pathname.1 => "Rpathname\twith HT.0"            | 0
+        pathname.3 => "Rpathname\nwith LF.0"            | 0
+        "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0
+        pathname.2 => Rpathname with SP.0               | 0
+        "pathname\twith HT.2" => Rpathname with SP.1    | 0
+        pathname.0 => Rpathname.0                       | 0
+        "pathname\twith HT.0" => Rpathname.1            | 0
+        7 files changed, 0 insertions(+), 0 deletions(-)
+       EOF
        git diff --stat -M HEAD >actual &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 test_done
index 4ac162cfcf891e5fe0a36a3074db0d486ffa5cf0..d4ab4f2ccf9a1bbbc1e6af84df5f09c373f9f465 100755 (executable)
@@ -85,13 +85,17 @@ test_expect_success 'status -v produces text' '
 '
 
 cat >expect.stat <<'EOF'
- file |  Bin 2 -> 4 bytes
+ file | Bin 2 -> 4 bytes
  1 file changed, 0 insertions(+), 0 deletions(-)
 EOF
 test_expect_success 'diffstat does not run textconv' '
        echo file diff=fail >.gitattributes &&
        git diff --stat HEAD^ HEAD >actual &&
-       test_cmp expect.stat actual
+       test_i18ncmp expect.stat actual &&
+
+       head -n1 <expect.stat >expect.line1 &&
+       head -n1 <actual >actual.line1 &&
+       test_cmp expect.line1 actual.line1
 '
 # restore working setup
 echo file diff=foo >.gitattributes
index 7d7470f21b66a937e7414f4fe5419f8830fd8e86..c8296fa4fc1fbfe2645554d4818ae586d1cc2a14 100755 (executable)
@@ -44,10 +44,16 @@ test_expect_success 'rewrite diff can show binary patch' '
        grep "GIT binary patch" diff
 '
 
-test_expect_success 'rewrite diff --stat shows binary changes' '
+test_expect_success 'rewrite diff --numstat shows binary changes' '
+       git diff -B --numstat --summary >diff &&
+       grep -e "-      -       " diff &&
+       grep " rewrite file" diff
+'
+
+test_expect_success 'diff --stat counts binary rewrite as 0 lines' '
        git diff -B --stat --summary >diff &&
        grep "Bin" diff &&
-       grep "0 insertions.*0 deletions" diff &&
+       test_i18ngrep "0 insertions.*0 deletions" diff &&
        grep " rewrite file" diff
 '
 
index 5c2012111c28d338ad979fb7bcca871e744184fe..30d42cb3bfd856a7d920119f1c4226c408a8f82f 100755 (executable)
@@ -3,6 +3,7 @@
 test_description='word diff colors'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 cat >pre.simple <<-\EOF
        h(4)
@@ -293,6 +294,10 @@ test_expect_success '--word-diff=none' '
        word_diff --word-diff=plain --word-diff=none
 '
 
+test_expect_success 'unset default driver' '
+       test_unconfig diff.wordregex
+'
+
 test_language_driver bibtex
 test_language_driver cpp
 test_language_driver csharp
@@ -348,4 +353,35 @@ test_expect_success 'word-diff with no newline at EOF' '
        word_diff --word-diff=plain
 '
 
+test_expect_success 'setup history with two files' '
+       echo "a b; c" >a.tex &&
+       echo "a b; c" >z.txt &&
+       git add a.tex z.txt &&
+       git commit -minitial &&
+
+       # modify both
+       echo "a bx; c" >a.tex &&
+       echo "a bx; c" >z.txt &&
+       git commit -mmodified -a
+'
+
+test_expect_success 'wordRegex for the first file does not apply to the second' '
+       echo "*.tex diff=tex" >.gitattributes &&
+       git config diff.tex.wordRegex "[a-z]+|." &&
+       cat >expect <<-\EOF &&
+               diff --git a/a.tex b/a.tex
+               --- a/a.tex
+               +++ b/a.tex
+               @@ -1 +1 @@
+               a [-b-]{+bx+}; c
+               diff --git a/z.txt b/z.txt
+               --- a/z.txt
+               +++ b/z.txt
+               @@ -1 +1 @@
+               a [-b;-]{+bx;+} c
+       EOF
+       git diff --word-diff HEAD~ >actual &&
+       compare_diff_patch expect actual
+'
+
 test_done
index e747e842272df5935f863f78ccfc3b311f64228b..cdb9202f57ea75983cf8a25f892bbf868724250d 100755 (executable)
@@ -15,65 +15,65 @@ test_expect_success 'setup' '
 
 test_expect_success 'git diff-tree HEAD^ HEAD' '
        git diff-tree --quiet HEAD^ HEAD >cnt
-       test $? = 1 && test $(wc -l <cnt) = 0
+       test $? = 1 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
        git diff-tree --quiet HEAD^ HEAD -- a >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
+       test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
        git diff-tree --quiet HEAD^ HEAD -- b >cnt
-       test $? = 1 && test $(wc -l <cnt) = 0
+       test $? = 1 && test_line_count = 0 cnt
 '
 # this diff outputs one line: sha1 of the given head
 test_expect_success 'echo HEAD | git diff-tree --stdin' '
        echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
-       test $? = 1 && test $(wc -l <cnt) = 1
+       test $? = 1 && test_line_count = 1 cnt
 '
 test_expect_success 'git diff-tree HEAD HEAD' '
        git diff-tree --quiet HEAD HEAD >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
+       test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-files' '
        git diff-files --quiet >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
+       test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD' '
        git diff-index --quiet --cached HEAD >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
+       test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD^' '
        git diff-index --quiet --cached HEAD^ >cnt
-       test $? = 1 && test $(wc -l <cnt) = 0
+       test $? = 1 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-index --cached HEAD^' '
        echo text >>b &&
        echo 3 >c &&
        git add . && {
                git diff-index --quiet --cached HEAD^ >cnt
-               test $? = 1 && test $(wc -l <cnt) = 0
+               test $? = 1 && test_line_count = 0 cnt
        }
 '
 test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
        git commit -m "text in b" && {
                git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
-               test $? = 1 && test $(wc -l <cnt) = 0
+               test $? = 1 && test_line_count = 0 cnt
        }
 '
 test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
        git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
-       test $? = 0 && test $(wc -l <cnt) = 0
+       test $? = 0 && test_line_count = 0 cnt
 '
 test_expect_success 'git diff-files' '
        echo 3 >>c && {
                git diff-files --quiet >cnt
-               test $? = 1 && test $(wc -l <cnt) = 0
+               test $? = 1 && test_line_count = 0 cnt
        }
 '
 test_expect_success 'git diff-index --cached HEAD' '
        git update-index c && {
                git diff-index --quiet --cached HEAD >cnt
-               test $? = 1 && test $(wc -l <cnt) = 0
+               test $? = 1 && test_line_count = 0 cnt
        }
 '
 
index bf9a7526bd38a17e0e991739db8c4a1f8541b2f6..6c01d0c056e2608393ed16eea29dd45fc79d786c 100755 (executable)
@@ -458,4 +458,38 @@ EOF
        test_cmp expected actual
 '
 
+test_expect_success 'diff --submodule with objects referenced by alternates' '
+       mkdir sub_alt &&
+       (cd sub_alt &&
+               git init &&
+               echo a >a &&
+               git add a &&
+               git commit -m a
+       ) &&
+       mkdir super &&
+       (cd super &&
+               git clone -s ../sub_alt sub &&
+               git init &&
+               git add sub &&
+               git commit -m "sub a"
+       ) &&
+       (cd sub_alt &&
+               sha1_before=$(git rev-parse --short HEAD)
+               echo b >b &&
+               git add b &&
+               git commit -m b
+               sha1_after=$(git rev-parse --short HEAD)
+               echo "Submodule sub $sha1_before..$sha1_after:
+  > b" >../expected
+       ) &&
+       (cd super &&
+               (cd sub &&
+                       git fetch &&
+                       git checkout origin/master
+               ) &&
+               git diff --submodule > ../actual
+       )
+       test_cmp expected actual
+'
+
 test_done
index 06012811a1abf2e7d9c766e84b32dae8a95a7321..2a2cf91352037b6f2c238237474aa1d78928f5ad 100755 (executable)
@@ -23,9 +23,8 @@ test_expect_success 'move the files into a "sub" directory' '
 '
 
 cat > expected <<\EOF
- bar => sub/bar |  Bin 5 -> 5 bytes
- foo => sub/foo |    0
- 2 files changed, 0 insertions(+), 0 deletions(-)
+-      -       bar => sub/bar
+0      0       foo => sub/foo
 
 diff --git a/bar b/sub/bar
 similarity index 100%
@@ -38,7 +37,8 @@ rename to sub/foo
 EOF
 
 test_expect_success 'git show -C -C report renames' '
-       git show -C -C --raw --binary --stat | tail -n 12 > current &&
+       git show -C -C --raw --binary --numstat >patch-with-stat &&
+       tail -n 11 patch-with-stat >current &&
        test_cmp expected current
 '
 
index bd119be106f1a2d6d91f982a1bf8afcbf6b8c831..3950f5034d319be99695c9332b22a8d26e20ff32 100755 (executable)
@@ -29,15 +29,27 @@ test_expect_success "-p $*" "
 "
 }
 
+check_numstat() {
+expect=$1; shift
+cat >expected <<EOF
+1      0       $expect
+EOF
+test_expect_success "--numstat $*" "
+       echo '1 0       $expect' >expected &&
+       git diff --numstat $* HEAD^ >actual &&
+       test_cmp expected actual
+"
+}
+
 check_stat() {
 expect=$1; shift
 cat >expected <<EOF
- $expect |    1 +
+ $expect | 1 +
  1 file changed, 1 insertion(+)
 EOF
 test_expect_success "--stat $*" "
        git diff --stat $* HEAD^ >actual &&
-       test_cmp expected actual
+       test_i18ncmp expected actual
 "
 }
 
@@ -52,7 +64,7 @@ test_expect_success "--raw $*" "
 "
 }
 
-for type in diff stat raw; do
+for type in diff numstat stat raw; do
        check_$type file2 --relative=subdir/
        check_$type file2 --relative=subdir
        check_$type dir/file2 --relative=sub
index 29e80a58cdcf43077bcc5bf42834aa8b4daad93d..ed7e093366bcbdaa177bac4294a07fc52d4233ed 100755 (executable)
@@ -252,50 +252,47 @@ EOF
 '
 
 cat <<EOF >expect_diff_stat
- changed/text             |    2 +-
- dst/copy/changed/text    |   10 ++++++++++
- dst/copy/rearranged/text |   10 ++++++++++
- dst/copy/unchanged/text  |   10 ++++++++++
- dst/move/changed/text    |   10 ++++++++++
- dst/move/rearranged/text |   10 ++++++++++
- dst/move/unchanged/text  |   10 ++++++++++
- rearranged/text          |    2 +-
- src/move/changed/text    |   10 ----------
- src/move/rearranged/text |   10 ----------
- src/move/unchanged/text  |   10 ----------
- 11 files changed, 62 insertions(+), 32 deletions(-)
+1      1       changed/text
+10     0       dst/copy/changed/text
+10     0       dst/copy/rearranged/text
+10     0       dst/copy/unchanged/text
+10     0       dst/move/changed/text
+10     0       dst/move/rearranged/text
+10     0       dst/move/unchanged/text
+1      1       rearranged/text
+0      10      src/move/changed/text
+0      10      src/move/rearranged/text
+0      10      src/move/unchanged/text
 EOF
 
 cat <<EOF >expect_diff_stat_M
- changed/text                      |    2 +-
- dst/copy/changed/text             |   10 ++++++++++
- dst/copy/rearranged/text          |   10 ++++++++++
- dst/copy/unchanged/text           |   10 ++++++++++
- {src => dst}/move/changed/text    |    2 +-
- {src => dst}/move/rearranged/text |    2 +-
- {src => dst}/move/unchanged/text  |    0
- rearranged/text                   |    2 +-
- 8 files changed, 34 insertions(+), 4 deletions(-)
+1      1       changed/text
+10     0       dst/copy/changed/text
+10     0       dst/copy/rearranged/text
+10     0       dst/copy/unchanged/text
+1      1       {src => dst}/move/changed/text
+1      1       {src => dst}/move/rearranged/text
+0      0       {src => dst}/move/unchanged/text
+1      1       rearranged/text
 EOF
 
 cat <<EOF >expect_diff_stat_CC
- changed/text                      |    2 +-
- {src => dst}/copy/changed/text    |    2 +-
- {src => dst}/copy/rearranged/text |    2 +-
- {src => dst}/copy/unchanged/text  |    0
- {src => dst}/move/changed/text    |    2 +-
- {src => dst}/move/rearranged/text |    2 +-
- {src => dst}/move/unchanged/text  |    0
- rearranged/text                   |    2 +-
- 8 files changed, 6 insertions(+), 6 deletions(-)
-EOF
-
-test_expect_success 'sanity check setup (--stat)' '
-       git diff --stat HEAD^..HEAD >actual_diff_stat &&
+1      1       changed/text
+1      1       {src => dst}/copy/changed/text
+1      1       {src => dst}/copy/rearranged/text
+0      0       {src => dst}/copy/unchanged/text
+1      1       {src => dst}/move/changed/text
+1      1       {src => dst}/move/rearranged/text
+0      0       {src => dst}/move/unchanged/text
+1      1       rearranged/text
+EOF
+
+test_expect_success 'sanity check setup (--numstat)' '
+       git diff --numstat HEAD^..HEAD >actual_diff_stat &&
        test_cmp expect_diff_stat actual_diff_stat &&
-       git diff --stat -M HEAD^..HEAD >actual_diff_stat_M &&
+       git diff --numstat -M HEAD^..HEAD >actual_diff_stat_M &&
        test_cmp expect_diff_stat_M actual_diff_stat_M &&
-       git diff --stat -C -C HEAD^..HEAD >actual_diff_stat_CC &&
+       git diff --numstat -C -C HEAD^..HEAD >actual_diff_stat_CC &&
        test_cmp expect_diff_stat_CC actual_diff_stat_CC
 '
 
index a6d1887536e240e89b8e2263e5f0a643e9a55f71..b41eb61ca8b1e66f618d480a28b2bc14815df2f7 100755 (executable)
@@ -14,12 +14,12 @@ test_expect_success setup '
        echo a >a &&
        echo b >b &&
        cat >expect <<-\EOF
-        a |    1 +
-        b |    1 +
+        a | 1 +
+        b | 1 +
         2 files changed, 2 insertions(+)
        EOF
        git diff --stat --stat-count=2 >actual &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 test_done
index 328aa8f39865fb90d4b54991f158affa9cf2e1dc..b68afefa3ce2bf8ad810cee2d6ab108d4f1219ba 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success 'preparation' '
 while read cmd args
 do
        cat >expect <<-'EOF'
-        ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa |    1 +
+        ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
        EOF
        test_expect_success "$cmd: small change with long name gives more space to the name" '
                git $cmd $args >output &&
@@ -31,7 +31,7 @@ do
        '
 
        cat >expect <<-'EOF'
-        ...aaaaaaaaaaaaaaaaaaaaaaaaaa |    1 +
+        ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
        EOF
        test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" '
                git $cmd $args --stat=40 >output &&
@@ -46,7 +46,7 @@ do
        '
 
        cat >expect <<-'EOF'
-        ...aaaaaaaaaaaaaaaaaaaaaaaaaaa |    1 +
+        ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
        EOF
        test_expect_success "$cmd --stat=...,name-width with long name" '
                git $cmd $args --stat=60,30 >output &&
@@ -82,11 +82,15 @@ test_expect_success 'preparation for big change tests' '
 cat >expect80 <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
-
+cat >expect80-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
 cat >expect200 <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
-
+cat >expect200-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
 while read verb expect cmd args
 do
        test_expect_success "$cmd $verb COLUMNS (big change)" '
@@ -94,6 +98,14 @@ do
                grep " | " output >actual &&
                test_cmp "$expect" actual
        '
+
+       test "$cmd" != diff || continue
+
+       test_expect_success "$cmd --graph $verb COLUMNS (big change)" '
+               COLUMNS=200 git $cmd $args --graph >output
+               grep " | " output >actual &&
+               test_cmp "$expect-graph" actual
+       '
 done <<\EOF
 ignores expect80 format-patch -1 --stdout
 respects expect200 diff HEAD^ HEAD --stat
@@ -104,7 +116,9 @@ EOF
 cat >expect40 <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++
 EOF
-
+cat >expect40-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++
+EOF
 while read verb expect cmd args
 do
        test_expect_success "$cmd $verb not enough COLUMNS (big change)" '
@@ -113,11 +127,41 @@ do
                test_cmp "$expect" actual
        '
 
+       test "$cmd" != diff || continue
+
+       test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" '
+               COLUMNS=40 git $cmd $args --graph >output
+               grep " | " output >actual &&
+               test_cmp "$expect-graph" actual
+       '
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect40 diff HEAD^ HEAD --stat
+respects expect40 show --stat
+respects expect40 log -1 --stat
+EOF
+
+cat >expect40 <<'EOF'
+ abcd | 1000 ++++++++++++++++++++++++++
+EOF
+cat >expect40-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++++
+EOF
+while read verb expect cmd args
+do
        test_expect_success "$cmd $verb statGraphWidth config" '
                git -c diff.statGraphWidth=26 $cmd $args >output
                grep " | " output >actual &&
                test_cmp "$expect" actual
        '
+
+       test "$cmd" != diff || continue
+
+       test_expect_success "$cmd --graph $verb statGraphWidth config" '
+               git -c diff.statGraphWidth=26 $cmd $args --graph >output
+               grep " | " output >actual &&
+               test_cmp "$expect-graph" actual
+       '
 done <<\EOF
 ignores expect80 format-patch -1 --stdout
 respects expect40 diff HEAD^ HEAD --stat
@@ -129,6 +173,9 @@ EOF
 cat >expect <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++
 EOF
+cat >expect-graph <<'EOF'
+|  abcd | 1000 ++++++++++++++++++++++++++
+EOF
 while read cmd args
 do
        test_expect_success "$cmd --stat=width with big change" '
@@ -143,11 +190,25 @@ do
                test_cmp expect actual
        '
 
-       test_expect_success "$cmd --stat-graph--width with big change" '
+       test_expect_success "$cmd --stat-graph-width with big change" '
                git $cmd $args --stat-graph-width=26 >output
                grep " | " output >actual &&
                test_cmp expect actual
        '
+
+       test "$cmd" != diff || continue
+
+       test_expect_success "$cmd --stat-width=width --graph with big change" '
+               git $cmd $args --stat-width=40 --graph >output
+               grep " | " output >actual &&
+               test_cmp expect-graph actual
+       '
+
+       test_expect_success "$cmd --stat-graph-width --graph with big change" '
+               git $cmd $args --stat-graph-width=26 --graph >output
+               grep " | " output >actual &&
+               test_cmp expect-graph actual
+       '
 done <<\EOF
 format-patch -1 --stdout
 diff HEAD^ HEAD --stat
@@ -164,6 +225,9 @@ test_expect_success 'preparation for long filename tests' '
 cat >expect <<'EOF'
  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++
 EOF
+cat >expect-graph <<'EOF'
+|  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++
+EOF
 while read cmd args
 do
        test_expect_success "$cmd --stat=width with big change is more balanced" '
@@ -171,6 +235,14 @@ do
                grep " | " output >actual &&
                test_cmp expect actual
        '
+
+       test "$cmd" != diff || continue
+
+       test_expect_success "$cmd --stat=width --graph with big change is balanced" '
+               git $cmd $args --stat-width=60 --graph >output &&
+               grep " | " output >actual &&
+               test_cmp expect-graph actual
+       '
 done <<\EOF
 format-patch -1 --stdout
 diff HEAD^ HEAD --stat
@@ -181,9 +253,15 @@ EOF
 cat >expect80 <<'EOF'
  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++
 EOF
+cat >expect80-graph <<'EOF'
+|  ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++
+EOF
 cat >expect200 <<'EOF'
  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
+cat >expect200-graph <<'EOF'
+|  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+EOF
 while read verb expect cmd args
 do
        test_expect_success "$cmd $verb COLUMNS (long filename)" '
@@ -191,6 +269,14 @@ do
                grep " | " output >actual &&
                test_cmp "$expect" actual
        '
+
+       test "$cmd" != diff || continue
+
+       test_expect_success "$cmd --graph $verb COLUMNS (long filename)" '
+               COLUMNS=200 git $cmd $args --graph >output
+               grep " | " output >actual &&
+               test_cmp "$expect-graph" actual
+       '
 done <<\EOF
 ignores expect80 format-patch -1 --stdout
 respects expect200 diff HEAD^ HEAD --stat
@@ -198,6 +284,36 @@ respects expect200 show --stat
 respects expect200 log -1 --stat
 EOF
 
+cat >expect1 <<'EOF'
+ ...aaaaaaa | 1000 ++++++
+EOF
+cat >expect1-graph <<'EOF'
+|  ...aaaaaaa | 1000 ++++++
+EOF
+while read verb expect cmd args
+do
+       test_expect_success COLUMNS_CAN_BE_1 \
+               "$cmd $verb prefix greater than COLUMNS (big change)" '
+               COLUMNS=1 git $cmd $args >output
+               grep " | " output >actual &&
+               test_cmp "$expect" actual
+       '
+
+       test "$cmd" != diff || continue
+
+       test_expect_success COLUMNS_CAN_BE_1 \
+               "$cmd --graph $verb prefix greater than COLUMNS (big change)" '
+               COLUMNS=1 git $cmd $args --graph >output
+               grep " | " output >actual &&
+               test_cmp "$expect-graph" actual
+       '
+done <<\EOF
+ignores expect80 format-patch -1 --stdout
+respects expect1 diff HEAD^ HEAD --stat
+respects expect1 show --stat
+respects expect1 log -1 --stat
+EOF
+
 cat >expect <<'EOF'
  abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 EOF
diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh
new file mode 100755 (executable)
index 0000000..4dc8c67
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='diff --no-index'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir a &&
+       mkdir b &&
+       echo 1 >a/1 &&
+       echo 2 >a/2
+'
+
+test_expect_success 'git diff --no-index directories' '
+       git diff --no-index a b >cnt
+       test $? = 1 && test_line_count = 14 cnt
+'
+
+test_done
index 9b433de83630774206fb89dfae1a4396264390cc..744b8e51beab59c78807e0622f10bf421b3142ec 100755 (executable)
@@ -17,13 +17,13 @@ do
        test_expect_success "$title" '
                git apply --stat --summary \
                        <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
-               test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+               test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
        '
 
        test_expect_success "$title with recount" '
                sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
                git apply --recount --stat --summary >current &&
-               test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+               test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
        '
 done <<\EOF
 rename
index ccc0280f52232e845f9d7c7bd5c3326b412d4a96..cdafd7e7c1e6c73a97c36a60f77810badff603f2 100755 (executable)
@@ -525,9 +525,9 @@ test_expect_success 'am empty-file does not infloop' '
        git reset --hard &&
        touch empty-file &&
        test_tick &&
-       { git am empty-file > actual 2>&1 && false || :; } &&
+       test_must_fail git am empty-file 2>actual &&
        echo Patch format detection failed. >expected &&
-       test_cmp expected actual
+       test_i18ncmp expected actual
 '
 
 test_done
index 222f7559e92caa2a0bbd128b0bb6bac14e2e113f..71be59d446f773c4b567f00c469ac26225d11cb8 100755 (executable)
@@ -516,4 +516,294 @@ test_expect_success 'show added path under "--follow -M"' '
        )
 '
 
+cat >expect <<\EOF
+*   commit COMMIT_OBJECT_NAME
+|\  Merge: MERGE_PARENTS
+| | Author: A U Thor <author@example.com>
+| |
+| |     Merge HEADS DESCRIPTION
+| |
+| * commit COMMIT_OBJECT_NAME
+| | Author: A U Thor <author@example.com>
+| |
+| |     reach
+| | ---
+| |  reach.t | 1 +
+| |  1 file changed, 1 insertion(+)
+| |
+| | diff --git a/reach.t b/reach.t
+| | new file mode 100644
+| | index 0000000..10c9591
+| | --- /dev/null
+| | +++ b/reach.t
+| | @@ -0,0 +1 @@
+| | +reach
+| |
+|  \
+*-. \   commit COMMIT_OBJECT_NAME
+|\ \ \  Merge: MERGE_PARENTS
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     Merge HEADS DESCRIPTION
+| | | |
+| | * | commit COMMIT_OBJECT_NAME
+| | |/  Author: A U Thor <author@example.com>
+| | |
+| | |       octopus-b
+| | |   ---
+| | |    octopus-b.t | 1 +
+| | |    1 file changed, 1 insertion(+)
+| | |
+| | |   diff --git a/octopus-b.t b/octopus-b.t
+| | |   new file mode 100644
+| | |   index 0000000..d5fcad0
+| | |   --- /dev/null
+| | |   +++ b/octopus-b.t
+| | |   @@ -0,0 +1 @@
+| | |   +octopus-b
+| | |
+| * | commit COMMIT_OBJECT_NAME
+| |/  Author: A U Thor <author@example.com>
+| |
+| |       octopus-a
+| |   ---
+| |    octopus-a.t | 1 +
+| |    1 file changed, 1 insertion(+)
+| |
+| |   diff --git a/octopus-a.t b/octopus-a.t
+| |   new file mode 100644
+| |   index 0000000..11ee015
+| |   --- /dev/null
+| |   +++ b/octopus-a.t
+| |   @@ -0,0 +1 @@
+| |   +octopus-a
+| |
+* | commit COMMIT_OBJECT_NAME
+|/  Author: A U Thor <author@example.com>
+|
+|       seventh
+|   ---
+|    seventh.t | 1 +
+|    1 file changed, 1 insertion(+)
+|
+|   diff --git a/seventh.t b/seventh.t
+|   new file mode 100644
+|   index 0000000..9744ffc
+|   --- /dev/null
+|   +++ b/seventh.t
+|   @@ -0,0 +1 @@
+|   +seventh
+|
+*   commit COMMIT_OBJECT_NAME
+|\  Merge: MERGE_PARENTS
+| | Author: A U Thor <author@example.com>
+| |
+| |     Merge branch 'tangle'
+| |
+| *   commit COMMIT_OBJECT_NAME
+| |\  Merge: MERGE_PARENTS
+| | | Author: A U Thor <author@example.com>
+| | |
+| | |     Merge branch 'side' (early part) into tangle
+| | |
+| * |   commit COMMIT_OBJECT_NAME
+| |\ \  Merge: MERGE_PARENTS
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     Merge branch 'master' (early part) into tangle
+| | | |
+| * | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     tangle-a
+| | | | ---
+| | | |  tangle-a | 1 +
+| | | |  1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/tangle-a b/tangle-a
+| | | | new file mode 100644
+| | | | index 0000000..7898192
+| | | | --- /dev/null
+| | | | +++ b/tangle-a
+| | | | @@ -0,0 +1 @@
+| | | | +a
+| | | |
+* | | |   commit COMMIT_OBJECT_NAME
+|\ \ \ \  Merge: MERGE_PARENTS
+| | | | | Author: A U Thor <author@example.com>
+| | | | |
+| | | | |     Merge branch 'side'
+| | | | |
+| * | | | commit COMMIT_OBJECT_NAME
+| | |_|/  Author: A U Thor <author@example.com>
+| |/| |
+| | | |       side-2
+| | | |   ---
+| | | |    2 | 1 +
+| | | |    1 file changed, 1 insertion(+)
+| | | |
+| | | |   diff --git a/2 b/2
+| | | |   new file mode 100644
+| | | |   index 0000000..0cfbf08
+| | | |   --- /dev/null
+| | | |   +++ b/2
+| | | |   @@ -0,0 +1 @@
+| | | |   +2
+| | | |
+| * | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     side-1
+| | | | ---
+| | | |  1 | 1 +
+| | | |  1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/1 b/1
+| | | | new file mode 100644
+| | | | index 0000000..d00491f
+| | | | --- /dev/null
+| | | | +++ b/1
+| | | | @@ -0,0 +1 @@
+| | | | +1
+| | | |
+* | | | commit COMMIT_OBJECT_NAME
+| | | | Author: A U Thor <author@example.com>
+| | | |
+| | | |     Second
+| | | | ---
+| | | |  one | 1 +
+| | | |  1 file changed, 1 insertion(+)
+| | | |
+| | | | diff --git a/one b/one
+| | | | new file mode 100644
+| | | | index 0000000..9a33383
+| | | | --- /dev/null
+| | | | +++ b/one
+| | | | @@ -0,0 +1 @@
+| | | | +case
+| | | |
+* | | | commit COMMIT_OBJECT_NAME
+| |_|/  Author: A U Thor <author@example.com>
+|/| |
+| | |       sixth
+| | |   ---
+| | |    a/two | 1 -
+| | |    1 file changed, 1 deletion(-)
+| | |
+| | |   diff --git a/a/two b/a/two
+| | |   deleted file mode 100644
+| | |   index 9245af5..0000000
+| | |   --- a/a/two
+| | |   +++ /dev/null
+| | |   @@ -1 +0,0 @@
+| | |   -ni
+| | |
+* | | commit COMMIT_OBJECT_NAME
+| | | Author: A U Thor <author@example.com>
+| | |
+| | |     fifth
+| | | ---
+| | |  a/two | 1 +
+| | |  1 file changed, 1 insertion(+)
+| | |
+| | | diff --git a/a/two b/a/two
+| | | new file mode 100644
+| | | index 0000000..9245af5
+| | | --- /dev/null
+| | | +++ b/a/two
+| | | @@ -0,0 +1 @@
+| | | +ni
+| | |
+* | | commit COMMIT_OBJECT_NAME
+|/ /  Author: A U Thor <author@example.com>
+| |
+| |       fourth
+| |   ---
+| |    ein | 1 +
+| |    1 file changed, 1 insertion(+)
+| |
+| |   diff --git a/ein b/ein
+| |   new file mode 100644
+| |   index 0000000..9d7e69f
+| |   --- /dev/null
+| |   +++ b/ein
+| |   @@ -0,0 +1 @@
+| |   +ichi
+| |
+* | commit COMMIT_OBJECT_NAME
+|/  Author: A U Thor <author@example.com>
+|
+|       third
+|   ---
+|    ichi | 1 +
+|    one  | 1 -
+|    2 files changed, 1 insertion(+), 1 deletion(-)
+|
+|   diff --git a/ichi b/ichi
+|   new file mode 100644
+|   index 0000000..9d7e69f
+|   --- /dev/null
+|   +++ b/ichi
+|   @@ -0,0 +1 @@
+|   +ichi
+|   diff --git a/one b/one
+|   deleted file mode 100644
+|   index 9d7e69f..0000000
+|   --- a/one
+|   +++ /dev/null
+|   @@ -1 +0,0 @@
+|   -ichi
+|
+* commit COMMIT_OBJECT_NAME
+| Author: A U Thor <author@example.com>
+|
+|     second
+| ---
+|  one | 2 +-
+|  1 file changed, 1 insertion(+), 1 deletion(-)
+|
+| diff --git a/one b/one
+| index 5626abf..9d7e69f 100644
+| --- a/one
+| +++ b/one
+| @@ -1 +1 @@
+| -one
+| +ichi
+|
+* commit COMMIT_OBJECT_NAME
+  Author: A U Thor <author@example.com>
+
+      initial
+  ---
+   one | 1 +
+   1 file changed, 1 insertion(+)
+
+  diff --git a/one b/one
+  new file mode 100644
+  index 0000000..5626abf
+  --- /dev/null
+  +++ b/one
+  @@ -0,0 +1 @@
+  +one
+EOF
+
+sanitize_output () {
+       sed -e 's/ *$//' \
+           -e 's/commit [0-9a-f]*$/commit COMMIT_OBJECT_NAME/' \
+           -e 's/Merge: [ 0-9a-f]*$/Merge: MERGE_PARENTS/' \
+           -e 's/Merge tag.*/Merge HEADS DESCRIPTION/' \
+           -e 's/Merge commit.*/Merge HEADS DESCRIPTION/' \
+           -e 's/, 0 deletions(-)//' \
+           -e 's/, 0 insertions(+)//' \
+           -e 's/ 1 files changed, / 1 file changed, /' \
+           -e 's/, 1 deletions(-)/, 1 deletion(-)/' \
+           -e 's/, 1 insertions(+)/, 1 insertion(+)/'
+}
+
+test_expect_success 'log --graph with diff and stats' '
+       git log --graph --pretty=short --stat -p >actual &&
+       sanitize_output >actual.sanitized <actual &&
+       test_cmp expect actual.sanitized
+'
+
 test_done
index 2ae9faa8b37821db6e7c28ae3d98e53cb25264b1..4afd77815f6bf698d4b865bad2300706d2a8e0fc 100755 (executable)
@@ -71,4 +71,32 @@ test_expect_success 'alias loop' '
        test_must_fail git log --pretty=test-foo
 '
 
+test_expect_success 'NUL separation' '
+       printf "add bar\0initial" >expected &&
+       git log -z --pretty="format:%s" >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'NUL termination' '
+       printf "add bar\0initial\0" >expected &&
+       git log -z --pretty="tformat:%s" >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'NUL separation with --stat' '
+       stat0_part=$(git diff --stat HEAD^ HEAD) &&
+       stat1_part=$(git diff --stat --root HEAD^) &&
+       printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n" >expected &&
+       git log -z --stat --pretty="format:%s" >actual &&
+       test_cmp expected actual
+'
+
+test_expect_failure 'NUL termination with --stat' '
+       stat0_part=$(git diff --stat HEAD^ HEAD) &&
+       stat1_part=$(git diff --stat --root HEAD^) &&
+       printf "add bar\n$stat0_part\n\0initial\n$stat1_part\n\0" >expected &&
+       git log -z --stat --pretty="tformat:%s" >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 527c9e7548517d4109a9da077cff804e072e38f1..ecf00edab2079099e201237c1936cf715148a684 100755 (executable)
@@ -31,6 +31,26 @@ GUNZIP=${GUNZIP:-gzip -d}
 
 SUBSTFORMAT=%H%n
 
+check_zip() {
+       zipfile=$1.zip
+       listfile=$1.lst
+       dir=$1
+       dir_with_prefix=$dir/$2
+
+       test_expect_success UNZIP " extract ZIP archive" "
+               (mkdir $dir && cd $dir && $UNZIP ../$zipfile)
+       "
+
+       test_expect_success UNZIP " validate filenames" "
+               (cd ${dir_with_prefix}a && find .) | sort >$listfile &&
+               test_cmp a.lst $listfile
+       "
+
+       test_expect_success UNZIP " validate file contents" "
+               diff -r a ${dir_with_prefix}a
+       "
+}
+
 test_expect_success \
     'populate workdir' \
     'mkdir a b c &&
@@ -84,6 +104,12 @@ test_expect_success \
     'git archive vs. git tar-tree' \
     'test_cmp b.tar b2.tar'
 
+test_expect_success 'git archive on large files' '
+    test_config core.bigfilethreshold 1 &&
+    git archive HEAD >b3.tar &&
+    test_cmp b.tar b3.tar
+'
+
 test_expect_success \
     'git archive in a bare repo' \
     '(cd bare.git && git archive HEAD) >b3.tar'
@@ -175,10 +201,19 @@ test_expect_success \
       test_cmp a/substfile2 g/prefix/a/substfile2
 '
 
+$UNZIP -v >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+       say "Skipping ZIP tests, because unzip was not found"
+else
+       test_set_prereq UNZIP
+fi
+
 test_expect_success \
     'git archive --format=zip' \
     'git archive --format=zip HEAD >d.zip'
 
+check_zip d
+
 test_expect_success \
     'git archive --format=zip in a bare repo' \
     '(cd bare.git && git archive --format=zip HEAD) >d1.zip'
@@ -201,42 +236,25 @@ test_expect_success 'git archive with --output, override inferred format' '
        test_cmp b.tar d4.zip
 '
 
-$UNZIP -v >/dev/null 2>&1
-if [ $? -eq 127 ]; then
-       say "Skipping ZIP tests, because unzip was not found"
-else
-       test_set_prereq UNZIP
-fi
-
-test_expect_success UNZIP \
-    'extract ZIP archive' \
-    '(mkdir d && cd d && $UNZIP ../d.zip)'
-
-test_expect_success UNZIP \
-    'validate filenames' \
-    '(cd d/a && find .) | sort >d.lst &&
-     test_cmp a.lst d.lst'
-
-test_expect_success UNZIP \
-    'validate file contents' \
-    'diff -r a d/a'
-
 test_expect_success \
     'git archive --format=zip with prefix' \
     'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
 
-test_expect_success UNZIP \
-    'extract ZIP archive with prefix' \
-    '(mkdir e && cd e && $UNZIP ../e.zip)'
+check_zip e prefix/
 
-test_expect_success UNZIP \
-    'validate filenames with prefix' \
-    '(cd e/prefix/a && find .) | sort >e.lst &&
-     test_cmp a.lst e.lst'
+test_expect_success 'git archive -0 --format=zip on large files' '
+       test_config core.bigfilethreshold 1 &&
+       git archive -0 --format=zip HEAD >large.zip
+'
 
-test_expect_success UNZIP \
-    'validate file contents with prefix' \
-    'diff -r a e/prefix/a'
+check_zip large
+
+test_expect_success 'git archive --format=zip on large files' '
+       test_config core.bigfilethreshold 1 &&
+       git archive --format=zip HEAD >large-compressed.zip
+'
+
+check_zip large-compressed
 
 test_expect_success \
     'git archive --list outside of a git repo' \
index ebc36c1758372f484055b62080d3ce81ae7c69b4..81904d9ec8d341399b534c5d576262439d0e1ad6 100755 (executable)
@@ -65,7 +65,7 @@ test_expect_success 'respect NULs' '
        git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
        test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 &&
        (cat 001 | git mailinfo msg patch) &&
-       test 4 = $(wc -l < patch)
+       test_line_count = 4 patch
 
 '
 
index 8ce155167d51de1868fdd2395c312c46b9cc2c63..02c97746d64fbfe13007a1ab4e9b9e4bbd99f42f 100644 (file)
@@ -1,5 +1,5 @@
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
index 8ce155167d51de1868fdd2395c312c46b9cc2c63..02c97746d64fbfe13007a1ab4e9b9e4bbd99f42f 100644 (file)
@@ -1,5 +1,5 @@
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
index 8ce155167d51de1868fdd2395c312c46b9cc2c63..02c97746d64fbfe13007a1ab4e9b9e4bbd99f42f 100644 (file)
@@ -1,5 +1,5 @@
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
index 7d24b24af83c861a1dc615c19cbaa392fdd69dec..ab7a38373bd857271c4d74d52e8dbda57a964ff3 100644 (file)
@@ -1,7 +1,7 @@
 ---
 
- Documentation/git-cvsimport-script.txt |    9 ++++++++-
- git-cvsimport-script                   |    4 ++--
+ Documentation/git-cvsimport-script.txt | 9 ++++++++-
+ git-cvsimport-script                   | 4 ++--
  2 files changed, 10 insertions(+), 3 deletions(-)
 
 50452f9c0c2df1f04d83a26266ba704b13861632
index 8ce155167d51de1868fdd2395c312c46b9cc2c63..02c97746d64fbfe13007a1ab4e9b9e4bbd99f42f 100644 (file)
@@ -1,5 +1,5 @@
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
index f055481d567212b7b43e7ca8251351b3f83b143b..436821c97aa52d7ab599dc43315a0379de32cdb6 100644 (file)
@@ -1,5 +1,5 @@
 ---
- builtin-mailinfo.c |    2 +-
+ builtin-mailinfo.c | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 8841d3c13916a85dec9e14762eb1406735f4744e..0988713761d4a6adc1039f8f4edbe77ea58d5ea4 100644 (file)
@@ -1,5 +1,5 @@
 ---
- builtin-mailinfo.c  |    4 ++--
+ builtin-mailinfo.c  | 4 ++--
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
 index 3e5fe51..aabfe5c 100644
index 124efd234f905c4981718ee7574e213316a31acb..3f3825f9f297983f5270f09971ecef00cac7ae15 100644 (file)
@@ -1,5 +1,5 @@
 ---
- builtin-mailinfo.c |   37 ++++++++++++++++++++++++++++++++++++-
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
  1 files changed, 36 insertions(+), 1 deletions(-)
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 124efd234f905c4981718ee7574e213316a31acb..3f3825f9f297983f5270f09971ecef00cac7ae15 100644 (file)
@@ -1,5 +1,5 @@
 ---
- builtin-mailinfo.c |   37 ++++++++++++++++++++++++++++++++++++-
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
  1 files changed, 36 insertions(+), 1 deletions(-)
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index de1031241d99a66db0653875ea163785691d12d6..34a09a0fc1c0ca9d1e68ed8bbf56e592c54e7227 100644 (file)
@@ -12,7 +12,7 @@ Subject: [PATCH] a commit.
 Here is a patch from A U Thor.
 
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
@@ -52,7 +52,7 @@ two truly blank and another full of spaces in between.
 Hope this helps.
 
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
@@ -83,7 +83,7 @@ Message-Id: <nitpicker.12121212@example.net>
 Hopefully this would fix the problem stated there.
 
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
@@ -249,8 +249,8 @@ actual flags.
 Signed-off-by: David K=E5gedal <davidk@lysator.liu.se>
 ---
 
- Documentation/git-cvsimport-script.txt |    9 ++++++++-
- git-cvsimport-script                   |    4 ++--
+ Documentation/git-cvsimport-script.txt | 9 ++++++++-
+ git-cvsimport-script                   | 4 ++--
  2 files changed, 10 insertions(+), 3 deletions(-)
 
 50452f9c0c2df1f04d83a26266ba704b13861632
@@ -379,7 +379,7 @@ Subject: [PATCH] a commit.
 Here is a patch from A U Thor.
 
 ---
- foo |    2 +-
+ foo | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/foo b/foo
@@ -449,7 +449,7 @@ memcmp("Subject: ", header[i], 7) will never match.
 Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
 Signed-off-by: Junio C Hamano <gitster@pobox.com>
 ---
- builtin-mailinfo.c |    2 +-
+ builtin-mailinfo.c | 2 +-
  1 files changed, 1 insertions(+), 1 deletions(-)
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
@@ -482,7 +482,7 @@ Content-Transfer-Encoding: quoted-printable
 Here comes a commit log message, and
 its second line is here.
 ---
- builtin-mailinfo.c  |    4 ++--
+ builtin-mailinfo.c  | 4 ++--
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
 index 3e5fe51..aabfe5c 100644
@@ -587,7 +587,7 @@ everything before it in the message body.
 
 Signed-off-by: Junio C Hamano <gitster@pobox.com>
 ---
- builtin-mailinfo.c |   37 ++++++++++++++++++++++++++++++++++++-
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
  1 files changed, 36 insertions(+), 1 deletions(-)
 
 diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 2af8947eebb3e9ee45f83acb398335ec163a521c..432f98c357601057cb89f9dd6bfbe1ab02e9477a 100755 (executable)
@@ -216,7 +216,7 @@ test_expect_success 'pull request format' '
                git request-pull initial "$downstream_url" >../request
        ) &&
        <request sed -nf fuzz.sed >request.fuzzy &&
-       test_cmp expect request.fuzzy
+       test_i18ncmp expect request.fuzzy
 
 '
 
index ce51692bb2b9ae221d11458a01ab8ef669f24659..1d1ca98588bd7e8ae264179b5a4a93371c567346 100755 (executable)
@@ -326,4 +326,70 @@ EOF
        test_cmp count7.expected count7.actual
 '
 
+test_expect_success 'setup tests for the --stdin parameter' '
+       for head in C D E F
+       do
+               add $head
+       done &&
+       for head in A B C D E F
+       do
+               git tag $head $head
+       done &&
+       cat >input <<-\EOF
+       refs/heads/C
+       refs/heads/A
+       refs/heads/D
+       refs/tags/C
+       refs/heads/B
+       refs/tags/A
+       refs/heads/E
+       refs/tags/B
+       refs/tags/E
+       refs/tags/D
+       EOF
+       sort <input >expect &&
+       (
+               echo refs/heads/E &&
+               echo refs/tags/E &&
+               cat input
+       ) >input.dup
+'
+
+test_expect_success 'fetch refs from cmdline' '
+       (
+               cd client &&
+               git fetch-pack --no-progress .. $(cat ../input)
+       ) >output &&
+       cut -d " " -f 2 <output | sort >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fetch refs from stdin' '
+       (
+               cd client &&
+               git fetch-pack --stdin --no-progress .. <../input
+       ) >output &&
+       cut -d " " -f 2 <output | sort >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fetch mixed refs from cmdline and stdin' '
+       (
+               cd client &&
+               tail -n +5 ../input |
+               git fetch-pack --stdin --no-progress .. $(head -n 4 ../input)
+       ) >output &&
+       cut -d " " -f 2 <output | sort >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'test duplicate refs from stdin' '
+       (
+       cd client &&
+       test_must_fail git fetch-pack --stdin --no-progress .. <../input.dup
+       ) >output &&
+       cut -d " " -f 2 <output | sort >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 308c02ea75f3400b4d215e6091f03179a93943ad..d7a19a182941c7ab54b81bb8013835cfb8c5919f 100755 (executable)
@@ -162,6 +162,36 @@ test_expect_success 'fetch following tags' '
 
 '
 
+test_expect_success 'fetch uses remote ref names to describe new refs' '
+       cd "$D" &&
+       git init descriptive &&
+       (
+               cd descriptive &&
+               git config remote.o.url .. &&
+               git config remote.o.fetch "refs/heads/*:refs/crazyheads/*" &&
+               git config --add remote.o.fetch "refs/others/*:refs/heads/*" &&
+               git fetch o
+       ) &&
+       git tag -a -m "Descriptive tag" descriptive-tag &&
+       git branch descriptive-branch &&
+       git checkout descriptive-branch &&
+       echo "Nuts" >crazy &&
+       git add crazy &&
+       git commit -a -m "descriptive commit" &&
+       git update-ref refs/others/crazy HEAD &&
+       (
+               cd descriptive &&
+               git fetch o 2>actual &&
+               grep " -> refs/crazyheads/descriptive-branch$" actual |
+               test_i18ngrep "new branch" &&
+               grep " -> descriptive-tag$" actual |
+               test_i18ngrep "new tag" &&
+               grep " -> crazy$" actual |
+               test_i18ngrep "new ref"
+       ) &&
+       git checkout master
+'
+
 test_expect_success 'fetch must not resolve short tag name' '
 
        cd "$D" &&
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
new file mode 100755 (executable)
index 0000000..4736da8
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+test_description='check various push.default settings'
+. ./test-lib.sh
+
+test_expect_success 'setup bare remotes' '
+       git init --bare repo1 &&
+       git remote add parent1 repo1 &&
+       git init --bare repo2 &&
+       git remote add parent2 repo2 &&
+       test_commit one &&
+       git push parent1 HEAD &&
+       git push parent2 HEAD
+'
+
+# $1 = local revision
+# $2 = remote revision (tested to be equal to the local one)
+check_pushed_commit () {
+       git log -1 --format='%h %s' "$1" >expect &&
+       git --git-dir=repo1 log -1 --format='%h %s' "$2" >actual &&
+       test_cmp expect actual
+}
+
+# $1 = push.default value
+# $2 = expected target branch for the push
+test_push_success () {
+       git -c push.default="$1" push &&
+       check_pushed_commit HEAD "$2"
+}
+
+# $1 = push.default value
+# check that push fails and does not modify any remote branch
+test_push_failure () {
+       git --git-dir=repo1 log --no-walk --format='%h %s' --all >expect &&
+       test_must_fail git -c push.default="$1" push &&
+       git --git-dir=repo1 log --no-walk --format='%h %s' --all >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success '"upstream" pushes to configured upstream' '
+       git checkout master &&
+       test_config branch.master.remote parent1 &&
+       test_config branch.master.merge refs/heads/foo &&
+       test_commit two &&
+       test_push_success upstream foo
+'
+
+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
+'
+
+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
+'
+
+test_expect_success '"upstream" does not push when remotes do not match' '
+       git checkout master &&
+       test_config branch.master.remote parent1 &&
+       test_config branch.master.merge refs/heads/foo &&
+       test_config push.default upstream &&
+       test_commit five &&
+       test_must_fail git push parent2
+'
+
+test_expect_success 'push from/to new branch with upstream, matching and simple' '
+       git checkout -b new-branch &&
+       test_push_failure simple &&
+       test_push_failure matching &&
+       test_push_failure upstream
+'
+
+test_expect_success 'push from/to new branch with current creates remote branch' '
+       test_config branch.new-branch.remote repo1 &&
+       git checkout new-branch &&
+       test_push_success current new-branch
+'
+
+test_expect_success 'push to existing branch, with no upstream configured' '
+       test_config branch.master.remote repo1 &&
+       git checkout master &&
+       test_push_failure simple &&
+       test_push_failure upstream
+'
+
+test_expect_success 'push to existing branch, upstream configured with same name' '
+       test_config branch.master.remote repo1 &&
+       test_config branch.master.merge refs/heads/master &&
+       git checkout master &&
+       test_commit six &&
+       test_push_success upstream master &&
+       test_commit seven &&
+       test_push_success simple master
+'
+
+test_expect_success 'push to existing branch, upstream configured with different name' '
+       test_config branch.master.remote repo1 &&
+       test_config branch.master.merge refs/heads/other-name &&
+       git checkout master &&
+       test_commit eight &&
+       test_push_success upstream other-name &&
+       test_commit nine &&
+       test_push_failure simple &&
+       git --git-dir=repo1 log -1 --format="%h %s" "other-name" >expect-other-name &&
+       test_push_success current master &&
+       git --git-dir=repo1 log -1 --format="%h %s" "other-name" >actual-other-name &&
+       test_cmp expect-other-name actual-other-name
+'
+
+test_done
index 30bec4b5f9f286cd97c57b70e3635c6c5c8cf85a..1947c28c6466d46c79c7b1b093488c1620324172 100755 (executable)
@@ -119,4 +119,98 @@ test_expect_success 'push succeeds if submodule has no remote and is on the firs
        )
 '
 
+test_expect_success 'push unpushed submodules when not needed' '
+       (
+               cd work &&
+               (
+                       cd gar/bage &&
+                       git checkout master &&
+                       >junk5 &&
+                       git add junk5 &&
+                       git commit -m "Fifth junk" &&
+                       git push &&
+                       git rev-parse origin/master >../../../expected
+               ) &&
+               git checkout master &&
+               git add gar/bage &&
+               git commit -m "Fifth commit for gar/bage" &&
+               git push --recurse-submodules=on-demand ../pub.git master
+       ) &&
+       (
+               cd submodule.git &&
+               git rev-parse master >../actual
+       ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'push unpushed submodules when not needed 2' '
+       (
+               cd submodule.git &&
+               git rev-parse master >../expected
+       ) &&
+       (
+               cd work &&
+               (
+                       cd gar/bage &&
+                       >junk6 &&
+                       git add junk6 &&
+                       git commit -m "Sixth junk"
+               ) &&
+               >junk2 &&
+               git add junk2 &&
+               git commit -m "Second junk for work" &&
+               git push --recurse-submodules=on-demand ../pub.git master
+       ) &&
+       (
+               cd submodule.git &&
+               git rev-parse master >../actual
+       ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'push unpushed submodules recursively' '
+       (
+               cd work &&
+               (
+                       cd gar/bage &&
+                       git checkout master &&
+                       > junk7 &&
+                       git add junk7 &&
+                       git commit -m "Seventh junk" &&
+                       git rev-parse master >../../../expected
+               ) &&
+               git checkout master &&
+               git add gar/bage &&
+               git commit -m "Seventh commit for gar/bage" &&
+               git push --recurse-submodules=on-demand ../pub.git master
+       ) &&
+       (
+               cd submodule.git &&
+               git rev-parse master >../actual
+       ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'push unpushable submodule recursively fails' '
+       (
+               cd work &&
+               (
+                       cd gar/bage &&
+                       git rev-parse origin/master >../../../expected &&
+                       git checkout master~0 &&
+                       > junk8 &&
+                       git add junk8 &&
+                       git commit -m "Eighth junk"
+               ) &&
+               git add gar/bage &&
+               git commit -m "Eighth commit for gar/bage" &&
+               test_must_fail git push --recurse-submodules=on-demand ../pub.git master
+       ) &&
+       (
+               cd submodule.git &&
+               git rev-parse master >../actual
+       ) &&
+       test_cmp expected actual
+'
+
 test_done
index cc6f081711002b42bcf6b2cb26287dcc56852a06..312e484090d1ceb392399768f2229ae553182fe7 100755 (executable)
@@ -30,6 +30,7 @@ test_expect_success 'setup remote repository' '
        git clone --bare test_repo test_repo.git &&
        cd test_repo.git &&
        git config http.receivepack true &&
+       git config core.logallrefupdates true &&
        ORIG_HEAD=$(git rev-parse --verify HEAD) &&
        cd - &&
        mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
@@ -167,7 +168,7 @@ test_expect_success 'push fails for non-fast-forward refs unmatched by remote he
 '
 
 test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' '
-       test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" \
+       test_i18ngrep "Updates were rejected because" \
                output
 '
 
@@ -215,12 +216,55 @@ test_expect_success 'push --mirror to repo with alternates' '
        git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git
 '
 
-test_expect_success TTY 'quiet push' '
+test_expect_success TTY 'push shows progress when stderr is a tty' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit noisy &&
+       test_terminal git push >output 2>&1 &&
+       grep "^Writing objects" output
+'
+
+test_expect_success TTY 'push --quiet silences status and progress' '
        cd "$ROOT_PATH"/test_repo_clone &&
        test_commit quiet &&
-       test_terminal git push --quiet --no-progress 2>&1 | tee output &&
+       test_terminal git push --quiet >output 2>&1 &&
        test_cmp /dev/null output
 '
 
+test_expect_success TTY 'push --no-progress silences progress but not status' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit no-progress &&
+       test_terminal git push --no-progress >output 2>&1 &&
+       grep "^To http" output &&
+       ! grep "^Writing objects"
+'
+
+test_expect_success 'push --progress shows progress to non-tty' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit progress &&
+       git push --progress >output 2>&1 &&
+       grep "^To http" output &&
+       grep "^Writing objects" output
+'
+
+test_expect_success 'http push gives sane defaults to reflog' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit reflog-test &&
+       git push "$HTTPD_URL"/smart/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -g -1 --format="%gn <%ge>" >actual &&
+       echo "anonymous <anonymous@http.127.0.0.1>" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
+       cd "$ROOT_PATH"/test_repo_clone &&
+       test_commit custom-reflog-test &&
+       git push "$HTTPD_URL"/smart_custom_env/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -g -1 --format="%gn <%ge>" >actual &&
+       echo "Custom User <custom@example.com>" >expect &&
+       test_cmp expect actual
+'
+
 stop_httpd
 test_done
index e5e6b8f643206c2d4fd01e3ad71ca50a43f3da19..b06f817af32483631b3ec297246e156400bc6b93 100755 (executable)
@@ -13,17 +13,22 @@ LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
 start_httpd
 
 test_expect_success 'setup repository' '
-       echo content >file &&
+       echo content1 >file &&
        git add file &&
        git commit -m one
+       echo content2 >file &&
+       git add file &&
+       git commit -m two
 '
 
-test_expect_success 'create http-accessible bare repository' '
-       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+test_expect_success 'create http-accessible bare repository with loose objects' '
+       cp -a .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
        (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-        git --bare init &&
+        git config core.bare true &&
+        mkdir -p hooks &&
         echo "exec git update-server-info" >hooks/post-update &&
-        chmod +x hooks/post-update
+        chmod +x hooks/post-update &&
+        hooks/post-update
        ) &&
        git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
        git push public master:master
index 26d355725f5e8d317c71cb466ea091ec8f741d63..be6094be774587314a5dd249403eaaa313afde70 100755 (executable)
@@ -109,5 +109,36 @@ test_expect_success 'follow redirects (302)' '
        git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
 '
 
+test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
+
+test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
+       (
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       for i in `seq 50000`
+       do
+               echo "commit refs/heads/too-many-refs"
+               echo "mark :$i"
+               echo "committer git <git@example.com> $i +0000"
+               echo "data 0"
+               echo "M 644 inline bla.txt"
+               echo "data 4"
+               echo "bla"
+               # make every commit dangling by always
+               # rewinding the branch after each commit
+               echo "reset refs/heads/too-many-refs"
+               echo "from :1"
+       done | git fast-import --export-marks=marks &&
+
+       # now assign tags to all the dangling commits we created above
+       tag=$(perl -e "print \"bla\" x 30") &&
+       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
+'
+
 stop_httpd
 test_done
index 7cbc9994a3d99530d57a4552cfb0ee7dd29c99de..a3a4e47e1d25379e981373e67124c9efe5c76213 100755 (executable)
@@ -103,14 +103,12 @@ test_remote_error()
                esac
        done
 
-       if test $# -ne 3
-       then
-               error "invalid number of arguments"
-       fi
-
+       msg=$1
+       shift
        cmd=$1
-       repo=$2
-       msg=$3
+       shift
+       repo=$1
+       shift || error "invalid number of arguments"
 
        if test -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo"
        then
@@ -122,7 +120,7 @@ test_remote_error()
                fi
        fi
 
-       test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" 2>output &&
+       test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output &&
        echo "fatal: remote error: $msg: /$repo" >expect &&
        test_cmp expect output
        ret=$?
@@ -131,18 +129,18 @@ test_remote_error()
 }
 
 msg="access denied or repository not exported"
-test_expect_success 'clone non-existent' "test_remote_error    clone nowhere.git '$msg'"
-test_expect_success 'push disabled'      "test_remote_error    push  repo.git    '$msg'"
-test_expect_success 'read access denied' "test_remote_error -x fetch repo.git    '$msg'"
-test_expect_success 'not exported'       "test_remote_error -n fetch repo.git    '$msg'"
+test_expect_success 'clone non-existent' "test_remote_error    '$msg' clone nowhere.git    "
+test_expect_success 'push disabled'      "test_remote_error    '$msg' push  repo.git master"
+test_expect_success 'read access denied' "test_remote_error -x '$msg' fetch repo.git       "
+test_expect_success 'not exported'       "test_remote_error -n '$msg' fetch repo.git       "
 
 stop_git_daemon
 start_git_daemon --informative-errors
 
-test_expect_success 'clone non-existent' "test_remote_error    clone nowhere.git 'no such repository'"
-test_expect_success 'push disabled'      "test_remote_error    push  repo.git    'service not enabled'"
-test_expect_success 'read access denied' "test_remote_error -x fetch repo.git    'no such repository'"
-test_expect_success 'not exported'       "test_remote_error -n fetch repo.git    'repository not exported'"
+test_expect_success 'clone non-existent' "test_remote_error    'no such repository'      clone nowhere.git    "
+test_expect_success 'push disabled'      "test_remote_error    'service not enabled'     push  repo.git master"
+test_expect_success 'read access denied' "test_remote_error -x 'no such repository'      fetch repo.git       "
+test_expect_success 'not exported'       "test_remote_error -n 'repository not exported' fetch repo.git       "
 
 stop_git_daemon
 test_done
index bbc4691bd7ef1e3633d4a66440211179fae42a84..c47d450cc3731cb471aa8485178f517bb0d6cbf5 100755 (executable)
@@ -34,7 +34,7 @@ test_expect_success 'cloning with reference (-l -s)' \
 cd "$base_dir"
 
 test_expect_success 'existence of info/alternates' \
-'test `wc -l <C/.git/objects/info/alternates` = 2'
+'test_line_count = 2 C/.git/objects/info/alternates'
 
 cd "$base_dir"
 
@@ -63,7 +63,7 @@ test_expect_success 'fetched no objects' \
 cd "$base_dir"
 
 test_expect_success 'existence of info/alternates' \
-'test `wc -l <D/.git/objects/info/alternates` = 1'
+'test_line_count = 1 D/.git/objects/info/alternates'
 
 cd "$base_dir"
 
index ef7127c1b3943a494692ac8027ec321608a31b9c..aa045295dec5af9dedc25495668d4afd6022d2cd 100755 (executable)
@@ -18,7 +18,7 @@ reachable_via() {
 
 test_valid_repo() {
        git fsck --full > fsck.log &&
-       test `wc -l < fsck.log` = 0
+       test_line_count = 0 fsck.log
 }
 
 base_dir=`pwd`
index 1c62001fce76a73cc951dd18bcf6cb6f650f69e8..57023345100d3c04f9bfb1b2a701c53b6e55deed 100755 (executable)
@@ -72,6 +72,19 @@ test_expect_success 'pushing to local repo' '
        compare_refs localclone HEAD server HEAD
 '
 
+# Generally, skip this test.  It demonstrates a now-fixed race in
+# git-remote-testgit, but is too slow to leave in for general use.
+: test_expect_success 'racily pushing to local repo' '
+       test_when_finished "rm -rf server2 localclone2" &&
+       cp -a server server2 &&
+       git clone "testgit::${PWD}/server2" localclone2 &&
+       (cd localclone2 &&
+       echo content >>file &&
+       git commit -a -m three &&
+       GIT_REMOTE_TESTGIT_SLEEPY=2 git push) &&
+       compare_refs localclone2 HEAD server2 HEAD
+'
+
 test_expect_success 'synch with changes from localclone' '
        (cd clone &&
         git pull)
index 444279077e803ca96e48281ae956ea6536317608..a01d2445022ecb82fe0e31739527339fa02e44a5 100755 (executable)
@@ -188,23 +188,23 @@ test_expect_success 'empty email' '
 
 test_expect_success 'del LF before empty (1)' '
        git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD^^ >actual &&
-       test $(wc -l <actual) = 2
+       test_line_count = 2 actual
 '
 
 test_expect_success 'del LF before empty (2)' '
        git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD >actual &&
-       test $(wc -l <actual) = 6 &&
+       test_line_count = 6 actual &&
        grep "^$" actual
 '
 
 test_expect_success 'add LF before non-empty (1)' '
        git show -s --pretty=format:"%s%+b%nThanks%n" HEAD^^ >actual &&
-       test $(wc -l <actual) = 2
+       test_line_count = 2 actual
 '
 
 test_expect_success 'add LF before non-empty (2)' '
        git show -s --pretty=format:"%s%+b%nThanks%n" HEAD >actual &&
-       test $(wc -l <actual) = 6 &&
+       test_line_count = 6 actual &&
        grep "^$" actual
 '
 
@@ -278,8 +278,9 @@ test_expect_success 'oneline with empty message' '
        git commit -m "dummy" --allow-empty &&
        git filter-branch --msg-filter "sed -e s/dummy//" HEAD^^.. &&
        git rev-list --oneline HEAD >test.txt &&
-       test $(git rev-list --oneline HEAD | wc -l) -eq 5 &&
-       test $(git rev-list --oneline --graph HEAD | wc -l) -eq 5
+       test_line_count = 5 test.txt &&
+       git rev-list --oneline --graph HEAD >testg.txt &&
+       test_line_count = 5 testg.txt
 '
 
 test_done
index 9d8584e957a26cadda2f04d38d27fd0c4b97ae29..1104249182074c1a987905e1661356ff8da7580c 100755 (executable)
@@ -884,4 +884,20 @@ test_expect_success 'no spurious "refusing to lose untracked" message' '
        ! grep "refusing to lose untracked file" errors.txt
 '
 
+test_expect_success 'do not follow renames for empty files' '
+       git checkout -f -b empty-base &&
+       >empty1 &&
+       git add empty1 &&
+       git commit -m base &&
+       echo content >empty1 &&
+       git add empty1 &&
+       git commit -m fill &&
+       git checkout -b empty-topic HEAD^ &&
+       git mv empty1 empty2 &&
+       git commit -m rename &&
+       test_must_fail git merge empty-base &&
+       >expect &&
+       test_cmp expect empty2
+'
+
 test_done
index a91644e3b2ac3490cfe49d5e67c9736197cd56a1..c518e9c30ccbb12c504d25aa965f8941a3989d8d 100755 (executable)
@@ -16,7 +16,12 @@ test_expect_success setup '
        test_tick &&
        git commit -m second &&
        git tag c1 &&
-       git branch test
+       git branch test &&
+       echo third >file &&
+       git add file &&
+       test_tick &&
+       git commit -m third &&
+       git tag c2
 '
 
 test_expect_success 'merge -s recursive up-to-date' '
@@ -74,4 +79,14 @@ test_expect_success 'merge -s subtree up-to-date' '
 
 '
 
+test_expect_success 'merge fast-forward octopus' '
+
+       git reset --hard c0 &&
+       test_tick &&
+       git merge c1 c2
+       expect=$(git rev-parse c2) &&
+       current=$(git rev-parse HEAD) &&
+       test "$expect" = "$current"
+'
+
 test_done
index 691e4a4481eba2994ec40e498d4cd25fcff67f26..72e28ee5350926f3c4f27e2c99f8323a3eb8e57c 100755 (executable)
@@ -480,7 +480,7 @@ test_expect_success 'many merge bases creation' '
        git merge -m "merge HASH7 and SIDE_HASH7" "$HASH7" &&
        B_HASH=$(git rev-parse --verify HEAD) &&
        git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt &&
-       test $(wc -l < merge_bases.txt) = "2" &&
+       test_line_count = 2 merge_bases.txt &&
        grep "$HASH5" merge_bases.txt &&
        grep "$SIDE_HASH5" merge_bases.txt
 '
index 94f010be8a17b8aaa8099d1a54ad2bd4317b67dc..15beecc3c6391fea89ffd5f0b6a091f19f9fce19 100755 (executable)
@@ -97,7 +97,7 @@ test_expect_success 'setup large simple rename' '
 test_expect_success 'massive simple rename does not spam added files' '
        sane_unset GIT_MERGE_VERBOSITY &&
        git merge --no-stat simple-rename | grep -v Removing >output &&
-       test 5 -gt "$(wc -l < output)"
+       test_line_count -lt 5 output
 '
 
 test_done
index 19272bc551277903bc1c444f4f0f05d8f2d7d672..ec2b516c3f79901ca5593f1edb97455e3fa8389e 100755 (executable)
@@ -71,13 +71,13 @@ test_expect_success 'checkout' '
        (
                cd test && git checkout b1
        ) >actual &&
-       grep "have 1 and 1 different" actual
+       test_i18ngrep "have 1 and 1 different" actual
 '
 
 test_expect_success 'checkout with local tracked branch' '
        git checkout master &&
        git checkout follower >actual &&
-       grep "is ahead of" actual
+       test_i18ngrep "is ahead of" actual
 '
 
 test_expect_success 'status' '
@@ -87,14 +87,14 @@ test_expect_success 'status' '
                # reports nothing to commit
                test_must_fail git commit --dry-run
        ) >actual &&
-       grep "have 1 and 1 different" actual
+       test_i18ngrep "have 1 and 1 different" actual
 '
 
 test_expect_success 'fail to track lightweight tags' '
        git checkout master &&
        git tag light &&
        test_must_fail git branch --track lighttrack light >actual &&
-       test_must_fail grep "set up to track" actual &&
+       test_i18ngrep ! "set up to track" actual &&
        test_must_fail git checkout lighttrack
 '
 
@@ -102,7 +102,7 @@ test_expect_success 'fail to track annotated tags' '
        git checkout master &&
        git tag -m heavy heavy &&
        test_must_fail git branch --track heavytrack heavy >actual &&
-       test_must_fail grep "set up to track" actual &&
+       test_i18ngrep ! "set up to track" actual &&
        test_must_fail git checkout heavytrack
 '
 
index 32591f94135755847b406389aba5779e73cff5e0..466fa3804bc8a840d994e9d2e9092f599cc87eab 100755 (executable)
@@ -104,7 +104,7 @@ test_expect_failure 'conflict caused if rename not detected' '
        test 0 -eq $(git ls-files -u | wc -l) &&
        test 0 -eq $(git ls-files -o | wc -l) &&
 
-       test 6 -eq $(wc -l < c) &&
+       test_line_count = 6 c &&
        test $(git rev-parse HEAD:a) = $(git rev-parse B:a) &&
        test $(git rev-parse HEAD:b) = $(git rev-parse A:b)
 '
index 9a168069217ef8d82173e563a04eaefe58d99f2a..9b50f54cc2d1cfb790b0fb68f71b9c1719061b7f 100755 (executable)
@@ -35,15 +35,18 @@ test_expect_success setup '
 
        echo "l3" >two &&
        test_tick &&
-       git commit -a -m "Left #3" &&
+       GIT_COMMITTER_NAME="Another Committer" \
+       GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #3" &&
 
        echo "l4" >two &&
        test_tick &&
-       git commit -a -m "Left #4" &&
+       GIT_COMMITTER_NAME="Another Committer" \
+       GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #4" &&
 
        echo "l5" >two &&
        test_tick &&
-       git commit -a -m "Left #5" &&
+       GIT_COMMITTER_NAME="Another Committer" \
+       GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #5" &&
        git tag tag-l5 &&
 
        git checkout right &&
@@ -99,6 +102,8 @@ test_expect_success '[merge] summary/log configuration' '
        cat >expected <<-EOF &&
        Merge branch ${apos}left${apos}
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * left:
          Left #5
          Left #4
@@ -144,6 +149,8 @@ test_expect_success 'merge.log=3 limits shortlog length' '
        cat >expected <<-EOF &&
        Merge branch ${apos}left${apos}
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * left: (5 commits)
          Left #5
          Left #4
@@ -159,6 +166,8 @@ test_expect_success 'merge.log=5 shows all 5 commits' '
        cat >expected <<-EOF &&
        Merge branch ${apos}left${apos}
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * left:
          Left #5
          Left #4
@@ -181,6 +190,8 @@ test_expect_success '--log=3 limits shortlog length' '
        cat >expected <<-EOF &&
        Merge branch ${apos}left${apos}
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * left: (5 commits)
          Left #5
          Left #4
@@ -196,6 +207,8 @@ test_expect_success '--log=5 shows all 5 commits' '
        cat >expected <<-EOF &&
        Merge branch ${apos}left${apos}
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * left:
          Left #5
          Left #4
@@ -225,6 +238,8 @@ test_expect_success 'fmt-merge-msg -m' '
        cat >expected.log <<-EOF &&
        Sync with left
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * ${apos}left${apos} of $(pwd):
          Left #5
          Left #4
@@ -256,6 +271,8 @@ test_expect_success 'setup: expected shortlog for two branches' '
        cat >expected <<-EOF
        Merge branches ${apos}left${apos} and ${apos}right${apos}
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * left:
          Left #5
          Left #4
@@ -379,6 +396,8 @@ test_expect_success 'merge-msg two tags' '
          Common #2
          Common #1
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * tag ${apos}tag-l5${apos}:
          Left #5
          Left #4
@@ -407,6 +426,8 @@ test_expect_success 'merge-msg tag and branch' '
          Common #2
          Common #1
 
+       By Another Author (3) and A U Thor (2)
+       via Another Committer
        * left:
          Left #5
          Left #4
index f8c247a7500d723e46796e7b9b76b9812e35db9b..518944653477225da94db0826d77936eaa0d249a 100755 (executable)
@@ -263,6 +263,50 @@ test_expect_success 'tag -l can accept multiple patterns' '
        test_cmp expect actual
 '
 
+test_expect_success 'listing tags in column' '
+       COLUMNS=40 git tag -l --column=row >actual &&
+       cat >expected <<\EOF &&
+a1      aa1     cba     t210    t211
+v0.2.1  v1.0    v1.0.1  v1.1.3
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'listing tags in column with column.*' '
+       git config column.tag row &&
+       git config column.ui dense &&
+       COLUMNS=40 git tag -l >actual &&
+       git config --unset column.ui &&
+       git config --unset column.tag &&
+       cat >expected <<\EOF &&
+a1      aa1   cba     t210    t211
+v0.2.1  v1.0  v1.0.1  v1.1.3
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'listing tag with -n --column should fail' '
+       test_must_fail git tag --column -n
+'
+
+test_expect_success 'listing tags -n in column with column.ui ignored' '
+       git config column.ui "row dense" &&
+       COLUMNS=40 git tag -l -n >actual &&
+       git config --unset column.ui &&
+       cat >expected <<\EOF &&
+a1              Foo
+aa1             Foo
+cba             Foo
+t210            Foo
+t211            Foo
+v0.2.1          Foo
+v1.0            Foo
+v1.0.1          Foo
+v1.1.3          Foo
+EOF
+       test_cmp expected actual
+'
+
 # creating and verifying lightweight tags:
 
 test_expect_success \
index 07fb53adcbc06e260b078de546bd07f11093071d..be9672e5a0222f0a796f400b2c22c615fff195a4 100755 (executable)
@@ -229,7 +229,7 @@ test_expect_success 'checkout to detach HEAD (with advice declined)' '
        git checkout -f renamer && git clean -f &&
        git checkout renamer^ 2>messages &&
        test_i18ngrep "HEAD is now at 7329388" messages &&
-       test 1 -eq $(wc -l <messages) &&
+       test_line_count = 1 messages &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/master) &&
        test "z$H" = "z$M" &&
@@ -247,7 +247,7 @@ test_expect_success 'checkout to detach HEAD' '
        git checkout -f renamer && git clean -f &&
        git checkout renamer^ 2>messages &&
        test_i18ngrep "HEAD is now at 7329388" messages &&
-       test 1 -lt $(wc -l <messages) &&
+       test_line_count -gt 1 messages &&
        H=$(git rev-parse --verify HEAD) &&
        M=$(git show-ref -s --verify refs/heads/master) &&
        test "z$H" = "z$M" &&
index 800b5368a5248835bb9817c0e0c8409131306b3c..ccfb54de7ad9473221390d019b109bcb010a2c76 100755 (executable)
@@ -399,8 +399,8 @@ test_expect_success SANITY 'removal failure' '
 '
 
 test_expect_success 'nested git work tree' '
-       rm -fr foo bar &&
-       mkdir foo bar &&
+       rm -fr foo bar baz &&
+       mkdir -p foo bar baz/boo &&
        (
                cd foo &&
                git init &&
@@ -412,15 +412,24 @@ test_expect_success 'nested git work tree' '
                cd bar &&
                >goodbye.people
        ) &&
+       (
+               cd baz/boo &&
+               git init &&
+               >deeper.world
+               git add . &&
+               git commit -a -m deeply.nested
+       ) &&
        git clean -f -d &&
        test -f foo/.git/index &&
        test -f foo/hello.world &&
+       test -f baz/boo/.git/index &&
+       test -f baz/boo/deeper.world &&
        ! test -d bar
 '
 
 test_expect_success 'force removal of nested git work tree' '
-       rm -fr foo bar &&
-       mkdir foo bar &&
+       rm -fr foo bar baz &&
+       mkdir -p foo bar baz/boo &&
        (
                cd foo &&
                git init &&
@@ -432,9 +441,17 @@ test_expect_success 'force removal of nested git work tree' '
                cd bar &&
                >goodbye.people
        ) &&
+       (
+               cd baz/boo &&
+               git init &&
+               >deeper.world
+               git add . &&
+               git commit -a -m deeply.nested
+       ) &&
        git clean -f -f -d &&
        ! test -d foo &&
-       ! test -d bar
+       ! test -d bar &&
+       ! test -d baz
 '
 
 test_expect_success 'git clean -e' '
index b377a7af28c9dde11bd4cf6adcf0d7eae31a0754..81827e696f21f598357313d6dad94400d8562718 100755 (executable)
@@ -234,7 +234,7 @@ EOF
 
 test_expect_success 'status should only print one line' '
        git submodule status >lines &&
-       test $(wc -l <lines) = 1
+       test_line_count = 1 lines
 '
 
 test_expect_success 'setup - fetch commit name from submodule' '
index ab37c368d071236d0a2851417e85d2a216c7f4fc..b770b2f04d56336f92b9394b4a2ce22a241480c4 100755 (executable)
@@ -28,7 +28,7 @@ git prune'
 
 cd "$base_dir"
 
-test_expect_success 'preparing supermodule' \
+test_expect_success 'preparing superproject' \
 'test_create_repo super && cd super &&
 echo file > file &&
 git add file &&
@@ -43,7 +43,7 @@ git commit -m B-super-added'
 cd "$base_dir"
 
 test_expect_success 'after add: existence of info/alternates' \
-'test `wc -l <super/.git/modules/sub/objects/info/alternates` = 1'
+'test_line_count = 1 super/.git/modules/sub/objects/info/alternates'
 
 cd "$base_dir"
 
@@ -55,7 +55,7 @@ diff expected current'
 
 cd "$base_dir"
 
-test_expect_success 'cloning supermodule' \
+test_expect_success 'cloning superproject' \
 'git clone super super-clone'
 
 cd "$base_dir"
@@ -66,7 +66,7 @@ test_expect_success 'update with reference' \
 cd "$base_dir"
 
 test_expect_success 'after update: existence of info/alternates' \
-'test `wc -l <super-clone/.git/modules/sub/objects/info/alternates` = 1'
+'test_line_count = 1 super-clone/.git/modules/sub/objects/info/alternates'
 
 cd "$base_dir"
 
index 8bb38337a9796142bc091c2b108f7f9e79b0f377..b20ca0eace9dd8f9a11227ebfb932e0446278ea1 100755 (executable)
@@ -30,10 +30,12 @@ test_expect_success 'setup: initial commit' '
 '
 
 test_expect_success '-m and -F do not mix' '
+       git checkout HEAD file && echo >>file && git add file &&
        test_must_fail git commit -m foo -m bar -F file
 '
 
 test_expect_success '-m and -C do not mix' '
+       git checkout HEAD file && echo >>file && git add file &&
        test_must_fail git commit -C HEAD -m illegal
 '
 
@@ -79,7 +81,19 @@ test_expect_success 'empty commit message' '
        test_must_fail git commit -F msg -a
 '
 
+test_expect_success 'template "emptyness" check does not kick in with -F' '
+       git checkout HEAD file && echo >>file && git add file &&
+       git commit -t file -F file
+'
+
+test_expect_success 'template "emptyness" check' '
+       git checkout HEAD file && echo >>file && git add file &&
+       test_must_fail git commit -t file 2>err &&
+       test_i18ngrep "did not edit" err
+'
+
 test_expect_success 'setup: commit message from file' '
+       git checkout HEAD file && echo >>file && git add file &&
        echo this is the commit message, coming from a file >msg &&
        git commit -F msg -a
 '
index 3f3adc31b98773d26715089c25d8d923dd342717..181456aa9a80893e93477302516a7f00594eba85 100755 (executable)
@@ -335,7 +335,7 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo
        git reset --hard &&
        git commit -s -m "hello: kitty" --allow-empty &&
        git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
-       test $(wc -l <actual) = 3
+       test_line_count = 3 actual
 
 '
 
index ee7f0cd4596f982f16cbf3859675e6faba424faa..984889b39d3f8e9941a2aadc8cec833fe42176a2 100755 (executable)
@@ -118,4 +118,22 @@ test_expect_success 'with failing hook requiring GIT_PREFIX' '
        git checkout -- file
 '
 
+test_expect_success 'check the author in hook' '
+       write_script "$HOOK" <<-\EOF &&
+       test "$GIT_AUTHOR_NAME" = "New Author" &&
+       test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
+       EOF
+       test_must_fail git commit --allow-empty -m "by a.u.thor" &&
+       (
+               GIT_AUTHOR_NAME="New Author" &&
+               GIT_AUTHOR_EMAIL="newauthor@example.com" &&
+               export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
+               git commit --allow-empty -m "by new.author via env" &&
+               git show -s
+       ) &&
+       git commit --author="New Author <newauthor@example.com>" \
+               --allow-empty -m "by new.author via command line" &&
+       git show -s
+'
+
 test_done
index fc57b135c50e34ab86c04d0a0ec81052ce40f8ff..28e184829c0f12d4060f0b793db8511259bef91e 100755 (executable)
@@ -59,6 +59,30 @@ test_expect_success 'status (1)' '
        test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output
 '
 
+test_expect_success 'status --column' '
+       COLUMNS=50 git status --column="column dense" >output &&
+       cat >expect <<\EOF &&
+# On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      new file:   dir2/added
+#
+# Changes not staged for commit:
+#   (use "git add <file>..." to update what will be committed)
+#   (use "git checkout -- <file>..." to discard changes in working directory)
+#
+#      modified:   dir1/modified
+#
+# Untracked files:
+#   (use "git add <file>..." to include in what will be committed)
+#
+#      dir1/untracked dir2/untracked untracked
+#      dir2/modified  output
+EOF
+       test_cmp expect output
+'
+
 cat >expect <<\EOF
 # On branch master
 # Changes to be committed:
@@ -271,6 +295,15 @@ test_expect_success 'status -s -b' '
 
 '
 
+test_expect_success 'status -s -z -b' '
+       tr "\\n" Q <expect >expect.q &&
+       mv expect.q expect &&
+       git status -s -z -b >output &&
+       nul_to_q <output >output.q &&
+       mv output.q output &&
+       test_cmp expect output
+'
+
 test_expect_success 'setup dir3' '
        mkdir dir3 &&
        : >dir3/untracked1 &&
@@ -647,9 +680,14 @@ test_expect_success 'status --porcelain ignores color.status' '
 git config --unset color.status
 git config --unset color.ui
 
-test_expect_success 'status --porcelain ignores -b' '
+test_expect_success 'status --porcelain respects -b' '
 
        git status --porcelain -b >output &&
+       {
+               echo "## master" &&
+               cat expect
+       } >tmp &&
+       mv tmp expect &&
        test_cmp expect output
 
 '
index 5783ebf3ab042d3c78633a89d842c432c96a0d4d..955f09f8e818cea8d32e453cd4971ff362d69bea 100755 (executable)
@@ -54,9 +54,9 @@ Trying simple merge with c2
 Trying simple merge with c3
 Trying simple merge with c4
 Merge made by the 'octopus' strategy.
- c2.c |    1 +
- c3.c |    1 +
- c4.c |    1 +
+ c2.c | 1 +
+ c3.c | 1 +
+ c4.c | 1 +
  3 files changed, 3 insertions(+)
  create mode 100644 c2.c
  create mode 100644 c3.c
@@ -66,29 +66,27 @@ EOF
 test_expect_success 'merge output uses pretty names' '
        git reset --hard c1 &&
        git merge c2 c3 c4 >actual &&
-       test_cmp actual expected
+       test_i18ncmp expected actual
 '
 
 cat >expected <<\EOF
-Already up-to-date with c4
-Trying simple merge with c5
-Merge made by the 'octopus' strategy.
- c5.c |    1 +
+Merge made by the 'recursive' strategy.
+ c5.c | 1 +
  1 file changed, 1 insertion(+)
  create mode 100644 c5.c
 EOF
 
-test_expect_success 'merge up-to-date output uses pretty names' '
-       git merge c4 c5 >actual &&
-       test_cmp actual expected
+test_expect_success 'merge reduces irrelevant remote heads' '
+       GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&
+       test_i18ncmp expected actual
 '
 
 cat >expected <<\EOF
 Fast-forwarding to: c1
 Trying simple merge with c2
 Merge made by the 'octopus' strategy.
- c1.c |    1 +
- c2.c |    1 +
+ c1.c | 1 +
+ c2.c | 1 +
  2 files changed, 2 insertions(+)
  create mode 100644 c1.c
  create mode 100644 c2.c
@@ -97,7 +95,7 @@ EOF
 test_expect_success 'merge fast-forward output uses pretty names' '
        git reset --hard c0 &&
        git merge c1 c2 >actual &&
-       test_cmp actual expected
+       test_i18ncmp expected actual
 '
 
 test_done
index 7e17eb490d4b1f4ac275a7033562aaa6693181ab..98948955ae507ef007dc541f7e981d06bf7b49ab 100755 (executable)
@@ -57,7 +57,36 @@ test_expect_success 'merge c1 with c2, c3, c4, c5' '
        test -f c2.c &&
        test -f c3.c &&
        test -f c4.c &&
-       test -f c5.c
+       test -f c5.c &&
+       git show --format=%s -s >actual &&
+       ! grep c1 actual &&
+       grep c2 actual &&
+       grep c3 actual &&
+       ! grep c4 actual &&
+       grep c5 actual
+'
+
+test_expect_success 'pull c2, c3, c4, c5 into c1' '
+       git reset --hard c1 &&
+       git pull . c2 c3 c4 c5 &&
+       test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+       test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+       test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+       test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
+       test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" &&
+       git diff --exit-code &&
+       test -f c0.c &&
+       test -f c1.c &&
+       test -f c2.c &&
+       test -f c3.c &&
+       test -f c4.c &&
+       test -f c5.c &&
+       git show --format=%s -s >actual &&
+       ! grep c1 actual &&
+       grep c2 actual &&
+       grep c3 actual &&
+       ! grep c4 actual &&
+       grep c5 actual
 '
 
 test_expect_success 'setup' '
@@ -113,4 +142,23 @@ test_expect_success 'verify merge result' '
        test $(git rev-parse HEAD^1) = $(git rev-parse E2) &&
        test $(git rev-parse HEAD^2) = $(git rev-parse I2)
 '
+
+test_expect_success 'fast-forward to redundant refs' '
+       git reset --hard c0 &&
+       git merge c4 c5
+'
+
+test_expect_success 'verify merge result' '
+       test $(git rev-parse HEAD) = $(git rev-parse c5)
+'
+
+test_expect_success 'merge up-to-date redundant refs' '
+       git reset --hard c5 &&
+       git merge c0 c4
+'
+
+test_expect_success 'verify merge result' '
+       test $(git rev-parse HEAD) = $(git rev-parse c5)
+'
+
 test_done
index aa74184c31cd6b2bd2e0e566e6805e60eed7aff8..6547eb8f5459d4d95113469d338e1879f86b79ea 100755 (executable)
@@ -92,6 +92,15 @@ test_expect_success 'will not overwrite removed file with staged changes' '
        test_cmp important c1.c
 '
 
+test_expect_failure 'will not overwrite unstaged changes in renamed file' '
+       git reset --hard c1 &&
+       git mv c1.c other.c &&
+       git commit -m rename &&
+       cp important other.c &&
+       git merge c1a &&
+       test_cmp important other.c
+'
+
 test_expect_success 'will not overwrite untracked subtree' '
        git reset --hard c0 &&
        rm -rf sub &&
index 200ab61278643e8b9deb4f624a95378e7e4c5b67..b8d4cdea8cc661e27367bc942587b3b80b433051 100755 (executable)
@@ -95,4 +95,18 @@ test_expect_success 'unpacked objects receive timestamp of pack file' '
        compare_mtimes < mtimes
 '
 
+test_expect_success 'do not bother loosening old objects' '
+       obj1=$(echo one | git hash-object -w --stdin) &&
+       obj2=$(echo two | git hash-object -w --stdin) &&
+       pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+       pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+       git prune-packed &&
+       git cat-file -p $obj1 &&
+       git cat-file -p $obj2 &&
+       test-chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+       git repack -A -d --unpack-unreachable=1.hour.ago &&
+       git cat-file -p $obj1 &&
+       test_must_fail git cat-file -p $obj2
+'
+
 test_done
index 4fb4c9384a0045d3b041d627e9d814637d9268e2..9c3e997b9d6be69467a78f9ff8b1ff746be8e9b6 100755 (executable)
@@ -83,6 +83,17 @@ test_expect_success PERL 'difftool ignores bad --tool values' '
        test "$diff" = ""
 '
 
+test_expect_success PERL 'difftool forwards arguments to diff' '
+       >for-diff &&
+       git add for-diff &&
+       echo changes>for-diff &&
+       git add for-diff &&
+       diff=$(git difftool --cached --no-prompt -- for-diff) &&
+       test "$diff" = "" &&
+       git reset -- for-diff &&
+       rm for-diff
+'
+
 test_expect_success PERL 'difftool honors --gui' '
        git config merge.tool bogus-tool &&
        git config diff.tool bogus-tool &&
@@ -94,6 +105,19 @@ test_expect_success PERL 'difftool honors --gui' '
        restore_test_defaults
 '
 
+test_expect_success PERL 'difftool --gui last setting wins' '
+       git config diff.guitool bogus-tool &&
+       git difftool --no-prompt --gui --no-gui &&
+
+       git config merge.tool bogus-tool &&
+       git config diff.tool bogus-tool &&
+       git config diff.guitool test-tool &&
+       diff=$(git difftool --no-prompt --no-gui --gui branch) &&
+       test "$diff" = "branch" &&
+
+       restore_test_defaults
+'
+
 test_expect_success PERL 'difftool --gui works without configured diff.guitool' '
        git config diff.tool test-tool &&
 
@@ -306,4 +330,48 @@ test_expect_success PERL 'say no to the second file' '
        echo "$diff" | stdin_doesnot_contain br2
 '
 
+test_expect_success PERL 'difftool --tool-help' '
+       tool_help=$(git difftool --tool-help) &&
+       echo "$tool_help" | stdin_contains tool
+'
+
+test_expect_success PERL 'setup change in subdirectory' '
+       git checkout master &&
+       mkdir sub &&
+       echo master >sub/sub &&
+       git add sub/sub &&
+       git commit -m "added sub/sub" &&
+       echo test >>file &&
+       echo test >>sub/sub &&
+       git add . &&
+       git commit -m "modified both"
+'
+
+test_expect_success PERL 'difftool -d' '
+       diff=$(git difftool -d --extcmd ls branch) &&
+       echo "$diff" | stdin_contains sub &&
+       echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff' '
+       diff=$(git difftool --dir-diff --extcmd ls branch) &&
+       echo "$diff" | stdin_contains sub &&
+       echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff ignores --prompt' '
+       diff=$(git difftool --dir-diff --prompt --extcmd ls branch) &&
+       echo "$diff" | stdin_contains sub &&
+       echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff from subdirectory' '
+       (
+               cd sub &&
+               diff=$(git difftool --dir-diff --extcmd ls branch) &&
+               echo "$diff" | stdin_contains sub &&
+               echo "$diff" | stdin_contains file
+       )
+'
+
 test_done
index d9ad633310a19a9ebbc2e5024875278d4631129e..24e9b1974d17a02dd1c668e888a67f894e7a1209 100755 (executable)
@@ -351,6 +351,11 @@ test_expect_success 'grep -f, multiple patterns' '
        test_cmp expected actual
 '
 
+test_expect_success 'grep, multiple patterns' '
+       git grep "$(cat patterns)" >actual &&
+       test_cmp expected actual
+'
+
 cat >expected <<EOF
 file:foo mmap bar
 file:foo_mmap bar
diff --git a/t/t9002-column.sh b/t/t9002-column.sh
new file mode 100755 (executable)
index 0000000..8998352
--- /dev/null
@@ -0,0 +1,180 @@
+#!/bin/sh
+
+test_description='git column'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       cat >lista <<\EOF
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+'
+
+test_expect_success 'never' '
+       git column --indent=Z --mode=never <lista >actual &&
+       test_cmp lista actual
+'
+
+test_expect_success 'always' '
+       cat >expected <<\EOF &&
+Zone
+Ztwo
+Zthree
+Zfour
+Zfive
+Zsix
+Zseven
+Zeight
+Znine
+Zten
+Zeleven
+EOF
+       git column --indent=Z --mode=plain <lista >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '80 columns' '
+       cat >expected <<\EOF &&
+one    two    three  four   five   six    seven  eight  nine   ten    eleven
+EOF
+       COLUMNS=80 git column --mode=column <lista >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+one
+two
+three
+four
+five
+six
+seven
+eight
+nine
+ten
+eleven
+EOF
+
+test_expect_success COLUMNS_CAN_BE_1 'COLUMNS = 1' '
+       COLUMNS=1 git column --mode=column <lista >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'width = 1' '
+       git column --mode=column --width=1 <lista >actual &&
+       test_cmp expected actual
+'
+
+COLUMNS=20
+export COLUMNS
+
+test_expect_success '20 columns' '
+       cat >expected <<\EOF &&
+one    seven
+two    eight
+three  nine
+four   ten
+five   eleven
+six
+EOF
+       git column --mode=column <lista >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '20 columns, nodense' '
+       cat >expected <<\EOF &&
+one    seven
+two    eight
+three  nine
+four   ten
+five   eleven
+six
+EOF
+       git column --mode=column,nodense < lista > actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '20 columns, dense' '
+       cat >expected <<\EOF &&
+one   five  nine
+two   six   ten
+three seven eleven
+four  eight
+EOF
+       git column --mode=column,dense < lista > actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '20 columns, padding 2' '
+       cat >expected <<\EOF &&
+one     seven
+two     eight
+three   nine
+four    ten
+five    eleven
+six
+EOF
+       git column --mode=column --padding 2 <lista >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '20 columns, indented' '
+       cat >expected <<\EOF &&
+  one    seven
+  two    eight
+  three  nine
+  four   ten
+  five   eleven
+  six
+EOF
+       git column --mode=column --indent="  " <lista >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first' '
+       cat >expected <<\EOF &&
+one    two
+three  four
+five   six
+seven  eight
+nine   ten
+eleven
+EOF
+       git column --mode=row <lista >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first, nodense' '
+       cat >expected <<\EOF &&
+one    two
+three  four
+five   six
+seven  eight
+nine   ten
+eleven
+EOF
+       git column --mode=row,nodense <lista >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '20 columns, row first, dense' '
+       cat >expected <<\EOF &&
+one   two    three
+four  five   six
+seven eight  nine
+ten   eleven
+EOF
+       git column --mode=row,dense <lista >actual &&
+       test_cmp expected actual
+'
+
+test_done
index 0f5b5e5964a60f31cdfd6bc456848c2f4b820d0a..2aa1824a940aa955fab408995d2479e50bd42b96 100755 (executable)
@@ -24,6 +24,13 @@ head_c () {
        ' - "$1"
 }
 
+verify_packs () {
+       for p in .git/objects/pack/*.pack
+       do
+               git verify-pack "$@" "$p" || return
+       done
+}
+
 file2_data='file2
 second line of EOF'
 
@@ -105,9 +112,10 @@ test_expect_success \
     'A: create pack from stdin' \
     'git fast-import --export-marks=marks.out <input &&
         git whatchanged master'
-test_expect_success \
-       'A: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'A: verify pack' '
+       verify_packs
+'
 
 cat >expect <<EOF
 author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -252,9 +260,11 @@ test_expect_success \
        'A: verify marks import does not crash' \
        'git fast-import --import-marks=marks.out <input &&
         git whatchanged verify--import-marks'
-test_expect_success \
-       'A: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'A: verify pack' '
+       verify_packs
+'
+
 cat >expect <<EOF
 :000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A     copy-of-file2
 EOF
@@ -514,9 +524,11 @@ test_expect_success \
     'C: incremental import create pack from stdin' \
     'git fast-import <input &&
         git whatchanged branch'
-test_expect_success \
-       'C: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'C: verify pack' '
+       verify_packs
+'
+
 test_expect_success \
        'C: validate reuse existing blob' \
        'test $newf = `git rev-parse --verify branch:file2/newf` &&
@@ -572,9 +584,10 @@ test_expect_success \
     'D: inline data in commit' \
     'git fast-import <input &&
         git whatchanged branch'
-test_expect_success \
-       'D: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'D: verify pack' '
+       verify_packs
+'
 
 cat >expect <<EOF
 :000000 100755 0000000000000000000000000000000000000000 35a59026a33beac1569b1c7f66f3090ce9c09afc A     newdir/exec.sh
@@ -618,9 +631,10 @@ test_expect_success 'E: rfc2822 date, --date-format=raw' '
 test_expect_success \
     'E: rfc2822 date, --date-format=rfc2822' \
     'git fast-import --date-format=rfc2822 <input'
-test_expect_success \
-       'E: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'E: verify pack' '
+       verify_packs
+'
 
 cat >expect <<EOF
 author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 1170778938 -0500
@@ -669,9 +683,10 @@ test_expect_success \
                fi
         fi
        '
-test_expect_success \
-       'F: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'F: verify pack' '
+       verify_packs
+'
 
 cat >expect <<EOF
 tree `git rev-parse branch~1^{tree}`
@@ -705,9 +720,11 @@ INPUT_END
 test_expect_success \
     'G: non-fast-forward update forced' \
     'git fast-import --force <input'
-test_expect_success \
-       'G: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'G: verify pack' '
+       verify_packs
+'
+
 test_expect_success \
        'G: branch changed, but logged' \
        'test $old_branch != `git rev-parse --verify branch^0` &&
@@ -742,9 +759,10 @@ test_expect_success \
     'H: deletall, add 1' \
     'git fast-import <input &&
         git whatchanged H'
-test_expect_success \
-       'H: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'H: verify pack' '
+       verify_packs
+'
 
 cat >expect <<EOF
 :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D     file2/newf
@@ -1639,7 +1657,7 @@ M 160000 :6 sub
 INPUT_END
 
 test_expect_success \
-       'P: supermodule & submodule mix' \
+       'P: superproject & submodule mix' \
        'git fast-import <input &&
         git checkout subuse1 &&
         rm -rf sub && mkdir sub && (cd sub &&
@@ -1857,9 +1875,10 @@ test_expect_success \
        'Q: commit notes' \
        'git fast-import <input &&
         git whatchanged notes-test'
-test_expect_success \
-       'Q: verify pack' \
-       'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+test_expect_success 'Q: verify pack' '
+       verify_packs
+'
 
 commit1=$(git rev-parse notes-test~2)
 commit2=$(git rev-parse notes-test^)
@@ -2616,13 +2635,14 @@ test_expect_success \
        'R: blob bigger than threshold' \
        'test_create_repo R &&
         git --git-dir=R/.git fast-import --big-file-threshold=1 <input'
-test_expect_success \
-       'R: verify created pack' \
-       ': >verify &&
-        for p in R/.git/objects/pack/*.pack;
-        do
-          git verify-pack -v $p >>verify || exit;
-        done'
+
+test_expect_success 'R: verify created pack' '
+       (
+               cd R &&
+               verify_packs -v > ../verify
+       )
+'
+
 test_expect_success \
        'R: verify written objects' \
        'git --git-dir=R/.git cat-file blob big-file:big1 >actual &&
@@ -2635,4 +2655,291 @@ test_expect_success \
        'n=$(grep $a verify | wc -l) &&
         test 1 = $n'
 
+###
+### series S
+###
+#
+# Make sure missing spaces and EOLs after mark references
+# cause errors.
+#
+# Setup:
+#
+#   1--2--4
+#    \   /
+#     -3-
+#
+#   commit marks:  301, 302, 303, 304
+#   blob marks:              403, 404, resp.
+#   note mark:          202
+#
+# The error message when a space is missing not at the
+# end of the line is:
+#
+#   Missing space after ..
+#
+# or when extra characters come after the mark at the end
+# of the line:
+#
+#   Garbage after ..
+#
+# or when the dataref is neither "inline " or a known SHA1,
+#
+#   Invalid dataref ..
+#
+test_tick
+
+cat >input <<INPUT_END
+commit refs/heads/S
+mark :301
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit 1
+COMMIT
+M 100644 inline hello.c
+data <<BLOB
+blob 1
+BLOB
+
+commit refs/heads/S
+mark :302
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit 2
+COMMIT
+from :301
+M 100644 inline hello.c
+data <<BLOB
+blob 2
+BLOB
+
+blob
+mark :403
+data <<BLOB
+blob 3
+BLOB
+
+blob
+mark :202
+data <<BLOB
+note 2
+BLOB
+INPUT_END
+
+test_expect_success 'S: initialize for S tests' '
+       git fast-import --export-marks=marks <input
+'
+
+#
+# filemodify, three datarefs
+#
+test_expect_success 'S: filemodify with garbage after mark must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       commit refs/heads/S
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit N
+       COMMIT
+       M 100644 :403x hello.c
+       EOF
+       cat err &&
+       test_i18ngrep "space after mark" err
+'
+
+# inline is misspelled; fast-import thinks it is some unknown dataref
+test_expect_success 'S: filemodify with garbage after inline must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       commit refs/heads/S
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit N
+       COMMIT
+       M 100644 inlineX hello.c
+       data <<BLOB
+       inline
+       BLOB
+       EOF
+       cat err &&
+       test_i18ngrep "nvalid dataref" err
+'
+
+test_expect_success 'S: filemodify with garbage after sha1 must fail' '
+       sha1=$(grep :403 marks | cut -d\  -f2) &&
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       commit refs/heads/S
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit N
+       COMMIT
+       M 100644 ${sha1}x hello.c
+       EOF
+       cat err &&
+       test_i18ngrep "space after SHA1" err
+'
+
+#
+# notemodify, three ways to say dataref
+#
+test_expect_success 'S: notemodify with garabge after mark dataref must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       commit refs/heads/S
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit S note dataref markref
+       COMMIT
+       N :202x :302
+       EOF
+       cat err &&
+       test_i18ngrep "space after mark" err
+'
+
+test_expect_success 'S: notemodify with garbage after inline dataref must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       commit refs/heads/S
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit S note dataref inline
+       COMMIT
+       N inlineX :302
+       data <<BLOB
+       note blob
+       BLOB
+       EOF
+       cat err &&
+       test_i18ngrep "nvalid dataref" err
+'
+
+test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' '
+       sha1=$(grep :202 marks | cut -d\  -f2) &&
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       commit refs/heads/S
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit S note dataref sha1
+       COMMIT
+       N ${sha1}x :302
+       EOF
+       cat err &&
+       test_i18ngrep "space after SHA1" err
+'
+
+#
+# notemodify, mark in committish
+#
+test_expect_success 'S: notemodify with garbarge after mark committish must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       commit refs/heads/Snotes
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit S note committish
+       COMMIT
+       N :202 :302x
+       EOF
+       cat err &&
+       test_i18ngrep "after mark" err
+'
+
+#
+# from
+#
+test_expect_success 'S: from with garbage after mark must fail' '
+       # no &&
+       git fast-import --import-marks=marks --export-marks=marks <<-EOF 2>err
+       commit refs/heads/S2
+       mark :303
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit 3
+       COMMIT
+       from :301x
+       M 100644 :403 hello.c
+       EOF
+
+       ret=$? &&
+       echo returned $ret &&
+       test $ret -ne 0 && # failed, but it created the commit
+
+       # go create the commit, need it for merge test
+       git fast-import --import-marks=marks --export-marks=marks <<-EOF &&
+       commit refs/heads/S2
+       mark :303
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit 3
+       COMMIT
+       from :301
+       M 100644 :403 hello.c
+       EOF
+
+       # now evaluate the error
+       cat err &&
+       test_i18ngrep "after mark" err
+'
+
+
+#
+# merge
+#
+test_expect_success 'S: merge with garbage after mark must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       commit refs/heads/S
+       mark :304
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       merge 4
+       COMMIT
+       from :302
+       merge :303x
+       M 100644 :403 hello.c
+       EOF
+       cat err &&
+       test_i18ngrep "after mark" err
+'
+
+#
+# tag, from markref
+#
+test_expect_success 'S: tag with garbage after mark must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       tag refs/tags/Stag
+       from :302x
+       tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<TAG
+       tag S
+       TAG
+       EOF
+       cat err &&
+       test_i18ngrep "after mark" err
+'
+
+#
+# cat-blob markref
+#
+test_expect_success 'S: cat-blob with garbage after mark must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       cat-blob :403x
+       EOF
+       cat err &&
+       test_i18ngrep "after mark" err
+'
+
+#
+# ls markref
+#
+test_expect_success 'S: ls with garbage after mark must fail' '
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       ls :302x hello.c
+       EOF
+       cat err &&
+       test_i18ngrep "space after mark" err
+'
+
+test_expect_success 'S: ls with garbage after sha1 must fail' '
+       sha1=$(grep :302 marks | cut -d\  -f2) &&
+       test_must_fail git fast-import --import-marks=marks <<-EOF 2>err &&
+       ls ${sha1}x hello.c
+       EOF
+       cat err &&
+       test_i18ngrep "space after tree-ish" err
+'
+
 test_done
index 950d0ff498fda58c2d3d68dfb2cf50512f8f81ba..b00196bd238f538f78ce421979e725ce501e84e1 100755 (executable)
@@ -86,7 +86,7 @@ test_expect_success 'import/export-marks' '
        git checkout -b marks master &&
        git fast-export --export-marks=tmp-marks HEAD &&
        test -s tmp-marks &&
-       test $(wc -l < tmp-marks) -eq 3 &&
+       test_line_count = 3 tmp-marks &&
        test $(
                git fast-export --import-marks=tmp-marks\
                --export-marks=tmp-marks HEAD |
@@ -101,7 +101,7 @@ test_expect_success 'import/export-marks' '
                grep ^commit\  |
                wc -l) \
        -eq 1 &&
-       test $(wc -l < tmp-marks) -eq 4
+       test_line_count = 4 tmp-marks
 
 '
 
index 9199550ef4ffa39e4ce8bdb36badfd723e95e55f..806623e8858eef6ebe224ea14a38732f63fd6e49 100755 (executable)
@@ -476,14 +476,14 @@ test_expect_success 'cvs status' '
     cd cvswork &&
     GIT_CONFIG="$git_config" cvs update &&
     GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
-    test $(wc -l <../out) = 2
+    test_line_count = 2 ../out
 '
 
 cd "$WORKDIR"
 test_expect_success 'cvs status (nonrecursive)' '
     cd cvswork &&
     GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
-    test $(wc -l <../out) = 1
+    test_line_count = 1 ../out
 '
 
 cd "$WORKDIR"
@@ -500,8 +500,8 @@ test_expect_success 'cvs status (no subdirs in header)' '
 cd "$WORKDIR"
 test_expect_success 'cvs co -c (shows module database)' '
     GIT_CONFIG="$git_config" cvs co -c > out &&
-    grep "^master[      ]\+master$" < out &&
-    ! grep -v "^master[         ]\+master$" < out
+    grep "^master[      ][     ]*master$" <out &&
+    ! grep -v "^master[         ][     ]*master$" <out
 '
 
 #------------
index 31076edc5bd45261f5874b10dad6376e49fb9002..fa2f65f6be44fb7d6c4c22b9642b33cd90d51646 100755 (executable)
@@ -92,7 +92,7 @@ test_debug 'cat gitweb.output'
 test_expect_success 'snapshots: bad tree-ish id (tagged object)' '
        echo object > tag-object &&
        git add tag-object &&
-       git commit -m "Object to be tagged" &&
+       test_tick && git commit -m "Object to be tagged" &&
        git tag tagged-object `git hash-object tag-object` &&
        gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" &&
        grep "400 - Object is not a tree-ish" gitweb.output
@@ -112,6 +112,64 @@ test_expect_success 'snapshots: bad object id' '
 '
 test_debug 'cat gitweb.output'
 
+# ----------------------------------------------------------------------
+# modification times (Last-Modified and If-Modified-Since)
+
+test_expect_success 'modification: feed last-modified' '
+       gitweb_run "p=.git;a=atom;h=master" &&
+       grep "Status: 200 OK" gitweb.headers &&
+       grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'modification: feed if-modified-since (modified)' '
+       export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+       gitweb_run "p=.git;a=atom;h=master" &&
+       grep "Status: 200 OK" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'modification: feed if-modified-since (unmodified)' '
+       export HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+       test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+       gitweb_run "p=.git;a=atom;h=master" &&
+       grep "Status: 304 Not Modified" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'modification: snapshot last-modified' '
+       gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+       grep "Status: 200 OK" gitweb.headers &&
+       grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'modification: snapshot if-modified-since (modified)' '
+       export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+       gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+       grep "Status: 200 OK" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'modification: snapshot if-modified-since (unmodified)' '
+       export HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" &&
+       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
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'modification: tree snapshot' '
+       ID=`git rev-parse --verify HEAD^{tree}` &&
+       export HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" &&
+       test_when_finished "unset HTTP_IF_MODIFIED_SINCE" &&
+       gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
+       grep "Status: 200 OK" gitweb.headers &&
+       ! grep -i "last-modified" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
 
 # ----------------------------------------------------------------------
 # load checking
index 486c8eeb7e616c5dd964da25b068a8432e4c0c6f..0f410c45f7005e7de1237e556396db5726a22e50 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 tests'
+test_description='git p4 tests'
 
 . ./lib-git-p4.sh
 
@@ -20,8 +20,8 @@ test_expect_success 'add p4 files' '
        )
 '
 
-test_expect_success 'basic git-p4 clone' '
-       "$GITP4" clone --dest="$git" //depot &&
+test_expect_success 'basic git p4 clone' '
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -30,8 +30,8 @@ test_expect_success 'basic git-p4 clone' '
        )
 '
 
-test_expect_success 'git-p4 clone @all' '
-       "$GITP4" clone --dest="$git" //depot@all &&
+test_expect_success 'git p4 clone @all' '
+       git p4 clone --dest="$git" //depot@all &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -40,12 +40,12 @@ test_expect_success 'git-p4 clone @all' '
        )
 '
 
-test_expect_success 'git-p4 sync uninitialized repo' '
+test_expect_success 'git p4 sync uninitialized repo' '
        test_create_repo "$git" &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
-               test_must_fail "$GITP4" sync
+               test_must_fail git p4 sync
        )
 '
 
@@ -53,13 +53,13 @@ test_expect_success 'git-p4 sync uninitialized repo' '
 # Create a git repo by hand.  Add a commit so that HEAD is valid.
 # Test imports a new p4 repository into a new git branch.
 #
-test_expect_success 'git-p4 sync new branch' '
+test_expect_success 'git p4 sync new branch' '
        test_create_repo "$git" &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
                test_commit head &&
-               "$GITP4" sync --branch=refs/remotes/p4/depot //depot@all &&
+               git p4 sync --branch=refs/remotes/p4/depot //depot@all &&
                git log --oneline p4/depot >lines &&
                test_line_count = 2 lines
        )
@@ -76,7 +76,7 @@ test_expect_success 'clone two dirs' '
                p4 add sub2/f2 &&
                p4 submit -d "sub2/f2"
        ) &&
-       "$GITP4" clone --dest="$git" //depot/sub1 //depot/sub2 &&
+       git p4 clone --dest="$git" //depot/sub1 //depot/sub2 &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -94,7 +94,7 @@ test_expect_success 'clone two dirs, @all' '
                p4 add sub1/f3 &&
                p4 submit -d "sub1/f3"
        ) &&
-       "$GITP4" clone --dest="$git" //depot/sub1@all //depot/sub2@all &&
+       git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -112,7 +112,7 @@ test_expect_success 'clone two dirs, @all, conflicting files' '
                p4 add sub2/f3 &&
                p4 submit -d "sub2/f3"
        ) &&
-       "$GITP4" clone --dest="$git" //depot/sub1@all //depot/sub2@all &&
+       git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -134,7 +134,7 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '
        exit 1
        EOF
        chmod 755 "$badp4dir"/p4 &&
-       PATH="$badp4dir:$PATH" "$GITP4" clone --dest="$git" //depot >errs 2>&1 ; retval=$? &&
+       PATH="$badp4dir:$PATH" git p4 clone --dest="$git" //depot >errs 2>&1 ; retval=$? &&
        test $retval -eq 1 &&
        test_must_fail grep -q Traceback errs
 '
@@ -151,8 +151,8 @@ test_expect_success 'add p4 files with wildcards in the names' '
        )
 '
 
-test_expect_success 'wildcard files git-p4 clone' '
-       "$GITP4" clone --dest="$git" //depot &&
+test_expect_success 'wildcard files git p4 clone' '
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -163,8 +163,114 @@ test_expect_success 'wildcard files git-p4 clone' '
        )
 '
 
+test_expect_success 'wildcard files submit back to p4, add' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo git-wild-hash >git-wild#hash &&
+               echo git-wild-star >git-wild\*star &&
+               echo git-wild-at >git-wild@at &&
+               echo git-wild-percent >git-wild%percent &&
+               git add git-wild* &&
+               git commit -m "add some wildcard filenames" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file git-wild#hash &&
+               test_path_is_file git-wild\*star &&
+               test_path_is_file git-wild@at &&
+               test_path_is_file git-wild%percent
+       )
+'
+
+test_expect_success 'wildcard files submit back to p4, modify' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               echo new-line >>git-wild#hash &&
+               echo new-line >>git-wild\*star &&
+               echo new-line >>git-wild@at &&
+               echo new-line >>git-wild%percent &&
+               git add git-wild* &&
+               git commit -m "modify the wildcard files" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_line_count = 2 git-wild#hash &&
+               test_line_count = 2 git-wild\*star &&
+               test_line_count = 2 git-wild@at &&
+               test_line_count = 2 git-wild%percent
+       )
+'
+
+test_expect_success 'wildcard files submit back to p4, copy' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               cp file2 git-wild-cp#hash &&
+               git add git-wild-cp#hash &&
+               cp git-wild\*star file-wild-3 &&
+               git add file-wild-3 &&
+               git commit -m "wildcard copies" &&
+               git config git-p4.detectCopies true &&
+               git config git-p4.detectCopiesHarder true &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file git-wild-cp#hash &&
+               test_path_is_file file-wild-3
+       )
+'
+
+test_expect_success 'wildcard files submit back to p4, rename' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git mv git-wild@at file-wild-4 &&
+               git mv file-wild-3 git-wild-cp%percent &&
+               git commit -m "wildcard renames" &&
+               git config git-p4.detectRenames true &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing git-wild@at &&
+               test_path_is_file git-wild-cp%percent
+       )
+'
+
+test_expect_success 'wildcard files submit back to p4, delete' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git rm git-wild* &&
+               git commit -m "delete the wildcard files" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing git-wild#hash &&
+               test_path_is_missing git-wild\*star &&
+               test_path_is_missing git-wild@at &&
+               test_path_is_missing git-wild%percent
+       )
+'
+
 test_expect_success 'clone bare' '
-       "$GITP4" clone --dest="$git" --bare //depot &&
+       git p4 clone --dest="$git" --bare //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -209,7 +315,7 @@ test_expect_success 'preserve users' '
        p4_add_user alice Alice &&
        p4_add_user bob Bob &&
        p4_grant_admin alice &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -218,7 +324,7 @@ test_expect_success 'preserve users' '
                git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 &&
                git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 &&
                git config git-p4.skipSubmitEditCheck true &&
-               P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit --preserve-user &&
+               P4EDITOR=touch P4USER=alice P4PASSWD=secret git p4 commit --preserve-user &&
                p4_check_commit_author file1 alice &&
                p4_check_commit_author file2 bob
        )
@@ -227,7 +333,7 @@ test_expect_success 'preserve users' '
 # Test username support, submitting as bob, who lacks admin rights. Should
 # not submit change to p4 (git diff should show deltas).
 test_expect_success 'refuse to preserve users without perms' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -236,14 +342,14 @@ test_expect_success 'refuse to preserve users without perms' '
                git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 &&
                P4EDITOR=touch P4USER=bob P4PASSWD=secret &&
                export P4EDITOR P4USER P4PASSWD &&
-               test_must_fail "$GITP4" commit --preserve-user &&
+               test_must_fail git p4 commit --preserve-user &&
                ! git diff --exit-code HEAD..p4/master
        )
 '
 
 # What happens with unknown author? Without allowMissingP4Users it should fail.
 test_expect_success 'preserve user where author is unknown to p4' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -254,24 +360,24 @@ test_expect_success 'preserve user where author is unknown to p4' '
                git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 &&
                P4EDITOR=touch P4USER=alice P4PASSWD=secret &&
                export P4EDITOR P4USER P4PASSWD &&
-               test_must_fail "$GITP4" commit --preserve-user &&
+               test_must_fail git p4 commit --preserve-user &&
                ! git diff --exit-code HEAD..p4/master &&
 
                echo "$0: repeat with allowMissingP4Users enabled" &&
                git config git-p4.allowMissingP4Users true &&
                git config git-p4.preserveUser true &&
-               "$GITP4" commit &&
+               git p4 commit &&
                git diff --exit-code HEAD..p4/master &&
                p4_check_commit_author file1 alice
        )
 '
 
-# If we're *not* using --preserve-user, git-p4 should warn if we're submitting
+# If we're *not* using --preserve-user, git p4 should warn if we're submitting
 # changes that are not all ours.
 # Test: user in p4 and user unknown to p4.
 # Test: warning disabled and user is the same.
 test_expect_success 'not preserving user with mixed authorship' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -281,20 +387,20 @@ test_expect_success 'not preserving user with mixed authorship' '
                make_change_by_user usernamefile3 Derek derek@localhost &&
                P4EDITOR=cat P4USER=alice P4PASSWD=secret &&
                export P4EDITOR P4USER P4PASSWD &&
-               "$GITP4" commit |\
+               git p4 commit |\
                grep "git author derek@localhost does not match" &&
 
                make_change_by_user usernamefile3 Charlie charlie@localhost &&
-               "$GITP4" commit |\
+               git p4 commit |\
                grep "git author charlie@localhost does not match" &&
 
                make_change_by_user usernamefile3 alice alice@localhost &&
-               "$GITP4" commit |\
+               git p4 commit |\
                test_must_fail grep "git author.*does not match" &&
 
                git config git-p4.skipUserNameCheck true &&
                make_change_by_user usernamefile3 Charlie charlie@localhost &&
-               "$GITP4" commit |\
+               git p4 commit |\
                test_must_fail grep "git author.*does not match" &&
 
                p4_check_commit_author usernamefile3 alice
@@ -313,7 +419,7 @@ test_expect_success 'initial import time from top change time' '
        p4change=$(p4 -G changes -m 1 //depot/... | marshal_dump change) &&
        p4time=$(p4 -G changes -m 1 //depot/... | marshal_dump time) &&
        sleep 3 &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -331,16 +437,16 @@ test_expect_success 'initial import time from top change time' '
 # Repeat, this time with a smaller threshold and confirm that the rename is
 # detected in P4.
 test_expect_success 'detect renames' '
-       "$GITP4" clone --dest="$git" //depot@all &&
+       git p4 clone --dest="$git" //depot@all &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
-               git config git-p4.skipSubmitEditCheck true &&
+               git config git-p4.skipSubmitEdit true &&
 
                git mv file1 file4 &&
                git commit -a -m "Rename file1 to file4" &&
                git diff-tree -r -M HEAD &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file4 &&
                p4 filelog //depot/file4 | test_must_fail grep -q "branch from" &&
 
@@ -348,7 +454,7 @@ test_expect_success 'detect renames' '
                git commit -a -m "Rename file4 to file5" &&
                git diff-tree -r -M HEAD &&
                git config git-p4.detectRenames true &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file5 &&
                p4 filelog //depot/file5 | grep -q "branch from //depot/file4" &&
 
@@ -360,7 +466,7 @@ test_expect_success 'detect renames' '
                level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
                test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
                git config git-p4.detectRenames $(($level + 2)) &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file6 &&
                p4 filelog //depot/file6 | test_must_fail grep -q "branch from" &&
 
@@ -372,7 +478,7 @@ test_expect_success 'detect renames' '
                level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") &&
                test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
                git config git-p4.detectRenames $(($level - 2)) &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file7 &&
                p4 filelog //depot/file7 | grep -q "branch from //depot/file6"
        )
@@ -390,17 +496,17 @@ test_expect_success 'detect renames' '
 # Modify and copy a file, configure a smaller threshold in detectCopies and
 # confirm that copy is detected in P4.
 test_expect_success 'detect copies' '
-       "$GITP4" clone --dest="$git" //depot@all &&
+       git p4 clone --dest="$git" //depot@all &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
-               git config git-p4.skipSubmitEditCheck true &&
+               git config git-p4.skipSubmitEdit true &&
 
                cp file2 file8 &&
                git add file8 &&
                git commit -a -m "Copy file2 to file8" &&
                git diff-tree -r -C HEAD &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file8 &&
                p4 filelog //depot/file8 | test_must_fail grep -q "branch from" &&
 
@@ -409,7 +515,7 @@ test_expect_success 'detect copies' '
                git commit -a -m "Copy file2 to file9" &&
                git diff-tree -r -C HEAD &&
                git config git-p4.detectCopies true &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file9 &&
                p4 filelog //depot/file9 | test_must_fail grep -q "branch from" &&
 
@@ -418,7 +524,7 @@ test_expect_success 'detect copies' '
                git add file2 file10 &&
                git commit -a -m "Modify and copy file2 to file10" &&
                git diff-tree -r -C HEAD &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file10 &&
                p4 filelog //depot/file10 | grep -q "branch from //depot/file" &&
 
@@ -429,7 +535,7 @@ test_expect_success 'detect copies' '
                src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
                test "$src" = file10 &&
                git config git-p4.detectCopiesHarder true &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file11 &&
                p4 filelog //depot/file11 | grep -q "branch from //depot/file" &&
 
@@ -443,7 +549,7 @@ test_expect_success 'detect copies' '
                src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
                test "$src" = file10 &&
                git config git-p4.detectCopies $(($level + 2)) &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file12 &&
                p4 filelog //depot/file12 | test_must_fail grep -q "branch from" &&
 
@@ -457,7 +563,7 @@ test_expect_success 'detect copies' '
                src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) &&
                test "$src" = file10 &&
                git config git-p4.detectCopies $(($level - 2)) &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 filelog //depot/file13 &&
                p4 filelog //depot/file13 | grep -q "branch from //depot/file"
        )
index d41470541650590355bf0de1a1b556b3502492b5..2859256de30deec3bdb7ceeef51b12342a901ed0 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 p4 branching tests'
+test_description='git p4 tests for p4 branches'
 
 . ./lib-git-p4.sh
 
@@ -63,7 +63,7 @@ test_expect_success 'basic p4 branches' '
 
 test_expect_success 'import main, no branch detection' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot/main@all &&
+       git p4 clone --dest="$git" //depot/main@all &&
        (
                cd "$git" &&
                git log --oneline --graph --decorate --all &&
@@ -74,7 +74,7 @@ test_expect_success 'import main, no branch detection' '
 
 test_expect_success 'import branch1, no branch detection' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot/branch1@all &&
+       git p4 clone --dest="$git" //depot/branch1@all &&
        (
                cd "$git" &&
                git log --oneline --graph --decorate --all &&
@@ -85,7 +85,7 @@ test_expect_success 'import branch1, no branch detection' '
 
 test_expect_success 'import branch2, no branch detection' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot/branch2@all &&
+       git p4 clone --dest="$git" //depot/branch2@all &&
        (
                cd "$git" &&
                git log --oneline --graph --decorate --all &&
@@ -96,7 +96,7 @@ test_expect_success 'import branch2, no branch detection' '
 
 test_expect_success 'import depot, no branch detection' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot@all &&
+       git p4 clone --dest="$git" //depot@all &&
        (
                cd "$git" &&
                git log --oneline --graph --decorate --all &&
@@ -107,7 +107,7 @@ test_expect_success 'import depot, no branch detection' '
 
 test_expect_success 'import depot, branch detection' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" --detect-branches //depot@all &&
+       git p4 clone --dest="$git" --detect-branches //depot@all &&
        (
                cd "$git" &&
 
@@ -132,7 +132,7 @@ test_expect_success 'import depot, branch detection, branchList branch definitio
        (
                cd "$git" &&
                git config git-p4.branchList main:branch1 &&
-               "$GITP4" clone --dest=. --detect-branches //depot@all &&
+               git p4 clone --dest=. --detect-branches //depot@all &&
 
                git log --oneline --graph --decorate --all &&
 
@@ -189,15 +189,15 @@ test_expect_success 'add simple p4 branches' '
 # Configure branches through git-config and clone them.
 # All files are tested to make sure branches were cloned correctly.
 # Finally, make an update to branch1 on P4 side to check if it is imported
-# correctly by git-p4.
-test_expect_success 'git-p4 clone simple branches' '
+# correctly by git p4.
+test_expect_success 'git p4 clone simple branches' '
        test_when_finished cleanup_git &&
        test_create_repo "$git" &&
        (
                cd "$git" &&
                git config git-p4.branchList branch1:branch2 &&
                git config --add git-p4.branchList branch1:branch3 &&
-               "$GITP4" clone --dest=. --detect-branches //depot@all &&
+               git p4 clone --dest=. --detect-branches //depot@all &&
                git log --all --graph --decorate --stat &&
                git reset --hard p4/depot/branch1 &&
                test -f file1 &&
@@ -221,13 +221,13 @@ test_expect_success 'git-p4 clone simple branches' '
                p4 submit -d "update file2 in branch3" &&
                cd "$git" &&
                git reset --hard p4/depot/branch1 &&
-               "$GITP4" rebase &&
+               git p4 rebase &&
                grep file2_ file2
        )
 '
 
 # Create a complex branch structure in P4 depot to check if they are correctly
-# cloned. The branches are created from older changelists to check if git-p4 is
+# cloned. The branches are created from older changelists to check if git p4 is
 # able to correctly detect them.
 # The final expected structure is:
 # `branch1
@@ -248,7 +248,7 @@ test_expect_success 'git-p4 clone simple branches' '
 #   `- file1
 #   `- file2
 #   `- file3
-test_expect_success 'git-p4 add complex branches' '
+test_expect_success 'git p4 add complex branches' '
        test_when_finished cleanup_git &&
        test_create_repo "$git" &&
        (
@@ -263,10 +263,10 @@ test_expect_success 'git-p4 add complex branches' '
        )
 '
 
-# Configure branches through git-config and clone them. git-p4 will only be able
+# Configure branches through git-config and clone them. git p4 will only be able
 # to clone the original structure if it is able to detect the origin changelist
 # of each branch.
-test_expect_success 'git-p4 clone complex branches' '
+test_expect_success 'git p4 clone complex branches' '
        test_when_finished cleanup_git &&
        test_create_repo "$git" &&
        (
@@ -275,7 +275,7 @@ test_expect_success 'git-p4 clone complex branches' '
                git config --add git-p4.branchList branch1:branch3 &&
                git config --add git-p4.branchList branch1:branch4 &&
                git config --add git-p4.branchList branch1:branch5 &&
-               "$GITP4" clone --dest=. --detect-branches //depot@all &&
+               git p4 clone --dest=. --detect-branches //depot@all &&
                git log --all --graph --decorate --stat &&
                git reset --hard p4/depot/branch1 &&
                test_path_is_file file1 &&
index 992bb8cf0ba40104e4c6c43babcd2edbb4ac90f1..21924dfd7db4fd5b8c0eb2c2823580ae33e73cc1 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 p4 filetype tests'
+test_description='git p4 filetype tests'
 
 . ./lib-git-p4.sh
 
@@ -37,7 +37,7 @@ test_expect_success 'utf-16 file create' '
 
 test_expect_success 'utf-16 file test' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot@all &&
+       git p4 clone --dest="$git" //depot@all &&
        (
                cd "$git" &&
 
@@ -84,7 +84,7 @@ test_expect_success 'keyword file test' '
        build_smush &&
        test_when_finished rm -f k_smush.py ko_smush.py &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot@all &&
+       git p4 clone --dest="$git" //depot@all &&
        (
                cd "$git" &&
 
@@ -94,7 +94,7 @@ test_expect_success 'keyword file test' '
                "$PYTHON_PATH" "$TRASH_DIRECTORY/ko_smush.py" <"$cli/k-text-ko" >cli-k-text-ko-smush &&
                test_cmp cli-k-text-ko-smush k-text-ko &&
 
-               # utf16, even though p4 expands keywords, git-p4 does not
+               # utf16, even though p4 expands keywords, git p4 does not
                # try to undo that
                test_cmp "$cli/k-utf16-k" k-utf16-k &&
                test_cmp "$cli/k-utf16-ko" k-utf16-ko
@@ -125,7 +125,7 @@ test_expect_success 'ignore apple' '
                p4 submit -d appledouble
        ) &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot@all &&
+       git p4 clone --dest="$git" //depot@all &&
        (
                cd "$git" &&
                test ! -f double.png
index db670207bde72177bff683863057d71cea34e6ae..fbacff34fed6607ec3d0470a28a9fde31bfa7c0c 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 transparency to shell metachars in filenames'
+test_description='git p4 transparency to shell metachars in filenames'
 
 . ./lib-git-p4.sh
 
@@ -18,7 +18,7 @@ test_expect_success 'init depot' '
 '
 
 test_expect_success 'shell metachars in filenames' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -28,7 +28,7 @@ test_expect_success 'shell metachars in filenames' '
                echo f2 >"file with spaces" &&
                git add "file with spaces" &&
                git commit -m "add files" &&
-               P4EDITOR=touch "$GITP4" submit
+               P4EDITOR=touch git p4 submit
        ) &&
        (
                cd "$cli" &&
@@ -39,7 +39,7 @@ test_expect_success 'shell metachars in filenames' '
 '
 
 test_expect_success 'deleting with shell metachars' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -47,7 +47,7 @@ test_expect_success 'deleting with shell metachars' '
                git rm foo\$bar &&
                git rm file\ with\ spaces &&
                git commit -m "remove files" &&
-               P4EDITOR=touch "$GITP4" submit
+               P4EDITOR=touch git p4 submit
        ) &&
        (
                cd "$cli" &&
@@ -97,7 +97,7 @@ test_expect_success 'branch with shell char' '
                cd "$git" &&
 
                git config git-p4.branchList main:branch\$3 &&
-               "$GITP4" clone --dest=. --detect-branches //depot@all &&
+               git p4 clone --dest=. --detect-branches //depot@all &&
                git log --all --graph --decorate --stat &&
                git reset --hard p4/depot/branch\$3 &&
                test -f shell_char_branch_file &&
index a9e04efb889c07037b6800171f9675c8afb68e18..e30f80e617674967b1d474c1485a6570f1ef2903 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 p4 label tests'
+test_description='git p4 label tests'
 
 . ./lib-git-p4.sh
 
@@ -50,7 +50,7 @@ test_expect_success 'basic p4 labels' '
 
                p4 labels ... &&
 
-               "$GITP4" clone --dest="$git" --detect-labels //depot@all &&
+               git p4 clone --dest="$git" --detect-labels //depot@all &&
                cd "$git" &&
 
                git tag &&
@@ -89,7 +89,7 @@ test_expect_failure 'two labels on the same changelist' '
 
                p4 labels ... &&
 
-               "$GITP4" clone --dest="$git" --detect-labels //depot@all &&
+               git p4 clone --dest="$git" --detect-labels //depot@all &&
                cd "$git" &&
 
                git tag | grep tag_f1 &&
index df929e05558bbe84d78a35cedc273cb77b2d2c29..353dcfbe09fec2c2a85427e3e5cb378b9a8318d8 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 skipSubmitEdit config variables'
+test_description='git p4 skipSubmitEdit config variables'
 
 . ./lib-git-p4.sh
 
@@ -19,33 +19,33 @@ test_expect_success 'init depot' '
 
 # this works because EDITOR is set to :
 test_expect_success 'no config, unedited, say yes' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
                echo line >>file1 &&
                git commit -a -m "change 2" &&
-               echo y | "$GITP4" submit &&
+               echo y | git p4 submit &&
                p4 changes //depot/... >wc &&
                test_line_count = 2 wc
        )
 '
 
 test_expect_success 'no config, unedited, say no' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
                echo line >>file1 &&
                git commit -a -m "change 3 (not really)" &&
-               printf "bad response\nn\n" | "$GITP4" submit &&
+               printf "bad response\nn\n" | git p4 submit &&
                p4 changes //depot/... >wc &&
                test_line_count = 2 wc
        )
 '
 
 test_expect_success 'skipSubmitEdit' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -54,21 +54,21 @@ test_expect_success 'skipSubmitEdit' '
                git config core.editor /bin/false &&
                echo line >>file1 &&
                git commit -a -m "change 3" &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 changes //depot/... >wc &&
                test_line_count = 3 wc
        )
 '
 
 test_expect_success 'skipSubmitEditCheck' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEditCheck true &&
                echo line >>file1 &&
                git commit -a -m "change 4" &&
-               "$GITP4" submit &&
+               git p4 submit &&
                p4 changes //depot/... >wc &&
                test_line_count = 4 wc
        )
@@ -76,7 +76,7 @@ test_expect_success 'skipSubmitEditCheck' '
 
 # check the normal case, where the template really is edited
 test_expect_success 'no config, edited' '
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        ed="$TRASH_DIRECTORY/ed.sh" &&
        test_when_finished "rm \"$ed\"" &&
@@ -91,7 +91,7 @@ test_expect_success 'no config, edited' '
                cd "$git" &&
                echo line >>file1 &&
                git commit -a -m "change 5" &&
-               EDITOR="\"$ed\"" "$GITP4" submit &&
+               P4EDITOR="" EDITOR="\"$ed\"" git p4 submit &&
                p4 changes //depot/... >wc &&
                test_line_count = 5 wc
        )
index 0571602129306f89292c482a7dc4858ea08a9867..2892367830c90353698ef0554b84c1e66d36a0a2 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 options'
+test_description='git p4 options'
 
 . ./lib-git-p4.sh
 
@@ -24,11 +24,11 @@ test_expect_success 'init depot' '
 '
 
 test_expect_success 'clone no --git-dir' '
-       test_must_fail "$GITP4" clone --git-dir=xx //depot
+       test_must_fail git p4 clone --git-dir=xx //depot
 '
 
 test_expect_success 'clone --branch' '
-       "$GITP4" clone --branch=refs/remotes/p4/sb --dest="$git" //depot &&
+       git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -42,7 +42,7 @@ test_expect_success 'clone --changesfile' '
        cf="$TRASH_DIRECTORY/cf" &&
        test_when_finished "rm \"$cf\"" &&
        printf "1\n3\n" >"$cf" &&
-       "$GITP4" clone --changesfile="$cf" --dest="$git" //depot &&
+       git p4 clone --changesfile="$cf" --dest="$git" //depot &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -58,14 +58,14 @@ test_expect_success 'clone --changesfile, @all' '
        cf="$TRASH_DIRECTORY/cf" &&
        test_when_finished "rm \"$cf\"" &&
        printf "1\n3\n" >"$cf" &&
-       test_must_fail "$GITP4" clone --changesfile="$cf" --dest="$git" //depot@all
+       test_must_fail git p4 clone --changesfile="$cf" --dest="$git" //depot@all
 '
 
 # imports both master and p4/master in refs/heads
 # requires --import-local on sync to find p4 refs/heads
 # does not update master on sync, just p4/master
 test_expect_success 'clone/sync --import-local' '
-       "$GITP4" clone --import-local --dest="$git" //depot@1,2 &&
+       git p4 clone --import-local --dest="$git" //depot@1,2 &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -73,9 +73,9 @@ test_expect_success 'clone/sync --import-local' '
                test_line_count = 2 lines &&
                git log --oneline refs/heads/p4/master >lines &&
                test_line_count = 2 lines &&
-               test_must_fail "$GITP4" sync &&
+               test_must_fail git p4 sync &&
 
-               "$GITP4" sync --import-local &&
+               git p4 sync --import-local &&
                git log --oneline refs/heads/master >lines &&
                test_line_count = 2 lines &&
                git log --oneline refs/heads/p4/master >lines &&
@@ -84,7 +84,7 @@ test_expect_success 'clone/sync --import-local' '
 '
 
 test_expect_success 'clone --max-changes' '
-       "$GITP4" clone --dest="$git" --max-changes 2 //depot@all &&
+       git p4 clone --dest="$git" --max-changes 2 //depot@all &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -101,7 +101,7 @@ test_expect_success 'clone --keep-path' '
                p4 add sub/dir/f4 &&
                p4 submit -d "change 4"
        ) &&
-       "$GITP4" clone --dest="$git" --keep-path //depot/sub/dir@all &&
+       git p4 clone --dest="$git" --keep-path //depot/sub/dir@all &&
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
@@ -109,7 +109,7 @@ test_expect_success 'clone --keep-path' '
                test_path_is_file sub/dir/f4
        ) &&
        cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot/sub/dir@all &&
+       git p4 clone --dest="$git" //depot/sub/dir@all &&
        (
                cd "$git" &&
                test_path_is_file f4 &&
@@ -126,7 +126,7 @@ test_expect_success 'clone --use-client-spec' '
        (
                # big usage message
                exec >/dev/null &&
-               test_must_fail "$GITP4" clone --dest="$git" --use-client-spec
+               test_must_fail git p4 clone --dest="$git" --use-client-spec
        ) &&
        cli2="$TRASH_DIRECTORY/cli2" &&
        mkdir -p "$cli2" &&
@@ -142,7 +142,7 @@ test_expect_success 'clone --use-client-spec' '
        ) &&
        P4CLIENT=client2 &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" --use-client-spec //depot/... &&
+       git p4 clone --dest="$git" --use-client-spec //depot/... &&
        (
                cd "$git" &&
                test_path_is_file bus/dir/f4 &&
@@ -156,7 +156,7 @@ test_expect_success 'clone --use-client-spec' '
                cd "$git" &&
                git init &&
                git config git-p4.useClientSpec true &&
-               "$GITP4" sync //depot/... &&
+               git p4 sync //depot/... &&
                git checkout -b master p4/master &&
                test_path_is_file bus/dir/f4 &&
                test_path_is_missing file1
index b1f61e3db555cc939135d3c40b78300103bcc1eb..f23b4c3620592704cac35839a010ce55de139da7 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 submit'
+test_description='git p4 submit'
 
 . ./lib-git-p4.sh
 
@@ -19,7 +19,7 @@ test_expect_success 'init depot' '
 
 test_expect_success 'submit with no client dir' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                echo file2 >file2 &&
@@ -27,24 +27,28 @@ test_expect_success 'submit with no client dir' '
                git commit -m "git commit 2" &&
                rm -rf "$cli" &&
                git config git-p4.skipSubmitEdit true &&
-               "$GITP4" submit
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file file1 &&
+               test_path_is_file file2
        )
 '
 
 # make two commits, but tell it to apply only from HEAD^
 test_expect_success 'submit --origin' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                test_commit "file3" &&
                test_commit "file4" &&
                git config git-p4.skipSubmitEdit true &&
-               "$GITP4" submit --origin=HEAD^
+               git p4 submit --origin=HEAD^
        ) &&
        (
                cd "$cli" &&
-               p4 sync &&
                test_path_is_missing "file3.t" &&
                test_path_is_file "file4.t"
        )
@@ -52,39 +56,132 @@ test_expect_success 'submit --origin' '
 
 test_expect_success 'submit with allowSubmit' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                test_commit "file5" &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.allowSubmit "nobranch" &&
-               test_must_fail "$GITP4" submit &&
+               test_must_fail git p4 submit &&
                git config git-p4.allowSubmit "nobranch,master" &&
-               "$GITP4" submit
+               git p4 submit
        )
 '
 
 test_expect_success 'submit with master branch name from argv' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                test_commit "file6" &&
                git config git-p4.skipSubmitEdit true &&
-               test_must_fail "$GITP4" submit nobranch &&
+               test_must_fail git p4 submit nobranch &&
                git branch otherbranch &&
                git reset --hard HEAD^ &&
                test_commit "file7" &&
-               "$GITP4" submit otherbranch
+               git p4 submit otherbranch
        ) &&
        (
                cd "$cli" &&
-               p4 sync &&
                test_path_is_file "file6.t" &&
                test_path_is_missing "file7.t"
        )
 '
 
+#
+# Basic submit tests, the five handled cases
+#
+
+test_expect_success 'submit modify' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               echo line >>file1 &&
+               git add file1 &&
+               git commit -m file1 &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file file1 &&
+               test_line_count = 2 file1
+       )
+'
+
+test_expect_success 'submit add' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               echo file13 >file13 &&
+               git add file13 &&
+               git commit -m file13 &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file file13
+       )
+'
+
+test_expect_success 'submit delete' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               git rm file4.t &&
+               git commit -m "delete file4.t" &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing file4.t
+       )
+'
+
+test_expect_success 'submit copy' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               git config git-p4.detectCopies true &&
+               git config git-p4.detectCopiesHarder true &&
+               cp file5.t file5.ta &&
+               git add file5.ta &&
+               git commit -m "copy to file5.ta" &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file file5.ta &&
+               test ! -w file5.ta
+       )
+'
+
+test_expect_success 'submit rename' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               git config git-p4.detectRenames true &&
+               git mv file6.t file6.ta &&
+               git commit -m "rename file6.t to file6.ta" &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing file6.t &&
+               test_path_is_file file6.ta &&
+               test ! -w file6.ta
+       )
+'
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index f0022839c76ede4b5f135a7318b9c53ede4a944a..2f8014a60ef07d84430b9a696d46cd63c4822524 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 relative chdir'
+test_description='git p4 relative chdir'
 
 . ./lib-git-p4.sh
 
@@ -26,7 +26,7 @@ test_expect_success 'P4CONFIG and absolute dir clone' '
        (
                P4CONFIG=p4config && export P4CONFIG &&
                sane_unset P4PORT P4CLIENT &&
-               "$GITP4" clone --verbose --dest="$git" //depot
+               git p4 clone --verbose --dest="$git" //depot
        )
 '
 
@@ -38,7 +38,7 @@ test_expect_success 'P4CONFIG and relative dir clone' '
        (
                P4CONFIG=p4config && export P4CONFIG &&
                sane_unset P4PORT P4CLIENT &&
-               "$GITP4" clone --verbose --dest="git" //depot
+               git p4 clone --verbose --dest="git" //depot
        )
 '
 
index 773a516ff0f40d396cb04cc474c697617192ae71..7d993ef80acdf1775d70713d2b9abfb984c47c1e 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-p4 client view'
+test_description='git p4 client view'
 
 . ./lib-git-p4.sh
 
@@ -96,25 +96,25 @@ test_expect_success 'init depot' '
 test_expect_success 'unsupported view wildcard %%n' '
        client_view "//depot/%%%%1/sub/... //client/sub/%%%%1/..." &&
        test_when_finished cleanup_git &&
-       test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot
+       test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
 '
 
 test_expect_success 'unsupported view wildcard *' '
        client_view "//depot/*/bar/... //client/*/bar/..." &&
        test_when_finished cleanup_git &&
-       test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot
+       test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
 '
 
 test_expect_success 'wildcard ... only supported at end of spec 1' '
        client_view "//depot/.../file11 //client/.../file11" &&
        test_when_finished cleanup_git &&
-       test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot
+       test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
 '
 
 test_expect_success 'wildcard ... only supported at end of spec 2' '
        client_view "//depot/.../a/... //client/.../a/..." &&
        test_when_finished cleanup_git &&
-       test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot
+       test_must_fail git p4 clone --use-client-spec --dest="$git" //depot
 '
 
 test_expect_success 'basic map' '
@@ -122,7 +122,7 @@ test_expect_success 'basic map' '
        files="cli1/file11 cli1/file12" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -130,7 +130,7 @@ test_expect_success 'client view with no mappings' '
        client_view &&
        client_verify &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify
 '
 
@@ -139,7 +139,7 @@ test_expect_success 'single file map' '
        files="file11" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -150,7 +150,7 @@ test_expect_success 'later mapping takes precedence (entire repo)' '
               cli2/dir2/file21 cli2/dir2/file22" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -160,7 +160,7 @@ test_expect_success 'later mapping takes precedence (partial repo)' '
        files="file21 file22" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -176,7 +176,7 @@ test_expect_success 'depot path matching rejected client path' '
        files="cli12/file21 cli12/file22" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -187,7 +187,7 @@ test_expect_success 'exclusion wildcard, client rhs same (odd)' '
                    "-//depot/dir2/... //client/..." &&
        client_verify &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify
 '
 
@@ -197,7 +197,7 @@ test_expect_success 'exclusion wildcard, client rhs different (normal)' '
        files="dir1/file11 dir1/file12" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -207,7 +207,7 @@ test_expect_success 'exclusion single file' '
        files="dir1/file11 dir1/file12 dir2/file21" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -217,7 +217,7 @@ test_expect_success 'overlay wildcard' '
        files="cli/file11 cli/file12 cli/file21 cli/file22" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -227,7 +227,7 @@ test_expect_success 'overlay single file' '
        files="cli/file11 cli/file12 cli/file21" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -238,7 +238,7 @@ test_expect_success 'exclusion with later inclusion' '
        files="dir1/file11 dir1/file12 dir2incl/file21 dir2incl/file22" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -246,7 +246,7 @@ test_expect_success 'quotes on rhs only' '
        client_view "//depot/dir1/... \"//client/cdir 1/...\"" &&
        client_verify "cdir 1/file11" "cdir 1/file12" &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify "cdir 1/file11" "cdir 1/file12"
 '
 
@@ -258,7 +258,7 @@ test_expect_success 'quotes on rhs only' '
 test_expect_success 'clone --use-client-spec sets useClientSpec' '
        client_view "//depot/... //client/..." &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        (
                cd "$git" &&
                git config --bool git-p4.useClientSpec >actual &&
@@ -273,7 +273,7 @@ test_expect_success 'subdir clone' '
        files="dir1/file11 dir1/file12 dir2/file21 dir2/file22" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
        git_verify dir1/file11 dir1/file12
 '
 
@@ -283,14 +283,14 @@ test_expect_success 'subdir clone' '
 test_expect_success 'subdir clone, submit modify' '
        client_view "//depot/... //client/..." &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
                echo line >>dir1/file12 &&
                git add dir1/file12 &&
                git commit -m dir1/file12 &&
-               "$GITP4" submit
+               git p4 submit
        ) &&
        (
                cd "$cli" &&
@@ -302,14 +302,14 @@ test_expect_success 'subdir clone, submit modify' '
 test_expect_success 'subdir clone, submit add' '
        client_view "//depot/... //client/..." &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
                echo file13 >dir1/file13 &&
                git add dir1/file13 &&
                git commit -m dir1/file13 &&
-               "$GITP4" submit
+               git p4 submit
        ) &&
        (
                cd "$cli" &&
@@ -320,13 +320,13 @@ test_expect_success 'subdir clone, submit add' '
 test_expect_success 'subdir clone, submit delete' '
        client_view "//depot/... //client/..." &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
                git rm dir1/file12 &&
                git commit -m "delete dir1/file12" &&
-               "$GITP4" submit
+               git p4 submit
        ) &&
        (
                cd "$cli" &&
@@ -337,7 +337,7 @@ test_expect_success 'subdir clone, submit delete' '
 test_expect_success 'subdir clone, submit copy' '
        client_view "//depot/... //client/..." &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
@@ -345,37 +345,71 @@ test_expect_success 'subdir clone, submit copy' '
                cp dir1/file11 dir1/file11a &&
                git add dir1/file11a &&
                git commit -m "copy to dir1/file11a" &&
-               "$GITP4" submit
+               git p4 submit
        ) &&
        (
                cd "$cli" &&
-               test_path_is_file dir1/file11a
+               test_path_is_file dir1/file11a &&
+               test ! -w dir1/file11a
        )
 '
 
 test_expect_success 'subdir clone, submit rename' '
        client_view "//depot/... //client/..." &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.detectRenames true &&
                git mv dir1/file13 dir1/file13a &&
                git commit -m "rename dir1/file13 to dir1/file13a" &&
-               "$GITP4" submit
+               git p4 submit
        ) &&
        (
                cd "$cli" &&
                test_path_is_missing dir1/file13 &&
-               test_path_is_file dir1/file13a
+               test_path_is_file dir1/file13a &&
+               test ! -w dir1/file13a
+       )
+'
+
+# see t9800 for the non-client-spec case, and the rest of the wildcard tests
+test_expect_success 'wildcard files submit back to p4, client-spec case' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       git p4 clone --use-client-spec --dest="$git" //depot/dir1 &&
+       (
+               cd "$git" &&
+               echo git-wild-hash >dir1/git-wild#hash &&
+               echo git-wild-star >dir1/git-wild\*star &&
+               echo git-wild-at >dir1/git-wild@at &&
+               echo git-wild-percent >dir1/git-wild%percent &&
+               git add dir1/git-wild* &&
+               git commit -m "add some wildcard filenames" &&
+               git config git-p4.skipSubmitEditCheck true &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file dir1/git-wild#hash &&
+               test_path_is_file dir1/git-wild\*star &&
+               test_path_is_file dir1/git-wild@at &&
+               test_path_is_file dir1/git-wild%percent
+       ) &&
+       (
+               # delete these carefully, cannot just do "p4 delete"
+               # on files with wildcards; but git-p4 knows how
+               cd "$git" &&
+               git rm dir1/git-wild* &&
+               git commit -m "clean up the wildcards" &&
+               git p4 submit
        )
 '
 
 test_expect_success 'reinit depot' '
        (
                cd "$cli" &&
-               p4 sync -f &&
                rm files &&
                p4 delete */* &&
                p4 submit -d "delete all files" &&
@@ -419,7 +453,7 @@ test_expect_success 'overlay collision 1 to 2' '
        client_verify $files &&
        test_cmp actual "$cli"/filecollide &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files &&
        test_cmp actual "$git"/filecollide
 '
@@ -432,7 +466,7 @@ test_expect_failure 'overlay collision 2 to 1' '
        client_verify $files &&
        test_cmp actual "$cli"/filecollide &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files &&
        test_cmp actual "$git"/filecollide
 '
@@ -454,7 +488,7 @@ test_expect_failure 'overlay collision 1 to 2, but 2 deleted' '
        files="file11 file12 file21 file22" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -477,7 +511,7 @@ test_expect_failure 'overlay collision 1 to 2, but 2 deleted, then 1 updated' '
        files="file11 file12 file21 file22" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files
 '
 
@@ -533,7 +567,7 @@ test_expect_success 'overlay sync: initial git checkout' '
        echo dir1/colA >actual &&
        client_verify $files &&
        test_cmp actual "$cli"/colA &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files &&
        test_cmp actual "$git"/colA
 '
@@ -558,7 +592,7 @@ test_expect_success 'overlay sync: colA content switch' '
        test_cmp actual "$cli"/colA &&
        (
                cd "$git" &&
-               "$GITP4" sync --use-client-spec &&
+               git p4 sync --use-client-spec &&
                git merge --ff-only p4/master
        ) &&
        git_verify $files &&
@@ -585,7 +619,7 @@ test_expect_success 'overlay sync: colB appears' '
        test_cmp actual "$cli"/colB &&
        (
                cd "$git" &&
-               "$GITP4" sync --use-client-spec &&
+               git p4 sync --use-client-spec &&
                git merge --ff-only p4/master
        ) &&
        git_verify $files &&
@@ -613,7 +647,7 @@ test_expect_success 'overlay sync: colB disappears' '
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
-               "$GITP4" sync --use-client-spec &&
+               git p4 sync --use-client-spec &&
                git merge --ff-only p4/master
        ) &&
        git_verify $files
@@ -671,7 +705,7 @@ test_expect_success 'overlay sync swap: initial git checkout' '
        echo dir1/colA >actual &&
        client_verify $files &&
        test_cmp actual "$cli"/colA &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify $files &&
        test_cmp actual "$git"/colA
 '
@@ -696,7 +730,7 @@ test_expect_failure 'overlay sync swap: colA no content switch' '
        test_cmp actual "$cli"/colA &&
        (
                cd "$git" &&
-               "$GITP4" sync --use-client-spec &&
+               git p4 sync --use-client-spec &&
                git merge --ff-only p4/master
        ) &&
        git_verify $files &&
@@ -723,7 +757,7 @@ test_expect_success 'overlay sync swap: colB appears' '
        test_cmp actual "$cli"/colB &&
        (
                cd "$git" &&
-               "$GITP4" sync --use-client-spec &&
+               git p4 sync --use-client-spec &&
                git merge --ff-only p4/master
        ) &&
        git_verify $files &&
@@ -753,7 +787,7 @@ test_expect_failure 'overlay sync swap: colB no change' '
        test_when_finished cleanup_git &&
        (
                cd "$git" &&
-               "$GITP4" sync --use-client-spec &&
+               git p4 sync --use-client-spec &&
                git merge --ff-only p4/master
        ) &&
        git_verify $files &&
@@ -801,7 +835,7 @@ test_expect_success 'quotes on lhs only' '
        files="cdir1/file11 cdir1/file12" &&
        client_verify $files &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        client_verify $files
 '
 
@@ -809,7 +843,7 @@ test_expect_success 'quotes on both sides' '
        client_view "\"//depot/dir 1/...\" \"//client/cdir 1/...\"" &&
        client_verify "cdir 1/file11" "cdir 1/file12" &&
        test_when_finished cleanup_git &&
-       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       git p4 clone --use-client-spec --dest="$git" //depot &&
        git_verify "cdir 1/file11" "cdir 1/file12"
 '
 
index 49dfde061613ccb848421d1dfe489e4e592a36ee..d8d9ca46793a6c09a19beb350e2f18dd199eb2c4 100755 (executable)
@@ -84,13 +84,13 @@ scrub_ko_check () {
 #
 test_expect_success 'edit far away from RCS lines' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
                sed -i "s/^line7/line7 edit/" filek &&
                git commit -m "filek line7 edit" filek &&
-               "$GITP4" submit &&
+               git p4 submit &&
                scrub_k_check filek
        )
 '
@@ -100,14 +100,14 @@ test_expect_success 'edit far away from RCS lines' '
 #
 test_expect_success 'edit near RCS lines' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.attemptRCSCleanup true &&
                sed -i "s/^line4/line4 edit/" filek &&
                git commit -m "filek line4 edit" filek &&
-               "$GITP4" submit &&
+               git p4 submit &&
                scrub_k_check filek
        )
 '
@@ -117,14 +117,14 @@ test_expect_success 'edit near RCS lines' '
 #
 test_expect_success 'edit keyword lines' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.attemptRCSCleanup true &&
                sed -i "/Revision/d" filek &&
                git commit -m "filek remove Revision line" filek &&
-               "$GITP4" submit &&
+               git p4 submit &&
                scrub_k_check filek
        )
 '
@@ -134,14 +134,14 @@ test_expect_success 'edit keyword lines' '
 #
 test_expect_success 'scrub ko files differently' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.attemptRCSCleanup true &&
                sed -i "s/^line4/line4 edit/" fileko &&
                git commit -m "fileko line4 edit" fileko &&
-               "$GITP4" submit &&
+               git p4 submit &&
                scrub_ko_check fileko &&
                ! scrub_k_check fileko
        )
@@ -168,7 +168,7 @@ test_expect_success 'cleanup after failure' '
 #
 test_expect_success 'do not scrub plain text' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
@@ -181,7 +181,7 @@ test_expect_success 'do not scrub plain text' '
                        sed -i "s/^line5/line5 p4 edit/" file_text &&
                        p4 submit -d "file5 p4 edit"
                ) &&
-               ! "$GITP4" submit &&
+               ! git p4 submit &&
                (
                        # exepct something like:
                        #    file_text - file(s) not opened on this client
@@ -239,7 +239,7 @@ p4_append_to_file () {
 # even though the change itself would otherwise apply cleanly.
 test_expect_success 'cope with rcs keyword expansion damage' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
@@ -252,10 +252,10 @@ test_expect_success 'cope with rcs keyword expansion damage' '
 
                git add kwfile1.c &&
                git commit -m "Zap an RCS kw line" &&
-               "$GITP4" submit &&
-               "$GITP4" rebase &&
+               git p4 submit &&
+               git p4 rebase &&
                git diff p4/master &&
-               "$GITP4" commit &&
+               git p4 commit &&
                echo "try modifying in both" &&
                cd "$cli" &&
                p4 edit kwfile1.c &&
@@ -265,8 +265,8 @@ test_expect_success 'cope with rcs keyword expansion damage' '
                echo "line from git at the top" | cat - kwfile1.c >kwfile1.c.new &&
                mv kwfile1.c.new kwfile1.c &&
                git commit -m "Add line in git at the top" kwfile1.c &&
-               "$GITP4" rebase &&
-               "$GITP4" submit
+               git p4 rebase &&
+               git p4 submit
        )
 '
 
@@ -280,7 +280,7 @@ test_expect_success 'cope with rcs keyword file deletion' '
                cat kwdelfile.c &&
                grep 1 kwdelfile.c
        ) &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                grep Revision kwdelfile.c &&
@@ -288,7 +288,7 @@ test_expect_success 'cope with rcs keyword file deletion' '
                git commit -m "Delete a file containing RCS keywords" &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.attemptRCSCleanup true &&
-               "$GITP4" submit
+               git p4 submit
        ) &&
        (
                cd "$cli" &&
@@ -301,7 +301,7 @@ test_expect_success 'cope with rcs keyword file deletion' '
 # work fine without any special handling.
 test_expect_success 'Add keywords in git which match the default p4 values' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                echo "NewKW: \$Revision\$" >>kwfile1.c &&
@@ -309,7 +309,7 @@ test_expect_success 'Add keywords in git which match the default p4 values' '
                git commit -m "Adding RCS keywords in git" &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.attemptRCSCleanup true &&
-               "$GITP4" submit
+               git p4 submit
        ) &&
        (
                cd "$cli" &&
@@ -325,7 +325,7 @@ test_expect_success 'Add keywords in git which match the default p4 values' '
 #
 test_expect_failure 'Add keywords in git which do not match the default p4 values' '
        test_when_finished cleanup_git &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                echo "NewKW2: \$Revision:1\$" >>kwfile1.c &&
@@ -333,7 +333,7 @@ test_expect_failure 'Add keywords in git which do not match the default p4 value
                git commit -m "Adding RCS keywords in git" &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.attemptRCSCleanup true &&
-               "$GITP4" submit
+               git p4 submit
        ) &&
        (
                cd "$cli" &&
@@ -356,7 +356,7 @@ test_expect_success 'merge conflict handling still works' '
                p4 add -t ktext merge2.c &&
                p4 submit -d "add merge test file"
        ) &&
-       "$GITP4" clone --dest="$git" //depot &&
+       git p4 clone --dest="$git" //depot &&
        (
                cd "$git" &&
                sed -e "/Hello/d" merge2.c >merge2.c.tmp &&
@@ -374,7 +374,7 @@ test_expect_success 'merge conflict handling still works' '
                test -f merge2.c &&
                git config git-p4.skipSubmitEdit true &&
                git config git-p4.attemptRCSCleanup true &&
-               !(echo "s" | "$GITP4" submit) &&
+               !(echo "s" | git p4 submit) &&
                git rebase --skip &&
                ! test -f merge2.c
        )
diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh
new file mode 100755 (executable)
index 0000000..095238f
--- /dev/null
@@ -0,0 +1,222 @@
+#!/bin/sh
+
+test_description='git p4 label tests'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+       start_p4d
+'
+
+# Basic p4 label import tests.
+#
+test_expect_success 'basic p4 labels' '
+       test_when_finished cleanup_git &&
+       (
+               cd "$cli" &&
+               mkdir -p main &&
+
+               echo f1 >main/f1 &&
+               p4 add main/f1 &&
+               p4 submit -d "main/f1" &&
+
+               echo f2 >main/f2 &&
+               p4 add main/f2 &&
+               p4 submit -d "main/f2" &&
+
+               echo f3 >main/file_with_\$metachar &&
+               p4 add main/file_with_\$metachar &&
+               p4 submit -d "file with metachar" &&
+
+               p4 tag -l TAG_F1_ONLY main/f1 &&
+               p4 tag -l TAG_WITH\$_SHELL_CHAR main/... &&
+               p4 tag -l this_tag_will_be\ skipped main/... &&
+
+               echo f4 >main/f4 &&
+               p4 add main/f4 &&
+               p4 submit -d "main/f4" &&
+
+               p4 label -i <<-EOF &&
+               Label: TAG_LONG_LABEL
+               Description:
+                  A Label first line
+                  A Label second line
+               View:   //depot/...
+               EOF
+
+               p4 tag -l TAG_LONG_LABEL ... &&
+
+               p4 labels ... &&
+
+               git p4 clone --dest="$git" //depot@all &&
+               cd "$git" &&
+               git config git-p4.labelImportRegexp ".*TAG.*" &&
+               git p4 sync --import-labels --verbose &&
+
+               git tag &&
+               git tag >taglist &&
+               test_line_count = 3 taglist &&
+
+               cd main &&
+               git checkout TAG_F1_ONLY &&
+               ! test -f f2 &&
+               git checkout TAG_WITH\$_SHELL_CHAR &&
+               test -f f1 && test -f f2 && test -f file_with_\$metachar &&
+
+               git show TAG_LONG_LABEL | grep -q "A Label second line"
+       )
+'
+# Test some label corner cases:
+#
+# - two tags on the same file; both should be available
+# - a tag that is only on one file; this kind of tag
+#   cannot be imported (at least not easily).
+
+test_expect_success 'two labels on the same changelist' '
+       test_when_finished cleanup_git &&
+       (
+               cd "$cli" &&
+               mkdir -p main &&
+
+               p4 edit main/f1 main/f2 &&
+               echo "hello world" >main/f1 &&
+               echo "not in the tag" >main/f2 &&
+               p4 submit -d "main/f[12]: testing two labels" &&
+
+               p4 tag -l TAG_F1_1 main/... &&
+               p4 tag -l TAG_F1_2 main/... &&
+
+               p4 labels ... &&
+
+               git p4 clone --dest="$git" //depot@all &&
+               cd "$git" &&
+               git p4 sync --import-labels &&
+
+               git tag | grep TAG_F1 &&
+               git tag | grep -q TAG_F1_1 &&
+               git tag | grep -q TAG_F1_2 &&
+
+               cd main &&
+
+               git checkout TAG_F1_1 &&
+               ls &&
+               test -f f1 &&
+
+               git checkout TAG_F1_2 &&
+               ls &&
+               test -f f1
+       )
+'
+
+# Export some git tags to p4
+test_expect_success 'export git tags to p4' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot@all &&
+       (
+               cd "$git" &&
+               git tag -m "A tag created in git:xyzzy" GIT_TAG_1 &&
+               echo "hello world" >main/f10 &&
+               git add main/f10 &&
+               git commit -m "Adding file for export test" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit &&
+               git tag -m "Another git tag" GIT_TAG_2 &&
+               git tag LIGHTWEIGHT_TAG &&
+               git p4 rebase --import-labels --verbose &&
+               git p4 submit --export-labels --verbose
+       ) &&
+       (
+               cd "$cli" &&
+               p4 sync ... &&
+               p4 labels ... | grep GIT_TAG_1 &&
+               p4 labels ... | grep GIT_TAG_2 &&
+               p4 labels ... | grep LIGHTWEIGHT_TAG &&
+               p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" &&
+               p4 sync ...@GIT_TAG_1 &&
+               ! test -f main/f10
+               p4 sync ...@GIT_TAG_2 &&
+               test -f main/f10
+       )
+'
+
+# Export a tag from git where an affected file is deleted later on
+# Need to create git tags after rebase, since only then can the
+# git commits be mapped to p4 changelists.
+test_expect_success 'export git tags to p4 with deletion' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot@all &&
+       (
+               cd "$git" &&
+               git p4 sync --import-labels &&
+               echo "deleted file" >main/deleted_file &&
+               git add main/deleted_file &&
+               git commit -m "create deleted file" &&
+               git rm main/deleted_file &&
+               echo "new file" >main/f11 &&
+               git add main/f11 &&
+               git commit -m "delete the deleted file" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit &&
+               git p4 rebase --import-labels --verbose &&
+               git tag -m "tag on deleted file" GIT_TAG_ON_DELETED HEAD~1 &&
+               git tag -m "tag after deletion" GIT_TAG_AFTER_DELETION HEAD &&
+               git p4 submit --export-labels --verbose
+       ) &&
+       (
+               cd "$cli" &&
+               p4 sync ... &&
+               p4 sync ...@GIT_TAG_ON_DELETED &&
+               test -f main/deleted_file &&
+               p4 sync ...@GIT_TAG_AFTER_DELETION &&
+               ! test -f main/deleted_file &&
+               echo "checking label contents" &&
+               p4 label -o GIT_TAG_ON_DELETED | grep "tag on deleted file"
+       )
+'
+
+# Create a tag in git that cannot be exported to p4
+test_expect_success 'tag that cannot be exported' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot@all &&
+       (
+               cd "$git" &&
+               git checkout -b a_branch &&
+               echo "hello" >main/f12 &&
+               git add main/f12 &&
+               git commit -m "adding f12" &&
+               git tag -m "tag on a_branch" GIT_TAG_ON_A_BRANCH &&
+               git checkout master &&
+               git p4 submit --export-labels
+       ) &&
+       (
+               cd "$cli" &&
+               p4 sync ... &&
+               !(p4 labels | grep GIT_TAG_ON_A_BRANCH)
+       )
+'
+
+test_expect_success 'use git config to enable import/export of tags' '
+       git p4 clone --verbose --dest="$git" //depot@all &&
+       (
+               cd "$git" &&
+               git config git-p4.exportLabels true &&
+               git config git-p4.importLabels true &&
+               git tag CFG_A_GIT_TAG &&
+               git p4 rebase --verbose &&
+               git p4 submit --verbose &&
+               git tag &&
+               git tag | grep TAG_F1_1
+       ) &&
+       (
+               cd "$cli" &&
+               p4 labels &&
+               p4 labels | grep CFG_A_GIT_TAG
+       )
+'
+
+
+test_expect_success 'kill p4d' '
+       kill_p4d
+'
+
+test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
new file mode 100755 (executable)
index 0000000..0f09fd6
--- /dev/null
@@ -0,0 +1,243 @@
+#!/bin/sh
+#
+# Copyright (c) 2012 Felipe Contreras
+#
+
+if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then
+       # we are in full-on bash mode
+       true
+elif type bash >/dev/null 2>&1; then
+       # execute in full-on bash mode
+       unset POSIXLY_CORRECT
+       exec bash "$0" "$@"
+else
+       echo '1..0 #SKIP skipping bash completion tests; bash not available'
+       exit 0
+fi
+
+test_description='test bash completion'
+
+. ./test-lib.sh
+
+complete ()
+{
+       # do nothing
+       return 0
+}
+
+. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash"
+
+# We don't need this function to actually join words or do anything special.
+# Also, it's cleaner to avoid touching bash's internal completion variables.
+# So let's override it with a minimal version for testing purposes.
+_get_comp_words_by_ref ()
+{
+       while [ $# -gt 0 ]; do
+               case "$1" in
+               cur)
+                       cur=${_words[_cword]}
+                       ;;
+               prev)
+                       prev=${_words[_cword-1]}
+                       ;;
+               words)
+                       words=("${_words[@]}")
+                       ;;
+               cword)
+                       cword=$_cword
+                       ;;
+               esac
+               shift
+       done
+}
+
+print_comp ()
+{
+       local IFS=$'\n'
+       echo "${COMPREPLY[*]}" > out
+}
+
+run_completion ()
+{
+       local -a COMPREPLY _words
+       local _cword
+       _words=( $1 )
+       (( _cword = ${#_words[@]} - 1 ))
+       __git_wrap_git && print_comp
+}
+
+test_completion ()
+{
+       test $# -gt 1 && echo "$2" > expected
+       run_completion "$@" &&
+       test_cmp expected out
+}
+
+newline=$'\n'
+
+test_expect_success '__gitcomp - trailing space - options' '
+       sed -e "s/Z$//" >expected <<-\EOF &&
+       --reuse-message=Z
+       --reedit-message=Z
+       --reset-author Z
+       EOF
+       (
+               local -a COMPREPLY &&
+               cur="--re" &&
+               __gitcomp "--dry-run --reuse-message= --reedit-message=
+                               --reset-author" &&
+               IFS="$newline" &&
+               echo "${COMPREPLY[*]}" > out
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__gitcomp - trailing space - config keys' '
+       sed -e "s/Z$//" >expected <<-\EOF &&
+       branch.Z
+       branch.autosetupmerge Z
+       branch.autosetuprebase Z
+       browser.Z
+       EOF
+       (
+               local -a COMPREPLY &&
+               cur="br" &&
+               __gitcomp "branch. branch.autosetupmerge
+                               branch.autosetuprebase browser." &&
+               IFS="$newline" &&
+               echo "${COMPREPLY[*]}" > out
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__gitcomp - option parameter' '
+       sed -e "s/Z$//" >expected <<-\EOF &&
+       recursive Z
+       resolve Z
+       EOF
+       (
+               local -a COMPREPLY &&
+               cur="--strategy=re" &&
+               __gitcomp "octopus ours recursive resolve subtree
+                       " "" "re" &&
+               IFS="$newline" &&
+               echo "${COMPREPLY[*]}" > out
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__gitcomp - prefix' '
+       sed -e "s/Z$//" >expected <<-\EOF &&
+       branch.maint.merge Z
+       branch.maint.mergeoptions Z
+       EOF
+       (
+               local -a COMPREPLY &&
+               cur="branch.me" &&
+               __gitcomp "remote merge mergeoptions rebase
+                       " "branch.maint." "me" &&
+               IFS="$newline" &&
+               echo "${COMPREPLY[*]}" > out
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__gitcomp - suffix' '
+       sed -e "s/Z$//" >expected <<-\EOF &&
+       branch.master.Z
+       branch.maint.Z
+       EOF
+       (
+               local -a COMPREPLY &&
+               cur="branch.me" &&
+               __gitcomp "master maint next pu
+                       " "branch." "ma" "." &&
+               IFS="$newline" &&
+               echo "${COMPREPLY[*]}" > out
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success 'basic' '
+       run_completion "git \"\"" &&
+       # built-in
+       grep -q "^add \$" out &&
+       # script
+       grep -q "^filter-branch \$" out &&
+       # plumbing
+       ! grep -q "^ls-files \$" out &&
+
+       run_completion "git f" &&
+       ! grep -q -v "^f" out
+'
+
+test_expect_success 'double dash "git" itself' '
+       sed -e "s/Z$//" >expected <<-\EOF &&
+       --paginate Z
+       --no-pager Z
+       --git-dir=
+       --bare Z
+       --version Z
+       --exec-path Z
+       --exec-path=
+       --html-path Z
+       --info-path Z
+       --work-tree=
+       --namespace=
+       --no-replace-objects Z
+       --help Z
+       EOF
+       test_completion "git --"
+'
+
+test_expect_success 'double dash "git checkout"' '
+       sed -e "s/Z$//" >expected <<-\EOF &&
+       --quiet Z
+       --ours Z
+       --theirs Z
+       --track Z
+       --no-track Z
+       --merge Z
+       --conflict=
+       --orphan Z
+       --patch Z
+       EOF
+       test_completion "git checkout --"
+'
+
+test_expect_success 'general options' '
+       test_completion "git --ver" "--version " &&
+       test_completion "git --hel" "--help " &&
+       sed -e "s/Z$//" >expected <<-\EOF &&
+       --exec-path Z
+       --exec-path=
+       EOF
+       test_completion "git --exe" &&
+       test_completion "git --htm" "--html-path " &&
+       test_completion "git --pag" "--paginate " &&
+       test_completion "git --no-p" "--no-pager " &&
+       test_completion "git --git" "--git-dir=" &&
+       test_completion "git --wor" "--work-tree=" &&
+       test_completion "git --nam" "--namespace=" &&
+       test_completion "git --bar" "--bare " &&
+       test_completion "git --inf" "--info-path " &&
+       test_completion "git --no-r" "--no-replace-objects "
+'
+
+test_expect_success 'general options plus command' '
+       test_completion "git --version check" "checkout " &&
+       test_completion "git --paginate check" "checkout " &&
+       test_completion "git --git-dir=foo check" "checkout " &&
+       test_completion "git --bare check" "checkout " &&
+       test_completion "git --help des" "describe " &&
+       test_completion "git --exec-path=foo check" "checkout " &&
+       test_completion "git --html-path check" "checkout " &&
+       test_completion "git --no-pager check" "checkout " &&
+       test_completion "git --work-tree=foo check" "checkout " &&
+       test_completion "git --namespace=foo check" "checkout " &&
+       test_completion "git --paginate check" "checkout " &&
+       test_completion "git --info-path check" "checkout " &&
+       test_completion "git --no-replace-objects check" "checkout "
+'
+
+test_done
index b7d7100c4e5c060c1956e5d4f372fa1fa6ee8c85..9e2b71132ab7bca267be755999211783c9b77f43 100644 (file)
@@ -615,6 +615,7 @@ case $(uname -s) in
        ;;
 esac
 
+( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_PERL" && test_set_prereq PERL
 test -z "$NO_PYTHON" && test_set_prereq PYTHON
 test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE
index 6bcd5b03c078bc532e074ef8e8775a51d0eef219..10afaabbfaed9ee1b4a6a498a2110fb904d48645 100644 (file)
@@ -7,13 +7,14 @@ static const char *usage_msg = "\n"
 
 static void show_dates(char **argv, struct timeval *now)
 {
-       char buf[128];
+       struct strbuf buf = STRBUF_INIT;
 
        for (; *argv; argv++) {
                time_t t = atoi(*argv);
-               show_date_relative(t, 0, now, buf, sizeof(buf));
-               printf("%s -> %s\n", *argv, buf);
+               show_date_relative(t, 0, now, &buf);
+               printf("%s -> %s\n", *argv, buf.buf);
        }
+       strbuf_release(&buf);
 }
 
 static void parse_dates(char **argv, struct timeval *now)
diff --git a/test-mergesort.c b/test-mergesort.c
new file mode 100644 (file)
index 0000000..3f388b4
--- /dev/null
@@ -0,0 +1,52 @@
+#include "cache.h"
+#include "mergesort.h"
+
+struct line {
+       char *text;
+       struct line *next;
+};
+
+static void *get_next(const void *a)
+{
+       return ((const struct line *)a)->next;
+}
+
+static void set_next(void *a, void *b)
+{
+       ((struct line *)a)->next = b;
+}
+
+static int compare_strings(const void *a, const void *b)
+{
+       const struct line *x = a, *y = b;
+       return strcmp(x->text, y->text);
+}
+
+int main(int argc, const char **argv)
+{
+       struct line *line, *p = NULL, *lines = NULL;
+       struct strbuf sb = STRBUF_INIT;
+
+       for (;;) {
+               if (strbuf_getwholeline(&sb, stdin, '\n'))
+                       break;
+               line = xmalloc(sizeof(struct line));
+               line->text = strbuf_detach(&sb, NULL);
+               if (p) {
+                       line->next = p->next;
+                       p->next = line;
+               } else {
+                       line->next = NULL;
+                       lines = line;
+               }
+               p = line;
+       }
+
+       lines = llist_mergesort(lines, get_next, set_next, compare_strings);
+
+       while (lines) {
+               printf("%s", lines->text);
+               lines = lines->next;
+       }
+       return 0;
+}
diff --git a/test-revision-walking.c b/test-revision-walking.c
new file mode 100644 (file)
index 0000000..3ade02c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * test-revision-walking.c: test revision walking API.
+ *
+ * (C) 2012 Heiko Voigt <hvoigt@hvoigt.net>
+ *
+ * This code 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.
+ */
+
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+static void print_commit(struct commit *commit)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct pretty_print_context ctx = {0};
+       ctx.date_mode = DATE_NORMAL;
+       format_commit_message(commit, " %m %s", &sb, &ctx);
+       printf("%s\n", sb.buf);
+       strbuf_release(&sb);
+}
+
+static int run_revision_walk(void)
+{
+       struct rev_info rev;
+       struct commit *commit;
+       const char *argv[] = {NULL, "--all", NULL};
+       int argc = ARRAY_SIZE(argv) - 1;
+       int got_revision = 0;
+
+       init_revisions(&rev, NULL);
+       setup_revisions(argc, argv, &rev, NULL);
+       if (prepare_revision_walk(&rev))
+               die("revision walk setup failed");
+
+       while ((commit = get_revision(&rev)) != NULL) {
+               print_commit(commit);
+               got_revision = 1;
+       }
+
+       reset_revision_walk();
+       return got_revision;
+}
+
+int main(int argc, char **argv)
+{
+       if (argc < 2)
+               return 1;
+
+       if (!strcmp(argv[1], "run-twice")) {
+               printf("1st\n");
+               if (!run_revision_walk())
+                       return 1;
+               printf("2nd\n");
+               if (!run_revision_walk())
+                       return 1;
+
+               return 0;
+       }
+
+       fprintf(stderr, "check usage\n");
+       return 1;
+}
index 8926bc52a9a6e2a16924664372a8af3ee2ed3136..f2d4c0d22bd418836eb47424a03d0e903246d92e 100644 (file)
@@ -1,7 +1,7 @@
 #include "cache.h"
 #include "run-command.h"
 
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
        struct child_process cp;
        int nogit = 0;
@@ -9,12 +9,12 @@ int main(int argc, char **argv)
        setup_git_directory_gently(&nogit);
        if (nogit)
                die("No git repo found");
-       if (!strcmp(argv[1], "--setup-work-tree")) {
+       if (argc > 1 && !strcmp(argv[1], "--setup-work-tree")) {
                setup_work_tree();
                argv++;
        }
        memset(&cp, 0, sizeof(cp));
        cp.git_cmd = 1;
-       cp.argv = (const char **)argv+1;
+       cp.argv = argv + 1;
        return run_command(&cp);
 }
index f6b3b1fb79468ef85eb9581c7d44ff04c1bb61bc..61c928f6cd85aa9cf60942208423e615c9b99352 100644 (file)
@@ -199,7 +199,7 @@ static struct child_process *get_helper(struct transport *transport)
                        data->import_marks = strbuf_detach(&arg, NULL);
                } else if (mandatory) {
                        die("Unknown mandatory capability %s. This remote "
-                           "helper probably needs newer version of Git.\n",
+                           "helper probably needs newer version of Git.",
                            capname);
                }
        }
@@ -599,7 +599,7 @@ static void push_update_ref_status(struct strbuf *buf,
                status = REF_STATUS_REMOTE_REJECT;
                refname = buf->buf + 6;
        } else
-               die("expected ok/error, helper said '%s'\n", buf->buf);
+               die("expected ok/error, helper said '%s'", buf->buf);
 
        msg = strchr(refname, ' ');
        if (msg) {
index ea9dcb6612d92c123c66c2eee03bc933ccb2b13a..1811b500d92b1120a01d0ac09f86c0218f3d163b 100644 (file)
@@ -11,6 +11,7 @@
 #include "branch.h"
 #include "url.h"
 #include "submodule.h"
+#include "string-list.h"
 
 /* rsync support */
 
@@ -721,6 +722,10 @@ void transport_print_push_status(const char *dest, struct ref *refs,
 {
        struct ref *ref;
        int n = 0;
+       unsigned char head_sha1[20];
+       char *head;
+
+       head = resolve_refdup("HEAD", head_sha1, 1, NULL);
 
        if (verbose) {
                for (ref = refs; ref; ref = ref->next)
@@ -738,8 +743,13 @@ void transport_print_push_status(const char *dest, struct ref *refs,
                    ref->status != REF_STATUS_UPTODATE &&
                    ref->status != REF_STATUS_OK)
                        n += print_one_push_status(ref, dest, n, porcelain);
-               if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD)
-                       *nonfastforward = 1;
+               if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD &&
+                   *nonfastforward != NON_FF_HEAD) {
+                       if (!strcmp(head, ref->name))
+                               *nonfastforward = NON_FF_HEAD;
+                       else
+                               *nonfastforward = NON_FF_OTHER;
+               }
        }
 }
 
@@ -1004,6 +1014,25 @@ void transport_set_verbosity(struct transport *transport, int verbosity,
                transport->progress = verbosity >= 0 && isatty(2);
 }
 
+static void die_with_unpushed_submodules(struct string_list *needs_pushing)
+{
+       int i;
+
+       fprintf(stderr, "The following submodule paths contain changes that can\n"
+                       "not be found on any remote:\n");
+       for (i = 0; i < needs_pushing->nr; i++)
+               printf("  %s\n", needs_pushing->items[i].string);
+       fprintf(stderr, "\nPlease try\n\n"
+                       "       git push --recurse-submodules=on-demand\n\n"
+                       "or cd to the path and use\n\n"
+                       "       git push\n\n"
+                       "to push them to a remote.\n\n");
+
+       string_list_clear(needs_pushing, 0);
+
+       die("Aborting.");
+}
+
 int transport_push(struct transport *transport,
                   int refspec_nr, const char **refspec, int flags,
                   int *nonfastforward)
@@ -1044,12 +1073,27 @@ int transport_push(struct transport *transport,
                        flags & TRANSPORT_PUSH_MIRROR,
                        flags & TRANSPORT_PUSH_FORCE);
 
-               if ((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) && !is_bare_repository()) {
+               if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        for (; ref; ref = ref->next)
                                if (!is_null_sha1(ref->new_sha1) &&
-                                   check_submodule_needs_pushing(ref->new_sha1,transport->remote->name))
-                                       die("There are unpushed submodules, aborting.");
+                                   !push_unpushed_submodules(ref->new_sha1,
+                                           transport->remote->name))
+                                   die ("Failed to push all needed submodules!");
+               }
+
+               if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
+                             TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
+                       struct ref *ref = remote_refs;
+                       struct string_list needs_pushing;
+
+                       memset(&needs_pushing, 0, sizeof(struct string_list));
+                       needs_pushing.strdup_strings = 1;
+                       for (; ref; ref = ref->next)
+                               if (!is_null_sha1(ref->new_sha1) &&
+                                   find_unpushed_submodules(ref->new_sha1,
+                                           transport->remote->name, &needs_pushing))
+                                       die_with_unpushed_submodules(&needs_pushing);
                }
 
                push_ret = transport->push_refs(transport, remote_refs, flags);
index ce99ef8b7e1692b6b77bd7fbdee5858ce4bfc408..b866c126e695810131cdab537b8b994c0c32e14e 100644 (file)
@@ -103,6 +103,7 @@ struct transport {
 #define TRANSPORT_PUSH_SET_UPSTREAM 32
 #define TRANSPORT_RECURSE_SUBMODULES_CHECK 64
 #define TRANSPORT_PUSH_PRUNE 128
+#define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
 
 #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
@@ -138,6 +139,8 @@ int transport_set_option(struct transport *transport, const char *name,
 void transport_set_verbosity(struct transport *transport, int verbosity,
        int force_progress);
 
+#define NON_FF_HEAD 1
+#define NON_FF_OTHER 2
 int transport_push(struct transport *connection,
                   int refspec_nr, const char **refspec, int flags,
                   int * nonfastforward);
index 7c9ecf665d062d79e9208875d9bf2577e98f4fb2..ad40109432971b8b26f107a84b02aeb405e8daf9 100644 (file)
@@ -102,21 +102,28 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                opts->unpack_rejects[i].strdup_strings = 1;
 }
 
-static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
-       unsigned int set, unsigned int clear)
+static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
+                        unsigned int set, unsigned int clear)
 {
-       unsigned int size = ce_size(ce);
-       struct cache_entry *new = xmalloc(size);
-
        clear |= CE_HASHED | CE_UNHASHED;
 
        if (set & CE_REMOVE)
                set |= CE_WT_REMOVE;
 
+       ce->next = NULL;
+       ce->ce_flags = (ce->ce_flags & ~clear) | set;
+       add_index_entry(&o->result, 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)
+{
+       unsigned int size = ce_size(ce);
+       struct cache_entry *new = xmalloc(size);
+
        memcpy(new, ce, size);
-       new->next = NULL;
-       new->ce_flags = (new->ce_flags & ~clear) | set;
-       add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+       do_add_entry(o, new, set, clear);
 }
 
 /*
@@ -587,7 +594,7 @@ static int unpack_nondirectories(int n, unsigned long mask,
 
        for (i = 0; i < n; i++)
                if (src[i] && src[i] != o->df_conflict_entry)
-                       add_entry(o, src[i], 0, 0);
+                       do_add_entry(o, src[i], 0, 0);
        return 0;
 }
 
@@ -772,7 +779,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
        if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
                return -1;
 
-       if (src[0]) {
+       if (o->merge && src[0]) {
                if (ce_stage(src[0]))
                        mark_ce_used_same_name(src[0], o);
                else
@@ -1020,6 +1027,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->result.initialized = 1;
        o->result.timestamp.sec = o->src_index->timestamp.sec;
        o->result.timestamp.nsec = o->src_index->timestamp.nsec;
+       o->result.version = o->src_index->version;
        o->merge_size = len;
        mark_all_ce_unused(o->src_index);
 
@@ -1202,7 +1210,7 @@ static int verify_uptodate_1(struct cache_entry *ce,
                        return 0;
                /*
                 * NEEDSWORK: the current default policy is to allow
-                * submodule to be out of sync wrt the supermodule
+                * submodule to be out of sync wrt the superproject
                 * index.  This needs to be tightened later for
                 * submodules that are marked to be automatically
                 * checked out.
@@ -1785,7 +1793,7 @@ int bind_merge(struct cache_entry **src,
        struct cache_entry *a = src[1];
 
        if (o->merge_size != 1)
-               return error("Cannot do a bind merge of %d trees\n",
+               return error("Cannot do a bind merge of %d trees",
                             o->merge_size);
        if (a && old)
                return o->gently ? -1 :
diff --git a/varint.c b/varint.c
new file mode 100644 (file)
index 0000000..4ed7729
--- /dev/null
+++ b/varint.c
@@ -0,0 +1,29 @@
+#include "varint.h"
+
+uintmax_t decode_varint(const unsigned char **bufp)
+{
+       const unsigned char *buf = *bufp;
+       unsigned char c = *buf++;
+       uintmax_t val = c & 127;
+       while (c & 128) {
+               val += 1;
+               if (!val || MSB(val, 7))
+                       return 0; /* overflow */
+               c = *buf++;
+               val = (val << 7) + (c & 127);
+       }
+       *bufp = buf;
+       return val;
+}
+
+int encode_varint(uintmax_t value, unsigned char *buf)
+{
+       unsigned char varint[16];
+       unsigned pos = sizeof(varint) - 1;
+       varint[pos] = value & 127;
+       while (value >>= 7)
+               varint[--pos] = 128 | (--value & 127);
+       if (buf)
+               memcpy(buf, varint + pos, sizeof(varint) - pos);
+       return sizeof(varint) - pos;
+}
diff --git a/varint.h b/varint.h
new file mode 100644 (file)
index 0000000..0321195
--- /dev/null
+++ b/varint.h
@@ -0,0 +1,9 @@
+#ifndef VARINT_H
+#define VARINT_H
+
+#include "git-compat-util.h"
+
+extern int encode_varint(uintmax_t, unsigned char *);
+extern uintmax_t decode_varint(const unsigned char **);
+
+#endif /* VARINT_H */
index 644fdc71ba8c665f5dfadd552c3da63f4d8498e9..0899790a3331d82b72ba3e58fd7ff6ff83b38466 100644 (file)
@@ -175,7 +175,7 @@ static void read_props(void)
                int ch;
 
                if (!type || t[1] != ' ')
-                       die("invalid property line: %s\n", t);
+                       die("invalid property line: %s", t);
                len = atoi(&t[2]);
                strbuf_reset(&val);
                buffer_read_binary(&input, &val, len);
@@ -201,7 +201,7 @@ static void read_props(void)
                        strbuf_reset(&key);
                        continue;
                default:
-                       die("invalid property line: %s\n", t);
+                       die("invalid property line: %s", t);
                }
        }
 }
index 85f09df747637b94e0488ad65984c3f97c732034..6ccd0595f43d0ef62bd60a5863804f9a842a4235 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -9,6 +9,18 @@ static void do_nothing(size_t size)
 
 static void (*try_to_free_routine)(size_t size) = do_nothing;
 
+static void memory_limit_check(size_t size)
+{
+       static int limit = -1;
+       if (limit == -1) {
+               const char *env = getenv("GIT_ALLOC_LIMIT");
+               limit = env ? atoi(env) * 1024 : 0;
+       }
+       if (limit && size > limit)
+               die("attempting to allocate %"PRIuMAX" over limit %d",
+                   (intmax_t)size, limit);
+}
+
 try_to_free_t set_try_to_free_routine(try_to_free_t routine)
 {
        try_to_free_t old = try_to_free_routine;
@@ -32,7 +44,10 @@ char *xstrdup(const char *str)
 
 void *xmalloc(size_t size)
 {
-       void *ret = malloc(size);
+       void *ret;
+
+       memory_limit_check(size);
+       ret = malloc(size);
        if (!ret && !size)
                ret = malloc(1);
        if (!ret) {
@@ -79,7 +94,10 @@ char *xstrndup(const char *str, size_t len)
 
 void *xrealloc(void *ptr, size_t size)
 {
-       void *ret = realloc(ptr, size);
+       void *ret;
+
+       memory_limit_check(size);
+       ret = realloc(ptr, size);
        if (!ret && !size)
                ret = realloc(ptr, 1);
        if (!ret) {
@@ -95,7 +113,10 @@ void *xrealloc(void *ptr, size_t size)
 
 void *xcalloc(size_t nmemb, size_t size)
 {
-       void *ret = calloc(nmemb, size);
+       void *ret;
+
+       memory_limit_check(size * nmemb);
+       ret = calloc(nmemb, size);
        if (!ret && (!nmemb || !size))
                ret = calloc(1, 1);
        if (!ret) {
index 9ffc535f1ab0296fd0a3330b1f136d69f9ac9bb2..dd6d8c41068e6664cff1c07100e482e7ca9f0959 100644 (file)
@@ -11,6 +11,7 @@
 #include "remote.h"
 #include "refs.h"
 #include "submodule.h"
+#include "column.h"
 
 static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
@@ -641,6 +642,8 @@ static void wt_status_print_other(struct wt_status *s,
 {
        int i;
        struct strbuf buf = STRBUF_INIT;
+       static struct string_list output = STRING_LIST_INIT_DUP;
+       struct column_options copts;
 
        if (!l->nr)
                return;
@@ -649,12 +652,33 @@ static void wt_status_print_other(struct wt_status *s,
 
        for (i = 0; i < l->nr; i++) {
                struct string_list_item *it;
+               const char *path;
                it = &(l->items[i]);
+               path = quote_path(it->string, strlen(it->string),
+                                 &buf, s->prefix);
+               if (column_active(s->colopts)) {
+                       string_list_append(&output, path);
+                       continue;
+               }
                status_printf(s, color(WT_STATUS_HEADER, s), "\t");
                status_printf_more(s, color(WT_STATUS_UNTRACKED, s),
-                       "%s\n", quote_path(it->string, strlen(it->string),
-                                           &buf, s->prefix));
+                                  "%s\n", path);
        }
+
+       strbuf_release(&buf);
+       if (!column_active(s->colopts))
+               return;
+
+       strbuf_addf(&buf, "%s#\t%s",
+                   color(WT_STATUS_HEADER, s),
+                   color(WT_STATUS_UNTRACKED, s));
+       memset(&copts, 0, sizeof(copts));
+       copts.padding = 1;
+       copts.indent = buf.buf;
+       if (want_color(s->use_color))
+               copts.nl = GIT_COLOR_RESET "\n";
+       print_columns(&output, s->colopts, &copts);
+       string_list_clear(&output, 0);
        strbuf_release(&buf);
 }
 
@@ -777,7 +801,7 @@ void wt_status_print(struct wt_status *s)
        }
 }
 
-static void wt_shortstatus_unmerged(int null_termination, struct string_list_item *it,
+static void wt_shortstatus_unmerged(struct string_list_item *it,
                           struct wt_status *s)
 {
        struct wt_status_change_data *d = it->util;
@@ -793,7 +817,7 @@ static void wt_shortstatus_unmerged(int null_termination, struct string_list_ite
        case 7: how = "UU"; break; /* both modified */
        }
        color_fprintf(s->fp, color(WT_STATUS_UNMERGED, s), "%s", how);
-       if (null_termination) {
+       if (s->null_termination) {
                fprintf(stdout, " %s%c", it->string, 0);
        } else {
                struct strbuf onebuf = STRBUF_INIT;
@@ -804,7 +828,7 @@ static void wt_shortstatus_unmerged(int null_termination, struct string_list_ite
        }
 }
 
-static void wt_shortstatus_status(int null_termination, struct string_list_item *it,
+static void wt_shortstatus_status(struct string_list_item *it,
                         struct wt_status *s)
 {
        struct wt_status_change_data *d = it->util;
@@ -818,7 +842,7 @@ static void wt_shortstatus_status(int null_termination, struct string_list_item
        else
                putchar(' ');
        putchar(' ');
-       if (null_termination) {
+       if (s->null_termination) {
                fprintf(stdout, "%s%c", it->string, 0);
                if (d->head_path)
                        fprintf(stdout, "%s%c", d->head_path, 0);
@@ -846,10 +870,10 @@ static void wt_shortstatus_status(int null_termination, struct string_list_item
        }
 }
 
-static void wt_shortstatus_other(int null_termination, struct string_list_item *it,
+static void wt_shortstatus_other(struct string_list_item *it,
                                 struct wt_status *s, const char *sign)
 {
-       if (null_termination) {
+       if (s->null_termination) {
                fprintf(stdout, "%s %s%c", sign, it->string, 0);
        } else {
                struct strbuf onebuf = STRBUF_INIT;
@@ -889,8 +913,8 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
        if (s->is_initial)
                color_fprintf(s->fp, header_color, _("Initial commit on "));
        if (!stat_tracking_info(branch, &num_ours, &num_theirs)) {
-               color_fprintf_ln(s->fp, branch_color_local,
-                       "%s", branch_name);
+               color_fprintf(s->fp, branch_color_local, "%s", branch_name);
+               fputc(s->null_termination ? '\0' : '\n', s->fp);
                return;
        }
 
@@ -914,14 +938,15 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
                color_fprintf(s->fp, branch_color_remote, "%d", num_theirs);
        }
 
-       color_fprintf_ln(s->fp, header_color, "]");
+       color_fprintf(s->fp, header_color, "]");
+       fputc(s->null_termination ? '\0' : '\n', s->fp);
 }
 
-void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch)
+void wt_shortstatus_print(struct wt_status *s)
 {
        int i;
 
-       if (show_branch)
+       if (s->show_branch)
                wt_shortstatus_print_tracking(s);
 
        for (i = 0; i < s->change.nr; i++) {
@@ -931,28 +956,28 @@ void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_br
                it = &(s->change.items[i]);
                d = it->util;
                if (d->stagemask)
-                       wt_shortstatus_unmerged(null_termination, it, s);
+                       wt_shortstatus_unmerged(it, s);
                else
-                       wt_shortstatus_status(null_termination, it, s);
+                       wt_shortstatus_status(it, s);
        }
        for (i = 0; i < s->untracked.nr; i++) {
                struct string_list_item *it;
 
                it = &(s->untracked.items[i]);
-               wt_shortstatus_other(null_termination, it, s, "??");
+               wt_shortstatus_other(it, s, "??");
        }
        for (i = 0; i < s->ignored.nr; i++) {
                struct string_list_item *it;
 
                it = &(s->ignored.items[i]);
-               wt_shortstatus_other(null_termination, it, s, "!!");
+               wt_shortstatus_other(it, s, "!!");
        }
 }
 
-void wt_porcelain_print(struct wt_status *s, int null_termination)
+void wt_porcelain_print(struct wt_status *s)
 {
        s->use_color = 0;
        s->relative_paths = 0;
        s->prefix = NULL;
-       wt_shortstatus_print(s, null_termination, 0);
+       wt_shortstatus_print(s);
 }
index 682b4c8f7da2c58f741a958f6488a48fd7b483b4..14aa9f7e13342f1210ead3aca658a6ed60961649 100644 (file)
@@ -56,6 +56,9 @@ struct wt_status {
        enum untracked_status_type show_untracked_files;
        const char *ignore_submodule_arg;
        char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
+       unsigned colopts;
+       int null_termination;
+       int show_branch;
 
        /* These are computed during processing of the individual sections */
        int commitable;
@@ -72,8 +75,8 @@ void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
 
-void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch);
-void wt_porcelain_print(struct wt_status *s, int null_termination);
+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, ...)
        ;
index 0e2c169227ad29b5bf546c6c1b97e1a1d8ed7409..ecfa05f616f4b72d65bcb129c1ee2141cf3d1c47 100644 (file)
@@ -156,50 +156,6 @@ int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
        return ret;
 }
 
-struct xdiff_emit_hunk_state {
-       xdiff_emit_hunk_consume_fn consume;
-       void *consume_callback_data;
-};
-
-static int process_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
-                       xdemitconf_t const *xecfg)
-{
-       long s1, s2, same, p_next, t_next;
-       xdchange_t *xch, *xche;
-       struct xdiff_emit_hunk_state *state = ecb->priv;
-       xdiff_emit_hunk_consume_fn fn = state->consume;
-       void *consume_callback_data = state->consume_callback_data;
-
-       for (xch = xscr; xch; xch = xche->next) {
-               xche = xdl_get_hunk(xch, xecfg);
-
-               s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
-               s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
-               same = s2 + XDL_MAX(xch->i1 - s1, 0);
-               p_next = xche->i1 + xche->chg1;
-               t_next = xche->i2 + xche->chg2;
-
-               fn(consume_callback_data, same, p_next, t_next);
-       }
-       return 0;
-}
-
-int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
-                  xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
-                  xpparam_t const *xpp, xdemitconf_t *xecfg)
-{
-       struct xdiff_emit_hunk_state state;
-       xdemitcb_t ecb;
-
-       memset(&state, 0, sizeof(state));
-       memset(&ecb, 0, sizeof(ecb));
-       state.consume = fn;
-       state.consume_callback_data = consume_callback_data;
-       xecfg->emit_func = (void (*)())process_diff;
-       ecb.priv = &state;
-       return xdi_diff(mf1, mf2, xpp, xecfg, &ecb);
-}
-
 int read_mmfile(mmfile_t *ptr, const char *filename)
 {
        struct stat st;
index 49d1116fc34f536ab9358313522a25564dd1f6c3..eff7762ee1a1bb0ea648c60a07389e22e9a1ac07 100644 (file)
@@ -4,15 +4,11 @@
 #include "xdiff/xdiff.h"
 
 typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
-typedef void (*xdiff_emit_hunk_consume_fn)(void *, long, long, long);
 
 int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
 int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
                  xdiff_emit_consume_fn fn, void *consume_callback_data,
                  xpparam_t const *xpp, xdemitconf_t const *xecfg);
-int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
-                  xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
-                  xpparam_t const *xpp, xdemitconf_t *xecfg);
 int parse_hunk_header(char *line, int len,
                      int *ob, int *on,
                      int *nb, int *nn);
index 00d36c3ac7a7642831d2ffc49647caf77a4d066c..219a3bbca613192d8b127659e4bfc0d60f38cf10 100644 (file)
@@ -32,14 +32,12 @@ extern "C" {
 #define XDF_IGNORE_WHITESPACE (1 << 2)
 #define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
 #define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
-#define XDF_PATIENCE_DIFF (1 << 5)
-#define XDF_HISTOGRAM_DIFF (1 << 6)
 #define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
 
-#define XDL_PATCH_NORMAL '-'
-#define XDL_PATCH_REVERSE '+'
-#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
-#define XDL_PATCH_IGNOREBSPACE (1 << 8)
+#define XDF_PATIENCE_DIFF (1 << 5)
+#define XDF_HISTOGRAM_DIFF (1 << 6)
+#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
+#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
 
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_COMMON (1 << 1)
@@ -88,13 +86,17 @@ typedef struct s_xdemitcb {
 
 typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
 
+typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a,
+                                           long start_b, long count_b,
+                                           void *cb_data);
+
 typedef struct s_xdemitconf {
        long ctxlen;
        long interhunkctxlen;
        unsigned long flags;
        find_func_t find_func;
        void *find_func_priv;
-       void (*emit_func)();
+       xdl_emit_hunk_consume_func_t hunk_func;
 } xdemitconf_t;
 
 typedef struct s_bdiffparam {
index 75a39227501715504cdd12ccc1b4854568a54ad7..1b7012a119ad3ea7e2ebe0aad80891529d4936da 100644 (file)
@@ -328,10 +328,10 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
        xdalgoenv_t xenv;
        diffdata_t dd1, dd2;
 
-       if (xpp->flags & XDF_PATIENCE_DIFF)
+       if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF)
                return xdl_do_patience_diff(mf1, mf2, xpp, xe);
 
-       if (xpp->flags & XDF_HISTOGRAM_DIFF)
+       if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
                return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
 
        if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
@@ -538,13 +538,26 @@ void xdl_free_script(xdchange_t *xscr) {
        }
 }
 
+static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+                             xdemitconf_t const *xecfg)
+{
+       xdchange_t *xch, *xche;
+
+       for (xch = xscr; xch; xch = xche->next) {
+               xche = xdl_get_hunk(xch, xecfg);
+               if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
+                                    xch->i2, xche->i2 + xche->chg2 - xch->i2,
+                                    ecb->priv) < 0)
+                       return -1;
+       }
+       return 0;
+}
 
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
             xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
        xdchange_t *xscr;
        xdfenv_t xe;
-       emit_func_t ef = xecfg->emit_func ?
-               (emit_func_t)xecfg->emit_func : xdl_emit_diff;
+       emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff;
 
        if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
 
index 18f6f997c321b5ac1f4d4211a4d448dc8542c22f..bf99787c3e4c791426311495dda9d4da81cbb571 100644 (file)
@@ -252,7 +252,7 @@ static int fall_back_to_classic_diff(struct histindex *index,
                int line1, int count1, int line2, int count2)
 {
        xpparam_t xpp;
-       xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
+       xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
 
        return xdl_fall_back_diff(index->env, &xpp,
                                  line1, count1, line2, count2);
index fdd7d0263f576a8dc1a8e791ef50f8dbe25c7ee5..04e1a1ab2a863814df3b9a91d4e854704d47f3f5 100644 (file)
@@ -288,7 +288,7 @@ static int fall_back_to_classic_diff(struct hashmap *map,
                int line1, int count1, int line2, int count2)
 {
        xpparam_t xpp;
-       xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
+       xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
 
        return xdl_fall_back_diff(map->env, &xpp,
                                  line1, count1, line2, count2);
index e419f4f726019a5b0365c589285439fb3bfb8db2..63a22c630e521969b08c8ecb1ce9fa3e0f3ff513 100644 (file)
@@ -181,7 +181,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
        if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
                goto abort;
 
-       if (xpp->flags & XDF_HISTOGRAM_DIFF)
+       if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
                hbits = hsize = 0;
        else {
                hbits = xdl_hashbits((unsigned int) narec);
@@ -209,8 +209,8 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
                        crec->ha = hav;
                        recs[nrec++] = crec;
 
-                       if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
-                               xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+                       if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+                           xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
                                goto abort;
                }
        }
@@ -273,16 +273,15 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
         * (nrecs) will be updated correctly anyway by
         * xdl_prepare_ctx().
         */
-       sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+       sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
+                 ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
 
        enl1 = xdl_guess_lines(mf1, sample) + 1;
        enl2 = xdl_guess_lines(mf2, sample) + 1;
 
-       if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
-               xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
-
+       if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF &&
+           xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
                return -1;
-       }
 
        if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
 
@@ -296,9 +295,9 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                return -1;
        }
 
-       if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
-                       !(xpp->flags & XDF_HISTOGRAM_DIFF) &&
-                       xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
+       if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
+           (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+           xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
 
                xdl_free_ctx(&xe->xdf2);
                xdl_free_ctx(&xe->xdf1);
index 0de084e53f5144153373cc66cae7b523b4ae2812..ae6ce0d95c9dee16975e43b37ed0b2b9e3ef61ea 100644 (file)
@@ -20,6 +20,8 @@
  *
  */
 
+#include <limits.h>
+#include <assert.h>
 #include "xinclude.h"
 
 
@@ -120,35 +122,6 @@ void *xdl_cha_alloc(chastore_t *cha) {
        return data;
 }
 
-
-void *xdl_cha_first(chastore_t *cha) {
-       chanode_t *sncur;
-
-       if (!(cha->sncur = sncur = cha->head))
-               return NULL;
-
-       cha->scurr = 0;
-
-       return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
-void *xdl_cha_next(chastore_t *cha) {
-       chanode_t *sncur;
-
-       if (!(sncur = cha->sncur))
-               return NULL;
-       cha->scurr += cha->isize;
-       if (cha->scurr == sncur->icurr) {
-               if (!(sncur = cha->sncur = sncur->next))
-                       return NULL;
-               cha->scurr = 0;
-       }
-
-       return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
 long xdl_guess_lines(mmfile_t *mf, long sample) {
        long nl = 0, size, tsize = 0;
        char const *data, *cur, *top;
@@ -276,6 +249,109 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
        return ha;
 }
 
+#ifdef XDL_FAST_HASH
+
+#define ONEBYTES       0x0101010101010101ul
+#define NEWLINEBYTES   0x0a0a0a0a0a0a0a0aul
+#define HIGHBITS       0x8080808080808080ul
+
+/* Return the high bit set in the first byte that is a zero */
+static inline unsigned long has_zero(unsigned long a)
+{
+       return ((a - ONEBYTES) & ~a) & HIGHBITS;
+}
+
+static inline long count_masked_bytes(unsigned long mask)
+{
+       if (sizeof(long) == 8) {
+               /*
+                * Jan Achrenius on G+: microoptimized version of
+                * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56"
+                * that works for the bytemasks without having to
+                * mask them first.
+                */
+               return mask * 0x0001020304050608 >> 56;
+       } else {
+               /*
+                * Modified Carl Chatfield G+ version for 32-bit *
+                *
+                * (a) gives us
+                *   -1 (0, ff), 0 (ffff) or 1 (ffffff)
+                * (b) gives us
+                *   0 for 0, 1 for (ff ffff ffffff)
+                * (a+b+1) gives us
+                *   correct 0-3 bytemask count result
+                */
+               long a = (mask - 256) >> 23;
+               long b = mask & 1;
+               return a + b + 1;
+       }
+}
+
+unsigned long xdl_hash_record(char const **data, char const *top, long flags)
+{
+       unsigned long hash = 5381;
+       unsigned long a = 0, mask = 0;
+       char const *ptr = *data;
+       char const *end = top - sizeof(unsigned long) + 1;
+
+       if (flags & XDF_WHITESPACE_FLAGS)
+               return xdl_hash_record_with_whitespace(data, top, flags);
+
+       ptr -= sizeof(unsigned long);
+       do {
+               hash += hash << 5;
+               hash ^= a;
+               ptr += sizeof(unsigned long);
+               if (ptr >= end)
+                       break;
+               a = *(unsigned long *)ptr;
+               /* Do we have any '\n' bytes in this word? */
+               mask = has_zero(a ^ NEWLINEBYTES);
+       } while (!mask);
+
+       if (ptr >= end) {
+               /*
+                * There is only a partial word left at the end of the
+                * buffer. Because we may work with a memory mapping,
+                * we have to grab the rest byte by byte instead of
+                * blindly reading it.
+                *
+                * To avoid problems with masking in a signed value,
+                * we use an unsigned char here.
+                */
+               const char *p;
+               for (p = top - 1; p >= ptr; p--)
+                       a = (a << 8) + *((const unsigned char *)p);
+               mask = has_zero(a ^ NEWLINEBYTES);
+               if (!mask)
+                       /*
+                        * No '\n' found in the partial word.  Make a
+                        * mask that matches what we read.
+                        */
+                       mask = 1UL << (8 * (top - ptr) + 7);
+       }
+
+       /* The mask *below* the first high bit set */
+       mask = (mask - 1) & ~mask;
+       mask >>= 7;
+       hash += hash << 5;
+       hash ^= a & mask;
+
+       /* Advance past the last (possibly partial) word */
+       ptr += count_masked_bytes(mask);
+
+       if (ptr < top) {
+               assert(*ptr == '\n');
+               ptr++;
+       }
+
+       *data = ptr;
+
+       return hash;
+}
+
+#else /* XDL_FAST_HASH */
 
 unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        unsigned long ha = 5381;
@@ -293,6 +369,7 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        return ha;
 }
 
+#endif /* XDL_FAST_HASH */
 
 unsigned int xdl_hashbits(unsigned int size) {
        unsigned int val = 1, bits = 0;
@@ -324,20 +401,6 @@ int xdl_num_out(char *out, long val) {
        return str - out;
 }
 
-
-long xdl_atol(char const *str, char const **next) {
-       long val, base;
-       char const *top;
-
-       for (top = str; XDL_ISDIGIT(*top); top++);
-       if (next)
-               *next = top;
-       for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
-               val += base * (long)(*top - '0');
-       return val;
-}
-
-
 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
                      const char *func, long funclen, xdemitcb_t *ecb) {
        int nb = 0;
index 714719a89cba9170820bf7d54b9c569d42861aa4..ad1428ed699383bf6f736b28033aeb9dbe114b76 100644 (file)
@@ -31,14 +31,11 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
 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);
-void *xdl_cha_first(chastore_t *cha);
-void *xdl_cha_next(chastore_t *cha);
 long xdl_guess_lines(mmfile_t *mf, long sample);
 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);
 int xdl_num_out(char *out, long val);
-long xdl_atol(char const *str, char const **next);
 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
                      const char *func, long funclen, xdemitcb_t *ecb);
 int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,