Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Sep 2008 00:10:08 +0000 (17:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Sep 2008 00:10:08 +0000 (17:10 -0700)
* maint:
Makefile: add merge_recursive.h to LIB_H
Improve documentation for --dirstat diff option
Bring local clone's origin URL in line with that of a remote clone
Documentation: minor cleanup in a use case in 'git stash' manual
Documentation: fix disappeared lines in 'git stash' manpage
Documentation: fix reference to a for-each-ref option

129 files changed:
.gitignore
Documentation/RelNotes-1.6.1.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-checkout.txt
Documentation/git-count-objects.txt
Documentation/git-daemon.txt
Documentation/git-diff-tree.txt
Documentation/git-hash-object.txt
Documentation/git-imap-send.txt
Documentation/git-merge-base.txt
Documentation/git-merge.txt
Documentation/git-submodule.txt
Documentation/git.txt
Documentation/gitattributes.txt
Makefile
RelNotes
builtin-add.c
builtin-blame.c
builtin-checkout.c
builtin-commit-tree.c
builtin-count-objects.c
builtin-diff-tree.c
builtin-help.c [new file with mode: 0644]
builtin-log.c
builtin-merge-base.c
builtin-merge-recursive.c
builtin-merge.c
builtin-push.c
builtin-reflog.c
builtin-revert.c
builtin-update-index.c
cache.h
combine-diff.c
commit.h
compat/mingw.c
compat/mingw.h
config.mak.in
contrib/completion/git-completion.bash
contrib/fast-import/git-p4
ctype.c
daemon.c
diff.c
dir.c
git-compat-util.h
git-filter-branch.sh
git-merge-octopus.sh
git-submodule.sh
hash-object.c
help.c
help.h [new file with mode: 0644]
imap-send.c
log-tree.c
pager.c
read-cache.c
remote.c
remote.h
run-command.c
run-command.h
sha1_file.c
sha1_name.c
t/.gitignore
t/Makefile
t/lib-httpd.sh
t/t0022-crlf-rename.sh
t/t0055-beyond-symlinks.sh [new file with mode: 0755]
t/t1000-read-tree-m-3way.sh
t/t1007-hash-object.sh
t/t3504-cherry-pick-rerere.sh [new file with mode: 0755]
t/t3900-i18n-commit.sh
t/t3901-i18n-patch.sh
t/t4000-diff-format.sh
t/t4001-diff-rename.sh
t/t4002-diff-basic.sh
t/t4003-diff-rename-1.sh
t/t4004-diff-rename-symlink.sh
t/t4005-diff-rename-2.sh
t/t4007-rename-3.sh
t/t4008-diff-break-rewrite.sh
t/t4009-diff-rename-4.sh
t/t4010-diff-pathspec.sh
t/t4011-diff-symlink.sh
t/t4012-diff-binary.sh
t/t4013-diff-various.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4020-diff-external.sh
t/t4022-diff-rewrite.sh
t/t4023-diff-rename-typechange.sh
t/t4027-diff-submodule.sh
t/t4029-diff-trailing-space.sh [new file with mode: 0755]
t/t4100-apply-stat.sh
t/t4101-apply-nonl.sh
t/t5100-mailinfo.sh
t/t5500-fetch-pack.sh
t/t5515-fetch-merge-logic.sh
t/t5540-http-push.sh
t/t6002-rev-list-bisect.sh
t/t6003-rev-list-topo-order.sh
t/t6010-merge-base.sh
t/t6023-merge-file.sh
t/t6027-merge-binary.sh
t/t6101-rev-parse-parents.sh
t/t6200-fmt-merge-msg.sh
t/t7001-mv.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7101-reset.sh
t/t7201-co.sh
t/t7500-commit.sh
t/t7600-merge.sh
t/t7606-merge-custom.sh [new file with mode: 0755]
t/t8001-annotate.sh
t/t8002-blame.sh
t/t9110-git-svn-use-svm-props.sh
t/t9111-git-svn-use-svnsync-props.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9121-git-svn-fetch-renamed-dir.sh
t/t9200-git-cvsexportcommit.sh
t/t9300-fast-import.sh
t/t9301-fast-export.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9700-perl-git.sh
t/t9700/test.pl
t/test-lib.sh
templates/Makefile
xdiff-interface.c
xdiff-interface.h
index a213e8e25bb2442326e86cbfb9ef56319f482869..bbaf9de0bca797508b9c2a011d0d18eac8907157 100644 (file)
@@ -51,6 +51,7 @@ git-gc
 git-get-tar-commit-id
 git-grep
 git-hash-object
+git-help
 git-http-fetch
 git-http-push
 git-imap-send
diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt
new file mode 100644 (file)
index 0000000..d37da03
--- /dev/null
@@ -0,0 +1,58 @@
+GIT v1.6.1 Release Notes
+========================
+
+Updates since v1.6.0
+--------------------
+
+When some commands (e.g. "git log", "git diff") spawn pager internally, we
+used to make the pager the parent process of the git command that produces
+output.  This meant that the exit status of the whole thing comes from the
+pager, not the underlying git command.  We swapped the order of the
+processes around and you will see the exit code from the command from now
+on.
+
+(subsystems)
+
+* ...
+
+(portability)
+
+* ...
+
+(documentation)
+
+* ...
+
+(performance)
+
+* The underlying diff machinery to produce textual output has been
+  optimized, which would result in faster "git blame" processing.
+
+(usability, bells and whistles)
+
+* "git checkout --track origin/hack" used to be a syntax error.  It now
+  DWIMs to create a corresponding local branch "hack", i.e. acts as if you
+  said "git checkout --track -b hack origin/hack".
+
+* "git diff" learned to mimick --suppress-blank-empty from GNU diff via a
+  configuration option.
+
+* "git imap-send" can optionally talk SSL.
+
+(internal)
+
+* "git hash-object" learned to lie about the path being hashed, so that
+  correct gitattributes processing can be done while hashing contents
+  stored in a temporary file.
+
+Fixes since v1.6.0
+------------------
+
+All of the fixes in v1.6.0.X maintenance series are included in this
+release, unless otherwise noted.
+
+--
+exec >/var/tmp/1
+O=v1.6.0-48-ge28a867
+echo O=$(git describe master)
+git shortlog --no-merges $O..master ^maint
index 841bead9db18a025638570c10cac72bcf4791f68..a1e9100f9e3ccb8466ec603e154cb230dc2cb33b 100644 (file)
@@ -71,7 +71,7 @@ run git diff --check on your changes before you commit.
 
 (1a) Try to be nice to older C compilers
 
-We try to support wide range of C compilers to compile
+We try to support wide range of C compilers to compile
 git with. That means that you should not use C99 initializers, even
 if a lot of compilers grok it.
 
index 3727239891321a52d16fa7f9ed73bf172b758c48..ed3285f89927e1d988527ae5b67a5bea357d30e0 100644 (file)
@@ -572,6 +572,10 @@ diff.autorefreshindex::
        affects only 'git-diff' Porcelain, and not lower level
        'diff' commands, such as 'git-diff-files'.
 
+diff.suppress-blank-empty::
+       A boolean to inhibit the standard behavior of printing a space
+       before each empty output line. Defaults to false.
+
 diff.external::
        If this config variable is set, diff generation is not
        performed using the internal diff machinery, but using the
index 746646bb3d09a16a5880fa6bff286389aae98870..6e268326da9e34882313b7b3d094a617c3966d29 100644 (file)
@@ -106,9 +106,9 @@ endif::git-format-patch[]
        --exit-code.
 
 --full-index::
-       Instead of the first handful characters, show full
-       object name of pre- and post-image blob on the "index"
-       line when generating patch format output.
+       Instead of the first handful of characters, show the full
+       pre- and post-image blob object names on the "index"
+       line when generating patch format output.
 
 --binary::
        In addition to --full-index, output "binary diff" that
index 5aa69c0e12a6756fd6f79c117008a373f65ba5f5..be54a0299fb2b6849993aa27d18c829ce91e7e00 100644 (file)
@@ -8,7 +8,7 @@ git-checkout - Checkout a branch or paths to the working tree
 SYNOPSIS
 --------
 [verse]
-'git checkout' [-q] [-f] [[--track | --no-track] -b <new_branch> [-l]] [-m] [<branch>]
+'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>]
 'git checkout' [<tree-ish>] [--] <paths>...
 
 DESCRIPTION
@@ -21,6 +21,10 @@ specified, <new_branch>.  Using -b will cause <new_branch> to
 be created; in this case you can use the --track or --no-track
 options, which will be passed to `git branch`.
 
+As a convenience, --track will default to create a branch whose
+name is constructed from the specified branch name by stripping
+the first namespace level.
+
 When <paths> are given, this command does *not* switch
 branches.  It updates the named paths in the working tree from
 the index file (i.e. it runs `git checkout-index -f -u`), or
@@ -59,6 +63,17 @@ OPTIONS
        'git-checkout' and 'git-branch' to always behave as if '--no-track' were
        given. Set it to `always` if you want this behavior when the
        start-point is either a local or remote branch.
++
+If no '-b' option was given, the name of the new branch will be
+derived from the remote branch, by attempting to guess the name
+of the branch on remote system.  If "remotes/" or "refs/remotes/"
+are prefixed, it is stripped away, and then the part up to the
+next slash (which would be the nickname of the remote) is removed.
+This would tell us to use "hack" as the local branch when branching
+off of "origin/hack" (or "remotes/origin/hack", or even
+"refs/remotes/origin/hack").  If the given name has no slash, or the above
+guessing results in an empty name, the guessing is aborted.  You can
+exlicitly give a name with '-b' in such a case.
 
 --no-track::
        Ignore the branch.autosetupmerge configuration variable.
index 75a8da1ca906aee4cc6a7d0c3ff19862b8e0fc2f..6bc1c21e6283284e2eae16d7ec4cb8183d8e0851 100644 (file)
@@ -21,8 +21,9 @@ OPTIONS
 --verbose::
        In addition to the number of loose objects and disk
        space consumed, it reports the number of in-pack
-       objects, number of packs, and number of objects that can be
-       removed by running `git prune-packed`.
+       objects, number of packs, disk space consumed by those packs,
+       and number of objects that can be removed by running
+       `git prune-packed`.
 
 
 Author
index 4ba4b75c1126d87c48935e7697e05f0d5210ad2d..b08a08cd95b6da192a008c58d7973769dfe3fc8c 100644 (file)
@@ -9,8 +9,9 @@ SYNOPSIS
 --------
 [verse]
 'git daemon' [--verbose] [--syslog] [--export-all]
-            [--timeout=n] [--init-timeout=n] [--strict-paths]
-            [--base-path=path] [--user-path | --user-path=path]
+            [--timeout=n] [--init-timeout=n] [--max-connections=n]
+            [--strict-paths] [--base-path=path] [--base-path-relaxed]
+            [--user-path | --user-path=path]
             [--interpolated-path=pathtemplate]
             [--reuseaddr] [--detach] [--pid-file=file]
             [--enable=service] [--disable=service]
@@ -99,6 +100,10 @@ OPTIONS
        it takes for the server to process the sub-request and time spent
        waiting for next client's request.
 
+--max-connections::
+       Maximum number of concurrent clients, defaults to 32.  Set it to
+       zero for no limit.
+
 --syslog::
        Log to syslog instead of stderr. Note that this option does not imply
        --verbose, thus by default only error conditions will be logged.
index 1fdf20dcc9169be2c7a51b32f94893442e160436..5d48664e624f59e0df16121ed67d84939f6d2a31 100644 (file)
@@ -49,13 +49,22 @@ include::diff-options.txt[]
 --stdin::
        When '--stdin' is specified, the command does not take
        <tree-ish> arguments from the command line.  Instead, it
-       reads either one <commit> or a list of <commit>
-       separated with a single space from its standard input.
+       reads lines containing either two <tree>, one <commit>, or a
+       list of <commit> from its standard input.  (Use a single space
+       as separator.)
 +
-When a single commit is given on one line of such input, it compares
-the commit with its parents.  The following flags further affects its
-behavior.  The remaining commits, when given, are used as if they are
+When two trees are given, it compares the first tree with the second.
+When a single commit is given, it compares the commit with its
+parents.  The remaining commits, when given, are used as if they are
 parents of the first commit.
++
+When comparing two trees, the ID of both trees (separated by a space
+and terminated by a newline) is printed before the difference.  When
+comparing commits, the ID of the first (or only) commit, followed by a
+newline, is printed.
++
+The following flags further affects the behavior when comparing
+commits (but not trees).
 
 -m::
        By default, 'git-diff-tree --stdin' does not show
index ac928e198e75595a6fcd4e83b89aaf68987bd420..0af40cfb85ca6e0eb6e540f0beb47e449ef25afd 100644 (file)
@@ -8,7 +8,9 @@ git-hash-object - Compute object ID and optionally creates a blob from a file
 
 SYNOPSIS
 --------
-'git hash-object' [-t <type>] [-w] [--stdin | --stdin-paths] [--] <file>...
+[verse]
+'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...
+'git hash-object' [-t <type>] [-w] --stdin-paths < <list-of-paths>
 
 DESCRIPTION
 -----------
@@ -35,6 +37,22 @@ OPTIONS
 --stdin-paths::
        Read file names from stdin instead of from the command-line.
 
+--path::
+       Hash object as it were located at the given path. The location of
+       file does not directly influence on the hash value, but path is
+       used to determine what git filters should be applied to the object
+       before it can be placed to the object database, and, as result of
+       applying filters, the actual blob put into the object database may
+       differ from the given file. This option is mainly useful for hashing
+       temporary files located outside of the working directory or files
+       read from stdin.
+
+--no-filters::
+       Hash the contents as is, ignoring any input filter that would
+       have been chosen by the attributes mechanism, including crlf
+       conversion. If the file is read from standard input then this
+       is always implied, unless the --path option is given.
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>
index b3d8da33ee64730794821440c287f30c4bb85789..bd49a0aee8881f983077d74bd8d0fbe5764dfea5 100644 (file)
@@ -3,7 +3,7 @@ git-imap-send(1)
 
 NAME
 ----
-git-imap-send - Dump a mailbox from stdin into an imap folder
+git-imap-send - Send a collection of patches from stdin to an IMAP folder
 
 
 SYNOPSIS
@@ -13,9 +13,9 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-This command uploads a mailbox generated with git-format-patch
-into an imap drafts folder.  This allows patches to be sent as
-other email is sent with mail clients that cannot read mailbox
+This command uploads a mailbox generated with 'git-format-patch'
+into an IMAP drafts folder.  This allows patches to be sent as
+other email is when using mail clients that cannot read mailbox
 files directly.
 
 Typical usage is something like:
@@ -26,21 +26,75 @@ git format-patch --signoff --stdout --attach origin | git imap-send
 CONFIGURATION
 -------------
 
-'git-imap-send' requires the following values in the repository
-configuration file (shown with examples):
+To use the tool, imap.folder and either imap.tunnel or imap.host must be set
+to appropriate values.
+
+Variables
+~~~~~~~~~
+
+imap.folder::
+       The folder to drop the mails into, which is typically the Drafts
+       folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
+       "[Gmail]/Drafts". Required to use imap-send.
+
+imap.tunnel::
+       Command used to setup a tunnel to the IMAP server through which
+       commands will be piped instead of using a direct network connection
+       to the server. Required when imap.host is not set to use imap-send.
+
+imap.host::
+       A URL identifying the server. Use a `imap://` prefix for non-secure
+       connections and a `imaps://` prefix for secure connections.
+       Ignored when imap.tunnel is set, but required to use imap-send
+       otherwise.
+
+imap.user::
+       The username to use when logging in to the server.
+
+imap.password::
+       The password to use when logging in to the server.
+
+imap.port::
+       An integer port number to connect to on the server.
+       Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
+       Ignored when imap.tunnel is set.
+
+imap.sslverify::
+       A boolean to enable/disable verification of the server certificate
+       used by the SSL/TLS connection. Default is `true`. Ignored when
+       imap.tunnel is set.
+
+Examples
+~~~~~~~~
+
+Using tunnel mode:
 
 ..........................
 [imap]
-    Folder = "INBOX.Drafts"
+    folder = "INBOX.Drafts"
+    tunnel = "ssh -q -C user@example.com /usr/bin/imapd ./Maildir 2> /dev/null"
+..........................
 
+Using direct mode:
+
+.........................
 [imap]
-    Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null"
+    folder = "INBOX.Drafts"
+    host = imap://imap.example.com
+    user = bob
+    pass = p4ssw0rd
+..........................
+
+Using direct mode with SSL:
 
+.........................
 [imap]
-    Host = imap.server.com
-    User = bob
-    Pass = pwd
-    Port = 143
+    folder = "INBOX.Drafts"
+    host = imaps://imap.example.com
+    user = bob
+    pass = p4ssw0rd
+    port = 123
+    sslverify = false
 ..........................
 
 
index 1a7ecbf8f39381b0c7b2da513dfa26eacec15cf6..2f0c5259e01917e456954de5eb59597a2c829b0c 100644 (file)
@@ -8,26 +8,81 @@ git-merge-base - Find as good common ancestors as possible for a merge
 
 SYNOPSIS
 --------
-'git merge-base' [--all] <commit> <commit>
+'git merge-base' [--all] <commit> <commit>...
 
 DESCRIPTION
 -----------
 
-'git-merge-base' finds as good a common ancestor as possible between
-the two commits. That is, given two commits A and B, `git merge-base A
-B` will output a commit which is reachable from both A and B through
-the parent relationship.
+'git-merge-base' finds best common ancestor(s) between two commits to use
+in a three-way merge.  One common ancestor is 'better' than another common
+ancestor if the latter is an ancestor of the former.  A common ancestor
+that does not have any better common ancestor than it is a 'best common
+ancestor', i.e. a 'merge base'.  Note that there can be more than one
+merge bases between two commits.
 
-Given a selection of equally good common ancestors it should not be
-relied on to decide in any particular way.
-
-The 'git-merge-base' algorithm is still in flux - use the source...
+Among the two commits to compute their merge bases, one is specified by
+the first commit argument on the command line; the other commit is a
+(possibly hypothetical) commit that is a merge across all the remaining
+commits on the command line.  As the most common special case, giving only
+two commits from the command line means computing the merge base between
+the given two commits.
 
 OPTIONS
 -------
 --all::
-       Output all common ancestors for the two commits instead of
-       just one.
+       Output all merge bases for the commits, instead of just one.
+
+DISCUSSION
+----------
+
+Given two commits 'A' and 'B', `git merge-base A B` will output a commit
+which is reachable from both 'A' and 'B' through the parent relationship.
+
+For example, with this topology:
+
+                o---o---o---B
+               /
+       ---o---1---o---o---o---A
+
+the merge base between 'A' and 'B' is '1'.
+
+Given three commits 'A', 'B' and 'C', `git merge-base A B C` will compute the
+merge base between 'A' and an hypothetical commit 'M', which is a merge
+between 'B' and 'C'.  For example, with this topology:
+
+              o---o---o---o---C
+             /
+            /   o---o---o---B
+           /   /
+       ---2---1---o---o---o---A
+
+the result of `git merge-base A B C` is '1'.  This is because the
+equivalent topology with a merge commit 'M' between 'B' and 'C' is:
+
+
+              o---o---o---o---o
+             /                 \
+            /   o---o---o---o---M
+           /   /
+       ---2---1---o---o---o---A
+
+and the result of `git merge-base A M` is '1'.  Commit '2' is also a
+common ancestor between 'A' and 'M', but '1' is a better common ancestor,
+because '2' is an ancestor of '1'.  Hence, '2' is not a merge base.
+
+When the history involves criss-cross merges, there can be more than one
+'best' common ancestors between two commits.  For example, with this
+topology:
+
+       ---1---o---A
+          \ /
+           X
+          / \
+       ---2---o---o---B
+
+both '1' and '2' are merge-base of A and B.  Neither one is better than
+the other (both are 'best' merge base).  When `--all` option is not given,
+it is unspecified which best one is output.
 
 Author
 ------
index 17a15acb07df2d8beed4a41cdcf820010f95b35b..685e1fed586cb74a50b95d10fff6c8773cc7c8e7 100644 (file)
@@ -126,13 +126,25 @@ After seeing a conflict, you can do two things:
    up working tree changes made by 2. and 3.; 'git-reset --hard' can
    be used for this.
 
- * Resolve the conflicts.  `git diff` would report only the
-   conflicting paths because of the above 2. and 3.
-   Edit the working tree files into a desirable shape
-   ('git mergetool' can ease this task), 'git-add' or 'git-rm'
-   them, to make the index file contain what the merge result
-   should be, and run 'git-commit' to commit the result.
+ * Resolve the conflicts.  Git will mark the conflicts in
+   the working tree.  Edit the files into shape and
+   'git-add' to the index.  'git-commit' to seal the deal.
 
+You can work through the conflict with a number of tools:
+
+ * Use a mergetool.  'git mergetool' to launch a graphical
+   mergetool which will work you through the merge.
+
+ * Look at the diffs.  'git diff' will show a three-way diff,
+   highlighting changes from both the HEAD and remote versions.
+
+ * Look at the diffs on their own. 'git log --merge -p <path>'
+   will show diffs first for the HEAD version and then the
+   remote version.
+
+ * Look at the originals.  'git show :1:filename' shows the
+   common ancestor, 'git show :2:filename' shows the HEAD
+   version and 'git show :3:filename' shows the remote version.
 
 SEE ALSO
 --------
index bf33b0cba05e8858e28275661c731aae6c8372ee..babaa9bc46a404c4610abc4e4c4281b6c5c5ea6c 100644 (file)
@@ -14,6 +14,8 @@ SYNOPSIS
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] update [--init] [--] [<path>...]
 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] foreach <command>
+'git submodule' [--quiet] sync [--] [<path>...]
 
 
 DESCRIPTION
@@ -123,6 +125,30 @@ summary::
        in the submodule between the given super project commit and the
        index or working tree (switched by --cached) are shown.
 
+foreach::
+       Evaluates an arbitrary shell command in each checked out submodule.
+       The command has access to the variables $path and $sha1:
+       $path is the name of the submodule directory relative to the
+       superproject, and $sha1 is the commit as recorded in the superproject.
+       Any submodules defined in the superproject but not checked out are
+       ignored by this command. Unless given --quiet, foreach prints the name
+       of each submodule before evaluating the command.
+       A non-zero return from the command in any submodule causes
+       the processing to terminate. This can be overridden by adding '|| :'
+       to the end of the command.
++
+As an example, "git submodule foreach 'echo $path `git rev-parse HEAD`' will
+show the path and currently checked out commit for each submodule.
+
+sync::
+       Synchronizes submodules' remote URL configuration setting
+       to the value specified in .gitmodules.  This is useful when
+       submodule URLs change upstream and you need to update your local
+       repositories accordingly.
++
+"git submodule sync" synchronizes all submodules while
+"git submodule sync -- A" synchronizes submodule "A" only.
+
 OPTIONS
 -------
 -q::
index 363a785452ec13c3e8bd8bd744eb4a4db7fcc991..e178fb581323adaa5f490fb67732f98147ec460f 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.0/git.html[documentation for release 1.6.0]
+* link:v1.6.1/git.html[documentation for release 1.6.1]
 
 * release notes for
+  link:RelNotes-1.6.1.txt[1.6.1],
   link:RelNotes-1.6.0.txt[1.6.0].
 
 * link:v1.5.6.5/git.html[documentation for release 1.5.6.5]
index 49a167f241f0832e62fb905390715e45ef5be3c4..5fb5007413666d0b3d0aaa4c553c1fd5a67e60d9 100644 (file)
@@ -315,10 +315,14 @@ patterns are available:
 
 - `pascal` suitable for source code in the Pascal/Delphi language.
 
+- `python` suitable for source code in the Python language.
+
 - `ruby` suitable for source code in the Ruby language.
 
 - `tex` suitable for source code for LaTeX documents.
 
+- `html` suitable for HTML/XHTML documents.
+
 
 Performing a three-way merge
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 672ea74233537ffce0e1b35a6ace6a06ece747ec..dfed7bae9439675bb734c0f9fb3852ecb01fdf6d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -124,6 +124,9 @@ all::
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-index perspective.
 #
+# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
+# field that counts the on-disk footprint in 512-byte blocks.
+#
 # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
 #
 # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
@@ -354,6 +357,7 @@ LIB_H += git-compat-util.h
 LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
+LIB_H += help.h
 LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
@@ -518,6 +522,7 @@ BUILTIN_OBJS += builtin-for-each-ref.o
 BUILTIN_OBJS += builtin-fsck.o
 BUILTIN_OBJS += builtin-gc.o
 BUILTIN_OBJS += builtin-grep.o
+BUILTIN_OBJS += builtin-help.o
 BUILTIN_OBJS += builtin-init-db.o
 BUILTIN_OBJS += builtin-log.o
 BUILTIN_OBJS += builtin-ls-files.o
@@ -575,9 +580,11 @@ EXTLIBS =
 
 ifeq ($(uname_S),Linux)
        NO_STRLCPY = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
        NO_STRLCPY = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),UnixWare)
        CC = cc
@@ -675,6 +682,7 @@ ifeq ($(uname_S),FreeBSD)
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
        DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@ -682,6 +690,7 @@ ifeq ($(uname_S),OpenBSD)
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
+       THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),NetBSD)
        ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
@@ -690,6 +699,7 @@ ifeq ($(uname_S),NetBSD)
        BASIC_CFLAGS += -I/usr/pkg/include
        BASIC_LDFLAGS += -L/usr/pkg/lib
        ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
+       THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
@@ -750,6 +760,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_SVN_TESTS = YesPlease
        NO_PERL_MAKEMAKER = YesPlease
        NO_POSIX_ONLY_PROGRAMS = YesPlease
+       NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
        COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
@@ -864,6 +875,9 @@ endif
 ifdef NO_D_INO_IN_DIRENT
        BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
 endif
+ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+       BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
+endif
 ifdef NO_C99_FORMAT
        BASIC_CFLAGS += -DNO_C99_FORMAT
 endif
@@ -1092,7 +1106,7 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
-help.o: help.c common-cmds.h GIT-CFLAGS
+builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
                '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
                '-DGIT_MAN_PATH="$(mandir_SQ)"' \
@@ -1219,7 +1233,9 @@ endif
 git-%$X: %.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
-git-imap-send$X: imap-send.o $(LIB_FILE)
+git-imap-send$X: imap-send.o $(GITLIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+               $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
 
 http.o http-walker.o http-push.o transport.o: http.h
 
@@ -1349,7 +1365,7 @@ install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-       $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)'
+       $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_TCLTK
index 8f32997335aab2ba3d8c2eee2af8fe745dae5fcc..3d420845b117b3f3eb82d1f0948a61c7989d8af9 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.0.1.txt
\ No newline at end of file
+Documentation/RelNotes-1.6.1.txt
\ No newline at end of file
index fc3f96eaefff91e4e85adb92162716939f0ecd72..7c874e31154a4c3f96b3403db1bdcb0b36fdec3e 100644 (file)
@@ -8,10 +8,6 @@
 #include "dir.h"
 #include "exec_cmd.h"
 #include "cache-tree.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "commit.h"
-#include "revision.h"
 #include "run-command.h"
 #include "parse-options.h"
 
@@ -22,6 +18,27 @@ static const char * const builtin_add_usage[] = {
 static int patch_interactive = 0, add_interactive = 0;
 static int take_worktree_changes;
 
+static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
+{
+       int num_unmatched = 0, i;
+
+       /*
+        * Since we are walking the index as if we are warlking the directory,
+        * we have to mark the matched pathspec as seen; otherwise we will
+        * mistakenly think that the user gave a pathspec that did not match
+        * anything.
+        */
+       for (i = 0; i < specs; i++)
+               if (!seen[i])
+                       num_unmatched++;
+       if (!num_unmatched)
+               return;
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
+       }
+}
+
 static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
 {
        char *seen;
@@ -41,6 +58,7 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
                        *dst++ = entry;
        }
        dir->nr = dst - dir->entries;
+       fill_pathspec_matches(pathspec, seen, specs);
 
        for (i = 0; i < specs; i++) {
                if (!seen[i] && !file_exists(pathspec[i]))
@@ -79,59 +97,6 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
                prune_directory(dir, pathspec, baselen);
 }
 
-struct update_callback_data
-{
-       int flags;
-       int add_errors;
-};
-
-static void update_callback(struct diff_queue_struct *q,
-                           struct diff_options *opt, void *cbdata)
-{
-       int i;
-       struct update_callback_data *data = cbdata;
-
-       for (i = 0; i < q->nr; i++) {
-               struct diff_filepair *p = q->queue[i];
-               const char *path = p->one->path;
-               switch (p->status) {
-               default:
-                       die("unexpected diff status %c", p->status);
-               case DIFF_STATUS_UNMERGED:
-               case DIFF_STATUS_MODIFIED:
-               case DIFF_STATUS_TYPE_CHANGED:
-                       if (add_file_to_cache(path, data->flags)) {
-                               if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
-                                       die("updating files failed");
-                               data->add_errors++;
-                       }
-                       break;
-               case DIFF_STATUS_DELETED:
-                       if (!(data->flags & ADD_CACHE_PRETEND))
-                               remove_file_from_cache(path);
-                       if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
-                               printf("remove '%s'\n", path);
-                       break;
-               }
-       }
-}
-
-int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
-{
-       struct update_callback_data data;
-       struct rev_info rev;
-       init_revisions(&rev, prefix);
-       setup_revisions(0, NULL, &rev, NULL);
-       rev.prune_data = pathspec;
-       rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
-       rev.diffopt.format_callback = update_callback;
-       data.flags = flags;
-       data.add_errors = 0;
-       rev.diffopt.format_callback_data = &data;
-       run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-       return !!data.add_errors;
-}
-
 static void refresh(int verbose, const char **pathspec)
 {
        char *seen;
@@ -153,6 +118,16 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
 {
        const char **pathspec = get_pathspec(prefix, argv);
 
+       if (pathspec) {
+               const char **p;
+               for (p = pathspec; *p; p++) {
+                       if (has_symlink_leading_path(strlen(*p), *p)) {
+                               int len = prefix ? strlen(prefix) : 0;
+                               die("'%s' is beyond a symbolic link", *p + len);
+                       }
+               }
+       }
+
        return pathspec;
 }
 
@@ -258,7 +233,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        if (addremove && take_worktree_changes)
                die("-A and -u are mutually incompatible");
-       if (addremove && !argc) {
+       if ((addremove || take_worktree_changes) && !argc) {
                static const char *here[2] = { ".", NULL };
                argc = 1;
                argv = here;
@@ -271,33 +246,30 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
        flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
                 (show_only ? ADD_CACHE_PRETEND : 0) |
-                (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0));
+                (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
+                (!(addremove || take_worktree_changes)
+                 ? ADD_CACHE_IGNORE_REMOVAL : 0));
 
        if (require_pathspec && argc == 0) {
                fprintf(stderr, "Nothing specified, nothing added.\n");
                fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
                return 0;
        }
-       pathspec = get_pathspec(prefix, argv);
-
-       /*
-        * If we are adding new files, we need to scan the working
-        * tree to find the ones that match pathspecs; this needs
-        * to be done before we read the index.
-        */
-       if (add_new_files)
-               fill_directory(&dir, pathspec, ignored_too);
+       pathspec = validate_pathspec(argc, argv, prefix);
 
        if (read_cache() < 0)
                die("index file corrupt");
 
+       if (add_new_files)
+               /* This picks up the paths that are not tracked */
+               fill_directory(&dir, pathspec, ignored_too);
+
        if (refresh_only) {
                refresh(verbose, pathspec);
                goto finish;
        }
 
-       if (take_worktree_changes || addremove)
-               exit_status |= add_files_to_cache(prefix, pathspec, flags);
+       exit_status |= add_files_to_cache(prefix, pathspec, flags);
 
        if (add_new_files)
                exit_status |= add_files(&dir, flags);
index 4ea343189fdad035318e94ce0c6c2ec16b62ba7f..e4d12de8a994434058745439a86929c0d556a3ec 100644 (file)
@@ -465,7 +465,6 @@ struct patch {
 };
 
 struct blame_diff_state {
-       struct xdiff_emit_state xm;
        struct patch *ret;
        unsigned hunk_post_context;
        unsigned hunk_in_pre_context : 1;
@@ -528,15 +527,12 @@ static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
        xpp.flags = xdl_opts;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = context;
-       ecb.outf = xdiff_outf;
-       ecb.priv = &state;
        memset(&state, 0, sizeof(state));
-       state.xm.consume = process_u_diff;
        state.ret = xmalloc(sizeof(struct patch));
        state.ret->chunks = NULL;
        state.ret->num = 0;
 
-       xdi_diff(file_p, file_o, &xpp, &xecfg, &ecb);
+       xdi_diff_outf(file_p, file_o, process_u_diff, &state, &xpp, &xecfg, &ecb);
 
        if (state.ret->num) {
                struct chunk *chunk;
index 411cc513c65ba854221ad52dd6aeaaac7d213c9d..b380ad6e80046e77ed78a438c26f507da2737b0e 100644 (file)
@@ -157,7 +157,7 @@ struct checkout_opts {
        int force;
        int writeout_error;
 
-       char *new_branch;
+       const char *new_branch;
        int new_branch_log;
        enum branch_track track;
 };
@@ -437,13 +437,28 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
        git_config(git_default_config, NULL);
 
-       opts.track = git_branch_track;
+       opts.track = BRANCH_TRACK_UNSPECIFIED;
 
        argc = parse_options(argc, argv, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
-       if (!opts.new_branch && (opts.track != git_branch_track))
-               die("git checkout: --track and --no-track require -b");
+       /* --track without -b should DWIM */
+       if (0 < opts.track && !opts.new_branch) {
+               const char *argv0 = argv[0];
+               if (!argc || !strcmp(argv0, "--"))
+                       die ("--track needs a branch name");
+               if (!prefixcmp(argv0, "refs/"))
+                       argv0 += 5;
+               if (!prefixcmp(argv0, "remotes/"))
+                       argv0 += 8;
+               argv0 = strchr(argv0, '/');
+               if (!argv0 || !argv0[1])
+                       die ("Missing branch name; try -b");
+               opts.new_branch = argv0 + 1;
+       }
+
+       if (opts.track == BRANCH_TRACK_UNSPECIFIED)
+               opts.track = git_branch_track;
 
        if (opts.force && opts.merge)
                die("git checkout: -f and -m are incompatible");
index 7a9a309be0543da7d27e7710ef82271f2582e0a9..f773db596c50430fa5223e3273b4fade32a0da34 100644 (file)
@@ -48,6 +48,7 @@ static const char commit_utf8_warn[] =
 int commit_tree(const char *msg, unsigned char *tree,
                struct commit_list *parents, unsigned char *ret)
 {
+       int result;
        int encoding_is_utf8;
        struct strbuf buffer;
 
@@ -86,7 +87,9 @@ int commit_tree(const char *msg, unsigned char *tree,
        if (encoding_is_utf8 && !is_utf8(buffer.buf))
                fprintf(stderr, commit_utf8_warn);
 
-       return write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+       result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+       strbuf_release(&buffer);
+       return result;
 }
 
 int cmd_commit_tree(int argc, const char **argv, const char *prefix)
index 91b5487478998e39bb8ae4a5cb667360cff82c9a..6e80fe7c016788c178e206c61f5ef3c1fdea506c 100644 (file)
@@ -43,7 +43,7 @@ static void count_objects(DIR *d, char *path, int len, int verbose,
                        if (lstat(path, &st) || !S_ISREG(st.st_mode))
                                bad = 1;
                        else
-                               (*loose_size) += xsize_t(st.st_blocks);
+                               (*loose_size) += xsize_t(on_disk_bytes(st));
                }
                if (bad) {
                        if (verbose) {
@@ -104,6 +104,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
        if (verbose) {
                struct packed_git *p;
                unsigned long num_pack = 0;
+               unsigned long size_pack = 0;
                if (!packed_git)
                        prepare_packed_git();
                for (p = packed_git; p; p = p->next) {
@@ -112,12 +113,14 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
                        if (open_pack_index(p))
                                continue;
                        packed += p->num_objects;
+                       size_pack += p->pack_size + p->index_size;
                        num_pack++;
                }
                printf("count: %lu\n", loose);
-               printf("size: %lu\n", loose_size / 2);
+               printf("size: %lu\n", loose_size / 1024);
                printf("in-pack: %lu\n", packed);
                printf("packs: %lu\n", num_pack);
+               printf("size-pack: %lu\n", size_pack / 1024);
                printf("prune-packable: %lu\n", packed_loose);
                printf("garbage: %lu\n", garbage);
        }
index 415cb1612f5322da89850874ba81885e41808678..1138c2da733bfe2730bbcb95d2a281de12595bde 100644 (file)
@@ -14,20 +14,10 @@ static int diff_tree_commit_sha1(const unsigned char *sha1)
        return log_tree_commit(&log_tree_opt, commit);
 }
 
-static int diff_tree_stdin(char *line)
+/* Diff one or more commits. */
+static int stdin_diff_commit(struct commit *commit, char *line, int len)
 {
-       int len = strlen(line);
        unsigned char sha1[20];
-       struct commit *commit;
-
-       if (!len || line[len-1] != '\n')
-               return -1;
-       line[len-1] = 0;
-       if (get_sha1_hex(line, sha1))
-               return -1;
-       commit = lookup_commit(sha1);
-       if (!commit || parse_commit(commit))
-               return -1;
        if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
                /* Graft the fake parents locally to the commit */
                int pos = 41;
@@ -52,6 +42,48 @@ static int diff_tree_stdin(char *line)
        return log_tree_commit(&log_tree_opt, commit);
 }
 
+/* Diff two trees. */
+static int stdin_diff_trees(struct tree *tree1, char *line, int len)
+{
+       unsigned char sha1[20];
+       struct tree *tree2;
+       if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1))
+               return error("Need exactly two trees, separated by a space");
+       tree2 = lookup_tree(sha1);
+       if (!tree2 || parse_tree(tree2))
+               return -1;
+       printf("%s %s\n", sha1_to_hex(tree1->object.sha1),
+                         sha1_to_hex(tree2->object.sha1));
+       diff_tree_sha1(tree1->object.sha1, tree2->object.sha1,
+                      "", &log_tree_opt.diffopt);
+       log_tree_diff_flush(&log_tree_opt);
+       return 0;
+}
+
+static int diff_tree_stdin(char *line)
+{
+       int len = strlen(line);
+       unsigned char sha1[20];
+       struct object *obj;
+
+       if (!len || line[len-1] != '\n')
+               return -1;
+       line[len-1] = 0;
+       if (get_sha1_hex(line, sha1))
+               return -1;
+       obj = lookup_object(sha1);
+       obj = obj ? obj : parse_object(sha1);
+       if (!obj)
+               return -1;
+       if (obj->type == OBJ_COMMIT)
+               return stdin_diff_commit((struct commit *)obj, line, len);
+       if (obj->type == OBJ_TREE)
+               return stdin_diff_trees((struct tree *)obj, line, len);
+       error("Object %s is a %s, not a commit or tree",
+             sha1_to_hex(sha1), typename(obj->type));
+       return -1;
+}
+
 static const char diff_tree_usage[] =
 "git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
 "[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
diff --git a/builtin-help.c b/builtin-help.c
new file mode 100644 (file)
index 0000000..721038e
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * builtin-help.c
+ *
+ * Builtin help command
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "common-cmds.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "help.h"
+
+static struct man_viewer_list {
+       struct man_viewer_list *next;
+       char name[FLEX_ARRAY];
+} *man_viewer_list;
+
+static struct man_viewer_info_list {
+       struct man_viewer_info_list *next;
+       const char *info;
+       char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
+enum help_format {
+       HELP_FORMAT_MAN,
+       HELP_FORMAT_INFO,
+       HELP_FORMAT_WEB,
+};
+
+static int show_all = 0;
+static enum help_format help_format = HELP_FORMAT_MAN;
+static struct option builtin_help_options[] = {
+       OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
+       OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
+       OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
+                       HELP_FORMAT_WEB),
+       OPT_SET_INT('i', "info", &help_format, "show info page",
+                       HELP_FORMAT_INFO),
+       OPT_END(),
+};
+
+static const char * const builtin_help_usage[] = {
+       "git help [--all] [--man|--web|--info] [command]",
+       NULL
+};
+
+static enum help_format parse_help_format(const char *format)
+{
+       if (!strcmp(format, "man"))
+               return HELP_FORMAT_MAN;
+       if (!strcmp(format, "info"))
+               return HELP_FORMAT_INFO;
+       if (!strcmp(format, "web") || !strcmp(format, "html"))
+               return HELP_FORMAT_WEB;
+       die("unrecognized help format '%s'", format);
+}
+
+static const char *get_man_viewer_info(const char *name)
+{
+       struct man_viewer_info_list *viewer;
+
+       for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
+       {
+               if (!strcasecmp(name, viewer->name))
+                       return viewer->info;
+       }
+       return NULL;
+}
+
+static int check_emacsclient_version(void)
+{
+       struct strbuf buffer = STRBUF_INIT;
+       struct child_process ec_process;
+       const char *argv_ec[] = { "emacsclient", "--version", NULL };
+       int version;
+
+       /* emacsclient prints its version number on stderr */
+       memset(&ec_process, 0, sizeof(ec_process));
+       ec_process.argv = argv_ec;
+       ec_process.err = -1;
+       ec_process.stdout_to_stderr = 1;
+       if (start_command(&ec_process)) {
+               fprintf(stderr, "Failed to start emacsclient.\n");
+               return -1;
+       }
+       strbuf_read(&buffer, ec_process.err, 20);
+       close(ec_process.err);
+
+       /*
+        * Don't bother checking return value, because "emacsclient --version"
+        * seems to always exits with code 1.
+        */
+       finish_command(&ec_process);
+
+       if (prefixcmp(buffer.buf, "emacsclient")) {
+               fprintf(stderr, "Failed to parse emacsclient version.\n");
+               strbuf_release(&buffer);
+               return -1;
+       }
+
+       strbuf_remove(&buffer, 0, strlen("emacsclient"));
+       version = atoi(buffer.buf);
+
+       if (version < 22) {
+               fprintf(stderr,
+                       "emacsclient version '%d' too old (< 22).\n",
+                       version);
+               strbuf_release(&buffer);
+               return -1;
+       }
+
+       strbuf_release(&buffer);
+       return 0;
+}
+
+static void exec_woman_emacs(const char* path, const char *page)
+{
+       if (!check_emacsclient_version()) {
+               /* This works only with emacsclient version >= 22. */
+               struct strbuf man_page = STRBUF_INIT;
+
+               if (!path)
+                       path = "emacsclient";
+               strbuf_addf(&man_page, "(woman \"%s\")", page);
+               execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+               warning("failed to exec '%s': %s", path, strerror(errno));
+       }
+}
+
+static void exec_man_konqueror(const char* path, const char *page)
+{
+       const char *display = getenv("DISPLAY");
+       if (display && *display) {
+               struct strbuf man_page = STRBUF_INIT;
+               const char *filename = "kfmclient";
+
+               /* It's simpler to launch konqueror using kfmclient. */
+               if (path) {
+                       const char *file = strrchr(path, '/');
+                       if (file && !strcmp(file + 1, "konqueror")) {
+                               char *new = xstrdup(path);
+                               char *dest = strrchr(new, '/');
+
+                               /* strlen("konqueror") == strlen("kfmclient") */
+                               strcpy(dest + 1, "kfmclient");
+                               path = new;
+                       }
+                       if (file)
+                               filename = file;
+               } else
+                       path = "kfmclient";
+               strbuf_addf(&man_page, "man:%s(1)", page);
+               execlp(path, filename, "newTab", man_page.buf, NULL);
+               warning("failed to exec '%s': %s", path, strerror(errno));
+       }
+}
+
+static void exec_man_man(const char* path, const char *page)
+{
+       if (!path)
+               path = "man";
+       execlp(path, "man", page, NULL);
+       warning("failed to exec '%s': %s", path, strerror(errno));
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
+{
+       struct strbuf shell_cmd = STRBUF_INIT;
+       strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+       execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+       warning("failed to exec '%s': %s", cmd, strerror(errno));
+}
+
+static void add_man_viewer(const char *name)
+{
+       struct man_viewer_list **p = &man_viewer_list;
+       size_t len = strlen(name);
+
+       while (*p)
+               p = &((*p)->next);
+       *p = xcalloc(1, (sizeof(**p) + len + 1));
+       strncpy((*p)->name, name, len);
+}
+
+static int supported_man_viewer(const char *name, size_t len)
+{
+       return (!strncasecmp("man", name, len) ||
+               !strncasecmp("woman", name, len) ||
+               !strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+                                  size_t len,
+                                  const char *value)
+{
+       struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
+
+       strncpy(new->name, name, len);
+       new->info = xstrdup(value);
+       new->next = man_viewer_info_list;
+       man_viewer_info_list = new;
+}
+
+static int add_man_viewer_path(const char *name,
+                              size_t len,
+                              const char *value)
+{
+       if (supported_man_viewer(name, len))
+               do_add_man_viewer_info(name, len, value);
+       else
+               warning("'%s': path for unsupported man viewer.\n"
+                       "Please consider using 'man.<tool>.cmd' instead.",
+                       name);
+
+       return 0;
+}
+
+static int add_man_viewer_cmd(const char *name,
+                             size_t len,
+                             const char *value)
+{
+       if (supported_man_viewer(name, len))
+               warning("'%s': cmd for supported man viewer.\n"
+                       "Please consider using 'man.<tool>.path' instead.",
+                       name);
+       else
+               do_add_man_viewer_info(name, len, value);
+
+       return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+       const char *name = var + 4;
+       const char *subkey = strrchr(name, '.');
+
+       if (!subkey)
+               return error("Config with no key for man viewer: %s", name);
+
+       if (!strcmp(subkey, ".path")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer_path(name, subkey - name, value);
+       }
+       if (!strcmp(subkey, ".cmd")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer_cmd(name, subkey - name, value);
+       }
+
+       warning("'%s': unsupported man viewer sub key.", subkey);
+       return 0;
+}
+
+static int git_help_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "help.format")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               help_format = parse_help_format(value);
+               return 0;
+       }
+       if (!strcmp(var, "man.viewer")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               add_man_viewer(value);
+               return 0;
+       }
+       if (!prefixcmp(var, "man."))
+               return add_man_viewer_info(var, value);
+
+       return git_default_config(var, value, cb);
+}
+
+static struct cmdnames main_cmds, other_cmds;
+
+void list_common_cmds_help(void)
+{
+       int i, longest = 0;
+
+       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+               if (longest < strlen(common_cmds[i].name))
+                       longest = strlen(common_cmds[i].name);
+       }
+
+       puts("The most commonly used git commands are:");
+       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+               printf("   %s   ", common_cmds[i].name);
+               mput_char(' ', longest - strlen(common_cmds[i].name));
+               puts(common_cmds[i].help);
+       }
+}
+
+static int is_git_command(const char *s)
+{
+       return is_in_cmdlist(&main_cmds, s) ||
+               is_in_cmdlist(&other_cmds, s);
+}
+
+static const char *prepend(const char *prefix, const char *cmd)
+{
+       size_t pre_len = strlen(prefix);
+       size_t cmd_len = strlen(cmd);
+       char *p = xmalloc(pre_len + cmd_len + 1);
+       memcpy(p, prefix, pre_len);
+       strcpy(p + pre_len, cmd);
+       return p;
+}
+
+static const char *cmd_to_page(const char *git_cmd)
+{
+       if (!git_cmd)
+               return "git";
+       else if (!prefixcmp(git_cmd, "git"))
+               return git_cmd;
+       else if (is_git_command(git_cmd))
+               return prepend("git-", git_cmd);
+       else
+               return prepend("git", git_cmd);
+}
+
+static void setup_man_path(void)
+{
+       struct strbuf new_path;
+       const char *old_path = getenv("MANPATH");
+
+       strbuf_init(&new_path, 0);
+
+       /* We should always put ':' after our path. If there is no
+        * old_path, the ':' at the end will let 'man' to try
+        * system-wide paths after ours to find the manual page. If
+        * there is old_path, we need ':' as delimiter. */
+       strbuf_addstr(&new_path, GIT_MAN_PATH);
+       strbuf_addch(&new_path, ':');
+       if (old_path)
+               strbuf_addstr(&new_path, old_path);
+
+       setenv("MANPATH", new_path.buf, 1);
+
+       strbuf_release(&new_path);
+}
+
+static void exec_viewer(const char *name, const char *page)
+{
+       const char *info = get_man_viewer_info(name);
+
+       if (!strcasecmp(name, "man"))
+               exec_man_man(info, page);
+       else if (!strcasecmp(name, "woman"))
+               exec_woman_emacs(info, page);
+       else if (!strcasecmp(name, "konqueror"))
+               exec_man_konqueror(info, page);
+       else if (info)
+               exec_man_cmd(info, page);
+       else
+               warning("'%s': unknown man viewer.", name);
+}
+
+static void show_man_page(const char *git_cmd)
+{
+       struct man_viewer_list *viewer;
+       const char *page = cmd_to_page(git_cmd);
+
+       setup_man_path();
+       for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+       {
+               exec_viewer(viewer->name, page); /* will return when unable */
+       }
+       exec_viewer("man", page);
+       die("no man viewer handled the request");
+}
+
+static void show_info_page(const char *git_cmd)
+{
+       const char *page = cmd_to_page(git_cmd);
+       setenv("INFOPATH", GIT_INFO_PATH, 1);
+       execlp("info", "info", "gitman", page, NULL);
+}
+
+static void get_html_page_path(struct strbuf *page_path, const char *page)
+{
+       struct stat st;
+       const char *html_path = system_path(GIT_HTML_PATH);
+
+       /* Check that we have a git documentation directory. */
+       if (stat(mkpath("%s/git.html", html_path), &st)
+           || !S_ISREG(st.st_mode))
+               die("'%s': not a documentation directory.", html_path);
+
+       strbuf_init(page_path, 0);
+       strbuf_addf(page_path, "%s/%s.html", html_path, page);
+}
+
+/*
+ * If open_html is not defined in a platform-specific way (see for
+ * example compat/mingw.h), we use the script web--browse to display
+ * HTML.
+ */
+#ifndef open_html
+void open_html(const char *path)
+{
+       execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
+}
+#endif
+
+static void show_html_page(const char *git_cmd)
+{
+       const char *page = cmd_to_page(git_cmd);
+       struct strbuf page_path; /* it leaks but we exec bellow */
+
+       get_html_page_path(&page_path, page);
+
+       open_html(page_path.buf);
+}
+
+int cmd_help(int argc, const char **argv, const char *prefix)
+{
+       int nongit;
+       const char *alias;
+       load_command_list("git-", &main_cmds, &other_cmds);
+
+       setup_git_directory_gently(&nongit);
+       git_config(git_help_config, NULL);
+
+       argc = parse_options(argc, argv, builtin_help_options,
+                       builtin_help_usage, 0);
+
+       if (show_all) {
+               printf("usage: %s\n\n", git_usage_string);
+               list_commands("git commands", &main_cmds, &other_cmds);
+               printf("%s\n", git_more_info_string);
+               return 0;
+       }
+
+       if (!argv[0]) {
+               printf("usage: %s\n\n", git_usage_string);
+               list_common_cmds_help();
+               printf("\n%s\n", git_more_info_string);
+               return 0;
+       }
+
+       alias = alias_lookup(argv[0]);
+       if (alias && !is_git_command(argv[0])) {
+               printf("`git %s' is aliased to `%s'\n", argv[0], alias);
+               return 0;
+       }
+
+       switch (help_format) {
+       case HELP_FORMAT_MAN:
+               show_man_page(argv[0]);
+               break;
+       case HELP_FORMAT_INFO:
+               show_info_page(argv[0]);
+               break;
+       case HELP_FORMAT_WEB:
+               show_html_page(argv[0]);
+               break;
+       }
+
+       return 0;
+}
index 911fd659906ebe87bf0adcd17519dd97777fb948..1d3c5cbf580f3989d9605ed68f80beb050e19d39 100644 (file)
@@ -217,6 +217,11 @@ static int cmd_log_walk(struct rev_info *rev)
        if (rev->early_output)
                finish_early_output(rev);
 
+       /*
+        * For --check and --exit-code, the exit code is based on CHECK_FAILED
+        * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
+        * retain that state information if replacing rev->diffopt in this loop
+        */
        while ((commit = get_revision(rev)) != NULL) {
                log_tree_commit(rev, commit);
                if (!rev->reflog_info) {
@@ -227,7 +232,11 @@ static int cmd_log_walk(struct rev_info *rev)
                free_commit_list(commit->parents);
                commit->parents = NULL;
        }
-       return 0;
+       if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
+           DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
+               return 02;
+       }
+       return diff_result_code(&rev->diffopt, 0);
 }
 
 static int git_log_config(const char *var, const char *value, void *cb)
@@ -923,7 +932,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (argc > 1)
                die ("unrecognized argument: %s", argv[1]);
 
-       if (!rev.diffopt.output_format)
+       if (!rev.diffopt.output_format
+               || rev.diffopt.output_format == DIFF_FORMAT_PATCH)
                rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
 
        if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
index 3382b1382a7dcbd525126a35209072da4b4d8041..b08da516e491e7089b1bb178e9a4b05c2ab36539 100644 (file)
@@ -2,9 +2,11 @@
 #include "cache.h"
 #include "commit.h"
 
-static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_all)
+static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
-       struct commit_list *result = get_merge_bases(rev1, rev2, 0);
+       struct commit_list *result;
+
+       result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
 
        if (!result)
                return 1;
@@ -20,7 +22,7 @@ static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_al
 }
 
 static const char merge_base_usage[] =
-"git merge-base [--all] <commit-id> <commit-id>";
+"git merge-base [--all] <commit-id> <commit-id>...";
 
 static struct commit *get_commit_reference(const char *arg)
 {
@@ -38,7 +40,8 @@ static struct commit *get_commit_reference(const char *arg)
 
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
 {
-       struct commit *rev1, *rev2;
+       struct commit **rev;
+       int rev_nr = 0;
        int show_all = 0;
 
        git_config(git_default_config, NULL);
@@ -51,10 +54,15 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)
                        usage(merge_base_usage);
                argc--; argv++;
        }
-       if (argc != 3)
+       if (argc < 3)
                usage(merge_base_usage);
-       rev1 = get_commit_reference(argv[1]);
-       rev2 = get_commit_reference(argv[2]);
 
-       return show_merge_base(rev1, rev2, show_all);
+       rev = xmalloc((argc - 1) * sizeof(*rev));
+
+       do {
+               rev[rev_nr++] = get_commit_reference(argv[1]);
+               argc--; argv++;
+       } while (argc > 1);
+
+       return show_merge_base(rev, rev_nr, show_all);
 }
index 43e55bf90154c51b94527b2ab7eb7c60fe36e9ec..dfb363ee2f2e5764264722171de17a9a57556dd8 100644 (file)
@@ -729,13 +729,13 @@ static void conflict_rename_rename(struct rename *ren1,
        const char *dst_name2 = ren2_dst;
        if (string_list_has_string(&current_directory_set, ren1_dst)) {
                dst_name1 = del[delp++] = unique_path(ren1_dst, branch1);
-               output(1, "%s is a directory in %s added as %s instead",
+               output(1, "%s is a directory in %s adding as %s instead",
                       ren1_dst, branch2, dst_name1);
                remove_file(0, ren1_dst, 0);
        }
        if (string_list_has_string(&current_directory_set, ren2_dst)) {
                dst_name2 = del[delp++] = unique_path(ren2_dst, branch2);
-               output(1, "%s is a directory in %s added as %s instead",
+               output(1, "%s is a directory in %s adding as %s instead",
                       ren2_dst, branch1, dst_name2);
                remove_file(0, ren2_dst, 0);
        }
@@ -760,7 +760,7 @@ static void conflict_rename_dir(struct rename *ren1,
                                const char *branch1)
 {
        char *new_path = unique_path(ren1->pair->two->path, branch1);
-       output(1, "Renamed %s to %s instead", ren1->pair->one->path, new_path);
+       output(1, "Renaming %s to %s instead", ren1->pair->one->path, new_path);
        remove_file(0, ren1->pair->two->path, 0);
        update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
        free(new_path);
@@ -773,7 +773,7 @@ static void conflict_rename_rename_2(struct rename *ren1,
 {
        char *new_path1 = unique_path(ren1->pair->two->path, branch1);
        char *new_path2 = unique_path(ren2->pair->two->path, branch2);
-       output(1, "Renamed %s to %s and %s to %s instead",
+       output(1, "Renaming %s to %s and %s to %s instead",
               ren1->pair->one->path, new_path1,
               ren2->pair->one->path, new_path2);
        remove_file(0, ren1->pair->two->path, 0);
@@ -887,10 +887,10 @@ static int process_renames(struct string_list *a_renames,
                                                 branch1,
                                                 branch2);
                                if (mfi.merge || !mfi.clean)
-                                       output(1, "Renamed %s->%s", src, ren1_dst);
+                                       output(1, "Renaming %s->%s", src, ren1_dst);
 
                                if (mfi.merge)
-                                       output(2, "Auto-merged %s", ren1_dst);
+                                       output(2, "Auto-merging %s", ren1_dst);
 
                                if (!mfi.clean) {
                                        output(1, "CONFLICT (content): merge conflict in %s",
@@ -924,14 +924,14 @@ static int process_renames(struct string_list *a_renames,
 
                        if (string_list_has_string(&current_directory_set, ren1_dst)) {
                                clean_merge = 0;
-                               output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s "
+                               output(1, "CONFLICT (rename/directory): Rename %s->%s in %s "
                                       " directory %s added in %s",
                                       ren1_src, ren1_dst, branch1,
                                       ren1_dst, branch2);
                                conflict_rename_dir(ren1, branch1);
                        } else if (sha_eq(src_other.sha1, null_sha1)) {
                                clean_merge = 0;
-                               output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s "
+                               output(1, "CONFLICT (rename/delete): Rename %s->%s in %s "
                                       "and deleted in %s",
                                       ren1_src, ren1_dst, branch1,
                                       branch2);
@@ -940,19 +940,19 @@ static int process_renames(struct string_list *a_renames,
                                const char *new_path;
                                clean_merge = 0;
                                try_merge = 1;
-                               output(1, "CONFLICT (rename/add): Renamed %s->%s in %s. "
+                               output(1, "CONFLICT (rename/add): Rename %s->%s in %s. "
                                       "%s added in %s",
                                       ren1_src, ren1_dst, branch1,
                                       ren1_dst, branch2);
                                new_path = unique_path(ren1_dst, branch2);
-                               output(1, "Added as %s instead", new_path);
+                               output(1, "Adding as %s instead", new_path);
                                update_file(0, dst_other.sha1, dst_other.mode, new_path);
                        } else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
                                ren2 = item->util;
                                clean_merge = 0;
                                ren2->processed = 1;
-                               output(1, "CONFLICT (rename/rename): Renamed %s->%s in %s. "
-                                      "Renamed %s->%s in %s",
+                               output(1, "CONFLICT (rename/rename): Rename %s->%s in %s. "
+                                      "Rename %s->%s in %s",
                                       ren1_src, ren1_dst, branch1,
                                       ren2->pair->one->path, ren2->pair->two->path, branch2);
                                conflict_rename_rename_2(ren1, branch1, ren2, branch2);
@@ -986,9 +986,9 @@ static int process_renames(struct string_list *a_renames,
                                        output(3, "Skipped %s (merged same as existing)", ren1_dst);
                                else {
                                        if (mfi.merge || !mfi.clean)
-                                               output(1, "Renamed %s => %s", ren1_src, ren1_dst);
+                                               output(1, "Renaming %s => %s", ren1_src, ren1_dst);
                                        if (mfi.merge)
-                                               output(2, "Auto-merged %s", ren1_dst);
+                                               output(2, "Auto-merging %s", ren1_dst);
                                        if (!mfi.clean) {
                                                output(1, "CONFLICT (rename/modify): Merge conflict in %s",
                                                       ren1_dst);
@@ -1039,7 +1039,7 @@ static int process_entry(const char *path, struct stage_data *entry,
                        /* Deleted in both or deleted in one and
                         * unchanged in the other */
                        if (a_sha)
-                               output(2, "Removed %s", path);
+                               output(2, "Removing %s", path);
                        /* do not touch working file if it did not exist */
                        remove_file(1, path, !a_sha);
                } else {
@@ -1086,12 +1086,12 @@ static int process_entry(const char *path, struct stage_data *entry,
                        const char *new_path = unique_path(path, add_branch);
                        clean_merge = 0;
                        output(1, "CONFLICT (%s): There is a directory with name %s in %s. "
-                              "Added %s as %s",
+                              "Adding %s as %s",
                               conf, path, other_branch, path, new_path);
                        remove_file(0, path, 0);
                        update_file(0, sha, mode, new_path);
                } else {
-                       output(2, "Added %s", path);
+                       output(2, "Adding %s", path);
                        update_file(1, sha, mode, path);
                }
        } else if (a_sha && b_sha) {
@@ -1105,7 +1105,7 @@ static int process_entry(const char *path, struct stage_data *entry,
                        reason = "add/add";
                        o_sha = (unsigned char *)null_sha1;
                }
-               output(2, "Auto-merged %s", path);
+               output(2, "Auto-merging %s", path);
                o.path = a.path = b.path = (char *)path;
                hashcpy(o.sha1, o_sha);
                o.mode = o_mode;
index b280444e10d67355da6fd0d9e1a2dd2d7a29d440..9ad9791068c9330f28413ac67315246989c8d96d 100644 (file)
@@ -22,6 +22,7 @@
 #include "log-tree.h"
 #include "color.h"
 #include "rerere.h"
+#include "help.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -77,7 +78,9 @@ static int option_parse_message(const struct option *opt,
 static struct strategy *get_strategy(const char *name)
 {
        int i;
-       struct strbuf err;
+       struct strategy *ret;
+       static struct cmdnames main_cmds, other_cmds;
+       static int loaded;
 
        if (!name)
                return NULL;
@@ -86,12 +89,43 @@ static struct strategy *get_strategy(const char *name)
                if (!strcmp(name, all_strategy[i].name))
                        return &all_strategy[i];
 
-       strbuf_init(&err, 0);
-       for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
-               strbuf_addf(&err, " %s", all_strategy[i].name);
-       fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
-       fprintf(stderr, "Available strategies are:%s.\n", err.buf);
-       exit(1);
+       if (!loaded) {
+               struct cmdnames not_strategies;
+               loaded = 1;
+
+               memset(&not_strategies, 0, sizeof(struct cmdnames));
+               load_command_list("git-merge-", &main_cmds, &other_cmds);
+               for (i = 0; i < main_cmds.cnt; i++) {
+                       int j, found = 0;
+                       struct cmdname *ent = main_cmds.names[i];
+                       for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+                               if (!strncmp(ent->name, all_strategy[j].name, ent->len)
+                                               && !all_strategy[j].name[ent->len])
+                                       found = 1;
+                       if (!found)
+                               add_cmdname(&not_strategies, ent->name, ent->len);
+                       exclude_cmds(&main_cmds, &not_strategies);
+               }
+       }
+       if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
+               fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
+               fprintf(stderr, "Available strategies are:");
+               for (i = 0; i < main_cmds.cnt; i++)
+                       fprintf(stderr, " %s", main_cmds.names[i]->name);
+               fprintf(stderr, ".\n");
+               if (other_cmds.cnt) {
+                       fprintf(stderr, "Available custom strategies are:");
+                       for (i = 0; i < other_cmds.cnt; i++)
+                               fprintf(stderr, " %s", other_cmds.names[i]->name);
+                       fprintf(stderr, ".\n");
+               }
+               exit(1);
+       }
+
+       ret = xmalloc(sizeof(struct strategy));
+       memset(ret, 0, sizeof(struct strategy));
+       ret->name = xstrdup(name);
+       return ret;
 }
 
 static void append_strategy(struct strategy *s)
@@ -834,6 +868,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                if (argc != 1)
                        die("Can merge only exactly one commit into "
                                "empty head");
+               if (squash)
+                       die("Squash commit into empty head not supported yet");
+               if (!allow_fast_forward)
+                       die("Non-fast-forward commit does not make sense into "
+                           "an empty head");
                remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
                if (!remote_head)
                        die("%s - not something we can merge", argv[0]);
index c1ed68d938f67343c6938cfef54d5ff69a522a63..cc6666f75e7db8a546d4a1589335589190e414fe 100644 (file)
@@ -59,8 +59,17 @@ static int do_push(const char *repo, int flags)
        if (remote->mirror)
                flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
-       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
-               return -1;
+       if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
+               if (!strcmp(*refspec, "refs/tags/*"))
+                       return error("--all and --tags are incompatible");
+               return error("--all can't be combined with refspecs");
+       }
+
+       if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
+               if (!strcmp(*refspec, "refs/tags/*"))
+                       return error("--mirror and --tags are incompatible");
+               return error("--mirror can't be combined with refspecs");
+       }
 
        if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
                                (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
index 196fa03b7fac795475a0e12f0fa3b443cb34bd1d..6b3667ef0ebdfc2b8b70b24e474b22989fbf0cb7 100644 (file)
@@ -540,11 +540,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
                free(collected.e);
        }
 
-       while (i < argc) {
-               const char *ref = argv[i++];
+       for (; i < argc; i++) {
+               char *ref;
                unsigned char sha1[20];
-               if (!resolve_ref(ref, sha1, 1, NULL)) {
-                       status |= error("%s points nowhere!", ref);
+               if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
+                       status |= error("%s points nowhere!", argv[i]);
                        continue;
                }
                set_reflog_expiry_param(&cb, explicit_expiry, ref);
index 27881e94937dd79b9a0524eb2d9770427ec4f4fb..36677053f87b107f658f1472369755c4fe957a46 100644 (file)
@@ -11,6 +11,7 @@
 #include "cache-tree.h"
 #include "diff.h"
 #include "revision.h"
+#include "rerere.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -395,6 +396,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                        die ("Error wrapping up %s", defmsg);
                fprintf(stderr, "Automatic %s failed.%s\n",
                        me, help_msg(commit->object.sha1));
+               rerere();
                exit(1);
        }
        if (commit_lock_file(&msg_file) < 0)
index 38eb53ccba2b97a0fccf50d6ba0b7424fe2d1bcb..434cb8e4a00565c53587d89dd259ae63bc133de7 100644 (file)
@@ -194,6 +194,10 @@ static int process_path(const char *path)
        int len;
        struct stat st;
 
+       len = strlen(path);
+       if (has_symlink_leading_path(len, path))
+               return error("'%s' is beyond a symbolic link", path);
+
        /*
         * First things first: get the stat information, to decide
         * what to do about the pathname!
@@ -201,7 +205,6 @@ static int process_path(const char *path)
        if (lstat(path, &st) < 0)
                return process_lstat_error(path, errno);
 
-       len = strlen(path);
        if (S_ISDIR(st.st_mode))
                return process_directory(path, len, &st);
 
diff --git a/cache.h b/cache.h
index 884fae826cb8c296520aecbc8c23d9848dacb606..de8c2b6266ee96d0958cb2c1430b2b2da99f1e86 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -126,6 +126,7 @@ struct cache_entry {
 
 #define CE_NAMEMASK  (0x0fff)
 #define CE_STAGEMASK (0x3000)
+#define CE_EXTENDED  (0x4000)
 #define CE_VALID     (0x8000)
 #define CE_STAGESHIFT 12
 
@@ -378,6 +379,7 @@ extern int remove_file_from_index(struct index_state *, const char *path);
 #define ADD_CACHE_VERBOSE 1
 #define ADD_CACHE_PRETEND 2
 #define ADD_CACHE_IGNORE_ERRORS        4
+#define ADD_CACHE_IGNORE_REMOVAL 8
 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
@@ -392,7 +394,6 @@ extern int ie_modified(const struct index_state *, struct cache_entry *, struct
 
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
-extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
@@ -452,6 +453,7 @@ enum safe_crlf {
 extern enum safe_crlf safe_crlf;
 
 enum branch_track {
+       BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
        BRANCH_TRACK_REMOTE,
        BRANCH_TRACK_ALWAYS,
index 4dfc33086755e7ae03310baabd0060149f323f4c..0cf2a830b5dad5c82d8a127cd57c8a8cba195631 100644 (file)
@@ -143,8 +143,6 @@ static void append_lost(struct sline *sline, int n, const char *line, int len)
 }
 
 struct combine_diff_state {
-       struct xdiff_emit_state xm;
-
        unsigned int lno;
        int ob, on, nb, nn;
        unsigned long nmask;
@@ -217,17 +215,15 @@ static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
        parent_file.size = sz;
        xpp.flags = XDF_NEED_MINIMAL;
        memset(&xecfg, 0, sizeof(xecfg));
-       ecb.outf = xdiff_outf;
-       ecb.priv = &state;
        memset(&state, 0, sizeof(state));
-       state.xm.consume = consume_line;
        state.nmask = nmask;
        state.sline = sline;
        state.lno = 1;
        state.num_parent = num_parent;
        state.n = n;
 
-       xdi_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
+       xdi_diff_outf(&parent_file, result_file, consume_line, &state,
+                     &xpp, &xecfg, &ecb);
        free(parent_file.ptr);
 
        /* Assign line numbers for this parent.
index ecdd5733f989f5931a0af05b387321da7068e9f9..de15f4d715ded4d8fd4f882b44fd9a320e7cdb2e 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -122,6 +122,7 @@ int read_graft_file(const char *graft_file);
 struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
+extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
 extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 
 extern int register_shallow(const unsigned char *sha1);
index 772cad510d5d260fdf33b4f7d6ff79f9f3367b05..ccfa2a0a3d3263862beb0d2796be1aba78574986 100644 (file)
@@ -31,11 +31,6 @@ static inline time_t filetime_to_time_t(const FILETIME *ft)
        return (time_t)winTime;
 }
 
-static inline size_t size_to_blocks(size_t s)
-{
-       return (s+511)/512;
-}
-
 extern int _getdrive( void );
 /* 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
@@ -57,10 +52,10 @@ static int do_lstat(const char *file_name, struct stat *buf)
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
+               buf->st_nlink = 1;
                buf->st_mode = fMode;
                buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
-               buf->st_blocks = size_to_blocks(buf->st_size);
-               buf->st_dev = _getdrive() - 1;
+               buf->st_dev = buf->st_rdev = (_getdrive() - 1);
                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));
@@ -94,7 +89,7 @@ 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 mingw_stat *buf)
+int mingw_lstat(const char *file_name, struct stat *buf)
 {
        int namelen;
        static char alt_name[PATH_MAX];
@@ -122,8 +117,7 @@ int mingw_lstat(const char *file_name, struct mingw_stat *buf)
 }
 
 #undef fstat
-#undef stat
-int mingw_fstat(int fd, struct mingw_stat *buf)
+int mingw_fstat(int fd, struct stat *buf)
 {
        HANDLE fh = (HANDLE)_get_osfhandle(fd);
        BY_HANDLE_FILE_INFORMATION fdata;
@@ -133,22 +127,8 @@ int mingw_fstat(int fd, struct mingw_stat *buf)
                return -1;
        }
        /* direct non-file handles to MS's fstat() */
-       if (GetFileType(fh) != FILE_TYPE_DISK) {
-               struct stat st;
-               if (fstat(fd, &st))
-                       return -1;
-               buf->st_ino = st.st_ino;
-               buf->st_gid = st.st_gid;
-               buf->st_uid = st.st_uid;
-               buf->st_mode = st.st_mode;
-               buf->st_size = st.st_size;
-               buf->st_blocks = size_to_blocks(buf->st_size);
-               buf->st_dev = st.st_dev;
-               buf->st_atime = st.st_atime;
-               buf->st_mtime = st.st_mtime;
-               buf->st_ctime = st.st_ctime;
-               return 0;
-       }
+       if (GetFileType(fh) != FILE_TYPE_DISK)
+               return fstat(fd, buf);
 
        if (GetFileInformationByHandle(fh, &fdata)) {
                int fMode = S_IREAD;
@@ -162,10 +142,10 @@ int mingw_fstat(int fd, struct mingw_stat *buf)
                buf->st_ino = 0;
                buf->st_gid = 0;
                buf->st_uid = 0;
+               buf->st_nlink = 1;
                buf->st_mode = fMode;
                buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
-               buf->st_blocks = size_to_blocks(buf->st_size);
-               buf->st_dev = _getdrive() - 1;
+               buf->st_dev = buf->st_rdev = (_getdrive() - 1);
                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));
index a52e657c51916a0032908a87b4d4df259547f4e8..4f275cb8e6a67515292a9dfc60bd1343065067a9 100644 (file)
@@ -162,22 +162,12 @@ int mingw_rename(const char*, const char*);
 
 /* Use mingw_lstat() instead of lstat()/stat() and
  * mingw_fstat() instead of fstat() on Windows.
- * struct stat is redefined because it lacks the st_blocks member.
  */
-struct mingw_stat {
-       unsigned st_mode;
-       time_t st_mtime, st_atime, st_ctime;
-       unsigned st_dev, st_ino, st_uid, st_gid;
-       size_t st_size;
-       size_t st_blocks;
-};
-int mingw_lstat(const char *file_name, struct mingw_stat *buf);
-int mingw_fstat(int fd, struct mingw_stat *buf);
+int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_fstat(int fd, struct stat *buf);
 #define fstat mingw_fstat
 #define lstat mingw_lstat
-#define stat mingw_stat
-static inline int mingw_stat(const char *file_name, struct mingw_stat *buf)
-{ return mingw_lstat(file_name, buf); }
+#define stat(x,y) mingw_lstat(x,y)
 
 int mingw_utime(const char *file_name, const struct utimbuf *times);
 #define utime mingw_utime
index b776149531025c85f5665d971e6e072f0cc64893..f67d673d013b3db84df804d5313c54ac186cae0b 100644 (file)
@@ -3,6 +3,7 @@
 
 CC = @CC@
 CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
 AR = @AR@
 TAR = @TAR@
 #INSTALL = @INSTALL@           # needs install-sh or install.sh in sources
index 89858c237eaca5a5fb7e89d716577c0ef84dd086..4f64f8ab7d5860391ddbf0909edcfcfe9073a282 100755 (executable)
@@ -1483,7 +1483,7 @@ _git_submodule ()
 {
        __git_has_doubledash && return
 
-       local subcommands="add status init update"
+       local subcommands="add status init update summary foreach sync"
        if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
                local cur="${COMP_WORDS[COMP_CWORD]}"
                case "$cur" in
index 46136d49bf99f61a5c85a548bec055742ed140f8..2216cacba79ad7171b7b5abbd1ff27e7b1ef84a1 100755 (executable)
@@ -708,6 +708,7 @@ class P4Submit(Command):
                 newdiff = newdiff.replace("\n", "\r\n")
             tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
             tmpFile.close()
+            mtime = os.stat(fileName).st_mtime
             defaultEditor = "vi"
             if platform.system() == "Windows":
                 defaultEditor = "notepad"
@@ -716,15 +717,29 @@ class P4Submit(Command):
             else:
                 editor = os.environ.get("EDITOR", defaultEditor);
             system(editor + " " + fileName)
-            tmpFile = open(fileName, "rb")
-            message = tmpFile.read()
-            tmpFile.close()
-            os.remove(fileName)
-            submitTemplate = message[:message.index(separatorLine)]
-            if self.isWindows:
-                submitTemplate = submitTemplate.replace("\r\n", "\n")
 
-            p4_write_pipe("submit -i", submitTemplate)
+            response = "y"
+            if os.stat(fileName).st_mtime <= mtime:
+                response = "x"
+                while response != "y" and response != "n":
+                    response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
+
+            if response == "y":
+                tmpFile = open(fileName, "rb")
+                message = tmpFile.read()
+                tmpFile.close()
+                submitTemplate = message[:message.index(separatorLine)]
+                if self.isWindows:
+                    submitTemplate = submitTemplate.replace("\r\n", "\n")
+                p4_write_pipe("submit -i", submitTemplate)
+            else:
+                for f in editedFiles:
+                    p4_system("revert \"%s\"" % f);
+                for f in filesToAdd:
+                    p4_system("revert \"%s\"" % f);
+                    system("rm %s" %f)
+
+            os.remove(fileName)
         else:
             fileName = "submit.txt"
             file = open(fileName, "w+")
@@ -1733,8 +1748,12 @@ class P4Clone(P4Sync):
         if not P4Sync.run(self, depotPaths):
             return False
         if self.branch != "master":
-            if gitBranchExists("refs/remotes/p4/master"):
-                system("git branch master refs/remotes/p4/master")
+            if self.importIntoRemotes:
+                masterbranch = "refs/remotes/p4/master"
+            else:
+                masterbranch = "refs/heads/p4/master"
+            if gitBranchExists(masterbranch):
+                system("git branch master %s" % masterbranch)
                 system("git checkout -f")
             else:
                 print "Could not detect main branch. No checkout/master branch created."
diff --git a/ctype.c b/ctype.c
index d2bd38e9013cdf5c4ab15dbb1770e94e5d6ed2cb..9208d674dbc081878532f08d2eddfc66c6a2e327 100644 (file)
--- a/ctype.c
+++ b/ctype.c
@@ -9,18 +9,20 @@
 #undef SS
 #undef AA
 #undef DD
+#undef GS
 
 #define SS GIT_SPACE
 #define AA GIT_ALPHA
 #define DD GIT_DIGIT
+#define GS GIT_SPECIAL  /* \0, *, ?, [, \\ */
 
 unsigned char sane_ctype[256] = {
-        0,  0,  0,  0,  0,  0,  0,  0,  0, SS, SS,  0,  0, SS,  0,  0,         /* 0-15 */
+       GS,  0,  0,  0,  0,  0,  0,  0,  0, SS, SS,  0,  0, SS,  0,  0,         /* 0-15 */
         0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,         /* 16-15 */
-       SS,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,         /* 32-15 */
-       DD, DD, DD, DD, DD, DD, DD, DD, DD, DD,  0,  0,  0,  0,  0,  0,         /* 48-15 */
+       SS,  0,  0,  0,  0,  0,  0,  0,  0,  0, GS,  0,  0,  0,  0,  0,         /* 32-15 */
+       DD, DD, DD, DD, DD, DD, DD, DD, DD, DD,  0,  0,  0,  0,  0, GS,         /* 48-15 */
         0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,         /* 64-15 */
-       AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,  0,  0,  0,  0,  0,         /* 80-15 */
+       AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, GS, GS,  0,  0,  0,         /* 80-15 */
         0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,         /* 96-15 */
        AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,  0,  0,  0,  0,  0,         /* 112-15 */
        /* Nothing in the 128.. range */
index 8dcde73200d1ddbc0dec0dbfdc2f4ff15047abd9..23278e28dc16cc30bd9516f2f2675cd93d5ac182 100644 (file)
--- a/daemon.c
+++ b/daemon.c
 static int log_syslog;
 static int verbose;
 static int reuseaddr;
-static int child_handler_pipe[2];
 
 static const char daemon_usage[] =
 "git daemon [--verbose] [--syslog] [--export-all]\n"
-"           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-"           [--base-path=path] [--base-path-relaxed]\n"
+"           [--timeout=n] [--init-timeout=n] [--max-connections=n]\n"
+"           [--strict-paths] [--base-path=path] [--base-path-relaxed]\n"
 "           [--user-path | --user-path=path]\n"
 "           [--interpolated-path=path]\n"
 "           [--reuseaddr] [--detach] [--pid-file=file]\n"
@@ -78,38 +77,19 @@ static struct interp interp_table[] = {
 
 static void logreport(int priority, const char *err, va_list params)
 {
-       /* We should do a single write so that it is atomic and output
-        * of several processes do not get intermingled. */
-       char buf[1024];
-       int buflen;
-       int maxlen, msglen;
-
-       /* sizeof(buf) should be big enough for "[pid] \n" */
-       buflen = snprintf(buf, sizeof(buf), "[%ld] ", (long) getpid());
-
-       maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */
-       msglen = vsnprintf(buf + buflen, maxlen, err, params);
-
        if (log_syslog) {
+               char buf[1024];
+               vsnprintf(buf, sizeof(buf), err, params);
                syslog(priority, "%s", buf);
-               return;
+       } else {
+               /*
+                * Since stderr is set to linebuffered mode, the
+                * logging of different processes will not overlap
+                */
+               fprintf(stderr, "[%d] ", (int)getpid());
+               vfprintf(stderr, err, params);
+               fputc('\n', stderr);
        }
-
-       /* maxlen counted our own LF but also counts space given to
-        * vsnprintf for the terminating NUL.  We want to make sure that
-        * we have space for our own LF and NUL after the "meat" of the
-        * message, so truncate it at maxlen - 1.
-        */
-       if (msglen > maxlen - 1)
-               msglen = maxlen - 1;
-       else if (msglen < 0)
-               msglen = 0; /* Protect against weird return values. */
-       buflen += msglen;
-
-       buf[buflen++] = '\n';
-       buf[buflen] = '\0';
-
-       write_in_full(2, buf, buflen);
 }
 
 static void logerror(const char *err, ...)
@@ -604,169 +584,107 @@ static int execute(struct sockaddr *addr)
        return -1;
 }
 
+static int max_connections = 32;
 
-/*
- * We count spawned/reaped separately, just to avoid any
- * races when updating them from signals. The SIGCHLD handler
- * will only update children_reaped, and the fork logic will
- * only update children_spawned.
- *
- * MAX_CHILDREN should be a power-of-two to make the modulus
- * operation cheap. It should also be at least twice
- * the maximum number of connections we will ever allow.
- */
-#define MAX_CHILDREN 128
-
-static int max_connections = 25;
-
-/* These are updated by the signal handler */
-static volatile unsigned int children_reaped;
-static pid_t dead_child[MAX_CHILDREN];
-
-/* These are updated by the main loop */
-static unsigned int children_spawned;
-static unsigned int children_deleted;
+static unsigned int live_children;
 
 static struct child {
+       struct child *next;
        pid_t pid;
-       int addrlen;
        struct sockaddr_storage address;
-} live_child[MAX_CHILDREN];
+} *firstborn;
 
-static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
+static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
 {
-       live_child[idx].pid = pid;
-       live_child[idx].addrlen = addrlen;
-       memcpy(&live_child[idx].address, addr, addrlen);
+       struct child *newborn, **cradle;
+
+       /*
+        * This must be xcalloc() -- we'll compare the whole sockaddr_storage
+        * but individual address may be shorter.
+        */
+       newborn = xcalloc(1, sizeof(*newborn));
+       live_children++;
+       newborn->pid = pid;
+       memcpy(&newborn->address, addr, addrlen);
+       for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
+               if (!memcmp(&(*cradle)->address, &newborn->address,
+                           sizeof(newborn->address)))
+                       break;
+       newborn->next = *cradle;
+       *cradle = newborn;
 }
 
-/*
- * Walk from "deleted" to "spawned", and remove child "pid".
- *
- * We move everything up by one, since the new "deleted" will
- * be one higher.
- */
-static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
+static void remove_child(pid_t pid)
 {
-       struct child n;
+       struct child **cradle, *blanket;
 
-       deleted %= MAX_CHILDREN;
-       spawned %= MAX_CHILDREN;
-       if (live_child[deleted].pid == pid) {
-               live_child[deleted].pid = -1;
-               return;
-       }
-       n = live_child[deleted];
-       for (;;) {
-               struct child m;
-               deleted = (deleted + 1) % MAX_CHILDREN;
-               if (deleted == spawned)
-                       die("could not find dead child %d\n", pid);
-               m = live_child[deleted];
-               live_child[deleted] = n;
-               if (m.pid == pid)
-                       return;
-               n = m;
-       }
+       for (cradle = &firstborn; (blanket = *cradle); cradle = &blanket->next)
+               if (blanket->pid == pid) {
+                       *cradle = blanket->next;
+                       live_children--;
+                       free(blanket);
+                       break;
+               }
 }
 
 /*
  * This gets called if the number of connections grows
  * past "max_connections".
  *
- * We _should_ start off by searching for connections
- * from the same IP, and if there is some address wth
- * multiple connections, we should kill that first.
- *
- * As it is, we just "randomly" kill 25% of the connections,
- * and our pseudo-random generator sucks too. I have no
- * shame.
- *
- * Really, this is just a place-holder for a _real_ algorithm.
+ * We kill the newest connection from a duplicate IP.
  */
-static void kill_some_children(int signo, unsigned start, unsigned stop)
+static void kill_some_child(void)
 {
-       start %= MAX_CHILDREN;
-       stop %= MAX_CHILDREN;
-       while (start != stop) {
-               if (!(start & 3))
-                       kill(live_child[start].pid, signo);
-               start = (start + 1) % MAX_CHILDREN;
-       }
-}
-
-static void check_dead_children(void)
-{
-       unsigned spawned, reaped, deleted;
-
-       spawned = children_spawned;
-       reaped = children_reaped;
-       deleted = children_deleted;
+       const struct child *blanket, *next;
 
-       while (deleted < reaped) {
-               pid_t pid = dead_child[deleted % MAX_CHILDREN];
-               const char *dead = pid < 0 ? " (with error)" : "";
-
-               if (pid < 0)
-                       pid = -pid;
+       if (!(blanket = firstborn))
+               return;
 
-               /* XXX: Custom logging, since we don't wanna getpid() */
-               if (verbose) {
-                       if (log_syslog)
-                               syslog(LOG_INFO, "[%d] Disconnected%s",
-                                               pid, dead);
-                       else
-                               fprintf(stderr, "[%d] Disconnected%s\n",
-                                               pid, dead);
+       for (; (next = blanket->next); blanket = next)
+               if (!memcmp(&blanket->address, &next->address,
+                           sizeof(next->address))) {
+                       kill(blanket->pid, SIGTERM);
+                       break;
                }
-               remove_child(pid, deleted, spawned);
-               deleted++;
-       }
-       children_deleted = deleted;
 }
 
-static void check_max_connections(void)
+static void check_dead_children(void)
 {
-       for (;;) {
-               int active;
-               unsigned spawned, deleted;
-
-               check_dead_children();
-
-               spawned = children_spawned;
-               deleted = children_deleted;
-
-               active = spawned - deleted;
-               if (active <= max_connections)
-                       break;
-
-               /* Kill some unstarted connections with SIGTERM */
-               kill_some_children(SIGTERM, deleted, spawned);
-               if (active <= max_connections << 1)
-                       break;
+       int status;
+       pid_t pid;
 
-               /* If the SIGTERM thing isn't helping use SIGKILL */
-               kill_some_children(SIGKILL, deleted, spawned);
-               sleep(1);
+       while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+               const char *dead = "";
+               remove_child(pid);
+               if (!WIFEXITED(status) || (WEXITSTATUS(status) > 0))
+                       dead = " (with error)";
+               loginfo("[%d] Disconnected%s", (int)pid, dead);
        }
 }
 
 static void handle(int incoming, struct sockaddr *addr, int addrlen)
 {
-       pid_t pid = fork();
+       pid_t pid;
 
-       if (pid) {
-               unsigned idx;
+       if (max_connections && live_children >= max_connections) {
+               kill_some_child();
+               sleep(1);  /* give it some time to die */
+               check_dead_children();
+               if (live_children >= max_connections) {
+                       close(incoming);
+                       logerror("Too many children, dropping connection");
+                       return;
+               }
+       }
 
+       if ((pid = fork())) {
                close(incoming);
-               if (pid < 0)
+               if (pid < 0) {
+                       logerror("Couldn't fork %s", strerror(errno));
                        return;
+               }
 
-               idx = children_spawned % MAX_CHILDREN;
-               children_spawned++;
-               add_child(idx, pid, addr, addrlen);
-
-               check_max_connections();
+               add_child(pid, addr, addrlen);
                return;
        }
 
@@ -779,21 +697,11 @@ static void handle(int incoming, struct sockaddr *addr, int addrlen)
 
 static void child_handler(int signo)
 {
-       for (;;) {
-               int status;
-               pid_t pid = waitpid(-1, &status, WNOHANG);
-
-               if (pid > 0) {
-                       unsigned reaped = children_reaped;
-                       if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
-                               pid = -pid;
-                       dead_child[reaped % MAX_CHILDREN] = pid;
-                       children_reaped = reaped + 1;
-                       write(child_handler_pipe[1], &status, 1);
-                       continue;
-               }
-               break;
-       }
+       /*
+        * Otherwise empty handler because systemcalls will get interrupted
+        * upon signal receipt
+        * SysV needs the handler to be rearmed
+        */
        signal(SIGCHLD, child_handler);
 }
 
@@ -836,7 +744,7 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
                if (sockfd < 0)
                        continue;
                if (sockfd >= FD_SETSIZE) {
-                       error("too large socket descriptor.");
+                       logerror("Socket descriptor too large");
                        close(sockfd);
                        continue;
                }
@@ -936,35 +844,28 @@ static int service_loop(int socknum, int *socklist)
        struct pollfd *pfd;
        int i;
 
-       if (pipe(child_handler_pipe) < 0)
-               die ("Could not set up pipe for child handler");
-
-       pfd = xcalloc(socknum + 1, sizeof(struct pollfd));
+       pfd = xcalloc(socknum, sizeof(struct pollfd));
 
        for (i = 0; i < socknum; i++) {
                pfd[i].fd = socklist[i];
                pfd[i].events = POLLIN;
        }
-       pfd[socknum].fd = child_handler_pipe[0];
-       pfd[socknum].events = POLLIN;
 
        signal(SIGCHLD, child_handler);
 
        for (;;) {
                int i;
 
-               if (poll(pfd, socknum + 1, -1) < 0) {
+               check_dead_children();
+
+               if (poll(pfd, socknum, -1) < 0) {
                        if (errno != EINTR) {
-                               error("poll failed, resuming: %s",
+                               logerror("Poll failed, resuming: %s",
                                      strerror(errno));
                                sleep(1);
                        }
                        continue;
                }
-               if (pfd[socknum].revents & POLLIN) {
-                       read(child_handler_pipe[0], &i, 1);
-                       check_dead_children();
-               }
 
                for (i = 0; i < socknum; i++) {
                        if (pfd[i].revents & POLLIN) {
@@ -1055,11 +956,6 @@ int main(int argc, char **argv)
        gid_t gid = 0;
        int i;
 
-       /* Without this we cannot rely on waitpid() to tell
-        * what happened to our children.
-        */
-       signal(SIGCHLD, SIG_DFL);
-
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
 
@@ -1105,6 +1001,12 @@ int main(int argc, char **argv)
                        init_timeout = atoi(arg+15);
                        continue;
                }
+               if (!prefixcmp(arg, "--max-connections=")) {
+                       max_connections = atoi(arg+18);
+                       if (max_connections < 0)
+                               max_connections = 0;            /* unlimited */
+                       continue;
+               }
                if (!strcmp(arg, "--strict-paths")) {
                        strict_paths = 1;
                        continue;
@@ -1178,9 +1080,10 @@ int main(int argc, char **argv)
        }
 
        if (log_syslog) {
-               openlog("git-daemon", 0, LOG_DAEMON);
+               openlog("git-daemon", LOG_PID, LOG_DAEMON);
                set_die_routine(daemon_die);
-       }
+       } else
+               setlinebuf(stderr); /* avoid splitting a message in the middle */
 
        if (inetd_mode && (group_name || user_name))
                die("--user and --group are incompatible with --inetd");
@@ -1233,8 +1136,10 @@ int main(int argc, char **argv)
                return execute(peer);
        }
 
-       if (detach)
+       if (detach) {
                daemonize();
+               loginfo("Ready to rumble");
+       }
        else
                sanitize_stdfds();
 
diff --git a/diff.c b/diff.c
index 7b4300a74ab116bdf96ef015322a7533204fd18f..135dec459a0f5d0abba812d773d7962d460e078f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -20,6 +20,7 @@
 
 static int diff_detect_rename_default;
 static int diff_rename_limit_default = 200;
+static int diff_suppress_blank_empty;
 int diff_use_color_default = -1;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
@@ -176,6 +177,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       /* like GNU diff's --suppress-blank-empty option  */
+       if (!strcmp(var, "diff.suppress-blank-empty")) {
+               diff_suppress_blank_empty = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!prefixcmp(var, "diff.")) {
                const char *ep = strrchr(var, '.');
                if (ep != var + 4) {
@@ -369,7 +376,6 @@ static void diff_words_append(char *line, unsigned long len,
 }
 
 struct diff_words_data {
-       struct xdiff_emit_state xm;
        struct diff_words_buffer minus, plus;
        FILE *file;
 };
@@ -459,11 +465,8 @@ static void diff_words_show(struct diff_words_data *diff_words)
 
        xpp.flags = XDF_NEED_MINIMAL;
        xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
-       ecb.outf = xdiff_outf;
-       ecb.priv = diff_words;
-       diff_words->xm.consume = fn_out_diff_words_aux;
-       xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
-
+       xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
+                     &xpp, &xecfg, &ecb);
        free(minus.ptr);
        free(plus.ptr);
        diff_words->minus.text.size = diff_words->plus.text.size = 0;
@@ -477,7 +480,6 @@ static void diff_words_show(struct diff_words_data *diff_words)
 typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
 
 struct emit_callback {
-       struct xdiff_emit_state xm;
        int nparents, color_diff;
        unsigned ws_rule;
        sane_truncate_fn truncate;
@@ -580,6 +582,12 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
 
+       if (diff_suppress_blank_empty
+           && len == 2 && line[0] == ' ' && line[1] == '\n') {
+               line[0] = '\n';
+               len = 1;
+       }
+
        /* This is not really necessary for now because
         * this codepath only deals with two-way diffs.
         */
@@ -708,8 +716,6 @@ static char *pprint_rename(const char *a, const char *b)
 }
 
 struct diffstat_t {
-       struct xdiff_emit_state xm;
-
        int nr;
        int alloc;
        struct diffstat_file {
@@ -1139,7 +1145,6 @@ static void free_diffstat_info(struct diffstat_t *diffstat)
 }
 
 struct checkdiff_t {
-       struct xdiff_emit_state xm;
        const char *filename;
        int lineno;
        struct diff_options *o;
@@ -1384,6 +1389,8 @@ static struct builtin_funcname_pattern {
        const char *name;
        const char *pattern;
 } builtin_funcname_pattern[] = {
+       { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" },
+       { "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$" },
        { "java", "!^[  ]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
                        "new\\|return\\|switch\\|throw\\|while\\)\n"
                        "^[     ]*\\(\\([       ]*"
@@ -1395,9 +1402,9 @@ static struct builtin_funcname_pattern {
                        "\\|"
                        "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$"
                        },
-       { "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" },
-       { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
+       { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" },
        { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" },
+       { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
 };
 
 static const char *diff_funcname_pattern(struct diff_filespec *one)
@@ -1529,15 +1536,13 @@ static void builtin_diff(const char *name_a,
                        xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
                else if (!prefixcmp(diffopts, "-u"))
                        xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
-               ecb.outf = xdiff_outf;
-               ecb.priv = &ecbdata;
-               ecbdata.xm.consume = fn_out_consume;
                if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
                        ecbdata.diff_words =
                                xcalloc(1, sizeof(struct diff_words_data));
                        ecbdata.diff_words->file = o->file;
                }
-               xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+               xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
+                             &xpp, &xecfg, &ecb);
                if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
                        free_diff_words_data(&ecbdata);
        }
@@ -1588,9 +1593,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
 
                memset(&xecfg, 0, sizeof(xecfg));
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
-               ecb.outf = xdiff_outf;
-               ecb.priv = diffstat;
-               xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+               xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
+                             &xpp, &xecfg, &ecb);
        }
 
  free_and_return:
@@ -1611,7 +1615,6 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
                return;
 
        memset(&data, 0, sizeof(data));
-       data.xm.consume = checkdiff_consume;
        data.filename = name_b ? name_b : name_a;
        data.lineno = 0;
        data.o = o;
@@ -1637,9 +1640,8 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
                memset(&xecfg, 0, sizeof(xecfg));
                xecfg.ctxlen = 1; /* at least one context line */
                xpp.flags = XDF_NEED_MINIMAL;
-               ecb.outf = xdiff_outf;
-               ecb.priv = &data;
-               xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+               xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
+                             &xpp, &xecfg, &ecb);
 
                if ((data.ws_rule & WS_TRAILING_SPACE) &&
                    data.trailing_blanks_start) {
@@ -3027,7 +3029,6 @@ static void diff_summary(FILE *file, struct diff_filepair *p)
 }
 
 struct patch_id_t {
-       struct xdiff_emit_state xm;
        SHA_CTX *ctx;
        int patchlen;
 };
@@ -3072,7 +3073,6 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
        SHA1_Init(&ctx);
        memset(&data, 0, sizeof(struct patch_id_t));
        data.ctx = &ctx;
-       data.xm.consume = patch_id_consume;
 
        for (i = 0; i < q->nr; i++) {
                xpparam_t xpp;
@@ -3137,9 +3137,8 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
                xpp.flags = XDF_NEED_MINIMAL;
                xecfg.ctxlen = 3;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
-               ecb.outf = xdiff_outf;
-               ecb.priv = &data;
-               xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+               xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
+                             &xpp, &xecfg, &ecb);
        }
 
        SHA1_Final(sha1, &ctx);
@@ -3216,7 +3215,6 @@ void diff_flush(struct diff_options *options)
                struct diffstat_t diffstat;
 
                memset(&diffstat, 0, sizeof(struct diffstat_t));
-               diffstat.xm.consume = diffstat_consume;
                for (i = 0; i < q->nr; i++) {
                        struct diff_filepair *p = q->queue[i];
                        if (check_pair_status(p))
diff --git a/dir.c b/dir.c
index 109e05b01346ac13296dfbcfa2355a43d97731cd..acf1001c4f9dd51587e3b07e52d4101ff3f1cb66 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -52,11 +52,6 @@ int common_prefix(const char **pathspec)
        return prefix;
 }
 
-static inline int special_char(unsigned char c1)
-{
-       return !c1 || c1 == '*' || c1 == '[' || c1 == '?' || c1 == '\\';
-}
-
 /*
  * Does 'match' matches the given name?
  * A match is found if
@@ -80,7 +75,7 @@ static int match_one(const char *match, const char *name, int namelen)
        for (;;) {
                unsigned char c1 = *match;
                unsigned char c2 = *name;
-               if (special_char(c1))
+               if (isspecial(c1))
                        break;
                if (c1 != c2)
                        return 0;
@@ -680,17 +675,12 @@ static int cmp_name(const void *p1, const void *p2)
  */
 static int simple_length(const char *match)
 {
-       const char special[256] = {
-               [0] = 1, ['?'] = 1,
-               ['\\'] = 1, ['*'] = 1,
-               ['['] = 1
-       };
        int len = -1;
 
        for (;;) {
                unsigned char c = *match++;
                len++;
-               if (special[c])
+               if (isspecial(c))
                        return len;
        }
 }
@@ -727,8 +717,12 @@ static void free_simplify(struct path_simplify *simplify)
 
 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
 {
-       struct path_simplify *simplify = create_simplify(pathspec);
+       struct path_simplify *simplify;
+
+       if (has_symlink_leading_path(strlen(path), path))
+               return dir->nr;
 
+       simplify = create_simplify(pathspec);
        read_directory_recursive(dir, path, base, baselen, 0, simplify);
        free_simplify(simplify);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
index cf89cdf4598b3796724a85aa707f740245155cdc..db2836fbdee6c1e069e6f5955031087d778cddf5 100644 (file)
 #include <iconv.h>
 #endif
 
+#ifndef NO_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
 /* On most systems <limits.h> would have given us this, but
  * not on some systems (e.g. GNU/Hurd).
  */
@@ -192,6 +197,12 @@ extern int git_munmap(void *start, size_t length);
 
 #endif /* NO_MMAP */
 
+#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+#define on_disk_bytes(st) ((st).st_size)
+#else
+#define on_disk_bytes(st) ((st).st_blocks * 512)
+#endif
+
 #define DEFAULT_PACKED_GIT_LIMIT \
        ((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
 
@@ -318,11 +329,13 @@ extern unsigned char sane_ctype[256];
 #define GIT_SPACE 0x01
 #define GIT_DIGIT 0x02
 #define GIT_ALPHA 0x04
+#define GIT_SPECIAL 0x08
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
 #define isdigit(x) sane_istest(x,GIT_DIGIT)
 #define isalpha(x) sane_istest(x,GIT_ALPHA)
 #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define isspecial(x) sane_istest(x,GIT_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
 
index a324cf0596ee0f05831190ce724fe9134bc7f568..2871a59e3280584a2620a67f53e2e28dc808dac0 100755 (executable)
@@ -416,15 +416,17 @@ if [ "$filter_tag_name" ]; then
                echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
 
                if [ "$type" = "tag" ]; then
-                       new_sha1=$(git cat-file tag "$ref" |
+                       new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \
+                                               "$new_sha1" "$new_ref"
+                               git cat-file tag "$ref" |
                                sed -n \
                                    -e "1,/^$/{
-                                         s/^object .*/object $new_sha1/
-                                         s/^type .*/type commit/
-                                         s/^tag .*/tag $new_ref/
+                                         /^object /d
+                                         /^type /d
+                                         /^tag /d
                                        }" \
                                    -e '/^-----BEGIN PGP SIGNATURE-----/q' \
-                                   -e 'p' |
+                                   -e 'p' |
                                git mktag) ||
                                die "Could not create new tag object for $ref"
                        if git cat-file tag "$ref" | \
index 645e1147dc886f2b1ca6d2020b44db746b082bf0..1dadbb49666c6d796df76babbfd291a2de4357e4 100755 (executable)
@@ -61,7 +61,7 @@ do
                exit 2
        esac
 
-       common=$(git merge-base --all $MRC $SHA1) ||
+       common=$(git merge-base --all $SHA1 $MRC) ||
                die "Unable to find common commit with $SHA1"
 
        case "$LF$common$LF" in
@@ -100,14 +100,7 @@ do
                next=$(git write-tree 2>/dev/null)
        fi
 
-       # We have merged the other branch successfully.  Ideally
-       # we could implement OR'ed heads in merge-base, and keep
-       # a list of commits we have merged so far in MRC to feed
-       # them to merge-base, but we approximate it by keep using
-       # the current MRC.  We used to update it to $common, which
-       # was incorrectly doing AND'ed merge-base here, which was
-       # unneeded.
-
+       MRC="$MRC $SHA1"
        MRT=$next
 done
 
index b40f876a2ca9fe985cedc622ab28a9f461edc5ab..1c39b593a628cc94d24d227cc5d92e00ac5e71ed 100755 (executable)
@@ -6,9 +6,10 @@
 
 USAGE="[--quiet] [--cached] \
 [add <repo> [-b branch] <path>]|[status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \
-[--] [<path>...]"
+[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
 OPTIONS_SPEC=
 . git-sh-setup
+. git-parse-remote
 require_work_tree
 
 command=
@@ -30,12 +31,11 @@ say()
 # Resolve relative url by appending to parent's url
 resolve_relative_url ()
 {
-       branch="$(git symbolic-ref HEAD 2>/dev/null)"
-       remote="$(git config branch.${branch#refs/heads/}.remote)"
-       remote="${remote:-origin}"
+       remote=$(get_default_remote)
        remoteurl=$(git config "remote.$remote.url") ||
                die "remote ($remote) does not have a url defined in .git/config"
        url="$1"
+       remoteurl=${remoteurl%/}
        while test -n "$url"
        do
                case "$url" in
@@ -50,7 +50,16 @@ resolve_relative_url ()
                        break;;
                esac
        done
-       echo "$remoteurl/$url"
+       echo "$remoteurl/${url%/}"
+}
+
+#
+# Get submodule info for registered submodules
+# $@ = path to limit submodule list
+#
+module_list()
+{
+       git ls-files --stage -- "$@" | grep '^160000 '
 }
 
 #
@@ -198,6 +207,26 @@ cmd_add()
        die "Failed to register submodule '$path'"
 }
 
+#
+# Execute an arbitrary command sequence in each checked out
+# submodule
+#
+# $@ = command to execute
+#
+cmd_foreach()
+{
+       module_list |
+       while read mode sha1 stage path
+       do
+               if test -e "$path"/.git
+               then
+                       say "Entering '$path'"
+                       (cd "$path" && eval "$@") ||
+                       die "Stopping at '$path'; script returned non-zero status."
+               fi
+       done
+}
+
 #
 # Register submodules in .git/config
 #
@@ -226,7 +255,7 @@ cmd_init()
                shift
        done
 
-       git ls-files --stage -- "$@" | grep '^160000 ' |
+       module_list "$@" |
        while read mode sha1 stage path
        do
                # Skip already registered paths
@@ -284,7 +313,7 @@ cmd_update()
                esac
        done
 
-       git ls-files --stage -- "$@" | grep '^160000 ' |
+       module_list "$@" |
        while read mode sha1 stage path
        do
                name=$(module_name "$path") || exit
@@ -549,7 +578,7 @@ cmd_status()
                shift
        done
 
-       git ls-files --stage -- "$@" | grep '^160000 ' |
+       module_list "$@" |
        while read mode sha1 stage path
        do
                name=$(module_name "$path") || exit
@@ -573,6 +602,50 @@ cmd_status()
                fi
        done
 }
+#
+# Sync remote urls for submodules
+# This makes the value for remote.$remote.url match the value
+# specified in .gitmodules.
+#
+cmd_sync()
+{
+       while test $# -ne 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       quiet=1
+                       shift
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+       done
+       cd_to_toplevel
+       module_list "$@" |
+       while read mode sha1 stage path
+       do
+               name=$(module_name "$path")
+               url=$(git config -f .gitmodules --get submodule."$name".url)
+               if test -e "$path"/.git
+               then
+               (
+                       unset GIT_DIR
+                       cd "$path"
+                       remote=$(get_default_remote)
+                       say "Synchronizing submodule url for '$name'"
+                       git config remote."$remote".url "$url"
+               )
+               fi
+       done
+}
 
 # This loop parses the command line arguments to find the
 # subcommand name to dispatch.  Parsing of the subcommand specific
@@ -583,7 +656,7 @@ cmd_status()
 while test $# != 0 && test -z "$command"
 do
        case "$1" in
-       add | init | update | status | summary)
+       add | foreach | init | update | status | summary | sync)
                command=$1
                ;;
        -q|--quiet)
index 46c06a9552dac5475afc607c3fe2bf00801eb055..a4d127cf78013ca43339c5493b7f7ef90b2145a8 100644 (file)
@@ -7,16 +7,14 @@
 #include "cache.h"
 #include "blob.h"
 #include "quote.h"
+#include "parse-options.h"
 
-static void hash_object(const char *path, enum object_type type, int write_object)
+static void hash_fd(int fd, const char *type, int write_object, const char *path)
 {
-       int fd;
        struct stat st;
        unsigned char sha1[20];
-       fd = open(path, O_RDONLY);
-       if (fd < 0 ||
-           fstat(fd, &st) < 0 ||
-           index_fd(sha1, fd, &st, write_object, type, path))
+       if (fstat(fd, &st) < 0 ||
+           index_fd(sha1, fd, &st, write_object, type_from_string(type), path))
                die(write_object
                    ? "Unable to add %s to database"
                    : "Unable to hash %s", path);
@@ -24,12 +22,14 @@ static void hash_object(const char *path, enum object_type type, int write_objec
        maybe_flush_or_die(stdout, "hash to stdout");
 }
 
-static void hash_stdin(const char *type, int write_object)
+static void hash_object(const char *path, const char *type, int write_object,
+                       const char *vpath)
 {
-       unsigned char sha1[20];
-       if (index_pipe(sha1, 0, type, write_object))
-               die("Unable to add stdin to database");
-       printf("%s\n", sha1_to_hex(sha1));
+       int fd;
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               die("Cannot open %s", path);
+       hash_fd(fd, type, write_object, vpath);
 }
 
 static void hash_stdin_paths(const char *type, int write_objects)
@@ -45,92 +45,91 @@ static void hash_stdin_paths(const char *type, int write_objects)
                                die("line is badly quoted");
                        strbuf_swap(&buf, &nbuf);
                }
-               hash_object(buf.buf, type_from_string(type), write_objects);
+               hash_object(buf.buf, type, write_objects, buf.buf);
        }
        strbuf_release(&buf);
        strbuf_release(&nbuf);
 }
 
-static const char hash_object_usage[] =
-"git hash-object [ [-t <type>] [-w] [--stdin] <file>... | --stdin-paths < <list-of-paths> ]";
+static const char * const hash_object_usage[] = {
+       "git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...",
+       "git hash-object  --stdin-paths < <list-of-paths>",
+       NULL
+};
 
-int main(int argc, char **argv)
+static const char *type;
+static int write_object;
+static int hashstdin;
+static int stdin_paths;
+static int no_filters;
+static const char *vpath;
+
+static const struct option hash_object_options[] = {
+       OPT_STRING('t', NULL, &type, "type", "object type"),
+       OPT_BOOLEAN('w', NULL, &write_object, "write the object into the object database"),
+       OPT_BOOLEAN( 0 , "stdin", &hashstdin, "read the object from stdin"),
+       OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, "read file names from stdin"),
+       OPT_BOOLEAN( 0 , "no-filters", &no_filters, "store file as is without filters"),
+       OPT_STRING( 0 , "path", &vpath, "file", "process file as it were from this path"),
+       OPT_END()
+};
+
+int main(int argc, const char **argv)
 {
        int i;
-       const char *type = blob_type;
-       int write_object = 0;
        const char *prefix = NULL;
        int prefix_length = -1;
-       int no_more_flags = 0;
-       int hashstdin = 0;
-       int stdin_paths = 0;
+       const char *errstr = NULL;
+
+       type = blob_type;
 
        git_config(git_default_config, NULL);
 
-       for (i = 1 ; i < argc; i++) {
-               if (!no_more_flags && argv[i][0] == '-') {
-                       if (!strcmp(argv[i], "-t")) {
-                               if (argc <= ++i)
-                                       usage(hash_object_usage);
-                               type = argv[i];
-                       }
-                       else if (!strcmp(argv[i], "-w")) {
-                               if (prefix_length < 0) {
-                                       prefix = setup_git_directory();
-                                       prefix_length =
-                                               prefix ? strlen(prefix) : 0;
-                               }
-                               write_object = 1;
-                       }
-                       else if (!strcmp(argv[i], "--")) {
-                               no_more_flags = 1;
-                       }
-                       else if (!strcmp(argv[i], "--help"))
-                               usage(hash_object_usage);
-                       else if (!strcmp(argv[i], "--stdin-paths")) {
-                               if (hashstdin) {
-                                       error("Can't use --stdin-paths with --stdin");
-                                       usage(hash_object_usage);
-                               }
-                               stdin_paths = 1;
-
-                       }
-                       else if (!strcmp(argv[i], "--stdin")) {
-                               if (stdin_paths) {
-                                       error("Can't use %s with --stdin-paths", argv[i]);
-                                       usage(hash_object_usage);
-                               }
-                               if (hashstdin)
-                                       die("Multiple --stdin arguments are not supported");
-                               hashstdin = 1;
-                       }
-                       else
-                               usage(hash_object_usage);
-               }
-               else {
-                       const char *arg = argv[i];
-
-                       if (stdin_paths) {
-                               error("Can't specify files (such as \"%s\") with --stdin-paths", arg);
-                               usage(hash_object_usage);
-                       }
-
-                       if (hashstdin) {
-                               hash_stdin(type, write_object);
-                               hashstdin = 0;
-                       }
-                       if (0 <= prefix_length)
-                               arg = prefix_filename(prefix, prefix_length,
-                                                     arg);
-                       hash_object(arg, type_from_string(type), write_object);
-                       no_more_flags = 1;
-               }
+       argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0);
+
+       if (write_object) {
+               prefix = setup_git_directory();
+               prefix_length = prefix ? strlen(prefix) : 0;
+               if (vpath && prefix)
+                       vpath = prefix_filename(prefix, prefix_length, vpath);
+       }
+
+       if (stdin_paths) {
+               if (hashstdin)
+                       errstr = "Can't use --stdin-paths with --stdin";
+               else if (argc)
+                       errstr = "Can't specify files with --stdin-paths";
+               else if (vpath)
+                       errstr = "Can't use --stdin-paths with --path";
+               else if (no_filters)
+                       errstr = "Can't use --stdin-paths with --no-filters";
+       }
+       else {
+               if (hashstdin > 1)
+                       errstr = "Multiple --stdin arguments are not supported";
+               if (vpath && no_filters)
+                       errstr = "Can't use --path with --no-filters";
+       }
+
+       if (errstr) {
+               error (errstr);
+               usage_with_options(hash_object_usage, hash_object_options);
+       }
+
+       if (hashstdin)
+               hash_fd(0, type, write_object, vpath);
+
+       for (i = 0 ; i < argc; i++) {
+               const char *arg = argv[i];
+
+               if (0 <= prefix_length)
+                       arg = prefix_filename(prefix, prefix_length, arg);
+               hash_object(arg, type, write_object,
+                           no_filters ? NULL : vpath ? vpath : arg);
        }
 
        if (stdin_paths)
                hash_stdin_paths(type, write_object);
 
-       if (hashstdin)
-               hash_stdin(type, write_object);
        return 0;
 }
diff --git a/help.c b/help.c
index dc0786d8002ef633cae4485eb5b25cdb6cf17df4..b278257aab15f7a195dd4f3c234b3e6b68aaa8f3 100644 (file)
--- a/help.c
+++ b/help.c
@@ -1,276 +1,7 @@
-/*
- * builtin-help.c
- *
- * Builtin help-related commands (help, usage, version)
- */
 #include "cache.h"
 #include "builtin.h"
 #include "exec_cmd.h"
-#include "common-cmds.h"
-#include "parse-options.h"
-#include "run-command.h"
-
-static struct man_viewer_list {
-       struct man_viewer_list *next;
-       char name[FLEX_ARRAY];
-} *man_viewer_list;
-
-static struct man_viewer_info_list {
-       struct man_viewer_info_list *next;
-       const char *info;
-       char name[FLEX_ARRAY];
-} *man_viewer_info_list;
-
-enum help_format {
-       HELP_FORMAT_MAN,
-       HELP_FORMAT_INFO,
-       HELP_FORMAT_WEB,
-};
-
-static int show_all = 0;
-static enum help_format help_format = HELP_FORMAT_MAN;
-static struct option builtin_help_options[] = {
-       OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
-       OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
-       OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
-                       HELP_FORMAT_WEB),
-       OPT_SET_INT('i', "info", &help_format, "show info page",
-                       HELP_FORMAT_INFO),
-       OPT_END(),
-};
-
-static const char * const builtin_help_usage[] = {
-       "git help [--all] [--man|--web|--info] [command]",
-       NULL
-};
-
-static enum help_format parse_help_format(const char *format)
-{
-       if (!strcmp(format, "man"))
-               return HELP_FORMAT_MAN;
-       if (!strcmp(format, "info"))
-               return HELP_FORMAT_INFO;
-       if (!strcmp(format, "web") || !strcmp(format, "html"))
-               return HELP_FORMAT_WEB;
-       die("unrecognized help format '%s'", format);
-}
-
-static const char *get_man_viewer_info(const char *name)
-{
-       struct man_viewer_info_list *viewer;
-
-       for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
-       {
-               if (!strcasecmp(name, viewer->name))
-                       return viewer->info;
-       }
-       return NULL;
-}
-
-static int check_emacsclient_version(void)
-{
-       struct strbuf buffer = STRBUF_INIT;
-       struct child_process ec_process;
-       const char *argv_ec[] = { "emacsclient", "--version", NULL };
-       int version;
-
-       /* emacsclient prints its version number on stderr */
-       memset(&ec_process, 0, sizeof(ec_process));
-       ec_process.argv = argv_ec;
-       ec_process.err = -1;
-       ec_process.stdout_to_stderr = 1;
-       if (start_command(&ec_process)) {
-               fprintf(stderr, "Failed to start emacsclient.\n");
-               return -1;
-       }
-       strbuf_read(&buffer, ec_process.err, 20);
-       close(ec_process.err);
-
-       /*
-        * Don't bother checking return value, because "emacsclient --version"
-        * seems to always exits with code 1.
-        */
-       finish_command(&ec_process);
-
-       if (prefixcmp(buffer.buf, "emacsclient")) {
-               fprintf(stderr, "Failed to parse emacsclient version.\n");
-               strbuf_release(&buffer);
-               return -1;
-       }
-
-       strbuf_remove(&buffer, 0, strlen("emacsclient"));
-       version = atoi(buffer.buf);
-
-       if (version < 22) {
-               fprintf(stderr,
-                       "emacsclient version '%d' too old (< 22).\n",
-                       version);
-               strbuf_release(&buffer);
-               return -1;
-       }
-
-       strbuf_release(&buffer);
-       return 0;
-}
-
-static void exec_woman_emacs(const char* path, const char *page)
-{
-       if (!check_emacsclient_version()) {
-               /* This works only with emacsclient version >= 22. */
-               struct strbuf man_page = STRBUF_INIT;
-
-               if (!path)
-                       path = "emacsclient";
-               strbuf_addf(&man_page, "(woman \"%s\")", page);
-               execlp(path, "emacsclient", "-e", man_page.buf, NULL);
-               warning("failed to exec '%s': %s", path, strerror(errno));
-       }
-}
-
-static void exec_man_konqueror(const char* path, const char *page)
-{
-       const char *display = getenv("DISPLAY");
-       if (display && *display) {
-               struct strbuf man_page = STRBUF_INIT;
-               const char *filename = "kfmclient";
-
-               /* It's simpler to launch konqueror using kfmclient. */
-               if (path) {
-                       const char *file = strrchr(path, '/');
-                       if (file && !strcmp(file + 1, "konqueror")) {
-                               char *new = xstrdup(path);
-                               char *dest = strrchr(new, '/');
-
-                               /* strlen("konqueror") == strlen("kfmclient") */
-                               strcpy(dest + 1, "kfmclient");
-                               path = new;
-                       }
-                       if (file)
-                               filename = file;
-               } else
-                       path = "kfmclient";
-               strbuf_addf(&man_page, "man:%s(1)", page);
-               execlp(path, filename, "newTab", man_page.buf, NULL);
-               warning("failed to exec '%s': %s", path, strerror(errno));
-       }
-}
-
-static void exec_man_man(const char* path, const char *page)
-{
-       if (!path)
-               path = "man";
-       execlp(path, "man", page, NULL);
-       warning("failed to exec '%s': %s", path, strerror(errno));
-}
-
-static void exec_man_cmd(const char *cmd, const char *page)
-{
-       struct strbuf shell_cmd = STRBUF_INIT;
-       strbuf_addf(&shell_cmd, "%s %s", cmd, page);
-       execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
-       warning("failed to exec '%s': %s", cmd, strerror(errno));
-}
-
-static void add_man_viewer(const char *name)
-{
-       struct man_viewer_list **p = &man_viewer_list;
-       size_t len = strlen(name);
-
-       while (*p)
-               p = &((*p)->next);
-       *p = xcalloc(1, (sizeof(**p) + len + 1));
-       strncpy((*p)->name, name, len);
-}
-
-static int supported_man_viewer(const char *name, size_t len)
-{
-       return (!strncasecmp("man", name, len) ||
-               !strncasecmp("woman", name, len) ||
-               !strncasecmp("konqueror", name, len));
-}
-
-static void do_add_man_viewer_info(const char *name,
-                                  size_t len,
-                                  const char *value)
-{
-       struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
-
-       strncpy(new->name, name, len);
-       new->info = xstrdup(value);
-       new->next = man_viewer_info_list;
-       man_viewer_info_list = new;
-}
-
-static int add_man_viewer_path(const char *name,
-                              size_t len,
-                              const char *value)
-{
-       if (supported_man_viewer(name, len))
-               do_add_man_viewer_info(name, len, value);
-       else
-               warning("'%s': path for unsupported man viewer.\n"
-                       "Please consider using 'man.<tool>.cmd' instead.",
-                       name);
-
-       return 0;
-}
-
-static int add_man_viewer_cmd(const char *name,
-                             size_t len,
-                             const char *value)
-{
-       if (supported_man_viewer(name, len))
-               warning("'%s': cmd for supported man viewer.\n"
-                       "Please consider using 'man.<tool>.path' instead.",
-                       name);
-       else
-               do_add_man_viewer_info(name, len, value);
-
-       return 0;
-}
-
-static int add_man_viewer_info(const char *var, const char *value)
-{
-       const char *name = var + 4;
-       const char *subkey = strrchr(name, '.');
-
-       if (!subkey)
-               return error("Config with no key for man viewer: %s", name);
-
-       if (!strcmp(subkey, ".path")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               return add_man_viewer_path(name, subkey - name, value);
-       }
-       if (!strcmp(subkey, ".cmd")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               return add_man_viewer_cmd(name, subkey - name, value);
-       }
-
-       warning("'%s': unsupported man viewer sub key.", subkey);
-       return 0;
-}
-
-static int git_help_config(const char *var, const char *value, void *cb)
-{
-       if (!strcmp(var, "help.format")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               help_format = parse_help_format(value);
-               return 0;
-       }
-       if (!strcmp(var, "man.viewer")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               add_man_viewer(value);
-               return 0;
-       }
-       if (!prefixcmp(var, "man."))
-               return add_man_viewer_info(var, value);
-
-       return git_default_config(var, value, cb);
-}
+#include "help.h"
 
 /* most GUI terminals set COLUMNS (although some don't export it) */
 static int term_columns(void)
@@ -294,24 +25,9 @@ static int term_columns(void)
        return 80;
 }
 
-static inline void mput_char(char c, unsigned int num)
+void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
-       while(num--)
-               putchar(c);
-}
-
-static struct cmdnames {
-       int alloc;
-       int cnt;
-       struct cmdname {
-               size_t len;
-               char name[1];
-       } **names;
-} main_cmds, other_cmds;
-
-static void add_cmdname(struct cmdnames *cmds, const char *name, int len)
-{
-       struct cmdname *ent = xmalloc(sizeof(*ent) + len);
+       struct cmdname *ent = xmalloc(sizeof(*ent) + len + 1);
 
        ent->len = len;
        memcpy(ent->name, name, len);
@@ -342,7 +58,7 @@ static void uniq(struct cmdnames *cmds)
        cmds->cnt = j;
 }
 
-static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
 {
        int ci, cj, ei;
        int cmp;
@@ -417,19 +133,21 @@ static int is_executable(const char *name)
        return st.st_mode & S_IXUSR;
 }
 
-static unsigned int list_commands_in_dir(struct cmdnames *cmds,
-                                        const char *path)
+static void list_commands_in_dir(struct cmdnames *cmds,
+                                        const char *path,
+                                        const char *prefix)
 {
-       unsigned int longest = 0;
-       const char *prefix = "git-";
-       int prefix_len = strlen(prefix);
+       int prefix_len;
        DIR *dir = opendir(path);
        struct dirent *de;
        struct strbuf buf = STRBUF_INIT;
        int len;
 
        if (!dir)
-               return 0;
+               return;
+       if (!prefix)
+               prefix = "git-";
+       prefix_len = strlen(prefix);
 
        strbuf_addf(&buf, "%s/", path);
        len = buf.len;
@@ -449,100 +167,81 @@ static unsigned int list_commands_in_dir(struct cmdnames *cmds,
                if (has_extension(de->d_name, ".exe"))
                        entlen -= 4;
 
-               if (longest < entlen)
-                       longest = entlen;
-
                add_cmdname(cmds, de->d_name + prefix_len, entlen);
        }
        closedir(dir);
        strbuf_release(&buf);
-
-       return longest;
 }
 
-static unsigned int load_command_list(void)
+void load_command_list(const char *prefix,
+               struct cmdnames *main_cmds,
+               struct cmdnames *other_cmds)
 {
-       unsigned int longest = 0;
-       unsigned int len;
        const char *env_path = getenv("PATH");
-       char *paths, *path, *colon;
        const char *exec_path = git_exec_path();
 
-       if (exec_path)
-               longest = list_commands_in_dir(&main_cmds, exec_path);
-
-       if (!env_path) {
-               fprintf(stderr, "PATH not set\n");
-               exit(1);
+       if (exec_path) {
+               list_commands_in_dir(main_cmds, exec_path, prefix);
+               qsort(main_cmds->names, main_cmds->cnt,
+                     sizeof(*main_cmds->names), cmdname_compare);
+               uniq(main_cmds);
        }
 
-       path = paths = xstrdup(env_path);
-       while (1) {
-               if ((colon = strchr(path, PATH_SEP)))
-                       *colon = 0;
+       if (env_path) {
+               char *paths, *path, *colon;
+               path = paths = xstrdup(env_path);
+               while (1) {
+                       if ((colon = strchr(path, PATH_SEP)))
+                               *colon = 0;
 
-               len = list_commands_in_dir(&other_cmds, path);
-               if (len > longest)
-                       longest = len;
+                       list_commands_in_dir(other_cmds, path, prefix);
 
-               if (!colon)
-                       break;
-               path = colon + 1;
-       }
-       free(paths);
-
-       qsort(main_cmds.names, main_cmds.cnt,
-             sizeof(*main_cmds.names), cmdname_compare);
-       uniq(&main_cmds);
-
-       qsort(other_cmds.names, other_cmds.cnt,
-             sizeof(*other_cmds.names), cmdname_compare);
-       uniq(&other_cmds);
-       exclude_cmds(&other_cmds, &main_cmds);
+                       if (!colon)
+                               break;
+                       path = colon + 1;
+               }
+               free(paths);
 
-       return longest;
+               qsort(other_cmds->names, other_cmds->cnt,
+                     sizeof(*other_cmds->names), cmdname_compare);
+               uniq(other_cmds);
+       }
+       exclude_cmds(other_cmds, main_cmds);
 }
 
-static void list_commands(void)
+void list_commands(const char *title, struct cmdnames *main_cmds,
+                  struct cmdnames *other_cmds)
 {
-       unsigned int longest = load_command_list();
-       const char *exec_path = git_exec_path();
+       int i, longest = 0;
 
-       if (main_cmds.cnt) {
-               printf("available git commands in '%s'\n", exec_path);
-               printf("----------------------------");
-               mput_char('-', strlen(exec_path));
+       for (i = 0; i < main_cmds->cnt; i++)
+               if (longest < main_cmds->names[i]->len)
+                       longest = main_cmds->names[i]->len;
+       for (i = 0; i < other_cmds->cnt; i++)
+               if (longest < other_cmds->names[i]->len)
+                       longest = other_cmds->names[i]->len;
+
+       if (main_cmds->cnt) {
+               const char *exec_path = git_exec_path();
+               printf("available %s in '%s'\n", title, exec_path);
+               printf("----------------");
+               mput_char('-', strlen(title) + strlen(exec_path));
                putchar('\n');
-               pretty_print_string_list(&main_cmds, longest);
+               pretty_print_string_list(main_cmds, longest);
                putchar('\n');
        }
 
-       if (other_cmds.cnt) {
-               printf("git commands available from elsewhere on your $PATH\n");
-               printf("---------------------------------------------------\n");
-               pretty_print_string_list(&other_cmds, longest);
+       if (other_cmds->cnt) {
+               printf("%s available from elsewhere on your $PATH\n", title);
+               printf("---------------------------------------");
+               mput_char('-', strlen(title));
+               putchar('\n');
+               pretty_print_string_list(other_cmds, longest);
                putchar('\n');
        }
 }
 
-void list_common_cmds_help(void)
-{
-       int i, longest = 0;
-
-       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-               if (longest < strlen(common_cmds[i].name))
-                       longest = strlen(common_cmds[i].name);
-       }
-
-       puts("The most commonly used git commands are:");
-       for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-               printf("   %s   ", common_cmds[i].name);
-               mput_char(' ', longest - strlen(common_cmds[i].name));
-               puts(common_cmds[i].help);
-       }
-}
-
-static int is_in_cmdlist(struct cmdnames *c, const char *s)
+int is_in_cmdlist(struct cmdnames *c, const char *s)
 {
        int i;
        for (i = 0; i < c->cnt; i++)
@@ -551,130 +250,6 @@ static int is_in_cmdlist(struct cmdnames *c, const char *s)
        return 0;
 }
 
-static int is_git_command(const char *s)
-{
-       load_command_list();
-       return is_in_cmdlist(&main_cmds, s) ||
-               is_in_cmdlist(&other_cmds, s) ||
-               !strcmp(s, "help");
-}
-
-static const char *prepend(const char *prefix, const char *cmd)
-{
-       size_t pre_len = strlen(prefix);
-       size_t cmd_len = strlen(cmd);
-       char *p = xmalloc(pre_len + cmd_len + 1);
-       memcpy(p, prefix, pre_len);
-       strcpy(p + pre_len, cmd);
-       return p;
-}
-
-static const char *cmd_to_page(const char *git_cmd)
-{
-       if (!git_cmd)
-               return "git";
-       else if (!prefixcmp(git_cmd, "git"))
-               return git_cmd;
-       else if (is_git_command(git_cmd))
-               return prepend("git-", git_cmd);
-       else
-               return prepend("git", git_cmd);
-}
-
-static void setup_man_path(void)
-{
-       struct strbuf new_path;
-       const char *old_path = getenv("MANPATH");
-
-       strbuf_init(&new_path, 0);
-
-       /* We should always put ':' after our path. If there is no
-        * old_path, the ':' at the end will let 'man' to try
-        * system-wide paths after ours to find the manual page. If
-        * there is old_path, we need ':' as delimiter. */
-       strbuf_addstr(&new_path, GIT_MAN_PATH);
-       strbuf_addch(&new_path, ':');
-       if (old_path)
-               strbuf_addstr(&new_path, old_path);
-
-       setenv("MANPATH", new_path.buf, 1);
-
-       strbuf_release(&new_path);
-}
-
-static void exec_viewer(const char *name, const char *page)
-{
-       const char *info = get_man_viewer_info(name);
-
-       if (!strcasecmp(name, "man"))
-               exec_man_man(info, page);
-       else if (!strcasecmp(name, "woman"))
-               exec_woman_emacs(info, page);
-       else if (!strcasecmp(name, "konqueror"))
-               exec_man_konqueror(info, page);
-       else if (info)
-               exec_man_cmd(info, page);
-       else
-               warning("'%s': unknown man viewer.", name);
-}
-
-static void show_man_page(const char *git_cmd)
-{
-       struct man_viewer_list *viewer;
-       const char *page = cmd_to_page(git_cmd);
-
-       setup_man_path();
-       for (viewer = man_viewer_list; viewer; viewer = viewer->next)
-       {
-               exec_viewer(viewer->name, page); /* will return when unable */
-       }
-       exec_viewer("man", page);
-       die("no man viewer handled the request");
-}
-
-static void show_info_page(const char *git_cmd)
-{
-       const char *page = cmd_to_page(git_cmd);
-       setenv("INFOPATH", GIT_INFO_PATH, 1);
-       execlp("info", "info", "gitman", page, NULL);
-}
-
-static void get_html_page_path(struct strbuf *page_path, const char *page)
-{
-       struct stat st;
-       const char *html_path = system_path(GIT_HTML_PATH);
-
-       /* Check that we have a git documentation directory. */
-       if (stat(mkpath("%s/git.html", html_path), &st)
-           || !S_ISREG(st.st_mode))
-               die("'%s': not a documentation directory.", html_path);
-
-       strbuf_init(page_path, 0);
-       strbuf_addf(page_path, "%s/%s.html", html_path, page);
-}
-
-/*
- * If open_html is not defined in a platform-specific way (see for
- * example compat/mingw.h), we use the script web--browse to display
- * HTML.
- */
-#ifndef open_html
-void open_html(const char *path)
-{
-       execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
-}
-#endif
-
-static void show_html_page(const char *git_cmd)
-{
-       const char *page = cmd_to_page(git_cmd);
-       struct strbuf page_path; /* it leaks but we exec bellow */
-
-       get_html_page_path(&page_path, page);
-
-       open_html(page_path.buf);
-}
-
 void help_unknown_cmd(const char *cmd)
 {
        fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);
@@ -686,49 +261,3 @@ int cmd_version(int argc, const char **argv, const char *prefix)
        printf("git version %s\n", git_version_string);
        return 0;
 }
-
-int cmd_help(int argc, const char **argv, const char *prefix)
-{
-       int nongit;
-       const char *alias;
-
-       setup_git_directory_gently(&nongit);
-       git_config(git_help_config, NULL);
-
-       argc = parse_options(argc, argv, builtin_help_options,
-                       builtin_help_usage, 0);
-
-       if (show_all) {
-               printf("usage: %s\n\n", git_usage_string);
-               list_commands();
-               printf("%s\n", git_more_info_string);
-               return 0;
-       }
-
-       if (!argv[0]) {
-               printf("usage: %s\n\n", git_usage_string);
-               list_common_cmds_help();
-               printf("\n%s\n", git_more_info_string);
-               return 0;
-       }
-
-       alias = alias_lookup(argv[0]);
-       if (alias && !is_git_command(argv[0])) {
-               printf("`git %s' is aliased to `%s'\n", argv[0], alias);
-               return 0;
-       }
-
-       switch (help_format) {
-       case HELP_FORMAT_MAN:
-               show_man_page(argv[0]);
-               break;
-       case HELP_FORMAT_INFO:
-               show_info_page(argv[0]);
-               break;
-       case HELP_FORMAT_WEB:
-               show_html_page(argv[0]);
-               break;
-       }
-
-       return 0;
-}
diff --git a/help.h b/help.h
new file mode 100644 (file)
index 0000000..2733433
--- /dev/null
+++ b/help.h
@@ -0,0 +1,29 @@
+#ifndef HELP_H
+#define HELP_H
+
+struct cmdnames {
+       int alloc;
+       int cnt;
+       struct cmdname {
+               size_t len;
+               char name[FLEX_ARRAY];
+       } **names;
+};
+
+static inline void mput_char(char c, unsigned int num)
+{
+       while(num--)
+               putchar(c);
+}
+
+void load_command_list(const char *prefix,
+               struct cmdnames *main_cmds,
+               struct cmdnames *other_cmds);
+void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+/* Here we require that excludes is a sorted list. */
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+int is_in_cmdlist(struct cmdnames *c, const char *s);
+void list_commands(const char *title, struct cmdnames *main_cmds,
+                  struct cmdnames *other_cmds);
+
+#endif /* HELP_H */
index 1ec131092109aa3fbed3cd20f10b56a864584a94..af7e08c0943ada9a650f80fd5b5fc1df23c04b88 100644 (file)
  */
 
 #include "cache.h"
+#ifdef NO_OPENSSL
+typedef void *SSL;
+#endif
 
-typedef struct store_conf {
+struct store_conf {
        char *name;
        const char *path; /* should this be here? its interpretation is driver-specific */
        char *map_inbox;
        char *trash;
        unsigned max_size; /* off_t is overkill */
        unsigned trash_remote_new:1, trash_only_new:1;
-} store_conf_t;
+};
 
-typedef struct string_list {
+struct string_list {
        struct string_list *next;
        char string[1];
-} string_list_t;
+};
 
-typedef struct channel_conf {
+struct channel_conf {
        struct channel_conf *next;
        char *name;
-       store_conf_t *master, *slave;
+       struct store_conf *master, *slave;
        char *master_name, *slave_name;
        char *sync_state;
-       string_list_t *patterns;
+       struct string_list *patterns;
        int mops, sops;
        unsigned max_messages; /* for slave only */
-} channel_conf_t;
+};
 
-typedef struct group_conf {
+struct group_conf {
        struct group_conf *next;
        char *name;
-       string_list_t *channels;
-} group_conf_t;
+       struct string_list *channels;
+};
 
 /* For message->status */
 #define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
 #define M_DEAD         (1<<1) /* expunged */
 #define M_FLAGS        (1<<2) /* flags fetched */
 
-typedef struct message {
+struct message {
        struct message *next;
-       /* string_list_t *keywords; */
+       /* struct string_list *keywords; */
        size_t size; /* zero implies "not fetched" */
        int uid;
        unsigned char flags, status;
-} message_t;
+};
 
-typedef struct store {
-       store_conf_t *conf; /* foreign */
+struct store {
+       struct store_conf *conf; /* foreign */
 
        /* currently open mailbox */
        const char *name; /* foreign! maybe preset? */
        char *path; /* own */
-       message_t *msgs; /* own */
+       struct message *msgs; /* own */
        int uidvalidity;
        unsigned char opts; /* maybe preset? */
        /* note that the following do _not_ reflect stats from msgs, but mailbox totals */
        int count; /* # of messages */
        int recent; /* # of recent messages - don't trust this beyond the initial read */
-} store_t;
+};
 
-typedef struct {
+struct msg_data {
        char *data;
        int len;
        unsigned char flags;
        unsigned int crlf:1;
-} msg_data_t;
+};
 
 #define DRV_OK          0
 #define DRV_MSG_BAD     -1
@@ -96,14 +99,14 @@ typedef struct {
 
 static int Verbose, Quiet;
 
-static void imap_info( const char *, ... );
-static void imap_warn( const char *, ... );
+static void imap_info(const char *, ...);
+static void imap_warn(const char *, ...);
 
-static char *next_arg( char ** );
+static char *next_arg(char **);
 
-static void free_generic_messages( message_t * );
+static void free_generic_messages(struct message *);
 
-static int nfsnprintf( char *buf, int blen, const char *fmt, ... );
+static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
 
 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
 {
@@ -119,67 +122,70 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap)
        return len;
 }
 
-static void arc4_init( void );
-static unsigned char arc4_getbyte( void );
+static void arc4_init(void);
+static unsigned char arc4_getbyte(void);
 
-typedef struct imap_server_conf {
+struct imap_server_conf {
        char *name;
        char *tunnel;
        char *host;
        int port;
        char *user;
        char *pass;
-} imap_server_conf_t;
+       int use_ssl;
+       int ssl_verify;
+};
 
-typedef struct imap_store_conf {
-       store_conf_t gen;
-       imap_server_conf_t *server;
+struct imap_store_conf {
+       struct store_conf gen;
+       struct imap_server_conf *server;
        unsigned use_namespace:1;
-} imap_store_conf_t;
+};
 
-#define NIL    (void*)0x1
-#define LIST   (void*)0x2
+#define NIL    (void *)0x1
+#define LIST   (void *)0x2
 
-typedef struct _list {
-       struct _list *next, *child;
+struct imap_list {
+       struct imap_list *next, *child;
        char *val;
        int len;
-} list_t;
+};
 
-typedef struct {
+struct imap_socket {
        int fd;
-} Socket_t;
+       SSL *ssl;
+};
 
-typedef struct {
-       Socket_t sock;
+struct imap_buffer {
+       struct imap_socket sock;
        int bytes;
        int offset;
        char buf[1024];
-} buffer_t;
+};
 
 struct imap_cmd;
 
-typedef struct imap {
+struct imap {
        int uidnext; /* from SELECT responses */
-       list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
+       struct imap_list *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
        unsigned caps, rcaps; /* CAPABILITY results */
        /* command queue */
        int nexttag, num_in_progress, literal_pending;
        struct imap_cmd *in_progress, **in_progress_append;
-       buffer_t buf; /* this is BIG, so put it last */
-} imap_t;
+       struct imap_buffer buf; /* this is BIG, so put it last */
+};
 
-typedef struct imap_store {
-       store_t gen;
+struct imap_store {
+       struct store gen;
        int uidvalidity;
-       imap_t *imap;
+       struct imap *imap;
        const char *prefix;
        unsigned /*currentnc:1,*/ trashnc:1;
-} imap_store_t;
+};
 
 struct imap_cmd_cb {
-       int (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt );
-       void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response);
+       int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
+       void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
        void *ctx;
        char *data;
        int dlen;
@@ -201,6 +207,7 @@ enum CAPABILITY {
        UIDPLUS,
        LITERALPLUS,
        NAMESPACE,
+       STARTTLS,
 };
 
 static const char *cap_list[] = {
@@ -208,13 +215,14 @@ static const char *cap_list[] = {
        "UIDPLUS",
        "LITERAL+",
        "NAMESPACE",
+       "STARTTLS",
 };
 
 #define RESP_OK    0
 #define RESP_NO    1
 #define RESP_BAD   2
 
-static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd );
+static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd);
 
 
 static const char *Flags[] = {
@@ -225,42 +233,137 @@ static const char *Flags[] = {
        "Deleted",
 };
 
-static void
-socket_perror( const char *func, Socket_t *sock, int ret )
+#ifndef NO_OPENSSL
+static void ssl_socket_perror(const char *func)
+{
+       fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0));
+}
+#endif
+
+static void socket_perror(const char *func, struct imap_socket *sock, int ret)
+{
+#ifndef NO_OPENSSL
+       if (sock->ssl) {
+               int sslerr = SSL_get_error(sock->ssl, ret);
+               switch (sslerr) {
+               case SSL_ERROR_NONE:
+                       break;
+               case SSL_ERROR_SYSCALL:
+                       perror("SSL_connect");
+                       break;
+               default:
+                       ssl_socket_perror("SSL_connect");
+                       break;
+               }
+       } else
+#endif
+       {
+               if (ret < 0)
+                       perror(func);
+               else
+                       fprintf(stderr, "%s: unexpected EOF\n", func);
+       }
+}
+
+static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
 {
-       if (ret < 0)
-               perror( func );
+#ifdef NO_OPENSSL
+       fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+       return -1;
+#else
+       SSL_METHOD *meth;
+       SSL_CTX *ctx;
+       int ret;
+
+       SSL_library_init();
+       SSL_load_error_strings();
+
+       if (use_tls_only)
+               meth = TLSv1_method();
        else
-               fprintf( stderr, "%s: unexpected EOF\n", func );
+               meth = SSLv23_method();
+
+       if (!meth) {
+               ssl_socket_perror("SSLv23_method");
+               return -1;
+       }
+
+       ctx = SSL_CTX_new(meth);
+
+       if (verify)
+               SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+
+       if (!SSL_CTX_set_default_verify_paths(ctx)) {
+               ssl_socket_perror("SSL_CTX_set_default_verify_paths");
+               return -1;
+       }
+       sock->ssl = SSL_new(ctx);
+       if (!sock->ssl) {
+               ssl_socket_perror("SSL_new");
+               return -1;
+       }
+       if (!SSL_set_fd(sock->ssl, sock->fd)) {
+               ssl_socket_perror("SSL_set_fd");
+               return -1;
+       }
+
+       ret = SSL_connect(sock->ssl);
+       if (ret <= 0) {
+               socket_perror("SSL_connect", sock, ret);
+               return -1;
+       }
+
+       return 0;
+#endif
 }
 
-static int
-socket_read( Socket_t *sock, char *buf, int len )
+static int socket_read(struct imap_socket *sock, char *buf, int len)
 {
-       ssize_t n = xread( sock->fd, buf, len );
+       ssize_t n;
+#ifndef NO_OPENSSL
+       if (sock->ssl)
+               n = SSL_read(sock->ssl, buf, len);
+       else
+#endif
+               n = xread(sock->fd, buf, len);
        if (n <= 0) {
-               socket_perror( "read", sock, n );
-               close( sock->fd );
+               socket_perror("read", sock, n);
+               close(sock->fd);
                sock->fd = -1;
        }
        return n;
 }
 
-static int
-socket_write( Socket_t *sock, const char *buf, int len )
+static int socket_write(struct imap_socket *sock, const char *buf, int len)
 {
-       int n = write_in_full( sock->fd, buf, len );
+       int n;
+#ifndef NO_OPENSSL
+       if (sock->ssl)
+               n = SSL_write(sock->ssl, buf, len);
+       else
+#endif
+               n = write_in_full(sock->fd, buf, len);
        if (n != len) {
-               socket_perror( "write", sock, n );
-               close( sock->fd );
+               socket_perror("write", sock, n);
+               close(sock->fd);
                sock->fd = -1;
        }
        return n;
 }
 
+static void socket_shutdown(struct imap_socket *sock)
+{
+#ifndef NO_OPENSSL
+       if (sock->ssl) {
+               SSL_shutdown(sock->ssl);
+               SSL_free(sock->ssl);
+       }
+#endif
+       close(sock->fd);
+}
+
 /* simple line buffering */
-static int
-buffer_gets( buffer_t * b, char **s )
+static int buffer_gets(struct imap_buffer *b, char **s)
 {
        int n;
        int start = b->offset;
@@ -274,7 +377,7 @@ buffer_gets( buffer_t * b, char **s )
                                /* shift down used bytes */
                                *s = b->buf;
 
-                               assert( start <= b->bytes );
+                               assert(start <= b->bytes);
                                n = b->bytes - start;
 
                                if (n)
@@ -284,8 +387,8 @@ buffer_gets( buffer_t * b, char **s )
                                start = 0;
                        }
 
-                       n = socket_read( &b->sock, b->buf + b->bytes,
-                                        sizeof(b->buf) - b->bytes );
+                       n = socket_read(&b->sock, b->buf + b->bytes,
+                                        sizeof(b->buf) - b->bytes);
 
                        if (n <= 0)
                                return -1;
@@ -294,12 +397,12 @@ buffer_gets( buffer_t * b, char **s )
                }
 
                if (b->buf[b->offset] == '\r') {
-                       assert( b->offset + 1 < b->bytes );
+                       assert(b->offset + 1 < b->bytes);
                        if (b->buf[b->offset + 1] == '\n') {
                                b->buf[b->offset] = 0;  /* terminate the string */
                                b->offset += 2; /* next line */
                                if (Verbose)
-                                       puts( *s );
+                                       puts(*s);
                                return 0;
                        }
                }
@@ -309,39 +412,36 @@ buffer_gets( buffer_t * b, char **s )
        /* not reached */
 }
 
-static void
-imap_info( const char *msg, ... )
+static void imap_info(const char *msg, ...)
 {
        va_list va;
 
        if (!Quiet) {
-               va_start( va, msg );
-               vprintf( msg, va );
-               va_end( va );
-               fflush( stdout );
+               va_start(va, msg);
+               vprintf(msg, va);
+               va_end(va);
+               fflush(stdout);
        }
 }
 
-static void
-imap_warn( const char *msg, ... )
+static void imap_warn(const char *msg, ...)
 {
        va_list va;
 
        if (Quiet < 2) {
-               va_start( va, msg );
-               vfprintf( stderr, msg, va );
-               va_end( va );
+               va_start(va, msg);
+               vfprintf(stderr, msg, va);
+               va_end(va);
        }
 }
 
-static char *
-next_arg( char **s )
+static char *next_arg(char **s)
 {
        char *ret;
 
        if (!s || !*s)
                return NULL;
-       while (isspace( (unsigned char) **s ))
+       while (isspace((unsigned char) **s))
                (*s)++;
        if (!**s) {
                *s = NULL;
@@ -350,10 +450,10 @@ next_arg( char **s )
        if (**s == '"') {
                ++*s;
                ret = *s;
-               *s = strchr( *s, '"' );
+               *s = strchr(*s, '"');
        } else {
                ret = *s;
-               while (**s && !isspace( (unsigned char) **s ))
+               while (**s && !isspace((unsigned char) **s))
                        (*s)++;
        }
        if (*s) {
@@ -365,27 +465,25 @@ next_arg( char **s )
        return ret;
 }
 
-static void
-free_generic_messages( message_t *msgs )
+static void free_generic_messages(struct message *msgs)
 {
-       message_t *tmsg;
+       struct message *tmsg;
 
        for (; msgs; msgs = tmsg) {
                tmsg = msgs->next;
-               free( msgs );
+               free(msgs);
        }
 }
 
-static int
-nfsnprintf( char *buf, int blen, const char *fmt, ... )
+static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
 {
        int ret;
        va_list va;
 
-       va_start( va, fmt );
-       if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
-               die( "Fatal: buffer too small. Please report a bug.\n");
-       va_end( va );
+       va_start(va, fmt);
+       if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
+               die("Fatal: buffer too small. Please report a bug.\n");
+       va_end(va);
        return ret;
 }
 
@@ -393,21 +491,20 @@ static struct {
        unsigned char i, j, s[256];
 } rs;
 
-static void
-arc4_init( void )
+static void arc4_init(void)
 {
        int i, fd;
        unsigned char j, si, dat[128];
 
-       if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
-               fprintf( stderr, "Fatal: no random number source available.\n" );
-               exit( 3 );
+       if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) {
+               fprintf(stderr, "Fatal: no random number source available.\n");
+               exit(3);
        }
-       if (read_in_full( fd, dat, 128 ) != 128) {
-               fprintf( stderr, "Fatal: cannot read random number source.\n" );
-               exit( 3 );
+       if (read_in_full(fd, dat, 128) != 128) {
+               fprintf(stderr, "Fatal: cannot read random number source.\n");
+               exit(3);
        }
-       close( fd );
+       close(fd);
 
        for (i = 0; i < 256; i++)
                rs.s[i] = i;
@@ -423,8 +520,7 @@ arc4_init( void )
                arc4_getbyte();
 }
 
-static unsigned char
-arc4_getbyte( void )
+static unsigned char arc4_getbyte(void)
 {
        unsigned char si, sj;
 
@@ -437,54 +533,53 @@ arc4_getbyte( void )
        return rs.s[(si + sj) & 0xff];
 }
 
-static struct imap_cmd *
-v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,
-                 const char *fmt, va_list ap )
+static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
+                                        struct imap_cmd_cb *cb,
+                                        const char *fmt, va_list ap)
 {
-       imap_t *imap = ctx->imap;
+       struct imap *imap = ctx->imap;
        struct imap_cmd *cmd;
        int n, bufl;
        char buf[1024];
 
-       cmd = xmalloc( sizeof(struct imap_cmd) );
-       nfvasprintf( &cmd->cmd, fmt, ap );
+       cmd = xmalloc(sizeof(struct imap_cmd));
+       nfvasprintf(&cmd->cmd, fmt, ap);
        cmd->tag = ++imap->nexttag;
 
        if (cb)
                cmd->cb = *cb;
        else
-               memset( &cmd->cb, 0, sizeof(cmd->cb) );
+               memset(&cmd->cb, 0, sizeof(cmd->cb));
 
        while (imap->literal_pending)
-               get_cmd_result( ctx, NULL );
+               get_cmd_result(ctx, NULL);
 
-       bufl = nfsnprintf( buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
+       bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
                           "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-                          cmd->tag, cmd->cmd, cmd->cb.dlen );
+                          cmd->tag, cmd->cmd, cmd->cb.dlen);
        if (Verbose) {
                if (imap->num_in_progress)
-                       printf( "(%d in progress) ", imap->num_in_progress );
-               if (memcmp( cmd->cmd, "LOGIN", 5 ))
-                       printf( ">>> %s", buf );
+                       printf("(%d in progress) ", imap->num_in_progress);
+               if (memcmp(cmd->cmd, "LOGIN", 5))
+                       printf(">>> %s", buf);
                else
-                       printf( ">>> %d LOGIN <user> <pass>\n", cmd->tag );
+                       printf(">>> %d LOGIN <user> <pass>\n", cmd->tag);
        }
-       if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {
-               free( cmd->cmd );
-               free( cmd );
+       if (socket_write(&imap->buf.sock, buf, bufl) != bufl) {
+               free(cmd->cmd);
+               free(cmd);
                if (cb)
-                       free( cb->data );
+                       free(cb->data);
                return NULL;
        }
        if (cmd->cb.data) {
                if (CAP(LITERALPLUS)) {
-                       n = socket_write( &imap->buf.sock, cmd->cb.data, cmd->cb.dlen );
-                       free( cmd->cb.data );
+                       n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen);
+                       free(cmd->cb.data);
                        if (n != cmd->cb.dlen ||
-                           (n = socket_write( &imap->buf.sock, "\r\n", 2 )) != 2)
-                       {
-                               free( cmd->cmd );
-                               free( cmd );
+                           (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) {
+                               free(cmd->cmd);
+                               free(cmd);
                                return NULL;
                        }
                        cmd->cb.data = NULL;
@@ -499,109 +594,106 @@ v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,
        return cmd;
 }
 
-static struct imap_cmd *
-issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
+                                      struct imap_cmd_cb *cb,
+                                      const char *fmt, ...)
 {
        struct imap_cmd *ret;
        va_list ap;
 
-       va_start( ap, fmt );
-       ret = v_issue_imap_cmd( ctx, cb, fmt, ap );
-       va_end( ap );
+       va_start(ap, fmt);
+       ret = v_issue_imap_cmd(ctx, cb, fmt, ap);
+       va_end(ap);
        return ret;
 }
 
-static int
-imap_exec( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
+                    const char *fmt, ...)
 {
        va_list ap;
        struct imap_cmd *cmdp;
 
-       va_start( ap, fmt );
-       cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
-       va_end( ap );
+       va_start(ap, fmt);
+       cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
+       va_end(ap);
        if (!cmdp)
                return RESP_BAD;
 
-       return get_cmd_result( ctx, cmdp );
+       return get_cmd_result(ctx, cmdp);
 }
 
-static int
-imap_exec_m( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
+                      const char *fmt, ...)
 {
        va_list ap;
        struct imap_cmd *cmdp;
 
-       va_start( ap, fmt );
-       cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
-       va_end( ap );
+       va_start(ap, fmt);
+       cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
+       va_end(ap);
        if (!cmdp)
                return DRV_STORE_BAD;
 
-       switch (get_cmd_result( ctx, cmdp )) {
+       switch (get_cmd_result(ctx, cmdp)) {
        case RESP_BAD: return DRV_STORE_BAD;
        case RESP_NO: return DRV_MSG_BAD;
        default: return DRV_OK;
        }
 }
 
-static int
-is_atom( list_t *list )
+static int is_atom(struct imap_list *list)
 {
        return list && list->val && list->val != NIL && list->val != LIST;
 }
 
-static int
-is_list( list_t *list )
+static int is_list(struct imap_list *list)
 {
        return list && list->val == LIST;
 }
 
-static void
-free_list( list_t *list )
+static void free_list(struct imap_list *list)
 {
-       list_t *tmp;
+       struct imap_list *tmp;
 
        for (; list; list = tmp) {
                tmp = list->next;
-               if (is_list( list ))
-                       free_list( list->child );
-               else if (is_atom( list ))
-                       free( list->val );
-               free( list );
+               if (is_list(list))
+                       free_list(list->child);
+               else if (is_atom(list))
+                       free(list->val);
+               free(list);
        }
 }
 
-static int
-parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
+static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **curp, int level)
 {
-       list_t *cur;
+       struct imap_list *cur;
        char *s = *sp, *p;
        int n, bytes;
 
        for (;;) {
-               while (isspace( (unsigned char)*s ))
+               while (isspace((unsigned char)*s))
                        s++;
                if (level && *s == ')') {
                        s++;
                        break;
                }
-               *curp = cur = xmalloc( sizeof(*cur) );
+               *curp = cur = xmalloc(sizeof(*cur));
                curp = &cur->next;
                cur->val = NULL; /* for clean bail */
                if (*s == '(') {
                        /* sublist */
                        s++;
                        cur->val = LIST;
-                       if (parse_imap_list_l( imap, &s, &cur->child, level + 1 ))
+                       if (parse_imap_list_l(imap, &s, &cur->child, level + 1))
                                goto bail;
                } else if (imap && *s == '{') {
                        /* literal */
-                       bytes = cur->len = strtol( s + 1, &s, 10 );
+                       bytes = cur->len = strtol(s + 1, &s, 10);
                        if (*s != '}')
                                goto bail;
 
-                       s = cur->val = xmalloc( cur->len );
+                       s = cur->val = xmalloc(cur->len);
 
                        /* dump whats left over in the input buffer */
                        n = imap->buf.bytes - imap->buf.offset;
@@ -610,7 +702,7 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
                                /* the entire message fit in the buffer */
                                n = bytes;
 
-                       memcpy( s, imap->buf.buf + imap->buf.offset, n );
+                       memcpy(s, imap->buf.buf + imap->buf.offset, n);
                        s += n;
                        bytes -= n;
 
@@ -619,13 +711,13 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
 
                        /* now read the rest of the message */
                        while (bytes > 0) {
-                               if ((n = socket_read (&imap->buf.sock, s, bytes)) <= 0)
+                               if ((n = socket_read(&imap->buf.sock, s, bytes)) <= 0)
                                        goto bail;
                                s += n;
                                bytes -= n;
                        }
 
-                       if (buffer_gets( &imap->buf, &s ))
+                       if (buffer_gets(&imap->buf, &s))
                                goto bail;
                } else if (*s == '"') {
                        /* quoted string */
@@ -640,15 +732,14 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
                } else {
                        /* atom */
                        p = s;
-                       for (; *s && !isspace( (unsigned char)*s ); s++)
+                       for (; *s && !isspace((unsigned char)*s); s++)
                                if (level && *s == ')')
                                        break;
                        cur->len = s - p;
-                       if (cur->len == 3 && !memcmp ("NIL", p, 3)) {
+                       if (cur->len == 3 && !memcmp("NIL", p, 3))
                                cur->val = NIL;
-                       } else {
+                       else
                                cur->val = xmemdupz(p, cur->len);
-                       }
                }
 
                if (!level)
@@ -660,127 +751,122 @@ parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
        *curp = NULL;
        return 0;
 
-  bail:
+bail:
        *curp = NULL;
        return -1;
 }
 
-static list_t *
-parse_imap_list( imap_t *imap, char **sp )
+static struct imap_list *parse_imap_list(struct imap *imap, char **sp)
 {
-       list_t *head;
+       struct imap_list *head;
 
-       if (!parse_imap_list_l( imap, sp, &head, 0 ))
+       if (!parse_imap_list_l(imap, sp, &head, 0))
                return head;
-       free_list( head );
+       free_list(head);
        return NULL;
 }
 
-static list_t *
-parse_list( char **sp )
+static struct imap_list *parse_list(char **sp)
 {
-       return parse_imap_list( NULL, sp );
+       return parse_imap_list(NULL, sp);
 }
 
-static void
-parse_capability( imap_t *imap, char *cmd )
+static void parse_capability(struct imap *imap, char *cmd)
 {
        char *arg;
        unsigned i;
 
        imap->caps = 0x80000000;
-       while ((arg = next_arg( &cmd )))
+       while ((arg = next_arg(&cmd)))
                for (i = 0; i < ARRAY_SIZE(cap_list); i++)
-                       if (!strcmp( cap_list[i], arg ))
+                       if (!strcmp(cap_list[i], arg))
                                imap->caps |= 1 << i;
        imap->rcaps = imap->caps;
 }
 
-static int
-parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s )
+static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
+                              char *s)
 {
-       imap_t *imap = ctx->imap;
+       struct imap *imap = ctx->imap;
        char *arg, *p;
 
        if (*s != '[')
                return RESP_OK;         /* no response code */
        s++;
-       if (!(p = strchr( s, ']' ))) {
-               fprintf( stderr, "IMAP error: malformed response code\n" );
+       if (!(p = strchr(s, ']'))) {
+               fprintf(stderr, "IMAP error: malformed response code\n");
                return RESP_BAD;
        }
        *p++ = 0;
-       arg = next_arg( &s );
-       if (!strcmp( "UIDVALIDITY", arg )) {
-               if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg ))) {
-                       fprintf( stderr, "IMAP error: malformed UIDVALIDITY status\n" );
+       arg = next_arg(&s);
+       if (!strcmp("UIDVALIDITY", arg)) {
+               if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg))) {
+                       fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n");
                        return RESP_BAD;
                }
-       } else if (!strcmp( "UIDNEXT", arg )) {
-               if (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) {
-                       fprintf( stderr, "IMAP error: malformed NEXTUID status\n" );
+       } else if (!strcmp("UIDNEXT", arg)) {
+               if (!(arg = next_arg(&s)) || !(imap->uidnext = atoi(arg))) {
+                       fprintf(stderr, "IMAP error: malformed NEXTUID status\n");
                        return RESP_BAD;
                }
-       } else if (!strcmp( "CAPABILITY", arg )) {
-               parse_capability( imap, s );
-       } else if (!strcmp( "ALERT", arg )) {
+       } else if (!strcmp("CAPABILITY", arg)) {
+               parse_capability(imap, s);
+       } else if (!strcmp("ALERT", arg)) {
                /* RFC2060 says that these messages MUST be displayed
                 * to the user
                 */
-               for (; isspace( (unsigned char)*p ); p++);
-               fprintf( stderr, "*** IMAP ALERT *** %s\n", p );
-       } else if (cb && cb->ctx && !strcmp( "APPENDUID", arg )) {
-               if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg )) ||
-                   !(arg = next_arg( &s )) || !(*(int *)cb->ctx = atoi( arg )))
-               {
-                       fprintf( stderr, "IMAP error: malformed APPENDUID status\n" );
+               for (; isspace((unsigned char)*p); p++);
+               fprintf(stderr, "*** IMAP ALERT *** %s\n", p);
+       } else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) {
+               if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg)) ||
+                   !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) {
+                       fprintf(stderr, "IMAP error: malformed APPENDUID status\n");
                        return RESP_BAD;
                }
        }
        return RESP_OK;
 }
 
-static int
-get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
+static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
 {
-       imap_t *imap = ctx->imap;
+       struct imap *imap = ctx->imap;
        struct imap_cmd *cmdp, **pcmdp, *ncmdp;
        char *cmd, *arg, *arg1, *p;
        int n, resp, resp2, tag;
 
        for (;;) {
-               if (buffer_gets( &imap->buf, &cmd ))
+               if (buffer_gets(&imap->buf, &cmd))
                        return RESP_BAD;
 
-               arg = next_arg( &cmd );
+               arg = next_arg(&cmd);
                if (*arg == '*') {
-                       arg = next_arg( &cmd );
+                       arg = next_arg(&cmd);
                        if (!arg) {
-                               fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
+                               fprintf(stderr, "IMAP error: unable to parse untagged response\n");
                                return RESP_BAD;
                        }
 
-                       if (!strcmp( "NAMESPACE", arg )) {
-                               imap->ns_personal = parse_list( &cmd );
-                               imap->ns_other = parse_list( &cmd );
-                               imap->ns_shared = parse_list( &cmd );
-                       } else if (!strcmp( "OK", arg ) || !strcmp( "BAD", arg ) ||
-                                  !strcmp( "NO", arg ) || !strcmp( "BYE", arg )) {
-                               if ((resp = parse_response_code( ctx, NULL, cmd )) != RESP_OK)
+                       if (!strcmp("NAMESPACE", arg)) {
+                               imap->ns_personal = parse_list(&cmd);
+                               imap->ns_other = parse_list(&cmd);
+                               imap->ns_shared = parse_list(&cmd);
+                       } else if (!strcmp("OK", arg) || !strcmp("BAD", arg) ||
+                                  !strcmp("NO", arg) || !strcmp("BYE", arg)) {
+                               if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK)
                                        return resp;
-                       } else if (!strcmp( "CAPABILITY", arg ))
-                               parse_capability( imap, cmd );
-                       else if ((arg1 = next_arg( &cmd ))) {
-                               if (!strcmp( "EXISTS", arg1 ))
-                                       ctx->gen.count = atoi( arg );
-                               else if (!strcmp( "RECENT", arg1 ))
-                                       ctx->gen.recent = atoi( arg );
+                       } else if (!strcmp("CAPABILITY", arg))
+                               parse_capability(imap, cmd);
+                       else if ((arg1 = next_arg(&cmd))) {
+                               if (!strcmp("EXISTS", arg1))
+                                       ctx->gen.count = atoi(arg);
+                               else if (!strcmp("RECENT", arg1))
+                                       ctx->gen.recent = atoi(arg);
                        } else {
-                               fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
+                               fprintf(stderr, "IMAP error: unable to parse untagged response\n");
                                return RESP_BAD;
                        }
                } else if (!imap->in_progress) {
-                       fprintf( stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
+                       fprintf(stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "");
                        return RESP_BAD;
                } else if (*arg == '+') {
                        /* This can happen only with the last command underway, as
@@ -788,57 +874,57 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
                        cmdp = (struct imap_cmd *)((char *)imap->in_progress_append -
                               offsetof(struct imap_cmd, next));
                        if (cmdp->cb.data) {
-                               n = socket_write( &imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen );
-                               free( cmdp->cb.data );
+                               n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen);
+                               free(cmdp->cb.data);
                                cmdp->cb.data = NULL;
                                if (n != (int)cmdp->cb.dlen)
                                        return RESP_BAD;
                        } else if (cmdp->cb.cont) {
-                               if (cmdp->cb.cont( ctx, cmdp, cmd ))
+                               if (cmdp->cb.cont(ctx, cmdp, cmd))
                                        return RESP_BAD;
                        } else {
-                               fprintf( stderr, "IMAP error: unexpected command continuation request\n" );
+                               fprintf(stderr, "IMAP error: unexpected command continuation request\n");
                                return RESP_BAD;
                        }
-                       if (socket_write( &imap->buf.sock, "\r\n", 2 ) != 2)
+                       if (socket_write(&imap->buf.sock, "\r\n", 2) != 2)
                                return RESP_BAD;
                        if (!cmdp->cb.cont)
                                imap->literal_pending = 0;
                        if (!tcmd)
                                return DRV_OK;
                } else {
-                       tag = atoi( arg );
+                       tag = atoi(arg);
                        for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
                                if (cmdp->tag == tag)
                                        goto gottag;
-                       fprintf( stderr, "IMAP error: unexpected tag %s\n", arg );
+                       fprintf(stderr, "IMAP error: unexpected tag %s\n", arg);
                        return RESP_BAD;
-                 gottag:
+               gottag:
                        if (!(*pcmdp = cmdp->next))
                                imap->in_progress_append = pcmdp;
                        imap->num_in_progress--;
                        if (cmdp->cb.cont || cmdp->cb.data)
                                imap->literal_pending = 0;
-                       arg = next_arg( &cmd );
-                       if (!strcmp( "OK", arg ))
+                       arg = next_arg(&cmd);
+                       if (!strcmp("OK", arg))
                                resp = DRV_OK;
                        else {
-                               if (!strcmp( "NO", arg )) {
-                                       if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, APPEND or UID COPY */
-                                               p = strchr( cmdp->cmd, '"' );
-                                               if (!issue_imap_cmd( ctx, NULL, "CREATE \"%.*s\"", strchr( p + 1, '"' ) - p + 1, p )) {
+                               if (!strcmp("NO", arg)) {
+                                       if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */
+                                               p = strchr(cmdp->cmd, '"');
+                                               if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) {
                                                        resp = RESP_BAD;
                                                        goto normal;
                                                }
                                                /* not waiting here violates the spec, but a server that does not
                                                   grok this nonetheless violates it too. */
                                                cmdp->cb.create = 0;
-                                               if (!(ncmdp = issue_imap_cmd( ctx, &cmdp->cb, "%s", cmdp->cmd ))) {
+                                               if (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, "%s", cmdp->cmd))) {
                                                        resp = RESP_BAD;
                                                        goto normal;
                                                }
-                                               free( cmdp->cmd );
-                                               free( cmdp );
+                                               free(cmdp->cmd);
+                                               free(cmdp);
                                                if (!tcmd)
                                                        return 0;       /* ignored */
                                                if (cmdp == tcmd)
@@ -846,21 +932,21 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
                                                continue;
                                        }
                                        resp = RESP_NO;
-                               } else /*if (!strcmp( "BAD", arg ))*/
+                               } else /*if (!strcmp("BAD", arg))*/
                                        resp = RESP_BAD;
-                               fprintf( stderr, "IMAP command '%s' returned response (%s) - %s\n",
-                                        memcmp (cmdp->cmd, "LOGIN", 5) ?
+                               fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n",
+                                        memcmp(cmdp->cmd, "LOGIN", 5) ?
                                                        cmdp->cmd : "LOGIN <user> <pass>",
                                                        arg, cmd ? cmd : "");
                        }
-                       if ((resp2 = parse_response_code( ctx, &cmdp->cb, cmd )) > resp)
+                       if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
                                resp = resp2;
-                 normal:
+               normal:
                        if (cmdp->cb.done)
-                               cmdp->cb.done( ctx, cmdp, resp );
-                       free( cmdp->cb.data );
-                       free( cmdp->cmd );
-                       free( cmdp );
+                               cmdp->cb.done(ctx, cmdp, resp);
+                       free(cmdp->cb.data);
+                       free(cmdp->cmd);
+                       free(cmdp);
                        if (!tcmd || tcmd == cmdp)
                                return resp;
                }
@@ -868,170 +954,184 @@ get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
        /* not reached */
 }
 
-static void
-imap_close_server( imap_store_t *ictx )
+static void imap_close_server(struct imap_store *ictx)
 {
-       imap_t *imap = ictx->imap;
+       struct imap *imap = ictx->imap;
 
        if (imap->buf.sock.fd != -1) {
-               imap_exec( ictx, NULL, "LOGOUT" );
-               close( imap->buf.sock.fd );
+               imap_exec(ictx, NULL, "LOGOUT");
+               socket_shutdown(&imap->buf.sock);
        }
-       free_list( imap->ns_personal );
-       free_list( imap->ns_other );
-       free_list( imap->ns_shared );
-       free( imap );
+       free_list(imap->ns_personal);
+       free_list(imap->ns_other);
+       free_list(imap->ns_shared);
+       free(imap);
 }
 
-static void
-imap_close_store( store_t *ctx )
+static void imap_close_store(struct store *ctx)
 {
-       imap_close_server( (imap_store_t *)ctx );
-       free_generic_messages( ctx->msgs );
-       free( ctx );
+       imap_close_server((struct imap_store *)ctx);
+       free_generic_messages(ctx->msgs);
+       free(ctx);
 }
 
-static store_t *
-imap_open_store( imap_server_conf_t *srvc )
+static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
-       imap_store_t *ctx;
-       imap_t *imap;
+       struct imap_store *ctx;
+       struct imap *imap;
        char *arg, *rsp;
        struct hostent *he;
        struct sockaddr_in addr;
        int s, a[2], preauth;
        pid_t pid;
 
-       ctx = xcalloc( sizeof(*ctx), 1 );
+       ctx = xcalloc(sizeof(*ctx), 1);
 
-       ctx->imap = imap = xcalloc( sizeof(*imap), 1 );
+       ctx->imap = imap = xcalloc(sizeof(*imap), 1);
        imap->buf.sock.fd = -1;
        imap->in_progress_append = &imap->in_progress;
 
        /* open connection to IMAP server */
 
        if (srvc->tunnel) {
-               imap_info( "Starting tunnel '%s'... ", srvc->tunnel );
+               imap_info("Starting tunnel '%s'... ", srvc->tunnel);
 
-               if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
-                       perror( "socketpair" );
-                       exit( 1 );
+               if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) {
+                       perror("socketpair");
+                       exit(1);
                }
 
                pid = fork();
                if (pid < 0)
-                       _exit( 127 );
+                       _exit(127);
                if (!pid) {
-                       if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1)
-                               _exit( 127 );
-                       close( a[0] );
-                       close( a[1] );
-                       execl( "/bin/sh", "sh", "-c", srvc->tunnel, NULL );
-                       _exit( 127 );
+                       if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1)
+                               _exit(127);
+                       close(a[0]);
+                       close(a[1]);
+                       execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL);
+                       _exit(127);
                }
 
-               close (a[0]);
+               close(a[0]);
 
                imap->buf.sock.fd = a[1];
 
-               imap_info( "ok\n" );
+               imap_info("ok\n");
        } else {
-               memset( &addr, 0, sizeof(addr) );
-               addr.sin_port = htons( srvc->port );
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_port = htons(srvc->port);
                addr.sin_family = AF_INET;
 
-               imap_info( "Resolving %s... ", srvc->host );
-               he = gethostbyname( srvc->host );
+               imap_info("Resolving %s... ", srvc->host);
+               he = gethostbyname(srvc->host);
                if (!he) {
-                       perror( "gethostbyname" );
+                       perror("gethostbyname");
                        goto bail;
                }
-               imap_info( "ok\n" );
+               imap_info("ok\n");
 
                addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
 
-               s = socket( PF_INET, SOCK_STREAM, 0 );
+               s = socket(PF_INET, SOCK_STREAM, 0);
 
-               imap_info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
-               if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {
-                       close( s );
-                       perror( "connect" );
+               imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+               if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
+                       close(s);
+                       perror("connect");
                        goto bail;
                }
-               imap_info( "ok\n" );
 
                imap->buf.sock.fd = s;
 
+               if (srvc->use_ssl &&
+                   ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+                       close(s);
+                       goto bail;
+               }
+               imap_info("ok\n");
        }
 
        /* read the greeting string */
-       if (buffer_gets( &imap->buf, &rsp )) {
-               fprintf( stderr, "IMAP error: no greeting response\n" );
+       if (buffer_gets(&imap->buf, &rsp)) {
+               fprintf(stderr, "IMAP error: no greeting response\n");
                goto bail;
        }
-       arg = next_arg( &rsp );
-       if (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) {
-               fprintf( stderr, "IMAP error: invalid greeting response\n" );
+       arg = next_arg(&rsp);
+       if (!arg || *arg != '*' || (arg = next_arg(&rsp)) == NULL) {
+               fprintf(stderr, "IMAP error: invalid greeting response\n");
                goto bail;
        }
        preauth = 0;
-       if (!strcmp( "PREAUTH", arg ))
+       if (!strcmp("PREAUTH", arg))
                preauth = 1;
-       else if (strcmp( "OK", arg ) != 0) {
-               fprintf( stderr, "IMAP error: unknown greeting response\n" );
+       else if (strcmp("OK", arg) != 0) {
+               fprintf(stderr, "IMAP error: unknown greeting response\n");
                goto bail;
        }
-       parse_response_code( ctx, NULL, rsp );
-       if (!imap->caps && imap_exec( ctx, NULL, "CAPABILITY" ) != RESP_OK)
+       parse_response_code(ctx, NULL, rsp);
+       if (!imap->caps && imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
                goto bail;
 
        if (!preauth) {
-
-               imap_info ("Logging in...\n");
+#ifndef NO_OPENSSL
+               if (!srvc->use_ssl && CAP(STARTTLS)) {
+                       if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK)
+                               goto bail;
+                       if (ssl_socket_connect(&imap->buf.sock, 1,
+                                              srvc->ssl_verify))
+                               goto bail;
+                       /* capabilities may have changed, so get the new capabilities */
+                       if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK)
+                               goto bail;
+               }
+#endif
+               imap_info("Logging in...\n");
                if (!srvc->user) {
-                       fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
+                       fprintf(stderr, "Skipping server %s, no user\n", srvc->host);
                        goto bail;
                }
                if (!srvc->pass) {
                        char prompt[80];
-                       sprintf( prompt, "Password (%s@%s): ", srvc->user, srvc->host );
-                       arg = getpass( prompt );
+                       sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host);
+                       arg = getpass(prompt);
                        if (!arg) {
-                               perror( "getpass" );
-                               exit( 1 );
+                               perror("getpass");
+                               exit(1);
                        }
                        if (!*arg) {
-                               fprintf( stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host );
+                               fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host);
                                goto bail;
                        }
                        /*
                         * getpass() returns a pointer to a static buffer.  make a copy
                         * for long term storage.
                         */
-                       srvc->pass = xstrdup( arg );
+                       srvc->pass = xstrdup(arg);
                }
                if (CAP(NOLOGIN)) {
-                       fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
+                       fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
                        goto bail;
                }
-               imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
-               if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
-                       fprintf( stderr, "IMAP error: LOGIN failed\n" );
+               if (!imap->buf.sock.ssl)
+                       imap_warn("*** IMAP Warning *** Password is being "
+                                 "sent in the clear\n");
+               if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+                       fprintf(stderr, "IMAP error: LOGIN failed\n");
                        goto bail;
                }
        } /* !preauth */
 
        ctx->prefix = "";
        ctx->trashnc = 1;
-       return (store_t *)ctx;
+       return (struct store *)ctx;
 
-  bail:
-       imap_close_store( &ctx->gen );
+bail:
+       imap_close_store(&ctx->gen);
        return NULL;
 }
 
-static int
-imap_make_flags( int flags, char *buf )
+static int imap_make_flags(int flags, char *buf)
 {
        const char *s;
        unsigned i, d;
@@ -1050,11 +1150,10 @@ imap_make_flags( int flags, char *buf )
 
 #define TUIDL 8
 
-static int
-imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
+static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
 {
-       imap_store_t *ctx = (imap_store_t *)gctx;
-       imap_t *imap = ctx->imap;
+       struct imap_store *ctx = (struct imap_store *)gctx;
+       struct imap *imap = ctx->imap;
        struct imap_cmd_cb cb;
        char *fmap, *buf;
        const char *prefix, *box;
@@ -1062,14 +1161,14 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
        int start, sbreak = 0, ebreak = 0;
        char flagstr[128], tuid[TUIDL * 2 + 1];
 
-       memset( &cb, 0, sizeof(cb) );
+       memset(&cb, 0, sizeof(cb));
 
        fmap = data->data;
        len = data->len;
        nocr = !data->crlf;
        extra = 0, i = 0;
        if (!CAP(UIDPLUS) && uid) {
-         nloop:
+       nloop:
                start = i;
                while (i < len)
                        if (fmap[i++] == '\n') {
@@ -1078,18 +1177,18 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
                                        sbreak = ebreak = i - 2 + nocr;
                                        goto mktid;
                                }
-                               if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
+                               if (!memcmp(fmap + start, "X-TUID: ", 8)) {
                                        extra -= (ebreak = i) - (sbreak = start) + nocr;
                                        goto mktid;
                                }
                                goto nloop;
                        }
                /* invalid message */
-               free( fmap );
+               free(fmap);
                return DRV_MSG_BAD;
-        mktid:
+       mktid:
                for (j = 0; j < TUIDL; j++)
-                       sprintf( tuid + j * 2, "%02x", arc4_getbyte() );
+                       sprintf(tuid + j * 2, "%02x", arc4_getbyte());
                extra += 8 + TUIDL * 2 + 2;
        }
        if (nocr)
@@ -1098,7 +1197,7 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
                                extra++;
 
        cb.dlen = len + extra;
-       buf = cb.data = xmalloc( cb.dlen );
+       buf = cb.data = xmalloc(cb.dlen);
        i = 0;
        if (!CAP(UIDPLUS) && uid) {
                if (nocr) {
@@ -1109,12 +1208,12 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
                                } else
                                        *buf++ = fmap[i];
                } else {
-                       memcpy( buf, fmap, sbreak );
+                       memcpy(buf, fmap, sbreak);
                        buf += sbreak;
                }
-               memcpy( buf, "X-TUID: ", 8 );
+               memcpy(buf, "X-TUID: ", 8);
                buf += 8;
-               memcpy( buf, tuid, TUIDL * 2 );
+               memcpy(buf, tuid, TUIDL * 2);
                buf += TUIDL * 2;
                *buf++ = '\r';
                *buf++ = '\n';
@@ -1128,13 +1227,13 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
                        } else
                                *buf++ = fmap[i];
        } else
-               memcpy( buf, fmap + i, len - i );
+               memcpy(buf, fmap + i, len - i);
 
-       free( fmap );
+       free(fmap);
 
        d = 0;
        if (data->flags) {
-               d = imap_make_flags( data->flags, flagstr );
+               d = imap_make_flags(data->flags, flagstr);
                flagstr[d++] = ' ';
        }
        flagstr[d] = 0;
@@ -1147,11 +1246,11 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
                        imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
        } else {
                box = gctx->name;
-               prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
+               prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
                cb.create = 0;
        }
        cb.ctx = uid;
-       ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr );
+       ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr);
        imap->caps = imap->rcaps;
        if (ret != DRV_OK)
                return ret;
@@ -1165,8 +1264,7 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
 
 #define CHUNKSIZE 0x1000
 
-static int
-read_message( FILE *f, msg_data_t *msg )
+static int read_message(FILE *f, struct msg_data *msg)
 {
        struct strbuf buf;
 
@@ -1183,8 +1281,7 @@ read_message( FILE *f, msg_data_t *msg )
        return msg->len;
 }
 
-static int
-count_messages( msg_data_t *msg )
+static int count_messages(struct msg_data *msg)
 {
        int count = 0;
        char *p = msg->data;
@@ -1194,7 +1291,7 @@ count_messages( msg_data_t *msg )
                        count++;
                        p += 5;
                }
-               p = strstr( p+5, "\nFrom ");
+               p = strstr(p+5, "\nFrom ");
                if (!p)
                        break;
                p++;
@@ -1202,22 +1299,21 @@ count_messages( msg_data_t *msg )
        return count;
 }
 
-static int
-split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
+static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 {
        char *p, *data;
 
-       memset( msg, 0, sizeof *msg );
+       memset(msg, 0, sizeof *msg);
        if (*ofs >= all_msgs->len)
                return 0;
 
-       data = &all_msgs->data[ *ofs ];
+       data = &all_msgs->data[*ofs];
        msg->len = all_msgs->len - *ofs;
 
        if (msg->len < 5 || prefixcmp(data, "From "))
                return 0;
 
-       p = strchr( data, '\n' );
+       p = strchr(data, '\n');
        if (p) {
                p = &p[1];
                msg->len -= p-data;
@@ -1225,7 +1321,7 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
                data = p;
        }
 
-       p = strstr( data, "\nFrom " );
+       p = strstr(data, "\nFrom ");
        if (p)
                msg->len = &p[1] - data;
 
@@ -1234,24 +1330,24 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
        return 1;
 }
 
-static imap_server_conf_t server =
-{
+static struct imap_server_conf server = {
        NULL,   /* name */
        NULL,   /* tunnel */
        NULL,   /* host */
        0,      /* port */
        NULL,   /* user */
        NULL,   /* pass */
+       0,      /* use_ssl */
+       1,      /* ssl_verify */
 };
 
 static char *imap_folder;
 
-static int
-git_imap_config(const char *key, const char *val, void *cb)
+static int git_imap_config(const char *key, const char *val, void *cb)
 {
        char imap_key[] = "imap.";
 
-       if (strncmp( key, imap_key, sizeof imap_key - 1 ))
+       if (strncmp(key, imap_key, sizeof imap_key - 1))
                return 0;
 
        if (!val)
@@ -1259,90 +1355,96 @@ git_imap_config(const char *key, const char *val, void *cb)
 
        key += sizeof imap_key - 1;
 
-       if (!strcmp( "folder", key )) {
-               imap_folder = xstrdup( val );
-       } else if (!strcmp( "host", key )) {
-               {
-                       if (!prefixcmp(val, "imap:"))
-                               val += 5;
-                       if (!server.port)
-                               server.port = 143;
+       if (!strcmp("folder", key)) {
+               imap_folder = xstrdup(val);
+       } else if (!strcmp("host", key)) {
+               if (!prefixcmp(val, "imap:"))
+                       val += 5;
+               else if (!prefixcmp(val, "imaps:")) {
+                       val += 6;
+                       server.use_ssl = 1;
                }
                if (!prefixcmp(val, "//"))
                        val += 2;
-               server.host = xstrdup( val );
-       }
-       else if (!strcmp( "user", key ))
-               server.user = xstrdup( val );
-       else if (!strcmp( "pass", key ))
-               server.pass = xstrdup( val );
-       else if (!strcmp( "port", key ))
-               server.port = git_config_int( key, val );
-       else if (!strcmp( "tunnel", key ))
-               server.tunnel = xstrdup( val );
+               server.host = xstrdup(val);
+       } else if (!strcmp("user", key))
+               server.user = xstrdup(val);
+       else if (!strcmp("pass", key))
+               server.pass = xstrdup(val);
+       else if (!strcmp("port", key))
+               server.port = git_config_int(key, val);
+       else if (!strcmp("tunnel", key))
+               server.tunnel = xstrdup(val);
+       else if (!strcmp("sslverify", key))
+               server.ssl_verify = git_config_bool(key, val);
        return 0;
 }
 
-int
-main(int argc, char **argv)
+int main(int argc, char **argv)
 {
-       msg_data_t all_msgs, msg;
-       store_t *ctx = NULL;
+       struct msg_data all_msgs, msg;
+       struct store *ctx = NULL;
        int uid = 0;
        int ofs = 0;
        int r;
        int total, n = 0;
+       int nongit_ok;
 
        /* init the random number generator */
        arc4_init();
 
+       setup_git_directory_gently(&nongit_ok);
        git_config(git_imap_config, NULL);
 
+       if (!server.port)
+               server.port = server.use_ssl ? 993 : 143;
+
        if (!imap_folder) {
-               fprintf( stderr, "no imap store specified\n" );
+               fprintf(stderr, "no imap store specified\n");
                return 1;
        }
        if (!server.host) {
                if (!server.tunnel) {
-                       fprintf( stderr, "no imap host specified\n" );
+                       fprintf(stderr, "no imap host specified\n");
                        return 1;
                }
                server.host = "tunnel";
        }
 
        /* read the messages */
-       if (!read_message( stdin, &all_msgs )) {
-               fprintf(stderr,"nothing to send\n");
+       if (!read_message(stdin, &all_msgs)) {
+               fprintf(stderr, "nothing to send\n");
                return 1;
        }
 
-       total = count_messages( &all_msgs );
+       total = count_messages(&all_msgs);
        if (!total) {
-               fprintf(stderr,"no messages to send\n");
+               fprintf(stderr, "no messages to send\n");
                return 1;
        }
 
        /* write it to the imap server */
-       ctx = imap_open_store( &server );
+       ctx = imap_open_store(&server);
        if (!ctx) {
-               fprintf( stderr,"failed to open store\n");
+               fprintf(stderr, "failed to open store\n");
                return 1;
        }
 
-       fprintf( stderr, "sending %d message%s\n", total, (total!=1)?"s":"" );
+       fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
        ctx->name = imap_folder;
        while (1) {
                unsigned percent = n * 100 / total;
-               fprintf( stderr, "%4u%% (%d/%d) done\r", percent, n, total );
-               if (!split_msg( &all_msgs, &msg, &ofs ))
+               fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
+               if (!split_msg(&all_msgs, &msg, &ofs))
+                       break;
+               r = imap_store_msg(ctx, &msg, &uid);
+               if (r != DRV_OK)
                        break;
-               r = imap_store_msg( ctx, &msg, &uid );
-               if (r != DRV_OK) break;
                n++;
        }
-       fprintf( stderr,"\n" );
+       fprintf(stderr, "\n");
 
-       imap_close_store( ctx );
+       imap_close_store(ctx);
 
        return 0;
 }
index bd8b9e45ab46b8664c8b7016b33bee22f86c9e0d..30cd5bb22800f5e897c1170e97721a4dcdb9c9d2 100644 (file)
@@ -432,7 +432,7 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
        struct commit_list *parents;
        unsigned const char *sha1 = commit->object.sha1;
 
-       if (!opt->diff)
+       if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
                return 0;
 
        /* Root commit? */
diff --git a/pager.c b/pager.c
index 6b5c9e44b4ded338ddb344ae454d83a685b7569a..aa0966c9c55566382bf32c946c0a1846f004125a 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "run-command.h"
 
 /*
  * This is split up from the rest of git so that we can do
@@ -8,7 +9,7 @@
 static int spawned_pager;
 
 #ifndef __MINGW32__
-static void run_pager(const char *pager)
+static void pager_preexec(void)
 {
        /*
         * Work around bug in "less" by not starting it until we
@@ -20,17 +21,13 @@ static void run_pager(const char *pager)
        FD_SET(0, &in);
        select(1, &in, NULL, &in, NULL);
 
-       execlp(pager, pager, NULL);
-       execl("/bin/sh", "sh", "-c", pager, NULL);
+       setenv("LESS", "FRSX", 0);
 }
-#else
-#include "run-command.h"
+#endif
 
 static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
-static struct child_process pager_process = {
-       .argv = pager_argv,
-       .in = -1
-};
+static struct child_process pager_process;
+
 static void wait_for_pager(void)
 {
        fflush(stdout);
@@ -40,14 +37,9 @@ static void wait_for_pager(void)
        close(2);
        finish_command(&pager_process);
 }
-#endif
 
 void setup_pager(void)
 {
-#ifndef __MINGW32__
-       pid_t pid;
-       int fd[2];
-#endif
        const char *pager = getenv("GIT_PAGER");
 
        if (!isatty(1))
@@ -66,37 +58,13 @@ void setup_pager(void)
 
        spawned_pager = 1; /* means we are emitting to terminal */
 
-#ifndef __MINGW32__
-       if (pipe(fd) < 0)
-               return;
-       pid = fork();
-       if (pid < 0) {
-               close(fd[0]);
-               close(fd[1]);
-               return;
-       }
-
-       /* return in the child */
-       if (!pid) {
-               dup2(fd[1], 1);
-               dup2(fd[1], 2);
-               close(fd[0]);
-               close(fd[1]);
-               return;
-       }
-
-       /* The original process turns into the PAGER */
-       dup2(fd[0], 0);
-       close(fd[0]);
-       close(fd[1]);
-
-       setenv("LESS", "FRSX", 0);
-       run_pager(pager);
-       die("unable to execute pager '%s'", pager);
-       exit(255);
-#else
        /* spawn the pager */
        pager_argv[2] = pager;
+       pager_process.argv = pager_argv;
+       pager_process.in = -1;
+#ifndef __MINGW32__
+       pager_process.preexec_cb = pager_preexec;
+#endif
        if (start_command(&pager_process))
                return;
 
@@ -107,7 +75,6 @@ void setup_pager(void)
 
        /* this makes sure that the parent terminates after the pager */
        atexit(wait_for_pager);
-#endif
 }
 
 int pager_in_use(void)
index 35fec468b1951cc17606fca8edc47f809471f652..5150c1e14b7384bd443bffb17da2887394237c61 100644 (file)
@@ -8,6 +8,11 @@
 #include "cache-tree.h"
 #include "refs.h"
 #include "dir.h"
+#include "tree.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
 
 /* Index extensions.
  *
@@ -1118,6 +1123,10 @@ static void convert_from_disk(struct ondisk_cache_entry *ondisk, struct cache_en
        ce->ce_size  = ntohl(ondisk->size);
        /* On-disk flags are just 16 bits */
        ce->ce_flags = ntohs(ondisk->flags);
+
+       /* For future extension: we do not understand this entry yet */
+       if (ce->ce_flags & CE_EXTENDED)
+               die("Unknown index entry format");
        hashcpy(ce->sha1, ondisk->sha1);
 
        len = ce->ce_flags & CE_NAMEMASK;
@@ -1479,3 +1488,59 @@ int read_index_unmerged(struct index_state *istate)
        istate->cache_nr = dst - istate->cache;
        return !!last;
 }
+
+struct update_callback_data
+{
+       int flags;
+       int add_errors;
+};
+
+static void update_callback(struct diff_queue_struct *q,
+                           struct diff_options *opt, void *cbdata)
+{
+       int i;
+       struct update_callback_data *data = cbdata;
+
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               const char *path = p->one->path;
+               switch (p->status) {
+               default:
+                       die("unexpected diff status %c", p->status);
+               case DIFF_STATUS_UNMERGED:
+               case DIFF_STATUS_MODIFIED:
+               case DIFF_STATUS_TYPE_CHANGED:
+                       if (add_file_to_index(&the_index, path, data->flags)) {
+                               if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+                                       die("updating files failed");
+                               data->add_errors++;
+                       }
+                       break;
+               case DIFF_STATUS_DELETED:
+                       if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
+                               break;
+                       if (!(data->flags & ADD_CACHE_PRETEND))
+                               remove_file_from_index(&the_index, path);
+                       if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
+                               printf("remove '%s'\n", path);
+                       break;
+               }
+       }
+}
+
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
+{
+       struct update_callback_data data;
+       struct rev_info rev;
+       init_revisions(&rev, prefix);
+       setup_revisions(0, NULL, &rev, NULL);
+       rev.prune_data = pathspec;
+       rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+       rev.diffopt.format_callback = update_callback;
+       data.flags = flags;
+       data.add_errors = 0;
+       rev.diffopt.format_callback_data = &data;
+       run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+       return !!data.add_errors;
+}
+
index 105668f8a3cf13c2a759e9da4d0acc50ef781de9..3ef09a44a14622bb175d2a18bc46c946cde9a8a1 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -449,6 +449,26 @@ static int verify_refname(char *name, int is_glob)
        return result;
 }
 
+/*
+ * This function frees a refspec array.
+ * Warning: code paths should be checked to ensure that the src
+ *          and dst pointers are always freeable pointers as well
+ *          as the refspec pointer itself.
+ */
+void free_refspecs(struct refspec *refspec, int nr_refspec)
+{
+       int i;
+
+       if (!refspec)
+               return;
+
+       for (i = 0; i < nr_refspec; i++) {
+               free(refspec[i].src);
+               free(refspec[i].dst);
+       }
+       free(refspec);
+}
+
 static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
        int i;
@@ -567,7 +587,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
 
  invalid:
        if (verify) {
-               free(rs);
+               /*
+                * nr_refspec must be greater than zero and i must be valid
+                * since it is only possible to reach this point from within
+                * the for loop above.
+                */
+               free_refspecs(rs, i+1);
                return NULL;
        }
        die("Invalid refspec '%s'", refspec[i]);
@@ -579,7 +604,7 @@ int valid_fetch_refspec(const char *fetch_refspec_str)
        struct refspec *refspec;
 
        refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
-       free(refspec);
+       free_refspecs(refspec, 1);
        return !!refspec;
 }
 
index 091b1d041f8a4d255f59bfc001e098e692dbc15c..2601f6e76d52c6a201e3952fb8b2c988eed33700 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -78,6 +78,7 @@ void ref_remove_duplicates(struct ref *ref_map);
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);
+void free_refspecs(struct refspec *refspec, int nr_refspec);
 
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
               int nr_refspec, const char **refspec, int all);
index bbb9c777e583c345d25a6651f9ddf7725c10f6af..caab374577e02e9a33cd8095b8da9234acb065f8 100644 (file)
@@ -111,6 +111,8 @@ int start_command(struct child_process *cmd)
                                        unsetenv(*cmd->env);
                        }
                }
+               if (cmd->preexec_cb)
+                       cmd->preexec_cb();
                if (cmd->git_cmd) {
                        execv_git_cmd(cmd->argv);
                } else {
index 5203a9ebb10b14bd06862abafed0ab73d7514a3d..4f2b7d7d403ee6d87fea5ba2dc32da7596965e5e 100644 (file)
@@ -42,6 +42,7 @@ struct child_process {
        unsigned no_stderr:1;
        unsigned git_cmd:1; /* if this is to be git sub-command */
        unsigned stdout_to_stderr:1;
+       void (*preexec_cb)(void);
 };
 
 int start_command(struct child_process *);
index 477d3fb4b06bcbcdd3f75a9e48d5945f91e1f3e6..9ee1ed16ad2df8847bd5008a5d61c807c145357a 100644 (file)
@@ -2361,51 +2361,22 @@ int has_sha1_file(const unsigned char *sha1)
        return has_loose_object(sha1);
 }
 
-int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
+static int index_mem(unsigned char *sha1, void *buf, size_t size,
+                    int write_object, enum object_type type, const char *path)
 {
-       struct strbuf buf;
-       int ret;
-
-       strbuf_init(&buf, 0);
-       if (strbuf_read(&buf, fd, 4096) < 0) {
-               strbuf_release(&buf);
-               return -1;
-       }
-
-       if (!type)
-               type = blob_type;
-       if (write_object)
-               ret = write_sha1_file(buf.buf, buf.len, type, sha1);
-       else
-               ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
-       strbuf_release(&buf);
-
-       return ret;
-}
-
-int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
-            enum object_type type, const char *path)
-{
-       size_t size = xsize_t(st->st_size);
-       void *buf = NULL;
        int ret, re_allocated = 0;
 
-       if (size)
-               buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-       close(fd);
-
        if (!type)
                type = OBJ_BLOB;
 
        /*
         * Convert blobs to git internal format
         */
-       if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
+       if ((type == OBJ_BLOB) && path) {
                struct strbuf nbuf;
                strbuf_init(&nbuf, 0);
                if (convert_to_git(path, buf, size, &nbuf,
                                   write_object ? safe_crlf : 0)) {
-                       munmap(buf, size);
                        buf = strbuf_detach(&nbuf, &size);
                        re_allocated = 1;
                }
@@ -2415,12 +2386,33 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
                ret = write_sha1_file(buf, size, typename(type), sha1);
        else
                ret = hash_sha1_file(buf, size, typename(type), sha1);
-       if (re_allocated) {
+       if (re_allocated)
                free(buf);
-               return ret;
-       }
-       if (size)
+       return ret;
+}
+
+int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
+            enum object_type type, const char *path)
+{
+       int ret;
+       size_t size = xsize_t(st->st_size);
+
+       if (!S_ISREG(st->st_mode)) {
+               struct strbuf sbuf;
+               strbuf_init(&sbuf, 0);
+               if (strbuf_read(&sbuf, fd, 4096) >= 0)
+                       ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
+                                       type, path);
+               else
+                       ret = -1;
+               strbuf_release(&sbuf);
+       } else if (size) {
+               void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+               ret = index_mem(sha1, buf, size, write_object, type, path);
                munmap(buf, size);
+       } else
+               ret = index_mem(sha1, NULL, size, write_object, type, path);
+       close(fd);
        return ret;
 }
 
index 4fb77f8863ec075de38b84171d3ef039a00cee4c..41b680915d7348bf622397da8b1465d3769a361a 100644 (file)
@@ -349,7 +349,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                        else
                                nth = -1;
                }
-               if (0 <= nth)
+               if (100000000 <= nth) {
+                       at_time = nth;
+                       nth = -1;
+               } else if (0 <= nth)
                        at_time = 0;
                else {
                        char *tmp = xstrndup(str + at + 2, reflog_len);
index b27e280083867ac03c4abc188f0f37291eb123a0..7dcbb232cd876cb7b976443cc586f60a94ab92bf 100644 (file)
@@ -1,2 +1,2 @@
-/trash directory
+/trash directory*
 /test-results
index 0d65cedaa6566a6dd654753cb574c9ee64b1c90b..ed49c20b16b520da1b460960dbadd5d31c4927ba 100644 (file)
@@ -14,7 +14,8 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
 TSVN = $(wildcard t91[0-9][0-9]-*.sh)
 
-all: pre-clean $(T) aggregate-results clean
+all: pre-clean
+       $(MAKE) aggregate-results-and-cleanup
 
 $(T):
        @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
@@ -25,6 +26,10 @@ pre-clean:
 clean:
        $(RM) -r 'trash directory' test-results
 
+aggregate-results-and-cleanup: $(T)
+       $(MAKE) aggregate-results
+       $(MAKE) clean
+
 aggregate-results:
        '$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-*
 
@@ -34,4 +39,3 @@ full-svn-test:
        $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8
 
 .PHONY: pre-clean $(T) aggregate-results clean
-.NOTPARALLEL:
index dc473dfb53d5ffafee72738a55caf21732fa4fb1..6ac312b9059394b44cd6e106f9da6394674ee54a 100644 (file)
@@ -14,7 +14,7 @@ fi
 LIB_HTTPD_PATH=${LIB_HTTPD_PATH-'/usr/sbin/apache2'}
 LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
 
-TEST_PATH="$PWD"/../lib-httpd
+TEST_PATH="$TEST_DIRECTORY"/lib-httpd
 HTTPD_ROOT_PATH="$PWD"/httpd
 HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
 
index 7d1ce2d0563b3734d754c171d21580075264bbb2..f1e1d48869a25c6ab1d0b570eb4c0e28afc5fa05 100755 (executable)
@@ -6,13 +6,13 @@ test_description='ignore CR in CRLF sequence while computing similiarity'
 
 test_expect_success setup '
 
-       cat ../t0022-crlf-rename.sh >sample &&
+       cat "$TEST_DIRECTORY"/t0022-crlf-rename.sh >sample &&
        git add sample &&
 
        test_tick &&
        git commit -m Initial &&
 
-       sed -e "s/\$/\r/" ../t0022-crlf-rename.sh >elpmas &&
+       sed -e "s/\$/\r/" "$TEST_DIRECTORY"/t0022-crlf-rename.sh >elpmas &&
        git add elpmas &&
        rm -f sample &&
 
diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh
new file mode 100755 (executable)
index 0000000..b29c37a
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='update-index and add refuse to add beyond symlinks'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       >a &&
+       mkdir b &&
+       ln -s b c &&
+       >c/d &&
+       git update-index --add a b/d
+'
+
+test_expect_success 'update-index --add beyond symlinks' '
+       test_must_fail git update-index --add c/d &&
+       ! ( git ls-files | grep c/d )
+'
+
+test_expect_success 'add beyond symlinks' '
+       test_must_fail git add c/d &&
+       ! ( git ls-files | grep c/d )
+'
+
+test_done
index 807fb83af8c65304f1dae2ee35ba0f2909ddf465..22ba7a5442c587f4536aad5668df43661231de56 100755 (executable)
@@ -72,7 +72,7 @@ In addition:
 
 '
 . ./test-lib.sh
-. ../lib-read-tree-m-3way.sh
+. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
 ################################################################
 # Trivial "majority when 3 stages exist" merge plus #2ALT, #3ALT
index 1ec0535138c72bbd1e497c35c21bc5ea46b0315f..fcdd15a358f8a82b617f8ac7b22e5051975b1a4a 100755 (executable)
@@ -49,16 +49,28 @@ setup_repo
 # Argument checking
 
 test_expect_success "multiple '--stdin's are rejected" '
-       test_must_fail git hash-object --stdin --stdin < example
+       echo example | test_must_fail git hash-object --stdin --stdin
 '
 
 test_expect_success "Can't use --stdin and --stdin-paths together" '
-       test_must_fail git hash-object --stdin --stdin-paths &&
-       test_must_fail git hash-object --stdin-paths --stdin
+       echo example | test_must_fail git hash-object --stdin --stdin-paths &&
+       echo example | test_must_fail git hash-object --stdin-paths --stdin
 '
 
 test_expect_success "Can't pass filenames as arguments with --stdin-paths" '
-       test_must_fail git hash-object --stdin-paths hello < example
+       echo example | test_must_fail git hash-object --stdin-paths hello
+'
+
+test_expect_success "Can't use --path with --stdin-paths" '
+       echo example | test_must_fail git hash-object --stdin-paths --path=foo
+'
+
+test_expect_success "Can't use --stdin-paths with --no-filters" '
+       echo example | test_must_fail git hash-object --stdin-paths --no-filters
+'
+
+test_expect_success "Can't use --path with --no-filters" '
+       test_must_fail git hash-object --no-filters --path=foo
 '
 
 # Behavior
@@ -93,6 +105,42 @@ test_expect_success 'git hash-object --stdin file1 <file0 first operates on file
        test "$obname1" = "$obname1new"
 '
 
+test_expect_success 'check that appropriate filter is invoke when --path is used' '
+       echo fooQ | tr Q "\\015" >file0 &&
+       cp file0 file1 &&
+       echo "file0 -crlf" >.gitattributes &&
+       echo "file1 crlf" >>.gitattributes &&
+       git config core.autocrlf true &&
+       file0_sha=$(git hash-object file0) &&
+       file1_sha=$(git hash-object file1) &&
+       test "$file0_sha" != "$file1_sha" &&
+       path1_sha=$(git hash-object --path=file1 file0) &&
+       path0_sha=$(git hash-object --path=file0 file1) &&
+       test "$file0_sha" = "$path0_sha" &&
+       test "$file1_sha" = "$path1_sha" &&
+       path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
+       path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+       test "$file0_sha" = "$path0_sha" &&
+       test "$file1_sha" = "$path1_sha" &&
+       git config --unset core.autocrlf
+'
+
+test_expect_success 'check that --no-filters option works' '
+       echo fooQ | tr Q "\\015" >file0 &&
+       cp file0 file1 &&
+       echo "file0 -crlf" >.gitattributes &&
+       echo "file1 crlf" >>.gitattributes &&
+       git config core.autocrlf true &&
+       file0_sha=$(git hash-object file0) &&
+       file1_sha=$(git hash-object file1) &&
+       test "$file0_sha" != "$file1_sha" &&
+       nofilters_file1=$(git hash-object --no-filters file1) &&
+       test "$file0_sha" = "$nofilters_file1" &&
+       nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+       test "$file0_sha" = "$nofilters_file1" &&
+       git config --unset core.autocrlf
+'
+
 pop_repo
 
 for args in "-w --stdin" "--stdin -w"; do
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
new file mode 100755 (executable)
index 0000000..f7b3518
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='cherry-pick should rerere for conflicts'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo foo >foo &&
+       git add foo && test_tick && git commit -q -m 1 &&
+       echo foo-master >foo &&
+       git add foo && test_tick && git commit -q -m 2 &&
+
+       git checkout -b dev HEAD^ &&
+       echo foo-dev >foo &&
+       git add foo && test_tick && git commit -q -m 3 &&
+       git config rerere.enabled true
+'
+
+test_expect_success 'conflicting merge' '
+       test_must_fail git merge master
+'
+
+test_expect_success 'fixup' '
+       echo foo-dev >foo &&
+       git add foo && test_tick && git commit -q -m 4 &&
+       git reset --hard HEAD^
+       echo foo-dev >expect
+'
+
+test_expect_success 'cherry-pick conflict' '
+       test_must_fail git cherry-pick master &&
+       test_cmp expect foo
+'
+
+test_expect_success 'reconfigure' '
+       git config rerere.enabled false
+       git reset --hard
+'
+
+test_expect_success 'cherry-pick conflict without rerere' '
+       test_must_fail git cherry-pick master &&
+       test_must_fail test_cmp expect foo
+'
+
+test_done
index 883281dbd6c02ea7b2d90336c2629eafacee0257..f4f41847f32ca253d5b79a4dd3ff1e4a533d02e1 100755 (executable)
@@ -16,7 +16,7 @@ test_expect_success setup '
        : >F &&
        git add F &&
        T=$(git write-tree) &&
-       C=$(git commit-tree $T <../t3900/1-UTF-8.txt) &&
+       C=$(git commit-tree $T <"$TEST_DIRECTORY"/t3900/1-UTF-8.txt) &&
        git update-ref HEAD $C &&
        git-tag C0
 '
@@ -32,7 +32,7 @@ do
                git config i18n.commitencoding $H &&
                git-checkout -b $H C0 &&
                echo $H >F &&
-               git-commit -a -F ../t3900/$H.txt
+               git-commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt
        '
 done
 
@@ -57,13 +57,13 @@ test_expect_success 'config to remove customization' '
 '
 
 test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
-       compare_with ISO-8859-1 ../t3900/1-UTF-8.txt
+       compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
 '
 
 for H in EUCJP ISO-2022-JP
 do
        test_expect_success "$H should be shown in UTF-8 now" '
-               compare_with '$H' ../t3900/2-UTF-8.txt
+               compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
        '
 done
 
@@ -82,7 +82,7 @@ for H in ISO-8859-1 EUCJP ISO-2022-JP
 do
        test_expect_success "$H should be shown in itself now" '
                git config i18n.commitencoding '$H' &&
-               compare_with '$H' ../t3900/'$H'.txt
+               compare_with '$H' "$TEST_DIRECTORY"/t3900/'$H'.txt
        '
 done
 
@@ -91,13 +91,13 @@ test_expect_success 'config to tweak customization' '
 '
 
 test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
-       compare_with ISO-8859-1 ../t3900/1-UTF-8.txt
+       compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
 '
 
 for H in EUCJP ISO-2022-JP
 do
        test_expect_success "$H should be shown in UTF-8 now" '
-               compare_with '$H' ../t3900/2-UTF-8.txt
+               compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
        '
 done
 
@@ -107,7 +107,7 @@ do
        for H in EUCJP ISO-2022-JP
        do
                test_expect_success "$H should be shown in $J now" '
-                       compare_with '$H' ../t3900/'$J'.txt
+                       compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt
                '
        done
 done
@@ -115,7 +115,7 @@ done
 for H in ISO-8859-1 EUCJP ISO-2022-JP
 do
        test_expect_success "No conversion with $H" '
-               compare_with "--encoding=none '$H'" ../t3900/'$H'.txt
+               compare_with "--encoding=none '$H'" "$TEST_DIRECTORY"/t3900/'$H'.txt
        '
 done
 
index 235f372832cb32aefff0a00c4f2ac0e19de2e55d..f68ff530513e09aefe8f1310d6b7509f22d1112a 100755 (executable)
@@ -35,7 +35,7 @@ test_expect_success setup '
 
        # use UTF-8 in author and committer name to match the
        # i18n.commitencoding settings
-       . ../t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        test_tick &&
        echo "$GIT_AUTHOR_NAME" >mine &&
@@ -57,7 +57,7 @@ test_expect_success setup '
        # the second one on the side branch is ISO-8859-1
        git config i18n.commitencoding ISO-8859-1 &&
        # use author and committer name in ISO-8859-1 to match it.
-       . ../t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
        test_tick &&
        echo Yet another >theirs &&
        git add theirs &&
@@ -101,7 +101,7 @@ test_expect_success 'rebase (U/U)' '
 
        # The result will be committed by GIT_COMMITTER_NAME --
        # we want UTF-8 encoded name.
-       . ../t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
        git checkout -b test &&
        git-rebase master &&
 
@@ -111,7 +111,7 @@ test_expect_success 'rebase (U/U)' '
 test_expect_success 'rebase (U/L)' '
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding ISO-8859-1 &&
-       . ../t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        git reset --hard side &&
        git-rebase master &&
@@ -123,7 +123,7 @@ test_expect_success 'rebase (L/L)' '
        # In this test we want ISO-8859-1 encoded commits as the result
        git config i18n.commitencoding ISO-8859-1 &&
        git config i18n.logoutputencoding ISO-8859-1 &&
-       . ../t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard side &&
        git-rebase master &&
@@ -136,7 +136,7 @@ test_expect_success 'rebase (L/U)' '
        # to get ISO-8859-1 results.
        git config i18n.commitencoding ISO-8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . ../t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard side &&
        git-rebase master &&
@@ -149,7 +149,7 @@ test_expect_success 'cherry-pick(U/U)' '
 
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . ../t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        git reset --hard master &&
        git cherry-pick side^ &&
@@ -164,7 +164,7 @@ test_expect_success 'cherry-pick(L/L)' '
 
        git config i18n.commitencoding ISO-8859-1 &&
        git config i18n.logoutputencoding ISO-8859-1 &&
-       . ../t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard master &&
        git cherry-pick side^ &&
@@ -179,7 +179,7 @@ test_expect_success 'cherry-pick(U/L)' '
 
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding ISO-8859-1 &&
-       . ../t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        git reset --hard master &&
        git cherry-pick side^ &&
@@ -195,7 +195,7 @@ test_expect_success 'cherry-pick(L/U)' '
 
        git config i18n.commitencoding ISO-8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . ../t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard master &&
        git cherry-pick side^ &&
@@ -208,7 +208,7 @@ test_expect_success 'cherry-pick(L/U)' '
 test_expect_success 'rebase --merge (U/U)' '
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . ../t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        git reset --hard side &&
        git-rebase --merge master &&
@@ -219,7 +219,7 @@ test_expect_success 'rebase --merge (U/U)' '
 test_expect_success 'rebase --merge (U/L)' '
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding ISO-8859-1 &&
-       . ../t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
        git reset --hard side &&
        git-rebase --merge master &&
@@ -231,7 +231,7 @@ test_expect_success 'rebase --merge (L/L)' '
        # In this test we want ISO-8859-1 encoded commits as the result
        git config i18n.commitencoding ISO-8859-1 &&
        git config i18n.logoutputencoding ISO-8859-1 &&
-       . ../t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard side &&
        git-rebase --merge master &&
@@ -244,7 +244,7 @@ test_expect_success 'rebase --merge (L/U)' '
        # to get ISO-8859-1 results.
        git config i18n.commitencoding ISO-8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . ../t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
        git reset --hard side &&
        git-rebase --merge master &&
index c44b27aeb24816a346f0aa84d70546a0ffd83b2a..6ddd46915d2757bb5a40057e6850a4f72cd4dafb 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test built-in diff output engine.
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 echo >path0 'Line 1
 Line 2
index a32692417db73444dbdc143e6908b7371be79d42..71bac83dd5e42a19e3b1a7e869df0e7143371c99 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test rename detection in diff engine.
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 echo >path0 'Line 1
 Line 2
index a4cfde6b2927a4655f582d7e92dad4568f7b5f89..cc3681f16118ca70ae9f65a27ccd6f354a6deee1 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test diff raw-output.
 
 '
 . ./test-lib.sh
-. ../lib-read-tree-m-3way.sh
+. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
 cat >.test-plain-OA <<\EOF
 :000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A     AA
@@ -168,6 +168,20 @@ test_expect_success \
     'git diff-tree -r $tree_A $tree_B >.test-a &&
      cmp -s .test-a .test-recursive-AB'
 
+test_expect_success \
+    'diff-tree --stdin of known trees.' \
+    'echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
+     echo $tree_A $tree_B > .test-plain-ABx &&
+     cat .test-plain-AB >> .test-plain-ABx &&
+     cmp -s .test-a .test-plain-ABx'
+
+test_expect_success \
+    'diff-tree --stdin of known trees.' \
+    'echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
+     echo $tree_A $tree_B > .test-recursive-ABx &&
+     cat .test-recursive-AB >> .test-recursive-ABx &&
+     cmp -s .test-a .test-recursive-ABx'
+
 test_expect_success \
     'diff-cache O with A in cache' \
     'git read-tree $tree_A &&
index 8b1f875286b25b5fc1a527b5b0281f866724a82d..c6130c40198ad5ed5ec8a0342341a4ec5cc49d7d 100755 (executable)
@@ -7,11 +7,11 @@ test_description='More rename detection
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -99,7 +99,7 @@ test_expect_success \
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
index 3d25be7a6709cdd23e0d583a8f1a3e19a3927cd8..b35af9b42d318904bd12649562be309fd49977a3 100755 (executable)
@@ -10,7 +10,7 @@ copy of symbolic links, but should not produce rename/copy followed
 by an edit for them.
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 test_expect_success \
     'prepare reference tree' \
index 66300173124eee8480e7214322faf8bc493ad940..1ba359d478e3d3491aed5f622932382767c8f4dc 100755 (executable)
@@ -7,11 +7,11 @@ test_description='Same rename detection as t4003 but testing diff-raw.
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -71,7 +71,7 @@ test_expect_success \
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -C --find-copies-harder $tree >current
index 104a4e1492b65a64d061e1ce1df0b0a2a49fb20e..42072d724ef67c51e6a9adcf9bf8e3ca1757e053 100755 (executable)
@@ -7,12 +7,12 @@ test_description='Rename interaction with pathspec.
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
     'mkdir path0 path1 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git update-index --add path0/COPYING &&
     tree=$(git write-tree) &&
     echo $tree'
index 26c2e4aa65c539c527ae8e2d71b5abb40c21fbbd..7e343a9cd130a73547ed1df9a4d9cd364e60bf9f 100755 (executable)
@@ -22,12 +22,12 @@ four changes in total.
 Further, with -B and -M together, these should turn into two renames.
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     setup \
-    'cat ../../README >file0 &&
-     cat ../../COPYING >file1 &&
+    'cat "$TEST_DIRECTORY"/../README >file0 &&
+     cat "$TEST_DIRECTORY"/../COPYING >file1 &&
     git update-index --add file0 file1 &&
     tree=$(git write-tree) &&
     echo "$tree"'
index d2b45e7b8fb3902cb740e0df582f92195a295f24..de3f17478efcaf008340a7ab81cb049f9a9e9a3a 100755 (executable)
@@ -7,11 +7,11 @@ test_description='Same rename detection as t4003 but testing diff-raw -z.
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -78,7 +78,7 @@ test_expect_success \
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -z -C --find-copies-harder $tree >current
index ad3d9e48454d2e72afce682df009cdaaee9ba3c5..9322298ecc6cb79f0123dea4e329317c5bfbfd11 100755 (executable)
@@ -10,7 +10,7 @@ Prepare:
         path1/file1
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     setup \
index c6d13693ba45b594704c2ef55d40540ee3f9c25f..02efecae3ad06e5a62553e990fc0934dd0c65eab 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test diff of symlinks.
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 cat > expected << EOF
 diff --git a/frotz b/frotz
index eced1f30fb8475739aef52230bbb79946a0f76d8..69934991cb297ca817e9fdf254e70de15b607780 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success 'prepare repository' \
        'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
         git update-index --add a b c d &&
         echo git >a &&
-        cat ../test4012.png >b &&
+        cat "$TEST_DIRECTORY"/test4012.png >b &&
         echo git >c &&
         cat b b >d'
 
index 9337b81064bbdbe4e7f590830b458c48226c4a17..1a6b52234d8df70e5ddcb28c0ddde582d8310d43 100755 (executable)
@@ -99,7 +99,7 @@ do
        test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
        cnt=`expr $test_count + 1`
        pfx=`printf "%04d" $cnt`
-       expect="../t4013/diff.$test"
+       expect="$TEST_DIRECTORY/t4013/diff.$test"
        actual="$pfx-diff.$test"
 
        test_expect_success "git $cmd" '
index 7fe853c20d1111e40371a3796d82bb8485f5ebcf..9d99dc28879d4f7f35001e0785f97f319fe13b40 100755 (executable)
@@ -230,4 +230,29 @@ test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
 
 '
 
+cat > expect << EOF
+---
+ file |   16 ++++++++++++++++
+ 1 files changed, 16 insertions(+), 0 deletions(-)
+
+diff --git a/file b/file
+index 40f36c6..2dc5c23 100644
+--- a/file
++++ b/file
+@@ -13,4 +13,20 @@ C
+ 10
+ D
+ E
+ F
++5
+EOF
+
+test_expect_success 'format-patch respects -U' '
+
+       git format-patch -U4 -2 &&
+       sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+       test_cmp expect output
+
+'
+
 test_done
index b1cbd36d1710a38b94838a2fdf08e0e5ded431f8..fc2307eaa3b4e8b1481fd7aa5ae0f93d085338af 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test special whitespace in diff engine.
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 # Ray Lehtiniemi's example
 
index 637b4e19d52e81cf1472a4ed9dcfb0c9ff00da2b..dfe3fbc74b96fff50160c032f0a65ba54f9ad1b2 100755 (executable)
@@ -104,7 +104,7 @@ echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
 test_expect_success 'force diff with "diff"' '
        echo >.gitattributes "file diff" &&
        git diff >actual &&
-       test_cmp ../t4020/diff.NUL actual
+       test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
 '
 
 test_done
index bf996fc414d3b5ea16488a8b274973a8347b29cb..2a537a21e8e6793e6ffa2fe1522e30f877b209fe 100755 (executable)
@@ -6,12 +6,12 @@ test_description='rewrite diff'
 
 test_expect_success setup '
 
-       cat ../../COPYING >test &&
+       cat "$TEST_DIRECTORY"/../COPYING >test &&
        git add test &&
        tr \
          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
          "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
-         <../../COPYING >test
+         <"$TEST_DIRECTORY"/../COPYING >test
 
 '
 
index 4dbfc6e8b751a6c93b1f9dfee8ce649235c98c93..297ddb5a25b682412851aed70370771aa5a15976 100755 (executable)
@@ -7,21 +7,21 @@ test_description='typechange rename detection'
 test_expect_success setup '
 
        rm -f foo bar &&
-       cat ../../COPYING >foo &&
+       cat "$TEST_DIRECTORY"/../COPYING >foo &&
        ln -s linklink bar &&
        git add foo bar &&
        git commit -a -m Initial &&
        git tag one &&
 
        rm -f foo bar &&
-       cat ../../COPYING >bar &&
+       cat "$TEST_DIRECTORY"/../COPYING >bar &&
        ln -s linklink foo &&
        git add foo bar &&
        git commit -a -m Second &&
        git tag two &&
 
        rm -f foo bar &&
-       cat ../../COPYING >foo &&
+       cat "$TEST_DIRECTORY"/../COPYING >foo &&
        git add foo &&
        git commit -a -m Third &&
        git tag three &&
@@ -35,15 +35,15 @@ test_expect_success setup '
        # This is purely for sanity check
 
        rm -f foo bar &&
-       cat ../../COPYING >foo &&
-       cat ../../Makefile >bar &&
+       cat "$TEST_DIRECTORY"/../COPYING >foo &&
+       cat "$TEST_DIRECTORY"/../Makefile >bar &&
        git add foo bar &&
        git commit -a -m Fifth &&
        git tag five &&
 
        rm -f foo bar &&
-       cat ../../Makefile >foo &&
-       cat ../../COPYING >bar &&
+       cat "$TEST_DIRECTORY"/../Makefile >foo &&
+       cat "$TEST_DIRECTORY"/../COPYING >bar &&
        git add foo bar &&
        git commit -a -m Sixth &&
        git tag six
index ba6679c6e4032bb12e4206226f95770946ece8cc..1c2edebb09bd6e1d7581365011c8500da57942c5 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='difference in submodules'
 
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 _z40=0000000000000000000000000000000000000000
 test_expect_success setup '
diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh
new file mode 100755 (executable)
index 0000000..4ca65e0
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright (c) Jim Meyering
+#
+test_description='diff honors config option, diff.suppress-blank-empty'
+
+. ./test-lib.sh
+
+cat <<\EOF > exp ||
+diff --git a/f b/f
+index 5f6a263..8cb8bae 100644
+--- a/f
++++ b/f
+@@ -1,2 +1,2 @@
+
+-x
++y
+EOF
+exit 1
+
+test_expect_success \
+    "$test_description" \
+    'printf "\nx\n" > f &&
+     git add f &&
+     git commit -q -m. f &&
+     printf "\ny\n" > f &&
+     git config --bool diff.suppress-blank-empty true &&
+     git diff f > actual &&
+     test_cmp exp actual &&
+     perl -i.bak -p -e "s/^\$/ /" exp &&
+     git config --bool diff.suppress-blank-empty false &&
+     git diff f > actual &&
+     test_cmp exp actual &&
+     git config --bool --unset diff.suppress-blank-empty &&
+     git diff f > actual &&
+     test_cmp exp actual
+     '
+
+test_done
index e0c67740a5ef85aaa3edc9a4514da72c92ce30ec..9b433de83630774206fb89dfae1a4396264390cc 100755 (executable)
@@ -17,13 +17,13 @@ do
        test_expect_success "$title" '
                git apply --stat --summary \
                        <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
-               test_cmp ../t4100/t-apply-$num.expect current
+               test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
        '
 
        test_expect_success "$title with recount" '
                sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
                git apply --recount --stat --summary >current &&
-               test_cmp ../t4100/t-apply-$num.expect current
+               test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
        '
 done <<\EOF
 rename
index da8abcf36418dbd2e9d8ec85871c245991f96fda..e3443d004d026c86fd783cb8e6e3d03f22676778 100755 (executable)
@@ -21,9 +21,10 @@ do
   do
     test $i -eq $j && continue
     cat frotz.$i >frotz
-    test_expect_success \
-        "apply diff between $i and $j" \
-       "git apply <../t4101/diff.$i-$j && diff frotz.$j frotz"
+    test_expect_success "apply diff between $i and $j" '
+       git apply <"$TEST_DIRECTORY"/t4101/diff.$i-$j &&
+       test_cmp frotz.$j frotz
+    '
   done
 done
 
index 198e3503d53c109e5090e213914ee23360c06902..fe14589427643b9bb7759c597935da724adf1a64 100755 (executable)
@@ -8,27 +8,28 @@ test_description='git mailinfo and git mailsplit test'
 . ./test-lib.sh
 
 test_expect_success 'split sample box' \
-       'git mailsplit -o. ../t5100/sample.mbox >last &&
+       'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
        last=`cat last` &&
        echo total is $last &&
        test `cat last` = 11'
 
 for mail in `echo 00*`
 do
-       test_expect_success "mailinfo $mail" \
-               "git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
+       test_expect_success "mailinfo $mail" '
+               git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
                echo msg &&
-               diff ../t5100/msg$mail msg$mail &&
+               test_cmp "$TEST_DIRECTORY"/t5100/msg$mail msg$mail &&
                echo patch &&
-               diff ../t5100/patch$mail patch$mail &&
+               test_cmp "$TEST_DIRECTORY"/t5100/patch$mail patch$mail &&
                echo info &&
-               diff ../t5100/info$mail info$mail"
+               test_cmp "$TEST_DIRECTORY"/t5100/info$mail info$mail
+       '
 done
 
 test_expect_success 'respect NULs' '
 
-       git mailsplit -d3 -o. ../t5100/nul-plain &&
-       cmp ../t5100/nul-plain 001 &&
+       git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
+       test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 &&
        (cat 001 | git mailinfo msg patch) &&
        test 4 = $(wc -l < patch)
 
@@ -36,10 +37,10 @@ test_expect_success 'respect NULs' '
 
 test_expect_success 'Preserve NULs out of MIME encoded message' '
 
-       git mailsplit -d5 -o. ../t5100/nul-b64.in &&
-       cmp ../t5100/nul-b64.in 00001 &&
+       git mailsplit -d5 -o. "$TEST_DIRECTORY"/t5100/nul-b64.in &&
+       test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.in 00001 &&
        git mailinfo msg patch <00001 &&
-       cmp ../t5100/nul-b64.expect patch
+       test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.expect patch
 
 '
 
index 362cf7e928090fb3752936317f78a4d128810127..7125baebb891d276ab2a9724b5a6801da7cfc884 100755 (executable)
@@ -137,7 +137,7 @@ test_expect_success "clone shallow object count" \
        "test \"in-pack: 18\" = \"$(grep in-pack count.shallow)\""
 
 count_output () {
-       sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/: 0$/d' "$1"
+       sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/^size-pack:/d' -e '/: 0$/d' "$1"
 }
 
 test_expect_success "clone shallow object count (part 2)" '
index 8becbc3f38fde02371ebbcd9a39a320a1c00c290..1f4608d8ba4748a2bd5c7a3d5a75a04364e8f646 100755 (executable)
@@ -131,9 +131,9 @@ do
        test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
        cnt=`expr $test_count + 1`
        pfx=`printf "%04d" $cnt`
-       expect_f="../../t5515/fetch.$test"
+       expect_f="$TEST_DIRECTORY/t5515/fetch.$test"
        actual_f="$pfx-fetch.$test"
-       expect_r="../../t5515/refs.$test"
+       expect_r="$TEST_DIRECTORY/t5515/refs.$test"
        actual_r="$pfx-refs.$test"
 
        test_expect_success "$cmd" '
index b0d242e3edc13dd7cad8f5fc4b5d2d205e620190..da9588645cd9d0054440e5ed3ba14f630c44f506 100755 (executable)
@@ -19,7 +19,7 @@ then
        exit
 fi
 
-. ../lib-httpd.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
 
 if ! start_httpd >&3 2>&4
 then
index 8f5de097ecd703ae5f6f889ecb735f7277f361be..b4e8fbaa5e6f2a56094c05ca505630669a51e101 100755 (executable)
@@ -5,7 +5,7 @@
 test_description='Tests git rev-list --bisect functionality'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 # usage: test_bisection max-diff bisect-option head ^prune...
 #
index 5daa0be8cc856bff513905bc6583854e0b5ae53a..2c73f2da7b0a1f560bfd41376b587d1c91b18615 100755 (executable)
@@ -6,7 +6,7 @@
 test_description='Tests git rev-list --topo-order functionality'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 list_duplicates()
 {
index b6e57b2426728cce308a57315247cd2a66cabf4a..04e4b7c5c2aa4f7c6922ebc5c9236962ab5d175c 100755 (executable)
@@ -108,4 +108,52 @@ test_expect_success 'compute merge-base (all)' \
     'MB=$(git merge-base --all PL PR) &&
      expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
 
+# Another set to demonstrate base between one commit and a merge
+# in the documentation.
+
+test_expect_success 'merge-base for octopus-step (setup)' '
+       test_tick && git commit --allow-empty -m root && git tag MMR &&
+       test_tick && git commit --allow-empty -m 1 && git tag MM1 &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m A && git tag MMA &&
+       git checkout MM1 &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m B && git tag MMB &&
+       git checkout MMR &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m C && git tag MMC
+'
+
+test_expect_success 'merge-base A B C' '
+       MB=$(git merge-base --all MMA MMB MMC) &&
+       MM1=$(git rev-parse --verify MM1) &&
+       test "$MM1" = "$MB"
+'
+
+test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
+       git reset --hard MMR &&
+       test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
+       git reset --hard E &&
+       test_tick && git commit --allow-empty -m 2 && git tag CC2 &&
+       test_tick && git merge -s ours CC1 &&
+       test_tick && git commit --allow-empty -m o &&
+       test_tick && git commit --allow-empty -m B && git tag CCB &&
+       git reset --hard CC1 &&
+       test_tick && git merge -s ours CC2 &&
+       test_tick && git commit --allow-empty -m A && git tag CCA
+'
+
+test_expect_success 'merge-base B A^^ A^^2' '
+       MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) &&
+       MB1=$(git rev-parse CC1 CC2 | sort) &&
+       test "$MB0" = "$MB1"
+'
+
 test_done
index f674c48cab3e80d63b5a5831c667b8e08b542905..42620e0732bfdcb0370ca9317670e9ac1e11c735 100755 (executable)
@@ -136,7 +136,7 @@ test_expect_success "expected conflict markers" "test_cmp expect out"
 
 test_expect_success 'binary files cannot be merged' '
        test_must_fail git merge-file -p \
-               orig.txt ../test4012.png new1.txt 2> merge.err &&
+               orig.txt "$TEST_DIRECTORY"/test4012.png new1.txt 2> merge.err &&
        grep "Cannot merge binary files" merge.err
 '
 
index 92ca1f0f8ccabe6f01159ea3e4a73683387ec4a3..b519626ca09255a433b0d1cb32780a6feafa09f6 100755 (executable)
@@ -6,7 +6,7 @@ test_description='ask merge-recursive to merge binary files'
 
 test_expect_success setup '
 
-       cat ../test4012.png >m &&
+       cat "$TEST_DIRECTORY"/test4012.png >m &&
        git add m &&
        git ls-files -s | sed -e "s/ 0  / 1     /" >E1 &&
        test_tick &&
index 919552a2fc5544c130268befca322a6e6a8081c3..f105fab98e2d493ab489d345676101fc13096c22 100755 (executable)
@@ -6,7 +6,7 @@
 test_description='Test git rev-parse with different parent options'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 date >path0
 git update-index --add path0
index bc74349416d858834c43f6c648daa95c8b9f3a7a..8f5a06f7dd8383722022eb7a8abd0ff9bafa6f45 100755 (executable)
@@ -83,13 +83,13 @@ test_expect_success 'merge-msg test #1' '
 '
 
 cat >expected <<EOF
-Merge branch 'left' of ../$test
+Merge branch 'left' of $TEST_DIRECTORY/$test
 EOF
 
 test_expect_success 'merge-msg test #2' '
 
        git checkout master &&
-       git fetch ../"$test" left &&
+       git fetch "$TEST_DIRECTORY/$test" left &&
 
        git fmt-merge-msg <.git/FETCH_HEAD >actual &&
        test_cmp expected actual
index 910a28c7e29b6dd8bd30d1ccb156681b44e51bca..78167980b31caa7d0da5e46337d6738ae473b940 100755 (executable)
@@ -6,7 +6,7 @@ test_description='git mv in subdirs'
 test_expect_success \
     'prepare reference tree' \
     'mkdir path0 path1 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git add path0/COPYING &&
      git-commit -m add -a'
 
@@ -40,7 +40,7 @@ test_expect_success \
 
 test_expect_success \
     'adding another file' \
-    'cp ../../README path0/README &&
+    'cp "$TEST_DIRECTORY"/../README path0/README &&
      git add path0/README &&
      git-commit -m add2 -a'
 
index a0ab096c8fdee153a89a1428f85c9bf107badada..f92d414e63550cf4e0dcf3b3bc9776f1e23c8a6c 100755 (executable)
@@ -250,4 +250,12 @@ test_expect_success 'Tag name filtering strips gpg signature' '
        test_cmp expect actual
 '
 
+test_expect_success 'Tag name filtering allows slashes in tag names' '
+       git tag -m tag-with-slash X/1 &&
+       git cat-file tag X/1 | sed -e s,X/1,X/2, > expect &&
+       git filter-branch -f --tag-name-filter "echo X/2" &&
+       git cat-file tag X/2 > actual &&
+       test_cmp expect actual
+'
+
 test_done
index 8d44c2ed1f5148cbfc7affe155eaa4b6bd04c66a..198244c57d0adb7c7e6c698ff098ac19055d3d21 100755 (executable)
@@ -625,7 +625,7 @@ esac
 # Name and email: C O Mitter <committer@example.com>
 # No password given, to enable non-interactive operation.
 
-cp -R ../t7004 ./gpghome
+cp -R "$TEST_DIRECTORY"/t7004 ./gpghome
 chmod 0700 gpghome
 GNUPGHOME="$(pwd)/gpghome"
 export GNUPGHOME
index 0d9874bfd7082f9ef16c1f6b3ff8a848a19d8937..ffaeb3983a2c393abdc77a35fa8546e20273f421 100755 (executable)
@@ -9,7 +9,7 @@ test_description='git-reset should cull empty subdirs'
 test_expect_success \
     'creating initial files' \
     'mkdir path0 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git add path0/COPYING &&
      git-commit -m add -a'
 
@@ -17,10 +17,10 @@ test_expect_success \
     'creating second files' \
     'mkdir path1 &&
      mkdir path1/path2 &&
-     cp ../../COPYING path1/path2/COPYING &&
-     cp ../../COPYING path1/COPYING &&
-     cp ../../COPYING COPYING &&
-     cp ../../COPYING path0/COPYING-TOO &&
+     cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
      git add path1/path2/COPYING &&
      git add path1/COPYING &&
      git add COPYING &&
index 9ad5d635a2881c920fff8e524aea0ed931f68e6c..1dff84d2fd9ef665a9db314819152fcb94d17974 100755 (executable)
@@ -337,4 +337,36 @@ test_expect_success \
     test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
     test_must_fail git checkout --track -b track'
 
+test_expect_success \
+    'checkout with --track fakes a sensible -b <name>' '
+    git update-ref refs/remotes/origin/koala/bear renamer &&
+    git update-ref refs/new/koala/bear renamer &&
+
+    git checkout --track origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track refs/remotes/origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track remotes/origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track refs/new/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
+'
+
+test_expect_success \
+    'checkout with --track, but without -b, fails with too short tracked name' '
+    test_must_fail git checkout --track renamer'
+
 test_done
index 809bdba6309c57f149b6c7f3dc3f4e147f9eb24b..7ae0bd0e31f70271a18935be2ac1a929b5cda1bf 100755 (executable)
@@ -46,15 +46,24 @@ test_expect_success 'unedited template with comments should not commit' '
 '
 
 test_expect_success 'a Signed-off-by line by itself should not commit' '
-       ! GIT_EDITOR=../t7500/add-signed-off git commit --template "$TEMPLATE"
+       (
+               test_set_editor "$TEST_DIRECTORY"/t7500/add-signed-off &&
+               test_must_fail git commit --template "$TEMPLATE"
+       )
 '
 
 test_expect_success 'adding comments to a template should not commit' '
-       ! GIT_EDITOR=../t7500/add-comments git commit --template "$TEMPLATE"
+       (
+               test_set_editor "$TEST_DIRECTORY"/t7500/add-comments &&
+               test_must_fail git commit --template "$TEMPLATE"
+       )
 '
 
 test_expect_success 'adding real content to a template should commit' '
-       GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" &&
+       (
+               test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+               git commit --template "$TEMPLATE"
+       ) &&
        commit_msg_is "template linecommit message"
 '
 
@@ -62,7 +71,10 @@ test_expect_success '-t option should be short for --template' '
        echo "short template" > "$TEMPLATE" &&
        echo "new content" >> foo &&
        git add foo &&
-       GIT_EDITOR=../t7500/add-content git commit -t "$TEMPLATE" &&
+       (
+               test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+               git commit -t "$TEMPLATE"
+       ) &&
        commit_msg_is "short templatecommit message"
 '
 
@@ -71,7 +83,10 @@ test_expect_success 'config-specified template should commit' '
        git config commit.template "$TEMPLATE" &&
        echo "more content" >> foo &&
        git add foo &&
-       GIT_EDITOR=../t7500/add-content git commit &&
+       (
+               test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+               git commit
+       ) &&
        git config --unset commit.template &&
        commit_msg_is "new templatecommit message"
 '
@@ -79,7 +94,7 @@ test_expect_success 'config-specified template should commit' '
 test_expect_success 'explicit commit message should override template' '
        echo "still more content" >> foo &&
        git add foo &&
-       GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" \
+       GIT_EDITOR="$TEST_DIRECTORY"/t7500/add-content git commit --template "$TEMPLATE" \
                -m "command line msg" &&
        commit_msg_is "command line msg"
 '
@@ -88,8 +103,10 @@ test_expect_success 'commit message from file should override template' '
        echo "content galore" >> foo &&
        git add foo &&
        echo "standard input msg" |
-               GIT_EDITOR=../t7500/add-content git commit \
-                       --template "$TEMPLATE" --file - &&
+       (
+               test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+               git commit --template "$TEMPLATE" --file -
+       ) &&
        commit_msg_is "standard input msg"
 '
 
@@ -132,10 +149,12 @@ EOF
 
 test_expect_success '--signoff' '
        echo "yet another content *narf*" >> foo &&
-       echo "zort" |
-               GIT_EDITOR=../t7500/add-content git commit -s -F - foo &&
+       echo "zort" | (
+               test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+               git commit -s -F - foo
+       ) &&
        git cat-file commit HEAD | sed "1,/^$/d" > output &&
-       diff expect output
+       test_cmp expect output
 '
 
 test_expect_success 'commit message from file (1)' '
index dbc90bc41625fccb4119cd6c2fc49668fe784122..6a2b12558a85db2d522b3cc6047b1d5b1b7fb327 100755 (executable)
@@ -230,6 +230,10 @@ test_expect_success 'test option parsing' '
        test_must_fail git merge
 '
 
+test_expect_success 'reject non-strategy with a git-merge-foo name' '
+       test_must_fail git merge -s index c1
+'
+
 test_expect_success 'merge c0 with c1' '
        git reset --hard c0 &&
        git merge c1 &&
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh
new file mode 100755 (executable)
index 0000000..de9b6ed
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='git-merge
+
+Testing a custom strategy.'
+
+. ./test-lib.sh
+
+cat >git-merge-theirs <<EOF
+#!$SHELL_PATH
+eval git read-tree --reset -u \\\$\$#
+EOF
+chmod +x git-merge-theirs
+PATH=.:$PATH
+export PATH
+
+test_expect_success 'setup' '
+       echo c0 >c0.c &&
+       git add c0.c &&
+       git commit -m c0 &&
+       git tag c0 &&
+       echo c1 >c1.c &&
+       git add c1.c &&
+       git commit -m c1 &&
+       git tag c1 &&
+       git reset --hard c0 &&
+       echo c1c1 >c1.c &&
+       echo c2 >c2.c &&
+       git add c1.c c2.c &&
+       git commit -m c2 &&
+       git tag c2
+'
+
+test_expect_success 'merge c2 with a custom strategy' '
+       git reset --hard c1 &&
+       git merge -s theirs c2 &&
+       test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+       test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+       test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+       test "$(git rev-parse c2^{tree})" = "$(git rev-parse HEAD^{tree})" &&
+       git diff --exit-code &&
+       git diff --exit-code c2 HEAD &&
+       git diff --exit-code c2 &&
+       test -f c0.c &&
+       grep c1c1 c1.c &&
+       test -f c2.c
+'
+
+test_done
index eabec2e06e2f97fc1790cd4ce30a80e402d4a205..45cb60ea4b167676b07ae1c847c0467f2a5e3d69 100755 (executable)
@@ -4,7 +4,7 @@ test_description='git annotate'
 . ./test-lib.sh
 
 PROG='git annotate'
-. ../annotate-tests.sh
+. "$TEST_DIRECTORY"/annotate-tests.sh
 
 test_expect_success \
     'Annotating an old revision works' \
index 92ece30fa94784bdad8ae50fc370487e60bbcb5c..597cf0486fbe1034594d3eec821f5278d9648d43 100755 (executable)
@@ -4,6 +4,6 @@ test_description='git blame'
 . ./test-lib.sh
 
 PROG='git blame -c'
-. ../annotate-tests.sh
+. "$TEST_DIRECTORY"/annotate-tests.sh
 
 test_done
index 04d2a65c087de78fa8126b68774673532497276e..83bd1cf17a4d4e21058a31d9115cd7a37a74cbac 100755 (executable)
@@ -8,7 +8,7 @@ test_description='git-svn useSvmProps test'
 . ./lib-git-svn.sh
 
 test_expect_success 'load svm repo' '
-       svnadmin load -q "$rawsvnrepo" < ../t9110/svm.dump &&
+       svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9110/svm.dump &&
        git-svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr &&
        git-svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh &&
        git-svn init --minimize-url -R argh -i e \
index a8d74dcd3aba7c462d46ea33c722d4307d24bded..c5dfd61e41dfd5e466bf163af17946215ee56a74 100755 (executable)
@@ -8,7 +8,7 @@ test_description='git-svn useSvnsyncProps test'
 . ./lib-git-svn.sh
 
 test_expect_success 'load svnsync repo' '
-       svnadmin load -q "$rawsvnrepo" < ../t9111/svnsync.dump &&
+       svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9111/svnsync.dump &&
        git-svn init --minimize-url -R arr -i bar "$svnrepo"/bar &&
        git-svn init --minimize-url -R argh -i dir "$svnrepo"/dir &&
        git-svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e &&
index f0fbd3aff7e63f64f8ba388db805013c43b4b22c..b0ba1f0200c368f06b13193b5c353e802f42c8a4 100755 (executable)
@@ -8,7 +8,7 @@ test_description='git-svn dcommit can commit renames of files with ugly names'
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
-       svnadmin load -q "$rawsvnrepo" < ../t9115/funky-names.dump &&
+       svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9115/funky-names.dump &&
        start_httpd gtk+
        '
 
index 99230b08107102836f752c14e1b0a67804b35ea3..92e69a2a75df6cb16452ec4858d7d004946e016f 100755 (executable)
@@ -8,7 +8,7 @@ test_description='git-svn can fetch renamed directories'
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with renamed directory' '
-       svnadmin load -q "$rawsvnrepo" < ../t9121/renamed-dir.dump
+       svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9121/renamed-dir.dump
        '
 
 test_expect_success 'init and fetch repository' '
index 3e32e84e6cd32413f98b5189f869bfb8f0a7f354..988c3ac754ab67867ed94ab23c58979621261a11 100755 (executable)
@@ -45,8 +45,8 @@ test_expect_success \
     'mkdir A B C D E F &&
      echo hello1 >A/newfile1.txt &&
      echo hello2 >B/newfile2.txt &&
-     cp ../test9200a.png C/newfile3.png &&
-     cp ../test9200a.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png C/newfile3.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png D/newfile4.png &&
      git add A/newfile1.txt &&
      git add B/newfile2.txt &&
      git add C/newfile3.png &&
@@ -71,8 +71,8 @@ test_expect_success \
      rm -f B/newfile2.txt &&
      rm -f C/newfile3.png &&
      echo Hello5  >E/newfile5.txt &&
-     cp ../test9200b.png D/newfile4.png &&
-     cp ../test9200a.png F/newfile6.png &&
+     cp "$TEST_DIRECTORY"/test9200b.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png F/newfile6.png &&
      git add E/newfile5.txt &&
      git add F/newfile6.png &&
      git commit -a -m "Test: Remove, add and update" &&
@@ -160,7 +160,7 @@ test_expect_success \
      'mkdir "G g" &&
       echo ok then >"G g/with spaces.txt" &&
       git add "G g/with spaces.txt" && \
-      cp ../test9200a.png "G g/with spaces.png" && \
+      cp "$TEST_DIRECTORY"/test9200a.png "G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "With spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -172,7 +172,7 @@ test_expect_success \
 test_expect_success \
      'Update file with spaces in file name' \
      'echo Ok then >>"G g/with spaces.txt" &&
-      cat ../test9200a.png >>"G g/with spaces.png" && \
+      cat "$TEST_DIRECTORY"/test9200a.png >>"G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "Update with spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -197,7 +197,7 @@ test_expect_success \
      'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
       echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
-      cp ../test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+      cp "$TEST_DIRECTORY"/test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
       git commit -a -m "Går det så går det" && \
       id=$(git rev-list --max-count=1 HEAD) &&
index c6bc0a607f200fcc8888b66ff1a8f0e324332db8..bd5d5af661b19bc1665e52d8859c1077ac6cbd93 100755 (executable)
@@ -5,7 +5,7 @@
 
 test_description='test git-fast-import utility'
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 file2_data='file2
 second line of EOF'
index c19b4a2bab586b21da43c7a838ba85626f913568..3cb9f807084456e248675301ce3fcf13d7dbe1da 100755 (executable)
@@ -67,7 +67,7 @@ test_expect_success 'iso-8859-1' '
 
        git config i18n.commitencoding ISO-8859-1 &&
        # use author and committer name in ISO-8859-1 to match it.
-       . ../t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
        test_tick &&
        echo rosten >file &&
        git commit -s -m den file &&
index ae7082be1d903e1f4d5758610d5166152f2847cc..46ba19be7da754ffa14321eabac9ba0a985e6d94 100755 (executable)
@@ -25,9 +25,9 @@ our \$site_name = "[localhost]";
 our \$site_header = "";
 our \$site_footer = "";
 our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css");
-our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png";
-our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png";
+our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css");
+our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png";
+our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png";
 our \$projects_list = "";
 our \$export_ok = "";
 our \$strict_export = "";
@@ -54,7 +54,7 @@ gitweb_run () {
        # written to web server logs, so we are not interested in that:
        # we are interested only in properly formatted errors/warnings
        rm -f gitweb.log &&
-       perl -- "$(pwd)/../../gitweb/gitweb.perl" \
+       perl -- "$TEST_DIRECTORY/../gitweb/gitweb.perl" \
                >/dev/null 2>gitweb.log &&
        if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi
 
@@ -525,20 +525,20 @@ test_debug 'cat gitweb.log'
 
 test_expect_success \
        'encode(commit): utf8' \
-       '. ../t3901-utf8.txt &&
+       '. "$TEST_DIRECTORY"/t3901-utf8.txt &&
         echo "UTF-8" >> file &&
         git add file &&
-        git commit -F ../t3900/1-UTF-8.txt &&
+        git commit -F "$TEST_DIRECTORY"/t3900/1-UTF-8.txt &&
         gitweb_run "p=.git;a=commit"'
 test_debug 'cat gitweb.log'
 
 test_expect_success \
        'encode(commit): iso-8859-1' \
-       '. ../t3901-8859-1.txt &&
+       '. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
         echo "ISO-8859-1" >> file &&
         git add file &&
         git config i18n.commitencoding ISO-8859-1 &&
-        git commit -F ../t3900/ISO-8859-1.txt &&
+        git commit -F "$TEST_DIRECTORY"/t3900/ISO-8859-1.txt &&
         git config --unset i18n.commitencoding &&
         gitweb_run "p=.git;a=commit"'
 test_debug 'cat gitweb.log'
index 9706ee5773692bd8fcfbc9015ef062947c0a2da5..0f04ba094b31285ff16b656f3b1e8fa7c3473a71 100755 (executable)
@@ -39,6 +39,6 @@ test_expect_success \
 
 test_external_without_stderr \
     'Perl API' \
-    perl ../t9700/test.pl
+    perl "$TEST_DIRECTORY"/t9700/test.pl
 
 test_done
index 4d2312548a81762918ac05b9a0014195b08ea532..851cea4a4b7c36ff4b852616108bdaf481e2e9bc 100755 (executable)
 BEGIN { use_ok('Git') }
 
 # set up
-our $repo_dir = "trash directory";
 our $abs_repo_dir = Cwd->cwd;
-die "this must be run by calling the t/t97* shell script(s)\n"
-    if basename(Cwd->cwd) ne $repo_dir;
 ok(our $r = Git->repository(Directory => "."), "open repository");
 
 # config
index 11c027571b44c429b4f6fdca88bff9c3360c7545..e2b106cb6a2337f873a7225670392b376f74c6e7 100644 (file)
@@ -406,7 +406,7 @@ test_create_repo () {
        error "bug in the test script: not 1 parameter to test-create-repo"
        owd=`pwd`
        repo="$1"
-       mkdir "$repo"
+       mkdir -p "$repo"
        cd "$repo" || error "Cannot setup test environment"
        "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 ||
        error "cannot run git init -- have you built things yet?"
@@ -449,6 +449,11 @@ test_done () {
                # we will leave things as they are.
 
                say_color pass "passed all $msg"
+
+               test -d "$remove_trash" &&
+               cd "$(dirname "$remove_trash")" &&
+               rm -rf "$(basename "$remove_trash")"
+
                exit 0 ;;
 
        *)
@@ -485,7 +490,8 @@ fi
 . ../GIT-BUILD-OPTIONS
 
 # Test repository
-test="trash directory"
+test="trash directory.$(basename "$0" .sh)"
+test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
 rm -fr "$test" || {
        trap - exit
        echo >&5 "FATAL: Cannot prepare test area"
index cc3fc3094cbbf1e15dff9cd1f3fc80e92d6be06e..0722a926f71fb91a03a916e02fea456e8cc0086d 100644 (file)
@@ -23,13 +23,13 @@ all: boilerplates.made custom
 
 bpsrc = $(filter-out %~,$(wildcard *--*))
 boilerplates.made : $(bpsrc)
-       $(QUIET)ls *--* 2>/dev/null | \
+       $(QUIET)umask 022 && ls *--* 2>/dev/null | \
        while read boilerplate; \
        do \
                case "$$boilerplate" in *~) continue ;; esac && \
                dst=`echo "$$boilerplate" | sed -e 's|^this|.|;s|--|/|g'` && \
                dir=`expr "$$dst" : '\(.*\)/'` && \
-               $(INSTALL) -d -m 755 blt/$$dir && \
+               mkdir -p blt/$$dir && \
                case "$$boilerplate" in \
                *--) ;; \
                *) cp -p $$boilerplate blt/$$dst ;; \
index 61dc5c547019776b971dc89d009f628bbac134fd..944ad9887f5c94ca0e63f9a6c901447634f871ce 100644 (file)
@@ -1,5 +1,12 @@
 #include "cache.h"
 #include "xdiff-interface.h"
+#include "strbuf.h"
+
+struct xdiff_emit_state {
+       xdiff_emit_consume_fn consume;
+       void *consume_callback_data;
+       struct strbuf remainder;
+};
 
 static int parse_num(char **cp_p, int *num_p)
 {
@@ -55,13 +62,13 @@ static void consume_one(void *priv_, char *s, unsigned long size)
                unsigned long this_size;
                ep = memchr(s, '\n', size);
                this_size = (ep == NULL) ? size : (ep - s + 1);
-               priv->consume(priv, s, this_size);
+               priv->consume(priv->consume_callback_data, s, this_size);
                size -= this_size;
                s += this_size;
        }
 }
 
-int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+static int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
 {
        struct xdiff_emit_state *priv = priv_;
        int i;
@@ -69,36 +76,22 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
        for (i = 0; i < nbuf; i++) {
                if (mb[i].ptr[mb[i].size-1] != '\n') {
                        /* Incomplete line */
-                       priv->remainder = xrealloc(priv->remainder,
-                                                  priv->remainder_size +
-                                                  mb[i].size);
-                       memcpy(priv->remainder + priv->remainder_size,
-                              mb[i].ptr, mb[i].size);
-                       priv->remainder_size += mb[i].size;
+                       strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
                        continue;
                }
 
                /* we have a complete line */
-               if (!priv->remainder) {
+               if (!priv->remainder.len) {
                        consume_one(priv, mb[i].ptr, mb[i].size);
                        continue;
                }
-               priv->remainder = xrealloc(priv->remainder,
-                                          priv->remainder_size +
-                                          mb[i].size);
-               memcpy(priv->remainder + priv->remainder_size,
-                      mb[i].ptr, mb[i].size);
-               consume_one(priv, priv->remainder,
-                           priv->remainder_size + mb[i].size);
-               free(priv->remainder);
-               priv->remainder = NULL;
-               priv->remainder_size = 0;
+               strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
+               consume_one(priv, priv->remainder.buf, priv->remainder.len);
+               strbuf_reset(&priv->remainder);
        }
-       if (priv->remainder) {
-               consume_one(priv, priv->remainder, priv->remainder_size);
-               free(priv->remainder);
-               priv->remainder = NULL;
-               priv->remainder_size = 0;
+       if (priv->remainder.len) {
+               consume_one(priv, priv->remainder.buf, priv->remainder.len);
+               strbuf_reset(&priv->remainder);
        }
        return 0;
 }
@@ -141,6 +134,25 @@ int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t co
        return xdl_diff(&a, &b, xpp, xecfg, xecb);
 }
 
+int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
+                 xdiff_emit_consume_fn fn, void *consume_callback_data,
+                 xpparam_t const *xpp,
+                 xdemitconf_t const *xecfg, xdemitcb_t *xecb)
+{
+       int ret;
+       struct xdiff_emit_state state;
+
+       memset(&state, 0, sizeof(state));
+       state.consume = fn;
+       state.consume_callback_data = consume_callback_data;
+       xecb->outf = xdiff_outf;
+       xecb->priv = &state;
+       strbuf_init(&state.remainder, 0);
+       ret = xdi_diff(mf1, mf2, xpp, xecfg, xecb);
+       strbuf_release(&state.remainder);
+       return ret;
+}
+
 int read_mmfile(mmfile_t *ptr, const char *filename)
 {
        struct stat st;
index f7f791d96b9a34ef0f08db4b007c5309b9adc3d6..558492ba38c50351a98f80c16e83a058ace849ab 100644 (file)
@@ -3,18 +3,13 @@
 
 #include "xdiff/xdiff.h"
 
-struct xdiff_emit_state;
-
 typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
 
-struct xdiff_emit_state {
-       xdiff_emit_consume_fn consume;
-       char *remainder;
-       unsigned long remainder_size;
-};
-
 int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
-int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
+int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
+                 xdiff_emit_consume_fn fn, void *consume_callback_data,
+                 xpparam_t const *xpp,
+                 xdemitconf_t const *xecfg, xdemitcb_t *xecb);
 int parse_hunk_header(char *line, int len,
                      int *ob, int *on,
                      int *nb, int *nn);