Code

Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Mon, 15 Nov 2010 19:00:24 +0000 (11:00 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 15 Nov 2010 19:00:24 +0000 (11:00 -0800)
* maint:
  rebase -X: do not clobber strategy

115 files changed:
.mailmap
Documentation/RelNotes/1.7.4.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-archimport.txt
Documentation/git-cvsexportcommit.txt
Documentation/git-cvsserver.txt
Documentation/git-daemon.txt
Documentation/git-fast-import.txt
Documentation/git-fmt-merge-msg.txt
Documentation/git-format-patch.txt
Documentation/git-read-tree.txt
Documentation/git-repack.txt
Documentation/git-reset.txt
Documentation/git-send-email.txt
Documentation/git-shell.txt
Documentation/gitattributes.txt
Documentation/gitdiffcore.txt
Documentation/merge-config.txt
Documentation/merge-options.txt
Documentation/merge-strategies.txt
Documentation/technical/api-merge.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
abspath.c
attr.c
builtin.h
builtin/checkout.c
builtin/commit-tree.c
builtin/fmt-merge-msg.c
builtin/log.c
builtin/merge-recursive.c
builtin/merge.c
builtin/read-tree.c
builtin/revert.c
compat/mingw.c
compat/mingw.h
configure.ac
contrib/ciabot/ciabot.py
contrib/completion/git-completion.bash
contrib/examples/git-svnimport.perl
contrib/fast-import/git-p4
contrib/fast-import/import-directories.perl
contrib/git-shell-commands/README [new file with mode: 0644]
contrib/git-shell-commands/help [new file with mode: 0755]
contrib/git-shell-commands/list [new file with mode: 0755]
contrib/hooks/post-receive-email
daemon.c
diff.c
diff.h
diffcore-pickaxe.c
diffcore.h
fast-import.c
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-difftool.perl
git-gui/git-gui.sh
git-gui/lib/branch_rename.tcl
git-gui/lib/diff.tcl
git-merge-octopus.sh
git-mergetool--lib.sh
git-relink.perl
git-repack.sh
git-send-email.perl
git-sh-setup.sh
git-svn.perl
gitweb/Makefile
gitweb/README
gitweb/gitweb.perl
grep.c
grep.h
ll-merge.c
ll-merge.h
merge-file.c
merge-recursive.c
merge-recursive.h
perl/Git.pm
rerere.c
shell.c
strbuf.c
t/gitweb-lib.sh
t/t0003-attributes.sh
t/t1300-repo-config.sh
t/t3032-merge-recursive-options.sh [new file with mode: 0755]
t/t3506-cherry-pick-ff.sh
t/t4013-diff-various.sh
t/t4013/diff.log_-GF_-p_--pickaxe-all_master [new file with mode: 0644]
t/t4013/diff.log_-GF_-p_master [new file with mode: 0644]
t/t4013/diff.log_-GF_master [new file with mode: 0644]
t/t4014-format-patch.sh
t/t4018-diff-funcname.sh
t/t4041-diff-submodule-option.sh
t/t5000-tar-tree.sh
t/t5503-tagfollow.sh
t/t5560-http-backend-noserver.sh
t/t6038-merge-text-auto.sh
t/t6200-fmt-merge-msg.sh
t/t7004-tag.sh
t/t7006/test-terminal.perl
t/t7401-submodule-summary.sh
t/t7810-grep.sh
t/t9001-send-email.sh
t/t9157-git-svn-fetch-merge.sh [changed mode: 0644->0755]
t/t9300-fast-import.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9700/test.pl
t/test-lib.sh
tree-diff.c
userdiff.c

index a8091eb5dfa430bf1b0537da47a31e7cf88d8622..19c87262322cf5acc9d4fd5fa06f96eb3e988049 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -36,7 +36,7 @@ Lars Doelle <lars.doelle@on-line ! de>
 Lars Doelle <lars.doelle@on-line.de>
 Li Hong <leehong@pku.edu.cn>
 Lukas Sandström <lukass@etek.chalmers.se>
-Martin Langhoff <martin@catalyst.net.nz>
+Martin Langhoff <martin@laptop.org>
 Michael Coleman <tutufan@gmail.com>
 Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
 Michael W. Olson <mwolson@gnu.org>
diff --git a/Documentation/RelNotes/1.7.4.txt b/Documentation/RelNotes/1.7.4.txt
new file mode 100644 (file)
index 0000000..05e8a43
--- /dev/null
@@ -0,0 +1,54 @@
+Git v1.7.4 Release Notes (draft)
+================================
+
+Updates since v1.7.3
+--------------------
+
+ * The option parsers of various commands that create new branch (or
+   rename existing ones to a new name) were too loose and users were
+   allowed to call a branch with a name that begins with a dash by
+   creative abuse of their command line options, which only lead to
+   burn themselves.  The name of a branch cannot begin with a dash
+   now.
+
+ * System-wide fallback default attributes can be stored in
+   /etc/gitattributes; core.attributesfile configuration variable can
+   be used to customize the path to this file.
+
+ * "git diff" and "git grep" learned how functions and subroutines
+   in Fortran look like.
+
+ * "git log -G<pattern>" limits the output to commits whose change has
+   added or deleted lines that match the given pattern.
+
+ * "git read-tree" with no argument as a way to empty the index is
+   deprecated; we might want to remove it in the future.  Users can
+   use the new --empty option to be more explicit instead.
+
+ * "git merge --log" used to limit the resulting merge log to 20
+   entries; this is now customizable by giving e.g. "--log=47".
+
+ * you can extend "git shell", which is often used on boxes that allow
+   git-only login over ssh as login shell, with custom set of
+   commands.
+
+Also contains various documentation updates.
+
+
+Fixes since v1.7.3
+------------------
+
+All of the fixes in v1.7.3.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git log --author=me --author=her" did not find commits written by
+   me or by her; instead it looked for commits written by me and by
+   her, which is impossible.
+
+
+---
+exec >/var/tmp/1
+O=v1.7.3
+O=v1.7.3.1-42-g34289ec
+echo O=$(git describe master)
+git shortlog --no-merges ^maint ^$O master
index 7f6b2109bd83a55b7dddd8a8bc36c6fc1006a134..538ebb5e2ecc58f1ddcb7adb44ded3b027555ec2 100644 (file)
@@ -459,6 +459,12 @@ core.askpass::
        prompt. The external program shall be given a suitable prompt as
        command line argument and write the password on its STDOUT.
 
+core.attributesfile::
+       In addition to '.gitattributes' (per-directory) and
+       '.git/info/attributes', git looks into this file for attributes
+       (see linkgit:gitattributes[5]). Path expansions are made the same
+       way as for `core.excludesfile`.
+
 core.editor::
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
@@ -1466,6 +1472,10 @@ pack.compression::
        not set,  defaults to -1, the zlib default, which is "a default
        compromise between speed and compression (currently equivalent
        to level 6)."
++
+Note that changing the compression level will not automatically recompress
+all existing objects. You can force recompression by passing the -F option
+to linkgit:git-repack[1].
 
 pack.deltaCacheSize::
        The maximum memory in bytes used for caching deltas in
@@ -1727,6 +1737,7 @@ sendemail.to::
 sendemail.smtpdomain::
 sendemail.smtpserver::
 sendemail.smtpserverport::
+sendemail.smtpserveroption::
 sendemail.smtpuser::
 sendemail.thread::
 sendemail.validate::
index d723e992321490e64041caa5021e82bb75eefa02..bfd0b571e23a6b6ee21d9616466acd82795eed6c 100644 (file)
@@ -207,6 +207,7 @@ endif::git-format-patch[]
        digits can be specified with `--abbrev=<n>`.
 
 -B[<n>][/<m>]::
+--break-rewrites[=[<n>][/<m>]]::
        Break complete rewrite changes into pairs of delete and
        create. This serves two purposes:
 +
@@ -229,6 +230,7 @@ eligible for being picked up as a possible source of a rename to
 another file.
 
 -M[<n>]::
+--detect-renames[=<n>]::
 ifndef::git-log[]
        Detect renames.
 endif::git-log[]
@@ -244,6 +246,7 @@ endif::git-log[]
        hasn't changed.
 
 -C[<n>]::
+--detect-copies[=<n>]::
        Detect copies as well as renames.  See also `--find-copies-harder`.
        If `n` is specified, it has the same meaning as for `-M<n>`.
 
@@ -284,8 +287,12 @@ ifndef::git-format-patch[]
        appearing in diff output; see the 'pickaxe' entry in
        linkgit:gitdiffcore[7] for more details.
 
+-G<regex>::
+       Look for differences whose added or removed line matches
+       the given <regex>.
+
 --pickaxe-all::
-       When `-S` finds a change, show all the changes in that
+       When `-S` or `-G` finds a change, show all the changes in that
        changeset, not just the files that contain the change
        in <string>.
 
index 4f358c8d6c3320c5ef0a78d6c4d155535433f3d0..2411ce5bfe05a14ed45c1546c3d1a6d92b39d8eb 100644 (file)
@@ -109,7 +109,7 @@ OPTIONS
 
 Author
 ------
-Written by Martin Langhoff <martin@catalyst.net.nz>.
+Written by Martin Langhoff <martin@laptop.org>.
 
 Documentation
 --------------
index b2696efae95b20b693251a9d9785405218285084..d25661eb215c8bd2002933a48dd6ad5ea360459a 100644 (file)
@@ -114,11 +114,11 @@ $ git cherry cvshead myhead | sed -n 's/^+ //p' | xargs -l1 git cvsexportcommit
 
 Author
 ------
-Written by Martin Langhoff <martin@catalyst.net.nz> and others.
+Written by Martin Langhoff <martin@laptop.org> and others.
 
 Documentation
 --------------
-Documentation by Martin Langhoff <martin@catalyst.net.nz> and others.
+Documentation by Martin Langhoff <martin@laptop.org> and others.
 
 GIT
 ---
index f4472c61dbb297d61053ef2e8503ad7c56d06148..70cbb2cae7b60a5c6514d0990da91ed5b23e0827 100644 (file)
@@ -399,13 +399,13 @@ This program is copyright The Open University UK - 2006.
 Authors:
 
 - Martyn Smith    <martyn@catalyst.net.nz>
-- Martin Langhoff <martin@catalyst.net.nz>
+- Martin Langhoff <martin@laptop.org>
 
 with ideas and patches from participants of the git-list <git@vger.kernel.org>.
 
 Documentation
 --------------
-Documentation by Martyn Smith <martyn@catalyst.net.nz>, Martin Langhoff <martin@catalyst.net.nz>, and Matthias Urlichs <smurf@smurf.noris.de>.
+Documentation by Martyn Smith <martyn@catalyst.net.nz>, Martin Langhoff <martin@laptop.org>, and Matthias Urlichs <smurf@smurf.noris.de>.
 
 GIT
 ---
index 2f0ddf6fe817ab7b59fb5f5276ba688447661ee3..5054f790a140b43640d1e4d68a032d95da3daaca 100644 (file)
@@ -85,6 +85,7 @@ OPTIONS
        be either an IPv4 address or an IPv6 address if supported.  If IPv6
        is not supported, then --listen=hostname is also not supported and
        --listen must be given an IPv4 address.
+       Can be given more than once.
        Incompatible with '--inetd' option.
 
 --port=<n>::
index 2c6ad5b2f32e6c0248f0a30423b07d01fed3baa3..5d0c245e38ff751d8c40a6eb6d5b17b80930d526 100644 (file)
@@ -524,6 +524,9 @@ start with double quote (`"`).
 If an `LF` or double quote must be encoded into `<path>` shell-style
 quoting should be used, e.g. `"path/with\n and \" in it"`.
 
+Additionally, in `040000` mode, `<path>` may also be an empty string
+(`""`) to specify the root of the tree.
+
 The value of `<path>` must be in canonical form. That is it must not:
 
 * contain an empty directory component (e.g. `foo//bar` is invalid),
index 302f56b88941cfaa002a8b75c078335bf8d86276..40dba8c0a9f308064f67d8f38ce8a22b2a42a77c 100644 (file)
@@ -9,8 +9,8 @@ git-fmt-merge-msg - Produce a merge commit message
 SYNOPSIS
 --------
 [verse]
-'git fmt-merge-msg' [-m <message>] [--log | --no-log] <$GIT_DIR/FETCH_HEAD
-'git fmt-merge-msg' [-m <message>] [--log | --no-log] -F <file>
+'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] <$GIT_DIR/FETCH_HEAD
+'git fmt-merge-msg' [-m <message>] [--log[=<n>] | --no-log] -F <file>
 
 DESCRIPTION
 -----------
@@ -24,10 +24,12 @@ automatically invoking 'git merge'.
 OPTIONS
 -------
 
---log::
+--log[=<n>]::
        In addition to branch names, populate the log message with
        one-line descriptions from the actual commits that are being
-       merged.
+       merged.  At most <n> commits from each merge parent will be
+       used (20 if <n> is omitted).  This overrides the `merge.log`
+       configuration variable.
 
 --no-log::
        Do not list one-line descriptions from the actual commits being
@@ -52,8 +54,10 @@ CONFIGURATION
 -------------
 
 merge.log::
-       Whether to include summaries of merged commits in newly
-       merge commit messages. False by default.
+       In addition to branch names, populate the log message with at
+       most the specified number of one-line descriptions from the
+       actual commits that are being merged.  Defaults to false, and
+       true is a synoym for 20.
 
 merge.summary::
        Synonym to `merge.log`; this is deprecated and will be removed in
index a00b783fe5dd7a074fc70f9aed85e6e45189ac8f..9dcafc6d4442b6db66df642e92336537395b9919 100644 (file)
@@ -74,7 +74,7 @@ OPTIONS
 include::diff-options.txt[]
 
 -<n>::
-       Limits the number of patches to prepare.
+       Prepare patches from the topmost <n> commits.
 
 -o <dir>::
 --output-directory <dir>::
index 2e78da448f30ad260cb7683a5610f424d0c5ffea..e88e9c2d55d19844ca56bc2d5b7b683e329b6fb8 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
                [-u [--exclude-per-directory=<gitignore>] | -i]]
                [--index-output=<file>] [--no-sparse-checkout]
-               <tree-ish1> [<tree-ish2> [<tree-ish3>]]
+               (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])
 
 
 DESCRIPTION
@@ -114,6 +114,10 @@ OPTIONS
        Disable sparse checkout support even if `core.sparseCheckout`
        is true.
 
+--empty::
+       Instead of reading tree object(s) into the index, just empty
+       it.
+
 <tree-ish#>::
        The id of the tree object(s) to be read/merged.
 
index af79b86516764617163b020ec3c4d9bbeda9fe9b..27f7865b061cc96398f7aa052d162723e1f9255e 100644 (file)
@@ -8,7 +8,7 @@ git-repack - Pack unpacked objects in a repository
 
 SYNOPSIS
 --------
-'git repack' [-a] [-A] [-d] [-f] [-l] [-n] [-q] [--window=<n>] [--depth=<n>]
+'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [--window=<n>] [--depth=<n>]
 
 DESCRIPTION
 -----------
@@ -62,6 +62,10 @@ other objects in that pack they already have locally.
        linkgit:git-pack-objects[1].
 
 -f::
+       Pass the `--no-reuse-delta` option to `git-pack-objects`, see
+       linkgit:git-pack-objects[1].
+
+-F::
        Pass the `--no-reuse-object` option to `git-pack-objects`, see
        linkgit:git-pack-objects[1].
 
index 9cf31485feec4b03850edb1f2f3a4b765245f4cc..fd72976371cee0fcd25e3419138eb13382dc6ab8 100644 (file)
@@ -15,17 +15,24 @@ SYNOPSIS
 DESCRIPTION
 -----------
 In the first and second form, copy entries from <commit> to the index.
-In the third form, set the current branch to <commit>, optionally
-modifying index and worktree to match.  The <commit> defaults to HEAD
+In the third form, set the current branch head (HEAD) to <commit>, optionally
+modifying index and working tree to match.  The <commit> defaults to HEAD
 in all forms.
 
 'git reset' [-q] [<commit>] [--] <paths>...::
        This form resets the index entries for all <paths> to their
-       state at the <commit>.  (It does not affect the worktree, nor
+       state at <commit>.  (It does not affect the working tree, nor
        the current branch.)
 +
 This means that `git reset <paths>` is the opposite of `git add
 <paths>`.
++
+After running `git reset <paths>` to update the index entry, you can
+use linkgit:git-checkout[1] to check the contents out of the index to
+the working tree.
+Alternatively, using linkgit:git-checkout[1] and specifying a commit, you
+can copy the contents of a path out of a commit to the index and to the
+working tree in one go.
 
 'git reset' --patch|-p [<commit>] [--] [<paths>...]::
        Interactively select hunks in the difference between the index
@@ -36,16 +43,17 @@ This means that `git reset -p` is the opposite of `git add -p` (see
 linkgit:git-add[1]).
 
 'git reset' [--<mode>] [<commit>]::
-       This form points the current branch to <commit> and then
-       updates index and working tree according to <mode>, which must
-       be one of the following:
+       This form resets the current branch head to <commit> and
+       possibly updates the index (resetting it to the tree of <commit>) and
+       the working tree depending on <mode>, which
+       must be one of the following:
 +
 --
 --soft::
-       Does not touch the index file nor the working tree at allbut
-       requires them to be in a good order. This leaves all your changed
-       files "Changes to be committed", as 'git status' would
-       put it.
+       Does not touch the index file nor the working tree at all (but
+       resets the head to <commit>, just like all modes do). This leaves
+       all your changed files "Changes to be committed", as 'git status'
+       would put it.
 
 --mixed::
        Resets the index but not the working tree (i.e., the changed files
@@ -53,22 +61,30 @@ linkgit:git-add[1]).
        been updated. This is the default action.
 
 --hard::
-       Matches the working tree and index to that of the tree being
-       switched to. Any changes to tracked files in the working tree
-       since <commit> are lost.
+       Resets the index and working tree. Any changes to tracked files in the
+       working tree since <commit> are discarded.
 
 --merge::
-       Resets the index to match the tree recorded by the named commit,
-       and updates the files that are different between the named commit
-       and the current commit in the working tree.
+       Resets the index and updates the files in the working tree that are
+       different between <commit> and HEAD, but keeps those which are
+       different between the index and working tree (i.e. which have changes
+       which have not been added).
+       If a file that is different between <commit> and the index has unstaged
+       changes, reset is aborted.
++
+In other words, --merge does something like a 'git read-tree -u -m <commit>',
+but carries forward unmerged index entries.
 
 --keep::
-       Reset the index to the given commit, keeping local changes in
-       the working tree since the current commit, while updating
-       working tree files without local changes to what appears in
-       the given commit.  If a file that is different between the
-       current commit and the given commit has local changes, reset
-       is aborted.
+       Resets the index, updates files in the working tree that are
+       different between <commit> and HEAD, but keeps those
+       which are different between HEAD and the working tree (i.e.
+       which have local changes).
+       If a file that is different between <commit> and HEAD has local changes,
+       reset is aborted.
++
+In other words, --keep does a 2-way merge between <commit> and HEAD followed by
+'git reset --mixed <commit>'.
 --
 
 If you want to undo a commit other than the latest on a branch,
@@ -184,7 +200,7 @@ tip of the current branch in ORIG_HEAD, so resetting hard to it
 brings your index file and the working tree back to that state,
 and resets the tip of the branch to that commit.
 
-Undo a merge or pull inside a dirty work tree::
+Undo a merge or pull inside a dirty working tree::
 +
 ------------
 $ git pull                         <1>
@@ -257,7 +273,7 @@ Suppose you are working on something and you commit it, and then you
 continue working a bit more, but now you think that what you have in
 your working tree should be in another branch that has nothing to do
 with what you committed previously. You can start a new branch and
-reset it while keeping the changes in your work tree.
+reset it while keeping the changes in your working tree.
 +
 ------------
 $ git tag start
@@ -294,8 +310,10 @@ In these tables, A, B, C and D are some different states of a
 file. For example, the first line of the first table means that if a
 file is in state A in the working tree, in state B in the index, in
 state C in HEAD and in state D in the target, then "git reset --soft
-target" will put the file in state A in the working tree, in state B
-in the index and in state D in HEAD.
+target" will leave the file in the working tree in state A and in the
+index in state B.  It resets (i.e. moves) the HEAD (i.e. the tip of
+the current branch, if you are on one) to "target" (which has the file
+in state D).
 
       working index HEAD target         working index HEAD
       ----------------------------------------------------
@@ -346,11 +364,11 @@ in the index and in state D in HEAD.
                                --keep   B       C     C
 
 "reset --merge" is meant to be used when resetting out of a conflicted
-merge. Any mergy operation guarantees that the work tree file that is
+merge. Any mergy operation guarantees that the working tree file that is
 involved in the merge does not have local change wrt the index before
-it starts, and that it writes the result out to the work tree. So if
+it starts, and that it writes the result out to the working tree. So if
 we see some difference between the index and the target and also
-between the index and the work tree, then it means that we are not
+between the index and the working tree, then it means that we are not
 resetting out from a state that a mergy operation left after failing
 with a conflict. That is why we disallow --merge option in this case.
 
index c283084272090c28d5529d200a1147c9f57c639b..05904e0e7f31896191bc10224c09d14e91372952 100644 (file)
@@ -97,7 +97,7 @@ See the CONFIGURATION section for 'sendemail.multiedit'.
        Specify the primary recipient of the emails generated. Generally, this
        will be the upstream maintainer of the project involved. Default is the
        value of the 'sendemail.to' configuration value; if that is unspecified,
-       this will be prompted for.
+       and --to-cmd is not specified, this will be prompted for.
 +
 The --to option must be repeated for each user you want on the to list.
 
@@ -165,6 +165,15 @@ user is prompted for a password while the input is masked for privacy.
        are also accepted. The port can also be set with the
        'sendemail.smtpserverport' configuration variable.
 
+--smtp-server-option=<option>::
+       If set, specifies the outgoing SMTP server option to use.
+       Default value can be specified by the 'sendemail.smtpserveroption'
+       configuration option.
++
+The --smtp-server-option option must be repeated for each option you want
+to pass to the server. Likewise, different lines in the configuration files
+must be used for each option.
+
 --smtp-ssl::
        Legacy alias for '--smtp-encryption ssl'.
 
@@ -177,6 +186,12 @@ user is prompted for a password while the input is masked for privacy.
 Automating
 ~~~~~~~~~~
 
+--to-cmd=<command>::
+       Specify a command to execute once per patch file which
+       should generate patch file specific "To:" entries.
+       Output of this command must be single email address per line.
+       Default is the value of 'sendemail.tocmd' configuration value.
+
 --cc-cmd=<command>::
        Specify a command to execute once per patch file which
        should generate patch file specific "Cc:" entries.
index 0f3ad811cfa41e65a3d807a5eb766ce2a66a7831..6403126a029bf43acaa219296353f7ab1f2040a0 100644 (file)
@@ -3,24 +3,30 @@ git-shell(1)
 
 NAME
 ----
-git-shell - Restricted login shell for GIT-only SSH access
+git-shell - Restricted login shell for Git-only SSH access
 
 
 SYNOPSIS
 --------
-'$(git --exec-path)/git-shell' -c <command> <argument>
+'git shell' [-c <command> <argument>]
 
 DESCRIPTION
 -----------
-This is meant to be used as a login shell for SSH accounts you want
-to restrict to GIT pull/push access only. It permits execution only
-of server-side GIT commands implementing the pull/push functionality.
-The commands can be executed only by the '-c' option; the shell is not
-interactive.
-
-Currently, only four commands are permitted to be called, 'git-receive-pack'
-'git-upload-pack' and 'git-upload-archive' with a single required argument, or
-'cvs server' (to invoke 'git-cvsserver').
+
+A login shell for SSH accounts to provide restricted Git access. When
+'-c' is given, the program executes <command> non-interactively;
+<command> can be one of 'git receive-pack', 'git upload-pack', 'git
+upload-archive', 'cvs server', or a command in COMMAND_DIR. The shell
+is started in interactive mode when no arguments are given; in this
+case, COMMAND_DIR must exist, and any of the executables in it can be
+invoked.
+
+'cvs server' is a special command which executes git-cvsserver.
+
+COMMAND_DIR is the path "$HOME/git-shell-commands". The user must have
+read and execute permissions to the directory in order to execute the
+programs in it. The programs are executed with a cwd of $HOME, and
+<argument> is parsed as a command-line string.
 
 Author
 ------
index e5a27d875eb63dbfeccbf5357983f5c94668ffeb..c80ca5da4387e44f42b3bfd54132efc1def881b6 100644 (file)
@@ -62,14 +62,21 @@ consults `$GIT_DIR/info/attributes` file (which has the highest
 precedence), `.gitattributes` file in the same directory as the
 path in question, and its parent directories up to the toplevel of the
 work tree (the further the directory that contains `.gitattributes`
-is from the path in question, the lower its precedence).
+is from the path in question, the lower its precedence). Finally
+global and system-wide files are considered (they have the lowest
+precedence).
 
 If you wish to affect only a single repository (i.e., to assign
-attributes to files that are particular to one user's workflow), then
+attributes to files that are particular to
+one user's workflow for that repository), then
 attributes should be placed in the `$GIT_DIR/info/attributes` file.
 Attributes which should be version-controlled and distributed to other
 repositories (i.e., attributes of interest to all users) should go into
-`.gitattributes` files.
+`.gitattributes` files. Attributes that should affect all repositories
+for a single user should be placed in a file specified by the
+`core.attributesfile` configuration option (see linkgit:git-config[1]).
+Attributes for all users on a system should be placed in the
+`$(prefix)/etc/gitattributes` file.
 
 Sometimes you would need to override an setting of an attribute
 for a path to `unspecified` state.  This can be done by listing
@@ -477,6 +484,8 @@ patterns are available:
 
 - `csharp` suitable for source code in the C# language.
 
+- `fortran` suitable for source code in the Fortran language.
+
 - `html` suitable for HTML/XHTML documents.
 
 - `java` suitable for source code in the Java language.
index 5d91a7e5b3a40cbf41f03a81799c162775c36607..6af29a4603de57e0040c4bcd819b831dd6649f92 100644 (file)
@@ -227,9 +227,9 @@ changes that touch a specified string, and is controlled by the
 commands.
 
 When diffcore-pickaxe is in use, it checks if there are
-filepairs whose "result" side has the specified string and
-whose "origin" side does not.  Such a filepair represents "the
-string appeared in this changeset".  It also checks for the
+filepairs whose "result" side and whose "origin" side have
+different number of specified string.  Such a filepair represents
+"the string appeared in this changeset".  It also checks for the
 opposite case that loses the specified string.
 
 When `\--pickaxe-all` is not in effect, diffcore-pickaxe leaves
index b72f5339704e89087992b7d6060e88f43b9d661b..92772e7c4eb8921d3b2763fe1b533c380bd2b92b 100644 (file)
@@ -7,8 +7,10 @@ merge.conflictstyle::
        marker and the original text before the `=======` marker.
 
 merge.log::
-       Whether to include summaries of merged commits in newly created
-       merge commit messages. False by default.
+       In addition to branch names, populate the log message with at
+       most the specified number of one-line descriptions from the
+       actual commits that are being merged.  Defaults to false, and
+       true is a synoym for 20.
 
 merge.renameLimit::
        The number of files to consider when performing rename detection
index 722d704ff2de1abd3d77a18ca396ba96d5cab6bc..e33e0f8e110879a0cd6fe81d97ee6044290e5cfa 100644 (file)
@@ -16,11 +16,11 @@ inspect and further tweak the merge result before committing.
 With --no-ff Generate a merge commit even if the merge
 resolved as a fast-forward.
 
---log::
+--log[=<n>]::
 --no-log::
        In addition to branch names, populate the log message with
-       one-line descriptions from the actual commits that are being
-       merged.
+       one-line descriptions from at most <n> actual commits that are being
+       merged. See also linkgit:git-fmt-merge-msg[1].
 +
 With --no-log do not list one-line descriptions from the
 actual commits being merged.
index 8676e26ca20311196d069ab28c08c913271c7631..595a3cf1a7118ba29a1d57d7fc17d233d89cd3d0 100644 (file)
@@ -40,6 +40,28 @@ the other tree did, declaring 'our' history contains all that happened in it.
 theirs;;
        This is opposite of 'ours'.
 
+patience;;
+       With this option, 'merge-recursive' spends a little extra time
+       to avoid mismerges that sometimes occur due to unimportant
+       matching lines (e.g., braces from distinct functions).  Use
+       this when the branches to be merged have diverged wildly.
+       See also linkgit:git-diff[1] `--patience`.
+
+ignore-space-change;;
+ignore-all-space;;
+ignore-space-at-eol;;
+       Treats lines with the indicated type of whitespace change as
+       unchanged for the sake of a three-way merge.  Whitespace
+       changes mixed with other changes to a line are not ignored.
+       See also linkgit:git-diff[1] `-b`, `-w`, and
+       `--ignore-space-at-eol`.
++
+* If 'their' version only introduces whitespace changes to a line,
+  'our' version is used;
+* If 'our' version introduces whitespace changes but 'their'
+  version includes a substantial change, 'their' version is used;
+* Otherwise, the merge proceeds in the usual way.
+
 renormalize;;
        This runs a virtual check-out and check-in of all three stages
        of a file when resolving a three-way merge.  This option is
@@ -52,6 +74,10 @@ no-renormalize;;
        Disables the `renormalize` option.  This overrides the
        `merge.renormalize` configuration variable.
 
+rename-threshold=<n>;;
+       Controls the similarity threshold used for rename detection.
+       See also linkgit:git-diff[1] `-M`.
+
 subtree[=<path>];;
        This option is a more advanced form of 'subtree' strategy, where
        the strategy makes a guess on how two trees must be shifted to
index a7e050bb7acb59764c851b09cd488b6620c0a2dd..9dc1bed768a473317dd77c4d1ed93f65b2c4d483 100644 (file)
@@ -17,6 +17,40 @@ responsible for a few things.
    path-specific merge drivers (specified in `.gitattributes`)
    into account.
 
+Data structures
+---------------
+
+* `mmbuffer_t`, `mmfile_t`
+
+These store data usable for use by the xdiff backend, for writing and
+for reading, respectively.  See `xdiff/xdiff.h` for the definitions
+and `diff.c` for examples.
+
+* `struct ll_merge_options`
+
+This describes the set of options the calling program wants to affect
+the operation of a low-level (single file) merge.  Some options:
+
+`virtual_ancestor`::
+       Behave as though this were part of a merge between common
+       ancestors in a recursive merge.
+       If a helper program is specified by the
+       `[merge "<driver>"] recursive` configuration, it will
+       be used (see linkgit:gitattributes[5]).
+
+`variant`::
+       Resolve local conflicts automatically in favor
+       of one side or the other (as in 'git merge-file'
+       `--ours`/`--theirs`/`--union`).  Can be `0`,
+       `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
+       `XDL_MERGE_FAVOR_UNION`.
+
+`renormalize`::
+       Resmudge and clean the "base", "theirs" and "ours" files
+       before merging.  Use this when the merge is likely to have
+       overlapped with a change in smudge/clean or end-of-line
+       normalization rules.
+
 Low-level (single file) merge
 -----------------------------
 
@@ -28,15 +62,24 @@ Low-level (single file) merge
        `.git/info/attributes` into account.  Returns 0 for a
        clean merge.
 
-The caller:
+Calling sequence:
 
-1. allocates an mmbuffer_t variable for the result;
-2. allocates and fills variables with the file's original content
-   and two modified versions (using `read_mmfile`, for example);
-3. calls ll_merge();
-4. reads the output from result_buf.ptr and result_buf.size;
-5. releases buffers when finished (free(ancestor.ptr); free(ours.ptr);
-   free(theirs.ptr); free(result_buf.ptr);).
+* Prepare a `struct ll_merge_options` to record options.
+  If you have no special requests, skip this and pass `NULL`
+  as the `opts` parameter to use the default options.
+
+* Allocate an mmbuffer_t variable for the result.
+
+* Allocate and fill variables with the file's original content
+  and two modified versions (using `read_mmfile`, for example).
+
+* Call `ll_merge()`.
+
+* Read the merged content from `result_buf.ptr` and `result_buf.size`.
+
+* Release buffers when finished.  A simple
+  `free(ancestor.ptr); free(ours.ptr); free(theirs.ptr);
+  free(result_buf.ptr);` will do.
 
 If the modifications do not merge cleanly, `ll_merge` will return a
 nonzero value and `result_buf` will generally include a description of
@@ -47,18 +90,6 @@ The `ancestor_label`, `our_label`, and `their_label` parameters are
 used to label the different sides of a conflict if the merge driver
 supports this.
 
-The `flag` parameter is a bitfield:
-
- - The `LL_OPT_VIRTUAL_ANCESTOR` bit indicates whether this is an
-   internal merge to consolidate ancestors for a recursive merge.
-
- - The `LL_OPT_FAVOR_MASK` bits allow local conflicts to be automatically
-   resolved in favor of one side or the other (as in 'git merge-file'
-   `--ours`/`--theirs`/`--union`).
-   They can be populated by `create_ll_flag`, whose argument can be
-   `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
-   `XDL_MERGE_FAVOR_UNION`.
-
 Everything else
 ---------------
 
index 9ff9e51205ca14aef2f0e2c5936d14f715f1ea61..d441d88d6fbd6d98bb617e5cf5d2bd7704f86a45 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.3.2
+DEF_VER=v1.7.3.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index 59200b730ec00a63f981691f7fd37f2eacb11653..10a1cba643cc9e6cc9a441708b1d43f468fb8383 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -67,10 +67,10 @@ Issues of note:
        - A POSIX-compliant shell is required to run many scripts needed
          for everyday use (e.g. "bisect", "pull").
 
-       - "Perl" is needed to use some of the features (e.g. preparing a
-         partial commit using "git add -i/-p", interacting with svn
-         repositories with "git svn").  If you can live without these, use
-         NO_PERL.
+       - "Perl" version 5.8 or later is needed to use some of the
+         features (e.g. preparing a partial commit using "git add -i/-p",
+         interacting with svn repositories with "git svn").  If you can
+         live without these, use NO_PERL.
 
        - "openssl" library is used by git-imap-send to use IMAP over SSL.
          If you don't need it, use NO_OPENSSL.
index d3dcfb18a7a4dffddb8e8cc742b7c151e639549e..1f1ce04edf0b87f6fdbd579052d5ccaa538e6b93 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -270,6 +270,7 @@ STRIP ?= strip
 #   infodir
 #   htmldir
 #   ETC_GITCONFIG (but not sysconfdir)
+#   ETC_GITATTRIBUTES
 # can be specified as a relative path some/where/else;
 # this is interpreted as relative to $(prefix) and "git" at
 # runtime figures out where they are based on the path to the executable.
@@ -288,9 +289,11 @@ htmldir = share/doc/git-doc
 ifeq ($(prefix),/usr)
 sysconfdir = /etc
 ETC_GITCONFIG = $(sysconfdir)/gitconfig
+ETC_GITATTRIBUTES = $(sysconfdir)/gitattributes
 else
 sysconfdir = $(prefix)/etc
 ETC_GITCONFIG = etc/gitconfig
+ETC_GITATTRIBUTES = etc/gitattributes
 endif
 lib = lib
 # DESTDIR=
@@ -1523,6 +1526,7 @@ endif
 
 SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
+ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
 
 DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
 bindir_SQ = $(subst ','\'',$(bindir))
@@ -1901,6 +1905,8 @@ builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
 
 config.s config.o: EXTRA_CPPFLAGS = -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 
+attr.s attr.o: EXTRA_CPPFLAGS = -DETC_GITATTRIBUTES='"$(ETC_GITATTRIBUTES_SQ)"'
+
 http.s http.o: EXTRA_CPPFLAGS = -DGIT_HTTP_USER_AGENT='"git/$(GIT_VERSION)"'
 
 ifdef NO_EXPAT
@@ -1977,7 +1983,7 @@ cscope:
        $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 
 ### Detect prefix changes
-TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
+TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
 
 GIT-CFLAGS: FORCE
index c2b0d4d1266e8f3b104133da35cfcc2ec4ad97a6..b942e499449d97aeb50c73ca2bdc1c6e6d528743 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.7.3.2.txt
\ No newline at end of file
+Documentation/RelNotes/1.7.4.txt
\ No newline at end of file
index c91a29cb298a3ad792ff8745f3e8e0eb28d71678..91ca00f05f7d648fa801a36b78c749f9d691ba43 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -108,10 +108,14 @@ const char *make_nonrelative_path(const char *path)
                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
                        die("Too long path: %.*s", 60, path);
        } else {
+               size_t len;
+               const char *fmt;
                const char *cwd = get_pwd_cwd();
                if (!cwd)
                        die_errno("Cannot determine the current working directory");
-               if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
+               len = strlen(cwd);
+               fmt = (len > 0 && is_dir_sep(cwd[len-1])) ? "%s%s" : "%s/%s";
+               if (snprintf(buf, PATH_MAX, fmt, cwd, path) >= PATH_MAX)
                        die("Too long path: %.*s", 60, path);
        }
        return buf;
diff --git a/attr.c b/attr.c
index 8ba606c933088e27ac08aabb546b764745f8187e..6aff6951d38d7f672aeb9851eca88c6e5a26b950 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -1,5 +1,6 @@
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "exec_cmd.h"
 #include "attr.h"
 
 const char git_attr__true[] = "(builtin)true";
@@ -10,6 +11,8 @@ static const char git_attr__unknown[] = "(builtin)unknown";
 #define ATTR__UNSET NULL
 #define ATTR__UNKNOWN git_attr__unknown
 
+static const char *attributes_file;
+
 /*
  * The basic design decision here is that we are not going to have
  * insanely large number of attributes.
@@ -462,6 +465,32 @@ static void drop_attr_stack(void)
        }
 }
 
+const char *git_etc_gitattributes(void)
+{
+       static const char *system_wide;
+       if (!system_wide)
+               system_wide = system_path(ETC_GITATTRIBUTES);
+       return system_wide;
+}
+
+int git_attr_system(void)
+{
+       return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
+}
+
+int git_attr_global(void)
+{
+       return !git_env_bool("GIT_ATTR_NOGLOBAL", 0);
+}
+
+static int git_attr_config(const char *var, const char *value, void *dummy)
+{
+       if (!strcmp(var, "core.attributesfile"))
+               return git_config_pathname(&attributes_file, var, value);
+
+       return 0;
+}
+
 static void bootstrap_attr_stack(void)
 {
        if (!attr_stack) {
@@ -472,6 +501,25 @@ static void bootstrap_attr_stack(void)
                elem->prev = attr_stack;
                attr_stack = elem;
 
+               if (git_attr_system()) {
+                       elem = read_attr_from_file(git_etc_gitattributes(), 1);
+                       if (elem) {
+                               elem->origin = NULL;
+                               elem->prev = attr_stack;
+                               attr_stack = elem;
+                       }
+               }
+
+               git_config(git_attr_config, NULL);
+               if (git_attr_global() && attributes_file) {
+                       elem = read_attr_from_file(attributes_file, 1);
+                       if (elem) {
+                               elem->origin = NULL;
+                               elem->prev = attr_stack;
+                               attr_stack = elem;
+                       }
+               }
+
                if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
                        elem = read_attr(GITATTRIBUTES_FILE, 1);
                        elem->origin = strdup("");
@@ -499,7 +547,9 @@ static void prepare_attr_stack(const char *path, int dirlen)
 
        /*
         * At the bottom of the attribute stack is the built-in
-        * set of attribute definitions.  Then, contents from
+        * set of attribute definitions, followed by the contents
+        * of $(prefix)/etc/gitattributes and a file specified by
+        * core.attributesfile.  Then, contents from
         * .gitattribute files from directories closer to the
         * root to the ones in deeper directories are pushed
         * to the stack.  Finally, at the very top of the stack
index 0398d247400e55f2a2ededc054902374c7516d5b..f2a25a084736e573f560672fabc9c6214997f437 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -7,14 +7,15 @@
 #include "commit.h"
 #include "notes.h"
 
+#define DEFAULT_MERGE_LOG_LEN 20
+
 extern const char git_version_string[];
 extern const char git_usage_string[];
 extern const char git_more_info_string[];
 
 extern void prune_packed_objects(int);
-extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
-       struct strbuf *out);
-extern int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out);
+extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+                        int merge_title, int shortlog_len);
 extern int commit_notes(struct notes_tree *t, const char *msg);
 
 struct notes_rewrite_cfg {
index a54583b3a4936b341820cf5c639e5647ce456a5a..9240fafb2ad17505bf407363e058d208a8a70497 100644 (file)
@@ -161,7 +161,7 @@ static int checkout_merged(int pos, struct checkout *state)
         * merge.renormalize set, too
         */
        status = ll_merge(&result_buf, path, &ancestor, "base",
-                         &ours, "ours", &theirs, "theirs", 0);
+                         &ours, "ours", &theirs, "theirs", NULL);
        free(ancestor.ptr);
        free(ours.ptr);
        free(theirs.ptr);
index e06573920f461229717b885e291c76831f87e428..d083795e26e7893c6b7d466f9bcf2be1311cfeb2 100644 (file)
@@ -56,10 +56,12 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        if (strbuf_read(&buffer, 0, 0) < 0)
                die_errno("git commit-tree: failed to read");
 
-       if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
-               printf("%s\n", sha1_to_hex(commit_sha1));
-               return 0;
-       }
-       else
+       if (commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
+               strbuf_release(&buffer);
                return 1;
+       }
+
+       printf("%s\n", sha1_to_hex(commit_sha1));
+       strbuf_release(&buffer);
+       return 0;
 }
index e7e12eea250691a2c0d70964d1fffea0923dd292..78c77742b6e6863b1eb4d61750ae253710223aa2 100644 (file)
@@ -7,21 +7,22 @@
 #include "string-list.h"
 
 static const char * const fmt_merge_msg_usage[] = {
-       "git fmt-merge-msg [-m <message>] [--log|--no-log] [--file <file>]",
+       "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]",
        NULL
 };
 
-static int merge_summary;
+static int shortlog_len;
 
 static int fmt_merge_msg_config(const char *key, const char *value, void *cb)
 {
-       static int found_merge_log = 0;
-       if (!strcmp("merge.log", key)) {
-               found_merge_log = 1;
-               merge_summary = git_config_bool(key, value);
+       if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
+               int is_bool;
+               shortlog_len = git_config_bool_or_int(key, value, &is_bool);
+               if (!is_bool && shortlog_len < 0)
+                       return error("%s: negative length %s", key, value);
+               if (is_bool && shortlog_len)
+                       shortlog_len = DEFAULT_MERGE_LOG_LEN;
        }
-       if (!found_merge_log && !strcmp("merge.summary", key))
-               merge_summary = git_config_bool(key, value);
        return 0;
 }
 
@@ -255,9 +256,9 @@ static void do_fmt_merge_msg_title(struct strbuf *out,
                strbuf_addf(out, " into %s\n", current_branch);
 }
 
-static int do_fmt_merge_msg(int merge_title, int merge_summary,
-       struct strbuf *in, struct strbuf *out) {
-       int limit = 20, i = 0, pos = 0;
+static int do_fmt_merge_msg(int merge_title, struct strbuf *in,
+       struct strbuf *out, int shortlog_len) {
+       int i = 0, pos = 0;
        unsigned char head_sha1[20];
        const char *current_branch;
 
@@ -288,7 +289,7 @@ static int do_fmt_merge_msg(int merge_title, int merge_summary,
        if (merge_title)
                do_fmt_merge_msg_title(out, current_branch);
 
-       if (merge_summary) {
+       if (shortlog_len) {
                struct commit *head;
                struct rev_info rev;
 
@@ -303,17 +304,14 @@ static int do_fmt_merge_msg(int merge_title, int merge_summary,
 
                for (i = 0; i < origins.nr; i++)
                        shortlog(origins.items[i].string, origins.items[i].util,
-                                       head, &rev, limit, out);
+                                       head, &rev, shortlog_len, out);
        }
        return 0;
 }
 
-int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
-       return do_fmt_merge_msg(1, merge_summary, in, out);
-}
-
-int fmt_merge_msg_shortlog(struct strbuf *in, struct strbuf *out) {
-       return do_fmt_merge_msg(0, 1, in, out);
+int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
+                 int merge_title, int shortlog_len) {
+       return do_fmt_merge_msg(merge_title, in, out, shortlog_len);
 }
 
 int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
@@ -321,10 +319,13 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        const char *inpath = NULL;
        const char *message = NULL;
        struct option options[] = {
-               OPT_BOOLEAN(0, "log",     &merge_summary, "populate log with the shortlog"),
-               { OPTION_BOOLEAN, 0, "summary", &merge_summary, NULL,
+               { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
+                 "populate log with at most <n> entries from shortlog",
+                 PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
+               { OPTION_INTEGER, 0, "summary", &shortlog_len, "n",
                  "alias for --log (deprecated)",
-                 PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+                 PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, NULL,
+                 DEFAULT_MERGE_LOG_LEN },
                OPT_STRING('m', "message", &message, "text",
                        "use <text> as start of message"),
                OPT_FILENAME('F', "file", &inpath, "file to read from"),
@@ -340,12 +341,14 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
                             0);
        if (argc > 0)
                usage_with_options(fmt_merge_msg_usage, options);
-       if (message && !merge_summary) {
+       if (message && !shortlog_len) {
                char nl = '\n';
                write_in_full(STDOUT_FILENO, message, strlen(message));
                write_in_full(STDOUT_FILENO, &nl, 1);
                return 0;
        }
+       if (shortlog_len < 0)
+               die("Negative --log=%d", shortlog_len);
 
        if (inpath && strcmp(inpath, "-")) {
                in = fopen(inpath, "r");
@@ -355,12 +358,13 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
 
        if (strbuf_read(&input, fileno(in), 0) < 0)
                die_errno("could not read input file");
-       if (message) {
+
+       if (message)
                strbuf_addstr(&output, message);
-               ret = fmt_merge_msg_shortlog(&input, &output);
-       } else {
-               ret = fmt_merge_msg(merge_summary, &input, &output);
-       }
+       ret = fmt_merge_msg(&input, &output,
+                           message ? 0 : 1,
+                           shortlog_len);
+
        if (ret)
                return ret;
        write_in_full(STDOUT_FILENO, output.buf, output.len);
index eaa1ee0fa71a344af747dec038c6a5907c8273a5..22d12903ac06597979f30d0fd94267fc543afa29 100644 (file)
@@ -1056,8 +1056,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.verbose_header = 1;
        rev.diff = 1;
-       rev.combine_merges = 0;
-       rev.ignore_merges = 1;
+       rev.no_merges = 1;
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
@@ -1228,10 +1227,6 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
-               /* ignore merges */
-               if (commit->parents && commit->parents->next)
-                       continue;
-
                if (ignore_if_in_upstream &&
                                has_commit_patch_id(commit, &ids))
                        continue;
index 78b9db76a0819529e7f6cab44319acd2978c9e9b..c33091b3ed52bc8539ff82f039ec8c7718f3dcc2 100644 (file)
@@ -2,6 +2,7 @@
 #include "commit.h"
 #include "tag.h"
 #include "merge-recursive.h"
+#include "xdiff-interface.h"
 
 static const char builtin_merge_recursive_usage[] =
        "git %s <base>... -- <head> <remote> ...";
@@ -40,19 +41,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                if (!prefixcmp(arg, "--")) {
                        if (!arg[2])
                                break;
-                       if (!strcmp(arg+2, "ours"))
-                               o.recursive_variant = MERGE_RECURSIVE_OURS;
-                       else if (!strcmp(arg+2, "theirs"))
-                               o.recursive_variant = MERGE_RECURSIVE_THEIRS;
-                       else if (!strcmp(arg+2, "subtree"))
-                               o.subtree_shift = "";
-                       else if (!prefixcmp(arg+2, "subtree="))
-                               o.subtree_shift = arg + 10;
-                       else if (!strcmp(arg+2, "renormalize"))
-                               o.renormalize = 1;
-                       else if (!strcmp(arg+2, "no-renormalize"))
-                               o.renormalize = 0;
-                       else
+                       if (parse_merge_opt(&o, arg + 2))
                                die("Unknown option %s", arg);
                        continue;
                }
index 5f65c0c8a6eacd4c6d05602ad9de2821ec62a034..10f091b51960cd6ae913b1c4d2ab9e6931862351 100644 (file)
@@ -42,7 +42,7 @@ static const char * const builtin_merge_usage[] = {
        NULL
 };
 
-static int show_diffstat = 1, option_log, squash;
+static int show_diffstat = 1, shortlog_len, squash;
 static int option_commit = 1, allow_fast_forward = 1;
 static int fast_forward_only;
 static int allow_trivial = 1, have_message;
@@ -177,8 +177,9 @@ static struct option builtin_merge_options[] = {
        OPT_BOOLEAN(0, "stat", &show_diffstat,
                "show a diffstat at the end of the merge"),
        OPT_BOOLEAN(0, "summary", &show_diffstat, "(synonym to --stat)"),
-       OPT_BOOLEAN(0, "log", &option_log,
-               "add list of one-line log to merge commit message"),
+       { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
+         "add (at most <n>) entries from shortlog to merge commit message",
+         PARSE_OPT_OPTARG, NULL, DEFAULT_MERGE_LOG_LEN },
        OPT_BOOLEAN(0, "squash", &squash,
                "create a single commit instead of doing a merge"),
        OPT_BOOLEAN(0, "commit", &option_commit,
@@ -504,10 +505,17 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                return git_config_string(&pull_twohead, k, v);
        else if (!strcmp(k, "pull.octopus"))
                return git_config_string(&pull_octopus, k, v);
-       else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary"))
-               option_log = git_config_bool(k, v);
        else if (!strcmp(k, "merge.renormalize"))
                option_renormalize = git_config_bool(k, v);
+       else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) {
+               int is_bool;
+               shortlog_len = git_config_bool_or_int(k, v, &is_bool);
+               if (!is_bool && shortlog_len < 0)
+                       return error("%s: negative length %s", k, v);
+               if (is_bool && shortlog_len)
+                       shortlog_len = DEFAULT_MERGE_LOG_LEN;
+               return 0;
+       }
        return git_diff_ui_config(k, v, cb);
 }
 
@@ -631,25 +639,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
 
                o.renormalize = option_renormalize;
 
-               /*
-                * NEEDSWORK: merge with table in builtin/merge-recursive
-                */
-               for (x = 0; x < xopts_nr; x++) {
-                       if (!strcmp(xopts[x], "ours"))
-                               o.recursive_variant = MERGE_RECURSIVE_OURS;
-                       else if (!strcmp(xopts[x], "theirs"))
-                               o.recursive_variant = MERGE_RECURSIVE_THEIRS;
-                       else if (!strcmp(xopts[x], "subtree"))
-                               o.subtree_shift = "";
-                       else if (!prefixcmp(xopts[x], "subtree="))
-                               o.subtree_shift = xopts[x]+8;
-                       else if (!strcmp(xopts[x], "renormalize"))
-                               o.renormalize = 1;
-                       else if (!strcmp(xopts[x], "no-renormalize"))
-                               o.renormalize = 0;
-                       else
+               for (x = 0; x < xopts_nr; x++)
+                       if (parse_merge_opt(&o, xopts[x]))
                                die("Unknown option for merge-recursive: -X%s", xopts[x]);
-               }
 
                o.branch1 = head_arg;
                o.branch2 = remoteheads->item->util;
@@ -1012,14 +1004,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                for (i = 0; i < argc; i++)
                        merge_name(argv[i], &merge_names);
 
-               if (have_message && option_log)
-                       fmt_merge_msg_shortlog(&merge_names, &merge_msg);
-               else if (!have_message)
-                       fmt_merge_msg(option_log, &merge_names, &merge_msg);
-
-
-               if (!(have_message && !option_log) && merge_msg.len)
-                       strbuf_setlen(&merge_msg, merge_msg.len-1);
+               if (!have_message || shortlog_len) {
+                       fmt_merge_msg(&merge_names, &merge_msg, !have_message,
+                                     shortlog_len);
+                       if (merge_msg.len)
+                               strbuf_setlen(&merge_msg, merge_msg.len - 1);
+               }
        }
 
        if (head_invalid || !argc)
index 9ad1e66916545dd2aeeb110661e56edfa5921019..eb1e3e7467ac722f4b6cc1da03f7671d21abc676 100644 (file)
@@ -16,6 +16,7 @@
 #include "resolve-undo.h"
 
 static int nr_trees;
+static int read_empty;
 static struct tree *trees[MAX_UNPACK_TREES];
 
 static int list_tree(unsigned char *sha1)
@@ -32,7 +33,7 @@ static int list_tree(unsigned char *sha1)
 }
 
 static const char * const read_tree_usage[] = {
-       "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
+       "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] (--empty | <tree-ish1> [<tree-ish2> [<tree-ish3>]])",
        NULL
 };
 
@@ -106,6 +107,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                { 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),
                OPT__VERBOSE(&opts.verbose_update),
                OPT_GROUP("Merging"),
                OPT_SET_INT('m', NULL, &opts.merge,
@@ -166,6 +169,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                        die("failed to unpack tree object %s", arg);
                stage++;
        }
+       if (nr_trees == 0 && !read_empty)
+               warning("read-tree: emptying the index with no arguments is deprecated; use --empty");
+       else if (nr_trees > 0 && read_empty)
+               die("passing trees as arguments contradicts --empty");
+
        if (1 < opts.index_only + opts.update)
                die("-u and -i at the same time makes no sense");
        if ((opts.update||opts.index_only) && !opts.merge)
index 4b47ace36b51566acb5b748f679070f02cbbf568..57b51e4a0e148062e1945d640b81559d65e4c2ba 100644 (file)
@@ -442,7 +442,7 @@ static int do_pick_commit(void)
        else
                parent = commit->parents->item;
 
-       if (allow_ff && !hashcmp(parent->object.sha1, head))
+       if (allow_ff && parent && !hashcmp(parent->object.sha1, head))
                return fast_forward_to(commit->object.sha1, head);
 
        if (parent && parse_commit(parent) < 0)
index f2d9e1fd974b7271366da09370e10fafd2c50f08..6590f33cc8157f61ff7b6e0f6330bacf465ad39c 100644 (file)
@@ -127,7 +127,7 @@ int mingw_open (const char *filename, int oflags, ...)
        mode = va_arg(args, int);
        va_end(args);
 
-       if (!strcmp(filename, "/dev/null"))
+       if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
 
        fd = open(filename, oflags, mode);
@@ -160,7 +160,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t count)
 #undef fopen
 FILE *mingw_fopen (const char *filename, const char *otype)
 {
-       if (!strcmp(filename, "/dev/null"))
+       if (filename && !strcmp(filename, "/dev/null"))
                filename = "nul";
        return fopen(filename, otype);
 }
@@ -192,8 +192,11 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
 /* We keep the do_lstat code in a separate function to avoid recursion.
  * When a path ends with a slash, the stat will fail with ENOENT. In
  * this case, we strip the trailing slashes and stat again.
+ *
+ * If follow is true then act like stat() and report on the link
+ * target. Otherwise report on the link itself.
  */
-static int do_lstat(const char *file_name, struct stat *buf)
+static int do_lstat(int follow, const char *file_name, struct stat *buf)
 {
        WIN32_FILE_ATTRIBUTE_DATA fdata;
 
@@ -209,6 +212,25 @@ static int do_lstat(const char *file_name, struct stat *buf)
                buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
                buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
                buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+               if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+                       WIN32_FIND_DATAA findbuf;
+                       HANDLE handle = FindFirstFileA(file_name, &findbuf);
+                       if (handle != INVALID_HANDLE_VALUE) {
+                               if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+                                               (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
+                                       if (follow) {
+                                               char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+                                               buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+                                       } else {
+                                               buf->st_mode = S_IFLNK;
+                                       }
+                                       buf->st_mode |= S_IREAD;
+                                       if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+                                               buf->st_mode |= S_IWRITE;
+                               }
+                               FindClose(handle);
+                       }
+               }
                return 0;
        }
        return -1;
@@ -220,12 +242,12 @@ static int do_lstat(const char *file_name, struct stat *buf)
  * complete. Note that Git stat()s are redirected to mingw_lstat()
  * too, since Windows doesn't really handle symlinks that well.
  */
-int mingw_lstat(const char *file_name, struct stat *buf)
+static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
 {
        int namelen;
        static char alt_name[PATH_MAX];
 
-       if (!do_lstat(file_name, buf))
+       if (!do_lstat(follow, file_name, buf))
                return 0;
 
        /* if file_name ended in a '/', Windows returned ENOENT;
@@ -244,7 +266,16 @@ int mingw_lstat(const char *file_name, struct stat *buf)
 
        memcpy(alt_name, file_name, namelen);
        alt_name[namelen] = 0;
-       return do_lstat(alt_name, buf);
+       return do_lstat(follow, alt_name, buf);
+}
+
+int mingw_lstat(const char *file_name, struct stat *buf)
+{
+       return do_stat_internal(0, file_name, buf);
+}
+int mingw_stat(const char *file_name, struct stat *buf)
+{
+       return do_stat_internal(1, file_name, buf);
 }
 
 #undef fstat
@@ -873,6 +904,11 @@ void mingw_execvp(const char *cmd, char *const *argv)
        free_path_split(path);
 }
 
+void mingw_execv(const char *cmd, char *const *argv)
+{
+       mingw_execve(cmd, argv, environ);
+}
+
 static char **copy_environ(void)
 {
        char **env;
@@ -1386,6 +1422,7 @@ void mingw_open_html(const char *unixpath)
                        const char *, const char *, const char *, INT);
        T ShellExecute;
        HMODULE shell32;
+       int r;
 
        shell32 = LoadLibrary("shell32.dll");
        if (!shell32)
@@ -1395,9 +1432,12 @@ void mingw_open_html(const char *unixpath)
                die("cannot run browser");
 
        printf("Launching default browser to display HTML ...\n");
-       ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
-
+       r = (int)ShellExecute(NULL, "open", htmlpath, NULL, "\\", SW_SHOWNORMAL);
        FreeLibrary(shell32);
+       /* see the MSDN documentation referring to the result codes here */
+       if (r <= 32) {
+               die("failed to launch browser for %.*s", MAX_PATH, unixpath);
+       }
 }
 
 int link(const char *oldpath, const char *newpath)
index 3b2477be5f658be665f19a12b48cc47fa07d1c6b..83e35e833b7f214e5d10a3e74a75fb17d80c2e13 100644 (file)
@@ -6,17 +6,30 @@
  */
 
 typedef int pid_t;
+typedef int uid_t;
 #define hstrerror strerror
 
 #define S_IFLNK    0120000 /* Symbolic link */
 #define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
 #define S_ISSOCK(x) 0
+
+#ifndef _STAT_H_
+#define S_IRUSR 0
+#define S_IWUSR 0
+#define S_IXUSR 0
+#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
+#endif
 #define S_IRGRP 0
 #define S_IWGRP 0
 #define S_IXGRP 0
-#define S_ISGID 0
+#define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
 #define S_IROTH 0
+#define S_IWOTH 0
 #define S_IXOTH 0
+#define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
+#define S_ISUID 0
+#define S_ISGID 0
+#define S_ISVTX 0
 
 #define WIFEXITED(x) 1
 #define WIFSIGNALED(x) 0
@@ -65,6 +78,12 @@ struct itimerval {
 };
 #define ITIMER_REAL 0
 
+/*
+ * sanitize preprocessor namespace polluted by Windows headers defining
+ * macros which collide with git local versions
+ */
+#undef HELP_COMMAND /* from winuser.h */
+
 /*
  * trivial stubs
  */
@@ -75,17 +94,17 @@ static inline int symlink(const char *oldpath, const char *newpath)
 { errno = ENOSYS; return -1; }
 static inline int fchmod(int fildes, mode_t mode)
 { errno = ENOSYS; return -1; }
-static inline int fork(void)
+static inline pid_t fork(void)
 { errno = ENOSYS; return -1; }
 static inline unsigned int alarm(unsigned int seconds)
 { return 0; }
 static inline int fsync(int fd)
 { return _commit(fd); }
-static inline int getppid(void)
+static inline pid_t getppid(void)
 { return 1; }
 static inline void sync(void)
 {}
-static inline int getuid()
+static inline uid_t getuid(void)
 { return 1; }
 static inline struct passwd *getpwnam(const char *name)
 { return NULL; }
@@ -117,7 +136,7 @@ static inline int mingw_unlink(const char *pathname)
 }
 #define unlink mingw_unlink
 
-static inline int waitpid(pid_t pid, int *status, unsigned options)
+static inline pid_t waitpid(pid_t pid, int *status, unsigned options)
 {
        if (options == 0)
                return _cwait(status, pid, 0);
@@ -158,7 +177,7 @@ int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
 struct tm *gmtime_r(const time_t *timep, struct tm *result);
 struct tm *localtime_r(const time_t *timep, struct tm *result);
 int getpagesize(void); /* defined in MinGW's libgcc.a */
-struct passwd *getpwuid(int uid);
+struct passwd *getpwuid(uid_t uid);
 int setitimer(int type, struct itimerval *in, struct itimerval *out);
 int sigaction(int sig, struct sigaction *in, struct sigaction *out);
 int link(const char *oldpath, const char *newpath);
@@ -222,10 +241,11 @@ int mingw_getpagesize(void);
 #ifndef ALREADY_DECLARED_STAT_FUNCS
 #define stat _stati64
 int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_stat(const char *file_name, struct stat *buf);
 int mingw_fstat(int fd, struct stat *buf);
 #define fstat mingw_fstat
 #define lstat mingw_lstat
-#define _stati64(x,y) mingw_lstat(x,y)
+#define _stati64(x,y) mingw_stat(x,y)
 #endif
 
 int mingw_utime(const char *file_name, const struct utimbuf *times);
@@ -236,6 +256,8 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
                     int fhin, int fhout, int fherr);
 void mingw_execvp(const char *cmd, char *const *argv);
 #define execvp mingw_execvp
+void mingw_execv(const char *cmd, char *const *argv);
+#define execv mingw_execv
 
 static inline unsigned int git_ntohl(unsigned int x)
 { return (unsigned int)ntohl(x); }
index 56731c35c9d1b5e2d2c82cdff51ab496dcbac7f5..cc55b6d4f741948f3bd05c6299fabdb9f21d7f95 100644 (file)
@@ -282,7 +282,15 @@ GIT_PARSE_WITH(iconv))
 GIT_PARSE_WITH_SET_MAKE_VAR(gitconfig, ETC_GITCONFIG,
                        Use VALUE instead of /etc/gitconfig as the
                        global git configuration file.
-                       If VALUE is not fully qualified it will be interpretted
+                       If VALUE is not fully qualified it will be interpreted
+                       as a path relative to the computed prefix at runtime.)
+
+#
+# Allow user to set ETC_GITATTRIBUTES variable
+GIT_PARSE_WITH_SET_MAKE_VAR(gitattributes, ETC_GITATTRIBUTES,
+                       Use VALUE instead of /etc/gitattributes as the
+                       global git attributes file.
+                       If VALUE is not fully qualified it will be interpreted
                        as a path relative to the computed prefix at runtime.)
 
 #
index d0627e0852cc47df3ffd29754254ccb99c3b2ad0..9775dffb5d49521a7078643104762cd136edffcc 100755 (executable)
@@ -122,7 +122,7 @@ def report(refname, merged):
     branch = os.path.basename(refname)
 
     # Compute a shortnane for the revision
-    rev = do("git describe ${merged} 2>/dev/null") or merged[:12]
+    rev = do("git describe '"+ merged +"' 2>/dev/null") or merged[:12]
 
     # Extract the neta-information for the commit
     rawcommit = do("git cat-file commit " + merged)
index f83f019ca91a2611f5bd3474ccf6d10081320c4c..168669bbf79cb33c527a688fb906e276beadaf79 100755 (executable)
 #    2) Added the following line to your .bashrc:
 #        source ~/.git-completion.sh
 #
+#       Or, add the following lines to your .zshrc:
+#        autoload bashcompinit
+#        bashcompinit
+#        source ~/.git-completion.sh
+#
 #    3) Consider changing your PS1 to also show the current branch:
 #        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 #
@@ -138,11 +143,12 @@ __git_ps1_show_upstream ()
                # get the upstream from the "git-svn-id: ..." in a commit message
                # (git-svn uses essentially the same procedure internally)
                local svn_upstream=($(git log --first-parent -1 \
-                                       --grep="^git-svn-id: \(${svn_url_pattern:2}\)" 2>/dev/null))
+                                       --grep="^git-svn-id: \(${svn_url_pattern#??}\)" 2>/dev/null))
                if [[ 0 -ne ${#svn_upstream[@]} ]]; then
                        svn_upstream=${svn_upstream[ ${#svn_upstream[@]} - 2 ]}
                        svn_upstream=${svn_upstream%@*}
-                       for ((n=1; "$n" <= "${#svn_remote[@]}"; ++n)); do
+                       local n_stop="${#svn_remote[@]}"
+                       for ((n=1; n <= n_stop; ++n)); do
                                svn_upstream=${svn_upstream#${svn_remote[$n]}}
                        done
 
@@ -2339,6 +2345,11 @@ _git ()
 {
        local i c=1 command __git_dir
 
+       if [[ -n ${ZSH_VERSION-} ]]; then
+               emulate -L bash
+               setopt KSH_TYPESET
+       fi
+
        while [ $c -lt $COMP_CWORD ]; do
                i="${COMP_WORDS[c]}"
                case "$i" in
@@ -2372,17 +2383,22 @@ _git ()
        fi
 
        local completion_func="_git_${command//-/_}"
-       declare -F $completion_func >/dev/null && $completion_func && return
+       declare -f $completion_func >/dev/null && $completion_func && return
 
        local expansion=$(__git_aliased_command "$command")
        if [ -n "$expansion" ]; then
                completion_func="_git_${expansion//-/_}"
-               declare -F $completion_func >/dev/null && $completion_func
+               declare -f $completion_func >/dev/null && $completion_func
        fi
 }
 
 _gitk ()
 {
+       if [[ -n ${ZSH_VERSION-} ]]; then
+               emulate -L bash
+               setopt KSH_TYPESET
+       fi
+
        __git_has_doubledash && return
 
        local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -2417,3 +2433,29 @@ if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
 complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
        || complete -o default -o nospace -F _git git.exe
 fi
+
+if [[ -n ${ZSH_VERSION-} ]]; then
+       shopt () {
+               local option
+               if [ $# -ne 2 ]; then
+                       echo "USAGE: $0 (-q|-s|-u) <option>" >&2
+                       return 1
+               fi
+               case "$2" in
+               nullglob)
+                       option="$2"
+                       ;;
+               *)
+                       echo "$0: invalid option: $2" >&2
+                       return 1
+               esac
+               case "$1" in
+               -q)     setopt | grep -q "$option" ;;
+               -u)     unsetopt "$option" ;;
+               -s)     setopt "$option" ;;
+               *)
+                       echo "$0: invalid flag: $1" >&2
+                       return 1
+               esac
+       }
+fi
index 4576c4a862c8ea0565a67eccdae6ef1ac4d9af9a..b09ff8f12f7e5b5b6faeaf857d7c61973de8590e 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
 # This tool is copyright (c) 2005, Matthias Urlichs.
 # It is released under the Gnu Public License, version 2.
@@ -289,7 +289,7 @@ my $current_rev = $opt_s || 1;
 unless(-d $git_dir) {
        system("git init");
        die "Cannot init the GIT db at $git_tree: $?\n" if $?;
-       system("git read-tree");
+       system("git read-tree --empty");
        die "Cannot init an empty tree: $?\n" if $?;
 
        $last_branch = $opt_o;
index c1ea643ace920e83a5577948553d4c9d1046abec..04ce7e3b020f489d59fe103970fc989d7b351127 100755 (executable)
@@ -706,7 +706,9 @@ class P4Submit(Command):
             submitTemplate = self.prepareLogMessage(template, logMessage)
             if os.environ.has_key("P4DIFF"):
                 del(os.environ["P4DIFF"])
-            diff = p4_read_pipe("diff -du ...")
+            diff = ""
+            for editedFile in editedFiles:
+                diff += p4_read_pipe("diff -du %r" % editedFile)
 
             newdiff = ""
             for newFile in filesToAdd:
index 3a5da4ab00f2e29c3244611f40d776293e796e7d..7f3afa5ac4a4ca979a4e5dd63ebc59344f20857b 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # Copyright 2008-2009 Peter Krefting <peter@softwolves.pp.se>
 #
@@ -140,6 +140,7 @@ by whitespace or other characters.
 
 # Globals
 use strict;
+use warnings;
 use integer;
 my $crlfmode = 0;
 my @revs;
diff --git a/contrib/git-shell-commands/README b/contrib/git-shell-commands/README
new file mode 100644 (file)
index 0000000..438463b
--- /dev/null
@@ -0,0 +1,18 @@
+Sample programs callable through git-shell.  Place a directory named
+'git-shell-commands' in the home directory of a user whose shell is
+git-shell.  Then anyone logging in as that user will be able to run
+executables in the 'git-shell-commands' directory.
+
+Provided commands:
+
+help: Prints out the names of available commands.  When run
+interactively, git-shell will automatically run 'help' on startup,
+provided it exists.
+
+list: Displays any bare repository whose name ends with ".git" under
+user's home directory.  No other git repositories are visible,
+although they might be clonable through git-shell.  'list' is designed
+to minimize the number of calls to git that must be made in finding
+available repositories; if your setup has additional repositories that
+should be user-discoverable, you may wish to modify 'list'
+accordingly.
diff --git a/contrib/git-shell-commands/help b/contrib/git-shell-commands/help
new file mode 100755 (executable)
index 0000000..535770c
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+if tty -s
+then
+       echo "Run 'help' for help, or 'exit' to leave.  Available commands:"
+else
+       echo "Run 'help' for help.  Available commands:"
+fi
+
+cd "$(dirname "$0")"
+
+for cmd in *
+do
+       case "$cmd" in
+       help) ;;
+       *) [ -f "$cmd" ] && [ -x "$cmd" ] && echo "$cmd" ;;
+       esac
+done
diff --git a/contrib/git-shell-commands/list b/contrib/git-shell-commands/list
new file mode 100755 (executable)
index 0000000..6f89938
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+print_if_bare_repo='
+       if "$(git --git-dir="$1" rev-parse --is-bare-repository)" = true
+       then
+               printf "%s\n" "${1#./}"
+       fi
+'
+
+find -type d -name "*.git" -exec sh -c "$print_if_bare_repo" -- \{} \; -prune 2>/dev/null
index 00850864371e16d1fa572b0d0e5832b8a88d8666..85724bfc08f61d3b928460a8d2ad5ec03cf80b52 100755 (executable)
 # ---------------------------- Functions
 
 #
-# Top level email generation function.  This decides what type of update
-# this is and calls the appropriate body-generation routine after outputting
-# the common header
+# Function to prepare for email generation. This decides what type
+# of update this is and whether an email should even be generated.
 #
-# Note this function doesn't actually generate any email output, that is
-# taken care of by the functions it calls:
-#  - generate_email_header
-#  - generate_create_XXXX_email
-#  - generate_update_XXXX_email
-#  - generate_delete_XXXX_email
-#  - generate_email_footer
-#
-generate_email()
+prep_for_email()
 {
        # --- Arguments
        oldrev=$(git rev-parse $1)
@@ -159,7 +150,7 @@ generate_email()
                        # Anything else (is there anything else?)
                        echo >&2 "*** Unknown type of update to $refname ($rev_type)"
                        echo >&2 "***  - no email generated"
-                       exit 1
+                       return 0
                        ;;
        esac
 
@@ -175,9 +166,32 @@ generate_email()
                esac
                echo >&2 "*** $config_name is not set so no email will be sent"
                echo >&2 "*** for $refname update $oldrev->$newrev"
-               exit 0
+               return 0
        fi
 
+       return 1
+}
+
+#
+# Top level email generation function.  This calls the appropriate
+# body-generation routine after outputting the common header.
+#
+# Note this function doesn't actually generate any email output, that is
+# taken care of by the functions it calls:
+#  - generate_email_header
+#  - generate_create_XXXX_email
+#  - generate_update_XXXX_email
+#  - generate_delete_XXXX_email
+#  - generate_email_footer
+#
+# Note also that this function cannot 'exit' from the script; when this
+# function is running (in hook script mode), the send_mail() function
+# is already executing in another process, connected via a pipe, and
+# if this function exits without, whatever has been generated to that
+# point will be sent as an email... even if nothing has been generated.
+#
+generate_email()
+{
        # Email parameters
        # The email subject will contain the best description of the ref
        # that we can build from the parameters
@@ -717,10 +731,11 @@ if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
        # Output to the terminal in command line mode - if someone wanted to
        # resend an email; they could redirect the output to sendmail
        # themselves
-       PAGER= generate_email $2 $3 $1
+       prep_for_email $2 $3 $1 && PAGER= generate_email
 else
        while read oldrev newrev refname
        do
-               generate_email $oldrev $newrev $refname $maxlines | send_mail
+               prep_for_email $oldrev $newrev $refname || continue
+               generate_email $maxlines | send_mail
        done
 fi
index 9326d3a1fa05f4878a21b58a623d07428f7230fe..7ccd097e1d1234d06316e8b7f271e86127750f85 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -3,6 +3,7 @@
 #include "exec_cmd.h"
 #include "run-command.h"
 #include "strbuf.h"
+#include "string-list.h"
 
 #include <syslog.h>
 
@@ -734,11 +735,17 @@ static int set_reuse_addr(int sockfd)
                          &on, sizeof(on));
 }
 
+struct socketlist {
+       int *list;
+       size_t nr;
+       size_t alloc;
+};
+
 #ifndef NO_IPV6
 
-static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
+static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
 {
-       int socknum = 0, *socklist = NULL;
+       int socknum = 0;
        int maxfd = -1;
        char pbuf[NI_MAXSERV];
        struct addrinfo hints, *ai0, *ai;
@@ -753,8 +760,10 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
        hints.ai_flags = AI_PASSIVE;
 
        gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
-       if (gai)
-               die("getaddrinfo() failed: %s", gai_strerror(gai));
+       if (gai) {
+               logerror("getaddrinfo() for %s failed: %s", listen_addr, gai_strerror(gai));
+               return 0;
+       }
 
        for (ai = ai0; ai; ai = ai->ai_next) {
                int sockfd;
@@ -795,8 +804,9 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
                if (flags >= 0)
                        fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
 
-               socklist = xrealloc(socklist, sizeof(int) * (socknum + 1));
-               socklist[socknum++] = sockfd;
+               ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
+               socklist->list[socklist->nr++] = sockfd;
+               socknum++;
 
                if (maxfd < sockfd)
                        maxfd = sockfd;
@@ -804,13 +814,12 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
 
        freeaddrinfo(ai0);
 
-       *socklist_p = socklist;
        return socknum;
 }
 
 #else /* NO_IPV6 */
 
-static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
+static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
 {
        struct sockaddr_in sin;
        int sockfd;
@@ -851,22 +860,39 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
        if (flags >= 0)
                fcntl(sockfd, F_SETFD, flags | FD_CLOEXEC);
 
-       *socklist_p = xmalloc(sizeof(int));
-       **socklist_p = sockfd;
+       ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
+       socklist->list[socklist->nr++] = sockfd;
        return 1;
 }
 
 #endif
 
-static int service_loop(int socknum, int *socklist)
+static void socksetup(struct string_list *listen_addr, int listen_port, struct socketlist *socklist)
+{
+       if (!listen_addr->nr)
+               setup_named_sock(NULL, listen_port, socklist);
+       else {
+               int i, socknum;
+               for (i = 0; i < listen_addr->nr; i++) {
+                       socknum = setup_named_sock(listen_addr->items[i].string,
+                                                  listen_port, socklist);
+
+                       if (socknum == 0)
+                               logerror("unable to allocate any listen sockets for host %s on port %u",
+                                        listen_addr->items[i].string, listen_port);
+               }
+       }
+}
+
+static int service_loop(struct socketlist *socklist)
 {
        struct pollfd *pfd;
        int i;
 
-       pfd = xcalloc(socknum, sizeof(struct pollfd));
+       pfd = xcalloc(socklist->nr, sizeof(struct pollfd));
 
-       for (i = 0; i < socknum; i++) {
-               pfd[i].fd = socklist[i];
+       for (i = 0; i < socklist->nr; i++) {
+               pfd[i].fd = socklist->list[i];
                pfd[i].events = POLLIN;
        }
 
@@ -877,7 +903,7 @@ static int service_loop(int socknum, int *socklist)
 
                check_dead_children();
 
-               if (poll(pfd, socknum, -1) < 0) {
+               if (poll(pfd, socklist->nr, -1) < 0) {
                        if (errno != EINTR) {
                                logerror("Poll failed, resuming: %s",
                                      strerror(errno));
@@ -886,7 +912,7 @@ static int service_loop(int socknum, int *socklist)
                        continue;
                }
 
-               for (i = 0; i < socknum; i++) {
+               for (i = 0; i < socklist->nr; i++) {
                        if (pfd[i].revents & POLLIN) {
                                struct sockaddr_storage ss;
                                unsigned int sslen = sizeof(ss);
@@ -946,27 +972,27 @@ static void store_pid(const char *path)
                die_errno("failed to write pid file '%s'", path);
 }
 
-static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
+static int serve(struct string_list *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
 {
-       int socknum, *socklist;
+       struct socketlist socklist = { NULL, 0, 0 };
 
-       socknum = socksetup(listen_addr, listen_port, &socklist);
-       if (socknum == 0)
-               die("unable to allocate any listen sockets on host %s port %u",
-                   listen_addr, listen_port);
+       socksetup(listen_addr, listen_port, &socklist);
+       if (socklist.nr == 0)
+               die("unable to allocate any listen sockets on port %u",
+                   listen_port);
 
        if (pass && gid &&
            (initgroups(pass->pw_name, gid) || setgid (gid) ||
             setuid(pass->pw_uid)))
                die("cannot drop privileges");
 
-       return service_loop(socknum, socklist);
+       return service_loop(&socklist);
 }
 
 int main(int argc, char **argv)
 {
        int listen_port = 0;
-       char *listen_addr = NULL;
+       struct string_list listen_addr = STRING_LIST_INIT_NODUP;
        int inetd_mode = 0;
        const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
        int detach = 0;
@@ -981,7 +1007,7 @@ int main(int argc, char **argv)
                char *arg = argv[i];
 
                if (!prefixcmp(arg, "--listen=")) {
-                       listen_addr = xstrdup_tolower(arg + 9);
+                       string_list_append(&listen_addr, xstrdup_tolower(arg + 9));
                        continue;
                }
                if (!prefixcmp(arg, "--port=")) {
@@ -1106,7 +1132,7 @@ int main(int argc, char **argv)
        if (inetd_mode && (group_name || user_name))
                die("--user and --group are incompatible with --inetd");
 
-       if (inetd_mode && (listen_port || listen_addr))
+       if (inetd_mode && (listen_port || (listen_addr.nr > 0)))
                die("--listen= and --port= are incompatible with --inetd");
        else if (listen_port == 0)
                listen_port = DEFAULT_GIT_PORT;
@@ -1161,5 +1187,5 @@ int main(int argc, char **argv)
        if (pid_file)
                store_pid(pid_file);
 
-       return serve(listen_addr, listen_port, pass, gid);
+       return serve(&listen_addr, listen_port, pass, gid);
 }
diff --git a/diff.c b/diff.c
index 340d878f02b0daa3b26cf5eb3bf71bdda1437ce2..d1c6b91982ccbb6489c57c9bbddb692c7b4b7ca3 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -3140,16 +3140,19 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                return stat_opt(options, av);
 
        /* renames options */
-       else if (!prefixcmp(arg, "-B")) {
+       else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") ||
+                !strcmp(arg, "--break-rewrites")) {
                if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
                        return -1;
        }
-       else if (!prefixcmp(arg, "-M")) {
+       else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--detect-renames=") ||
+                !strcmp(arg, "--detect-renames")) {
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return -1;
                options->detect_rename = DIFF_DETECT_RENAME;
        }
-       else if (!prefixcmp(arg, "-C")) {
+       else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--detect-copies=") ||
+                !strcmp(arg, "--detect-copies")) {
                if (options->detect_rename == DIFF_DETECT_COPY)
                        DIFF_OPT_SET(options, FIND_COPIES_HARDER);
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
@@ -3271,12 +3274,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        else if ((argcount = short_opt('S', av, &optarg))) {
                options->pickaxe = optarg;
+               options->pickaxe_opts |= DIFF_PICKAXE_KIND_S;
+               return argcount;
+       } else if ((argcount = short_opt('G', av, &optarg))) {
+               options->pickaxe = optarg;
+               options->pickaxe_opts |= DIFF_PICKAXE_KIND_G;
                return argcount;
        }
        else if (!strcmp(arg, "--pickaxe-all"))
-               options->pickaxe_opts = DIFF_PICKAXE_ALL;
+               options->pickaxe_opts |= DIFF_PICKAXE_ALL;
        else if (!strcmp(arg, "--pickaxe-regex"))
-               options->pickaxe_opts = DIFF_PICKAXE_REGEX;
+               options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
        else if ((argcount = short_opt('O', av, &optarg))) {
                options->orderfile = optarg;
                return argcount;
@@ -3318,7 +3326,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        return 1;
 }
 
-static int parse_num(const char **cp_p)
+int parse_rename_score(const char **cp_p)
 {
        unsigned long num, scale;
        int ch, dot;
@@ -3361,10 +3369,26 @@ static int diff_scoreopt_parse(const char *opt)
        if (*opt++ != '-')
                return -1;
        cmd = *opt++;
+       if (cmd == '-') {
+               /* convert the long-form arguments into short-form versions */
+               if (!prefixcmp(opt, "break-rewrites")) {
+                       opt += strlen("break-rewrites");
+                       if (*opt == 0 || *opt++ == '=')
+                               cmd = 'B';
+               } else if (!prefixcmp(opt, "detect-copies")) {
+                       opt += strlen("detect-copies");
+                       if (*opt == 0 || *opt++ == '=')
+                               cmd = 'C';
+               } else if (!prefixcmp(opt, "detect-renames")) {
+                       opt += strlen("detect-renames");
+                       if (*opt == 0 || *opt++ == '=')
+                               cmd = 'M';
+               }
+       }
        if (cmd != 'M' && cmd != 'C' && cmd != 'B')
                return -1; /* that is not a -M, -C nor -B option */
 
-       opt1 = parse_num(&opt);
+       opt1 = parse_rename_score(&opt);
        if (cmd != 'B')
                opt2 = 0;
        else {
@@ -3374,7 +3398,7 @@ static int diff_scoreopt_parse(const char *opt)
                        return -1; /* we expect -B80/99 or -B80 */
                else {
                        opt++;
-                       opt2 = parse_num(&opt);
+                       opt2 = parse_rename_score(&opt);
                }
        }
        if (*opt != 0)
@@ -4176,7 +4200,7 @@ void diffcore_std(struct diff_options *options)
                        diffcore_merge_broken();
        }
        if (options->pickaxe)
-               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+               diffcore_pickaxe(options);
        if (options->orderfile)
                diffcore_order(options->orderfile);
        if (!options->found_follow)
diff --git a/diff.h b/diff.h
index bf2f44d840735684c89f654ee6f3b3a935af2c55..0083d92438916a8188656df140ba70d6acc8c6f6 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -238,6 +238,9 @@ extern int diff_setup_done(struct diff_options *);
 #define DIFF_PICKAXE_ALL       1
 #define DIFF_PICKAXE_REGEX     2
 
+#define DIFF_PICKAXE_KIND_S    4 /* traditional plumbing counter */
+#define DIFF_PICKAXE_KIND_G    8 /* grep in the patch */
+
 extern void diffcore_std(struct diff_options *);
 extern void diffcore_fix_diff_index(struct diff_options *);
 
@@ -312,4 +315,6 @@ extern size_t fill_textconv(struct userdiff_driver *driver,
 
 extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
 
+extern int parse_rename_score(const char **cp_p);
+
 #endif /* DIFF_H */
index 9c6544daacb6d0c7aeb2cc188d089ee5aeb06c5d..ea03b9107eb2e85dd4a4c5ad93ac08dfe7e1c9f0 100644 (file)
@@ -1,9 +1,148 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
+ * Copyright (C) 2010 Google Inc.
  */
 #include "cache.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "xdiff-interface.h"
+
+struct diffgrep_cb {
+       regex_t *regexp;
+       int hit;
+};
+
+static void diffgrep_consume(void *priv, char *line, unsigned long len)
+{
+       struct diffgrep_cb *data = priv;
+       regmatch_t regmatch;
+       int hold;
+
+       if (line[0] != '+' && line[0] != '-')
+               return;
+       if (data->hit)
+               /*
+                * NEEDSWORK: we should have a way to terminate the
+                * caller early.
+                */
+               return;
+       /* Yuck -- line ought to be "const char *"! */
+       hold = line[len];
+       line[len] = '\0';
+       data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
+       line[len] = hold;
+}
+
+static void fill_one(struct diff_filespec *one,
+                    mmfile_t *mf, struct userdiff_driver **textconv)
+{
+       if (DIFF_FILE_VALID(one)) {
+               *textconv = get_textconv(one);
+               mf->size = fill_textconv(*textconv, one, &mf->ptr);
+       } else {
+               memset(mf, 0, sizeof(*mf));
+       }
+}
+
+static int diff_grep(struct diff_filepair *p, regex_t *regexp, struct diff_options *o)
+{
+       regmatch_t regmatch;
+       struct userdiff_driver *textconv_one = NULL;
+       struct userdiff_driver *textconv_two = NULL;
+       mmfile_t mf1, mf2;
+       int hit;
+
+       if (diff_unmodified_pair(p))
+               return 0;
+
+       fill_one(p->one, &mf1, &textconv_one);
+       fill_one(p->two, &mf2, &textconv_two);
+
+       if (!mf1.ptr) {
+               if (!mf2.ptr)
+                       return 0; /* ignore unmerged */
+               /* created "two" -- does it have what we are looking for? */
+               hit = !regexec(regexp, p->two->data, 1, &regmatch, 0);
+       } else if (!mf2.ptr) {
+               /* removed "one" -- did it have what we are looking for? */
+               hit = !regexec(regexp, p->one->data, 1, &regmatch, 0);
+       } else {
+               /*
+                * We have both sides; need to run textual diff and see if
+                * the pattern appears on added/deleted lines.
+                */
+               struct diffgrep_cb ecbdata;
+               xpparam_t xpp;
+               xdemitconf_t xecfg;
+
+               memset(&xpp, 0, sizeof(xpp));
+               memset(&xecfg, 0, sizeof(xecfg));
+               ecbdata.regexp = regexp;
+               ecbdata.hit = 0;
+               xecfg.ctxlen = o->context;
+               xecfg.interhunkctxlen = o->interhunkcontext;
+               xdi_diff_outf(&mf1, &mf2, diffgrep_consume, &ecbdata,
+                             &xpp, &xecfg);
+               hit = ecbdata.hit;
+       }
+       if (textconv_one)
+               free(mf1.ptr);
+       if (textconv_two)
+               free(mf2.ptr);
+       return hit;
+}
+
+static void diffcore_pickaxe_grep(struct diff_options *o)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
+       int i, has_changes, err;
+       regex_t regex;
+       struct diff_queue_struct outq;
+       outq.queue = NULL;
+       outq.nr = outq.alloc = 0;
+
+       err = regcomp(&regex, o->pickaxe, REG_EXTENDED | REG_NEWLINE);
+       if (err) {
+               char errbuf[1024];
+               regerror(err, &regex, errbuf, 1024);
+               regfree(&regex);
+               die("invalid log-grep regex: %s", errbuf);
+       }
+
+       if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
+               /* Showing the whole changeset if needle exists */
+               for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (diff_grep(p, &regex, o))
+                               has_changes++;
+               }
+               if (has_changes)
+                       return; /* do not munge the queue */
+
+               /*
+                * Otherwise we will clear the whole queue by copying
+                * the empty outq at the end of this function, but
+                * first clear the current entries in the queue.
+                */
+               for (i = 0; i < q->nr; i++)
+                       diff_free_filepair(q->queue[i]);
+       } else {
+               /* Showing only the filepairs that has the needle */
+               for (i = 0; i < q->nr; i++) {
+                       struct diff_filepair *p = q->queue[i];
+                       if (diff_grep(p, &regex, o))
+                               diff_q(&outq, p);
+                       else
+                               diff_free_filepair(p);
+               }
+       }
+
+       regfree(&regex);
+
+       free(q->queue);
+       *q = outq;
+       return;
+}
 
 static unsigned int contains(struct diff_filespec *one,
                             const char *needle, unsigned long len,
@@ -48,8 +187,10 @@ static unsigned int contains(struct diff_filespec *one,
        return cnt;
 }
 
-void diffcore_pickaxe(const char *needle, int opts)
+static void diffcore_pickaxe_count(struct diff_options *o)
 {
+       const char *needle = o->pickaxe;
+       int opts = o->pickaxe_opts;
        struct diff_queue_struct *q = &diff_queued_diff;
        unsigned long len = strlen(needle);
        int i, has_changes;
@@ -135,3 +276,12 @@ void diffcore_pickaxe(const char *needle, int opts)
        *q = outq;
        return;
 }
+
+void diffcore_pickaxe(struct diff_options *o)
+{
+       /* Might want to warn when both S and G are on; I don't care... */
+       if (o->pickaxe_opts & DIFF_PICKAXE_KIND_G)
+               diffcore_pickaxe_grep(o);
+       else
+               diffcore_pickaxe_count(o);
+}
index 8b3241ad137f5934e32336cd1caf8d99ca11d1f5..b8f1fdecf4d9e5e8f8e834eb55a374f1af8352fd 100644 (file)
@@ -107,7 +107,7 @@ extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);
 extern void diffcore_break(int);
 extern void diffcore_rename(struct diff_options *);
 extern void diffcore_merge_broken(void);
-extern void diffcore_pickaxe(const char *needle, int opts);
+extern void diffcore_pickaxe(struct diff_options *);
 extern void diffcore_order(const char *orderfile);
 
 #define DIFF_DEBUG 0
index eab68d58c394a67f4805d77b0007b583e79e24b9..77549ebd6fbfbea051901d4ab474eebc8e2dbdbd 100644 (file)
@@ -1454,6 +1454,15 @@ static int tree_content_set(
                n = slash1 - p;
        else
                n = strlen(p);
+       if (!slash1 && !n) {
+               if (!S_ISDIR(mode))
+                       die("Root cannot be a non-directory");
+               hashcpy(root->versions[1].sha1, sha1);
+               if (root->tree)
+                       release_tree_content_recursive(root->tree);
+               root->tree = subtree;
+               return 1;
+       }
        if (!n)
                die("Empty path component found in input");
        if (!slash1 && !S_ISDIR(mode) && subtree)
index 27fc79347af428dd39daa5b550814cc6cf980510..77f60fa3960323e412f8d09968e63865cc639298 100755 (executable)
@@ -1,6 +1,8 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
+use 5.008;
 use strict;
+use warnings;
 use Git;
 
 binmode(STDOUT, ":raw");
index de116a29ef091a23f60603a28f1260ba60f054ac..df09b42840b7675e135605f9108894ce93e6d477 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -444,12 +444,12 @@ else
                                set x
                                first=
                        }
-                       case "$arg" in
-                       /*)
-                               set "$@" "$arg" ;;
-                       *)
-                               set "$@" "$prefix$arg" ;;
-                       esac
+                       if is_absolute_path "$arg"
+                       then
+                               set "$@" "$arg"
+                       else
+                               set "$@" "$prefix$arg"
+                       fi
                done
                shift
        fi
index 98f3ede566a6cb0c902ce84795f7de8f8afbe633..bc32f18d6d9cbf915eb7797817390adbbc111506 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # This tool is copyright (c) 2005, Martin Langhoff.
 # It is released under the Gnu Public License, version 2.
@@ -54,6 +54,7 @@ and can contain multiple, unrelated branches.
 
 =cut
 
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Std;
index 59b672213bfc36f95db089f0e13bafc1c2f2ed71..39a426e067c76e01d3f2a0d63243ec494436da46 100755 (executable)
@@ -1,6 +1,8 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
+use 5.008;
 use strict;
+use warnings;
 use Getopt::Std;
 use File::Temp qw(tempdir);
 use Data::Dumper;
index 9e03eee4586ca3b7476b56f66e9dcf6ffe3088cf..d27abfe7f32ef47ee8b613293110147ca3006575 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 
 # This tool is copyright (c) 2005, Matthias Urlichs.
 # It is released under the Gnu Public License, version 2.
@@ -13,6 +13,7 @@
 # The head revision is on branch "origin" by default.
 # You can change that with the '-o' option.
 
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Long;
@@ -611,7 +612,7 @@ my %index; # holds filenames of one index per branch
 unless (-d $git_dir) {
        system(qw(git init));
        die "Cannot init the GIT db at $git_tree: $?\n" if $?;
-       system(qw(git read-tree));
+       system(qw(git read-tree --empty));
        die "Cannot init an empty tree: $?\n" if $?;
 
        $last_branch = $opt_o;
index e9f3037df351ceed0262a8995f19903464163af8..1b8bff2cac163a6588df397168f57214c30b4784 100755 (executable)
@@ -8,13 +8,14 @@
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 #### Released under the GNU Public License, version 2.
 ####
 ####
 
+use 5.008;
 use strict;
 use warnings;
 use bytes;
@@ -2680,7 +2681,7 @@ package GITCVS::log;
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 
@@ -2847,7 +2848,7 @@ package GITCVS::updater;
 #### Copyright The Open University UK - 2006.
 ####
 #### Authors: Martyn Smith    <martyn@catalyst.net.nz>
-####          Martin Langhoff <martin@catalyst.net.nz>
+####          Martin Langhoff <martin@laptop.org>
 ####
 ####
 
index adc42de8752fd395707f1e395c61991b3146cebb..e95e4ad973f21dd104950dc6ebb28485844b36f4 100755 (executable)
@@ -10,6 +10,7 @@
 #
 # Any arguments that are unknown to this script are forwarded to 'git diff'.
 
+use 5.008;
 use strict;
 use warnings;
 use Cwd qw(abs_path);
index 4617f29c26726c2cd438f160be21a8422e66b352..d3acf0d2134b40b8ffb3d0e8c3229f71fa6db663 100755 (executable)
@@ -83,6 +83,7 @@ if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
                puts stderr "source    $name"
                uplevel 1 real__source $name
        }
+       if {[tk windowingsystem] eq "win32"} { console show }
 }
 
 ######################################################################
@@ -444,6 +445,8 @@ proc _lappend_nice {cmd_var} {
                set _nice [_which nice]
                if {[catch {exec $_nice git version}]} {
                        set _nice {}
+               } elseif {[is_Windows] && [file dirname $_nice] ne [file dirname $::_git]} {
+                       set _nice {}
                }
        }
        if {$_nice ne {}} {
@@ -673,6 +676,7 @@ bind . <Visibility> {
 if {[is_Windows]} {
        wm iconbitmap . -default $oguilib/git-gui.ico
        set ::tk::AlwaysShowSelection 1
+       bind . <Control-F2> {console show}
 
        # Spoof an X11 display for SSH
        if {![info exists env(DISPLAY)]} {
@@ -874,12 +878,19 @@ if {![regsub {^git version } $_git_version {} _git_version]} {
        exit 1
 }
 
+proc get_trimmed_version {s} {
+    set r {}
+    foreach x [split $s -._] {
+        if {[string is integer -strict $x]} {
+            lappend r $x
+        } else {
+            break
+        }
+    }
+    return [join $r .]
+}
 set _real_git_version $_git_version
-regsub -- {[\-\.]dirty$} $_git_version {} _git_version
-regsub {\.[0-9]+\.g[0-9a-f]+$} $_git_version {} _git_version
-regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
-regsub {\.GIT$} $_git_version {} _git_version
-regsub {\.[a-zA-Z]+\.?[0-9]+$} $_git_version {} _git_version
+set _git_version [get_trimmed_version $_git_version]
 
 if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
        catch {wm withdraw .}
@@ -1183,13 +1194,22 @@ if {![file isdirectory $_gitdir]} {
 # _gitdir exists, so try loading the config
 load_config 0
 apply_config
-# try to set work tree from environment, falling back to core.worktree
-if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
-       set _gitworktree [get_config core.worktree]
-       if {$_gitworktree eq ""} {
-               set _gitworktree [file dirname [file normalize $_gitdir]]
+
+# v1.7.0 introduced --show-toplevel to return the canonical work-tree
+if {[package vsatisfies $_git_version 1.7.0]} {
+       set _gitworktree [git rev-parse --show-toplevel]
+} else {
+       # try to set work tree from environment, core.worktree or use
+       # cdup to obtain a relative path to the top of the worktree. If
+       # run from the top, the ./ prefix ensures normalize expands pwd.
+       if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
+               set _gitworktree [get_config core.worktree]
+               if {$_gitworktree eq ""} {
+                       set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
+               }
        }
 }
+
 if {$_prefix ne {}} {
        if {$_gitworktree eq {}} {
                regsub -all {[^/]+/} $_prefix ../ cdup
@@ -2861,7 +2881,8 @@ proc usage {} {
        set s "usage: $::argv0 $::subcommand $::subcommand_args"
        if {[tk windowingsystem] eq "win32"} {
                wm withdraw .
-               tk_messageBox -icon info -title "Usage" -message $s
+               tk_messageBox -icon info -message $s \
+                       -title [mc "Usage"]
        } else {
                puts stderr $s
        }
@@ -2934,7 +2955,11 @@ blame {
                        if {[catch {
                                        set head [git rev-parse --verify $head]
                                } err]} {
-                               puts stderr $err
+                               if {[tk windowingsystem] eq "win32"} {
+                                       tk_messageBox -icon error -title [mc Error] -message $err
+                               } else {
+                                       puts stderr $err
+                               }
                                exit 1
                        }
                }
@@ -2973,18 +2998,19 @@ blame {
 citool -
 gui {
        if {[llength $argv] != 0} {
-               puts -nonewline stderr "usage: $argv0"
-               if {$subcommand ne {gui}
-                       && [file tail $argv0] ne "git-$subcommand"} {
-                       puts -nonewline stderr " $subcommand"
-               }
-               puts stderr {}
-               exit 1
+               usage
        }
        # fall through to setup UI for commits
 }
 default {
-       puts stderr "usage: $argv0 \[{blame|browser|citool}\]"
+       set err "usage: $argv0 \[{blame|browser|citool}\]"
+       if {[tk windowingsystem] eq "win32"} {
+               wm withdraw .
+               tk_messageBox -icon error -message $err \
+                       -title [mc "Usage"]
+       } else {
+               puts stderr $err
+       }
        exit 1
 }
 }
@@ -3286,6 +3312,7 @@ text $ui_diff -background white -foreground black \
        -xscrollcommand {.vpane.lower.diff.body.sbx set} \
        -yscrollcommand {.vpane.lower.diff.body.sby set} \
        -state disabled
+catch {$ui_diff configure -tabstyle wordprocessor}
 ${NS}::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
        -command [list $ui_diff xview]
 ${NS}::scrollbar .vpane.lower.diff.body.sby -orient vertical \
@@ -3296,8 +3323,16 @@ pack $ui_diff -side left -fill both -expand 1
 pack .vpane.lower.diff.header -side top -fill x
 pack .vpane.lower.diff.body -side bottom -fill both -expand 1
 
+foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 grey60} {
+       $ui_diff tag configure clr4$n -background $c
+       $ui_diff tag configure clri4$n -foreground $c
+       $ui_diff tag configure clr3$n -foreground $c
+       $ui_diff tag configure clri3$n -background $c
+}
+$ui_diff tag configure clr1 -font font_diffbold
+
 $ui_diff tag conf d_cr -elide true
-$ui_diff tag conf d_@ -foreground blue -font font_diffbold
+$ui_diff tag conf d_@ -font font_diffbold
 $ui_diff tag conf d_+ -foreground {#00a000}
 $ui_diff tag conf d_- -foreground red
 
index 63988773ba0d02af3dc91474b4a1669ecda27c5c..6e510ec2e39305e6d89de61ab2c925bcbc821a65 100644 (file)
@@ -53,7 +53,7 @@ constructor dialog {} {
                        return 1
                }
 
-       grid $w.rename.oldname_l $w.rename.oldname_m -sticky w  -padx {0 5}
+       grid $w.rename.oldname_l $w.rename.oldname_m -sticky we -padx {0 5}
        grid $w.rename.newname_l $w.rename.newname_t -sticky we -padx {0 5}
        grid columnconfigure $w.rename 1 -weight 1
        pack $w.rename -anchor nw -fill x -pady 5 -padx 5
index c628750276303fca620c9292463c4c59690c764f..dcf0711be0119a8f5ec42b48d97980330f7113b3 100644 (file)
@@ -294,7 +294,7 @@ proc start_show_diff {cont_info {add_opts {}}} {
        }
 
        lappend cmd -p
-       lappend cmd --no-color
+       lappend cmd --color
        if {$repo_config(gui.diffcontext) >= 1} {
                lappend cmd "-U$repo_config(gui.diffcontext)"
        }
@@ -332,6 +332,23 @@ proc start_show_diff {cont_info {add_opts {}}} {
        fileevent $fd readable [list read_diff $fd $cont_info]
 }
 
+proc parse_color_line {line} {
+       set start 0
+       set result ""
+       set markup [list]
+       set regexp {\033\[((?:\d+;)*\d+)?m}
+       while {[regexp -indices -start $start $regexp $line match code]} {
+               foreach {begin end} $match break
+               append result [string range $line $start [expr {$begin - 1}]]
+               lappend markup [string length $result] \
+                       [eval [linsert $code 0 string range $line]]
+               set start [incr end]
+       }
+       append result [string range $line $start end]
+       if {[llength $markup] < 4} {set markup {}}
+       return [list $result $markup]
+}
+
 proc read_diff {fd cont_info} {
        global ui_diff diff_active is_submodule_diff
        global is_3way_diff is_conflict_diff current_diff_header
@@ -340,6 +357,9 @@ proc read_diff {fd cont_info} {
 
        $ui_diff conf -state normal
        while {[gets $fd line] >= 0} {
+               foreach {line markup} [parse_color_line $line] break
+               set line [string map {\033 ^} $line]
+
                # -- Cleanup uninteresting diff header lines.
                #
                if {$::current_diff_inheader} {
@@ -434,11 +454,23 @@ proc read_diff {fd cont_info} {
                        }
                        }
                }
+               set mark [$ui_diff index "end - 1 line linestart"]
                $ui_diff insert end $line $tags
                if {[string index $line end] eq "\r"} {
                        $ui_diff tag add d_cr {end - 2c}
                }
                $ui_diff insert end "\n" $tags
+
+               foreach {posbegin colbegin posend colend} $markup {
+                       set prefix clr
+                       foreach style [split $colbegin ";"] {
+                               if {$style eq "7"} {append prefix i; continue}
+                               if {$style < 30 || $style > 47} {continue}
+                               set a "$mark linestart + $posbegin chars"
+                               set b "$mark linestart + $posend chars"
+                               catch {$ui_diff tag add $prefix$style $a $b}
+                       }
+               }
        }
        $ui_diff conf -state disabled
 
index 615753c83c38f4a97752d5ca3400beeef4acd5f1..8643f74cb09f278c37851c418e839c7d160f36ca 100755 (executable)
@@ -61,6 +61,11 @@ do
        esac
 
        eval pretty_name=\${GITHEAD_$SHA1:-$SHA1}
+       if test "$SHA1" = "$pretty_name"
+       then
+               SHA1_UP="$(echo "$SHA1" | tr a-z A-Z)"
+               eval pretty_name=\${GITHEAD_$SHA1_UP:-$pretty_name}
+       fi
        common=$(git merge-base --all $SHA1 $MRC) ||
                die "Unable to find common commit with $pretty_name"
 
index b5e1943b1d166b543998c80579c65e82de36516a..77d4aee20ee5b8b01f9a0e3d1f8dbc9f7fdf3eb9 100644 (file)
@@ -10,10 +10,10 @@ merge_mode() {
 
 translate_merge_tool_path () {
        case "$1" in
-       vimdiff)
+       vimdiff|vimdiff2)
                echo vim
                ;;
-       gvimdiff)
+       gvimdiff|gvimdiff2)
                echo gvim
                ;;
        emerge)
@@ -47,7 +47,8 @@ check_unchanged () {
 valid_tool () {
        case "$1" in
        kdiff3 | tkdiff | xxdiff | meld | opendiff | \
-       emerge | vimdiff | gvimdiff | ecmerge | diffuse | araxis | p4merge)
+       vimdiff | gvimdiff | vimdiff2 | gvimdiff2 | \
+       emerge | ecmerge | diffuse | araxis | p4merge)
                ;; # happy
        tortoisemerge)
                if ! merge_mode; then
@@ -169,25 +170,30 @@ run_merge_tool () {
                        "$merge_tool_path" "$LOCAL" "$REMOTE" | cat
                fi
                ;;
-       vimdiff)
+       vimdiff|gvimdiff)
                if merge_mode; then
                        touch "$BACKUP"
-                       "$merge_tool_path" -d -c "wincmd l" \
-                               "$LOCAL" "$MERGED" "$REMOTE"
+                       if $base_present; then
+                               "$merge_tool_path" -f -d -c "wincmd J" \
+                                       "$MERGED" "$LOCAL" "$BASE" "$REMOTE"
+                       else
+                               "$merge_tool_path" -f -d -c "wincmd l" \
+                                       "$LOCAL" "$MERGED" "$REMOTE"
+                       fi
                        check_unchanged
                else
-                       "$merge_tool_path" -d -c "wincmd l" \
+                       "$merge_tool_path" -f -d -c "wincmd l" \
                                "$LOCAL" "$REMOTE"
                fi
                ;;
-       gvimdiff)
+       vimdiff2|gvimdiff2)
                if merge_mode; then
                        touch "$BACKUP"
-                       "$merge_tool_path" -d -c "wincmd l" -f \
+                       "$merge_tool_path" -f -d -c "wincmd l" \
                                "$LOCAL" "$MERGED" "$REMOTE"
                        check_unchanged
                else
-                       "$merge_tool_path" -d -c "wincmd l" -f \
+                       "$merge_tool_path" -f -d -c "wincmd l" \
                                "$LOCAL" "$REMOTE"
                fi
                ;;
index c2a0ef8d5a2794a45908cbcfff1b419ebfc17f8e..e136732cea80c1594ac02c93cf246e8c77ff89b8 100755 (executable)
@@ -6,7 +6,7 @@
 #
 # Scan two git object-trees, and hardlink any common objects between them.
 
-use 5.006;
+use 5.008;
 use strict;
 use warnings;
 use Getopt::Long;
index 1eb3bca352f38ec3807383cc5fff7f7e62731b2b..769baaf7e11277db47f4e897aea82c1581201c6b 100755 (executable)
@@ -10,7 +10,8 @@ git repack [options]
 a               pack everything in a single pack
 A               same as -a, and turn unreachable objects loose
 d               remove redundant packs, and run git-prune-packed
-f               pass --no-reuse-object to git-pack-objects
+f               pass --no-reuse-delta to git-pack-objects
+F               pass --no-reuse-object to git-pack-objects
 n               do not run git-update-server-info
 q,quiet         be quiet
 l               pass --local to git-pack-objects
@@ -34,7 +35,8 @@ do
                unpack_unreachable=--unpack-unreachable ;;
        -d)     remove_redundant=t ;;
        -q)     GIT_QUIET=t ;;
-       -f)     no_reuse=--no-reuse-object ;;
+       -f)     no_reuse=--no-reuse-delta ;;
+       -F)     no_reuse=--no-reuse-object ;;
        -l)     local=--local ;;
        --max-pack-size|--window|--window-memory|--depth)
                extra="$extra $1=$2"; shift ;;
index e1f29a72a10ff68c972b439749535d251bfd311c..f68ed5a5d3208eb0669d7dc1289f40c567e077c7 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # Copyright 2002,2005 Greg Kroah-Hartman <greg@kroah.com>
 # Copyright 2005 Ryan Anderson <ryan@michonline.com>
@@ -16,6 +16,7 @@
 #    and second line is the subject of the message.
 #
 
+use 5.008;
 use strict;
 use warnings;
 use Term::ReadLine;
@@ -24,6 +25,7 @@ use Text::ParseWords;
 use Data::Dumper;
 use Term::ANSIColor;
 use File::Temp qw/ tempdir tempfile /;
+use File::Spec::Functions qw(catfile);
 use Error qw(:try);
 use Git;
 
@@ -60,6 +62,7 @@ git send-email [options] <file | directory | rev-list options >
     --envelope-sender       <str>  * Email envelope sender.
     --smtp-server       <str:int>  * Outgoing SMTP server to use. The port
                                      is optional. Default 'localhost'.
+    --smtp-server-option    <str>  * Outgoing SMTP server option to use.
     --smtp-server-port      <int>  * Outgoing SMTP server port.
     --smtp-user             <str>  * Username for SMTP-AUTH.
     --smtp-pass             <str>  * Password for SMTP-AUTH; not necessary.
@@ -70,6 +73,7 @@ git send-email [options] <file | directory | rev-list options >
 
   Automating:
     --identity              <str>  * Use the sendemail.<id> options.
+    --to-cmd                <str>  * Email To: via `<str> \$patch_path`
     --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
     --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
     --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
@@ -85,6 +89,7 @@ git send-email [options] <file | directory | rev-list options >
     --[no-]validate                * Perform patch sanity checks. Default on.
     --[no-]format-patch            * understand any non optional arguments as
                                      `git format-patch` ones.
+    --force                        * Send even if safety checks would prevent it.
 
 EOT
        exit(1);
@@ -134,11 +139,8 @@ my $have_mail_address = eval { require Mail::Address; 1 };
 my $smtp;
 my $auth;
 
-sub unique_email_list(@);
-sub cleanup_compose_files();
-
 # Variables we fill in automatically, or via prompting:
-my (@to,$no_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
+my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
        $initial_reply_to,$initial_subject,@files,
        $author,$sender,$smtp_authpass,$annotate,$compose,$time);
 
@@ -162,6 +164,7 @@ if ($@) {
 my ($quiet, $dry_run) = (0, 0);
 my $format_patch;
 my $compose_filename;
+my $force = 0;
 
 # Handle interactive edition of files.
 my $multiedit;
@@ -187,9 +190,11 @@ sub do_edit {
 }
 
 # Variables with corresponding config settings
-my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
-my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
-my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts, $smtp_domain);
+my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
+my ($to_cmd, $cc_cmd);
+my ($smtp_server, $smtp_server_port, @smtp_server_options);
+my ($smtp_authuser, $smtp_encryption);
+my ($identity, $aliasfiletype, @alias_files, $smtp_domain);
 my ($validate, $confirm);
 my (@suppress_cc);
 my ($auto_8bit_encoding);
@@ -210,10 +215,12 @@ my %config_bool_settings = (
 my %config_settings = (
     "smtpserver" => \$smtp_server,
     "smtpserverport" => \$smtp_server_port,
+    "smtpserveroption" => \@smtp_server_options,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
-       "smtpdomain" => \$smtp_domain,
-    "to" => \@to,
+    "smtpdomain" => \$smtp_domain,
+    "to" => \@initial_to,
+    "tocmd" => \$to_cmd,
     "cc" => \@initial_cc,
     "cccmd" => \$cc_cmd,
     "aliasfiletype" => \$aliasfiletype,
@@ -271,7 +278,8 @@ $SIG{INT}  = \&signal_handler;
 my $rc = GetOptions("sender|from=s" => \$sender,
                     "in-reply-to=s" => \$initial_reply_to,
                    "subject=s" => \$initial_subject,
-                   "to=s" => \@to,
+                   "to=s" => \@initial_to,
+                   "to-cmd=s" => \$to_cmd,
                    "no-to" => \$no_to,
                    "cc=s" => \@initial_cc,
                    "no-cc" => \$no_cc,
@@ -279,6 +287,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                    "no-bcc" => \$no_bcc,
                    "chain-reply-to!" => \$chain_reply_to,
                    "smtp-server=s" => \$smtp_server,
+                   "smtp-server-option=s" => \@smtp_server_options,
                    "smtp-server-port=s" => \$smtp_server_port,
                    "smtp-user=s" => \$smtp_authuser,
                    "smtp-pass:s" => \$smtp_authpass,
@@ -301,6 +310,7 @@ my $rc = GetOptions("sender|from=s" => \$sender,
                    "validate!" => \$validate,
                    "format-patch!" => \$format_patch,
                    "8bit-encoding=s" => \$auto_8bit_encoding,
+                   "force" => \$force,
         );
 
 unless ($rc) {
@@ -364,7 +374,7 @@ my(%suppress_cc);
 if (@suppress_cc) {
        foreach my $entry (@suppress_cc) {
                die "Unknown --suppress-cc field: '$entry'\n"
-                       unless $entry =~ /^(all|cccmd|cc|author|self|sob|body|bodycc)$/;
+                       unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
                $suppress_cc{$entry} = 1;
        }
 }
@@ -409,7 +419,7 @@ my ($repoauthor, $repocommitter);
 
 # Verify the user input
 
-foreach my $entry (@to) {
+foreach my $entry (@initial_to) {
        die "Comma in --to entry: $entry'\n" unless $entry !~ m/,/;
 }
 
@@ -508,12 +518,12 @@ while (defined(my $f = shift @ARGV)) {
                push @rev_list_opts, "--", @ARGV;
                @ARGV = ();
        } elsif (-d $f and !check_file_rev_conflict($f)) {
-               opendir(DH,$f)
+               opendir my $dh, $f
                        or die "Failed to opendir $f: $!";
 
-               push @files, grep { -f $_ } map { +$f . "/" . $_ }
-                               sort readdir(DH);
-               closedir(DH);
+               push @files, grep { -f $_ } map { catfile($f, $_) }
+                               sort readdir $dh;
+               closedir $dh;
        } elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
                push @files, $f;
        } else {
@@ -545,7 +555,7 @@ if (@files) {
        usage();
 }
 
-sub get_patch_subject($) {
+sub get_patch_subject {
        my $fn = shift;
        open (my $fh, '<', $fn);
        while (my $line = <$fh>) {
@@ -563,7 +573,7 @@ if ($compose) {
        $compose_filename = ($repo ?
                tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
                tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
-       open(C,">",$compose_filename)
+       open my $c, ">", $compose_filename
                or die "Failed to open for writing $compose_filename: $!";
 
 
@@ -571,7 +581,7 @@ if ($compose) {
        my $tpl_subject = $initial_subject || '';
        my $tpl_reply_to = $initial_reply_to || '';
 
-       print C <<EOT;
+       print $c <<EOT;
 From $tpl_sender # This line is ignored.
 GIT: Lines beginning in "GIT:" will be removed.
 GIT: Consider including an overall diffstat or table of contents
@@ -584,9 +594,9 @@ In-Reply-To: $tpl_reply_to
 
 EOT
        for my $f (@files) {
-               print C get_patch_subject($f);
+               print $c get_patch_subject($f);
        }
-       close(C);
+       close $c;
 
        if ($annotate) {
                do_edit($compose_filename, @files);
@@ -594,23 +604,23 @@ EOT
                do_edit($compose_filename);
        }
 
-       open(C2,">",$compose_filename . ".final")
+       open my $c2, ">", $compose_filename . ".final"
                or die "Failed to open $compose_filename.final : " . $!;
 
-       open(C,"<",$compose_filename)
+       open $c, "<", $compose_filename
                or die "Failed to open $compose_filename : " . $!;
 
        my $need_8bit_cte = file_has_nonascii($compose_filename);
        my $in_body = 0;
        my $summary_empty = 1;
-       while(<C>) {
+       while(<$c>) {
                next if m/^GIT:/;
                if ($in_body) {
                        $summary_empty = 0 unless (/^\n$/);
                } elsif (/^\n$/) {
                        $in_body = 1;
                        if ($need_8bit_cte) {
-                               print C2 "MIME-Version: 1.0\n",
+                               print $c2 "MIME-Version: 1.0\n",
                                         "Content-Type: text/plain; ",
                                           "charset=UTF-8\n",
                                         "Content-Transfer-Encoding: 8bit\n";
@@ -635,10 +645,10 @@ EOT
                        print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
                        next;
                }
-               print C2 $_;
+               print $c2 $_;
        }
-       close(C);
-       close(C2);
+       close $c;
+       close $c2;
 
        if ($summary_empty) {
                print "Summary email is empty, skipping it\n";
@@ -675,7 +685,7 @@ sub ask {
 
 my %broken_encoding;
 
-sub file_declares_8bit_cte($) {
+sub file_declares_8bit_cte {
        my $fn = shift;
        open (my $fh, '<', $fn);
        while (my $line = <$fh>) {
@@ -702,6 +712,16 @@ if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
                                  default => "UTF-8");
 }
 
+if (!$force) {
+       for my $f (@files) {
+               if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) {
+                       die "Refusing to send because the patch\n\t$f\n"
+                               . "has the template subject '*** SUBJECT HERE ***'. "
+                               . "Pass --force if you really want to send.\n";
+               }
+       }
+}
+
 my $prompting = 0;
 if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
@@ -711,9 +731,9 @@ if (!defined $sender) {
        $prompting++;
 }
 
-if (!@to) {
+if (!@initial_to && !defined $to_cmd) {
        my $to = ask("Who should the emails be sent to? ");
-       push @to, parse_address_line($to) if defined $to; # sanitized/validated later
+       push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
        $prompting++;
 }
 
@@ -731,8 +751,8 @@ sub expand_one_alias {
        return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
 }
 
-@to = expand_aliases(@to);
-@to = (map { sanitize_address($_) } @to);
+@initial_to = expand_aliases(@initial_to);
+@initial_to = (map { sanitize_address($_) } @initial_to);
 @initial_cc = expand_aliases(@initial_cc);
 @bcclist = expand_aliases(@bcclist);
 
@@ -766,8 +786,8 @@ our ($message_id, %mail, $subject, $reply_to, $references, $message,
 
 sub extract_valid_address {
        my $address = shift;
-       my $local_part_regexp = '[^<>"\s@]+';
-       my $domain_regexp = '[^.<>"\s@]+(?:\.[^.<>"\s@]+)+';
+       my $local_part_regexp = qr/[^<>"\s@]+/;
+       my $domain_regexp = qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/;
 
        # check for a local address:
        return $address if ($address =~ /^($local_part_regexp)$/);
@@ -808,7 +828,7 @@ sub make_message_id {
                last if (defined $du_part and $du_part ne '');
        }
        if (not defined $du_part or $du_part eq '') {
-               use Sys::Hostname qw();
+               require Sys::Hostname;
                $du_part = 'user@' . Sys::Hostname::hostname();
        }
        my $message_id_template = "<%s-git-send-email-%s>";
@@ -841,8 +861,8 @@ sub quote_rfc2047 {
 
 sub is_rfc2047_quoted {
        my $s = shift;
-       my $token = '[^][()<>@,;:"\/?.= \000-\037\177-\377]+';
-       my $encoded_text = '[!->@-~]+';
+       my $token = qr/[^][()<>@,;:"\/?.= \000-\037\177-\377]+/;
+       my $encoded_text = qr/[!->@-~]+/;
        length($s) <= 75 &&
        $s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o;
 }
@@ -853,7 +873,7 @@ sub sanitize_address {
        my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
 
        if (not $recipient_name) {
-               return "$recipient";
+               return $recipient;
        }
 
        # if recipient_name is already quoted, do nothing
@@ -870,7 +890,7 @@ sub sanitize_address {
        # double quotes are needed if specials or CTLs are included
        elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
                $recipient_name =~ s/(["\\\r])/\\$1/g;
-               $recipient_name = "\"$recipient_name\"";
+               $recipient_name = qq["$recipient_name"];
        }
 
        return "$recipient_name $recipient_addr";
@@ -1015,6 +1035,8 @@ X-Mailer: git-send-email $gitversion
                }
        }
 
+       unshift (@sendmail_parameters, @smtp_server_options);
+
        if ($dry_run) {
                # We don't want to send the email.
        } elsif ($smtp_server =~ m#^/#) {
@@ -1024,7 +1046,7 @@ X-Mailer: git-send-email $gitversion
                        exec($smtp_server, @sendmail_parameters) or die $!;
                }
                print $sm "$header\n$message";
-               close $sm or die $?;
+               close $sm or die $!;
        } else {
 
                if (!defined $smtp_server) {
@@ -1130,12 +1152,13 @@ $subject = $initial_subject;
 $message_num = 0;
 
 foreach my $t (@files) {
-       open(F,"<",$t) or die "can't open file $t";
+       open my $fh, "<", $t or die "can't open file $t";
 
        my $author = undef;
        my $author_encoding;
        my $has_content_type;
        my $body_encoding;
+       @to = ();
        @cc = ();
        @xh = ();
        my $input_format = undef;
@@ -1143,7 +1166,7 @@ foreach my $t (@files) {
        $message = "";
        $message_num++;
        # First unfold multiline header fields
-       while(<F>) {
+       while(<$fh>) {
                last if /^\s*$/;
                if (/^\s+\S/ and @header) {
                        chomp($header[$#header]);
@@ -1176,6 +1199,13 @@ foreach my $t (@files) {
                                        $1, $_) unless $quiet;
                                push @cc, $1;
                        }
+                       elsif (/^To:\s+(.*)$/) {
+                               foreach my $addr (parse_address_line($1)) {
+                                       printf("(mbox) Adding to: %s from line '%s'\n",
+                                               $addr, $_) unless $quiet;
+                                       push @to, sanitize_address($addr);
+                               }
+                       }
                        elsif (/^Cc:\s+(.*)$/) {
                                foreach my $addr (parse_address_line($1)) {
                                        if (unquote_rfc2047($addr) eq $sender) {
@@ -1219,7 +1249,7 @@ foreach my $t (@files) {
                }
        }
        # Now parse the message body
-       while(<F>) {
+       while(<$fh>) {
                $message .=  $_;
                if (/^(Signed-off-by|Cc): (.*)$/i) {
                        chomp;
@@ -1236,23 +1266,12 @@ foreach my $t (@files) {
                                $c, $_) unless $quiet;
                }
        }
-       close F;
-
-       if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
-               open(F, "$cc_cmd \Q$t\E |")
-                       or die "(cc-cmd) Could not execute '$cc_cmd'";
-               while(<F>) {
-                       my $c = $_;
-                       $c =~ s/^\s*//g;
-                       $c =~ s/\n$//g;
-                       next if ($c eq $sender and $suppress_from);
-                       push @cc, $c;
-                       printf("(cc-cmd) Adding cc: %s from: '%s'\n",
-                               $c, $cc_cmd) unless $quiet;
-               }
-               close F
-                       or die "(cc-cmd) failed to close pipe to '$cc_cmd'";
-       }
+       close $fh;
+
+       push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t)
+               if defined $to_cmd;
+       push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t)
+               if defined $cc_cmd && !$suppress_cc{'cccmd'};
 
        if ($broken_encoding{$t} && !$has_content_type) {
                $has_content_type = 1;
@@ -1293,6 +1312,7 @@ foreach my $t (@files) {
                ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
        $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
 
+       @to = (@initial_to, @to);
        @cc = (@initial_cc, @cc);
 
        my $message_was_sent = send_message();
@@ -1310,15 +1330,38 @@ foreach my $t (@files) {
        $message_id = undef;
 }
 
+# Execute a command (e.g. $to_cmd) to get a list of email addresses
+# and return a results array
+sub recipients_cmd {
+       my ($prefix, $what, $cmd, $file) = @_;
+
+       my $sanitized_sender = sanitize_address($sender);
+       my @addresses = ();
+       open my $fh, "$cmd \Q$file\E |"
+           or die "($prefix) Could not execute '$cmd'";
+       while (my $address = <$fh>) {
+               $address =~ s/^\s*//g;
+               $address =~ s/\s*$//g;
+               $address = sanitize_address($address);
+               next if ($address eq $sanitized_sender and $suppress_from);
+               push @addresses, $address;
+               printf("($prefix) Adding %s: %s from: '%s'\n",
+                      $what, $address, $cmd) unless $quiet;
+               }
+       close $fh
+           or die "($prefix) failed to close pipe to '$cmd'";
+       return @addresses;
+}
+
 cleanup_compose_files();
 
-sub cleanup_compose_files() {
+sub cleanup_compose_files {
        unlink($compose_filename, $compose_filename . ".final") if $compose;
 }
 
 $smtp->quit if $smtp;
 
-sub unique_email_list(@) {
+sub unique_email_list {
        my %seen;
        my @emails;
 
index 6131670860514c215a6b9b7417dd05c3124e81b4..ae031a1375e9c674d80b9fa3d3d3b76780deaf9f 100644 (file)
@@ -151,17 +151,14 @@ get_author_ident_from_commit () {
                s/'\''/'\''\\'\'\''/g
                h
                s/^author \([^<]*\) <[^>]*> .*$/\1/
-               s/'\''/'\''\'\'\''/g
                s/.*/GIT_AUTHOR_NAME='\''&'\''/p
 
                g
                s/^author [^<]* <\([^>]*\)> .*$/\1/
-               s/'\''/'\''\'\'\''/g
                s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
 
                g
                s/^author [^<]* <[^>]*> \(.*\)$/\1/
-               s/'\''/'\''\'\'\''/g
                s/.*/GIT_AUTHOR_DATE='\''&'\''/p
 
                q
@@ -209,5 +206,20 @@ case $(uname -s) in
        find () {
                /usr/bin/find "$@"
        }
+       is_absolute_path () {
+               case "$1" in
+               [/\\]* | [A-Za-z]:*)
+                       return 0 ;;
+               esac
+               return 1
+       }
        ;;
+*)
+       is_absolute_path () {
+               case "$1" in
+               /*)
+                       return 0 ;;
+               esac
+               return 1
+       }
 esac
index 18cfb2466d11aa28a795e3dabac70e236061fbe9..757de82161e05b9d12c489efeff05c7fec341fe4 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/env perl
 # Copyright (C) 2006, Eric Wong <normalperson@yhbt.net>
 # License: GPL v2 or later
+use 5.008;
 use warnings;
 use strict;
 use vars qw/   $AUTHOR $VERSION
index 2fb7c2d77bbd5f2041341822859dce51ae504d83..e32ee76309606c82abcac483f8801968916047f2 100644 (file)
@@ -35,6 +35,7 @@ GITWEB_FAVICON = static/git-favicon.png
 GITWEB_JS = static/gitweb.js
 GITWEB_SITE_HEADER =
 GITWEB_SITE_FOOTER =
+HIGHLIGHT_BIN = highlight
 
 # include user config
 -include ../config.mak.autogen
@@ -129,7 +130,8 @@ GITWEB_REPLACE = \
        -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
        -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
        -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
-       -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g'
+       -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
+       -e 's|++HIGHLIGHT_BIN++|$(HIGHLIGHT_BIN)|g'
 
 GITWEB-BUILD-OPTIONS: FORCE
        @rm -f $@+
index d4811987965e5a5036b7ed71e32d93578579073a..bf3664f2b7b7b4b5177d172c6b99a84940244469 100644 (file)
@@ -114,6 +114,11 @@ You can specify the following configuration variables when building GIT:
    when gitweb.cgi is executed, then the file specified in the environment
    variable will be loaded instead of the file specified when gitweb.cgi was
    created.  [Default: /etc/gitweb.conf]
+ * HIGHLIGHT_BIN
+   Path to the highlight executable to use (must be the one from
+   http://www.andre-simon.de due to assumptions about parameters and output).
+   Useful if highlight is not installed on your webserver's PATH.
+   [Default: highlight]
 
 
 Runtime gitweb configuration
@@ -236,7 +241,11 @@ not include variables usually directly set during build):
    If server load exceed this value then return "503 Service Unavailable" error.
    Server load is taken to be 0 if gitweb cannot determine its value.  Set it to
    undefined value to turn it off.  The default is 300.
-
+ * $highlight_bin
+   Path to the highlight executable to use (must be the one from
+   http://www.andre-simon.de due to assumptions about parameters and output).
+   Useful if highlight is not installed on your webserver's PATH.
+   [Default: highlight]
 
 Projects list file format
 ~~~~~~~~~~~~~~~~~~~~~~~~~
index a85e2f63197649bff4842ea2970448205618c104..253f41adc9e64105bf9d1642102b20bba6bc5f20 100755 (executable)
@@ -7,6 +7,7 @@
 #
 # This program is licensed under the GPLv2
 
+use 5.008;
 use strict;
 use warnings;
 use CGI qw(:standard :escapeHTML -nosticky);
@@ -165,6 +166,12 @@ our @diff_opts = ('-M'); # taken from git_commit
 # the gitweb domain.
 our $prevent_xss = 0;
 
+# Path to the highlight executable to use (must be the one from
+# http://www.andre-simon.de due to assumptions about parameters and output).
+# Useful if highlight is not installed on your webserver's PATH.
+# [Default: highlight]
+our $highlight_bin = "++HIGHLIGHT_BIN++";
+
 # information about snapshot formats that gitweb is capable of serving
 our %known_snapshot_formats = (
        # name => {
@@ -774,10 +781,10 @@ sub evaluate_path_info {
                'history',
        );
 
-       # we want to catch
+       # we want to catch, among others
        # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
        my ($parentrefname, $parentpathname, $refname, $pathname) =
-               ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/);
+               ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?([^:]+?)?(?::(.+))?$/);
 
        # first, analyze the 'current' part
        if (defined $pathname) {
@@ -813,8 +820,15 @@ sub evaluate_path_info {
                # hash_base instead. It should also be noted that hand-crafted
                # links having 'history' as an action and no pathname or hash
                # set will fail, but that happens regardless of PATH_INFO.
-               $input_params{'action'} ||= "shortlog";
-               if (grep { $_ eq $input_params{'action'} } @wants_base) {
+               if (defined $parentrefname) {
+                       # if there is parent let the default be 'shortlog' action
+                       # (for http://git.example.com/repo.git/A..B links); if there
+                       # is no parent, dispatch will detect type of object and set
+                       # action appropriately if required (if action is not set)
+                       $input_params{'action'} ||= "shortlog";
+               }
+               if ($input_params{'action'} &&
+                   grep { $_ eq $input_params{'action'} } @wants_base) {
                        $input_params{'hash_base'} ||= $refname;
                } else {
                        $input_params{'hash'} ||= $refname;
@@ -3360,7 +3374,8 @@ sub run_highlighter {
        close $fd
                or die_error(404, "Reading blob failed");
        open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
-                 "highlight --xhtml --fragment --syntax $syntax |"
+                 quote_command($highlight_bin).
+                 " --xhtml --fragment --syntax $syntax |"
                or die_error(500, "Couldn't open file or run syntax highlighter");
        return $fd;
 }
diff --git a/grep.c b/grep.c
index 82fb349be6f6ef8211d4828101cd8e88bcbb304f..63c4280cac9a87f867451c0d9787d1fe21ea9c2d 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -189,30 +189,74 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
        return compile_pattern_or(list);
 }
 
-void compile_grep_patterns(struct grep_opt *opt)
+static struct grep_expr *grep_true_expr(void)
+{
+       struct grep_expr *z = xcalloc(1, sizeof(*z));
+       z->node = GREP_NODE_TRUE;
+       return z;
+}
+
+static struct grep_expr *grep_or_expr(struct grep_expr *left, struct grep_expr *right)
+{
+       struct grep_expr *z = xcalloc(1, sizeof(*z));
+       z->node = GREP_NODE_OR;
+       z->u.binary.left = left;
+       z->u.binary.right = right;
+       return z;
+}
+
+static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
 {
        struct grep_pat *p;
-       struct grep_expr *header_expr = NULL;
-
-       if (opt->header_list) {
-               p = opt->header_list;
-               header_expr = compile_pattern_expr(&p);
-               if (p)
-                       die("incomplete pattern expression: %s", p->pattern);
-               for (p = opt->header_list; p; p = p->next) {
-                       switch (p->token) {
-                       case GREP_PATTERN: /* atom */
-                       case GREP_PATTERN_HEAD:
-                       case GREP_PATTERN_BODY:
-                               compile_regexp(p, opt);
-                               break;
-                       default:
-                               opt->extended = 1;
-                               break;
-                       }
+       struct grep_expr *header_expr;
+       struct grep_expr *(header_group[GREP_HEADER_FIELD_MAX]);
+       enum grep_header_field fld;
+
+       if (!opt->header_list)
+               return NULL;
+       p = opt->header_list;
+       for (p = opt->header_list; p; p = p->next) {
+               if (p->token != GREP_PATTERN_HEAD)
+                       die("bug: a non-header pattern in grep header list.");
+               if (p->field < 0 || GREP_HEADER_FIELD_MAX <= p->field)
+                       die("bug: unknown header field %d", p->field);
+               compile_regexp(p, opt);
+       }
+
+       for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++)
+               header_group[fld] = NULL;
+
+       for (p = opt->header_list; p; p = p->next) {
+               struct grep_expr *h;
+               struct grep_pat *pp = p;
+
+               h = compile_pattern_atom(&pp);
+               if (!h || pp != p->next)
+                       die("bug: malformed header expr");
+               if (!header_group[p->field]) {
+                       header_group[p->field] = h;
+                       continue;
                }
+               header_group[p->field] = grep_or_expr(h, header_group[p->field]);
        }
 
+       header_expr = NULL;
+
+       for (fld = 0; fld < GREP_HEADER_FIELD_MAX; fld++) {
+               if (!header_group[fld])
+                       continue;
+               if (!header_expr)
+                       header_expr = grep_true_expr();
+               header_expr = grep_or_expr(header_group[fld], header_expr);
+       }
+       return header_expr;
+}
+
+void compile_grep_patterns(struct grep_opt *opt)
+{
+       struct grep_pat *p;
+       struct grep_expr *header_expr = prep_header_patterns(opt);
+
        for (p = opt->pattern_list; p; p = p->next) {
                switch (p->token) {
                case GREP_PATTERN: /* atom */
@@ -231,9 +275,6 @@ void compile_grep_patterns(struct grep_opt *opt)
        else if (!opt->extended)
                return;
 
-       /* Then bundle them up in an expression.
-        * A classic recursive descent parser would do.
-        */
        p = opt->pattern_list;
        if (p)
                opt->pattern_expression = compile_pattern_expr(&p);
@@ -243,22 +284,18 @@ void compile_grep_patterns(struct grep_opt *opt)
        if (!header_expr)
                return;
 
-       if (opt->pattern_expression) {
-               struct grep_expr *z;
-               z = xcalloc(1, sizeof(*z));
-               z->node = GREP_NODE_OR;
-               z->u.binary.left = opt->pattern_expression;
-               z->u.binary.right = header_expr;
-               opt->pattern_expression = z;
-       } else {
+       if (!opt->pattern_expression)
                opt->pattern_expression = header_expr;
-       }
+       else
+               opt->pattern_expression = grep_or_expr(opt->pattern_expression,
+                                                      header_expr);
        opt->all_match = 1;
 }
 
 static void free_pattern_expr(struct grep_expr *x)
 {
        switch (x->node) {
+       case GREP_NODE_TRUE:
        case GREP_NODE_ATOM:
                break;
        case GREP_NODE_NOT:
@@ -487,6 +524,9 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
        if (!x)
                die("Not a valid grep expression");
        switch (x->node) {
+       case GREP_NODE_TRUE:
+               h = 1;
+               break;
        case GREP_NODE_ATOM:
                h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
                break;
diff --git a/grep.h b/grep.h
index efa8cff980af2b2c06dad080876637d16b5c4985..06621fe663545af52fbc42827a8374ab5bd42f38 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -22,6 +22,7 @@ enum grep_header_field {
        GREP_HEADER_AUTHOR = 0,
        GREP_HEADER_COMMITTER
 };
+#define GREP_HEADER_FIELD_MAX (GREP_HEADER_COMMITTER + 1)
 
 struct grep_pat {
        struct grep_pat *next;
@@ -41,6 +42,7 @@ enum grep_expr_node {
        GREP_NODE_ATOM,
        GREP_NODE_NOT,
        GREP_NODE_AND,
+       GREP_NODE_TRUE,
        GREP_NODE_OR
 };
 
index 6bb3095c3a85d771bb60e8f2f025287d16e792df..007dd3e4d38ff657a29a06e559173b796f193763 100644 (file)
@@ -18,7 +18,7 @@ typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
                           mmfile_t *orig, const char *orig_name,
                           mmfile_t *src1, const char *name1,
                           mmfile_t *src2, const char *name2,
-                          int flag,
+                          const struct ll_merge_options *opts,
                           int marker_size);
 
 struct ll_merge_driver {
@@ -39,14 +39,18 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
                           mmfile_t *orig, const char *orig_name,
                           mmfile_t *src1, const char *name1,
                           mmfile_t *src2, const char *name2,
-                          int flag, int marker_size)
+                          const struct ll_merge_options *opts,
+                          int marker_size)
 {
+       mmfile_t *stolen;
+       assert(opts);
+
        /*
         * The tentative merge result is "ours" for the final round,
         * or common ancestor for an internal merge.  Still return
         * "conflicted merge" status.
         */
-       mmfile_t *stolen = (flag & LL_OPT_VIRTUAL_ANCESTOR) ? orig : src1;
+       stolen = opts->virtual_ancestor ? orig : src1;
 
        result->ptr = stolen->ptr;
        result->size = stolen->size;
@@ -60,9 +64,11 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
                        mmfile_t *orig, const char *orig_name,
                        mmfile_t *src1, const char *name1,
                        mmfile_t *src2, const char *name2,
-                       int flag, int marker_size)
+                       const struct ll_merge_options *opts,
+                       int marker_size)
 {
        xmparam_t xmp;
+       assert(opts);
 
        if (buffer_is_binary(orig->ptr, orig->size) ||
            buffer_is_binary(src1->ptr, src1->size) ||
@@ -74,12 +80,13 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
                                       orig, orig_name,
                                       src1, name1,
                                       src2, name2,
-                                      flag, marker_size);
+                                      opts, marker_size);
        }
 
        memset(&xmp, 0, sizeof(xmp));
        xmp.level = XDL_MERGE_ZEALOUS;
-       xmp.favor = ll_opt_favor(flag);
+       xmp.favor = opts->variant;
+       xmp.xpp.flags = opts->xdl_opts;
        if (git_xmerge_style >= 0)
                xmp.style = git_xmerge_style;
        if (marker_size > 0)
@@ -96,15 +103,17 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
                          mmfile_t *orig, const char *orig_name,
                          mmfile_t *src1, const char *name1,
                          mmfile_t *src2, const char *name2,
-                         int flag, int marker_size)
+                         const struct ll_merge_options *opts,
+                         int marker_size)
 {
        /* Use union favor */
-       flag &= ~LL_OPT_FAVOR_MASK;
-       flag |= create_ll_flag(XDL_MERGE_FAVOR_UNION);
+       struct ll_merge_options o;
+       assert(opts);
+       o = *opts;
+       o.variant = XDL_MERGE_FAVOR_UNION;
        return ll_xdl_merge(drv_unused, result, path_unused,
                            orig, NULL, src1, NULL, src2, NULL,
-                           flag, marker_size);
-       return 0;
+                           &o, marker_size);
 }
 
 #define LL_BINARY_MERGE 0
@@ -136,7 +145,8 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
                        mmfile_t *orig, const char *orig_name,
                        mmfile_t *src1, const char *name1,
                        mmfile_t *src2, const char *name2,
-                       int flag, int marker_size)
+                       const struct ll_merge_options *opts,
+                       int marker_size)
 {
        char temp[4][50];
        struct strbuf cmd = STRBUF_INIT;
@@ -144,6 +154,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
        const char *args[] = { NULL, NULL };
        int status, fd, i;
        struct stat st;
+       assert(opts);
 
        dict[0].placeholder = "O"; dict[0].value = temp[0];
        dict[1].placeholder = "A"; dict[1].value = temp[1];
@@ -337,15 +348,21 @@ int ll_merge(mmbuffer_t *result_buf,
             mmfile_t *ancestor, const char *ancestor_label,
             mmfile_t *ours, const char *our_label,
             mmfile_t *theirs, const char *their_label,
-            int flag)
+            const struct ll_merge_options *opts)
 {
        static struct git_attr_check check[2];
        const char *ll_driver_name = NULL;
        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
        const struct ll_merge_driver *driver;
-       int virtual_ancestor = flag & LL_OPT_VIRTUAL_ANCESTOR;
 
-       if (flag & LL_OPT_RENORMALIZE) {
+       if (!opts) {
+               struct ll_merge_options default_opts = {0};
+               return ll_merge(result_buf, path, ancestor, ancestor_label,
+                               ours, our_label, theirs, their_label,
+                               &default_opts);
+       }
+
+       if (opts->renormalize) {
                normalize_file(ancestor, path);
                normalize_file(ours, path);
                normalize_file(theirs, path);
@@ -359,11 +376,11 @@ int ll_merge(mmbuffer_t *result_buf,
                }
        }
        driver = find_ll_merge_driver(ll_driver_name);
-       if (virtual_ancestor && driver->recursive)
+       if (opts->virtual_ancestor && driver->recursive)
                driver = find_ll_merge_driver(driver->recursive);
        return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
                          ours, our_label, theirs, their_label,
-                         flag, marker_size);
+                         opts, marker_size);
 }
 
 int ll_merge_marker_size(const char *path)
index ff7ca87bfa01d0e9705f4bfd83b9e4d2c155d588..244a31f55ac7375b59d7c257bfc2391957fdca94 100644 (file)
@@ -5,27 +5,19 @@
 #ifndef LL_MERGE_H
 #define LL_MERGE_H
 
-#define LL_OPT_VIRTUAL_ANCESTOR        (1 << 0)
-#define LL_OPT_FAVOR_MASK      ((1 << 1) | (1 << 2))
-#define LL_OPT_FAVOR_SHIFT 1
-#define LL_OPT_RENORMALIZE     (1 << 3)
-
-static inline int ll_opt_favor(int flag)
-{
-       return (flag & LL_OPT_FAVOR_MASK) >> LL_OPT_FAVOR_SHIFT;
-}
-
-static inline int create_ll_flag(int favor)
-{
-       return ((favor << LL_OPT_FAVOR_SHIFT) & LL_OPT_FAVOR_MASK);
-}
+struct ll_merge_options {
+       unsigned virtual_ancestor : 1;
+       unsigned variant : 2;   /* favor ours, favor theirs, or union merge */
+       unsigned renormalize : 1;
+       long xdl_opts;
+};
 
 int ll_merge(mmbuffer_t *result_buf,
             const char *path,
             mmfile_t *ancestor, const char *ancestor_label,
             mmfile_t *ours, const char *our_label,
             mmfile_t *theirs, const char *their_label,
-            int flag);
+            const struct ll_merge_options *opts);
 
 int ll_merge_marker_size(const char *path);
 
index db4d0d50d32d8852d1cb5173125e4173c3badb49..f7f4533926e35867725db00f0c89522c4b290898 100644 (file)
@@ -37,7 +37,7 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our
         * common ancestor.
         */
        merge_status = ll_merge(&res, path, base, NULL,
-                               our, ".our", their, ".their", 0);
+                               our, ".our", their, ".their", NULL);
        if (merge_status < 0)
                return NULL;
 
index c5746988196b8139ca3234f8b30d03af56d33324..875859f68efaab5113785896326140c8d0543f45 100644 (file)
@@ -334,6 +334,7 @@ static struct string_list *get_renames(struct merge_options *o,
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
                            500;
+       opts.rename_score = o->rename_score;
        opts.warn_on_too_large_rename = 1;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        if (diff_setup_done(&opts) < 0)
@@ -605,22 +606,26 @@ static int merge_3way(struct merge_options *o,
                      const char *branch2)
 {
        mmfile_t orig, src1, src2;
+       struct ll_merge_options ll_opts = {0};
        char *base_name, *name1, *name2;
        int merge_status;
-       int favor;
 
-       if (o->call_depth)
-               favor = 0;
-       else {
+       ll_opts.renormalize = o->renormalize;
+       ll_opts.xdl_opts = o->xdl_opts;
+
+       if (o->call_depth) {
+               ll_opts.virtual_ancestor = 1;
+               ll_opts.variant = 0;
+       } else {
                switch (o->recursive_variant) {
                case MERGE_RECURSIVE_OURS:
-                       favor = XDL_MERGE_FAVOR_OURS;
+                       ll_opts.variant = XDL_MERGE_FAVOR_OURS;
                        break;
                case MERGE_RECURSIVE_THEIRS:
-                       favor = XDL_MERGE_FAVOR_THEIRS;
+                       ll_opts.variant = XDL_MERGE_FAVOR_THEIRS;
                        break;
                default:
-                       favor = 0;
+                       ll_opts.variant = 0;
                        break;
                }
        }
@@ -643,10 +648,7 @@ static int merge_3way(struct merge_options *o,
        read_mmblob(&src2, b->sha1);
 
        merge_status = ll_merge(result_buf, a->path, &orig, base_name,
-                               &src1, name1, &src2, name2,
-                               ((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |
-                                (o->renormalize ? LL_OPT_RENORMALIZE : 0) |
-                                create_ll_flag(favor)));
+                               &src1, name1, &src2, name2, &ll_opts);
 
        free(name1);
        free(name2);
@@ -1550,3 +1552,37 @@ void init_merge_options(struct merge_options *o)
        memset(&o->current_directory_set, 0, sizeof(struct string_list));
        o->current_directory_set.strdup_strings = 1;
 }
+
+int parse_merge_opt(struct merge_options *o, const char *s)
+{
+       if (!s || !*s)
+               return -1;
+       if (!strcmp(s, "ours"))
+               o->recursive_variant = MERGE_RECURSIVE_OURS;
+       else if (!strcmp(s, "theirs"))
+               o->recursive_variant = MERGE_RECURSIVE_THEIRS;
+       else if (!strcmp(s, "subtree"))
+               o->subtree_shift = "";
+       else if (!prefixcmp(s, "subtree="))
+               o->subtree_shift = s + strlen("subtree=");
+       else if (!strcmp(s, "patience"))
+               o->xdl_opts |= XDF_PATIENCE_DIFF;
+       else if (!strcmp(s, "ignore-space-change"))
+               o->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+       else if (!strcmp(s, "ignore-all-space"))
+               o->xdl_opts |= XDF_IGNORE_WHITESPACE;
+       else if (!strcmp(s, "ignore-space-at-eol"))
+               o->xdl_opts |= XDF_IGNORE_WHITESPACE_AT_EOL;
+       else if (!strcmp(s, "renormalize"))
+               o->renormalize = 1;
+       else if (!strcmp(s, "no-renormalize"))
+               o->renormalize = 0;
+       else if (!prefixcmp(s, "rename-threshold=")) {
+               const char *score = s + strlen("rename-threshold=");
+               if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
+                       return -1;
+       }
+       else
+               return -1;
+       return 0;
+}
index 34492dbd6ea22d715c98acbeeabe04033ff9a794..c8135b0ec70cc2eb92e5a6fd9353a134fda6b7bf 100644 (file)
@@ -15,9 +15,11 @@ struct merge_options {
        const char *subtree_shift;
        unsigned buffer_output : 1;
        unsigned renormalize : 1;
+       long xdl_opts;
        int verbosity;
        int diff_rename_limit;
        int merge_rename_limit;
+       int rename_score;
        int call_depth;
        struct strbuf obuf;
        struct string_list current_file_set;
@@ -52,6 +54,8 @@ int merge_recursive_generic(struct merge_options *o,
 void init_merge_options(struct merge_options *o);
 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);
 
index 6cb0dd19344ba1169b23b7e4949033f8a9088a00..205e48aa3a7b912005565ac9c296205a897a1476 100644 (file)
@@ -7,6 +7,7 @@ Git - Perl interface to the Git version control system
 
 package Git;
 
+use 5.008;
 use strict;
 
 
index 861ca7c815b4857f1fde399617860e5a179e2ca3..d2608434750c336c3f3881efda8373b7c67d4b11 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -325,7 +325,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
         */
        ll_merge(&result, path, &mmfile[0], NULL,
                 &mmfile[1], "ours",
-                &mmfile[2], "theirs", 0);
+                &mmfile[2], "theirs", NULL);
        for (i = 0; i < 3; i++)
                free(mmfile[i].ptr);
 
diff --git a/shell.c b/shell.c
index e4864e04da3b0e237342c2ca0548c0ec0082c171..dea4cfdd2c230af6afd6233cacfa5c776a9962f9 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -2,6 +2,10 @@
 #include "quote.h"
 #include "exec_cmd.h"
 #include "strbuf.h"
+#include "run-command.h"
+
+#define COMMAND_DIR "git-shell-commands"
+#define HELP_COMMAND COMMAND_DIR "/help"
 
 static int do_generic_cmd(const char *me, char *arg)
 {
@@ -33,6 +37,86 @@ static int do_cvs_cmd(const char *me, char *arg)
        return execv_git_cmd(cvsserver_argv);
 }
 
+static int is_valid_cmd_name(const char *cmd)
+{
+       /* Test command contains no . or / characters */
+       return cmd[strcspn(cmd, "./")] == '\0';
+}
+
+static char *make_cmd(const char *prog)
+{
+       char *prefix = xmalloc((strlen(prog) + strlen(COMMAND_DIR) + 2));
+       strcpy(prefix, COMMAND_DIR);
+       strcat(prefix, "/");
+       strcat(prefix, prog);
+       return prefix;
+}
+
+static void cd_to_homedir(void)
+{
+       const char *home = getenv("HOME");
+       if (!home)
+               die("could not determine user's home directory; HOME is unset");
+       if (chdir(home) == -1)
+               die("could not chdir to user's home directory");
+}
+
+static void run_shell(void)
+{
+       int done = 0;
+       static const char *help_argv[] = { HELP_COMMAND, NULL };
+       /* Print help if enabled */
+       run_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE);
+
+       do {
+               struct strbuf line = STRBUF_INIT;
+               const char *prog;
+               char *full_cmd;
+               char *rawargs;
+               char *split_args;
+               const char **argv;
+               int code;
+               int count;
+
+               fprintf(stderr, "git> ");
+               if (strbuf_getline(&line, stdin, '\n') == EOF) {
+                       fprintf(stderr, "\n");
+                       strbuf_release(&line);
+                       break;
+               }
+               strbuf_trim(&line);
+               rawargs = strbuf_detach(&line, NULL);
+               split_args = xstrdup(rawargs);
+               count = split_cmdline(split_args, &argv);
+               if (count < 0) {
+                       fprintf(stderr, "invalid command format '%s': %s\n", rawargs,
+                               split_cmdline_strerror(count));
+                       free(split_args);
+                       free(rawargs);
+                       continue;
+               }
+
+               prog = argv[0];
+               if (!strcmp(prog, "")) {
+               } else if (!strcmp(prog, "quit") || !strcmp(prog, "logout") ||
+                          !strcmp(prog, "exit") || !strcmp(prog, "bye")) {
+                       done = 1;
+               } else if (is_valid_cmd_name(prog)) {
+                       full_cmd = make_cmd(prog);
+                       argv[0] = full_cmd;
+                       code = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
+                       if (code == -1 && errno == ENOENT) {
+                               fprintf(stderr, "unrecognized command '%s'\n", prog);
+                       }
+                       free(full_cmd);
+               } else {
+                       fprintf(stderr, "invalid command format '%s'\n", prog);
+               }
+
+               free(argv);
+               free(rawargs);
+       } while (!done);
+}
 
 static struct commands {
        const char *name;
@@ -48,8 +132,10 @@ static struct commands {
 int main(int argc, char **argv)
 {
        char *prog;
+       const char **user_argv;
        struct commands *cmd;
        int devnull_fd;
+       int count;
 
        /*
         * Always open file descriptors 0/1/2 to avoid clobbering files
@@ -66,17 +152,28 @@ int main(int argc, char **argv)
        /*
         * Special hack to pretend to be a CVS server
         */
-       if (argc == 2 && !strcmp(argv[1], "cvs server"))
+       if (argc == 2 && !strcmp(argv[1], "cvs server")) {
                argv--;
+       } else if (argc == 1) {
+               /* Allow the user to run an interactive shell */
+               cd_to_homedir();
+               if (access(COMMAND_DIR, R_OK | X_OK) == -1) {
+                       die("Interactive git shell is not enabled.\n"
+                           "hint: ~/" COMMAND_DIR " should exist "
+                           "and have read and execute access.");
+               }
+               run_shell();
+               exit(0);
+       } else if (argc != 3 || strcmp(argv[1], "-c")) {
+               /*
+                * We do not accept any other modes except "-c" followed by
+                * "cmd arg", where "cmd" is a very limited subset of git
+                * commands or a command in the COMMAND_DIR
+                */
+               die("Run with no arguments or with -c cmd");
+       }
 
-       /*
-        * We do not accept anything but "-c" followed by "cmd arg",
-        * where "cmd" is a very limited subset of git commands.
-        */
-       else if (argc != 3 || strcmp(argv[1], "-c"))
-               die("What do you think I am? A shell?");
-
-       prog = argv[2];
+       prog = xstrdup(argv[2]);
        if (!strncmp(prog, "git", 3) && isspace(prog[3]))
                /* Accept "git foo" as if the caller said "git-foo". */
                prog[3] = '-';
@@ -99,5 +196,21 @@ int main(int argc, char **argv)
                }
                exit(cmd->exec(cmd->name, arg));
        }
-       die("unrecognized command '%s'", prog);
+
+       cd_to_homedir();
+       count = split_cmdline(prog, &user_argv);
+       if (count >= 0) {
+               if (is_valid_cmd_name(user_argv[0])) {
+                       prog = make_cmd(user_argv[0]);
+                       user_argv[0] = prog;
+                       execv(user_argv[0], (char *const *) user_argv);
+               }
+               free(prog);
+               free(user_argv);
+               die("unrecognized command '%s'", argv[2]);
+       } else {
+               free(prog);
+               die("invalid command format '%s': %s", argv[2],
+                   split_cmdline_strerror(count));
+       }
 }
index bc3a0802ea7e7b1743602972de182391b4bf0b3f..65b4cf43434a8b7327565d0e0f87d5378d098f99 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -399,6 +399,8 @@ int strbuf_branchname(struct strbuf *sb, const char *name)
 int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
 {
        strbuf_branchname(sb, name);
+       if (name[0] == '-')
+               return CHECK_REF_FORMAT_ERROR;
        strbuf_splice(sb, 0, 0, "refs/heads/", 11);
        return check_ref_format(sb->buf);
 }
index 81ef2a0969d98f05ce22cd8a06fcb55a7af9aee8..8c490c87071cc71228d1811d6e08cf542bf11d2e 100644 (file)
@@ -19,9 +19,9 @@ our \$site_name = '[localhost]';
 our \$site_header = '';
 our \$site_footer = '';
 our \$home_text = 'indextext.html';
-our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/static/gitweb.css');
-our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/static/git-logo.png';
-our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/static/git-favicon.png';
+our @stylesheets = ('file:///$GIT_BUILD_DIR/gitweb/static/gitweb.css');
+our \$logo = 'file:///$GIT_BUILD_DIR/gitweb/static/git-logo.png';
+our \$favicon = 'file:///$GIT_BUILD_DIR/gitweb/static/git-favicon.png';
 our \$projects_list = '';
 our \$export_ok = '';
 our \$strict_export = '';
@@ -38,7 +38,7 @@ gitweb_run () {
        GATEWAY_INTERFACE='CGI/1.1'
        HTTP_ACCEPT='*/*'
        REQUEST_METHOD='GET'
-       SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
+       SCRIPT_NAME="$GIT_BUILD_DIR/gitweb/gitweb.perl"
        QUERY_STRING=""$1""
        PATH_INFO=""$2""
        export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
@@ -81,8 +81,8 @@ if ! test_have_prereq PERL; then
 fi
 
 perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
-    skip_all='skipping gitweb tests, perl version is too old'
-    test_done
+       skip_all='skipping gitweb tests, perl version is too old'
+       test_done
 }
 
 gitweb_init
index de38c7f7aab16a0c2766ef020a3fc03fadece555..e75153bdea05d2e34570aaf03fbb3902694ddedf 100755 (executable)
@@ -36,6 +36,9 @@ test_expect_success 'setup' '
                echo "d/* test=a/b/d/*"
                echo "d/yes notest"
        ) >a/b/.gitattributes
+       (
+               echo "global test=global"
+       ) >"$HOME"/global-gitattributes
 
 '
 
@@ -57,6 +60,16 @@ test_expect_success 'attribute test' '
 
 '
 
+test_expect_success 'core.attributesfile' '
+       attr_check global unspecified &&
+       git config core.attributesfile "$HOME/global-gitattributes" &&
+       attr_check global global &&
+       git config core.attributesfile "~/global-gitattributes" &&
+       attr_check global global &&
+       echo "global test=precedence" >> .gitattributes &&
+       attr_check global precedence
+'
+
 test_expect_success 'attribute test: read paths from stdin' '
 
        cat <<EOF > expect
index 074f2f2e3e57f76b01f16502998e95b493676711..d0ab8ffe1b91a7fa37dfc7939cb3d5ae2c3cc100 100755 (executable)
@@ -701,13 +701,13 @@ cat >expect <<\EOF
        trailingtilde = foo~
 EOF
 
-test_expect_success 'set --path' '
+test_expect_success NOT_MINGW 'set --path' '
        git config --path path.home "~/" &&
        git config --path path.normal "/dev/null" &&
        git config --path path.trailingtilde "foo~" &&
        test_cmp expect .git/config'
 
-if test "${HOME+set}"
+if test_have_prereq NOT_MINGW && test "${HOME+set}"
 then
        test_set_prereq HOMEVAR
 fi
@@ -730,7 +730,7 @@ cat >expect <<\EOF
 foo~
 EOF
 
-test_expect_success 'get --path copes with unset $HOME' '
+test_expect_success NOT_MINGW 'get --path copes with unset $HOME' '
        (
                unset HOME;
                test_must_fail git config --get --path path.home \
diff --git a/t/t3032-merge-recursive-options.sh b/t/t3032-merge-recursive-options.sh
new file mode 100755 (executable)
index 0000000..2293797
--- /dev/null
@@ -0,0 +1,186 @@
+#!/bin/sh
+
+test_description='merge-recursive options
+
+* [master] Clarify
+ ! [remote] Remove cruft
+--
+ + [remote] Remove cruft
+*  [master] Clarify
+*+ [remote^] Initial revision
+*   ok 1: setup
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       conflict_hunks () {
+               sed -n -e "
+                       /^<<<</ b inconflict
+                       b
+                       : inconflict
+                       p
+                       /^>>>>/ b
+                       n
+                       b inconflict
+               " "$@"
+       } &&
+
+       cat <<-\EOF >text.txt &&
+           Hope, he says, cherishes the soul of him who lives in
+           justice and holiness and is the nurse of his age and the
+           companion of his journey;--hope which is mightiest to sway
+           the restless soul of man.
+
+       How admirable are his words!  And the great blessing of riches, I do
+       not say to every man, but to a good man, is, that he has had no
+       occasion to deceive or to defraud others, either intentionally or
+       unintentionally; and when he departs to the world below he is not in
+       any apprehension about offerings due to the gods or debts which he owes
+       to men.  Now to this peace of mind the possession of wealth greatly
+       contributes; and therefore I say, that, setting one thing against
+       another, of the many advantages which wealth has to give, to a man of
+       sense this is in my opinion the greatest.
+
+       Well said, Cephalus, I replied; but as concerning justice, what is
+       it?--to speak the truth and to pay your debts--no more than this?  And
+       even to this are there not exceptions?  Suppose that a friend when in
+       his right mind has deposited arms with me and he asks for them when he
+       is not in his right mind, ought I to give them back to him?  No one
+       would say that I ought or that I should be right in doing so, any more
+       than they would say that I ought always to speak the truth to one who
+       is in his condition.
+
+       You are quite right, he replied.
+
+       But then, I said, speaking the truth and paying your debts is not a
+       correct definition of justice.
+
+       CEPHALUS - SOCRATES - POLEMARCHUS
+
+       Quite correct, Socrates, if Simonides is to be believed, said
+       Polemarchus interposing.
+
+       I fear, said Cephalus, that I must go now, for I have to look after the
+       sacrifices, and I hand over the argument to Polemarchus and the company.
+       EOF
+       git add text.txt &&
+       test_tick &&
+       git commit -m "Initial revision" &&
+
+       git checkout -b remote &&
+       sed -e "
+                       s/\.  /\. /g
+                       s/[?]  /? /g
+                       s/    / /g
+                       s/--/---/g
+                       s/but as concerning/but as con cerning/
+                       /CEPHALUS - SOCRATES - POLEMARCHUS/ d
+               " text.txt >text.txt+ &&
+       mv text.txt+ text.txt &&
+       git commit -a -m "Remove cruft" &&
+
+       git checkout master &&
+       sed -e "
+                       s/\(not in his right mind\),\(.*\)/\1;\2Q/
+                       s/Quite correct\(.*\)/It is too correct\1Q/
+                       s/unintentionally/un intentionally/
+                       /un intentionally/ s/$/Q/
+                       s/Polemarchus interposing./Polemarchus, interposing.Q/
+                       /justice and holiness/ s/$/Q/
+                       /pay your debts/ s/$/Q/
+               " text.txt | q_to_cr >text.txt+ &&
+       mv text.txt+ text.txt &&
+       git commit -a -m "Clarify" &&
+       git show-branch --all
+'
+
+test_expect_success 'naive merge fails' '
+       git read-tree --reset -u HEAD &&
+       test_must_fail git merge-recursive HEAD^ -- HEAD remote &&
+       test_must_fail git update-index --refresh &&
+       grep "<<<<<<" text.txt
+'
+
+test_expect_success '--ignore-space-change makes merge succeed' '
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-space-change HEAD^ -- HEAD 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
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+       grep "justice and holiness" text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-change: their real change wins over w/s' '
+       cat <<-\EOF >expected &&
+       it?---to speak the truth and to pay your debts---no more than this? And
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+       grep "pay your debts" text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-change: does not ignore new spaces' '
+       cat <<-\EOF >expected1 &&
+       Well said, Cephalus, I replied; but as con cerning justice, what is
+       EOF
+       q_to_cr <<-\EOF >expected2 &&
+       un intentionally; and when he departs to the world below he is not inQ
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-space-change HEAD^ -- HEAD remote &&
+       grep "Well said" text.txt >actual1 &&
+       grep "when he departs" text.txt >actual2 &&
+       test_cmp expected1 actual1 &&
+       test_cmp expected2 actual2
+'
+
+test_expect_success '--ignore-all-space drops their new spaces' '
+       cat <<-\EOF >expected &&
+       Well said, Cephalus, I replied; but as concerning justice, what is
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
+       grep "Well said" text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--ignore-all-space keeps our new spaces' '
+       q_to_cr <<-\EOF >expected &&
+       un intentionally; and when he departs to the world below he is not inQ
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       git merge-recursive --ignore-all-space HEAD^ -- HEAD remote &&
+       grep "when he departs" text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--ignore-space-at-eol' '
+       q_to_cr <<-\EOF >expected &&
+       <<<<<<< HEAD
+       is not in his right mind; ought I to give them back to him?  No oneQ
+       =======
+       is not in his right mind, ought I to give them back to him? No one
+       >>>>>>> remote
+       EOF
+
+       git read-tree --reset -u HEAD &&
+       test_must_fail git merge-recursive --ignore-space-at-eol \
+                                                HEAD^ -- HEAD remote &&
+       conflict_hunks text.txt >actual &&
+       test_cmp expected actual
+'
+
+test_done
index e17ae712b1078333fa191b97764177016bc01b84..51ca391e472441d2d503e721795d609bfaad1100 100755 (executable)
@@ -95,4 +95,14 @@ test_expect_success 'cherry pick a merge relative to nonexistent parent with --f
        test_must_fail git cherry-pick --ff -m 3 C
 '
 
+test_expect_success 'cherry pick a root commit with --ff' '
+       git reset --hard first -- &&
+       git rm file1 &&
+       echo first >file2 &&
+       git add file2 &&
+       git commit --amend -m "file2" &&
+       git cherry-pick --ff first &&
+       test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1"
+'
+
 test_done
index 19857f4326aa491a4c7d292900a0a766e7c45f80..9a665205884fcdb1c5888558d6edf960e335e013 100755 (executable)
@@ -210,6 +210,9 @@ log -m -p master
 log -SF master
 log -S F master
 log -SF -p master
+log -GF master
+log -GF -p master
+log -GF -p --pickaxe-all master
 log --decorate --all
 log --decorate=full --all
 
diff --git a/t/t4013/diff.log_-GF_-p_--pickaxe-all_master b/t/t4013/diff.log_-GF_-p_--pickaxe-all_master
new file mode 100644 (file)
index 0000000..d36f880
--- /dev/null
@@ -0,0 +1,27 @@
+$ git log -GF -p --pickaxe-all master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+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.log_-GF_-p_master b/t/t4013/diff.log_-GF_-p_master
new file mode 100644 (file)
index 0000000..9d93f2c
--- /dev/null
@@ -0,0 +1,18 @@
+$ git log -GF -p master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+$
diff --git a/t/t4013/diff.log_-GF_master b/t/t4013/diff.log_-GF_master
new file mode 100644 (file)
index 0000000..4c6708d
--- /dev/null
@@ -0,0 +1,7 @@
+$ git log -GF master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+$
index f87434b9f8e0d520813389e6ea7c94f74222767b..07bf6eb49dd2879758f64a8c09a0ae8d7e54dd45 100755 (executable)
@@ -12,24 +12,29 @@ test_expect_success setup '
        for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
        cat file >elif &&
        git add file elif &&
+       test_tick &&
        git commit -m Initial &&
        git checkout -b side &&
 
        for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
        test_chmod +x elif &&
+       test_tick &&
        git commit -m "Side changes #1" &&
 
        for i in D E F; do echo "$i"; done >>file &&
        git update-index file &&
+       test_tick &&
        git commit -m "Side changes #2" &&
        git tag C2 &&
 
        for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
        git update-index file &&
+       test_tick &&
        git commit -m "Side changes #3 with \\n backslash-n in it." &&
 
        git checkout master &&
        git diff-tree -p C2 | git apply --index &&
+       test_tick &&
        git commit -m "Master accepts moral equivalent of #2"
 
 '
@@ -51,6 +56,22 @@ test_expect_success "format-patch --ignore-if-in-upstream" '
 
 '
 
+test_expect_success "format-patch doesn't consider merge commits" '
+
+       git checkout -b slave master &&
+       echo "Another line" >>file &&
+       test_tick &&
+       git commit -am "Slave change #1" &&
+       echo "Yet another line" >>file &&
+       test_tick &&
+       git commit -am "Slave change #2" &&
+       git checkout -b merger master &&
+       test_tick &&
+       git merge --no-ff slave &&
+       cnt=`git format-patch -3 --stdout | grep "^From " | wc -l` &&
+       test $cnt = 3
+'
+
 test_expect_success "format-patch result applies" '
 
        git checkout -b rebuild-0 master &&
index c8e19372b00f128646b809e1f5723d88b58a3f30..0a61b57b5f6f00ae4c0734a34ce45c0ba1fcf098 100755 (executable)
@@ -32,7 +32,7 @@ EOF
 
 sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
 
-builtin_patterns="bibtex cpp csharp html java objc pascal php python ruby tex"
+builtin_patterns="bibtex cpp csharp fortran html java objc pascal php python ruby tex"
 for p in $builtin_patterns
 do
        test_expect_success "builtin $p pattern compiles" '
index 995bdfafecedf6b629acdb3300732a4bb691f23c..bf9a7526bd38a17e0e991739db8c4a1f8541b2f6 100755 (executable)
@@ -37,9 +37,10 @@ head1=$(add_file sm1 foo1 foo2)
 test_expect_success 'added submodule' "
        git add sm1 &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 0000000...$head1 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -47,33 +48,36 @@ head2=$(add_file sm1 foo3)
 
 test_expect_success 'modified submodule(forward)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head1..$head2:
   > Add foo3
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule(forward)' "
        git diff --submodule=log >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head1..$head2:
   > Add foo3
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule(forward) --submodule' "
        git diff --submodule >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head1..$head2:
   > Add foo3
 EOF
+       test_cmp expected actual
 "
 
 fullhead1=$(cd sm1; git rev-list --max-count=1 $head1)
 fullhead2=$(cd sm1; git rev-list --max-count=1 $head2)
 test_expect_success 'modified submodule(forward) --submodule=short' "
        git diff --submodule=short >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 diff --git a/sm1 b/sm1
 index $head1..$head2 160000
 --- a/sm1
@@ -82,6 +86,7 @@ index $head1..$head2 160000
 -Subproject commit $fullhead1
 +Subproject commit $fullhead2
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -93,24 +98,26 @@ head3=$(
 
 test_expect_success 'modified submodule(backward)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head2..$head3 (rewind):
   < Add foo3
   < Add foo2
 EOF
+       test_cmp expected actual
 "
 
 head4=$(add_file sm1 foo4 foo5) &&
 head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
 test_expect_success 'modified submodule(backward and forward)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head2...$head4:
   > Add foo5
   > Add foo4
   < Add foo3
   < Add foo2
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -123,7 +130,7 @@ mv sm1-bak sm1
 
 test_expect_success 'typechanged submodule(submodule->blob), --cached' "
        git diff --submodule=log --cached >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 41fbea9...0000000 (submodule deleted)
 diff --git a/sm1 b/sm1
 new file mode 100644
@@ -133,11 +140,12 @@ index 0000000..9da5fb8
 @@ -0,0 +1 @@
 +sm1
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'typechanged submodule(submodule->blob)' "
        git diff --submodule=log >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 diff --git a/sm1 b/sm1
 deleted file mode 100644
 index 9da5fb8..0000000
@@ -147,13 +155,14 @@ index 9da5fb8..0000000
 -sm1
 Submodule sm1 0000000...$head4 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head4...0000000 (submodule deleted)
 diff --git a/sm1 b/sm1
 new file mode 100644
@@ -163,6 +172,7 @@ index 0000000..$head5
 @@ -0,0 +1 @@
 +sm1
 EOF
+       test_cmp expected actual
 "
 
 rm -f sm1 &&
@@ -171,15 +181,16 @@ head6=$(add_file sm1 foo6 foo7)
 fullhead6=$(cd sm1; git rev-list --max-count=1 $head6)
 test_expect_success 'nonexistent commit' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head4...$head6 (commits not present)
 EOF
+       test_cmp expected actual
 "
 
 commit_file
 test_expect_success 'typechanged submodule(blob->submodule)' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 diff --git a/sm1 b/sm1
 deleted file mode 100644
 index $head5..0000000
@@ -189,21 +200,24 @@ index $head5..0000000
 -sm1
 Submodule sm1 0000000...$head6 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
 test_expect_success 'submodule is up to date' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'submodule contains untracked content' "
        echo new > sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains untracked content
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'submodule contains untracked content (untracked ignored)' "
@@ -224,18 +238,20 @@ test_expect_success 'submodule contains untracked content (all ignored)' "
 test_expect_success 'submodule contains untracked and modifed content' "
        echo new > sm1/foo6 &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains untracked content
 Submodule sm1 contains modified content
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' "
        echo new > sm1/foo6 &&
        git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains modified content
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' "
@@ -253,45 +269,50 @@ test_expect_success 'submodule contains untracked and modifed content (all ignor
 test_expect_success 'submodule contains modifed content' "
        rm -f sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains modified content
 EOF
+       test_cmp expected actual
 "
 
 (cd sm1; git commit -mchange foo6 >/dev/null) &&
 head8=$(cd sm1; git rev-parse --verify HEAD | cut -c1-7) &&
 test_expect_success 'submodule is modified' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked content' "
        echo new > sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains untracked content
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked content (untracked ignored)' "
        git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked content (dirty ignored)' "
        git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked content (all ignored)' "
@@ -302,31 +323,34 @@ test_expect_success 'modified submodule contains untracked content (all ignored)
 test_expect_success 'modified submodule contains untracked and modifed content' "
        echo modification >> sm1/foo6 &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains untracked content
 Submodule sm1 contains modified content
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked and modifed content (untracked ignored)' "
        echo modification >> sm1/foo6 &&
        git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains modified content
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked and modifed content (dirty ignored)' "
        echo modification >> sm1/foo6 &&
        git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' "
@@ -338,19 +362,21 @@ test_expect_success 'modified submodule contains untracked and modifed content (
 test_expect_success 'modified submodule contains modifed content' "
        rm -f sm1/new-file &&
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 contains modified content
 Submodule sm1 $head6..$head8:
   > change
 EOF
+       test_cmp expected actual
 "
 
 rm -rf sm1
 test_expect_success 'deleted submodule' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 EOF
+       test_cmp expected actual
 "
 
 test_create_repo sm2 &&
@@ -359,41 +385,45 @@ git add sm2
 
 test_expect_success 'multiple submodules' "
        git diff-index -p --submodule=log HEAD >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'path filter' "
        git diff-index -p --submodule=log HEAD sm2 >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm2
 test_expect_success 'given commit' "
        git diff-index -p --submodule=log HEAD^ >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'given commit --submodule' "
        git diff-index -p --submodule HEAD^ >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 "
 
 fullhead7=$(cd sm2; git rev-list --max-count=1 $head7)
 
 test_expect_success 'given commit --submodule=short' "
        git diff-index -p --submodule=short HEAD^ >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 diff --git a/sm1 b/sm1
 deleted file mode 160000
 index $head6..0000000
@@ -409,6 +439,7 @@ index 0000000..$head7
 @@ -0,0 +1 @@
 +Subproject commit $fullhead7
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'setup .git file for sm2' '
@@ -420,10 +451,11 @@ test_expect_success 'setup .git file for sm2' '
 
 test_expect_success 'diff --submodule with .git file' '
        git diff --submodule HEAD^ >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 Submodule sm1 $head6...0000000 (submodule deleted)
 Submodule sm2 0000000...$head7 (new submodule)
 EOF
+       test_cmp expected actual
 '
 
 test_done
index 27bfba55bd9631a666ee382b343c3c04af1ad26d..cff1b3e0502e96ff6232611ef70c24752a970a96 100755 (executable)
@@ -94,7 +94,7 @@ test_expect_success 'git archive with --output' \
     'git archive --output=b4.tar HEAD &&
     test_cmp b.tar b4.tar'
 
-test_expect_success 'git archive --remote' \
+test_expect_success NOT_MINGW 'git archive --remote' \
     'git archive --remote=. HEAD >b5.tar &&
     test_cmp b.tar b5.tar'
 
index 8a298a655fa007fbd9a43d6212b53f749c980eb6..aa0ada01476b89f3f284dda42f892155496f3467 100755 (executable)
@@ -4,14 +4,9 @@ test_description='test automatic tag following'
 
 . ./test-lib.sh
 
-case $(uname -s) in
-*MINGW*)
+if ! test_have_prereq NOT_MINGW; then
        say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
-       ;;
-*)
-       test_set_prereq NOT_MINGW
-       ;;
-esac
+fi
 
 # End state of the repository:
 #
index 94f9d2e8e0790c22b9884cee4b668b8a6d408d99..0ad7ce07c4550ed28a22743a3c543e16f4d4c558 100755 (executable)
@@ -5,11 +5,12 @@ test_description='test git-http-backend-noserver'
 
 HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
 
+test_have_prereq MINGW && export GREP_OPTIONS=-U
+
 run_backend() {
        echo "$2" |
        QUERY_STRING="${1#*\?}" \
-       GIT_PROJECT_ROOT="$HTTPD_DOCUMENT_ROOT_PATH" \
-       PATH_INFO="${1%%\?*}" \
+       PATH_TRANSLATED="$HTTPD_DOCUMENT_ROOT_PATH/${1%%\?*}" \
        git http-backend >act.out 2>act.err
 }
 
index 52d0dc4bb8f80393c79935f9c951b5323287e41c..460bf741b594d4d6b7f2220ac0f1a0d28c653619 100755 (executable)
@@ -14,6 +14,8 @@ test_description='CRLF merge conflict across text=auto change
 
 . ./test-lib.sh
 
+test_have_prereq MINGW && SED_OPTIONS=-b
+
 test_expect_success setup '
        git config core.autocrlf false &&
 
@@ -60,7 +62,7 @@ test_expect_success setup '
 
 test_expect_success 'set up fuzz_conflict() helper' '
        fuzz_conflict() {
-               sed -e "s/^\([<>=]......\) .*/\1/" "$@"
+               sed $SED_OPTIONS -e "s/^\([<>=]......\) .*/\1/" "$@"
        }
 '
 
index 71f6cad3c2b8e8ed0b16d292f4922760901702ef..9a168069217ef8d82173e563a04eaefe58d99f2a 100755 (executable)
@@ -129,6 +129,97 @@ test_expect_success '[merge] summary/log configuration' '
        test_cmp expected actual2
 '
 
+test_expect_success 'setup: clear [merge] configuration' '
+       test_might_fail git config --unset-all merge.log &&
+       test_might_fail git config --unset-all merge.summary
+'
+
+test_expect_success 'setup FETCH_HEAD' '
+       git checkout master &&
+       test_tick &&
+       git fetch . left
+'
+
+test_expect_success 'merge.log=3 limits shortlog length' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
+
+       * left: (5 commits)
+         Left #5
+         Left #4
+         Left #3
+         ...
+       EOF
+
+       git -c merge.log=3 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'merge.log=5 shows all 5 commits' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
+
+       * left:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
+
+       git -c merge.log=5 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'merge.log=0 disables shortlog' '
+       echo "Merge branch ${apos}left${apos}" >expected
+       git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--log=3 limits shortlog length' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
+
+       * left: (5 commits)
+         Left #5
+         Left #4
+         Left #3
+         ...
+       EOF
+
+       git fmt-merge-msg --log=3 <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--log=5 shows all 5 commits' '
+       cat >expected <<-EOF &&
+       Merge branch ${apos}left${apos}
+
+       * left:
+         Left #5
+         Left #4
+         Left #3
+         Common #2
+         Common #1
+       EOF
+
+       git fmt-merge-msg --log=5 <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--no-log disables shortlog' '
+       echo "Merge branch ${apos}left${apos}" >expected &&
+       git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success '--log=0 disables shortlog' '
+       echo "Merge branch ${apos}left${apos}" >expected &&
+       git fmt-merge-msg --no-log <.git/FETCH_HEAD >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'fmt-merge-msg -m' '
        echo "Sync with left" >expected &&
        cat >expected.log <<-EOF &&
index ac943f5eeecd17dd1edc5834265df9e16e5032f2..700b556fe886ff8e6927591e4a047542bc00ef83 100755 (executable)
@@ -1051,13 +1051,23 @@ test_expect_success \
 
 test_expect_success \
        'message in editor has initial comment' '
-       GIT_EDITOR=cat git tag -a initial-comment > actual
+       ! (GIT_EDITOR=cat git tag -a initial-comment > actual)
+'
+
+test_expect_success \
+       'message in editor has initial comment: first line' '
        # check the first line --- should be empty
-       first=$(sed -e 1q <actual) &&
-       test -z "$first" &&
+       echo >first.expect &&
+       sed -e 1q <actual >first.actual &&
+       test_cmp first.expect first.actual
+'
+
+test_expect_success \
+       'message in editor has initial comment: remainder' '
        # remove commented lines from the remainder -- should be empty
-       rest=$(sed -e 1d -e '/^#/d' <actual) &&
-       test -z "$rest"
+       >rest.expect
+       sed -e 1d -e '/^#/d' <actual >rest.actual &&
+       test_cmp rest.expect rest.actual
 '
 
 get_tag_header reuse $commit commit $time >expect
index 73ff80937195f3ee8d4bf647a6c06bef23bb8891..6b5f22ae4a3ca96a22fa0a7d77bac1b59e747b00 100755 (executable)
@@ -1,4 +1,5 @@
 #!/usr/bin/perl
+use 5.008;
 use strict;
 use warnings;
 use IO::Pty;
index 294584452bbc3226e631ce0b81c546b04cd8d992..7d7fde057b04e4615e32b0ddc71fd3b07968a129 100755 (executable)
@@ -37,11 +37,12 @@ head1=$(add_file sm1 foo1 foo2)
 test_expect_success 'added submodule' "
        git add sm1 &&
        git submodule summary >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 * sm1 0000000...$head1 (2):
   > Add foo2
 
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -49,20 +50,22 @@ head2=$(add_file sm1 foo3)
 
 test_expect_success 'modified submodule(forward)' "
        git submodule summary >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 * sm1 $head1...$head2 (1):
   > Add foo3
 
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success 'modified submodule(forward), --files' "
        git submodule summary --files >actual &&
-       diff actual - <<-EOF
+       cat >expected <<-EOF &&
 * sm1 $head1...$head2 (1):
   > Add foo3
 
 EOF
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -74,19 +77,20 @@ head3=$(
 
 test_expect_success 'modified submodule(backward)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head3 (2):
   < Add foo3
   < Add foo2
 
 EOF
+       test_cmp expected actual
 "
 
 head4=$(add_file sm1 foo4 foo5) &&
 head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
 test_expect_success 'modified submodule(backward and forward)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
@@ -94,17 +98,19 @@ test_expect_success 'modified submodule(backward and forward)' "
   < Add foo2
 
 EOF
+       test_cmp expected actual
 "
 
 test_expect_success '--summary-limit' "
     git submodule summary -n 3 >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head2...$head4 (4):
   > Add foo5
   > Add foo4
   < Add foo3
 
 EOF
+    test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -117,30 +123,33 @@ mv sm1-bak sm1
 
 test_expect_success 'typechanged submodule(submodule->blob), --cached' "
     git submodule summary --cached >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob) (3):
   < Add foo5
 
 EOF
+       test_cmp actual expected
 "
 
 test_expect_success 'typechanged submodule(submodule->blob), --files' "
     git submodule summary --files >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head4(submodule) (3):
   > Add foo5
 
 EOF
+    test_cmp actual expected
 "
 
 rm -rf sm1 &&
 git checkout-index sm1
 test_expect_success 'typechanged submodule(submodule->blob)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4(submodule)->$head5(blob):
 
 EOF
+    test_cmp actual expected
 "
 
 rm -f sm1 &&
@@ -148,31 +157,34 @@ test_create_repo sm1 &&
 head6=$(add_file sm1 foo6 foo7)
 test_expect_success 'nonexistent commit' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head4...$head6:
   Warn: sm1 doesn't contain commit $head4_full
 
 EOF
+    test_cmp actual expected
 "
 
 commit_file
 test_expect_success 'typechanged submodule(blob->submodule)' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head5(blob)->$head6(submodule) (2):
   > Add foo7
 
 EOF
+    test_cmp expected actual
 "
 
 commit_file sm1 &&
 rm -rf sm1
 test_expect_success 'deleted submodule' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 EOF
+    test_cmp expected actual
 "
 
 test_create_repo sm2 &&
@@ -181,34 +193,37 @@ git add sm2
 
 test_expect_success 'multiple submodules' "
     git submodule summary >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 test_expect_success 'path filter' "
     git submodule summary sm2 >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 commit_file sm2
 test_expect_success 'given commit' "
     git submodule summary HEAD^ >actual &&
-    diff actual - <<-EOF
+    cat >expected <<-EOF &&
 * sm1 $head6...0000000:
 
 * sm2 0000000...$head7 (2):
   > Add foo9
 
 EOF
+    test_cmp expected actual
 "
 
 test_expect_success '--for-status' "
index 023f225a4b1f22c66fb291a76690564696d56035..50658845ca8769e963cfc13b20efb892f1f63cc0 100755 (executable)
@@ -324,8 +324,13 @@ test_expect_success 'log grep setup' '
 
        echo a >>file &&
        test_tick &&
-       git commit -a -m "third"
+       git commit -a -m "third" &&
 
+       echo a >>file &&
+       test_tick &&
+       GIT_AUTHOR_NAME="Night Fall" \
+       GIT_AUTHOR_EMAIL="nitfol@frobozz.com" \
+       git commit -a -m "fourth"
 '
 
 test_expect_success 'log grep (1)' '
@@ -372,6 +377,28 @@ test_expect_success 'log --grep --author implicitly uses all-match' '
        test_cmp expect actual
 '
 
+test_expect_success 'log with multiple --author uses union' '
+       git log --author="Thor" --author="Aster" --format=%s >actual &&
+       {
+           echo third && echo second && echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log with --grep and multiple --author uses all-match' '
+       git log --author="Thor" --author="Night" --grep=i --format=%s >actual &&
+       {
+           echo third && echo initial
+       } >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log with --grep and multiple --author uses all-match' '
+       git log --author="Thor" --author="Night" --grep=q --format=%s >actual &&
+       >expect &&
+       test_cmp expect actual
+'
+
 test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&
index 07c50c764c5ed465b78d2e61ac0b0f9bf58efb64..d1ba25205b86c3a4aeca16ec988e2ed2ecca1da2 100755 (executable)
@@ -201,10 +201,28 @@ test_expect_success $PREREQ 'Prompting works' '
                grep "^To: to@example.com\$" msgtxt1
 '
 
+test_expect_success $PREREQ 'tocmd works' '
+       clean_fake_sendmail &&
+       cp $patches tocmd.patch &&
+       echo tocmd--tocmd@example.com >>tocmd.patch &&
+       {
+         echo "#!$SHELL_PATH"
+         echo sed -n -e s/^tocmd--//p \"\$1\"
+       } > tocmd-sed &&
+       chmod +x tocmd-sed &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to-cmd=./tocmd-sed \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               tocmd.patch \
+               &&
+       grep "^To: tocmd@example.com" msgtxt1
+'
+
 test_expect_success $PREREQ 'cccmd works' '
        clean_fake_sendmail &&
        cp $patches cccmd.patch &&
-       echo cccmd--cccmd@example.com >>cccmd.patch &&
+       echo "cccmd--  cccmd@example.com" >>cccmd.patch &&
        {
          echo "#!$SHELL_PATH"
          echo sed -n -e s/^cccmd--//p \"\$1\"
@@ -947,6 +965,45 @@ test_expect_success $PREREQ '--no-bcc overrides sendemail.bcc' '
        ! grep "RCPT TO:<other@ex.com>" stdout
 '
 
+test_expect_success $PREREQ 'patches To headers are used by default' '
+       patch=`git format-patch -1 --to="bodies@example.com"` &&
+       test_when_finished "rm $patch" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --smtp-server relay.example.com \
+               $patch >stdout &&
+       grep "RCPT TO:<bodies@example.com>" stdout
+'
+
+test_expect_success $PREREQ 'patches To headers are appended to' '
+       patch=`git format-patch -1 --to="bodies@example.com"` &&
+       test_when_finished "rm $patch" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server relay.example.com \
+               $patch >stdout &&
+       grep "RCPT TO:<bodies@example.com>" stdout &&
+       grep "RCPT TO:<nobody@example.com>" stdout
+'
+
+test_expect_success $PREREQ 'To headers from files reset each patch' '
+       patch1=`git format-patch -1 --to="bodies@example.com"` &&
+       patch2=`git format-patch -1 --to="other@example.com" HEAD~` &&
+       test_when_finished "rm $patch1 && rm $patch2" &&
+       git send-email \
+               --dry-run \
+               --from="Example <nobody@example.com>" \
+               --to="nobody@example.com" \
+               --smtp-server relay.example.com \
+               $patch1 $patch2 >stdout &&
+       test $(grep -c "RCPT TO:<bodies@example.com>" stdout) = 1 &&
+       test $(grep -c "RCPT TO:<nobody@example.com>" stdout) = 2 &&
+       test $(grep -c "RCPT TO:<other@example.com>" stdout) = 1
+'
+
 test_expect_success $PREREQ 'setup expect' '
 cat >email-using-8bit <<EOF
 From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
@@ -1032,4 +1089,40 @@ test_expect_success $PREREQ '--8bit-encoding also treats subject' '
        test_cmp expected actual
 '
 
+# Note that the patches in this test are deliberately out of order; we
+# want to make sure it works even if the cover-letter is not in the
+# first mail.
+test_expect_success 'refusing to send cover letter template' '
+       clean_fake_sendmail &&
+       rm -fr outdir &&
+       git format-patch --cover-letter -2 -o outdir &&
+       test_must_fail git send-email \
+         --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         outdir/0002-*.patch \
+         outdir/0000-*.patch \
+         outdir/0001-*.patch \
+         2>errors >out &&
+       grep "SUBJECT HERE" errors &&
+       test -z "$(ls msgtxt*)"
+'
+
+test_expect_success '--force sends cover letter template anyway' '
+       clean_fake_sendmail &&
+       rm -fr outdir &&
+       git format-patch --cover-letter -2 -o outdir &&
+       git send-email \
+         --force \
+         --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         outdir/0002-*.patch \
+         outdir/0000-*.patch \
+         outdir/0001-*.patch \
+         2>errors >out &&
+       ! grep "SUBJECT HERE" errors &&
+       test -n "$(ls msgtxt*)"
+'
+
 test_done
old mode 100644 (file)
new mode 100755 (executable)
index 7c059204e90722db20bae1d10a0988853f852262..3c0cf0509d2cfe3afc0316ed50ea85cb0d2d14de 100755 (executable)
@@ -874,6 +874,27 @@ test_expect_success \
         git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
         compare_diff_raw expect actual'
 
+test_expect_success \
+       'N: copy root directory by tree hash' \
+       'cat >expect <<-\EOF &&
+       :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D      file3/newf
+       :100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D      file3/oldf
+       EOF
+        root=$(git rev-parse refs/heads/branch^0^{tree}) &&
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N6
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy root directory by tree hash
+       COMMIT
+
+       from refs/heads/branch^0
+       M 040000 $root ""
+       INPUT_END
+        git fast-import <input &&
+        git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
+        compare_diff_raw expect actual'
+
 test_expect_success \
        'N: modify copied tree' \
        'cat >expect <<-\EOF &&
index 4f2b9b062b08302aedab42dcdd0a33776559c05e..21cd286bb7abf33c944a8758ce8d702e83ca2022 100755 (executable)
@@ -650,25 +650,26 @@ test_debug 'cat gitweb.log'
 # ----------------------------------------------------------------------
 # syntax highlighting
 
-cat >>gitweb_config.perl <<\EOF
-$feature{'highlight'}{'override'} = 1;
-EOF
 
 highlight --version >/dev/null 2>&1
 if [ $? -eq 127 ]; then
        say "Skipping syntax highlighting test, because 'highlight' was not found"
 else
        test_set_prereq HIGHLIGHT
+       cat >>gitweb_config.perl <<-\EOF
+       our $highlight_bin = "highlight";
+       $feature{'highlight'}{'override'} = 1;
+       EOF
 fi
 
 test_expect_success HIGHLIGHT \
-       'syntax highlighting (no 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)' \
+       'syntax highlighting (highlighted, shell script)' \
        'git config gitweb.highlight yes &&
         echo "#!/usr/bin/sh" > test.sh &&
         git add test.sh &&
index 671f38db2bb78c076b281602ca5b87f10fdf4d00..c15ca2d6479a814d7649a38a671a554b6ed8b68f 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/perl
 use lib (split(/:/, $ENV{GITPERLLIB}));
 
-use 5.006002;
+use 5.008;
 use warnings;
 use strict;
 
index 830e5e7360350a424a2fe6016a03c913209f7557..2af8f10c837fd8c1543cf17d877ae0684cbc61a4 100644 (file)
@@ -970,11 +970,13 @@ case $(uname -s) in
        # no POSIX permissions
        # backslashes in pathspec are converted to '/'
        # exec does not inherit the PID
+       test_set_prereq MINGW
        ;;
 *)
        test_set_prereq POSIXPERM
        test_set_prereq BSLASHPSPEC
        test_set_prereq EXECKEEPSPID
+       test_set_prereq NOT_MINGW
        ;;
 esac
 
index cd659c6fe447b6fcdedee2843113feb358f23849..12c9a88884ec3bb70a1744e44235c578a44e08e6 100644 (file)
@@ -85,6 +85,8 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
 /*
  * Is a tree entry interesting given the pathspec we have?
  *
+ * Pre-condition: baselen == 0 || base[baselen-1] == '/'
+ *
  * Return:
  *  - 2 for "yes, and all subsequent entries will be"
  *  - 1 for yes
@@ -101,7 +103,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
        int never_interesting = -1;
 
        if (!opt->nr_paths)
-               return 1;
+               return 2;
 
        sha1 = tree_entry_extract(desc, &path, &mode);
 
@@ -257,19 +259,12 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
        }
 }
 
-static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
+static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt, int *all_interesting)
 {
-       int all_interesting = 0;
        while (t->size) {
-               int show;
-
-               if (all_interesting)
-                       show = 1;
-               else {
-                       show = tree_entry_interesting(t, base, baselen, opt);
-                       if (show == 2)
-                               all_interesting = 1;
-               }
+               int show = tree_entry_interesting(t, base, baselen, opt);
+               if (show == 2)
+                       *all_interesting = 1;
                if (!show) {
                        update_tree_entry(t);
                        continue;
@@ -284,14 +279,20 @@ static void skip_uninteresting(struct tree_desc *t, const char *base, int basele
 int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
 {
        int baselen = strlen(base);
+       int all_t1_interesting = 0;
+       int all_t2_interesting = 0;
 
        for (;;) {
                if (DIFF_OPT_TST(opt, QUICK) &&
                    DIFF_OPT_TST(opt, HAS_CHANGES))
                        break;
                if (opt->nr_paths) {
-                       skip_uninteresting(t1, base, baselen, opt);
-                       skip_uninteresting(t2, base, baselen, opt);
+                       if (!all_t1_interesting)
+                               skip_uninteresting(t1, base, baselen, opt,
+                                                  &all_t1_interesting);
+                       if (!all_t2_interesting)
+                               skip_uninteresting(t2, base, baselen, opt,
+                                                  &all_t2_interesting);
                }
                if (!t1->size) {
                        if (!t2->size)
index e5522159b362811346b4306246391e960a4e7041..f9e05b548c2fdbc349a1d47d81619e266ec9fb83 100644 (file)
@@ -9,7 +9,23 @@ static int drivers_alloc;
 
 #define PATTERNS(name, pattern, word_regex)                    \
        { name, NULL, -1, { pattern, REG_EXTENDED }, word_regex }
+#define IPATTERN(name, pattern, word_regex)                    \
+       { name, NULL, -1, { pattern, REG_EXTENDED | REG_ICASE }, word_regex }
 static struct userdiff_driver builtin_drivers[] = {
+IPATTERN("fortran",
+        "!^([C*]|[ \t]*!)\n"
+        "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
+        "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA"
+               "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$",
+        /* -- */
+        "[a-zA-Z][a-zA-Z0-9_]*"
+        "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\."
+        /* numbers and format statements like 2E14.4, or ES12.6, 9X.
+         * 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]+"),
 PATTERNS("java",
@@ -101,6 +117,7 @@ PATTERNS("csharp",
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
+#undef IPATTERN
 
 static struct userdiff_driver driver_true = {
        "diff=true",