Merge branch 'tb/t0027-eol-conversion'
authorJunio C Hamano <gitster@pobox.com>
Mon, 22 Dec 2014 20:27:34 +0000 (12:27 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 22 Dec 2014 20:27:34 +0000 (12:27 -0800)
* tb/t0027-eol-conversion:
t0027: check the eol conversion warnings

124 files changed:
Documentation/RelNotes/1.8.5.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.9.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.0.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.1.4.txt [new file with mode: 0644]
Documentation/RelNotes/2.2.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.3.0.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-check-ignore.txt
Documentation/git-mailinfo.txt
Documentation/git-notes.txt
Documentation/git-push.txt
Documentation/git-send-email.txt
Documentation/git-stripspace.txt
Documentation/git.txt
Documentation/technical/api-credentials.txt
Documentation/technical/api-string-list.txt
Makefile
RelNotes
archive.c
builtin/add.c
builtin/apply.c
builtin/checkout.c
builtin/commit.c
builtin/config.c
builtin/for-each-ref.c
builtin/help.c
builtin/init-db.c
builtin/log.c
builtin/ls-tree.c
builtin/mailinfo.c
builtin/merge.c
builtin/notes.c
builtin/push.c
builtin/receive-pack.c
builtin/remote.c
builtin/repack.c
cache.h
commit.c
commit.h
compat/mingw.c
compat/stat.c [new file with mode: 0644]
config.c
config.mak.uname
configure.ac
connect.c
contrib/completion/git-completion.bash
contrib/workdir/git-new-workdir
credential.c
credential.h
csum-file.h
environment.c
exec_cmd.c
exec_cmd.h
fsck.c
git-am.sh
git-compat-util.h
git-difftool--helper.sh
git-mergetool--lib.sh
git-mergetool.sh
git-send-email.perl
git-sh-setup.sh
git.c
gitweb/gitweb.perl
mailmap.c
merge-recursive.c
mergetools/diffmerge
mergetools/emerge
mergetools/kdiff3
notes.c
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
path.c
prompt.c
quote.c
quote.h
read-cache.c
refs.c
refs.h
remote.c
run-command.c
run-command.h
sequencer.c
sequencer.h
sha1_file.c
sha1_name.c
string-list.c
string-list.h
t/README
t/t0001-init.sh
t/t0050-filesystem.sh
t/t0090-cache-tree.sh
t/t0300-credentials.sh
t/t1014-read-tree-confusing.sh [new file with mode: 0755]
t/t1410-reflog.sh
t/t1450-fsck.sh
t/t2022-checkout-paths.sh
t/t3102-ls-tree-wildcards.sh
t/t3301-notes.sh
t/t3507-cherry-pick-conflict.sh
t/t3700-add.sh
t/t4026-color.sh
t/t4150-am.sh
t/t5000-tar-tree.sh
t/t5100-mailinfo.sh
t/t5100/info0012--message-id [new file with mode: 0644]
t/t5100/msg0012--message-id [new file with mode: 0644]
t/t5100/patch0012--message-id [new file with mode: 0644]
t/t5516-fetch-push.sh
t/t5528-push-default.sh
t/t7513-interpret-trailers.sh
t/t9001-send-email.sh
t/t9603-cvsimport-patchsets.sh
t/t9604-cvsimport-timestamps.sh
t/test-lib.sh
trailer.c
tree.c
tree.h
unpack-trees.c
utf8.c
utf8.h
wt-status.c
diff --git a/Documentation/RelNotes/1.8.5.6.txt b/Documentation/RelNotes/1.8.5.6.txt
new file mode 100644 (file)
index 0000000..92ff92b
--- /dev/null
@@ -0,0 +1,34 @@
+Git v1.8.5.6 Release Notes
+==========================
+
+Fixes since v1.8.5.5
+--------------------
+
+ * We used to allow committing a path ".Git/config" with Git that is
+   running on a case sensitive filesystem, but an attempt to check out
+   such a path with Git that runs on a case insensitive filesystem
+   would have clobbered ".git/config", which is definitely not what
+   the user would have expected.  Git now prevents you from tracking
+   a path with ".Git" (in any case combination) as a path component.
+
+ * On Windows, certain path components that are different from ".git"
+   are mapped to ".git", e.g. "git~1/config" is treated as if it were
+   ".git/config".  HFS+ has a similar issue, where certain unicode
+   codepoints are ignored, e.g. ".g\u200cit/config" is treated as if
+   it were ".git/config".  Pathnames with these potential issues are
+   rejected on the affected systems.  Git on systems that are not
+   affected by this issue (e.g. Linux) can also be configured to
+   reject them to ensure cross platform interoperability of the hosted
+   projects.
+
+ * "git fsck" notices a tree object that records such a path that can
+   be confused with ".git", and with receive.fsckObjects configuration
+   set to true, an attempt to "git push" such a tree object will be
+   rejected.  Such a path may not be a problem on a well behaving
+   filesystem but in order to protect those on HFS+ and on case
+   insensitive filesystems, this check is enabled on all platforms.
+
+A big "thanks!" for bringing this issue to us goes to our friends in
+the Mercurial land, namely, Matt Mackall and Augie Fackler.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
diff --git a/Documentation/RelNotes/1.9.5.txt b/Documentation/RelNotes/1.9.5.txt
new file mode 100644 (file)
index 0000000..8d6ac0c
--- /dev/null
@@ -0,0 +1,34 @@
+Git v1.9.5 Release Notes
+========================
+
+Fixes since v1.9.4
+------------------
+
+ * We used to allow committing a path ".Git/config" with Git that is
+   running on a case sensitive filesystem, but an attempt to check out
+   such a path with Git that runs on a case insensitive filesystem
+   would have clobbered ".git/config", which is definitely not what
+   the user would have expected.  Git now prevents you from tracking
+   a path with ".Git" (in any case combination) as a path component.
+
+ * On Windows, certain path components that are different from ".git"
+   are mapped to ".git", e.g. "git~1/config" is treated as if it were
+   ".git/config".  HFS+ has a similar issue, where certain unicode
+   codepoints are ignored, e.g. ".g\u200cit/config" is treated as if
+   it were ".git/config".  Pathnames with these potential issues are
+   rejected on the affected systems.  Git on systems that are not
+   affected by this issue (e.g. Linux) can also be configured to
+   reject them to ensure cross platform interoperability of the hosted
+   projects.
+
+ * "git fsck" notices a tree object that records such a path that can
+   be confused with ".git", and with receive.fsckObjects configuration
+   set to true, an attempt to "git push" such a tree object will be
+   rejected.  Such a path may not be a problem on a well behaving
+   filesystem but in order to protect those on HFS+ and on case
+   insensitive filesystems, this check is enabled on all platforms.
+
+A big "thanks!" for bringing this issue to us goes to our friends in
+the Mercurial land, namely, Matt Mackall and Augie Fackler.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
diff --git a/Documentation/RelNotes/2.0.5.txt b/Documentation/RelNotes/2.0.5.txt
new file mode 100644 (file)
index 0000000..3a16f69
--- /dev/null
@@ -0,0 +1,34 @@
+Git v2.0.5 Release Notes
+========================
+
+Fixes since v2.0.4
+------------------
+
+ * We used to allow committing a path ".Git/config" with Git that is
+   running on a case sensitive filesystem, but an attempt to check out
+   such a path with Git that runs on a case insensitive filesystem
+   would have clobbered ".git/config", which is definitely not what
+   the user would have expected.  Git now prevents you from tracking
+   a path with ".Git" (in any case combination) as a path component.
+
+ * On Windows, certain path components that are different from ".git"
+   are mapped to ".git", e.g. "git~1/config" is treated as if it were
+   ".git/config".  HFS+ has a similar issue, where certain unicode
+   codepoints are ignored, e.g. ".g\u200cit/config" is treated as if
+   it were ".git/config".  Pathnames with these potential issues are
+   rejected on the affected systems.  Git on systems that are not
+   affected by this issue (e.g. Linux) can also be configured to
+   reject them to ensure cross platform interoperability of the hosted
+   projects.
+
+ * "git fsck" notices a tree object that records such a path that can
+   be confused with ".git", and with receive.fsckObjects configuration
+   set to true, an attempt to "git push" such a tree object will be
+   rejected.  Such a path may not be a problem on a well behaving
+   filesystem but in order to protect those on HFS+ and on case
+   insensitive filesystems, this check is enabled on all platforms.
+
+A big "thanks!" for bringing this issue to us goes to our friends in
+the Mercurial land, namely, Matt Mackall and Augie Fackler.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
diff --git a/Documentation/RelNotes/2.1.4.txt b/Documentation/RelNotes/2.1.4.txt
new file mode 100644 (file)
index 0000000..d16e5f0
--- /dev/null
@@ -0,0 +1,34 @@
+Git v2.1.4 Release Notes
+========================
+
+Fixes since v2.1.3
+------------------
+
+ * We used to allow committing a path ".Git/config" with Git that is
+   running on a case sensitive filesystem, but an attempt to check out
+   such a path with Git that runs on a case insensitive filesystem
+   would have clobbered ".git/config", which is definitely not what
+   the user would have expected.  Git now prevents you from tracking
+   a path with ".Git" (in any case combination) as a path component.
+
+ * On Windows, certain path components that are different from ".git"
+   are mapped to ".git", e.g. "git~1/config" is treated as if it were
+   ".git/config".  HFS+ has a similar issue, where certain unicode
+   codepoints are ignored, e.g. ".g\u200cit/config" is treated as if
+   it were ".git/config".  Pathnames with these potential issues are
+   rejected on the affected systems.  Git on systems that are not
+   affected by this issue (e.g. Linux) can also be configured to
+   reject them to ensure cross platform interoperability of the hosted
+   projects.
+
+ * "git fsck" notices a tree object that records such a path that can
+   be confused with ".git", and with receive.fsckObjects configuration
+   set to true, an attempt to "git push" such a tree object will be
+   rejected.  Such a path may not be a problem on a well behaving
+   filesystem but in order to protect those on HFS+ and on case
+   insensitive filesystems, this check is enabled on all platforms.
+
+A big "thanks!" for bringing this issue to us goes to our friends in
+the Mercurial land, namely, Matt Mackall and Augie Fackler.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
diff --git a/Documentation/RelNotes/2.2.1.txt b/Documentation/RelNotes/2.2.1.txt
new file mode 100644 (file)
index 0000000..d5a3cd9
--- /dev/null
@@ -0,0 +1,34 @@
+Git v2.2.1 Release Notes
+========================
+
+Fixes since v2.2
+----------------
+
+ * We used to allow committing a path ".Git/config" with Git that is
+   running on a case sensitive filesystem, but an attempt to check out
+   such a path with Git that runs on a case insensitive filesystem
+   would have clobbered ".git/config", which is definitely not what
+   the user would have expected.  Git now prevents you from tracking
+   a path with ".Git" (in any case combination) as a path component.
+
+ * On Windows, certain path components that are different from ".git"
+   are mapped to ".git", e.g. "git~1/config" is treated as if it were
+   ".git/config".  HFS+ has a similar issue, where certain unicode
+   codepoints are ignored, e.g. ".g\u200cit/config" is treated as if
+   it were ".git/config".  Pathnames with these potential issues are
+   rejected on the affected systems.  Git on systems that are not
+   affected by this issue (e.g. Linux) can also be configured to
+   reject them to ensure cross platform interoperability of the hosted
+   projects.
+
+ * "git fsck" notices a tree object that records such a path that can
+   be confused with ".git", and with receive.fsckObjects configuration
+   set to true, an attempt to "git push" such a tree object will be
+   rejected.  Such a path may not be a problem on a well behaving
+   filesystem but in order to protect those on HFS+ and on case
+   insensitive filesystems, this check is enabled on all platforms.
+
+A big "thanks!" for bringing this issue to us goes to our friends in
+the Mercurial land, namely, Matt Mackall and Augie Fackler.
+
+Also contains typofixes, documentation updates and trivial code clean-ups.
diff --git a/Documentation/RelNotes/2.3.0.txt b/Documentation/RelNotes/2.3.0.txt
new file mode 100644 (file)
index 0000000..880c062
--- /dev/null
@@ -0,0 +1,74 @@
+Git v2.3 Release Notes
+======================
+
+Updates since v2.2
+------------------
+
+Ports
+
+ *
+
+UI, Workflows & Features
+
+ * It was cumbersome to use "GIT_SSH" mechanism when the user wanted
+   to pass an extra set of arguments to the underlying ssh.  A new
+   environment variable GIT_SSH_COMMAND can be used for this.
+
+ * A request to store an empty note via "git notes" meant to remove
+   note from the object but with --allow-empty we will store a
+   (surprise!)  note that is empty.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ *
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.2
+----------------
+
+Unless otherwise noted, all the fixes since v2.2 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * "git checkout $treeish $path", when $path in the index and the
+   working tree already matched what is in $treeish at the $path,
+   still overwrote the $path unnecessarily.
+   (merge c5326bd jk/checkout-from-tree later to maint).
+
+ * "git config --get-color" did not parse its command line arguments
+   carefully.
+   (merge cb35722 jk/colors-fix later to maint).
+
+ * open() emulated on Windows platforms did not give EISDIR upon
+   an attempt to open a directory for writing.
+   (merge ba6fad0 js/windows-open-eisdir-error later to maint).
+
+ * A few code paths used abs() when they should have used labs() on
+   long integers.
+   (merge 83915ba rs/maint-config-use-labs later to maint).
+   (merge 31a8aa1 rs/receive-pack-use-labs later to maint).
+
+ * "gitweb" used to depend on a behaviour recent CGI.pm deprecated.
+   (merge 13dbf46 jk/gitweb-with-newer-cgi-multi-param later to maint).
+
+ * "git init" (hence "git clone") initialized the per-repository
+   configuration file .git/config with x-bit by mistake.
+   (merge 1f32ecf mh/config-flip-xbit-back-after-checking later to maint).
+
+ * Git 2.0 was supposed to make the "simple" mode for the default of
+   "git push", but it didn't.
+   (merge 00a6fa0 jk/push-simple later to maint).
+
+ * "Everyday" document had a broken link.
+   (merge 366c8d4 po/everyday-doc later to maint).
+
+ * A few test fixes.
+   (merge 880ef58 jk/no-perl-tests later to maint).
+
+ * The build procedure did not bother fixing perl and python scripts
+   when NO_PERL and NO_PYTHON build-time configuration changed.
+   (merge ca2051d jk/rebuild-perl-scripts-with-no-perl-seting-change later to maint).
index fa71b5f0b62f43483d02c24d809e6282fa49576a..e3c942e57902abc2767c202978a4bd5060ca449f 100644 (file)
@@ -57,7 +57,8 @@ change, the approach taken by the change, and if relevant how this
 differs substantially from the prior version, are all good things
 to have.
 
-Make sure that you have tests for the bug you are fixing.
+Make sure that you have tests for the bug you are fixing.  See
+t/README for guidance.
 
 When adding a new feature, make sure that you have new tests to show
 the feature triggers the new behaviour when it should, and to show the
index 922072596fdd80eaaa47746fba5e5591223c5108..ac781a44d733f672d5b88281a8bea48bc25be226 100644 (file)
@@ -246,6 +246,17 @@ core.precomposeunicode::
        When false, file names are handled fully transparent by Git,
        which is backward compatible with older versions of Git.
 
+core.protectHFS::
+       If set to true, do not allow checkout of paths that would
+       be considered equivalent to `.git` on an HFS+ filesystem.
+       Defaults to `true` on Mac OS, and `false` elsewhere.
+
+core.protectNTFS::
+       If set to true, do not allow checkout of paths that would
+       cause problems with the NTFS filesystem, e.g. conflict with
+       8.3 "short" names.
+       Defaults to `true` on Windows, and `false` elsewhere.
+
 core.trustctime::
        If false, the ctime differences between the index and the
        working tree are ignored; useful when the inode change time
@@ -839,6 +850,10 @@ accepted are `normal`, `black`, `red`, `green`, `yellow`, `blue`,
 `blink` and `reverse`.  The first color given is the foreground; the
 second is the background.  The position of the attribute, if any,
 doesn't matter.
++
+Colors (foreground and background) may also be given as numbers between
+0 and 255; these use ANSI 256-color mode (but note that not all
+terminals may support this).
 
 color.diff::
        Whether to use ANSI escape sequences to add color to patches.
@@ -2129,6 +2144,13 @@ receive.denyCurrentBranch::
        print a warning of such a push to stderr, but allow the push to
        proceed. If set to false or "ignore", allow such pushes with no
        message. Defaults to "refuse".
++
+Another option is "updateInstead" which will update the working
+directory (must be clean) if pushing into the current branch. This option is
+intended for synchronizing working directories when one side is not easily
+accessible via interactive ssh (e.g. a live web site, hence the requirement
+that the working directory be clean). This mode also comes in handy when
+developing inside a VM to test and fix code on different Operating Systems.
 
 receive.denyNonFastForwards::
        If set to true, git-receive-pack will deny a ref update which is
@@ -2303,6 +2325,7 @@ sendemail.smtpserverport::
 sendemail.smtpserveroption::
 sendemail.smtpuser::
 sendemail.thread::
+sendemail.transferencoding::
 sendemail.validate::
        See linkgit:git-send-email[1] for description.
 
index 9adce372ec6e6ec0d371c411da1e5002baea5f51..cfb74bcf0b3cbc3a3d972af65bff49cea2d9ca4d 100644 (file)
@@ -57,6 +57,17 @@ OPTIONS
 --no-scissors::
        Ignore scissors lines (see linkgit:git-mailinfo[1]).
 
+-m::
+--message-id::
+       Pass the `-m` flag to 'git mailinfo' (see linkgit:git-mailinfo[1]),
+       so that the Message-ID header is added to the commit message.
+       The `am.messageid` configuration variable can be used to specify
+       the default behaviour.
+
+--no-message-id::
+       Do not add the Message-ID header to the commit message.
+       `no-message-id` is useful to override `am.messageid`.
+
 -q::
 --quiet::
        Be quiet. Only print error messages.
index ee2e0917040909bd62df073e20f1120c23a43a63..788a0115805214f4169c50279d94cd31e5f0becc 100644 (file)
@@ -21,6 +21,9 @@ the exclude mechanism) that decides if the pathname is excluded or
 included.  Later patterns within a file take precedence over earlier
 ones.
 
+By default, tracked files are not shown at all since they are not
+subject to exclude rules; but see `--no-index'.
+
 OPTIONS
 -------
 -q, --quiet::
index 164a3c6ede9be2c3a42cf0ba3612403114d4dde5..0947084140f217a4e7fe1d6fa203e4d7e9a36ffe 100644 (file)
@@ -66,6 +66,11 @@ conversion, even with this flag.
 -n::
        Disable all charset re-coding of the metadata.
 
+-m::
+--message-id::
+       Copy the Message-ID header at the end of the commit message.  This
+       is useful in order to associate commits with mailing list discussions.
+
 --scissors::
        Remove everything in body before a scissors line.  A line that
        mainly consists of scissors (either ">8" or "8<") and perforation
index 310f0a5e8c1819f1f49e00b78aacface36ac21ad..851518d531b53966dc6dca1321c71e8381f50e7b 100644 (file)
@@ -9,10 +9,10 @@ SYNOPSIS
 --------
 [verse]
 'git notes' [list [<object>]]
-'git notes' add [-f] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' add [-f] [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
 'git notes' copy [-f] ( --stdin | <from-object> <to-object> )
-'git notes' append [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
-'git notes' edit [<object>]
+'git notes' append [--allow-empty] [-F <file> | -m <msg> | (-c | -C) <object>] [<object>]
+'git notes' edit [--allow-empty] [<object>]
 'git notes' show [<object>]
 'git notes' merge [-v | -q] [-s <strategy> ] <notes-ref>
 'git notes' merge --commit [-v | -q]
@@ -155,6 +155,10 @@ OPTIONS
        Like '-C', but with '-c' the editor is invoked, so that
        the user can further edit the note message.
 
+--allow-empty::
+       Allow an empty note object to be stored. The default behavior is
+       to automatically remove empty notes.
+
 --ref <ref>::
        Manipulate the notes tree in <ref>.  This overrides
        'GIT_NOTES_REF' and the "core.notesRef" configuration.  The ref
@@ -287,7 +291,7 @@ arbitrary files using 'git hash-object':
 ------------
 $ cc *.c
 $ blob=$(git hash-object -w a.out)
-$ git notes --ref=built add -C "$blob" HEAD
+$ git notes --ref=built add --allow-empty -C "$blob" HEAD
 ------------
 
 (You cannot simply use `git notes --ref=built add -F a.out HEAD`
index 21b3f29c3bc603df74e07226d27ad63faa6fae24..b17283ab7a1cc73c5ec741e0da1128bea2a57a65 100644 (file)
@@ -34,7 +34,7 @@ When the command line does not specify what to push with `<refspec>...`
 arguments or `--all`, `--mirror`, `--tags` options, the command finds
 the default `<refspec>` by consulting `remote.*.push` configuration,
 and if it is not found, honors `push.default` configuration to decide
-what to push (See gitlink:git-config[1] for the meaning of `push.default`).
+what to push (See linkgit:git-config[1] for the meaning of `push.default`).
 
 
 OPTIONS[[OPTIONS]]
index a60776eb579e10507814c643cc292dfa99750aa9..a9efa5c2ec18776e12c5bdbb250df5b8e0f41581 100644 (file)
@@ -131,6 +131,16 @@ Note that no attempts whatsoever are made to validate the encoding.
        Specify encoding of compose message. Default is the value of the
        'sendemail.composeencoding'; if that is unspecified, UTF-8 is assumed.
 
+--transfer-encoding=(7bit|8bit|quoted-printable|base64)::
+       Specify the transfer encoding to be used to send the message over SMTP.
+       7bit will fail upon encountering a non-ASCII message.  quoted-printable
+       can be useful when the repository contains files that contain carriage
+       returns, but makes the raw patch email file (as saved from a MUA) much
+       harder to inspect manually.  base64 is even more fool proof, but also
+       even more opaque.  Default is the value of the 'sendemail.transferEncoding'
+       configuration value; if that is unspecified, git will use 8bit and not
+       add a Content-Transfer-Encoding header.
+
 
 Sending
 ~~~~~~~
index c87bfcb674b9a1026ab0d7b8e4907d538c99c7e5..6c6e989074c58840ca1c8c4e6f4cad0b8fdb718d 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git stripspace' [-s | --strip-comments] < input
+'git stripspace' [-c | --comment-lines] < input
 
 DESCRIPTION
 -----------
index afb48d39bbd229bb0a28d2e7e2fb835e63248525..179615195fd260d4d6d87bc1cda6f5c8a699719d 100644 (file)
@@ -43,40 +43,45 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.2.0/git.html[documentation for release 2.2]
+* link:v2.2.1/git.html[documentation for release 2.2.1]
 
 * release notes for
+  link:RelNotes/2.2.1.txt[2.2.1],
   link:RelNotes/2.2.0.txt[2.2].
 
-* link:v2.1.3/git.html[documentation for release 2.1.3]
+* link:v2.1.4/git.html[documentation for release 2.1.4]
 
 * release notes for
+  link:RelNotes/2.1.4.txt[2.1.4],
   link:RelNotes/2.1.3.txt[2.1.3],
   link:RelNotes/2.1.2.txt[2.1.2],
   link:RelNotes/2.1.1.txt[2.1.1],
   link:RelNotes/2.1.0.txt[2.1].
 
-* link:v2.0.4/git.html[documentation for release 2.0.4]
+* link:v2.0.5/git.html[documentation for release 2.0.5]
 
 * release notes for
+  link:RelNotes/2.0.5.txt[2.0.5],
   link:RelNotes/2.0.4.txt[2.0.4],
   link:RelNotes/2.0.3.txt[2.0.3],
   link:RelNotes/2.0.2.txt[2.0.2],
   link:RelNotes/2.0.1.txt[2.0.1],
   link:RelNotes/2.0.0.txt[2.0.0].
 
-* link:v1.9.4/git.html[documentation for release 1.9.4]
+* link:v1.9.5/git.html[documentation for release 1.9.5]
 
 * release notes for
+  link:RelNotes/1.9.5.txt[1.9.5],
   link:RelNotes/1.9.4.txt[1.9.4],
   link:RelNotes/1.9.3.txt[1.9.3],
   link:RelNotes/1.9.2.txt[1.9.2],
   link:RelNotes/1.9.1.txt[1.9.1],
   link:RelNotes/1.9.0.txt[1.9.0].
 
-* link:v1.8.5.5/git.html[documentation for release 1.8.5.5]
+* link:v1.8.5.6/git.html[documentation for release 1.8.5.6]
 
 * release notes for
+  link:RelNotes/1.8.5.6.txt[1.8.5.6],
   link:RelNotes/1.8.5.5.txt[1.8.5.5],
   link:RelNotes/1.8.5.4.txt[1.8.5.4],
   link:RelNotes/1.8.5.3.txt[1.8.5.3],
@@ -881,19 +886,21 @@ other
        and the `core.editor` option in linkgit:git-config[1].
 
 'GIT_SSH'::
-       If this environment variable is set then 'git fetch'
-       and 'git push' will use this command instead
-       of 'ssh' when they need to connect to a remote system.
-       The '$GIT_SSH' command will be given exactly two or
-       four arguments: the 'username@host' (or just 'host')
-       from the URL and the shell command to execute on that
-       remote system, optionally preceded by '-p' (literally) and
-       the 'port' from the URL when it specifies something other
-       than the default SSH port.
+'GIT_SSH_COMMAND'::
+       If either of these environment variables is set then 'git fetch'
+       and 'git push' will use the specified command instead of 'ssh'
+       when they need to connect to a remote system.
+       The command will be given exactly two or four arguments: the
+       'username@host' (or just 'host') from the URL and the shell
+       command to execute on that remote system, optionally preceded by
+       '-p' (literally) and the 'port' from the URL when it specifies
+       something other than the default SSH port.
 +
-To pass options to the program that you want to list in GIT_SSH
-you will need to wrap the program and options into a shell script,
-then set GIT_SSH to refer to the shell script.
+`$GIT_SSH_COMMAND` takes precedence over `$GIT_SSH`, and is interpreted
+by the shell, which allows additional arguments to be included.
+`$GIT_SSH` on the other hand must be just the path to a program
+(which can be a wrapper shell script, if additional arguments are
+needed).
 +
 Usually it is easier to configure any desired options through your
 personal `.ssh/config` file.  Please consult your ssh documentation
@@ -906,6 +913,10 @@ for further details.
        and read the password from its STDOUT. See also the 'core.askpass'
        option in linkgit:git-config[1].
 
+'GIT_TERMINAL_PROMPT'::
+       If this environment variable is set to `0`, git will not prompt
+       on the terminal (e.g., when asking for HTTP authentication).
+
 'GIT_CONFIG_NOSYSTEM'::
        Whether to skip reading settings from the system-wide
        `$(prefix)/etc/gitconfig` file.  This environment variable can
index c1b42a40d3c0c297994cd2966b60cd48629e57fa..e44426dd041516b8d9220ce9f1b2413be0319703 100644 (file)
@@ -248,7 +248,10 @@ FORMAT` in linkgit:git-credential[7] for a detailed specification).
 For a `get` operation, the helper should produce a list of attributes
 on stdout in the same format. A helper is free to produce a subset, or
 even no values at all if it has nothing useful to provide. Any provided
-attributes will overwrite those already known about by Git.
+attributes will overwrite those already known about by Git.  If a helper
+outputs a `quit` attribute with a value of `true` or `1`, no further
+helpers will be consulted, nor will the user be prompted (if no
+credential has been provided, the operation will then fail).
 
 For a `store` or `erase` operation, the helper's output is ignored.
 If it fails to perform the requested operation, it may complain to
index d51a6579c85f7417708f918cf9807447b1508e3c..c08402b12e88930ca37cce6d3b4537d7f360852f 100644 (file)
@@ -29,7 +29,7 @@ member (you need this if you add things later) and you should set the
   `unsorted_string_list_has_string` and get it from the list using
   `string_list_lookup` for sorted lists.
 
-. Can sort an unsorted list using `sort_string_list`.
+. Can sort an unsorted list using `string_list_sort`.
 
 . Can remove duplicate items from a sorted list using
   `string_list_remove_duplicates`.
@@ -146,7 +146,7 @@ write `string_list_insert(...)->util = ...;`.
        ownership of a malloc()ed string to a `string_list` that has
        `strdup_string` set.
 
-`sort_string_list`::
+`string_list_sort`::
 
        Sort the list's entries by string value in `strcmp()` order.
 
index 827006ba611c7e8be0ae4d92eaeaa321d7b9feb2..86bc0c2f04b8f3b45412a407a4e6124b16897867 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -191,6 +191,10 @@ all::
 # Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support
 # the executable mode bit, but doesn't really do so.
 #
+# Define NEEDS_MODE_TRANSLATION if your OS strays from the typical file type
+# bits in mode values (e.g. z/OS defines I_SFMT to 0xFF000000 as opposed to the
+# usual 0xF000).
+#
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
 #
 # Define NO_UNIX_SOCKETS if your system does not offer unix sockets.
@@ -1230,6 +1234,10 @@ endif
 ifdef NO_TRUSTABLE_FILEMODE
        BASIC_CFLAGS += -DNO_TRUSTABLE_FILEMODE
 endif
+ifdef NEEDS_MODE_TRANSLATION
+       COMPAT_CFLAGS += -DNEEDS_MODE_TRANSLATION
+       COMPAT_OBJS += compat/stat.o
+endif
 ifdef NO_IPV6
        BASIC_CFLAGS += -DNO_IPV6
 endif
@@ -1662,7 +1670,7 @@ GIT-SCRIPT-DEFINES: FORCE
             fi
 
 
-$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh GIT-SCRIPT-DEFINES
+$(SCRIPT_SH_GEN) : % : %.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
@@ -1676,8 +1684,11 @@ git.res: git.rc GIT-VERSION-FILE
          $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
          -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
 
+# This makes sure we depend on the NO_PERL setting itself.
+$(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
+
 ifndef NO_PERL
-$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
+$(SCRIPT_PERL_GEN): perl/perl.mak
 
 perl/perl.mak: perl/PM.stamp
 
@@ -1690,7 +1701,7 @@ perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
        $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
 
 PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
-$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
+$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
        INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
        INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
@@ -1724,7 +1735,7 @@ git-instaweb: git-instaweb.sh gitweb GIT-SCRIPT-DEFINES
        chmod +x $@+ && \
        mv $@+ $@
 else # NO_PERL
-$(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
+$(SCRIPT_PERL_GEN) git-instaweb: % : unimplemented.sh
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
@@ -1733,6 +1744,9 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
        mv $@+ $@
 endif # NO_PERL
 
+# This makes sure we depend on the NO_PYTHON setting itself.
+$(SCRIPT_PYTHON_GEN): GIT-BUILD-OPTIONS
+
 ifndef NO_PYTHON
 $(SCRIPT_PYTHON_GEN): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
 $(SCRIPT_PYTHON_GEN): % : %.py
index c473b35ad95f0fc1ff05c19493ded96ca6a48f55..9257c74b5cff52da962e55a4f96d0b9d974cf362 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.2.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.3.0.txt
\ No newline at end of file
index 94a9981adf72c437f8660c66b67506b50ed8d3a9..9e30246b6402454e8d92fb6aa24a1a07c534b0ef 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -157,18 +157,26 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
        return write_entry(args, sha1, path.buf, path.len, mode);
 }
 
+static int write_archive_entry_buf(const unsigned char *sha1, struct strbuf *base,
+               const char *filename, unsigned mode, int stage,
+               void *context)
+{
+       return write_archive_entry(sha1, base->buf, base->len,
+                                    filename, mode, stage, context);
+}
+
 static void queue_directory(const unsigned char *sha1,
-               const char *base, int baselen, const char *filename,
+               struct strbuf *base, const char *filename,
                unsigned mode, int stage, struct archiver_context *c)
 {
        struct directory *d;
-       d = xmallocz(sizeof(*d) + baselen + 1 + strlen(filename));
+       d = xmallocz(sizeof(*d) + base->len + 1 + strlen(filename));
        d->up      = c->bottom;
-       d->baselen = baselen;
+       d->baselen = base->len;
        d->mode    = mode;
        d->stage   = stage;
        c->bottom  = d;
-       d->len = sprintf(d->path, "%.*s%s/", baselen, base, filename);
+       d->len = sprintf(d->path, "%.*s%s/", (int)base->len, base->buf, filename);
        hashcpy(d->sha1, sha1);
 }
 
@@ -191,28 +199,28 @@ static int write_directory(struct archiver_context *c)
 }
 
 static int queue_or_write_archive_entry(const unsigned char *sha1,
-               const char *base, int baselen, const char *filename,
+               struct strbuf *base, const char *filename,
                unsigned mode, int stage, void *context)
 {
        struct archiver_context *c = context;
 
        while (c->bottom &&
-              !(baselen >= c->bottom->len &&
-                !strncmp(base, c->bottom->path, c->bottom->len))) {
+              !(base->len >= c->bottom->len &&
+                !strncmp(base->buf, c->bottom->path, c->bottom->len))) {
                struct directory *next = c->bottom->up;
                free(c->bottom);
                c->bottom = next;
        }
 
        if (S_ISDIR(mode)) {
-               queue_directory(sha1, base, baselen, filename,
+               queue_directory(sha1, base, filename,
                                mode, stage, c);
                return READ_TREE_RECURSIVE;
        }
 
        if (write_directory(c))
                return -1;
-       return write_archive_entry(sha1, base, baselen, filename, mode,
+       return write_archive_entry(sha1, base->buf, base->len, filename, mode,
                                   stage, context);
 }
 
@@ -260,7 +268,7 @@ int write_archive_entries(struct archiver_args *args,
        err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
                                  args->pathspec.has_wildcard ?
                                  queue_or_write_archive_entry :
-                                 write_archive_entry,
+                                 write_archive_entry_buf,
                                  &context);
        if (err == READ_TREE_RECURSIVE)
                err = 0;
@@ -286,14 +294,14 @@ static const struct archiver *lookup_archiver(const char *name)
        return NULL;
 }
 
-static int reject_entry(const unsigned char *sha1, const char *base,
-                       int baselen, const char *filename, unsigned mode,
+static int reject_entry(const unsigned char *sha1, struct strbuf *base,
+                       const char *filename, unsigned mode,
                        int stage, void *context)
 {
        int ret = -1;
        if (S_ISDIR(mode)) {
                struct strbuf sb = STRBUF_INIT;
-               strbuf_addstr(&sb, base);
+               strbuf_addbuf(&sb, base);
                strbuf_addstr(&sb, filename);
                if (!match_pathspec(context, sb.buf, sb.len, 0, NULL, 1))
                        ret = READ_TREE_RECURSIVE;
index ae6d3e262bcbb3dc57c0ddf146791380259b31be..1074e3234964deb279c3a487e9efd164afdb9c6c 100644 (file)
@@ -284,7 +284,7 @@ static int add_files(struct dir_struct *dir, int flags)
                for (i = 0; i < dir->ignored_nr; i++)
                        fprintf(stderr, "%s\n", dir->ignored[i]->name);
                fprintf(stderr, _("Use -f if you really want to add them.\n"));
-               die(_("no files added"));
+               exit_status = 1;
        }
 
        for (i = 0; i < dir->nr; i++)
index 6696ea4c3ffd4f7af04f3f56609a78a74a0a0b55..0aad91283959bce51aba596c6f3d2d0925e5ba92 100644 (file)
@@ -3728,7 +3728,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
                        if (!preimage_sha1_in_gitlink_patch(patch, sha1))
                                ; /* ok, the textual part looks sane */
                        else
-                               die("sha1 information is lacking or useless for submoule %s",
+                               die("sha1 information is lacking or useless for submodule %s",
                                    name);
                } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) {
                        ; /* ok */
@@ -4180,7 +4180,7 @@ static int write_out_results(struct patch *list)
        if (cpath.nr) {
                struct string_list_item *item;
 
-               sort_string_list(&cpath);
+               string_list_sort(&cpath);
                for_each_string_list_item(item, &cpath)
                        fprintf(stderr, "U %s\n", item->string);
                string_list_clear(&cpath, 0);
index 5410dacea0699f70cd942837394c06eac99873d1..52d6cbb0a84e2693fc53c88ca1fb4cd899b26a91 100644 (file)
@@ -62,23 +62,41 @@ static int post_checkout_hook(struct commit *old, struct commit *new,
 
 }
 
-static int update_some(const unsigned char *sha1, const char *base, int baselen,
+static int update_some(const unsigned char *sha1, struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
 {
        int len;
        struct cache_entry *ce;
+       int pos;
 
        if (S_ISDIR(mode))
                return READ_TREE_RECURSIVE;
 
-       len = baselen + strlen(pathname);
+       len = base->len + strlen(pathname);
        ce = xcalloc(1, cache_entry_size(len));
        hashcpy(ce->sha1, sha1);
-       memcpy(ce->name, base, baselen);
-       memcpy(ce->name + baselen, pathname, len - baselen);
+       memcpy(ce->name, base->buf, base->len);
+       memcpy(ce->name + base->len, pathname, len - base->len);
        ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
+
+       /*
+        * If the entry is the same as the current index, we can leave the old
+        * entry in place. Whether it is UPTODATE or not, checkout_entry will
+        * do the right thing.
+        */
+       pos = cache_name_pos(ce->name, ce->ce_namelen);
+       if (pos >= 0) {
+               struct cache_entry *old = active_cache[pos];
+               if (ce->ce_mode == old->ce_mode &&
+                   !hashcmp(ce->sha1, old->sha1)) {
+                       old->ce_flags |= CE_UPDATE;
+                       free(ce);
+                       return 0;
+               }
+       }
+
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
 }
index e108c5301564a86d396d72169f40cd24a9916a1f..cda74e9a68f75fe3809a6dfa3901448be88eb7e5 100644 (file)
@@ -800,32 +800,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
        if (clean_message_contents)
                stripspace(&sb, 0);
 
-       if (signoff) {
-               /*
-                * See if we have a Conflicts: block at the end. If yes, count
-                * its size, so we can ignore it.
-                */
-               int ignore_footer = 0;
-               int i, eol, previous = 0;
-               const char *nl;
-
-               for (i = 0; i < sb.len; i++) {
-                       nl = memchr(sb.buf + i, '\n', sb.len - i);
-                       if (nl)
-                               eol = nl - sb.buf;
-                       else
-                               eol = sb.len;
-                       if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
-                               ignore_footer = sb.len - previous;
-                               break;
-                       }
-                       while (i < eol)
-                               i++;
-                       previous = eol;
-               }
-
-               append_signoff(&sb, ignore_footer, 0);
-       }
+       if (signoff)
+               append_signoff(&sb, ignore_non_trailer(&sb), 0);
 
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
                die_errno(_("could not write commit template"));
index 8cc2604069cd008297081988ac807412ba9901a7..15a7bea936d667042cef27f83c86a863df2caa8f 100644 (file)
@@ -69,8 +69,8 @@ static struct option builtin_config_options[] = {
        OPT_BIT(0, "remove-section", &actions, N_("remove a section: name"), ACTION_REMOVE_SECTION),
        OPT_BIT('l', "list", &actions, N_("list all"), ACTION_LIST),
        OPT_BIT('e', "edit", &actions, N_("open an editor"), ACTION_EDIT),
-       OPT_STRING(0, "get-color", &get_color_slot, N_("slot"), N_("find the color configured: [default]")),
-       OPT_STRING(0, "get-colorbool", &get_colorbool_slot, N_("slot"), N_("find the color setting: [stdout-is-tty]")),
+       OPT_BIT(0, "get-color", &actions, N_("find the color configured: slot [default]"), ACTION_GET_COLOR),
+       OPT_BIT(0, "get-colorbool", &actions, N_("find the color setting: slot [stdout-is-tty]"), ACTION_GET_COLORBOOL),
        OPT_GROUP(N_("Type")),
        OPT_BIT(0, "bool", &types, N_("value is \"true\" or \"false\""), TYPE_BOOL),
        OPT_BIT(0, "int", &types, N_("value is decimal number"), TYPE_INT),
@@ -303,8 +303,9 @@ static int git_get_color_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
-static void get_color(const char *def_color)
+static void get_color(const char *var, const char *def_color)
 {
+       get_color_slot = var;
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config_with_options(git_get_color_config, NULL,
@@ -333,8 +334,9 @@ static int git_get_colorbool_config(const char *var, const char *value,
        return 0;
 }
 
-static int get_colorbool(int print)
+static int get_colorbool(const char *var, int print)
 {
+       get_colorbool_slot = var;
        get_colorbool_found = -1;
        get_diff_color_found = -1;
        get_color_ui_found = -1;
@@ -532,12 +534,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
 
-       if (get_color_slot)
-           actions |= ACTION_GET_COLOR;
-       if (get_colorbool_slot)
-           actions |= ACTION_GET_COLORBOOL;
-
-       if ((get_color_slot || get_colorbool_slot) && types) {
+       if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && types) {
                error("--get-color and variable type are incoherent");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
@@ -568,8 +565,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                }
        }
        else if (actions == ACTION_EDIT) {
-               const char *config_file = given_config_source.file ?
-                       given_config_source.file : git_path("config");
+               char *config_file;
+
                check_argc(argc, 0, 0);
                if (!given_config_source.file && nongit)
                        die("not in a git directory");
@@ -578,6 +575,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                if (given_config_source.blob)
                        die("editing blobs is not supported");
                git_config(git_default_config, NULL);
+               config_file = xstrdup(given_config_source.file ?
+                                     given_config_source.file : git_path("config"));
                if (use_global_config) {
                        int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
                        if (fd) {
@@ -590,6 +589,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                                die_errno(_("cannot create configuration file %s"), config_file);
                }
                launch_editor(config_file, NULL, NULL);
+               free(config_file);
        }
        else if (actions == ACTION_SET) {
                int ret;
@@ -683,12 +683,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                        die("No such section!");
        }
        else if (actions == ACTION_GET_COLOR) {
-               get_color(argv[0]);
+               check_argc(argc, 1, 2);
+               get_color(argv[0], argv[1]);
        }
        else if (actions == ACTION_GET_COLORBOOL) {
-               if (argc == 1)
-                       color_stdout_is_tty = git_config_bool("command line", argv[0]);
-               return get_colorbool(argc != 0);
+               check_argc(argc, 1, 2);
+               if (argc == 2)
+                       color_stdout_is_tty = git_config_bool("command line", argv[1]);
+               return get_colorbool(argv[0], argc == 2);
        }
 
        return 0;
index 603a90e29b808a86fa39f031d13a1ca94f5d236e..f3ce004d53c55e3f53d6a90d21d2877368676f08 100644 (file)
@@ -1075,7 +1075,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_BIT(0 , "python", &quote_style,
                        N_("quote placeholders suitably for python"), QUOTE_PYTHON),
                OPT_BIT(0 , "tcl",  &quote_style,
-                       N_("quote placeholders suitably for tcl"), QUOTE_TCL),
+                       N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
 
                OPT_GROUP(""),
                OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
index b3c818ee01b6f64c7cf00f2e58d372f4dc8f0250..e78c135e010afa3f9447992ae85a7cc404d0192b 100644 (file)
@@ -321,16 +321,18 @@ static void setup_man_path(void)
 {
        struct strbuf new_path = STRBUF_INIT;
        const char *old_path = getenv("MANPATH");
+       char *git_man_path = system_path(GIT_MAN_PATH);
 
        /* We should always put ':' after our path. If there is no
         * old_path, the ':' at the end will let 'man' to try
         * system-wide paths after ours to find the manual page. If
         * there is old_path, we need ':' as delimiter. */
-       strbuf_addstr(&new_path, system_path(GIT_MAN_PATH));
+       strbuf_addstr(&new_path, git_man_path);
        strbuf_addch(&new_path, ':');
        if (old_path)
                strbuf_addstr(&new_path, old_path);
 
+       free(git_man_path);
        setenv("MANPATH", new_path.buf, 1);
 
        strbuf_release(&new_path);
@@ -380,8 +382,10 @@ static void show_info_page(const char *git_cmd)
 static void get_html_page_path(struct strbuf *page_path, const char *page)
 {
        struct stat st;
+       char *to_free = NULL;
+
        if (!html_path)
-               html_path = system_path(GIT_HTML_PATH);
+               html_path = to_free = system_path(GIT_HTML_PATH);
 
        /* Check that we have a git documentation directory. */
        if (!strstr(html_path, "://")) {
@@ -392,6 +396,7 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
 
        strbuf_init(page_path, 0);
        strbuf_addf(page_path, "%s/%s.html", html_path, page);
+       free(to_free);
 }
 
 /*
index 587a5055ed677c3541c85101d944ffb0a19b1962..9966522b4a97a0d1ae430e581125fbfd1d5609ac 100644 (file)
@@ -119,15 +119,18 @@ static void copy_templates(const char *template_dir)
        DIR *dir;
        const char *git_dir = get_git_dir();
        int len = strlen(git_dir);
+       char *to_free = NULL;
 
        if (!template_dir)
                template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
        if (!template_dir)
                template_dir = init_db_template_dir;
        if (!template_dir)
-               template_dir = system_path(DEFAULT_GIT_TEMPLATE_DIR);
-       if (!template_dir[0])
+               template_dir = to_free = system_path(DEFAULT_GIT_TEMPLATE_DIR);
+       if (!template_dir[0]) {
+               free(to_free);
                return;
+       }
        template_len = strlen(template_dir);
        if (PATH_MAX <= (template_len+strlen("/config")))
                die(_("insanely long template path %s"), template_dir);
@@ -139,7 +142,7 @@ static void copy_templates(const char *template_dir)
        dir = opendir(template_path);
        if (!dir) {
                warning(_("templates not found %s"), template_dir);
-               return;
+               goto free_return;
        }
 
        /* Make sure that template is from the correct vintage */
@@ -155,8 +158,7 @@ static void copy_templates(const char *template_dir)
                        "a wrong format version %d from '%s'"),
                        repository_format_version,
                        template_dir);
-               closedir(dir);
-               return;
+               goto close_free_return;
        }
 
        memcpy(path, git_dir, len);
@@ -166,7 +168,10 @@ static void copy_templates(const char *template_dir)
        copy_templates_1(path, len,
                         template_path, template_len,
                         dir);
+close_free_return:
        closedir(dir);
+free_return:
+       free(to_free);
 }
 
 static int git_init_db_config(const char *k, const char *v, void *cb)
@@ -254,7 +259,10 @@ static int create_default_files(const char *template_path)
                struct stat st2;
                filemode = (!chmod(path, st1.st_mode ^ S_IXUSR) &&
                                !lstat(path, &st2) &&
-                               st1.st_mode != st2.st_mode);
+                               st1.st_mode != st2.st_mode &&
+                               !chmod(path, st1.st_mode));
+               if (filemode && !reinit && (st1.st_mode & S_IXUSR))
+                       filemode = 0;
        }
        git_config_set("core.filemode", filemode ? "true" : "false");
 
index 734aab3a73185964d18c82fe5eb31008bbc409b7..f2a9f0156d8a8b15c7c879355b137b669dea8cf2 100644 (file)
@@ -489,7 +489,7 @@ static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
 }
 
 static int show_tree_object(const unsigned char *sha1,
-               const char *base, int baselen,
+               struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
 {
        printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
index 51184dfa2efa46323e79c4504121bf5fe948a6d2..3b04a0f082a48c7b9c16f57a5bf4be94c159204c 100644 (file)
@@ -61,10 +61,11 @@ static int show_recursive(const char *base, int baselen, const char *pathname)
        }
 }
 
-static int show_tree(const unsigned char *sha1, const char *base, int baselen,
+static int show_tree(const unsigned char *sha1, struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
 {
        int retval = 0;
+       int baselen;
        const char *type = blob_type;
 
        if (S_ISGITLINK(mode)) {
@@ -79,7 +80,7 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
                 */
                type = commit_type;
        } else if (S_ISDIR(mode)) {
-               if (show_recursive(base, baselen, pathname)) {
+               if (show_recursive(base->buf, base->len, pathname)) {
                        retval = READ_TREE_RECURSIVE;
                        if (!(ls_options & LS_SHOW_TREES))
                                return retval;
@@ -89,10 +90,6 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
        else if (ls_options & LS_TREE_ONLY)
                return 0;
 
-       if (chomp_prefix &&
-           (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))
-               return 0;
-
        if (!(ls_options & LS_NAME_ONLY)) {
                if (ls_options & LS_SHOW_SIZE) {
                        char size_text[24];
@@ -112,8 +109,12 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
                        printf("%06o %s %s\t", mode, type,
                               find_unique_abbrev(sha1, abbrev));
        }
-       write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,
-                         pathname, stdout, line_termination);
+       baselen = base->len;
+       strbuf_addstr(base, pathname);
+       write_name_quoted_relative(base->buf,
+                                  chomp_prefix ? ls_tree_prefix : NULL,
+                                  stdout, line_termination);
+       strbuf_setlen(base, baselen);
        return retval;
 }
 
@@ -173,7 +174,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
         * cannot be lifted until it is converted to use
         * match_pathspec() or tree_entry_interesting()
         */
-       parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE,
+       parse_pathspec(&pathspec, PATHSPEC_GLOB | PATHSPEC_ICASE |
+                                 PATHSPEC_EXCLUDE,
                       PATHSPEC_PREFER_CWD,
                       prefix, argv + 1);
        for (i = 0; i < pathspec.nr; i++)
index 6a14d2985d12394d343cb91290111d6a8c0a5d06..c8a47c173d011713a57dc744a5f5e5ea5434eefa 100644 (file)
@@ -15,6 +15,7 @@ static const char *metainfo_charset;
 static struct strbuf line = STRBUF_INIT;
 static struct strbuf name = STRBUF_INIT;
 static struct strbuf email = STRBUF_INIT;
+static char *message_id;
 
 static enum  {
        TE_DONTCARE, TE_QP, TE_BASE64
@@ -24,6 +25,7 @@ static struct strbuf charset = STRBUF_INIT;
 static int patch_lines;
 static struct strbuf **p_hdr_data, **s_hdr_data;
 static int use_scissors;
+static int add_message_id;
 static int use_inbody_headers = 1;
 
 #define MAX_HDR_PARSED 10
@@ -198,6 +200,12 @@ static void handle_content_type(struct strbuf *line)
        }
 }
 
+static void handle_message_id(const struct strbuf *line)
+{
+       if (add_message_id)
+               message_id = strdup(line->buf);
+}
+
 static void handle_content_transfer_encoding(const struct strbuf *line)
 {
        if (strcasestr(line->buf, "base64"))
@@ -342,6 +350,14 @@ static int check_header(const struct strbuf *line,
                ret = 1;
                goto check_header_out;
        }
+       if (cmp_header(line, "Message-Id")) {
+               len = strlen("Message-Id: ");
+               strbuf_add(&sb, line->buf + len, line->len - len);
+               decode_header(&sb);
+               handle_message_id(&sb);
+               ret = 1;
+               goto check_header_out;
+       }
 
        /* for inbody stuff */
        if (starts_with(line->buf, ">From") && isspace(line->buf[5])) {
@@ -816,6 +832,8 @@ static int handle_commit_msg(struct strbuf *line)
        }
 
        if (patchbreak(line)) {
+               if (message_id)
+                       fprintf(cmitmsg, "Message-Id: %s\n", message_id);
                fclose(cmitmsg);
                cmitmsg = NULL;
                return 1;
@@ -1013,7 +1031,7 @@ static int git_mailinfo_config(const char *var, const char *value, void *unused)
 }
 
 static const char mailinfo_usage[] =
-       "git mailinfo [-k|-b] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
+       "git mailinfo [-k|-b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
 
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
@@ -1032,6 +1050,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
                        keep_subject = 1;
                else if (!strcmp(argv[1], "-b"))
                        keep_non_patch_brackets_in_subject = 1;
+               else if (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--message-id"))
+                       add_message_id = 1;
                else if (!strcmp(argv[1], "-u"))
                        metainfo_charset = def_charset;
                else if (!strcmp(argv[1], "-n"))
index bebbe5b3081ebe2602abaf0dc9508a1342f01086..215d4856e507f671139f41c73b57e661d8d1bf87 100644 (file)
@@ -29,6 +29,7 @@
 #include "remote.h"
 #include "fmt-merge-msg.h"
 #include "gpg-interface.h"
+#include "sequencer.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -880,28 +881,19 @@ static int finish_automerge(struct commit *head,
        return 0;
 }
 
-static int suggest_conflicts(int renormalizing)
+static int suggest_conflicts(void)
 {
        const char *filename;
        FILE *fp;
-       int pos;
+       struct strbuf msgbuf = STRBUF_INIT;
 
        filename = git_path("MERGE_MSG");
        fp = fopen(filename, "a");
        if (!fp)
                die_errno(_("Could not open '%s' for writing"), filename);
-       fprintf(fp, "\nConflicts:\n");
-       for (pos = 0; pos < active_nr; pos++) {
-               const struct cache_entry *ce = active_cache[pos];
-
-               if (ce_stage(ce)) {
-                       fprintf(fp, "\t%s\n", ce->name);
-                       while (pos + 1 < active_nr &&
-                                       !strcmp(ce->name,
-                                               active_cache[pos + 1]->name))
-                               pos++;
-               }
-       }
+
+       append_conflicts_hint(&msgbuf);
+       fputs(msgbuf.buf, fp);
        fclose(fp);
        rerere(allow_rerere_auto);
        printf(_("Automatic merge failed; "
@@ -1550,7 +1542,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                fprintf(stderr, _("Automatic merge went well; "
                        "stopped before committing as requested\n"));
        else
-               ret = suggest_conflicts(option_renormalize);
+               ret = suggest_conflicts();
 
 done:
        free(branch_to_free);
index 68b6cd8cc1ded390df592e1c6536d70bf1ffc8a0..a9f37d045641236ee910f3eb22be6ac8d04e386a 100644 (file)
 
 static const char * const git_notes_usage[] = {
        N_("git notes [--ref <notes_ref>] [list [<object>]]"),
-       N_("git notes [--ref <notes_ref>] add [-f] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+       N_("git notes [--ref <notes_ref>] add [-f] [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
        N_("git notes [--ref <notes_ref>] copy [-f] <from-object> <to-object>"),
-       N_("git notes [--ref <notes_ref>] append [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
-       N_("git notes [--ref <notes_ref>] edit [<object>]"),
+       N_("git notes [--ref <notes_ref>] append [--allow-empty] [-m <msg> | -F <file> | (-c | -C) <object>] [<object>]"),
+       N_("git notes [--ref <notes_ref>] edit [--allow-empty] [<object>]"),
        N_("git notes [--ref <notes_ref>] show [<object>]"),
        N_("git notes [--ref <notes_ref>] merge [-v | -q] [-s <strategy> ] <notes_ref>"),
        N_("git notes merge --commit [-v | -q]"),
@@ -92,12 +92,22 @@ static const char * const git_notes_get_ref_usage[] = {
 static const char note_template[] =
        "\nWrite/edit the notes for the following object:\n";
 
-struct msg_arg {
+struct note_data {
        int given;
        int use_editor;
+       char *edit_path;
        struct strbuf buf;
 };
 
+static void free_note_data(struct note_data *d)
+{
+       if (d->edit_path) {
+               unlink_or_warn(d->edit_path);
+               free(d->edit_path);
+       }
+       strbuf_release(&d->buf);
+}
+
 static int list_each_note(const unsigned char *object_sha1,
                const unsigned char *note_sha1, char *note_path,
                void *cb_data)
@@ -106,7 +116,7 @@ static int list_each_note(const unsigned char *object_sha1,
        return 0;
 }
 
-static void write_note_data(int fd, const unsigned char *sha1)
+static void copy_obj_to_fd(int fd, const unsigned char *sha1)
 {
        unsigned long size;
        enum object_type type;
@@ -149,26 +159,23 @@ static void write_commented_object(int fd, const unsigned char *object)
                    sha1_to_hex(object));
 }
 
-static void create_note(const unsigned char *object, struct msg_arg *msg,
-                       int append_only, const unsigned char *prev,
-                       unsigned char *result)
+static void prepare_note_data(const unsigned char *object, struct note_data *d,
+               const unsigned char *old_note)
 {
-       char *path = NULL;
-
-       if (msg->use_editor || !msg->given) {
+       if (d->use_editor || !d->given) {
                int fd;
                struct strbuf buf = STRBUF_INIT;
 
                /* write the template message before editing: */
-               path = git_pathdup("NOTES_EDITMSG");
-               fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+               d->edit_path = git_pathdup("NOTES_EDITMSG");
+               fd = open(d->edit_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
                if (fd < 0)
-                       die_errno(_("could not create file '%s'"), path);
+                       die_errno(_("could not create file '%s'"), d->edit_path);
 
-               if (msg->given)
-                       write_or_die(fd, msg->buf.buf, msg->buf.len);
-               else if (prev && !append_only)
-                       write_note_data(fd, prev);
+               if (d->given)
+                       write_or_die(fd, d->buf.buf, d->buf.len);
+               else if (old_note)
+                       copy_obj_to_fd(fd, old_note);
 
                strbuf_addch(&buf, '\n');
                strbuf_add_commented_lines(&buf, note_template, strlen(note_template));
@@ -179,94 +186,71 @@ static void create_note(const unsigned char *object, struct msg_arg *msg,
 
                close(fd);
                strbuf_release(&buf);
-               strbuf_reset(&(msg->buf));
-
-               if (launch_editor(path, &(msg->buf), NULL)) {
-                       die(_("Please supply the note contents using either -m" \
-                           " or -F option"));
-               }
-               stripspace(&(msg->buf), 1);
-       }
-
-       if (prev && append_only) {
-               /* Append buf to previous note contents */
-               unsigned long size;
-               enum object_type type;
-               char *prev_buf = read_sha1_file(prev, &type, &size);
+               strbuf_reset(&d->buf);
 
-               strbuf_grow(&(msg->buf), size + 1);
-               if (msg->buf.len && prev_buf && size)
-                       strbuf_insert(&(msg->buf), 0, "\n", 1);
-               if (prev_buf && size)
-                       strbuf_insert(&(msg->buf), 0, prev_buf, size);
-               free(prev_buf);
-       }
-
-       if (!msg->buf.len) {
-               fprintf(stderr, _("Removing note for object %s\n"),
-                       sha1_to_hex(object));
-               hashclr(result);
-       } else {
-               if (write_sha1_file(msg->buf.buf, msg->buf.len, blob_type, result)) {
-                       error(_("unable to write note object"));
-                       if (path)
-                               error(_("The note contents have been left in %s"),
-                                     path);
-                       exit(128);
+               if (launch_editor(d->edit_path, &d->buf, NULL)) {
+                       die(_("Please supply the note contents using either -m or -F option"));
                }
+               stripspace(&d->buf, 1);
        }
+}
 
-       if (path) {
-               unlink_or_warn(path);
-               free(path);
+static void write_note_data(struct note_data *d, unsigned char *sha1)
+{
+       if (write_sha1_file(d->buf.buf, d->buf.len, blob_type, sha1)) {
+               error(_("unable to write note object"));
+               if (d->edit_path)
+                       error(_("The note contents have been left in %s"),
+                               d->edit_path);
+               exit(128);
        }
 }
 
 static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 {
-       struct msg_arg *msg = opt->value;
+       struct note_data *d = opt->value;
 
-       strbuf_grow(&(msg->buf), strlen(arg) + 2);
-       if (msg->buf.len)
-               strbuf_addch(&(msg->buf), '\n');
-       strbuf_addstr(&(msg->buf), arg);
-       stripspace(&(msg->buf), 0);
+       strbuf_grow(&d->buf, strlen(arg) + 2);
+       if (d->buf.len)
+               strbuf_addch(&d->buf, '\n');
+       strbuf_addstr(&d->buf, arg);
+       stripspace(&d->buf, 0);
 
-       msg->given = 1;
+       d->given = 1;
        return 0;
 }
 
 static int parse_file_arg(const struct option *opt, const char *arg, int unset)
 {
-       struct msg_arg *msg = opt->value;
+       struct note_data *d = opt->value;
 
-       if (msg->buf.len)
-               strbuf_addch(&(msg->buf), '\n');
+       if (d->buf.len)
+               strbuf_addch(&d->buf, '\n');
        if (!strcmp(arg, "-")) {
-               if (strbuf_read(&(msg->buf), 0, 1024) < 0)
+               if (strbuf_read(&d->buf, 0, 1024) < 0)
                        die_errno(_("cannot read '%s'"), arg);
-       } else if (strbuf_read_file(&(msg->buf), arg, 1024) < 0)
+       } else if (strbuf_read_file(&d->buf, arg, 1024) < 0)
                die_errno(_("could not open or read '%s'"), arg);
-       stripspace(&(msg->buf), 0);
+       stripspace(&d->buf, 0);
 
-       msg->given = 1;
+       d->given = 1;
        return 0;
 }
 
 static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
 {
-       struct msg_arg *msg = opt->value;
+       struct note_data *d = opt->value;
        char *buf;
        unsigned char object[20];
        enum object_type type;
        unsigned long len;
 
-       if (msg->buf.len)
-               strbuf_addch(&(msg->buf), '\n');
+       if (d->buf.len)
+               strbuf_addch(&d->buf, '\n');
 
        if (get_sha1(arg, object))
                die(_("Failed to resolve '%s' as a valid ref."), arg);
-       if (!(buf = read_sha1_file(object, &type, &len)) || !len) {
+       if (!(buf = read_sha1_file(object, &type, &len))) {
                free(buf);
                die(_("Failed to read object '%s'."), arg);
        }
@@ -274,17 +258,17 @@ static int parse_reuse_arg(const struct option *opt, const char *arg, int unset)
                free(buf);
                die(_("Cannot read note data from non-blob object '%s'."), arg);
        }
-       strbuf_add(&(msg->buf), buf, len);
+       strbuf_add(&d->buf, buf, len);
        free(buf);
 
-       msg->given = 1;
+       d->given = 1;
        return 0;
 }
 
 static int parse_reedit_arg(const struct option *opt, const char *arg, int unset)
 {
-       struct msg_arg *msg = opt->value;
-       msg->use_editor = 1;
+       struct note_data *d = opt->value;
+       d->use_editor = 1;
        return parse_reuse_arg(opt, arg, unset);
 }
 
@@ -397,26 +381,27 @@ static int append_edit(int argc, const char **argv, const char *prefix);
 
 static int add(int argc, const char **argv, const char *prefix)
 {
-       int retval = 0, force = 0;
+       int force = 0, allow_empty = 0;
        const char *object_ref;
        struct notes_tree *t;
        unsigned char object[20], new_note[20];
-       char logmsg[100];
        const unsigned char *note;
-       struct msg_arg msg = { 0, 0, STRBUF_INIT };
+       struct note_data d = { 0, 0, NULL, STRBUF_INIT };
        struct option options[] = {
-               { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
+               { OPTION_CALLBACK, 'm', "message", &d, N_("message"),
                        N_("note contents as a string"), PARSE_OPT_NONEG,
                        parse_msg_arg},
-               { OPTION_CALLBACK, 'F', "file", &msg, N_("file"),
+               { OPTION_CALLBACK, 'F', "file", &d, N_("file"),
                        N_("note contents in a file"), PARSE_OPT_NONEG,
                        parse_file_arg},
-               { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"),
+               { OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"),
                        N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
                        parse_reedit_arg},
-               { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"),
+               { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
                        N_("reuse specified note object"), PARSE_OPT_NONEG,
                        parse_reuse_arg},
+               OPT_BOOL(0, "allow-empty", &allow_empty,
+                       N_("allow storing empty note")),
                OPT__FORCE(&force, N_("replace existing notes")),
                OPT_END()
        };
@@ -439,41 +424,44 @@ static int add(int argc, const char **argv, const char *prefix)
 
        if (note) {
                if (!force) {
-                       if (!msg.given) {
-                               /*
-                                * Redirect to "edit" subcommand.
-                                *
-                                * We only end up here if none of -m/-F/-c/-C
-                                * or -f are given. The original args are
-                                * therefore still in argv[0-1].
-                                */
-                               argv[0] = "edit";
-                               free_notes(t);
-                               return append_edit(argc, argv, prefix);
+                       free_notes(t);
+                       if (d.given) {
+                               free_note_data(&d);
+                               return error(_("Cannot add notes. "
+                                       "Found existing notes for object %s. "
+                                       "Use '-f' to overwrite existing notes"),
+                                       sha1_to_hex(object));
                        }
-                       retval = error(_("Cannot add notes. Found existing notes "
-                                      "for object %s. Use '-f' to overwrite "
-                                      "existing notes"), sha1_to_hex(object));
-                       goto out;
+                       /*
+                        * Redirect to "edit" subcommand.
+                        *
+                        * We only end up here if none of -m/-F/-c/-C or -f are
+                        * given. The original args are therefore still in
+                        * argv[0-1].
+                        */
+                       argv[0] = "edit";
+                       return append_edit(argc, argv, prefix);
                }
                fprintf(stderr, _("Overwriting existing notes for object %s\n"),
                        sha1_to_hex(object));
        }
 
-       create_note(object, &msg, 0, note, new_note);
-
-       if (is_null_sha1(new_note))
+       prepare_note_data(object, &d, note);
+       if (d.buf.len || allow_empty) {
+               write_note_data(&d, new_note);
+               if (add_note(t, object, new_note, combine_notes_overwrite))
+                       die("BUG: combine_notes_overwrite failed");
+               commit_notes(t, "Notes added by 'git notes add'");
+       } else {
+               fprintf(stderr, _("Removing note for object %s\n"),
+                       sha1_to_hex(object));
                remove_note(t, object);
-       else if (add_note(t, object, new_note, combine_notes_overwrite))
-               die("BUG: combine_notes_overwrite failed");
+               commit_notes(t, "Notes removed by 'git notes add'");
+       }
 
-       snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
-                is_null_sha1(new_note) ? "removed" : "added", "add");
-       commit_notes(t, logmsg);
-out:
+       free_note_data(&d);
        free_notes(t);
-       strbuf_release(&(msg.buf));
-       return retval;
+       return 0;
 }
 
 static int copy(int argc, const char **argv, const char *prefix)
@@ -554,26 +542,29 @@ static int copy(int argc, const char **argv, const char *prefix)
 
 static int append_edit(int argc, const char **argv, const char *prefix)
 {
+       int allow_empty = 0;
        const char *object_ref;
        struct notes_tree *t;
        unsigned char object[20], new_note[20];
        const unsigned char *note;
        char logmsg[100];
        const char * const *usage;
-       struct msg_arg msg = { 0, 0, STRBUF_INIT };
+       struct note_data d = { 0, 0, NULL, STRBUF_INIT };
        struct option options[] = {
-               { OPTION_CALLBACK, 'm', "message", &msg, N_("message"),
+               { OPTION_CALLBACK, 'm', "message", &d, N_("message"),
                        N_("note contents as a string"), PARSE_OPT_NONEG,
                        parse_msg_arg},
-               { OPTION_CALLBACK, 'F', "file", &msg, N_("file"),
+               { OPTION_CALLBACK, 'F', "file", &d, N_("file"),
                        N_("note contents in a file"), PARSE_OPT_NONEG,
                        parse_file_arg},
-               { OPTION_CALLBACK, 'c', "reedit-message", &msg, N_("object"),
+               { OPTION_CALLBACK, 'c', "reedit-message", &d, N_("object"),
                        N_("reuse and edit specified note object"), PARSE_OPT_NONEG,
                        parse_reedit_arg},
-               { OPTION_CALLBACK, 'C', "reuse-message", &msg, N_("object"),
+               { OPTION_CALLBACK, 'C', "reuse-message", &d, N_("object"),
                        N_("reuse specified note object"), PARSE_OPT_NONEG,
                        parse_reuse_arg},
+               OPT_BOOL(0, "allow-empty", &allow_empty,
+                       N_("allow storing empty note")),
                OPT_END()
        };
        int edit = !strcmp(argv[0], "edit");
@@ -587,7 +578,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
                usage_with_options(usage, options);
        }
 
-       if (msg.given && edit)
+       if (d.given && edit)
                fprintf(stderr, _("The -m/-F/-c/-C options have been deprecated "
                        "for the 'edit' subcommand.\n"
                        "Please use 'git notes add -f -m/-F/-c/-C' instead.\n"));
@@ -600,18 +591,39 @@ static int append_edit(int argc, const char **argv, const char *prefix)
        t = init_notes_check(argv[0]);
        note = get_note(t, object);
 
-       create_note(object, &msg, !edit, note, new_note);
+       prepare_note_data(object, &d, edit ? note : NULL);
 
-       if (is_null_sha1(new_note))
-               remove_note(t, object);
-       else if (add_note(t, object, new_note, combine_notes_overwrite))
-               die("BUG: combine_notes_overwrite failed");
+       if (note && !edit) {
+               /* Append buf to previous note contents */
+               unsigned long size;
+               enum object_type type;
+               char *prev_buf = read_sha1_file(note, &type, &size);
+
+               strbuf_grow(&d.buf, size + 1);
+               if (d.buf.len && prev_buf && size)
+                       strbuf_insert(&d.buf, 0, "\n", 1);
+               if (prev_buf && size)
+                       strbuf_insert(&d.buf, 0, prev_buf, size);
+               free(prev_buf);
+       }
 
-       snprintf(logmsg, sizeof(logmsg), "Notes %s by 'git notes %s'",
-                is_null_sha1(new_note) ? "removed" : "added", argv[0]);
+       if (d.buf.len || allow_empty) {
+               write_note_data(&d, new_note);
+               if (add_note(t, object, new_note, combine_notes_overwrite))
+                       die("BUG: combine_notes_overwrite failed");
+               snprintf(logmsg, sizeof(logmsg), "Notes added by 'git notes %s'",
+                       argv[0]);
+       } else {
+               fprintf(stderr, _("Removing note for object %s\n"),
+                       sha1_to_hex(object));
+               remove_note(t, object);
+               snprintf(logmsg, sizeof(logmsg), "Notes removed by 'git notes %s'",
+                       argv[0]);
+       }
        commit_notes(t, logmsg);
+
+       free_note_data(&d);
        free_notes(t);
-       strbuf_release(&(msg.buf));
        return 0;
 }
 
index a076b1964d6ac0ca96496eda4e88fd23409299a9..12f5e69393bc2981dd5ff1433e34bc4932c0ee22 100644 (file)
@@ -161,7 +161,7 @@ static const char message_detached_head_die[] =
           "    git push %s HEAD:<name-of-remote-branch>\n");
 
 static void setup_push_upstream(struct remote *remote, struct branch *branch,
-                               int triangular)
+                               int triangular, int simple)
 {
        struct strbuf refspec = STRBUF_INIT;
 
@@ -184,7 +184,7 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,
                      "to update which remote branch."),
                    remote->name, branch->name);
 
-       if (push_default == PUSH_DEFAULT_SIMPLE) {
+       if (simple) {
                /* Additional safety */
                if (strcmp(branch->refname, branch->merge[0]->src))
                        die_push_simple(branch, remote);
@@ -257,11 +257,11 @@ static void setup_default_push_refspecs(struct remote *remote)
                if (triangular)
                        setup_push_current(remote, branch);
                else
-                       setup_push_upstream(remote, branch, triangular);
+                       setup_push_upstream(remote, branch, triangular, 1);
                break;
 
        case PUSH_DEFAULT_UPSTREAM:
-               setup_push_upstream(remote, branch, triangular);
+               setup_push_upstream(remote, branch, triangular, 0);
                break;
 
        case PUSH_DEFAULT_CURRENT:
@@ -503,7 +503,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                  0, CAS_OPT_NAME, &cas, N_("refname>:<expect"),
                  N_("require old value of ref to be at this value"),
                  PARSE_OPT_OPTARG, parseopt_push_cas_option },
-               { OPTION_CALLBACK, 0, "recurse-submodules", &flags, N_("check"),
+               { OPTION_CALLBACK, 0, "recurse-submodules", &flags, "check|on-demand",
                        N_("control recursive pushing of submodules"),
                        PARSE_OPT_OPTARG, option_parse_recurse_submodules },
                OPT_BOOL( 0 , "thin", &thin, N_("use thin pack")),
index 32fc540ef3ddfc4d1bb33ae75a0e07b555bdc441..8266c1fccf0c0b5908c14a726d443dec36a22d58 100644 (file)
@@ -26,7 +26,8 @@ enum deny_action {
        DENY_UNCONFIGURED,
        DENY_IGNORE,
        DENY_WARN,
-       DENY_REFUSE
+       DENY_REFUSE,
+       DENY_UPDATE_INSTEAD
 };
 
 static int deny_deletes;
@@ -76,6 +77,8 @@ static enum deny_action parse_deny_action(const char *var, const char *value)
                        return DENY_WARN;
                if (!strcasecmp(value, "refuse"))
                        return DENY_REFUSE;
+               if (!strcasecmp(value, "updateinstead"))
+                       return DENY_UPDATE_INSTEAD;
        }
        if (git_config_bool(var, value))
                return DENY_REFUSE;
@@ -431,7 +434,7 @@ static const char *check_nonce(const char *buf, size_t len)
        nonce_stamp_slop = (long)ostamp - (long)stamp;
 
        if (nonce_stamp_slop_limit &&
-           abs(nonce_stamp_slop) <= nonce_stamp_slop_limit) {
+           labs(nonce_stamp_slop) <= nonce_stamp_slop_limit) {
                /*
                 * Pretend as if the received nonce (which passes the
                 * HMAC check, so it is not a forged by third-party)
@@ -730,11 +733,89 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
        return 0;
 }
 
+static const char *update_worktree(unsigned char *sha1)
+{
+       const char *update_refresh[] = {
+               "update-index", "-q", "--ignore-submodules", "--refresh", NULL
+       };
+       const char *diff_files[] = {
+               "diff-files", "--quiet", "--ignore-submodules", "--", NULL
+       };
+       const char *diff_index[] = {
+               "diff-index", "--quiet", "--cached", "--ignore-submodules",
+               "HEAD", "--", NULL
+       };
+       const char *read_tree[] = {
+               "read-tree", "-u", "-m", NULL, NULL
+       };
+       const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : "..";
+       struct argv_array env = ARGV_ARRAY_INIT;
+       struct child_process child = CHILD_PROCESS_INIT;
+
+       if (is_bare_repository())
+               return "denyCurrentBranch = updateInstead needs a worktree";
+
+       argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir()));
+
+       child.argv = update_refresh;
+       child.env = env.argv;
+       child.dir = work_tree;
+       child.no_stdin = 1;
+       child.stdout_to_stderr = 1;
+       child.git_cmd = 1;
+       if (run_command(&child)) {
+               argv_array_clear(&env);
+               return "Up-to-date check failed";
+       }
+
+       /* run_command() does not clean up completely; reinitialize */
+       child_process_init(&child);
+       child.argv = diff_files;
+       child.env = env.argv;
+       child.dir = work_tree;
+       child.no_stdin = 1;
+       child.stdout_to_stderr = 1;
+       child.git_cmd = 1;
+       if (run_command(&child)) {
+               argv_array_clear(&env);
+               return "Working directory has unstaged changes";
+       }
+
+       child_process_init(&child);
+       child.argv = diff_index;
+       child.env = env.argv;
+       child.no_stdin = 1;
+       child.no_stdout = 1;
+       child.stdout_to_stderr = 0;
+       child.git_cmd = 1;
+       if (run_command(&child)) {
+               argv_array_clear(&env);
+               return "Working directory has staged changes";
+       }
+
+       read_tree[3] = sha1_to_hex(sha1);
+       child_process_init(&child);
+       child.argv = read_tree;
+       child.env = env.argv;
+       child.dir = work_tree;
+       child.no_stdin = 1;
+       child.no_stdout = 1;
+       child.stdout_to_stderr = 0;
+       child.git_cmd = 1;
+       if (run_command(&child)) {
+               argv_array_clear(&env);
+               return "Could not update working tree to new HEAD";
+       }
+
+       argv_array_clear(&env);
+       return NULL;
+}
+
 static const char *update(struct command *cmd, struct shallow_info *si)
 {
        const char *name = cmd->ref_name;
        struct strbuf namespaced_name_buf = STRBUF_INIT;
-       const char *namespaced_name;
+       const char *namespaced_name, *ret;
        unsigned char *old_sha1 = cmd->old_sha1;
        unsigned char *new_sha1 = cmd->new_sha1;
 
@@ -760,6 +841,11 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                        if (deny_current_branch == DENY_UNCONFIGURED)
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
+               case DENY_UPDATE_INSTEAD:
+                       ret = update_worktree(new_sha1);
+                       if (ret)
+                               return ret;
+                       break;
                }
        }
 
@@ -784,10 +870,13 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                                break;
                        case DENY_REFUSE:
                        case DENY_UNCONFIGURED:
+                       case DENY_UPDATE_INSTEAD:
                                if (deny_delete_current == DENY_UNCONFIGURED)
                                        refuse_unconfigured_deny_delete_current();
                                rp_error("refusing to delete the current branch: %s", name);
                                return "deletion of the current branch prohibited";
+                       default:
+                               return "Invalid denyDeleteCurrent setting";
                        }
                }
        }
@@ -964,7 +1053,7 @@ static void check_aliased_updates(struct command *commands)
                        string_list_append(&ref_list, cmd->ref_name);
                item->util = (void *)cmd;
        }
-       sort_string_list(&ref_list);
+       string_list_sort(&ref_list);
 
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (!cmd->error_string)
index 7f28f92a378a4e89d8c938beac23cbe053f1ce33..46ecfd9f7b0dd0b28169341d14132df376e509bc 100644 (file)
@@ -352,9 +352,9 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
        free_refs(stale_refs);
        free_refs(fetch_map);
 
-       sort_string_list(&states->new);
-       sort_string_list(&states->tracked);
-       sort_string_list(&states->stale);
+       string_list_sort(&states->new);
+       string_list_sort(&states->tracked);
+       string_list_sort(&states->stale);
 
        return 0;
 }
@@ -750,16 +750,11 @@ static int mv(int argc, const char **argv)
 static int remove_branches(struct string_list *branches)
 {
        struct strbuf err = STRBUF_INIT;
-       const char **branch_names;
        int i, result = 0;
 
-       branch_names = xmalloc(branches->nr * sizeof(*branch_names));
-       for (i = 0; i < branches->nr; i++)
-               branch_names[i] = branches->items[i].string;
-       if (repack_without_refs(branch_names, branches->nr, &err))
+       if (repack_without_refs(branches, &err))
                result |= error("%s", err.buf);
        strbuf_release(&err);
-       free(branch_names);
 
        for (i = 0; i < branches->nr; i++) {
                struct string_list_item *item = branches->items + i;
@@ -914,7 +909,7 @@ static int get_remote_ref_states(const char *name,
                        get_push_ref_states(remote_refs, states);
        } else {
                for_each_ref(append_ref_to_tracked_list, states);
-               sort_string_list(&states->tracked);
+               string_list_sort(&states->tracked);
                get_push_ref_states_noquery(states);
        }
 
@@ -1133,7 +1128,7 @@ static int show_all(void)
        if (!result) {
                int i;
 
-               sort_string_list(&list);
+               string_list_sort(&list);
                for (i = 0; i < list.nr; i++) {
                        struct string_list_item *item = list.items + i;
                        if (verbose)
@@ -1314,10 +1309,10 @@ static int set_head(int argc, const char **argv)
 
 static int prune_remote(const char *remote, int dry_run)
 {
-       int result = 0, i;
+       int result = 0;
        struct ref_states states;
-       struct string_list delete_refs_list = STRING_LIST_INIT_NODUP;
-       const char **delete_refs;
+       struct string_list refs_to_prune = STRING_LIST_INIT_NODUP;
+       struct string_list_item *item;
        const char *dangling_msg = dry_run
                ? _(" %s will become dangling!")
                : _(" %s has become dangling!");
@@ -1325,30 +1320,30 @@ static int prune_remote(const char *remote, int dry_run)
        memset(&states, 0, sizeof(states));
        get_remote_ref_states(remote, &states, GET_REF_STATES);
 
-       if (states.stale.nr) {
-               printf_ln(_("Pruning %s"), remote);
-               printf_ln(_("URL: %s"),
-                      states.remote->url_nr
-                      ? states.remote->url[0]
-                      : _("(no URL)"));
-
-               delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
-               for (i = 0; i < states.stale.nr; i++)
-                       delete_refs[i] = states.stale.items[i].util;
-               if (!dry_run) {
-                       struct strbuf err = STRBUF_INIT;
-                       if (repack_without_refs(delete_refs, states.stale.nr,
-                                               &err))
-                               result |= error("%s", err.buf);
-                       strbuf_release(&err);
-               }
-               free(delete_refs);
+       if (!states.stale.nr) {
+               free_remote_ref_states(&states);
+               return 0;
        }
 
-       for (i = 0; i < states.stale.nr; i++) {
-               const char *refname = states.stale.items[i].util;
+       printf_ln(_("Pruning %s"), remote);
+       printf_ln(_("URL: %s"),
+                 states.remote->url_nr
+                 ? states.remote->url[0]
+                 : _("(no URL)"));
+
+       for_each_string_list_item(item, &states.stale)
+               string_list_append(&refs_to_prune, item->util);
+       string_list_sort(&refs_to_prune);
+
+       if (!dry_run) {
+               struct strbuf err = STRBUF_INIT;
+               if (repack_without_refs(&refs_to_prune, &err))
+                       result |= error("%s", err.buf);
+               strbuf_release(&err);
+       }
 
-               string_list_insert(&delete_refs_list, refname);
+       for_each_string_list_item(item, &states.stale) {
+               const char *refname = item->util;
 
                if (!dry_run)
                        result |= delete_ref(refname, NULL, 0);
@@ -1361,9 +1356,9 @@ static int prune_remote(const char *remote, int dry_run)
                               abbrev_ref(refname, "refs/remotes/"));
        }
 
-       warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list);
-       string_list_clear(&delete_refs_list, 0);
+       warn_dangling_symrefs(stdout, dangling_msg, &refs_to_prune);
 
+       string_list_clear(&refs_to_prune, 0);
        free_remote_ref_states(&states);
        return result;
 }
index 28456206c5d135adba191c865e49aa75dc201691..3f852f35d1e786b2584d435d640ae830a9f34639 100644 (file)
@@ -135,7 +135,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        };
        struct child_process cmd = CHILD_PROCESS_INIT;
        struct string_list_item *item;
-       struct argv_array cmd_args = ARGV_ARRAY_INIT;
        struct string_list names = STRING_LIST_INIT_DUP;
        struct string_list rollback = STRING_LIST_INIT_NODUP;
        struct string_list existing_packs = STRING_LIST_INIT_DUP;
@@ -202,56 +201,55 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        sigchain_push_common(remove_pack_on_signal);
 
-       argv_array_push(&cmd_args, "pack-objects");
-       argv_array_push(&cmd_args, "--keep-true-parents");
+       argv_array_push(&cmd.args, "pack-objects");
+       argv_array_push(&cmd.args, "--keep-true-parents");
        if (!pack_kept_objects)
-               argv_array_push(&cmd_args, "--honor-pack-keep");
-       argv_array_push(&cmd_args, "--non-empty");
-       argv_array_push(&cmd_args, "--all");
-       argv_array_push(&cmd_args, "--reflog");
-       argv_array_push(&cmd_args, "--indexed-objects");
+               argv_array_push(&cmd.args, "--honor-pack-keep");
+       argv_array_push(&cmd.args, "--non-empty");
+       argv_array_push(&cmd.args, "--all");
+       argv_array_push(&cmd.args, "--reflog");
+       argv_array_push(&cmd.args, "--indexed-objects");
        if (window)
-               argv_array_pushf(&cmd_args, "--window=%s", window);
+               argv_array_pushf(&cmd.args, "--window=%s", window);
        if (window_memory)
-               argv_array_pushf(&cmd_args, "--window-memory=%s", window_memory);
+               argv_array_pushf(&cmd.args, "--window-memory=%s", window_memory);
        if (depth)
-               argv_array_pushf(&cmd_args, "--depth=%s", depth);
+               argv_array_pushf(&cmd.args, "--depth=%s", depth);
        if (max_pack_size)
-               argv_array_pushf(&cmd_args, "--max-pack-size=%s", max_pack_size);
+               argv_array_pushf(&cmd.args, "--max-pack-size=%s", max_pack_size);
        if (no_reuse_delta)
-               argv_array_pushf(&cmd_args, "--no-reuse-delta");
+               argv_array_pushf(&cmd.args, "--no-reuse-delta");
        if (no_reuse_object)
-               argv_array_pushf(&cmd_args, "--no-reuse-object");
+               argv_array_pushf(&cmd.args, "--no-reuse-object");
        if (write_bitmaps)
-               argv_array_push(&cmd_args, "--write-bitmap-index");
+               argv_array_push(&cmd.args, "--write-bitmap-index");
 
        if (pack_everything & ALL_INTO_ONE) {
                get_non_kept_pack_filenames(&existing_packs);
 
                if (existing_packs.nr && delete_redundant) {
                        if (unpack_unreachable)
-                               argv_array_pushf(&cmd_args,
+                               argv_array_pushf(&cmd.args,
                                                "--unpack-unreachable=%s",
                                                unpack_unreachable);
                        else if (pack_everything & LOOSEN_UNREACHABLE)
-                               argv_array_push(&cmd_args,
+                               argv_array_push(&cmd.args,
                                                "--unpack-unreachable");
                }
        } else {
-               argv_array_push(&cmd_args, "--unpacked");
-               argv_array_push(&cmd_args, "--incremental");
+               argv_array_push(&cmd.args, "--unpacked");
+               argv_array_push(&cmd.args, "--incremental");
        }
 
        if (local)
-               argv_array_push(&cmd_args,  "--local");
+               argv_array_push(&cmd.args,  "--local");
        if (quiet)
-               argv_array_push(&cmd_args,  "--quiet");
+               argv_array_push(&cmd.args,  "--quiet");
        if (delta_base_offset)
-               argv_array_push(&cmd_args,  "--delta-base-offset");
+               argv_array_push(&cmd.args,  "--delta-base-offset");
 
-       argv_array_push(&cmd_args, packtmp);
+       argv_array_push(&cmd.args, packtmp);
 
-       cmd.argv = cmd_args.argv;
        cmd.git_cmd = 1;
        cmd.out = -1;
        cmd.no_stdin = 1;
@@ -270,7 +268,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        ret = finish_command(&cmd);
        if (ret)
                return ret;
-       argv_array_clear(&cmd_args);
 
        if (!names.nr && !quiet)
                printf("Nothing new to pack.\n");
@@ -379,7 +376,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
 
        if (delete_redundant) {
                int opts = 0;
-               sort_string_list(&names);
+               string_list_sort(&names);
                for_each_string_list_item(item, &existing_packs) {
                        char *sha1;
                        size_t len = strlen(item->string);
diff --git a/cache.h b/cache.h
index 99ed096aed03b865dd6e263474e6e6a265681b91..f704af5df0984e092fe318e535596921c4ea7122 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -65,13 +65,6 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long);
  *
  * The value 0160000 is not normally a valid mode, and
  * also just happens to be S_IFDIR + S_IFLNK
- *
- * NOTE! We *really* shouldn't depend on the S_IFxxx macros
- * always having the same values everywhere. We should use
- * our internal git values for these things, and then we can
- * translate that to the OS-specific value. It just so
- * happens that everybody shares the same bit representation
- * in the UNIX world (and apparently wider too..)
  */
 #define S_IFGITLINK    0160000
 #define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
@@ -617,6 +610,8 @@ extern int fsync_object_files;
 extern int core_preload_index;
 extern int core_apply_sparse_checkout;
 extern int precomposed_unicode;
+extern int protect_hfs;
+extern int protect_ntfs;
 
 /*
  * The character that begins a commented line in user-editable file
@@ -831,6 +826,7 @@ int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, struct string_list *prefixes);
 char *strip_path_suffix(const char *path, const char *suffix);
 int daemon_avoid_alias(const char *path);
+extern int is_ntfs_dotgit(const char *name);
 
 /* object replacement */
 #define LOOKUP_REPLACE_OBJECT 1
index 19cf8f9c67bd0f4d71172f33424f7d2371a3af68..a54cb9a454fdce3ccf9dff58338e43d73945d728 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1640,3 +1640,49 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
        }
        return NULL;
 }
+
+/*
+ * Inspect sb 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
+ * "git commit -s --amend" on an existing commit that forgot to remove
+ * it.
+ *
+ * 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 boc = 0;
+       int bol = 0;
+       int in_old_conflicts_block = 0;
+
+       while (bol < sb->len) {
+               char *next_line;
+
+               if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol)))
+                       next_line = sb->buf + sb->len;
+               else
+                       next_line++;
+
+               if (sb->buf[bol] == comment_line_char || sb->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")) {
+                       in_old_conflicts_block = 1;
+                       if (!boc)
+                               boc = bol;
+               } else if (in_old_conflicts_block && sb->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;
+       }
+       return boc ? sb->len - boc : 0;
+}
index bc68ccbe691a28ed9b22fa935c78d2e9640347ea..cd35ac150acd076047eed2e3200d1be12e4e7d5e 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -337,6 +337,9 @@ extern void free_commit_extra_headers(struct commit_extra_header *extra);
 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);
+
 typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
                                 void *cb_data);
 
index c5c37e53ce0fc534b1739a75c4abde0c67027d14..70f3191a4f19f10a156d1f2c054943d5147ab049 100644 (file)
@@ -312,7 +312,7 @@ int mingw_open (const char *filename, int oflags, ...)
                return -1;
        fd = _wopen(wfilename, oflags, mode);
 
-       if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) {
+       if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
                DWORD attrs = GetFileAttributesW(wfilename);
                if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
                        errno = EISDIR;
diff --git a/compat/stat.c b/compat/stat.c
new file mode 100644 (file)
index 0000000..a2d3931
--- /dev/null
@@ -0,0 +1,48 @@
+#define _POSIX_C_SOURCE 200112L
+#include <sys/stat.h>  /* *stat, S_IS* */
+#include <sys/types.h> /* mode_t       */
+
+static inline mode_t mode_native_to_git(mode_t native_mode)
+{
+       mode_t perm_bits = native_mode & 07777;
+       if (S_ISREG(native_mode))
+               return 0100000 | perm_bits;
+       if (S_ISDIR(native_mode))
+               return 0040000 | perm_bits;
+       if (S_ISLNK(native_mode))
+               return 0120000 | perm_bits;
+       if (S_ISBLK(native_mode))
+               return 0060000 | perm_bits;
+       if (S_ISCHR(native_mode))
+               return 0020000 | perm_bits;
+       if (S_ISFIFO(native_mode))
+               return 0010000 | perm_bits;
+       if (S_ISSOCK(native_mode))
+               return 0140000 | perm_bits;
+       /* Non-standard type bits were given. */
+       return perm_bits;
+}
+
+int git_stat(const char *path, struct stat *buf)
+{
+       int rc = stat(path, buf);
+       if (rc == 0)
+               buf->st_mode = mode_native_to_git(buf->st_mode);
+       return rc;
+}
+
+int git_fstat(int fd, struct stat *buf)
+{
+       int rc = fstat(fd, buf);
+       if (rc == 0)
+               buf->st_mode = mode_native_to_git(buf->st_mode);
+       return rc;
+}
+
+int git_lstat(const char *path, struct stat *buf)
+{
+       int rc = lstat(path, buf);
+       if (rc == 0)
+               buf->st_mode = mode_native_to_git(buf->st_mode);
+       return rc;
+}
index 15a298357796ffa80f7fb2258e55cf95be0b8895..752e2e227f56edfb7b2ad168798c110c14a2c5e8 100644 (file)
--- a/config.c
+++ b/config.c
@@ -506,9 +506,9 @@ static int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
                        errno = EINVAL;
                        return 0;
                }
-               uval = abs(val);
+               uval = labs(val);
                uval *= factor;
-               if (uval > max || abs(val) > uval) {
+               if (uval > max || labs(val) > uval) {
                        errno = ERANGE;
                        return 0;
                }
@@ -896,6 +896,16 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.protecthfs")) {
+               protect_hfs = git_config_bool(var, value);
+               return 0;
+       }
+
+       if (!strcmp(var, "core.protectntfs")) {
+               protect_ntfs = git_config_bool(var, value);
+               return 0;
+       }
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
index a2f380fd8dbb852ceeb4933b1b942e0e4803f44a..f3c93f27c945193754090e97c2070c9e66759f02 100644 (file)
@@ -105,6 +105,7 @@ ifeq ($(uname_S),Darwin)
        HAVE_DEV_TTY = YesPlease
        COMPAT_OBJS += compat/precompose_utf8.o
        BASIC_CFLAGS += -DPRECOMPOSE_UNICODE
+       BASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1
 endif
 ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
@@ -373,6 +374,7 @@ ifeq ($(uname_S),Windows)
        EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
        PTHREAD_LIBS =
        lib =
+       BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
 ifndef DEBUG
        BASIC_CFLAGS += -GL -Os -MD
        BASIC_LDFLAGS += -LTCG
@@ -514,6 +516,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        COMPAT_OBJS += compat/mingw.o compat/winansi.o \
                compat/win32/pthread.o compat/win32/syslog.o \
                compat/win32/dirent.o
+       BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
        BASIC_LDFLAGS += -Wl,--large-address-aware
        EXTLIBS += -lws2_32
        GITLIBS += git.res
index 6af964797f7b0ee64deb34bdff40b2a17db067b6..5c1312f24a247fca9bdd7dc9d66aa05f8a7c60d5 100644 (file)
@@ -873,6 +873,29 @@ else
        SNPRINTF_RETURNS_BOGUS=
 fi
 GIT_CONF_SUBST([SNPRINTF_RETURNS_BOGUS])
+#
+# Define NEEDS_MODE_TRANSLATION if your OS strays from the typical file type
+# bits in mode values.
+AC_CACHE_CHECK([whether the platform uses typical file type bits],
+ [ac_cv_sane_mode_bits], [
+AC_EGREP_CPP(yippeeyeswehaveit,
+       AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT],
+[#if S_IFMT == 0170000 && \
+       S_IFREG == 0100000 && S_IFDIR == 0040000 && S_IFLNK == 0120000 && \
+       S_IFBLK == 0060000 && S_IFCHR == 0020000 && \
+       S_IFIFO == 0010000 && S_IFSOCK == 0140000
+yippeeyeswehaveit
+#endif
+]),
+       [ac_cv_sane_mode_bits=yes],
+       [ac_cv_sane_mode_bits=no])
+])
+if test $ac_cv_sane_mode_bits = yes; then
+       NEEDS_MODE_TRANSLATION=
+else
+       NEEDS_MODE_TRANSLATION=UnfortunatelyYes
+fi
+GIT_CONF_SUBST([NEEDS_MODE_TRANSLATION])
 
 
 ## Checks for library functions.
index d47d0ec6040fdb8b07a689e299dac121bb468717..062e133aa39b9e522bf3a03c5e57ac67c03ad88e 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -93,7 +93,7 @@ static void annotate_refs_with_symref_info(struct ref *ref)
                parse_one_symref_info(&symref, val, len);
                feature_list = val + 1;
        }
-       sort_string_list(&symref);
+       string_list_sort(&symref);
 
        for (; ref; ref = ref->next) {
                struct string_list_item *item;
@@ -700,14 +700,23 @@ struct child_process *git_connect(int fd[2], const char *url,
 
                conn->in = conn->out = -1;
                if (protocol == PROTO_SSH) {
-                       const char *ssh = getenv("GIT_SSH");
-                       int putty = ssh && strcasestr(ssh, "plink");
+                       const char *ssh;
+                       int putty;
                        char *ssh_host = hostandport;
                        const char *port = NULL;
                        get_host_and_port(&ssh_host, &port);
                        port = get_port_numeric(port);
 
-                       if (!ssh) ssh = "ssh";
+                       ssh = getenv("GIT_SSH_COMMAND");
+                       if (ssh) {
+                               conn->use_shell = 1;
+                               putty = 0;
+                       } else {
+                               ssh = getenv("GIT_SSH");
+                               if (!ssh)
+                                       ssh = "ssh";
+                               putty = !!strcasestr(ssh, "plink");
+                       }
 
                        argv_array_push(&conn->args, ssh);
                        if (putty && !strcasestr(ssh, "tortoiseplink"))
index 2fece98c603e24ed5ce08156e0e44199a149ba83..23988ec124a85d766006c6cd569c8341d3612c8b 100644 (file)
@@ -1875,6 +1875,10 @@ _git_config ()
                __gitcomp "$__git_send_email_suppresscc_options"
                return
                ;;
+       sendemail.transferencoding)
+               __gitcomp "7bit 8bit quoted-printable base64"
+               return
+               ;;
        --get|--get-all|--unset|--unset-all)
                __gitcomp_nl "$(__git_config_get_set_variables)"
                return
@@ -2548,6 +2552,16 @@ _git_tag ()
                __gitcomp_nl "$(__git_refs)"
                ;;
        esac
+
+       case "$cur" in
+       --*)
+               __gitcomp "
+                       --list --delete --verify --annotate --message --file
+                       --sign --cleanup --local-user --force --column --sort
+                       --contains --points-at
+                       "
+               ;;
+       esac
 }
 
 _git_whatchanged ()
index 75e8b258177f7f04dadcac125f2bf7ebea4d0f81..888c34a5215258ad7d5715771f92ab58f3efd363 100755 (executable)
@@ -10,6 +10,10 @@ die () {
        exit 128
 }
 
+failed () {
+       die "unable to create new workdir '$new_workdir'!"
+}
+
 if test $# -lt 2 || test $# -gt 3
 then
        usage "$0 <repository> <new_workdir> [<branch>]"
@@ -35,7 +39,7 @@ esac
 
 # don't link to a configured bare repository
 isbare=$(git --git-dir="$git_dir" config --bool --get core.bare)
-if test ztrue = z$isbare
+if test ztrue = "z$isbare"
 then
        die "\"$git_dir\" has core.bare set to true," \
                " remove from \"$git_dir/config\" to use $0"
@@ -48,35 +52,54 @@ then
                "a complete repository."
 fi
 
-# don't recreate a workdir over an existing repository
-if test -e "$new_workdir"
+# make sure the links in the workdir have full paths to the original repo
+git_dir=$(cd "$git_dir" && pwd) || exit 1
+
+# don't recreate a workdir over an existing directory, unless it's empty
+if test -d "$new_workdir"
 then
-       die "destination directory '$new_workdir' already exists."
+       if test $(ls -a1 "$new_workdir/." | wc -l) -ne 2
+       then
+               die "destination directory '$new_workdir' is not empty."
+       fi
+       cleandir="$new_workdir/.git"
+else
+       cleandir="$new_workdir"
 fi
 
-# make sure the links use full paths
-git_dir=$(cd "$git_dir"; pwd)
+mkdir -p "$new_workdir/.git" || failed
+cleandir=$(cd "$cleandir" && pwd) || failed
 
-# create the workdir
-mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
+cleanup () {
+       rm -rf "$cleandir"
+}
+siglist="0 1 2 15"
+trap cleanup $siglist
 
 # create the links to the original repo.  explicitly exclude index, HEAD and
 # logs/HEAD from the list since they are purely related to the current working
 # directory, and should not be shared.
 for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
 do
+       # create a containing directory if needed
        case $x in
        */*)
-               mkdir -p "$(dirname "$new_workdir/.git/$x")"
+               mkdir -p "$new_workdir/.git/${x%/*}"
                ;;
        esac
-       ln -s "$git_dir/$x" "$new_workdir/.git/$x"
+
+       ln -s "$git_dir/$x" "$new_workdir/.git/$x" || failed
 done
 
-# now setup the workdir
-cd "$new_workdir"
+# commands below this are run in the context of the new workdir
+cd "$new_workdir" || failed
+
 # copy the HEAD from the original repository as a default branch
-cp "$git_dir/HEAD" .git/HEAD
-# checkout the branch (either the same as HEAD from the original repository, or
-# the one that was asked for)
+cp "$git_dir/HEAD" .git/HEAD || failed
+
+# the workdir is set up.  if the checkout fails, the user can fix it.
+trap - $siglist
+
+# checkout the branch (either the same as HEAD from the original repository,
+# or the one that was asked for)
 git checkout -f $branch
index 1886ea50b3b3c2a213e34876de691fa3d0a83b6c..b146ad8481b133892a542691c7e36f5e7974cfdc 100644 (file)
@@ -173,6 +173,8 @@ int credential_read(struct credential *c, FILE *fp)
                        c->path = xstrdup(value);
                } else if (!strcmp(key, "url")) {
                        credential_from_url(c, value);
+               } else if (!strcmp(key, "quit")) {
+                       c->quit = !!git_config_bool("quit", value);
                }
                /*
                 * Ignore other lines; we don't know what they mean, but
@@ -274,6 +276,9 @@ void credential_fill(struct credential *c)
                credential_do(c, c->helpers.items[i].string, "get");
                if (c->username && c->password)
                        return;
+               if (c->quit)
+                       die("credential helper '%s' told us to quit",
+                           c->helpers.items[i].string);
        }
 
        credential_getpass(c);
index 0c3e85e8e4232ad9298372efebf7696a77dc94b1..6b0cd16be2b96b23ae5ce14a07a8ad130af8d5bb 100644 (file)
@@ -7,6 +7,7 @@ struct credential {
        struct string_list helpers;
        unsigned approved:1,
                 configured:1,
+                quit:1,
                 use_http_path:1;
 
        char *username;
index bb543d52f108cf1e976ee644e337f5ccec983790..7530927d774562f82636aa773481ae93600c1756 100644 (file)
@@ -39,4 +39,15 @@ extern void sha1flush(struct sha1file *f);
 extern void crc32_begin(struct sha1file *);
 extern uint32_t crc32_end(struct sha1file *);
 
+static inline void sha1write_u8(struct sha1file *f, uint8_t data)
+{
+       sha1write(f, &data, sizeof(data));
+}
+
+static inline void sha1write_be32(struct sha1file *f, uint32_t data)
+{
+       data = htonl(data);
+       sha1write(f, &data, sizeof(data));
+}
+
 #endif
index 565f65293bb25ebdcb6ada262bcca9164c3ea113..1ade5c9684a9f901db7d0b3fff04d0cad443caf7 100644 (file)
@@ -64,6 +64,16 @@ int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
 struct startup_info *startup_info;
 unsigned long pack_size_limit_cfg;
 
+#ifndef PROTECT_HFS_DEFAULT
+#define PROTECT_HFS_DEFAULT 0
+#endif
+int protect_hfs = PROTECT_HFS_DEFAULT;
+
+#ifndef PROTECT_NTFS_DEFAULT
+#define PROTECT_NTFS_DEFAULT 0
+#endif
+int protect_ntfs = PROTECT_NTFS_DEFAULT;
+
 /*
  * The character that begins a commented line in user-editable file
  * that is subject to stripspace.
index 698e7526c40749fd333367018c192537cf6b4562..8ab37b5f74f360abf8ff4d689b4ff4f3d29cf785 100644 (file)
@@ -6,7 +6,7 @@
 static const char *argv_exec_path;
 static const char *argv0_path;
 
-const char *system_path(const char *path)
+char *system_path(const char *path)
 {
 #ifdef RUNTIME_PREFIX
        static const char *prefix;
@@ -16,7 +16,7 @@ const char *system_path(const char *path)
        struct strbuf d = STRBUF_INIT;
 
        if (is_absolute_path(path))
-               return path;
+               return xstrdup(path);
 
 #ifdef RUNTIME_PREFIX
        assert(argv0_path);
@@ -34,8 +34,7 @@ const char *system_path(const char *path)
 #endif
 
        strbuf_addf(&d, "%s/%s", prefix, path);
-       path = strbuf_detach(&d, NULL);
-       return path;
+       return strbuf_detach(&d, NULL);
 }
 
 const char *git_extract_argv0_path(const char *argv0)
index e4c9702f02858973096f6ef0f41e5a572ff4d0db..93b0c02529a854af4ccb648ea0d0571c5495b63a 100644 (file)
@@ -9,6 +9,6 @@ extern const char **prepare_git_cmd(const char **argv);
 extern int execv_git_cmd(const char **argv); /* NULL terminated */
 LAST_ARG_MUST_BE_NULL
 extern int execl_git_cmd(const char *cmd, ...);
-extern const char *system_path(const char *path);
+extern char *system_path(const char *path);
 
 #endif /* GIT_EXEC_CMD_H */
diff --git a/fsck.c b/fsck.c
index 2fffa434a5763abb6d7895ee8c7582307766ee62..032419463126234fa855b934bae85fb27d6b26a4 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -7,6 +7,7 @@
 #include "tag.h"
 #include "fsck.h"
 #include "refs.h"
+#include "utf8.h"
 
 static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)
 {
@@ -171,7 +172,9 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
                has_empty_name |= !*name;
                has_dot |= !strcmp(name, ".");
                has_dotdot |= !strcmp(name, "..");
-               has_dotgit |= !strcmp(name, ".git");
+               has_dotgit |= (!strcmp(name, ".git") ||
+                              is_hfs_dotgit(name) ||
+                              is_ntfs_dotgit(name));
                has_zero_pad |= *(char *)desc.buffer == '0';
                update_tree_entry(&desc);
 
index ee61a77d717a7210a1cf614914ab62788fd5e4fc..a67d0f98989706fa69df4cf7d6242ad9531eb337 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -17,6 +17,7 @@ s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
 k,keep          pass -k flag to git-mailinfo
 keep-non-patch  pass -b flag to git-mailinfo
+m,message-id    pass -m flag to git-mailinfo
 keep-cr         pass --keep-cr flag to git-mailsplit for mbox format
 no-keep-cr      do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
 c,scissors      strip everything before a scissors line
@@ -371,13 +372,18 @@ split_patches () {
 prec=4
 dotest="$GIT_DIR/rebase-apply"
 sign= utf8=t keep= keepcr= skip= interactive= resolved= rebasing= abort=
-resolvemsg= resume= scissors= no_inbody_headers=
+messageid= resolvemsg= resume= scissors= no_inbody_headers=
 git_apply_opt=
 committer_date_is_author_date=
 ignore_date=
 allow_rerere_autoupdate=
 gpg_sign_opt=
 
+if test "$(git config --bool --get am.messageid)" = true
+then
+    messageid=t
+fi
+
 if test "$(git config --bool --get am.keepcr)" = true
 then
     keepcr=t
@@ -400,6 +406,10 @@ it will be removed. Please do not use it anymore."
                utf8=t ;; # this is now default
        --no-utf8)
                utf8= ;;
+       -m|--message-id)
+               messageid=t ;;
+       --no-message-id)
+               messageid=f ;;
        -k|--keep)
                keep=t ;;
        --keep-non-patch)
@@ -567,6 +577,7 @@ Use \"git am --abort\" to remove it.")"
        echo "$sign" >"$dotest/sign"
        echo "$utf8" >"$dotest/utf8"
        echo "$keep" >"$dotest/keep"
+       echo "$messageid" >"$dotest/messageid"
        echo "$scissors" >"$dotest/scissors"
        echo "$no_inbody_headers" >"$dotest/no_inbody_headers"
        echo "$GIT_QUIET" >"$dotest/quiet"
@@ -621,6 +632,12 @@ b)
 *)
        keep= ;;
 esac
+case "$(cat "$dotest/messageid")" in
+t)
+       messageid=-m ;;
+f)
+       messageid= ;;
+esac
 case "$(cat "$dotest/scissors")" in
 t)
        scissors=--scissors ;;
@@ -692,7 +709,7 @@ do
                        get_author_ident_from_commit "$commit" >"$dotest/author-script"
                        git diff-tree --root --binary --full-index "$commit" >"$dotest/patch"
                else
-                       git mailinfo $keep $no_inbody_headers $scissors $utf8 "$dotest/msg" "$dotest/patch" \
+                       git mailinfo $keep $no_inbody_headers $messageid $scissors $utf8 "$dotest/msg" "$dotest/patch" \
                                <"$dotest/$msgnum" >"$dotest/info" ||
                                stop_here $this
 
index 400e92108687e31dd16fbcea1a43e04556b98566..b763a4e2693a5f08977132513196e78951868d5b 100644 (file)
@@ -75,7 +75,8 @@
 # endif
 #elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
       !defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
-      !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__)
+      !defined(__TANDEM) && !defined(__QNX__) && !defined(__MirBSD__) && \
+      !defined(__CYGWIN__)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif
@@ -474,6 +475,40 @@ extern int git_munmap(void *start, size_t length);
 #define on_disk_bytes(st) ((st).st_blocks * 512)
 #endif
 
+#ifdef NEEDS_MODE_TRANSLATION
+#undef S_IFMT
+#undef S_IFREG
+#undef S_IFDIR
+#undef S_IFLNK
+#undef S_IFBLK
+#undef S_IFCHR
+#undef S_IFIFO
+#undef S_IFSOCK
+#define S_IFMT   0170000
+#define S_IFREG  0100000
+#define S_IFDIR  0040000
+#define S_IFLNK  0120000
+#define S_IFBLK  0060000
+#define S_IFCHR  0020000
+#define S_IFIFO  0010000
+#define S_IFSOCK 0140000
+#ifdef stat
+#undef stat
+#endif
+#define stat(path, buf) git_stat(path, buf)
+extern int git_stat(const char *, struct stat *);
+#ifdef fstat
+#undef fstat
+#endif
+#define fstat(fd, buf) git_fstat(fd, buf)
+extern int git_fstat(int, struct stat *);
+#ifdef lstat
+#undef lstat
+#endif
+#define lstat(path, buf) git_lstat(path, buf)
+extern int git_lstat(const char *, struct stat *);
+#endif
+
 #define DEFAULT_PACKED_GIT_LIMIT \
        ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
 
index d4fb6dfe138fffd4d000c7d5aeb7787bea9970f4..2b11b1d6fe33cf3f61dd35b8f48d6953f7a25bff 100755 (executable)
@@ -94,3 +94,5 @@ else
                shift 7
        done
 fi
+
+exit 0
index 2b6635130a6c6e80cd8d188ae611bf220d25bc0a..fe61e89f31c44ca90dbfba468cf8b23d3993ba9f 100644 (file)
@@ -92,7 +92,7 @@ translate_merge_tool_path () {
 check_unchanged () {
        if test "$MERGED" -nt "$BACKUP"
        then
-               status=0
+               return 0
        else
                while true
                do
@@ -100,8 +100,8 @@ check_unchanged () {
                        printf "Was the merge successful? [y/n] "
                        read answer || return 1
                        case "$answer" in
-                       y*|Y*) status=0; break ;;
-                       n*|N*) status=1; break ;;
+                       y*|Y*) return 0 ;;
+                       n*|N*) return 1 ;;
                        esac
                done
        fi
@@ -119,8 +119,6 @@ setup_user_tool () {
 
        diff_cmd () {
                ( eval $merge_tool_cmd )
-               status=$?
-               return $status
        }
 
        merge_cmd () {
@@ -130,13 +128,10 @@ setup_user_tool () {
                then
                        touch "$BACKUP"
                        ( eval $merge_tool_cmd )
-                       status=$?
                        check_unchanged
                else
                        ( eval $merge_tool_cmd )
-                       status=$?
                fi
-               return $status
        }
 }
 
@@ -153,13 +148,11 @@ setup_tool () {
        }
 
        diff_cmd () {
-               status=1
-               return $status
+               return 1
        }
 
        merge_cmd () {
-               status=1
-               return $status
+               return 1
        }
 
        translate_merge_tool_path () {
@@ -210,7 +203,6 @@ run_merge_tool () {
 
        merge_tool_path=$(get_merge_tool_path "$1") || exit
        base_present="$2"
-       status=0
 
        # Bring tool-specific functions into scope
        setup_tool "$1" || return 1
@@ -221,8 +213,6 @@ run_merge_tool () {
        else
                run_diff_cmd "$1"
        fi
-       status=$?
-       return $status
 }
 
 # Run a either a configured or built-in diff tool
index ff050e58ff3b752113105ba3ea5e20c6b633aae9..d20581c15c5086b02411cab618cde0a9ff14e7ed 100755 (executable)
@@ -426,8 +426,6 @@ fi
 merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)"
 merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
 
-last_status=0
-rollup_status=0
 files=
 
 if test $# -eq 0
@@ -455,19 +453,15 @@ printf "%s\n" "$files"
 
 IFS='
 '
+rc=0
 for i in $files
 do
-       if test $last_status -ne 0
-       then
-               prompt_after_failed_merge || exit 1
-       fi
        printf "\n"
-       merge_file "$i"
-       last_status=$?
-       if test $last_status -ne 0
+       if ! merge_file "$i"
        then
-               rollup_status=1
+               rc=1
+               prompt_after_failed_merge || exit 1
        fi
 done
 
-exit $rollup_status
+exit $rc
index 9949db01e11959c81fae75fa018551601e6d5c3b..82c6feaa4640e993051bfbe57df593c0f012a9ba 100755 (executable)
@@ -58,6 +58,7 @@ sub usage {
     --compose                      * Open an editor for introduction.
     --compose-encoding      <str>  * Encoding to assume for introduction.
     --8bit-encoding         <str>  * Encoding to assume 8bit mails if undeclared
+    --transfer-encoding     <str>  * Transfer encoding to use (quoted-printable, 8bit, base64)
 
   Sending:
     --envelope-sender       <str>  * Email envelope sender.
@@ -206,6 +207,7 @@ sub do_edit {
 my (@suppress_cc);
 my ($auto_8bit_encoding);
 my ($compose_encoding);
+my ($target_xfer_encoding);
 
 my ($debug_net_smtp) = 0;              # Net::SMTP, see send_message()
 
@@ -242,6 +244,7 @@ sub do_edit {
     "from" => \$sender,
     "assume8bitencoding" => \$auto_8bit_encoding,
     "composeencoding" => \$compose_encoding,
+    "transferencoding" => \$target_xfer_encoding,
 );
 
 my %config_path_settings = (
@@ -314,6 +317,7 @@ sub signal_handler {
                    "envelope-sender=s" => \$envelope_sender,
                    "thread!" => \$thread,
                    "validate!" => \$validate,
+                   "transfer-encoding=s" => \$target_xfer_encoding,
                    "format-patch!" => \$format_patch,
                    "8bit-encoding=s" => \$auto_8bit_encoding,
                    "compose-encoding=s" => \$compose_encoding,
@@ -1324,6 +1328,8 @@ sub send_message {
        my $author_encoding;
        my $has_content_type;
        my $body_encoding;
+       my $xfer_encoding;
+       my $has_mime_version;
        @to = ();
        @cc = ();
        @xh = ();
@@ -1394,9 +1400,16 @@ sub send_message {
                                }
                                push @xh, $_;
                        }
+                       elsif (/^MIME-Version/i) {
+                               $has_mime_version = 1;
+                               push @xh, $_;
+                       }
                        elsif (/^Message-Id: (.*)/i) {
                                $message_id = $1;
                        }
+                       elsif (/^Content-Transfer-Encoding: (.*)/i) {
+                               $xfer_encoding = $1 if not defined $xfer_encoding;
+                       }
                        elsif (!/^Date:\s/i && /^[-A-Za-z]+:\s+\S/) {
                                push @xh, $_;
                        }
@@ -1444,10 +1457,9 @@ sub send_message {
                if defined $cc_cmd && !$suppress_cc{'cccmd'};
 
        if ($broken_encoding{$t} && !$has_content_type) {
+               $xfer_encoding = '8bit' if not defined $xfer_encoding;
                $has_content_type = 1;
-               push @xh, "MIME-Version: 1.0",
-                       "Content-Type: text/plain; charset=$auto_8bit_encoding",
-                       "Content-Transfer-Encoding: 8bit";
+               push @xh, "Content-Type: text/plain; charset=$auto_8bit_encoding";
                $body_encoding = $auto_8bit_encoding;
        }
 
@@ -1467,14 +1479,25 @@ sub send_message {
                                }
                        }
                        else {
+                               $xfer_encoding = '8bit' if not defined $xfer_encoding;
                                $has_content_type = 1;
                                push @xh,
-                                 'MIME-Version: 1.0',
-                                 "Content-Type: text/plain; charset=$author_encoding",
-                                 'Content-Transfer-Encoding: 8bit';
+                                 "Content-Type: text/plain; charset=$author_encoding";
                        }
                }
        }
+       if (defined $target_xfer_encoding) {
+               $xfer_encoding = '8bit' if not defined $xfer_encoding;
+               $message = apply_transfer_encoding(
+                       $message, $xfer_encoding, $target_xfer_encoding);
+               $xfer_encoding = $target_xfer_encoding;
+       }
+       if (defined $xfer_encoding) {
+               push @xh, "Content-Transfer-Encoding: $xfer_encoding";
+       }
+       if (defined $xfer_encoding or $has_content_type) {
+               unshift @xh, 'MIME-Version: 1.0' unless $has_mime_version;
+       }
 
        $needs_confirm = (
                $confirm eq "always" or
@@ -1543,6 +1566,32 @@ sub cleanup_compose_files {
 
 $smtp->quit if $smtp;
 
+sub apply_transfer_encoding {
+       my $message = shift;
+       my $from = shift;
+       my $to = shift;
+
+       return $message if ($from eq $to and $from ne '7bit');
+
+       require MIME::QuotedPrint;
+       require MIME::Base64;
+
+       $message = MIME::QuotedPrint::decode($message)
+               if ($from eq 'quoted-printable');
+       $message = MIME::Base64::decode($message)
+               if ($from eq 'base64');
+
+       die "cannot send message as 7bit"
+               if ($to eq '7bit' and $message =~ /[^[:ascii:]]/);
+       return $message
+               if ($to eq '7bit' or $to eq '8bit');
+       return MIME::QuotedPrint::encode($message, "\n", 0)
+               if ($to eq 'quoted-printable');
+       return MIME::Base64::encode($message, "\n")
+               if ($to eq 'base64');
+       die "invalid transfer encoding";
+}
+
 sub unique_email_list {
        my %seen;
        my @emails;
index d968760139b0e7b9219a1602a2e646a3ed398136..c42c6e6365090e22ec7892baa51b919f2898e956 100644 (file)
@@ -81,7 +81,7 @@ if test -n "$OPTIONS_SPEC"; then
                echo exit $?
        )"
 else
-       dashless=$(basename "$0" | sed -e 's/-/ /')
+       dashless=$(basename -- "$0" | sed -e 's/-/ /')
        usage() {
                die "usage: $dashless $USAGE"
        }
diff --git a/git.c b/git.c
index 18fbf79430687a473e9306f2bb65abbaec161bb4..82d7a1cfee806001c2d8b81a9dc822a513c75451 100644 (file)
--- a/git.c
+++ b/git.c
@@ -487,15 +487,20 @@ static struct cmd_struct commands[] = {
        { "write-tree", cmd_write_tree, RUN_SETUP },
 };
 
-int is_builtin(const char *s)
+static struct cmd_struct *get_builtin(const char *s)
 {
        int i;
        for (i = 0; i < ARRAY_SIZE(commands); i++) {
-               struct cmd_struct *p = commands+i;
+               struct cmd_struct *p = commands + i;
                if (!strcmp(s, p->cmd))
-                       return 1;
+                       return p;
        }
-       return 0;
+       return NULL;
+}
+
+int is_builtin(const char *s)
+{
+       return !!get_builtin(s);
 }
 
 static void handle_builtin(int argc, const char **argv)
@@ -503,6 +508,7 @@ static void handle_builtin(int argc, const char **argv)
        const char *cmd = argv[0];
        int i;
        static const char ext[] = STRIP_EXTENSION;
+       struct cmd_struct *builtin;
 
        if (sizeof(ext) > 1) {
                i = strlen(argv[0]) - strlen(ext);
@@ -519,15 +525,12 @@ static void handle_builtin(int argc, const char **argv)
                argv[0] = cmd = "help";
        }
 
-       for (i = 0; i < ARRAY_SIZE(commands); i++) {
-               struct cmd_struct *p = commands+i;
-               if (strcmp(p->cmd, cmd))
-                       continue;
-               if (saved_environment && (p->option & NO_SETUP)) {
+       builtin = get_builtin(cmd);
+       if (builtin) {
+               if (saved_environment && (builtin->option & NO_SETUP))
                        restore_env();
-                       break;
-               }
-               exit(run_builtin(p, argc, argv));
+               else
+                       exit(run_builtin(builtin, argc, argv));
        }
 }
 
index ccf75169dd6fb50e2f8ce584af8fa7841fa55fd6..7a5b23acf2155fb4e39dcb2f20944362cfbe0ac7 100755 (executable)
 use Time::HiRes qw(gettimeofday tv_interval);
 binmode STDOUT, ':utf8';
 
+if (!defined($CGI::VERSION) || $CGI::VERSION < 4.08) {
+       eval 'sub CGI::multi_param { CGI::param(@_) }'
+}
+
 our $t0 = [ gettimeofday() ];
 our $number_of_git_cmds = 0;
 
@@ -871,7 +875,7 @@ sub evaluate_query_params {
 
        while (my ($name, $symbol) = each %cgi_param_mapping) {
                if ($symbol eq 'opt') {
-                       $input_params{$name} = [ map { decode_utf8($_) } $cgi->param($symbol) ];
+                       $input_params{$name} = [ map { decode_utf8($_) } $cgi->multi_param($symbol) ];
                } else {
                        $input_params{$name} = decode_utf8($cgi->param($symbol));
                }
index 81890a6680029ec1d369a7eef11a4ec112847fb7..9e9589730fa44c94d122dfdb50172baf95e2578d 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -71,22 +71,17 @@ static void add_mapping(struct string_list *map,
                        char *old_name, char *old_email)
 {
        struct mailmap_entry *me;
-       int index;
+       struct string_list_item *item;
 
        if (old_email == NULL) {
                old_email = new_email;
                new_email = NULL;
        }
 
-       if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) {
-               /* mailmap entry exists, invert index value */
-               index = -1 - index;
-               me = (struct mailmap_entry *)map->items[index].util;
+       item = string_list_insert(map, old_email);
+       if (item->util) {
+               me = (struct mailmap_entry *)item->util;
        } else {
-               /* create mailmap entry */
-               struct string_list_item *item;
-
-               item = string_list_insert_at_index(map, index, old_email);
                me = xcalloc(1, sizeof(struct mailmap_entry));
                me->namemap.strdup_strings = 1;
                me->namemap.cmp = namemap_cmp;
@@ -94,8 +89,8 @@ static void add_mapping(struct string_list *map,
        }
 
        if (old_name == NULL) {
-               debug_mm("mailmap: adding (simple) entry for %s at index %d\n",
-                        old_email, index);
+               debug_mm("mailmap: adding (simple) entry for '%s'\n", old_email);
+
                /* Replace current name and new email for simple entry */
                if (new_name) {
                        free(me->name);
@@ -107,8 +102,7 @@ static void add_mapping(struct string_list *map,
                }
        } else {
                struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
-               debug_mm("mailmap: adding (complex) entry for %s at index %d\n",
-                        old_email, index);
+               debug_mm("mailmap: adding (complex) entry for '%s'\n", old_email);
                if (new_name)
                        mi->name = xstrdup(new_name);
                if (new_email)
index fdb7d0f10ba471bcdff26dc87851d7df34fdc971..25c067e4a71f1ed2c5d4971d41ab6b4e1108e9e4 100644 (file)
@@ -275,23 +275,20 @@ struct tree *write_tree_from_memory(struct merge_options *o)
 }
 
 static int save_files_dirs(const unsigned char *sha1,
-               const char *base, int baselen, const char *path,
+               struct strbuf *base, const char *path,
                unsigned int mode, int stage, void *context)
 {
-       int len = strlen(path);
-       char *newpath = xmalloc(baselen + len + 1);
+       int baselen = base->len;
        struct merge_options *o = context;
 
-       memcpy(newpath, base, baselen);
-       memcpy(newpath + baselen, path, len);
-       newpath[baselen + len] = '\0';
+       strbuf_addstr(base, path);
 
        if (S_ISDIR(mode))
-               string_list_insert(&o->current_directory_set, newpath);
+               string_list_insert(&o->current_directory_set, base->buf);
        else
-               string_list_insert(&o->current_file_set, newpath);
-       free(newpath);
+               string_list_insert(&o->current_file_set, base->buf);
 
+       strbuf_setlen(base, baselen);
        return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
 }
 
index 85ac7201571ed34c35a3613b97bd1b82982e0d61..f138cb4e731018b40426337eb8062f40512df088 100644 (file)
@@ -11,5 +11,4 @@ merge_cmd () {
                "$merge_tool_path" --merge \
                        --result="$MERGED" "$LOCAL" "$REMOTE"
        fi
-       status=$?
 }
index f96d9e550a138d395ccb23c9496111630d2de594..7b895fdb1f9955eb470a2614e0634bfcd26d90c9 100644 (file)
@@ -15,7 +15,6 @@ merge_cmd () {
                        "$LOCAL" "$REMOTE" \
                        "$(basename "$MERGED")"
        fi
-       status=$?
 }
 
 translate_merge_tool_path() {
index a30034f116fda6f90448aeb4ddd42063ea60aad1..793d1293b16c9b28a9ec5c70ec7fa0875ab0da54 100644 (file)
@@ -20,5 +20,4 @@ merge_cmd () {
                        -o "$MERGED" "$LOCAL" "$REMOTE" \
                >/dev/null 2>&1
        fi
-       status=$?
 }
diff --git a/notes.c b/notes.c
index 5fe691dbcdfb9e3f9fa3c768f243234d5bb2e816..c763a21eef5b64c984c22d18cd5f21c1bd32e5a3 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -902,7 +902,7 @@ int combine_notes_cat_sort_uniq(unsigned char *cur_sha1,
        if (string_list_add_note_lines(&sort_uniq_list, new_sha1))
                goto out;
        string_list_remove_empty_items(&sort_uniq_list, 0);
-       sort_string_list(&sort_uniq_list);
+       string_list_sort(&sort_uniq_list);
        string_list_remove_duplicates(&sort_uniq_list, 0);
 
        /* create a new blob object from sort_uniq_list */
@@ -1218,8 +1218,7 @@ static void format_note(struct notes_tree *t, const unsigned char *object_sha1,
        if (!sha1)
                return;
 
-       if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||
-                       type != OBJ_BLOB) {
+       if (!(msg = read_sha1_file(sha1, &type, &msglen)) || type != OBJ_BLOB) {
                free(msg);
                return;
        }
index 8029ae35619fbff7e9e595b20ea25c379c698016..c05d1386af7210e75a1012f3772745315ecc3592 100644 (file)
@@ -472,7 +472,6 @@ static void write_selected_commits_v1(struct sha1file *f,
 
        for (i = 0; i < writer.selected_nr; ++i) {
                struct bitmapped_commit *stored = &writer.selected[i];
-               struct bitmap_disk_entry on_disk;
 
                int commit_pos =
                        sha1_pos(stored->commit->object.sha1, index, index_nr, sha1_access);
@@ -480,11 +479,10 @@ static void write_selected_commits_v1(struct sha1file *f,
                if (commit_pos < 0)
                        die("BUG: trying to write commit not in index");
 
-               on_disk.object_pos = htonl(commit_pos);
-               on_disk.xor_offset = stored->xor_offset;
-               on_disk.flags = stored->flags;
+               sha1write_be32(f, commit_pos);
+               sha1write_u8(f, stored->xor_offset);
+               sha1write_u8(f, stored->flags);
 
-               sha1write(f, &on_disk, sizeof(on_disk));
                dump_bitmap(f, stored->write_as);
        }
 }
index a1f3c0d34f8958c08310aa9fc77e764885b76008..6a818419ca46f8d66adbeb0027da49e6bab1bfa3 100644 (file)
@@ -197,13 +197,24 @@ static struct stored_bitmap *store_bitmap(struct bitmap_index *index,
        return stored;
 }
 
+static inline uint32_t read_be32(const unsigned char *buffer, size_t *pos)
+{
+       uint32_t result = get_be32(buffer + *pos);
+       (*pos) += sizeof(result);
+       return result;
+}
+
+static inline uint8_t read_u8(const unsigned char *buffer, size_t *pos)
+{
+       return buffer[(*pos)++];
+}
+
 static int load_bitmap_entries_v1(struct bitmap_index *index)
 {
        static const size_t MAX_XOR_OFFSET = 160;
 
        uint32_t i;
        struct stored_bitmap **recent_bitmaps;
-       struct bitmap_disk_entry *entry;
 
        recent_bitmaps = xcalloc(MAX_XOR_OFFSET, sizeof(struct stored_bitmap));
 
@@ -214,15 +225,12 @@ static int load_bitmap_entries_v1(struct bitmap_index *index)
                uint32_t commit_idx_pos;
                const unsigned char *sha1;
 
-               entry = (struct bitmap_disk_entry *)(index->map + index->map_pos);
-               index->map_pos += sizeof(struct bitmap_disk_entry);
+               commit_idx_pos = read_be32(index->map, &index->map_pos);
+               xor_offset = read_u8(index->map, &index->map_pos);
+               flags = read_u8(index->map, &index->map_pos);
 
-               commit_idx_pos = ntohl(entry->object_pos);
                sha1 = nth_packed_object_sha1(index->pack, commit_idx_pos);
 
-               xor_offset = (int)entry->xor_offset;
-               flags = (int)entry->flags;
-
                bitmap = read_bitmap_1(index);
                if (!bitmap)
                        return -1;
index 8b7f4e9f0df2d50ffab1c0535d678c69fd7d6e94..487600b18c79be22c4c611fc4c3d29d817f1f304 100644 (file)
@@ -5,12 +5,6 @@
 #include "khash.h"
 #include "pack-objects.h"
 
-struct bitmap_disk_entry {
-       uint32_t object_pos;
-       uint8_t xor_offset;
-       uint8_t flags;
-} __attribute__((packed));
-
 struct bitmap_disk_header {
        char magic[4];
        uint16_t version;
diff --git a/path.c b/path.c
index f68df0cf888124592db6c771d78d94894ffbacd9..e6089938018b74fb8a6ea547ffc64d428a13daa0 100644 (file)
--- a/path.c
+++ b/path.c
@@ -823,3 +823,36 @@ int daemon_avoid_alias(const char *p)
                }
        }
 }
+
+static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
+{
+       if (len < skip)
+               return 0;
+       len -= skip;
+       path += skip;
+       while (len-- > 0) {
+               char c = *(path++);
+               if (c != ' ' && c != '.')
+                       return 0;
+       }
+       return 1;
+}
+
+int is_ntfs_dotgit(const char *name)
+{
+       int len;
+
+       for (len = 0; ; len++)
+               if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
+                       if (only_spaces_and_periods(name, len, 4) &&
+                                       !strncasecmp(name, ".git", 4))
+                               return 1;
+                       if (only_spaces_and_periods(name, len, 5) &&
+                                       !strncasecmp(name, "git~1", 5))
+                               return 1;
+                       if (name[len] != '\\')
+                               return 0;
+                       name += len + 1;
+                       len = -1;
+               }
+}
index e5b4938efcf329eb4f25b6f90231c35abeacbbd3..8181eebbfcd0080540bc580416336436ced98297 100644 (file)
--- a/prompt.c
+++ b/prompt.c
@@ -57,11 +57,19 @@ char *git_prompt(const char *prompt, int flags)
                        r = do_askpass(askpass, prompt);
        }
 
-       if (!r)
-               r = git_terminal_prompt(prompt, flags & PROMPT_ECHO);
        if (!r) {
-               /* prompts already contain ": " at the end */
-               die("could not read %s%s", prompt, strerror(errno));
+               const char *err;
+
+               if (git_env_bool("GIT_TERMINAL_PROMPT", 1)) {
+                       r = git_terminal_prompt(prompt, flags & PROMPT_ECHO);
+                       err = strerror(errno);
+               } else {
+                       err = "terminal prompts disabled";
+               }
+               if (!r) {
+                       /* prompts already contain ": " at the end */
+                       die("could not read %s%s", prompt, err);
+               }
        }
        return r;
 }
diff --git a/quote.c b/quote.c
index 45e3db12d514dd7393539ca0aff09f5aa8467b8d..7920e18e44834f1f86c218c172ae10227858ed66 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -274,27 +274,6 @@ void write_name_quoted(const char *name, FILE *fp, int terminator)
        fputc(terminator, fp);
 }
 
-void write_name_quotedpfx(const char *pfx, size_t pfxlen,
-                         const char *name, FILE *fp, int terminator)
-{
-       int needquote = 0;
-
-       if (terminator) {
-               needquote = next_quote_pos(pfx, pfxlen) < pfxlen
-                       || name[next_quote_pos(name, -1)];
-       }
-       if (needquote) {
-               fputc('"', fp);
-               quote_c_style_counted(pfx, pfxlen, NULL, fp, 1);
-               quote_c_style(name, NULL, fp, 1);
-               fputc('"', fp);
-       } else {
-               fwrite(pfx, pfxlen, 1, fp);
-               fputs(name, fp);
-       }
-       fputc(terminator, fp);
-}
-
 void write_name_quoted_relative(const char *name, const char *prefix,
                                FILE *fp, int terminator)
 {
diff --git a/quote.h b/quote.h
index 71dcc3aa0279af6611f7471d1a4c9949b1dc49fb..99e04d34bf223a0147e65623322ef2c2fb01e01d 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -56,8 +56,6 @@ extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq
 extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
 
 extern void write_name_quoted(const char *name, FILE *, int terminator);
-extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
-                                 const char *name, FILE *, int terminator);
 extern void write_name_quoted_relative(const char *name, const char *prefix,
                FILE *fp, int terminator);
 
index 8f3e9eb31498d708acf63bf3fc6de3d34562d42e..9cff715d6b669ecf05f861bc553b61d20d04191d 100644 (file)
@@ -17,6 +17,7 @@
 #include "varint.h"
 #include "split-index.h"
 #include "sigchain.h"
+#include "utf8.h"
 
 static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
                                               unsigned int options);
@@ -776,9 +777,10 @@ static int verify_dotfile(const char *rest)
         * shares the path end test with the ".." case.
         */
        case 'g':
-               if (rest[1] != 'i')
+       case 'G':
+               if (rest[1] != 'i' && rest[1] != 'I')
                        break;
-               if (rest[2] != 't')
+               if (rest[2] != 't' && rest[2] != 'T')
                        break;
                rest += 2;
        /* fallthrough */
@@ -802,6 +804,10 @@ int verify_path(const char *path)
                        return 1;
                if (is_dir_sep(c)) {
 inside:
+                       if (protect_hfs && is_hfs_dotgit(path))
+                               return 0;
+                       if (protect_ntfs && is_ntfs_dotgit(path))
+                               return 0;
                        c = *path++;
                        if ((c == '.' && !verify_dotfile(path)) ||
                            is_dir_sep(c) || c == '\0')
diff --git a/refs.c b/refs.c
index 5ff457ebfce7aba9cd470f3e2e1f3f5dbe9d9741..bfe4ba4213d445189b564726a9acbaa0924e1370 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -2318,6 +2318,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 
        lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
        if (lock->lock_fd < 0) {
+               last_errno = errno;
                if (errno == ENOENT && --attempts_remaining > 0)
                        /*
                         * Maybe somebody just deleted one of the
@@ -2325,8 +2326,13 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                         * again:
                         */
                        goto retry;
-               else
-                       unable_to_lock_die(ref_file, errno);
+               else {
+                       struct strbuf err = STRBUF_INIT;
+                       unable_to_lock_message(ref_file, errno, &err);
+                       error("%s", err.buf);
+                       strbuf_reset(&err);
+                       goto error_return;
+               }
        }
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 
@@ -2639,22 +2645,25 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
        return 0;
 }
 
-int repack_without_refs(const char **refnames, int n, struct strbuf *err)
+int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
-       struct string_list_item *ref_to_delete;
-       int i, ret, removed = 0;
+       struct string_list_item *refname, *ref_to_delete;
+       int ret, needs_repacking = 0, removed = 0;
 
        assert(err);
 
        /* Look for a packed ref */
-       for (i = 0; i < n; i++)
-               if (get_packed_ref(refnames[i]))
+       for_each_string_list_item(refname, refnames) {
+               if (get_packed_ref(refname->string)) {
+                       needs_repacking = 1;
                        break;
+               }
+       }
 
        /* Avoid locking if we have nothing to do */
-       if (i == n)
+       if (!needs_repacking)
                return 0; /* no refname exists in packed refs */
 
        if (lock_packed_refs(0)) {
@@ -2664,8 +2673,8 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
        packed = get_packed_refs(&ref_cache);
 
        /* Remove refnames from the cache */
-       for (i = 0; i < n; i++)
-               if (remove_entry(packed, refnames[i]) != -1)
+       for_each_string_list_item(refname, refnames)
+               if (remove_entry(packed, refname->string) != -1)
                        removed = 1;
        if (!removed) {
                /*
@@ -3404,29 +3413,54 @@ int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void
 
                        bp = find_beginning_of_line(buf, scanp);
 
-                       if (*bp != '\n') {
-                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
-                               if (pos)
-                                       break; /* need to fill another block */
-                               scanp = buf - 1; /* leave loop */
-                       } else {
+                       if (*bp == '\n') {
                                /*
-                                * (bp + 1) thru endp is the beginning of the
-                                * current line we have in sb
+                                * The newline is the end of the previous line,
+                                * so we know we have complete line starting
+                                * at (bp + 1). Prefix it onto any prior data
+                                * we collected for the line and process it.
                                 */
                                strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
                                scanp = bp;
                                endp = bp + 1;
+                               ret = show_one_reflog_ent(&sb, fn, cb_data);
+                               strbuf_reset(&sb);
+                               if (ret)
+                                       break;
+                       } else if (!pos) {
+                               /*
+                                * We are at the start of the buffer, and the
+                                * start of the file; there is no previous
+                                * line, and we have everything for this one.
+                                * Process it, and we can end the loop.
+                                */
+                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
+                               ret = show_one_reflog_ent(&sb, fn, cb_data);
+                               strbuf_reset(&sb);
+                               break;
                        }
-                       ret = show_one_reflog_ent(&sb, fn, cb_data);
-                       strbuf_reset(&sb);
-                       if (ret)
+
+                       if (bp == buf) {
+                               /*
+                                * We are at the start of the buffer, and there
+                                * is more file to read backwards. Which means
+                                * we are in the middle of a line. Note that we
+                                * may get here even if *bp was a newline; that
+                                * just means we are at the exact end of the
+                                * previous line, rather than some spot in the
+                                * middle.
+                                *
+                                * Save away what we have to be combined with
+                                * the data from the next read.
+                                */
+                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
                                break;
+                       }
                }
 
        }
        if (!ret && sb.len)
-               ret = show_one_reflog_ent(&sb, fn, cb_data);
+               die("BUG: reverse reflog parser had leftover data");
 
        fclose(logfp);
        strbuf_release(&sb);
@@ -3738,10 +3772,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
 int ref_transaction_commit(struct ref_transaction *transaction,
                           struct strbuf *err)
 {
-       int ret = 0, delnum = 0, i;
-       const char **delnames;
+       int ret = 0, i;
        int n = transaction->nr;
        struct ref_update **updates = transaction->updates;
+       struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
+       struct string_list_item *ref_to_delete;
 
        assert(err);
 
@@ -3753,9 +3788,6 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                return 0;
        }
 
-       /* Allocate work space */
-       delnames = xmalloc(sizeof(*delnames) * n);
-
        /* Copy, sort, and reject duplicate refs */
        qsort(updates, n, sizeof(*updates), ref_update_compare);
        if (ref_update_reject_duplicates(updates, n, err)) {
@@ -3815,16 +3847,17 @@ int ref_transaction_commit(struct ref_transaction *transaction,
                        }
 
                        if (!(update->flags & REF_ISPRUNING))
-                               delnames[delnum++] = update->lock->ref_name;
+                               string_list_append(&refs_to_delete,
+                                                  update->lock->ref_name);
                }
        }
 
-       if (repack_without_refs(delnames, delnum, err)) {
+       if (repack_without_refs(&refs_to_delete, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
-       for (i = 0; i < delnum; i++)
-               unlink_or_warn(git_path("logs/%s", delnames[i]));
+       for_each_string_list_item(ref_to_delete, &refs_to_delete)
+               unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
        clear_loose_ref_cache(&ref_cache);
 
 cleanup:
@@ -3833,7 +3866,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
        for (i = 0; i < n; i++)
                if (updates[i]->lock)
                        unlock_ref(updates[i]->lock);
-       free(delnames);
+       string_list_clear(&refs_to_delete, 0);
        return ret;
 }
 
diff --git a/refs.h b/refs.h
index 2bc3556874a7b1cbccb4cebf4e3dc2fa13cc780f..405c6572f1135d06b68b6f60543e67343b117227 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -163,7 +163,15 @@ extern void rollback_packed_refs(void);
  */
 int pack_refs(unsigned int flags);
 
-extern int repack_without_refs(const char **refnames, int n,
+/*
+ * Rewrite the packed-refs file, omitting any refs listed in
+ * 'refnames'. On error, packed-refs will be unchanged, the return
+ * value is nonzero, and a message about the error is written to the
+ * 'err' strbuf.
+ *
+ * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
+ */
+extern int repack_without_refs(struct string_list *refnames,
                               struct strbuf *err);
 
 extern int ref_exists(const char *);
index f62421702f66bb9b8f64247dd339d01f2fbfb4c1..ae4ecfa1163137f7372fd02a51dc8a93319d218f 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1356,7 +1356,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
        }
        clear_commit_marks_many(sent_tips.nr, sent_tips.tip, TMP_MARK);
 
-       sort_string_list(&dst_tag);
+       string_list_sort(&dst_tag);
 
        /* Collect tags they do not have. */
        for (ref = src; ref; ref = ref->next) {
@@ -1421,7 +1421,7 @@ static void prepare_ref_index(struct string_list *ref_index, struct ref *ref)
        for ( ; ref; ref = ref->next)
                string_list_append_nodup(ref_index, ref->name)->util = ref;
 
-       sort_string_list(ref_index);
+       string_list_sort(ref_index);
 }
 
 /*
@@ -2135,7 +2135,7 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet
        info.ref_count = ref_count;
        for (ref = fetch_map; ref; ref = ref->next)
                string_list_append(&ref_names, ref->name);
-       sort_string_list(&ref_names);
+       string_list_sort(&ref_names);
        for_each_ref(get_stale_heads_cb, &info);
        string_list_clear(&ref_names, 0);
        return stale_refs;
index a47699966c59f708683e439bcd3296d5fce8acf5..0b432cc9713251b4d116bedf52ae1f51bd9d80cf 100644 (file)
@@ -833,20 +833,3 @@ int run_hook_le(const char *const *env, const char *name, ...)
 
        return ret;
 }
-
-int run_hook_with_custom_index(const char *index_file, const char *name, ...)
-{
-       const char *hook_env[3] =  { NULL };
-       char index[PATH_MAX];
-       va_list args;
-       int ret;
-
-       snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
-       hook_env[0] = index;
-
-       va_start(args, name);
-       ret = run_hook_ve(hook_env, name, args);
-       va_end(args);
-
-       return ret;
-}
index 2137315ee46f672d945e0d3e011a8cbfbd5582a7..d6868dc8c868a61fb1ccc6230f43048dd9838a24 100644 (file)
@@ -57,10 +57,6 @@ LAST_ARG_MUST_BE_NULL
 extern int run_hook_le(const char *const *env, const char *name, ...);
 extern int run_hook_ve(const char *const *env, const char *name, va_list args);
 
-LAST_ARG_MUST_BE_NULL
-__attribute__((deprecated))
-extern int run_hook_with_custom_index(const char *index_file, const char *name, ...);
-
 #define RUN_COMMAND_NO_STDIN 1
 #define RUN_GIT_CMD         2  /*If this is to be git sub-command */
 #define RUN_COMMAND_STDOUT_TO_STDERR 4
index a03d4fa2521fbc119a8d6b471b9e3c1046610b84..77a1266760718b8e5b029636409634e241981888 100644 (file)
@@ -267,6 +267,23 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
        return 0;
 }
 
+void append_conflicts_hint(struct strbuf *msgbuf)
+{
+       int i;
+
+       strbuf_addch(msgbuf, '\n');
+       strbuf_commented_addf(msgbuf, "Conflicts:\n");
+       for (i = 0; i < active_nr;) {
+               const struct cache_entry *ce = active_cache[i++];
+               if (ce_stage(ce)) {
+                       strbuf_commented_addf(msgbuf, "\t%s\n", ce->name);
+                       while (i < active_nr && !strcmp(ce->name,
+                                                       active_cache[i]->name))
+                               i++;
+               }
+       }
+}
+
 static int do_recursive_merge(struct commit *base, struct commit *next,
                              const char *base_label, const char *next_label,
                              unsigned char *head, struct strbuf *msgbuf,
@@ -307,21 +324,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        if (opts->signoff)
                append_signoff(msgbuf, 0, 0);
 
-       if (!clean) {
-               int i;
-               strbuf_addstr(msgbuf, "\nConflicts:\n");
-               for (i = 0; i < active_nr;) {
-                       const struct cache_entry *ce = active_cache[i++];
-                       if (ce_stage(ce)) {
-                               strbuf_addch(msgbuf, '\t');
-                               strbuf_addstr(msgbuf, ce->name);
-                               strbuf_addch(msgbuf, '\n');
-                               while (i < active_nr && !strcmp(ce->name,
-                                               active_cache[i]->name))
-                                       i++;
-                       }
-               }
-       }
+       if (!clean)
+               append_conflicts_hint(msgbuf);
 
        return !clean;
 }
index db43e9cf86dcc0914a674adca63303c44e2ccc24..5ed5cb1d97ced70a9ebffd1dad524a12e7f7810a 100644 (file)
@@ -53,5 +53,6 @@ int sequencer_pick_revisions(struct replay_opts *opts);
 extern const char sign_off_header[];
 
 void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
+void append_conflicts_hint(struct strbuf *msgbuf);
 
 #endif
index d7f1838c13f5a88448d0f2034347bfb6482b3edf..30995e61b38fbfd1733acc5c3c5866b61d9f09e1 100644 (file)
@@ -1198,7 +1198,7 @@ static void report_pack_garbage(struct string_list *list)
        if (!report_garbage)
                return;
 
-       sort_string_list(list);
+       string_list_sort(list);
 
        for (i = 0; i < list->nr; i++) {
                const char *path = list->items[i].string;
index 5b004f513b999b31b7968b22dde955c859704853..cb88170252a26c37d90f5c03263136439ec60e54 100644 (file)
@@ -372,10 +372,10 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
        int status, exists;
        static char hex[41];
 
-       exists = has_sha1_file(sha1);
        memcpy(hex, sha1_to_hex(sha1), 40);
        if (len == 40 || !len)
                return hex;
+       exists = has_sha1_file(sha1);
        while (len < 40) {
                unsigned char sha1_ret[20];
                status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
index c5aa0765e8e0a044e9e84e43280d5964f232ef63..2a32a3f1f5d250d7ea2a1b4a65998eeddb44962e 100644 (file)
@@ -59,13 +59,7 @@ static int add_entry(int insert_at, struct string_list *list, const char *string
 
 struct string_list_item *string_list_insert(struct string_list *list, const char *string)
 {
-       return string_list_insert_at_index(list, -1, string);
-}
-
-struct string_list_item *string_list_insert_at_index(struct string_list *list,
-                                                    int insert_at, const char *string)
-{
-       int index = add_entry(insert_at, list, string);
+       int index = add_entry(-1, list, string);
 
        if (index < 0)
                index = -1 - index;
@@ -220,7 +214,7 @@ struct string_list_item *string_list_append(struct string_list *list,
 /* Yuck */
 static compare_strings_fn compare_for_qsort;
 
-/* Only call this from inside sort_string_list! */
+/* Only call this from inside string_list_sort! */
 static int cmp_items(const void *a, const void *b)
 {
        const struct string_list_item *one = a;
@@ -228,7 +222,7 @@ static int cmp_items(const void *a, const void *b)
        return compare_for_qsort(one->string, two->string);
 }
 
-void sort_string_list(struct string_list *list)
+void string_list_sort(struct string_list *list)
 {
        compare_for_qsort = list->cmp ? list->cmp : strcmp;
        qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
index 494eb5d95de0e5e8d69705f7a6f4fe2efcb232ad..d3809a14172da5b1512b08fa411136821465659e 100644 (file)
@@ -55,9 +55,17 @@ void string_list_remove_empty_items(struct string_list *list, int free_util);
 int string_list_has_string(const struct string_list *list, const char *string);
 int string_list_find_insert_index(const struct string_list *list, const char *string,
                                  int negative_existing_index);
+/*
+ * Inserts the given string into the sorted list.
+ * If the string already exists, the list is not altered.
+ * Returns the string_list_item, the string is part of.
+ */
 struct string_list_item *string_list_insert(struct string_list *list, const char *string);
-struct string_list_item *string_list_insert_at_index(struct string_list *list,
-                                                    int insert_at, const char *string);
+
+/*
+ * Checks if the given string is part of a sorted list. If it is part of the list,
+ * return the coresponding string_list_item, NULL otherwise.
+ */
 struct string_list_item *string_list_lookup(struct string_list *list, const char *string);
 
 /*
@@ -85,7 +93,7 @@ struct string_list_item *string_list_append(struct string_list *list, const char
  */
 struct string_list_item *string_list_append_nodup(struct string_list *list, char *string);
 
-void sort_string_list(struct string_list *list);
+void string_list_sort(struct string_list *list);
 int unsorted_string_list_has_string(struct string_list *list, const char *string);
 struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
                                                     const char *string);
index 9952261299e62c88d04954a2a8e738cd6a2adb30..d5bb0c9aa197661bf154d78858a79e60fa50fbaa 100644 (file)
--- a/t/README
+++ b/t/README
@@ -418,7 +418,8 @@ Don't:
    dies in an unexpected way (e.g. segfault).
 
    On the other hand, don't use test_must_fail for running regular
-   platform commands; just use '! cmd'.
+   platform commands; just use '! cmd'.  We are not in the business
+   of verifying that the world given to us sanely works.
 
  - use perl without spelling it as "$PERL_PATH". This is to help our
    friends on Windows where the platform Perl often adds CR before
index e62c0ffbc22cffffd9c29254a96222bfb5b85633..7de8d85ee85e2387463e8d98175c9d8f66ca8959 100755 (executable)
@@ -12,6 +12,13 @@ check_config () {
                echo "expected a directory $1, a file $1/config and $1/refs"
                return 1
        fi
+
+       if test_have_prereq POSIXPERM && test -x "$1/config"
+       then
+               echo "$1/config is executable?"
+               return 1
+       fi
+
        bare=$(cd "$1" && git config --bool core.bare)
        worktree=$(cd "$1" && git config core.worktree) ||
        worktree=unset
index 6b3cedcf24613b9c72c67b02c0b731db67055a26..988c3925d5e68be461b252675413671617e88809 100755 (executable)
@@ -64,7 +64,7 @@ test_expect_success "setup case tests" '
        git checkout -f master
 '
 
-$test_case 'rename (case change)' '
+test_expect_success 'rename (case change)' '
        git mv camelcase CamelCase &&
        git commit -m "rename"
 '
index 158cf4f03b63cf4eb61901dd1e178b3d1d08467a..067f4c6e52abe86e952367d8cf576a80aa5139bd 100755 (executable)
@@ -131,7 +131,7 @@ test_expect_success 'second commit has cache-tree' '
        test_cache_tree
 '
 
-test_expect_success 'commit --interactive gives cache-tree on partial commit' '
+test_expect_success PERL 'commit --interactive gives cache-tree on partial commit' '
        cat <<-\EOT >foo.c &&
        int foo()
        {
index 57ea5a10c5c716f52a8c8c7335ac7f68fe9dd92d..d7ef44b4a261a588874df3b8359d809655286a43 100755 (executable)
@@ -289,4 +289,13 @@ test_expect_success 'http paths can be part of context' '
        EOF
 '
 
+test_expect_success 'helpers can abort the process' '
+       test_must_fail git \
+               -c credential.helper="!f() { echo quit=1; }; f" \
+               -c credential.helper="verbatim foo bar" \
+               credential fill >stdout &&
+       >expect &&
+       test_cmp expect stdout
+'
+
 test_done
diff --git a/t/t1014-read-tree-confusing.sh b/t/t1014-read-tree-confusing.sh
new file mode 100755 (executable)
index 0000000..2f5a25d
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='check that read-tree rejects confusing paths'
+. ./test-lib.sh
+
+test_expect_success 'create base tree' '
+       echo content >file &&
+       git add file &&
+       git commit -m base &&
+       blob=$(git rev-parse HEAD:file) &&
+       tree=$(git rev-parse HEAD^{tree})
+'
+
+test_expect_success 'enable core.protectHFS for rejection tests' '
+       git config core.protectHFS true
+'
+
+test_expect_success 'enable core.protectNTFS for rejection tests' '
+       git config core.protectNTFS true
+'
+
+while read path pretty; do
+       : ${pretty:=$path}
+       case "$path" in
+       *SPACE)
+               path="${path%SPACE} "
+               ;;
+       esac
+       test_expect_success "reject $pretty at end of path" '
+               printf "100644 blob %s\t%s" "$blob" "$path" >tree &&
+               bogus=$(git mktree <tree) &&
+               test_must_fail git read-tree $bogus
+       '
+
+       test_expect_success "reject $pretty as subtree" '
+               printf "040000 tree %s\t%s" "$tree" "$path" >tree &&
+               bogus=$(git mktree <tree) &&
+               test_must_fail git read-tree $bogus
+       '
+done <<-EOF
+.
+..
+.git
+.GIT
+${u200c}.Git {u200c}.Git
+.gI${u200c}T .gI{u200c}T
+.GiT${u200c} .GiT{u200c}
+git~1
+.git.SPACE .git.{space}
+.\\\\.GIT\\\\foobar backslashes
+.git\\\\foobar backslashes2
+EOF
+
+test_expect_success 'utf-8 paths allowed with core.protectHFS off' '
+       test_when_finished "git read-tree HEAD" &&
+       test_config core.protectHFS false &&
+       printf "100644 blob %s\t%s" "$blob" ".gi${u200c}t" >tree &&
+       ok=$(git mktree <tree) &&
+       git read-tree $ok
+'
+
+test_done
index 8cf446165eff431106dd86d01754633fbd3ce246..779d4e3829b29d7092d54f6e342f923f0d239b96 100755 (executable)
@@ -287,4 +287,34 @@ test_expect_success 'stale dirs do not cause d/f conflicts (reflogs off)' '
        test_cmp expect actual
 '
 
+# Triggering the bug detected by this test requires a newline to fall
+# exactly BUFSIZ-1 bytes from the end of the file. We don't know
+# what that value is, since it's platform dependent. However, if
+# we choose some value N, we also catch any D which divides N evenly
+# (since we will read backwards in chunks of D). So we choose 8K,
+# which catches glibc (with an 8K BUFSIZ) and *BSD (1K).
+#
+# Each line is 114 characters, so we need 75 to still have a few before the
+# last 8K. The 89-character padding on the final entry lines up our
+# newline exactly.
+test_expect_success 'parsing reverse reflogs at BUFSIZ boundaries' '
+       git checkout -b reflogskip &&
+       z38=00000000000000000000000000000000000000 &&
+       ident="abc <xyz> 0000000001 +0000" &&
+       for i in $(test_seq 1 75); do
+               printf "$z38%02d $z38%02d %s\t" $i $(($i+1)) "$ident" &&
+               if test $i = 75; then
+                       for j in $(test_seq 1 89); do
+                               printf X
+                       done
+               else
+                       printf X
+               fi &&
+               printf "\n"
+       done >.git/logs/refs/heads/reflogskip &&
+       git rev-parse reflogskip@{73} >actual &&
+       echo ${z38}03 >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 019fddd4e81ed7b2103f916a634f3e85e7291017..d00b70f99deffc6a4009fcea2ef387aade68d3e6 100755 (executable)
@@ -309,36 +309,41 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' '
        )
 '
 
-test_expect_success 'fsck notices "." and ".." in trees' '
-       (
-               git init dots &&
-               cd dots &&
-               blob=$(echo foo | git hash-object -w --stdin) &&
-               tab=$(printf "\\t") &&
-               git mktree <<-EOF &&
-               100644 blob $blob$tab.
-               100644 blob $blob$tab..
-               EOF
-               git fsck 2>out &&
-               cat out &&
-               grep "warning.*\\." out
-       )
-'
-
-test_expect_success 'fsck notices ".git" in trees' '
-       (
-               git init dotgit &&
-               cd dotgit &&
-               blob=$(echo foo | git hash-object -w --stdin) &&
-               tab=$(printf "\\t") &&
-               git mktree <<-EOF &&
-               100644 blob $blob$tab.git
-               EOF
-               git fsck 2>out &&
-               cat out &&
-               grep "warning.*\\.git" out
-       )
-'
+while read name path pretty; do
+       while read mode type; do
+               : ${pretty:=$path}
+               test_expect_success "fsck notices $pretty as $type" '
+               (
+                       git init $name-$type &&
+                       cd $name-$type &&
+                       echo content >file &&
+                       git add file &&
+                       git commit -m base &&
+                       blob=$(git rev-parse :file) &&
+                       tree=$(git rev-parse HEAD^{tree}) &&
+                       value=$(eval "echo \$$type") &&
+                       printf "$mode $type %s\t%s" "$value" "$path" >bad &&
+                       bad_tree=$(git mktree <bad) &&
+                       git fsck 2>out &&
+                       cat out &&
+                       grep "warning.*tree $bad_tree" out
+               )'
+       done <<-\EOF
+       100644 blob
+       040000 tree
+       EOF
+done <<-EOF
+dot .
+dotdot ..
+dotgit .git
+dotgit-case .GIT
+dotgit-unicode .gI${u200c}T .gI{u200c}T
+dotgit-case2 .Git
+git-tilde1 git~1
+dotgitdot .git.
+dot-backslash-case .\\\\.GIT\\\\foobar
+dotgit-case-backslash .git\\\\foobar
+EOF
 
 # create a static test repo which is broken by omitting
 # one particular object ($1, which is looked up via rev-parse
index 8e3545d8680c5f5179977082849388b1b31c17d8..f46d0499bc6ea95236f7ca05060b8f8fbbdbbc50 100755 (executable)
@@ -61,4 +61,21 @@ test_expect_success 'do not touch unmerged entries matching $path but not in $tr
        test_cmp expect.next0 actual.next0
 '
 
+test_expect_success 'do not touch files that are already up-to-date' '
+       git reset --hard &&
+       echo one >file1 &&
+       echo two >file2 &&
+       git add file1 file2 &&
+       git commit -m base &&
+       echo modified >file1 &&
+       test-chmtime =1000000000 file2 &&
+       git update-index -q --refresh &&
+       git checkout HEAD -- file1 file2 &&
+       echo one >expect &&
+       test_cmp expect file1 &&
+       echo "1000000000        file2" >expect &&
+       test-chmtime -v +0 file2 >actual &&
+       test_cmp expect actual
+'
+
 test_done
index c286854485d065816ff2c9fdd225c54734d48b60..4d4b02e760d322586eb496d72d0fd6c6b6946a02 100755 (executable)
@@ -12,11 +12,25 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'ls-tree a[a] matches literally' '
-       cat >expected <<EOF &&
-100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391   a[a]/three
-EOF
+       cat >expect <<-\EOF &&
+       100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    a[a]/three
+       EOF
        git ls-tree -r HEAD "a[a]" >actual &&
-       test_cmp expected actual
+       test_cmp expect actual
+'
+
+test_expect_success 'ls-tree outside prefix' '
+       cat >expect <<-\EOF &&
+       100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    ../a[a]/three
+       EOF
+       ( cd aa && git ls-tree -r HEAD "../a[a]"; ) >actual &&
+       test_cmp expect actual
+'
+
+test_expect_failure 'ls-tree does not yet support negated pathspec' '
+       git ls-files ":(exclude)a" "a*" >expect &&
+       git ls-tree --name-only -r HEAD ":(exclude)a" "a*" >actual &&
+       test_cmp expect actual
 '
 
 test_done
index cfd67ff3dfc64b160bbc53318df3eb661970f2e1..245406ab979d63d3588c4e2000b02cc5f24e493b 100755 (executable)
@@ -7,28 +7,22 @@ test_description='Test commit notes'
 
 . ./test-lib.sh
 
-cat > fake_editor.sh << \EOF
-#!/bin/sh
-echo "$MSG" > "$1"
-echo "$MSG" >& 2
+write_script fake_editor <<\EOF
+echo "$MSG" >"$1"
+echo "$MSG" >&2
 EOF
-chmod a+x fake_editor.sh
-GIT_EDITOR=./fake_editor.sh
+GIT_EDITOR=./fake_editor
 export GIT_EDITOR
 
+indent="    "
+
 test_expect_success 'cannot annotate non-existing HEAD' '
        test_must_fail env MSG=3 git notes add
 '
 
-test_expect_success setup '
-       : > a1 &&
-       git add a1 &&
-       test_tick &&
-       git commit -m 1st &&
-       : > a2 &&
-       git add a2 &&
-       test_tick &&
-       git commit -m 2nd
+test_expect_success 'setup' '
+       test_commit 1st &&
+       test_commit 2nd
 '
 
 test_expect_success 'need valid notes ref' '
@@ -50,206 +44,186 @@ test_expect_success 'handle empty notes gracefully' '
 '
 
 test_expect_success 'show non-existent notes entry with %N' '
-       for l in A B
-       do
-               echo "$l"
-       done >expect &&
-       git show -s --format='A%n%NB' >output &&
-       test_cmp expect output
+       test_write_lines A B >expect &&
+       git show -s --format="A%n%NB" >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'create notes' '
-       git config core.notesRef refs/notes/commits &&
        MSG=b4 git notes add &&
-       test ! -f .git/NOTES_EDITMSG &&
-       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
-       test b4 = $(git notes show) &&
+       test_path_is_missing .git/NOTES_EDITMSG &&
+       git ls-tree -r refs/notes/commits >actual &&
+       test_line_count = 1 actual &&
+       test "b4" = "$(git notes show)" &&
        git show HEAD^ &&
        test_must_fail git notes show HEAD^
 '
 
 test_expect_success 'show notes entry with %N' '
-       for l in A b4 B
-       do
-               echo "$l"
-       done >expect &&
-       git show -s --format='A%n%NB' >output &&
-       test_cmp expect output
+       test_write_lines A b4 B >expect &&
+       git show -s --format="A%n%NB" >actual &&
+       test_cmp expect actual
 '
 
-cat >expect <<EOF
-d423f8c refs/notes/commits@{0}: notes: Notes added by 'git notes add'
-EOF
-
 test_expect_success 'create reflog entry' '
-       git reflog show refs/notes/commits >output &&
-       test_cmp expect output
+       cat <<-EOF >expect &&
+               a1d8fa6 refs/notes/commits@{0}: notes: Notes added by '\''git notes add'\''
+       EOF
+       git reflog show refs/notes/commits >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'edit existing notes' '
        MSG=b3 git notes edit &&
-       test ! -f .git/NOTES_EDITMSG &&
-       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
-       test b3 = $(git notes show) &&
+       test_path_is_missing .git/NOTES_EDITMSG &&
+       git ls-tree -r refs/notes/commits >actual &&
+       test_line_count = 1 actual &&
+       test "b3" = "$(git notes show)" &&
        git show HEAD^ &&
        test_must_fail git notes show HEAD^
 '
 
 test_expect_success 'cannot "git notes add -m" where notes already exists' '
        test_must_fail git notes add -m "b2" &&
-       test ! -f .git/NOTES_EDITMSG &&
-       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
-       test b3 = $(git notes show) &&
+       test_path_is_missing .git/NOTES_EDITMSG &&
+       git ls-tree -r refs/notes/commits >actual &&
+       test_line_count = 1 actual &&
+       test "b3" = "$(git notes show)" &&
        git show HEAD^ &&
        test_must_fail git notes show HEAD^
 '
 
 test_expect_success 'can overwrite existing note with "git notes add -f -m"' '
        git notes add -f -m "b1" &&
-       test ! -f .git/NOTES_EDITMSG &&
-       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
-       test b1 = $(git notes show) &&
+       test_path_is_missing .git/NOTES_EDITMSG &&
+       git ls-tree -r refs/notes/commits >actual &&
+       test_line_count = 1 actual &&
+       test "b1" = "$(git notes show)" &&
        git show HEAD^ &&
        test_must_fail git notes show HEAD^
 '
 
 test_expect_success 'add w/no options on existing note morphs into edit' '
        MSG=b2 git notes add &&
-       test ! -f .git/NOTES_EDITMSG &&
-       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
-       test b2 = $(git notes show) &&
+       test_path_is_missing .git/NOTES_EDITMSG &&
+       git ls-tree -r refs/notes/commits >actual &&
+       test_line_count = 1 actual &&
+       test "b2" = "$(git notes show)" &&
        git show HEAD^ &&
        test_must_fail git notes show HEAD^
 '
 
 test_expect_success 'can overwrite existing note with "git notes add -f"' '
        MSG=b1 git notes add -f &&
-       test ! -f .git/NOTES_EDITMSG &&
-       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
-       test b1 = $(git notes show) &&
+       test_path_is_missing .git/NOTES_EDITMSG &&
+       git ls-tree -r refs/notes/commits >actual &&
+       test_line_count = 1 actual &&
+       test "b1" = "$(git notes show)" &&
        git show HEAD^ &&
        test_must_fail git notes show HEAD^
 '
 
-cat > expect << EOF
-commit 268048bfb8a1fb38e703baceb8ab235421bf80c5
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:14:13 2005 -0700
-
-    2nd
+test_expect_success 'show notes' '
+       cat >expect <<-EOF &&
+               commit 7a4ca6ee52a974a66cbaa78e33214535dff1d691
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:14:13 2005 -0700
 
-Notes:
-    b1
-EOF
+               ${indent}2nd
 
-test_expect_success 'show notes' '
+               Notes:
+               ${indent}b1
+       EOF
        ! (git cat-file commit HEAD | grep b1) &&
-       git log -1 > output &&
-       test_cmp expect output
-'
-
-test_expect_success 'create multi-line notes (setup)' '
-       : > a3 &&
-       git add a3 &&
-       test_tick &&
-       git commit -m 3rd &&
-       MSG="b3
-c3c3c3c3
-d3d3d3" git notes add
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
-cat > expect-multiline << EOF
-commit 1584215f1d29c65e99c6c6848626553fdd07fd75
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:15:13 2005 -0700
-
-    3rd
+test_expect_success 'show multi-line notes' '
+       test_commit 3rd &&
+       MSG="b3${LF}c3c3c3c3${LF}d3d3d3" git notes add &&
+       cat >expect-multiline <<-EOF &&
+               commit d07d62e5208f22eb5695e7eb47667dc8b9860290
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:15:13 2005 -0700
 
-Notes:
-    b3
-    c3c3c3c3
-    d3d3d3
-EOF
+               ${indent}3rd
 
-printf "\n" >> expect-multiline
-cat expect >> expect-multiline
+               Notes:
+               ${indent}b3
+               ${indent}c3c3c3c3
+               ${indent}d3d3d3
 
-test_expect_success 'show multi-line notes' '
-       git log -2 > output &&
-       test_cmp expect-multiline output
-'
-test_expect_success 'create -F notes (setup)' '
-       : > a4 &&
-       git add a4 &&
-       test_tick &&
-       git commit -m 4th &&
-       echo "xyzzy" > note5 &&
-       git notes add -F note5
+       EOF
+       cat expect >>expect-multiline &&
+       git log -2 >actual &&
+       test_cmp expect-multiline actual
 '
 
-cat > expect-F << EOF
-commit 15023535574ded8b1a89052b32673f84cf9582b8
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:16:13 2005 -0700
+test_expect_success 'show -F notes' '
+       test_commit 4th &&
+       echo "xyzzy" >note5 &&
+       git notes add -F note5 &&
+       cat >expect-F <<-EOF &&
+               commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:16:13 2005 -0700
 
-    4th
+               ${indent}4th
 
-Notes:
-    xyzzy
-EOF
+               Notes:
+               ${indent}xyzzy
 
-printf "\n" >> expect-F
-cat expect-multiline >> expect-F
-
-test_expect_success 'show -F notes' '
-       git log -3 > output &&
-       test_cmp expect-F output
+       EOF
+       cat expect-multiline >>expect-F &&
+       git log -3 >actual &&
+       test_cmp expect-F actual
 '
 
 test_expect_success 'Re-adding -F notes without -f fails' '
-       echo "zyxxy" > note5 &&
+       echo "zyxxy" >note5 &&
        test_must_fail git notes add -F note5 &&
-       git log -3 > output &&
-       test_cmp expect-F output
+       git log -3 >actual &&
+       test_cmp expect-F actual
 '
 
-cat >expect << EOF
-commit 15023535574ded8b1a89052b32673f84cf9582b8
-tree e070e3af51011e47b183c33adf9736736a525709
-parent 1584215f1d29c65e99c6c6848626553fdd07fd75
-author A U Thor <author@example.com> 1112912173 -0700
-committer C O Mitter <committer@example.com> 1112912173 -0700
-
-    4th
-EOF
 test_expect_success 'git log --pretty=raw does not show notes' '
-       git log -1 --pretty=raw >output &&
-       test_cmp expect output
+       cat >expect <<-EOF &&
+               commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
+               tree 05ac65288c4c4b3b709a020ae94b2ece2f2201ae
+               parent d07d62e5208f22eb5695e7eb47667dc8b9860290
+               author A U Thor <author@example.com> 1112912173 -0700
+               committer C O Mitter <committer@example.com> 1112912173 -0700
+
+               ${indent}4th
+       EOF
+       git log -1 --pretty=raw >actual &&
+       test_cmp expect actual
 '
 
-cat >>expect <<EOF
-
-Notes:
-    xyzzy
-EOF
 test_expect_success 'git log --show-notes' '
-       git log -1 --pretty=raw --show-notes >output &&
-       test_cmp expect output
+       cat >>expect <<-EOF &&
+
+       Notes:
+       ${indent}xyzzy
+       EOF
+       git log -1 --pretty=raw --show-notes >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git log --no-notes' '
-       git log -1 --no-notes >output &&
-       ! grep xyzzy output
+       git log -1 --no-notes >actual &&
+       ! grep xyzzy actual
 '
 
 test_expect_success 'git format-patch does not show notes' '
-       git format-patch -1 --stdout >output &&
-       ! grep xyzzy output
+       git format-patch -1 --stdout >actual &&
+       ! grep xyzzy actual
 '
 
 test_expect_success 'git format-patch --show-notes does show notes' '
-       git format-patch --show-notes -1 --stdout >output &&
-       grep xyzzy output
+       git format-patch --show-notes -1 --stdout >actual &&
+       grep xyzzy actual
 '
 
 for pretty in \
@@ -261,8 +235,8 @@ do
        ?*) p="$pretty" not=" not" negate="!" ;;
        esac
        test_expect_success "git show $pretty does$not show notes" '
-               git show $p >output &&
-               eval "$negate grep xyzzy output"
+               git show $p >actual &&
+               eval "$negate grep xyzzy actual"
        '
 done
 
@@ -271,161 +245,131 @@ test_expect_success 'setup alternate notes ref' '
 '
 
 test_expect_success 'git log --notes shows default notes' '
-       git log -1 --notes >output &&
-       grep xyzzy output &&
-       ! grep alternate output
+       git log -1 --notes >actual &&
+       grep xyzzy actual &&
+       ! grep alternate actual
 '
 
 test_expect_success 'git log --notes=X shows only X' '
-       git log -1 --notes=alternate >output &&
-       ! grep xyzzy output &&
-       grep alternate output
+       git log -1 --notes=alternate >actual &&
+       ! grep xyzzy actual &&
+       grep alternate actual
 '
 
 test_expect_success 'git log --notes --notes=X shows both' '
-       git log -1 --notes --notes=alternate >output &&
-       grep xyzzy output &&
-       grep alternate output
+       git log -1 --notes --notes=alternate >actual &&
+       grep xyzzy actual &&
+       grep alternate actual
 '
 
 test_expect_success 'git log --no-notes resets default state' '
        git log -1 --notes --notes=alternate \
                --no-notes --notes=alternate \
-               >output &&
-       ! grep xyzzy output &&
-       grep alternate output
+               >actual &&
+       ! grep xyzzy actual &&
+       grep alternate actual
 '
 
 test_expect_success 'git log --no-notes resets ref list' '
        git log -1 --notes --notes=alternate \
                --no-notes --notes \
-               >output &&
-       grep xyzzy output &&
-       ! grep alternate output
-'
-
-test_expect_success 'create -m notes (setup)' '
-       : > a5 &&
-       git add a5 &&
-       test_tick &&
-       git commit -m 5th &&
-       git notes add -m spam -m "foo
-bar
-baz"
-'
-
-whitespace="    "
-cat > expect-m << EOF
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:17:13 2005 -0700
-
-    5th
-
-Notes:
-    spam
-$whitespace
-    foo
-    bar
-    baz
-EOF
-
-printf "\n" >> expect-m
-cat expect-F >> expect-m
-
-test_expect_success 'show -m notes' '
-       git log -4 > output &&
-       test_cmp expect-m output
+               >actual &&
+       grep xyzzy actual &&
+       ! grep alternate actual
 '
 
-test_expect_success 'remove note with add -f -F /dev/null (setup)' '
-       git notes add -f -F /dev/null
-'
-
-cat > expect-rm-F << EOF
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:17:13 2005 -0700
-
-    5th
-EOF
-
-printf "\n" >> expect-rm-F
-cat expect-F >> expect-rm-F
-
-test_expect_success 'verify note removal with -F /dev/null' '
-       git log -4 > output &&
-       test_cmp expect-rm-F output &&
+test_expect_success 'show -m notes' '
+       test_commit 5th &&
+       git notes add -m spam -m "foo${LF}bar${LF}baz" &&
+       cat >expect-m <<-EOF &&
+               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:17:13 2005 -0700
+
+               ${indent}5th
+
+               Notes:
+               ${indent}spam
+               ${indent}
+               ${indent}foo
+               ${indent}bar
+               ${indent}baz
+
+       EOF
+       cat expect-F >>expect-m &&
+       git log -4 >actual &&
+       test_cmp expect-m actual
+'
+
+test_expect_success 'remove note with add -f -F /dev/null' '
+       git notes add -f -F /dev/null &&
+       cat >expect-rm-F <<-EOF &&
+               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:17:13 2005 -0700
+
+               ${indent}5th
+
+       EOF
+       cat expect-F >>expect-rm-F &&
+       git log -4 >actual &&
+       test_cmp expect-rm-F actual &&
        test_must_fail git notes show
 '
 
-test_expect_success 'do not create empty note with -m "" (setup)' '
-       git notes add -m ""
-'
-
-test_expect_success 'verify non-creation of note with -m ""' '
-       git log -4 > output &&
-       test_cmp expect-rm-F output &&
+test_expect_success 'do not create empty note with -m ""' '
+       git notes add -m "" &&
+       git log -4 >actual &&
+       test_cmp expect-rm-F actual &&
        test_must_fail git notes show
 '
 
-cat > expect-combine_m_and_F << EOF
-foo
-
-xyzzy
+test_expect_success 'create note with combination of -m and -F' '
+       cat >expect-combine_m_and_F <<-EOF &&
+               foo
 
-bar
+               xyzzy
 
-zyxxy
+               bar
 
-baz
-EOF
+               zyxxy
 
-test_expect_success 'create note with combination of -m and -F' '
-       echo "xyzzy" > note_a &&
-       echo "zyxxy" > note_b &&
+               baz
+       EOF
+       echo "xyzzy" >note_a &&
+       echo "zyxxy" >note_b &&
        git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" &&
-       git notes show > output &&
-       test_cmp expect-combine_m_and_F output
+       git notes show >actual &&
+       test_cmp expect-combine_m_and_F actual
 '
 
-test_expect_success 'remove note with "git notes remove" (setup)' '
+test_expect_success 'remove note with "git notes remove"' '
        git notes remove HEAD^ &&
-       git notes remove
-'
-
-cat > expect-rm-remove << EOF
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:17:13 2005 -0700
+       git notes remove &&
+       cat >expect-rm-remove <<-EOF &&
+               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:17:13 2005 -0700
 
-    5th
+               ${indent}5th
 
-commit 15023535574ded8b1a89052b32673f84cf9582b8
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:16:13 2005 -0700
+               commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:16:13 2005 -0700
 
-    4th
-EOF
-
-printf "\n" >> expect-rm-remove
-cat expect-multiline >> expect-rm-remove
+               ${indent}4th
 
-test_expect_success 'verify note removal with "git notes remove"' '
-       git log -4 > output &&
-       test_cmp expect-rm-remove output &&
+       EOF
+       cat expect-multiline >>expect-rm-remove &&
+       git log -4 >actual &&
+       test_cmp expect-rm-remove actual &&
        test_must_fail git notes show HEAD^
 '
 
-cat > expect << EOF
-c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
-c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
-EOF
-
 test_expect_success 'removing non-existing note should not create new commit' '
-       git rev-parse --verify refs/notes/commits > before_commit &&
+       git rev-parse --verify refs/notes/commits >before_commit &&
        test_must_fail git notes remove HEAD^ &&
-       git rev-parse --verify refs/notes/commits > after_commit &&
+       git rev-parse --verify refs/notes/commits >after_commit &&
        test_cmp before_commit after_commit
 '
 
@@ -505,70 +449,68 @@ test_expect_success 'removing with --stdin --ignore-missing' '
 '
 
 test_expect_success 'list notes with "git notes list"' '
-       git notes list > output &&
-       test_cmp expect output
+       cat >expect <<-EOF &&
+               c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691
+               c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290
+       EOF
+       git notes list >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'list notes with "git notes"' '
-       git notes > output &&
-       test_cmp expect output
+       git notes >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-c18dc024e14f08d18d14eea0d747ff692d66d6a3
-EOF
-
 test_expect_success 'list specific note with "git notes list <object>"' '
-       git notes list HEAD^^ > output &&
-       test_cmp expect output
+       cat >expect <<-EOF &&
+               c18dc024e14f08d18d14eea0d747ff692d66d6a3
+       EOF
+       git notes list HEAD^^ >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-EOF
-
 test_expect_success 'listing non-existing notes fails' '
-       test_must_fail git notes list HEAD > output &&
-       test_cmp expect output
+       cat >expect <<-EOF &&
+       EOF
+       test_must_fail git notes list HEAD >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-Initial set of notes
-
-More notes appended with git notes append
-EOF
-
 test_expect_success 'append to existing note with "git notes append"' '
+       cat >expect <<-EOF &&
+               Initial set of notes
+
+               More notes appended with git notes append
+       EOF
        git notes add -m "Initial set of notes" &&
        git notes append -m "More notes appended with git notes append" &&
-       git notes show > output &&
-       test_cmp expect output
+       git notes show >actual &&
+       test_cmp expect actual
 '
 
-cat > expect_list << EOF
-c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
-c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
-4b6ad22357cc8a1296720574b8d2fbc22fab0671 bd1753200303d0a0344be813e504253b3d98e74d
-EOF
-
 test_expect_success '"git notes list" does not expand to "git notes list HEAD"' '
-       git notes list > output &&
-       test_cmp expect_list output
+       cat >expect_list <<-EOF &&
+               c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691
+               4b6ad22357cc8a1296720574b8d2fbc22fab0671 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290
+       EOF
+       git notes list >actual &&
+       test_cmp expect_list actual
 '
 
 test_expect_success 'appending empty string does not change existing note' '
        git notes append -m "" &&
-       git notes show > output &&
-       test_cmp expect output
+       git notes show >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git notes append == add when there is no existing note' '
        git notes remove HEAD &&
        test_must_fail git notes list HEAD &&
-       git notes append -m "Initial set of notes
-
-More notes appended with git notes append" &&
-       git notes show > output &&
-       test_cmp expect output
+       git notes append -m "Initial set of notes${LF}${LF}More notes appended with git notes append" &&
+       git notes show >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'appending empty string to non-existing note does not create note' '
@@ -579,229 +521,208 @@ test_expect_success 'appending empty string to non-existing note does not create
 '
 
 test_expect_success 'create other note on a different notes ref (setup)' '
-       : > a6 &&
-       git add a6 &&
-       test_tick &&
-       git commit -m 6th &&
-       GIT_NOTES_REF="refs/notes/other" git notes add -m "other note"
-'
+       test_commit 6th &&
+       GIT_NOTES_REF="refs/notes/other" git notes add -m "other note" &&
+       cat >expect-not-other <<-EOF &&
+               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:18:13 2005 -0700
 
-cat > expect-other << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:18:13 2005 -0700
-
-    6th
-
-Notes (other):
-    other note
-EOF
+               ${indent}6th
+       EOF
+       cp expect-not-other expect-other &&
+       cat >>expect-other <<-EOF
 
-cat > expect-not-other << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:18:13 2005 -0700
-
-    6th
-EOF
+               Notes (other):
+               ${indent}other note
+       EOF
+'
 
 test_expect_success 'Do not show note on other ref by default' '
-       git log -1 > output &&
-       test_cmp expect-not-other output
+       git log -1 >actual &&
+       test_cmp expect-not-other actual
 '
 
 test_expect_success 'Do show note when ref is given in GIT_NOTES_REF' '
-       GIT_NOTES_REF="refs/notes/other" git log -1 > output &&
-       test_cmp expect-other output
+       GIT_NOTES_REF="refs/notes/other" git log -1 >actual &&
+       test_cmp expect-other actual
 '
 
 test_expect_success 'Do show note when ref is given in core.notesRef config' '
-       git config core.notesRef "refs/notes/other" &&
-       git log -1 > output &&
-       test_cmp expect-other output
+       test_config core.notesRef "refs/notes/other" &&
+       git log -1 >actual &&
+       test_cmp expect-other actual
 '
 
 test_expect_success 'Do not show note when core.notesRef is overridden' '
-       GIT_NOTES_REF="refs/notes/wrong" git log -1 > output &&
-       test_cmp expect-not-other output
+       test_config core.notesRef "refs/notes/other" &&
+       GIT_NOTES_REF="refs/notes/wrong" git log -1 >actual &&
+       test_cmp expect-not-other actual
 '
 
-cat > expect-both << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:18:13 2005 -0700
-
-    6th
+test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
+       cat >expect-both <<-EOF &&
+               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:18:13 2005 -0700
 
-Notes:
-    order test
+               ${indent}6th
 
-Notes (other):
-    other note
+               Notes:
+               ${indent}order test
 
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:17:13 2005 -0700
+               Notes (other):
+               ${indent}other note
 
-    5th
+               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:17:13 2005 -0700
 
-Notes:
-    replacement for deleted note
-EOF
+               ${indent}5th
 
-test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
+               Notes:
+               ${indent}replacement for deleted note
+       EOF
        GIT_NOTES_REF=refs/notes/commits git notes add \
                -m"replacement for deleted note" HEAD^ &&
        GIT_NOTES_REF=refs/notes/commits git notes add -m"order test" &&
-       git config --unset core.notesRef &&
-       git config notes.displayRef "refs/notes/*" &&
-       git log -2 > output &&
-       test_cmp expect-both output
+       test_unconfig core.notesRef &&
+       test_config notes.displayRef "refs/notes/*" &&
+       git log -2 >actual &&
+       test_cmp expect-both actual
 '
 
 test_expect_success 'core.notesRef is implicitly in notes.displayRef' '
-       git config core.notesRef refs/notes/commits &&
-       git config notes.displayRef refs/notes/other &&
-       git log -2 > output &&
-       test_cmp expect-both output
+       test_config core.notesRef refs/notes/commits &&
+       test_config notes.displayRef refs/notes/other &&
+       git log -2 >actual &&
+       test_cmp expect-both actual
 '
 
 test_expect_success 'notes.displayRef can be given more than once' '
-       git config --unset core.notesRef &&
-       git config notes.displayRef refs/notes/commits &&
+       test_unconfig core.notesRef &&
+       test_config notes.displayRef refs/notes/commits &&
        git config --add notes.displayRef refs/notes/other &&
-       git log -2 > output &&
-       test_cmp expect-both output
+       git log -2 >actual &&
+       test_cmp expect-both actual
 '
 
-cat > expect-both-reversed << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:18:13 2005 -0700
-
-    6th
+test_expect_success 'notes.displayRef respects order' '
+       cat >expect-both-reversed <<-EOF &&
+               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:18:13 2005 -0700
 
-Notes (other):
-    other note
+               ${indent}6th
 
-Notes:
-    order test
-EOF
+               Notes (other):
+               ${indent}other note
 
-test_expect_success 'notes.displayRef respects order' '
-       git config core.notesRef refs/notes/other &&
-       git config --unset-all notes.displayRef &&
-       git config notes.displayRef refs/notes/commits &&
-       git log -1 > output &&
-       test_cmp expect-both-reversed output
+               Notes:
+               ${indent}order test
+       EOF
+       test_config core.notesRef refs/notes/other &&
+       test_config notes.displayRef refs/notes/commits &&
+       git log -1 >actual &&
+       test_cmp expect-both-reversed actual
 '
 
 test_expect_success 'GIT_NOTES_DISPLAY_REF works' '
-       git config --unset-all core.notesRef &&
-       git config --unset-all notes.displayRef &&
        GIT_NOTES_DISPLAY_REF=refs/notes/commits:refs/notes/other \
-               git log -2 > output &&
-       test_cmp expect-both output
+               git log -2 >actual &&
+       test_cmp expect-both actual
 '
 
-cat > expect-none << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:18:13 2005 -0700
-
-    6th
+test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '
+       cat >expect-none <<-EOF &&
+               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:18:13 2005 -0700
 
-commit bd1753200303d0a0344be813e504253b3d98e74d
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:17:13 2005 -0700
+               ${indent}6th
 
-    5th
-EOF
+               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:17:13 2005 -0700
 
-test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '
-       git config notes.displayRef "refs/notes/*" &&
-       GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 > output &&
-       test_cmp expect-none output
+               ${indent}5th
+       EOF
+       test_config notes.displayRef "refs/notes/*" &&
+       GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 >actual &&
+       test_cmp expect-none actual
 '
 
 test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '
-       GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 > output &&
-       test_cmp expect-both output
+       GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 >actual &&
+       test_cmp expect-both actual
 '
 
-cat > expect-commits << EOF
-commit 387a89921c73d7ed72cd94d179c1c7048ca47756
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:18:13 2005 -0700
-
-    6th
+test_expect_success '--no-standard-notes' '
+       cat >expect-commits <<EOF
+               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:18:13 2005 -0700
 
-Notes:
-    order test
-EOF
+               ${indent}6th
 
-test_expect_success '--no-standard-notes' '
-       git log --no-standard-notes --show-notes=commits -1 > output &&
-       test_cmp expect-commits output
+               Notes:
+               ${indent}order test
+       EOF
+       git log --no-standard-notes --show-notes=commits -1 >actual &&
+       test_cmp expect-commits actual
 '
 
 test_expect_success '--standard-notes' '
+       test_config notes.displayRef "refs/notes/*" &&
        git log --no-standard-notes --show-notes=commits \
-               --standard-notes -2 > output &&
-       test_cmp expect-both output
+               --standard-notes -2 >actual &&
+       test_cmp expect-both actual
 '
 
 test_expect_success '--show-notes=ref accumulates' '
        git log --show-notes=other --show-notes=commits \
-                --no-standard-notes -1 > output &&
-       test_cmp expect-both-reversed output
+                --no-standard-notes -1 >actual &&
+       test_cmp expect-both-reversed actual
 '
 
 test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
-       git config core.notesRef refs/notes/other &&
-       echo "Note on a tree" > expect &&
+       test_config core.notesRef refs/notes/other &&
+       echo "Note on a tree" >expect &&
        git notes add -m "Note on a tree" HEAD: &&
-       git notes show HEAD: > actual &&
+       git notes show HEAD: >actual &&
        test_cmp expect actual &&
-       echo "Note on a blob" > expect &&
+       echo "Note on a blob" >expect &&
        filename=$(git ls-tree --name-only HEAD | head -n1) &&
        git notes add -m "Note on a blob" HEAD:$filename &&
-       git notes show HEAD:$filename > actual &&
+       git notes show HEAD:$filename >actual &&
        test_cmp expect actual &&
-       echo "Note on a tag" > expect &&
+       echo "Note on a tag" >expect &&
        git tag -a -m "This is an annotated tag" foobar HEAD^ &&
        git notes add -m "Note on a tag" foobar &&
-       git notes show foobar > actual &&
+       git notes show foobar >actual &&
        test_cmp expect actual
 '
 
-cat > expect << EOF
-commit 2ede89468182a62d0bde2583c736089bcf7d7e92
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:19:13 2005 -0700
-
-    7th
+test_expect_success 'create note from other note with "git notes add -C"' '
+       cat >expect <<-EOF &&
+               commit fb01e0ca8c33b6cc0c6451dde747f97df567cb5c
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:19:13 2005 -0700
 
-Notes (other):
-    other note
-EOF
+               ${indent}7th
 
-test_expect_success 'create note from other note with "git notes add -C"' '
-       : > a7 &&
-       git add a7 &&
-       test_tick &&
-       git commit -m 7th &&
+               Notes:
+               ${indent}order test
+       EOF
+       test_commit 7th &&
        git notes add -C $(git notes list HEAD^) &&
-       git log -1 > actual &&
+       git log -1 >actual &&
        test_cmp expect actual &&
        test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
 '
 
 test_expect_success 'create note from non-existing note with "git notes add -C" fails' '
-       : > a8 &&
-       git add a8 &&
-       test_tick &&
-       git commit -m 8th &&
+       test_commit 8th &&
        test_must_fail git notes add -C deadbeef &&
        test_must_fail git notes list HEAD
 '
@@ -814,405 +735,386 @@ test_expect_success 'create note from non-blob with "git notes add -C" fails' '
        test_must_fail git notes list HEAD
 '
 
-cat > expect << EOF
-commit 80d796defacd5db327b7a4e50099663902fbdc5c
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:20:13 2005 -0700
-
-    8th
+test_expect_success 'create note from blob with "git notes add -C" reuses blob id' '
+       cat >expect <<-EOF &&
+               commit 9a4c31c7f722b5d517e92c64e932dd751e1413bf
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:20:13 2005 -0700
 
-Notes (other):
-    This is a blob object
-EOF
+               ${indent}8th
 
-test_expect_success 'create note from blob with "git notes add -C" reuses blob id' '
+               Notes:
+               ${indent}This is a blob object
+       EOF
        blob=$(echo "This is a blob object" | git hash-object -w --stdin) &&
        git notes add -C $blob &&
-       git log -1 > actual &&
+       git log -1 >actual &&
        test_cmp expect actual &&
        test "$(git notes list HEAD)" = "$blob"
 '
 
-cat > expect << EOF
-commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:21:13 2005 -0700
-
-    9th
+test_expect_success 'create note from other note with "git notes add -c"' '
+       cat >expect <<-EOF &&
+               commit 2e0db4bc649e174d667a1cde19e725cf897a5bd2
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:21:13 2005 -0700
 
-Notes (other):
-    yet another note
-EOF
+               ${indent}9th
 
-test_expect_success 'create note from other note with "git notes add -c"' '
-       : > a9 &&
-       git add a9 &&
-       test_tick &&
-       git commit -m 9th &&
+               Notes:
+               ${indent}yet another note
+       EOF
+       test_commit 9th &&
        MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
-       git log -1 > actual &&
+       git log -1 >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'create note from non-existing note with "git notes add -c" fails' '
-       : > a10 &&
-       git add a10 &&
-       test_tick &&
-       git commit -m 10th &&
+       test_commit 10th &&
        test_must_fail env MSG="yet another note" git notes add -c deadbeef &&
        test_must_fail git notes list HEAD
 '
 
-cat > expect << EOF
-commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:21:13 2005 -0700
-
-    9th
-
-Notes (other):
-    yet another note
-$whitespace
-    yet another note
-EOF
-
 test_expect_success 'append to note from other note with "git notes append -C"' '
+       cat >expect <<-EOF &&
+               commit 2e0db4bc649e174d667a1cde19e725cf897a5bd2
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:21:13 2005 -0700
+
+               ${indent}9th
+
+               Notes:
+               ${indent}yet another note
+               ${indent}
+               ${indent}yet another note
+       EOF
        git notes append -C $(git notes list HEAD^) HEAD^ &&
-       git log -1 HEAD^ > actual &&
+       git log -1 HEAD^ >actual &&
        test_cmp expect actual
 '
 
-cat > expect << EOF
-commit ffed603236bfa3891c49644257a83598afe8ae5a
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:22:13 2005 -0700
+test_expect_success 'create note from other note with "git notes append -c"' '
+       cat >expect <<-EOF &&
+               commit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:22:13 2005 -0700
 
-    10th
+               ${indent}10th
 
-Notes (other):
-    other note
-EOF
-
-test_expect_success 'create note from other note with "git notes append -c"' '
+               Notes:
+               ${indent}other note
+       EOF
        MSG="other note" git notes append -c $(git notes list HEAD^) &&
-       git log -1 > actual &&
+       git log -1 >actual &&
        test_cmp expect actual
 '
 
-cat > expect << EOF
-commit ffed603236bfa3891c49644257a83598afe8ae5a
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:22:13 2005 -0700
-
-    10th
-
-Notes (other):
-    other note
-$whitespace
-    yet another note
-EOF
-
 test_expect_success 'append to note from other note with "git notes append -c"' '
+       cat >expect <<-EOF &&
+               commit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:22:13 2005 -0700
+
+               ${indent}10th
+
+               Notes:
+               ${indent}other note
+               ${indent}
+               ${indent}yet another note
+       EOF
        MSG="yet another note" git notes append -c $(git notes list HEAD) &&
-       git log -1 > actual &&
+       git log -1 >actual &&
        test_cmp expect actual
 '
 
-cat > expect << EOF
-commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:23:13 2005 -0700
-
-    11th
-
-Notes (other):
-    other note
-$whitespace
-    yet another note
-EOF
-
 test_expect_success 'copy note with "git notes copy"' '
-       : > a11 &&
-       git add a11 &&
-       test_tick &&
-       git commit -m 11th &&
+       cat >expect <<-EOF &&
+               commit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:23:13 2005 -0700
+
+               ${indent}11th
+
+               Notes:
+               ${indent}other note
+               ${indent}
+               ${indent}yet another note
+       EOF
+       test_commit 11th &&
        git notes copy HEAD^ HEAD &&
-       git log -1 > actual &&
+       git log -1 >actual &&
        test_cmp expect actual &&
        test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
 '
 
 test_expect_success 'prevent overwrite with "git notes copy"' '
        test_must_fail git notes copy HEAD~2 HEAD &&
-       git log -1 > actual &&
+       git log -1 >actual &&
        test_cmp expect actual &&
        test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
 '
 
-cat > expect << EOF
-commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:23:13 2005 -0700
-
-    11th
-
-Notes (other):
-    yet another note
-$whitespace
-    yet another note
-EOF
-
 test_expect_success 'allow overwrite with "git notes copy -f"' '
+       cat >expect <<-EOF &&
+               commit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:23:13 2005 -0700
+
+               ${indent}11th
+
+               Notes:
+               ${indent}yet another note
+               ${indent}
+               ${indent}yet another note
+       EOF
        git notes copy -f HEAD~2 HEAD &&
-       git log -1 > actual &&
+       git log -1 >actual &&
        test_cmp expect actual &&
        test "$(git notes list HEAD)" = "$(git notes list HEAD~2)"
 '
 
 test_expect_success 'cannot copy note from object without notes' '
-       : > a12 &&
-       git add a12 &&
-       test_tick &&
-       git commit -m 12th &&
-       : > a13 &&
-       git add a13 &&
-       test_tick &&
-       git commit -m 13th &&
+       test_commit 12th &&
+       test_commit 13th &&
        test_must_fail git notes copy HEAD^ HEAD
 '
 
-cat > expect << EOF
-commit e5d4fb5698d564ab8c73551538ecaf2b0c666185
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:25:13 2005 -0700
-
-    13th
+test_expect_success 'git notes copy --stdin' '
+       cat >expect <<-EOF &&
+               commit e871aa61182b1d95d0a6fb75445d891722863b6b
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:25:13 2005 -0700
 
-Notes (other):
-    yet another note
-$whitespace
-    yet another note
+               ${indent}13th
 
-commit 7038787dfe22a14c3867ce816dbba39845359719
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:24:13 2005 -0700
+               Notes:
+               ${indent}yet another note
+               ${indent}
+               ${indent}yet another note
 
-    12th
+               commit 65e263ded02ae4e8839bc151095113737579dc12
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:24:13 2005 -0700
 
-Notes (other):
-    other note
-$whitespace
-    yet another note
-EOF
+               ${indent}12th
 
-test_expect_success 'git notes copy --stdin' '
+               Notes:
+               ${indent}other note
+               ${indent}
+               ${indent}yet another note
+       EOF
        (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
        echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
        git notes copy --stdin &&
-       git log -2 > output &&
-       test_cmp expect output &&
+       git log -2 >actual &&
+       test_cmp expect actual &&
        test "$(git notes list HEAD)" = "$(git notes list HEAD~2)" &&
        test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
 '
 
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:27:13 2005 -0700
-
-    15th
+test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
+       cat >expect <<-EOF &&
+               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:27:13 2005 -0700
 
-commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:26:13 2005 -0700
+               ${indent}15th
 
-    14th
-EOF
+               commit 07c85d77059393ed0154b8c96906547a59dfcddd
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:26:13 2005 -0700
 
-test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
+               ${indent}14th
+       EOF
        test_commit 14th &&
        test_commit 15th &&
        (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
        echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
        git notes copy --for-rewrite=foo &&
-       git log -2 > output &&
-       test_cmp expect output
+       git log -2 >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:27:13 2005 -0700
-
-    15th
-
-Notes (other):
-    yet another note
-$whitespace
-    yet another note
-
-commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:26:13 2005 -0700
-
-    14th
-
-Notes (other):
-    other note
-$whitespace
-    yet another note
-EOF
-
 test_expect_success 'git notes copy --for-rewrite (enabled)' '
-       git config notes.rewriteMode overwrite &&
-       git config notes.rewriteRef "refs/notes/*" &&
+       cat >expect <<-EOF &&
+               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:27:13 2005 -0700
+
+               ${indent}15th
+
+               Notes:
+               ${indent}yet another note
+               ${indent}
+               ${indent}yet another note
+
+               commit 07c85d77059393ed0154b8c96906547a59dfcddd
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:26:13 2005 -0700
+
+               ${indent}14th
+
+               Notes:
+               ${indent}other note
+               ${indent}
+               ${indent}yet another note
+       EOF
+       test_config notes.rewriteMode overwrite &&
+       test_config notes.rewriteRef "refs/notes/*" &&
        (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
        echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
        git notes copy --for-rewrite=foo &&
-       git log -2 > output &&
-       test_cmp expect output
+       git log -2 >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git notes copy --for-rewrite (disabled)' '
-       git config notes.rewrite.bar false &&
+       test_config notes.rewrite.bar false &&
        echo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |
        git notes copy --for-rewrite=bar &&
-       git log -2 > output &&
-       test_cmp expect output
+       git log -2 >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:27:13 2005 -0700
-
-    15th
+test_expect_success 'git notes copy --for-rewrite (overwrite)' '
+       cat >expect <<-EOF &&
+               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:27:13 2005 -0700
 
-Notes (other):
-    a fresh note
-EOF
+               ${indent}15th
 
-test_expect_success 'git notes copy --for-rewrite (overwrite)' '
+               Notes:
+               ${indent}a fresh note
+       EOF
        git notes add -f -m"a fresh note" HEAD^ &&
+       test_config notes.rewriteMode overwrite &&
+       test_config notes.rewriteRef "refs/notes/*" &&
        echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
        git notes copy --for-rewrite=foo &&
-       git log -1 > output &&
-       test_cmp expect output
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git notes copy --for-rewrite (ignore)' '
-       git config notes.rewriteMode ignore &&
+       test_config notes.rewriteMode ignore &&
+       test_config notes.rewriteRef "refs/notes/*" &&
        echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
        git notes copy --for-rewrite=foo &&
-       git log -1 > output &&
-       test_cmp expect output
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:27:13 2005 -0700
-
-    15th
-
-Notes (other):
-    a fresh note
-$whitespace
-    another fresh note
-EOF
-
 test_expect_success 'git notes copy --for-rewrite (append)' '
+       cat >expect <<-EOF &&
+               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:27:13 2005 -0700
+
+               ${indent}15th
+
+               Notes:
+               ${indent}a fresh note
+               ${indent}
+               ${indent}another fresh note
+       EOF
        git notes add -f -m"another fresh note" HEAD^ &&
-       git config notes.rewriteMode concatenate &&
+       test_config notes.rewriteMode concatenate &&
+       test_config notes.rewriteRef "refs/notes/*" &&
        echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
        git notes copy --for-rewrite=foo &&
-       git log -1 > output &&
-       test_cmp expect output
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:27:13 2005 -0700
-
-    15th
-
-Notes (other):
-    a fresh note
-$whitespace
-    another fresh note
-$whitespace
-    append 1
-$whitespace
-    append 2
-EOF
-
 test_expect_success 'git notes copy --for-rewrite (append two to one)' '
+       cat >expect <<-EOF &&
+               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:27:13 2005 -0700
+
+               ${indent}15th
+
+               Notes:
+               ${indent}a fresh note
+               ${indent}
+               ${indent}another fresh note
+               ${indent}
+               ${indent}append 1
+               ${indent}
+               ${indent}append 2
+       EOF
        git notes add -f -m"append 1" HEAD^ &&
        git notes add -f -m"append 2" HEAD^^ &&
+       test_config notes.rewriteMode concatenate &&
+       test_config notes.rewriteRef "refs/notes/*" &&
        (echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
        echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
        git notes copy --for-rewrite=foo &&
-       git log -1 > output &&
-       test_cmp expect output
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git notes copy --for-rewrite (append empty)' '
        git notes remove HEAD^ &&
+       test_config notes.rewriteMode concatenate &&
+       test_config notes.rewriteRef "refs/notes/*" &&
        echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
        git notes copy --for-rewrite=foo &&
-       git log -1 > output &&
-       test_cmp expect output
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:27:13 2005 -0700
-
-    15th
-
-Notes (other):
-    replacement note 1
-EOF
-
 test_expect_success 'GIT_NOTES_REWRITE_MODE works' '
+       cat >expect <<-EOF &&
+               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:27:13 2005 -0700
+
+               ${indent}15th
+
+               Notes:
+               ${indent}replacement note 1
+       EOF
+       test_config notes.rewriteMode concatenate &&
+       test_config notes.rewriteRef "refs/notes/*" &&
        git notes add -f -m"replacement note 1" HEAD^ &&
        echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
        GIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&
-       git log -1 > output &&
-       test_cmp expect output
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
-cat > expect << EOF
-commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:27:13 2005 -0700
+test_expect_success 'GIT_NOTES_REWRITE_REF works' '
+       cat >expect <<-EOF &&
+               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               Author: A U Thor <author@example.com>
+               Date:   Thu Apr 7 15:27:13 2005 -0700
 
-    15th
+               ${indent}15th
 
-Notes (other):
-    replacement note 2
-EOF
-
-test_expect_success 'GIT_NOTES_REWRITE_REF works' '
-       git config notes.rewriteMode overwrite &&
+               Notes:
+               ${indent}replacement note 2
+       EOF
        git notes add -f -m"replacement note 2" HEAD^ &&
-       git config --unset-all notes.rewriteRef &&
+       test_config notes.rewriteMode overwrite &&
+       test_unconfig notes.rewriteRef &&
        echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
        GIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \
                git notes copy --for-rewrite=foo &&
-       git log -1 > output &&
-       test_cmp expect output
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
-       git config notes.rewriteRef refs/notes/other &&
        git notes add -f -m"replacement note 3" HEAD^ &&
+       test_config notes.rewriteMode overwrite &&
+       test_config notes.rewriteRef refs/notes/other &&
        echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
        GIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&
-       git log -1 > output &&
-       test_cmp expect output
+       git log -1 >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'git notes copy diagnoses too many or too few parameters' '
@@ -1221,13 +1123,13 @@ test_expect_success 'git notes copy diagnoses too many or too few parameters' '
 '
 
 test_expect_success 'git notes get-ref (no overrides)' '
-       git config --unset core.notesRef &&
+       test_unconfig core.notesRef &&
        sane_unset GIT_NOTES_REF &&
        test "$(git notes get-ref)" = "refs/notes/commits"
 '
 
 test_expect_success 'git notes get-ref (core.notesRef)' '
-       git config core.notesRef refs/notes/foo &&
+       test_config core.notesRef refs/notes/foo &&
        test "$(git notes get-ref)" = "refs/notes/foo"
 '
 
@@ -1239,4 +1141,51 @@ test_expect_success 'git notes get-ref (--ref)' '
        test "$(GIT_NOTES_REF=refs/notes/bar git notes --ref=baz get-ref)" = "refs/notes/baz"
 '
 
+test_expect_success 'setup testing of empty notes' '
+       test_unconfig core.notesRef &&
+       test_commit 16th &&
+       empty_blob=$(git hash-object -w /dev/null) &&
+       echo "$empty_blob" >expect_empty
+'
+
+while read cmd
+do
+       test_expect_success "'git notes $cmd' removes empty note" "
+               test_might_fail git notes remove HEAD &&
+               MSG= git notes $cmd &&
+               test_must_fail git notes list HEAD
+       "
+
+       test_expect_success "'git notes $cmd --allow-empty' stores empty note" "
+               test_might_fail git notes remove HEAD &&
+               MSG= git notes $cmd --allow-empty &&
+               git notes list HEAD >actual &&
+               test_cmp expect_empty actual
+       "
+done <<\EOF
+add
+add -F /dev/null
+add -m ""
+add -c "$empty_blob"
+add -C "$empty_blob"
+append
+append -F /dev/null
+append -m ""
+append -c "$empty_blob"
+append -C "$empty_blob"
+edit
+EOF
+
+test_expect_success 'empty notes are displayed by git log' '
+       test_commit 17th &&
+       git log -1 >expect &&
+       cat >>expect <<-EOF &&
+
+               Notes:
+       EOF
+       git notes add -C "$empty_blob" --allow-empty &&
+       git log -1 >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 223b98433c502b03c4ba70550cb32d42a5f6295b..7c5ad086264e3d1c140da7fd996f27c1352a26a9 100755 (executable)
@@ -351,19 +351,45 @@ test_expect_success 'commit after failed cherry-pick does not add duplicated -s'
 test_expect_success 'commit after failed cherry-pick adds -s at the right place' '
        pristine_detach initial &&
        test_must_fail git cherry-pick picked &&
+
        git commit -a -s &&
-       pwd &&
-       cat <<EOF > expected &&
-picked
 
-Signed-off-by: C O Mitter <committer@example.com>
+       # Do S-o-b and Conflicts appear in the right order?
+       cat <<-\EOF >expect &&
+       Signed-off-by: C O Mitter <committer@example.com>
+       # Conflicts:
+       EOF
+       grep -e "^# Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+       test_cmp expect actual &&
+
+       cat <<-\EOF >expected &&
+       picked
 
-Conflicts:
-       foo
-EOF
+       Signed-off-by: C O Mitter <committer@example.com>
+       EOF
 
-       git show -s --pretty=format:%B > actual &&
+       git show -s --pretty=format:%B >actual &&
        test_cmp expected actual
 '
 
+test_expect_success 'commit --amend -s places the sign-off at the right place' '
+       pristine_detach initial &&
+       test_must_fail git cherry-pick picked &&
+
+       # emulate old-style conflicts block
+       mv .git/MERGE_MSG .git/MERGE_MSG+ &&
+       sed -e "/^# Conflicts:/,\$s/^# *//" <.git/MERGE_MSG+ >.git/MERGE_MSG &&
+
+       git commit -a &&
+       git commit --amend -s &&
+
+       # Do S-o-b and Conflicts appear in the right order?
+       cat <<-\EOF >expect &&
+       Signed-off-by: C O Mitter <committer@example.com>
+       Conflicts:
+       EOF
+       grep -e "^Conflicts:" -e '^Signed-off-by' <.git/COMMIT_EDITMSG >actual &&
+       test_cmp expect actual
+'
+
 test_done
index fe274e2fb14869314dfb21d563fd2c3f367365c8..f7ff1f555d473ec1e7bd0f32d8d3c29426adc9f0 100755 (executable)
@@ -91,6 +91,13 @@ test_expect_success 'error out when attempting to add ignored ones without -f' '
        ! (git ls-files | grep "\\.ig")
 '
 
+test_expect_success 'error out when attempting to add ignored ones but add others' '
+       touch a.if &&
+       test_must_fail git add a.?? &&
+       ! (git ls-files | grep "\\.ig") &&
+       (git ls-files | grep a.if)
+'
+
 test_expect_success 'add ignored ones with -f' '
        git add -f a.?? &&
        git ls-files --error-unmatch a.ig
@@ -311,7 +318,6 @@ cat >expect.err <<\EOF
 The following paths are ignored by one of your .gitignore files:
 ignored-file
 Use -f if you really want to add them.
-fatal: no files added
 EOF
 cat >expect.out <<\EOF
 add 'track-this'
index 3726a0e2012e534623ddbdd71da04f177c32d4d0..63e423838f1025d74b830bf84629427defdb756a 100755 (executable)
@@ -53,6 +53,14 @@ test_expect_success '256 colors' '
        color "254 bold 255" "[1;38;5;254;48;5;255m"
 '
 
+test_expect_success '"normal" yields no color at all"' '
+       color "normal black" "[40m"
+'
+
+test_expect_success '-1 is a synonym for "normal"' '
+       color "-1 black" "[40m"
+'
+
 test_expect_success 'color too small' '
        invalid_color "-2"
 '
index 5edb79a058f407f47353041afb8e7851b066a517..306e6f39ac88e7ee4ee0fb16dee12cf999fbba1b 100755 (executable)
@@ -85,6 +85,7 @@ test_expect_success setup '
 
        git format-patch --stdout first >patch1 &&
        {
+               echo "Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>" &&
                echo "X-Fake-Field: Line One" &&
                echo "X-Fake-Field: Line Two" &&
                echo "X-Fake-Field: Line Three" &&
@@ -536,4 +537,26 @@ test_expect_success 'am empty-file does not infloop' '
        test_i18ncmp expected actual
 '
 
+test_expect_success 'am --message-id really adds the message id' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout HEAD^ &&
+       git am --message-id patch1.eml &&
+       test_path_is_missing .git/rebase-apply &&
+       git cat-file commit HEAD | tail -n1 >actual &&
+       grep Message-Id patch1.eml >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'am --message-id -s signs off after the message id' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout HEAD^ &&
+       git am -s --message-id patch1.eml &&
+       test_path_is_missing .git/rebase-apply &&
+       git cat-file commit HEAD | tail -n2 | head -n1 >actual &&
+       grep Message-Id patch1.eml >expected &&
+       test_cmp expected actual
+'
+
 test_done
index d01bbdc968a0fcde9eb5410793f8a22108ee6c2d..4b68bbafbe9016d66ce4f96f2058ac67640ed147 100755 (executable)
@@ -101,7 +101,7 @@ test_expect_success \
      ten=0123456789 && hundred=$ten$ten$ten$ten$ten$ten$ten$ten$ten$ten &&
      echo long filename >a/four$hundred &&
      mkdir a/bin &&
-     cp /bin/sh a/bin &&
+     test-genrandom "frotz" 500000 >a/bin/sh &&
      printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
      printf "A not substituted O" >a/substfile2 &&
      if test_have_prereq SYMLINKS; then
index 9e1ad1ca21341d449fa3ab1b896a899e51272127..60df10f46a52ea4ea33eded268beddc13fec29ea 100755 (executable)
@@ -35,6 +35,10 @@ do
                then
                        check_mailinfo $mail --no-inbody-headers
                fi
+               if test -f "$TEST_DIRECTORY"/t5100/msg$mail--message-id
+               then
+                       check_mailinfo $mail --message-id
+               fi
        '
 done
 
diff --git a/t/t5100/info0012--message-id b/t/t5100/info0012--message-id
new file mode 100644 (file)
index 0000000..ac1216f
--- /dev/null
@@ -0,0 +1,5 @@
+Author: Dmitriy Blinov
+Email: bda@mnsspb.ru
+Subject: Изменён список пакетов необходимых для сборки
+Date: Wed, 12 Nov 2008 17:54:41 +0300
+
diff --git a/t/t5100/msg0012--message-id b/t/t5100/msg0012--message-id
new file mode 100644 (file)
index 0000000..376e26e
--- /dev/null
@@ -0,0 +1,8 @@
+textlive-* исправлены на texlive-*
+docutils заменён на python-docutils
+
+Действительно, оказалось, что rest2web вытягивает за собой
+python-docutils. В то время как сам rest2web не нужен.
+
+Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
+Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
diff --git a/t/t5100/patch0012--message-id b/t/t5100/patch0012--message-id
new file mode 100644 (file)
index 0000000..36a0b68
--- /dev/null
@@ -0,0 +1,30 @@
+---
+ howto/build_navy.txt |    6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/howto/build_navy.txt b/howto/build_navy.txt
+index 3fd3afb..0ee807e 100644
+--- a/howto/build_navy.txt
++++ b/howto/build_navy.txt
+@@ -119,8 +119,8 @@
+    - libxv-dev
+    - libusplash-dev
+    - latex-make
+-   - textlive-lang-cyrillic
+-   - textlive-latex-extra
++   - texlive-lang-cyrillic
++   - texlive-latex-extra
+    - dia
+    - python-pyrex
+    - libtool
+@@ -128,7 +128,7 @@
+    - sox
+    - cython
+    - imagemagick
+-   - docutils
++   - python-docutils
+ #. на машине dinar: добавить свой открытый ssh-ключ в authorized_keys2 пользователя ddev
+ #. на своей машине: отредактировать /etc/sudoers (команда ``visudo``) примерно следующим образом::
+-- 
+1.5.6.5
index f4da20aa9bf7df1b2ab1fda207b70e1913277603..85c7fecd22a37d06001398883954180a2b1a9868 100755 (executable)
@@ -1330,4 +1330,108 @@ test_expect_success 'fetch into bare respects core.logallrefupdates' '
        )
 '
 
+test_expect_success 'receive.denyCurrentBranch = updateInstead' '
+       git push testrepo master &&
+       (
+               cd testrepo &&
+               git reset --hard &&
+               git config receive.denyCurrentBranch updateInstead
+       ) &&
+       test_commit third path2 &&
+
+       # Try pushing into a repository with pristine working tree
+       git push testrepo master &&
+       (
+               cd testrepo &&
+               git update-index -q --refresh &&
+               git diff-files --quiet -- &&
+               git diff-index --quiet --cached HEAD -- &&
+               test third = "$(cat path2)" &&
+               test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
+       ) &&
+
+       # Try pushing into a repository with working tree needing a refresh
+       (
+               cd testrepo &&
+               git reset --hard HEAD^ &&
+               test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
+               test-chmtime +100 path1
+       ) &&
+       git push testrepo master &&
+       (
+               cd testrepo &&
+               git update-index -q --refresh &&
+               git diff-files --quiet -- &&
+               git diff-index --quiet --cached HEAD -- &&
+               test_cmp ../path1 path1 &&
+               test third = "$(cat path2)" &&
+               test $(git -C .. rev-parse HEAD) = $(git rev-parse HEAD)
+       ) &&
+
+       # Update what is to be pushed
+       test_commit fourth path2 &&
+
+       # Try pushing into a repository with a dirty working tree
+       # (1) the working tree updated
+       (
+               cd testrepo &&
+               echo changed >path1
+       ) &&
+       test_must_fail git push testrepo master &&
+       (
+               cd testrepo &&
+               test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
+               git diff --quiet --cached &&
+               test changed = "$(cat path1)"
+       ) &&
+
+       # (2) the index updated
+       (
+               cd testrepo &&
+               echo changed >path1 &&
+               git add path1
+       ) &&
+       test_must_fail git push testrepo master &&
+       (
+               cd testrepo &&
+               test $(git -C .. rev-parse HEAD^) = $(git rev-parse HEAD) &&
+               git diff --quiet &&
+               test changed = "$(cat path1)"
+       ) &&
+
+       # Introduce a new file in the update
+       test_commit fifth path3 &&
+
+       # (3) the working tree has an untracked file that would interfere
+       (
+               cd testrepo &&
+               git reset --hard &&
+               echo changed >path3
+       ) &&
+       test_must_fail git push testrepo master &&
+       (
+               cd testrepo &&
+               test $(git -C .. rev-parse HEAD^^) = $(git rev-parse HEAD) &&
+               git diff --quiet &&
+               git diff --quiet --cached &&
+               test changed = "$(cat path3)"
+       ) &&
+
+       # (4) the target changes to what gets pushed but it still is a change
+       (
+               cd testrepo &&
+               git reset --hard &&
+               echo fifth >path3 &&
+               git add path3
+       ) &&
+       test_must_fail git push testrepo master &&
+       (
+               cd testrepo &&
+               test $(git -C .. rev-parse HEAD^^) = $(git rev-parse HEAD) &&
+               git diff --quiet &&
+               test fifth = "$(cat path3)"
+       )
+
+'
+
 test_done
index 6a5ac3add47399918737168e60f75d2d7cec1557..cc7451908baa815467606dae49a36d0df26492dc 100755 (executable)
@@ -26,7 +26,7 @@ check_pushed_commit () {
 # $2 = expected target branch for the push
 # $3 = [optional] repo to check for actual output (repo1 by default)
 test_push_success () {
-       git -c push.default="$1" push &&
+       git ${1:+-c push.default="$1"} push &&
        check_pushed_commit HEAD "$2" "$3"
 }
 
@@ -34,7 +34,7 @@ test_push_success () {
 # check that push fails and does not modify any remote branch
 test_push_failure () {
        git --git-dir=repo1 log --no-walk --format='%h %s' --all >expect &&
-       test_must_fail git -c push.default="$1" push &&
+       test_must_fail git ${1:+-c push.default="$1"} push &&
        git --git-dir=repo1 log --no-walk --format='%h %s' --all >actual &&
        test_cmp expect actual
 }
@@ -172,4 +172,32 @@ test_pushdefault_workflow success simple master triangular
 # master is updated (parent2 does not have foo)
 test_pushdefault_workflow success matching master triangular
 
+# default tests, when no push-default is specified. This
+# should behave the same as "simple" in non-triangular
+# settings, and as "current" otherwise.
+
+test_expect_success 'default behavior allows "simple" push' '
+       test_config branch.master.remote parent1 &&
+       test_config branch.master.merge refs/heads/master &&
+       test_config remote.pushdefault parent1 &&
+       test_commit default-master-master &&
+       test_push_success "" master
+'
+
+test_expect_success 'default behavior rejects non-simple push' '
+       test_config branch.master.remote parent1 &&
+       test_config branch.master.merge refs/heads/foo &&
+       test_config remote.pushdefault parent1 &&
+       test_commit default-master-foo &&
+       test_push_failure ""
+'
+
+test_expect_success 'default triangular behavior acts like "current"' '
+       test_config branch.master.remote parent1 &&
+       test_config branch.master.merge refs/heads/foo &&
+       test_config remote.pushdefault parent2 &&
+       test_commit default-triangular &&
+       test_push_success "" master repo2
+'
+
 test_done
index 1efb88051af0d783dff554337d565547f946d06e..bd0ab4675089b54c336ccc9590c4b65bbd37138d 100755 (executable)
@@ -213,7 +213,7 @@ test_expect_success 'with 2 files arguments' '
 '
 
 test_expect_success 'with message that has comments' '
-       cat basic_message >>message_with_comments &&
+       cat basic_message >message_with_comments &&
        sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
                # comment
 
@@ -232,12 +232,44 @@ test_expect_success 'with message that has comments' '
 
                Reviewed-by: Johan
                Cc: Peff
+               # last comment
+
        EOF
        cat basic_patch >>expected &&
        git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
        test_cmp expected actual
 '
 
+test_expect_success 'with message that has an old style conflict block' '
+       cat basic_message >message_with_comments &&
+       sed -e "s/ Z\$/ /" >>message_with_comments <<-\EOF &&
+               # comment
+
+               # other comment
+               Cc: Z
+               # yet another comment
+               Reviewed-by: Johan
+               Reviewed-by: Z
+               # last comment
+
+               Conflicts:
+
+       EOF
+       cat basic_message >expected &&
+       cat >>expected <<-\EOF &&
+               # comment
+
+               Reviewed-by: Johan
+               Cc: Peff
+               # last comment
+
+               Conflicts:
+
+       EOF
+       git interpret-trailers --trim-empty --trailer "Cc: Peff" message_with_comments >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'with commit complex message and trailer args' '
        cat complex_message_body >expected &&
        sed -e "s/ Z\$/ /" >>expected <<-\EOF &&
index 19a3ced600a20d805d430533b275a8fa4269b835..e5016f457398a0770ebfc0e98d6f60fdfa4be2fc 100755 (executable)
@@ -1298,6 +1298,163 @@ test_expect_success $PREREQ '--8bit-encoding also treats subject' '
        test_cmp expected actual
 '
 
+test_expect_success $PREREQ 'setup expect' '
+cat >email-using-8bit <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: A U Thor <author@example.com>
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Content-Type: text/plain; charset=UTF-8
+Subject: Nothing to see here.
+
+Dieser Betreff enthält auch einen Umlaut!
+EOF
+'
+
+test_expect_success $PREREQ 'sendemail.transferencoding=7bit fails on 8bit data' '
+       clean_fake_sendmail &&
+       git config sendemail.transferEncoding 7bit &&
+       test_must_fail git send-email \
+         --transfer-encoding=7bit \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         email-using-8bit \
+         2>errors >out &&
+       grep "cannot send message as 7bit" errors &&
+       test -z "$(ls msgtxt*)"
+'
+
+test_expect_success $PREREQ '--transfer-encoding overrides sendemail.transferEncoding' '
+       clean_fake_sendmail &&
+       git config sendemail.transferEncoding 8bit
+       test_must_fail git send-email \
+         --transfer-encoding=7bit \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         email-using-8bit \
+         2>errors >out &&
+       grep "cannot send message as 7bit" errors &&
+       test -z "$(ls msgtxt*)"
+'
+
+test_expect_success $PREREQ 'sendemail.transferencoding=8bit' '
+       clean_fake_sendmail &&
+       git send-email \
+         --transfer-encoding=8bit \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         email-using-8bit \
+         2>errors >out &&
+       sed '1,/^$/d' msgtxt1 >actual &&
+       sed '1,/^$/d' email-using-8bit >expected &&
+       test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >expected <<EOF
+Dieser Betreff enth=C3=A4lt auch einen Umlaut!
+EOF
+'
+
+test_expect_success $PREREQ '8-bit and sendemail.transferencoding=quoted-printable' '
+       clean_fake_sendmail &&
+       git send-email \
+         --transfer-encoding=quoted-printable \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         email-using-8bit \
+         2>errors >out &&
+       sed '1,/^$/d' msgtxt1 >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >expected <<EOF
+RGllc2VyIEJldHJlZmYgZW50aMOkbHQgYXVjaCBlaW5lbiBVbWxhdXQhCg==
+EOF
+'
+
+test_expect_success $PREREQ '8-bit and sendemail.transferencoding=base64' '
+       clean_fake_sendmail &&
+       git send-email \
+         --transfer-encoding=base64 \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         email-using-8bit \
+         2>errors >out &&
+       sed '1,/^$/d' msgtxt1 >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >email-using-qp <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: A U Thor <author@example.com>
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+MIME-Version: 1.0
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain; charset=UTF-8
+Subject: Nothing to see here.
+
+Dieser Betreff enth=C3=A4lt auch einen Umlaut!
+EOF
+'
+
+test_expect_success $PREREQ 'convert from quoted-printable to base64' '
+       clean_fake_sendmail &&
+       git send-email \
+         --transfer-encoding=base64 \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         email-using-qp \
+         2>errors >out &&
+       sed '1,/^$/d' msgtxt1 >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' "
+tr -d '\\015' | tr '%' '\\015' > email-using-crlf <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: A U Thor <author@example.com>
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Content-Type: text/plain; charset=UTF-8
+Subject: Nothing to see here.
+
+Look, I have a CRLF and an = sign!%
+EOF
+"
+
+test_expect_success $PREREQ 'setup expect' '
+cat >expected <<EOF
+Look, I have a CRLF and an =3D sign!=0D
+EOF
+'
+
+test_expect_success $PREREQ 'CRLF and sendemail.transferencoding=quoted-printable' '
+       clean_fake_sendmail &&
+       git send-email \
+         --transfer-encoding=quoted-printable \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         email-using-crlf \
+         2>errors >out &&
+       sed '1,/^$/d' msgtxt1 >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success $PREREQ 'setup expect' '
+cat >expected <<EOF
+TG9vaywgSSBoYXZlIGEgQ1JMRiBhbmQgYW4gPSBzaWduIQ0K
+EOF
+'
+
+test_expect_success $PREREQ 'CRLF and sendemail.transferencoding=base64' '
+       clean_fake_sendmail &&
+       git send-email \
+         --transfer-encoding=base64 \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         email-using-crlf \
+         2>errors >out &&
+       sed '1,/^$/d' msgtxt1 >actual &&
+       test_cmp expected actual
+'
+
+
 # Note that the patches in this test are deliberately out of order; we
 # want to make sure it works even if the cover-letter is not in the
 # first mail.
index 52034c8f77abd2eb0079866331c49a2f5f34e20d..c4c3c4954612ff317a72bc342741bee8f3c628b0 100755 (executable)
@@ -16,7 +16,7 @@ test_description='git cvsimport testing for correct patchset estimation'
 
 setup_cvs_test_repository t9603
 
-test_expect_failure 'import with criss cross times on revisions' '
+test_expect_failure PERL 'import with criss cross times on revisions' '
 
     git cvsimport -p"-x" -C module-git module &&
     (cd module-git &&
index 1fd51423ee3d473a969180c29bfe05a6ac9b5b21..a4b3db24bd9afee29de1679b47a5371a10c9863c 100755 (executable)
@@ -5,7 +5,7 @@ test_description='git cvsimport timestamps'
 
 setup_cvs_test_repository t9604
 
-test_expect_success 'check timestamps are UTC (TZ=CST6CDT)' '
+test_expect_success PERL 'check timestamps are UTC (TZ=CST6CDT)' '
 
        TZ=CST6CDT git cvsimport -p"-x" -C module-1 module &&
        git cvsimport -p"-x" -C module-1 module &&
@@ -34,7 +34,7 @@ test_expect_success 'check timestamps are UTC (TZ=CST6CDT)' '
        test_cmp actual-1 expect-1
 '
 
-test_expect_success 'check timestamps with author-specific timezones' '
+test_expect_success PERL 'check timestamps with author-specific timezones' '
 
        cat >cvs-authors <<-EOF &&
        user1=User One <user1@domain.org>
index cf19339ccebd0ee5f55822ebe11386101f358f41..79e8a33d043e7208c2c1a08ab38fad207049ef4c 100644 (file)
@@ -169,7 +169,11 @@ _z40=0000000000000000000000000000000000000000
 LF='
 '
 
-export _x05 _x40 _z40 LF
+# UTF-8 ZERO WIDTH NON-JOINER, which HFS+ ignores
+# when case-folding filenames
+u200c=$(printf '\342\200\214')
+
+export _x05 _x40 _z40 LF u200c
 
 # Each test should start with something like this, after copyright notices:
 #
index a905f5c50cef099c23d8a05acefc279168de69ec..623adeb02d0e06a83d7b17a9b605ff8c6475a0bd 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -2,6 +2,7 @@
 #include "string-list.h"
 #include "run-command.h"
 #include "string-list.h"
+#include "commit.h"
 #include "trailer.h"
 /*
  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
@@ -768,6 +769,22 @@ static int find_trailer_start(struct strbuf **lines, int count)
        return only_spaces ? count : 0;
 }
 
+/* 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;
+}
+
 static int has_blank_line_before(struct strbuf **lines, int start)
 {
        for (;start >= 0; start--) {
@@ -790,14 +807,15 @@ static int process_input_file(struct strbuf **lines,
                              struct trailer_item **in_tok_last)
 {
        int count = 0;
-       int patch_start, trailer_start, i;
+       int patch_start, trailer_start, trailer_end, i;
 
        /* Get the line count */
        while (lines[count])
                count++;
 
        patch_start = find_patch_start(lines, count);
-       trailer_start = find_trailer_start(lines, patch_start);
+       trailer_end = find_trailer_end(lines, patch_start);
+       trailer_start = find_trailer_start(lines, trailer_end);
 
        /* Print lines before the trailers as is */
        print_lines(lines, 0, trailer_start);
@@ -806,14 +824,14 @@ static int process_input_file(struct strbuf **lines,
                printf("\n");
 
        /* Parse trailer lines */
-       for (i = trailer_start; i < patch_start; i++) {
+       for (i = trailer_start; i < trailer_end; i++) {
                if (lines[i]->buf[0] != comment_line_char) {
                        struct trailer_item *new = create_trailer_item(lines[i]->buf);
                        add_trailer_item(in_tok_first, in_tok_last, new);
                }
        }
 
-       return patch_start;
+       return trailer_end;
 }
 
 static void free_all(struct trailer_item **first)
@@ -830,7 +848,7 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai
        struct trailer_item *in_tok_last = NULL;
        struct trailer_item *arg_tok_first;
        struct strbuf **lines;
-       int patch_start;
+       int trailer_end;
 
        /* Default config must be setup first */
        git_config(git_trailer_default_config, NULL);
@@ -839,7 +857,7 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai
        lines = read_input_file(file);
 
        /* Print the lines before the trailers */
-       patch_start = process_input_file(lines, &in_tok_first, &in_tok_last);
+       trailer_end = process_input_file(lines, &in_tok_first, &in_tok_last);
 
        arg_tok_first = process_command_line_args(trailers);
 
@@ -850,7 +868,7 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai
        free_all(&in_tok_first);
 
        /* Print the lines after the trailers as is */
-       print_lines(lines, patch_start, INT_MAX);
+       print_lines(lines, trailer_end, INT_MAX);
 
        strbuf_list_free(lines);
 }
diff --git a/tree.c b/tree.c
index bb02c1caa4ff6a8af40b89498de3af3381272999..58ebfce1bca609b0f7119eb210345c2065d02f40 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -30,9 +30,12 @@ static int read_one_entry_opt(const unsigned char *sha1, const char *base, int b
        return add_cache_entry(ce, opt);
 }
 
-static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, void *context)
+static int read_one_entry(const unsigned char *sha1, struct strbuf *base,
+                         const char *pathname, unsigned mode, int stage,
+                         void *context)
 {
-       return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
+       return read_one_entry_opt(sha1, base->buf, base->len, pathname,
+                                 mode, stage,
                                  ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
 }
 
@@ -40,9 +43,12 @@ static int read_one_entry(const unsigned char *sha1, const char *base, int basel
  * This is used when the caller knows there is no existing entries at
  * the stage that will conflict with the entry being added.
  */
-static int read_one_entry_quick(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, void *context)
+static int read_one_entry_quick(const unsigned char *sha1, struct strbuf *base,
+                               const char *pathname, unsigned mode, int stage,
+                               void *context)
 {
-       return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
+       return read_one_entry_opt(sha1, base->buf, base->len, pathname,
+                                 mode, stage,
                                  ADD_CACHE_JUST_APPEND);
 }
 
@@ -70,7 +76,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
                                continue;
                }
 
-               switch (fn(entry.sha1, base->buf, base->len,
+               switch (fn(entry.sha1, base,
                           entry.path, entry.mode, stage, context)) {
                case 0:
                        continue;
diff --git a/tree.h b/tree.h
index d84ac63e511c30ae4e1c8ee2b9719d67fd9424da..d24125f84f01cddaecbd047b24e3a710014560a4 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -4,6 +4,7 @@
 #include "object.h"
 
 extern const char *tree_type;
+struct strbuf;
 
 struct tree {
        struct object object;
@@ -22,7 +23,7 @@ void free_tree_buffer(struct tree *tree);
 struct tree *parse_tree_indirect(const unsigned char *sha1);
 
 #define READ_TREE_RECURSIVE 1
-typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const char *, unsigned int, int, void *);
+typedef int (*read_tree_fn_t)(const unsigned char *, struct strbuf *, const char *, unsigned int, int, void *);
 
 extern int read_tree_recursive(struct tree *tree,
                               const char *base, int baselen,
index 629c658c46a1b4f4bcd8bbe9770d7fd767ae2216..be84ba2607ad2dbdb17397869c459418abad78e4 100644 (file)
@@ -98,7 +98,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                opts->unpack_rejects[i].strdup_strings = 1;
 }
 
-static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
+static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
                         unsigned int set, unsigned int clear)
 {
        clear |= CE_HASHED;
@@ -107,8 +107,8 @@ static void do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
                set |= CE_WT_REMOVE;
 
        ce->ce_flags = (ce->ce_flags & ~clear) | set;
-       add_index_entry(&o->result, ce,
-                       ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
+       return add_index_entry(&o->result, ce,
+                              ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
 }
 
 static struct cache_entry *dup_entry(const struct cache_entry *ce)
@@ -609,7 +609,9 @@ static int unpack_nondirectories(int n, unsigned long mask,
 
        for (i = 0; i < n; i++)
                if (src[i] && src[i] != o->df_conflict_entry)
-                       do_add_entry(o, src[i], 0, 0);
+                       if (do_add_entry(o, src[i], 0, 0))
+                               return -1;
+
        return 0;
 }
 
@@ -1155,6 +1157,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        if (o->dst_index) {
                discard_index(o->dst_index);
                *o->dst_index = o->result;
+       } else {
+               discard_index(&o->result);
        }
 
 done:
diff --git a/utf8.c b/utf8.c
index 454177794929933a6ccc4d91e7d5c784f0f07bff..9a3f4ad23293bf6a3fc06a1942fd324fa1b74ded 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -561,3 +561,67 @@ int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding)
 
        return chrlen;
 }
+
+/*
+ * Pick the next char from the stream, folding as an HFS+ filename comparison
+ * would. Note that this is _not_ complete by any means. It's just enough
+ * to make is_hfs_dotgit() work, and should not be used otherwise.
+ */
+static ucs_char_t next_hfs_char(const char **in)
+{
+       while (1) {
+               ucs_char_t out = pick_one_utf8_char(in, NULL);
+               /*
+                * check for malformed utf8. Technically this
+                * gets converted to a percent-sequence, but
+                * returning 0 is good enough for is_hfs_dotgit
+                * to realize it cannot be .git
+                */
+               if (!*in)
+                       return 0;
+
+               /* these code points are ignored completely */
+               switch (out) {
+               case 0x200c: /* ZERO WIDTH NON-JOINER */
+               case 0x200d: /* ZERO WIDTH JOINER */
+               case 0x200e: /* LEFT-TO-RIGHT MARK */
+               case 0x200f: /* RIGHT-TO-LEFT MARK */
+               case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
+               case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
+               case 0x202c: /* POP DIRECTIONAL FORMATTING */
+               case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
+               case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
+               case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
+               case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
+               case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
+               case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
+               case 0x206e: /* NATIONAL DIGIT SHAPES */
+               case 0x206f: /* NOMINAL DIGIT SHAPES */
+               case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
+                       continue;
+               }
+
+               /*
+                * there's a great deal of other case-folding that occurs,
+                * but this is enough to catch anything that will convert
+                * to ".git"
+                */
+               return tolower(out);
+       }
+}
+
+int is_hfs_dotgit(const char *path)
+{
+       ucs_char_t c;
+
+       if (next_hfs_char(&path) != '.' ||
+           next_hfs_char(&path) != 'g' ||
+           next_hfs_char(&path) != 'i' ||
+           next_hfs_char(&path) != 't')
+               return 0;
+       c = next_hfs_char(&path);
+       if (c && !is_dir_sep(c))
+               return 0;
+
+       return 1;
+}
diff --git a/utf8.h b/utf8.h
index 65d0e42b96b5a55ecf7362c58d486b9693bcfc46..e4d9183c5fec81f8dbc75d1f2aea136f9b397a72 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -42,4 +42,12 @@ static inline char *reencode_string(const char *in,
 
 int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);
 
+/*
+ * Returns true if the the path would match ".git" after HFS case-folding.
+ * The path should be NUL-terminated, but we will match variants of both ".git\0"
+ * and ".git/..." (but _not_ ".../.git"). This makes it suitable for both fsck
+ * and verify_path().
+ */
+int is_hfs_dotgit(const char *path);
+
 #endif
index cdbc8d798aaff6fff833224ff94b83af49289a5a..b54eac5af6233de183144230259bd317cf24513a 100644 (file)
@@ -726,7 +726,6 @@ static void wt_status_print_changed(struct wt_status *s)
 static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
 {
        struct child_process sm_summary = CHILD_PROCESS_INIT;
-       struct argv_array argv = ARGV_ARRAY_INIT;
        struct strbuf cmd_stdout = STRBUF_INIT;
        struct strbuf summary = STRBUF_INIT;
        char *summary_content;
@@ -735,23 +734,21 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
        argv_array_pushf(&sm_summary.env_array, "GIT_INDEX_FILE=%s",
                         s->index_file);
 
-       argv_array_push(&argv, "submodule");
-       argv_array_push(&argv, "summary");
-       argv_array_push(&argv, uncommitted ? "--files" : "--cached");
-       argv_array_push(&argv, "--for-status");
-       argv_array_push(&argv, "--summary-limit");
-       argv_array_pushf(&argv, "%d", s->submodule_summary);
+       argv_array_push(&sm_summary.args, "submodule");
+       argv_array_push(&sm_summary.args, "summary");
+       argv_array_push(&sm_summary.args, uncommitted ? "--files" : "--cached");
+       argv_array_push(&sm_summary.args, "--for-status");
+       argv_array_push(&sm_summary.args, "--summary-limit");
+       argv_array_pushf(&sm_summary.args, "%d", s->submodule_summary);
        if (!uncommitted)
-               argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD");
+               argv_array_push(&sm_summary.args, s->amend ? "HEAD^" : "HEAD");
 
-       sm_summary.argv = argv.argv;
        sm_summary.git_cmd = 1;
        sm_summary.no_stdin = 1;
        fflush(s->fp);
        sm_summary.out = -1;
 
        run_command(&sm_summary);
-       argv_array_clear(&argv);
 
        len = strbuf_read(&cmd_stdout, sm_summary.out, 1024);