Code

Merge branch 'jc/grep--no-index-pathspec-fix'
authorJunio C Hamano <gitster@pobox.com>
Fri, 4 Mar 2011 23:02:26 +0000 (15:02 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 4 Mar 2011 23:02:26 +0000 (15:02 -0800)
* jc/grep--no-index-pathspec-fix:
  grep --no-index: honor pathspecs correctly

170 files changed:
.gitignore
Documentation/CodingGuidelines
Documentation/RelNotes/1.7.4.1.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.5.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/fetch-options.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-commit.txt
Documentation/git-diff.txt
Documentation/git-fast-import.txt
Documentation/git-pull.txt
Documentation/git-remote-ext.txt
Documentation/git-remote-helpers.txt
Documentation/git-revert.txt
Documentation/git-status.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git-verify-pack.txt
Documentation/git.txt
Documentation/howto/using-merge-subtree.txt
Makefile
RelNotes
aclocal.m4
builtin.h
builtin/add.c
builtin/branch.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch.c
builtin/fsck.c
builtin/hash-object.c
builtin/init-db.c
builtin/merge.c
builtin/notes.c
builtin/patch-id.c
builtin/push.c
builtin/read-tree.c
builtin/rerere.c
builtin/revert.c
builtin/tag.c
bundle.c
cache.h
commit.c
commit.h
compat/mingw.c
compat/mingw.h
config.c
configure.ac
contrib/examples/git-revert.sh
contrib/fast-import/git-p4
contrib/fast-import/git-p4.txt
contrib/hooks/post-receive-email
contrib/svn-fe/svn-fe.c
contrib/svn-fe/svn-fe.txt
fast-import.c
git-compat-util.h
git-mergetool.sh
git-pull.sh
git.c
gitweb/INSTALL
gitweb/gitweb.perl
list-objects.c
merge-recursive.h
notes-merge.c
parse-options.h
patch-delta.c
perl/Git.pm
quote.h
read-cache.c
remote-curl.c
rerere.c
rerere.h
run-command.c
setup.c
sha1_file.c
strbuf.c
string-list.c
t/gitweb-lib.sh
t/t0040-parse-options.sh
t/t0070-fundamental.sh
t/t0080-vcs-svn.sh
t/t0081-line-buffer.sh [new file with mode: 0755]
t/t1007-hash-object.sh
t/t1300-repo-config.sh
t/t2019-checkout-ambiguous-ref.sh [new file with mode: 0755]
t/t2020-checkout-detach.sh [new file with mode: 0755]
t/t3032-merge-recursive-options.sh
t/t3509-cherry-pick-merge-df.sh
t/t4013-diff-various.sh
t/t4013/diff.diff_--cached [new file with mode: 0644]
t/t4013/diff.diff_--cached_--_file0 [new file with mode: 0644]
t/t4034-diff-words.sh
t/t4034/bibtex/expect [new file with mode: 0644]
t/t4034/bibtex/post [new file with mode: 0644]
t/t4034/bibtex/pre [new file with mode: 0644]
t/t4034/cpp/expect [new file with mode: 0644]
t/t4034/cpp/post [new file with mode: 0644]
t/t4034/cpp/pre [new file with mode: 0644]
t/t4034/csharp/expect [new file with mode: 0644]
t/t4034/csharp/post [new file with mode: 0644]
t/t4034/csharp/pre [new file with mode: 0644]
t/t4034/fortran/expect [new file with mode: 0644]
t/t4034/fortran/post [new file with mode: 0644]
t/t4034/fortran/pre [new file with mode: 0644]
t/t4034/html/expect [new file with mode: 0644]
t/t4034/html/post [new file with mode: 0644]
t/t4034/html/pre [new file with mode: 0644]
t/t4034/java/expect [new file with mode: 0644]
t/t4034/java/post [new file with mode: 0644]
t/t4034/java/pre [new file with mode: 0644]
t/t4034/objc/expect [new file with mode: 0644]
t/t4034/objc/post [new file with mode: 0644]
t/t4034/objc/pre [new file with mode: 0644]
t/t4034/pascal/expect [new file with mode: 0644]
t/t4034/pascal/post [new file with mode: 0644]
t/t4034/pascal/pre [new file with mode: 0644]
t/t4034/perl/expect [new file with mode: 0644]
t/t4034/perl/post [new file with mode: 0644]
t/t4034/perl/pre [new file with mode: 0644]
t/t4034/php/expect [new file with mode: 0644]
t/t4034/php/post [new file with mode: 0644]
t/t4034/php/pre [new file with mode: 0644]
t/t4034/python/expect [new file with mode: 0644]
t/t4034/python/post [new file with mode: 0644]
t/t4034/python/pre [new file with mode: 0644]
t/t4034/ruby/expect [new file with mode: 0644]
t/t4034/ruby/post [new file with mode: 0644]
t/t4034/ruby/pre [new file with mode: 0644]
t/t4034/tex/expect [new file with mode: 0644]
t/t4034/tex/post [new file with mode: 0644]
t/t4034/tex/pre [new file with mode: 0644]
t/t4120-apply-popt.sh
t/t4204-patch-id.sh
t/t5526-fetch-submodules.sh
t/t6000-rev-list-misc.sh [new file with mode: 0755]
t/t7407-submodule-foreach.sh
t/t7500-commit.sh
t/t7505-prepare-commit-msg-hook.sh
t/t7607-merge-overwrite.sh
t/t7610-mergetool.sh
t/t9010-svn-fe.sh
t/t9300-fast-import.sh
t/t9301-fast-import-notes.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9501-gitweb-standalone-http-status.sh
t/t9700/test.pl
t/t9800-git-p4.sh [new file with mode: 0755]
tag.c
tag.h
test-line-buffer.c
test-mktemp.c [new file with mode: 0644]
test-parse-options.c
test-svn-fe.c
unpack-trees.c
userdiff.c
vcs-svn/fast_export.c
vcs-svn/fast_export.h
vcs-svn/line_buffer.c
vcs-svn/line_buffer.h
vcs-svn/line_buffer.txt
vcs-svn/repo_tree.c
vcs-svn/repo_tree.h
vcs-svn/svndump.c
vcs-svn/svndump.h
wrapper.c

index 3dd6ef7d259d8fe2811f68525b5309e97c79f4cc..c460c66766ab590239e4ae9107bb55b9fd30fac6 100644 (file)
 /test-index-version
 /test-line-buffer
 /test-match-trees
+/test-mktemp
 /test-obj-pool
 /test-parse-options
 /test-path-utils
index ba2006d892ec09d606f8846588c7727396b1ee28..fe1c1e5bc26e683540cb9fe5f43320192be9185d 100644 (file)
@@ -152,7 +152,7 @@ Writing Documentation:
  when writing or modifying command usage strings and synopsis sections
  in the manual pages:
 
- Placeholders are enclosed in angle brackets:
+ Placeholders are spelled in lowercase and enclosed in angle brackets:
    <file>
    --sort=<key>
    --abbrev[=<n>]
diff --git a/Documentation/RelNotes/1.7.4.1.txt b/Documentation/RelNotes/1.7.4.1.txt
new file mode 100644 (file)
index 0000000..79923a6
--- /dev/null
@@ -0,0 +1,27 @@
+Git v1.7.4.1 Release Notes
+==========================
+
+Fixes since v1.7.4
+------------------
+
+ * On Windows platform, the codepath to spawn a new child process forgot
+   to first flush the output buffer.
+
+ * "git bundle" did not use OFS_DELTA encoding, making its output a few
+   per-cent larger than necessarily.
+
+ * The option to tell "git clone" to recurse into the submodules was
+   misspelled with an underscore "--recurse_submodules".
+
+ * "git diff --cached HEAD" before the first commit does what an end user
+   would expect (namely, show what would be committed without further "git
+   add").
+
+ * "git fast-import" didn't accept the command to ask for "notes" feature
+   to be present in its input stream, even though it was capable of the
+   feature.
+
+ * "git fsck" gave up scanning loose object files in directories with
+   garbage files.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes/1.7.5.txt b/Documentation/RelNotes/1.7.5.txt
new file mode 100644 (file)
index 0000000..56c3863
--- /dev/null
@@ -0,0 +1,56 @@
+Git v1.7.5 Release Notes (draft)
+========================
+
+Updates since v1.7.4
+--------------------
+
+ * Various MinGW portability fixes.
+
+ * Various git-p4 enhancements (in contrib).
+
+ * "git config" used to be also known as "git repo-config", but the old
+   name is now officially deprecated.
+
+ * "git checkout --detach <commit>" is a more user friendly synonym for
+   "git checkout <commit>^0".
+
+ * "git cherry-pick" and "git revert" can be told to use custom merge
+   strategy, similar to "git rebase".
+
+ * "rev-list --objects $revs -- $pathspec" would limit the objects listed
+   in its output properly with the pathspec, in preparation for narrow
+   clones.
+
+ * "git log" family of commands now understand globbing pathspecs.  You
+   can say "git log -- '*.txt'" for example.
+
+ * "git rerere" learned a new subcommand "remaining", that is similar to
+   "status" that lists the paths that had conflicts that are known to
+   rerere, but excludes the paths that have already been marked as
+   resolved in the index from its output.  "git mergetool" has been
+   updated to use this facility.
+
+ * A possible value to the "push.default" configuration variable,
+   'tracking', gained a synonym that more naturally describes what it
+   does, 'upstream'.
+
+Also contains various documentation updates.
+
+
+Fixes since v1.7.4
+------------------
+
+All of the fixes in the v1.7.4.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git merge" triggers prepare-commit-msg hook.  Earlier, only "git
+   commit" to conclude an interrupted merge triggered the hook, leading to
+   an inconsistent overall user experience (js/maint-merge-use-prepare-commit-msg-hook).
+
+
+---
+exec >/var/tmp/1
+O=v1.7.4
+O=v1.7.4.1-140-g8978166
+echo O=$(git describe 'master')
+git shortlog --no-merges ^maint ^$O master
index c5e183516a104e6efb7ed597fb4498d75560ab68..c995a1a47b3cfb99a2bebbadf4f7a5360cbe3d1a 100644 (file)
@@ -1591,7 +1591,8 @@ push.default::
 * `matching` - push all matching branches.
   All branches having the same name in both ends are considered to be
   matching. This is the default.
-* `tracking` - push the current branch to its upstream branch.
+* `upstream` - push the current branch to its upstream branch.
+* `tracking` - deprecated synonym for `upstream`.
 * `current` - push the current branch to a branch of the same name.
 
 rebase.stat::
index 695696da1b6e78639014388e1d4ac8bf09a0e12d..f37276e5add75583fd363af2cd0b7ba051771a97 100644 (file)
@@ -64,13 +64,11 @@ ifndef::git-pull[]
        downloaded. The default behavior for a remote may be
        specified with the remote.<name>.tagopt setting. See
        linkgit:git-config[1].
-endif::git-pull[]
 
 --[no-]recurse-submodules::
        This option controls if new commits of all populated submodules should
        be fetched too (see linkgit:git-config[1] and linkgit:gitmodules[5]).
 
-ifndef::git-pull[]
 --submodule-prefix=<path>::
        Prepend <path> to paths printed in informative messages
        such as "Fetching submodule foo".  This option is used
index 880763d391d18364485edc2e1e3dfc6f52243973..396f4cc15bff745e72e137239dbd49e6584e23d2 100644 (file)
@@ -9,6 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git checkout' [-q] [-f] [-m] [<branch>]
+'git checkout' [-q] [-f] [-m] [--detach] [<commit>]
 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
 'git checkout' --patch [<tree-ish>] [--] [<paths>...]
@@ -22,9 +23,10 @@ branch.
 
 'git checkout' [<branch>]::
 'git checkout' -b|-B <new_branch> [<start point>]::
+'git checkout' [--detach] [<commit>]::
 
        This form switches branches by updating the index, working
-       tree, and HEAD to reflect the specified branch.
+       tree, and HEAD to reflect the specified branch or commit.
 +
 If `-b` is given, a new branch is created as if linkgit:git-branch[1]
 were called and then checked out; in this case you can
@@ -115,6 +117,13 @@ explicitly give a name with '-b' in such a case.
        Create the new branch's reflog; see linkgit:git-branch[1] for
        details.
 
+--detach::
+       Rather than checking out a branch to work on it, check out a
+       commit for inspection and discardable experiments.
+       This is the default behavior of "git checkout <commit>" when
+       <commit> is not a branch name.  See the "DETACHED HEAD" section
+       below for details.
+
 --orphan::
        Create a new 'orphan' branch, named <new_branch>, started from
        <start_point> and switch to it.  The first commit made on this
@@ -204,42 +213,140 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 
 
 
-Detached HEAD
+DETACHED HEAD
 -------------
+HEAD normally refers to a named branch (e.g. 'master'). Meanwhile, each
+branch refers to a specific commit. Let's look at a repo with three
+commits, one of them tagged, and with branch 'master' checked out:
+
+------------
+          HEAD (refers to branch 'master')
+           |
+           v
+a---b---c  branch 'master' (refers to commit 'c')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
 
-It is sometimes useful to be able to 'checkout' a commit that is
-not at the tip of one of your branches.  The most obvious
-example is to check out the commit at a tagged official release
-point, like this:
+When a commit is created in this state, the branch is updated to refer to
+the new commit. Specifically, 'git commit' creates a new commit 'd', whose
+parent is commit 'c', and then updates branch 'master' to refer to new
+commit 'd'. HEAD still refers to branch 'master' and so indirectly now refers
+to commit 'd':
 
 ------------
-$ git checkout v2.6.18
+$ edit; git add; git commit
+
+              HEAD (refers to branch 'master')
+               |
+               v
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
 ------------
 
-Earlier versions of git did not allow this and asked you to
-create a temporary branch using the `-b` option, but starting from
-version 1.5.0, the above command 'detaches' your HEAD from the
-current branch and directly points at the commit named by the tag
-(`v2.6.18` in the example above).
+It is sometimes useful to be able to checkout a commit that is not at
+the tip of any named branch, or even to create a new commit that is not
+referenced by a named branch. Let's look at what happens when we
+checkout commit 'b' (here we show two ways this may be done):
 
-You can use all git commands while in this state.  You can use
-`git reset --hard $othercommit` to further move around, for
-example.  You can make changes and create a new commit on top of
-a detached HEAD.  You can even create a merge by using `git
-merge $othercommit`.
+------------
+$ git checkout v2.0  # or
+$ git checkout master^^
+
+   HEAD (refers to commit 'b')
+    |
+    v
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
 
-The state you are in while your HEAD is detached is not recorded
-by any branch (which is natural --- you are not on any branch).
-What this means is that you can discard your temporary commits
-and merges by switching back to an existing branch (e.g. `git
-checkout master`), and a later `git prune` or `git gc` would
-garbage-collect them.  If you did this by mistake, you can ask
-the reflog for HEAD where you were, e.g.
+Notice that regardless of which checkout command we use, HEAD now refers
+directly to commit 'b'. This is known as being in detached HEAD state.
+It means simply that HEAD refers to a specific commit, as opposed to
+referring to a named branch. Let's see what happens when we create a commit:
 
 ------------
-$ git log -g -2 HEAD
+$ edit; git add; git commit
+
+     HEAD (refers to commit 'e')
+      |
+      v
+      e
+     /
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
+
+There is now a new commit 'e', but it is referenced only by HEAD. We can
+of course add yet another commit in this state:
+
+------------
+$ edit; git add; git commit
+
+        HEAD (refers to commit 'f')
+         |
+         v
+      e---f
+     /
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
+------------
+
+In fact, we can perform all the normal git operations. But, let's look
+at what happens when we then checkout master:
+
+------------
+$ git checkout master
+
+              HEAD (refers to branch 'master')
+      e---f     |
+     /          v
+a---b---c---d  branch 'master' (refers to commit 'd')
+    ^
+    |
+  tag 'v2.0' (refers to commit 'b')
 ------------
 
+It is important to realize that at this point nothing refers to commit
+'f'. Eventually commit 'f' (and by extension commit 'e') will be deleted
+by the routine git garbage collection process, unless we create a reference
+before that happens. If we have not yet moved away from commit 'f',
+any of these will create a reference to it:
+
+------------
+$ git checkout -b foo   <1>
+$ git branch foo        <2>
+$ git tag foo           <3>
+------------
+
+<1> creates a new branch 'foo', which refers to commit 'f', and then
+updates HEAD to refer to branch 'foo'. In other words, we'll no longer
+be in detached HEAD state after this command.
+
+<2> similarly creates a new branch 'foo', which refers to commit 'f',
+but leaves HEAD detached.
+
+<3> creates a new tag 'foo', which refers to commit 'f',
+leaving HEAD detached.
+
+If we have moved away from commit 'f', then we must first recover its object
+name (typically by using git reflog), and then we can create a reference to
+it. For example, to see the last two commits to which HEAD referred, we
+can use either of these commands:
+
+------------
+$ git reflog -2 HEAD # or
+$ git log -g -2 HEAD
+------------
 
 EXAMPLES
 --------
index 73008705ebe304c8d2a2d089e9328be369c50fc6..749d68a72bf720d82260fe175be0824f7ad069eb 100644 (file)
@@ -79,6 +79,16 @@ effect to your index in a row.
        cherry-pick'ed commit, then a fast forward to this commit will
        be performed.
 
+--strategy=<strategy>::
+       Use the given merge strategy.  Should only be used once.
+       See the MERGE STRATEGIES section in linkgit:git-merge[1]
+       for details.
+
+-X<option>::
+--strategy-option=<option>::
+       Pass the merge strategy-specific option through to the
+       merge strategy.  See linkgit:git-merge[1] for details.
+
 EXAMPLES
 --------
 git cherry-pick master::
@@ -120,6 +130,28 @@ git rev-list --reverse master \-- README | git cherry-pick -n --stdin::
        so the result can be inspected and made into a single new
        commit if suitable.
 
+The following sequence attempts to backport a patch, bails out because
+the code the patch applies to has changed too much, and then tries
+again, this time exercising more care about matching up context lines.
+
+------------
+$ git cherry-pick topic^             <1>
+$ git diff                           <2>
+$ git reset --merge ORIG_HEAD        <3>
+$ git cherry-pick -Xpatience topic^  <4>
+------------
+<1> apply the change that would be shown by `git show topic^`.
+In this example, the patch does not apply cleanly, so
+information about the conflict is written to the index and
+working tree and no new commit results.
+<2> summarize changes to be reconciled
+<3> cancel the cherry-pick.  In other words, return to the
+pre-cherry-pick state, preserving any local modifications you had in
+the working tree.
+<4> try to apply the change introduced by `topic^` again,
+spending extra time to avoid mistakes based on incorrectly matching
+context lines.
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>
index b586c0f442afd33c2875af26b83bb261de7361b0..8f89f6f08c57335ea6606dd1eaddde98477e9c9e 100644 (file)
@@ -214,10 +214,11 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
 
 -u[<mode>]::
 --untracked-files[=<mode>]::
-       Show untracked files (Default: 'all').
+       Show untracked files.
 +
-The mode parameter is optional, and is used to specify
-the handling of untracked files.
+The mode parameter is optional (defaults to 'all'), and is used to
+specify the handling of untracked files; when -u is not used, the
+default is 'normal', i.e. show untracked files and directories.
 +
 The possible options are:
 +
@@ -225,9 +226,8 @@ The possible options are:
        - 'normal' - Shows untracked files and directories
        - 'all'    - Also shows individual files in untracked directories.
 +
-See linkgit:git-config[1] for configuration variable
-used to change the default for when the option is not
-specified.
+The default can be changed using the status.showUntrackedFiles
+configuration variable documented in linkgit:git-config[1].
 
 -v::
 --verbose::
index f6ac847507a635759323565be44b088b479d8bf0..49105102dbf899c62833572fdda51cefaed53476 100644 (file)
@@ -38,6 +38,8 @@ directories. This behavior can be forced by --no-index.
        commit relative to the named <commit>.  Typically you
        would want comparison with the latest commit, so if you
        do not give <commit>, it defaults to HEAD.
+       If HEAD does not exist (e.g. unborned branches) and
+       <commit> is not given, it shows all staged changes.
        --staged is a synonym of --cached.
 
 'git diff' [--options] <commit> [--] [<path>...]::
index 4415e636352ea88f34a798b6c29dc5e34d60d868..e1b7a0f9ec88deab1711d99293e23d4396a7bc0c 100644 (file)
@@ -78,6 +78,10 @@ OPTIONS
        set of marks.  If a mark is defined to different values,
        the last file wins.
 
+--import-marks-if-exists=<file>::
+       Like --import-marks but instead of erroring out, silently
+       skips the file if it does not exist.
+
 --relative-marks::
        After specifying --relative-marks= the paths specified
        with --import-marks= and --export-marks= are relative
@@ -192,7 +196,8 @@ especially when a higher level language such as Perl, Python or
 Ruby is being used.
 
 fast-import is very strict about its input.  Where we say SP below we mean
-*exactly* one space.  Likewise LF means one (and only one) linefeed.
+*exactly* one space.  Likewise LF means one (and only one) linefeed
+and HT one (and only one) horizontal tab.
 Supplying additional whitespace characters will cause unexpected
 results, such as branch names or file names with leading or trailing
 spaces in their name, or early termination of fast-import when it encounters
@@ -330,6 +335,11 @@ and control the current import process.  More detailed discussion
        format to the file descriptor set with `--cat-blob-fd` or
        `stdout` if unspecified.
 
+`ls`::
+       Causes fast-import to print a line describing a directory
+       entry in 'ls-tree' format to the file descriptor set with
+       `--cat-blob-fd` or `stdout` if unspecified.
+
 `feature`::
        Require that fast-import supports the specified feature, or
        abort if it does not.
@@ -915,6 +925,55 @@ This command can be used anywhere in the stream that comments are
 accepted.  In particular, the `cat-blob` command can be used in the
 middle of a commit but not in the middle of a `data` command.
 
+`ls`
+~~~~
+Prints information about the object at a path to a file descriptor
+previously arranged with the `--cat-blob-fd` argument.  This allows
+printing a blob from the active commit (with `cat-blob`) or copying a
+blob or tree from a previous commit for use in the current one (with
+`filemodify`).
+
+The `ls` command can be used anywhere in the stream that comments are
+accepted, including the middle of a commit.
+
+Reading from the active commit::
+       This form can only be used in the middle of a `commit`.
+       The path names a directory entry within fast-import's
+       active commit.  The path must be quoted in this case.
++
+....
+       'ls' SP <path> LF
+....
+
+Reading from a named tree::
+       The `<dataref>` can be a mark reference (`:<idnum>`) or the
+       full 40-byte SHA-1 of a Git tag, commit, or tree object,
+       preexisting or waiting to be written.
+       The path is relative to the top level of the tree
+       named by `<dataref>`.
++
+....
+       'ls' SP <dataref> SP <path> LF
+....
+
+See `filemodify` above for a detailed description of `<path>`.
+
+Output uses the same format as `git ls-tree <tree> {litdd} <path>`:
+
+====
+       <mode> SP ('blob' | 'tree' | 'commit') SP <dataref> HT <path> LF
+====
+
+The <dataref> represents the blob, tree, or commit object at <path>
+and can be used in later 'cat-blob', 'filemodify', or 'ls' commands.
+
+If there is no file or subtree at that path, 'git fast-import' will
+instead report
+
+====
+       missing SP <path> LF
+====
+
 `feature`
 ~~~~~~~~~
 Require that fast-import supports the specified feature, or abort if
@@ -942,12 +1001,21 @@ import-marks::
        any "feature import-marks" command in the stream.
 
 cat-blob::
-       Ignored.  Versions of fast-import not supporting the
-       "cat-blob" command will exit with a message indicating so.
+ls::
+       Require that the backend support the 'cat-blob' or 'ls' command.
+       Versions of fast-import not supporting the specified command
+       will exit with a message indicating so.
        This lets the import error out early with a clear message,
        rather than wasting time on the early part of an import
        before the unsupported command is detected.
 
+notes::
+       Require that the backend support the 'notemodify' (N)
+       subcommand to the 'commit' command.
+       Versions of fast-import not supporting notes will exit
+       with a message indicating so.
+
+
 `option`
 ~~~~~~~~
 Processes the specified option so that git fast-import behaves in a
index 30466917dad52eefd76de95aead3fdc12ad8425d..b33e6be872508778faa59f478229cd6e16bddad7 100644 (file)
@@ -84,6 +84,15 @@ must be given before the options meant for 'git fetch'.
 --verbose::
        Pass --verbose to git-fetch and git-merge.
 
+--[no-]recurse-submodules::
+       This option controls if new commits of all populated submodules should
+       be fetched too (see linkgit:git-config[1] and linkgit:gitmodules[5]).
+       That might be necessary to get the data needed for merging submodule
+       commits, a feature git learned in 1.7.3. Notice that the result of a
+       merge will not be checked out in the submodule, "git submodule update"
+       has to be called afterwards to bring the work tree up to date with the
+       merge result.
+
 Options related to merging
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index 2d65cfefd5d2aeaafcbc0bb6e6705f8379b2f826..68263a6a538b3d4659ca9dc0afd2253170143fb6 100644 (file)
@@ -7,17 +7,17 @@ git-remote-ext - Bridge smart transport to external command.
 
 SYNOPSIS
 --------
-git remote add nick "ext::<command>[ <arguments>...]"
+git remote add <nick> "ext::<command>[ <arguments>...]"
 
 DESCRIPTION
 -----------
-This remote helper uses the specified 'program' to connect
+This remote helper uses the specified '<command>' to connect
 to a remote git server.
 
-Data written to stdin of this specified 'program' is assumed
+Data written to stdin of the specified '<command>' is assumed
 to be sent to a git:// server, git-upload-pack, git-receive-pack
 or git-upload-archive (depending on situation), and data read
-from stdout of this program is assumed to be received from
+from stdout of <command> is assumed to be received from
 the same service.
 
 Command and arguments are separated by an unescaped space.
@@ -40,7 +40,7 @@ The following sequences have a special meaning:
        git wants to invoke.
 
 '%G' (must be the first characters in an argument)::
-       This argument will not be passed to 'program'. Instead, it
+       This argument will not be passed to '<command>'. Instead, it
        will cause the helper to start by sending git:// service requests to
        the remote side with the service field set to an appropriate value and
        the repository field set to rest of the argument. Default is not to send
@@ -50,7 +50,7 @@ This is useful if remote side is git:// server accessed over
 some tunnel.
 
 '%V' (must be first characters in argument)::
-       This argument will not be passed to 'program'. Instead it sets
+       This argument will not be passed to '<command>'. Instead it sets
        the vhost field in the git:// service request (to rest of the argument).
        Default is not to send vhost in such request (if sent).
 
@@ -76,7 +76,7 @@ EXAMPLES:
 ---------
 This remote helper is transparently used by git when
 you use commands such as "git fetch <URL>", "git clone <URL>",
-, "git push <URL>" or "git remote add nick <URL>", where <URL>
+, "git push <URL>" or "git remote add <nick> <URL>", where <URL>
 begins with `ext::`.  Examples:
 
 "ext::ssh -i /home/foo/.ssh/somekey user&#64;host.example %S 'foo/repo'"::
index 3a23477ce72763b562258a25c3777dd57ac1a962..51de895822d6d79f970012c835d68b2cd8c8e136 100644 (file)
@@ -201,12 +201,12 @@ REF LIST ATTRIBUTES
 
 OPTIONS
 -------
-'option verbosity' <N>::
+'option verbosity' <n>::
        Changes the verbosity of messages displayed by the helper.
-       A value of 0 for N means that processes operate
+       A value of 0 for <n> means that processes operate
        quietly, and the helper produces only error output.
        1 is the default level of verbosity, and higher values
-       of N correspond to the number of -v flags passed on the
+       of <n> correspond to the number of -v flags passed on the
        command line.
 
 'option progress' \{'true'|'false'\}::
index 752fc88e768e4b8e1afddad4bdd7833cf20978c7..45be851750c2ce81500765f6dfbd23578c68abda 100644 (file)
@@ -80,6 +80,16 @@ effect to your index in a row.
 --signoff::
        Add Signed-off-by line at the end of the commit message.
 
+--strategy=<strategy>::
+       Use the given merge strategy.  Should only be used once.
+       See the MERGE STRATEGIES section in linkgit:git-merge[1]
+       for details.
+
+-X<option>::
+--strategy-option=<option>::
+       Pass the merge strategy-specific option through to the
+       merge strategy.  See linkgit:git-merge[1] for details.
+
 EXAMPLES
 --------
 git revert HEAD~3::
index dae190a5f21d3835024ce263bf4bd9b08048cfa6..5102a23f8ee30fcff2a6e67d098495329a2fca1d 100644 (file)
@@ -38,20 +38,20 @@ OPTIONS
 
 -u[<mode>]::
 --untracked-files[=<mode>]::
-       Show untracked files (Default: 'all').
+       Show untracked files.
 +
-The mode parameter is optional, and is used to specify
-the handling of untracked files. The possible options are:
+The mode parameter is optional (defaults to 'all'), and is used to
+specify the handling of untracked files; when -u is not used, the
+default is 'normal', i.e. show untracked files and directories.
++
+The possible options are:
 +
---
        - 'no'     - Show no untracked files
        - 'normal' - Shows untracked files and directories
        - 'all'    - Also shows individual files in untracked directories.
---
 +
-See linkgit:git-config[1] for configuration variable
-used to change the default for when the option is not
-specified.
+The default can be changed using the status.showUntrackedFiles
+configuration variable documented in linkgit:git-config[1].
 
 --ignore-submodules[=<when>]::
        Ignore changes to submodules when looking for changes. <when> can be
index 0ade2ce54efee3cd2241fc7e3009909eef5cf8bb..e161a40a73cb57557435e94c9444935da0828f1c 100644 (file)
@@ -66,7 +66,7 @@ COMMANDS
        Set the 'rewriteRoot' option in the [svn-remote] config.
 --rewrite-uuid=<UUID>;;
        Set the 'rewriteUUID' option in the [svn-remote] config.
---username=<USER>;;
+--username=<user>;;
        For transports that SVN handles authentication for (http,
        https, and plain svn), specify the username.  For other
        transports (eg svn+ssh://), you must include the username in
@@ -443,8 +443,8 @@ OPTIONS
        Only used with the 'init' command.
        These are passed directly to 'git init'.
 
--r <ARG>::
---revision <ARG>::
+-r <arg>::
+--revision <arg>::
           Used with the 'fetch' command.
 +
 This allows revision ranges for partial/cauterized history
index 8b169e364a32afe7d2ff3fa8ae54121ed44e3788..65f76c544086458ec84322c73319d8b071288a0b 100644 (file)
@@ -18,21 +18,22 @@ SYNOPSIS
 DESCRIPTION
 -----------
 
-Adds a tag reference in `.git/refs/tags/`.
+Add a tag reference in `.git/refs/tags/`, unless `-d/-l/-v` is given
+to delete, list or verify tags.
 
-Unless `-f` is given, the tag must not yet exist in
+Unless `-f` is given, the tag to be created must not yet exist in the
 `.git/refs/tags/` directory.
 
 If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
-creates a 'tag' object, and requires the tag message.  Unless
+creates a 'tag' object, and requires a tag message.  Unless
 `-m <msg>` or `-F <file>` is given, an editor is started for the user to type
 in the tag message.
 
 If `-m <msg>` or `-F <file>` is given and `-a`, `-s`, and `-u <key-id>`
 are absent, `-a` is implied.
 
-Otherwise just the SHA1 object name of the commit object is
-written (i.e. a lightweight tag).
+Otherwise just a tag reference for the SHA1 object name of the commit object is
+created (i.e. a lightweight tag).
 
 A GnuPG signed tag object will be created when `-s` or `-u
 <key-id>` is used.  When `-u <key-id>` is not used, the
index 916a38aa99020efe281907670932517a66d075e1..0f848de8b29dd32aebf0ab9b21ddddce0b06a5b2 100644 (file)
@@ -8,7 +8,7 @@ git-verify-pack - Validate packed git archive files
 
 SYNOPSIS
 --------
-'git verify-pack' [-v|--verbose] [--] <pack>.idx ...
+'git verify-pack' [-v|--verbose] [-s|--stat-only] [--] <pack>.idx ...
 
 
 DESCRIPTION
index e968ed4aa09705d98530add23135e76141ec75c2..0c32d45248c99e9e4b51f168ba03c829ad96423f 100644 (file)
@@ -44,9 +44,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.4/git.html[documentation for release 1.7.4]
+* link:v1.7.4.1/git.html[documentation for release 1.7.4.1]
 
 * release notes for
+  link:RelNotes/1.7.4.1.txt[1.7.4.1],
   link:RelNotes/1.7.4.txt[1.7.4].
 
 * link:v1.7.3.5/git.html[documentation for release 1.7.3.5]
index 0953a50b693307976977c81a4c0b611fd5dfb490..2933056120bf0a0d6249c6951ab1e964ae40674b 100644 (file)
@@ -71,5 +71,5 @@ Additional tips
   relevant parts of your tree.
 
 - Please note that if the other project merges from you, then it will
-  connects its history to yours, which can be something they don't want
+  connect its history to yours, which can be something they don't want
   to.
index 775ee838c332c7311df34a98448fc3723c6bd95e..ade79232f4c3c080fa7d35c4b55870c416e49da6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -435,6 +435,7 @@ TEST_PROGRAMS_NEED_X += test-subprocess
 TEST_PROGRAMS_NEED_X += test-svn-fe
 TEST_PROGRAMS_NEED_X += test-treap
 TEST_PROGRAMS_NEED_X += test-index-version
+TEST_PROGRAMS_NEED_X += test-mktemp
 
 TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
 
index b942e499449d97aeb50c73ca2bdc1c6e6d528743..540b756d2f64d8c8ebf927a707b9b2cc0a4c6151 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.7.4.txt
\ No newline at end of file
+Documentation/RelNotes/1.7.5.txt
\ No newline at end of file
index d399de2671bff39ee34b55017c179242010d643f..f11bc7ed7df2f6b058f947bfe850abb33590bfa7 100644 (file)
@@ -13,7 +13,7 @@ AC_DEFUN([TYPE_SOCKLEN_T],
          git_cv_socklen_t_equiv=
          for arg2 in "struct sockaddr" void; do
             for t in int size_t unsigned long "unsigned long"; do
-               AC_TRY_COMPILE([
+               AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
                   #include <sys/types.h>
                   #include <sys/socket.h>
 
@@ -21,7 +21,7 @@ AC_DEFUN([TYPE_SOCKLEN_T],
                ],[
                   $t len;
                   getpeername(0,0,&len);
-               ],[
+               ])],[
                   git_cv_socklen_t_equiv="$t"
                   break 2
                ])
index 904e067a88f242b42f16b715d2b67b45a101e468..0e9da9083491eb3c18464b730f481cf74ee82430 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -57,6 +57,7 @@ extern int cmd_clone(int argc, const char **argv, const char *prefix);
 extern int cmd_clean(int argc, const char **argv, const char *prefix);
 extern int cmd_commit(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_config(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_describe(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
@@ -110,7 +111,7 @@ extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
 extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
-extern int cmd_config(int argc, const char **argv, const char *prefix);
+extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_reset(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
index 5f817ad77f1b3ba247c3b76d1d9a50f7fcc5edee..f7a17e43f6816622ab4ff836a169221289756505 100644 (file)
@@ -317,12 +317,12 @@ static struct option builtin_add_options[] = {
        OPT__VERBOSE(&verbose, "be verbose"),
        OPT_GROUP(""),
        OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"),
-       OPT_BOOLEAN('p', "patch", &patch_interactive, "interactive patching"),
+       OPT_BOOLEAN('p', "patch", &patch_interactive, "select hunks interactively"),
        OPT_BOOLEAN('e', "edit", &edit_interactive, "edit current diff and apply"),
        OPT__FORCE(&ignored_too, "allow adding otherwise ignored files"),
        OPT_BOOLEAN('u', "update", &take_worktree_changes, "update tracked files"),
        OPT_BOOLEAN('N', "intent-to-add", &intent_to_add, "record only the fact that the path will be added later"),
-       OPT_BOOLEAN('A', "all", &addremove, "add all, noticing removal of tracked files"),
+       OPT_BOOLEAN('A', "all", &addremove, "add changes from all tracked and untracked files"),
        OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
        OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
        OPT_BOOLEAN( 0 , "ignore-missing", &ignore_missing, "check if - even missing - files are ignored in dry run"),
index 9e546e4a83d07dd6dd4d4b0cb7d23a02c82747fb..fe8f2fcd524cd690c8601d9370079f9d9ea21df3 100644 (file)
@@ -134,7 +134,7 @@ static int branch_merged(int kind, const char *name,
            in_merge_bases(rev, &head_rev, 1) != merged) {
                if (merged)
                        warning("deleting branch '%s' that has been merged to\n"
-                               "         '%s', but it is not yet merged to HEAD.",
+                               "         '%s', but not yet been merged to HEAD.",
                                name, reference_name);
                else
                        warning("not deleting branch '%s' that is not yet merged to\n"
index 757f9a08ddbaf102726a781b06d7294a4638984e..cc97dbc30f1b064a44157897d9e325d934111cee 100644 (file)
@@ -30,6 +30,7 @@ struct checkout_opts {
        int quiet;
        int merge;
        int force;
+       int force_detach;
        int writeout_stage;
        int writeout_error;
 
@@ -297,7 +298,7 @@ static void show_local_changes(struct object *head, struct diff_options *opts)
        run_diff_index(&rev, 0);
 }
 
-static void describe_detached_head(char *msg, struct commit *commit)
+static void describe_detached_head(const char *msg, struct commit *commit)
 {
        struct strbuf sb = STRBUF_INIT;
        struct pretty_print_context ctx = {0};
@@ -404,7 +405,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.dir->exclude_per_dir = ".gitignore";
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
-                                          (unsigned char *)EMPTY_TREE_SHA1_BIN);
+                                          EMPTY_TREE_SHA1_BIN);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
                tree = parse_tree_indirect(new->commit->object.sha1);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
@@ -541,7 +542,17 @@ static void update_refs_for_switch(struct checkout_opts *opts,
        strbuf_addf(&msg, "checkout: moving from %s to %s",
                    old_desc ? old_desc : "(invalid)", new->name);
 
-       if (new->path) {
+       if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
+               /* Nothing to do. */
+       } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
+               update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
+                          REF_NODEREF, DIE_ON_ERR);
+               if (!opts->quiet) {
+                       if (old->path && advice_detached_head)
+                               detach_advice(old->path, new->name);
+                       describe_detached_head("HEAD is now at", new->commit);
+               }
+       } else if (new->path) { /* Switch branches. */
                create_symref("HEAD", new->path, msg.buf);
                if (!opts->quiet) {
                        if (old->path && !strcmp(new->path, old->path))
@@ -563,18 +574,11 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                        if (!file_exists(ref_file) && file_exists(log_file))
                                remove_path(log_file);
                }
-       } else if (strcmp(new->name, "HEAD")) {
-               update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
-                          REF_NODEREF, DIE_ON_ERR);
-               if (!opts->quiet) {
-                       if (old->path && advice_detached_head)
-                               detach_advice(old->path, new->name);
-                       describe_detached_head("HEAD is now at", new->commit);
-               }
        }
        remove_branch_state();
        strbuf_release(&msg);
-       if (!opts->quiet && (new->path || !strcmp(new->name, "HEAD")))
+       if (!opts->quiet &&
+           (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD"))))
                report_tracking(new);
 }
 
@@ -675,11 +679,123 @@ static const char *unique_tracking_name(const char *name)
        return NULL;
 }
 
+static int parse_branchname_arg(int argc, const char **argv,
+                               int dwim_new_local_branch_ok,
+                               struct branch_info *new,
+                               struct tree **source_tree,
+                               unsigned char rev[20],
+                               const char **new_branch)
+{
+       int argcount = 0;
+       unsigned char branch_rev[20];
+       const char *arg;
+       int has_dash_dash;
+
+       /*
+        * case 1: git checkout <ref> -- [<paths>]
+        *
+        *   <ref> must be a valid tree, everything after the '--' must be
+        *   a path.
+        *
+        * case 2: git checkout -- [<paths>]
+        *
+        *   everything after the '--' must be paths.
+        *
+        * case 3: git checkout <something> [<paths>]
+        *
+        *   With no paths, if <something> is a commit, that is to
+        *   switch to the branch or detach HEAD at it.  As a special case,
+        *   if <something> is A...B (missing A or B means HEAD but you can
+        *   omit at most one side), and if there is a unique merge base
+        *   between A and B, A...B names that merge base.
+        *
+        *   With no paths, if <something> is _not_ a commit, no -t nor -b
+        *   was given, and there is a tracking branch whose name is
+        *   <something> in one and only one remote, then this is a short-hand
+        *   to fork local <something> from that remote-tracking branch.
+        *
+        *   Otherwise <something> shall not be ambiguous.
+        *   - If it's *only* a reference, treat it like case (1).
+        *   - If it's only a path, treat it like case (2).
+        *   - else: fail.
+        *
+        */
+       if (!argc)
+               return 0;
+
+       if (!strcmp(argv[0], "--"))     /* case (2) */
+               return 1;
+
+       arg = argv[0];
+       has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
+
+       if (!strcmp(arg, "-"))
+               arg = "@{-1}";
+
+       if (get_sha1_mb(arg, rev)) {
+               if (has_dash_dash)          /* case (1) */
+                       die("invalid reference: %s", arg);
+               if (dwim_new_local_branch_ok &&
+                   !check_filename(NULL, arg) &&
+                   argc == 1) {
+                       const char *remote = unique_tracking_name(arg);
+                       if (!remote || get_sha1(remote, rev))
+                               return argcount;
+                       *new_branch = arg;
+                       arg = remote;
+                       /* DWIMmed to create local branch */
+               } else {
+                       return argcount;
+               }
+       }
+
+       /* we can't end up being in (2) anymore, eat the argument */
+       argcount++;
+       argv++;
+       argc--;
+
+       new->name = arg;
+       setup_branch_path(new);
+
+       if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
+           resolve_ref(new->path, branch_rev, 1, NULL))
+               hashcpy(rev, branch_rev);
+       else
+               new->path = NULL; /* not an existing branch */
+
+       new->commit = lookup_commit_reference_gently(rev, 1);
+       if (!new->commit) {
+               /* not a commit */
+               *source_tree = parse_tree_indirect(rev);
+       } else {
+               parse_commit(new->commit);
+               *source_tree = new->commit->tree;
+       }
+
+       if (!*source_tree)                   /* case (1): want a tree */
+               die("reference is not a tree: %s", arg);
+       if (!has_dash_dash) {/* case (3 -> 1) */
+               /*
+                * Do not complain the most common case
+                *      git checkout branch
+                * even if there happen to be a file called 'branch';
+                * it would be extremely annoying.
+                */
+               if (argc)
+                       verify_non_filename(NULL, arg);
+       } else {
+               argcount++;
+               argv++;
+               argc--;
+       }
+
+       return argcount;
+}
+
 int cmd_checkout(int argc, const char **argv, const char *prefix)
 {
        struct checkout_opts opts;
        unsigned char rev[20];
-       const char *arg;
        struct branch_info new;
        struct tree *source_tree = NULL;
        char *conflict_style = NULL;
@@ -692,6 +808,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
                           "create/reset and checkout a branch"),
                OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "create reflog for new branch"),
+               OPT_BOOLEAN(0, "detach", &opts.force_detach, "detach the HEAD at named commit"),
                OPT_SET_INT('t', "track",  &opts.track, "set upstream info for new branch",
                        BRANCH_TRACK_EXPLICIT),
                OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
@@ -709,7 +826,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
                OPT_END(),
        };
-       int has_dash_dash;
 
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
@@ -731,9 +847,15 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                opts.new_branch = opts.new_branch_force;
 
        if (patch_mode && (opts.track > 0 || opts.new_branch
-                          || opts.new_branch_log || opts.merge || opts.force))
+                          || opts.new_branch_log || opts.merge || opts.force
+                          || opts.force_detach))
                die ("--patch is incompatible with all other options");
 
+       if (opts.force_detach && (opts.new_branch || opts.new_orphan_branch))
+               die("--detach cannot be used with -b/-B/--orphan");
+       if (opts.force_detach && 0 < opts.track)
+               die("--detach cannot be used with -t");
+
        /* --track without -b should DWIM */
        if (0 < opts.track && !opts.new_branch) {
                const char *argv0 = argv[0];
@@ -766,105 +888,30 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                die("git checkout: -f and -m are incompatible");
 
        /*
-        * case 1: git checkout <ref> -- [<paths>]
-        *
-        *   <ref> must be a valid tree, everything after the '--' must be
-        *   a path.
-        *
-        * case 2: git checkout -- [<paths>]
-        *
-        *   everything after the '--' must be paths.
-        *
-        * case 3: git checkout <something> [<paths>]
-        *
-        *   With no paths, if <something> is a commit, that is to
-        *   switch to the branch or detach HEAD at it.  As a special case,
-        *   if <something> is A...B (missing A or B means HEAD but you can
-        *   omit at most one side), and if there is a unique merge base
-        *   between A and B, A...B names that merge base.
+        * Extract branch name from command line arguments, so
+        * all that is left is pathspecs.
         *
-        *   With no paths, if <something> is _not_ a commit, no -t nor -b
-        *   was given, and there is a remote-tracking branch whose name is
-        *   <something> in one and only one remote, then this is a short-hand
-        *   to fork local <something> from that remote-tracking branch.
+        * Handle
         *
-        *   Otherwise <something> shall not be ambiguous.
-        *   - If it's *only* a reference, treat it like case (1).
-        *   - If it's only a path, treat it like case (2).
-        *   - else: fail.
+        *  1) git checkout <tree> -- [<paths>]
+        *  2) git checkout -- [<paths>]
+        *  3) git checkout <something> [<paths>]
         *
+        * including "last branch" syntax and DWIM-ery for names of
+        * remote branches, erroring out for invalid or ambiguous cases.
         */
        if (argc) {
-               if (!strcmp(argv[0], "--")) {       /* case (2) */
-                       argv++;
-                       argc--;
-                       goto no_reference;
-               }
-
-               arg = argv[0];
-               has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
-
-               if (!strcmp(arg, "-"))
-                       arg = "@{-1}";
-
-               if (get_sha1_mb(arg, rev)) {
-                       if (has_dash_dash)          /* case (1) */
-                               die("invalid reference: %s", arg);
-                       if (!patch_mode &&
-                           dwim_new_local_branch &&
-                           opts.track == BRANCH_TRACK_UNSPECIFIED &&
-                           !opts.new_branch &&
-                           !check_filename(NULL, arg) &&
-                           argc == 1) {
-                               const char *remote = unique_tracking_name(arg);
-                               if (!remote || get_sha1(remote, rev))
-                                       goto no_reference;
-                               opts.new_branch = arg;
-                               arg = remote;
-                               /* DWIMmed to create local branch */
-                       }
-                       else
-                               goto no_reference;
-               }
-
-               /* we can't end up being in (2) anymore, eat the argument */
-               argv++;
-               argc--;
-
-               new.name = arg;
-               if ((new.commit = lookup_commit_reference_gently(rev, 1))) {
-                       setup_branch_path(&new);
-
-                       if ((check_ref_format(new.path) == CHECK_REF_FORMAT_OK) &&
-                           resolve_ref(new.path, rev, 1, NULL))
-                               ;
-                       else
-                               new.path = NULL;
-                       parse_commit(new.commit);
-                       source_tree = new.commit->tree;
-               } else
-                       source_tree = parse_tree_indirect(rev);
-
-               if (!source_tree)                   /* case (1): want a tree */
-                       die("reference is not a tree: %s", arg);
-               if (!has_dash_dash) {/* case (3 -> 1) */
-                       /*
-                        * Do not complain the most common case
-                        *      git checkout branch
-                        * even if there happen to be a file called 'branch';
-                        * it would be extremely annoying.
-                        */
-                       if (argc)
-                               verify_non_filename(NULL, arg);
-               }
-               else {
-                       argv++;
-                       argc--;
-               }
+               int dwim_ok =
+                       !patch_mode &&
+                       dwim_new_local_branch &&
+                       opts.track == BRANCH_TRACK_UNSPECIFIED &&
+                       !opts.new_branch;
+               int n = parse_branchname_arg(argc, argv, dwim_ok,
+                               &new, &source_tree, rev, &opts.new_branch);
+               argv += n;
+               argc -= n;
        }
 
-no_reference:
-
        if (opts.track == BRANCH_TRACK_UNSPECIFIED)
                opts.track = git_branch_track;
 
@@ -886,6 +933,9 @@ no_reference:
                        }
                }
 
+               if (opts.force_detach)
+                       die("git checkout: --detach does not take a path argument");
+
                if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
                        die("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index.");
 
index 61e0989b5ab8fffb16ec28e64c38b4fa236cb3ed..60d9a64280b0318b1248b229d2ff99b27eb0e5c6 100644 (file)
@@ -66,10 +66,10 @@ static struct option builtin_clone_options[] = {
                    "setup as shared repository"),
        OPT_BOOLEAN(0, "recursive", &option_recursive,
                    "initialize submodules in the clone"),
-       OPT_BOOLEAN(0, "recurse_submodules", &option_recursive,
+       OPT_BOOLEAN(0, "recurse-submodules", &option_recursive,
                    "initialize submodules in the clone"),
-       OPT_STRING(0, "template", &option_template, "path",
-                  "path the template repository"),
+       OPT_STRING(0, "template", &option_template, "template-directory",
+                  "directory from which templates will be used"),
        OPT_STRING(0, "reference", &option_reference, "repo",
                   "reference repository"),
        OPT_STRING('o', "origin", &option_origin, "branch",
index 03cff5af631fec975442c528b1ffba264b879c8e..355b2cbca7460d40b013380c38801686e61b74f1 100644 (file)
@@ -118,14 +118,14 @@ static struct option builtin_commit_options[] = {
        OPT__VERBOSE(&verbose, "show diff in commit message template"),
 
        OPT_GROUP("Commit message options"),
-       OPT_FILENAME('F', "file", &logfile, "read log from file"),
-       OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
-       OPT_STRING(0, "date", &force_date, "DATE", "override date for commit"),
-       OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
-       OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"),
-       OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
-       OPT_STRING(0, "fixup", &fixup_message, "COMMIT", "use autosquash formatted message to fixup specified commit"),
-       OPT_STRING(0, "squash", &squash_message, "COMMIT", "use autosquash formatted message to squash specified commit"),
+       OPT_FILENAME('F', "file", &logfile, "read message from file"),
+       OPT_STRING(0, "author", &force_author, "author", "override author for commit"),
+       OPT_STRING(0, "date", &force_date, "date", "override date for commit"),
+       OPT_CALLBACK('m', "message", &message, "message", "commit message", opt_parse_m),
+       OPT_STRING('c', "reedit-message", &edit_message, "commit", "reuse and edit message from specified commit"),
+       OPT_STRING('C', "reuse-message", &use_message, "commit", "reuse message from specified commit"),
+       OPT_STRING(0, "fixup", &fixup_message, "commit", "use autosquash formatted message to fixup specified commit"),
+       OPT_STRING(0, "squash", &squash_message, "commit", "use autosquash formatted message to squash specified commit"),
        OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
        OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_FILENAME('t', "template", &template_file, "use specified template file"),
@@ -145,12 +145,12 @@ static struct option builtin_commit_options[] = {
                    STATUS_FORMAT_SHORT),
        OPT_BOOLEAN(0, "branch", &status_show_branch, "show branch information"),
        OPT_SET_INT(0, "porcelain", &status_format,
-                   "show porcelain output format", STATUS_FORMAT_PORCELAIN),
+                   "machine-readable output", STATUS_FORMAT_PORCELAIN),
        OPT_BOOLEAN('z', "null", &null_termination,
                    "terminate entries with NUL"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
-       { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+       { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
        /* end commit contents options */
 
        { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
@@ -1092,7 +1092,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN('b', "branch", &status_show_branch,
                            "show branch information"),
                OPT_SET_INT(0, "porcelain", &status_format,
-                           "show porcelain output format",
+                           "machine-readable output",
                            STATUS_FORMAT_PORCELAIN),
                OPT_BOOLEAN('z', "null", &null_termination,
                            "terminate entries with NUL"),
index ca4a0db4a79241d7ded005483b4693967eea636f..76be0b786fd52f181f91b5ad3bdef891068946f1 100644 (file)
@@ -52,7 +52,7 @@ static struct option builtin_config_options[] = {
        OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
        OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
        OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"),
-       OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
+       OPT_STRING('f', "file", &given_config_file, "file", "use given config file"),
        OPT_GROUP("Action"),
        OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
        OPT_BIT(0, "get-all", &actions, "get all values: key [value-regex]", ACTION_GET_ALL),
@@ -153,7 +153,6 @@ static int show_config(const char *key_, const char *value_, void *cb)
 static int get_value(const char *key_, const char *regex_)
 {
        int ret = -1;
-       char *tl;
        char *global = NULL, *repo_config = NULL;
        const char *system_wide = NULL, *local;
 
@@ -167,18 +166,32 @@ static int get_value(const char *key_, const char *regex_)
                        system_wide = git_etc_gitconfig();
        }
 
-       key = xstrdup(key_);
-       for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
-               *tl = tolower(*tl);
-       for (tl=key; *tl && *tl != '.'; ++tl)
-               *tl = tolower(*tl);
-
        if (use_key_regexp) {
+               char *tl;
+
+               /*
+                * NEEDSWORK: this naive pattern lowercasing obviously does not
+                * work for more complex patterns like "^[^.]*Foo.*bar".
+                * Perhaps we should deprecate this altogether someday.
+                */
+
+               key = xstrdup(key_);
+               for (tl = key + strlen(key) - 1;
+                    tl >= key && *tl != '.';
+                    tl--)
+                       *tl = tolower(*tl);
+               for (tl = key; *tl && *tl != '.'; tl++)
+                       *tl = tolower(*tl);
+
                key_regexp = (regex_t*)xmalloc(sizeof(regex_t));
                if (regcomp(key_regexp, key, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid key pattern: %s\n", key_);
+                       free(key);
                        goto free_strings;
                }
+       } else {
+               if (git_config_parse_key(key_, &key, NULL))
+                       goto free_strings;
        }
 
        if (regex_) {
@@ -500,3 +513,9 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 
        return 0;
 }
+
+int cmd_repo_config(int argc, const char **argv, const char *prefix)
+{
+       fprintf(stderr, "WARNING: git repo-config is deprecated in favor of git config.\n");
+       return cmd_config(argc, argv, prefix);
+}
index d12de8f392fc660c5048062f2a754a9f63e897fb..4c9deb28ec15d0c2adae795cc2c177b24256e585 100644 (file)
@@ -330,8 +330,11 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                        else if (!strcmp(arg, "--cached") ||
                                 !strcmp(arg, "--staged")) {
                                add_head_to_pending(&rev);
-                               if (!rev.pending.nr)
-                                       die("No HEAD commit to compare with (yet)");
+                               if (!rev.pending.nr) {
+                                       struct tree *tree;
+                                       tree = lookup_tree((const unsigned char*)EMPTY_TREE_SHA1_BIN);
+                                       add_pending_object(&rev, &tree->object, "HEAD");
+                               }
                                break;
                        }
                }
index ba57457cc54812ba935c5259277e12d72463f63f..daf19451ba7df541d1314c880679749232ccce35 100644 (file)
@@ -619,9 +619,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, "mode",
                             "select handling of tags that tag filtered objects",
                             parse_opt_tag_of_filtered_mode),
-               OPT_STRING(0, "export-marks", &export_filename, "FILE",
+               OPT_STRING(0, "export-marks", &export_filename, "file",
                             "Dump marks to this file"),
-               OPT_STRING(0, "import-marks", &import_filename, "FILE",
+               OPT_STRING(0, "import-marks", &import_filename, "file",
                             "Import marks from this file"),
                OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
                             "Fake a tagger when tags lack one"),
index 357f3cdbbfd601e2ce3f1261a4d6b30c1257cda4..7efecfe1cf6b14a53d619b4bcc4760c81a492747 100644 (file)
@@ -49,7 +49,7 @@ static struct option builtin_fetch_options[] = {
                    "fetch from all remotes"),
        OPT_BOOLEAN('a', "append", &append,
                    "append to .git/FETCH_HEAD instead of overwriting"),
-       OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
+       OPT_STRING(0, "upload-pack", &upload_pack, "path",
                   "path to upload pack on remote end"),
        OPT__FORCE(&force, "force overwrite of local branch"),
        OPT_BOOLEAN('m', "multiple", &multiple,
@@ -69,9 +69,9 @@ static struct option builtin_fetch_options[] = {
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
                    "allow updating of HEAD ref"),
        OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
-       OPT_STRING(0, "depth", &depth, "DEPTH",
+       OPT_STRING(0, "depth", &depth, "depth",
                   "deepen history of shallow clone"),
-       { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "DIR",
+       { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "dir",
                   "prepend this to submodule path output", PARSE_OPT_HIDDEN },
        OPT_END()
 };
index 6d5ebca7a9bc9333ea8e493057ecce1b4814c252..795aba087fe9c6ce0e644526d662a561b7f13f31 100644 (file)
@@ -74,7 +74,13 @@ static int mark_object(struct object *obj, int type, void *data)
 {
        struct object *parent = data;
 
+       /*
+        * The only case data is NULL or type is OBJ_ANY is when
+        * mark_object_reachable() calls us.  All the callers of
+        * that function has non-NULL obj hence ...
+        */
        if (!obj) {
+               /* ... these references to parent->fld are safe here */
                printf("broken link from %7s %s\n",
                           typename(parent->type), sha1_to_hex(parent->sha1));
                printf("broken link from %7s %s\n",
@@ -84,6 +90,7 @@ static int mark_object(struct object *obj, int type, void *data)
        }
 
        if (type != OBJ_ANY && obj->type != type)
+               /* ... and the reference to parent is safe here */
                objerror(parent, "wrong object type in link");
 
        if (obj->flags & REACHABLE)
@@ -109,7 +116,7 @@ static void mark_object_reachable(struct object *obj)
        mark_object(obj, OBJ_ANY, NULL);
 }
 
-static int traverse_one_object(struct object *obj, struct object *parent)
+static int traverse_one_object(struct object *obj)
 {
        int result;
        struct tree *tree = NULL;
@@ -138,7 +145,7 @@ static int traverse_reachable(void)
                entry = pending.objects + --pending.nr;
                obj = entry->item;
                parent = (struct object *) entry->name;
-               result |= traverse_one_object(obj, parent);
+               result |= traverse_one_object(obj);
        }
        return !!result;
 }
@@ -385,10 +392,20 @@ static void add_sha1_list(unsigned char *sha1, unsigned long ino)
        sha1_list.nr = ++nr;
 }
 
+static inline int is_loose_object_file(struct dirent *de,
+                                      char *name, unsigned char *sha1)
+{
+       if (strlen(de->d_name) != 38)
+               return 0;
+       memcpy(name + 2, de->d_name, 39);
+       return !get_sha1_hex(name, sha1);
+}
+
 static void fsck_dir(int i, char *path)
 {
        DIR *dir = opendir(path);
        struct dirent *de;
+       char name[100];
 
        if (!dir)
                return;
@@ -396,17 +413,13 @@ static void fsck_dir(int i, char *path)
        if (verbose)
                fprintf(stderr, "Checking directory %s\n", path);
 
+       sprintf(name, "%02x", i);
        while ((de = readdir(dir)) != NULL) {
-               char name[100];
                unsigned char sha1[20];
 
                if (is_dot_or_dotdot(de->d_name))
                        continue;
-               if (strlen(de->d_name) == 38) {
-                       sprintf(name, "%02x", i);
-                       memcpy(name+2, de->d_name, 39);
-                       if (get_sha1_hex(name, sha1) < 0)
-                               break;
+               if (is_loose_object_file(de, name, sha1)) {
                        add_sha1_list(sha1, DIRENT_SORT_HINT(de));
                        continue;
                }
@@ -556,8 +569,8 @@ static int fsck_cache_tree(struct cache_tree *it)
                              sha1_to_hex(it->sha1));
                        return 1;
                }
-               mark_object_reachable(obj);
                obj->used = 1;
+               mark_object_reachable(obj);
                if (obj->type != OBJ_TREE)
                        err |= objerror(obj, "non-tree in cache-tree");
        }
index 080af1a01b8155680faf6c04101217b60ae7b919..c90acddcb2c32ce5170a220c9b1af96b44552a41 100644 (file)
@@ -15,7 +15,7 @@ static void hash_fd(int fd, const char *type, int write_object, const char *path
        struct stat st;
        unsigned char sha1[20];
        if (fstat(fd, &st) < 0 ||
-           index_fd(sha1, fd, &st, write_object, type_from_string(type), path))
+           index_fd(sha1, fd, &st, write_object, type_from_string(type), path, 1))
                die(write_object
                    ? "Unable to add %s to database"
                    : "Unable to hash %s", path);
index e3af9eaa877089639d6ba4f47d7155a0c1ee106e..4f5348eec670faaa62b24fff30b477ddd42acbc6 100644 (file)
@@ -419,7 +419,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
        unsigned int flags = 0;
        const struct option init_db_options[] = {
                OPT_STRING(0, "template", &template_dir, "template-directory",
-                               "provide the directory from which templates will be used"),
+                               "directory from which templates will be used"),
                OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
                                "create a bare repository", 1),
                { OPTION_CALLBACK, 0, "shared", &init_shared_repository,
index 42fff387e69d9b5412e8e776aed3272b78ebe758..a89ddbb65578182d1d0cbd9b0104889a6dc05ac8 100644 (file)
@@ -195,7 +195,7 @@ static struct option builtin_merge_options[] = {
        OPT_CALLBACK('X', "strategy-option", &xopts, "option=value",
                "option for selected merge strategy", option_parse_x),
        OPT_CALLBACK('m', "message", &merge_msg, "message",
-               "message to be used for the merge commit (if any)",
+               "merge commit message (for a non-fast-forward merge)",
                option_parse_message),
        OPT__VERBOSITY(&verbosity),
        OPT_BOOLEAN(0, "abort", &abort_current_merge,
@@ -582,7 +582,8 @@ static void write_tree_trivial(unsigned char *sha1)
                die("git write-tree failed to write a tree");
 }
 
-int try_merge_command(const char *strategy, struct commit_list *common,
+int try_merge_command(const char *strategy, size_t xopts_nr,
+                     const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
 {
        const char **args;
@@ -680,7 +681,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                rollback_lock_file(lock);
                return clean ? 0 : 1;
        } else {
-               return try_merge_command(strategy, common, head_arg, remoteheads);
+               return try_merge_command(strategy, xopts_nr, xopts,
+                                               common, head_arg, remoteheads);
        }
 }
 
@@ -795,6 +797,32 @@ static void add_strategies(const char *string, unsigned attr)
 
 }
 
+static void write_merge_msg(void)
+{
+       int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
+       if (fd < 0)
+               die_errno("Could not open '%s' for writing",
+                         git_path("MERGE_MSG"));
+       if (write_in_full(fd, merge_msg.buf, merge_msg.len) != merge_msg.len)
+               die_errno("Could not write to '%s'", git_path("MERGE_MSG"));
+       close(fd);
+}
+
+static void read_merge_msg(void)
+{
+       strbuf_reset(&merge_msg);
+       if (strbuf_read_file(&merge_msg, git_path("MERGE_MSG"), 0) < 0)
+               die_errno("Could not read from '%s'", git_path("MERGE_MSG"));
+}
+
+static void run_prepare_commit_msg(void)
+{
+       write_merge_msg();
+       run_hook(get_index_file(), "prepare-commit-msg",
+                git_path("MERGE_MSG"), "merge", NULL, NULL);
+       read_merge_msg();
+}
+
 static int merge_trivial(void)
 {
        unsigned char result_tree[20], result_commit[20];
@@ -806,6 +834,7 @@ static int merge_trivial(void)
        parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
+       run_prepare_commit_msg();
        commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
        finish(result_commit, "In-index merge");
        drop_save();
@@ -835,6 +864,7 @@ static int finish_automerge(struct commit_list *common,
        }
        free_commit_list(remoteheads);
        strbuf_addch(&merge_msg, '\n');
+       run_prepare_commit_msg();
        commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
        strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
        finish(result_commit, buf.buf);
@@ -1316,14 +1346,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        die_errno("Could not write to '%s'", git_path("MERGE_HEAD"));
                close(fd);
                strbuf_addch(&merge_msg, '\n');
-               fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
-               if (fd < 0)
-                       die_errno("Could not open '%s' for writing",
-                                 git_path("MERGE_MSG"));
-               if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
-                       merge_msg.len)
-                       die_errno("Could not write to '%s'", git_path("MERGE_MSG"));
-               close(fd);
+               write_merge_msg();
                fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
                if (fd < 0)
                        die_errno("Could not open '%s' for writing",
index 4d5556e2cb5bccaf0100d9f5019e1a1c493e35e7..0aab150c52c839512b82b5efc6e978b7a80653e3 100644 (file)
@@ -537,16 +537,16 @@ static int add(int argc, const char **argv, const char *prefix)
        const unsigned char *note;
        struct msg_arg msg = { 0, 0, STRBUF_INIT };
        struct option options[] = {
-               { OPTION_CALLBACK, 'm', "message", &msg, "MSG",
+               { OPTION_CALLBACK, 'm', "message", &msg, "msg",
                        "note contents as a string", PARSE_OPT_NONEG,
                        parse_msg_arg},
-               { OPTION_CALLBACK, 'F', "file", &msg, "FILE",
+               { OPTION_CALLBACK, 'F', "file", &msg, "file",
                        "note contents in a file", PARSE_OPT_NONEG,
                        parse_file_arg},
-               { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT",
+               { OPTION_CALLBACK, 'c', "reedit-message", &msg, "object",
                        "reuse and edit specified note object", PARSE_OPT_NONEG,
                        parse_reedit_arg},
-               { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
+               { OPTION_CALLBACK, 'C', "reuse-message", &msg, "object",
                        "reuse specified note object", PARSE_OPT_NONEG,
                        parse_reuse_arg},
                OPT__FORCE(&force, "replace existing notes"),
@@ -682,16 +682,16 @@ static int append_edit(int argc, const char **argv, const char *prefix)
        const char * const *usage;
        struct msg_arg msg = { 0, 0, STRBUF_INIT };
        struct option options[] = {
-               { OPTION_CALLBACK, 'm', "message", &msg, "MSG",
+               { OPTION_CALLBACK, 'm', "message", &msg, "msg",
                        "note contents as a string", PARSE_OPT_NONEG,
                        parse_msg_arg},
-               { OPTION_CALLBACK, 'F', "file", &msg, "FILE",
+               { OPTION_CALLBACK, 'F', "file", &msg, "file",
                        "note contents in a file", PARSE_OPT_NONEG,
                        parse_file_arg},
-               { OPTION_CALLBACK, 'c', "reedit-message", &msg, "OBJECT",
+               { OPTION_CALLBACK, 'c', "reedit-message", &msg, "object",
                        "reuse and edit specified note object", PARSE_OPT_NONEG,
                        parse_reedit_arg},
-               { OPTION_CALLBACK, 'C', "reuse-message", &msg, "OBJECT",
+               { OPTION_CALLBACK, 'C', "reuse-message", &msg, "object",
                        "reuse specified note object", PARSE_OPT_NONEG,
                        parse_reuse_arg},
                OPT_END()
index 512530022edac398f8541ee6c400c7312659e730..49a0472a9bd28274c4be1352996e700a1db4b94a 100644 (file)
@@ -73,6 +73,8 @@ int get_one_patchid(unsigned char *next_sha1, git_SHA_CTX *ctx)
                        p += 7;
                else if (!memcmp(line, "From ", 5))
                        p += 5;
+               else if (!memcmp(line, "\\ ", 2) && 12 < strlen(line))
+                       continue;
 
                if (!get_sha1_hex(p, next_sha1)) {
                        found_next = 1;
index e655eb7695faba13c4d9e8f25b9649ffec7195be..31da418cf4a9e4732edb8201c80b07f7ddadbac1 100644 (file)
@@ -64,17 +64,17 @@ static void set_refspecs(const char **refs, int nr)
        }
 }
 
-static void setup_push_tracking(void)
+static void setup_push_upstream(void)
 {
        struct strbuf refspec = STRBUF_INIT;
        struct branch *branch = branch_get(NULL);
        if (!branch)
                die("You are not currently on a branch.");
        if (!branch->merge_nr || !branch->merge)
-               die("The current branch %s is not tracking anything.",
+               die("The current branch %s has no upstream branch.",
                    branch->name);
        if (branch->merge_nr != 1)
-               die("The current branch %s is tracking multiple branches, "
+               die("The current branch %s has multiple upstream branches, "
                    "refusing to push.", branch->name);
        strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
        add_refspec(refspec.buf);
@@ -88,8 +88,8 @@ static void setup_default_push_refspecs(void)
                add_refspec(":");
                break;
 
-       case PUSH_DEFAULT_TRACKING:
-               setup_push_tracking();
+       case PUSH_DEFAULT_UPSTREAM:
+               setup_push_upstream();
                break;
 
        case PUSH_DEFAULT_CURRENT:
index 73c89ed15ba2fb0c470141499c3390b77cf1d81c..93c92814cf28855aee87cabf4a3b906e11a36125 100644 (file)
@@ -104,8 +104,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        struct unpack_trees_options opts;
        int prefix_set = 0;
        const struct option read_tree_options[] = {
-               { OPTION_CALLBACK, 0, "index-output", NULL, "FILE",
-                 "write resulting index to <FILE>",
+               { OPTION_CALLBACK, 0, "index-output", NULL, "file",
+                 "write resulting index to <file>",
                  PARSE_OPT_NONEG, index_output_cb },
                OPT_SET_INT(0, "empty", &read_empty,
                            "only empty the index", 1),
index 642bf35587ed994948d3eeac0a189ae29e39bf11..67cbfeb152386a1c918a103a2073d83138bfdff0 100644 (file)
@@ -8,7 +8,7 @@
 #include "xdiff-interface.h"
 
 static const char * const rerere_usage[] = {
-       "git rerere [clear | status | diff | gc]",
+       "git rerere [clear | status | remaining | diff | gc]",
        NULL,
 };
 
@@ -156,7 +156,17 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
        else if (!strcmp(argv[0], "status"))
                for (i = 0; i < merge_rr.nr; i++)
                        printf("%s\n", merge_rr.items[i].string);
-       else if (!strcmp(argv[0], "diff"))
+       else if (!strcmp(argv[0], "remaining")) {
+               rerere_remaining(&merge_rr);
+               for (i = 0; i < merge_rr.nr; i++) {
+                       if (merge_rr.items[i].util != RERERE_RESOLVED)
+                               printf("%s\n", merge_rr.items[i].string);
+                       else
+                               /* prepare for later call to
+                                * string_list_clear() */
+                               merge_rr.items[i].util = NULL;
+               }
+       } else if (!strcmp(argv[0], "diff"))
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *path = merge_rr.items[i].string;
                        const char *name = (const char *)merge_rr.items[i].util;
index bb6e9e83b756b47dae449064ca7762fe3558fc0e..dc1b702edc6b4bd95ebf41c12bf1da6fe9d6850d 100644 (file)
@@ -44,7 +44,11 @@ static const char **commit_argv;
 static int allow_rerere_auto;
 
 static const char *me;
+
+/* Merge strategy. */
 static const char *strategy;
+static const char **xopts;
+static size_t xopts_nr, xopts_alloc;
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -55,6 +59,17 @@ static const char * const *revert_or_cherry_pick_usage(void)
        return action == REVERT ? revert_usage : cherry_pick_usage;
 }
 
+static int option_parse_x(const struct option *opt,
+                         const char *arg, int unset)
+{
+       if (unset)
+               return 0;
+
+       ALLOC_GROW(xopts, xopts_nr + 1, xopts_alloc);
+       xopts[xopts_nr++] = xstrdup(arg);
+       return 0;
+}
+
 static void parse_args(int argc, const char **argv)
 {
        const char * const * usage_str = revert_or_cherry_pick_usage();
@@ -67,6 +82,8 @@ static void parse_args(int argc, const char **argv)
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
                OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
                OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
+               OPT_CALLBACK('X', "strategy-option", &xopts, "option",
+                       "option for merge strategy", option_parse_x),
                OPT_END(),
                OPT_END(),
                OPT_END(),
@@ -311,18 +328,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
        int clean, index_fd;
+       const char **xopt;
        static struct lock_file index_lock;
 
        index_fd = hold_locked_index(&index_lock, 1);
 
        read_cache();
 
-       /*
-        * NEEDSWORK: cherry-picking between branches with
-        * different end-of-line normalization is a pain;
-        * plumb in an option to set o.renormalize?
-        * (or better: arbitrary -X options)
-        */
        init_merge_options(&o);
        o.ancestor = base ? base_label : "(empty tree)";
        o.branch1 = "HEAD";
@@ -332,6 +344,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
        next_tree = next ? next->tree : empty_tree();
        base_tree = base ? base->tree : empty_tree();
 
+       for (xopt = xopts; xopt != xopts + xopts_nr; xopt++)
+               parse_merge_opt(&o, *xopt);
+
        clean = merge_trees(&o,
                            head_tree,
                            next_tree, base_tree, &result);
@@ -503,7 +518,7 @@ static int do_pick_commit(void)
 
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
-               res = try_merge_command(strategy, common,
+               res = try_merge_command(strategy, xopts_nr, xopts, common,
                                        sha1_to_hex(head), remotes);
                free_commit_list(common);
                free_commit_list(remotes);
index aa1f87d47a3e4bdaa2ca3711c9dfc495fc2247e0..7cf48abca857f049243dfab7c95fea70d216ea93 100644 (file)
@@ -376,9 +376,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_GROUP("Tag creation options"),
                OPT_BOOLEAN('a', NULL, &annotate,
                                        "annotated tag, needs a message"),
-               OPT_CALLBACK('m', NULL, &msg, "msg",
-                            "message for the tag", parse_msg_arg),
-               OPT_FILENAME('F', NULL, &msgfile, "message in a file"),
+               OPT_CALLBACK('m', NULL, &msg, "message",
+                            "tag message", parse_msg_arg),
+               OPT_FILENAME('F', NULL, &msgfile, "read message from file"),
                OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
                OPT_STRING('u', NULL, &keyid, "key-id",
                                        "use another key to sign the tag"),
index 65ea26bdb8c2cef671030c3a62d87d84612e8144..f48fd7d4c1c706b68e879e4dfabdd3fb8597a328 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -200,7 +200,7 @@ int create_bundle(struct bundle_header *header, const char *path,
        int bundle_fd = -1;
        int bundle_to_stdout;
        const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
-       const char **argv_pack = xmalloc(5 * sizeof(const char *));
+       const char **argv_pack = xmalloc(6 * sizeof(const char *));
        int i, ref_count = 0;
        char buffer[1024];
        struct rev_info revs;
@@ -346,7 +346,8 @@ int create_bundle(struct bundle_header *header, const char *path,
        argv_pack[1] = "--all-progress-implied";
        argv_pack[2] = "--stdout";
        argv_pack[3] = "--thin";
-       argv_pack[4] = NULL;
+       argv_pack[4] = "--delta-base-offset";
+       argv_pack[5] = NULL;
        memset(&rls, 0, sizeof(rls));
        rls.argv = argv_pack;
        rls.in = -1;
diff --git a/cache.h b/cache.h
index 4beb2dc6ff875322f970eccbf3980746527a91ab..08a902210c6374c65f94a1238d895a65c7cde43a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -516,7 +516,7 @@ struct pathspec {
 extern int init_pathspec(struct pathspec *, const char **);
 extern void free_pathspec(struct pathspec *);
 extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
-extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
+extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
@@ -526,7 +526,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 #define REFRESH_IGNORE_MISSING 0x0008  /* ignore non-existent */
 #define REFRESH_IGNORE_SUBMODULES      0x0010  /* ignore submodules */
 #define REFRESH_IN_PORCELAIN   0x0020  /* user friendly output, not "needs update" */
-extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg);
+extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
 
 struct lock_file {
        struct lock_file *next;
@@ -623,7 +623,7 @@ enum rebase_setup_type {
 enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
-       PUSH_DEFAULT_TRACKING,
+       PUSH_DEFAULT_UPSTREAM,
        PUSH_DEFAULT_CURRENT
 };
 
@@ -691,9 +691,11 @@ static inline void hashclr(unsigned char *hash)
 
 #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
-#define EMPTY_TREE_SHA1_BIN \
+#define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
+#define EMPTY_TREE_SHA1_BIN \
+        ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
 
 int git_mkstemp(char *path, size_t n, const char *template);
 
@@ -1012,6 +1014,7 @@ extern int git_config_maybe_bool(const char *, const char *);
 extern int git_config_string(const char **, const char *, const char *);
 extern int git_config_pathname(const char **, const char *, const char *);
 extern int git_config_set(const char *, const char *);
+extern int git_config_parse_key(const char *, char **, int *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
 extern int git_config_rename_section(const char *, const char *);
 extern const char *git_etc_gitconfig(void);
index 74d66018800d7a2a2b3a3365e87a23049690da6b..ac337c7d7dc1724fa918f9340816d3102edb10bd 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -245,10 +245,10 @@ int unregister_shallow(const unsigned char *sha1)
        return 0;
 }
 
-int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
+int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
 {
-       char *tail = buffer;
-       char *bufptr = buffer;
+       const char *tail = buffer;
+       const char *bufptr = buffer;
        unsigned char parent[20];
        struct commit_list **pptr;
        struct commit_graft *graft;
index eb6c5af1f6b18546b3b3f1683142b15bb0ae9e34..659c87c3ee90c23064f617e3bcf3ace12e32ea04 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -38,7 +38,7 @@ struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
                                              int quiet);
 struct commit *lookup_commit_reference_by_name(const char *name);
 
-int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
+int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
 int parse_commit(struct commit *item);
 
 /* Find beginning and length of commit subject. */
index bee605441910caeddd1e94eb7795f615a3d1348a..878b1de97c6c437857e7a29e1b9afc677610f1fe 100644 (file)
@@ -2,6 +2,9 @@
 #include "win32.h"
 #include <conio.h>
 #include "../strbuf.h"
+#include "../run-command.h"
+
+static const int delay[] = { 0, 1, 10, 20, 40 };
 
 int err_win_to_posix(DWORD winerr)
 {
@@ -116,6 +119,165 @@ int err_win_to_posix(DWORD winerr)
        return error;
 }
 
+static inline int is_file_in_use_error(DWORD errcode)
+{
+       switch (errcode) {
+       case ERROR_SHARING_VIOLATION:
+       case ERROR_ACCESS_DENIED:
+               return 1;
+       }
+
+       return 0;
+}
+
+static int read_yes_no_answer(void)
+{
+       char answer[1024];
+
+       if (fgets(answer, sizeof(answer), stdin)) {
+               size_t answer_len = strlen(answer);
+               int got_full_line = 0, c;
+
+               /* remove the newline */
+               if (answer_len >= 2 && answer[answer_len-2] == '\r') {
+                       answer[answer_len-2] = '\0';
+                       got_full_line = 1;
+               } else if (answer_len >= 1 && answer[answer_len-1] == '\n') {
+                       answer[answer_len-1] = '\0';
+                       got_full_line = 1;
+               }
+               /* flush the buffer in case we did not get the full line */
+               if (!got_full_line)
+                       while ((c = getchar()) != EOF && c != '\n')
+                               ;
+       } else
+               /* we could not read, return the
+                * default answer which is no */
+               return 0;
+
+       if (tolower(answer[0]) == 'y' && !answer[1])
+               return 1;
+       if (!strncasecmp(answer, "yes", sizeof(answer)))
+               return 1;
+       if (tolower(answer[0]) == 'n' && !answer[1])
+               return 0;
+       if (!strncasecmp(answer, "no", sizeof(answer)))
+               return 0;
+
+       /* did not find an answer we understand */
+       return -1;
+}
+
+static int ask_yes_no_if_possible(const char *format, ...)
+{
+       char question[4096];
+       const char *retry_hook[] = { NULL, NULL, NULL };
+       va_list args;
+
+       va_start(args, format);
+       vsnprintf(question, sizeof(question), format, args);
+       va_end(args);
+
+       if ((retry_hook[0] = getenv("GIT_ASK_YESNO"))) {
+               retry_hook[1] = question;
+               return !run_command_v_opt(retry_hook, 0);
+       }
+
+       if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))
+               return 0;
+
+       while (1) {
+               int answer;
+               fprintf(stderr, "%s (y/n) ", question);
+
+               if ((answer = read_yes_no_answer()) >= 0)
+                       return answer;
+
+               fprintf(stderr, "Sorry, I did not understand your answer. "
+                               "Please type 'y' or 'n'\n");
+       }
+}
+
+#undef unlink
+int mingw_unlink(const char *pathname)
+{
+       int ret, tries = 0;
+
+       /* read-only files cannot be removed */
+       chmod(pathname, 0666);
+       while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
+               if (!is_file_in_use_error(GetLastError()))
+                       break;
+               /*
+                * We assume that some other process had the source or
+                * destination file open at the wrong moment and retry.
+                * In order to give the other process a higher chance to
+                * complete its operation, we give up our time slice now.
+                * If we have to retry again, we do sleep a bit.
+                */
+               Sleep(delay[tries]);
+               tries++;
+       }
+       while (ret == -1 && is_file_in_use_error(GetLastError()) &&
+              ask_yes_no_if_possible("Unlink of file '%s' failed. "
+                       "Should I try again?", pathname))
+              ret = unlink(pathname);
+       return ret;
+}
+
+static int is_dir_empty(const char *path)
+{
+       struct strbuf buf = STRBUF_INIT;
+       WIN32_FIND_DATAA findbuf;
+       HANDLE handle;
+
+       strbuf_addf(&buf, "%s\\*", path);
+       handle = FindFirstFileA(buf.buf, &findbuf);
+       if (handle == INVALID_HANDLE_VALUE) {
+               strbuf_release(&buf);
+               return GetLastError() == ERROR_NO_MORE_FILES;
+       }
+
+       while (!strcmp(findbuf.cFileName, ".") ||
+                       !strcmp(findbuf.cFileName, ".."))
+               if (!FindNextFile(handle, &findbuf)) {
+                       strbuf_release(&buf);
+                       return GetLastError() == ERROR_NO_MORE_FILES;
+               }
+       FindClose(handle);
+       strbuf_release(&buf);
+       return 0;
+}
+
+#undef rmdir
+int mingw_rmdir(const char *pathname)
+{
+       int ret, tries = 0;
+
+       while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) {
+               if (!is_file_in_use_error(GetLastError()))
+                       break;
+               if (!is_dir_empty(pathname)) {
+                       errno = ENOTEMPTY;
+                       break;
+               }
+               /*
+                * We assume that some other process had the source or
+                * destination file open at the wrong moment and retry.
+                * In order to give the other process a higher chance to
+                * complete its operation, we give up our time slice now.
+                * If we have to retry again, we do sleep a bit.
+                */
+               Sleep(delay[tries]);
+               tries++;
+       }
+       while (ret == -1 && is_file_in_use_error(GetLastError()) &&
+              ask_yes_no_if_possible("Deletion of directory '%s' failed. "
+                       "Should I try again?", pathname))
+              ret = rmdir(pathname);
+       return ret;
+}
+
 #undef open
 int mingw_open (const char *filename, int oflags, ...)
 {
@@ -1249,7 +1411,6 @@ int mingw_rename(const char *pold, const char *pnew)
 {
        DWORD attrs, gle;
        int tries = 0;
-       static const int delay[] = { 0, 1, 10, 20, 40 };
 
        /*
         * Try native rename() first to get errno right.
@@ -1291,6 +1452,11 @@ repeat:
                tries++;
                goto repeat;
        }
+       if (gle == ERROR_ACCESS_DENIED &&
+              ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
+                      "Should I try again?", pold, pnew))
+               goto repeat;
+
        errno = EACCES;
        return -1;
 }
index cafc1eb08a71dd60566a7389ed606e777cf17fc8..fe6ba340437b67237afd8cfdb4cf2a20074d27f7 100644 (file)
@@ -119,14 +119,6 @@ static inline int mingw_mkdir(const char *path, int mode)
 }
 #define mkdir mingw_mkdir
 
-static inline int mingw_unlink(const char *pathname)
-{
-       /* read-only files cannot be removed */
-       chmod(pathname, 0666);
-       return unlink(pathname);
-}
-#define unlink mingw_unlink
-
 #define WNOHANG 1
 pid_t waitpid(pid_t pid, int *status, unsigned options);
 
@@ -174,6 +166,12 @@ int link(const char *oldpath, const char *newpath);
  * replacements of existing functions
  */
 
+int mingw_unlink(const char *pathname);
+#define unlink mingw_unlink
+
+int mingw_rmdir(const char *path);
+#define rmdir mingw_rmdir
+
 int mingw_open (const char *filename, int oflags, ...);
 #define open mingw_open
 
index 625e0518767712583f917762634c2fc852c4d2eb..31f778b5e8999bd898a18d8ea121c23a01fed352 100644 (file)
--- a/config.c
+++ b/config.c
@@ -737,8 +737,10 @@ static int git_default_push_config(const char *var, const char *value)
                        push_default = PUSH_DEFAULT_NOTHING;
                else if (!strcmp(value, "matching"))
                        push_default = PUSH_DEFAULT_MATCHING;
-               else if (!strcmp(value, "tracking"))
-                       push_default = PUSH_DEFAULT_TRACKING;
+               else if (!strcmp(value, "upstream"))
+                       push_default = PUSH_DEFAULT_UPSTREAM;
+               else if (!strcmp(value, "tracking")) /* deprecated */
+                       push_default = PUSH_DEFAULT_UPSTREAM;
                else if (!strcmp(value, "current"))
                        push_default = PUSH_DEFAULT_CURRENT;
                else {
@@ -1098,6 +1100,75 @@ int git_config_set(const char *key, const char *value)
        return git_config_set_multivar(key, value, NULL, 0);
 }
 
+/*
+ * Auxiliary function to sanity-check and split the key into the section
+ * identifier and variable name.
+ *
+ * Returns 0 on success, -1 when there is an invalid character in the key and
+ * -2 if there is no section name in the key.
+ *
+ * store_key - pointer to char* which will hold a copy of the key with
+ *             lowercase section and variable name
+ * baselen - pointer to int which will hold the length of the
+ *           section + subsection part, can be NULL
+ */
+int git_config_parse_key(const char *key, char **store_key, int *baselen_)
+{
+       int i, dot, baselen;
+       const char *last_dot = strrchr(key, '.');
+
+       /*
+        * Since "key" actually contains the section name and the real
+        * key name separated by a dot, we have to know where the dot is.
+        */
+
+       if (last_dot == NULL || last_dot == key) {
+               error("key does not contain a section: %s", key);
+               return -2;
+       }
+
+       if (!last_dot[1]) {
+               error("key does not contain variable name: %s", key);
+               return -2;
+       }
+
+       baselen = last_dot - key;
+       if (baselen_)
+               *baselen_ = baselen;
+
+       /*
+        * Validate the key and while at it, lower case it for matching.
+        */
+       *store_key = xmalloc(strlen(key) + 1);
+
+       dot = 0;
+       for (i = 0; key[i]; i++) {
+               unsigned char c = key[i];
+               if (c == '.')
+                       dot = 1;
+               /* Leave the extended basename untouched.. */
+               if (!dot || i > baselen) {
+                       if (!iskeychar(c) ||
+                           (i == baselen + 1 && !isalpha(c))) {
+                               error("invalid key: %s", key);
+                               goto out_free_ret_1;
+                       }
+                       c = tolower(c);
+               } else if (c == '\n') {
+                       error("invalid key (newline): %s", key);
+                       goto out_free_ret_1;
+               }
+               (*store_key)[i] = c;
+       }
+       (*store_key)[i] = 0;
+
+       return 0;
+
+out_free_ret_1:
+       free(*store_key);
+       return -1;
+}
+
 /*
  * If value==NULL, unset in (remove from) config,
  * if value_regex!=NULL, disregard key/value pairs where value does not match.
@@ -1124,59 +1195,23 @@ int git_config_set(const char *key, const char *value)
 int git_config_set_multivar(const char *key, const char *value,
        const char *value_regex, int multi_replace)
 {
-       int i, dot;
        int fd = -1, in_fd;
        int ret;
        char *config_filename;
        struct lock_file *lock = NULL;
-       const char *last_dot = strrchr(key, '.');
 
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
        else
                config_filename = git_pathdup("config");
 
-       /*
-        * Since "key" actually contains the section name and the real
-        * key name separated by a dot, we have to know where the dot is.
-        */
-
-       if (last_dot == NULL) {
-               error("key does not contain a section: %s", key);
-               ret = 2;
+       /* parse-key returns negative; flip the sign to feed exit(3) */
+       ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
+       if (ret)
                goto out_free;
-       }
-       store.baselen = last_dot - key;
 
        store.multi_replace = multi_replace;
 
-       /*
-        * Validate the key and while at it, lower case it for matching.
-        */
-       store.key = xmalloc(strlen(key) + 1);
-       dot = 0;
-       for (i = 0; key[i]; i++) {
-               unsigned char c = key[i];
-               if (c == '.')
-                       dot = 1;
-               /* Leave the extended basename untouched.. */
-               if (!dot || i > store.baselen) {
-                       if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
-                               error("invalid key: %s", key);
-                               free(store.key);
-                               ret = 1;
-                               goto out_free;
-                       }
-                       c = tolower(c);
-               } else if (c == '\n') {
-                       error("invalid key (newline): %s", key);
-                       free(store.key);
-                       ret = 1;
-                       goto out_free;
-               }
-               store.key[i] = c;
-       }
-       store.key[i] = 0;
 
        /*
         * The lock serves a purpose in addition to locking: the new
index 5792425a4948148b5a110346f5b830063dd78914..20039c546f1cc91575771acbe6f52de6778bea89 100644 (file)
@@ -345,7 +345,7 @@ esac
 AC_CACHE_CHECK([if linker supports -R], git_cv_ld_dashr, [
    SAVE_LDFLAGS="${LDFLAGS}"
    LDFLAGS="${SAVE_LDFLAGS} -R /"
-   AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_dashr=yes], [git_cv_ld_dashr=no])
+   AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_dashr=yes], [git_cv_ld_dashr=no])
    LDFLAGS="${SAVE_LDFLAGS}"
 ])
 if test "$git_cv_ld_dashr" = "yes"; then
@@ -354,7 +354,7 @@ else
    AC_CACHE_CHECK([if linker supports -Wl,-rpath,], git_cv_ld_wl_rpath, [
       SAVE_LDFLAGS="${LDFLAGS}"
       LDFLAGS="${SAVE_LDFLAGS} -Wl,-rpath,/"
-      AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_wl_rpath=yes], [git_cv_ld_wl_rpath=no])
+      AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_wl_rpath=yes], [git_cv_ld_wl_rpath=no])
       LDFLAGS="${SAVE_LDFLAGS}"
    ])
    if test "$git_cv_ld_wl_rpath" = "yes"; then
@@ -363,7 +363,7 @@ else
       AC_CACHE_CHECK([if linker supports -rpath], git_cv_ld_rpath, [
          SAVE_LDFLAGS="${LDFLAGS}"
          LDFLAGS="${SAVE_LDFLAGS} -rpath /"
-         AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [git_cv_ld_rpath=yes], [git_cv_ld_rpath=no])
+         AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])], [git_cv_ld_rpath=yes], [git_cv_ld_rpath=no])
          LDFLAGS="${SAVE_LDFLAGS}"
       ])
       if test "$git_cv_ld_rpath" = "yes"; then
@@ -472,15 +472,9 @@ if test -z "$NO_ICONV"; then
 
 GIT_STASH_FLAGS($ICONVDIR)
 
-AC_DEFUN([ICONVTEST_SRC], [
-#include <iconv.h>
-
-int main(void)
-{
-       iconv_open("", "");
-       return 0;
-}
-])
+AC_DEFUN([ICONVTEST_SRC],
+[AC_LANG_PROGRAM([#include <iconv.h>],
+ [iconv_open("", "");])])
 
 if test -n "$ICONVDIR"; then
    lib_order="-liconv -lc"
@@ -500,7 +494,7 @@ for l in $lib_order; do
     old_LIBS="$LIBS"
     LIBS="$LIBS $l"
     AC_MSG_CHECKING([for iconv in $l])
-    AC_LINK_IFELSE(ICONVTEST_SRC,
+    AC_LINK_IFELSE([ICONVTEST_SRC],
        [AC_MSG_RESULT([yes])
        NO_ICONV=
        break],
@@ -528,18 +522,12 @@ fi
 GIT_STASH_FLAGS($ZLIB_PATH)
 
 AC_DEFUN([ZLIBTEST_SRC], [
-#include <zlib.h>
-
-int main(void)
-{
-       deflateBound(0, 0);
-       return 0;
-}
-])
+AC_LANG_PROGRAM([#include <zlib.h>],
+ [deflateBound(0, 0);])])
 AC_MSG_CHECKING([for deflateBound in -lz])
 old_LIBS="$LIBS"
 LIBS="$LIBS -lz"
-AC_LINK_IFELSE(ZLIBTEST_SRC,
+AC_LINK_IFELSE([ZLIBTEST_SRC],
        [AC_MSG_RESULT([yes])],
        [AC_MSG_RESULT([no])
        NO_DEFLATE_BOUND=yes])
@@ -631,23 +619,19 @@ AC_SUBST(NO_INTTYPES_H)
 #
 # Define OLD_ICONV if your library has an old iconv(), where the second
 # (input buffer pointer) parameter is declared with type (const char **).
-AC_DEFUN([OLDICONVTEST_SRC], [[
+AC_DEFUN([OLDICONVTEST_SRC], [
+AC_LANG_PROGRAM([[
 #include <iconv.h>
 
 extern size_t iconv(iconv_t cd,
                    char **inbuf, size_t *inbytesleft,
                    char **outbuf, size_t *outbytesleft);
-
-int main(void)
-{
-       return 0;
-}
-]])
+]], [])])
 
 GIT_STASH_FLAGS($ICONVDIR)
 
 AC_MSG_CHECKING([for old iconv()])
-AC_COMPILE_IFELSE(OLDICONVTEST_SRC,
+AC_COMPILE_IFELSE([OLDICONVTEST_SRC],
        [AC_MSG_RESULT([no])],
        [AC_MSG_RESULT([yes])
        OLD_ICONV=UnfortunatelyYes])
@@ -931,18 +915,16 @@ AC_SUBST(NO_INITGROUPS)
 #
 # Define PTHREAD_LIBS to the linker flag used for Pthread support.
 AC_DEFUN([PTHREADTEST_SRC], [
+AC_LANG_PROGRAM([[
 #include <pthread.h>
-
-int main(void)
-{
+]], [[
        pthread_mutex_t test_mutex;
        int retcode = 0;
        retcode |= pthread_mutex_init(&test_mutex,(void *)0);
        retcode |= pthread_mutex_lock(&test_mutex);
        retcode |= pthread_mutex_unlock(&test_mutex);
        return retcode;
-}
-])
+]])])
 
 dnl AC_LANG_CONFTEST([AC_LANG_PROGRAM(
 dnl   [[#include <pthread.h>]],
@@ -962,7 +944,7 @@ elif test -z "$PTHREAD_CFLAGS"; then
      old_CFLAGS="$CFLAGS"
      CFLAGS="$opt $CFLAGS"
      AC_MSG_CHECKING([Checking for POSIX Threads with '$opt'])
-     AC_LINK_IFELSE(PTHREADTEST_SRC,
+     AC_LINK_IFELSE([PTHREADTEST_SRC],
        [AC_MSG_RESULT([yes])
                NO_PTHREADS=
                PTHREAD_LIBS="$opt"
@@ -982,7 +964,7 @@ else
   old_CFLAGS="$CFLAGS"
   CFLAGS="$PTHREAD_CFLAGS $CFLAGS"
   AC_MSG_CHECKING([Checking for POSIX Threads with '$PTHREAD_CFLAGS'])
-  AC_LINK_IFELSE(PTHREADTEST_SRC,
+  AC_LINK_IFELSE([PTHREADTEST_SRC],
        [AC_MSG_RESULT([yes])
                NO_PTHREADS=
                PTHREAD_LIBS="$PTHREAD_CFLAGS"
index 60a05a8b978345224c0313edb8060f5a7c2ccb54..6bf155cbdb61498bdaf84712a2dea6fb05361ce2 100755 (executable)
@@ -26,6 +26,7 @@ require_work_tree
 cd_to_toplevel
 
 no_commit=
+xopt=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -44,6 +45,16 @@ do
        -x|--i-really-want-to-expose-my-private-commit-object-name)
                replay=
                ;;
+       -X?*)
+               xopt="$xopt$(git rev-parse --sq-quote "--${1#-X}")"
+               ;;
+       --strategy-option=*)
+               xopt="$xopt$(git rev-parse --sq-quote "--${1#--strategy-option=}")"
+               ;;
+       -X|--strategy-option)
+               shift
+               xopt="$xopt$(git rev-parse --sq-quote "--$1")"
+               ;;
        -*)
                usage
                ;;
@@ -159,7 +170,7 @@ export GITHEAD_$head GITHEAD_$next
 # and $prev on top of us (when reverting), or the change between
 # $prev and $commit on top of us (when cherry-picking or replaying).
 
-git-merge-recursive $base -- $head $next &&
+eval "git merge-recursive $xopt $base -- $head $next" &&
 result=$(git-write-tree 2>/dev/null) || {
        mv -f .msg "$GIT_DIR/MERGE_MSG"
        {
index 04ce7e3b020f489d59fe103970fc989d7b351127..a4f440d11696caa6ee4756108bec78a85d4b04f7 100755 (executable)
@@ -543,13 +543,13 @@ class P4Submit(Command):
         self.options = [
                 optparse.make_option("--verbose", dest="verbose", action="store_true"),
                 optparse.make_option("--origin", dest="origin"),
-                optparse.make_option("-M", dest="detectRename", action="store_true"),
+                optparse.make_option("-M", dest="detectRenames", action="store_true"),
         ]
         self.description = "Submit changes from git to the perforce depot."
         self.usage += " [name of git branch to submit into perforce depot]"
         self.interactive = True
         self.origin = ""
-        self.detectRename = False
+        self.detectRenames = False
         self.verbose = False
         self.isWindows = (platform.system() == "Windows")
 
@@ -613,7 +613,22 @@ class P4Submit(Command):
 
     def applyCommit(self, id):
         print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id))
-        diffOpts = ("", "-M")[self.detectRename]
+
+        if not self.detectRenames:
+            # If not explicitly set check the config variable
+            self.detectRenames = gitConfig("git-p4.detectRenames").lower() == "true"
+
+        if self.detectRenames:
+            diffOpts = "-M"
+        else:
+            diffOpts = ""
+
+        if gitConfig("git-p4.detectCopies").lower() == "true":
+            diffOpts += " -C"
+
+        if gitConfig("git-p4.detectCopiesHarder").lower() == "true":
+            diffOpts += " --find-copies-harder"
+
         diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
         filesToAdd = set()
         filesToDelete = set()
@@ -637,11 +652,23 @@ class P4Submit(Command):
                 filesToDelete.add(path)
                 if path in filesToAdd:
                     filesToAdd.remove(path)
+            elif modifier == "C":
+                src, dest = diff['src'], diff['dst']
+                p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
+                if diff['src_sha1'] != diff['dst_sha1']:
+                    p4_system("edit \"%s\"" % (dest))
+                if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
+                    p4_system("edit \"%s\"" % (dest))
+                    filesToChangeExecBit[dest] = diff['dst_mode']
+                os.unlink(dest)
+                editedFiles.add(dest)
             elif modifier == "R":
                 src, dest = diff['src'], diff['dst']
                 p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
-                p4_system("edit \"%s\"" % (dest))
+                if diff['src_sha1'] != diff['dst_sha1']:
+                    p4_system("edit \"%s\"" % (dest))
                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
+                    p4_system("edit \"%s\"" % (dest))
                     filesToChangeExecBit[dest] = diff['dst_mode']
                 os.unlink(dest)
                 editedFiles.add(dest)
@@ -834,6 +861,8 @@ class P4Submit(Command):
         return True
 
 class P4Sync(Command):
+    delete_actions = ( "delete", "move/delete", "purge" )
+
     def __init__(self):
         Command.__init__(self)
         self.options = [
@@ -882,6 +911,23 @@ class P4Sync(Command):
         if gitConfig("git-p4.syncFromOrigin") == "false":
             self.syncWithOrigin = False
 
+    #
+    # P4 wildcards are not allowed in filenames.  P4 complains
+    # if you simply add them, but you can force it with "-f", in
+    # which case it translates them into %xx encoding internally.
+    # Search for and fix just these four characters.  Do % last so
+    # that fixing it does not inadvertently create new %-escapes.
+    #
+    def wildcard_decode(self, path):
+        # Cannot have * in a filename in windows; untested as to
+        # what p4 would do in such a case.
+        if not self.isWindows:
+            path = path.replace("%2A", "*")
+        path = path.replace("%23", "#") \
+                   .replace("%40", "@") \
+                   .replace("%25", "%")
+        return path
+
     def extractFilesFromCommit(self, commit):
         self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
                              for path in self.cloneExclude]
@@ -910,6 +956,22 @@ class P4Sync(Command):
         return files
 
     def stripRepoPath(self, path, prefixes):
+        if self.useClientSpec:
+
+            # if using the client spec, we use the output directory
+            # specified in the client.  For example, a view
+            #   //depot/foo/branch/... //client/branch/foo/...
+            # will end up putting all foo/branch files into
+            #  branch/foo/
+            for val in self.clientSpecDirs:
+                if path.startswith(val[0]):
+                    # replace the depot path with the client path
+                    path = path.replace(val[0], val[1][1])
+                    # now strip out the client (//client/...)
+                    path = re.sub("^(//[^/]+/)", '', path)
+                    # the rest is all path
+                    return path
+
         if self.keepRepoPath:
             prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])]
 
@@ -960,6 +1022,7 @@ class P4Sync(Command):
            return
 
         relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
+        relPath = self.wildcard_decode(relPath)
         if verbose:
             sys.stderr.write("%s\n" % relPath)
 
@@ -1032,16 +1095,16 @@ class P4Sync(Command):
             includeFile = True
             for val in self.clientSpecDirs:
                 if f['path'].startswith(val[0]):
-                    if val[1] <= 0:
+                    if val[1][0] <= 0:
                         includeFile = False
                     break
 
             if includeFile:
                 filesForCommit.append(f)
-                if f['action'] not in ('delete', 'move/delete', 'purge'):
-                    filesToRead.append(f)
-                else:
+                if f['action'] in self.delete_actions:
                     filesToDelete.append(f)
+                else:
+                    filesToRead.append(f)
 
         # deleted files...
         for f in filesToDelete:
@@ -1127,7 +1190,7 @@ class P4Sync(Command):
 
                 cleanedFiles = {}
                 for info in files:
-                    if info["action"] in ("delete", "purge"):
+                    if info["action"] in self.delete_actions:
                         continue
                     cleanedFiles[info["depotFile"]] = info["rev"]
 
@@ -1429,7 +1492,7 @@ class P4Sync(Command):
         print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
 
         details = { "user" : "git perforce import user", "time" : int(time.time()) }
-        details["desc"] = ("Initial import of %s from the state at revision %s"
+        details["desc"] = ("Initial import of %s from the state at revision %s\n"
                            % (' '.join(self.depotPaths), revision))
         details["change"] = revision
         newestRevision = 0
@@ -1440,9 +1503,16 @@ class P4Sync(Command):
                                            % (p, revision)
                                            for p in self.depotPaths])):
 
-            if info['code'] == 'error':
+            if 'code' in info and info['code'] == 'error':
                 sys.stderr.write("p4 returned an error: %s\n"
                                  % info['data'])
+                if info['data'].find("must refer to client") >= 0:
+                    sys.stderr.write("This particular p4 error is misleading.\n")
+                    sys.stderr.write("Perhaps the depot path was misspelled.\n");
+                    sys.stderr.write("Depot path:  %s\n" % " ".join(self.depotPaths))
+                sys.exit(1)
+            if 'p4ExitCode' in info:
+                sys.stderr.write("p4 exitcode: %s\n" % info['p4ExitCode'])
                 sys.exit(1)
 
 
@@ -1450,7 +1520,7 @@ class P4Sync(Command):
             if change > newestRevision:
                 newestRevision = change
 
-            if info["action"] in ("delete", "purge"):
+            if info["action"] in self.delete_actions:
                 # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
                 #fileCnt = fileCnt + 1
                 continue
@@ -1475,19 +1545,45 @@ class P4Sync(Command):
         for entry in specList:
             for k,v in entry.iteritems():
                 if k.startswith("View"):
+
+                    # p4 has these %%1 to %%9 arguments in specs to
+                    # reorder paths; which we can't handle (yet :)
+                    if re.match('%%\d', v) != None:
+                        print "Sorry, can't handle %%n arguments in client specs"
+                        sys.exit(1)
+
                     if v.startswith('"'):
                         start = 1
                     else:
                         start = 0
                     index = v.find("...")
+
+                    # save the "client view"; i.e the RHS of the view
+                    # line that tells the client where to put the
+                    # files for this view.
+                    cv = v[index+3:].strip() # +3 to remove previous '...'
+
+                    # if the client view doesn't end with a
+                    # ... wildcard, then we're going to mess up the
+                    # output directory, so fail gracefully.
+                    if not cv.endswith('...'):
+                        print 'Sorry, client view in "%s" needs to end with wildcard' % (k)
+                        sys.exit(1)
+                    cv=cv[:-3]
+
+                    # now save the view; +index means included, -index
+                    # means it should be filtered out.
                     v = v[start:index]
                     if v.startswith("-"):
                         v = v[1:]
-                        temp[v] = -len(v)
+                        include = -len(v)
                     else:
-                        temp[v] = len(v)
+                        include = len(v)
+
+                    temp[v] = (include, cv)
+
         self.clientSpecDirs = temp.items()
-        self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) )
+        self.clientSpecDirs.sort( lambda x, y: abs( y[1][0] ) - abs( x[1][0] ) )
 
     def run(self, args):
         self.depotPaths = []
@@ -1667,6 +1763,8 @@ class P4Sync(Command):
 
                 changes.sort()
             else:
+                if not self.p4BranchesInGit:
+                    die("No remote p4 branches.  Perhaps you never did \"git p4 clone\" in here.");
                 if self.verbose:
                     print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
                                                               self.changeRange)
@@ -1747,10 +1845,13 @@ class P4Clone(P4Sync):
                                  help="where to leave result of the clone"),
             optparse.make_option("-/", dest="cloneExclude",
                                  action="append", type="string",
-                                 help="exclude depot path")
+                                 help="exclude depot path"),
+            optparse.make_option("--bare", dest="cloneBare",
+                                 action="store_true", default=False),
         ]
         self.cloneDestination = None
         self.needsGit = False
+        self.cloneBare = False
 
     # This is required for the "append" cloneExclude action
     def ensure_value(self, attr, value):
@@ -1790,11 +1891,16 @@ class P4Clone(P4Sync):
             self.cloneDestination = self.defaultDestination(args)
 
         print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
+
         if not os.path.exists(self.cloneDestination):
             os.makedirs(self.cloneDestination)
         chdir(self.cloneDestination)
-        system("git init")
-        self.gitdir = os.getcwd() + "/.git"
+
+        init_cmd = [ "git", "init" ]
+        if self.cloneBare:
+            init_cmd.append("--bare")
+        subprocess.check_call(init_cmd)
+
         if not P4Sync.run(self, depotPaths):
             return False
         if self.branch != "master":
@@ -1804,7 +1910,8 @@ class P4Clone(P4Sync):
                 masterbranch = "refs/heads/p4/master"
             if gitBranchExists(masterbranch):
                 system("git branch master %s" % masterbranch)
-                system("git checkout -f")
+                if not self.cloneBare:
+                    system("git checkout -f")
             else:
                 print "Could not detect main branch. No checkout/master branch created."
 
index 49b335921a3871d82a2c0110170a6e66d71561ee..e09da445b692db2a1cbdba7ee49456de6e2571e9 100644 (file)
@@ -191,6 +191,11 @@ git-p4.useclientspec
 
   git config [--global] git-p4.useclientspec false
 
+The P4CLIENT environment variable should be correctly set for p4 to be
+able to find the relevant client.  This client spec will be used to
+both filter the files cloned by git and set the directory layout as
+specified in the client (this implies --keep-path style semantics).
+
 Implementation Details...
 =========================
 
index f99ea9585014face001849aeeefa5f20e79bc1e2..21989fc6ab84c0503cdc9418673d9d0bf06f9d49 100755 (executable)
@@ -709,7 +709,7 @@ if [ -z "$GIT_DIR" ]; then
        exit 1
 fi
 
-projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
+projectdesc=$(sed -ne '1p' "$GIT_DIR/description" 2>/dev/null)
 # Check if the description is unchanged from it's default, and shorten it to
 # a more manageable length if it is
 if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
index a2677b03e0ff8347d13a5d56f4fa2e1aba18824a..35db24f5eae600a973391915ceb61c4635941aec 100644 (file)
@@ -8,7 +8,8 @@
 
 int main(int argc, char **argv)
 {
-       svndump_init(NULL);
+       if (svndump_init(NULL))
+               return 1;
        svndump_read((argc > 1) ? argv[1] : NULL);
        svndump_deinit();
        svndump_reset();
index 35f84bd9e7577b58bc9905c8b46806bfb37963ff..cd075b96c54419fdaa25c56b24133dd080f440ca 100644 (file)
@@ -18,6 +18,9 @@ Subversion repository mirrored on the local disk. Remote Subversion
 repositories can be mirrored on local disk using the `svnsync`
 command.
 
+Note: this tool is very young.  The details of its commandline
+interface may change in backward incompatible ways.
+
 INPUT FORMAT
 ------------
 Subversion's repository dump format is documented in full in
index 60f26fe473ad6922166e952f4f7e5be2b79d45de..e1268b8cb4ae2ac482928b23e6a3a04bf78c7fcb 100644 (file)
@@ -24,10 +24,12 @@ Format of STDIN stream:
     commit_msg
     ('from' sp committish lf)?
     ('merge' sp committish lf)*
-    file_change*
+    (file_change | ls)*
     lf?;
   commit_msg ::= data;
 
+  ls ::= 'ls' sp '"' quoted(path) '"' lf;
+
   file_change ::= file_clr
     | file_del
     | file_rnm
@@ -132,7 +134,7 @@ Format of STDIN stream:
   ts    ::= # time since the epoch in seconds, ascii base10 notation;
   tz    ::= # GIT style timezone;
 
-     # note: comments and cat requests may appear anywhere
+     # note: comments, ls and cat requests may appear anywhere
      # in the input, except within a data command.  Any form
      # of the data command always escapes the related input
      # from comment processing.
@@ -141,7 +143,9 @@ Format of STDIN stream:
      # must be the first character on that line (an lf
      # preceded it).
      #
+
   cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
+  ls_tree  ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
 
   comment ::= '#' not_lf* lf;
   not_lf  ::= # Any byte that is not ASCII newline (LF);
@@ -329,6 +333,7 @@ static struct mark_set *marks;
 static const char *export_marks_file;
 static const char *import_marks_file;
 static int import_marks_file_from_stream;
+static int import_marks_file_ignore_missing;
 static int relative_marks_paths;
 
 /* Our last blob */
@@ -373,6 +378,7 @@ static int cat_blob_fd = STDOUT_FILENO;
 
 static void parse_argv(void);
 static void parse_cat_blob(void);
+static void parse_ls(struct branch *b);
 
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
@@ -1795,7 +1801,11 @@ static void read_marks(void)
 {
        char line[512];
        FILE *f = fopen(import_marks_file, "r");
-       if (!f)
+       if (f)
+               ;
+       else if (import_marks_file_ignore_missing && errno == ENOENT)
+               return; /* Marks file does not exist */
+       else
                die_errno("cannot read '%s'", import_marks_file);
        while (fgets(line, sizeof(line), f)) {
                uintmax_t mark;
@@ -2609,6 +2619,8 @@ static void parse_new_commit(void)
                        note_change_n(b, prev_fanout);
                else if (!strcmp("deleteall", command_buf.buf))
                        file_change_deleteall(b);
+               else if (!prefixcmp(command_buf.buf, "ls "))
+                       parse_ls(b);
                else {
                        unread_command_buf = 1;
                        break;
@@ -2832,6 +2844,153 @@ static void parse_cat_blob(void)
        cat_blob(oe, sha1);
 }
 
+static struct object_entry *dereference(struct object_entry *oe,
+                                       unsigned char sha1[20])
+{
+       unsigned long size;
+       char *buf = NULL;
+       if (!oe) {
+               enum object_type type = sha1_object_info(sha1, NULL);
+               if (type < 0)
+                       die("object not found: %s", sha1_to_hex(sha1));
+               /* cache it! */
+               oe = insert_object(sha1);
+               oe->type = type;
+               oe->pack_id = MAX_PACK_ID;
+               oe->idx.offset = 1;
+       }
+       switch (oe->type) {
+       case OBJ_TREE:  /* easy case. */
+               return oe;
+       case OBJ_COMMIT:
+       case OBJ_TAG:
+               break;
+       default:
+               die("Not a treeish: %s", command_buf.buf);
+       }
+
+       if (oe->pack_id != MAX_PACK_ID) {       /* in a pack being written */
+               buf = gfi_unpack_entry(oe, &size);
+       } else {
+               enum object_type unused;
+               buf = read_sha1_file(sha1, &unused, &size);
+       }
+       if (!buf)
+               die("Can't load object %s", sha1_to_hex(sha1));
+
+       /* Peel one layer. */
+       switch (oe->type) {
+       case OBJ_TAG:
+               if (size < 40 + strlen("object ") ||
+                   get_sha1_hex(buf + strlen("object "), sha1))
+                       die("Invalid SHA1 in tag: %s", command_buf.buf);
+               break;
+       case OBJ_COMMIT:
+               if (size < 40 + strlen("tree ") ||
+                   get_sha1_hex(buf + strlen("tree "), sha1))
+                       die("Invalid SHA1 in commit: %s", command_buf.buf);
+       }
+
+       free(buf);
+       return find_object(sha1);
+}
+
+static struct object_entry *parse_treeish_dataref(const char **p)
+{
+       unsigned char sha1[20];
+       struct object_entry *e;
+
+       if (**p == ':') {       /* <mark> */
+               char *endptr;
+               e = find_mark(strtoumax(*p + 1, &endptr, 10));
+               if (endptr == *p + 1)
+                       die("Invalid mark: %s", command_buf.buf);
+               if (!e)
+                       die("Unknown mark: %s", command_buf.buf);
+               *p = endptr;
+               hashcpy(sha1, e->idx.sha1);
+       } else {        /* <sha1> */
+               if (get_sha1_hex(*p, sha1))
+                       die("Invalid SHA1: %s", command_buf.buf);
+               e = find_object(sha1);
+               *p += 40;
+       }
+
+       while (!e || e->type != OBJ_TREE)
+               e = dereference(e, sha1);
+       return e;
+}
+
+static void print_ls(int mode, const unsigned char *sha1, const char *path)
+{
+       static struct strbuf line = STRBUF_INIT;
+
+       /* See show_tree(). */
+       const char *type =
+               S_ISGITLINK(mode) ? commit_type :
+               S_ISDIR(mode) ? tree_type :
+               blob_type;
+
+       if (!mode) {
+               /* missing SP path LF */
+               strbuf_reset(&line);
+               strbuf_addstr(&line, "missing ");
+               quote_c_style(path, &line, NULL, 0);
+               strbuf_addch(&line, '\n');
+       } else {
+               /* mode SP type SP object_name TAB path LF */
+               strbuf_reset(&line);
+               strbuf_addf(&line, "%06o %s %s\t",
+                               mode, type, sha1_to_hex(sha1));
+               quote_c_style(path, &line, NULL, 0);
+               strbuf_addch(&line, '\n');
+       }
+       cat_blob_write(line.buf, line.len);
+}
+
+static void parse_ls(struct branch *b)
+{
+       const char *p;
+       struct tree_entry *root = NULL;
+       struct tree_entry leaf = {0};
+
+       /* ls SP (<treeish> SP)? <path> */
+       p = command_buf.buf + strlen("ls ");
+       if (*p == '"') {
+               if (!b)
+                       die("Not in a commit: %s", command_buf.buf);
+               root = &b->branch_tree;
+       } else {
+               struct object_entry *e = parse_treeish_dataref(&p);
+               root = new_tree_entry();
+               hashcpy(root->versions[1].sha1, e->idx.sha1);
+               load_tree(root);
+               if (*p++ != ' ')
+                       die("Missing space after tree-ish: %s", command_buf.buf);
+       }
+       if (*p == '"') {
+               static struct strbuf uq = STRBUF_INIT;
+               const char *endp;
+               strbuf_reset(&uq);
+               if (unquote_c_style(&uq, p, &endp))
+                       die("Invalid path: %s", command_buf.buf);
+               if (*endp)
+                       die("Garbage after path in: %s", command_buf.buf);
+               p = uq.buf;
+       }
+       tree_content_get(root, p, &leaf);
+       /*
+        * A directory in preparation would have a sha1 of zero
+        * until it is saved.  Save, for simplicity.
+        */
+       if (S_ISDIR(leaf.versions[1].mode))
+               store_tree(&leaf);
+
+       print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p);
+       if (!b || root != &b->branch_tree)
+               release_tree_entry(root);
+}
+
 static void checkpoint(void)
 {
        checkpoint_requested = 0;
@@ -2867,7 +3026,8 @@ static char* make_fast_import_path(const char *path)
        return strbuf_detach(&abs_path, NULL);
 }
 
-static void option_import_marks(const char *marks, int from_stream)
+static void option_import_marks(const char *marks,
+                                       int from_stream, int ignore_missing)
 {
        if (import_marks_file) {
                if (from_stream)
@@ -2881,6 +3041,7 @@ static void option_import_marks(const char *marks, int from_stream)
        import_marks_file = make_fast_import_path(marks);
        safe_create_leading_directories_const(import_marks_file);
        import_marks_file_from_stream = from_stream;
+       import_marks_file_ignore_missing = ignore_missing;
 }
 
 static void option_date_format(const char *fmt)
@@ -2980,7 +3141,10 @@ static int parse_one_feature(const char *feature, int from_stream)
        if (!prefixcmp(feature, "date-format=")) {
                option_date_format(feature + 12);
        } else if (!prefixcmp(feature, "import-marks=")) {
-               option_import_marks(feature + 13, from_stream);
+               option_import_marks(feature + 13, from_stream, 0);
+       } else if (!prefixcmp(feature, "import-marks-if-exists=")) {
+               option_import_marks(feature + strlen("import-marks-if-exists="),
+                                       from_stream, 1);
        } else if (!prefixcmp(feature, "export-marks=")) {
                option_export_marks(feature + 13);
        } else if (!strcmp(feature, "cat-blob")) {
@@ -2991,6 +3155,8 @@ static int parse_one_feature(const char *feature, int from_stream)
                relative_marks_paths = 0;
        } else if (!prefixcmp(feature, "force")) {
                force_update = 1;
+       } else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
+               ; /* do nothing; we have the feature */
        } else {
                return 0;
        }
@@ -3130,6 +3296,8 @@ int main(int argc, const char **argv)
        while (read_next_command() != EOF) {
                if (!strcmp("blob", command_buf.buf))
                        parse_new_blob();
+               else if (!prefixcmp(command_buf.buf, "ls "))
+                       parse_ls(NULL);
                else if (!prefixcmp(command_buf.buf, "commit "))
                        parse_new_commit();
                else if (!prefixcmp(command_buf.buf, "tag "))
index d6d269f138b06791ba6e8712b06036f00a73c7e3..9c23622ed5ee9b60d40921ae74dd1149e1d68b8c 100644 (file)
@@ -31,6 +31,9 @@
 #define maximum_signed_value_of_type(a) \
     (INTMAX_MAX >> (bitsizeof(intmax_t) - bitsizeof(a)))
 
+#define maximum_unsigned_value_of_type(a) \
+    (UINTMAX_MAX >> (bitsizeof(uintmax_t) - bitsizeof(a)))
+
 /*
  * Signed integer overflow is undefined in C, so here's a helper macro
  * to detect if the sum of two integers will overflow.
@@ -40,6 +43,9 @@
 #define signed_add_overflows(a, b) \
     ((b) > maximum_signed_value_of_type(a) - (a))
 
+#define unsigned_add_overflows(a, b) \
+    ((b) > maximum_unsigned_value_of_type(a) - (a))
+
 #ifdef __GNUC__
 #define TYPEOF(x) (__typeof__(x))
 #else
index 2f8dc441c6d575323f2fad920ecdb7b20e1445e6..bacbda2bb75854235b912903c7e45fc94d38e719 100755 (executable)
@@ -269,7 +269,7 @@ rerere=false
 files_to_merge() {
     if test "$rerere" = true
     then
-       git rerere status
+       git rerere remaining
     else
        git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u
     fi
index eb87f49062b70d707adebca4013809822123608b..f6b7b8404896c15487044bcfaa64f758faeeef06 100755 (executable)
@@ -114,7 +114,7 @@ do
        --d|--dr|--dry|--dry-|--dry-r|--dry-ru|--dry-run)
                dry_run=--dry-run
                ;;
-       -h|--h|--he|--hel|--help)
+       -h|--h|--he|--hel|--help|--help-|--help-a|--help-al|--help-all)
                usage
                ;;
        *)
diff --git a/git.c b/git.c
index 68334f6a3134bb76a8ffc59e4da773400c59a388..ef598c3e7053b8dd2859f4d582ce2917a804fe42 100644 (file)
--- a/git.c
+++ b/git.c
@@ -177,24 +177,24 @@ static int handle_alias(int *argcp, const char ***argv)
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
                if (alias_string[0] == '!') {
+                       const char **alias_argv;
+                       int argc = *argcp, i;
+
                        commit_pager_choice();
-                       if (*argcp > 1) {
-                               struct strbuf buf;
-
-                               strbuf_init(&buf, PATH_MAX);
-                               strbuf_addstr(&buf, alias_string);
-                               sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
-                               free(alias_string);
-                               alias_string = buf.buf;
-                       }
-                       trace_printf("trace: alias to shell cmd: %s => %s\n",
-                                    alias_command, alias_string + 1);
-                       ret = system(alias_string + 1);
-                       if (ret >= 0 && WIFEXITED(ret) &&
-                           WEXITSTATUS(ret) != 127)
-                               exit(WEXITSTATUS(ret));
-                       die("Failed to run '%s' when expanding alias '%s'",
-                           alias_string + 1, alias_command);
+
+                       /* build alias_argv */
+                       alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
+                       alias_argv[0] = alias_string + 1;
+                       for (i = 1; i < argc; ++i)
+                               alias_argv[i] = (*argv)[i];
+                       alias_argv[argc] = NULL;
+
+                       ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
+                       if (ret >= 0)   /* normal exit */
+                               exit(ret);
+
+                       die_errno("While expanding alias '%s': '%s'",
+                           alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
                if (count < 0)
@@ -313,7 +313,6 @@ static void handle_internal_command(int argc, const char **argv)
        const char *cmd = argv[0];
        static struct cmd_struct commands[] = {
                { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
-               { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "annotate", cmd_annotate, RUN_SETUP },
                { "apply", cmd_apply, RUN_SETUP_GENTLY },
                { "archive", cmd_archive },
@@ -322,15 +321,15 @@ static void handle_internal_command(int argc, const char **argv)
                { "branch", cmd_branch, RUN_SETUP },
                { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
                { "cat-file", cmd_cat_file, RUN_SETUP },
+               { "check-attr", cmd_check_attr, RUN_SETUP },
+               { "check-ref-format", cmd_check_ref_format },
                { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
                { "checkout-index", cmd_checkout_index,
                        RUN_SETUP | NEED_WORK_TREE},
-               { "check-ref-format", cmd_check_ref_format },
-               { "check-attr", cmd_check_attr, RUN_SETUP },
                { "cherry", cmd_cherry, RUN_SETUP },
                { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
-               { "clone", cmd_clone },
                { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
+               { "clone", cmd_clone },
                { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "config", cmd_config, RUN_SETUP_GENTLY },
@@ -358,8 +357,8 @@ static void handle_internal_command(int argc, const char **argv)
                { "init-db", cmd_init_db },
                { "log", cmd_log, RUN_SETUP },
                { "ls-files", cmd_ls_files, RUN_SETUP },
-               { "ls-tree", cmd_ls_tree, RUN_SETUP },
                { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
+               { "ls-tree", cmd_ls_tree, RUN_SETUP },
                { "mailinfo", cmd_mailinfo },
                { "mailsplit", cmd_mailsplit },
                { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
@@ -379,6 +378,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "notes", cmd_notes, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
                { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
+               { "pack-refs", cmd_pack_refs, RUN_SETUP },
                { "patch-id", cmd_patch_id },
                { "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
                { "pickaxe", cmd_blame, RUN_SETUP },
@@ -392,7 +392,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "remote-ext", cmd_remote_ext },
                { "remote-fd", cmd_remote_fd },
                { "replace", cmd_replace, RUN_SETUP },
-               { "repo-config", cmd_config, RUN_SETUP_GENTLY },
+               { "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
                { "rerere", cmd_rerere, RUN_SETUP },
                { "reset", cmd_reset, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
@@ -401,8 +401,10 @@ static void handle_internal_command(int argc, const char **argv)
                { "rm", cmd_rm, RUN_SETUP },
                { "send-pack", cmd_send_pack, RUN_SETUP },
                { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
-               { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP },
+               { "show-branch", cmd_show_branch, RUN_SETUP },
+               { "show-ref", cmd_show_ref, RUN_SETUP },
+               { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
@@ -415,13 +417,11 @@ static void handle_internal_command(int argc, const char **argv)
                { "update-server-info", cmd_update_server_info, RUN_SETUP },
                { "upload-archive", cmd_upload_archive },
                { "var", cmd_var, RUN_SETUP_GENTLY },
+               { "verify-pack", cmd_verify_pack },
                { "verify-tag", cmd_verify_tag, RUN_SETUP },
                { "version", cmd_version },
                { "whatchanged", cmd_whatchanged, RUN_SETUP },
                { "write-tree", cmd_write_tree, RUN_SETUP },
-               { "verify-pack", cmd_verify_pack },
-               { "show-ref", cmd_show_ref, RUN_SETUP },
-               { "pack-refs", cmd_pack_refs, RUN_SETUP },
        };
        int i;
        static const char ext[] = STRIP_EXTENSION;
index 823053173c92e4097dad54945db16f42b4b5d994..4964a679b3013caa9bfa2f438dc110eaa0274b69 100644 (file)
@@ -237,6 +237,12 @@ Requirements
  - Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
  - web server
 
+The following optional Perl modules are required for extra features
+ - Digest::MD5 - for gravatar support
+ - CGI::Fast and FCGI - for running gitweb as FastCGI script
+ - HTML::TagCloud - for fancy tag cloud in project list view
+ - HTTP::Date or Time::ParseDate - to support If-Modified-Since for feeds
+
 
 Example web server configuration
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 0779f12d6178c23a47f4b9a08152176d0e19a2a8..1b9369d1a72f625bcb66e021bdab8559930cb0eb 100755 (executable)
@@ -3501,7 +3501,7 @@ sub print_feed_meta {
                        $href_params{'-title'} = 'log';
                }
 
-               foreach my $format qw(RSS Atom) {
+               foreach my $format (qw(RSS Atom)) {
                        my $type = lc($format);
                        my %link_attr = (
                                '-rel' => 'alternate',
@@ -3682,7 +3682,7 @@ sub git_footer_html {
                }
                $href_params{'-title'} ||= 'log';
 
-               foreach my $format qw(RSS Atom) {
+               foreach my $format (qw(RSS Atom)) {
                        $href_params{'action'} = lc($format);
                        print $cgi->a({-href => href(%href_params),
                                      -title => "$href_params{'-title'} $format feed",
@@ -4412,7 +4412,7 @@ sub git_difftree_body {
                }
                if ($diff->{'from_mode'} ne ('0' x 6)) {
                        $from_mode_oct = oct $diff->{'from_mode'};
-                       if (S_ISREG($to_mode_oct)) { # only for regular file
+                       if (S_ISREG($from_mode_oct)) { # only for regular file
                                $from_mode_str = sprintf("%04o", $from_mode_oct & 0777); # permission bits
                        }
                        $from_file_type = file_type($diff->{'from_mode'});
index 8953548c07bb36f20798c7ca344d07960c22618c..61f6cc98d917c3e4395ec397ac74df6a40eeb07f 100644 (file)
@@ -61,12 +61,15 @@ static void process_tree(struct rev_info *revs,
                         struct tree *tree,
                         show_object_fn show,
                         struct name_path *path,
+                        struct strbuf *base,
                         const char *name)
 {
        struct object *obj = &tree->object;
        struct tree_desc desc;
        struct name_entry entry;
        struct name_path me;
+       int all_interesting = (revs->diffopt.pathspec.nr == 0);
+       int baselen = base->len;
 
        if (!revs->tree_objects)
                return;
@@ -82,13 +85,32 @@ static void process_tree(struct rev_info *revs,
        me.elem = name;
        me.elem_len = strlen(name);
 
+       if (!all_interesting) {
+               strbuf_addstr(base, name);
+               if (base->len)
+                       strbuf_addch(base, '/');
+       }
+
        init_tree_desc(&desc, tree->buffer, tree->size);
 
        while (tree_entry(&desc, &entry)) {
+               if (!all_interesting) {
+                       int showit = tree_entry_interesting(&entry,
+                                                           base, 0,
+                                                           &revs->diffopt.pathspec);
+
+                       if (showit < 0)
+                               break;
+                       else if (!showit)
+                               continue;
+                       else if (showit == 2)
+                               all_interesting = 1;
+               }
+
                if (S_ISDIR(entry.mode))
                        process_tree(revs,
                                     lookup_tree(entry.sha1),
-                                    show, &me, entry.path);
+                                    show, &me, base, entry.path);
                else if (S_ISGITLINK(entry.mode))
                        process_gitlink(revs, entry.sha1,
                                        show, &me, entry.path);
@@ -97,6 +119,7 @@ static void process_tree(struct rev_info *revs,
                                     lookup_blob(entry.sha1),
                                     show, &me, entry.path);
        }
+       strbuf_setlen(base, baselen);
        free(tree->buffer);
        tree->buffer = NULL;
 }
@@ -146,7 +169,9 @@ void traverse_commit_list(struct rev_info *revs,
 {
        int i;
        struct commit *commit;
+       struct strbuf base;
 
+       strbuf_init(&base, PATH_MAX);
        while ((commit = get_revision(revs)) != NULL) {
                add_pending_tree(revs, commit->tree);
                show_commit(commit, data);
@@ -164,7 +189,7 @@ void traverse_commit_list(struct rev_info *revs,
                }
                if (obj->type == OBJ_TREE) {
                        process_tree(revs, (struct tree *)obj, show_object,
-                                    NULL, name);
+                                    NULL, &base, name);
                        continue;
                }
                if (obj->type == OBJ_BLOB) {
@@ -181,4 +206,5 @@ void traverse_commit_list(struct rev_info *revs,
                revs->pending.alloc = 0;
                revs->pending.objects = NULL;
        }
+       strbuf_release(&base);
 }
index c8135b0ec70cc2eb92e5a6fd9353a134fda6b7bf..981ed6ac94b5ed73f15a3b0dca723586374ca0b8 100644 (file)
@@ -57,6 +57,8 @@ struct tree *write_tree_from_memory(struct merge_options *o);
 int parse_merge_opt(struct merge_options *out, const char *s);
 
 /* builtin/merge.c */
-int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes);
+int try_merge_command(const char *strategy, size_t xopts_nr,
+               const char **xopts, struct commit_list *common,
+               const char *head_arg, struct commit_list *remotes);
 
 #endif
index 71c4d45fcd1ac49630bdb8bc7b0aec658011c21b..1467ad31795ae21896f1e608e7cb032df2bc94a8 100644 (file)
@@ -615,7 +615,7 @@ int notes_merge(struct notes_merge_options *o,
        bases = get_merge_bases(local, remote, 1);
        if (!bases) {
                base_sha1 = null_sha1;
-               base_tree_sha1 = (unsigned char *)EMPTY_TREE_SHA1_BIN;
+               base_tree_sha1 = EMPTY_TREE_SHA1_BIN;
                OUTPUT(o, 4, "No merge base found; doing history-less merge");
        } else if (!bases->next) {
                base_sha1 = bases->item->object.sha1;
index 31ec5d24763b13b3e97c79f7a3edd6e7799bcd91..d1b12fe979b34ac9bbf2ff637934b96f3878a53d 100644 (file)
@@ -141,7 +141,7 @@ struct option {
        { OPTION_NUMBER, 0, NULL, (v), NULL, (h), \
          PARSE_OPT_NOARG | PARSE_OPT_NONEG, (f) }
 #define OPT_FILENAME(s, l, v, h)    { OPTION_FILENAME, (s), (l), (v), \
-                                      "FILE", (h) }
+                                      "file", (h) }
 #define OPT_COLOR_FLAG(s, l, v, h) \
        { OPTION_CALLBACK, (s), (l), (v), "when", (h), PARSE_OPT_OPTARG, \
                parse_opt_color_flag_cb, (intptr_t)"always" }
index d218faa02bd12b0e6a0df298a6a0e5787e46d93f..56e0a5ede22c9396fc897bf1d3444dce92d8916f 100644 (file)
@@ -48,7 +48,7 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
                        if (cmd & 0x20) cp_size |= (*data++ << 8);
                        if (cmd & 0x40) cp_size |= (*data++ << 16);
                        if (cp_size == 0) cp_size = 0x10000;
-                       if (cp_off + cp_size < cp_size ||
+                       if (unsigned_add_overflows(cp_off, cp_size) ||
                            cp_off + cp_size > src_size ||
                            cp_size > size)
                                break;
index 205e48aa3a7b912005565ac9c296205a897a1476..a86ab709c25b5e110aa2708941cea560f82c1fa8 100644 (file)
@@ -99,7 +99,7 @@ increase notwithstanding).
 
 use Carp qw(carp croak); # but croak is bad - throw instead
 use Error qw(:try);
-use Cwd qw(abs_path);
+use Cwd qw(abs_path cwd);
 use IPC::Open2 qw(open2);
 use Fcntl qw(SEEK_SET SEEK_CUR);
 }
@@ -396,7 +396,16 @@ See C<command_close_bidi_pipe()> for details.
 
 sub command_bidi_pipe {
        my ($pid, $in, $out);
+       my ($self) = _maybe_self(@_);
+       local %ENV = %ENV;
+       my $cwd_save = undef;
+       if ($self) {
+               shift;
+               $cwd_save = cwd();
+               _setup_git_cmd_env($self);
+       }
        $pid = open2($in, $out, 'git', @_);
+       chdir($cwd_save) if $cwd_save;
        return ($pid, $in, $out, join(' ', @_));
 }
 
@@ -843,7 +852,7 @@ sub _open_hash_and_insert_object_if_needed {
 
        ($self->{hash_object_pid}, $self->{hash_object_in},
         $self->{hash_object_out}, $self->{hash_object_ctx}) =
-               command_bidi_pipe(qw(hash-object -w --stdin-paths --no-filters));
+               $self->command_bidi_pipe(qw(hash-object -w --stdin-paths --no-filters));
 }
 
 sub _close_hash_and_insert_object {
@@ -932,7 +941,7 @@ sub _open_cat_blob_if_needed {
 
        ($self->{cat_blob_pid}, $self->{cat_blob_in},
         $self->{cat_blob_out}, $self->{cat_blob_ctx}) =
-               command_bidi_pipe(qw(cat-file --batch));
+               $self->command_bidi_pipe(qw(cat-file --batch));
 }
 
 sub _close_cat_blob {
@@ -1279,6 +1288,14 @@ sub _command_common_pipe {
 # for the given repository and execute the git command.
 sub _cmd_exec {
        my ($self, @args) = @_;
+       _setup_git_cmd_env($self);
+       _execv_git_cmd(@args);
+       die qq[exec "@args" failed: $!];
+}
+
+# set up the appropriate state for git command
+sub _setup_git_cmd_env {
+       my $self = shift;
        if ($self) {
                $self->repo_path() and $ENV{'GIT_DIR'} = $self->repo_path();
                $self->repo_path() and $self->wc_path()
@@ -1286,8 +1303,6 @@ sub _cmd_exec {
                $self->wc_path() and chdir($self->wc_path());
                $self->wc_subdir() and chdir($self->wc_subdir());
        }
-       _execv_git_cmd(@args);
-       die qq[exec "@args" failed: $!];
 }
 
 # Execute the given Git command ($_[0]) with arguments ($_[1..])
diff --git a/quote.h b/quote.h
index 38003bff5f97a11e051b106522a8548d43d24f6c..024e21d80ceaa37ec1f893acc907a62092560d21 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -1,8 +1,7 @@
 #ifndef QUOTE_H
 #define QUOTE_H
 
-#include <stddef.h>
-#include <stdio.h>
+struct strbuf;
 
 /* Help to copy the thing properly quoted for the shell safety.
  * any single quote is replaced with '\'', any exclamation point
index 7772079ad3101a2a51cfef1c5b1c250ed3ff37de..98d526bd48d2a0e764dd0efb4c6efe4965174c9a 100644 (file)
@@ -92,7 +92,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)
 
        if (fd >= 0) {
                unsigned char sha1[20];
-               if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name))
+               if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name, 0))
                        match = hashcmp(sha1, ce->sha1);
                /* index_fd() closed the file descriptor already */
        }
@@ -1083,7 +1083,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
 }
 
 static void show_file(const char * fmt, const char * name, int in_porcelain,
-                     int * first, char *header_msg)
+                     int * first, const char *header_msg)
 {
        if (in_porcelain && *first && header_msg) {
                printf("%s\n", header_msg);
@@ -1093,7 +1093,7 @@ static void show_file(const char * fmt, const char * name, int in_porcelain,
 }
 
 int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
-                 char *seen, char *header_msg)
+                 char *seen, const char *header_msg)
 {
        int i;
        int has_errors = 0;
index 04d4813e4183c675b54aba942cd078d8ff632053..d0fb0a044a85ec6169c4623c7c2ab2143dbb5784 100644 (file)
@@ -356,14 +356,59 @@ static size_t rpc_in(const void *ptr, size_t eltsize,
        return size;
 }
 
+static int run_slot(struct active_request_slot *slot)
+{
+       int err = 0;
+       struct slot_results results;
+
+       slot->results = &results;
+       slot->curl_result = curl_easy_perform(slot->curl);
+       finish_active_slot(slot);
+
+       if (results.curl_result != CURLE_OK) {
+               err |= error("RPC failed; result=%d, HTTP code = %ld",
+                       results.curl_result, results.http_code);
+       }
+
+       return err;
+}
+
+static int probe_rpc(struct rpc_state *rpc)
+{
+       struct active_request_slot *slot;
+       struct curl_slist *headers = NULL;
+       struct strbuf buf = STRBUF_INIT;
+       int err;
+
+       slot = get_active_slot();
+
+       headers = curl_slist_append(headers, rpc->hdr_content_type);
+       headers = curl_slist_append(headers, rpc->hdr_accept);
+
+       curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
+       curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
+       curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
+       curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
+       curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, "0000");
+       curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, 4);
+       curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
+       curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+       curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf);
+
+       err = run_slot(slot);
+
+       curl_slist_free_all(headers);
+       strbuf_release(&buf);
+       return err;
+}
+
 static int post_rpc(struct rpc_state *rpc)
 {
        struct active_request_slot *slot;
-       struct slot_results results;
        struct curl_slist *headers = NULL;
        int use_gzip = rpc->gzip_request;
        char *gzip_body = NULL;
-       int err = 0, large_request = 0;
+       int err, large_request = 0;
 
        /* Try to load the entire request, if we can fit it into the
         * allocated buffer space we can use HTTP/1.0 and avoid the
@@ -386,8 +431,13 @@ static int post_rpc(struct rpc_state *rpc)
                rpc->len += n;
        }
 
+       if (large_request) {
+               err = probe_rpc(rpc);
+               if (err)
+                       return err;
+       }
+
        slot = get_active_slot();
-       slot->results = &results;
 
        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
        curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
@@ -401,7 +451,7 @@ static int post_rpc(struct rpc_state *rpc)
                /* The request body is large and the size cannot be predicted.
                 * We must use chunked encoding to send it.
                 */
-               headers = curl_slist_append(headers, "Expect: 100-continue");
+               headers = curl_slist_append(headers, "Expect:");
                headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
                rpc->initial_buffer = 1;
                curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
@@ -475,13 +525,7 @@ static int post_rpc(struct rpc_state *rpc)
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
        curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
 
-       slot->curl_result = curl_easy_perform(slot->curl);
-       finish_active_slot(slot);
-
-       if (results.curl_result != CURLE_OK) {
-               err |= error("RPC failed; result=%d, HTTP code = %ld",
-                       results.curl_result, results.http_code);
-       }
+       err = run_slot(slot);
 
        curl_slist_free_all(headers);
        free(gzip_body);
index d2608434750c336c3f3881efda8373b7c67d4b11..22996bd08b532fb8016f24391dd0e63010706385 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -7,6 +7,11 @@
 #include "ll-merge.h"
 #include "attr.h"
 
+#define RESOLVED 0
+#define PUNTED 1
+#define THREE_STAGED 2
+void *RERERE_RESOLVED = &RERERE_RESOLVED;
+
 /* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
 static int rerere_enabled = -1;
 
@@ -345,21 +350,74 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
        return hunk_no;
 }
 
-static int find_conflict(struct string_list *conflict)
+static int check_one_conflict(int i, int *type)
 {
-       int i;
-       if (read_cache() < 0)
-               return error("Could not read index");
-       for (i = 0; i+1 < active_nr; i++) {
+       struct cache_entry *e = active_cache[i];
+
+       if (!ce_stage(e)) {
+               *type = RESOLVED;
+               return i + 1;
+       }
+
+       *type = PUNTED;
+       if (ce_stage(e) == 1) {
+               if (active_nr <= ++i)
+                       return i + 1;
+       }
+
+       /* Only handle regular files with both stages #2 and #3 */
+       if (i + 1 < active_nr) {
                struct cache_entry *e2 = active_cache[i];
-               struct cache_entry *e3 = active_cache[i+1];
+               struct cache_entry *e3 = active_cache[i + 1];
                if (ce_stage(e2) == 2 &&
                    ce_stage(e3) == 3 &&
-                   ce_same_name(e2, e3) &&
+                   ce_same_name(e, e3) &&
                    S_ISREG(e2->ce_mode) &&
-                   S_ISREG(e3->ce_mode)) {
-                       string_list_insert(conflict, (const char *)e2->name);
-                       i++; /* skip over both #2 and #3 */
+                   S_ISREG(e3->ce_mode))
+                       *type = THREE_STAGED;
+       }
+
+       /* Skip the entries with the same name */
+       while (i < active_nr && ce_same_name(e, active_cache[i]))
+               i++;
+       return i;
+}
+
+static int find_conflict(struct string_list *conflict)
+{
+       int i;
+       if (read_cache() < 0)
+               return error("Could not read index");
+
+       for (i = 0; i < active_nr;) {
+               int conflict_type;
+               struct cache_entry *e = active_cache[i];
+               i = check_one_conflict(i, &conflict_type);
+               if (conflict_type == THREE_STAGED)
+                       string_list_insert(conflict, (const char *)e->name);
+       }
+       return 0;
+}
+
+int rerere_remaining(struct string_list *merge_rr)
+{
+       int i;
+       if (read_cache() < 0)
+               return error("Could not read index");
+
+       for (i = 0; i < active_nr;) {
+               int conflict_type;
+               struct cache_entry *e = active_cache[i];
+               i = check_one_conflict(i, &conflict_type);
+               if (conflict_type == PUNTED)
+                       string_list_insert(merge_rr, (const char *)e->name);
+               else if (conflict_type == RESOLVED) {
+                       struct string_list_item *it;
+                       it = string_list_lookup(merge_rr, (const char *)e->name);
+                       if (it != NULL) {
+                               free(it->util);
+                               it->util = RERERE_RESOLVED;
+                       }
                }
        }
        return 0;
index eaa9004dcdbb2f0dfd446e1ac68237c6dff18530..595f49f701dc1003927679085ca6eea8ad1083ae 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -6,11 +6,19 @@
 #define RERERE_AUTOUPDATE   01
 #define RERERE_NOAUTOUPDATE 02
 
+/*
+ * Marks paths that have been hand-resolved and added to the
+ * index. Set in the util field of such paths after calling
+ * rerere_remaining.
+ */
+extern void *RERERE_RESOLVED;
+
 extern int setup_rerere(struct string_list *, int);
 extern int rerere(int);
 extern const char *rerere_path(const char *hex, const char *file);
 extern int has_rerere_resolution(const char *hex);
 extern int rerere_forget(const char **);
+extern int rerere_remaining(struct string_list *);
 
 #define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
        "update the index with reused conflict resolution if possible")
index 2a1041ef6599c84fff6a8d9faf5dea23a2af3ab0..f91e446c86be8e27f98554567143d7ce6f934bd1 100644 (file)
@@ -194,6 +194,7 @@ fail_pipe:
        }
 
        trace_argv_printf(cmd->argv, "trace: run_command:");
+       fflush(NULL);
 
 #ifndef WIN32
 {
@@ -201,7 +202,6 @@ fail_pipe:
        if (pipe(notify_pipe))
                notify_pipe[0] = notify_pipe[1] = -1;
 
-       fflush(NULL);
        cmd->pid = fork();
        if (!cmd->pid) {
                /*
diff --git a/setup.c b/setup.c
index dadc66659a4037b614b215b7f812c4df8969562b..021d0133ae1d6cf7f3a6a8284cb5b6e9a42dbe0d 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -7,10 +7,13 @@ static int inside_work_tree = -1;
 char *prefix_path(const char *prefix, int len, const char *path)
 {
        const char *orig = path;
-       char *sanitized = xmalloc(len + strlen(path) + 1);
-       if (is_absolute_path(orig))
-               strcpy(sanitized, path);
-       else {
+       char *sanitized;
+       if (is_absolute_path(orig)) {
+               const char *temp = make_absolute_path(path);
+               sanitized = xmalloc(len + strlen(temp) + 1);
+               strcpy(sanitized, temp);
+       } else {
+               sanitized = xmalloc(len + strlen(path) + 1);
                if (len)
                        memcpy(sanitized, prefix, len);
                strcpy(sanitized + len, path);
index d86a8db69ade6fd26ebd88bbb361a7a86838f14e..d949b35c3308ae5c06774da7f4fecdc84778d5eb 100644 (file)
@@ -13,6 +13,7 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree.h"
+#include "tree-walk.h"
 #include "refs.h"
 #include "pack-revindex.h"
 #include "sha1-lookup.h"
@@ -37,6 +38,41 @@ const unsigned char null_sha1[20];
 
 static int git_open_noatime(const char *name, struct packed_git *p);
 
+/*
+ * This is meant to hold a *small* number of objects that you would
+ * want read_sha1_file() to be able to return, but yet you do not want
+ * to write them into the object store (e.g. a browse-only
+ * application).
+ */
+static struct cached_object {
+       unsigned char sha1[20];
+       enum object_type type;
+       void *buf;
+       unsigned long size;
+} *cached_objects;
+static int cached_object_nr, cached_object_alloc;
+
+static struct cached_object empty_tree = {
+       EMPTY_TREE_SHA1_BIN_LITERAL,
+       OBJ_TREE,
+       "",
+       0
+};
+
+static struct cached_object *find_cached_object(const unsigned char *sha1)
+{
+       int i;
+       struct cached_object *co = cached_objects;
+
+       for (i = 0; i < cached_object_nr; i++, co++) {
+               if (!hashcmp(co->sha1, sha1))
+                       return co;
+       }
+       if (!hashcmp(sha1, empty_tree.sha1))
+               return &empty_tree;
+       return NULL;
+}
+
 int safe_create_leading_directories(char *path)
 {
        char *pos = path + offset_1st_component(path);
@@ -1985,9 +2021,17 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size
 
 int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 {
+       struct cached_object *co;
        struct pack_entry e;
        int status;
 
+       co = find_cached_object(sha1);
+       if (co) {
+               if (sizep)
+                       *sizep = co->size;
+               return co->type;
+       }
+
        if (!find_pack_entry(sha1, &e)) {
                /* Most likely it's a loose object. */
                status = sha1_loose_object_info(sha1, sizep);
@@ -2033,41 +2077,6 @@ static void *read_packed_sha1(const unsigned char *sha1,
        return data;
 }
 
-/*
- * This is meant to hold a *small* number of objects that you would
- * want read_sha1_file() to be able to return, but yet you do not want
- * to write them into the object store (e.g. a browse-only
- * application).
- */
-static struct cached_object {
-       unsigned char sha1[20];
-       enum object_type type;
-       void *buf;
-       unsigned long size;
-} *cached_objects;
-static int cached_object_nr, cached_object_alloc;
-
-static struct cached_object empty_tree = {
-       EMPTY_TREE_SHA1_BIN,
-       OBJ_TREE,
-       "",
-       0
-};
-
-static struct cached_object *find_cached_object(const unsigned char *sha1)
-{
-       int i;
-       struct cached_object *co = cached_objects;
-
-       for (i = 0; i < cached_object_nr; i++, co++) {
-               if (!hashcmp(co->sha1, sha1))
-                       return co;
-       }
-       if (!hashcmp(sha1, empty_tree.sha1))
-               return &empty_tree;
-       return NULL;
-}
-
 int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
                      unsigned char *sha1)
 {
@@ -2471,8 +2480,37 @@ int has_sha1_file(const unsigned char *sha1)
        return has_loose_object(sha1);
 }
 
+static void check_tree(const void *buf, size_t size)
+{
+       struct tree_desc desc;
+       struct name_entry entry;
+
+       init_tree_desc(&desc, buf, size);
+       while (tree_entry(&desc, &entry))
+               /* do nothing
+                * tree_entry() will die() on malformed entries */
+               ;
+}
+
+static void check_commit(const void *buf, size_t size)
+{
+       struct commit c;
+       memset(&c, 0, sizeof(c));
+       if (parse_commit_buffer(&c, buf, size))
+               die("corrupt commit");
+}
+
+static void check_tag(const void *buf, size_t size)
+{
+       struct tag t;
+       memset(&t, 0, sizeof(t));
+       if (parse_tag_buffer(&t, buf, size))
+               die("corrupt tag");
+}
+
 static int index_mem(unsigned char *sha1, void *buf, size_t size,
-                    int write_object, enum object_type type, const char *path)
+                    int write_object, enum object_type type,
+                    const char *path, int format_check)
 {
        int ret, re_allocated = 0;
 
@@ -2490,6 +2528,14 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
                        re_allocated = 1;
                }
        }
+       if (format_check) {
+               if (type == OBJ_TREE)
+                       check_tree(buf, size);
+               if (type == OBJ_COMMIT)
+                       check_commit(buf, size);
+               if (type == OBJ_TAG)
+                       check_tag(buf, size);
+       }
 
        if (write_object)
                ret = write_sha1_file(buf, size, typename(type), sha1);
@@ -2503,7 +2549,7 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
 #define SMALL_FILE_SIZE (32*1024)
 
 int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
-            enum object_type type, const char *path)
+            enum object_type type, const char *path, int format_check)
 {
        int ret;
        size_t size = xsize_t(st->st_size);
@@ -2512,23 +2558,25 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
                struct strbuf sbuf = STRBUF_INIT;
                if (strbuf_read(&sbuf, fd, 4096) >= 0)
                        ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
-                                       type, path);
+                                       type, path, format_check);
                else
                        ret = -1;
                strbuf_release(&sbuf);
        } else if (!size) {
-               ret = index_mem(sha1, NULL, size, write_object, type, path);
+               ret = index_mem(sha1, NULL, size, write_object, type, path,
+                               format_check);
        } else if (size <= SMALL_FILE_SIZE) {
                char *buf = xmalloc(size);
                if (size == read_in_full(fd, buf, size))
                        ret = index_mem(sha1, buf, size, write_object, type,
-                                       path);
+                                       path, format_check);
                else
                        ret = error("short read %s", strerror(errno));
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-               ret = index_mem(sha1, buf, size, write_object, type, path);
+               ret = index_mem(sha1, buf, size, write_object, type, path,
+                               format_check);
                munmap(buf, size);
        }
        close(fd);
@@ -2546,7 +2594,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
                if (fd < 0)
                        return error("open(\"%s\"): %s", path,
                                     strerror(errno));
-               if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path) < 0)
+               if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path, 0) < 0)
                        return error("%s: failed to insert into database",
                                     path);
                break;
index 9b3c4457f229041784edfa65218aa09de7a5eff8..07e8883ceb297bdde79f06ffa2d0519581ee60b9 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -63,7 +63,8 @@ void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc)
 
 void strbuf_grow(struct strbuf *sb, size_t extra)
 {
-       if (sb->len + extra + 1 <= sb->len)
+       if (unsigned_add_overflows(extra, 1) ||
+           unsigned_add_overflows(sb->len, extra + 1))
                die("you want to use way too much memory");
        if (!sb->alloc)
                sb->buf = NULL;
@@ -152,7 +153,7 @@ int strbuf_cmp(const struct strbuf *a, const struct strbuf *b)
 void strbuf_splice(struct strbuf *sb, size_t pos, size_t len,
                                   const void *data, size_t dlen)
 {
-       if (pos + len < pos)
+       if (unsigned_add_overflows(pos, len))
                die("you want to use way too much memory");
        if (pos > sb->len)
                die("`pos' is too far after the end of the buffer");
index 9b023a25841cb7a9c1faecc30c484eba0e16d6af..51681189e8380cc43ed26d35526a6dff78f1a24c 100644 (file)
@@ -153,6 +153,7 @@ struct string_list_item *string_list_append(struct string_list *list, const char
        ALLOC_GROW(list->items, list->nr + 1, list->alloc);
        list->items[list->nr].string =
                list->strdup_strings ? xstrdup(string) : (char *)string;
+       list->items[list->nr].util = NULL;
        return list->items + list->nr++;
 }
 
index b9bb95feaa5088254b002c2806c2c2ae9e9d7be4..143eb1f24092df070c5ddf75340bebb319a0c746 100644 (file)
@@ -82,7 +82,12 @@ gitweb_run () {
                }
                close O;
        ' gitweb.output &&
-       if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi
+       if grep '^[[]' gitweb.log >/dev/null 2>&1; then
+               test_debug 'cat gitweb.log >&2' &&
+               false
+       else
+               true
+       fi
 
        # gitweb.log is left for debugging
        # gitweb.output is used to parse HTTP output
index 20924506af886c8d0ce28f3fcb2742214d80020f..ae266147b6db9e06abeb4dc30e4271b6c611deac 100755 (executable)
@@ -19,7 +19,7 @@ usage: test-parse-options <options>
     --set23               set integer to 23
     -t <time>             get timestamp of <time>
     -L, --length <str>    get length of <str>
-    -F, --file <FILE>     set file to <FILE>
+    -F, --file <file>     set file to <file>
 
 String options
     -s, --string <string>
index 680d7d68612b168a2642ab64e7bc6c15c316d5b4..9bee8bfd2e063c40bae2d76930370bd9e8ba8fa5 100755 (executable)
@@ -12,4 +12,17 @@ test_expect_success 'character classes (isspace, isalpha etc.)' '
        test-ctype
 '
 
+test_expect_success 'mktemp to nonexistent directory prints filename' '
+       test_must_fail test-mktemp doesnotexist/testXXXXXX 2>err &&
+       grep "doesnotexist/test" err
+'
+
+test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' '
+       mkdir cannotwrite &&
+       chmod -w cannotwrite &&
+       test_when_finished "chmod +w cannotwrite" &&
+       test_must_fail test-mktemp cannotwrite/testXXXXXX 2>err &&
+       grep "cannotwrite/test" err
+'
+
 test_done
index d3225ada681d205f43914a6b59f4bdf4f2263451..99a314b08078960b8736ce94890e805943e4f970 100755 (executable)
@@ -76,60 +76,6 @@ test_expect_success 'obj pool: high-water mark' '
        test_cmp expected actual
 '
 
-test_expect_success 'line buffer' '
-       echo HELLO >expected1 &&
-       printf "%s\n" "" HELLO >expected2 &&
-       echo >expected3 &&
-       printf "%s\n" "" Q | q_to_nul >expected4 &&
-       printf "%s\n" foo "" >expected5 &&
-       printf "%s\n" "" foo >expected6 &&
-
-       test-line-buffer <<-\EOF >actual1 &&
-       5
-       HELLO
-       EOF
-
-       test-line-buffer <<-\EOF >actual2 &&
-       0
-
-       5
-       HELLO
-       EOF
-
-       q_to_nul <<-\EOF |
-       1
-       Q
-       EOF
-       test-line-buffer >actual3 &&
-
-       q_to_nul <<-\EOF |
-       0
-
-       1
-       Q
-       EOF
-       test-line-buffer >actual4 &&
-
-       test-line-buffer <<-\EOF >actual5 &&
-       5
-       foo
-       EOF
-
-       test-line-buffer <<-\EOF >actual6 &&
-       0
-
-       5
-       foo
-       EOF
-
-       test_cmp expected1 actual1 &&
-       test_cmp expected2 actual2 &&
-       test_cmp expected3 actual3 &&
-       test_cmp expected4 actual4 &&
-       test_cmp expected5 actual5 &&
-       test_cmp expected6 actual6
-'
-
 test_expect_success 'string pool' '
        echo a does not equal b >expected.differ &&
        echo a equals a >expected.match &&
diff --git a/t/t0081-line-buffer.sh b/t/t0081-line-buffer.sh
new file mode 100755 (executable)
index 0000000..550fad0
--- /dev/null
@@ -0,0 +1,201 @@
+#!/bin/sh
+
+test_description="Test the svn importer's input handling routines.
+
+These tests exercise the line_buffer library, but their real purpose
+is to check the assumptions that library makes of the platform's input
+routines.  Processes engaged in bi-directional communication would
+hang if fread or fgets is too greedy.
+
+While at it, check that input of newlines and null bytes are handled
+correctly.
+"
+. ./test-lib.sh
+
+test -n "$GIT_REMOTE_SVN_TEST_BIG_FILES" && test_set_prereq EXPENSIVE
+
+generate_tens_of_lines () {
+       tens=$1 &&
+       line=$2 &&
+
+       i=0 &&
+       while test $i -lt "$tens"
+       do
+               for j in a b c d e f g h i j
+               do
+                       echo "$line"
+               done &&
+               : $((i = $i + 1)) ||
+               return
+       done
+}
+
+long_read_test () {
+       : each line is 10 bytes, including newline &&
+       line=abcdefghi &&
+       echo "$line" >expect &&
+
+       if ! test_declared_prereq PIPE
+       then
+               echo >&4 "long_read_test: need to declare PIPE prerequisite"
+               return 127
+       fi &&
+       tens_of_lines=$(($1 / 100 + 1)) &&
+       lines=$(($tens_of_lines * 10)) &&
+       readsize=$((($lines - 1) * 10 + 3)) &&
+       copysize=7 &&
+       rm -f input &&
+       mkfifo input &&
+       {
+               {
+                       generate_tens_of_lines $tens_of_lines "$line" &&
+                       sleep 100
+               } >input &
+       } &&
+       test-line-buffer input <<-EOF >output &&
+       read $readsize
+       copy $copysize
+       EOF
+       kill $! &&
+       test_line_count = $lines output &&
+       tail -n 1 <output >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success 'setup: have pipes?' '
+      rm -f frob &&
+      if mkfifo frob
+      then
+               test_set_prereq PIPE
+      fi
+'
+
+test_expect_success 'hello world' '
+       echo HELLO >expect &&
+       test-line-buffer <<-\EOF >actual &&
+       read 6
+       HELLO
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success PIPE '0-length read, no input available' '
+       >expect &&
+       rm -f input &&
+       mkfifo input &&
+       {
+               sleep 100 >input &
+       } &&
+       test-line-buffer input <<-\EOF >actual &&
+       read 0
+       copy 0
+       EOF
+       kill $! &&
+       test_cmp expect actual
+'
+
+test_expect_success '0-length read, send along greeting' '
+       echo HELLO >expect &&
+       test-line-buffer <<-\EOF >actual &&
+       read 0
+       copy 6
+       HELLO
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success PIPE '1-byte read, no input available' '
+       printf "%s" ab >expect &&
+       rm -f input &&
+       mkfifo input &&
+       {
+               {
+                       printf "%s" a &&
+                       printf "%s" b &&
+                       sleep 100
+               } >input &
+       } &&
+       test-line-buffer input <<-\EOF >actual &&
+       read 1
+       copy 1
+       EOF
+       kill $! &&
+       test_cmp expect actual
+'
+
+test_expect_success PIPE 'long read (around 8192 bytes)' '
+       long_read_test 8192
+'
+
+test_expect_success PIPE,EXPENSIVE 'longer read (around 65536 bytes)' '
+       long_read_test 65536
+'
+
+test_expect_success 'read from file descriptor' '
+       rm -f input &&
+       echo hello >expect &&
+       echo hello >input &&
+       echo copy 6 |
+       test-line-buffer "&4" 4<input >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'buffer_read_string copes with null byte' '
+       >expect &&
+       q_to_nul <<-\EOF | test-line-buffer >actual &&
+       read 2
+       Q
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'skip, copy null byte' '
+       echo Q | q_to_nul >expect &&
+       q_to_nul <<-\EOF | test-line-buffer >actual &&
+       skip 2
+       Q
+       copy 2
+       Q
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'read null byte' '
+       echo ">QhelloQ" | q_to_nul >expect &&
+       q_to_nul <<-\EOF | test-line-buffer >actual &&
+       binary 8
+       QhelloQ
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'long reads are truncated' '
+       echo foo >expect &&
+       test-line-buffer <<-\EOF >actual &&
+       read 5
+       foo
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'long copies are truncated' '
+       printf "%s\n" "" foo >expect &&
+       test-line-buffer <<-\EOF >actual &&
+       read 1
+
+       copy 5
+       foo
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'long binary reads are truncated' '
+       echo ">foo" >expect &&
+       test-line-buffer <<-\EOF >actual &&
+       binary 5
+       foo
+       EOF
+       test_cmp expect actual
+'
+
+test_done
index dd32432d626e4f3d192c2bbe4824772025bb08b1..6d52b824b115964c5551aaf4284205b98b885ce3 100755 (executable)
@@ -188,4 +188,17 @@ for args in "-w --stdin-paths" "--stdin-paths -w"; do
        pop_repo
 done
 
+test_expect_success 'corrupt tree' '
+       echo abc >malformed-tree
+       test_must_fail git hash-object -t tree malformed-tree
+'
+
+test_expect_success 'corrupt commit' '
+       test_must_fail git hash-object -t commit --stdin </dev/null
+'
+
+test_expect_success 'corrupt tag' '
+       test_must_fail git hash-object -t tag --stdin </dev/null
+'
+
 test_done
index d0e55465ff08698376f5d9fa86357d51bd57458c..53fb8228cf18e2b58f3ea63e98a0220fa0fd39f2 100755 (executable)
@@ -876,11 +876,25 @@ test_expect_success 'check split_cmdline return' "
        "
 
 test_expect_success 'git -c "key=value" support' '
-       test "z$(git -c name=value config name)" = zvalue &&
        test "z$(git -c core.name=value config core.name)" = zvalue &&
-       test "z$(git -c CamelCase=value config camelcase)" = zvalue &&
-       test "z$(git -c flag config --bool flag)" = ztrue &&
-       test_must_fail git -c core.name=value config name
+       test "z$(git -c foo.CamelCase=value config foo.camelcase)" = zvalue &&
+       test "z$(git -c foo.flag config --bool foo.flag)" = ztrue &&
+       test_must_fail git -c name=value config core.name
+'
+
+test_expect_success 'key sanity-checking' '
+       test_must_fail git config foo=bar &&
+       test_must_fail git config foo=.bar &&
+       test_must_fail git config foo.ba=r &&
+       test_must_fail git config foo.1bar &&
+       test_must_fail git config foo."ba
+                               z".bar &&
+       test_must_fail git config . false &&
+       test_must_fail git config .foo false &&
+       test_must_fail git config foo. false &&
+       test_must_fail git config .foo. false &&
+       git config foo.bar true &&
+       git config foo."ba =z".bar false
 '
 
 test_done
diff --git a/t/t2019-checkout-ambiguous-ref.sh b/t/t2019-checkout-ambiguous-ref.sh
new file mode 100755 (executable)
index 0000000..943541d
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='checkout handling of ambiguous (branch/tag) refs'
+. ./test-lib.sh
+
+test_expect_success 'setup ambiguous refs' '
+       test_commit branch file &&
+       git branch ambiguity &&
+       git branch vagueness &&
+       test_commit tag file &&
+       git tag ambiguity &&
+       git tag vagueness HEAD:file &&
+       test_commit other file
+'
+
+test_expect_success 'checkout ambiguous ref succeeds' '
+       git checkout ambiguity >stdout 2>stderr
+'
+
+test_expect_success 'checkout produces ambiguity warning' '
+       grep "warning.*ambiguous" stderr
+'
+
+test_expect_success 'checkout chooses branch over tag' '
+       echo refs/heads/ambiguity >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
+       echo branch >expect &&
+       test_cmp expect file
+'
+
+test_expect_success 'checkout reports switch to branch' '
+       grep "Switched to branch" stderr &&
+       ! grep "^HEAD is now at" stderr
+'
+
+test_expect_success 'checkout vague ref succeeds' '
+       git checkout vagueness >stdout 2>stderr &&
+       test_set_prereq VAGUENESS_SUCCESS
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout produces ambiguity warning' '
+       grep "warning.*ambiguous" stderr
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout chooses branch over tag' '
+       echo refs/heads/vagueness >expect &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expect actual &&
+       echo branch >expect &&
+       test_cmp expect file
+'
+
+test_expect_success VAGUENESS_SUCCESS 'checkout reports switch to branch' '
+       grep "Switched to branch" stderr &&
+       ! grep "^HEAD is now at" stderr
+'
+
+test_done
diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh
new file mode 100755 (executable)
index 0000000..0042145
--- /dev/null
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='checkout into detached HEAD state'
+. ./test-lib.sh
+
+check_detached () {
+       test_must_fail git symbolic-ref -q HEAD >/dev/null
+}
+
+check_not_detached () {
+       git symbolic-ref -q HEAD >/dev/null
+}
+
+reset () {
+       git checkout master &&
+       check_not_detached
+}
+
+test_expect_success 'setup' '
+       test_commit one &&
+       test_commit two &&
+       git branch branch &&
+       git tag tag
+'
+
+test_expect_success 'checkout branch does not detach' '
+       reset &&
+       git checkout branch &&
+       check_not_detached
+'
+
+test_expect_success 'checkout tag detaches' '
+       reset &&
+       git checkout tag &&
+       check_detached
+'
+
+test_expect_success 'checkout branch by full name detaches' '
+       reset &&
+       git checkout refs/heads/branch &&
+       check_detached
+'
+
+test_expect_success 'checkout non-ref detaches' '
+       reset &&
+       git checkout branch^ &&
+       check_detached
+'
+
+test_expect_success 'checkout ref^0 detaches' '
+       reset &&
+       git checkout branch^0 &&
+       check_detached
+'
+
+test_expect_success 'checkout --detach detaches' '
+       reset &&
+       git checkout --detach branch &&
+       check_detached
+'
+
+test_expect_success 'checkout --detach without branch name' '
+       reset &&
+       git checkout --detach &&
+       check_detached
+'
+
+test_expect_success 'checkout --detach errors out for non-commit' '
+       reset &&
+       test_must_fail git checkout --detach one^{tree} &&
+       check_not_detached
+'
+
+test_expect_success 'checkout --detach errors out for extra argument' '
+       reset &&
+       git checkout master &&
+       test_must_fail git checkout --detach tag one.t &&
+       check_not_detached
+'
+
+test_expect_success 'checkout --detached and -b are incompatible' '
+       reset &&
+       test_must_fail git checkout --detach -b newbranch tag &&
+       check_not_detached
+'
+
+test_expect_success 'checkout --detach moves HEAD' '
+       reset &&
+       git checkout one &&
+       git checkout --detach two &&
+       git diff --exit-code HEAD &&
+       git diff --exit-code two
+'
+
+test_done
index 44f5421be45579b10c5556a958404ad2daa02002..2b17311cb0870ea210d9b5cbe167363d13641d67 100755 (executable)
@@ -110,6 +110,20 @@ test_expect_success '--ignore-space-change makes merge succeed' '
        git merge-recursive --ignore-space-change HEAD^ -- HEAD remote
 '
 
+test_expect_success 'naive cherry-pick fails' '
+       git read-tree --reset -u HEAD &&
+       test_must_fail git cherry-pick --no-commit remote &&
+       git read-tree --reset -u HEAD &&
+       test_must_fail git cherry-pick remote &&
+       test_must_fail git update-index --refresh &&
+       grep "<<<<<<" text.txt
+'
+
+test_expect_success '-Xignore-space-change makes cherry-pick succeed' '
+       git read-tree --reset -u HEAD &&
+       git cherry-pick --no-commit -Xignore-space-change remote
+'
+
 test_expect_success '--ignore-space-change: our w/s-only change wins' '
        q_to_cr <<-\EOF >expected &&
            justice and holiness and is the nurse of his age and theQ
index 948ca1bce66cab317bf74984b22384f2625418c1..df921d1f33df34dd2b2631580f8d53b18270662a 100755 (executable)
@@ -3,12 +3,14 @@
 test_description='Test cherry-pick with directory/file conflicts'
 . ./test-lib.sh
 
-test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' '
+test_expect_success 'Initialize repository' '
        mkdir a &&
        >a/f &&
        git add a &&
-       git commit -m a &&
+       git commit -m a
+'
 
+test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' '
        mkdir b &&
        ln -s ../a b/a &&
        git add b &&
index 9a665205884fcdb1c5888558d6edf960e335e013..b8f81d07c33d3596fef5f5a361f4774d5e357fca 100755 (executable)
@@ -290,4 +290,15 @@ test_expect_success 'log -S requires an argument' '
        test_must_fail git log -S
 '
 
+test_expect_success 'diff --cached on unborn branch' '
+       echo ref: refs/heads/unborn >.git/HEAD &&
+       git diff --cached >result &&
+       test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached" result
+'
+
+test_expect_success 'diff --cached -- file on unborn branch' '
+       git diff --cached -- file0 >result &&
+       test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached_--_file0" result
+'
+
 test_done
diff --git a/t/t4013/diff.diff_--cached b/t/t4013/diff.diff_--cached
new file mode 100644 (file)
index 0000000..ff16e83
--- /dev/null
@@ -0,0 +1,38 @@
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..992913c
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,8 @@
++A
++B
++C
++D
++E
++F
++1
++2
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..10a8a9f
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,9 @@
++1
++2
++3
++4
++5
++6
++A
++B
++C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
diff --git a/t/t4013/diff.diff_--cached_--_file0 b/t/t4013/diff.diff_--cached_--_file0
new file mode 100644 (file)
index 0000000..b9bb858
--- /dev/null
@@ -0,0 +1,15 @@
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..10a8a9f
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,9 @@
++1
++2
++3
++4
++5
++6
++A
++B
++C
index 8096d8a337867b4afdc4b061fdc314fdd8eac185..37aeab0d5c7415a54384c90708626f6a5807acee 100755 (executable)
@@ -4,331 +4,307 @@ test_description='word diff colors'
 
 . ./test-lib.sh
 
-test_expect_success setup '
-
-       git config diff.color.old red &&
-       git config diff.color.new green &&
-       git config diff.color.func magenta
+cat >pre.simple <<-\EOF
+       h(4)
 
-'
+       a = b + c
+EOF
+cat >post.simple <<-\EOF
+       h(4),hh[44]
 
-word_diff () {
-       test_must_fail git diff --no-index "$@" pre post > output &&
-       test_decode_color <output >output.decrypted &&
-       test_cmp expect output.decrypted
-}
+       a = b + c
 
-cat > pre <<\EOF
-h(4)
+       aa = a
 
-a = b + c
+       aeff = aeff * ( aaa )
 EOF
+cat >expect.letter-runs-are-words <<-\EOF
+       <BOLD>diff --git a/pre b/post<RESET>
+       <BOLD>index 330b04f..5ed8eff 100644<RESET>
+       <BOLD>--- a/pre<RESET>
+       <BOLD>+++ b/post<RESET>
+       <CYAN>@@ -1,3 +1,7 @@<RESET>
+       h(4),<GREEN>hh<RESET>[44]
 
-cat > post <<\EOF
-h(4),hh[44]
-
-a = b + c
+       a = b + c<RESET>
 
-aa = a
+       <GREEN>aa = a<RESET>
 
-aeff = aeff * ( aaa )
+       <GREEN>aeff = aeff * ( aaa<RESET> )
 EOF
+cat >expect.non-whitespace-is-word <<-\EOF
+       <BOLD>diff --git a/pre b/post<RESET>
+       <BOLD>index 330b04f..5ed8eff 100644<RESET>
+       <BOLD>--- a/pre<RESET>
+       <BOLD>+++ b/post<RESET>
+       <CYAN>@@ -1,3 +1,7 @@<RESET>
+       h(4)<GREEN>,hh[44]<RESET>
 
-cat > expect <<\EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 330b04f..5ed8eff 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,3 +1,7 @@<RESET>
-<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+       a = b + c<RESET>
 
-a = b + c<RESET>
+       <GREEN>aa = a<RESET>
 
-<GREEN>aa = a<RESET>
-
-<GREEN>aeff = aeff * ( aaa )<RESET>
+       <GREEN>aeff = aeff * ( aaa )<RESET>
 EOF
 
-test_expect_success 'word diff with runs of whitespace' '
+word_diff () {
+       test_must_fail git diff --no-index "$@" pre post >output &&
+       test_decode_color <output >output.decrypted &&
+       test_cmp expect output.decrypted
+}
 
-       word_diff --color-words
+test_language_driver () {
+       lang=$1
+       test_expect_success "diff driver '$lang'" '
+               cp "$TEST_DIRECTORY/t4034/'"$lang"'/pre" \
+                       "$TEST_DIRECTORY/t4034/'"$lang"'/post" \
+                       "$TEST_DIRECTORY/t4034/'"$lang"'/expect" . &&
+               echo "* diff='"$lang"'" >.gitattributes &&
+               word_diff --color-words
+       '
+}
 
+test_expect_success setup '
+       git config diff.color.old red &&
+       git config diff.color.new green &&
+       git config diff.color.func magenta
 '
 
-test_expect_success '--word-diff=color' '
-
-       word_diff --word-diff=color
-
+test_expect_success 'set up pre and post with runs of whitespace' '
+       cp pre.simple pre &&
+       cp post.simple post
 '
 
-test_expect_success '--color --word-diff=color' '
-
+test_expect_success 'word diff with runs of whitespace' '
+       cat >expect <<-\EOF &&
+               <BOLD>diff --git a/pre b/post<RESET>
+               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>--- a/pre<RESET>
+               <BOLD>+++ b/post<RESET>
+               <CYAN>@@ -1,3 +1,7 @@<RESET>
+               <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+
+               a = b + c<RESET>
+
+               <GREEN>aa = a<RESET>
+
+               <GREEN>aeff = aeff * ( aaa )<RESET>
+       EOF
+       word_diff --color-words &&
+       word_diff --word-diff=color &&
        word_diff --color --word-diff=color
-
 '
 
-sed 's/#.*$//' > expect <<EOF
-diff --git a/pre b/post
-index 330b04f..5ed8eff 100644
---- a/pre
-+++ b/post
-@@ -1,3 +1,7 @@
--h(4)
-+h(4),hh[44]
-~
- # significant space
-~
- a = b + c
-~
-~
-+aa = a
-~
-~
-+aeff = aeff * ( aaa )
-~
-EOF
-
 test_expect_success '--word-diff=porcelain' '
-
+       sed 's/#.*$//' >expect <<-\EOF &&
+               diff --git a/pre b/post
+               index 330b04f..5ed8eff 100644
+               --- a/pre
+               +++ b/post
+               @@ -1,3 +1,7 @@
+               -h(4)
+               +h(4),hh[44]
+               ~
+                # significant space
+               ~
+                a = b + c
+               ~
+               ~
+               +aa = a
+               ~
+               ~
+               +aeff = aeff * ( aaa )
+               ~
+       EOF
        word_diff --word-diff=porcelain
-
 '
 
-cat > expect <<EOF
-diff --git a/pre b/post
-index 330b04f..5ed8eff 100644
---- a/pre
-+++ b/post
-@@ -1,3 +1,7 @@
-[-h(4)-]{+h(4),hh[44]+}
-
-a = b + c
-
-{+aa = a+}
-
-{+aeff = aeff * ( aaa )+}
-EOF
-
 test_expect_success '--word-diff=plain' '
+       cat >expect <<-\EOF &&
+               diff --git a/pre b/post
+               index 330b04f..5ed8eff 100644
+               --- a/pre
+               +++ b/post
+               @@ -1,3 +1,7 @@
+               [-h(4)-]{+h(4),hh[44]+}
 
-       word_diff --word-diff=plain
-
-'
+               a = b + c
 
-test_expect_success '--word-diff=plain --no-color' '
+               {+aa = a+}
 
+               {+aeff = aeff * ( aaa )+}
+       EOF
+       word_diff --word-diff=plain &&
        word_diff --word-diff=plain --no-color
-
 '
 
-cat > expect <<EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 330b04f..5ed8eff 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,3 +1,7 @@<RESET>
-<RED>[-h(4)-]<RESET><GREEN>{+h(4),hh[44]+}<RESET>
-
-a = b + c<RESET>
-
-<GREEN>{+aa = a+}<RESET>
+test_expect_success '--word-diff=plain --color' '
+       cat >expect <<-\EOF &&
+               <BOLD>diff --git a/pre b/post<RESET>
+               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>--- a/pre<RESET>
+               <BOLD>+++ b/post<RESET>
+               <CYAN>@@ -1,3 +1,7 @@<RESET>
+               <RED>[-h(4)-]<RESET><GREEN>{+h(4),hh[44]+}<RESET>
 
-<GREEN>{+aeff = aeff * ( aaa )+}<RESET>
-EOF
+               a = b + c<RESET>
 
-test_expect_success '--word-diff=plain --color' '
+               <GREEN>{+aa = a+}<RESET>
 
+               <GREEN>{+aeff = aeff * ( aaa )+}<RESET>
+       EOF
        word_diff --word-diff=plain --color
-
 '
 
-cat > expect <<\EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 330b04f..5ed8eff 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1 +1 @@<RESET>
-<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
-<CYAN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
-
-<GREEN>aa = a<RESET>
-
-<GREEN>aeff = aeff * ( aaa )<RESET>
-EOF
-
 test_expect_success 'word diff without context' '
-
+       cat >expect <<-\EOF &&
+               <BOLD>diff --git a/pre b/post<RESET>
+               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>--- a/pre<RESET>
+               <BOLD>+++ b/post<RESET>
+               <CYAN>@@ -1 +1 @@<RESET>
+               <RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+               <CYAN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
+
+               <GREEN>aa = a<RESET>
+
+               <GREEN>aeff = aeff * ( aaa )<RESET>
+       EOF
        word_diff --color-words --unified=0
-
 '
 
-cat > expect <<\EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 330b04f..5ed8eff 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,3 +1,7 @@<RESET>
-h(4),<GREEN>hh<RESET>[44]
-
-a = b + c<RESET>
-
-<GREEN>aa = a<RESET>
-
-<GREEN>aeff = aeff * ( aaa<RESET> )
-EOF
-cp expect expect.letter-runs-are-words
-
 test_expect_success 'word diff with a regular expression' '
-
+       cp expect.letter-runs-are-words expect &&
        word_diff --color-words="[a-z]+"
-
 '
 
-test_expect_success 'set a diff driver' '
+test_expect_success 'set up a diff driver' '
        git config diff.testdriver.wordRegex "[^[:space:]]" &&
-       cat <<EOF > .gitattributes
-pre diff=testdriver
-post diff=testdriver
-EOF
+       cat <<-\EOF >.gitattributes
+               pre diff=testdriver
+               post diff=testdriver
+       EOF
 '
 
 test_expect_success 'option overrides .gitattributes' '
-
+       cp expect.letter-runs-are-words expect &&
        word_diff --color-words="[a-z]+"
-
 '
 
-cat > expect <<\EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 330b04f..5ed8eff 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,3 +1,7 @@<RESET>
-h(4)<GREEN>,hh[44]<RESET>
-
-a = b + c<RESET>
-
-<GREEN>aa = a<RESET>
-
-<GREEN>aeff = aeff * ( aaa )<RESET>
-EOF
-cp expect expect.non-whitespace-is-word
-
 test_expect_success 'use regex supplied by driver' '
-
+       cp expect.non-whitespace-is-word expect &&
        word_diff --color-words
-
 '
 
-test_expect_success 'set diff.wordRegex option' '
+test_expect_success 'set up diff.wordRegex option' '
        git config diff.wordRegex "[[:alnum:]]+"
 '
 
-cp expect.letter-runs-are-words expect
-
 test_expect_success 'command-line overrides config' '
+       cp expect.letter-runs-are-words expect &&
        word_diff --color-words="[a-z]+"
 '
 
-cat > expect <<\EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 330b04f..5ed8eff 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,3 +1,7 @@<RESET>
-h(4),<GREEN>{+hh+}<RESET>[44]
-
-a = b + c<RESET>
+test_expect_success 'command-line overrides config: --word-diff-regex' '
+       cat >expect <<-\EOF &&
+               <BOLD>diff --git a/pre b/post<RESET>
+               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>--- a/pre<RESET>
+               <BOLD>+++ b/post<RESET>
+               <CYAN>@@ -1,3 +1,7 @@<RESET>
+               h(4),<GREEN>{+hh+}<RESET>[44]
 
-<GREEN>{+aa = a+}<RESET>
+               a = b + c<RESET>
 
-<GREEN>{+aeff = aeff * ( aaa+}<RESET> )
-EOF
+               <GREEN>{+aa = a+}<RESET>
 
-test_expect_success 'command-line overrides config: --word-diff-regex' '
+               <GREEN>{+aeff = aeff * ( aaa+}<RESET> )
+       EOF
        word_diff --color --word-diff-regex="[a-z]+"
 '
 
-cp expect.non-whitespace-is-word expect
-
 test_expect_success '.gitattributes override config' '
+       cp expect.non-whitespace-is-word expect &&
        word_diff --color-words
 '
 
-test_expect_success 'remove diff driver regex' '
-       git config --unset diff.testdriver.wordRegex
+test_expect_success 'setup: remove diff driver regex' '
+       test_might_fail git config --unset diff.testdriver.wordRegex
 '
 
-cat > expect <<\EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 330b04f..5ed8eff 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1,3 +1,7 @@<RESET>
-h(4),<GREEN>hh[44<RESET>]
-
-a = b + c<RESET>
+test_expect_success 'use configured regex' '
+       cat >expect <<-\EOF &&
+               <BOLD>diff --git a/pre b/post<RESET>
+               <BOLD>index 330b04f..5ed8eff 100644<RESET>
+               <BOLD>--- a/pre<RESET>
+               <BOLD>+++ b/post<RESET>
+               <CYAN>@@ -1,3 +1,7 @@<RESET>
+               h(4),<GREEN>hh[44<RESET>]
 
-<GREEN>aa = a<RESET>
+               a = b + c<RESET>
 
-<GREEN>aeff = aeff * ( aaa<RESET> )
-EOF
+               <GREEN>aa = a<RESET>
 
-test_expect_success 'use configured regex' '
+               <GREEN>aeff = aeff * ( aaa<RESET> )
+       EOF
        word_diff --color-words
 '
 
-echo 'aaa (aaa)' > pre
-echo 'aaa (aaa) aaa' > post
-
-cat > expect <<\EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index c29453b..be22f37 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1 +1 @@<RESET>
-aaa (aaa) <GREEN>aaa<RESET>
-EOF
-
 test_expect_success 'test parsing words for newline' '
-
+       echo "aaa (aaa)" >pre &&
+       echo "aaa (aaa) aaa" >post &&
+       cat >expect <<-\EOF &&
+               <BOLD>diff --git a/pre b/post<RESET>
+               <BOLD>index c29453b..be22f37 100644<RESET>
+               <BOLD>--- a/pre<RESET>
+               <BOLD>+++ b/post<RESET>
+               <CYAN>@@ -1 +1 @@<RESET>
+               aaa (aaa) <GREEN>aaa<RESET>
+       EOF
        word_diff --color-words="a+"
-
-
 '
 
-echo '(:' > pre
-echo '(' > post
-
-cat > expect <<\EOF
-<BOLD>diff --git a/pre b/post<RESET>
-<BOLD>index 289cb9d..2d06f37 100644<RESET>
-<BOLD>--- a/pre<RESET>
-<BOLD>+++ b/post<RESET>
-<CYAN>@@ -1 +1 @@<RESET>
-(<RED>:<RESET>
-EOF
-
 test_expect_success 'test when words are only removed at the end' '
-
+       echo "(:" >pre &&
+       echo "(" >post &&
+       cat >expect <<-\EOF &&
+               <BOLD>diff --git a/pre b/post<RESET>
+               <BOLD>index 289cb9d..2d06f37 100644<RESET>
+               <BOLD>--- a/pre<RESET>
+               <BOLD>+++ b/post<RESET>
+               <CYAN>@@ -1 +1 @@<RESET>
+               (<RED>:<RESET>
+       EOF
        word_diff --color-words=.
-
 '
 
-cat > expect <<\EOF
-diff --git a/pre b/post
-index 289cb9d..2d06f37 100644
---- a/pre
-+++ b/post
-@@ -1 +1 @@
--(:
-+(
-EOF
-
 test_expect_success '--word-diff=none' '
-
+       echo "(:" >pre &&
+       echo "(" >post &&
+       cat >expect <<-\EOF &&
+               diff --git a/pre b/post
+               index 289cb9d..2d06f37 100644
+               --- a/pre
+               +++ b/post
+               @@ -1 +1 @@
+               -(:
+               +(
+       EOF
        word_diff --word-diff=plain --word-diff=none
-
 '
 
+test_language_driver bibtex
+test_language_driver cpp
+test_language_driver csharp
+test_language_driver fortran
+test_language_driver html
+test_language_driver java
+test_language_driver objc
+test_language_driver pascal
+test_language_driver perl
+test_language_driver php
+test_language_driver python
+test_language_driver ruby
+test_language_driver tex
+
 test_done
diff --git a/t/t4034/bibtex/expect b/t/t4034/bibtex/expect
new file mode 100644 (file)
index 0000000..a157774
--- /dev/null
@@ -0,0 +1,15 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 95cd55b..ddcba9b 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,9 +1,10 @@<RESET>
+@article{aldous1987uie,<RESET>
+  title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},<RESET>
+  author={Aldous, <RED>D.<RESET><GREEN>David<RESET>},
+  journal={Information Theory, IEEE Transactions on},<RESET>
+  volume={<RED>33<RESET><GREEN>Bogus.<RESET>},
+  number={<RED>2<RESET><GREEN>4<RESET>},
+  pages={219--223},<RESET>
+  year=<GREEN>1987,<RESET>
+<GREEN>  note={This is in fact a rather funny read since ethernet works well in practice. The<RESET> {<RED>1987<RESET><GREEN>\em pre} reference is the right one, however.<RESET>}<RED>,<RESET>
+}<RESET>
diff --git a/t/t4034/bibtex/post b/t/t4034/bibtex/post
new file mode 100644 (file)
index 0000000..ddcba9b
--- /dev/null
@@ -0,0 +1,10 @@
+@article{aldous1987uie,
+  title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},
+  author={Aldous, David},
+  journal={Information Theory, IEEE Transactions on},
+  volume={Bogus.},
+  number={4},
+  pages={219--223},
+  year=1987,
+  note={This is in fact a rather funny read since ethernet works well in practice. The {\em pre} reference is the right one, however.}
+}
diff --git a/t/t4034/bibtex/pre b/t/t4034/bibtex/pre
new file mode 100644 (file)
index 0000000..95cd55b
--- /dev/null
@@ -0,0 +1,9 @@
+@article{aldous1987uie,
+  title={{Ultimate instability of exponential back-off protocol for acknowledgment-based transmission control of random access communication channels}},
+  author={Aldous, D.},
+  journal={Information Theory, IEEE Transactions on},
+  volume={33},
+  number={2},
+  pages={219--223},
+  year={1987},
+}
diff --git a/t/t4034/cpp/expect b/t/t4034/cpp/expect
new file mode 100644 (file)
index 0000000..37d1ea2
--- /dev/null
@@ -0,0 +1,36 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,19 +1,19 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/cpp/post b/t/t4034/cpp/post
new file mode 100644 (file)
index 0000000..7e8c026
--- /dev/null
@@ -0,0 +1,19 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/cpp/pre b/t/t4034/cpp/pre
new file mode 100644 (file)
index 0000000..23d5c8a
--- /dev/null
@@ -0,0 +1,19 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/csharp/expect b/t/t4034/csharp/expect
new file mode 100644 (file)
index 0000000..e5d1dd2
--- /dev/null
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 9106d63..dd5f421 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/csharp/post b/t/t4034/csharp/post
new file mode 100644 (file)
index 0000000..dd5f421
--- /dev/null
@@ -0,0 +1,18 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/csharp/pre b/t/t4034/csharp/pre
new file mode 100644 (file)
index 0000000..9106d63
--- /dev/null
@@ -0,0 +1,18 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/fortran/expect b/t/t4034/fortran/expect
new file mode 100644 (file)
index 0000000..b233dbd
--- /dev/null
@@ -0,0 +1,10 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 87f0d0b..d308da2 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,5 +1,5 @@<RESET>
+print *, "Hello World<RED>!<RESET><GREEN>?<RESET>"
+
+DO10I = 1,10<RESET>
+<RED>DO10I<RESET><GREEN>DO 10 I<RESET> = 1,10
+<RED>DO10I<RESET><GREEN>DO 1 0 I<RESET> = 1,10
diff --git a/t/t4034/fortran/post b/t/t4034/fortran/post
new file mode 100644 (file)
index 0000000..d308da2
--- /dev/null
@@ -0,0 +1,5 @@
+print *, "Hello World?"
+
+DO10I = 1,10
+DO 10 I = 1,10
+DO 1 0 I = 1,10
diff --git a/t/t4034/fortran/pre b/t/t4034/fortran/pre
new file mode 100644 (file)
index 0000000..87f0d0b
--- /dev/null
@@ -0,0 +1,5 @@
+print *, "Hello World!"
+
+DO10I = 1,10
+DO10I = 1,10
+DO10I = 1,10
diff --git a/t/t4034/html/expect b/t/t4034/html/expect
new file mode 100644 (file)
index 0000000..447b49a
--- /dev/null
@@ -0,0 +1,8 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 8ca4aea..46921e5 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,3 @@<RESET>
+<tag <GREEN>newattr="newvalue"<RESET>><GREEN>added<RESET> content</tag>
+<tag attr=<RED>"value"<RESET><GREEN>"newvalue"<RESET>><RED>content<RESET><GREEN>changed<RESET></tag>
+<<RED>tag<RESET><GREEN>newtag<RESET>>content <RED>&entity;<RESET><GREEN>&newentity;<RESET><<RED>/tag<RESET><GREEN>/newtag<RESET>>
diff --git a/t/t4034/html/post b/t/t4034/html/post
new file mode 100644 (file)
index 0000000..46921e5
--- /dev/null
@@ -0,0 +1,3 @@
+<tag newattr="newvalue">added content</tag>
+<tag attr="newvalue">changed</tag>
+<newtag>content &newentity;</newtag>
diff --git a/t/t4034/html/pre b/t/t4034/html/pre
new file mode 100644 (file)
index 0000000..8ca4aea
--- /dev/null
@@ -0,0 +1,3 @@
+<tag>content</tag>
+<tag attr="value">content</tag>
+<tag>content &entity;</tag>
diff --git a/t/t4034/java/expect b/t/t4034/java/expect
new file mode 100644 (file)
index 0000000..37d1ea2
--- /dev/null
@@ -0,0 +1,36 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 23d5c8a..7e8c026 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,19 +1,19 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/java/post b/t/t4034/java/post
new file mode 100644 (file)
index 0000000..7e8c026
--- /dev/null
@@ -0,0 +1,19 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/java/pre b/t/t4034/java/pre
new file mode 100644 (file)
index 0000000..23d5c8a
--- /dev/null
@@ -0,0 +1,19 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/objc/expect b/t/t4034/objc/expect
new file mode 100644 (file)
index 0000000..e5d1dd2
--- /dev/null
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 9106d63..dd5f421 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+Foo() : x(0<RED>&&1<RESET><GREEN>&42<RESET>) { <GREEN>bar(x);<RESET> }
+cout<<"Hello World<RED>!<RESET><GREEN>?<RESET>\n"<<endl;
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/objc/post b/t/t4034/objc/post
new file mode 100644 (file)
index 0000000..dd5f421
--- /dev/null
@@ -0,0 +1,18 @@
+Foo() : x(0&42) { bar(x); }
+cout<<"Hello World?\n"<<endl;
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/objc/pre b/t/t4034/objc/pre
new file mode 100644 (file)
index 0000000..9106d63
--- /dev/null
@@ -0,0 +1,18 @@
+Foo():x(0&&1){}
+cout<<"Hello World!\n"<<endl;
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/pascal/expect b/t/t4034/pascal/expect
new file mode 100644 (file)
index 0000000..2ce4230
--- /dev/null
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 077046c..8865e6b 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+writeln("Hello World<RED>!<RESET><GREEN>?<RESET>");
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
+<RED>a<RESET><GREEN>x<RESET>::<RED>b<RESET><GREEN>y<RESET>
diff --git a/t/t4034/pascal/post b/t/t4034/pascal/post
new file mode 100644 (file)
index 0000000..8865e6b
--- /dev/null
@@ -0,0 +1,18 @@
+writeln("Hello World?");
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
+x::y
diff --git a/t/t4034/pascal/pre b/t/t4034/pascal/pre
new file mode 100644 (file)
index 0000000..077046c
--- /dev/null
@@ -0,0 +1,18 @@
+writeln("Hello World!");
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
+a::b
diff --git a/t/t4034/perl/expect b/t/t4034/perl/expect
new file mode 100644 (file)
index 0000000..a1deb6b
--- /dev/null
@@ -0,0 +1,13 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index f6610d3..e8b72ef 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -4,8 +4,8 @@<RESET>
+
+package Frotz;<RESET>
+sub new {<RESET>
+       my <GREEN>(<RESET>$class<GREEN>, %opts)<RESET> = <RED>shift<RESET><GREEN>@_<RESET>;
+       return bless { <GREEN>xyzzy => "nitfol", %opts<RESET> }, $class;
+}<RESET>
+
+__END__<RESET>
diff --git a/t/t4034/perl/post b/t/t4034/perl/post
new file mode 100644 (file)
index 0000000..e8b72ef
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+
+package Frotz;
+sub new {
+       my ($class, %opts) = @_;
+       return bless { xyzzy => "nitfol", %opts }, $class;
+}
+
+__END__
+=head1 NAME
+
+frotz - Frotz
+
+=head1 SYNOPSIS
+
+  use frotz;
+
+  $nitfol = new Frotz();
+
+=cut
diff --git a/t/t4034/perl/pre b/t/t4034/perl/pre
new file mode 100644 (file)
index 0000000..f6610d3
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+
+package Frotz;
+sub new {
+       my $class = shift;
+       return bless {}, $class;
+}
+
+__END__
+=head1 NAME
+
+frotz - Frotz
+
+=head1 SYNOPSIS
+
+  use frotz;
+
+  $nitfol = new Frotz();
+
+=cut
diff --git a/t/t4034/php/expect b/t/t4034/php/expect
new file mode 100644 (file)
index 0000000..0404408
--- /dev/null
@@ -0,0 +1,35 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index cf6e06b..4420a49 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,18 +1,18 @@<RESET>
+<GREEN>(<RESET>$var<GREEN>)<RESET> $ var
+<?="Hello World<RED>!<RESET><GREEN>?<RESET>"?>
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/php/post b/t/t4034/php/post
new file mode 100644 (file)
index 0000000..4420a49
--- /dev/null
@@ -0,0 +1,18 @@
+($var) $ var
+<?="Hello World?"?>
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/php/pre b/t/t4034/php/pre
new file mode 100644 (file)
index 0000000..cf6e06b
--- /dev/null
@@ -0,0 +1,18 @@
+$var $var
+<?= "Hello World!" ?>
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/python/expect b/t/t4034/python/expect
new file mode 100644 (file)
index 0000000..8abb8a4
--- /dev/null
@@ -0,0 +1,34 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 438f776..68baf34 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,17 +1,17 @@<RESET>
+print<RED>u<RESET> "Hello World<RED>!<RESET><GREEN>?<RESET>\n"<GREEN>; print<RESET>
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>) u<RESET>'<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>?<RED>b<RESET><GREEN>y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/python/post b/t/t4034/python/post
new file mode 100644 (file)
index 0000000..68baf34
--- /dev/null
@@ -0,0 +1,17 @@
+print "Hello World?\n"; print
+(1) (-1e10) (0xabcdef) u'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/python/pre b/t/t4034/python/pre
new file mode 100644 (file)
index 0000000..438f776
--- /dev/null
@@ -0,0 +1,17 @@
+print u"Hello World!\n"
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/ruby/expect b/t/t4034/ruby/expect
new file mode 100644 (file)
index 0000000..16e1dd5
--- /dev/null
@@ -0,0 +1,34 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 30ed9a1..7678f14 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,17 +1,17 @@<RESET>
+10.downto(1) {|<RED>x<RESET><GREEN>y<RESET>| puts <RED>x<RESET><GREEN>y<RESET>}
+<GREEN>(<RESET>1<GREEN>) (<RESET>-1e10<GREEN>) (<RESET>0xabcdef<GREEN>)<RESET> '<RED>x<RESET><GREEN>y<RESET>'
+[<RED>a<RESET><GREEN>x<RESET>] <RED>a<RESET><GREEN>x<RESET>-><RED>b a<RESET><GREEN>y x<RESET>.<RED>b<RESET><GREEN>y<RESET>
+!<RED>a<RESET><GREEN>x<RESET> ~<RED>a a<RESET><GREEN>x x<RESET>++ <RED>a<RESET><GREEN>x<RESET>-- <RED>a<RESET><GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b a<RESET><GREEN>y x<RESET>%<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<<RED>b a<RESET><GREEN>y x<RESET>>><RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>==<RED>b a<RESET><GREEN>y x<RESET>!=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>^<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>|<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>&&<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>||<RED>b<RESET>
+<RED>a?b<RESET><GREEN>y<RESET>
+<GREEN>x?y<RESET>:z
+<RED>a<RESET><GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>+=<RED>b a<RESET><GREEN>y x<RESET>-=<RED>b a<RESET><GREEN>y x<RESET>*=<RED>b a<RESET><GREEN>y x<RESET>/=<RED>b a<RESET><GREEN>y x<RESET>%=<RED>b a<RESET><GREEN>y x<RESET><<=<RED>b a<RESET><GREEN>y x<RESET>>>=<RED>b a<RESET><GREEN>y x<RESET>&=<RED>b a<RESET><GREEN>y x<RESET>^=<RED>b a<RESET><GREEN>y x<RESET>|=<RED>b<RESET>
+<RED>a<RESET><GREEN>y<RESET>
+<GREEN>x<RESET>,y
diff --git a/t/t4034/ruby/post b/t/t4034/ruby/post
new file mode 100644 (file)
index 0000000..7678f14
--- /dev/null
@@ -0,0 +1,17 @@
+10.downto(1) {|y| puts y}
+(1) (-1e10) (0xabcdef) 'y'
+[x] x->y x.y
+!x ~x x++ x-- x*y x&y
+x*y x/y x%y
+x+y x-y
+x<<y x>>y
+x<y x<=y x>y x>=y
+x==y x!=y
+x&y
+x^y
+x|y
+x&&y
+x||y
+x?y:z
+x=y x+=y x-=y x*=y x/=y x%=y x<<=y x>>=y x&=y x^=y x|=y
+x,y
diff --git a/t/t4034/ruby/pre b/t/t4034/ruby/pre
new file mode 100644 (file)
index 0000000..30ed9a1
--- /dev/null
@@ -0,0 +1,17 @@
+10.downto(1) {|x| puts x}
+1 -1e10 0xabcdef 'x'
+[a] a->b a.b
+!a ~a a++ a-- a*b a&b
+a*b a/b a%b
+a+b a-b
+a<<b a>>b
+a<b a<=b a>b a>=b
+a==b a!=b
+a&b
+a^b
+a|b
+a&&b
+a||b
+a?b:z
+a=b a+=b a-=b a*=b a/=b a%=b a<<=b a>>=b a&=b a^=b a|=b
+a,y
diff --git a/t/t4034/tex/expect b/t/t4034/tex/expect
new file mode 100644 (file)
index 0000000..604969b
--- /dev/null
@@ -0,0 +1,9 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index 2b2dfcb..65cab61 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,4 +1,4 @@<RESET>
+\section{Something <GREEN>new<RESET>}
+<RED>\emph<RESET><GREEN>\textbf<RESET>{Macro style}
+{<RED>\em<RESET><GREEN>\bfseries<RESET> State toggle style}
+\\[<RED>1em<RESET><GREEN>1cm<RESET>]
diff --git a/t/t4034/tex/post b/t/t4034/tex/post
new file mode 100644 (file)
index 0000000..65cab61
--- /dev/null
@@ -0,0 +1,4 @@
+\section{Something new}
+\textbf{Macro style}
+{\bfseries State toggle style}
+\\[1cm]
diff --git a/t/t4034/tex/pre b/t/t4034/tex/pre
new file mode 100644 (file)
index 0000000..2b2dfcb
--- /dev/null
@@ -0,0 +1,4 @@
+\section{Something}
+\emph{Macro style}
+{\em State toggle style}
+\\[1em]
index 579c9e61054521301464bfc31351040d45966836..a33d510bf6b27a6bb2c640cfc9d11f462cbdf7a6 100755 (executable)
@@ -6,6 +6,7 @@
 test_description='git apply -p handling.'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh
 
 test_expect_success setup '
        mkdir sub &&
@@ -62,8 +63,12 @@ test_expect_success 'apply (-p2) diff, mode change only' '
        old mode 100644
        new mode 100755
        EOF
-       chmod 644 file1 &&
-       git apply -p2 patch.chmod &&
+       test_chmod -x file1 &&
+       git apply --index -p2 patch.chmod &&
+       case $(git ls-files -s file1) in 100755*) : good;; *) false;; esac
+'
+
+test_expect_success FILEMODE 'file mode was changed' '
        test -x file1
 '
 
index 68e2652814c6a52265407b0fdfb70162eb634d53..d2c930de87f721a0e876351e511295ae0b094108 100755 (executable)
@@ -63,4 +63,40 @@ test_expect_success 'patch-id supports git-format-patch MIME output' '
        test_cmp patch-id_master patch-id_same
 '
 
+cat >nonl <<\EOF
+diff --git i/a w/a
+index e69de29..2e65efe 100644
+--- i/a
++++ w/a
+@@ -0,0 +1 @@
++a
+\ No newline at end of file
+diff --git i/b w/b
+index e69de29..6178079 100644
+--- i/b
++++ w/b
+@@ -0,0 +1 @@
++b
+EOF
+
+cat >withnl <<\EOF
+diff --git i/a w/a
+index e69de29..7898192 100644
+--- i/a
++++ w/a
+@@ -0,0 +1 @@
++a
+diff --git i/b w/b
+index e69de29..6178079 100644
+--- i/b
++++ w/b
+@@ -0,0 +1 @@
++b
+EOF
+
+test_expect_success 'patch-id handles no-nl-at-eof markers' '
+       cat nonl | calc_patch_id nonl &&
+       cat withnl | calc_patch_id withnl &&
+       test_cmp patch-id_nonl patch-id_withnl
+'
 test_done
index 884a5e567c308e976ce50036dc93c3932b717603..a5f458533fd071c13550a72bf251e2e10c109637 100755 (executable)
@@ -124,7 +124,7 @@ test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setti
        (
                cd downstream &&
                git fetch --recurse-submodules >../actual.out 2>../actual.err &&
-               git config -f --unset .gitmodules submodule.submodule.fetchRecurseSubmodules true &&
+               git config --unset -f .gitmodules submodule.submodule.fetchRecurseSubmodules &&
                git config --unset submodule.submodule.fetchRecurseSubmodules
        ) &&
        test_cmp expect.out actual.out &&
diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh
new file mode 100755 (executable)
index 0000000..b10685a
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='miscellaneous rev-list tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo content1 >wanted_file &&
+       echo content2 >unwanted_file &&
+       git add wanted_file unwanted_file &&
+       git commit -m one
+'
+
+test_expect_success 'rev-list --objects heeds pathspecs' '
+       git rev-list --objects HEAD -- wanted_file >output &&
+       grep wanted_file output &&
+       ! grep unwanted_file output
+'
+
+test_expect_success 'rev-list --objects with pathspecs and deeper paths' '
+       mkdir foo &&
+       >foo/file &&
+       git add foo/file &&
+       git commit -m two &&
+
+       git rev-list --objects HEAD -- foo >output &&
+       grep foo/file output &&
+
+       git rev-list --objects HEAD -- foo/file >output &&
+       grep foo/file output &&
+       ! grep unwanted_file output
+'
+
+test_expect_success 'rev-list --objects with pathspecs and copied files' '
+       git checkout --orphan junio-testcase &&
+       git rm -rf . &&
+
+       mkdir two &&
+       echo frotz >one &&
+       cp one two/three &&
+       git add one two/three &&
+       test_tick &&
+       git commit -m that &&
+
+       ONE=$(git rev-parse HEAD:one)
+       git rev-list --objects HEAD two >output &&
+       grep "$ONE two/three" output &&
+       ! grep one output
+'
+
+test_done
index d8ad25036f325ecdcc71257614e19b5e6ab2cdc5..e5be13c271c92ce7c51601ee1eaf966d849d8ae4 100755 (executable)
@@ -238,6 +238,10 @@ test_expect_success 'ensure "status --cached --recursive" preserves the --cached
                ) &&
                git submodule status --cached --recursive -- nested1 > ../actual
        ) &&
+       if test_have_prereq MINGW
+       then
+               dos2unix actual
+       fi &&
        test_cmp expect actual
 '
 
index 162527c2114955df2c55db9bf37db59d05fe75f8..d551b77ce6d94acce80069f2197f0169ee59e6d5 100755 (executable)
@@ -10,7 +10,12 @@ Tests for selected commit options.'
 . ./test-lib.sh
 
 commit_msg_is () {
-       test "`git log --pretty=format:%s%b -1`" = "$1"
+       expect=commit_msg_is.expect
+       actual=commit_msg_is.actual
+
+       printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect &&
+       printf "%s" "$1" >$actual &&
+       test_cmp $expect $actual
 }
 
 # A sanity check to see if commit is working at all.
index ff189624d48fb9f68997395d121d4e7056511245..5b4b694f1801f5c2284346f882cb496df9e7d74e 100755 (executable)
@@ -132,6 +132,18 @@ test_expect_success 'with hook (-c)' '
 
 '
 
+test_expect_success 'with hook (merge)' '
+
+       head=`git rev-parse HEAD` &&
+       git checkout -b other HEAD@{1} &&
+       echo "more" >> file &&
+       git add file &&
+       git commit -m other &&
+       git checkout - &&
+       git merge other &&
+       test "`git log -1 --pretty=format:%s`" = merge
+'
+
 cat > "$HOOK" <<'EOF'
 #!/bin/sh
 exit 1
index 4d5ce4e682c1ea69034c3f7789a8ac0c89b12466..5f731a1177267e76f19dbe1605a4d0f2e8e749bd 100755 (executable)
@@ -156,4 +156,20 @@ test_expect_success 'will not overwrite untracked file on unborn branch' '
        test_cmp important c0.c
 '
 
+test_expect_success 'set up unborn branch and content' '
+       git symbolic-ref HEAD refs/heads/unborn &&
+       rm -f .git/index &&
+       echo foo > tracked-file &&
+       git add tracked-file &&
+       echo bar > untracked-file
+'
+
+test_expect_failure 'will not clobber WT/index when merging into unborn' '
+       git merge master &&
+       grep foo tracked-file &&
+       git show :tracked-file >expect &&
+       grep foo expect &&
+       grep bar untracked-file
+'
+
 test_done
index d78bdec330cd8b9505aa92ff2e81e0f716b9a741..dc838c93bcb00a80ad56c4d1233009fe9220b90e 100755 (executable)
@@ -16,23 +16,33 @@ Testing basic merge tool invocation'
 test_expect_success 'setup' '
     git config rerere.enabled true &&
     echo master >file1 &&
+    echo master file11 >file11 &&
+    echo master file12 >file12 &&
+    echo master file13 >file13 &&
+    echo master file14 >file14 &&
     mkdir subdir &&
     echo master sub >subdir/file3 &&
-    git add file1 subdir/file3 &&
-    git commit -m "added file1" &&
+    git add file1 file1[1-4] subdir/file3 &&
+    git commit -m "add initial versions" &&
 
     git checkout -b branch1 master &&
     echo branch1 change >file1 &&
     echo branch1 newfile >file2 &&
+    echo branch1 change file11 >file11 &&
+    echo branch1 change file13 >file13 &&
     echo branch1 sub >subdir/file3 &&
-    git add file1 file2 subdir/file3 &&
+    git add file1 file11 file13 file2 subdir/file3 &&
+    git rm file12 &&
     git commit -m "branch1 changes" &&
 
     git checkout master &&
     echo master updated >file1 &&
     echo master new >file2 &&
+    echo master updated file12 >file12 &&
+    echo master updated file14 >file14 &&
     echo master new sub >subdir/file3 &&
-    git add file1 file2 subdir/file3 &&
+    git add file1 file12 file14 file2 subdir/file3 &&
+    git rm file11 &&
     git commit -m "master updates" &&
 
     git config merge.tool mytool &&
@@ -46,6 +56,8 @@ test_expect_success 'custom mergetool' '
     ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
     test "$(cat file1)" = "master updated" &&
     test "$(cat file2)" = "master new" &&
     test "$(cat subdir/file3)" = "master new sub" &&
@@ -59,6 +71,8 @@ test_expect_success 'mergetool crlf' '
     ( yes "" | git mergetool file1 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool file2 >/dev/null 2>&1 ) &&
     ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
     test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
     test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
     test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
@@ -82,6 +96,8 @@ test_expect_success 'mergetool on file in parent dir' '
        cd subdir &&
        ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
        ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+       ( yes "d" | git mergetool ../file11 >/dev/null 2>&1 ) &&
+       ( yes "d" | git mergetool ../file12 >/dev/null 2>&1 ) &&
        test "$(cat ../file1)" = "master updated" &&
        test "$(cat ../file2)" = "master new" &&
        git commit -m "branch1 resolved with mergetool - subdir"
@@ -92,6 +108,8 @@ test_expect_success 'mergetool skips autoresolved' '
     git checkout -b test4 branch1 &&
     test_must_fail git merge master &&
     test -n "$(git ls-files -u)" &&
+    ( yes "d" | git mergetool file11 >/dev/null 2>&1 ) &&
+    ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) &&
     output="$(git mergetool --no-prompt)" &&
     test "$output" = "No files need merging" &&
     git reset --hard
@@ -102,13 +120,23 @@ test_expect_success 'mergetool merges all from subdir' '
        cd subdir &&
        git config rerere.enabled false &&
        test_must_fail git merge master &&
-       git mergetool --no-prompt &&
+       ( yes "d" "d" | git mergetool --no-prompt ) &&
        test "$(cat ../file1)" = "master updated" &&
        test "$(cat ../file2)" = "master new" &&
        test "$(cat file3)" = "master new sub" &&
-       git add ../file1 ../file2 file3 &&
        git commit -m "branch2 resolved by mergetool from subdir"
     )
 '
 
+test_expect_success 'mergetool skips resolved paths when rerere is active' '
+    git config rerere.enabled true &&
+    rm -rf .git/rr-cache &&
+    git checkout -b test5 branch1
+    test_must_fail git merge master >/dev/null 2>&1 &&
+    ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) &&
+    output="$(yes "n" | git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git reset --hard
+'
+
 test_done
index 88a9751dd3c73a39a15ec3d18d3dddb7790706bb..5a6a4b9b7a6e6abbb9570ddceaac68baf5c2690b 100755 (executable)
@@ -9,6 +9,30 @@ reinit_git () {
        git init
 }
 
+properties () {
+       while test "$#" -ne 0
+       do
+               property="$1" &&
+               value="$2" &&
+               printf "%s\n" "K ${#property}" &&
+               printf "%s\n" "$property" &&
+               printf "%s\n" "V ${#value}" &&
+               printf "%s\n" "$value" &&
+               shift 2 ||
+               return 1
+       done
+}
+
+text_no_props () {
+       text="$1
+" &&
+       printf "%s\n" "Prop-content-length: 10" &&
+       printf "%s\n" "Text-content-length: ${#text}" &&
+       printf "%s\n" "Content-length: $((${#text} + 10))" &&
+       printf "%s\n" "" "PROPS-END" &&
+       printf "%s\n" "$text"
+}
+
 >empty
 
 test_expect_success 'empty dump' '
@@ -18,13 +42,686 @@ test_expect_success 'empty dump' '
        git fast-import <stream
 '
 
-test_expect_success 'v3 dumps not supported' '
+test_expect_success 'v4 dumps not supported' '
        reinit_git &&
-       echo "SVN-fs-dump-format-version: 3" >input &&
-       test_must_fail test-svn-fe input >stream &&
+       echo "SVN-fs-dump-format-version: 4" >v4.dump &&
+       test_must_fail test-svn-fe v4.dump >stream &&
        test_cmp empty stream
 '
 
+test_expect_failure 'empty revision' '
+       reinit_git &&
+       printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
+       cat >emptyrev.dump <<-\EOF &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 0
+       Content-length: 0
+
+       Revision-number: 2
+       Prop-content-length: 0
+       Content-length: 0
+
+       EOF
+       test-svn-fe emptyrev.dump >stream &&
+       git fast-import <stream &&
+       git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'empty properties' '
+       reinit_git &&
+       printf "rev <nobody, nobody@local>: %s\n" "" "" >expect &&
+       cat >emptyprop.dump <<-\EOF &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Revision-number: 2
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+       EOF
+       test-svn-fe emptyprop.dump >stream &&
+       git fast-import <stream &&
+       git log -p --format="rev <%an, %ae>: %s" HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'author name and commit message' '
+       reinit_git &&
+       echo "<author@example.com, author@example.com@local>" >expect.author &&
+       cat >message <<-\EOF &&
+       A concise summary of the change
+
+       A detailed description of the change, why it is needed, what
+       was broken and why applying this is the best course of action.
+
+       * file.c
+           Details pertaining to an individual file.
+       EOF
+       {
+               properties \
+                       svn:author author@example.com \
+                       svn:log "$(cat message)" &&
+               echo PROPS-END
+       } >props &&
+       {
+               echo "SVN-fs-dump-format-version: 3" &&
+               echo &&
+               echo "Revision-number: 1" &&
+               echo Prop-content-length: $(wc -c <props) &&
+               echo Content-length: $(wc -c <props) &&
+               echo &&
+               cat props
+       } >log.dump &&
+       test-svn-fe log.dump >stream &&
+       git fast-import <stream &&
+       git log -p --format="%B" HEAD >actual.log &&
+       git log --format="<%an, %ae>" >actual.author &&
+       test_cmp message actual.log &&
+       test_cmp expect.author actual.author
+'
+
+test_expect_success 'unsupported properties are ignored' '
+       reinit_git &&
+       echo author >expect &&
+       cat >extraprop.dump <<-\EOF &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 56
+       Content-length: 56
+
+       K 8
+       nonsense
+       V 1
+       y
+       K 10
+       svn:author
+       V 6
+       author
+       PROPS-END
+       EOF
+       test-svn-fe extraprop.dump >stream &&
+       git fast-import <stream &&
+       git log -p --format=%an HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_failure 'timestamp and empty file' '
+       echo author@example.com >expect.author &&
+       echo 1999-01-01 >expect.date &&
+       echo file >expect.files &&
+       reinit_git &&
+       {
+               properties \
+                       svn:author author@example.com \
+                       svn:date "1999-01-01T00:01:002.000000Z" \
+                       svn:log "add empty file" &&
+               echo PROPS-END
+       } >props &&
+       {
+               cat <<-EOF &&
+               SVN-fs-dump-format-version: 3
+
+               Revision-number: 1
+               EOF
+               echo Prop-content-length: $(wc -c <props) &&
+               echo Content-length: $(wc -c <props) &&
+               echo &&
+               cat props &&
+               cat <<-\EOF
+
+               Node-path: empty-file
+               Node-kind: file
+               Node-action: add
+               Content-length: 0
+
+               EOF
+       } >emptyfile.dump &&
+       test-svn-fe emptyfile.dump >stream &&
+       git fast-import <stream &&
+       git log --format=%an HEAD >actual.author &&
+       git log --date=short --format=%ad HEAD >actual.date &&
+       git ls-tree -r --name-only HEAD >actual.files &&
+       test_cmp expect.author actual.author &&
+       test_cmp expect.date actual.date &&
+       test_cmp expect.files actual.files &&
+       git checkout HEAD empty-file &&
+       test_cmp empty file
+'
+
+test_expect_success 'directory with files' '
+       reinit_git &&
+       printf "%s\n" directory/file1 directory/file2 >expect.files &&
+       echo hi >hi &&
+       echo hello >hello &&
+       {
+               properties \
+                       svn:author author@example.com \
+                       svn:date "1999-02-01T00:01:002.000000Z" \
+                       svn:log "add directory with some files in it" &&
+               echo PROPS-END
+       } >props &&
+       {
+               cat <<-EOF &&
+               SVN-fs-dump-format-version: 3
+
+               Revision-number: 1
+               EOF
+               echo Prop-content-length: $(wc -c <props) &&
+               echo Content-length: $(wc -c <props) &&
+               echo &&
+               cat props &&
+               cat <<-\EOF &&
+
+               Node-path: directory
+               Node-kind: dir
+               Node-action: add
+               Prop-content-length: 10
+               Content-length: 10
+
+               PROPS-END
+
+               Node-path: directory/file1
+               Node-kind: file
+               Node-action: add
+               EOF
+               text_no_props hello &&
+               cat <<-\EOF &&
+               Node-path: directory/file2
+               Node-kind: file
+               Node-action: add
+               EOF
+               text_no_props hi
+       } >directory.dump &&
+       test-svn-fe directory.dump >stream &&
+       git fast-import <stream &&
+
+       git ls-tree -r --name-only HEAD >actual.files &&
+       git checkout HEAD directory &&
+       test_cmp expect.files actual.files &&
+       test_cmp hello directory/file1 &&
+       test_cmp hi directory/file2
+'
+
+test_expect_success 'node without action' '
+       cat >inaction.dump <<-\EOF &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: directory
+       Node-kind: dir
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+       EOF
+       test_must_fail test-svn-fe inaction.dump
+'
+
+test_expect_success 'action: add node without text' '
+       cat >textless.dump <<-\EOF &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: textless
+       Node-kind: file
+       Node-action: add
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+       EOF
+       test_must_fail test-svn-fe textless.dump
+'
+
+test_expect_failure 'change file mode but keep old content' '
+       reinit_git &&
+       cat >expect <<-\EOF &&
+       OBJID
+       :120000 100644 OBJID OBJID T    greeting
+       OBJID
+       :100644 120000 OBJID OBJID T    greeting
+       OBJID
+       :000000 100644 OBJID OBJID A    greeting
+       EOF
+       echo "link hello" >expect.blob &&
+       echo hello >hello &&
+       cat >filemode.dump <<-\EOF &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: greeting
+       Node-kind: file
+       Node-action: add
+       Prop-content-length: 10
+       Text-content-length: 11
+       Content-length: 21
+
+       PROPS-END
+       link hello
+
+       Revision-number: 2
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: greeting
+       Node-kind: file
+       Node-action: change
+       Prop-content-length: 33
+       Content-length: 33
+
+       K 11
+       svn:special
+       V 1
+       *
+       PROPS-END
+
+       Revision-number: 3
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: greeting
+       Node-kind: file
+       Node-action: change
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+       EOF
+       test-svn-fe filemode.dump >stream &&
+       git fast-import <stream &&
+       {
+               git rev-list HEAD |
+               git diff-tree --root --stdin |
+               sed "s/$_x40/OBJID/g"
+       } >actual &&
+       git show HEAD:greeting >actual.blob &&
+       git show HEAD^:greeting >actual.target &&
+       test_cmp expect actual &&
+       test_cmp expect.blob actual.blob &&
+       test_cmp hello actual.target
+'
+
+test_expect_success 'change file mode and reiterate content' '
+       reinit_git &&
+       cat >expect <<-\EOF &&
+       OBJID
+       :120000 100644 OBJID OBJID T    greeting
+       OBJID
+       :100644 120000 OBJID OBJID T    greeting
+       OBJID
+       :000000 100644 OBJID OBJID A    greeting
+       EOF
+       echo "link hello" >expect.blob &&
+       echo hello >hello &&
+       cat >filemode.dump <<-\EOF &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: greeting
+       Node-kind: file
+       Node-action: add
+       Prop-content-length: 10
+       Text-content-length: 11
+       Content-length: 21
+
+       PROPS-END
+       link hello
+
+       Revision-number: 2
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: greeting
+       Node-kind: file
+       Node-action: change
+       Prop-content-length: 33
+       Text-content-length: 11
+       Content-length: 44
+
+       K 11
+       svn:special
+       V 1
+       *
+       PROPS-END
+       link hello
+
+       Revision-number: 3
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: greeting
+       Node-kind: file
+       Node-action: change
+       Prop-content-length: 10
+       Text-content-length: 11
+       Content-length: 21
+
+       PROPS-END
+       link hello
+       EOF
+       test-svn-fe filemode.dump >stream &&
+       git fast-import <stream &&
+       {
+               git rev-list HEAD |
+               git diff-tree --root --stdin |
+               sed "s/$_x40/OBJID/g"
+       } >actual &&
+       git show HEAD:greeting >actual.blob &&
+       git show HEAD^:greeting >actual.target &&
+       test_cmp expect actual &&
+       test_cmp expect.blob actual.blob &&
+       test_cmp hello actual.target
+'
+
+test_expect_success 'deltas not supported' '
+       {
+               # (old) h + (inline) ello + (old) \n
+               printf "SVNQ%b%b%s" "Q\003\006\005\004" "\001Q\0204\001\002" "ello" |
+               q_to_nul
+       } >delta &&
+       {
+               properties \
+                       svn:author author@example.com \
+                       svn:date "1999-01-05T00:01:002.000000Z" \
+                       svn:log "add greeting" &&
+               echo PROPS-END
+       } >props &&
+       {
+               properties \
+                       svn:author author@example.com \
+                       svn:date "1999-01-06T00:01:002.000000Z" \
+                       svn:log "change it" &&
+               echo PROPS-END
+       } >props2 &&
+       {
+               echo SVN-fs-dump-format-version: 3 &&
+               echo &&
+               echo Revision-number: 1 &&
+               echo Prop-content-length: $(wc -c <props) &&
+               echo Content-length: $(wc -c <props) &&
+               echo &&
+               cat props &&
+               cat <<-\EOF &&
+
+               Node-path: hello
+               Node-kind: file
+               Node-action: add
+               Prop-content-length: 10
+               Text-content-length: 3
+               Content-length: 13
+
+               PROPS-END
+               hi
+
+               EOF
+               echo Revision-number: 2 &&
+               echo Prop-content-length: $(wc -c <props2) &&
+               echo Content-length: $(wc -c <props2) &&
+               echo &&
+               cat props2 &&
+               cat <<-\EOF &&
+
+               Node-path: hello
+               Node-kind: file
+               Node-action: change
+               Text-delta: true
+               Prop-content-length: 10
+               EOF
+               echo Text-content-length: $(wc -c <delta) &&
+               echo Content-length: $((10 + $(wc -c <delta))) &&
+               echo &&
+               echo PROPS-END &&
+               cat delta
+       } >delta.dump &&
+       test_must_fail test-svn-fe delta.dump
+'
+
+test_expect_success 'property deltas supported' '
+       reinit_git &&
+       cat >expect <<-\EOF &&
+       OBJID
+       :100755 100644 OBJID OBJID M    script.sh
+       EOF
+       {
+               properties \
+                       svn:author author@example.com \
+                       svn:date "1999-03-06T00:01:002.000000Z" \
+                       svn:log "make an executable, or chmod -x it" &&
+               echo PROPS-END
+       } >revprops &&
+       {
+               echo SVN-fs-dump-format-version: 3 &&
+               echo &&
+               echo Revision-number: 1 &&
+               echo Prop-content-length: $(wc -c <revprops) &&
+               echo Content-length: $(wc -c <revprops) &&
+               echo &&
+               cat revprops &&
+               echo &&
+               cat <<-\EOF &&
+               Node-path: script.sh
+               Node-kind: file
+               Node-action: add
+               Text-content-length: 0
+               Prop-content-length: 39
+               Content-length: 39
+
+               K 14
+               svn:executable
+               V 4
+               true
+               PROPS-END
+
+               EOF
+               echo Revision-number: 2 &&
+               echo Prop-content-length: $(wc -c <revprops) &&
+               echo Content-length: $(wc -c <revprops) &&
+               echo &&
+               cat revprops &&
+               echo &&
+               cat <<-\EOF
+               Node-path: script.sh
+               Node-kind: file
+               Node-action: change
+               Prop-delta: true
+               Prop-content-length: 30
+               Content-length: 30
+
+               D 14
+               svn:executable
+               PROPS-END
+               EOF
+       } >propdelta.dump &&
+       test-svn-fe propdelta.dump >stream &&
+       git fast-import <stream &&
+       {
+               git rev-list HEAD |
+               git diff-tree --stdin |
+               sed "s/$_x40/OBJID/g"
+       } >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'properties on /' '
+       reinit_git &&
+       cat <<-\EOF >expect &&
+       OBJID
+       OBJID
+       :000000 100644 OBJID OBJID A    greeting
+       EOF
+       sed -e "s/X$//" <<-\EOF >changeroot.dump &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: greeting
+       Node-kind: file
+       Node-action: add
+       Text-content-length: 0
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Revision-number: 2
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: X
+       Node-kind: dir
+       Node-action: change
+       Prop-delta: true
+       Prop-content-length: 43
+       Content-length: 43
+
+       K 10
+       svn:ignore
+       V 11
+       build-area
+
+       PROPS-END
+       EOF
+       test-svn-fe changeroot.dump >stream &&
+       git fast-import <stream &&
+       {
+               git rev-list HEAD |
+               git diff-tree --root --always --stdin |
+               sed "s/$_x40/OBJID/g"
+       } >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'deltas for typechange' '
+       reinit_git &&
+       cat >expect <<-\EOF &&
+       OBJID
+       :120000 100644 OBJID OBJID T    test-file
+       OBJID
+       :100755 120000 OBJID OBJID T    test-file
+       OBJID
+       :000000 100755 OBJID OBJID A    test-file
+       EOF
+       cat >deleteprop.dump <<-\EOF &&
+       SVN-fs-dump-format-version: 3
+
+       Revision-number: 1
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: test-file
+       Node-kind: file
+       Node-action: add
+       Prop-delta: true
+       Prop-content-length: 35
+       Text-content-length: 17
+       Content-length: 52
+
+       K 14
+       svn:executable
+       V 0
+
+       PROPS-END
+       link testing 123
+
+       Revision-number: 2
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: test-file
+       Node-kind: file
+       Node-action: change
+       Prop-delta: true
+       Prop-content-length: 53
+       Text-content-length: 17
+       Content-length: 70
+
+       K 11
+       svn:special
+       V 1
+       *
+       D 14
+       svn:executable
+       PROPS-END
+       link testing 231
+
+       Revision-number: 3
+       Prop-content-length: 10
+       Content-length: 10
+
+       PROPS-END
+
+       Node-path: test-file
+       Node-kind: file
+       Node-action: change
+       Prop-delta: true
+       Prop-content-length: 27
+       Text-content-length: 17
+       Content-length: 44
+
+       D 11
+       svn:special
+       PROPS-END
+       link testing 321
+       EOF
+       test-svn-fe deleteprop.dump >stream &&
+       git fast-import <stream &&
+       {
+               git rev-list HEAD |
+               git diff-tree --root --stdin |
+               sed "s/$_x40/OBJID/g"
+       } >actual &&
+       test_cmp expect actual
+'
+
+
 test_expect_success 'set up svn repo' '
        svnconf=$PWD/svnconf &&
        mkdir -p "$svnconf" &&
index 986bc14d58c30201e75f3d95d9b8245e997b99e9..6b1ba6c858562b7c085951fb1d69d4d2371e866c 100755 (executable)
@@ -42,6 +42,14 @@ echo "$@"'
 
 >empty
 
+test_expect_success 'setup: have pipes?' '
+       rm -f frob &&
+       if mkfifo frob
+       then
+               test_set_prereq PIPE
+       fi
+'
+
 ###
 ### series A
 ###
@@ -898,6 +906,77 @@ test_expect_success \
         git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
         compare_diff_raw expect actual'
 
+test_expect_success PIPE 'N: read and copy directory' '
+       cat >expect <<-\EOF
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file3/newf
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      file3/oldf
+       EOF
+       git update-ref -d refs/heads/N4 &&
+       rm -f backflow &&
+       mkfifo backflow &&
+       (
+               exec <backflow &&
+               cat <<-EOF &&
+               commit refs/heads/N4
+               committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+               data <<COMMIT
+               copy by tree hash, part 2
+               COMMIT
+
+               from refs/heads/branch^0
+               ls "file2"
+               EOF
+               read mode type tree filename &&
+               echo "M 040000 $tree file3"
+       ) |
+       git fast-import --cat-blob-fd=3 3>backflow &&
+       git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+       compare_diff_raw expect actual
+'
+
+test_expect_success PIPE 'N: empty directory reads as missing' '
+       cat <<-\EOF >expect &&
+       OBJNAME
+       :000000 100644 OBJNAME OBJNAME A        unrelated
+       EOF
+       echo "missing src" >expect.response &&
+       git update-ref -d refs/heads/read-empty &&
+       rm -f backflow &&
+       mkfifo backflow &&
+       (
+               exec <backflow &&
+               cat <<-EOF &&
+               commit refs/heads/read-empty
+               committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+               data <<COMMIT
+               read "empty" (missing) directory
+               COMMIT
+
+               M 100644 inline src/greeting
+               data <<BLOB
+               hello
+               BLOB
+               C src/greeting dst1/non-greeting
+               C src/greeting unrelated
+               # leave behind "empty" src directory
+               D src/greeting
+               ls "src"
+               EOF
+               read -r line &&
+               printf "%s\n" "$line" >response &&
+               cat <<-\EOF
+               D dst1
+               D dst2
+               EOF
+       ) |
+       git fast-import --cat-blob-fd=3 3>backflow &&
+       test_cmp expect.response response &&
+       git rev-list read-empty |
+       git diff-tree -r --root --stdin |
+       sed "s/$_x40/OBJNAME/g" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success \
        'N: copy root directory by tree hash' \
        'cat >expect <<-\EOF &&
@@ -1748,6 +1827,61 @@ test_expect_success \
     'cat input | git fast-import --export-marks=other.marks &&
     grep :1 other.marks'
 
+test_expect_success 'R: catch typo in marks file name' '
+       test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
+       echo "feature import-marks=nonexistent.marks" |
+       test_must_fail git fast-import
+'
+
+test_expect_success 'R: import and output marks can be the same file' '
+       rm -f io.marks &&
+       blob=$(echo hi | git hash-object --stdin) &&
+       cat >expect <<-EOF &&
+       :1 $blob
+       :2 $blob
+       EOF
+       git fast-import --export-marks=io.marks <<-\EOF &&
+       blob
+       mark :1
+       data 3
+       hi
+
+       EOF
+       git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF &&
+       blob
+       mark :2
+       data 3
+       hi
+
+       EOF
+       test_cmp expect io.marks
+'
+
+test_expect_success 'R: --import-marks=foo --output-marks=foo to create foo fails' '
+       rm -f io.marks &&
+       test_must_fail git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF
+       blob
+       mark :1
+       data 3
+       hi
+
+       EOF
+'
+
+test_expect_success 'R: --import-marks-if-exists' '
+       rm -f io.marks &&
+       blob=$(echo hi | git hash-object --stdin) &&
+       echo ":1 $blob" >expect &&
+       git fast-import --import-marks-if-exists=io.marks --export-marks=io.marks <<-\EOF &&
+       blob
+       mark :1
+       data 3
+       hi
+
+       EOF
+       test_cmp expect io.marks
+'
+
 cat >input << EOF
 feature import-marks=marks.out
 feature export-marks=marks.new
@@ -1806,6 +1940,11 @@ test_expect_success 'R: feature no-relative-marks should be honoured' '
     test_cmp marks.new non-relative.out
 '
 
+test_expect_success 'R: feature ls supported' '
+       echo "feature ls" |
+       git fast-import
+'
+
 test_expect_success 'R: feature cat-blob supported' '
        echo "feature cat-blob" |
        git fast-import
@@ -1931,14 +2070,6 @@ test_expect_success 'R: print two blobs to stdout' '
        test_cmp expect actual
 '
 
-test_expect_success 'setup: have pipes?' '
-       rm -f frob &&
-       if mkfifo frob
-       then
-               test_set_prereq PIPE
-       fi
-'
-
 test_expect_success PIPE 'R: copy using cat-file' '
        expect_id=$(git hash-object big) &&
        expect_len=$(wc -c <big) &&
index 7cf8cd8a2fed2191b31082c92a95598a3e06513d..463254c72734beaf74948b6c424367ef4fea9d1a 100755 (executable)
@@ -120,6 +120,7 @@ test_expect_success 'add notes with simple M command' '
 
 test_tick
 cat >input <<INPUT_END
+feature notes
 commit refs/notes/test
 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 data <<COMMIT
index 21cd286bb7abf33c944a8758ce8d702e83ca2022..afac5b56a87f3dcc6467b8ad260e1b59d041eb03 100755 (executable)
@@ -18,42 +18,34 @@ or warnings to log.'
 test_expect_success \
        'no commits: projects_list (implicit)' \
        'gitweb_run'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'no commits: projects_index' \
        'gitweb_run "a=project_index"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'no commits: .git summary (implicit)' \
        'gitweb_run "p=.git"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'no commits: .git commit (implicit HEAD)' \
        'gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'no commits: .git commitdiff (implicit HEAD)' \
        'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'no commits: .git tree (implicit HEAD)' \
        'gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'no commits: .git heads' \
        'gitweb_run "p=.git;a=heads"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'no commits: .git tags' \
        'gitweb_run "p=.git;a=tags"'
-test_debug 'cat gitweb.log'
 
 
 # ----------------------------------------------------------------------
@@ -69,52 +61,42 @@ test_expect_success \
 test_expect_success \
        'projects_list (implicit)' \
        'gitweb_run'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'projects_index' \
        'gitweb_run "a=project_index"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git summary (implicit)' \
        'gitweb_run "p=.git"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git commit (implicit HEAD)' \
        'gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git commitdiff (implicit HEAD, root commit)' \
        'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git commitdiff_plain (implicit HEAD, root commit)' \
        'gitweb_run "p=.git;a=commitdiff_plain"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git commit (HEAD)' \
        'gitweb_run "p=.git;a=commit;h=HEAD"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git tree (implicit HEAD)' \
        'gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git blob (file)' \
        'gitweb_run "p=.git;a=blob;f=file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git blob_plain (file)' \
        'gitweb_run "p=.git;a=blob_plain;f=file"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # nonexistent objects
@@ -122,37 +104,30 @@ test_debug 'cat gitweb.log'
 test_expect_success \
        '.git commit (non-existent)' \
        'gitweb_run "p=.git;a=commit;h=non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git commitdiff (non-existent)' \
        'gitweb_run "p=.git;a=commitdiff;h=non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git commitdiff (non-existent vs HEAD)' \
        'gitweb_run "p=.git;a=commitdiff;hp=non-existent;h=HEAD"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git tree (0000000000000000000000000000000000000000)' \
        'gitweb_run "p=.git;a=tree;h=0000000000000000000000000000000000000000"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git tag (0000000000000000000000000000000000000000)' \
        'gitweb_run "p=.git;a=tag;h=0000000000000000000000000000000000000000"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git blob (non-existent)' \
        'gitweb_run "p=.git;a=blob;f=non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        '.git blob_plain (non-existent)' \
        'gitweb_run "p=.git;a=blob_plain;f=non-existent"'
-test_debug 'cat gitweb.log'
 
 
 # ----------------------------------------------------------------------
@@ -161,7 +136,6 @@ test_debug 'cat gitweb.log'
 test_expect_success \
        'commitdiff(0): root' \
        'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): file added' \
@@ -169,21 +143,18 @@ test_expect_success \
         git add new_file &&
         git commit -a -m "File added." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): mode change' \
        'test_chmod +x new_file &&
         git commit -a -m "Mode changed." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): file renamed' \
        'git mv new_file renamed_file &&
         git commit -a -m "File renamed." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success SYMLINKS \
        'commitdiff(0): file to symlink' \
@@ -191,7 +162,6 @@ test_expect_success SYMLINKS \
         ln -s file renamed_file &&
         git commit -a -m "File to symlink." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): file deleted' \
@@ -199,7 +169,6 @@ test_expect_success \
         rm -f renamed_file &&
         git commit -a -m "File removed." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): file copied / new file' \
@@ -207,7 +176,6 @@ test_expect_success \
         git add file2 &&
         git commit -a -m "File copied." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): mode change and modified' \
@@ -215,7 +183,6 @@ test_expect_success \
         test_chmod +x file2 &&
         git commit -a -m "Mode change and modification." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): renamed and modified' \
@@ -233,7 +200,6 @@ EOF
         echo "Propter nomen suum." >> file3 &&
         git commit -a -m "File rename and modification." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): renamed, mode change and modified' \
@@ -242,7 +208,6 @@ test_expect_success \
         test_chmod +x file2 &&
         git commit -a -m "File rename, mode change and modification." &&
         gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # commitdiff testing (taken from t4114-apply-typechange.sh)
@@ -279,42 +244,34 @@ test_expect_success SYMLINKS 'setup typechange commits' '
 test_expect_success \
        'commitdiff(2): file renamed from foo to foo/baz' \
        'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-baz-renamed-from-foo"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(2): file renamed from foo/baz to foo' \
        'gitweb_run "p=.git;a=commitdiff;hp=foo-baz-renamed-from-foo;h=initial"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(2): directory becomes file' \
        'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=initial"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(2): file becomes directory' \
        'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-becomes-a-directory"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(2): file becomes symlink' \
        'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-symlinked-to-bar"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(2): symlink becomes file' \
        'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-back-to-file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(2): symlink becomes directory' \
        'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-becomes-a-directory"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(2): directory becomes symlink' \
        'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # commit, commitdiff: merge, large
@@ -330,12 +287,10 @@ test_expect_success \
 test_expect_success \
        'commit(0): merge commit' \
        'gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(0): merge commit' \
        'gitweb_run "p=.git;a=commitdiff"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'Prepare large commit' \
@@ -371,12 +326,10 @@ test_expect_success \
 test_expect_success \
        'commit(1): large commit' \
        'gitweb_run "p=.git;a=commit;h=b"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'commitdiff(1): large commit' \
        'gitweb_run "p=.git;a=commitdiff;h=b"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # tags testing
@@ -394,17 +347,14 @@ test_expect_success \
         git tag lightweight/tag-tree HEAD^{tree} &&
         git tag lightweight/tag-blob HEAD:file &&
         gitweb_run "p=.git;a=tags"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'tag: Tag to commit object' \
        'gitweb_run "p=.git;a=tag;h=tag-commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'tag: on lightweight tag (invalid)' \
        'gitweb_run "p=.git;a=tag;h=lightweight/tag-commit"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # logs
@@ -412,22 +362,18 @@ test_debug 'cat gitweb.log'
 test_expect_success \
        'logs: log (implicit HEAD)' \
        'gitweb_run "p=.git;a=log"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'logs: shortlog (implicit HEAD)' \
        'gitweb_run "p=.git;a=shortlog"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'logs: history (implicit HEAD, file)' \
        'gitweb_run "p=.git;a=history;f=file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'logs: history (implicit HEAD, non-existent file)' \
        'gitweb_run "p=.git;a=history;f=non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'logs: history (implicit HEAD, deleted file)' \
@@ -438,55 +384,45 @@ test_expect_success \
         git rm deleted_file &&
         git commit -m "Delete file" &&
         gitweb_run "p=.git;a=history;f=deleted_file"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # path_info links
 test_expect_success \
        'path_info: project' \
        'gitweb_run "" "/.git"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'path_info: project/branch' \
        'gitweb_run "" "/.git/b"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'path_info: project/branch:file' \
        'gitweb_run "" "/.git/master:file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'path_info: project/branch:dir/' \
        'gitweb_run "" "/.git/master:foo/"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'path_info: project/branch:file (non-existent)' \
        'gitweb_run "" "/.git/master:non-existent"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'path_info: project/branch:dir/ (non-existent)' \
        'gitweb_run "" "/.git/master:non-existent/"'
-test_debug 'cat gitweb.log'
 
 
 test_expect_success \
        'path_info: project/branch:/file' \
        'gitweb_run "" "/.git/master:/file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'path_info: project/:/file (implicit HEAD)' \
        'gitweb_run "" "/.git/:/file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'path_info: project/:/ (implicit HEAD, top tree)' \
        'gitweb_run "" "/.git/:/"'
-test_debug 'cat gitweb.log'
 
 
 # ----------------------------------------------------------------------
@@ -495,17 +431,14 @@ test_debug 'cat gitweb.log'
 test_expect_success \
        'feeds: OPML' \
        'gitweb_run "a=opml"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'feed: RSS' \
        'gitweb_run "p=.git;a=rss"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'feed: Atom' \
        'gitweb_run "p=.git;a=atom"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # encoding/decoding
@@ -513,27 +446,28 @@ test_debug 'cat gitweb.log'
 test_expect_success \
        'encode(commit): utf8' \
        '. "$TEST_DIRECTORY"/t3901-utf8.txt &&
+        test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" &&
+        test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
         echo "UTF-8" >> file &&
         git add file &&
         git commit -F "$TEST_DIRECTORY"/t3900/1-UTF-8.txt &&
         gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'encode(commit): iso-8859-1' \
        '. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+        test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" &&
+        test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
         echo "ISO-8859-1" >> file &&
         git add file &&
         git config i18n.commitencoding ISO-8859-1 &&
+        test_when_finished "git config --unset i18n.commitencoding" &&
         git commit -F "$TEST_DIRECTORY"/t3900/ISO8859-1.txt &&
-        git config --unset i18n.commitencoding &&
         gitweb_run "p=.git;a=commit"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'encode(log): utf-8 and iso-8859-1' \
        'gitweb_run "p=.git;a=log"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # extra options
@@ -541,27 +475,22 @@ test_debug 'cat gitweb.log'
 test_expect_success \
        'opt: log --no-merges' \
        'gitweb_run "p=.git;a=log;opt=--no-merges"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'opt: atom --no-merges' \
        'gitweb_run "p=.git;a=log;opt=--no-merges"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'opt: "file" history --no-merges' \
        'gitweb_run "p=.git;a=history;f=file;opt=--no-merges"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'opt: log --no-such-option (invalid option)' \
        'gitweb_run "p=.git;a=log;opt=--no-such-option"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'opt: tree --no-merges (invalid option for action)' \
        'gitweb_run "p=.git;a=tree;opt=--no-merges"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # testing config_to_multi / cloneurl
@@ -569,14 +498,12 @@ test_debug 'cat gitweb.log'
 test_expect_success \
        'URL: no project URLs, no base URL' \
        'gitweb_run "p=.git;a=summary"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'URL: project URLs via gitweb.url' \
        'git config --add gitweb.url git://example.com/git/trash.git &&
         git config --add gitweb.url http://example.com/git/trash.git &&
         gitweb_run "p=.git;a=summary"'
-test_debug 'cat gitweb.log'
 
 cat >.git/cloneurl <<\EOF
 git://example.com/git/trash.git
@@ -586,7 +513,6 @@ EOF
 test_expect_success \
        'URL: project URLs via cloneurl file' \
        'gitweb_run "p=.git;a=summary"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # gitweb config and repo config
@@ -604,12 +530,10 @@ EOF
 test_expect_success \
        'config override: projects list (implicit)' \
        'gitweb_run'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'config override: tree view, features not overridden in repo config' \
        'gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'config override: tree view, features disabled in repo config' \
@@ -617,14 +541,12 @@ test_expect_success \
         git config gitweb.snapshot none &&
         git config gitweb.avatar gravatar &&
         gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 test_expect_success \
        'config override: tree view, features enabled in repo config (1)' \
        'git config gitweb.blame yes &&
         git config gitweb.snapshot "zip,tgz, tbz2" &&
         gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 cat >.git/config <<\EOF
 # testing noval and alternate separator
@@ -635,7 +557,6 @@ EOF
 test_expect_success \
        'config override: tree view, features enabled in repo config (2)' \
        'gitweb_run "p=.git;a=tree"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # non-ASCII in README.html
@@ -645,7 +566,6 @@ test_expect_success \
        'echo "<b>UTF-8 example:</b><br />" > .git/README.html &&
         cat "$TEST_DIRECTORY"/t3900/1-UTF-8.txt >> .git/README.html &&
         gitweb_run "p=.git;a=summary"'
-test_debug 'cat gitweb.log'
 
 # ----------------------------------------------------------------------
 # syntax highlighting
@@ -666,7 +586,6 @@ test_expect_success HIGHLIGHT \
        'syntax highlighting (no highlight, unknown syntax)' \
        'git config gitweb.highlight yes &&
         gitweb_run "p=.git;a=blob;f=file"'
-test_debug 'cat gitweb.log'
 
 test_expect_success HIGHLIGHT \
        'syntax highlighting (highlighted, shell script)' \
@@ -675,6 +594,5 @@ test_expect_success HIGHLIGHT \
         git add test.sh &&
         git commit -m "Add test.sh" &&
         gitweb_run "p=.git;a=blob;f=test.sh"'
-test_debug 'cat gitweb.log'
 
 test_done
index 18825aff89f5699afaabc34df0fd7baab5863747..26102ee9b0c36a87ba17a75b0ca644cc42e2c1c4 100755 (executable)
@@ -126,7 +126,6 @@ test_expect_success 'load checking: load too high (default action)' '
        grep "Status: 503 Service Unavailable" gitweb.headers &&
        grep "503 - The load average on the server is too high" gitweb.body
 '
-test_debug 'cat gitweb.log' # just in case
 test_debug 'cat gitweb.headers'
 
 # turn off load checking
index c15ca2d6479a814d7649a38a671a554b6ed8b68f..13ba96e21a9295805aed40c89ecd5178db6bfae4 100755 (executable)
@@ -113,6 +113,16 @@ like($last_commit, qr/^[0-9a-fA-F]{40}$/, 'rev-parse returned hash');
 my $dir_commit = $r2->command_oneline('log', '-n1', '--pretty=format:%H', '.');
 isnt($last_commit, $dir_commit, 'log . does not show last commit');
 
+# commands outside working tree
+chdir($abs_repo_dir . '/..');
+my $r3 = Git->repository(Directory => $abs_repo_dir);
+my $tmpfile3 = "$abs_repo_dir/file3.tmp";
+open TEMPFILE3, "+>$tmpfile3" or die "Can't open $tmpfile3: $!";
+is($r3->cat_blob($file1hash, \*TEMPFILE3), 15, "cat_blob(outside): size");
+close TEMPFILE3;
+unlink $tmpfile3;
+chdir($abs_repo_dir);
+
 printf "1..%d\n", Test::More->builder->current_test;
 
 my $is_passing = eval { Test::More->is_passing };
diff --git a/t/t9800-git-p4.sh b/t/t9800-git-p4.sh
new file mode 100755 (executable)
index 0000000..1969e6b
--- /dev/null
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='git-p4 tests'
+
+. ./test-lib.sh
+
+( p4 -h && p4d -h ) >/dev/null 2>&1 || {
+       skip_all='skipping git-p4 tests; no p4 or p4d'
+       test_done
+}
+
+GITP4=$GIT_BUILD_DIR/contrib/fast-import/git-p4
+P4DPORT=10669
+
+db="$TRASH_DIRECTORY/db"
+cli="$TRASH_DIRECTORY/cli"
+git="$TRASH_DIRECTORY/git"
+
+test_debug 'echo p4d -q -d -r "$db" -p $P4DPORT'
+test_expect_success setup '
+       mkdir -p "$db" &&
+       p4d -q -d -r "$db" -p $P4DPORT &&
+       mkdir -p "$cli" &&
+       mkdir -p "$git" &&
+       export P4PORT=localhost:$P4DPORT
+'
+
+test_expect_success 'add p4 files' '
+       cd "$cli" &&
+       p4 client -i <<-EOF &&
+       Client: client
+       Description: client
+       Root: $cli
+       View: //depot/... //client/...
+       EOF
+       export P4CLIENT=client &&
+       echo file1 >file1 &&
+       p4 add file1 &&
+       p4 submit -d "file1" &&
+       cd "$TRASH_DIRECTORY"
+'
+
+test_expect_success 'basic git-p4 clone' '
+       "$GITP4" clone --dest="$git" //depot &&
+       rm -rf "$git" && mkdir "$git"
+'
+
+test_expect_success 'exit when p4 fails to produce marshaled output' '
+       badp4dir="$TRASH_DIRECTORY/badp4dir" &&
+       mkdir -p "$badp4dir" &&
+       cat >"$badp4dir"/p4 <<-EOF &&
+       #!$SHELL_PATH
+       exit 1
+       EOF
+       chmod 755 "$badp4dir"/p4 &&
+       PATH="$badp4dir:$PATH" "$GITP4" clone --dest="$git" //depot >errs 2>&1 ; retval=$? &&
+       test $retval -eq 1 &&
+       test_must_fail grep -q Traceback errs
+'
+
+test_expect_success 'add p4 files with wildcards in the names' '
+       cd "$cli" &&
+       echo file-wild-hash >file-wild#hash &&
+       echo file-wild-star >file-wild\*star &&
+       echo file-wild-at >file-wild@at &&
+       echo file-wild-percent >file-wild%percent &&
+       p4 add -f file-wild* &&
+       p4 submit -d "file wildcards" &&
+       cd "$TRASH_DIRECTORY"
+'
+
+test_expect_success 'wildcard files git-p4 clone' '
+       "$GITP4" clone --dest="$git" //depot &&
+       cd "$git" &&
+       test -f file-wild#hash &&
+       test -f file-wild\*star &&
+       test -f file-wild@at &&
+       test -f file-wild%percent &&
+       cd "$TRASH_DIRECTORY" &&
+       rm -rf "$git" && mkdir "$git"
+'
+
+test_expect_success 'clone bare' '
+       "$GITP4" clone --dest="$git" --bare //depot &&
+       cd "$git" &&
+       test ! -d .git &&
+       bare=`git config --get core.bare` &&
+       test "$bare" = true &&
+       cd "$TRASH_DIRECTORY" &&
+       rm -rf "$git" && mkdir "$git"
+'
+
+test_expect_success 'shutdown' '
+       pid=`pgrep -f p4d` &&
+       test -n "$pid" &&
+       test_debug "ps wl `echo $pid`" &&
+       kill $pid
+'
+
+test_done
diff --git a/tag.c b/tag.c
index f789744ccaf5d5fb6f63ba0911a869492affc162..7d38cc0f4de1c16b5b52725ba7a6a361650a6b41 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -56,7 +56,7 @@ static unsigned long parse_tag_date(const char *buf, const char *tail)
        return strtoul(dateptr, NULL, 10);
 }
 
-int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
+int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
 {
        unsigned char sha1[20];
        char type[20];
@@ -97,7 +97,9 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
                item->tagged = NULL;
        }
 
-       if (prefixcmp(bufptr, "tag "))
+       if (bufptr + 4 < tail && !prefixcmp(bufptr, "tag "))
+               ;               /* good */
+       else
                return -1;
        bufptr += 4;
        nl = memchr(bufptr, '\n', tail - bufptr);
@@ -106,7 +108,7 @@ int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
        item->tag = xmemdupz(bufptr, nl - bufptr);
        bufptr = nl + 1;
 
-       if (!prefixcmp(bufptr, "tagger "))
+       if (bufptr + 7 < tail && !prefixcmp(bufptr, "tagger "))
                item->date = parse_tag_date(bufptr, tail);
        else
                item->date = 0;
diff --git a/tag.h b/tag.h
index 85223700396f5ddb00c046a1a9fdb63c92aae40e..5ee88e6550cafa78b7e4acaa1285d5805974a037 100644 (file)
--- a/tag.h
+++ b/tag.h
@@ -13,7 +13,7 @@ struct tag {
 };
 
 extern struct tag *lookup_tag(const unsigned char *sha1);
-extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size);
+extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long size);
 extern int parse_tag(struct tag *item);
 extern struct object *deref_tag(struct object *, const char *, int);
 extern size_t parse_signature(const char *buf, unsigned long size);
index c11bf7f96708c90c992423fc27b2eba73d0c34a0..25b20b93fd4a1113629c99bd5a53dfee965119c1 100644 (file)
@@ -1,14 +1,9 @@
 /*
  * test-line-buffer.c: code to exercise the svn importer's input helper
- *
- * Input format:
- *     number NL
- *     (number bytes) NL
- *     number NL
- *     ...
  */
 
 #include "git-compat-util.h"
+#include "strbuf.h"
 #include "vcs-svn/line_buffer.h"
 
 static uint32_t strtouint32(const char *s)
@@ -20,27 +15,84 @@ static uint32_t strtouint32(const char *s)
        return (uint32_t) n;
 }
 
+static void handle_command(const char *command, const char *arg, struct line_buffer *buf)
+{
+       switch (*command) {
+       case 'b':
+               if (!prefixcmp(command, "binary ")) {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addch(&sb, '>');
+                       buffer_read_binary(buf, &sb, strtouint32(arg));
+                       fwrite(sb.buf, 1, sb.len, stdout);
+                       strbuf_release(&sb);
+                       return;
+               }
+       case 'c':
+               if (!prefixcmp(command, "copy ")) {
+                       buffer_copy_bytes(buf, strtouint32(arg));
+                       return;
+               }
+       case 'r':
+               if (!prefixcmp(command, "read ")) {
+                       const char *s = buffer_read_string(buf, strtouint32(arg));
+                       fputs(s, stdout);
+                       return;
+               }
+       case 's':
+               if (!prefixcmp(command, "skip ")) {
+                       buffer_skip_bytes(buf, strtouint32(arg));
+                       return;
+               }
+       default:
+               die("unrecognized command: %s", command);
+       }
+}
+
+static void handle_line(const char *line, struct line_buffer *stdin_buf)
+{
+       const char *arg = strchr(line, ' ');
+       if (!arg)
+               die("no argument in line: %s", line);
+       handle_command(line, arg + 1, stdin_buf);
+}
+
 int main(int argc, char *argv[])
 {
+       struct line_buffer stdin_buf = LINE_BUFFER_INIT;
+       struct line_buffer file_buf = LINE_BUFFER_INIT;
+       struct line_buffer *input = &stdin_buf;
+       const char *filename;
        char *s;
 
-       if (argc != 1)
-               usage("test-line-buffer < input.txt");
-       if (buffer_init(NULL))
+       if (argc == 1)
+               filename = NULL;
+       else if (argc == 2)
+               filename = argv[1];
+       else
+               usage("test-line-buffer [file | &fd] < script");
+
+       if (buffer_init(&stdin_buf, NULL))
                die_errno("open error");
-       while ((s = buffer_read_line())) {
-               s = buffer_read_string(strtouint32(s));
-               fputs(s, stdout);
-               fputc('\n', stdout);
-               buffer_skip_bytes(1);
-               if (!(s = buffer_read_line()))
-                       break;
-               buffer_copy_bytes(strtouint32(s) + 1);
+       if (filename) {
+               if (*filename == '&') {
+                       if (buffer_fdinit(&file_buf, strtouint32(filename + 1)))
+                               die_errno("error opening fd %s", filename + 1);
+               } else {
+                       if (buffer_init(&file_buf, filename))
+                               die_errno("error opening %s", filename);
+               }
+               input = &file_buf;
        }
-       if (buffer_deinit())
+
+       while ((s = buffer_read_line(&stdin_buf)))
+               handle_line(s, input);
+
+       if (filename && buffer_deinit(&file_buf))
+               die("error reading from %s", filename);
+       if (buffer_deinit(&stdin_buf))
                die("input error");
        if (ferror(stdout))
                die("output error");
-       buffer_reset();
+       buffer_reset(&stdin_buf);
        return 0;
 }
diff --git a/test-mktemp.c b/test-mktemp.c
new file mode 100644 (file)
index 0000000..c8c5421
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * test-mktemp.c: code to exercise the creation of temporary files
+ */
+#include "git-compat-util.h"
+
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               usage("Expected 1 parameter defining the temporary file template");
+
+       xmkstemp(xstrdup(argv[1]));
+
+       return 0;
+}
index 0828592162cc7c61f0d025ba4d83fc29bc4c2067..4e3710b9a8fc868642e1e0e8f4af37eeb1ce0ce4 100644 (file)
@@ -46,7 +46,7 @@ int main(int argc, const char **argv)
                OPT_DATE('t', NULL, &timestamp, "get timestamp of <time>"),
                OPT_CALLBACK('L', "length", &integer, "str",
                        "get length of <str>", length_callback),
-               OPT_FILENAME('F', "file", &file, "set file to <FILE>"),
+               OPT_FILENAME('F', "file", &file, "set file to <file>"),
                OPT_GROUP("String options"),
                OPT_STRING('s', "string", &string, "string", "get a string"),
                OPT_STRING(0, "string2", &string, "str", "get another string"),
index 77cf78abcfd18532b1b75e5ad5efa21b27f1a28c..b42ba789b176160285844e49cb834d60170d9a54 100644 (file)
@@ -9,7 +9,8 @@ int main(int argc, char *argv[])
 {
        if (argc != 2)
                usage("test-svn-fe <file>");
-       svndump_init(argv[1]);
+       if (svndump_init(argv[1]))
+               return 1;
        svndump_read(NULL);
        svndump_deinit();
        svndump_reset();
index 1ca41b1a6986559a7c5ddc3983d751d75a0d23ff..b68ec820dde935eb2578a8983c265290d0511add 100644 (file)
@@ -381,7 +381,7 @@ static void add_same_unmerged(struct cache_entry *ce,
 static int unpack_index_entry(struct cache_entry *ce,
                              struct unpack_trees_options *o)
 {
-       struct cache_entry *src[5] = { NULL };
+       struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
        int ret;
 
        src[0] = ce;
@@ -427,7 +427,10 @@ static int switch_cache_bottom(struct traverse_info *info)
        return ret;
 }
 
-static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
+static int traverse_trees_recursive(int n, unsigned long dirmask,
+                                   unsigned long df_conflicts,
+                                   struct name_entry *names,
+                                   struct traverse_info *info)
 {
        int i, ret, bottom;
        struct tree_desc t[MAX_UNPACK_TREES];
@@ -1105,6 +1108,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
 
                }
                if (o->result.cache_nr && empty_worktree) {
+                       /* dubious---why should this fail??? */
                        ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory");
                        goto done;
                }
@@ -1374,16 +1378,22 @@ static int verify_absent_1(struct cache_entry *ce,
                char path[PATH_MAX + 1];
                memcpy(path, ce->name, len);
                path[len] = 0;
-               lstat(path, &st);
+               if (lstat(path, &st))
+                       return error("cannot stat '%s': %s", path,
+                                       strerror(errno));
 
                return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st,
                                error_type, o);
-       } else if (!lstat(ce->name, &st))
+       } else if (lstat(ce->name, &st)) {
+               if (errno != ENOENT)
+                       return error("cannot stat '%s': %s", ce->name,
+                                    strerror(errno));
+               return 0;
+       } else {
                return check_ok_to_remove(ce->name, ce_namelen(ce),
-                               ce_to_dtype(ce), ce, &st,
-                               error_type, o);
-
-       return 0;
+                                         ce_to_dtype(ce), ce, &st,
+                                         error_type, o);
+       }
 }
 
 static int verify_absent(struct cache_entry *ce,
index 9ebf231ea5ee2e7fd1f71c9557a6537d29428d8d..1ff47977d549992ada8c1236187d4516b648ee7d 100644 (file)
@@ -8,9 +8,11 @@ static int ndrivers;
 static int drivers_alloc;
 
 #define PATTERNS(name, pattern, word_regex)                    \
-       { name, NULL, -1, { pattern, REG_EXTENDED }, word_regex }
+       { name, NULL, -1, { pattern, REG_EXTENDED },            \
+         word_regex "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" }
 #define IPATTERN(name, pattern, word_regex)                    \
-       { name, NULL, -1, { pattern, REG_EXTENDED | REG_ICASE }, word_regex }
+       { name, NULL, -1, { pattern, REG_EXTENDED | REG_ICASE }, \
+         word_regex "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" }
 static struct userdiff_driver builtin_drivers[] = {
 IPATTERN("fortran",
         "!^([C*]|[ \t]*!)\n"
@@ -24,10 +26,9 @@ IPATTERN("fortran",
          * Don't worry about format statements without leading digits since
          * they would have been matched above as a variable anyway. */
         "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?"
-        "|//|\\*\\*|::|[/<>=]="
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|//|\\*\\*|::|[/<>=]="),
 PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
-        "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
+        "[^<>= \t]+"),
 PATTERNS("java",
         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
         "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
@@ -35,8 +36,7 @@ PATTERNS("java",
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]="
-        "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
 PATTERNS("objc",
         /* Negate C statements that can look like functions */
         "!^[ \t]*(do|for|if|else|return|switch|while)\n"
@@ -49,8 +49,7 @@ PATTERNS("objc",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
-        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 PATTERNS("pascal",
         "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
                "implementation|initialization|finalization)[ \t]*.*)$"
@@ -59,8 +58,7 @@ PATTERNS("pascal",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
-        "|<>|<=|>=|:=|\\.\\."
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|<>|<=|>=|:=|\\.\\."),
 PATTERNS("perl",
         "^[ \t]*package .*;\n"
         "^[ \t]*sub .* \\{\n"
@@ -76,33 +74,29 @@ PATTERNS("perl",
         "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?"
         "|[-+*/%.^&<>=!|]="
         "|=~|!~"
-        "|<<|<>|<=>|>>"
-        "|[^[:space:]]"),
+        "|<<|<>|<=>|>>"),
 PATTERNS("php",
         "^[\t ]*(((public|protected|private|static)[\t ]+)*function.*)$\n"
         "^[\t ]*(class.*)$",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
-        "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"),
 PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
-        "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"),
         /* -- */
 PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
         /* -- */
         "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
-        "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"),
 PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
         "[={}\"]|[^={}\" \t]+"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
-        "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"),
+        "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
 PATTERNS("cpp",
         /* Jump targets or access declarations */
         "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
@@ -113,8 +107,7 @@ PATTERNS("cpp",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
-        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 PATTERNS("csharp",
         /* Keywords */
         "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
@@ -129,8 +122,7 @@ PATTERNS("csharp",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
-        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
-        "|[^[:space:]]|[\x80-\xff]+"),
+        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
index 6cfa256a3702332205faab546a405f6810cc16bd..260cf50e7725c9199fa44134a7f3408da7cad984 100644 (file)
@@ -63,14 +63,14 @@ void fast_export_commit(uint32_t revision, uint32_t author, char *log,
        printf("progress Imported commit %"PRIu32".\n\n", revision);
 }
 
-void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len)
+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len, struct line_buffer *input)
 {
        if (mode == REPO_MODE_LNK) {
                /* svn symlink blobs start with "link " */
-               buffer_skip_bytes(5);
+               buffer_skip_bytes(input, 5);
                len -= 5;
        }
        printf("blob\nmark :%"PRIu32"\ndata %"PRIu32"\n", mark, len);
-       buffer_copy_bytes(len);
+       buffer_copy_bytes(input, len);
        fputc('\n', stdout);
 }
index 2aaaea53d57bcc6a5280765dd39edf7fc5569c19..054e7d5eb1aeb4b2808acc0360e6371a376d40f7 100644 (file)
@@ -1,11 +1,14 @@
 #ifndef FAST_EXPORT_H_
 #define FAST_EXPORT_H_
 
+#include "line_buffer.h"
+
 void fast_export_delete(uint32_t depth, uint32_t *path);
 void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
                        uint32_t mark);
 void fast_export_commit(uint32_t revision, uint32_t author, char *log,
                        uint32_t uuid, uint32_t url, unsigned long timestamp);
-void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len);
+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len,
+                     struct line_buffer *input);
 
 #endif
index 1543567093fad603e09ba61b2fc47ec232b4cae2..aedf105b70586f2ef7567547426bb6622799b067 100644 (file)
@@ -5,47 +5,76 @@
 
 #include "git-compat-util.h"
 #include "line_buffer.h"
-#include "obj_pool.h"
+#include "strbuf.h"
 
-#define LINE_BUFFER_LEN 10000
 #define COPY_BUFFER_LEN 4096
 
-/* Create memory pool for char sequence of known length */
-obj_pool_gen(blob, char, 4096)
+int buffer_init(struct line_buffer *buf, const char *filename)
+{
+       buf->infile = filename ? fopen(filename, "r") : stdin;
+       if (!buf->infile)
+               return -1;
+       return 0;
+}
 
-static char line_buffer[LINE_BUFFER_LEN];
-static char byte_buffer[COPY_BUFFER_LEN];
-static FILE *infile;
+int buffer_fdinit(struct line_buffer *buf, int fd)
+{
+       buf->infile = fdopen(fd, "r");
+       if (!buf->infile)
+               return -1;
+       return 0;
+}
 
-int buffer_init(const char *filename)
+int buffer_tmpfile_init(struct line_buffer *buf)
 {
-       infile = filename ? fopen(filename, "r") : stdin;
-       if (!infile)
+       buf->infile = tmpfile();
+       if (!buf->infile)
                return -1;
        return 0;
 }
 
-int buffer_deinit(void)
+int buffer_deinit(struct line_buffer *buf)
 {
        int err;
-       if (infile == stdin)
-               return ferror(infile);
-       err = ferror(infile);
-       err |= fclose(infile);
+       if (buf->infile == stdin)
+               return ferror(buf->infile);
+       err = ferror(buf->infile);
+       err |= fclose(buf->infile);
        return err;
 }
 
+FILE *buffer_tmpfile_rewind(struct line_buffer *buf)
+{
+       rewind(buf->infile);
+       return buf->infile;
+}
+
+long buffer_tmpfile_prepare_to_read(struct line_buffer *buf)
+{
+       long pos = ftell(buf->infile);
+       if (pos < 0)
+               return error("ftell error: %s", strerror(errno));
+       if (fseek(buf->infile, 0, SEEK_SET))
+               return error("seek error: %s", strerror(errno));
+       return pos;
+}
+
+int buffer_read_char(struct line_buffer *buf)
+{
+       return fgetc(buf->infile);
+}
+
 /* Read a line without trailing newline. */
-char *buffer_read_line(void)
+char *buffer_read_line(struct line_buffer *buf)
 {
        char *end;
-       if (!fgets(line_buffer, sizeof(line_buffer), infile))
+       if (!fgets(buf->line_buffer, sizeof(buf->line_buffer), buf->infile))
                /* Error or data exhausted. */
                return NULL;
-       end = line_buffer + strlen(line_buffer);
+       end = buf->line_buffer + strlen(buf->line_buffer);
        if (end[-1] == '\n')
                end[-1] = '\0';
-       else if (feof(infile))
+       else if (feof(buf->infile))
                ; /* No newline at end of file.  That's fine. */
        else
                /*
@@ -54,44 +83,50 @@ char *buffer_read_line(void)
                 * but for now let's return an error.
                 */
                return NULL;
-       return line_buffer;
+       return buf->line_buffer;
+}
+
+char *buffer_read_string(struct line_buffer *buf, uint32_t len)
+{
+       strbuf_reset(&buf->blob_buffer);
+       strbuf_fread(&buf->blob_buffer, len, buf->infile);
+       return ferror(buf->infile) ? NULL : buf->blob_buffer.buf;
 }
 
-char *buffer_read_string(uint32_t len)
+void buffer_read_binary(struct line_buffer *buf,
+                               struct strbuf *sb, uint32_t size)
 {
-       char *s;
-       blob_free(blob_pool.size);
-       s = blob_pointer(blob_alloc(len + 1));
-       s[fread(s, 1, len, infile)] = '\0';
-       return ferror(infile) ? NULL : s;
+       strbuf_fread(sb, size, buf->infile);
 }
 
-void buffer_copy_bytes(uint32_t len)
+void buffer_copy_bytes(struct line_buffer *buf, uint32_t len)
 {
+       char byte_buffer[COPY_BUFFER_LEN];
        uint32_t in;
-       while (len > 0 && !feof(infile) && !ferror(infile)) {
+       while (len > 0 && !feof(buf->infile) && !ferror(buf->infile)) {
                in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
-               in = fread(byte_buffer, 1, in, infile);
+               in = fread(byte_buffer, 1, in, buf->infile);
                len -= in;
                fwrite(byte_buffer, 1, in, stdout);
                if (ferror(stdout)) {
-                       buffer_skip_bytes(len);
+                       buffer_skip_bytes(buf, len);
                        return;
                }
        }
 }
 
-void buffer_skip_bytes(uint32_t len)
+void buffer_skip_bytes(struct line_buffer *buf, uint32_t len)
 {
+       char byte_buffer[COPY_BUFFER_LEN];
        uint32_t in;
-       while (len > 0 && !feof(infile) && !ferror(infile)) {
+       while (len > 0 && !feof(buf->infile) && !ferror(buf->infile)) {
                in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
-               in = fread(byte_buffer, 1, in, infile);
+               in = fread(byte_buffer, 1, in, buf->infile);
                len -= in;
        }
 }
 
-void buffer_reset(void)
+void buffer_reset(struct line_buffer *buf)
 {
-       blob_reset();
+       strbuf_release(&buf->blob_buffer);
 }
index 9c78ae11a1e0774611b61a5407fab6cabfec226a..96ce966a229110e6775cd33ee01a93d5f133404e 100644 (file)
@@ -1,12 +1,31 @@
 #ifndef LINE_BUFFER_H_
 #define LINE_BUFFER_H_
 
-int buffer_init(const char *filename);
-int buffer_deinit(void);
-char *buffer_read_line(void);
-char *buffer_read_string(uint32_t len);
-void buffer_copy_bytes(uint32_t len);
-void buffer_skip_bytes(uint32_t len);
-void buffer_reset(void);
+#include "strbuf.h"
+
+#define LINE_BUFFER_LEN 10000
+
+struct line_buffer {
+       char line_buffer[LINE_BUFFER_LEN];
+       struct strbuf blob_buffer;
+       FILE *infile;
+};
+#define LINE_BUFFER_INIT {"", STRBUF_INIT, NULL}
+
+int buffer_init(struct line_buffer *buf, const char *filename);
+int buffer_fdinit(struct line_buffer *buf, int fd);
+int buffer_deinit(struct line_buffer *buf);
+void buffer_reset(struct line_buffer *buf);
+
+int buffer_tmpfile_init(struct line_buffer *buf);
+FILE *buffer_tmpfile_rewind(struct line_buffer *buf);  /* prepare to write. */
+long buffer_tmpfile_prepare_to_read(struct line_buffer *buf);
+
+char *buffer_read_line(struct line_buffer *buf);
+char *buffer_read_string(struct line_buffer *buf, uint32_t len);
+int buffer_read_char(struct line_buffer *buf);
+void buffer_read_binary(struct line_buffer *buf, struct strbuf *sb, uint32_t len);
+void buffer_copy_bytes(struct line_buffer *buf, uint32_t len);
+void buffer_skip_bytes(struct line_buffer *buf, uint32_t len);
 
 #endif
index 8906fb1f505e125ea58fd7a15c4f13bb0dc78f27..e89cc41d562448ad65a42b039dcf06a8c38e3335 100644 (file)
@@ -14,22 +14,46 @@ Calling sequence
 
 The calling program:
 
+ - initializes a `struct line_buffer` to LINE_BUFFER_INIT
  - specifies a file to read with `buffer_init`
  - processes input with `buffer_read_line`, `buffer_read_string`,
    `buffer_skip_bytes`, and `buffer_copy_bytes`
  - closes the file with `buffer_deinit`, perhaps to start over and
    read another file.
 
-Before exiting, the caller can use `buffer_reset` to deallocate
-resources for the benefit of profiling tools.
+When finished, the caller can use `buffer_reset` to deallocate
+resources.
+
+Using temporary files
+---------------------
+
+Temporary files provide a place to store data that should not outlive
+the calling program.  A program
+
+ - initializes a `struct line_buffer` to LINE_BUFFER_INIT
+ - requests a temporary file with `buffer_tmpfile_init`
+ - acquires an output handle by calling `buffer_tmpfile_rewind`
+ - uses standard I/O functions like `fprintf` and `fwrite` to fill
+   the temporary file
+ - declares writing is over with `buffer_tmpfile_prepare_to_read`
+ - can re-read what was written with `buffer_read_line`,
+   `buffer_read_string`, and so on
+ - can reuse the temporary file by calling `buffer_tmpfile_rewind`
+   again
+ - removes the temporary file with `buffer_deinit`, perhaps to
+   reuse the line_buffer for some other file.
+
+When finished, the calling program can use `buffer_reset` to deallocate
+resources.
 
 Functions
 ---------
 
-`buffer_init`::
-       Open the named file for input.  If filename is NULL,
-       start reading from stdin.  On failure, returns -1 (with
-       errno indicating the nature of the failure).
+`buffer_init`, `buffer_fdinit`::
+       Open the named file or file descriptor for input.
+       buffer_init(buf, NULL) prepares to read from stdin.
+       On failure, returns -1 (with errno indicating the nature
+       of the failure).
 
 `buffer_deinit`::
        Stop reading from the current file (closing it unless
index e3d1fa35444dd693ab8723a030b9fc6265cad8bc..491f0135a798f518fe2d226c9d01af90e6dac736 100644 (file)
@@ -175,25 +175,18 @@ void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark)
        repo_write_dirent(path, mode, blob_mark, 0);
 }
 
-uint32_t repo_replace(uint32_t *path, uint32_t blob_mark)
+uint32_t repo_modify_path(uint32_t *path, uint32_t mode, uint32_t blob_mark)
 {
-       uint32_t mode = 0;
        struct repo_dirent *src_dent;
        src_dent = repo_read_dirent(active_commit, path);
-       if (src_dent != NULL) {
-               mode = src_dent->mode;
-               repo_write_dirent(path, mode, blob_mark, 0);
-       }
-       return mode;
-}
-
-void repo_modify(uint32_t *path, uint32_t mode, uint32_t blob_mark)
-{
-       struct repo_dirent *src_dent;
-       src_dent = repo_read_dirent(active_commit, path);
-       if (src_dent != NULL && blob_mark == 0)
+       if (!src_dent)
+               return 0;
+       if (!blob_mark)
                blob_mark = src_dent->content_offset;
+       if (!mode)
+               mode = src_dent->mode;
        repo_write_dirent(path, mode, blob_mark, 0);
+       return mode;
 }
 
 void repo_delete(uint32_t *path)
index 5476175922740eaba663533a58deedfa981de659..68baeb582ff0ad8700061a78947cd186ff116536 100644 (file)
@@ -14,8 +14,7 @@
 uint32_t next_blob_mark(void);
 uint32_t repo_copy(uint32_t revision, uint32_t *src, uint32_t *dst);
 void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark);
-uint32_t repo_replace(uint32_t *path, uint32_t blob_mark);
-void repo_modify(uint32_t *path, uint32_t mode, uint32_t blob_mark);
+uint32_t repo_modify_path(uint32_t *path, uint32_t mode, uint32_t blob_mark);
 void repo_delete(uint32_t *path);
 void repo_commit(uint32_t revision, uint32_t author, char *log, uint32_t uuid,
                 uint32_t url, long unsigned timestamp);
index 2ad2c307dd6e8f4bddf5cbd9c588973a085bf870..ee7c0bb2ea869c66cdbab4f4f23a0d2207e6b27d 100644 (file)
@@ -30,7 +30,9 @@
 /* Create memory pool for log messages */
 obj_pool_gen(log, char, 4096)
 
-static char* log_copy(uint32_t length, char *log)
+static struct line_buffer input = LINE_BUFFER_INIT;
+
+static char *log_copy(uint32_t length, const char *log)
 {
        char *buffer;
        log_free(log_pool.size);
@@ -40,8 +42,9 @@ static char* log_copy(uint32_t length, char *log)
 }
 
 static struct {
-       uint32_t action, propLength, textLength, srcRev, srcMode, mark, type;
+       uint32_t action, propLength, textLength, srcRev, type;
        uint32_t src[REPO_MAX_PATH_DEPTH], dst[REPO_MAX_PATH_DEPTH];
+       uint32_t text_delta, prop_delta;
 } node_ctx;
 
 static struct {
@@ -58,7 +61,9 @@ static struct {
        uint32_t svn_log, svn_author, svn_date, svn_executable, svn_special, uuid,
                revision_number, node_path, node_kind, node_action,
                node_copyfrom_path, node_copyfrom_rev, text_content_length,
-               prop_content_length, content_length, svn_fs_dump_format_version;
+               prop_content_length, content_length, svn_fs_dump_format_version,
+               /* version 3 format */
+               text_delta, prop_delta;
 } keys;
 
 static void reset_node_ctx(char *fname)
@@ -69,9 +74,9 @@ static void reset_node_ctx(char *fname)
        node_ctx.textLength = LENGTH_UNKNOWN;
        node_ctx.src[0] = ~0;
        node_ctx.srcRev = 0;
-       node_ctx.srcMode = 0;
        pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.dst, "/", fname);
-       node_ctx.mark = 0;
+       node_ctx.text_delta = 0;
+       node_ctx.prop_delta = 0;
 }
 
 static void reset_rev_ctx(uint32_t revision)
@@ -107,81 +112,148 @@ static void init_keys(void)
        keys.prop_content_length = pool_intern("Prop-content-length");
        keys.content_length = pool_intern("Content-length");
        keys.svn_fs_dump_format_version = pool_intern("SVN-fs-dump-format-version");
+       /* version 3 format (Subversion 1.1.0) */
+       keys.text_delta = pool_intern("Text-delta");
+       keys.prop_delta = pool_intern("Prop-delta");
+}
+
+static void handle_property(uint32_t key, const char *val, uint32_t len,
+                               uint32_t *type_set)
+{
+       if (key == keys.svn_log) {
+               if (!val)
+                       die("invalid dump: unsets svn:log");
+               /* Value length excludes terminating nul. */
+               rev_ctx.log = log_copy(len + 1, val);
+       } else if (key == keys.svn_author) {
+               rev_ctx.author = pool_intern(val);
+       } else if (key == keys.svn_date) {
+               if (!val)
+                       die("invalid dump: unsets svn:date");
+               if (parse_date_basic(val, &rev_ctx.timestamp, NULL))
+                       warning("invalid timestamp: %s", val);
+       } else if (key == keys.svn_executable || key == keys.svn_special) {
+               if (*type_set) {
+                       if (!val)
+                               return;
+                       die("invalid dump: sets type twice");
+               }
+               if (!val) {
+                       node_ctx.type = REPO_MODE_BLB;
+                       return;
+               }
+               *type_set = 1;
+               node_ctx.type = key == keys.svn_executable ?
+                               REPO_MODE_EXE :
+                               REPO_MODE_LNK;
+       }
 }
 
 static void read_props(void)
 {
-       uint32_t len;
        uint32_t key = ~0;
-       char *val = NULL;
-       char *t;
-       while ((t = buffer_read_line()) && strcmp(t, "PROPS-END")) {
-               if (!strncmp(t, "K ", 2)) {
-                       len = atoi(&t[2]);
-                       key = pool_intern(buffer_read_string(len));
-                       buffer_read_line();
-               } else if (!strncmp(t, "V ", 2)) {
-                       len = atoi(&t[2]);
-                       val = buffer_read_string(len);
-                       if (key == keys.svn_log) {
-                               /* Value length excludes terminating nul. */
-                               rev_ctx.log = log_copy(len + 1, val);
-                       } else if (key == keys.svn_author) {
-                               rev_ctx.author = pool_intern(val);
-                       } else if (key == keys.svn_date) {
-                               if (parse_date_basic(val, &rev_ctx.timestamp, NULL))
-                                       fprintf(stderr, "Invalid timestamp: %s\n", val);
-                       } else if (key == keys.svn_executable) {
-                               node_ctx.type = REPO_MODE_EXE;
-                       } else if (key == keys.svn_special) {
-                               node_ctx.type = REPO_MODE_LNK;
-                       }
+       const char *t;
+       /*
+        * NEEDSWORK: to support simple mode changes like
+        *      K 11
+        *      svn:special
+        *      V 1
+        *      *
+        *      D 14
+        *      svn:executable
+        * we keep track of whether a mode has been set and reset to
+        * plain file only if not.  We should be keeping track of the
+        * symlink and executable bits separately instead.
+        */
+       uint32_t type_set = 0;
+       while ((t = buffer_read_line(&input)) && strcmp(t, "PROPS-END")) {
+               uint32_t len;
+               const char *val;
+               const char type = t[0];
+
+               if (!type || t[1] != ' ')
+                       die("invalid property line: %s\n", t);
+               len = atoi(&t[2]);
+               val = buffer_read_string(&input, len);
+               buffer_skip_bytes(&input, 1);   /* Discard trailing newline. */
+
+               switch (type) {
+               case 'K':
+                       key = pool_intern(val);
+                       continue;
+               case 'D':
+                       key = pool_intern(val);
+                       val = NULL;
+                       len = 0;
+                       /* fall through */
+               case 'V':
+                       handle_property(key, val, len, &type_set);
                        key = ~0;
-                       buffer_read_line();
+                       continue;
+               default:
+                       die("invalid property line: %s\n", t);
                }
        }
 }
 
 static void handle_node(void)
 {
-       if (node_ctx.propLength != LENGTH_UNKNOWN && node_ctx.propLength)
-               read_props();
-
-       if (node_ctx.srcRev)
-               node_ctx.srcMode = repo_copy(node_ctx.srcRev, node_ctx.src, node_ctx.dst);
-
-       if (node_ctx.textLength != LENGTH_UNKNOWN &&
-           node_ctx.type != REPO_MODE_DIR)
-               node_ctx.mark = next_blob_mark();
+       uint32_t mark = 0;
+       const uint32_t type = node_ctx.type;
+       const int have_props = node_ctx.propLength != LENGTH_UNKNOWN;
 
+       if (node_ctx.text_delta)
+               die("text deltas not supported");
+       if (node_ctx.textLength != LENGTH_UNKNOWN)
+               mark = next_blob_mark();
        if (node_ctx.action == NODEACT_DELETE) {
+               if (mark || have_props || node_ctx.srcRev)
+                       die("invalid dump: deletion node has "
+                               "copyfrom info, text, or properties");
+               return repo_delete(node_ctx.dst);
+       }
+       if (node_ctx.action == NODEACT_REPLACE) {
                repo_delete(node_ctx.dst);
-       } else if (node_ctx.action == NODEACT_CHANGE ||
-                          node_ctx.action == NODEACT_REPLACE) {
-               if (node_ctx.action == NODEACT_REPLACE &&
-                   node_ctx.type == REPO_MODE_DIR)
-                       repo_replace(node_ctx.dst, node_ctx.mark);
-               else if (node_ctx.propLength != LENGTH_UNKNOWN)
-                       repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark);
-               else if (node_ctx.textLength != LENGTH_UNKNOWN)
-                       node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);
+               node_ctx.action = NODEACT_ADD;
+       }
+       if (node_ctx.srcRev) {
+               repo_copy(node_ctx.srcRev, node_ctx.src, node_ctx.dst);
+               if (node_ctx.action == NODEACT_ADD)
+                       node_ctx.action = NODEACT_CHANGE;
+       }
+       if (mark && type == REPO_MODE_DIR)
+               die("invalid dump: directories cannot have text attached");
+       if (node_ctx.action == NODEACT_CHANGE && !~*node_ctx.dst) {
+               if (type != REPO_MODE_DIR)
+                       die("invalid dump: root of tree is not a regular file");
+       } else if (node_ctx.action == NODEACT_CHANGE) {
+               uint32_t mode = repo_modify_path(node_ctx.dst, 0, mark);
+               if (!mode)
+                       die("invalid dump: path to be modified is missing");
+               if (mode == REPO_MODE_DIR && type != REPO_MODE_DIR)
+                       die("invalid dump: cannot modify a directory into a file");
+               if (mode != REPO_MODE_DIR && type == REPO_MODE_DIR)
+                       die("invalid dump: cannot modify a file into a directory");
+               node_ctx.type = mode;
        } else if (node_ctx.action == NODEACT_ADD) {
-               if (node_ctx.srcRev && node_ctx.propLength != LENGTH_UNKNOWN)
-                       repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark);
-               else if (node_ctx.srcRev && node_ctx.textLength != LENGTH_UNKNOWN)
-                       node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);
-               else if ((node_ctx.type == REPO_MODE_DIR && !node_ctx.srcRev) ||
-                        node_ctx.textLength != LENGTH_UNKNOWN)
-                       repo_add(node_ctx.dst, node_ctx.type, node_ctx.mark);
+               if (!mark && type != REPO_MODE_DIR)
+                       die("invalid dump: adds node without text");
+               repo_add(node_ctx.dst, type, mark);
+       } else {
+               die("invalid dump: Node-path block lacks Node-action");
        }
-
-       if (node_ctx.propLength == LENGTH_UNKNOWN && node_ctx.srcMode)
-               node_ctx.type = node_ctx.srcMode;
-
-       if (node_ctx.mark)
-               fast_export_blob(node_ctx.type, node_ctx.mark, node_ctx.textLength);
-       else if (node_ctx.textLength != LENGTH_UNKNOWN)
-               buffer_skip_bytes(node_ctx.textLength);
+       if (have_props) {
+               const uint32_t old_mode = node_ctx.type;
+               if (!node_ctx.prop_delta)
+                       node_ctx.type = type;
+               if (node_ctx.propLength)
+                       read_props();
+               if (node_ctx.type != old_mode)
+                       repo_modify_path(node_ctx.dst, node_ctx.type, mark);
+       }
+       if (mark)
+               fast_export_blob(node_ctx.type, mark,
+                                node_ctx.textLength, &input);
 }
 
 static void handle_revision(void)
@@ -200,7 +272,7 @@ void svndump_read(const char *url)
        uint32_t key;
 
        reset_dump_ctx(pool_intern(url));
-       while ((t = buffer_read_line())) {
+       while ((t = buffer_read_line(&input))) {
                val = strstr(t, ": ");
                if (!val)
                        continue;
@@ -210,8 +282,8 @@ void svndump_read(const char *url)
 
                if (key == keys.svn_fs_dump_format_version) {
                        dump_ctx.version = atoi(val);
-                       if (dump_ctx.version > 2)
-                               die("expected svn dump format version <= 2, found %"PRIu32,
+                       if (dump_ctx.version > 3)
+                               die("expected svn dump format version <= 3, found %"PRIu32,
                                    dump_ctx.version);
                } else if (key == keys.uuid) {
                        dump_ctx.uuid = pool_intern(val);
@@ -255,9 +327,13 @@ void svndump_read(const char *url)
                        node_ctx.textLength = atoi(val);
                } else if (key == keys.prop_content_length) {
                        node_ctx.propLength = atoi(val);
+               } else if (key == keys.text_delta) {
+                       node_ctx.text_delta = !strcmp(val, "true");
+               } else if (key == keys.prop_delta) {
+                       node_ctx.prop_delta = !strcmp(val, "true");
                } else if (key == keys.content_length) {
                        len = atoi(val);
-                       buffer_read_line();
+                       buffer_read_line(&input);
                        if (active_ctx == REV_CTX) {
                                read_props();
                        } else if (active_ctx == NODE_CTX) {
@@ -265,7 +341,7 @@ void svndump_read(const char *url)
                                active_ctx = REV_CTX;
                        } else {
                                fprintf(stderr, "Unexpected content length header: %"PRIu32"\n", len);
-                               buffer_skip_bytes(len);
+                               buffer_skip_bytes(&input, len);
                        }
                }
        }
@@ -275,14 +351,16 @@ void svndump_read(const char *url)
                handle_revision();
 }
 
-void svndump_init(const char *filename)
+int svndump_init(const char *filename)
 {
-       buffer_init(filename);
+       if (buffer_init(&input, filename))
+               return error("cannot open %s: %s", filename, strerror(errno));
        repo_init();
        reset_dump_ctx(~0);
        reset_rev_ctx(0);
        reset_node_ctx(NULL);
        init_keys();
+       return 0;
 }
 
 void svndump_deinit(void)
@@ -292,7 +370,7 @@ void svndump_deinit(void)
        reset_dump_ctx(~0);
        reset_rev_ctx(0);
        reset_node_ctx(NULL);
-       if (buffer_deinit())
+       if (buffer_deinit(&input))
                fprintf(stderr, "Input error\n");
        if (ferror(stdout))
                fprintf(stderr, "Output error\n");
@@ -301,7 +379,7 @@ void svndump_deinit(void)
 void svndump_reset(void)
 {
        log_reset();
-       buffer_reset();
+       buffer_reset(&input);
        repo_reset();
        reset_dump_ctx(~0);
        reset_rev_ctx(0);
index 93c412f14a4ca48b9de18325c09d69f051d97647..df9ceb0e8d473b65fc22ac142d3856dc4f9980e0 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef SVNDUMP_H_
 #define SVNDUMP_H_
 
-void svndump_init(const char *filename);
+int svndump_init(const char *filename);
 void svndump_read(const char *url);
 void svndump_deinit(void);
 void svndump_reset(void);
index 8d7dd31c4ba5439652d11e5ada06e0d52bc04e4f..4c147d6c48c000bab636fad3edc2fe7da6670948 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -53,7 +53,7 @@ void *xmalloc(size_t size)
 void *xmallocz(size_t size)
 {
        void *ret;
-       if (size + 1 < size)
+       if (unsigned_add_overflows(size, 1))
                die("Data too large to fit into virtual memory space.");
        ret = xmalloc(size + 1);
        ((char*)ret)[size] = 0;
@@ -198,10 +198,22 @@ FILE *xfdopen(int fd, const char *mode)
 int xmkstemp(char *template)
 {
        int fd;
+       char origtemplate[PATH_MAX];
+       strlcpy(origtemplate, template, sizeof(origtemplate));
 
        fd = mkstemp(template);
-       if (fd < 0)
-               die_errno("Unable to create temporary file");
+       if (fd < 0) {
+               int saved_errno = errno;
+               const char *nonrelative_template;
+
+               if (!template[0])
+                       template = origtemplate;
+
+               nonrelative_template = make_nonrelative_path(template);
+               errno = saved_errno;
+               die_errno("Unable to create temporary file '%s'",
+                       nonrelative_template);
+       }
        return fd;
 }
 
@@ -321,10 +333,22 @@ int gitmkstemps(char *pattern, int suffix_len)
 int xmkstemp_mode(char *template, int mode)
 {
        int fd;
+       char origtemplate[PATH_MAX];
+       strlcpy(origtemplate, template, sizeof(origtemplate));
 
        fd = git_mkstemp_mode(template, mode);
-       if (fd < 0)
-               die_errno("Unable to create temporary file");
+       if (fd < 0) {
+               int saved_errno = errno;
+               const char *nonrelative_template;
+
+               if (!template[0])
+                       template = origtemplate;
+
+               nonrelative_template = make_nonrelative_path(template);
+               errno = saved_errno;
+               die_errno("Unable to create temporary file '%s'",
+                       nonrelative_template);
+       }
        return fd;
 }