Merge branch 'sg/gpg-sig'
authorJunio C Hamano <gitster@pobox.com>
Fri, 5 Apr 2013 21:15:16 +0000 (14:15 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 5 Apr 2013 21:15:16 +0000 (14:15 -0700)
Teach "merge/pull" to optionally verify and reject commits that are
not signed properly.

* sg/gpg-sig:
pretty printing: extend %G? to include 'N' and 'U'
merge/pull Check for untrusted good GPG signatures
merge/pull: verify GPG signatures of commits being merged
commit.c/GPG signature verification: Also look at the first GPG status line
Move commit GPG signature verification to commit.c

117 files changed:
.mailmap
Documentation/CodingGuidelines
Documentation/RelNotes/1.8.1.6.txt [new file with mode: 0644]
Documentation/RelNotes/1.8.2.1.txt
Documentation/RelNotes/1.8.3.txt
Documentation/config.txt
Documentation/git-commit-tree.txt
Documentation/git-difftool.txt
Documentation/git-merge.txt
Documentation/git-tag.txt
Documentation/merge-options.txt
Documentation/revisions.txt
Makefile
attr.c
builtin/apply.c
builtin/archive.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/fetch-pack.c
builtin/index-pack.c
builtin/log.c
builtin/merge.c
builtin/receive-pack.c
builtin/send-pack.c
builtin/upload-archive.c
cache.h
compat/cygwin.c
compat/cygwin.h
config.mak.in
connect.c
contrib/completion/git-prompt.sh
contrib/mw-to-git/git-remote-mediawiki.txt
daemon.c
dir.c
entry.c
fast-import.c
fetch-pack.c
git-am.sh
git-compat-util.h
git-difftool.perl
git-send-email.perl
git-submodule.sh
git-web--browse.sh
http-backend.c
http.c
log-tree.c
match-trees.c
name-hash.c
path.c
pkt-line.c
pkt-line.h
read-cache.c
remote-curl.c
remote.c
resolve-undo.c
resolve-undo.h
revision.c
revision.h
send-pack.c
sequencer.c
sequencer.h
sha1_file.c
sha1_name.c
sideband.c
sideband.h
streaming.c
submodule.c
t/README
t/t1060-object-corruption.sh [new file with mode: 0755]
t/t1300-repo-config.sh
t/t2003-checkout-cache-mkdir.sh
t/t2022-checkout-paths.sh
t/t3200-branch.sh
t/t3203-branch-output.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3511-cherry-pick-x.sh [new file with mode: 0755]
t/t4014-format-patch.sh
t/t4018-diff-funcname.sh
t/t4034-diff-words.sh
t/t4124-apply-ws-rule.sh
t/t4150-am.sh
t/t4202-log.sh
t/t5002-archive-attr-pattern.sh
t/t5503-tagfollow.sh
t/t5516-fetch-push.sh
t/t5520-pull.sh
t/t5541-http-push.sh
t/t5700-clone-reference.sh
t/t5710-info-alternate.sh
t/t6009-rev-list-parent.sh
t/t6012-rev-list-simplify.sh
t/t6030-bisect-porcelain.sh
t/t6200-fmt-merge-msg.sh
t/t7062-wtstatus-ignorecase.sh [new file with mode: 0755]
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7500-commit.sh
t/t7502-commit.sh
t/t7508-status.sh
t/t7512-status-help.sh
t/t7600-merge.sh
t/t7800-difftool.sh
t/t7811-grep-open.sh
t/t9500-gitweb-standalone-no-errors.sh
t/test-lib-functions.sh
t/test-lib.sh
t/valgrind/valgrind.sh
transport.c
upload-pack.c
write_or_die.c
wt-status.c
wt-status.h
index c7e86183001a00ad2105765708b5b59852ef6640..48d7acf013c611d4885cddffdf516bb5d0ea5a86 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -73,6 +73,7 @@ Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
 <nico@fluxnic.net> <nico@cam.org>
 Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se>
 Peter Krefting <peter@softwolves.pp.se> <peter@softwolves.pp.se>
+Petr Baudis <pasky@ucw.cz> <pasky@suse.cz>
 Philippe Bruhat <book@cpan.org>
 Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com>
 Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
index b1bfff630fc0ec22d68757ebab84e76ee5abbc2c..7e4d5716a62bf59e8f072163ebac3064dad323ae 100644 (file)
@@ -237,6 +237,9 @@ For Python scripts:
 
 Writing Documentation:
 
+ Most (if not all) of the documentation pages are written in AsciiDoc
+ and processed into HTML output and manpages.
+
  Every user-visible change should be reflected in the documentation.
  The same general rule as for code applies -- imitate the existing
  conventions.  A few commented examples follow to provide reference
diff --git a/Documentation/RelNotes/1.8.1.6.txt b/Documentation/RelNotes/1.8.1.6.txt
new file mode 100644 (file)
index 0000000..d9de639
--- /dev/null
@@ -0,0 +1,34 @@
+Git 1.8.1.6 Release Notes
+=========================
+
+Fixes since v1.8.1.5
+--------------------
+
+ * The code to keep track of what directory names are known to Git on
+   platforms with case insensitive filesystems can get confused upon a
+   hash collision between these pathnames and looped forever.
+
+ * When the "--prefix" option is used to "checkout-index", the code
+   did not pick the correct output filter based on the attribute
+   setting.
+
+ * Annotated tags outside refs/tags/ hierarchy were not advertised
+   correctly to the ls-remote and fetch with recent version of Git.
+
+ * The logic used by "git diff -M --stat" to shorten the names of
+   files before and after a rename did not work correctly when the
+   common prefix and suffix between the two filenames overlapped.
+
+ * "git update-index -h" did not do the usual "-h(elp)" thing.
+
+ * perl/Git.pm::cat_blob slurped everything in core only to write it
+   out to a file descriptor, which was not a very smart thing to do.
+
+ * The SSL peer verification done by "git imap-send" did not ask for
+   Server Name Indication (RFC 4366), failing to connect SSL/TLS
+   sites that serve multiple hostnames on a single IP.
+
+ * "git bundle verify" did not say "records a complete history" for a
+   bundle that does not have any prerequisites.
+
+Also contains various documentation fixes.
index b4cf3cccde57d5dff014fb036e8dbf072554f93f..1ded500fc34a9d4da164e74c2ff433dc764d7e57 100644 (file)
@@ -4,6 +4,45 @@ Git v1.8.2.1 Release Notes
 Fixes since v1.8.2
 ------------------
 
+ * Verification of signed tags were not done correctly when not in C
+   or en/US locale.
+
+ * 'git commit -m "$msg"' used to add an extra newline even when
+   $msg already ended with one.
+
+ * The "--match=<pattern>" option of "git describe", when used with
+   "--all" to allow refs that are not annotated tags to be used as a
+   base of description, did not restrict the output from the command
+   to those that match the given pattern.
+
+ * An aliased command spawned from a bare repository that does not say
+   it is bare with "core.bare = yes" is treated as non-bare by mistake.
+
+ * When "format-patch" quoted a non-ascii strings on the header files,
+   it incorrectly applied rfc2047 and chopped a single character in
+   the middle of it.
+
+ * "git archive" reports a failure when asked to create an archive out
+   of an empty tree.  It would be more intuitive to give an empty
+   archive back in such a case.
+
+ * "git tag -f <tag>" always said "Updated tag '<tag>'" even when
+   creating a new tag (i.e. not overwriting nor updating).
+
+ * "git cmd -- ':(top'" was not diagnosed as an invalid syntax, and
+   instead the parser kept reading beyond the end of the string.
+
+ * Annotated tags outside refs/tags/ hierarchy were not advertised
+   correctly to the ls-remote and fetch with recent version of Git.
+
+ * The code to keep track of what directory names are known to Git on
+   platforms with case insensitive filesystems can get confused upon a
+   hash collision between these pathnames and looped forever.
+
+ * The logic used by "git diff -M --stat" to shorten the names of
+   files before and after a rename did not work correctly when the
+   common prefix and suffix between the two filenames overlapped.
+
  * "git submodule update", when recursed into sub-submodules, did not
    acccumulate the prefix paths.
 
index b027992d82c81f304336fdb783d62b833ff5eb95..822c0596a93bc43890aee8782f3a161020256652 100644 (file)
@@ -37,9 +37,17 @@ UI, Workflows & Features
    An explicit way to help the end users who connect to the service by
    issuing custom messages to refuse such an access has been added.
 
- * "git status" suggests users to look into using--untracked=no option
+ * In addition to the case where the user edits the log message with
+   the "e)dit" option of "am -i", replace the "Applying: this patch"
+   message with the final log message contents after applymsg hook
+   munges it.
+
+ * "git status" suggests users to look into using --untracked=no option
    when it takes too long.
 
+ * "git status" shows a bit more information to "git status" during a
+   rebase/bisect session.
+
  * "git fetch" learned to fetch a commit at the tip of an unadvertised
    ref by specifying a raw object name from the command line when the
    server side supports this feature.
@@ -67,6 +75,23 @@ UI, Workflows & Features
    shown on the left side, which is the opposite from other backend
    expects.
 
+ * "show/log" now honors gpg.program configuration just like other
+   parts of the code that use GnuPG.
+
+ * "git log" that shows the difference between the parent and the
+   child has been optimized somewhat.
+
+ * "git difftool" allows the user to write into the temporary files
+   being shown; if the user makes changes to the working tree at the
+   same time, one of the changes has to be lost in such a case, but it
+   tells the user what happened and refrains from overwriting the copy
+   in the working tree.
+
+ * There was no good way to ask "I have a random string that came from
+   outside world. I want to turn it into a 40-hex object name while
+   making sure such an object exists".  A new peeling suffix ^{object}
+   can be used for that purpose, together with "rev-parse --verify".
+
 
 Performance, Internal Implementation, etc.
 
@@ -80,6 +105,21 @@ Performance, Internal Implementation, etc.
    necessary for operations such as "git checkout -", was cumbersome
    to use correctly and also inefficient.
 
+ * Codepaths that inspect log-message-to-be and decide when to add a
+   new Signed-off-by line in various commands have been consolidated.
+
+ * The pkt-line API, implementation and its callers have been cleaned
+   up to make them more robust.
+
+ * Cygwin port has a faster-but-lying lstat(2) emulation whose
+   incorrectness does not matter in practice except for a few
+   codepaths, and setting permission bits to directories is a codepath
+   that needs to use a more correct one.
+
+ * "git checkout" had repeated pathspec matches on the same paths,
+   which have been consolidated.  Also a bug in "git checkout dir/"
+   that is started from an unmerged index has been fixed.
+
 
 Also contains minor documentation updates and code clean-ups.
 
@@ -91,20 +131,45 @@ Unless otherwise noted, all the fixes since v1.8.2 in the maintenance
 track are contained in this release (see release notes to them for
 details).
 
+ * Fix 1.8.1.x regression that stopped matching "dir" (without
+   trailing slash) to a directory "dir".
+   (merge efa5f82 jc/directory-attrs-regression-fix later to maint-1.8.1).
+
+ * "git apply --whitespace=fix" was not prepared to see a line getting
+   longer after fixing whitespaces (e.g. tab-in-indent aka Python).
+   (merge 329b26e jc/apply-ws-fix-tab-in-indent later to maint-1.8.1).
+
+ * The prompt string generator (in contrib/completion/) did not notice
+   when we are in a middle of a "git revert" session.
+   (merge 3ee4452 rr/prompt-revert-head later to maint).
+
+ * "submodule summary --summary-limit" option did not support
+   "--option=value" form.
+   (merge 862ae6c rs/submodule-summary-limit later to maint).
+
+ * "index-pack --fix-thin" used uninitialize value to compute delta
+   depths of objects it appends to the resulting pack.
+   (merge 57165db jk/index-pack-correct-depth-fix later to maint).
+
+ * "index-pack --verify-stat" used a few counters outside protection
+   of mutex, possibly showing incorrect numbers.
+   (merge 8f82aad nd/index-pack-threaded-fixes later to maint).
+
+ * The code to keep track of what directory names are known to Git on
+   platforms with case insensitive filesystems can get confused upon a
+   hash collision between these pathnames and looped forever.
+
  * Annotated tags outside refs/tags/ hierarchy were not advertised
    correctly to the ls-remote and fetch with recent version of Git.
-   (merge c29c46f jk/fully-peeled-packed-ref later to maint).
 
  * Recent optimization broke shallow clones.
    (merge f59de5d jk/peel-ref later to maint).
 
  * "git cmd -- ':(top'" was not diagnosed as an invalid syntax, and
    instead the parser kept reading beyond the end of the string.
-   (merge f612a67 lf/setup-prefix-pathspec later to maint).
 
  * "git tag -f <tag>" always said "Updated tag '<tag>'" even when
    creating a new tag (i.e. not overwriting nor updating).
-   (merge 3ae851e ph/tag-force-no-warn-on-creation later to maint).
 
  * "git p4" did not behave well when the path to the root of the P4
    client was not its real path.
@@ -113,16 +178,13 @@ details).
  * "git archive" reports a failure when asked to create an archive out
    of an empty tree.  It would be more intuitive to give an empty
    archive back in such a case.
-   (merge bd54cf1 jk/empty-archive later to maint).
 
  * When "format-patch" quoted a non-ascii strings on the header files,
    it incorrectly applied rfc2047 and chopped a single character in
    the middle of it.
-   (merge 6cd3c05 ks/rfc2047-one-char-at-a-time later to maint).
 
  * An aliased command spawned from a bare repository that does not say
    it is bare with "core.bare = yes" is treated as non-bare by mistake.
-   (merge 2cd83d1 jk/alias-in-bare later to maint).
 
  * In "git reflog expire", REACHABLE bit was not cleared from the
    correct objects.
@@ -130,13 +192,11 @@ details).
  * The logic used by "git diff -M --stat" to shorten the names of
    files before and after a rename did not work correctly when the
    common prefix and suffix between the two filenames overlapped.
-   (merge b174eb4 ap/maint-diff-rename-avoid-overlap later to maint).
 
  * The "--match=<pattern>" option of "git describe", when used with
    "--all" to allow refs that are not annotated tags to be used as a
    base of description, did not restrict the output from the command
    to those that match the given pattern.
-   (merge 46e1d6e jc/describe later to maint).
 
  * Clarify in the documentation "what" gets pushed to "where" when the
    command line to "git push" does not say these explicitly.
@@ -173,7 +233,6 @@ details).
 
  * 'git commit -m "$msg"' used to add an extra newline even when
    $msg already ended with one.
-   (merge 46fbf75 bc/commit-complete-lines-given-via-m-option later to maint).
 
  * The SSL peer verification done by "git imap-send" did not ask for
    Server Name Indication (RFC 4366), failing to connect SSL/TLS
@@ -187,7 +246,6 @@ details).
 
  * Verification of signed tags were not done correctly when not in C
    or en/US locale.
-   (merge 0174eea mg/gpg-interface-using-status later to maint).
 
  * Some platforms and users spell UTF-8 differently; retry with the
    most official "UTF-8" when the system does not understand the
index c1f435f6df0b2e16d50546b61c2abab50b67acc0..f79184c0a83170ea54298f2b8ac960372371b0f3 100644 (file)
@@ -555,7 +555,7 @@ core.commentchar::
        (default '#').
 
 sequence.editor::
-       Text editor used by `git rebase -i` for editing the rebase insn file.
+       Text editor used by `git rebase -i` for editing the rebase instruction file.
        The value is meant to be interpreted by the shell when it is used.
        It can be overridden by the `GIT_SEQUENCE_EDITOR` environment variable.
        When not configured the default commit message editor is used instead.
index 86ef56e7c8760d622848da4131ebf231d27eb873..cafdc9642d312776b0122c4d010a9e9246bad8ff 100644 (file)
@@ -10,7 +10,9 @@ SYNOPSIS
 --------
 [verse]
 'git commit-tree' <tree> [(-p <parent>)...] < changelog
-'git commit-tree' [(-p <parent>)...] [(-m <message>)...] [(-F <file>)...] <tree>
+'git commit-tree' [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...]
+                 [(-F <file>)...] <tree>
+
 
 DESCRIPTION
 -----------
@@ -52,6 +54,9 @@ OPTIONS
        Read the commit log message from the given file. Use `-` to read
        from the standard input.
 
+-S[<keyid>]::
+       GPG-sign commit.
+
 
 Commit Information
 ------------------
index e0e12e947034b3f690d7f103ee90e0bc1a21f926..8361e6e4e3d2d4829d3ce3c6f3ec4a2ac974e86e 100644 (file)
@@ -72,10 +72,12 @@ with custom merge tool commands and has the same value as `$MERGED`.
 --symlinks::
 --no-symlinks::
        'git difftool''s default behavior is create symlinks to the
-       working tree when run in `--dir-diff` mode.
+       working tree when run in `--dir-diff` mode and the right-hand
+       side of the comparison yields the same content as the file in
+       the working tree.
 +
-       Specifying `--no-symlinks` instructs 'git difftool' to create
-       copies instead.  `--no-symlinks` is the default on Windows.
+Specifying `--no-symlinks` instructs 'git difftool' to create copies
+instead.  `--no-symlinks` is the default on Windows.
 
 -x <command>::
 --extcmd=<command>::
index c852a2677ab9e32801b0dc6ed8bc85b03982233d..42391f2ae76aa579605470b6cad8cf4812f29aae 100644 (file)
@@ -170,6 +170,30 @@ happens:
 If you tried a merge which resulted in complex conflicts and
 want to start over, you can recover with `git merge --abort`.
 
+MERGING TAG
+-----------
+
+When merging an annotated (and possibly signed) tag, Git always
+creates a merge commit even if a fast-forward merge is possible, and
+the commit message template is prepared with the tag message.
+Additionally, if the tag is signed, the signature check is reported
+as a comment in the message template. See also linkgit:git-tag[1].
+
+When you want to just integrate with the work leading to the commit
+that happens to be tagged, e.g. synchronizing with an upstream
+release point, you may not want to make an unnecessary merge commit.
+
+In such a case, you can "unwrap" the tag yourself before feeding it
+to `git merge`, or pass `--ff-only` when you do not have any work on
+your own. e.g.
+
+---
+git fetch origin
+git merge v1.2.3^0
+git merge --ff-only v1.2.3
+---
+
+
 HOW CONFLICTS ARE PRESENTED
 ---------------------------
 
index e3032c46c637f39a5a3dd965f122365ece197a5e..b21aa87fe87a26f738c971f8c3dffdcf44fb13cd 100644 (file)
@@ -126,6 +126,12 @@ This option is only applicable when listing tags without annotation lines.
        linkgit:git-check-ref-format[1].  Some of these checks
        may restrict the characters allowed in a tag name.
 
+<commit>::
+<object>::
+       The object that the new tag will refer to, usually a commit.
+       Defaults to HEAD.
+
+
 CONFIGURATION
 -------------
 By default, 'git tag' in sign-with-default mode (-s) will use your
index a0f022b41df07b3f2d431a9f284d63ce040a55ad..2adccf8fec71c10f8223490f1be4b2a215ace5ea 100644 (file)
@@ -30,7 +30,8 @@ set to `no` at the beginning of them.
 
 --no-ff::
        Create a merge commit even when the merge resolves as a
-       fast-forward.
+       fast-forward.  This is the default behaviour when merging an
+       annotated (and possibly signed) tag.
 
 --ff-only::
        Refuse to merge and exit with a non-zero status unless the
index 314e25da7344dda459ca5b3292aaaabdfc0d6495..1707d451b604e05de69d8a3d9f8dc326079d4742 100644 (file)
@@ -116,6 +116,11 @@ some output processing may assume ref names in UTF-8.
   object of that type is found or the object cannot be
   dereferenced anymore (in which case, barf).  '<rev>{caret}0'
   is a short-hand for '<rev>{caret}\{commit\}'.
++
+'rev{caret}\{object\}' can be used to make sure 'rev' names an
+object that exists, without requiring 'rev' to be a tag, and
+without dereferencing 'rev'; because a tag is already an object,
+it does not have to be dereferenced even once to get to an object.
 
 '<rev>{caret}\{\}', e.g. 'v0.99.8{caret}\{\}'::
   A suffix '{caret}' followed by an empty brace pair
index 598d6313da96f8ba8f76fbd982f491077b6d5d97..0f931a203002aec397f8b454f93df9aee97a21ad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -358,33 +358,39 @@ STRIP ?= strip
 # Among the variables below, these:
 #   gitexecdir
 #   template_dir
-#   mandir
-#   infodir
-#   htmldir
 #   sysconfdir
 # can be specified as a relative path some/where/else;
 # this is interpreted as relative to $(prefix) and "git" at
 # runtime figures out where they are based on the path to the executable.
+# Additionally, the following will be treated as relative by "git" if they
+# begin with "$(prefix)/":
+#   mandir
+#   infodir
+#   htmldir
 # This can help installing the suite in a relocatable way.
 
 prefix = $(HOME)
 bindir_relative = bin
 bindir = $(prefix)/$(bindir_relative)
-mandir = share/man
-infodir = share/info
+mandir = $(prefix)/share/man
+infodir = $(prefix)/share/info
 gitexecdir = libexec/git-core
 mergetoolsdir = $(gitexecdir)/mergetools
 sharedir = $(prefix)/share
 gitwebdir = $(sharedir)/gitweb
 localedir = $(sharedir)/locale
 template_dir = share/git-core/templates
-htmldir = share/doc/git-doc
+htmldir = $(prefix)/share/doc/git-doc
 ETC_GITCONFIG = $(sysconfdir)/gitconfig
 ETC_GITATTRIBUTES = $(sysconfdir)/gitattributes
 lib = lib
 # DESTDIR =
 pathsep = :
 
+mandir_relative = $(patsubst $(prefix)/%,%,$(mandir))
+infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
+htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
+
 export prefix bindir sharedir sysconfdir gitwebdir localedir
 
 CC = cc
@@ -1539,12 +1545,12 @@ ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
 bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
-mandir_SQ = $(subst ','\'',$(mandir))
-infodir_SQ = $(subst ','\'',$(infodir))
+mandir_relative_SQ = $(subst ','\'',$(mandir_relative))
+infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
 localedir_SQ = $(subst ','\'',$(localedir))
 gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
 template_dir_SQ = $(subst ','\'',$(template_dir))
-htmldir_SQ = $(subst ','\'',$(htmldir))
+htmldir_relative_SQ = $(subst ','\'',$(htmldir_relative))
 prefix_SQ = $(subst ','\'',$(prefix))
 gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
 
@@ -1676,9 +1682,9 @@ strip: $(PROGRAMS) git$X
 
 git.sp git.s git.o: GIT-PREFIX
 git.sp git.s git.o: EXTRA_CPPFLAGS = \
-       '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
-       '-DGIT_MAN_PATH="$(mandir_SQ)"' \
-       '-DGIT_INFO_PATH="$(infodir_SQ)"'
+       '-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \
+       '-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
+       '-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
 
 git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
@@ -1688,9 +1694,9 @@ help.sp help.s help.o: common-cmds.h
 
 builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h GIT-PREFIX
 builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
-       '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
-       '-DGIT_MAN_PATH="$(mandir_SQ)"' \
-       '-DGIT_INFO_PATH="$(infodir_SQ)"'
+       '-DGIT_HTML_PATH="$(htmldir_relative_SQ)"' \
+       '-DGIT_MAN_PATH="$(mandir_relative_SQ)"' \
+       '-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
 
 version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
 version.sp version.s version.o: EXTRA_CPPFLAGS = \
diff --git a/attr.c b/attr.c
index e2f9377891a7f6e0d56803016726e3cd3137fdc2..689bc2a8961fac72a01e615764af53f49d01c3ec 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -657,24 +657,24 @@ static void prepare_attr_stack(const char *path, int dirlen)
 }
 
 static int path_matches(const char *pathname, int pathlen,
-                       const char *basename,
+                       int basename_offset,
                        const struct pattern *pat,
                        const char *base, int baselen)
 {
        const char *pattern = pat->pattern;
        int prefix = pat->nowildcardlen;
+       int isdir = (pathlen && pathname[pathlen - 1] == '/');
 
-       if ((pat->flags & EXC_FLAG_MUSTBEDIR) &&
-           ((!pathlen) || (pathname[pathlen-1] != '/')))
+       if ((pat->flags & EXC_FLAG_MUSTBEDIR) && !isdir)
                return 0;
 
        if (pat->flags & EXC_FLAG_NODIR) {
-               return match_basename(basename,
-                                     pathlen - (basename - pathname),
+               return match_basename(pathname + basename_offset,
+                                     pathlen - basename_offset - isdir,
                                      pattern, prefix,
                                      pat->patternlen, pat->flags);
        }
-       return match_pathname(pathname, pathlen,
+       return match_pathname(pathname, pathlen - isdir,
                              base, baselen,
                              pattern, prefix, pat->patternlen, pat->flags);
 }
@@ -703,7 +703,7 @@ static int fill_one(const char *what, struct match_attr *a, int rem)
        return rem;
 }
 
-static int fill(const char *path, int pathlen, const char *basename,
+static int fill(const char *path, int pathlen, int basename_offset,
                struct attr_stack *stk, int rem)
 {
        int i;
@@ -713,7 +713,7 @@ static int fill(const char *path, int pathlen, const char *basename,
                struct match_attr *a = stk->attrs[i];
                if (a->is_macro)
                        continue;
-               if (path_matches(path, pathlen, basename,
+               if (path_matches(path, pathlen, basename_offset,
                                 &a->u.pat, base, stk->originlen))
                        rem = fill_one("fill", a, rem);
        }
@@ -752,7 +752,8 @@ static void collect_all_attrs(const char *path)
 {
        struct attr_stack *stk;
        int i, pathlen, rem, dirlen;
-       const char *basename, *cp, *last_slash = NULL;
+       const char *cp, *last_slash = NULL;
+       int basename_offset;
 
        for (cp = path; *cp; cp++) {
                if (*cp == '/' && cp[1])
@@ -760,10 +761,10 @@ static void collect_all_attrs(const char *path)
        }
        pathlen = cp - path;
        if (last_slash) {
-               basename = last_slash + 1;
+               basename_offset = last_slash + 1 - path;
                dirlen = last_slash - path;
        } else {
-               basename = path;
+               basename_offset = 0;
                dirlen = 0;
        }
 
@@ -773,7 +774,7 @@ static void collect_all_attrs(const char *path)
 
        rem = attr_nr;
        for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
-               rem = fill(path, pathlen, basename, stk, rem);
+               rem = fill(path, pathlen, basename_offset, stk, rem);
 }
 
 int git_check_attr(const char *path, int num, struct git_attr_check *check)
index 06f5320b18bc5690ae944a722d19ec5496687228..5b882d01f6622b1be4f494d2f4a3cbda5adeec08 100644 (file)
@@ -2117,10 +2117,10 @@ static void update_pre_post_images(struct image *preimage,
 
        /*
         * Adjust the common context lines in postimage. This can be
-        * done in-place when we are just doing whitespace fixing,
-        * which does not make the string grow, but needs a new buffer
-        * when ignoring whitespace causes the update, since in this case
-        * we could have e.g. tabs converted to multiple spaces.
+        * done in-place when we are shrinking it with whitespace
+        * fixing, but needs a new buffer when ignoring whitespace or
+        * expanding leading tabs to spaces.
+        *
         * We trust the caller to tell us if the update can be done
         * in place (postlen==0) or not.
         */
@@ -2185,7 +2185,7 @@ static int match_fragment(struct image *img,
        int i;
        char *fixed_buf, *buf, *orig, *target;
        struct strbuf fixed;
-       size_t fixed_len;
+       size_t fixed_len, postlen;
        int preimage_limit;
 
        if (preimage->nr + try_lno <= img->nr) {
@@ -2335,6 +2335,7 @@ static int match_fragment(struct image *img,
        strbuf_init(&fixed, preimage->len + 1);
        orig = preimage->buf;
        target = img->buf + try;
+       postlen = 0;
        for (i = 0; i < preimage_limit; i++) {
                size_t oldlen = preimage->line[i].len;
                size_t tgtlen = img->line[try_lno + i].len;
@@ -2362,6 +2363,7 @@ static int match_fragment(struct image *img,
                match = (tgtfix.len == fixed.len - fixstart &&
                         !memcmp(tgtfix.buf, fixed.buf + fixstart,
                                             fixed.len - fixstart));
+               postlen += tgtfix.len;
 
                strbuf_release(&tgtfix);
                if (!match)
@@ -2399,8 +2401,10 @@ static int match_fragment(struct image *img,
         * hunk match.  Update the context lines in the postimage.
         */
        fixed_buf = strbuf_detach(&fixed, &fixed_len);
+       if (postlen < postimage->len)
+               postlen = 0;
        update_pre_post_images(preimage, postimage,
-                              fixed_buf, fixed_len, 0);
+                              fixed_buf, fixed_len, postlen);
        return 1;
 
  unmatch_exit:
index 9a1cfd3dac0123cb65d3db7597221a29e9010bd9..49178f159e246c9a99805d806fa5f096337e8d2f 100644 (file)
@@ -27,8 +27,8 @@ static int run_remote_archiver(int argc, const char **argv,
                               const char *remote, const char *exec,
                               const char *name_hint)
 {
-       char buf[LARGE_PACKET_MAX];
-       int fd[2], i, len, rv;
+       char *buf;
+       int fd[2], i, rv;
        struct transport *transport;
        struct remote *_remote;
 
@@ -53,21 +53,18 @@ static int run_remote_archiver(int argc, const char **argv,
                packet_write(fd[1], "argument %s\n", argv[i]);
        packet_flush(fd[1]);
 
-       len = packet_read_line(fd[0], buf, sizeof(buf));
-       if (!len)
+       buf = packet_read_line(fd[0], NULL);
+       if (!buf)
                die(_("git archive: expected ACK/NAK, got EOF"));
-       if (buf[len-1] == '\n')
-               buf[--len] = 0;
        if (strcmp(buf, "ACK")) {
-               if (len > 5 && !prefixcmp(buf, "NACK "))
+               if (!prefixcmp(buf, "NACK "))
                        die(_("git archive: NACK %s"), buf + 5);
-               if (len > 4 && !prefixcmp(buf, "ERR "))
+               if (!prefixcmp(buf, "ERR "))
                        die(_("remote error: %s"), buf + 4);
                die(_("git archive: protocol error"));
        }
 
-       len = packet_read_line(fd[0], buf, sizeof(buf));
-       if (len)
+       if (packet_read_line(fd[0], NULL))
                die(_("git archive: expected a flush"));
 
        /* Now, start reading from fd[0] and spit it out to stdout */
index 00d17d25d1866323f9e1dffcb334a573f5de5221..3f0fbc082aef95b3d02a8b677116264eea892369 100644 (file)
@@ -18,6 +18,7 @@
 #include "string-list.h"
 #include "column.h"
 #include "utf8.h"
+#include "wt-status.h"
 
 static const char * const builtin_branch_usage[] = {
        N_("git branch [options] [-r | -a] [--merged | --no-merged]"),
@@ -550,6 +551,29 @@ static int calc_maxwidth(struct ref_list *refs)
        return w;
 }
 
+static char *get_head_description(void)
+{
+       struct strbuf desc = STRBUF_INIT;
+       struct wt_status_state state;
+       memset(&state, 0, sizeof(state));
+       wt_status_get_state(&state, 1);
+       if (state.rebase_in_progress ||
+           state.rebase_interactive_in_progress)
+               strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+                           state.branch);
+       else if (state.bisect_in_progress)
+               strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
+                           state.branch);
+       else if (state.detached_from)
+               strbuf_addf(&desc, _("(detached from %s)"),
+                           state.detached_from);
+       else
+               strbuf_addstr(&desc, _("(no branch)"));
+       free(state.branch);
+       free(state.onto);
+       free(state.detached_from);
+       return strbuf_detach(&desc, NULL);
+}
 
 static void show_detached(struct ref_list *ref_list)
 {
@@ -557,7 +581,7 @@ static void show_detached(struct ref_list *ref_list)
 
        if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
                struct ref_item item;
-               item.name = xstrdup(_("(no branch)"));
+               item.name = get_head_description();
                item.width = utf8_strwidth(item.name);
                item.kind = REF_LOCAL_BRANCH;
                item.dest = NULL;
@@ -880,7 +904,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                if (edit_branch_description(branch_name))
                        return 1;
        } else if (rename) {
-               if (argc == 1)
+               if (!argc)
+                       die(_("branch name required"));
+               else if (argc == 1)
                        rename_branch(head, argv[0], rename > 1);
                else if (argc == 2)
                        rename_branch(argv[0], argv[1], rename > 1);
index ad290007363120062b044ecb47cf976dbe118384..40f87b4649aa97ca1f33a02f09f5938ce5f41f74 100644 (file)
@@ -193,7 +193,7 @@ static int batch_one_object(const char *obj_name, int print_contents)
        unsigned char sha1[20];
        enum object_type type = 0;
        unsigned long size;
-       void *contents;
+       void *contents = NULL;
 
        if (!obj_name)
           return 1;
index a9c1b5a95fea0a805819f995da2d2f1ba194433d..f8033f446e8f9ef1807aa28c2f9eca4e821c42c9 100644 (file)
@@ -271,24 +271,55 @@ static int checkout_paths(const struct checkout_opts *opts,
                ;
        ps_matched = xcalloc(1, pos);
 
+       /*
+        * Make sure all pathspecs participated in locating the paths
+        * to be checked out.
+        */
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
+               ce->ce_flags &= ~CE_MATCHED;
                if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
+                       /*
+                        * "git checkout tree-ish -- path", but this entry
+                        * is in the original index; it will not be checked
+                        * out to the working tree and it does not matter
+                        * if pathspec matched this entry.  We will not do
+                        * anything to this entry at all.
+                        */
                        continue;
-               match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
+               /*
+                * Either this entry came from the tree-ish we are
+                * checking the paths out of, or we are checking out
+                * of the index.
+                *
+                * If it comes from the tree-ish, we already know it
+                * matches the pathspec and could just stamp
+                * CE_MATCHED to it from update_some(). But we still
+                * need ps_matched and read_tree_recursive (and
+                * eventually tree_entry_interesting) cannot fill
+                * ps_matched yet. Once it can, we can avoid calling
+                * match_pathspec() for _all_ entries when
+                * opts->source_tree != NULL.
+                */
+               if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce),
+                                  0, ps_matched))
+                       ce->ce_flags |= CE_MATCHED;
        }
 
-       if (report_path_error(ps_matched, opts->pathspec, opts->prefix))
+       if (report_path_error(ps_matched, opts->pathspec, opts->prefix)) {
+               free(ps_matched);
                return 1;
+       }
+       free(ps_matched);
 
        /* "checkout -m path" to recreate conflicted state */
        if (opts->merge)
-               unmerge_cache(opts->pathspec);
+               unmerge_marked_index(&the_index);
 
        /* Any unmerged paths? */
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
-               if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
+               if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce))
                                continue;
                        if (opts->force) {
@@ -313,9 +344,7 @@ static int checkout_paths(const struct checkout_opts *opts,
        state.refresh_cache = 1;
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
-               if (opts->source_tree && !(ce->ce_flags & CE_UPDATE))
-                       continue;
-               if (match_pathspec(opts->pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
+               if (ce->ce_flags & CE_MATCHED) {
                        if (!ce_stage(ce)) {
                                errs |= checkout_entry(ce, &state, NULL);
                                continue;
index e0aaf13583376c7adf49f504cc9e7e1303fb4a4d..f9c380eb6c536b657ddc65b88f6839ec761f7f25 100644 (file)
@@ -23,6 +23,7 @@
 #include "branch.h"
 #include "remote.h"
 #include "run-command.h"
+#include "connected.h"
 
 /*
  * Overall FIXMEs:
@@ -376,10 +377,32 @@ static void clone_local(const char *src_repo, const char *dest_repo)
 static const char *junk_work_tree;
 static const char *junk_git_dir;
 static pid_t junk_pid;
+enum {
+       JUNK_LEAVE_NONE,
+       JUNK_LEAVE_REPO,
+       JUNK_LEAVE_ALL
+} junk_mode = JUNK_LEAVE_NONE;
+
+static const char junk_leave_repo_msg[] =
+N_("Clone succeeded, but checkout failed.\n"
+   "You can inspect what was checked out with 'git status'\n"
+   "and retry the checkout with 'git checkout -f HEAD'\n");
 
 static void remove_junk(void)
 {
        struct strbuf sb = STRBUF_INIT;
+
+       switch (junk_mode) {
+       case JUNK_LEAVE_REPO:
+               warning("%s", _(junk_leave_repo_msg));
+               /* fall-through */
+       case JUNK_LEAVE_ALL:
+               return;
+       default:
+               /* proceed to removal */
+               break;
+       }
+
        if (getpid() != junk_pid)
                return;
        if (junk_git_dir) {
@@ -485,12 +508,37 @@ static void write_followtags(const struct ref *refs, const char *msg)
        }
 }
 
+static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+{
+       struct ref **rm = cb_data;
+       struct ref *ref = *rm;
+
+       /*
+        * Skip anything missing a peer_ref, which we are not
+        * actually going to write a ref for.
+        */
+       while (ref && !ref->peer_ref)
+               ref = ref->next;
+       /* Returning -1 notes "end of list" to the caller. */
+       if (!ref)
+               return -1;
+
+       hashcpy(sha1, ref->old_sha1);
+       *rm = ref->next;
+       return 0;
+}
+
 static void update_remote_refs(const struct ref *refs,
                               const struct ref *mapped_refs,
                               const struct ref *remote_head_points_at,
                               const char *branch_top,
                               const char *msg)
 {
+       const struct ref *rm = mapped_refs;
+
+       if (check_everything_connected(iterate_ref_map, 0, &rm))
+               die(_("remote did not send all necessary objects"));
+
        if (refs) {
                write_remote_refs(mapped_refs);
                if (option_single_branch)
@@ -579,7 +627,8 @@ static int checkout(void)
        tree = parse_tree_indirect(sha1);
        parse_tree(tree);
        init_tree_desc(&t, tree->buffer, tree->size);
-       unpack_trees(1, &t, &opts);
+       if (unpack_trees(1, &t, &opts) < 0)
+               die(_("unable to checkout working tree"));
 
        if (write_cache(fd, active_cache, active_nr) ||
            commit_locked_index(lock_file))
@@ -898,12 +947,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        transport_unlock_pack(transport);
        transport_disconnect(transport);
 
+       junk_mode = JUNK_LEAVE_REPO;
        err = checkout();
 
        strbuf_release(&reflog_msg);
        strbuf_release(&branch_top);
        strbuf_release(&key);
        strbuf_release(&value);
-       junk_pid = 0;
+       junk_mode = JUNK_LEAVE_ALL;
        return err;
 }
index eac901a0ee15f72eacffea32d1b327c7a336f19c..f641ff2a898cf76d288ed139772e247015ca554b 100644 (file)
@@ -10,7 +10,7 @@
 #include "utf8.h"
 #include "gpg-interface.h"
 
-static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S<signer>] [-m <message>] [-F <file>] <sha1> <changelog";
+static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1> <changelog";
 
 static void new_parent(struct commit *parent, struct commit_list **parents_p)
 {
index d21d07a1a8e9fbc365a4555a0f9e81d0c1f2a7d0..46204375e786ea583ca1a7eabc3060611adc3d04 100644 (file)
@@ -702,7 +702,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                        previous = eol;
                }
 
-               append_signoff(&sb, ignore_footer);
+               append_signoff(&sb, ignore_footer, 0);
        }
 
        if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
index 670e81fd995953bff9c29cc6fab99d03f1fee8b7..aba44655524ff722d90de09760945f5f30088752 100644 (file)
@@ -119,14 +119,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                        /* in stateless RPC mode we use pkt-line to read
                         * from stdin, until we get a flush packet
                         */
-                       static char line[1000];
                        for (;;) {
-                               int n = packet_read_line(0, line, sizeof(line));
-                               if (!n)
+                               char *line = packet_read_line(0, NULL);
+                               if (!line)
                                        break;
-                               if (line[n-1] == '\n')
-                                       n--;
-                               add_sought_entry_mem(&sought, &nr_sought,  &alloc_sought, line, n);
+                               add_sought_entry(&sought, &nr_sought,  &alloc_sought, line);
                        }
                }
                else {
@@ -147,7 +144,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
                                   args.verbose ? CONNECT_VERBOSE : 0);
        }
 
-       get_remote_heads(fd[0], &ref, 0, NULL);
+       get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL);
 
        ref = fetch_pack(&args, fd, conn, ref, dest,
                         sought, nr_sought, pack_lockfile_ptr);
index ef62124aa491a9ac1e430d5baa3cf448902ac9c4..79dfe47320e244c3110ce65d5b19fa87ebca3cb5 100644 (file)
@@ -78,6 +78,7 @@ static int nr_threads;
 static int from_stdin;
 static int strict;
 static int verbose;
+static int show_stat;
 
 static struct progress *progress;
 
@@ -108,6 +109,10 @@ static pthread_mutex_t work_mutex;
 #define work_lock()            lock_mutex(&work_mutex)
 #define work_unlock()          unlock_mutex(&work_mutex)
 
+static pthread_mutex_t deepest_delta_mutex;
+#define deepest_delta_lock()   lock_mutex(&deepest_delta_mutex)
+#define deepest_delta_unlock() unlock_mutex(&deepest_delta_mutex)
+
 static pthread_key_t key;
 
 static inline void lock_mutex(pthread_mutex_t *mutex)
@@ -130,6 +135,8 @@ static void init_thread(void)
        init_recursive_mutex(&read_mutex);
        pthread_mutex_init(&counter_mutex, NULL);
        pthread_mutex_init(&work_mutex, NULL);
+       if (show_stat)
+               pthread_mutex_init(&deepest_delta_mutex, NULL);
        pthread_key_create(&key, NULL);
        thread_data = xcalloc(nr_threads, sizeof(*thread_data));
        threads_active = 1;
@@ -143,6 +150,8 @@ static void cleanup_thread(void)
        pthread_mutex_destroy(&read_mutex);
        pthread_mutex_destroy(&counter_mutex);
        pthread_mutex_destroy(&work_mutex);
+       if (show_stat)
+               pthread_mutex_destroy(&deepest_delta_mutex);
        pthread_key_delete(key);
        free(thread_data);
 }
@@ -158,6 +167,9 @@ static void cleanup_thread(void)
 #define work_lock()
 #define work_unlock()
 
+#define deepest_delta_lock()
+#define deepest_delta_unlock()
+
 #endif
 
 
@@ -833,9 +845,13 @@ static void resolve_delta(struct object_entry *delta_obj,
        void *base_data, *delta_data;
 
        delta_obj->real_type = base->obj->real_type;
-       delta_obj->delta_depth = base->obj->delta_depth + 1;
-       if (deepest_delta < delta_obj->delta_depth)
-               deepest_delta = delta_obj->delta_depth;
+       if (show_stat) {
+               delta_obj->delta_depth = base->obj->delta_depth + 1;
+               deepest_delta_lock();
+               if (deepest_delta < delta_obj->delta_depth)
+                       deepest_delta = delta_obj->delta_depth;
+               deepest_delta_unlock();
+       }
        delta_obj->base_object_no = base->obj - objects;
        delta_data = get_data_from_pack(delta_obj);
        base_data = get_base_data(base);
@@ -951,8 +967,10 @@ static void *threaded_second_pass(void *data)
        set_thread_data(data);
        for (;;) {
                int i;
-               work_lock();
+               counter_lock();
                display_progress(progress, nr_resolved_deltas);
+               counter_unlock();
+               work_lock();
                while (nr_dispatched < nr_objects &&
                       is_delta_type(objects[nr_dispatched].type))
                        nr_dispatched++;
@@ -1107,6 +1125,8 @@ static void conclude_pack(int fix_thin_pack, const char *curr_pack, unsigned cha
                objects = xrealloc(objects,
                                   (nr_objects + nr_unresolved + 1)
                                   * sizeof(*objects));
+               memset(objects + nr_objects + 1, 0,
+                      nr_unresolved * sizeof(*objects));
                f = sha1fd(output_fd, curr_pack);
                fix_unresolved_deltas(f, nr_unresolved);
                strbuf_addf(&msg, _("completed with %d local objects"),
@@ -1463,7 +1483,7 @@ static void show_pack_info(int stat_only)
 
 int cmd_index_pack(int argc, const char **argv, const char *prefix)
 {
-       int i, fix_thin_pack = 0, verify = 0, stat_only = 0, stat = 0;
+       int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
        const char *curr_pack, *curr_index;
        const char *index_name = NULL, *pack_name = NULL;
        const char *keep_name = NULL, *keep_msg = NULL;
@@ -1496,10 +1516,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                verify = 1;
                        } else if (!strcmp(arg, "--verify-stat")) {
                                verify = 1;
-                               stat = 1;
+                               show_stat = 1;
                        } else if (!strcmp(arg, "--verify-stat-only")) {
                                verify = 1;
-                               stat = 1;
+                               show_stat = 1;
                                stat_only = 1;
                        } else if (!strcmp(arg, "--keep")) {
                                keep_msg = "";
@@ -1607,7 +1627,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (strict)
                check_objects();
 
-       if (stat)
+       if (show_stat)
                show_pack_info(stat_only);
 
        idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
index 8f0b2e84fef5d1b9c07ea8846c9fbc1318d8d51b..0f318107e5732a08bc6f979633829ea958d8f9b0 100644 (file)
@@ -23,6 +23,7 @@
 #include "streaming.h"
 #include "version.h"
 #include "mailmap.h"
+#include "gpg-interface.h"
 
 /* Set a default date-time format for git log ("log.date" config variable) */
 static const char *default_date_mode = NULL;
@@ -367,6 +368,8 @@ static int git_log_config(const char *var, const char *value, void *cb)
 
        if (grep_config(var, value, cb) < 0)
                return -1;
+       if (git_gpg_config(var, value, cb) < 0)
+               return -1;
        return git_diff_ui_config(var, value, cb);
 }
 
@@ -1086,7 +1089,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        struct commit *origin = NULL, *head = NULL;
        const char *in_reply_to = NULL;
        struct patch_ids ids;
-       char *add_signoff = NULL;
        struct strbuf buf = STRBUF_INIT;
        int use_patch_format = 0;
        int quiet = 0;
@@ -1193,16 +1195,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                rev.subject_prefix = strbuf_detach(&sprefix, NULL);
        }
 
-       if (do_signoff) {
-               const char *committer;
-               const char *endpos;
-               committer = git_committer_info(IDENT_STRICT);
-               endpos = strchr(committer, '>');
-               if (!endpos)
-                       die(_("bogus committer info %s"), committer);
-               add_signoff = xmemdupz(committer, endpos - committer + 1);
-       }
-
        for (i = 0; i < extra_hdr.nr; i++) {
                strbuf_addstr(&buf, extra_hdr.items[i].string);
                strbuf_addch(&buf, '\n');
@@ -1393,7 +1385,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                total++;
                start_number--;
        }
-       rev.add_signoff = add_signoff;
+       rev.add_signoff = do_signoff;
        while (0 <= --nr) {
                int shown;
                commit = list[nr];
index bac11d1605c51d558dc18d7d2e67470b79a39d2d..3e2daa37c367560450217cfae5cbb717bfb508af 100644 (file)
@@ -518,6 +518,19 @@ static void merge_name(const char *remote, struct strbuf *msg)
                strbuf_release(&line);
                goto cleanup;
        }
+
+       if (remote_head->util) {
+               struct merge_remote_desc *desc;
+               desc = merge_remote_util(remote_head);
+               if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
+                       strbuf_addf(msg, "%s\t\t%s '%s'\n",
+                                   sha1_to_hex(desc->obj->sha1),
+                                   typename(desc->obj->type),
+                                   remote);
+                       goto cleanup;
+               }
+       }
+
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
                sha1_to_hex(remote_head->object.sha1), remote);
 cleanup:
index 62ba6e7a3d014be01fe01ed497c083f7e44e9d34..ccebd74f166f5d02e8e6a0bb6033d741f8580f7b 100644 (file)
@@ -754,17 +754,15 @@ static struct command *read_head_info(void)
        struct command *commands = NULL;
        struct command **p = &commands;
        for (;;) {
-               static char line[1000];
+               char *line;
                unsigned char old_sha1[20], new_sha1[20];
                struct command *cmd;
                char *refname;
                int len, reflen;
 
-               len = packet_read_line(0, line, sizeof(line));
-               if (!len)
+               line = packet_read_line(0, &len);
+               if (!line)
                        break;
-               if (line[len-1] == '\n')
-                       line[--len] = 0;
                if (len < 83 ||
                    line[40] != ' ' ||
                    line[81] != ' ' ||
@@ -932,7 +930,7 @@ static void report(struct command *commands, const char *unpack_status)
        if (use_sideband)
                send_sideband(1, 1, buf.buf, buf.len, use_sideband);
        else
-               safe_write(1, buf.buf, buf.len);
+               write_or_die(1, buf.buf, buf.len);
        strbuf_release(&buf);
 }
 
index 57a46b2654aa82b154d32eb77addd6a4f89c8e8d..152c4ea092a44ff9e70e293ddeecb4ad7105e25d 100644 (file)
@@ -79,7 +79,7 @@ static void print_helper_status(struct ref *ref)
                }
                strbuf_addch(&buf, '\n');
 
-               safe_write(1, buf.buf, buf.len);
+               write_or_die(1, buf.buf, buf.len);
        }
        strbuf_release(&buf);
 }
@@ -207,7 +207,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 
        memset(&extra_have, 0, sizeof(extra_have));
 
-       get_remote_heads(fd[0], &remote_refs, REF_NORMAL, &extra_have);
+       get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL, &extra_have);
 
        transport_verify_remote_names(nr_refspecs, refspecs);
 
index b928beb8ed51c9af9aa4f640e80443a5e059d505..af2da35e7d05f4de2d7a77a708ee3cf6f490266e 100644 (file)
@@ -7,6 +7,7 @@
 #include "pkt-line.h"
 #include "sideband.h"
 #include "run-command.h"
+#include "argv-array.h"
 
 static const char upload_archive_usage[] =
        "git upload-archive <repo>";
@@ -18,51 +19,31 @@ static const char deadchild[] =
 
 int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
 {
-       const char *sent_argv[MAX_ARGS];
+       struct argv_array sent_argv = ARGV_ARRAY_INIT;
        const char *arg_cmd = "argument ";
-       char *p, buf[4096];
-       int sent_argc;
-       int len;
 
        if (argc != 2)
                usage(upload_archive_usage);
 
-       if (strlen(argv[1]) + 1 > sizeof(buf))
-               die("insanely long repository name");
-
-       strcpy(buf, argv[1]); /* enter-repo smudges its argument */
-
-       if (!enter_repo(buf, 0))
-               die("'%s' does not appear to be a git repository", buf);
+       if (!enter_repo(argv[1], 0))
+               die("'%s' does not appear to be a git repository", argv[1]);
 
        /* put received options in sent_argv[] */
-       sent_argc = 1;
-       sent_argv[0] = "git-upload-archive";
-       for (p = buf;;) {
-               /* This will die if not enough free space in buf */
-               len = packet_read_line(0, p, (buf + sizeof buf) - p);
-               if (len == 0)
+       argv_array_push(&sent_argv, "git-upload-archive");
+       for (;;) {
+               char *buf = packet_read_line(0, NULL);
+               if (!buf)
                        break;  /* got a flush */
-               if (sent_argc > MAX_ARGS - 2)
-                       die("Too many options (>%d)", MAX_ARGS - 2);
+               if (sent_argv.argc > MAX_ARGS)
+                       die("Too many options (>%d)", MAX_ARGS - 1);
 
-               if (p[len-1] == '\n') {
-                       p[--len] = 0;
-               }
-               if (len < strlen(arg_cmd) ||
-                   strncmp(arg_cmd, p, strlen(arg_cmd)))
+               if (prefixcmp(buf, arg_cmd))
                        die("'argument' token or flush expected");
-
-               len -= strlen(arg_cmd);
-               memmove(p, p + strlen(arg_cmd), len);
-               sent_argv[sent_argc++] = p;
-               p += len;
-               *p++ = 0;
+               argv_array_push(&sent_argv, buf + strlen(arg_cmd));
        }
-       sent_argv[sent_argc] = NULL;
 
        /* parse all options sent by the client */
-       return write_archive(sent_argc, sent_argv, prefix, 0, NULL, 1);
+       return write_archive(sent_argv.argc, sent_argv.argv, prefix, 0, NULL, 1);
 }
 
 __attribute__((format (printf, 1, 2)))
diff --git a/cache.h b/cache.h
index 59e5b5317910c2cd41a00d694f0de50b3d6f40f7..c12957bb0364ed8e316cb055bf7c0710ef2ce28e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -132,7 +132,6 @@ struct cache_entry {
        unsigned int ce_namelen;
        unsigned char sha1[20];
        struct cache_entry *next;
-       struct cache_entry *dir_next;
        char name[FLEX_ARRAY]; /* more */
 };
 
@@ -163,6 +162,9 @@ struct cache_entry {
 #define CE_UNPACKED          (1 << 24)
 #define CE_NEW_SKIP_WORKTREE (1 << 25)
 
+/* used to temporarily mark paths matched by pathspecs */
+#define CE_MATCHED           (1 << 26)
+
 /*
  * Extended on-disk flags
  */
@@ -268,25 +270,15 @@ struct index_state {
        unsigned name_hash_initialized : 1,
                 initialized : 1;
        struct hash_table name_hash;
+       struct hash_table dir_hash;
 };
 
 extern struct index_state the_index;
 
 /* Name hashing */
 extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
-/*
- * We don't actually *remove* it, we can just mark it invalid so that
- * we won't find it in lookups.
- *
- * Not only would we have to search the lists (simple enough), but
- * we'd also have to rehash other hash buckets in case this makes the
- * hash bucket empty (common). So it's much better to just mark
- * it.
- */
-static inline void remove_name_hash(struct cache_entry *ce)
-{
-       ce->ce_flags |= CE_UNHASHED;
-}
+extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
+extern void free_name_hash(struct index_state *istate);
 
 
 #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
@@ -1064,7 +1056,9 @@ struct extra_have_objects {
        int nr, alloc;
        unsigned char (*array)[20];
 };
-extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
+extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
+                                    struct ref **list, unsigned int flags,
+                                    struct extra_have_objects *);
 extern int server_supports(const char *feature);
 extern int parse_feature_request(const char *features, const char *feature);
 extern const char *server_feature_value(const char *feature, int *len_ret);
index 5428858875a20b89411d3662e777586d9d798fab..871b41d23a7471724a0446ff3333b577a175c579 100644 (file)
@@ -1,3 +1,4 @@
+#define CYGWIN_C
 #define WIN32_LEAN_AND_MEAN
 #ifdef CYGWIN_V15_WIN32API
 #include "../git-compat-util.h"
 #endif
 #include "../cache.h" /* to read configuration */
 
+/*
+ * Return POSIX permission bits, regardless of core.ignorecygwinfstricks
+ */
+int cygwin_get_st_mode_bits(const char *path, int *mode)
+{
+       struct stat st;
+       if (lstat(path, &st) < 0)
+               return -1;
+       *mode = st.st_mode;
+       return 0;
+}
+
 static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
 {
        long long winTime = ((long long)ft->dwHighDateTime << 32) +
index a3229f5b4fbb819ba1beabcf560725e80ad51bb4..c04965a2e0e259d3bded51730f699bde5c4262e6 100644 (file)
@@ -4,6 +4,11 @@
 typedef int (*stat_fn_t)(const char*, struct stat*);
 extern stat_fn_t cygwin_stat_fn;
 extern stat_fn_t cygwin_lstat_fn;
+int cygwin_get_st_mode_bits(const char *path, int *mode);
 
+#define get_st_mode_bits(p,m) cygwin_get_st_mode_bits((p),(m))
+#ifndef CYGWIN_C
+/* cygwin.c needs the original lstat() */
 #define stat(path, buf) (*cygwin_stat_fn)(path, buf)
 #define lstat(path, buf) (*cygwin_lstat_fn)(path, buf)
+#endif
index fa02bdd82ada7c45b2c666438aa2df9407f5c1af..e6a6d0f941cb2d61586c5d5eddb2ccf27c6439db 100644 (file)
@@ -22,9 +22,3 @@ docdir = @docdir@
 
 mandir = @mandir@
 htmldir = @htmldir@
-
-srcdir = @srcdir@
-VPATH = @srcdir@
-
-export exec_prefix mandir
-export srcdir VPATH
index 49e56ba35a645359b0d7c1f7bbc9e2108b3424d9..f57efd06c1d7ab01076b67d37ed24e34e17c4ebb 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -62,8 +62,8 @@ static void die_initial_contact(int got_at_least_one_head)
 /*
  * Read all the refs from the other end
  */
-struct ref **get_remote_heads(int in, struct ref **list,
-                             unsigned int flags,
+struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
+                             struct ref **list, unsigned int flags,
                              struct extra_have_objects *extra_have)
 {
        int got_at_least_one_head = 0;
@@ -72,18 +72,19 @@ struct ref **get_remote_heads(int in, struct ref **list,
        for (;;) {
                struct ref *ref;
                unsigned char old_sha1[20];
-               static char buffer[1000];
                char *name;
                int len, name_len;
+               char *buffer = packet_buffer;
 
-               len = packet_read(in, buffer, sizeof(buffer));
+               len = packet_read(in, &src_buf, &src_len,
+                                 packet_buffer, sizeof(packet_buffer),
+                                 PACKET_READ_GENTLE_ON_EOF |
+                                 PACKET_READ_CHOMP_NEWLINE);
                if (len < 0)
                        die_initial_contact(got_at_least_one_head);
 
                if (!len)
                        break;
-               if (buffer[len-1] == '\n')
-                       buffer[--len] = 0;
 
                if (len > 4 && !prefixcmp(buffer, "ERR "))
                        die("remote error: %s", buffer + 4);
index 341422a766efe70580817d98a574d95bd9ddebf9..756a9514591c7664c82c93d3a6d50d91f46857d8 100644 (file)
@@ -282,6 +282,8 @@ __git_ps1 ()
                                r="|MERGING"
                        elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
                                r="|CHERRY-PICKING"
+                       elif [ -f "$g/REVERT_HEAD" ]; then
+                               r="|REVERTING"
                        elif [ -f "$g/BISECT_LOG" ]; then
                                r="|BISECTING"
                        fi
index 4d211f5b81ae3f96938211c4bd2e20789d38fcdb..23b7ef9f6208720310d2a8d93812cbe6fb3a42ca 100644 (file)
@@ -4,4 +4,4 @@ objects from mediawiki just as one would do with a classic git
 repository thanks to remote-helpers.
 
 For more information, visit the wiki at
-https://github.com/Bibzball/Git-Mediawiki/wiki
+https://github.com/moy/Git-Mediawiki/wiki
index df8c0ab0588e70ad6e6f56195535030055782d85..6aeddcb98d2694a705d34e80293fc81a20bc39a8 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -600,7 +600,7 @@ static void parse_host_arg(char *extra_args, int buflen)
 
 static int execute(void)
 {
-       static char line[1000];
+       char *line = packet_buffer;
        int pktlen, len, i;
        char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
 
@@ -608,7 +608,7 @@ static int execute(void)
                loginfo("Connection from %s:%s", addr, port);
 
        alarm(init_timeout ? init_timeout : timeout);
-       pktlen = packet_read_line(0, line, sizeof(line));
+       pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
        alarm(0);
 
        len = strlen(line);
diff --git a/dir.c b/dir.c
index 57394e452eb0de117b27f64804e529b617a6c7e0..1e42b2b1509e2cf154a1d9722138fbeae437daf5 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -59,6 +59,35 @@ inline int git_fnmatch(const char *pattern, const char *string,
        return fnmatch(pattern, string, fnm_flags);
 }
 
+static int fnmatch_icase_mem(const char *pattern, int patternlen,
+                            const char *string, int stringlen,
+                            int flags)
+{
+       int match_status;
+       struct strbuf pat_buf = STRBUF_INIT;
+       struct strbuf str_buf = STRBUF_INIT;
+       const char *use_pat = pattern;
+       const char *use_str = string;
+
+       if (pattern[patternlen]) {
+               strbuf_add(&pat_buf, pattern, patternlen);
+               use_pat = pat_buf.buf;
+       }
+       if (string[stringlen]) {
+               strbuf_add(&str_buf, string, stringlen);
+               use_str = str_buf.buf;
+       }
+
+       if (ignore_case)
+               flags |= WM_CASEFOLD;
+       match_status = wildmatch(use_pat, use_str, flags, NULL);
+
+       strbuf_release(&pat_buf);
+       strbuf_release(&str_buf);
+
+       return match_status;
+}
+
 static size_t common_prefix_len(const char **pathspec)
 {
        const char *n, *first;
@@ -626,15 +655,20 @@ int match_basename(const char *basename, int basenamelen,
                   int flags)
 {
        if (prefix == patternlen) {
-               if (!strcmp_icase(pattern, basename))
+               if (patternlen == basenamelen &&
+                   !strncmp_icase(pattern, basename, basenamelen))
                        return 1;
        } else if (flags & EXC_FLAG_ENDSWITH) {
+               /* "*literal" matching against "fooliteral" */
                if (patternlen - 1 <= basenamelen &&
-                   !strcmp_icase(pattern + 1,
-                                 basename + basenamelen - patternlen + 1))
+                   !strncmp_icase(pattern + 1,
+                                  basename + basenamelen - (patternlen - 1),
+                                  patternlen - 1))
                        return 1;
        } else {
-               if (fnmatch_icase(pattern, basename, 0) == 0)
+               if (fnmatch_icase_mem(pattern, patternlen,
+                                     basename, basenamelen,
+                                     0) == 0)
                        return 1;
        }
        return 0;
@@ -654,6 +688,7 @@ int match_pathname(const char *pathname, int pathlen,
         */
        if (*pattern == '/') {
                pattern++;
+               patternlen--;
                prefix--;
        }
 
@@ -680,13 +715,22 @@ int match_pathname(const char *pathname, int pathlen,
                if (strncmp_icase(pattern, name, prefix))
                        return 0;
                pattern += prefix;
+               patternlen -= prefix;
                name    += prefix;
                namelen -= prefix;
+
+               /*
+                * If the whole pattern did not have a wildcard,
+                * then our prefix match is all we need; we
+                * do not need to call fnmatch at all.
+                */
+               if (!patternlen && !namelen)
+                       return 1;
        }
 
-       return wildmatch(pattern, name,
-                        WM_PATHNAME | (ignore_case ? WM_CASEFOLD : 0),
-                        NULL) == 0;
+       return fnmatch_icase_mem(pattern, patternlen,
+                                name, namelen,
+                                WM_PATHNAME) == 0;
 }
 
 /*
diff --git a/entry.c b/entry.c
index 17a6bccec64e0e523aacc124611c43bd818372e3..d7c131d45309a496714221616244a70569155913 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -120,16 +120,18 @@ static int streaming_write_entry(struct cache_entry *ce, char *path,
                                 const struct checkout *state, int to_tempfile,
                                 int *fstat_done, struct stat *statbuf)
 {
-       int result = -1;
+       int result = 0;
        int fd;
 
        fd = open_output_fd(path, ce, to_tempfile);
-       if (0 <= fd) {
-               result = stream_blob_to_fd(fd, ce->sha1, filter, 1);
-               *fstat_done = fstat_output(fd, state, statbuf);
-               result = close(fd);
-       }
-       if (result && 0 <= fd)
+       if (fd < 0)
+               return -1;
+
+       result |= stream_blob_to_fd(fd, ce->sha1, filter, 1);
+       *fstat_done = fstat_output(fd, state, statbuf);
+       result |= close(fd);
+
+       if (result)
                unlink(path);
        return result;
 }
@@ -145,7 +147,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
        struct stat st;
 
        if (ce_mode_s_ifmt == S_IFREG) {
-               struct stream_filter *filter = get_stream_filter(path, ce->sha1);
+               struct stream_filter *filter = get_stream_filter(ce->name, ce->sha1);
                if (filter &&
                    !streaming_write_entry(ce, path, filter,
                                           state, to_tempfile,
index a0c2c2ff14f471c4ffa6ff1f4ec9085562378ad1..5f539d7d8f7e087423734fb1d19e0d5e580fbe6e 100644 (file)
@@ -2465,6 +2465,7 @@ static void note_change_n(struct branch *b, unsigned char *old_fanout)
                hashcpy(sha1, oe->idx.sha1);
        } else if (!prefixcmp(p, "inline ")) {
                inline_data = 1;
+               oe = NULL; /* not used with inline_data, but makes gcc happy */
                p += strlen("inline");  /* advance to space */
        } else {
                if (get_sha1_hex(p, sha1))
index cef8fde61bf4516f95ae13b05f73cb26a268f90c..f156dd4fac30cda4e09c508b7091cdb8d96917e2 100644 (file)
@@ -172,8 +172,8 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
                 * shallow and unshallow commands every time there
                 * is a block of have lines exchanged.
                 */
-               char line[1000];
-               while (packet_read_line(fd, line, sizeof(line))) {
+               char *line;
+               while ((line = packet_read_line(fd, NULL))) {
                        if (!prefixcmp(line, "shallow "))
                                continue;
                        if (!prefixcmp(line, "unshallow "))
@@ -215,17 +215,17 @@ static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
 
 static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 {
-       static char line[1000];
-       int len = packet_read_line(fd, line, sizeof(line));
+       int len;
+       char *line = packet_read_line(fd, &len);
 
        if (!len)
                die("git fetch-pack: expected ACK/NAK, got EOF");
-       if (line[len-1] == '\n')
-               line[--len] = 0;
        if (!strcmp(line, "NAK"))
                return NAK;
        if (!prefixcmp(line, "ACK ")) {
                if (!get_sha1_hex(line+4, result_sha1)) {
+                       if (len < 45)
+                               return ACK;
                        if (strstr(line+45, "continue"))
                                return ACK_continue;
                        if (strstr(line+45, "common"))
@@ -245,7 +245,7 @@ static void send_request(struct fetch_pack_args *args,
                send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
                packet_flush(fd);
        } else
-               safe_write(fd, buf->buf, buf->len);
+               write_or_die(fd, buf->buf, buf->len);
 }
 
 static void insert_one_alternate_ref(const struct ref *ref, void *unused)
@@ -346,11 +346,11 @@ static int find_common(struct fetch_pack_args *args,
        state_len = req_buf.len;
 
        if (args->depth > 0) {
-               char line[1024];
+               char *line;
                unsigned char sha1[20];
 
                send_request(args, fd[1], &req_buf);
-               while (packet_read_line(fd[0], line, sizeof(line))) {
+               while ((line = packet_read_line(fd[0], NULL))) {
                        if (!prefixcmp(line, "shallow ")) {
                                if (get_sha1_hex(line + 8, sha1))
                                        die("invalid shallow line: %s", line);
index 202130f888bee14e73b8cc108f39f2da8a14d23b..c092855dd7c277d41288ee89f033bef0a8677a9f 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -778,13 +778,6 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"."
            action=yes
        fi
 
-       if test -f "$dotest/final-commit"
-       then
-               FIRSTLINE=$(sed 1q "$dotest/final-commit")
-       else
-               FIRSTLINE=""
-       fi
-
        if test $action = skip
        then
                go_next
@@ -797,6 +790,13 @@ To restore the original branch and stop patching run \"\$cmdline --abort\"."
                stop_here $this
        fi
 
+       if test -f "$dotest/final-commit"
+       then
+               FIRSTLINE=$(sed 1q "$dotest/final-commit")
+       else
+               FIRSTLINE=""
+       fi
+
        say "$(eval_gettext "Applying: \$FIRSTLINE")"
 
        case "$resolved" in
index 90e037203879f6ba91403ae5371f18910206b580..cde442fb5f47cd13757e09ae435963bc061db083 100644 (file)
 typedef long intptr_t;
 typedef unsigned long uintptr_t;
 #endif
+int get_st_mode_bits(const char *path, int *mode);
 #if defined(__CYGWIN__)
 #undef _XOPEN_SOURCE
 #include <grp.h>
index 12231fbc6708971f6350006c710124bd369de7ab..67802922ccc41fa2993c1bce4ea1a3d2899c7a40 100755 (executable)
@@ -13,9 +13,9 @@
 use 5.008;
 use strict;
 use warnings;
+use Error qw(:try);
 use File::Basename qw(dirname);
 use File::Copy;
-use File::Compare;
 use File::Find;
 use File::stat;
 use File::Path qw(mkpath rmtree);
@@ -83,6 +83,52 @@ sub exit_cleanup
        exit($status | ($status >> 8));
 }
 
+sub use_wt_file
+{
+       my ($repo, $workdir, $file, $sha1, $symlinks) = @_;
+       my $null_sha1 = '0' x 40;
+
+       if ($sha1 ne $null_sha1 and not $symlinks) {
+               return 0;
+       }
+
+       my $wt_sha1 = $repo->command_oneline('hash-object', "$workdir/$file");
+       my $use = ($sha1 eq $null_sha1) || ($sha1 eq $wt_sha1);
+       return ($use, $wt_sha1);
+}
+
+sub changed_files
+{
+       my ($repo_path, $index, $worktree) = @_;
+       $ENV{GIT_INDEX_FILE} = $index;
+       $ENV{GIT_WORK_TREE} = $worktree;
+       my $must_unset_git_dir = 0;
+       if (not defined($ENV{GIT_DIR})) {
+               $must_unset_git_dir = 1;
+               $ENV{GIT_DIR} = $repo_path;
+       }
+
+       my @refreshargs = qw/update-index --really-refresh -q --unmerged/;
+       my @gitargs = qw/diff-files --name-only -z/;
+       try {
+               Git::command_oneline(@refreshargs);
+       } catch Git::Error::Command with {};
+
+       my $line = Git::command_oneline(@gitargs);
+       my @files;
+       if (defined $line) {
+               @files = split('\0', $line);
+       } else {
+               @files = ();
+       }
+
+       delete($ENV{GIT_INDEX_FILE});
+       delete($ENV{GIT_WORK_TREE});
+       delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
+
+       return map { $_ => 1 } @files;
+}
+
 sub setup_dir_diff
 {
        my ($repo, $workdir, $symlinks) = @_;
@@ -106,6 +152,7 @@ sub setup_dir_diff
        my $null_sha1 = '0' x 40;
        my $lindex = '';
        my $rindex = '';
+       my $wtindex = '';
        my %submodule;
        my %symlink;
        my @working_tree = ();
@@ -159,10 +206,14 @@ sub setup_dir_diff
                }
 
                if ($rmode ne $null_mode) {
-                       if ($rsha1 ne $null_sha1) {
-                               $rindex .= "$rmode $rsha1\t$dst_path\0";
+                       my ($use, $wt_sha1) = use_wt_file($repo, $workdir,
+                                                         $dst_path, $rsha1,
+                                                         $symlinks);
+                       if ($use) {
+                               push @working_tree, $dst_path;
+                               $wtindex .= "$rmode $wt_sha1\t$dst_path\0";
                        } else {
-                               push(@working_tree, $dst_path);
+                               $rindex .= "$rmode $rsha1\t$dst_path\0";
                        }
                }
        }
@@ -203,13 +254,21 @@ sub setup_dir_diff
        $rc = system('git', 'checkout-index', '--all', "--prefix=$rdir/");
        exit_cleanup($tmpdir, $rc) if $rc != 0;
 
+       $ENV{GIT_INDEX_FILE} = "$tmpdir/wtindex";
+       ($inpipe, $ctx) =
+               $repo->command_input_pipe(qw(update-index --info-only -z --index-info));
+       print($inpipe $wtindex);
+       $repo->command_close_pipe($inpipe, $ctx);
+
        # If $GIT_DIR was explicitly set just for the update/checkout
        # commands, then it should be unset before continuing.
        delete($ENV{GIT_DIR}) if ($must_unset_git_dir);
        delete($ENV{GIT_INDEX_FILE});
 
        # Changes in the working tree need special treatment since they are
-       # not part of the index
+       # not part of the index. Remove any trailing slash from $workdir
+       # before starting to avoid double slashes in symlink targets.
+       $workdir =~ s|/$||;
        for my $file (@working_tree) {
                my $dir = dirname($file);
                unless (-d "$rdir/$dir") {
@@ -373,19 +432,34 @@ sub dir_diff
        # should be copied back to the working tree.
        # Do not copy back files when symlinks are used and the
        # external tool did not replace the original link with a file.
+       #
+       # These hashes are loaded lazily since they aren't needed
+       # in the common case of --symlinks and the difftool updating
+       # files through the symlink.
+       my %wt_modified;
+       my %tmp_modified;
+       my $indices_loaded = 0;
+
        for my $file (@worktree) {
                next if $symlinks && -l "$b/$file";
                next if ! -f "$b/$file";
 
-               my $diff = compare("$b/$file", "$workdir/$file");
-               if ($diff == 0) {
-                       next;
-               } elsif ($diff == -1) {
-                       my $errmsg = "warning: Could not compare ";
-                       $errmsg += "'$b/$file' with '$workdir/$file'\n";
+               if (!$indices_loaded) {
+                       %wt_modified = changed_files($repo->repo_path(),
+                               "$tmpdir/wtindex", "$workdir");
+                       %tmp_modified = changed_files($repo->repo_path(),
+                               "$tmpdir/wtindex", "$b");
+                       $indices_loaded = 1;
+               }
+
+               if (exists $wt_modified{$file} and exists $tmp_modified{$file}) {
+                       my $errmsg = "warning: Both files modified: ";
+                       $errmsg .= "'$workdir/$file' and '$b/$file'.\n";
+                       $errmsg .= "warning: Working tree file has been left.\n";
+                       $errmsg .= "warning:\n";
                        warn $errmsg;
                        $error = 1;
-               } elsif ($diff == 1) {
+               } elsif (exists $tmp_modified{$file}) {
                        my $mode = stat("$b/$file")->mode;
                        copy("$b/$file", "$workdir/$file") or
                        exit_cleanup($tmpdir, 1);
index c3501d987e66d330189f156590316d335b580aaf..729747281864155bee7ef009de3ab92cc7643a89 100755 (executable)
@@ -512,8 +512,9 @@ sub split_addrs {
 
 ($sender) = expand_aliases($sender) if defined $sender;
 
-# returns 1 if the conflict must be solved using it as a format-patch argument
-sub check_file_rev_conflict($) {
+# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if
+# $f is a revision list specification to be passed to format-patch.
+sub is_format_patch_arg {
        return unless $repo;
        my $f = shift;
        try {
@@ -529,6 +530,7 @@ ($)
     * Giving --format-patch option if you mean a range.
 EOF
        } catch Git::Error::Command with {
+               # Not a valid revision.  Treat it as a filename.
                return 0;
        }
 }
@@ -540,14 +542,14 @@ ($)
        if ($f eq "--") {
                push @rev_list_opts, "--", @ARGV;
                @ARGV = ();
-       } elsif (-d $f and !check_file_rev_conflict($f)) {
+       } elsif (-d $f and !is_format_patch_arg($f)) {
                opendir my $dh, $f
                        or die "Failed to opendir $f: $!";
 
                push @files, grep { -f $_ } map { catfile($f, $_) }
                                sort readdir $dh;
                closedir $dh;
-       } elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
+       } elsif ((-f $f or -p $f) and !is_format_patch_arg($f)) {
                push @files, $f;
        } else {
                push @rev_list_opts, $f;
@@ -711,7 +713,7 @@ sub ask {
                        }
                }
        }
-       return undef;
+       return;
 }
 
 my %broken_encoding;
@@ -833,7 +835,7 @@ sub extract_valid_address {
        # less robust/correct than the monster regexp in Email::Valid,
        # but still does a 99% job, and one less dependency
        return $1 if $address =~ /($local_part_regexp\@$domain_regexp)/;
-       return undef;
+       return;
 }
 
 sub extract_valid_address_or_die {
@@ -1453,7 +1455,7 @@ sub recipients_cmd {
 
        my $sanitized_sender = sanitize_address($sender);
        my @addresses = ();
-       open my $fh, "$cmd \Q$file\E |"
+       open my $fh, "-|", "$cmd \Q$file\E"
            or die "($prefix) Could not execute '$cmd'";
        while (my $address = <$fh>) {
                $address =~ s/^\s*//g;
@@ -1499,7 +1501,7 @@ sub validate_patch {
                        return "$.: patch contains a line longer than 998 characters";
                }
        }
-       return undef;
+       return;
 }
 
 sub file_has_nonascii {
index 204bc78a9fdffcd11c397a85f94636bb17529b91..79bfaac9d4cb9a04e5e1fd7675d740cf9fc27e87 100755 (executable)
@@ -267,6 +267,11 @@ module_clone()
        (clear_local_git_env; cd "$sm_path" && GIT_WORK_TREE=. git config core.worktree "$rel/$b")
 }
 
+isnumber()
+{
+       n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
+}
+
 #
 # Add a new submodule to the working tree, .gitmodules and the index
 #
@@ -601,10 +606,12 @@ cmd_deinit()
 
                        if test -z "$force"
                        then
-                               git rm -n "$sm_path" ||
+                               git rm -qn "$sm_path" ||
                                die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
                        fi
-                       rm -rf "$sm_path" || say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
+                       rm -rf "$sm_path" &&
+                       say "$(eval_gettext "Cleared directory '\$sm_path'")" ||
+                       say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
                fi
 
                mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"
@@ -889,14 +896,14 @@ cmd_summary() {
                        for_status="$1"
                        ;;
                -n|--summary-limit)
-                       if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
-                       then
-                               :
-                       else
-                               usage
-                       fi
+                       summary_limit="$2"
+                       isnumber "$summary_limit" || usage
                        shift
                        ;;
+               --summary-limit=*)
+                       summary_limit="${1#--summary-limit=}"
+                       isnumber "$summary_limit" || usage
+                       ;;
                --)
                        shift
                        break
index 1e827264b4cab04a801804b8d066f9d2cbb85edc..9f446798d473cee7dedc82d5d2c820cb0967b375 100755 (executable)
@@ -119,8 +119,8 @@ if test -z "$browser" ; then
                browser_candidates="w3m elinks links lynx"
        fi
        # SECURITYSESSIONID indicates an OS X GUI login session
-       if test -n "$SECURITYSESSIONID" \
-               -o "$TERM_PROGRAM" = "Apple_Terminal" ; then
+       if test -n "$SECURITYSESSIONID" || test -n "$TERM_PROGRAM"
+       then
                browser_candidates="open $browser_candidates"
        fi
        # /bin/start indicates MinGW
index f50e77fb2890207bd7385b59bdd561133991c910..8144f3ad5eb68c789fd031921e6e702170310d68 100644 (file)
@@ -70,7 +70,7 @@ static void format_write(int fd, const char *fmt, ...)
        if (n >= sizeof(buffer))
                die("protocol error: impossibly long line");
 
-       safe_write(fd, buffer, n);
+       write_or_die(fd, buffer, n);
 }
 
 static void http_status(unsigned code, const char *msg)
@@ -111,7 +111,7 @@ static void hdr_cache_forever(void)
 
 static void end_headers(void)
 {
-       safe_write(1, "\r\n", 2);
+       write_or_die(1, "\r\n", 2);
 }
 
 __attribute__((format (printf, 1, 2)))
@@ -157,7 +157,7 @@ static void send_strbuf(const char *type, struct strbuf *buf)
        hdr_int(content_length, buf->len);
        hdr_str(content_type, type);
        end_headers();
-       safe_write(1, buf->buf, buf->len);
+       write_or_die(1, buf->buf, buf->len);
 }
 
 static void send_local_file(const char *the_type, const char *name)
@@ -185,7 +185,7 @@ static void send_local_file(const char *the_type, const char *name)
                        die_errno("Cannot read '%s'", p);
                if (!n)
                        break;
-               safe_write(1, buf, n);
+               write_or_die(1, buf, n);
        }
        close(fd);
        free(buf);
diff --git a/http.c b/http.c
index d9d1aad3be468394e39966c0b45e7e101b387b0c..8803c70825ac8aefb619b6c56d6015573eb5d227 100644 (file)
--- a/http.c
+++ b/http.c
@@ -5,6 +5,7 @@
 #include "url.h"
 #include "credential.h"
 #include "version.h"
+#include "pkt-line.h"
 
 int active_requests;
 int http_is_verbose;
index 3d888238711364338a83e7d81bf27f555352bdca..7cc7d598e712fab531f7c6f938be17c317161f3e 100644 (file)
@@ -9,6 +9,7 @@
 #include "string-list.h"
 #include "color.h"
 #include "gpg-interface.h"
+#include "sequencer.h"
 
 struct decoration name_decoration = { "object names" };
 
@@ -206,89 +207,6 @@ void show_decorations(struct rev_info *opt, struct commit *commit)
        putchar(')');
 }
 
-/*
- * Search for "^[-A-Za-z]+: [^@]+@" pattern. It usually matches
- * Signed-off-by: and Acked-by: lines.
- */
-static int detect_any_signoff(char *letter, int size)
-{
-       char *cp;
-       int seen_colon = 0;
-       int seen_at = 0;
-       int seen_name = 0;
-       int seen_head = 0;
-
-       cp = letter + size;
-       while (letter <= --cp && *cp == '\n')
-               continue;
-
-       while (letter <= cp) {
-               char ch = *cp--;
-               if (ch == '\n')
-                       break;
-
-               if (!seen_at) {
-                       if (ch == '@')
-                               seen_at = 1;
-                       continue;
-               }
-               if (!seen_colon) {
-                       if (ch == '@')
-                               return 0;
-                       else if (ch == ':')
-                               seen_colon = 1;
-                       else
-                               seen_name = 1;
-                       continue;
-               }
-               if (('A' <= ch && ch <= 'Z') ||
-                   ('a' <= ch && ch <= 'z') ||
-                   ch == '-') {
-                       seen_head = 1;
-                       continue;
-               }
-               /* no empty last line doesn't match */
-               return 0;
-       }
-       return seen_head && seen_name;
-}
-
-static void append_signoff(struct strbuf *sb, const char *signoff)
-{
-       static const char signed_off_by[] = "Signed-off-by: ";
-       size_t signoff_len = strlen(signoff);
-       int has_signoff = 0;
-       char *cp;
-
-       cp = sb->buf;
-
-       /* First see if we already have the sign-off by the signer */
-       while ((cp = strstr(cp, signed_off_by))) {
-
-               has_signoff = 1;
-
-               cp += strlen(signed_off_by);
-               if (cp + signoff_len >= sb->buf + sb->len)
-                       break;
-               if (strncmp(cp, signoff, signoff_len))
-                       continue;
-               if (!isspace(cp[signoff_len]))
-                       continue;
-               /* we already have him */
-               return;
-       }
-
-       if (!has_signoff)
-               has_signoff = detect_any_signoff(sb->buf, sb->len);
-
-       if (!has_signoff)
-               strbuf_addch(sb, '\n');
-
-       strbuf_addstr(sb, signed_off_by);
-       strbuf_add(sb, signoff, signoff_len);
-       strbuf_addch(sb, '\n');
-}
-
 static unsigned int digits_in_number(unsigned int number)
 {
        unsigned int i = 10, result = 1;
@@ -669,8 +587,10 @@ void show_log(struct rev_info *opt)
        /*
         * And then the pretty-printed message itself
         */
-       if (ctx.need_8bit_cte >= 0)
-               ctx.need_8bit_cte = has_non_ascii(opt->add_signoff);
+       if (ctx.need_8bit_cte >= 0 && opt->add_signoff)
+               ctx.need_8bit_cte =
+                       has_non_ascii(fmt_name(getenv("GIT_COMMITTER_NAME"),
+                                              getenv("GIT_COMMITTER_EMAIL")));
        ctx.date_mode = opt->date_mode;
        ctx.date_mode_explicit = opt->date_mode_explicit;
        ctx.abbrev = opt->diffopt.abbrev;
@@ -683,7 +603,7 @@ void show_log(struct rev_info *opt)
        pretty_print_commit(&ctx, commit, &msgbuf);
 
        if (opt->add_signoff)
-               append_signoff(&msgbuf, opt->add_signoff);
+               append_signoff(&msgbuf, 0, APPEND_SIGNOFF_DEDUP);
 
        if ((ctx.fmt != CMIT_FMT_USERFORMAT) &&
            ctx.notes_message && *ctx.notes_message) {
@@ -789,11 +709,14 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
 {
        int showed_log;
        struct commit_list *parents;
-       unsigned const char *sha1 = commit->object.sha1;
+       unsigned const char *sha1;
 
        if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
                return 0;
 
+       parse_commit(commit);
+       sha1 = commit->tree->object.sha1;
+
        /* Root commit? */
        parents = commit->parents;
        if (!parents) {
@@ -816,7 +739,9 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
                         * parent, showing summary diff of the others
                         * we merged _in_.
                         */
-                       diff_tree_sha1(parents->item->object.sha1, sha1, "", &opt->diffopt);
+                       parse_commit(parents->item);
+                       diff_tree_sha1(parents->item->tree->object.sha1,
+                                      sha1, "", &opt->diffopt);
                        log_tree_diff_flush(opt);
                        return !opt->loginfo;
                }
@@ -829,7 +754,9 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
        for (;;) {
                struct commit *parent = parents->item;
 
-               diff_tree_sha1(parent->object.sha1, sha1, "", &opt->diffopt);
+               parse_commit(parent);
+               diff_tree_sha1(parent->tree->object.sha1,
+                              sha1, "", &opt->diffopt);
                log_tree_diff_flush(opt);
 
                showed_log |= !opt->loginfo;
index 26f7ed143e0649b6377ea427b5c57ca7ddb663d3..2bb734d51cbe59e3eed7c82e7522c594d5efa579 100644 (file)
@@ -47,6 +47,13 @@ static int score_matches(unsigned mode1, unsigned mode2, const char *path)
        return score;
 }
 
+static int base_name_entries_compare(const struct name_entry *a,
+                                    const struct name_entry *b)
+{
+       return base_name_compare(a->path, tree_entry_len(a), a->mode,
+                                b->path, tree_entry_len(b), b->mode);
+}
+
 /*
  * Inspect two trees, and give a score that tells how similar they are.
  */
@@ -71,54 +78,35 @@ static int score_trees(const unsigned char *hash1, const unsigned char *hash2)
        if (type != OBJ_TREE)
                die("%s is not a tree", sha1_to_hex(hash2));
        init_tree_desc(&two, two_buf, size);
-       while (one.size | two.size) {
-               const unsigned char *elem1 = elem1;
-               const unsigned char *elem2 = elem2;
-               const char *path1 = path1;
-               const char *path2 = path2;
-               unsigned mode1 = mode1;
-               unsigned mode2 = mode2;
+       for (;;) {
+               struct name_entry e1, e2;
+               int got_entry_from_one = tree_entry(&one, &e1);
+               int got_entry_from_two = tree_entry(&two, &e2);
                int cmp;
 
-               if (one.size)
-                       elem1 = tree_entry_extract(&one, &path1, &mode1);
-               if (two.size)
-                       elem2 = tree_entry_extract(&two, &path2, &mode2);
-
-               if (!one.size) {
-                       /* two has more entries */
-                       score += score_missing(mode2, path2);
-                       update_tree_entry(&two);
-                       continue;
-               }
-               if (!two.size) {
+               if (got_entry_from_one && got_entry_from_two)
+                       cmp = base_name_entries_compare(&e1, &e2);
+               else if (got_entry_from_one)
                        /* two lacks this entry */
-                       score += score_missing(mode1, path1);
-                       update_tree_entry(&one);
-                       continue;
-               }
-               cmp = base_name_compare(path1, strlen(path1), mode1,
-                                       path2, strlen(path2), mode2);
-               if (cmp < 0) {
+                       cmp = -1;
+               else if (got_entry_from_two)
+                       /* two has more entries */
+                       cmp = 1;
+               else
+                       break;
+
+               if (cmp < 0)
                        /* path1 does not appear in two */
-                       score += score_missing(mode1, path1);
-                       update_tree_entry(&one);
-                       continue;
-               }
-               else if (cmp > 0) {
+                       score += score_missing(e1.mode, e1.path);
+               else if (cmp > 0)
                        /* path2 does not appear in one */
-                       score += score_missing(mode2, path2);
-                       update_tree_entry(&two);
-                       continue;
-               }
-               else if (hashcmp(elem1, elem2))
+                       score += score_missing(e2.mode, e2.path);
+               else if (hashcmp(e1.sha1, e2.sha1))
                        /* they are different */
-                       score += score_differs(mode1, mode2, path1);
+                       score += score_differs(e1.mode, e2.mode, e1.path);
                else
                        /* same subtree or blob */
-                       score += score_matches(mode1, mode2, path1);
-               update_tree_entry(&one);
-               update_tree_entry(&two);
+                       score += score_matches(e1.mode, e2.mode, e1.path);
        }
        free(one_buf);
        free(two_buf);
index 9bac31a6ab7aa80586828e7f48ab829e5b77a740..617c86c53762575373d0bcbf3d8bab09cb042caa 100644 (file)
@@ -32,38 +32,96 @@ static unsigned int hash_name(const char *name, int namelen)
        return hash;
 }
 
-static void hash_index_entry_directories(struct index_state *istate, struct cache_entry *ce)
+struct dir_entry {
+       struct dir_entry *next;
+       struct dir_entry *parent;
+       struct cache_entry *ce;
+       int nr;
+       unsigned int namelen;
+};
+
+static struct dir_entry *find_dir_entry(struct index_state *istate,
+               const char *name, unsigned int namelen)
+{
+       unsigned int hash = hash_name(name, namelen);
+       struct dir_entry *dir;
+
+       for (dir = lookup_hash(hash, &istate->dir_hash); dir; dir = dir->next)
+               if (dir->namelen == namelen &&
+                   !strncasecmp(dir->ce->name, name, namelen))
+                       return dir;
+       return NULL;
+}
+
+static struct dir_entry *hash_dir_entry(struct index_state *istate,
+               struct cache_entry *ce, int namelen)
 {
        /*
         * Throw each directory component in the hash for quick lookup
         * during a git status. Directory components are stored with their
         * closing slash.  Despite submodules being a directory, they never
         * reach this point, because they are stored without a closing slash
-        * in the cache.
+        * in index_state.name_hash (as ordinary cache_entries).
         *
-        * Note that the cache_entry stored with the directory does not
-        * represent the directory itself.  It is a pointer to an existing
-        * filename, and its only purpose is to represent existence of the
-        * directory in the cache.  It is very possible multiple directory
-        * hash entries may point to the same cache_entry.
+        * Note that the cache_entry stored with the dir_entry merely
+        * supplies the name of the directory (up to dir_entry.namelen). We
+        * track the number of 'active' files in a directory in dir_entry.nr,
+        * so we can tell if the directory is still relevant, e.g. for git
+        * status. However, if cache_entries are removed, we cannot pinpoint
+        * an exact cache_entry that's still active. It is very possible that
+        * multiple dir_entries point to the same cache_entry.
         */
-       unsigned int hash;
-       void **pos;
+       struct dir_entry *dir;
+
+       /* get length of parent directory */
+       while (namelen > 0 && !is_dir_sep(ce->name[namelen - 1]))
+               namelen--;
+       if (namelen <= 0)
+               return NULL;
+
+       /* lookup existing entry for that directory */
+       dir = find_dir_entry(istate, ce->name, namelen);
+       if (!dir) {
+               /* not found, create it and add to hash table */
+               void **pdir;
+               unsigned int hash = hash_name(ce->name, namelen);
 
-       const char *ptr = ce->name;
-       while (*ptr) {
-               while (*ptr && *ptr != '/')
-                       ++ptr;
-               if (*ptr == '/') {
-                       ++ptr;
-                       hash = hash_name(ce->name, ptr - ce->name);
-                       pos = insert_hash(hash, ce, &istate->name_hash);
-                       if (pos) {
-                               ce->dir_next = *pos;
-                               *pos = ce;
-                       }
+               dir = xcalloc(1, sizeof(struct dir_entry));
+               dir->namelen = namelen;
+               dir->ce = ce;
+
+               pdir = insert_hash(hash, dir, &istate->dir_hash);
+               if (pdir) {
+                       dir->next = *pdir;
+                       *pdir = dir;
                }
+
+               /* recursively add missing parent directories */
+               dir->parent = hash_dir_entry(istate, ce, namelen - 1);
        }
+       return dir;
+}
+
+static void add_dir_entry(struct index_state *istate, struct cache_entry *ce)
+{
+       /* Add reference to the directory entry (and parents if 0). */
+       struct dir_entry *dir = hash_dir_entry(istate, ce, ce_namelen(ce));
+       while (dir && !(dir->nr++))
+               dir = dir->parent;
+}
+
+static void remove_dir_entry(struct index_state *istate, struct cache_entry *ce)
+{
+       /*
+        * Release reference to the directory entry (and parents if 0).
+        *
+        * Note: we do not remove / free the entry because there's no
+        * hash.[ch]::remove_hash and dir->next may point to other entries
+        * that are still valid, so we must not free the memory.
+        */
+       struct dir_entry *dir = hash_dir_entry(istate, ce, ce_namelen(ce));
+       while (dir && dir->nr && !(--dir->nr))
+               dir = dir->parent;
 }
 
 static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
@@ -74,7 +132,7 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
        if (ce->ce_flags & CE_HASHED)
                return;
        ce->ce_flags |= CE_HASHED;
-       ce->next = ce->dir_next = NULL;
+       ce->next = NULL;
        hash = hash_name(ce->name, ce_namelen(ce));
        pos = insert_hash(hash, ce, &istate->name_hash);
        if (pos) {
@@ -82,8 +140,8 @@ static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
                *pos = ce;
        }
 
-       if (ignore_case)
-               hash_index_entry_directories(istate, ce);
+       if (ignore_case && !(ce->ce_flags & CE_UNHASHED))
+               add_dir_entry(istate, ce);
 }
 
 static void lazy_init_name_hash(struct index_state *istate)
@@ -101,11 +159,33 @@ static void lazy_init_name_hash(struct index_state *istate)
 
 void add_name_hash(struct index_state *istate, struct cache_entry *ce)
 {
+       /* if already hashed, add reference to directory entries */
+       if (ignore_case && (ce->ce_flags & CE_STATE_MASK) == CE_STATE_MASK)
+               add_dir_entry(istate, ce);
+
        ce->ce_flags &= ~CE_UNHASHED;
        if (istate->name_hash_initialized)
                hash_index_entry(istate, ce);
 }
 
+/*
+ * We don't actually *remove* it, we can just mark it invalid so that
+ * we won't find it in lookups.
+ *
+ * Not only would we have to search the lists (simple enough), but
+ * we'd also have to rehash other hash buckets in case this makes the
+ * hash bucket empty (common). So it's much better to just mark
+ * it.
+ */
+void remove_name_hash(struct index_state *istate, struct cache_entry *ce)
+{
+       /* if already hashed, release reference to directory entries */
+       if (ignore_case && (ce->ce_flags & CE_STATE_MASK) == CE_HASHED)
+               remove_dir_entry(istate, ce);
+
+       ce->ce_flags |= CE_UNHASHED;
+}
+
 static int slow_same_name(const char *name1, int len1, const char *name2, int len2)
 {
        if (len1 != len2)
@@ -139,18 +219,7 @@ static int same_name(const struct cache_entry *ce, const char *name, int namelen
        if (!icase)
                return 0;
 
-       /*
-        * If the entry we're comparing is a filename (no trailing slash), then compare
-        * the lengths exactly.
-        */
-       if (name[namelen - 1] != '/')
-               return slow_same_name(name, namelen, ce->name, len);
-
-       /*
-        * For a directory, we point to an arbitrary cache_entry filename.  Just
-        * make sure the directory portion matches.
-        */
-       return slow_same_name(name, namelen, ce->name, namelen < len ? namelen : len);
+       return slow_same_name(name, namelen, ce->name, len);
 }
 
 struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
@@ -166,27 +235,54 @@ struct cache_entry *index_name_exists(struct index_state *istate, const char *na
                        if (same_name(ce, name, namelen, icase))
                                return ce;
                }
-               if (icase && name[namelen - 1] == '/')
-                       ce = ce->dir_next;
-               else
-                       ce = ce->next;
+               ce = ce->next;
        }
 
        /*
-        * Might be a submodule.  Despite submodules being directories,
+        * When looking for a directory (trailing '/'), it might be a
+        * submodule or a directory. Despite submodules being directories,
         * they are stored in the name hash without a closing slash.
-        * When ignore_case is 1, directories are stored in the name hash
-        * with their closing slash.
+        * When ignore_case is 1, directories are stored in a separate hash
+        * table *with* their closing slash.
         *
         * The side effect of this storage technique is we have need to
+        * lookup the directory in a separate hash table, and if not found
         * remove the slash from name and perform the lookup again without
         * the slash.  If a match is made, S_ISGITLINK(ce->mode) will be
         * true.
         */
        if (icase && name[namelen - 1] == '/') {
+               struct dir_entry *dir = find_dir_entry(istate, name, namelen);
+               if (dir && dir->nr)
+                       return dir->ce;
+
                ce = index_name_exists(istate, name, namelen - 1, icase);
                if (ce && S_ISGITLINK(ce->ce_mode))
                        return ce;
        }
        return NULL;
 }
+
+static int free_dir_entry(void *entry, void *unused)
+{
+       struct dir_entry *dir = entry;
+       while (dir) {
+               struct dir_entry *next = dir->next;
+               free(dir);
+               dir = next;
+       }
+       return 0;
+}
+
+void free_name_hash(struct index_state *istate)
+{
+       if (!istate->name_hash_initialized)
+               return;
+       istate->name_hash_initialized = 0;
+       if (ignore_case)
+               /* free directory entries */
+               for_each_hash(&istate->dir_hash, free_dir_entry, NULL);
+
+       free_hash(&istate->name_hash);
+       free_hash(&istate->dir_hash);
+}
diff --git a/path.c b/path.c
index d3d3f8b8ad75b9817df3014296aabc34b6a4eb14..2fdccc2f18367399b8247ee79ec8bf4934f596cb 100644 (file)
--- a/path.c
+++ b/path.c
 #include "strbuf.h"
 #include "string-list.h"
 
+#ifndef get_st_mode_bits
+/*
+ * The replacement lstat(2) we use on Cygwin is incomplete and
+ * may return wrong permission bits. Most of the time we do not care,
+ * but the callsites of this wrapper do care.
+ */
+int get_st_mode_bits(const char *path, int *mode)
+{
+       struct stat st;
+       if (lstat(path, &st) < 0)
+               return -1;
+       *mode = st.st_mode;
+       return 0;
+}
+#endif
+
 static char bad_path[] = "/bad-path/";
 
 static char *get_pathname(void)
@@ -391,7 +407,6 @@ const char *enter_repo(const char *path, int strict)
 
 int set_shared_perm(const char *path, int mode)
 {
-       struct stat st;
        int tweak, shared, orig_mode;
 
        if (!shared_repository) {
@@ -400,9 +415,8 @@ int set_shared_perm(const char *path, int mode)
                return 0;
        }
        if (!mode) {
-               if (lstat(path, &st) < 0)
+               if (get_st_mode_bits(path, &mode) < 0)
                        return -1;
-               mode = st.st_mode;
                orig_mode = mode;
        } else
                orig_mode = 0;
index eaba15f124b5d8247d39bd0844967cb5a9101ba8..70f19501d0e0c0dd625976519fd039d5c7a8b314 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "pkt-line.h"
 
+char packet_buffer[LARGE_PACKET_MAX];
 static const char *packet_trace_prefix = "git";
 static const char trace_key[] = "GIT_TRACE_PACKET";
 
@@ -46,38 +47,6 @@ static void packet_trace(const char *buf, unsigned int len, int write)
        strbuf_release(&out);
 }
 
-/*
- * Write a packetized stream, where each line is preceded by
- * its length (including the header) as a 4-byte hex number.
- * A length of 'zero' means end of stream (and a length of 1-3
- * would be an error).
- *
- * This is all pretty stupid, but we use this packetized line
- * format to make a streaming format possible without ever
- * over-running the read buffers. That way we'll never read
- * into what might be the pack data (which should go to another
- * process entirely).
- *
- * The writing side could use stdio, but since the reading
- * side can't, we stay with pure read/write interfaces.
- */
-ssize_t safe_write(int fd, const void *buf, ssize_t n)
-{
-       ssize_t nn = n;
-       while (n) {
-               int ret = xwrite(fd, buf, n);
-               if (ret > 0) {
-                       buf = (char *) buf + ret;
-                       n -= ret;
-                       continue;
-               }
-               if (!ret)
-                       die("write error (disk full?)");
-               die_errno("write error");
-       }
-       return nn;
-}
-
 /*
  * If we buffered things up above (we don't, but we should),
  * we'd flush it here
@@ -85,7 +54,7 @@ ssize_t safe_write(int fd, const void *buf, ssize_t n)
 void packet_flush(int fd)
 {
        packet_trace("0000", 4, 1);
-       safe_write(fd, "0000", 4);
+       write_or_die(fd, "0000", 4);
 }
 
 void packet_buf_flush(struct strbuf *buf)
@@ -121,7 +90,7 @@ void packet_write(int fd, const char *fmt, ...)
        va_start(args, fmt);
        n = format_packet(fmt, args);
        va_end(args);
-       safe_write(fd, buffer, n);
+       write_or_die(fd, buffer, n);
 }
 
 void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
@@ -135,13 +104,29 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
        strbuf_add(buf, buffer, n);
 }
 
-static int safe_read(int fd, void *buffer, unsigned size, int return_line_fail)
+static int get_packet_data(int fd, char **src_buf, size_t *src_size,
+                          void *dst, unsigned size, int options)
 {
-       ssize_t ret = read_in_full(fd, buffer, size);
-       if (ret < 0)
-               die_errno("read error");
-       else if (ret < size) {
-               if (return_line_fail)
+       ssize_t ret;
+
+       if (fd >= 0 && src_buf && *src_buf)
+               die("BUG: multiple sources given to packet_read");
+
+       /* Read up to "size" bytes from our source, whatever it is. */
+       if (src_buf && *src_buf) {
+               ret = size < *src_size ? size : *src_size;
+               memcpy(dst, *src_buf, ret);
+               *src_buf += ret;
+               *src_size -= ret;
+       } else {
+               ret = read_in_full(fd, dst, size);
+               if (ret < 0)
+                       die_errno("read error");
+       }
+
+       /* And complain if we didn't get enough bytes to satisfy the read. */
+       if (ret < size) {
+               if (options & PACKET_READ_GENTLE_ON_EOF)
                        return -1;
 
                die("The remote end hung up unexpectedly");
@@ -175,13 +160,14 @@ static int packet_length(const char *linelen)
        return len;
 }
 
-static int packet_read_internal(int fd, char *buffer, unsigned size, int return_line_fail)
+int packet_read(int fd, char **src_buf, size_t *src_len,
+               char *buffer, unsigned size, int options)
 {
        int len, ret;
        char linelen[4];
 
-       ret = safe_read(fd, linelen, 4, return_line_fail);
-       if (return_line_fail && ret < 0)
+       ret = get_packet_data(fd, src_buf, src_len, linelen, 4, options);
+       if (ret < 0)
                return ret;
        len = packet_length(linelen);
        if (len < 0)
@@ -193,50 +179,37 @@ static int packet_read_internal(int fd, char *buffer, unsigned size, int return_
        len -= 4;
        if (len >= size)
                die("protocol error: bad line length %d", len);
-       ret = safe_read(fd, buffer, len, return_line_fail);
-       if (return_line_fail && ret < 0)
+       ret = get_packet_data(fd, src_buf, src_len, buffer, len, options);
+       if (ret < 0)
                return ret;
+
+       if ((options & PACKET_READ_CHOMP_NEWLINE) &&
+           len && buffer[len-1] == '\n')
+               len--;
+
        buffer[len] = 0;
        packet_trace(buffer, len, 0);
        return len;
 }
 
-int packet_read(int fd, char *buffer, unsigned size)
+static char *packet_read_line_generic(int fd,
+                                     char **src, size_t *src_len,
+                                     int *dst_len)
 {
-       return packet_read_internal(fd, buffer, size, 1);
+       int len = packet_read(fd, src, src_len,
+                             packet_buffer, sizeof(packet_buffer),
+                             PACKET_READ_CHOMP_NEWLINE);
+       if (dst_len)
+               *dst_len = len;
+       return len ? packet_buffer : NULL;
 }
 
-int packet_read_line(int fd, char *buffer, unsigned size)
+char *packet_read_line(int fd, int *len_p)
 {
-       return packet_read_internal(fd, buffer, size, 0);
+       return packet_read_line_generic(fd, NULL, NULL, len_p);
 }
 
-int packet_get_line(struct strbuf *out,
-       char **src_buf, size_t *src_len)
+char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len)
 {
-       int len;
-
-       if (*src_len < 4)
-               return -1;
-       len = packet_length(*src_buf);
-       if (len < 0)
-               return -1;
-       if (!len) {
-               *src_buf += 4;
-               *src_len -= 4;
-               packet_trace("0000", 4, 0);
-               return 0;
-       }
-       if (*src_len < len)
-               return -2;
-
-       *src_buf += 4;
-       *src_len -= 4;
-       len -= 4;
-
-       strbuf_add(out, *src_buf, len);
-       *src_buf += len;
-       *src_len -= len;
-       packet_trace(out->buf, out->len, 0);
-       return len;
+       return packet_read_line_generic(-1, src, src_len, dst_len);
 }
index 8cfeb0c31c8661bf11ce78d3679a21cd030ffb75..0a838d1656171019c07fe802d615ecaf78412003 100644 (file)
@@ -5,16 +5,78 @@
 #include "strbuf.h"
 
 /*
- * Silly packetized line writing interface
+ * Write a packetized stream, where each line is preceded by
+ * its length (including the header) as a 4-byte hex number.
+ * A length of 'zero' means end of stream (and a length of 1-3
+ * would be an error).
+ *
+ * This is all pretty stupid, but we use this packetized line
+ * format to make a streaming format possible without ever
+ * over-running the read buffers. That way we'll never read
+ * into what might be the pack data (which should go to another
+ * process entirely).
+ *
+ * The writing side could use stdio, but since the reading
+ * side can't, we stay with pure read/write interfaces.
  */
 void packet_flush(int fd);
 void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 void packet_buf_flush(struct strbuf *buf);
 void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 
-int packet_read_line(int fd, char *buffer, unsigned size);
-int packet_read(int fd, char *buffer, unsigned size);
-int packet_get_line(struct strbuf *out, char **src_buf, size_t *src_len);
-ssize_t safe_write(int, const void *, ssize_t);
+/*
+ * Read a packetized line into the buffer, which must be at least size bytes
+ * long. The return value specifies the number of bytes read into the buffer.
+ *
+ * If src_buffer is not NULL (and nor is *src_buffer), it should point to a
+ * buffer containing the packet data to parse, of at least *src_len bytes.
+ * After the function returns, src_buf will be incremented and src_len
+ * decremented by the number of bytes consumed.
+ *
+ * If src_buffer (or *src_buffer) is NULL, then data is read from the
+ * descriptor "fd".
+ *
+ * If options does not contain PACKET_READ_GENTLE_ON_EOF, we will die under any
+ * of the following conditions:
+ *
+ *   1. Read error from descriptor.
+ *
+ *   2. Protocol error from the remote (e.g., bogus length characters).
+ *
+ *   3. Receiving a packet larger than "size" bytes.
+ *
+ *   4. Truncated output from the remote (e.g., we expected a packet but got
+ *      EOF, or we got a partial packet followed by EOF).
+ *
+ * If options does contain PACKET_READ_GENTLE_ON_EOF, we will not die on
+ * condition 4 (truncated input), but instead return -1. However, we will still
+ * die for the other 3 conditions.
+ *
+ * If options contains PACKET_READ_CHOMP_NEWLINE, a trailing newline (if
+ * present) is removed from the buffer before returning.
+ */
+#define PACKET_READ_GENTLE_ON_EOF (1u<<0)
+#define PACKET_READ_CHOMP_NEWLINE (1u<<1)
+int packet_read(int fd, char **src_buffer, size_t *src_len, char
+               *buffer, unsigned size, int options);
+
+/*
+ * Convenience wrapper for packet_read that is not gentle, and sets the
+ * CHOMP_NEWLINE option. The return value is NULL for a flush packet,
+ * and otherwise points to a static buffer (that may be overwritten by
+ * subsequent calls). If the size parameter is not NULL, the length of the
+ * packet is written to it.
+ */
+char *packet_read_line(int fd, int *size);
+
+/*
+ * Same as packet_read_line, but read from a buf rather than a descriptor;
+ * see packet_read for details on how src_* is used.
+ */
+char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
+
+#define DEFAULT_PACKET_MAX 1000
+#define LARGE_PACKET_MAX 65520
+extern char packet_buffer[LARGE_PACKET_MAX];
 
 #endif
index 670a06bc7996da83650d47a72685315f918e26b2..5a9704f4e5b974a46bc5486373a26816c5b3b4bd 100644 (file)
@@ -46,7 +46,7 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache
 {
        struct cache_entry *old = istate->cache[nr];
 
-       remove_name_hash(old);
+       remove_name_hash(istate, old);
        set_index_entry(istate, nr, ce);
        istate->cache_changed = 1;
 }
@@ -460,7 +460,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)
        struct cache_entry *ce = istate->cache[pos];
 
        record_resolve_undo(istate, ce);
-       remove_name_hash(ce);
+       remove_name_hash(istate, ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
@@ -483,7 +483,7 @@ void remove_marked_cache_entries(struct index_state *istate)
 
        for (i = j = 0; i < istate->cache_nr; i++) {
                if (ce_array[i]->ce_flags & CE_REMOVE)
-                       remove_name_hash(ce_array[i]);
+                       remove_name_hash(istate, ce_array[i]);
                else
                        ce_array[j++] = ce_array[i];
        }
@@ -1515,8 +1515,7 @@ int discard_index(struct index_state *istate)
        istate->cache_changed = 0;
        istate->timestamp.sec = 0;
        istate->timestamp.nsec = 0;
-       istate->name_hash_initialized = 0;
-       free_hash(&istate->name_hash);
+       free_name_hash(istate);
        cache_tree_free(&(istate->cache_tree));
        istate->initialized = 0;
 
index 933c69ac2627c1903276b92b457843e12649f39e..93a09a64c3b1689b0f29a26c6cdd601ef298b975 100644 (file)
@@ -76,21 +76,82 @@ struct discovery {
        char *buf_alloc;
        char *buf;
        size_t len;
+       struct ref *refs;
        unsigned proto_git : 1;
 };
 static struct discovery *last_discovery;
 
+static struct ref *parse_git_refs(struct discovery *heads, int for_push)
+{
+       struct ref *list = NULL;
+       get_remote_heads(-1, heads->buf, heads->len, &list,
+                        for_push ? REF_NORMAL : 0, NULL);
+       return list;
+}
+
+static struct ref *parse_info_refs(struct discovery *heads)
+{
+       char *data, *start, *mid;
+       char *ref_name;
+       int i = 0;
+
+       struct ref *refs = NULL;
+       struct ref *ref = NULL;
+       struct ref *last_ref = NULL;
+
+       data = heads->buf;
+       start = NULL;
+       mid = data;
+       while (i < heads->len) {
+               if (!start) {
+                       start = &data[i];
+               }
+               if (data[i] == '\t')
+                       mid = &data[i];
+               if (data[i] == '\n') {
+                       if (mid - start != 40)
+                               die("%sinfo/refs not valid: is this a git repository?", url);
+                       data[i] = 0;
+                       ref_name = mid + 1;
+                       ref = xmalloc(sizeof(struct ref) +
+                                     strlen(ref_name) + 1);
+                       memset(ref, 0, sizeof(struct ref));
+                       strcpy(ref->name, ref_name);
+                       get_sha1_hex(start, ref->old_sha1);
+                       if (!refs)
+                               refs = ref;
+                       if (last_ref)
+                               last_ref->next = ref;
+                       last_ref = ref;
+                       start = NULL;
+               }
+               i++;
+       }
+
+       ref = alloc_ref("HEAD");
+       if (!http_fetch_ref(url, ref) &&
+           !resolve_remote_symref(ref, refs)) {
+               ref->next = refs;
+               refs = ref;
+       } else {
+               free(ref);
+       }
+
+       return refs;
+}
+
 static void free_discovery(struct discovery *d)
 {
        if (d) {
                if (d == last_discovery)
                        last_discovery = NULL;
                free(d->buf_alloc);
+               free_refs(d->refs);
                free(d);
        }
 }
 
-static struct discovery* discover_refs(const char *service)
+static struct discovery* discover_refs(const char *service, int for_push)
 {
        struct strbuf exp = STRBUF_INIT;
        struct strbuf type = STRBUF_INIT;
@@ -138,32 +199,35 @@ static struct discovery* discover_refs(const char *service)
        if (maybe_smart &&
            (5 <= last->len && last->buf[4] == '#') &&
            !strbuf_cmp(&exp, &type)) {
+               char *line;
+
                /*
                 * smart HTTP response; validate that the service
                 * pkt-line matches our request.
                 */
-               if (packet_get_line(&buffer, &last->buf, &last->len) <= 0)
-                       die("%s has invalid packet header", refs_url);
-               if (buffer.len && buffer.buf[buffer.len - 1] == '\n')
-                       strbuf_setlen(&buffer, buffer.len - 1);
+               line = packet_read_line_buf(&last->buf, &last->len, NULL);
 
                strbuf_reset(&exp);
                strbuf_addf(&exp, "# service=%s", service);
-               if (strbuf_cmp(&exp, &buffer))
-                       die("invalid server response; got '%s'", buffer.buf);
+               if (strcmp(line, exp.buf))
+                       die("invalid server response; got '%s'", line);
                strbuf_release(&exp);
 
                /* The header can include additional metadata lines, up
                 * until a packet flush marker.  Ignore these now, but
                 * in the future we might start to scan them.
                 */
-               strbuf_reset(&buffer);
-               while (packet_get_line(&buffer, &last->buf, &last->len) > 0)
-                       strbuf_reset(&buffer);
+               while (packet_read_line_buf(&last->buf, &last->len, NULL))
+                       ;
 
                last->proto_git = 1;
        }
 
+       if (last->proto_git)
+               last->refs = parse_git_refs(last, for_push);
+       else
+               last->refs = parse_info_refs(last);
+
        free(refs_url);
        strbuf_release(&exp);
        strbuf_release(&type);
@@ -172,99 +236,16 @@ static struct discovery* discover_refs(const char *service)
        return last;
 }
 
-static int write_discovery(int in, int out, void *data)
-{
-       struct discovery *heads = data;
-       int err = 0;
-       if (write_in_full(out, heads->buf, heads->len) != heads->len)
-               err = 1;
-       close(out);
-       return err;
-}
-
-static struct ref *parse_git_refs(struct discovery *heads, int for_push)
-{
-       struct ref *list = NULL;
-       struct async async;
-
-       memset(&async, 0, sizeof(async));
-       async.proc = write_discovery;
-       async.data = heads;
-       async.out = -1;
-
-       if (start_async(&async))
-               die("cannot start thread to parse advertised refs");
-       get_remote_heads(async.out, &list,
-                       for_push ? REF_NORMAL : 0, NULL);
-       close(async.out);
-       if (finish_async(&async))
-               die("ref parsing thread failed");
-       return list;
-}
-
-static struct ref *parse_info_refs(struct discovery *heads)
-{
-       char *data, *start, *mid;
-       char *ref_name;
-       int i = 0;
-
-       struct ref *refs = NULL;
-       struct ref *ref = NULL;
-       struct ref *last_ref = NULL;
-
-       data = heads->buf;
-       start = NULL;
-       mid = data;
-       while (i < heads->len) {
-               if (!start) {
-                       start = &data[i];
-               }
-               if (data[i] == '\t')
-                       mid = &data[i];
-               if (data[i] == '\n') {
-                       if (mid - start != 40)
-                               die("%sinfo/refs not valid: is this a git repository?", url);
-                       data[i] = 0;
-                       ref_name = mid + 1;
-                       ref = xmalloc(sizeof(struct ref) +
-                                     strlen(ref_name) + 1);
-                       memset(ref, 0, sizeof(struct ref));
-                       strcpy(ref->name, ref_name);
-                       get_sha1_hex(start, ref->old_sha1);
-                       if (!refs)
-                               refs = ref;
-                       if (last_ref)
-                               last_ref->next = ref;
-                       last_ref = ref;
-                       start = NULL;
-               }
-               i++;
-       }
-
-       ref = alloc_ref("HEAD");
-       if (!http_fetch_ref(url, ref) &&
-           !resolve_remote_symref(ref, refs)) {
-               ref->next = refs;
-               refs = ref;
-       } else {
-               free(ref);
-       }
-
-       return refs;
-}
-
 static struct ref *get_refs(int for_push)
 {
        struct discovery *heads;
 
        if (for_push)
-               heads = discover_refs("git-receive-pack");
+               heads = discover_refs("git-receive-pack", for_push);
        else
-               heads = discover_refs("git-upload-pack");
+               heads = discover_refs("git-upload-pack", for_push);
 
-       if (heads->proto_git)
-               return parse_git_refs(heads, for_push);
-       return parse_info_refs(heads);
+       return heads->refs;
 }
 
 static void output_refs(struct ref *refs)
@@ -278,7 +259,6 @@ static void output_refs(struct ref *refs)
        }
        printf("\n");
        fflush(stdout);
-       free_refs(refs);
 }
 
 struct rpc_state {
@@ -308,7 +288,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
 
        if (!avail) {
                rpc->initial_buffer = 0;
-               avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
+               avail = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
                if (!avail)
                        return 0;
                rpc->pos = 0;
@@ -425,7 +405,7 @@ static int post_rpc(struct rpc_state *rpc)
                        break;
                }
 
-               n = packet_read_line(rpc->out, buf, left);
+               n = packet_read(rpc->out, NULL, NULL, buf, left, 0);
                if (!n)
                        break;
                rpc->len += n;
@@ -579,7 +559,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
        rpc->hdr_accept = strbuf_detach(&buf, NULL);
 
        while (!err) {
-               int n = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
+               int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
                if (!n)
                        break;
                rpc->pos = 0;
@@ -685,7 +665,7 @@ static int fetch_git(struct discovery *heads,
 
        err = rpc_service(&rpc, heads);
        if (rpc.result.len)
-               safe_write(1, rpc.result.buf, rpc.result.len);
+               write_or_die(1, rpc.result.buf, rpc.result.len);
        strbuf_release(&rpc.result);
        strbuf_release(&preamble);
        free(depth_arg);
@@ -694,7 +674,7 @@ static int fetch_git(struct discovery *heads,
 
 static int fetch(int nr_heads, struct ref **to_fetch)
 {
-       struct discovery *d = discover_refs("git-upload-pack");
+       struct discovery *d = discover_refs("git-upload-pack", 0);
        if (d->proto_git)
                return fetch_git(d, nr_heads, to_fetch);
        else
@@ -805,7 +785,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
 
        err = rpc_service(&rpc, heads);
        if (rpc.result.len)
-               safe_write(1, rpc.result.buf, rpc.result.len);
+               write_or_die(1, rpc.result.buf, rpc.result.len);
        strbuf_release(&rpc.result);
        free(argv);
        return err;
@@ -813,7 +793,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
 
 static int push(int nr_spec, char **specs)
 {
-       struct discovery *heads = discover_refs("git-receive-pack");
+       struct discovery *heads = discover_refs("git-receive-pack", 1);
        int ret;
 
        if (heads->proto_git)
index 57f36e14da7a8a33ae8c02a4bf46492936c79fdd..ca1edd901e4e64cb497b790f675537283c4ee1c0 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1322,9 +1322,6 @@ int match_push_refs(struct ref *src, struct ref **dst,
                const struct refspec *pat = NULL;
                char *dst_name;
 
-               if (ref->peer_ref)
-                       continue;
-
                dst_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_SRC, &pat);
                if (!dst_name)
                        continue;
index 72b46125b719861641a55ee8fd534e0d1f47a94f..639eb9c59f355e46bdd53cf13fd94e8a6a9537da 100644 (file)
@@ -118,7 +118,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
        struct cache_entry *ce;
        struct string_list_item *item;
        struct resolve_undo_info *ru;
-       int i, err = 0;
+       int i, err = 0, matched;
 
        if (!istate->resolve_undo)
                return pos;
@@ -137,6 +137,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
        ru = item->util;
        if (!ru)
                return pos;
+       matched = ce->ce_flags & CE_MATCHED;
        remove_index_entry_at(istate, pos);
        for (i = 0; i < 3; i++) {
                struct cache_entry *nce;
@@ -144,6 +145,8 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
                        continue;
                nce = make_cache_entry(ru->mode[i], ru->sha1[i],
                                       ce->name, i + 1, 0);
+               if (matched)
+                       nce->ce_flags |= CE_MATCHED;
                if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
                        err = 1;
                        error("cannot unmerge '%s'", ce->name);
@@ -156,6 +159,20 @@ int unmerge_index_entry_at(struct index_state *istate, int pos)
        return unmerge_index_entry_at(istate, pos);
 }
 
+void unmerge_marked_index(struct index_state *istate)
+{
+       int i;
+
+       if (!istate->resolve_undo)
+               return;
+
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
+               if (ce->ce_flags & CE_MATCHED)
+                       i = unmerge_index_entry_at(istate, i);
+       }
+}
+
 void unmerge_index(struct index_state *istate, const char **pathspec)
 {
        int i;
index 845876911db978c6262dacd9aa122ce9d55bf234..7a30206aad1fdee74f7e7b6e5967f9f8d9048dbf 100644 (file)
@@ -12,5 +12,6 @@ extern struct string_list *resolve_undo_read(const char *, unsigned long);
 extern void resolve_undo_clear_index(struct index_state *);
 extern int unmerge_index_entry_at(struct index_state *, int);
 extern void unmerge_index(struct index_state *, const char **);
+extern void unmerge_marked_index(struct index_state *);
 
 #endif
index ef6020541282770b9edc4c921cadb7ab1506da56..71e62d831229773a9af2c4a81d72be5b33d3c921 100644 (file)
@@ -709,7 +709,7 @@ static int still_interesting(struct commit_list *src, unsigned long date, int sl
         * Does the destination list contain entries with a date
         * before the source list? Definitely _not_ done.
         */
-       if (date < src->item->date)
+       if (date <= src->item->date)
                return SLOP;
 
        /*
@@ -1970,6 +1970,22 @@ static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs,
        return st;
 }
 
+static void remove_treesame_parents(struct commit *commit)
+{
+       struct commit_list **pp, *p;
+
+       pp = &commit->parents;
+       while ((p = *pp) != NULL) {
+               struct commit *parent = p->item;
+               if (parent->object.flags & TREESAME) {
+                       *pp = p->next;
+                       free(p);
+                       continue;
+               }
+               pp = &p->next;
+       }
+}
+
 static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail)
 {
        struct commit_list *p;
@@ -2022,10 +2038,18 @@ static struct commit_list **simplify_one(struct rev_info *revs, struct commit *c
                if (revs->first_parent_only)
                        break;
        }
-       if (!revs->first_parent_only)
-               cnt = remove_duplicate_parents(commit);
-       else
+
+       if (revs->first_parent_only) {
                cnt = 1;
+       } else {
+               /*
+                * A merge with a tree-same parent is useless
+                */
+               if (commit->parents && commit->parents->next)
+                       remove_treesame_parents(commit);
+
+               cnt = remove_duplicate_parents(commit);
+       }
 
        /*
         * It is possible that we are a merge and one side branch
index 5da09ee3efa976b503cba5d13e347aad0f6c764c..01bd2b7c07719c9628bba13e34b581dc1fdbc0af 100644 (file)
@@ -138,7 +138,7 @@ struct rev_info {
        int             reroll_count;
        char            *message_id;
        struct string_list *ref_message_ids;
-       const char      *add_signoff;
+       int             add_signoff;
        const char      *extra_headers;
        const char      *log_reencode;
        const char      *subject_prefix;
index 97ab336097bfb45c63aec739a6eef2df26cc4790..7d172ef37f7b185f9586ce9015a08481d0bd259c 100644 (file)
@@ -106,15 +106,11 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
 static int receive_status(int in, struct ref *refs)
 {
        struct ref *hint;
-       char line[1000];
        int ret = 0;
-       int len = packet_read_line(in, line, sizeof(line));
-       if (len < 10 || memcmp(line, "unpack ", 7))
+       char *line = packet_read_line(in, NULL);
+       if (prefixcmp(line, "unpack "))
                return error("did not receive remote status");
-       if (memcmp(line, "unpack ok\n", 10)) {
-               char *p = line + strlen(line) - 1;
-               if (*p == '\n')
-                       *p = '\0';
+       if (strcmp(line, "unpack ok")) {
                error("unpack failed: %s", line + 7);
                ret = -1;
        }
@@ -122,17 +118,15 @@ static int receive_status(int in, struct ref *refs)
        while (1) {
                char *refname;
                char *msg;
-               len = packet_read_line(in, line, sizeof(line));
-               if (!len)
+               line = packet_read_line(in, NULL);
+               if (!line)
                        break;
-               if (len < 3 ||
-                   (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
-                       fprintf(stderr, "protocol error: %s\n", line);
+               if (prefixcmp(line, "ok ") && prefixcmp(line, "ng ")) {
+                       error("invalid ref status from remote: %s", line);
                        ret = -1;
                        break;
                }
 
-               line[strlen(line)-1] = '\0';
                refname = line + 3;
                msg = strchr(refname, ' ');
                if (msg)
@@ -281,7 +275,7 @@ int send_pack(struct send_pack_args *args,
                        send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
                }
        } else {
-               safe_write(out, req_buf.buf, req_buf.len);
+               write_or_die(out, req_buf.buf, req_buf.len);
                packet_flush(out);
        }
        strbuf_release(&req_buf);
index aef5e8a0170c5b337f1aa3ac7a35be3b54957729..baa031052e4e3f77bb6d4a257a5669ca5a5d617e 100644 (file)
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
 const char sign_off_header[] = "Signed-off-by: ";
+static const char cherry_picked_prefix[] = "(cherry picked from commit ";
+
+static int is_rfc2822_line(const char *buf, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               int ch = buf[i];
+               if (ch == ':')
+                       return 1;
+               if (!isalnum(ch) && ch != '-')
+                       break;
+       }
+
+       return 0;
+}
+
+static int is_cherry_picked_from_line(const char *buf, int len)
+{
+       /*
+        * We only care that it looks roughly like (cherry picked from ...)
+        */
+       return len > strlen(cherry_picked_prefix) + 1 &&
+               !prefixcmp(buf, cherry_picked_prefix) && buf[len - 1] == ')';
+}
+
+/*
+ * Returns 0 for non-conforming footer
+ * Returns 1 for conforming footer
+ * Returns 2 when sob exists within conforming footer
+ * Returns 3 when sob exists within conforming footer as last entry
+ */
+static int has_conforming_footer(struct strbuf *sb, struct strbuf *sob,
+       int ignore_footer)
+{
+       char prev;
+       int i, k;
+       int len = sb->len - ignore_footer;
+       const char *buf = sb->buf;
+       int found_sob = 0;
+
+       /* footer must end with newline */
+       if (!len || buf[len - 1] != '\n')
+               return 0;
+
+       prev = '\0';
+       for (i = len - 1; i > 0; i--) {
+               char ch = buf[i];
+               if (prev == '\n' && ch == '\n') /* paragraph break */
+                       break;
+               prev = ch;
+       }
+
+       /* require at least one blank line */
+       if (prev != '\n' || buf[i] != '\n')
+               return 0;
+
+       /* advance to start of last paragraph */
+       while (i < len - 1 && buf[i] == '\n')
+               i++;
+
+       for (; i < len; i = k) {
+               int found_rfc2822;
+
+               for (k = i; k < len && buf[k] != '\n'; k++)
+                       ; /* do nothing */
+               k++;
+
+               found_rfc2822 = is_rfc2822_line(buf + i, k - i - 1);
+               if (found_rfc2822 && sob &&
+                   !strncmp(buf + i, sob->buf, sob->len))
+                       found_sob = k;
+
+               if (!(found_rfc2822 ||
+                     is_cherry_picked_from_line(buf + i, k - i - 1)))
+                       return 0;
+       }
+       if (found_sob == i)
+               return 3;
+       if (found_sob)
+               return 2;
+       return 1;
+}
 
 static void remove_sequencer_state(void)
 {
@@ -237,7 +320,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        rollback_lock_file(&index_lock);
 
        if (opts->signoff)
-               append_signoff(msgbuf, 0);
+               append_signoff(msgbuf, 0, 0);
 
        if (!clean) {
                int i;
@@ -496,7 +579,9 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                }
 
                if (opts->record_origin) {
-                       strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+                       if (!has_conforming_footer(&msgbuf, NULL, 0))
+                               strbuf_addch(&msgbuf, '\n');
+                       strbuf_addstr(&msgbuf, cherry_picked_prefix);
                        strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
                        strbuf_addstr(&msgbuf, ")\n");
                }
@@ -1021,62 +1106,67 @@ int sequencer_pick_revisions(struct replay_opts *opts)
        return pick_commits(todo_list, opts);
 }
 
-static int ends_rfc2822_footer(struct strbuf *sb, int ignore_footer)
-{
-       int ch;
-       int hit = 0;
-       int i, j, k;
-       int len = sb->len - ignore_footer;
-       int first = 1;
-       const char *buf = sb->buf;
-
-       for (i = len - 1; i > 0; i--) {
-               if (hit && buf[i] == '\n')
-                       break;
-               hit = (buf[i] == '\n');
-       }
-
-       while (i < len - 1 && buf[i] == '\n')
-               i++;
-
-       for (; i < len; i = k) {
-               for (k = i; k < len && buf[k] != '\n'; k++)
-                       ; /* do nothing */
-               k++;
-
-               if ((buf[k] == ' ' || buf[k] == '\t') && !first)
-                       continue;
-
-               first = 0;
-
-               for (j = 0; i + j < len; j++) {
-                       ch = buf[i + j];
-                       if (ch == ':')
-                               break;
-                       if (isalnum(ch) ||
-                           (ch == '-'))
-                               continue;
-                       return 0;
-               }
-       }
-       return 1;
-}
-
-void append_signoff(struct strbuf *msgbuf, int ignore_footer)
+void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
 {
+       unsigned no_dup_sob = flag & APPEND_SIGNOFF_DEDUP;
        struct strbuf sob = STRBUF_INIT;
-       int i;
+       int has_footer;
 
        strbuf_addstr(&sob, sign_off_header);
        strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"),
                                getenv("GIT_COMMITTER_EMAIL")));
        strbuf_addch(&sob, '\n');
-       for (i = msgbuf->len - 1 - ignore_footer; i > 0 && msgbuf->buf[i - 1] != '\n'; i--)
-               ; /* do nothing */
-       if (prefixcmp(msgbuf->buf + i, sob.buf)) {
-               if (!i || !ends_rfc2822_footer(msgbuf, ignore_footer))
-                       strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, "\n", 1);
-               strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0, sob.buf, sob.len);
+
+       /*
+        * If the whole message buffer is equal to the sob, pretend that we
+        * found a conforming footer with a matching sob
+        */
+       if (msgbuf->len - ignore_footer == sob.len &&
+           !strncmp(msgbuf->buf, sob.buf, sob.len))
+               has_footer = 3;
+       else
+               has_footer = has_conforming_footer(msgbuf, &sob, ignore_footer);
+
+       if (!has_footer) {
+               const char *append_newlines = NULL;
+               size_t len = msgbuf->len - ignore_footer;
+
+               if (!len) {
+                       /*
+                        * The buffer is completely empty.  Leave foom for
+                        * the title and body to be filled in by the user.
+                        */
+                       append_newlines = "\n\n";
+               } else if (msgbuf->buf[len - 1] != '\n') {
+                       /*
+                        * Incomplete line.  Complete the line and add a
+                        * blank one so that there is an empty line between
+                        * the message body and the sob.
+                        */
+                       append_newlines = "\n\n";
+               } else if (len == 1) {
+                       /*
+                        * Buffer contains a single newline.  Add another
+                        * so that we leave room for the title and body.
+                        */
+                       append_newlines = "\n";
+               } else if (msgbuf->buf[len - 2] != '\n') {
+                       /*
+                        * Buffer ends with a single newline.  Add another
+                        * so that there is an empty line between the message
+                        * body and the sob.
+                        */
+                       append_newlines = "\n";
+               } /* else, the buffer already ends with two newlines. */
+
+               if (append_newlines)
+                       strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
+                               append_newlines, strlen(append_newlines));
        }
+
+       if (has_footer != 3 && (!no_dup_sob || has_footer != 2))
+               strbuf_splice(msgbuf, msgbuf->len - ignore_footer, 0,
+                               sob.buf, sob.len);
+
        strbuf_release(&sob);
 }
index 9d57d57524d411c54d55346a5ecff67bc256595c..1fc22dcabe132cd437e7b0d3c56588c8cd7e63f2 100644 (file)
@@ -6,6 +6,8 @@
 #define SEQ_TODO_FILE  "sequencer/todo"
 #define SEQ_OPTS_FILE  "sequencer/opts"
 
+#define APPEND_SIGNOFF_DEDUP (1u << 0)
+
 enum replay_action {
        REPLAY_REVERT,
        REPLAY_PICK
@@ -48,6 +50,6 @@ int sequencer_pick_revisions(struct replay_opts *opts);
 
 extern const char sign_off_header[];
 
-void append_signoff(struct strbuf *msgbuf, int ignore_footer);
+void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag);
 
 #endif
index 16967d3b9a86dc481a5161f0a98220e05790ca01..0ed23981b363a07c6f4c5b930b1a5991d5c8a622 100644 (file)
@@ -124,8 +124,13 @@ int safe_create_leading_directories(char *path)
                        }
                }
                else if (mkdir(path, 0777)) {
-                       *pos = '/';
-                       return -1;
+                       if (errno == EEXIST &&
+                           !stat(path, &st) && S_ISDIR(st.st_mode)) {
+                               ; /* somebody created it since we checked */
+                       } else {
+                               *pos = '/';
+                               return -1;
+                       }
                }
                else if (adjust_shared_perm(path)) {
                        *pos = '/';
@@ -1266,6 +1271,10 @@ int check_sha1_signature(const unsigned char *sha1, void *map,
                char buf[1024 * 16];
                ssize_t readlen = read_istream(st, buf, sizeof(buf));
 
+               if (readlen < 0) {
+                       close_istream(st);
+                       return -1;
+               }
                if (!readlen)
                        break;
                git_SHA1_Update(&c, buf, readlen);
index 2fbda48e02f167d08aa19210951712b85f40b602..3820f28ae757cce54a95014629ade4f7feb56efc 100644 (file)
@@ -594,7 +594,7 @@ struct object *peel_to_type(const char *name, int namelen,
        while (1) {
                if (!o || (!o->parsed && !parse_object(o->sha1)))
                        return NULL;
-               if (o->type == expected_type)
+               if (expected_type == OBJ_ANY || o->type == expected_type)
                        return o;
                if (o->type == OBJ_TAG)
                        o = ((struct tag*) o)->tagged;
@@ -645,6 +645,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                expected_type = OBJ_TREE;
        else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
                expected_type = OBJ_BLOB;
+       else if (!prefixcmp(sp, "object}"))
+               expected_type = OBJ_ANY;
        else if (sp[0] == '}')
                expected_type = OBJ_NONE;
        else if (sp[0] == '/')
@@ -654,6 +656,8 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
 
        if (expected_type == OBJ_COMMIT)
                lookup_flags = GET_SHA1_COMMITTISH;
+       else if (expected_type == OBJ_TREE)
+               lookup_flags = GET_SHA1_TREEISH;
 
        if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
                return -1;
index d5ffa1c8919a6db750606c78a1b44d8618fa35a5..d1125f5c528b680f915bb2fbf54231cc8c3b2ec0 100644 (file)
@@ -1,3 +1,4 @@
+#include "cache.h"
 #include "pkt-line.h"
 #include "sideband.h"
 
@@ -37,7 +38,7 @@ int recv_sideband(const char *me, int in_stream, int out)
 
        while (1) {
                int band, len;
-               len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX);
+               len = packet_read(in_stream, NULL, NULL, buf + pf, LARGE_PACKET_MAX, 0);
                if (len == 0)
                        break;
                if (len < 1) {
@@ -108,7 +109,7 @@ int recv_sideband(const char *me, int in_stream, int out)
                        } while (len);
                        continue;
                case 1:
-                       safe_write(out, buf + pf+1, len);
+                       write_or_die(out, buf + pf+1, len);
                        continue;
                default:
                        fprintf(stderr, "%s: protocol error: bad band #%d\n",
@@ -138,12 +139,12 @@ ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet
                if (0 <= band) {
                        sprintf(hdr, "%04x", n + 5);
                        hdr[4] = band;
-                       safe_write(fd, hdr, 5);
+                       write_or_die(fd, hdr, 5);
                } else {
                        sprintf(hdr, "%04x", n + 4);
-                       safe_write(fd, hdr, 4);
+                       write_or_die(fd, hdr, 4);
                }
-               safe_write(fd, p, n);
+               write_or_die(fd, p, n);
                p += n;
                sz -= n;
        }
index d72db35d1e0dc109f75b292762013c11b86426aa..e46bed0b0158c0253bacb2a3db028770ad221666 100644 (file)
@@ -4,9 +4,6 @@
 #define SIDEBAND_PROTOCOL_ERROR -2
 #define SIDEBAND_REMOTE_ERROR -1
 
-#define DEFAULT_PACKET_MAX 1000
-#define LARGE_PACKET_MAX 65520
-
 int recv_sideband(const char *me, int in_stream, int out);
 ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
 
index 4d978e54e4fb9d84e81977539ffa083534ef2968..cabcd9d1577d89c5e944a4c12e3a8e6af901078c 100644 (file)
@@ -237,7 +237,7 @@ static read_method_decl(filtered)
                if (!fs->input_finished) {
                        fs->i_end = read_istream(fs->upstream, fs->ibuf, FILTER_BUFFER);
                        if (fs->i_end < 0)
-                               break;
+                               return -1;
                        if (fs->i_end)
                                continue;
                }
@@ -309,7 +309,7 @@ static read_method_decl(loose)
                        st->z_state = z_done;
                        break;
                }
-               if (status != Z_OK && status != Z_BUF_ERROR) {
+               if (status != Z_OK && (status != Z_BUF_ERROR || total_read < sz)) {
                        git_inflate_end(&st->z);
                        st->z_state = z_error;
                        return -1;
@@ -514,6 +514,8 @@ int stream_blob_to_fd(int fd, unsigned const char *sha1, struct stream_filter *f
                ssize_t wrote, holeto;
                ssize_t readlen = read_istream(st, buf, sizeof(buf));
 
+               if (readlen < 0)
+                       goto close_and_exit;
                if (!readlen)
                        break;
                if (can_seek && sizeof(buf) == readlen) {
index 9ba149654322840323cf2d8f4980fa09e56f4068..975bc87e48b33e70bd7c38e82b7f59e34bb81ca3 100644 (file)
@@ -261,7 +261,7 @@ void show_submodule_summary(FILE *f, const char *path,
                const char *del, const char *add, const char *reset)
 {
        struct rev_info rev;
-       struct commit *left = left, *right = right;
+       struct commit *left = NULL, *right = NULL;
        const char *message = NULL;
        struct strbuf sb = STRBUF_INIT;
        int fast_forward = 0, fast_backward = 0;
@@ -275,10 +275,8 @@ void show_submodule_summary(FILE *f, const char *path,
        else if (!(left = lookup_commit_reference(one)) ||
                 !(right = lookup_commit_reference(two)))
                message = "(commits not present)";
-
-       if (!message &&
-           prepare_submodule_summary(&rev, path, left, right,
-                                       &fast_forward, &fast_backward))
+       else if (prepare_submodule_summary(&rev, path, left, right,
+                                          &fast_forward, &fast_backward))
                message = "(revision walker failed)";
 
        if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
@@ -302,11 +300,12 @@ void show_submodule_summary(FILE *f, const char *path,
                strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset);
        fwrite(sb.buf, sb.len, 1, f);
 
-       if (!message) {
+       if (!message) /* only NULL if we succeeded in setting up the walk */
                print_submodule_summary(&rev, f, del, add, reset);
+       if (left)
                clear_commit_marks(left, ~0);
+       if (right)
                clear_commit_marks(right, ~0);
-       }
 
        strbuf_release(&sb);
 }
index e4128e5769722473f7fd1024fdf8a5100bd8ccf6..9b41fe7a9f55b3803aa52cd26c9f0af4e34830ee 100644 (file)
--- a/t/README
+++ b/t/README
@@ -92,17 +92,26 @@ appropriately before running "make".
        This causes additional long-running tests to be run (where
        available), for more exhaustive testing.
 
---valgrind::
-       Execute all Git binaries with valgrind and exit with status
-       126 on errors (just like regular tests, this will only stop
-       the test script when running under -i).  Valgrind errors
-       go to stderr, so you might want to pass the -v option, too.
+--valgrind=<tool>::
+       Execute all Git binaries under valgrind tool <tool> and exit
+       with status 126 on errors (just like regular tests, this will
+       only stop the test script when running under -i).
 
        Since it makes no sense to run the tests with --valgrind and
        not see any output, this option implies --verbose.  For
        convenience, it also implies --tee.
 
-       Note that valgrind is run with the option --leak-check=no,
+       <tool> defaults to 'memcheck', just like valgrind itself.
+       Other particularly useful choices include 'helgrind' and
+       'drd', but you may use any tool recognized by your valgrind
+       installation.
+
+       As a special case, <tool> can be 'memcheck-fast', which uses
+       memcheck but disables --track-origins.  Use this if you are
+       running tests in bulk, to see if there are _any_ memory
+       issues.
+
+       Note that memcheck is run with the option --leak-check=no,
        as the git process is short-lived and some errors are not
        interesting. In order to run a single command under the same
        conditions manually, you should set GIT_VALGRIND to point to
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
new file mode 100755 (executable)
index 0000000..3f87051
--- /dev/null
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+test_description='see how we handle various forms of corruption'
+. ./test-lib.sh
+
+# convert "1234abcd" to ".git/objects/12/34abcd"
+obj_to_file() {
+       echo "$(git rev-parse --git-dir)/objects/$(git rev-parse "$1" | sed 's,..,&/,')"
+}
+
+# Convert byte at offset "$2" of object "$1" into '\0'
+corrupt_byte() {
+       obj_file=$(obj_to_file "$1") &&
+       chmod +w "$obj_file" &&
+       printf '\0' | dd of="$obj_file" bs=1 seek="$2" conv=notrunc
+}
+
+test_expect_success 'setup corrupt repo' '
+       git init bit-error &&
+       (
+               cd bit-error &&
+               test_commit content &&
+               corrupt_byte HEAD:content.t 10
+       )
+'
+
+test_expect_success 'setup repo with missing object' '
+       git init missing &&
+       (
+               cd missing &&
+               test_commit content &&
+               rm -f "$(obj_to_file HEAD:content.t)"
+       )
+'
+
+test_expect_success 'setup repo with misnamed object' '
+       git init misnamed &&
+       (
+               cd misnamed &&
+               test_commit content &&
+               good=$(obj_to_file HEAD:content.t) &&
+               blob=$(echo corrupt | git hash-object -w --stdin) &&
+               bad=$(obj_to_file $blob) &&
+               rm -f "$good" &&
+               mv "$bad" "$good"
+       )
+'
+
+test_expect_success 'streaming a corrupt blob fails' '
+       (
+               cd bit-error &&
+               test_must_fail git cat-file blob HEAD:content.t
+       )
+'
+
+test_expect_success 'read-tree -u detects bit-errors in blobs' '
+       (
+               cd bit-error &&
+               rm -f content.t &&
+               test_must_fail git read-tree --reset -u HEAD
+       )
+'
+
+test_expect_success 'read-tree -u detects missing objects' '
+       (
+               cd missing &&
+               rm -f content.t &&
+               test_must_fail git read-tree --reset -u HEAD
+       )
+'
+
+# We use --bare to make sure that the transport detects it, not the checkout
+# phase.
+test_expect_success 'clone --no-local --bare detects corruption' '
+       test_must_fail git clone --no-local --bare bit-error corrupt-transport
+'
+
+test_expect_success 'clone --no-local --bare detects missing object' '
+       test_must_fail git clone --no-local --bare missing missing-transport
+'
+
+test_expect_success 'clone --no-local --bare detects misnamed object' '
+       test_must_fail git clone --no-local --bare misnamed misnamed-transport
+'
+
+# We do not expect --local to detect corruption at the transport layer,
+# so we are really checking the checkout() code path.
+test_expect_success 'clone --local detects corruption' '
+       test_must_fail git clone --local bit-error corrupt-checkout
+'
+
+test_expect_success 'error detected during checkout leaves repo intact' '
+       test_path_is_dir corrupt-checkout/.git
+'
+
+test_expect_success 'clone --local detects missing objects' '
+       test_must_fail git clone --local missing missing-checkout
+'
+
+test_expect_failure 'clone --local detects misnamed objects' '
+       test_must_fail git clone --local misnamed misnamed-checkout
+'
+
+test_done
index 3c96fda548709835e10f283df8cab667c280af81..c4a7d84f46fd218af1824ac1f78a1e9f4cb15cec 100755 (executable)
@@ -1087,4 +1087,39 @@ test_expect_success 'barf on incomplete string' '
        grep " line 3 " error
 '
 
+# good section hygiene
+test_expect_failure 'unsetting the last key in a section removes header' '
+       cat >.git/config <<-\EOF &&
+       # some generic comment on the configuration file itself
+       # a comment specific to this "section" section.
+       [section]
+       # some intervening lines
+       # that should also be dropped
+
+       key = value
+       # please be careful when you update the above variable
+       EOF
+
+       cat >expect <<-\EOF &&
+       # some generic comment on the configuration file itself
+       EOF
+
+       git config --unset section.key &&
+       test_cmp expect .git/config
+'
+
+test_expect_failure 'adding a key into an empty section reuses header' '
+       cat >.git/config <<-\EOF &&
+       [section]
+       EOF
+
+       q_to_tab >expect <<-\EOF &&
+       [section]
+       Qkey = value
+       EOF
+
+       git config section.key value
+       test_cmp expect .git/config
+'
+
 test_done
index 02a4fc5d36a08d1046b9384c799f697640da0c4e..ff163cf6750f6d82c36964eb694efbcfae06135f 100755 (executable)
@@ -12,85 +12,108 @@ the GIT controlled paths.
 
 . ./test-lib.sh
 
-test_expect_success \
-    'setup' \
-    'mkdir path1 &&
-    echo frotz >path0 &&
-    echo rezrov >path1/file1 &&
-    git update-index --add path0 path1/file1'
+test_expect_success 'setup' '
+       mkdir path1 &&
+       echo frotz >path0 &&
+       echo rezrov >path1/file1 &&
+       git update-index --add path0 path1/file1
+'
+
+test_expect_success SYMLINKS 'have symlink in place where dir is expected.' '
+       rm -fr path0 path1 &&
+       mkdir path2 &&
+       ln -s path2 path1 &&
+       git checkout-index -f -a &&
+       test ! -h path1 && test -d path1 &&
+       test -f path1/file1 && test ! -f path2/file1
+'
 
-test_expect_success SYMLINKS \
-    'have symlink in place where dir is expected.' \
-    'rm -fr path0 path1 &&
-     mkdir path2 &&
-     ln -s path2 path1 &&
-     git checkout-index -f -a &&
-     test ! -h path1 && test -d path1 &&
-     test -f path1/file1 && test ! -f path2/file1'
+test_expect_success 'use --prefix=path2/' '
+       rm -fr path0 path1 path2 &&
+       mkdir path2 &&
+       git checkout-index --prefix=path2/ -f -a &&
+       test -f path2/path0 &&
+       test -f path2/path1/file1 &&
+       test ! -f path0 &&
+       test ! -f path1/file1
+'
+
+test_expect_success 'use --prefix=tmp-' '
+       rm -fr path0 path1 path2 tmp* &&
+       git checkout-index --prefix=tmp- -f -a &&
+       test -f tmp-path0 &&
+       test -f tmp-path1/file1 &&
+       test ! -f path0 &&
+       test ! -f path1/file1
+'
 
-test_expect_success \
-    'use --prefix=path2/' \
-    'rm -fr path0 path1 path2 &&
-     mkdir path2 &&
-     git checkout-index --prefix=path2/ -f -a &&
-     test -f path2/path0 &&
-     test -f path2/path1/file1 &&
-     test ! -f path0 &&
-     test ! -f path1/file1'
+test_expect_success 'use --prefix=tmp- but with a conflicting file and dir' '
+       rm -fr path0 path1 path2 tmp* &&
+       echo nitfol >tmp-path1 &&
+       mkdir tmp-path0 &&
+       git checkout-index --prefix=tmp- -f -a &&
+       test -f tmp-path0 &&
+       test -f tmp-path1/file1 &&
+       test ! -f path0 &&
+       test ! -f path1/file1
+'
 
-test_expect_success \
-    'use --prefix=tmp-' \
-    'rm -fr path0 path1 path2 tmp* &&
-     git checkout-index --prefix=tmp- -f -a &&
-     test -f tmp-path0 &&
-     test -f tmp-path1/file1 &&
-     test ! -f path0 &&
-     test ! -f path1/file1'
+test_expect_success SYMLINKS 'use --prefix=tmp/orary/ where tmp is a symlink' '
+       rm -fr path0 path1 path2 tmp* &&
+       mkdir tmp1 tmp1/orary &&
+       ln -s tmp1 tmp &&
+       git checkout-index --prefix=tmp/orary/ -f -a &&
+       test -d tmp1/orary &&
+       test -f tmp1/orary/path0 &&
+       test -f tmp1/orary/path1/file1 &&
+       test -h tmp
+'
 
-test_expect_success \
-    'use --prefix=tmp- but with a conflicting file and dir' \
-    'rm -fr path0 path1 path2 tmp* &&
-     echo nitfol >tmp-path1 &&
-     mkdir tmp-path0 &&
-     git checkout-index --prefix=tmp- -f -a &&
-     test -f tmp-path0 &&
-     test -f tmp-path1/file1 &&
-     test ! -f path0 &&
-     test ! -f path1/file1'
+test_expect_success SYMLINKS 'use --prefix=tmp/orary- where tmp is a symlink' '
+       rm -fr path0 path1 path2 tmp* &&
+       mkdir tmp1 &&
+       ln -s tmp1 tmp &&
+       git checkout-index --prefix=tmp/orary- -f -a &&
+       test -f tmp1/orary-path0 &&
+       test -f tmp1/orary-path1/file1 &&
+       test -h tmp
+'
 
-# Linus fix #1
-test_expect_success SYMLINKS \
-    'use --prefix=tmp/orary/ where tmp is a symlink' \
-    'rm -fr path0 path1 path2 tmp* &&
-     mkdir tmp1 tmp1/orary &&
-     ln -s tmp1 tmp &&
-     git checkout-index --prefix=tmp/orary/ -f -a &&
-     test -d tmp1/orary &&
-     test -f tmp1/orary/path0 &&
-     test -f tmp1/orary/path1/file1 &&
-     test -h tmp'
+test_expect_success SYMLINKS 'use --prefix=tmp- where tmp-path1 is a symlink' '
+       rm -fr path0 path1 path2 tmp* &&
+       mkdir tmp1 &&
+       ln -s tmp1 tmp-path1 &&
+       git checkout-index --prefix=tmp- -f -a &&
+       test -f tmp-path0 &&
+       test ! -h tmp-path1 &&
+       test -d tmp-path1 &&
+       test -f tmp-path1/file1
+'
 
-# Linus fix #2
-test_expect_success SYMLINKS \
-    'use --prefix=tmp/orary- where tmp is a symlink' \
-    'rm -fr path0 path1 path2 tmp* &&
-     mkdir tmp1 &&
-     ln -s tmp1 tmp &&
-     git checkout-index --prefix=tmp/orary- -f -a &&
-     test -f tmp1/orary-path0 &&
-     test -f tmp1/orary-path1/file1 &&
-     test -h tmp'
+test_expect_success 'apply filter from working tree .gitattributes with --prefix' '
+       rm -fr path0 path1 path2 tmp* &&
+       mkdir path1 &&
+       mkdir tmp &&
+       git config filter.replace-all.smudge "sed -e s/./,/g" &&
+       git config filter.replace-all.clean cat &&
+       git config filter.replace-all.required true &&
+       echo "file1 filter=replace-all" >path1/.gitattributes &&
+       git checkout-index --prefix=tmp/ -f -a &&
+       echo frotz >expected &&
+       test_cmp expected tmp/path0 &&
+       echo ,,,,,, >expected &&
+       test_cmp expected tmp/path1/file1
+'
 
-# Linus fix #3
-test_expect_success SYMLINKS \
-    'use --prefix=tmp- where tmp-path1 is a symlink' \
-    'rm -fr path0 path1 path2 tmp* &&
-     mkdir tmp1 &&
-     ln -s tmp1 tmp-path1 &&
-     git checkout-index --prefix=tmp- -f -a &&
-     test -f tmp-path0 &&
-     test ! -h tmp-path1 &&
-     test -d tmp-path1 &&
-     test -f tmp-path1/file1'
+test_expect_success 'apply CRLF filter from working tree .gitattributes with --prefix' '
+       rm -fr path0 path1 path2 tmp* &&
+       mkdir path1 &&
+       mkdir tmp &&
+       echo "file1 eol=crlf" >path1/.gitattributes &&
+       git checkout-index --prefix=tmp/ -f -a &&
+       echo rezrovQ >expected &&
+       tr \\015 Q <tmp/path1/file1 >actual &&
+       test_cmp expected actual
+'
 
 test_done
index 56090d2ebadcdfb127b9db005ae8d092b0255a75..8e3545d8680c5f5179977082849388b1b31c17d8 100755 (executable)
@@ -39,4 +39,26 @@ test_expect_success 'checking out paths out of a tree does not clobber unrelated
        test_cmp expect.next2 dir/next2
 '
 
+test_expect_success 'do not touch unmerged entries matching $path but not in $tree' '
+       git checkout next &&
+       git reset --hard &&
+
+       cat dir/common >expect.common &&
+       EMPTY_SHA1=$(git hash-object -w --stdin </dev/null) &&
+       git rm dir/next0 &&
+       cat >expect.next0 <<-EOF &&
+       100644 $EMPTY_SHA1 1    dir/next0
+       100644 $EMPTY_SHA1 2    dir/next0
+       EOF
+       git update-index --index-info <expect.next0 &&
+
+       git checkout master dir &&
+
+       test_cmp expect.common dir/common &&
+       test_path_is_file dir/master &&
+       git diff --exit-code master dir/master &&
+       git ls-files -s dir/next0 >actual.next0 &&
+       test_cmp expect.next0 actual.next0
+'
+
 test_done
index 12f1e4a63c33b2f394ea37db71e1cd4467cee8f5..8311c7da440a5932107cd8e4787f236c29518861 100755 (executable)
@@ -7,20 +7,18 @@ test_description='git branch assorted tests'
 
 . ./test-lib.sh
 
-test_expect_success \
-    'prepare a trivial repository' \
-    'echo Hello > A &&
-     git update-index --add A &&
-     git commit -m "Initial commit." &&
-     echo World >> A &&
-     git update-index --add A &&
-     git commit -m "Second commit." &&
-     HEAD=$(git rev-parse --verify HEAD)'
-
-test_expect_success \
-    'git branch --help should not have created a bogus branch' '
-     test_might_fail git branch --help </dev/null >/dev/null 2>/dev/null &&
-     test_path_is_missing .git/refs/heads/--help
+test_expect_success 'prepare a trivial repository' '
+       echo Hello >A &&
+       git update-index --add A &&
+       git commit -m "Initial commit." &&
+       echo World >>A &&
+       git update-index --add A &&
+       git commit -m "Second commit." &&
+       HEAD=$(git rev-parse --verify HEAD)'
+
+test_expect_success 'git branch --help should not have created a bogus branch' '
+       test_might_fail git branch --help </dev/null >/dev/null 2>/dev/null &&
+       test_path_is_missing .git/refs/heads/--help
 '
 
 test_expect_success 'branch -h in broken repository' '
@@ -34,67 +32,67 @@ test_expect_success 'branch -h in broken repository' '
        test_i18ngrep "[Uu]sage" broken/usage
 '
 
-test_expect_success \
-    'git branch abc should create a branch' \
-    'git branch abc && test_path_is_file .git/refs/heads/abc'
+test_expect_success 'git branch abc should create a branch' '
+       git branch abc && test_path_is_file .git/refs/heads/abc
+'
 
-test_expect_success \
-    'git branch a/b/c should create a branch' \
-    'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c'
+test_expect_success 'git branch a/b/c should create a branch' '
+       git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c
+'
 
-test_expect_success \
-    'git branch HEAD should fail' \
-    'test_must_fail git branch HEAD'
+test_expect_success 'git branch HEAD should fail' '
+       test_must_fail git branch HEAD
+'
 
 cat >expect <<EOF
 $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000        branch: Created from master
 EOF
-test_expect_success \
-    'git branch -l d/e/f should create a branch and a log' \
-       'GIT_COMMITTER_DATE="2005-05-26 23:30" \
-     git branch -l d/e/f &&
-        test_path_is_file .git/refs/heads/d/e/f &&
-        test_path_is_file .git/logs/refs/heads/d/e/f &&
-        test_cmp expect .git/logs/refs/heads/d/e/f'
-
-test_expect_success \
-    'git branch -d d/e/f should delete a branch and a log' \
-       'git branch -d d/e/f &&
-        test_path_is_missing .git/refs/heads/d/e/f &&
-        test_path_is_missing .git/logs/refs/heads/d/e/f'
-
-test_expect_success \
-    'git branch j/k should work after branch j has been deleted' \
-       'git branch j &&
-        git branch -d j &&
-        git branch j/k'
-
-test_expect_success \
-    'git branch l should work after branch l/m has been deleted' \
-       'git branch l/m &&
-        git branch -d l/m &&
-        git branch l'
-
-test_expect_success \
-    'git branch -m dumps usage' \
-       'test_expect_code 128 git branch -m 2>err &&
-       test_i18ngrep "too many branches for a rename operation" err'
-
-test_expect_success \
-    'git branch -m m m/m should work' \
-       'git branch -l m &&
-        git branch -m m m/m &&
-       test_path_is_file .git/logs/refs/heads/m/m'
-
-test_expect_success \
-    'git branch -m n/n n should work' \
-       'git branch -l n/n &&
+test_expect_success 'git branch -l d/e/f should create a branch and a log' '
+       GIT_COMMITTER_DATE="2005-05-26 23:30" \
+       git branch -l d/e/f &&
+       test_path_is_file .git/refs/heads/d/e/f &&
+       test_path_is_file .git/logs/refs/heads/d/e/f &&
+       test_cmp expect .git/logs/refs/heads/d/e/f
+'
+
+test_expect_success 'git branch -d d/e/f should delete a branch and a log' '
+       git branch -d d/e/f &&
+       test_path_is_missing .git/refs/heads/d/e/f &&
+       test_path_is_missing .git/logs/refs/heads/d/e/f
+'
+
+test_expect_success 'git branch j/k should work after branch j has been deleted' '
+       git branch j &&
+       git branch -d j &&
+       git branch j/k
+'
+
+test_expect_success 'git branch l should work after branch l/m has been deleted' '
+       git branch l/m &&
+       git branch -d l/m &&
+       git branch l
+'
+
+test_expect_success 'git branch -m dumps usage' '
+       test_expect_code 128 git branch -m 2>err &&
+       test_i18ngrep "branch name required" err
+'
+
+test_expect_success 'git branch -m m m/m should work' '
+       git branch -l m &&
+       git branch -m m m/m &&
+       test_path_is_file .git/logs/refs/heads/m/m
+'
+
+test_expect_success 'git branch -m n/n n should work' '
+       git branch -l n/n &&
        git branch -m n/n n &&
-       test_path_is_file .git/logs/refs/heads/n'
+       test_path_is_file .git/logs/refs/heads/n
+'
 
 test_expect_success 'git branch -m o/o o should fail when o/p exists' '
        git branch o/o &&
-        git branch o/p &&
+       git branch o/p &&
        test_must_fail git branch -m o/o o
 '
 
@@ -252,19 +250,20 @@ mv .git/config-saved .git/config
 
 git config branch.s/s.dummy Hello
 
-test_expect_success \
-    'git branch -m s/s s should work when s/t is deleted' \
-       'git branch -l s/s &&
+test_expect_success 'git branch -m s/s s should work when s/t is deleted' '
+       git branch -l s/s &&
        test_path_is_file .git/logs/refs/heads/s/s &&
-        git branch -l s/t &&
+       git branch -l s/t &&
        test_path_is_file .git/logs/refs/heads/s/t &&
-        git branch -d s/t &&
-        git branch -m s/s s &&
-       test_path_is_file .git/logs/refs/heads/s'
+       git branch -d s/t &&
+       git branch -m s/s s &&
+       test_path_is_file .git/logs/refs/heads/s
+'
 
-test_expect_success 'config information was renamed, too' \
-       "test $(git config branch.s.dummy) = Hello &&
-        test_must_fail git config branch.s/s/dummy"
+test_expect_success 'config information was renamed, too' '
+       test $(git config branch.s.dummy) = Hello &&
+       test_must_fail git config branch.s/s/dummy
+'
 
 test_expect_success 'deleting a symref' '
        git branch target &&
@@ -285,8 +284,7 @@ test_expect_success 'deleting a dangling symref' '
        test_i18ncmp expect actual
 '
 
-test_expect_success 'renaming a symref is not allowed' \
-'
+test_expect_success 'renaming a symref is not allowed' '
        git symbolic-ref refs/heads/master2 refs/heads/master &&
        test_must_fail git branch -m master2 master3 &&
        git symbolic-ref refs/heads/master2 &&
@@ -294,163 +292,179 @@ test_expect_success 'renaming a symref is not allowed' \
        test_path_is_missing .git/refs/heads/master3
 '
 
-test_expect_success SYMLINKS \
-    'git branch -m u v should fail when the reflog for u is a symlink' '
-     git branch -l u &&
-     mv .git/logs/refs/heads/u real-u &&
-     ln -s real-u .git/logs/refs/heads/u &&
-     test_must_fail git branch -m u v
-'
-
-test_expect_success 'test tracking setup via --track' \
-    'git config remote.local.url . &&
-     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
-     (git show-ref -q refs/remotes/local/master || git fetch local) &&
-     git branch --track my1 local/master &&
-     test $(git config branch.my1.remote) = local &&
-     test $(git config branch.my1.merge) = refs/heads/master'
-
-test_expect_success 'test tracking setup (non-wildcard, matching)' \
-    'git config remote.local.url . &&
-     git config remote.local.fetch refs/heads/master:refs/remotes/local/master &&
-     (git show-ref -q refs/remotes/local/master || git fetch local) &&
-     git branch --track my4 local/master &&
-     test $(git config branch.my4.remote) = local &&
-     test $(git config branch.my4.merge) = refs/heads/master'
-
-test_expect_success 'test tracking setup (non-wildcard, not matching)' \
-    'git config remote.local.url . &&
-     git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
-     (git show-ref -q refs/remotes/local/master || git fetch local) &&
-     git branch --track my5 local/master &&
-     ! test "$(git config branch.my5.remote)" = local &&
-     ! test "$(git config branch.my5.merge)" = refs/heads/master'
-
-test_expect_success 'test tracking setup via config' \
-    'git config branch.autosetupmerge true &&
-     git config remote.local.url . &&
-     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
-     (git show-ref -q refs/remotes/local/master || git fetch local) &&
-     git branch my3 local/master &&
-     test $(git config branch.my3.remote) = local &&
-     test $(git config branch.my3.merge) = refs/heads/master'
-
-test_expect_success 'test overriding tracking setup via --no-track' \
-    'git config branch.autosetupmerge true &&
-     git config remote.local.url . &&
-     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
-     (git show-ref -q refs/remotes/local/master || git fetch local) &&
-     git branch --no-track my2 local/master &&
-     git config branch.autosetupmerge false &&
-     ! test "$(git config branch.my2.remote)" = local &&
-     ! test "$(git config branch.my2.merge)" = refs/heads/master'
-
-test_expect_success 'no tracking without .fetch entries' \
-    'git config branch.autosetupmerge true &&
-     git branch my6 s &&
-     git config branch.automsetupmerge false &&
-     test -z "$(git config branch.my6.remote)" &&
-     test -z "$(git config branch.my6.merge)"'
-
-test_expect_success 'test tracking setup via --track but deeper' \
-    'git config remote.local.url . &&
-     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
-     (git show-ref -q refs/remotes/local/o/o || git fetch local) &&
-     git branch --track my7 local/o/o &&
-     test "$(git config branch.my7.remote)" = local &&
-     test "$(git config branch.my7.merge)" = refs/heads/o/o'
-
-test_expect_success 'test deleting branch deletes branch config' \
-    'git branch -d my7 &&
-     test -z "$(git config branch.my7.remote)" &&
-     test -z "$(git config branch.my7.merge)"'
-
-test_expect_success 'test deleting branch without config' \
-    'git branch my7 s &&
-     sha1=$(git rev-parse my7 | cut -c 1-7) &&
-     echo "Deleted branch my7 (was $sha1)." >expect &&
-     git branch -d my7 >actual 2>&1 &&
-     test_i18ncmp expect actual'
-
-test_expect_success 'test --track without .fetch entries' \
-    'git branch --track my8 &&
-     test "$(git config branch.my8.remote)" &&
-     test "$(git config branch.my8.merge)"'
-
-test_expect_success \
-    'branch from non-branch HEAD w/autosetupmerge=always' \
-    'git config branch.autosetupmerge always &&
-     git branch my9 HEAD^ &&
-     git config branch.autosetupmerge false'
-
-test_expect_success \
-    'branch from non-branch HEAD w/--track causes failure' \
-    'test_must_fail git branch --track my10 HEAD^'
-
-test_expect_success \
-    'branch from tag w/--track causes failure' \
-    'git tag foobar &&
-     test_must_fail git branch --track my11 foobar'
-
-test_expect_success '--set-upstream-to fails on multiple branches' \
-    'test_must_fail git branch --set-upstream-to master a b c'
-
-test_expect_success '--set-upstream-to fails on detached HEAD' \
-    'git checkout HEAD^{} &&
-     test_must_fail git branch --set-upstream-to master &&
-     git checkout -'
-
-test_expect_success 'use --set-upstream-to modify HEAD' \
-    'test_config branch.master.remote foo &&
-     test_config branch.master.merge foo &&
-     git branch my12
-     git branch --set-upstream-to my12 &&
-     test "$(git config branch.master.remote)" = "." &&
-     test "$(git config branch.master.merge)" = "refs/heads/my12"'
-
-test_expect_success 'use --set-upstream-to modify a particular branch' \
-    'git branch my13
-     git branch --set-upstream-to master my13 &&
-     test "$(git config branch.my13.remote)" = "." &&
-     test "$(git config branch.my13.merge)" = "refs/heads/master"'
-
-test_expect_success '--unset-upstream should fail if given a non-existent branch' \
-    'test_must_fail git branch --unset-upstream i-dont-exist'
-
-test_expect_success 'test --unset-upstream on HEAD' \
-    'git branch my14
-     test_config branch.master.remote foo &&
-     test_config branch.master.merge foo &&
-     git branch --set-upstream-to my14 &&
-     git branch --unset-upstream &&
-     test_must_fail git config branch.master.remote &&
-     test_must_fail git config branch.master.merge &&
-     # fail for a branch without upstream set
-     test_must_fail git branch --unset-upstream
-'
-
-test_expect_success '--unset-upstream should fail on multiple branches' \
-    'test_must_fail git branch --unset-upstream a b c'
-
-test_expect_success '--unset-upstream should fail on detached HEAD' \
-    'git checkout HEAD^{} &&
-     test_must_fail git branch --unset-upstream &&
-     git checkout -
-'
-
-test_expect_success 'test --unset-upstream on a particular branch' \
-    'git branch my15
-     git branch --set-upstream-to master my14 &&
-     git branch --unset-upstream my14 &&
-     test_must_fail git config branch.my14.remote &&
-     test_must_fail git config branch.my14.merge'
-
-test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' \
-    'git update-ref refs/remotes/origin/master HEAD &&
-     git branch --set-upstream origin/master 2>actual &&
-     test_when_finished git update-ref -d refs/remotes/origin/master &&
-     test_when_finished git branch -d origin/master &&
-     cat >expected <<EOF &&
+test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
+       git branch -l u &&
+       mv .git/logs/refs/heads/u real-u &&
+       ln -s real-u .git/logs/refs/heads/u &&
+       test_must_fail git branch -m u v
+'
+
+test_expect_success 'test tracking setup via --track' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git fetch local) &&
+       git branch --track my1 local/master &&
+       test $(git config branch.my1.remote) = local &&
+       test $(git config branch.my1.merge) = refs/heads/master
+'
+
+test_expect_success 'test tracking setup (non-wildcard, matching)' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/master:refs/remotes/local/master &&
+       (git show-ref -q refs/remotes/local/master || git fetch local) &&
+       git branch --track my4 local/master &&
+       test $(git config branch.my4.remote) = local &&
+       test $(git config branch.my4.merge) = refs/heads/master
+'
+
+test_expect_success 'test tracking setup (non-wildcard, not matching)' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
+       (git show-ref -q refs/remotes/local/master || git fetch local) &&
+       git branch --track my5 local/master &&
+       ! test "$(git config branch.my5.remote)" = local &&
+       ! test "$(git config branch.my5.merge)" = refs/heads/master
+'
+
+test_expect_success 'test tracking setup via config' '
+       git config branch.autosetupmerge true &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git fetch local) &&
+       git branch my3 local/master &&
+       test $(git config branch.my3.remote) = local &&
+       test $(git config branch.my3.merge) = refs/heads/master
+'
+
+test_expect_success 'test overriding tracking setup via --no-track' '
+       git config branch.autosetupmerge true &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git fetch local) &&
+       git branch --no-track my2 local/master &&
+       git config branch.autosetupmerge false &&
+       ! test "$(git config branch.my2.remote)" = local &&
+       ! test "$(git config branch.my2.merge)" = refs/heads/master
+'
+
+test_expect_success 'no tracking without .fetch entries' '
+       git config branch.autosetupmerge true &&
+       git branch my6 s &&
+       git config branch.automsetupmerge false &&
+       test -z "$(git config branch.my6.remote)" &&
+       test -z "$(git config branch.my6.merge)"
+'
+
+test_expect_success 'test tracking setup via --track but deeper' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/o/o || git fetch local) &&
+       git branch --track my7 local/o/o &&
+       test "$(git config branch.my7.remote)" = local &&
+       test "$(git config branch.my7.merge)" = refs/heads/o/o
+'
+
+test_expect_success 'test deleting branch deletes branch config' '
+       git branch -d my7 &&
+       test -z "$(git config branch.my7.remote)" &&
+       test -z "$(git config branch.my7.merge)"
+'
+
+test_expect_success 'test deleting branch without config' '
+       git branch my7 s &&
+       sha1=$(git rev-parse my7 | cut -c 1-7) &&
+       echo "Deleted branch my7 (was $sha1)." >expect &&
+       git branch -d my7 >actual 2>&1 &&
+       test_i18ncmp expect actual
+'
+
+test_expect_success 'test --track without .fetch entries' '
+       git branch --track my8 &&
+       test "$(git config branch.my8.remote)" &&
+       test "$(git config branch.my8.merge)"
+'
+
+test_expect_success 'branch from non-branch HEAD w/autosetupmerge=always' '
+       git config branch.autosetupmerge always &&
+       git branch my9 HEAD^ &&
+       git config branch.autosetupmerge false
+'
+
+test_expect_success 'branch from non-branch HEAD w/--track causes failure' '
+       test_must_fail git branch --track my10 HEAD^
+'
+
+test_expect_success 'branch from tag w/--track causes failure' '
+       git tag foobar &&
+       test_must_fail git branch --track my11 foobar
+'
+
+test_expect_success '--set-upstream-to fails on multiple branches' '
+       test_must_fail git branch --set-upstream-to master a b c
+'
+
+test_expect_success '--set-upstream-to fails on detached HEAD' '
+       git checkout HEAD^{} &&
+       test_must_fail git branch --set-upstream-to master &&
+       git checkout -
+'
+
+test_expect_success 'use --set-upstream-to modify HEAD' '
+       test_config branch.master.remote foo &&
+       test_config branch.master.merge foo &&
+       git branch my12
+       git branch --set-upstream-to my12 &&
+       test "$(git config branch.master.remote)" = "." &&
+       test "$(git config branch.master.merge)" = "refs/heads/my12"
+'
+
+test_expect_success 'use --set-upstream-to modify a particular branch' '
+       git branch my13
+       git branch --set-upstream-to master my13 &&
+       test "$(git config branch.my13.remote)" = "." &&
+       test "$(git config branch.my13.merge)" = "refs/heads/master"
+'
+
+test_expect_success '--unset-upstream should fail if given a non-existent branch' '
+       test_must_fail git branch --unset-upstream i-dont-exist
+'
+
+test_expect_success 'test --unset-upstream on HEAD' '
+       git branch my14
+       test_config branch.master.remote foo &&
+       test_config branch.master.merge foo &&
+       git branch --set-upstream-to my14 &&
+       git branch --unset-upstream &&
+       test_must_fail git config branch.master.remote &&
+       test_must_fail git config branch.master.merge &&
+       # fail for a branch without upstream set
+       test_must_fail git branch --unset-upstream
+'
+
+test_expect_success '--unset-upstream should fail on multiple branches' '
+       test_must_fail git branch --unset-upstream a b c
+'
+
+test_expect_success '--unset-upstream should fail on detached HEAD' '
+       git checkout HEAD^{} &&
+       test_must_fail git branch --unset-upstream &&
+       git checkout -
+'
+
+test_expect_success 'test --unset-upstream on a particular branch' '
+       git branch my15
+       git branch --set-upstream-to master my14 &&
+       git branch --unset-upstream my14 &&
+       test_must_fail git config branch.my14.remote &&
+       test_must_fail git config branch.my14.merge
+'
+
+test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' '
+       git update-ref refs/remotes/origin/master HEAD &&
+       git branch --set-upstream origin/master 2>actual &&
+       test_when_finished git update-ref -d refs/remotes/origin/master &&
+       test_when_finished git branch -d origin/master &&
+       cat >expected <<EOF &&
 The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
 
 If you wanted to make '"'master'"' track '"'origin/master'"', do this:
@@ -458,38 +472,38 @@ If you wanted to make '"'master'"' track '"'origin/master'"', do this:
     git branch -d origin/master
     git branch --set-upstream-to origin/master
 EOF
-     test_cmp expected actual
+       test_cmp expected actual
 '
 
-test_expect_success '--set-upstream with two args only shows the deprecation message' \
-    'git branch --set-upstream master my13 2>actual &&
-     test_when_finished git branch --unset-upstream master &&
-     cat >expected <<EOF &&
+test_expect_success '--set-upstream with two args only shows the deprecation message' '
+       git branch --set-upstream master my13 2>actual &&
+       test_when_finished git branch --unset-upstream master &&
+       cat >expected <<EOF &&
 The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
 EOF
-     test_cmp expected actual
+       test_cmp expected actual
 '
 
-test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' \
-    'git branch --set-upstream my13 2>actual &&
-     test_when_finished git branch --unset-upstream my13 &&
-     cat >expected <<EOF &&
+test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' '
+       git branch --set-upstream my13 2>actual &&
+       test_when_finished git branch --unset-upstream my13 &&
+       cat >expected <<EOF &&
 The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to
 EOF
-     test_cmp expected actual
+       test_cmp expected actual
 '
 
 # Keep this test last, as it changes the current branch
 cat >expect <<EOF
 $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000        branch: Created from master
 EOF
-test_expect_success \
-    'git checkout -b g/h/i -l should create a branch and a log' \
-       'GIT_COMMITTER_DATE="2005-05-26 23:30" \
-     git checkout -b g/h/i -l master &&
-        test_path_is_file .git/refs/heads/g/h/i &&
-        test_path_is_file .git/logs/refs/heads/g/h/i &&
-        test_cmp expect .git/logs/refs/heads/g/h/i'
+test_expect_success 'git checkout -b g/h/i -l should create a branch and a log' '
+       GIT_COMMITTER_DATE="2005-05-26 23:30" \
+       git checkout -b g/h/i -l master &&
+       test_path_is_file .git/refs/heads/g/h/i &&
+       test_path_is_file .git/logs/refs/heads/g/h/i &&
+       test_cmp expect .git/logs/refs/heads/g/h/i
+'
 
 test_expect_success 'checkout -b makes reflog by default' '
        git checkout master &&
@@ -760,7 +774,7 @@ test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
 
 test_expect_success 'detect misconfigured autosetuprebase (no value)' '
        git config --unset branch.autosetuprebase &&
-       echo "[branch] autosetuprebase" >> .git/config &&
+       echo "[branch] autosetuprebase" >>.git/config &&
        test_must_fail git branch &&
        git config --unset branch.autosetuprebase
 '
index 76fe7e0060c20507cb6eb317451b69a2a0c9146d..ba4f98e800f262242ef7925f82dc8d13272fe3ca 100755 (executable)
@@ -94,13 +94,13 @@ test_expect_success 'git branch -v pattern does not show branch summaries' '
        test_must_fail git branch -v branch*
 '
 
-cat >expect <<'EOF'
-* (no branch)
+test_expect_success 'git branch shows detached HEAD properly' '
+       cat >expect <<EOF &&
+* (detached from $(git rev-parse --short HEAD^0))
   branch-one
   branch-two
   master
 EOF
-test_expect_success 'git branch shows detached HEAD properly' '
        git checkout HEAD^0 &&
        git branch >actual &&
        test_i18ncmp expect actual
index 1de0ebda25c1034fc1b343a8f0e3da9c2c7f8c6e..f6cc10265700730999174347704a0df6989c7f1b 100755 (executable)
@@ -138,8 +138,7 @@ test_expect_success 'rebase a single mode change' '
 '
 
 test_expect_success 'rebase is not broken by diff.renames' '
-       git config diff.renames copies &&
-       test_when_finished "git config --unset diff.renames" &&
+       test_config diff.renames copies &&
        git checkout filemove &&
        GIT_TRACE=1 git rebase force-3way
 '
index 15dcbd42d367c3ef6b487e0f235ad201b6cf8413..a58406d12fc8dabf8f2e73e721d2f622a781cc9a 100755 (executable)
@@ -937,8 +937,7 @@ test_expect_success 'rebase --edit-todo can be used to modify todo' '
 test_expect_success 'rebase -i respects core.commentchar' '
        git reset --hard &&
        git checkout E^0 &&
-       git config core.commentchar "\\" &&
-       test_when_finished "git config --unset core.commentchar" &&
+       test_config core.commentchar "\\" &&
        write_script remove-all-but-first.sh <<-\EOF &&
        sed -e "2,\$s/^/\\\\/" "$1" >"$1.tmp" &&
        mv "$1.tmp" "$1"
diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh
new file mode 100755 (executable)
index 0000000..f977279
--- /dev/null
@@ -0,0 +1,219 @@
+#!/bin/sh
+
+test_description='Test cherry-pick -x and -s'
+
+. ./test-lib.sh
+
+pristine_detach () {
+       git cherry-pick --quit &&
+       git checkout -f "$1^0" &&
+       git read-tree -u --reset HEAD &&
+       git clean -d -f -f -q -x
+}
+
+mesg_one_line='base: commit message'
+
+mesg_no_footer="$mesg_one_line
+
+OneWordBodyThatsNotA-S-o-B"
+
+mesg_with_footer="$mesg_no_footer
+
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+Signed-off-by: A.U. Thor <author@example.com>
+Signed-off-by: B.U. Thor <buthor@example.com>"
+
+mesg_broken_footer="$mesg_no_footer
+
+The signed-off-by string should begin with the words Signed-off-by followed
+by a colon and space, and then the signers name and email address. e.g.
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+
+mesg_with_footer_sob="$mesg_with_footer
+Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+
+mesg_with_cherry_footer="$mesg_with_footer_sob
+(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
+Tested-by: C.U. Thor <cuthor@example.com>"
+
+
+test_expect_success setup '
+       git config advice.detachedhead false &&
+       echo unrelated >unrelated &&
+       git add unrelated &&
+       test_commit initial foo a &&
+       test_commit "$mesg_one_line" foo b mesg-one-line &&
+       git reset --hard initial &&
+       test_commit "$mesg_no_footer" foo b mesg-no-footer &&
+       git reset --hard initial &&
+       test_commit "$mesg_broken_footer" foo b mesg-broken-footer &&
+       git reset --hard initial &&
+       test_commit "$mesg_with_footer" foo b mesg-with-footer &&
+       git reset --hard initial &&
+       test_commit "$mesg_with_footer_sob" foo b mesg-with-footer-sob &&
+       git reset --hard initial &&
+       test_commit "$mesg_with_cherry_footer" foo b mesg-with-cherry-footer &&
+       pristine_detach initial &&
+       test_commit conflicting unrelated
+'
+
+test_expect_success 'cherry-pick -x inserts blank line after one line subject' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-one-line^0` &&
+       git cherry-pick -x mesg-one-line &&
+       cat <<-EOF >expect &&
+               $mesg_one_line
+
+               (cherry picked from commit $sha1)
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s inserts blank line after one line subject' '
+       pristine_detach initial &&
+       git cherry-pick -s mesg-one-line &&
+       cat <<-EOF >expect &&
+               $mesg_one_line
+
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s inserts blank line after non-conforming footer' '
+       pristine_detach initial &&
+       git cherry-pick -s mesg-broken-footer &&
+       cat <<-EOF >expect &&
+               $mesg_broken_footer
+
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x inserts blank line when conforming footer not found' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-no-footer^0` &&
+       git cherry-pick -x mesg-no-footer &&
+       cat <<-EOF >expect &&
+               $mesg_no_footer
+
+               (cherry picked from commit $sha1)
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s inserts blank line when conforming footer not found' '
+       pristine_detach initial &&
+       git cherry-pick -s mesg-no-footer &&
+       cat <<-EOF >expect &&
+               $mesg_no_footer
+
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x -s inserts blank line when conforming footer not found' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-no-footer^0` &&
+       git cherry-pick -x -s mesg-no-footer &&
+       cat <<-EOF >expect &&
+               $mesg_no_footer
+
+               (cherry picked from commit $sha1)
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s adds sob when last sob doesnt match committer' '
+       pristine_detach initial &&
+       git cherry-pick -s mesg-with-footer &&
+       cat <<-EOF >expect &&
+               $mesg_with_footer
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x -s adds sob when last sob doesnt match committer' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-with-footer^0` &&
+       git cherry-pick -x -s mesg-with-footer &&
+       cat <<-EOF >expect &&
+               $mesg_with_footer
+               (cherry picked from commit $sha1)
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s refrains from adding duplicate trailing sob' '
+       pristine_detach initial &&
+       git cherry-pick -s mesg-with-footer-sob &&
+       cat <<-EOF >expect &&
+               $mesg_with_footer_sob
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists for committer' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-with-footer-sob^0` &&
+       git cherry-pick -x -s mesg-with-footer-sob &&
+       cat <<-EOF >expect &&
+               $mesg_with_footer_sob
+               (cherry picked from commit $sha1)
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x treats "(cherry picked from..." line as part of footer' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-with-cherry-footer^0` &&
+       git cherry-pick -x mesg-with-cherry-footer &&
+       cat <<-EOF >expect &&
+               $mesg_with_cherry_footer
+               (cherry picked from commit $sha1)
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -s treats "(cherry picked from..." line as part of footer' '
+       pristine_detach initial &&
+       git cherry-pick -s mesg-with-cherry-footer &&
+       cat <<-EOF >expect &&
+               $mesg_with_cherry_footer
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'cherry-pick -x -s treats "(cherry picked from..." line as part of footer' '
+       pristine_detach initial &&
+       sha1=`git rev-parse mesg-with-cherry-footer^0` &&
+       git cherry-pick -x -s mesg-with-cherry-footer &&
+       cat <<-EOF >expect &&
+               $mesg_with_cherry_footer
+               (cherry picked from commit $sha1)
+               Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+       EOF
+       git log -1 --pretty=format:%B >actual &&
+       test_cmp expect actual
+'
+
+test_done
index bb1fc47fe8aa6904555c153f90a6026c45bf3645..b993dae64574cd2828fc636d3afc15b2c0c2e5a0 100755 (executable)
@@ -972,6 +972,268 @@ test_expect_success 'empty subject prefix does not have extra space' '
        test_cmp expect actual
 '
 
+append_signoff()
+{
+       C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
+       git format-patch --stdout --signoff $C^..$C >append_signoff.patch &&
+       sed -n -e "1,/^---$/p" append_signoff.patch |
+               egrep -n "^Subject|Sign|^$"
+}
+
+test_expect_success 'signoff: commit with no body' '
+       append_signoff </dev/null >actual &&
+       cat <<\EOF | sed "s/EOL$//" >expected &&
+4:Subject: [PATCH] EOL
+8:
+9:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: commit with only subject' '
+       echo subject | append_signoff >actual &&
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: commit with only subject that does not end with NL' '
+       printf subject | append_signoff >actual &&
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: no existing signoffs' '
+       append_signoff <<\EOF >actual &&
+subject
+
+body
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: no existing signoffs and no trailing NL' '
+       printf "subject\n\nbody" | append_signoff >actual &&
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: some random signoff' '
+       append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Signed-off-by: my@house
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: my@house
+12:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: misc conforming footer elements' '
+       append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Signed-off-by: my@house
+(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
+Tested-by: Some One <someone@example.com>
+Bug: 1234
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: my@house
+15:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: some random signoff-alike' '
+       append_signoff <<\EOF >actual &&
+subject
+
+body
+Fooled-by-me: my@house
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+11:
+12:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: not really a signoff' '
+       append_signoff <<\EOF >actual &&
+subject
+
+I want to mention about Signed-off-by: here.
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:I want to mention about Signed-off-by: here.
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: not really a signoff (2)' '
+       append_signoff <<\EOF >actual &&
+subject
+
+My unfortunate
+Signed-off-by: example happens to be wrapped here.
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:Signed-off-by: example happens to be wrapped here.
+11:
+12:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: valid S-o-b paragraph in the middle' '
+       append_signoff <<\EOF >actual &&
+subject
+
+Signed-off-by: my@house
+Signed-off-by: your@house
+
+A lot of houses.
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:Signed-off-by: my@house
+10:Signed-off-by: your@house
+11:
+13:
+14:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: the same signoff at the end' '
+       append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: the same signoff at the end, no trailing NL' '
+       printf "subject\n\nSigned-off-by: C O Mitter <committer@example.com>" |
+               append_signoff >actual &&
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+9:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: the same signoff NOT at the end' '
+       append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Signed-off-by: C O Mitter <committer@example.com>
+Signed-off-by: my@house
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+11:Signed-off-by: C O Mitter <committer@example.com>
+12:Signed-off-by: my@house
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: detect garbage in non-conforming footer' '
+       append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Tested-by: my@house
+Some Trash
+Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+13:Signed-off-by: C O Mitter <committer@example.com>
+14:
+15:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'signoff: footer begins with non-signoff without @ sign' '
+       append_signoff <<\EOF >actual &&
+subject
+
+body
+
+Reviewed-id: Noone
+Tested-by: my@house
+Change-id: Ideadbeef
+Signed-off-by: C O Mitter <committer@example.com>
+Bug: 1234
+EOF
+       cat >expected <<\EOF &&
+4:Subject: [PATCH] subject
+8:
+10:
+14:Signed-off-by: C O Mitter <committer@example.com>
+EOF
+       test_cmp expected actual
+'
+
 test_expect_success 'format patch ignores color.ui' '
        test_unconfig color.ui &&
        git format-patch --stdout -1 >expect &&
index 082d3e83bddf242fb02f5124fd52d125520af6b8..38a092a0dadbe09b903484c1d34e240a9d819c77 100755 (executable)
@@ -93,11 +93,6 @@ sed -e '
        s/song;/song();/
 ' <Beer.perl >Beer-correct.perl
 
-test_config () {
-       git config "$1" "$2" &&
-       test_when_finished "git config --unset $1"
-}
-
 test_expect_funcname () {
        lang=${2-java}
        test_expect_code 1 git diff --no-index -U1 \
index 40ab333a8a4849d620e1d41a75f611921f83055a..f2f55fc51ccd294194300d4bbf66307ac73f9ac6 100755 (executable)
@@ -230,7 +230,7 @@ test_expect_success '.gitattributes override config' '
 '
 
 test_expect_success 'setup: remove diff driver regex' '
-       test_might_fail git config --unset diff.testdriver.wordRegex
+       test_unconfig diff.testdriver.wordRegex
 '
 
 test_expect_success 'use configured regex' '
@@ -335,8 +335,7 @@ test_expect_success 'word-diff with diff.sbe' '
 
        c
        EOF
-       test_when_finished "git config --unset diff.suppress-blank-empty" &&
-       git config diff.suppress-blank-empty true &&
+       test_config diff.suppress-blank-empty true &&
        word_diff --word-diff=plain
 '
 
@@ -368,7 +367,7 @@ test_expect_success 'setup history with two files' '
 
 test_expect_success 'wordRegex for the first file does not apply to the second' '
        echo "*.tex diff=tex" >.gitattributes &&
-       git config diff.tex.wordRegex "[a-z]+|." &&
+       test_config diff.tex.wordRegex "[a-z]+|." &&
        cat >expect <<-\EOF &&
                diff --git a/a.tex b/a.tex
                --- a/a.tex
index 6f6ee88b28bc5417035b45d87aaf4a9c974ab6c5..0bbcf0603d20380e71ef27cec654ec6c604561d1 100755 (executable)
@@ -486,4 +486,30 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
        test_cmp one expect
 '
 
+test_expect_success 'whitespace=fix to expand' '
+       qz_to_tab_space >preimage <<-\EOF &&
+       QQa
+       QQb
+       QQc
+       ZZZZZZZZZZZZZZZZd
+       QQe
+       QQf
+       QQg
+       EOF
+       qz_to_tab_space >patch <<-\EOF &&
+       diff --git a/preimage b/preimage
+       --- a/preimage
+       +++ b/preimage
+       @@ -1,7 +1,6 @@
+        QQa
+        QQb
+        QQc
+       -QQd
+        QQe
+        QQf
+        QQg
+       EOF
+       git -c core.whitespace=tab-in-indent apply --whitespace=fix patch
+'
+
 test_done
index cdafd7e7c1e6c73a97c36a60f77810badff603f2..12f6b027acbccae3fe524906970ff139ddccfed0 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success 'setup: messages' '
        vero eos et accusam et justo duo dolores et ea rebum.
 
        EOF
-       q_to_tab <<-\EOF >>msg &&
+       qz_to_tab_space <<-\EOF >>msg &&
        QDuis autem vel eum iriure dolor in hendrerit in vulputate velit
        Qesse molestie consequat, vel illum dolore eu feugiat nulla facilisis
        Qat vero eros et accumsan et iusto odio dignissim qui blandit
index fa686b887d6b49ab8e6d30893501744645f72a91..9243a979933997286bb6004bd7b224bad1bbc5cc 100755 (executable)
@@ -419,8 +419,6 @@ test_expect_success 'log --graph with merge' '
 '
 
 test_expect_success 'log.decorate configuration' '
-       test_might_fail git config --unset-all log.decorate &&
-
        git log --oneline >expect.none &&
        git log --oneline --decorate >expect.short &&
        git log --oneline --decorate=full >expect.full &&
@@ -429,8 +427,7 @@ test_expect_success 'log.decorate configuration' '
        git log --oneline >actual &&
        test_cmp expect.short actual &&
 
-       git config --unset-all log.decorate &&
-       git config log.decorate true &&
+       test_config log.decorate true &&
        git log --oneline >actual &&
        test_cmp expect.short actual &&
        git log --oneline --decorate=full >actual &&
@@ -438,8 +435,7 @@ test_expect_success 'log.decorate configuration' '
        git log --oneline --decorate=no >actual &&
        test_cmp expect.none actual &&
 
-       git config --unset-all log.decorate &&
-       git config log.decorate no &&
+       test_config log.decorate no &&
        git log --oneline >actual &&
        test_cmp expect.none actual &&
        git log --oneline --decorate >actual &&
@@ -447,8 +443,7 @@ test_expect_success 'log.decorate configuration' '
        git log --oneline --decorate=full >actual &&
        test_cmp expect.full actual &&
 
-       git config --unset-all log.decorate &&
-       git config log.decorate 1 &&
+       test_config log.decorate 1 &&
        git log --oneline >actual &&
        test_cmp expect.short actual &&
        git log --oneline --decorate=full >actual &&
@@ -456,8 +451,7 @@ test_expect_success 'log.decorate configuration' '
        git log --oneline --decorate=no >actual &&
        test_cmp expect.none actual &&
 
-       git config --unset-all log.decorate &&
-       git config log.decorate short &&
+       test_config log.decorate short &&
        git log --oneline >actual &&
        test_cmp expect.short actual &&
        git log --oneline --no-decorate >actual &&
@@ -465,8 +459,7 @@ test_expect_success 'log.decorate configuration' '
        git log --oneline --decorate=full >actual &&
        test_cmp expect.full actual &&
 
-       git config --unset-all log.decorate &&
-       git config log.decorate full &&
+       test_config log.decorate full &&
        git log --oneline >actual &&
        test_cmp expect.full actual &&
        git log --oneline --no-decorate >actual &&
@@ -474,16 +467,15 @@ test_expect_success 'log.decorate configuration' '
        git log --oneline --decorate >actual &&
        test_cmp expect.short actual
 
-       git config --unset-all log.decorate &&
+       test_unconfig log.decorate &&
        git log --pretty=raw >expect.raw &&
-       git config log.decorate full &&
+       test_config log.decorate full &&
        git log --pretty=raw >actual &&
        test_cmp expect.raw actual
 
 '
 
 test_expect_success 'reflog is expected format' '
-       test_might_fail git config --remove-section log &&
        git log -g --abbrev-commit --pretty=oneline >expect &&
        git reflog >actual &&
        test_cmp expect actual
@@ -496,10 +488,6 @@ test_expect_success 'whatchanged is expected format' '
 '
 
 test_expect_success 'log.abbrevCommit configuration' '
-       test_when_finished "git config --unset log.abbrevCommit" &&
-
-       test_might_fail git config --unset log.abbrevCommit &&
-
        git log --abbrev-commit >expect.log.abbrev &&
        git log --no-abbrev-commit >expect.log.full &&
        git log --pretty=raw >expect.log.raw &&
@@ -508,7 +496,7 @@ test_expect_success 'log.abbrevCommit configuration' '
        git whatchanged --abbrev-commit >expect.whatchanged.abbrev &&
        git whatchanged --no-abbrev-commit >expect.whatchanged.full &&
 
-       git config log.abbrevCommit true &&
+       test_config log.abbrevCommit true &&
 
        git log >actual &&
        test_cmp expect.log.abbrev actual &&
index 0c847fb45482b67266dc6eb9ec62b8cc7b5a9d76..6667d159ab0950f10a3271f1cb81a63953214f17 100755 (executable)
@@ -27,6 +27,25 @@ test_expect_success 'setup' '
        echo ignored-only-if-dir/ export-ignore >>.git/info/attributes &&
        git add ignored-only-if-dir &&
 
+       mkdir -p ignored-without-slash &&
+       echo "ignored without slash" >ignored-without-slash/foo &&
+       git add ignored-without-slash/foo &&
+       echo "ignored-without-slash export-ignore" >>.git/info/attributes &&
+
+       mkdir -p wildcard-without-slash &&
+       echo "ignored without slash" >wildcard-without-slash/foo &&
+       git add wildcard-without-slash/foo &&
+       echo "wild*-without-slash export-ignore" >>.git/info/attributes &&
+
+       mkdir -p deep/and/slashless &&
+       echo "ignored without slash" >deep/and/slashless/foo &&
+       git add deep/and/slashless/foo &&
+       echo "deep/and/slashless export-ignore" >>.git/info/attributes &&
+
+       mkdir -p deep/with/wildcard &&
+       echo "ignored without slash" >deep/with/wildcard/foo &&
+       git add deep/with/wildcard/foo &&
+       echo "deep/*t*/wildcard export-ignore" >>.git/info/attributes &&
 
        mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir &&
        echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir &&
@@ -49,6 +68,14 @@ test_expect_exists   archive/not-ignored-dir/ignored-only-if-dir
 test_expect_exists     archive/not-ignored-dir/
 test_expect_missing    archive/ignored-only-if-dir/
 test_expect_missing    archive/ignored-ony-if-dir/ignored-by-ignored-dir
+test_expect_missing    archive/ignored-without-slash/ &&
+test_expect_missing    archive/ignored-without-slash/foo &&
+test_expect_missing    archive/wildcard-without-slash/
+test_expect_missing    archive/wildcard-without-slash/foo &&
+test_expect_missing    archive/deep/and/slashless/ &&
+test_expect_missing    archive/deep/and/slashless/foo &&
+test_expect_missing    archive/deep/with/wildcard/ &&
+test_expect_missing    archive/deep/with/wildcard/foo &&
 test_expect_exists     archive/one-level-lower/
 test_expect_missing    archive/one-level-lower/two-levels-lower/ignored-only-if-dir/
 test_expect_missing    archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir
index 60de2d6ede958e713aebe85d73ee65ddbc10201d..f30c03885cda050f0bc704428ffa8a66932ff3fd 100755 (executable)
@@ -4,10 +4,6 @@ test_description='test automatic tag following'
 
 . ./test-lib.sh
 
-if ! test_have_prereq NOT_MINGW; then
-       say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
-fi
-
 # End state of the repository:
 #
 #         T - tag1          S - tag2
@@ -17,7 +13,7 @@ fi
 #     \   C - origin/cat    \
 #      origin/master         master
 
-test_expect_success NOT_MINGW setup '
+test_expect_success setup '
        test_tick &&
        echo ichi >file &&
        git add file &&
@@ -39,28 +35,35 @@ test_expect_success NOT_MINGW setup '
 '
 
 U=UPLOAD_LOG
+UPATH="$(pwd)/$U"
 
-test_expect_success NOT_MINGW 'setup expect' '
+test_expect_success 'setup expect' '
 cat - <<EOF >expect
-#S
 want $A
-#E
 EOF
 '
 
-test_expect_success NOT_MINGW 'fetch A (new commit : 1 connection)' '
+get_needs () {
+       test -s "$1" &&
+       perl -alne '
+               next unless $F[1] eq "upload-pack<";
+               last if $F[2] eq "0000";
+               print $F[2], " ", $F[3];
+       ' "$1"
+}
+
+test_expect_success 'fetch A (new commit : 1 connection)' '
        rm -f $U &&
        (
                cd cloned &&
-               GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+               GIT_TRACE_PACKET=$UPATH git fetch &&
                test $A = $(git rev-parse --verify origin/master)
        ) &&
-       test -s $U &&
-       cut -d" " -f1,2 $U >actual &&
+       get_needs $U >actual &&
        test_cmp expect actual
 '
 
-test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" '
+test_expect_success "create tag T on A, create C on branch cat" '
        git tag -a -m tag1 tag1 $A &&
        T=$(git rev-parse --verify tag1) &&
 
@@ -72,30 +75,27 @@ test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" '
        git checkout master
 '
 
-test_expect_success NOT_MINGW 'setup expect' '
+test_expect_success 'setup expect' '
 cat - <<EOF >expect
-#S
 want $C
 want $T
-#E
 EOF
 '
 
-test_expect_success NOT_MINGW 'fetch C, T (new branch, tag : 1 connection)' '
+test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
        rm -f $U &&
        (
                cd cloned &&
-               GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+               GIT_TRACE_PACKET=$UPATH git fetch &&
                test $C = $(git rev-parse --verify origin/cat) &&
                test $T = $(git rev-parse --verify tag1) &&
                test $A = $(git rev-parse --verify tag1^0)
        ) &&
-       test -s $U &&
-       cut -d" " -f1,2 $U >actual &&
+       get_needs $U >actual &&
        test_cmp expect actual
 '
 
-test_expect_success NOT_MINGW "create commits O, B, tag S on B" '
+test_expect_success "create commits O, B, tag S on B" '
        test_tick &&
        echo O >file &&
        git add file &&
@@ -111,39 +111,34 @@ test_expect_success NOT_MINGW "create commits O, B, tag S on B" '
        S=$(git rev-parse --verify tag2)
 '
 
-test_expect_success NOT_MINGW 'setup expect' '
+test_expect_success 'setup expect' '
 cat - <<EOF >expect
-#S
 want $B
 want $S
-#E
 EOF
 '
 
-test_expect_success NOT_MINGW 'fetch B, S (commit and tag : 1 connection)' '
+test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
        rm -f $U &&
        (
                cd cloned &&
-               GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+               GIT_TRACE_PACKET=$UPATH git fetch &&
                test $B = $(git rev-parse --verify origin/master) &&
                test $B = $(git rev-parse --verify tag2^0) &&
                test $S = $(git rev-parse --verify tag2)
        ) &&
-       test -s $U &&
-       cut -d" " -f1,2 $U >actual &&
+       get_needs $U >actual &&
        test_cmp expect actual
 '
 
-test_expect_success NOT_MINGW 'setup expect' '
+test_expect_success 'setup expect' '
 cat - <<EOF >expect
-#S
 want $B
 want $S
-#E
 EOF
 '
 
-test_expect_success NOT_MINGW 'new clone fetch master and tags' '
+test_expect_success 'new clone fetch master and tags' '
        git branch -D cat
        rm -f $U
        (
@@ -151,15 +146,14 @@ test_expect_success NOT_MINGW 'new clone fetch master and tags' '
                cd clone2 &&
                git init &&
                git remote add origin .. &&
-               GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+               GIT_TRACE_PACKET=$UPATH git fetch &&
                test $B = $(git rev-parse --verify origin/master) &&
                test $S = $(git rev-parse --verify tag2) &&
                test $B = $(git rev-parse --verify tag2^0) &&
                test $T = $(git rev-parse --verify tag1) &&
                test $A = $(git rev-parse --verify tag1^0)
        ) &&
-       test -s $U &&
-       cut -d" " -f1,2 $U >actual &&
+       get_needs $U >actual &&
        test_cmp expect actual
 '
 
index d3dc5df7b145a23a8d0ab55f69d7ca7351d78274..32ad89107b20fc2187b416bc7e3f4d45e17474c6 100755 (executable)
@@ -22,19 +22,16 @@ mk_test () {
        (
                for ref in "$@"
                do
-                       git push testrepo $the_first_commit:refs/$ref || {
-                               echo "Oops, push refs/$ref failure"
-                               exit 1
-                       }
+                       git push testrepo $the_first_commit:refs/$ref ||
+                       exit
                done &&
                cd testrepo &&
                for ref in "$@"
                do
-                       r=$(git show-ref -s --verify refs/$ref) &&
-                       test "z$r" = "z$the_first_commit" || {
-                               echo "Oops, refs/$ref is wrong"
-                               exit 1
-                       }
+                       echo "$the_first_commit" >expect &&
+                       git show-ref -s --verify refs/$ref >actual &&
+                       test_cmp expect actual ||
+                       exit
                done &&
                git fsck --full
        )
@@ -82,15 +79,13 @@ mk_child() {
 check_push_result () {
        (
                cd testrepo &&
-               it="$1" &&
-               shift
+               echo "$1" >expect &&
+               shift &&
                for ref in "$@"
                do
-                       r=$(git show-ref -s --verify refs/$ref) &&
-                       test "z$r" = "z$it" || {
-                               echo "Oops, refs/$ref is wrong"
-                               exit 1
-                       }
+                       git show-ref -s --verify refs/$ref >actual &&
+                       test_cmp expect actual ||
+                       exit
                done &&
                git fsck --full
        )
@@ -118,10 +113,9 @@ test_expect_success 'fetch without wildcard' '
                cd testrepo &&
                git fetch .. refs/heads/master:refs/remotes/origin/master &&
 
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -133,10 +127,9 @@ test_expect_success 'fetch with wildcard' '
                git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
                git fetch up &&
 
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -150,10 +143,9 @@ test_expect_success 'fetch with insteadOf' '
                git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
                git fetch up &&
 
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -167,10 +159,9 @@ test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
                git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
                git fetch up &&
 
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -180,10 +171,9 @@ test_expect_success 'push without wildcard' '
        git push testrepo refs/heads/master:refs/remotes/origin/master &&
        (
                cd testrepo &&
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -193,54 +183,50 @@ test_expect_success 'push with wildcard' '
        git push testrepo "refs/heads/*:refs/remotes/origin/*" &&
        (
                cd testrepo &&
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
 test_expect_success 'push with insteadOf' '
        mk_empty &&
        TRASH="$(pwd)/" &&
-       git config "url.$TRASH.insteadOf" trash/ &&
+       test_config "url.$TRASH.insteadOf" trash/ &&
        git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
        (
                cd testrepo &&
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
 test_expect_success 'push with pushInsteadOf' '
        mk_empty &&
        TRASH="$(pwd)/" &&
-       git config "url.$TRASH.pushInsteadOf" trash/ &&
+       test_config "url.$TRASH.pushInsteadOf" trash/ &&
        git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
        (
                cd testrepo &&
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
 test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
        mk_empty &&
-       TRASH="$(pwd)/" &&
-       git config "url.trash2/.pushInsteadOf" trash/ &&
-       git config remote.r.url trash/wrong &&
-       git config remote.r.pushurl "$TRASH/testrepo" &&
+       test_config "url.trash2/.pushInsteadOf" testrepo/ &&
+       test_config "url.trash3/.pusnInsteadOf" trash/wrong &&
+       test_config remote.r.url trash/wrong &&
+       test_config remote.r.pushurl "testrepo/" &&
        git push r refs/heads/master:refs/remotes/origin/master &&
        (
                cd testrepo &&
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -340,13 +326,8 @@ test_expect_success 'push with weak ambiguity (2)' '
 test_expect_success 'push with ambiguity' '
 
        mk_test heads/frotz tags/frotz &&
-       if git push testrepo master:frotz
-       then
-               echo "Oops, should have failed"
-               false
-       else
-               check_push_result $the_first_commit heads/frotz tags/frotz
-       fi
+       test_must_fail git push testrepo master:frotz &&
+       check_push_result $the_first_commit heads/frotz tags/frotz
 
 '
 
@@ -489,31 +470,24 @@ test_expect_success 'push with config remote.*.push = HEAD' '
                git checkout local &&
                git reset --hard $the_first_commit
        ) &&
-       git config remote.there.url testrepo &&
-       git config remote.there.push HEAD &&
-       git config branch.master.remote there &&
+       test_config remote.there.url testrepo &&
+       test_config remote.there.push HEAD &&
+       test_config branch.master.remote there &&
        git push &&
        check_push_result $the_commit heads/master &&
        check_push_result $the_first_commit heads/local
 '
 
-# clean up the cruft left with the previous one
-git config --remove-section remote.there
-git config --remove-section branch.master
-
 test_expect_success 'push with config remote.*.pushurl' '
 
        mk_test heads/master &&
        git checkout master &&
-       git config remote.there.url test2repo &&
-       git config remote.there.pushurl testrepo &&
+       test_config remote.there.url test2repo &&
+       test_config remote.there.pushurl testrepo &&
        git push there &&
        check_push_result $the_commit heads/master
 '
 
-# clean up the cruft left with the previous one
-git config --remove-section remote.there
-
 test_expect_success 'push with dry-run' '
 
        mk_test heads/master &&
@@ -834,9 +808,9 @@ test_expect_success 'fetch with branches' '
        (
                cd testrepo &&
                git fetch branch1 &&
-               r=$(git show-ref -s --verify refs/heads/branch1) &&
-               test "z$r" = "z$the_commit" &&
-               test 1 = $(git for-each-ref refs/heads | wc -l)
+               echo "$the_commit commit        refs/heads/branch1" >expect &&
+               git for-each-ref refs/heads >actual &&
+               test_cmp expect actual
        ) &&
        git checkout master
 '
@@ -847,9 +821,9 @@ test_expect_success 'fetch with branches containing #' '
        (
                cd testrepo &&
                git fetch branch2 &&
-               r=$(git show-ref -s --verify refs/heads/branch2) &&
-               test "z$r" = "z$the_first_commit" &&
-               test 1 = $(git for-each-ref refs/heads | wc -l)
+               echo "$the_first_commit commit  refs/heads/branch2" >expect &&
+               git for-each-ref refs/heads >actual &&
+               test_cmp expect actual
        ) &&
        git checkout master
 '
@@ -861,9 +835,9 @@ test_expect_success 'push with branches' '
        git push branch1 &&
        (
                cd testrepo &&
-               r=$(git show-ref -s --verify refs/heads/master) &&
-               test "z$r" = "z$the_first_commit" &&
-               test 1 = $(git for-each-ref refs/heads | wc -l)
+               echo "$the_first_commit commit  refs/heads/master" >expect &&
+               git for-each-ref refs/heads >actual &&
+               test_cmp expect actual
        )
 '
 
@@ -873,9 +847,9 @@ test_expect_success 'push with branches containing #' '
        git push branch2 &&
        (
                cd testrepo &&
-               r=$(git show-ref -s --verify refs/heads/branch3) &&
-               test "z$r" = "z$the_first_commit" &&
-               test 1 = $(git for-each-ref refs/heads | wc -l)
+               echo "$the_first_commit commit  refs/heads/branch3" >expect &&
+               git for-each-ref refs/heads >actual &&
+               test_cmp expect actual
        ) &&
        git checkout master
 '
@@ -958,9 +932,9 @@ test_expect_success 'push --porcelain' '
        git push >.git/bar --porcelain  testrepo refs/heads/master:refs/remotes/origin/master &&
        (
                cd testrepo &&
-               r=$(git show-ref -s --verify refs/remotes/origin/master) &&
-               test "z$r" = "z$the_commit" &&
-               test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+               echo "$the_commit commit        refs/remotes/origin/master" >expect &&
+               git for-each-ref refs/remotes/origin >actual &&
+               test_cmp expect actual
        ) &&
        test_cmp .git/foo .git/bar
 '
index 35304b41e9ce6222f7d713e3d310e47241d8e6e0..6af6c63350383e049082e61d3c8133af79d5a749 100755 (executable)
@@ -96,8 +96,7 @@ test_expect_success '--rebase' '
 '
 test_expect_success 'pull.rebase' '
        git reset --hard before-rebase &&
-       git config --bool pull.rebase true &&
-       test_when_finished "git config --unset pull.rebase" &&
+       test_config pull.rebase true &&
        git pull . copy &&
        test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
        test new = $(git show HEAD:file2)
@@ -105,8 +104,7 @@ test_expect_success 'pull.rebase' '
 
 test_expect_success 'branch.to-rebase.rebase' '
        git reset --hard before-rebase &&
-       git config --bool branch.to-rebase.rebase true &&
-       test_when_finished "git config --unset branch.to-rebase.rebase" &&
+       test_config branch.to-rebase.rebase true &&
        git pull . copy &&
        test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
        test new = $(git show HEAD:file2)
@@ -114,10 +112,8 @@ test_expect_success 'branch.to-rebase.rebase' '
 
 test_expect_success 'branch.to-rebase.rebase should override pull.rebase' '
        git reset --hard before-rebase &&
-       git config --bool pull.rebase true &&
-       test_when_finished "git config --unset pull.rebase" &&
-       git config --bool branch.to-rebase.rebase false &&
-       test_when_finished "git config --unset branch.to-rebase.rebase" &&
+       test_config pull.rebase true &&
+       test_config branch.to-rebase.rebase false &&
        git pull . copy &&
        test $(git rev-parse HEAD^) != $(git rev-parse copy) &&
        test new = $(git show HEAD:file2)
@@ -171,9 +167,9 @@ test_expect_success 'pull --rebase dies early with dirty working directory' '
        git update-ref refs/remotes/me/copy copy^ &&
        COPY=$(git rev-parse --verify me/copy) &&
        git rebase --onto $COPY copy &&
-       git config branch.to-rebase.remote me &&
-       git config branch.to-rebase.merge refs/heads/copy &&
-       git config branch.to-rebase.rebase true &&
+       test_config branch.to-rebase.remote me &&
+       test_config branch.to-rebase.merge refs/heads/copy &&
+       test_config branch.to-rebase.rebase true &&
        echo dirty >> file &&
        git add file &&
        test_must_fail git pull &&
index 4b4b4a604f3075dec1056b31da5cccc0c4ed5a9a..4086f02bc129b3628c724c7037fe60509fbef46c 100755 (executable)
@@ -181,8 +181,7 @@ test_expect_success 'push (chunked)' '
        git checkout master &&
        test_commit commit path3 &&
        HEAD=$(git rev-parse --verify HEAD) &&
-       git config http.postbuffer 4 &&
-       test_when_finished "git config --unset http.postbuffer" &&
+       test_config http.postbuffer 4 &&
        git push -v -v origin $BRANCH 2>err &&
        grep "POST git-receive-pack (chunked)" err &&
        (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
index c47d450cc3731cb471aa8485178f517bb0d6cbf5..60f1552adef417b8d37ca4215be1e66627c37258 100755 (executable)
@@ -54,11 +54,14 @@ cd "$base_dir"
 
 rm -f "$U.D"
 
-test_expect_success 'cloning with reference (no -l -s)' \
-'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>"$U.D"'
+test_expect_success 'cloning with reference (no -l -s)' '
+       GIT_TRACE_PACKET=$U.D git clone --reference B "file://$(pwd)/A" D
+'
 
-test_expect_success 'fetched no objects' \
-'! grep "^want" "$U.D"'
+test_expect_success 'fetched no objects' '
+       test -s "$U.D" &&
+       ! grep " want" "$U.D"
+'
 
 cd "$base_dir"
 
@@ -173,12 +176,13 @@ test_expect_success 'fetch with incomplete alternates' '
        (
                cd K &&
                git remote add J "file://$base_dir/J" &&
-               GIT_DEBUG_SEND_PACK=3 git fetch J 3>"$U.K"
+               GIT_TRACE_PACKET=$U.K git fetch J
        ) &&
        master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) &&
-       ! grep "^want $master_object" "$U.K" &&
+       test -s "$U.K" &&
+       ! grep " want $master_object" "$U.K" &&
        tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) &&
-       ! grep "^want $tag_object" "$U.K"
+       ! grep " want $tag_object" "$U.K"
 '
 
 test_done
index aa045295dec5af9dedc25495668d4afd6022d2cd..8956c21617410863660bff0bc22ec8e81903e81a 100755 (executable)
@@ -58,13 +58,7 @@ test_expect_success 'creating too deep nesting' \
 git clone -l -s D E &&
 git clone -l -s E F &&
 git clone -l -s F G &&
-git clone -l -s G H'
-
-test_expect_success 'invalidity of deepest repository' \
-'cd H && {
-       test_valid_repo
-       test $? -ne 0
-}'
+test_must_fail git clone --bare -l -s G H'
 
 cd "$base_dir"
 
index 30507407ff6375f96c632c7c3f62ae3d338ed5ea..66cda17ef342a2791ce7bac873eecfd4cf319faf 100755 (executable)
@@ -133,4 +133,17 @@ test_expect_success 'dodecapus' '
        check_revlist "--min-parents=13" &&
        check_revlist "--min-parents=4 --max-parents=11" tetrapus
 '
+
+test_expect_success 'ancestors with the same commit time' '
+
+       test_tick_keep=$test_tick &&
+       for i in 1 2 3 4 5 6 7 8; do
+               test_tick=$test_tick_keep
+               test_commit t$i
+       done &&
+       git rev-list t1^! --not t$i >result &&
+       >expect &&
+       test_cmp expect result
+'
+
 test_done
index 839ad97b791c6aa757d0b82eea7fc16369ad4586..8e2ff13423ba01095404ec49232760bc66c68978 100755 (executable)
@@ -56,7 +56,23 @@ test_expect_success setup '
 
        echo "Final change" >file &&
        test_tick && git commit -a -m "Final change" &&
-       note I
+       note I &&
+
+       git symbolic-ref HEAD refs/heads/unrelated &&
+       git rm -f "*" &&
+       echo "Unrelated branch" >side &&
+       git add side &&
+       test_tick && git commit -m "Side root" &&
+       note J &&
+
+       git checkout master &&
+       test_tick && git merge -m "Coolest" unrelated &&
+       note K &&
+
+       echo "Immaterial" >elif &&
+       git add elif &&
+       test_tick && git commit -m "Last" &&
+       note L
 '
 
 FMT='tformat:%P        %H | %s'
@@ -79,10 +95,10 @@ check_result () {
        '
 }
 
-check_result 'I H G F E D C B A' --full-history
-check_result 'I H E C B A' --full-history -- file
-check_result 'I H E C B A' --full-history --topo-order -- file
-check_result 'I H E C B A' --full-history --date-order -- file
+check_result 'L K J I H G F E D C B A' --full-history
+check_result 'I H E C B A' --full-history -- file
+check_result 'I H E C B A' --full-history --topo-order -- file
+check_result 'I H E C B A' --full-history --date-order -- file
 check_result 'I E C B A' --simplify-merges -- file
 check_result 'I B A' -- file
 check_result 'I B A' --topo-order -- file
index 3e0e15fb3e0b92ab7e95789d27db34d9a1fa423c..2fce99a0754481a36b3644d01f8577310e19d763 100755 (executable)
@@ -164,7 +164,7 @@ test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if
        cp .git/BISECT_START saved &&
        test_must_fail git bisect start $HASH4 foo -- &&
        git branch > branch.output &&
-       test_i18ngrep "* (no branch)" branch.output > /dev/null &&
+       test_i18ngrep "* (no branch, bisect started on other)" branch.output > /dev/null &&
        test_cmp saved .git/BISECT_START
 '
 test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
index 992c2a04674d474a8875da955935512ec99f335a..f73eceabfbcd0a763b8908a8d829894a5e36055b 100755 (executable)
@@ -112,8 +112,8 @@ test_expect_success '[merge] summary/log configuration' '
          Common #1
        EOF
 
-       git config merge.log true &&
-       test_might_fail git config --unset-all merge.summary &&
+       test_config merge.log true &&
+       test_unconfig merge.summary &&
 
        git checkout master &&
        test_tick &&
@@ -121,8 +121,8 @@ test_expect_success '[merge] summary/log configuration' '
 
        git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
 
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary true &&
+       test_unconfig merge.log &&
+       test_config merge.summary true &&
 
        git checkout master &&
        test_tick &&
@@ -134,11 +134,6 @@ test_expect_success '[merge] summary/log configuration' '
        test_cmp expected actual2
 '
 
-test_expect_success 'setup: clear [merge] configuration' '
-       test_might_fail git config --unset-all merge.log &&
-       test_might_fail git config --unset-all merge.summary
-'
-
 test_expect_success 'setup FETCH_HEAD' '
        git checkout master &&
        test_tick &&
@@ -248,14 +243,14 @@ test_expect_success 'fmt-merge-msg -m' '
          Common #1
        EOF
 
-       test_might_fail git config --unset merge.log &&
-       test_might_fail git config --unset merge.summary &&
+       test_unconfig merge.log &&
+       test_unconfig merge.summary &&
        git checkout master &&
        git fetch "$(pwd)" left &&
        git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual &&
        git fmt-merge-msg --log -m "Sync with left" \
                                        <.git/FETCH_HEAD >actual.log &&
-       git config merge.log true &&
+       test_config merge.log true &&
        git fmt-merge-msg -m "Sync with left" \
                                        <.git/FETCH_HEAD >actual.log-config &&
        git fmt-merge-msg --no-log -m "Sync with left" \
@@ -290,29 +285,29 @@ test_expect_success 'setup: expected shortlog for two branches' '
 '
 
 test_expect_success 'shortlog for two branches' '
-       git config merge.log true &&
-       test_might_fail git config --unset-all merge.summary &&
+       test_config merge.log true &&
+       test_unconfig merge.summary &&
        git checkout master &&
        test_tick &&
        git fetch . left right &&
        git fmt-merge-msg <.git/FETCH_HEAD >actual1 &&
 
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary true &&
+       test_unconfig merge.log &&
+       test_config merge.summary true &&
        git checkout master &&
        test_tick &&
        git fetch . left right &&
        git fmt-merge-msg <.git/FETCH_HEAD >actual2 &&
 
-       git config merge.log yes &&
-       test_might_fail git config --unset-all merge.summary &&
+       test_config merge.log yes &&
+       test_unconfig merge.summary &&
        git checkout master &&
        test_tick &&
        git fetch . left right &&
        git fmt-merge-msg <.git/FETCH_HEAD >actual3 &&
 
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary yes &&
+       test_unconfig merge.log &&
+       test_config merge.summary yes &&
        git checkout master &&
        test_tick &&
        git fetch . left right &&
@@ -325,8 +320,8 @@ test_expect_success 'shortlog for two branches' '
 '
 
 test_expect_success 'merge-msg -F' '
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary yes &&
+       test_unconfig merge.log &&
+       test_config merge.summary yes &&
        git checkout master &&
        test_tick &&
        git fetch . left right &&
@@ -335,8 +330,8 @@ test_expect_success 'merge-msg -F' '
 '
 
 test_expect_success 'merge-msg -F in subdirectory' '
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary yes &&
+       test_unconfig merge.log &&
+       test_config merge.summary yes &&
        git checkout master &&
        test_tick &&
        git fetch . left right &&
@@ -350,8 +345,8 @@ test_expect_success 'merge-msg -F in subdirectory' '
 '
 
 test_expect_success 'merge-msg with nothing to merge' '
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary yes &&
+       test_unconfig merge.log &&
+       test_config merge.summary yes &&
 
        >empty &&
 
@@ -376,8 +371,8 @@ test_expect_success 'merge-msg tag' '
          Common #1
        EOF
 
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary yes &&
+       test_unconfig merge.log &&
+       test_config merge.summary yes &&
 
        git checkout master &&
        test_tick &&
@@ -406,8 +401,8 @@ test_expect_success 'merge-msg two tags' '
          Common #1
        EOF
 
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary yes &&
+       test_unconfig merge.log &&
+       test_config merge.summary yes &&
 
        git checkout master &&
        test_tick &&
@@ -436,8 +431,8 @@ test_expect_success 'merge-msg tag and branch' '
          Common #1
        EOF
 
-       test_might_fail git config --unset-all merge.log &&
-       git config merge.summary yes &&
+       test_unconfig merge.log &&
+       test_config merge.summary yes &&
 
        git checkout master &&
        test_tick &&
@@ -464,6 +459,8 @@ test_expect_success 'merge-msg lots of commits' '
                echo "  ..."
        } >expected &&
 
+       test_config merge.summary yes &&
+
        git checkout master &&
        test_tick &&
        git fetch . long &&
@@ -472,4 +469,43 @@ test_expect_success 'merge-msg lots of commits' '
        test_cmp expected actual
 '
 
+test_expect_success 'merge-msg with "merging" an annotated tag' '
+       test_config merge.log true &&
+
+       git checkout master^0 &&
+       git commit --allow-empty -m "One step ahead" &&
+       git tag -a -m "An annotated one" annote HEAD &&
+
+       git checkout master &&
+       git fetch . annote &&
+
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       {
+               cat <<-\EOF
+               Merge tag '\''annote'\''
+
+               An annotated one
+
+               * tag '\''annote'\'':
+                 One step ahead
+               EOF
+       } >expected &&
+       test_cmp expected actual &&
+
+       test_when_finished "git reset --hard" &&
+       annote=$(git rev-parse annote) &&
+       git merge --no-commit $annote &&
+       {
+               cat <<-EOF
+               Merge tag '\''$annote'\''
+
+               An annotated one
+
+               * tag '\''$annote'\'':
+                 One step ahead
+               EOF
+       } >expected &&
+       test_cmp expected .git/MERGE_MSG
+'
+
 test_done
diff --git a/t/t7062-wtstatus-ignorecase.sh b/t/t7062-wtstatus-ignorecase.sh
new file mode 100755 (executable)
index 0000000..73709db
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='git-status with core.ignorecase=true'
+
+. ./test-lib.sh
+
+test_expect_success 'status with hash collisions' '
+       # note: "V/", "V/XQANY/" and "WURZAUP/" produce the same hash code
+       # in name-hash.c::hash_name
+       mkdir V &&
+       mkdir V/XQANY &&
+       mkdir WURZAUP &&
+       touch V/XQANY/test &&
+       git config core.ignorecase true &&
+       git add . &&
+       # test is successful if git status completes (no endless loop)
+       git status
+'
+
+test_done
index 5030f1f1bcc2442482ca923d03454fbc69f1a7ca..ff265353a375d02bb578fed29ea00f07dc08a6fc 100755 (executable)
@@ -777,18 +777,22 @@ test_expect_success 'submodule deinit . deinits all initialized submodules' '
        git config submodule.example.foo bar &&
        git config submodule.example2.frotz nitfol &&
        test_must_fail git submodule deinit &&
-       git submodule deinit . &&
+       git submodule deinit . >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
        test -z "$(git config --get-regexp "submodule\.example2\.")" &&
+       test_i18ngrep "Cleared directory .init" actual &&
+       test_i18ngrep "Cleared directory .example2" actual &&
        rmdir init example2
 '
 
 test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' '
        git submodule update --init &&
        rm -rf init example2/* example2/.git &&
-       git submodule deinit init example2 &&
+       git submodule deinit init example2 >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
        test -z "$(git config --get-regexp "submodule\.example2\.")" &&
+       test_i18ngrep ! "Cleared directory .init" actual &&
+       test_i18ngrep "Cleared directory .example2" actual &&
        rmdir init
 '
 
@@ -798,8 +802,9 @@ test_expect_success 'submodule deinit fails when the submodule contains modifica
        test_must_fail git submodule deinit init &&
        test -n "$(git config --get-regexp "submodule\.example\.")" &&
        test -f example2/.git &&
-       git submodule deinit -f init &&
+       git submodule deinit -f init >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
+       test_i18ngrep "Cleared directory .init" actual &&
        rmdir init
 '
 
@@ -809,8 +814,9 @@ test_expect_success 'submodule deinit fails when the submodule contains untracke
        test_must_fail git submodule deinit init &&
        test -n "$(git config --get-regexp "submodule\.example\.")" &&
        test -f example2/.git &&
-       git submodule deinit -f init &&
+       git submodule deinit -f init >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
+       test_i18ngrep "Cleared directory .init" actual &&
        rmdir init
 '
 
@@ -823,8 +829,9 @@ test_expect_success 'submodule deinit fails when the submodule HEAD does not mat
        test_must_fail git submodule deinit init &&
        test -n "$(git config --get-regexp "submodule\.example\.")" &&
        test -f example2/.git &&
-       git submodule deinit -f init &&
+       git submodule deinit -f init >actual &&
        test -z "$(git config --get-regexp "submodule\.example\.")" &&
+       test_i18ngrep "Cleared directory .init" actual &&
        rmdir init
 '
 
@@ -832,14 +839,18 @@ test_expect_success 'submodule deinit is silent when used on an uninitialized su
        git submodule update --init &&
        git submodule deinit init >actual &&
        test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual &&
+       test_i18ngrep "Cleared directory .init" actual &&
        git submodule deinit init >actual &&
        test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
+       test_i18ngrep "Cleared directory .init" actual &&
        git submodule deinit . >actual &&
        test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
        test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual &&
+       test_i18ngrep "Cleared directory .init" actual &&
        git submodule deinit . >actual &&
        test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual &&
        test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual &&
+       test_i18ngrep "Cleared directory .init" actual &&
        rmdir init example2
 '
 
index 1a3d20bdc36c63c5261090d76858d43bd7f64113..2a0cfaac329c6c0974f205c81c45a0304a04897d 100755 (executable)
@@ -665,8 +665,10 @@ test_expect_success 'submodule add properly re-creates deeper level submodules'
 
 test_expect_success 'submodule update properly revives a moved submodule' '
        (cd super &&
+        H=$(git rev-parse --short HEAD) &&
         git commit -am "pre move" &&
-        git status >expect&&
+        H2=$(git rev-parse --short HEAD) &&
+        git status | sed "s/$H/XXX/" >expect &&
         H=$(cd submodule2; git rev-parse HEAD) &&
         git rm --cached submodule2 &&
         rm -rf submodule2 &&
@@ -675,7 +677,7 @@ test_expect_success 'submodule update properly revives a moved submodule' '
         git config -f .gitmodules submodule.submodule2.path "moved/sub module"
         git commit -am "post move" &&
         git submodule update &&
-        git status >actual &&
+        git status | sed "s/$H2/XXX/" >actual &&
         test_cmp expect actual
        )
 '
index 1c908f4d3966cb9a2769465652981bef831f312d..436b7b606e3eab09a0dd8c1b67ab90e43c24dffc 100755 (executable)
@@ -36,8 +36,7 @@ test_expect_success 'nonexistent template file should return error' '
 '
 
 test_expect_success 'nonexistent template file in config should return error' '
-       git config commit.template "$PWD"/notexist &&
-       test_when_finished "git config --unset commit.template" &&
+       test_config commit.template "$PWD"/notexist &&
        (
                GIT_EDITOR="echo hello >\"\$1\"" &&
                export GIT_EDITOR &&
@@ -93,14 +92,13 @@ test_expect_success '-t option should be short for --template' '
 
 test_expect_success 'config-specified template should commit' '
        echo "new template" > "$TEMPLATE" &&
-       git config commit.template "$TEMPLATE" &&
+       test_config commit.template "$TEMPLATE" &&
        echo "more content" >> foo &&
        git add foo &&
        (
                test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
                git commit
        ) &&
-       git config --unset commit.template &&
        commit_msg_is "new templatecommit message"
 '
 
index f9b44b72449d258647c5278c52e5c792ee10445e..a4938b1e4549d5082362ec3f5513ffe91f210d39 100755 (executable)
@@ -171,10 +171,9 @@ test_expect_success 'verbose' '
 
 test_expect_success 'verbose respects diff config' '
 
-       git config color.diff always &&
+       test_config color.diff always &&
        git status -v >actual &&
-       grep "\[1mdiff --git" actual &&
-       git config --unset color.diff
+       grep "\[1mdiff --git" actual
 '
 
 mesg_with_comment_and_newlines='
@@ -425,6 +424,18 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo
 
 '
 
+test_expect_success 'commit -s places sob on third line after two empty lines' '
+       git commit -s --allow-empty --allow-empty-message &&
+       cat <<-EOF >expect &&
+
+
+       Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>
+
+       EOF
+       sed -e "/^#/d" -e "s/^:.*//" .git/COMMIT_EDITMSG >actual &&
+       test_cmp expect actual
+'
+
 write_script .git/FAKE_EDITOR <<\EOF
 mv "$1" "$1.orig"
 (
@@ -435,16 +446,6 @@ EOF
 
 echo '## Custom template' >template
 
-clear_config () {
-       (
-               git config --unset-all "$1"
-               case $? in
-               0|5)    exit 0 ;;
-               *)      exit 1 ;;
-               esac
-       )
-}
-
 try_commit () {
        git reset --hard &&
        echo >>negative &&
@@ -460,67 +461,57 @@ try_commit () {
 try_commit_status_combo () {
 
        test_expect_success 'commit' '
-               clear_config commit.status &&
                try_commit "" &&
                test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit' '
-               clear_config commit.status &&
                try_commit "" &&
                test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --status' '
-               clear_config commit.status &&
                try_commit --status &&
                test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --no-status' '
-               clear_config commit.status &&
                try_commit --no-status &&
                test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit with commit.status = yes' '
-               clear_config commit.status &&
-               git config commit.status yes &&
+               test_config commit.status yes &&
                try_commit "" &&
                test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit with commit.status = no' '
-               clear_config commit.status &&
-               git config commit.status no &&
+               test_config commit.status no &&
                try_commit "" &&
                test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --status with commit.status = yes' '
-               clear_config commit.status &&
-               git config commit.status yes &&
+               test_config commit.status yes &&
                try_commit --status &&
                test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --no-status with commit.status = yes' '
-               clear_config commit.status &&
-               git config commit.status yes &&
+               test_config commit.status yes &&
                try_commit --no-status &&
                test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --status with commit.status = no' '
-               clear_config commit.status &&
-               git config commit.status no &&
+               test_config commit.status no &&
                try_commit --status &&
                test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
 
        test_expect_success 'commit --no-status with commit.status = no' '
-               clear_config commit.status &&
-               git config commit.status no &&
+               test_config commit.status no &&
                try_commit --no-status &&
                test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG
        '
@@ -534,8 +525,7 @@ use_template="-t template"
 try_commit_status_combo
 
 test_expect_success 'commit --status with custom comment character' '
-       test_when_finished "git config --unset core.commentchar" &&
-       git config core.commentchar ";" &&
+       test_config core.commentchar ";" &&
        try_commit --status &&
        test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG
 '
index aecb4d1e5fdc1e966aad07ee1f1e7af834388bb2..e2ffdacc267c22e369247791e24338a20badf94d 100755 (executable)
@@ -131,8 +131,7 @@ cat >expect <<\EOF
 EOF
 
 test_expect_success 'status (advice.statusHints false)' '
-       test_when_finished "git config --unset advice.statusHints" &&
-       git config advice.statusHints false &&
+       test_config advice.statusHints false &&
        git status >output &&
        test_i18ncmp expect output
 
@@ -332,8 +331,7 @@ test_expect_success 'status -uno' '
 '
 
 test_expect_success 'status (status.showUntrackedFiles no)' '
-       git config status.showuntrackedfiles no
-       test_when_finished "git config --unset status.showuntrackedfiles" &&
+       test_config status.showuntrackedfiles no &&
        git status >output &&
        test_i18ncmp expect output
 '
@@ -348,12 +346,11 @@ cat >expect <<EOF
 #
 # Untracked files not listed
 EOF
-git config advice.statusHints false
 test_expect_success 'status -uno (advice.statusHints false)' '
+       test_config advice.statusHints false &&
        git status -uno >output &&
        test_i18ncmp expect output
 '
-git config --unset advice.statusHints
 
 cat >expect << EOF
  M dir1/modified
@@ -400,8 +397,7 @@ test_expect_success 'status -unormal' '
 '
 
 test_expect_success 'status (status.showUntrackedFiles normal)' '
-       git config status.showuntrackedfiles normal
-       test_when_finished "git config --unset status.showuntrackedfiles" &&
+       test_config status.showuntrackedfiles normal
        git status >output &&
        test_i18ncmp expect output
 '
@@ -459,8 +455,7 @@ test_expect_success 'status -uall' '
 '
 
 test_expect_success 'status (status.showUntrackedFiles all)' '
-       git config status.showuntrackedfiles all
-       test_when_finished "git config --unset status.showuntrackedfiles" &&
+       test_config status.showuntrackedfiles all
        git status >output &&
        test_i18ncmp expect output
 '
@@ -485,10 +480,9 @@ test_expect_success 'status -s -uall' '
        test_cmp expect output
 '
 test_expect_success 'status -s (status.showUntrackedFiles all)' '
-       git config status.showuntrackedfiles all
+       test_config status.showuntrackedfiles all &&
        git status -s >output &&
        rm -rf dir3 &&
-       git config --unset status.showuntrackedfiles &&
        test_cmp expect output
 '
 
@@ -588,15 +582,13 @@ cat >expect <<\EOF
 EOF
 
 test_expect_success 'status with color.ui' '
-       git config color.ui always &&
-       test_when_finished "git config --unset color.ui" &&
+       test_config color.ui always &&
        git status | test_decode_color >output &&
        test_i18ncmp expect output
 '
 
 test_expect_success 'status with color.status' '
-       git config color.status always &&
-       test_when_finished "git config --unset color.status" &&
+       test_config color.status always &&
        git status | test_decode_color >output &&
        test_i18ncmp expect output
 '
@@ -720,8 +712,7 @@ EOF
 
 test_expect_success 'status without relative paths' '
 
-       git config status.relativePaths false &&
-       test_when_finished "git config --unset status.relativePaths" &&
+       test_config status.relativePaths false &&
        (cd dir1 && git status) >output &&
        test_i18ncmp expect output
 
@@ -740,8 +731,7 @@ EOF
 
 test_expect_success 'status -s without relative paths' '
 
-       git config status.relativePaths false &&
-       test_when_finished "git config --unset status.relativePaths" &&
+       test_config status.relativePaths false &&
        (cd dir1 && git status -s) >output &&
        test_cmp expect output
 
@@ -1038,15 +1028,14 @@ test_expect_success '--ignore-submodules=untracked suppresses submodules with un
 '
 
 test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
-       git config diff.ignoreSubmodules dirty &&
+       test_config diff.ignoreSubmodules dirty &&
        git status >output &&
        test_i18ncmp expect output &&
        git config --add -f .gitmodules submodule.subname.ignore untracked &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status >output &&
        test_i18ncmp expect output &&
-       git config -f .gitmodules  --remove-section submodule.subname &&
-       git config --unset diff.ignoreSubmodules
+       git config -f .gitmodules  --remove-section submodule.subname
 '
 
 test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' '
@@ -1066,15 +1055,14 @@ test_expect_success '--ignore-submodules=dirty suppresses submodules with untrac
 '
 
 test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
-       git config diff.ignoreSubmodules dirty &&
+       test_config diff.ignoreSubmodules dirty &&
        git status >output &&
        ! test -s actual &&
        git config --add -f .gitmodules submodule.subname.ignore dirty &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status >output &&
        test_i18ncmp expect output &&
-       git config -f .gitmodules  --remove-section submodule.subname &&
-       git config --unset diff.ignoreSubmodules
+       git config -f .gitmodules  --remove-section submodule.subname
 '
 
 test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' '
@@ -1291,15 +1279,13 @@ cat > expect << EOF
 EOF
 
 test_expect_success "status (core.commentchar with submodule summary)" '
-       test_when_finished "git config --unset core.commentchar" &&
-       git config core.commentchar ";" &&
+       test_config core.commentchar ";" &&
        git status >output &&
        test_i18ncmp expect output
 '
 
 test_expect_success "status (core.commentchar with two chars with submodule summary)" '
-       test_when_finished "git config --unset core.commentchar" &&
-       git config core.commentchar ";;" &&
+       test_config core.commentchar ";;" &&
        git status >output &&
        test_i18ncmp expect output
 '
index 9d4610629d725c22b9de5187abf71b2b8544292d..06749a6aa07e40a5e5081c8912264d9c15b385ef 100755 (executable)
@@ -77,7 +77,7 @@ test_expect_success 'status when rebase in progress before resolving conflicts'
        ONTO=$(git rev-parse --short HEAD^^) &&
        test_must_fail git rebase HEAD^ --onto HEAD^^ &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached at $ONTO
        # You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
        #   (fix conflicts and then run "git rebase --continue")
        #   (use "git rebase --skip" to skip this patch)
@@ -104,7 +104,7 @@ test_expect_success 'status when rebase in progress before rebase --continue' '
        echo three >main.txt &&
        git add main.txt &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached at $ONTO
        # You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
        #   (all conflicts fixed: run "git rebase --continue")
        #
@@ -136,7 +136,7 @@ test_expect_success 'status during rebase -i when conflicts unresolved' '
        ONTO=$(git rev-parse --short rebase_i_conflicts) &&
        test_must_fail git rebase -i rebase_i_conflicts &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached at $ONTO
        # You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
        #   (fix conflicts and then run "git rebase --continue")
        #   (use "git rebase --skip" to skip this patch)
@@ -162,7 +162,7 @@ test_expect_success 'status during rebase -i after resolving conflicts' '
        test_must_fail git rebase -i rebase_i_conflicts &&
        git add main.txt &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached at $ONTO
        # You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
        #   (all conflicts fixed: run "git rebase --continue")
        #
@@ -188,9 +188,10 @@ test_expect_success 'status when rebasing -i in edit mode' '
        export FAKE_LINES &&
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD~2) &&
+       TGT=$(git rev-parse --short two_rebase_i) &&
        git rebase -i HEAD~2 &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $TGT
        # You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -215,8 +216,9 @@ test_expect_success 'status when splitting a commit' '
        ONTO=$(git rev-parse --short HEAD~3) &&
        git rebase -i HEAD~3 &&
        git reset HEAD^ &&
+       TGT=$(git rev-parse --short HEAD) &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached at $TGT
        # You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -244,10 +246,11 @@ test_expect_success 'status after editing the last commit with --amend during a
        export FAKE_LINES &&
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD~3) &&
+       TGT=$(git rev-parse --short three_amend) &&
        git rebase -i HEAD~3 &&
        git commit --amend -m "foo" &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $TGT
        # You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -277,7 +280,7 @@ test_expect_success 'status: (continue first edit) second edit' '
        git rebase -i HEAD~3 &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -299,7 +302,7 @@ test_expect_success 'status: (continue first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -326,7 +329,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "foo" &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -348,7 +351,7 @@ test_expect_success 'status: (amend first edit) second edit' '
        git commit --amend -m "a" &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -371,7 +374,7 @@ test_expect_success 'status: (amend first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -399,7 +402,7 @@ test_expect_success 'status: (amend first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "d" &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -423,7 +426,7 @@ test_expect_success 'status: (split first edit) second edit' '
        git commit -m "e" &&
        git rebase --continue &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -448,7 +451,7 @@ test_expect_success 'status: (split first edit) second edit and split' '
        git rebase --continue &&
        git reset HEAD^ &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (Once your working directory is clean, run "git rebase --continue")
        #
@@ -478,7 +481,7 @@ test_expect_success 'status: (split first edit) second edit and amend' '
        git rebase --continue &&
        git commit --amend -m "h" &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached from $ONTO
        # You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
        #   (use "git commit --amend" to amend the current commit)
        #   (use "git rebase --continue" once you are satisfied with your changes)
@@ -573,9 +576,10 @@ test_expect_success 'status when bisecting' '
        git bisect start &&
        git bisect bad &&
        git bisect good one_bisect &&
-       cat >expected <<-\EOF &&
-       # Not currently on any branch.
-       # You are currently bisecting branch '\''bisect'\''.
+       TGT=$(git rev-parse --short two_bisect) &&
+       cat >expected <<-EOF &&
+       # HEAD detached at $TGT
+       # You are currently bisecting, started from branch '\''bisect'\''.
        #   (use "git bisect reset" to get back to the original branch)
        #
        nothing to commit (use -u to show untracked files)
@@ -597,7 +601,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
        ONTO=$(git rev-parse --short HEAD^^) &&
        test_must_fail git rebase HEAD^ --onto HEAD^^ &&
        cat >expected <<-EOF &&
-       # Not currently on any branch.
+       # HEAD detached at $ONTO
        # You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
        #
        # Unmerged paths:
@@ -663,5 +667,15 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '
        test_i18ncmp expected actual
 '
 
+test_expect_success 'status showing detached from a tag' '
+       test_commit atag tagging &&
+       git checkout atag &&
+       cat >expected <<-\EOF
+       # HEAD detached at atag
+       nothing to commit (use -u to show untracked files)
+       EOF
+       git status --untracked-files=no >actual &&
+       test_i18ncmp expected actual
+'
 
 test_done
index 5e19598fe72787e17d521980a3edfacb1dfa2024..2f70433568e3134b09d7e1eb10cc5c8995c1a106 100755 (executable)
@@ -56,7 +56,8 @@ create_merge_msgs () {
                echo &&
                git log --no-merges ^HEAD c2 c3
        } >squash.1-5-9 &&
-       echo >msg.nolog &&
+       : >msg.nologff &&
+       echo >msg.nolognoff &&
        {
                echo "* tag 'c3':" &&
                echo "  commit 3" &&
@@ -244,8 +245,7 @@ test_expect_success 'merges with --ff-only' '
 test_expect_success 'merges with merge.ff=only' '
        git reset --hard c1 &&
        test_tick &&
-       test_when_finished "git config --unset merge.ff" &&
-       git config merge.ff only &&
+       test_config merge.ff "only" &&
        test_must_fail git merge c2 &&
        test_must_fail git merge c3 &&
        test_must_fail git merge c2 c3 &&
@@ -336,7 +336,7 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (no-commit in config)' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "--no-commit" &&
+       test_config branch.master.mergeoptions "--no-commit" &&
        git merge c2 &&
        verify_merge file result.1-5 &&
        verify_head $c1 &&
@@ -346,12 +346,11 @@ test_expect_success 'merge c1 with c2 (no-commit in config)' '
 test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (log in config)' '
-       git config branch.master.mergeoptions "" &&
        git reset --hard c1 &&
        git merge --log c2 &&
        git show -s --pretty=tformat:%s%n%b >expect &&
 
-       git config branch.master.mergeoptions --log &&
+       test_config branch.master.mergeoptions "--log" &&
        git reset --hard c1 &&
        git merge c2 &&
        git show -s --pretty=tformat:%s%n%b >actual &&
@@ -360,17 +359,12 @@ test_expect_success 'merge c1 with c2 (log in config)' '
 '
 
 test_expect_success 'merge c1 with c2 (log in config gets overridden)' '
-       test_when_finished "git config --remove-section branch.master" &&
-       test_when_finished "git config --remove-section merge" &&
-       test_might_fail git config --remove-section branch.master &&
-       test_might_fail git config --remove-section merge &&
-
        git reset --hard c1 &&
        git merge c2 &&
        git show -s --pretty=tformat:%s%n%b >expect &&
 
-       git config branch.master.mergeoptions "--no-log" &&
-       git config merge.log true &&
+       test_config branch.master.mergeoptions "--no-log" &&
+       test_config merge.log "true" &&
        git reset --hard c1 &&
        git merge c2 &&
        git show -s --pretty=tformat:%s%n%b >actual &&
@@ -380,7 +374,7 @@ test_expect_success 'merge c1 with c2 (log in config gets overridden)' '
 
 test_expect_success 'merge c1 with c2 (squash in config)' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "--squash" &&
+       test_config branch.master.mergeoptions "--squash" &&
        git merge c2 &&
        verify_merge file result.1-5 &&
        verify_head $c1 &&
@@ -392,7 +386,7 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'override config option -n with --summary' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "-n" &&
+       test_config branch.master.mergeoptions "-n" &&
        test_tick &&
        git merge --summary c2 >diffstat.txt &&
        verify_merge file result.1-5 msg.1-5 &&
@@ -406,7 +400,7 @@ test_expect_success 'override config option -n with --summary' '
 
 test_expect_success 'override config option -n with --stat' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "-n" &&
+       test_config branch.master.mergeoptions "-n" &&
        test_tick &&
        git merge --stat c2 >diffstat.txt &&
        verify_merge file result.1-5 msg.1-5 &&
@@ -422,7 +416,7 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'override config option --stat' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "--stat" &&
+       test_config branch.master.mergeoptions "--stat" &&
        test_tick &&
        git merge -n c2 >diffstat.txt &&
        verify_merge file result.1-5 msg.1-5 &&
@@ -438,7 +432,7 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (override --no-commit)' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "--no-commit" &&
+       test_config branch.master.mergeoptions "--no-commit" &&
        test_tick &&
        git merge --commit c2 &&
        verify_merge file result.1-5 msg.1-5 &&
@@ -449,7 +443,7 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c2 (override --squash)' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "--squash" &&
+       test_config branch.master.mergeoptions "--squash" &&
        test_tick &&
        git merge --no-squash c2 &&
        verify_merge file result.1-5 msg.1-5 &&
@@ -460,7 +454,6 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c0 with c1 (no-ff)' '
        git reset --hard c0 &&
-       git config branch.master.mergeoptions "" &&
        test_tick &&
        git merge --no-ff c1 &&
        verify_merge file result.1 &&
@@ -471,10 +464,9 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c0 with c1 (merge.ff=false)' '
        git reset --hard c0 &&
-       git config merge.ff false &&
+       test_config merge.ff "false" &&
        test_tick &&
        git merge c1 &&
-       git config --remove-section merge &&
        verify_merge file result.1 &&
        verify_parents $c0 $c1
 '
@@ -482,22 +474,19 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'combine branch.master.mergeoptions with merge.ff' '
        git reset --hard c0 &&
-       git config branch.master.mergeoptions --ff &&
-       git config merge.ff false &&
+       test_config branch.master.mergeoptions "--ff" &&
+       test_config merge.ff "false" &&
        test_tick &&
        git merge c1 &&
-       git config --remove-section "branch.master" &&
-       git config --remove-section "merge" &&
        verify_merge file result.1 &&
        verify_parents "$c0"
 '
 
 test_expect_success 'tolerate unknown values for merge.ff' '
        git reset --hard c0 &&
-       git config merge.ff something-new &&
+       test_config merge.ff "something-new" &&
        test_tick &&
        git merge c1 2>message &&
-       git config --remove-section "merge" &&
        verify_head "$c1" &&
        test_cmp empty message
 '
@@ -515,7 +504,7 @@ test_expect_success 'combining --ff-only and --no-ff is refused' '
 
 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
        git reset --hard c0 &&
-       git config branch.master.mergeoptions "--no-ff" &&
+       test_config branch.master.mergeoptions "--no-ff" &&
        git merge --ff c1 &&
        verify_merge file result.1 &&
        verify_head $c1
@@ -525,14 +514,20 @@ test_expect_success 'merge log message' '
        git reset --hard c0 &&
        git merge --no-log c2 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
-       test_cmp msg.nolog msg.act &&
+       test_cmp msg.nologff msg.act &&
+
+       git reset --hard c0 &&
+       test_config branch.master.mergeoptions "--no-ff" &&
+       git merge --no-log c2 &&
+       git show -s --pretty=format:%b HEAD >msg.act &&
+       test_cmp msg.nolognoff msg.act &&
 
        git merge --log c3 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
        test_cmp msg.log msg.act &&
 
        git reset --hard HEAD^ &&
-       git config merge.log yes &&
+       test_config merge.log "yes" &&
        git merge c3 &&
        git show -s --pretty=format:%b HEAD >msg.act &&
        test_cmp msg.log msg.act
@@ -542,7 +537,6 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "" &&
        test_tick &&
        git merge c0 c2 c0 c1 &&
        verify_merge file result.1-5 &&
@@ -553,7 +547,6 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c0, c2, c0, and c1' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "" &&
        test_tick &&
        git merge c0 c2 c0 c1 &&
        verify_merge file result.1-5 &&
@@ -564,7 +557,6 @@ test_debug 'git log --graph --decorate --oneline --all'
 
 test_expect_success 'merge c1 with c1 and c2' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "" &&
        test_tick &&
        git merge c1 c2 &&
        verify_merge file result.1-5 &&
index 3aab6e15000f57e76d05f70920525d5734e20ef7..a6bd99eaf51943a899aa5e5801479b0a84b6b692 100755 (executable)
@@ -23,16 +23,6 @@ prompt_given ()
        test "$prompt" = "Launch 'test-tool' [Y/n]: branch"
 }
 
-stdin_contains ()
-{
-       grep >/dev/null "$1"
-}
-
-stdin_doesnot_contain ()
-{
-       ! stdin_contains "$1"
-}
-
 # Create a file on master and change it on branch
 test_expect_success PERL 'setup' '
        echo master >file &&
@@ -296,24 +286,24 @@ test_expect_success PERL 'setup with 2 files different' '
 test_expect_success PERL 'say no to the first file' '
        (echo n && echo) >input &&
        git difftool -x cat branch <input >output &&
-       stdin_contains m2 <output &&
-       stdin_contains br2 <output &&
-       stdin_doesnot_contain master <output &&
-       stdin_doesnot_contain branch <output
+       grep m2 output &&
+       grep br2 output &&
+       ! grep master output &&
+       ! grep branch output
 '
 
 test_expect_success PERL 'say no to the second file' '
        (echo && echo n) >input &&
        git difftool -x cat branch <input >output &&
-       stdin_contains master <output &&
-       stdin_contains branch  <output &&
-       stdin_doesnot_contain m2 <output &&
-       stdin_doesnot_contain br2 <output
+       grep master output &&
+       grep branch output &&
+       ! grep m2 output &&
+       ! grep br2 output
 '
 
 test_expect_success PERL 'difftool --tool-help' '
        git difftool --tool-help >output &&
-       stdin_contains tool <output
+       grep tool output
 '
 
 test_expect_success PERL 'setup change in subdirectory' '
@@ -324,34 +314,97 @@ test_expect_success PERL 'setup change in subdirectory' '
        git commit -m "added sub/sub" &&
        echo test >>file &&
        echo test >>sub/sub &&
-       git add . &&
+       git add file sub/sub &&
        git commit -m "modified both"
 '
 
-test_expect_success PERL 'difftool -d' '
-       git difftool -d --extcmd ls branch >output &&
-       stdin_contains sub <output &&
-       stdin_contains file <output
+run_dir_diff_test () {
+       test_expect_success PERL "$1 --no-symlinks" "
+               symlinks=--no-symlinks &&
+               $2
+       "
+       test_expect_success PERL,SYMLINKS "$1 --symlinks" "
+               symlinks=--symlinks &&
+               $2
+       "
+}
+
+run_dir_diff_test 'difftool -d' '
+       git difftool -d $symlinks --extcmd ls branch >output &&
+       grep sub output &&
+       grep file output
 '
 
-test_expect_success PERL 'difftool --dir-diff' '
-       git difftool --dir-diff --extcmd ls branch >output &&
-       stdin_contains sub <output &&
-       stdin_contains file <output
+run_dir_diff_test 'difftool --dir-diff' '
+       git difftool --dir-diff $symlinks --extcmd ls branch >output &&
+       grep sub output &&
+       grep file output
 '
 
-test_expect_success PERL 'difftool --dir-diff ignores --prompt' '
-       git difftool --dir-diff --prompt --extcmd ls branch >output &&
-       stdin_contains sub <output &&
-       stdin_contains file <output
+run_dir_diff_test 'difftool --dir-diff ignores --prompt' '
+       git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output &&
+       grep sub output &&
+       grep file output
 '
 
-test_expect_success PERL 'difftool --dir-diff from subdirectory' '
+run_dir_diff_test 'difftool --dir-diff from subdirectory' '
        (
                cd sub &&
-               git difftool --dir-diff --extcmd ls branch >output &&
-               stdin_contains sub <output &&
-               stdin_contains file <output
+               git difftool --dir-diff $symlinks --extcmd ls branch >output &&
+               grep sub output &&
+               grep file output
+       )
+'
+
+write_script .git/CHECK_SYMLINKS <<\EOF
+for f in file file2 sub/sub
+do
+       echo "$f"
+       readlink "$2/$f"
+done >actual
+EOF
+
+test_expect_success PERL,SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' '
+       cat >expect <<-EOF &&
+       file
+       $(pwd)/file
+       file2
+       $(pwd)/file2
+       sub/sub
+       $(pwd)/sub/sub
+       EOF
+       git difftool --dir-diff --symlink \
+               --extcmd "./.git/CHECK_SYMLINKS" branch HEAD &&
+       test_cmp actual expect
+'
+
+write_script modify-file <<\EOF
+echo "new content" >file
+EOF
+
+test_expect_success PERL 'difftool --no-symlinks does not overwrite working tree file ' '
+       echo "orig content" >file &&
+       git difftool --dir-diff --no-symlinks --extcmd "$(pwd)/modify-file" branch &&
+       echo "new content" >expect &&
+       test_cmp expect file
+'
+
+write_script modify-both-files <<\EOF
+echo "wt content" >file &&
+echo "tmp content" >"$2/file" &&
+echo "$2" >tmpdir
+EOF
+
+test_expect_success PERL 'difftool --no-symlinks detects conflict ' '
+       (
+               TMPDIR=$TRASH_DIRECTORY &&
+               export TMPDIR &&
+               echo "orig content" >file &&
+               test_must_fail git difftool --dir-diff --no-symlinks --extcmd "$(pwd)/modify-both-files" branch &&
+               echo "wt content" >expect &&
+               test_cmp expect file &&
+               echo "tmp content" >expect &&
+               test_cmp expect "$(cat tmpdir)/file"
        )
 '
 
index a8957782cfb8fae4b7c171d4c9db24ce6cd5505e..e1951a5cbb759a0f233b624a1a1a7e9c55ff9640 100755 (executable)
@@ -125,11 +125,6 @@ test_expect_success 'modified file' '
        test_cmp empty out
 '
 
-test_config() {
-       git config "$1" "$2" &&
-       test_when_finished "git config --unset $1"
-}
-
 test_expect_success 'copes with color settings' '
        rm -f actual &&
        echo grep.h >expect &&
index 90bb6050c13ece02199b6978e43e3c67de563c84..6783c14c1ad9af0f5aa3d16e13250e9a1a292b28 100755 (executable)
@@ -539,8 +539,7 @@ test_expect_success \
         test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
         echo "ISO-8859-1" >> file &&
         git add file &&
-        git config i18n.commitencoding ISO-8859-1 &&
-        test_when_finished "git config --unset i18n.commitencoding" &&
+        test_config i18n.commitencoding ISO-8859-1 &&
         git commit -F "$TEST_DIRECTORY"/t3900/ISO8859-1.txt &&
         gitweb_run "p=.git;a=commit"'
 
index fa62d010f68e3ee97e6754687ad4d08564d3c96b..52510094add59b508e1581ffebfa555e7249561c 100644 (file)
@@ -91,6 +91,10 @@ q_to_tab () {
        tr Q '\011'
 }
 
+qz_to_tab_space () {
+       tr QZ '\011\040'
+}
+
 append_cr () {
        sed -e 's/$/Q/' | tr Q '\015'
 }
@@ -135,12 +139,12 @@ test_pause () {
        fi
 }
 
-# Call test_commit with the arguments "<message> [<file> [<contents>]]"
+# Call test_commit with the arguments "<message> [<file> [<contents> [<tag>]]]"
 #
 # This will commit a file with the given contents and the given commit
-# message.  It will also add a tag with <message> as name.
+# message, and tag the resulting commit with the given tag name.
 #
-# Both <file> and <contents> default to <message>.
+# <file>, <contents>, and <tag> all default to <message>.
 
 test_commit () {
        notick= &&
@@ -168,7 +172,7 @@ test_commit () {
                test_tick
        fi &&
        git commit $signoff -m "$1" &&
-       git tag "$1"
+       git tag "${4:-$1}"
 }
 
 # Call test_merge with the arguments "<message> <commit>", where <commit>
@@ -536,6 +540,9 @@ test_must_fail () {
        elif test $exit_code = 127; then
                echo >&2 "test_must_fail: command not found: $*"
                return 1
+       elif test $exit_code = 126; then
+               echo >&2 "test_must_fail: valgrind error: $*"
+               return 1
        fi
        return 0
 }
index 1f510252ad7cb7a5afae67731bc37fccf941b654..da57a2f45c3d76991f823364743fcdf73b524a91 100644 (file)
@@ -193,7 +193,11 @@ do
        --no-color)
                color=; shift ;;
        --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
-               valgrind=t; verbose=t; shift ;;
+               valgrind=memcheck
+               shift ;;
+       --valgrind=*)
+               valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
        --tee)
                shift ;; # was handled already
        --root=*)
@@ -204,6 +208,8 @@ do
        esac
 done
 
+test -n "$valgrind" && verbose=t
+
 if test -n "$color"
 then
        say_color () {
@@ -530,6 +536,8 @@ then
        PATH=$GIT_VALGRIND/bin:$PATH
        GIT_EXEC_PATH=$GIT_VALGRIND/bin
        export GIT_VALGRIND
+       GIT_VALGRIND_MODE="$valgrind"
+       export GIT_VALGRIND_MODE
 elif test -n "$GIT_TEST_INSTALLED"
 then
        GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
index 582b4dca9497363c7bfd6ae5ecc2c98d28d5ac0a..6b87c91b60cde4b19d6644227548f1ba6b226545 100755 (executable)
@@ -2,20 +2,27 @@
 
 base=$(basename "$0")
 
-TRACK_ORIGINS=
+TOOL_OPTIONS='--leak-check=no'
 
-VALGRIND_VERSION=$(valgrind --version)
-VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
-VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
-test 3 -gt "$VALGRIND_MAJOR" ||
-test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
-TRACK_ORIGINS=--track-origins=yes
+case "$GIT_VALGRIND_MODE" in
+memcheck-fast)
+       ;;
+memcheck)
+       VALGRIND_VERSION=$(valgrind --version)
+       VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
+       VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
+       test 3 -gt "$VALGRIND_MAJOR" ||
+       test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+       TOOL_OPTIONS="$TOOL_OPTIONS --track-origins=yes"
+       ;;
+*)
+       TOOL_OPTIONS="--tool=$GIT_VALGRIND_MODE"
+esac
 
 exec valgrind -q --error-exitcode=126 \
-       --leak-check=no \
-       --suppressions="$GIT_VALGRIND/default.supp" \
        --gen-suppressions=all \
-       $TRACK_ORIGINS \
+       --suppressions="$GIT_VALGRIND/default.supp" \
+       $TOOL_OPTIONS \
        --log-fd=4 \
        --input-fd=4 \
        $GIT_VALGRIND_OPTIONS \
index eb9eb33e0f0f151156ca646bb998c538f2471d7f..ba5d8afb1b04ba9d331c721fd9f730184fff2f23 100644 (file)
@@ -508,7 +508,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
        struct ref *refs;
 
        connect_setup(transport, for_push, 0);
-       get_remote_heads(data->fd[0], &refs,
+       get_remote_heads(data->fd[0], NULL, 0, &refs,
                         for_push ? REF_NORMAL : 0, &data->extra_have);
        data->got_remote_heads = 1;
 
@@ -537,7 +537,7 @@ static int fetch_refs_via_pack(struct transport *transport,
 
        if (!data->got_remote_heads) {
                connect_setup(transport, 0, 0);
-               get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
+               get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0, NULL);
                data->got_remote_heads = 1;
        }
 
@@ -795,7 +795,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
                struct ref *tmp_refs;
                connect_setup(transport, 1, 0);
 
-               get_remote_heads(data->fd[0], &tmp_refs, REF_NORMAL, NULL);
+               get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL);
                data->got_remote_heads = 1;
        }
 
index f5673ee4c2d65787040658c5e3ee06ecd129636d..bfa6279cc418278079bd95854adfe8d8301b6788 100644 (file)
@@ -44,7 +44,6 @@ static unsigned int timeout;
  * otherwise maximum packet size (up to 65520 bytes).
  */
 static int use_sideband;
-static int debug_fd;
 static int advertise_refs;
 static int stateless_rpc;
 
@@ -53,13 +52,6 @@ static void reset_timeout(void)
        alarm(timeout);
 }
 
-static int strip(char *line, int len)
-{
-       if (len && line[len-1] == '\n')
-               line[--len] = 0;
-       return len;
-}
-
 static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
 {
        if (use_sideband)
@@ -72,7 +64,8 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
                xwrite(fd, data, sz);
                return sz;
        }
-       return safe_write(fd, data, sz);
+       write_or_die(fd, data, sz);
+       return sz;
 }
 
 static FILE *pack_pipe = NULL;
@@ -415,7 +408,6 @@ static int ok_to_give_up(void)
 
 static int get_common_commits(void)
 {
-       static char line[1000];
        unsigned char sha1[20];
        char last_hex[41];
        int got_common = 0;
@@ -425,10 +417,10 @@ static int get_common_commits(void)
        save_commit_buffer = 0;
 
        for (;;) {
-               int len = packet_read_line(0, line, sizeof(line));
+               char *line = packet_read_line(0, NULL);
                reset_timeout();
 
-               if (!len) {
+               if (!line) {
                        if (multi_ack == 2 && got_common
                            && !got_other && ok_to_give_up()) {
                                sent_ready = 1;
@@ -447,7 +439,6 @@ static int get_common_commits(void)
                        got_other = 0;
                        continue;
                }
-               strip(line, len);
                if (!prefixcmp(line, "have ")) {
                        switch (got_sha1(line+5, sha1)) {
                        case -1: /* they have what we do not */
@@ -581,36 +572,33 @@ static void check_non_tip(void)
 static void receive_needs(void)
 {
        struct object_array shallows = OBJECT_ARRAY_INIT;
-       static char line[1000];
-       int len, depth = 0;
+       int depth = 0;
        int has_non_tip = 0;
 
        shallow_nr = 0;
-       if (debug_fd)
-               write_str_in_full(debug_fd, "#S\n");
        for (;;) {
                struct object *o;
                const char *features;
                unsigned char sha1_buf[20];
-               len = packet_read_line(0, line, sizeof(line));
+               char *line = packet_read_line(0, NULL);
                reset_timeout();
-               if (!len)
+               if (!line)
                        break;
-               if (debug_fd)
-                       write_in_full(debug_fd, line, len);
 
                if (!prefixcmp(line, "shallow ")) {
                        unsigned char sha1[20];
                        struct object *object;
-                       if (get_sha1(line + 8, sha1))
+                       if (get_sha1_hex(line + 8, sha1))
                                die("invalid shallow line: %s", line);
                        object = parse_object(sha1);
                        if (!object)
                                die("did not find object for %s", line);
                        if (object->type != OBJ_COMMIT)
                                die("invalid shallow object %s", sha1_to_hex(sha1));
-                       object->flags |= CLIENT_SHALLOW;
-                       add_object_array(object, NULL, &shallows);
+                       if (!(object->flags & CLIENT_SHALLOW)) {
+                               object->flags |= CLIENT_SHALLOW;
+                               add_object_array(object, NULL, &shallows);
+                       }
                        continue;
                }
                if (!prefixcmp(line, "deepen ")) {
@@ -657,8 +645,6 @@ static void receive_needs(void)
                        add_object_array(o, NULL, &want_obj);
                }
        }
-       if (debug_fd)
-               write_str_in_full(debug_fd, "#E\n");
 
        /*
         * We have sent all our refs already, and the other end
@@ -854,8 +840,6 @@ int main(int argc, char **argv)
        if (is_repository_shallow())
                die("attempt to fetch/clone from a shallow repository");
        git_config(upload_pack_config, NULL);
-       if (getenv("GIT_DEBUG_SEND_PACK"))
-               debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
        upload_pack();
        return 0;
 }
index 960f448cffd9ffd4e53763dfed3669bc374b8620..b50f99a9361926d2116c85e3e90a132fb9dab742 100644 (file)
@@ -1,5 +1,15 @@
 #include "cache.h"
 
+static void check_pipe(int err)
+{
+       if (err == EPIPE) {
+               signal(SIGPIPE, SIG_DFL);
+               raise(SIGPIPE);
+               /* Should never happen, but just in case... */
+               exit(141);
+       }
+}
+
 /*
  * Some cases use stdio, but want to flush after the write
  * to get error handling (and to get better interactive
@@ -34,8 +44,7 @@ void maybe_flush_or_die(FILE *f, const char *desc)
                        return;
        }
        if (fflush(f)) {
-               if (errno == EPIPE)
-                       exit(0);
+               check_pipe(errno);
                die_errno("write failure on '%s'", desc);
        }
 }
@@ -50,8 +59,7 @@ void fsync_or_die(int fd, const char *msg)
 void write_or_die(int fd, const void *buf, size_t count)
 {
        if (write_in_full(fd, buf, count) < 0) {
-               if (errno == EPIPE)
-                       exit(0);
+               check_pipe(errno);
                die_errno("write error");
        }
 }
@@ -59,8 +67,7 @@ void write_or_die(int fd, const void *buf, size_t count)
 int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
 {
        if (write_in_full(fd, buf, count) < 0) {
-               if (errno == EPIPE)
-                       exit(0);
+               check_pipe(errno);
                fprintf(stderr, "%s: write error (%s)\n",
                        msg, strerror(errno));
                return 0;
index 54f4391f9cc86d3d485ed80383cb142b1f25b1e7..cea8e55d8bc1d9117a6104c4181be9ff875c3ffa 100644 (file)
@@ -971,7 +971,7 @@ static void show_bisect_in_progress(struct wt_status *s,
 {
        if (state->branch)
                status_printf_ln(s, color,
-                                _("You are currently bisecting branch '%s'."),
+                                _("You are currently bisecting, started from branch '%s'."),
                                 state->branch);
        else
                status_printf_ln(s, color,
@@ -985,96 +985,164 @@ static void show_bisect_in_progress(struct wt_status *s,
 /*
  * Extract branch information from rebase/bisect
  */
-static void read_and_strip_branch(struct strbuf *sb,
-                                 const char **branch,
-                                 const char *path)
+static char *read_and_strip_branch(const char *path)
 {
+       struct strbuf sb = STRBUF_INIT;
        unsigned char sha1[20];
 
-       strbuf_reset(sb);
-       if (strbuf_read_file(sb, git_path("%s", path), 0) <= 0)
-               return;
+       if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
+               goto got_nothing;
 
-       while (sb->len && sb->buf[sb->len - 1] == '\n')
-               strbuf_setlen(sb, sb->len - 1);
-       if (!sb->len)
-               return;
-       if (!prefixcmp(sb->buf, "refs/heads/"))
-               *branch = sb->buf + strlen("refs/heads/");
-       else if (!prefixcmp(sb->buf, "refs/"))
-               *branch = sb->buf;
-       else if (!get_sha1_hex(sb->buf, sha1)) {
+       while (&sb.len && sb.buf[sb.len - 1] == '\n')
+               strbuf_setlen(&sb, sb.len - 1);
+       if (!sb.len)
+               goto got_nothing;
+       if (!prefixcmp(sb.buf, "refs/heads/"))
+               strbuf_remove(&sb,0, strlen("refs/heads/"));
+       else if (!prefixcmp(sb.buf, "refs/"))
+               ;
+       else if (!get_sha1_hex(sb.buf, sha1)) {
                const char *abbrev;
                abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV);
-               strbuf_reset(sb);
-               strbuf_addstr(sb, abbrev);
-               *branch = sb->buf;
-       } else if (!strcmp(sb->buf, "detached HEAD")) /* rebase */
-               ;
+               strbuf_reset(&sb);
+               strbuf_addstr(&sb, abbrev);
+       } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
+               goto got_nothing;
        else                    /* bisect */
-               *branch = sb->buf;
+               ;
+       return strbuf_detach(&sb, NULL);
+
+got_nothing:
+       strbuf_release(&sb);
+       return NULL;
 }
 
-static void wt_status_print_state(struct wt_status *s)
+struct grab_1st_switch_cbdata {
+       int found;
+       struct strbuf buf;
+       unsigned char nsha1[20];
+};
+
+static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
+                          const char *email, unsigned long timestamp, int tz,
+                          const char *message, void *cb_data)
 {
-       const char *state_color = color(WT_STATUS_HEADER, s);
-       struct strbuf branch = STRBUF_INIT;
-       struct strbuf onto = STRBUF_INIT;
-       struct wt_status_state state;
-       struct stat st;
+       struct grab_1st_switch_cbdata *cb = cb_data;
+       const char *target = NULL, *end;
 
-       memset(&state, 0, sizeof(state));
+       if (prefixcmp(message, "checkout: moving from "))
+               return 0;
+       message += strlen("checkout: moving from ");
+       target = strstr(message, " to ");
+       if (!target)
+               return 0;
+       target += strlen(" to ");
+       strbuf_reset(&cb->buf);
+       hashcpy(cb->nsha1, nsha1);
+       for (end = target; *end && *end != '\n'; end++)
+               ;
+       strbuf_add(&cb->buf, target, end - target);
+       cb->found = 1;
+       return 1;
+}
+
+static void wt_status_get_detached_from(struct wt_status_state *state)
+{
+       struct grab_1st_switch_cbdata cb;
+       struct commit *commit;
+       unsigned char sha1[20];
+       char *ref = NULL;
+
+       strbuf_init(&cb.buf, 0);
+       if (for_each_reflog_ent_reverse("HEAD", grab_1st_switch, &cb) <= 0) {
+               strbuf_release(&cb.buf);
+               return;
+       }
+
+       if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&
+           /* sha1 is a commit? match without further lookup */
+           (!hashcmp(cb.nsha1, sha1) ||
+            /* perhaps sha1 is a tag, try to dereference to a commit */
+            ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
+             !hashcmp(cb.nsha1, commit->object.sha1)))) {
+               int ofs;
+               if (!prefixcmp(ref, "refs/tags/"))
+                       ofs = strlen("refs/tags/");
+               else if (!prefixcmp(ref, "refs/remotes/"))
+                       ofs = strlen("refs/remotes/");
+               else
+                       ofs = 0;
+               state->detached_from = xstrdup(ref + ofs);
+       } else
+               state->detached_from =
+                       xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));
+       hashcpy(state->detached_sha1, cb.nsha1);
+
+       free(ref);
+       strbuf_release(&cb.buf);
+}
+
+void wt_status_get_state(struct wt_status_state *state,
+                        int get_detached_from)
+{
+       struct stat st;
 
        if (!stat(git_path("MERGE_HEAD"), &st)) {
-               state.merge_in_progress = 1;
+               state->merge_in_progress = 1;
        } else if (!stat(git_path("rebase-apply"), &st)) {
                if (!stat(git_path("rebase-apply/applying"), &st)) {
-                       state.am_in_progress = 1;
+                       state->am_in_progress = 1;
                        if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
-                               state.am_empty_patch = 1;
+                               state->am_empty_patch = 1;
                } else {
-                       state.rebase_in_progress = 1;
-                       read_and_strip_branch(&branch, &state.branch,
-                                             "rebase-apply/head-name");
-                       read_and_strip_branch(&onto, &state.onto,
-                                             "rebase-apply/onto");
+                       state->rebase_in_progress = 1;
+                       state->branch = read_and_strip_branch("rebase-apply/head-name");
+                       state->onto = read_and_strip_branch("rebase-apply/onto");
                }
        } else if (!stat(git_path("rebase-merge"), &st)) {
                if (!stat(git_path("rebase-merge/interactive"), &st))
-                       state.rebase_interactive_in_progress = 1;
+                       state->rebase_interactive_in_progress = 1;
                else
-                       state.rebase_in_progress = 1;
-               read_and_strip_branch(&branch, &state.branch,
-                                     "rebase-merge/head-name");
-               read_and_strip_branch(&onto, &state.onto,
-                                     "rebase-merge/onto");
+                       state->rebase_in_progress = 1;
+               state->branch = read_and_strip_branch("rebase-merge/head-name");
+               state->onto = read_and_strip_branch("rebase-merge/onto");
        } else if (!stat(git_path("CHERRY_PICK_HEAD"), &st)) {
-               state.cherry_pick_in_progress = 1;
+               state->cherry_pick_in_progress = 1;
        }
        if (!stat(git_path("BISECT_LOG"), &st)) {
-               state.bisect_in_progress = 1;
-               read_and_strip_branch(&branch, &state.branch,
-                                     "BISECT_START");
+               state->bisect_in_progress = 1;
+               state->branch = read_and_strip_branch("BISECT_START");
        }
 
-       if (state.merge_in_progress)
-               show_merge_in_progress(s, &state, state_color);
-       else if (state.am_in_progress)
-               show_am_in_progress(s, &state, state_color);
-       else if (state.rebase_in_progress || state.rebase_interactive_in_progress)
-               show_rebase_in_progress(s, &state, state_color);
-       else if (state.cherry_pick_in_progress)
-               show_cherry_pick_in_progress(s, &state, state_color);
-       if (state.bisect_in_progress)
-               show_bisect_in_progress(s, &state, state_color);
-       strbuf_release(&branch);
-       strbuf_release(&onto);
+       if (get_detached_from)
+               wt_status_get_detached_from(state);
+}
+
+static void wt_status_print_state(struct wt_status *s,
+                                 struct wt_status_state *state)
+{
+       const char *state_color = color(WT_STATUS_HEADER, s);
+       if (state->merge_in_progress)
+               show_merge_in_progress(s, state, state_color);
+       else if (state->am_in_progress)
+               show_am_in_progress(s, state, state_color);
+       else if (state->rebase_in_progress || state->rebase_interactive_in_progress)
+               show_rebase_in_progress(s, state, state_color);
+       else if (state->cherry_pick_in_progress)
+               show_cherry_pick_in_progress(s, state, state_color);
+       if (state->bisect_in_progress)
+               show_bisect_in_progress(s, state, state_color);
 }
 
 void wt_status_print(struct wt_status *s)
 {
        const char *branch_color = color(WT_STATUS_ONBRANCH, s);
        const char *branch_status_color = color(WT_STATUS_HEADER, s);
+       struct wt_status_state state;
+
+       memset(&state, 0, sizeof(state));
+       wt_status_get_state(&state,
+                           s->branch && !strcmp(s->branch, "HEAD"));
 
        if (s->branch) {
                const char *on_what = _("On branch ");
@@ -1082,9 +1150,19 @@ void wt_status_print(struct wt_status *s)
                if (!prefixcmp(branch_name, "refs/heads/"))
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
-                       branch_name = "";
                        branch_status_color = color(WT_STATUS_NOBRANCH, s);
-                       on_what = _("Not currently on any branch.");
+                       if (state.detached_from) {
+                               unsigned char sha1[20];
+                               branch_name = state.detached_from;
+                               if (!get_sha1("HEAD", sha1) &&
+                                   !hashcmp(sha1, state.detached_sha1))
+                                       on_what = _("HEAD detached at ");
+                               else
+                                       on_what = _("HEAD detached from ");
+                       } else {
+                               branch_name = "";
+                               on_what = _("Not currently on any branch.");
+                       }
                }
                status_printf(s, color(WT_STATUS_HEADER, s), "");
                status_printf_more(s, branch_status_color, "%s", on_what);
@@ -1093,7 +1171,11 @@ void wt_status_print(struct wt_status *s)
                        wt_status_print_tracking(s);
        }
 
-       wt_status_print_state(s);
+       wt_status_print_state(s, &state);
+       free(state.branch);
+       free(state.onto);
+       free(state.detached_from);
+
        if (s->is_initial) {
                status_printf_ln(s, color(WT_STATUS_HEADER, s), "");
                status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit"));
index 74208c06fd08b49041489c869d3c5f16804493da..be7a016173487780fdd4d37e24b4a47fcb766454 100644 (file)
@@ -80,13 +80,16 @@ struct wt_status_state {
        int rebase_interactive_in_progress;
        int cherry_pick_in_progress;
        int bisect_in_progress;
-       const char *branch;
-       const char *onto;
+       char *branch;
+       char *onto;
+       char *detached_from;
+       unsigned char detached_sha1[20];
 };
 
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);
 void wt_status_collect(struct wt_status *s);
+void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
 
 void wt_shortstatus_print(struct wt_status *s);
 void wt_porcelain_print(struct wt_status *s);