Merge branch 'ok/rebase-i-count-todo'
authorJunio C Hamano <gitster@pobox.com>
Mon, 22 Dec 2014 20:28:06 +0000 (12:28 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 22 Dec 2014 20:28:06 +0000 (12:28 -0800)
* ok/rebase-i-count-todo:
Show number of TODO items for interactive rebase

128 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
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/git-am.txt
Documentation/git-check-ignore.txt
Documentation/git-mailinfo.txt
Documentation/git-push.txt
Documentation/git-send-email.txt
Documentation/git-stripspace.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/gitignore.txt
Documentation/technical/api-credentials.txt
Documentation/technical/api-string-list.txt
Makefile
archive.c
builtin/add.c
builtin/apply.c
builtin/branch.c
builtin/checkout.c
builtin/commit.c
builtin/config.c
builtin/for-each-ref.c
builtin/help.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/ls-tree.c
builtin/mailinfo.c
builtin/merge.c
builtin/push.c
builtin/receive-pack.c
builtin/remote.c
builtin/repack.c
builtin/unpack-objects.c
cache.h
color.c
color.h
commit.c
commit.h
compat/stat.c [new file with mode: 0644]
config.c
config.mak.uname
configure.ac
connect.c
contrib/completion/git-completion.bash
contrib/diff-highlight/README
contrib/diff-highlight/diff-highlight
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
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
pkt-line.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/t0027-auto-crlf.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/t3102-ls-tree-wildcards.sh
t/t3200-branch.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/t5527-fetch-odd-refs.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
test-hashmap.c
trailer.c
tree.c
tree.h
unpack-trees.c
utf8.c
utf8.h
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.
index fb956b2c624141a91c18fdfaa0d5c94e8204505b..880c062117b896f40bc1d2468a2a429b7dc0a44b 100644 (file)
@@ -39,6 +39,10 @@ notes for details).
    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).
@@ -54,3 +58,17 @@ notes for details).
  * "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..6862e3e30155b7139c960ce2f42f5fba96f11886 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
@@ -838,7 +849,13 @@ accepted are `normal`, `black`, `red`, `green`, `yellow`, `blue`,
 `magenta`, `cyan` and `white`; the attributes are `bold`, `dim`, `ul`,
 `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.
+doesn't matter. Attributes may be turned off specifically by prefixing
+them with `no` (e.g., `noreverse`, `noul`, etc).
++
+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).  If your terminal supports it, you may also
+specify 24-bit RGB values as hex, like `#ff0ab3`.
 
 color.diff::
        Whether to use ANSI escape sequences to add color to patches.
@@ -2129,6 +2146,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 +2327,7 @@ sendemail.smtpserverport::
 sendemail.smtpserveroption::
 sendemail.smtpuser::
 sendemail.thread::
+sendemail.transferencoding::
 sendemail.validate::
        See linkgit:git-send-email[1] for description.
 
index 9adce372ec6e6ec0d371c411da1e5002baea5f51..f4eea28dc4d03e02f9af3eedba5d15743b8553f6 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.
@@ -83,7 +94,6 @@ default.   You can use `--no-utf8` to override this.
        it is supposed to apply to and we have those blobs
        available locally.
 
---ignore-date::
 --ignore-space-change::
 --ignore-whitespace::
 --whitespace=<option>::
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 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 929869b0a0576b8c60a685af2fd6b236e3b4bf1d..aff01798cdf6b114009eae8dfc4f2866a8a24d17 100644 (file)
@@ -82,20 +82,18 @@ OPTIONS
         Set the execute permissions on the updated files.
 
 --[no-]assume-unchanged::
-       When these flags are specified, the object names recorded
-       for the paths are not updated.  Instead, these options
-       set and unset the "assume unchanged" bit for the
-       paths.  When the "assume unchanged" bit is on, Git stops
-       checking the working tree files for possible
-       modifications, so you need to manually unset the bit to
-       tell Git when you change the working tree file. This is
+       When this flag is specified, the object names recorded
+       for the paths are not updated.  Instead, this option
+       sets/unsets the "assume unchanged" bit for the
+       paths.  When the "assume unchanged" bit is on, the user
+       promises not to change the file and allows Git to assume
+       that the working tree file matches what is recorded in
+       the index.  If you want to change the working tree file,
+       you need to unset the bit to tell Git.  This is
        sometimes helpful when working with a big project on a
        filesystem that has very slow lstat(2) system call
        (e.g. cifs).
 +
-This option can be also used as a coarse file-level mechanism
-to ignore uncommitted changes in tracked files (akin to what
-`.gitignore` does for untracked files).
 Git will fail (gracefully) in case it needs to modify this file
 in the index e.g. when merging in a commit;
 thus, in case the assumed-untracked file is changed upstream,
index dd172548f31011d1261c3c09323869596d4d6cb7..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],
@@ -908,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 09e82c31bd320a162689a7f2d919de439d05375a..4fd04423e9342e05d34b4a72a4e1af802b8c10c8 100644 (file)
@@ -138,9 +138,6 @@ NOTES
 The purpose of gitignore files is to ensure that certain files
 not tracked by Git remain untracked.
 
-To ignore uncommitted changes in a file that is already tracked,
-use 'git update-index {litdd}assume-unchanged'.
-
 To stop tracking a file that is currently tracked, use
 'git rm --cached'.
 
@@ -203,7 +200,6 @@ everything within `foo/bar`):
 SEE ALSO
 --------
 linkgit:git-rm[1],
-linkgit:git-update-index[1],
 linkgit:gitrepository-layout[5],
 linkgit:git-check-ignore[1]
 
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 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 3b79c5087fbf1157d1bdfa2cccc99547f8abdb09..dc6f0b266c9cf621486b9dba111c2e7b37d3bc1b 100644 (file)
@@ -800,7 +800,7 @@ static int edit_branch_description(const char *branch_name)
 
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
-       int delete = 0, rename = 0, force_create = 0, list = 0;
+       int delete = 0, rename = 0, force = 0, list = 0;
        int verbose = 0, abbrev = -1, detached = 0;
        int reflog = 0, edit_description = 0;
        int quiet = 0, unset_upstream = 0;
@@ -848,7 +848,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")),
                OPT_BOOL(0, "edit-description", &edit_description,
                         N_("edit the description for the branch")),
-               OPT__FORCE(&force_create, N_("force creation (when already exists)")),
+               OPT__FORCE(&force, N_("force creation, move/rename, deletion")),
                {
                        OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
                        N_("commit"), N_("print only not merged branches"),
@@ -891,7 +891,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (with_commit || merge_filter != NO_FILTER)
                list = 1;
 
-       if (!!delete + !!rename + !!force_create + !!new_upstream +
+       if (!!delete + !!rename + !!new_upstream +
            list + unset_upstream > 1)
                usage_with_options(builtin_branch_usage, options);
 
@@ -904,6 +904,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                colopts = 0;
        }
 
+       if (force) {
+               delete *= 2;
+               rename *= 2;
+       }
+
        if (delete) {
                if (!argc)
                        die(_("branch name required"));
@@ -1020,7 +1025,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
                branch_existed = ref_exists(branch->refname);
                create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
-                             force_create, reflog, 0, quiet, track);
+                             force, reflog, 0, quiet, track);
 
                /*
                 * We only show the instructions if the user gave us
index 5a787580368bd3e6410128ff300853a10e4a50ee..52d6cbb0a84e2693fc53c88ca1fb4cd899b26a91 100644 (file)
@@ -62,7 +62,7 @@ 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;
@@ -72,11 +72,11 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
        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);
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 a369f5535351645f15b08efb9d592be4257cdeff..46321176719808dd53a38675dd0d9c558ae336dd 100644 (file)
@@ -447,7 +447,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size,
        if (type == OBJ_BLOB && size > big_file_threshold)
                buf = fixed_buf;
        else
-               buf = xmalloc(size);
+               buf = xmallocz(size);
 
        memset(&stream, 0, sizeof(stream));
        git_inflate_init(&stream);
@@ -552,7 +552,7 @@ static void *unpack_data(struct object_entry *obj,
        git_zstream stream;
        int status;
 
-       data = xmalloc(consume ? 64*1024 : obj->size);
+       data = xmallocz(consume ? 64*1024 : obj->size);
        inbuf = xmalloc((len < 64*1024) ? len : 64*1024);
 
        memset(&stream, 0, sizeof(stream));
index aab44d2e451b1b6a2c764a5d62464ae690ab5566..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)
@@ -256,6 +261,8 @@ static int create_default_files(const char *template_path)
                                !lstat(path, &st2) &&
                                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 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 e908d079bae83884cc218cf6a39d3ed705cf439b..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;
@@ -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 83e91c7382fc913ca7c51e6286cd73cdd91fbeae..3f852f35d1e786b2584d435d640ae830a9f34639 100644 (file)
@@ -376,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);
index 855d94b90ba019ff19832ff49a76d6cc0077e956..ac6667242c562bb7a215982754211a3e035aae8a 100644 (file)
@@ -91,7 +91,7 @@ static void use(int bytes)
 static void *get_data(unsigned long size)
 {
        git_zstream stream;
-       void *buf = xmalloc(size);
+       void *buf = xmallocz(size);
 
        memset(&stream, 0, sizeof(stream));
 
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
diff --git a/color.c b/color.c
index 7941e932d2fb7cb0af5d15dcb2754bc8c96ec657..809b359a42d426d61f0452e89725177eadeb54cb 100644 (file)
--- a/color.c
+++ b/color.c
@@ -26,30 +26,109 @@ const char *column_colors_ansi[] = {
 /* Ignore the RESET at the end when giving the size */
 const int column_colors_ansi_max = ARRAY_SIZE(column_colors_ansi) - 1;
 
-static int parse_color(const char *name, int len)
+/* An individual foreground or background color. */
+struct color {
+       enum {
+               COLOR_UNSPECIFIED = 0,
+               COLOR_NORMAL,
+               COLOR_ANSI, /* basic 0-7 ANSI colors */
+               COLOR_256,
+               COLOR_RGB
+       } type;
+       /* The numeric value for ANSI and 256-color modes */
+       unsigned char value;
+       /* 24-bit RGB color values */
+       unsigned char red, green, blue;
+};
+
+/*
+ * "word" is a buffer of length "len"; does it match the NUL-terminated
+ * "match" exactly?
+ */
+static int match_word(const char *word, int len, const char *match)
 {
+       return !strncasecmp(word, match, len) && !match[len];
+}
+
+static int get_hex_color(const char *in, unsigned char *out)
+{
+       unsigned int val;
+       val = (hexval(in[0]) << 4) | hexval(in[1]);
+       if (val & ~0xff)
+               return -1;
+       *out = val;
+       return 0;
+}
+
+static int parse_color(struct color *out, const char *name, int len)
+{
+       /* Positions in array must match ANSI color codes */
        static const char * const color_names[] = {
-               "normal", "black", "red", "green", "yellow",
+               "black", "red", "green", "yellow",
                "blue", "magenta", "cyan", "white"
        };
        char *end;
        int i;
+       long val;
+
+       /* First try the special word "normal"... */
+       if (match_word(name, len, "normal")) {
+               out->type = COLOR_NORMAL;
+               return 0;
+       }
+
+       /* Try a 24-bit RGB value */
+       if (len == 7 && name[0] == '#') {
+               if (!get_hex_color(name + 1, &out->red) &&
+                   !get_hex_color(name + 3, &out->green) &&
+                   !get_hex_color(name + 5, &out->blue)) {
+                       out->type = COLOR_RGB;
+                       return 0;
+               }
+       }
+
+       /* Then pick from our human-readable color names... */
        for (i = 0; i < ARRAY_SIZE(color_names); i++) {
-               const char *str = color_names[i];
-               if (!strncasecmp(name, str, len) && !str[len])
-                       return i - 1;
+               if (match_word(name, len, color_names[i])) {
+                       out->type = COLOR_ANSI;
+                       out->value = i;
+                       return 0;
+               }
+       }
+
+       /* And finally try a literal 256-color-mode number */
+       val = strtol(name, &end, 10);
+       if (end - name == len) {
+               /*
+                * Allow "-1" as an alias for "normal", but other negative
+                * numbers are bogus.
+                */
+               if (val < -1)
+                       ; /* fall through to error */
+               else if (val < 0) {
+                       out->type = COLOR_NORMAL;
+                       return 0;
+               /* Rewrite low numbers as more-portable standard colors. */
+               } else if (val < 8) {
+                       out->type = COLOR_ANSI;
+                       out->value = val;
+               } else if (val < 256) {
+                       out->type = COLOR_256;
+                       out->value = val;
+                       return 0;
+               }
        }
-       i = strtol(name, &end, 10);
-       if (end - name == len && i >= -1 && i <= 255)
-               return i;
-       return -2;
+
+       return -1;
 }
 
 static int parse_attr(const char *name, int len)
 {
-       static const int attr_values[] = { 1, 2, 4, 5, 7 };
+       static const int attr_values[] = { 1, 2, 4, 5, 7,
+                                          22, 22, 24, 25, 27 };
        static const char * const attr_names[] = {
-               "bold", "dim", "ul", "blink", "reverse"
+               "bold", "dim", "ul", "blink", "reverse",
+               "nobold", "nodim", "noul", "noblink", "noreverse"
        };
        int i;
        for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
@@ -65,13 +144,44 @@ int color_parse(const char *value, char *dst)
        return color_parse_mem(value, strlen(value), dst);
 }
 
+/*
+ * Write the ANSI color codes for "c" to "out"; the string should
+ * already have the ANSI escape code in it. "out" should have enough
+ * space in it to fit any color.
+ */
+static char *color_output(char *out, const struct color *c, char type)
+{
+       switch (c->type) {
+       case COLOR_UNSPECIFIED:
+       case COLOR_NORMAL:
+               break;
+       case COLOR_ANSI:
+               *out++ = type;
+               *out++ = '0' + c->value;
+               break;
+       case COLOR_256:
+               out += sprintf(out, "%c8;5;%d", type, c->value);
+               break;
+       case COLOR_RGB:
+               out += sprintf(out, "%c8;2;%d;%d;%d", type,
+                              c->red, c->green, c->blue);
+               break;
+       }
+       return out;
+}
+
+static int color_empty(const struct color *c)
+{
+       return c->type <= COLOR_NORMAL;
+}
+
 int color_parse_mem(const char *value, int value_len, char *dst)
 {
        const char *ptr = value;
        int len = value_len;
        unsigned int attr = 0;
-       int fg = -2;
-       int bg = -2;
+       struct color fg = { COLOR_UNSPECIFIED };
+       struct color bg = { COLOR_UNSPECIFIED };
 
        if (!strncasecmp(value, "reset", len)) {
                strcpy(dst, GIT_COLOR_RESET);
@@ -81,6 +191,7 @@ int color_parse_mem(const char *value, int value_len, char *dst)
        /* [fg [bg]] [attr]... */
        while (len > 0) {
                const char *word = ptr;
+               struct color c;
                int val, wordlen = 0;
 
                while (len > 0 && !isspace(word[wordlen])) {
@@ -94,14 +205,13 @@ int color_parse_mem(const char *value, int value_len, char *dst)
                        len--;
                }
 
-               val = parse_color(word, wordlen);
-               if (val >= -1) {
-                       if (fg == -2) {
-                               fg = val;
+               if (!parse_color(&c, word, wordlen)) {
+                       if (fg.type == COLOR_UNSPECIFIED) {
+                               fg = c;
                                continue;
                        }
-                       if (bg == -2) {
-                               bg = val;
+                       if (bg.type == COLOR_UNSPECIFIED) {
+                               bg = c;
                                continue;
                        }
                        goto bad;
@@ -113,7 +223,7 @@ int color_parse_mem(const char *value, int value_len, char *dst)
                        goto bad;
        }
 
-       if (attr || fg >= 0 || bg >= 0) {
+       if (attr || !color_empty(&fg) || !color_empty(&bg)) {
                int sep = 0;
                int i;
 
@@ -127,27 +237,19 @@ int color_parse_mem(const char *value, int value_len, char *dst)
                        attr &= ~bit;
                        if (sep++)
                                *dst++ = ';';
-                       *dst++ = '0' + i;
+                       dst += sprintf(dst, "%d", i);
                }
-               if (fg >= 0) {
+               if (!color_empty(&fg)) {
                        if (sep++)
                                *dst++ = ';';
-                       if (fg < 8) {
-                               *dst++ = '3';
-                               *dst++ = '0' + fg;
-                       } else {
-                               dst += sprintf(dst, "38;5;%d", fg);
-                       }
+                       /* foreground colors are all in the 3x range */
+                       dst = color_output(dst, &fg, '3');
                }
-               if (bg >= 0) {
+               if (!color_empty(&bg)) {
                        if (sep++)
                                *dst++ = ';';
-                       if (bg < 8) {
-                               *dst++ = '4';
-                               *dst++ = '0' + bg;
-                       } else {
-                               dst += sprintf(dst, "48;5;%d", bg);
-                       }
+                       /* background colors are all in the 4x range */
+                       dst = color_output(dst, &bg, '4');
                }
                *dst++ = 'm';
        }
diff --git a/color.h b/color.h
index f5beab1ed782549eb08ad11392ed8e6331bdd653..7fe77fb55edac6aa8a284f6c6671c54da79e2591 100644 (file)
--- a/color.h
+++ b/color.h
@@ -8,15 +8,15 @@ struct strbuf;
 /*
  * The maximum length of ANSI color sequence we would generate:
  * - leading ESC '['            2
- * - attr + ';'                 2 * 8 (e.g. "1;")
- * - fg color + ';'             9 (e.g. "38;5;2xx;")
- * - fg color + ';'             9 (e.g. "48;5;2xx;")
+ * - attr + ';'                 3 * 10 (e.g. "1;")
+ * - fg color + ';'             17 (e.g. "38;2;255;255;255;")
+ * - bg color + ';'             17 (e.g. "48;2;255;255;255;")
  * - terminating 'm' NUL        2
  *
  * The above overcounts attr (we only use 5 not 8) and one semicolon
  * but it is close enough.
  */
-#define COLOR_MAXLEN 40
+#define COLOR_MAXLEN 70
 
 /*
  * IMPORTANT: Due to the way these color codes are emulated on Windows,
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);
 
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 ae1398f4f486e65adbeb8e5b82513febf31e6dd2..752e2e227f56edfb7b2ad168798c110c14a2c5e8 100644 (file)
--- a/config.c
+++ b/config.c
@@ -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 cb23bbcf22c8804c9e589dcaf1a42c3340c27782..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;
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 502e03b3058e947835d396540af39c83a45d42aa..836b97a73075a51ef8bc66ba080085a7de4c8f32 100644 (file)
@@ -58,6 +58,47 @@ following in your git configuration:
        diff = diff-highlight | less
 ---------------------------------------------
 
+
+Color Config
+------------
+
+You can configure the highlight colors and attributes using git's
+config. The colors for "old" and "new" lines can be specified
+independently. There are two "modes" of configuration:
+
+  1. You can specify a "highlight" color and a matching "reset" color.
+     This will retain any existing colors in the diff, and apply the
+     "highlight" and "reset" colors before and after the highlighted
+     portion.
+
+  2. You can specify a "normal" color and a "highlight" color. In this
+     case, existing colors are dropped from that line. The non-highlighted
+     bits of the line get the "normal" color, and the highlights get the
+     "highlight" color.
+
+If no "new" colors are specified, they default to the "old" colors. If
+no "old" colors are specified, the default is to reverse the foreground
+and background for highlighted portions.
+
+Examples:
+
+---------------------------------------------
+# Underline highlighted portions
+[color "diff-highlight"]
+oldHighlight = ul
+oldReset = noul
+---------------------------------------------
+
+---------------------------------------------
+# Varying background intensities
+[color "diff-highlight"]
+oldNormal = "black #f8cbcb"
+oldHighlight = "black #ffaaaa"
+newNormal = "black #cbeecb"
+newHighlight = "black #aaffaa"
+---------------------------------------------
+
+
 Bugs
 ----
 
index 69a652e7b2cc4ed2e03ead8aec4b3abeebf20b14..08c88bbc87e51e25cacc5826b26a223f3bfbd382 100755 (executable)
@@ -5,8 +5,18 @@ use strict;
 
 # Highlight by reversing foreground and background. You could do
 # other things like bold or underline if you prefer.
-my $HIGHLIGHT   = "\x1b[7m";
-my $UNHIGHLIGHT = "\x1b[27m";
+my @OLD_HIGHLIGHT = (
+       color_config('color.diff-highlight.oldnormal'),
+       color_config('color.diff-highlight.oldhighlight', "\x1b[7m"),
+       color_config('color.diff-highlight.oldreset', "\x1b[27m")
+);
+my @NEW_HIGHLIGHT = (
+       color_config('color.diff-highlight.newnormal', $OLD_HIGHLIGHT[0]),
+       color_config('color.diff-highlight.newhighlight', $OLD_HIGHLIGHT[1]),
+       color_config('color.diff-highlight.newreset', $OLD_HIGHLIGHT[2])
+);
+
+my $RESET = "\x1b[m";
 my $COLOR = qr/\x1b\[[0-9;]*m/;
 my $BORING = qr/$COLOR|\s/;
 
@@ -57,6 +67,17 @@ show_hunk(\@removed, \@added);
 
 exit 0;
 
+# Ideally we would feed the default as a human-readable color to
+# git-config as the fallback value. But diff-highlight does
+# not otherwise depend on git at all, and there are reports
+# of it being used in other settings. Let's handle our own
+# fallback, which means we will work even if git can't be run.
+sub color_config {
+       my ($key, $default) = @_;
+       my $s = `git config --get-color $key 2>/dev/null`;
+       return length($s) ? $s : $default;
+}
+
 sub show_hunk {
        my ($a, $b) = @_;
 
@@ -132,8 +153,8 @@ sub highlight_pair {
        }
 
        if (is_pair_interesting(\@a, $pa, $sa, \@b, $pb, $sb)) {
-               return highlight_line(\@a, $pa, $sa),
-                      highlight_line(\@b, $pb, $sb);
+               return highlight_line(\@a, $pa, $sa, \@OLD_HIGHLIGHT),
+                      highlight_line(\@b, $pb, $sb, \@NEW_HIGHLIGHT);
        }
        else {
                return join('', @a),
@@ -148,15 +169,30 @@ sub split_line {
 }
 
 sub highlight_line {
-       my ($line, $prefix, $suffix) = @_;
-
-       return join('',
-               @{$line}[0..($prefix-1)],
-               $HIGHLIGHT,
-               @{$line}[$prefix..$suffix],
-               $UNHIGHLIGHT,
-               @{$line}[($suffix+1)..$#$line]
-       );
+       my ($line, $prefix, $suffix, $theme) = @_;
+
+       my $start = join('', @{$line}[0..($prefix-1)]);
+       my $mid = join('', @{$line}[$prefix..$suffix]);
+       my $end = join('', @{$line}[($suffix+1)..$#$line]);
+
+       # If we have a "normal" color specified, then take over the whole line.
+       # Otherwise, we try to just manipulate the highlighted bits.
+       if (defined $theme->[0]) {
+               s/$COLOR//g for ($start, $mid, $end);
+               chomp $end;
+               return join('',
+                       $theme->[0], $start, $RESET,
+                       $theme->[1], $mid, $RESET,
+                       $theme->[0], $end, $RESET,
+                       "\n"
+               );
+       } else {
+               return join('',
+                       $start,
+                       $theme->[1], $mid, $theme->[2],
+                       $end
+               );
+       }
 }
 
 # Pairs are interesting to highlight only if we are going to end up
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..10bcb651516e2c25979f8f5af62f87407289e8e6 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);
 
@@ -423,7 +426,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,
        }
        strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer);
        if (check_refname_format(sb.buf, 0))
-               error_func(&tag->object, FSCK_WARN, "invalid 'tag' name: %s", buffer);
+               error_func(&tag->object, FSCK_WARN, "invalid 'tag' name: %.*s",
+                          (int)(eol - buffer), buffer);
        buffer = eol + 1;
 
        if (!skip_prefix(buffer, "tagger ", &buffer))
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"
        }
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 62bc6e1818f02dea10022e3df30b4129ee1d8fcd..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 */
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 8bc89b1e0c0206647a8f5ee8da7727f304d14b2f..187a2293e7d9a36ba294fe775efcf2b87f4e9233 100644 (file)
@@ -64,44 +64,45 @@ void packet_buf_flush(struct strbuf *buf)
 }
 
 #define hex(a) (hexchar[(a) & 15])
-static char buffer[1000];
-static unsigned format_packet(const char *fmt, va_list args)
+static void format_packet(struct strbuf *out, const char *fmt, va_list args)
 {
        static char hexchar[] = "0123456789abcdef";
-       unsigned n;
+       size_t orig_len, n;
 
-       n = vsnprintf(buffer + 4, sizeof(buffer) - 4, fmt, args);
-       if (n >= sizeof(buffer)-4)
+       orig_len = out->len;
+       strbuf_addstr(out, "0000");
+       strbuf_vaddf(out, fmt, args);
+       n = out->len - orig_len;
+
+       if (n > LARGE_PACKET_MAX)
                die("protocol error: impossibly long line");
-       n += 4;
-       buffer[0] = hex(n >> 12);
-       buffer[1] = hex(n >> 8);
-       buffer[2] = hex(n >> 4);
-       buffer[3] = hex(n);
-       packet_trace(buffer+4, n-4, 1);
-       return n;
+
+       out->buf[orig_len + 0] = hex(n >> 12);
+       out->buf[orig_len + 1] = hex(n >> 8);
+       out->buf[orig_len + 2] = hex(n >> 4);
+       out->buf[orig_len + 3] = hex(n);
+       packet_trace(out->buf + orig_len + 4, n - 4, 1);
 }
 
 void packet_write(int fd, const char *fmt, ...)
 {
+       static struct strbuf buf = STRBUF_INIT;
        va_list args;
-       unsigned n;
 
+       strbuf_reset(&buf);
        va_start(args, fmt);
-       n = format_packet(fmt, args);
+       format_packet(&buf, fmt, args);
        va_end(args);
-       write_or_die(fd, buffer, n);
+       write_or_die(fd, buf.buf, buf.len);
 }
 
 void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
 {
        va_list args;
-       unsigned n;
 
        va_start(args, fmt);
-       n = format_packet(fmt, args);
+       format_packet(buf, fmt, args);
        va_end(args);
-       strbuf_add(buf, buffer, n);
 }
 
 static int get_packet_data(int fd, char **src_buf, size_t *src_size,
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..5fcacc6c41e6c7033367cc9c4cf7c18b33eea286 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1068,8 +1068,10 @@ static const char PACKED_REFS_HEADER[] =
  * Return a pointer to the refname within the line (null-terminated),
  * or NULL if there was a problem.
  */
-static const char *parse_ref_line(char *line, unsigned char *sha1)
+static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
 {
+       const char *ref;
+
        /*
         * 42: the answer to everything.
         *
@@ -1078,22 +1080,23 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
         *  +1 (space in between hex and name)
         *  +1 (newline at the end of the line)
         */
-       int len = strlen(line) - 42;
-
-       if (len <= 0)
+       if (line->len <= 42)
                return NULL;
-       if (get_sha1_hex(line, sha1) < 0)
+
+       if (get_sha1_hex(line->buf, sha1) < 0)
                return NULL;
-       if (!isspace(line[40]))
+       if (!isspace(line->buf[40]))
                return NULL;
-       line += 41;
-       if (isspace(*line))
+
+       ref = line->buf + 41;
+       if (isspace(*ref))
                return NULL;
-       if (line[len] != '\n')
+
+       if (line->buf[line->len - 1] != '\n')
                return NULL;
-       line[len] = 0;
+       line->buf[--line->len] = 0;
 
-       return line;
+       return ref;
 }
 
 /*
@@ -1126,16 +1129,15 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
 static void read_packed_refs(FILE *f, struct ref_dir *dir)
 {
        struct ref_entry *last = NULL;
-       char refline[PATH_MAX];
+       struct strbuf line = STRBUF_INIT;
        enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
 
-       while (fgets(refline, sizeof(refline), f)) {
+       while (strbuf_getwholeline(&line, f, '\n') != EOF) {
                unsigned char sha1[20];
                const char *refname;
-               static const char header[] = "# pack-refs with:";
+               const char *traits;
 
-               if (!strncmp(refline, header, sizeof(header)-1)) {
-                       const char *traits = refline + sizeof(header) - 1;
+               if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
                        if (strstr(traits, " fully-peeled "))
                                peeled = PEELED_FULLY;
                        else if (strstr(traits, " peeled "))
@@ -1144,7 +1146,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        continue;
                }
 
-               refname = parse_ref_line(refline, sha1);
+               refname = parse_ref_line(&line, sha1);
                if (refname) {
                        int flag = REF_ISPACKED;
 
@@ -1160,10 +1162,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        continue;
                }
                if (last &&
-                   refline[0] == '^' &&
-                   strlen(refline) == PEELED_LINE_LENGTH &&
-                   refline[PEELED_LINE_LENGTH - 1] == '\n' &&
-                   !get_sha1_hex(refline + 1, sha1)) {
+                   line.buf[0] == '^' &&
+                   line.len == PEELED_LINE_LENGTH &&
+                   line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
+                   !get_sha1_hex(line.buf + 1, sha1)) {
                        hashcpy(last->u.value.peeled, sha1);
                        /*
                         * Regardless of what the file header said,
@@ -1173,6 +1175,8 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        last->flag |= REF_KNOWS_PEELED;
                }
        }
+
+       strbuf_release(&line);
 }
 
 /*
@@ -2318,6 +2322,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 +2330,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 +2649,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 +2677,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 +3417,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 +3776,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 +3792,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 +3851,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 +3870,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 2a4a6c1226f7cf315510f8bd84b5d6fb8045ee5d..452320df8391580fbc1d7112c45e1758691a0c16 100755 (executable)
@@ -55,16 +55,41 @@ create_gitattributes () {
        esac
 }
 
+check_warning () {
+       case "$1" in
+       LF_CRLF) grep "LF will be replaced by CRLF" $2;;
+       CRLF_LF) grep "CRLF will be replaced by LF" $2;;
+       '')
+               >expect
+               grep "will be replaced by" $2 >actual
+               test_cmp expect actual
+               ;;
+       *) false ;;
+       esac
+}
+
 create_file_in_repo () {
        crlf=$1
        attr=$2
+       lfname=$3
+       crlfname=$4
+       lfmixcrlf=$5
+       lfmixcr=$6
+       crlfnul=$7
        create_gitattributes "$attr" &&
+       pfx=crlf_${crlf}_attr_${attr}
        for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
        do
-               pfx=crlf_${crlf}_attr_${attr}_$f.txt &&
-               cp $f $pfx && git -c core.autocrlf=$crlf add $pfx
+               fname=${pfx}_$f.txt &&
+               cp $f $fname &&
+               git -c core.autocrlf=$crlf add $fname 2>"${pfx}_$f.err"
        done &&
-       git commit -m "core.autocrlf $crlf"
+       git commit -m "core.autocrlf $crlf" &&
+       check_warning "$lfname" ${pfx}_LF.err &&
+       check_warning "$crlfname" ${pfx}_CRLF.err &&
+       check_warning "$lfmixcrlf" ${pfx}_CRLF_mix_LF.err &&
+       check_warning "$lfmixcr" ${pfx}_LF_mix_CR.err &&
+       check_warning "$crlfnul" ${pfx}_CRLF_nul.err
 }
 
 check_files_in_repo () {
@@ -140,22 +165,47 @@ test_expect_success 'setup master' '
 '
 
 
-test_expect_success 'create files' '
-       create_file_in_repo false "" &&
-       create_file_in_repo true  "" &&
-       create_file_in_repo input "" &&
 
-       create_file_in_repo false "auto" &&
-       create_file_in_repo true  "auto" &&
-       create_file_in_repo input "auto" &&
+warn_LF_CRLF="LF will be replaced by CRLF"
+warn_CRLF_LF="CRLF will be replaced by LF"
+
+test_expect_success 'add files empty attr' '
+       create_file_in_repo false ""     ""        ""        ""        ""        "" &&
+       create_file_in_repo true  ""     "LF_CRLF" ""        "LF_CRLF" ""        "" &&
+       create_file_in_repo input ""     ""        "CRLF_LF" "CRLF_LF" ""        ""
+'
+
+test_expect_success 'add files attr=auto' '
+       create_file_in_repo false "auto" ""        "CRLF_LF" "CRLF_LF" ""        "" &&
+       create_file_in_repo true  "auto" "LF_CRLF" ""        "LF_CRLF" ""        "" &&
+       create_file_in_repo input "auto" ""        "CRLF_LF" "CRLF_LF" ""        ""
+'
+
+test_expect_success 'add files attr=text' '
+       create_file_in_repo false "text" ""        "CRLF_LF" "CRLF_LF" ""        "CRLF_LF" &&
+       create_file_in_repo true  "text" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" ""        &&
+       create_file_in_repo input "text" ""        "CRLF_LF" "CRLF_LF" ""        "CRLF_LF"
+'
+
+test_expect_success 'add files attr=-text' '
+       create_file_in_repo false "-text" ""       ""        ""        ""        "" &&
+       create_file_in_repo true  "-text" ""       ""        ""        ""        "" &&
+       create_file_in_repo input "-text" ""       ""        ""        ""        ""
+'
+
+test_expect_success 'add files attr=lf' '
+       create_file_in_repo false "lf"    ""       "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF" &&
+       create_file_in_repo true  "lf"    ""       "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF" &&
+       create_file_in_repo input "lf"    ""       "CRLF_LF" "CRLF_LF"  ""       "CRLF_LF"
+'
 
-       create_file_in_repo false "text" &&
-       create_file_in_repo true  "text" &&
-       create_file_in_repo input "text" &&
+test_expect_success 'add files attr=crlf' '
+       create_file_in_repo false "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" "" &&
+       create_file_in_repo true  "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" "" &&
+       create_file_in_repo input "crlf" "LF_CRLF" ""        "LF_CRLF" "LF_CRLF" ""
+'
 
-       create_file_in_repo false "-text" &&
-       create_file_in_repo true  "-text" &&
-       create_file_in_repo input "-text" &&
+test_expect_success 'create files cleanup' '
        rm -f *.txt &&
        git reset --hard
 '
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..1f04b8aa3ff925f469ffa60ce47f9d5659b9acaa 100755 (executable)
@@ -229,8 +229,12 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '
        echo $tag >.git/refs/tags/wrong &&
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        git fsck --tags 2>out &&
-       grep "invalid .tag. name" out &&
-       grep "expected .tagger. line" out
+
+       cat >expect <<-EOF &&
+       warning in tag $tag: invalid '\''tag'\'' name: wrong name format
+       warning in tag $tag: invalid format - expected '\''tagger'\'' line
+       EOF
+       test_cmp expect out
 '
 
 test_expect_success 'tag with bad tagger' '
@@ -309,36 +313,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 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 432921b6b81684313c6b0041a288749c30fdee8e..ddea49808d063f6c169cf8078f5698e34c2ba803 100755 (executable)
@@ -97,6 +97,20 @@ test_expect_success 'git branch -m o/o o should fail when o/p exists' '
        test_must_fail git branch -m o/o o
 '
 
+test_expect_success 'git branch -m o/q o/p should fail when o/p exists' '
+       git branch o/q &&
+       test_must_fail git branch -m o/q o/p
+'
+
+test_expect_success 'git branch -M o/q o/p should work when o/p exists' '
+       git branch -M o/q o/p
+'
+
+test_expect_success 'git branch -m -f o/q o/p should work when o/p exists' '
+       git branch o/q &&
+       git branch -m -f o/q o/p
+'
+
 test_expect_success 'git branch -m q r/q should fail when r exists' '
        git branch q &&
        git branch r &&
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..267c43bd95c9b7dbbc5991e7312deeb5527a175a 100755 (executable)
@@ -45,14 +45,37 @@ test_expect_success 'fg bg attr...' '
        color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
 '
 
+# note that nobold and nodim are the same code (22)
+test_expect_success 'attr negation' '
+       color "nobold nodim noul noblink noreverse" "[22;24;25;27m"
+'
+
 test_expect_success 'long color specification' '
        color "254 255 bold dim ul blink reverse" "[1;2;4;5;7;38;5;254;48;5;255m"
 '
 
+test_expect_success 'absurdly long color specification' '
+       color \
+         "#ffffff #ffffff bold nobold dim nodim ul noul blink noblink reverse noreverse" \
+         "[1;2;4;5;7;22;24;25;27;38;2;255;255;255;48;2;255;255;255m"
+'
+
 test_expect_success '256 colors' '
        color "254 bold 255" "[1;38;5;254;48;5;255m"
 '
 
+test_expect_success '24-bit colors' '
+       color "#ff00ff black" "[38;2;255;0;255;40m"
+'
+
+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 edea9f957e4034098faeb51dec59e9661af8e29e..207899a99faa4ff8fb4d7226e930ba1e9803dc74 100755 (executable)
@@ -26,4 +26,37 @@ test_expect_success 'suffix ref is ignored during fetch' '
        test_cmp expect actual
 '
 
+test_expect_success 'try to create repo with absurdly long refname' '
+       ref240=$_z40/$_z40/$_z40/$_z40/$_z40/$_z40 &&
+       ref1440=$ref240/$ref240/$ref240/$ref240/$ref240/$ref240 &&
+       git init long &&
+       (
+               cd long &&
+               test_commit long &&
+               test_commit master
+       ) &&
+       if git -C long update-ref refs/heads/$ref1440 long; then
+               test_set_prereq LONG_REF
+       else
+               echo >&2 "long refs not supported"
+       fi
+'
+
+test_expect_success LONG_REF 'fetch handles extremely long refname' '
+       git fetch long refs/heads/*:refs/remotes/long/* &&
+       cat >expect <<-\EOF &&
+       long
+       master
+       EOF
+       git for-each-ref --format="%(subject)" refs/remotes/long >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success LONG_REF 'push handles extremely long refname' '
+       git push long :refs/heads/$ref1440 &&
+       git -C long for-each-ref --format="%(subject)" refs/heads >actual &&
+       echo master >expect &&
+       test_cmp expect actual
+'
+
 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 07aa7ecdeede64477b424620425826b380bf895d..cc2891dd971edfa70733eb327d12ccb66fd09f3e 100644 (file)
@@ -47,7 +47,7 @@ static struct test_entry *alloc_test_entry(int hash, char *key, int klen,
 
 static unsigned int hash(unsigned int method, unsigned int i, const char *key)
 {
-       unsigned int hash;
+       unsigned int hash = 0;
        switch (method & 3)
        {
        case HASH_METHOD_FNV:
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