Merge branch 'js/compat-itimer'
authorJunio C Hamano <gitster@pobox.com>
Wed, 12 Sep 2012 21:21:44 +0000 (14:21 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 12 Sep 2012 21:21:44 +0000 (14:21 -0700)
Pieces to support compilation on __TANDEM.

* js/compat-itimer:
Add a no-op setitimer() wrapper

251 files changed:
.gitignore
.mailmap
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/RelNotes/1.7.11.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.12.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.0.txt [new file with mode: 0644]
Documentation/asciidoc.conf
Documentation/config.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-config.txt
Documentation/git-credential-cache--daemon.txt
Documentation/git-credential-cache.txt
Documentation/git-credential-store.txt
Documentation/git-daemon.txt
Documentation/git-describe.txt
Documentation/git-difftool.txt
Documentation/git-filter-branch.txt
Documentation/git-fsck.txt
Documentation/git-grep.txt
Documentation/git-lost-found.txt
Documentation/git-merge-base.txt
Documentation/git-mergetool.txt
Documentation/git-pack-refs.txt
Documentation/git-pull.txt
Documentation/git-replace.txt
Documentation/git-submodule.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitcli.txt
Documentation/rev-list-options.txt
Documentation/revisions.txt
Documentation/technical/api-argv-array.txt
Documentation/user-manual.conf
GIT-VERSION-GEN
Makefile
RelNotes
archive.c
argv-array.c
argv-array.h
attr.c
builtin.h
builtin/add.c
builtin/apply.c
builtin/archive.c
builtin/bisect--helper.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/check-attr.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/column.c
builtin/commit.c
builtin/config.c
builtin/count-objects.c
builtin/describe.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/hash-object.c
builtin/help.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/ls-files.c
builtin/ls-tree.c
builtin/mailinfo.c
builtin/merge-base.c
builtin/merge-file.c
builtin/merge.c
builtin/mktree.c
builtin/mv.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/pack-refs.c
builtin/prune-packed.c
builtin/prune.c
builtin/push.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/remote.c
builtin/replace.c
builtin/rerere.c
builtin/reset.c
builtin/rev-parse.c
builtin/revert.c
builtin/rm.c
builtin/send-pack.c
builtin/shortlog.c
builtin/show-branch.c
builtin/show-ref.c
builtin/symbolic-ref.c
builtin/tag.c
builtin/update-index.c
builtin/update-ref.c
builtin/update-server-info.c
builtin/verify-pack.c
builtin/verify-tag.c
builtin/write-tree.c
cache.h
combine-diff.c
command-list.txt
commit.c
commit.h
compat/mkdir.c [new file with mode: 0644]
compat/precompose_utf8.c
config.c
connect.c
contrib/ciabot/INSTALL [new file with mode: 0644]
contrib/ciabot/README
contrib/ciabot/ciabot.py
contrib/ciabot/ciabot.sh
contrib/completion/git-completion.bash
contrib/credential/gnome-keyring/.gitignore [new file with mode: 0644]
contrib/credential/gnome-keyring/Makefile [new file with mode: 0644]
contrib/credential/gnome-keyring/git-credential-gnome-keyring.c [new file with mode: 0644]
contrib/credential/wincred/Makefile [new file with mode: 0644]
contrib/credential/wincred/git-credential-wincred.c [new file with mode: 0644]
contrib/examples/builtin-fetch--tool.c
daemon.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-rename.c
diffcore.h
dir.c
fast-import.c
fsck.c
gettext.h
git-am.sh
git-compat-util.h
git-difftool.perl
git-mergetool--lib.sh
git-mergetool.sh
git-p4.py
git-rebase--interactive.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
gitk-git/gitk
gitweb/gitweb.perl
grep.h
http-push.c
http.c
http.h
ident.c
merge-recursive.c
mergetools/codecompare [new file with mode: 0644]
notes-merge.c
parse-options.h
patch-ids.c
perl/Git/SVN.pm
perl/Git/SVN/Fetcher.pm
perl/Git/SVN/Migration.pm
perl/Git/SVN/Ra.pm
perl/Git/SVN/Utils.pm
read-cache.c
remote-curl.c
revision.c
revision.h
run-command.c
sequencer.c
sequencer.h
setup.c
sha1_file.c
submodule.c
submodule.h
t/Git-SVN/Utils/add_path_to_url.t [new file with mode: 0644]
t/Git-SVN/Utils/canonicalize_url.t [new file with mode: 0644]
t/Git-SVN/Utils/collapse_dotdot.t [new file with mode: 0644]
t/Git-SVN/Utils/join_paths.t [new file with mode: 0644]
t/README
t/lib-credential.sh
t/lib-git-p4.sh
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/perf/perf-lib.sh
t/t0003-attributes.sh
t/t0006-date.sh
t/t0040-parse-options.sh
t/t0050-filesystem.sh
t/t0070-fundamental.sh
t/t1300-repo-config.sh
t/t1450-fsck.sh
t/t1502-rev-parse-parseopt.sh
t/t1506-rev-parse-diagnosis.sh
t/t2006-checkout-index-basic.sh
t/t2107-update-index-basic.sh
t/t3004-ls-files-basic.sh
t/t3200-branch.sh
t/t3401-rebase-partial.sh
t/t3501-revert-cherry-pick.sh
t/t3505-cherry-pick-empty.sh
t/t3508-cherry-pick-many-commits.sh
t/t3903-stash.sh
t/t3910-mac-os-precompose.sh
t/t4006-diff-mode.sh
t/t4012-diff-binary.sh
t/t4022-diff-rewrite.sh
t/t4054-diff-bogus-tree.sh [new file with mode: 0755]
t/t4103-apply-binary.sh
t/t4120-apply-popt.sh
t/t4133-apply-filenames.sh
t/t4200-rerere.sh
t/t4202-log.sh
t/t4205-log-pretty-formats.sh
t/t5300-pack-object.sh
t/t5400-send-pack.sh
t/t5505-remote.sh
t/t5530-upload-pack-error.sh
t/t5540-http-push.sh
t/t5541-http-push.sh
t/t5550-http-fetch.sh
t/t5551-http-fetch.sh
t/t6300-for-each-ref.sh
t/t6500-gc.sh
t/t7007-show.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7508-status.sh
t/t7600-merge.sh
t/t7610-mergetool.sh
t/t7810-grep.sh
t/t9001-send-email.sh
t/t9107-git-svn-migrate.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9801-git-p4-branch.sh
t/t9809-git-p4-client-view.sh
t/test-lib-functions.sh
t/test-lib.sh
test-regex.c [new file with mode: 0644]
tree-diff.c
upload-pack.c
version.c
version.h
wrapper.c
index bb5c91e712349686ccd008d9ccfd5fa8c2320834..68fe464090606b95b40b8b99e2454edb52862abe 100644 (file)
 /test-mktemp
 /test-parse-options
 /test-path-utils
+/test-regex
 /test-revision-walking
 /test-run-command
 /test-sha1
index 6303782106cc733dd06ad6e85c866610aa020c65..bcf4f8770f0751a61cf1c1b89a0d66cff559c54e 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -18,6 +18,7 @@ David Kågedal <davidk@lysator.liu.se>
 David S. Miller <davem@davemloft.net>
 Deskin Miller <deskinm@umich.edu>
 Dirk Süsserott <newsletter@dirk.my1.cc>
+Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com>
 Fredrik Kuivinen <freku045@student.liu.se>
 H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
 H. Peter Anvin <hpa@tazenda.sc.orionmulti.com>
@@ -26,6 +27,9 @@ Horst H. von Brand <vonbrand@inf.utfsm.cl>
 İsmail Dönmez <ismail@pardus.org.tr>
 Jay Soffian <jaysoffian+git@gmail.com>
 Joachim Berdal Haga <cjhaga@fys.uio.no>
+Johannes Sixt <j6t@kdbg.org> <johannes.sixt@telecom.at>
+Johannes Sixt <j6t@kdbg.org> <j.sixt@viscovery.net>
+Johannes Sixt <j6t@kdbg.org> <J.Sixt@eudaptics.com>
 Jon Loeliger <jdl@freescale.com>
 Jon Seymour <jon@blackcubes.dyndns.org>
 Jonathan Nieder <jrnieder@uchicago.edu>
@@ -43,6 +47,7 @@ Lars Doelle <lars.doelle@on-line.de>
 Li Hong <leehong@pku.edu.cn>
 Lukas Sandström <lukass@etek.chalmers.se>
 Martin Langhoff <martin@laptop.org>
+Martin von Zweigbergk <martinvonz@gmail.com> <martin.von.zweigbergk@gmail.com>
 Michael Coleman <tutufan@gmail.com>
 Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
 Michael W. Olson <mwolson@gnu.org>
@@ -51,7 +56,10 @@ Nanako Shiraishi <nanako3@bluebottle.com>
 Nanako Shiraishi <nanako3@lavabit.com>
 Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
 <nico@fluxnic.net> <nico@cam.org>
+Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se>
+Peter Krefting <peter@softwolves.pp.se> <peter@softwolves.pp.se>
 Philippe Bruhat <book@cpan.org>
+Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>
 Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
 René Scharfe <rene.scharfe@lsrfire.ath.cx>
 Robert Fitzsimons <robfitz@273k.net>
@@ -61,6 +69,7 @@ Sean Estabrooks <seanlkml@sympatico.ca>
 Shawn O. Pearce <spearce@spearce.org>
 Steven Grimm <koreth@midwinter.com>
 Theodore Ts'o <tytso@mit.edu>
+Thomas Rast <trast@inf.ethz.ch> <trast@student.ethz.ch>
 Tony Luck <tony.luck@intel.com>
 Uwe Kleine-König <Uwe_Zeisberger@digi.com>
 Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com>
index 45577117c2a02dd4a4f9e63e78139b3df665b8f2..57da6aadeb8b4d65037035afaa269f391e989c38 100644 (file)
@@ -76,11 +76,19 @@ For shell scripts specifically (not exhaustive):
 
  - We do not use Process Substitution <(list) or >(list).
 
+ - Do not write control structures on a single line with semicolon.
+   "then" should be on the next line for if statements, and "do"
+   should be on the next line for "while" and "for".
+
  - We prefer "test" over "[ ... ]".
 
  - We do not write the noiseword "function" in front of shell
    functions.
 
+ - We prefer a space between the function name and the parentheses. The
+   opening "{" should also be on the same line.
+   E.g.: my_function () {
+
  - As to use of grep, stick to a subset of BRE (namely, no \{m,n\},
    [::], [==], nor [..]) for portability.
 
index 063fa696c9eff617e4fc6ad37f0b6e49d32d58f9..cf5916fe8b79e39eb57f97b36e82697694f41d7a 100644 (file)
@@ -344,4 +344,7 @@ require-htmlrepo::
 quick-install-html: require-htmlrepo
        '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REPO) $(DESTDIR)$(htmldir)
 
+print-man1:
+       @for i in $(MAN1_TXT); do echo $$i; done
+
 .PHONY: FORCE
diff --git a/Documentation/RelNotes/1.7.11.6.txt b/Documentation/RelNotes/1.7.11.6.txt
new file mode 100644 (file)
index 0000000..ba7d3c3
--- /dev/null
@@ -0,0 +1,84 @@
+Git v1.7.11.6 Release Notes
+===========================
+
+Fixes since v1.7.11.5
+---------------------
+
+ * "ciabot" script (in contrib/) has been updated with extensive
+   documentation.
+
+ * "git foo" errored out with "Not a directory" when the user had a
+   non-directory on $PATH, and worse yet it masked an alias "foo" from
+   running.
+
+ * When the user exports a non-default IFS without HT, scripts that
+   rely on being able to parse "ls-files -s | while read a b c..."
+   started to fail.  Protect them from such a misconfiguration.
+
+ * When the user gives an argument that can be taken as both a
+   revision name and a pathname without disambiguating with "--", we
+   used to give a help message "Use '--' to separate".  The message
+   has been clarified to show where that '--' goes on the command
+   line.
+
+ * Documentation for the configuration file format had a confusing
+   example.
+
+ * Older parts of the documentation described as if having a regular
+   file in .git/refs/ hierarchy were the only way to have branches and
+   tags, which is not true for quite some time.
+
+ * It was generally understood that "--long-option"s to many of our
+   subcommands can be abbreviated to the unique prefix, but it was not
+   easy to find it described for new readers of the documentation set.
+
+ * The "--topo-order", "--date-order" (and the lack of either means
+   the default order) options to "rev-list" and "log" family of
+   commands were poorly described in the documentation.
+
+ * "git commit --amend" let the user edit the log message and then
+   died when the human-readable committer name was given
+   insufficiently by getpwent(3).
+
+ * The exit status code from "git config" was way overspecified while
+   being incorrect.  The implementation has been updated to give the
+   documented status for a case that was documented, and introduce a
+   new code for "all other errors".
+
+ * The output from "git diff -B" for a file that ends with an
+   incomplete line did not put "\ No newline..." on a line of its own.
+
+ * "git diff" had a confusion between taking data from a path in the
+   working tree and taking data from an object that happens to have
+   name 0{40} recorded in a tree.
+
+ * The "--rebase" option to "git pull" can be abbreviated to "-r",
+   but we didn't document it.
+
+ * When "git push" triggered the automatic gc on the receiving end, a
+   message from "git prune" that said it was removing cruft leaked to
+   the standard output, breaking the communication protocol.
+
+ * The reflog entries left by "git rebase" and "git rebase -i" were
+   inconsistent (the interactive one gave an abbreviated object name).
+
+ * "git send-email" did not unquote encoded words that appear on the
+   header correctly, and lost "_" from strings.
+
+ * "git stash apply/pop" did not trigger "rerere" upon conflicts
+   unlike other mergy operations.
+
+ * "git submodule <cmd> path" did not error out when the path to the
+   submodule was misspelt.
+
+ * "git submodule update -f" did not update paths in the working tree
+   that has local changes.
+   (merge 01d4721 sz/submodule-force-update later to maint).
+
+ * "gitweb" when used with PATH_INFO failed to notice directories with
+   SP (and other characters that need URL-style quoting) in them.
+
+ * Fallback 'getpass' implementation made unportable use of stdio API.
+
+ * A utility shell function test_seq has been added as a replacement
+   for the 'seq' utility found on some platforms.
diff --git a/Documentation/RelNotes/1.7.12.1.txt b/Documentation/RelNotes/1.7.12.1.txt
new file mode 100644 (file)
index 0000000..da51dd5
--- /dev/null
@@ -0,0 +1,75 @@
+Git 1.7.12.1 Release Notes
+==========================
+
+Fixes since v1.7.12
+-------------------
+
+ * "ciabot" script (in contrib/) has been updated with extensive
+   documentation.
+
+ * The "--rebase" option to "git pull" can be abbreviated to "-r",
+   but we didn't document it.
+
+ * It was generally understood that "--long-option"s to many of our
+   subcommands can be abbreviated to the unique prefix, but it was not
+   easy to find it described for new readers of the documentation set.
+
+ * The "--topo-order", "--date-order" (and the lack of either means
+   the default order) options to "rev-list" and "log" family of
+   commands were poorly described in the documentation.
+
+ * Older parts of the documentation described as if having a regular
+   file in .git/refs/ hierarchy were the only way to have branches and
+   tags, which is not true for quite some time.
+
+ * A utility shell function test_seq has been added as a replacement
+   for the 'seq' utility found on some platforms.
+
+ * Compatibility wrapper to learn the maximum number of file
+   descriptors we can open around sysconf(_SC_OPEN_MAX) and
+   getrlimit(RLIMIT_NO_FILE) has been introduced for portability.
+
+ * We used curl_easy_strerror() without checking version of cURL,
+   breaking the build for versions before curl 7.12.0.
+
+ * Code to work around MacOS X UTF-8 gotcha has been cleaned up.
+
+ * Fallback 'getpass' implementation made unportable use of stdio API.
+
+ * "git commit --amend" let the user edit the log message and then
+   died when the human-readable committer name was given
+   insufficiently by getpwent(3).
+
+ * Some capabilities were asked by fetch-pack even when upload-pack
+   did not advertise that they are available.  fetch-pack has been
+   fixed not to do so.
+
+ * "git prune" without "-v" used to warn about leftover temporary
+   files (which is an indication of an earlier aborted operation).
+
+ * The reflog entries left by "git rebase" and "git rebase -i" were
+   inconsistent (the interactive one gave an abbreviated object name).
+
+ * When the user exports a non-default IFS without HT, scripts that
+   rely on being able to parse "ls-files -s | while read a b c..."
+   started to fail.  Protect them from such a misconfiguration.
+
+ * When "git push" triggered the automatic gc on the receiving end, a
+   message from "git prune" that said it was removing cruft leaked to
+   the standard output, breaking the communication protocol.
+
+ * "git diff" had a confusion between taking data from a path in the
+   working tree and taking data from an object that happens to have
+   name 0{40} recorded in a tree.
+
+ * "git send-email" did not unquote encoded words that appear on the
+   header correctly, and lost "_" from strings.
+
+ * When the user gives an argument that can be taken as both a
+   revision name and a pathname without disambiguating with "--", we
+   used to give a help message "Use '--' to separate".  The message
+   has been clarified to show where that '--' goes on the command
+   line.
+
+ * "gitweb" when used with PATH_INFO failed to notice directories with
+   SP (and other characters that need URL-style quoting) in them.
diff --git a/Documentation/RelNotes/1.8.0.txt b/Documentation/RelNotes/1.8.0.txt
new file mode 100644 (file)
index 0000000..689c387
--- /dev/null
@@ -0,0 +1,165 @@
+Git v1.8.0 Release Notes
+========================
+
+Backward compatibility notes
+----------------------------
+
+In the next major release, we will change the behaviour of the "git
+push" command.  When "git push [$there]" does not say what to push, we
+have used the traditional "matching" semantics (all your branches were
+sent to the remote as long as there already are branches of the same
+name over there).  We will use the "simple" semantics, that pushes the
+current branch to the branch with the same name only when the current
+branch is set to integrate with that remote branch.  There is a user
+preference configuration variable "push.default" to change this, and
+"git push" will warn about the upcoming change until you set this
+variable.
+
+"git branch --set-upstream" is deprecated and may be removed in a
+relatively distant future.  "git branch [-u|--set-upstream-to]" has
+been introduced with a saner order of arguments.
+
+
+Updates since v1.7.12
+---------------------
+
+UI, Workflows & Features
+
+ * A credential helper for Win32 to allow access to the keychain of
+   the logged-in user has been added.
+
+ * A credential helper to allow access to the Gnome keyring has been
+   added.
+
+ * It was tempting to say "git branch --set-upstream origin/master",
+   but that tells Git to arrange the local branch "origin/master" to
+   integrate with the currently checked out branch, which is highly
+   unlikely what the user meant.  The option is deprecated; use the
+   new "--set-upstream-to" (with a short-and-sweet "-u") option
+   instead.
+
+ * "git cherry-pick" learned the "--allow-empty-message" option to
+   allow it to replay a commit without any log message.
+
+ * "git daemon" learned the "--access-hook" option to allow an
+   external command to decline service based on the client address,
+   repository path, etc.
+
+ * "git difftool --dir-diff" learned to use symbolic links to prepare
+   temporary copy of the working tree when available.
+
+ * "git grep" learned to use a non-standard pattern type by default if
+   a configuration variable tells it to.
+
+ * "git merge-base" learned "--is-ancestor A B" option to tell if A is
+   an ancestor of B.  The result is indicated by its exit status code.
+
+
+Foreign Interface
+
+ * "git svn" has been updated to work with SVN 1.7.
+
+Performance, Internal Implementation, etc. (please report possible regressions)
+
+ * Git ships with a fall-back regexp implementation for platforms with
+   buggy regexp library, but it was easy for people to keep using their
+   platform regexp.  A new test has been added to check this.
+
+ * The "check-docs" build target has been updated and greatly
+   simplified.
+
+ * The documentation in the TeXinfo format was using indented output
+   for materials meant to be examples that are better typeset in
+   monospace.
+
+ * Compatibility wrapper around some mkdir(2) implementations that
+   reject parameter with trailing slash has been introduced.
+
+ * Many internal uses of "git merge-base" equivalent were only to see
+   if one commit fast-forwards to the other, which did not need the
+   full set of merge bases to be computed. They have been updated to
+   use less expensive checks.
+
+ * The heuristics to detect and silently convert latin1 to utf8 when
+   we were told to use utf-8 in the log message has been transplanted
+   from "mailinfo" to "commit" and "commit-tree".
+
+ * Messages given by "git <subcommand> -h" from many subcommands have
+   been marked for translation.
+
+Also contains minor documentation updates and code clean-ups.
+
+
+Fixes since v1.7.12
+-------------------
+
+Unless otherwise noted, all the fixes since v1.7.12 in the
+maintenance track are contained in this release (see release notes
+to them for details).
+
+ * "git show --format='%ci'" did not give timestamp correctly for
+   commits created without human readable name on "committer" line.
+   (merge e27ddb6 jc/maint-ident-missing-human-name later to maint).
+
+ * "git cherry-pick A C B" used to replay changes in A and then B and
+   then C if these three commits had committer timestamps in that
+   order, which is not what the user who said "A C B" naturally
+   expects.
+   (merge a73e22e mz/cherry-pick-cmdline-order later to maint).
+
+ * "git show --quiet" ought to be a synonym for "git show -s", but
+   wasn't.
+   (merge f9c75d8 jk/maint-quiet-is-synonym-to-s-in-log later to maint).
+
+ * "git p4", when "--use-client-spec" and "--detect-branches" are used
+   together, misdetected branches.
+   (merge 21ef5df pw/p4-use-client-spec-branch-detection later to maint).
+
+ * Output from "git branch -v" contains "(no branch)" that could be
+   localized, but the code to align it along with the names of
+   branches were counting in bytes, not in display columns.
+   (merge 1452bd6 nd/branch-v-alignment later to maint).
+
+ * "git for-each-ref" did not currectly support more than one --sort
+   option.
+   (merge 3b51222 kk/maint-for-each-ref-multi-sort later to maint).
+
+ * Pushing to smart HTTP server with recent Git fails without having
+   the username in the URL to force authentication, if the server is
+   configured to allow GET anonymously, while requiring authentication
+   for POST.
+   (merge b81401c jk/maint-http-half-auth-push later to maint).
+
+ * When looking for $HOME/.gitconfig etc., it is OK if we cannot read
+   them because they do not exist, but we did not diagnose existing
+   files that we cannot read.
+
+ * The synopsis said "checkout [-B branch]" to make it clear the
+   branch name is a parameter to the option, but the heading for the
+   option description was "-B::", not "-B branch::", making the
+   documentation misleading.  There may be room in documentation pages
+   of other commands for similar improvements (hint, hint).
+   (merge 45aaf03 jc/maint-doc-checkout-b-always-takes-branch-name later to maint).
+
+ * "git log .." errored out saying it is both rev range and a path
+   when there is no disambiguating "--" is on the command line.
+   Update the command line parser to interpret ".." as a path in such
+   a case.
+   (merge 003c84f jc/dotdot-is-parent-directory later to maint).
+
+ * "git apply -p0" did not parse pathnames on "diff --git" line
+   correctly.  This caused patches that had pathnames in no other
+   places to be mistakenly rejected (most notably, binary patch that
+   does not rename nor change mode).  Textual patches, renames or mode
+   changes have preimage and postimage pathnames in different places
+   in a form that can be parsed unambiguously and did not suffer from
+   this problem.
+   (merge 6a2abdc jc/apply-binary-p0 later to maint).
+
+ * The interactive prompt "git send-email" gives was error prone. It
+   asked "What e-mail address do you want to use?" with the address it
+   guessed (correctly) the user would want to use in its prompt,
+   tempting the user to say "y". But the response was taken as "No,
+   please use 'y' as the e-mail address instead", which is most
+   certainly not what the user meant.
+   (merge 51bbccf jc/send-email-reconfirm later to maint).
index a26d245ab46551b75ed3360a1d9fe33992a9cbd0..1273a85c8a45c225a24574937c35565774e87196 100644 (file)
@@ -36,7 +36,7 @@ ifndef::git-asciidoc-no-roff[]
 # v1.72 breaks with this because it replaces dots not in roff requests.
 [listingblock]
 <example><title>{title}</title>
-<literallayout>
+<literallayout class="monospaced">
 ifdef::doctype-manpage[]
 &#10;.ft C&#10;
 endif::doctype-manpage[]
@@ -53,7 +53,7 @@ ifdef::doctype-manpage[]
 # The following two small workarounds insert a simple paragraph after screen
 [listingblock]
 <example><title>{title}</title>
-<literallayout>
+<literallayout class="monospaced">
 |
 </literallayout><simpara></simpara>
 {title#}</example>
index a95e5a4ac9a7e35623ada63d7919b193d7f86a6e..6416cae51158a1a960f91a0673c89f67e105f0cc 100644 (file)
@@ -1210,8 +1210,16 @@ gitweb.snapshot::
 grep.lineNumber::
        If set to true, enable '-n' option by default.
 
+grep.patternType::
+       Set the default matching behavior. Using a value of 'basic', 'extended',
+       'fixed', or 'perl' will enable the '--basic-regexp', '--extended-regexp',
+       '--fixed-strings', or '--perl-regexp' option accordingly, while the
+       value 'default' will return to the default matching behavior.
+
 grep.extendedRegexp::
-       If set to true, enable '--extended-regexp' option by default.
+       If set to true, enable '--extended-regexp' option by default. This
+       option is ignored when the 'grep.patternType' option is set to a value
+       other than 'default'.
 
 gpg.program::
        Use this custom program instead of "gpg" found on $PATH when
index 47235bea0403bf25fa73529a37a7c5f87d0d7eb9..9c1d2f1781c15da786d0b1e9fb2d39562e474f29 100644 (file)
@@ -13,6 +13,8 @@ SYNOPSIS
        [--column[=<options>] | --no-column]
        [(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
+'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
+'git branch' --unset-upstream [<branchname>]
 'git branch' (-m | -M) [<oldbranch>] <newbranch>
 'git branch' (-d | -D) [-r] <branchname>...
 'git branch' --edit-description [<branchname>]
@@ -48,7 +50,7 @@ branch so that 'git pull' will appropriately merge from
 the remote-tracking branch. This behavior may be changed via the global
 `branch.autosetupmerge` configuration flag. That setting can be
 overridden by using the `--track` and `--no-track` options, and
-changed later using `git branch --set-upstream`.
+changed later using `git branch --set-upstream-to`.
 
 With a `-m` or `-M` option, <oldbranch> will be renamed to <newbranch>.
 If <oldbranch> had a corresponding reflog, it is renamed to match
@@ -173,6 +175,16 @@ start-point is either a local or remote-tracking branch.
        like `--track` would when creating the branch, except that where
        branch points to is not changed.
 
+-u <upstream>::
+--set-upstream-to=<upstream>::
+       Set up <branchname>'s tracking information so <upstream> is
+       considered <branchname>'s upstream branch. If no <branchname>
+       is specified, then it defaults to the current branch.
+
+--unset-upstream::
+       Remove the upstream information for <branchname>. If no branch
+       is specified it defaults to the current branch.
+
 --edit-description::
        Open an editor and edit the text to explain what the branch is
        for, to be used by various other commands (e.g. `request-pull`).
index 63a251612ab9a25b798c8cdbb3a69e8a76241e36..11cc7f0588d4e3a3827bf9cf8990edc5ab0da8b0 100644 (file)
@@ -84,11 +84,11 @@ entries; instead, unmerged entries are ignored.
        When checking out paths from the index, check out stage #2
        ('ours') or #3 ('theirs') for unmerged paths.
 
--b::
+-b <new_branch>::
        Create a new branch named <new_branch> and start it at
        <start_point>; see linkgit:git-branch[1] for details.
 
--B::
+-B <new_branch>::
        Creates the branch <new_branch> and start it at <start_point>;
        if it already exists, then reset it to <start_point>. This is
        equivalent to running "git branch" with "-f"; see
@@ -124,7 +124,7 @@ explicitly give a name with '-b' in such a case.
        <commit> is not a branch name.  See the "DETACHED HEAD" section
        below for details.
 
---orphan::
+--orphan <new_branch>::
        Create a new 'orphan' branch, named <new_branch>, started from
        <start_point> and switch to it.  The first commit made on this
        new branch will have no parents and it will be the root of a new
index 0e170a51cac1de3f20b31f8dba7f832c292b815c..c205d2363e426ed6b1a91df2b8d9b4a3c576a5ea 100644 (file)
@@ -118,6 +118,11 @@ effect to your index in a row.
        previous commit are dropped.  To force the inclusion of those commits
        use `--keep-redundant-commits`.
 
+--allow-empty-message::
+       By default, cherry-picking a commit with an empty message will fail.
+       This option overrides that behaviour, allowing commits with empty
+       messages to be cherry picked.
+
 --keep-redundant-commits::
        If a commit being cherry picked duplicates a commit already in the
        current history, it will become empty.  By default these
index 2d6ef32a087f3f7355251a84a7d0d294f18479a5..eaea0791657386469e448def614b949c0a1f45e9 100644 (file)
@@ -54,16 +54,16 @@ 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:
+This command will fail with non-zero status upon error.  Some exit
+codes are:
 
 . The config file is invalid (ret=3),
 . can not write to the config file (ret=4),
 . no section or name was provided (ret=2),
 . the section or key is invalid (ret=1),
 . you try to unset an option which does not exist (ret=5),
-. you try to unset/set an option for which multiple lines match (ret=5),
-. you try to use an invalid regexp (ret=6), or
-. you use '--global' option without $HOME being properly set (ret=128).
+. you try to unset/set an option for which multiple lines match (ret=5), or
+. you try to use an invalid regexp (ret=6).
 
 On success, the command returns the exit code 0.
 
@@ -267,7 +267,7 @@ Given a .git/config like this:
 
        ; Proxy settings
        [core]
-               gitproxy="proxy-command" for kernel.org
+               gitproxy=proxy-command for kernel.org
                gitproxy=default-proxy ; for all the rest
 
 you can set the filemode to true with
@@ -342,7 +342,7 @@ To actually match only values with an exclamation mark, you have to
 To add a new proxy, without altering any of the existing ones, use
 
 ------------
-% git config core.gitproxy '"proxy-command" for example.com'
+% git config --add core.gitproxy '"proxy-command" for example.com'
 ------------
 
 An example to use customized color from the configuration in your
index 11edc5a173e89e80952a5663c3c197dee4c6329c..d15db42d43b9e94628246a30e7c12df41b45b935 100644 (file)
@@ -3,7 +3,7 @@ git-credential-cache--daemon(1)
 
 NAME
 ----
-git-credential-cache--daemon - temporarily store user credentials in memory
+git-credential-cache--daemon - Temporarily store user credentials in memory
 
 SYNOPSIS
 --------
index f3d09c5d51bc49d05c423de81504d68f18f99ede..eeff5fa9891fd7e3f1afac7cac6b9e5b17112449 100644 (file)
@@ -3,7 +3,7 @@ git-credential-cache(1)
 
 NAME
 ----
-git-credential-cache - helper to temporarily store passwords in memory
+git-credential-cache - Helper to temporarily store passwords in memory
 
 SYNOPSIS
 --------
index 31093467d1bc0af0f8229cd335ad0a56c4911b36..b27c03c361111a01dbc5c2407c209e5f8530eec2 100644 (file)
@@ -3,7 +3,7 @@ git-credential-store(1)
 
 NAME
 ----
-git-credential-store - helper to store credentials on disk
+git-credential-store - Helper to store credentials on disk
 
 SYNOPSIS
 --------
index e8f757704c7fcf93e8e19f66c34557b8887ef29a..7e5098a95e48bbe0a82630e2ca5d4cebec6c3112 100644 (file)
@@ -16,6 +16,7 @@ SYNOPSIS
             [--reuseaddr] [--detach] [--pid-file=<file>]
             [--enable=<service>] [--disable=<service>]
             [--allow-override=<service>] [--forbid-override=<service>]
+            [--access-hook=<path>]
             [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>] [--user=<user> [--group=<group>]]
             [<directory>...]
 
@@ -171,6 +172,21 @@ the facility of inet daemon to achieve the same before spawning
        errors are not enabled, all errors report "access denied" to the
        client. The default is --no-informative-errors.
 
+--access-hook=<path>::
+       Every time a client connects, first run an external command
+       specified by the <path> with service name (e.g. "upload-pack"),
+       path to the repository, hostname (%H), canonical hostname
+       (%CH), ip address (%IP), and tcp port (%P) as its command line
+       arguments. The external command can decide to decline the
+       service by exiting with a non-zero status (or to allow it by
+       exiting with a zero status).  It can also look at the $REMOTE_ADDR
+       and $REMOTE_PORT environment variables to learn about the
+       requestor when making this decision.
++
+The external command can optionally write a single line to its
+standard output to be sent to the requestor as an error message when
+it declines the service.
+
 <directory>::
        A directory to add to the whitelist of allowed directories. Unless
        --strict-paths is specified this will also include subdirectories
index 039cce2e98367fdbff6f7c0ea68f8f4f77b8a107..72d6bb612ba8890a7f2a8c3ff13820a53918c74a 100644 (file)
@@ -36,12 +36,12 @@ OPTIONS
 
 --all::
        Instead of using only the annotated tags, use any ref
-       found in `.git/refs/`.  This option enables matching
+       found in `refs/` namespace.  This option enables matching
        any known branch, remote-tracking branch, or lightweight tag.
 
 --tags::
        Instead of using only the annotated tags, use any tag
-       found in `.git/refs/tags`.  This option enables matching
+       found in `refs/tags` namespace.  This option enables matching
        a lightweight (non-annotated) tag.
 
 --contains::
index 31fc2e3aed613acd8cb6e4a56ae66c378fb07a0d..73ca7025a39630907e3a019dfed14542f4ac7538 100644 (file)
@@ -69,6 +69,14 @@ with custom merge tool commands and has the same value as `$MERGED`.
 --tool-help::
        Print a list of diff tools that may be used with `--tool`.
 
+--symlinks::
+--no-symlinks::
+       'git difftool''s default behavior is create symlinks to the
+       working tree when run in `--dir-diff` mode.
++
+       Specifying `--no-symlinks` instructs 'git difftool' to create
+       copies instead.  `--no-symlinks` is the default on Windows.
+
 -x <command>::
 --extcmd=<command>::
        Specify a custom command for viewing diffs.
index 81f58234a79a94ef5e9b60e1985b096cff6fcb5c..15e7ac80c087eee7d0bf10af5653f421bcbee0dc 100644 (file)
@@ -32,7 +32,8 @@ changes, which would normally have no effect.  Nevertheless, this may be
 useful in the future for compensating for some git bugs or such,
 therefore such a usage is permitted.
 
-*NOTE*: This command honors `.git/info/grafts` and `.git/refs/replace/`.
+*NOTE*: This command honors `.git/info/grafts` file and refs in
+the `refs/replace/` namespace.
 If you have any grafts or replacement refs defined, running this command
 will make them permanent.
 
index bbb25da2dd601cd9ddcdbeae525c82a08fbfde4a..da348fc9427d144335acc9d4d2c20549f850fa32 100644 (file)
@@ -23,8 +23,8 @@ OPTIONS
        An object to treat as the head of an unreachability trace.
 +
 If no objects are given, 'git fsck' defaults to using the
-index file, all SHA1 references in .git/refs/*, and all reflogs (unless
---no-reflogs is given) as heads.
+index file, all SHA1 references in `refs` namespace, and all reflogs
+(unless --no-reflogs is given) as heads.
 
 --unreachable::
        Print out objects that exist but that aren't reachable from any
index 3bec0368831a04bc3653f11d3a0b1bff5a335907..cfecf848fb330333a655b0a54d58000041b73338 100644 (file)
@@ -42,8 +42,16 @@ CONFIGURATION
 grep.lineNumber::
        If set to true, enable '-n' option by default.
 
+grep.patternType::
+       Set the default matching behavior. Using a value of 'basic', 'extended',
+       'fixed', or 'perl' will enable the '--basic-regexp', '--extended-regexp',
+       '--fixed-strings', or '--perl-regexp' option accordingly, while the
+       value 'default' will return to the default matching behavior.
+
 grep.extendedRegexp::
-       If set to true, enable '--extended-regexp' option by default.
+       If set to true, enable '--extended-regexp' option by default. This
+       option is ignored when the 'grep.patternType' option is set to a value
+       other than 'default'.
 
 
 OPTIONS
index c406a1100159471cf904629b5d4afc0469c382c0..d54932889f3d5b3c59f403920203911d52227365 100644 (file)
@@ -48,7 +48,8 @@ $ gitk $(cd .git/lost-found/commit && echo ??*)
 ------------
 
 After making sure you know which the object is the tag you are looking
-for, you can reconnect it to your regular .git/refs hierarchy.
+for, you can reconnect it to your regular `refs` hierarchy by using
+the `update-ref` command.
 
 ------------
 $ git cat-file -t 1ef2b196
index b295bf83302de3fdac2833df756799a7aad6b404..87842e33f8aebc7d27fa732af1de15f0e4175c07 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git merge-base' [-a|--all] <commit> <commit>...
 'git merge-base' [-a|--all] --octopus <commit>...
+'git merge-base' --is-ancestor <commit> <commit>
 'git merge-base' --independent <commit>...
 
 DESCRIPTION
@@ -50,6 +51,12 @@ from linkgit:git-show-branch[1] when used with the `--merge-base` option.
        from any other.  This mimics the behavior of 'git show-branch
        --independent'.
 
+--is-ancestor::
+       Check if the first <commit> is an ancestor of the second <commit>,
+       and exit with status 0 if true, or with status 1 if not.
+       Errors are signaled by a non-zero status that is not 1.
+
+
 OPTIONS
 -------
 -a::
@@ -110,6 +117,27 @@ both '1' and '2' are merge-bases of A and B.  Neither one is better than
 the other (both are 'best' merge bases).  When the `--all` option is not given,
 it is unspecified which best one is output.
 
+A common idiom to check "fast-forward-ness" between two commits A
+and B is (or at least used to be) to compute the merge base between
+A and B, and check if it is the same as A, in which case, A is an
+ancestor of B.  You will see this idiom used often in older scripts.
+
+       A=$(git rev-parse --verify A)
+       if test "$A" = "$(git merge-base A B)"
+       then
+               ... A is an ancestor of B ...
+       fi
+
+In modern git, you can say this in a more direct way:
+
+       if git merge-base --is-ancestor A B
+       then
+               ... A is an ancestor of B ...
+       fi
+
+instead.
+
+
 See also
 --------
 linkgit:git-rev-list[1],
index d7207bd9b9bba59a0e1f97341fb77927026e1b52..6b563c500f660486951cbcac62876bbda5cc3975 100644 (file)
@@ -64,6 +64,9 @@ variable `mergetool.<tool>.trustExitCode` can be set to `true`.
 Otherwise, 'git mergetool' will prompt the user to indicate the
 success of the resolution after the custom tool has exited.
 
+--tool-help::
+       Print a list of merge tools that may be used with `--tool`.
+
 -y::
 --no-prompt::
        Don't prompt before each invocation of the merge resolution
index 10afd4edfe515013bd92ad18d842536a537142a5..f131677478e91f8ab6611a6e45dda56ff48a9e2a 100644 (file)
@@ -14,7 +14,8 @@ DESCRIPTION
 -----------
 
 Traditionally, tips of branches and tags (collectively known as
-'refs') were stored one file per ref under `$GIT_DIR/refs`
+'refs') were stored one file per ref in a (sub)directory
+under `$GIT_DIR/refs`
 directory.  While many branch tips tend to be updated often,
 most tags and some branch tips are never updated.  When a
 repository has hundreds or thousands of tags, this
@@ -22,13 +23,14 @@ one-file-per-ref format both wastes storage and hurts
 performance.
 
 This command is used to solve the storage and performance
-problem by stashing the refs in a single file,
+problem by storing the refs in a single file,
 `$GIT_DIR/packed-refs`.  When a ref is missing from the
-traditional `$GIT_DIR/refs` hierarchy, it is looked up in this
+traditional `$GIT_DIR/refs` directory hierarchy, it is looked
+up in this
 file and used if found.
 
 Subsequent updates to branches always create new files under
-`$GIT_DIR/refs` hierarchy.
+`$GIT_DIR/refs` directory hierarchy.
 
 A recommended practice to deal with a repository with too many
 refs is to pack its refs with `--all --prune` once, and
@@ -57,6 +59,15 @@ a repository with many branches of historical interests.
 The command usually removes loose refs under `$GIT_DIR/refs`
 hierarchy after packing them.  This option tells it not to.
 
+
+BUGS
+----
+
+Older documentation written before the packed-refs mechanism was
+introduced may still say things like ".git/refs/heads/<branch> file
+exists" when it means "branch <branch> exists".
+
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index defb544ed0af407a2e6360355bad34b1cb43fed9..67fa5ee19520a113bceb1a704ee84f40e8eb7693 100644 (file)
@@ -101,6 +101,7 @@ include::merge-options.txt[]
 
 :git-pull: 1
 
+-r::
 --rebase::
        Rebase the current branch on top of the upstream branch after
        fetching.  If there is a remote-tracking branch corresponding to
index 17df525275493f5bdcdd912ad56581ffa8a97fa2..51131d0858bf5a7519b8759e2ee081b3b5f27b48 100644 (file)
@@ -14,14 +14,13 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Adds a 'replace' reference in `.git/refs/replace/`
+Adds a 'replace' reference in `refs/replace/` namespace.
 
 The name of the 'replace' reference is the SHA1 of the object that is
 replaced. The content of the 'replace' reference is the SHA1 of the
 replacement object.
 
-Unless `-f` is given, the 'replace' reference must not yet exist in
-`.git/refs/replace/` directory.
+Unless `-f` is given, the 'replace' reference must not yet exist.
 
 Replacement references will be used by default by all git commands
 except those doing reachability traversal (prune, pack transfer and
index fbbbcb282cc8d6b2fd9c0ea8aea28d4a584646ea..2de7bf090094cd4a5c2d6697493040b445077bf7 100644 (file)
@@ -149,6 +149,11 @@ submodule with the `--init` option.
 +
 If `--recursive` is specified, this command will recurse into the
 registered submodules, and update any nested submodules within.
++
+If `--force` is specified, the submodule will be checked out (using
+`git checkout --force` if appropriate), even if the commit specified in the
+index of the containing repository already matches the commit checked out in
+the submodule.
 
 summary::
        Show commit summary between the given commit (defaults to HEAD) and
@@ -210,7 +215,9 @@ OPTIONS
        This option is only valid for add and update commands.
        When running add, allow adding an otherwise ignored submodule path.
        When running update, throw away local changes in submodules when
-       switching to a different commit.
+       switching to a different commit; and always run a checkout operation
+       in the submodule, even if the commit listed in the index of the
+       containing repository matches the commit checked out in the submodule.
 
 --cached::
        This option is only valid for status and summary commands.  These
index e36a7c3d1e11e38950c74b32ab4f1ac9a4683a41..247534e908d9f5b804a60b6707d1962eccc9bb1a 100644 (file)
@@ -20,11 +20,10 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Add a tag reference in `.git/refs/tags/`, unless `-d/-l/-v` is given
+Add a tag reference in `refs/tags/`, unless `-d/-l/-v` is given
 to delete, list or verify tags.
 
-Unless `-f` is given, the tag to be created must not yet exist in the
-`.git/refs/tags/` directory.
+Unless `-f` is given, the named tag must not yet exist.
 
 If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
 creates a 'tag' object, and requires a tag message.  Unless
index 27da0eb209446ea9d7c1b7705f64403f0013e49b..fab6e77e02c9773a0230ac7dadf53670a60ac8a5 100644 (file)
@@ -22,18 +22,17 @@ unusually rich command set that provides both high-level operations
 and full access to internals.
 
 See linkgit:gittutorial[7] to get started, then see
-link:everyday.html[Everyday Git] for a useful minimum set of commands, and
-"man git-commandname" for documentation of each command.  CVS users may
-also want to read linkgit:gitcvs-migration[7].  See
-the link:user-manual.html[Git User's Manual] for a more in-depth
-introduction.
+link:everyday.html[Everyday Git] for a useful minimum set of
+commands.  The link:user-manual.html[Git User's Manual] has a more
+in-depth introduction.
 
-The '<command>' is either a name of a Git command (see below) or an alias
-as defined in the configuration file (see linkgit:git-config[1]).
+After you mastered the basic concepts, you can come back to this
+page to learn what commands git offers.  You can learn more about
+individual git commands with "git help command".  linkgit:gitcli[7]
+manual page gives you an overview of the command line command syntax.
 
-Formatted and hyperlinked version of the latest git
-documentation can be viewed at
-`http://git-htmldocs.googlecode.com/git/git.html`.
+Formatted and hyperlinked version of the latest git documentation
+can be viewed at `http://git-htmldocs.googlecode.com/git/git.html`.
 
 ifdef::stalenotes[]
 [NOTE]
@@ -49,9 +48,10 @@ Documentation for older releases are available here:
 * release notes for
   link:RelNotes/1.7.12.txt[1.7.12].
 
-* link:v1.7.11.5/git.html[documentation for release 1.7.11.5]
+* link:v1.7.11.6/git.html[documentation for release 1.7.11.6]
 
 * release notes for
+  link:RelNotes/1.7.11.6.txt[1.7.11.6],
   link:RelNotes/1.7.11.5.txt[1.7.11.5],
   link:RelNotes/1.7.11.4.txt[1.7.11.4],
   link:RelNotes/1.7.11.3.txt[1.7.11.3],
@@ -411,24 +411,6 @@ help ...`.
        linkgit:git-replace[1] for more information.
 
 
-FURTHER DOCUMENTATION
----------------------
-
-See the references above to get started using git.  The following is
-probably more detail than necessary for a first-time user.
-
-The link:user-manual.html#git-concepts[git concepts chapter of the
-user-manual] and linkgit:gitcore-tutorial[7] both provide
-introductions to the underlying git architecture.
-
-See linkgit:gitworkflows[7] for an overview of recommended workflows.
-
-See also the link:howto-index.html[howto] documents for some useful
-examples.
-
-The internals are documented in the
-link:technical/api-index.html[GIT API documentation].
-
 GIT COMMANDS
 ------------
 
@@ -848,6 +830,29 @@ The index is also capable of storing multiple entries (called "stages")
 for a given pathname.  These stages are used to hold the various
 unmerged version of a file when a merge is in progress.
 
+FURTHER DOCUMENTATION
+---------------------
+
+See the references in the "description" section to get started
+using git.  The following is probably more detail than necessary
+for a first-time user.
+
+The link:user-manual.html#git-concepts[git concepts chapter of the
+user-manual] and linkgit:gitcore-tutorial[7] both provide
+introductions to the underlying git architecture.
+
+See linkgit:gitworkflows[7] for an overview of recommended workflows.
+
+See also the link:howto-index.html[howto] documents for some useful
+examples.
+
+The internals are documented in the
+link:technical/api-index.html[GIT API documentation].
+
+Users migrating from CVS may also want to
+read linkgit:gitcvs-migration[7].
+
+
 Authors
 -------
 Git was started by Linus Torvalds, and is currently maintained by Junio
index ea17f7a53b664d5e9e86ceb814c5cc7430035125..3e72a5d68ea75f4e88ef6ec8cf5bd354f1094a5d 100644 (file)
@@ -62,6 +62,14 @@ scripting git:
    `git log -1 HEAD` but write `git log -1 HEAD --`; the former will not work
    if you happen to have a file called `HEAD` in the work tree.
 
+ * many commands allow a long option "--option" to be abbreviated
+   only to their unique prefix (e.g. if there is no other option
+   whose name begins with "opt", you may be able to spell "--opt" to
+   invoke the "--option" flag), but you should fully spell them out
+   when writing your scripts; later versions of Git may introduce a
+   new option whose name shares the same prefix, e.g. "--optimize",
+   to make a short prefix that used to be unique no longer unique.
+
 
 ENHANCED OPTION PARSER
 ----------------------
index d9b2b5b2e07827ff0a8a276d2593f5e197e029b3..5436eba6e761716cfeaee2683566137736497791 100644 (file)
@@ -578,16 +578,33 @@ Commit Ordering
 
 By default, the commits are shown in reverse chronological order.
 
---topo-order::
+--date-order::
+       Show no parents before all of its children are shown, but
+       otherwise show commits in the commit timestamp order.
 
-       This option makes them appear in topological order (i.e.
-       descendant commits are shown before their parents).
+--topo-order::
+       Show no parents before all of its children are shown, and
+       avoid showing commits on multiple lines of history
+       intermixed.
++
+For example, in a commit history like this:
++
+----------------------------------------------------------------
 
---date-order::
+    ---1----2----4----7
+       \              \
+        3----5----6----8---
 
-       This option is similar to '--topo-order' in the sense that no
-       parent comes before all of its children, but otherwise things
-       are still ordered in the commit timestamp order.
+----------------------------------------------------------------
++
+where the numbers denote the order of commit timestamps, `git
+rev-list` and friends with `--date-order` show the commits in the
+timestamp order: 8 7 6 5 4 3 2 1.
++
+With `--topo-order`, they would show 8 6 5 3 7 4 2 1 (or 8 7 4 2 6 5
+3 1); some older commits are shown before newer ones in order to
+avoid showing the commits from two parallel development track mixed
+together.
 
 --reverse::
 
@@ -619,10 +636,14 @@ These options are mostly targeted for packing of git repositories.
        Only useful with '--objects'; print the object IDs that are not
        in packs.
 
---no-walk::
+--no-walk[=(sorted|unsorted)]::
 
-       Only show the given revs, but do not traverse their ancestors.
-       This has no effect if a range is specified.
+       Only show the given commits, but do not traverse their ancestors.
+       This has no effect if a range is specified. If the argument
+       "unsorted" is given, the commits are show in the order they were
+       given on the command line. Otherwise (if "sorted" or no argument
+       was given), the commits are show in reverse chronological order
+       by commit time.
 
 --do-walk::
 
index dc0070bcb7ec2f4e6773066ffb745967d9df6fb3..991fcd8f3fabe5a1d399db3b4c7f79e3989e5019 100644 (file)
@@ -55,6 +55,8 @@ when you run `git cherry-pick`.
 +
 Note that any of the 'refs/*' cases above may come either from
 the '$GIT_DIR/refs' directory or from the '$GIT_DIR/packed-refs' file.
+While the ref name encoding is unspecified, UTF-8 is prefered as
+some output processing may assume ref names in UTF-8.
 
 '<refname>@\{<date>\}', e.g. 'master@\{yesterday\}', 'HEAD@\{5 minutes ago\}'::
   A ref followed by the suffix '@' with a date specification
@@ -213,6 +215,13 @@ of 'r1' and 'r2' and is defined as
 It is the set of commits that are reachable from either one of
 'r1' or 'r2' but not from both.
 
+In these two shorthands, you can omit one end and let it default to HEAD.
+For example, 'origin..' is a shorthand for 'origin..HEAD' and asks "What
+did I do since I forked from the origin branch?"  Similarly, '..origin'
+is a shorthand for 'HEAD..origin' and asks "What did the origin do since
+I forked from them?"  Note that '..' would mean 'HEAD..HEAD' which is an
+empty range that is both reachable and unreachable from HEAD.
+
 Two other shorthands for naming a set that is formed by a commit
 and its parent commits exist.  The 'r1{caret}@' notation means all
 parents of 'r1'.  'r1{caret}!' includes commit 'r1' but excludes
index 1b7d8f140c27d76cfa460c0839c44c6742110df7..1a797812fb426189b03a814498dd7019c96607b4 100644 (file)
@@ -46,6 +46,10 @@ Functions
        Format a string and push it onto the end of the array. This is a
        convenience wrapper combining `strbuf_addf` and `argv_array_push`.
 
+`argv_array_pop`::
+       Remove the final element from the array. If there are no
+       elements in the array, do nothing.
+
 `argv_array_clear`::
        Free all memory associated with the array and return it to the
        initial, empty state.
index 339b30919e6cd9791a5cc30b93395a88fb5e9d96..d87294de2f54c354c6215fb6029a182c966d6b2e 100644 (file)
@@ -14,7 +14,7 @@ ifdef::backend-docbook[]
 # "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this.
 [listingblock]
 <example><title>{title}</title>
-<literallayout>
+<literallayout class="monospaced">
 |
 </literallayout>
 {title#}</example>
index b27a2ff687183607854ed630519b30e4136d0b6c..d2d2d699e1985ce1edbf52eaa21424c3ae1cddbc 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.12
+DEF_VER=v1.7.12.GIT
 
 LF='
 '
index f93bf1959ae47fabd0e927aa4876ea3b135f2623..56301dc0a80db5085400436bab3346f2dbdd5321 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -90,6 +90,8 @@ all::
 #
 # Define NO_MKDTEMP if you don't have mkdtemp in the C library.
 #
+# Define MKDIR_WO_TRAILING_SLASH if your mkdir() can't deal with trailing slash.
+#
 # Define NO_MKSTEMPS if you don't have mkstemps in the C library.
 #
 # Define NO_STRTOK_R if you don't have strtok_r in the C library.
@@ -501,6 +503,7 @@ 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-regex
 TEST_PROGRAMS_NEED_X += test-revision-walking
 TEST_PROGRAMS_NEED_X += test-run-command
 TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
@@ -1644,6 +1647,10 @@ ifdef NO_MKDTEMP
        COMPAT_CFLAGS += -DNO_MKDTEMP
        COMPAT_OBJS += compat/mkdtemp.o
 endif
+ifdef MKDIR_WO_TRAILING_SLASH
+       COMPAT_CFLAGS += -DMKDIR_WO_TRAILING_SLASH
+       COMPAT_OBJS += compat/mkdir.o
+endif
 ifdef NO_MKSTEMPS
        COMPAT_CFLAGS += -DNO_MKSTEMPS
 endif
@@ -2817,8 +2824,13 @@ endif
 
 ### Check documentation
 #
+ALL_COMMANDS = $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS)
+ALL_COMMANDS += git
+ALL_COMMANDS += gitk
+ALL_COMMANDS += gitweb
+ALL_COMMANDS += git-gui git-citool
 check-docs::
-       @(for v in $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git gitk; \
+       @(for v in $(ALL_COMMANDS); \
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
@@ -2840,35 +2852,13 @@ check-docs::
                sed -e '/^#/d' \
                    -e 's/[     ].*//' \
                    -e 's/^/listed /' command-list.txt; \
-               ls -1 Documentation/git*txt | \
+               $(MAKE) -C Documentation print-man1 | \
+               grep '\.txt$$' | \
                sed -e 's|Documentation/|documented |' \
                    -e 's/\.txt//'; \
        ) | while read how cmd; \
        do \
-               case "$$how,$$cmd" in \
-               *,git-citool | \
-               *,git-gui | \
-               *,git-help | \
-               documented,gitattributes | \
-               documented,gitignore | \
-               documented,gitmodules | \
-               documented,gitcli | \
-               documented,git-tools | \
-               documented,gitcore-tutorial | \
-               documented,gitcvs-migration | \
-               documented,gitdiffcore | \
-               documented,gitglossary | \
-               documented,githooks | \
-               documented,gitrepository-layout | \
-               documented,gitrevisions | \
-               documented,gittutorial | \
-               documented,gittutorial-2 | \
-               documented,git-bisect-lk2009 | \
-               documented,git-remote-helpers | \
-               documented,gitworkflows | \
-               sentinel,not,matching,is,ok ) continue ;; \
-               esac; \
-               case " $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git gitk " in \
+               case " $(ALL_COMMANDS) " in \
                *" $$cmd "*)    ;; \
                *) echo "removed but $$how: $$cmd" ;; \
                esac; \
index 19bb2ebbe687d9ded1a5c82d28d4410de1e2f19f..2484b38bbbae654684f65a874653487439f0df2f 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.7.12.txt
\ No newline at end of file
+Documentation/RelNotes/1.8.0.txt
\ No newline at end of file
index a4844330fb4da325aa364640dc80f052dc7b7ad7..466640479ecb332d8d06f6f26254023eb6e22c85 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -7,10 +7,10 @@
 #include "unpack-trees.h"
 
 static char const * const archive_usage[] = {
-       "git archive [options] <tree-ish> [<path>...]",
-       "git archive --list",
-       "git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]",
-       "git archive --remote <repo> [--exec <cmd>] --list",
+       N_("git archive [options] <tree-ish> [<path>...]"),
+       N_("git archive --list"),
+       N_("git archive --remote <repo> [--exec <cmd>] [options] <tree-ish> [<path>...]"),
+       N_("git archive --remote <repo> [--exec <cmd>] --list"),
        NULL
 };
 
@@ -319,16 +319,16 @@ static int parse_archive_args(int argc, const char **argv,
        int worktree_attributes = 0;
        struct option opts[] = {
                OPT_GROUP(""),
-               OPT_STRING(0, "format", &format, "fmt", "archive format"),
-               OPT_STRING(0, "prefix", &base, "prefix",
-                       "prepend prefix to each pathname in the archive"),
-               OPT_STRING('o', "output", &output, "file",
-                       "write the archive to this file"),
+               OPT_STRING(0, "format", &format, N_("fmt"), N_("archive format")),
+               OPT_STRING(0, "prefix", &base, N_("prefix"),
+                       N_("prepend prefix to each pathname in the archive")),
+               OPT_STRING('o', "output", &output, N_("file"),
+                       N_("write the archive to this file")),
                OPT_BOOL(0, "worktree-attributes", &worktree_attributes,
-                       "read .gitattributes in working directory"),
-               OPT__VERBOSE(&verbose, "report archived files on stderr"),
-               OPT__COMPR('0', &compression_level, "store only", 0),
-               OPT__COMPR('1', &compression_level, "compress faster", 1),
+                       N_("read .gitattributes in working directory")),
+               OPT__VERBOSE(&verbose, N_("report archived files on stderr")),
+               OPT__COMPR('0', &compression_level, N_("store only"), 0),
+               OPT__COMPR('1', &compression_level, N_("compress faster"), 1),
                OPT__COMPR_HIDDEN('2', &compression_level, 2),
                OPT__COMPR_HIDDEN('3', &compression_level, 3),
                OPT__COMPR_HIDDEN('4', &compression_level, 4),
@@ -336,15 +336,15 @@ static int parse_archive_args(int argc, const char **argv,
                OPT__COMPR_HIDDEN('6', &compression_level, 6),
                OPT__COMPR_HIDDEN('7', &compression_level, 7),
                OPT__COMPR_HIDDEN('8', &compression_level, 8),
-               OPT__COMPR('9', &compression_level, "compress better", 9),
+               OPT__COMPR('9', &compression_level, N_("compress better"), 9),
                OPT_GROUP(""),
                OPT_BOOL('l', "list", &list,
-                       "list supported archive formats"),
+                       N_("list supported archive formats")),
                OPT_GROUP(""),
-               OPT_STRING(0, "remote", &remote, "repo",
-                       "retrieve the archive from remote repository <repo>"),
-               OPT_STRING(0, "exec", &exec, "cmd",
-                       "path to the remote git-upload-archive command"),
+               OPT_STRING(0, "remote", &remote, N_("repo"),
+                       N_("retrieve the archive from remote repository <repo>")),
+               OPT_STRING(0, "exec", &exec, N_("command"),
+                       N_("path to the remote git-upload-archive command")),
                OPT_END()
        };
 
index 0b5f8898a10f16df8a6273f8960f05b670ba94bc..256741d2262b237c56b6730bea9d52c9b39b7ee3 100644 (file)
@@ -49,12 +49,21 @@ void argv_array_pushl(struct argv_array *array, ...)
        va_end(ap);
 }
 
+void argv_array_pop(struct argv_array *array)
+{
+       if (!array->argc)
+               return;
+       free((char *)array->argv[array->argc - 1]);
+       array->argv[array->argc - 1] = NULL;
+       array->argc--;
+}
+
 void argv_array_clear(struct argv_array *array)
 {
        if (array->argv != empty_argv) {
                int i;
                for (i = 0; i < array->argc; i++)
-                       free((char **)array->argv[i]);
+                       free((char *)array->argv[i]);
                free(array->argv);
        }
        argv_array_init(array);
index b93a69c36cb8d391c1d7de93f75b3d54c00e60ca..f4b98660f8a98e2cecdf70285d1d76205b3f4be7 100644 (file)
@@ -16,6 +16,7 @@ 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_pop(struct argv_array *);
 void argv_array_clear(struct argv_array *);
 
 #endif /* ARGV_ARRAY_H */
diff --git a/attr.c b/attr.c
index b52efb55a036be10618d531d66697d7f7220c524..f12c83f80a0f03b2112c212e46c571ec4074abf3 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -352,8 +352,11 @@ static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
        char buf[2048];
        int lineno = 0;
 
-       if (!fp)
+       if (!fp) {
+               if (errno != ENOENT)
+                       warn_on_inaccessible(path);
                return NULL;
+       }
        res = xcalloc(1, sizeof(*res));
        while (fgets(buf, sizeof(buf), fp))
                handle_attr_line(res, buf, path, ++lineno, macro_ok);
index ba6626b03505dd0622faea4e2bea51ed4b6720f5..8e377522fee4be94f9cdca40d44ecdcc18b42d8b 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -43,7 +43,7 @@ extern int check_pager_config(const char *cmd);
 struct diff_options;
 extern void setup_diff_pager(struct diff_options *);
 
-extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
+extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
 extern int cmd_annotate(int argc, const char **argv, const char *prefix);
index 89dce56a240d69ccb224590bf134c9656d8a0522..2816789b9d8e0976e24e110ce21de12a4eb9bafe 100644 (file)
@@ -16,7 +16,7 @@
 #include "bulk-checkin.h"
 
 static const char * const builtin_add_usage[] = {
-       "git add [options] [--] <filepattern>...",
+       N_("git add [options] [--] <filepattern>..."),
        NULL
 };
 static int patch_interactive, add_interactive, edit_interactive;
@@ -315,19 +315,19 @@ static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
 static int ignore_add_errors, addremove, intent_to_add, ignore_missing = 0;
 
 static struct option builtin_add_options[] = {
-       OPT__DRY_RUN(&show_only, "dry run"),
-       OPT__VERBOSE(&verbose, "be verbose"),
+       OPT__DRY_RUN(&show_only, N_("dry run")),
+       OPT__VERBOSE(&verbose, N_("be verbose")),
        OPT_GROUP(""),
-       OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
-       OPT_BOOLEAN('p', "patch", &patch_interactive, "select hunks interactively"),
-       OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
-       OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"),
-       OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
-       OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
-       OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"),
-       OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
-       OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
-       OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
+       OPT_BOOLEAN('i', "interactive", &add_interactive, N_("interactive picking")),
+       OPT_BOOLEAN('p', "patch", &patch_interactive, N_("select hunks interactively")),
+       OPT_BOOLEAN('e', "edit", &edit_interactive, N_("edit current diff and apply")),
+       OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files")),
+       OPT_BOOLEAN('u', "update", &take_worktree_changes, N_("update tracked files")),
+       OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, N_("record only the fact that the path will be added later")),
+       OPT_BOOLEAN('A', "all", &addremove, N_("add changes from all tracked and untracked files")),
+       OPT_BOOLEAN( 0 , "refresh", &refresh_only, N_("don't add, only refresh the index")),
+       OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
+       OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
        OPT_END(),
 };
 
index d453c833782c6aae4dd04fb9f9f68c3a8211d3d5..156b3ce3b72f51013baacff0a8ffd37ac97e6158 100644 (file)
@@ -188,7 +188,6 @@ struct patch {
        int is_new, is_delete;  /* -1 = unknown, 0 = false, 1 = true */
        int rejected;
        unsigned ws_rule;
-       unsigned long deflate_origlen;
        int lines_added, lines_deleted;
        int score;
        unsigned int is_toplevel_relative:1;
@@ -1096,15 +1095,23 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
        return -1;
 }
 
-static const char *stop_at_slash(const char *line, int llen)
+/*
+ * Skip p_value leading components from "line"; as we do not accept
+ * absolute paths, return NULL in that case.
+ */
+static const char *skip_tree_prefix(const char *line, int llen)
 {
-       int nslash = p_value;
+       int nslash;
        int i;
 
+       if (!p_value)
+               return (llen && line[0] == '/') ? NULL : line;
+
+       nslash = p_value;
        for (i = 0; i < llen; i++) {
                int ch = line[i];
                if (ch == '/' && --nslash <= 0)
-                       return &line[i];
+                       return (i == 0) ? NULL : &line[i + 1];
        }
        return NULL;
 }
@@ -1134,12 +1141,11 @@ static char *git_header_name(const char *line, int llen)
                if (unquote_c_style(&first, line, &second))
                        goto free_and_fail1;
 
-               /* advance to the first slash */
-               cp = stop_at_slash(first.buf, first.len);
-               /* we do not accept absolute paths */
-               if (!cp || cp == first.buf)
+               /* strip the a/b prefix including trailing slash */
+               cp = skip_tree_prefix(first.buf, first.len);
+               if (!cp)
                        goto free_and_fail1;
-               strbuf_remove(&first, 0, cp + 1 - first.buf);
+               strbuf_remove(&first, 0, cp - first.buf);
 
                /*
                 * second points at one past closing dq of name.
@@ -1153,22 +1159,21 @@ static char *git_header_name(const char *line, int llen)
                if (*second == '"') {
                        if (unquote_c_style(&sp, second, NULL))
                                goto free_and_fail1;
-                       cp = stop_at_slash(sp.buf, sp.len);
-                       if (!cp || cp == sp.buf)
+                       cp = skip_tree_prefix(sp.buf, sp.len);
+                       if (!cp)
                                goto free_and_fail1;
                        /* They must match, otherwise ignore */
-                       if (strcmp(cp + 1, first.buf))
+                       if (strcmp(cp, first.buf))
                                goto free_and_fail1;
                        strbuf_release(&sp);
                        return strbuf_detach(&first, NULL);
                }
 
                /* unquoted second */
-               cp = stop_at_slash(second, line + llen - second);
-               if (!cp || cp == second)
+               cp = skip_tree_prefix(second, line + llen - second);
+               if (!cp)
                        goto free_and_fail1;
-               cp++;
-               if (line + llen - cp != first.len + 1 ||
+               if (line + llen - cp != first.len ||
                    memcmp(first.buf, cp, first.len))
                        goto free_and_fail1;
                return strbuf_detach(&first, NULL);
@@ -1180,10 +1185,9 @@ static char *git_header_name(const char *line, int llen)
        }
 
        /* unquoted first name */
-       name = stop_at_slash(line, llen);
-       if (!name || name == line)
+       name = skip_tree_prefix(line, llen);
+       if (!name)
                return NULL;
-       name++;
 
        /*
         * since the first name is unquoted, a dq if exists must be
@@ -1197,10 +1201,9 @@ static char *git_header_name(const char *line, int llen)
                        if (unquote_c_style(&sp, second, NULL))
                                goto free_and_fail2;
 
-                       np = stop_at_slash(sp.buf, sp.len);
-                       if (!np || np == sp.buf)
+                       np = skip_tree_prefix(sp.buf, sp.len);
+                       if (!np)
                                goto free_and_fail2;
-                       np++;
 
                        len = sp.buf + sp.len - np;
                        if (len < second - name &&
@@ -1232,13 +1235,27 @@ static char *git_header_name(const char *line, int llen)
                case '\n':
                        return NULL;
                case '\t': case ' ':
-                       second = stop_at_slash(name + len, line_len - len);
+                       /*
+                        * Is this the separator between the preimage
+                        * and the postimage pathname?  Again, we are
+                        * only interested in the case where there is
+                        * no rename, as this is only to set def_name
+                        * and a rename patch has the names elsewhere
+                        * in an unambiguous form.
+                        */
+                       if (!name[len + 1])
+                               return NULL; /* no postimage name */
+                       second = skip_tree_prefix(name + len + 1,
+                                                 line_len - (len + 1));
                        if (!second)
                                return NULL;
-                       second++;
-                       if (second[len] == '\n' && !strncmp(name, second, len)) {
+                       /*
+                        * Does len bytes starting at "name" and "second"
+                        * (that are separated by one HT or SP we just
+                        * found) exactly match?
+                        */
+                       if (second[len] == '\n' && !strncmp(name, second, len))
                                return xmemdupz(name, len);
-                       }
                }
        }
 }
@@ -4297,7 +4314,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                OPT_NOOP_NOARG(0, "allow-binary-replacement"),
                OPT_NOOP_NOARG(0, "binary"),
                OPT_BOOLEAN(0, "numstat", &numstat,
-                       N_("shows number of added and deleted lines in decimal notation")),
+                       N_("show number of added and deleted lines in decimal notation")),
                OPT_BOOLEAN(0, "summary", &summary,
                        N_("instead of applying the patch, output a summary for the input")),
                OPT_BOOLEAN(0, "check", &check,
index 931956def986bbdf5b77e163f1b18c961e7be09c..9a1cfd3dac0123cb65d3db7597221a29e9010bd9 100644 (file)
@@ -88,12 +88,12 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
        const char *output = NULL;
        const char *remote = NULL;
        struct option local_opts[] = {
-               OPT_STRING('o', "output", &output, "file",
-                       "write the archive to this file"),
-               OPT_STRING(0, "remote", &remote, "repo",
-                       "retrieve the archive from remote repository <repo>"),
-               OPT_STRING(0, "exec", &exec, "cmd",
-                       "path to the remote git-upload-archive command"),
+               OPT_STRING('o', "output", &output, N_("file"),
+                       N_("write the archive to this file")),
+               OPT_STRING(0, "remote", &remote, N_("repo"),
+                       N_("retrieve the archive from remote repository <repo>")),
+               OPT_STRING(0, "exec", &exec, N_("command"),
+                       N_("path to the remote git-upload-archive command")),
                OPT_END()
        };
 
index 8d325a5179f68d2810b5b2082fe66dc6d00da455..e3884e3bb61c7dfb0cd97ddba7bd0558b44c6b8f 100644 (file)
@@ -4,7 +4,7 @@
 #include "bisect.h"
 
 static const char * const git_bisect_helper_usage[] = {
-       "git bisect--helper --next-all [--no-checkout]",
+       N_("git bisect--helper --next-all [--no-checkout]"),
        NULL
 };
 
@@ -14,9 +14,9 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
        int no_checkout = 0;
        struct option options[] = {
                OPT_BOOLEAN(0, "next-all", &next_all,
-                           "perform 'git bisect next'"),
+                           N_("perform 'git bisect next'")),
                OPT_BOOLEAN(0, "no-checkout", &no_checkout,
-                           "update BISECT_HEAD instead of checking out the current commit"),
+                           N_("update BISECT_HEAD instead of checking out the current commit")),
                OPT_END()
        };
 
index 0d50273ce975d956cba3c17160bf3704b57f3f9c..0e102bf2c2aa26bd0dcbcb1788341eaa7ac2c037 100644 (file)
 #include "utf8.h"
 #include "userdiff.h"
 
-static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
+static char blame_usage[] = N_("git blame [options] [rev-opts] [rev] [--] file");
 
 static const char *blame_opt_usage[] = {
        blame_usage,
        "",
-       "[rev-opts] are documented in git-rev-list(1)",
+       N_("[rev-opts] are documented in git-rev-list(1)"),
        NULL
 };
 
@@ -110,6 +110,7 @@ static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b, long ctxlen,
 int textconv_object(const char *path,
                    unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    char **buf,
                    unsigned long *buf_size)
 {
@@ -117,7 +118,7 @@ int textconv_object(const char *path,
        struct userdiff_driver *textconv;
 
        df = alloc_filespec(path);
-       fill_filespec(df, sha1, mode);
+       fill_filespec(df, sha1, sha1_valid, mode);
        textconv = get_textconv(df);
        if (!textconv) {
                free_filespec(df);
@@ -142,7 +143,7 @@ static void fill_origin_blob(struct diff_options *opt,
 
                num_read_blob++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                   textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
+                   textconv_object(o->path, o->mode, o->blob_sha1, 1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@ -406,8 +407,7 @@ static struct origin *find_origin(struct scoreboard *sb,
        paths[1] = NULL;
 
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
 
        if (is_null_sha1(origin->commit->object.sha1))
                do_diff_cache(parent->tree->object.sha1, &diff_opts);
@@ -493,8 +493,7 @@ static struct origin *find_rename(struct scoreboard *sb,
        diff_opts.single_follow = origin->path;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
 
        if (is_null_sha1(origin->commit->object.sha1))
                do_diff_cache(parent->tree->object.sha1, &diff_opts);
@@ -1074,8 +1073,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
 
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("diff-setup");
+       diff_setup_done(&diff_opts);
 
        /* Try "find copies harder" on new path if requested;
         * we do not want to use diffcore_rename() actually to
@@ -2123,7 +2121,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
-                           textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
+                           textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len))
                                strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
@@ -2315,27 +2313,27 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        static const char *revs_file = NULL;
        static const char *contents_from = NULL;
        static const struct option options[] = {
-               OPT_BOOLEAN(0, "incremental", &incremental, "Show blame entries as we find them, incrementally"),
-               OPT_BOOLEAN('b', NULL, &blank_boundary, "Show blank SHA-1 for boundary commits (Default: off)"),
-               OPT_BOOLEAN(0, "root", &show_root, "Do not treat root commits as boundaries (Default: off)"),
-               OPT_BOOLEAN(0, "show-stats", &show_stats, "Show work cost statistics"),
-               OPT_BIT(0, "score-debug", &output_option, "Show output score for blame entries", OUTPUT_SHOW_SCORE),
-               OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
-               OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
-               OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
-               OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
-               OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
-               OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
-               OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
-               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 },
-               { OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
-               OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
+               OPT_BOOLEAN(0, "incremental", &incremental, N_("Show blame entries as we find them, incrementally")),
+               OPT_BOOLEAN('b', NULL, &blank_boundary, N_("Show blank SHA-1 for boundary commits (Default: off)")),
+               OPT_BOOLEAN(0, "root", &show_root, N_("Do not treat root commits as boundaries (Default: off)")),
+               OPT_BOOLEAN(0, "show-stats", &show_stats, N_("Show work cost statistics")),
+               OPT_BIT(0, "score-debug", &output_option, N_("Show output score for blame entries"), OUTPUT_SHOW_SCORE),
+               OPT_BIT('f', "show-name", &output_option, N_("Show original filename (Default: auto)"), OUTPUT_SHOW_NAME),
+               OPT_BIT('n', "show-number", &output_option, N_("Show original linenumber (Default: off)"), OUTPUT_SHOW_NUMBER),
+               OPT_BIT('p', "porcelain", &output_option, N_("Show in a format designed for machine consumption"), OUTPUT_PORCELAIN),
+               OPT_BIT(0, "line-porcelain", &output_option, N_("Show porcelain format with per-line commit information"), OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
+               OPT_BIT('c', NULL, &output_option, N_("Use the same output mode as git-annotate (Default: off)"), OUTPUT_ANNOTATE_COMPAT),
+               OPT_BIT('t', NULL, &output_option, N_("Show raw timestamp (Default: off)"), OUTPUT_RAW_TIMESTAMP),
+               OPT_BIT('l', NULL, &output_option, N_("Show long commit SHA1 (Default: off)"), OUTPUT_LONG_OBJECT_NAME),
+               OPT_BIT('s', NULL, &output_option, N_("Suppress author name and timestamp (Default: off)"), OUTPUT_NO_AUTHOR),
+               OPT_BIT('e', "show-email", &output_option, N_("Show author email instead of name (Default: off)"), OUTPUT_SHOW_EMAIL),
+               OPT_BIT('w', NULL, &xdl_opts, N_("Ignore whitespace differences"), XDF_IGNORE_WHITESPACE),
+               OPT_BIT(0, "minimal", &xdl_opts, N_("Spend extra cycles to find better match"), XDF_NEED_MINIMAL),
+               OPT_STRING('S', NULL, &revs_file, N_("file"), N_("Use revisions from <file> instead of calling git-rev-list")),
+               OPT_STRING(0, "contents", &contents_from, N_("file"), N_("Use <file>'s contents as the final image")),
+               { OPTION_CALLBACK, 'C', NULL, &opt, N_("score"), N_("Find line copies within and across files"), PARSE_OPT_OPTARG, blame_copy_callback },
+               { OPTION_CALLBACK, 'M', NULL, &opt, N_("score"), N_("Find line movements within and across files"), PARSE_OPT_OPTARG, blame_move_callback },
+               OPT_CALLBACK('L', NULL, &bottomtop, N_("n,m"), N_("Process only line range n,m, counting from 1"), blame_bottomtop_callback),
                OPT__ABBREV(&abbrev),
                OPT_END()
        };
@@ -2516,7 +2514,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                        die("no such path %s in %s", path, final_commit_name);
 
                if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
-                   textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
+                   textconv_object(path, o->mode, o->blob_sha1, 1, (char **) &sb.final_buf,
                                    &sb.final_buf_size))
                        ;
                else
index 0e060f2e4a982efdda0f88d021e0a9d3f23c6b75..5cb6d78f2c472cc6e579bf6618898f0e44e5ef03 100644 (file)
 #include "revision.h"
 #include "string-list.h"
 #include "column.h"
+#include "utf8.h"
 
 static const char * const builtin_branch_usage[] = {
-       "git branch [options] [-r | -a] [--merged | --no-merged]",
-       "git branch [options] [-l] [-f] <branchname> [<start-point>]",
-       "git branch [options] [-r] (-d | -D) <branchname>...",
-       "git branch [options] (-m | -M) [<oldbranch>] <newbranch>",
+       N_("git branch [options] [-r | -a] [--merged | --no-merged]"),
+       N_("git branch [options] [-l] [-f] <branchname> [<start-point>]"),
+       N_("git branch [options] [-r] (-d | -D) <branchname>..."),
+       N_("git branch [options] (-m | -M) [<oldbranch>] <newbranch>"),
        NULL
 };
 
@@ -129,7 +130,7 @@ static int branch_merged(int kind, const char *name,
        if (!reference_rev)
                reference_rev = head_rev;
 
-       merged = in_merge_bases(rev, &reference_rev, 1);
+       merged = in_merge_bases(rev, reference_rev);
 
        /*
         * After the safety valve is fully redefined to "check with
@@ -139,7 +140,7 @@ static int branch_merged(int kind, const char *name,
         * a gentle reminder is in order.
         */
        if ((head_rev != reference_rev) &&
-           in_merge_bases(rev, &head_rev, 1) != merged) {
+           in_merge_bases(rev, head_rev) != merged) {
                if (merged)
                        warning(_("deleting branch '%s' that has been merged to\n"
                                "         '%s', but not yet merged to HEAD."),
@@ -249,7 +250,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 struct ref_item {
        char *name;
        char *dest;
-       unsigned int kind, len;
+       unsigned int kind, width;
        struct commit *commit;
 };
 
@@ -354,14 +355,14 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        newitem->name = xstrdup(refname);
        newitem->kind = kind;
        newitem->commit = commit;
-       newitem->len = strlen(refname);
+       newitem->width = utf8_strwidth(refname);
        newitem->dest = resolve_symref(orig_refname, prefix);
        /* adjust for "remotes/" */
        if (newitem->kind == REF_REMOTE_BRANCH &&
            ref_list->kinds != REF_REMOTE_BRANCH)
-               newitem->len += 8;
-       if (newitem->len > ref_list->maxwidth)
-               ref_list->maxwidth = newitem->len;
+               newitem->width += 8;
+       if (newitem->width > ref_list->maxwidth)
+               ref_list->maxwidth = newitem->width;
 
        return 0;
 }
@@ -490,11 +491,12 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
        }
 
        strbuf_addf(&name, "%s%s", prefix, item->name);
-       if (verbose)
+       if (verbose) {
+               int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
                strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
-                           maxwidth, name.buf,
+                           maxwidth + utf8_compensation, name.buf,
                            branch_get_color(BRANCH_COLOR_RESET));
-       else
+       else
                strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
                            name.buf, branch_get_color(BRANCH_COLOR_RESET));
 
@@ -519,8 +521,8 @@ static int calc_maxwidth(struct ref_list *refs)
        for (i = 0; i < refs->index; i++) {
                if (!matches_merge_filter(refs->list[i].commit))
                        continue;
-               if (refs->list[i].len > w)
-                       w = refs->list[i].len;
+               if (refs->list[i].width > w)
+                       w = refs->list[i].width;
        }
        return w;
 }
@@ -533,12 +535,12 @@ static void show_detached(struct ref_list *ref_list)
        if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
                struct ref_item item;
                item.name = xstrdup(_("(no branch)"));
-               item.len = strlen(item.name);
+               item.width = utf8_strwidth(item.name);
                item.kind = REF_LOCAL_BRANCH;
                item.dest = NULL;
                item.commit = head_commit;
-               if (item.len > ref_list->maxwidth)
-                       ref_list->maxwidth = item.len;
+               if (item.width > ref_list->maxwidth)
+                       ref_list->maxwidth = item.width;
                print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
                free(item.name);
        }
@@ -712,62 +714,65 @@ 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;
+       int quiet = 0, unset_upstream = 0;
+       const char *new_upstream = NULL;
        enum branch_track track;
        int kinds = REF_LOCAL_BRANCH;
        struct commit_list *with_commit = NULL;
 
        struct option options[] = {
-               OPT_GROUP("Generic options"),
+               OPT_GROUP(N_("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))",
+                       N_("show hash and subject, give twice for upstream branch")),
+               OPT__QUIET(&quiet, N_("suppress informational messages")),
+               OPT_SET_INT('t', "track",  &track, N_("set up tracking mode (see git-pull(1))"),
                        BRANCH_TRACK_EXPLICIT),
-               OPT_SET_INT( 0, "set-upstream",  &track, "change upstream info",
+               OPT_SET_INT( 0, "set-upstream",  &track, N_("change upstream info"),
                        BRANCH_TRACK_OVERRIDE),
-               OPT__COLOR(&branch_use_color, "use colored output"),
-               OPT_SET_INT('r', "remotes",     &kinds, "act on remote-tracking branches",
+               OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
+               OPT_BOOLEAN(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
+               OPT__COLOR(&branch_use_color, N_("use colored output")),
+               OPT_SET_INT('r', "remotes",     &kinds, N_("act on remote-tracking branches"),
                        REF_REMOTE_BRANCH),
                {
-                       OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
-                       "print only branches that contain the commit",
+                       OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"),
+                       N_("print only branches that contain the commit"),
                        PARSE_OPT_LASTARG_DEFAULT,
                        parse_opt_with_commit, (intptr_t)"HEAD",
                },
                {
-                       OPTION_CALLBACK, 0, "with", &with_commit, "commit",
-                       "print only branches that contain the commit",
+                       OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"),
+                       N_("print only branches that contain the commit"),
                        PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
                        parse_opt_with_commit, (intptr_t) "HEAD",
                },
                OPT__ABBREV(&abbrev),
 
-               OPT_GROUP("Specific git-branch actions:"),
-               OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches",
+               OPT_GROUP(N_("Specific git-branch actions:")),
+               OPT_SET_INT('a', "all", &kinds, N_("list both remote-tracking and local branches"),
                        REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
-               OPT_BIT('d', "delete", &delete, "delete fully merged branch", 1),
-               OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
-               OPT_BIT('m', "move", &rename, "move/rename a branch and its reflog", 1),
-               OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
-               OPT_BOOLEAN(0, "list", &list, "list branch names"),
-               OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
+               OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1),
+               OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2),
+               OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1),
+               OPT_BIT('M', NULL, &rename, N_("move/rename a branch, even if target exists"), 2),
+               OPT_BOOLEAN(0, "list", &list, N_("list branch names")),
+               OPT_BOOLEAN('l', "create-reflog", &reflog, N_("create the branch's reflog")),
                OPT_BOOLEAN(0, "edit-description", &edit_description,
-                           "edit the description for the branch"),
-               OPT__FORCE(&force_create, "force creation (when already exists)"),
+                           N_("edit the description for the branch")),
+               OPT__FORCE(&force_create, N_("force creation (when already exists)")),
                {
                        OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
-                       "commit", "print only not merged branches",
+                       N_("commit"), N_("print only not merged branches"),
                        PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
                        opt_parse_merge_filter, (intptr_t) "HEAD",
                },
                {
                        OPTION_CALLBACK, 0, "merged", &merge_filter_ref,
-                       "commit", "print only merged branches",
+                       N_("commit"), N_("print only merged branches"),
                        PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG,
                        opt_parse_merge_filter, (intptr_t) "HEAD",
                },
-               OPT_COLUMN(0, "column", &colopts, "list branches in columns"),
+               OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
                OPT_END(),
        };
 
@@ -794,10 +799,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
                             0);
 
-       if (!delete && !rename && !edit_description && argc == 0)
+       if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
                list = 1;
 
-       if (!!delete + !!rename + !!force_create + !!list > 1)
+       if (!!delete + !!rename + !!force_create + !!list + !!new_upstream + !!unset_upstream > 1)
                usage_with_options(builtin_branch_usage, options);
 
        if (abbrev == -1)
@@ -852,11 +857,62 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        rename_branch(argv[0], argv[1], rename > 1);
                else
                        usage_with_options(builtin_branch_usage, options);
+       } else if (new_upstream) {
+               struct branch *branch = branch_get(argv[0]);
+
+               if (!ref_exists(branch->refname))
+                       die(_("branch '%s' does not exist"), branch->name);
+
+               /*
+                * create_branch takes care of setting up the tracking
+                * info and making sure new_upstream is correct
+                */
+               create_branch(head, branch->name, new_upstream, 0, 0, 0, quiet, BRANCH_TRACK_OVERRIDE);
+       } else if (unset_upstream) {
+               struct branch *branch = branch_get(argv[0]);
+               struct strbuf buf = STRBUF_INIT;
+
+               if (!branch_has_merge_config(branch)) {
+                       die(_("Branch '%s' has no upstream information"), branch->name);
+               }
+
+               strbuf_addf(&buf, "branch.%s.remote", branch->name);
+               git_config_set_multivar(buf.buf, NULL, NULL, 1);
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "branch.%s.merge", branch->name);
+               git_config_set_multivar(buf.buf, NULL, NULL, 1);
+               strbuf_release(&buf);
        } else if (argc > 0 && argc <= 2) {
+               struct branch *branch = branch_get(argv[0]);
+               int branch_existed = 0, remote_tracking = 0;
+               struct strbuf buf = STRBUF_INIT;
+
                if (kinds != REF_LOCAL_BRANCH)
                        die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
+
+               if (track == BRANCH_TRACK_OVERRIDE)
+                       fprintf(stderr, _("The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to\n"));
+
+               strbuf_addf(&buf, "refs/remotes/%s", branch->name);
+               remote_tracking = ref_exists(buf.buf);
+               strbuf_release(&buf);
+
+               branch_existed = ref_exists(branch->refname);
                create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
                              force_create, reflog, 0, quiet, track);
+
+               /*
+                * We only show the instructions if the user gave us
+                * one branch which doesn't exist locally, but is the
+                * name of a remote-tracking branch.
+                */
+               if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
+                   !branch_existed && remote_tracking) {
+                       fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
+                       fprintf(stderr, _("    git branch -d %s\n"), branch->name);
+                       fprintf(stderr, _("    git branch --set-upstream-to %s\n"), branch->name);
+               }
+
        } else
                usage_with_options(builtin_branch_usage, options);
 
index af74e775a182bdf764436362cb37471e74930459..00528ddc389212f045a7718ba55a26d723927f06 100644 (file)
@@ -146,7 +146,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
 
-               if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
+               if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
                        die("git cat-file --textconv: unable to run textconv on %s",
                            obj_name);
                break;
@@ -244,8 +244,8 @@ static int batch_objects(int print_contents)
 }
 
 static const char * const cat_file_usage[] = {
-       "git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>",
-       "git cat-file (--batch|--batch-check) < <list_of_objects>",
+       N_("git cat-file (-t|-s|-e|-p|<type>|--textconv) <object>"),
+       N_("git cat-file (--batch|--batch-check) < <list_of_objects>"),
        NULL
 };
 
@@ -263,19 +263,19 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
        const char *exp_type = NULL, *obj_name = NULL;
 
        const struct option options[] = {
-               OPT_GROUP("<type> can be one of: blob, tree, commit, tag"),
-               OPT_SET_INT('t', NULL, &opt, "show object type", 't'),
-               OPT_SET_INT('s', NULL, &opt, "show object size", 's'),
+               OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")),
+               OPT_SET_INT('t', NULL, &opt, N_("show object type"), 't'),
+               OPT_SET_INT('s', NULL, &opt, N_("show object size"), 's'),
                OPT_SET_INT('e', NULL, &opt,
-                           "exit with zero when there's no error", 'e'),
-               OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'),
+                           N_("exit with zero when there's no error"), 'e'),
+               OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'),
                OPT_SET_INT(0, "textconv", &opt,
-                           "for blob objects, run textconv on object's content", 'c'),
+                           N_("for blob objects, run textconv on object's content"), 'c'),
                OPT_SET_INT(0, "batch", &batch,
-                           "show info and content of objects fed from the standard input",
+                           N_("show info and content of objects fed from the standard input"),
                            BATCH),
                OPT_SET_INT(0, "batch-check", &batch,
-                           "show info about objects fed from the standard input",
+                           N_("show info about objects fed from the standard input"),
                            BATCH_CHECK),
                OPT_END()
        };
index 44c421eb0fe9cd8947d6666d15d790bc241ee7f3..e1ff575daac8e1c12706c9555efcea0238cb7e64 100644 (file)
@@ -8,19 +8,19 @@ static int all_attrs;
 static int cached_attrs;
 static int stdin_paths;
 static const char * const check_attr_usage[] = {
-"git check-attr [-a | --all | attr...] [--] pathname...",
-"git check-attr --stdin [-a | --all | attr...] < <list-of-paths>",
+N_("git check-attr [-a | --all | attr...] [--] pathname..."),
+N_("git check-attr --stdin [-a | --all | attr...] < <list-of-paths>"),
 NULL
 };
 
 static int null_term_line;
 
 static const struct option check_attr_options[] = {
-       OPT_BOOLEAN('a', "all", &all_attrs, "report all attributes set on file"),
-       OPT_BOOLEAN(0,  "cached", &cached_attrs, "use .gitattributes only from the index"),
-       OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"),
+       OPT_BOOLEAN('a', "all", &all_attrs, N_("report all attributes set on file")),
+       OPT_BOOLEAN(0,  "cached", &cached_attrs, N_("use .gitattributes only from the index")),
+       OPT_BOOLEAN(0 , "stdin", &stdin_paths, N_("read file names from stdin")),
        OPT_BOOLEAN('z', NULL, &null_term_line,
-               "input paths are terminated by a null character"),
+               N_("input paths are terminated by a null character")),
        OPT_END()
 };
 
index c16d82b7de652026b70fc3d949ddc849f749eee8..b1feda7d5ef34f45ed2de018a56149212b51ec0a 100644 (file)
@@ -123,7 +123,7 @@ static void checkout_all(const char *prefix, int prefix_length)
 }
 
 static const char * const builtin_checkout_index_usage[] = {
-       "git checkout-index [options] [--] [<file>...]",
+       N_("git checkout-index [options] [--] [<file>...]"),
        NULL
 };
 
@@ -184,27 +184,27 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        int force = 0, quiet = 0, not_new = 0;
        struct option builtin_checkout_index_options[] = {
                OPT_BOOLEAN('a', "all", &all,
-                       "checks out all files in the index"),
-               OPT__FORCE(&force, "forces overwrite of existing files"),
+                       N_("check out all files in the index")),
+               OPT__FORCE(&force, N_("force overwrite of existing files")),
                OPT__QUIET(&quiet,
-                       "no warning for existing files and files not in index"),
+                       N_("no warning for existing files and files not in index")),
                OPT_BOOLEAN('n', "no-create", &not_new,
-                       "don't checkout new files"),
+                       N_("don't checkout new files")),
                { OPTION_CALLBACK, 'u', "index", &newfd, NULL,
-                       "update stat information in the index file",
+                       N_("update stat information in the index file"),
                        PARSE_OPT_NOARG, option_parse_u },
                { 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_BOOLEAN(0, "stdin", &read_from_stdin,
-                       "read list of paths from the standard input"),
+                       N_("read list of paths from the standard input")),
                OPT_BOOLEAN(0, "temp", &to_tempfile,
-                       "write the content to temporary files"),
-               OPT_CALLBACK(0, "prefix", NULL, "string",
-                       "when creating files, prepend <string>",
+                       N_("write the content to temporary files")),
+               OPT_CALLBACK(0, "prefix", NULL, N_("string"),
+                       N_("when creating files, prepend <string>"),
                        option_parse_prefix),
                OPT_CALLBACK(0, "stage", NULL, NULL,
-                       "copy out the files from named stage",
+                       N_("copy out the files from named stage"),
                        option_parse_stage),
                OPT_END()
        };
index d812219b30247795b6db7487329605a088057db1..d287ee6e4838603ae6f3f389431a4455099e1008 100644 (file)
@@ -22,8 +22,8 @@
 #include "argv-array.h"
 
 static const char * const checkout_usage[] = {
-       "git checkout [options] <branch>",
-       "git checkout [options] [<branch>] -- <file>...",
+       N_("git checkout [options] <branch>"),
+       N_("git checkout [options] [<branch>] -- <file>..."),
        NULL,
 };
 
@@ -316,8 +316,7 @@ static void show_local_changes(struct object *head, struct diff_options *opts)
        init_revisions(&rev, NULL);
        rev.diffopt.flags = opts->flags;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
-       if (diff_setup_done(&rev.diffopt) < 0)
-               die(_("diff_setup_done failed"));
+       diff_setup_done(&rev.diffopt);
        add_pending_object(&rev, head, NULL);
        run_diff_index(&rev, 0);
 }
@@ -934,28 +933,28 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        int patch_mode = 0;
        int dwim_new_local_branch = 1;
        struct option options[] = {
-               OPT__QUIET(&opts.quiet, "suppress progress reporting"),
-               OPT_STRING('b', NULL, &opts.new_branch, "branch",
-                          "create and checkout a new branch"),
-               OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
-                          "create/reset and checkout a branch"),
-               OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"),
-               OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"),
-               OPT_SET_INT('t', "track",  &opts.track, "set upstream info for new branch",
+               OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
+               OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
+                          N_("create and checkout a new branch")),
+               OPT_STRING('B', NULL, &opts.new_branch_force, N_("branch"),
+                          N_("create/reset and checkout a branch")),
+               OPT_BOOLEAN('l', NULL, &opts.new_branch_log, N_("create reflog for new branch")),
+               OPT_BOOLEAN(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
+               OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
                        BRANCH_TRACK_EXPLICIT),
-               OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
-               OPT_SET_INT('2', "ours", &opts.writeout_stage, "checkout our version for unmerged files",
+               OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new branch"), N_("new unparented branch")),
+               OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"),
                            2),
-               OPT_SET_INT('3', "theirs", &opts.writeout_stage, "checkout their version for unmerged files",
+               OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
                            3),
-               OPT__FORCE(&opts.force, "force checkout (throw away local modifications)"),
-               OPT_BOOLEAN('m', "merge", &opts.merge, "perform a 3-way merge with the new branch"),
-               OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, "update ignored files (default)"),
-               OPT_STRING(0, "conflict", &conflict_style, "style",
-                          "conflict style (merge or diff3)"),
-               OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
+               OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)")),
+               OPT_BOOLEAN('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
+               OPT_BOOLEAN(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")),
+               OPT_STRING(0, "conflict", &conflict_style, N_("style"),
+                          N_("conflict style (merge or diff3)")),
+               OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")),
                { OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL,
-                 "second guess 'git checkout no-such-branch'",
+                 N_("second guess 'git checkout no-such-branch'"),
                  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
                OPT_END(),
        };
index 0c7b3d0f4c28c9e9c721c961e529196751271d11..69c1cda9061f248479377d2841a1ddf703ea59ba 100644 (file)
@@ -16,7 +16,7 @@
 static int force = -1; /* unset */
 
 static const char *const builtin_clean_usage[] = {
-       "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...",
+       N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
        NULL
 };
 
@@ -48,16 +48,16 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        const char *qname;
        char *seen = NULL;
        struct option options[] = {
-               OPT__QUIET(&quiet, "do not print names of files removed"),
-               OPT__DRY_RUN(&show_only, "dry run"),
-               OPT__FORCE(&force, "force"),
+               OPT__QUIET(&quiet, N_("do not print names of files removed")),
+               OPT__DRY_RUN(&show_only, N_("dry run")),
+               OPT__FORCE(&force, N_("force")),
                OPT_BOOLEAN('d', NULL, &remove_directories,
-                               "remove whole directories"),
-               { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
-                 "add <pattern> to ignore rules", PARSE_OPT_NONEG, exclude_cb },
-               OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
+                               N_("remove whole directories")),
+               { OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
+                 N_("add <pattern> to ignore rules"), PARSE_OPT_NONEG, exclude_cb },
+               OPT_BOOLEAN('x', NULL, &ignored, N_("remove ignored files, too")),
                OPT_BOOLEAN('X', NULL, &ignored_only,
-                               "remove only ignored files"),
+                               N_("remove only ignored files")),
                OPT_END()
        };
 
index e314b0b6d2133b09307dc09fa256577e6cdba03a..5e8f3ba22c3df9ee5ec7b6ae061c97ccc1740fdd 100644 (file)
@@ -33,7 +33,7 @@
  *
  */
 static const char * const builtin_clone_usage[] = {
-       "git clone [options] [--] <repo> [<dir>]",
+       N_("git clone [options] [--] <repo> [<dir>]"),
        NULL
 };
 
@@ -61,43 +61,43 @@ static int opt_parse_reference(const struct option *opt, const char *arg, int un
 static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
        OPT_BOOL(0, "progress", &option_progress,
-                "force progress reporting"),
+                N_("force progress reporting")),
        OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
-                   "don't create a checkout"),
-       OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
+                   N_("don't create a checkout")),
+       OPT_BOOLEAN(0, "bare", &option_bare, N_("create a bare repository")),
        { OPTION_BOOLEAN, 0, "naked", &option_bare, NULL,
-               "create a bare repository",
+               N_("create a bare repository"),
                PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
        OPT_BOOLEAN(0, "mirror", &option_mirror,
-                   "create a mirror repository (implies bare)"),
+                   N_("create a mirror repository (implies bare)")),
        OPT_BOOL('l', "local", &option_local,
-               "to clone from a local repository"),
+               N_("to clone from a local repository")),
        OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks,
-                   "don't use local hardlinks, always copy"),
+                   N_("don't use local hardlinks, always copy")),
        OPT_BOOLEAN('s', "shared", &option_shared,
-                   "setup as shared repository"),
+                   N_("setup as shared repository")),
        OPT_BOOLEAN(0, "recursive", &option_recursive,
-                   "initialize submodules in the clone"),
+                   N_("initialize submodules in the clone")),
        OPT_BOOLEAN(0, "recurse-submodules", &option_recursive,
-                   "initialize submodules in the clone"),
-       OPT_STRING(0, "template", &option_template, "template-directory",
-                  "directory from which templates will be used"),
-       OPT_CALLBACK(0 , "reference", &option_reference, "repo",
-                    "reference repository", &opt_parse_reference),
-       OPT_STRING('o', "origin", &option_origin, "name",
-                  "use <name> instead of 'origin' to track upstream"),
-       OPT_STRING('b', "branch", &option_branch, "branch",
-                  "checkout <branch> instead of the remote's HEAD"),
-       OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
-                  "path to git-upload-pack on the remote"),
-       OPT_STRING(0, "depth", &option_depth, "depth",
-                   "create a shallow clone of that depth"),
+                   N_("initialize submodules in the clone")),
+       OPT_STRING(0, "template", &option_template, N_("template-directory"),
+                  N_("directory from which templates will be used")),
+       OPT_CALLBACK(0 , "reference", &option_reference, N_("repo"),
+                    N_("reference repository"), &opt_parse_reference),
+       OPT_STRING('o', "origin", &option_origin, N_("name"),
+                  N_("use <name> instead of 'origin' to track upstream")),
+       OPT_STRING('b', "branch", &option_branch, N_("branch"),
+                  N_("checkout <branch> instead of the remote's HEAD")),
+       OPT_STRING('u', "upload-pack", &option_upload_pack, N_("path"),
+                  N_("path to git-upload-pack on the remote")),
+       OPT_STRING(0, "depth", &option_depth, N_("depth"),
+                   N_("create a shallow clone of that depth")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
-                   "clone only one branch, HEAD or --branch"),
-       OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
-                  "separate git dir from working tree"),
-       OPT_STRING_LIST('c', "config", &option_config, "key=value",
-                       "set config inside the new repository"),
+                   N_("clone only one branch, HEAD or --branch")),
+       OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
+                  N_("separate git dir from working tree")),
+       OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
+                       N_("set config inside the new repository")),
        OPT_END()
 };
 
index 5ea798a7ca6a3a3969c7f911b07a01490f689f98..e125a55fc9de2b8f6d82fd5574f109b3d245934c 100644 (file)
@@ -6,7 +6,7 @@
 #include "column.h"
 
 static const char * const builtin_column_usage[] = {
-       "git column [options]",
+       N_("git column [options]"),
        NULL
 };
 static unsigned int colopts;
@@ -23,13 +23,13 @@ int cmd_column(int argc, const char **argv, const char *prefix)
        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_STRING(0, "command", &real_command, N_("name"), N_("lookup config vars")),
+               OPT_COLUMN(0, "mode", &colopts, N_("layout to use")),
+               OPT_INTEGER(0, "raw-mode", &colopts, N_("layout to use")),
+               OPT_INTEGER(0, "width", &copts.width, N_("Maximum width")),
+               OPT_STRING(0, "indent", &copts.indent, N_("string"), N_("Padding space on left border")),
+               OPT_INTEGER(0, "nl", &copts.nl, N_("Padding space on right border")),
+               OPT_INTEGER(0, "padding", &copts.padding, N_("Padding space between columns")),
                OPT_END()
        };
 
index 20cef95d600aeb15b716793beb5fb2534cc2b682..778cf16fde64ab81bbe75ec0949a77e6fd9ee702 100644 (file)
 #include "column.h"
 
 static const char * const builtin_commit_usage[] = {
-       "git commit [options] [--] <filepattern>...",
+       N_("git commit [options] [--] <filepattern>..."),
        NULL
 };
 
 static const char * const builtin_status_usage[] = {
-       "git status [options] [--] <filepattern>...",
+       N_("git status [options] [--] <filepattern>..."),
        NULL
 };
 
@@ -478,6 +478,20 @@ static void export_one(const char *var, const char *s, const char *e, int hack)
        strbuf_release(&buf);
 }
 
+static int sane_ident_split(struct ident_split *person)
+{
+       if (!person->name_begin || !person->name_end ||
+           person->name_begin == person->name_end)
+               return 0; /* no human readable name */
+       if (!person->mail_begin || !person->mail_end ||
+           person->mail_begin == person->mail_end)
+               return 0; /* no usable mail */
+       if (!person->date_begin || !person->date_end ||
+           !person->tz_begin || !person->tz_end)
+               return 0;
+       return 1;
+}
+
 static void determine_author_info(struct strbuf *author_ident)
 {
        char *name, *email, *date;
@@ -530,7 +544,8 @@ static void determine_author_info(struct strbuf *author_ident)
        if (force_date)
                date = force_date;
        strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
-       if (!split_ident_line(&author, author_ident->buf, author_ident->len)) {
+       if (!split_ident_line(&author, author_ident->buf, author_ident->len) &&
+           sane_ident_split(&author)) {
                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, '@');
@@ -1169,26 +1184,26 @@ int cmd_status(int argc, const char **argv, const char *prefix)
        int fd;
        unsigned char sha1[20];
        static struct option builtin_status_options[] = {
-               OPT__VERBOSE(&verbose, "be verbose"),
+               OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_SET_INT('s', "short", &status_format,
-                           "show status concisely", STATUS_FORMAT_SHORT),
+                           N_("show status concisely"), STATUS_FORMAT_SHORT),
                OPT_BOOLEAN('b', "branch", &s.show_branch,
-                           "show branch information"),
+                           N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
-                           "machine-readable output",
+                           N_("machine-readable output"),
                            STATUS_FORMAT_PORCELAIN),
                OPT_BOOLEAN('z', "null", &s.null_termination,
-                           "terminate entries with NUL"),
+                           N_("terminate entries with NUL")),
                { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg,
-                 "mode",
-                 "show untracked files, optional modes: all, normal, no. (Default: all)",
+                 N_("mode"),
+                 N_("show untracked files, optional modes: all, normal, no. (Default: all)"),
                  PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
                OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
-                           "show ignored files"),
-               { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, "when",
-                 "ignore changes to submodules, optional when: all, dirty, untracked. (Default: all)",
+                           N_("show ignored files")),
+               { OPTION_STRING, 0, "ignore-submodules", &ignore_submodule_arg, N_("when"),
+                 N_("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_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")),
                OPT_END(),
        };
 
@@ -1369,53 +1384,53 @@ 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) "" },
+               OPT__QUIET(&quiet, N_("suppress summary after successful commit")),
+               OPT__VERBOSE(&verbose, N_("show diff in commit message template")),
+
+               OPT_GROUP(N_("Commit message options")),
+               OPT_FILENAME('F', "file", &logfile, N_("read message from file")),
+               OPT_STRING(0, "author", &force_author, N_("author"), N_("override author for commit")),
+               OPT_STRING(0, "date", &force_date, N_("date"), N_("override date for commit")),
+               OPT_CALLBACK('m', "message", &message, N_("message"), N_("commit message"), opt_parse_m),
+               OPT_STRING('c', "reedit-message", &edit_message, N_("commit"), N_("reuse and edit message from specified commit")),
+               OPT_STRING('C', "reuse-message", &use_message, N_("commit"), N_("reuse message from specified commit")),
+               OPT_STRING(0, "fixup", &fixup_message, N_("commit"), N_("use autosquash formatted message to fixup specified commit")),
+               OPT_STRING(0, "squash", &squash_message, N_("commit"), N_("use autosquash formatted message to squash specified commit")),
+               OPT_BOOLEAN(0, "reset-author", &renew_authorship, N_("the commit is authored by me now (used with -C/-c/--amend)")),
+               OPT_BOOLEAN('s', "signoff", &signoff, N_("add Signed-off-by:")),
+               OPT_FILENAME('t', "template", &template_file, N_("use specified template file")),
+               OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
+               OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
+               OPT_BOOLEAN(0, "status", &include_status, N_("include status in commit message template")),
+               { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
+                 N_("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",
+               OPT_GROUP(N_("Commit contents options")),
+               OPT_BOOLEAN('a', "all", &all, N_("commit all changed files")),
+               OPT_BOOLEAN('i', "include", &also, N_("add specified files to index for commit")),
+               OPT_BOOLEAN(0, "interactive", &interactive, N_("interactively add files")),
+               OPT_BOOLEAN('p', "patch", &patch_interactive, N_("interactively add changes")),
+               OPT_BOOLEAN('o', "only", &only, N_("commit only specified files")),
+               OPT_BOOLEAN('n', "no-verify", &no_verify, N_("bypass pre-commit hook")),
+               OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")),
+               OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
                            STATUS_FORMAT_SHORT),
-               OPT_BOOLEAN(0, "branch", &s.show_branch, "show branch information"),
+               OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
                OPT_SET_INT(0, "porcelain", &status_format,
-                           "machine-readable output", STATUS_FORMAT_PORCELAIN),
+                           N_("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" },
+                           N_("terminate entries with NUL")),
+               OPT_BOOLEAN(0, "amend", &amend, N_("amend previous commit")),
+               OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
+               { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("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",
+                 N_("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",
+                 N_("ok to record a change with an empty message"),
                  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
 
                OPT_END()
index 8cd08da99122bc79025d2a78204d316f1b7ba478..e1c33e0691796601ac869278c155194d33ee8332 100644 (file)
@@ -4,7 +4,7 @@
 #include "parse-options.h"
 
 static const char *const builtin_config_usage[] = {
-       "git config [options]",
+       N_("git config [options]"),
        NULL
 };
 
@@ -49,33 +49,33 @@ static int respect_includes = -1;
 #define TYPE_PATH (1<<3)
 
 static struct option builtin_config_options[] = {
-       OPT_GROUP("Config file location"),
-       OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
-       OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
-       OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"),
-       OPT_STRING('f', "file", &given_config_file, "file", "use given config file"),
-       OPT_GROUP("Action"),
-       OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
-       OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL),
-       OPT_BIT(0, "get-regexp", &actions, "get values for regexp: name-regex [value-regex]", ACTION_GET_REGEXP),
-       OPT_BIT(0, "replace-all", &actions, "replace all matching variables: name value [value_regex]", ACTION_REPLACE_ALL),
-       OPT_BIT(0, "add", &actions, "adds a new variable: name value", ACTION_ADD),
-       OPT_BIT(0, "unset", &actions, "removes a variable: name [value-regex]", ACTION_UNSET),
-       OPT_BIT(0, "unset-all", &actions, "removes all matches: name [value-regex]", ACTION_UNSET_ALL),
-       OPT_BIT(0, "rename-section", &actions, "rename section: old-name new-name", ACTION_RENAME_SECTION),
-       OPT_BIT(0, "remove-section", &actions, "remove a section: name", ACTION_REMOVE_SECTION),
-       OPT_BIT('l', "list", &actions, "list all", ACTION_LIST),
-       OPT_BIT('e', "edit", &actions, "opens an editor", ACTION_EDIT),
-       OPT_STRING(0, "get-color", &get_color_slot, "slot", "find the color configured: [default]"),
-       OPT_STRING(0, "get-colorbool", &get_colorbool_slot, "slot", "find the color setting: [stdout-is-tty]"),
-       OPT_GROUP("Type"),
-       OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL),
-       OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT),
-       OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT),
-       OPT_BIT(0, "path", &types, "value is a path (file or directory name)", TYPE_PATH),
-       OPT_GROUP("Other"),
-       OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"),
-       OPT_BOOL(0, "includes", &respect_includes, "respect include directives on lookup"),
+       OPT_GROUP(N_("Config file location")),
+       OPT_BOOLEAN(0, "global", &use_global_config, N_("use global config file")),
+       OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
+       OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
+       OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
+       OPT_GROUP(N_("Action")),
+       OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
+       OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
+       OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP),
+       OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL),
+       OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
+       OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
+       OPT_BIT(0, "unset-all", &actions, N_("remove all matches: name [value-regex]"), ACTION_UNSET_ALL),
+       OPT_BIT(0, "rename-section", &actions, N_("rename section: old-name new-name"), ACTION_RENAME_SECTION),
+       OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
+       OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
+       OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
+       OPT_STRING(0, "get-color", &get_color_slot, N_("slot"), N_("find the color configured: [default]")),
+       OPT_STRING(0, "get-colorbool", &get_colorbool_slot, N_("slot"), N_("find the color setting: [stdout-is-tty]")),
+       OPT_GROUP(N_("Type")),
+       OPT_BIT(0, "bool", &types, N_("value is \"true\" or \"false\""), TYPE_BOOL),
+       OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT),
+       OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
+       OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
+       OPT_GROUP(N_("Other")),
+       OPT_BOOLEAN('z', "null", &end_null, N_("terminate values with NUL byte")),
+       OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
        OPT_END(),
 };
 
@@ -160,7 +160,7 @@ static int show_config(const char *key_, const char *value_, void *cb)
 
 static int get_value(const char *key_, const char *regex_)
 {
-       int ret = -1;
+       int ret = CONFIG_GENERIC_ERROR;
        char *global = NULL, *xdg = NULL, *repo_config = NULL;
        const char *system_wide = NULL, *local;
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
@@ -196,11 +196,14 @@ static int get_value(const char *key_, const char *regex_)
                if (regcomp(key_regexp, key, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid key pattern: %s\n", key_);
                        free(key);
+                       ret = CONFIG_INVALID_PATTERN;
                        goto free_strings;
                }
        } else {
-               if (git_config_parse_key(key_, &key, NULL))
+               if (git_config_parse_key(key_, &key, NULL)) {
+                       ret = CONFIG_INVALID_KEY;
                        goto free_strings;
+               }
        }
 
        if (regex_) {
@@ -212,6 +215,7 @@ static int get_value(const char *key_, const char *regex_)
                regexp = (regex_t*)xmalloc(sizeof(regex_t));
                if (regcomp(regexp, regex_, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid pattern: %s\n", regex_);
+                       ret = CONFIG_INVALID_PATTERN;
                        goto free_strings;
                }
        }
@@ -396,8 +400,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                         */
                        die("$HOME not set");
 
-               if (access(user_config, R_OK) &&
-                   xdg_config && !access(xdg_config, R_OK))
+               if (access_or_warn(user_config, R_OK) &&
+                   xdg_config && !access_or_warn(xdg_config, R_OK))
                        given_config_file = xdg_config;
                else
                        given_config_file = user_config;
index c37cb98c31ddfaa90d38a0355d32cf3d6c404bff..9afaa88f776468a0de33dd153eadae7621cf6267 100644 (file)
@@ -66,7 +66,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
 }
 
 static char const * const count_objects_usage[] = {
-       "git count-objects [-v]",
+       N_("git count-objects [-v]"),
        NULL
 };
 
@@ -79,7 +79,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
        unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
        off_t loose_size = 0;
        struct option opts[] = {
-               OPT__VERBOSE(&verbose, "be verbose"),
+               OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_END(),
        };
 
index 9f63067f50a6f49d61d40474608535905bec905b..9fe11ed9de7b9e5c13187a630c45d8e44f50c851 100644 (file)
@@ -12,8 +12,8 @@
 #define MAX_TAGS       (FLAG_BITS - 1)
 
 static const char * const describe_usage[] = {
-       "git describe [options] <committish>*",
-       "git describe [options] --dirty",
+       N_("git describe [options] <committish>*"),
+       N_("git describe [options] --dirty"),
        NULL
 };
 
@@ -400,22 +400,22 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 {
        int contains = 0;
        struct option options[] = {
-               OPT_BOOLEAN(0, "contains",   &contains, "find the tag that comes after the commit"),
-               OPT_BOOLEAN(0, "debug",      &debug, "debug search strategy on stderr"),
-               OPT_BOOLEAN(0, "all",        &all, "use any ref in .git/refs"),
-               OPT_BOOLEAN(0, "tags",       &tags, "use any tag in .git/refs/tags"),
-               OPT_BOOLEAN(0, "long",       &longformat, "always use long format"),
+               OPT_BOOLEAN(0, "contains",   &contains, N_("find the tag that comes after the commit")),
+               OPT_BOOLEAN(0, "debug",      &debug, N_("debug search strategy on stderr")),
+               OPT_BOOLEAN(0, "all",        &all, N_("use any ref in .git/refs")),
+               OPT_BOOLEAN(0, "tags",       &tags, N_("use any tag in .git/refs/tags")),
+               OPT_BOOLEAN(0, "long",       &longformat, N_("always use long format")),
                OPT__ABBREV(&abbrev),
                OPT_SET_INT(0, "exact-match", &max_candidates,
-                           "only output exact matches", 0),
+                           N_("only output exact matches"), 0),
                OPT_INTEGER(0, "candidates", &max_candidates,
-                           "consider <n> most recent tags (default: 10)"),
-               OPT_STRING(0, "match",       &pattern, "pattern",
-                          "only consider tags matching <pattern>"),
+                           N_("consider <n> most recent tags (default: 10)")),
+               OPT_STRING(0, "match",       &pattern, N_("pattern"),
+                          N_("only consider tags matching <pattern>")),
                OPT_BOOLEAN(0, "always",     &always,
-                          "show abbreviated commit object as fallback"),
-               {OPTION_STRING, 0, "dirty",  &dirty, "mark",
-                          "append <mark> on dirty working tree (default: \"-dirty\")",
+                          N_("show abbreviated commit object as fallback")),
+               {OPTION_STRING, 0, "dirty",  &dirty, N_("mark"),
+                          N_("append <mark> on dirty working tree (default: \"-dirty\")"),
                 PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
                OPT_END(),
        };
index da8f6aac2bde9bb93cb059898a21c19c9bb27634..9650be2c50e6f79dbe033fae39f490d49c6318da 100644 (file)
@@ -29,6 +29,8 @@ static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
                         const unsigned char *old_sha1,
                         const unsigned char *new_sha1,
+                        int old_sha1_valid,
+                        int new_sha1_valid,
                         const char *old_name,
                         const char *new_name)
 {
@@ -54,8 +56,8 @@ static void stuff_change(struct diff_options *opt,
 
        one = alloc_filespec(old_name);
        two = alloc_filespec(new_name);
-       fill_filespec(one, old_sha1, old_mode);
-       fill_filespec(two, new_sha1, new_mode);
+       fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
+       fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
 
        diff_queue(&diff_queued_diff, one, two);
 }
@@ -84,6 +86,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
        stuff_change(&revs->diffopt,
                     blob[0].mode, canon_mode(st.st_mode),
                     blob[0].sha1, null_sha1,
+                    1, 0,
                     path, path);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
@@ -108,6 +111,7 @@ static int builtin_diff_blobs(struct rev_info *revs,
        stuff_change(&revs->diffopt,
                     blob[0].mode, blob[1].mode,
                     blob[0].sha1, blob[1].sha1,
+                    1, 1,
                     blob[0].name, blob[1].name);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
@@ -298,8 +302,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        argc = setup_revisions(argc, argv, &rev, NULL);
        if (!rev.diffopt.output_format) {
                rev.diffopt.output_format = DIFF_FORMAT_PATCH;
-               if (diff_setup_done(&rev.diffopt) < 0)
-                       die(_("diff_setup_done failed"));
+               diff_setup_done(&rev.diffopt);
        }
 
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
index 9ab6db3fb042d0cb7ee3a1919788b9a6cd26a3d6..12220ad8dac65acaa556c6524310d5540709f324 100644 (file)
@@ -19,7 +19,7 @@
 #include "quote.h"
 
 static const char *fast_export_usage[] = {
-       "git fast-export [rev-list-opts]",
+       N_("git fast-export [rev-list-opts]"),
        NULL
 };
 
@@ -632,24 +632,24 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        char *export_filename = NULL, *import_filename = NULL;
        struct option options[] = {
                OPT_INTEGER(0, "progress", &progress,
-                           "show progress after <n> objects"),
-               OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
-                            "select handling of signed tags",
+                           N_("show progress after <n> objects")),
+               OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, N_("mode"),
+                            N_("select handling of signed tags"),
                             parse_opt_signed_tag_mode),
-               OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, "mode",
-                            "select handling of tags that tag filtered objects",
+               OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, N_("mode"),
+                            N_("select handling of tags that tag filtered objects"),
                             parse_opt_tag_of_filtered_mode),
-               OPT_STRING(0, "export-marks", &export_filename, "file",
-                            "Dump marks to this file"),
-               OPT_STRING(0, "import-marks", &import_filename, "file",
-                            "Import marks from this file"),
+               OPT_STRING(0, "export-marks", &export_filename, N_("file"),
+                            N_("Dump marks to this file")),
+               OPT_STRING(0, "import-marks", &import_filename, N_("file"),
+                            N_("Import marks from this file")),
                OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
-                            "Fake a tagger when tags lack one"),
+                            N_("Fake a tagger when tags lack one")),
                OPT_BOOLEAN(0, "full-tree", &full_tree,
-                            "Output full tree for each commit"),
+                            N_("Output full tree for each commit")),
                OPT_BOOLEAN(0, "use-done-feature", &use_done_feature,
-                            "Use the done feature to terminate the stream"),
-               OPT_BOOL(0, "no-data", &no_data, "Skip output of blob data"),
+                            N_("Use the done feature to terminate the stream")),
+               OPT_BOOL(0, "no-data", &no_data, N_("Skip output of blob data")),
                OPT_END()
        };
 
index 149db88726b8b5f924e6d136828008ec952bc88e..fdda36f1497eaa971e81cc1df0fb21ada6d21120 100644 (file)
@@ -10,6 +10,7 @@
 #include "remote.h"
 #include "run-command.h"
 #include "transport.h"
+#include "version.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -18,6 +19,7 @@ static int prefer_ofs_delta = 1;
 static int no_done;
 static int fetch_fsck_objects = -1;
 static int transfer_fsck_objects = -1;
+static int agent_supported;
 static struct fetch_pack_args args = {
        /* .uploadpack = */ "git-upload-pack",
 };
@@ -327,6 +329,8 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                        if (args.no_progress)   strbuf_addstr(&c, " no-progress");
                        if (args.include_tag)   strbuf_addstr(&c, " include-tag");
                        if (prefer_ofs_delta)   strbuf_addstr(&c, " ofs-delta");
+                       if (agent_supported)    strbuf_addf(&c, " agent=%s",
+                                                           git_user_agent_sanitized());
                        packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
                        strbuf_release(&c);
                } else
@@ -783,6 +787,8 @@ static struct ref *do_fetch_pack(int fd[2],
 {
        struct ref *ref = copy_ref_list(orig_ref);
        unsigned char sha1[20];
+       const char *agent_feature;
+       int agent_len;
 
        sort_ref_list(&ref, ref_compare_name);
 
@@ -814,11 +820,25 @@ static struct ref *do_fetch_pack(int fd[2],
                        fprintf(stderr, "Server supports side-band\n");
                use_sideband = 1;
        }
+       if (!server_supports("thin-pack"))
+               args.use_thin_pack = 0;
+       if (!server_supports("no-progress"))
+               args.no_progress = 0;
+       if (!server_supports("include-tag"))
+               args.include_tag = 0;
        if (server_supports("ofs-delta")) {
                if (args.verbose)
                        fprintf(stderr, "Server supports ofs-delta\n");
        } else
                prefer_ofs_delta = 0;
+
+       if ((agent_feature = server_feature_value("agent", &agent_len))) {
+               agent_supported = 1;
+               if (args.verbose && agent_len)
+                       fprintf(stderr, "Server version is %.*s\n",
+                               agent_len, agent_feature);
+       }
+
        if (everything_local(&ref, nr_match, match)) {
                packet_flush(fd[1]);
                goto all_done;
index bb9a0743ff565f3002eb0fede8e35b7f01a73b75..24be754e1b4444ea46752016ba77bdef1c892712 100644 (file)
 #include "transport.h"
 #include "submodule.h"
 #include "connected.h"
+#include "argv-array.h"
 
 static const char * const builtin_fetch_usage[] = {
-       "git fetch [<options>] [<repository> [<refspec>...]]",
-       "git fetch [<options>] <group>",
-       "git fetch --multiple [<options>] [(<repository> | <group>)...]",
-       "git fetch --all [<options>]",
+       N_("git fetch [<options>] [<repository> [<refspec>...]]"),
+       N_("git fetch [<options>] <group>"),
+       N_("git fetch --multiple [<options>] [(<repository> | <group>)...]"),
+       N_("git fetch --all [<options>]"),
        NULL
 };
 
@@ -56,36 +57,36 @@ static int option_parse_recurse_submodules(const struct option *opt,
 static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
        OPT_BOOLEAN(0, "all", &all,
-                   "fetch from all remotes"),
+                   N_("fetch from all remotes")),
        OPT_BOOLEAN('a', "append", &append,
-                   "append to .git/FETCH_HEAD instead of overwriting"),
-       OPT_STRING(0, "upload-pack", &upload_pack, "path",
-                  "path to upload pack on remote end"),
-       OPT__FORCE(&force, "force overwrite of local branch"),
+                   N_("append to .git/FETCH_HEAD instead of overwriting")),
+       OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
+                  N_("path to upload pack on remote end")),
+       OPT__FORCE(&force, N_("force overwrite of local branch")),
        OPT_BOOLEAN('m', "multiple", &multiple,
-                   "fetch from multiple remotes"),
+                   N_("fetch from multiple remotes")),
        OPT_SET_INT('t', "tags", &tags,
-                   "fetch all tags and associated objects", TAGS_SET),
+                   N_("fetch all tags and associated objects"), TAGS_SET),
        OPT_SET_INT('n', NULL, &tags,
-                   "do not fetch all tags (--no-tags)", TAGS_UNSET),
+                   N_("do not fetch all tags (--no-tags)"), TAGS_UNSET),
        OPT_BOOLEAN('p', "prune", &prune,
-                   "prune remote-tracking branches no longer on remote"),
-       { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "on-demand",
-                   "control recursive fetching of submodules",
+                   N_("prune remote-tracking branches no longer on remote")),
+       { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
+                   N_("control recursive fetching of submodules"),
                    PARSE_OPT_OPTARG, option_parse_recurse_submodules },
        OPT_BOOLEAN(0, "dry-run", &dry_run,
-                   "dry run"),
-       OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
+                   N_("dry run")),
+       OPT_BOOLEAN('k', "keep", &keep, N_("keep downloaded pack")),
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
-                   "allow updating of HEAD ref"),
-       OPT_BOOL(0, "progress", &progress, "force progress reporting"),
-       OPT_STRING(0, "depth", &depth, "depth",
-                  "deepen history of shallow clone"),
-       { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "dir",
-                  "prepend this to submodule path output", PARSE_OPT_HIDDEN },
+                   N_("allow updating of HEAD ref")),
+       OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+       OPT_STRING(0, "depth", &depth, N_("depth"),
+                  N_("deepen history of shallow clone")),
+       { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
+                  N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
        { OPTION_STRING, 0, "recurse-submodules-default",
                   &recurse_submodules_default, NULL,
-                  "default mode for recursion", PARSE_OPT_HIDDEN },
+                  N_("default mode for recursion"), PARSE_OPT_HIDDEN },
        OPT_END()
 };
 
@@ -323,7 +324,7 @@ static int update_local_ref(struct ref *ref,
                return r;
        }
 
-       if (in_merge_bases(current, &updated, 1)) {
+       if (in_merge_bases(current, updated)) {
                char quickref[83];
                int r;
                strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
@@ -841,38 +842,35 @@ static int add_remote_or_group(const char *name, struct string_list *list)
        return 1;
 }
 
-static void add_options_to_argv(int *argc, const char **argv)
+static void add_options_to_argv(struct argv_array *argv)
 {
        if (dry_run)
-               argv[(*argc)++] = "--dry-run";
+               argv_array_push(argv, "--dry-run");
        if (prune)
-               argv[(*argc)++] = "--prune";
+               argv_array_push(argv, "--prune");
        if (update_head_ok)
-               argv[(*argc)++] = "--update-head-ok";
+               argv_array_push(argv, "--update-head-ok");
        if (force)
-               argv[(*argc)++] = "--force";
+               argv_array_push(argv, "--force");
        if (keep)
-               argv[(*argc)++] = "--keep";
+               argv_array_push(argv, "--keep");
        if (recurse_submodules == RECURSE_SUBMODULES_ON)
-               argv[(*argc)++] = "--recurse-submodules";
+               argv_array_push(argv, "--recurse-submodules");
        else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
-               argv[(*argc)++] = "--recurse-submodules=on-demand";
+               argv_array_push(argv, "--recurse-submodules=on-demand");
        if (verbosity >= 2)
-               argv[(*argc)++] = "-v";
+               argv_array_push(argv, "-v");
        if (verbosity >= 1)
-               argv[(*argc)++] = "-v";
+               argv_array_push(argv, "-v");
        else if (verbosity < 0)
-               argv[(*argc)++] = "-q";
+               argv_array_push(argv, "-q");
 
 }
 
 static int fetch_multiple(struct string_list *list)
 {
        int i, result = 0;
-       const char *argv[12] = { "fetch", "--append" };
-       int argc = 2;
-
-       add_options_to_argv(&argc, argv);
+       struct argv_array argv = ARGV_ARRAY_INIT;
 
        if (!append && !dry_run) {
                int errcode = truncate_fetch_head();
@@ -880,18 +878,22 @@ static int fetch_multiple(struct string_list *list)
                        return errcode;
        }
 
+       argv_array_pushl(&argv, "fetch", "--append", NULL);
+       add_options_to_argv(&argv);
+
        for (i = 0; i < list->nr; i++) {
                const char *name = list->items[i].string;
-               argv[argc] = name;
-               argv[argc + 1] = NULL;
+               argv_array_push(&argv, name);
                if (verbosity >= 0)
                        printf(_("Fetching %s\n"), name);
-               if (run_command_v_opt(argv, RUN_GIT_CMD)) {
+               if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
                        error(_("Could not fetch %s"), name);
                        result = 1;
                }
+               argv_array_pop(&argv);
        }
 
+       argv_array_clear(&argv);
        return result;
 }
 
@@ -1007,13 +1009,14 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        }
 
        if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
-               const char *options[10];
-               int num_options = 0;
-               add_options_to_argv(&num_options, options);
-               result = fetch_populated_submodules(num_options, options,
+               struct argv_array options = ARGV_ARRAY_INIT;
+
+               add_options_to_argv(&options);
+               result = fetch_populated_submodules(&options,
                                                    submodule_prefix,
                                                    recurse_submodules,
                                                    verbosity < 0);
+               argv_array_clear(&options);
        }
 
        /* All names were strdup()ed or strndup()ed */
index 2c4d435da111770bcb3271de4c46e806f3a693b4..e2e27b2c404a1c451a20e396a7aa03dece722dad 100644 (file)
@@ -10,7 +10,7 @@
 #include "gpg-interface.h"
 
 static const char * const fmt_merge_msg_usage[] = {
-       "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]",
+       N_("git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]"),
        NULL
 };
 
@@ -650,16 +650,16 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        const char *message = NULL;
        int shortlog_len = -1;
        struct option options[] = {
-               { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
-                 "populate log with at most <n> entries from shortlog",
+               { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
+                 N_("populate log with at most <n> entries from shortlog"),
                  PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
-               { OPTION_INTEGER, 0, "summary", &shortlog_len, "n",
-                 "alias for --log (deprecated)",
+               { OPTION_INTEGER, 0, "summary", &shortlog_len, N_("n"),
+                 N_("alias for --log (deprecated)"),
                  PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL,
                  DEFAULT_MERGE_LOG_LEN },
-               OPT_STRING('m', "message", &message, "text",
-                       "use <text> as start of message"),
-               OPT_FILENAME('F', "file", &inpath, "file to read from"),
+               OPT_STRING('m', "message", &message, N_("text"),
+                       N_("use <text> as start of message")),
+               OPT_FILENAME('F', "file", &inpath, N_("file to read from")),
                OPT_END()
        };
 
index b01d76a24323e86e9c9cbf1cd3adc9c0d1b2c6d8..7f059c31dfbd11f77c891134ccc34cdc1f65bcb8 100644 (file)
@@ -962,7 +962,9 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
        if (!arg) /* should --no-sort void the list ? */
                return -1;
 
-       *sort_tail = s = xcalloc(1, sizeof(*s));
+       s = xcalloc(1, sizeof(*s));
+       s->next = *sort_tail;
+       *sort_tail = s;
 
        if (*arg == '-') {
                s->reverse = 1;
@@ -974,7 +976,7 @@ static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
 }
 
 static char const * const for_each_ref_usage[] = {
-       "git for-each-ref [options] [<pattern>]",
+       N_("git for-each-ref [options] [<pattern>]"),
        NULL
 };
 
@@ -989,19 +991,19 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 
        struct option opts[] = {
                OPT_BIT('s', "shell", &quote_style,
-                       "quote placeholders suitably for shells", QUOTE_SHELL),
+                       N_("quote placeholders suitably for shells"), QUOTE_SHELL),
                OPT_BIT('p', "perl",  &quote_style,
-                       "quote placeholders suitably for perl", QUOTE_PERL),
+                       N_("quote placeholders suitably for perl"), QUOTE_PERL),
                OPT_BIT(0 , "python", &quote_style,
-                       "quote placeholders suitably for python", QUOTE_PYTHON),
+                       N_("quote placeholders suitably for python"), QUOTE_PYTHON),
                OPT_BIT(0 , "tcl",  &quote_style,
-                       "quote placeholders suitably for tcl", QUOTE_TCL),
+                       N_("quote placeholders suitably for tcl"), QUOTE_TCL),
 
                OPT_GROUP(""),
-               OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"),
-               OPT_STRING(  0 , "format", &format, "format", "format to use for the output"),
-               OPT_CALLBACK(0 , "sort", sort_tail, "key",
-                           "field name to sort on", &opt_parse_sort),
+               OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
+               OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
+               OPT_CALLBACK(0 , "sort", sort_tail, N_("key"),
+                           N_("field name to sort on"), &opt_parse_sort),
                OPT_END(),
        };
 
index a710227a64a9862c0a70f3022f901fc65b0c7f90..bb9a2cd44722dc27d54aa5451278a10f512becb8 100644 (file)
@@ -605,23 +605,23 @@ static int fsck_cache_tree(struct cache_tree *it)
 }
 
 static char const * const fsck_usage[] = {
-       "git fsck [options] [<object>...]",
+       N_("git fsck [options] [<object>...]"),
        NULL
 };
 
 static struct option fsck_opts[] = {
-       OPT__VERBOSE(&verbose, "be verbose"),
-       OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"),
-       OPT_BOOL(0, "dangling", &show_dangling, "show dangling objects"),
-       OPT_BOOLEAN(0, "tags", &show_tags, "report tags"),
-       OPT_BOOLEAN(0, "root", &show_root, "report root nodes"),
-       OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"),
-       OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"),
-       OPT_BOOLEAN(0, "full", &check_full, "also consider packs and alternate objects"),
-       OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"),
+       OPT__VERBOSE(&verbose, N_("be verbose")),
+       OPT_BOOLEAN(0, "unreachable", &show_unreachable, N_("show unreachable objects")),
+       OPT_BOOL(0, "dangling", &show_dangling, N_("show dangling objects")),
+       OPT_BOOLEAN(0, "tags", &show_tags, N_("report tags")),
+       OPT_BOOLEAN(0, "root", &show_root, N_("report root nodes")),
+       OPT_BOOLEAN(0, "cache", &keep_cache_objects, N_("make index objects head nodes")),
+       OPT_BOOLEAN(0, "reflogs", &include_reflogs, N_("make reflogs head nodes (default)")),
+       OPT_BOOLEAN(0, "full", &check_full, N_("also consider packs and alternate objects")),
+       OPT_BOOLEAN(0, "strict", &check_strict, N_("enable more strict checking")),
        OPT_BOOLEAN(0, "lost-found", &write_lost_and_found,
-                               "write dangling objects in .git/lost-found"),
-       OPT_BOOL(0, "progress", &show_progress, "show progress"),
+                               N_("write dangling objects in .git/lost-found")),
+       OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
        OPT_END(),
 };
 
index 9b4232c8f30715327342c3d86c027d04c2a44c94..6d46608fc9ffe45a9ebb7ebb4570e0b163455411 100644 (file)
@@ -19,7 +19,7 @@
 #define FAILED_RUN "failed to run %s"
 
 static const char * const builtin_gc_usage[] = {
-       "git gc [options]",
+       N_("git gc [options]"),
        NULL
 };
 
@@ -174,12 +174,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        int quiet = 0;
 
        struct option builtin_gc_options[] = {
-               OPT__QUIET(&quiet, "suppress progress reporting"),
-               { OPTION_STRING, 0, "prune", &prune_expire, "date",
-                       "prune unreferenced objects",
+               OPT__QUIET(&quiet, N_("suppress progress reporting")),
+               { OPTION_STRING, 0, "prune", &prune_expire, N_("date"),
+                       N_("prune unreferenced objects"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
-               OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
-               OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
+               OPT_BOOLEAN(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
+               OPT_BOOLEAN(0, "auto", &auto_gc, N_("enable auto-gc mode")),
                OPT_END()
        };
 
index 29adb0ac9399002b07942711863fa3b353926468..09ca4c980e633895dd104307e76770da3f208a06 100644 (file)
@@ -19,7 +19,7 @@
 #include "dir.h"
 
 static char const * const grep_usage[] = {
-       "git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]",
+       N_("git grep [options] [-e] <pattern> [<rev>...] [[--] <path>...]"),
        NULL
 };
 
@@ -260,6 +260,53 @@ static int wait_all(void)
 }
 #endif
 
+static int parse_pattern_type_arg(const char *opt, const char *arg)
+{
+       if (!strcmp(arg, "default"))
+               return GREP_PATTERN_TYPE_UNSPECIFIED;
+       else if (!strcmp(arg, "basic"))
+               return GREP_PATTERN_TYPE_BRE;
+       else if (!strcmp(arg, "extended"))
+               return GREP_PATTERN_TYPE_ERE;
+       else if (!strcmp(arg, "fixed"))
+               return GREP_PATTERN_TYPE_FIXED;
+       else if (!strcmp(arg, "perl"))
+               return GREP_PATTERN_TYPE_PCRE;
+       die("bad %s argument: %s", opt, arg);
+}
+
+static void grep_pattern_type_options(const int pattern_type, struct grep_opt *opt)
+{
+       switch (pattern_type) {
+       case GREP_PATTERN_TYPE_UNSPECIFIED:
+               /* fall through */
+
+       case GREP_PATTERN_TYPE_BRE:
+               opt->fixed = 0;
+               opt->pcre = 0;
+               opt->regflags &= ~REG_EXTENDED;
+               break;
+
+       case GREP_PATTERN_TYPE_ERE:
+               opt->fixed = 0;
+               opt->pcre = 0;
+               opt->regflags |= REG_EXTENDED;
+               break;
+
+       case GREP_PATTERN_TYPE_FIXED:
+               opt->fixed = 1;
+               opt->pcre = 0;
+               opt->regflags &= ~REG_EXTENDED;
+               break;
+
+       case GREP_PATTERN_TYPE_PCRE:
+               opt->fixed = 0;
+               opt->pcre = 1;
+               opt->regflags &= ~REG_EXTENDED;
+               break;
+       }
+}
+
 static int grep_config(const char *var, const char *value, void *cb)
 {
        struct grep_opt *opt = cb;
@@ -270,12 +317,17 @@ static int grep_config(const char *var, const char *value, void *cb)
 
        if (!strcmp(var, "grep.extendedregexp")) {
                if (git_config_bool(var, value))
-                       opt->regflags |= REG_EXTENDED;
+                       opt->extended_regexp_option = 1;
                else
-                       opt->regflags &= ~REG_EXTENDED;
+                       opt->extended_regexp_option = 0;
                return 0;
        }
 
+       if (!strcmp(var, "grep.patterntype")) {
+               opt->pattern_type_option = parse_pattern_type_arg(var, value);
+               return 0;
+  }
+
        if (!strcmp(var, "grep.linenumber")) {
                opt->linenum = git_config_bool(var, value);
                return 0;
@@ -669,95 +721,88 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        int i;
        int dummy;
        int use_index = 1;
-       enum {
-               pattern_type_unspecified = 0,
-               pattern_type_bre,
-               pattern_type_ere,
-               pattern_type_fixed,
-               pattern_type_pcre,
-       };
-       int pattern_type = pattern_type_unspecified;
+       int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
 
        struct option options[] = {
                OPT_BOOLEAN(0, "cached", &cached,
-                       "search in index instead of in the work tree"),
+                       N_("search in index instead of in the work tree")),
                OPT_NEGBIT(0, "no-index", &use_index,
-                        "finds in contents not managed by git", 1),
+                        N_("find in contents not managed by git"), 1),
                OPT_BOOLEAN(0, "untracked", &untracked,
-                       "search in both tracked and untracked files"),
+                       N_("search in both tracked and untracked files")),
                OPT_SET_INT(0, "exclude-standard", &opt_exclude,
-                           "search also in ignored files", 1),
+                           N_("search also in ignored files"), 1),
                OPT_GROUP(""),
                OPT_BOOLEAN('v', "invert-match", &opt.invert,
-                       "show non-matching lines"),
+                       N_("show non-matching lines")),
                OPT_BOOLEAN('i', "ignore-case", &opt.ignore_case,
-                       "case insensitive matching"),
+                       N_("case insensitive matching")),
                OPT_BOOLEAN('w', "word-regexp", &opt.word_regexp,
-                       "match patterns only at word boundaries"),
+                       N_("match patterns only at word boundaries")),
                OPT_SET_INT('a', "text", &opt.binary,
-                       "process binary files as text", GREP_BINARY_TEXT),
+                       N_("process binary files as text"), GREP_BINARY_TEXT),
                OPT_SET_INT('I', NULL, &opt.binary,
-                       "don't match patterns in binary files",
+                       N_("don't match patterns in binary files"),
                        GREP_BINARY_NOMATCH),
-               { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth",
-                       "descend at most <depth> levels", PARSE_OPT_NONEG,
+               { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"),
+                       N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
                        NULL, 1 },
                OPT_GROUP(""),
-               OPT_SET_INT('E', "extended-regexp", &pattern_type,
-                           "use extended POSIX regular expressions",
-                           pattern_type_ere),
-               OPT_SET_INT('G', "basic-regexp", &pattern_type,
-                           "use basic POSIX regular expressions (default)",
-                           pattern_type_bre),
-               OPT_SET_INT('F', "fixed-strings", &pattern_type,
-                           "interpret patterns as fixed strings",
-                           pattern_type_fixed),
-               OPT_SET_INT('P', "perl-regexp", &pattern_type,
-                           "use Perl-compatible regular expressions",
-                           pattern_type_pcre),
+               OPT_SET_INT('E', "extended-regexp", &pattern_type_arg,
+                           N_("use extended POSIX regular expressions"),
+                           GREP_PATTERN_TYPE_ERE),
+               OPT_SET_INT('G', "basic-regexp", &pattern_type_arg,
+                           N_("use basic POSIX regular expressions (default)"),
+                           GREP_PATTERN_TYPE_BRE),
+               OPT_SET_INT('F', "fixed-strings", &pattern_type_arg,
+                           N_("interpret patterns as fixed strings"),
+                           GREP_PATTERN_TYPE_FIXED),
+               OPT_SET_INT('P', "perl-regexp", &pattern_type_arg,
+                           N_("use Perl-compatible regular expressions"),
+                           GREP_PATTERN_TYPE_PCRE),
                OPT_GROUP(""),
-               OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"),
-               OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
-               OPT_BIT('H', NULL, &opt.pathname, "show filenames", 1),
+               OPT_BOOLEAN('n', "line-number", &opt.linenum, N_("show line numbers")),
+               OPT_NEGBIT('h', NULL, &opt.pathname, N_("don't show filenames"), 1),
+               OPT_BIT('H', NULL, &opt.pathname, N_("show filenames"), 1),
                OPT_NEGBIT(0, "full-name", &opt.relative,
-                       "show filenames relative to top directory", 1),
+                       N_("show filenames relative to top directory"), 1),
                OPT_BOOLEAN('l', "files-with-matches", &opt.name_only,
-                       "show only filenames instead of matching lines"),
+                       N_("show only filenames instead of matching lines")),
                OPT_BOOLEAN(0, "name-only", &opt.name_only,
-                       "synonym for --files-with-matches"),
+                       N_("synonym for --files-with-matches")),
                OPT_BOOLEAN('L', "files-without-match",
                        &opt.unmatch_name_only,
-                       "show only the names of files without match"),
+                       N_("show only the names of files without match")),
                OPT_BOOLEAN('z', "null", &opt.null_following_name,
-                       "print NUL after filenames"),
+                       N_("print NUL after filenames")),
                OPT_BOOLEAN('c', "count", &opt.count,
-                       "show the number of matches instead of matching lines"),
-               OPT__COLOR(&opt.color, "highlight matches"),
+                       N_("show the number of matches instead of matching lines")),
+               OPT__COLOR(&opt.color, N_("highlight matches")),
                OPT_BOOLEAN(0, "break", &opt.file_break,
-                       "print empty line between matches from different files"),
+                       N_("print empty line between matches from different files")),
                OPT_BOOLEAN(0, "heading", &opt.heading,
-                       "show filename only once above matches from same file"),
+                       N_("show filename only once above matches from same file")),
                OPT_GROUP(""),
-               OPT_CALLBACK('C', "context", &opt, "n",
-                       "show <n> context lines before and after matches",
+               OPT_CALLBACK('C', "context", &opt, N_("n"),
+                       N_("show <n> context lines before and after matches"),
                        context_callback),
                OPT_INTEGER('B', "before-context", &opt.pre_context,
-                       "show <n> context lines before matches"),
+                       N_("show <n> context lines before matches")),
                OPT_INTEGER('A', "after-context", &opt.post_context,
-                       "show <n> context lines after matches"),
-               OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
+                       N_("show <n> context lines after matches")),
+               OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"),
                        context_callback),
                OPT_BOOLEAN('p', "show-function", &opt.funcname,
-                       "show a line with the function name before matches"),
+                       N_("show a line with the function name before matches")),
                OPT_BOOLEAN('W', "function-context", &opt.funcbody,
-                       "show the surrounding function"),
+                       N_("show the surrounding function")),
                OPT_GROUP(""),
-               OPT_CALLBACK('f', NULL, &opt, "file",
-                       "read patterns from file", file_callback),
-               { OPTION_CALLBACK, 'e', NULL, &opt, "pattern",
-                       "match <pattern>", PARSE_OPT_NONEG, pattern_callback },
+               OPT_CALLBACK('f', NULL, &opt, N_("file"),
+                       N_("read patterns from file"), file_callback),
+               { OPTION_CALLBACK, 'e', NULL, &opt, N_("pattern"),
+                       N_("match <pattern>"), PARSE_OPT_NONEG, pattern_callback },
                { OPTION_CALLBACK, 0, "and", &opt, NULL,
-                 "combine patterns specified with -e",
+                 N_("combine patterns specified with -e"),
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG, and_callback },
                OPT_BOOLEAN(0, "or", &dummy, ""),
                { OPTION_CALLBACK, 0, "not", &opt, NULL, "",
@@ -769,16 +814,16 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG | PARSE_OPT_NODASH,
                  close_callback },
                OPT__QUIET(&opt.status_only,
-                          "indicate hit with exit status without output"),
+                          N_("indicate hit with exit status without output")),
                OPT_BOOLEAN(0, "all-match", &opt.all_match,
-                       "show only matches from files that match all patterns"),
+                       N_("show only matches from files that match all patterns")),
                OPT_GROUP(""),
                { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
-                       "pager", "show matching files in the pager",
+                       N_("pager"), N_("show matching files in the pager"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
                OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored,
-                           "allow calling of grep(1) (ignored by this build)"),
-               { OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
+                           N_("allow calling of grep(1) (ignored by this build)")),
+               { OPTION_CALLBACK, 0, "help-all", &options, NULL, N_("show usage"),
                  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
                OPT_END()
        };
@@ -799,6 +844,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        opt.header_tail = &opt.header_list;
        opt.regflags = REG_NEWLINE;
        opt.max_depth = -1;
+       opt.pattern_type_option = GREP_PATTERN_TYPE_UNSPECIFIED;
+       opt.extended_regexp_option = 0;
 
        strcpy(opt.color_context, "");
        strcpy(opt.color_filename, "");
@@ -824,28 +871,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_KEEP_DASHDASH |
                             PARSE_OPT_STOP_AT_NON_OPTION |
                             PARSE_OPT_NO_INTERNAL_HELP);
-       switch (pattern_type) {
-       case pattern_type_fixed:
-               opt.fixed = 1;
-               opt.pcre = 0;
-               break;
-       case pattern_type_bre:
-               opt.fixed = 0;
-               opt.pcre = 0;
-               opt.regflags &= ~REG_EXTENDED;
-               break;
-       case pattern_type_ere:
-               opt.fixed = 0;
-               opt.pcre = 0;
-               opt.regflags |= REG_EXTENDED;
-               break;
-       case pattern_type_pcre:
-               opt.fixed = 0;
-               opt.pcre = 1;
-               break;
-       default:
-               break; /* nothing */
-       }
+
+       if (pattern_type_arg != GREP_PATTERN_TYPE_UNSPECIFIED)
+               grep_pattern_type_options(pattern_type_arg, &opt);
+       else if (opt.pattern_type_option != GREP_PATTERN_TYPE_UNSPECIFIED)
+               grep_pattern_type_options(opt.pattern_type_option, &opt);
+       else if (opt.extended_regexp_option)
+               grep_pattern_type_options(GREP_PATTERN_TYPE_ERE, &opt);
 
        if (use_index && !startup_info->have_repository)
                /* die the same way as if we did it at the beginning */
index 33911fd5e9325210dae63b3252fa3b56d42b545c..8d184f1a99120b1ebccac4f5e1d4fdb0a692b3c9 100644 (file)
@@ -57,8 +57,8 @@ static void hash_stdin_paths(const char *type, int write_objects)
 }
 
 static const char * const hash_object_usage[] = {
-       "git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...",
-       "git hash-object  --stdin-paths < <list-of-paths>",
+       N_("git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>..."),
+       N_("git hash-object  --stdin-paths < <list-of-paths>"),
        NULL
 };
 
@@ -69,12 +69,12 @@ static int stdin_paths;
 static const char *vpath;
 
 static const struct option hash_object_options[] = {
-       OPT_STRING('t', NULL, &type, "type", "object type"),
-       OPT_BOOLEAN('w', NULL, &write_object, "write the object into the object database"),
-       OPT_BOOLEAN( 0 , "stdin", &hashstdin, "read the object from stdin"),
-       OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, "read file names from stdin"),
-       OPT_BOOLEAN( 0 , "no-filters", &no_filters, "store file as is without filters"),
-       OPT_STRING( 0 , "path", &vpath, "file", "process file as it were from this path"),
+       OPT_STRING('t', NULL, &type, N_("type"), N_("object type")),
+       OPT_BOOLEAN('w', NULL, &write_object, N_("write the object into the object database")),
+       OPT_BOOLEAN( 0 , "stdin", &hashstdin, N_("read the object from stdin")),
+       OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")),
+       OPT_BOOLEAN( 0 , "no-filters", &no_filters, N_("store file as is without filters")),
+       OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")),
        OPT_END()
 };
 
index efea4f55e173a5ff2d7e1748316d6a94266e915a..bd86253d835fce671ed19676e5efeb59a6fc261d 100644 (file)
@@ -40,17 +40,17 @@ 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"),
-       OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
-       OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
+       OPT_BOOLEAN('a', "all", &show_all, N_("print all available commands")),
+       OPT_SET_INT('m', "man", &help_format, N_("show man page"), HELP_FORMAT_MAN),
+       OPT_SET_INT('w', "web", &help_format, N_("show manual in web browser"),
                        HELP_FORMAT_WEB),
-       OPT_SET_INT('i', "info", &help_format, "show info page",
+       OPT_SET_INT('i', "info", &help_format, N_("show info page"),
                        HELP_FORMAT_INFO),
        OPT_END(),
 };
 
 static const char * const builtin_help_usage[] = {
-       "git help [--all] [--man|--web|--info] [command]",
+       N_("git help [--all] [--man|--web|--info] [command]"),
        NULL
 };
 
index 953dd3004e285ce7aa0432f4fe0eacb0d0c2c887..43d364b8d5e5b0cb4b78be517e78d64c96da4304 100644 (file)
@@ -291,7 +291,7 @@ static void parse_pack_header(void)
        if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
                die(_("pack signature mismatch"));
        if (!pack_version_ok(hdr->hdr_version))
-               die("pack version %"PRIu32" unsupported",
+               die(_("pack version %"PRIu32" unsupported"),
                        ntohl(hdr->hdr_version));
 
        nr_objects = ntohl(hdr->hdr_entries);
@@ -1061,7 +1061,8 @@ static void resolve_deltas(void)
                        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));
+                               die(_("unable to create thread: %s"),
+                                   strerror(ret));
                }
                for (i = 0; i < nr_threads; i++)
                        pthread_join(thread_data[i].thread, NULL);
@@ -1108,7 +1109,7 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
                                   * sizeof(*objects));
                f = sha1fd(output_fd, curr_pack);
                fix_unresolved_deltas(f, nr_unresolved);
-               sprintf(msg, "completed with %d local objects",
+               sprintf(msg, _("completed with %d local objects"),
                        nr_objects - nr_objects_initial);
                stop_progress_msg(&progress, msg);
                sha1close(f, tail_sha1, 0);
@@ -1117,8 +1118,8 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
                                         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);
+                       die(_("Unexpected tail checksum for %s "
+                             "(disk corruption?)"), curr_pack);
        }
        if (nr_deltas != nr_resolved_deltas)
                die(Q_("pack has %d unresolved delta",
@@ -1327,17 +1328,17 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
        if (!strcmp(k, "pack.indexversion")) {
                opts->version = git_config_int(k, v);
                if (opts->version > 2)
-                       die("bad pack.indexversion=%"PRIu32, opts->version);
+                       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)",
+                       die(_("invalid number of threads specified (%d)"),
                            nr_threads);
 #ifdef NO_PTHREADS
                if (nr_threads != 1)
-                       warning("no threads support, ignoring %s", k);
+                       warning(_("no threads support, ignoring %s"), k);
                nr_threads = 1;
 #endif
                return 0;
@@ -1510,8 +1511,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                        usage(index_pack_usage);
 #ifdef NO_PTHREADS
                                if (nr_threads != 1)
-                                       warning("no threads support, "
-                                               "ignoring %s", arg);
+                                       warning(_("no threads support, "
+                                                 "ignoring %s"), arg);
                                nr_threads = 1;
 #endif
                        } else if (!prefixcmp(arg, "--pack_header=")) {
index 244fb7fc32e8263c9ab92ca10a440625f4c2944c..78aa3872dddbba8988b61c49996f9e63719bac75 100644 (file)
@@ -464,7 +464,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)
 }
 
 static const char *const init_db_usage[] = {
-       "git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]",
+       N_("git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]"),
        NULL
 };
 
@@ -482,17 +482,17 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        const char *template_dir = NULL;
        unsigned int flags = 0;
        const struct option init_db_options[] = {
-               OPT_STRING(0, "template", &template_dir, "template-directory",
-                               "directory from which templates will be used"),
+               OPT_STRING(0, "template", &template_dir, N_("template-directory"),
+                               N_("directory from which templates will be used")),
                OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
-                               "create a bare repository", 1),
+                               N_("create a bare repository"), 1),
                { OPTION_CALLBACK, 0, "shared", &init_shared_repository,
-                       "permissions",
-                       "specify that the git repository is to be shared amongst several users",
+                       N_("permissions"),
+                       N_("specify that the git repository is to be shared amongst several users"),
                        PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
-               OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
-               OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
-                          "separate git dir from working tree"),
+               OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
+               OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
+                          N_("separate git dir from working tree")),
                OPT_END()
        };
 
index ecc2793690496531546765516d80032a1cbf8844..09cf43e6d40efc13f6b7bda2094fca5bdc1bbc87 100644 (file)
@@ -34,8 +34,8 @@ static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
 static const char * const builtin_log_usage[] = {
-       "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
-       "   or: git show [options] <object>...",
+       N_("git log [<options>] [<since>..<until>] [[--] <path>...]\n")
+       N_("   or: git show [options] <object>..."),
        NULL
 };
 
@@ -97,9 +97,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
        int quiet = 0, source = 0;
 
        const struct option builtin_log_options[] = {
-               OPT_BOOLEAN(0, "quiet", &quiet, "suppress diff output"),
-               OPT_BOOLEAN(0, "source", &source, "show source"),
-               { OPTION_CALLBACK, 0, "decorate", NULL, NULL, "decorate options",
+               OPT_BOOLEAN(0, "quiet", &quiet, N_("suppress diff output")),
+               OPT_BOOLEAN(0, "source", &source, N_("show source")),
+               { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
                  PARSE_OPT_OPTARG, decorate_callback},
                OPT_END()
        };
@@ -109,9 +109,9 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
                             PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
                             PARSE_OPT_KEEP_DASHDASH);
 
-       argc = setup_revisions(argc, argv, rev, opt);
        if (quiet)
                rev->diffopt.output_format |= DIFF_FORMAT_NO_OUTPUT;
+       argc = setup_revisions(argc, argv, rev, opt);
 
        /* Any arguments at this point are not recognized */
        if (argc > 1)
@@ -456,7 +456,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
        init_revisions(&rev, prefix);
        rev.diff = 1;
        rev.always_show_header = 1;
-       rev.no_walk = 1;
+       rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
        rev.diffopt.stat_width = -1;    /* Scale to real terminal size */
 
        memset(&opt, 0, sizeof(opt));
@@ -696,7 +696,7 @@ static int reopen_stdout(struct commit *commit, const char *subject,
        return 0;
 }
 
-static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
+static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
 {
        struct rev_info check_rev;
        struct commit *commit;
@@ -717,7 +717,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
        init_patch_ids(ids);
 
        /* given a range a..b get all patch ids for b..a */
-       init_revisions(&check_rev, prefix);
+       init_revisions(&check_rev, rev->prefix);
+       check_rev.max_parents = 1;
        o1->flags ^= UNINTERESTING;
        o2->flags ^= UNINTERESTING;
        add_pending_object(&check_rev, o1, "o1");
@@ -726,10 +727,6 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
                die(_("revision walk setup failed"));
 
        while ((commit = get_revision(&check_rev)) != NULL) {
-               /* ignore merges */
-               if (commit->parents && commit->parents->next)
-                       continue;
-
                add_commit_patch_id(commit, ids);
        }
 
@@ -890,7 +887,7 @@ static const char *set_outdir(const char *prefix, const char *output_directory)
 }
 
 static const char * const builtin_format_patch_usage[] = {
-       "git format-patch [options] [<since> | <revision range>]",
+       N_("git format-patch [options] [<since> | <revision range>]"),
        NULL
 };
 
@@ -1063,61 +1060,61 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        char *branch_name = NULL;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
-                           "use [PATCH n/m] even with a single patch",
+                           N_("use [PATCH n/m] even with a single patch"),
                            PARSE_OPT_NOARG, numbered_callback },
                { OPTION_CALLBACK, 'N', "no-numbered", &numbered, NULL,
-                           "use [PATCH] even with multiple patches",
+                           N_("use [PATCH] even with multiple patches"),
                            PARSE_OPT_NOARG, no_numbered_callback },
-               OPT_BOOLEAN('s', "signoff", &do_signoff, "add Signed-off-by:"),
+               OPT_BOOLEAN('s', "signoff", &do_signoff, N_("add Signed-off-by:")),
                OPT_BOOLEAN(0, "stdout", &use_stdout,
-                           "print patches to standard out"),
+                           N_("print patches to standard out")),
                OPT_BOOLEAN(0, "cover-letter", &cover_letter,
-                           "generate a cover letter"),
+                           N_("generate a cover letter")),
                OPT_BOOLEAN(0, "numbered-files", &numbered_files,
-                           "use simple number sequence for output file names"),
-               OPT_STRING(0, "suffix", &fmt_patch_suffix, "sfx",
-                           "use <sfx> instead of '.patch'"),
+                           N_("use simple number sequence for output file names")),
+               OPT_STRING(0, "suffix", &fmt_patch_suffix, N_("sfx"),
+                           N_("use <sfx> instead of '.patch'")),
                OPT_INTEGER(0, "start-number", &start_number,
-                           "start numbering patches at <n> instead of 1"),
-               { OPTION_CALLBACK, 0, "subject-prefix", &rev, "prefix",
-                           "Use [<prefix>] instead of [PATCH]",
+                           N_("start numbering patches at <n> instead of 1")),
+               { OPTION_CALLBACK, 0, "subject-prefix", &rev, N_("prefix"),
+                           N_("Use [<prefix>] instead of [PATCH]"),
                            PARSE_OPT_NONEG, subject_prefix_callback },
                { OPTION_CALLBACK, 'o', "output-directory", &output_directory,
-                           "dir", "store resulting files in <dir>",
+                           N_("dir"), N_("store resulting files in <dir>"),
                            PARSE_OPT_NONEG, output_directory_callback },
                { OPTION_CALLBACK, 'k', "keep-subject", &rev, NULL,
-                           "don't strip/add [PATCH]",
+                           N_("don't strip/add [PATCH]"),
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
                OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
-                           "don't output binary diffs"),
+                           N_("don't output binary diffs")),
                OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
-                           "don't include a patch matching a commit upstream"),
+                           N_("don't include a patch matching a commit upstream")),
                { OPTION_BOOLEAN, 'p', "no-stat", &use_patch_format, NULL,
-                 "show patch format instead of default (patch + stat)",
+                 N_("show patch format instead of default (patch + stat)"),
                  PARSE_OPT_NONEG | PARSE_OPT_NOARG },
-               OPT_GROUP("Messaging"),
-               { OPTION_CALLBACK, 0, "add-header", NULL, "header",
-                           "add email header", 0, header_callback },
-               { OPTION_CALLBACK, 0, "to", NULL, "email", "add To: header",
+               OPT_GROUP(N_("Messaging")),
+               { OPTION_CALLBACK, 0, "add-header", NULL, N_("header"),
+                           N_("add email header"), 0, header_callback },
+               { OPTION_CALLBACK, 0, "to", NULL, N_("email"), N_("add To: header"),
                            0, to_callback },
-               { OPTION_CALLBACK, 0, "cc", NULL, "email", "add Cc: header",
+               { OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
                            0, cc_callback },
-               OPT_STRING(0, "in-reply-to", &in_reply_to, "message-id",
-                           "make first mail a reply to <message-id>"),
-               { OPTION_CALLBACK, 0, "attach", &rev, "boundary",
-                           "attach the patch", PARSE_OPT_OPTARG,
+               OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
+                           N_("make first mail a reply to <message-id>")),
+               { OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
+                           N_("attach the patch"), PARSE_OPT_OPTARG,
                            attach_callback },
-               { OPTION_CALLBACK, 0, "inline", &rev, "boundary",
-                           "inline the patch",
+               { OPTION_CALLBACK, 0, "inline", &rev, N_("boundary"),
+                           N_("inline the patch"),
                            PARSE_OPT_OPTARG | PARSE_OPT_NONEG,
                            inline_callback },
-               { OPTION_CALLBACK, 0, "thread", &thread, "style",
-                           "enable message threading, styles: shallow, deep",
+               { OPTION_CALLBACK, 0, "thread", &thread, N_("style"),
+                           N_("enable message threading, styles: shallow, deep"),
                            PARSE_OPT_OPTARG, thread_callback },
-               OPT_STRING(0, "signature", &signature, "signature",
-                           "add a signature"),
+               OPT_STRING(0, "signature", &signature, N_("signature"),
+                           N_("add a signature")),
                OPT_BOOLEAN(0, "quiet", &quiet,
-                           "don't print the patch filenames"),
+                           N_("don't print the patch filenames")),
                OPT_END()
        };
 
@@ -1306,7 +1303,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
                                return 0;
                }
-               get_patch_ids(&rev, &ids, prefix);
+               get_patch_ids(&rev, &ids);
        }
 
        if (!use_stdout)
@@ -1443,7 +1440,7 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
 }
 
 static const char * const cherry_usage[] = {
-       "git cherry [-v] [<upstream> [<head> [<limit>]]]",
+       N_("git cherry [-v] [<upstream> [<head> [<limit>]]]"),
        NULL
 };
 
@@ -1477,7 +1474,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
 
        struct option options[] = {
                OPT__ABBREV(&abbrev),
-               OPT__VERBOSE(&verbose, "be verbose"),
+               OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_END()
        };
 
@@ -1508,10 +1505,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
        }
 
        init_revisions(&revs, prefix);
-       revs.diff = 1;
-       revs.combine_merges = 0;
-       revs.ignore_merges = 1;
-       DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
+       revs.max_parents = 1;
 
        if (add_pending_commit(head, &revs, 0))
                die(_("Unknown commit %s"), head);
@@ -1525,7 +1519,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
                        return 0;
        }
 
-       get_patch_ids(&revs, &ids, prefix);
+       get_patch_ids(&revs, &ids);
 
        if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
                die(_("Unknown commit %s"), limit);
@@ -1534,10 +1528,6 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
        while ((commit = get_revision(&revs)) != NULL) {
-               /* ignore merges */
-               if (commit->parents && commit->parents->next)
-                       continue;
-
                commit_list_insert(commit, &list);
        }
 
index 31b3f2d9006e0f5703ca9fb37bea247012581c0e..b5434af0c87741f3160388cf86904c6e3f903527 100644 (file)
@@ -405,7 +405,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char
 }
 
 static const char * const ls_files_usage[] = {
-       "git ls-files [options] [<file>...]",
+       N_("git ls-files [options] [<file>...]"),
        NULL
 };
 
@@ -457,57 +457,57 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
        struct dir_struct dir;
        struct option builtin_ls_files_options[] = {
                { 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_BOOLEAN('t', NULL, &show_tag,
-                       "identify the file status with tags"),
+                       N_("identify the file status with tags")),
                OPT_BOOLEAN('v', NULL, &show_valid_bit,
-                       "use lowercase letters for 'assume unchanged' files"),
+                       N_("use lowercase letters for 'assume unchanged' files")),
                OPT_BOOLEAN('c', "cached", &show_cached,
-                       "show cached files in the output (default)"),
+                       N_("show cached files in the output (default)")),
                OPT_BOOLEAN('d', "deleted", &show_deleted,
-                       "show deleted files in the output"),
+                       N_("show deleted files in the output")),
                OPT_BOOLEAN('m', "modified", &show_modified,
-                       "show modified files in the output"),
+                       N_("show modified files in the output")),
                OPT_BOOLEAN('o', "others", &show_others,
-                       "show other files in the output"),
+                       N_("show other files in the output")),
                OPT_BIT('i', "ignored", &dir.flags,
-                       "show ignored files in the output",
+                       N_("show ignored files in the output"),
                        DIR_SHOW_IGNORED),
                OPT_BOOLEAN('s', "stage", &show_stage,
-                       "show staged contents' object name in the output"),
+                       N_("show staged contents' object name in the output")),
                OPT_BOOLEAN('k', "killed", &show_killed,
-                       "show files on the filesystem that need to be removed"),
+                       N_("show files on the filesystem that need to be removed")),
                OPT_BIT(0, "directory", &dir.flags,
-                       "show 'other' directories' name only",
+                       N_("show 'other' directories' name only"),
                        DIR_SHOW_OTHER_DIRECTORIES),
                OPT_NEGBIT(0, "empty-directory", &dir.flags,
-                       "don't show empty directories",
+                       N_("don't show empty directories"),
                        DIR_HIDE_EMPTY_DIRECTORIES),
                OPT_BOOLEAN('u', "unmerged", &show_unmerged,
-                       "show unmerged files in the output"),
+                       N_("show unmerged files in the output")),
                OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
-                           "show resolve-undo information"),
-               { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
-                       "skip files matching pattern",
+                           N_("show resolve-undo information")),
+               { OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], N_("pattern"),
+                       N_("skip files matching pattern"),
                        0, option_parse_exclude },
-               { OPTION_CALLBACK, 'X', "exclude-from", &dir, "file",
-                       "exclude patterns are read from <file>",
+               { OPTION_CALLBACK, 'X', "exclude-from", &dir, N_("file"),
+                       N_("exclude patterns are read from <file>"),
                        0, option_parse_exclude_from },
-               OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, "file",
-                       "read additional per-directory exclude patterns in <file>"),
+               OPT_STRING(0, "exclude-per-directory", &dir.exclude_per_dir, N_("file"),
+                       N_("read additional per-directory exclude patterns in <file>")),
                { OPTION_CALLBACK, 0, "exclude-standard", &dir, NULL,
-                       "add the standard git exclusions",
+                       N_("add the standard git exclusions"),
                        PARSE_OPT_NOARG, option_parse_exclude_standard },
                { OPTION_SET_INT, 0, "full-name", &prefix_len, NULL,
-                       "make the output relative to the project top directory",
+                       N_("make the output relative to the project top directory"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL },
                OPT_BOOLEAN(0, "error-unmatch", &error_unmatch,
-                       "if any <file> is not in the index, treat this as an error"),
-               OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
-                       "pretend that paths removed since <tree-ish> are still present"),
+                       N_("if any <file> is not in the index, treat this as an error")),
+               OPT_STRING(0, "with-tree", &with_tree, N_("tree-ish"),
+                       N_("pretend that paths removed since <tree-ish> are still present")),
                OPT__ABBREV(&abbrev),
-               OPT_BOOLEAN(0, "debug", &debug_mode, "show debugging data"),
+               OPT_BOOLEAN(0, "debug", &debug_mode, N_("show debugging data")),
                OPT_END()
        };
 
index 6b666e1e87017f41d2f53c50b157f280b3a5f282..235c17cc015acfb73358bc5ee5bde712fa2b0fa9 100644 (file)
@@ -24,7 +24,7 @@ static int chomp_prefix;
 static const char *ls_tree_prefix;
 
 static const  char * const ls_tree_usage[] = {
-       "git ls-tree [<options>] <tree-ish> [<path>...]",
+       N_("git ls-tree [<options>] <tree-ish> [<path>...]"),
        NULL
 };
 
@@ -122,25 +122,25 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
        struct tree *tree;
        int i, full_tree = 0;
        const struct option ls_tree_options[] = {
-               OPT_BIT('d', NULL, &ls_options, "only show trees",
+               OPT_BIT('d', NULL, &ls_options, N_("only show trees"),
                        LS_TREE_ONLY),
-               OPT_BIT('r', NULL, &ls_options, "recurse into subtrees",
+               OPT_BIT('r', NULL, &ls_options, N_("recurse into subtrees"),
                        LS_RECURSIVE),
-               OPT_BIT('t', NULL, &ls_options, "show trees when recursing",
+               OPT_BIT('t', NULL, &ls_options, N_("show trees when recursing"),
                        LS_SHOW_TREES),
                OPT_SET_INT('z', NULL, &line_termination,
-                           "terminate entries with NUL byte", 0),
-               OPT_BIT('l', "long", &ls_options, "include object size",
+                           N_("terminate entries with NUL byte"), 0),
+               OPT_BIT('l', "long", &ls_options, N_("include object size"),
                        LS_SHOW_SIZE),
-               OPT_BIT(0, "name-only", &ls_options, "list only filenames",
+               OPT_BIT(0, "name-only", &ls_options, N_("list only filenames"),
                        LS_NAME_ONLY),
-               OPT_BIT(0, "name-status", &ls_options, "list only filenames",
+               OPT_BIT(0, "name-status", &ls_options, N_("list only filenames"),
                        LS_NAME_ONLY),
                OPT_SET_INT(0, "full-name", &chomp_prefix,
-                           "use full path names", 0),
+                           N_("use full path names"), 0),
                OPT_BOOLEAN(0, "full-tree", &full_tree,
-                           "list entire tree; not just current directory "
-                           "(implies --full-name)"),
+                           N_("list entire tree; not just current directory "
+                              "(implies --full-name)")),
                OPT__ABBREV(&abbrev),
                OPT_END()
        };
index eaf9e157a3897c2442756911b906ff9d3ee2be90..dd4f925475fd2012822575a86e78dcd24c1ef8ef 100644 (file)
@@ -481,36 +481,12 @@ static struct strbuf *decode_b_segment(const struct strbuf *b_seg)
        return out;
 }
 
-/*
- * When there is no known charset, guess.
- *
- * Right now we assume that if the target is UTF-8 (the default),
- * and it already looks like UTF-8 (which includes US-ASCII as its
- * subset, of course) then that is what it is and there is nothing
- * to do.
- *
- * Otherwise, we default to assuming it is Latin1 for historical
- * reasons.
- */
-static const char *guess_charset(const struct strbuf *line, const char *target_charset)
-{
-       if (is_encoding_utf8(target_charset)) {
-               if (is_utf8(line->buf))
-                       return NULL;
-       }
-       return "ISO8859-1";
-}
-
 static void convert_to_utf8(struct strbuf *line, const char *charset)
 {
        char *out;
 
-       if (!charset || !*charset) {
-               charset = guess_charset(line, metainfo_charset);
-               if (!charset)
-                       return;
-       }
-
+       if (!charset || !*charset)
+               return;
        if (!strcasecmp(metainfo_charset, charset))
                return;
        out = reencode_string(line->buf, metainfo_charset, charset);
index 4f30f1b0c8b14e78e272ec54a86584d66501f754..1bc79910481e6ee04b470620da5609c1c6fbcac4 100644 (file)
@@ -23,9 +23,10 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 }
 
 static const char * const merge_base_usage[] = {
-       "git merge-base [-a|--all] <commit> <commit>...",
-       "git merge-base [-a|--all] --octopus <commit>...",
-       "git merge-base --independent <commit>...",
+       N_("git merge-base [-a|--all] <commit> <commit>..."),
+       N_("git merge-base [-a|--all] --octopus <commit>..."),
+       N_("git merge-base --independent <commit>..."),
+       N_("git merge-base --is-ancestor <commit> <commit>"),
        NULL
 };
 
@@ -70,6 +71,20 @@ static int handle_octopus(int count, const char **args, int reduce, int show_all
        return 0;
 }
 
+static int handle_is_ancestor(int argc, const char **argv)
+{
+       struct commit *one, *two;
+
+       if (argc != 2)
+               die("--is-ancestor takes exactly two commits");
+       one = get_commit_reference(argv[0]);
+       two = get_commit_reference(argv[1]);
+       if (in_merge_bases(one, two))
+               return 0;
+       else
+               return 1;
+}
+
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
 {
        struct commit **rev;
@@ -77,11 +92,14 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        int show_all = 0;
        int octopus = 0;
        int reduce = 0;
+       int is_ancestor = 0;
 
        struct option options[] = {
-               OPT_BOOLEAN('a', "all", &show_all, "output all common ancestors"),
-               OPT_BOOLEAN(0, "octopus", &octopus, "find ancestors for a single n-way merge"),
-               OPT_BOOLEAN(0, "independent", &reduce, "list revs not reachable from others"),
+               OPT_BOOLEAN('a', "all", &show_all, N_("output all common ancestors")),
+               OPT_BOOLEAN(0, "octopus", &octopus, N_("find ancestors for a single n-way merge")),
+               OPT_BOOLEAN(0, "independent", &reduce, N_("list revs not reachable from others")),
+               OPT_BOOLEAN(0, "is-ancestor", &is_ancestor,
+                           N_("is the first one ancestor of the other?")),
                OPT_END()
        };
 
@@ -89,6 +107,10 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, merge_base_usage, 0);
        if (!octopus && !reduce && argc < 2)
                usage_with_options(merge_base_usage, options);
+       if (is_ancestor && (show_all | octopus | reduce))
+               die("--is-ancestor cannot be used with other options");
+       if (is_ancestor)
+               return handle_is_ancestor(argc, argv);
        if (reduce && (show_all || octopus))
                die("--independent cannot be used with other options");
 
index 6f0efef43c5b93c87fa8f586319cd03c8018bd55..c0570f24072a8e29a13e00a2f4fe1b5054ebac7c 100644 (file)
@@ -5,7 +5,7 @@
 #include "parse-options.h"
 
 static const char *const merge_file_usage[] = {
-       "git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2",
+       N_("git merge-file [options] [-L name1 [-L orig [-L name2]]] file1 orig_file file2"),
        NULL
 };
 
@@ -30,19 +30,19 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        int quiet = 0;
        int prefixlen = 0;
        struct option options[] = {
-               OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
-               OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3),
-               OPT_SET_INT(0, "ours", &xmp.favor, "for conflicts, use our version",
+               OPT_BOOLEAN('p', "stdout", &to_stdout, N_("send results to standard output")),
+               OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
+               OPT_SET_INT(0, "ours", &xmp.favor, N_("for conflicts, use our version"),
                            XDL_MERGE_FAVOR_OURS),
-               OPT_SET_INT(0, "theirs", &xmp.favor, "for conflicts, use their version",
+               OPT_SET_INT(0, "theirs", &xmp.favor, N_("for conflicts, use their version"),
                            XDL_MERGE_FAVOR_THEIRS),
-               OPT_SET_INT(0, "union", &xmp.favor, "for conflicts, use a union version",
+               OPT_SET_INT(0, "union", &xmp.favor, N_("for conflicts, use a union version"),
                            XDL_MERGE_FAVOR_UNION),
                OPT_INTEGER(0, "marker-size", &xmp.marker_size,
-                           "for conflicts, use this marker size"),
-               OPT__QUIET(&quiet, "do not warn about conflicts"),
-               OPT_CALLBACK('L', NULL, names, "name",
-                            "set labels for file1/orig_file/file2", &label_cb),
+                           N_("for conflicts, use this marker size")),
+               OPT__QUIET(&quiet, N_("do not warn about conflicts")),
+               OPT_CALLBACK('L', NULL, names, N_("name"),
+                            N_("set labels for file1/orig_file/file2"), &label_cb),
                OPT_END(),
        };
 
index dd50a0c57b4d73d03cc84afd90594a56616d4953..0ec8f0d449e505a52166b560f43f30d8c30cfbaf 100644 (file)
@@ -40,9 +40,9 @@ struct strategy {
 };
 
 static const char * const builtin_merge_usage[] = {
-       "git merge [options] [<commit>...]",
-       "git merge [options] <msg> HEAD <commit>",
-       "git merge --abort",
+       N_("git merge [options] [<commit>...]"),
+       N_("git merge [options] <msg> HEAD <commit>"),
+       N_("git merge --abort"),
        NULL
 };
 
@@ -180,39 +180,39 @@ static int option_parse_n(const struct option *opt,
 
 static struct option builtin_merge_options[] = {
        { OPTION_CALLBACK, 'n', NULL, NULL, NULL,
-               "do not show a diffstat at the end of the merge",
+               N_("do not show a diffstat at the end of the merge"),
                PARSE_OPT_NOARG, option_parse_n },
        OPT_BOOLEAN(0, "stat", &show_diffstat,
-               "show a diffstat at the end of the merge"),
-       OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
-       { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
-         "add (at most <n>) entries from shortlog to merge commit message",
+               N_("show a diffstat at the end of the merge")),
+       OPT_BOOLEAN(0, "summary", &show_diffstat, N_("(synonym to --stat)")),
+       { OPTION_INTEGER, 0, "log", &shortlog_len, N_("n"),
+         N_("add (at most <n>) entries from shortlog to merge commit message"),
          PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
        OPT_BOOLEAN(0, "squash", &squash,
-               "create a single commit instead of doing a merge"),
+               N_("create a single commit instead of doing a merge")),
        OPT_BOOLEAN(0, "commit", &option_commit,
-               "perform a commit if the merge succeeds (default)"),
+               N_("perform a commit if the merge succeeds (default)")),
        OPT_BOOL('e', "edit", &option_edit,
-               "edit message before committing"),
+               N_("edit message before committing")),
        OPT_BOOLEAN(0, "ff", &allow_fast_forward,
-               "allow fast-forward (default)"),
+               N_("allow fast-forward (default)")),
        OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
-               "abort if fast-forward is not possible"),
+               N_("abort if fast-forward is not possible")),
        OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
-       OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
-               "merge strategy to use", option_parse_strategy),
-       OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
-               "option for selected merge strategy", option_parse_x),
-       OPT_CALLBACK('m', "message", &merge_msg, "message",
-               "merge commit message (for a non-fast-forward merge)",
+       OPT_CALLBACK('s', "strategy", &use_strategies, N_("strategy"),
+               N_("merge strategy to use"), option_parse_strategy),
+       OPT_CALLBACK('X', "strategy-option", &xopts, N_("option=value"),
+               N_("option for selected merge strategy"), option_parse_x),
+       OPT_CALLBACK('m', "message", &merge_msg, N_("message"),
+               N_("merge commit message (for a non-fast-forward merge)"),
                option_parse_message),
        OPT__VERBOSITY(&verbosity),
        OPT_BOOLEAN(0, "abort", &abort_current_merge,
-               "abort the current in-progress merge"),
-       OPT_SET_INT(0, "progress", &show_progress, "force progress reporting", 1),
-       { OPTION_STRING, 'S', "gpg-sign", &sign_commit, "key id",
-         "GPG sign commit", PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
-       OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, "update ignored files (default)"),
+               N_("abort the current in-progress merge")),
+       OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
+       { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
+         N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
+       OPT_BOOLEAN(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
        OPT_END()
 };
 
@@ -404,8 +404,7 @@ static void finish(struct commit *head_commit,
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
-               if (diff_setup_done(&opts) < 0)
-                       die(_("diff_setup_done failed"));
+               diff_setup_done(&opts);
                diff_tree_sha1(head, new_head, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
index 4ae1c412d47bd581f22ec8b821a1111224dddd28..f92ba404abd5cea0cde344936f697e46c1a46045 100644 (file)
@@ -64,7 +64,7 @@ static void write_tree(unsigned char *sha1)
 }
 
 static const char *mktree_usage[] = {
-       "git mktree [-z] [--missing] [--batch]",
+       N_("git mktree [-z] [--missing] [--batch]"),
        NULL
 };
 
@@ -150,9 +150,9 @@ int cmd_mktree(int ac, const char **av, const char *prefix)
        int got_eof = 0;
 
        const struct option option[] = {
-               OPT_SET_INT('z', NULL, &line_termination, "input is NUL terminated", '\0'),
-               OPT_SET_INT( 0 , "missing", &allow_missing, "allow missing objects", 1),
-               OPT_SET_INT( 0 , "batch", &is_batch_mode, "allow creation of more than one tree", 1),
+               OPT_SET_INT('z', NULL, &line_termination, N_("input is NUL terminated"), '\0'),
+               OPT_SET_INT( 0 , "missing", &allow_missing, N_("allow missing objects"), 1),
+               OPT_SET_INT( 0 , "batch", &is_batch_mode, N_("allow creation of more than one tree"), 1),
                OPT_END()
        };
 
index 2a144b011caa8ecb70f55976bdec60cae89fad9e..034fec92a11d79f28d80171ca86394817084ee09 100644 (file)
@@ -11,7 +11,7 @@
 #include "parse-options.h"
 
 static const char * const builtin_mv_usage[] = {
-       "git mv [options] <source>... <destination>",
+       N_("git mv [options] <source>... <destination>"),
        NULL
 };
 
@@ -59,10 +59,10 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        int i, newfd;
        int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
        struct option builtin_mv_options[] = {
-               OPT__VERBOSE(&verbose, "be verbose"),
-               OPT__DRY_RUN(&show_only, "dry run"),
-               OPT__FORCE(&force, "force move/rename even if target exists"),
-               OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
+               OPT__VERBOSE(&verbose, N_("be verbose")),
+               OPT__DRY_RUN(&show_only, N_("dry run")),
+               OPT__FORCE(&force, N_("force move/rename even if target exists")),
+               OPT_BOOLEAN('k', NULL, &ignore_errors, N_("skip move/rename errors")),
                OPT_END(),
        };
        const char **source, **destination, **dest_path;
index 1b374583c2751801dfae1d0d1861d28f81c7ad7b..62382479749909d449b718fc2f3bef36906caa4b 100644 (file)
@@ -172,9 +172,9 @@ static void show_name(const struct object *obj,
 }
 
 static char const * const name_rev_usage[] = {
-       "git name-rev [options] <commit>...",
-       "git name-rev [options] --all",
-       "git name-rev [options] --stdin",
+       N_("git name-rev [options] <commit>..."),
+       N_("git name-rev [options] --all"),
+       N_("git name-rev [options] --stdin"),
        NULL
 };
 
@@ -226,16 +226,16 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
        int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
-               OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"),
-               OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"),
-               OPT_STRING(0, "refs", &data.ref_filter, "pattern",
-                                  "only use refs matching <pattern>"),
+               OPT_BOOLEAN(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
+               OPT_BOOLEAN(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
+               OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"),
+                                  N_("only use refs matching <pattern>")),
                OPT_GROUP(""),
-               OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"),
-               OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"),
-               OPT_BOOLEAN(0, "undefined", &allow_undefined, "allow to print `undefined` names"),
+               OPT_BOOLEAN(0, "all", &all, N_("list all commits reachable from all refs")),
+               OPT_BOOLEAN(0, "stdin", &transform_stdin, N_("read from stdin")),
+               OPT_BOOLEAN(0, "undefined", &allow_undefined, N_("allow to print `undefined` names")),
                OPT_BOOLEAN(0, "always",     &always,
-                          "show abbreviated commit object as fallback"),
+                          N_("show abbreviated commit object as fallback")),
                OPT_END(),
        };
 
index 3644d140ece9f2cfc6381e1a60c5b4fc8f645e2a..554c80167c31d4d42aefe2f02feacffef9424844 100644 (file)
 #include "notes-merge.h"
 
 static const char * const git_notes_usage[] = {
-       "git notes [--ref <notes_ref>] [list [<object>]]",
-       "git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
-       "git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>",
-       "git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]",
-       "git notes [--ref <notes_ref>] edit [<object>]",
-       "git notes [--ref <notes_ref>] show [<object>]",
-       "git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>",
-       "git notes merge --commit [-v | -q]",
-       "git notes merge --abort [-v | -q]",
-       "git notes [--ref <notes_ref>] remove [<object>...]",
-       "git notes [--ref <notes_ref>] prune [-n | -v]",
-       "git notes [--ref <notes_ref>] get-ref",
+       N_("git notes [--ref <notes_ref>] [list [<object>]]"),
+       N_("git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+       N_("git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"),
+       N_("git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+       N_("git notes [--ref <notes_ref>] edit [<object>]"),
+       N_("git notes [--ref <notes_ref>] show [<object>]"),
+       N_("git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"),
+       N_("git notes merge --commit [-v | -q]"),
+       N_("git notes merge --abort [-v | -q]"),
+       N_("git notes [--ref <notes_ref>] remove [<object>...]"),
+       N_("git notes [--ref <notes_ref>] prune [-n | -v]"),
+       N_("git notes [--ref <notes_ref>] get-ref"),
        NULL
 };
 
 static const char * const git_notes_list_usage[] = {
-       "git notes [list [<object>]]",
+       N_("git notes [list [<object>]]"),
        NULL
 };
 
 static const char * const git_notes_add_usage[] = {
-       "git notes add [<options>] [<object>]",
+       N_("git notes add [<options>] [<object>]"),
        NULL
 };
 
 static const char * const git_notes_copy_usage[] = {
-       "git notes copy [<options>] <from-object> <to-object>",
-       "git notes copy --stdin [<from-object> <to-object>]...",
+       N_("git notes copy [<options>] <from-object> <to-object>"),
+       N_("git notes copy --stdin [<from-object> <to-object>]..."),
        NULL
 };
 
 static const char * const git_notes_append_usage[] = {
-       "git notes append [<options>] [<object>]",
+       N_("git notes append [<options>] [<object>]"),
        NULL
 };
 
 static const char * const git_notes_edit_usage[] = {
-       "git notes edit [<object>]",
+       N_("git notes edit [<object>]"),
        NULL
 };
 
 static const char * const git_notes_show_usage[] = {
-       "git notes show [<object>]",
+       N_("git notes show [<object>]"),
        NULL
 };
 
 static const char * const git_notes_merge_usage[] = {
-       "git notes merge [<options>] <notes_ref>",
-       "git notes merge --commit [<options>]",
-       "git notes merge --abort [<options>]",
+       N_("git notes merge [<options>] <notes_ref>"),
+       N_("git notes merge --commit [<options>]"),
+       N_("git notes merge --abort [<options>]"),
        NULL
 };
 
 static const char * const git_notes_remove_usage[] = {
-       "git notes remove [<object>]",
+       N_("git notes remove [<object>]"),
        NULL
 };
 
 static const char * const git_notes_prune_usage[] = {
-       "git notes prune [<options>]",
+       N_("git notes prune [<options>]"),
        NULL
 };
 
 static const char * const git_notes_get_ref_usage[] = {
-       "git notes get-ref",
+       N_("git notes get-ref"),
        NULL
 };
 
@@ -531,19 +531,19 @@ static int add(int argc, const char **argv, const char *prefix)
        const unsigned char *note;
        struct msg_arg msg = { 0, 0, STRBUF_INIT };
        struct option options[] = {
-               { OPTION_CALLBACK, 'm', "message", &msg, "msg",
-                       "note contents as a string", PARSE_OPT_NONEG,
+               { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
+                       N_("note contents as a string"), PARSE_OPT_NONEG,
                        parse_msg_arg},
-               { OPTION_CALLBACK, 'F', "file", &msg, "file",
-                       "note contents in a file", PARSE_OPT_NONEG,
+               { OPTION_CALLBACK, 'F', "file", &msg, N_("file"),
+                       N_("note contents in a file"), PARSE_OPT_NONEG,
                        parse_file_arg},
-               { OPTION_CALLBACK, 'c', "reedit-message", &msg, "object",
-                       "reuse and edit specified note object", PARSE_OPT_NONEG,
+               { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"),
+                       N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
                        parse_reedit_arg},
-               { OPTION_CALLBACK, 'C', "reuse-message", &msg, "object",
-                       "reuse specified note object", PARSE_OPT_NONEG,
+               { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"),
+                       N_("reuse specified note object"), PARSE_OPT_NONEG,
                        parse_reuse_arg},
-               OPT__FORCE(&force, "replace existing notes"),
+               OPT__FORCE(&force, N_("replace existing notes")),
                OPT_END()
        };
 
@@ -611,11 +611,11 @@ static int copy(int argc, const char **argv, const char *prefix)
        struct notes_tree *t;
        const char *rewrite_cmd = NULL;
        struct option options[] = {
-               OPT__FORCE(&force, "replace existing notes"),
-               OPT_BOOLEAN(0, "stdin", &from_stdin, "read objects from stdin"),
-               OPT_STRING(0, "for-rewrite", &rewrite_cmd, "command",
-                          "load rewriting config for <command> (implies "
-                          "--stdin)"),
+               OPT__FORCE(&force, N_("replace existing notes")),
+               OPT_BOOLEAN(0, "stdin", &from_stdin, N_("read objects from stdin")),
+               OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"),
+                          N_("load rewriting config for <command> (implies "
+                             "--stdin)")),
                OPT_END()
        };
 
@@ -688,17 +688,17 @@ static int append_edit(int argc, const char **argv, const char *prefix)
        const char * const *usage;
        struct msg_arg msg = { 0, 0, STRBUF_INIT };
        struct option options[] = {
-               { OPTION_CALLBACK, 'm', "message", &msg, "msg",
-                       "note contents as a string", PARSE_OPT_NONEG,
+               { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
+                       N_("note contents as a string"), PARSE_OPT_NONEG,
                        parse_msg_arg},
-               { OPTION_CALLBACK, 'F', "file", &msg, "file",
-                       "note contents in a file", PARSE_OPT_NONEG,
+               { OPTION_CALLBACK, 'F', "file", &msg, N_("file"),
+                       N_("note contents in a file"), PARSE_OPT_NONEG,
                        parse_file_arg},
-               { OPTION_CALLBACK, 'c', "reedit-message", &msg, "object",
-                       "reuse and edit specified note object", PARSE_OPT_NONEG,
+               { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"),
+                       N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
                        parse_reedit_arg},
-               { OPTION_CALLBACK, 'C', "reuse-message", &msg, "object",
-                       "reuse specified note object", PARSE_OPT_NONEG,
+               { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"),
+                       N_("reuse specified note object"), PARSE_OPT_NONEG,
                        parse_reuse_arg},
                OPT_END()
        };
@@ -861,19 +861,19 @@ static int merge(int argc, const char **argv, const char *prefix)
        int verbosity = 0, result;
        const char *strategy = NULL;
        struct option options[] = {
-               OPT_GROUP("General options"),
+               OPT_GROUP(N_("General options")),
                OPT__VERBOSITY(&verbosity),
-               OPT_GROUP("Merge options"),
-               OPT_STRING('s', "strategy", &strategy, "strategy",
-                          "resolve notes conflicts using the given strategy "
-                          "(manual/ours/theirs/union/cat_sort_uniq)"),
-               OPT_GROUP("Committing unmerged notes"),
+               OPT_GROUP(N_("Merge options")),
+               OPT_STRING('s', "strategy", &strategy, N_("strategy"),
+                          N_("resolve notes conflicts using the given strategy "
+                             "(manual/ours/theirs/union/cat_sort_uniq)")),
+               OPT_GROUP(N_("Committing unmerged notes")),
                { OPTION_BOOLEAN, 0, "commit", &do_commit, NULL,
-                       "finalize notes merge by committing unmerged notes",
+                       N_("finalize notes merge by committing unmerged notes"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG },
-               OPT_GROUP("Aborting notes merge resolution"),
+               OPT_GROUP(N_("Aborting notes merge resolution")),
                { OPTION_BOOLEAN, 0, "abort", &do_abort, NULL,
-                       "abort notes merge",
+                       N_("abort notes merge"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG },
                OPT_END()
        };
@@ -980,10 +980,10 @@ static int remove_cmd(int argc, const char **argv, const char *prefix)
        int from_stdin = 0;
        struct option options[] = {
                OPT_BIT(0, "ignore-missing", &flag,
-                       "attempt to remove non-existent note is not an error",
+                       N_("attempt to remove non-existent note is not an error"),
                        IGNORE_MISSING),
                OPT_BOOLEAN(0, "stdin", &from_stdin,
-                           "read object names from the standard input"),
+                           N_("read object names from the standard input")),
                OPT_END()
        };
        struct notes_tree *t;
@@ -1064,8 +1064,8 @@ int cmd_notes(int argc, const char **argv, const char *prefix)
        int result;
        const char *override_notes_ref = NULL;
        struct option options[] = {
-               OPT_STRING(0, "ref", &override_notes_ref, "notes_ref",
-                          "use notes from <notes_ref>"),
+               OPT_STRING(0, "ref", &override_notes_ref, N_("notes_ref"),
+                          N_("use notes from <notes_ref>")),
                OPT_END()
        };
 
index 782e7d0c38aa939a7db03266c3e6ff4993e4d2bd..5e140640947cd9c38da00a01d8db9f036e8babd7 100644 (file)
@@ -20,8 +20,8 @@
 #include "thread-utils.h"
 
 static const char *pack_usage[] = {
-       "git pack-objects --stdout [options...] [< ref-list | < object-list]",
-       "git pack-objects [options...] base-name [< ref-list | < object-list]",
+       N_("git pack-objects --stdout [options...] [< ref-list | < object-list]"),
+       N_("git pack-objects [options...] base-name [< ref-list | < object-list]"),
        NULL
 };
 
@@ -2445,67 +2445,67 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
        struct option pack_objects_options[] = {
                OPT_SET_INT('q', "quiet", &progress,
-                           "do not show progress meter", 0),
+                           N_("do not show progress meter"), 0),
                OPT_SET_INT(0, "progress", &progress,
-                           "show progress meter", 1),
+                           N_("show progress meter"), 1),
                OPT_SET_INT(0, "all-progress", &progress,
-                           "show progress meter during object writing phase", 2),
+                           N_("show progress meter during object writing phase"), 2),
                OPT_BOOL(0, "all-progress-implied",
                         &all_progress_implied,
-                        "similar to --all-progress when progress meter is shown"),
-               { OPTION_CALLBACK, 0, "index-version", NULL, "version[,offset]",
-                 "write the pack index file in the specified idx format version",
+                        N_("similar to --all-progress when progress meter is shown")),
+               { OPTION_CALLBACK, 0, "index-version", NULL, N_("version[,offset]"),
+                 N_("write the pack index file in the specified idx format version"),
                  0, option_parse_index_version },
                OPT_ULONG(0, "max-pack-size", &pack_size_limit,
-                         "maximum size of each output pack file"),
+                         N_("maximum size of each output pack file")),
                OPT_BOOL(0, "local", &local,
-                        "ignore borrowed objects from alternate object store"),
+                        N_("ignore borrowed objects from alternate object store")),
                OPT_BOOL(0, "incremental", &incremental,
-                        "ignore packed objects"),
+                        N_("ignore packed objects")),
                OPT_INTEGER(0, "window", &window,
-                           "limit pack window by objects"),
+                           N_("limit pack window by objects")),
                OPT_ULONG(0, "window-memory", &window_memory_limit,
-                         "limit pack window by memory in addition to object limit"),
+                         N_("limit pack window by memory in addition to object limit")),
                OPT_INTEGER(0, "depth", &depth,
-                           "maximum length of delta chain allowed in the resulting pack"),
+                           N_("maximum length of delta chain allowed in the resulting pack")),
                OPT_BOOL(0, "reuse-delta", &reuse_delta,
-                        "reuse existing deltas"),
+                        N_("reuse existing deltas")),
                OPT_BOOL(0, "reuse-object", &reuse_object,
-                        "reuse existing objects"),
+                        N_("reuse existing objects")),
                OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta,
-                        "use OFS_DELTA objects"),
+                        N_("use OFS_DELTA objects")),
                OPT_INTEGER(0, "threads", &delta_search_threads,
-                           "use threads when searching for best delta matches"),
+                           N_("use threads when searching for best delta matches")),
                OPT_BOOL(0, "non-empty", &non_empty,
-                        "do not create an empty pack output"),
+                        N_("do not create an empty pack output")),
                OPT_BOOL(0, "revs", &use_internal_rev_list,
-                        "read revision arguments from standard input"),
+                        N_("read revision arguments from standard input")),
                { OPTION_SET_INT, 0, "unpacked", &rev_list_unpacked, NULL,
-                 "limit the objects to those that are not yet packed",
+                 N_("limit the objects to those that are not yet packed"),
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
                { OPTION_SET_INT, 0, "all", &rev_list_all, NULL,
-                 "include objects reachable from any reference",
+                 N_("include objects reachable from any reference"),
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
                { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL,
-                 "include objects referred by reflog entries",
+                 N_("include objects referred by reflog entries"),
                  PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
                OPT_BOOL(0, "stdout", &pack_to_stdout,
-                        "output pack to stdout"),
+                        N_("output pack to stdout")),
                OPT_BOOL(0, "include-tag", &include_tag,
-                        "include tag objects that refer to objects to be packed"),
+                        N_("include tag objects that refer to objects to be packed")),
                OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
-                        "keep unreachable objects"),
-               { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, "time",
-                 "unpack unreachable objects newer than <time>",
+                        N_("keep unreachable objects")),
+               { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, N_("time"),
+                 N_("unpack unreachable objects newer than <time>"),
                  PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
                OPT_BOOL(0, "thin", &thin,
-                        "create thin packs"),
+                        N_("create thin packs")),
                OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep,
-                        "ignore packs that have companion .keep file"),
+                        N_("ignore packs that have companion .keep file")),
                OPT_INTEGER(0, "compression", &pack_compression_level,
-                           "pack compression level"),
+                           N_("pack compression level")),
                OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
-                           "do not hide commits by grafts", 0),
+                           N_("do not hide commits by grafts"), 0),
                OPT_END(),
        };
 
index 39a9d89fbdf322a8ef42a62e41ac36af934ff638..b5a0f88eb8c5641b41bfe95bd342fb180e7fadad 100644 (file)
@@ -3,7 +3,7 @@
 #include "pack-refs.h"
 
 static char const * const pack_refs_usage[] = {
-       "git pack-refs [options]",
+       N_("git pack-refs [options]"),
        NULL
 };
 
@@ -11,8 +11,8 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
 {
        unsigned int flags = PACK_REFS_PRUNE;
        struct option opts[] = {
-               OPT_BIT(0, "all",   &flags, "pack everything", PACK_REFS_ALL),
-               OPT_BIT(0, "prune", &flags, "prune loose refs (default)", PACK_REFS_PRUNE),
+               OPT_BIT(0, "all",   &flags, N_("pack everything"), PACK_REFS_ALL),
+               OPT_BIT(0, "prune", &flags, N_("prune loose refs (default)"), PACK_REFS_PRUNE),
                OPT_END(),
        };
        if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
index b58a2e1eb28d0eb94cc6863c64af7e0ac816caea..83382c1fe13e1f99aad4736993c08877c989a748 100644 (file)
@@ -4,7 +4,7 @@
 #include "parse-options.h"
 
 static const char * const prune_packed_usage[] = {
-       "git prune-packed [-n|--dry-run] [-q|--quiet]",
+       N_("git prune-packed [-n|--dry-run] [-q|--quiet]"),
        NULL
 };
 
@@ -73,8 +73,8 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)
 {
        int opts = isatty(2) ? VERBOSE : 0;
        const struct option prune_packed_options[] = {
-               OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN),
-               OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE),
+               OPT_BIT('n', "dry-run", &opts, N_("dry run"), DRY_RUN),
+               OPT_NEGBIT('q', "quiet", &opts, N_("be quiet"), VERBOSE),
                OPT_END()
        };
 
index b99b635e44f6145395b5c761505099c0a0f5b6cc..9a03d24dadaaba8bc1fc0c778dbfe33b1651607e 100644 (file)
@@ -9,7 +9,7 @@
 #include "dir.h"
 
 static const char * const prune_usage[] = {
-       "git prune [-n] [-v] [--expire <time>] [--] [<head>...]",
+       N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"),
        NULL
 };
 static int show_only;
@@ -25,7 +25,8 @@ static int prune_tmp_object(const char *path, const char *filename)
                return error("Could not stat '%s'", fullpath);
        if (st.st_mtime > expire)
                return 0;
-       printf("Removing stale temporary file %s\n", fullpath);
+       if (show_only || verbose)
+               printf("Removing stale temporary file %s\n", fullpath);
        if (!show_only)
                unlink_or_warn(fullpath);
        return 0;
@@ -128,11 +129,11 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        struct rev_info revs;
        struct progress *progress = NULL;
        const struct option options[] = {
-               OPT__DRY_RUN(&show_only, "do not remove, show only"),
-               OPT__VERBOSE(&verbose, "report pruned objects"),
-               OPT_BOOL(0, "progress", &show_progress, "show progress"),
+               OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
+               OPT__VERBOSE(&verbose, N_("report pruned objects")),
+               OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
                OPT_DATE(0, "expire", &expire,
-                        "expire objects older than <time>"),
+                        N_("expire objects older than <time>")),
                OPT_END()
        };
        char *s;
index fdfcc6c71607a7412666b7cc647db98e1b85c8c1..db9ba30b08c221ac2290b1ffc939713fe378e666 100644 (file)
@@ -11,7 +11,7 @@
 #include "submodule.h"
 
 static const char * const push_usage[] = {
-       "git push [<options>] [<repository> [<refspec>...]]",
+       N_("git push [<options>] [<repository> [<refspec>...]]"),
        NULL,
 };
 
@@ -147,12 +147,37 @@ static void setup_push_upstream(struct remote *remote, int simple)
        add_refspec(refspec.buf);
 }
 
+static char warn_unspecified_push_default_msg[] =
+N_("push.default is unset; its implicit value is changing in\n"
+   "Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
+   "and maintain the current behavior after the default changes, use:\n"
+   "\n"
+   "  git config --global push.default matching\n"
+   "\n"
+   "To squelch this message and adopt the new behavior now, use:\n"
+   "\n"
+   "  git config --global push.default simple\n"
+   "\n"
+   "See 'git help config' and search for 'push.default' for further information.\n"
+   "(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode\n"
+   "'current' instead of 'simple' if you sometimes use older versions of Git)");
+
+static void warn_unspecified_push_default_configuration(void)
+{
+       static int warn_once;
+
+       if (warn_once++)
+               return;
+       warning("%s\n", _(warn_unspecified_push_default_msg));
+}
+
 static void setup_default_push_refspecs(struct remote *remote)
 {
        switch (push_default) {
        default:
        case PUSH_DEFAULT_UNSPECIFIED:
                default_matching_used = 1;
+               warn_unspecified_push_default_configuration();
                /* fallthru */
        case PUSH_DEFAULT_MATCHING:
                add_refspec(":");
@@ -186,8 +211,8 @@ static const char message_advice_pull_before_push[] =
 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.");
+          "specify branches to push or set the 'push.default' configuration variable\n"
+          "to 'simple', '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"
@@ -354,25 +379,25 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        const char *repo = NULL;        /* default repository */
        struct option options[] = {
                OPT__VERBOSITY(&verbosity),
-               OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
-               OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
-               OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
+               OPT_STRING( 0 , "repo", &repo, N_("repository"), N_("repository")),
+               OPT_BIT( 0 , "all", &flags, N_("push all refs"), TRANSPORT_PUSH_ALL),
+               OPT_BIT( 0 , "mirror", &flags, N_("mirror all refs"),
                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
-               OPT_BOOLEAN( 0, "delete", &deleterefs, "delete refs"),
-               OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror)"),
-               OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
-               OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
-               OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
-               { OPTION_CALLBACK, 0, "recurse-submodules", &flags, "check",
-                       "controls recursive pushing of submodules",
+               OPT_BOOLEAN( 0, "delete", &deleterefs, N_("delete refs")),
+               OPT_BOOLEAN( 0 , "tags", &tags, N_("push tags (can't be used with --all or --mirror)")),
+               OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
+               OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
+               OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
+               { OPTION_CALLBACK, 0, "recurse-submodules", &flags, N_("check"),
+                       N_("control recursive pushing of submodules"),
                        PARSE_OPT_OPTARG, option_parse_recurse_submodules },
-               OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
-               OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
-               OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
-               OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
+               OPT_BOOLEAN( 0 , "thin", &thin, N_("use thin pack")),
+               OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", N_("receive pack program")),
+               OPT_STRING( 0 , "exec", &receivepack, "receive-pack", N_("receive pack program")),
+               OPT_BIT('u', "set-upstream", &flags, N_("set upstream for git pull/status"),
                        TRANSPORT_PUSH_SET_UPSTREAM),
-               OPT_BOOL(0, "progress", &progress, "force progress reporting"),
-               OPT_BIT(0, "prune", &flags, "prune locally removed refs",
+               OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
+               OPT_BIT(0, "prune", &flags, N_("prune locally removed refs"),
                        TRANSPORT_PUSH_PRUNE),
                OPT_END()
        };
index df6c4c8819e7903f34cd6dc6bb0078a6cddac310..042ac1b84f22257d1a8a4d32d34124b6dc372f16 100644 (file)
@@ -33,7 +33,7 @@ static int list_tree(unsigned char *sha1)
 }
 
 static const char * const read_tree_usage[] = {
-       "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])",
+       N_("git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])"),
        NULL
 };
 
@@ -104,37 +104,37 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        struct unpack_trees_options opts;
        int prefix_set = 0;
        const struct option read_tree_options[] = {
-               { OPTION_CALLBACK, 0, "index-output", NULL, "file",
-                 "write resulting index to <file>",
+               { OPTION_CALLBACK, 0, "index-output", NULL, N_("file"),
+                 N_("write resulting index to <file>"),
                  PARSE_OPT_NONEG, index_output_cb },
                OPT_SET_INT(0, "empty", &read_empty,
-                           "only empty the index", 1),
-               OPT__VERBOSE(&opts.verbose_update, "be verbose"),
-               OPT_GROUP("Merging"),
+                           N_("only empty the index"), 1),
+               OPT__VERBOSE(&opts.verbose_update, N_("be verbose")),
+               OPT_GROUP(N_("Merging")),
                OPT_SET_INT('m', NULL, &opts.merge,
-                           "perform a merge in addition to a read", 1),
+                           N_("perform a merge in addition to a read"), 1),
                OPT_SET_INT(0, "trivial", &opts.trivial_merges_only,
-                           "3-way merge if no file level merging required", 1),
+                           N_("3-way merge if no file level merging required"), 1),
                OPT_SET_INT(0, "aggressive", &opts.aggressive,
-                           "3-way merge in presence of adds and removes", 1),
+                           N_("3-way merge in presence of adds and removes"), 1),
                OPT_SET_INT(0, "reset", &opts.reset,
-                           "same as -m, but discard unmerged entries", 1),
-               { OPTION_STRING, 0, "prefix", &opts.prefix, "<subdirectory>/",
-                 "read the tree into the index under <subdirectory>/",
+                           N_("same as -m, but discard unmerged entries"), 1),
+               { OPTION_STRING, 0, "prefix", &opts.prefix, N_("<subdirectory>/"),
+                 N_("read the tree into the index under <subdirectory>/"),
                  PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP },
                OPT_SET_INT('u', NULL, &opts.update,
-                           "update working tree with merge result", 1),
+                           N_("update working tree with merge result"), 1),
                { OPTION_CALLBACK, 0, "exclude-per-directory", &opts,
-                 "gitignore",
-                 "allow explicitly ignored files to be overwritten",
+                 N_("gitignore"),
+                 N_("allow explicitly ignored files to be overwritten"),
                  PARSE_OPT_NONEG, exclude_per_directory_cb },
                OPT_SET_INT('i', NULL, &opts.index_only,
-                           "don't check the working tree after merging", 1),
-               OPT__DRY_RUN(&opts.dry_run, "don't update the index or the work tree"),
+                           N_("don't check the working tree after merging"), 1),
+               OPT__DRY_RUN(&opts.dry_run, N_("don't update the index or the work tree")),
                OPT_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
-                           "skip applying sparse checkout filter", 1),
+                           N_("skip applying sparse checkout filter"), 1),
                OPT_SET_INT(0, "debug-unpack", &opts.debug_unpack,
-                           "debug unpack-trees", 1),
+                           N_("debug unpack-trees"), 1),
                OPT_END()
        };
 
index 0afb8b289621c419bd7472097335e4235da37d61..9145f1a5950fcd363a5e65e817df5c61bd769c01 100644 (file)
@@ -12,6 +12,7 @@
 #include "string-list.h"
 #include "sha1-array.h"
 #include "connected.h"
+#include "version.h"
 
 static const char receive_pack_usage[] = "git receive-pack <git-dir>";
 
@@ -121,10 +122,11 @@ static void show_ref(const char *path, const unsigned char *sha1)
        if (sent_capabilities)
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
        else
-               packet_write(1, "%s %s%c%s%s\n",
+               packet_write(1, "%s %s%c%s%s agent=%s\n",
                             sha1_to_hex(sha1), path, 0,
                             " report-status delete-refs side-band-64k quiet",
-                            prefer_ofs_delta ? " ofs-delta" : "");
+                            prefer_ofs_delta ? " ofs-delta" : "",
+                            git_user_agent_sanitized());
        sent_capabilities = 1;
 }
 
@@ -478,7 +480,6 @@ static const char *update(struct command *cmd)
            !prefixcmp(name, "refs/heads/")) {
                struct object *old_object, *new_object;
                struct commit *old_commit, *new_commit;
-               struct commit_list *bases, *ent;
 
                old_object = parse_object(old_sha1);
                new_object = parse_object(new_sha1);
@@ -491,12 +492,7 @@ static const char *update(struct command *cmd)
                }
                old_commit = (struct commit *)old_object;
                new_commit = (struct commit *)new_object;
-               bases = get_merge_bases(old_commit, new_commit, 1);
-               for (ent = bases; ent; ent = ent->next)
-                       if (!hashcmp(old_sha1, ent->item->object.sha1))
-                               break;
-               free_commit_list(bases);
-               if (!ent) {
+               if (!in_merge_bases(old_commit, new_commit)) {
                        rp_error("denying non-fast-forward %s"
                                 " (you should pull first)", name);
                        return "non-fast-forward";
@@ -977,7 +973,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                        const char *argv_gc_auto[] = {
                                "gc", "--auto", "--quiet", NULL,
                        };
-                       run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
+                       int opt = RUN_GIT_CMD | RUN_COMMAND_STDOUT_TO_STDERR;
+                       run_command_v_opt(argv_gc_auto, opt);
                }
                if (auto_update_server_info)
                        update_server_info(0);
index 920262d76ec9bdc40a8ad7703cf1eb972a40eb9e..8a670d82a74017d0f7b27a5b5b5232abf32d2816 100644 (file)
@@ -8,66 +8,66 @@
 #include "refs.h"
 
 static const char * const builtin_remote_usage[] = {
-       "git remote [-v | --verbose]",
-       "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>)",
-       "git remote [-v | --verbose] show [-n] <name>",
-       "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 [--push] <name> <newurl> [<oldurl>]",
-       "git remote set-url --add <name> <newurl>",
-       "git remote set-url --delete <name> <url>",
+       N_("git remote [-v | --verbose]"),
+       N_("git remote add [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>"),
+       N_("git remote rename <old> <new>"),
+       N_("git remote rm <name>"),
+       N_("git remote set-head <name> (-a | -d | <branch>)"),
+       N_("git remote [-v | --verbose] show [-n] <name>"),
+       N_("git remote prune [-n | --dry-run] <name>"),
+       N_("git remote [-v | --verbose] update [-p | --prune] [(<group> | <remote>)...]"),
+       N_("git remote set-branches [--add] <name> <branch>..."),
+       N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"),
+       N_("git remote set-url --add <name> <newurl>"),
+       N_("git remote set-url --delete <name> <url>"),
        NULL
 };
 
 static const char * const builtin_remote_add_usage[] = {
-       "git remote add [<options>] <name> <url>",
+       N_("git remote add [<options>] <name> <url>"),
        NULL
 };
 
 static const char * const builtin_remote_rename_usage[] = {
-       "git remote rename <old> <new>",
+       N_("git remote rename <old> <new>"),
        NULL
 };
 
 static const char * const builtin_remote_rm_usage[] = {
-       "git remote rm <name>",
+       N_("git remote rm <name>"),
        NULL
 };
 
 static const char * const builtin_remote_sethead_usage[] = {
-       "git remote set-head <name> (-a | -d | <branch>])",
+       N_("git remote set-head <name> (-a | -d | <branch>])"),
        NULL
 };
 
 static const char * const builtin_remote_setbranches_usage[] = {
-       "git remote set-branches <name> <branch>...",
-       "git remote set-branches --add <name> <branch>...",
+       N_("git remote set-branches <name> <branch>..."),
+       N_("git remote set-branches --add <name> <branch>..."),
        NULL
 };
 
 static const char * const builtin_remote_show_usage[] = {
-       "git remote show [<options>] <name>",
+       N_("git remote show [<options>] <name>"),
        NULL
 };
 
 static const char * const builtin_remote_prune_usage[] = {
-       "git remote prune [<options>] <name>",
+       N_("git remote prune [<options>] <name>"),
        NULL
 };
 
 static const char * const builtin_remote_update_usage[] = {
-       "git remote update [<options>] [<group> | <remote>]...",
+       N_("git remote update [<options>] [<group> | <remote>]..."),
        NULL
 };
 
 static const char * const builtin_remote_seturl_usage[] = {
-       "git remote set-url [--push] <name> <newurl> [<oldurl>]",
-       "git remote set-url --add <name> <newurl>",
-       "git remote set-url --delete <name> <url>",
+       N_("git remote set-url [--push] <name> <newurl> [<oldurl>]"),
+       N_("git remote set-url --add <name> <newurl>"),
+       N_("git remote set-url --delete <name> <url>"),
        NULL
 };
 
@@ -160,17 +160,17 @@ static int add(int argc, const char **argv)
        int i;
 
        struct option options[] = {
-               OPT_BOOLEAN('f', "fetch", &fetch, "fetch the remote branches"),
+               OPT_BOOLEAN('f', "fetch", &fetch, N_("fetch the remote branches")),
                OPT_SET_INT(0, "tags", &fetch_tags,
-                           "import all tags and associated objects when fetching",
+                           N_("import all tags and associated objects when fetching"),
                            TAGS_SET),
                OPT_SET_INT(0, NULL, &fetch_tags,
-                           "or do not fetch any tag at all (--no-tags)", TAGS_UNSET),
-               OPT_STRING_LIST('t', "track", &track, "branch",
-                               "branch(es) to track"),
-               OPT_STRING('m', "master", &master, "branch", "master branch"),
-               { OPTION_CALLBACK, 0, "mirror", &mirror, "push|fetch",
-                       "set up remote as a mirror to push to or fetch from",
+                           N_("or do not fetch any tag at all (--no-tags)"), TAGS_UNSET),
+               OPT_STRING_LIST('t', "track", &track, N_("branch"),
+                               N_("branch(es) to track")),
+               OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
+               { OPTION_CALLBACK, 0, "mirror", &mirror, N_("push|fetch"),
+                       N_("set up remote as a mirror to push to or fetch from"),
                        PARSE_OPT_OPTARG, parse_mirror_opt },
                OPT_END()
        };
@@ -1088,7 +1088,7 @@ static int show(int argc, const char **argv)
 {
        int no_query = 0, result = 0, query_flag = 0;
        struct option options[] = {
-               OPT_BOOLEAN('n', NULL, &no_query, "do not query remotes"),
+               OPT_BOOLEAN('n', NULL, &no_query, N_("do not query remotes")),
                OPT_END()
        };
        struct ref_states states;
@@ -1196,9 +1196,9 @@ static int set_head(int argc, const char **argv)
 
        struct option options[] = {
                OPT_BOOLEAN('a', "auto", &opt_a,
-                           "set refs/remotes/<name>/HEAD according to remote"),
+                           N_("set refs/remotes/<name>/HEAD according to remote")),
                OPT_BOOLEAN('d', "delete", &opt_d,
-                           "delete refs/remotes/<name>/HEAD"),
+                           N_("delete refs/remotes/<name>/HEAD")),
                OPT_END()
        };
        argc = parse_options(argc, argv, NULL, options, builtin_remote_sethead_usage,
@@ -1250,7 +1250,7 @@ static int prune(int argc, const char **argv)
 {
        int dry_run = 0, result = 0;
        struct option options[] = {
-               OPT__DRY_RUN(&dry_run, "dry run"),
+               OPT__DRY_RUN(&dry_run, N_("dry run")),
                OPT_END()
        };
 
@@ -1318,7 +1318,7 @@ static int update(int argc, const char **argv)
        int i, prune = 0;
        struct option options[] = {
                OPT_BOOLEAN('p', "prune", &prune,
-                           "prune remotes after fetching"),
+                           N_("prune remotes after fetching")),
                OPT_END()
        };
        const char **fetch_argv;
@@ -1404,7 +1404,7 @@ static int set_branches(int argc, const char **argv)
 {
        int add_mode = 0;
        struct option options[] = {
-               OPT_BOOLEAN('\0', "add", &add_mode, "add branch"),
+               OPT_BOOLEAN('\0', "add", &add_mode, N_("add branch")),
                OPT_END()
        };
 
@@ -1433,11 +1433,11 @@ static int set_url(int argc, const char **argv)
        struct strbuf name_buf = STRBUF_INIT;
        struct option options[] = {
                OPT_BOOLEAN('\0', "push", &push_mode,
-                           "manipulate push URLs"),
+                           N_("manipulate push URLs")),
                OPT_BOOLEAN('\0', "add", &add_mode,
-                           "add URL"),
+                           N_("add URL")),
                OPT_BOOLEAN('\0', "delete", &delete_mode,
-                           "delete URLs"),
+                           N_("delete URLs")),
                OPT_END()
        };
        argc = parse_options(argc, argv, NULL, options, builtin_remote_seturl_usage,
@@ -1566,7 +1566,7 @@ static int show_all(void)
 int cmd_remote(int argc, const char **argv, const char *prefix)
 {
        struct option options[] = {
-               OPT__VERBOSE(&verbose, "be verbose; must be placed before a subcommand"),
+               OPT__VERBOSE(&verbose, N_("be verbose; must be placed before a subcommand")),
                OPT_END()
        };
        int result;
index 4a8970e9c95c1e07c1ed919d40f33238dcd05588..e3aaf70203d4f5b6f099ed538d3366fb0a7747ce 100644 (file)
@@ -14,9 +14,9 @@
 #include "parse-options.h"
 
 static const char * const git_replace_usage[] = {
-       "git replace [-f] <object> <replacement>",
-       "git replace -d <object>...",
-       "git replace -l [<pattern>]",
+       N_("git replace [-f] <object> <replacement>"),
+       N_("git replace -d <object>..."),
+       N_("git replace -l [<pattern>]"),
        NULL
 };
 
@@ -115,9 +115,9 @@ int cmd_replace(int argc, const char **argv, const char *prefix)
 {
        int list = 0, delete = 0, force = 0;
        struct option options[] = {
-               OPT_BOOLEAN('l', NULL, &list, "list replace refs"),
-               OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"),
-               OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"),
+               OPT_BOOLEAN('l', NULL, &list, N_("list replace refs")),
+               OPT_BOOLEAN('d', NULL, &delete, N_("delete replace refs")),
+               OPT_BOOLEAN('f', NULL, &force, N_("replace the ref if it exists")),
                OPT_END()
        };
 
index 08213c7c0bc6fa5d649a38520dc87222019babe2..dc1708e6d6de54b5aaec36d5ff4242bd60183f0a 100644 (file)
@@ -8,7 +8,7 @@
 #include "xdiff-interface.h"
 
 static const char * const rerere_usage[] = {
-       "git rerere [clear | forget path... | status | remaining | diff | gc]",
+       N_("git rerere [clear | forget path... | status | remaining | diff | gc]"),
        NULL,
 };
 
@@ -53,7 +53,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
 
        struct option options[] = {
                OPT_SET_INT(0, "rerere-autoupdate", &autoupdate,
-                       "register clean resolutions in index", 1),
+                       N_("register clean resolutions in index"), 1),
                OPT_END(),
        };
 
index 74442bd7663387ab9864927f9c0d511e04e6e379..915cc9f86f855d517abaa55d31c502899fb77220 100644 (file)
@@ -22,9 +22,9 @@
 #include "cache-tree.h"
 
 static const char * const git_reset_usage[] = {
-       "git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]",
-       "git reset [-q] <commit> [--] <paths>...",
-       "git reset --patch [<commit>] [--] [<paths>...]",
+       N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
+       N_("git reset [-q] <commit> [--] <paths>..."),
+       N_("git reset --patch [<commit>] [--] [<paths>...]"),
        NULL
 };
 
@@ -235,17 +235,17 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        struct commit *commit;
        struct strbuf msg = STRBUF_INIT;
        const struct option options[] = {
-               OPT__QUIET(&quiet, "be quiet, only report errors"),
+               OPT__QUIET(&quiet, N_("be quiet, only report errors")),
                OPT_SET_INT(0, "mixed", &reset_type,
-                                               "reset HEAD and index", MIXED),
-               OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
+                                               N_("reset HEAD and index"), MIXED),
+               OPT_SET_INT(0, "soft", &reset_type, N_("reset only HEAD"), SOFT),
                OPT_SET_INT(0, "hard", &reset_type,
-                               "reset HEAD, index and working tree", HARD),
+                               N_("reset HEAD, index and working tree"), HARD),
                OPT_SET_INT(0, "merge", &reset_type,
-                               "reset HEAD, index and working tree", MERGE),
+                               N_("reset HEAD, index and working tree"), MERGE),
                OPT_SET_INT(0, "keep", &reset_type,
-                               "reset HEAD but keep local changes", KEEP),
-               OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
+                               N_("reset HEAD but keep local changes"), KEEP),
+               OPT_BOOLEAN('p', "patch", &patch_mode, N_("select hunks interactively")),
                OPT_END()
        };
 
index 32788a9f86427237d0f4838362c1e3bc722fa258..f267a1d3b57c1f1ab44c83a197f46051f4f13060 100644 (file)
@@ -230,6 +230,7 @@ static int try_difference(const char *arg)
        const char *next;
        const char *this;
        int symmetric;
+       static const char head_by_default[] = "HEAD";
 
        if (!(dotdot = strstr(arg, "..")))
                return 0;
@@ -241,9 +242,20 @@ static int try_difference(const char *arg)
        next += symmetric;
 
        if (!*next)
-               next = "HEAD";
+               next = head_by_default;
        if (dotdot == arg)
-               this = "HEAD";
+               this = head_by_default;
+
+       if (this == head_by_default && next == head_by_default &&
+           !symmetric) {
+               /*
+                * Just ".."?  That is not a range but the
+                * pathspec for the parent directory.
+                */
+               *dotdot = '.';
+               return 0;
+       }
+
        if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
                show_rev(NORMAL, end, next);
                show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
@@ -324,15 +336,15 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
 {
        static int keep_dashdash = 0, stop_at_non_option = 0;
        static char const * const parseopt_usage[] = {
-               "git rev-parse --parseopt [options] -- [<args>...]",
+               N_("git rev-parse --parseopt [options] -- [<args>...]"),
                NULL
        };
        static struct option parseopt_opts[] = {
                OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
-                                       "keep the `--` passed as an arg"),
+                                       N_("keep the `--` passed as an arg")),
                OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
-                                       "stop parsing after the "
-                                       "first non-option argument"),
+                                       N_("stop parsing after the "
+                                          "first non-option argument")),
                OPT_END(),
        };
 
@@ -449,11 +461,11 @@ static void die_no_single_rev(int quiet)
 }
 
 static const char builtin_rev_parse_usage[] =
-"git rev-parse --parseopt [options] -- [<args>...]\n"
-"   or: git rev-parse --sq-quote [<arg>...]\n"
-"   or: git rev-parse [options] [<arg>...]\n"
-"\n"
-"Run \"git rev-parse --parseopt -h\" for more information on the first usage.";
+N_("git rev-parse --parseopt [options] -- [<args>...]\n"
+   "   or: git rev-parse --sq-quote [<arg>...]\n"
+   "   or: git rev-parse [options] [<arg>...]\n"
+   "\n"
+   "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
 
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
index 82d1bf844b996d5d121169f244358964fd36fd19..c5e36b94c080212ec9241146c20ef2e323663bea 100644 (file)
  */
 
 static const char * const revert_usage[] = {
-       "git revert [options] <commit-ish>",
-       "git revert <subcommand>",
+       N_("git revert [options] <commit-ish>"),
+       N_("git revert <subcommand>"),
        NULL
 };
 
 static const char * const cherry_pick_usage[] = {
-       "git cherry-pick [options] <commit-ish>",
-       "git cherry-pick <subcommand>",
+       N_("git cherry-pick [options] <commit-ish>"),
+       N_("git cherry-pick <subcommand>"),
        NULL
 };
 
@@ -100,18 +100,19 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
        int contin = 0;
        int rollback = 0;
        struct option options[] = {
-               OPT_BOOLEAN(0, "quit", &remove_state, "end revert or cherry-pick sequence"),
-               OPT_BOOLEAN(0, "continue", &contin, "resume revert or cherry-pick sequence"),
-               OPT_BOOLEAN(0, "abort", &rollback, "cancel revert or cherry-pick sequence"),
-               OPT_BOOLEAN('n', "no-commit", &opts->no_commit, "don't automatically commit"),
-               OPT_BOOLEAN('e', "edit", &opts->edit, "edit the commit message"),
+               OPT_BOOLEAN(0, "quit", &remove_state, N_("end revert or cherry-pick sequence")),
+               OPT_BOOLEAN(0, "continue", &contin, N_("resume revert or cherry-pick sequence")),
+               OPT_BOOLEAN(0, "abort", &rollback, N_("cancel revert or cherry-pick sequence")),
+               OPT_BOOLEAN('n', "no-commit", &opts->no_commit, N_("don't automatically commit")),
+               OPT_BOOLEAN('e', "edit", &opts->edit, N_("edit the commit message")),
                OPT_NOOP_NOARG('r', NULL),
-               OPT_BOOLEAN('s', "signoff", &opts->signoff, "add Signed-off-by:"),
-               OPT_INTEGER('m', "mainline", &opts->mainline, "parent number"),
+               OPT_BOOLEAN('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
+               OPT_INTEGER('m', "mainline", &opts->mainline, N_("parent number")),
                OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
-               OPT_STRING(0, "strategy", &opts->strategy, "strategy", "merge strategy"),
-               OPT_CALLBACK('X', "strategy-option", &opts, "option",
-                       "option for merge strategy", option_parse_x),
+               OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
+               OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
+                       N_("option for merge strategy"), option_parse_x),
+               OPT_END(),
                OPT_END(),
                OPT_END(),
                OPT_END(),
@@ -121,10 +122,11 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 
        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_BOOLEAN('x', NULL, &opts->record_origin, N_("append commit name")),
+                       OPT_BOOLEAN(0, "ff", &opts->allow_ff, N_("allow fast-forward")),
+                       OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, N_("preserve initially empty commits")),
+                       OPT_BOOLEAN(0, "allow-empty-message", &opts->allow_empty_message, N_("allow commits with empty messages")),
+                       OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, N_("keep redundant, empty commits")),
                        OPT_END(),
                };
                if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
@@ -193,7 +195,7 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
                struct setup_revision_opt s_r_opt;
                opts->revs = xmalloc(sizeof(*opts->revs));
                init_revisions(opts->revs, NULL);
-               opts->revs->no_walk = 1;
+               opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
                if (argc < 2)
                        usage_with_options(usage_str, options);
                memset(&s_r_opt, 0, sizeof(s_r_opt));
index 90c8a5047c26cd7bdb939d150b842823f595333c..b384c4c3cfe973346b5b295416f3883ae2d73c94 100644 (file)
@@ -11,7 +11,7 @@
 #include "parse-options.h"
 
 static const char * const builtin_rm_usage[] = {
-       "git rm [options] [--] <file>...",
+       N_("git rm [options] [--] <file>..."),
        NULL
 };
 
@@ -130,13 +130,13 @@ static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
 static int ignore_unmatch = 0;
 
 static struct option builtin_rm_options[] = {
-       OPT__DRY_RUN(&show_only, "dry run"),
-       OPT__QUIET(&quiet, "do not list removed files"),
-       OPT_BOOLEAN( 0 , "cached",         &index_only, "only remove from the index"),
-       OPT__FORCE(&force, "override the up-to-date check"),
-       OPT_BOOLEAN('r', NULL,             &recursive,  "allow recursive removal"),
+       OPT__DRY_RUN(&show_only, N_("dry run")),
+       OPT__QUIET(&quiet, N_("do not list removed files")),
+       OPT_BOOLEAN( 0 , "cached",         &index_only, N_("only remove from the index")),
+       OPT__FORCE(&force, N_("override the up-to-date check")),
+       OPT_BOOLEAN('r', NULL,             &recursive,  N_("allow recursive removal")),
        OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch,
-                               "exit with a zero status even if nothing matched"),
+                               N_("exit with a zero status even if nothing matched")),
        OPT_END(),
 };
 
index d5d7105ba2debde9fa6f2f33171d84cc7c25339a..7d05064218184b815fe01228a24138c1f9ac7c7f 100644 (file)
@@ -8,6 +8,7 @@
 #include "send-pack.h"
 #include "quote.h"
 #include "transport.h"
+#include "version.h"
 
 static const char send_pack_usage[] =
 "git send-pack [--all | --mirror] [--dry-run] [--force] [--receive-pack=<git-receive-pack>] [--verbose] [--thin] [<host>:]<directory> [<ref>...]\n"
@@ -251,6 +252,7 @@ int send_pack(struct send_pack_args *args,
        int status_report = 0;
        int use_sideband = 0;
        int quiet_supported = 0;
+       int agent_supported = 0;
        unsigned cmds_sent = 0;
        int ret;
        struct async demux;
@@ -266,6 +268,8 @@ int send_pack(struct send_pack_args *args,
                use_sideband = 1;
        if (server_supports("quiet"))
                quiet_supported = 1;
+       if (server_supports("agent"))
+               agent_supported = 1;
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -305,12 +309,17 @@ int send_pack(struct send_pack_args *args,
                        char *new_hex = sha1_to_hex(ref->new_sha1);
                        int quiet = quiet_supported && (args->quiet || !args->progress);
 
-                       if (!cmds_sent && (status_report || use_sideband || args->quiet)) {
-                               packet_buf_write(&req_buf, "%s %s %s%c%s%s%s",
+                       if (!cmds_sent && (status_report || use_sideband ||
+                                          quiet || agent_supported)) {
+                               packet_buf_write(&req_buf,
+                                                "%s %s %s%c%s%s%s%s%s",
                                                 old_hex, new_hex, ref->name, 0,
                                                 status_report ? " report-status" : "",
                                                 use_sideband ? " side-band-64k" : "",
-                                                quiet ? " quiet" : "");
+                                                quiet ? " quiet" : "",
+                                                agent_supported ? " agent=" : "",
+                                                agent_supported ? git_user_agent_sanitized() : ""
+                                               );
                        }
                        else
                                packet_buf_write(&req_buf, "%s %s %s",
index 37f3193a3366725a6b11bdb55db7b6ffc6e9b3ce..b316cf37ca768f1502c5d0cfa64372a9b322a572 100644 (file)
@@ -10,9 +10,9 @@
 #include "parse-options.h"
 
 static char const * const shortlog_usage[] = {
-       "git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]",
+       N_("git shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]"),
        "",
-       "[rev-opts] are documented in git-rev-list(1)",
+       N_("[rev-opts] are documented in git-rev-list(1)"),
        NULL
 };
 
@@ -250,13 +250,13 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 
        static const struct option options[] = {
                OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
-                           "sort output according to the number of commits per author"),
+                           N_("sort output according to the number of commits per author")),
                OPT_BOOLEAN('s', "summary", &log.summary,
-                           "Suppress commit descriptions, only provides commit count"),
+                           N_("Suppress commit descriptions, only provides commit count")),
                OPT_BOOLEAN('e', "email", &log.email,
-                           "Show the email address of each author"),
-               { OPTION_CALLBACK, 'w', NULL, &log, "w[,i1[,i2]]",
-                       "Linewrap output", PARSE_OPT_OPTARG, &parse_wrap_args },
+                           N_("Show the email address of each author")),
+               { OPTION_CALLBACK, 'w', NULL, &log, N_("w[,i1[,i2]]"),
+                       N_("Linewrap output"), PARSE_OPT_OPTARG, &parse_wrap_args },
                OPT_END(),
        };
 
index a59e088cf59215d2d196dda530ad43ef0596b6be..d208fd6c6821c729a713edb485a20bbe8d5e2692 100644 (file)
@@ -6,8 +6,8 @@
 #include "parse-options.h"
 
 static const char* show_branch_usage[] = {
-    "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]",
-    "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]",
+    N_("git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color[=<when>] | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [(<rev> | <glob>)...]"),
+    N_("git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]"),
     NULL
 };
 
@@ -648,36 +648,36 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
        const char *reflog_base = NULL;
        struct option builtin_show_branch_options[] = {
                OPT_BOOLEAN('a', "all", &all_heads,
-                           "show remote-tracking and local branches"),
+                           N_("show remote-tracking and local branches")),
                OPT_BOOLEAN('r', "remotes", &all_remotes,
-                           "show remote-tracking branches"),
+                           N_("show remote-tracking branches")),
                OPT__COLOR(&showbranch_use_color,
-                           "color '*!+-' corresponding to the branch"),
-               { OPTION_INTEGER, 0, "more", &extra, "n",
-                           "show <n> more commits after the common ancestor",
+                           N_("color '*!+-' corresponding to the branch")),
+               { OPTION_INTEGER, 0, "more", &extra, N_("n"),
+                           N_("show <n> more commits after the common ancestor"),
                            PARSE_OPT_OPTARG, NULL, (intptr_t)1 },
-               OPT_SET_INT(0, "list", &extra, "synonym to more=-1", -1),
-               OPT_BOOLEAN(0, "no-name", &no_name, "suppress naming strings"),
+               OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1),
+               OPT_BOOLEAN(0, "no-name", &no_name, N_("suppress naming strings")),
                OPT_BOOLEAN(0, "current", &with_current_branch,
-                           "include the current branch"),
+                           N_("include the current branch")),
                OPT_BOOLEAN(0, "sha1-name", &sha1_name,
-                           "name commits with their object names"),
+                           N_("name commits with their object names")),
                OPT_BOOLEAN(0, "merge-base", &merge_base,
-                           "show possible merge bases"),
+                           N_("show possible merge bases")),
                OPT_BOOLEAN(0, "independent", &independent,
-                           "show refs unreachable from any other ref"),
+                           N_("show refs unreachable from any other ref")),
                OPT_BOOLEAN(0, "topo-order", &lifo,
-                           "show commits in topological order"),
+                           N_("show commits in topological order")),
                OPT_BOOLEAN(0, "topics", &topics,
-                           "show only commits not on the first branch"),
+                           N_("show only commits not on the first branch")),
                OPT_SET_INT(0, "sparse", &dense,
-                           "show merges reachable from only one tip", 0),
+                           N_("show merges reachable from only one tip"), 0),
                OPT_SET_INT(0, "date-order", &lifo,
-                           "show commits where no parent comes before its "
-                           "children", 0),
-               { OPTION_CALLBACK, 'g', "reflog", &reflog_base, "<n>[,<base>]",
-                           "show <n> most recent ref-log entries starting at "
-                           "base",
+                           N_("show commits where no parent comes before its "
+                              "children"), 0),
+               { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"),
+                           N_("show <n> most recent ref-log entries starting at "
+                              "base"),
                            PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP,
                            parse_reflog_param },
                OPT_END()
index 3911661900f79562133b4ebd6cbb513a06232b5b..4eb016d6e53628f19d51a50aeb074bf424154026 100644 (file)
@@ -7,8 +7,8 @@
 #include "parse-options.h"
 
 static const char * const show_ref_usage[] = {
-       "git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ",
-       "git show-ref --exclude-existing[=pattern] < ref-list",
+       N_("git show-ref [-q|--quiet] [--verify] [--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] "),
+       N_("git show-ref --exclude-existing[=pattern] < ref-list"),
        NULL
 };
 
@@ -179,26 +179,26 @@ static int help_callback(const struct option *opt, const char *arg, int unset)
 }
 
 static const struct option show_ref_options[] = {
-       OPT_BOOLEAN(0, "tags", &tags_only, "only show tags (can be combined with heads)"),
-       OPT_BOOLEAN(0, "heads", &heads_only, "only show heads (can be combined with tags)"),
-       OPT_BOOLEAN(0, "verify", &verify, "stricter reference checking, "
-                   "requires exact ref path"),
+       OPT_BOOLEAN(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")),
+       OPT_BOOLEAN(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")),
+       OPT_BOOLEAN(0, "verify", &verify, N_("stricter reference checking, "
+                   "requires exact ref path")),
        { OPTION_BOOLEAN, 'h', NULL, &show_head, NULL,
-         "show the HEAD reference",
+         N_("show the HEAD reference"),
          PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
-       OPT_BOOLEAN(0, "head", &show_head, "show the HEAD reference"),
+       OPT_BOOLEAN(0, "head", &show_head, N_("show the HEAD reference")),
        OPT_BOOLEAN('d', "dereference", &deref_tags,
-                   "dereference tags into object IDs"),
-       { OPTION_CALLBACK, 's', "hash", &abbrev, "n",
-         "only show SHA1 hash using <n> digits",
+                   N_("dereference tags into object IDs")),
+       { OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"),
+         N_("only show SHA1 hash using <n> digits"),
          PARSE_OPT_OPTARG, &hash_callback },
        OPT__ABBREV(&abbrev),
        OPT__QUIET(&quiet,
-                  "do not print results to stdout (useful with --verify)"),
+                  N_("do not print results to stdout (useful with --verify)")),
        { OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
-         "pattern", "show refs from stdin that aren't in local repository",
+         N_("pattern"), N_("show refs from stdin that aren't in local repository"),
          PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
-       { OPTION_CALLBACK, 0, "help-all", NULL, NULL, "show usage",
+       { OPTION_CALLBACK, 0, "help-all", NULL, NULL, N_("show usage"),
          PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
        OPT_END()
 };
index 801d62ece5d2ec84d027089c29fc58e279126ebb..9e92828b3ab02fb93c568eab03f0db9d398e8fa2 100644 (file)
@@ -4,7 +4,7 @@
 #include "parse-options.h"
 
 static const char * const git_symbolic_ref_usage[] = {
-       "git symbolic-ref [options] name [ref]",
+       N_("git symbolic-ref [options] name [ref]"),
        NULL
 };
 
@@ -35,9 +35,9 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
        const char *msg = NULL;
        struct option options[] = {
                OPT__QUIET(&quiet,
-                       "suppress error message for non-symbolic (detached) refs"),
-               OPT_BOOL(0, "short", &shorten, "shorten ref output"),
-               OPT_STRING('m', NULL, &msg, "reason", "reason of the update"),
+                       N_("suppress error message for non-symbolic (detached) refs")),
+               OPT_BOOL(0, "short", &shorten, N_("shorten ref output")),
+               OPT_STRING('m', NULL, &msg, N_("reason"), N_("reason of the update")),
                OPT_END(),
        };
 
index 7b1be85e48b67f2a2eb075f6d8366246c7938646..9c3e0673d5bd4e011c877c4d3a17975498265e22 100644 (file)
 #include "column.h"
 
 static const char * const git_tag_usage[] = {
-       "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
-       "git tag -d <tagname>...",
-       "git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] "
-               "\n\t\t[<pattern>...]",
-       "git tag -v <tagname>...",
+       N_("git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]"),
+       N_("git tag -d <tagname>..."),
+       N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] "
+               "\n\t\t[<pattern>...]"),
+       N_("git tag -v <tagname>..."),
        NULL
 };
 
@@ -444,37 +444,37 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        struct msg_arg msg = { 0, STRBUF_INIT };
        struct commit_list *with_commit = NULL;
        struct option options[] = {
-               OPT_BOOLEAN('l', "list", &list, "list tag names"),
-               { OPTION_INTEGER, 'n', NULL, &lines, "n",
-                               "print <n> lines of each tag message",
+               OPT_BOOLEAN('l', "list", &list, N_("list tag names")),
+               { OPTION_INTEGER, 'n', NULL, &lines, N_("n"),
+                               N_("print <n> lines of each tag message"),
                                PARSE_OPT_OPTARG, NULL, 1 },
-               OPT_BOOLEAN('d', "delete", &delete, "delete tags"),
-               OPT_BOOLEAN('v', "verify", &verify, "verify tags"),
+               OPT_BOOLEAN('d', "delete", &delete, N_("delete tags")),
+               OPT_BOOLEAN('v', "verify", &verify, N_("verify tags")),
 
-               OPT_GROUP("Tag creation options"),
+               OPT_GROUP(N_("Tag creation options")),
                OPT_BOOLEAN('a', "annotate", &annotate,
-                                       "annotated tag, needs a message"),
-               OPT_CALLBACK('m', "message", &msg, "message",
-                            "tag message", parse_msg_arg),
-               OPT_FILENAME('F', "file", &msgfile, "read message from file"),
-               OPT_BOOLEAN('s', "sign", &opt.sign, "annotated and GPG-signed tag"),
-               OPT_STRING(0, "cleanup", &cleanup_arg, "mode",
-                       "how to strip spaces and #comments from message"),
-               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"),
+                                       N_("annotated tag, needs a message")),
+               OPT_CALLBACK('m', "message", &msg, N_("message"),
+                            N_("tag message"), parse_msg_arg),
+               OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
+               OPT_BOOLEAN('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
+               OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
+                       N_("how to strip spaces and #comments from message")),
+               OPT_STRING('u', "local-user", &keyid, N_("key id"),
+                                       N_("use another key to sign the tag")),
+               OPT__FORCE(&force, N_("replace the tag if exists")),
+               OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
+
+               OPT_GROUP(N_("Tag listing options")),
                {
-                       OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
-                       "print only tags that contain the commit",
+                       OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"),
+                       N_("print only tags that contain the commit"),
                        PARSE_OPT_LASTARG_DEFAULT,
                        parse_opt_with_commit, (intptr_t)"HEAD",
                },
                {
-                       OPTION_CALLBACK, 0, "points-at", NULL, "object",
-                       "print only tags of the object", 0, parse_opt_points_at
+                       OPTION_CALLBACK, 0, "points-at", NULL, N_("object"),
+                       N_("print only tags of the object"), 0, parse_opt_points_at
                },
                OPT_END()
        };
index 4ce341ceebbe27cc21b0341e4a4339af84af51de..74986bf163d4dadb4497a90364c29ac71a801638 100644 (file)
@@ -398,7 +398,7 @@ static void read_index_info(int line_termination)
 }
 
 static const char * const update_index_usage[] = {
-       "git update-index [options] [--] [<file>...]",
+       N_("git update-index [options] [--] [<file>...]"),
        NULL
 };
 
@@ -714,83 +714,83 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        int parseopt_state = PARSE_OPT_UNKNOWN;
        struct option options[] = {
                OPT_BIT('q', NULL, &refresh_args.flags,
-                       "continue refresh even when index needs update",
+                       N_("continue refresh even when index needs update"),
                        REFRESH_QUIET),
                OPT_BIT(0, "ignore-submodules", &refresh_args.flags,
-                       "refresh: ignore submodules",
+                       N_("refresh: ignore submodules"),
                        REFRESH_IGNORE_SUBMODULES),
                OPT_SET_INT(0, "add", &allow_add,
-                       "do not ignore new files", 1),
+                       N_("do not ignore new files"), 1),
                OPT_SET_INT(0, "replace", &allow_replace,
-                       "let files replace directories and vice-versa", 1),
+                       N_("let files replace directories and vice-versa"), 1),
                OPT_SET_INT(0, "remove", &allow_remove,
-                       "notice files missing from worktree", 1),
+                       N_("notice files missing from worktree"), 1),
                OPT_BIT(0, "unmerged", &refresh_args.flags,
-                       "refresh even if index contains unmerged entries",
+                       N_("refresh even if index contains unmerged entries"),
                        REFRESH_UNMERGED),
                {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL,
-                       "refresh stat information",
+                       N_("refresh stat information"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        refresh_callback},
                {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL,
-                       "like --refresh, but ignore assume-unchanged setting",
+                       N_("like --refresh, but ignore assume-unchanged setting"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        really_refresh_callback},
                {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
-                       "<mode> <object> <path>",
-                       "add the specified entry to the index",
+                       N_("<mode> <object> <path>"),
+                       N_("add the specified entry to the index"),
                        PARSE_OPT_NOARG |       /* disallow --cacheinfo=<mode> form */
                        PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
                        (parse_opt_cb *) cacheinfo_callback},
-               {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+/-)x",
-                       "override the executable bit of the listed files",
+               {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, N_("(+/-)x"),
+                       N_("override the executable bit of the listed files"),
                        PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
                        chmod_callback},
                {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
-                       "mark files as \"not changing\"",
+                       N_("mark files as \"not changing\""),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
                {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL,
-                       "clear assumed-unchanged bit",
+                       N_("clear assumed-unchanged bit"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
                {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL,
-                       "mark files as \"index-only\"",
+                       N_("mark files as \"index-only\""),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
                {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
-                       "clear skip-worktree bit",
+                       N_("clear skip-worktree bit"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
                OPT_SET_INT(0, "info-only", &info_only,
-                       "add to index only; do not add content to object database", 1),
+                       N_("add to index only; do not add content to object database"), 1),
                OPT_SET_INT(0, "force-remove", &force_remove,
-                       "remove named paths even if present in worktree", 1),
+                       N_("remove named paths even if present in worktree"), 1),
                OPT_SET_INT('z', NULL, &line_termination,
-                       "with --stdin: input lines are terminated by null bytes", '\0'),
+                       N_("with --stdin: input lines are terminated by null bytes"), '\0'),
                {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL,
-                       "read list of paths to be updated from standard input",
+                       N_("read list of paths to be updated from standard input"),
                        PARSE_OPT_NONEG | PARSE_OPT_NOARG,
                        (parse_opt_cb *) stdin_callback},
                {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL,
-                       "add entries from standard input to the index",
+                       N_("add entries from standard input to the index"),
                        PARSE_OPT_NONEG | PARSE_OPT_NOARG,
                        (parse_opt_cb *) stdin_cacheinfo_callback},
                {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL,
-                       "repopulate stages #2 and #3 for the listed paths",
+                       N_("repopulate stages #2 and #3 for the listed paths"),
                        PARSE_OPT_NONEG | PARSE_OPT_NOARG,
                        (parse_opt_cb *) unresolve_callback},
                {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL,
-                       "only update entries that differ from HEAD",
+                       N_("only update entries that differ from HEAD"),
                        PARSE_OPT_NONEG | PARSE_OPT_NOARG,
                        (parse_opt_cb *) reupdate_callback},
                OPT_BIT(0, "ignore-missing", &refresh_args.flags,
-                       "ignore files missing from worktree",
+                       N_("ignore files missing from worktree"),
                        REFRESH_IGNORE_MISSING),
                OPT_SET_INT(0, "verbose", &verbose,
-                       "report actions to standard output", 1),
+                       N_("report actions to standard output"), 1),
                {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL,
-                       "(for porcelains) forget saved unresolved conflicts",
+                       N_("(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"),
+                       N_("write index in this format")),
                OPT_END()
        };
 
index 835c62ab15c742af2f75e7bf66e35501701ed15a..51d26848598d7f18749f1f2859d7a74558d60b69 100644 (file)
@@ -4,8 +4,8 @@
 #include "parse-options.h"
 
 static const char * const git_update_ref_usage[] = {
-       "git update-ref [options] -d <refname> [<oldval>]",
-       "git update-ref [options]    <refname> <newval> [<oldval>]",
+       N_("git update-ref [options] -d <refname> [<oldval>]"),
+       N_("git update-ref [options]    <refname> <newval> [<oldval>]"),
        NULL
 };
 
@@ -15,10 +15,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        unsigned char sha1[20], oldsha1[20];
        int delete = 0, no_deref = 0, flags = 0;
        struct option options[] = {
-               OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"),
-               OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"),
+               OPT_STRING( 'm', NULL, &msg, N_("reason"), N_("reason of the update")),
+               OPT_BOOLEAN('d', NULL, &delete, N_("delete the reference")),
                OPT_BOOLEAN( 0 , "no-deref", &no_deref,
-                                       "update <refname> not the one it points to"),
+                                       N_("update <refname> not the one it points to")),
                OPT_END(),
        };
 
index 0d63c4498c0c10193846c020a2d76958bd12e1bd..6c8cc3edc1f5fec4e2aa173f2650a69db5e065f0 100644 (file)
@@ -3,7 +3,7 @@
 #include "parse-options.h"
 
 static const char * const update_server_info_usage[] = {
-       "git update-server-info [--force]",
+       N_("git update-server-info [--force]"),
        NULL
 };
 
@@ -11,7 +11,7 @@ int cmd_update_server_info(int argc, const char **argv, const char *prefix)
 {
        int force = 0;
        struct option options[] = {
-               OPT__FORCE(&force, "update the info files from scratch"),
+               OPT__FORCE(&force, N_("update the info files from scratch")),
                OPT_END()
        };
 
index e841b4a38d2b47c39d95683a5747cbc19da1f48b..66cd2df0f878d362a86e8dbc3fcd49b0c2f64c0c 100644 (file)
@@ -53,7 +53,7 @@ static int verify_one_pack(const char *path, unsigned int flags)
 }
 
 static const char * const verify_pack_usage[] = {
-       "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...",
+       N_("git verify-pack [-v|--verbose] [-s|--stat-only] <pack>..."),
        NULL
 };
 
@@ -63,9 +63,9 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
        unsigned int flags = 0;
        int i;
        const struct option verify_pack_options[] = {
-               OPT_BIT('v', "verbose", &flags, "verbose",
+               OPT_BIT('v', "verbose", &flags, N_("verbose"),
                        VERIFY_PACK_VERBOSE),
-               OPT_BIT('s', "stat-only", &flags, "show statistics only",
+               OPT_BIT('s', "stat-only", &flags, N_("show statistics only"),
                        VERIFY_PACK_STAT_ONLY),
                OPT_END()
        };
index 986789f706911d122e24b0d784ab193e2ec3b74b..a8eee886a5281965a0660308df9e94d14c1cb2b8 100644 (file)
@@ -14,7 +14,7 @@
 #include "gpg-interface.h"
 
 static const char * const verify_tag_usage[] = {
-               "git verify-tag [-v|--verbose] <tag>...",
+               N_("git verify-tag [-v|--verbose] <tag>..."),
                NULL
 };
 
@@ -70,7 +70,7 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
 {
        int i = 1, verbose = 0, had_error = 0;
        const struct option verify_tag_options[] = {
-               OPT__VERBOSE(&verbose, "print tag contents"),
+               OPT__VERBOSE(&verbose, N_("print tag contents")),
                OPT_END()
        };
 
index b223af416fee5fc219fbcca7afa2b9e03feaa7d0..084c0df7833f29c7554a4ffe95dac4bda1c6ce66 100644 (file)
@@ -10,7 +10,7 @@
 #include "parse-options.h"
 
 static const char * const write_tree_usage[] = {
-       "git write-tree [--missing-ok] [--prefix=<prefix>/]",
+       N_("git write-tree [--missing-ok] [--prefix=<prefix>/]"),
        NULL
 };
 
@@ -21,13 +21,13 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
        unsigned char sha1[20];
        const char *me = "git-write-tree";
        struct option write_tree_options[] = {
-               OPT_BIT(0, "missing-ok", &flags, "allow missing objects",
+               OPT_BIT(0, "missing-ok", &flags, N_("allow missing objects"),
                        WRITE_TREE_MISSING_OK),
-               { OPTION_STRING, 0, "prefix", &prefix, "<prefix>/",
-                 "write tree object for a subdirectory <prefix>" ,
+               { OPTION_STRING, 0, "prefix", &prefix, N_("<prefix>/"),
+                 N_("write tree object for a subdirectory <prefix>") ,
                  PARSE_OPT_LITERAL_ARGHELP },
                { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL,
-                 "only useful for debugging",
+                 N_("only useful for debugging"),
                  PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL,
                  WRITE_TREE_IGNORE_CACHE_TREE },
                OPT_END()
diff --git a/cache.h b/cache.h
index 67f28b4da97b5aded337e1594043a73c075ea02f..eb4a0316d2d654160a51789dce5ad69fc9d1af45 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1038,7 +1038,9 @@ struct extra_have_objects {
 };
 extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
 extern int server_supports(const char *feature);
-extern const char *parse_feature_request(const char *features, const char *feature);
+extern int parse_feature_request(const char *features, const char *feature);
+extern const char *server_feature_value(const char *feature, int *len_ret);
+extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret);
 
 extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
@@ -1108,6 +1110,7 @@ extern int update_server_info(int);
 #define CONFIG_NO_WRITE 4
 #define CONFIG_NOTHING_SET 5
 #define CONFIG_INVALID_PATTERN 6
+#define CONFIG_GENERIC_ERROR 7
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
index 978668036835e16df4b6bfd37a7b1e9f8494cf07..bb1cc96c4e73c90ee327858aa3b36cf2bfe043a4 100644 (file)
@@ -111,7 +111,7 @@ static char *grab_blob(const unsigned char *sha1, unsigned int mode,
                return xcalloc(1, 1);
        } else if (textconv) {
                struct diff_filespec *df = alloc_filespec(path);
-               fill_filespec(df, sha1, mode);
+               fill_filespec(df, sha1, 1, mode);
                *size = fill_textconv(textconv, df, &blob);
                free_filespec(df);
        } else {
@@ -823,7 +823,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                                                   &result_size, NULL, NULL);
                } else if (textconv) {
                        struct diff_filespec *df = alloc_filespec(elem->path);
-                       fill_filespec(df, null_sha1, st.st_mode);
+                       fill_filespec(df, null_sha1, 0, st.st_mode);
                        result_size = fill_textconv(textconv, df, &result);
                        free_filespec(df);
                } else if (0 <= (fd = open(elem->path, O_RDONLY))) {
index ec64cacf06d1cc7c0f2de4a12bb2b465f5351250..7e8cfec29d9e00cd6dfb26bca1f1295f64ae53fd 100644 (file)
@@ -26,6 +26,8 @@ git-commit-tree                         plumbingmanipulators
 git-config                              ancillarymanipulators
 git-count-objects                       ancillaryinterrogators
 git-credential                          purehelpers
+git-credential-cache                    purehelpers
+git-credential-store                    purehelpers
 git-cvsexportcommit                     foreignscminterface
 git-cvsimport                           foreignscminterface
 git-cvsserver                           foreignscminterface
@@ -114,6 +116,7 @@ git-show                                mainporcelain common
 git-show-branch                         ancillaryinterrogators
 git-show-index                          plumbinginterrogators
 git-show-ref                            plumbinginterrogators
+git-sh-i18n                             purehelpers
 git-sh-setup                            purehelpers
 git-stash                               mainporcelain
 git-status                              mainporcelain common
index 42af4c1f238b96eeb9e88b227b6f6c10ef317058..0ea441d13b1430231f30d9524aed88b9719290b6 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -607,28 +607,12 @@ static struct commit *interesting(struct commit_list *list)
        return NULL;
 }
 
-static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
+static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
 {
        struct commit_list *list = NULL;
        struct commit_list *result = NULL;
        int i;
 
-       for (i = 0; i < n; i++) {
-               if (one == twos[i])
-                       /*
-                        * We do not mark this even with RESULT so we do not
-                        * have to clean it up.
-                        */
-                       return commit_list_insert(one, &result);
-       }
-
-       if (parse_commit(one))
-               return NULL;
-       for (i = 0; i < n; i++) {
-               if (parse_commit(twos[i]))
-                       return NULL;
-       }
-
        one->object.flags |= PARENT1;
        commit_list_insert_by_date(one, &list);
        for (i = 0; i < n; i++) {
@@ -669,9 +653,34 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
                }
        }
 
-       /* Clean up the result to remove stale ones */
        free_commit_list(list);
-       list = result; result = NULL;
+       return result;
+}
+
+static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
+{
+       struct commit_list *list = NULL;
+       struct commit_list *result = NULL;
+       int i;
+
+       for (i = 0; i < n; i++) {
+               if (one == twos[i])
+                       /*
+                        * We do not mark this even with RESULT so we do not
+                        * have to clean it up.
+                        */
+                       return commit_list_insert(one, &result);
+       }
+
+       if (parse_commit(one))
+               return NULL;
+       for (i = 0; i < n; i++) {
+               if (parse_commit(twos[i]))
+                       return NULL;
+       }
+
+       list = paint_down_to_common(one, n, twos);
+
        while (list) {
                struct commit_list *next = list->next;
                if (!(list->item->object.flags & STALE))
@@ -709,6 +718,60 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)
        return ret;
 }
 
+static int remove_redundant(struct commit **array, int cnt)
+{
+       /*
+        * Some commit in the array may be an ancestor of
+        * another commit.  Move such commit to the end of
+        * the array, and return the number of commits that
+        * are independent from each other.
+        */
+       struct commit **work;
+       unsigned char *redundant;
+       int *filled_index;
+       int i, j, filled;
+
+       work = xcalloc(cnt, sizeof(*work));
+       redundant = xcalloc(cnt, 1);
+       filled_index = xmalloc(sizeof(*filled_index) * (cnt - 1));
+
+       for (i = 0; i < cnt; i++) {
+               struct commit_list *common;
+
+               if (redundant[i])
+                       continue;
+               for (j = filled = 0; j < cnt; j++) {
+                       if (i == j || redundant[j])
+                               continue;
+                       filled_index[filled] = j;
+                       work[filled++] = array[j];
+               }
+               common = paint_down_to_common(array[i], filled, work);
+               if (array[i]->object.flags & PARENT2)
+                       redundant[i] = 1;
+               for (j = 0; j < filled; j++)
+                       if (work[j]->object.flags & PARENT1)
+                               redundant[filled_index[j]] = 1;
+               clear_commit_marks(array[i], all_flags);
+               for (j = 0; j < filled; j++)
+                       clear_commit_marks(work[j], all_flags);
+               free_commit_list(common);
+       }
+
+       /* Now collect the result */
+       memcpy(work, array, sizeof(*array) * cnt);
+       for (i = filled = 0; i < cnt; i++)
+               if (!redundant[i])
+                       array[filled++] = work[i];
+       for (j = filled, i = 0; i < cnt; i++)
+               if (redundant[i])
+                       array[j++] = work[i];
+       free(work);
+       free(redundant);
+       free(filled_index);
+       return filled;
+}
+
 struct commit_list *get_merge_bases_many(struct commit *one,
                                         int n,
                                         struct commit **twos,
@@ -717,7 +780,7 @@ struct commit_list *get_merge_bases_many(struct commit *one,
        struct commit_list *list;
        struct commit **rslt;
        struct commit_list *result;
-       int cnt, i, j;
+       int cnt, i;
 
        result = merge_bases_many(one, n, twos);
        for (i = 0; i < n; i++) {
@@ -748,28 +811,11 @@ struct commit_list *get_merge_bases_many(struct commit *one,
        clear_commit_marks(one, all_flags);
        for (i = 0; i < n; i++)
                clear_commit_marks(twos[i], all_flags);
-       for (i = 0; i < cnt - 1; i++) {
-               for (j = i+1; j < cnt; j++) {
-                       if (!rslt[i] || !rslt[j])
-                               continue;
-                       result = merge_bases_many(rslt[i], 1, &rslt[j]);
-                       clear_commit_marks(rslt[i], all_flags);
-                       clear_commit_marks(rslt[j], all_flags);
-                       for (list = result; list; list = list->next) {
-                               if (rslt[i] == list->item)
-                                       rslt[i] = NULL;
-                               if (rslt[j] == list->item)
-                                       rslt[j] = NULL;
-                       }
-               }
-       }
 
-       /* Surviving ones in rslt[] are the independent results */
+       cnt = remove_redundant(rslt, cnt);
        result = NULL;
-       for (i = 0; i < cnt; i++) {
-               if (rslt[i])
-                       commit_list_insert_by_date(rslt[i], &result);
-       }
+       for (i = 0; i < cnt; i++)
+               commit_list_insert_by_date(rslt[i], &result);
        free(rslt);
        return result;
 }
@@ -780,6 +826,9 @@ struct commit_list *get_merge_bases(struct commit *one, struct commit *two,
        return get_merge_bases_many(one, 1, &two, cleanup);
 }
 
+/*
+ * Is "commit" a decendant of one of the elements on the "with_commit" list?
+ */
 int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
 {
        if (!with_commit)
@@ -789,28 +838,28 @@ int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
 
                other = with_commit->item;
                with_commit = with_commit->next;
-               if (in_merge_bases(other, &commit, 1))
+               if (in_merge_bases(other, commit))
                        return 1;
        }
        return 0;
 }
 
-int in_merge_bases(struct commit *commit, struct commit **reference, int num)
+/*
+ * Is "commit" an ancestor of (i.e. reachable from) the "reference"?
+ */
+int in_merge_bases(struct commit *commit, struct commit *reference)
 {
-       struct commit_list *bases, *b;
+       struct commit_list *bases;
        int ret = 0;
 
-       if (num == 1)
-               bases = get_merge_bases(commit, *reference, 1);
-       else
-               die("not yet");
-       for (b = bases; b; b = b->next) {
-               if (!hashcmp(commit->object.sha1, b->item->object.sha1)) {
-                       ret = 1;
-                       break;
-               }
-       }
+       if (parse_commit(commit) || parse_commit(reference))
+               return ret;
 
+       bases = paint_down_to_common(commit, 1, &reference);
+       if (commit->object.flags & PARENT2)
+               ret = 1;
+       clear_commit_marks(commit, all_flags);
+       clear_commit_marks(reference, all_flags);
        free_commit_list(bases);
        return ret;
 }
@@ -819,51 +868,31 @@ struct commit_list *reduce_heads(struct commit_list *heads)
 {
        struct commit_list *p;
        struct commit_list *result = NULL, **tail = &result;
-       struct commit **other;
-       size_t num_head, num_other;
+       struct commit **array;
+       int num_head, i;
 
        if (!heads)
                return NULL;
 
-       /* Avoid unnecessary reallocations */
-       for (p = heads, num_head = 0; p; p = p->next)
-               num_head++;
-       other = xcalloc(sizeof(*other), num_head);
-
-       /* For each commit, see if it can be reached by others */
-       for (p = heads; p; p = p->next) {
-               struct commit_list *q, *base;
-
-               /* Do we already have this in the result? */
-               for (q = result; q; q = q->next)
-                       if (p->item == q->item)
-                               break;
-               if (q)
+       /* Uniquify */
+       for (p = heads; p; p = p->next)
+               p->item->object.flags &= ~STALE;
+       for (p = heads, num_head = 0; p; p = p->next) {
+               if (p->item->object.flags & STALE)
                        continue;
-
-               num_other = 0;
-               for (q = heads; q; q = q->next) {
-                       if (p->item == q->item)
-                               continue;
-                       other[num_other++] = q->item;
+               p->item->object.flags |= STALE;
+               num_head++;
+       }
+       array = xcalloc(sizeof(*array), num_head);
+       for (p = heads, i = 0; p; p = p->next) {
+               if (p->item->object.flags & STALE) {
+                       array[i++] = p->item;
+                       p->item->object.flags &= ~STALE;
                }
-               if (num_other)
-                       base = get_merge_bases_many(p->item, num_other, other, 1);
-               else
-                       base = NULL;
-               /*
-                * If p->item does not have anything common with other
-                * commits, there won't be any merge base.  If it is
-                * reachable from some of the others, p->item will be
-                * the merge base.  If its history is connected with
-                * others, but p->item is not reachable by others, we
-                * will get something other than p->item back.
-                */
-               if (!base || (base->item != p->item))
-                       tail = &(commit_list_insert(p->item, tail)->next);
-               free_commit_list(base);
        }
-       free(other);
+       num_head = remove_redundant(array, num_head);
+       for (i = 0; i < num_head; i++)
+               tail = &commit_list_insert(array[i], tail)->next;
        return result;
 }
 
@@ -1112,8 +1141,92 @@ int commit_tree(const struct strbuf *msg, unsigned char *tree,
        return result;
 }
 
+static int find_invalid_utf8(const char *buf, int len)
+{
+       int offset = 0;
+
+       while (len) {
+               unsigned char c = *buf++;
+               int bytes, bad_offset;
+
+               len--;
+               offset++;
+
+               /* Simple US-ASCII? No worries. */
+               if (c < 0x80)
+                       continue;
+
+               bad_offset = offset-1;
+
+               /*
+                * Count how many more high bits set: that's how
+                * many more bytes this sequence should have.
+                */
+               bytes = 0;
+               while (c & 0x40) {
+                       c <<= 1;
+                       bytes++;
+               }
+
+               /* Must be between 1 and 5 more bytes */
+               if (bytes < 1 || bytes > 5)
+                       return bad_offset;
+
+               /* Do we *have* that many bytes? */
+               if (len < bytes)
+                       return bad_offset;
+
+               offset += bytes;
+               len -= bytes;
+
+               /* And verify that they are good continuation bytes */
+               do {
+                       if ((*buf++ & 0xc0) != 0x80)
+                               return bad_offset;
+               } while (--bytes);
+
+               /* We could/should check the value and length here too */
+       }
+       return -1;
+}
+
+/*
+ * This verifies that the buffer is in proper utf8 format.
+ *
+ * If it isn't, it assumes any non-utf8 characters are Latin1,
+ * and does the conversion.
+ *
+ * Fixme: we should probably also disallow overlong forms and
+ * invalid characters. But we don't do that currently.
+ */
+static int verify_utf8(struct strbuf *buf)
+{
+       int ok = 1;
+       long pos = 0;
+
+       for (;;) {
+               int bad;
+               unsigned char c;
+               unsigned char replace[2];
+
+               bad = find_invalid_utf8(buf->buf + pos, buf->len - pos);
+               if (bad < 0)
+                       return ok;
+               pos += bad;
+               ok = 0;
+               c = buf->buf[pos];
+               strbuf_remove(buf, pos, 1);
+
+               /* We know 'c' must be in the range 128-255 */
+               replace[0] = 0xc0 + (c >> 6);
+               replace[1] = 0x80 + (c & 0x3f);
+               strbuf_insert(buf, pos, replace, 2);
+               pos += 2;
+       }
+}
+
 static const char commit_utf8_warn[] =
-"Warning: commit message does not conform to UTF-8.\n"
+"Warning: commit message did not conform to UTF-8.\n"
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
@@ -1170,7 +1283,7 @@ int commit_tree_extended(const struct strbuf *msg, unsigned char *tree,
        strbuf_addbuf(&buffer, msg);
 
        /* And check the encoding */
-       if (encoding_is_utf8 && !is_utf8(buffer.buf))
+       if (encoding_is_utf8 && !verify_utf8(&buffer))
                fprintf(stderr, commit_utf8_warn);
 
        if (sign_commit && do_sign_commit(&buffer, sign_commit))
index d617fa3f28d9fecf0bbf8e87c373f8701c286227..6edce876739672b4fde2b6970e7ff55b8a921743 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -171,7 +171,7 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
 
 int is_descendant_of(struct commit *, struct commit_list *);
-int in_merge_bases(struct commit *, struct commit **, int);
+int in_merge_bases(struct commit *, struct commit *);
 
 extern int interactive_add(int argc, const char **argv, const char *prefix, int patch);
 extern int run_add_interactive(const char *revision, const char *patch_mode,
diff --git a/compat/mkdir.c b/compat/mkdir.c
new file mode 100644 (file)
index 0000000..9e253fb
--- /dev/null
@@ -0,0 +1,24 @@
+#include "../git-compat-util.h"
+#undef mkdir
+
+/* for platforms that can't deal with a trailing '/' */
+int compat_mkdir_wo_trailing_slash(const char *dir, mode_t mode)
+{
+       int retval;
+       char *tmp_dir = NULL;
+       size_t len = strlen(dir);
+
+       if (len && dir[len-1] == '/') {
+               if ((tmp_dir = strdup(dir)) == NULL)
+                       return -1;
+               tmp_dir[len-1] = '\0';
+       }
+       else
+               tmp_dir = (char *)dir;
+
+       retval = mkdir(tmp_dir, mode);
+       if (tmp_dir != dir)
+               free(tmp_dir);
+
+       return retval;
+}
index d40d1b38076d690faaf7e77e590c0d6245ad1139..8cf59558e1c6fcc6782b8eaf94534ea041599191 100644 (file)
@@ -1,8 +1,7 @@
 /*
  * Converts filenames from decomposed unicode into precomposed unicode.
  * Used on MacOS X.
-*/
-
+ */
 
 #define PRECOMPOSE_UNICODE_C
 
 #include "precompose_utf8.h"
 
 typedef char *iconv_ibp;
-const static char *repo_encoding = "UTF-8";
-const static char *path_encoding = "UTF-8-MAC";
-
+static const char *repo_encoding = "UTF-8";
+static const char *path_encoding = "UTF-8-MAC";
 
-static size_t has_utf8(const char *s, size_t maxlen, size_t *strlen_c)
+static size_t has_non_ascii(const char *s, size_t maxlen, size_t *strlen_c)
 {
-       const uint8_t *utf8p = (const uint8_t*) s;
+       const uint8_t *ptr = (const uint8_t *)s;
        size_t strlen_chars = 0;
        size_t ret = 0;
 
-       if ((!utf8p) || (!*utf8p)) {
+       if (!ptr || !*ptr)
                return 0;
-       }
 
-       while((*utf8p) && maxlen) {
-               if (*utf8p & 0x80)
+       while (*ptr && maxlen) {
+               if (*ptr & 0x80)
                        ret++;
                strlen_chars++;
-               utf8p++;
+               ptr++;
                maxlen--;
        }
        if (strlen_c)
@@ -41,26 +38,24 @@ static size_t has_utf8(const char *s, size_t maxlen, size_t *strlen_c)
 
 void probe_utf8_pathname_composition(char *path, int len)
 {
-       const static char *auml_nfc = "\xc3\xa4";
-       const static char *auml_nfd = "\x61\xcc\x88";
+       static const char *auml_nfc = "\xc3\xa4";
+       static const char *auml_nfd = "\x61\xcc\x88";
        int output_fd;
        if (precomposed_unicode != -1)
                return; /* We found it defined in the global config, respect it */
-       path[len] = 0;
        strcpy(path + len, auml_nfc);
        output_fd = open(path, O_CREAT|O_EXCL|O_RDWR, 0600);
-       if (output_fd >=0) {
+       if (output_fd >= 0) {
                close(output_fd);
-               path[len] = 0;
                strcpy(path + len, auml_nfd);
                /* Indicate to the user, that we can configure it to true */
-               if (0 == access(path, R_OK))
+               if (!access(path, R_OK))
                        git_config_set("core.precomposeunicode", "false");
-                       /* To be backward compatible, set precomposed_unicode to 0 */
+               /* To be backward compatible, set precomposed_unicode to 0 */
                precomposed_unicode = 0;
-               path[len] = 0;
                strcpy(path + len, auml_nfc);
-               unlink(path);
+               if (unlink(path))
+                       die_errno(_("failed to unlink '%s'"), path);
        }
 }
 
@@ -82,7 +77,7 @@ void precompose_argv(int argc, const char **argv)
        while (i < argc) {
                size_t namelen;
                oldarg = argv[i];
-               if (has_utf8(oldarg, (size_t)-1, &namelen)) {
+               if (has_non_ascii(oldarg, (size_t)-1, &namelen)) {
                        newarg = reencode_string_iconv(oldarg, namelen, ic_precompose);
                        if (newarg)
                                argv[i] = newarg;
@@ -135,7 +130,7 @@ struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)
                prec_dir->dirent_nfc->d_ino  = res->d_ino;
                prec_dir->dirent_nfc->d_type = res->d_type;
 
-               if ((precomposed_unicode == 1) && has_utf8(res->d_name, (size_t)-1, NULL)) {
+               if ((precomposed_unicode == 1) && has_non_ascii(res->d_name, (size_t)-1, NULL)) {
                        if (prec_dir->ic_precompose == (iconv_t)-1) {
                                die("iconv_open(%s,%s) failed, but needed:\n"
                                                "    precomposed unicode is not supported.\n"
@@ -160,8 +155,7 @@ struct dirent_prec_psx *precompose_utf8_readdir(PREC_DIR *prec_dir)
                                        namelenz = 0; /* trigger strlcpy */
                                }
                        }
-               }
-               else
+               } else
                        namelenz = 0;
 
                if (!namelenz)
index 2b706ea2053714fdcec8997d978ce5688d072941..08e47e2e48a5bd310025752ee5ecdce81721b34d 100644 (file)
--- a/config.c
+++ b/config.c
@@ -60,7 +60,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
                path = buf.buf;
        }
 
-       if (!access(path, R_OK)) {
+       if (!access_or_warn(path, R_OK)) {
                if (++inc->depth > MAX_INCLUDE_DEPTH)
                        die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
                            cf && cf->name ? cf->name : "the command line");
@@ -939,23 +939,23 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
 
        home_config_paths(&user_config, &xdg_config, "config");
 
-       if (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {
+       if (git_config_system() && !access_or_warn(git_etc_gitconfig(), R_OK)) {
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
                found += 1;
        }
 
-       if (xdg_config && !access(xdg_config, R_OK)) {
+       if (xdg_config && !access_or_warn(xdg_config, R_OK)) {
                ret += git_config_from_file(fn, xdg_config, data);
                found += 1;
        }
 
-       if (user_config && !access(user_config, R_OK)) {
+       if (user_config && !access_or_warn(user_config, R_OK)) {
                ret += git_config_from_file(fn, user_config, data);
                found += 1;
        }
 
-       if (repo_config && !access(repo_config, R_OK)) {
+       if (repo_config && !access_or_warn(repo_config, R_OK)) {
                ret += git_config_from_file(fn, repo_config, data);
                found += 1;
        }
index 55a85adea94c87c912280079ee13afd6926901aa..49e56ba35a645359b0d7c1f7bbc9e2108b3424d9 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -115,12 +115,7 @@ struct ref **get_remote_heads(int in, struct ref **list,
        return list;
 }
 
-int server_supports(const char *feature)
-{
-       return !!parse_feature_request(server_capabilities, feature);
-}
-
-const char *parse_feature_request(const char *feature_list, const char *feature)
+const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
 {
        int len;
 
@@ -132,14 +127,46 @@ const char *parse_feature_request(const char *feature_list, const char *feature)
                const char *found = strstr(feature_list, feature);
                if (!found)
                        return NULL;
-               if ((feature_list == found || isspace(found[-1])) &&
-                   (!found[len] || isspace(found[len]) || found[len] == '='))
-                       return found;
+               if (feature_list == found || isspace(found[-1])) {
+                       const char *value = found + len;
+                       /* feature with no value (e.g., "thin-pack") */
+                       if (!*value || isspace(*value)) {
+                               if (lenp)
+                                       *lenp = 0;
+                               return value;
+                       }
+                       /* feature with a value (e.g., "agent=git/1.2.3") */
+                       else if (*value == '=') {
+                               value++;
+                               if (lenp)
+                                       *lenp = strcspn(value, " \t\n");
+                               return value;
+                       }
+                       /*
+                        * otherwise we matched a substring of another feature;
+                        * keep looking
+                        */
+               }
                feature_list = found + 1;
        }
        return NULL;
 }
 
+int parse_feature_request(const char *feature_list, const char *feature)
+{
+       return !!parse_feature_value(feature_list, feature, NULL);
+}
+
+const char *server_feature_value(const char *feature, int *len)
+{
+       return parse_feature_value(server_capabilities, feature, len);
+}
+
+int server_supports(const char *feature)
+{
+       return !!server_feature_value(feature, NULL);
+}
+
 enum protocol {
        PROTO_LOCAL = 1,
        PROTO_SSH,
diff --git a/contrib/ciabot/INSTALL b/contrib/ciabot/INSTALL
new file mode 100644 (file)
index 0000000..7222961
--- /dev/null
@@ -0,0 +1,54 @@
+= Installation instructions =
+
+Two scripts are included.  The Python one (ciabot.py) is faster and
+more capable; the shell one (ciabot.sh) is a fallback in case Python
+gives your git hosting site indigestion. (I know of no such sites.)
+
+It is no longer necessary to modify the script in order to put it
+in place; in fact, this is now discouraged. It is entirely
+configurable with the following git config variables:
+
+ciabot.project = name of the project
+ciabot.repo = name of the project repo for gitweb/cgit purposes
+ciabot.xmlrpc  = if true, ship notifications via XML-RPC
+ciabot.revformat = format in which the revision is shown
+
+The revformat variable may have the following values
+raw -> full hex ID of commit
+short -> first 12 chars of hex ID
+describe -> describe relative to last tag, falling back to short
+
+ciabot.project defaults to the directory name of the repository toplevel.
+ciabot.repo defaults to ciabot.project lowercased.
+ciabot.xmlrpc defaults to True
+ciabot.revformat defaults to 'describe'.
+
+This means that in the normal case you need not do any configuration at all,
+however setting ciabot.project will allow the hook to run slightly faster.
+
+Once you've set these variables, try your script with -n to see the
+notification message dumped to stdout and verify that it looks sane.
+
+To live-test these scripts, your project needs to have been registered with
+the CIA site.  Here are the steps:
+
+1. Open an IRC window on irc://freenode/commits or your registered
+   project IRC channel.
+
+2. Run ciabot.py and/or ciabot.sh from any directory under git
+   control.
+
+You should see a notification on the channel for your most recent commit.
+
+After verifying correct function, install one of these scripts either
+in a post-commit hook or in an update hook.
+
+In post-commit, run it without arguments. It will query for
+current HEAD and the latest commit ID to get the information it
+needs.
+
+In update, call it with a refname followed by a list of commits:
+You want to reverse the order git rev-list emits because it lists
+from most recent to oldest.
+
+/path/to/ciabot.py ${refname} $(git rev-list ${oldhead}..${newhead} | tac)
index 3b916acece20d521a9fbefea6089b9a9173adbf1..2dfe1f91f5f0ace6b8e190979ee604d13d1861b5 100644 (file)
@@ -8,5 +8,4 @@ You probably want the Python version; it's faster, more capable, and
 better documented.  The shell version is maintained only as a fallback
 for use on hosting sites that don't permit Python hook scripts.
 
-You will find installation instructions for each script in its comment
-header.
+See the file INSTALL for installation instructions.
index 8ce04eb9d26b02806cdee21d40050677b78b09d1..bd24395d4cf404f886803892d0be98c964a43ce7 100755 (executable)
 # usage: ciabot.py [-V] [-n] [-p projectname]  [refname [commits...]]
 #
 # This script is meant to be run either in a post-commit hook or in an
-# update hook.  If there's nothing unusual about your hosting setup,
-# you can specify the project name and repo with config variables and
-# avoid having to modify this script.  Try it with -n to see the
-# notification mail dumped to stdout and verify that it looks
-# sane. With -V it dumps its version and exits.
+# update hook. Try it with -n to see the notification mail dumped to
+# stdout and verify that it looks sane. With -V it dumps its version
+# and exits.
 #
 # In post-commit, run it without arguments. It will query for
 # current HEAD and the latest commit ID to get the information it
 # /path/to/ciabot.py ${refname} $(git rev-list ${oldhead}..${newhead} | tac)
 #
 # Configuration variables affecting this script:
-# ciabot.project = name of the project (required)
+#
+# ciabot.project = name of the project
 # ciabot.repo = name of the project repo for gitweb/cgit purposes
 # ciabot.xmlrpc  = if true (default), ship notifications via XML-RPC
 # ciabot.revformat = format in which the revision is shown
 #
-# The ciabot.repo value defaults to ciabot.project lowercased.
+# ciabot.project defaults to the directory name of the repository toplevel.
+# ciabot.repo defaults to ciabot.project lowercased.
+#
+# This means that in the normal case you need not do any configuration at all,
+# but setting the project name will speed it up slightly.
 #
 # The revformat variable may have the following values
 # raw -> full hex ID of commit
 # Identify the generator script.
 # Should only change when the script itself gets a new home and maintainer.
 generator = "http://www.catb.org/~esr/ciabot.py"
-version = "3.5"
+version = "3.6"
 
 def do(command):
     return commands.getstatusoutput(command)[1]
@@ -192,10 +195,17 @@ def report(refname, merged, xmlrpc=True):
             print "ciabot.py: version", version
             sys.exit(0)
 
-    # Cough and die if user has not specified a project
+    # The project variable defaults to the name of the repository toplevel.
     if not project:
-        sys.stderr.write("ciabot.py: no project specified, bailing out.\n")
-        sys.exit(1)
+        here = os.getcwd()
+        while True:
+            if os.path.exists(os.path.join(here, ".git")):
+                project = os.path.basename(here)
+                break
+            elif here == '/':
+                sys.stderr.write("ciabot.py: no .git below root!\n")
+                sys.exit(1)
+            here = os.path.dirname(here)
 
     if not repo:
         repo = project.lower()
index dde74004cb439e34bb875c3020d3ba20fffb6058..3fbbc534ae668b979132f6df60c936314917d1f0 100755 (executable)
 # usage: ciabot.sh [-V] [-n] [-p projectname] [refname commit]
 #
 # This script is meant to be run either in a post-commit hook or in an
-# update hook.  If there's nothing unusual about your hosting setup,
-# you can specify the project name and repo with config variables and
-# avoid having to modify this script.  Try it with -n to see the
-# notification mail dumped to stdout and verify that it looks
-# sane. With -V it dumps its version and exits.
+# update hook. Try it with -n to see the notification mail dumped to
+# stdout and verify that it looks sane. With -V it dumps its version
+# and exits.
 #
 # In post-commit, run it without arguments. It will query for
 # current HEAD and the latest commit ID to get the information it
 # most recent to least - better to ship notifactions from oldest to newest.
 #
 # Configuration variables affecting this script:
-# ciabot.project = name of the project (makes -p option unnecessary)
+#
+# ciabot.project = name of the project
 # ciabot.repo = name of the project repo for gitweb/cgit purposes
 # ciabot.revformat = format in which the revision is shown
 #
-# The ciabot.repo defaults to ciabot.project lowercased.
+# ciabot.project defaults to the directory name of the repository toplevel.
+# ciabot.repo defaults to ciabot.project lowercased.
+#
+# This means that in the normal case you need not do any configuration at all,
+# but setting the project name will speed it up slightly.
 #
 # The revformat variable may have the following values
 # raw -> full hex ID of commit
 # shpped from an update in their actual order.)
 #
 
-# The project as known to CIA. You can also hardwire this or set it with a
-# -p option.
+# The project as known to CIA. You can set this with a -p option,
+# or let it default to the directory name of the repo toplevel.
 project=$(git config --get ciabot.project)
 
+if [ -z $project ]
+then
+    here=`pwd`;
+    while :; do
+       if [ -d $here/.git ]
+       then
+           project=`basename $here`
+           break
+       elif [ $here = '/' ]
+       then
+           echo "ciabot.sh: no .git below root!"
+           exit 1
+       fi
+       here=`dirname $here`
+    done
+fi
+
 # Name of the repo for gitweb/cgit purposes
 repo=$(git config --get ciabot.repo)
 [ -z $repo] && repo=$(echo "${project}" | tr '[A-Z]' '[a-z]')
@@ -100,7 +120,7 @@ urlprefix="http://${host}/cgi-bin/cgit.cgi/${repo}/commit/?id="
 # Identify the script. The 'generator' variable should change only
 # when the script itself gets a new home and maintainer.
 generator="http://www.catb.org/~esr/ciabot/ciabot.sh"
-version=3.4
+version=3.5
 
 # Addresses for the e-mail
 from="CIABOT-NOREPLY@${hostname}"
index ffedce751c80eac032843f6ecad50246cfc28035..222b804ce4d5314b7eaff73f51291cff3903662d 100644 (file)
@@ -1071,7 +1071,7 @@ _git_diff ()
 }
 
 __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
-                       tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3
+                       tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc3 codecompare
 "
 
 _git_difftool ()
diff --git a/contrib/credential/gnome-keyring/.gitignore b/contrib/credential/gnome-keyring/.gitignore
new file mode 100644 (file)
index 0000000..88d8fcd
--- /dev/null
@@ -0,0 +1 @@
+git-credential-gnome-keyring
diff --git a/contrib/credential/gnome-keyring/Makefile b/contrib/credential/gnome-keyring/Makefile
new file mode 100644 (file)
index 0000000..e6561d8
--- /dev/null
@@ -0,0 +1,24 @@
+MAIN:=git-credential-gnome-keyring
+all:: $(MAIN)
+
+CC = gcc
+RM = rm -f
+CFLAGS = -g -O2 -Wall
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+INCS:=$(shell pkg-config --cflags gnome-keyring-1)
+LIBS:=$(shell pkg-config --libs gnome-keyring-1)
+
+SRCS:=$(MAIN).c
+OBJS:=$(SRCS:.c=.o)
+
+%.o: %.c
+       $(CC) $(CFLAGS) $(CPPFLAGS) $(INCS) -o $@ -c $<
+
+$(MAIN): $(OBJS)
+       $(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
+
+clean:
+       @$(RM) $(MAIN) $(OBJS)
diff --git a/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c b/contrib/credential/gnome-keyring/git-credential-gnome-keyring.c
new file mode 100644 (file)
index 0000000..41f61c5
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2011 John Szakmeister <john@szakmeister.net>
+ *               2012 Philipp A. Hartmann <pah@qo.cx>
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * Credits:
+ * - GNOME Keyring API handling originally written by John Szakmeister
+ * - ported to credential helper API by Philipp A. Hartmann
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <gnome-keyring.h>
+
+/*
+ * This credential struct and API is simplified from git's credential.{h,c}
+ */
+struct credential
+{
+       char          *protocol;
+       char          *host;
+       unsigned short port;
+       char          *path;
+       char          *username;
+       char          *password;
+};
+
+#define CREDENTIAL_INIT \
+  { NULL,NULL,0,NULL,NULL,NULL }
+
+void credential_init(struct credential *c);
+void credential_clear(struct credential *c);
+int  credential_read(struct credential *c);
+void credential_write(const struct credential *c);
+
+typedef int (*credential_op_cb)(struct credential*);
+
+struct credential_operation
+{
+       char             *name;
+       credential_op_cb op;
+};
+
+#define CREDENTIAL_OP_END \
+  { NULL,NULL }
+
+/*
+ * Table with operation callbacks is defined in concrete
+ * credential helper implementation and contains entries
+ * like { "get", function_to_get_credential } terminated
+ * by CREDENTIAL_OP_END.
+ */
+struct credential_operation const credential_helper_ops[];
+
+/* ---------------- common helper functions ----------------- */
+
+static inline void free_password(char *password)
+{
+       char *c = password;
+       if (!password)
+               return;
+
+       while (*c) *c++ = '\0';
+       free(password);
+}
+
+static inline void warning(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       fprintf(stderr, "warning: ");
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n" );
+       va_end(ap);
+}
+
+static inline void error(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       fprintf(stderr, "error: ");
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n" );
+       va_end(ap);
+}
+
+static inline void die(const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap,fmt);
+       error(fmt, ap);
+       va_end(ap);
+       exit(EXIT_FAILURE);
+}
+
+static inline void die_errno(int err)
+{
+       error("%s", strerror(err));
+       exit(EXIT_FAILURE);
+}
+
+static inline char *xstrdup(const char *str)
+{
+       char *ret = strdup(str);
+       if (!ret)
+               die_errno(errno);
+
+       return ret;
+}
+
+/* ----------------- GNOME Keyring functions ----------------- */
+
+/* create a special keyring option string, if path is given */
+static char* keyring_object(struct credential *c)
+{
+       char* object = NULL;
+
+       if (!c->path)
+               return object;
+
+       object = (char*) malloc(strlen(c->host)+strlen(c->path)+8);
+       if(!object)
+               die_errno(errno);
+
+       if(c->port)
+               sprintf(object,"%s:%hd/%s",c->host,c->port,c->path);
+       else
+               sprintf(object,"%s/%s",c->host,c->path);
+
+       return object;
+}
+
+int keyring_get(struct credential *c)
+{
+       char* object = NULL;
+       GList *entries;
+       GnomeKeyringNetworkPasswordData *password_data;
+       GnomeKeyringResult result;
+
+       if (!c->protocol || !(c->host || c->path))
+               return EXIT_FAILURE;
+
+       object = keyring_object(c);
+
+       result = gnome_keyring_find_network_password_sync(
+                               c->username,
+                               NULL /* domain */,
+                               c->host,
+                               object,
+                               c->protocol,
+                               NULL /* authtype */,
+                               c->port,
+                               &entries);
+
+       free(object);
+
+       if (result == GNOME_KEYRING_RESULT_NO_MATCH)
+               return EXIT_SUCCESS;
+
+       if (result == GNOME_KEYRING_RESULT_CANCELLED)
+               return EXIT_SUCCESS;
+
+       if (result != GNOME_KEYRING_RESULT_OK) {
+               error("%s",gnome_keyring_result_to_message(result));
+               return EXIT_FAILURE;
+       }
+
+       /* pick the first one from the list */
+       password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
+
+       free_password(c->password);
+       c->password = xstrdup(password_data->password);
+
+       if (!c->username)
+               c->username = xstrdup(password_data->user);
+
+       gnome_keyring_network_password_list_free(entries);
+
+       return EXIT_SUCCESS;
+}
+
+
+int keyring_store(struct credential *c)
+{
+       guint32 item_id;
+       char  *object = NULL;
+
+       /*
+        * Sanity check that what we are storing is actually sensible.
+        * In particular, we can't make a URL without a protocol field.
+        * Without either a host or pathname (depending on the scheme),
+        * we have no primary key. And without a username and password,
+        * we are not actually storing a credential.
+        */
+       if (!c->protocol || !(c->host || c->path) ||
+           !c->username || !c->password)
+               return EXIT_FAILURE;
+
+       object = keyring_object(c);
+
+       gnome_keyring_set_network_password_sync(
+                               GNOME_KEYRING_DEFAULT,
+                               c->username,
+                               NULL /* domain */,
+                               c->host,
+                               object,
+                               c->protocol,
+                               NULL /* authtype */,
+                               c->port,
+                               c->password,
+                               &item_id);
+
+       free(object);
+       return EXIT_SUCCESS;
+}
+
+int keyring_erase(struct credential *c)
+{
+       char  *object = NULL;
+       GList *entries;
+       GnomeKeyringNetworkPasswordData *password_data;
+       GnomeKeyringResult result;
+
+       /*
+        * Sanity check that we actually have something to match
+        * against. The input we get is a restrictive pattern,
+        * so technically a blank credential means "erase everything".
+        * But it is too easy to accidentally send this, since it is equivalent
+        * to empty input. So explicitly disallow it, and require that the
+        * pattern have some actual content to match.
+        */
+       if (!c->protocol && !c->host && !c->path && !c->username)
+               return EXIT_FAILURE;
+
+       object = keyring_object(c);
+
+       result = gnome_keyring_find_network_password_sync(
+                               c->username,
+                               NULL /* domain */,
+                               c->host,
+                               object,
+                               c->protocol,
+                               NULL /* authtype */,
+                               c->port,
+                               &entries);
+
+       free(object);
+
+       if (result == GNOME_KEYRING_RESULT_NO_MATCH)
+               return EXIT_SUCCESS;
+
+       if (result == GNOME_KEYRING_RESULT_CANCELLED)
+               return EXIT_SUCCESS;
+
+       if (result != GNOME_KEYRING_RESULT_OK)
+       {
+               error("%s",gnome_keyring_result_to_message(result));
+               return EXIT_FAILURE;
+       }
+
+       /* pick the first one from the list (delete all matches?) */
+       password_data = (GnomeKeyringNetworkPasswordData *) entries->data;
+
+       result = gnome_keyring_item_delete_sync(
+               password_data->keyring, password_data->item_id);
+
+       gnome_keyring_network_password_list_free(entries);
+
+       if (result != GNOME_KEYRING_RESULT_OK)
+       {
+               error("%s",gnome_keyring_result_to_message(result));
+               return EXIT_FAILURE;
+       }
+
+       return EXIT_SUCCESS;
+}
+
+/*
+ * Table with helper operation callbacks, used by generic
+ * credential helper main function.
+ */
+struct credential_operation const credential_helper_ops[] =
+{
+       { "get",   keyring_get   },
+       { "store", keyring_store },
+       { "erase", keyring_erase },
+       CREDENTIAL_OP_END
+};
+
+/* ------------------ credential functions ------------------ */
+
+void credential_init(struct credential *c)
+{
+       memset(c, 0, sizeof(*c));
+}
+
+void credential_clear(struct credential *c)
+{
+       free(c->protocol);
+       free(c->host);
+       free(c->path);
+       free(c->username);
+       free_password(c->password);
+
+       credential_init(c);
+}
+
+int credential_read(struct credential *c)
+{
+       char    buf[1024];
+       ssize_t line_len = 0;
+       char   *key      = buf;
+       char   *value;
+
+       while (fgets(buf, sizeof(buf), stdin))
+       {
+               line_len = strlen(buf);
+
+               if(buf[line_len-1]=='\n')
+                       buf[--line_len]='\0';
+
+               if(!line_len)
+                       break;
+
+               value = strchr(buf,'=');
+               if(!value) {
+                       warning("invalid credential line: %s", key);
+                       return -1;
+               }
+               *value++ = '\0';
+
+               if (!strcmp(key, "protocol")) {
+                       free(c->protocol);
+                       c->protocol = xstrdup(value);
+               } else if (!strcmp(key, "host")) {
+                       free(c->host);
+                       c->host = xstrdup(value);
+                       value = strrchr(c->host,':');
+                       if (value) {
+                               *value++ = '\0';
+                               c->port = atoi(value);
+                       }
+               } else if (!strcmp(key, "path")) {
+                       free(c->path);
+                       c->path = xstrdup(value);
+               } else if (!strcmp(key, "username")) {
+                       free(c->username);
+                       c->username = xstrdup(value);
+               } else if (!strcmp(key, "password")) {
+                       free_password(c->password);
+                       c->password = xstrdup(value);
+                       while (*value) *value++ = '\0';
+               }
+               /*
+                * Ignore other lines; we don't know what they mean, but
+                * this future-proofs us when later versions of git do
+                * learn new lines, and the helpers are updated to match.
+                */
+       }
+       return 0;
+}
+
+void credential_write_item(FILE *fp, const char *key, const char *value)
+{
+       if (!value)
+               return;
+       fprintf(fp, "%s=%s\n", key, value);
+}
+
+void credential_write(const struct credential *c)
+{
+       /* only write username/password, if set */
+       credential_write_item(stdout, "username", c->username);
+       credential_write_item(stdout, "password", c->password);
+}
+
+static void usage(const char *name)
+{
+       struct credential_operation const *try_op = credential_helper_ops;
+       const char *basename = strrchr(name,'/');
+
+       basename = (basename) ? basename + 1 : name;
+       fprintf(stderr, "Usage: %s <", basename);
+       while(try_op->name) {
+               fprintf(stderr,"%s",(try_op++)->name);
+               if(try_op->name)
+                       fprintf(stderr,"%s","|");
+       }
+       fprintf(stderr,"%s",">\n");
+}
+
+int main(int argc, char *argv[])
+{
+       int ret = EXIT_SUCCESS;
+
+       struct credential_operation const *try_op = credential_helper_ops;
+       struct credential                  cred   = CREDENTIAL_INIT;
+
+       if (!argv[1]) {
+               usage(argv[0]);
+               goto out;
+       }
+
+       /* lookup operation callback */
+       while(try_op->name && strcmp(argv[1], try_op->name))
+               try_op++;
+
+       /* unsupported operation given -- ignore silently */
+       if(!try_op->name || !try_op->op)
+               goto out;
+
+       ret = credential_read(&cred);
+       if(ret)
+               goto out;
+
+       /* perform credential operation */
+       ret = (*try_op->op)(&cred);
+
+       credential_write(&cred);
+
+out:
+       credential_clear(&cred);
+       return ret;
+}
diff --git a/contrib/credential/wincred/Makefile b/contrib/credential/wincred/Makefile
new file mode 100644 (file)
index 0000000..bad45ca
--- /dev/null
@@ -0,0 +1,14 @@
+all: git-credential-wincred.exe
+
+CC = gcc
+RM = rm -f
+CFLAGS = -O2 -Wall
+
+-include ../../../config.mak.autogen
+-include ../../../config.mak
+
+git-credential-wincred.exe : git-credential-wincred.c
+       $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+clean:
+       $(RM) git-credential-wincred.exe
diff --git a/contrib/credential/wincred/git-credential-wincred.c b/contrib/credential/wincred/git-credential-wincred.c
new file mode 100644 (file)
index 0000000..cbaec5f
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * A git credential helper that interface with Windows' Credential Manager
+ *
+ */
+#include <windows.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+
+/* common helpers */
+
+static void die(const char *err, ...)
+{
+       char msg[4096];
+       va_list params;
+       va_start(params, err);
+       vsnprintf(msg, sizeof(msg), err, params);
+       fprintf(stderr, "%s\n", msg);
+       va_end(params);
+       exit(1);
+}
+
+static void *xmalloc(size_t size)
+{
+       void *ret = malloc(size);
+       if (!ret && !size)
+               ret = malloc(1);
+       if (!ret)
+                die("Out of memory");
+       return ret;
+}
+
+static char *xstrdup(const char *str)
+{
+       char *ret = strdup(str);
+       if (!ret)
+               die("Out of memory");
+       return ret;
+}
+
+/* MinGW doesn't have wincred.h, so we need to define stuff */
+
+typedef struct _CREDENTIAL_ATTRIBUTEW {
+       LPWSTR Keyword;
+       DWORD  Flags;
+       DWORD  ValueSize;
+       LPBYTE Value;
+} CREDENTIAL_ATTRIBUTEW, *PCREDENTIAL_ATTRIBUTEW;
+
+typedef struct _CREDENTIALW {
+       DWORD                  Flags;
+       DWORD                  Type;
+       LPWSTR                 TargetName;
+       LPWSTR                 Comment;
+       FILETIME               LastWritten;
+       DWORD                  CredentialBlobSize;
+       LPBYTE                 CredentialBlob;
+       DWORD                  Persist;
+       DWORD                  AttributeCount;
+       PCREDENTIAL_ATTRIBUTEW Attributes;
+       LPWSTR                 TargetAlias;
+       LPWSTR                 UserName;
+} CREDENTIALW, *PCREDENTIALW;
+
+#define CRED_TYPE_GENERIC 1
+#define CRED_PERSIST_LOCAL_MACHINE 2
+#define CRED_MAX_ATTRIBUTES 64
+
+typedef BOOL (WINAPI *CredWriteWT)(PCREDENTIALW, DWORD);
+typedef BOOL (WINAPI *CredUnPackAuthenticationBufferWT)(DWORD, PVOID, DWORD,
+    LPWSTR, DWORD *, LPWSTR, DWORD *, LPWSTR, DWORD *);
+typedef BOOL (WINAPI *CredEnumerateWT)(LPCWSTR, DWORD, DWORD *,
+    PCREDENTIALW **);
+typedef BOOL (WINAPI *CredPackAuthenticationBufferWT)(DWORD, LPWSTR, LPWSTR,
+    PBYTE, DWORD *);
+typedef VOID (WINAPI *CredFreeT)(PVOID);
+typedef BOOL (WINAPI *CredDeleteWT)(LPCWSTR, DWORD, DWORD);
+
+static HMODULE advapi, credui;
+static CredWriteWT CredWriteW;
+static CredUnPackAuthenticationBufferWT CredUnPackAuthenticationBufferW;
+static CredEnumerateWT CredEnumerateW;
+static CredPackAuthenticationBufferWT CredPackAuthenticationBufferW;
+static CredFreeT CredFree;
+static CredDeleteWT CredDeleteW;
+
+static void load_cred_funcs(void)
+{
+       /* load DLLs */
+       advapi = LoadLibrary("advapi32.dll");
+       credui = LoadLibrary("credui.dll");
+       if (!advapi || !credui)
+               die("failed to load DLLs");
+
+       /* get function pointers */
+       CredWriteW = (CredWriteWT)GetProcAddress(advapi, "CredWriteW");
+       CredUnPackAuthenticationBufferW = (CredUnPackAuthenticationBufferWT)
+           GetProcAddress(credui, "CredUnPackAuthenticationBufferW");
+       CredEnumerateW = (CredEnumerateWT)GetProcAddress(advapi,
+           "CredEnumerateW");
+       CredPackAuthenticationBufferW = (CredPackAuthenticationBufferWT)
+           GetProcAddress(credui, "CredPackAuthenticationBufferW");
+       CredFree = (CredFreeT)GetProcAddress(advapi, "CredFree");
+       CredDeleteW = (CredDeleteWT)GetProcAddress(advapi, "CredDeleteW");
+       if (!CredWriteW || !CredUnPackAuthenticationBufferW ||
+           !CredEnumerateW || !CredPackAuthenticationBufferW || !CredFree ||
+           !CredDeleteW)
+               die("failed to load functions");
+}
+
+static char target_buf[1024];
+static char *protocol, *host, *path, *username;
+static WCHAR *wusername, *password, *target;
+
+static void write_item(const char *what, WCHAR *wbuf)
+{
+       char *buf;
+       int len = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, NULL, 0, NULL,
+           FALSE);
+       buf = xmalloc(len);
+
+       if (!WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, len, NULL, FALSE))
+               die("WideCharToMultiByte failed!");
+
+       printf("%s=", what);
+       fwrite(buf, 1, len - 1, stdout);
+       putchar('\n');
+       free(buf);
+}
+
+static int match_attr(const CREDENTIALW *cred, const WCHAR *keyword,
+    const char *want)
+{
+       int i;
+       if (!want)
+               return 1;
+
+       for (i = 0; i < cred->AttributeCount; ++i)
+               if (!wcscmp(cred->Attributes[i].Keyword, keyword))
+                       return !strcmp((const char *)cred->Attributes[i].Value,
+                           want);
+
+       return 0; /* not found */
+}
+
+static int match_cred(const CREDENTIALW *cred)
+{
+       return (!wusername || !wcscmp(wusername, cred->UserName)) &&
+           match_attr(cred, L"git_protocol", protocol) &&
+           match_attr(cred, L"git_host", host) &&
+           match_attr(cred, L"git_path", path);
+}
+
+static void get_credential(void)
+{
+       WCHAR *user_buf, *pass_buf;
+       DWORD user_buf_size = 0, pass_buf_size = 0;
+       CREDENTIALW **creds, *cred = NULL;
+       DWORD num_creds;
+       int i;
+
+       if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds))
+               return;
+
+       /* search for the first credential that matches username */
+       for (i = 0; i < num_creds; ++i)
+               if (match_cred(creds[i])) {
+                       cred = creds[i];
+                       break;
+               }
+       if (!cred)
+               return;
+
+       CredUnPackAuthenticationBufferW(0, cred->CredentialBlob,
+           cred->CredentialBlobSize, NULL, &user_buf_size, NULL, NULL,
+           NULL, &pass_buf_size);
+
+       user_buf = xmalloc(user_buf_size * sizeof(WCHAR));
+       pass_buf = xmalloc(pass_buf_size * sizeof(WCHAR));
+
+       if (!CredUnPackAuthenticationBufferW(0, cred->CredentialBlob,
+           cred->CredentialBlobSize, user_buf, &user_buf_size, NULL, NULL,
+           pass_buf, &pass_buf_size))
+               die("CredUnPackAuthenticationBuffer failed");
+
+       CredFree(creds);
+
+       /* zero-terminate (sizes include zero-termination) */
+       user_buf[user_buf_size - 1] = L'\0';
+       pass_buf[pass_buf_size - 1] = L'\0';
+
+       write_item("username", user_buf);
+       write_item("password", pass_buf);
+
+       free(user_buf);
+       free(pass_buf);
+}
+
+static void write_attr(CREDENTIAL_ATTRIBUTEW *attr, const WCHAR *keyword,
+    const char *value)
+{
+       attr->Keyword = (LPWSTR)keyword;
+       attr->Flags = 0;
+       attr->ValueSize = strlen(value) + 1; /* store zero-termination */
+       attr->Value = (LPBYTE)value;
+}
+
+static void store_credential(void)
+{
+       CREDENTIALW cred;
+       BYTE *auth_buf;
+       DWORD auth_buf_size = 0;
+       CREDENTIAL_ATTRIBUTEW attrs[CRED_MAX_ATTRIBUTES];
+
+       if (!wusername || !password)
+               return;
+
+       /* query buffer size */
+       CredPackAuthenticationBufferW(0, wusername, password,
+           NULL, &auth_buf_size);
+
+       auth_buf = xmalloc(auth_buf_size);
+
+       if (!CredPackAuthenticationBufferW(0, wusername, password,
+           auth_buf, &auth_buf_size))
+               die("CredPackAuthenticationBuffer failed");
+
+       cred.Flags = 0;
+       cred.Type = CRED_TYPE_GENERIC;
+       cred.TargetName = target;
+       cred.Comment = L"saved by git-credential-wincred";
+       cred.CredentialBlobSize = auth_buf_size;
+       cred.CredentialBlob = auth_buf;
+       cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
+       cred.AttributeCount = 1;
+       cred.Attributes = attrs;
+       cred.TargetAlias = NULL;
+       cred.UserName = wusername;
+
+       write_attr(attrs, L"git_protocol", protocol);
+
+       if (host) {
+               write_attr(attrs + cred.AttributeCount, L"git_host", host);
+               cred.AttributeCount++;
+       }
+
+       if (path) {
+               write_attr(attrs + cred.AttributeCount, L"git_path", path);
+               cred.AttributeCount++;
+       }
+
+       if (!CredWriteW(&cred, 0))
+               die("CredWrite failed");
+}
+
+static void erase_credential(void)
+{
+       CREDENTIALW **creds;
+       DWORD num_creds;
+       int i;
+
+       if (!CredEnumerateW(L"git:*", 0, &num_creds, &creds))
+               return;
+
+       for (i = 0; i < num_creds; ++i) {
+               if (match_cred(creds[i]))
+                       CredDeleteW(creds[i]->TargetName, creds[i]->Type, 0);
+       }
+
+       CredFree(creds);
+}
+
+static WCHAR *utf8_to_utf16_dup(const char *str)
+{
+       int wlen = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
+       WCHAR *wstr = xmalloc(sizeof(WCHAR) * wlen);
+       MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, wlen);
+       return wstr;
+}
+
+static void read_credential(void)
+{
+       char buf[1024];
+
+       while (fgets(buf, sizeof(buf), stdin)) {
+               char *v;
+
+               if (!strcmp(buf, "\n"))
+                       break;
+               buf[strlen(buf)-1] = '\0';
+
+               v = strchr(buf, '=');
+               if (!v)
+                       die("bad input: %s", buf);
+               *v++ = '\0';
+
+               if (!strcmp(buf, "protocol"))
+                       protocol = xstrdup(v);
+               else if (!strcmp(buf, "host"))
+                       host = xstrdup(v);
+               else if (!strcmp(buf, "path"))
+                       path = xstrdup(v);
+               else if (!strcmp(buf, "username")) {
+                       username = xstrdup(v);
+                       wusername = utf8_to_utf16_dup(v);
+               } else if (!strcmp(buf, "password"))
+                       password = utf8_to_utf16_dup(v);
+               else
+                       die("unrecognized input");
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       const char *usage =
+           "Usage: git credential-wincred <get|store|erase>\n";
+
+       if (!argv[1])
+               die(usage);
+
+       /* git use binary pipes to avoid CRLF-issues */
+       _setmode(_fileno(stdin), _O_BINARY);
+       _setmode(_fileno(stdout), _O_BINARY);
+
+       read_credential();
+
+       load_cred_funcs();
+
+       if (!protocol || !(host || path))
+               return 0;
+
+       /* prepare 'target', the unique key for the credential */
+       strncat(target_buf, "git:", sizeof(target_buf));
+       strncat(target_buf, protocol, sizeof(target_buf));
+       strncat(target_buf, "://", sizeof(target_buf));
+       if (username) {
+               strncat(target_buf, username, sizeof(target_buf));
+               strncat(target_buf, "@", sizeof(target_buf));
+       }
+       if (host)
+               strncat(target_buf, host, sizeof(target_buf));
+       if (path) {
+               strncat(target_buf, "/", sizeof(target_buf));
+               strncat(target_buf, path, sizeof(target_buf));
+       }
+
+       target = utf8_to_utf16_dup(target_buf);
+
+       if (!strcmp(argv[1], "get"))
+               get_credential();
+       else if (!strcmp(argv[1], "store"))
+               store_credential();
+       else if (!strcmp(argv[1], "erase"))
+               erase_credential();
+       /* otherwise, ignore unknown action */
+       return 0;
+}
index 0d54aa7061e780dd0000b8c2a48e266ad5b8ce53..8bc8c7533a8678e2a43d3ddd49bc9aa8d670897d 100644 (file)
@@ -96,7 +96,7 @@ static int update_local_ref(const char *name,
        strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
        strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
 
-       if (in_merge_bases(current, &updated, 1)) {
+       if (in_merge_bases(current, updated)) {
                fprintf(stderr, "* %s: fast-forward to %s\n",
                        name, note);
                fprintf(stderr, "  old..new: %s..%s\n", oldh, newh);
index ab21e66b2fb69015c3085761b344b11053c67f4a..4602b46a5c39e1d501143ab4e95b55aff5c8f23b 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -30,6 +30,7 @@ static const char daemon_usage[] =
 "           [--interpolated-path=<path>]\n"
 "           [--reuseaddr] [--pid-file=<file>]\n"
 "           [--(enable|disable|allow-override|forbid-override)=<service>]\n"
+"           [--access-hook=<path>]\n"
 "           [--inetd | [--listen=<host_or_ipaddr>] [--port=<n>]\n"
 "                      [--detach] [--user=<user> [--group=<group>]]\n"
 "           [<directory>...]";
@@ -256,6 +257,71 @@ static int daemon_error(const char *dir, const char *msg)
        return -1;
 }
 
+static char *access_hook;
+
+static int run_access_hook(struct daemon_service *service, const char *dir, const char *path)
+{
+       struct child_process child;
+       struct strbuf buf = STRBUF_INIT;
+       const char *argv[8];
+       const char **arg = argv;
+       char *eol;
+       int seen_errors = 0;
+
+#define STRARG(x) ((x) ? (x) : "")
+       *arg++ = access_hook;
+       *arg++ = service->name;
+       *arg++ = path;
+       *arg++ = STRARG(hostname);
+       *arg++ = STRARG(canon_hostname);
+       *arg++ = STRARG(ip_address);
+       *arg++ = STRARG(tcp_port);
+       *arg = NULL;
+#undef STRARG
+
+       memset(&child, 0, sizeof(child));
+       child.use_shell = 1;
+       child.argv = argv;
+       child.no_stdin = 1;
+       child.no_stderr = 1;
+       child.out = -1;
+       if (start_command(&child)) {
+               logerror("daemon access hook '%s' failed to start",
+                        access_hook);
+               goto error_return;
+       }
+       if (strbuf_read(&buf, child.out, 0) < 0) {
+               logerror("failed to read from pipe to daemon access hook '%s'",
+                        access_hook);
+               strbuf_reset(&buf);
+               seen_errors = 1;
+       }
+       if (close(child.out) < 0) {
+               logerror("failed to close pipe to daemon access hook '%s'",
+                        access_hook);
+               seen_errors = 1;
+       }
+       if (finish_command(&child))
+               seen_errors = 1;
+
+       if (!seen_errors) {
+               strbuf_release(&buf);
+               return 0;
+       }
+
+error_return:
+       strbuf_ltrim(&buf);
+       if (!buf.len)
+               strbuf_addstr(&buf, "service rejected");
+       eol = strchr(buf.buf, '\n');
+       if (eol)
+               *eol = '\0';
+       errno = EACCES;
+       daemon_error(dir, buf.buf);
+       strbuf_release(&buf);
+       return -1;
+}
+
 static int run_service(char *dir, struct daemon_service *service)
 {
        const char *path;
@@ -303,6 +369,13 @@ static int run_service(char *dir, struct daemon_service *service)
                return daemon_error(dir, "service not enabled");
        }
 
+       /*
+        * Optionally, a hook can choose to deny access to the
+        * repository depending on the phase of the moon.
+        */
+       if (access_hook && run_access_hook(service, dir, path))
+               return -1;
+
        /*
         * We'll ignore SIGTERM from now on, we have a
         * good client.
@@ -1142,6 +1215,10 @@ int main(int argc, char **argv)
                        export_all_trees = 1;
                        continue;
                }
+               if (!prefixcmp(arg, "--access-hook=")) {
+                       access_hook = arg + 14;
+                       continue;
+               }
                if (!prefixcmp(arg, "--timeout=")) {
                        timeout = atoi(arg+10);
                        continue;
index fc0dff31b58c8bd6668de5c6396a93b31cc5729d..f35de0ffa0ec263acae7ac6211e3c2817987f760 100644 (file)
@@ -206,7 +206,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        if (silent_on_removed)
                                continue;
                        diff_addremove(&revs->diffopt, '-', ce->ce_mode,
-                                      ce->sha1, ce->name, 0);
+                                      ce->sha1, !is_null_sha1(ce->sha1),
+                                      ce->name, 0);
                        continue;
                }
                changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
@@ -220,6 +221,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                newmode = ce_mode_from_stat(ce, st.st_mode);
                diff_change(&revs->diffopt, oldmode, newmode,
                            ce->sha1, (changed ? null_sha1 : ce->sha1),
+                           !is_null_sha1(ce->sha1), (changed ? 0 : !is_null_sha1(ce->sha1)),
                            ce->name, 0, dirty_submodule);
 
        }
@@ -236,11 +238,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
 static void diff_index_show_file(struct rev_info *revs,
                                 const char *prefix,
                                 struct cache_entry *ce,
-                                const unsigned char *sha1, unsigned int mode,
+                                const unsigned char *sha1, int sha1_valid,
+                                unsigned int mode,
                                 unsigned dirty_submodule)
 {
        diff_addremove(&revs->diffopt, prefix[0], mode,
-                      sha1, ce->name, dirty_submodule);
+                      sha1, sha1_valid, ce->name, dirty_submodule);
 }
 
 static int get_stat_data(struct cache_entry *ce,
@@ -295,7 +298,7 @@ static void show_new_file(struct rev_info *revs,
            &dirty_submodule, &revs->diffopt) < 0)
                return;
 
-       diff_index_show_file(revs, "+", new, sha1, mode, dirty_submodule);
+       diff_index_show_file(revs, "+", new, sha1, !is_null_sha1(sha1), mode, dirty_submodule);
 }
 
 static int show_modified(struct rev_info *revs,
@@ -312,7 +315,7 @@ static int show_modified(struct rev_info *revs,
                          &dirty_submodule, &revs->diffopt) < 0) {
                if (report_missing)
                        diff_index_show_file(revs, "-", old,
-                                            old->sha1, old->ce_mode, 0);
+                                            old->sha1, 1, old->ce_mode, 0);
                return -1;
        }
 
@@ -347,7 +350,8 @@ static int show_modified(struct rev_info *revs,
                return 0;
 
        diff_change(&revs->diffopt, oldmode, mode,
-                   old->sha1, sha1, old->name, 0, dirty_submodule);
+                   old->sha1, sha1, 1, !is_null_sha1(sha1),
+                   old->name, 0, dirty_submodule);
        return 0;
 }
 
@@ -380,7 +384,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
                struct diff_filepair *pair;
                pair = diff_unmerge(&revs->diffopt, idx->name);
                if (tree)
-                       fill_filespec(pair->one, tree->sha1, tree->ce_mode);
+                       fill_filespec(pair->one, tree->sha1, 1, tree->ce_mode);
                return;
        }
 
@@ -396,7 +400,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
         * Something removed from the tree?
         */
        if (!idx) {
-               diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode, 0);
+               diff_index_show_file(revs, "-", tree, tree->sha1, 1, tree->ce_mode, 0);
                return;
        }
 
index 7d805a06afacae7eaa36a192e3a16406ef0fb41f..74da659368e4db67715a9c81dc7a5e0edcd3bcce 100644 (file)
@@ -82,7 +82,7 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode)
        if (!name)
                name = "/dev/null";
        s = alloc_filespec(name);
-       fill_filespec(s, null_sha1, mode);
+       fill_filespec(s, null_sha1, 0, mode);
        if (name == file_from_standard_input)
                populate_from_stdin(s);
        return s;
@@ -258,8 +258,7 @@ void diff_no_index(struct rev_info *revs,
        DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
 
        revs->max_count = -2;
-       if (diff_setup_done(&revs->diffopt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&revs->diffopt);
 
        setup_diff_pager(&revs->diffopt);
        DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
diff --git a/diff.c b/diff.c
index 95706a5b4098afc8f20fe53425ff760c996a3b4e..e6846ca7507aa8a423c5e1f89390fa37723248ff 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -574,6 +574,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb,
        if (!endp) {
                const char *plain = diff_get_color(ecb->color_diff,
                                                   DIFF_PLAIN);
+               putc('\n', ecb->opt->file);
                emit_line_0(ecb->opt, plain, reset, '\\',
                            nneof, strlen(nneof));
        }
@@ -2541,12 +2542,12 @@ void free_filespec(struct diff_filespec *spec)
 }
 
 void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
-                  unsigned short mode)
+                  int sha1_valid, unsigned short mode)
 {
        if (mode) {
                spec->mode = canon_mode(mode);
                hashcpy(spec->sha1, sha1);
-               spec->sha1_valid = !is_null_sha1(sha1);
+               spec->sha1_valid = sha1_valid;
        }
 }
 
@@ -3187,7 +3188,7 @@ void diff_setup(struct diff_options *options)
        }
 }
 
-int diff_setup_done(struct diff_options *options)
+void diff_setup_done(struct diff_options *options)
 {
        int count = 0;
 
@@ -3286,8 +3287,6 @@ int diff_setup_done(struct diff_options *options)
                options->output_format = DIFF_FORMAT_NO_OUTPUT;
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
-
-       return 0;
 }
 
 static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
@@ -4693,6 +4692,7 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    const char *concatpath, unsigned dirty_submodule)
 {
        struct diff_filespec *one, *two;
@@ -4724,9 +4724,9 @@ void diff_addremove(struct diff_options *options,
        two = alloc_filespec(concatpath);
 
        if (addremove != '+')
-               fill_filespec(one, sha1, mode);
+               fill_filespec(one, sha1, sha1_valid, mode);
        if (addremove != '-') {
-               fill_filespec(two, sha1, mode);
+               fill_filespec(two, sha1, sha1_valid, mode);
                two->dirty_submodule = dirty_submodule;
        }
 
@@ -4739,6 +4739,7 @@ void diff_change(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
+                int old_sha1_valid, int new_sha1_valid,
                 const char *concatpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
 {
@@ -4753,6 +4754,8 @@ void diff_change(struct diff_options *options,
                const unsigned char *tmp_c;
                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
                tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
+               tmp = old_sha1_valid; old_sha1_valid = new_sha1_valid;
+                       new_sha1_valid = tmp;
                tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule;
                        new_dirty_submodule = tmp;
        }
@@ -4763,8 +4766,8 @@ void diff_change(struct diff_options *options,
 
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
-       fill_filespec(one, old_sha1, old_mode);
-       fill_filespec(two, new_sha1, new_mode);
+       fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
+       fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
        one->dirty_submodule = old_dirty_submodule;
        two->dirty_submodule = new_dirty_submodule;
 
diff --git a/diff.h b/diff.h
index e027650cb0ff2651e2e890e7f00753c15f5b3cff..e25addb806a190f138a14ca64190fa5d2f311aaa 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -19,12 +19,14 @@ typedef void (*change_fn_t)(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
+                int old_sha1_valid, int new_sha1_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule);
 
 typedef void (*add_remove_fn_t)(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    const char *fullpath, unsigned dirty_submodule);
 
 typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
@@ -214,12 +216,15 @@ extern void diff_addremove(struct diff_options *,
                           int addremove,
                           unsigned mode,
                           const unsigned char *sha1,
+                          int sha1_valid,
                           const char *fullpath, unsigned dirty_submodule);
 
 extern void diff_change(struct diff_options *,
                        unsigned mode1, unsigned mode2,
                        const unsigned char *sha1,
                        const unsigned char *sha2,
+                       int sha1_valid,
+                       int sha2_valid,
                        const char *fullpath,
                        unsigned dirty_submodule1, unsigned dirty_submodule2);
 
@@ -241,7 +246,7 @@ extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern int diff_use_color_default;
 extern void diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int);
-extern int diff_setup_done(struct diff_options *);
+extern void diff_setup_done(struct diff_options *);
 
 #define DIFF_DETECT_RENAME     1
 #define DIFF_DETECT_COPY       2
index 216a7a4bbcab189b5c3d1b7f58728b94b8d6aec8..512d0ac5fd2bc0acfb57147a6eb77f61f92b7c7e 100644 (file)
@@ -48,7 +48,7 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two,
                memmove(rename_dst + first + 1, rename_dst + first,
                        (rename_dst_nr - first - 1) * sizeof(*rename_dst));
        rename_dst[first].two = alloc_filespec(two->path);
-       fill_filespec(rename_dst[first].two, two->sha1, two->mode);
+       fill_filespec(rename_dst[first].two, two->sha1, two->sha1_valid, two->mode);
        rename_dst[first].pair = NULL;
        return &(rename_dst[first]);
 }
index be0739c5c401c059a0d3030acbc99a7744f17065..1c16c8595b21c2712259041c01d4b58d76a60222 100644 (file)
@@ -55,7 +55,7 @@ struct diff_filespec {
 extern struct diff_filespec *alloc_filespec(const char *);
 extern void free_filespec(struct diff_filespec *);
 extern void fill_filespec(struct diff_filespec *, const unsigned char *,
-                         unsigned short);
+                         int, unsigned short);
 
 extern int diff_populate_filespec(struct diff_filespec *, int);
 extern void diff_free_filespec_data(struct diff_filespec *);
diff --git a/dir.c b/dir.c
index 240bf0c49c9654b7cef56959786613e17113c3a0..486833986ed4b4e7d05d2086d53b15ec63905dd0 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -397,6 +397,8 @@ int add_excludes_from_file_to_list(const char *fname,
 
        fd = open(fname, O_RDONLY);
        if (fd < 0 || fstat(fd, &st) < 0) {
+               if (errno != ENOENT)
+                       warn_on_inaccessible(fname);
                if (0 <= fd)
                        close(fd);
                if (!check_index ||
@@ -1311,9 +1313,9 @@ void setup_standard_excludes(struct dir_struct *dir)
                home_config_paths(NULL, &xdg_path, "ignore");
                excludes_file = xdg_path;
        }
-       if (!access(path, R_OK))
+       if (!access_or_warn(path, R_OK))
                add_excludes_from_file(dir, path);
-       if (excludes_file && !access(excludes_file, R_OK))
+       if (excludes_file && !access_or_warn(excludes_file, R_OK))
                add_excludes_from_file(dir, excludes_file);
 }
 
index eed97c8fa9f3e1624f69443e28f76d995e589b34..c2a814ec660862937e495c0a7efb6375dc7718ac 100644 (file)
@@ -1691,7 +1691,7 @@ static int update_branch(struct branch *b)
                        return error("Branch %s is missing commits.", b->name);
                }
 
-               if (!in_merge_bases(old_cmit, &new_cmit, 1)) {
+               if (!in_merge_bases(old_cmit, new_cmit)) {
                        unlock_ref(lock);
                        warning("Not updating %s"
                                " (new tip %s does not contain %s)",
diff --git a/fsck.c b/fsck.c
index 4c63b2cc41eec4f568ee6f0d18a51c97304d5d96..7395ef6a425f5c7725767f34a0383f61907fce93 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -139,6 +139,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con
 static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
 {
        int retval;
+       int has_null_sha1 = 0;
        int has_full_path = 0;
        int has_empty_name = 0;
        int has_zero_pad = 0;
@@ -157,9 +158,12 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
        while (desc.size) {
                unsigned mode;
                const char *name;
+               const unsigned char *sha1;
 
-               tree_entry_extract(&desc, &name, &mode);
+               sha1 = tree_entry_extract(&desc, &name, &mode);
 
+               if (is_null_sha1(sha1))
+                       has_null_sha1 = 1;
                if (strchr(name, '/'))
                        has_full_path = 1;
                if (!*name)
@@ -207,6 +211,8 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
        }
 
        retval = 0;
+       if (has_null_sha1)
+               retval += error_func(&item->object, FSCK_WARN, "contains entries pointing to null sha1");
        if (has_full_path)
                retval += error_func(&item->object, FSCK_WARN, "contains full pathnames");
        if (has_empty_name)
index 57ba8bb02e39d59752a5b2fbf016bc6fe49d27c1..376297bf730d421fa13237350d2beeb525be5ec4 100644 (file)
--- a/gettext.h
+++ b/gettext.h
@@ -44,6 +44,8 @@ extern int use_gettext_poison(void);
 
 static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)
 {
+       if (!*msgid)
+               return "";
        return use_gettext_poison() ? "# GETTEXT POISON #" : gettext(msgid);
 }
 
index bd9620c6c6b25a083dacc0bb3fe9d45337ed0d7b..c682d34094d618734ac52d6d7e15a6c10ccfdeb6 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -855,8 +855,8 @@ did you forget to use 'git add'?"
                eval_gettextln 'Patch failed at $msgnum $FIRSTLINE'
                if test "$(git config --bool advice.amworkdir)" != false
                then
-                       eval_gettextln "The copy of the patch that failed is found in:
-   $dotest/patch"
+                       eval_gettextln 'The copy of the patch that failed is found in:
+   $dotest/patch'
                fi
                stop_here_user_resolve $this
        fi
index 3515366b129b7343d15c67bc8e4fe3c83e0b1f7d..24b5432462234ee25123cb5aa96222bab0f9e28f 100644 (file)
 #define probe_utf8_pathname_composition(a,b)
 #endif
 
+#ifdef MKDIR_WO_TRAILING_SLASH
+#define mkdir(a,b) compat_mkdir_wo_trailing_slash((a),(b))
+extern int compat_mkdir_wo_trailing_slash(const char*, mode_t);
+#endif
+
 #ifdef NO_STRUCT_ITIMERVAL
 struct itimerval {
        struct timeval it_interval;
@@ -615,6 +620,12 @@ int rmdir_or_warn(const char *path);
  */
 int remove_or_warn(unsigned int mode, const char *path);
 
+/* Call access(2), but warn for any error besides ENOENT. */
+int access_or_warn(const char *path, int mode);
+
+/* Warn on an inaccessible file that ought to be accessible */
+void warn_on_inaccessible(const char *path);
+
 /* Get the passwd entry for the UID of the current process. */
 struct passwd *xgetpwuid_self(void);
 
index c0798540adee04cb7cfb6fd04ef3541a1fcbb762..edd0493a08b501d60c04044e79a9e0b096d77953 100755 (executable)
 use File::Compare;
 use File::Find;
 use File::stat;
-use File::Path qw(mkpath);
+use File::Path qw(mkpath rmtree);
 use File::Temp qw(tempdir);
 use Getopt::Long qw(:config pass_through);
 use Git;
 
-my @tools;
-my @working_tree;
-my $rc;
-my $repo = Git->repository();
-my $repo_path = $repo->repo_path();
-
 sub usage
 {
        my $exitcode = shift;
@@ -45,6 +39,8 @@ sub usage
 
 sub find_worktree
 {
+       my ($repo) = @_;
+
        # 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.
@@ -63,10 +59,9 @@ sub find_worktree
        return $worktree;
 }
 
-my $workdir = find_worktree();
-
 sub filter_tool_scripts
 {
+       my ($tools) = @_;
        if (-d $_) {
                if ($_ ne ".") {
                        # Ignore files in subdirectories
@@ -74,17 +69,17 @@ sub filter_tool_scripts
                }
        } else {
                if ((-f $_) && ($_ ne "defaults")) {
-                       push(@tools, $_);
+                       push(@$tools, $_);
                }
        }
 }
 
 sub print_tool_help
 {
-       my ($cmd, @found, @notfound);
+       my ($cmd, @found, @notfound, @tools);
        my $gitpath = Git::exec_path();
 
-       find(\&filter_tool_scripts, "$gitpath/mergetools");
+       find(sub { filter_tool_scripts(\@tools) }, "$gitpath/mergetools");
 
        foreach my $tool (@tools) {
                $cmd  = "TOOL_MODE=diff";
@@ -98,34 +93,52 @@ sub print_tool_help
                }
        }
 
-       print "'git difftool --tool=<tool>' may be set to one of the following:\n";
+       print << 'EOF';
+'git difftool --tool=<tool>' may be set to one of the following:
+EOF
        print "\t$_\n" for (sort(@found));
 
-       print "\nThe following tools are valid, but not currently available:\n";
+       print << 'EOF';
+
+The following tools are valid, but not currently available:
+EOF
        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";
+       print << 'EOF';
 
+NOTE: Some of the tools listed above only work in a windowed
+environment. If run in a terminal-only session, they will fail.
+EOF
        exit(0);
 }
 
+sub exit_cleanup
+{
+       my ($tmpdir, $status) = @_;
+       my $errno = $!;
+       rmtree($tmpdir);
+       if ($status and $errno) {
+               my ($package, $file, $line) = caller();
+               warn "$file line $line: $errno\n";
+       }
+       exit($status | ($status >> 8));
+}
+
 sub setup_dir_diff
 {
+       my ($repo, $workdir, $symlinks) = @_;
+
        # 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);
+       my $repo_path = $repo->repo_path();
+       my %repo_args = (Repository => $repo_path, WorkingCopy => $workdir);
+       my $diffrepo = Git->repository(%repo_args);
 
-       # 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 $!;
+       my @gitargs = ('diff', '--raw', '--no-abbrev', '-z', @ARGV);
+       my $diffrtn = $diffrepo->command_oneline(@gitargs);
+       exit(0) unless defined($diffrtn);
 
        # Build index info for left and right sides of the diff
        my $submodule_mode = '160000';
@@ -136,16 +149,21 @@ sub setup_dir_diff
        my $rindex = '';
        my %submodule;
        my %symlink;
+       my @working_tree = ();
        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";
+                       warn << 'EOF';
+Combined diff formats ('-c' and '--cc') are not supported in
+directory diff mode ('-d' and '--dir-diff').
+EOF
                        exit(1);
                }
 
-               my ($lmode, $rmode, $lsha1, $rsha1, $status) = split(' ', substr($rawdiff[$i], 1));
+               my ($lmode, $rmode, $lsha1, $rsha1, $status) =
+                       split(' ', substr($rawdiff[$i], 1));
                my $src_path = $rawdiff[$i + 1];
                my $dst_path;
 
@@ -157,7 +175,7 @@ sub setup_dir_diff
                        $i += 2;
                }
 
-               if (($lmode eq $submodule_mode) or ($rmode eq $submodule_mode)) {
+               if ($lmode eq $submodule_mode or $rmode eq $submodule_mode) {
                        $submodule{$src_path}{left} = $lsha1;
                        if ($lsha1 ne $rsha1) {
                                $submodule{$dst_path}{right} = $rsha1;
@@ -168,14 +186,16 @@ sub setup_dir_diff
                }
 
                if ($lmode eq $symlink_mode) {
-                       $symlink{$src_path}{left} = $diffrepo->command_oneline('show', "$lsha1");
+                       $symlink{$src_path}{left} =
+                               $diffrepo->command_oneline('show', "$lsha1");
                }
 
                if ($rmode eq $symlink_mode) {
-                       $symlink{$dst_path}{right} = $diffrepo->command_oneline('show', "$rsha1");
+                       $symlink{$dst_path}{right} =
+                               $diffrepo->command_oneline('show', "$rsha1");
                }
 
-               if (($lmode ne $null_mode) and ($status !~ /^C/)) {
+               if ($lmode ne $null_mode and $status !~ /^C/) {
                        $lindex .= "$lmode $lsha1\t$src_path\0";
                }
 
@@ -188,6 +208,13 @@ sub setup_dir_diff
                }
        }
 
+       # Setup temp directories
+       my $tmpdir = tempdir('git-difftool.XXXXX', CLEANUP => 0, TMPDIR => 1);
+       my $ldir = "$tmpdir/left";
+       my $rdir = "$tmpdir/right";
+       mkpath($ldir) or exit_cleanup($tmpdir, 1);
+       mkpath($rdir) or exit_cleanup($tmpdir, 1);
+
        # 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.
@@ -200,18 +227,22 @@ sub setup_dir_diff
        # 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/);
+       ($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);
+
+       my $rc = system('git', 'checkout-index', '--all', "--prefix=$ldir/");
+       exit_cleanup($tmpdir, $rc) if $rc != 0;
 
        $ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
-       ($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);
+       ($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);
+       exit_cleanup($tmpdir, $rc) if $rc != 0;
 
        # If $GIT_DIR was explicitly set just for the update/checkout
        # commands, then it should be unset before continuing.
@@ -223,37 +254,55 @@ sub setup_dir_diff
        for my $file (@working_tree) {
                my $dir = dirname($file);
                unless (-d "$rdir/$dir") {
-                       mkpath("$rdir/$dir") or die $!;
+                       mkpath("$rdir/$dir") or
+                       exit_cleanup($tmpdir, 1);
+               }
+               if ($symlinks) {
+                       symlink("$workdir/$file", "$rdir/$file") or
+                       exit_cleanup($tmpdir, 1);
+               } else {
+                       copy("$workdir/$file", "$rdir/$file") or
+                       exit_cleanup($tmpdir, 1);
+
+                       my $mode = stat("$workdir/$file")->mode;
+                       chmod($mode, "$rdir/$file") or
+                       exit_cleanup($tmpdir, 1);
                }
-               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) {
+               my $ok;
                if (defined($submodule{$path}{left})) {
-                       write_to_file("$ldir/$path", "Subproject commit $submodule{$path}{left}");
+                       $ok = 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}");
+                       $ok = write_to_file("$rdir/$path",
+                               "Subproject commit $submodule{$path}{right}");
                }
+               exit_cleanup($tmpdir, 1) if not $ok;
        }
 
        # 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) {
+               my $ok;
                if (defined($symlink{$path}{left})) {
-                       write_to_file("$ldir/$path", $symlink{$path}{left});
+                       $ok = write_to_file("$ldir/$path",
+                                       $symlink{$path}{left});
                }
                if (defined($symlink{$path}{right})) {
-                       write_to_file("$rdir/$path", $symlink{$path}{right});
+                       $ok = write_to_file("$rdir/$path",
+                                       $symlink{$path}{right});
                }
+               exit_cleanup($tmpdir, 1) if not $ok;
        }
 
-       return ($ldir, $rdir);
+       return ($ldir, $rdir, $tmpdir, @working_tree);
 }
 
 sub write_to_file
@@ -264,85 +313,141 @@ sub write_to_file
        # Make sure the path to the file exists
        my $dir = dirname($path);
        unless (-d "$dir") {
-               mkpath("$dir") or die $!;
+               mkpath("$dir") or return 0;
        }
 
        # If the file already exists in that location, delete it.  This
        # is required in the case of symbolic links.
-       unlink("$path");
+       unlink($path);
 
-       open(my $fh, '>', "$path") or die $!;
+       open(my $fh, '>', $path) or return 0;
        print($fh $value);
        close($fh);
-}
 
-# 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);
-
-if (defined($help)) {
-       usage(0);
-}
-if (defined($tool_help)) {
-       print_tool_help();
+       return 1;
 }
-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);
+
+sub main
+{
+       # parse command-line options. all unrecognized options and arguments
+       # are passed through to the 'git diff' command.
+       my %opts = (
+               difftool_cmd => undef,
+               dirdiff => undef,
+               extcmd => undef,
+               gui => undef,
+               help => undef,
+               prompt => undef,
+               symlinks => $^O ne 'cygwin' &&
+                               $^O ne 'MSWin32' && $^O ne 'msys',
+               tool_help => undef,
+       );
+       GetOptions('g|gui!' => \$opts{gui},
+               'd|dir-diff' => \$opts{dirdiff},
+               'h' => \$opts{help},
+               'prompt!' => \$opts{prompt},
+               'y' => sub { $opts{prompt} = 0; },
+               'symlinks' => \$opts{symlinks},
+               'no-symlinks' => sub { $opts{symlinks} = 0; },
+               't|tool:s' => \$opts{difftool_cmd},
+               'tool-help' => \$opts{tool_help},
+               'x|extcmd:s' => \$opts{extcmd});
+
+       if (defined($opts{help})) {
+               usage(0);
        }
-}
-if (defined($extcmd)) {
-       if (length($extcmd) > 0) {
-               $ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;
-       } else {
-               print "No <cmd> given for --extcmd=<cmd>\n";
-               usage(1);
+       if (defined($opts{tool_help})) {
+               print_tool_help();
        }
-}
-if ($gui) {
-       my $guitool = '';
-       $guitool = Git::config('diff.guitool');
-       if (length($guitool) > 0) {
-               $ENV{GIT_DIFF_TOOL} = $guitool;
+       if (defined($opts{difftool_cmd})) {
+               if (length($opts{difftool_cmd}) > 0) {
+                       $ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
+               } else {
+                       print "No <tool> given for --tool=<tool>\n";
+                       usage(1);
+               }
+       }
+       if (defined($opts{extcmd})) {
+               if (length($opts{extcmd}) > 0) {
+                       $ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
+               } else {
+                       print "No <cmd> given for --extcmd=<cmd>\n";
+                       usage(1);
+               }
+       }
+       if ($opts{gui}) {
+               my $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($opts{dirdiff})) {
+               dir_diff($opts{extcmd}, $opts{symlinks});
+       } else {
+               file_diff($opts{prompt});
        }
 }
 
-# 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();
+sub dir_diff
+{
+       my ($extcmd, $symlinks) = @_;
+       my $rc;
+       my $error = 0;
+       my $repo = Git->repository();
+       my $workdir = find_worktree($repo);
+       my ($a, $b, $tmpdir, @worktree) =
+               setup_dir_diff($repo, $workdir, $symlinks);
+
        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) {
-               if (-e "$b/$file" && compare("$b/$file", "$workdir/$file")) {
-                       copy("$b/$file", "$workdir/$file") or die $!;
-                       chmod(stat("$b/$file")->mode, "$workdir/$file") or die $!;
+       # should be copied back to the working tree.
+       # Do not copy back files when symlinks are used and the
+       # external tool did not replace the original link with a file.
+       for my $file (@worktree) {
+               next if $symlinks && -l "$b/$file";
+               next if ! -f "$b/$file";
+
+               my $diff = compare("$b/$file", "$workdir/$file");
+               if ($diff == 0) {
+                       next;
+               } elsif ($diff == -1) {
+                       my $errmsg = "warning: Could not compare ";
+                       $errmsg += "'$b/$file' with '$workdir/$file'\n";
+                       warn $errmsg;
+                       $error = 1;
+               } elsif ($diff == 1) {
+                       my $mode = stat("$b/$file")->mode;
+                       copy("$b/$file", "$workdir/$file") or
+                       exit_cleanup($tmpdir, 1);
+
+                       chmod($mode, "$workdir/$file") or
+                       exit_cleanup($tmpdir, 1);
                }
        }
-} else {
+       if ($error) {
+               warn "warning: Temporary files exist in '$tmpdir'.\n";
+               warn "warning: You may want to cleanup or recover these.\n";
+               exit(1);
+       } else {
+               exit_cleanup($tmpdir, $rc);
+       }
+}
+
+sub file_diff
+{
+       my ($prompt) = @_;
+
        if (defined($prompt)) {
                if ($prompt) {
                        $ENV{GIT_DIFFTOOL_PROMPT} = 'true';
@@ -362,3 +467,5 @@ sub write_to_file
        my $rc = system('git', 'diff', @ARGV);
        exit($rc | ($rc >> 8));
 }
+
+main();
index f730253c0eac181ec8507d2c837d4ec85a0ae991..54cb708254d38062983373c536589add648ae304 100644 (file)
@@ -126,7 +126,7 @@ list_merge_tool_candidates () {
                else
                        tools="opendiff kdiff3 tkdiff xxdiff meld $tools"
                fi
-               tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3"
+               tools="$tools gvimdiff diffuse ecmerge p4merge araxis bc3 codecompare"
        fi
        case "${VISUAL:-$EDITOR}" in
        *vim*)
index 0db0c44845f54920e26d97391f577e76305ecc56..c50e18a8998c1f4ca1449e1733f76cb769f501b9 100755 (executable)
@@ -18,270 +18,301 @@ require_work_tree
 
 # Returns true if the mode reflects a symlink
 is_symlink () {
-    test "$1" = 120000
+       test "$1" = 120000
 }
 
 is_submodule () {
-    test "$1" = 160000
+       test "$1" = 160000
 }
 
 local_present () {
-    test -n "$local_mode"
+       test -n "$local_mode"
 }
 
 remote_present () {
-    test -n "$remote_mode"
+       test -n "$remote_mode"
 }
 
 base_present () {
-    test -n "$base_mode"
+       test -n "$base_mode"
 }
 
 cleanup_temp_files () {
-    if test "$1" = --save-backup ; then
-       rm -rf -- "$MERGED.orig"
-       test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
-       rm -f -- "$LOCAL" "$REMOTE" "$BASE"
-    else
-       rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
-    fi
+       if test "$1" = --save-backup
+       then
+               rm -rf -- "$MERGED.orig"
+               test -e "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig"
+               rm -f -- "$LOCAL" "$REMOTE" "$BASE"
+       else
+               rm -f -- "$LOCAL" "$REMOTE" "$BASE" "$BACKUP"
+       fi
 }
 
 describe_file () {
-    mode="$1"
-    branch="$2"
-    file="$3"
-
-    printf "  {%s}: " "$branch"
-    if test -z "$mode"; then
-       echo "deleted"
-    elif is_symlink "$mode" ; then
-       echo "a symbolic link -> '$(cat "$file")'"
-    elif is_submodule "$mode" ; then
-       echo "submodule commit $file"
-    else
-       if base_present; then
-           echo "modified file"
+       mode="$1"
+       branch="$2"
+       file="$3"
+
+       printf "  {%s}: " "$branch"
+       if test -z "$mode"
+       then
+               echo "deleted"
+       elif is_symlink "$mode"
+       then
+               echo "a symbolic link -> '$(cat "$file")'"
+       elif is_submodule "$mode"
+       then
+               echo "submodule commit $file"
+       elif base_present
+       then
+               echo "modified file"
        else
-           echo "created file"
+               echo "created file"
        fi
-    fi
 }
 
-
 resolve_symlink_merge () {
-    while true; do
-       printf "Use (l)ocal or (r)emote, or (a)bort? "
-       read ans || return 1
-       case "$ans" in
-           [lL]*)
-               git checkout-index -f --stage=2 -- "$MERGED"
-               git add -- "$MERGED"
-               cleanup_temp_files --save-backup
-               return 0
-               ;;
-           [rR]*)
-               git checkout-index -f --stage=3 -- "$MERGED"
-               git add -- "$MERGED"
-               cleanup_temp_files --save-backup
-               return 0
-               ;;
-           [aA]*)
-               return 1
-               ;;
-           esac
+       while true
+       do
+               printf "Use (l)ocal or (r)emote, or (a)bort? "
+               read ans || return 1
+               case "$ans" in
+               [lL]*)
+                       git checkout-index -f --stage=2 -- "$MERGED"
+                       git add -- "$MERGED"
+                       cleanup_temp_files --save-backup
+                       return 0
+                       ;;
+               [rR]*)
+                       git checkout-index -f --stage=3 -- "$MERGED"
+                       git add -- "$MERGED"
+                       cleanup_temp_files --save-backup
+                       return 0
+                       ;;
+               [aA]*)
+                       return 1
+                       ;;
+               esac
        done
 }
 
 resolve_deleted_merge () {
-    while true; do
-       if base_present; then
-           printf "Use (m)odified or (d)eleted file, or (a)bort? "
-       else
-           printf "Use (c)reated or (d)eleted file, or (a)bort? "
-       fi
-       read ans || return 1
-       case "$ans" in
-           [mMcC]*)
-               git add -- "$MERGED"
-               cleanup_temp_files --save-backup
-               return 0
-               ;;
-           [dD]*)
-               git rm -- "$MERGED" > /dev/null
-               cleanup_temp_files
-               return 0
-               ;;
-           [aA]*)
-               return 1
-               ;;
-           esac
+       while true
+       do
+               if base_present
+               then
+                       printf "Use (m)odified or (d)eleted file, or (a)bort? "
+               else
+                       printf "Use (c)reated or (d)eleted file, or (a)bort? "
+               fi
+               read ans || return 1
+               case "$ans" in
+               [mMcC]*)
+                       git add -- "$MERGED"
+                       cleanup_temp_files --save-backup
+                       return 0
+                       ;;
+               [dD]*)
+                       git rm -- "$MERGED" > /dev/null
+                       cleanup_temp_files
+                       return 0
+                       ;;
+               [aA]*)
+                       return 1
+                       ;;
+               esac
        done
 }
 
 resolve_submodule_merge () {
-    while true; do
-       printf "Use (l)ocal or (r)emote, or (a)bort? "
-       read ans || return 1
-       case "$ans" in
-           [lL]*)
-               if ! local_present; then
-                   if test -n "$(git ls-tree HEAD -- "$MERGED")"; then
-                       # Local isn't present, but it's a subdirectory
-                       git ls-tree --full-name -r HEAD -- "$MERGED" | git update-index --index-info || exit $?
-                   else
-                       test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
-                       git update-index --force-remove "$MERGED"
+       while true
+       do
+               printf "Use (l)ocal or (r)emote, or (a)bort? "
+               read ans || return 1
+               case "$ans" in
+               [lL]*)
+                       if ! local_present
+                       then
+                               if test -n "$(git ls-tree HEAD -- "$MERGED")"
+                               then
+                                       # Local isn't present, but it's a subdirectory
+                                       git ls-tree --full-name -r HEAD -- "$MERGED" |
+                                       git update-index --index-info || exit $?
+                               else
+                                       test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+                                       git update-index --force-remove "$MERGED"
+                                       cleanup_temp_files --save-backup
+                               fi
+                       elif is_submodule "$local_mode"
+                       then
+                               stage_submodule "$MERGED" "$local_sha1"
+                       else
+                               git checkout-index -f --stage=2 -- "$MERGED"
+                               git add -- "$MERGED"
+                       fi
+                       return 0
+                       ;;
+               [rR]*)
+                       if ! remote_present
+                       then
+                               if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"
+                               then
+                                       # Remote isn't present, but it's a subdirectory
+                                       git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" |
+                                       git update-index --index-info || exit $?
+                               else
+                                       test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+                                       git update-index --force-remove "$MERGED"
+                               fi
+                       elif is_submodule "$remote_mode"
+                       then
+                               ! is_submodule "$local_mode" &&
+                               test -e "$MERGED" &&
+                               mv -- "$MERGED" "$BACKUP"
+                               stage_submodule "$MERGED" "$remote_sha1"
+                       else
+                               test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
+                               git checkout-index -f --stage=3 -- "$MERGED"
+                               git add -- "$MERGED"
+                       fi
                        cleanup_temp_files --save-backup
-                   fi
-               elif is_submodule "$local_mode"; then
-                   stage_submodule "$MERGED" "$local_sha1"
-               else
-                   git checkout-index -f --stage=2 -- "$MERGED"
-                   git add -- "$MERGED"
-               fi
-               return 0
-               ;;
-           [rR]*)
-               if ! remote_present; then
-                   if test -n "$(git ls-tree MERGE_HEAD -- "$MERGED")"; then
-                       # Remote isn't present, but it's a subdirectory
-                       git ls-tree --full-name -r MERGE_HEAD -- "$MERGED" | git update-index --index-info || exit $?
-                   else
-                       test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
-                       git update-index --force-remove "$MERGED"
-                   fi
-               elif is_submodule "$remote_mode"; then
-                   ! is_submodule "$local_mode" && test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
-                   stage_submodule "$MERGED" "$remote_sha1"
-               else
-                   test -e "$MERGED" && mv -- "$MERGED" "$BACKUP"
-                   git checkout-index -f --stage=3 -- "$MERGED"
-                   git add -- "$MERGED"
-               fi
-               cleanup_temp_files --save-backup
-               return 0
-               ;;
-           [aA]*)
-               return 1
-               ;;
-           esac
+                       return 0
+                       ;;
+               [aA]*)
+                       return 1
+                       ;;
+               esac
        done
 }
 
 stage_submodule () {
-    path="$1"
-    submodule_sha1="$2"
-    mkdir -p "$path" || die "fatal: unable to create directory for module at $path"
-    # Find $path relative to work tree
-    work_tree_root=$(cd_to_toplevel && pwd)
-    work_rel_path=$(cd "$path" && GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix)
-    test -n "$work_rel_path" || die "fatal: unable to get path of module $path relative to work tree"
-    git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
+       path="$1"
+       submodule_sha1="$2"
+       mkdir -p "$path" ||
+       die "fatal: unable to create directory for module at $path"
+       # Find $path relative to work tree
+       work_tree_root=$(cd_to_toplevel && pwd)
+       work_rel_path=$(cd "$path" &&
+               GIT_WORK_TREE="${work_tree_root}" git rev-parse --show-prefix
+       )
+       test -n "$work_rel_path" ||
+       die "fatal: unable to get path of module $path relative to work tree"
+       git update-index --add --replace --cacheinfo 160000 "$submodule_sha1" "${work_rel_path%/}" || die
 }
 
 checkout_staged_file () {
-    tmpfile=$(expr \
-           "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
-           : '\([^     ]*\)    ')
-
-    if test $? -eq 0 -a -n "$tmpfile" ; then
-       mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
-    else
-       >"$3"
-    fi
+       tmpfile=$(expr \
+               "$(git checkout-index --temp --stage="$1" "$2" 2>/dev/null)" \
+               : '\([^ ]*\)    ')
+
+       if test $? -eq 0 -a -n "$tmpfile"
+       then
+               mv -- "$(git rev-parse --show-cdup)$tmpfile" "$3"
+       else
+               >"$3"
+       fi
 }
 
 merge_file () {
-    MERGED="$1"
+       MERGED="$1"
 
-    f=$(git ls-files -u -- "$MERGED")
-    if test -z "$f" ; then
-       if test ! -f "$MERGED" ; then
-           echo "$MERGED: file not found"
-       else
-           echo "$MERGED: file does not need merging"
+       f=$(git ls-files -u -- "$MERGED")
+       if test -z "$f"
+       then
+               if test ! -f "$MERGED"
+               then
+                       echo "$MERGED: file not found"
+               else
+                       echo "$MERGED: file does not need merging"
+               fi
+               return 1
        fi
-       return 1
-    fi
-
-    ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
-    BACKUP="./$MERGED.BACKUP.$ext"
-    LOCAL="./$MERGED.LOCAL.$ext"
-    REMOTE="./$MERGED.REMOTE.$ext"
-    BASE="./$MERGED.BASE.$ext"
-
-    base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
-    local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
-    remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
-
-    if is_submodule "$local_mode" || is_submodule "$remote_mode"; then
-       echo "Submodule merge conflict for '$MERGED':"
-       local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
-       remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
-       describe_file "$local_mode" "local" "$local_sha1"
-       describe_file "$remote_mode" "remote" "$remote_sha1"
-       resolve_submodule_merge
-       return
-    fi
-
-    mv -- "$MERGED" "$BACKUP"
-    cp -- "$BACKUP" "$MERGED"
-
-    checkout_staged_file 1 "$MERGED" "$BASE"
-    checkout_staged_file 2 "$MERGED" "$LOCAL"
-    checkout_staged_file 3 "$MERGED" "$REMOTE"
-
-    if test -z "$local_mode" -o -z "$remote_mode"; then
-       echo "Deleted merge conflict for '$MERGED':"
-       describe_file "$local_mode" "local" "$LOCAL"
-       describe_file "$remote_mode" "remote" "$REMOTE"
-       resolve_deleted_merge
-       return
-    fi
 
-    if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
-       echo "Symbolic link merge conflict for '$MERGED':"
+       ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
+       BACKUP="./$MERGED.BACKUP.$ext"
+       LOCAL="./$MERGED.LOCAL.$ext"
+       REMOTE="./$MERGED.REMOTE.$ext"
+       BASE="./$MERGED.BASE.$ext"
+
+       base_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==1) print $1;}')
+       local_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $1;}')
+       remote_mode=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $1;}')
+
+       if is_submodule "$local_mode" || is_submodule "$remote_mode"
+       then
+               echo "Submodule merge conflict for '$MERGED':"
+               local_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==2) print $2;}')
+               remote_sha1=$(git ls-files -u -- "$MERGED" | awk '{if ($3==3) print $2;}')
+               describe_file "$local_mode" "local" "$local_sha1"
+               describe_file "$remote_mode" "remote" "$remote_sha1"
+               resolve_submodule_merge
+               return
+       fi
+
+       mv -- "$MERGED" "$BACKUP"
+       cp -- "$BACKUP" "$MERGED"
+
+       checkout_staged_file 1 "$MERGED" "$BASE"
+       checkout_staged_file 2 "$MERGED" "$LOCAL"
+       checkout_staged_file 3 "$MERGED" "$REMOTE"
+
+       if test -z "$local_mode" -o -z "$remote_mode"
+       then
+               echo "Deleted merge conflict for '$MERGED':"
+               describe_file "$local_mode" "local" "$LOCAL"
+               describe_file "$remote_mode" "remote" "$REMOTE"
+               resolve_deleted_merge
+               return
+       fi
+
+       if is_symlink "$local_mode" || is_symlink "$remote_mode"
+       then
+               echo "Symbolic link merge conflict for '$MERGED':"
+               describe_file "$local_mode" "local" "$LOCAL"
+               describe_file "$remote_mode" "remote" "$REMOTE"
+               resolve_symlink_merge
+               return
+       fi
+
+       echo "Normal merge conflict for '$MERGED':"
        describe_file "$local_mode" "local" "$LOCAL"
        describe_file "$remote_mode" "remote" "$REMOTE"
-       resolve_symlink_merge
-       return
-    fi
-
-    echo "Normal merge conflict for '$MERGED':"
-    describe_file "$local_mode" "local" "$LOCAL"
-    describe_file "$remote_mode" "remote" "$REMOTE"
-    if "$prompt" = true; then
-       printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
-       read ans || return 1
-    fi
-
-    if base_present; then
-           present=true
-    else
-           present=false
-    fi
-
-    if ! run_merge_tool "$merge_tool" "$present"; then
-       echo "merge of $MERGED failed" 1>&2
-       mv -- "$BACKUP" "$MERGED"
-
-       if test "$merge_keep_temporaries" = "false"; then
-           cleanup_temp_files
+       if "$prompt" = true
+       then
+               printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
+               read ans || return 1
        fi
 
-       return 1
-    fi
+       if base_present
+       then
+               present=true
+       else
+               present=false
+       fi
+
+       if ! run_merge_tool "$merge_tool" "$present"
+       then
+               echo "merge of $MERGED failed" 1>&2
+               mv -- "$BACKUP" "$MERGED"
+
+               if test "$merge_keep_temporaries" = "false"
+               then
+                       cleanup_temp_files
+               fi
 
-    if test "$merge_keep_backup" = "true"; then
-       mv -- "$BACKUP" "$MERGED.orig"
-    else
-       rm -- "$BACKUP"
-    fi
+               return 1
+       fi
 
-    git add -- "$MERGED"
-    cleanup_temp_files
-    return 0
+       if test "$merge_keep_backup" = "true"
+       then
+               mv -- "$BACKUP" "$MERGED.orig"
+       else
+               rm -- "$BACKUP"
+       fi
+
+       git add -- "$MERGED"
+       cleanup_temp_files
+       return 0
 }
 
 show_tool_help () {
@@ -325,61 +356,61 @@ prompt=$(git config --bool mergetool.prompt || echo true)
 
 while test $# != 0
 do
-    case "$1" in
+       case "$1" in
        --tool-help)
                show_tool_help
                ;;
        -t|--tool*)
-           case "$#,$1" in
+               case "$#,$1" in
                *,*=*)
-                   merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
-                   ;;
+                       merge_tool=$(expr "z$1" : 'z-[^=]*=\(.*\)')
+                       ;;
                1,*)
-                   usage ;;
+                       usage ;;
                *)
-                   merge_tool="$2"
-                   shift ;;
-           esac
-           ;;
+                       merge_tool="$2"
+                       shift ;;
+               esac
+               ;;
        -y|--no-prompt)
-           prompt=false
-           ;;
+               prompt=false
+               ;;
        --prompt)
-           prompt=true
-           ;;
+               prompt=true
+               ;;
        --)
-           shift
-           break
-           ;;
+               shift
+               break
+               ;;
        -*)
-           usage
-           ;;
-       *)
-           break
-           ;;
-    esac
-    shift
-done
-
-prompt_after_failed_merge() {
-    while true; do
-       printf "Continue merging other unresolved paths (y/n) ? "
-       read ans || return 1
-       case "$ans" in
-
-           [yY]*)
-               return 0
+               usage
                ;;
-
-           [nN]*)
-               return 1
+       *)
+               break
                ;;
        esac
-    done
+       shift
+done
+
+prompt_after_failed_merge () {
+       while true
+       do
+               printf "Continue merging other unresolved paths (y/n) ? "
+               read ans || return 1
+               case "$ans" in
+               [yY]*)
+                       return 0
+                       ;;
+               [nN]*)
+                       return 1
+                       ;;
+               esac
+       done
 }
 
-if test -z "$merge_tool"; then
-    merge_tool=$(get_merge_tool "$merge_tool") || exit
+if test -z "$merge_tool"
+then
+       merge_tool=$(get_merge_tool "$merge_tool") || exit
 fi
 merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
 merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
@@ -388,22 +419,24 @@ last_status=0
 rollup_status=0
 files=
 
-if test $# -eq 0 ; then
-    cd_to_toplevel
+if test $# -eq 0
+then
+       cd_to_toplevel
 
-    if test -e "$GIT_DIR/MERGE_RR"
-    then
-       files=$(git rerere remaining)
-    else
-       files=$(git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u)
-    fi
+       if test -e "$GIT_DIR/MERGE_RR"
+       then
+               files=$(git rerere remaining)
+       else
+               files=$(git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u)
+       fi
 else
-    files=$(git ls-files -u -- "$@" | sed -e 's/^[^    ]*      //' | sort -u)
+       files=$(git ls-files -u -- "$@" | sed -e 's/^[^ ]*      //' | sort -u)
 fi
 
-if test -z "$files" ; then
-    echo "No files need merging"
-    exit 0
+if test -z "$files"
+then
+       echo "No files need merging"
+       exit 0
 fi
 
 printf "Merging:\n"
@@ -413,15 +446,17 @@ IFS='
 '
 for i in $files
 do
-    if test $last_status -ne 0; then
-       prompt_after_failed_merge || exit 1
-    fi
-    printf "\n"
-    merge_file "$i"
-    last_status=$?
-    if test $last_status -ne 0; then
-       rollup_status=1
-    fi
+       if test $last_status -ne 0
+       then
+               prompt_after_failed_merge || exit 1
+       fi
+       printf "\n"
+       merge_file "$i"
+       last_status=$?
+       if test $last_status -ne 0
+       then
+               rollup_status=1
+       fi
 done
 
 exit $rollup_status
index e67d37d2f93361337df6e680d2b2ab961e31ad66..aed1a2de3295bcd57a8c8c602cbdd28a6a6f2f7f 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1818,19 +1818,41 @@ def extractFilesFromCommit(self, commit):
         return files
 
     def stripRepoPath(self, path, prefixes):
-        if self.useClientSpec:
-            return self.clientSpecDirs.map_in_client(path)
+        """When streaming files, this is called to map a p4 depot path
+           to where it should go in git.  The prefixes are either
+           self.depotPaths, or self.branchPrefixes in the case of
+           branch detection."""
 
-        if self.keepRepoPath:
-            prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
+        if self.useClientSpec:
+            # branch detection moves files up a level (the branch name)
+            # from what client spec interpretation gives
+            path = self.clientSpecDirs.map_in_client(path)
+            if self.detectBranches:
+                for b in self.knownBranches:
+                    if path.startswith(b + "/"):
+                        path = path[len(b)+1:]
+
+        elif self.keepRepoPath:
+            # Preserve everything in relative path name except leading
+            # //depot/; just look at first prefix as they all should
+            # be in the same depot.
+            depot = re.sub("^(//[^/]+/).*", r'\1', prefixes[0])
+            if p4PathStartsWith(path, depot):
+                path = path[len(depot):]
 
-        for p in prefixes:
-            if p4PathStartsWith(path, p):
-                path = path[len(p):]
+        else:
+            for p in prefixes:
+                if p4PathStartsWith(path, p):
+                    path = path[len(p):]
+                    break
 
+        path = wildcard_decode(path)
         return path
 
     def splitFilesIntoBranches(self, commit):
+        """Look at each depotFile in the commit to figure out to what
+           branch it belongs."""
+
         branches = {}
         fnum = 0
         while commit.has_key("depotFile%s" % fnum):
@@ -1848,12 +1870,16 @@ def splitFilesIntoBranches(self, commit):
             file["type"] = commit["type%s" % fnum]
             fnum = fnum + 1
 
-            relPath = self.stripRepoPath(path, self.depotPaths)
-            relPath = wildcard_decode(relPath)
+            # start with the full relative path where this file would
+            # go in a p4 client
+            if self.useClientSpec:
+                relPath = self.clientSpecDirs.map_in_client(path)
+            else:
+                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
+                # add a trailing slash so that a commit into qt/4.2foo
+                # doesn't end up in qt/4.2, e.g.
                 if relPath.startswith(branch + "/"):
                     if branch not in branches:
                         branches[branch] = []
@@ -1867,7 +1893,6 @@ def splitFilesIntoBranches(self, commit):
 
     def streamOneP4File(self, file, contents):
         relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
-        relPath = wildcard_decode(relPath)
         if verbose:
             sys.stderr.write("%s\n" % relPath)
 
@@ -1936,7 +1961,6 @@ def streamOneP4File(self, file, contents):
 
     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)
@@ -2041,10 +2065,9 @@ def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
         gitStream.write(description)
         gitStream.write("\n")
 
-    def commit(self, details, files, branch, branchPrefixes, parent = ""):
+    def commit(self, details, files, branch, parent = ""):
         epoch = details["time"]
         author = details["user"]
-        self.branchPrefixes = branchPrefixes
 
         if self.verbose:
             print "commit into %s" % branch
@@ -2053,7 +2076,7 @@ def commit(self, details, files, branch, branchPrefixes, parent = ""):
         # create a commit.
         new_files = []
         for f in files:
-            if [p for p in branchPrefixes if p4PathStartsWith(f['path'], p)]:
+            if [p for p in self.branchPrefixes if p4PathStartsWith(f['path'], p)]:
                 new_files.append (f)
             else:
                 sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])
@@ -2070,8 +2093,8 @@ def commit(self, details, files, branch, branchPrefixes, parent = ""):
 
         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"]))
+        self.gitStream.write("\n[git-p4: depot-paths = \"%s\": change = %s" %
+                             (','.join(self.branchPrefixes), details["change"]))
         if len(details['options']) > 0:
             self.gitStream.write(": options = %s" % details['options'])
         self.gitStream.write("]\nEOT\n\n")
@@ -2094,7 +2117,7 @@ def commit(self, details, files, branch, branchPrefixes, parent = ""):
                 print "Change %s is labelled %s" % (change, labelDetails)
 
             files = p4CmdList(["files"] + ["%s...@%s" % (p, change)
-                                                    for p in branchPrefixes])
+                                                for p in self.branchPrefixes])
 
             if len(files) == len(labelRevisions):
 
@@ -2405,6 +2428,7 @@ def importChanges(self, changes):
                     for branch in branches.keys():
                         ## HACK  --hwn
                         branchPrefix = self.depotPaths[0] + branch + "/"
+                        self.branchPrefixes = [ branchPrefix ]
 
                         parent = ""
 
@@ -2449,19 +2473,19 @@ def importChanges(self, changes):
                             tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change))
                             if self.verbose:
                                 print "Creating temporary branch: " + tempBranch
-                            self.commit(description, filesForCommit, tempBranch, [branchPrefix])
+                            self.commit(description, filesForCommit, tempBranch)
                             self.tempBranches.append(tempBranch)
                             self.checkpoint()
                             blob = self.searchParent(parent, branch, tempBranch)
                         if blob:
-                            self.commit(description, filesForCommit, branch, [branchPrefix], blob)
+                            self.commit(description, filesForCommit, branch, 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)
+                            self.commit(description, filesForCommit, branch, parent)
                 else:
                     files = self.extractFilesFromCommit(description)
-                    self.commit(description, files, self.branch, self.depotPaths,
+                    self.commit(description, files, self.branch,
                                 self.initialParent)
                     self.initialParent = ""
             except IOError:
@@ -2525,7 +2549,7 @@ def importHeadRevision(self, revision):
 
         self.updateOptionDict(details)
         try:
-            self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
+            self.commit(details, self.extractFilesFromCommit(details), self.branch)
         except IOError:
             print "IO error with git fast-import. Is your git version recent enough?"
             print self.gitError.read()
@@ -2683,6 +2707,9 @@ def run(self, args):
 
         self.depotPaths = newPaths
 
+        # --detect-branches may change this for each branch
+        self.branchPrefixes = self.depotPaths
+
         self.loadUserMapFromCache()
         self.labels = {}
         if self.detectLabels:
index 0d2056f027cbd6811faee3c45088a11b24d86102..a09e8423ddb1d05c581ef72101c3f5201ce2eeb9 100644 (file)
@@ -569,11 +569,10 @@ do_next () {
        test -s "$todo" && return
 
        comment_for_reflog finish &&
-       shortonto=$(git rev-parse --short $onto) &&
        newhead=$(git rev-parse HEAD) &&
        case $head_name in
        refs/*)
-               message="$GIT_REFLOG_ACTION: $head_name onto $shortonto" &&
+               message="$GIT_REFLOG_ACTION: $head_name onto $onto" &&
                git update-ref -m "$message" $head_name $newhead $orig_head &&
                git symbolic-ref \
                  -m "$GIT_REFLOG_ACTION: returning to $head_name" \
index ef30c557c7dee549e891fe7605902ba58d0566a3..607137b9aaf5e3ea1a7f7f8e4b412afae2f2c320 100755 (executable)
@@ -681,6 +681,7 @@ sub ask {
        my ($prompt, %arg) = @_;
        my $valid_re = $arg{valid_re};
        my $default = $arg{default};
+       my $confirm_only = $arg{confirm_only};
        my $resp;
        my $i = 0;
        return defined $default ? $default : undef
@@ -698,6 +699,12 @@ sub ask {
                if (!defined $valid_re or $resp =~ /$valid_re/) {
                        return $resp;
                }
+               if ($confirm_only) {
+                       my $yesno = $term->readline("Are you sure you want to use <$resp> [y/N]? ");
+                       if (defined $yesno && $yesno =~ /y/i) {
+                               return $resp;
+                       }
+               }
        }
        return undef;
 }
@@ -745,13 +752,15 @@ sub file_declares_8bit_cte {
 if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
        $sender = ask("Who should the emails appear to be from? [$sender] ",
-                     default => $sender);
+                     default => $sender,
+                     valid_re => qr/\@.*\./, confirm_only => 1);
        print "Emails will be sent from: ", $sender, "\n";
        $prompting++;
 }
 
 if (!@initial_to && !defined $to_cmd) {
-       my $to = ask("Who should the emails be sent to? ");
+       my $to = ask("Who should the emails be sent to? ",
+                    valid_re => qr/\@.*\./, confirm_only => 1);
        push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
        $prompting++;
 }
@@ -777,7 +786,8 @@ sub expand_one_alias {
 
 if ($thread && !defined $initial_reply_to && $prompting) {
        $initial_reply_to = ask(
-               "Message-ID to be used as In-Reply-To for the first email? ");
+               "Message-ID to be used as In-Reply-To for the first email? ",
+               valid_re => qr/\@.*\./, confirm_only => 1);
 }
 if (defined $initial_reply_to) {
        $initial_reply_to =~ s/^\s*<?//;
@@ -862,11 +872,13 @@ sub make_message_id {
 sub unquote_rfc2047 {
        local ($_) = @_;
        my $encoding;
-       if (s/=\?([^?]+)\?q\?(.*)\?=/$2/g) {
+       s{=\?([^?]+)\?q\?(.*?)\?=}{
                $encoding = $1;
-               s/_/ /g;
-               s/=([0-9A-F]{2})/chr(hex($1))/eg;
-       }
+               my $e = $2;
+               $e =~ s/_/ /g;
+               $e =~ s/=([0-9A-F]{2})/chr(hex($1))/eg;
+               $e;
+       }eg;
        return wantarray ? ($_, $encoding) : $_;
 }
 
index 770a86e2b7a5e517e51d9897b274b876a6b9e0e1..ee0e0bc045bb7d92045a3afb0a042748834e6163 100644 (file)
@@ -9,8 +9,12 @@
 # you would cause "cd" to be taken to unexpected places.  If you
 # like CDPATH, define it for your interactive shell sessions without
 # exporting it.
+# But we protect ourselves from such a user mistake nevertheless.
 unset CDPATH
 
+# Similarly for IFS
+unset IFS
+
 git_broken_path_fix () {
        case ":$PATH:" in
        *:$1:*) : ok ;;
index 4e2c7f83314954de6655b0d1bff773c31dfe3ed1..bbefdf64244063f4708f381b8a772466d64e90e5 100755 (executable)
@@ -469,6 +469,7 @@ apply_stash () {
        else
                # Merge conflict; keep the exit status from merge-recursive
                status=$?
+               git rerere
                if test -n "$INDEX_OPTION"
                then
                        gettextln "Index was not unstashed." >&2
index aac575e74f833c9bce28b779733b28a7611246b2..3e2045e52daf43cc351af23af0fd770fb1a5044f 100755 (executable)
@@ -109,26 +109,48 @@ resolve_relative_url ()
 #
 module_list()
 {
-       git ls-files --error-unmatch --stage -- "$@" |
+       (
+               git ls-files --error-unmatch --stage -- "$@" ||
+               echo "unmatched pathspec exists"
+       ) |
        perl -e '
        my %unmerged = ();
        my ($null_sha1) = ("0" x 40);
+       my @out = ();
+       my $unmatched = 0;
        while (<STDIN>) {
+               if (/^unmatched pathspec/) {
+                       $unmatched = 1;
+                       next;
+               }
                chomp;
                my ($mode, $sha1, $stage, $path) =
                        /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
                next unless $mode eq "160000";
                if ($stage ne "0") {
                        if (!$unmerged{$path}++) {
-                               print "$mode $null_sha1 U\t$path\n";
+                               push @out, "$mode $null_sha1 U\t$path\n";
                        }
                        next;
                }
-               print "$_\n";
+               push @out, "$_\n";
+       }
+       if ($unmatched) {
+               print "#unmatched\n";
+       } else {
+               print for (@out);
        }
        '
 }
 
+die_if_unmatched ()
+{
+       if test "$1" = "#unmatched"
+       then
+               exit 1
+       fi
+}
+
 #
 # Map submodule path to submodule name
 #
@@ -385,6 +407,7 @@ cmd_foreach()
        module_list |
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                if test -e "$sm_path"/.git
                then
                        say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
@@ -437,6 +460,7 @@ cmd_init()
        module_list "$@" |
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
 
                # Copy url setting when it is not set yet
@@ -537,6 +561,7 @@ cmd_update()
        err=
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                if test "$stage" = U
                then
                        echo >&2 "Skipping unmerged submodule $sm_path"
@@ -578,7 +603,7 @@ Maybe you want to use 'update --init'?")"
                        die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")"
                fi
 
-               if test "$subsha1" != "$sha1"
+               if test "$subsha1" != "$sha1" -o -n "$force"
                then
                        subforce=$force
                        # If we don't already have a -f flag and the submodule has never been checked out
@@ -932,6 +957,7 @@ cmd_status()
        module_list "$@" |
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                name=$(module_name "$sm_path") || exit
                url=$(git config submodule."$name".url)
                displaypath="$prefix$sm_path"
@@ -1000,6 +1026,7 @@ cmd_sync()
        module_list "$@" |
        while read mode sha1 stage sm_path
        do
+               die_if_unmatched "$mode"
                name=$(module_name "$sm_path")
                url=$(git config -f .gitmodules --get submodule."$name".url)
 
index 828b8f0c8e6de81593db108495565e591cd91363..0d77ffb0b92b9e94454aeb94b434d9f86acd8a13 100755 (executable)
 use Git::SVN::Log;
 use Git::SVN::Migration;
 
-use Git::SVN::Utils qw(fatal can_compress);
+use Git::SVN::Utils qw(
+       fatal
+       can_compress
+       canonicalize_path
+       canonicalize_url
+       join_paths
+       add_path_to_url
+       join_paths
+);
+
 use Git qw(
        git_cmd_try
        command
@@ -1231,7 +1240,7 @@ sub cmd_show_ignore {
        my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
        $gs ||= Git::SVN->new;
        my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
-       $gs->prop_walk($gs->{path}, $r, sub {
+       $gs->prop_walk($gs->path, $r, sub {
                my ($gs, $path, $props) = @_;
                print STDOUT "\n# $path\n";
                my $s = $props->{'svn:ignore'} or return;
@@ -1247,7 +1256,7 @@ sub cmd_show_externals {
        my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
        $gs ||= Git::SVN->new;
        my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
-       $gs->prop_walk($gs->{path}, $r, sub {
+       $gs->prop_walk($gs->path, $r, sub {
                my ($gs, $path, $props) = @_;
                print STDOUT "\n# $path\n";
                my $s = $props->{'svn:externals'} or return;
@@ -1262,7 +1271,7 @@ sub cmd_create_ignore {
        my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
        $gs ||= Git::SVN->new;
        my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
-       $gs->prop_walk($gs->{path}, $r, sub {
+       $gs->prop_walk($gs->path, $r, sub {
                my ($gs, $path, $props) = @_;
                # $path is of the form /path/to/dir/
                $path = '.' . $path;
@@ -1292,31 +1301,6 @@ sub cmd_mkdirs {
        $gs->mkemptydirs($_revision);
 }
 
-sub canonicalize_path {
-       my ($path) = @_;
-       my $dot_slash_added = 0;
-       if (substr($path, 0, 1) ne "/") {
-               $path = "./" . $path;
-               $dot_slash_added = 1;
-       }
-       # File::Spec->canonpath doesn't collapse x/../y into y (for a
-       # good reason), so let's do this manually.
-       $path =~ s#/+#/#g;
-       $path =~ s#/\.(?:/|$)#/#g;
-       $path =~ s#/[^/]+/\.\.##g;
-       $path =~ s#/$##g;
-       $path =~ s#^\./## if $dot_slash_added;
-       $path =~ s#^/##;
-       $path =~ s#^\.$##;
-       return $path;
-}
-
-sub canonicalize_url {
-       my ($url) = @_;
-       $url =~ s#^([^:]+://[^/]*/)(.*)$#$1 . canonicalize_path($2)#e;
-       return $url;
-}
-
 # get_svnprops(PATH)
 # ------------------
 # Helper for cmd_propget and cmd_proplist below.
@@ -1330,7 +1314,7 @@ sub get_svnprops {
        $path = $cmd_dir_prefix . $path;
        fatal("No such file or directory: $path") unless -e $path;
        my $is_dir = -d $path ? 1 : 0;
-       $path = $gs->{path} . '/' . $path;
+       $path = join_paths($gs->{path}, $path);
 
        # canonicalize the path (otherwise libsvn will abort or fail to
        # find the file)
@@ -1431,8 +1415,8 @@ sub cmd_commit_diff {
                        fatal("Needed URL or usable git-svn --id in ",
                              "the command-line\n", $usage);
                }
-               $url = $gs->{url};
-               $svn_path = $gs->{path};
+               $url = $gs->url;
+               $svn_path = $gs->path;
        }
        unless (defined $_revision) {
                fatal("-r|--revision is a required argument\n", $usage);
@@ -1466,24 +1450,6 @@ sub cmd_commit_diff {
        }
 }
 
-sub escape_uri_only {
-       my ($uri) = @_;
-       my @tmp;
-       foreach (split m{/}, $uri) {
-               s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
-               push @tmp, $_;
-       }
-       join('/', @tmp);
-}
-
-sub escape_url {
-       my ($url) = @_;
-       if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) {
-               my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
-               $url = "$scheme://$domain$uri";
-       }
-       $url;
-}
 
 sub cmd_info {
        my $path = canonicalize_path(defined($_[0]) ? $_[0] : ".");
@@ -1508,21 +1474,21 @@ sub cmd_info {
        # canonicalize_path() will return "" to make libsvn 1.5.x happy,
        $path = "." if $path eq "";
 
-       my $full_url = $url . ($fullpath eq "" ? "" : "/$fullpath");
+       my $full_url = canonicalize_url( add_path_to_url( $url, $fullpath ) );
 
        if ($_url) {
-               print escape_url($full_url), "\n";
+               print "$full_url\n";
                return;
        }
 
        my $result = "Path: $path\n";
        $result .= "Name: " . basename($path) . "\n" if $file_type ne "dir";
-       $result .= "URL: " . escape_url($full_url) . "\n";
+       $result .= "URL: $full_url\n";
 
        eval {
                my $repos_root = $gs->repos_root;
                Git::SVN::remove_username($repos_root);
-               $result .= "Repository Root: " . escape_url($repos_root) . "\n";
+               $result .= "Repository Root: " . canonicalize_url($repos_root) . "\n";
        };
        if ($@) {
                $result .= "Repository Root: (offline)\n";
@@ -1669,7 +1635,9 @@ sub post_fetch_checkout {
 
 sub complete_svn_url {
        my ($url, $path) = @_;
-       $path =~ s#/+$##;
+       $path = canonicalize_path($path);
+
+       # If the path is not a URL...
        if ($path !~ m#^[a-z\+]+://#) {
                if (!defined $url || $url !~ m#^[a-z\+]+://#) {
                        fatal("E: '$path' is not a complete URL ",
@@ -1686,7 +1654,7 @@ sub complete_url_ls_init {
                print STDERR "W: $switch not specified\n";
                return;
        }
-       $repo_path =~ s#/+$##;
+       $repo_path = canonicalize_path($repo_path);
        if ($repo_path =~ m#^[a-z\+]+://#) {
                $ra = Git::SVN::Ra->new($repo_path);
                $repo_path = '';
@@ -1697,18 +1665,18 @@ sub complete_url_ls_init {
                              "and a separate URL is not specified");
                }
        }
-       my $url = $ra->{url};
+       my $url = $ra->url;
        my $gs = Git::SVN->init($url, undef, undef, undef, 1);
        my $k = "svn-remote.$gs->{repo_id}.url";
        my $orig_url = eval { command_oneline(qw/config --get/, $k) };
-       if ($orig_url && ($orig_url ne $gs->{url})) {
+       if ($orig_url && ($orig_url ne $gs->url)) {
                die "$k already set: $orig_url\n",
-                   "wanted to set to: $gs->{url}\n";
+                   "wanted to set to: $gs->url\n";
        }
-       command_oneline('config', $k, $gs->{url}) unless $orig_url;
-       my $remote_path = "$gs->{path}/$repo_path";
+       command_oneline('config', $k, $gs->url) unless $orig_url;
+
+       my $remote_path = join_paths( $gs->path, $repo_path );
        $remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
-       $remote_path =~ s#/+#/#g;
        $remote_path =~ s#^/##g;
        $remote_path .= "/*" if $remote_path !~ /\*/;
        my ($n) = ($switch =~ /^--(\w+)/);
index 22270ce46bb73bc8fc8e82409ef0179576f7ba20..d93bd990a9d7fc9ad7c926e25f5d58f4e7514260 100755 (executable)
@@ -2038,7 +2038,7 @@ proc makewindow {} {
     set file {
        mc "File" cascade {
            {mc "Update" command updatecommits -accelerator F5}
-           {mc "Reload" command reloadcommits -accelerator Meta1-F5}
+           {mc "Reload" command reloadcommits -accelerator Shift-F5}
            {mc "Reread references" command rereadrefs}
            {mc "List references" command showrefs -accelerator F2}
            {xx "" separator}
@@ -2495,7 +2495,7 @@ proc makewindow {} {
     bindkey ? {dofind -1 1}
     bindkey f nextfile
     bind . <F5> updatecommits
-    bind . <$M1B-F5> reloadcommits
+    bind . <Shift-F5> reloadcommits
     bind . <F2> showrefs
     bind . <Shift-F4> {newview 0}
     catch { bind . <Shift-Key-XF86_Switch_VT_4> {newview 0} }
@@ -10599,7 +10599,7 @@ proc movedhead {hid head} {
 }
 
 proc changedrefs {} {
-    global cached_dheads cached_dtags cached_atags
+    global cached_dheads cached_dtags cached_atags cached_tagcontent
     global arctags archeads arcnos arcout idheads idtags
 
     foreach id [concat [array names idheads] [array names idtags]] {
@@ -10611,6 +10611,7 @@ proc changedrefs {} {
            }
        }
     }
+    catch {unset cached_tagcontent}
     catch {unset cached_dtags}
     catch {unset cached_atags}
     catch {unset cached_dheads}
@@ -10663,7 +10664,7 @@ proc listrefs {id} {
 }
 
 proc showtag {tag isnew} {
-    global ctext tagcontents tagids linknum tagobjid
+    global ctext cached_tagcontent tagids linknum tagobjid
 
     if {$isnew} {
        addtohistory [list showtag $tag 0] savectextpos
@@ -10672,13 +10673,13 @@ proc showtag {tag isnew} {
     clear_ctext
     settabs 0
     set linknum 0
-    if {![info exists tagcontents($tag)]} {
+    if {![info exists cached_tagcontent($tag)]} {
        catch {
-           set tagcontents($tag) [exec git cat-file tag $tag]
+           set cached_tagcontent($tag) [exec git cat-file tag $tag]
        }
     }
-    if {[info exists tagcontents($tag)]} {
-       set text $tagcontents($tag)
+    if {[info exists cached_tagcontent($tag)]} {
+       set text $cached_tagcontent($tag)
     } else {
        set text "[mc "Tag"]: $tag\n[mc "Id"]:  $tagids($tag)"
     }
@@ -11532,6 +11533,11 @@ if {[catch {package require Tk 8.4} err]} {
     exit 1
 }
 
+# Unset GIT_TRACE var if set
+if { [info exists ::env(GIT_TRACE)] } {
+    unset ::env(GIT_TRACE)
+}
+
 # defaults...
 set wrcomcmd "git diff-tree --stdin -p --pretty"
 
index 3d6a7053881ec7646dc28a2eba991a15ff80c634..7f8c1878d407e07c3ac4ac16d840557a18176d29 100755 (executable)
@@ -54,6 +54,11 @@ sub evaluate_uri {
        # to build the base URL ourselves:
        our $path_info = decode_utf8($ENV{"PATH_INFO"});
        if ($path_info) {
+               # $path_info has already been URL-decoded by the web server, but
+               # $my_url and $my_uri have not. URL-decode them so we can properly
+               # strip $path_info.
+               $my_url = unescape($my_url);
+               $my_uri = unescape($my_uri);
                if ($my_url =~ s,\Q$path_info\E$,, &&
                    $my_uri =~ s,\Q$path_info\E$,, &&
                    defined $ENV{'SCRIPT_NAME'}) {
diff --git a/grep.h b/grep.h
index ed7de6bec8e604a7b3dcb9f9a19053696b7c3fdf..75afb7b10564a58f6427a03abccfb334facfea3b 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -58,6 +58,14 @@ enum grep_expr_node {
        GREP_NODE_OR
 };
 
+enum grep_pattern_type {
+       GREP_PATTERN_TYPE_UNSPECIFIED = 0,
+       GREP_PATTERN_TYPE_BRE,
+       GREP_PATTERN_TYPE_ERE,
+       GREP_PATTERN_TYPE_FIXED,
+       GREP_PATTERN_TYPE_PCRE
+};
+
 struct grep_expr {
        enum grep_expr_node node;
        unsigned hit;
@@ -103,6 +111,8 @@ struct grep_opt {
        int max_depth;
        int funcname;
        int funcbody;
+       int extended_regexp_option;
+       int pattern_type_option;
        char color_context[COLOR_MAXLEN];
        char color_filename[COLOR_MAXLEN];
        char color_function[COLOR_MAXLEN];
index a832ca77a31245e8ef9c71adcc0d7110fcda1e5c..8701c1215d21cd0413c1d69be91b309984cf6b1f 100644 (file)
@@ -1610,9 +1610,8 @@ static int verify_merge_base(unsigned char *head_sha1, struct ref *remote)
 {
        struct commit *head = lookup_commit_or_die(head_sha1, "HEAD");
        struct commit *branch = lookup_commit_or_die(remote->old_sha1, remote->name);
-       struct commit_list *merge_bases = get_merge_bases(head, branch, 1);
 
-       return (merge_bases && !merge_bases->next && merge_bases->item == branch);
+       return in_merge_bases(branch, head);
 }
 
 static int delete_remote_branch(const char *pattern, int force)
diff --git a/http.c b/http.c
index b61ac85d4b19a0b9121c5b19f2b9c731bbb238d4..9bac1d89fdb639f6991bfe9f683ccf7bd5ada4cb 100644 (file)
--- a/http.c
+++ b/http.c
@@ -745,6 +745,35 @@ char *get_remote_object_url(const char *url, const char *hex,
        return strbuf_detach(&buf, NULL);
 }
 
+int handle_curl_result(struct active_request_slot *slot)
+{
+       struct slot_results *results = slot->results;
+
+       if (results->curl_result == CURLE_OK) {
+               credential_approve(&http_auth);
+               return HTTP_OK;
+       } else if (missing_target(results))
+               return HTTP_MISSING_TARGET;
+       else if (results->http_code == 401) {
+               if (http_auth.username && http_auth.password) {
+                       credential_reject(&http_auth);
+                       return HTTP_NOAUTH;
+               } else {
+                       credential_fill(&http_auth);
+                       init_curl_http_auth(slot->curl);
+                       return HTTP_REAUTH;
+               }
+       } else {
+#if LIBCURL_VERSION_NUM >= 0x070c00
+               if (!curl_errorstr[0])
+                       strlcpy(curl_errorstr,
+                               curl_easy_strerror(results->curl_result),
+                               sizeof(curl_errorstr));
+#endif
+               return HTTP_ERROR;
+       }
+}
+
 /* http_request() targets */
 #define HTTP_REQUEST_STRBUF    0
 #define HTTP_REQUEST_FILE      1
@@ -792,26 +821,7 @@ static int http_request(const char *url, void *result, int target, int options)
 
        if (start_active_slot(slot)) {
                run_active_slot(slot);
-               if (results.curl_result == CURLE_OK)
-                       ret = HTTP_OK;
-               else if (missing_target(&results))
-                       ret = HTTP_MISSING_TARGET;
-               else if (results.http_code == 401) {
-                       if (http_auth.username && http_auth.password) {
-                               credential_reject(&http_auth);
-                               ret = HTTP_NOAUTH;
-                       } else {
-                               credential_fill(&http_auth);
-                               init_curl_http_auth(slot->curl);
-                               ret = HTTP_REAUTH;
-                       }
-               } else {
-                       if (!curl_errorstr[0])
-                               strlcpy(curl_errorstr,
-                                       curl_easy_strerror(results.curl_result),
-                                       sizeof(curl_errorstr));
-                       ret = HTTP_ERROR;
-               }
+               ret = handle_curl_result(slot);
        } else {
                error("Unable to start HTTP request for %s", url);
                ret = HTTP_START_FAILED;
@@ -820,9 +830,6 @@ static int http_request(const char *url, void *result, int target, int options)
        curl_slist_free_all(headers);
        strbuf_release(&buf);
 
-       if (ret == HTTP_OK)
-               credential_approve(&http_auth);
-
        return ret;
 }
 
diff --git a/http.h b/http.h
index 915c2862a65bdbf42472099f9ed53ca68c2ccfd9..12de25597df4077a52a44dace83093091514c10d 100644 (file)
--- a/http.h
+++ b/http.h
@@ -78,6 +78,7 @@ extern int start_active_slot(struct active_request_slot *slot);
 extern void run_active_slot(struct active_request_slot *slot);
 extern void finish_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
+extern int handle_curl_result(struct active_request_slot *slot);
 
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);
diff --git a/ident.c b/ident.c
index 443c0751bd1f63c059649f9f7f9174349cc1d270..484e0a980308fd2201bf443815a58c41bea17073 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -210,8 +210,10 @@ int split_ident_line(struct ident_split *split, const char *line, int len)
                        split->name_end = cp + 1;
                        break;
                }
-       if (!split->name_end)
-               return status;
+       if (!split->name_end) {
+               /* no human readable name */
+               split->name_end = split->name_begin;
+       }
 
        for (cp = split->mail_begin; cp < line + len; cp++)
                if (*cp == '>') {
index 39b2e165e05472daf38cd6f458541b6db4abc9a8..7866ca1026730e2e0446f711e3c621e44cef9f60 100644 (file)
@@ -493,8 +493,7 @@ static struct string_list *get_renames(struct merge_options *o,
        opts.rename_score = o->rename_score;
        opts.show_rename_progress = o->show_rename_progress;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opts) < 0)
-               die(_("diff setup failed"));
+       diff_setup_done(&opts);
        diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
        diffcore_std(&opts);
        if (opts.needed_rename_limit > o->needed_rename_limit)
@@ -614,23 +613,6 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
        return newpath;
 }
 
-static void flush_buffer(int fd, const char *buf, unsigned long size)
-{
-       while (size > 0) {
-               long ret = write_in_full(fd, buf, size);
-               if (ret < 0) {
-                       /* Ignore epipe */
-                       if (errno == EPIPE)
-                               break;
-                       die_errno("merge-recursive");
-               } else if (!ret) {
-                       die(_("merge-recursive: disk full?"));
-               }
-               size -= ret;
-               buf += ret;
-       }
-}
-
 static int dir_in_way(const char *path, int check_working_copy)
 {
        int pos, pathlen = strlen(path);
@@ -789,7 +771,7 @@ static void update_file_flags(struct merge_options *o,
                        fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
                        if (fd < 0)
                                die_errno(_("failed to open '%s'"), path);
-                       flush_buffer(fd, buf, size);
+                       write_in_full(fd, buf, size);
                        close(fd);
                } else if (S_ISLNK(mode)) {
                        char *lnk = xmemdupz(buf, size);
diff --git a/mergetools/codecompare b/mergetools/codecompare
new file mode 100644 (file)
index 0000000..3f0486b
--- /dev/null
@@ -0,0 +1,25 @@
+diff_cmd () {
+       "$merge_tool_path" "$LOCAL" "$REMOTE"
+}
+
+merge_cmd () {
+       touch "$BACKUP"
+       if $base_present
+       then
+               "$merge_tool_path" -MF="$LOCAL" -TF="$REMOTE" -BF="$BASE" \
+                       -RF="$MERGED"
+       else
+               "$merge_tool_path" -MF="$LOCAL" -TF="$REMOTE" \
+                       -RF="$MERGED"
+       fi
+       check_unchanged
+}
+
+translate_merge_tool_path() {
+       if merge_mode
+       then
+               echo CodeMerge
+       else
+               echo CodeCompare
+       fi
+}
index 29c6411fc63a01460f95b25bf62115c350ef2191..0f67bd3f9605aa7a3c98b88b4ae4aae037ace3d7 100644 (file)
@@ -126,8 +126,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
        diff_setup(&opt);
        DIFF_OPT_SET(&opt, RECURSIVE);
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&opt);
        diff_tree_sha1(base, remote, "", &opt);
        diffcore_std(&opt);
 
@@ -190,8 +189,7 @@ static void diff_tree_local(struct notes_merge_options *o,
        diff_setup(&opt);
        DIFF_OPT_SET(&opt, RECURSIVE);
        opt.output_format = DIFF_FORMAT_NO_OUTPUT;
-       if (diff_setup_done(&opt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&opt);
        diff_tree_sha1(base, local, "", &opt);
        diffcore_std(&opt);
 
index 77a4a8b2e6d2f6bc08be74b39ee6dd4964828763..71a39c60d942601a1cf30e218df56777742d5580 100644 (file)
@@ -238,6 +238,6 @@ extern int parse_opt_noop_cb(const struct option *, const char *, int);
 #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 }
+       { OPTION_CALLBACK, (s), (l), (v), N_("style"), (h), PARSE_OPT_OPTARG, parseopt_column_callback }
 
 #endif
index 5717257051aceff129a4d0777c0a11bc156cae54..bc8a28fdd71ae1476002d26adec64f54841f5cba 100644 (file)
@@ -39,8 +39,7 @@ int init_patch_ids(struct patch_ids *ids)
        memset(ids, 0, sizeof(*ids));
        diff_setup(&ids->diffopts);
        DIFF_OPT_SET(&ids->diffopts, RECURSIVE);
-       if (diff_setup_done(&ids->diffopts) < 0)
-               return error("diff_setup_done failed");
+       diff_setup_done(&ids->diffopts);
        return 0;
 }
 
index 8478d0c95293b531547084e19b6680cf73187469..acb25394f433bc4043f835be4944b13785d46356 100644 (file)
@@ -23,7 +23,14 @@ package Git::SVN;
     command_output_pipe
     command_close_pipe
 );
-use Git::SVN::Utils qw(fatal can_compress);
+use Git::SVN::Utils qw(
+       fatal
+       can_compress
+       join_paths
+       canonicalize_path
+       canonicalize_url
+       add_path_to_url
+);
 
 my $can_use_yaml;
 BEGIN {
@@ -195,9 +202,9 @@ sub read_all_remotes {
                } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
                        $r->{$1}->{svm} = {};
                } elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
-                       $r->{$1}->{url} = $2;
+                       $r->{$1}->{url} = canonicalize_url($2);
                } elsif (m!^(.+)\.pushurl=\s*(.*)\s*$!) {
-                       $r->{$1}->{pushurl} = $2;
+                       $r->{$1}->{pushurl} = canonicalize_url($2);
                } elsif (m!^(.+)\.ignore-refs=\s*(.*)\s*$!) {
                        $r->{$1}->{ignore_refs_regex} = $2;
                } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) {
@@ -290,7 +297,7 @@ sub find_existing_remote {
 
 sub init_remote_config {
        my ($self, $url, $no_write) = @_;
-       $url =~ s!/+$!!; # strip trailing slash
+       $url = canonicalize_url($url);
        my $r = read_all_remotes();
        my $existing = find_existing_remote($url, $r);
        if ($existing) {
@@ -314,12 +321,10 @@ sub init_remote_config {
                                print STDERR "Using higher level of URL: ",
                                             "$url => $min_url\n";
                        }
-                       my $old_path = $self->{path};
-                       $self->{path} = $url;
-                       $self->{path} =~ s!^\Q$min_url\E(/|$)!!;
-                       if (length $old_path) {
-                               $self->{path} .= "/$old_path";
-                       }
+                       my $old_path = $self->path;
+                       $url =~ s!^\Q$min_url\E(/|$)!!;
+                       $url = join_paths($url, $old_path);
+                       $self->path($url);
                        $url = $min_url;
                }
        }
@@ -343,18 +348,22 @@ sub init_remote_config {
        unless ($no_write) {
                command_noisy('config',
                              "svn-remote.$self->{repo_id}.url", $url);
-               $self->{path} =~ s{^/}{};
-               $self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
+               my $path = $self->path;
+               $path =~ s{^/}{};
+               $path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
+               $self->path($path);
                command_noisy('config', '--add',
                              "svn-remote.$self->{repo_id}.fetch",
-                             "$self->{path}:".$self->refname);
+                             $self->path.":".$self->refname);
        }
-       $self->{url} = $url;
+       $self->url($url);
 }
 
 sub find_by_url { # repos_root and, path are optional
        my ($class, $full_url, $repos_root, $path) = @_;
 
+       $full_url = canonicalize_url($full_url);
+
        return undef unless defined $full_url;
        remove_username($full_url);
        remove_username($repos_root) if defined $repos_root;
@@ -393,6 +402,11 @@ sub find_by_url { # repos_root and, path are optional
                        }
                        $p =~ s#^\Q$z\E(?:/|$)#$prefix# or next;
                }
+
+               # remote fetch paths are not URI escaped.  Decode ours
+               # so they match
+               $p = uri_decode($p);
+
                foreach my $f (keys %$fetch) {
                        next if $f ne $p;
                        return Git::SVN->new($fetch->{$f}, $repo_id, $f);
@@ -435,20 +449,25 @@ sub new {
                }
        }
        my $self = _new($class, $repo_id, $ref_id, $path);
-       if (!defined $self->{path} || !length $self->{path}) {
+       if (!defined $self->path || !length $self->path) {
                my $fetch = command_oneline('config', '--get',
                                            "svn-remote.$repo_id.fetch",
                                            ":$ref_id\$") or
                     die "Failed to read \"svn-remote.$repo_id.fetch\" ",
                         "\":$ref_id\$\" in config\n";
-               ($self->{path}, undef) = split(/\s*:\s*/, $fetch);
+               my($path) = split(/\s*:\s*/, $fetch);
+               $self->path($path);
        }
-       $self->{path} =~ s{/+}{/}g;
-       $self->{path} =~ s{\A/}{};
-       $self->{path} =~ s{/\z}{};
-       $self->{url} = command_oneline('config', '--get',
-                                      "svn-remote.$repo_id.url") or
+       {
+               my $path = $self->path;
+               $path =~ s{\A/}{};
+               $path =~ s{/\z}{};
+               $self->path($path);
+       }
+       my $url = command_oneline('config', '--get',
+                                 "svn-remote.$repo_id.url") or
                   die "Failed to read \"svn-remote.$repo_id.url\" in config\n";
+       $self->url($url);
        $self->{pushurl} = eval { command_oneline('config', '--get',
                                  "svn-remote.$repo_id.pushurl") };
        $self->rebuild;
@@ -552,8 +571,7 @@ sub _set_svm_vars {
                # username is of no interest
                $src =~ s{(^[a-z\+]*://)[^/@]*@}{$1};
 
-               my $replace = $ra->{url};
-               $replace .= "/$path" if length $path;
+               my $replace = add_path_to_url($ra->url, $path);
 
                my $section = "svn-remote.$self->{repo_id}";
                tmp_config("$section.svm-source", $src);
@@ -567,20 +585,21 @@ sub _set_svm_vars {
        }
 
        my $r = $ra->get_latest_revnum;
-       my $path = $self->{path};
+       my $path = $self->path;
        my %tried;
        while (length $path) {
-               unless ($tried{"$self->{url}/$path"}) {
+               my $try = add_path_to_url($self->url, $path);
+               unless ($tried{$try}) {
                        return $ra if $self->read_svm_props($ra, $path, $r);
-                       $tried{"$self->{url}/$path"} = 1;
+                       $tried{$try} = 1;
                }
                $path =~ s#/?[^/]+$##;
        }
        die "Path: '$path' should be ''\n" if $path ne '';
        return $ra if $self->read_svm_props($ra, $path, $r);
-       $tried{"$self->{url}/$path"} = 1;
+       $tried{ add_path_to_url($self->url, $path) } = 1;
 
-       if ($ra->{repos_root} eq $self->{url}) {
+       if ($ra->{repos_root} eq $self->url) {
                die @err, (map { "  $_\n" } keys %tried), "\n";
        }
 
@@ -590,20 +609,21 @@ sub _set_svm_vars {
        $path = $ra->{svn_path};
        $ra = Git::SVN::Ra->new($ra->{repos_root});
        while (length $path) {
-               unless ($tried{"$ra->{url}/$path"}) {
+               my $try = add_path_to_url($ra->url, $path);
+               unless ($tried{$try}) {
                        $ok = $self->read_svm_props($ra, $path, $r);
                        last if $ok;
-                       $tried{"$ra->{url}/$path"} = 1;
+                       $tried{$try} = 1;
                }
                $path =~ s#/?[^/]+$##;
        }
        die "Path: '$path' should be ''\n" if $path ne '';
        $ok ||= $self->read_svm_props($ra, $path, $r);
-       $tried{"$ra->{url}/$path"} = 1;
+       $tried{ add_path_to_url($ra->url, $path) } = 1;
        if (!$ok) {
                die @err, (map { "  $_\n" } keys %tried), "\n";
        }
-       Git::SVN::Ra->new($self->{url});
+       Git::SVN::Ra->new($self->url);
 }
 
 sub svnsync {
@@ -670,7 +690,7 @@ sub ra_uuid {
                if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/i) {
                        $self->{ra_uuid} = $uuid;
                } else {
-                       die "ra_uuid called without URL\n" unless $self->{url};
+                       die "ra_uuid called without URL\n" unless $self->url;
                        $self->{ra_uuid} = $self->ra->get_uuid;
                        tmp_config('--add', $key, $self->{ra_uuid});
                }
@@ -694,7 +714,7 @@ sub repos_root {
 
 sub ra {
        my ($self) = shift;
-       my $ra = Git::SVN::Ra->new($self->{url});
+       my $ra = Git::SVN::Ra->new($self->url);
        $self->_set_repos_root($ra->{repos_root});
        if ($self->use_svm_props && !$self->{svm}) {
                if ($self->no_metadata) {
@@ -728,7 +748,7 @@ sub prop_walk {
        $path =~ s#^/*#/#g;
        my $p = $path;
        # Strip the irrelevant part of the path.
-       $p =~ s#^/+\Q$self->{path}\E(/|$)#/#;
+       $p =~ s#^/+\Q@{[$self->path]}\E(/|$)#/#;
        # Ensure the path is terminated by a `/'.
        $p =~ s#/*$#/#;
 
@@ -749,7 +769,7 @@ sub prop_walk {
 
        foreach (sort keys %$dirent) {
                next if $dirent->{$_}->{kind} != $SVN::Node::dir;
-               $self->prop_walk($self->{path} . $p . $_, $rev, $sub);
+               $self->prop_walk($self->path . $p . $_, $rev, $sub);
        }
 }
 
@@ -919,20 +939,19 @@ sub rewrite_uuid {
 
 sub metadata_url {
        my ($self) = @_;
-       ($self->rewrite_root || $self->{url}) .
-          (length $self->{path} ? '/' . $self->{path} : '');
+       my $url = $self->rewrite_root || $self->url;
+       return canonicalize_url( add_path_to_url( $url, $self->path ) );
 }
 
 sub full_url {
        my ($self) = @_;
-       $self->{url} . (length $self->{path} ? '/' . $self->{path} : '');
+       return canonicalize_url( add_path_to_url( $self->url, $self->path ) );
 }
 
 sub full_pushurl {
        my ($self) = @_;
        if ($self->{pushurl}) {
-               return $self->{pushurl} . (length $self->{path} ? '/' .
-                      $self->{path} : '');
+               return canonicalize_url( add_path_to_url( $self->{pushurl}, $self->path ) );
        } else {
                return $self->full_url;
        }
@@ -1048,20 +1067,20 @@ sub do_git_commit {
 
 sub match_paths {
        my ($self, $paths, $r) = @_;
-       return 1 if $self->{path} eq '';
-       if (my $path = $paths->{"/$self->{path}"}) {
+       return 1 if $self->path eq '';
+       if (my $path = $paths->{"/".$self->path}) {
                return ($path->{action} eq 'D') ? 0 : 1;
        }
-       $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//;
+       $self->{path_regex} ||= qr{^/\Q@{[$self->path]}\E/};
        if (grep /$self->{path_regex}/, keys %$paths) {
                return 1;
        }
        my $c = '';
-       foreach (split m#/#, $self->{path}) {
+       foreach (split m#/#, $self->path) {
                $c .= "/$_";
                next unless ($paths->{$c} &&
                             ($paths->{$c}->{action} =~ /^[AR]$/));
-               if ($self->ra->check_path($self->{path}, $r) ==
+               if ($self->ra->check_path($self->path, $r) ==
                    $SVN::Node::dir) {
                        return 1;
                }
@@ -1075,14 +1094,14 @@ sub find_parent_branch {
        unless (defined $paths) {
                my $err_handler = $SVN::Error::handler;
                $SVN::Error::handler = \&Git::SVN::Ra::skip_unknown_revs;
-               $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1,
+               $self->ra->get_log([$self->path], $rev, $rev, 0, 1, 1,
                                   sub { $paths = $_[0] });
                $SVN::Error::handler = $err_handler;
        }
        return undef unless defined $paths;
 
        # look for a parent from another branch:
-       my @b_path_components = split m#/#, $self->{path};
+       my @b_path_components = split m#/#, $self->path;
        my @a_path_components;
        my $i;
        while (@b_path_components) {
@@ -1099,8 +1118,8 @@ sub find_parent_branch {
        }
        my $r = $i->{copyfrom_rev};
        my $repos_root = $self->ra->{repos_root};
-       my $url = $self->ra->{url};
-       my $new_url = $url . $branch_from;
+       my $url = $self->ra->url;
+       my $new_url = canonicalize_url( add_path_to_url( $url, $branch_from ) );
        print STDERR  "Found possible branch point: ",
                      "$new_url => ", $self->full_url, ", $r\n"
                      unless $::_q > 1;
@@ -1114,7 +1133,7 @@ sub find_parent_branch {
                        ($base, $head) = parse_revision_argument(0, $r);
                } else {
                        if ($r0 < $r) {
-                               $gs->ra->get_log([$gs->{path}], $r0 + 1, $r, 1,
+                               $gs->ra->get_log([$gs->path], $r0 + 1, $r, 1,
                                        0, 1, sub { $base = $_[1] - 1 });
                        }
                }
@@ -1136,7 +1155,7 @@ sub find_parent_branch {
                        # at the moment), so we can't rely on it
                        $self->{last_rev} = $r0;
                        $self->{last_commit} = $parent;
-                       $ed = Git::SVN::Fetcher->new($self, $gs->{path});
+                       $ed = Git::SVN::Fetcher->new($self, $gs->path);
                        $gs->ra->gs_do_switch($r0, $rev, $gs,
                                              $self->full_url, $ed)
                          or die "SVN connection failed somewhere...\n";
@@ -1235,7 +1254,7 @@ sub mkemptydirs {
                close $fh;
        }
 
-       my $strip = qr/\A\Q$self->{path}\E(?:\/|$)/;
+       my $strip = qr/\A\Q@{[$self->path]}\E(?:\/|$)/;
        foreach my $d (sort keys %empty_dirs) {
                $d = uri_decode($d);
                $d =~ s/$strip//;
@@ -1429,12 +1448,11 @@ sub find_extra_svk_parents {
        for my $ticket ( @tickets ) {
                my ($uuid, $path, $rev) = split /:/, $ticket;
                if ( $uuid eq $self->ra_uuid ) {
-                       my $url = $self->{url};
-                       my $repos_root = $url;
+                       my $repos_root = $self->url;
                        my $branch_from = $path;
                        $branch_from =~ s{^/}{};
-                       my $gs = $self->other_gs($repos_root."/".$branch_from,
-                                                $url,
+                       my $gs = $self->other_gs(add_path_to_url( $repos_root, $branch_from ),
+                                                $repos_root,
                                                 $branch_from,
                                                 $rev,
                                                 $self->{ref_id});
@@ -1693,7 +1711,7 @@ sub find_extra_svn_parents {
        # are now marked as merge, we can add the tip as a parent.
        my @merges = split "\n", $mergeinfo;
        my @merge_tips;
-       my $url = $self->{url};
+       my $url = $self->url;
        my $uuid = $self->ra_uuid;
        my %ranges;
        for my $merge ( @merges ) {
@@ -1875,8 +1893,9 @@ sub make_log_entry {
                $email ||= "$author\@$uuid";
                $commit_email ||= "$author\@$uuid";
        } elsif ($self->use_svnsync_props) {
-               my $full_url = $self->svnsync->{url};
-               $full_url .= "/$self->{path}" if length $self->{path};
+               my $full_url = canonicalize_url(
+                       add_path_to_url( $self->svnsync->{url}, $self->path )
+               );
                remove_username($full_url);
                my $uuid = $self->svnsync->{uuid};
                $log_entry{metadata} = "$full_url\@$rev $uuid";
@@ -1923,7 +1942,7 @@ sub set_tree {
                        tree_b => $tree,
                        editor_cb => sub {
                               $self->set_tree_cb($log_entry, $tree, @_) },
-                       svn_path => $self->{path} );
+                       svn_path => $self->path );
        if (!Git::SVN::Editor->new(\%ed_opts)->apply_diff) {
                print "No changes\nr$self->{last_rev} = $tree\n";
        }
@@ -2299,10 +2318,39 @@ sub _new {
 
        $_[3] = $path = '' unless (defined $path);
        mkpath([$dir]);
-       bless {
+       my $obj = bless {
                ref_id => $ref_id, dir => $dir, index => "$dir/index",
-               path => $path, config => "$ENV{GIT_DIR}/svn/config",
+               config => "$ENV{GIT_DIR}/svn/config",
                map_root => "$dir/.rev_map", repo_id => $repo_id }, $class;
+
+       # Ensure it gets canonicalized
+       $obj->path($path);
+
+       return $obj;
+}
+
+sub path {
+       my $self = shift;
+
+       if (@_) {
+               my $path = shift;
+               $self->{path} = canonicalize_path($path);
+               return;
+       }
+
+       return $self->{path};
+}
+
+sub url {
+       my $self = shift;
+
+       if (@_) {
+               my $url = shift;
+               $self->{url} = canonicalize_url($url);
+               return;
+       }
+
+       return $self->{url};
 }
 
 # for read-only access of old .rev_db formats
index 76fae9bce04ded09a4bb507995df4aab27ef6ea5..046a7a2f31cf923f634bd8b516b3da38ff989a46 100644 (file)
@@ -83,7 +83,7 @@ sub _mark_empty_symlinks {
        chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`);
        my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt);
        local $/ = "\0";
-       my $pfx = defined($switch_path) ? $switch_path : $git_svn->{path};
+       my $pfx = defined($switch_path) ? $switch_path : $git_svn->path;
        $pfx .= '/' if length($pfx);
        while (<$ls>) {
                chomp;
index 75d74298ea5e39ed846254ac4d95f88d0be6ed7b..30daf354655f0b4cc02d08e7ba5af43133a09b95 100644 (file)
@@ -177,14 +177,14 @@ sub minimize_connections {
                my $ra = Git::SVN::Ra->new($url);
 
                # skip existing cases where we already connect to the root
-               if (($ra->{url} eq $ra->{repos_root}) ||
+               if (($ra->url eq $ra->{repos_root}) ||
                    ($ra->{repos_root} eq $repo_id)) {
-                       $root_repos->{$ra->{url}} = $repo_id;
+                       $root_repos->{$ra->url} = $repo_id;
                        next;
                }
 
                my $root_ra = Git::SVN::Ra->new($ra->{repos_root});
-               my $root_path = $ra->{url};
+               my $root_path = $ra->url;
                $root_path =~ s#^\Q$ra->{repos_root}\E(/|$)##;
                foreach my $path (keys %$fetch) {
                        my $ref_id = $fetch->{$path};
index 23ff43e86b4f08fb38b8d9ca90a6c77319ae8f63..90ec30bfff25c5cb5cd3c171fdc4a81c46e0f156 100644 (file)
@@ -3,6 +3,12 @@ package Git::SVN::Ra;
 use strict;
 use warnings;
 use SVN::Client;
+use Git::SVN::Utils qw(
+       canonicalize_url
+       canonicalize_path
+       add_path_to_url
+);
+
 use SVN::Ra;
 BEGIN {
        @ISA = qw(SVN::Ra);
@@ -62,29 +68,11 @@ ()
        \@rv;
 }
 
-sub escape_uri_only {
-       my ($uri) = @_;
-       my @tmp;
-       foreach (split m{/}, $uri) {
-               s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
-               push @tmp, $_;
-       }
-       join('/', @tmp);
-}
-
-sub escape_url {
-       my ($url) = @_;
-       if ($url =~ m#^(https?)://([^/]+)(.*)$#) {
-               my ($scheme, $domain, $uri) = ($1, $2, escape_uri_only($3));
-               $url = "$scheme://$domain$uri";
-       }
-       $url;
-}
 
 sub new {
        my ($class, $url) = @_;
-       $url =~ s!/+$!!;
-       return $RA if ($RA && $RA->{url} eq $url);
+       $url = canonicalize_url($url);
+       return $RA if ($RA && $RA->url eq $url);
 
        ::_req_svn();
 
@@ -115,17 +103,34 @@ sub new {
                        $Git::SVN::Prompt::_no_auth_cache = 1;
                }
        } # no warnings 'once'
-       my $self = SVN::Ra->new(url => escape_url($url), auth => $baton,
+
+       my $self = SVN::Ra->new(url => $url, auth => $baton,
                              config => $config,
                              pool => SVN::Pool->new,
                              auth_provider_callbacks => $callbacks);
-       $self->{url} = $url;
+       $RA = bless $self, $class;
+
+       # Make sure its canonicalized
+       $self->url($url);
        $self->{svn_path} = $url;
        $self->{repos_root} = $self->get_repos_root;
        $self->{svn_path} =~ s#^\Q$self->{repos_root}\E(/|$)##;
        $self->{cache} = { check_path => { r => 0, data => {} },
                           get_dir => { r => 0, data => {} } };
-       $RA = bless $self, $class;
+
+       return $RA;
+}
+
+sub url {
+       my $self = shift;
+
+       if (@_) {
+               my $url = shift;
+               $self->{url} = canonicalize_url($url);
+               return;
+       }
+
+       return $self->{url};
 }
 
 sub check_path {
@@ -195,6 +200,7 @@ sub get_log {
                                qw/copyfrom_path copyfrom_rev action/;
                        if ($s{'copyfrom_path'}) {
                                $s{'copyfrom_path'} =~ s/$prefix_regex//;
+                               $s{'copyfrom_path'} = canonicalize_path($s{'copyfrom_path'});
                        }
                        $_[0]{$p} = \%s;
                }
@@ -246,7 +252,7 @@ sub get_commit_editor {
 sub gs_do_update {
        my ($self, $rev_a, $rev_b, $gs, $editor) = @_;
        my $new = ($rev_a == $rev_b);
-       my $path = $gs->{path};
+       my $path = $gs->path;
 
        if ($new && -e $gs->{index}) {
                unlink $gs->{index} or die
@@ -282,30 +288,33 @@ sub gs_do_update {
 # svn_ra_reparent didn't work before 1.4)
 sub gs_do_switch {
        my ($self, $rev_a, $rev_b, $gs, $url_b, $editor) = @_;
-       my $path = $gs->{path};
+       my $path = $gs->path;
        my $pool = SVN::Pool->new;
 
-       my $full_url = $self->{url};
-       my $old_url = $full_url;
-       $full_url .= '/' . $path if length $path;
+       my $old_url = $self->url;
+       my $full_url = add_path_to_url( $self->url, $path );
        my ($ra, $reparented);
 
        if ($old_url =~ m#^svn(\+ssh)?://# ||
            ($full_url =~ m#^https?://# &&
-            escape_url($full_url) ne $full_url)) {
+            canonicalize_url($full_url) ne $full_url)) {
                $_[0] = undef;
                $self = undef;
                $RA = undef;
                $ra = Git::SVN::Ra->new($full_url);
                $ra_invalid = 1;
        } elsif ($old_url ne $full_url) {
-               SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, $pool);
-               $self->{url} = $full_url;
+               SVN::_Ra::svn_ra_reparent(
+                       $self->{session},
+                       canonicalize_url($full_url),
+                       $pool
+               );
+               $self->url($full_url);
                $reparented = 1;
        }
 
        $ra ||= $self;
-       $url_b = escape_url($url_b);
+       $url_b = canonicalize_url($url_b);
        my $reporter = $ra->do_switch($rev_b, '', 1, $url_b, $editor, $pool);
        my @lock = (::compare_svn_version('1.2.0') >= 0) ? (undef) : ();
        $reporter->set_path('', $rev_a, 0, @lock, $pool);
@@ -313,7 +322,7 @@ sub gs_do_switch {
 
        if ($reparented) {
                SVN::_Ra::svn_ra_reparent($self->{session}, $old_url, $pool);
-               $self->{url} = $old_url;
+               $self->url($old_url);
        }
 
        $pool->clear;
@@ -326,7 +335,7 @@ sub longest_common_path {
        my $common_max = scalar @$gsv;
 
        foreach my $gs (@$gsv) {
-               my @tmp = split m#/#, $gs->{path};
+               my @tmp = split m#/#, $gs->path;
                my $p = '';
                foreach (@tmp) {
                        $p .= length($p) ? "/$_" : $_;
@@ -362,7 +371,7 @@ sub gs_fetch_loop_common {
        my $inc = $_log_window_size;
        my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
        my $longest_path = longest_common_path($gsv, $globs);
-       my $ra_url = $self->{url};
+       my $ra_url = $self->url;
        my $find_trailing_edge;
        while (1) {
                my %revs;
@@ -508,7 +517,7 @@ sub match_globs {
                                 ($self->check_path($p, $r) !=
                                  $SVN::Node::dir));
                        next unless $p =~ /$g->{path}->{regex}/;
-                       $exists->{$p} = Git::SVN->init($self->{url}, $p, undef,
+                       $exists->{$p} = Git::SVN->init($self->url, $p, undef,
                                         $g->{ref}->full_path($de), 1);
                }
        }
@@ -532,7 +541,7 @@ sub match_globs {
                        next if ($self->check_path($pathname, $r) !=
                                 $SVN::Node::dir);
                        $exists->{$pathname} = Git::SVN->init(
-                                             $self->{url}, $pathname, undef,
+                                             $self->url, $pathname, undef,
                                              $g->{ref}->full_path($p), 1);
                }
                my $c = '';
@@ -548,19 +557,20 @@ sub match_globs {
 
 sub minimize_url {
        my ($self) = @_;
-       return $self->{url} if ($self->{url} eq $self->{repos_root});
+       return $self->url if ($self->url eq $self->{repos_root});
        my $url = $self->{repos_root};
        my @components = split(m!/!, $self->{svn_path});
        my $c = '';
        do {
-               $url .= "/$c" if length $c;
+               $url = add_path_to_url($url, $c);
                eval {
                        my $ra = (ref $self)->new($url);
                        my $latest = $ra->get_latest_revnum;
                        $ra->get_log("", $latest, 0, 1, 0, 1, sub {});
                };
        } while ($@ && ($c = shift @components));
-       $url;
+
+       return canonicalize_url($url);
 }
 
 sub can_do_switch {
@@ -568,7 +578,7 @@ sub can_do_switch {
        unless (defined $can_do_switch) {
                my $pool = SVN::Pool->new;
                my $rep = eval {
-                       $self->do_switch(1, '', 0, $self->{url},
+                       $self->do_switch(1, '', 0, $self->url,
                                         SVN::Delta::Editor->new, $pool);
                };
                if ($@) {
index 496006bc7b3b9492f2747f689a8cdda411eb5806..4bb4dde89a3821ad7714c299b429f751cc98ebcc 100644 (file)
@@ -3,9 +3,18 @@ package Git::SVN::Utils;
 use strict;
 use warnings;
 
+use SVN::Core;
+
 use base qw(Exporter);
 
-our @EXPORT_OK = qw(fatal can_compress);
+our @EXPORT_OK = qw(
+       fatal
+       can_compress
+       canonicalize_path
+       canonicalize_url
+       join_paths
+       add_path_to_url
+);
 
 
 =head1 NAME
@@ -56,4 +65,169 @@ sub can_compress {
 }
 
 
+=head3 canonicalize_path
+
+    my $canoncalized_path = canonicalize_path($path);
+
+Converts $path into a canonical form which is safe to pass to the SVN
+API as a file path.
+
+=cut
+
+# Turn foo/../bar into bar
+sub _collapse_dotdot {
+       my $path = shift;
+
+       1 while $path =~ s{/[^/]+/+\.\.}{};
+       1 while $path =~ s{[^/]+/+\.\./}{};
+       1 while $path =~ s{[^/]+/+\.\.}{};
+
+       return $path;
+}
+
+
+sub canonicalize_path {
+       my $path = shift;
+       my $rv;
+
+       # The 1.7 way to do it
+       if ( defined &SVN::_Core::svn_dirent_canonicalize ) {
+               $path = _collapse_dotdot($path);
+               $rv = SVN::_Core::svn_dirent_canonicalize($path);
+       }
+       # The 1.6 way to do it
+       # This can return undef on subversion-perl-1.4.2-2.el5 (CentOS 5.2)
+       elsif ( defined &SVN::_Core::svn_path_canonicalize ) {
+               $path = _collapse_dotdot($path);
+               $rv = SVN::_Core::svn_path_canonicalize($path);
+       }
+
+       return $rv if defined $rv;
+
+       # No SVN API canonicalization is available, or the SVN API
+       # didn't return a successful result, do it ourselves
+       return _canonicalize_path_ourselves($path);
+}
+
+
+sub _canonicalize_path_ourselves {
+       my ($path) = @_;
+       my $dot_slash_added = 0;
+       if (substr($path, 0, 1) ne "/") {
+               $path = "./" . $path;
+               $dot_slash_added = 1;
+       }
+       $path =~ s#/+#/#g;
+       $path =~ s#/\.(?:/|$)#/#g;
+       $path = _collapse_dotdot($path);
+       $path =~ s#/$##g;
+       $path =~ s#^\./## if $dot_slash_added;
+       $path =~ s#^/##;
+       $path =~ s#^\.$##;
+       return $path;
+}
+
+
+=head3 canonicalize_url
+
+    my $canonicalized_url = canonicalize_url($url);
+
+Converts $url into a canonical form which is safe to pass to the SVN
+API as a URL.
+
+=cut
+
+sub canonicalize_url {
+       my $url = shift;
+
+       # The 1.7 way to do it
+       if ( defined &SVN::_Core::svn_uri_canonicalize ) {
+               return SVN::_Core::svn_uri_canonicalize($url);
+       }
+       # There wasn't a 1.6 way to do it, so we do it ourself.
+       else {
+               return _canonicalize_url_ourselves($url);
+       }
+}
+
+
+sub _canonicalize_url_path {
+       my ($uri_path) = @_;
+
+       my @parts;
+       foreach my $part (split m{/+}, $uri_path) {
+               $part =~ s/([^~\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+               push @parts, $part;
+       }
+
+       return join('/', @parts);
+}
+
+sub _canonicalize_url_ourselves {
+       my ($url) = @_;
+       if ($url =~ m#^([^:]+)://([^/]*)(.*)$#) {
+               my ($scheme, $domain, $uri) = ($1, $2, _canonicalize_url_path(canonicalize_path($3)));
+               $url = "$scheme://$domain$uri";
+       }
+       $url;
+}
+
+
+=head3 join_paths
+
+    my $new_path = join_paths(@paths);
+
+Appends @paths together into a single path.  Any empty paths are ignored.
+
+=cut
+
+sub join_paths {
+       my @paths = @_;
+
+       @paths = grep { defined $_ && length $_ } @paths;
+
+       return '' unless @paths;
+       return $paths[0] if @paths == 1;
+
+       my $new_path = shift @paths;
+       $new_path =~ s{/+$}{};
+
+       my $last_path = pop @paths;
+       $last_path =~ s{^/+}{};
+
+       for my $path (@paths) {
+               $path =~ s{^/+}{};
+               $path =~ s{/+$}{};
+               $new_path .= "/$path";
+       }
+
+       return $new_path .= "/$last_path";
+}
+
+
+=head3 add_path_to_url
+
+    my $new_url = add_path_to_url($url, $path);
+
+Appends $path onto the $url.  If $path is empty, $url is returned unchanged.
+
+=cut
+
+sub add_path_to_url {
+       my($url, $path) = @_;
+
+       return $url if !defined $path or !length $path;
+
+       # Strip trailing and leading slashes so we don't
+       # wind up with http://x.com///path
+       $url  =~ s{/+$}{};
+       $path =~ s{^/+}{};
+
+       # If a path has a % in it, URI escape it so it's not
+       # mistaken for a URI escape later.
+       $path =~ s{%}{%25}g;
+
+       return join '/', $url, $path;
+}
+
 1;
index 2f8159fb165f853aafb5e0cee61f35aa854271ec..79e3bbe0240e16629e8ba4b755d60f986132802a 100644 (file)
@@ -1414,11 +1414,9 @@ int read_index_from(struct index_state *istate, const char *path)
        size_t mmap_size;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
 
-       errno = EBUSY;
        if (istate->initialized)
                return istate->cache_nr;
 
-       errno = ENOENT;
        istate->timestamp.sec = 0;
        istate->timestamp.nsec = 0;
        fd = open(path, O_RDONLY);
@@ -1431,15 +1429,14 @@ int read_index_from(struct index_state *istate, const char *path)
        if (fstat(fd, &st))
                die_errno("cannot stat the open index");
 
-       errno = EINVAL;
        mmap_size = xsize_t(st.st_size);
        if (mmap_size < sizeof(struct cache_header) + 20)
                die("index file smaller than expected");
 
        mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-       close(fd);
        if (mmap == MAP_FAILED)
                die_errno("unable to map index file");
+       close(fd);
 
        hdr = mmap;
        if (verify_hdr(hdr, mmap_size) < 0)
@@ -1495,7 +1492,6 @@ int read_index_from(struct index_state *istate, const char *path)
 
 unmap:
        munmap(mmap, mmap_size);
-       errno = EINVAL;
        die("index file corrupt");
 }
 
@@ -1800,6 +1796,8 @@ int write_index(struct index_state *istate, int newfd)
                        continue;
                if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
+               if (is_null_sha1(ce->sha1))
+                       return error("cache entry has null sha1: %s", ce->name);
                if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
                        return -1;
        }
index 04a9d6277db8fac78cb223a906b12744aab32193..3ec474fc631eb3b433b3cadbd74aed4b83cba724 100644 (file)
@@ -362,16 +362,17 @@ static size_t rpc_in(char *ptr, size_t eltsize,
 
 static int run_slot(struct active_request_slot *slot)
 {
-       int err = 0;
+       int err;
        struct slot_results results;
 
        slot->results = &results;
        slot->curl_result = curl_easy_perform(slot->curl);
        finish_active_slot(slot);
 
-       if (results.curl_result != CURLE_OK) {
-               err |= error("RPC failed; result=%d, HTTP code = %ld",
-                       results.curl_result, results.http_code);
+       err = handle_curl_result(slot);
+       if (err != HTTP_OK && err != HTTP_REAUTH) {
+               error("RPC failed; result=%d, HTTP code = %ld",
+                     results.curl_result, results.http_code);
        }
 
        return err;
@@ -436,9 +437,11 @@ static int post_rpc(struct rpc_state *rpc)
        }
 
        if (large_request) {
-               err = probe_rpc(rpc);
-               if (err)
-                       return err;
+               do {
+                       err = probe_rpc(rpc);
+               } while (err == HTTP_REAUTH);
+               if (err != HTTP_OK)
+                       return -1;
        }
 
        slot = get_active_slot();
@@ -525,7 +528,11 @@ static int post_rpc(struct rpc_state *rpc)
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
        curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
 
-       err = run_slot(slot);
+       do {
+               err = run_slot(slot);
+       } while (err == HTTP_REAUTH && !large_request && !use_gzip);
+       if (err != HTTP_OK)
+               err = -1;
 
        curl_slist_free_all(headers);
        free(gzip_body);
index 9e8f47a25d8def94cbf2a8389039e4d3d670cd06..dc3fecf903aa0ce4927b9ebe5b95f99cc9c1018d 100644 (file)
@@ -345,6 +345,7 @@ static int tree_difference = REV_TREE_SAME;
 static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
+                   int sha1_valid,
                    const char *fullpath, unsigned dirty_submodule)
 {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
@@ -358,6 +359,7 @@ static void file_change(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
                 const unsigned char *old_sha1,
                 const unsigned char *new_sha1,
+                int old_sha1_valid, int new_sha1_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
 {
@@ -1132,15 +1134,27 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
                const char *this = arg;
                int symmetric = *next == '.';
                unsigned int flags_exclude = flags ^ UNINTERESTING;
+               static const char head_by_default[] = "HEAD";
                unsigned int a_flags;
 
                *dotdot = 0;
                next += symmetric;
 
                if (!*next)
-                       next = "HEAD";
+                       next = head_by_default;
                if (dotdot == arg)
-                       this = "HEAD";
+                       this = head_by_default;
+               if (this == head_by_default && next == head_by_default &&
+                   !symmetric) {
+                       /*
+                        * Just ".."?  That is not a range but the
+                        * pathspec for the parent directory.
+                        */
+                       if (!cant_be_filename) {
+                               *dotdot = '.';
+                               return -1;
+                       }
+               }
                if (!get_sha1_committish(this, from_sha1) &&
                    !get_sha1_committish(next, sha1)) {
                        struct commit *a, *b;
@@ -1298,7 +1312,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
            !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
            !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
-           !prefixcmp(arg, "--remotes="))
+           !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk="))
        {
                unkv[(*unkc)++] = arg;
                return 1;
@@ -1693,7 +1707,18 @@ static int handle_revision_pseudo_opt(const char *submodule,
        } else if (!strcmp(arg, "--not")) {
                *flags ^= UNINTERESTING;
        } else if (!strcmp(arg, "--no-walk")) {
-               revs->no_walk = 1;
+               revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+       } else if (!prefixcmp(arg, "--no-walk=")) {
+               /*
+                * Detached form ("--no-walk X" as opposed to "--no-walk=X")
+                * not allowed, since the argument is optional.
+                */
+               if (!strcmp(arg + 10, "sorted"))
+                       revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
+               else if (!strcmp(arg + 10, "unsorted"))
+                       revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
+               else
+                       return error("invalid argument to --no-walk");
        } else if (!strcmp(arg, "--do-walk")) {
                revs->no_walk = 0;
        } else {
@@ -1861,8 +1886,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->combine_merges)
                revs->ignore_merges = 0;
        revs->diffopt.abbrev = revs->abbrev;
-       if (diff_setup_done(&revs->diffopt) < 0)
-               die("diff_setup_done failed");
+       diff_setup_done(&revs->diffopt);
 
        compile_grep_patterns(&revs->grep_filter);
 
@@ -2116,10 +2140,11 @@ int prepare_revision_walk(struct rev_info *revs)
                }
                e++;
        }
-       commit_list_sort_by_date(&revs->commits);
        if (!revs->leak_pending)
                free(list);
 
+       if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
+               commit_list_sort_by_date(&revs->commits);
        if (revs->no_walk)
                return 0;
        if (revs->limited)
index cb5ab3513bc9982095e292119b9119d333556256..a95bd0b3f3026068046d469a74d1a2d85e50e314 100644 (file)
@@ -41,6 +41,10 @@ struct rev_cmdline_info {
        } *rev;
 };
 
+#define REVISION_WALK_WALK 0
+#define REVISION_WALK_NO_WALK_SORTED 1
+#define REVISION_WALK_NO_WALK_UNSORTED 2
+
 struct rev_info {
        /* Starting list */
        struct commit_list *commits;
@@ -62,7 +66,7 @@ struct rev_info {
        /* Traversal flags */
        unsigned int    dense:1,
                        prune:1,
-                       no_walk:1,
+                       no_walk:2,
                        show_all:1,
                        remove_empty_trees:1,
                        simplify_history:1,
index 606791dc674a1d24459d85504f0c981634b52020..f9922b9ecc8e4956e19d7143bb6cb6ef4d97abf8 100644 (file)
@@ -139,6 +139,8 @@ int sane_execvp(const char *file, char * const argv[])
         */
        if (errno == EACCES && !strchr(file, '/'))
                errno = exists_in_PATH(file) ? EACCES : ENOENT;
+       else if (errno == ENOTDIR && !strchr(file, '/'))
+               errno = ENOENT;
        return -1;
 }
 
index bf078f274bfe23997fcd1bc8c2a1c958f39ac9fe..f86f116a15326f514fbe0405a9301f4ee2416344 100644 (file)
@@ -311,6 +311,9 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
        if (allow_empty)
                argv_array_push(&array, "--allow-empty");
 
+       if (opts->allow_empty_message)
+               argv_array_push(&array, "--allow-empty-message");
+
        rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
        argv_array_clear(&array);
        return rc;
@@ -543,7 +546,11 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
 
 static void prepare_revs(struct replay_opts *opts)
 {
-       if (opts->action != REPLAY_REVERT)
+       /*
+        * picking (but not reverting) ranges (but not individual revisions)
+        * should be done in reverse
+        */
+       if (opts->action == REPLAY_PICK && !opts->revs->no_walk)
                opts->revs->reverse ^= 1;
 
        if (prepare_revision_walk(opts->revs))
index aa5f17cc3079f39000e9d7526d24354a027daabd..d8494201e0a0ba052356de1094b25bac5b9024c0 100644 (file)
@@ -30,6 +30,7 @@ struct replay_opts {
        int allow_ff;
        int allow_rerere_auto;
        int allow_empty;
+       int allow_empty_message;
        int keep_redundant_commits;
 
        int mainline;
diff --git a/setup.c b/setup.c
index 9139beefc75c4fe92cf8c7dc54a9fae972995aca..3a1b2fd45580cad7ecd55efa755872efc81075ad 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -79,7 +79,7 @@ static void NORETURN die_verify_filename(const char *prefix,
 {
        if (!diagnose_misspelt_rev)
                die("%s: no such path in the working tree.\n"
-                   "Use '-- <path>...' to specify paths that do not exist locally.",
+                   "Use 'git <command> -- <path>...' to specify paths that do not exist locally.",
                    arg);
        /*
         * Saying "'(icase)foo' does not exist in the index" when the
@@ -92,7 +92,8 @@ static void NORETURN die_verify_filename(const char *prefix,
 
        /* ... or fall back the most general message. */
        die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
-           "Use '--' to separate paths from revisions", arg);
+           "Use '--' to separate paths from revisions, like this:\n"
+           "'git <command> [<revision>...] -- [<file>...]'", arg);
 
 }
 
@@ -141,7 +142,8 @@ void verify_non_filename(const char *prefix, const char *arg)
        if (!check_filename(prefix, arg))
                return;
        die("ambiguous argument '%s': both revision and filename\n"
-           "Use '--' to separate filenames from revisions", arg);
+           "Use '--' to separate paths from revisions, like this:\n"
+           "'git <command> [<revision>...] -- [<file>...]'", arg);
 }
 
 /*
index af5cfbde63aed41d95ac7318d064d3012832d2c5..9152974642986de2307ec4ed6beda4efa49e8383 100644 (file)
@@ -731,6 +731,24 @@ void free_pack_by_name(const char *pack_name)
        }
 }
 
+static unsigned int get_max_fd_limit(void)
+{
+#ifdef RLIMIT_NOFILE
+       struct rlimit lim;
+
+       if (getrlimit(RLIMIT_NOFILE, &lim))
+               die_errno("cannot get RLIMIT_NOFILE");
+
+       return lim.rlim_cur;
+#elif defined(_SC_OPEN_MAX)
+       return sysconf(_SC_OPEN_MAX);
+#elif defined(OPEN_MAX)
+       return OPEN_MAX;
+#else
+       return 1; /* see the caller ;-) */
+#endif
+}
+
 /*
  * Do not call this directly as this leaks p->pack_fd on error return;
  * call open_packed_git() instead.
@@ -747,13 +765,7 @@ static int open_packed_git_1(struct packed_git *p)
                return error("packfile %s index unavailable", p->pack_name);
 
        if (!pack_max_fds) {
-               struct rlimit lim;
-               unsigned int max_fds;
-
-               if (getrlimit(RLIMIT_NOFILE, &lim))
-                       die_errno("cannot get RLIMIT_NOFILE");
-
-               max_fds = lim.rlim_cur;
+               unsigned int max_fds = get_max_fd_limit();
 
                /* Save 3 for stdin/stdout/stderr, 22 for work */
                if (25 < max_fds)
index 959d349ea7426344289020ee4216785fa65ec1e6..50f213e926374e86c23cdbfc730f6d81af4816b8 100644 (file)
@@ -574,8 +574,7 @@ static void calculate_changed_submodule_paths(void)
                        DIFF_OPT_SET(&diff_opts, RECURSIVE);
                        diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
                        diff_opts.format_callback = submodule_collect_changed_cb;
-                       if (diff_setup_done(&diff_opts) < 0)
-                               die("diff_setup_done failed");
+                       diff_setup_done(&diff_opts);
                        diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts);
                        diffcore_std(&diff_opts);
                        diff_flush(&diff_opts);
@@ -589,13 +588,13 @@ static void calculate_changed_submodule_paths(void)
        initialized_fetch_ref_tips = 0;
 }
 
-int fetch_populated_submodules(int num_options, const char **options,
+int fetch_populated_submodules(const struct argv_array *options,
                               const char *prefix, int command_line_option,
                               int quiet)
 {
-       int i, result = 0, argc = 0, default_argc;
+       int i, result = 0;
        struct child_process cp;
-       const char **argv;
+       struct argv_array argv = ARGV_ARRAY_INIT;
        struct string_list_item *name_for_path;
        const char *work_tree = get_git_work_tree();
        if (!work_tree)
@@ -605,17 +604,13 @@ int fetch_populated_submodules(int num_options, const char **options,
                if (read_cache() < 0)
                        die("index file corrupt");
 
-       /* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */
-       argv = xcalloc(num_options + 6, sizeof(const char *));
-       argv[argc++] = "fetch";
-       for (i = 0; i < num_options; i++)
-               argv[argc++] = options[i];
-       argv[argc++] = "--recurse-submodules-default";
-       default_argc = argc++;
-       argv[argc++] = "--submodule-prefix";
+       argv_array_push(&argv, "fetch");
+       for (i = 0; i < options->argc; i++)
+               argv_array_push(&argv, options->argv[i]);
+       argv_array_push(&argv, "--recurse-submodules-default");
+       /* default value, "--submodule-prefix" and its value are added later */
 
        memset(&cp, 0, sizeof(cp));
-       cp.argv = argv;
        cp.env = local_repo_env;
        cp.git_cmd = 1;
        cp.no_stdin = 1;
@@ -675,16 +670,21 @@ int fetch_populated_submodules(int num_options, const char **options,
                        if (!quiet)
                                printf("Fetching submodule %s%s\n", prefix, ce->name);
                        cp.dir = submodule_path.buf;
-                       argv[default_argc] = default_argv;
-                       argv[argc] = submodule_prefix.buf;
+                       argv_array_push(&argv, default_argv);
+                       argv_array_push(&argv, "--submodule-prefix");
+                       argv_array_push(&argv, submodule_prefix.buf);
+                       cp.argv = argv.argv;
                        if (run_command(&cp))
                                result = 1;
+                       argv_array_pop(&argv);
+                       argv_array_pop(&argv);
+                       argv_array_pop(&argv);
                }
                strbuf_release(&submodule_path);
                strbuf_release(&submodule_git_dir);
                strbuf_release(&submodule_prefix);
        }
-       free(argv);
+       argv_array_clear(&argv);
 out:
        string_list_clear(&changed_submodule_paths, 1);
        return result;
@@ -789,7 +789,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                die("revision walk setup failed");
        while ((commit = get_revision(&revs)) != NULL) {
                struct object *o = &(commit->object);
-               if (in_merge_bases(b, &commit, 1))
+               if (in_merge_bases(b, commit))
                        add_object_array(o, NULL, &merges);
        }
        reset_revision_walk();
@@ -804,7 +804,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                contains_another = 0;
                for (j = 0; j < merges.nr; j++) {
                        struct commit *m2 = (struct commit *) merges.objects[j].item;
-                       if (i != j && in_merge_bases(m2, &m1, 1)) {
+                       if (i != j && in_merge_bases(m2, m1)) {
                                contains_another = 1;
                                break;
                        }
@@ -866,18 +866,18 @@ int merge_submodule(unsigned char result[20], const char *path,
        }
 
        /* check whether both changes are forward */
-       if (!in_merge_bases(commit_base, &commit_a, 1) ||
-           !in_merge_bases(commit_base, &commit_b, 1)) {
+       if (!in_merge_bases(commit_base, commit_a) ||
+           !in_merge_bases(commit_base, commit_b)) {
                MERGE_WARNING(path, "commits don't follow merge-base");
                return 0;
        }
 
        /* Case #1: a is contained in b or vice versa */
-       if (in_merge_bases(commit_a, &commit_b, 1)) {
+       if (in_merge_bases(commit_a, commit_b)) {
                hashcpy(result, b);
                return 1;
        }
-       if (in_merge_bases(commit_b, &commit_a, 1)) {
+       if (in_merge_bases(commit_b, commit_a)) {
                hashcpy(result, a);
                return 1;
        }
index e105b0ebe6c06a03af7f82bdfbc9beb66377544f..594b50d51066be35d2f9dc9aa795f3ecdf131573 100644 (file)
@@ -2,6 +2,7 @@
 #define SUBMODULE_H
 
 struct diff_options;
+struct argv_array;
 
 enum {
        RECURSE_SUBMODULES_ON_DEMAND = -1,
@@ -23,7 +24,7 @@ void show_submodule_summary(FILE *f, const char *path,
                const char *del, const char *add, const char *reset);
 void set_config_fetch_recurse_submodules(int value);
 void check_for_new_submodule_commits(unsigned char new_sha1[20]);
-int fetch_populated_submodules(int num_options, const char **options,
+int fetch_populated_submodules(const struct argv_array *options,
                               const char *prefix, int command_line_option,
                               int quiet);
 unsigned is_submodule_modified(const char *path, int ignore_untracked);
diff --git a/t/Git-SVN/Utils/add_path_to_url.t b/t/Git-SVN/Utils/add_path_to_url.t
new file mode 100644 (file)
index 0000000..bfbd878
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils qw(
+       add_path_to_url
+);
+
+# A reference cannot be a hash key, so we use an array.
+my @tests = (
+       ["http://x.com", "bar"]                 => 'http://x.com/bar',
+       ["http://x.com", ""]                    => 'http://x.com',
+       ["http://x.com/foo/", undef]            => 'http://x.com/foo/',
+       ["http://x.com/foo/", "/bar/baz/"]      => 'http://x.com/foo/bar/baz/',
+       ["http://x.com", 'per%cent']            => 'http://x.com/per%25cent',
+);
+
+while(@tests) {
+       my($have, $want) = splice @tests, 0, 2;
+
+       my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have;
+       my $name = "add_path_to_url($args) eq $want";
+       is add_path_to_url(@$have), $want, $name;
+}
diff --git a/t/Git-SVN/Utils/canonicalize_url.t b/t/Git-SVN/Utils/canonicalize_url.t
new file mode 100644 (file)
index 0000000..05795ab
--- /dev/null
@@ -0,0 +1,26 @@
+#!/usr/bin/env perl
+
+# Test our own home rolled URL canonicalizer.  Test the private one
+# directly because we can't predict what the SVN API is doing to do.
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils;
+my $canonicalize_url = \&Git::SVN::Utils::_canonicalize_url_ourselves;
+
+my %tests = (
+       "http://x.com"                  => "http://x.com",
+       "http://x.com/"                 => "http://x.com",
+       "http://x.com/foo/bar"          => "http://x.com/foo/bar",
+       "http://x.com//foo//bar//"      => "http://x.com/foo/bar",
+       "http://x.com/  /%/"            => "http://x.com/%20%20/%25",
+);
+
+for my $arg (keys %tests) {
+       my $want = $tests{$arg};
+
+       is $canonicalize_url->($arg), $want, "canonicalize_url('$arg') => $want";
+}
diff --git a/t/Git-SVN/Utils/collapse_dotdot.t b/t/Git-SVN/Utils/collapse_dotdot.t
new file mode 100644 (file)
index 0000000..1da1cce
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils;
+my $collapse_dotdot = \&Git::SVN::Utils::_collapse_dotdot;
+
+my %tests = (
+       "foo/bar/baz"                   => "foo/bar/baz",
+       ".."                            => "..",
+       "foo/.."                        => "",
+       "/foo/bar/../../baz"            => "/baz",
+       "deeply/.././deeply/nested"     => "./deeply/nested",
+);
+
+for my $arg (keys %tests) {
+       my $want = $tests{$arg};
+
+       is $collapse_dotdot->($arg), $want, "_collapse_dotdot('$arg') => $want";
+}
diff --git a/t/Git-SVN/Utils/join_paths.t b/t/Git-SVN/Utils/join_paths.t
new file mode 100644 (file)
index 0000000..d4488e7
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Git::SVN::Utils qw(
+       join_paths
+);
+
+# A reference cannot be a hash key, so we use an array.
+my @tests = (
+       []                                      => '',
+       ["/x.com", "bar"]                       => '/x.com/bar',
+       ["x.com", ""]                           => 'x.com',
+       ["/x.com/foo/", undef, "bar"]           => '/x.com/foo/bar',
+       ["x.com/foo/", "/bar/baz/"]             => 'x.com/foo/bar/baz/',
+       ["foo", "bar"]                          => 'foo/bar',
+       ["/foo/bar", "baz", "/biff"]            => '/foo/bar/baz/biff',
+       ["", undef, "."]                        => '.',
+       []                                      => '',
+
+);
+
+while(@tests) {
+       my($have, $want) = splice @tests, 0, 2;
+
+       my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have;
+       my $name = "join_paths($args) eq '$want'";
+       is join_paths(@$have), $want, $name;
+}
index 4c3ea25e6649007c9dde7ae4570d6311824051b6..e4128e5769722473f7fd1024fdf8a5100bd8ccf6 100644 (file)
--- a/t/README
+++ b/t/README
@@ -625,6 +625,15 @@ use these, and "test_set_prereq" for how to define your own.
    Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests
    that use git-grep --perl-regexp or git-grep -P in these.
 
+ - CASE_INSENSITIVE_FS
+
+   Test is run on a case insensitive file system.
+
+ - UTF8_NFD_TO_NFC
+
+   Test is run on a filesystem which converts decomposed utf-8 (nfd)
+   to precomposed utf-8 (nfc).
+
 Tips for Writing Tests
 ----------------------
 
index 957ae936e8b785f8b1afbffd9a5918a3260d213f..3c43ff11b305c5d533323c1ed9520ac52d0cfbe3 100755 (executable)
@@ -18,6 +18,10 @@ check() {
                cat stderr &&
                false
        fi &&
+       if test_have_prereq MINGW
+       then
+               dos2unix -q stderr
+       fi &&
        test_cmp expect-stdout stdout &&
        test_cmp expect-stderr stderr
 }
index 2d753ab7e118e0905e63860cc4ee6ae4932f16c4..d748c36df596b640d403626fce3200d85d68253b 100644 (file)
@@ -115,3 +115,20 @@ marshal_dump() {
        EOF
        "$PYTHON_PATH" "$TRASH_DIRECTORY/marshal-dump.py"
 }
+
+#
+# Construct a client with this list of View lines
+#
+client_view() {
+       (
+               cat <<-EOF &&
+               Client: client
+               Description: client
+               Root: $cli
+               View:
+               EOF
+               for arg ; do
+                       printf "\t$arg\n"
+               done
+       ) | p4 client -i
+}
index d773542680d9f3c3d77e52871877560a9e545a26..02f442bfadbb1239785a456cddc50223da4f3834 100644 (file)
@@ -167,3 +167,42 @@ test_http_push_nonff() {
                test_i18ngrep "Updates were rejected because" output
        '
 }
+
+setup_askpass_helper() {
+       test_expect_success 'setup askpass helper' '
+               write_script "$TRASH_DIRECTORY/askpass" <<-\EOF &&
+               echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" &&
+               cat "$TRASH_DIRECTORY/askpass-response"
+               EOF
+               GIT_ASKPASS="$TRASH_DIRECTORY/askpass" &&
+               export GIT_ASKPASS &&
+               export TRASH_DIRECTORY
+       '
+}
+
+set_askpass() {
+       >"$TRASH_DIRECTORY/askpass-query" &&
+       echo "$*" >"$TRASH_DIRECTORY/askpass-response"
+}
+
+expect_askpass() {
+       dest=$HTTPD_DEST
+       {
+               case "$1" in
+               none)
+                       ;;
+               pass)
+                       echo "askpass: Password for 'http://$2@$dest': "
+                       ;;
+               both)
+                       echo "askpass: Username for 'http://$dest': "
+                       echo "askpass: Password for 'http://$2@$dest': "
+                       ;;
+               *)
+                       false
+                       ;;
+               esac
+       } >"$TRASH_DIRECTORY/askpass-expect" &&
+       test_cmp "$TRASH_DIRECTORY/askpass-expect" \
+                "$TRASH_DIRECTORY/askpass-query"
+}
index 36b1596a10c04b5c5c095e96e8e247463eb3f022..49d5d877ceeeba3738b0939f14d059c2a75ff3ae 100644 (file)
@@ -46,24 +46,22 @@ PassEnv GIT_VALGRIND
 PassEnv GIT_VALGRIND_OPTIONS
 
 Alias /dumb/ www/
-Alias /auth/ www/auth/
+Alias /auth/dumb/ www/auth/dumb/
 
-<Location /smart/>
+<LocationMatch /smart/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
-</Location>
-<Location /smart_noexport/>
+</LocationMatch>
+<LocationMatch /smart_noexport/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
-</Location>
-<Location /smart_custom_env/>
+</LocationMatch>
+<LocationMatch /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/
+</LocationMatch>
+ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 <Directory ${GIT_EXEC_PATH}>
        Options FollowSymlinks
 </Directory>
@@ -94,6 +92,13 @@ SSLEngine On
        Require valid-user
 </Location>
 
+<LocationMatch "^/auth-push/.*/git-receive-pack$">
+       AuthType Basic
+       AuthName "git-auth"
+       AuthUserFile passwd
+       Require valid-user
+</LocationMatch>
+
 <IfDefine DAV>
        LoadModule dav_module modules/mod_dav.so
        LoadModule dav_fs_module modules/mod_dav_fs.so
index 5580c22812be1cadd6b70974eb85ce7cda1b8df3..a1361e530c60609a52948dd0fd35642d57f84486 100644 (file)
@@ -163,7 +163,7 @@ test_perf () {
                else
                        echo "perf $test_count - $1:"
                fi
-               for i in $(seq 1 $GIT_PERF_REPEAT_COUNT); do
+               for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do
                        say >&3 "running: $2"
                        if test_run_perf_ "$2"
                        then
index 51f3045ba4bde549b06ea669b1877f0e2db7c401..febc45c9cc6f34a606ebac8470e582efad798148 100755 (executable)
@@ -123,16 +123,6 @@ test_expect_success 'attribute matching is case insensitive when core.ignorecase
 
 '
 
-test_expect_success 'check whether FS is case-insensitive' '
-       mkdir junk &&
-       echo good >junk/CamelCase &&
-       echo bad >junk/camelcase &&
-       if test "$(cat junk/CamelCase)" != good
-       then
-               test_set_prereq CASE_INSENSITIVE_FS
-       fi
-'
-
 test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' '
        test_must_fail attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=0" &&
        test_must_fail attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
index 1d29810a7a94dad15645869c2f4f015a470fa622..e53cf6d36d573c3d758f0005c95aa4a364df82b1 100755 (executable)
@@ -11,7 +11,7 @@ check_show() {
        echo "$t -> $2" >expect
        test_expect_${3:-success} "relative date ($2)" "
        test-date show $t >actual &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
        "
 }
 
index e3f354a45e382dbaa9de4557aa9310fa21eb626b..244a43c9201a3d81d0931f5f127bd2dcfd068eba 100755 (executable)
@@ -51,7 +51,7 @@ EOF
 test_expect_success 'test help' '
        test_must_fail test-parse-options -h > output 2> output.err &&
        test ! -s output.err &&
-       test_cmp expect output
+       test_i18ncmp expect output
 '
 
 mv expect expect.err
@@ -79,6 +79,17 @@ check() {
        test_cmp expect output
 }
 
+check_i18n() {
+       what="$1" &&
+       shift &&
+       expect="$1" &&
+       shift &&
+       sed "s/^$what .*/$what $expect/" <expect.template >expect &&
+       test-parse-options $* >output 2>output.err &&
+       test ! -s output.err &&
+       test_i18ncmp expect output
+}
+
 check_unknown() {
        case "$1" in
        --*)
@@ -92,6 +103,19 @@ check_unknown() {
        test_cmp expect output.err
 }
 
+check_unknown_i18n() {
+       case "$1" in
+       --*)
+               echo error: unknown option \`${1#--}\' >expect ;;
+       -*)
+               echo error: unknown switch \`${1#-}\' >expect ;;
+       esac &&
+       cat expect.err >>expect &&
+       test_must_fail test-parse-options $* >output 2>output.err &&
+       test ! -s output &&
+       test_i18ncmp expect output.err
+}
+
 test_expect_success 'OPT_BOOL() #1' 'check boolean: 1 --yes'
 test_expect_success 'OPT_BOOL() #2' 'check boolean: 1 --no-doubt'
 test_expect_success 'OPT_BOOL() #3' 'check boolean: 1 -D'
@@ -104,8 +128,8 @@ test_expect_success 'OPT_BOOL() is idempotent #2' 'check boolean: 1 -DB'
 test_expect_success 'OPT_BOOL() negation #1' 'check boolean: 0 -D --no-yes'
 test_expect_success 'OPT_BOOL() negation #2' 'check boolean: 0 -D --no-no-doubt'
 
-test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown --fear'
-test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown --no-no-fear'
+test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown_i18n --fear'
+test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown_i18n --no-no-fear'
 
 test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt'
 
@@ -310,8 +334,8 @@ EOF
 
 test_expect_success 'OPT_CALLBACK() and callback errors work' '
        test_must_fail test-parse-options --no-length > output 2> output.err &&
-       test_cmp expect output &&
-       test_cmp expect.err output.err
+       test_i18ncmp expect output &&
+       test_i18ncmp expect.err output.err
 '
 
 cat > expect <<EOF
index 1542cf6a1313963fccdf42702c620be9e05f7143..78816d9d935df478f5cdf93e06fae26cc5488910 100755 (executable)
@@ -7,48 +7,26 @@ test_description='Various filesystem issues'
 auml=$(printf '\303\244')
 aumlcdiar=$(printf '\141\314\210')
 
-case_insensitive=
-unibad=
-no_symlinks=
-test_expect_success 'see what we expect' '
-
-       test_case=test_expect_success &&
-       test_unicode=test_expect_success &&
-       mkdir junk &&
-       echo good >junk/CamelCase &&
-       echo bad >junk/camelcase &&
-       if test "$(cat junk/CamelCase)" != good
-       then
-               test_case=test_expect_failure &&
-               case_insensitive=t
-       fi &&
-       rm -fr junk &&
-       mkdir junk &&
-       >junk/"$auml" &&
-       case "$(cd junk && echo *)" in
-       "$aumlcdiar")
-               test_unicode=test_expect_failure &&
-               unibad=t
-               ;;
-       *)      ;;
-       esac &&
-       rm -fr junk &&
-       {
-               ln -s x y 2> /dev/null &&
-               test -h y 2> /dev/null ||
-               no_symlinks=1 &&
-               rm -f y
-       }
-'
-
-test "$case_insensitive" &&
+if test_have_prereq CASE_INSENSITIVE_FS
+then
        say "will test on a case insensitive filesystem"
-test "$unibad" &&
+       test_case=test_expect_failure
+else
+       test_case=test_expect_success
+fi
+
+if test_have_prereq UTF8_NFD_TO_NFC
+then
        say "will test on a unicode corrupting filesystem"
-test "$no_symlinks" &&
+       test_unicode=test_expect_failure
+else
+       test_unicode=test_expect_success
+fi
+
+test_have_prereq SYMLINKS ||
        say "will test on a filesystem lacking symbolic links"
 
-if test "$case_insensitive"
+if test_have_prereq CASE_INSENSITIVE_FS
 then
 test_expect_success "detection of case insensitive filesystem during repo init" '
 
@@ -62,18 +40,18 @@ test_expect_success "detection of case insensitive filesystem during repo init"
 '
 fi
 
-if test "$no_symlinks"
+if test_have_prereq SYMLINKS
 then
 test_expect_success "detection of filesystem w/o symlink support during repo init" '
 
-       v=$(git config --bool core.symlinks) &&
-       test "$v" = false
+       test_must_fail git config --bool core.symlinks ||
+       test "$(git config --bool core.symlinks)" = true
 '
 else
 test_expect_success "detection of filesystem w/o symlink support during repo init" '
 
-       test_must_fail git config --bool core.symlinks ||
-       test "$(git config --bool core.symlinks)" = true
+       v=$(git config --bool core.symlinks) &&
+       test "$v" = false
 '
 fi
 
index 9bee8bfd2e063c40bae2d76930370bd9e8ba8fa5..da2c504e53e37840f71790d6b262106b7b57d697 100755 (executable)
@@ -25,4 +25,9 @@ test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' '
        grep "cannotwrite/test" err
 '
 
+test_expect_success 'check for a bug in the regex routines' '
+       # if this test fails, re-build git with NO_REGEX=1
+       test-regex
+'
+
 test_done
index a477453e2ef8ffedafb2dd05e00f507cd112bc97..e127f35db9d12dba84cbf0503628ab5d9a3fc24f 100755 (executable)
@@ -391,7 +391,7 @@ test_expect_success 'get bool variable with empty value' \
 
 test_expect_success 'no arguments, but no crash' '
        test_must_fail git config >output 2>&1 &&
-       grep usage output
+       test_i18ngrep usage output
 '
 
 cat > .git/config << EOF
index 5b79c51b8c51b3fc62823e1b51f9087fbd7f30e3..bf7a2cd6fb649e3b210b537d4d25312ba2921f16 100755 (executable)
@@ -213,4 +213,30 @@ test_expect_success 'rev-list --verify-objects with bad sha1' '
        grep -q "error: sha1 mismatch 63ffffffffffffffffffffffffffffffffffffff" out
 '
 
+_bz='\0'
+_bz5="$_bz$_bz$_bz$_bz$_bz"
+_bz20="$_bz5$_bz5$_bz5$_bz5"
+
+test_expect_success 'fsck notices blob entry pointing to null sha1' '
+       (git init null-blob &&
+        cd null-blob &&
+        sha=$(printf "100644 file$_bz$_bz20" |
+              git hash-object -w --stdin -t tree) &&
+         git fsck 2>out &&
+         cat out &&
+         grep "warning.*null sha1" out
+       )
+'
+
+test_expect_success 'fsck notices submodule entry pointing to null sha1' '
+       (git init null-commit &&
+        cd null-commit &&
+        sha=$(printf "160000 submodule$_bz$_bz20" |
+              git hash-object -w --stdin -t tree) &&
+         git fsck 2>out &&
+         cat out &&
+         grep "warning.*null sha1" out
+       )
+'
+
 test_done
index 1efd7f76ddea8dbd788032c5a9076270d1159825..13c88c9aae7f7177cbff278cf5e587f86acb11e7 100755 (executable)
@@ -41,7 +41,7 @@ EOF
 
 test_expect_success 'test --parseopt help output' '
        test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
-       test_cmp expect output
+       test_i18ncmp expect output
 '
 
 cat > expect <<EOF
index c5cb77a0e1f34ac46dd8727341948389624e8f38..f950c101287c2f05bbd70c1faffbcf2cda1edbee 100755 (executable)
@@ -182,4 +182,18 @@ test_expect_success '<commit>:file correctly diagnosed after a pathname' '
        test_cmp expect actual
 '
 
+test_expect_success 'dotdot is not an empty set' '
+       ( H=$(git rev-parse HEAD) && echo $H && echo ^$H ) >expect &&
+
+       git rev-parse HEAD.. >actual &&
+       test_cmp expect actual &&
+
+       git rev-parse ..HEAD >actual &&
+       test_cmp expect actual &&
+
+       echo .. >expect &&
+       git rev-parse .. >actual &&
+       test_cmp expect actual
+'
+
 test_done
index b8559838b176ec8fa3c1d4acb71ae42ac71056c6..57cbdfe9bce93ddd33df08fec506be93e362db58 100755 (executable)
@@ -7,7 +7,7 @@ test_description='basic checkout-index tests
 
 test_expect_success 'checkout-index --gobbledegook' '
        test_expect_code 129 git checkout-index --gobbledegook 2>err &&
-       grep "[Uu]sage" err
+       test_i18ngrep "[Uu]sage" err
 '
 
 test_expect_success 'checkout-index -h in broken repository' '
@@ -18,7 +18,7 @@ test_expect_success 'checkout-index -h in broken repository' '
                >.git/index &&
                test_expect_code 129 git checkout-index -h >usage 2>&1
        ) &&
-       grep "[Uu]sage" broken/usage
+       test_i18ngrep "[Uu]sage" broken/usage
 '
 
 test_done
index 809fafe208cbf68121295922a4c230b2a5e1aee3..a6405d318d322b9e1f49e4762570a2c1f5371726 100755 (executable)
@@ -15,7 +15,7 @@ test_expect_success 'update-index --nonsense fails' '
 
 test_expect_success 'update-index --nonsense dumps usage' '
        test_expect_code 129 git update-index --nonsense 2>err &&
-       grep "[Uu]sage: git update-index" err
+       test_i18ngrep "[Uu]sage: git update-index" err
 '
 
 test_expect_success 'update-index -h with corrupt index' '
@@ -26,7 +26,26 @@ test_expect_success 'update-index -h with corrupt index' '
                >.git/index &&
                test_expect_code 129 git update-index -h >usage 2>&1
        ) &&
-       grep "[Uu]sage: git update-index" broken/usage
+       test_i18ngrep "[Uu]sage: git update-index" broken/usage
+'
+
+test_expect_success '--cacheinfo does not accept blob null sha1' '
+       echo content >file &&
+       git add file &&
+       git rev-parse :file >expect &&
+       test_must_fail git update-index --cacheinfo 100644 $_z40 file &&
+       git rev-parse :file >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--cacheinfo does not accept gitlink null sha1' '
+       git init submodule &&
+       (cd submodule && test_commit foo) &&
+       git add submodule &&
+       git rev-parse :submodule >expect &&
+       test_must_fail git update-index --cacheinfo 160000 $_z40 submodule &&
+       git rev-parse :submodule >actual &&
+       test_cmp expect actual
 '
 
 test_done
index 490e052875806849e9cbaa54958f1d8d02f5e8f2..8d9bc3c2af6ad84c006010ee7be65146107c2259 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success 'ls-files with nonexistent path' '
 
 test_expect_success 'ls-files with nonsense option' '
        test_expect_code 129 git ls-files --nonsense 2>actual &&
-       grep "[Uu]sage: git ls-files" actual
+       test_i18ngrep "[Uu]sage: git ls-files" actual
 '
 
 test_expect_success 'ls-files -h in corrupt repository' '
@@ -33,7 +33,7 @@ test_expect_success 'ls-files -h in corrupt repository' '
                >.git/index &&
                test_expect_code 129 git ls-files -h >usage 2>&1
        ) &&
-       grep "[Uu]sage: git ls-files " broken/usage
+       test_i18ngrep "[Uu]sage: git ls-files " broken/usage
 '
 
 test_done
index a17f8b2a407c2de2bf81d8a858957da409f1c980..79c8d0142ea72d25a2c403738312a01d0ace50cb 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success 'branch -h in broken repository' '
                >.git/refs/heads/master &&
                test_expect_code 129 git branch -h >usage 2>&1
        ) &&
-       grep "[Uu]sage" broken/usage
+       test_i18ngrep "[Uu]sage" broken/usage
 '
 
 test_expect_success \
@@ -74,7 +74,7 @@ test_expect_success \
 test_expect_success \
     'git branch -m dumps usage' \
        'test_expect_code 129 git branch -m 2>err &&
-       grep "[Uu]sage: git branch" err'
+       test_i18ngrep "[Uu]sage: git branch" err'
 
 test_expect_success \
     'git branch -m m m/m should work' \
@@ -369,6 +369,76 @@ test_expect_success \
     'git tag foobar &&
      test_must_fail git branch --track my11 foobar'
 
+test_expect_success 'use --set-upstream-to modify HEAD' \
+    'test_config branch.master.remote foo &&
+     test_config branch.master.merge foo &&
+     git branch my12
+     git branch --set-upstream-to my12 &&
+     test "$(git config branch.master.remote)" = "." &&
+     test "$(git config branch.master.merge)" = "refs/heads/my12"'
+
+test_expect_success 'use --set-upstream-to modify a particular branch' \
+    'git branch my13
+     git branch --set-upstream-to master my13 &&
+     test "$(git config branch.my13.remote)" = "." &&
+     test "$(git config branch.my13.merge)" = "refs/heads/master"'
+
+test_expect_success '--unset-upstream should fail if given a non-existent branch' \
+    'test_must_fail git branch --unset-upstream i-dont-exist'
+
+test_expect_success 'test --unset-upstream on HEAD' \
+    'git branch my14
+     test_config branch.master.remote foo &&
+     test_config branch.master.merge foo &&
+     git branch --set-upstream-to my14 &&
+     git branch --unset-upstream &&
+     test_must_fail git config branch.master.remote &&
+     test_must_fail git config branch.master.merge &&
+     # fail for a branch without upstream set
+     test_must_fail git branch --unset-upstream
+'
+
+test_expect_success 'test --unset-upstream on a particular branch' \
+    'git branch my15
+     git branch --set-upstream-to master my14 &&
+     git branch --unset-upstream my14 &&
+     test_must_fail git config branch.my14.remote &&
+     test_must_fail git config branch.my14.merge'
+
+test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' \
+    'git update-ref refs/remotes/origin/master HEAD &&
+     git branch --set-upstream origin/master 2>actual &&
+     test_when_finished git update-ref -d refs/remotes/origin/master &&
+     test_when_finished git branch -d origin/master &&
+     cat >expected <<EOF &&
+The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
+
+If you wanted to make '"'master'"' track '"'origin/master'"', do this:
+
+    git branch -d origin/master
+    git branch --set-upstream-to origin/master
+EOF
+     test_cmp expected actual
+'
+
+test_expect_success '--set-upstream with two args only shows the deprecation message' \
+    'git branch --set-upstream master my13 2>actual &&
+     test_when_finished git branch --unset-upstream master &&
+     cat >expected <<EOF &&
+The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
+EOF
+     test_cmp expected actual
+'
+
+test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' \
+    'git branch --set-upstream my13 2>actual &&
+     test_when_finished git branch --unset-upstream my13 &&
+     cat >expected <<EOF &&
+The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
+EOF
+     test_cmp expected actual
+'
+
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
 $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000        branch: Created from master
index 7f8693b928fdb827906f7c51ec0a13b2f36c5b83..58f482378392c8c358a3129094885f90ead9808c 100755 (executable)
@@ -47,7 +47,23 @@ test_expect_success 'rebase ignores empty commit' '
        git commit --allow-empty -m empty &&
        test_commit D &&
        git rebase C &&
-       test $(git log --format=%s C..) = "D"
+       test "$(git log --format=%s C..)" = "D"
+'
+
+test_expect_success 'rebase --keep-empty' '
+       git reset --hard D &&
+       git rebase --keep-empty C &&
+       test "$(git log --format=%s C..)" = "D
+empty"
+'
+
+test_expect_success 'rebase --keep-empty keeps empty even if already in upstream' '
+       git reset --hard A &&
+       git commit --allow-empty -m also-empty &&
+       git rebase --keep-empty D &&
+       test "$(git log --format=%s A..)" = "also-empty
+D
+empty"
 '
 
 test_done
index 595d2ff990ad3305f47977431c0b7d102ca3866b..34c86e5de69468fe7618383e556d1711cc49b0bb 100755 (executable)
@@ -47,7 +47,7 @@ test_expect_success 'cherry-pick --nonsense' '
        git diff --exit-code HEAD &&
        test_must_fail git cherry-pick --nonsense 2>msg &&
        git diff --exit-code HEAD "$pos" &&
-       grep '[Uu]sage:' msg
+       test_i18ngrep '[Uu]sage:' msg
 '
 
 test_expect_success 'revert --nonsense' '
@@ -56,7 +56,7 @@ test_expect_success 'revert --nonsense' '
        git diff --exit-code HEAD &&
        test_must_fail git revert --nonsense 2>msg &&
        git diff --exit-code HEAD "$pos" &&
-       grep '[Uu]sage:' msg
+       test_i18ngrep '[Uu]sage:' msg
 '
 
 test_expect_success 'cherry-pick after renaming branch' '
index 5a1340cee69f344613c09b7d4a3f3f7bebd6719e..a0c6e30d805e6e71b9fec4311a6ede298b6265b4 100755 (executable)
@@ -53,6 +53,11 @@ test_expect_success 'index lockfile was removed' '
 
 '
 
+test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' '
+       git checkout -f master &&
+       git cherry-pick --allow-empty-message empty-branch
+'
+
 test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' '
        git checkout master &&
        echo fourth >>file2 &&
index 75f7ff4f2fe21e86e0a26fe5a6c2119bef38404c..340afc760de030879fff496dca3db8432a6b992b 100755 (executable)
@@ -44,6 +44,21 @@ test_expect_success 'cherry-pick first..fourth works' '
        check_head_differs_from fourth
 '
 
+test_expect_success 'cherry-pick three one two works' '
+       git checkout -f first &&
+       test_commit one &&
+       test_commit two &&
+       test_commit three &&
+       git checkout -f master &&
+       git reset --hard first &&
+       git cherry-pick three one two &&
+       git diff --quiet three &&
+       git diff --quiet HEAD three &&
+       test "$(git log --reverse --format=%s first..)" = "three
+one
+two"
+'
+
 test_expect_success 'output to keep user entertained during multi-pick' '
        cat <<-\EOF >expected &&
        [master OBJID] second
index cd042633ba5ec36be89507d25254b32a8f0b98ae..5dfbda7491aceaa64215ac94bdd416f9b93f55d3 100755 (executable)
@@ -610,7 +610,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu
                git stash apply
        ) |
        sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..."
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 cat > expect << EOF
index 88b7a20c112a523b9c6640d778ac2840ce9adeb4..5fe57c5438e72150639fd6b970471a1689d30911 100755 (executable)
@@ -7,158 +7,147 @@ test_description='utf-8 decomposed (nfd) converted to precomposed (nfc)'
 
 . ./test-lib.sh
 
+if ! test_have_prereq UTF8_NFD_TO_NFC
+then
+       skip_all="filesystem does not corrupt utf-8"
+       test_done
+fi
+
+# create utf-8 variables
 Adiarnfc=`printf '\303\204'`
 Adiarnfd=`printf 'A\314\210'`
 
-# check if the feature is compiled in
-mkdir junk &&
->junk/"$Adiarnfc" &&
-case "$(cd junk && echo *)" in
-       "$Adiarnfd")
-       test_nfd=1
-       ;;
-       *)      ;;
-esac
-rm -rf junk
+Odiarnfc=`printf '\303\226'`
+Odiarnfd=`printf 'O\314\210'`
+AEligatu=`printf '\303\206'`
+Invalidu=`printf '\303\377'`
 
 
-if test "$test_nfd"
-then
-       # create more utf-8 variables
-       Odiarnfc=`printf '\303\226'`
-       Odiarnfd=`printf 'O\314\210'`
-       AEligatu=`printf '\303\206'`
-       Invalidu=`printf '\303\377'`
+#Create a string with 255 bytes (decomposed)
+Alongd=$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd #21 Byte
+Alongd=$Alongd$Alongd$Alongd                                           #63 Byte
+Alongd=$Alongd$Alongd$Alongd$Alongd$Adiarnfd                           #255 Byte
 
+#Create a string with 254 bytes (precomposed)
+Alongc=$AEligatu$AEligatu$AEligatu$AEligatu$AEligatu #10 Byte
+Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc           #50 Byte
+Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc           #250 Byte
+Alongc=$Alongc$AEligatu$AEligatu                     #254 Byte
 
-       #Create a string with 255 bytes (decomposed)
-       Alongd=$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd #21 Byte
-       Alongd=$Alongd$Alongd$Alongd                                           #63 Byte
-       Alongd=$Alongd$Alongd$Alongd$Alongd$Adiarnfd                           #255 Byte
-
-       #Create a string with 254 bytes (precomposed)
-       Alongc=$AEligatu$AEligatu$AEligatu$AEligatu$AEligatu #10 Byte
-       Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc           #50 Byte
-       Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc           #250 Byte
-       Alongc=$Alongc$AEligatu$AEligatu                     #254 Byte
-
-       test_expect_success "detect if nfd needed" '
-               precomposeunicode=`git config core.precomposeunicode` &&
-               test "$precomposeunicode" = false &&
-               git config core.precomposeunicode true
-       '
-       test_expect_success "setup" '
-               >x &&
-               git add x &&
-               git commit -m "1st commit" &&
-               git rm x &&
-               git commit -m "rm x"
-       '
-       test_expect_success "setup case mac" '
-               git checkout -b mac_os
-       '
-       # This will test nfd2nfc in readdir()
-       test_expect_success "add file Adiarnfc" '
-               echo f.Adiarnfc >f.$Adiarnfc &&
-               git add f.$Adiarnfc &&
-               git commit -m "add f.$Adiarnfc"
-       '
-       # This will test nfd2nfc in git stage()
-       test_expect_success "stage file d.Adiarnfd/f.Adiarnfd" '
-               mkdir d.$Adiarnfd &&
-               echo d.$Adiarnfd/f.$Adiarnfd >d.$Adiarnfd/f.$Adiarnfd &&
-               git stage d.$Adiarnfd/f.$Adiarnfd &&
-               git commit -m "add d.$Adiarnfd/f.$Adiarnfd"
-       '
-       test_expect_success "add link Adiarnfc" '
-               ln -s d.$Adiarnfd/f.$Adiarnfd l.$Adiarnfc &&
-               git add l.$Adiarnfc &&
-               git commit -m "add l.Adiarnfc"
-       '
-       # This will test git log
-       test_expect_success "git log f.Adiar" '
-               git log f.$Adiarnfc > f.Adiarnfc.log &&
-               git log f.$Adiarnfd > f.Adiarnfd.log &&
-               test -s f.Adiarnfc.log &&
-               test -s f.Adiarnfd.log &&
-               test_cmp f.Adiarnfc.log f.Adiarnfd.log &&
-               rm f.Adiarnfc.log f.Adiarnfd.log
-       '
-       # This will test git ls-files
-       test_expect_success "git lsfiles f.Adiar" '
-               git ls-files f.$Adiarnfc > f.Adiarnfc.log &&
-               git ls-files f.$Adiarnfd > f.Adiarnfd.log &&
-               test -s f.Adiarnfc.log &&
-               test -s f.Adiarnfd.log &&
-               test_cmp f.Adiarnfc.log f.Adiarnfd.log &&
-               rm f.Adiarnfc.log f.Adiarnfd.log
-       '
-       # This will test git mv
-       test_expect_success "git mv" '
-               git mv f.$Adiarnfd f.$Odiarnfc &&
-               git mv d.$Adiarnfd d.$Odiarnfc &&
-               git mv l.$Adiarnfd l.$Odiarnfc &&
-               git commit -m "mv Adiarnfd Odiarnfc"
-       '
-       # Files can be checked out as nfc
-       # And the link has been corrected from nfd to nfc
-       test_expect_success "git checkout nfc" '
-               rm f.$Odiarnfc &&
-               git checkout f.$Odiarnfc
-       '
-       # Make it possible to checkout files with their NFD names
-       test_expect_success "git checkout file nfd" '
-               rm -f f.* &&
-               git checkout f.$Odiarnfd
-       '
-       # Make it possible to checkout links with their NFD names
-       test_expect_success "git checkout link nfd" '
-               rm l.* &&
-               git checkout l.$Odiarnfd
-       '
-       test_expect_success "setup case mac2" '
-               git checkout master &&
-               git reset --hard &&
-               git checkout -b mac_os_2
-       '
-       # This will test nfd2nfc in git commit
-       test_expect_success "commit file d2.Adiarnfd/f.Adiarnfd" '
-               mkdir d2.$Adiarnfd &&
-               echo d2.$Adiarnfd/f.$Adiarnfd >d2.$Adiarnfd/f.$Adiarnfd &&
-               git add d2.$Adiarnfd/f.$Adiarnfd &&
-               git commit -m "add d2.$Adiarnfd/f.$Adiarnfd" -- d2.$Adiarnfd/f.$Adiarnfd
-       '
-       test_expect_success "setup for long decomposed filename" '
-               git checkout master &&
-               git reset --hard &&
-               git checkout -b mac_os_long_nfd_fn
-       '
-       test_expect_success "Add long decomposed filename" '
-               echo longd >$Alongd &&
-               git add * &&
-               git commit -m "Long filename"
-       '
-       test_expect_success "setup for long precomposed filename" '
-               git checkout master &&
-               git reset --hard &&
-               git checkout -b mac_os_long_nfc_fn
-       '
-       test_expect_success "Add long precomposed filename" '
-               echo longc >$Alongc &&
-               git add * &&
-               git commit -m "Long filename"
-       '
-       # Test if the global core.precomposeunicode stops autosensing
-       # Must be the last test case
-       test_expect_success "respect git config --global core.precomposeunicode" '
-               git config --global core.precomposeunicode true &&
-               rm -rf .git &&
-               git init &&
-               precomposeunicode=`git config core.precomposeunicode` &&
-               test "$precomposeunicode" = "true"
-       '
-else
-        say "Skipping nfc/nfd tests"
-fi
+test_expect_success "detect if nfd needed" '
+       precomposeunicode=`git config core.precomposeunicode` &&
+       test "$precomposeunicode" = false &&
+       git config core.precomposeunicode true
+'
+test_expect_success "setup" '
+       >x &&
+       git add x &&
+       git commit -m "1st commit" &&
+       git rm x &&
+       git commit -m "rm x"
+'
+test_expect_success "setup case mac" '
+       git checkout -b mac_os
+'
+# This will test nfd2nfc in readdir()
+test_expect_success "add file Adiarnfc" '
+       echo f.Adiarnfc >f.$Adiarnfc &&
+       git add f.$Adiarnfc &&
+       git commit -m "add f.$Adiarnfc"
+'
+# This will test nfd2nfc in git stage()
+test_expect_success "stage file d.Adiarnfd/f.Adiarnfd" '
+       mkdir d.$Adiarnfd &&
+       echo d.$Adiarnfd/f.$Adiarnfd >d.$Adiarnfd/f.$Adiarnfd &&
+       git stage d.$Adiarnfd/f.$Adiarnfd &&
+       git commit -m "add d.$Adiarnfd/f.$Adiarnfd"
+'
+test_expect_success "add link Adiarnfc" '
+       ln -s d.$Adiarnfd/f.$Adiarnfd l.$Adiarnfc &&
+       git add l.$Adiarnfc &&
+       git commit -m "add l.Adiarnfc"
+'
+# This will test git log
+test_expect_success "git log f.Adiar" '
+       git log f.$Adiarnfc > f.Adiarnfc.log &&
+       git log f.$Adiarnfd > f.Adiarnfd.log &&
+       test -s f.Adiarnfc.log &&
+       test -s f.Adiarnfd.log &&
+       test_cmp f.Adiarnfc.log f.Adiarnfd.log &&
+       rm f.Adiarnfc.log f.Adiarnfd.log
+'
+# This will test git ls-files
+test_expect_success "git lsfiles f.Adiar" '
+       git ls-files f.$Adiarnfc > f.Adiarnfc.log &&
+       git ls-files f.$Adiarnfd > f.Adiarnfd.log &&
+       test -s f.Adiarnfc.log &&
+       test -s f.Adiarnfd.log &&
+       test_cmp f.Adiarnfc.log f.Adiarnfd.log &&
+       rm f.Adiarnfc.log f.Adiarnfd.log
+'
+# This will test git mv
+test_expect_success "git mv" '
+       git mv f.$Adiarnfd f.$Odiarnfc &&
+       git mv d.$Adiarnfd d.$Odiarnfc &&
+       git mv l.$Adiarnfd l.$Odiarnfc &&
+       git commit -m "mv Adiarnfd Odiarnfc"
+'
+# Files can be checked out as nfc
+# And the link has been corrected from nfd to nfc
+test_expect_success "git checkout nfc" '
+       rm f.$Odiarnfc &&
+       git checkout f.$Odiarnfc
+'
+# Make it possible to checkout files with their NFD names
+test_expect_success "git checkout file nfd" '
+       rm -f f.* &&
+       git checkout f.$Odiarnfd
+'
+# Make it possible to checkout links with their NFD names
+test_expect_success "git checkout link nfd" '
+       rm l.* &&
+       git checkout l.$Odiarnfd
+'
+test_expect_success "setup case mac2" '
+       git checkout master &&
+       git reset --hard &&
+       git checkout -b mac_os_2
+'
+# This will test nfd2nfc in git commit
+test_expect_success "commit file d2.Adiarnfd/f.Adiarnfd" '
+       mkdir d2.$Adiarnfd &&
+       echo d2.$Adiarnfd/f.$Adiarnfd >d2.$Adiarnfd/f.$Adiarnfd &&
+       git add d2.$Adiarnfd/f.$Adiarnfd &&
+       git commit -m "add d2.$Adiarnfd/f.$Adiarnfd" -- d2.$Adiarnfd/f.$Adiarnfd
+'
+test_expect_success "setup for long decomposed filename" '
+       git checkout master &&
+       git reset --hard &&
+       git checkout -b mac_os_long_nfd_fn
+'
+test_expect_success "Add long decomposed filename" '
+       echo longd >$Alongd &&
+       git add * &&
+       git commit -m "Long filename"
+'
+test_expect_success "setup for long precomposed filename" '
+       git checkout master &&
+       git reset --hard &&
+       git checkout -b mac_os_long_nfc_fn
+'
+test_expect_success "Add long precomposed filename" '
+       echo longc >$Alongc &&
+       git add * &&
+       git commit -m "Long filename"
+'
+# Test if the global core.precomposeunicode stops autosensing
+# Must be the last test case
+test_expect_success "respect git config --global core.precomposeunicode" '
+       git config --global core.precomposeunicode true &&
+       rm -rf .git &&
+       git init &&
+       precomposeunicode=`git config core.precomposeunicode` &&
+       test "$precomposeunicode" = "true"
+'
 
 test_done
index 7a3e1f9a24e4be3759c29acc632fc461ede2b74b..3d4b1ba23f9eacec4e1bcd29345cc7d3943badce 100755 (executable)
@@ -36,24 +36,24 @@ 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_i18ncmp expect actual
 '
 
 test_expect_success '--shortstat output after text chmod' '
        git diff HEAD --shortstat >actual &&
-       test_cmp expect actual
+       test_i18ncmp 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_i18ncmp expect actual
 '
 
 test_expect_success '--shortstat output after binary chmod' '
        git diff HEAD --shortstat >actual &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 test_done
index ec4deea1923919a88c6f1f9d35e11cb7737e323d..1215ae544b6915fe2cc6df4d193c8f3a805beba1 100755 (executable)
@@ -63,7 +63,7 @@ test_expect_success 'apply --numstat understands diff --binary format' '
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
-test_expect_success 'apply detecting corrupt patch correctly' '
+test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
        git diff >output &&
        sed -e "s/-CIT/xCIT/" <output >broken &&
        test_must_fail git apply --stat --summary broken 2>detected &&
@@ -73,7 +73,7 @@ test_expect_success 'apply detecting corrupt patch correctly' '
        test "$detected" = xCIT
 '
 
-test_expect_success 'apply detecting corrupt patch correctly' '
+test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
        git diff --binary | sed -e "s/-CIT/xCIT/" >broken &&
        test_must_fail git apply --stat --summary broken 2>detected &&
        detected=`cat detected` &&
index c00a94b9ba4498e72a31856aac9cf653bbce8ba3..2d030a4ec35009105c26e9689dcae1150fed7b2d 100755 (executable)
@@ -66,5 +66,35 @@ test_expect_success 'suppress deletion diff with -B -D' '
        grep -v "Linus Torvalds" actual
 '
 
+test_expect_success 'prepare a file that ends with an incomplete line' '
+       test_seq 1 99 >seq &&
+       printf 100 >>seq &&
+       git add seq &&
+       git commit seq -m seq
+'
+
+test_expect_success 'rewrite the middle 90% of sequence file and terminate with newline' '
+       test_seq 1 5 >seq &&
+       test_seq 9331 9420 >>seq &&
+       test_seq 96 100 >>seq
+'
+
+test_expect_success 'confirm that sequence file is considered a rewrite' '
+       git diff -B seq >res &&
+       grep "dissimilarity index" res
+'
+
+test_expect_success 'no newline at eof is on its own line without -B' '
+       git diff seq >res &&
+       grep "^\\\\ " res &&
+       ! grep "^..*\\\\ " res
+'
+
+test_expect_success 'no newline at eof is on its own line with -B' '
+       git diff -B seq >res &&
+       grep "^\\\\ " res &&
+       ! grep "^..*\\\\ " res
+'
+
 test_done
 
diff --git a/t/t4054-diff-bogus-tree.sh b/t/t4054-diff-bogus-tree.sh
new file mode 100755 (executable)
index 0000000..0843c87
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='test diff with a bogus tree containing the null sha1'
+. ./test-lib.sh
+
+empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+
+test_expect_success 'create bogus tree' '
+       bogus_tree=$(
+               printf "100644 fooQQQQQQQQQQQQQQQQQQQQQ" |
+               q_to_nul |
+               git hash-object -w --stdin -t tree
+       )
+'
+
+test_expect_success 'create tree with matching file' '
+       echo bar >foo &&
+       git add foo &&
+       good_tree=$(git write-tree)
+       blob=$(git rev-parse :foo)
+'
+
+test_expect_success 'raw diff shows null sha1 (addition)' '
+       echo ":000000 100644 $_z40 $_z40 A      foo" >expect &&
+       git diff-tree $empty_tree $bogus_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (removal)' '
+       echo ":100644 000000 $_z40 $_z40 D      foo" >expect &&
+       git diff-tree $bogus_tree $empty_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (modification)' '
+       echo ":100644 100644 $blob $_z40 M      foo" >expect &&
+       git diff-tree $good_tree $bogus_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (other direction)' '
+       echo ":100644 100644 $_z40 $blob M      foo" >expect &&
+       git diff-tree $bogus_tree $good_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (reverse)' '
+       echo ":100644 100644 $_z40 $blob M      foo" >expect &&
+       git diff-tree -R $good_tree $bogus_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'raw diff shows null sha1 (index)' '
+       echo ":100644 100644 $_z40 $blob M      foo" >expect &&
+       git diff-index $bogus_tree >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'patch fails due to bogus sha1 (addition)' '
+       test_must_fail git diff-tree -p $empty_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (removal)' '
+       test_must_fail git diff-tree -p $bogus_tree $empty_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (modification)' '
+       test_must_fail git diff-tree -p $good_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (other direction)' '
+       test_must_fail git diff-tree -p $bogus_tree $good_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (reverse)' '
+       test_must_fail git diff-tree -R -p $good_tree $bogus_tree
+'
+
+test_expect_success 'patch fails due to bogus sha1 (index)' '
+       test_must_fail git diff-index -p $bogus_tree
+'
+
+test_done
index 99627bc6d69f17a8dce0ad318764e32cd9d9f507..b1b906b1bb58ad9f96ee8f6146a2d49ec999e3f2 100755 (executable)
@@ -8,30 +8,28 @@ test_description='git apply handling binary patches
 '
 . ./test-lib.sh
 
-# setup
-
-cat >file1 <<EOF
-A quick brown fox jumps over the lazy dog.
-A tiny little penguin runs around in circles.
-There is a flag with Linux written on it.
-A slow black-and-white panda just sits there,
-munching on his bamboo.
-EOF
-cat file1 >file2
-cat file1 >file4
-
-test_expect_success 'setup' "
+test_expect_success 'setup' '
+       cat >file1 <<-\EOF &&
+       A quick brown fox jumps over the lazy dog.
+       A tiny little penguin runs around in circles.
+       There is a flag with Linux written on it.
+       A slow black-and-white panda just sits there,
+       munching on his bamboo.
+       EOF
+       cat file1 >file2 &&
+       cat file1 >file4 &&
+
        git update-index --add --remove file1 file2 file4 &&
-       git commit -m 'Initial Version' 2>/dev/null &&
+       git commit -m "Initial Version" 2>/dev/null &&
 
        git checkout -b binary &&
-       "$PERL_PATH" -pe 'y/x/\000/' <file1 >file3 &&
+       "$PERL_PATH" -pe "y/x/\000/" <file1 >file3 &&
        cat file3 >file4 &&
        git add file2 &&
-       "$PERL_PATH" -pe 'y/\000/v/' <file3 >file1 &&
+       "$PERL_PATH" -pe "y/\000/v/" <file3 >file1 &&
        rm -f file2 &&
        git update-index --add --remove file1 file2 file3 file4 &&
-       git commit -m 'Second Version' &&
+       git commit -m "Second Version" &&
 
        git diff-tree -p master binary >B.diff &&
        git diff-tree -p -C master binary >C.diff &&
@@ -42,17 +40,25 @@ test_expect_success 'setup' "
        git diff-tree -p --full-index master binary >B-index.diff &&
        git diff-tree -p -C --full-index master binary >C-index.diff &&
 
+       git diff-tree -p --binary --no-prefix master binary -- file3 >B0.diff &&
+
        git init other-repo &&
-       (cd other-repo &&
-        git fetch .. master &&
-        git reset --hard FETCH_HEAD
+       (
+               cd other-repo &&
+               git fetch .. master &&
+               git reset --hard FETCH_HEAD
        )
-"
+'
 
 test_expect_success 'stat binary diff -- should not fail.' \
        'git checkout master &&
         git apply --stat --summary B.diff'
 
+test_expect_success 'stat binary -p0 diff -- should not fail.' '
+        git checkout master &&
+        git apply --stat -p0 B0.diff
+'
+
 test_expect_success 'stat binary diff (copy) -- should not fail.' \
        'git checkout master &&
         git apply --stat --summary C.diff'
@@ -143,4 +149,10 @@ test_expect_success 'apply binary diff (copy).' \
         git apply --allow-binary-replacement --index CF.diff &&
         test -z "$(git diff --name-status binary)"'
 
+test_expect_success 'apply binary -p0 diff' '
+       do_reset &&
+       git apply -p0 --index B0.diff &&
+       test -z "$(git diff --name-status binary -- file3)"
+'
+
 test_done
index a33d510bf6b27a6bb2c640cfc9d11f462cbdf7a6..c5fecdfed43dc5682307dd0fdb66ddd78dece056 100755 (executable)
@@ -32,7 +32,7 @@ test_expect_success 'apply git diff with -p2' '
 test_expect_success 'apply with too large -p' '
        cp file1.saved file1 &&
        test_must_fail git apply --stat -p3 patch.file 2>err &&
-       grep "removing 3 leading" err
+       test_i18ngrep "removing 3 leading" err
 '
 
 test_expect_success 'apply (-p2) traditional diff with funny filenames' '
@@ -54,7 +54,7 @@ test_expect_success 'apply (-p2) traditional diff with funny filenames' '
 test_expect_success 'apply with too large -p and fancy filename' '
        cp file1.saved file1 &&
        test_must_fail git apply --stat -p3 patch.escaped 2>err &&
-       grep "removing 3 leading" err
+       test_i18ngrep "removing 3 leading" err
 '
 
 test_expect_success 'apply (-p2) diff, mode change only' '
index 94da99075c55c790aae7a260ff6c4964f950c47c..2ecb4216b7179ef836836d90a5f76f5e4349ca23 100755 (executable)
@@ -30,9 +30,9 @@ EOF
 
 test_expect_success 'apply diff with inconsistent filenames in headers' '
        test_must_fail git apply bad1.patch 2>err &&
-       grep "inconsistent new filename" err &&
+       test_i18ngrep "inconsistent new filename" err &&
        test_must_fail git apply bad2.patch 2>err &&
-       grep "inconsistent old filename" err
+       test_i18ngrep "inconsistent old filename" err
 '
 
 test_done
index 3ab670d36aea890e07827b14486e4c49de83bdb5..7f6666fcd3ffd6dfe53ed72cedcbabfe5a626ed8 100755 (executable)
@@ -382,13 +382,13 @@ test_expect_success 'rerere --no-no-rerere-autoupdate' '
        git update-index --index-info <failedmerge &&
        cp file3.conflict file3 &&
        test_must_fail git rerere --no-no-rerere-autoupdate 2>err &&
-       grep [Uu]sage err &&
+       test_i18ngrep [Uu]sage err &&
        test_must_fail git update-index --refresh
 '
 
 test_expect_success 'rerere -h' '
        test_must_fail git rerere -h >help &&
-       grep [Uu]sage help
+       test_i18ngrep [Uu]sage help
 '
 
 test_done
index 71be59d446f773c4b567f00c469ac26225d11cb8..924ba536ca9718da0f4126d53c316a5f7a1926a5 100755 (executable)
@@ -178,11 +178,21 @@ test_expect_success 'git log --no-walk <commits> sorts by commit time' '
        test_cmp expect actual
 '
 
+test_expect_success 'git log --no-walk=sorted <commits> sorts by commit time' '
+       git log --no-walk=sorted --oneline 5d31159 804a787 394ef78 > actual &&
+       test_cmp expect actual
+'
+
 cat > expect << EOF
 5d31159 fourth
 804a787 sixth
 394ef78 fifth
 EOF
+test_expect_success 'git log --no-walk=unsorted <commits> leaves list of commits as given' '
+       git log --no-walk=unsorted --oneline 5d31159 804a787 394ef78 > actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'git show <commits> leaves list of commits as given' '
        git show --oneline -s 5d31159 804a787 394ef78 > actual &&
        test_cmp expect actual
@@ -803,7 +813,14 @@ sanitize_output () {
 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_i18ncmp expect actual.sanitized
+'
+
+test_expect_success 'dotdot is a parent directory' '
+       mkdir -p a/b &&
+       ( echo sixth && echo fifth ) >expect &&
+       ( cd a/b && git log --format=%s .. ) >actual &&
+       test_cmp expect actual
 '
 
 test_done
index 4afd77815f6bf698d4b865bad2300706d2a8e0fc..2c45de7aeaf2872873685ce9d055a7579161d875 100755 (executable)
@@ -88,7 +88,7 @@ test_expect_success 'NUL separation with --stat' '
        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_i18ncmp expected actual
 '
 
 test_expect_failure 'NUL termination with --stat' '
@@ -96,7 +96,7 @@ test_expect_failure 'NUL termination with --stat' '
        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_i18ncmp expected actual
 '
 
 test_done
index 2e52f8b83894faa0aeb324d8c43193b2cc9ea0cc..a07c871797d22e1da4b2d3904cab024a7bef37b4 100755 (executable)
@@ -416,11 +416,11 @@ test_expect_success \
 test_expect_success \
     'make sure index-pack detects the SHA1 collision' \
     'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg &&
-     grep "SHA1 COLLISION FOUND" msg'
+     test_i18ngrep "SHA1 COLLISION FOUND" msg'
 
 test_expect_success \
     'make sure index-pack detects the SHA1 collision (large blobs)' \
     'test_must_fail git -c core.bigfilethreshold=1 index-pack -o bad.idx test-3.pack 2>msg &&
-     grep "SHA1 COLLISION FOUND" msg'
+     test_i18ngrep "SHA1 COLLISION FOUND" msg'
 
 test_done
index 0eace37a03d75a7205575c36a0ec0de3ec401b1b..250c720c14602bdf21e6a7200437b13fe6feaca3 100755 (executable)
@@ -145,6 +145,41 @@ test_expect_success 'push --all excludes remote-tracking hierarchy' '
        )
 '
 
+test_expect_success 'receive-pack runs auto-gc in remote repo' '
+       rm -rf parent child &&
+       git init parent &&
+       (
+           # Setup a repo with 2 packs
+           cd parent &&
+           echo "Some text" >file.txt &&
+           git add . &&
+           git commit -m "Initial commit" &&
+           git repack -adl &&
+           echo "Some more text" >>file.txt &&
+           git commit -a -m "Second commit" &&
+           git repack
+       ) &&
+       cp -a parent child &&
+       (
+           # Set the child to auto-pack if more than one pack exists
+           cd child &&
+           git config gc.autopacklimit 1 &&
+           git branch test_auto_gc &&
+           # And create a file that follows the temporary object naming
+           # convention for the auto-gc to remove
+           : >.git/objects/tmp_test_object &&
+           test-chmtime =-1209601 .git/objects/tmp_test_object
+       ) &&
+       (
+           cd parent &&
+           echo "Even more text" >>file.txt &&
+           git commit -a -m "Third commit" &&
+           git send-pack ../child HEAD:refs/heads/test_auto_gc >output 2>&1 &&
+           grep "Auto packing the repository for optimum performance." output
+       ) &&
+       test ! -e child/.git/objects/tmp_test_object
+'
+
 rewound_push_setup() {
        rm -rf parent child &&
        mkdir parent &&
index e8af615e6dcdf365416ef9f71825c17a0d601186..88a21ff0bfbc166de47293c87b1ea930ba7732d7 100755 (executable)
@@ -52,7 +52,7 @@ test_expect_success setup '
 
 '
 
-test_expect_success 'remote information for the origin' '
+test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
 (
        cd test &&
        tokens_match origin "$(git remote)" &&
@@ -66,8 +66,6 @@ test_expect_success 'add another remote' '
        cd test &&
        git remote add -f second ../two &&
        tokens_match "origin second" "$(git remote)" &&
-       check_remote_track origin master side &&
-       check_remote_track second master side another &&
        check_tracking_branch second master side another &&
        git for-each-ref "--format=%(refname)" refs/remotes |
        sed -e "/^refs\/remotes\/origin\//d" \
@@ -77,6 +75,14 @@ test_expect_success 'add another remote' '
 )
 '
 
+test_expect_success C_LOCALE_OUTPUT 'check remote tracking' '
+(
+       cd test &&
+       check_remote_track origin master side &&
+       check_remote_track second master side another
+)
+'
+
 test_expect_success 'remote forces tracking branches' '
 (
        cd test &&
@@ -95,7 +101,7 @@ test_expect_success 'remove remote' '
 )
 '
 
-test_expect_success 'remove remote' '
+test_expect_success C_LOCALE_OUTPUT 'remove remote' '
 (
        cd test &&
        tokens_match origin "$(git remote)" &&
@@ -131,8 +137,8 @@ EOF
        git remote rm oops 2>actual2 &&
        git branch -d foobranch &&
        git tag -d footag &&
-       test_cmp expect1 actual1 &&
-       test_cmp expect2 actual2
+       test_i18ncmp expect1 actual1 &&
+       test_i18ncmp expect2 actual2
 )
 '
 
@@ -192,7 +198,7 @@ test_expect_success 'show' '
         git config --add remote.two.push refs/heads/master:refs/heads/another &&
         git remote show origin two > output &&
         git branch -d rebase octopus &&
-        test_cmp expect output)
+        test_i18ncmp expect output)
 '
 
 cat > test/expect << EOF
@@ -217,7 +223,7 @@ test_expect_success 'show -n' '
         cd test &&
         git remote show -n origin > output &&
         mv ../one.unreachable ../one &&
-        test_cmp expect output)
+        test_i18ncmp expect output)
 '
 
 test_expect_success 'prune' '
@@ -255,7 +261,7 @@ EOF
 test_expect_success 'set-head --auto fails w/multiple HEADs' '
        (cd test &&
         test_must_fail git remote set-head --auto two >output 2>&1 &&
-       test_cmp expect output)
+       test_i18ncmp expect output)
 '
 
 cat >test/expect <<EOF
@@ -285,7 +291,7 @@ test_expect_success 'prune --dry-run' '
         test_must_fail git rev-parse refs/remotes/origin/side &&
        (cd ../one &&
         git branch -m side side2) &&
-        test_cmp expect output)
+        test_i18ncmp expect output)
 '
 
 test_expect_success 'add --mirror && prune' '
@@ -705,7 +711,7 @@ test_expect_success 'remote prune to cause a dangling symref' '
                cd seven &&
                git remote prune origin
        ) >err 2>&1 &&
-       grep "has become dangling" err &&
+       test_i18ngrep "has become dangling" err &&
 
        : And the dangling symref will not cause other annoying errors &&
        (
index 6b2a5f4a65659b7fcfcd9d096a0b43473e1cf1d6..c983d3694c74cf6de6b7bd9a74c28d41462171f0 100755 (executable)
@@ -35,8 +35,8 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' '
        printf "0032want %s\n00000009done\n0000" \
                $(git rev-parse HEAD) >input &&
        test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
-       grep "unable to read" output.err &&
-       grep "pack-objects died" output.err
+       test_i18ngrep "unable to read" output.err &&
+       test_i18ngrep "pack-objects died" output.err
 '
 
 test_expect_success 'corrupt repo differently' '
index 1eea64765608dc4a2e4b19eec4155890306e5d59..f141f2d1da3ca8186ccc7a742bddd06f2e7f8970 100755 (executable)
@@ -46,15 +46,7 @@ test_expect_success 'create password-protected repository' '
               "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git"
 '
 
-test_expect_success 'setup askpass helper' '
-       cat >askpass <<-\EOF &&
-       #!/bin/sh
-       echo user@host
-       EOF
-       chmod +x askpass &&
-       GIT_ASKPASS="$PWD/askpass" &&
-       export GIT_ASKPASS
-'
+setup_askpass_helper
 
 test_expect_success 'clone remote repository' '
        cd "$ROOT_PATH" &&
@@ -162,6 +154,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
 
 test_expect_success 'push to password-protected repository (user in URL)' '
        test_commit pw-user &&
+       set_askpass user@host &&
        git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD &&
        git rev-parse --verify HEAD >expect &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
@@ -169,9 +162,15 @@ test_expect_success 'push to password-protected repository (user in URL)' '
        test_cmp expect actual
 '
 
+test_expect_failure 'user was prompted only once for password' '
+       expect_askpass pass user@host
+'
+
 test_expect_failure 'push to password-protected repository (no user in URL)' '
        test_commit pw-nouser &&
+       set_askpass user@host &&
        git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD &&
+       expect_askpass both user@host
        git rev-parse --verify HEAD >expect &&
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \
                rev-parse --verify HEAD >actual &&
index 312e484090d1ceb392399768f2229ae553182fe7..4b4b4a604f3075dec1056b31da5cccc0c4ed5a9a 100755 (executable)
@@ -36,6 +36,8 @@ test_expect_success 'setup remote repository' '
        mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
 '
 
+setup_askpass_helper
+
 cat >exp <<EOF
 GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
 POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
@@ -64,7 +66,10 @@ test_expect_success 'no empty path components' '
 
 test_expect_success 'clone remote repository' '
        rm -rf test_repo_clone &&
-       git clone $HTTPD_URL/smart/test_repo.git test_repo_clone
+       git clone $HTTPD_URL/smart/test_repo.git test_repo_clone &&
+       (
+               cd test_repo_clone && git config push.default matching
+       )
 '
 
 test_expect_success 'push to remote repository (standard)' '
@@ -266,5 +271,29 @@ test_expect_success 'http push respects GIT_COMMITTER_* in reflog' '
        test_cmp expect actual
 '
 
+test_expect_success 'push over smart http with auth' '
+       cd "$ROOT_PATH/test_repo_clone" &&
+       echo push-auth-test >expect &&
+       test_commit push-auth-test &&
+       set_askpass user@host &&
+       git push "$HTTPD_URL"/auth/smart/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -1 --format=%s >actual &&
+       expect_askpass both user@host &&
+       test_cmp expect actual
+'
+
+test_expect_success 'push to auth-only-for-push repo' '
+       cd "$ROOT_PATH/test_repo_clone" &&
+       echo push-half-auth >expect &&
+       test_commit push-half-auth &&
+       set_askpass user@host &&
+       git push "$HTTPD_URL"/auth-push/smart/test_repo.git &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \
+               log -1 --format=%s >actual &&
+       expect_askpass both user@host &&
+       test_cmp expect actual
+'
+
 stop_httpd
 test_done
index b06f817af32483631b3ec297246e156400bc6b93..16ef0419e9e4ea8d42aebd63c5bc5ec7f63d3732 100755 (executable)
@@ -41,68 +41,34 @@ test_expect_success 'clone http repository' '
 '
 
 test_expect_success 'create password-protected repository' '
-       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/auth/" &&
+       mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" &&
        cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
-              "$HTTPD_DOCUMENT_ROOT_PATH/auth/repo.git"
-'
-
-test_expect_success 'setup askpass helpers' '
-       cat >askpass <<-EOF &&
-       #!/bin/sh
-       echo >>"$PWD/askpass-query" "askpass: \$*" &&
-       cat "$PWD/askpass-response"
-       EOF
-       chmod +x askpass &&
-       GIT_ASKPASS="$PWD/askpass" &&
-       export GIT_ASKPASS
-'
-
-expect_askpass() {
-       dest=$HTTPD_DEST
-       {
-               case "$1" in
-               none)
-                       ;;
-               pass)
-                       echo "askpass: Password for 'http://$2@$dest': "
-                       ;;
-               both)
-                       echo "askpass: Username for 'http://$dest': "
-                       echo "askpass: Password for 'http://$2@$dest': "
-                       ;;
-               *)
-                       false
-                       ;;
-               esac
-       } >askpass-expect &&
-       test_cmp askpass-expect askpass-query
-}
+              "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git"
+'
+
+setup_askpass_helper
 
 test_expect_success 'cloning password-protected repository can fail' '
-       >askpass-query &&
-       echo wrong >askpass-response &&
-       test_must_fail git clone "$HTTPD_URL/auth/repo.git" clone-auth-fail &&
+       set_askpass wrong &&
+       test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail &&
        expect_askpass both wrong
 '
 
 test_expect_success 'http auth can use user/pass in URL' '
-       >askpass-query &&
-       echo wrong >askpass-response &&
-       git clone "$HTTPD_URL_USER_PASS/auth/repo.git" clone-auth-none &&
+       set_askpass wrong &&
+       git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none &&
        expect_askpass none
 '
 
 test_expect_success 'http auth can use just user in URL' '
-       >askpass-query &&
-       echo user@host >askpass-response &&
-       git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-pass &&
+       set_askpass user@host &&
+       git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass &&
        expect_askpass pass user@host
 '
 
 test_expect_success 'http auth can request both user and pass' '
-       >askpass-query &&
-       echo user@host >askpass-response &&
-       git clone "$HTTPD_URL/auth/repo.git" clone-auth-both &&
+       set_askpass user@host &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both &&
        expect_askpass both user@host
 '
 
@@ -112,25 +78,22 @@ test_expect_success 'http auth respects credential helper config' '
                echo username=user@host
                echo password=user@host
        }; f" &&
-       >askpass-query &&
-       echo wrong >askpass-response &&
-       git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper &&
+       set_askpass wrong &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper &&
        expect_askpass none
 '
 
 test_expect_success 'http auth can get username from config' '
        test_config_global "credential.$HTTPD_URL.username" user@host &&
-       >askpass-query &&
-       echo user@host >askpass-response &&
-       git clone "$HTTPD_URL/auth/repo.git" clone-auth-user &&
+       set_askpass user@host &&
+       git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user &&
        expect_askpass pass user@host
 '
 
 test_expect_success 'configured username does not override URL' '
        test_config_global "credential.$HTTPD_URL.username" wrong &&
-       >askpass-query &&
-       echo user@host >askpass-response &&
-       git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-user2 &&
+       set_askpass user@host &&
+       git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 &&
        expect_askpass pass user@host
 '
 
index fadf2f258ea5305fb52d418a6409fb07889bc205..2db5c3564181818efdf885188d7cc597024c6f12 100755 (executable)
@@ -27,6 +27,8 @@ test_expect_success 'create http-accessible bare repository' '
        git push public master:master
 '
 
+setup_askpass_helper
+
 cat >exp <<EOF
 > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
 > Accept: */*
@@ -109,12 +111,30 @@ test_expect_success 'follow redirects (302)' '
        git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
 '
 
+test_expect_success 'clone from password-protected repository' '
+       echo two >expect &&
+       set_askpass user@host &&
+       git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth &&
+       expect_askpass both user@host &&
+       git --git-dir=smart-auth log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone from auth-only-for-push repository' '
+       echo two >expect &&
+       set_askpass wrong &&
+       git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth &&
+       expect_askpass none &&
+       git --git-dir=smart-noauth log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
 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`
+       for i in `test_seq 50000`
        do
                echo "commit refs/heads/too-many-refs"
                echo "mark :$i"
index 172178490ad126e07b70a6e3f72e0bf723d2c42b..752f5cb7d0f522a8a6654bae06b07a1727df7505 100755 (executable)
@@ -456,4 +456,14 @@ test_atom refs/tags/signed-long contents "subject line
 body contents
 $sig"
 
+cat >expected <<\EOF
+408fe76d02a785a006c2e9c669b7be5589ede96d <committer@example.com> refs/tags/master
+90b5ebede4899eda64893bc2a4c8f1d6fb6dfc40 <committer@example.com> refs/tags/bogo
+EOF
+
+test_expect_success 'Verify sort with multiple keys' '
+       git for-each-ref --format="%(objectname) %(taggeremail) %(refname)" --sort=objectname --sort=taggeremail \
+               refs/tags/bogo refs/tags/master > actual &&
+       test_cmp expected actual
+'
 test_done
index 82f363993712d2c0de44de6285f956b46e1ceaf2..b1a63655f98dfc8d19c1a80f86652654cd9cade6 100755 (executable)
@@ -11,7 +11,7 @@ test_expect_success 'gc empty repository' '
 
 test_expect_success 'gc --gobbledegook' '
        test_expect_code 129 git gc --nonsense 2>err &&
-       grep "[Uu]sage: git gc" err
+       test_i18ngrep "[Uu]sage: git gc" err
 '
 
 test_expect_success 'gc -h with invalid configuration' '
@@ -22,7 +22,7 @@ test_expect_success 'gc -h with invalid configuration' '
                echo "[gc] pruneexpire = CORRUPT" >>.git/config &&
                test_expect_code 129 git gc -h >usage 2>&1
        ) &&
-       grep "[Uu]sage" broken/usage
+       test_i18ngrep "[Uu]sage" broken/usage
 '
 
 test_done
index a40cd3630c28a13f4e0f36ef9f56925d3c8579ed..e41fa00b80e9b3eb47e45be848397ccda0f47c53 100755 (executable)
@@ -108,4 +108,16 @@ test_expect_success 'showing range' '
        test_cmp expect actual.filtered
 '
 
+test_expect_success '-s suppresses diff' '
+       echo main3 >expect &&
+       git show -s --format=%s main3 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--quiet suppresses diff' '
+       echo main3 >expect &&
+       git show --quiet --format=%s main3 >actual &&
+       test_cmp expect actual
+'
+
 test_done
index c73bec9551eb27dab25aad69c83ae7ce9f9b11a8..56a81cd7486716897cc4f08196d0815a62461ffd 100755 (executable)
@@ -258,6 +258,27 @@ test_expect_success 'init should register submodule url in .git/config' '
        test_cmp expect url
 '
 
+test_failure_with_unknown_submodule () {
+       test_must_fail git submodule $1 no-such-submodule 2>output.err &&
+       grep "^error: .*no-such-submodule" output.err
+}
+
+test_expect_success 'init should fail with unknown submodule' '
+       test_failure_with_unknown_submodule init
+'
+
+test_expect_success 'update should fail with unknown submodule' '
+       test_failure_with_unknown_submodule update
+'
+
+test_expect_success 'status should fail with unknown submodule' '
+       test_failure_with_unknown_submodule status
+'
+
+test_expect_success 'sync should fail with unknown submodule' '
+       test_failure_with_unknown_submodule sync
+'
+
 test_expect_success 'update should fail when path is used by a file' '
        echo hello >expect &&
 
@@ -418,10 +439,7 @@ test_expect_success 'moving to a commit without submodule does not leave empty d
 '
 
 test_expect_success 'submodule <invalid-path> warns' '
-
-       git submodule no-such-submodule 2> output.err &&
-       grep "^error: .*no-such-submodule" output.err
-
+       test_failure_with_unknown_submodule
 '
 
 test_expect_success 'add submodules without specifying an explicit path' '
index ce61d4c0fa3d916757e09224ed79bc191fb0cb3d..15426530e49ef2e42cda2528f4a72a784eb40cd9 100755 (executable)
@@ -123,6 +123,18 @@ test_expect_success 'submodule update should throw away changes with --force ' '
        )
 '
 
+test_expect_success 'submodule update --force forcibly checks out submodules' '
+       (cd super &&
+        (cd submodule &&
+         rm -f file
+        ) &&
+        git submodule update --force submodule &&
+        (cd submodule &&
+         test "$(git status -s file)" = ""
+        )
+       )
+'
+
 test_expect_success 'submodule update --rebase staying on master' '
        (cd super/submodule &&
          git checkout master
@@ -367,7 +379,7 @@ test_expect_success 'submodule update continues after checkout error' '
         git submodule init &&
         git commit -am "new_submodule" &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../expect
+         git rev-parse --verify HEAD >../expect
         ) &&
         (cd submodule &&
          test_commit "update_submodule" file
@@ -384,7 +396,7 @@ test_expect_success 'submodule update continues after checkout error' '
         git checkout HEAD^ &&
         test_must_fail git submodule update &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../actual
+         git rev-parse --verify HEAD >../actual
         ) &&
         test_cmp expect actual
        )
@@ -413,7 +425,7 @@ test_expect_success 'submodule update continues after recursive checkout error'
          test_commit "update_submodule_again_again" file
         ) &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../expect &&
+         git rev-parse --verify HEAD >../expect &&
          test_commit "update_submodule2_again" file
         ) &&
         git add submodule &&
@@ -428,7 +440,7 @@ test_expect_success 'submodule update continues after recursive checkout error'
         ) &&
         test_must_fail git submodule update --recursive &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../actual
+         git rev-parse --verify HEAD >../actual
         ) &&
         test_cmp expect actual
        )
@@ -460,12 +472,12 @@ test_expect_success 'submodule update exit immediately in case of merge conflict
         ) &&
         git checkout HEAD^ &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../expect
+         git rev-parse --verify HEAD >../expect
         ) &&
         git config submodule.submodule.update merge &&
         test_must_fail git submodule update &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../actual
+         git rev-parse --verify HEAD >../actual
         ) &&
         test_cmp expect actual
        )
@@ -495,12 +507,12 @@ test_expect_success 'submodule update exit immediately after recursive rebase er
         ) &&
         git checkout HEAD^ &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../expect
+         git rev-parse --verify HEAD >../expect
         ) &&
         git config submodule.submodule.update rebase &&
         test_must_fail git submodule update &&
         (cd submodule2 &&
-         git rev-parse --max-count=1 HEAD > ../actual
+         git rev-parse --verify HEAD >../actual
         ) &&
         test_cmp expect actual
        )
index c206f4777a36a28687def873bd9fe60ed322fbbc..e313ef196ed91a6ad12c70b147e2309af8fb4bfd 100755 (executable)
@@ -80,7 +80,7 @@ test_expect_success 'status --column' '
 #      dir1/untracked dir2/untracked untracked
 #      dir2/modified  output
 EOF
-       test_cmp expect output
+       test_i18ncmp expect output
 '
 
 cat >expect <<\EOF
index 9e27bbf902e275671cb80310630a8bedcaab353c..5e19598fe72787e17d521980a3edfacb1dfa2024 100755 (executable)
@@ -157,7 +157,7 @@ test_expect_success 'merge -h with invalid index' '
                >.git/index &&
                test_expect_code 129 git merge -h 2>usage
        ) &&
-       grep "[Uu]sage: git merge" broken/usage
+       test_i18ngrep "[Uu]sage: git merge" broken/usage
 '
 
 test_expect_success 'reject non-strategy with a git-merge-foo name' '
index f5e16fc7db3f1dd84aff6d86261d10b3b3d00826..6fa0c70506b4a4f6b066d6f3d25aa0e499b419b5 100755 (executable)
@@ -55,6 +55,16 @@ test_expect_success 'setup' '
     git rm file12 &&
     git commit -m "branch1 changes" &&
 
+    git checkout -b stash1 master &&
+    echo stash1 change file11 >file11 &&
+    git add file11 &&
+    git commit -m "stash1 changes" &&
+
+    git checkout -b stash2 master &&
+    echo stash2 change file11 >file11 &&
+    git add file11 &&
+    git commit -m "stash2 changes" &&
+
     git checkout master &&
     git submodule update -N &&
     echo master updated >file1 &&
@@ -193,7 +203,35 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' '
     git reset --hard
 '
 
+test_expect_success 'conflicted stash sets up rerere'  '
+    git config rerere.enabled true &&
+    git checkout stash1 &&
+    echo "Conflicting stash content" >file11 &&
+    git stash &&
+
+    git checkout --detach stash2 &&
+    test_must_fail git stash apply &&
+
+    test -n "$(git ls-files -u)" &&
+    conflicts="$(git rerere remaining)" &&
+    test "$conflicts" = "file11" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" != "No files need merging" &&
+
+    git commit -am "save the stash resolution" &&
+
+    git reset --hard stash2 &&
+    test_must_fail git stash apply &&
+
+    test -n "$(git ls-files -u)" &&
+    conflicts="$(git rerere remaining)" &&
+    test -z "$conflicts" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging"
+'
+
 test_expect_success 'mergetool takes partial path' '
+    git reset --hard
     git config rerere.enabled false &&
     git checkout -b test12 branch1 &&
     git submodule update -N &&
index 523d04123d02cabb8703f91e9bec194c7d737f00..35d357d4c8bdc0ef313a2fc269a9ca81b22489f1 100755 (executable)
@@ -250,6 +250,84 @@ do
                git -c grep.extendedRegexp=true grep "a+b*c" ab >actual &&
                test_cmp expected actual
        '
+
+       test_expect_success "grep $L with grep.patterntype=basic" '
+               echo "ab:a+bc" >expected &&
+               git -c grep.patterntype=basic grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep $L with grep.patterntype=extended" '
+               echo "ab:abc" >expected &&
+               git -c grep.patterntype=extended grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep $L with grep.patterntype=fixed" '
+               echo "ab:a+b*c" >expected &&
+               git -c grep.patterntype=fixed grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success LIBPCRE "grep $L with grep.patterntype=perl" '
+               echo "ab:a+b*c" >expected &&
+               git -c grep.patterntype=perl grep "a\x{2b}b\x{2a}c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" '
+               echo "ab:abc" >expected &&
+               git \
+                       -c grep.patternType=default \
+                       -c grep.extendedRegexp=true \
+                       grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=default" '
+               echo "ab:abc" >expected &&
+               git \
+                       -c grep.extendedRegexp=true \
+                       -c grep.patternType=default \
+                       grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success 'grep $L with grep.patternType=extended and grep.extendedRegexp=false' '
+               echo "ab:abc" >expected &&
+               git \
+                       -c grep.patternType=extended \
+                       -c grep.extendedRegexp=false \
+                       grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success 'grep $L with grep.patternType=basic and grep.extendedRegexp=true' '
+               echo "ab:a+bc" >expected &&
+               git \
+                       -c grep.patternType=basic \
+                       -c grep.extendedRegexp=true \
+                       grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success 'grep $L with grep.extendedRegexp=false and grep.patternType=extended' '
+               echo "ab:abc" >expected &&
+               git \
+                       -c grep.extendedRegexp=false \
+                       -c grep.patternType=extended \
+                       grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success 'grep $L with grep.extendedRegexp=true and grep.patternType=basic' '
+               echo "ab:a+bc" >expected &&
+               git \
+                       -c grep.extendedRegexp=true \
+                       -c grep.patternType=basic \
+                       grep "a+b*c" ab >actual &&
+               test_cmp expected actual
+       '
 done
 
 cat >expected <<EOF
@@ -761,44 +839,147 @@ test_expect_success 'grep -G invalidpattern properly dies ' '
        test_must_fail git grep -G "a["
 '
 
+test_expect_success 'grep invalidpattern properly dies with grep.patternType=basic' '
+       test_must_fail git -c grep.patterntype=basic grep "a["
+'
+
 test_expect_success 'grep -E invalidpattern properly dies ' '
        test_must_fail git grep -E "a["
 '
 
+test_expect_success 'grep invalidpattern properly dies with grep.patternType=extended' '
+       test_must_fail git -c grep.patterntype=extended grep "a["
+'
+
 test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' '
        test_must_fail git grep -P "a["
 '
 
+test_expect_success LIBPCRE 'grep invalidpattern properly dies with grep.patternType=perl' '
+       test_must_fail git -c grep.patterntype=perl grep "a["
+'
+
 test_expect_success 'grep -G -E -F pattern' '
        echo "ab:a+b*c" >expected &&
        git grep -G -E -F "a+b*c" ab >actual &&
        test_cmp expected actual
 '
 
+test_expect_success 'grep pattern with grep.patternType=basic, =extended, =fixed' '
+       echo "ab:a+b*c" >expected &&
+       git \
+               -c grep.patterntype=basic \
+               -c grep.patterntype=extended \
+               -c grep.patterntype=fixed \
+               grep "a+b*c" ab >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'grep -E -F -G pattern' '
        echo "ab:a+bc" >expected &&
        git grep -E -F -G "a+b*c" ab >actual &&
        test_cmp expected actual
 '
 
+test_expect_success 'grep pattern with grep.patternType=extended, =fixed, =basic' '
+       echo "ab:a+bc" >expected &&
+       git \
+               -c grep.patterntype=extended \
+               -c grep.patterntype=fixed \
+               -c grep.patterntype=basic \
+               grep "a+b*c" ab >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'grep -F -G -E pattern' '
        echo "ab:abc" >expected &&
        git grep -F -G -E "a+b*c" ab >actual &&
        test_cmp expected actual
 '
 
+test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =extended' '
+       echo "ab:abc" >expected &&
+       git \
+               -c grep.patterntype=fixed \
+               -c grep.patterntype=basic \
+               -c grep.patterntype=extended \
+               grep "a+b*c" ab >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'grep -G -F -P -E pattern' '
        >empty &&
        test_must_fail git grep -G -F -P -E "a\x{2b}b\x{2a}c" ab >actual &&
        test_cmp empty actual
 '
 
+test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =perl, =extended' '
+       >empty &&
+       test_must_fail git \
+               -c grep.patterntype=fixed \
+               -c grep.patterntype=basic \
+               -c grep.patterntype=perl \
+               -c grep.patterntype=extended \
+               grep "a\x{2b}b\x{2a}c" ab >actual &&
+       test_cmp empty actual
+'
+
 test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '
        echo "ab:a+b*c" >expected &&
        git grep -G -F -E -P "a\x{2b}b\x{2a}c" ab >actual &&
        test_cmp expected actual
 '
 
+test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' '
+       echo "ab:a+b*c" >expected &&
+       git \
+               -c grep.patterntype=fixed \
+               -c grep.patterntype=basic \
+               -c grep.patterntype=extended \
+               -c grep.patterntype=perl \
+               grep "a\x{2b}b\x{2a}c" ab >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success LIBPCRE 'grep -P pattern with grep.patternType=fixed' '
+       echo "ab:a+b*c" >expected &&
+       git \
+               -c grep.patterntype=fixed \
+               grep -P "a\x{2b}b\x{2a}c" ab >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -F pattern with grep.patternType=basic' '
+       echo "ab:a+b*c" >expected &&
+       git \
+               -c grep.patterntype=basic \
+               grep -F "*c" ab >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -G pattern with grep.patternType=fixed' '
+       {
+               echo "ab:a+b*c"
+               echo "ab:a+bc"
+       } >expected &&
+       git \
+               -c grep.patterntype=fixed \
+               grep -G "a+b" ab >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -E pattern with grep.patternType=fixed' '
+       {
+               echo "ab:a+b*c"
+               echo "ab:a+bc"
+               echo "ab:abc"
+       } >expected &&
+       git \
+               -c grep.patterntype=fixed \
+               grep -E "a+" ab >actual &&
+       test_cmp expected actual
+'
+
 test_config() {
        git config "$1" "$2" &&
        test_when_finished "git config --unset $1"
index 8c12c65c72658acb37fa715fc93756381bc824bb..035122808b6e859810ee70daf381c2c895527894 100755 (executable)
@@ -841,6 +841,19 @@ test_expect_success $PREREQ '--compose adds MIME for utf8 subject' '
        grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
 '
 
+test_expect_success $PREREQ 'utf8 author is correctly passed on' '
+       clean_fake_sendmail &&
+       test_commit weird_author &&
+       test_when_finished "git reset --hard HEAD^" &&
+       git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" &&
+       git format-patch --stdout -1 >funny_name.patch &&
+       git send-email --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         funny_name.patch &&
+       grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1
+'
+
 test_expect_success $PREREQ 'detects ambiguous reference/file conflict' '
        echo master > master &&
        git add master &&
index 289fc313fb737ac7895580fd8407cff758b085e7..ee73013eed0b1c6410319aa3d7e4e57ea242a594 100755 (executable)
@@ -27,15 +27,17 @@ test_expect_success 'setup old-looking metadata' '
 head=`git rev-parse --verify refs/heads/git-svn-HEAD^0`
 test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'"
 
+svnrepo_escaped=`echo $svnrepo | sed 's/ /%20/'`
+
 test_expect_success 'initialize old-style (v0) git svn layout' '
        mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info &&
        echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url &&
        echo "$svnrepo" > "$GIT_DIR"/svn/info/url &&
        git svn migrate &&
-       ! test -d "$GIT_DIR"/git svn &&
+       ! test -d "$GIT_DIR"/git-svn &&
        git rev-parse --verify refs/${remotes_git_svn}^0 &&
        git rev-parse --verify refs/remotes/svn^0 &&
-       test "$(git config --get svn-remote.svn.url)" = "$svnrepo" &&
+       test "$(git config --get svn-remote.svn.url)" = "$svnrepo_escaped" &&
        test `git config --get svn-remote.svn.fetch` = \
              ":refs/${remotes_git_svn}"
        '
index 63fc982c8cdbd9c19eb06bba58ad5e86da5dd03e..193d3cabddec0c4ed63edeade9b3fb93bef790bb 100755 (executable)
@@ -32,6 +32,11 @@ test_expect_success 'setup svnrepo' '
        start_httpd
        '
 
+# SVN 1.7 will truncate "not-a%40{0]" to just "not-a".
+# Look at what SVN wound up naming the branch and use that.
+# Be sure to escape the @ if it shows up.
+non_reflog=`svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/'`
+
 test_expect_success 'test clone with funky branch names' '
        git svn clone -s "$svnrepo/pr ject" project &&
        (
@@ -42,7 +47,7 @@ test_expect_success 'test clone with funky branch names' '
                git rev-parse "refs/remotes/%2Eleading_dot" &&
                git rev-parse "refs/remotes/trailing_dot%2E" &&
                git rev-parse "refs/remotes/trailing_dotlock%2Elock" &&
-               git rev-parse "refs/remotes/not-a%40{0}reflog"
+               git rev-parse "refs/remotes/$non_reflog"
        )
        '
 
index 99fe16b72d448e9b5cf686304cb040c221ad1f44..9730821c3032c7d3251e2ce0d260df15f6c24f21 100755 (executable)
@@ -410,6 +410,83 @@ test_expect_failure 'git p4 clone file subset branch' '
                test_path_is_missing file3
        )
 '
+
+# From a report in http://stackoverflow.com/questions/11893688
+# where --use-client-spec caused branch prefixes not to be removed;
+# every file in git appeared into a subdirectory of the branch name.
+test_expect_success 'use-client-spec detect-branches setup' '
+       rm -rf "$cli" &&
+       mkdir "$cli" &&
+       (
+               cd "$cli" &&
+               client_view "//depot/usecs/... //client/..." &&
+               mkdir b1 &&
+               echo b1/b1-file1 >b1/b1-file1 &&
+               p4 add b1/b1-file1 &&
+               p4 submit -d "b1/b1-file1" &&
+
+               p4 integrate //depot/usecs/b1/... //depot/usecs/b2/... &&
+               p4 submit -d "b1 -> b2" &&
+               p4 branch -i <<-EOF &&
+               Branch: b2
+               View: //depot/usecs/b1/... //depot/usecs/b2/...
+               EOF
+
+               echo b2/b2-file2 >b2/b2-file2 &&
+               p4 add b2/b2-file2 &&
+               p4 submit -d "b2/b2-file2"
+       )
+'
+
+test_expect_success 'use-client-spec detect-branches files in top-level' '
+       test_when_finished cleanup_git &&
+       test_create_repo "$git" &&
+       (
+               cd "$git" &&
+               git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
+               git checkout -b master p4/usecs/b1 &&
+               test_path_is_file b1-file1 &&
+               test_path_is_missing b2-file2 &&
+               test_path_is_missing b1 &&
+               test_path_is_missing b2 &&
+
+               git checkout -b b2 p4/usecs/b2 &&
+               test_path_is_file b1-file1 &&
+               test_path_is_file b2-file2 &&
+               test_path_is_missing b1 &&
+               test_path_is_missing b2
+       )
+'
+
+test_expect_success 'use-client-spec detect-branches skips branches setup' '
+       (
+               cd "$cli" &&
+
+               p4 integrate //depot/usecs/b1/... //depot/usecs/b3/... &&
+               p4 submit -d "b1 -> b3" &&
+               p4 branch -i <<-EOF &&
+               Branch: b3
+               View: //depot/usecs/b1/... //depot/usecs/b3/...
+               EOF
+
+               echo b3/b3-file3 >b3/b3-file3 &&
+               p4 add b3/b3-file3 &&
+               p4 submit -d "b3/b3-file3"
+       )
+'
+
+test_expect_success 'use-client-spec detect-branches skips branches' '
+       client_view "//depot/usecs/... //client/..." \
+                   "-//depot/usecs/b3/... //client/b3/..." &&
+       test_when_finished cleanup_git &&
+       test_create_repo "$git" &&
+       (
+               cd "$git" &&
+               git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
+               test_must_fail git rev-parse refs/remotes/p4/usecs/b3
+       )
+'
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index 7d993ef80acdf1775d70713d2b9abfb984c47c1e..281be29174b8b4fea854d72c663eaa2d46b00259 100755 (executable)
@@ -8,23 +8,6 @@ test_expect_success 'start p4d' '
        start_p4d
 '
 
-#
-# Construct a client with this list of View lines
-#
-client_view() {
-       (
-               cat <<-EOF &&
-               Client: client
-               Description: client
-               Root: $cli
-               View:
-               EOF
-               for arg ; do
-                       printf "\t$arg\n"
-               done
-       ) | p4 client -i
-}
-
 #
 # Verify these files exist, exactly.  Caller creates
 # a list of files in file "files".
index 80daaca7806cbe1a6de0ddeba40400c8a811328e..9bc57d27e961720c98f75f95ab3186723e77f88b 100644 (file)
@@ -221,9 +221,35 @@ write_script () {
 # capital letters by convention).
 
 test_set_prereq () {
-       satisfied="$satisfied$1 "
+       satisfied_prereq="$satisfied_prereq$1 "
+}
+satisfied_prereq=" "
+lazily_testable_prereq= lazily_tested_prereq=
+
+# Usage: test_lazy_prereq PREREQ 'script'
+test_lazy_prereq () {
+       lazily_testable_prereq="$lazily_testable_prereq$1 "
+       eval test_prereq_lazily_$1=\$2
+}
+
+test_run_lazy_prereq_ () {
+       script='
+mkdir -p "$TRASH_DIRECTORY/prereq-test-dir" &&
+(
+       cd "$TRASH_DIRECTORY/prereq-test-dir" &&'"$2"'
+)'
+       say >&3 "checking prerequisite: $1"
+       say >&3 "$script"
+       test_eval_ "$script"
+       eval_ret=$?
+       rm -rf "$TRASH_DIRECTORY/prereq-test-dir"
+       if test "$eval_ret" = 0; then
+               say >&3 "prerequisite $1 ok"
+       else
+               say >&3 "prerequisite $1 not satisfied"
+       fi
+       return $eval_ret
 }
-satisfied=" "
 
 test_have_prereq () {
        # prerequisites can be concatenated with ','
@@ -238,8 +264,24 @@ test_have_prereq () {
 
        for prerequisite
        do
+               case " $lazily_tested_prereq " in
+               *" $prerequisite "*)
+                       ;;
+               *)
+                       case " $lazily_testable_prereq " in
+                       *" $prerequisite "*)
+                               eval "script=\$test_prereq_lazily_$prerequisite" &&
+                               if test_run_lazy_prereq_ "$prerequisite" "$script"
+                               then
+                                       test_set_prereq $prerequisite
+                               fi
+                               lazily_tested_prereq="$lazily_tested_prereq$prerequisite "
+                       esac
+                       ;;
+               esac
+
                total_prereq=$(($total_prereq + 1))
-               case $satisfied in
+               case "$satisfied_prereq" in
                *" $prerequisite "*)
                        ok_prereq=$(($ok_prereq + 1))
                        ;;
@@ -530,6 +572,27 @@ test_cmp() {
        $GIT_TEST_CMP "$@"
 }
 
+# Print a sequence of numbers or letters in increasing order.  This is
+# similar to GNU seq(1), but the latter might not be available
+# everywhere (and does not do letters).  It may be used like:
+#
+#      for i in `test_seq 100`; do
+#              for j in `test_seq 10 20`; do
+#                      for k in `test_seq a z`; do
+#                              echo $i-$j-$k
+#                      done
+#              done
+#      done
+
+test_seq () {
+       case $# in
+       1)      set 1 "$@" ;;
+       2)      ;;
+       *)      error "bug in the test script: not 1 or 2 parameters to test_seq" ;;
+       esac
+       "$PERL_PATH" -le 'print for $ARGV[0]..$ARGV[1]' -- "$@"
+}
+
 # This function can be used to schedule some commands to be run
 # unconditionally at the end of the test to restore sanity:
 #
index bb4f8865b2628665f4adbf9ceef03d5a9081e244..78c428619e99ea0225f61a1fe257e444d464ad0b 100644 (file)
@@ -659,9 +659,29 @@ test_i18ngrep () {
        fi
 }
 
-# test whether the filesystem supports symbolic links
-ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
-rm -f y
+test_lazy_prereq SYMLINKS '
+       # test whether the filesystem supports symbolic links
+       ln -s x y && test -h y
+'
+
+test_lazy_prereq CASE_INSENSITIVE_FS '
+       echo good >CamelCase &&
+       echo bad >camelcase &&
+       test "$(cat CamelCase)" != good
+'
+
+test_lazy_prereq UTF8_NFD_TO_NFC '
+       # check whether FS converts nfd unicode to nfc
+       auml=$(printf "\303\244")
+       aumlcdiar=$(printf "\141\314\210")
+       >"$auml" &&
+       case "$(echo *)" in
+       "$aumlcdiar")
+               true ;;
+       *)
+               false ;;
+       esac
+'
 
 # When the tests are run as root, permission tests will report that
 # things are writable when they shouldn't be.
diff --git a/test-regex.c b/test-regex.c
new file mode 100644 (file)
index 0000000..b5bfd54
--- /dev/null
@@ -0,0 +1,20 @@
+#include <git-compat-util.h>
+
+int main(int argc, char **argv)
+{
+       char *pat = "[^={} \t]+";
+       char *str = "={}\nfred";
+       regex_t r;
+       regmatch_t m[1];
+
+       if (regcomp(&r, pat, REG_EXTENDED | REG_NEWLINE))
+               die("failed regcomp() for pattern '%s'", pat);
+       if (regexec(&r, str, 1, m, 0))
+               die("no match of pattern '%s' to string '%s'", pat, str);
+
+       /* http://sourceware.org/bugzilla/show_bug.cgi?id=3957  */
+       if (m[0].rm_so == 3) /* matches '\n' when it should not */
+               die("regex bug confirmed: re-build git with NO_REGEX=1");
+
+       exit(0);
+}
index 28ad6db9ffa854c3ef9185ba85108e95edc44d51..ba01563a0241041cb401cfb03495e6067847248e 100644 (file)
@@ -49,12 +49,12 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2,
        if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) {
                if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
                        opt->change(opt, mode1, mode2,
-                                   sha1, sha2, base->buf, 0, 0);
+                                   sha1, sha2, 1, 1, base->buf, 0, 0);
                }
                strbuf_addch(base, '/');
                diff_tree_sha1(sha1, sha2, base->buf, opt);
        } else {
-               opt->change(opt, mode1, mode2, sha1, sha2, base->buf, 0, 0);
+               opt->change(opt, mode1, mode2, sha1, sha2, 1, 1, base->buf, 0, 0);
        }
        strbuf_setlen(base, old_baselen);
        return 0;
@@ -100,7 +100,7 @@ static void show_entry(struct diff_options *opt, const char *prefix,
                        die("corrupt tree sha %s", sha1_to_hex(sha1));
 
                if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE))
-                       opt->add_remove(opt, *prefix, mode, sha1, base->buf, 0);
+                       opt->add_remove(opt, *prefix, mode, sha1, 1, base->buf, 0);
 
                strbuf_addch(base, '/');
 
@@ -108,7 +108,7 @@ static void show_entry(struct diff_options *opt, const char *prefix,
                show_tree(opt, prefix, &inner, base);
                free(tree);
        } else
-               opt->add_remove(opt, prefix[0], mode, sha1, base->buf, 0);
+               opt->add_remove(opt, prefix[0], mode, sha1, 1, base->buf, 0);
 
        strbuf_setlen(base, old_baselen);
 }
@@ -212,8 +212,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        diff_opts.rename_score = opt->rename_score;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
-       if (diff_setup_done(&diff_opts) < 0)
-               die("unable to set up diff options to follow renames");
+       diff_setup_done(&diff_opts);
        diff_tree(t1, t2, base, &diff_opts);
        diffcore_std(&diff_opts);
        diff_tree_release_paths(&diff_opts);
index bb08e2eb0dc52f68807a83b671fa61a2c2224f41..2e90ccb74fe45e6335ee6a76451da40d7d3f5a09 100644 (file)
@@ -11,6 +11,7 @@
 #include "list-objects.h"
 #include "run-command.h"
 #include "sigchain.h"
+#include "version.h"
 
 static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
 
@@ -734,9 +735,11 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
        }
 
        if (capabilities)
-               packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname_nons,
+               packet_write(1, "%s %s%c%s%s agent=%s\n",
+                            sha1_to_hex(sha1), refname_nons,
                             0, capabilities,
-                            stateless_rpc ? " no-done" : "");
+                            stateless_rpc ? " no-done" : "",
+                            git_user_agent_sanitized());
        else
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
        capabilities = NULL;
index f98d5a654d4a17a9d85157b33f161afe6951a6e8..6106a8098c8a46575f9afae5da9909b5c196f754 100644 (file)
--- a/version.c
+++ b/version.c
@@ -1,5 +1,6 @@
 #include "git-compat-util.h"
 #include "version.h"
+#include "strbuf.h"
 
 const char git_version_string[] = GIT_VERSION;
 
@@ -15,3 +16,23 @@ const char *git_user_agent(void)
 
        return agent;
 }
+
+const char *git_user_agent_sanitized(void)
+{
+       static const char *agent = NULL;
+
+       if (!agent) {
+               struct strbuf buf = STRBUF_INIT;
+               int i;
+
+               strbuf_addstr(&buf, git_user_agent());
+               strbuf_trim(&buf);
+               for (i = 0; i < buf.len; i++) {
+                       if (buf.buf[i] <= 32 || buf.buf[i] >= 127)
+                               buf.buf[i] = '.';
+               }
+               agent = buf.buf;
+       }
+
+       return agent;
+}
index fd9cdd6316a17d3dfdc0976e668d26f7854fb181..6911a4f1558de0c0d0b880a5b859925869a3e86e 100644 (file)
--- a/version.h
+++ b/version.h
@@ -4,5 +4,6 @@
 extern const char git_version_string[];
 
 const char *git_user_agent(void);
+const char *git_user_agent_sanitized(void);
 
 #endif /* VERSION_H */
index b5e33e49c77bdf1d19292971b63ff5221b013f33..68739aaa3b9e9e1a1bbbd43c75c9b5c244fb6c3e 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -403,6 +403,19 @@ int remove_or_warn(unsigned int mode, const char *file)
        return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
 }
 
+void warn_on_inaccessible(const char *path)
+{
+       warning(_("unable to access '%s': %s"), path, strerror(errno));
+}
+
+int access_or_warn(const char *path, int mode)
+{
+       int ret = access(path, mode);
+       if (ret && errno != ENOENT)
+               warn_on_inaccessible(path);
+       return ret;
+}
+
 struct passwd *xgetpwuid_self(void)
 {
        struct passwd *pw;