Merge branch 'kd/doc-for-each-ref' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 18 Oct 2017 05:19:10 +0000 (14:19 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 18 Oct 2017 05:19:10 +0000 (14:19 +0900)
Doc update.

* kd/doc-for-each-ref:
doc/for-each-ref: explicitly specify option names
doc/for-each-ref: consistently use '=' to between argument names and values

172 files changed:
Documentation/RelNotes/2.10.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.11.4.txt [new file with mode: 0644]
Documentation/RelNotes/2.12.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.13.6.txt [new file with mode: 0644]
Documentation/RelNotes/2.14.2.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-config.txt
Documentation/diff-options.txt
Documentation/git-for-each-ref.txt
Documentation/git-merge.txt
Documentation/git-pack-objects.txt
Documentation/git-shell.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/merge-strategies.txt
Documentation/pretty-formats.txt
Documentation/technical/api-builtin.txt [deleted file]
Documentation/technical/api-sub-process.txt [deleted file]
GIT-VERSION-GEN
Makefile
RelNotes
apply.c
apply.h
archive.c
builtin.h
builtin/add.c
builtin/am.c
builtin/branch.c
builtin/cat-file.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/describe.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/log.c
builtin/ls-files.c
builtin/merge.c
builtin/name-rev.c
builtin/pack-objects.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/show-branch.c
builtin/submodule--helper.c
builtin/tag.c
builtin/verify-tag.c
cache-tree.c
cache.h
color.c
commit.c
commit.h
compat/bswap.h
compat/poll/poll.c
compat/win32/syslog.c
config.c
contrib/coccinelle/array.cocci
contrib/diff-highlight/Makefile
convert.c
convert.h
credential-cache.c
diff.c
dir.c
entry.c
exec_cmd.c
git-archimport.perl
git-compat-util.h
git-cvsimport.perl
git-cvsserver.perl
git-gui/Makefile
git-gui/lib/choose_repository.tcl
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-rebase--am.sh
git-rebase--interactive.sh
git-request-pull.sh
git-send-email.perl
git-stash.sh
git-submodule.sh
git.c
gitweb/gitweb.perl
grep.c
http-push.c
http.c
mailinfo.c
notes-merge.c
object.c
object.h
path.c
perl/Git.pm
perl/Git/SVN.pm
pkt-line.c
pkt-line.h
pretty.c
read-cache.c
ref-filter.c
ref-filter.h
reflog-walk.c
reflog-walk.h
refs.c
refs.h
revision.c
revision.h
run-command.c
sequencer.c
sha1-lookup.c
sha1-lookup.h
sha1_file.c
shell.c
strbuf.c
string-list.c
string-list.h
sub-process.c
sub-process.h
submodule.c
t/check-non-portable-shell.pl
t/helper/test-path-utils.c
t/lib-gpg.sh
t/t0001-init.sh
t/t0021-conversion.sh
t/t0021/rot13-filter.pl
t/t1002-read-tree-m-u-2way.sh
t/t1411-reflog-show.sh
t/t1414-reflog-walk.sh [new file with mode: 0755]
t/t1450-fsck.sh
t/t3200-branch.sh
t/t3203-branch-output.sh
t/t3418-rebase-continue.sh
t/t3504-cherry-pick-rerere.sh
t/t3700-add.sh
t/t4062-diff-pickaxe.sh
t/t4124-apply-ws-rule.sh
t/t4150-am.sh
t/t4202-log.sh
t/t4207-log-decoration-colors.sh
t/t5001-archive-attr.sh
t/t5002-archive-attr-pattern.sh
t/t5004-archive-corner-cases.sh
t/t5150-request-pull.sh
t/t5308-pack-detect-duplicates.sh
t/t5526-fetch-submodules.sh
t/t5531-deep-submodule-push.sh
t/t6002-rev-list-bisect.sh
t/t6006-rev-list-format.sh
t/t6018-rev-list-glob.sh
t/t6120-describe.sh
t/t6300-for-each-ref.sh
t/t7004-tag.sh
t/t7006-pager.sh
t/t7400-submodule-basic.sh
t/t7600-merge.sh
t/t7810-grep.sh
t/t8008-blame-formats.sh
t/t8010-cat-file-filters.sh
t/t9001-send-email.sh
t/t9400-git-cvsserver-server.sh
t/test-lib-functions.sh
t/test-lib.sh
unpack-trees.c
userdiff.c
vcs-svn/fast_export.c
vcs-svn/fast_export.h
vcs-svn/repo_tree.c [deleted file]
vcs-svn/repo_tree.h [deleted file]
vcs-svn/svndump.c
diff --git a/Documentation/RelNotes/2.10.5.txt b/Documentation/RelNotes/2.10.5.txt
new file mode 100644 (file)
index 0000000..a498fd6
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.10.5 Release Notes
+=========================
+
+Fixes since v2.10.4
+-------------------
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
+
diff --git a/Documentation/RelNotes/2.11.4.txt b/Documentation/RelNotes/2.11.4.txt
new file mode 100644 (file)
index 0000000..ad4da8e
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.11.4 Release Notes
+=========================
+
+Fixes since v2.11.3
+-------------------
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
+
diff --git a/Documentation/RelNotes/2.12.5.txt b/Documentation/RelNotes/2.12.5.txt
new file mode 100644 (file)
index 0000000..8fa73cf
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.12.5 Release Notes
+=========================
+
+Fixes since v2.12.4
+-------------------
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
+
diff --git a/Documentation/RelNotes/2.13.6.txt b/Documentation/RelNotes/2.13.6.txt
new file mode 100644 (file)
index 0000000..afcae9c
--- /dev/null
@@ -0,0 +1,17 @@
+Git v2.13.6 Release Notes
+=========================
+
+Fixes since v2.13.5
+-------------------
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
+
diff --git a/Documentation/RelNotes/2.14.2.txt b/Documentation/RelNotes/2.14.2.txt
new file mode 100644 (file)
index 0000000..bec9186
--- /dev/null
@@ -0,0 +1,105 @@
+Git v2.14.2 Release Notes
+=========================
+
+Fixes since v2.14.1
+-------------------
+
+ * Because recent Git for Windows do come with a real msgfmt, the
+   build procedure for git-gui has been updated to use it instead of a
+   hand-rolled substitute.
+
+ * "%C(color name)" in the pretty print format always produced ANSI
+   color escape codes, which was an early design mistake.  They now
+   honor the configuration (e.g. "color.ui = never") and also tty-ness
+   of the output medium.
+
+ * The http.{sslkey,sslCert} configuration variables are to be
+   interpreted as a pathname that honors "~[username]/" prefix, but
+   weren't, which has been fixed.
+
+ * Numerous bugs in walking of reflogs via "log -g" and friends have
+   been fixed.
+
+ * "git commit" when seeing an totally empty message said "you did not
+   edit the message", which is clearly wrong.  The message has been
+   corrected.
+
+ * When a directory is not readable, "gitweb" fails to build the
+   project list.  Work this around by skipping such a directory.
+
+ * A recently added test for the "credential-cache" helper revealed
+   that EOF detection done around the time the connection to the cache
+   daemon is torn down were flaky.  This was fixed by reacting to
+   ECONNRESET and behaving as if we got an EOF.
+
+ * Some versions of GnuPG fail to kill gpg-agent it auto-spawned
+   and such a left-over agent can interfere with a test.  Work it
+   around by attempting to kill one before starting a new test.
+
+ * "git log --tag=no-such-tag" showed log starting from HEAD, which
+   has been fixed---it now shows nothing.
+
+ * The "tag.pager" configuration variable was useless for those who
+   actually create tag objects, as it interfered with the use of an
+   editor.  A new mechanism has been introduced for commands to enable
+   pager depending on what operation is being carried out to fix this,
+   and then "git tag -l" is made to run pager by default.
+
+ * "git push --recurse-submodules $there HEAD:$target" was not
+   propagated down to the submodules, but now it is.
+
+ * Commands like "git rebase" accepted the --rerere-autoupdate option
+   from the command line, but did not always use it.  This has been
+   fixed.
+
+ * "git clone --recurse-submodules --quiet" did not pass the quiet
+   option down to submodules.
+
+ * "git am -s" has been taught that some input may end with a trailer
+   block that is not Signed-off-by: and it should refrain from adding
+   an extra blank line before adding a new sign-off in such a case.
+
+ * "git svn" used with "--localtime" option did not compute the tz
+   offset for the timestamp in question and instead always used the
+   current time, which has been corrected.
+
+ * Memory leaks in a few error codepaths have been plugged.
+
+ * bash 4.4 or newer gave a warning on NUL byte in command
+   substitution done in "git stash"; this has been squelched.
+
+ * "git grep -L" and "git grep --quiet -L" reported different exit
+   codes; this has been corrected.
+
+ * When handshake with a subprocess filter notices that the process
+   asked for an unknown capability, Git did not report what program
+   the offending subprocess was running.  This has been corrected.
+
+ * "git apply" that is used as a better "patch -p1" failed to apply a
+   taken from a file with CRLF line endings to a file with CRLF line
+   endings.  The root cause was because it misused convert_to_git()
+   that tried to do "safe-crlf" processing by looking at the index
+   entry at the same path, which is a nonsense---in that mode, "apply"
+   is not working on the data in (or derived from) the index at all.
+   This has been fixed.
+
+ * Killing "git merge --edit" before the editor returns control left
+   the repository in a state with MERGE_MSG but without MERGE_HEAD,
+   which incorrectly tells the subsequent "git commit" that there was
+   a squash merge in progress.  This has been fixed.
+
+ * "git archive" did not work well with pathspecs and the
+   export-ignore attribute.
+
+ * "git cvsserver" no longer is invoked by "git daemon" by default,
+   as it is old and largely unmaintained.
+
+ * Various Perl scripts did not use safe_pipe_capture() instead of
+   backticks, leaving them susceptible to end-user input.  They have
+   been corrected.
+
+Also contains various documentation updates and code clean-ups.
+
+Credits go to joernchen <joernchen@phenoelit.de> for finding the
+unsafe constructs in "git cvsserver", and to Jeff King at GitHub for
+finding and fixing instances of the same issue in other scripts.
index d5c9c4cab60531d178d5c11d623a5c3f1036a0d0..2271809d90a5855c40de64bf1952c58a48b0d386 100644 (file)
@@ -216,15 +216,15 @@ boolean::
        synonyms are accepted for 'true' and 'false'; these are all
        case-insensitive.
 
-       true;; Boolean true can be spelled as `yes`, `on`, `true`,
-               or `1`.  Also, a variable defined without `= <value>`
+       true;; Boolean true literals are `yes`, `on`, `true`,
+               and `1`.  Also, a variable defined without `= <value>`
                is taken as true.
 
-       false;; Boolean false can be spelled as `no`, `off`,
-               `false`, or `0`.
+       false;; Boolean false literals are `no`, `off`, `false`,
+               `0` and the empty string.
 +
 When converting value to the canonical form using `--bool` type
-specifier; 'git config' will ensure that the output is "true" or
+specifier, 'git config' will ensure that the output is "true" or
 "false" (spelled in lowercase).
 
 integer::
@@ -2912,8 +2912,8 @@ sendemail.smtpsslcertpath::
 
 sendemail.<identity>.*::
        Identity-specific versions of the 'sendemail.*' parameters
-       found below, taking precedence over those when the this
-       identity is selected, through command-line or
+       found below, taking precedence over those when this
+       identity is selected, through either the command-line or
        `sendemail.identity`.
 
 sendemail.aliasesFile::
index cbce8ec63841e8631f7122dae6e084b15e16832d..5ca942ab5e2d5dfd67ceb06c5991f446b207de90 100644 (file)
@@ -200,7 +200,10 @@ diff.algorithm::
 +
 
 diff.wsErrorHighlight::
-       A comma separated list of `old`, `new`, `context`, that
-       specifies how whitespace errors on lines are highlighted
-       with `color.diff.whitespace`.  Can be overridden by the
-       command line option `--ws-error-highlight=<kind>`
+       Highlight whitespace errors in the `context`, `old` or `new`
+       lines of the diff.  Multiple values are separated by comma,
+       `none` resets previous values, `default` reset the list to
+       `new` and `all` is a shorthand for `old,new,context`.  The
+       whitespace errors are colored with `color.diff.whitespace`.
+       The command line option `--ws-error-highlight=<kind>`
+       overrides this setting.
index 43d18a4c5c894db93501d189880ff733ee36913b..56dedafcd4bd68c4192fbe22469428ed274a46a0 100644 (file)
@@ -300,15 +300,14 @@ ifndef::git-format-patch[]
        with --exit-code.
 
 --ws-error-highlight=<kind>::
-       Highlight whitespace errors on lines specified by <kind>
-       in the color specified by `color.diff.whitespace`.  <kind>
-       is a comma separated list of `old`, `new`, `context`.  When
-       this option is not given, only whitespace errors in `new`
-       lines are highlighted.  E.g. `--ws-error-highlight=new,old`
-       highlights whitespace errors on both deleted and added lines.
-       `all` can be used as a short-hand for `old,new,context`.
-       The `diff.wsErrorHighlight` configuration variable can be
-       used to specify the default behaviour.
+       Highlight whitespace errors in the `context`, `old` or `new`
+       lines of the diff.  Multiple values are separated by comma,
+       `none` resets previous values, `default` reset the list to
+       `new` and `all` is a shorthand for `old,new,context`.  When
+       this option is not given, and the configuration variable
+       `diff.wsErrorHighlight` is not set, only whitespace errors in
+       `new` lines are highlighted. The whitespace errors are colored
+       whith `color.diff.whitespace`.
 
 endif::git-format-patch[]
 
index d3a0994db27005bcc0f139014cfd18f4fc4529d8..66b4e0a4050655e7fab27a6fe72af03d6c99f3b2 100644 (file)
@@ -158,8 +158,10 @@ HEAD::
        otherwise.
 
 color::
-       Change output color.  Followed by `:<colorname>`, where names
-       are described in `color.branch.*`.
+       Change output color. Followed by `:<colorname>`, where color
+       names are described under Values in the "CONFIGURATION FILE"
+       section of linkgit:git-config[1].  For example,
+       `%(color:bold red)`.
 
 align::
        Left-, middle-, or right-align the content between
index 04fdd8cf086db6413a01421c306a80c9583f7fa4..f90faf7aaa250b2fea09d611e4efac31968ce6db 100644 (file)
@@ -280,7 +280,10 @@ After seeing a conflict, you can do two things:
 
  * Resolve the conflicts.  Git will mark the conflicts in
    the working tree.  Edit the files into shape and
-   'git add' them to the index.  Use 'git commit' to seal the deal.
+   'git add' them to the index.  Use 'git commit' or
+   'git merge --continue' to seal the deal. The latter command
+   checks whether there is a (interrupted) merge in progress
+   before calling 'git commit'.
 
 You can work through the conflict with a number of tools:
 
index 8973510a41c1e31319dad85d4987b3f0dfd7d8ca..473a16135abf867a77af26b35016dab0a2b6954d 100644 (file)
@@ -18,8 +18,9 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Reads list of objects from the standard input, and writes a packed
-archive with specified base-name, or to the standard output.
+Reads list of objects from the standard input, and writes either one or
+more packed archives with the specified base-name to disk, or a packed
+archive to the standard output.
 
 A packed archive is an efficient way to transfer a set of objects
 between two repositories as well as an access efficient archival
@@ -47,9 +48,9 @@ transport by their peers.
 OPTIONS
 -------
 base-name::
-       Write into a pair of files (.pack and .idx), using
+       Write into pairs of files (.pack and .idx), using
        <base-name> to determine the name of the created file.
-       When this option is used, the two files are written in
+       When this option is used, the two files in a pair are written in
        <base-name>-<SHA-1>.{pack,idx} files.  <SHA-1> is a hash
        based on the pack content and is written to the standard
        output of the command.
@@ -108,9 +109,13 @@ base-name::
        is taken from the `pack.windowMemory` configuration variable.
 
 --max-pack-size=<n>::
-       Maximum size of each output pack file. The size can be suffixed with
+       In unusual scenarios, you may not be able to create files
+       larger than a certain size on your filesystem, and this option
+       can be used to tell the command to split the output packfile
+       into multiple independent packfiles, each not larger than the
+       given size. The size can be suffixed with
        "k", "m", or "g". The minimum size allowed is limited to 1 MiB.
-       If specified, multiple packfiles may be created, which also
+       This option
        prevents the creation of a bitmap index.
        The default is unlimited, unless the config variable
        `pack.packSizeLimit` is set.
index 2e30a3e42d4e4e2bab580b0dcb5701ca3b33aeed..54cf2560bebbfc538644c636f696d4e500f1d852 100644 (file)
@@ -79,6 +79,22 @@ EOF
 $ chmod +x $HOME/git-shell-commands/no-interactive-login
 ----------------
 
+To enable git-cvsserver access (which should generally have the
+`no-interactive-login` example above as a prerequisite, as creating
+the git-shell-commands directory allows interactive logins):
+
+----------------
+$ cat >$HOME/git-shell-commands/cvs <<\EOF
+if ! test $# = 1 && test "$1" = "server"
+then
+       echo >&2 "git-cvsserver only handles \"server\""
+       exit 1
+fi
+exec git cvsserver server
+EOF
+$ chmod +x $HOME/git-shell-commands/cvs
+----------------
+
 SEE ALSO
 --------
 ssh(1),
index 32410f3d22f0a7961d99854599ea1d5b86e03e07..543fb425ee7c1d4c09a5ad4afbd00b9b812e4871 100644 (file)
@@ -205,6 +205,9 @@ it in the repository configuration as follows:
     signingKey = <gpg-keyid>
 -------------------------------------
 
+`pager.tag` is only respected when listing tags, i.e., when `-l` is
+used or implied. The default is to use a pager.
+See linkgit:git-config[1].
 
 DISCUSSION
 ----------
index 7dd5e03280b09f21f59289b0b00cd6fdee0ca3cb..6e3a6767e5f0ce347b2363cc7829d8eab042ae0a 100644 (file)
@@ -75,7 +75,8 @@ example the following invocations are equivalent:
 Note that omitting the `=` in `git -c foo.bar ...` is allowed and sets
 `foo.bar` to the boolean true value (just like `[foo]bar` would in a
 config file). Including the equals but with an empty value (like `git -c
-foo.bar= ...`) sets `foo.bar` to the empty string.
+foo.bar= ...`) sets `foo.bar` to the empty string which ` git config
+--bool` will convert to `false`.
 
 --exec-path[=<path>]::
        Path to wherever your core Git programs are installed.
index 2a2d7e2a4d2a9cab2af8bd881dd880ca2a52235a..4c68bc19d5108d1ba0c91b05d35acf8060eb2c9d 100644 (file)
@@ -151,7 +151,10 @@ unspecified.
 
 This attribute sets a specific line-ending style to be used in the
 working directory.  It enables end-of-line conversion without any
-content checks, effectively setting the `text` attribute.
+content checks, effectively setting the `text` attribute.  Note that
+setting this attribute on paths which are in the index with CRLF line
+endings may make the paths to be considered dirty.  Adding the path to
+the index again will normalize the line endings in the index.
 
 Set to string value "crlf"::
 
@@ -425,8 +428,8 @@ packet:          git< capability=clean
 packet:          git< capability=smudge
 packet:          git< 0000
 ------------------------
-Supported filter capabilities in version 2 are "clean" and
-"smudge".
+Supported filter capabilities in version 2 are "clean", "smudge",
+and "delay".
 
 Afterwards Git sends a list of "key=value" pairs terminated with
 a flush packet. The list will contain at least the filter command
@@ -512,12 +515,73 @@ the protocol then Git will stop the filter process and restart it
 with the next file that needs to be processed. Depending on the
 `filter.<driver>.required` flag Git will interpret that as error.
 
-After the filter has processed a blob it is expected to wait for
-the next "key=value" list containing a command. Git will close
+After the filter has processed a command it is expected to wait for
+a "key=value" list containing the next command. Git will close
 the command pipe on exit. The filter is expected to detect EOF
 and exit gracefully on its own. Git will wait until the filter
 process has stopped.
 
+Delay
+^^^^^
+
+If the filter supports the "delay" capability, then Git can send the
+flag "can-delay" after the filter command and pathname. This flag
+denotes that the filter can delay filtering the current blob (e.g. to
+compensate network latencies) by responding with no content but with
+the status "delayed" and a flush packet.
+------------------------
+packet:          git> command=smudge
+packet:          git> pathname=path/testfile.dat
+packet:          git> can-delay=1
+packet:          git> 0000
+packet:          git> CONTENT
+packet:          git> 0000
+packet:          git< status=delayed
+packet:          git< 0000
+------------------------
+
+If the filter supports the "delay" capability then it must support the
+"list_available_blobs" command. If Git sends this command, then the
+filter is expected to return a list of pathnames representing blobs
+that have been delayed earlier and are now available.
+The list must be terminated with a flush packet followed
+by a "success" status that is also terminated with a flush packet. If
+no blobs for the delayed paths are available, yet, then the filter is
+expected to block the response until at least one blob becomes
+available. The filter can tell Git that it has no more delayed blobs
+by sending an empty list. As soon as the filter responds with an empty
+list, Git stops asking. All blobs that Git has not received at this
+point are considered missing and will result in an error.
+
+------------------------
+packet:          git> command=list_available_blobs
+packet:          git> 0000
+packet:          git< pathname=path/testfile.dat
+packet:          git< pathname=path/otherfile.dat
+packet:          git< 0000
+packet:          git< status=success
+packet:          git< 0000
+------------------------
+
+After Git received the pathnames, it will request the corresponding
+blobs again. These requests contain a pathname and an empty content
+section. The filter is expected to respond with the smudged content
+in the usual way as explained above.
+------------------------
+packet:          git> command=smudge
+packet:          git> pathname=path/testfile.dat
+packet:          git> 0000
+packet:          git> 0000  # empty content!
+packet:          git< status=success
+packet:          git< 0000
+packet:          git< SMUDGED_CONTENT
+packet:          git< 0000
+packet:          git< 0000  # empty list, keep "status=success" unchanged!
+------------------------
+
+Example
+^^^^^^^
+
 A long running filter demo implementation can be found in
 `contrib/long-running-filter/example.pl` located in the Git
 core repository. If you develop your own long running filter
index 2eb92b93274df9fb5002336114654fe869903808..a09d5974639fa772ba86c5547fe70716fd044f73 100644 (file)
@@ -39,7 +39,8 @@ even look at what the other tree contains at all.  It discards everything
 the other tree did, declaring 'our' history contains all that happened in it.
 
 theirs;;
-       This is the opposite of 'ours'.
+       This is the opposite of 'ours'; note that, unlike 'ours', there is
+       no 'theirs' merge stragegy to confuse this merge option with.
 
 patience;;
        With this option, 'merge-recursive' spends a little extra time
index 4d6dac5770bed76cada2f53334e239c70d15a073..973d19606b63c314786de4965160b2c285987947 100644 (file)
@@ -173,13 +173,17 @@ endif::git-rev-list[]
 - '%Cblue': switch color to blue
 - '%Creset': reset color
 - '%C(...)': color specification, as described under Values in the
-  "CONFIGURATION FILE" section of linkgit:git-config[1];
-  adding `auto,` at the beginning (e.g. `%C(auto,red)`) will emit
-  color only when colors are enabled for log output (by `color.diff`,
-  `color.ui`, or `--color`, and respecting the `auto` settings of the
-  former if we are going to a terminal). `auto` alone (i.e.
-  `%C(auto)`) will turn on auto coloring on the next placeholders
-  until the color is switched again.
+  "CONFIGURATION FILE" section of linkgit:git-config[1].
+  By default, colors are shown only when enabled for log output (by
+  `color.diff`, `color.ui`, or `--color`, and respecting the `auto`
+  settings of the former if we are going to a terminal). `%C(auto,...)`
+  is accepted as a historical synonym for the default (e.g.,
+  `%C(auto,red)`). Specifying `%C(always,...) will show the colors
+  even when color is not otherwise enabled (though consider
+  just using `--color=always` to enable color for the whole output,
+  including this format and anything else git might color).  `auto`
+  alone (i.e. `%C(auto)`) will turn on auto coloring on the next
+  placeholders until the color is switched again.
 - '%m': left (`<`), right (`>`) or boundary (`-`) mark
 - '%n': newline
 - '%%': a raw '%'
diff --git a/Documentation/technical/api-builtin.txt b/Documentation/technical/api-builtin.txt
deleted file mode 100644 (file)
index 22a39b9..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-builtin API
-===========
-
-Adding a new built-in
----------------------
-
-There are 4 things to do to add a built-in command implementation to
-Git:
-
-. Define the implementation of the built-in command `foo` with
-  signature:
-
-       int cmd_foo(int argc, const char **argv, const char *prefix);
-
-. Add the external declaration for the function to `builtin.h`.
-
-. Add the command to the `commands[]` table defined in `git.c`.
-  The entry should look like:
-
-       { "foo", cmd_foo, <options> },
-+
-where options is the bitwise-or of:
-
-`RUN_SETUP`::
-       If there is not a Git directory to work on, abort.  If there
-       is a work tree, chdir to the top of it if the command was
-       invoked in a subdirectory.  If there is no work tree, no
-       chdir() is done.
-
-`RUN_SETUP_GENTLY`::
-       If there is a Git directory, chdir as per RUN_SETUP, otherwise,
-       don't chdir anywhere.
-
-`USE_PAGER`::
-
-       If the standard output is connected to a tty, spawn a pager and
-       feed our output to it.
-
-`NEED_WORK_TREE`::
-
-       Make sure there is a work tree, i.e. the command cannot act
-       on bare repositories.
-       This only makes sense when `RUN_SETUP` is also set.
-
-. Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`.
-
-Additionally, if `foo` is a new command, there are 3 more things to do:
-
-. Add tests to `t/` directory.
-
-. Write documentation in `Documentation/git-foo.txt`.
-
-. Add an entry for `git-foo` to `command-list.txt`.
-
-. Add an entry for `/git-foo` to `.gitignore`.
-
-
-How a built-in is called
-------------------------
-
-The implementation `cmd_foo()` takes three parameters, `argc`, `argv,
-and `prefix`.  The first two are similar to what `main()` of a
-standalone command would be called with.
-
-When `RUN_SETUP` is specified in the `commands[]` table, and when you
-were started from a subdirectory of the work tree, `cmd_foo()` is called
-after chdir(2) to the top of the work tree, and `prefix` gets the path
-to the subdirectory the command started from.  This allows you to
-convert a user-supplied pathname (typically relative to that directory)
-to a pathname relative to the top of the work tree.
-
-The return value from `cmd_foo()` becomes the exit status of the
-command.
diff --git a/Documentation/technical/api-sub-process.txt b/Documentation/technical/api-sub-process.txt
deleted file mode 100644 (file)
index 793508c..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-sub-process API
-===============
-
-The sub-process API makes it possible to run background sub-processes
-for the entire lifetime of a Git invocation. If Git needs to communicate
-with an external process multiple times, then this can reduces the process
-invocation overhead. Git and the sub-process communicate through stdin and
-stdout.
-
-The sub-processes are kept in a hashmap by command name and looked up
-via the subprocess_find_entry function.  If an existing instance can not
-be found then a new process should be created and started.  When the
-parent git command terminates, all sub-processes are also terminated.
-
-This API is based on the run-command API.
-
-Data structures
----------------
-
-* `struct subprocess_entry`
-
-The sub-process structure.  Members should not be accessed directly.
-
-Types
------
-
-'int(*subprocess_start_fn)(struct subprocess_entry *entry)'::
-
-       User-supplied function to initialize the sub-process.  This is
-       typically used to negotiate the interface version and capabilities.
-
-
-Functions
----------
-
-`cmd2process_cmp`::
-
-       Function to test two subprocess hashmap entries for equality.
-
-`subprocess_start`::
-
-       Start a subprocess and add it to the subprocess hashmap.
-
-`subprocess_stop`::
-
-       Kill a subprocess and remove it from the subprocess hashmap.
-
-`subprocess_find_entry`::
-
-       Find a subprocess in the subprocess hashmap.
-
-`subprocess_get_child_process`::
-
-       Get the underlying `struct child_process` from a subprocess.
-
-`subprocess_read_status`::
-
-       Helper function to read packets looking for the last "status=<foo>"
-       key/value pair.
index 2388ba13ca4b31d00888c5484da27b3e356d8fe4..1ab52e381e2f4e6bfb0e4d08fd339aee9b891c8d 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.14.1
+DEF_VER=v2.14.2
 
 LF='
 '
index 461c845d33cbc5f201096ea4b3e1048492cb0a6a..043ec8377b72ff3d86193982eb866a2f055f752c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2037,7 +2037,6 @@ XDIFF_OBJS += xdiff/xhistogram.o
 
 VCSSVN_OBJS += vcs-svn/line_buffer.o
 VCSSVN_OBJS += vcs-svn/sliding_window.o
-VCSSVN_OBJS += vcs-svn/repo_tree.o
 VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
index 86e33ed6820a9d3391011bde90852e9a593358db..29ef98331028ae5b1cdc10be75626e48b173a961 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.14.1.txt
\ No newline at end of file
+Documentation/RelNotes/2.14.2.txt
\ No newline at end of file
diff --git a/apply.c b/apply.c
index f2d599141d00c64d8c50b3f5519412ada94bcd6f..0c7b25989cdf58b411190744c97d93450be9b53e 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -80,7 +80,6 @@ int init_apply_state(struct apply_state *state,
 {
        memset(state, 0, sizeof(*state));
        state->prefix = prefix;
-       state->prefix_length = state->prefix ? strlen(state->prefix) : 0;
        state->lock_file = lock_file;
        state->newfd = -1;
        state->apply = 1;
@@ -220,6 +219,7 @@ struct patch {
        unsigned int recount:1;
        unsigned int conflicted_threeway:1;
        unsigned int direct_to_threeway:1;
+       unsigned int crlf_in_old:1;
        struct fragment *fragments;
        char *result;
        size_t resultsize;
@@ -786,11 +786,11 @@ static int guess_p_value(struct apply_state *state, const char *nameline)
                 * Does it begin with "a/$our-prefix" and such?  Then this is
                 * very likely to apply to our directory.
                 */
-               if (!strncmp(name, state->prefix, state->prefix_length))
+               if (starts_with(name, state->prefix))
                        val = count_slashes(state->prefix);
                else {
                        cp++;
-                       if (!strncmp(cp, state->prefix, state->prefix_length))
+                       if (starts_with(cp, state->prefix))
                                val = count_slashes(state->prefix) + 1;
                }
        }
@@ -1662,6 +1662,19 @@ static void check_whitespace(struct apply_state *state,
        record_ws_error(state, result, line + 1, len - 2, state->linenr);
 }
 
+/*
+ * Check if the patch has context lines with CRLF or
+ * the patch wants to remove lines with CRLF.
+ */
+static void check_old_for_crlf(struct patch *patch, const char *line, int len)
+{
+       if (len >= 2 && line[len-1] == '\n' && line[len-2] == '\r') {
+               patch->ws_rule |= WS_CR_AT_EOL;
+               patch->crlf_in_old = 1;
+       }
+}
+
+
 /*
  * Parse a unified diff. Note that this really needs to parse each
  * fragment separately, since the only way to know the difference
@@ -1712,11 +1725,14 @@ static int parse_fragment(struct apply_state *state,
                        if (!deleted && !added)
                                leading++;
                        trailing++;
+                       check_old_for_crlf(patch, line, len);
                        if (!state->apply_in_reverse &&
                            state->ws_error_action == correct_ws_error)
                                check_whitespace(state, line, len, patch->ws_rule);
                        break;
                case '-':
+                       if (!state->apply_in_reverse)
+                               check_old_for_crlf(patch, line, len);
                        if (state->apply_in_reverse &&
                            state->ws_error_action != nowarn_ws_error)
                                check_whitespace(state, line, len, patch->ws_rule);
@@ -1725,6 +1741,8 @@ static int parse_fragment(struct apply_state *state,
                        trailing = 0;
                        break;
                case '+':
+                       if (state->apply_in_reverse)
+                               check_old_for_crlf(patch, line, len);
                        if (!state->apply_in_reverse &&
                            state->ws_error_action != nowarn_ws_error)
                                check_whitespace(state, line, len, patch->ws_rule);
@@ -2089,10 +2107,9 @@ static int use_patch(struct apply_state *state, struct patch *p)
        int i;
 
        /* Paths outside are not touched regardless of "--include" */
-       if (0 < state->prefix_length) {
-               int pathlen = strlen(pathname);
-               if (pathlen <= state->prefix_length ||
-                   memcmp(state->prefix, pathname, state->prefix_length))
+       if (state->prefix && *state->prefix) {
+               const char *rest;
+               if (!skip_prefix(pathname, state->prefix, &rest) || !*rest)
                        return 0;
        }
 
@@ -2268,8 +2285,11 @@ static void show_stats(struct apply_state *state, struct patch *patch)
                add, pluses, del, minuses);
 }
 
-static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
+static int read_old_data(struct stat *st, struct patch *patch,
+                        const char *path, struct strbuf *buf)
 {
+       enum safe_crlf safe_crlf = patch->crlf_in_old ?
+               SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
@@ -2278,7 +2298,15 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
        case S_IFREG:
                if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
                        return error(_("unable to open or read %s"), path);
-               convert_to_git(&the_index, path, buf->buf, buf->len, buf, 0);
+               /*
+                * "git apply" without "--index/--cached" should never look
+                * at the index; the target file may not have been added to
+                * the index yet, and we may not even be in any Git repository.
+                * Pass NULL to convert_to_git() to stress this; the function
+                * should never look at the index when explicit crlf option
+                * is given.
+                */
+               convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
                return 0;
        default:
                return -1;
@@ -2809,13 +2837,10 @@ static void update_image(struct apply_state *state,
                img->line_allocated = img->line;
        }
        if (preimage_limit != postimage->nr)
-               memmove(img->line + applied_pos + postimage->nr,
-                       img->line + applied_pos + preimage_limit,
-                       (img->nr - (applied_pos + preimage_limit)) *
-                       sizeof(*img->line));
-       memcpy(img->line + applied_pos,
-              postimage->line,
-              postimage->nr * sizeof(*img->line));
+               MOVE_ARRAY(img->line + applied_pos + postimage->nr,
+                          img->line + applied_pos + preimage_limit,
+                          img->nr - (applied_pos + preimage_limit));
+       COPY_ARRAY(img->line + applied_pos, postimage->line, postimage->nr);
        if (!state->allow_overlap)
                for (i = 0; i < postimage->nr; i++)
                        img->line[applied_pos + i].flag |= LINE_PATCHED;
@@ -3384,6 +3409,7 @@ static int load_patch_target(struct apply_state *state,
                             struct strbuf *buf,
                             const struct cache_entry *ce,
                             struct stat *st,
+                            struct patch *patch,
                             const char *name,
                             unsigned expected_mode)
 {
@@ -3399,7 +3425,7 @@ static int load_patch_target(struct apply_state *state,
                } else if (has_symlink_leading_path(name, strlen(name))) {
                        return error(_("reading from '%s' beyond a symbolic link"), name);
                } else {
-                       if (read_old_data(st, name, buf))
+                       if (read_old_data(st, patch, name, buf))
                                return error(_("failed to read %s"), name);
                }
        }
@@ -3432,7 +3458,7 @@ static int load_preimage(struct apply_state *state,
                /* We have a patched copy in memory; use that. */
                strbuf_add(&buf, previous->result, previous->resultsize);
        } else {
-               status = load_patch_target(state, &buf, ce, st,
+               status = load_patch_target(state, &buf, ce, st, patch,
                                           patch->old_name, patch->old_mode);
                if (status < 0)
                        return status;
@@ -3520,7 +3546,7 @@ static int load_current(struct apply_state *state,
        if (verify_index_match(ce, &st))
                return error(_("%s: does not match index"), name);
 
-       status = load_patch_target(state, &buf, ce, &st, name, mode);
+       status = load_patch_target(state, &buf, ce, &st, patch, name, mode);
        if (status < 0)
                return status;
        else if (status)
diff --git a/apply.h b/apply.h
index b3d6783d55344de5aaa3d4b81a22abed0b6972fb..d9b395770364b5495fc1136248efd64bd3a5e66d 100644 (file)
--- a/apply.h
+++ b/apply.h
@@ -35,7 +35,6 @@ enum apply_verbosity {
 
 struct apply_state {
        const char *prefix;
-       int prefix_length;
 
        /* These are lock_file related */
        struct lock_file *lock_file;
index 60b3035a7a6a9e7c2e69b4ba7de00ebdf8bfdbb8..f81bd3649a1f4bf20bc03100d12fc157401bdc14 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -103,17 +103,34 @@ struct archiver_context {
        struct directory *bottom;
 };
 
+static const struct attr_check *get_archive_attrs(const char *path)
+{
+       static struct attr_check *check;
+       if (!check)
+               check = attr_check_initl("export-ignore", "export-subst", NULL);
+       return git_check_attr(path, check) ? NULL : check;
+}
+
+static int check_attr_export_ignore(const struct attr_check *check)
+{
+       return check && ATTR_TRUE(check->items[0].value);
+}
+
+static int check_attr_export_subst(const struct attr_check *check)
+{
+       return check && ATTR_TRUE(check->items[1].value);
+}
+
 static int write_archive_entry(const unsigned char *sha1, const char *base,
                int baselen, const char *filename, unsigned mode, int stage,
                void *context)
 {
        static struct strbuf path = STRBUF_INIT;
-       static struct attr_check *check;
        struct archiver_context *c = context;
        struct archiver_args *args = c->args;
        write_archive_entry_fn_t write_entry = c->write_entry;
-       const char *path_without_prefix;
        int err;
+       const char *path_without_prefix;
 
        args->convert = 0;
        strbuf_reset(&path);
@@ -125,12 +142,12 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                strbuf_addch(&path, '/');
        path_without_prefix = path.buf + args->baselen;
 
-       if (!check)
-               check = attr_check_initl("export-ignore", "export-subst", NULL);
-       if (!git_check_attr(path_without_prefix, check)) {
-               if (ATTR_TRUE(check->items[0].value))
+       if (!S_ISDIR(mode)) {
+               const struct attr_check *check;
+               check = get_archive_attrs(path_without_prefix);
+               if (check_attr_export_ignore(check))
                        return 0;
-               args->convert = ATTR_TRUE(check->items[1].value);
+               args->convert = check_attr_export_subst(check);
        }
 
        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
@@ -147,14 +164,6 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
        return write_entry(args, sha1, path.buf, path.len, mode);
 }
 
-static int write_archive_entry_buf(const unsigned char *sha1, struct strbuf *base,
-               const char *filename, unsigned mode, int stage,
-               void *context)
-{
-       return write_archive_entry(sha1, base->buf, base->len,
-                                    filename, mode, stage, context);
-}
-
 static void queue_directory(const unsigned char *sha1,
                struct strbuf *base, const char *filename,
                unsigned mode, int stage, struct archiver_context *c)
@@ -204,6 +213,17 @@ static int queue_or_write_archive_entry(const unsigned char *sha1,
        }
 
        if (S_ISDIR(mode)) {
+               size_t baselen = base->len;
+               const struct attr_check *check;
+
+               /* Borrow base, but restore its original value when done. */
+               strbuf_addstr(base, filename);
+               strbuf_addch(base, '/');
+               check = get_archive_attrs(base->buf);
+               strbuf_setlen(base, baselen);
+
+               if (check_attr_export_ignore(check))
+                       return 0;
                queue_directory(sha1, base, filename,
                                mode, stage, c);
                return READ_TREE_RECURSIVE;
@@ -257,9 +277,7 @@ int write_archive_entries(struct archiver_args *args,
        }
 
        err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
-                                 args->pathspec.has_wildcard ?
-                                 queue_or_write_archive_entry :
-                                 write_archive_entry_buf,
+                                 queue_or_write_archive_entry,
                                  &context);
        if (err == READ_TREE_RECURSIVE)
                err = 0;
index 498ac80d07bc5b00a9c66e9e62449d7700bc7550..42378f3aa471eb79594d96736ad2410b54d6c4dd 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -6,6 +6,94 @@
 #include "cache.h"
 #include "commit.h"
 
+/*
+ * builtin API
+ * ===========
+ *
+ * Adding a new built-in
+ * ---------------------
+ *
+ * There are 4 things to do to add a built-in command implementation to
+ * Git:
+ *
+ * . Define the implementation of the built-in command `foo` with
+ *   signature:
+ *
+ *     int cmd_foo(int argc, const char **argv, const char *prefix);
+ *
+ * . Add the external declaration for the function to `builtin.h`.
+ *
+ * . Add the command to the `commands[]` table defined in `git.c`.
+ *   The entry should look like:
+ *
+ *     { "foo", cmd_foo, <options> },
+ *
+ * where options is the bitwise-or of:
+ *
+ * `RUN_SETUP`:
+ *     If there is not a Git directory to work on, abort.  If there
+ *     is a work tree, chdir to the top of it if the command was
+ *     invoked in a subdirectory.  If there is no work tree, no
+ *     chdir() is done.
+ *
+ * `RUN_SETUP_GENTLY`:
+ *     If there is a Git directory, chdir as per RUN_SETUP, otherwise,
+ *     don't chdir anywhere.
+ *
+ * `USE_PAGER`:
+ *
+ *     If the standard output is connected to a tty, spawn a pager and
+ *     feed our output to it.
+ *
+ * `NEED_WORK_TREE`:
+ *
+ *     Make sure there is a work tree, i.e. the command cannot act
+ *     on bare repositories.
+ *     This only makes sense when `RUN_SETUP` is also set.
+ *
+ * `SUPPORT_SUPER_PREFIX`:
+ *
+ *     The built-in supports `--super-prefix`.
+ *
+ * `DELAY_PAGER_CONFIG`:
+ *
+ *     If RUN_SETUP or RUN_SETUP_GENTLY is set, git.c normally handles
+ *     the `pager.<cmd>`-configuration. If this flag is used, git.c
+ *     will skip that step, instead allowing the built-in to make a
+ *     more informed decision, e.g., by ignoring `pager.<cmd>` for
+ *     certain subcommands.
+ *
+ * . Add `builtin/foo.o` to `BUILTIN_OBJS` in `Makefile`.
+ *
+ * Additionally, if `foo` is a new command, there are 4 more things to do:
+ *
+ * . Add tests to `t/` directory.
+ *
+ * . Write documentation in `Documentation/git-foo.txt`.
+ *
+ * . Add an entry for `git-foo` to `command-list.txt`.
+ *
+ * . Add an entry for `/git-foo` to `.gitignore`.
+ *
+ *
+ * How a built-in is called
+ * ------------------------
+ *
+ * The implementation `cmd_foo()` takes three parameters, `argc`, `argv,
+ * and `prefix`.  The first two are similar to what `main()` of a
+ * standalone command would be called with.
+ *
+ * When `RUN_SETUP` is specified in the `commands[]` table, and when you
+ * were started from a subdirectory of the work tree, `cmd_foo()` is called
+ * after chdir(2) to the top of the work tree, and `prefix` gets the path
+ * to the subdirectory the command started from.  This allows you to
+ * convert a user-supplied pathname (typically relative to that directory)
+ * to a pathname relative to the top of the work tree.
+ *
+ * The return value from `cmd_foo()` becomes the exit status of the
+ * command.
+ */
+
 #define DEFAULT_MERGE_LOG_LEN 20
 
 extern const char git_usage_string[];
@@ -25,6 +113,18 @@ struct fmt_merge_msg_opts {
 extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                         struct fmt_merge_msg_opts *);
 
+/**
+ * If a built-in has DELAY_PAGER_CONFIG set, the built-in should call this early
+ * when it wishes to respect the `pager.foo`-config. The `cmd` is the name of
+ * the built-in, e.g., "foo". If a paging-choice has already been setup, this
+ * does nothing. The default in `def` should be 0 for "pager off", 1 for "pager
+ * on" or -1 for "punt".
+ *
+ * You should most likely use a default of 0 or 1. "Punt" (-1) could be useful
+ * to be able to fall back to some historical compatibility name.
+ */
+extern void setup_auto_pager(const char *cmd, int def);
+
 extern int is_builtin(const char *s);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
index e888fb8c5f2a1fa2be33e834724460dec8072726..5d5773d5cd2fc9e713498a72da7b57b84fd25d19 100644 (file)
@@ -32,7 +32,7 @@ struct update_callback_data {
        int add_errors;
 };
 
-static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
+static void chmod_pathspec(struct pathspec *pathspec, char flip)
 {
        int i;
 
@@ -42,8 +42,8 @@ static void chmod_pathspec(struct pathspec *pathspec, int force_mode)
                if (pathspec && !ce_path_match(ce, pathspec, NULL))
                        continue;
 
-               if (chmod_cache_entry(ce, force_mode) < 0)
-                       fprintf(stderr, "cannot chmod '%s'", ce->name);
+               if (chmod_cache_entry(ce, flip) < 0)
+                       fprintf(stderr, "cannot chmod %cx '%s'\n", flip, ce->name);
        }
 }
 
index c973bd96dcb5d630d56e935733bfa4530ccd2872..73f542bec51d77b92d39ced0dfefcc5b6b649db8 100644 (file)
@@ -431,6 +431,14 @@ static void am_load(struct am_state *state)
        read_state_file(&sb, state, "utf8", 1);
        state->utf8 = !strcmp(sb.buf, "t");
 
+       if (file_exists(am_path(state, "rerere-autoupdate"))) {
+               read_state_file(&sb, state, "rerere-autoupdate", 1);
+               state->allow_rerere_autoupdate = strcmp(sb.buf, "t") ?
+                       RERERE_NOAUTOUPDATE : RERERE_AUTOUPDATE;
+       } else {
+               state->allow_rerere_autoupdate = 0;
+       }
+
        read_state_file(&sb, state, "keep", 1);
        if (!strcmp(sb.buf, "t"))
                state->keep = KEEP_TRUE;
@@ -1003,6 +1011,10 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
        write_state_bool(state, "sign", state->signoff);
        write_state_bool(state, "utf8", state->utf8);
 
+       if (state->allow_rerere_autoupdate)
+               write_state_bool(state, "rerere-autoupdate",
+                        state->allow_rerere_autoupdate == RERERE_AUTOUPDATE);
+
        switch (state->keep) {
        case KEEP_FALSE:
                str = "f";
@@ -1181,34 +1193,10 @@ static void NORETURN die_user_resolve(const struct am_state *state)
  */
 static void am_append_signoff(struct am_state *state)
 {
-       char *cp;
-       struct strbuf mine = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
 
        strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
-
-       /* our sign-off */
-       strbuf_addf(&mine, "\n%s%s\n",
-                   sign_off_header,
-                   fmt_name(getenv("GIT_COMMITTER_NAME"),
-                            getenv("GIT_COMMITTER_EMAIL")));
-
-       /* Does sb end with it already? */
-       if (mine.len < sb.len &&
-           !strcmp(mine.buf, sb.buf + sb.len - mine.len))
-               goto exit; /* no need to duplicate */
-
-       /* Does it have any Signed-off-by: in the text */
-       for (cp = sb.buf;
-            cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
-            cp = strchr(cp, '\n')) {
-               if (sb.buf == cp || cp[-1] == '\n')
-                       break;
-       }
-
-       strbuf_addstr(&sb, mine.buf + !!cp);
-exit:
-       strbuf_release(&mine);
+       append_signoff(&sb, 0, 0);
        state->msg = strbuf_detach(&sb, &state->msg_len);
 }
 
index 8a0595e11587aeac0f784400c43609404da3e045..16d391b407c9fa688b48a3e28f77800dcc1e2e0c 100644 (file)
@@ -92,7 +92,7 @@ static int git_branch_config(const char *var, const char *value, void *cb)
                        return config_error_nonbool(var);
                return color_parse(value, branch_colors[slot]);
        }
-       return git_color_default_config(var, value, cb);
+       return git_default_config(var, value, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
@@ -383,7 +383,7 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
        return strbuf_detach(&fmt, NULL);
 }
 
-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, struct ref_format *format)
 {
        int i;
        struct ref_array array;
@@ -407,14 +407,17 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
        if (filter->verbose)
                maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
 
-       if (!format)
-               format = to_free = build_format(filter, maxwidth, remote_prefix);
-       verify_ref_format(format);
+       if (!format->format)
+               format->format = to_free = build_format(filter, maxwidth, remote_prefix);
+       format->use_color = branch_use_color;
+
+       if (verify_ref_format(format))
+               die(_("unable to parse format string"));
 
        ref_array_sort(sorting, &array);
 
        for (i = 0; i < array.nr; i++) {
-               format_ref_array_item(array.items[i], format, 0, &out);
+               format_ref_array_item(array.items[i], format, &out);
                if (column_active(colopts)) {
                        assert(!filter->verbose && "--column and --verbose are incompatible");
                         /* format to a string_list to let print_columns() do its job */
@@ -549,7 +552,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        struct ref_filter filter;
        int icase = 0;
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
-       const char *format = NULL;
+       struct ref_format format = REF_FORMAT_INIT;
 
        struct option options[] = {
                OPT_GROUP(N_("Generic options")),
@@ -593,7 +596,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        N_("print only branches of the object"), 0, parse_opt_object_name
                },
                OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
-               OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
+               OPT_STRING(  0 , "format", &format.format, N_("format"), N_("format to use for the output")),
                OPT_END(),
        };
 
@@ -667,7 +670,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                if (!sorting)
                        sorting = ref_default_sorting();
                sorting->ignore_case = icase;
-               print_ref_list(&filter, sorting, format);
+               print_ref_list(&filter, sorting, &format);
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
                return 0;
index 96b786e4892aec0ed7e5d07ac8d3261c7b765f33..188ddc3e507acdae6e3fb361a3763da620efcb90 100644 (file)
@@ -96,7 +96,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                return !has_object_file(&oid);
 
        case 'w':
-               if (!path[0])
+               if (!path)
                        die("git cat-file --filters %s: <object> must be "
                            "<sha1:path>", obj_name);
 
@@ -106,7 +106,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                break;
 
        case 'c':
-               if (!path[0])
+               if (!path)
                        die("git cat-file --textconv %s: <object> must be <sha1:path>",
                            obj_name);
 
index 9661e1bcba31ffa4f7b8a2fb1a9d2060cb8efda7..2d75ac66c7f8447e846e0bc38072b76bddc3672b 100644 (file)
@@ -358,6 +358,8 @@ static int checkout_paths(const struct checkout_opts *opts,
        state.force = 1;
        state.refresh_cache = 1;
        state.istate = &the_index;
+
+       enable_delayed_checkout(&state);
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
                if (ce->ce_flags & CE_MATCHED) {
@@ -372,6 +374,7 @@ static int checkout_paths(const struct checkout_opts *opts,
                        pos = skip_same_name(ce, pos) - 1;
                }
        }
+       errs |= finish_delayed_checkout(&state);
 
        if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
index 057fc97fe4494338e6a85ac9f695563f1fcb9596..c1bafda5b63324b0aba7400535cc0ba745e46004 100644 (file)
@@ -125,8 +125,7 @@ static int git_clean_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       /* inspect the color.ui config variable and others */
-       return git_color_default_config(var, value, cb);
+       return git_default_config(var, value, cb);
 }
 
 static const char *clean_get_color(enum color_clean ix)
index 08b5cc433c6fcad5eea2dfc3321aea28a599d51f..f7e17d22951cfd8e498143c009fa0303d0ff8319 100644 (file)
@@ -768,6 +768,9 @@ static int checkout(int submodule_progress)
                if (submodule_progress)
                        argv_array_push(&args, "--progress");
 
+               if (option_verbosity < 0)
+                       argv_array_push(&args, "--quiet");
+
                err = run_command_v_opt(args.argv, RUN_GIT_CMD);
                argv_array_clear(&args);
        }
index a4a923d7c0b688e162c8e4d0411ff9b72748fb5c..c1de41c67f8c245ab53b3c3c86a9ad981f433917 100644 (file)
@@ -102,7 +102,6 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                        if (fd && close(fd))
                                die_errno("git commit-tree: failed to close '%s'",
                                          argv[i]);
-                       strbuf_complete_line(&buffer);
                        continue;
                }
 
index 8e93802511e2a798e43d30da9218a56f97404dc7..1a0da71a43cfef8818b52a3d27131e340725cef2 100644 (file)
@@ -1739,17 +1739,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        if (verbose || /* Truncate the message just before the diff, if any. */
            cleanup_mode == CLEANUP_SCISSORS)
                strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
-
        if (cleanup_mode != CLEANUP_NONE)
                strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
-       if (template_untouched(&sb) && !allow_empty_message) {
+
+       if (message_is_empty(&sb) && !allow_empty_message) {
                rollback_index_files();
-               fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
+               fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
                exit(1);
        }
-       if (message_is_empty(&sb) && !allow_empty_message) {
+       if (template_untouched(&sb) && !allow_empty_message) {
                rollback_index_files();
-               fprintf(stderr, _("Aborting commit due to empty commit message.\n"));
+               fprintf(stderr, _("Aborting commit; you did not edit the message.\n"));
                exit(1);
        }
 
index 89ea1cdd60a215473ecb52ff2a4ce51932e1cf6d..94ff2fba0b024dd351d65f11510cb07070084d4d 100644 (file)
@@ -155,18 +155,21 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
         * pattern.
         */
        if (patterns.nr) {
+               int found = 0;
                struct string_list_item *item;
 
                if (!is_tag)
                        return 0;
 
                for_each_string_list_item(item, &patterns) {
-                       if (!wildmatch(item->string, path + 10, 0))
+                       if (!wildmatch(item->string, path + 10, 0)) {
+                               found = 1;
                                break;
+                       }
+               }
 
-                       /* If we get here, no pattern matched. */
+               if (!found)
                        return 0;
-               }
        }
 
        /* Is it annotated? */
index 10cbb434163f2f6e60e0cc9cb55bab220004a549..e99b5ddbf9a51be17bc0d976c942c29afe6563cd 100644 (file)
@@ -408,7 +408,8 @@ static void shortlog(const char *name,
 }
 
 static void fmt_merge_msg_title(struct strbuf *out,
-       const char *current_branch) {
+                               const char *current_branch)
+{
        int i = 0;
        char *sep = "";
 
index 52be99cbacdd84f60e5b9f2efd1f1d50f602cdb8..5d7c921a773718d6bee7f85b8d868fc22602377d 100644 (file)
@@ -17,25 +17,25 @@ static char const * const for_each_ref_usage[] = {
 int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 {
        int i;
-       const char *format = "%(objectname) %(objecttype)\t%(refname)";
        struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
-       int maxcount = 0, quote_style = 0, icase = 0;
+       int maxcount = 0, icase = 0;
        struct ref_array array;
        struct ref_filter filter;
+       struct ref_format format = REF_FORMAT_INIT;
 
        struct option opts[] = {
-               OPT_BIT('s', "shell", &quote_style,
+               OPT_BIT('s', "shell", &format.quote_style,
                        N_("quote placeholders suitably for shells"), QUOTE_SHELL),
-               OPT_BIT('p', "perl",  &quote_style,
+               OPT_BIT('p', "perl",  &format.quote_style,
                        N_("quote placeholders suitably for perl"), QUOTE_PERL),
-               OPT_BIT(0 , "python", &quote_style,
+               OPT_BIT(0 , "python", &format.quote_style,
                        N_("quote placeholders suitably for python"), QUOTE_PYTHON),
-               OPT_BIT(0 , "tcl",  &quote_style,
+               OPT_BIT(0 , "tcl",  &format.quote_style,
                        N_("quote placeholders suitably for Tcl"), QUOTE_TCL),
 
                OPT_GROUP(""),
                OPT_INTEGER( 0 , "count", &maxcount, N_("show only <n> matched refs")),
-               OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
+               OPT_STRING(  0 , "format", &format.format, N_("format"), N_("format to use for the output")),
                OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
                            N_("field name to sort on"), &parse_opt_ref_sorting),
                OPT_CALLBACK(0, "points-at", &filter.points_at,
@@ -52,16 +52,20 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        memset(&array, 0, sizeof(array));
        memset(&filter, 0, sizeof(filter));
 
+       format.format = "%(objectname) %(objecttype)\t%(refname)";
+
+       git_config(git_default_config, NULL);
+
        parse_options(argc, argv, prefix, opts, for_each_ref_usage, 0);
        if (maxcount < 0) {
                error("invalid --count argument: `%d'", maxcount);
                usage_with_options(for_each_ref_usage, opts);
        }
-       if (HAS_MULTI_BITS(quote_style)) {
+       if (HAS_MULTI_BITS(format.quote_style)) {
                error("more than one quoting style?");
                usage_with_options(for_each_ref_usage, opts);
        }
-       if (verify_ref_format(format))
+       if (verify_ref_format(&format))
                usage_with_options(for_each_ref_usage, opts);
 
        if (!sorting)
@@ -69,9 +73,6 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        sorting->ignore_case = icase;
        filter.ignore_case = icase;
 
-       /* for warn_ambiguous_refs */
-       git_config(git_default_config, NULL);
-
        filter.name_patterns = argv;
        filter.match_as_path = 1;
        filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
@@ -80,7 +81,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
        if (!maxcount || array.nr < maxcount)
                maxcount = array.nr;
        for (i = 0; i < maxcount; i++)
-               show_ref_array_item(array.items[i], format, quote_style);
+               show_ref_array_item(array.items[i], &format);
        ref_array_clear(&array);
        return 0;
 }
index 99dea7adf60a61906500b4aebc6cb7d566339b38..d18244ab546bae863e6ef81d58663dc5c27c49c0 100644 (file)
@@ -19,6 +19,8 @@
 #define REACHABLE 0x0001
 #define SEEN      0x0002
 #define HAS_OBJ   0x0004
+/* This flag is set if something points to this object. */
+#define USED      0x0008
 
 static int show_root;
 static int show_tags;
@@ -168,18 +170,7 @@ static void mark_object_reachable(struct object *obj)
 
 static int traverse_one_object(struct object *obj)
 {
-       int result;
-       struct tree *tree = NULL;
-
-       if (obj->type == OBJ_TREE) {
-               tree = (struct tree *)obj;
-               if (parse_tree(tree) < 0)
-                       return 1; /* error already displayed */
-       }
-       result = fsck_walk(obj, obj, &fsck_walk_options);
-       if (tree)
-               free_tree_buffer(tree);
-       return result;
+       return fsck_walk(obj, obj, &fsck_walk_options);
 }
 
 static int traverse_reachable(void)
@@ -206,7 +197,7 @@ static int mark_used(struct object *obj, int type, void *data, struct fsck_optio
 {
        if (!obj)
                return 1;
-       obj->used = 1;
+       obj->flags |= USED;
        return 0;
 }
 
@@ -255,7 +246,7 @@ static void check_unreachable_object(struct object *obj)
        }
 
        /*
-        * "!used" means that nothing at all points to it, including
+        * "!USED" means that nothing at all points to it, including
         * other unreachable objects. In other words, it's the "tip"
         * of some set of unreachable objects, usually a commit that
         * got dropped.
@@ -266,7 +257,7 @@ static void check_unreachable_object(struct object *obj)
         * deleted a branch by mistake, this is a prime candidate to
         * start looking at, for example.
         */
-       if (!obj->used) {
+       if (!(obj->flags & USED)) {
                if (show_dangling)
                        printf("dangling %s %s\n", printable_type(obj),
                               describe_object(obj));
@@ -335,6 +326,8 @@ static void check_connectivity(void)
 
 static int fsck_obj(struct object *obj)
 {
+       int err;
+
        if (obj->flags & SEEN)
                return 0;
        obj->flags |= SEEN;
@@ -345,20 +338,13 @@ static int fsck_obj(struct object *obj)
 
        if (fsck_walk(obj, NULL, &fsck_obj_options))
                objerror(obj, "broken links");
-       if (fsck_object(obj, NULL, 0, &fsck_obj_options))
-               return -1;
-
-       if (obj->type == OBJ_TREE) {
-               struct tree *item = (struct tree *) obj;
-
-               free_tree_buffer(item);
-       }
+       err = fsck_object(obj, NULL, 0, &fsck_obj_options);
+       if (err)
+               goto out;
 
        if (obj->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *) obj;
 
-               free_commit_buffer(commit);
-
                if (!commit->parents && show_root)
                        printf("root %s\n", describe_object(&commit->object));
        }
@@ -374,7 +360,12 @@ static int fsck_obj(struct object *obj)
                }
        }
 
-       return 0;
+out:
+       if (obj->type == OBJ_TREE)
+               free_tree_buffer((struct tree *)obj);
+       if (obj->type == OBJ_COMMIT)
+               free_commit_buffer((struct commit *)obj);
+       return err;
 }
 
 static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
@@ -390,7 +381,8 @@ static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
                errors_found |= ERROR_OBJECT;
                return error("%s: object corrupt or missing", oid_to_hex(oid));
        }
-       obj->flags = HAS_OBJ;
+       obj->flags &= ~(REACHABLE | SEEN);
+       obj->flags |= HAS_OBJ;
        return fsck_obj(obj);
 }
 
@@ -408,7 +400,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
                                add_decoration(fsck_walk_options.object_names,
                                        obj,
                                        xstrfmt("%s@{%"PRItime"}", refname, timestamp));
-                       obj->used = 1;
+                       obj->flags |= USED;
                        mark_object_reachable(obj);
                } else {
                        error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
@@ -456,7 +448,7 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
                errors_found |= ERROR_REFS;
        }
        default_refs++;
-       obj->used = 1;
+       obj->flags |= USED;
        if (name_objects)
                add_decoration(fsck_walk_options.object_names,
                        obj, xstrdup(refname));
@@ -524,7 +516,8 @@ static int fsck_loose(const struct object_id *oid, const char *path, void *data)
                return 0; /* keep checking other objects */
        }
 
-       obj->flags = HAS_OBJ;
+       obj->flags &= ~(REACHABLE | SEEN);
+       obj->flags |= HAS_OBJ;
        if (fsck_obj(obj))
                errors_found |= ERROR_OBJECT;
        return 0;
@@ -606,7 +599,7 @@ static int fsck_cache_tree(struct cache_tree *it)
                        errors_found |= ERROR_REFS;
                        return 1;
                }
-               obj->used = 1;
+               obj->flags |= USED;
                if (name_objects)
                        add_decoration(fsck_walk_options.object_names,
                                obj, xstrdup(":"));
@@ -667,7 +660,7 @@ static struct option fsck_opts[] = {
 
 int cmd_fsck(int argc, const char **argv, const char *prefix)
 {
-       int i, heads;
+       int i;
        struct alternate_object_database *alt;
 
        errors_found = 0;
@@ -735,7 +728,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                }
        }
 
-       heads = 0;
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                unsigned char sha1[20];
@@ -748,12 +740,11 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                                continue;
                        }
 
-                       obj->used = 1;
+                       obj->flags |= USED;
                        if (name_objects)
                                add_decoration(fsck_walk_options.object_names,
                                        obj, xstrdup(arg));
                        mark_object_reachable(obj);
-                       heads++;
                        continue;
                }
                error("invalid parameter: expected sha1, got '%s'", arg);
@@ -785,7 +776,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
                        if (!blob)
                                continue;
                        obj = &blob->object;
-                       obj->used = 1;
+                       obj->flags |= USED;
                        if (name_objects)
                                add_decoration(fsck_walk_options.object_names,
                                        obj,
index e6b84475aef6aeab05beda137fb2882f663fac0f..53c19be8b2c2e6b5495c879d7ab77a6123e3c1b6 100644 (file)
@@ -257,7 +257,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
                int should_exit;
 
                if (!scan_fmt)
-                       scan_fmt = xstrfmt("%s %%%dc", "%"SCNuMAX, HOST_NAME_MAX);
+                       scan_fmt = xstrfmt("%s %%%ds", "%"SCNuMAX, HOST_NAME_MAX);
                fp = fopen(pidfile_path, "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
index 7e79eb1a754a5f604829e3e9e6d98d83712be9b6..42ff87065a0e72c999488d2844463f901fd73a5c 100644 (file)
@@ -284,7 +284,7 @@ static int wait_all(void)
 static int grep_cmd_config(const char *var, const char *value, void *cb)
 {
        int st = grep_config(var, value, cb);
-       if (git_color_default_config(var, value, cb) < 0)
+       if (git_default_config(var, value, cb) < 0)
                st = -1;
 
        if (!strcmp(var, "grep.threads")) {
index c6362cf92ed799696128a5da6c802813db62fbcf..5ffc380bd71f0ab8b5bec06f864e9cdcffda1e2f 100644 (file)
@@ -372,11 +372,14 @@ static int cmd_log_walk(struct rev_info *rev)
                         */
                        rev->max_count++;
                if (!rev->reflog_info) {
-                       /* we allow cycles in reflog ancestry */
+                       /*
+                        * We may show a given commit multiple times when
+                        * walking the reflogs.
+                        */
                        free_commit_buffer(commit);
+                       free_commit_list(commit->parents);
+                       commit->parents = NULL;
                }
-               free_commit_list(commit->parents);
-               commit->parents = NULL;
                if (saved_nrl < rev->diffopt.needed_rename_limit)
                        saved_nrl = rev->diffopt.needed_rename_limit;
                if (rev->diffopt.degraded_cc_to_c)
index b8514a0029a1437687a5cf6b6f8bad9792340bc3..c6126eae550beb75e7c90094be337efbb48cd3bc 100644 (file)
@@ -362,7 +362,7 @@ static void prune_index(struct index_state *istate,
        int pos;
        unsigned int first, last;
 
-       if (!prefix)
+       if (!prefix || !istate->cache_nr)
                return;
        pos = index_name_pos(istate, prefix, prefixlen);
        if (pos < 0)
@@ -378,8 +378,7 @@ static void prune_index(struct index_state *istate,
                }
                last = next;
        }
-       memmove(istate->cache, istate->cache + pos,
-               (last - pos) * sizeof(struct cache_entry *));
+       MOVE_ARRAY(istate->cache, istate->cache + pos, last - pos);
        istate->cache_nr = last - pos;
 }
 
index 900bafdb45d0b28ab5497cfd251535266fc92be5..23c53a3082b26e8f89f8be1f14985acc491d9907 100644 (file)
@@ -537,7 +537,7 @@ static void parse_branch_merge_options(char *bmo)
                die(_("Bad branch.%s.mergeoptions string: %s"), branch,
                    split_cmdline_strerror(argc));
        REALLOC_ARRAY(argv, argc + 2);
-       memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
+       MOVE_ARRAY(argv + 1, argv, argc + 1);
        argc++;
        argv[0] = "branch.*.mergeoptions";
        parse_options(argc, argv, NULL, builtin_merge_options,
@@ -756,13 +756,17 @@ N_("Please enter a commit message to explain why this merge is necessary,\n"
    "Lines starting with '%c' will be ignored, and an empty message aborts\n"
    "the commit.\n");
 
+static void write_merge_heads(struct commit_list *);
 static void prepare_to_commit(struct commit_list *remoteheads)
 {
        struct strbuf msg = STRBUF_INIT;
        strbuf_addbuf(&msg, &merge_msg);
        strbuf_addch(&msg, '\n');
+       if (squash)
+               BUG("the control must not reach here under --squash");
        if (0 < option_edit)
                strbuf_commented_addf(&msg, _(merge_editor_comment), comment_line_char);
+       write_merge_heads(remoteheads);
        write_file_buf(git_path_merge_msg(), msg.buf, msg.len);
        if (run_commit_hook(0 < option_edit, get_index_file(), "prepare-commit-msg",
                            git_path_merge_msg(), "merge", NULL))
@@ -904,7 +908,7 @@ static int setup_with_upstream(const char ***argv)
        return i;
 }
 
-static void write_merge_state(struct commit_list *remoteheads)
+static void write_merge_heads(struct commit_list *remoteheads)
 {
        struct commit_list *j;
        struct strbuf buf = STRBUF_INIT;
@@ -920,8 +924,6 @@ static void write_merge_state(struct commit_list *remoteheads)
                strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
        }
        write_file_buf(git_path_merge_head(), buf.buf, buf.len);
-       strbuf_addch(&merge_msg, '\n');
-       write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
 
        strbuf_reset(&buf);
        if (fast_forward == FF_NO)
@@ -929,6 +931,13 @@ static void write_merge_state(struct commit_list *remoteheads)
        write_file_buf(git_path_merge_mode(), buf.buf, buf.len);
 }
 
+static void write_merge_state(struct commit_list *remoteheads)
+{
+       write_merge_heads(remoteheads);
+       strbuf_addch(&merge_msg, '\n');
+       write_file_buf(git_path_merge_msg(), merge_msg.buf, merge_msg.len);
+}
+
 static int default_edit_option(void)
 {
        static const char name[] = "GIT_MERGE_AUTOEDIT";
@@ -1117,8 +1126,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * current branch.
         */
        branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
-       if (branch && starts_with(branch, "refs/heads/"))
-               branch += 11;
+       if (branch)
+               skip_prefix(branch, "refs/heads/", &branch);
        if (!branch || is_null_oid(&head_oid))
                head_commit = NULL;
        else
index c41ea7c2a62ba868061884e511256c24f4d020d9..598da6c8bc75e9de50f85eac78d3051f27f96a6c 100644 (file)
@@ -253,7 +253,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
                struct commit *commit = (struct commit *)o;
                int from_tag = starts_with(path, "refs/tags/");
 
-               if (taggerdate == ULONG_MAX)
+               if (taggerdate == TIME_MAX)
                        taggerdate = ((struct commit *)o)->date;
                path = name_ref_abbrev(path, can_abbreviate_output);
                name_rev(commit, xstrdup(path), taggerdate, 0, 0,
index f4a8441fe913850e7a1cadf3376837237519e761..c753e9237a8d5981a17e872db33d5326bd8d7eab 100644 (file)
@@ -1289,7 +1289,7 @@ static int done_pbase_path_pos(unsigned hash)
 
 static int check_pbase_path(unsigned hash)
 {
-       int pos = (!done_pbase_paths) ? -1 : done_pbase_path_pos(hash);
+       int pos = done_pbase_path_pos(hash);
        if (0 <= pos)
                return 1;
        pos = -pos - 1;
@@ -1298,9 +1298,8 @@ static int check_pbase_path(unsigned hash)
                   done_pbase_paths_alloc);
        done_pbase_paths_num++;
        if (pos < done_pbase_paths_num)
-               memmove(done_pbase_paths + pos + 1,
-                       done_pbase_paths + pos,
-                       (done_pbase_paths_num - pos - 1) * sizeof(unsigned));
+               MOVE_ARRAY(done_pbase_paths + pos + 1, done_pbase_paths + pos,
+                          done_pbase_paths_num - pos - 1);
        done_pbase_paths[pos] = hash;
        return 0;
 }
index 95d84d5cda1bdb6a699bc74ad57f7f1910946440..95b4128250c850eb130fbdfea2407bad819a85b8 100644 (file)
@@ -11,6 +11,7 @@
 #include "graph.h"
 #include "bisect.h"
 #include "progress.h"
+#include "reflog-walk.h"
 
 static const char rev_list_usage[] =
 "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -122,6 +123,7 @@ static void show_commit(struct commit *commit, void *data)
                ctx.date_mode_explicit = revs->date_mode_explicit;
                ctx.fmt = revs->commit_format;
                ctx.output_encoding = get_log_output_encoding();
+               ctx.color = revs->diffopt.use_color;
                pretty_print_commit(&ctx, commit, &buf);
                if (buf.len) {
                        if (revs->commit_format != CMIT_FMT_ONELINE)
@@ -348,9 +350,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                /* Only --header was specified */
                revs.commit_format = CMIT_FMT_RAW;
 
-       if ((!revs.commits &&
+       if ((!revs.commits && reflog_walk_empty(revs.reflog_info) &&
             (!(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
-             !revs.pending.nr)) ||
+             !revs.pending.nr) &&
+            !revs.rev_input_given) ||
            revs.diff)
                usage(rev_list_usage);
 
index c78b7b33d6604bb38a16e30d64cfabee0fd67f40..7f965fe74e5e98496522eddb3ef87fa831265e17 100644 (file)
@@ -757,8 +757,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
-                               for_each_ref_in("refs/bisect/bad", show_reference, NULL);
-                               for_each_ref_in("refs/bisect/good", anti_reference, NULL);
+                               for_each_fullref_in("refs/bisect/bad", show_reference, NULL, 0);
+                               for_each_fullref_in("refs/bisect/good", anti_reference, NULL, 0);
                                continue;
                        }
                        if (opt_with_value(arg, "--branches", &arg)) {
index 16028b9ea82edee9cf41044c69a47e8994d78fc6..b9d927eb09c9ed87c84681df1396f4e6d9b13c97 100644 (file)
@@ -155,6 +155,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
                                "--strategy-option", opts->xopts ? 1 : 0,
                                "-x", opts->record_origin,
                                "--ff", opts->allow_ff,
+                               "--rerere-autoupdate", opts->allow_rerere_auto == RERERE_AUTOUPDATE,
+                               "--no-rerere-autoupdate", opts->allow_rerere_auto == RERERE_NOAUTOUPDATE,
                                NULL);
        }
 
index 7073a3eb9769cae1f00e714a93528b33ca651d9c..28f245c8cccbc20c96a189516e9df77d238c7e96 100644 (file)
@@ -554,7 +554,7 @@ static int git_show_branch_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       return git_color_default_config(var, value, cb);
+       return git_default_config(var, value, cb);
 }
 
 static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
index 6abdad3294ce84652a55522aff55f6e7673d192f..895555c93a3bb86ad5511a2ded2350614732d618 100644 (file)
@@ -930,7 +930,7 @@ static int update_clone_task_finished(int result,
        const struct cache_entry *ce;
        struct submodule_update_clone *suc = suc_cb;
 
-       int *idxP = *(int**)idx_task_cb;
+       int *idxP = idx_task_cb;
        int idx = *idxP;
        free(idxP);
 
@@ -1108,9 +1108,28 @@ static int resolve_remote_submodule_branch(int argc, const char **argv,
 static int push_check(int argc, const char **argv, const char *prefix)
 {
        struct remote *remote;
+       const char *superproject_head;
+       char *head;
+       int detached_head = 0;
+       struct object_id head_oid;
 
-       if (argc < 2)
-               die("submodule--helper push-check requires at least 1 argument");
+       if (argc < 3)
+               die("submodule--helper push-check requires at least 2 arguments");
+
+       /*
+        * superproject's resolved head ref.
+        * if HEAD then the superproject is in a detached head state, otherwise
+        * it will be the resolved head ref.
+        */
+       superproject_head = argv[1];
+       argv++;
+       argc--;
+       /* Get the submodule's head ref and determine if it is detached */
+       head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+       if (!head)
+               die(_("Failed to resolve HEAD as a valid ref."));
+       if (!strcmp(head, "HEAD"))
+               detached_head = 1;
 
        /*
         * The remote must be configured.
@@ -1133,18 +1152,30 @@ static int push_check(int argc, const char **argv, const char *prefix)
                        if (rs->pattern || rs->matching)
                                continue;
 
-                       /*
-                        * LHS must match a single ref
-                        * NEEDSWORK: add logic to special case 'HEAD' once
-                        * working with submodules in a detached head state
-                        * ceases to be the norm.
-                        */
-                       if (count_refspec_match(rs->src, local_refs, NULL) != 1)
+                       /* LHS must match a single ref */
+                       switch (count_refspec_match(rs->src, local_refs, NULL)) {
+                       case 1:
+                               break;
+                       case 0:
+                               /*
+                                * If LHS matches 'HEAD' then we need to ensure
+                                * that it matches the same named branch
+                                * checked out in the superproject.
+                                */
+                               if (!strcmp(rs->src, "HEAD")) {
+                                       if (!detached_head &&
+                                           !strcmp(head, superproject_head))
+                                               break;
+                                       die("HEAD does not match the named branch in the superproject");
+                               }
+                       default:
                                die("src refspec '%s' must name a ref",
                                    rs->src);
+                       }
                }
                free_refspec(refspec_nr, refspec);
        }
+       free(head);
 
        return 0;
 }
index 01154ea8dcca869ed635eb433d5afe879b8e883c..7a70d5a9bb5d456f032fa35dbef9a2fbf4c24124 100644 (file)
@@ -32,7 +32,8 @@ static const char * const git_tag_usage[] = {
 static unsigned int colopts;
 static int force_sign_annotate;
 
-static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
+                    struct ref_format *format)
 {
        struct ref_array array;
        char *to_free = NULL;
@@ -43,23 +44,24 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
        if (filter->lines == -1)
                filter->lines = 0;
 
-       if (!format) {
+       if (!format->format) {
                if (filter->lines) {
                        to_free = xstrfmt("%s %%(contents:lines=%d)",
                                          "%(align:15)%(refname:lstrip=2)%(end)",
                                          filter->lines);
-                       format = to_free;
+                       format->format = to_free;
                } else
-                       format = "%(refname:lstrip=2)";
+                       format->format = "%(refname:lstrip=2)";
        }
 
-       verify_ref_format(format);
+       if (verify_ref_format(format))
+               die(_("unable to parse format string"));
        filter->with_commit_tag_algo = 1;
        filter_refs(&array, filter, FILTER_REFS_TAGS);
        ref_array_sort(sorting, &array);
 
        for (i = 0; i < array.nr; i++)
-               show_ref_array_item(array.items[i], format, 0);
+               show_ref_array_item(array.items[i], format);
        ref_array_clear(&array);
        free(to_free);
 
@@ -105,17 +107,17 @@ static int verify_tag(const char *name, const char *ref,
                      const struct object_id *oid, const void *cb_data)
 {
        int flags;
-       const char *fmt_pretty = cb_data;
+       const struct ref_format *format = cb_data;
        flags = GPG_VERIFY_VERBOSE;
 
-       if (fmt_pretty)
+       if (format->format)
                flags = GPG_VERIFY_OMIT_STATUS;
 
        if (gpg_verify_tag(oid->hash, name, flags))
                return -1;
 
-       if (fmt_pretty)
-               pretty_print_ref(name, oid->hash, fmt_pretty);
+       if (format->format)
+               pretty_print_ref(name, oid->hash, format);
 
        return 0;
 }
@@ -134,30 +136,6 @@ static const char tag_template_nocleanup[] =
        "Lines starting with '%c' will be kept; you may remove them"
        " yourself if you want to.\n");
 
-/* Parse arg given and add it the ref_sorting array */
-static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
-{
-       struct ref_sorting *s;
-       int len;
-
-       s = xcalloc(1, sizeof(*s));
-       s->next = *sorting_tail;
-       *sorting_tail = s;
-
-       if (*arg == '-') {
-               s->reverse = 1;
-               arg++;
-       }
-       if (skip_prefix(arg, "version:", &arg) ||
-           skip_prefix(arg, "v:", &arg))
-               s->version = 1;
-
-       len = strlen(arg);
-       s->atom = parse_ref_filter_atom(arg, arg+len);
-
-       return 0;
-}
-
 static int git_tag_config(const char *var, const char *value, void *cb)
 {
        int status;
@@ -166,7 +144,7 @@ static int git_tag_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "tag.sort")) {
                if (!value)
                        return config_error_nonbool(var);
-               parse_sorting_string(value, sorting_tail);
+               parse_ref_sorting(sorting_tail, value);
                return 0;
        }
 
@@ -392,7 +370,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        struct strbuf err = STRBUF_INIT;
        struct ref_filter filter;
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
-       const char *format = NULL;
+       struct ref_format format = REF_FORMAT_INIT;
        int icase = 0;
        struct option options[] = {
                OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
@@ -431,7 +409,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
                        parse_opt_object_name, (intptr_t) "HEAD"
                },
-               OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
+               OPT_STRING(  0 , "format", &format.format, N_("format"),
+                          N_("format to use for the output")),
                OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
                OPT_END()
        };
@@ -461,6 +440,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        cmdmode = 'l';
        }
 
+       if (cmdmode == 'l')
+               setup_auto_pager("tag", 1);
+
        if ((create_tag_object || force) && (cmdmode != 0))
                usage_with_options(git_tag_usage, options);
 
@@ -483,7 +465,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        run_column_filter(colopts, &copts);
                }
                filter.name_patterns = argv;
-               ret = list_tags(&filter, sorting, format);
+               ret = list_tags(&filter, sorting, &format);
                if (column_active(colopts))
                        stop_column_filter();
                return ret;
@@ -501,9 +483,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (cmdmode == 'd')
                return for_each_tag_name(argv, delete_tag, NULL);
        if (cmdmode == 'v') {
-               if (format)
-                       verify_ref_format(format);
-               return for_each_tag_name(argv, verify_tag, format);
+               if (format.format && verify_ref_format(&format))
+                       usage_with_options(git_tag_usage, options);
+               return for_each_tag_name(argv, verify_tag, &format);
        }
 
        if (msg.given || msgfile) {
index f9a5f7535aad9c24041e0677dfa4ed4af87cabf0..87d73e856a13e4abaa67ee8cad3e520f1bd9f779 100644 (file)
@@ -32,11 +32,11 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
 {
        int i = 1, verbose = 0, had_error = 0;
        unsigned flags = 0;
-       char *fmt_pretty = NULL;
+       struct ref_format format = REF_FORMAT_INIT;
        const struct option verify_tag_options[] = {
                OPT__VERBOSE(&verbose, N_("print tag contents")),
                OPT_BIT(0, "raw", &flags, N_("print raw gpg status output"), GPG_VERIFY_RAW),
-               OPT_STRING(  0 , "format", &fmt_pretty, N_("format"), N_("format to use for the output")),
+               OPT_STRING(0, "format", &format.format, N_("format"), N_("format to use for the output")),
                OPT_END()
        };
 
@@ -50,8 +50,10 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
        if (verbose)
                flags |= GPG_VERIFY_VERBOSE;
 
-       if (fmt_pretty) {
-               verify_ref_format(fmt_pretty);
+       if (format.format) {
+               if (verify_ref_format(&format))
+                       usage_with_options(verify_tag_usage,
+                                          verify_tag_options);
                flags |= GPG_VERIFY_OMIT_STATUS;
        }
 
@@ -68,8 +70,8 @@ int cmd_verify_tag(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
-               if (fmt_pretty)
-                       pretty_print_ref(name, sha1, fmt_pretty);
+               if (format.format)
+                       pretty_print_ref(name, sha1, &format);
        }
        return had_error;
 }
index ec23d8c03d10bd3815f59c2d70ae5eb45170aaad..2440d1dc89175efaf74286da4b2c8f97fc52fbd9 100644 (file)
@@ -131,9 +131,8 @@ static int do_invalidate_path(struct cache_tree *it, const char *path)
                         * move 4 and 5 up one place (2 entries)
                         * 2 = 6 - 3 - 1 = subtree_nr - pos - 1
                         */
-                       memmove(it->down+pos, it->down+pos+1,
-                               sizeof(struct cache_tree_sub *) *
-                               (it->subtree_nr - pos - 1));
+                       MOVE_ARRAY(it->down + pos, it->down + pos + 1,
+                                  it->subtree_nr - pos - 1);
                        it->subtree_nr--;
                }
                return 1;
diff --git a/cache.h b/cache.h
index a09a5017fb940a72f7a9a08c6652563b8651f2a8..849bc0dcdd0d6bc572d3524f2646ea7f719eb7a9 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -939,14 +939,7 @@ extern const struct object_id null_oid;
 
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
-       int i;
-
-       for (i = 0; i < GIT_SHA1_RAWSZ; i++, sha1++, sha2++) {
-               if (*sha1 != *sha2)
-                       return *sha1 - *sha2;
-       }
-
-       return 0;
+       return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
 }
 
 static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
@@ -1500,6 +1493,7 @@ struct checkout {
        struct index_state *istate;
        const char *base_dir;
        int base_dir_len;
+       struct delayed_checkout *delayed_checkout;
        unsigned force:1,
                 quiet:1,
                 not_new:1,
@@ -1509,6 +1503,8 @@ struct checkout {
 
 #define TEMPORARY_FILENAME_LENGTH 25
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
+extern void enable_delayed_checkout(struct checkout *state);
+extern int finish_delayed_checkout(struct checkout *state);
 
 struct cache_def {
        struct strbuf path;
@@ -1549,7 +1545,6 @@ extern struct alternate_object_database {
        char path[FLEX_ARRAY];
 } *alt_odb_list;
 extern void prepare_alt_odb(void);
-extern void read_info_alternates(const char * relative_base, int depth);
 extern char *compute_alternate_path(const char *path, struct strbuf *err);
 typedef int alt_odb_fn(struct alternate_object_database *, void *);
 extern int foreach_alt_odb(alt_odb_fn, void*);
diff --git a/color.c b/color.c
index 31b6207a00de42a386e98c5656209ea7a010abe4..7aa8b076f045e5c0b74826153030e2923c0fc56e 100644 (file)
--- a/color.c
+++ b/color.c
@@ -361,14 +361,6 @@ int git_color_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
-int git_color_default_config(const char *var, const char *value, void *cb)
-{
-       if (git_color_config(var, value, cb) < 0)
-               return -1;
-
-       return git_default_config(var, value, cb);
-}
-
 void color_print_strbuf(FILE *fp, const char *color, const struct strbuf *sb)
 {
        if (*color)
index cbfd6899392e8715b206c96b3b58cbdaf29c77cd..d3150d627071316accafb7d9c69b5988050f4a8e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -223,9 +223,8 @@ int unregister_shallow(const struct object_id *oid)
        if (pos < 0)
                return -1;
        if (pos + 1 < commit_graft_nr)
-               memmove(commit_graft + pos, commit_graft + pos + 1,
-                               sizeof(struct commit_graft *)
-                               * (commit_graft_nr - pos - 1));
+               MOVE_ARRAY(commit_graft + pos, commit_graft + pos + 1,
+                          commit_graft_nr - pos - 1);
        commit_graft_nr--;
        return 0;
 }
index 4127c298cb1370a9ab56ca9d003e853b3e26b67e..f3805fec6b3a5853316d1476619adc5410224a7e 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -313,11 +313,6 @@ extern int interactive_add(int argc, const char **argv, const char *prefix, int
 extern int run_add_interactive(const char *revision, const char *patch_mode,
                               const struct pathspec *pathspec);
 
-static inline int single_parent(struct commit *commit)
-{
-       return commit->parents && !commit->parents->next;
-}
-
 struct commit_list *reduce_heads(struct commit_list *heads);
 
 struct commit_extra_header {
index d47c0035449b412ee074c9a94c0f5281211f5f7e..7d063e9e40a213ce378d90377a9ac2b1f04ddbe3 100644 (file)
@@ -162,19 +162,29 @@ static inline uint64_t git_bswap64(uint64_t x)
 
 #else
 
-#define get_be16(p)    ( \
-       (*((unsigned char *)(p) + 0) << 8) | \
-       (*((unsigned char *)(p) + 1) << 0) )
-#define get_be32(p)    ( \
-       (*((unsigned char *)(p) + 0) << 24) | \
-       (*((unsigned char *)(p) + 1) << 16) | \
-       (*((unsigned char *)(p) + 2) <<  8) | \
-       (*((unsigned char *)(p) + 3) <<  0) )
-#define put_be32(p, v) do { \
-       unsigned int __v = (v); \
-       *((unsigned char *)(p) + 0) = __v >> 24; \
-       *((unsigned char *)(p) + 1) = __v >> 16; \
-       *((unsigned char *)(p) + 2) = __v >>  8; \
-       *((unsigned char *)(p) + 3) = __v >>  0; } while (0)
+static inline uint16_t get_be16(const void *ptr)
+{
+       const unsigned char *p = ptr;
+       return  (uint16_t)p[0] << 8 |
+               (uint16_t)p[1] << 0;
+}
+
+static inline uint32_t get_be32(const void *ptr)
+{
+       const unsigned char *p = ptr;
+       return  (uint32_t)p[0] << 24 |
+               (uint32_t)p[1] << 16 |
+               (uint32_t)p[2] <<  8 |
+               (uint32_t)p[3] <<  0;
+}
+
+static inline void put_be32(void *ptr, uint32_t value)
+{
+       unsigned char *p = ptr;
+       p[0] = value >> 24;
+       p[1] = value >> 16;
+       p[2] = value >>  8;
+       p[3] = value >>  0;
+}
 
 #endif
index b10adc780fc334383f2684d21fa1c5a8d92771b9..ae03b74a6f4e9c8ff6d519d9a784ab6b84179872 100644 (file)
@@ -438,6 +438,10 @@ poll (struct pollfd *pfd, nfds_t nfd, int timeout)
            pfd[i].revents = happened;
            rc++;
          }
+       else
+         {
+           pfd[i].revents = 0;
+         }
       }
 
   return rc;
index 6c7c9b60538d932d6dbc2d32774a6f64f8687c69..161978d720aed9db5a00c77d1c6bd9a073544f15 100644 (file)
@@ -43,8 +43,10 @@ void syslog(int priority, const char *fmt, ...)
        va_end(ap);
 
        while ((pos = strstr(str, "%1")) != NULL) {
+               char *oldstr = str;
                str = realloc(str, st_add(++str_len, 1));
                if (!str) {
+                       free(oldstr);
                        warning_errno("realloc failed");
                        return;
                }
index 231f9a750b96deda8d62f5a5debb384f40481566..c138c0ba055bcec33f3a78a5a2227d2ce6cfa4e3 100644 (file)
--- a/config.c
+++ b/config.c
@@ -16,6 +16,7 @@
 #include "string-list.h"
 #include "utf8.h"
 #include "dir.h"
+#include "color.h"
 
 struct config_source {
        struct config_source *prev;
@@ -1350,6 +1351,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
        if (starts_with(var, "advice."))
                return git_default_advice_config(var, value);
 
+       if (git_color_config(var, value, dummy) < 0)
+               return -1;
+
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
                pager_use_color = git_config_bool(var,value);
                return 0;
@@ -2400,7 +2404,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
 {
        int fd = -1, in_fd = -1;
        int ret;
-       struct lock_file *lock = NULL;
+       static struct lock_file lock;
        char *filename_buf = NULL;
        char *contents = NULL;
        size_t contents_sz;
@@ -2419,8 +2423,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
         * The lock serves a purpose in addition to locking: the new
         * contents of .git/config will be written into it.
         */
-       lock = xcalloc(1, sizeof(struct lock_file));
-       fd = hold_lock_file_for_update(lock, config_filename, 0);
+       fd = hold_lock_file_for_update(&lock, config_filename, 0);
        if (fd < 0) {
                error_errno("could not lock config file %s", config_filename);
                free(store.key);
@@ -2533,8 +2536,8 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                close(in_fd);
                in_fd = -1;
 
-               if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
-                       error_errno("chmod on %s failed", get_lock_file_path(lock));
+               if (chmod(get_lock_file_path(&lock), st.st_mode & 07777) < 0) {
+                       error_errno("chmod on %s failed", get_lock_file_path(&lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
@@ -2589,28 +2592,19 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
                contents = NULL;
        }
 
-       if (commit_lock_file(lock) < 0) {
+       if (commit_lock_file(&lock) < 0) {
                error_errno("could not write config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
-               lock = NULL;
                goto out_free;
        }
 
-       /*
-        * lock is committed, so don't try to roll it back below.
-        * NOTE: Since lockfile.c keeps a linked list of all created
-        * lock_file structures, it isn't safe to free(lock).  It's
-        * better to just leave it hanging around.
-        */
-       lock = NULL;
        ret = 0;
 
        /* Invalidate the config cache */
        git_config_clear();
 
 out_free:
-       if (lock)
-               rollback_lock_file(lock);
+       rollback_lock_file(&lock);
        free(filename_buf);
        if (contents)
                munmap(contents, contents_sz);
@@ -2619,7 +2613,7 @@ int git_config_set_multivar_in_file_gently(const char *config_filename,
        return ret;
 
 write_err_out:
-       ret = write_error(get_lock_file_path(lock));
+       ret = write_error(get_lock_file_path(&lock));
        goto out_free;
 
 }
index 4ba98b7eaff268c6fc9f2f984449fd4d9e1bfd4b..c61d1ca8dc49f1da8dd70cf0c00ce97b1f18fcd2 100644 (file)
@@ -25,6 +25,23 @@ expression n;
 - memcpy(dst, src, n * sizeof(T));
 + COPY_ARRAY(dst, src, n);
 
+@@
+type T;
+T *dst;
+T *src;
+expression n;
+@@
+(
+- memmove(dst, src, (n) * sizeof(*dst));
++ MOVE_ARRAY(dst, src, n);
+|
+- memmove(dst, src, (n) * sizeof(*src));
++ MOVE_ARRAY(dst, src, n);
+|
+- memmove(dst, src, (n) * sizeof(T));
++ MOVE_ARRAY(dst, src, n);
+)
+
 @@
 type T;
 T *ptr;
index fbf5c582496eece56b29a91a2f3085716f4b0e9f..f2be7cc92437191219d16ddf1536883da23541c0 100644 (file)
@@ -17,4 +17,7 @@ shebang.perl: FORCE
 test: all
        $(MAKE) -C t
 
+clean:
+       $(RM) diff-highlight
+
 .PHONY: FORCE
index deaf0ba7b30ffa4e0003649985ef1594ee2ac049..387c1c5455be954d2be3d35f8be794bb5bd7f3e8 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -501,6 +501,7 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le
 
 #define CAP_CLEAN    (1u<<0)
 #define CAP_SMUDGE   (1u<<1)
+#define CAP_DELAY    (1u<<2)
 
 struct cmd2process {
        struct subprocess_entry subprocess; /* must be the first member! */
@@ -512,69 +513,49 @@ static struct hashmap subprocess_map;
 
 static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
 {
-       int err;
+       static int versions[] = {2, 0};
+       static struct subprocess_capability capabilities[] = {
+               { "clean",  CAP_CLEAN  },
+               { "smudge", CAP_SMUDGE },
+               { "delay",  CAP_DELAY  },
+               { NULL, 0 }
+       };
        struct cmd2process *entry = (struct cmd2process *)subprocess;
-       struct string_list cap_list = STRING_LIST_INIT_NODUP;
-       char *cap_buf;
-       const char *cap_name;
-       struct child_process *process = &subprocess->process;
-       const char *cmd = subprocess->cmd;
-
-       sigchain_push(SIGPIPE, SIG_IGN);
-
-       err = packet_writel(process->in, "git-filter-client", "version=2", NULL);
-       if (err)
-               goto done;
-
-       err = strcmp(packet_read_line(process->out, NULL), "git-filter-server");
-       if (err) {
-               error("external filter '%s' does not support filter protocol version 2", cmd);
-               goto done;
-       }
-       err = strcmp(packet_read_line(process->out, NULL), "version=2");
-       if (err)
-               goto done;
-       err = packet_read_line(process->out, NULL) != NULL;
-       if (err)
-               goto done;
-
-       err = packet_writel(process->in, "capability=clean", "capability=smudge", NULL);
-
-       for (;;) {
-               cap_buf = packet_read_line(process->out, NULL);
-               if (!cap_buf)
-                       break;
-               string_list_split_in_place(&cap_list, cap_buf, '=', 1);
-
-               if (cap_list.nr != 2 || strcmp(cap_list.items[0].string, "capability"))
-                       continue;
-
-               cap_name = cap_list.items[1].string;
-               if (!strcmp(cap_name, "clean")) {
-                       entry->supported_capabilities |= CAP_CLEAN;
-               } else if (!strcmp(cap_name, "smudge")) {
-                       entry->supported_capabilities |= CAP_SMUDGE;
-               } else {
-                       warning(
-                               "external filter '%s' requested unsupported filter capability '%s'",
-                               cmd, cap_name
-                       );
-               }
+       return subprocess_handshake(subprocess, "git-filter", versions, NULL,
+                                   capabilities,
+                                   &entry->supported_capabilities);
+}
 
-               string_list_clear(&cap_list, 0);
+static void handle_filter_error(const struct strbuf *filter_status,
+                               struct cmd2process *entry,
+                               const unsigned int wanted_capability) {
+       if (!strcmp(filter_status->buf, "error"))
+               ; /* The filter signaled a problem with the file. */
+       else if (!strcmp(filter_status->buf, "abort") && wanted_capability) {
+               /*
+                * The filter signaled a permanent problem. Don't try to filter
+                * files with the same command for the lifetime of the current
+                * Git process.
+                */
+                entry->supported_capabilities &= ~wanted_capability;
+       } else {
+               /*
+                * Something went wrong with the protocol filter.
+                * Force shutdown and restart if another blob requires filtering.
+                */
+               error("external filter '%s' failed", entry->subprocess.cmd);
+               subprocess_stop(&subprocess_map, &entry->subprocess);
+               free(entry);
        }
-
-done:
-       sigchain_pop(SIGPIPE);
-
-       return err;
 }
 
 static int apply_multi_file_filter(const char *path, const char *src, size_t len,
                                   int fd, struct strbuf *dst, const char *cmd,
-                                  const unsigned int wanted_capability)
+                                  const unsigned int wanted_capability,
+                                  struct delayed_checkout *dco)
 {
        int err;
+       int can_delay = 0;
        struct cmd2process *entry;
        struct child_process *process;
        struct strbuf nbuf = STRBUF_INIT;
@@ -603,12 +584,12 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
        }
        process = &entry->subprocess.process;
 
-       if (!(wanted_capability & entry->supported_capabilities))
+       if (!(entry->supported_capabilities & wanted_capability))
                return 0;
 
-       if (CAP_CLEAN & wanted_capability)
+       if (wanted_capability & CAP_CLEAN)
                filter_type = "clean";
-       else if (CAP_SMUDGE & wanted_capability)
+       else if (wanted_capability & CAP_SMUDGE)
                filter_type = "smudge";
        else
                die("unexpected filter type");
@@ -630,6 +611,14 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
        if (err)
                goto done;
 
+       if ((entry->supported_capabilities & CAP_DELAY) &&
+           dco && dco->state == CE_CAN_DELAY) {
+               can_delay = 1;
+               err = packet_write_fmt_gently(process->in, "can-delay=1\n");
+               if (err)
+                       goto done;
+       }
+
        err = packet_flush_gently(process->in);
        if (err)
                goto done;
@@ -645,14 +634,73 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
        if (err)
                goto done;
 
-       err = strcmp(filter_status.buf, "success");
+       if (can_delay && !strcmp(filter_status.buf, "delayed")) {
+               string_list_insert(&dco->filters, cmd);
+               string_list_insert(&dco->paths, path);
+       } else {
+               /* The filter got the blob and wants to send us a response. */
+               err = strcmp(filter_status.buf, "success");
+               if (err)
+                       goto done;
+
+               err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
+               if (err)
+                       goto done;
+
+               err = subprocess_read_status(process->out, &filter_status);
+               if (err)
+                       goto done;
+
+               err = strcmp(filter_status.buf, "success");
+       }
+
+done:
+       sigchain_pop(SIGPIPE);
+
+       if (err)
+               handle_filter_error(&filter_status, entry, wanted_capability);
+       else
+               strbuf_swap(dst, &nbuf);
+       strbuf_release(&nbuf);
+       return !err;
+}
+
+
+int async_query_available_blobs(const char *cmd, struct string_list *available_paths)
+{
+       int err;
+       char *line;
+       struct cmd2process *entry;
+       struct child_process *process;
+       struct strbuf filter_status = STRBUF_INIT;
+
+       assert(subprocess_map_initialized);
+       entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd);
+       if (!entry) {
+               error("external filter '%s' is not available anymore although "
+                     "not all paths have been filtered", cmd);
+               return 0;
+       }
+       process = &entry->subprocess.process;
+       sigchain_push(SIGPIPE, SIG_IGN);
+
+       err = packet_write_fmt_gently(
+               process->in, "command=list_available_blobs\n");
        if (err)
                goto done;
 
-       err = read_packetized_to_strbuf(process->out, &nbuf) < 0;
+       err = packet_flush_gently(process->in);
        if (err)
                goto done;
 
+       while ((line = packet_read_line(process->out, NULL))) {
+               const char *path;
+               if (skip_prefix(line, "pathname=", &path))
+                       string_list_insert(available_paths, xstrdup(path));
+               else
+                       ; /* ignore unknown keys */
+       }
+
        err = subprocess_read_status(process->out, &filter_status);
        if (err)
                goto done;
@@ -662,29 +710,8 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
 done:
        sigchain_pop(SIGPIPE);
 
-       if (err) {
-               if (!strcmp(filter_status.buf, "error")) {
-                       /* The filter signaled a problem with the file. */
-               } else if (!strcmp(filter_status.buf, "abort")) {
-                       /*
-                        * The filter signaled a permanent problem. Don't try to filter
-                        * files with the same command for the lifetime of the current
-                        * Git process.
-                        */
-                        entry->supported_capabilities &= ~wanted_capability;
-               } else {
-                       /*
-                        * Something went wrong with the protocol filter.
-                        * Force shutdown and restart if another blob requires filtering.
-                        */
-                       error("external filter '%s' failed", cmd);
-                       subprocess_stop(&subprocess_map, &entry->subprocess);
-                       free(entry);
-               }
-       } else {
-               strbuf_swap(dst, &nbuf);
-       }
-       strbuf_release(&nbuf);
+       if (err)
+               handle_filter_error(&filter_status, entry, 0);
        return !err;
 }
 
@@ -699,7 +726,8 @@ static struct convert_driver {
 
 static int apply_filter(const char *path, const char *src, size_t len,
                        int fd, struct strbuf *dst, struct convert_driver *drv,
-                       const unsigned int wanted_capability)
+                       const unsigned int wanted_capability,
+                       struct delayed_checkout *dco)
 {
        const char *cmd = NULL;
 
@@ -709,15 +737,16 @@ static int apply_filter(const char *path, const char *src, size_t len,
        if (!dst)
                return 1;
 
-       if ((CAP_CLEAN & wanted_capability) && !drv->process && drv->clean)
+       if ((wanted_capability & CAP_CLEAN) && !drv->process && drv->clean)
                cmd = drv->clean;
-       else if ((CAP_SMUDGE & wanted_capability) && !drv->process && drv->smudge)
+       else if ((wanted_capability & CAP_SMUDGE) && !drv->process && drv->smudge)
                cmd = drv->smudge;
 
        if (cmd && *cmd)
                return apply_single_file_filter(path, src, len, fd, dst, cmd);
        else if (drv->process && *drv->process)
-               return apply_multi_file_filter(path, src, len, fd, dst, drv->process, wanted_capability);
+               return apply_multi_file_filter(path, src, len, fd, dst,
+                       drv->process, wanted_capability, dco);
 
        return 0;
 }
@@ -1058,7 +1087,7 @@ int would_convert_to_git_filter_fd(const char *path)
        if (!ca.drv->required)
                return 0;
 
-       return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN);
+       return apply_filter(path, NULL, 0, -1, NULL, ca.drv, CAP_CLEAN, NULL);
 }
 
 const char *get_convert_attr_ascii(const char *path)
@@ -1096,7 +1125,7 @@ int convert_to_git(const struct index_state *istate,
 
        convert_attrs(&ca, path);
 
-       ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN);
+       ret |= apply_filter(path, src, len, -1, dst, ca.drv, CAP_CLEAN, NULL);
        if (!ret && ca.drv && ca.drv->required)
                die("%s: clean filter '%s' failed", path, ca.drv->name);
 
@@ -1104,10 +1133,12 @@ int convert_to_git(const struct index_state *istate,
                src = dst->buf;
                len = dst->len;
        }
-       ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
-       if (ret && dst) {
-               src = dst->buf;
-               len = dst->len;
+       if (checksafe != SAFE_CRLF_KEEP_CRLF) {
+               ret |= crlf_to_git(istate, path, src, len, dst, ca.crlf_action, checksafe);
+               if (ret && dst) {
+                       src = dst->buf;
+                       len = dst->len;
+               }
        }
        return ret | ident_to_git(path, src, len, dst, ca.ident);
 }
@@ -1122,7 +1153,7 @@ void convert_to_git_filter_fd(const struct index_state *istate,
        assert(ca.drv);
        assert(ca.drv->clean || ca.drv->process);
 
-       if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN))
+       if (!apply_filter(path, NULL, 0, fd, dst, ca.drv, CAP_CLEAN, NULL))
                die("%s: clean filter '%s' failed", path, ca.drv->name);
 
        crlf_to_git(istate, path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
@@ -1131,7 +1162,7 @@ void convert_to_git_filter_fd(const struct index_state *istate,
 
 static int convert_to_working_tree_internal(const char *path, const char *src,
                                            size_t len, struct strbuf *dst,
-                                           int normalizing)
+                                           int normalizing, struct delayed_checkout *dco)
 {
        int ret = 0, ret_filter = 0;
        struct conv_attrs ca;
@@ -1156,22 +1187,30 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
                }
        }
 
-       ret_filter = apply_filter(path, src, len, -1, dst, ca.drv, CAP_SMUDGE);
+       ret_filter = apply_filter(
+               path, src, len, -1, dst, ca.drv, CAP_SMUDGE, dco);
        if (!ret_filter && ca.drv && ca.drv->required)
                die("%s: smudge filter %s failed", path, ca.drv->name);
 
        return ret | ret_filter;
 }
 
+int async_convert_to_working_tree(const char *path, const char *src,
+                                 size_t len, struct strbuf *dst,
+                                 void *dco)
+{
+       return convert_to_working_tree_internal(path, src, len, dst, 0, dco);
+}
+
 int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
 {
-       return convert_to_working_tree_internal(path, src, len, dst, 0);
+       return convert_to_working_tree_internal(path, src, len, dst, 0, NULL);
 }
 
 int renormalize_buffer(const struct index_state *istate, const char *path,
                       const char *src, size_t len, struct strbuf *dst)
 {
-       int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
+       int ret = convert_to_working_tree_internal(path, src, len, dst, 1, NULL);
        if (ret) {
                src = dst->buf;
                len = dst->len;
index cecf59d1aa15f234473dc16520c0a6647e255280..4f2da225a8926f92e465c7dea27cdc4589864e1f 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -4,13 +4,16 @@
 #ifndef CONVERT_H
 #define CONVERT_H
 
+#include "string-list.h"
+
 struct index_state;
 
 enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
        SAFE_CRLF_FAIL = 1,
        SAFE_CRLF_WARN = 2,
-       SAFE_CRLF_RENORMALIZE = 3
+       SAFE_CRLF_RENORMALIZE = 3,
+       SAFE_CRLF_KEEP_CRLF = 4
 };
 
 extern enum safe_crlf safe_crlf;
@@ -34,6 +37,26 @@ enum eol {
 #endif
 };
 
+enum ce_delay_state {
+       CE_NO_DELAY = 0,
+       CE_CAN_DELAY = 1,
+       CE_RETRY = 2
+};
+
+struct delayed_checkout {
+       /*
+        * State of the currently processed cache entry. If the state is
+        * CE_CAN_DELAY, then the filter can delay the current cache entry.
+        * If the state is CE_RETRY, then this signals the filter that the
+        * cache entry was requested before.
+        */
+       enum ce_delay_state state;
+       /* List of filter drivers that signaled delayed blobs. */
+       struct string_list filters;
+       /* List of delayed blobs identified by their path. */
+       struct string_list paths;
+};
+
 extern enum eol core_eol;
 extern const char *get_cached_convert_stats_ascii(const struct index_state *istate,
                                                  const char *path);
@@ -46,6 +69,10 @@ extern int convert_to_git(const struct index_state *istate,
                          struct strbuf *dst, enum safe_crlf checksafe);
 extern int convert_to_working_tree(const char *path, const char *src,
                                   size_t len, struct strbuf *dst);
+extern int async_convert_to_working_tree(const char *path, const char *src,
+                                        size_t len, struct strbuf *dst,
+                                        void *dco);
+extern int async_query_available_blobs(const char *cmd, struct string_list *available_paths);
 extern int renormalize_buffer(const struct index_state *istate,
                              const char *path, const char *src, size_t len,
                              struct strbuf *dst);
index 91550bfb0b3325ad92a3f3c65d5f779b61fa9951..1cccc3a0b9cfb2a14e9c87d5afc29234fa62adc4 100644 (file)
@@ -25,7 +25,7 @@ static int send_request(const char *socket, const struct strbuf *out)
                int r;
 
                r = read_in_full(fd, in, sizeof(in));
-               if (r == 0)
+               if (r == 0 || (r < 0 && errno == ECONNRESET))
                        break;
                if (r < 0)
                        die_errno("read error from cache daemon");
diff --git a/diff.c b/diff.c
index 85e714f6c68d24e11228b69d2511c49811c979b4..9c382580306e340ed6333f96bc4919c4c507a7b9 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -299,9 +299,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (git_color_config(var, value, cb) < 0)
-               return -1;
-
        return git_diff_basic_config(var, value, cb);
 }
 
diff --git a/dir.c b/dir.c
index ae6f5c9636abd34d026579b7ddc8452825af9b30..1c55dc3e366f8c9e8bcbc62305d7085533059512 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -2398,7 +2398,8 @@ struct ondisk_untracked_cache {
        char exclude_per_dir[FLEX_ARRAY];
 };
 
-#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, exclude_per_dir) + len + 1)
+#define ouc_offset(x) offsetof(struct ondisk_untracked_cache, x)
+#define ouc_size(len) (ouc_offset(exclude_per_dir) + len + 1)
 
 struct write_data {
        int index;         /* number of written untracked_cache_dir */
@@ -2560,17 +2561,18 @@ struct read_data {
        const unsigned char *end;
 };
 
-static void stat_data_from_disk(struct stat_data *to, const struct stat_data *from)
+static void stat_data_from_disk(struct stat_data *to, const unsigned char *data)
 {
-       to->sd_ctime.sec  = get_be32(&from->sd_ctime.sec);
-       to->sd_ctime.nsec = get_be32(&from->sd_ctime.nsec);
-       to->sd_mtime.sec  = get_be32(&from->sd_mtime.sec);
-       to->sd_mtime.nsec = get_be32(&from->sd_mtime.nsec);
-       to->sd_dev        = get_be32(&from->sd_dev);
-       to->sd_ino        = get_be32(&from->sd_ino);
-       to->sd_uid        = get_be32(&from->sd_uid);
-       to->sd_gid        = get_be32(&from->sd_gid);
-       to->sd_size       = get_be32(&from->sd_size);
+       memcpy(to, data, sizeof(*to));
+       to->sd_ctime.sec  = ntohl(to->sd_ctime.sec);
+       to->sd_ctime.nsec = ntohl(to->sd_ctime.nsec);
+       to->sd_mtime.sec  = ntohl(to->sd_mtime.sec);
+       to->sd_mtime.nsec = ntohl(to->sd_mtime.nsec);
+       to->sd_dev        = ntohl(to->sd_dev);
+       to->sd_ino        = ntohl(to->sd_ino);
+       to->sd_uid        = ntohl(to->sd_uid);
+       to->sd_gid        = ntohl(to->sd_gid);
+       to->sd_size       = ntohl(to->sd_size);
 }
 
 static int read_one_dir(struct untracked_cache_dir **untracked_,
@@ -2645,7 +2647,7 @@ static void read_stat(size_t pos, void *cb)
                rd->data = rd->end + 1;
                return;
        }
-       stat_data_from_disk(&ud->stat_data, (struct stat_data *)rd->data);
+       stat_data_from_disk(&ud->stat_data, rd->data);
        rd->data += sizeof(struct stat_data);
        ud->valid = 1;
 }
@@ -2663,22 +2665,22 @@ static void read_sha1(size_t pos, void *cb)
 }
 
 static void load_sha1_stat(struct sha1_stat *sha1_stat,
-                          const struct stat_data *stat,
+                          const unsigned char *data,
                           const unsigned char *sha1)
 {
-       stat_data_from_disk(&sha1_stat->stat, stat);
+       stat_data_from_disk(&sha1_stat->stat, data);
        hashcpy(sha1_stat->sha1, sha1);
        sha1_stat->valid = 1;
 }
 
 struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
 {
-       const struct ondisk_untracked_cache *ouc;
        struct untracked_cache *uc;
        struct read_data rd;
        const unsigned char *next = data, *end = (const unsigned char *)data + sz;
        const char *ident;
        int ident_len, len;
+       const char *exclude_per_dir;
 
        if (sz <= 1 || end[-1] != '\0')
                return NULL;
@@ -2690,21 +2692,23 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
        ident = (const char *)next;
        next += ident_len;
 
-       ouc = (const struct ondisk_untracked_cache *)next;
        if (next + ouc_size(0) > end)
                return NULL;
 
        uc = xcalloc(1, sizeof(*uc));
        strbuf_init(&uc->ident, ident_len);
        strbuf_add(&uc->ident, ident, ident_len);
-       load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat,
-                      ouc->info_exclude_sha1);
-       load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat,
-                      ouc->excludes_file_sha1);
-       uc->dir_flags = get_be32(&ouc->dir_flags);
-       uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir);
+       load_sha1_stat(&uc->ss_info_exclude,
+                      next + ouc_offset(info_exclude_stat),
+                      next + ouc_offset(info_exclude_sha1));
+       load_sha1_stat(&uc->ss_excludes_file,
+                      next + ouc_offset(excludes_file_stat),
+                      next + ouc_offset(excludes_file_sha1));
+       uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
+       exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
+       uc->exclude_per_dir = xstrdup(exclude_per_dir);
        /* NUL after exclude_per_dir is covered by sizeof(*ouc) */
-       next += ouc_size(strlen(ouc->exclude_per_dir));
+       next += ouc_size(strlen(exclude_per_dir));
        if (next >= end)
                goto done2;
 
diff --git a/entry.c b/entry.c
index d6b263f78e0d2dfef997d94905412b0543a8b8c7..65458f07a4453fa29731fe2b9c20848d6b192b20 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -137,6 +137,105 @@ static int streaming_write_entry(const struct cache_entry *ce, char *path,
        return result;
 }
 
+void enable_delayed_checkout(struct checkout *state)
+{
+       if (!state->delayed_checkout) {
+               state->delayed_checkout = xmalloc(sizeof(*state->delayed_checkout));
+               state->delayed_checkout->state = CE_CAN_DELAY;
+               string_list_init(&state->delayed_checkout->filters, 0);
+               string_list_init(&state->delayed_checkout->paths, 0);
+       }
+}
+
+static int remove_available_paths(struct string_list_item *item, void *cb_data)
+{
+       struct string_list *available_paths = cb_data;
+       struct string_list_item *available;
+
+       available = string_list_lookup(available_paths, item->string);
+       if (available)
+               available->util = (void *)item->string;
+       return !available;
+}
+
+int finish_delayed_checkout(struct checkout *state)
+{
+       int errs = 0;
+       struct string_list_item *filter, *path;
+       struct delayed_checkout *dco = state->delayed_checkout;
+
+       if (!state->delayed_checkout)
+               return errs;
+
+       dco->state = CE_RETRY;
+       while (dco->filters.nr > 0) {
+               for_each_string_list_item(filter, &dco->filters) {
+                       struct string_list available_paths = STRING_LIST_INIT_NODUP;
+
+                       if (!async_query_available_blobs(filter->string, &available_paths)) {
+                               /* Filter reported an error */
+                               errs = 1;
+                               filter->string = "";
+                               continue;
+                       }
+                       if (available_paths.nr <= 0) {
+                               /*
+                                * Filter responded with no entries. That means
+                                * the filter is done and we can remove the
+                                * filter from the list (see
+                                * "string_list_remove_empty_items" call below).
+                                */
+                               filter->string = "";
+                               continue;
+                       }
+
+                       /*
+                        * In dco->paths we store a list of all delayed paths.
+                        * The filter just send us a list of available paths.
+                        * Remove them from the list.
+                        */
+                       filter_string_list(&dco->paths, 0,
+                               &remove_available_paths, &available_paths);
+
+                       for_each_string_list_item(path, &available_paths) {
+                               struct cache_entry* ce;
+
+                               if (!path->util) {
+                                       error("external filter '%s' signaled that '%s' "
+                                             "is now available although it has not been "
+                                             "delayed earlier",
+                                             filter->string, path->string);
+                                       errs |= 1;
+
+                                       /*
+                                        * Do not ask the filter for available blobs,
+                                        * again, as the filter is likely buggy.
+                                        */
+                                       filter->string = "";
+                                       continue;
+                               }
+                               ce = index_file_exists(state->istate, path->string,
+                                                      strlen(path->string), 0);
+                               errs |= (ce ? checkout_entry(ce, state, NULL) : 1);
+                       }
+               }
+               string_list_remove_empty_items(&dco->filters, 0);
+       }
+       string_list_clear(&dco->filters, 0);
+
+       /* At this point we should not have any delayed paths anymore. */
+       errs |= dco->paths.nr;
+       for_each_string_list_item(path, &dco->paths) {
+               error("'%s' was not filtered properly", path->string);
+       }
+       string_list_clear(&dco->paths, 0);
+
+       free(dco);
+       state->delayed_checkout = NULL;
+
+       return errs;
+}
+
 static int write_entry(struct cache_entry *ce,
                       char *path, const struct checkout *state, int to_tempfile)
 {
@@ -179,11 +278,34 @@ static int write_entry(struct cache_entry *ce,
                /*
                 * Convert from git internal format to working tree format
                 */
-               if (ce_mode_s_ifmt == S_IFREG &&
-                   convert_to_working_tree(ce->name, new, size, &buf)) {
-                       free(new);
-                       new = strbuf_detach(&buf, &newsize);
-                       size = newsize;
+               if (ce_mode_s_ifmt == S_IFREG) {
+                       struct delayed_checkout *dco = state->delayed_checkout;
+                       if (dco && dco->state != CE_NO_DELAY) {
+                               /* Do not send the blob in case of a retry. */
+                               if (dco->state == CE_RETRY) {
+                                       new = NULL;
+                                       size = 0;
+                               }
+                               ret = async_convert_to_working_tree(
+                                       ce->name, new, size, &buf, dco);
+                               if (ret && string_list_has_string(&dco->paths, ce->name)) {
+                                       free(new);
+                                       goto finish;
+                               }
+                       } else
+                               ret = convert_to_working_tree(
+                                       ce->name, new, size, &buf);
+
+                       if (ret) {
+                               free(new);
+                               new = strbuf_detach(&buf, &newsize);
+                               size = newsize;
+                       }
+                       /*
+                        * No "else" here as errors from convert are OK at this
+                        * point. If the error would have been fatal (e.g.
+                        * filter is required), then we would have died already.
+                        */
                }
 
                fd = open_output_fd(path, ce, to_tempfile);
index fb94aeba9cef2893ea7173e5634640e596659474..ce192a2d64c846e9d7ccffe10c9476f9c5abb8b8 100644 (file)
@@ -5,21 +5,14 @@
 #define MAX_ARGS       32
 
 static const char *argv_exec_path;
+
+#ifdef RUNTIME_PREFIX
 static const char *argv0_path;
 
-char *system_path(const char *path)
+static const char *system_prefix(void)
 {
-#ifdef RUNTIME_PREFIX
        static const char *prefix;
-#else
-       static const char *prefix = PREFIX;
-#endif
-       struct strbuf d = STRBUF_INIT;
-
-       if (is_absolute_path(path))
-               return xstrdup(path);
 
-#ifdef RUNTIME_PREFIX
        assert(argv0_path);
        assert(is_absolute_path(argv0_path));
 
@@ -32,10 +25,7 @@ char *system_path(const char *path)
                                "but prefix computation failed.  "
                                "Using static fallback '%s'.\n", prefix);
        }
-#endif
-
-       strbuf_addf(&d, "%s/%s", prefix, path);
-       return strbuf_detach(&d, NULL);
+       return prefix;
 }
 
 void git_extract_argv0_path(const char *argv0)
@@ -51,6 +41,30 @@ void git_extract_argv0_path(const char *argv0)
                argv0_path = xstrndup(argv0, slash - argv0);
 }
 
+#else
+
+static const char *system_prefix(void)
+{
+       return PREFIX;
+}
+
+void git_extract_argv0_path(const char *argv0)
+{
+}
+
+#endif /* RUNTIME_PREFIX */
+
+char *system_path(const char *path)
+{
+       struct strbuf d = STRBUF_INIT;
+
+       if (is_absolute_path(path))
+               return xstrdup(path);
+
+       strbuf_addf(&d, "%s/%s", system_prefix(), path);
+       return strbuf_detach(&d, NULL);
+}
+
 void git_set_argv_exec_path(const char *exec_path)
 {
        argv_exec_path = exec_path;
index 9cb123a07df88c975cf1b0d6c5832cb410175dd0..b7c173c345544d61a887cfbebb58703478a29d2d 100755 (executable)
@@ -983,7 +983,7 @@ sub find_parents {
        # check that we actually know about the branch
        next unless -e "$git_dir/refs/heads/$branch";
 
-       my $mergebase = `git-merge-base $branch $ps->{branch}`;
+       my $mergebase = safe_pipe_capture(qw(git-merge-base), $branch, $ps->{branch});
        if ($?) {
            # Don't die here, Arch supports one-way cherry-picking
            # between branches with no common base (or any relationship
@@ -1074,7 +1074,7 @@ sub find_parents {
 
 sub git_rev_parse {
     my $name = shift;
-    my $val  = `git-rev-parse $name`;
+    my $val  = safe_pipe_capture(qw(git-rev-parse), $name);
     die "Error: git-rev-parse $name" if $?;
     chomp $val;
     return $val;
index db9c22de7693af4e905fdeb0a1766ba4469eda94..7d2c0ca75922ea1ca8c67ec798ff8ce1249c925f 100644 (file)
@@ -828,6 +828,14 @@ static inline void copy_array(void *dst, const void *src, size_t n, size_t size)
                memcpy(dst, src, st_mult(size, n));
 }
 
+#define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \
+       BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
+static inline void move_array(void *dst, const void *src, size_t n, size_t size)
+{
+       if (n)
+               memmove(dst, src, st_mult(size, n));
+}
+
 /*
  * These functions help you allocate structs with flex arrays, and copy
  * the data directly into the array. For example, if you had:
index 1e4e65a45d16cfcc5a36e6855216e9be8fe0e0b4..36929921ea79006dc659f6996ae158131ccf2b73 100755 (executable)
@@ -642,6 +642,7 @@ sub is_sha1 {
 
 sub get_headref ($) {
        my $name = shift;
+       $name =~ s/'/'\\''/;
        my $r = `git rev-parse --verify '$name' 2>/dev/null`;
        return undef unless $? == 0;
        chomp $r;
index d50c85ed7ba719a0dc04f7ca96cf05216104599d..ae1044273d01125af96a06f1d68cbbd3d0e25c41 100755 (executable)
@@ -356,7 +356,7 @@ sub req_Root
        return 0;
     }
 
-    my @gitvars = `git config -l`;
+    my @gitvars = safe_pipe_capture(qw(git config -l));
     if ($?) {
        print "E problems executing git-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
         print "E \n";
@@ -841,7 +841,7 @@ sub req_Modified
     # Save the file data in $state
     $state->{entries}{$state->{directory}.$data}{modified_filename} = $filename;
     $state->{entries}{$state->{directory}.$data}{modified_mode} = $mode;
-    $state->{entries}{$state->{directory}.$data}{modified_hash} = `git hash-object $filename`;
+    $state->{entries}{$state->{directory}.$data}{modified_hash} = safe_pipe_capture('git','hash-object',$filename);
     $state->{entries}{$state->{directory}.$data}{modified_hash} =~ s/\s.*$//s;
 
     #$log->debug("req_Modified : file=$data mode=$mode size=$size");
@@ -943,7 +943,7 @@ sub req_co
 
     # Provide list of modules, if -c was used.
     if (exists $state->{opt}{c}) {
-        my $showref = `git show-ref --heads`;
+        my $showref = safe_pipe_capture(qw(git show-ref --heads));
         for my $line (split '\n', $showref) {
             if ( $line =~ m% refs/heads/(.*)$% ) {
                 print "M $1\t$1\n";
@@ -1181,7 +1181,7 @@ sub req_update
     # projects (heads in this case) to checkout.
     #
     if ($state->{module} eq '') {
-        my $showref = `git show-ref --heads`;
+        my $showref = safe_pipe_capture(qw(git show-ref --heads));
         print "E cvs update: Updating .\n";
         for my $line (split '\n', $showref) {
             if ( $line =~ m% refs/heads/(.*)$% ) {
@@ -1463,7 +1463,7 @@ sub req_update
                 # transmit file, format is single integer on a line by itself (file
                 # size) followed by the file contents
                 # TODO : we should copy files in blocks
-                my $data = `cat $mergedFile`;
+                my $data = safe_pipe_capture('cat', $mergedFile);
                 $log->debug("File size : " . length($data));
                 print length($data) . "\n";
                 print $data;
@@ -1579,7 +1579,7 @@ sub req_ci
                 $branchRef = "refs/heads/$stickyInfo->{tag}";
             }
 
-            $parenthash = `git show-ref -s $branchRef`;
+            $parenthash = safe_pipe_capture('git', 'show-ref', '-s', $branchRef);
             chomp $parenthash;
             if ($parenthash !~ /^[0-9a-f]{40}$/)
             {
@@ -1687,7 +1687,7 @@ sub req_ci
         return;
     }
 
-    my $treehash = `git write-tree`;
+    my $treehash = safe_pipe_capture(qw(git write-tree));
     chomp $treehash;
 
     $log->debug("Treehash : $treehash, Parenthash : $parenthash");
@@ -1704,7 +1704,7 @@ sub req_ci
     }
     close $msg_fh;
 
-    my $commithash = `git commit-tree $treehash -p $parenthash < $msg_filename`;
+    my $commithash = safe_pipe_capture('git', 'commit-tree', $treehash, '-p', $parenthash, '-F', $msg_filename);
     chomp($commithash);
     $log->info("Commit hash : $commithash");
 
@@ -2854,12 +2854,12 @@ sub transmitfile
 
     die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{40}$/ );
 
-    my $type = `git cat-file -t $filehash`;
+    my $type = safe_pipe_capture('git', 'cat-file', '-t', $filehash);
     chomp $type;
 
     die ( "Invalid type '$type' (expected 'blob')" ) unless ( defined ( $type ) and $type eq "blob" );
 
-    my $size = `git cat-file -s $filehash`;
+    my $size = safe_pipe_capture('git', 'cat-file', '-s', $filehash);
     chomp $size;
 
     $log->debug("transmitfile($filehash) size=$size, type=$type");
@@ -3040,7 +3040,7 @@ sub ensureWorkTree
     chdir $work->{emptyDir} or
         die "Unable to chdir to $work->{emptyDir}\n";
 
-    my $ver = `git show-ref -s refs/heads/$state->{module}`;
+    my $ver = safe_pipe_capture('git', 'show-ref', '-s', "refs/heads/$state->{module}");
     chomp $ver;
     if ($ver !~ /^[0-9a-f]{40}$/)
     {
@@ -3287,7 +3287,7 @@ sub open_blob_or_die
             die "Need filehash\n";
         }
 
-        my $type = `git cat-file -t $name`;
+        my $type = safe_pipe_capture('git', 'cat-file', '-t', $name);
         chomp $type;
 
         unless ( defined ( $type ) and $type eq "blob" )
@@ -3296,7 +3296,7 @@ sub open_blob_or_die
             die ( "Invalid type '$type' (expected 'blob')" )
         }
 
-        my $size = `git cat-file -s $name`;
+        my $size = safe_pipe_capture('git', 'cat-file', '-s', $name);
         chomp $size;
 
         $log->debug("open_blob_or_die($name) size=$size, type=$type");
@@ -3406,6 +3406,22 @@ sub refHashEqual
     return $out;
 }
 
+# an alternative to `command` that allows input to be passed as an array
+# to work around shell problems with weird characters in arguments
+
+sub safe_pipe_capture {
+
+    my @output;
+
+    if (my $pid = open my $child, '-|') {
+        @output = (<$child>);
+        close $child or die join(' ',@_).": $! $?";
+    } else {
+        exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
+    }
+    return wantarray ? @output : join('',@output);
+}
+
 
 package GITCVS::log;
 
@@ -3797,10 +3813,10 @@ sub update
     # first lets get the commit list
     $ENV{GIT_DIR} = $self->{git_path};
 
-    my $commitsha1 = `git rev-parse $self->{module}`;
+    my $commitsha1 = ::safe_pipe_capture('git', 'rev-parse', $self->{module});
     chomp $commitsha1;
 
-    my $commitinfo = `git cat-file commit $self->{module} 2>&1`;
+    my $commitinfo = ::safe_pipe_capture('git', 'cat-file', 'commit', $self->{module});
     unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
     {
         die("Invalid module '$self->{module}'");
@@ -3882,7 +3898,7 @@ sub update
                     # several candidate merge bases. let's assume
                     # that the first one is the best one.
                    my $base = eval {
-                           safe_pipe_capture('git', 'merge-base',
+                           ::safe_pipe_capture('git', 'merge-base',
                                                 $lastpicked, $parent);
                    };
                    # The two branches may not be related at all,
@@ -4749,7 +4765,7 @@ sub getMetaFromCommithash
         return $retVal;
     }
 
-    my($fileHash)=safe_pipe_capture("git","rev-parse","$revCommit:$filename");
+    my($fileHash) = ::safe_pipe_capture("git","rev-parse","$revCommit:$filename");
     chomp $fileHash;
     if(!($fileHash=~/^[0-9a-f]{40}$/))
     {
@@ -4844,8 +4860,8 @@ sub lookupCommitRef
         return $commitHash;
     }
 
-    $commitHash=safe_pipe_capture("git","rev-parse","--verify","--quiet",
-                                  $self->unescapeRefName($ref));
+    $commitHash = ::safe_pipe_capture("git","rev-parse","--verify","--quiet",
+                                     $self->unescapeRefName($ref));
     $commitHash=~s/\s*$//;
     if(!($commitHash=~/^[0-9a-f]{40}$/))
     {
@@ -4854,7 +4870,7 @@ sub lookupCommitRef
 
     if( defined($commitHash) )
     {
-        my $type=safe_pipe_capture("git","cat-file","-t",$commitHash);
+        my $type = ::safe_pipe_capture("git","cat-file","-t",$commitHash);
         if( ! ($type=~/^commit\s*$/ ) )
         {
             $commitHash=undef;
@@ -4907,7 +4923,7 @@ sub commitmessage
         return $message;
     }
 
-    my @lines = safe_pipe_capture("git", "cat-file", "commit", $commithash);
+    my @lines = ::safe_pipe_capture("git", "cat-file", "commit", $commithash);
     shift @lines while ( $lines[0] =~ /\S/ );
     $message = join("",@lines);
     $message .= " " if ( $message =~ /\n$/ );
@@ -5056,25 +5072,6 @@ sub in_array
     return $retval;
 }
 
-=head2 safe_pipe_capture
-
-an alternative to `command` that allows input to be passed as an array
-to work around shell problems with weird characters in arguments
-
-=cut
-sub safe_pipe_capture {
-
-    my @output;
-
-    if (my $pid = open my $child, '-|') {
-        @output = (<$child>);
-        close $child or die join(' ',@_).": $! $?";
-    } else {
-        exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
-    }
-    return wantarray ? @output : join('',@output);
-}
-
 =head2 mangle_dirname
 
 create a string from a directory name that is suitable to use as
index fe30be38dc8c534cabf8a0f7145c41aecd1e6989..918a8de369113bb022f5080ac9c516771f544577 100644 (file)
@@ -161,7 +161,9 @@ ifeq ($(uname_S),Darwin)
        endif
 endif
 ifneq (,$(findstring MINGW,$(uname_S)))
+ifeq ($(shell expr "$(uname_R)" : '1\.'),2)
        NO_MSGFMT=1
+endif
        GITGUI_WINDOWS_WRAPPER := YesPlease
        GITGUI_RELATIVE := 1
 endif
index 75d1da8d31c31db298aea078db863735b6d45439..80f5a59bbbfcf1623913fd1196370a4de75729d6 100644 (file)
@@ -142,6 +142,10 @@ constructor pick {} {
                                -label [mc "Recent Repositories"]
                }
 
+       if {[set lenrecent [llength $sorted_recent]] < $maxrecent} {
+               set lenrecent $maxrecent
+       }
+
                ${NS}::label $w_body.space
                ${NS}::label $w_body.recentlabel \
                        -anchor w \
@@ -153,7 +157,7 @@ constructor pick {} {
                        -background [get_bg_color $w_body.recentlabel] \
                        -wrap none \
                        -width 50 \
-                       -height $maxrecent
+                       -height $lenrecent
                $w_recentlist tag conf link \
                        -foreground blue \
                        -underline 1
@@ -235,19 +239,19 @@ method _invoke_next {} {
 
 proc _get_recentrepos {} {
        set recent [list]
-       foreach p [get_config gui.recentrepo] {
+       foreach p [lsort -unique [get_config gui.recentrepo]] {
                if {[_is_git [file join $p .git]]} {
                        lappend recent $p
                } else {
                        _unset_recentrepo $p
                }
        }
-       return [lsort $recent]
+       return $recent
 }
 
 proc _unset_recentrepo {p} {
        regsub -all -- {([()\[\]{}\.^$+*?\\])} $p {\\\1} p
-       git config --global --unset gui.recentrepo "^$p\$"
+       catch {git config --global --unset-all gui.recentrepo "^$p\$"}
        load_config 1
 }
 
@@ -262,12 +266,11 @@ proc _append_recentrepos {path} {
        set i [lsearch $recent $path]
        if {$i >= 0} {
                _unset_recentrepo $path
-               set recent [lreplace $recent $i $i]
        }
 
-       lappend recent $path
        git config --global --add gui.recentrepo $path
        load_config 1
+       set recent [get_config gui.recentrepo]
 
        if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} {
                set maxrecent 10
@@ -275,7 +278,7 @@ proc _append_recentrepos {path} {
 
        while {[llength $recent] > $maxrecent} {
                _unset_recentrepo [lindex $recent 0]
-               set recent [lrange $recent 1 end]
+               set recent [get_config gui.recentrepo]
        }
 }
 
index bcf0d92ec223bf9e1f0995c5acef6376d1299502..6c390d6c229d7ef8cca8ae01d33b42b57e29c3b3 100755 (executable)
@@ -100,7 +100,7 @@ do
        if test $? -ne 0
        then
                gettextln "Simple merge did not work, trying automatic merge."
-               git-merge-index -o git-merge-one-file -a ||
+               git merge-index -o git-merge-one-file -a ||
                OCTOPUS_FAILURE=1
                next=$(git write-tree 2>/dev/null)
        fi
index 424b034e34b4b40e7c44da16ddf83a31dee6f6a0..9879c59395edff1a02e390397fc623bc60724c57 100755 (executable)
@@ -115,16 +115,16 @@ case "${1:-.}${2:-.}${3:-.}" in
                ;;
        esac
 
-       src1=$(git-unpack-file $2)
-       src2=$(git-unpack-file $3)
+       src1=$(git unpack-file $2)
+       src2=$(git unpack-file $3)
        case "$1" in
        '')
                echo "Added $4 in both, but differently."
-               orig=$(git-unpack-file e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
+               orig=$(git unpack-file e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
                ;;
        *)
                echo "Auto-merging $4"
-               orig=$(git-unpack-file $1)
+               orig=$(git unpack-file $1)
                ;;
        esac
 
index c9da747fcfe504b1fd233c68d91e549def0f3571..343fe7bccd0d64f0caff1ad5d3f981729a8e5fc9 100755 (executable)
@@ -45,7 +45,7 @@ then
        exit 0
 else
        echo "Simple merge failed, trying Automatic merge."
-       if git-merge-index -o git-merge-one-file -a
+       if git merge-index -o git-merge-one-file -a
        then
                exit 0
        else
index 375239341fbfe885e51a25e9e0dc2d4fee791345..319933e70a34f9da4ec93d063eb102eff33b6787 100644 (file)
@@ -45,7 +45,7 @@ then
        # itself well to recording empty patches.  fortunately, cherry-pick
        # makes this easy
        git cherry-pick ${gpg_sign_opt:+"$gpg_sign_opt"} --allow-empty \
-               --right-only "$revisions" \
+               $allow_rerere_autoupdate --right-only "$revisions" \
                ${restrict_revision+^$restrict_revision}
        ret=$?
 else
@@ -82,6 +82,7 @@ else
        fi
 
        git am $git_am_opt --rebasing --resolvemsg="$resolvemsg" \
+               $allow_rerere_autoupdate \
                ${gpg_sign_opt:+"$gpg_sign_opt"} <"$GIT_DIR/rebased-patches"
        ret=$?
 
index 90b1fbe9cf6e8dfb2f4331916809fa40bf9050d2..29b7e8824b53abeaa68780b95d5954f67f734098 100644 (file)
@@ -281,7 +281,7 @@ pick_one () {
 
        test -d "$rewritten" &&
                pick_one_preserving_merges "$@" && return
-       output eval git cherry-pick \
+       output eval git cherry-pick $allow_rerere_autoupdate \
                        ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
                        "$strategy_args" $empty_args $ff "$@"
 
@@ -393,7 +393,8 @@ pick_one_preserving_merges () {
                        merge_args="--no-log --no-ff"
                        if ! do_with_author output eval \
                        'git merge ${gpg_sign_opt:+"$gpg_sign_opt"} \
-                               $merge_args $strategy_args -m "$msg_content" $new_parents'
+                               $allow_rerere_autoupdate $merge_args \
+                               $strategy_args -m "$msg_content" $new_parents'
                        then
                                printf "%s\n" "$msg_content" > "$GIT_DIR"/MERGE_MSG
                                die_with_patch $sha1 "$(eval_gettext "Error redoing merge \$sha1")"
@@ -401,7 +402,7 @@ pick_one_preserving_merges () {
                        echo "$sha1 $(git rev-parse HEAD^0)" >> "$rewritten_list"
                        ;;
                *)
-                       output eval git cherry-pick \
+                       output eval git cherry-pick $allow_rerere_autoupdate \
                                ${gpg_sign_opt:+$(git rev-parse --sq-quote "$gpg_sign_opt")} \
                                "$strategy_args" "$@" ||
                                die_with_patch $sha1 "$(eval_gettext "Could not pick \$sha1")"
index eebd33276da9028afa1f2ed3b03e60f037d7e646..13c172bd94fc5d4a9658b27fae9733f73a272376 100755 (executable)
@@ -128,7 +128,7 @@ git show -s --format='The following changes since commit %H:
 
   %s (%ci)
 
-are available in the git repository at:
+are available in the Git repository at:
 ' $merge_base &&
 echo "  $url $pretty_remote" &&
 git show -s --format='
index fa6526986ed40143a12ffcd6563fd52b49d209e7..2208dcc2139be7130749fd3bae462320f17d3b21 100755 (executable)
@@ -155,7 +155,6 @@ sub format_2822_time {
 }
 
 my $have_email_valid = eval { require Email::Valid; 1 };
-my $have_mail_address = eval { require Mail::Address; 1 };
 my $smtp;
 my $auth;
 my $num_sent = 0;
@@ -490,11 +489,7 @@ sub read_config {
 ($repocommitter) = Git::ident_person(@repo, 'committer');
 
 sub parse_address_line {
-       if ($have_mail_address) {
-               return map { $_->format } Mail::Address->parse($_[0]);
-       } else {
-               return Git::parse_mailboxes($_[0]);
-       }
+       return Git::parse_mailboxes($_[0]);
 }
 
 sub split_addrs {
@@ -1089,6 +1084,26 @@ sub sanitize_address {
 
 }
 
+sub strip_garbage_one_address {
+       my ($addr) = @_;
+       chomp $addr;
+       if ($addr =~ /^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {
+               # "Foo Bar" <foobar@example.com> [possibly garbage here]
+               # Foo Bar <foobar@example.com> [possibly garbage here]
+               return $1;
+       }
+       if ($addr =~ /^(<[^>]*>).*/) {
+               # <foo@example.com> [possibly garbage here]
+               # if garbage contains other addresses, they are ignored.
+               return $1;
+       }
+       if ($addr =~ /^([^"#,\s]*)/) {
+               # address without quoting: remove anything after the address
+               return $1;
+       }
+       return $addr;
+}
+
 sub sanitize_address_list {
        return (map { sanitize_address($_) } @_);
 }
@@ -1590,10 +1605,12 @@ sub send_message {
        # Now parse the message body
        while(<$fh>) {
                $message .=  $_;
-               if (/^(Signed-off-by|Cc): ([^>]*>?)/i) {
+               if (/^(Signed-off-by|Cc): (.*)/i) {
                        chomp;
                        my ($what, $c) = ($1, $2);
-                       chomp $c;
+                       # strip garbage for the address we'll use:
+                       $c = strip_garbage_one_address($c);
+                       # sanitize a bit more to decide whether to suppress the address:
                        my $sc = sanitize_address($c);
                        if ($sc eq $sender) {
                                next if ($suppress_cc{'self'});
index 9b6c2da7b4b75980403d836f6fa1fc516bdc3639..328cd80d83a03a674e0541bddb6bce0532c682b7 100755 (executable)
@@ -43,9 +43,16 @@ no_changes () {
 }
 
 untracked_files () {
+       if test "$1" = "-z"
+       then
+               shift
+               z=-z
+       else
+               z=
+       fi
        excl_opt=--exclude-standard
        test "$untracked" = "all" && excl_opt=
-       git ls-files -o -z $excl_opt -- "$@"
+       git ls-files -o $z $excl_opt -- "$@"
 }
 
 clear_stash () {
@@ -114,7 +121,7 @@ create_stash () {
                # Untracked files are stored by themselves in a parentless commit, for
                # ease of unpacking later.
                u_commit=$(
-                       untracked_files "$@" | (
+                       untracked_files -z "$@" | (
                                GIT_INDEX_FILE="$TMPindex" &&
                                export GIT_INDEX_FILE &&
                                rm -f "$TMPindex" &&
@@ -573,7 +580,7 @@ apply_stash () {
 
        if test -n "$u_tree"
        then
-               GIT_INDEX_FILE="$TMPindex" git-read-tree "$u_tree" &&
+               GIT_INDEX_FILE="$TMPindex" git read-tree "$u_tree" &&
                GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
                rm -f "$TMPindex" ||
                die "$(gettext "Could not restore untracked files from stash entry")"
index e131760eecf944aee97e18a68ab1f171fc0e1d1b..66d1ae8ef6c5f12c856a2f88eabce515dcafd489 100755 (executable)
@@ -611,7 +611,6 @@ cmd_update()
                die_if_unmatched "$mode" "$sha1"
 
                name=$(git submodule--helper name "$sm_path") || exit
-               url=$(git config submodule."$name".url)
                if ! test -z "$update"
                then
                        update_module=$update
@@ -864,7 +863,7 @@ cmd_summary() {
                                test $status != A && test $ignore_config = all && continue
                        fi
                        # Also show added or modified modules which are checked out
-                       GIT_DIR="$sm_path/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
+                       GIT_DIR="$sm_path/.git" git rev-parse --git-dir >/dev/null 2>&1 &&
                        printf '%s\n' "$sm_path"
                done
        )
@@ -898,11 +897,11 @@ cmd_summary() {
                missing_dst=
 
                test $mod_src = 160000 &&
-               ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
+               ! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_src^0 >/dev/null &&
                missing_src=t
 
                test $mod_dst = 160000 &&
-               ! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
+               ! GIT_DIR="$name/.git" git rev-parse -q --verify $sha1_dst^0 >/dev/null &&
                missing_dst=t
 
                display_name=$(git submodule--helper relative-path "$name" "$wt_prefix")
diff --git a/git.c b/git.c
index 489aab4d83a1e222c3e5f3b3bd812b4f29ecd34f..6b6d9f68e1ea010cb951501cab1fdfa82037b878 100644 (file)
--- a/git.c
+++ b/git.c
@@ -33,6 +33,16 @@ static void commit_pager_choice(void) {
        }
 }
 
+void setup_auto_pager(const char *cmd, int def)
+{
+       if (use_pager != -1 || pager_in_use())
+               return;
+       use_pager = check_pager_config(cmd);
+       if (use_pager == -1)
+               use_pager = def;
+       commit_pager_choice();
+}
+
 static int handle_options(const char ***argv, int *argc, int *envchanged)
 {
        const char **orig_argv = *argv;
@@ -283,6 +293,7 @@ static int handle_alias(int *argcp, const char ***argv)
  */
 #define NEED_WORK_TREE         (1<<3)
 #define SUPPORT_SUPER_PREFIX   (1<<4)
+#define DELAY_PAGER_CONFIG     (1<<5)
 
 struct cmd_struct {
        const char *cmd;
@@ -306,7 +317,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
                        prefix = setup_git_directory_gently(&nongit_ok);
                }
 
-               if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
+               if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
+                   !(p->option & DELAY_PAGER_CONFIG))
                        use_pager = check_pager_config(p->cmd);
                if (use_pager == -1 && p->option & USE_PAGER)
                        use_pager = 1;
@@ -454,7 +466,7 @@ static struct cmd_struct commands[] = {
        { "stripspace", cmd_stripspace },
        { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX},
        { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
-       { "tag", cmd_tag, RUN_SETUP },
+       { "tag", cmd_tag, RUN_SETUP | DELAY_PAGER_CONFIG },
        { "unpack-file", cmd_unpack_file, RUN_SETUP },
        { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
        { "update-index", cmd_update_index, RUN_SETUP },
@@ -547,7 +559,7 @@ static void execv_dashed_external(const char **argv)
        if (get_super_prefix())
                die("%s doesn't support --super-prefix", argv[0]);
 
-       if (use_pager == -1)
+       if (use_pager == -1 && !is_builtin(argv[0]))
                use_pager = check_pager_config(argv[0]);
        commit_pager_choice();
 
index 3d4a8ee27c96a8e1eb94f95771aeca7825f8a076..9208f42ed1753530b8ea46761f8ecc61bbe95976 100755 (executable)
@@ -3071,6 +3071,8 @@ sub git_get_projects_list {
                                return if (m!^[/.]$!);
                                # only directories can be git repositories
                                return unless (-d $_);
+                               # need search permission
+                               return unless (-x $_);
                                # don't traverse too deep (Find is super slow on os x)
                                # $project_maxdepth excludes depth of $projectroot
                                if (($File::Find::name =~ tr!/!!) - $pfxdepth > $project_maxdepth) {
diff --git a/grep.c b/grep.c
index 2efec0e182d5c20fb77903fc61dedd09a8ee7ba3..c9e7cc735681ed4c670abd7f319336204391173b 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1821,7 +1821,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                return 0;
 
        if (opt->status_only)
-               return 0;
+               return opt->unmatch_name_only;
        if (opt->unmatch_name_only) {
                /* We did not see any hit, so we want to show this */
                show_name(opt, gs->name);
index c91f40a610541d6201edf2a2c95b8fef203f3c2f..df969609beed88439845d9324b2fed15ff32b109 100644 (file)
@@ -1017,7 +1017,7 @@ static int get_oid_hex_from_objpath(const char *path, struct object_id *oid)
        memcpy(hex, path, 2);
        path += 2;
        path++; /* skip '/' */
-       memcpy(hex, path, GIT_SHA1_HEXSZ - 2);
+       memcpy(hex + 2, path, GIT_SHA1_HEXSZ - 2);
 
        return get_oid_hex(hex, oid);
 }
diff --git a/http.c b/http.c
index c6c010f8816f4a01c4b7658a9044b179515590ea..76ff63c14d44057f78b532fb9d873c94ae56231e 100644 (file)
--- a/http.c
+++ b/http.c
@@ -272,10 +272,10 @@ static int http_options(const char *var, const char *value, void *cb)
        if (!strcmp("http.sslversion", var))
                return git_config_string(&ssl_version, var, value);
        if (!strcmp("http.sslcert", var))
-               return git_config_string(&ssl_cert, var, value);
+               return git_config_pathname(&ssl_cert, var, value);
 #if LIBCURL_VERSION_NUM >= 0x070903
        if (!strcmp("http.sslkey", var))
-               return git_config_string(&ssl_key, var, value);
+               return git_config_pathname(&ssl_key, var, value);
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
        if (!strcmp("http.sslcapath", var))
index bd574cb75210334b1a4628f182743b49bd389cd7..70187e3eb30b102c7038a1d1c4e0033d856c99ac 100644 (file)
@@ -368,11 +368,16 @@ static struct strbuf *decode_q_segment(const struct strbuf *q_seg, int rfc2047)
 
        while ((c = *in++) != 0) {
                if (c == '=') {
-                       int d = *in++;
+                       int ch, d = *in;
                        if (d == '\n' || !d)
                                break; /* drop trailing newline */
-                       strbuf_addch(out, (hexval(d) << 4) | hexval(*in++));
-                       continue;
+                       ch = hex2chr(in);
+                       if (ch >= 0) {
+                               strbuf_addch(out, ch);
+                               in += 2;
+                               continue;
+                       }
+                       /* garbage -- fall through */
                }
                if (rfc2047 && c == '_') /* rfc2047 4.2 (2) */
                        c = 0x20;
index 70e3fbeefbe24233a545e75c0322a8a310778e43..c12b354f1003a29c6faf58e7ec970e4ac761812e 100644 (file)
@@ -99,8 +99,7 @@ static struct notes_merge_pair *find_notes_merge_pair_pos(
        else {
                *occupied = 0;
                if (insert_new && i < len) {
-                       memmove(list + i + 1, list + i,
-                               (len - i) * sizeof(struct notes_merge_pair));
+                       MOVE_ARRAY(list + i + 1, list + i, len - i);
                        memset(list + i, 0, sizeof(struct notes_merge_pair));
                }
        }
index f818777412187890b88a6c90507b8b41f131487c..321d7e9201b2ba6c8b39592a591786409a87edb1 100644 (file)
--- a/object.c
+++ b/object.c
@@ -141,7 +141,6 @@ void *create_object(const unsigned char *sha1, void *o)
        struct object *obj = o;
 
        obj->parsed = 0;
-       obj->used = 0;
        obj->flags = 0;
        hashcpy(obj->oid.hash, sha1);
 
index 33e5cc9943eeff8618493fbd750aecde0b836710..0a419ba8da52453ebbd202f50d5c2aa1c9aea2f0 100644 (file)
--- a/object.h
+++ b/object.h
@@ -38,6 +38,7 @@ struct object_array {
  * http-push.c:                            16-----19
  * commit.c:                               16-----19
  * sha1_name.c:                                     20
+ * builtin/fsck.c:  0--3
  */
 #define FLAG_BITS  27
 
@@ -46,7 +47,6 @@ struct object_array {
  */
 struct object {
        unsigned parsed : 1;
-       unsigned used : 1;
        unsigned type : TYPE_BITS;
        unsigned flags : FLAG_BITS;
        struct object_id oid;
diff --git a/path.c b/path.c
index e50d2befcf826680c70ac59295c06004dd6221cf..2fecf854fe6d87cfef34678f1bc9c864ea834cb4 100644 (file)
--- a/path.c
+++ b/path.c
@@ -33,11 +33,10 @@ static struct strbuf *get_pathname(void)
        return sb;
 }
 
-static char *cleanup_path(char *path)
+static const char *cleanup_path(const char *path)
 {
        /* Clean it up */
-       if (!memcmp(path, "./", 2)) {
-               path += 2;
+       if (skip_prefix(path, "./", &path)) {
                while (*path == '/')
                        path++;
        }
@@ -46,7 +45,7 @@ static char *cleanup_path(char *path)
 
 static void strbuf_cleanup_path(struct strbuf *sb)
 {
-       char *path = cleanup_path(sb->buf);
+       const char *path = cleanup_path(sb->buf);
        if (path > sb->buf)
                strbuf_remove(sb, 0, path - sb->buf);
 }
@@ -63,7 +62,7 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
                strlcpy(buf, bad_path, n);
                return buf;
        }
-       return cleanup_path(buf);
+       return (char *)cleanup_path(buf);
 }
 
 static int dir_prefix(const char *buf, const char *dir)
index f4b56e6d4d38cbc4365ecfc9073f5caf3a05377a..ffa09ace924e0a7b079d039e905363435b08cf9b 100644 (file)
@@ -532,7 +532,7 @@ sub version {
 =cut
 
 sub get_tz_offset {
-       # some systmes don't handle or mishandle %z, so be creative.
+       # some systems don't handle or mishandle %z, so be creative.
        my $t = shift || time;
        my $gm = timegm(localtime($t));
        my $sign = qw( + + - )[ $gm <=> $t ];
index 98518f4ddb4c031417e313dcf4daaa68e9955be0..bc4eed3d75461444f8af0e27e2930ccb25663312 100644 (file)
@@ -1416,7 +1416,7 @@ sub parse_svn_date {
                        delete $ENV{TZ};
                }
 
-               my $our_TZ = get_tz_offset();
+               my $our_TZ = get_tz_offset($epoch_in_UTC);
 
                # This converts $epoch_in_UTC into our local timezone.
                my ($sec, $min, $hour, $mday, $mon, $year,
index 9d845ecc3ccc65194852a1821432cec771ac49dc..f364944b931a756b3b4819fb49ff5e9e01daf676 100644 (file)
@@ -136,9 +136,10 @@ static void format_packet(struct strbuf *out, const char *fmt, va_list args)
 static int packet_write_fmt_1(int fd, int gently,
                              const char *fmt, va_list args)
 {
-       struct strbuf buf = STRBUF_INIT;
+       static struct strbuf buf = STRBUF_INIT;
        ssize_t count;
 
+       strbuf_reset(&buf);
        format_packet(&buf, fmt, args);
        count = write_in_full(fd, buf.buf, buf.len);
        if (count == buf.len)
@@ -171,25 +172,6 @@ int packet_write_fmt_gently(int fd, const char *fmt, ...)
        return status;
 }
 
-int packet_writel(int fd, const char *line, ...)
-{
-       va_list args;
-       int err;
-       va_start(args, line);
-       for (;;) {
-               if (!line)
-                       break;
-               if (strlen(line) > LARGE_PACKET_DATA_MAX)
-                       return -1;
-               err = packet_write_fmt_gently(fd, "%s\n", line);
-               if (err)
-                       return err;
-               line = va_arg(args, const char*);
-       }
-       va_end(args);
-       return packet_flush_gently(fd);
-}
-
 static int packet_write_gently(const int fd_out, const char *buf, size_t size)
 {
        static char packet_write_buffer[LARGE_PACKET_MAX];
index 450183b6496fce4ec3077322f61623152c87cf07..66ef610fc4f7de2f6b7572f1e5fb36438c5dec19 100644 (file)
@@ -25,8 +25,6 @@ void packet_buf_flush(struct strbuf *buf);
 void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
 int packet_flush_gently(int fd);
 int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
-LAST_ARG_MUST_BE_NULL
-int packet_writel(int fd, const char *line, ...);
 int write_packetized_from_fd(int fd_in, int fd_out);
 int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);
 
index e4b561c5822b7b19878065c77a04ffa0c9e43d61..39cad5112b603df9c8ed927e738b2f7917da7522 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -947,6 +947,7 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
                          struct format_commit_context *c)
 {
        const char *rest = placeholder;
+       const char *basic_color = NULL;
 
        if (placeholder[1] == '(') {
                const char *begin = placeholder + 2;
@@ -955,23 +956,41 @@ static size_t parse_color(struct strbuf *sb, /* in UTF-8 */
 
                if (!end)
                        return 0;
+
                if (skip_prefix(begin, "auto,", &begin)) {
                        if (!want_color(c->pretty_ctx->color))
                                return end - placeholder + 1;
+               } else if (skip_prefix(begin, "always,", &begin)) {
+                       /* nothing to do; we do not respect want_color at all */
+               } else {
+                       /* the default is the same as "auto" */
+                       if (!want_color(c->pretty_ctx->color))
+                               return end - placeholder + 1;
                }
+
                if (color_parse_mem(begin, end - begin, color) < 0)
                        die(_("unable to parse --pretty format"));
                strbuf_addstr(sb, color);
                return end - placeholder + 1;
        }
+
+       /*
+        * We handle things like "%C(red)" above; for historical reasons, there
+        * are a few colors that can be specified without parentheses (and
+        * they cannot support things like "auto" or "always" at all).
+        */
        if (skip_prefix(placeholder + 1, "red", &rest))
-               strbuf_addstr(sb, GIT_COLOR_RED);
+               basic_color = GIT_COLOR_RED;
        else if (skip_prefix(placeholder + 1, "green", &rest))
-               strbuf_addstr(sb, GIT_COLOR_GREEN);
+               basic_color = GIT_COLOR_GREEN;
        else if (skip_prefix(placeholder + 1, "blue", &rest))
-               strbuf_addstr(sb, GIT_COLOR_BLUE);
+               basic_color = GIT_COLOR_BLUE;
        else if (skip_prefix(placeholder + 1, "reset", &rest))
-               strbuf_addstr(sb, GIT_COLOR_RESET);
+               basic_color = GIT_COLOR_RESET;
+
+       if (basic_color && want_color(c->pretty_ctx->color))
+               strbuf_addstr(sb, basic_color);
+
        return rest - placeholder;
 }
 
index 2121b6e7bba5f310825e0b1f2eccd524cc8658ce..acfb028f480b5545aa82d5c0533fe8bdfc581dfd 100644 (file)
@@ -515,9 +515,8 @@ int remove_index_entry_at(struct index_state *istate, int pos)
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
                return 0;
-       memmove(istate->cache + pos,
-               istate->cache + pos + 1,
-               (istate->cache_nr - pos) * sizeof(struct cache_entry *));
+       MOVE_ARRAY(istate->cache + pos, istate->cache + pos + 1,
+                  istate->cache_nr - pos);
        return 1;
 }
 
index ae6ecbd1cfe42936b22488d7761e4e487e2922ca..f3e53d4448f10ca1002e546890ca682c02ecc9ad 100644 (file)
@@ -97,14 +97,19 @@ static struct used_atom {
        } u;
 } *used_atom;
 static int used_atom_cnt, need_tagged, need_symref;
-static int need_color_reset_at_eol;
 
-static void color_atom_parser(struct used_atom *atom, const char *color_value)
+static void color_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *color_value)
 {
        if (!color_value)
                die(_("expected format: %%(color:<color>)"));
        if (color_parse(color_value, atom->u.color) < 0)
                die(_("unrecognized color: %%(color:%s)"), color_value);
+       /*
+        * We check this after we've parsed the color, which lets us complain
+        * about syntactically bogus color names even if they won't be used.
+        */
+       if (!want_color(format->use_color))
+               color_parse("", atom->u.color);
 }
 
 static void refname_atom_parser_internal(struct refname_atom *atom,
@@ -127,7 +132,7 @@ static void refname_atom_parser_internal(struct refname_atom *atom,
                die(_("unrecognized %%(%s) argument: %s"), name, arg);
 }
 
-static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
+static void remote_ref_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        struct string_list params = STRING_LIST_INIT_DUP;
        int i;
@@ -161,28 +166,28 @@ static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
        string_list_clear(&params, 0);
 }
 
-static void body_atom_parser(struct used_atom *atom, const char *arg)
+static void body_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        if (arg)
                die(_("%%(body) does not take arguments"));
        atom->u.contents.option = C_BODY_DEP;
 }
 
-static void subject_atom_parser(struct used_atom *atom, const char *arg)
+static void subject_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        if (arg)
                die(_("%%(subject) does not take arguments"));
        atom->u.contents.option = C_SUB;
 }
 
-static void trailers_atom_parser(struct used_atom *atom, const char *arg)
+static void trailers_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        if (arg)
                die(_("%%(trailers) does not take arguments"));
        atom->u.contents.option = C_TRAILERS;
 }
 
-static void contents_atom_parser(struct used_atom *atom, const char *arg)
+static void contents_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        if (!arg)
                atom->u.contents.option = C_BARE;
@@ -202,7 +207,7 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)
                die(_("unrecognized %%(contents) argument: %s"), arg);
 }
 
-static void objectname_atom_parser(struct used_atom *atom, const char *arg)
+static void objectname_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        if (!arg)
                atom->u.objectname.option = O_FULL;
@@ -219,7 +224,7 @@ static void objectname_atom_parser(struct used_atom *atom, const char *arg)
                die(_("unrecognized %%(objectname) argument: %s"), arg);
 }
 
-static void refname_atom_parser(struct used_atom *atom, const char *arg)
+static void refname_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        refname_atom_parser_internal(&atom->u.refname, arg, atom->name);
 }
@@ -235,7 +240,7 @@ static align_type parse_align_position(const char *s)
        return -1;
 }
 
-static void align_atom_parser(struct used_atom *atom, const char *arg)
+static void align_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        struct align *align = &atom->u.align;
        struct string_list params = STRING_LIST_INIT_DUP;
@@ -274,7 +279,7 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)
        string_list_clear(&params, 0);
 }
 
-static void if_atom_parser(struct used_atom *atom, const char *arg)
+static void if_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        if (!arg) {
                atom->u.if_then_else.cmp_status = COMPARE_NONE;
@@ -288,7 +293,7 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)
        }
 }
 
-static void head_atom_parser(struct used_atom *atom, const char *arg)
+static void head_atom_parser(const struct ref_format *format, struct used_atom *atom, const char *arg)
 {
        struct object_id unused;
 
@@ -298,7 +303,7 @@ static void head_atom_parser(struct used_atom *atom, const char *arg)
 static struct {
        const char *name;
        cmp_type cmp_type;
-       void (*parser)(struct used_atom *atom, const char *arg);
+       void (*parser)(const struct ref_format *format, struct used_atom *atom, const char *arg);
 } valid_atom[] = {
        { "refname" , FIELD_STR, refname_atom_parser },
        { "objecttype" },
@@ -365,7 +370,8 @@ struct atom_value {
 /*
  * Used to parse format string and sort specifiers
  */
-int parse_ref_filter_atom(const char *atom, const char *ep)
+static int parse_ref_filter_atom(const struct ref_format *format,
+                                const char *atom, const char *ep)
 {
        const char *sp;
        const char *arg;
@@ -409,11 +415,19 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
        REALLOC_ARRAY(used_atom, used_atom_cnt);
        used_atom[at].name = xmemdupz(atom, ep - atom);
        used_atom[at].type = valid_atom[i].cmp_type;
-       if (arg)
+       if (arg) {
                arg = used_atom[at].name + (arg - atom) + 1;
+               if (!*arg) {
+                       /*
+                        * Treat empty sub-arguments list as NULL (i.e.,
+                        * "%(atom:)" is equivalent to "%(atom)").
+                        */
+                       arg = NULL;
+               }
+       }
        memset(&used_atom[at].u, 0, sizeof(used_atom[at].u));
        if (valid_atom[i].parser)
-               valid_atom[i].parser(&used_atom[at], arg);
+               valid_atom[i].parser(format, &used_atom[at], arg);
        if (*atom == '*')
                need_tagged = 1;
        if (!strcmp(valid_atom[i].name, "symref"))
@@ -657,24 +671,26 @@ static const char *find_next(const char *cp)
  * Make sure the format string is well formed, and parse out
  * the used atoms.
  */
-int verify_ref_format(const char *format)
+int verify_ref_format(struct ref_format *format)
 {
        const char *cp, *sp;
 
-       need_color_reset_at_eol = 0;
-       for (cp = format; *cp && (sp = find_next(cp)); ) {
+       format->need_color_reset_at_eol = 0;
+       for (cp = format->format; *cp && (sp = find_next(cp)); ) {
                const char *color, *ep = strchr(sp, ')');
                int at;
 
                if (!ep)
                        return error(_("malformed format string %s"), sp);
                /* sp points at "%(" and ep points at the closing ")" */
-               at = parse_ref_filter_atom(sp + 2, ep);
+               at = parse_ref_filter_atom(format, sp + 2, ep);
                cp = ep + 1;
 
                if (skip_prefix(used_atom[at].name, "color:", &color))
-                       need_color_reset_at_eol = !!strcmp(color, "reset");
+                       format->need_color_reset_at_eol = !!strcmp(color, "reset");
        }
+       if (format->need_color_reset_at_eol && !want_color(format->use_color))
+               format->need_color_reset_at_eol = 0;
        return 0;
 }
 
@@ -2060,35 +2076,34 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting
        }
 }
 
-void format_ref_array_item(struct ref_array_item *info, const char *format,
-                          int quote_style, struct strbuf *final_buf)
+void format_ref_array_item(struct ref_array_item *info,
+                          const struct ref_format *format,
+                          struct strbuf *final_buf)
 {
        const char *cp, *sp, *ep;
        struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
 
-       state.quote_style = quote_style;
+       state.quote_style = format->quote_style;
        push_stack_element(&state.stack);
 
-       for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
+       for (cp = format->format; *cp && (sp = find_next(cp)); cp = ep + 1) {
                struct atom_value *atomv;
 
                ep = strchr(sp, ')');
                if (cp < sp)
                        append_literal(cp, sp, &state);
-               get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
+               get_ref_atom_value(info,
+                                  parse_ref_filter_atom(format, sp + 2, ep),
+                                  &atomv);
                atomv->handler(atomv, &state);
        }
        if (*cp) {
                sp = cp + strlen(cp);
                append_literal(cp, sp, &state);
        }
-       if (need_color_reset_at_eol) {
+       if (format->need_color_reset_at_eol) {
                struct atom_value resetv;
-               char color[COLOR_MAXLEN] = "";
-
-               if (color_parse("reset", color) < 0)
-                       die("BUG: couldn't parse 'reset' as a color");
-               resetv.s = color;
+               resetv.s = GIT_COLOR_RESET;
                append_atom(&resetv, &state);
        }
        if (state.stack->prev)
@@ -2097,26 +2112,38 @@ void format_ref_array_item(struct ref_array_item *info, const char *format,
        pop_stack_element(&state.stack);
 }
 
-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
+void show_ref_array_item(struct ref_array_item *info,
+                        const struct ref_format *format)
 {
        struct strbuf final_buf = STRBUF_INIT;
 
-       format_ref_array_item(info, format, quote_style, &final_buf);
+       format_ref_array_item(info, format, &final_buf);
        fwrite(final_buf.buf, 1, final_buf.len, stdout);
        strbuf_release(&final_buf);
        putchar('\n');
 }
 
 void pretty_print_ref(const char *name, const unsigned char *sha1,
-               const char *format)
+                     const struct ref_format *format)
 {
        struct ref_array_item *ref_item;
        ref_item = new_ref_array_item(name, sha1, 0);
        ref_item->kind = ref_kind_from_refname(name);
-       show_ref_array_item(ref_item, format, 0);
+       show_ref_array_item(ref_item, format);
        free_array_item(ref_item);
 }
 
+static int parse_sorting_atom(const char *atom)
+{
+       /*
+        * This parses an atom using a dummy ref_format, since we don't
+        * actually care about the formatting details.
+        */
+       struct ref_format dummy = REF_FORMAT_INIT;
+       const char *end = atom + strlen(atom);
+       return parse_ref_filter_atom(&dummy, atom, end);
+}
+
 /*  If no sorting option is given, use refname to sort as default */
 struct ref_sorting *ref_default_sorting(void)
 {
@@ -2125,18 +2152,13 @@ struct ref_sorting *ref_default_sorting(void)
        struct ref_sorting *sorting = xcalloc(1, sizeof(*sorting));
 
        sorting->next = NULL;
-       sorting->atom = parse_ref_filter_atom(cstr_name, cstr_name + strlen(cstr_name));
+       sorting->atom = parse_sorting_atom(cstr_name);
        return sorting;
 }
 
-int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
+void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
 {
-       struct ref_sorting **sorting_tail = opt->value;
        struct ref_sorting *s;
-       int len;
-
-       if (!arg) /* should --no-sort void the list ? */
-               return -1;
 
        s = xcalloc(1, sizeof(*s));
        s->next = *sorting_tail;
@@ -2149,8 +2171,14 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
        if (skip_prefix(arg, "version:", &arg) ||
            skip_prefix(arg, "v:", &arg))
                s->version = 1;
-       len = strlen(arg);
-       s->atom = parse_ref_filter_atom(arg, arg+len);
+       s->atom = parse_sorting_atom(arg);
+}
+
+int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
+{
+       if (!arg) /* should --no-sort void the list ? */
+               return -1;
+       parse_ref_sorting(opt->value, arg);
        return 0;
 }
 
index 6552024f09e4d4d587eaf49377e7504a79818c1d..0d98342b343196387c0f4e2dcd5978a9361d8edb 100644 (file)
@@ -72,6 +72,21 @@ struct ref_filter {
                verbose;
 };
 
+struct ref_format {
+       /*
+        * Set these to define the format; make sure you call
+        * verify_ref_format() afterwards to finalize.
+        */
+       const char *format;
+       int quote_style;
+       int use_color;
+
+       /* Internal state to ref-filter */
+       int need_color_reset_at_eol;
+};
+
+#define REF_FORMAT_INIT { NULL, 0, -1 }
+
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
        { OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \
@@ -90,17 +105,18 @@ struct ref_filter {
 int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int type);
 /*  Clear all memory allocated to ref_array */
 void ref_array_clear(struct ref_array *array);
-/*  Parse format string and sort specifiers */
-int parse_ref_filter_atom(const char *atom, const char *ep);
 /*  Used to verify if the given format is correct and to parse out the used atoms */
-int verify_ref_format(const char *format);
+int verify_ref_format(struct ref_format *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
 void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
 /*  Based on the given format and quote_style, fill the strbuf */
-void format_ref_array_item(struct ref_array_item *info, const char *format,
-                          int quote_style, struct strbuf *final_buf);
+void format_ref_array_item(struct ref_array_item *info,
+                          const struct ref_format *format,
+                          struct strbuf *final_buf);
 /*  Print the ref using the given format and quote_style */
-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);
+void show_ref_array_item(struct ref_array_item *info, const struct ref_format *format);
+/*  Parse a single sort specifier and add it to the list */
+void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *atom);
 /*  Callback function for parsing the sort option */
 int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);
 /*  Default sort option based on refname */
@@ -117,6 +133,6 @@ void setup_ref_filter_porcelain_msg(void);
  * name must be a fully qualified refname.
  */
 void pretty_print_ref(const char *name, const unsigned char *sha1,
-               const char *format);
+                     const struct ref_format *format);
 
 #endif /*  REF_FILTER_H  */
index 081f89b70d643514f148aaff9121263361fdb956..74ebe5148f6623f0bffd612262519266a8ab2cc4 100644 (file)
@@ -94,45 +94,6 @@ static int get_reflog_recno_by_time(struct complete_reflogs *array,
        return -1;
 }
 
-struct commit_info_lifo {
-       struct commit_info {
-               struct commit *commit;
-               void *util;
-       } *items;
-       int nr, alloc;
-};
-
-static struct commit_info *get_commit_info(struct commit *commit,
-               struct commit_info_lifo *lifo, int pop)
-{
-       int i;
-       for (i = 0; i < lifo->nr; i++)
-               if (lifo->items[i].commit == commit) {
-                       struct commit_info *result = &lifo->items[i];
-                       if (pop) {
-                               if (i + 1 < lifo->nr)
-                                       memmove(lifo->items + i,
-                                               lifo->items + i + 1,
-                                               (lifo->nr - i) *
-                                               sizeof(struct commit_info));
-                               lifo->nr--;
-                       }
-                       return result;
-               }
-       return NULL;
-}
-
-static void add_commit_info(struct commit *commit, void *util,
-               struct commit_info_lifo *lifo)
-{
-       struct commit_info *info;
-       ALLOC_GROW(lifo->items, lifo->nr + 1, lifo->alloc);
-       info = lifo->items + lifo->nr;
-       info->commit = commit;
-       info->util = util;
-       lifo->nr++;
-}
-
 struct commit_reflog {
        int recno;
        enum selector_type {
@@ -144,7 +105,8 @@ struct commit_reflog {
 };
 
 struct reflog_walk_info {
-       struct commit_info_lifo reflogs;
+       struct commit_reflog **logs;
+       size_t nr, alloc;
        struct string_list complete_reflogs;
        struct commit_reflog *last_commit_reflog;
 };
@@ -233,52 +195,10 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
        commit_reflog->selector = selector;
        commit_reflog->reflogs = reflogs;
 
-       add_commit_info(commit, commit_reflog, &info->reflogs);
-       return 0;
-}
+       ALLOC_GROW(info->logs, info->nr + 1, info->alloc);
+       info->logs[info->nr++] = commit_reflog;
 
-void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
-{
-       struct commit_info *commit_info =
-               get_commit_info(commit, &info->reflogs, 0);
-       struct commit_reflog *commit_reflog;
-       struct object *logobj;
-       struct reflog_info *reflog;
-
-       info->last_commit_reflog = NULL;
-       if (!commit_info)
-               return;
-
-       commit_reflog = commit_info->util;
-       if (commit_reflog->recno < 0) {
-               commit->parents = NULL;
-               return;
-       }
-       info->last_commit_reflog = commit_reflog;
-
-       do {
-               reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
-               commit_reflog->recno--;
-               logobj = parse_object(&reflog->ooid);
-       } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
-
-       if (!logobj && commit_reflog->recno >= 0 && is_null_oid(&reflog->ooid)) {
-               /* a root commit, but there are still more entries to show */
-               reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
-               logobj = parse_object(&reflog->noid);
-               if (!logobj)
-                       logobj = parse_object(&reflog->ooid);
-       }
-
-       if (!logobj || logobj->type != OBJ_COMMIT) {
-               commit_info->commit = NULL;
-               commit->parents = NULL;
-               return;
-       }
-       commit_info->commit = (struct commit *)logobj;
-
-       commit->parents = xcalloc(1, sizeof(struct commit_list));
-       commit->parents->item = commit_info->commit;
+       return 0;
 }
 
 void get_reflog_selector(struct strbuf *sb,
@@ -344,6 +264,18 @@ const char *get_reflog_ident(struct reflog_walk_info *reflog_info)
        return info->email;
 }
 
+timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info)
+{
+       struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog;
+       struct reflog_info *info;
+
+       if (!commit_reflog)
+               return 0;
+
+       info = &commit_reflog->reflogs->items[commit_reflog->recno+1];
+       return info->timestamp;
+}
+
 void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
                         const struct date_mode *dmode, int force_date)
 {
@@ -365,3 +297,53 @@ void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline,
                strbuf_release(&selector);
        }
 }
+
+int reflog_walk_empty(struct reflog_walk_info *info)
+{
+       return !info || !info->nr;
+}
+
+static struct commit *next_reflog_commit(struct commit_reflog *log)
+{
+       for (; log->recno >= 0; log->recno--) {
+               struct reflog_info *entry = &log->reflogs->items[log->recno];
+               struct object *obj = parse_object(&entry->noid);
+
+               if (obj && obj->type == OBJ_COMMIT)
+                       return (struct commit *)obj;
+       }
+       return NULL;
+}
+
+static timestamp_t log_timestamp(struct commit_reflog *log)
+{
+       return log->reflogs->items[log->recno].timestamp;
+}
+
+struct commit *next_reflog_entry(struct reflog_walk_info *walk)
+{
+       struct commit_reflog *best = NULL;
+       struct commit *best_commit = NULL;
+       size_t i;
+
+       for (i = 0; i < walk->nr; i++) {
+               struct commit_reflog *log = walk->logs[i];
+               struct commit *commit = next_reflog_commit(log);
+
+               if (!commit)
+                       continue;
+
+               if (!best || log_timestamp(log) > log_timestamp(best)) {
+                       best = log;
+                       best_commit = commit;
+               }
+       }
+
+       if (best) {
+               best->recno--;
+               walk->last_commit_reflog = best;
+               return best_commit;
+       }
+
+       return NULL;
+}
index 27886f793e75de484b2a3c168e6024cf4139aae3..7553c448fe5bf8b432d7fdb2ef5cba967bb985cd 100644 (file)
@@ -8,16 +8,19 @@ struct reflog_walk_info;
 extern void init_reflog_walk(struct reflog_walk_info **info);
 extern int add_reflog_for_walk(struct reflog_walk_info *info,
                struct commit *commit, const char *name);
-extern void fake_reflog_parent(struct reflog_walk_info *info,
-               struct commit *commit);
 extern void show_reflog_message(struct reflog_walk_info *info, int,
                                const struct date_mode *, int force_date);
 extern void get_reflog_message(struct strbuf *sb,
                struct reflog_walk_info *reflog_info);
 extern const char *get_reflog_ident(struct reflog_walk_info *reflog_info);
+extern timestamp_t get_reflog_timestamp(struct reflog_walk_info *reflog_info);
 extern void get_reflog_selector(struct strbuf *sb,
                struct reflog_walk_info *reflog_info,
                const struct date_mode *dmode, int force_date,
                int shorten);
 
+extern int reflog_walk_empty(struct reflog_walk_info *walk);
+
+struct commit *next_reflog_entry(struct reflog_walk_info *reflog_info);
+
 #endif
diff --git a/refs.c b/refs.c
index ba22f4acefa262cc1029bcf8360edb21c0635953..41aec6505d1309fa2efed2bd9aca0a0ef2646444 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -921,6 +921,8 @@ int ref_transaction_update(struct ref_transaction *transaction,
                return -1;
        }
 
+       flags &= REF_TRANSACTION_UPDATE_ALLOWED_FLAGS;
+
        flags |= (new_sha1 ? REF_HAVE_NEW : 0) | (old_sha1 ? REF_HAVE_OLD : 0);
 
        ref_transaction_add_update(transaction, refname, flags,
@@ -1160,7 +1162,7 @@ int ref_is_hidden(const char *refname, const char *refname_full)
                const char *match = hide_refs->items[i].string;
                const char *subject;
                int neg = 0;
-               int len;
+               const char *p;
 
                if (*match == '!') {
                        neg = 1;
@@ -1175,10 +1177,9 @@ int ref_is_hidden(const char *refname, const char *refname_full)
                }
 
                /* refname can be NULL when namespaces are used. */
-               if (!subject || !starts_with(subject, match))
-                       continue;
-               len = strlen(match);
-               if (!subject[len] || subject[len] == '/')
+               if (subject &&
+                   skip_prefix(subject, match, &p) &&
+                   (!*p || *p == '/'))
                        return !neg;
        }
        return 0;
diff --git a/refs.h b/refs.h
index 6daa78eb50ddc195c6d5bfb568d0b05ab08f7923..4d75c207e18bd6504a5613a373adf9dd8c745034 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -354,6 +354,14 @@ int refs_pack_refs(struct ref_store *refs, unsigned int flags);
 #define REF_NODEREF    0x01
 #define REF_FORCE_CREATE_REFLOG 0x40
 
+/*
+ * Flags that can be passed in to ref_transaction_update
+ */
+#define REF_TRANSACTION_UPDATE_ALLOWED_FLAGS \
+       REF_ISPRUNING |                      \
+       REF_FORCE_CREATE_REFLOG |            \
+       REF_NODEREF
+
 /*
  * Setup reflog before using. Fill in err and return -1 on failure.
  */
index 6603af944486db6d5ec560dd9c655d24c31ce9e9..f032ab2e5c5c73c691338cdaf99d5b3b3d16b674 100644 (file)
@@ -148,16 +148,14 @@ static void add_pending_object_with_path(struct rev_info *revs,
        if (revs->reflog_info && obj->type == OBJ_COMMIT) {
                struct strbuf buf = STRBUF_INIT;
                int len = interpret_branch_name(name, 0, &buf, 0);
-               int st;
 
                if (0 < len && name[len] && buf.len)
                        strbuf_addstr(&buf, name + len);
-               st = add_reflog_for_walk(revs->reflog_info,
-                                        (struct commit *)obj,
-                                        buf.buf[0] ? buf.buf: name);
+               add_reflog_for_walk(revs->reflog_info,
+                                   (struct commit *)obj,
+                                   buf.buf[0] ? buf.buf: name);
                strbuf_release(&buf);
-               if (st)
-                       return;
+               return; /* do not add the commit itself */
        }
        add_object_array_with_path(obj, name, &revs->pending, mode, path);
 }
@@ -1168,6 +1166,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
 {
        cb->all_revs = revs;
        cb->all_flags = flags;
+       revs->rev_input_given = 1;
 }
 
 void clear_ref_exclusion(struct string_list **ref_excludes_p)
@@ -2315,7 +2314,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                opt->tweak(revs, opt);
        if (revs->show_merge)
                prepare_show_merge(revs);
-       if (revs->def && !revs->pending.nr && !got_rev_arg) {
+       if (revs->def && !revs->pending.nr && !revs->rev_input_given && !got_rev_arg) {
                struct object_id oid;
                struct object *object;
                struct object_context oc;
@@ -2364,6 +2363,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 
        if (revs->reverse && revs->reflog_info)
                die("cannot combine --reverse with --walk-reflogs");
+       if (revs->reflog_info && revs->limited)
+               die("cannot combine --walk-reflogs with history-limiting options");
        if (revs->rewrite_parents && revs->children.name)
                die("cannot combine --parents and --children");
 
@@ -2963,6 +2964,18 @@ static inline int want_ancestry(const struct rev_info *revs)
        return (revs->rewrite_parents || revs->children.name);
 }
 
+/*
+ * Return a timestamp to be used for --since/--until comparisons for this
+ * commit, based on the revision options.
+ */
+static timestamp_t comparison_date(const struct rev_info *revs,
+                                  struct commit *commit)
+{
+       return revs->reflog_info ?
+               get_reflog_timestamp(revs->reflog_info) :
+               commit->date;
+}
+
 enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
 {
        if (commit->object.flags & SHOWN)
@@ -2973,8 +2986,9 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
                return commit_show;
        if (commit->object.flags & UNINTERESTING)
                return commit_ignore;
-       if (revs->min_age != -1 && (commit->date > revs->min_age))
-               return commit_ignore;
+       if (revs->min_age != -1 &&
+           comparison_date(revs, commit) > revs->min_age)
+                       return commit_ignore;
        if (revs->min_parents || (revs->max_parents >= 0)) {
                int n = commit_list_count(commit->parents);
                if ((n < revs->min_parents) ||
@@ -3107,17 +3121,19 @@ static void track_linear(struct rev_info *revs, struct commit *commit)
 
 static struct commit *get_revision_1(struct rev_info *revs)
 {
-       if (!revs->commits)
-               return NULL;
+       while (1) {
+               struct commit *commit;
 
-       do {
-               struct commit *commit = pop_commit(&revs->commits);
+               if (revs->reflog_info)
+                       commit = next_reflog_entry(revs->reflog_info);
+               else
+                       commit = pop_commit(&revs->commits);
 
-               if (revs->reflog_info) {
-                       save_parents(revs, commit);
-                       fake_reflog_parent(revs->reflog_info, commit);
+               if (!commit)
+                       return NULL;
+
+               if (revs->reflog_info)
                        commit->object.flags &= ~(ADDED | SEEN | SHOWN);
-               }
 
                /*
                 * If we haven't done the list limiting, we need to look at
@@ -3126,9 +3142,12 @@ static struct commit *get_revision_1(struct rev_info *revs)
                 */
                if (!revs->limited) {
                        if (revs->max_age != -1 &&
-                           (commit->date < revs->max_age))
+                           comparison_date(revs, commit) < revs->max_age)
                                continue;
-                       if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
+
+                       if (revs->reflog_info)
+                               try_to_simplify_commit(revs, commit);
+                       else if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
                                if (!revs->ignore_missing_links)
                                        die("Failed to traverse parents of commit %s",
                                                oid_to_hex(&commit->object.oid));
@@ -3146,8 +3165,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
                                track_linear(revs, commit);
                        return commit;
                }
-       } while (revs->commits);
-       return NULL;
+       }
 }
 
 /*
index f96e7f7f49debccac9f85e7c4f0fc06c0be16ca8..bc18487d6fff5e86dff60748c084995a77d2b588 100644 (file)
@@ -71,6 +71,13 @@ struct rev_info {
        const char *def;
        struct pathspec prune_data;
 
+       /*
+        * Whether the arguments parsed by setup_revisions() included any
+        * "input" revisions that might still have yielded an empty pending
+        * list (e.g., patterns like "--all" or "--glob").
+        */
+       int rev_input_given;
+
        /* topo-sort */
        enum rev_sort_order sort_order;
 
index 9e36151bf97d36945ca3464e719687718ee6d1c7..b5e6eb37c0eb3d6ed0cbd4fc35e879bf9fbd3db6 100644 (file)
@@ -1533,7 +1533,7 @@ static int pp_start_one(struct parallel_processes *pp)
        if (start_command(&pp->children[i].process)) {
                code = pp->start_failure(&pp->children[i].err,
                                         pp->data,
-                                        &pp->children[i].data);
+                                        pp->children[i].data);
                strbuf_addbuf(&pp->buffered_output, &pp->children[i].err);
                strbuf_reset(&pp->children[i].err);
                if (code)
@@ -1601,7 +1601,7 @@ static int pp_collect_finished(struct parallel_processes *pp)
 
                code = pp->task_finished(code,
                                         &pp->children[i].err, pp->data,
-                                        &pp->children[i].data);
+                                        pp->children[i].data);
 
                if (code)
                        result = code;
index 3010faf86398697469e903318a35421d911acb23..e0e66b987b27072da4aea6304a565ab708be91e4 100644 (file)
@@ -127,6 +127,7 @@ static GIT_PATH_FUNC(rebase_path_onto, "rebase-merge/onto")
 static GIT_PATH_FUNC(rebase_path_autostash, "rebase-merge/autostash")
 static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
 static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
+static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
 
 static inline int is_rebase_i(const struct replay_opts *opts)
 {
@@ -1438,7 +1439,11 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
        else if (!strcmp(key, "options.strategy-option")) {
                ALLOC_GROW(opts->xopts, opts->xopts_nr + 1, opts->xopts_alloc);
                opts->xopts[opts->xopts_nr++] = xstrdup(value);
-       } else
+       } else if (!strcmp(key, "options.allow-rerere-auto"))
+               opts->allow_rerere_auto =
+                       git_config_bool_or_int(key, value, &error_flag) ?
+                               RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
+       else
                return error(_("invalid key: %s"), key);
 
        if (!error_flag)
@@ -1479,6 +1484,15 @@ static int read_populate_opts(struct replay_opts *opts)
                                free(opts->gpg_sign);
                                opts->gpg_sign = xstrdup(buf.buf + 2);
                        }
+                       strbuf_reset(&buf);
+               }
+
+               if (read_oneliner(&buf, rebase_path_allow_rerere_autoupdate(), 1)) {
+                       if (!strcmp(buf.buf, "--rerere-autoupdate"))
+                               opts->allow_rerere_auto = RERERE_AUTOUPDATE;
+                       else if (!strcmp(buf.buf, "--no-rerere-autoupdate"))
+                               opts->allow_rerere_auto = RERERE_NOAUTOUPDATE;
+                       strbuf_reset(&buf);
                }
 
                if (file_exists(rebase_path_verbose()))
@@ -1742,6 +1756,10 @@ static int save_opts(struct replay_opts *opts)
                                                        "options.strategy-option",
                                                        opts->xopts[i], "^$", 0);
        }
+       if (opts->allow_rerere_auto)
+               res |= git_config_set_in_file_gently(opts_file, "options.allow-rerere-auto",
+                                                    opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
+                                                    "true" : "false");
        return res;
 }
 
index 5f069214d9060da8ceb001e6c037607c1d0096bf..2552b7902c70154282eb278ffed7e21b2d618514 100644 (file)
@@ -99,219 +99,3 @@ int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
        } while (lo < hi);
        return -lo-1;
 }
-
-/*
- * Conventional binary search loop looks like this:
- *
- *     unsigned lo, hi;
- *      do {
- *              unsigned mi = (lo + hi) / 2;
- *              int cmp = "entry pointed at by mi" minus "target";
- *              if (!cmp)
- *                      return (mi is the wanted one)
- *              if (cmp > 0)
- *                      hi = mi; "mi is larger than target"
- *              else
- *                      lo = mi+1; "mi is smaller than target"
- *      } while (lo < hi);
- *
- * The invariants are:
- *
- * - When entering the loop, lo points at a slot that is never
- *   above the target (it could be at the target), hi points at a
- *   slot that is guaranteed to be above the target (it can never
- *   be at the target).
- *
- * - We find a point 'mi' between lo and hi (mi could be the same
- *   as lo, but never can be as same as hi), and check if it hits
- *   the target.  There are three cases:
- *
- *    - if it is a hit, we are happy.
- *
- *    - if it is strictly higher than the target, we set it to hi,
- *      and repeat the search.
- *
- *    - if it is strictly lower than the target, we update lo to
- *      one slot after it, because we allow lo to be at the target.
- *
- *   If the loop exits, there is no matching entry.
- *
- * When choosing 'mi', we do not have to take the "middle" but
- * anywhere in between lo and hi, as long as lo <= mi < hi is
- * satisfied.  When we somehow know that the distance between the
- * target and lo is much shorter than the target and hi, we could
- * pick mi that is much closer to lo than the midway.
- *
- * Now, we can take advantage of the fact that SHA-1 is a good hash
- * function, and as long as there are enough entries in the table, we
- * can expect uniform distribution.  An entry that begins with for
- * example "deadbeef..." is much likely to appear much later than in
- * the midway of the table.  It can reasonably be expected to be near
- * 87% (222/256) from the top of the table.
- *
- * However, we do not want to pick "mi" too precisely.  If the entry at
- * the 87% in the above example turns out to be higher than the target
- * we are looking for, we would end up narrowing the search space down
- * only by 13%, instead of 50% we would get if we did a simple binary
- * search.  So we would want to hedge our bets by being less aggressive.
- *
- * The table at "table" holds at least "nr" entries of "elem_size"
- * bytes each.  Each entry has the SHA-1 key at "key_offset".  The
- * table is sorted by the SHA-1 key of the entries.  The caller wants
- * to find the entry with "key", and knows that the entry at "lo" is
- * not higher than the entry it is looking for, and that the entry at
- * "hi" is higher than the entry it is looking for.
- */
-int sha1_entry_pos(const void *table,
-                  size_t elem_size,
-                  size_t key_offset,
-                  unsigned lo, unsigned hi, unsigned nr,
-                  const unsigned char *key)
-{
-       const unsigned char *base = table;
-       const unsigned char *hi_key, *lo_key;
-       unsigned ofs_0;
-       static int debug_lookup = -1;
-
-       if (debug_lookup < 0)
-               debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
-
-       if (!nr || lo >= hi)
-               return -1;
-
-       if (nr == hi)
-               hi_key = NULL;
-       else
-               hi_key = base + elem_size * hi + key_offset;
-       lo_key = base + elem_size * lo + key_offset;
-
-       ofs_0 = 0;
-       do {
-               int cmp;
-               unsigned ofs, mi, range;
-               unsigned lov, hiv, kyv;
-               const unsigned char *mi_key;
-
-               range = hi - lo;
-               if (hi_key) {
-                       for (ofs = ofs_0; ofs < 20; ofs++)
-                               if (lo_key[ofs] != hi_key[ofs])
-                                       break;
-                       ofs_0 = ofs;
-                       /*
-                        * byte 0 thru (ofs-1) are the same between
-                        * lo and hi; ofs is the first byte that is
-                        * different.
-                        *
-                        * If ofs==20, then no bytes are different,
-                        * meaning we have entries with duplicate
-                        * keys. We know that we are in a solid run
-                        * of this entry (because the entries are
-                        * sorted, and our lo and hi are the same,
-                        * there can be nothing but this single key
-                        * in between). So we can stop the search.
-                        * Either one of these entries is it (and
-                        * we do not care which), or we do not have
-                        * it.
-                        *
-                        * Furthermore, we know that one of our
-                        * endpoints must be the edge of the run of
-                        * duplicates. For example, given this
-                        * sequence:
-                        *
-                        *     idx 0 1 2 3 4 5
-                        *     key A C C C C D
-                        *
-                        * If we are searching for "B", we might
-                        * hit the duplicate run at lo=1, hi=3
-                        * (e.g., by first mi=3, then mi=0). But we
-                        * can never have lo > 1, because B < C.
-                        * That is, if our key is less than the
-                        * run, we know that "lo" is the edge, but
-                        * we can say nothing of "hi". Similarly,
-                        * if our key is greater than the run, we
-                        * know that "hi" is the edge, but we can
-                        * say nothing of "lo".
-                        *
-                        * Therefore if we do not find it, we also
-                        * know where it would go if it did exist:
-                        * just on the far side of the edge that we
-                        * know about.
-                        */
-                       if (ofs == 20) {
-                               mi = lo;
-                               mi_key = base + elem_size * mi + key_offset;
-                               cmp = memcmp(mi_key, key, 20);
-                               if (!cmp)
-                                       return mi;
-                               if (cmp < 0)
-                                       return -1 - hi;
-                               else
-                                       return -1 - lo;
-                       }
-
-                       hiv = hi_key[ofs_0];
-                       if (ofs_0 < 19)
-                               hiv = (hiv << 8) | hi_key[ofs_0+1];
-               } else {
-                       hiv = 256;
-                       if (ofs_0 < 19)
-                               hiv <<= 8;
-               }
-               lov = lo_key[ofs_0];
-               kyv = key[ofs_0];
-               if (ofs_0 < 19) {
-                       lov = (lov << 8) | lo_key[ofs_0+1];
-                       kyv = (kyv << 8) | key[ofs_0+1];
-               }
-               assert(lov < hiv);
-
-               if (kyv < lov)
-                       return -1 - lo;
-               if (hiv < kyv)
-                       return -1 - hi;
-
-               /*
-                * Even if we know the target is much closer to 'hi'
-                * than 'lo', if we pick too precisely and overshoot
-                * (e.g. when we know 'mi' is closer to 'hi' than to
-                * 'lo', pick 'mi' that is higher than the target), we
-                * end up narrowing the search space by a smaller
-                * amount (i.e. the distance between 'mi' and 'hi')
-                * than what we would have (i.e. about half of 'lo'
-                * and 'hi').  Hedge our bets to pick 'mi' less
-                * aggressively, i.e. make 'mi' a bit closer to the
-                * middle than we would otherwise pick.
-                */
-               kyv = (kyv * 6 + lov + hiv) / 8;
-               if (lov < hiv - 1) {
-                       if (kyv == lov)
-                               kyv++;
-                       else if (kyv == hiv)
-                               kyv--;
-               }
-               mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
-
-               if (debug_lookup) {
-                       printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
-                       printf("ofs %u lov %x, hiv %x, kyv %x\n",
-                              ofs_0, lov, hiv, kyv);
-               }
-               if (!(lo <= mi && mi < hi))
-                       die("assertion failure lo %u mi %u hi %u %s",
-                           lo, mi, hi, sha1_to_hex(key));
-
-               mi_key = base + elem_size * mi + key_offset;
-               cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
-               if (!cmp)
-                       return mi;
-               if (cmp > 0) {
-                       hi = mi;
-                       hi_key = mi_key;
-               } else {
-                       lo = mi + 1;
-                       lo_key = mi_key + elem_size;
-               }
-       } while (lo < hi);
-       return -lo-1;
-}
index 20af2856818ed51b2afb1718a7e317133ee0d7bd..cf5314f402ce78f0d5ab2bd72ee7f334b6394e04 100644 (file)
@@ -7,10 +7,4 @@ extern int sha1_pos(const unsigned char *sha1,
                    void *table,
                    size_t nr,
                    sha1_access_fn fn);
-
-extern int sha1_entry_pos(const void *table,
-                         size_t elem_size,
-                         size_t key_offset,
-                         unsigned lo, unsigned hi, unsigned nr,
-                         const unsigned char *key);
 #endif
index b60ae15f7068c157df6407933ea7ee94a53f0640..9008d6aa7bb59944d97f8ed73d9772d9ab529187 100644 (file)
@@ -347,6 +347,7 @@ static int alt_odb_usable(struct strbuf *path, const char *normalized_objdir)
  * SHA1, an extra slash for the first level indirection, and the
  * terminating NUL.
  */
+static void read_info_alternates(const char * relative_base, int depth);
 static int link_alt_odb_entry(const char *entry, const char *relative_base,
        int depth, const char *normalized_objdir)
 {
@@ -448,7 +449,7 @@ static void link_alt_odb_entries(const char *alt, int len, int sep,
        strbuf_release(&objdirbuf);
 }
 
-void read_info_alternates(const char * relative_base, int depth)
+static void read_info_alternates(const char * relative_base, int depth)
 {
        char *map;
        size_t mapsz;
@@ -2542,8 +2543,8 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
                                error("bad packed object CRC for %s",
                                      sha1_to_hex(sha1));
                                mark_bad_packed_object(p, sha1);
-                               unuse_pack(&w_curs);
-                               return NULL;
+                               data = NULL;
+                               goto out;
                        }
                }
 
@@ -2681,6 +2682,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
        if (final_size)
                *final_size = size;
 
+out:
        unuse_pack(&w_curs);
 
        if (delta_stack != small_delta_stack)
@@ -2759,7 +2761,6 @@ off_t find_pack_entry_one(const unsigned char *sha1,
        const uint32_t *level1_ofs = p->index_data;
        const unsigned char *index = p->index_data;
        unsigned hi, lo, stride;
-       static int use_lookup = -1;
        static int debug_lookup = -1;
 
        if (debug_lookup < 0)
@@ -2789,17 +2790,7 @@ off_t find_pack_entry_one(const unsigned char *sha1,
                printf("%02x%02x%02x... lo %u hi %u nr %"PRIu32"\n",
                       sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
 
-       if (use_lookup < 0)
-               use_lookup = !!getenv("GIT_USE_LOOKUP");
-       if (use_lookup) {
-               int pos = sha1_entry_pos(index, stride, 0,
-                                        lo, hi, p->num_objects, sha1);
-               if (pos < 0)
-                       return 0;
-               return nth_packed_object_offset(p, pos);
-       }
-
-       do {
+       while (lo < hi) {
                unsigned mi = (lo + hi) / 2;
                int cmp = hashcmp(index + mi * stride, sha1);
 
@@ -2812,7 +2803,7 @@ off_t find_pack_entry_one(const unsigned char *sha1,
                        hi = mi;
                else
                        lo = mi+1;
-       } while (lo < hi);
+       }
        return 0;
 }
 
diff --git a/shell.c b/shell.c
index fe2d314593ba5ee3b985b778c2e7ebfdcd9da44c..234b2d4f16fe79e9260c8409bd4d7c964fe27e72 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -25,19 +25,6 @@ static int do_generic_cmd(const char *me, char *arg)
        return execv_git_cmd(my_argv);
 }
 
-static int do_cvs_cmd(const char *me, char *arg)
-{
-       const char *cvsserver_argv[3] = {
-               "cvsserver", "server", NULL
-       };
-
-       if (!arg || strcmp(arg, "server"))
-               die("git-cvsserver only handles server: %s", arg);
-
-       setup_path();
-       return execv_git_cmd(cvsserver_argv);
-}
-
 static int is_valid_cmd_name(const char *cmd)
 {
        /* Test command contains no . or / characters */
@@ -134,7 +121,6 @@ static struct commands {
        { "git-receive-pack", do_generic_cmd },
        { "git-upload-pack", do_generic_cmd },
        { "git-upload-archive", do_generic_cmd },
-       { "cvs", do_cvs_cmd },
        { NULL },
 };
 
index 89d22e3b0903a220fa958b8d912607828ab2a9ba..323c49ceb35cb053434248df869578e649ccadc0 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -476,6 +476,7 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
        /* Translate slopbuf to NULL, as we cannot call realloc on it */
        if (!sb->alloc)
                sb->buf = NULL;
+       errno = 0;
        r = getdelim(&sb->buf, &sb->alloc, term, fp);
 
        if (r > 0) {
index c650500c6e51d983dc45a4be3b8dad98f1dece92..806b4c87232fb2ed307f6ce64817e14feefc216f 100644 (file)
@@ -43,9 +43,8 @@ static int add_entry(int insert_at, struct string_list *list, const char *string
 
        ALLOC_GROW(list->items, list->nr+1, list->alloc);
        if (index < list->nr)
-               memmove(list->items + index + 1, list->items + index,
-                               (list->nr - index)
-                               * sizeof(struct string_list_item));
+               MOVE_ARRAY(list->items + index + 1, list->items + index,
+                          list->nr - index);
        list->items[index].string = list->strdup_strings ?
                xstrdup(string) : (char *)string;
        list->items[index].util = NULL;
@@ -77,8 +76,7 @@ void string_list_remove(struct string_list *list, const char *string,
                        free(list->items[i].util);
 
                list->nr--;
-               memmove(list->items + i, list->items + i + 1,
-                       (list->nr - i) * sizeof(struct string_list_item));
+               MOVE_ARRAY(list->items + i, list->items + i + 1, list->nr - i);
        }
 }
 
index 29bfb7ae45931686e14c91b1a47a703e7b32fe52..79ae567cbc3bfbd7574b8156aa9fcf56581cad83 100644 (file)
@@ -32,8 +32,10 @@ void string_list_clear_func(struct string_list *list, string_list_clear_func_t c
 typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
 int for_each_string_list(struct string_list *list,
                         string_list_each_func_t, void *cb_data);
-#define for_each_string_list_item(item,list) \
-       for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
+#define for_each_string_list_item(item,list)            \
+       for (item = (list)->items;                      \
+            item && item < (list)->items + (list)->nr; \
+            ++item)
 
 /*
  * Apply want to each item in list, retaining only the ones for which
index a3cfab1a9d1feb1fea3fbc9bb9dbe04a89b9c874..80571567f2a64be451fe2ac92c230f66fa04c844 100644 (file)
@@ -74,13 +74,12 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
 {
        int err;
        struct child_process *process;
-       const char *argv[] = { cmd, NULL };
 
        entry->cmd = cmd;
        process = &entry->process;
 
        child_process_init(process);
-       process->argv = argv;
+       argv_array_push(&process->args, cmd);
        process->use_shell = 1;
        process->in = -1;
        process->out = -1;
@@ -105,3 +104,107 @@ int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, co
        hashmap_add(hashmap, entry);
        return 0;
 }
+
+static int handshake_version(struct child_process *process,
+                            const char *welcome_prefix, int *versions,
+                            int *chosen_version)
+{
+       int version_scratch;
+       int i;
+       char *line;
+       const char *p;
+
+       if (!chosen_version)
+               chosen_version = &version_scratch;
+
+       if (packet_write_fmt_gently(process->in, "%s-client\n",
+                                   welcome_prefix))
+               return error("Could not write client identification");
+       for (i = 0; versions[i]; i++) {
+               if (packet_write_fmt_gently(process->in, "version=%d\n",
+                                           versions[i]))
+                       return error("Could not write requested version");
+       }
+       if (packet_flush_gently(process->in))
+               return error("Could not write flush packet");
+
+       if (!(line = packet_read_line(process->out, NULL)) ||
+           !skip_prefix(line, welcome_prefix, &p) ||
+           strcmp(p, "-server"))
+               return error("Unexpected line '%s', expected %s-server",
+                            line ? line : "<flush packet>", welcome_prefix);
+       if (!(line = packet_read_line(process->out, NULL)) ||
+           !skip_prefix(line, "version=", &p) ||
+           strtol_i(p, 10, chosen_version))
+               return error("Unexpected line '%s', expected version",
+                            line ? line : "<flush packet>");
+       if ((line = packet_read_line(process->out, NULL)))
+               return error("Unexpected line '%s', expected flush", line);
+
+       /* Check to make sure that the version received is supported */
+       for (i = 0; versions[i]; i++) {
+               if (versions[i] == *chosen_version)
+                       break;
+       }
+       if (!versions[i])
+               return error("Version %d not supported", *chosen_version);
+
+       return 0;
+}
+
+static int handshake_capabilities(struct child_process *process,
+                                 struct subprocess_capability *capabilities,
+                                 unsigned int *supported_capabilities)
+{
+       int i;
+       char *line;
+
+       for (i = 0; capabilities[i].name; i++) {
+               if (packet_write_fmt_gently(process->in, "capability=%s\n",
+                                           capabilities[i].name))
+                       return error("Could not write requested capability");
+       }
+       if (packet_flush_gently(process->in))
+               return error("Could not write flush packet");
+
+       while ((line = packet_read_line(process->out, NULL))) {
+               const char *p;
+               if (!skip_prefix(line, "capability=", &p))
+                       continue;
+
+               for (i = 0;
+                    capabilities[i].name && strcmp(p, capabilities[i].name);
+                    i++)
+                       ;
+               if (capabilities[i].name) {
+                       if (supported_capabilities)
+                               *supported_capabilities |= capabilities[i].flag;
+               } else {
+                       die("subprocess '%s' requested unsupported capability '%s'",
+                           process->argv[0], p);
+               }
+       }
+
+       return 0;
+}
+
+int subprocess_handshake(struct subprocess_entry *entry,
+                        const char *welcome_prefix,
+                        int *versions,
+                        int *chosen_version,
+                        struct subprocess_capability *capabilities,
+                        unsigned int *supported_capabilities)
+{
+       int retval;
+       struct child_process *process = &entry->process;
+
+       sigchain_push(SIGPIPE, SIG_IGN);
+
+       retval = handshake_version(process, welcome_prefix, versions,
+                                  chosen_version) ||
+                handshake_capabilities(process, capabilities,
+                                       supported_capabilities);
+
+       sigchain_pop(SIGPIPE);
+       return retval;
+}
index 96a2cca360c5660fb8e72c684dc4c25e96ba0546..caa91a9b92f2256a33d3330b1084a205bef98632 100644 (file)
@@ -6,41 +6,88 @@
 #include "run-command.h"
 
 /*
- * Generic implementation of background process infrastructure.
- * See: Documentation/technical/api-sub-process.txt
+ * The sub-process API makes it possible to run background sub-processes
+ * for the entire lifetime of a Git invocation. If Git needs to communicate
+ * with an external process multiple times, then this can reduces the process
+ * invocation overhead. Git and the sub-process communicate through stdin and
+ * stdout.
+ *
+ * The sub-processes are kept in a hashmap by command name and looked up
+ * via the subprocess_find_entry function.  If an existing instance can not
+ * be found then a new process should be created and started.  When the
+ * parent git command terminates, all sub-processes are also terminated.
+ *
+ * This API is based on the run-command API.
  */
 
  /* data structures */
 
+/* Members should not be accessed directly. */
 struct subprocess_entry {
        struct hashmap_entry ent; /* must be the first member! */
        const char *cmd;
        struct child_process process;
 };
 
+struct subprocess_capability {
+       const char *name;
+
+       /*
+        * subprocess_handshake will "|=" this value to supported_capabilities
+        * if the server reports that it supports this capability.
+        */
+       unsigned int flag;
+};
+
 /* subprocess functions */
 
+/* Function to test two subprocess hashmap entries for equality. */
 extern int cmd2process_cmp(const void *unused_cmp_data,
                           const struct subprocess_entry *e1,
                           const struct subprocess_entry *e2,
                           const void *unused_keydata);
 
+/*
+ * User-supplied function to initialize the sub-process.  This is
+ * typically used to negotiate the interface version and capabilities.
+ */
 typedef int(*subprocess_start_fn)(struct subprocess_entry *entry);
+
+/* Start a subprocess and add it to the subprocess hashmap. */
 int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd,
                subprocess_start_fn startfn);
 
+/* Kill a subprocess and remove it from the subprocess hashmap. */
 void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry);
 
+/* Find a subprocess in the subprocess hashmap. */
 struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd);
 
 /* subprocess helper functions */
 
+/* Get the underlying `struct child_process` from a subprocess. */
 static inline struct child_process *subprocess_get_child_process(
                struct subprocess_entry *entry)
 {
        return &entry->process;
 }
 
+/*
+ * Perform the version and capability negotiation as described in the "Long
+ * Running Filter Process" section of the gitattributes documentation using the
+ * given requested versions and capabilities. The "versions" and "capabilities"
+ * parameters are arrays terminated by a 0 or blank struct.
+ *
+ * This function is typically called when a subprocess is started (as part of
+ * the "startfn" passed to subprocess_start).
+ */
+int subprocess_handshake(struct subprocess_entry *entry,
+                        const char *welcome_prefix,
+                        int *versions,
+                        int *chosen_version,
+                        struct subprocess_capability *capabilities,
+                        unsigned int *supported_capabilities);
+
 /*
  * Helper function that will read packets looking for "status=<foo>"
  * key/value pairs and return the value from the last "status" packet
index 6531c5d6094e47b251e69ce741699abfee7a64df..36f45f5a5a2adedd7e598371f98f6a54880668df 100644 (file)
@@ -1015,7 +1015,8 @@ static int push_submodule(const char *path,
  * Perform a check in the submodule to see if the remote and refspec work.
  * Die if the submodule can't be pushed.
  */
-static void submodule_push_check(const char *path, const struct remote *remote,
+static void submodule_push_check(const char *path, const char *head,
+                                const struct remote *remote,
                                 const char **refspec, int refspec_nr)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -1023,6 +1024,7 @@ static void submodule_push_check(const char *path, const struct remote *remote,
 
        argv_array_push(&cp.args, "submodule--helper");
        argv_array_push(&cp.args, "push-check");
+       argv_array_push(&cp.args, head);
        argv_array_push(&cp.args, remote->name);
 
        for (i = 0; i < refspec_nr; i++)
@@ -1061,10 +1063,20 @@ int push_unpushed_submodules(struct oid_array *commits,
         * won't be propagated due to the remote being unconfigured (e.g. a URL
         * instead of a remote name).
         */
-       if (remote->origin != REMOTE_UNCONFIGURED)
+       if (remote->origin != REMOTE_UNCONFIGURED) {
+               char *head;
+               struct object_id head_oid;
+
+               head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
+               if (!head)
+                       die(_("Failed to resolve HEAD as a valid ref."));
+
                for (i = 0; i < needs_pushing.nr; i++)
                        submodule_push_check(needs_pushing.items[i].string,
-                                            remote, refspec, refspec_nr);
+                                            head, remote,
+                                            refspec, refspec_nr);
+               free(head);
+       }
 
        /* Actually push the submodules */
        for (i = 0; i < needs_pushing.nr; i++) {
index b170cbc0452ca1b5a624f5cf330d420c397cb760..03dc9d28525730cdf57c74d13cab22da59dcdfbd 100755 (executable)
@@ -17,7 +17,7 @@ sub err {
 while (<>) {
        chomp;
        /\bsed\s+-i/ and err 'sed -i is not portable';
-       /\becho\s+-n/ and err 'echo -n is not portable (please use printf)';
+       /\becho\s+-[neE]/ and err 'echo with option is not portable (please use printf)';
        /^\s*declare\s+/ and err 'arrays/declare not portable';
        /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
        /\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
index 1ebe0f750c648cd4d92983c11ace9e8a86327dd1..2b3c5092a199835ea2e84339b473a9e29778178d 100644 (file)
@@ -38,6 +38,20 @@ struct test_data {
        const char *alternative; /* output: ... or this.      */
 };
 
+/*
+ * Compatibility wrappers for OpenBSD, whose basename(3) and dirname(3)
+ * have const parameters.
+ */
+static char *posix_basename(char *path)
+{
+       return basename(path);
+}
+
+static char *posix_dirname(char *path)
+{
+       return dirname(path);
+}
+
 static int test_function(struct test_data *data, char *(*func)(char *input),
        const char *funcname)
 {
@@ -251,10 +265,10 @@ int cmd_main(int argc, const char **argv)
        }
 
        if (argc == 2 && !strcmp(argv[1], "basename"))
-               return test_function(basename_data, basename, argv[1]);
+               return test_function(basename_data, posix_basename, argv[1]);
 
        if (argc == 2 && !strcmp(argv[1], "dirname"))
-               return test_function(dirname_data, dirname, argv[1]);
+               return test_function(dirname_data, posix_dirname, argv[1]);
 
        fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
                argv[1] ? argv[1] : "(there was none)");
index ec2aa8f6879d68078caeccaff25d89178b9fb902..43679a4c640e874aed8d3a378a378b1a6080fc1c 100755 (executable)
@@ -31,6 +31,7 @@ then
                chmod 0700 ./gpghome &&
                GNUPGHOME="$(pwd)/gpghome" &&
                export GNUPGHOME &&
+               (gpgconf --kill gpg-agent 2>&1 >/dev/null || : ) &&
                gpg --homedir "${GNUPGHOME}" 2>/dev/null --import \
                        "$TEST_DIRECTORY"/lib-gpg/keyring.gpg &&
                gpg --homedir "${GNUPGHOME}" 2>/dev/null --import-ownertrust \
index c4814d248fc96e7dc0177b6987baa18853c6213c..86c1a51654fa6c1b049a313fe8d880c6a266ee62 100755 (executable)
@@ -315,18 +315,44 @@ test_expect_success 'init with separate gitdir' '
        test_path_is_dir realgitdir/refs
 '
 
-test_expect_success 'init in long base path' '
+test_lazy_prereq GETCWD_IGNORES_PERMS '
+       base=GETCWD_TEST_BASE_DIR &&
+       mkdir -p $base/dir &&
+       chmod 100 $base ||
+       error "bug in test script: cannot prepare $base"
+
+       (cd $base/dir && /bin/pwd -P)
+       status=$?
+
+       chmod 700 $base &&
+       rm -rf $base ||
+       error "bug in test script: cannot clean $base"
+       return $status
+'
+
+check_long_base_path () {
        # exceed initial buffer size of strbuf_getcwd()
        component=123456789abcdef &&
        test_when_finished "chmod 0700 $component; rm -rf $component" &&
        p31=$component/$component &&
        p127=$p31/$p31/$p31/$p31 &&
        mkdir -p $p127 &&
-       chmod 0111 $component &&
+       if test $# = 1
+       then
+               chmod $1 $component
+       fi &&
        (
                cd $p127 &&
                git init newdir
        )
+}
+
+test_expect_success 'init in long base path' '
+       check_long_base_path
+'
+
+test_expect_success GETCWD_IGNORES_PERMS 'init in long restricted base path' '
+       check_long_base_path 0111
 '
 
 test_expect_success 're-init on .git file' '
index 161f5604464d2f963e60af54eedea3f91be041fa..46f8e583c37da7d03d715ea5cb1a4ee5bbe0ca28 100755 (executable)
@@ -28,7 +28,7 @@ file_size () {
 }
 
 filter_git () {
-       rm -f rot13-filter.log &&
+       rm -f *.log &&
        git "$@"
 }
 
@@ -42,10 +42,10 @@ test_cmp_count () {
        for FILE in "$expect" "$actual"
        do
                sort "$FILE" | uniq -c |
-               sed -e "s/^ *[0-9][0-9]*[       ]*IN: /x IN: /" >"$FILE.tmp" &&
-               mv "$FILE.tmp" "$FILE" || return
+               sed -e "s/^ *[0-9][0-9]*[       ]*IN: /x IN: /" >"$FILE.tmp"
        done &&
-       test_cmp "$expect" "$actual"
+       test_cmp "$expect.tmp" "$actual.tmp" &&
+       rm "$expect.tmp" "$actual.tmp"
 }
 
 # Compare two files but exclude all `clean` invocations because Git can
@@ -56,10 +56,10 @@ test_cmp_exclude_clean () {
        actual=$2
        for FILE in "$expect" "$actual"
        do
-               grep -v "IN: clean" "$FILE" >"$FILE.tmp" &&
-               mv "$FILE.tmp" "$FILE"
+               grep -v "IN: clean" "$FILE" >"$FILE.tmp"
        done &&
-       test_cmp "$expect" "$actual"
+       test_cmp "$expect.tmp" "$actual.tmp" &&
+       rm "$expect.tmp" "$actual.tmp"
 }
 
 # Check that the contents of two files are equal and that their rot13 version
@@ -342,7 +342,7 @@ test_expect_success 'diff does not reuse worktree files that need cleaning' '
 '
 
 test_expect_success PERL 'required process filter should filter data' '
-       test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+       test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
        test_config_global filter.protocol.required true &&
        rm -rf repo &&
        mkdir repo &&
@@ -375,7 +375,7 @@ test_expect_success PERL 'required process filter should filter data' '
                        IN: clean testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
                        STOP
                EOF
-               test_cmp_count expected.log rot13-filter.log &&
+               test_cmp_count expected.log debug.log &&
 
                git commit -m "test commit 2" &&
                rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" &&
@@ -388,7 +388,7 @@ test_expect_success PERL 'required process filter should filter data' '
                        IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
                        STOP
                EOF
-               test_cmp_exclude_clean expected.log rot13-filter.log &&
+               test_cmp_exclude_clean expected.log debug.log &&
 
                filter_git checkout --quiet --no-progress empty-branch &&
                cat >expected.log <<-EOF &&
@@ -397,7 +397,7 @@ test_expect_success PERL 'required process filter should filter data' '
                        IN: clean test.r $S [OK] -- OUT: $S . [OK]
                        STOP
                EOF
-               test_cmp_exclude_clean expected.log rot13-filter.log &&
+               test_cmp_exclude_clean expected.log debug.log &&
 
                filter_git checkout --quiet --no-progress master &&
                cat >expected.log <<-EOF &&
@@ -409,7 +409,7 @@ test_expect_success PERL 'required process filter should filter data' '
                        IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK]
                        STOP
                EOF
-               test_cmp_exclude_clean expected.log rot13-filter.log &&
+               test_cmp_exclude_clean expected.log debug.log &&
 
                test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
                test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
@@ -419,7 +419,7 @@ test_expect_success PERL 'required process filter should filter data' '
 
 test_expect_success PERL 'required process filter takes precedence' '
        test_config_global filter.protocol.clean false &&
-       test_config_global filter.protocol.process "rot13-filter.pl clean" &&
+       test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
        test_config_global filter.protocol.required true &&
        rm -rf repo &&
        mkdir repo &&
@@ -439,12 +439,12 @@ test_expect_success PERL 'required process filter takes precedence' '
                        IN: clean test.r $S [OK] -- OUT: $S . [OK]
                        STOP
                EOF
-               test_cmp_count expected.log rot13-filter.log
+               test_cmp_count expected.log debug.log
        )
 '
 
 test_expect_success PERL 'required process filter should be used only for "clean" operation only' '
-       test_config_global filter.protocol.process "rot13-filter.pl clean" &&
+       test_config_global filter.protocol.process "rot13-filter.pl debug.log clean" &&
        rm -rf repo &&
        mkdir repo &&
        (
@@ -462,7 +462,7 @@ test_expect_success PERL 'required process filter should be used only for "clean
                        IN: clean test.r $S [OK] -- OUT: $S . [OK]
                        STOP
                EOF
-               test_cmp_count expected.log rot13-filter.log &&
+               test_cmp_count expected.log debug.log &&
 
                rm test.r &&
 
@@ -474,12 +474,12 @@ test_expect_success PERL 'required process filter should be used only for "clean
                        init handshake complete
                        STOP
                EOF
-               test_cmp_exclude_clean expected.log rot13-filter.log
+               test_cmp_exclude_clean expected.log debug.log
        )
 '
 
 test_expect_success PERL 'required process filter should process multiple packets' '
-       test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+       test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
        test_config_global filter.protocol.required true &&
 
        rm -rf repo &&
@@ -514,7 +514,7 @@ test_expect_success PERL 'required process filter should process multiple packet
                        IN: clean 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
                        STOP
                EOF
-               test_cmp_count expected.log rot13-filter.log &&
+               test_cmp_count expected.log debug.log &&
 
                rm -f *.file &&
 
@@ -529,7 +529,7 @@ test_expect_success PERL 'required process filter should process multiple packet
                        IN: smudge 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK]
                        STOP
                EOF
-               test_cmp_exclude_clean expected.log rot13-filter.log &&
+               test_cmp_exclude_clean expected.log debug.log &&
 
                for FILE in *.file
                do
@@ -539,7 +539,7 @@ test_expect_success PERL 'required process filter should process multiple packet
 '
 
 test_expect_success PERL 'required process filter with clean error should fail' '
-       test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+       test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
        test_config_global filter.protocol.required true &&
        rm -rf repo &&
        mkdir repo &&
@@ -558,7 +558,7 @@ test_expect_success PERL 'required process filter with clean error should fail'
 '
 
 test_expect_success PERL 'process filter should restart after unexpected write failure' '
-       test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+       test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
        rm -rf repo &&
        mkdir repo &&
        (
@@ -579,7 +579,7 @@ test_expect_success PERL 'process filter should restart after unexpected write f
                git add . &&
                rm -f *.r &&
 
-               rm -f rot13-filter.log &&
+               rm -f debug.log &&
                git checkout --quiet --no-progress . 2>git-stderr.log &&
 
                grep "smudge write error at" git-stderr.log &&
@@ -588,14 +588,14 @@ test_expect_success PERL 'process filter should restart after unexpected write f
                cat >expected.log <<-EOF &&
                        START
                        init handshake complete
-                       IN: smudge smudge-write-fail.r $SF [OK] -- OUT: $SF [WRITE FAIL]
+                       IN: smudge smudge-write-fail.r $SF [OK] -- [WRITE FAIL]
                        START
                        init handshake complete
                        IN: smudge test.r $S [OK] -- OUT: $S . [OK]
                        IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
                        STOP
                EOF
-               test_cmp_exclude_clean expected.log rot13-filter.log &&
+               test_cmp_exclude_clean expected.log debug.log &&
 
                test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
                test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
@@ -609,7 +609,7 @@ test_expect_success PERL 'process filter should restart after unexpected write f
 '
 
 test_expect_success PERL 'process filter should not be restarted if it signals an error' '
-       test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+       test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
        rm -rf repo &&
        mkdir repo &&
        (
@@ -634,12 +634,12 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
                cat >expected.log <<-EOF &&
                        START
                        init handshake complete
-                       IN: smudge error.r $SE [OK] -- OUT: 0 [ERROR]
+                       IN: smudge error.r $SE [OK] -- [ERROR]
                        IN: smudge test.r $S [OK] -- OUT: $S . [OK]
                        IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK]
                        STOP
                EOF
-               test_cmp_exclude_clean expected.log rot13-filter.log &&
+               test_cmp_exclude_clean expected.log debug.log &&
 
                test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r &&
                test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r &&
@@ -648,7 +648,7 @@ test_expect_success PERL 'process filter should not be restarted if it signals a
 '
 
 test_expect_success PERL 'process filter abort stops processing of all further files' '
-       test_config_global filter.protocol.process "rot13-filter.pl clean smudge" &&
+       test_config_global filter.protocol.process "rot13-filter.pl debug.log clean smudge" &&
        rm -rf repo &&
        mkdir repo &&
        (
@@ -673,10 +673,10 @@ test_expect_success PERL 'process filter abort stops processing of all further f
                cat >expected.log <<-EOF &&
                        START
                        init handshake complete
-                       IN: smudge abort.r $SA [OK] -- OUT: 0 [ABORT]
+                       IN: smudge abort.r $SA [OK] -- [ABORT]
                        STOP
                EOF
-               test_cmp_exclude_clean expected.log rot13-filter.log &&
+               test_cmp_exclude_clean expected.log debug.log &&
 
                test_cmp "$TEST_ROOT/test.o" test.r &&
                test_cmp "$TEST_ROOT/test2.o" test2.r &&
@@ -697,8 +697,124 @@ test_expect_success PERL 'invalid process filter must fail (and not hang!)' '
 
                cp "$TEST_ROOT/test.o" test.r &&
                test_must_fail git add . 2>git-stderr.log &&
-               grep "does not support filter protocol version" git-stderr.log
+               grep "expected git-filter-server" git-stderr.log
        )
 '
 
+test_expect_success PERL 'delayed checkout in process filter' '
+       test_config_global filter.a.process "rot13-filter.pl a.log clean smudge delay" &&
+       test_config_global filter.a.required true &&
+       test_config_global filter.b.process "rot13-filter.pl b.log clean smudge delay" &&
+       test_config_global filter.b.required true &&
+
+       rm -rf repo &&
+       mkdir repo &&
+       (
+               cd repo &&
+               git init &&
+               echo "*.a filter=a" >.gitattributes &&
+               echo "*.b filter=b" >>.gitattributes &&
+               cp "$TEST_ROOT/test.o" test.a &&
+               cp "$TEST_ROOT/test.o" test-delay10.a &&
+               cp "$TEST_ROOT/test.o" test-delay11.a &&
+               cp "$TEST_ROOT/test.o" test-delay20.a &&
+               cp "$TEST_ROOT/test.o" test-delay10.b &&
+               git add . &&
+               git commit -m "test commit"
+       ) &&
+
+       S=$(file_size "$TEST_ROOT/test.o") &&
+       cat >a.exp <<-EOF &&
+               START
+               init handshake complete
+               IN: smudge test.a $S [OK] -- OUT: $S . [OK]
+               IN: smudge test-delay10.a $S [OK] -- [DELAYED]
+               IN: smudge test-delay11.a $S [OK] -- [DELAYED]
+               IN: smudge test-delay20.a $S [OK] -- [DELAYED]
+               IN: list_available_blobs test-delay10.a test-delay11.a [OK]
+               IN: smudge test-delay10.a 0 [OK] -- OUT: $S . [OK]
+               IN: smudge test-delay11.a 0 [OK] -- OUT: $S . [OK]
+               IN: list_available_blobs test-delay20.a [OK]
+               IN: smudge test-delay20.a 0 [OK] -- OUT: $S . [OK]
+               IN: list_available_blobs [OK]
+               STOP
+       EOF
+       cat >b.exp <<-EOF &&
+               START
+               init handshake complete
+               IN: smudge test-delay10.b $S [OK] -- [DELAYED]
+               IN: list_available_blobs test-delay10.b [OK]
+               IN: smudge test-delay10.b 0 [OK] -- OUT: $S . [OK]
+               IN: list_available_blobs [OK]
+               STOP
+       EOF
+
+       rm -rf repo-cloned &&
+       filter_git clone repo repo-cloned &&
+       test_cmp_count a.exp repo-cloned/a.log &&
+       test_cmp_count b.exp repo-cloned/b.log &&
+
+       (
+               cd repo-cloned &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay11.a &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay20.a &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.b &&
+
+               rm *.a *.b &&
+               filter_git checkout . &&
+               test_cmp_count ../a.exp a.log &&
+               test_cmp_count ../b.exp b.log &&
+
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.a &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.a &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay11.a &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay20.a &&
+               test_cmp_committed_rot13 "$TEST_ROOT/test.o" test-delay10.b
+       )
+'
+
+test_expect_success PERL 'missing file in delayed checkout' '
+       test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
+       test_config_global filter.bug.required true &&
+
+       rm -rf repo &&
+       mkdir repo &&
+       (
+               cd repo &&
+               git init &&
+               echo "*.a filter=bug" >.gitattributes &&
+               cp "$TEST_ROOT/test.o" missing-delay.a
+               git add . &&
+               git commit -m "test commit"
+       ) &&
+
+       rm -rf repo-cloned &&
+       test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
+       cat git-stderr.log &&
+       grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
+'
+
+test_expect_success PERL 'invalid file in delayed checkout' '
+       test_config_global filter.bug.process "rot13-filter.pl bug.log clean smudge delay" &&
+       test_config_global filter.bug.required true &&
+
+       rm -rf repo &&
+       mkdir repo &&
+       (
+               cd repo &&
+               git init &&
+               echo "*.a filter=bug" >.gitattributes &&
+               cp "$TEST_ROOT/test.o" invalid-delay.a &&
+               cp "$TEST_ROOT/test.o" unfiltered
+               git add . &&
+               git commit -m "test commit"
+       ) &&
+
+       rm -rf repo-cloned &&
+       test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
+       grep "error: external filter .* signaled that .unfiltered. is now available although it has not been delayed earlier" git-stderr.log
+'
+
 test_done
index 617f581e56cac95191d6f1301566fa6671ecaf6f..ad685d92f8c7aa138cb29b0ef46e5d09e246f42c 100644 (file)
@@ -2,8 +2,9 @@
 # Example implementation for the Git filter protocol version 2
 # See Documentation/gitattributes.txt, section "Filter Protocol"
 #
-# The script takes the list of supported protocol capabilities as
-# arguments ("clean", "smudge", etc).
+# The first argument defines a debug log file that the script write to.
+# All remaining arguments define a list of supported protocol
+# capabilities ("clean", "smudge", etc).
 #
 # This implementation supports special test cases:
 # (1) If data with the pathname "clean-write-fail.r" is processed with
 #     operation then the filter signals that it cannot or does not want
 #     to process the file and any file after that is processed with the
 #     same command.
+# (5) If data with a pathname that is a key in the DELAY hash is
+#     requested (e.g. "test-delay10.a") then the filter responds with
+#     a "delay" status and sets the "requested" field in the DELAY hash.
+#     The filter will signal the availability of this object after
+#     "count" (field in DELAY hash) "list_available_blobs" commands.
+# (6) If data with the pathname "missing-delay.a" is processed that the
+#     filter will drop the path from the "list_available_blobs" response.
+# (7) If data with the pathname "invalid-delay.a" is processed that the
+#     filter will add the path "unfiltered" which was not delayed before
+#     to the "list_available_blobs" response.
 #
 
 use strict;
 use IO::File;
 
 my $MAX_PACKET_CONTENT_SIZE = 65516;
+my $log_file                = shift @ARGV;
 my @capabilities            = @ARGV;
 
-open my $debug, ">>", "rot13-filter.log" or die "cannot open log file: $!";
+open my $debug, ">>", $log_file or die "cannot open log file: $!";
+
+my %DELAY = (
+       'test-delay10.a' => { "requested" => 0, "count" => 1 },
+       'test-delay11.a' => { "requested" => 0, "count" => 1 },
+       'test-delay20.a' => { "requested" => 0, "count" => 2 },
+       'test-delay10.b' => { "requested" => 0, "count" => 1 },
+       'missing-delay.a' => { "requested" => 0, "count" => 1 },
+       'invalid-delay.a' => { "requested" => 0, "count" => 1 },
+);
 
 sub rot13 {
        my $str = shift;
@@ -64,7 +85,7 @@ sub packet_bin_read {
 
 sub packet_txt_read {
        my ( $res, $buf ) = packet_bin_read();
-       unless ( $buf =~ s/\n$// ) {
+       unless ( $buf eq '' or $buf =~ s/\n$// ) {
                die "A non-binary line MUST be terminated by an LF.";
        }
        return ( $res, $buf );
@@ -99,6 +120,7 @@ sub packet_flush {
 
 ( packet_txt_read() eq ( 0, "capability=clean" ) )  || die "bad capability";
 ( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability";
+( packet_txt_read() eq ( 0, "capability=delay" ) )  || die "bad capability";
 ( packet_bin_read() eq ( 1, "" ) )                  || die "bad capability end";
 
 foreach (@capabilities) {
@@ -109,88 +131,142 @@ sub packet_flush {
 $debug->flush();
 
 while (1) {
-       my ($command) = packet_txt_read() =~ /^command=(.+)$/;
+       my ( $command ) = packet_txt_read() =~ /^command=(.+)$/;
        print $debug "IN: $command";
        $debug->flush();
 
-       my ($pathname) = packet_txt_read() =~ /^pathname=(.+)$/;
-       print $debug " $pathname";
-       $debug->flush();
-
-       if ( $pathname eq "" ) {
-               die "bad pathname '$pathname'";
-       }
+       if ( $command eq "list_available_blobs" ) {
+               # Flush
+               packet_bin_read();
 
-       # Flush
-       packet_bin_read();
-
-       my $input = "";
-       {
-               binmode(STDIN);
-               my $buffer;
-               my $done = 0;
-               while ( !$done ) {
-                       ( $done, $buffer ) = packet_bin_read();
-                       $input .= $buffer;
+               foreach my $pathname ( sort keys %DELAY ) {
+                       if ( $DELAY{$pathname}{"requested"} >= 1 ) {
+                               $DELAY{$pathname}{"count"} = $DELAY{$pathname}{"count"} - 1;
+                               if ( $pathname eq "invalid-delay.a" ) {
+                                       # Send Git a pathname that was not delayed earlier
+                                       packet_txt_write("pathname=unfiltered");
+                               }
+                               if ( $pathname eq "missing-delay.a" ) {
+                                       # Do not signal Git that this file is available
+                               } elsif ( $DELAY{$pathname}{"count"} == 0 ) {
+                                       print $debug " $pathname";
+                                       packet_txt_write("pathname=$pathname");
+                               }
+                       }
                }
-               print $debug " " . length($input) . " [OK] -- ";
-               $debug->flush();
-       }
-
-       my $output;
-       if ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
-               $output = "";
-       }
-       elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
-               $output = rot13($input);
-       }
-       elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
-               $output = rot13($input);
-       }
-       else {
-               die "bad command '$command'";
-       }
 
-       print $debug "OUT: " . length($output) . " ";
-       $debug->flush();
-
-       if ( $pathname eq "error.r" ) {
-               print $debug "[ERROR]\n";
-               $debug->flush();
-               packet_txt_write("status=error");
                packet_flush();
-       }
-       elsif ( $pathname eq "abort.r" ) {
-               print $debug "[ABORT]\n";
+
+               print $debug " [OK]\n";
                $debug->flush();
-               packet_txt_write("status=abort");
+               packet_txt_write("status=success");
                packet_flush();
        }
        else {
-               packet_txt_write("status=success");
-               packet_flush();
+               my ( $pathname ) = packet_txt_read() =~ /^pathname=(.+)$/;
+               print $debug " $pathname";
+               $debug->flush();
 
-               if ( $pathname eq "${command}-write-fail.r" ) {
-                       print $debug "[WRITE FAIL]\n";
+               if ( $pathname eq "" ) {
+                       die "bad pathname '$pathname'";
+               }
+
+               # Read until flush
+               my ( $done, $buffer ) = packet_txt_read();
+               while ( $buffer ne '' ) {
+                       if ( $buffer eq "can-delay=1" ) {
+                               if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
+                                       $DELAY{$pathname}{"requested"} = 1;
+                               }
+                       } else {
+                               die "Unknown message '$buffer'";
+                       }
+
+                       ( $done, $buffer ) = packet_txt_read();
+               }
+
+               my $input = "";
+               {
+                       binmode(STDIN);
+                       my $buffer;
+                       my $done = 0;
+                       while ( !$done ) {
+                               ( $done, $buffer ) = packet_bin_read();
+                               $input .= $buffer;
+                       }
+                       print $debug " " . length($input) . " [OK] -- ";
                        $debug->flush();
-                       die "${command} write error";
                }
 
-               while ( length($output) > 0 ) {
-                       my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
-                       packet_bin_write($packet);
-                       # dots represent the number of packets
-                       print $debug ".";
-                       if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
-                               $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
+               my $output;
+               if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
+                       $output = $DELAY{$pathname}{"output"}
+               }
+               elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
+                       $output = "";
+               }
+               elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
+                       $output = rot13($input);
+               }
+               elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
+                       $output = rot13($input);
+               }
+               else {
+                       die "bad command '$command'";
+               }
+
+               if ( $pathname eq "error.r" ) {
+                       print $debug "[ERROR]\n";
+                       $debug->flush();
+                       packet_txt_write("status=error");
+                       packet_flush();
+               }
+               elsif ( $pathname eq "abort.r" ) {
+                       print $debug "[ABORT]\n";
+                       $debug->flush();
+                       packet_txt_write("status=abort");
+                       packet_flush();
+               }
+               elsif ( $command eq "smudge" and
+                       exists $DELAY{$pathname} and
+                       $DELAY{$pathname}{"requested"} == 1
+               ) {
+                       print $debug "[DELAYED]\n";
+                       $debug->flush();
+                       packet_txt_write("status=delayed");
+                       packet_flush();
+                       $DELAY{$pathname}{"requested"} = 2;
+                       $DELAY{$pathname}{"output"} = $output;
+               }
+               else {
+                       packet_txt_write("status=success");
+                       packet_flush();
+
+                       if ( $pathname eq "${command}-write-fail.r" ) {
+                               print $debug "[WRITE FAIL]\n";
+                               $debug->flush();
+                               die "${command} write error";
                        }
-                       else {
-                               $output = "";
+
+                       print $debug "OUT: " . length($output) . " ";
+                       $debug->flush();
+
+                       while ( length($output) > 0 ) {
+                               my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
+                               packet_bin_write($packet);
+                               # dots represent the number of packets
+                               print $debug ".";
+                               if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
+                                       $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
+                               }
+                               else {
+                                       $output = "";
+                               }
                        }
+                       packet_flush();
+                       print $debug " [OK]\n";
+                       $debug->flush();
+                       packet_flush();
                }
-               packet_flush();
-               print $debug " [OK]\n";
-               $debug->flush();
-               packet_flush();
        }
 }
index e3bf821694553354a0be2e93b71038bddb1d689d..7ca2e65d102afd76dc4136a9f12c53a5ba0fce25 100755 (executable)
@@ -51,7 +51,9 @@ test_expect_success \
      treeM=$(git write-tree) &&
      echo treeM $treeM &&
      git ls-tree $treeM &&
-     sum bozbar frotz nitfol >M.sum &&
+     cp bozbar bozbar.M &&
+     cp frotz frotz.M &&
+     cp nitfol nitfol.M &&
      git diff-tree $treeH $treeM'
 
 test_expect_success \
@@ -61,8 +63,9 @@ test_expect_success \
      read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >1-3.out &&
      cmp M.out 1-3.out &&
-     sum bozbar frotz nitfol >actual3.sum &&
-     cmp M.sum actual3.sum &&
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
+     test_cmp nitfol.M nitfol &&
      check_cache_at bozbar clean &&
      check_cache_at frotz clean &&
      check_cache_at nitfol clean'
@@ -79,8 +82,9 @@ test_expect_success \
      test_might_fail git diff -U0 --no-index M.out 4.out >4diff.out &&
      compare_change 4diff.out expected &&
      check_cache_at yomin clean &&
-     sum bozbar frotz nitfol >actual4.sum &&
-     cmp M.sum actual4.sum &&
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
+     test_cmp nitfol.M nitfol &&
      echo yomin >yomin1 &&
      diff yomin yomin1 &&
      rm -f yomin1'
@@ -98,8 +102,9 @@ test_expect_success \
      test_might_fail git diff -U0 --no-index M.out 5.out >5diff.out &&
      compare_change 5diff.out expected &&
      check_cache_at yomin dirty &&
-     sum bozbar frotz nitfol >actual5.sum &&
-     cmp M.sum actual5.sum &&
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
+     test_cmp nitfol.M nitfol &&
      : dirty index should have prevented -u from checking it out. &&
      echo yomin yomin >yomin1 &&
      diff yomin yomin1 &&
@@ -115,8 +120,9 @@ test_expect_success \
      git ls-files --stage >6.out &&
      test_cmp M.out 6.out &&
      check_cache_at frotz clean &&
-     sum bozbar frotz nitfol >actual3.sum &&
-     cmp M.sum actual3.sum &&
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
+     test_cmp nitfol.M nitfol &&
      echo frotz >frotz1 &&
      diff frotz frotz1 &&
      rm -f frotz1'
@@ -132,8 +138,8 @@ test_expect_success \
      git ls-files --stage >7.out &&
      test_cmp M.out 7.out &&
      check_cache_at frotz dirty &&
-     sum bozbar frotz nitfol >actual7.sum &&
-     if cmp M.sum actual7.sum; then false; else :; fi &&
+     test_cmp bozbar.M bozbar &&
+     test_cmp nitfol.M nitfol &&
      : dirty index should have prevented -u from checking it out. &&
      echo frotz frotz >frotz1 &&
      diff frotz frotz1 &&
@@ -165,8 +171,10 @@ test_expect_success \
      read_tree_u_must_succeed -m -u $treeH $treeM &&
      git ls-files --stage >10.out &&
      cmp M.out 10.out &&
-     sum bozbar frotz nitfol >actual10.sum &&
-     cmp M.sum actual10.sum'
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
+     test_cmp nitfol.M nitfol
+'
 
 test_expect_success \
     '11 - dirty path removed.' \
@@ -209,11 +217,8 @@ test_expect_success \
      git ls-files --stage >14.out &&
      test_must_fail git diff -U0 --no-index M.out 14.out >14diff.out &&
      compare_change 14diff.out expected &&
-     sum bozbar frotz >actual14.sum &&
-     grep -v nitfol M.sum > expected14.sum &&
-     cmp expected14.sum actual14.sum &&
-     sum bozbar frotz nitfol >actual14a.sum &&
-     if cmp M.sum actual14a.sum; then false; else :; fi &&
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
      check_cache_at nitfol clean &&
      echo nitfol nitfol >nitfol1 &&
      diff nitfol nitfol1 &&
@@ -231,11 +236,8 @@ test_expect_success \
      test_must_fail git diff -U0 --no-index M.out 15.out >15diff.out &&
      compare_change 15diff.out expected &&
      check_cache_at nitfol dirty &&
-     sum bozbar frotz >actual15.sum &&
-     grep -v nitfol M.sum > expected15.sum &&
-     cmp expected15.sum actual15.sum &&
-     sum bozbar frotz nitfol >actual15a.sum &&
-     if cmp M.sum actual15a.sum; then false; else :; fi &&
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
      echo nitfol nitfol nitfol >nitfol1 &&
      diff nitfol nitfol1 &&
      rm -f nitfol1'
@@ -267,8 +269,10 @@ test_expect_success \
      git ls-files --stage >18.out &&
      test_cmp M.out 18.out &&
      check_cache_at bozbar clean &&
-     sum bozbar frotz nitfol >actual18.sum &&
-     cmp M.sum actual18.sum'
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
+     test_cmp nitfol.M nitfol
+'
 
 test_expect_success \
     '19 - local change already having a good result, further modified.' \
@@ -281,11 +285,8 @@ test_expect_success \
      git ls-files --stage >19.out &&
      test_cmp M.out 19.out &&
      check_cache_at bozbar dirty &&
-     sum frotz nitfol >actual19.sum &&
-     grep -v bozbar  M.sum > expected19.sum &&
-     cmp expected19.sum actual19.sum &&
-     sum bozbar frotz nitfol >actual19a.sum &&
-     if cmp M.sum actual19a.sum; then false; else :; fi &&
+     test_cmp frotz.M frotz &&
+     test_cmp nitfol.M nitfol &&
      echo gnusto gnusto >bozbar1 &&
      diff bozbar bozbar1 &&
      rm -f bozbar1'
@@ -300,8 +301,10 @@ test_expect_success \
      git ls-files --stage >20.out &&
      test_cmp M.out 20.out &&
      check_cache_at bozbar clean &&
-     sum bozbar frotz nitfol >actual20.sum &&
-     cmp M.sum actual20.sum'
+     test_cmp bozbar.M bozbar &&
+     test_cmp frotz.M frotz &&
+     test_cmp nitfol.M nitfol
+'
 
 test_expect_success \
     '21 - no local change, dirty cache.' \
index b9cb76654bd6cf0b7a910ec4c935c38a5f3bf9bc..6ac7734d79be21a82feeadff10064bb4ca7ad47b 100755 (executable)
@@ -171,14 +171,4 @@ test_expect_success 'reflog exists works' '
        ! git reflog exists refs/heads/nonexistent
 '
 
-# The behavior with two reflogs is buggy and the output is in flux; for now
-# we're just checking that the program works at all without segfaulting.
-test_expect_success 'showing multiple reflogs works' '
-       git log -g HEAD HEAD >actual
-'
-
-test_expect_success 'showing multiple reflogs with an old date' '
-       git log -g HEAD@{1979-01-01} HEAD >actual
-'
-
 test_done
diff --git a/t/t1414-reflog-walk.sh b/t/t1414-reflog-walk.sh
new file mode 100755 (executable)
index 0000000..feb1efd
--- /dev/null
@@ -0,0 +1,135 @@
+#!/bin/sh
+
+test_description='various tests of reflog walk (log -g) behavior'
+. ./test-lib.sh
+
+test_expect_success 'set up some reflog entries' '
+       test_commit one &&
+       test_commit two &&
+       git checkout -b side HEAD^ &&
+       test_commit three &&
+       git merge --no-commit master &&
+       echo evil-merge-content >>one.t &&
+       test_tick &&
+       git commit --no-edit -a
+'
+
+do_walk () {
+       git log -g --format="%gd %gs" "$@"
+}
+
+sq="'"
+test_expect_success 'set up expected reflog' '
+       cat >expect.all <<-EOF
+       HEAD@{0} commit (merge): Merge branch ${sq}master${sq} into side
+       HEAD@{1} commit: three
+       HEAD@{2} checkout: moving from master to side
+       HEAD@{3} commit: two
+       HEAD@{4} commit (initial): one
+       EOF
+'
+
+test_expect_success 'reflog walk shows expected logs' '
+       do_walk >actual &&
+       test_cmp expect.all actual
+'
+
+test_expect_success 'reflog can limit with --no-merges' '
+       grep -v merge expect.all >expect &&
+       do_walk --no-merges >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'reflog can limit with pathspecs' '
+       grep two expect.all >expect &&
+       do_walk -- two.t >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'pathspec limiting handles merges' '
+       # we pick up:
+       #   - the initial commit of one
+       #   - the checkout back to commit one
+       #   - the evil merge which touched one
+       sed -n "1p;3p;5p" expect.all >expect &&
+       do_walk -- one.t >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--parents shows true parents' '
+       # convert newlines to spaces
+       echo $(git rev-parse HEAD HEAD^1 HEAD^2) >expect &&
+       git rev-list -g --parents -1 HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'walking multiple reflogs shows all' '
+       # We expect to see all entries for all reflogs, but interleaved by
+       # date, with order on the command line breaking ties. We
+       # can use "sort" on the separate lists to generate this,
+       # but note two tricks:
+       #
+       #   1. We use "{" as the delimiter, which lets us skip to the reflog
+       #      date specifier as our second field, and then our "-n" numeric
+       #      sort ignores the bits after the timestamp.
+       #
+       #   2. POSIX leaves undefined whether this is a stable sort or not. So
+       #      we use "-k 1" to ensure that we see HEAD before master before
+       #      side when breaking ties.
+       {
+               do_walk --date=unix HEAD &&
+               do_walk --date=unix side &&
+               do_walk --date=unix master
+       } >expect.raw &&
+       sort -t "{" -k 2nr -k 1 <expect.raw >expect &&
+       do_walk --date=unix HEAD master side >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'date-limiting does not interfere with other logs' '
+       do_walk HEAD@{1979-01-01} HEAD >actual &&
+       test_cmp expect.all actual
+'
+
+test_expect_success 'min/max age uses entry date to limit' '
+       # Flip between commits one and two so each ref update actually
+       # does something (and does not get optimized out). We know
+       # that the timestamps of those commits will be before our "min".
+
+       git update-ref -m before refs/heads/minmax one &&
+
+       test_tick &&
+       min=$test_tick &&
+       git update-ref -m min refs/heads/minmax two &&
+
+       test_tick &&
+       max=$test_tick &&
+       git update-ref -m max refs/heads/minmax one &&
+
+       test_tick &&
+       git update-ref -m after refs/heads/minmax two &&
+
+       cat >expect <<-\EOF &&
+       max
+       min
+       EOF
+       git log -g --since=$min --until=$max --format=%gs minmax >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'walk prefers reflog to ref tip' '
+       head=$(git rev-parse HEAD) &&
+       one=$(git rev-parse one) &&
+       ident="$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" &&
+       echo "$head $one $ident broken reflog entry" >>.git/logs/HEAD &&
+
+       echo $one >expect &&
+       git log -g --format=%H -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-list -g complains when there are no reflogs' '
+       test_must_fail git rev-list -g
+'
+
+test_done
index bb89e1a5db293dd886bff9df668bcaf187a58d11..4087150db1b787540abc5a5f648839efbe4d78e3 100755 (executable)
@@ -608,6 +608,22 @@ test_expect_success 'fsck errors in packed objects' '
        ! grep corrupt out
 '
 
+test_expect_success 'fsck fails on corrupt packfile' '
+       hsh=$(git commit-tree -m mycommit HEAD^{tree}) &&
+       pack=$(echo $hsh | git pack-objects .git/objects/pack/pack) &&
+
+       # Corrupt the first byte of the first object. (It contains 3 type bits,
+       # at least one of which is not zero, so setting the first byte to 0 is
+       # sufficient.)
+       chmod a+w .git/objects/pack/pack-$pack.pack &&
+       printf '\0' | dd of=.git/objects/pack/pack-$pack.pack bs=1 conv=notrunc seek=12 &&
+
+       test_when_finished "rm -f .git/objects/pack/pack-$pack.*" &&
+       remove_object $hsh &&
+       test_must_fail git fsck 2>out &&
+       test_i18ngrep "checksum mismatch" out
+'
+
 test_expect_success 'fsck finds problems in duplicate loose objects' '
        rm -rf broken-duplicate &&
        git init broken-duplicate &&
index dd37ac47c5e16bc8df39bcc14e1a2861ab556374..9d707d2a40a457abcd85fb93d9f8195946d09d01 100755 (executable)
@@ -166,10 +166,9 @@ test_expect_success 'resulting reflog can be shown by log -g' '
        oid=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
        HEAD@{0} $oid $msg
-       HEAD@{1} $oid $msg
        HEAD@{2} $oid checkout: moving from foo to baz
        EOF
-       git log -g --format="%gd %H %gs" -3 HEAD >actual &&
+       git log -g --format="%gd %H %gs" -2 HEAD >actual &&
        test_cmp expect actual
 '
 
index a428ae670369505476ab19338bf0f1da70018301..d2aec0f38b7913b27687b332029dc78be59bc182 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='git branch display tests'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_expect_success 'make commits' '
        echo content >file &&
@@ -239,4 +240,34 @@ test_expect_success 'git branch --format option' '
        test_i18ncmp expect actual
 '
 
+test_expect_success "set up color tests" '
+       echo "<RED>master<RESET>" >expect.color &&
+       echo "master" >expect.bare &&
+       color_args="--format=%(color:red)%(refname:short) --list master"
+'
+
+test_expect_success '%(color) omitted without tty' '
+       TERM=vt100 git branch $color_args >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect.bare actual
+'
+
+test_expect_success TTY '%(color) present with tty' '
+       test_terminal env TERM=vt100 git branch $color_args >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect.color actual
+'
+
+test_expect_success 'color.branch=always overrides auto-color' '
+       git -c color.branch=always branch $color_args >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect.color actual
+'
+
+test_expect_success '--color overrides auto-color' '
+       git branch --color $color_args >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect.color actual
+'
+
 test_done
index 4428b9086e8bcb383df801834d0de323f316f4fa..fcfdd197bd352a9dca10233c2ba6d2aa4a66149e 100755 (executable)
@@ -40,25 +40,6 @@ test_expect_success 'non-interactive rebase --continue works with touched file'
        git rebase --continue
 '
 
-test_expect_success 'non-interactive rebase --continue with rerere enabled' '
-       test_config rerere.enabled true &&
-       test_when_finished "test_might_fail git rebase --abort" &&
-       git reset --hard commit-new-file-F2-on-topic-branch &&
-       git checkout master &&
-       rm -fr .git/rebase-* &&
-
-       test_must_fail git rebase --onto master master topic &&
-       echo "Resolved" >F2 &&
-       git add F2 &&
-       cp F2 F2.expected &&
-       git rebase --continue &&
-
-       git reset --hard commit-new-file-F2-on-topic-branch &&
-       git checkout master &&
-       test_must_fail git rebase --onto master master topic &&
-       test_cmp F2.expected F2
-'
-
 test_expect_success 'rebase --continue can not be used with other options' '
        test_must_fail git rebase -v --continue &&
        test_must_fail git rebase --continue -v
@@ -93,25 +74,75 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
        test -f funny.was.run
 '
 
-test_expect_success 'rebase --continue remembers --rerere-autoupdate' '
+test_expect_success 'setup rerere database' '
        rm -fr .git/rebase-* &&
        git reset --hard commit-new-file-F3-on-topic-branch &&
        git checkout master &&
        test_commit "commit-new-file-F3" F3 3 &&
-       git config rerere.enabled true &&
+       test_config rerere.enabled true &&
        test_must_fail git rebase -m master topic &&
        echo "Resolved" >F2 &&
+       cp F2 expected-F2 &&
        git add F2 &&
        test_must_fail git rebase --continue &&
        echo "Resolved" >F3 &&
+       cp F3 expected-F3 &&
        git add F3 &&
        git rebase --continue &&
-       git reset --hard topic@{1} &&
-       test_must_fail git rebase -m --rerere-autoupdate master &&
-       test "$(cat F2)" = "Resolved" &&
-       test_must_fail git rebase --continue &&
-       test "$(cat F3)" = "Resolved" &&
-       git rebase --continue
+       git reset --hard topic@{1}
 '
 
+prepare () {
+       rm -fr .git/rebase-* &&
+       git reset --hard commit-new-file-F3-on-topic-branch &&
+       git checkout master &&
+       test_config rerere.enabled true
+}
+
+test_rerere_autoupdate () {
+       action=$1 &&
+       test_expect_success "rebase $action --continue remembers --rerere-autoupdate" '
+               prepare &&
+               test_must_fail git rebase $action --rerere-autoupdate master topic &&
+               test_cmp expected-F2 F2 &&
+               git diff-files --quiet &&
+               test_must_fail git rebase --continue &&
+               test_cmp expected-F3 F3 &&
+               git diff-files --quiet &&
+               git rebase --continue
+       '
+
+       test_expect_success "rebase $action --continue honors rerere.autoUpdate" '
+               prepare &&
+               test_config rerere.autoupdate true &&
+               test_must_fail git rebase $action master topic &&
+               test_cmp expected-F2 F2 &&
+               git diff-files --quiet &&
+               test_must_fail git rebase --continue &&
+               test_cmp expected-F3 F3 &&
+               git diff-files --quiet &&
+               git rebase --continue
+       '
+
+       test_expect_success "rebase $action --continue remembers --no-rerere-autoupdate" '
+               prepare &&
+               test_config rerere.autoupdate true &&
+               test_must_fail git rebase $action --no-rerere-autoupdate master topic &&
+               test_cmp expected-F2 F2 &&
+               test_must_fail git diff-files --quiet &&
+               git add F2 &&
+               test_must_fail git rebase --continue &&
+               test_cmp expected-F3 F3 &&
+               test_must_fail git diff-files --quiet &&
+               git add F3 &&
+               git rebase --continue
+       '
+}
+
+test_rerere_autoupdate
+test_rerere_autoupdate -m
+GIT_SEQUENCE_EDITOR=: && export GIT_SEQUENCE_EDITOR
+test_rerere_autoupdate -i
+test_rerere_autoupdate --preserve-merges
+
 test_done
index e6a64816efef0e53018c7a56784d1af62602e9d3..a267b2d144df4a84f18ba4907b317e757ba98f16 100755 (executable)
@@ -5,14 +5,13 @@ test_description='cherry-pick should rerere for conflicts'
 . ./test-lib.sh
 
 test_expect_success setup '
-       echo foo >foo &&
-       git add foo && test_tick && git commit -q -m 1 &&
-       echo foo-master >foo &&
-       git add foo && test_tick && git commit -q -m 2 &&
-
-       git checkout -b dev HEAD^ &&
-       echo foo-dev >foo &&
-       git add foo && test_tick && git commit -q -m 3 &&
+       test_commit foo &&
+       test_commit foo-master foo &&
+       test_commit bar-master bar &&
+
+       git checkout -b dev foo &&
+       test_commit foo-dev foo &&
+       test_commit bar-dev bar &&
        git config rerere.enabled true
 '
 
@@ -21,23 +20,80 @@ test_expect_success 'conflicting merge' '
 '
 
 test_expect_success 'fixup' '
-       echo foo-dev >foo &&
-       git add foo && test_tick && git commit -q -m 4 &&
-       git reset --hard HEAD^ &&
-       echo foo-dev >expect
+       echo foo-resolved >foo &&
+       echo bar-resolved >bar &&
+       git commit -am resolved &&
+       cp foo foo-expect &&
+       cp bar bar-expect &&
+       git reset --hard HEAD^
 '
 
-test_expect_success 'cherry-pick conflict' '
-       test_must_fail git cherry-pick master &&
-       test_cmp expect foo
+test_expect_success 'cherry-pick conflict with --rerere-autoupdate' '
+       test_must_fail git cherry-pick --rerere-autoupdate foo..bar-master &&
+       test_cmp foo-expect foo &&
+       git diff-files --quiet &&
+       test_must_fail git cherry-pick --continue &&
+       test_cmp bar-expect bar &&
+       git diff-files --quiet &&
+       git cherry-pick --continue &&
+       git reset --hard bar-dev
+'
+
+test_expect_success 'cherry-pick conflict repsects rerere.autoUpdate' '
+       test_config rerere.autoUpdate true &&
+       test_must_fail git cherry-pick foo..bar-master &&
+       test_cmp foo-expect foo &&
+       git diff-files --quiet &&
+       test_must_fail git cherry-pick --continue &&
+       test_cmp bar-expect bar &&
+       git diff-files --quiet &&
+       git cherry-pick --continue &&
+       git reset --hard bar-dev
+'
+
+test_expect_success 'cherry-pick conflict with --no-rerere-autoupdate' '
+       test_config rerere.autoUpdate true &&
+       test_must_fail git cherry-pick --no-rerere-autoupdate foo..bar-master &&
+       test_cmp foo-expect foo &&
+       test_must_fail git diff-files --quiet &&
+       git add foo &&
+       test_must_fail git cherry-pick --continue &&
+       test_cmp bar-expect bar &&
+       test_must_fail git diff-files --quiet &&
+       git add bar &&
+       git cherry-pick --continue &&
+       git reset --hard bar-dev
+'
+
+test_expect_success 'cherry-pick --continue rejects --rerere-autoupdate' '
+       test_must_fail git cherry-pick --rerere-autoupdate foo..bar-master &&
+       test_cmp foo-expect foo &&
+       git diff-files --quiet &&
+       test_must_fail git cherry-pick --continue --rerere-autoupdate >actual 2>&1 &&
+       echo "fatal: cherry-pick: --rerere-autoupdate cannot be used with --continue" >expect &&
+       test_i18ncmp expect actual &&
+       test_must_fail git cherry-pick --continue --no-rerere-autoupdate >actual 2>&1 &&
+       echo "fatal: cherry-pick: --no-rerere-autoupdate cannot be used with --continue" >expect &&
+       test_i18ncmp expect actual &&
+       git cherry-pick --abort
 '
 
-test_expect_success 'reconfigure' '
-       git config rerere.enabled false &&
-       git reset --hard
+test_expect_success 'cherry-pick --rerere-autoupdate more than once' '
+       test_must_fail git cherry-pick --rerere-autoupdate --rerere-autoupdate foo..bar-master &&
+       test_cmp foo-expect foo &&
+       git diff-files --quiet &&
+       git cherry-pick --abort &&
+       test_must_fail git cherry-pick --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate foo..bar-master &&
+       test_cmp foo-expect foo &&
+       git diff-files --quiet &&
+       git cherry-pick --abort &&
+       test_must_fail git cherry-pick --rerere-autoupdate --no-rerere-autoupdate foo..bar-master &&
+       test_must_fail git diff-files --quiet &&
+       git cherry-pick --abort
 '
 
 test_expect_success 'cherry-pick conflict without rerere' '
+       test_config rerere.enabled false &&
        test_must_fail git cherry-pick master &&
        test_must_fail test_cmp expect foo
 '
index f3a4b4a913f344ce140344ec7b70482a6d36bcbe..0aae21d6984bf0bd5c7c7de425a021da6122beee 100755 (executable)
@@ -356,6 +356,7 @@ test_expect_success POSIXPERM,SYMLINKS 'git add --chmod=+x with symlinks' '
 
 test_expect_success 'git add --chmod=[+-]x changes index with already added file' '
        rm -f foo3 xfoo3 &&
+       git reset --hard &&
        echo foo >foo3 &&
        git add foo3 &&
        git add --chmod=+x foo3 &&
index 7c4903f49713a22d7fba28a608acf07f1330110b..1130c8019b4c14975744f31f360886ffb4c3f14c 100755 (executable)
@@ -14,8 +14,10 @@ test_expect_success setup '
        test_tick &&
        git commit -m "A 4k file"
 '
+
+# OpenBSD only supports up to 255 repetitions, so repeat twice for 64*64=4096.
 test_expect_success '-G matches' '
-       git diff --name-only -G "^0{4096}$" HEAD^ >out &&
+       git diff --name-only -G "^(0{64}){64}$" HEAD^ >out &&
        test 4096-zeroes.txt = "$(cat out)"
 '
 
index d350065f25c2fb8cd61d9f12f720cf315aa3436d..4fc27c51f7322ccb49a58d369185e64278ea246b 100755 (executable)
@@ -467,21 +467,42 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
        test_cmp one expect
 '
 
-test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
+test_expect_success 'CR-LF line endings && add line && text=auto' '
        git config --unset core.whitespace &&
        printf "a\r\n" >one &&
+       cp one save-one &&
+       git add one &&
        printf "b\r\n" >>one &&
-       printf "c\r\n" >>one &&
+       cp one expect &&
+       git diff -- one >patch &&
+       mv save-one one &&
+       echo "one text=auto" >.gitattributes &&
+       git apply patch &&
+       test_cmp one expect
+'
+
+test_expect_success 'CR-LF line endings && change line && text=auto' '
+       printf "a\r\n" >one &&
        cp one save-one &&
-       printf "                 \r\n" >>one &&
        git add one &&
+       printf "b\r\n" >one &&
        cp one expect &&
-       printf "d\r\n" >>one &&
        git diff -- one >patch &&
        mv save-one one &&
-       echo d >>expect &&
+       echo "one text=auto" >.gitattributes &&
+       git apply patch &&
+       test_cmp one expect
+'
 
-       git apply --ignore-space-change --whitespace=fix patch &&
+test_expect_success 'LF in repo, CRLF in worktree && change line && text=auto' '
+       printf "a\n" >one &&
+       git add one &&
+       printf "b\r\n" >one &&
+       git diff -- one >patch &&
+       printf "a\r\n" >one &&
+       echo "one text=auto" >.gitattributes &&
+       git -c core.eol=CRLF apply patch &&
+       printf "b\r\n" >expect &&
        test_cmp one expect
 '
 
index 44807e218d7016f58bd41b89af71104a37f31a8b..73b67b4280b99e0328e201e6b69c3d88b766ea84 100755 (executable)
@@ -40,6 +40,8 @@ test_expect_success 'setup: messages' '
        dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
        dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
        feugait nulla facilisi.
+
+       Reported-by: A N Other <a.n.other@example.com>
        EOF
 
        cat >failmail <<-\EOF &&
@@ -93,7 +95,7 @@ test_expect_success setup '
        echo world >>file &&
        git add file &&
        test_tick &&
-       git commit -s -F msg &&
+       git commit -F msg &&
        git tag second &&
 
        git format-patch --stdout first >patch1 &&
@@ -124,8 +126,6 @@ test_expect_success setup '
                echo "Date: $GIT_AUTHOR_DATE" &&
                echo &&
                sed -e "1,2d" msg &&
-               echo &&
-               echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
                echo "---" &&
                git diff-tree --no-commit-id --stat -p second
        } >patch1-stgit.eml &&
@@ -144,8 +144,6 @@ test_expect_success setup '
                echo "# Parent  $_z40" &&
                cat msg &&
                echo &&
-               echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" &&
-               echo &&
                git diff-tree --no-commit-id -p second
        } >patch1-hg.eml &&
 
@@ -470,13 +468,15 @@ test_expect_success 'am --signoff adds Signed-off-by: line' '
        git reset --hard &&
        git checkout -b master2 first &&
        git am --signoff <patch2 &&
-       printf "%s\n" "$signoff" >expected &&
-       echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >>expected &&
-       git cat-file commit HEAD^ | grep "Signed-off-by:" >actual &&
-       test_cmp expected actual &&
-       echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
-       git cat-file commit HEAD | grep "Signed-off-by:" >actual &&
-       test_cmp expected actual
+       {
+               printf "third\n\nSigned-off-by: %s <%s>\n\n" \
+                       "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" &&
+               cat msg &&
+               printf "Signed-off-by: %s <%s>\n\n" \
+                       "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL"
+       } >expected-log &&
+       git log --pretty=%B -2 HEAD >actual &&
+       test_cmp expected-log actual
 '
 
 test_expect_success 'am stays in branch' '
@@ -486,17 +486,60 @@ test_expect_success 'am stays in branch' '
 '
 
 test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
-       git format-patch --stdout HEAD^ >patch3 &&
-       sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," patch3 >patch4 &&
-       rm -fr .git/rebase-apply &&
-       git reset --hard &&
-       git checkout HEAD^ &&
-       git am --signoff patch4 &&
-       git cat-file commit HEAD >actual &&
-       test $(grep -c "^Signed-off-by:" actual) -eq 1
+       git format-patch --stdout first >patch3 &&
+       git reset --hard first &&
+       git am --signoff <patch3 &&
+       git log --pretty=%B -2 HEAD >actual &&
+       test_cmp expected-log actual
+'
+
+test_expect_success 'am --signoff adds Signed-off-by: if another author is preset' '
+       NAME="A N Other" &&
+       EMAIL="a.n.other@example.com" &&
+       {
+               printf "third\n\nSigned-off-by: %s <%s>\nSigned-off-by: %s <%s>\n\n" \
+                       "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \
+                       "$NAME" "$EMAIL" &&
+               cat msg &&
+               printf "Signed-off-by: %s <%s>\nSigned-off-by: %s <%s>\n\n" \
+                       "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \
+                       "$NAME" "$EMAIL"
+       } >expected-log &&
+       git reset --hard first &&
+       GIT_COMMITTER_NAME="$NAME" GIT_COMMITTER_EMAIL="$EMAIL" \
+               git am --signoff <patch3 &&
+       git log --pretty=%B -2 HEAD >actual &&
+       test_cmp expected-log actual
+'
+
+test_expect_success 'am --signoff duplicates Signed-off-by: if it is not the last one' '
+       NAME="A N Other" &&
+       EMAIL="a.n.other@example.com" &&
+       {
+               printf "third\n\nSigned-off-by: %s <%s>\n\
+Signed-off-by: %s <%s>\nSigned-off-by: %s <%s>\n\n" \
+                       "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \
+                       "$NAME" "$EMAIL" \
+                       "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" &&
+               cat msg &&
+               printf "Signed-off-by: %s <%s>\nSigned-off-by: %s <%s>\n\
+Signed-off-by: %s <%s>\n\n" \
+                       "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL" \
+                       "$NAME" "$EMAIL" \
+                       "$GIT_COMMITTER_NAME" "$GIT_COMMITTER_EMAIL"
+       } >expected-log &&
+       git format-patch --stdout first >patch3 &&
+       git reset --hard first &&
+       git am --signoff <patch3 &&
+       git log --pretty=%B -2 HEAD >actual &&
+       test_cmp expected-log actual
 '
 
 test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
+       git format-patch --stdout HEAD^ >tmp &&
+       sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," tmp >patch4 &&
+       git reset --hard HEAD^ &&
+       git am <patch4 &&
        git rev-parse HEAD >expected &&
        git rev-parse master2 >actual &&
        test_cmp expected actual
index 3f3531f0a49be39e29b20a36a55f0d27f04b4fc8..36d120c969f6c116c2bed19c8bb0c027919d66a9 100755 (executable)
@@ -1523,6 +1523,12 @@ test_expect_success 'log diagnoses bogus HEAD' '
        test_i18ngrep broken stderr
 '
 
+test_expect_success 'log does not default to HEAD when rev input is given' '
+       >expect &&
+       git log --branches=does-not-exist >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'set up --source tests' '
        git checkout --orphan source-a &&
        test_commit one &&
index b972296f0634144defe5a7a083c5607893a7ca10..60f040cab8ad5661b5b9b690798abba467748dd6 100755 (executable)
@@ -7,11 +7,6 @@ test_description='Test for "git log --decorate" colors'
 
 . ./test-lib.sh
 
-get_color ()
-{
-       git config --get-color no.such.slot "$1"
-}
-
 test_expect_success setup '
        git config diff.color.commit yellow &&
        git config color.decorate.branch green &&
@@ -20,14 +15,14 @@ test_expect_success setup '
        git config color.decorate.stash magenta &&
        git config color.decorate.HEAD cyan &&
 
-       c_reset=$(get_color reset) &&
+       c_reset="<RESET>" &&
 
-       c_commit=$(get_color yellow) &&
-       c_branch=$(get_color green) &&
-       c_remoteBranch=$(get_color red) &&
-       c_tag=$(get_color "reverse bold yellow") &&
-       c_stash=$(get_color magenta) &&
-       c_HEAD=$(get_color cyan) &&
+       c_commit="<YELLOW>" &&
+       c_branch="<GREEN>" &&
+       c_remoteBranch="<RED>" &&
+       c_tag="<BOLD;REVERSE;YELLOW>" &&
+       c_stash="<MAGENTA>" &&
+       c_HEAD="<CYAN>" &&
 
        test_commit A &&
        git clone . other &&
@@ -59,7 +54,8 @@ EOF
 # to this test since it does not contain any decoration, hence --first-parent
 test_expect_success 'Commit Decorations Colored Correctly' '
        git log --first-parent --abbrev=10 --all --decorate --oneline --color=always |
-       sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" >out &&
+       sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" |
+       test_decode_color >out &&
        test_cmp expected out
 '
 
index b04d955bfa8229a9c864ad53d1f9f1ec06260637..e9aa97117ab41beab708f4ded34628b015021e4e 100755 (executable)
@@ -7,11 +7,15 @@ test_description='git archive attribute tests'
 SUBSTFORMAT='%H (%h)%n'
 
 test_expect_exists() {
-       test_expect_success " $1 exists" "test -e $1"
+       test_expect_${2:-success} " $1 exists" "test -e $1"
 }
 
 test_expect_missing() {
-       test_expect_success " $1 does not exist" "test ! -e $1"
+       test_expect_${2:-success} " $1 does not exist" "test ! -e $1"
+}
+
+extract_tar_to_dir () {
+       (mkdir "$1" && cd "$1" && "$TAR" xf -) <"$1.tar"
 }
 
 test_expect_success 'setup' '
@@ -21,12 +25,19 @@ test_expect_success 'setup' '
 
        echo ignored by tree >ignored-by-tree &&
        echo ignored-by-tree export-ignore >.gitattributes &&
-       git add ignored-by-tree .gitattributes &&
+       mkdir ignored-by-tree.d &&
+       >ignored-by-tree.d/file &&
+       echo ignored-by-tree.d export-ignore >>.gitattributes &&
+       git add ignored-by-tree ignored-by-tree.d .gitattributes &&
 
        echo ignored by worktree >ignored-by-worktree &&
        echo ignored-by-worktree export-ignore >.gitattributes &&
        git add ignored-by-worktree &&
 
+       mkdir excluded-by-pathspec.d &&
+       >excluded-by-pathspec.d/file &&
+       git add excluded-by-pathspec.d &&
+
        printf "A\$Format:%s\$O" "$SUBSTFORMAT" >nosubstfile &&
        printf "A\$Format:%s\$O" "$SUBSTFORMAT" >substfile1 &&
        printf "A not substituted O" >substfile2 &&
@@ -46,7 +57,37 @@ test_expect_success 'git archive' '
 
 test_expect_missing    archive/ignored
 test_expect_missing    archive/ignored-by-tree
+test_expect_missing    archive/ignored-by-tree.d
+test_expect_missing    archive/ignored-by-tree.d/file
 test_expect_exists     archive/ignored-by-worktree
+test_expect_exists     archive/excluded-by-pathspec.d
+test_expect_exists     archive/excluded-by-pathspec.d/file
+
+test_expect_success 'git archive with pathspec' '
+       git archive HEAD ":!excluded-by-pathspec.d" >archive-pathspec.tar &&
+       extract_tar_to_dir archive-pathspec
+'
+
+test_expect_missing    archive-pathspec/ignored
+test_expect_missing    archive-pathspec/ignored-by-tree
+test_expect_missing    archive-pathspec/ignored-by-tree.d
+test_expect_missing    archive-pathspec/ignored-by-tree.d/file
+test_expect_exists     archive-pathspec/ignored-by-worktree
+test_expect_missing    archive-pathspec/excluded-by-pathspec.d
+test_expect_missing    archive-pathspec/excluded-by-pathspec.d/file
+
+test_expect_success 'git archive with wildcard pathspec' '
+       git archive HEAD ":!excluded-by-p*" >archive-pathspec-wildcard.tar &&
+       extract_tar_to_dir archive-pathspec-wildcard
+'
+
+test_expect_missing    archive-pathspec-wildcard/ignored
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree.d
+test_expect_missing    archive-pathspec-wildcard/ignored-by-tree.d/file
+test_expect_exists     archive-pathspec-wildcard/ignored-by-worktree
+test_expect_missing    archive-pathspec-wildcard/excluded-by-pathspec.d
+test_expect_missing    archive-pathspec-wildcard/excluded-by-pathspec.d/file
 
 test_expect_success 'git archive with worktree attributes' '
        git archive --worktree-attributes HEAD >worktree.tar &&
index 6667d159ab0950f10a3271f1cb81a63953214f17..bda6d7d7e9e835213f9b1323407222850db47ad6 100755 (executable)
@@ -76,7 +76,7 @@ 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/
 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 f6207f42b5a4ed382b819cb81631cd0acab61463..ced44355cab99fc4f5fd9768daf10f5a2c1b21c7 100755 (executable)
@@ -108,14 +108,14 @@ test_expect_success 'archive empty subtree with no pathspec' '
        git archive --format=tar $root_tree >subtree-all.tar &&
        make_dir extract &&
        "$TAR" xf subtree-all.tar -C extract &&
-       check_dir extract sub
+       check_dir extract
 '
 
 test_expect_success 'archive empty subtree by direct pathspec' '
        git archive --format=tar $root_tree -- sub >subtree-path.tar &&
        make_dir extract &&
        "$TAR" xf subtree-path.tar -C extract &&
-       check_dir extract sub
+       check_dir extract
 '
 
 ZIPINFO=zipinfo
index 82c33b88e710b5fb6163a7bdcd9ecb783633aa83..08c210f03586ab8cd26d69cb0761180ef5588f58 100755 (executable)
@@ -68,7 +68,7 @@ test_expect_success 'setup: two scripts for reading pull requests' '
        cat <<-\EOT >read-request.sed &&
        #!/bin/sed -nf
        # Note that a request could ask for "tag $tagname"
-       / in the git repository at:$/!d
+       / in the Git repository at:$/!d
        n
        /^$/ n
        s/ tag \([^ ]*\)$/ tag--\1/
@@ -192,7 +192,7 @@ test_expect_success 'pull request format' '
 
          SUBJECT (DATE)
 
-       are available in the git repository at:
+       are available in the Git repository at:
 
          URL BRANCH
 
index 9c5a8766ab0af40382b83cf3340b13a32f7f0c8e..156ae9e9d335029e63430a6cc7d0ec2fd0d68dd3 100755 (executable)
@@ -56,20 +56,11 @@ test_expect_success 'create batch-check test vectors' '
        EOF
 '
 
-test_expect_success 'lookup in duplicated pack (binary search)' '
+test_expect_success 'lookup in duplicated pack' '
        git cat-file --batch-check <input >actual &&
        test_cmp expect actual
 '
 
-test_expect_success 'lookup in duplicated pack (GIT_USE_LOOKUP)' '
-       (
-               GIT_USE_LOOKUP=1 &&
-               export GIT_USE_LOOKUP &&
-               git cat-file --batch-check <input >actual
-       ) &&
-       test_cmp expect actual
-'
-
 test_expect_success 'index-pack can reject packs with duplicates' '
        clear_packs &&
        create_pack dups.pack 2 &&
index 162baf101f340885619e0a1e67b0b14cf37a2347..42251f7f3af0865a31fd38722fbf7433d81fe3f0 100755 (executable)
@@ -193,7 +193,7 @@ test_expect_success "recurseSubmodules=true propagates into submodules" '
        add_upstream_commit &&
        (
                cd downstream &&
-               git config fetch.recurseSubmodules true
+               git config fetch.recurseSubmodules true &&
                git fetch >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
@@ -218,7 +218,7 @@ test_expect_success "--no-recurse-submodules overrides config setting" '
        add_upstream_commit &&
        (
                cd downstream &&
-               git config fetch.recurseSubmodules true
+               git config fetch.recurseSubmodules true &&
                git fetch --no-recurse-submodules >../actual.out 2>../actual.err
        ) &&
        ! test -s actual.out &&
@@ -232,7 +232,7 @@ test_expect_success "Recursion doesn't happen when no new commits are fetched in
                        cd submodule &&
                        git config --unset fetch.recurseSubmodules
                ) &&
-               git config --unset fetch.recurseSubmodules
+               git config --unset fetch.recurseSubmodules &&
                git fetch >../actual.out 2>../actual.err
        ) &&
        ! test -s actual.out &&
@@ -312,7 +312,7 @@ test_expect_success "Recursion picks up all submodules when necessary" '
                ) &&
                head1=$(git rev-parse --short HEAD^) &&
                git add subdir/deepsubmodule &&
-               git commit -m "new deepsubmodule"
+               git commit -m "new deepsubmodule" &&
                head2=$(git rev-parse --short HEAD) &&
                echo "Fetching submodule submodule" > ../expect.err.sub &&
                echo "From $pwd/submodule" >> ../expect.err.sub &&
index beff65b8ace505d78c3997a737711ee7fc6383b5..0f84a53146f3bac1bded4d2b11212b8ddef1bf2c 100755 (executable)
@@ -533,7 +533,8 @@ test_expect_success 'push propagating refspec to a submodule' '
        # Fails when refspec includes an object id
        test_must_fail git -C work push --recurse-submodules=on-demand origin \
                "$(git -C work rev-parse branch2):refs/heads/branch2" &&
-       # Fails when refspec includes 'HEAD' as it is unsupported at this time
+       # Fails when refspec includes HEAD and parent and submodule do not
+       # have the same named branch checked out
        test_must_fail git -C work push --recurse-submodules=on-demand origin \
                HEAD:refs/heads/branch2 &&
 
@@ -548,4 +549,26 @@ test_expect_success 'push propagating refspec to a submodule' '
        test_cmp expected_pub actual_pub
 '
 
+test_expect_success 'push propagating HEAD refspec to a submodule' '
+       git -C work/gar/bage checkout branch2 &&
+       > work/gar/bage/junk12 &&
+       git -C work/gar/bage add junk12 &&
+       git -C work/gar/bage commit -m "Twelfth junk" &&
+
+       git -C work checkout branch2 &&
+       git -C work add gar/bage &&
+       git -C work commit -m "updating gar/bage in branch2" &&
+
+       # Passes since the superproject and submodules HEAD are both on branch2
+       git -C work push --recurse-submodules=on-demand origin \
+               HEAD:refs/heads/branch2 &&
+
+       git -C submodule.git rev-parse branch2 >actual_submodule &&
+       git -C pub.git rev-parse branch2 >actual_pub &&
+       git -C work/gar/bage rev-parse branch2 >expected_submodule &&
+       git -C work rev-parse branch2 >expected_pub &&
+       test_cmp expected_submodule actual_submodule &&
+       test_cmp expected_pub actual_pub
+'
+
 test_done
index 534903bbd2972301965a3d7eed6fa8e380eeeb86..a6614080388353c828fdd3894c093ef03f71511d 100755 (executable)
@@ -236,17 +236,31 @@ test_sequence "--bisect"
 #
 #
 
-test_expect_success '--bisect can default to good/bad refs' '
+test_expect_success 'set up fake --bisect refs' '
        git update-ref refs/bisect/bad c3 &&
        good=$(git rev-parse b1) &&
        git update-ref refs/bisect/good-$good $good &&
        good=$(git rev-parse c1) &&
-       git update-ref refs/bisect/good-$good $good &&
+       git update-ref refs/bisect/good-$good $good
+'
 
+test_expect_success 'rev-list --bisect can default to good/bad refs' '
        # the only thing between c3 and c1 is c2
        git rev-parse c2 >expect &&
        git rev-list --bisect >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'rev-parse --bisect can default to good/bad refs' '
+       git rev-parse c3 ^b1 ^c1 >expect &&
+       git rev-parse --bisect >actual &&
+
+       # output order depends on the refnames, which in turn depends on
+       # the exact sha1s. We just want to make sure we have the same set
+       # of lines in any order.
+       sort <expect >expect.sorted &&
+       sort <actual >actual.sorted &&
+       test_cmp expect.sorted actual.sorted
+'
+
 test_done
index a1dcdb81d789cfa2d0e8fcc178311a807a608771..b326d550f3e281e4b19ab669b95f08e8c63bf555 100755 (executable)
@@ -59,10 +59,14 @@ test_format () {
 }
 
 # Feed to --format to provide predictable colored sequences.
+BASIC_COLOR='%Credfoo%Creset'
+COLOR='%C(red)foo%C(reset)'
 AUTO_COLOR='%C(auto,red)foo%C(auto,reset)'
+ALWAYS_COLOR='%C(always,red)foo%C(always,reset)'
 has_color () {
-       printf '\033[31mfoo\033[m\n' >expect &&
-       test_cmp expect "$1"
+       test_decode_color <"$1" >decoded &&
+       echo "<RED>foo<RESET>" >expect &&
+       test_cmp expect decoded
 }
 
 has_no_color () {
@@ -170,62 +174,84 @@ $added
 
 EOF
 
-test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<EOF
-commit $head2
-\e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
-commit $head1
-\e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
-EOF
-
-test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<EOF
-commit $head2
-\e[1;31;43mfoo\e[m
-commit $head1
-\e[1;31;43mfoo\e[m
-EOF
-
-test_expect_success '%C(auto,...) does not enable color by default' '
-       git log --format=$AUTO_COLOR -1 >actual &&
-       has_no_color actual
+test_expect_success 'basic colors' '
+       cat >expect <<-EOF &&
+       commit $head2
+       <RED>foo<GREEN>bar<BLUE>baz<RESET>xyzzy
+       EOF
+       format="%Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy" &&
+       git rev-list --color --format="$format" -1 master >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success '%C(auto,...) enables colors for color.diff' '
-       git -c color.diff=always log --format=$AUTO_COLOR -1 >actual &&
-       has_color actual
+test_expect_success 'advanced colors' '
+       cat >expect <<-EOF &&
+       commit $head2
+       <BOLD;RED;BYELLOW>foo<RESET>
+       EOF
+       format="%C(red yellow bold)foo%C(reset)" &&
+       git rev-list --color --format="$format" -1 master >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect actual
 '
 
-test_expect_success '%C(auto,...) enables colors for color.ui' '
-       git -c color.ui=always log --format=$AUTO_COLOR -1 >actual &&
-       has_color actual
-'
+for spec in \
+       "%Cred:$BASIC_COLOR" \
+       "%C(...):$COLOR" \
+       "%C(auto,...):$AUTO_COLOR"
+do
+       desc=${spec%%:*}
+       color=${spec#*:}
+       test_expect_success "$desc does not enable color by default" '
+               git log --format=$color -1 >actual &&
+               has_no_color actual
+       '
 
-test_expect_success '%C(auto,...) respects --color' '
-       git log --format=$AUTO_COLOR -1 --color >actual &&
-       has_color actual
-'
+       test_expect_success "$desc enables colors for color.diff" '
+               git -c color.diff=always log --format=$color -1 >actual &&
+               has_color actual
+       '
 
-test_expect_success '%C(auto,...) respects --no-color' '
-       git -c color.ui=always log --format=$AUTO_COLOR -1 --no-color >actual &&
-       has_no_color actual
-'
+       test_expect_success "$desc enables colors for color.ui" '
+               git -c color.ui=always log --format=$color -1 >actual &&
+               has_color actual
+       '
 
-test_expect_success TTY '%C(auto,...) respects --color=auto (stdout is tty)' '
-       test_terminal env TERM=vt100 \
-               git log --format=$AUTO_COLOR -1 --color=auto >actual &&
-       has_color actual
-'
+       test_expect_success "$desc respects --color" '
+               git log --format=$color -1 --color >actual &&
+               has_color actual
+       '
 
-test_expect_success '%C(auto,...) respects --color=auto (stdout not tty)' '
-       (
-               TERM=vt100 && export TERM &&
-               git log --format=$AUTO_COLOR -1 --color=auto >actual &&
+       test_expect_success "$desc respects --no-color" '
+               git -c color.ui=always log --format=$color -1 --no-color >actual &&
                has_no_color actual
-       )
+       '
+
+       test_expect_success TTY "$desc respects --color=auto (stdout is tty)" '
+               test_terminal env TERM=vt100 \
+                       git log --format=$color -1 --color=auto >actual &&
+               has_color actual
+       '
+
+       test_expect_success "$desc respects --color=auto (stdout not tty)" '
+               (
+                       TERM=vt100 && export TERM &&
+                       git log --format=$color -1 --color=auto >actual &&
+                       has_no_color actual
+               )
+       '
+done
+
+test_expect_success '%C(always,...) enables color even without tty' '
+       git log --format=$ALWAYS_COLOR -1 >actual &&
+       has_color actual
 '
 
 test_expect_success '%C(auto) respects --color' '
-       git log --color --format="%C(auto)%H" -1 >actual &&
-       printf "\\033[33m%s\\033[m\\n" $(git rev-parse HEAD) >expect &&
+       git log --color --format="%C(auto)%H" -1 >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       echo "<YELLOW>$(git rev-parse HEAD)<RESET>" >expect &&
        test_cmp expect actual
 '
 
@@ -235,6 +261,17 @@ test_expect_success '%C(auto) respects --no-color' '
        test_cmp expect actual
 '
 
+test_expect_success 'rev-list %C(auto,...) respects --color' '
+       git rev-list --color --format="%C(auto,green)foo%C(auto,reset)" \
+               -1 HEAD >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       cat >expect <<-EOF &&
+       commit $(git rev-parse HEAD)
+       <GREEN>foo<RESET>
+       EOF
+       test_cmp expect actual
+'
+
 iconv -f utf-8 -t $test_encoding > commit-msg <<EOF
 Test printing of complex bodies
 
index 381f35ed16d152e7926774587c1c0a006a600ee6..d3453c583c1921a10409c16b5e2cf949edde6cd2 100755 (executable)
@@ -255,27 +255,19 @@ test_expect_success 'rev-list accumulates multiple --exclude' '
        compare rev-list "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches
 '
 
-
-# "git rev-list<ENTER>" is likely to be a bug in the calling script and may
-# deserve an error message, but do cases where set of refs programmatically
-# given using globbing and/or --stdin need to fail with the same error, or
-# are we better off reporting a success with no output?  The following few
-# tests document the current behaviour to remind us that we might want to
-# think about this issue.
-
-test_expect_failure 'rev-list may want to succeed with empty output on no input (1)' '
+test_expect_failure 'rev-list should succeed with empty output on empty stdin' '
        >expect &&
        git rev-list --stdin <expect >actual &&
        test_cmp expect actual
 '
 
-test_expect_failure 'rev-list may want to succeed with empty output on no input (2)' '
+test_expect_success 'rev-list should succeed with empty output with all refs excluded' '
        >expect &&
        git rev-list --exclude=* --all >actual &&
        test_cmp expect actual
 '
 
-test_expect_failure 'rev-list may want to succeed with empty output on no input (3)' '
+test_expect_success 'rev-list should succeed with empty output with empty --all' '
        (
                test_create_repo empty &&
                cd empty &&
@@ -285,6 +277,12 @@ test_expect_failure 'rev-list may want to succeed with empty output on no input
        )
 '
 
+test_expect_success 'rev-list should succeed with empty output with empty glob' '
+       >expect &&
+       git rev-list --glob=does-not-match-anything >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'shortlog accepts --glob/--tags/--remotes' '
 
        compare shortlog "subspace/one subspace/two" --branches=subspace &&
index aa74eb8f0d5dfb4f228b1e8c7b9dbbc0b88adb4d..25110ea55d89656ab3fd4da525d4c94bc75840ac 100755 (executable)
@@ -182,10 +182,14 @@ check_describe "test2-lightweight-*" --tags --match="test2-*"
 
 check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
 
-check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^
+check_describe "test2-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^
 
 check_describe "test2-lightweight-*" --long --tags --match="test1-*" --no-match --match="test2-*" HEAD^
 
+check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test3-*" HEAD
+
+check_describe "test1-lightweight-*" --long --tags --match="test3-*" --match="test1-*" HEAD
+
 test_expect_success 'name-rev with exact tags' '
        echo A >expect &&
        tag_object=$(git rev-parse refs/tags/A) &&
index 834a9ed168fad35258955447326e7d3d1f09b7d1..edc1bd8eabd6df02e49646930ee5eb56bf1b77f9 100755 (executable)
@@ -7,6 +7,7 @@ test_description='for-each-ref test'
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
 
 # Mon Jul 3 23:18:43 2006 +0000
 datestamp=1151968723
@@ -50,6 +51,7 @@ test_atom() {
 }
 
 test_atom head refname refs/heads/master
+test_atom head refname: refs/heads/master
 test_atom head refname:short master
 test_atom head refname:lstrip=1 heads/master
 test_atom head refname:lstrip=2 master
@@ -412,21 +414,33 @@ test_expect_success 'Check for invalid refname format' '
        test_must_fail git for-each-ref --format="%(refname:INVALID)"
 '
 
-get_color ()
-{
-       git config --get-color no.such.slot "$1"
-}
+test_expect_success 'set up color tests' '
+       cat >expected.color <<-EOF &&
+       $(git rev-parse --short refs/heads/master) <GREEN>master<RESET>
+       $(git rev-parse --short refs/remotes/origin/master) <GREEN>origin/master<RESET>
+       $(git rev-parse --short refs/tags/testtag) <GREEN>testtag<RESET>
+       $(git rev-parse --short refs/tags/two) <GREEN>two<RESET>
+       EOF
+       sed "s/<[^>]*>//g" <expected.color >expected.bare &&
+       color_format="%(objectname:short) %(color:green)%(refname:short)"
+'
 
-cat >expected <<EOF
-$(git rev-parse --short refs/heads/master) $(get_color green)master$(get_color reset)
-$(git rev-parse --short refs/remotes/origin/master) $(get_color green)origin/master$(get_color reset)
-$(git rev-parse --short refs/tags/testtag) $(get_color green)testtag$(get_color reset)
-$(git rev-parse --short refs/tags/two) $(get_color green)two$(get_color reset)
-EOF
+test_expect_success TTY '%(color) shows color with a tty' '
+       test_terminal env TERM=vt100 \
+               git for-each-ref --format="$color_format" >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expected.color actual
+'
 
-test_expect_success 'Check %(color:...) ' '
-       git for-each-ref --format="%(objectname:short) %(color:green)%(refname:short)" >actual &&
-       test_cmp expected actual
+test_expect_success '%(color) does not show color without tty' '
+       TERM=vt100 git for-each-ref --format="$color_format" >actual &&
+       test_cmp expected.bare actual
+'
+
+test_expect_success 'color.ui=always can override tty check' '
+       git -c color.ui=always for-each-ref --format="$color_format" >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expected.color actual
 '
 
 cat >expected <<\EOF
index 0ef7b94394151da9a9a6d602952e7f2fa64cf6a4..dbcd6f623c7e233b487d4d0c1dd7d71cb34059b0 100755 (executable)
@@ -9,6 +9,7 @@ Tests for operations with tags.'
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-gpg.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
 
 # creating and listing lightweight tags:
 
@@ -1887,7 +1888,7 @@ EOF"
        run_with_limited_stack git tag --contains HEAD >actual &&
        test_cmp expect actual &&
        run_with_limited_stack git tag --no-contains HEAD >actual &&
-       test_line_count ">" 10 actual
+       test_line_count "-gt" 10 actual
 '
 
 test_expect_success '--format should list tags as per format given' '
@@ -1900,6 +1901,30 @@ test_expect_success '--format should list tags as per format given' '
        test_cmp expect actual
 '
 
+test_expect_success "set up color tests" '
+       echo "<RED>v1.0<RESET>" >expect.color &&
+       echo "v1.0" >expect.bare &&
+       color_args="--format=%(color:red)%(refname:short) --list v1.0"
+'
+
+test_expect_success '%(color) omitted without tty' '
+       TERM=vt100 git tag $color_args >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect.bare actual
+'
+
+test_expect_success TTY '%(color) present with tty' '
+       test_terminal env TERM=vt100 git tag $color_args >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect.color actual
+'
+
+test_expect_success 'color.ui=always overrides auto-color' '
+       git -c color.ui=always tag $color_args >actual.raw &&
+       test_decode_color <actual.raw >actual &&
+       test_cmp expect.color actual
+'
+
 test_expect_success 'setup --merged test tags' '
        git tag mergetest-1 HEAD~2 &&
        git tag mergetest-2 HEAD~1 &&
index 20b4d83c281e2fde8812f309bdebef012c6ecb74..9128ec5acda3b4b2f568dee50169046a333c4527 100755 (executable)
@@ -134,6 +134,86 @@ test_expect_success TTY 'configuration can enable pager (from subdir)' '
        }
 '
 
+test_expect_success TTY 'git tag -l defaults to paging' '
+       rm -f paginated.out &&
+       test_terminal git tag -l &&
+       test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -l respects pager.tag' '
+       rm -f paginated.out &&
+       test_terminal git -c pager.tag=false tag -l &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -l respects --no-pager' '
+       rm -f paginated.out &&
+       test_terminal git -c pager.tag --no-pager tag -l &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag with no args defaults to paging' '
+       # no args implies -l so this should page like -l
+       rm -f paginated.out &&
+       test_terminal git tag &&
+       test -e paginated.out
+'
+
+test_expect_success TTY 'git tag with no args respects pager.tag' '
+       # no args implies -l so this should page like -l
+       rm -f paginated.out &&
+       test_terminal git -c pager.tag=false tag &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag --contains defaults to paging' '
+       # --contains implies -l so this should page like -l
+       rm -f paginated.out &&
+       test_terminal git tag --contains &&
+       test -e paginated.out
+'
+
+test_expect_success TTY 'git tag --contains respects pager.tag' '
+       # --contains implies -l so this should page like -l
+       rm -f paginated.out &&
+       test_terminal git -c pager.tag=false tag --contains &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -a defaults to not paging' '
+       test_when_finished "git tag -d newtag" &&
+       rm -f paginated.out &&
+       test_terminal git tag -am message newtag &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -a ignores pager.tag' '
+       test_when_finished "git tag -d newtag" &&
+       rm -f paginated.out &&
+       test_terminal git -c pager.tag tag -am message newtag &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag -a respects --paginate' '
+       test_when_finished "git tag -d newtag" &&
+       rm -f paginated.out &&
+       test_terminal git --paginate tag -am message newtag &&
+       test -e paginated.out
+'
+
+test_expect_success TTY 'git tag as alias ignores pager.tag with -a' '
+       test_when_finished "git tag -d newtag" &&
+       rm -f paginated.out &&
+       test_terminal git -c pager.tag -c alias.t=tag t -am message newtag &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git tag as alias respects pager.tag with -l' '
+       rm -f paginated.out &&
+       test_terminal git -c pager.tag=false -c alias.t=tag t -l &&
+       ! test -e paginated.out
+'
+
 # A colored commit log will begin with an appropriate ANSI escape
 # for the first color; the text "commit" comes later.
 colorful() {
index dcac364c5fa2a8ff7fe264a21fb9682ec934a874..e9c3335b78bb6f78c640cf496f7b055462c1e275 100755 (executable)
@@ -1289,4 +1289,10 @@ test_expect_success 'init properly sets the config' '
        test_must_fail git -C multisuper_clone config --get submodule.sub1.active
 '
 
+test_expect_success 'recursive clone respects -q' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       git clone -q --recurse-submodules multisuper multisuper_clone >actual &&
+       test_must_be_empty actual
+'
+
 test_done
index 2ebda509ac337f99c7ab5651734a5b20d4a0b6f4..80194b79f90c800e7aaeecca9cd502e3ade494b4 100755 (executable)
@@ -774,4 +774,19 @@ test_expect_success 'merge can be completed with --continue' '
        verify_parents $c0 $c1
 '
 
+write_script .git/FAKE_EDITOR <<EOF
+# kill -TERM command added below.
+EOF
+
+test_expect_success EXECKEEPSPID 'killed merge can be completed with --continue' '
+       git reset --hard c0 &&
+       ! "$SHELL_PATH" -c '\''
+         echo kill -TERM $$ >> .git/FAKE_EDITOR
+         GIT_EDITOR=.git/FAKE_EDITOR
+         export GIT_EDITOR
+         exec git merge --no-ff --edit c1'\'' &&
+       git merge --continue &&
+       verify_parents $c0 $c1
+'
+
 test_done
index f1063878205cfb9b0af8e54d997040ddb65bc27e..2a6679c2f596fb13a34786a4aa4b954e61c61113 100755 (executable)
@@ -374,6 +374,11 @@ test_expect_success 'grep -L -C' '
        test_cmp expected actual
 '
 
+test_expect_success 'grep --files-without-match --quiet' '
+       git grep --files-without-match --quiet nonexistent_string >actual &&
+       test_cmp /dev/null actual
+'
+
 cat >expected <<EOF
 file:foo mmap bar_mmap
 EOF
index 92c8e792d166ee37d5463de2bce32f61cfa570a1..ae4b579d245caf568d07d1e452f917581dbfd355 100755 (executable)
@@ -12,22 +12,25 @@ test_expect_success 'setup' '
        echo c >>file &&
        echo d >>file &&
        test_tick &&
-       git commit -a -m two
+       git commit -a -m two &&
+       ID1=$(git rev-parse HEAD^) &&
+       shortID1="^$(git rev-parse HEAD^ |cut -c 1-17)" &&
+       ID2=$(git rev-parse HEAD) &&
+       shortID2="$(git rev-parse HEAD |cut -c 1-18)"
 '
 
-cat >expect <<'EOF'
-^baf5e0b (A U Thor 2005-04-07 15:13:13 -0700 1) a
-8825379d (A U Thor 2005-04-07 15:14:13 -0700 2) b
-8825379d (A U Thor 2005-04-07 15:14:13 -0700 3) c
-8825379d (A U Thor 2005-04-07 15:14:13 -0700 4) d
+cat >expect <<EOF
+$shortID1 (A U Thor 2005-04-07 15:13:13 -0700 1) a
+$shortID2 (A U Thor 2005-04-07 15:14:13 -0700 2) b
+$shortID2 (A U Thor 2005-04-07 15:14:13 -0700 3) c
+$shortID2 (A U Thor 2005-04-07 15:14:13 -0700 4) d
 EOF
 test_expect_success 'normal blame output' '
-       git blame file >actual &&
+       git blame --abbrev=17 file >actual &&
        test_cmp expect actual
 '
 
-ID1=baf5e0b3869e0b2b2beb395a3720c7b51eac94fc
-COMMIT1='author A U Thor
+COMMIT1="author A U Thor
 author-mail <author@example.com>
 author-time 1112911993
 author-tz -0700
@@ -37,9 +40,8 @@ committer-time 1112911993
 committer-tz -0700
 summary one
 boundary
-filename file'
-ID2=8825379dfb8a1267b58e8e5bcf69eec838f685ec
-COMMIT2='author A U Thor
+filename file"
+COMMIT2="author A U Thor
 author-mail <author@example.com>
 author-time 1112912053
 author-tz -0700
@@ -48,8 +50,8 @@ committer-mail <committer@example.com>
 committer-time 1112912053
 committer-tz -0700
 summary two
-previous baf5e0b3869e0b2b2beb395a3720c7b51eac94fc file
-filename file'
+previous $ID1 file
+filename file"
 
 cat >expect <<EOF
 $ID1 1 1 1
index d8242e467eaa4f0a46cfa0a4ad431a447e58dd6c..0f86c191745d65c88c70881c6520685289bc2405 100755 (executable)
@@ -51,6 +51,11 @@ test_expect_success '--path=<path> complains without --textconv/--filters' '
        grep "path.*needs.*filters" err
 '
 
+test_expect_success '--textconv/--filters complain without path' '
+       test_must_fail git cat-file --textconv HEAD &&
+       test_must_fail git cat-file --filters HEAD
+'
+
 test_expect_success 'cat-file --textconv --batch works' '
        sha1=$(git rev-parse -q --verify HEAD:world.txt) &&
        test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" &&
index d1e4e8ad19f3d3e005e177b98e4eee16f4ef1461..f30980895c2f1b93317870e6b471d67bc99b0fd2 100755 (executable)
@@ -148,6 +148,8 @@ cat >expected-cc <<\EOF
 !two@example.com!
 !three@example.com!
 !four@example.com!
+!five@example.com!
+!six@example.com!
 EOF
 "
 
@@ -161,6 +163,8 @@ test_expect_success $PREREQ 'cc trailer with various syntax' '
        Cc: <two@example.com> # trailing comments are ignored
        Cc: <three@example.com>, <not.four@example.com> one address per line
        Cc: "Some # Body" <four@example.com> [ <also.a.comment> ]
+       Cc: five@example.com # not.six@example.com
+       Cc: six@example.com, not.seven@example.com
        EOF
        clean_fake_sendmail &&
        git send-email -1 --to=recipient@example.com \
index 432c61d246c938192a2b31f2db35f4f9b320a788..c30660d60626c886dfa5993acddaebf2d3364de9 100755 (executable)
@@ -588,4 +588,52 @@ test_expect_success 'cvs annotate' '
     test_cmp ../expect ../actual
 '
 
+#------------
+# running via git-shell
+#------------
+
+cd "$WORKDIR"
+
+test_expect_success 'create remote-cvs helper' '
+       write_script remote-cvs <<-\EOF
+       exec git shell -c "cvs server"
+       EOF
+'
+
+test_expect_success 'cvs server does not run with vanilla git-shell' '
+       (
+               cd cvswork &&
+               CVS_SERVER=$WORKDIR/remote-cvs &&
+               export CVS_SERVER &&
+               test_must_fail cvs log merge
+       )
+'
+
+test_expect_success 'configure git shell to run cvs server' '
+       mkdir "$HOME"/git-shell-commands &&
+
+       write_script "$HOME"/git-shell-commands/cvs <<-\EOF &&
+       if ! test $# = 1 && test "$1" = "server"
+       then
+               echo >&2 "git-cvsserver only handles \"server\""
+               exit 1
+       fi
+       exec git cvsserver server
+       EOF
+
+       # Should not be used, but part of the recommended setup
+       write_script "$HOME"/git-shell-commands/no-interactive-login <<-\EOF
+       echo Interactive login forbidden
+       EOF
+'
+
+test_expect_success 'cvs server can run with recommended config' '
+       (
+               cd cvswork &&
+               CVS_SERVER=$WORKDIR/remote-cvs &&
+               export CVS_SERVER &&
+               cvs log merge
+       )
+'
+
 test_done
index 50a9a1d1c49e719d0ee65435b05171747e065259..1701fe2a06057530d845b91b4fd4cce99b4521a2 100644 (file)
@@ -42,6 +42,7 @@ test_decode_color () {
                function name(n) {
                        if (n == 0) return "RESET";
                        if (n == 1) return "BOLD";
+                       if (n == 7) return "REVERSE";
                        if (n == 30) return "BLACK";
                        if (n == 31) return "RED";
                        if (n == 32) return "GREEN";
index 1b6e53f78aef40673354e5f1911d78808ae8ef4d..5fbd8d4a90b3b88cf57ca53c6b1fe99d5a957460 100644 (file)
@@ -99,7 +99,6 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e '
        my $ok = join("|", qw(
                TRACE
                DEBUG
-               USE_LOOKUP
                TEST
                .*_TEST
                PROVE
@@ -991,9 +990,6 @@ case $uname_s in
        find () {
                /usr/bin/find "$@"
        }
-       sum () {
-               md5sum "$@"
-       }
        # git sees Windows-style pwd
        pwd () {
                builtin pwd -W
index dd535bc8497e5d8202a94923fd0a68c3f46907fd..862cfce661e57e50f7c6030dd5a8eda0add0cca5 100644 (file)
@@ -380,6 +380,7 @@ static int check_updates(struct unpack_trees_options *o)
        if (should_update_submodules() && o->update && !o->dry_run)
                reload_gitmodules_file(index, &state);
 
+       enable_delayed_checkout(&state);
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
 
@@ -394,6 +395,7 @@ static int check_updates(struct unpack_trees_options *o)
                        }
                }
        }
+       errs |= finish_delayed_checkout(&state);
        stop_progress(&progress);
        if (o->update)
                git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
index 2c1502f719a17abaef6c5c56439c560994b45d37..d31466722091c600bcb42420f5e4f38e6763b0fd 100644 (file)
@@ -38,7 +38,7 @@ IPATTERN("fortran",
         "|//|\\*\\*|::|[/<>=]="),
 IPATTERN("fountain", "^((\\.[^.]|(int|ext|est|int\\.?/ext|i/e)[. ]).*)$",
         "[^ \t-]+"),
-PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
+PATTERNS("html", "^[ \t]*(<[Hh][1-6]([ \t].*)?>.*)$",
         "[^<>= \t]+"),
 PATTERNS("java",
         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
index 5a89db30e3ffcb64ee25597fe014592929e07abb..3fd047a8b82b1c2a6b32ec0a5ded3d67913b5be0 100644 (file)
@@ -6,7 +6,6 @@
 #include "cache.h"
 #include "quote.h"
 #include "fast_export.h"
-#include "repo_tree.h"
 #include "strbuf.h"
 #include "svndiff.h"
 #include "sliding_window.h"
@@ -210,7 +209,7 @@ static long apply_delta(off_t len, struct line_buffer *input,
                        die("invalid cat-blob response: %s", response);
                check_preimage_overflow(preimage.max_off, 1);
        }
-       if (old_mode == REPO_MODE_LNK) {
+       if (old_mode == S_IFLNK) {
                strbuf_addstr(&preimage.buf, "link ");
                check_preimage_overflow(preimage.max_off, strlen("link "));
                preimage.max_off += strlen("link ");
@@ -244,7 +243,7 @@ void fast_export_buf_to_data(const struct strbuf *data)
 void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input)
 {
        assert(len >= 0);
-       if (mode == REPO_MODE_LNK) {
+       if (mode == S_IFLNK) {
                /* svn symlink blobs start with "link " */
                if (len < 5)
                        die("invalid dump: symlink too short for \"link\" prefix");
@@ -312,6 +311,40 @@ int fast_export_ls(const char *path, uint32_t *mode, struct strbuf *dataref)
        return parse_ls_response(get_response_line(), mode, dataref);
 }
 
+const char *fast_export_read_path(const char *path, uint32_t *mode_out)
+{
+       int err;
+       static struct strbuf buf = STRBUF_INIT;
+
+       strbuf_reset(&buf);
+       err = fast_export_ls(path, mode_out, &buf);
+       if (err) {
+               if (errno != ENOENT)
+                       die_errno("BUG: unexpected fast_export_ls error");
+               /* Treat missing paths as directories. */
+               *mode_out = S_IFDIR;
+               return NULL;
+       }
+       return buf.buf;
+}
+
+void fast_export_copy(uint32_t revision, const char *src, const char *dst)
+{
+       int err;
+       uint32_t mode;
+       static struct strbuf data = STRBUF_INIT;
+
+       strbuf_reset(&data);
+       err = fast_export_ls_rev(revision, src, &mode, &data);
+       if (err) {
+               if (errno != ENOENT)
+                       die_errno("BUG: unexpected fast_export_ls_rev error");
+               fast_export_delete(dst);
+               return;
+       }
+       fast_export_modify(dst, mode, data.buf);
+}
+
 void fast_export_blob_delta(uint32_t mode,
                                uint32_t old_mode, const char *old_data,
                                off_t len, struct line_buffer *input)
@@ -320,7 +353,7 @@ void fast_export_blob_delta(uint32_t mode,
 
        assert(len >= 0);
        postimage_len = apply_delta(len, input, old_data, old_mode);
-       if (mode == REPO_MODE_LNK) {
+       if (mode == S_IFLNK) {
                buffer_skip_bytes(&postimage, strlen("link "));
                postimage_len -= strlen("link ");
        }
index b9a3b71c99fd59312f493d6b1eb24c9a0b44578c..60b79c35b97cbdc9589fe3778ac1f4b5f61646ff 100644 (file)
@@ -28,4 +28,7 @@ int fast_export_ls_rev(uint32_t rev, const char *path,
 int fast_export_ls(const char *path,
                        uint32_t *mode_out, struct strbuf *dataref_out);
 
+void fast_export_copy(uint32_t revision, const char *src, const char *dst);
+const char *fast_export_read_path(const char *path, uint32_t *mode_out);
+
 #endif
diff --git a/vcs-svn/repo_tree.c b/vcs-svn/repo_tree.c
deleted file mode 100644 (file)
index 67d27f0..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed under a two-clause BSD-style license.
- * See LICENSE for details.
- */
-
-#include "git-compat-util.h"
-#include "strbuf.h"
-#include "repo_tree.h"
-#include "fast_export.h"
-
-const char *repo_read_path(const char *path, uint32_t *mode_out)
-{
-       int err;
-       static struct strbuf buf = STRBUF_INIT;
-
-       strbuf_reset(&buf);
-       err = fast_export_ls(path, mode_out, &buf);
-       if (err) {
-               if (errno != ENOENT)
-                       die_errno("BUG: unexpected fast_export_ls error");
-               /* Treat missing paths as directories. */
-               *mode_out = REPO_MODE_DIR;
-               return NULL;
-       }
-       return buf.buf;
-}
-
-void repo_copy(uint32_t revision, const char *src, const char *dst)
-{
-       int err;
-       uint32_t mode;
-       static struct strbuf data = STRBUF_INIT;
-
-       strbuf_reset(&data);
-       err = fast_export_ls_rev(revision, src, &mode, &data);
-       if (err) {
-               if (errno != ENOENT)
-                       die_errno("BUG: unexpected fast_export_ls_rev error");
-               fast_export_delete(dst);
-               return;
-       }
-       fast_export_modify(dst, mode, data.buf);
-}
-
-void repo_delete(const char *path)
-{
-       fast_export_delete(path);
-}
diff --git a/vcs-svn/repo_tree.h b/vcs-svn/repo_tree.h
deleted file mode 100644 (file)
index 889c6a3..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef REPO_TREE_H_
-#define REPO_TREE_H_
-
-struct strbuf;
-
-#define REPO_MODE_DIR 0040000
-#define REPO_MODE_BLB 0100644
-#define REPO_MODE_EXE 0100755
-#define REPO_MODE_LNK 0120000
-
-uint32_t next_blob_mark(void);
-void repo_copy(uint32_t revision, const char *src, const char *dst);
-void repo_add(const char *path, uint32_t mode, uint32_t blob_mark);
-const char *repo_read_path(const char *path, uint32_t *mode_out);
-void repo_delete(const char *path);
-void repo_commit(uint32_t revision, const char *author,
-               const struct strbuf *log, const char *uuid, const char *url,
-               long unsigned timestamp);
-void repo_diff(uint32_t r1, uint32_t r2);
-void repo_init(void);
-void repo_reset(void);
-
-#endif
index 1846685a21a44decfe5790f0020dcf933c8d6583..ec6b350611d367fd75f909c4695d9faf2d68f91e 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include "cache.h"
-#include "repo_tree.h"
 #include "fast_export.h"
 #include "line_buffer.h"
 #include "strbuf.h"
@@ -134,13 +133,13 @@ static void handle_property(const struct strbuf *key_buf,
                        die("invalid dump: sets type twice");
                }
                if (!val) {
-                       node_ctx.type = REPO_MODE_BLB;
+                       node_ctx.type = S_IFREG | 0644;
                        return;
                }
                *type_set = 1;
                node_ctx.type = keylen == strlen("svn:executable") ?
-                               REPO_MODE_EXE :
-                               REPO_MODE_LNK;
+                               (S_IFREG | 0755) :
+                               S_IFLNK;
        }
 }
 
@@ -219,45 +218,45 @@ static void handle_node(void)
         */
        static const char *const empty_blob = "::empty::";
        const char *old_data = NULL;
-       uint32_t old_mode = REPO_MODE_BLB;
+       uint32_t old_mode = S_IFREG | 0644;
 
        if (node_ctx.action == NODEACT_DELETE) {
                if (have_text || have_props || node_ctx.srcRev)
                        die("invalid dump: deletion node has "
                                "copyfrom info, text, or properties");
-               repo_delete(node_ctx.dst.buf);
+               fast_export_delete(node_ctx.dst.buf);
                return;
        }
        if (node_ctx.action == NODEACT_REPLACE) {
-               repo_delete(node_ctx.dst.buf);
+               fast_export_delete(node_ctx.dst.buf);
                node_ctx.action = NODEACT_ADD;
        }
        if (node_ctx.srcRev) {
-               repo_copy(node_ctx.srcRev, node_ctx.src.buf, node_ctx.dst.buf);
+               fast_export_copy(node_ctx.srcRev, node_ctx.src.buf, node_ctx.dst.buf);
                if (node_ctx.action == NODEACT_ADD)
                        node_ctx.action = NODEACT_CHANGE;
        }
-       if (have_text && type == REPO_MODE_DIR)
+       if (have_text && type == S_IFDIR)
                die("invalid dump: directories cannot have text attached");
 
        /*
         * Find old content (old_data) and decide on the new mode.
         */
        if (node_ctx.action == NODEACT_CHANGE && !*node_ctx.dst.buf) {
-               if (type != REPO_MODE_DIR)
+               if (type != S_IFDIR)
                        die("invalid dump: root of tree is not a regular file");
                old_data = NULL;
        } else if (node_ctx.action == NODEACT_CHANGE) {
                uint32_t mode;
-               old_data = repo_read_path(node_ctx.dst.buf, &mode);
-               if (mode == REPO_MODE_DIR && type != REPO_MODE_DIR)
+               old_data = fast_export_read_path(node_ctx.dst.buf, &mode);
+               if (mode == S_IFDIR && type != S_IFDIR)
                        die("invalid dump: cannot modify a directory into a file");
-               if (mode != REPO_MODE_DIR && type == REPO_MODE_DIR)
+               if (mode != S_IFDIR && type == S_IFDIR)
                        die("invalid dump: cannot modify a file into a directory");
                node_ctx.type = mode;
                old_mode = mode;
        } else if (node_ctx.action == NODEACT_ADD) {
-               if (type == REPO_MODE_DIR)
+               if (type == S_IFDIR)
                        old_data = NULL;
                else if (have_text)
                        old_data = empty_blob;
@@ -280,7 +279,7 @@ static void handle_node(void)
        /*
         * Save the result.
         */
-       if (type == REPO_MODE_DIR)      /* directories are not tracked. */
+       if (type == S_IFDIR)    /* directories are not tracked. */
                return;
        assert(old_data);
        if (old_data == empty_blob)
@@ -385,9 +384,9 @@ void svndump_read(const char *url, const char *local_ref, const char *notes_ref)
                                continue;
                        strbuf_addf(&rev_ctx.note, "%s\n", t);
                        if (!strcmp(val, "dir"))
-                               node_ctx.type = REPO_MODE_DIR;
+                               node_ctx.type = S_IFDIR;
                        else if (!strcmp(val, "file"))
-                               node_ctx.type = REPO_MODE_BLB;
+                               node_ctx.type = S_IFREG | 0644;
                        else
                                fprintf(stderr, "Unknown node-kind: %s\n", val);
                        break;