Merge branch 'cp/merge-continue'
authorJunio C Hamano <gitster@pobox.com>
Tue, 27 Dec 2016 08:11:41 +0000 (00:11 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 27 Dec 2016 08:11:41 +0000 (00:11 -0800)
"git merge --continue" has been added as a synonym to "git commit"
to conclude a merge that has stopped due to conflicts.

* cp/merge-continue:
merge: mark usage error strings for translation
merge: ensure '--abort' option takes no arguments
completion: add --continue option for merge
merge: add '--continue' option as a synonym for 'git commit'

172 files changed:
.gitignore
.travis.yml
Documentation/RelNotes/2.10.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.12.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/date-formats.txt
Documentation/git-branch.txt
Documentation/git-commit.txt
Documentation/git-for-each-ref.txt
Documentation/git-p4.txt
Documentation/git-rebase.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitcore-tutorial.txt
Documentation/pretty-formats.txt
Documentation/technical/api-submodule-config.txt
GIT-VERSION-GEN
Makefile
README.md
RelNotes
apply.c
bisect.c
branch.c
builtin/add.c
builtin/am.c
builtin/branch.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/fetch.c
builtin/for-each-ref.c
builtin/index-pack.c
builtin/merge.c
builtin/mv.c
builtin/pull.c
builtin/push.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/reset.c
builtin/rev-parse.c
builtin/rm.c
builtin/submodule--helper.c
builtin/tag.c
builtin/update-index.c
builtin/worktree.c
commit.c
commit.h
compat/mingw.h
compat/winansi.c
config.mak.uname
contrib/completion/git-completion.bash
contrib/update-unicode/.gitignore [new file with mode: 0644]
contrib/update-unicode/README [new file with mode: 0644]
contrib/update-unicode/update_unicode.sh [new file with mode: 0755]
convert.c
diff.c
git-add--interactive.perl
git-difftool.perl
git-mergetool--lib.sh
git-p4.py
git-rebase.sh
git-send-email.perl
git-stash.sh
http-walker.c
http.c
http.h
lockfile.c
lockfile.h
mailinfo.c
merge-recursive.c
merge.c
mergetools/araxis
mergetools/bc
mergetools/codecompare
mergetools/deltawalker
mergetools/diffmerge
mergetools/diffuse
mergetools/ecmerge
mergetools/emerge
mergetools/examdiff
mergetools/kdiff3
mergetools/kompare
mergetools/meld
mergetools/opendiff
mergetools/p4merge
mergetools/tkdiff
mergetools/tortoisemerge
mergetools/vimdiff
mergetools/winmerge
mergetools/xxdiff
parse-options.c
path.c
perl/Git.pm
perl/Git/I18N.pm
perl/Git/SVN/Ra.pm
pretty.c
read-cache.c
ref-filter.c
ref-filter.h
remote-curl.c
rerere.c
sequencer.c
sha1_file.c
shallow.c
submodule-config.c
submodule-config.h
submodule.c
submodule.h
t/helper/test-scrap-cache-tree.c
t/lib-httpd/apache.conf
t/t0021-conversion.sh
t/t0202/test.pl
t/t1308-config-set.sh
t/t2027-worktree-list.sh
t/t3030-merge-recursive.sh
t/t3203-branch-output.sh
t/t3407-rebase-abort.sh
t/t3426-rebase-submodule.sh
t/t3501-revert-cherry-pick.sh
t/t3510-cherry-pick-sequence.sh
t/t3511-cherry-pick-x.sh
t/t3600-rm.sh
t/t3903-stash.sh
t/t4013-diff-various.sh
t/t4013/diff.diff_--no-index_--raw_--abbrev=4_dir2_dir [new file with mode: 0644]
t/t4013/diff.diff_--no-index_--raw_--no-abbrev_dir2_dir [new file with mode: 0644]
t/t4013/diff.diff_--no-index_--raw_dir2_dir [new file with mode: 0644]
t/t4013/diff.diff_--raw_--abbrev=4_initial [new file with mode: 0644]
t/t4013/diff.diff_--raw_--no-abbrev_initial [new file with mode: 0644]
t/t4013/diff.diff_--raw_initial [new file with mode: 0644]
t/t4014-format-patch.sh
t/t4205-log-pretty-formats.sh
t/t5000-tar-tree.sh
t/t5300-pack-object.sh
t/t5520-pull.sh
t/t5528-push-default.sh
t/t5531-deep-submodule-push.sh
t/t5547-push-quarantine.sh
t/t5550-http-fetch-dumb.sh
t/t5551-http-fetch-smart.sh
t/t5615-alternate-env.sh
t/t5812-proto-disable-http.sh
t/t6101-rev-parse-parents.sh
t/t6300-for-each-ref.sh
t/t7004-tag.sh
t/t7408-submodule-reference.sh
t/t7411-submodule-config.sh
t/t7501-commit.sh
t/t7609-merge-co-error-msgs.sh
t/t7610-mergetool.sh
t/t7800-difftool.sh
t/t9100-git-svn-basic.sh
t/t9800-git-p4-basic.sh
t/t9806-git-p4-options.sh
t/t9807-git-p4-submit.sh
t/t9824-git-p4-git-lfs.sh
t/t9902-completion.sh
t/test-lib-functions.sh
tmp-objdir.c
trailer.c
trailer.h
transport.c
unicode_width.h
unpack-trees.c
update_unicode.sh [deleted file]
worktree.c
worktree.h
wt-status.c
xdiff/xutils.c
index 05cb58a3d4ef47295fa8ef02add44a0f0dd90d1f..6722f78f9ab7e9647a3358a52e627f1c9e83f685 100644 (file)
 /config.mak.autogen
 /config.mak.append
 /configure
-/unicode
 /tags
 /TAGS
 /cscope*
index 0b2ea5c3e2daa057cb88763f1943f6a250d019d9..3843967a692d1642e43f536d5e2652b566ca554d 100644 (file)
@@ -27,8 +27,8 @@ env:
     # The Linux build installs the defined dependency versions below.
     # The OS X build installs the latest available versions. Keep that
     # in mind when you encounter a broken OS X build!
-    - LINUX_P4_VERSION="16.1"
-    - LINUX_GIT_LFS_VERSION="1.2.0"
+    - LINUX_P4_VERSION="16.2"
+    - LINUX_GIT_LFS_VERSION="1.5.2"
     - DEFAULT_TEST_TARGET=prove
     - GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
     - GIT_TEST_OPTS="--verbose-log"
diff --git a/Documentation/RelNotes/2.10.3.txt b/Documentation/RelNotes/2.10.3.txt
new file mode 100644 (file)
index 0000000..277a2a1
--- /dev/null
@@ -0,0 +1,48 @@
+Git v2.10.3 Release Notes
+=========================
+
+Fixes since v2.10.2
+-------------------
+
+ * Extract a small helper out of the function that reads the authors
+   script file "git am" internally uses.
+   This by itself is not useful until a second caller appears in the
+   future for "rebase -i" helper.
+
+ * The command-line completion script (in contrib/) learned to
+   complete "git cmd ^mas<HT>" to complete the negative end of
+   reference to "git cmd ^master".
+
+ * "git send-email" attempts to pick up valid e-mails from the
+   trailers, but people in real world write non-addresses there, like
+   "Cc: Stable <add@re.ss> # 4.8+", which broke the output depending
+   on the availability and vintage of Mail::Address perl module.
+
+ * The code that we have used for the past 10+ years to cycle
+   4-element ring buffers turns out to be not quite portable in
+   theoretical world.
+
+ * "git daemon" used fixed-length buffers to turn URL to the
+   repository the client asked for into the server side directory
+   path, using snprintf() to avoid overflowing these buffers, but
+   allowed possibly truncated paths to the directory.  This has been
+   tightened to reject such a request that causes overlong path to be
+   required to serve.
+
+ * Recent update to git-sh-setup (a library of shell functions that
+   are used by our in-tree scripted Porcelain commands) included
+   another shell library git-sh-i18n without specifying where it is,
+   relying on the $PATH.  This has been fixed to be more explicit by
+   prefixing $(git --exec-path) output in front.
+
+ * Fix for a racy false-positive test failure.
+
+ * Portability update and workaround for builds on recent Mac OS X.
+
+ * Update to the test framework made in 2.9 timeframe broke running
+   the tests under valgrind, which has been fixed.
+
+ * Improve the rule to convert "unsigned char [20]" into "struct
+   object_id *" in contrib/coccinelle/
+
+Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.12.0.txt b/Documentation/RelNotes/2.12.0.txt
new file mode 100644 (file)
index 0000000..159a1bb
--- /dev/null
@@ -0,0 +1,213 @@
+Git 2.12 Release Notes
+======================
+
+Backward compatibility notes.
+
+ * Use of an empty string that is used for 'everything matches' is
+   still warned and Git asks users to use a more explicit '.' for that
+   instead.  The hope is that existing users will not mind this
+   change, and eventually the warning can be turned into a hard error,
+   upgrading the deprecation into removal of this (mis)feature.  That
+   is not scheduled to happen in the upcoming release (yet).
+
+ * The historical argument order "git merge <msg> HEAD <commit>..."
+   has been deprecated for quite some time, and will be removed in the
+   upcoming release.
+
+
+Updates since v2.11
+-------------------
+
+UI, Workflows & Features
+
+ * Various updates to "git p4".
+
+ * "git p4" didn't interact with the internal of .git directory
+   correctly in the modern "git-worktree"-enabled world.
+
+ * "git branch --list" and friends learned "--ignore-case" option to
+   optionally sort branches and tags case insensitively.
+
+ * In addition to %(subject), %(body), "log --pretty=format:..."
+   learned a new placeholder %(trailers).
+
+ * "git rebase" learned "--quit" option, which allows a user to
+   remove the metadata left by an earlier "git rebase" that was
+   manually aborted without using "git rebase --abort".
+
+ * "git clone --reference $there --recurse-submodules $super" has been
+   taught to guess repositories usable as references for submodules of
+   $super that are embedded in $there while making a clone of the
+   superproject borrow objects from $there; extend the mechanism to
+   also allow submodules of these submodules to borrow repositories
+   embedded in these clones of the submodules embedded in the clone of
+   the superproject.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Commands that operate on a log message and add lines to the trailer
+   blocks, such as "format-patch -s", "cherry-pick (-x|-s)", and
+   "commit -s", have been taught to use the logic of and share the
+   code with "git interpret-trailer".
+
+ * The default Travis-CI configuration specifies newer P4 and GitLFS.
+   (merge 5f703e8f02 ls/travis-update-p4-and-lfs later to maint).
+
+ * The "fast hash" that had disastrous performance issues in some
+   corner cases has been retired from the internal diff.
+
+ * The character width table has been updated to match Unicode 9.0
+   (merge 9e6e9aefdf bb/unicode-9.0 later to maint).
+
+ * Update the procedure to generate "tags" for developer support.
+   (merge 046e4c1c09 jk/make-tags-find-sources-tweak later to maint).
+
+
+
+Also contains various documentation updates and code clean-ups.
+
+Fixes since v2.10
+-----------------
+
+Unless otherwise noted, all the fixes since v2.9 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * We often decide if a session is interactive by checking if the
+   standard I/O streams are connected to a TTY, but isatty() that
+   comes with Windows incorrectly returned true if it is used on NUL
+   (i.e. an equivalent to /dev/null).  This has been fixed.
+   (merge cbb3f3c9b1 js/mingw-isatty later to maint).
+
+ * "git svn" did not work well with path components that are "0", and
+   some configuration variable it uses were not documented.
+   (merge ea9a93dcc2 ew/svn-fixes later to maint).
+
+ * "git rev-parse --symbolic" failed with a more recent notation like
+   "HEAD^-1" and "HEAD^!".
+   (merge a2e7b04c44 jk/rev-parse-symbolic-parents-fix later to maint).
+
+ * An empty directory in a working tree that can simply be nuked used
+   to interfere while merging or cherry-picking a change to create a
+   submodule directory there, which has been fixed..
+   (merge 5423d2e700 dt/empty-submodule-in-merge later to maint).
+
+ * The code in "git push" to compute if any commit being pushed in the
+   superproject binds a commit in a submodule that hasn't been pushed
+   out was overly inefficient, making it unusable even for a small
+   project that does not have any submodule but have a reasonable
+   number of refs.
+   (merge 250ab24ab3 hv/submodule-not-yet-pushed-fix later to maint).
+
+ * "git push --dry-run --recurse-submodule=on-demand" wasn't
+   "--dry-run" in the submodules.
+   (merge 0301c821c5 bw/push-dry-run later to maint).
+
+ * The output from "git worktree list" was made in readdir() order,
+   and was unstable.
+   (merge 4df1d4d466 nd/worktree-list-fixup later to maint).
+
+ * mergetool.<tool>.trustExitCode configuration variable did not apply
+   to built-in tools, but now it does.
+   (merge 2967284456 da/mergetool-trust-exit-code later to maint).
+
+ * "git p4" LFS support was broken when LFS stores an empty blob.
+   (merge d5eb3cf5e7 ls/p4-empty-file-on-lfs later to maint).
+
+ * A corner case in merge-recursive regression that crept in
+   during 2.10 development cycle has been fixed.
+   (merge 1c25d2d8ed jc/renormalize-merge-kill-safer-crlf later to maint).
+
+ * Transport with dumb http can be fooled into following foreign URLs
+   that the end user does not intend to, especially with the server
+   side redirects and http-alternates mechanism, which can lead to
+   security issues.  Tighten the redirection and make it more obvious
+   to the end user when it happens.
+   (merge cb4d2d35c4 jk/http-walker-limit-redirect-2.9 later to maint).
+
+ * Update the error messages from the dumb-http client when it fails
+   to obtain loose objects; we used to give sensible error message
+   only upon 404 but we now forbid unexpected redirects that needs to
+   be reported with something sensible.
+   (merge 3680f16f9d jk/http-walker-limit-redirect later to maint).
+
+ * When diff.renames configuration is on (and with Git 2.9 and later,
+   it is enabled by default, which made it worse), "git stash"
+   misbehaved if a file is removed and another file with a very
+   similar content is added.
+   (merge 9d4e28ead5 jk/stash-disable-renames-internally later to maint).
+
+ * "git diff --no-index" did not take "--no-abbrev" option.
+   (merge 43d1948b7b jb/diff-no-index-no-abbrev later to maint).
+
+ * "git difftool --dir-diff" had a minor regression when started from
+   a subdirectory, which has been fixed.
+   (merge 853e10c197 da/difftool-dir-diff-fix later to maint).
+
+ * "git commit --allow-empty --only" (no pathspec) with dirty index
+   ought to be an acceptable way to create a new commit that does not
+   change any paths, but it was forbidden, perhaps because nobody
+   needed it so far.
+   (merge beb635ca9c ak/commit-only-allow-empty later to maint).
+
+ * Git 2.11 had a minor regression in "merge --ff-only" that competed
+   with another process that simultanously attempted to update the
+   index. We used to explain what went wrong with an error message,
+   but the new code silently failed.  The error message has been
+   resurrected.
+
+ * A pathname that begins with "//" or "\\" on Windows is special but
+   path normalization logic was unaware of it.
+   (merge 7814fbe3f1 js/normalize-path-copy-ceil later to maint).
+
+ * "git pull --rebase", when there is no new commits on our side since
+   we forked from the upstream, should be able to fast-forward without
+   invoking "git rebase", but it didn't.
+   (merge 33b842a1e9 jc/pull-rebase-ff later to maint).
+
+ * The way to specify hotkeys to "xxdiff" that is used by "git
+   mergetool" has been modernized to match recent versions of xxdiff.
+   (merge 6cf5f6cef7 da/mergetool-xxdiff-hotkey later to maint).
+
+ * Unlike "git am --abort", "git cherry-pick --abort" moved HEAD back
+   to where cherry-pick started while picking multiple changes, when
+   the cherry-pick stopped to ask for help from the user, and the user
+   did "git reset --hard" to a different commit in order to re-attempt
+   the operation.
+   (merge ce73bb22d8 sb/sequencer-abort-safety later to maint).
+
+ * Code cleanup in shallow boundary computation.
+   (merge 649b0c316a nd/shallow-fixup later to maint).
+
+ * A recent update to receive-pack to make it easier to drop garbage
+   objects made it clear that GIT_ALTERNATE_OBJECT_DIRECTORIES cannot
+   have a pathname with a colon in it (no surprise!), and this in turn
+   made it impossible to push into a repository at such a path.  This
+   has been fixed by introducing a quoting mechanism used when
+   appending such a path to the colon-separated list.
+   (merge 5e74824fac jk/quote-env-path-list-component later to maint).
+
+ * The function usage_msg_opt() has been updated to say "fatal:"
+   before the custom message programs give, when they want to die
+   with a message about wrong command line options followed by the
+   standard usage string.
+   (merge 87433261a4 jk/parseopt-usage-msg-opt later to maint).
+
+ * "git index-pack --stdin" needs an access to an existing repository,
+   but "git index-pack file.pack" to generate an .idx file that
+   corresponds to a packfile does not.
+   (merge 29401e1575 jk/index-pack-wo-repo-from-stdin later to maint).
+
+ * Fix for NDEBUG builds.
+   (merge 08414938a2 jt/mailinfo-fold-in-body-headers later to maint).
+
+ * Other minor doc, test and build updates and code cleanups.
+   (merge fa6ca11105 nd/qsort-in-merge-recursive later to maint).
+   (merge fa3142c919 ak/lazy-prereq-mktemp later to maint).
+   (merge 9c48b4fb23 ls/t0021-fixup later to maint).
+   (merge 584f99c87b sb/unpack-trees-grammofix later to maint).
+   (merge 54471fdcc3 jk/readme-gmane-is-no-more later to maint).
+   (merge 9e189f1a5c sb/t3600-cleanup later to maint).
+   (merge e2c20be57c lr/doc-fix-cet later to maint).
+   (merge 47437fd3bd kh/tutorial-grammofix later to maint).
index a0ab66aae70db90bd18e14ec5bc5c95007b118f7..d51182a0606aa30168d640d2d821a3ec1bc2458d 100644 (file)
@@ -1891,6 +1891,16 @@ http.userAgent::
        of common USER_AGENT strings (but not including those like git/1.7.1).
        Can be overridden by the `GIT_HTTP_USER_AGENT` environment variable.
 
+http.followRedirects::
+       Whether git should follow HTTP redirects. If set to `true`, git
+       will transparently follow any redirect issued by a server it
+       encounters. If set to `false`, git will treat all redirects as
+       errors. If set to `initial`, git will follow redirects only for
+       the initial request to a remote, but not for subsequent
+       follow-up HTTP requests. Since git uses the redirected URL as
+       the base for the follow-up requests, this is generally
+       sufficient. The default is `initial`.
+
 http.<url>.*::
        Any of the http.* options above can be applied selectively to some URLs.
        For a config key to match a URL, each element of the config key is
index 35e8da201005dee34b75c87aebf720a8f7300a9c..6926e0a4c86a0c8396d9cca4942a81bbb8e7dc85 100644 (file)
@@ -11,7 +11,7 @@ Git internal format::
        It is `<unix timestamp> <time zone offset>`, where `<unix
        timestamp>` is the number of seconds since the UNIX epoch.
        `<time zone offset>` is a positive or negative offset from UTC.
-       For example CET (which is 2 hours ahead UTC) is `+0200`.
+       For example CET (which is 1 hour ahead of UTC) is `+0100`.
 
 RFC 2822::
        The standard email format as described by RFC 2822, for example
index 1fe73448f3f5a3ff48f939924156771cabfc1f77..5516a47b5490ff1e79ead61cd2229946cc1a7fe2 100644 (file)
@@ -118,6 +118,10 @@ OPTIONS
        default to color output.
        Same as `--color=never`.
 
+-i::
+--ignore-case::
+       Sorting and filtering branches are case insensitive.
+
 --column[=<options>]::
 --no-column::
        Display branch listing in columns. See configuration variable
index f2ab0ee2e7d1ff0f79c09cbd27e745f5f08d139d..4f8f20a3606201b1835affc523ff2828549d0285 100644 (file)
@@ -265,7 +265,8 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
        If this option is specified together with `--amend`, then
        no paths need to be specified, which can be used to amend
        the last commit without committing changes that have
-       already been staged.
+       already been staged. If used together with `--allow-empty`
+       paths are also not required, and an empty commit will be created.
 
 -u[<mode>]::
 --untracked-files[=<mode>]::
index f57e69bc83e33e3de3d02e339fd71066b93e7d4d..abe13f3bedaf55dbefc03d26b934351107ff50af 100644 (file)
@@ -79,6 +79,9 @@ OPTIONS
        Only list refs which contain the specified commit (HEAD if not
        specified).
 
+--ignore-case::
+       Sorting and filtering refs are case insensitive.
+
 FIELD NAMES
 -----------
 
@@ -165,6 +168,8 @@ of all lines of the commit message up to the first blank line.  The next
 line is 'contents:body', where body is all of the lines after the first
 blank line.  The optional GPG signature is `contents:signature`.  The
 first `N` lines of the message is obtained using `contents:lines=N`.
+Additionally, the trailers as interpreted by linkgit:git-interpret-trailers[1]
+are obtained as 'contents:trailers'.
 
 For sorting purposes, fields with numeric values sort in numeric order
 (`objectsize`, `authordate`, `committerdate`, `creatordate`, `taggerdate`).
index c83aaf39c33505ead72d523f278620fcc6f8f757..bae862ddcb9fe85765ea5bb126ced4fad8e98268 100644 (file)
@@ -303,6 +303,15 @@ These options can be used to modify 'git p4 submit' behavior.
        submit manually or revert.  This option always stops after the
        first (oldest) commit.  Git tags are not exported to p4.
 
+--shelve::
+       Instead of submitting create a series of shelved changelists.
+       After creating each shelve, the relevant files are reverted/deleted.
+       If you have multiple commits pending multiple shelves will be created.
+
+--update-shelve CHANGELIST::
+       Update an existing shelved changelist with this commit. Implies
+       --shelve.
+
 --conflict=(ask|skip|quit)::
        Conflicts can occur when applying a commit to p4.  When this
        happens, the default behavior ("ask") is to prompt whether to
@@ -467,6 +476,10 @@ git-p4.client::
        Client specified as an option to all p4 commands, with
        '-c <client>', including the client spec.
 
+git-p4.retries::
+       Specifies the number of times to retry a p4 command (notably,
+       'p4 sync') if the network times out. The default value is 3.
+
 Clone and sync variables
 ~~~~~~~~~~~~~~~~~~~~~~~~
 git-p4.syncFromOrigin::
index de222c81af98c96678841f258342fe2cc3c426a8..67d48e68831561303c4f39f46bf105371ed0d916 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
        [<upstream> [<branch>]]
 'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
        --root [<branch>]
-'git rebase' --continue | --skip | --abort | --edit-todo
+'git rebase' --continue | --skip | --abort | --quit | --edit-todo
 
 DESCRIPTION
 -----------
@@ -252,6 +252,11 @@ leave out at most one of A and B, in which case it defaults to HEAD.
        will be reset to where it was when the rebase operation was
        started.
 
+--quit::
+       Abort the rebase operation but HEAD is not reset back to the
+       original branch. The index and working tree are also left
+       unchanged as a result.
+
 --keep-empty::
        Keep the commits that do not change anything from its
        parents in the result.
index 5f9e65b0c4de117942babf0a14d255922d02b4ad..9bee9b0c4c53692bab569577d584797c0bd9e217 100644 (file)
@@ -664,13 +664,19 @@ creating the branch or tag.
        When retrieving svn commits into Git (as part of 'fetch', 'rebase', or
        'dcommit' operations), look for the first `From:` or `Signed-off-by:` line
        in the log message and use that as the author string.
++
+[verse]
+config key: svn.useLogAuthor
+
 --add-author-from::
        When committing to svn from Git (as part of 'commit-diff', 'set-tree' or 'dcommit'
        operations), if the existing log message doesn't already have a
        `From:` or `Signed-off-by:` line, append a `From:` line based on the
        Git commit's author string.  If you use this, then `--use-log-author`
        will retrieve a valid author string for all commits.
-
++
+[verse]
+config key: svn.addAuthorFrom
 
 ADVANCED OPTIONS
 ----------------
index 80019c584b11b35b2b14da83a31f326634bc6708..76cfe40d969bc5f37dce674f43857ec89f25ae67 100644 (file)
@@ -108,6 +108,10 @@ OPTIONS
        variable if it exists, or lexicographic order otherwise. See
        linkgit:git-config[1].
 
+-i::
+--ignore-case::
+       Sorting and filtering tags are case insensitive.
+
 --column[=<options>]::
 --no-column::
        Display tag listing in columns. See configuration variable
index af191c51b1d63d995cfba0784de36d98a23de838..98033302bb97ef802f7050e02a1727a878bb79aa 100644 (file)
@@ -871,6 +871,12 @@ Git so take care if using a foreign front-end.
        specifies a ":" separated (on Windows ";" separated) list
        of Git object directories which can be used to search for Git
        objects. New objects will not be written to these directories.
++
+       Entries that begin with `"` (double-quote) will be interpreted
+       as C-style quoted paths, removing leading and trailing
+       double-quotes and respecting backslash escapes. E.g., the value
+       `"path-with-\"-and-:-in-it":vanilla-path` has two paths:
+       `path-with-"-and-:-in-it` and `vanilla-path`.
 
 `GIT_DIR`::
        If the `GIT_DIR` environment variable is set then it
index 4546fa0d751705c27c88448ec3e7beb954233280..22309cfb48a6754d6bb4831cafc2981a8da7310b 100644 (file)
@@ -25,7 +25,7 @@ you want to understand Git's internals.
 The core Git is often called "plumbing", with the prettier user
 interfaces on top of it called "porcelain". You may not want to use the
 plumbing directly very often, but it can be good to know what the
-plumbing does for when the porcelain isn't flushing.
+plumbing does when the porcelain isn't flushing.
 
 Back when this document was originally written, many porcelain
 commands were shell scripts. For simplicity, it still uses them as
@@ -1368,7 +1368,7 @@ $ git repack
 will do it for you. If you followed the tutorial examples, you
 would have accumulated about 17 objects in `.git/objects/??/`
 directories by now. 'git repack' tells you how many objects it
-packed, and stores the packed file in `.git/objects/pack`
+packed, and stores the packed file in the `.git/objects/pack`
 directory.
 
 [NOTE]
@@ -1478,7 +1478,7 @@ You can repack this private repository whenever you feel like.
 A recommended work cycle for a "subsystem maintainer" who works
 on that project and has an own "public repository" goes like this:
 
-1. Prepare your work repository, by 'git clone' the public
+1. Prepare your work repository, by running 'git clone' on the public
    repository of the "project lead". The URL used for the
    initial cloning is stored in the remote.origin.url
    configuration variable.
@@ -1543,9 +1543,9 @@ like this:
 Working with Others, Shared Repository Style
 --------------------------------------------
 
-If you are coming from CVS background, the style of cooperation
+If you are coming from CVS background, the style of cooperation
 suggested in the previous section may be new to you. You do not
-have to worry. Git supports "shared public repository" style of
+have to worry. Git supports the "shared public repository" style of
 cooperation you are probably more familiar with as well.
 
 See linkgit:gitcvs-migration[7] for the details.
@@ -1635,7 +1635,7 @@ $ git show-branch
 ++* [master~2] Pretty-print messages.
 ------------
 
-Note that you should not do Octopus because you can.  An octopus
+Note that you should not do Octopus just because you can.  An octopus
 is a valid thing to do and often makes it easier to view the
 commit history if you are merging more than two independent
 changes at the same time.  However, if you have merge conflicts
index 3bcee2ddb1244c7ef52888d1de2654a688f6702f..47b286b33e4edd937fe6f7cbe727713e0e61e324 100644 (file)
@@ -199,6 +199,8 @@ endif::git-rev-list[]
   than given and there are spaces on its left, use those spaces
 - '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'
   respectively, but padding both sides (i.e. the text is centered)
+-%(trailers): display the trailers of the body as interpreted by
+  linkgit:git-interpret-trailers[1]
 
 NOTE: Some placeholders may depend on other options given to the
 revision traversal engine. For example, the `%g*` reflog options will
index 941fa178dd866162b4d3e8cb0c23a17f03016bb5..3dce003fda008e61c7a91c85554f9a1cafe6106f 100644 (file)
@@ -47,16 +47,20 @@ Functions
        Can be passed to the config parsing infrastructure to parse
        local (worktree) submodule configurations.
 
-`const struct submodule *submodule_from_path(const unsigned char *commit_sha1, const char *path)`::
+`const struct submodule *submodule_from_path(const unsigned char *treeish_name, const char *path)`::
 
-       Lookup values for one submodule by its commit_sha1 and path.
+       Given a tree-ish in the superproject and a path, return the
+       submodule that is bound at the path in the named tree.
 
-`const struct submodule *submodule_from_name(const unsigned char *commit_sha1, const char *name)`::
+`const struct submodule *submodule_from_name(const unsigned char *treeish_name, const char *name)`::
 
        The same as above but lookup by name.
 
-If given the null_sha1 as commit_sha1 the local configuration of a
-submodule will be returned (e.g. consolidated values from local git
+Whenever a submodule configuration is parsed in `parse_submodule_config_option`
+via e.g. `gitmodules_config()`, it will overwrite the null_sha1 entry.
+So in the normal case, when HEAD:.gitmodules is parsed first and then overlayed
+with the repository configuration, the null_sha1 entry contains the local
+configuration of a submodule (e.g. consolidated values from local git
 configuration and the .gitmodules file in the worktree).
 
 For an example usage see test-submodule-config.c.
index 520d6e66ec9f14ac1a2dedc84411398d8916ad15..f95b04bb365fb83c0366a7206ee700ba3f898f6c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.11.0
+DEF_VER=v2.11.GIT
 
 LF='
 '
@@ -12,7 +12,7 @@ if test -f version
 then
        VN=$(cat version) || VN="$DEF_VER"
 elif test -d ${GIT_DIR:-.git} -o -f .git &&
-       VN=$(git describe --match "v[0-9]*" --abbrev=7 HEAD 2>/dev/null) &&
+       VN=$(git describe --match "v[0-9]*" HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
        v[0-9]*)
index f53fcc90d71b7e0ba95b94e9baf67d559085863c..d861bd9985dd41c24d5ff08a985c9d280d9dc07c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -338,11 +338,6 @@ all::
 #
 # Define NATIVE_CRLF if your platform uses CRLF for line endings.
 #
-# Define XDL_FAST_HASH to use an alternative line-hashing method in
-# the diff algorithm.  It gives a nice speedup if your processor has
-# fast unaligned word loads.  Does NOT work on big-endian systems!
-# Enabled by default on x86_64.
-#
 # Define GIT_USER_AGENT if you want to change how git identifies itself during
 # network interactions.  The default is "git/$(GIT_VERSION)".
 #
@@ -1485,10 +1480,6 @@ ifndef NO_MSGFMT_EXTENDED_OPTIONS
        MSGFMT += --check --statistics
 endif
 
-ifneq (,$(XDL_FAST_HASH))
-       BASIC_CFLAGS += -DXDL_FAST_HASH
-endif
-
 ifdef GMTIME_UNRELIABLE_ERRORS
        COMPAT_OBJS += compat/gmtime.o
        BASIC_CFLAGS += -DGMTIME_UNRELIABLE_ERRORS
@@ -2114,7 +2105,8 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
        --keyword=_ --keyword=N_ --keyword="Q_:1,2"
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \
        --keyword=gettextln --keyword=eval_gettextln
-XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
+XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --language=Perl \
+       --keyword=__ --keyword=N__ --keyword="__n:1,2"
 LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH = $(SCRIPT_SH)
 LOCALIZED_SH += git-parse-remote.sh
@@ -2149,9 +2141,22 @@ endif
 po/build/locale/%/LC_MESSAGES/git.mo: po/%.po
        $(QUIET_MSGFMT)mkdir -p $(dir $@) && $(MSGFMT) -o $@ $<
 
-FIND_SOURCE_FILES = ( git ls-files '*.[hcS]' 2>/dev/null || \
-                       $(FIND) . \( -name .git -type d -prune \) \
-                               -o \( -name '*.[hcS]' -type f -print \) )
+FIND_SOURCE_FILES = ( \
+       git ls-files \
+               '*.[hcS]' \
+               '*.sh' \
+               ':!*[tp][0-9][0-9][0-9][0-9]*' \
+               ':!contrib' \
+               2>/dev/null || \
+       $(FIND) . \
+               \( -name .git -type d -prune \) \
+               -o \( -name '[tp][0-9][0-9][0-9][0-9]*' -prune \) \
+               -o \( -name contrib -type d -prune \) \
+               -o \( -name build -type d -prune \) \
+               -o \( -name 'trash*' -type d -prune \) \
+               -o \( -name '*.[hcS]' -type f -print \) \
+               -o \( -name '*.sh' -type f -print \) \
+       )
 
 $(ETAGS_TARGET): FORCE
        $(RM) $(ETAGS_TARGET)
index bd8a918a9bcbd46590078d98a66f3d7e939df3fb..c0cd5580ea48cf4818781bb8d4d7c2d9d5d63ae6 100644 (file)
--- a/README.md
+++ b/README.md
@@ -33,7 +33,7 @@ requests, comments and patches to git@vger.kernel.org (read
 [Documentation/SubmittingPatches][] for instructions on patch submission).
 To subscribe to the list, send an email with just "subscribe git" in
 the body to majordomo@vger.kernel.org. The mailing list archives are
-available at http://news.gmane.org/gmane.comp.version-control.git/,
+available at https://public-inbox.org/git,
 http://marc.info/?l=git and other archival sites.
 
 The maintainer frequently sends the "What's cooking" reports that
index b54330f7cdb37630dc47d6bdb71e847b8a947c17..d09c3d51093ac9e4da65e8a127b17ac9023520b5 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.11.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.12.0.txt
\ No newline at end of file
diff --git a/apply.c b/apply.c
index 705cf562f07098aafcd9f6e27105d2105714751f..2ed808d429969ff9516ba1bf166c3fdfa6d63ed5 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -4688,7 +4688,7 @@ static int apply_patch(struct apply_state *state,
                                                                 state->index_file,
                                                                 LOCK_DIE_ON_ERROR);
                else
-                       state->newfd = hold_locked_index(state->lock_file, 1);
+                       state->newfd = hold_locked_index(state->lock_file, LOCK_DIE_ON_ERROR);
        }
 
        if (state->check_index && read_apply_cache(state) < 0) {
index 21bc6daa4393cb33994ba2eb305354e56d166cc0..8e63c40d274d7693b0c41e702cbe9ed03b1d12ae 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -747,7 +747,7 @@ static void handle_bad_merge_base(void)
                exit(3);
        }
 
-       fprintf(stderr, _("Some %s revs are not ancestor of the %s rev.\n"
+       fprintf(stderr, _("Some %s revs are not ancestors of the %s rev.\n"
                "git bisect cannot work properly in this case.\n"
                "Maybe you mistook %s and %s revs?\n"),
                term_good, term_bad, term_good, term_bad);
index 0d459b3cfe507b3906760fbea7f35e6191366057..c431cbf6a9f08dedc791317cc1a357730bdf3515 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -348,7 +348,7 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree)
 int replace_each_worktree_head_symref(const char *oldref, const char *newref)
 {
        int ret = 0;
-       struct worktree **worktrees = get_worktrees();
+       struct worktree **worktrees = get_worktrees(0);
        int i;
 
        for (i = 0; worktrees[i]; i++) {
index e8fb80b36e7386fa9a9da91a61893c14ec696be4..9f53f020d0fc7184ac803a4346bdf426a814ed72 100644 (file)
@@ -361,7 +361,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        add_new_files = !take_worktree_changes && !refresh_only;
        require_pathspec = !(take_worktree_changes || (0 < addremove_explicit));
 
-       hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
                 (show_only ? ADD_CACHE_PRETEND : 0) |
index 6981f42ce986dc5a9fa76bed8364cee7c6c642aa..31fb60578f6caacfb376fe84938dda9432dcfc5a 100644 (file)
@@ -1119,7 +1119,7 @@ static void refresh_and_write_cache(void)
 {
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
        if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write index file"));
@@ -1976,7 +1976,7 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
                return -1;
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 
        refresh_cache(REFRESH_QUIET);
 
@@ -2016,7 +2016,7 @@ static int merge_tree(struct tree *tree)
                return -1;
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof(opts));
        opts.head_idx = 1;
@@ -2124,7 +2124,7 @@ static int safe_to_abort(const struct am_state *state)
 
        if (read_state_file(&sb, state, "abort-safety", 1) > 0) {
                if (get_oid_hex(sb.buf, &abort_safety))
-                       die(_("could not parse %s"), am_path(state, "abort_safety"));
+                       die(_("could not parse %s"), am_path(state, "abort-safety"));
        } else
                oidclr(&abort_safety);
 
@@ -2134,7 +2134,7 @@ static int safe_to_abort(const struct am_state *state)
        if (!oidcmp(&head, &abort_safety))
                return 1;
 
-       error(_("You seem to have moved HEAD since the last 'am' failure.\n"
+       warning(_("You seem to have moved HEAD since the last 'am' failure.\n"
                "Not rewinding to ORIG_HEAD"));
 
        return 0;
index 60cc5c8e8da08e628d05f245003bc558657f6237..9d30f55b0b83cfc09294e2cb7cabdc9aa72e2508 100644 (file)
@@ -512,15 +512,6 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
        if (filter->verbose)
                maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
 
-       /*
-        * If no sorting parameter is given then we default to sorting
-        * by 'refname'. This would give us an alphabetically sorted
-        * array with the 'HEAD' ref at the beginning followed by
-        * local branches 'refs/heads/...' and finally remote-tacking
-        * branches 'refs/remotes/...'.
-        */
-       if (!sorting)
-               sorting = ref_default_sorting();
        ref_array_sort(sorting, &array);
 
        for (i = 0; i < array.nr; i++)
@@ -531,7 +522,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 
 static void reject_rebase_or_bisect_branch(const char *target)
 {
-       struct worktree **worktrees = get_worktrees();
+       struct worktree **worktrees = get_worktrees(0);
        int i;
 
        for (i = 0; worktrees[i]; i++) {
@@ -645,6 +636,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        const char *new_upstream = NULL;
        enum branch_track track;
        struct ref_filter filter;
+       int icase = 0;
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
 
        struct option options[] = {
@@ -686,6 +678,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
                        N_("print only branches of the object"), 0, parse_opt_object_name
                },
+               OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
                OPT_END(),
        };
 
@@ -723,6 +716,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        if (filter.abbrev == -1)
                filter.abbrev = DEFAULT_ABBREV;
+       filter.ignore_case = icase;
+
        finalize_colopts(&colopts, -1);
        if (filter.verbose) {
                if (explicitly_enable_column(colopts))
@@ -744,6 +739,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
                        filter.kind |= FILTER_REFS_DETACHED_HEAD;
                filter.name_patterns = argv;
+               /*
+                * If no sorting parameter is given then we default to sorting
+                * by 'refname'. This would give us an alphabetically sorted
+                * array with the 'HEAD' ref at the beginning followed by
+                * local branches 'refs/heads/...' and finally remote-tacking
+                * branches 'refs/remotes/...'.
+                */
+               if (!sorting)
+                       sorting = ref_default_sorting();
+               sorting->ignore_case = icase;
                print_ref_list(&filter, sorting);
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
index 30a49d9f424c0a1e04b321b5eb0a5e25462a53af..07631d0c9c59f6ba03f288294797e08cdfe22b7c 100644 (file)
@@ -205,7 +205,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        if (index_opt && !state.base_dir_len && !to_tempfile) {
                state.refresh_cache = 1;
                state.istate = &the_index;
-               newfd = hold_locked_index(&lock_file, 1);
+               newfd = hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        }
 
        /* Check out named files first */
index 512492aad9099dd2a7aa85391b5c9a8e321c3bfe..bfe685c198cd38b59ffe66fa6b3d3a7bced60824 100644 (file)
@@ -274,7 +274,7 @@ static int checkout_paths(const struct checkout_opts *opts,
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("index file corrupt"));
 
@@ -467,7 +467,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
        int ret;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(NULL) < 0)
                return error(_("index file corrupt"));
 
index 0371010afbad54283ceca9883f2a9fbe6da4686b..d6bc3aaaea0dfdbac2386d54f4ff2d25dbccf7e6 100644 (file)
@@ -287,11 +287,11 @@ static void pretty_print_menus(struct string_list *menu_list)
 static void prompt_help_cmd(int singleton)
 {
        clean_print_color(CLEAN_COLOR_HELP);
-       printf_ln(singleton ?
+       printf(singleton ?
                  _("Prompt help:\n"
                    "1          - select a numbered item\n"
                    "foo        - select item based on unique prefix\n"
-                   "           - (empty) select nothing") :
+                   "           - (empty) select nothing\n") :
                  _("Prompt help:\n"
                    "1          - select a single item\n"
                    "3-5        - select a range of items\n"
@@ -299,7 +299,7 @@ static void prompt_help_cmd(int singleton)
                    "foo        - select item based on unique prefix\n"
                    "-...       - unselect specified items\n"
                    "*          - choose all items\n"
-                   "           - (empty) finish selecting"));
+                   "           - (empty) finish selecting\n"));
        clean_print_color(CLEAN_COLOR_RESET);
 }
 
@@ -508,7 +508,7 @@ static int parse_choice(struct menu_stuff *menu_stuff,
                if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
                    (is_single && bottom != top)) {
                        clean_print_color(CLEAN_COLOR_ERROR);
-                       printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+                       printf(_("Huh (%s)?\n"), (*ptr)->buf);
                        clean_print_color(CLEAN_COLOR_RESET);
                        continue;
                }
@@ -774,7 +774,7 @@ static int ask_each_cmd(void)
 static int quit_cmd(void)
 {
        string_list_clear(&del_list, 0);
-       printf_ln(_("Bye."));
+       printf(_("Bye.\n"));
        return MENU_RETURN_NO_LOOP;
 }
 
index 6c76a6ed66fef567ca06e3e864b37fc3bed151d5..5ef81927a629985aa4f0de6acba326f79747e7aa 100644 (file)
@@ -99,7 +99,7 @@ static struct option builtin_clone_options[] = {
        OPT_STRING(0, "shallow-since", &option_since, N_("time"),
                    N_("create a shallow clone since a specific time")),
        OPT_STRING_LIST(0, "shallow-exclude", &option_not, N_("revision"),
-                       N_("deepen history of shallow clone by excluding rev")),
+                       N_("deepen history of shallow clone, excluding rev")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
                    N_("clone only one branch, HEAD or --branch")),
        OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
@@ -711,7 +711,7 @@ static int checkout(int submodule_progress)
        setup_work_tree();
 
        lock_file = xcalloc(1, sizeof(struct lock_file));
-       hold_locked_index(lock_file, 1);
+       hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
index 8976c3d29bf817be789f983697f7052fe5769f39..711f96cc438c0125290a587a0187702121bc461d 100644 (file)
@@ -351,7 +351,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 
        if (interactive) {
                char *old_index_env = NULL;
-               hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
 
                refresh_cache_or_die(refresh_flags);
 
@@ -396,7 +396,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
         * (B) on failure, rollback the real index.
         */
        if (all || (also && pathspec.nr)) {
-               hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
                add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
                refresh_cache_or_die(refresh_flags);
                update_main_cache_tree(WRITE_TREE_SILENT);
@@ -416,7 +416,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
         * We still need to refresh the index here.
         */
        if (!only && !pathspec.nr) {
-               hold_locked_index(&index_lock, 1);
+               hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
                refresh_cache_or_die(refresh_flags);
                if (active_cache_changed
                    || !cache_tree_fully_valid(active_cache_tree))
@@ -468,7 +468,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
        if (read_cache() < 0)
                die(_("cannot read the index"));
 
-       hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
        update_main_cache_tree(WRITE_TREE_SILENT);
@@ -790,7 +790,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                strbuf_stripspace(&sb, 0);
 
        if (signoff)
-               append_signoff(&sb, ignore_non_trailer(&sb), 0);
+               append_signoff(&sb, ignore_non_trailer(sb.buf, sb.len), 0);
 
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
                die_errno(_("could not write commit template"));
@@ -1206,10 +1206,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
 
        if (also + only + all + interactive > 1)
                die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
-       if (argc == 0 && (also || (only && !amend)))
+       if (argc == 0 && (also || (only && !amend && !allow_empty)))
                die(_("No paths with --include/--only does not make sense."));
-       if (argc == 0 && only && amend)
-               only_include_assumed = _("Clever... amending the last one with dirty index.");
        if (argc > 0 && !also && !only)
                only_include_assumed = _("Explicit paths specified without -i or -o; assuming --only paths...");
        if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
index b6a5597cbf332fc56b5feabfd29d03c98d4d6686..fc74c8471cec5d1d1855c7d4362e465e44259e6c 100644 (file)
@@ -122,7 +122,7 @@ static struct option builtin_fetch_options[] = {
        OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
                   N_("deepen history of shallow repository based on time")),
        OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
-                       N_("deepen history of shallow clone by excluding rev")),
+                       N_("deepen history of shallow clone, excluding rev")),
        OPT_INTEGER(0, "deepen", &deepen_relative,
                    N_("deepen history of shallow clone")),
        { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
index 4e9f6c29bf1e0c1cc7b44548f49077c2e4a81ec8..df41fa035004e1cf8a0c7c9ff09081a47b944f38 100644 (file)
@@ -18,7 +18,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        int i;
        const char *format = "%(objectname) %(objecttype)\t%(refname)";
        struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
-       int maxcount = 0, quote_style = 0;
+       int maxcount = 0, quote_style = 0, icase = 0;
        struct ref_array array;
        struct ref_filter filter;
 
@@ -43,6 +43,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_MERGED(&filter, N_("print only refs that are merged")),
                OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
                OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
+               OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
                OPT_END(),
        };
 
@@ -63,6 +64,8 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 
        if (!sorting)
                sorting = ref_default_sorting();
+       sorting->ignore_case = icase;
+       filter.ignore_case = icase;
 
        /* for warn_ambiguous_refs */
        git_config(git_default_config, NULL);
index 0a27bab11b6b5a1b162c4dfef34c645c7d5f14ad..f4b87c6c9f901e5834ce9e50db089fb487c6eafc 100644 (file)
@@ -787,13 +787,15 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                        const unsigned char *sha1)
 {
        void *new_data = NULL;
-       int collision_test_needed;
+       int collision_test_needed = 0;
 
        assert(data || obj_entry);
 
-       read_lock();
-       collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK);
-       read_unlock();
+       if (startup_info->have_repository) {
+               read_lock();
+               collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK);
+               read_unlock();
+       }
 
        if (collision_test_needed && !data) {
                read_lock();
@@ -1730,6 +1732,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                usage(index_pack_usage);
        if (fix_thin_pack && !from_stdin)
                die(_("--fix-thin cannot be used without --stdin"));
+       if (from_stdin && !startup_info->have_repository)
+               die(_("--stdin requires a git repository"));
        if (!index_name && pack_name)
                index_name = derive_filename(pack_name, ".idx", &index_name_buf);
        if (keep_msg && !keep_name && pack_name)
index 599d25c4ccb71c9d0c2b246d5c8c35b88dc73a5e..a96d4fb501bf1441b52a313313b8c04f3187e4d9 100644 (file)
@@ -638,7 +638,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 {
        static struct lock_file lock;
 
-       hold_locked_index(&lock, 1);
+       hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
            write_locked_index(&the_index, &lock, COMMIT_LOCK))
@@ -675,7 +675,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
 
-               hold_locked_index(&lock, 1);
+               hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
                if (clean < 0)
@@ -785,7 +785,7 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
        struct commit_list *parents, **pptr = &parents;
        static struct lock_file lock;
 
-       hold_locked_index(&lock, 1);
+       hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
            write_locked_index(&the_index, &lock, COMMIT_LOCK))
index 2f43877bc9a17c5bef2906a383cc8cdd6f4f5b82..43adf92ba64426a2f312cd6133276a98b20855f1 100644 (file)
@@ -126,7 +126,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        if (--argc < 1)
                usage_with_options(builtin_mv_usage, builtin_mv_options);
 
-       hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache() < 0)
                die(_("index file corrupt"));
 
index d6e46ee6d0054c152d721e92e354795c6f6002f8..3ecb881b0bcacbf1a453bf9a6cb95ae00cdecb1d 100644 (file)
@@ -857,10 +857,24 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
                if (merge_heads.nr > 1)
                        die(_("Cannot merge multiple branches into empty head."));
                return pull_into_void(*merge_heads.sha1, curr_head);
-       } else if (opt_rebase) {
-               if (merge_heads.nr > 1)
-                       die(_("Cannot rebase onto multiple branches."));
+       }
+       if (opt_rebase && merge_heads.nr > 1)
+               die(_("Cannot rebase onto multiple branches."));
+
+       if (opt_rebase) {
+               struct commit_list *list = NULL;
+               struct commit *merge_head, *head;
+
+               head = lookup_commit_reference(orig_head);
+               commit_list_insert(head, &list);
+               merge_head = lookup_commit_reference(merge_heads.sha1[0]);
+               if (is_descendant_of(merge_head, list)) {
+                       /* we can fast-forward this without invoking rebase */
+                       opt_ff = "--ff-only";
+                       return run_merge();
+               }
                return run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point);
-       } else
+       } else {
                return run_merge();
+       }
 }
index 3bb9d6b7e63b3e3082023c3d333c11757df6fbda..9307ad56a9fb91455eaf06371382b1431d115598 100644 (file)
@@ -194,15 +194,18 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,
                        die_push_simple(branch, remote);
        }
 
-       strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
+       strbuf_addf(&refspec, "%s:%s", branch->refname, branch->merge[0]->src);
        add_refspec(refspec.buf);
 }
 
 static void setup_push_current(struct remote *remote, struct branch *branch)
 {
+       struct strbuf refspec = STRBUF_INIT;
+
        if (!branch)
                die(_(message_detached_head_die), remote->name);
-       add_refspec(branch->name);
+       strbuf_addf(&refspec, "%s:%s", branch->refname, branch->refname);
+       add_refspec(refspec.buf);
 }
 
 static int is_workflow_triangular(struct remote *remote)
index 9bd1fd755ef03824442f6c751a9603b95bfe66ee..fa6edb35b21cede4425746d84b41c8bd9e31e1dc 100644 (file)
@@ -150,7 +150,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        argc = parse_options(argc, argv, unused_prefix, read_tree_options,
                             read_tree_usage, 0);
 
-       hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        prefix_set = opts.prefix ? 1 : 0;
        if (1 < opts.merge + opts.reset + prefix_set)
index e6b3879a5b90034937dfb8eee32f8d2981f6bb53..6b97cbdbe9444d72d575c149678e4b609a154842 100644 (file)
@@ -795,8 +795,8 @@ static char *refuse_unconfigured_deny_msg =
           "with what you pushed, and will require 'git reset --hard' to match\n"
           "the work tree to HEAD.\n"
           "\n"
-          "You can set 'receive.denyCurrentBranch' configuration variable to\n"
-          "'ignore' or 'warn' in the remote repository to allow pushing into\n"
+          "You can set the 'receive.denyCurrentBranch' configuration variable\n"
+          "to 'ignore' or 'warn' in the remote repository to allow pushing into\n"
           "its current branch; however, this is not recommended unless you\n"
           "arranged to update its work tree to match what you pushed in some\n"
           "other way.\n"
index c04ac076dc53b99039768dcdcbf2861e285896c2..8ab915bfcb71ae5d5f4bd1bebe7fd3c8725780b2 100644 (file)
@@ -354,7 +354,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 
        if (reset_type != SOFT) {
                struct lock_file *lock = xcalloc(1, sizeof(*lock));
-               hold_locked_index(lock, 1);
+               hold_locked_index(lock, LOCK_DIE_ON_ERROR);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
                        if (read_from_tree(&pathspec, &oid, intent_to_add))
index cfb0f1510c59674abe68fb67ff45142b5c92ac89..ff13e59e1dbd200b6e7cf7d1c508b620b19e8cc7 100644 (file)
@@ -342,11 +342,16 @@ static int try_parent_shorthands(const char *arg)
        for (parents = commit->parents, parent_number = 1;
             parents;
             parents = parents->next, parent_number++) {
+               char *name = NULL;
+
                if (exclude_parent && parent_number != exclude_parent)
                        continue;
 
+               if (symbolic)
+                       name = xstrfmt("%s^%d", arg, parent_number);
                show_rev(include_parents ? NORMAL : REVERSED,
-                        parents->item->object.oid.hash, arg);
+                        parents->item->object.oid.hash, name);
+               free(name);
        }
 
        *dotdot = '^';
index 3f3e24eb36af03481f4e7b3f4d22d8e4b5904593..7f15a3d7f82a7b610dcb8c6c9f713b485614e2f7 100644 (file)
@@ -292,7 +292,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
        if (!index_only)
                setup_work_tree();
 
-       hold_locked_index(&lock_file, 1);
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        if (read_cache() < 0)
                die(_("index file corrupt"));
index 4beeda5f9f49d6c2e2f552308b50bacf70ebd421..92fd676a2e3c5d4866eb5bed0d41503fc5796dfd 100644 (file)
@@ -498,9 +498,9 @@ static int add_possible_reference_from_superproject(
 
        /*
         * If the alternate object store is another repository, try the
-        * standard layout with .git/modules/<name>/objects
+        * standard layout with .git/(modules/<name>)+/objects
         */
-       if (ends_with(alt->path, ".git/objects")) {
+       if (ends_with(alt->path, "/objects")) {
                char *sm_alternate;
                struct strbuf sb = STRBUF_INIT;
                struct strbuf err = STRBUF_INIT;
@@ -583,6 +583,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
        struct strbuf rel_path = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
        struct string_list reference = STRING_LIST_INIT_NODUP;
+       char *sm_alternate = NULL, *error_strategy = NULL;
 
        struct option module_clone_options[] = {
                OPT_STRING(0, "prefix", &prefix,
@@ -672,6 +673,20 @@ static int module_clone(int argc, const char **argv, const char *prefix)
                die(_("could not get submodule directory for '%s'"), path);
        git_config_set_in_file(p, "core.worktree",
                               relative_path(path, sm_gitdir, &rel_path));
+
+       /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
+       git_config_get_string("submodule.alternateLocation", &sm_alternate);
+       if (sm_alternate)
+               git_config_set_in_file(p, "submodule.alternateLocation",
+                                          sm_alternate);
+       git_config_get_string("submodule.alternateErrorStrategy", &error_strategy);
+       if (error_strategy)
+               git_config_set_in_file(p, "submodule.alternateErrorStrategy",
+                                          error_strategy);
+
+       free(sm_alternate);
+       free(error_strategy);
+
        strbuf_release(&sb);
        strbuf_release(&rel_path);
        free(sm_gitdir);
index 50e4ae5678c21f348c3ce0e0d0662c9d5f995847..73df728114e81ac87dd1b84604947d739d948fb6 100644 (file)
@@ -335,6 +335,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        struct ref_filter filter;
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
        const char *format = NULL;
+       int icase = 0;
        struct option options[] = {
                OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
                { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
@@ -370,6 +371,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        N_("print only tags of the object"), 0, parse_opt_object_name
                },
                OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
+               OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
                OPT_END()
        };
 
@@ -401,6 +403,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        }
        if (!sorting)
                sorting = ref_default_sorting();
+       sorting->ignore_case = icase;
+       filter.ignore_case = icase;
        if (cmdmode == 'l') {
                int ret;
                if (column_active(colopts)) {
index f3f07e7f1cb2d952144bf98969b481dc331155bf..d530e89368b42bf1465784b80163b9158a06585a 100644 (file)
@@ -1012,6 +1012,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        /* We can't free this memory, it becomes part of a linked list parsed atexit() */
        lock_file = xcalloc(1, sizeof(struct lock_file));
 
+       /* we will diagnose later if it turns out that we need to update it */
        newfd = hold_locked_index(lock_file, 0);
        if (newfd < 0)
                lock_error = errno;
index 5c4854d3e4a679f59f4a7db7cb3f37bd0e210622..9a97e37a3fa53e730f0963f8f00bbca9f6d6efc6 100644 (file)
@@ -388,7 +388,7 @@ static void show_worktree_porcelain(struct worktree *wt)
                printf("HEAD %s\n", sha1_to_hex(wt->head_sha1));
                if (wt->is_detached)
                        printf("detached\n");
-               else
+               else if (wt->head_ref)
                        printf("branch %s\n", wt->head_ref);
        }
        printf("\n");
@@ -406,10 +406,12 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
        else {
                strbuf_addf(&sb, "%-*s ", abbrev_len,
                                find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
-               if (!wt->is_detached)
+               if (wt->is_detached)
+                       strbuf_addstr(&sb, "(detached HEAD)");
+               else if (wt->head_ref)
                        strbuf_addf(&sb, "[%s]", shorten_unambiguous_ref(wt->head_ref, 0));
                else
-                       strbuf_addstr(&sb, "(detached HEAD)");
+                       strbuf_addstr(&sb, "(error)");
        }
        printf("%s\n", sb.buf);
 
@@ -445,7 +447,7 @@ static int list(int ac, const char **av, const char *prefix)
        if (ac)
                usage_with_options(worktree_usage, options);
        else {
-               struct worktree **worktrees = get_worktrees();
+               struct worktree **worktrees = get_worktrees(GWT_SORT_LINKED);
                int path_maxlen = 0, abbrev = DEFAULT_ABBREV, i;
 
                if (!porcelain)
@@ -476,7 +478,7 @@ static int lock_worktree(int ac, const char **av, const char *prefix)
        if (ac != 1)
                usage_with_options(worktree_usage, options);
 
-       worktrees = get_worktrees();
+       worktrees = get_worktrees(0);
        wt = find_worktree(worktrees, prefix, av[0]);
        if (!wt)
                die(_("'%s' is not a working tree"), av[0]);
@@ -509,7 +511,7 @@ static int unlock_worktree(int ac, const char **av, const char *prefix)
        if (ac != 1)
                usage_with_options(worktree_usage, options);
 
-       worktrees = get_worktrees();
+       worktrees = get_worktrees(0);
        wt = find_worktree(worktrees, prefix, av[0]);
        if (!wt)
                die(_("'%s' is not a working tree"), av[0]);
index 856fd4aeeff654c45ba0a6149f9db2c22806e0cb..2cf85158b4899b664a3cbae8d0777f5a9e473318 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1649,7 +1649,7 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
 }
 
 /*
- * Inspect sb and determine the true "end" of the log message, in
+ * Inspect the given string and determine the true "end" of the log message, in
  * order to find where to put a new Signed-off-by: line.  Ignored are
  * trailing comment lines and blank lines, and also the traditional
  * "Conflicts:" block that is not commented out, so that we can use
@@ -1659,37 +1659,37 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
  * Returns the number of bytes from the tail to ignore, to be fed as
  * the second parameter to append_signoff().
  */
-int ignore_non_trailer(struct strbuf *sb)
+int ignore_non_trailer(const char *buf, size_t len)
 {
        int boc = 0;
        int bol = 0;
        int in_old_conflicts_block = 0;
 
-       while (bol < sb->len) {
-               char *next_line;
+       while (bol < len) {
+               const char *next_line = memchr(buf + bol, '\n', len - bol);
 
-               if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol)))
-                       next_line = sb->buf + sb->len;
+               if (!next_line)
+                       next_line = buf + len;
                else
                        next_line++;
 
-               if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') {
+               if (buf[bol] == comment_line_char || buf[bol] == '\n') {
                        /* is this the first of the run of comments? */
                        if (!boc)
                                boc = bol;
                        /* otherwise, it is just continuing */
-               } else if (starts_with(sb->buf + bol, "Conflicts:\n")) {
+               } else if (starts_with(buf + bol, "Conflicts:\n")) {
                        in_old_conflicts_block = 1;
                        if (!boc)
                                boc = bol;
-               } else if (in_old_conflicts_block && sb->buf[bol] == '\t') {
+               } else if (in_old_conflicts_block && buf[bol] == '\t') {
                        ; /* a pathname in the conflicts block */
                } else if (boc) {
                        /* the previous was not trailing comment */
                        boc = 0;
                        in_old_conflicts_block = 0;
                }
-               bol = next_line - sb->buf;
+               bol = next_line - buf;
        }
-       return boc ? sb->len - boc : 0;
+       return boc ? len - boc : 0;
 }
index afd14f318c0c4d18f3a03781d3229f2fdc0f64c6..9c12abb91110151e3a982c4d8baf8753d0382441 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -355,7 +355,7 @@ extern const char *find_commit_header(const char *msg, const char *key,
                                      size_t *out_len);
 
 /* Find the end of the log message, the right place for a new trailer. */
-extern int ignore_non_trailer(struct strbuf *sb);
+extern int ignore_non_trailer(const char *buf, size_t len);
 
 typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
                                 void *cb_data);
index 034fff9479d03d2a2e3c7017a4fe4131461f0ec6..33501695550accdb08368aa867b9addb25c90d5f 100644 (file)
@@ -384,6 +384,9 @@ int mingw_raise(int sig);
  * ANSI emulation wrappers
  */
 
+int winansi_isatty(int fd);
+#define isatty winansi_isatty
+
 void winansi_init(void);
 HANDLE winansi_get_osfhandle(int fd);
 
index db4a5b0a37d687218ab085f22b57eb35e28893b8..cb725fb02f6e4f48c4f314c3c6c5e8535aa6eda8 100644 (file)
@@ -7,6 +7,9 @@
 #include <wingdi.h>
 #include <winreg.h>
 
+/* In this file, we actually want to use Windows' own isatty(). */
+#undef isatty
+
 /*
  ANSI codes used by git: m, K
 
@@ -570,6 +573,36 @@ static void detect_msys_tty(int fd)
 
 #endif
 
+int winansi_isatty(int fd)
+{
+       int res = isatty(fd);
+
+       if (res) {
+               /*
+                * Make sure that /dev/null is not fooling Git into believing
+                * that we are connected to a terminal, as "_isatty() returns a
+                * nonzero value if the descriptor is associated with a
+                * character device."; for more information, see
+                *
+                * https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx
+                */
+               HANDLE handle = (HANDLE)_get_osfhandle(fd);
+               if (fd == STDIN_FILENO) {
+                       DWORD dummy;
+
+                       if (!GetConsoleMode(handle, &dummy))
+                               res = 0;
+               } else if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
+                       CONSOLE_SCREEN_BUFFER_INFO dummy;
+
+                       if (!GetConsoleScreenBufferInfo(handle, &dummy))
+                               res = 0;
+               }
+       }
+
+       return res;
+}
+
 void winansi_init(void)
 {
        int con1, con2;
index b232908f8c8c2eae84bd6ef8ab2a96ac45bf94a3..447f36ac2e31dd4d11e90f326b114a78fdba8df0 100644 (file)
@@ -17,9 +17,6 @@ endif
 # because maintaining the nesting to match is a pain.  If
 # we had "elif" things would have been much nicer...
 
-ifeq ($(uname_M),x86_64)
-       XDL_FAST_HASH = YesPlease
-endif
 ifeq ($(uname_S),OSF1)
        # Need this for u_short definitions et al
        BASIC_CFLAGS += -D_OSF_SOURCE
index 1f97ffae199232fcbf372d028c4802ace91d638b..6721ff80fb1324ec1cf450f7b69cbf8266880035 100644 (file)
@@ -1734,10 +1734,10 @@ _git_rebase ()
 {
        local dir="$(__gitdir)"
        if [ -f "$dir"/rebase-merge/interactive ]; then
-               __gitcomp "--continue --skip --abort --edit-todo"
+               __gitcomp "--continue --skip --abort --quit --edit-todo"
                return
        elif [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
-               __gitcomp "--continue --skip --abort"
+               __gitcomp "--continue --skip --abort --quit"
                return
        fi
        __git_complete_strategy && return
diff --git a/contrib/update-unicode/.gitignore b/contrib/update-unicode/.gitignore
new file mode 100644 (file)
index 0000000..b0ebc6a
--- /dev/null
@@ -0,0 +1,3 @@
+uniset/
+UnicodeData.txt
+EastAsianWidth.txt
diff --git a/contrib/update-unicode/README b/contrib/update-unicode/README
new file mode 100644 (file)
index 0000000..b9e2fc8
--- /dev/null
@@ -0,0 +1,20 @@
+TL;DR: Run update_unicode.sh after the publication of a new Unicode
+standard and commit the resulting unicode_widths.h file.
+
+The long version
+================
+
+The Git source code ships the file unicode_widths.h which contains
+tables of zero and double width Unicode code points, respectively.
+These tables are generated using update_unicode.sh in this directory.
+update_unicode.sh itself uses a third-party tool, uniset, to query two
+Unicode data files for the interesting code points.
+
+On first run, update_unicode.sh clones uniset from Github and builds it.
+This requires a current-ish version of autoconf (2.69 works per December
+2016).
+
+On each run, update_unicode.sh checks whether more recent Unicode data
+files are available from the Unicode consortium, and rebuilds the header
+unicode_widths.h with the new data. The new header can then be
+committed.
diff --git a/contrib/update-unicode/update_unicode.sh b/contrib/update-unicode/update_unicode.sh
new file mode 100755 (executable)
index 0000000..e05db92
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+#See http://www.unicode.org/reports/tr44/
+#
+#Me Enclosing_Mark  an enclosing combining mark
+#Mn Nonspacing_Mark a nonspacing combining mark (zero advance width)
+#Cf Format          a format control character
+#
+cd "$(dirname "$0")"
+UNICODEWIDTH_H=$(git rev-parse --show-toplevel)/unicode_width.h
+
+wget -N http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt \
+       http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt &&
+if ! test -d uniset; then
+       git clone https://github.com/depp/uniset.git &&
+       ( cd uniset && git checkout 4b186196dd )
+fi &&
+(
+       cd uniset &&
+       if ! test -x uniset; then
+               autoreconf -i &&
+               ./configure --enable-warnings=-Werror CFLAGS='-O0 -ggdb'
+       fi &&
+       make
+) &&
+UNICODE_DIR=. && export UNICODE_DIR &&
+cat >$UNICODEWIDTH_H <<-EOF
+static const struct interval zero_width[] = {
+       $(uniset/uniset --32 cat:Me,Mn,Cf + U+1160..U+11FF - U+00AD)
+};
+static const struct interval double_width[] = {
+       $(uniset/uniset --32 eaw:F,W)
+};
+EOF
index be91358462a72c9d5a6c02f2b0e44585a76d4783..4e17e45ed265b3f4861d1b7a1e86edaa4df3b1ac 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -279,15 +279,16 @@ static int crlf_to_git(const char *path, const char *src, size_t len,
                if (convert_is_binary(len, &stats))
                        return 0;
                /*
-                * If the file in the index has any CR in it, do not convert.
-                * This is the new safer autocrlf handling.
+                * If the file in the index has any CR in it, do not
+                * convert.  This is the new safer autocrlf handling,
+                * unless we want to renormalize in a merge or
+                * cherry-pick.
                 */
-               if (checksafe == SAFE_CRLF_RENORMALIZE)
-                       checksafe = SAFE_CRLF_FALSE;
-               else if (has_cr_in_index(path))
+               if ((checksafe != SAFE_CRLF_RENORMALIZE) && has_cr_in_index(path))
                        convert_crlf_into_lf = 0;
        }
-       if (checksafe && len) {
+       if ((checksafe == SAFE_CRLF_WARN ||
+           (checksafe == SAFE_CRLF_FAIL)) && len) {
                struct text_stat new_stats;
                memcpy(&new_stats, &stats, sizeof(new_stats));
                /* simulate "git add" */
diff --git a/diff.c b/diff.c
index ec8728362dae5a3bf404f6782f9091d6a1c9a6c2..84dba60c405b588ed794a2539ce3cddaa1fbaf8b 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3106,7 +3106,8 @@ static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
                        abbrev = FALLBACK_DEFAULT_ABBREV;
                if (abbrev > GIT_SHA1_HEXSZ)
                        die("BUG: oid abbreviation out of range: %d", abbrev);
-               hex[abbrev] = '\0';
+               if (abbrev)
+                       hex[abbrev] = '\0';
                return hex;
        }
 }
@@ -3364,6 +3365,7 @@ void diff_setup(struct diff_options *options)
 
        options->file = stdout;
 
+       options->abbrev = DEFAULT_ABBREV;
        options->line_termination = '\n';
        options->break_opt = -1;
        options->rename_limit = -1;
@@ -4024,6 +4026,8 @@ int diff_opt_parse(struct diff_options *options,
                            offending, optarg);
                return argcount;
        }
+       else if (!strcmp(arg, "--no-abbrev"))
+               options->abbrev = 0;
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
        else if (skip_prefix(arg, "--abbrev=", &arg)) {
index ee3d812695fa232f30682b0e546105ca0eea5d49..4e0ab5a9bc2f8b22fb7e358b162ebaa93c55e139 100755 (executable)
@@ -4,6 +4,7 @@
 use strict;
 use warnings;
 use Git;
+use Git::I18N;
 
 binmode(STDOUT, ":raw");
 
@@ -92,6 +93,7 @@ sub colored {
 }
 
 # command line options
+my $cmd;
 my $patch_mode;
 my $patch_mode_revision;
 
@@ -104,9 +106,6 @@ sub colored {
                DIFF => 'diff-files -p',
                APPLY => sub { apply_patch 'apply --cached', @_; },
                APPLY_CHECK => 'apply --cached',
-               VERB => 'Stage',
-               TARGET => '',
-               PARTICIPLE => 'staging',
                FILTER => 'file-only',
                IS_REVERSE => 0,
        },
@@ -114,9 +113,6 @@ sub colored {
                DIFF => 'diff-index -p HEAD',
                APPLY => sub { apply_patch 'apply --cached', @_; },
                APPLY_CHECK => 'apply --cached',
-               VERB => 'Stash',
-               TARGET => '',
-               PARTICIPLE => 'stashing',
                FILTER => undef,
                IS_REVERSE => 0,
        },
@@ -124,9 +120,6 @@ sub colored {
                DIFF => 'diff-index -p --cached',
                APPLY => sub { apply_patch 'apply -R --cached', @_; },
                APPLY_CHECK => 'apply -R --cached',
-               VERB => 'Unstage',
-               TARGET => '',
-               PARTICIPLE => 'unstaging',
                FILTER => 'index-only',
                IS_REVERSE => 1,
        },
@@ -134,9 +127,6 @@ sub colored {
                DIFF => 'diff-index -R -p --cached',
                APPLY => sub { apply_patch 'apply --cached', @_; },
                APPLY_CHECK => 'apply --cached',
-               VERB => 'Apply',
-               TARGET => ' to index',
-               PARTICIPLE => 'applying',
                FILTER => 'index-only',
                IS_REVERSE => 0,
        },
@@ -144,9 +134,6 @@ sub colored {
                DIFF => 'diff-files -p',
                APPLY => sub { apply_patch 'apply -R', @_; },
                APPLY_CHECK => 'apply -R',
-               VERB => 'Discard',
-               TARGET => ' from worktree',
-               PARTICIPLE => 'discarding',
                FILTER => 'file-only',
                IS_REVERSE => 1,
        },
@@ -154,9 +141,6 @@ sub colored {
                DIFF => 'diff-index -p',
                APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
                APPLY_CHECK => 'apply -R',
-               VERB => 'Discard',
-               TARGET => ' from index and worktree',
-               PARTICIPLE => 'discarding',
                FILTER => undef,
                IS_REVERSE => 1,
        },
@@ -164,15 +148,13 @@ sub colored {
                DIFF => 'diff-index -R -p',
                APPLY => sub { apply_patch_for_checkout_commit '', @_ },
                APPLY_CHECK => 'apply',
-               VERB => 'Apply',
-               TARGET => ' to index and worktree',
-               PARTICIPLE => 'applying',
                FILTER => undef,
                IS_REVERSE => 0,
        },
 );
 
-my %patch_mode_flavour = %{$patch_modes{stage}};
+$patch_mode = 'stage';
+my %patch_mode_flavour = %{$patch_modes{$patch_mode}};
 
 sub run_cmd_pipe {
        if ($^O eq 'MSWin32') {
@@ -253,8 +235,9 @@ sub list_untracked {
        run_cmd_pipe(qw(git ls-files --others --exclude-standard --), @ARGV);
 }
 
-my $status_fmt = '%12s %12s %s';
-my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
+# TRANSLATORS: you can adjust this to align "git add -i" status menu
+my $status_fmt = __('%12s %12s %s');
+my $status_head = sprintf($status_fmt, __('staged'), __('unstaged'), __('path'));
 
 {
        my $initial;
@@ -312,7 +295,7 @@ sub list_modified {
                        my ($change, $bin);
                        $file = unquote_path($file);
                        if ($add eq '-' && $del eq '-') {
-                               $change = 'binary';
+                               $change = __('binary');
                                $bin = 1;
                        }
                        else {
@@ -321,7 +304,7 @@ sub list_modified {
                        $data{$file} = {
                                INDEX => $change,
                                BINARY => $bin,
-                               FILE => 'nothing',
+                               FILE => __('nothing'),
                        }
                }
                elsif (($adddel, $file) =
@@ -337,7 +320,7 @@ sub list_modified {
                        $file = unquote_path($file);
                        my ($change, $bin);
                        if ($add eq '-' && $del eq '-') {
-                               $change = 'binary';
+                               $change = __('binary');
                                $bin = 1;
                        }
                        else {
@@ -357,7 +340,7 @@ sub list_modified {
                        $file = unquote_path($2);
                        if (!exists $data{$file}) {
                                $data{$file} = +{
-                                       INDEX => 'unchanged',
+                                       INDEX => __('unchanged'),
                                        BINARY => 0,
                                };
                        }
@@ -372,10 +355,10 @@ sub list_modified {
 
                if ($only) {
                        if ($only eq 'index-only') {
-                               next if ($it->{INDEX} eq 'unchanged');
+                               next if ($it->{INDEX} eq __('unchanged'));
                        }
                        if ($only eq 'file-only') {
-                               next if ($it->{FILE} eq 'nothing');
+                               next if ($it->{FILE} eq __('nothing'));
                        }
                }
                push @return, +{
@@ -613,12 +596,12 @@ sub list_and_choose {
                        else {
                                $bottom = $top = find_unique($choice, @stuff);
                                if (!defined $bottom) {
-                                       error_msg "Huh ($choice)?\n";
+                                       error_msg sprintf(__("Huh (%s)?\n"), $choice);
                                        next TOPLOOP;
                                }
                        }
                        if ($opts->{SINGLETON} && $bottom != $top) {
-                               error_msg "Huh ($choice)?\n";
+                               error_msg sprintf(__("Huh (%s)?\n"), $choice);
                                next TOPLOOP;
                        }
                        for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -637,7 +620,7 @@ sub list_and_choose {
 }
 
 sub singleton_prompt_help_cmd {
-       print colored $help_color, <<\EOF ;
+       print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1          - select a numbered item
 foo        - select item based on unique prefix
@@ -646,7 +629,7 @@ sub singleton_prompt_help_cmd {
 }
 
 sub prompt_help_cmd {
-       print colored $help_color, <<\EOF ;
+       print colored $help_color, __ <<'EOF' ;
 Prompt help:
 1          - select a single item
 3-5        - select a range of items
@@ -667,12 +650,18 @@ sub status_cmd {
 sub say_n_paths {
        my $did = shift @_;
        my $cnt = scalar @_;
-       print "$did ";
-       if (1 < $cnt) {
-               print "$cnt paths\n";
-       }
-       else {
-               print "one path\n";
+       if ($did eq 'added') {
+               printf(__n("added %d path\n", "added %d paths\n",
+                          $cnt), $cnt);
+       } elsif ($did eq 'updated') {
+               printf(__n("updated %d path\n", "updated %d paths\n",
+                          $cnt), $cnt);
+       } elsif ($did eq 'reverted') {
+               printf(__n("reverted %d path\n", "reverted %d paths\n",
+                          $cnt), $cnt);
+       } else {
+               printf(__n("touched %d path\n", "touched %d paths\n",
+                          $cnt), $cnt);
        }
 }
 
@@ -680,7 +669,7 @@ sub update_cmd {
        my @mods = list_modified('file-only');
        return if (!@mods);
 
-       my @update = list_and_choose({ PROMPT => 'Update',
+       my @update = list_and_choose({ PROMPT => __('Update'),
                                       HEADER => $status_head, },
                                     @mods);
        if (@update) {
@@ -692,7 +681,7 @@ sub update_cmd {
 }
 
 sub revert_cmd {
-       my @update = list_and_choose({ PROMPT => 'Revert',
+       my @update = list_and_choose({ PROMPT => __('Revert'),
                                       HEADER => $status_head, },
                                     list_modified());
        if (@update) {
@@ -715,7 +704,7 @@ sub revert_cmd {
                                    $_->{INDEX_ADDDEL} eq 'create') {
                                        system(qw(git update-index --force-remove --),
                                               $_->{VALUE});
-                                       print "note: $_->{VALUE} is untracked now.\n";
+                                       printf(__("note: %s is untracked now.\n"), $_->{VALUE});
                                }
                        }
                }
@@ -726,13 +715,13 @@ sub revert_cmd {
 }
 
 sub add_untracked_cmd {
-       my @add = list_and_choose({ PROMPT => 'Add untracked' },
+       my @add = list_and_choose({ PROMPT => __('Add untracked') },
                                  list_untracked());
        if (@add) {
                system(qw(git update-index --add --), @add);
                say_n_paths('added', @add);
        } else {
-               print "No untracked files.\n";
+               print __("No untracked files.\n");
        }
        print "\n";
 }
@@ -1048,29 +1037,55 @@ sub color_diff {
        } @_;
 }
 
+my %edit_hunk_manually_modes = (
+       stage => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for staging."),
+       stash => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for stashing."),
+       reset_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for unstaging."),
+       reset_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+       checkout_index => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding"),
+       checkout_head => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for discarding."),
+       checkout_nothead => N__(
+"If the patch applies cleanly, the edited hunk will immediately be
+marked for applying."),
+);
+
 sub edit_hunk_manually {
        my ($oldtext) = @_;
 
        my $hunkfile = $repo->repo_path . "/addp-hunk-edit.diff";
        my $fh;
        open $fh, '>', $hunkfile
-               or die "failed to open hunk edit file for writing: " . $!;
-       print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
+               or die sprintf(__("failed to open hunk edit file for writing: %s"), $!);
+       print $fh Git::comment_lines __("Manual hunk edit mode -- see bottom for a quick guide.\n");
        print $fh @$oldtext;
-       my $participle = $patch_mode_flavour{PARTICIPLE};
        my $is_reverse = $patch_mode_flavour{IS_REVERSE};
        my ($remove_plus, $remove_minus) = $is_reverse ? ('-', '+') : ('+', '-');
-       print $fh <<EOF;
-# ---
-# To remove '$remove_minus' lines, make them ' ' lines (context).
-# To remove '$remove_plus' lines, delete them.
-# Lines starting with # will be removed.
-#
-# If the patch applies cleanly, the edited hunk will immediately be
-# marked for $participle. If it does not apply cleanly, you will be given
-# an opportunity to edit again. If all lines of the hunk are removed,
-# then the edit is aborted and the hunk is left unchanged.
+       my $comment_line_char = Git::get_comment_line_char;
+       print $fh Git::comment_lines sprintf(__ <<EOF, $remove_minus, $remove_plus, $comment_line_char),
+---
+To remove '%s' lines, make them ' ' lines (context).
+To remove '%s' lines, delete them.
+Lines starting with %s will be removed.
 EOF
+__($edit_hunk_manually_modes{$patch_mode}),
+# TRANSLATORS: 'it' refers to the patch mentioned in the previous messages.
+__ <<EOF2 ;
+If it does not apply cleanly, you will be given an opportunity to
+edit again.  If all lines of the hunk are removed, then the edit is
+aborted and the hunk is left unchanged.
+EOF2
        close $fh;
 
        chomp(my $editor = run_cmd_pipe(qw(git var GIT_EDITOR)));
@@ -1081,8 +1096,8 @@ sub edit_hunk_manually {
        }
 
        open $fh, '<', $hunkfile
-               or die "failed to open hunk edit file for reading: " . $!;
-       my @newtext = grep { !/^#/ } <$fh>;
+               or die sprintf(__("failed to open hunk edit file for reading: %s"), $!);
+       my @newtext = grep { !/^$comment_line_char/ } <$fh>;
        close $fh;
        unlink $hunkfile;
 
@@ -1166,22 +1181,66 @@ sub edit_hunk_loop {
                }
                else {
                        prompt_yesno(
-                               'Your edited hunk does not apply. Edit again '
-                               . '(saying "no" discards!) [y/n]? '
+                               # TRANSLATORS: do not translate [y/n]
+                               # The program will only accept that input
+                               # at this point.
+                               # Consider translating (saying "no" discards!) as
+                               # (saying "n" for "no" discards!) if the translation
+                               # of the word "no" does not start with n.
+                               __('Your edited hunk does not apply. Edit again '
+                                  . '(saying "no" discards!) [y/n]? ')
                                ) or return undef;
                }
        }
 }
 
+my %help_patch_modes = (
+       stage => N__(
+"y - stage this hunk
+n - do not stage this hunk
+q - quit; do not stage this hunk or any of the remaining ones
+a - stage this hunk and all later hunks in the file
+d - do not stage this hunk or any of the later hunks in the file"),
+       stash => N__(
+"y - stash this hunk
+n - do not stash this hunk
+q - quit; do not stash this hunk or any of the remaining ones
+a - stash this hunk and all later hunks in the file
+d - do not stash this hunk or any of the later hunks in the file"),
+       reset_head => N__(
+"y - unstage this hunk
+n - do not unstage this hunk
+q - quit; do not unstage this hunk or any of the remaining ones
+a - unstage this hunk and all later hunks in the file
+d - do not unstage this hunk or any of the later hunks in the file"),
+       reset_nothead => N__(
+"y - apply this hunk to index
+n - do not apply this hunk to index
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+       checkout_index => N__(
+"y - discard this hunk from worktree
+n - do not discard this hunk from worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+       checkout_head => N__(
+"y - discard this hunk from index and worktree
+n - do not discard this hunk from index and worktree
+q - quit; do not discard this hunk or any of the remaining ones
+a - discard this hunk and all later hunks in the file
+d - do not discard this hunk or any of the later hunks in the file"),
+       checkout_nothead => N__(
+"y - apply this hunk to index and worktree
+n - do not apply this hunk to index and worktree
+q - quit; do not apply this hunk or any of the remaining ones
+a - apply this hunk and all later hunks in the file
+d - do not apply this hunk or any of the later hunks in the file"),
+);
+
 sub help_patch_cmd {
-       my $verb = lc $patch_mode_flavour{VERB};
-       my $target = $patch_mode_flavour{TARGET};
-       print colored $help_color, <<EOF ;
-y - $verb this hunk$target
-n - do not $verb this hunk$target
-q - quit; do not $verb this hunk or any of the remaining ones
-a - $verb this hunk and all later hunks in the file
-d - do not $verb this hunk or any of the later hunks in the file
+       print colored $help_color, __($help_patch_modes{$patch_mode}), "\n", __ <<EOF ;
 g - select a hunk to go to
 / - search for a hunk matching the given regex
 j - leave this hunk undecided, see next undecided hunk
@@ -1213,11 +1272,11 @@ sub apply_patch_for_checkout_commit {
                run_git_apply 'apply '.$reverse, @_;
                return 1;
        } elsif (!$applies_index) {
-               print colored $error_color, "The selected hunks do not apply to the index!\n";
-               if (prompt_yesno "Apply them to the worktree anyway? ") {
+               print colored $error_color, __("The selected hunks do not apply to the index!\n");
+               if (prompt_yesno __("Apply them to the worktree anyway? ")) {
                        return run_git_apply 'apply '.$reverse, @_;
                } else {
-                       print colored $error_color, "Nothing was applied.\n";
+                       print colored $error_color, __("Nothing was applied.\n");
                        return 0;
                }
        } else {
@@ -1228,7 +1287,7 @@ sub apply_patch_for_checkout_commit {
 
 sub patch_update_cmd {
        my @all_mods = list_modified($patch_mode_flavour{FILTER});
-       error_msg "ignoring unmerged: $_->{VALUE}\n"
+       error_msg sprintf(__("ignoring unmerged: %s\n"), $_->{VALUE})
                for grep { $_->{UNMERGED} } @all_mods;
        @all_mods = grep { !$_->{UNMERGED} } @all_mods;
 
@@ -1237,9 +1296,9 @@ sub patch_update_cmd {
 
        if (!@mods) {
                if (@all_mods) {
-                       print STDERR "Only binary files changed.\n";
+                       print STDERR __("Only binary files changed.\n");
                } else {
-                       print STDERR "No changes.\n";
+                       print STDERR __("No changes.\n");
                }
                return 0;
        }
@@ -1247,7 +1306,7 @@ sub patch_update_cmd {
                @them = @mods;
        }
        else {
-               @them = list_and_choose({ PROMPT => 'Patch update',
+               @them = list_and_choose({ PROMPT => __('Patch update'),
                                          HEADER => $status_head, },
                                        @mods);
        }
@@ -1297,6 +1356,44 @@ sub display_hunks {
        return $i;
 }
 
+my %patch_update_prompt_modes = (
+       stage => {
+               mode => N__("Stage mode change [y,n,q,a,d,/%s,?]? "),
+               deletion => N__("Stage deletion [y,n,q,a,d,/%s,?]? "),
+               hunk => N__("Stage this hunk [y,n,q,a,d,/%s,?]? "),
+       },
+       stash => {
+               mode => N__("Stash mode change [y,n,q,a,d,/%s,?]? "),
+               deletion => N__("Stash deletion [y,n,q,a,d,/%s,?]? "),
+               hunk => N__("Stash this hunk [y,n,q,a,d,/%s,?]? "),
+       },
+       reset_head => {
+               mode => N__("Unstage mode change [y,n,q,a,d,/%s,?]? "),
+               deletion => N__("Unstage deletion [y,n,q,a,d,/%s,?]? "),
+               hunk => N__("Unstage this hunk [y,n,q,a,d,/%s,?]? "),
+       },
+       reset_nothead => {
+               mode => N__("Apply mode change to index [y,n,q,a,d,/%s,?]? "),
+               deletion => N__("Apply deletion to index [y,n,q,a,d,/%s,?]? "),
+               hunk => N__("Apply this hunk to index [y,n,q,a,d,/%s,?]? "),
+       },
+       checkout_index => {
+               mode => N__("Discard mode change from worktree [y,n,q,a,d,/%s,?]? "),
+               deletion => N__("Discard deletion from worktree [y,n,q,a,d,/%s,?]? "),
+               hunk => N__("Discard this hunk from worktree [y,n,q,a,d,/%s,?]? "),
+       },
+       checkout_head => {
+               mode => N__("Discard mode change from index and worktree [y,n,q,a,d,/%s,?]? "),
+               deletion => N__("Discard deletion from index and worktree [y,n,q,a,d,/%s,?]? "),
+               hunk => N__("Discard this hunk from index and worktree [y,n,q,a,d,/%s,?]? "),
+       },
+       checkout_nothead => {
+               mode => N__("Apply mode change to index and worktree [y,n,q,a,d,/%s,?]? "),
+               deletion => N__("Apply deletion to index and worktree [y,n,q,a,d,/%s,?]? "),
+               hunk => N__("Apply this hunk to index and worktree [y,n,q,a,d,/%s,?]? "),
+       },
+);
+
 sub patch_update_file {
        my $quit = 0;
        my ($ix, $num);
@@ -1369,12 +1466,9 @@ sub patch_update_file {
                for (@{$hunk[$ix]{DISPLAY}}) {
                        print;
                }
-               print colored $prompt_color, $patch_mode_flavour{VERB},
-                 ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' :
-                  $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' :
-                  ' this hunk'),
-                 $patch_mode_flavour{TARGET},
-                 " [y,n,q,a,d,/$other,?]? ";
+               print colored $prompt_color,
+                       sprintf(__($patch_update_prompt_modes{$patch_mode}{$hunk[$ix]{TYPE}}), $other);
+
                my $line = prompt_single_character;
                last unless defined $line;
                if ($line) {
@@ -1397,12 +1491,12 @@ sub patch_update_file {
                                my $response = $1;
                                my $no = $ix > 10 ? $ix - 10 : 0;
                                while ($response eq '') {
-                                       my $extra = "";
                                        $no = display_hunks(\@hunk, $no);
                                        if ($no < $num) {
-                                               $extra = " (<ret> to see more)";
+                                               print __("go to which hunk (<ret> to see more)? ");
+                                       } else {
+                                               print __("go to which hunk? ");
                                        }
-                                       print "go to which hunk$extra? ";
                                        $response = <STDIN>;
                                        if (!defined $response) {
                                                $response = '';
@@ -1410,11 +1504,13 @@ sub patch_update_file {
                                        chomp $response;
                                }
                                if ($response !~ /^\s*\d+\s*$/) {
-                                       error_msg "Invalid number: '$response'\n";
+                                       error_msg sprintf(__("Invalid number: '%s'\n"),
+                                                            $response);
                                } elsif (0 < $response && $response <= $num) {
                                        $ix = $response - 1;
                                } else {
-                                       error_msg "Sorry, only $num hunks available.\n";
+                                       error_msg sprintf(__n("Sorry, only %d hunk available.\n",
+                                                             "Sorry, only %d hunks available.\n", $num), $num);
                                }
                                next;
                        }
@@ -1439,7 +1535,7 @@ sub patch_update_file {
                        elsif ($line =~ m|^/(.*)|) {
                                my $regex = $1;
                                if ($1 eq "") {
-                                       print colored $prompt_color, "search for regex? ";
+                                       print colored $prompt_color, __("search for regex? ");
                                        $regex = <STDIN>;
                                        if (defined $regex) {
                                                chomp $regex;
@@ -1452,7 +1548,7 @@ sub patch_update_file {
                                if ($@) {
                                        my ($err,$exp) = ($@, $1);
                                        $err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
-                                       error_msg "Malformed search regexp $exp: $err\n";
+                                       error_msg sprintf(__("Malformed search regexp %s: %s\n"), $exp, $err);
                                        next;
                                }
                                my $iy = $ix;
@@ -1462,7 +1558,7 @@ sub patch_update_file {
                                        $iy++;
                                        $iy = 0 if ($iy >= $num);
                                        if ($ix == $iy) {
-                                               error_msg "No hunk matches the given pattern\n";
+                                               error_msg __("No hunk matches the given pattern\n");
                                                last;
                                        }
                                }
@@ -1474,7 +1570,7 @@ sub patch_update_file {
                                        $ix--;
                                }
                                else {
-                                       error_msg "No previous hunk\n";
+                                       error_msg __("No previous hunk\n");
                                }
                                next;
                        }
@@ -1483,7 +1579,7 @@ sub patch_update_file {
                                        $ix++;
                                }
                                else {
-                                       error_msg "No next hunk\n";
+                                       error_msg __("No next hunk\n");
                                }
                                next;
                        }
@@ -1496,21 +1592,23 @@ sub patch_update_file {
                                        }
                                }
                                else {
-                                       error_msg "No previous hunk\n";
+                                       error_msg __("No previous hunk\n");
                                }
                                next;
                        }
                        elsif ($line =~ /^j/) {
                                if ($other !~ /j/) {
-                                       error_msg "No next hunk\n";
+                                       error_msg __("No next hunk\n");
                                        next;
                                }
                        }
                        elsif ($other =~ /s/ && $line =~ /^s/) {
                                my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
                                if (1 < @split) {
-                                       print colored $header_color, "Split into ",
-                                       scalar(@split), " hunks.\n";
+                                       print colored $header_color, sprintf(
+                                               __n("Split into %d hunk.\n",
+                                                   "Split into %d hunks.\n",
+                                                   scalar(@split)), scalar(@split));
                                }
                                splice (@hunk, $ix, 1, @split);
                                $num = scalar @hunk;
@@ -1560,23 +1658,25 @@ sub diff_cmd {
        my @mods = list_modified('index-only');
        @mods = grep { !($_->{BINARY}) } @mods;
        return if (!@mods);
-       my (@them) = list_and_choose({ PROMPT => 'Review diff',
+       my (@them) = list_and_choose({ PROMPT => __('Review diff'),
                                     IMMEDIATE => 1,
                                     HEADER => $status_head, },
                                   @mods);
        return if (!@them);
-       my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+       my $reference = (is_initial_commit()) ? get_empty_tree() : 'HEAD';
        system(qw(git diff -p --cached), $reference, '--',
                map { $_->{VALUE} } @them);
 }
 
 sub quit_cmd {
-       print "Bye.\n";
+       print __("Bye.\n");
        exit(0);
 }
 
 sub help_cmd {
-       print colored $help_color, <<\EOF ;
+# TRANSLATORS: please do not translate the command names
+# 'status', 'update', 'revert', etc.
+       print colored $help_color, __ <<'EOF' ;
 status        - show paths with changes
 update        - add working tree state to the staged set of changes
 revert        - revert staged set of changes back to the HEAD version
@@ -1594,39 +1694,40 @@ sub process_args {
                        if ($1 eq 'reset') {
                                $patch_mode = 'reset_head';
                                $patch_mode_revision = 'HEAD';
-                               $arg = shift @ARGV or die "missing --";
+                               $arg = shift @ARGV or die __("missing --");
                                if ($arg ne '--') {
                                        $patch_mode_revision = $arg;
                                        $patch_mode = ($arg eq 'HEAD' ?
                                                       'reset_head' : 'reset_nothead');
-                                       $arg = shift @ARGV or die "missing --";
+                                       $arg = shift @ARGV or die __("missing --");
                                }
                        } elsif ($1 eq 'checkout') {
-                               $arg = shift @ARGV or die "missing --";
+                               $arg = shift @ARGV or die __("missing --");
                                if ($arg eq '--') {
                                        $patch_mode = 'checkout_index';
                                } else {
                                        $patch_mode_revision = $arg;
                                        $patch_mode = ($arg eq 'HEAD' ?
                                                       'checkout_head' : 'checkout_nothead');
-                                       $arg = shift @ARGV or die "missing --";
+                                       $arg = shift @ARGV or die __("missing --");
                                }
                        } elsif ($1 eq 'stage' or $1 eq 'stash') {
                                $patch_mode = $1;
-                               $arg = shift @ARGV or die "missing --";
+                               $arg = shift @ARGV or die __("missing --");
                        } else {
-                               die "unknown --patch mode: $1";
+                               die sprintf(__("unknown --patch mode: %s"), $1);
                        }
                } else {
                        $patch_mode = 'stage';
-                       $arg = shift @ARGV or die "missing --";
+                       $arg = shift @ARGV or die __("missing --");
                }
-               die "invalid argument $arg, expecting --"
-                   unless $arg eq "--";
+               die sprintf(__("invalid argument %s, expecting --"),
+                              $arg) unless $arg eq "--";
                %patch_mode_flavour = %{$patch_modes{$patch_mode}};
+               $cmd = 1;
        }
        elsif ($arg ne "--") {
-               die "invalid argument $arg, expecting --";
+               die sprintf(__("invalid argument %s, expecting --"), $arg);
        }
 }
 
@@ -1641,10 +1742,10 @@ sub main_loop {
                   [ 'help', \&help_cmd, ],
        );
        while (1) {
-               my ($it) = list_and_choose({ PROMPT => 'What now',
+               my ($it) = list_and_choose({ PROMPT => __('What now'),
                                             SINGLETON => 1,
                                             LIST_FLAT => 4,
-                                            HEADER => '*** Commands ***',
+                                            HEADER => __('*** Commands ***'),
                                             ON_EOF => \&quit_cmd,
                                             IMMEDIATE => 1 }, @cmd);
                if ($it) {
@@ -1660,7 +1761,7 @@ sub main_loop {
 
 process_args();
 refresh();
-if ($patch_mode) {
+if ($cmd) {
        patch_update_cmd();
 }
 else {
index a5790d03a075884ffe93dbff782b28ec87263051..674828de3920634505291eee03f8ea908430d938 100755 (executable)
@@ -22,6 +22,7 @@
 use File::Temp qw(tempdir);
 use Getopt::Long qw(:config pass_through);
 use Git;
+use Git::I18N;
 
 sub usage
 {
@@ -122,7 +123,7 @@ sub setup_dir_diff
        my $i = 0;
        while ($i < $#rawdiff) {
                if ($rawdiff[$i] =~ /^::/) {
-                       warn << 'EOF';
+                       warn __ <<'EOF';
 Combined diff formats ('-c' and '--cc') are not supported in
 directory diff mode ('-d' and '--dir-diff').
 EOF
@@ -182,6 +183,10 @@ sub setup_dir_diff
                }
        }
 
+       # Go to the root of the worktree so that the left index files
+       # are properly setup -- the index is toplevel-relative.
+       chdir($workdir);
+
        # Setup temp directories
        my $tmpdir = tempdir('git-difftool.XXXXX', CLEANUP => 0, TMPDIR => 1);
        my $ldir = "$tmpdir/left";
@@ -338,7 +343,7 @@ sub main
                if (length($opts{difftool_cmd}) > 0) {
                        $ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};
                } else {
-                       print "No <tool> given for --tool=<tool>\n";
+                       print __("No <tool> given for --tool=<tool>\n");
                        usage(1);
                }
        }
@@ -346,7 +351,7 @@ sub main
                if (length($opts{extcmd}) > 0) {
                        $ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};
                } else {
-                       print "No <cmd> given for --extcmd=<cmd>\n";
+                       print __("No <cmd> given for --extcmd=<cmd>\n");
                        usage(1);
                }
        }
@@ -419,11 +424,11 @@ sub dir_diff
                }
 
                if (exists $wt_modified{$file} and exists $tmp_modified{$file}) {
-                       my $errmsg = "warning: Both files modified: ";
-                       $errmsg .= "'$workdir/$file' and '$b/$file'.\n";
-                       $errmsg .= "warning: Working tree file has been left.\n";
-                       $errmsg .= "warning:\n";
-                       warn $errmsg;
+                       warn sprintf(__(
+                               "warning: Both files modified:\n" .
+                               "'%s/%s' and '%s/%s'.\n" .
+                               "warning: Working tree file has been left.\n" .
+                               "warning:\n"), $workdir, $file, $b, $file);
                        $error = 1;
                } elsif (exists $tmp_modified{$file}) {
                        my $mode = stat("$b/$file")->mode;
@@ -435,8 +440,9 @@ sub dir_diff
                }
        }
        if ($error) {
-               warn "warning: Temporary files exist in '$tmpdir'.\n";
-               warn "warning: You may want to cleanup or recover these.\n";
+               warn sprintf(__(
+                       "warning: Temporary files exist in '%s'.\n" .
+                       "warning: You may want to cleanup or recover these.\n"), $tmpdir);
                exit(1);
        } else {
                exit_cleanup($tmpdir, $rc);
index 9abd00be212b3e55883c0f9fa0487c7b31a6039f..9a8b97a2aba9aa9fabb095558c03cb8f27df3d50 100644 (file)
@@ -125,16 +125,7 @@ setup_user_tool () {
        }
 
        merge_cmd () {
-               trust_exit_code=$(git config --bool \
-                       "mergetool.$1.trustExitCode" || echo false)
-               if test "$trust_exit_code" = "false"
-               then
-                       touch "$BACKUP"
-                       ( eval $merge_tool_cmd )
-                       check_unchanged
-               else
-                       ( eval $merge_tool_cmd )
-               fi
+               ( eval $merge_tool_cmd )
        }
 }
 
@@ -162,6 +153,28 @@ setup_tool () {
                echo "$1"
        }
 
+       # Most tools' exit codes cannot be trusted, so By default we ignore
+       # their exit code and check the merged file's modification time in
+       # check_unchanged() to determine whether or not the merge was
+       # successful.  The return value from run_merge_cmd, by default, is
+       # determined by check_unchanged().
+       #
+       # When a tool's exit code can be trusted then the return value from
+       # run_merge_cmd is simply the tool's exit code, and check_unchanged()
+       # is not called.
+       #
+       # The return value of exit_code_trustable() tells us whether or not we
+       # can trust the tool's exit code.
+       #
+       # User-defined and built-in tools default to false.
+       # Built-in tools advertise that their exit code is trustable by
+       # redefining exit_code_trustable() to true.
+
+       exit_code_trustable () {
+               false
+       }
+
+
        if ! test -f "$MERGE_TOOLS_DIR/$tool"
        then
                setup_user_tool
@@ -197,6 +210,19 @@ get_merge_tool_cmd () {
        fi
 }
 
+trust_exit_code () {
+       if git config --bool "mergetool.$1.trustExitCode"
+       then
+               :; # OK
+       elif exit_code_trustable
+       then
+               echo true
+       else
+               echo false
+       fi
+}
+
+
 # Entry point for running tools
 run_merge_tool () {
        # If GIT_PREFIX is empty then we cannot use it in tools
@@ -225,7 +251,15 @@ run_diff_cmd () {
 
 # Run a either a configured or built-in merge tool
 run_merge_cmd () {
-       merge_cmd "$1"
+       mergetool_trust_exit_code=$(trust_exit_code "$1")
+       if test "$mergetool_trust_exit_code" = "true"
+       then
+               merge_cmd "$1"
+       else
+               touch "$BACKUP"
+               merge_cmd "$1"
+               check_unchanged
+       fi
 }
 
 list_merge_tool_candidates () {
index fd5ca524626c40823371422e52a3457fd1d45579..73f5fce4d6b65d05a89a1ac5a9564280038f5f54 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -78,6 +78,11 @@ def p4_build_cmd(cmd):
     if len(client) > 0:
         real_cmd += ["-c", client]
 
+    retries = gitConfigInt("git-p4.retries")
+    if retries is None:
+        # Perform 3 retries by default
+        retries = 3
+    real_cmd += ["-r", str(retries)]
 
     if isinstance(cmd,basestring):
         real_cmd = ' '.join(real_cmd) + ' ' + cmd
@@ -85,6 +90,16 @@ def p4_build_cmd(cmd):
         real_cmd += cmd
     return real_cmd
 
+def git_dir(path):
+    """ Return TRUE if the given path is a git directory (/path/to/dir/.git).
+        This won't automatically add ".git" to a directory.
+    """
+    d = read_pipe(["git", "--git-dir", path, "rev-parse", "--git-dir"], True).strip()
+    if not d or len(d) == 0:
+        return None
+    else:
+        return d
+
 def chdir(path, is_client_path=False):
     """Do chdir to the given path, and set the PWD environment
        variable for use by P4.  It does not look at getcwd() output.
@@ -262,6 +277,10 @@ def p4_revert(f):
 def p4_reopen(type, f):
     p4_system(["reopen", "-t", type, wildcard_encode(f)])
 
+def p4_reopen_in_change(changelist, files):
+    cmd = ["reopen", "-c", str(changelist)] + files
+    p4_system(cmd)
+
 def p4_move(src, dest):
     p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
 
@@ -563,10 +582,7 @@ def currentGitBranch():
         return read_pipe(["git", "name-rev", "HEAD"]).split(" ")[1].strip()
 
 def isValidGitDir(path):
-    if (os.path.exists(path + "/HEAD")
-        and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")):
-        return True;
-    return False
+    return git_dir(path) != None
 
 def parseRevision(ref):
     return read_pipe("git rev-parse %s" % ref).strip()
@@ -1005,18 +1021,20 @@ def processContent(self, git_mode, relPath, contents):
            steps."""
         if self.exceedsLargeFileThreshold(relPath, contents) or self.hasLargeFileExtension(relPath):
             contentTempFile = self.generateTempFile(contents)
-            (git_mode, contents, localLargeFile) = self.generatePointer(contentTempFile)
-
-            # Move temp file to final location in large file system
-            largeFileDir = os.path.dirname(localLargeFile)
-            if not os.path.isdir(largeFileDir):
-                os.makedirs(largeFileDir)
-            shutil.move(contentTempFile, localLargeFile)
-            self.addLargeFile(relPath)
-            if gitConfigBool('git-p4.largeFilePush'):
-                self.pushFile(localLargeFile)
-            if verbose:
-                sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile))
+            (pointer_git_mode, contents, localLargeFile) = self.generatePointer(contentTempFile)
+            if pointer_git_mode:
+                git_mode = pointer_git_mode
+            if localLargeFile:
+                # Move temp file to final location in large file system
+                largeFileDir = os.path.dirname(localLargeFile)
+                if not os.path.isdir(largeFileDir):
+                    os.makedirs(largeFileDir)
+                shutil.move(contentTempFile, localLargeFile)
+                self.addLargeFile(relPath)
+                if gitConfigBool('git-p4.largeFilePush'):
+                    self.pushFile(localLargeFile)
+                if verbose:
+                    sys.stderr.write("%s moved to large file system (%s)\n" % (relPath, localLargeFile))
         return (git_mode, contents)
 
 class MockLFS(LargeFileSystem):
@@ -1056,6 +1074,9 @@ def generatePointer(self, contentFile):
            the actual content. Return also the new location of the actual
            content.
            """
+        if os.path.getsize(contentFile) == 0:
+            return (None, '', None)
+
         pointerProcess = subprocess.Popen(
             ['git', 'lfs', 'pointer', '--file=' + contentFile],
             stdout=subprocess.PIPE
@@ -1289,6 +1310,12 @@ def __init__(self):
                 optparse.make_option("--conflict", dest="conflict_behavior",
                                      choices=self.conflict_behavior_choices),
                 optparse.make_option("--branch", dest="branch"),
+                optparse.make_option("--shelve", dest="shelve", action="store_true",
+                                     help="Shelve instead of submit. Shelved files are reverted, "
+                                     "restoring the workspace to the state before the shelve"),
+                optparse.make_option("--update-shelve", dest="update_shelve", action="store", type="int",
+                                     metavar="CHANGELIST",
+                                     help="update an existing shelved changelist, implies --shelve")
         ]
         self.description = "Submit changes from git to the perforce depot."
         self.usage += " [name of git branch to submit into perforce depot]"
@@ -1296,6 +1323,8 @@ def __init__(self):
         self.detectRenames = False
         self.preserveUser = gitConfigBool("git-p4.preserveUser")
         self.dry_run = False
+        self.shelve = False
+        self.update_shelve = None
         self.prepare_p4_only = False
         self.conflict_behavior = None
         self.isWindows = (platform.system() == "Windows")
@@ -1464,7 +1493,7 @@ def canChangeChangelists(self):
                     return 1
         return 0
 
-    def prepareSubmitTemplate(self):
+    def prepareSubmitTemplate(self, changelist=None):
         """Run "p4 change -o" to grab a change specification template.
            This does not use "p4 -G", as it is nice to keep the submission
            template in original order, since a human might edit it.
@@ -1476,7 +1505,11 @@ def prepareSubmitTemplate(self):
 
         template = ""
         inFilesSection = False
-        for line in p4_read_pipe_lines(['change', '-o']):
+        args = ['change', '-o']
+        if changelist:
+            args.append(str(changelist))
+
+        for line in p4_read_pipe_lines(args):
             if line.endswith("\r\n"):
                 line = line[:-2] + "\n"
             if inFilesSection:
@@ -1575,11 +1608,14 @@ def applyCommit(self, id):
         editedFiles = set()
         pureRenameCopy = set()
         filesToChangeExecBit = {}
+        all_files = list()
 
         for line in diff:
             diff = parseDiffTreeEntry(line)
             modifier = diff['status']
             path = diff['src']
+            all_files.append(path)
+
             if modifier == "M":
                 p4_edit(path)
                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
@@ -1705,6 +1741,10 @@ def applyCommit(self, id):
             mode = filesToChangeExecBit[f]
             setP4ExecBit(f, mode)
 
+        if self.update_shelve:
+            print("all_files = %s" % str(all_files))
+            p4_reopen_in_change(self.update_shelve, all_files)
+
         #
         # Build p4 change description, starting with the contents
         # of the git commit message.
@@ -1713,7 +1753,7 @@ def applyCommit(self, id):
         logMessage = logMessage.strip()
         (logMessage, jobs) = self.separate_jobs_from_description(logMessage)
 
-        template = self.prepareSubmitTemplate()
+        template = self.prepareSubmitTemplate(self.update_shelve)
         submitTemplate = self.prepareLogMessage(template, logMessage, jobs)
 
         if self.preserveUser:
@@ -1785,7 +1825,17 @@ def applyCommit(self, id):
                 if self.isWindows:
                     message = message.replace("\r\n", "\n")
                 submitTemplate = message[:message.index(separatorLine)]
-                p4_write_pipe(['submit', '-i'], submitTemplate)
+
+                if self.update_shelve:
+                    p4_write_pipe(['shelve', '-r', '-i'], submitTemplate)
+                elif self.shelve:
+                    p4_write_pipe(['shelve', '-i'], submitTemplate)
+                else:
+                    p4_write_pipe(['submit', '-i'], submitTemplate)
+                    # The rename/copy happened by applying a patch that created a
+                    # new file.  This leaves it writable, which confuses p4.
+                    for f in pureRenameCopy:
+                        p4_sync(f, "-f")
 
                 if self.preserveUser:
                     if p4User:
@@ -1795,23 +1845,20 @@ def applyCommit(self, id):
                         changelist = self.lastP4Changelist()
                         self.modifyChangelistUser(changelist, p4User)
 
-                # The rename/copy happened by applying a patch that created a
-                # new file.  This leaves it writable, which confuses p4.
-                for f in pureRenameCopy:
-                    p4_sync(f, "-f")
                 submitted = True
 
         finally:
             # skip this patch
-            if not submitted:
-                print "Submission cancelled, undoing p4 changes."
-                for f in editedFiles:
+            if not submitted or self.shelve:
+                if self.shelve:
+                    print ("Reverting shelved files.")
+                else:
+                    print ("Submission cancelled, undoing p4 changes.")
+                for f in editedFiles | filesToDelete:
                     p4_revert(f)
                 for f in filesToAdd:
                     p4_revert(f)
                     os.remove(f)
-                for f in filesToDelete:
-                    p4_revert(f)
 
         os.remove(fileName)
         return submitted
@@ -1907,6 +1954,9 @@ def run(self, args):
         if len(self.origin) == 0:
             self.origin = upstream
 
+        if self.update_shelve:
+            self.shelve = True
+
         if self.preserveUser:
             if not self.canChangeChangelists():
                 die("Cannot preserve user names without p4 super-user or admin permissions")
@@ -2067,13 +2117,13 @@ def run(self, args):
                         break
 
         chdir(self.oldWorkingDirectory)
-
+        shelved_applied = "shelved" if self.shelve else "applied"
         if self.dry_run:
             pass
         elif self.prepare_p4_only:
             pass
         elif len(commits) == len(applied):
-            print "All commits applied!"
+            print ("All commits {0}!".format(shelved_applied))
 
             sync = P4Sync()
             if self.branch:
@@ -2085,9 +2135,9 @@ def run(self, args):
 
         else:
             if len(applied) == 0:
-                print "No commits applied."
+                print ("No commits {0}.".format(shelved_applied))
             else:
-                print "Applied only the commits marked with '*':"
+                print ("{0} only the commits marked with '*':".format(shelved_applied.capitalize()))
                 for c in commits:
                     if c in applied:
                         star = "*"
@@ -3682,6 +3732,7 @@ def main():
         if cmd.gitdir == None:
             cmd.gitdir = os.path.abspath(".git")
             if not isValidGitDir(cmd.gitdir):
+                # "rev-parse --git-dir" without arguments will try $PWD/.git
                 cmd.gitdir = read_pipe("git rev-parse --git-dir").strip()
                 if os.path.exists(cmd.gitdir):
                     cdup = read_pipe("git rev-parse --show-cdup").strip()
@@ -3694,6 +3745,7 @@ def main():
             else:
                 die("fatal: cannot locate git repository at %s" % cmd.gitdir)
 
+        # so git commands invoked from the P4 workspace will succeed
         os.environ["GIT_DIR"] = cmd.gitdir
 
     if not cmd.run(args):
index 04f6e44bc8c779d7f4c60c19066820533279324a..48d7c5ded40e1801a36a897849e0f32b314e5981 100755 (executable)
@@ -43,6 +43,7 @@ continue!          continue
 abort!             abort and check out the original branch
 skip!              skip current patch and continue
 edit-todo!         edit the todo list during an interactive rebase
+quit!              abort but keep HEAD where it is
 "
 . git-sh-setup
 set_reflog_action rebase
@@ -241,7 +242,7 @@ do
        --verify)
                ok_to_skip_pre_rebase=
                ;;
-       --continue|--skip|--abort|--edit-todo)
+       --continue|--skip|--abort|--quit|--edit-todo)
                test $total_argc -eq 2 || usage
                action=${1##--}
                ;;
@@ -399,6 +400,9 @@ abort)
        finish_rebase
        exit
        ;;
+quit)
+       exec rm -rf "$state_dir"
+       ;;
 edit-todo)
        run_specific_rebase
        ;;
index da81be40cb7f9af1a960be2c22f8e75c9d16822e..068d60b3e698f03f433db33d9e5f085ea57ce0a3 100755 (executable)
@@ -28,6 +28,7 @@
 use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
+use Git::I18N;
 
 Getopt::Long::Configure qw/ pass_through /;
 
@@ -117,20 +118,20 @@ sub format_2822_time {
        my $localmin = $localtm[1] + $localtm[2] * 60;
        my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
        if ($localtm[0] != $gmttm[0]) {
-               die "local zone differs from GMT by a non-minute interval\n";
+               die __("local zone differs from GMT by a non-minute interval\n");
        }
        if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
                $localmin += 1440;
        } elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
                $localmin -= 1440;
        } elsif ($gmttm[6] != $localtm[6]) {
-               die "local time offset greater than or equal to 24 hours\n";
+               die __("local time offset greater than or equal to 24 hours\n");
        }
        my $offset = $localmin - $gmtmin;
        my $offhour = $offset / 60;
        my $offmin = abs($offset % 60);
        if (abs($offhour) >= 24) {
-               die ("local time offset greater than or equal to 24 hours\n");
+               die __("local time offset greater than or equal to 24 hours\n");
        }
 
        return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
@@ -198,13 +199,13 @@ sub do_edit {
                map {
                        system('sh', '-c', $editor.' "$@"', $editor, $_);
                        if (($? & 127) || ($? >> 8)) {
-                               die("the editor exited uncleanly, aborting everything");
+                               die(__("the editor exited uncleanly, aborting everything"));
                        }
                } @_;
        } else {
                system('sh', '-c', $editor.' "$@"', $editor, @_);
                if (($? & 127) || ($? >> 8)) {
-                       die("the editor exited uncleanly, aborting everything");
+                       die(__("the editor exited uncleanly, aborting everything"));
                }
        }
 }
@@ -278,10 +279,13 @@ sub signal_handler {
        # tmp files from --compose
        if (defined $compose_filename) {
                if (-e $compose_filename) {
-                       print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
+                       printf __("'%s' contains an intermediate version ".
+                                 "of the email you were composing.\n"),
+                                 $compose_filename;
                }
                if (-e ($compose_filename . ".final")) {
-                       print "'$compose_filename.final' contains the composed email.\n"
+                       printf __("'%s.final' contains the composed email.\n"),
+                                 $compose_filename;
                }
        }
 
@@ -298,7 +302,7 @@ sub signal_handler {
 my $rc = GetOptions("h" => \$help,
                     "dump-aliases" => \$dump_aliases);
 usage() unless $rc;
-die "--dump-aliases incompatible with other options\n"
+die __("--dump-aliases incompatible with other options\n")
     if !$help and $dump_aliases and @ARGV;
 $rc = GetOptions(
                    "sender|from=s" => \$sender,
@@ -361,7 +365,7 @@ sub signal_handler {
     usage();
 }
 
-die "Cannot run git format-patch from outside a repository\n"
+die __("Cannot run git format-patch from outside a repository\n")
        if $format_patch and not $repo;
 
 # Now, let's fill any that aren't set in with defaults:
@@ -430,7 +434,7 @@ sub read_config {
 my(%suppress_cc);
 if (@suppress_cc) {
        foreach my $entry (@suppress_cc) {
-               die "Unknown --suppress-cc field: '$entry'\n"
+               die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
                        unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
                $suppress_cc{$entry} = 1;
        }
@@ -459,7 +463,7 @@ sub read_config {
 if ($confirm_unconfigured) {
        $confirm = scalar %suppress_cc ? 'compose' : 'auto';
 };
-die "Unknown --confirm setting: '$confirm'\n"
+die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
        unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
 
 # Debugging, print out the suppressions.
@@ -491,16 +495,16 @@ sub split_addrs {
 sub parse_sendmail_alias {
        local $_ = shift;
        if (/"/) {
-               print STDERR "warning: sendmail alias with quotes is not supported: $_\n";
+               printf STDERR __("warning: sendmail alias with quotes is not supported: %s\n"), $_;
        } elsif (/:include:/) {
-               print STDERR "warning: `:include:` not supported: $_\n";
+               printf STDERR __("warning: `:include:` not supported: %s\n"), $_;
        } elsif (/[\/|]/) {
-               print STDERR "warning: `/file` or `|pipe` redirection not supported: $_\n";
+               printf STDERR __("warning: `/file` or `|pipe` redirection not supported: %s\n"), $_;
        } elsif (/^(\S+?)\s*:\s*(.+)$/) {
                my ($alias, $addr) = ($1, $2);
                $aliases{$alias} = [ split_addrs($addr) ];
        } else {
-               print STDERR "warning: sendmail line is not recognized: $_\n";
+               printf STDERR __("warning: sendmail line is not recognized: %s\n"), $_;
        }
 }
 
@@ -581,11 +585,11 @@ sub is_format_patch_arg {
                if (defined($format_patch)) {
                        return $format_patch;
                }
-               die(<<EOF);
-File '$f' exists but it could also be the range of commits
+               die sprintf(__ <<EOF, $f, $f);
+File '%s' exists but it could also be the range of commits
 to produce patches for.  Please disambiguate by...
 
-    * Saying "./$f" if you mean a file; or
+    * Saying "./%s" if you mean a file; or
     * Giving --format-patch option if you mean a range.
 EOF
        } catch Git::Error::Command with {
@@ -603,7 +607,7 @@ sub is_format_patch_arg {
                @ARGV = ();
        } elsif (-d $f and !is_format_patch_arg($f)) {
                opendir my $dh, $f
-                       or die "Failed to opendir $f: $!";
+                       or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
 
                push @files, grep { -f $_ } map { catfile($f, $_) }
                                sort readdir $dh;
@@ -616,7 +620,7 @@ sub is_format_patch_arg {
 }
 
 if (@rev_list_opts) {
-       die "Cannot run git format-patch from outside a repository\n"
+       die __("Cannot run git format-patch from outside a repository\n")
                unless $repo;
        push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
 }
@@ -627,7 +631,8 @@ sub is_format_patch_arg {
        foreach my $f (@files) {
                unless (-p $f) {
                        my $error = validate_patch($f);
-                       $error and die "fatal: $f: $error\nwarning: no patches were sent\n";
+                       $error and die sprintf(__("fatal: %s: %s\nwarning: no patches were sent\n"),
+                                                 $f, $error);
                }
        }
 }
@@ -637,7 +642,7 @@ sub is_format_patch_arg {
                print $_,"\n" for (@files);
        }
 } else {
-       print STDERR "\nNo patch files specified!\n\n";
+       print STDERR __("\nNo patch files specified!\n\n");
        usage();
 }
 
@@ -650,7 +655,7 @@ sub get_patch_subject {
                return "GIT: $1\n";
        }
        close $fh;
-       die "No subject line in $fn ?";
+       die sprintf(__("No subject line in %s?"), $fn);
 }
 
 if ($compose) {
@@ -660,25 +665,27 @@ sub get_patch_subject {
                tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
                tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
        open my $c, ">", $compose_filename
-               or die "Failed to open for writing $compose_filename: $!";
+               or die sprintf(__("Failed to open for writing %s: %s"), $compose_filename, $!);
 
 
        my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
        my $tpl_subject = $initial_subject || '';
        my $tpl_reply_to = $initial_reply_to || '';
 
-       print $c <<EOT;
+       print $c <<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3;
 From $tpl_sender # This line is ignored.
-GIT: Lines beginning in "GIT:" will be removed.
-GIT: Consider including an overall diffstat or table of contents
-GIT: for the patch you are writing.
-GIT:
-GIT: Clear the body content if you don't wish to send a summary.
+EOT1
+Lines beginning in "GIT:" will be removed.
+Consider including an overall diffstat or table of contents
+for the patch you are writing.
+
+Clear the body content if you don't wish to send a summary.
+EOT2
 From: $tpl_sender
 Subject: $tpl_subject
 In-Reply-To: $tpl_reply_to
 
-EOT
+EOT3
        for my $f (@files) {
                print $c get_patch_subject($f);
        }
@@ -691,10 +698,10 @@ sub get_patch_subject {
        }
 
        open my $c2, ">", $compose_filename . ".final"
-               or die "Failed to open $compose_filename.final : " . $!;
+               or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
 
        open $c, "<", $compose_filename
-               or die "Failed to open $compose_filename : " . $!;
+               or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!);
 
        my $need_8bit_cte = file_has_nonascii($compose_filename);
        my $in_body = 0;
@@ -729,7 +736,7 @@ sub get_patch_subject {
                        $sender = $1;
                        next;
                } elsif (/^(?:To|Cc|Bcc):/i) {
-                       print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
+                       print __("To/Cc/Bcc fields are not interpreted yet, they have been ignored\n");
                        next;
                }
                print $c2 $_;
@@ -738,7 +745,7 @@ sub get_patch_subject {
        close $c2;
 
        if ($summary_empty) {
-               print "Summary email is empty, skipping it\n";
+               print __("Summary email is empty, skipping it\n");
                $compose = -1;
        }
 } elsif ($annotate) {
@@ -768,7 +775,9 @@ sub ask {
                        return $resp;
                }
                if ($confirm_only) {
-                       my $yesno = $term->readline("Are you sure you want to use <$resp> [y/N]? ");
+                       my $yesno = $term->readline(
+                               # TRANSLATORS: please keep [y/N] as is.
+                               sprintf(__("Are you sure you want to use <%s> [y/N]? "), $resp));
                        if (defined $yesno && $yesno =~ /y/i) {
                                return $resp;
                        }
@@ -797,12 +806,12 @@ sub file_declares_8bit_cte {
 }
 
 if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
-       print "The following files are 8bit, but do not declare " .
-               "a Content-Transfer-Encoding.\n";
+       print __("The following files are 8bit, but do not declare " .
+                "a Content-Transfer-Encoding.\n");
        foreach my $f (sort keys %broken_encoding) {
                print "    $f\n";
        }
-       $auto_8bit_encoding = ask("Which 8bit encoding should I declare [UTF-8]? ",
+       $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare [UTF-8]? "),
                                  valid_re => qr/.{4}/, confirm_only => 1,
                                  default => "UTF-8");
 }
@@ -810,9 +819,9 @@ sub file_declares_8bit_cte {
 if (!$force) {
        for my $f (@files) {
                if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) {
-                       die "Refusing to send because the patch\n\t$f\n"
+                       die sprintf(__("Refusing to send because the patch\n\t%s\n"
                                . "has the template subject '*** SUBJECT HERE ***'. "
-                               . "Pass --force if you really want to send.\n";
+                               . "Pass --force if you really want to send.\n"), $f);
                }
        }
 }
@@ -829,7 +838,7 @@ sub file_declares_8bit_cte {
 # But it's a no-op to run sanitize_address on an already sanitized address.
 $sender = sanitize_address($sender);
 
-my $to_whom = "To whom should the emails be sent (if anyone)?";
+my $to_whom = __("To whom should the emails be sent (if anyone)?");
 my $prompting = 0;
 if (!@initial_to && !defined $to_cmd) {
        my $to = ask("$to_whom ",
@@ -847,7 +856,7 @@ sub expand_aliases {
 sub expand_one_alias {
        my $alias = shift;
        if ($EXPANDED_ALIASES{$alias}) {
-               die "fatal: alias '$alias' expands to itself\n";
+               die sprintf(__("fatal: alias '%s' expands to itself\n"), $alias);
        }
        local $EXPANDED_ALIASES{$alias} = 1;
        return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
@@ -859,7 +868,7 @@ 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 (if any)? ",
+               __("Message-ID to be used as In-Reply-To for the first email (if any)? "),
                default => "",
                valid_re => qr/\@.*\./, confirm_only => 1);
 }
@@ -909,7 +918,7 @@ sub extract_valid_address {
 sub extract_valid_address_or_die {
        my $address = shift;
        $address = extract_valid_address($address);
-       die "error: unable to extract a valid address from: $address\n"
+       die sprintf(__("error: unable to extract a valid address from: %s\n"), $address)
                if !$address;
        return $address;
 }
@@ -917,8 +926,11 @@ sub extract_valid_address_or_die {
 sub validate_address {
        my $address = shift;
        while (!extract_valid_address($address)) {
-               print STDERR "error: unable to extract a valid address from: $address\n";
-               $_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): ",
+               printf STDERR __("error: unable to extract a valid address from: %s\n"), $address;
+               # TRANSLATORS: Make sure to include [q] [d] [e] in your
+               # translation. The program will only accept English input
+               # at this point.
+               $_ = ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),
                        valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
                        default => 'q');
                if (/^d/i) {
@@ -1219,7 +1231,7 @@ sub ssl_verify_params {
                return (SSL_verify_mode => SSL_VERIFY_PEER(),
                        SSL_ca_file => $smtp_ssl_cert_path);
        } else {
-               die "CA path \"$smtp_ssl_cert_path\" does not exist";
+               die sprintf(__("CA path \"%s\" does not exist"), $smtp_ssl_cert_path);
        }
 }
 
@@ -1293,20 +1305,26 @@ sub send_message {
                if ($needs_confirm eq "inform") {
                        $confirm_unconfigured = 0; # squelch this message for the rest of this run
                        $ask_default = "y"; # assume yes on EOF since user hasn't explicitly asked for confirmation
-                       print "    The Cc list above has been expanded by additional\n";
-                       print "    addresses found in the patch commit message. By default\n";
-                       print "    send-email prompts before sending whenever this occurs.\n";
-                       print "    This behavior is controlled by the sendemail.confirm\n";
-                       print "    configuration setting.\n";
-                       print "\n";
-                       print "    For additional information, run 'git send-email --help'.\n";
-                       print "    To retain the current behavior, but squelch this message,\n";
-                       print "    run 'git config --global sendemail.confirm auto'.\n\n";
+                       print __ <<EOF ;
+    The Cc list above has been expanded by additional
+    addresses found in the patch commit message. By default
+    send-email prompts before sending whenever this occurs.
+    This behavior is controlled by the sendemail.confirm
+    configuration setting.
+
+    For additional information, run 'git send-email --help'.
+    To retain the current behavior, but squelch this message,
+    run 'git config --global sendemail.confirm auto'.
+
+EOF
                }
-               $_ = ask("Send this email? ([y]es|[n]o|[q]uit|[a]ll): ",
+               # TRANSLATORS: Make sure to include [y] [n] [q] [a] in your
+               # translation. The program will only accept English input
+               # at this point.
+               $_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "),
                         valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
                         default => $ask_default);
-               die "Send this email reply required" unless defined $_;
+               die __("Send this email reply required") unless defined $_;
                if (/^n/i) {
                        return 0;
                } elsif (/^q/i) {
@@ -1332,7 +1350,7 @@ sub send_message {
        } else {
 
                if (!defined $smtp_server) {
-                       die "The required SMTP server is not properly defined."
+                       die __("The required SMTP server is not properly defined.")
                }
 
                if ($smtp_encryption eq 'ssl') {
@@ -1376,14 +1394,14 @@ sub send_message {
                                        # supported commands
                                        $smtp->hello($smtp_domain);
                                } else {
-                                       die "Server does not support STARTTLS! ".$smtp->message;
+                                       die sprintf(__("Server does not support STARTTLS! %s"), $smtp->message);
                                }
                        }
                }
 
                if (!$smtp) {
-                       die "Unable to initialize SMTP properly. Check config and use --smtp-debug. ",
-                           "VALUES: server=$smtp_server ",
+                       die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),
+                           " VALUES: server=$smtp_server ",
                            "encryption=$smtp_encryption ",
                            "hello=$smtp_domain",
                            defined $smtp_server_port ? " port=$smtp_server_port" : "";
@@ -1400,12 +1418,12 @@ sub send_message {
                        $smtp->datasend("$line") or die $smtp->message;
                }
                $smtp->dataend() or die $smtp->message;
-               $smtp->code =~ /250|200/ or die "Failed to send $subject\n".$smtp->message;
+               $smtp->code =~ /250|200/ or die sprintf(__("Failed to send %s\n"), $subject).$smtp->message;
        }
        if ($quiet) {
-               printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject);
+               printf($dry_run ? __("Dry-Sent %s\n") : __("Sent %s\n"), $subject);
        } else {
-               print (($dry_run ? "Dry-" : "")."OK. Log says:\n");
+               print($dry_run ? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));
                if (!file_name_is_absolute($smtp_server)) {
                        print "Server: $smtp_server\n";
                        print "MAIL FROM:<$raw_from>\n";
@@ -1417,10 +1435,10 @@ sub send_message {
                }
                print $header, "\n";
                if ($smtp) {
-                       print "Result: ", $smtp->code, ' ',
+                       print __("Result: "), $smtp->code, ' ',
                                ($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
                } else {
-                       print "Result: OK\n";
+                       print __("Result: OK\n");
                }
        }
 
@@ -1433,7 +1451,7 @@ sub send_message {
 $message_num = 0;
 
 foreach my $t (@files) {
-       open my $fh, "<", $t or die "can't open file $t";
+       open my $fh, "<", $t or die sprintf(__("can't open file %s"), $t);
 
        my $author = undef;
        my $sauthor = undef;
@@ -1480,13 +1498,13 @@ sub send_message {
                                $sauthor = sanitize_address($author);
                                next if $suppress_cc{'author'};
                                next if $suppress_cc{'self'} and $sauthor eq $sender;
-                               printf("(mbox) Adding cc: %s from line '%s'\n",
+                               printf(__("(mbox) Adding cc: %s from line '%s'\n"),
                                        $1, $_) unless $quiet;
                                push @cc, $1;
                        }
                        elsif (/^To:\s+(.*)$/i) {
                                foreach my $addr (parse_address_line($1)) {
-                                       printf("(mbox) Adding to: %s from line '%s'\n",
+                                       printf(__("(mbox) Adding to: %s from line '%s'\n"),
                                                $addr, $_) unless $quiet;
                                        push @to, $addr;
                                }
@@ -1500,7 +1518,7 @@ sub send_message {
                                        } else {
                                                next if ($suppress_cc{'cc'});
                                        }
-                                       printf("(mbox) Adding cc: %s from line '%s'\n",
+                                       printf(__("(mbox) Adding cc: %s from line '%s'\n"),
                                                $addr, $_) unless $quiet;
                                        push @cc, $addr;
                                }
@@ -1534,7 +1552,7 @@ sub send_message {
                        # So let's support that, too.
                        $input_format = 'lots';
                        if (@cc == 0 && !$suppress_cc{'cc'}) {
-                               printf("(non-mbox) Adding cc: %s from line '%s'\n",
+                               printf(__("(non-mbox) Adding cc: %s from line '%s'\n"),
                                        $_, $_) unless $quiet;
                                push @cc, $_;
                        } elsif (!defined $subject) {
@@ -1557,7 +1575,7 @@ sub send_message {
                                next if $suppress_cc{'bodycc'} and $what =~ /Cc/i;
                        }
                        push @cc, $c;
-                       printf("(body) Adding cc: %s from line '%s'\n",
+                       printf(__("(body) Adding cc: %s from line '%s'\n"),
                                $c, $_) unless $quiet;
                }
        }
@@ -1655,18 +1673,18 @@ sub recipients_cmd {
 
        my @addresses = ();
        open my $fh, "-|", "$cmd \Q$file\E"
-           or die "($prefix) Could not execute '$cmd'";
+           or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
        while (my $address = <$fh>) {
                $address =~ s/^\s*//g;
                $address =~ s/\s*$//g;
                $address = sanitize_address($address);
                next if ($address eq $sender and $suppress_cc{'self'});
                push @addresses, $address;
-               printf("($prefix) Adding %s: %s from: '%s'\n",
-                      $what, $address, $cmd) unless $quiet;
+               printf(__("(%s) Adding %s: %s from: '%s'\n"),
+                      $prefix, $what, $address, $cmd) unless $quiet;
                }
        close $fh
-           or die "($prefix) failed to close pipe to '$cmd'";
+           or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
        return @addresses;
 }
 
@@ -1693,7 +1711,7 @@ sub apply_transfer_encoding {
        $message = MIME::Base64::decode($message)
                if ($from eq 'base64');
 
-       die "cannot send message as 7bit"
+       die __("cannot send message as 7bit")
                if ($to eq '7bit' and $message =~ /[^[:ascii:]]/);
        return $message
                if ($to eq '7bit' or $to eq '8bit');
@@ -1701,7 +1719,7 @@ sub apply_transfer_encoding {
                if ($to eq 'quoted-printable');
        return MIME::Base64::encode($message, "\n")
                if ($to eq 'base64');
-       die "invalid transfer encoding";
+       die __("invalid transfer encoding");
 }
 
 sub unique_email_list {
@@ -1720,10 +1738,10 @@ sub unique_email_list {
 sub validate_patch {
        my $fn = shift;
        open(my $fh, '<', $fn)
-               or die "unable to open $fn: $!\n";
+               or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
        while (my $line = <$fh>) {
                if (length($line) > 998) {
-                       return "$.: patch contains a line longer than 998 characters";
+                       return sprintf(__("%s: patch contains a line longer than 998 characters"), $.);
                }
        }
        return;
@@ -1739,10 +1757,11 @@ sub handle_backup {
            (substr($file, 0, $lastlen) eq $last) &&
            ($suffix = substr($file, $lastlen)) !~ /^[a-z0-9]/i) {
                if (defined $known_suffix && $suffix eq $known_suffix) {
-                       print "Skipping $file with backup suffix '$known_suffix'.\n";
+                       printf(__("Skipping %s with backup suffix '%s'.\n"), $file, $known_suffix);
                        $skip = 1;
                } else {
-                       my $answer = ask("Do you really want to send $file? (y|N): ",
+                       # TRANSLATORS: please keep "[y|N]" as is.
+                       my $answer = ask(sprintf(__("Do you really want to send %s? [y|N]: "), $file),
                                         valid_re => qr/^(?:y|n)/i,
                                         default => 'n');
                        $skip = ($answer ne 'y');
@@ -1770,7 +1789,7 @@ sub handle_backup_files {
 sub file_has_nonascii {
        my $fn = shift;
        open(my $fh, '<', $fn)
-               or die "unable to open $fn: $!\n";
+               or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
        while (my $line = <$fh>) {
                return 1 if $line =~ /[^[:ascii:]]/;
        }
@@ -1780,7 +1799,7 @@ sub file_has_nonascii {
 sub body_or_subject_has_nonascii {
        my $fn = shift;
        open(my $fh, '<', $fn)
-               or die "unable to open $fn: $!\n";
+               or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
        while (my $line = <$fh>) {
                last if $line =~ /^$/;
                return 1 if $line =~ /^Subject.*[^[:ascii:]]/;
index 4546abaaef3dab9e0c4dc719f111682f1cb1dd57..10c284d1aa2273a3dfe5cc39f1d6738830d02462 100755 (executable)
@@ -115,7 +115,7 @@ create_stash () {
                        git read-tree --index-output="$TMPindex" -m $i_tree &&
                        GIT_INDEX_FILE="$TMPindex" &&
                        export GIT_INDEX_FILE &&
-                       git diff --name-only -z HEAD -- >"$TMP-stagenames" &&
+                       git diff-index --name-only -z HEAD -- >"$TMP-stagenames" &&
                        git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
                        git write-tree &&
                        rm -f "$TMPindex"
index 0b2425531a8120fb29f37ee473a1e6e974959605..c2f81cd6af9d9da0f10fa2e657e6fe62bdbdc4bc 100644 (file)
@@ -274,9 +274,8 @@ static void process_alternates_response(void *callback_data)
                                struct strbuf target = STRBUF_INIT;
                                strbuf_add(&target, base, serverlen);
                                strbuf_add(&target, data + i, posn - i - 7);
-                               if (walker->get_verbosely)
-                                       fprintf(stderr, "Also look at %s\n",
-                                               target.buf);
+                               warning("adding alternate object store: %s",
+                                       target.buf);
                                newalt = xmalloc(sizeof(*newalt));
                                newalt->next = NULL;
                                newalt->base = strbuf_detach(&target, NULL);
@@ -302,6 +301,9 @@ static void fetch_alternates(struct walker *walker, const char *base)
        struct alternates_request alt_req;
        struct walker_data *cdata = walker->data;
 
+       if (http_follow_config != HTTP_FOLLOW_ALWAYS)
+               return;
+
        /*
         * If another request has already started fetching alternates,
         * wait for them to arrive and return to processing this request's
@@ -480,10 +482,13 @@ static int fetch_object(struct walker *walker, unsigned char *sha1)
         * we turned off CURLOPT_FAILONERROR to avoid losing a
         * persistent connection and got CURLE_OK.
         */
-       if (req->http_code == 404 && req->curl_result == CURLE_OK &&
+       if (req->http_code >= 300 && req->curl_result == CURLE_OK &&
                        (starts_with(req->url, "http://") ||
-                        starts_with(req->url, "https://")))
+                        starts_with(req->url, "https://"))) {
                req->curl_result = CURLE_HTTP_RETURNED_ERROR;
+               xsnprintf(req->errorstr, sizeof(req->errorstr),
+                         "HTTP request failed");
+       }
 
        if (obj_req->state == ABORTED) {
                ret = error("Request for %s aborted", hex);
diff --git a/http.c b/http.c
index 4c4a812fcc39509e32fbcae3db21871b97a1a5eb..051fe6e5ab77a5dc6e53dce7011d0fc445f15ab0 100644 (file)
--- a/http.c
+++ b/http.c
@@ -111,6 +111,8 @@ static int http_proactive_auth;
 static const char *user_agent;
 static int curl_empty_auth;
 
+enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
+
 #if LIBCURL_VERSION_NUM >= 0x071700
 /* Use CURLOPT_KEYPASSWD as is */
 #elif LIBCURL_VERSION_NUM >= 0x070903
@@ -366,6 +368,16 @@ static int http_options(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp("http.followredirects", var)) {
+               if (value && !strcmp(value, "initial"))
+                       http_follow_config = HTTP_FOLLOW_INITIAL;
+               else if (git_config_bool(var, value))
+                       http_follow_config = HTTP_FOLLOW_ALWAYS;
+               else
+                       http_follow_config = HTTP_FOLLOW_NONE;
+               return 0;
+       }
+
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
 }
@@ -717,7 +729,6 @@ static CURL *get_curl_handle(void)
                                 curl_low_speed_time);
        }
 
-       curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
        curl_easy_setopt(result, CURLOPT_MAXREDIRS, 20);
 #if LIBCURL_VERSION_NUM >= 0x071301
        curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
@@ -734,6 +745,7 @@ static CURL *get_curl_handle(void)
        if (is_transport_allowed("ftps"))
                allowed_protocols |= CURLPROTO_FTPS;
        curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS, allowed_protocols);
+       curl_easy_setopt(result, CURLOPT_PROTOCOLS, allowed_protocols);
 #else
        if (transport_restrict_protocols())
                warning("protocol restrictions not applied to curl redirects because\n"
@@ -1044,6 +1056,16 @@ struct active_request_slot *get_active_slot(void)
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
        curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
 
+       /*
+        * Default following to off unless "ALWAYS" is configured; this gives
+        * callers a sane starting point, and they can tweak for individual
+        * HTTP_FOLLOW_* cases themselves.
+        */
+       if (http_follow_config == HTTP_FOLLOW_ALWAYS)
+               curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
+       else
+               curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 0);
+
 #if LIBCURL_VERSION_NUM >= 0x070a08
        curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
 #endif
@@ -1286,9 +1308,12 @@ static int handle_curl_result(struct slot_results *results)
         * If we see a failing http code with CURLE_OK, we have turned off
         * FAILONERROR (to keep the server's custom error response), and should
         * translate the code into failure here.
+        *
+        * Likewise, if we see a redirect (30x code), that means we turned off
+        * redirect-following, and we should treat the result as an error.
         */
        if (results->curl_result == CURLE_OK &&
-           results->http_code >= 400) {
+           results->http_code >= 300) {
                results->curl_result = CURLE_HTTP_RETURNED_ERROR;
                /*
                 * Normally curl will already have put the "reason phrase"
@@ -1607,6 +1632,9 @@ static int http_request(const char *url,
                strbuf_addstr(&buf, " no-cache");
        if (options && options->keep_error)
                curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
+       if (options && options->initial_request &&
+           http_follow_config == HTTP_FOLLOW_INITIAL)
+               curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
 
        headers = curl_slist_append(headers, buf.buf);
 
@@ -1655,16 +1683,16 @@ static int http_request(const char *url,
  *
  * Note that this assumes a sane redirect scheme. It's entirely possible
  * in the example above to end up at a URL that does not even end in
- * "info/refs".  In such a case we simply punt, as there is not much we can
- * do (and such a scheme is unlikely to represent a real git repository,
- * which means we are likely about to abort anyway).
+ * "info/refs".  In such a case we die. There's not much we can do, such a
+ * scheme is unlikely to represent a real git repository, and failing to
+ * rewrite the base opens options for malicious redirects to do funny things.
  */
 static int update_url_from_redirect(struct strbuf *base,
                                    const char *asked,
                                    const struct strbuf *got)
 {
        const char *tail;
-       size_t tail_len;
+       size_t new_len;
 
        if (!strcmp(asked, got->buf))
                return 0;
@@ -1673,14 +1701,16 @@ static int update_url_from_redirect(struct strbuf *base,
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
 
-       tail_len = strlen(tail);
-
-       if (got->len < tail_len ||
-           strcmp(tail, got->buf + got->len - tail_len))
-               return 0; /* insane redirect scheme */
+       new_len = got->len;
+       if (!strip_suffix_mem(got->buf, &new_len, tail))
+               die(_("unable to update url base from redirection:\n"
+                     "  asked for: %s\n"
+                     "   redirect: %s"),
+                   asked, got->buf);
 
        strbuf_reset(base);
-       strbuf_add(base, got->buf, got->len - tail_len);
+       strbuf_add(base, got->buf, new_len);
+
        return 1;
 }
 
@@ -2028,7 +2058,7 @@ static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
                if (c != CURLE_OK)
                        die("BUG: curl_easy_getinfo for HTTP code failed: %s",
                                curl_easy_strerror(c));
-               if (slot->http_code >= 400)
+               if (slot->http_code >= 300)
                        return size;
        }
 
diff --git a/http.h b/http.h
index 5ab9d9c329378f4f254c9d912dd7f05105ea6695..02bccb7b0caf9f2b1be0b98cb16c6fc84b95cc74 100644 (file)
--- a/http.h
+++ b/http.h
@@ -116,6 +116,13 @@ extern struct credential http_auth;
 
 extern char curl_errorstr[CURL_ERROR_SIZE];
 
+enum http_follow_config {
+       HTTP_FOLLOW_NONE,
+       HTTP_FOLLOW_ALWAYS,
+       HTTP_FOLLOW_INITIAL
+};
+extern enum http_follow_config http_follow_config;
+
 static inline int missing__target(int code, int result)
 {
        return  /* file:// URL -- do we ever use one??? */
@@ -139,7 +146,8 @@ extern char *get_remote_object_url(const char *url, const char *hex,
 /* Options for http_get_*() */
 struct http_get_options {
        unsigned no_cache:1,
-                keep_error:1;
+                keep_error:1,
+                initial_request:1;
 
        /* If non-NULL, returns the content-type of the response. */
        struct strbuf *content_type;
index 9268cdf325f3881104d6d12ef31639536b15dcb2..aa69210d8b3a9063517d2d84153dc3ba738bef30 100644 (file)
@@ -174,8 +174,16 @@ int hold_lock_file_for_update_timeout(struct lock_file *lk, const char *path,
                                      int flags, long timeout_ms)
 {
        int fd = lock_file_timeout(lk, path, flags, timeout_ms);
-       if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
-               unable_to_lock_die(path, errno);
+       if (fd < 0) {
+               if (flags & LOCK_DIE_ON_ERROR)
+                       unable_to_lock_die(path, errno);
+               if (flags & LOCK_REPORT_ON_ERROR) {
+                       struct strbuf buf = STRBUF_INIT;
+                       unable_to_lock_message(path, errno, &buf);
+                       error("%s", buf.buf);
+                       strbuf_release(&buf);
+               }
+       }
        return fd;
 }
 
index d26ad27b2b2df207872cb20ce9cc0299ee0aff8e..16775a7d79bba00c297b6193f6117dfedcd3d70a 100644 (file)
@@ -129,10 +129,16 @@ struct lock_file {
 /*
  * If a lock is already taken for the file, `die()` with an error
  * message. If this flag is not specified, trying to lock a file that
- * is already locked returns -1 to the caller.
+ * is already locked silently returns -1 to the caller, or ...
  */
 #define LOCK_DIE_ON_ERROR 1
 
+/*
+ * ... this flag can be passed instead to return -1 and give the usual
+ * error message upon an error.
+ */
+#define LOCK_REPORT_ON_ERROR 2
+
 /*
  * Usually symbolic links in the destination path are resolved. This
  * means that (1) the lockfile is created by adding ".lock" to the
index 2fb3877ee44e9cdc83c43fa23971f02a94b5a49f..a489d9d0fbcc37a1198247a8daae4b3104ebe284 100644 (file)
@@ -710,7 +710,8 @@ static void flush_inbody_header_accum(struct mailinfo *mi)
 {
        if (!mi->inbody_header_accum.len)
                return;
-       assert(check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0));
+       if (!check_header(mi, &mi->inbody_header_accum, mi->s_hdr_data, 0))
+               die("BUG: inbody_header_accum, if not empty, must always contain a valid in-body header");
        strbuf_reset(&mi->inbody_header_accum);
 }
 
index 9041c2f149c01134ce02119354455894533e713c..d3272094430ccd3914c8214df73ba8e11363cfa7 100644 (file)
@@ -235,6 +235,8 @@ static int add_cacheinfo(struct merge_options *o,
                struct cache_entry *nce;
 
                nce = refresh_cache_entry(ce, CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING);
+               if (!nce)
+                       return err(o, _("addinfo_cache failed for path '%s'"), path);
                if (nce != ce)
                        ret = add_cache_entry(nce, options);
        }
@@ -388,12 +390,10 @@ static struct string_list *get_unmerged(void)
        return unmerged;
 }
 
-static int string_list_df_name_compare(const void *a, const void *b)
+static int string_list_df_name_compare(const char *one, const char *two)
 {
-       const struct string_list_item *one = a;
-       const struct string_list_item *two = b;
-       int onelen = strlen(one->string);
-       int twolen = strlen(two->string);
+       int onelen = strlen(one);
+       int twolen = strlen(two);
        /*
         * Here we only care that entries for D/F conflicts are
         * adjacent, in particular with the file of the D/F conflict
@@ -406,8 +406,8 @@ static int string_list_df_name_compare(const void *a, const void *b)
         * since in other cases any changes in their order due to
         * sorting cause no problems for us.
         */
-       int cmp = df_name_compare(one->string, onelen, S_IFDIR,
-                                 two->string, twolen, S_IFDIR);
+       int cmp = df_name_compare(one, onelen, S_IFDIR,
+                                 two, twolen, S_IFDIR);
        /*
         * Now that 'foo' and 'foo/bar' compare equal, we have to make sure
         * that 'foo' comes before 'foo/bar'.
@@ -451,8 +451,8 @@ static void record_df_conflict_files(struct merge_options *o,
                string_list_append(&df_sorted_entries, next->string)->util =
                                   next->util;
        }
-       qsort(df_sorted_entries.items, entries->nr, sizeof(*entries->items),
-             string_list_df_name_compare);
+       df_sorted_entries.cmp = string_list_df_name_compare;
+       string_list_sort(&df_sorted_entries);
 
        string_list_clear(&o->df_conflict_file_set, 1);
        for (i = 0; i < df_sorted_entries.nr; i++) {
@@ -664,7 +664,13 @@ static char *unique_path(struct merge_options *o, const char *path, const char *
        return strbuf_detach(&newpath, NULL);
 }
 
-static int dir_in_way(const char *path, int check_working_copy)
+/**
+ * Check whether a directory in the index is in the way of an incoming
+ * file.  Return 1 if so.  If check_working_copy is non-zero, also
+ * check the working directory.  If empty_ok is non-zero, also return
+ * 0 in the case where the working-tree dir exists but is empty.
+ */
+static int dir_in_way(const char *path, int check_working_copy, int empty_ok)
 {
        int pos;
        struct strbuf dirpath = STRBUF_INIT;
@@ -684,7 +690,8 @@ static int dir_in_way(const char *path, int check_working_copy)
        }
 
        strbuf_release(&dirpath);
-       return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode);
+       return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode) &&
+               !(empty_ok && is_empty_dir(path));
 }
 
 static int was_tracked(const char *path)
@@ -1062,7 +1069,7 @@ static int handle_change_delete(struct merge_options *o,
 {
        char *renamed = NULL;
        int ret = 0;
-       if (dir_in_way(path, !o->call_depth)) {
+       if (dir_in_way(path, !o->call_depth, 0)) {
                renamed = unique_path(o, path, a_oid ? o->branch1 : o->branch2);
        }
 
@@ -1195,7 +1202,7 @@ static int handle_file(struct merge_options *o,
                remove_file(o, 0, rename->path, 0);
                dst_name = unique_path(o, rename->path, cur_branch);
        } else {
-               if (dir_in_way(rename->path, !o->call_depth)) {
+               if (dir_in_way(rename->path, !o->call_depth, 0)) {
                        dst_name = unique_path(o, rename->path, cur_branch);
                        output(o, 1, _("%s is a directory in %s adding as %s instead"),
                               rename->path, other_branch, dst_name);
@@ -1704,7 +1711,8 @@ static int merge_content(struct merge_options *o,
                         o->branch2 == rename_conflict_info->branch1) ?
                        pair1->two->path : pair1->one->path;
 
-               if (dir_in_way(path, !o->call_depth))
+               if (dir_in_way(path, !o->call_depth,
+                              S_ISGITLINK(pair1->two->mode)))
                        df_conflict_remains = 1;
        }
        if (merge_file_special_markers(o, &one, &a, &b,
@@ -1862,7 +1870,8 @@ static int process_entry(struct merge_options *o,
                        oid = b_oid;
                        conf = _("directory/file");
                }
-               if (dir_in_way(path, !o->call_depth)) {
+               if (dir_in_way(path, !o->call_depth,
+                              S_ISGITLINK(a_mode))) {
                        char *new_path = unique_path(o, path, add_branch);
                        clean_merge = 0;
                        output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. "
@@ -2124,7 +2133,7 @@ int merge_recursive_generic(struct merge_options *o,
                }
        }
 
-       hold_locked_index(lock, 1);
+       hold_locked_index(lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
        if (clean < 0)
diff --git a/merge.c b/merge.c
index 23866c91655638d9e1a7213478772b78aa9d7c69..04ee5fc911c8d8134decb5c1a400576ae5681bf6 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -57,7 +57,7 @@ int checkout_fast_forward(const unsigned char *head,
 
        refresh_cache(REFRESH_QUIET);
 
-       if (hold_locked_index(lock_file, 0) < 0)
+       if (hold_locked_index(lock_file, LOCK_REPORT_ON_ERROR) < 0)
                return -1;
 
        memset(&trees, 0, sizeof(trees));
index 64f97c5e9755d54ace864cb53bf0e7844f530f49..e2407b65b70d1e622979cdb2fe0e425ba6403ee1 100644 (file)
@@ -3,7 +3,6 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if $base_present
        then
                "$merge_tool_path" -wait -merge -3 -a1 \
@@ -12,7 +11,6 @@ merge_cmd () {
                "$merge_tool_path" -wait -2 \
                        "$LOCAL" "$REMOTE" "$MERGED" >/dev/null 2>&1
        fi
-       check_unchanged
 }
 
 translate_merge_tool_path() {
index b6319d206e2333e42469486604d4635e846e5faa..3a69e60faa9c95dcf388cd295d040e9b88e0defd 100644 (file)
@@ -3,7 +3,6 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if $base_present
        then
                "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" \
@@ -12,7 +11,6 @@ merge_cmd () {
                "$merge_tool_path" "$LOCAL" "$REMOTE" \
                        -mergeoutput="$MERGED"
        fi
-       check_unchanged
 }
 
 translate_merge_tool_path() {
index 3f0486bc807133f50abcaab27c991d4a806ec63c..9f60e8da6527cf28dcc5ad4a3e5f7f0ca9442cb4 100644 (file)
@@ -3,7 +3,6 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if $base_present
        then
                "$merge_tool_path" -MF="$LOCAL" -TF="$REMOTE" -BF="$BASE" \
@@ -12,7 +11,6 @@ merge_cmd () {
                "$merge_tool_path" -MF="$LOCAL" -TF="$REMOTE" \
                        -RF="$MERGED"
        fi
-       check_unchanged
 }
 
 translate_merge_tool_path() {
index b3c71b6236e343dd3b268a2288af853804b3acf6..ee6f374bceb8e14af5b424e64fbe9787006091e8 100644 (file)
@@ -16,6 +16,10 @@ merge_cmd () {
        fi >/dev/null 2>&1
 }
 
-translate_merge_tool_path() {
+translate_merge_tool_path () {
        echo DeltaWalker
 }
+
+exit_code_trustable () {
+       true
+}
index f138cb4e731018b40426337eb8062f40512df088..9b6355b98a71da2627b68c6543cea4f2515b8d54 100644 (file)
@@ -12,3 +12,7 @@ merge_cmd () {
                        --result="$MERGED" "$LOCAL" "$REMOTE"
        fi
 }
+
+exit_code_trustable () {
+       true
+}
index 02e0843f47e09a7cc6cdd2a9c70e36773ecc96af..5a3ae8b5695d3141ff0310a706495ea52037b464 100644 (file)
@@ -3,7 +3,6 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if $base_present
        then
                "$merge_tool_path" \
@@ -13,5 +12,4 @@ merge_cmd () {
                "$merge_tool_path" \
                        "$LOCAL" "$MERGED" "$REMOTE" | cat
        fi
-       check_unchanged
 }
index 13c2e439def45fe6cf58ae4345355894463ba34f..6c5101c4f729d49c544436e9262ca75e4ce6cddd 100644 (file)
@@ -3,7 +3,6 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if $base_present
        then
                "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" \
@@ -12,5 +11,4 @@ merge_cmd () {
                "$merge_tool_path" "$LOCAL" "$REMOTE" \
                        --default --mode=merge2 --to="$MERGED"
        fi
-       check_unchanged
 }
index 7b895fdb1f9955eb470a2614e0634bfcd26d90c9..d1ce513ff5d3b3db14dd1270d2181099e1134c2c 100644 (file)
@@ -20,3 +20,7 @@ merge_cmd () {
 translate_merge_tool_path() {
        echo emacs
 }
+
+exit_code_trustable () {
+       true
+}
index 7b524d4088ff198d70c5e40a378011f2e515770e..e72b06fc4d8ff76e06a0b972ae28df73efd4180d 100644 (file)
@@ -3,14 +3,12 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if $base_present
        then
                "$merge_tool_path" -merge "$LOCAL" "$BASE" "$REMOTE" -o:"$MERGED" -nh
        else
                "$merge_tool_path" -merge "$LOCAL" "$REMOTE" -o:"$MERGED" -nh
        fi
-       check_unchanged
 }
 
 translate_merge_tool_path() {
index 793d1293b16c9b28a9ec5c70ec7fa0875ab0da54..0264ed5b20b29fec0fc5586408f88b0e8755d212 100644 (file)
@@ -21,3 +21,7 @@ merge_cmd () {
                >/dev/null 2>&1
        fi
 }
+
+exit_code_trustable () {
+       true
+}
index 433686c12a088195bbf21b2e409c21df199ba988..e8c0bfa678547258ff0f330ae756f3621e6aadc7 100644 (file)
@@ -5,3 +5,7 @@ can_merge () {
 diff_cmd () {
        "$merge_tool_path" "$LOCAL" "$REMOTE"
 }
+
+exit_code_trustable () {
+       true
+}
index 83ebdfb4c328ac79af38bfe935f0dbfa7fb6f53b..bc178e88827a1a0558e9fbee1a6f18f6ede83df1 100644 (file)
@@ -7,7 +7,7 @@ merge_cmd () {
        then
                check_meld_for_output_version
        fi
-       touch "$BACKUP"
+
        if test "$meld_has_output_option" = true
        then
                "$merge_tool_path" --output "$MERGED" \
@@ -15,7 +15,6 @@ merge_cmd () {
        else
                "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"
        fi
-       check_unchanged
 }
 
 # Check whether we should use 'meld --output <file>'
index 0942b2a733592d3f0a5a39b4fc1defd635eb7b04..b608dd6de30aaab9b30729145029761330f9fb55 100644 (file)
@@ -3,7 +3,6 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if $base_present
        then
                "$merge_tool_path" "$LOCAL" "$REMOTE" \
@@ -12,5 +11,4 @@ merge_cmd () {
                "$merge_tool_path" "$LOCAL" "$REMOTE" \
                        -merge "$MERGED" | cat
        fi
-       check_unchanged
 }
index 5a608abf9c77f436ab1472592e3d5777ef83805d..7a5b291dd28ad5b60ca0492480b800aaa9cc929e 100644 (file)
@@ -20,14 +20,12 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if ! $base_present
        then
                cp -- "$LOCAL" "$BASE"
                create_virtual_base "$BASE" "$REMOTE"
        fi
        "$merge_tool_path" "$BASE" "$REMOTE" "$LOCAL" "$MERGED"
-       check_unchanged
 }
 
 create_empty_file () {
index 618c438e8765a485ffcc70a105fb0cf46010540c..eee5cb57e3ccf7f1ad7ad150c37a8b6d7ab6d436 100644 (file)
@@ -10,3 +10,7 @@ merge_cmd () {
                "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
        fi
 }
+
+exit_code_trustable () {
+       true
+}
index 3b89f1c82dc944fcbec889d24e8e7032fda612fc..d7ab666a59a2c8690861146bb6ae4874ed48e0de 100644 (file)
@@ -5,7 +5,6 @@ can_diff () {
 merge_cmd () {
        if $base_present
        then
-               touch "$BACKUP"
                basename="$(basename "$merge_tool_path" .exe)"
                if test "$basename" = "tortoisegitmerge"
                then
@@ -17,7 +16,6 @@ merge_cmd () {
                                -base:"$BASE" -mine:"$LOCAL" \
                                -theirs:"$REMOTE" -merged:"$MERGED"
                fi
-               check_unchanged
        else
                echo "$merge_tool_path cannot be used without a base" 1>&2
                return 1
index 74ea6d54793f62188cd88e93f1adfe74c5957dda..10d86f3e19304125cecf06ddad928b9ffe2d0c08 100644 (file)
@@ -4,7 +4,6 @@ diff_cmd () {
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        case "$1" in
        gvimdiff|vimdiff)
                if $base_present
@@ -31,7 +30,6 @@ merge_cmd () {
                fi
                ;;
        esac
-       check_unchanged
 }
 
 translate_merge_tool_path() {
@@ -44,3 +42,7 @@ translate_merge_tool_path() {
                ;;
        esac
 }
+
+exit_code_trustable () {
+       true
+}
index f3819d316a2c05d9fe1f850d6ac20b04d9df0417..74d03259fdf157c9ee07eec7b2c40727f5ce49dc 100644 (file)
@@ -6,10 +6,8 @@ diff_cmd () {
 merge_cmd () {
        # mergetool.winmerge.trustExitCode is implicitly false.
        # touch $BACKUP so that we can check_unchanged.
-       touch "$BACKUP"
        "$merge_tool_path" -u -e -dl Local -dr Remote \
                "$LOCAL" "$REMOTE" "$MERGED"
-       check_unchanged
 }
 
 translate_merge_tool_path() {
index 05b443394ba166a82a22abfcf27865b4627405e2..ce5b8e9f296233470d18feae929772235e4f12f2 100644 (file)
@@ -1,25 +1,23 @@
 diff_cmd () {
        "$merge_tool_path" \
                -R 'Accel.Search: "Ctrl+F"' \
-               -R 'Accel.SearchForward: "Ctrl-G"' \
+               -R 'Accel.SearchForward: "Ctrl+G"' \
                "$LOCAL" "$REMOTE"
 }
 
 merge_cmd () {
-       touch "$BACKUP"
        if $base_present
        then
                "$merge_tool_path" -X --show-merged-pane \
-                       -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+                       -R 'Accel.SaveAsMerged: "Ctrl+S"' \
                        -R 'Accel.Search: "Ctrl+F"' \
-                       -R 'Accel.SearchForward: "Ctrl-G"' \
+                       -R 'Accel.SearchForward: "Ctrl+G"' \
                        --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
        else
                "$merge_tool_path" -X $extra \
-                       -R 'Accel.SaveAsMerged: "Ctrl-S"' \
+                       -R 'Accel.SaveAsMerged: "Ctrl+S"' \
                        -R 'Accel.Search: "Ctrl+F"' \
-                       -R 'Accel.SearchForward: "Ctrl-G"' \
+                       -R 'Accel.SearchForward: "Ctrl+G"' \
                        --merged-file "$MERGED" "$LOCAL" "$REMOTE"
        fi
-       check_unchanged
 }
index 312a85dbdef5723ef805917f5a556b75a55a7041..4fbe924a5de27d04b271a11a2ae040e817483951 100644 (file)
@@ -661,7 +661,7 @@ void NORETURN usage_msg_opt(const char *msg,
                   const char * const *usagestr,
                   const struct option *options)
 {
-       fprintf(stderr, "%s\n\n", msg);
+       fprintf(stderr, "fatal: %s\n\n", msg);
        usage_with_options(usagestr, options);
 }
 
diff --git a/path.c b/path.c
index 52d889c88ea8483b7a78937804bb1b789d2daa1e..efcedafba6f2c0cae218f2d32425c13272df8dea 100644 (file)
--- a/path.c
+++ b/path.c
@@ -991,7 +991,7 @@ const char *remove_leading_path(const char *in, const char *prefix)
  *
  * Performs the following normalizations on src, storing the result in dst:
  * - Ensures that components are separated by '/' (Windows only)
- * - Squashes sequences of '/'.
+ * - Squashes sequences of '/' except "//server/share" on Windows
  * - Removes "." components.
  * - Removes ".." components, and the components the precede them.
  * Returns failure (non-zero) if a ".." component appears as first path
@@ -1014,17 +1014,22 @@ const char *remove_leading_path(const char *in, const char *prefix)
 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
 {
        char *dst0;
-       int i;
+       const char *end;
 
-       for (i = has_dos_drive_prefix(src); i > 0; i--)
-               *dst++ = *src++;
+       /*
+        * Copy initial part of absolute path: "/", "C:/", "//server/share/".
+        */
+       end = src + offset_1st_component(src);
+       while (src < end) {
+               char c = *src++;
+               if (is_dir_sep(c))
+                       c = '/';
+               *dst++ = c;
+       }
        dst0 = dst;
 
-       if (is_dir_sep(*src)) {
-               *dst++ = '/';
-               while (is_dir_sep(*src))
-                       src++;
-       }
+       while (is_dir_sep(*src))
+               src++;
 
        for (;;) {
                char c = *src;
index b2732822af0f34c6f7731df8a1a017d19f3e0508..bfce1f795dfa5fea05f4f96637a1ae2333038735 100644 (file)
@@ -1438,6 +1438,44 @@ sub END {
 
 } # %TEMP_* Lexical Context
 
+=item prefix_lines ( PREFIX, STRING [, STRING... ])
+
+Prefixes lines in C<STRING> with C<PREFIX>.
+
+=cut
+
+sub prefix_lines {
+       my $prefix = shift;
+       my $string = join("\n", @_);
+       $string =~ s/^/$prefix/mg;
+       return $string;
+}
+
+=item get_comment_line_char ( )
+
+Gets the core.commentchar configuration value.
+The value falls-back to '#' if core.commentchar is set to 'auto'.
+
+=cut
+
+sub get_comment_line_char {
+       my $comment_line_char = config("core.commentchar") || '#';
+       $comment_line_char = '#' if ($comment_line_char eq 'auto');
+       $comment_line_char = '#' if (length($comment_line_char) != 1);
+       return $comment_line_char;
+}
+
+=item comment_lines ( STRING [, STRING... ])
+
+Comments lines following core.commentchar configuration.
+
+=cut
+
+sub comment_lines {
+       my $comment_line_char = get_comment_line_char;
+       return prefix_lines("$comment_line_char ", @_);
+}
+
 =back
 
 =head1 ERROR HANDLING
index f889fd6da91d2d9b6a7469442a02e5478f094f8d..c41425c8d07a4b9a223ef3fb71e24ff526cb9fc9 100644 (file)
@@ -13,7 +13,7 @@ BEGIN
        }
 }
 
-our @EXPORT = qw(__);
+our @EXPORT = qw(__ __n N__);
 our @EXPORT_OK = @EXPORT;
 
 sub __bootstrap_locale_messages {
@@ -44,6 +44,7 @@ BEGIN
        eval {
                __bootstrap_locale_messages();
                *__ = \&Locale::Messages::gettext;
+               *__n = \&Locale::Messages::ngettext;
                1;
        } or do {
                # Tell test.pl that we couldn't load the gettext library.
@@ -51,7 +52,10 @@ BEGIN
 
                # Just a fall-through no-op
                *__ = sub ($) { $_[0] };
+               *__n = sub ($$$) { $_[2] == 1 ? $_[0] : $_[1] };
        };
+
+       sub N__($) { return shift; }
 }
 
 1;
@@ -70,6 +74,9 @@ =head1 SYNOPSIS
 
        printf __("The following error occurred: %s\n"), $error;
 
+       printf __n("commited %d file\n", "commited %d files\n", $files), $files;
+
+
 =head1 DESCRIPTION
 
 Git's internal Perl interface to gettext via L<Locale::Messages>. If
@@ -87,6 +94,16 @@ =head2 __($)
 L<Locale::Messages>'s gettext function if all goes well, otherwise our
 passthrough fallback function.
 
+=head2 __n($$$)
+
+L<Locale::Messages>'s ngettext function or passthrough fallback function.
+
+=head2 N__($)
+
+No-operation that only returns its argument. Use this if you want xgettext to
+extract the text to the pot template but do not want to trigger retrival of the
+translation at run time.
+
 =head1 AUTHOR
 
 E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason <avarab@gmail.com>
index e7646968011234c09e1565577357a7678c015762..56ad9870bcfdfae68bd0fb5b57aa73e208bcb0fd 100644 (file)
@@ -606,7 +606,7 @@ sub minimize_url {
                        my $latest = $ra->get_latest_revnum;
                        $ra->get_log("", $latest, 0, 1, 0, 1, sub {});
                };
-       } while ($@ && ($c = shift @components));
+       } while ($@ && defined($c = shift @components));
 
        return canonicalize_url($url);
 }
index 37b2c3b1f9954d79bf799a3af979e8c337698b79..5e683830d9d66a6a1b50a26ab8c3a8338b33198b 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -10,6 +10,7 @@
 #include "color.h"
 #include "reflog-walk.h"
 #include "gpg-interface.h"
+#include "trailer.h"
 
 static char *user_format;
 static struct cmt_fmt_map {
@@ -889,6 +890,16 @@ const char *format_subject(struct strbuf *sb, const char *msg,
        return msg;
 }
 
+static void format_trailers(struct strbuf *sb, const char *msg)
+{
+       struct trailer_info info;
+
+       trailer_info_get(&info, msg);
+       strbuf_add(sb, info.trailer_start,
+                  info.trailer_end - info.trailer_start);
+       trailer_info_release(&info);
+}
+
 static void parse_commit_message(struct format_commit_context *c)
 {
        const char *msg = c->message + c->message_off;
@@ -1292,6 +1303,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                strbuf_addstr(sb, msg + c->body_off);
                return 1;
        }
+
+       if (starts_with(placeholder, "(trailers)")) {
+               format_trailers(sb, msg + c->subject_off);
+               return strlen("(trailers)");
+       }
+
        return 0;       /* unknown placeholder */
 }
 
index db5d910642663e73e4e4fc8d91e0caba4445bdb1..f92a912dcb224b3f6da9e9be07a33cfe3b08edcc 100644 (file)
@@ -1425,12 +1425,9 @@ static int read_index_extension(struct index_state *istate,
        return 0;
 }
 
-int hold_locked_index(struct lock_file *lk, int die_on_error)
+int hold_locked_index(struct lock_file *lk, int lock_flags)
 {
-       return hold_lock_file_for_update(lk, get_index_file(),
-                                        die_on_error
-                                        ? LOCK_DIE_ON_ERROR
-                                        : 0);
+       return hold_lock_file_for_update(lk, get_index_file(), lock_flags);
 }
 
 int read_index(struct index_state *istate)
index f5f7a70c6d9b8ef36ecfe282167714640cace6b1..1a978405e6b97d1f57d75b867ddeae30f091ef19 100644 (file)
@@ -13,6 +13,7 @@
 #include "utf8.h"
 #include "git-compat-util.h"
 #include "version.h"
+#include "trailer.h"
 
 typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
 
@@ -40,7 +41,7 @@ static struct used_atom {
                enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }
                        remote_ref;
                struct {
-                       enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB } option;
+                       enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
                        unsigned int nlines;
                } contents;
                enum { O_FULL, O_SHORT } objectname;
@@ -85,6 +86,13 @@ static void subject_atom_parser(struct used_atom *atom, const char *arg)
        atom->u.contents.option = C_SUB;
 }
 
+static void trailers_atom_parser(struct used_atom *atom, const char *arg)
+{
+       if (arg)
+               die(_("%%(trailers) does not take arguments"));
+       atom->u.contents.option = C_TRAILERS;
+}
+
 static void contents_atom_parser(struct used_atom *atom, const char *arg)
 {
        if (!arg)
@@ -95,6 +103,8 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)
                atom->u.contents.option = C_SIG;
        else if (!strcmp(arg, "subject"))
                atom->u.contents.option = C_SUB;
+       else if (!strcmp(arg, "trailers"))
+               atom->u.contents.option = C_TRAILERS;
        else if (skip_prefix(arg, "lines=", &arg)) {
                atom->u.contents.option = C_LINES;
                if (strtoul_ui(arg, 10, &atom->u.contents.nlines))
@@ -194,6 +204,7 @@ static struct {
        { "creatordate", FIELD_TIME },
        { "subject", FIELD_STR, subject_atom_parser },
        { "body", FIELD_STR, body_atom_parser },
+       { "trailers", FIELD_STR, trailers_atom_parser },
        { "contents", FIELD_STR, contents_atom_parser },
        { "upstream", FIELD_STR, remote_ref_atom_parser },
        { "push", FIELD_STR, remote_ref_atom_parser },
@@ -785,6 +796,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
                        name++;
                if (strcmp(name, "subject") &&
                    strcmp(name, "body") &&
+                   strcmp(name, "trailers") &&
                    !starts_with(name, "contents"))
                        continue;
                if (!subpos)
@@ -808,6 +820,14 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, struct obj
                        /*  Size is the length of the message after removing the signature */
                        append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);
                        v->s = strbuf_detach(&s, NULL);
+               } else if (atom->u.contents.option == C_TRAILERS) {
+                       struct trailer_info info;
+
+                       /* Search for trailer info */
+                       trailer_info_get(&info, subpos);
+                       v->s = xmemdupz(info.trailer_start,
+                                       info.trailer_end - info.trailer_start);
+                       trailer_info_release(&info);
                } else if (atom->u.contents.option == C_BARE)
                        v->s = xstrdup(subpos);
        }
@@ -1231,8 +1251,14 @@ static int commit_contains(struct ref_filter *filter, struct commit *commit)
  * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
  * matches "refs/heads/mas*", too).
  */
-static int match_pattern(const char **patterns, const char *refname)
+static int match_pattern(const struct ref_filter *filter, const char *refname)
 {
+       const char **patterns = filter->name_patterns;
+       unsigned flags = 0;
+
+       if (filter->ignore_case)
+               flags |= WM_CASEFOLD;
+
        /*
         * When no '--format' option is given we need to skip the prefix
         * for matching refs of tags and branches.
@@ -1243,7 +1269,7 @@ static int match_pattern(const char **patterns, const char *refname)
               skip_prefix(refname, "refs/", &refname));
 
        for (; *patterns; patterns++) {
-               if (!wildmatch(*patterns, refname, 0, NULL))
+               if (!wildmatch(*patterns, refname, flags, NULL))
                        return 1;
        }
        return 0;
@@ -1255,9 +1281,15 @@ static int match_pattern(const char **patterns, const char *refname)
  * matches a pattern "refs/heads/" but not "refs/heads/m") or a
  * wildcard (e.g. the same ref matches "refs/heads/m*", too).
  */
-static int match_name_as_path(const char **pattern, const char *refname)
+static int match_name_as_path(const struct ref_filter *filter, const char *refname)
 {
+       const char **pattern = filter->name_patterns;
        int namelen = strlen(refname);
+       unsigned flags = WM_PATHNAME;
+
+       if (filter->ignore_case)
+               flags |= WM_CASEFOLD;
+
        for (; *pattern; pattern++) {
                const char *p = *pattern;
                int plen = strlen(p);
@@ -1280,8 +1312,8 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
        if (!*filter->name_patterns)
                return 1; /* No pattern always matches */
        if (filter->match_as_path)
-               return match_name_as_path(filter->name_patterns, refname);
-       return match_pattern(filter->name_patterns, refname);
+               return match_name_as_path(filter, refname);
+       return match_pattern(filter, refname);
 }
 
 /*
@@ -1536,18 +1568,20 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
        struct atom_value *va, *vb;
        int cmp;
        cmp_type cmp_type = used_atom[s->atom].type;
+       int (*cmp_fn)(const char *, const char *);
 
        get_ref_atom_value(a, s->atom, &va);
        get_ref_atom_value(b, s->atom, &vb);
+       cmp_fn = s->ignore_case ? strcasecmp : strcmp;
        if (s->version)
                cmp = versioncmp(va->s, vb->s);
        else if (cmp_type == FIELD_STR)
-               cmp = strcmp(va->s, vb->s);
+               cmp = cmp_fn(va->s, vb->s);
        else {
                if (va->ul < vb->ul)
                        cmp = -1;
                else if (va->ul == vb->ul)
-                       cmp = strcmp(a->refname, b->refname);
+                       cmp = cmp_fn(a->refname, b->refname);
                else
                        cmp = 1;
        }
index 14d435e2ccf0204312d1f72555f1c2c36fabd215..fc55fa3574620bfdc76b2997afc56909528e2275 100644 (file)
@@ -29,6 +29,7 @@ struct ref_sorting {
        struct ref_sorting *next;
        int atom; /* index into used_atom array (internal) */
        unsigned reverse : 1,
+               ignore_case : 1,
                version : 1;
 };
 
@@ -62,6 +63,7 @@ struct ref_filter {
 
        unsigned int with_commit_tag_algo : 1,
                match_as_path : 1,
+               ignore_case : 1,
                detached : 1;
        unsigned int kind,
                lines;
index f14c41f4c0d6d6c9bbfd18d6417d16f0e6068c1d..28d9d1063880b9b7e6ec18ea18c18f77fb99ef32 100644 (file)
@@ -274,7 +274,7 @@ static struct discovery *discover_refs(const char *service, int for_push)
        struct strbuf effective_url = STRBUF_INIT;
        struct discovery *last = last_discovery;
        int http_ret, maybe_smart = 0;
-       struct http_get_options options;
+       struct http_get_options http_options;
 
        if (last && !strcmp(service, last->service))
                return last;
@@ -291,15 +291,16 @@ static struct discovery *discover_refs(const char *service, int for_push)
                strbuf_addf(&refs_url, "service=%s", service);
        }
 
-       memset(&options, 0, sizeof(options));
-       options.content_type = &type;
-       options.charset = &charset;
-       options.effective_url = &effective_url;
-       options.base_url = &url;
-       options.no_cache = 1;
-       options.keep_error = 1;
+       memset(&http_options, 0, sizeof(http_options));
+       http_options.content_type = &type;
+       http_options.charset = &charset;
+       http_options.effective_url = &effective_url;
+       http_options.base_url = &url;
+       http_options.initial_request = 1;
+       http_options.no_cache = 1;
+       http_options.keep_error = 1;
 
-       http_ret = http_get_strbuf(refs_url.buf, &buffer, &options);
+       http_ret = http_get_strbuf(refs_url.buf, &buffer, &http_options);
        switch (http_ret) {
        case HTTP_OK:
                break;
@@ -314,6 +315,9 @@ static struct discovery *discover_refs(const char *service, int for_push)
                die("unable to access '%s': %s", url.buf, curl_errorstr);
        }
 
+       if (options.verbosity && !starts_with(refs_url.buf, url.buf))
+               warning(_("redirecting to %s"), url.buf);
+
        last= xcalloc(1, sizeof(*last_discovery));
        last->service = service;
        last->buf_alloc = strbuf_detach(&buffer, &last->len);
index 5d083ca572df0c8a2cb90c0b12c14e15b4134d48..3bd55caf3b0961888bcca06d3c54577cb25f5223 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -708,7 +708,7 @@ static void update_paths(struct string_list *update)
 {
        int i;
 
-       hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
 
        for (i = 0; i < update->nr; i++) {
                struct string_list_item *item = &update->items[i];
index 30b10ba143959bf2618872427f730a9f0e30d369..9adb7bbf1d4815d9e4f67b66e90c9ec327b2fb72 100644 (file)
@@ -16,6 +16,7 @@
 #include "refs.h"
 #include "argv-array.h"
 #include "quote.h"
+#include "trailer.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -27,6 +28,7 @@ GIT_PATH_FUNC(git_path_seq_dir, "sequencer")
 static GIT_PATH_FUNC(git_path_todo_file, "sequencer/todo")
 static GIT_PATH_FUNC(git_path_opts_file, "sequencer/opts")
 static GIT_PATH_FUNC(git_path_head_file, "sequencer/head")
+static GIT_PATH_FUNC(git_path_abort_safety_file, "sequencer/abort-safety")
 
 /*
  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
@@ -56,30 +58,6 @@ static const char *get_todo_path(const struct replay_opts *opts)
        return git_path_todo_file();
 }
 
-static int is_rfc2822_line(const char *buf, int len)
-{
-       int i;
-
-       for (i = 0; i < len; i++) {
-               int ch = buf[i];
-               if (ch == ':')
-                       return 1;
-               if (!isalnum(ch) && ch != '-')
-                       break;
-       }
-
-       return 0;
-}
-
-static int is_cherry_picked_from_line(const char *buf, int len)
-{
-       /*
-        * We only care that it looks roughly like (cherry picked from ...)
-        */
-       return len > strlen(cherry_picked_prefix) + 1 &&
-               starts_with(buf, cherry_picked_prefix) && buf[len - 1] == ')';
-}
-
 /*
  * Returns 0 for non-conforming footer
  * Returns 1 for conforming footer
@@ -89,49 +67,25 @@ static int is_cherry_picked_from_line(const char *buf, int len)
 static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
        int ignore_footer)
 {
-       char prev;
-       int i, k;
-       int len = sb->len - ignore_footer;
-       const char *buf = sb->buf;
-       int found_sob = 0;
-
-       /* footer must end with newline */
-       if (!len || buf[len - 1] != '\n')
-               return 0;
+       struct trailer_info info;
+       int i;
+       int found_sob = 0, found_sob_last = 0;
 
-       prev = '\0';
-       for (i = len - 1; i > 0; i--) {
-               char ch = buf[i];
-               if (prev == '\n' && ch == '\n') /* paragraph break */
-                       break;
-               prev = ch;
-       }
+       trailer_info_get(&info, sb->buf);
 
-       /* require at least one blank line */
-       if (prev != '\n' || buf[i] != '\n')
+       if (info.trailer_start == info.trailer_end)
                return 0;
 
-       /* advance to start of last paragraph */
-       while (i < len - 1 && buf[i] == '\n')
-               i++;
-
-       for (; i < len; i = k) {
-               int found_rfc2822;
-
-               for (k = i; k < len && buf[k] != '\n'; k++)
-                       ; /* do nothing */
-               k++;
+       for (i = 0; i < info.trailer_nr; i++)
+               if (sob && !strncmp(info.trailers[i], sob->buf, sob->len)) {
+                       found_sob = 1;
+                       if (i == info.trailer_nr - 1)
+                               found_sob_last = 1;
+               }
 
-               found_rfc2822 = is_rfc2822_line(buf + i, k - i - 1);
-               if (found_rfc2822 && sob &&
-                   !strncmp(buf + i, sob->buf, sob->len))
-                       found_sob = k;
+       trailer_info_release(&info);
 
-               if (!(found_rfc2822 ||
-                     is_cherry_picked_from_line(buf + i, k - i - 1)))
-                       return 0;
-       }
-       if (found_sob == i)
+       if (found_sob_last)
                return 3;
        if (found_sob)
                return 2;
@@ -310,6 +264,20 @@ static int error_dirty_index(struct replay_opts *opts)
        return -1;
 }
 
+static void update_abort_safety_file(void)
+{
+       struct object_id head;
+
+       /* Do nothing on a single-pick */
+       if (!file_exists(git_path_seq_dir()))
+               return;
+
+       if (!get_oid("HEAD", &head))
+               write_file(git_path_abort_safety_file(), "%s", oid_to_hex(&head));
+       else
+               write_file(git_path_abort_safety_file(), "%s", "");
+}
+
 static int fast_forward_to(const unsigned char *to, const unsigned char *from,
                        int unborn, struct replay_opts *opts)
 {
@@ -339,6 +307,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
        strbuf_release(&sb);
        strbuf_release(&err);
        ref_transaction_free(transaction);
+       update_abort_safety_file();
        return 0;
 }
 
@@ -370,7 +339,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        char **xopt;
        static struct lock_file index_lock;
 
-       hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
 
        read_cache();
 
@@ -813,6 +782,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
 
 leave:
        free_message(commit, &msg);
+       update_abort_safety_file();
 
        return res;
 }
@@ -1132,9 +1102,34 @@ static int save_head(const char *head)
        return 0;
 }
 
+static int rollback_is_safe(void)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct object_id expected_head, actual_head;
+
+       if (strbuf_read_file(&sb, git_path_abort_safety_file(), 0) >= 0) {
+               strbuf_trim(&sb);
+               if (get_oid_hex(sb.buf, &expected_head)) {
+                       strbuf_release(&sb);
+                       die(_("could not parse %s"), git_path_abort_safety_file());
+               }
+               strbuf_release(&sb);
+       }
+       else if (errno == ENOENT)
+               oidclr(&expected_head);
+       else
+               die_errno(_("could not read '%s'"), git_path_abort_safety_file());
+
+       if (get_oid("HEAD", &actual_head))
+               oidclr(&actual_head);
+
+       return !oidcmp(&actual_head, &expected_head);
+}
+
 static int reset_for_rollback(const unsigned char *sha1)
 {
        const char *argv[4];    /* reset --merge <arg> + NULL */
+
        argv[0] = "reset";
        argv[1] = "--merge";
        argv[2] = sha1_to_hex(sha1);
@@ -1189,6 +1184,12 @@ int sequencer_rollback(struct replay_opts *opts)
                error(_("cannot abort from a branch yet to be born"));
                goto fail;
        }
+
+       if (!rollback_is_safe()) {
+               /* Do not error, just do not rollback */
+               warning(_("You seem to have moved HEAD. "
+                         "Not rewinding, check your HEAD!"));
+       } else
        if (reset_for_rollback(sha1))
                goto fail;
        strbuf_release(&buf);
@@ -1393,6 +1394,7 @@ int sequencer_pick_revisions(struct replay_opts *opts)
                return -1;
        if (save_opts(opts))
                return -1;
+       update_abort_safety_file();
        res = pick_commits(&todo_list, opts);
        todo_list_release(&todo_list);
        return res;
index 9c86d1924a23e4c81369f34ec8e1058c2dd15c1f..1173071859dae68f72cc72efb20f816152d3eabc 100644 (file)
@@ -26,6 +26,7 @@
 #include "mru.h"
 #include "list.h"
 #include "mergesort.h"
+#include "quote.h"
 
 #ifndef O_NOATIME
 #if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -329,13 +330,40 @@ static int link_alt_odb_entry(const char *entry, const char *relative_base,
        return 0;
 }
 
+static const char *parse_alt_odb_entry(const char *string,
+                                      int sep,
+                                      struct strbuf *out)
+{
+       const char *end;
+
+       strbuf_reset(out);
+
+       if (*string == '#') {
+               /* comment; consume up to next separator */
+               end = strchrnul(string, sep);
+       } else if (*string == '"' && !unquote_c_style(out, string, &end)) {
+               /*
+                * quoted path; unquote_c_style has copied the
+                * data for us and set "end". Broken quoting (e.g.,
+                * an entry that doesn't end with a quote) falls
+                * back to the unquoted case below.
+                */
+       } else {
+               /* normal, unquoted path */
+               end = strchrnul(string, sep);
+               strbuf_add(out, string, end - string);
+       }
+
+       if (*end)
+               end++;
+       return end;
+}
+
 static void link_alt_odb_entries(const char *alt, int len, int sep,
                                 const char *relative_base, int depth)
 {
-       struct string_list entries = STRING_LIST_INIT_NODUP;
-       char *alt_copy;
-       int i;
        struct strbuf objdirbuf = STRBUF_INIT;
+       struct strbuf entry = STRBUF_INIT;
 
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
@@ -348,16 +376,13 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
                die("unable to normalize object directory: %s",
                    objdirbuf.buf);
 
-       alt_copy = xmemdupz(alt, len);
-       string_list_split_in_place(&entries, alt_copy, sep, -1);
-       for (i = 0; i < entries.nr; i++) {
-               const char *entry = entries.items[i].string;
-               if (entry[0] == '\0' || entry[0] == '#')
+       while (*alt) {
+               alt = parse_alt_odb_entry(alt, sep, &entry);
+               if (!entry.len)
                        continue;
-               link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
+               link_alt_odb_entry(entry.buf, relative_base, depth, objdirbuf.buf);
        }
-       string_list_clear(&entries, 0);
-       free(alt_copy);
+       strbuf_release(&entry);
        strbuf_release(&objdirbuf);
 }
 
index 4d0b005d39c1c1ab2379911666d4df75d66b824c..11f7dde9d910093dce6a767150bb4823bff80200 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -431,12 +431,14 @@ void remove_nonexistent_theirs_shallow(struct shallow_info *info)
 
 define_commit_slab(ref_bitmap, uint32_t *);
 
+#define POOL_SIZE (512 * 1024)
+
 struct paint_info {
        struct ref_bitmap ref_bitmap;
        unsigned nr_bits;
-       char **slab;
+       char **pools;
        char *free, *end;
-       unsigned slab_count;
+       unsigned pool_count;
 };
 
 static uint32_t *paint_alloc(struct paint_info *info)
@@ -444,12 +446,15 @@ static uint32_t *paint_alloc(struct paint_info *info)
        unsigned nr = (info->nr_bits + 31) / 32;
        unsigned size = nr * sizeof(uint32_t);
        void *p;
-       if (!info->slab_count || info->free + size > info->end) {
-               info->slab_count++;
-               REALLOC_ARRAY(info->slab, info->slab_count);
-               info->free = xmalloc(COMMIT_SLAB_SIZE);
-               info->slab[info->slab_count - 1] = info->free;
-               info->end = info->free + COMMIT_SLAB_SIZE;
+       if (!info->pool_count || size > info->end - info->free) {
+               if (size > POOL_SIZE)
+                       die("BUG: pool size too small for %d in paint_alloc()",
+                           size);
+               info->pool_count++;
+               REALLOC_ARRAY(info->pools, info->pool_count);
+               info->free = xmalloc(POOL_SIZE);
+               info->pools[info->pool_count - 1] = info->free;
+               info->end = info->free + POOL_SIZE;
        }
        p = info->free;
        info->free += size;
@@ -462,7 +467,7 @@ static uint32_t *paint_alloc(struct paint_info *info)
  * all walked commits.
  */
 static void paint_down(struct paint_info *info, const unsigned char *sha1,
-                      int id)
+                      unsigned int id)
 {
        unsigned int i, nr;
        struct commit_list *head = NULL;
@@ -474,7 +479,7 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1,
        if (!c)
                return;
        memset(bitmap, 0, bitmap_size);
-       bitmap[id / 32] |= (1 << (id % 32));
+       bitmap[id / 32] |= (1U << (id % 32));
        commit_list_insert(c, &head);
        while (head) {
                struct commit_list *p;
@@ -507,12 +512,8 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1,
                            oid_to_hex(&c->object.oid));
 
                for (p = c->parents; p; p = p->next) {
-                       uint32_t **p_refs = ref_bitmap_at(&info->ref_bitmap,
-                                                         p->item);
                        if (p->item->object.flags & SEEN)
                                continue;
-                       if (*p_refs == NULL || *p_refs == *refs)
-                               *p_refs = *refs;
                        commit_list_insert(p->item, &head);
                }
        }
@@ -624,9 +625,9 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
                post_assign_shallow(info, &pi.ref_bitmap, ref_status);
 
        clear_ref_bitmap(&pi.ref_bitmap);
-       for (i = 0; i < pi.slab_count; i++)
-               free(pi.slab[i]);
-       free(pi.slab);
+       for (i = 0; i < pi.pool_count; i++)
+               free(pi.pools[i]);
+       free(pi.pools);
        free(shallow);
 }
 
@@ -648,11 +649,11 @@ static int add_ref(const char *refname, const struct object_id *oid,
 
 static void update_refstatus(int *ref_status, int nr, uint32_t *bitmap)
 {
-       int i;
+       unsigned int i;
        if (!ref_status)
                return;
        for (i = 0; i < nr; i++)
-               if (bitmap[i / 32] & (1 << (i % 32)))
+               if (bitmap[i / 32] & (1U << (i % 32)))
                        ref_status[i]++;
 }
 
index 098085be69b97687b0febfe9e29c6dc0c64569a1..ec13ab5a3dd82ac90c1c83d9e07dc8c045a992d2 100644 (file)
@@ -263,12 +263,12 @@ int parse_push_recurse_submodules_arg(const char *opt, const char *arg)
        return parse_push_recurse(opt, arg, 1);
 }
 
-static void warn_multiple_config(const unsigned char *commit_sha1,
+static void warn_multiple_config(const unsigned char *treeish_name,
                                 const char *name, const char *option)
 {
        const char *commit_string = "WORKTREE";
-       if (commit_sha1)
-               commit_string = sha1_to_hex(commit_sha1);
+       if (treeish_name)
+               commit_string = sha1_to_hex(treeish_name);
        warning("%s:.gitmodules, multiple configurations found for "
                        "'submodule.%s.%s'. Skipping second one!",
                        commit_string, name, option);
@@ -276,7 +276,7 @@ static void warn_multiple_config(const unsigned char *commit_sha1,
 
 struct parse_config_parameter {
        struct submodule_cache *cache;
-       const unsigned char *commit_sha1;
+       const unsigned char *treeish_name;
        const unsigned char *gitmodules_sha1;
        int overwrite;
 };
@@ -300,7 +300,7 @@ static int parse_config(const char *var, const char *value, void *data)
                if (!value)
                        ret = config_error_nonbool(var);
                else if (!me->overwrite && submodule->path)
-                       warn_multiple_config(me->commit_sha1, submodule->name,
+                       warn_multiple_config(me->treeish_name, submodule->name,
                                        "path");
                else {
                        if (submodule->path)
@@ -314,7 +314,7 @@ static int parse_config(const char *var, const char *value, void *data)
                int die_on_error = is_null_sha1(me->gitmodules_sha1);
                if (!me->overwrite &&
                    submodule->fetch_recurse != RECURSE_SUBMODULES_NONE)
-                       warn_multiple_config(me->commit_sha1, submodule->name,
+                       warn_multiple_config(me->treeish_name, submodule->name,
                                        "fetchrecursesubmodules");
                else
                        submodule->fetch_recurse = parse_fetch_recurse(
@@ -324,7 +324,7 @@ static int parse_config(const char *var, const char *value, void *data)
                if (!value)
                        ret = config_error_nonbool(var);
                else if (!me->overwrite && submodule->ignore)
-                       warn_multiple_config(me->commit_sha1, submodule->name,
+                       warn_multiple_config(me->treeish_name, submodule->name,
                                        "ignore");
                else if (strcmp(value, "untracked") &&
                         strcmp(value, "dirty") &&
@@ -340,7 +340,7 @@ static int parse_config(const char *var, const char *value, void *data)
                if (!value) {
                        ret = config_error_nonbool(var);
                } else if (!me->overwrite && submodule->url) {
-                       warn_multiple_config(me->commit_sha1, submodule->name,
+                       warn_multiple_config(me->treeish_name, submodule->name,
                                        "url");
                } else {
                        free((void *) submodule->url);
@@ -351,21 +351,21 @@ static int parse_config(const char *var, const char *value, void *data)
                        ret = config_error_nonbool(var);
                else if (!me->overwrite &&
                         submodule->update_strategy.type != SM_UPDATE_UNSPECIFIED)
-                       warn_multiple_config(me->commit_sha1, submodule->name,
+                       warn_multiple_config(me->treeish_name, submodule->name,
                                             "update");
                else if (parse_submodule_update_strategy(value,
                         &submodule->update_strategy) < 0)
                                die(_("invalid value for %s"), var);
        } else if (!strcmp(item.buf, "shallow")) {
                if (!me->overwrite && submodule->recommend_shallow != -1)
-                       warn_multiple_config(me->commit_sha1, submodule->name,
+                       warn_multiple_config(me->treeish_name, submodule->name,
                                             "shallow");
                else
                        submodule->recommend_shallow =
                                git_config_bool(var, value);
        } else if (!strcmp(item.buf, "branch")) {
                if (!me->overwrite && submodule->branch)
-                       warn_multiple_config(me->commit_sha1, submodule->name,
+                       warn_multiple_config(me->treeish_name, submodule->name,
                                             "branch");
                else {
                        free((void *)submodule->branch);
@@ -379,18 +379,18 @@ static int parse_config(const char *var, const char *value, void *data)
        return ret;
 }
 
-static int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
+static int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
                                      unsigned char *gitmodules_sha1,
                                      struct strbuf *rev)
 {
        int ret = 0;
 
-       if (is_null_sha1(commit_sha1)) {
+       if (is_null_sha1(treeish_name)) {
                hashclr(gitmodules_sha1);
                return 1;
        }
 
-       strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(commit_sha1));
+       strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(treeish_name));
        if (get_sha1(rev->buf, gitmodules_sha1) >= 0)
                ret = 1;
 
@@ -402,7 +402,7 @@ static int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
  * revisions.
  */
 static const struct submodule *config_from(struct submodule_cache *cache,
-               const unsigned char *commit_sha1, const char *key,
+               const unsigned char *treeish_name, const char *key,
                enum lookup_type lookup_type)
 {
        struct strbuf rev = STRBUF_INIT;
@@ -418,7 +418,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
         * return the first submodule. Can be used to check whether
         * there are any submodules parsed.
         */
-       if (!commit_sha1 || !key) {
+       if (!treeish_name || !key) {
                struct hashmap_iter iter;
                struct submodule_entry *entry;
 
@@ -428,7 +428,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
                return entry->config;
        }
 
-       if (!gitmodule_sha1_from_commit(commit_sha1, sha1, &rev))
+       if (!gitmodule_sha1_from_commit(treeish_name, sha1, &rev))
                goto out;
 
        switch (lookup_type) {
@@ -448,7 +448,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
 
        /* fill the submodule config into the cache */
        parameter.cache = cache;
-       parameter.commit_sha1 = commit_sha1;
+       parameter.treeish_name = treeish_name;
        parameter.gitmodules_sha1 = sha1;
        parameter.overwrite = 0;
        git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
@@ -471,18 +471,6 @@ static const struct submodule *config_from(struct submodule_cache *cache,
        return submodule;
 }
 
-static const struct submodule *config_from_path(struct submodule_cache *cache,
-               const unsigned char *commit_sha1, const char *path)
-{
-       return config_from(cache, commit_sha1, path, lookup_path);
-}
-
-static const struct submodule *config_from_name(struct submodule_cache *cache,
-               const unsigned char *commit_sha1, const char *name)
-{
-       return config_from(cache, commit_sha1, name, lookup_name);
-}
-
 static void ensure_cache_init(void)
 {
        if (is_cache_init)
@@ -496,7 +484,7 @@ int parse_submodule_config_option(const char *var, const char *value)
 {
        struct parse_config_parameter parameter;
        parameter.cache = &the_submodule_cache;
-       parameter.commit_sha1 = NULL;
+       parameter.treeish_name = NULL;
        parameter.gitmodules_sha1 = null_sha1;
        parameter.overwrite = 1;
 
@@ -504,18 +492,18 @@ int parse_submodule_config_option(const char *var, const char *value)
        return parse_config(var, value, &parameter);
 }
 
-const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
+const struct submodule *submodule_from_name(const unsigned char *treeish_name,
                const char *name)
 {
        ensure_cache_init();
-       return config_from_name(&the_submodule_cache, commit_sha1, name);
+       return config_from(&the_submodule_cache, treeish_name, name, lookup_name);
 }
 
-const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
+const struct submodule *submodule_from_path(const unsigned char *treeish_name,
                const char *path)
 {
        ensure_cache_init();
-       return config_from_path(&the_submodule_cache, commit_sha1, path);
+       return config_from(&the_submodule_cache, treeish_name, path, lookup_path);
 }
 
 void submodule_free(void)
index d05c542d2cdace181ea72055c0f71db6b1afda42..99df8e593cc7c274f88a73dfdd6524b7f661692c 100644 (file)
@@ -25,9 +25,9 @@ struct submodule {
 int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
 int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
 int parse_submodule_config_option(const char *var, const char *value);
-const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
+const struct submodule *submodule_from_name(const unsigned char *commit_or_tree,
                const char *name);
-const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
+const struct submodule *submodule_from_path(const unsigned char *commit_or_tree,
                const char *path);
 void submodule_free(void);
 
index 6f7d883de950af8ba6427537d09e021a4bed36bd..ece17315d671cf182f21c261d879c58f193cde09 100644 (file)
@@ -500,27 +500,67 @@ static int has_remote(const char *refname, const struct object_id *oid,
        return 1;
 }
 
-static int submodule_needs_pushing(const char *path, const unsigned char sha1[20])
+static int append_sha1_to_argv(const unsigned char sha1[20], void *data)
 {
-       if (add_submodule_odb(path) || !lookup_commit_reference(sha1))
+       struct argv_array *argv = data;
+       argv_array_push(argv, sha1_to_hex(sha1));
+       return 0;
+}
+
+static int check_has_commit(const unsigned char sha1[20], void *data)
+{
+       int *has_commit = data;
+
+       if (!lookup_commit_reference(sha1))
+               *has_commit = 0;
+
+       return 0;
+}
+
+static int submodule_has_commits(const char *path, struct sha1_array *commits)
+{
+       int has_commit = 1;
+
+       if (add_submodule_odb(path))
+               return 0;
+
+       sha1_array_for_each_unique(commits, check_has_commit, &has_commit);
+       return has_commit;
+}
+
+static int submodule_needs_pushing(const char *path, struct sha1_array *commits)
+{
+       if (!submodule_has_commits(path, commits))
+               /*
+                * NOTE: We do consider it safe to return "no" here. The
+                * correct answer would be "We do not know" instead of
+                * "No push needed", but it is quite hard to change
+                * the submodule pointer without having the submodule
+                * around. If a user did however change the submodules
+                * without having the submodule around, this indicates
+                * an expert who knows what they are doing or a
+                * maintainer integrating work from other people. In
+                * both cases it should be safe to skip this check.
+                */
                return 0;
 
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
                struct child_process cp = CHILD_PROCESS_INIT;
-               const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL};
                struct strbuf buf = STRBUF_INIT;
                int needs_pushing = 0;
 
-               argv[1] = sha1_to_hex(sha1);
-               cp.argv = argv;
+               argv_array_push(&cp.args, "rev-list");
+               sha1_array_for_each_unique(commits, append_sha1_to_argv, &cp.args);
+               argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL);
+
                prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.out = -1;
                cp.dir = path;
                if (start_command(&cp))
-                       die("Could not run 'git rev-list %s --not --remotes -n 1' command in submodule %s",
-                               sha1_to_hex(sha1), path);
+                       die("Could not run 'git rev-list <commits> --not --remotes -n 1' command in submodule %s",
+                                       path);
                if (strbuf_read(&buf, cp.out, 41))
                        needs_pushing = 1;
                finish_command(&cp);
@@ -532,19 +572,34 @@ static int submodule_needs_pushing(const char *path, const unsigned char sha1[20
        return 0;
 }
 
+static struct sha1_array *submodule_commits(struct string_list *submodules,
+                                           const char *path)
+{
+       struct string_list_item *item;
+
+       item = string_list_insert(submodules, path);
+       if (item->util)
+               return (struct sha1_array *) item->util;
+
+       /* NEEDSWORK: should we have sha1_array_init()? */
+       item->util = xcalloc(1, sizeof(struct sha1_array));
+       return (struct sha1_array *) item->util;
+}
+
 static void collect_submodules_from_diff(struct diff_queue_struct *q,
                                         struct diff_options *options,
                                         void *data)
 {
        int i;
-       struct string_list *needs_pushing = data;
+       struct string_list *submodules = data;
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
+               struct sha1_array *commits;
                if (!S_ISGITLINK(p->two->mode))
                        continue;
-               if (submodule_needs_pushing(p->two->path, p->two->oid.hash))
-                       string_list_insert(needs_pushing, p->two->path);
+               commits = submodule_commits(submodules, p->two->path);
+               sha1_array_append(commits, p->two->oid.hash);
        }
 }
 
@@ -560,46 +615,63 @@ static void find_unpushed_submodule_commits(struct commit *commit,
        diff_tree_combined_merge(commit, 1, &rev);
 }
 
-int find_unpushed_submodules(unsigned char new_sha1[20],
+static void free_submodules_sha1s(struct string_list *submodules)
+{
+       struct string_list_item *item;
+       for_each_string_list_item(item, submodules)
+               sha1_array_clear((struct sha1_array *) item->util);
+       string_list_clear(submodules, 1);
+}
+
+int find_unpushed_submodules(struct sha1_array *commits,
                const char *remotes_name, struct string_list *needs_pushing)
 {
        struct rev_info rev;
        struct commit *commit;
-       const char *argv[] = {NULL, NULL, "--not", "NULL", NULL};
-       int argc = ARRAY_SIZE(argv) - 1;
-       char *sha1_copy;
-
-       struct strbuf remotes_arg = STRBUF_INIT;
+       struct string_list submodules = STRING_LIST_INIT_DUP;
+       struct string_list_item *submodule;
+       struct argv_array argv = ARGV_ARRAY_INIT;
 
-       strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name);
        init_revisions(&rev, NULL);
-       sha1_copy = xstrdup(sha1_to_hex(new_sha1));
-       argv[1] = sha1_copy;
-       argv[3] = remotes_arg.buf;
-       setup_revisions(argc, argv, &rev, NULL);
+
+       /* argv.argv[0] will be ignored by setup_revisions */
+       argv_array_push(&argv, "find_unpushed_submodules");
+       sha1_array_for_each_unique(commits, append_sha1_to_argv, &argv);
+       argv_array_push(&argv, "--not");
+       argv_array_pushf(&argv, "--remotes=%s", remotes_name);
+
+       setup_revisions(argv.argc, argv.argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
 
        while ((commit = get_revision(&rev)) != NULL)
-               find_unpushed_submodule_commits(commit, needs_pushing);
+               find_unpushed_submodule_commits(commit, &submodules);
 
        reset_revision_walk();
-       free(sha1_copy);
-       strbuf_release(&remotes_arg);
+       argv_array_clear(&argv);
+
+       for_each_string_list_item(submodule, &submodules) {
+               struct sha1_array *commits = (struct sha1_array *) submodule->util;
+
+               if (submodule_needs_pushing(submodule->string, commits))
+                       string_list_insert(needs_pushing, submodule->string);
+       }
+       free_submodules_sha1s(&submodules);
 
        return needs_pushing->nr;
 }
 
-static int push_submodule(const char *path)
+static int push_submodule(const char *path, int dry_run)
 {
        if (add_submodule_odb(path))
                return 1;
 
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
                struct child_process cp = CHILD_PROCESS_INIT;
-               const char *argv[] = {"push", NULL};
+               argv_array_push(&cp.args, "push");
+               if (dry_run)
+                       argv_array_push(&cp.args, "--dry-run");
 
-               cp.argv = argv;
                prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
@@ -612,18 +684,20 @@ static int push_submodule(const char *path)
        return 1;
 }
 
-int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
+int push_unpushed_submodules(struct sha1_array *commits,
+                            const char *remotes_name,
+                            int dry_run)
 {
        int i, ret = 1;
        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
-       if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
+       if (!find_unpushed_submodules(commits, remotes_name, &needs_pushing))
                return 1;
 
        for (i = 0; i < needs_pushing.nr; i++) {
                const char *path = needs_pushing.items[i].string;
                fprintf(stderr, "Pushing submodule '%s'\n", path);
-               if (!push_submodule(path)) {
+               if (!push_submodule(path, dry_run)) {
                        fprintf(stderr, "Unable to push submodule '%s'\n", path);
                        ret = 0;
                }
index d9e197a948fdab44b7a5df4161a5df9c3a2938ff..23d76682b1ea123d040a29f6f9613c3e1794f87d 100644 (file)
@@ -3,6 +3,7 @@
 
 struct diff_options;
 struct argv_array;
+struct sha1_array;
 
 enum {
        RECURSE_SUBMODULES_CHECK = -4,
@@ -62,9 +63,11 @@ int submodule_uses_gitfile(const char *path);
 int ok_to_remove_submodule(const char *path);
 int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
                    const unsigned char a[20], const unsigned char b[20], int search);
-int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
+int find_unpushed_submodules(struct sha1_array *commits, const char *remotes_name,
                struct string_list *needs_pushing);
-int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
+extern int push_unpushed_submodules(struct sha1_array *commits,
+                                   const char *remotes_name,
+                                   int dry_run);
 void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
 int parallel_submodules(void);
 
index 27fe0405b887671ff0ca4cea1b258aa850e66c8c..d2a63bea4346fb76d38ba43508ee6e60599e41a9 100644 (file)
@@ -8,7 +8,7 @@ static struct lock_file index_lock;
 int cmd_main(int ac, const char **av)
 {
        setup_git_directory();
-       hold_locked_index(&index_lock, 1);
+       hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
        if (read_cache() < 0)
                die("unable to read index file");
        active_cache_tree = NULL;
index c3e631394f4a47f32e62e266431607861929f328..69174c6e3110d5e214c048aceccf07232b813ce7 100644 (file)
@@ -123,6 +123,7 @@ ScriptAlias /error/ error.sh/
 </Files>
 
 RewriteEngine on
+RewriteRule ^/dumb-redir/(.*)$ /dumb/$1 [R=301]
 RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
 RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
 RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301]
@@ -132,6 +133,19 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
 RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
 RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
 
+# The first rule issues a client-side redirect to something
+# that _doesn't_ look like a git repo. The second rule is a
+# server-side rewrite, so that it turns out the odd-looking
+# thing _is_ a git repo. The "[PT]" tells Apache to match
+# the usual ScriptAlias rules for /smart.
+RewriteRule ^/insane-redir/(.*)$ /intern-redir/$1/foo [R=301]
+RewriteRule ^/intern-redir/(.*)/foo$ /smart/$1 [PT]
+
+# Serve info/refs internally without redirecting, but
+# issue a redirect for any object requests.
+RewriteRule ^/redir-objects/(.*/info/refs)$ /dumb/$1 [PT]
+RewriteRule ^/redir-objects/(.*/objects/.*)$ /dumb/$1 [R=301]
+
 # Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
 # And as RewriteCond does not allow testing for non-matches, we match
 # the desired case first (one has abra, two has cadabra), and let it
index 4ea534e9fa70ea302d4a88a8bc922c100f896873..34891c4b1ad1c53ebc78b18484c7c146659ce68d 100755 (executable)
@@ -350,10 +350,9 @@ test_expect_success PERL 'required process filter should filter data' '
                cd repo &&
                git init &&
 
-               echo "git-stderr.log" >.gitignore &&
                echo "*.r filter=protocol" >.gitattributes &&
                git add . &&
-               git commit -m "test commit 1" &&
+               git commit -m "test commit 1" &&
                git branch empty-branch &&
 
                cp "$TEST_ROOT/test.o" test.r &&
@@ -378,7 +377,7 @@ test_expect_success PERL 'required process filter should filter data' '
                EOF
                test_cmp_count expected.log rot13-filter.log &&
 
-               filter_git commit -m "test commit 2" &&
+               filter_git commit -m "test commit 2" &&
                cat >expected.log <<-EOF &&
                        START
                        init handshake complete
index 2c10cb4693e28eecf7771978a2f3b702e5f3bd30..2cbf7b95907384b4b4b5ac91f801026810b85f0a 100755 (executable)
@@ -4,7 +4,7 @@
 use strict;
 use warnings;
 use POSIX qw(:locale_h);
-use Test::More tests => 8;
+use Test::More tests => 13;
 use Git::I18N;
 
 my $has_gettext_library = $Git::I18N::__HAS_LIBRARY;
@@ -31,6 +31,8 @@
        # more gettext wrapper functions.
        my %prototypes = (qw(
                __      $
+               __n     $$$
+               N__     $
        ));
        while (my ($sub, $proto) = each %prototypes) {
                is(prototype(\&{"Git::I18N::$sub"}), $proto, "sanity: $sub has a $proto prototype");
        my ($got, $expect) = (('TEST: A Perl test string') x 2);
 
        is(__($got), $expect, "Passing a string through __() in the C locale works");
+
+       my ($got_singular, $got_plural, $expect_singular, $expect_plural) =
+               (('TEST: 1 file', 'TEST: n files') x 2);
+
+       is(__n($got_singular, $got_plural, 1), $expect_singular,
+               "Get singular string through __n() in C locale");
+       is(__n($got_singular, $got_plural, 2), $expect_plural,
+               "Get plural string through __n() in C locale");
+
+       is(N__($got), $expect, "Passing a string through N__() in the C locale works");
 }
 
 # Test a basic message on different locales
index 7655c94c2801f0070a7b60ea17f30414b16c8d12..ff50960ccaed9953c5738c9bbf602bf0d326e15a 100755 (executable)
@@ -219,14 +219,8 @@ test_expect_success 'check line errors for malformed values' '
 '
 
 test_expect_success 'error on modifying repo config without repo' '
-       mkdir no-repo &&
-       (
-               GIT_CEILING_DIRECTORIES=$(pwd) &&
-               export GIT_CEILING_DIRECTORIES &&
-               cd no-repo &&
-               test_must_fail git config a.b c 2>err &&
-               grep "not in a git directory" err
-       )
+       nongit test_must_fail git config a.b c 2>err &&
+       grep "not in a git directory" err
 '
 
 cmdline_config="'foo.bar=from-cmdline'"
index 1b1b65a6b0fc7ace4e18e8642a6c47631d7e6a43..465eeeacd3de1971c7256c84583377b3b47d520b 100755 (executable)
@@ -96,4 +96,44 @@ test_expect_success 'bare repo cleanup' '
        rm -rf bare1
 '
 
+test_expect_success 'broken main worktree still at the top' '
+       git init broken-main &&
+       (
+               cd broken-main &&
+               test_commit new &&
+               git worktree add linked &&
+               cat >expected <<-EOF &&
+               worktree $(pwd)
+               HEAD $_z40
+
+               EOF
+               cd linked &&
+               echo "worktree $(pwd)" >expected &&
+               echo "ref: .broken" >../.git/HEAD &&
+               git worktree list --porcelain | head -n 3 >actual &&
+               test_cmp ../expected actual &&
+               git worktree list | head -n 1 >actual.2 &&
+               grep -F "(error)" actual.2
+       )
+'
+
+test_expect_success 'linked worktrees are sorted' '
+       mkdir sorted &&
+       git init sorted/main &&
+       (
+               cd sorted/main &&
+               test_tick &&
+               test_commit new &&
+               git worktree add ../first &&
+               git worktree add ../second &&
+               git worktree list --porcelain | grep ^worktree >actual
+       ) &&
+       cat >expected <<-EOF &&
+       worktree $(pwd)/sorted/main
+       worktree $(pwd)/sorted/first
+       worktree $(pwd)/sorted/second
+       EOF
+       test_cmp expected sorted/main/actual
+'
+
 test_done
index 470f33466ca561484da080a522add72bed51ad6e..9a893b5fe746f03521454972199ecb2d243d79ab 100755 (executable)
@@ -575,13 +575,13 @@ test_expect_success 'merge removes empty directories' '
        test_must_fail test -d d
 '
 
-test_expect_failure 'merge-recursive simple w/submodule' '
+test_expect_success 'merge-recursive simple w/submodule' '
 
        git checkout submod &&
        git merge remove
 '
 
-test_expect_failure 'merge-recursive simple w/submodule result' '
+test_expect_success 'merge-recursive simple w/submodule result' '
 
        git ls-files -s >actual &&
        (
index c6a3ccba1b992cff7412bcf8139da7557981de94..52283dfc8cdafabba485fa6896279d600230abf9 100755 (executable)
@@ -89,6 +89,11 @@ test_expect_success 'git branch --list -v pattern shows branch summaries' '
        awk "{print \$NF}" <tmp >actual &&
        test_cmp expect actual
 '
+test_expect_success 'git branch --ignore-case --list -v pattern shows branch summaries' '
+       git branch --list --ignore-case -v BRANCH* >tmp &&
+       awk "{print \$NF}" <tmp >actual &&
+       test_cmp expect actual
+'
 
 test_expect_success 'git branch -v pattern does not show branch summaries' '
        test_must_fail git branch -v branch*
@@ -196,4 +201,28 @@ test_expect_success 'local-branch symrefs shortened properly' '
        test_cmp expect actual
 '
 
+test_expect_success 'sort branches, ignore case' '
+       (
+               git init sort-icase &&
+               cd sort-icase &&
+               test_commit initial &&
+               git branch branch-one &&
+               git branch BRANCH-two &&
+               git branch --list | awk "{print \$NF}" >actual &&
+               cat >expected <<-\EOF &&
+               BRANCH-two
+               branch-one
+               master
+               EOF
+               test_cmp expected actual &&
+               git branch --list -i | awk "{print \$NF}" >actual &&
+               cat >expected <<-\EOF &&
+               branch-one
+               BRANCH-two
+               master
+               EOF
+               test_cmp expected actual
+       )
+'
+
 test_done
index a6a6c40a98512b190f8610391aa153a294e4b5cb..910f2182843476edf878bfc42f34d868c70d8cf5 100755 (executable)
@@ -99,4 +99,28 @@ testrebase() {
 testrebase "" .git/rebase-apply
 testrebase " --merge" .git/rebase-merge
 
+test_expect_success 'rebase --quit' '
+       cd "$work_dir" &&
+       # Clean up the state from the previous one
+       git reset --hard pre-rebase &&
+       test_must_fail git rebase master &&
+       test_path_is_dir .git/rebase-apply &&
+       head_before=$(git rev-parse HEAD) &&
+       git rebase --quit &&
+       test $(git rev-parse HEAD) = $head_before &&
+       test ! -d .git/rebase-apply
+'
+
+test_expect_success 'rebase --merge --quit' '
+       cd "$work_dir" &&
+       # Clean up the state from the previous one
+       git reset --hard pre-rebase &&
+       test_must_fail git rebase --merge master &&
+       test_path_is_dir .git/rebase-merge &&
+       head_before=$(git rev-parse HEAD) &&
+       git rebase --quit &&
+       test $(git rev-parse HEAD) = $head_before &&
+       test ! -d .git/rebase-merge
+'
+
 test_done
index d5b896d4459e82cdc7eb363af77d8eef28871229..ebf4f5e4b2c1c1cc20a49888df14cedd14b49a8b 100755 (executable)
@@ -38,9 +38,6 @@ git_rebase_interactive () {
        git rebase -i "$1"
 }
 
-KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
-# The real reason "replace directory with submodule" fails is because a
-# directory "sub1" exists, but we reuse the suppression added for merge here
 test_submodule_switch "git_rebase_interactive"
 
 test_done
index 394f0005a195c1fbc713226c97ecee5cf4600007..4f2a263b63e14348032959059c15b160fecba39c 100755 (executable)
@@ -141,4 +141,16 @@ test_expect_success 'cherry-pick "-" works with arguments' '
        test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick works with dirty renamed file' '
+       test_commit to-rename &&
+       git checkout -b unrelated &&
+       test_commit unrelated &&
+       git checkout @{-1} &&
+       git mv to-rename.t renamed &&
+       test_tick &&
+       git commit -m renamed &&
+       echo modified >renamed &&
+       git cherry-pick refs/heads/unrelated
+'
+
 test_done
index 7b7a89dbd5ce578e0a722345a00f247e383689ef..372307c21b983437b870ac65ff4f91d2c36fab30 100755 (executable)
@@ -147,6 +147,16 @@ test_expect_success '--abort to cancel single cherry-pick' '
        git diff-index --exit-code HEAD
 '
 
+test_expect_success '--abort does not unsafely change HEAD' '
+       pristine_detach initial &&
+       test_must_fail git cherry-pick picked anotherpick &&
+       git reset --hard base &&
+       test_must_fail git cherry-pick picked anotherpick &&
+       git cherry-pick --abort 2>actual &&
+       test_i18ngrep "You seem to have moved HEAD" actual &&
+       test_cmp_rev base HEAD
+'
+
 test_expect_success 'cherry-pick --abort to cancel multiple revert' '
        pristine_detach anotherpick &&
        test_expect_code 1 git revert base..picked &&
index 9cce5ae8815a11e3453f5669b0592f7f7659009b..bf0a5c9887235afb5e336a34815d6bb3d64875d8 100755 (executable)
@@ -25,9 +25,8 @@ Signed-off-by: B.U. Thor <buthor@example.com>"
 
 mesg_broken_footer="$mesg_no_footer
 
-The signed-off-by string should begin with the words Signed-off-by followed
-by a colon and space, and then the signers name and email address. e.g.
-Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+This is not recognized as a footer because Myfooter is not a recognized token.
+Myfooter: A.U. Thor <author@example.com>"
 
 mesg_with_footer_sob="$mesg_with_footer
 Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
@@ -112,6 +111,17 @@ test_expect_success 'cherry-pick -s inserts blank line after non-conforming foot
        test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -s recognizes trailer config' '
+       pristine_detach initial &&
+       git -c "trailer.Myfooter.ifexists=add" cherry-pick -s mesg-broken-footer &&
+       cat <<-EOF >expect &&
+               $mesg_broken_footer
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'cherry-pick -x inserts blank line when conforming footer not found' '
        pristine_detach initial &&
        sha1=$(git rev-parse mesg-no-footer^0) &&
index 14f0edca2b6f67e6ae98379702b5566e4083d8e0..bcbb68065113f6a2032a3879fabb3353c42e79d0 100755 (executable)
@@ -111,21 +111,21 @@ test_expect_success 'Remove nonexistent file with --ignore-unmatch' '
 '
 
 test_expect_success '"rm" command printed' '
-       echo frotz > test-file &&
+       echo frotz >test-file &&
        git add test-file &&
        git commit -m "add file for rm test" &&
-       git rm test-file > rm-output &&
+       git rm test-file >rm-output &&
        test $(grep "^rm " rm-output | wc -l) = 1 &&
        rm -f test-file rm-output &&
        git commit -m "remove file from rm test"
 '
 
 test_expect_success '"rm" command suppressed with --quiet' '
-       echo frotz > test-file &&
+       echo frotz >test-file &&
        git add test-file &&
        git commit -m "add file for rm --quiet test" &&
-       git rm --quiet test-file > rm-output &&
-       test $(wc -l < rm-output) = 0 &&
+       git rm --quiet test-file >rm-output &&
+       test_must_be_empty rm-output &&
        rm -f test-file rm-output &&
        git commit -m "remove file from rm --quiet test"
 '
@@ -221,7 +221,7 @@ test_expect_success 'Call "rm" from outside the work tree' '
        mkdir repo &&
        (cd repo &&
         git init &&
-        echo something > somefile &&
+        echo something >somefile &&
         git add somefile &&
         git commit -m "add a file" &&
         (cd .. &&
@@ -287,7 +287,7 @@ test_expect_success 'rm removes empty submodules from work tree' '
        git commit -m "add submodule" &&
        git rm submod &&
        test ! -e submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
        test_must_fail git config -f .gitmodules submodule.sub.path
@@ -298,7 +298,7 @@ test_expect_success 'rm removes removed submodule from index and .gitmodules' '
        git submodule update &&
        rm -rf submod &&
        git rm submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
        test_must_fail git config -f .gitmodules submodule.sub.path
@@ -309,7 +309,7 @@ test_expect_success 'rm removes work tree of unmodified submodules' '
        git submodule update &&
        git rm submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
        test_must_fail git config -f .gitmodules submodule.sub.path
@@ -320,7 +320,7 @@ test_expect_success 'rm removes a submodule with a trailing /' '
        git submodule update &&
        git rm submod/ &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
@@ -335,17 +335,15 @@ test_expect_success 'rm succeeds when given a directory with a trailing /' '
 test_expect_success 'rm of a populated submodule with different HEAD fails unless forced' '
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
-               git checkout HEAD^
-       ) &&
+       git -C submod checkout HEAD^ &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
        test_must_fail git config -f .gitmodules submodule.sub.path
@@ -418,34 +416,30 @@ test_expect_success 'rm issues a warning when section is not found in .gitmodule
 test_expect_success 'rm of a populated submodule with modifications fails unless forced' '
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
-               echo X >empty
-       ) &&
+       echo X >submod/empty &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'rm of a populated submodule with untracked files fails unless forced' '
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
-               echo X >untracked
-       ) &&
+       echo X >submod/untracked &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
@@ -461,16 +455,12 @@ test_expect_success 'setup submodule conflict' '
        git add nitfol &&
        git commit -m "added nitfol 2" &&
        git checkout -b conflict1 master &&
-       (cd submod &&
-               git fetch &&
-               git checkout branch1
-       ) &&
+       git -C submod fetch &&
+       git -C submod checkout branch1 &&
        git add submod &&
        git commit -m "submod 1" &&
        git checkout -b conflict2 master &&
-       (cd submod &&
-               git checkout branch2
-       ) &&
+       git -C submod checkout branch2 &&
        git add submod &&
        git commit -m "submod 2"
 '
@@ -486,7 +476,7 @@ test_expect_success 'rm removes work tree of unmodified conflicted submodule' '
        test_must_fail git merge conflict2 &&
        git rm submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
@@ -494,18 +484,16 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD
        git checkout conflict1 &&
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
-               git checkout HEAD^
-       ) &&
+       git -C submod checkout HEAD^ &&
        test_must_fail git merge conflict2 &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
        test_must_fail git config -f .gitmodules submodule.sub.path
@@ -515,18 +503,16 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f
        git checkout conflict1 &&
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
-               echo X >empty
-       ) &&
+       echo X >submod/empty &&
        test_must_fail git merge conflict2 &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
        test_must_fail git config -f .gitmodules submodule.sub.path
@@ -536,18 +522,16 @@ test_expect_success 'rm of a conflicted populated submodule with untracked files
        git checkout conflict1 &&
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
-               echo X >untracked
-       ) &&
+       echo X >submod/untracked &&
        test_must_fail git merge conflict2 &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
@@ -564,12 +548,12 @@ test_expect_success 'rm of a conflicted populated submodule with a .git director
        test_must_fail git rm submod &&
        test -d submod &&
        test -d submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        test_must_fail git rm -f submod &&
        test -d submod &&
        test -d submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        git merge --abort &&
        rm -rf submod
@@ -581,7 +565,7 @@ test_expect_success 'rm of a conflicted unpopulated submodule succeeds' '
        test_must_fail git merge conflict2 &&
        git rm submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
@@ -597,12 +581,12 @@ test_expect_success 'rm of a populated submodule with a .git directory fails eve
        test_must_fail git rm submod &&
        test -d submod &&
        test -d submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        ! test -s actual &&
        test_must_fail git rm -f submod &&
        test -d submod &&
        test -d submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        ! test -s actual &&
        rm -rf submod
 '
@@ -629,58 +613,52 @@ test_expect_success 'setup subsubmodule' '
 test_expect_success 'rm recursively removes work tree of unmodified submodules' '
        git rm submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'rm of a populated nested submodule with different nested HEAD fails unless forced' '
        git reset --hard &&
        git submodule update --recursive &&
-       (cd submod/subsubmod &&
-               git checkout HEAD^
-       ) &&
+       git -C submod/subsubmod checkout HEAD^ &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'rm of a populated nested submodule with nested modifications fails unless forced' '
        git reset --hard &&
        git submodule update --recursive &&
-       (cd submod/subsubmod &&
-               echo X >empty
-       ) &&
+       echo X >submod/subsubmod/empty &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'rm of a populated nested submodule with nested untracked files fails unless forced' '
        git reset --hard &&
        git submodule update --recursive &&
-       (cd submod/subsubmod &&
-               echo X >untracked
-       ) &&
+       echo X >submod/subsubmod/untracked &&
        test_must_fail git rm submod &&
        test -d submod &&
        test -f submod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified actual &&
        git rm -f submod &&
        test ! -d submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
@@ -695,12 +673,12 @@ test_expect_success 'rm of a populated nested submodule with a nested .git direc
        test_must_fail git rm submod &&
        test -d submod &&
        test -d submod/subsubmod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        ! test -s actual &&
        test_must_fail git rm -f submod &&
        test -d submod &&
        test -d submod/subsubmod/.git &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        ! test -s actual &&
        rm -rf submod
 '
@@ -709,14 +687,14 @@ test_expect_success 'checking out a commit after submodule removal needs manual
        git commit -m "submodule removal" submod &&
        git checkout HEAD^ &&
        git submodule update &&
-       git checkout -q HEAD^ 2>actual &&
+       git checkout -q HEAD^ &&
        git checkout -q master 2>actual &&
        test_i18ngrep "^warning: unable to rmdir submod:" actual &&
        git status -s submod >actual &&
        echo "?? submod/" >expected &&
        test_cmp expected actual &&
        rm -rf submod &&
-       git status -s -uno --ignore-submodules=none > actual &&
+       git status -s -uno --ignore-submodules=none >actual &&
        ! test -s actual
 '
 
index e1a6ccc00c5988cfa8808bccc49a5e21703166b6..2de3e18ce68a94111c8dfcca858b8515ea8919c1 100755 (executable)
@@ -766,4 +766,13 @@ test_expect_success 'stash list --cc shows combined diff' '
        test_cmp expect actual
 '
 
+test_expect_success 'stash is not confused by partial renames' '
+       mv file renamed &&
+       git add renamed &&
+       git stash &&
+       git stash apply &&
+       test_path_is_file renamed &&
+       test_path_is_missing file
+'
+
 test_done
index 566817e2efdccc4747e871f3bdabb90fa5b134ec..d09acfe48e81b2567fd7408d7cb6345c73d99754 100755 (executable)
@@ -311,6 +311,13 @@ diff --line-prefix=abc master master^ side
 diff --dirstat master~1 master~2
 diff --dirstat initial rearrange
 diff --dirstat-by-file initial rearrange
+# No-index --abbrev and --no-abbrev
+diff --raw initial
+diff --raw --abbrev=4 initial
+diff --raw --no-abbrev initial
+diff --no-index --raw dir2 dir
+diff --no-index --raw --abbrev=4 dir2 dir
+diff --no-index --raw --no-abbrev dir2 dir
 EOF
 
 test_expect_success 'log -S requires an argument' '
diff --git a/t/t4013/diff.diff_--no-index_--raw_--abbrev=4_dir2_dir b/t/t4013/diff.diff_--no-index_--raw_--abbrev=4_dir2_dir
new file mode 100644 (file)
index 0000000..a71b38a
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff --no-index --raw --abbrev=4 dir2 dir
+:000000 100644 0000... 0000... A       dir/sub
+$
diff --git a/t/t4013/diff.diff_--no-index_--raw_--no-abbrev_dir2_dir b/t/t4013/diff.diff_--no-index_--raw_--no-abbrev_dir2_dir
new file mode 100644 (file)
index 0000000..e0f0097
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff --no-index --raw --no-abbrev dir2 dir
+:000000 100644 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 A     dir/sub
+$
diff --git a/t/t4013/diff.diff_--no-index_--raw_dir2_dir b/t/t4013/diff.diff_--no-index_--raw_dir2_dir
new file mode 100644 (file)
index 0000000..3cb4ee7
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff --no-index --raw dir2 dir
+:000000 100644 0000000... 0000000... A dir/sub
+$
diff --git a/t/t4013/diff.diff_--raw_--abbrev=4_initial b/t/t4013/diff.diff_--raw_--abbrev=4_initial
new file mode 100644 (file)
index 0000000..c3641db
--- /dev/null
@@ -0,0 +1,6 @@
+$ git diff --raw --abbrev=4 initial
+:100644 100644 35d2... 9929... M       dir/sub
+:100644 100644 01e7... 10a8... M       file0
+:000000 100644 0000... b1e6... A       file1
+:100644 000000 01e7... 0000... D       file2
+$
diff --git a/t/t4013/diff.diff_--raw_--no-abbrev_initial b/t/t4013/diff.diff_--raw_--no-abbrev_initial
new file mode 100644 (file)
index 0000000..c87a125
--- /dev/null
@@ -0,0 +1,6 @@
+$ git diff --raw --no-abbrev initial
+:100644 100644 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e 992913c5aa0a5476d10c49ed0f21fc0c6d1aedf3 M     dir/sub
+:100644 100644 01e79c32a8c99c557f0757da7cb6d65b3414466d 10a8a9f3657f91a156b9f0184ed79a20adef9f7f M     file0
+:000000 100644 0000000000000000000000000000000000000000 b1e67221afe8461efd244b487afca22d46b95eb8 A     file1
+:100644 000000 01e79c32a8c99c557f0757da7cb6d65b3414466d 0000000000000000000000000000000000000000 D     file2
+$
diff --git a/t/t4013/diff.diff_--raw_initial b/t/t4013/diff.diff_--raw_initial
new file mode 100644 (file)
index 0000000..a3e9780
--- /dev/null
@@ -0,0 +1,6 @@
+$ git diff --raw initial
+:100644 100644 35d242b... 992913c... M dir/sub
+:100644 100644 01e79c3... 10a8a9f... M file0
+:000000 100644 0000000... b1e6722... A file1
+:100644 000000 01e79c3... 0000000... D file2
+$
index ba4902df2b605f89ec2abcac50abefc2f23fc9bf..482112ca339f05fc31e6f9c2c6168971a1121e87 100755 (executable)
@@ -1294,8 +1294,7 @@ EOF
 4:Subject: [PATCH] subject
 8:
 10:Signed-off-by: example happens to be wrapped here.
-11:
-12:Signed-off-by: C O Mitter <committer@example.com>
+11:Signed-off-by: C O Mitter <committer@example.com>
 EOF
        test_cmp expected actual
 '
@@ -1368,7 +1367,7 @@ EOF
        test_cmp expected actual
 '
 
-test_expect_success 'signoff: detect garbage in non-conforming footer' '
+test_expect_success 'signoff: tolerate garbage in conforming footer' '
        append_signoff <<\EOF >actual &&
 subject
 
@@ -1383,8 +1382,36 @@ EOF
 8:
 10:
 13:Signed-off-by: C O Mitter <committer@example.com>
-14:
-15:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: respect trailer config' '
+       append_signoff <<\EOF >actual &&
+subject
+
+Myfooter: x
+Some Trash
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+11:
+12:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual &&
+
+       test_config trailer.Myfooter.ifexists add &&
+       append_signoff <<\EOF >actual &&
+subject
+
+Myfooter: x
+Some Trash
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+11:Signed-off-by: C O Mitter <committer@example.com>
 EOF
        test_cmp expected actual
 '
index f5435fd250baf7415fb6a205f4497cb5c118de65..21eb8c8587f2b4de01595d91d27c8ec6706d2a00 100755 (executable)
@@ -535,4 +535,30 @@ test_expect_success 'clean log decoration' '
        test_cmp expected actual1
 '
 
+cat >trailers <<EOF
+Signed-off-by: A U Thor <author@example.com>
+Acked-by: A U Thor <author@example.com>
+[ v2 updated patch description ]
+Signed-off-by: A U Thor <author@example.com>
+EOF
+
+test_expect_success 'pretty format %(trailers) shows trailers' '
+       echo "Some contents" >trailerfile &&
+       git add trailerfile &&
+       git commit -F - <<-EOF &&
+       trailers: this commit message has trailers
+
+       This commit is a test commit with trailers at the end. We parse this
+       message and display the trailers using %bT
+
+       $(cat trailers)
+       EOF
+       git log --no-walk --pretty="%(trailers)" >actual &&
+       cat >expect <<-EOF &&
+       $(cat trailers)
+
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index 830bf2a2f6d4d85358a54f58f74b4ed9fdc954d5..886b6953e40f9fceae18642ebec031e0bdf511e3 100755 (executable)
@@ -94,20 +94,6 @@ check_tar() {
        '
 }
 
-# run "$@" inside a non-git directory
-nongit () {
-       test -d non-repo ||
-       mkdir non-repo ||
-       return 1
-
-       (
-               GIT_CEILING_DIRECTORIES=$(pwd) &&
-               export GIT_CEILING_DIRECTORIES &&
-               cd non-repo &&
-               "$@"
-       )
-}
-
 test_expect_success \
     'populate workdir' \
     'mkdir a &&
index 899e52d50f0d73e4a755c4f7991364f34912b336..43a672c3451146f800852a6d8688c10e497b47cf 100755 (executable)
@@ -406,6 +406,21 @@ test_expect_success 'verify resulting packs' '
        git verify-pack test-11-*.pack
 '
 
+test_expect_success 'set up pack for non-repo tests' '
+       # make sure we have a pack with no matching index file
+       cp test-1-*.pack foo.pack
+'
+
+test_expect_success 'index-pack --stdin complains of non-repo' '
+       nongit test_must_fail git index-pack --stdin <foo.pack &&
+       test_path_is_missing non-repo/.git
+'
+
+test_expect_success 'index-pack <pack> works in non-repo' '
+       nongit git index-pack ../foo.pack &&
+       test_path_is_file foo.idx
+'
+
 #
 # WARNING!
 #
index 551844584fc7b8ff71667dd61fcba62889ded69f..17f4d0fe4e7244cb58eaedcf41bcaa9c7b157bb4 100755 (executable)
@@ -255,6 +255,23 @@ test_expect_success '--rebase' '
        test new = "$(git show HEAD:file2)"
 '
 
+test_expect_success '--rebase fast forward' '
+       git reset --hard before-rebase &&
+       git checkout -b ff &&
+       echo another modification >file &&
+       git commit -m third file &&
+
+       git checkout to-rebase &&
+       git pull --rebase . ff &&
+       test "$(git rev-parse HEAD)" = "$(git rev-parse ff)" &&
+
+       # The above only validates the result.  Did we actually bypass rebase?
+       git reflog -1 >reflog.actual &&
+       sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+       echo "OBJID HEAD@{0}: pull --rebase . ff: Fast-forward" >reflog.expected &&
+       test_cmp reflog.expected reflog.fuzzy
+'
+
 test_expect_success '--rebase with conflicts shows advice' '
        test_when_finished "git rebase --abort; git checkout -f to-rebase" &&
        git checkout -b seq &&
index 73f4bb63465e50aa12de824859903e5293696a85..44309566f13431fe5e70f834b4cd6bb0a674624e 100755 (executable)
@@ -98,6 +98,16 @@ test_expect_success 'push from/to new branch with upstream, matching and simple'
        test_push_failure upstream
 '
 
+test_expect_success 'push ambiguously named branch with upstream, matching and simple' '
+       git checkout -b ambiguous &&
+       test_config branch.ambiguous.remote parent1 &&
+       test_config branch.ambiguous.merge refs/heads/ambiguous &&
+       git tag ambiguous &&
+       test_push_success simple ambiguous &&
+       test_push_success matching ambiguous &&
+       test_push_success upstream ambiguous
+'
+
 test_expect_success 'push from/to new branch with current creates remote branch' '
        test_config branch.new-branch.remote repo1 &&
        git checkout new-branch &&
index 198ce8475416be494fc023de3835828332da424d..1524ff5ba692d9c962ec51ef0c7948aca6ddf240 100755 (executable)
@@ -427,7 +427,31 @@ test_expect_success 'push unpushable submodule recursively fails' '
                cd submodule.git &&
                git rev-parse master >../actual
        ) &&
+       test_when_finished git -C work reset --hard master^ &&
        test_cmp expected actual
 '
 
+test_expect_success 'push --dry-run does not recursively update submodules' '
+       (
+               cd work/gar/bage &&
+               git checkout master &&
+               git rev-parse master >../../../expected_submodule &&
+               > junk9 &&
+               git add junk9 &&
+               git commit -m "Ninth junk" &&
+
+               # Go up to 'work' directory
+               cd ../.. &&
+               git checkout master &&
+               git rev-parse master >../expected_pub &&
+               git add gar/bage &&
+               git commit -m "Ninth commit for gar/bage" &&
+               git push --dry-run --recurse-submodules=on-demand ../pub.git master
+       ) &&
+       git -C submodule.git rev-parse master >actual_submodule &&
+       git -C pub.git rev-parse master >actual_pub &&
+       test_cmp expected_pub actual_pub &&
+       test_cmp expected_submodule actual_submodule
+'
+
 test_done
index 1e5d32d068539098598b23fb3b489688ac140169..af9fcd833a5e9e4997dd5cb917ac13f4b07f10ab 100755 (executable)
@@ -33,4 +33,29 @@ test_expect_success 'rejected objects are removed' '
        test_cmp expect actual
 '
 
+test_expect_success 'push to repo path with path separator (colon)' '
+       # The interesting failure case here is when the
+       # receiving end cannot access its original object directory,
+       # so make it likely for us to generate a delta by having
+       # a non-trivial file with multiple versions.
+
+       test-genrandom foo 4096 >file.bin &&
+       git add file.bin &&
+       git commit -m bin &&
+
+       if test_have_prereq MINGW
+       then
+               pathsep=";"
+       else
+               pathsep=":"
+       fi &&
+       git clone --bare . "xxx${pathsep}yyy.git" &&
+
+       echo change >>file.bin &&
+       git commit -am change &&
+       # Note that we have to use the full path here, or it gets confused
+       # with the ssh host:path syntax.
+       git push "$(pwd)/xxx${pathsep}yyy.git" HEAD
+'
+
 test_done
index 7641417b4a3848fa9d065572e5a8248fea5c7574..264a1ab8b0ea794ce398d9d5c3a40adb31bd0ff9 100755 (executable)
@@ -307,5 +307,66 @@ test_expect_success 'remote-http complains cleanly about malformed urls' '
        test_must_fail git remote-http http::/example.com/repo.git
 '
 
+test_expect_success 'redirects can be forbidden/allowed' '
+       test_must_fail git -c http.followRedirects=false \
+               clone $HTTPD_URL/dumb-redir/repo.git dumb-redir &&
+       git -c http.followRedirects=true \
+               clone $HTTPD_URL/dumb-redir/repo.git dumb-redir 2>stderr
+'
+
+test_expect_success 'redirects are reported to stderr' '
+       # just look for a snippet of the redirected-to URL
+       test_i18ngrep /dumb/ stderr
+'
+
+test_expect_success 'non-initial redirects can be forbidden' '
+       test_must_fail git -c http.followRedirects=initial \
+               clone $HTTPD_URL/redir-objects/repo.git redir-objects &&
+       git -c http.followRedirects=true \
+               clone $HTTPD_URL/redir-objects/repo.git redir-objects
+'
+
+test_expect_success 'http.followRedirects defaults to "initial"' '
+       test_must_fail git clone $HTTPD_URL/redir-objects/repo.git default
+'
+
+# The goal is for a clone of the "evil" repository, which has no objects
+# itself, to cause the client to fetch objects from the "victim" repository.
+test_expect_success 'set up evil alternates scheme' '
+       victim=$HTTPD_DOCUMENT_ROOT_PATH/victim.git &&
+       git init --bare "$victim" &&
+       git -C "$victim" --work-tree=. commit --allow-empty -m secret &&
+       git -C "$victim" repack -ad &&
+       git -C "$victim" update-server-info &&
+       sha1=$(git -C "$victim" rev-parse HEAD) &&
+
+       evil=$HTTPD_DOCUMENT_ROOT_PATH/evil.git &&
+       git init --bare "$evil" &&
+       # do this by hand to avoid object existence check
+       printf "%s\\t%s\\n" $sha1 refs/heads/master >"$evil/info/refs"
+'
+
+# Here we'll just redirect via HTTP. In a real-world attack these would be on
+# different servers, but we should reject it either way.
+test_expect_success 'http-alternates is a non-initial redirect' '
+       echo "$HTTPD_URL/dumb/victim.git/objects" \
+               >"$evil/objects/info/http-alternates" &&
+       test_must_fail git -c http.followRedirects=initial \
+               clone $HTTPD_URL/dumb/evil.git evil-initial &&
+       git -c http.followRedirects=true \
+               clone $HTTPD_URL/dumb/evil.git evil-initial
+'
+
+# Curl supports a lot of protocols that we'd prefer not to allow
+# http-alternates to use, but it's hard to test whether curl has
+# accessed, say, the SMTP protocol, because we are not running an SMTP server.
+# But we can check that it does not allow access to file://, which would
+# otherwise allow this clone to complete.
+test_expect_success 'http-alternates cannot point at funny protocols' '
+       echo "file://$victim/objects" >"$evil/objects/info/http-alternates" &&
+       test_must_fail git -c http.followRedirects=true \
+               clone "$HTTPD_URL/dumb/evil.git" evil-file
+'
+
 stop_httpd
 test_done
index 1ec5b2747a2100d3ae043cde5a2436eed2d52aec..6e5b9e42fb6d3f8c25c1a08388c4eb5ec3284500 100755 (executable)
@@ -119,6 +119,10 @@ test_expect_success 'redirects re-root further requests' '
        git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
 '
 
+test_expect_success 're-rooting dies on insane schemes' '
+       test_must_fail git clone $HTTPD_URL/insane-redir/repo.git insane
+'
+
 test_expect_success 'clone from password-protected repository' '
        echo two >expect &&
        set_askpass user@host pass@host &&
index eec4137ca5b392743e29068f4fffae1ffb78d812..26ebb0375deb67be38974c7c5610f090a5a2a9aa 100755 (executable)
@@ -68,4 +68,22 @@ test_expect_success 'access alternate via relative path (subdir)' '
        EOF
 '
 
+# set variables outside test to avoid quote insanity; the \057 is '/',
+# which doesn't need quoting, but just confirms that de-quoting
+# is working.
+quoted='"one.git\057objects"'
+unquoted='two.git/objects'
+test_expect_success 'mix of quoted and unquoted alternates' '
+       check_obj "$quoted:$unquoted" <<-EOF
+       $one blob
+       $two blob
+'
+
+test_expect_success !MINGW 'broken quoting falls back to interpreting raw' '
+       mv one.git \"one.git &&
+       check_obj \"one.git/objects <<-EOF
+       $one blob
+       EOF
+'
+
 test_done
index 0d105d54174e061b20707de808b284314f730667..044cc152f83b05e090a65268c8818d56adb4422b 100755 (executable)
@@ -18,6 +18,7 @@ test_proto "smart http" http "$HTTPD_URL/smart/repo.git"
 
 test_expect_success 'curl redirects respect whitelist' '
        test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
+                          GIT_SMART_HTTP=0 \
                git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
        {
                test_i18ngrep "ftp.*disabled" stderr ||
index 64a9850e3197dc2cbff00593f04a5fa196dbd632..8c617981a3a06c1ff49f03a2914bfdc31c0e293c 100755 (executable)
@@ -83,12 +83,24 @@ test_expect_success 'final^1^@ = final^1^1 final^1^2' '
        test_cmp expect actual
 '
 
+test_expect_success 'symbolic final^1^@ = final^1^1 final^1^2' '
+       git rev-parse --symbolic final^1^1 final^1^2 >expect &&
+       git rev-parse --symbolic final^1^@ >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' '
        git rev-parse final^1 ^final^1^1 ^final^1^2 >expect &&
        git rev-parse final^1^! >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'symbolic final^1^! = final^1 ^final^1^1 ^final^1^2' '
+       git rev-parse --symbolic final^1 ^final^1^1 ^final^1^2 >expect &&
+       git rev-parse --symbolic final^1^! >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'large graft octopus' '
        test_cmp_rev_output b31 "git rev-parse --verify b1^30"
 '
@@ -143,6 +155,12 @@ test_expect_success 'rev-parse merge^-2 = merge^2..merge' '
        test_cmp expect actual
 '
 
+test_expect_success 'symbolic merge^-1 = merge^1..merge' '
+       git rev-parse --symbolic merge^1..merge >expect &&
+       git rev-parse --symbolic merge^-1 >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'rev-parse merge^-0 (invalid parent)' '
        test_must_fail git rev-parse merge^-0
 '
index 039509a9cb94ef5c653df09e0453ac83157bf184..aea1dfc7148e4885e52a579ca60ee622c49b5693 100755 (executable)
@@ -563,4 +563,29 @@ test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
        test_cmp expect actual
 '
 
+cat >trailers <<EOF
+Reviewed-by: A U Thor <author@example.com>
+Signed-off-by: A U Thor <author@example.com>
+EOF
+
+test_expect_success 'basic atom: head contents:trailers' '
+       echo "Some contents" > two &&
+       git add two &&
+       git commit -F - <<-EOF &&
+       trailers: this commit message has trailers
+
+       Some message contents
+
+       $(cat trailers)
+       EOF
+       git for-each-ref --format="%(contents:trailers)" refs/heads/master >actual &&
+       sanitize_pgp <actual >actual.clean &&
+       # git for-each-ref ends with a blank line
+       cat >expect <<-EOF &&
+       $(cat trailers)
+
+       EOF
+       test_cmp expect actual.clean
+'
+
 test_done
index 8b0f71a2ac15d65f977b0daa2a53ad47b64a043a..07869b0c09da1313dd5564c2318dc5d44818f3a9 100755 (executable)
@@ -27,6 +27,30 @@ test_expect_success 'listing all tags in an empty tree should output nothing' '
        test $(git tag | wc -l) -eq 0
 '
 
+test_expect_success 'sort tags, ignore case' '
+       (
+               git init sort &&
+               cd sort &&
+               test_commit initial &&
+               git tag tag-one &&
+               git tag TAG-two &&
+               git tag -l >actual &&
+               cat >expected <<-\EOF &&
+               TAG-two
+               initial
+               tag-one
+               EOF
+               test_cmp expected actual &&
+               git tag -l -i >actual &&
+               cat >expected <<-\EOF &&
+               initial
+               tag-one
+               TAG-two
+               EOF
+               test_cmp expected actual
+       )
+'
+
 test_expect_success 'looking for a tag in an empty tree should fail' \
        '! (tag_exists mytag)'
 
@@ -81,6 +105,9 @@ test_expect_success 'listing all tags if one exists should output that tag' '
 test_expect_success 'listing a tag using a matching pattern should succeed' \
        'git tag -l mytag'
 
+test_expect_success 'listing a tag with --ignore-case' \
+       'test $(git tag -l --ignore-case MYTAG) = mytag'
+
 test_expect_success \
        'listing a tag using a matching pattern should output that tag' \
        'test $(git tag -l mytag) = mytag'
index 1c1e289ffd982ab74ed80fb776b3ecc4d7abf2f1..e159fc5035d08eee2c1577afd2ac792ddce2934c 100755 (executable)
@@ -125,4 +125,70 @@ test_expect_success 'ignoring missing submodule alternates passes clone and subm
        )
 '
 
+test_expect_success 'preparing second superproject with a nested submodule plus partial clone' '
+       test_create_repo supersuper &&
+       (
+               cd supersuper &&
+               echo "I am super super." >file &&
+               git add file &&
+               git commit -m B-super-super-initial
+               git submodule add "file://$base_dir/super" subwithsub &&
+               git commit -m B-super-super-added &&
+               git submodule update --init --recursive &&
+               git repack -ad
+       ) &&
+       git clone supersuper supersuper2 &&
+       (
+               cd supersuper2 &&
+               git submodule update --init
+       )
+'
+
+# At this point there are three root-level positories: A, B, super and super2
+
+test_expect_success 'nested submodule alternate in works and is actually used' '
+       test_when_finished "rm -rf supersuper-clone" &&
+       git clone --recursive --reference supersuper supersuper supersuper-clone &&
+       (
+               cd supersuper-clone &&
+               # test superproject has alternates setup correctly
+               test_alternate_is_used .git/objects/info/alternates . &&
+               # immediate submodule has alternate:
+               test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub &&
+               # nested submodule also has alternate:
+               test_alternate_is_used .git/modules/subwithsub/modules/sub/objects/info/alternates subwithsub/sub
+       )
+'
+
+check_that_two_of_three_alternates_are_used() {
+       test_alternate_is_used .git/objects/info/alternates . &&
+       # immediate submodule has alternate:
+       test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub &&
+       # but nested submodule has no alternate:
+       test_must_fail test_alternate_is_used .git/modules/subwithsub/modules/sub/objects/info/alternates subwithsub/sub
+}
+
+
+test_expect_success 'missing nested submodule alternate fails clone and submodule update' '
+       test_when_finished "rm -rf supersuper-clone" &&
+       test_must_fail git clone --recursive --reference supersuper2 supersuper2 supersuper-clone &&
+       (
+               cd supersuper-clone &&
+               check_that_two_of_three_alternates_are_used &&
+               # update of the submodule fails
+               test_must_fail git submodule update --init --recursive
+       )
+'
+
+test_expect_success 'missing nested submodule alternate in --reference-if-able mode' '
+       test_when_finished "rm -rf supersuper-clone" &&
+       git clone --recursive --reference-if-able supersuper2 supersuper2 supersuper-clone &&
+       (
+               cd supersuper-clone &&
+               check_that_two_of_three_alternates_are_used &&
+               # update of the submodule succeeds
+               git submodule update --init --recursive
+       )
+'
+
 test_done
index 47562ce4654db9d763cdf0c6d6b91724489110ff..d389ae5408ab783467f64368adc01325e258046b 100755 (executable)
@@ -93,6 +93,20 @@ test_expect_success 'error message contains blob reference' '
        )
 '
 
+test_expect_success 'using different treeishs works' '
+       (
+               cd super &&
+               git tag new_tag &&
+               tree=$(git rev-parse HEAD^{tree}) &&
+               commit=$(git rev-parse HEAD^{commit}) &&
+               test-submodule-config $commit b >expect &&
+               test-submodule-config $tree b >actual.1 &&
+               test-submodule-config new_tag b >actual.2 &&
+               test_cmp expect actual.1 &&
+               test_cmp expect actual.2
+       )
+'
+
 cat >super/expect_url <<EOF
 Submodule url: 'git@somewhere.else.net:a.git' for path 'b'
 Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule'
index d84897a67a3c365e280f88b36f43fc49e1ac9d7b..0b6da7ae1fd3d15f0d47783d3af50cb17d2c66df 100755 (executable)
@@ -155,6 +155,15 @@ test_expect_success 'amend --only ignores staged contents' '
        git diff --exit-code
 '
 
+test_expect_success 'allow-empty --only ignores staged contents' '
+       echo changed-again >file &&
+       git add file &&
+       git commit --allow-empty --only -m "empty" &&
+       git cat-file blob HEAD:file >file.actual &&
+       test_cmp file.expect file.actual &&
+       git diff --exit-code
+'
+
 test_expect_success 'set up editor' '
        cat >editor <<-\EOF &&
        #!/bin/sh
@@ -460,6 +469,42 @@ $alt" &&
        test_cmp expected actual
 '
 
+test_expect_success 'signoff respects trailer config' '
+
+       echo 5 >positive &&
+       git add positive &&
+       git commit -s -m "subject
+
+non-trailer line
+Myfooter: x" &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+       (
+               echo subject
+               echo
+               echo non-trailer line
+               echo Myfooter: x
+               echo
+               echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+       ) >expected &&
+       test_cmp expected actual &&
+
+       echo 6 >positive &&
+       git add positive &&
+       git -c "trailer.Myfooter.ifexists=add" commit -s -m "subject
+
+non-trailer line
+Myfooter: x" &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+       (
+               echo subject
+               echo
+               echo non-trailer line
+               echo Myfooter: x
+               echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+       ) >expected &&
+       test_cmp expected actual
+'
+
 test_expect_success 'multiple -m' '
 
        >negative &&
index f80bdb81e1f12124e32d5cd2c5c0ecdcd82dc782..e90413204ee32cafc3380244a5cf013eadcd72c1 100755 (executable)
@@ -105,7 +105,7 @@ test_expect_success 'not uptodate file porcelain checkout error' '
 '
 
 cat >expect <<\EOF
-error: Updating the following directories would lose untracked files in it:
+error: Updating the following directories would lose untracked files in them:
        rep
        rep2
 
index 6d9f21511fe1062565d8d08f60c385cffd94cac6..63d36fb28e44426708a743979c4bc341bdac714e 100755 (executable)
@@ -591,7 +591,8 @@ test_expect_success 'filenames seen by tools start with ./' '
 
 test_lazy_prereq MKTEMP '
        tempdir=$(mktemp -d -t foo.XXXXXX) &&
-       test -d "$tempdir"
+       test -d "$tempdir" &&
+       rmdir "$tempdir"
 '
 
 test_expect_success MKTEMP 'temporary filenames are used with mergetool.writeToTemp' '
index 70a2de461af58119f507a915104ba139dd0245df..99d4123461096196c11368864c1f8c3d524d5c1a 100755 (executable)
@@ -374,6 +374,7 @@ test_expect_success PERL 'setup change in subdirectory' '
        echo master >sub/sub &&
        git add sub/sub &&
        git commit -m "added sub/sub" &&
+       git tag v1 &&
        echo test >>file &&
        echo test >>sub/sub &&
        git add file sub/sub &&
@@ -409,12 +410,49 @@ run_dir_diff_test 'difftool --dir-diff ignores --prompt' '
        grep file output
 '
 
-run_dir_diff_test 'difftool --dir-diff from subdirectory' '
+run_dir_diff_test 'difftool --dir-diff branch from subdirectory' '
        (
                cd sub &&
                git difftool --dir-diff $symlinks --extcmd ls branch >output &&
-               grep sub output &&
-               grep file output
+               # "sub" must only exist in "right"
+               # "file" and "file2" must be listed in both "left" and "right"
+               test "1" = $(grep sub output | wc -l) &&
+               test "2" = $(grep file"$" output | wc -l) &&
+               test "2" = $(grep file2 output | wc -l)
+       )
+'
+
+run_dir_diff_test 'difftool --dir-diff v1 from subdirectory' '
+       (
+               cd sub &&
+               git difftool --dir-diff $symlinks --extcmd ls v1 >output &&
+               # "sub" and "file" exist in both v1 and HEAD.
+               # "file2" is unchanged.
+               test "2" = $(grep sub output | wc -l) &&
+               test "2" = $(grep file output | wc -l) &&
+               test "0" = $(grep file2 output | wc -l)
+       )
+'
+
+run_dir_diff_test 'difftool --dir-diff branch from subdirectory w/ pathspec' '
+       (
+               cd sub &&
+               git difftool --dir-diff $symlinks --extcmd ls branch -- .>output &&
+               # "sub" only exists in "right"
+               # "file" and "file2" must not be listed
+               test "1" = $(grep sub output | wc -l) &&
+               test "0" = $(grep file output | wc -l)
+       )
+'
+
+run_dir_diff_test 'difftool --dir-diff v1 from subdirectory w/ pathspec' '
+       (
+               cd sub &&
+               git difftool --dir-diff $symlinks --extcmd ls v1 -- .>output &&
+               # "sub" exists in v1 and HEAD
+               # "file" is filtered out by the pathspec
+               test "2" = $(grep sub output | wc -l) &&
+               test "0" = $(grep file output | wc -l)
        )
 '
 
index 92a3aa8063f810bd2b8df68f3f0f30c2faf3bdc6..8a8ba65a2ae583aa5d0b0526e604a8f9613b5a5e 100755 (executable)
@@ -17,25 +17,12 @@ case "$GIT_SVN_LC_ALL" in
        ;;
 esac
 
-deepdir=nothing-above
-ceiling=$PWD
-
 test_expect_success 'git svn --version works anywhere' '
-       mkdir -p "$deepdir" && (
-               GIT_CEILING_DIRECTORIES="$ceiling" &&
-               export GIT_CEILING_DIRECTORIES &&
-               cd "$deepdir" &&
-               git svn --version
-       )
+       nongit git svn --version
 '
 
 test_expect_success 'git svn help works anywhere' '
-       mkdir -p "$deepdir" && (
-               GIT_CEILING_DIRECTORIES="$ceiling" &&
-               export GIT_CEILING_DIRECTORIES &&
-               cd "$deepdir" &&
-               git svn help
-       )
+       nongit git svn help
 '
 
 test_expect_success \
index 0730f18d0f83f4145c5a0dbfde784d71bdc57a1d..093e9bd586da55cc27412422d58551a6830bd1a3 100755 (executable)
@@ -257,6 +257,26 @@ test_expect_success 'submit from detached head' '
        )
 '
 
+test_expect_success 'submit from worktree' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git worktree add ../worktree-test
+       ) &&
+       (
+               cd "$git/../worktree-test" &&
+               test_commit "worktree-commit" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit
+       ) &&
+       (
+               cd "$cli" &&
+               p4 sync &&
+               test_path_is_file worktree-commit.t
+       )
+'
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index 254d428b73fe4983298f57e0d87405002abdfbc2..1ab76c4246f215cfc0082bdbfd090e1622362140 100755 (executable)
@@ -269,6 +269,38 @@ test_expect_success 'submit works with two branches' '
        )
 '
 
+test_expect_success 'use --git-dir option and GIT_DIR' '
+       test_when_finished cleanup_git &&
+       git p4 clone //depot --destination="$git" &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               test_commit first-change &&
+               git p4 submit --git-dir "$git"
+       ) &&
+       (
+               cd "$cli" &&
+               p4 sync &&
+               test_path_is_file first-change.t &&
+               echo "cli_file" >cli_file.t &&
+               p4 add cli_file.t &&
+               p4 submit -d "cli change"
+       ) &&
+       (git --git-dir "$git" p4 sync) &&
+       (cd "$git" && git checkout -q p4/master) &&
+       test_path_is_file "$git"/cli_file.t &&
+       (
+               cd "$cli" &&
+               echo "cli_file2" >cli_file2.t &&
+               p4 add cli_file2.t  &&
+               p4 submit -d "cli change2"
+       ) &&
+       (GIT_DIR="$git" git p4 sync) &&
+       (cd "$git" && git checkout -q p4/master) &&
+       test_path_is_file "$git"/cli_file2.t
+'
+
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index 593152817dadaf1804f4a851c51b8bf1cbf52db9..e37239e657964e948e51e5ac186f5fdf4fab6003 100755 (executable)
@@ -413,6 +413,75 @@ test_expect_success 'submit --prepare-p4-only' '
        )
 '
 
+test_expect_success 'submit --shelve' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$cli" &&
+               p4 revert ... &&
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               test_commit "shelveme1" &&
+               git p4 submit --origin=HEAD^ &&
+
+               echo 654321 >shelveme2.t &&
+               echo 123456 >>shelveme1.t &&
+               git add shelveme* &&
+               git commit -m"shelvetest" &&
+               git p4 submit --shelve --origin=HEAD^ &&
+
+               test_path_is_file shelveme1.t &&
+               test_path_is_file shelveme2.t
+       ) &&
+       (
+               cd "$cli" &&
+               change=$(p4 -G changes -s shelved -m 1 //depot/... | \
+                        marshal_dump change) &&
+               p4 describe -S $change | grep shelveme2 &&
+               p4 describe -S $change | grep 123456 &&
+               test_path_is_file shelveme1.t &&
+               test_path_is_missing shelveme2.t
+       )
+'
+
+# Update an existing shelved changelist
+
+test_expect_success 'submit --update-shelve' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$cli" &&
+               p4 revert ... &&
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               test_commit "test-update-shelved-change" &&
+               git p4 submit --origin=HEAD^ --shelve &&
+
+               shelf_cl=$(p4 -G changes -s shelved -m 1 |\
+                       marshal_dump change) &&
+               test -n $shelf_cl &&
+               echo "updating shelved change list $shelf_cl" &&
+
+               echo "updated-line" >>shelf.t &&
+               echo added-file.t >added-file.t &&
+               git add shelf.t added-file.t &&
+               git rm -f test-update-shelved-change.t &&
+               git commit --amend -C HEAD &&
+               git show --stat HEAD &&
+               git p4 submit -v --origin HEAD^ --update-shelve $shelf_cl &&
+               echo "done git p4 submit"
+       ) &&
+       (
+               cd "$cli" &&
+               change=$(p4 -G changes -s shelved -m 1 //depot/... | \
+                        marshal_dump change) &&
+               p4 unshelve -c $change -s $change &&
+               grep -q updated-line shelf.t &&
+               p4 describe -S $change | grep added-file.t &&
+               test_path_is_missing test-update-shelved-change.t
+       )
+'
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index 110a7e792475fcf19ef782fae1d265ae81653853..734b8db4cbd557cf8e703cc4e2b085185634baba 100755 (executable)
@@ -42,6 +42,8 @@ test_expect_success 'Create repo with binary files' '
        (
                cd "$cli" &&
 
+               >file0.dat &&
+               p4 add file0.dat &&
                echo "content 1 txt 23 bytes" >file1.txt &&
                p4 add file1.txt &&
                echo "content 2-3 bin 25 bytes" >file2.dat &&
index 2ba62fbc178e1c92dff493beebdc9683b36efd2a..a34e55f874ca06da4cae6ab5d46bc3263c58d521 100755 (executable)
@@ -257,12 +257,7 @@ test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' '
 '
 
 test_expect_success '__gitdir - not a git repository' '
-       (
-               cd subdir/subsubdir &&
-               GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
-               export GIT_CEILING_DIRECTORIES &&
-               test_must_fail __gitdir
-       )
+       nongit test_must_fail __gitdir
 '
 
 test_expect_success '__gitcomp - trailing space - options' '
index fdaeb3a96bed361f53030cb6ea5c6bdde445163f..adab7f51f4c962967c90e3184853377feece2b1e 100644 (file)
@@ -994,3 +994,17 @@ test_copy_bytes () {
                }
        ' - "$1"
 }
+
+# run "$@" inside a non-git directory
+nongit () {
+       test -d non-repo ||
+       mkdir non-repo ||
+       return 1
+
+       (
+               GIT_CEILING_DIRECTORIES=$(pwd) &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd non-repo &&
+               "$@"
+       )
+}
index 64435f23a483dfc40e91437f0689bdfb9ac4b8f8..b2d9280f104aec7095dd2b37ff1f7165355b5388 100644 (file)
@@ -5,6 +5,7 @@
 #include "string-list.h"
 #include "strbuf.h"
 #include "argv-array.h"
+#include "quote.h"
 
 struct tmp_objdir {
        struct strbuf path;
@@ -79,12 +80,27 @@ static void remove_tmp_objdir_on_signal(int signo)
  */
 static void env_append(struct argv_array *env, const char *key, const char *val)
 {
-       const char *old = getenv(key);
+       struct strbuf quoted = STRBUF_INIT;
+       const char *old;
 
+       /*
+        * Avoid quoting if it's not necessary, for maximum compatibility
+        * with older parsers which don't understand the quoting.
+        */
+       if (*val == '"' || strchr(val, PATH_SEP)) {
+               strbuf_addch(&quoted, '"');
+               quote_c_style(val, &quoted, NULL, 1);
+               strbuf_addch(&quoted, '"');
+               val = quoted.buf;
+       }
+
+       old = getenv(key);
        if (!old)
                argv_array_pushf(env, "%s=%s", key, val);
        else
                argv_array_pushf(env, "%s=%s%c%s", key, old, PATH_SEP, val);
+
+       strbuf_release(&quoted);
 }
 
 static void env_replace(struct argv_array *env, const char *key, const char *val)
index f0ecde2d2f8b35c565ab176c76882a5fee8d8fb2..11f0b9fb40bcedbfcd3111fa8076ce0d9e1eb20f 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -46,6 +46,8 @@ static LIST_HEAD(conf_head);
 
 static char *separators = ":";
 
+static int configured;
+
 #define TRAILER_ARG_STRING "$ARG"
 
 static const char *git_generated_prefixes[] = {
@@ -102,12 +104,12 @@ static int same_trailer(struct trailer_item *a, struct arg_item *b)
        return same_token(a, b) && same_value(a, b);
 }
 
-static inline int contains_only_spaces(const char *str)
+static inline int is_blank_line(const char *str)
 {
        const char *s = str;
-       while (*s && isspace(*s))
+       while (*s && *s != '\n' && isspace(*s))
                s++;
-       return !*s;
+       return !*s || *s == '\n';
 }
 
 static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)
@@ -546,6 +548,17 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
        return 0;
 }
 
+static void ensure_configured(void)
+{
+       if (configured)
+               return;
+
+       /* Default config must be setup first */
+       git_config(git_trailer_default_config, NULL);
+       git_config(git_trailer_config, NULL);
+       configured = 1;
+}
+
 static const char *token_from_item(struct arg_item *item, char *tok)
 {
        if (item->conf.key)
@@ -563,15 +576,32 @@ static int token_matches_item(const char *tok, struct arg_item *item, int tok_le
 }
 
 /*
- * Return the location of the first separator in line, or -1 if there is no
- * separator.
+ * If the given line is of the form
+ * "<token><optional whitespace><separator>..." or "<separator>...", return the
+ * location of the separator. Otherwise, return -1.  The optional whitespace
+ * is allowed there primarily to allow things like "Bug #43" where <token> is
+ * "Bug" and <separator> is "#".
+ *
+ * The separator-starts-line case (in which this function returns 0) is
+ * distinguished from the non-well-formed-line case (in which this function
+ * returns -1) because some callers of this function need such a distinction.
  */
 static int find_separator(const char *line, const char *separators)
 {
-       int loc = strcspn(line, separators);
-       if (!line[loc])
-               return -1;
-       return loc;
+       int whitespace_found = 0;
+       const char *c;
+       for (c = line; *c; c++) {
+               if (strchr(separators, *c))
+                       return c - line;
+               if (!whitespace_found && (isalnum(*c) || *c == '-'))
+                       continue;
+               if (c != line && (*c == ' ' || *c == '\t')) {
+                       whitespace_found = 1;
+                       continue;
+               }
+               break;
+       }
+       return -1;
 }
 
 /*
@@ -685,51 +715,71 @@ static void process_command_line_args(struct list_head *arg_head,
        free(cl_separators);
 }
 
-static struct strbuf **read_input_file(const char *file)
+static void read_input_file(struct strbuf *sb, const char *file)
 {
-       struct strbuf **lines;
-       struct strbuf sb = STRBUF_INIT;
-
        if (file) {
-               if (strbuf_read_file(&sb, file, 0) < 0)
+               if (strbuf_read_file(sb, file, 0) < 0)
                        die_errno(_("could not read input file '%s'"), file);
        } else {
-               if (strbuf_read(&sb, fileno(stdin), 0) < 0)
+               if (strbuf_read(sb, fileno(stdin), 0) < 0)
                        die_errno(_("could not read from stdin"));
        }
+}
 
-       lines = strbuf_split(&sb, '\n');
+static const char *next_line(const char *str)
+{
+       const char *nl = strchrnul(str, '\n');
+       return nl + !!*nl;
+}
 
-       strbuf_release(&sb);
+/*
+ * Return the position of the start of the last line. If len is 0, return -1.
+ */
+static int last_line(const char *buf, size_t len)
+{
+       int i;
+       if (len == 0)
+               return -1;
+       if (len == 1)
+               return 0;
+       /*
+        * Skip the last character (in addition to the null terminator),
+        * because if the last character is a newline, it is considered as part
+        * of the last line anyway.
+        */
+       i = len - 2;
 
-       return lines;
+       for (; i >= 0; i--) {
+               if (buf[i] == '\n')
+                       return i + 1;
+       }
+       return 0;
 }
 
 /*
- * Return the (0 based) index of the start of the patch or the line
- * count if there is no patch in the message.
+ * Return the position of the start of the patch or the length of str if there
+ * is no patch in the message.
  */
-static int find_patch_start(struct strbuf **lines, int count)
+static int find_patch_start(const char *str)
 {
-       int i;
+       const char *s;
 
-       /* Get the start of the patch part if any */
-       for (i = 0; i < count; i++) {
-               if (starts_with(lines[i]->buf, "---"))
-                       return i;
+       for (s = str; *s; s = next_line(s)) {
+               if (starts_with(s, "---"))
+                       return s - str;
        }
 
-       return count;
+       return s - str;
 }
 
 /*
- * Return the (0 based) index of the first trailer line or count if
- * there are no trailers. Trailers are searched only in the lines from
- * index (count - 1) down to index 0.
+ * Return the position of the first trailer line or len if there are no
+ * trailers.
  */
-static int find_trailer_start(struct strbuf **lines, int count)
+static int find_trailer_start(const char *buf, size_t len)
 {
-       int start, end_of_title, only_spaces = 1;
+       const char *s;
+       int end_of_title, l, only_spaces = 1;
        int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
        /*
         * Number of possible continuation lines encountered. This will be
@@ -741,13 +791,13 @@ static int find_trailer_start(struct strbuf **lines, int count)
        int possible_continuation_lines = 0;
 
        /* The first paragraph is the title and cannot be trailers */
-       for (start = 0; start < count; start++) {
-               if (lines[start]->buf[0] == comment_line_char)
+       for (s = buf; s < buf + len; s = next_line(s)) {
+               if (s[0] == comment_line_char)
                        continue;
-               if (contains_only_spaces(lines[start]->buf))
+               if (is_blank_line(s))
                        break;
        }
-       end_of_title = start;
+       end_of_title = s - buf;
 
        /*
         * Get the start of the trailers by looking starting from the end for a
@@ -755,30 +805,33 @@ static int find_trailer_start(struct strbuf **lines, int count)
         * trailers, or (ii) contains at least one Git-generated trailer and
         * consists of at least 25% trailers.
         */
-       for (start = count - 1; start >= end_of_title; start--) {
+       for (l = last_line(buf, len);
+            l >= end_of_title;
+            l = last_line(buf, l)) {
+               const char *bol = buf + l;
                const char **p;
                int separator_pos;
 
-               if (lines[start]->buf[0] == comment_line_char) {
+               if (bol[0] == comment_line_char) {
                        non_trailer_lines += possible_continuation_lines;
                        possible_continuation_lines = 0;
                        continue;
                }
-               if (contains_only_spaces(lines[start]->buf)) {
+               if (is_blank_line(bol)) {
                        if (only_spaces)
                                continue;
                        non_trailer_lines += possible_continuation_lines;
                        if (recognized_prefix &&
                            trailer_lines * 3 >= non_trailer_lines)
-                               return start + 1;
-                       if (trailer_lines && !non_trailer_lines)
-                               return start + 1;
-                       return count;
+                               return next_line(bol) - buf;
+                       else if (trailer_lines && !non_trailer_lines)
+                               return next_line(bol) - buf;
+                       return len;
                }
                only_spaces = 0;
 
                for (p = git_generated_prefixes; *p; p++) {
-                       if (starts_with(lines[start]->buf, *p)) {
+                       if (starts_with(bol, *p)) {
                                trailer_lines++;
                                possible_continuation_lines = 0;
                                recognized_prefix = 1;
@@ -786,8 +839,8 @@ static int find_trailer_start(struct strbuf **lines, int count)
                        }
                }
 
-               separator_pos = find_separator(lines[start]->buf, separators);
-               if (separator_pos >= 1 && !isspace(lines[start]->buf[0])) {
+               separator_pos = find_separator(bol, separators);
+               if (separator_pos >= 1 && !isspace(bol[0])) {
                        struct list_head *pos;
 
                        trailer_lines++;
@@ -797,13 +850,13 @@ static int find_trailer_start(struct strbuf **lines, int count)
                        list_for_each(pos, &conf_head) {
                                struct arg_item *item;
                                item = list_entry(pos, struct arg_item, list);
-                               if (token_matches_item(lines[start]->buf, item,
+                               if (token_matches_item(bol, item,
                                                       separator_pos)) {
                                        recognized_prefix = 1;
                                        break;
                                }
                        }
-               } else if (isspace(lines[start]->buf[0]))
+               } else if (isspace(bol[0]))
                        possible_continuation_lines++;
                else {
                        non_trailer_lines++;
@@ -814,97 +867,64 @@ static int find_trailer_start(struct strbuf **lines, int count)
                ;
        }
 
-       return count;
-}
-
-/* Get the index of the end of the trailers */
-static int find_trailer_end(struct strbuf **lines, int patch_start)
-{
-       struct strbuf sb = STRBUF_INIT;
-       int i, ignore_bytes;
-
-       for (i = 0; i < patch_start; i++)
-               strbuf_addbuf(&sb, lines[i]);
-       ignore_bytes = ignore_non_trailer(&sb);
-       strbuf_release(&sb);
-       for (i = patch_start - 1; i >= 0 && ignore_bytes > 0; i--)
-               ignore_bytes -= lines[i]->len;
-
-       return i + 1;
+       return len;
 }
 
-static int has_blank_line_before(struct strbuf **lines, int start)
+/* Return the position of the end of the trailers. */
+static int find_trailer_end(const char *buf, size_t len)
 {
-       for (;start >= 0; start--) {
-               if (lines[start]->buf[0] == comment_line_char)
-                       continue;
-               return contains_only_spaces(lines[start]->buf);
-       }
-       return 0;
+       return len - ignore_non_trailer(buf, len);
 }
 
-static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
+static int ends_with_blank_line(const char *buf, size_t len)
 {
-       int i;
-       for (i = start; lines[i] && i < end; i++)
-               fprintf(outfile, "%s", lines[i]->buf);
+       int ll = last_line(buf, len);
+       if (ll < 0)
+               return 0;
+       return is_blank_line(buf + ll);
 }
 
 static int process_input_file(FILE *outfile,
-                             struct strbuf **lines,
+                             const char *str,
                              struct list_head *head)
 {
-       int count = 0;
-       int patch_start, trailer_start, trailer_end, i;
+       struct trailer_info info;
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
-       struct trailer_item *last = NULL;
-
-       /* Get the line count */
-       while (lines[count])
-               count++;
+       int i;
 
-       patch_start = find_patch_start(lines, count);
-       trailer_end = find_trailer_end(lines, patch_start);
-       trailer_start = find_trailer_start(lines, trailer_end);
+       trailer_info_get(&info, str);
 
        /* Print lines before the trailers as is */
-       print_lines(outfile, lines, 0, trailer_start);
+       fwrite(str, 1, info.trailer_start - str, outfile);
 
-       if (!has_blank_line_before(lines, trailer_start - 1))
+       if (!info.blank_line_before_trailer)
                fprintf(outfile, "\n");
 
-       /* Parse trailer lines */
-       for (i = trailer_start; i < trailer_end; i++) {
+       for (i = 0; i < info.trailer_nr; i++) {
                int separator_pos;
-               if (lines[i]->buf[0] == comment_line_char)
+               char *trailer = info.trailers[i];
+               if (trailer[0] == comment_line_char)
                        continue;
-               if (last && isspace(lines[i]->buf[0])) {
-                       struct strbuf sb = STRBUF_INIT;
-                       strbuf_addf(&sb, "%s\n%s", last->value, lines[i]->buf);
-                       strbuf_strip_suffix(&sb, "\n");
-                       free(last->value);
-                       last->value = strbuf_detach(&sb, NULL);
-                       continue;
-               }
-               separator_pos = find_separator(lines[i]->buf, separators);
+               separator_pos = find_separator(trailer, separators);
                if (separator_pos >= 1) {
-                       parse_trailer(&tok, &val, NULL, lines[i]->buf,
+                       parse_trailer(&tok, &val, NULL, trailer,
                                      separator_pos);
-                       last = add_trailer_item(head,
-                                               strbuf_detach(&tok, NULL),
-                                               strbuf_detach(&val, NULL));
+                       add_trailer_item(head,
+                                        strbuf_detach(&tok, NULL),
+                                        strbuf_detach(&val, NULL));
                } else {
-                       strbuf_addbuf(&val, lines[i]);
+                       strbuf_addstr(&val, trailer);
                        strbuf_strip_suffix(&val, "\n");
                        add_trailer_item(head,
                                         NULL,
                                         strbuf_detach(&val, NULL));
-                       last = NULL;
                }
        }
 
-       return trailer_end;
+       trailer_info_release(&info);
+
+       return info.trailer_end - str;
 }
 
 static void free_all(struct list_head *head)
@@ -951,21 +971,19 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str
 {
        LIST_HEAD(head);
        LIST_HEAD(arg_head);
-       struct strbuf **lines;
+       struct strbuf sb = STRBUF_INIT;
        int trailer_end;
        FILE *outfile = stdout;
 
-       /* Default config must be setup first */
-       git_config(git_trailer_default_config, NULL);
-       git_config(git_trailer_config, NULL);
+       ensure_configured();
 
-       lines = read_input_file(file);
+       read_input_file(&sb, file);
 
        if (in_place)
                outfile = create_in_place_tempfile(file);
 
        /* Print the lines before the trailers */
-       trailer_end = process_input_file(outfile, lines, &head);
+       trailer_end = process_input_file(outfile, sb.buf, &head);
 
        process_command_line_args(&arg_head, trailers);
 
@@ -976,11 +994,62 @@ void process_trailers(const char *file, int in_place, int trim_empty, struct str
        free_all(&head);
 
        /* Print the lines after the trailers as is */
-       print_lines(outfile, lines, trailer_end, INT_MAX);
+       fwrite(sb.buf + trailer_end, 1, sb.len - trailer_end, outfile);
 
        if (in_place)
                if (rename_tempfile(&trailers_tempfile, file))
                        die_errno(_("could not rename temporary file to %s"), file);
 
-       strbuf_list_free(lines);
+       strbuf_release(&sb);
+}
+
+void trailer_info_get(struct trailer_info *info, const char *str)
+{
+       int patch_start, trailer_end, trailer_start;
+       struct strbuf **trailer_lines, **ptr;
+       char **trailer_strings = NULL;
+       size_t nr = 0, alloc = 0;
+       char **last = NULL;
+
+       ensure_configured();
+
+       patch_start = find_patch_start(str);
+       trailer_end = find_trailer_end(str, patch_start);
+       trailer_start = find_trailer_start(str, trailer_end);
+
+       trailer_lines = strbuf_split_buf(str + trailer_start,
+                                        trailer_end - trailer_start,
+                                        '\n',
+                                        0);
+       for (ptr = trailer_lines; *ptr; ptr++) {
+               if (last && isspace((*ptr)->buf[0])) {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_attach(&sb, *last, strlen(*last), strlen(*last));
+                       strbuf_addbuf(&sb, *ptr);
+                       *last = strbuf_detach(&sb, NULL);
+                       continue;
+               }
+               ALLOC_GROW(trailer_strings, nr + 1, alloc);
+               trailer_strings[nr] = strbuf_detach(*ptr, NULL);
+               last = find_separator(trailer_strings[nr], separators) >= 1
+                       ? &trailer_strings[nr]
+                       : NULL;
+               nr++;
+       }
+       strbuf_list_free(trailer_lines);
+
+       info->blank_line_before_trailer = ends_with_blank_line(str,
+                                                              trailer_start);
+       info->trailer_start = str + trailer_start;
+       info->trailer_end = str + trailer_end;
+       info->trailers = trailer_strings;
+       info->trailer_nr = nr;
+}
+
+void trailer_info_release(struct trailer_info *info)
+{
+       int i;
+       for (i = 0; i < info->trailer_nr; i++)
+               free(info->trailers[i]);
+       free(info->trailers);
 }
index 36b40b81761f95742c9c6fe7e94ba89f19aabfde..65cc5d79c6cecfce5cf3302dd7dcb6323f9f85dc 100644 (file)
--- a/trailer.h
+++ b/trailer.h
@@ -1,7 +1,32 @@
 #ifndef TRAILER_H
 #define TRAILER_H
 
+struct trailer_info {
+       /*
+        * True if there is a blank line before the location pointed to by
+        * trailer_start.
+        */
+       int blank_line_before_trailer;
+
+       /*
+        * Pointers to the start and end of the trailer block found. If there
+        * is no trailer block found, these 2 pointers point to the end of the
+        * input string.
+        */
+       const char *trailer_start, *trailer_end;
+
+       /*
+        * Array of trailers found.
+        */
+       char **trailers;
+       size_t trailer_nr;
+};
+
 void process_trailers(const char *file, int in_place, int trim_empty,
                      struct string_list *trailers);
 
+void trailer_info_get(struct trailer_info *info, const char *str);
+
+void trailer_info_release(struct trailer_info *info);
+
 #endif /* TRAILER_H */
index d57e8dec28d6890ce981a8422e9b8b60b697ecd6..04e5d6623e39014622e0a185d37f8a456fd352e6 100644 (file)
@@ -949,23 +949,39 @@ int transport_push(struct transport *transport,
 
                if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
+                       struct sha1_array commits = SHA1_ARRAY_INIT;
+
                        for (; ref; ref = ref->next)
-                               if (!is_null_oid(&ref->new_oid) &&
-                                   !push_unpushed_submodules(ref->new_oid.hash,
-                                           transport->remote->name))
-                                   die ("Failed to push all needed submodules!");
+                               if (!is_null_oid(&ref->new_oid))
+                                       sha1_array_append(&commits, ref->new_oid.hash);
+
+                       if (!push_unpushed_submodules(&commits,
+                                                     transport->remote->name,
+                                                     pretend)) {
+                               sha1_array_clear(&commits);
+                               die("Failed to push all needed submodules!");
+                       }
+                       sha1_array_clear(&commits);
                }
 
-               if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
-                             TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
+               if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) ||
+                    ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) &&
+                     !pretend)) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
+                       struct sha1_array commits = SHA1_ARRAY_INIT;
 
                        for (; ref; ref = ref->next)
-                               if (!is_null_oid(&ref->new_oid) &&
-                                   find_unpushed_submodules(ref->new_oid.hash,
-                                           transport->remote->name, &needs_pushing))
-                                       die_with_unpushed_submodules(&needs_pushing);
+                               if (!is_null_oid(&ref->new_oid))
+                                       sha1_array_append(&commits, ref->new_oid.hash);
+
+                       if (find_unpushed_submodules(&commits, transport->remote->name,
+                                               &needs_pushing)) {
+                               sha1_array_clear(&commits);
+                               die_with_unpushed_submodules(&needs_pushing);
+                       }
+                       string_list_clear(&needs_pushing, 0);
+                       sha1_array_clear(&commits);
                }
 
                push_ret = transport->push_refs(transport, remote_refs, flags);
index 47cdd2369d1919b7b7b267c622ad31e2ad6f24c0..02207be4fc47537168d1b1a7d1455af7e4c0df60 100644 (file)
@@ -25,7 +25,7 @@ static const struct interval zero_width[] = {
 { 0x0825, 0x0827 },
 { 0x0829, 0x082D },
 { 0x0859, 0x085B },
-{ 0x08E4, 0x0902 },
+{ 0x08D4, 0x0902 },
 { 0x093A, 0x093A },
 { 0x093C, 0x093C },
 { 0x0941, 0x0948 },
@@ -120,6 +120,7 @@ static const struct interval zero_width[] = {
 { 0x17C9, 0x17D3 },
 { 0x17DD, 0x17DD },
 { 0x180B, 0x180E },
+{ 0x1885, 0x1886 },
 { 0x18A9, 0x18A9 },
 { 0x1920, 0x1922 },
 { 0x1927, 0x1928 },
@@ -158,7 +159,7 @@ static const struct interval zero_width[] = {
 { 0x1CF4, 0x1CF4 },
 { 0x1CF8, 0x1CF9 },
 { 0x1DC0, 0x1DF5 },
-{ 0x1DFC, 0x1DFF },
+{ 0x1DFB, 0x1DFF },
 { 0x200B, 0x200F },
 { 0x202A, 0x202E },
 { 0x2060, 0x2064 },
@@ -171,13 +172,13 @@ static const struct interval zero_width[] = {
 { 0x3099, 0x309A },
 { 0xA66F, 0xA672 },
 { 0xA674, 0xA67D },
-{ 0xA69F, 0xA69F },
+{ 0xA69E, 0xA69F },
 { 0xA6F0, 0xA6F1 },
 { 0xA802, 0xA802 },
 { 0xA806, 0xA806 },
 { 0xA80B, 0xA80B },
 { 0xA825, 0xA826 },
-{ 0xA8C4, 0xA8C4 },
+{ 0xA8C4, 0xA8C5 },
 { 0xA8E0, 0xA8F1 },
 { 0xA926, 0xA92D },
 { 0xA947, 0xA951 },
@@ -204,7 +205,7 @@ static const struct interval zero_width[] = {
 { 0xABED, 0xABED },
 { 0xFB1E, 0xFB1E },
 { 0xFE00, 0xFE0F },
-{ 0xFE20, 0xFE2D },
+{ 0xFE20, 0xFE2F },
 { 0xFEFF, 0xFEFF },
 { 0xFFF9, 0xFFFB },
 { 0x101FD, 0x101FD },
@@ -228,16 +229,21 @@ static const struct interval zero_width[] = {
 { 0x11173, 0x11173 },
 { 0x11180, 0x11181 },
 { 0x111B6, 0x111BE },
+{ 0x111CA, 0x111CC },
 { 0x1122F, 0x11231 },
 { 0x11234, 0x11234 },
 { 0x11236, 0x11237 },
+{ 0x1123E, 0x1123E },
 { 0x112DF, 0x112DF },
 { 0x112E3, 0x112EA },
-{ 0x11301, 0x11301 },
+{ 0x11300, 0x11301 },
 { 0x1133C, 0x1133C },
 { 0x11340, 0x11340 },
 { 0x11366, 0x1136C },
 { 0x11370, 0x11374 },
+{ 0x11438, 0x1143F },
+{ 0x11442, 0x11444 },
+{ 0x11446, 0x11446 },
 { 0x114B3, 0x114B8 },
 { 0x114BA, 0x114BA },
 { 0x114BF, 0x114C0 },
@@ -245,6 +251,7 @@ static const struct interval zero_width[] = {
 { 0x115B2, 0x115B5 },
 { 0x115BC, 0x115BD },
 { 0x115BF, 0x115C0 },
+{ 0x115DC, 0x115DD },
 { 0x11633, 0x1163A },
 { 0x1163D, 0x1163D },
 { 0x1163F, 0x11640 },
@@ -252,6 +259,16 @@ static const struct interval zero_width[] = {
 { 0x116AD, 0x116AD },
 { 0x116B0, 0x116B5 },
 { 0x116B7, 0x116B7 },
+{ 0x1171D, 0x1171F },
+{ 0x11722, 0x11725 },
+{ 0x11727, 0x1172B },
+{ 0x11C30, 0x11C36 },
+{ 0x11C38, 0x11C3D },
+{ 0x11C3F, 0x11C3F },
+{ 0x11C92, 0x11CA7 },
+{ 0x11CAA, 0x11CB0 },
+{ 0x11CB2, 0x11CB3 },
+{ 0x11CB5, 0x11CB6 },
 { 0x16AF0, 0x16AF4 },
 { 0x16B30, 0x16B36 },
 { 0x16F8F, 0x16F92 },
@@ -262,31 +279,59 @@ static const struct interval zero_width[] = {
 { 0x1D185, 0x1D18B },
 { 0x1D1AA, 0x1D1AD },
 { 0x1D242, 0x1D244 },
+{ 0x1DA00, 0x1DA36 },
+{ 0x1DA3B, 0x1DA6C },
+{ 0x1DA75, 0x1DA75 },
+{ 0x1DA84, 0x1DA84 },
+{ 0x1DA9B, 0x1DA9F },
+{ 0x1DAA1, 0x1DAAF },
+{ 0x1E000, 0x1E006 },
+{ 0x1E008, 0x1E018 },
+{ 0x1E01B, 0x1E021 },
+{ 0x1E023, 0x1E024 },
+{ 0x1E026, 0x1E02A },
 { 0x1E8D0, 0x1E8D6 },
+{ 0x1E944, 0x1E94A },
 { 0xE0001, 0xE0001 },
 { 0xE0020, 0xE007F },
 { 0xE0100, 0xE01EF }
 };
 static const struct interval double_width[] = {
-{ /* plane */ 0x0, 0x1C },
-{ /* plane */ 0x1C, 0x21 },
-{ /* plane */ 0x21, 0x22 },
-{ /* plane */ 0x22, 0x23 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
-{ /* plane */ 0x0, 0x0 },
 { 0x1100, 0x115F },
+{ 0x231A, 0x231B },
 { 0x2329, 0x232A },
+{ 0x23E9, 0x23EC },
+{ 0x23F0, 0x23F0 },
+{ 0x23F3, 0x23F3 },
+{ 0x25FD, 0x25FE },
+{ 0x2614, 0x2615 },
+{ 0x2648, 0x2653 },
+{ 0x267F, 0x267F },
+{ 0x2693, 0x2693 },
+{ 0x26A1, 0x26A1 },
+{ 0x26AA, 0x26AB },
+{ 0x26BD, 0x26BE },
+{ 0x26C4, 0x26C5 },
+{ 0x26CE, 0x26CE },
+{ 0x26D4, 0x26D4 },
+{ 0x26EA, 0x26EA },
+{ 0x26F2, 0x26F3 },
+{ 0x26F5, 0x26F5 },
+{ 0x26FA, 0x26FA },
+{ 0x26FD, 0x26FD },
+{ 0x2705, 0x2705 },
+{ 0x270A, 0x270B },
+{ 0x2728, 0x2728 },
+{ 0x274C, 0x274C },
+{ 0x274E, 0x274E },
+{ 0x2753, 0x2755 },
+{ 0x2757, 0x2757 },
+{ 0x2795, 0x2797 },
+{ 0x27B0, 0x27B0 },
+{ 0x27BF, 0x27BF },
+{ 0x2B1B, 0x2B1C },
+{ 0x2B50, 0x2B50 },
+{ 0x2B55, 0x2B55 },
 { 0x2E80, 0x2E99 },
 { 0x2E9B, 0x2EF3 },
 { 0x2F00, 0x2FD5 },
@@ -313,11 +358,49 @@ static const struct interval double_width[] = {
 { 0xFE68, 0xFE6B },
 { 0xFF01, 0xFF60 },
 { 0xFFE0, 0xFFE6 },
+{ 0x16FE0, 0x16FE0 },
+{ 0x17000, 0x187EC },
+{ 0x18800, 0x18AF2 },
 { 0x1B000, 0x1B001 },
+{ 0x1F004, 0x1F004 },
+{ 0x1F0CF, 0x1F0CF },
+{ 0x1F18E, 0x1F18E },
+{ 0x1F191, 0x1F19A },
 { 0x1F200, 0x1F202 },
-{ 0x1F210, 0x1F23A },
+{ 0x1F210, 0x1F23B },
 { 0x1F240, 0x1F248 },
 { 0x1F250, 0x1F251 },
+{ 0x1F300, 0x1F320 },
+{ 0x1F32D, 0x1F335 },
+{ 0x1F337, 0x1F37C },
+{ 0x1F37E, 0x1F393 },
+{ 0x1F3A0, 0x1F3CA },
+{ 0x1F3CF, 0x1F3D3 },
+{ 0x1F3E0, 0x1F3F0 },
+{ 0x1F3F4, 0x1F3F4 },
+{ 0x1F3F8, 0x1F43E },
+{ 0x1F440, 0x1F440 },
+{ 0x1F442, 0x1F4FC },
+{ 0x1F4FF, 0x1F53D },
+{ 0x1F54B, 0x1F54E },
+{ 0x1F550, 0x1F567 },
+{ 0x1F57A, 0x1F57A },
+{ 0x1F595, 0x1F596 },
+{ 0x1F5A4, 0x1F5A4 },
+{ 0x1F5FB, 0x1F64F },
+{ 0x1F680, 0x1F6C5 },
+{ 0x1F6CC, 0x1F6CC },
+{ 0x1F6D0, 0x1F6D2 },
+{ 0x1F6EB, 0x1F6EC },
+{ 0x1F6F4, 0x1F6F6 },
+{ 0x1F910, 0x1F91E },
+{ 0x1F920, 0x1F927 },
+{ 0x1F930, 0x1F930 },
+{ 0x1F933, 0x1F93E },
+{ 0x1F940, 0x1F94B },
+{ 0x1F950, 0x1F95E },
+{ 0x1F980, 0x1F991 },
+{ 0x1F9C0, 0x1F9C0 },
 { 0x20000, 0x2FFFD },
 { 0x30000, 0x3FFFD }
 };
index ea6bdd20e0491554e88f4b84bd3b6354867021bc..7a6df99d10e1dbabc9f7df74316fd91bd55c1fd5 100644 (file)
@@ -78,7 +78,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                xstrfmt(msg, cmd, cmd);
 
        msgs[ERROR_NOT_UPTODATE_DIR] =
-               _("Updating the following directories would lose untracked files in it:\n%s");
+               _("Updating the following directories would lose untracked files in them:\n%s");
 
        if (!strcmp(cmd, "checkout"))
                msg = advice_commit_before_merge
diff --git a/update_unicode.sh b/update_unicode.sh
deleted file mode 100755 (executable)
index 27af77c..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/sh
-#See http://www.unicode.org/reports/tr44/
-#
-#Me Enclosing_Mark  an enclosing combining mark
-#Mn Nonspacing_Mark a nonspacing combining mark (zero advance width)
-#Cf Format          a format control character
-#
-UNICODEWIDTH_H=../unicode_width.h
-if ! test -d unicode; then
-       mkdir unicode
-fi &&
-( cd unicode &&
-       if ! test -f UnicodeData.txt; then
-               wget http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
-       fi &&
-       if ! test -f EastAsianWidth.txt; then
-               wget http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt
-       fi &&
-       if ! test -d uniset; then
-               git clone https://github.com/depp/uniset.git
-       fi &&
-       (
-               cd uniset &&
-               if ! test -x uniset; then
-                       autoreconf -i &&
-                       ./configure --enable-warnings=-Werror CFLAGS='-O0 -ggdb'
-               fi &&
-               make
-       ) &&
-       UNICODE_DIR=. && export UNICODE_DIR &&
-       cat >$UNICODEWIDTH_H <<-EOF
-       static const struct interval zero_width[] = {
-               $(uniset/uniset --32 cat:Me,Mn,Cf + U+1160..U+11FF - U+00AD |
-                 grep -v plane)
-       };
-       static const struct interval double_width[] = {
-               $(uniset/uniset --32 eaw:F,W)
-       };
-       EOF
-)
index f7869f8d6072c98c3e9be3387a9a3c19508e13ea..eb6121263b0d56b6b7ea7c5024e9ceb68256c1c5 100644 (file)
@@ -88,21 +88,13 @@ static struct worktree *get_main_worktree(void)
 
        strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
 
-       if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
-               goto done;
-
-       worktree = xmalloc(sizeof(struct worktree));
+       worktree = xcalloc(1, sizeof(*worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
-       worktree->id = NULL;
        worktree->is_bare = is_bare;
-       worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
-       worktree->is_current = 0;
-       add_head_info(&head_ref, worktree);
-       worktree->lock_reason = NULL;
-       worktree->lock_reason_valid = 0;
+       if (!parse_ref(path.buf, &head_ref, &is_detached))
+               add_head_info(&head_ref, worktree);
 
-done:
        strbuf_release(&path);
        strbuf_release(&worktree_path);
        strbuf_release(&head_ref);
@@ -138,16 +130,11 @@ static struct worktree *get_linked_worktree(const char *id)
        if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
                goto done;
 
-       worktree = xmalloc(sizeof(struct worktree));
+       worktree = xcalloc(1, sizeof(*worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
        worktree->id = xstrdup(id);
-       worktree->is_bare = 0;
-       worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
-       worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
-       worktree->lock_reason = NULL;
-       worktree->lock_reason_valid = 0;
 
 done:
        strbuf_release(&path);
@@ -173,7 +160,14 @@ static void mark_current_worktree(struct worktree **worktrees)
        free(git_dir);
 }
 
-struct worktree **get_worktrees(void)
+static int compare_worktree(const void *a_, const void *b_)
+{
+       const struct worktree *const *a = a_;
+       const struct worktree *const *b = b_;
+       return fspathcmp((*a)->path, (*b)->path);
+}
+
+struct worktree **get_worktrees(unsigned flags)
 {
        struct worktree **list = NULL;
        struct strbuf path = STRBUF_INIT;
@@ -183,8 +177,7 @@ struct worktree **get_worktrees(void)
 
        list = xmalloc(alloc * sizeof(struct worktree *));
 
-       if ((list[counter] = get_main_worktree()))
-               counter++;
+       list[counter++] = get_main_worktree();
 
        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
        dir = opendir(path.buf);
@@ -205,6 +198,13 @@ struct worktree **get_worktrees(void)
        ALLOC_GROW(list, counter + 1, alloc);
        list[counter] = NULL;
 
+       if (flags & GWT_SORT_LINKED)
+               /*
+                * don't sort the first item (main worktree), which will
+                * always be the first
+                */
+               QSORT(list + 1, counter - 1, compare_worktree);
+
        mark_current_worktree(list);
        return list;
 }
@@ -341,7 +341,7 @@ const struct worktree *find_shared_symref(const char *symref,
 
        if (worktrees)
                free_worktrees(worktrees);
-       worktrees = get_worktrees();
+       worktrees = get_worktrees(0);
 
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
index 90e1311fa73fa6bdde5249d68818eacb6dc8edd0..d59ce1fee875800462a1e464592fd9dd01828e17 100644 (file)
@@ -15,6 +15,8 @@ struct worktree {
 
 /* Functions for acting on the information about worktrees. */
 
+#define GWT_SORT_LINKED (1 << 0) /* keeps linked worktrees sorted */
+
 /*
  * Get the worktrees.  The primary worktree will always be the first returned,
  * and linked worktrees will be pointed to by 'next' in each subsequent
@@ -23,7 +25,7 @@ struct worktree {
  * The caller is responsible for freeing the memory from the returned
  * worktree(s).
  */
-extern struct worktree **get_worktrees(void);
+extern struct worktree **get_worktrees(unsigned flags);
 
 /*
  * Return git dir of the worktree. Note that the path may be relative.
index a2e9d332d8332bb1dbeea26b683c5e3f032a822e..a715e71906a9a2fbd62180dd017d140f68ad14a0 100644 (file)
@@ -2258,11 +2258,12 @@ int has_uncommitted_changes(int ignore_submodules)
 int require_clean_work_tree(const char *action, const char *hint, int ignore_submodules, int gently)
 {
        struct lock_file *lock_file = xcalloc(1, sizeof(*lock_file));
-       int err = 0;
+       int err = 0, fd;
 
-       hold_locked_index(lock_file, 0);
+       fd = hold_locked_index(lock_file, 0);
        refresh_cache(REFRESH_QUIET);
-       update_index_if_able(&the_index, lock_file);
+       if (0 <= fd)
+               update_index_if_able(&the_index, lock_file);
        rollback_lock_file(lock_file);
 
        if (has_unstaged_changes(ignore_submodules)) {
index 027192a1c7f12214c0ff6787296769ce708ba407..04d7b32e4e4a75b9d5762f3d3a2a2f9c236075a0 100644 (file)
@@ -264,110 +264,6 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
        return ha;
 }
 
-#ifdef XDL_FAST_HASH
-
-#define REPEAT_BYTE(x)  ((~0ul / 0xff) * (x))
-
-#define ONEBYTES       REPEAT_BYTE(0x01)
-#define NEWLINEBYTES   REPEAT_BYTE(0x0a)
-#define HIGHBITS       REPEAT_BYTE(0x80)
-
-/* Return the high bit set in the first byte that is a zero */
-static inline unsigned long has_zero(unsigned long a)
-{
-       return ((a - ONEBYTES) & ~a) & HIGHBITS;
-}
-
-static inline long count_masked_bytes(unsigned long mask)
-{
-       if (sizeof(long) == 8) {
-               /*
-                * Jan Achrenius on G+: microoptimized version of
-                * the simpler "(mask & ONEBYTES) * ONEBYTES >> 56"
-                * that works for the bytemasks without having to
-                * mask them first.
-                */
-               /*
-                * return mask * 0x0001020304050608 >> 56;
-                *
-                * Doing it like this avoids warnings on 32-bit machines.
-                */
-               long a = (REPEAT_BYTE(0x01) / 0xff + 1);
-               return mask * a >> (sizeof(long) * 7);
-       } else {
-               /* Carl Chatfield / Jan Achrenius G+ version for 32-bit */
-               /* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */
-               long a = (0x0ff0001 + mask) >> 23;
-               /* Fix the 1 for 00 case */
-               return a & mask;
-       }
-}
-
-unsigned long xdl_hash_record(char const **data, char const *top, long flags)
-{
-       unsigned long hash = 5381;
-       unsigned long a = 0, mask = 0;
-       char const *ptr = *data;
-       char const *end = top - sizeof(unsigned long) + 1;
-
-       if (flags & XDF_WHITESPACE_FLAGS)
-               return xdl_hash_record_with_whitespace(data, top, flags);
-
-       ptr -= sizeof(unsigned long);
-       do {
-               hash += hash << 5;
-               hash ^= a;
-               ptr += sizeof(unsigned long);
-               if (ptr >= end)
-                       break;
-               a = *(unsigned long *)ptr;
-               /* Do we have any '\n' bytes in this word? */
-               mask = has_zero(a ^ NEWLINEBYTES);
-       } while (!mask);
-
-       if (ptr >= end) {
-               /*
-                * There is only a partial word left at the end of the
-                * buffer. Because we may work with a memory mapping,
-                * we have to grab the rest byte by byte instead of
-                * blindly reading it.
-                *
-                * To avoid problems with masking in a signed value,
-                * we use an unsigned char here.
-                */
-               const char *p;
-               for (p = top - 1; p >= ptr; p--)
-                       a = (a << 8) + *((const unsigned char *)p);
-               mask = has_zero(a ^ NEWLINEBYTES);
-               if (!mask)
-                       /*
-                        * No '\n' found in the partial word.  Make a
-                        * mask that matches what we read.
-                        */
-                       mask = 1UL << (8 * (top - ptr) + 7);
-       }
-
-       /* The mask *below* the first high bit set */
-       mask = (mask - 1) & ~mask;
-       mask >>= 7;
-       hash += hash << 5;
-       hash ^= a & mask;
-
-       /* Advance past the last (possibly partial) word */
-       ptr += count_masked_bytes(mask);
-
-       if (ptr < top) {
-               assert(*ptr == '\n');
-               ptr++;
-       }
-
-       *data = ptr;
-
-       return hash;
-}
-
-#else /* XDL_FAST_HASH */
-
 unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        unsigned long ha = 5381;
        char const *ptr = *data;
@@ -384,8 +280,6 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        return ha;
 }
 
-#endif /* XDL_FAST_HASH */
-
 unsigned int xdl_hashbits(unsigned int size) {
        unsigned int val = 1, bits = 0;