Code

Merge branch 'tr/filter-branch'
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Sep 2008 00:47:13 +0000 (17:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Sep 2008 00:47:13 +0000 (17:47 -0700)
* tr/filter-branch:
  revision --simplify-merges: make it a no-op without pathspec
  revision --simplify-merges: do not leave commits unprocessed
  revision --simplify-merges: use decoration instead of commit->util field
  Documentation: rev-list-options: move --simplify-merges documentation
  filter-branch: use --simplify-merges
  filter-branch: fix ref rewriting with --subdirectory-filter
  filter-branch: Extend test to show rewriting bug
  Topo-sort before --simplify-merges
  revision traversal: show full history with merge simplification
  revision.c: whitespace fix

206 files changed:
.gitignore
Documentation/RelNotes-1.6.0.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.0.txt
Documentation/RelNotes-1.6.1.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-am.txt
Documentation/git-checkout.txt
Documentation/git-commit.txt
Documentation/git-count-objects.txt
Documentation/git-cvsserver.txt
Documentation/git-daemon.txt
Documentation/git-diff-tree.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-hash-object.txt
Documentation/git-help.txt
Documentation/git-imap-send.txt
Documentation/git-merge-base.txt
Documentation/git-merge.txt
Documentation/git-name-rev.txt
Documentation/git-revert.txt
Documentation/git-send-email.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitk.txt
Documentation/gitmodules.txt
Documentation/gittutorial.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
GIT-VERSION-GEN
Makefile
RelNotes
archive.c
builtin-add.c
builtin-apply.c
builtin-blame.c
builtin-checkout.c
builtin-clone.c
builtin-commit-tree.c
builtin-commit.c
builtin-count-objects.c
builtin-diff-tree.c
builtin-fast-export.c
builtin-fetch-pack.c
builtin-for-each-ref.c
builtin-help.c [new file with mode: 0644]
builtin-log.c
builtin-mailinfo.c
builtin-merge-base.c
builtin-merge-recursive.c
builtin-merge.c
builtin-pack-objects.c
builtin-push.c
builtin-reflog.c
builtin-rev-list.c
builtin-revert.c
builtin-update-index.c
builtin-verify-pack.c
cache.h
combine-diff.c
commit.h
compat/mingw.c
compat/mingw.h
compat/snprintf.c
config.mak.in
contrib/completion/git-completion.bash
contrib/fast-import/git-p4
contrib/fast-import/git-p4.txt
csum-file.c
ctype.c
daemon.c
decorate.c
decorate.h
diff.c
dir.c
fast-import.c
git-am.sh
git-compat-util.h
git-filter-branch.sh
git-gui/GIT-VERSION-GEN
git-merge-octopus.sh
git-rebase--interactive.sh
git-rebase.sh
git-stash.sh
git-submodule.sh
git-svn.perl
gitk-git/gitk
gitweb/gitweb.css
gitweb/gitweb.perl
hash-object.c
help.c
help.h [new file with mode: 0644]
imap-send.c
index-pack.c
log-tree.c
pack-revindex.c
pack-revindex.h
pack-write.c
pack.h
pager.c
perl/Git.pm
pretty.c
read-cache.c
receive-pack.c
remote.c
remote.h
revision.c
revision.h
run-command.c
run-command.h
sha1_file.c
sha1_name.c
shell.c
t/.gitignore
t/Makefile
t/lib-httpd.sh
t/t0022-crlf-rename.sh
t/t0023-crlf-am.sh
t/t0055-beyond-symlinks.sh [new file with mode: 0755]
t/t1000-read-tree-m-3way.sh
t/t1002-read-tree-m-u-2way.sh
t/t1007-hash-object.sh
t/t3030-merge-recursive.sh
t/t3300-funny-names.sh
t/t3404-rebase-interactive.sh
t/t3504-cherry-pick-rerere.sh [new file with mode: 0755]
t/t3700-add.sh
t/t3900-i18n-commit.sh
t/t3901-i18n-patch.sh
t/t3902-quoted.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/t4016-diff-quote.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/t4104-apply-boundary.sh
t/t4202-log.sh
t/t5100-mailinfo.sh
t/t5100/info-from.expect [new file with mode: 0644]
t/t5100/info-from.in [new file with mode: 0644]
t/t5100/sample.mbox
t/t5300-pack-object.sh
t/t5304-prune.sh
t/t5306-pack-nobase.sh [new file with mode: 0755]
t/t5500-fetch-pack.sh
t/t5505-remote.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/t6006-rev-list-format.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/t6300-for-each-ref.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/t7605-merge-resolve.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
test-parse-options.c
unpack-trees.c
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.0.1.txt b/Documentation/RelNotes-1.6.0.1.txt
new file mode 100644 (file)
index 0000000..49d7a1c
--- /dev/null
@@ -0,0 +1,36 @@
+GIT v1.6.0.1 Release Notes
+==========================
+
+Fixes since v1.6.0
+------------------
+
+* "git diff --cc" did not honor content mangling specified by
+  gitattributes and core.autocrlf when reading from the work tree.
+
+* "git diff --check" incorrectly detected new trailing blank lines when
+  whitespace check was in effect.
+
+* "git for-each-ref" tried to dereference NULL when asked for '%(body)" on
+  a tag with a single incomplete line as its payload.
+
+* "git format-patch" peeked before the beginning of a string when
+  "format.headers" variable is empty (a misconfiguration).
+
+* "git help help" did not work correctly.
+
+* "git mailinfo" (hence "git am") was unhappy when MIME multipart message
+  contained garbage after the finishing boundary.
+
+* "git mailinfo" also was unhappy when the "From: " line only had a bare
+  e-mail address.
+
+* "git merge" did not refresh the index correctly when a merge resulted in
+  a fast-forward.
+
+* "git merge" did not resolve a truly trivial merges that can be done
+  without content level merges.
+
+* "git svn dcommit" to a repository with URL that has embedded usernames
+  did not work correctly.
+
+Contains other various documentation fixes.
index e3c0c1d88d9a2e9bf7305462a3fca09470c0a60d..de7ef166b692fd0db72db3e7f9e3a4d1b402da54 100644 (file)
@@ -178,7 +178,7 @@ Updates since v1.5.6
 * git-diff --check now checks leftover merge conflict markers.
 
 * "git-diff -p" learned to grab a better hunk header lines in
-  Pascal/Delphi and Ruby source files, and also pays attention to
+  BibTex, Pascal/Delphi, and Ruby files and also pays attention to
   chapter and part boundary in TeX documents.
 
 * When remote side used to have branch 'foo' and git-fetch finds that now
@@ -245,15 +245,14 @@ Fixes since v1.5.6
 All of the fixes in v1.5.6 maintenance series are included in
 this release, unless otherwise noted.
 
-* git-clone ignored its -u option; the fix needs to be backported to
-  'maint';
+ * git-clone ignored its -u option; the fix needs to be backported to
+   'maint';
 
-* git-mv used to lose the distinction between changes that are staged
-  and that are only in the working tree, by staging both in the index
-  after moving such a path.
+ * git-mv used to lose the distinction between changes that are staged
+   and that are only in the working tree, by staging both in the index
+   after moving such a path.
+
+ * "git-rebase -i -p" rewrote the parents to wrong ones when amending
+   (either edit or squash) was involved, and did not work correctly
+   when fast forwarding.
 
----
-exec >/var/tmp/1
-O=v1.6.0-rc2-21-g0bb3a0b
-echo O=$(git describe refs/heads/master)
-git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
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 b8ec01c923a8bdb8614f5c5700a7fda603365e8b..ed3285f89927e1d988527ae5b67a5bea357d30e0 100644 (file)
@@ -358,8 +358,13 @@ core.editor::
        `EDITOR` environment variables and then finally `vi`.
 
 core.pager::
-       The command that git will use to paginate output.  Can be overridden
-       with the `GIT_PAGER` environment variable.
+       The command that git will use to paginate output.  Can
+       be overridden with the `GIT_PAGER` environment
+       variable.  Note that git sets the `LESS` environment
+       variable to `FRSX` if it is unset when it runs the
+       pager.  One can change these settings by setting the
+       `LESS` variable to some other value or by giving the
+       `core.pager` option a value such as "`less -+FRSX`".
 
 core.whitespace::
        A comma separated list of common whitespace problems to
@@ -567,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
@@ -688,7 +697,7 @@ gitcvs.logfile::
        Path to a log file where the CVS server interface well... logs
        various stuff. See linkgit:git-cvsserver[1].
 
-gitcvs.usecrlfattr
+gitcvs.usecrlfattr::
        If true, the server will look up the `crlf` attribute for
        files to determine the '-k' modes to use. If `crlf` is set,
        the '-k' mode will be left blank, so cvs clients will
@@ -978,6 +987,13 @@ pack.packSizeLimit::
        can be overridden by the `\--max-pack-size` option of
        linkgit:git-repack[1].
 
+pager.<cmd>::
+       Allows turning on or off pagination of the output of a
+       particular git subcommand when writing to a tty.  If
+       `\--paginate` or `\--no-pager` is specified on the command line,
+       it takes precedence over this option.  To disable pagination for
+       all commands, set `core.pager` or 'GIT_PAGER' to "`cat`".
+
 pull.octopus::
        The default merge strategy to use when pulling multiple branches
        at once.
index cba90fd27c6a1baaca884328e96adc8a6da8fc36..6e268326da9e34882313b7b3d094a617c3966d29 100644 (file)
@@ -59,12 +59,11 @@ endif::git-format-patch[]
        lines.
 
 --dirstat[=limit]::
-       Output only the sub-directories that are impacted by a diff,
-       and to what degree they are impacted.  You can override the
-       default cut-off in percent (3) by "--dirstat=limit".  If you
-       want to enable "cumulative" directory statistics, you can use
-       the "--cumulative" flag, which adds up percentages recursively
-       even when they have been already reported for a sub-directory.
+       Output the distribution of relative amount of changes (number of lines added or
+       removed) for each sub-directory. Directories with changes below
+       a cut-off percent (3% by default) are not shown. The cut-off percent
+       can be set with "--dirstat=limit". Changes in a child directory is not
+       counted for the parent directory, unless "--cumulative" is used.
 
 --summary::
        Output a condensed summary of extended header information
@@ -107,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 c45c53ec2404725394563a9fba40f31cd314adb2..b9c6fac7483dbefba0afb60a76ac0362aa390a6d 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
-         [--3way] [--interactive] [--binary]
+        [--3way] [--interactive]
          [--whitespace=<option>] [-C<n>] [-p<n>]
         [<mbox> | <Maildir>...]
 'git am' (--skip | --resolved | --abort)
@@ -59,11 +59,6 @@ default.   You could use `--no-utf8` to override this.
        it is supposed to apply to, and we have those blobs
        available locally.
 
--b::
---binary::
-       Pass `--allow-binary-replacement` flag to 'git-apply'
-       (see linkgit:git-apply[1]).
-
 --whitespace=<option>::
        This flag is passed to the 'git-apply' (see linkgit:git-apply[1])
        program that applies
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 0e25bb862704eee4a22fe5349c04823d14ea9cba..eb05b0f49b0c513581150415d1a4e679d7bfbea6 100644 (file)
@@ -75,8 +75,10 @@ OPTIONS
        read the message from the standard input.
 
 --author=<author>::
-       Override the author name used in the commit.  Use
-       `A U Thor <author@example.com>` format.
+       Override the author name used in the commit.  You can use the
+       standard `A U Thor <author@example.com>` format.  Otherwise,
+       an existing commit that matches the given string and its author
+       name is used.
 
 -m <msg>::
 --message=<msg>::
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 c2d3c90d27084e7de7e0f7c37b40f130f6960244..785779e22122156bdc8c58a94d36edb66a8ee266 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 SSH:
 
 [verse]
-export CVS_SERVER=git-cvsserver
+export CVS_SERVER="git cvsserver"
 'cvs' -d :ext:user@server/path/repo.git co <HEAD_name>
 
 pserver (/etc/inetd.conf):
@@ -109,7 +109,7 @@ Note: Newer CVS versions (>= 1.12.11) also support specifying
 CVS_SERVER directly in CVSROOT like
 
 ------
-cvs -d ":ext;CVS_SERVER=git-cvsserver:user@server/path/repo.git" co <HEAD_name>
+cvs -d ":ext;CVS_SERVER=git cvsserver:user@server/path/repo.git" co <HEAD_name>
 ------
 This has the advantage that it will be saved in your 'CVS/Root' files and
 you don't need to worry about always setting the correct environment
@@ -158,7 +158,7 @@ allowing access over SSH.
 --
 ------
      export CVSROOT=:ext:user@server:/var/git/project.git
-     export CVS_SERVER=git-cvsserver
+     export CVS_SERVER="git cvsserver"
 ------
 --
 4. For SSH clients that will make commits, make sure their server-side
@@ -283,7 +283,7 @@ To get a checkout with the Eclipse CVS client:
 Protocol notes: If you are using anonymous access via pserver, just select that.
 Those using SSH access should choose the 'ext' protocol, and configure 'ext'
 access on the Preferences->Team->CVS->ExtConnection pane. Set CVS_SERVER to
-'git-cvsserver'. Note that password support is not good when using 'ext',
+"'git cvsserver'". Note that password support is not good when using 'ext',
 you will definitely want to have SSH keys setup.
 
 Alternatively, you can just use the non-standard extssh protocol that Eclipse
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 eae6c0e7bcad5708442d10a7bc73eac3ec90bcbd..ebd7c5fbb34576fd2af98b00d9045340ff77ee2b 100644 (file)
@@ -16,7 +16,7 @@ DESCRIPTION
 
 Iterate over all refs that match `<pattern>` and show them
 according to the given `<format>`, after sorting them according
-to the given set of `<key>`.  If `<max>` is given, stop after
+to the given set of `<key>`.  If `<count>` is given, stop after
 showing that many refs.  The interpolated values in `<format>`
 can optionally be quoted as string literals in the specified
 host language allowing their direct evaluation in that language.
index 010d9e432231f41a179023df2e85610583b572cf..adb4ea7b1b6c70fb6fc2486487ef34ed280ec015 100644 (file)
@@ -147,9 +147,9 @@ include::diff-options.txt[]
        to any configured headers, and may be used multiple times.
 
 --cover-letter::
-       Generate a cover letter template.  You still have to fill in
-       a description, but the shortlog and the diffstat will be
-       generated for you.
+       In addition to the patches, generate a cover letter file
+       containing the shortlog and the overall diffstat.  You can
+       fill in a description in the file before sending it out.
 
 --suffix=.<sfx>::
        Instead of using `.patch` as the suffix for generated
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 f414583fc48e85e4785fbf5f9431bb81a96ccd9d..d9b9c34b3a60f09bea4f9e53b5127f33f356e042 100644 (file)
@@ -112,7 +112,9 @@ For example, this configuration:
 will try to use konqueror first. But this may fail (for example if
 DISPLAY is not set) and in that case emacs' woman mode will be tried.
 
-If everything fails the 'man' program will be tried anyway.
+If everything fails, or if no viewer is configured, the viewer specified
+in the GIT_MAN_VIEWER environment variable will be tried.  If that
+fails too, the 'man' program will be tried anyway.
 
 man.<tool>.path
 ~~~~~~~~~~~~~~~
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 abd2237e51dfd86bcea98320edfff8922bc04eb6..7ca8a7b48cea191b58db93581f02fa4ff265acdc 100644 (file)
@@ -59,7 +59,7 @@ Enter 'git-name-rev':
 
 ------------
 % git name-rev 33db5f4d9027a10e477ccf054b2c1ab94f74c85a
-33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99^0~940
+33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99~940
 ------------
 
 Now you are wiser, because you know that it happened 940 revisions before v0.99.
index 98cfa3c0d0f27e0cb603f07c76285f85ef07a771..caa07298a6b6c0ea2fe2257cb2c62fcae2e62d46 100644 (file)
@@ -15,6 +15,15 @@ Given one existing commit, revert the change the patch introduces, and record a
 new commit that records it.  This requires your working tree to be clean (no
 modifications from the HEAD commit).
 
+Note: 'git revert' is used to record a new commit to reverse the
+effect of an earlier commit (often a faulty one).  If you want to
+throw away all uncommitted changes in your working directory, you
+should see linkgit:git-reset[1], particularly the '--hard' option.  If
+you want to extract specific files as they were in another commit, you
+should see linkgit:git-checkout[1], specifically the 'git checkout
+<commit> -- <filename>' syntax.  Take care with these alternatives as
+both will discard uncommitted changes in your working directory.
+
 OPTIONS
 -------
 <commit>::
index e2437f30ca1314dc00a55f72c4e2a325cc75c7b6..3c3e1b0e77abe171ac7531b8098ea2af7d6809dd 100644 (file)
@@ -179,6 +179,9 @@ user is prompted for a password while the input is masked for privacy.
        This is useful if your default address is not the address that is
        subscribed to a list. If you use the sendmail binary, you must have
        suitable privileges for the -f parameter.
+       Default is the value of the 'sendemail.envelopesender' configuration
+       variable; if that is unspecified, choosing the envelope sender is left
+       to your MTA.
 
 --to::
        Specify the primary recipient of the emails generated.
index 7d50d74cc9a945f0dd82b0c26509bf0392eff837..051f94d26f9f057cbd4db0d6886ca1a4d33c912c 100644 (file)
@@ -8,11 +8,13 @@ git-stash - Stash the changes in a dirty working directory away
 SYNOPSIS
 --------
 [verse]
-'git stash' list
-'git stash' (show | apply | drop | pop ) [<stash>]
+'git stash' list [<options>]
+'git stash' (show | drop | pop ) [<stash>]
+'git stash' apply [--index] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [save [<message>]]
+'git stash' [save [--keep-index] [<message>]]
 'git stash' clear
+'git stash' create
 
 DESCRIPTION
 -----------
@@ -116,6 +118,11 @@ pop [<stash>]::
        of the current working tree state. When no `<stash>` is given,
        `stash@\{0}` is assumed. See also `apply`.
 
+create::
+
+       Create a stash (which is a regular commit object) and return its
+       object name, without storing it anywhere in the ref namespace.
+
 
 DISCUSSION
 ----------
@@ -152,7 +159,7 @@ perform a pull, and then unstash, like this:
 +
 ----------------------------------------------------------------
 $ git pull
-...
+ ...
 file foobar not up to date, cannot merge.
 $ git stash
 $ git pull
@@ -167,7 +174,7 @@ make a commit to a temporary branch to store your changes away, and
 return to your original branch to make the emergency fix, like this:
 +
 ----------------------------------------------------------------
-... hack hack hack ...
+... hack hack hack ...
 $ git checkout -b my_wip
 $ git commit -a -m "WIP"
 $ git checkout master
@@ -175,18 +182,18 @@ $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
 $ git checkout my_wip
 $ git reset --soft HEAD^
-... continue hacking ...
+... continue hacking ...
 ----------------------------------------------------------------
 +
 You can use 'git-stash' to simplify the above, like this:
 +
 ----------------------------------------------------------------
-... hack hack hack ...
+... hack hack hack ...
 $ git stash
 $ edit emergency fix
 $ git commit -a -m "Fix in a hurry"
 $ git stash apply
-... continue hacking ...
+... continue hacking ...
 ----------------------------------------------------------------
 
 Testing partial commits::
@@ -196,13 +203,13 @@ more commits out of the changes in the work tree, and you want to test
 each change before committing:
 +
 ----------------------------------------------------------------
-... hack hack hack ...
+... hack hack hack ...
 $ git add --patch foo            # add just first part to the index
 $ git stash save --keep-index    # save all other changes to the stash
 $ edit/build/test first part
-$ git commit foo -m 'First part' # commit fully tested change
+$ git commit -m 'First part'     # commit fully tested change
 $ git stash pop                  # prepare to work on all other changes
-... repeat above five steps until one commit remains ...
+... repeat above five steps until one commit remains ...
 $ edit/build/test remaining parts
 $ git commit foo -m 'Remaining parts'
 ----------------------------------------------------------------
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 b1cb97236982dd5fe718765136aa4f1a4fdb994f..e178fb581323adaa5f490fb67732f98147ec460f 100644 (file)
@@ -43,6 +43,12 @@ 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.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]
 
 * release notes for
@@ -492,7 +498,8 @@ other
 'GIT_PAGER'::
        This environment variable overrides `$PAGER`. If it is set
        to an empty string or to the value "cat", git will not launch
-       a pager.
+       a pager.  See also the `core.pager` option in
+       linkgit:git-config[1].
 
 'GIT_SSH'::
        If this environment variable is set then 'git-fetch'
index db16b0ca5b8146361a379de894a1394e0950fd38..5fb5007413666d0b3d0aaa4c553c1fd5a67e60d9 100644 (file)
@@ -7,7 +7,7 @@ gitattributes - defining attributes per path
 
 SYNOPSIS
 --------
-$GIT_DIR/info/attributes, gitattributes
+$GIT_DIR/info/attributes, .gitattributes
 
 
 DESCRIPTION
@@ -105,9 +105,8 @@ Set::
 
 Unset::
 
-       Unsetting the `crlf` attribute on a path is meant to
-       mark the path as a "binary" file.  The path never goes
-       through line endings conversion upon checkin/checkout.
+       Unsetting the `crlf` attribute on a path tells git not to
+       attempt any end-of-line conversion upon checkin or checkout.
 
 Unspecified::
 
@@ -316,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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -482,6 +485,41 @@ in the file.  E.g. the string `$Format:%H$` will be replaced by the
 commit hash.
 
 
+USING ATTRIBUTE MACROS
+----------------------
+
+You do not want any end-of-line conversions applied to, nor textual diffs
+produced for, any binary file you track.  You would need to specify e.g.
+
+------------
+*.jpg -crlf -diff
+------------
+
+but that may become cumbersome, when you have many attributes.  Using
+attribute macros, you can specify groups of attributes set or unset at
+the same time.  The system knows a built-in attribute macro, `binary`:
+
+------------
+*.jpg binary
+------------
+
+which is equivalent to the above.  Note that the attribute macros can only
+be "Set" (see the above example that sets "binary" macro as if it were an
+ordinary attribute --- setting it in turn unsets "crlf" and "diff").
+
+
+DEFINING ATTRIBUTE MACROS
+-------------------------
+
+Custom attribute macros can be defined only in the `.gitattributes` file
+at the toplevel (i.e. not in any subdirectory).  The built-in attribute
+macro "binary" is equivalent to:
+
+------------
+[attr]binary -diff -crlf
+------------
+
+
 EXAMPLE
 -------
 
index 6e827cd11c6d464e1369bf1ca23af0dd53b9be32..ae29a00d591289b0b2534402599fa7a6fe1e3266 100644 (file)
@@ -49,6 +49,13 @@ frequently used options.
        the history between two branches (i.e. the HEAD and the MERGE_HEAD)
        that modify the conflicted files.
 
+--argscmd=<command>::
+       Command to be run each time gitk has to determine the list of
+       <revs> to show.  The command is expected to print on its standard
+       output a list of additional revs to be shown, one per line.
+       Use this instead of explicitly specifying <revs> if the set of
+       commits to show may vary between refreshes.
+
 <revs>::
 
        Limit the revisions to show. This can be either a single revision
index f8d122a8b90ca7cb4920768ca23fd9a27574ffdf..d1a17e2625890245341a2099cc2b058e63564da2 100644 (file)
@@ -7,7 +7,7 @@ gitmodules - defining submodule properties
 
 SYNOPSIS
 --------
-gitmodules
+$GIT_WORK_DIR/.gitmodules
 
 
 DESCRIPTION
index 48d1454a90cf9453e5e3c9fa01b3dbc369a58f1f..384972cb9bb4a04c55bd8c4796bcd3408e44d5e9 100644 (file)
@@ -321,10 +321,37 @@ pulling, like this:
 
 ------------------------------------------------
 alice$ git fetch /home/bob/myrepo master
-alice$ git log -p ..FETCH_HEAD
+alice$ git log -p HEAD..FETCH_HEAD
 ------------------------------------------------
 
 This operation is safe even if Alice has uncommitted local changes.
+The range notation HEAD..FETCH_HEAD" means "show everything that is reachable
+from the FETCH_HEAD but exclude anything that is reachable from HEAD.
+Alice already knows everything that leads to her current state (HEAD),
+and reviewing what Bob has in his state (FETCH_HEAD) that she has not
+seen with this command
+
+If Alice wants to visualize what Bob did since their histories forked
+she can issue the following command:
+
+------------------------------------------------
+$ gitk HEAD..FETCH_HEAD
+------------------------------------------------
+
+This uses the same two-dot range notation we saw earlier with 'git log'.
+
+Alice may want to view what both of them did since they forked.
+She can use three-dot form instead of the two-dot form:
+
+------------------------------------------------
+$ gitk HEAD...FETCH_HEAD
+------------------------------------------------
+
+This means "show everything that is reachable from either one, but
+exclude anything that is reachable from both of them".
+
+Please note that these range notation can be used with both gitk
+and "git log".
 
 After inspecting what Bob did, if there is nothing urgent, Alice may
 decide to continue working without pulling from Bob.  If Bob's history
index c11d4957714db202a012209e2437b9e050a28ae0..388d4925e6bc4bacb708f75437e9aaa216fcb9cc 100644 (file)
@@ -103,7 +103,7 @@ The placeholders are:
 - '%an': author name
 - '%aN': author name (respecting .mailmap)
 - '%ae': author email
-- '%ad': author date
+- '%ad': author date (format respects --date= option)
 - '%aD': author date, RFC2822 style
 - '%ar': author date, relative
 - '%at': author date, UNIX timestamp
index 15752b92a59a87d5bbd197e9e00b94b2f19e0269..0ce916a1887b0846bfc5a6e2233242601e0dde79 100644 (file)
@@ -96,6 +96,7 @@ you would get an output like this:
 This implies the '--topo-order' option by default, but the
 '--date-order' option may also be specified.
 
+ifndef::git-rev-list[]
 Diff Formatting
 ~~~~~~~~~~~~~~~
 
@@ -125,6 +126,7 @@ options may be given. See linkgit:git-diff-files[1] for more options.
 -t::
 
        Show the tree objects in the diff output. This implies '-r'.
+endif::git-rev-list[]
 
 Commit Limiting
 ~~~~~~~~~~~~~~~
index e1290d8901bb63bf3c302c1f72a10559ea45f906..156dc137339c4184727d1916e49a5bdf8c674d68 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.6.GIT
+DEF_VER=v1.6.0.GIT
 
 LF='
 '
index 90c5a131be24463183a1f21cf3bf337ef00dd67c..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.
@@ -333,7 +336,6 @@ endif
 export PERL_PATH
 
 LIB_FILE=libgit.a
-COMPAT_LIB = compat/lib.a
 XDIFF_LIB=xdiff/lib.a
 
 LIB_H += archive.h
@@ -355,10 +357,12 @@ 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
 LIB_H += mailmap.h
+LIB_H += merge-recursive.h
 LIB_H += object.h
 LIB_H += pack.h
 LIB_H += pack-refs.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
@@ -727,6 +737,7 @@ ifeq ($(uname_S),HP-UX)
        NO_UNSETENV = YesPlease
        NO_HSTRERROR = YesPlease
        NO_SYS_SELECT_H = YesPlease
+       SNPRINTF_RETURNS_BOGUS = YesPlease
 endif
 ifneq (,$(findstring MINGW,$(uname_S)))
        NO_MMAP = YesPlease
@@ -749,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\"
@@ -863,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
@@ -1064,7 +1079,7 @@ SHELL = $(SHELL_PATH)
 
 all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) GIT-BUILD-OPTIONS
 ifneq (,$X)
-       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), $(RM) '$p';)
+       $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$p' -ef '$p$X' || $(RM) '$p';)
 endif
 
 all::
@@ -1091,14 +1106,17 @@ 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)"' \
                '-DGIT_INFO_PATH="$(infodir_SQ)"' $<
 
 $(BUILT_INS): git$X
-       $(QUIET_BUILT_IN)$(RM) $@ && ln git$X $@
+       $(QUIET_BUILT_IN)$(RM) $@ && \
+       ln git$X $@ 2>/dev/null || \
+       ln -s git$X $@ 2>/dev/null || \
+       cp git$X $@
 
 common-cmds.h: ./generate-cmdlist.sh command-list.txt
 
@@ -1215,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
 
@@ -1223,12 +1243,6 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-$(COMPAT_LIB): $(COMPAT_OBJS)
-       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(COMPAT_OBJS)
-
-git-shell$X: abspath.o ctype.o exec_cmd.o quote.o strbuf.o usage.o wrapper.o shell.o $(COMPAT_LIB)
-       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(COMPAT_LIB)
-
 $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
 $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 builtin-revert.o wt-status.o: wt-status.h
@@ -1351,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
@@ -1363,16 +1377,13 @@ ifneq (,$X)
 endif
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
-       if test "z$$bindir" != "z$$execdir"; \
-       then \
-               ln -f "$$bindir/git$X" "$$execdir/git$X" || \
-               cp "$$bindir/git$X" "$$execdir/git$X"; \
-       fi && \
-       { $(foreach p,$(BUILT_INS), $(RM) "$$execdir/$p" && ln "$$execdir/git$X" "$$execdir/$p" ;) } && \
-       if test "z$$bindir" != "z$$execdir"; \
-       then \
-               $(RM) "$$execdir/git$X"; \
-       fi && \
+       { $(RM) "$$execdir/git-add$X" && \
+               ln git-add$X "$$execdir/git-add$X" 2>/dev/null || \
+               cp git-add$X "$$execdir/git-add$X"; } && \
+       { $(foreach p,$(filter-out git-add$X,$(BUILT_INS)), $(RM) "$$execdir/$p" && \
+               ln "$$execdir/git-add$X" "$$execdir/$p" 2>/dev/null || \
+               ln -s "git-add$X" "$$execdir/$p" 2>/dev/null || \
+               cp "$$execdir/git-add$X" "$$execdir/$p" || exit;) } && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
 install-doc:
@@ -1441,7 +1452,7 @@ distclean: clean
 
 clean:
        $(RM) *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
-               $(LIB_FILE) $(XDIFF_LIB) $(COMPAT_LIB)
+               $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
        $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
index b9a53c3416991b66e1d35c2bbf663b48340b0041..3d420845b117b3f3eb82d1f0948a61c7989d8af9 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.0.txt
\ No newline at end of file
+Documentation/RelNotes-1.6.1.txt
\ No newline at end of file
index 5b40e261f10e42b9f9ee9b4dbfe231765156bf64..e2280df56723809c5af1d566f0721e2639fff10c 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -48,7 +48,7 @@ static void format_subst(const struct commit *commit,
                strbuf_add(&fmt, b + 8, c - b - 8);
 
                strbuf_add(buf, src, b - src);
-               format_commit_message(commit, fmt.buf, buf);
+               format_commit_message(commit, fmt.buf, buf, DATE_NORMAL);
                len -= c + 1 - src;
                src  = c + 1;
        }
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 2216a0bf7cd53adc31346f66a3b9786a1d688bad..47261e10fb3901f3b99861020762e63856ee2d60 100644 (file)
@@ -1996,6 +1996,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
        /*
         * A hunk to change lines at the beginning would begin with
         * @@ -1,L +N,M @@
+        * but we need to be careful.  -U0 that inserts before the second
+        * line also has this pattern.
         *
         * And a hunk to add to an empty file would begin with
         * @@ -0,0 +N,M @@
@@ -2003,7 +2005,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
         * In other words, a hunk that is (frag->oldpos <= 1) with or
         * without leading context must match at the beginning.
         */
-       match_beginning = frag->oldpos <= 1;
+       match_beginning = (!frag->oldpos ||
+                          (frag->oldpos == 1 && !unidiff_zero));
 
        /*
         * A hunk without trailing lines must match at the end.
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 c0e3086437ced05d1b81d4098e3a4c7c3031b0ee..f44eceab3a31ba38baaa5cbec366e69a989183d2 100644 (file)
@@ -387,7 +387,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        path = get_repo_path(repo_name, &is_bundle);
        if (path)
-               repo = path;
+               repo = xstrdup(make_nonrelative_path(repo_name));
        else if (!strchr(repo_name, ':'))
                repo = xstrdup(make_absolute_path(repo_name));
        else
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 649c8beb3e716dd5797787ced61d2fee23b7140f..8165bb3d31c6cdd92c83663c5dd081e564dcfc1e 100644 (file)
@@ -320,7 +320,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
                die("unable to write new_index file");
 
        fd = hold_lock_file_for_update(&false_lock,
-                                      git_path("next-index-%d", getpid()), 1);
+                                      git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), 1);
 
        create_base_index();
        add_remove_files(&partial);
@@ -710,6 +710,31 @@ static int message_is_empty(struct strbuf *sb, int start)
        return 1;
 }
 
+static const char *find_author_by_nickname(const char *name)
+{
+       struct rev_info revs;
+       struct commit *commit;
+       struct strbuf buf = STRBUF_INIT;
+       const char *av[20];
+       int ac = 0;
+
+       init_revisions(&revs, NULL);
+       strbuf_addf(&buf, "--author=%s", name);
+       av[++ac] = "--all";
+       av[++ac] = "-i";
+       av[++ac] = buf.buf;
+       av[++ac] = NULL;
+       setup_revisions(ac, av, &revs, NULL);
+       prepare_revision_walk(&revs);
+       commit = get_revision(&revs);
+       if (commit) {
+               strbuf_release(&buf);
+               format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL);
+               return strbuf_detach(&buf, NULL);
+       }
+       die("No existing author found with '%s'", name);
+}
+
 static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[],
                                      const char *prefix)
@@ -720,6 +745,9 @@ static int parse_and_validate_options(int argc, const char *argv[],
        logfile = parse_options_fix_filename(prefix, logfile);
        template_file = parse_options_fix_filename(prefix, template_file);
 
+       if (force_author && !strchr(force_author, '>'))
+               force_author = find_author_by_nickname(force_author);
+
        if (logfile || message.len || use_message)
                use_editor = 0;
        if (edit_flag)
@@ -882,7 +910,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
 
        if (!log_tree_commit(&rev, commit)) {
                struct strbuf buf = STRBUF_INIT;
-               format_commit_message(commit, "%h: %s", &buf);
+               format_commit_message(commit, "%h: %s", &buf, DATE_NORMAL);
                printf("%s\n", buf.buf);
                strbuf_release(&buf);
        }
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"
index 070971616dbb12d005c5c9a1f82cc5b0c5391f62..7c93eb878d7da87b39959fb9599e4b7b6b81570b 100644 (file)
@@ -417,7 +417,8 @@ static void export_marks(char *file)
        for (i = 0; i < idnums.size; i++) {
                if (deco->base && deco->base->type == 1) {
                        mark = ptr_to_mark(deco->decoration);
-                       fprintf(f, ":%u %s\n", mark, sha1_to_hex(deco->base->sha1));
+                       fprintf(f, ":%"PRIu32" %s\n", mark,
+                               sha1_to_hex(deco->base->sha1));
                }
                deco++;
        }
index 273239af3be61736ee4ff484d628950c4de7311a..17a5a422c27e898fb68e3d55a9a5d7e714a40ff6 100644 (file)
@@ -540,7 +540,7 @@ static int get_pack(int xd[2], char **pack_lockfile)
                        *av++ = "--fix-thin";
                if (args.lock_pack || unpack_limit) {
                        int s = sprintf(keep_arg,
-                                       "--keep=fetch-pack %d on ", getpid());
+                                       "--keep=fetch-pack %"PRIuMAX " on ", (uintmax_t) getpid());
                        if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
                                strcpy(keep_arg + s, "localhost");
                        *av++ = keep_arg;
index 445039e19c75e4c9321f7ee64289ef8201a25c14..21e92bbcb577c0361df51bd81e6fb5ab546619ea 100644 (file)
@@ -459,8 +459,10 @@ static void find_subpos(const char *buf, unsigned long sz, const char **sub, con
                return;
        *sub = buf; /* first non-empty line */
        buf = strchr(buf, '\n');
-       if (!buf)
+       if (!buf) {
+               *body = "";
                return; /* no body */
+       }
        while (*buf == '\n')
                buf++; /* skip blank between subject and body */
        *body = buf;
@@ -650,7 +652,8 @@ static int grab_single_ref(const char *refname, const unsigned char *sha1, int f
                        if ((plen <= namelen) &&
                            !strncmp(refname, p, plen) &&
                            (refname[plen] == '\0' ||
-                            refname[plen] == '/'))
+                            refname[plen] == '/' ||
+                            p[plen-1] == '/'))
                                break;
                        if (!fnmatch(p, refname, FNM_PATHNAME))
                                break;
diff --git a/builtin-help.c b/builtin-help.c
new file mode 100644 (file)
index 0000000..64207cb
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * 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);
+       const char *fallback = getenv("GIT_MAN_VIEWER");
+
+       setup_man_path();
+       for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+       {
+               exec_viewer(viewer->name, page); /* will return when unable */
+       }
+       if (fallback)
+               exec_viewer(fallback, page);
+       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 f4975cf35f7f1555739f7657ee62ed983d18cb84..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)
@@ -461,7 +470,7 @@ static int extra_cc_alloc;
 static void add_header(const char *value)
 {
        int len = strlen(value);
-       while (value[len - 1] == '\n')
+       while (len && value[len - 1] == '\n')
                len--;
        if (!strncasecmp(value, "to: ", 4)) {
                ALLOC_GROW(extra_to, extra_to_nr + 1, extra_to_alloc);
@@ -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 3577382d7039784e8a0c5ef9ce3d765409abb2d8..e890f7a6d1ff7248aed4f03ebbcdfafd7e472dad 100644 (file)
@@ -107,7 +107,7 @@ static void handle_from(const struct strbuf *from)
        el = strcspn(at, " \n\t\r\v\f>");
        strbuf_reset(&email);
        strbuf_add(&email, at, el);
-       strbuf_remove(&f, at - f.buf, el + 1);
+       strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
 
        /* The remainder is name.  It could be "John Doe <john.doe@xz>"
         * or "john.doe@xz (John Doe)", but we have removed the
@@ -175,7 +175,7 @@ static void handle_content_type(struct strbuf *line)
                 message_type = TYPE_OTHER;
        if (slurp_attr(line->buf, "boundary=", boundary)) {
                strbuf_insert(boundary, 0, "--", 2);
-               if (content_top++ >= &content[MAX_BOUNDARIES]) {
+               if (++content_top > &content[MAX_BOUNDARIES]) {
                        fprintf(stderr, "Too many boundaries to handle\n");
                        exit(1);
                }
@@ -603,7 +603,7 @@ static void handle_filter(struct strbuf *line);
 static int find_boundary(void)
 {
        while (!strbuf_getline(&line, fin, '\n')) {
-               if (is_multipart_boundary(&line))
+               if (*content_top && is_multipart_boundary(&line))
                        return 1;
        }
        return 0;
@@ -626,7 +626,7 @@ again:
                /* technically won't happen as is_multipart_boundary()
                   will fail first.  But just in case..
                 */
-               if (content_top-- < content) {
+               if (--content_top < content) {
                        fprintf(stderr, "Detected mismatched boundaries, "
                                        "can't recover\n");
                        exit(1);
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 dde0c7ed33118ff8d0cc421e8a0366e342c6d011..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)
@@ -564,8 +598,7 @@ static int checkout_fast_forward(unsigned char *head, unsigned char *remote)
        struct dir_struct dir;
        struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
-       if (read_cache_unmerged())
-               die("you need to resolve your current index first");
+       refresh_cache(REFRESH_QUIET);
 
        fd = hold_locked_index(lock_file, 1);
 
@@ -650,13 +683,15 @@ static void add_strategies(const char *string, unsigned attr)
 static int merge_trivial(void)
 {
        unsigned char result_tree[20], result_commit[20];
-       struct commit_list parent;
+       struct commit_list *parent = xmalloc(sizeof(struct commit_list *));
 
        write_tree_trivial(result_tree);
        printf("Wonderful.\n");
-       parent.item = remoteheads->item;
-       parent.next = NULL;
-       commit_tree(merge_msg.buf, result_tree, &parent, result_commit);
+       parent->item = lookup_commit(head);
+       parent->next = xmalloc(sizeof(struct commit_list *));
+       parent->next->item = remoteheads->item;
+       parent->next->next = NULL;
+       commit_tree(merge_msg.buf, result_tree, parent, result_commit);
        finish(result_commit, "In-index merge");
        drop_save();
        return 0;
@@ -742,6 +777,7 @@ static int evaluate_result(void)
        int cnt = 0;
        struct rev_info rev;
 
+       discard_cache();
        if (read_cache() < 0)
                die("failed to read the cache");
 
@@ -775,7 +811,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        struct commit_list **remotes = &remoteheads;
 
        setup_work_tree();
-       if (unmerged_cache())
+       if (read_cache_unmerged())
                die("You are in the middle of a conflicted merge.");
 
        /*
@@ -832,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]);
@@ -936,7 +977,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        hex,
                        find_unique_abbrev(remoteheads->item->object.sha1,
                        DEFAULT_ABBREV));
-               refresh_cache(REFRESH_QUIET);
                strbuf_init(&msg, 0);
                strbuf_addstr(&msg, "Fast forward");
                if (have_message)
@@ -1073,6 +1113,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
 
                /* Automerge succeeded. */
+               discard_cache();
                write_tree_trivial(result_tree);
                automerge_was_ok = 1;
                break;
index 2dadec1630c266bbaf42e84810f7059ed5c43b1e..ba2cf00f5ec60bcfcefe120746422f086559a1ae 100644 (file)
@@ -410,25 +410,22 @@ static unsigned long write_object(struct sha1file *f,
        return hdrlen + datalen;
 }
 
-static off_t write_one(struct sha1file *f,
+static int write_one(struct sha1file *f,
                               struct object_entry *e,
-                              off_t offset)
+                              off_t *offset)
 {
        unsigned long size;
 
        /* offset is non zero if object is written already. */
        if (e->idx.offset || e->preferred_base)
-               return offset;
+               return 1;
 
        /* if we are deltified, write out base object first. */
-       if (e->delta) {
-               offset = write_one(f, e->delta, offset);
-               if (!offset)
-                       return 0;
-       }
+       if (e->delta && !write_one(f, e->delta, offset))
+               return 0;
 
-       e->idx.offset = offset;
-       size = write_object(f, e, offset);
+       e->idx.offset = *offset;
+       size = write_object(f, e, *offset);
        if (!size) {
                e->idx.offset = 0;
                return 0;
@@ -436,9 +433,10 @@ static off_t write_one(struct sha1file *f,
        written_list[nr_written++] = &e->idx;
 
        /* make sure off_t is sufficiently large not to wrap */
-       if (offset > offset + size)
+       if (*offset > *offset + size)
                die("pack too large for current definition of off_t");
-       return offset + size;
+       *offset += size;
+       return 1;
 }
 
 /* forward declaration for write_pack_file */
@@ -448,7 +446,7 @@ static void write_pack_file(void)
 {
        uint32_t i = 0, j;
        struct sha1file *f;
-       off_t offset, offset_one, last_obj_offset = 0;
+       off_t offset;
        struct pack_header hdr;
        uint32_t nr_remaining = nr_result;
        time_t last_mtime = 0;
@@ -480,11 +478,8 @@ static void write_pack_file(void)
                offset = sizeof(hdr);
                nr_written = 0;
                for (; i < nr_objects; i++) {
-                       last_obj_offset = offset;
-                       offset_one = write_one(f, objects + i, offset);
-                       if (!offset_one)
+                       if (!write_one(f, objects + i, &offset))
                                break;
-                       offset = offset_one;
                        display_progress(progress_state, written);
                }
 
@@ -497,9 +492,9 @@ static void write_pack_file(void)
                } else if (nr_written == nr_remaining) {
                        sha1close(f, sha1, CSUM_FSYNC);
                } else {
-                       int fd = sha1close(f, NULL, 0);
-                       fixup_pack_header_footer(fd, sha1, pack_tmp_name, nr_written);
-                       fsync_or_die(fd, pack_tmp_name);
+                       int fd = sha1close(f, sha1, 0);
+                       fixup_pack_header_footer(fd, sha1, pack_tmp_name,
+                                                nr_written, sha1, offset);
                        close(fd);
                }
 
@@ -1096,9 +1091,12 @@ static void check_object(struct object_entry *entry)
        }
 
        entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
-       if (entry->type < 0)
-               die("unable to get type of object %s",
-                   sha1_to_hex(entry->idx.sha1));
+       /*
+        * The error condition is checked in prepare_pack().  This is
+        * to permit a missing preferred base object to be ignored
+        * as a preferred base.  Doing so can result in a larger
+        * pack file, but the transfer will still take place.
+        */
 }
 
 static int pack_offset_sort(const void *_a, const void *_b)
@@ -1722,8 +1720,12 @@ static void prepare_pack(int window, int depth)
                if (entry->no_try_delta)
                        continue;
 
-               if (!entry->preferred_base)
+               if (!entry->preferred_base) {
                        nr_deltas++;
+                       if (entry->type < 0)
+                               die("unable to get type of object %s",
+                                   sha1_to_hex(entry->idx.sha1));
+               }
 
                delta_list[n++] = entry;
        }
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 893762c80f4910fadf2d6df414bd835cccb7faaa..c023003b2bb6402f2e8d68c00b9e3a675c0065cd 100644 (file)
@@ -645,7 +645,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
            revs.diff)
                usage(rev_list_usage);
 
-       save_commit_buffer = revs.verbose_header || revs.grep_filter;
+       save_commit_buffer = revs.verbose_header ||
+               revs.grep_filter.pattern_list;
        if (bisect_list)
                revs.limited = 1;
 
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);
 
index f4ac595695b1fff1317ff7d14ea9427780327aea..25a29f11a4b9642c1bd367b81779d8f09ae04604 100644 (file)
@@ -1,7 +1,7 @@
 #include "builtin.h"
 #include "cache.h"
 #include "pack.h"
-
+#include "pack-revindex.h"
 
 #define MAX_CHAIN 50
 
@@ -129,6 +129,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
                else {
                        if (verify_one_pack(argv[1], verbose))
                                err = 1;
+                       discard_revindex();
                        nothing_done = 0;
                }
                argc--; argv++;
diff --git a/cache.h b/cache.h
index 2475de9fa837596303284157e08b3080d64351ee..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
 
@@ -222,7 +223,8 @@ struct index_state {
        struct cache_tree *cache_tree;
        time_t timestamp;
        void *alloc;
-       unsigned name_hash_initialized : 1;
+       unsigned name_hash_initialized : 1,
+                initialized : 1;
        struct hash_table name_hash;
 };
 
@@ -377,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);
@@ -391,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);
 
@@ -451,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 9f80a1c5e3a461afd11966625589684d61187911..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.
@@ -727,6 +723,18 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                                die("early EOF '%s'", elem->path);
 
                        result[len] = 0;
+
+                       /* If not a fake symlink, apply filters, e.g. autocrlf */
+                       if (is_file) {
+                               struct strbuf buf;
+
+                               strbuf_init(&buf, 0);
+                               if (convert_to_git(elem->path, result, len, &buf, safe_crlf)) {
+                                       free(result);
+                                       result = strbuf_detach(&buf, &len);
+                                       result_size = len;
+                               }
+                       }
                }
                else {
                deleted_file:
index 77de9621d9c926c6fb8a2bf9ca81c6c376a2ad41..de15f4d715ded4d8fd4f882b44fd9a320e7cdb2e 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -67,7 +67,8 @@ extern int non_ascii(int);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 extern void get_commit_format(const char *arg, struct rev_info *);
 extern void format_commit_message(const struct commit *commit,
-                                  const void *format, struct strbuf *sb);
+                                 const void *format, struct strbuf *sb,
+                                 enum date_mode dmode);
 extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
                                 struct strbuf *,
                                 int abbrev, const char *subject,
@@ -121,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 580966e56a3455b9970b7eef33143c1e9c57d41c..357e733074ea7c85f880fa577ad65dfb3787fec7 100644 (file)
@@ -17,6 +17,8 @@ int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
 
        if (maxsize > 0) {
                ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
+               if (ret == maxsize-1)
+                       ret = -1;
                /* Windows does not NUL-terminate if result fills buffer */
                str[maxsize-1] = 0;
        }
@@ -34,6 +36,8 @@ int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
                        break;
                s = str;
                ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
+               if (ret == maxsize-1)
+                       ret = -1;
        }
        free(s);
        return ret;
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 78189c1b7bca120d896bab7a6160da1f436b4ff1..4f64f8ab7d5860391ddbf0909edcfcfe9073a282 100755 (executable)
@@ -271,15 +271,17 @@ __git_merge_strategies ()
                echo "$__git_merge_strategylist"
                return
        fi
-       sed -n "/^all_strategies='/{
-               s/^all_strategies='//
-               s/'//
+       git merge -s help 2>&1 |
+       sed -n -e '/[Aa]vailable strategies are: /,/^$/{
+               s/\.$//
+               s/.*://
+               s/^[    ]*//
+               s/[     ]*$//
                p
-               q
-               }" "$(git --exec-path)/git-merge"
+       }'
 }
 __git_merge_strategylist=
-__git_merge_strategylist="$(__git_merge_strategies 2>/dev/null)"
+__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null)
 
 __git_complete_file ()
 {
@@ -501,7 +503,7 @@ __git_has_doubledash ()
        return 1
 }
 
-__git_whitespacelist="nowarn warn error error-all strip"
+__git_whitespacelist="nowarn warn error error-all fix"
 
 _git_am ()
 {
@@ -769,7 +771,7 @@ _git_diff ()
                __gitcomp "--cached --stat --numstat --shortstat --summary
                        --patch-with-stat --name-only --name-status --color
                        --no-color --color-words --no-renames --check
-                       --full-index --binary --abbrev --diff-filter
+                       --full-index --binary --abbrev --diff-filter=
                        --find-copies-harder --pickaxe-all --pickaxe-regex
                        --text --ignore-space-at-eol --ignore-space-change
                        --ignore-all-space --exit-code --quiet --ext-diff
@@ -885,7 +887,11 @@ _git_help ()
                return
                ;;
        esac
-       __gitcomp "$(__git_all_commands)"
+       __gitcomp "$(__git_all_commands)
+               attributes cli core-tutorial cvs-migration
+               diffcore gitk glossary hooks ignore modules
+               repository-layout tutorial tutorial-2
+               "
 }
 
 _git_init ()
@@ -972,6 +978,7 @@ _git_log ()
                        --decorate --diff-filter=
                        --color-words --walk-reflogs
                        --parents --children --full-history
+                       --merge
                        "
                return
                ;;
@@ -1001,6 +1008,25 @@ _git_merge ()
        __gitcomp "$(__git_refs)"
 }
 
+_git_mergetool ()
+{
+       local cur="${COMP_WORDS[COMP_CWORD]}"
+       case "$cur" in
+       --tool=*)
+               __gitcomp "
+                       kdiff3 tkdiff meld xxdiff emerge
+                       vimdiff gvimdiff ecmerge opendiff
+                       " "" "${cur##--tool=}"
+               return
+               ;;
+       --*)
+               __gitcomp "--tool="
+               return
+               ;;
+       esac
+       COMPREPLY=()
+}
+
 _git_merge_base ()
 {
        __gitcomp "$(__git_refs)"
@@ -1457,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
@@ -1650,6 +1676,7 @@ _git ()
        ls-remote)   _git_ls_remote ;;
        ls-tree)     _git_ls_tree ;;
        merge)       _git_merge;;
+       mergetool)   _git_mergetool;;
        merge-base)  _git_merge_base ;;
        mv)          _git_mv ;;
        name-rev)    _git_name_rev ;;
index 6ae0429c2dde435f8ae33991ad10f40485aefdc6..2216cacba79ad7171b7b5abbd1ff27e7b1ef84a1 100755 (executable)
@@ -16,6 +16,46 @@ from sets import Set;
 
 verbose = False
 
+
+def p4_build_cmd(cmd):
+    """Build a suitable p4 command line.
+
+    This consolidates building and returning a p4 command line into one
+    location. It means that hooking into the environment, or other configuration
+    can be done more easily.
+    """
+    real_cmd = "%s " % "p4"
+
+    user = gitConfig("git-p4.user")
+    if len(user) > 0:
+        real_cmd += "-u %s " % user
+
+    password = gitConfig("git-p4.password")
+    if len(password) > 0:
+        real_cmd += "-P %s " % password
+
+    port = gitConfig("git-p4.port")
+    if len(port) > 0:
+        real_cmd += "-p %s " % port
+
+    host = gitConfig("git-p4.host")
+    if len(host) > 0:
+        real_cmd += "-h %s " % host
+
+    client = gitConfig("git-p4.client")
+    if len(client) > 0:
+        real_cmd += "-c %s " % client
+
+    real_cmd += "%s" % (cmd)
+    if verbose:
+        print real_cmd
+    return real_cmd
+
+def chdir(dir):
+    if os.name == 'nt':
+        os.environ['PWD']=dir
+    os.chdir(dir)
+
 def die(msg):
     if verbose:
         raise Exception(msg)
@@ -34,6 +74,10 @@ def write_pipe(c, str):
 
     return val
 
+def p4_write_pipe(c, str):
+    real_cmd = p4_build_cmd(c)
+    return write_pipe(real_cmd, str)
+
 def read_pipe(c, ignore_error=False):
     if verbose:
         sys.stderr.write('Reading pipe: %s\n' % c)
@@ -45,6 +89,9 @@ def read_pipe(c, ignore_error=False):
 
     return val
 
+def p4_read_pipe(c, ignore_error=False):
+    real_cmd = p4_build_cmd(c)
+    return read_pipe(real_cmd, ignore_error)
 
 def read_pipe_lines(c):
     if verbose:
@@ -57,12 +104,22 @@ def read_pipe_lines(c):
 
     return val
 
+def p4_read_pipe_lines(c):
+    """Specifically invoke p4 on the command supplied. """
+    real_cmd = p4_build_cmd(c)
+    return read_pipe_lines(real_cmd)
+
 def system(cmd):
     if verbose:
         sys.stderr.write("executing %s\n" % cmd)
     if os.system(cmd) != 0:
         die("command failed: %s" % cmd)
 
+def p4_system(cmd):
+    """Specifically invoke p4 as the system command. """
+    real_cmd = p4_build_cmd(cmd)
+    return system(real_cmd)
+
 def isP4Exec(kind):
     """Determine if a Perforce 'kind' should have execute permission
 
@@ -84,12 +141,12 @@ def setP4ExecBit(file, mode):
         if p4Type[-1] == "+":
             p4Type = p4Type[0:-1]
 
-    system("p4 reopen -t %s %s" % (p4Type, file))
+    p4_system("reopen -t %s %s" % (p4Type, file))
 
 def getP4OpenedType(file):
     # Returns the perforce file type for the given file.
 
-    result = read_pipe("p4 opened %s" % file)
+    result = p4_read_pipe("opened %s" % file)
     match = re.match(".*\((.+)\)\r?$", result)
     if match:
         return match.group(1)
@@ -145,7 +202,7 @@ def isModeExecChanged(src_mode, dst_mode):
     return isModeExec(src_mode) != isModeExec(dst_mode)
 
 def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
-    cmd = "p4 -G %s" % cmd
+    cmd = p4_build_cmd("-G %s" % (cmd))
     if verbose:
         sys.stderr.write("Opening pipe: %s\n" % cmd)
 
@@ -364,7 +421,7 @@ def originP4BranchesExist():
 
 def p4ChangesForPaths(depotPaths, changeRange):
     assert depotPaths
-    output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange)
+    output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange)
                                                         for p in depotPaths]))
 
     changes = []
@@ -512,7 +569,7 @@ class P4Submit(Command):
         # remove lines in the Files section that show changes to files outside the depot path we're committing into
         template = ""
         inFilesSection = False
-        for line in read_pipe_lines("p4 change -o"):
+        for line in p4_read_pipe_lines("change -o"):
             if line.endswith("\r\n"):
                 line = line[:-2] + "\n"
             if inFilesSection:
@@ -547,7 +604,7 @@ class P4Submit(Command):
             modifier = diff['status']
             path = diff['src']
             if modifier == "M":
-                system("p4 edit \"%s\"" % path)
+                p4_system("edit \"%s\"" % path)
                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
                     filesToChangeExecBit[path] = diff['dst_mode']
                 editedFiles.add(path)
@@ -562,8 +619,8 @@ class P4Submit(Command):
                     filesToAdd.remove(path)
             elif modifier == "R":
                 src, dest = diff['src'], diff['dst']
-                system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest))
-                system("p4 edit \"%s\"" % (dest))
+                p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest))
+                p4_system("edit \"%s\"" % (dest))
                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
                     filesToChangeExecBit[dest] = diff['dst_mode']
                 os.unlink(dest)
@@ -587,7 +644,7 @@ class P4Submit(Command):
             if response == "s":
                 print "Skipping! Good luck with the next patches..."
                 for f in editedFiles:
-                    system("p4 revert \"%s\"" % f);
+                    p4_system("revert \"%s\"" % f);
                 for f in filesToAdd:
                     system("rm %s" %f)
                 return
@@ -610,10 +667,10 @@ class P4Submit(Command):
         system(applyPatchCmd)
 
         for f in filesToAdd:
-            system("p4 add \"%s\"" % f)
+            p4_system("add \"%s\"" % f)
         for f in filesToDelete:
-            system("p4 revert \"%s\"" % f)
-            system("p4 delete \"%s\"" % f)
+            p4_system("revert \"%s\"" % f)
+            p4_system("delete \"%s\"" % f)
 
         # Set/clear executable bits
         for f in filesToChangeExecBit.keys():
@@ -629,7 +686,7 @@ class P4Submit(Command):
             submitTemplate = self.prepareLogMessage(template, logMessage)
             if os.environ.has_key("P4DIFF"):
                 del(os.environ["P4DIFF"])
-            diff = read_pipe("p4 diff -du ...")
+            diff = p4_read_pipe("diff -du ...")
 
             newdiff = ""
             for newFile in filesToAdd:
@@ -651,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"
@@ -659,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")
 
-            write_pipe("p4 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+")
@@ -712,9 +784,9 @@ class P4Submit(Command):
         print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
         self.oldWorkingDirectory = os.getcwd()
 
-        os.chdir(self.clientPath)
+        chdir(self.clientPath)
         print "Syncronizing p4 checkout..."
-        system("p4 sync ...")
+        p4_system("sync ...")
 
         self.check()
 
@@ -732,7 +804,7 @@ class P4Submit(Command):
 
         if len(commits) == 0:
             print "All changes applied!"
-            os.chdir(self.oldWorkingDirectory)
+            chdir(self.oldWorkingDirectory)
 
             sync = P4Sync()
             sync.run([])
@@ -1399,7 +1471,7 @@ class P4Sync(Command):
             if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
                 system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
 
-        if self.useClientSpec or gitConfig("p4.useclientspec") == "true":
+        if self.useClientSpec or gitConfig("git-p4.useclientspec") == "true":
             self.getClientSpec()
 
         # TODO: should always look at previous commits,
@@ -1670,14 +1742,18 @@ class P4Clone(P4Sync):
         print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
         if not os.path.exists(self.cloneDestination):
             os.makedirs(self.cloneDestination)
-        os.chdir(self.cloneDestination)
+        chdir(self.cloneDestination)
         system("git init")
         self.gitdir = os.getcwd() + "/.git"
         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."
@@ -1782,7 +1858,7 @@ def main():
                 if os.path.exists(cmd.gitdir):
                     cdup = read_pipe("git rev-parse --show-cdup").strip()
                     if len(cdup) > 0:
-                        os.chdir(cdup);
+                        chdir(cdup);
 
         if not isValidGitDir(cmd.gitdir):
             if isValidGitDir(cmd.gitdir + "/.git"):
index b16a8384bcfbfe33dc33e1076c64f5d36e75e803..49b335921a3871d82a2c0110170a6e66d71561ee 100644 (file)
@@ -3,14 +3,16 @@ git-p4 - Perforce <-> Git converter using git-fast-import
 Usage
 =====
 
-git-p4 supports two main modes: Importing from Perforce to a Git repository is
-done using "git-p4 sync" or "git-p4 rebase". Submitting changes from Git back
-to Perforce is done using "git-p4 submit".
+git-p4 can be used in two different ways:
+
+1) To import changes from Perforce to a Git repository, using "git-p4 sync".
+
+2) To submit changes from Git back to Perforce, using "git-p4 submit".
 
 Importing
 =========
 
-You can simply start with
+Simply start with
 
   git-p4 clone //depot/path/project
 
@@ -18,11 +20,18 @@ or
 
   git-p4 clone //depot/path/project myproject
 
-This will create an empty git repository in a subdirectory called "project" (or
-"myproject" with the second command), import the head revision from the
-specified perforce path into a git "p4" branch (remotes/p4 actually), create a
-master branch off it and check it out. If you want the entire history (not just
-the head revision) then you can simply append a "@all" to the depot path:
+This will:
+
+1) Create an empty git repository in a subdirectory called "project" (or
+"myproject" with the second command)
+
+2) Import the head revision from the given Perforce path into a git branch
+called "p4" (remotes/p4 actually)
+
+3) Create a master branch based on it and check it out.
+
+If you want the entire history (not just the head revision) then you can simply
+append a "@all" to the depot path:
 
   git-p4 clone //depot/project/main@all myproject
 
@@ -37,43 +46,40 @@ If you want more control you can also use the git-p4 sync command directly:
 
 This will import the current head revision of the specified depot path into a
 "remotes/p4/master" branch of your git repository. You can use the
---branch=mybranch option to use a different branch.
+--branch=mybranch option to import into a different branch.
 
-If you want to import the entire history of a given depot path just use
+If you want to import the entire history of a given depot path simply use:
 
   git-p4 sync //path/in/depot@all
 
+
+Note:
+
 To achieve optimal compression you may want to run 'git repack -a -d -f' after
 a big import. This may take a while.
 
-Support for Perforce integrations is still work in progress. Don't bother
-trying it unless you want to hack on it :)
-
 Incremental Imports
 ===================
 
-After an initial import you can easily synchronize your git repository with
-newer changes from the Perforce depot by just calling
+After an initial import you can continue to synchronize your git repository
+with newer changes from the Perforce depot by just calling
 
   git-p4 sync
 
 in your git repository. By default the "remotes/p4/master" branch is updated.
 
-It is recommended to run 'git repack -a -d -f' from time to time when using
-incremental imports to optimally combine the individual git packs that each
-incremental import creates through the use of git-fast-import.
-
+Advanced Setup
+==============
 
-A useful setup may be that you have a periodically updated git repository
-somewhere that contains a complete import of a Perforce project. That git
-repository can be used to clone the working repository from and one would
-import from Perforce directly after cloning using git-p4. If the connection to
-the Perforce server is slow and the working repository hasn't been synced for a
-while it may be desirable to fetch changes from the origin git repository using
-the efficient git protocol. git-p4 supports this setup by calling "git fetch origin"
-by default if there is an origin branch. You can disable this using
+Suppose you have a periodically updated git repository somewhere, containing a
+complete import of a Perforce project. This repository can be cloned and used
+with git-p4. When updating the cloned repository with the "sync" command,
+git-p4 will try to fetch changes from the original repository first. The git
+protocol used with this is usually faster than importing from Perforce
+directly.
 
-  git config git-p4.syncFromOrigin false
+This behaviour can be disabled by setting the "git-p4.syncFromOrigin" git
+configuration variable to "false".
 
 Updating
 ========
@@ -91,7 +97,7 @@ Submitting
 ==========
 
 git-p4 has support for submitting changes from a git repository back to the
-Perforce depot. This requires a Perforce checkout separate to your git
+Perforce depot. This requires a Perforce checkout separate from your git
 repository. To submit all changes that are in the current git branch but not in
 the "p4" branch (or "origin" if "p4" doesn't exist) simply call
 
@@ -109,17 +115,6 @@ continue importing the remaining changes with
 
   git-p4 submit --continue
 
-After submitting you should sync your perforce import branch ("p4" or "origin")
-from Perforce using git-p4's sync command.
-
-If you have changes in your working directory that you haven't committed into
-git yet but that you want to commit to Perforce directly ("quick fixes") then
-you do not have to go through the intermediate step of creating a git commit
-first but you can just call
-
-  git-p4 submit --direct
-
-
 Example
 =======
 
@@ -140,6 +135,62 @@ Example
   git-p4 rebase
 
 
+Configuration parameters
+========================
+
+git-p4.user ($P4USER)
+
+Allows you to specify the username to use to connect to the Perforce repository.
+
+  git config [--global] git-p4.user public
+
+git-p4.password ($P4PASS)
+
+Allows you to specify the password to use to connect to the Perforce repository.
+Warning this password will be visible on the command-line invocation of the p4 binary.
+
+  git config [--global] git-p4.password public1234
+
+git-p4.port ($P4PORT)
+
+Specify the port to be used to contact the Perforce server. As this will be passed
+directly to the p4 binary, it may be in the format host:port as well.
+
+  git config [--global] git-p4.port codes.zimbra.com:2666
+
+git-p4.host ($P4HOST)
+
+Specify the host to contact for a Perforce repository.
+
+  git config [--global] git-p4.host perforce.example.com
+
+git-p4.client ($P4CLIENT)
+
+Specify the client name to use
+
+  git config [--global] git-p4.client public-view
+
+git-p4.allowSubmit
+
+  git config [--global] git-p4.allowSubmit false
+
+git-p4.syncFromOrigin
+
+A useful setup may be that you have a periodically updated git repository
+somewhere that contains a complete import of a Perforce project. That git
+repository can be used to clone the working repository from and one would
+import from Perforce directly after cloning using git-p4. If the connection to
+the Perforce server is slow and the working repository hasn't been synced for a
+while it may be desirable to fetch changes from the origin git repository using
+the efficient git protocol. git-p4 supports this setup by calling "git fetch origin"
+by default if there is an origin branch. You can disable this using:
+
+  git config [--global] git-p4.syncFromOrigin false
+
+git-p4.useclientspec
+
+  git config [--global] git-p4.useclientspec false
+
 Implementation Details...
 =========================
 
index ace64f165e4a01fb99892e9b89e7df791a7f4ca1..28389541a32454f2d988bb02e4268ffe12755bbc 100644 (file)
@@ -42,11 +42,11 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
                sha1flush(f, offset);
                f->offset = 0;
        }
+       SHA1_Final(f->buffer, &f->ctx);
+       if (result)
+               hashcpy(result, f->buffer);
        if (flags & (CSUM_CLOSE | CSUM_FSYNC)) {
                /* write checksum and close fd */
-               SHA1_Final(f->buffer, &f->ctx);
-               if (result)
-                       hashcpy(result, f->buffer);
                sha1flush(f, 20);
                if (flags & CSUM_FSYNC)
                        fsync_or_die(f->fd, f->name);
diff --git a/ctype.c b/ctype.c
index ee06eb7f48f1d3e818b3037369b4e056fe7e5be7..9208d674dbc081878532f08d2eddfc66c6a2e327 100644 (file)
--- a/ctype.c
+++ b/ctype.c
@@ -5,17 +5,24 @@
  */
 #include "cache.h"
 
+/* Just so that no insane platform contaminate namespace with these symbols */
+#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..c315932ced825f96f669003616a9fb304309c75d 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, "[%"PRIuMAX"] ", (uintmax_t)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("[%"PRIuMAX"] Disconnected%s", (uintmax_t)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) {
@@ -1022,7 +923,7 @@ static void store_pid(const char *path)
        FILE *f = fopen(path, "w");
        if (!f)
                die("cannot open pid file %s: %s", path, strerror(errno));
-       if (fprintf(f, "%d\n", getpid()) < 0 || fclose(f) != 0)
+       if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0)
                die("failed to write pid file %s: %s", path, strerror(errno));
 }
 
@@ -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();
 
index d9668d2ef94c73e4a7a5602011ff13a9fd9d8c6a..82d9e221eabab53acc418d0db6327e480836a5ed 100644 (file)
@@ -6,13 +6,13 @@
 #include "object.h"
 #include "decorate.h"
 
-static unsigned int hash_obj(struct object *obj, unsigned int n)
+static unsigned int hash_obj(const struct object *obj, unsigned int n)
 {
        unsigned int hash = *(unsigned int *)obj->sha1;
        return hash % n;
 }
 
-static void *insert_decoration(struct decoration *n, struct object *base, void *decoration)
+static void *insert_decoration(struct decoration *n, const struct object *base, void *decoration)
 {
        int size = n->size;
        struct object_decoration *hash = n->hash;
@@ -44,7 +44,7 @@ static void grow_decoration(struct decoration *n)
        n->nr = 0;
 
        for (i = 0; i < old_size; i++) {
-               struct object *base = old_hash[i].base;
+               const struct object *base = old_hash[i].base;
                void *decoration = old_hash[i].decoration;
 
                if (!base)
@@ -55,7 +55,8 @@ static void grow_decoration(struct decoration *n)
 }
 
 /* Add a decoration pointer, return any old one */
-void *add_decoration(struct decoration *n, struct object *obj, void *decoration)
+void *add_decoration(struct decoration *n, const struct object *obj,
+               void *decoration)
 {
        int nr = n->nr + 1;
 
@@ -65,7 +66,7 @@ void *add_decoration(struct decoration *n, struct object *obj, void *decoration)
 }
 
 /* Lookup a decoration pointer */
-void *lookup_decoration(struct decoration *n, struct object *obj)
+void *lookup_decoration(struct decoration *n, const struct object *obj)
 {
        int j;
 
index 1fa4ad9beb08f23888814b99183487ab85378bfd..e7328044ff84a4acaaa7f5f4bc5f85375dc7a07a 100644 (file)
@@ -2,7 +2,7 @@
 #define DECORATE_H
 
 struct object_decoration {
-       struct object *base;
+       const struct object *base;
        void *decoration;
 };
 
@@ -12,7 +12,7 @@ struct decoration {
        struct object_decoration *hash;
 };
 
-extern void *add_decoration(struct decoration *n, struct object *obj, void *decoration);
-extern void *lookup_decoration(struct decoration *n, struct object *obj);
+extern void *add_decoration(struct decoration *n, const struct object *obj, void *decoration);
+extern void *lookup_decoration(struct decoration *n, const struct object *obj);
 
 #endif
diff --git a/diff.c b/diff.c
index bf5d5f15a3b54f1e1a0a2068990e899fd869d435..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 {
@@ -1054,6 +1060,13 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch
        return this_dir;
 }
 
+static int dirstat_compare(const void *_a, const void *_b)
+{
+       const struct dirstat_file *a = _a;
+       const struct dirstat_file *b = _b;
+       return strcmp(a->name, b->name);
+}
+
 static void show_dirstat(struct diff_options *options)
 {
        int i;
@@ -1113,6 +1126,7 @@ static void show_dirstat(struct diff_options *options)
                return;
 
        /* Show all directories with more than x% of the changes */
+       qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
        gather_dirstat(options->file, &dir, changed, "", 0);
 }
 
@@ -1131,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;
@@ -1376,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"
                        "^[     ]*\\(\\([       ]*"
@@ -1387,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)
@@ -1521,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);
        }
@@ -1580,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:
@@ -1603,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;
@@ -1627,10 +1638,10 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
                xdemitcb_t ecb;
 
                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) {
@@ -3018,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;
 };
@@ -3063,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;
@@ -3128,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);
@@ -3207,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 29d1d5ba31def46ba8b55905dc60773cc6cc167e..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 == '?';
-}
-
 /*
  * 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 7089e6f9e6c5fa9142f468e54afe7d33a6d2eec7..ccdf2e57b0cf1846996bcda8e89d7d8575a031f6 100644 (file)
@@ -376,7 +376,7 @@ static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *);
 
 static void write_crash_report(const char *err)
 {
-       char *loc = git_path("fast_import_crash_%d", getpid());
+       char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
        FILE *rpt = fopen(loc, "w");
        struct branch *b;
        unsigned long lu;
@@ -390,8 +390,8 @@ static void write_crash_report(const char *err)
        fprintf(stderr, "fast-import: dumping crash report to %s\n", loc);
 
        fprintf(rpt, "fast-import crash report:\n");
-       fprintf(rpt, "    fast-import process: %d\n", getpid());
-       fprintf(rpt, "    parent process     : %d\n", getppid());
+       fprintf(rpt, "    fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid());
+       fprintf(rpt, "    parent process     : %"PRIuMAX"\n", (uintmax_t) getppid());
        fprintf(rpt, "    at %s\n", show_date(time(NULL), 0, DATE_LOCAL));
        fputc('\n', rpt);
 
@@ -951,7 +951,8 @@ static void end_packfile(void)
 
                close_pack_windows(pack_data);
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
-                                   pack_data->pack_name, object_count);
+                                   pack_data->pack_name, object_count,
+                                   NULL, 0);
                close(pack_data->pack_fd);
                idx_name = keep_pack(create_index());
 
index 8f91a97eb3a07908b35de086d555822c928fd2e9..aa602618e6caedbfdd2d54ad4bb8375356cc55f6 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -10,7 +10,7 @@ git am [options] (--resolved | --skip | --abort)
 --
 d,dotest=       (removed -- do not use)
 i,interactive   run interactively
-b,binary        pass --allow-binary-replacement to git-apply
+b,binary        (historical option -- no-op)
 3,3way          allow fall back on 3way merging if needed
 s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
@@ -87,7 +87,7 @@ fall_back_3way () {
 
     echo Using index info to reconstruct a base tree...
     if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
-       git apply $binary --cached <"$dotest/patch"
+       git apply --cached <"$dotest/patch"
     then
        mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
        mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
@@ -121,7 +121,7 @@ It does not apply to blobs recorded in its index."
 
 prec=4
 dotest="$GIT_DIR/rebase-apply"
-sign= utf8=t keep= skip= interactive= resolved= binary= rebasing= abort=
+sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
 resolvemsg= resume=
 git_apply_opt=
 
@@ -131,7 +131,7 @@ do
        -i|--interactive)
                interactive=t ;;
        -b|--binary)
-               binary=t ;;
+               : ;;
        -3|--3way)
                threeway=t ;;
        -s|--signoff)
@@ -149,7 +149,7 @@ do
        --abort)
                abort=t ;;
        --rebasing)
-               rebasing=t threeway=t keep=t binary=t ;;
+               rebasing=t threeway=t keep=t ;;
        -d|--dotest)
                die "-d option is no longer supported.  Do not use."
                ;;
@@ -247,10 +247,9 @@ else
                exit 1
        }
 
-       # -b, -s, -u, -k and --whitespace flags are kept for the
+       # -s, -u, -k and --whitespace flags are kept for the
        # resuming session after a patch failure.
        # -3 and -i can and must be given when resuming.
-       echo "$binary" >"$dotest/binary"
        echo " $ws" >"$dotest/whitespace"
        echo "$sign" >"$dotest/sign"
        echo "$utf8" >"$dotest/utf8"
@@ -274,10 +273,6 @@ case "$resolved" in
        fi
 esac
 
-if test "$(cat "$dotest/binary")" = t
-then
-       binary=--allow-binary-replacement
-fi
 if test "$(cat "$dotest/utf8")" = t
 then
        utf8=-u
@@ -459,7 +454,7 @@ do
 
        case "$resolved" in
        '')
-               git apply $git_apply_opt $binary --index "$dotest/patch"
+               git apply $git_apply_opt --index "$dotest/patch"
                apply_status=$?
                ;;
        t)
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 2688254af399ddc452b120734d41b41292b0ed69..81392add0b852f51f63a470727c33e0c306260d8 100755 (executable)
@@ -412,15 +412,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 0ab478ef90aba9048a2de785d538da16e1bae431..4e709ebe776f722ff5509c8bf1b9cfaf9d7923b4 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=0.10.GITGUI
+DEF_VER=0.11.GITGUI
 
 LF='
 '
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 4e334ba41dad3067394b79c15ebfe610b2d3e178..929d681c4716fa4f6b2947fdb40ad6fbb580bef3 100755 (executable)
@@ -145,7 +145,16 @@ pick_one () {
 }
 
 pick_one_preserving_merges () {
-       case "$1" in -n) sha1=$2 ;; *) sha1=$1 ;; esac
+       fast_forward=t
+       case "$1" in
+       -n)
+               fast_forward=f
+               sha1=$2
+               ;;
+       *)
+               sha1=$1
+               ;;
+       esac
        sha1=$(git rev-parse $sha1)
 
        if test -f "$DOTEST"/current-commit
@@ -156,15 +165,14 @@ pick_one_preserving_merges () {
                die "Cannot write current commit's replacement sha1"
        fi
 
+       echo $sha1 > "$DOTEST"/current-commit
+
        # rewrite parents; if none were rewritten, we can fast-forward.
-       fast_forward=t
-       preserve=t
        new_parents=
        for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
        do
                if test -f "$REWRITTEN"/$p
                then
-                       preserve=f
                        new_p=$(cat "$REWRITTEN"/$p)
                        test $p != $new_p && fast_forward=f
                        case "$new_parents" in
@@ -181,7 +189,8 @@ pick_one_preserving_merges () {
        case $fast_forward in
        t)
                output warn "Fast forward to $sha1"
-               test $preserve = f || echo $sha1 > "$REWRITTEN"/$sha1
+               output git reset --hard $sha1 ||
+                       die "Cannot fast forward to $sha1"
                ;;
        f)
                test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
@@ -191,7 +200,6 @@ pick_one_preserving_merges () {
                output git checkout $first_parent 2> /dev/null ||
                        die "Cannot move HEAD to $first_parent"
 
-               echo $sha1 > "$DOTEST"/current-commit
                case "$new_parents" in
                ' '*' '*)
                        # redo merge
index 412e135c3ae88d76b5bdf3f08083b153da220a95..528b604cd57a774030c5f5830d3d78b5a04454cf 100755 (executable)
@@ -144,8 +144,19 @@ is_interactive () {
        done && test -n "$1"
 }
 
+test -f "$GIT_DIR"/rebase-apply/applying &&
+       die 'It looks like git-am is in progress. Cannot rebase.'
+
 is_interactive "$@" && exec git-rebase--interactive "$@"
 
+if test $# -eq 0
+then
+       test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply || usage
+       test -d "$dotest" -o -f "$GIT_DIR"/rebase-apply/rebasing &&
+               die 'A rebase is in progress, try --continue, --skip or --abort.'
+       die "No arguments given and $GIT_DIR/rebase-apply already exists."
+fi
+
 while test $# != 0
 do
        case "$1" in
@@ -268,16 +279,16 @@ done
 # Make sure we do not have $GIT_DIR/rebase-apply
 if test -z "$do_merge"
 then
-       if mkdir "$GIT_DIR"/rebase-apply
+       if mkdir "$GIT_DIR"/rebase-apply 2>/dev/null
        then
                rmdir "$GIT_DIR"/rebase-apply
        else
                echo >&2 '
-It seems that I cannot create a '"$GIT_DIR"'/rebase-apply directory,
-and I wonder if you are in the middle of patch application or another
+It seems that I cannot create a rebase-apply directory, and
+I wonder if you are in the middle of patch application or another
 rebase.  If that is not the case, please
        rm -fr '"$GIT_DIR"'/rebase-apply
- and run me again.  I am stopping in case you still have something
+and run me again.  I am stopping in case you still have something
 valuable there.'
                exit 1
        fi
@@ -285,7 +296,7 @@ else
        if test -d "$dotest"
        then
                die "previous rebase directory $dotest still exists." \
-                       'try git-rebase < --continue | --abort >'
+                       'Try git rebase (--continue | --abort | --skip)'
        fi
 fi
 
index 5ad2c4b7a33cf1be24ee85f17c8738ca05b98db0..e15c12abc31c1e4d22bb3943d70a65ddf33abb53 100755 (executable)
@@ -1,7 +1,13 @@
 #!/bin/sh
 # Copyright (c) 2007, Nanako Shiraishi
 
-USAGE='[  | save | list | show | apply | clear | drop | pop | create | branch ]'
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="list [<options>]
+   or: $dashless (show | drop | pop ) [<stash>]
+   or: $dashless apply [--index] [<stash>]
+   or: $dashless branch <branchname> [<stash>]
+   or: $dashless [save [--keep-index] [<message>]]
+   or: $dashless clear"
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
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 4dc33801a88c63f62a263b5008649b05fc1b9d7d..7a1d26db8bcc451545fad2f8d55d43a5079d2302 100755 (executable)
@@ -421,7 +421,7 @@ sub cmd_dcommit {
        $head ||= 'HEAD';
        my @refs;
        my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
-       $url = $_commit_url if defined $_commit_url;
+       $url = defined $_commit_url ? $_commit_url : $gs->full_url;
        my $last_rev = $_revision if defined $_revision;
        if ($url) {
                print "Committing to $url ...\n";
@@ -437,6 +437,8 @@ sub cmd_dcommit {
                     "If these changes depend on each other, re-running ",
                     "without --no-rebase may be required."
        }
+       my $expect_url = $url;
+       Git::SVN::remove_username($expect_url);
        while (1) {
                my $d = shift @$linear_refs or last;
                unless (defined $last_rev) {
@@ -511,9 +513,9 @@ sub cmd_dcommit {
                                          $gs->refname,
                                          "\nBefore dcommitting";
                                }
-                               if ($url_ ne $url) {
+                               if ($url_ ne $expect_url) {
                                        fatal "URL mismatch after rebase: ",
-                                             "$url_ != $url";
+                                             "$url_ != $expect_url";
                                }
                                if ($uuid_ ne $uuid) {
                                        fatal "uuid mismatch after rebase: ",
@@ -1265,7 +1267,7 @@ sub md5sum {
        my $arg = shift;
        my $ref = ref $arg;
        my $md5 = Digest::MD5->new();
-        if ($ref eq 'GLOB' || $ref eq 'IO::File') {
+        if ($ref eq 'GLOB' || $ref eq 'IO::File' || $ref eq 'File::Temp') {
                $md5->addfile($arg) or croak $!;
        } elsif ($ref eq 'SCALAR') {
                $md5->add($$arg) or croak $!;
@@ -1328,6 +1330,7 @@ BEGIN {
        }
 }
 
+
 my (%LOCKFILES, %INDEX_FILES);
 END {
        unlink keys %LOCKFILES if %LOCKFILES;
@@ -3230,13 +3233,11 @@ sub change_file_prop {
 
 sub apply_textdelta {
        my ($self, $fb, $exp) = @_;
-       my $fh = IO::File->new_tmpfile;
-       $fh->autoflush(1);
+       my $fh = Git::temp_acquire('svn_delta');
        # $fh gets auto-closed() by SVN::TxDelta::apply(),
        # (but $base does not,) so dup() it for reading in close_file
        open my $dup, '<&', $fh or croak $!;
-       my $base = IO::File->new_tmpfile;
-       $base->autoflush(1);
+       my $base = Git::temp_acquire('git_blob');
        if ($fb->{blob}) {
                print $base 'link ' if ($fb->{mode_a} == 120000);
                my $size = $::_repository->cat_blob($fb->{blob}, $base);
@@ -3251,9 +3252,9 @@ sub apply_textdelta {
                }
        }
        seek $base, 0, 0 or croak $!;
-       $fb->{fh} = $dup;
+       $fb->{fh} = $fh;
        $fb->{base} = $base;
-       [ SVN::TxDelta::apply($base, $fh, undef, $fb->{path}, $fb->{pool}) ];
+       [ SVN::TxDelta::apply($base, $dup, undef, $fb->{path}, $fb->{pool}) ];
 }
 
 sub close_file {
@@ -3269,35 +3270,36 @@ sub close_file {
                                    "expected: $exp\n    got: $got\n";
                        }
                }
-               sysseek($fh, 0, 0) or croak $!;
                if ($fb->{mode_b} == 120000) {
-                       eval {
-                               sysread($fh, my $buf, 5) == 5 or croak $!;
-                               $buf eq 'link ' or die "$path has mode 120000",
-                                                      " but is not a link";
-                       };
-                       if ($@) {
-                               warn "$@\n";
-                               sysseek($fh, 0, 0) or croak $!;
-                       }
-               }
+                       sysseek($fh, 0, 0) or croak $!;
+                       sysread($fh, my $buf, 5) == 5 or croak $!;
 
-               my ($tmp_fh, $tmp_filename) = File::Temp::tempfile(UNLINK => 1);
-               my $result;
-               while ($result = sysread($fh, my $string, 1024)) {
-                       my $wrote = syswrite($tmp_fh, $string, $result);
-                       defined($wrote) && $wrote == $result
-                               or croak("write $tmp_filename: $!\n");
-               }
-               defined $result or croak $!;
-               close $tmp_fh or croak $!;
+                       unless ($buf eq 'link ') {
+                               warn "$path has mode 120000",
+                                               " but is not a link\n";
+                       } else {
+                               my $tmp_fh = Git::temp_acquire('svn_hash');
+                               my $res;
+                               while ($res = sysread($fh, my $str, 1024)) {
+                                       my $out = syswrite($tmp_fh, $str, $res);
+                                       defined($out) && $out == $res
+                                               or croak("write ",
+                                                       $tmp_fh->filename,
+                                                       ": $!\n");
+                               }
+                               defined $res or croak $!;
 
-               close $fh or croak $!;
+                               ($fh, $tmp_fh) = ($tmp_fh, $fh);
+                               Git::temp_release($tmp_fh, 1);
+                       }
+               }
 
-               $hash = $::_repository->hash_and_insert_object($tmp_filename);
-               unlink($tmp_filename);
+               $hash = $::_repository->hash_and_insert_object(
+                               $fh->filename);
                $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
-               close $fb->{base} or croak $!;
+
+               Git::temp_release($fb->{base}, 1);
+               Git::temp_release($fh, 1);
        } else {
                $hash = $fb->{blob} or die "no blob information\n";
        }
@@ -3667,7 +3669,7 @@ sub chg_file {
        } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
                $self->change_file_prop($fbat,'svn:executable',undef);
        }
-       my $fh = IO::File->new_tmpfile or croak $!;
+       my $fh = Git::temp_acquire('git_blob');
        if ($m->{mode_b} =~ /^120/) {
                print $fh 'link ' or croak $!;
                $self->change_file_prop($fbat,'svn:special','*');
@@ -3686,9 +3688,8 @@ sub chg_file {
        my $atd = $self->apply_textdelta($fbat, undef, $pool);
        my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool);
        die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
+       Git::temp_release($fh, 1);
        $pool->clear;
-
-       close $fh or croak $!;
 }
 
 sub D {
index d093a39506798c750cc838f0085f6721e0f733a9..087c4ac733be4b788751d0bae5b7aad22ce0dd99 100644 (file)
@@ -22,11 +22,11 @@ proc gitdir {} {
 # run before X event handlers, so reading from a fast source can
 # make the GUI completely unresponsive.
 proc run args {
-    global isonrunq runq
+    global isonrunq runq currunq
 
     set script $args
     if {[info exists isonrunq($script)]} return
-    if {$runq eq {}} {
+    if {$runq eq {} && ![info exists currunq]} {
        after idle dorunq
     }
     lappend runq [list {} $script]
@@ -38,10 +38,10 @@ proc filerun {fd script} {
 }
 
 proc filereadable {fd script} {
-    global runq
+    global runq currunq
 
     fileevent $fd readable {}
-    if {$runq eq {}} {
+    if {$runq eq {} && ![info exists currunq]} {
        after idle dorunq
     }
     lappend runq [list $fd $script]
@@ -60,17 +60,19 @@ proc nukefile {fd} {
 }
 
 proc dorunq {} {
-    global isonrunq runq
+    global isonrunq runq currunq
 
     set tstart [clock clicks -milliseconds]
     set t0 $tstart
     while {[llength $runq] > 0} {
        set fd [lindex $runq 0 0]
        set script [lindex $runq 0 1]
+       set currunq [lindex $runq 0]
+       set runq [lrange $runq 1 end]
        set repeat [eval $script]
+       unset currunq
        set t1 [clock clicks -milliseconds]
        set t [expr {$t1 - $t0}]
-       set runq [lrange $runq 1 end]
        if {$repeat ne {} && $repeat} {
            if {$fd eq {} || $repeat == 2} {
                # script returns 1 if it wants to be readded
index aa0eeca24786dbd5143354fc3bb5e8cdb3ef831f..07f5b5378805a520e22b0f1b598aed7af4235538 100644 (file)
@@ -481,6 +481,19 @@ span.refs span {
        border-color: #ffccff #ff00ee #ff00ee #ffccff;
 }
 
+span.refs span a {
+       text-decoration: none;
+       color: inherit;
+}
+
+span.refs span a:hover {
+       text-decoration: underline;
+}
+
+span.refs span.indirect {
+       font-style: italic;
+}
+
 span.refs span.ref {
        background-color: #aaaaff;
        border-color: #ccccff #0033cc #0033cc #ccccff;
index 90cd99bf916135e5c0a9e1bd7d5e9ff45555c489..29e21564c8e7b2dd711af2e96e03b811b2c1a268 100755 (executable)
@@ -1090,13 +1090,23 @@ sub format_log_line_html {
 }
 
 # format marker of refs pointing to given object
+
+# the destination action is chosen based on object type and current context:
+# - for annotated tags, we choose the tag view unless it's the current view
+#   already, in which case we go to shortlog view
+# - for other refs, we keep the current view if we're in history, shortlog or
+#   log view, and select shortlog otherwise
 sub format_ref_marker {
        my ($refs, $id) = @_;
        my $markers = '';
 
        if (defined $refs->{$id}) {
                foreach my $ref (@{$refs->{$id}}) {
+                       # this code exploits the fact that non-lightweight tags are the
+                       # only indirect objects, and that they are the only objects for which
+                       # we want to use tag instead of shortlog as action
                        my ($type, $name) = qw();
+                       my $indirect = ($ref =~ s/\^\{\}$//);
                        # e.g. tags/v2.6.11 or heads/next
                        if ($ref =~ m!^(.*?)s?/(.*)$!) {
                                $type = $1;
@@ -1106,8 +1116,29 @@ sub format_ref_marker {
                                $name = $ref;
                        }
 
-                       $markers .= " <span class=\"$type\" title=\"$ref\">" .
-                                   esc_html($name) . "</span>";
+                       my $class = $type;
+                       $class .= " indirect" if $indirect;
+
+                       my $dest_action = "shortlog";
+
+                       if ($indirect) {
+                               $dest_action = "tag" unless $action eq "tag";
+                       } elsif ($action =~ /^(history|(short)?log)$/) {
+                               $dest_action = $action;
+                       }
+
+                       my $dest = "";
+                       $dest .= "refs/" unless $ref =~ m!^refs/!;
+                       $dest .= $ref;
+
+                       my $link = $cgi->a({
+                               -href => href(
+                                       action=>$dest_action,
+                                       hash=>$dest
+                               )}, $name);
+
+                       $markers .= " <span class=\"$class\" title=\"$ref\">" .
+                               $link . "</span>";
                }
        }
 
@@ -1918,7 +1949,7 @@ sub git_get_references {
 
        while (my $line = <$fd>) {
                chomp $line;
-               if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type/?[^^]+)!) {
+               if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) {
                        if (defined $refs{$1}) {
                                push @{$refs{$1}}, $2;
                        } else {
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 3cb19628965685ce59a5377b81bef975851996e8..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,129 +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);
-}
-
-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);
@@ -685,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 52064befdbbbdf671bd08e369a133d4f1fee03c1..a6e91fe3ba47e3b0837648180bb7d47acd3e7dc7 100644 (file)
@@ -654,7 +654,7 @@ static void parse_pack_objects(unsigned char *sha1)
        }
 }
 
-static int write_compressed(int fd, void *in, unsigned int size, uint32_t *obj_crc)
+static int write_compressed(struct sha1file *f, void *in, unsigned int size)
 {
        z_stream stream;
        unsigned long maxsize;
@@ -674,13 +674,12 @@ static int write_compressed(int fd, void *in, unsigned int size, uint32_t *obj_c
        deflateEnd(&stream);
 
        size = stream.total_out;
-       write_or_die(fd, out, size);
-       *obj_crc = crc32(*obj_crc, out, size);
+       sha1write(f, out, size);
        free(out);
        return size;
 }
 
-static struct object_entry *append_obj_to_pack(
+static struct object_entry *append_obj_to_pack(struct sha1file *f,
                               const unsigned char *sha1, void *buf,
                               unsigned long size, enum object_type type)
 {
@@ -696,15 +695,15 @@ static struct object_entry *append_obj_to_pack(
                s >>= 7;
        }
        header[n++] = c;
-       write_or_die(output_fd, header, n);
-       obj[0].idx.crc32 = crc32(0, Z_NULL, 0);
-       obj[0].idx.crc32 = crc32(obj[0].idx.crc32, header, n);
+       crc32_begin(f);
+       sha1write(f, header, n);
        obj[0].size = size;
        obj[0].hdr_size = n;
        obj[0].type = type;
        obj[0].real_type = type;
        obj[1].idx.offset = obj[0].idx.offset + n;
-       obj[1].idx.offset += write_compressed(output_fd, buf, size, &obj[0].idx.crc32);
+       obj[1].idx.offset += write_compressed(f, buf, size);
+       obj[0].idx.crc32 = crc32_end(f);
        hashcpy(obj->idx.sha1, sha1);
        return obj;
 }
@@ -716,7 +715,7 @@ static int delta_pos_compare(const void *_a, const void *_b)
        return a->obj_no - b->obj_no;
 }
 
-static void fix_unresolved_deltas(int nr_unresolved)
+static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
 {
        struct delta_entry **sorted_by_pos;
        int i, n = 0;
@@ -754,8 +753,8 @@ static void fix_unresolved_deltas(int nr_unresolved)
                if (check_sha1_signature(d->base.sha1, base_obj.data,
                                base_obj.size, typename(type)))
                        die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
-               base_obj.obj = append_obj_to_pack(d->base.sha1, base_obj.data,
-                       base_obj.size, type);
+               base_obj.obj = append_obj_to_pack(f, d->base.sha1,
+                                       base_obj.data, base_obj.size, type);
                link_base_data(NULL, &base_obj);
 
                find_delta_children(&d->base, &first, &last);
@@ -875,8 +874,10 @@ int main(int argc, char **argv)
        const char *keep_name = NULL, *keep_msg = NULL;
        char *index_name_buf = NULL, *keep_name_buf = NULL;
        struct pack_idx_entry **idx_objects;
-       unsigned char sha1[20];
+       unsigned char pack_sha1[20];
+       int nongit = 0;
 
+       setup_git_directory_gently(&nongit);
        git_config(git_index_pack_config, NULL);
 
        for (i = 1; i < argc; i++) {
@@ -960,13 +961,15 @@ int main(int argc, char **argv)
        parse_pack_header();
        objects = xmalloc((nr_objects + 1) * sizeof(struct object_entry));
        deltas = xmalloc(nr_objects * sizeof(struct delta_entry));
-       parse_pack_objects(sha1);
+       parse_pack_objects(pack_sha1);
        if (nr_deltas == nr_resolved_deltas) {
                stop_progress(&progress);
                /* Flush remaining pack final 20-byte SHA1. */
                flush();
        } else {
                if (fix_thin_pack) {
+                       struct sha1file *f;
+                       unsigned char read_sha1[20], tail_sha1[20];
                        char msg[48];
                        int nr_unresolved = nr_deltas - nr_resolved_deltas;
                        int nr_objects_initial = nr_objects;
@@ -975,12 +978,19 @@ int main(int argc, char **argv)
                        objects = xrealloc(objects,
                                           (nr_objects + nr_unresolved + 1)
                                           * sizeof(*objects));
-                       fix_unresolved_deltas(nr_unresolved);
+                       f = sha1fd(output_fd, curr_pack);
+                       fix_unresolved_deltas(f, nr_unresolved);
                        sprintf(msg, "completed with %d local objects",
                                nr_objects - nr_objects_initial);
                        stop_progress_msg(&progress, msg);
-                       fixup_pack_header_footer(output_fd, sha1,
-                                                curr_pack, nr_objects);
+                       sha1close(f, tail_sha1, 0);
+                       hashcpy(read_sha1, pack_sha1);
+                       fixup_pack_header_footer(output_fd, pack_sha1,
+                                                curr_pack, nr_objects,
+                                                read_sha1, consumed_bytes-20);
+                       if (hashcmp(read_sha1, tail_sha1) != 0)
+                               die("Unexpected tail checksum for %s "
+                                   "(disk corruption?)", curr_pack);
                }
                if (nr_deltas != nr_resolved_deltas)
                        die("pack has %d unresolved deltas",
@@ -993,13 +1003,13 @@ int main(int argc, char **argv)
        idx_objects = xmalloc((nr_objects) * sizeof(struct pack_idx_entry *));
        for (i = 0; i < nr_objects; i++)
                idx_objects[i] = &objects[i].idx;
-       curr_index = write_idx_file(index_name, idx_objects, nr_objects, sha1);
+       curr_index = write_idx_file(index_name, idx_objects, nr_objects, pack_sha1);
        free(idx_objects);
 
        final(pack_name, curr_pack,
                index_name, curr_index,
                keep_name, keep_msg,
-               sha1);
+               pack_sha1);
        free(objects);
        free(index_name_buf);
        free(keep_name_buf);
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? */
index cd300bdff5b524a4d509ba5276e9ef21f443013d..6096b6224ad551086afa346617eb714c15a51f78 100644 (file)
@@ -142,3 +142,15 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
        } while (lo < hi);
        die("internal error: pack revindex corrupt");
 }
+
+void discard_revindex(void)
+{
+       if (pack_revindex_hashsz) {
+               int i;
+               for (i = 0; i < pack_revindex_hashsz; i++)
+                       if (pack_revindex[i].revindex)
+                               free(pack_revindex[i].revindex);
+               free(pack_revindex);
+               pack_revindex_hashsz = 0;
+       }
+}
index 36a514a6cf600e7e77794e50998a9d160e30c8e9..8d5027ad917224f689e786e9a0b4e9a387e59dfe 100644 (file)
@@ -7,5 +7,6 @@ struct revindex_entry {
 };
 
 struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs);
+void discard_revindex(void);
 
 #endif
index a8f02699366c87de960d7637e9f69c26c2241693..939ed56362d3c29bfc64e8cca91032df9a57961e 100644 (file)
@@ -144,41 +144,94 @@ char *write_idx_file(char *index_name, struct pack_idx_entry **objects,
        return index_name;
 }
 
+/*
+ * Update pack header with object_count and compute new SHA1 for pack data
+ * associated to pack_fd, and write that SHA1 at the end.  That new SHA1
+ * is also returned in new_pack_sha1.
+ *
+ * If partial_pack_sha1 is non null, then the SHA1 of the existing pack
+ * (without the header update) is computed and validated against the
+ * one provided in partial_pack_sha1.  The validation is performed at
+ * partial_pack_offset bytes in the pack file.  The SHA1 of the remaining
+ * data (i.e. from partial_pack_offset to the end) is then computed and
+ * returned in partial_pack_sha1.
+ *
+ * Note that new_pack_sha1 is updated last, so both new_pack_sha1 and
+ * partial_pack_sha1 can refer to the same buffer if the caller is not
+ * interested in the resulting SHA1 of pack data above partial_pack_offset.
+ */
 void fixup_pack_header_footer(int pack_fd,
-                        unsigned char *pack_file_sha1,
+                        unsigned char *new_pack_sha1,
                         const char *pack_name,
-                        uint32_t object_count)
+                        uint32_t object_count,
+                        unsigned char *partial_pack_sha1,
+                        off_t partial_pack_offset)
 {
-       static const int buf_sz = 128 * 1024;
-       SHA_CTX c;
+       int aligned_sz, buf_sz = 8 * 1024;
+       SHA_CTX old_sha1_ctx, new_sha1_ctx;
        struct pack_header hdr;
        char *buf;
 
+       SHA1_Init(&old_sha1_ctx);
+       SHA1_Init(&new_sha1_ctx);
+
        if (lseek(pack_fd, 0, SEEK_SET) != 0)
-               die("Failed seeking to start: %s", strerror(errno));
+               die("Failed seeking to start of %s: %s", pack_name, strerror(errno));
        if (read_in_full(pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
                die("Unable to reread header of %s: %s", pack_name, strerror(errno));
        if (lseek(pack_fd, 0, SEEK_SET) != 0)
-               die("Failed seeking to start: %s", strerror(errno));
+               die("Failed seeking to start of %s: %s", pack_name, strerror(errno));
+       SHA1_Update(&old_sha1_ctx, &hdr, sizeof(hdr));
        hdr.hdr_entries = htonl(object_count);
+       SHA1_Update(&new_sha1_ctx, &hdr, sizeof(hdr));
        write_or_die(pack_fd, &hdr, sizeof(hdr));
-
-       SHA1_Init(&c);
-       SHA1_Update(&c, &hdr, sizeof(hdr));
+       partial_pack_offset -= sizeof(hdr);
 
        buf = xmalloc(buf_sz);
+       aligned_sz = buf_sz - sizeof(hdr);
        for (;;) {
-               ssize_t n = xread(pack_fd, buf, buf_sz);
+               ssize_t m, n;
+               m = (partial_pack_sha1 && partial_pack_offset < aligned_sz) ?
+                       partial_pack_offset : aligned_sz;
+               n = xread(pack_fd, buf, m);
                if (!n)
                        break;
                if (n < 0)
                        die("Failed to checksum %s: %s", pack_name, strerror(errno));
-               SHA1_Update(&c, buf, n);
+               SHA1_Update(&new_sha1_ctx, buf, n);
+
+               aligned_sz -= n;
+               if (!aligned_sz)
+                       aligned_sz = buf_sz;
+
+               if (!partial_pack_sha1)
+                       continue;
+
+               SHA1_Update(&old_sha1_ctx, buf, n);
+               partial_pack_offset -= n;
+               if (partial_pack_offset == 0) {
+                       unsigned char sha1[20];
+                       SHA1_Final(sha1, &old_sha1_ctx);
+                       if (hashcmp(sha1, partial_pack_sha1) != 0)
+                               die("Unexpected checksum for %s "
+                                   "(disk corruption?)", pack_name);
+                       /*
+                        * Now let's compute the SHA1 of the remainder of the
+                        * pack, which also means making partial_pack_offset
+                        * big enough not to matter anymore.
+                        */
+                       SHA1_Init(&old_sha1_ctx);
+                       partial_pack_offset = ~partial_pack_offset;
+                       partial_pack_offset -= MSB(partial_pack_offset, 1);
+               }
        }
        free(buf);
 
-       SHA1_Final(pack_file_sha1, &c);
-       write_or_die(pack_fd, pack_file_sha1, 20);
+       if (partial_pack_sha1)
+               SHA1_Final(partial_pack_sha1, &old_sha1_ctx);
+       SHA1_Final(new_pack_sha1, &new_sha1_ctx);
+       write_or_die(pack_fd, new_pack_sha1, 20);
+       fsync_or_die(pack_fd, pack_name);
 }
 
 char *index_pack_lockfile(int ip_out)
diff --git a/pack.h b/pack.h
index 76e6aa2aad06545e7c44fc2c1e117ea668356ccf..a883334b269c76d8de1395adf2b8f3d0d7e8564f 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -58,7 +58,7 @@ struct pack_idx_entry {
 extern char *write_idx_file(char *index_name, struct pack_idx_entry **objects, int nr_objects, unsigned char *sha1);
 extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
 extern int verify_pack(struct packed_git *);
-extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t);
+extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
 extern char *index_pack_lockfile(int fd);
 
 #define PH_ERROR_EOF           (-1)
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 e1ca5b4a2289c59aa54f013aff54ecf1385e3030..102e6a4ce3f63ea5754eff581c17df325cc1e073 100644 (file)
@@ -57,7 +57,8 @@ require Exporter;
                 command_output_pipe command_input_pipe command_close_pipe
                 command_bidi_pipe command_close_bidi_pipe
                 version exec_path hash_object git_cmd_try
-                remote_refs);
+                remote_refs
+                temp_acquire temp_release temp_reset);
 
 
 =head1 DESCRIPTION
@@ -99,7 +100,7 @@ use Carp qw(carp croak); # but croak is bad - throw instead
 use Error qw(:try);
 use Cwd qw(abs_path);
 use IPC::Open2 qw(open2);
-
+use Fcntl qw(SEEK_SET SEEK_CUR);
 }
 
 
@@ -933,6 +934,131 @@ sub _close_cat_blob {
        delete @$self{@vars};
 }
 
+
+{ # %TEMP_* Lexical Context
+
+my (%TEMP_LOCKS, %TEMP_FILES);
+
+=item temp_acquire ( NAME )
+
+Attempts to retreive the temporary file mapped to the string C<NAME>. If an
+associated temp file has not been created this session or was closed, it is
+created, cached, and set for autoflush and binmode.
+
+Internally locks the file mapped to C<NAME>. This lock must be released with
+C<temp_release()> when the temp file is no longer needed. Subsequent attempts
+to retrieve temporary files mapped to the same C<NAME> while still locked will
+cause an error. This locking mechanism provides a weak guarantee and is not
+threadsafe. It does provide some error checking to help prevent temp file refs
+writing over one another.
+
+In general, the L<File::Handle> returned should not be closed by consumers as
+it defeats the purpose of this caching mechanism. If you need to close the temp
+file handle, then you should use L<File::Temp> or another temp file faculty
+directly. If a handle is closed and then requested again, then a warning will
+issue.
+
+=cut
+
+sub temp_acquire {
+       my ($self, $name) = _maybe_self(@_);
+
+       my $temp_fd = _temp_cache($name);
+
+       $TEMP_LOCKS{$temp_fd} = 1;
+       $temp_fd;
+}
+
+=item temp_release ( NAME )
+
+=item temp_release ( FILEHANDLE )
+
+Releases a lock acquired through C<temp_acquire()>. Can be called either with
+the C<NAME> mapping used when acquiring the temp file or with the C<FILEHANDLE>
+referencing a locked temp file.
+
+Warns if an attempt is made to release a file that is not locked.
+
+The temp file will be truncated before being released. This can help to reduce
+disk I/O where the system is smart enough to detect the truncation while data
+is in the output buffers. Beware that after the temp file is released and
+truncated, any operations on that file may fail miserably until it is
+re-acquired. All contents are lost between each release and acquire mapped to
+the same string.
+
+=cut
+
+sub temp_release {
+       my ($self, $temp_fd, $trunc) = _maybe_self(@_);
+
+       if (ref($temp_fd) ne 'File::Temp') {
+               $temp_fd = $TEMP_FILES{$temp_fd};
+       }
+       unless ($TEMP_LOCKS{$temp_fd}) {
+               carp "Attempt to release temp file '",
+                       $temp_fd, "' that has not been locked";
+       }
+       temp_reset($temp_fd) if $trunc and $temp_fd->opened;
+
+       $TEMP_LOCKS{$temp_fd} = 0;
+       undef;
+}
+
+sub _temp_cache {
+       my ($name) = @_;
+
+       _verify_require();
+
+       my $temp_fd = \$TEMP_FILES{$name};
+       if (defined $$temp_fd and $$temp_fd->opened) {
+               if ($TEMP_LOCKS{$$temp_fd}) {
+                       throw Error::Simple("Temp file with moniker '",
+                               $name, "' already in use");
+               }
+       } else {
+               if (defined $$temp_fd) {
+                       # then we're here because of a closed handle.
+                       carp "Temp file '", $name,
+                               "' was closed. Opening replacement.";
+               }
+               $$temp_fd = File::Temp->new(
+                       TEMPLATE => 'Git_XXXXXX',
+                       DIR => File::Spec->tmpdir
+                       ) or throw Error::Simple("couldn't open new temp file");
+               $$temp_fd->autoflush;
+               binmode $$temp_fd;
+       }
+       $$temp_fd;
+}
+
+sub _verify_require {
+       eval { require File::Temp; require File::Spec; };
+       $@ and throw Error::Simple($@);
+}
+
+=item temp_reset ( FILEHANDLE )
+
+Truncates and resets the position of the C<FILEHANDLE>.
+
+=cut
+
+sub temp_reset {
+       my ($self, $temp_fd) = _maybe_self(@_);
+
+       truncate $temp_fd, 0
+               or throw Error::Simple("couldn't truncate file");
+       sysseek($temp_fd, 0, SEEK_SET) and seek($temp_fd, 0, SEEK_SET)
+               or throw Error::Simple("couldn't seek to beginning of file");
+       sysseek($temp_fd, 0, SEEK_CUR) == 0 and tell($temp_fd) == 0
+               or throw Error::Simple("expected file position to be reset");
+}
+
+sub END {
+       unlink values %TEMP_FILES if %TEMP_FILES;
+}
+
+} # %TEMP_* Lexical Context
+
 =back
 
 =head1 ERROR HANDLING
index 33ef34a4119812674726254fee3f391fb5734fdb..a29c290009587a12cdc6aec335d508d29481e697 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -310,7 +310,7 @@ static int mailmap_name(struct strbuf *sb, const char *email)
 }
 
 static size_t format_person_part(struct strbuf *sb, char part,
-                               const char *msg, int len)
+                                const char *msg, int len, enum date_mode dmode)
 {
        /* currently all placeholders have same length */
        const int placeholder_len = 2;
@@ -377,7 +377,7 @@ static size_t format_person_part(struct strbuf *sb, char part,
 
        switch (part) {
        case 'd':       /* date */
-               strbuf_addstr(sb, show_date(date, tz, DATE_NORMAL));
+               strbuf_addstr(sb, show_date(date, tz, dmode));
                return placeholder_len;
        case 'D':       /* date, RFC2822 style */
                strbuf_addstr(sb, show_date(date, tz, DATE_RFC2822));
@@ -409,6 +409,7 @@ struct chunk {
 
 struct format_commit_context {
        const struct commit *commit;
+       enum date_mode dmode;
 
        /* These offsets are relative to the start of the commit message. */
        int commit_header_parsed;
@@ -584,10 +585,12 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                return 1;
        case 'a':       /* author ... */
                return format_person_part(sb, placeholder[1],
-                                  msg + c->author.off, c->author.len);
+                                  msg + c->author.off, c->author.len,
+                                  c->dmode);
        case 'c':       /* committer ... */
                return format_person_part(sb, placeholder[1],
-                                  msg + c->committer.off, c->committer.len);
+                                  msg + c->committer.off, c->committer.len,
+                                  c->dmode);
        case 'e':       /* encoding */
                strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
                return 1;
@@ -599,12 +602,14 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
 }
 
 void format_commit_message(const struct commit *commit,
-                           const void *format, struct strbuf *sb)
+                          const void *format, struct strbuf *sb,
+                          enum date_mode dmode)
 {
        struct format_commit_context context;
 
        memset(&context, 0, sizeof(context));
        context.commit = commit;
+       context.dmode = dmode;
        strbuf_expand(sb, format, format_commit_item, &context);
 }
 
@@ -770,7 +775,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
        const char *encoding;
 
        if (fmt == CMIT_FMT_USERFORMAT) {
-               format_commit_message(commit, user_format, sb);
+               format_commit_message(commit, user_format, sb, dmode);
                return;
        }
 
index 2c03ec3069decb20f7557af4ac7dbe295f2dcf9c..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;
@@ -1155,7 +1164,7 @@ int read_index_from(struct index_state *istate, const char *path)
        size_t mmap_size;
 
        errno = EBUSY;
-       if (istate->alloc)
+       if (istate->initialized)
                return istate->cache_nr;
 
        errno = ENOENT;
@@ -1195,6 +1204,7 @@ int read_index_from(struct index_state *istate, const char *path)
         * index size
         */
        istate->alloc = xmalloc(estimate_cache_size(mmap_size, istate->cache_nr));
+       istate->initialized = 1;
 
        src_offset = sizeof(*hdr);
        dst_offset = 0;
@@ -1247,6 +1257,7 @@ int discard_index(struct index_state *istate)
        cache_tree_free(&(istate->cache_tree));
        free(istate->alloc);
        istate->alloc = NULL;
+       istate->initialized = 0;
 
        /* no need to throw away allocated active_cache */
        return 0;
@@ -1477,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 d44c19e6b577023dcbaa188a0e67130ff4e5bd9a..b81678a9705c3f4bb7a6ff47e2af87db70e0ef66 100644 (file)
@@ -407,7 +407,7 @@ static const char *unpack(void)
                char keep_arg[256];
                struct child_process ip;
 
-               s = sprintf(keep_arg, "--keep=receive-pack %i on ", getpid());
+               s = sprintf(keep_arg, "--keep=receive-pack %"PRIuMAX" on ", (uintmax_t) getpid());
                if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
                        strcpy(keep_arg + s, "localhost");
 
index f61a3ab399aa6960fb8eba050321cea4a3b05344..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,8 +604,7 @@ int valid_fetch_refspec(const char *fetch_refspec_str)
        struct refspec *refspec;
 
        refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
-       if (refspec)
-               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 db2ab2b11a39c295373488f6ff28c1340a9a666d..bcbc7bd7e4257088a8504c12b60ef33d2fac9514 100644 (file)
@@ -782,6 +782,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
 
        revs->commit_format = CMIT_FMT_DEFAULT;
 
+       revs->grep_filter.status_only = 1;
+       revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
+       revs->grep_filter.regflags = REG_NEWLINE;
+
        diff_setup(&revs->diffopt);
        if (prefix && !revs->diffopt.prefix) {
                revs->diffopt.prefix = prefix;
@@ -946,15 +950,7 @@ void read_revisions_from_stdin(struct rev_info *revs)
 
 static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
 {
-       if (!revs->grep_filter) {
-               struct grep_opt *opt = xcalloc(1, sizeof(*opt));
-               opt->status_only = 1;
-               opt->pattern_tail = &(opt->pattern_list);
-               opt->regflags = REG_NEWLINE;
-               revs->grep_filter = opt;
-       }
-       append_grep_pattern(revs->grep_filter, ptn,
-                           "command line", 0, what);
+       append_grep_pattern(&revs->grep_filter, ptn, "command line", 0, what);
 }
 
 static void add_header_grep(struct rev_info *revs, const char *field, const char *pattern)
@@ -1169,17 +1165,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!prefixcmp(arg, "--grep=")) {
                add_message_grep(revs, arg+7);
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
-               if (revs->grep_filter)
-                       revs->grep_filter->regflags |= REG_EXTENDED;
+               revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
-               if (revs->grep_filter)
-                       revs->grep_filter->regflags |= REG_ICASE;
+               revs->grep_filter.regflags |= REG_ICASE;
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
-               if (revs->grep_filter)
-                       revs->grep_filter->fixed = 1;
+               revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
-               if (revs->grep_filter)
-                       revs->grep_filter->all_match = 1;
+               revs->grep_filter.all_match = 1;
        } else if (!prefixcmp(arg, "--encoding=")) {
                arg += 11;
                if (strcmp(arg, "none"))
@@ -1354,9 +1346,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (diff_setup_done(&revs->diffopt) < 0)
                die("diff_setup_done failed");
 
-       if (revs->grep_filter) {
-               compile_grep_patterns(revs->grep_filter);
-       }
+       compile_grep_patterns(&revs->grep_filter);
 
        if (revs->reverse && revs->reflog_info)
                die("cannot combine --reverse with --walk-reflogs");
@@ -1652,9 +1642,9 @@ static int rewrite_parents(struct rev_info *revs, struct commit *commit)
 
 static int commit_match(struct commit *commit, struct rev_info *opt)
 {
-       if (!opt->grep_filter)
+       if (!opt->grep_filter.pattern_list)
                return 1;
-       return grep_buffer(opt->grep_filter,
+       return grep_buffer(&opt->grep_filter,
                           NULL, /* we say nothing, not even filename */
                           commit->buffer, strlen(commit->buffer));
 }
index 765ef6c5e2f3bf0fbf84479cf83e0c0e4c82dda4..fc23522b3850edb3752181596fd83da8599e3d27 100644 (file)
@@ -2,6 +2,7 @@
 #define REVISION_H
 
 #include "parse-options.h"
+#include "grep.h"
 
 #define SEEN           (1u<<0)
 #define UNINTERESTING   (1u<<1)
@@ -93,7 +94,7 @@ struct rev_info {
        int             show_log_size;
 
        /* Filter by commit log message */
-       struct grep_opt *grep_filter;
+       struct grep_opt grep_filter;
 
        /* Display history graph */
        struct git_graph *graph;
@@ -121,7 +122,7 @@ struct rev_info {
 void read_revisions_from_stdin(struct rev_info *revs);
 
 typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
-volatile show_early_output_fn_t show_early_output;
+extern volatile show_early_output_fn_t show_early_output;
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
 extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
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 32e4664b1b52542bbd77218a4b88cef610f49649..9ee1ed16ad2df8847bd5008a5d61c807c145357a 100644 (file)
@@ -990,6 +990,7 @@ void prepare_packed_git(void)
 
 void reprepare_packed_git(void)
 {
+       discard_revindex();
        prepare_packed_git_run_once = 0;
        prepare_packed_git();
 }
@@ -2360,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;
                }
@@ -2414,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);
diff --git a/shell.c b/shell.c
index 6a48de05ff80f86050715ef3dab87a48b0a86ac9..e3393690dd3b79af80e6b95710583e744fd574d4 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -3,14 +3,6 @@
 #include "exec_cmd.h"
 #include "strbuf.h"
 
-/* Stubs for functions that make no sense for git-shell. These stubs
- * are provided here to avoid linking in external redundant modules.
- */
-void release_pack_memory(size_t need, int fd){}
-void trace_argv_printf(const char **argv, const char *fmt, ...){}
-void trace_printf(const char *fmt, ...){}
-
-
 static int do_generic_cmd(const char *me, char *arg)
 {
        const char *my_argv[4];
@@ -56,6 +48,19 @@ int main(int argc, char **argv)
 {
        char *prog;
        struct commands *cmd;
+       int devnull_fd;
+
+       /*
+        * Always open file descriptors 0/1/2 to avoid clobbering files
+        * in die().  It also avoids not messing up when the pipes are
+        * dup'ed onto stdin/stdout/stderr in the child processes we spawn.
+        */
+       devnull_fd = open("/dev/null", O_RDWR);
+       while (devnull_fd >= 0 && devnull_fd <= 2)
+               devnull_fd = dup(devnull_fd);
+       if (devnull_fd == -1)
+               die("opening /dev/null failed (%s)", strerror(errno));
+       close (devnull_fd);
 
        /*
         * Special hack to pretend to be a CVS server
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 &&
 
index 6f8a4347d5397b8b396db800c12c6e045a0d2b7c..aaed7254023b86a30c499db7c4b069c9d08b1085 100755 (executable)
@@ -36,7 +36,7 @@ test_expect_success 'setup' '
 
 test_expect_success 'am' '
 
-       git am --binary -3 <patchfile &&
+       git am -3 <patchfile &&
        git diff-files --name-status --exit-code
 
 '
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 aa9dd580a658ffd980ec9689b01f7964580661f2..5e40cec530df07a8b7c088d31b28ac2d39abdc1b 100755 (executable)
@@ -14,6 +14,8 @@ _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 compare_change () {
        sed >current \
+           -e '1{/^diff --git /d;}' \
+           -e '2{/^index /d;}' \
            -e '/^--- /d; /^+++ /d; /^@@ /d;' \
            -e 's/^\(.[0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /' "$1"
        test_cmp expected current
@@ -75,7 +77,7 @@ test_expect_success \
      git update-index --add yomin &&
      git read-tree -m -u $treeH $treeM &&
      git ls-files --stage >4.out || return 1
-     diff -U0 M.out 4.out >4diff.out
+     git diff -U0 --no-index M.out 4.out >4diff.out
      compare_change 4diff.out expected &&
      check_cache_at yomin clean &&
      sum bozbar frotz nitfol >actual4.sum &&
@@ -94,7 +96,7 @@ test_expect_success \
      echo yomin yomin >yomin &&
      git read-tree -m -u $treeH $treeM &&
      git ls-files --stage >5.out || return 1
-     diff -U0 M.out 5.out >5diff.out
+     git diff -U0 --no-index M.out 5.out >5diff.out
      compare_change 5diff.out expected &&
      check_cache_at yomin dirty &&
      sum bozbar frotz nitfol >actual5.sum &&
@@ -206,7 +208,7 @@ test_expect_success \
      git update-index --add nitfol &&
      git read-tree -m -u $treeH $treeM &&
      git ls-files --stage >14.out || return 1
-     diff -U0 M.out 14.out >14diff.out
+     git diff -U0 --no-index M.out 14.out >14diff.out
      compare_change 14diff.out expected &&
      sum bozbar frotz >actual14.sum &&
      grep -v nitfol M.sum > expected14.sum &&
@@ -227,7 +229,7 @@ test_expect_success \
      echo nitfol nitfol nitfol >nitfol &&
      git read-tree -m -u $treeH $treeM &&
      git ls-files --stage >15.out || return 1
-     diff -U0 M.out 15.out >15diff.out
+     git diff -U0 --no-index M.out 15.out >15diff.out
      compare_change 15diff.out expected &&
      check_cache_at nitfol dirty &&
      sum bozbar frotz >actual15.sum &&
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
index aff360303ae2a304bff4799def6906defdb85843..f2880152b019f4d77e3b743d7a7b02ba6d197d11 100755 (executable)
@@ -269,6 +269,17 @@ test_expect_success 'merge-recursive result' '
 
 '
 
+test_expect_success 'fail if the index has unresolved entries' '
+
+       rm -fr [abcd] &&
+       git checkout -f "$c1" &&
+
+       test_must_fail git merge "$c5" &&
+       test_must_fail git merge "$c5" 2> out &&
+       grep "You are in the middle of a conflicted merge" out
+
+'
+
 test_expect_success 'merge-recursive remove conflict' '
 
        rm -fr [abcd] &&
index 0574ef1f101df172a30755726b0ea1b6c2ef5f7d..db46d53e8271c0410a0dbf53a3560a8b635e2853 100755 (executable)
@@ -21,7 +21,7 @@ cat >"$p0" <<\EOF
 3. A quick brown fox jumps over the lazy cat, oops dog.
 EOF
 
-cat >"$p1" "$p0"
+cat 2>/dev/null >"$p1" "$p0"
 echo 'Foo Bar Baz' >"$p2"
 
 test -f "$p1" && cmp "$p0" "$p1" || {
index ffe3dd97b7b1c056d854e28795e1313ce1633452..5aa487ac02fc93c9ddbef85e430795543b976b60 100755 (executable)
@@ -202,6 +202,9 @@ test_expect_success 'retain authorship when squashing' '
 test_expect_success '-p handles "no changes" gracefully' '
        HEAD=$(git rev-parse HEAD) &&
        git rebase -i -p HEAD^ &&
+       git update-index --refresh &&
+       git diff-files --quiet &&
+       git diff-index --quiet --cached HEAD -- &&
        test $HEAD = $(git rev-parse HEAD)
 '
 
@@ -235,6 +238,9 @@ test_expect_success 'preserve merges with -p' '
        git checkout -b to-be-rebased &&
        test_tick &&
        git rebase -i -p --onto branch1 master &&
+       git update-index --refresh &&
+       git diff-files --quiet &&
+       git diff-index --quiet --cached HEAD -- &&
        test $(git rev-parse HEAD~6) = $(git rev-parse branch1) &&
        test $(git rev-parse HEAD~4^2) = $(git rev-parse to-be-preserved) &&
        test $(git rev-parse HEAD^^2^) = $(git rev-parse HEAD^^^) &&
@@ -244,6 +250,18 @@ test_expect_success 'preserve merges with -p' '
        test $(git show HEAD:unrelated-file) = 1
 '
 
+test_expect_success 'edit ancestor with -p' '
+       FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 &&
+       echo 2 > unrelated-file &&
+       test_tick &&
+       git commit -m L2-modified --amend unrelated-file &&
+       git rebase --continue &&
+       git update-index --refresh &&
+       git diff-files --quiet &&
+       git diff-index --quiet --cached HEAD -- &&
+       test $(git show HEAD:unrelated-file) = 2
+'
+
 test_expect_success '--continue tries to commit' '
        test_tick &&
        test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
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 7d123d17fc156c61a8e85a399c3762e8075485de..2ac93a346d016c65614f2bf6142049e7bdc39bd1 100755 (executable)
@@ -222,4 +222,12 @@ test_expect_success 'git add (add.ignore-errors = false)' '
        ! ( git ls-files foo1 | grep foo1 )
 '
 
+test_expect_success 'git add '\''fo\[ou\]bar'\'' ignores foobar' '
+       git reset --hard &&
+       touch fo\[ou\]bar foobar &&
+       git add '\''fo\[ou\]bar'\'' &&
+       git ls-files fo\[ou\]bar | grep -F fo\[ou\]bar &&
+       ! ( git ls-files foobar | grep foobar )
+'
+
 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 fe4fb5116ac4c482c357f0af3f0a34da27cee237..58680524258037eb0c17f7e2929d58ed1c16b574 100755 (executable)
@@ -7,12 +7,6 @@ test_description='quoted output'
 
 . ./test-lib.sh
 
-P1='pathname   with HT'
-: >"$P1" 2>&1 && test -f "$P1" && rm -f "$P1" || {
-       echo >&2 'Filesystem does not support HT in names'
-       test_done
-}
-
 FN='濱野'
 GN='純'
 HT='   '
@@ -20,7 +14,7 @@ LF='
 '
 DQ='"'
 
-echo foo > "Name and an${HT}HT"
+echo foo 2>/dev/null > "Name and an${HT}HT"
 test -f "Name and an${HT}HT" || {
        # since FAT/NTFS does not allow tabs in filenames, skip this test
        say 'Your filesystem does not allow tabs in filenames, test skipped.'
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 a27fccc8dce431169ce41f7137fb75f44149719c..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
 
@@ -341,4 +341,31 @@ test_expect_success 'checkdiff detects trailing blank lines' '
        git diff --check | grep "ends with blank"
 '
 
+test_expect_success 'checkdiff allows new blank lines' '
+       git checkout x &&
+       mv x y &&
+       (
+               echo "/* This is new */" &&
+               echo "" &&
+               cat y
+       ) >x &&
+       git diff --check
+'
+
+test_expect_success 'combined diff with autocrlf conversion' '
+
+       git reset --hard &&
+       echo >x hello &&
+       git commit -m "one side" x &&
+       git checkout HEAD^ &&
+       echo >x goodbye &&
+       git commit -m "the other side" x &&
+       git config core.autocrlf true &&
+       test_must_fail git merge master &&
+
+       git diff | sed -e "1,/^@@@/d" >actual &&
+       ! grep "^-" actual
+
+'
+
 test_done
index f07035ab7ec72557be7a0cba9ea286bcbaa79da9..55eb5f83f17c0ebfb537d390fd3913b7c89d65f4 100755 (executable)
@@ -13,8 +13,8 @@ P1='pathname  with HT'
 P2='pathname with SP'
 P3='pathname
 with LF'
-: >"$P1" 2>&1 && test -f "$P1" && rm -f "$P1" || {
-       echo >&2 'Filesystem does not support tabs in names'
+: 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" || {
+       say 'Your filesystem does not allow tabs in filenames, test skipped.'
        test_done
 }
 
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 e7e2913de745cc9f7639103757933f6238fdd564..0e3ce3611d9e83ab290ce034f2439961864ce30a 100755 (executable)
@@ -27,6 +27,15 @@ test_expect_success setup '
        git diff victim >add-a-patch.with &&
        git diff --unified=0 >add-a-patch.without &&
 
+       : insert at line two
+       for i in b a '"$L"' y
+       do
+               echo $i
+       done >victim &&
+       cat victim >insert-a-expect &&
+       git diff victim >insert-a-patch.with &&
+       git diff --unified=0 >insert-a-patch.without &&
+
        : modify at the head
        for i in a '"$L"' y
        do
@@ -55,7 +64,7 @@ test_expect_success setup '
        git diff --unified=0 >add-z-patch.without &&
 
        : modify at the tail
-       for i in a '"$L"' y
+       for i in b '"$L"' z
        do
                echo $i
        done >victim &&
@@ -81,7 +90,7 @@ do
        with) u= ;;
        without) u='--unidiff-zero ' ;;
        esac
-       for kind in add-a add-z mod-a mod-z del-a del-z
+       for kind in add-a add-z insert-a mod-a mod-z del-a del-z
        do
                test_expect_success "apply $kind-patch $with context" '
                        cat original >victim &&
@@ -95,7 +104,7 @@ do
        done
 done
 
-for kind in add-a add-z mod-a mod-z del-a del-z
+for kind in add-a add-z insert-a mod-a mod-z del-a del-z
 do
        rm -f $kind-ng.without
        sed     -e "s/^diff --git /diff /" \
index 4c8af45f834d034529c2a627768a0a3e6f1aac8d..0ab925c4e4710a560f0d35e47ccdda8ddb2b8212 100755 (executable)
@@ -69,7 +69,29 @@ test_expect_success 'diff-filter=D' '
 
 '
 
+test_expect_success 'setup case sensitivity tests' '
+       echo case >one &&
+       test_tick &&
+       git commit -a -m Second
+'
+
+test_expect_success 'log --grep' '
+       echo second >expect &&
+       git log -1 --pretty="tformat:%s" --grep=sec >actual &&
+       test_cmp expect actual
+'
 
+test_expect_success 'log -i --grep' '
+       echo Second >expect &&
+       git log -1 --pretty="tformat:%s" -i --grep=sec >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'log --grep -i' '
+       echo Second >expect &&
+       git log -1 --pretty="tformat:%s" --grep=sec -i >actual &&
+       test_cmp expect actual
+'
 
 test_done
 
index 8dfaddda9129b476016afa88c518e51511da878c..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,21 @@ 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
+
+'
+
+test_expect_success 'mailinfo on from header without name works' '
+
+       mkdir info-from &&
+       git mailsplit -oinfo-from "$TEST_DIRECTORY"/t5100/info-from.in &&
+       test_cmp "$TEST_DIRECTORY"/t5100/info-from.in info-from/0001 &&
+       git mailinfo info-from/msg info-from/patch \
+         <info-from/0001 >info-from/out &&
+       test_cmp "$TEST_DIRECTORY"/t5100/info-from.expect info-from/out
 
 '
 
diff --git a/t/t5100/info-from.expect b/t/t5100/info-from.expect
new file mode 100644 (file)
index 0000000..c31d2eb
--- /dev/null
@@ -0,0 +1,5 @@
+Author: bare@example.com
+Email: bare@example.com
+Subject: testing bare address in from header
+Date: Sun, 25 May 2008 00:38:18 -0700
+
diff --git a/t/t5100/info-from.in b/t/t5100/info-from.in
new file mode 100644 (file)
index 0000000..4f08209
--- /dev/null
@@ -0,0 +1,8 @@
+From 667d8940e719cddee1cfe237cbbe215e20270b09 Mon Sep 17 00:00:00 2001
+From: bare@example.com
+Date: Sun, 25 May 2008 00:38:18 -0700
+Subject: [PATCH] testing bare address in from header
+
+commit message
+---
+patch
index d7ca79b1fc1c5842cb0ebd95cc7055459f0391a0..4bf7947b418963e9b15e393fc738e515b3d2141d 100644 (file)
@@ -500,3 +500,4 @@ index 3e5fe51..aabfe5c 100644
 1.6.0.rc2
 
 --=-=-=--
+
index 645583f9d729cb04ea7bd9638b0c49c48128822e..83abe5f25eb4eb0627d47c233be27f1dd77d4455 100755 (executable)
@@ -186,6 +186,12 @@ test_expect_success \
                        test-2-${packname_2}.idx \
                        test-3-${packname_3}.idx'
 
+test_expect_success \
+    'verify pack -v' \
+    'git verify-pack -v        test-1-${packname_1}.idx \
+                       test-2-${packname_2}.idx \
+                       test-3-${packname_3}.idx'
+
 test_expect_success \
     'verify-pack catches mismatched .idx and .pack files' \
     'cat test-1-${packname_1}.idx >test-3.idx &&
index 9fd9d0700033027921c90290cedc0a31d71c7733..771c0a06a4bd01cf40628ffae379b5d992802ef6 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success 'prune stale packs' '
        orig_pack=$(echo .git/objects/pack/*.pack) &&
        : > .git/objects/tmp_1.pack &&
        : > .git/objects/tmp_2.pack &&
-       test-chmtime -86501 .git/objects/tmp_1.pack &&
+       test-chmtime =-86501 .git/objects/tmp_1.pack &&
        git prune --expire 1.day &&
        test -f $orig_pack &&
        test -f .git/objects/tmp_2.pack &&
@@ -39,7 +39,7 @@ test_expect_success 'prune --expire' '
        git prune --expire=1.hour.ago &&
        test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
        test -f $BLOB_FILE &&
-       test-chmtime -86500 $BLOB_FILE &&
+       test-chmtime =-86500 $BLOB_FILE &&
        git prune --expire 1.day &&
        test $before = $(git count-objects | sed "s/ .*//") &&
        ! test -f $BLOB_FILE
@@ -53,11 +53,11 @@ test_expect_success 'gc: implicit prune --expire' '
        BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
        test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
        test -f $BLOB_FILE &&
-       test-chmtime -$((86400*14-30)) $BLOB_FILE &&
+       test-chmtime =-$((86400*14-30)) $BLOB_FILE &&
        git gc &&
        test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
        test -f $BLOB_FILE &&
-       test-chmtime -$((86400*14+1)) $BLOB_FILE &&
+       test-chmtime =-$((86400*14+1)) $BLOB_FILE &&
        git gc &&
        test $before = $(git count-objects | sed "s/ .*//") &&
        ! test -f $BLOB_FILE
diff --git a/t/t5306-pack-nobase.sh b/t/t5306-pack-nobase.sh
new file mode 100755 (executable)
index 0000000..f4931c0
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Google Inc.
+#
+
+test_description='git-pack-object with missing base
+
+'
+. ./test-lib.sh
+
+# Create A-B chain
+#
+test_expect_success \
+    'setup base' \
+    'for a in a b c d e f g h i; do echo $a >>text; done &&
+     echo side >side &&
+     git update-index --add text side &&
+     A=$(echo A | git commit-tree $(git write-tree)) &&
+
+     echo m >>text &&
+     git update-index text &&
+     B=$(echo B | git commit-tree $(git write-tree) -p $A) &&
+     git update-ref HEAD $B
+    '
+
+# Create repository with C whose parent is B.
+# Repository contains C, C^{tree}, C:text, B, B^{tree}.
+# Repository is missing B:text (best delta base for C:text).
+# Repository is missing A (parent of B).
+# Repository is missing A:side.
+#
+test_expect_success \
+    'setup patch_clone' \
+    'base_objects=$(pwd)/.git/objects &&
+     (mkdir patch_clone &&
+      cd patch_clone &&
+      git init &&
+      echo "$base_objects" >.git/objects/info/alternates &&
+      echo q >>text &&
+      git read-tree $B &&
+      git update-index text &&
+      git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) &&
+      rm .git/objects/info/alternates &&
+
+      git --git-dir=../.git cat-file commit $B |
+      git hash-object -t commit -w --stdin &&
+
+      git --git-dir=../.git cat-file tree "$B^{tree}" |
+      git hash-object -t tree -w --stdin
+     ) &&
+     C=$(git --git-dir=patch_clone/.git rev-parse HEAD)
+    '
+
+# Clone patch_clone indirectly by cloning base and fetching.
+#
+test_expect_success \
+    'indirectly clone patch_clone' \
+    '(mkdir user_clone &&
+      cd user_clone &&
+      git init &&
+      git pull ../.git &&
+      test $(git rev-parse HEAD) = $B &&
+
+      git pull ../patch_clone/.git &&
+      test $(git rev-parse HEAD) = $C
+     )
+    '
+
+# Cloning the patch_clone directly should fail.
+#
+test_expect_success \
+    'clone of patch_clone is incomplete' \
+    '(mkdir user_direct &&
+      cd user_direct &&
+      git init &&
+      test_must_fail git fetch ../patch_clone/.git
+     )
+    '
+
+test_done
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 be9ee9326fc4590dcc875e31b6cf64b800451bc5..c4496635dfba85b0e9fc105c4516096d433881a4 100755 (executable)
@@ -109,7 +109,7 @@ test_expect_success 'remove remote' '
 
 cat > test/expect << EOF
 * remote origin
-  URL: $(pwd)/one/.git
+  URL: $(pwd)/one
   Remote branch merged with 'git pull' while on branch master
     master
   New remote branch (next fetch will store in remotes/origin)
@@ -140,7 +140,7 @@ test_expect_success 'show' '
 
 cat > test/expect << EOF
 * remote origin
-  URL: $(pwd)/one/.git
+  URL: $(pwd)/one
   Remote branch merged with 'git pull' while on branch master
     master
   Tracked remote branches
@@ -169,7 +169,7 @@ test_expect_success 'prune' '
 
 cat > test/expect << EOF
 Pruning origin
-URL: $(pwd)/one/.git
+URL: $(pwd)/one
  * [would prune] origin/side2
 EOF
 
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 9176484db2f78122f71c0f11889e01382effcfb9..485ad4d44a6b9714c1986d6cff9c02263e1eb0f1 100755 (executable)
@@ -139,6 +139,12 @@ commit 131a310eb913d107dd3c09a65d1651175898735d
 commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
 EOF
 
+test_expect_success '%ad respects --date=' '
+       echo 2005-04-07 >expect.ad-short &&
+       git log -1 --date=short --pretty=tformat:%ad >output.ad-short master &&
+       test_cmp expect.ad-short output.ad-short
+'
+
 test_expect_success 'empty email' '
        test_tick &&
        C=$(GIT_AUTHOR_EMAIL= git commit-tree HEAD^{tree} </dev/null) &&
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 a3c8941c726d77fd993a3cfcd7fde4e9aa43da74..8ced59321ef69b6ea2fe3a011f900affea83336e 100755 (executable)
@@ -262,4 +262,14 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
        "
 done
 
+test_expect_success 'an unusual tag with an incomplete line' '
+
+       git tag -m "bogo" bogo &&
+       bogo=$(git cat-file tag bogo) &&
+       bogo=$(printf "%s" "$bogo" | git mktag) &&
+       git tag -f bogo "$bogo" &&
+       git for-each-ref --format "%(body)" refs/tags/bogo
+
+'
+
 test_done
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 233254f2b541b7c67d99aa738eb55f0a0a6d3131..95f13a8b2ba41b95f04d667ad18b4a07c8ee9d15 100755 (executable)
@@ -254,4 +254,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 5eeb6c2b2708d582a6e86cd2e06e2b00b7b7b391..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 &&
@@ -488,4 +492,23 @@ test_expect_success 'merge c1 with c1 and c2' '
 
 test_debug 'gitk --all'
 
+test_expect_success 'merge fast-forward in a dirty tree' '
+       git reset --hard c0 &&
+       mv file file1 &&
+       cat file1 >file &&
+       rm -f file1 &&
+       git merge c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'in-index merge' '
+       git reset --hard c0 &&
+       git merge --no-ff -s resolve c1 > out &&
+       grep "Wonderful." out &&
+       verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
 test_done
index ee21a107fd99e35b9d6325a040b7549b7a6c2502..f1f86ddb2381ce70e6845298e86ed96d6399fdf9 100755 (executable)
@@ -36,7 +36,9 @@ test_expect_success 'merge c1 to c2' '
        git diff --exit-code &&
        test -f c0.c &&
        test -f c1.c &&
-       test -f c2.c
+       test -f c2.c &&
+       test 3 = $(git ls-tree -r HEAD | wc -l) &&
+       test 3 = $(git ls-files | wc -l)
 '
 
 test_expect_success 'merge c2 to c3 (fails)' '
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 1fc06c5a23b50d54c33755a9fce4ddd9ed3b9c79..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'
@@ -1045,7 +1045,7 @@ DATA
 INPUT_END
 
 test_expect_success 'P: fail on inline gitlink' '
-    ! git-fast-import <input'
+    test_must_fail git-fast-import <input'
 
 test_tick
 cat >input <<INPUT_END
@@ -1068,6 +1068,6 @@ M 160000 :1 sub
 INPUT_END
 
 test_expect_success 'P: fail on blob mark in gitlink' '
-    ! git-fast-import <input'
+    test_must_fail git-fast-import <input'
 
 test_done
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)
@@ -14,10 +14,7 @@ use File::Temp;
 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 9f3f1fc352dea624bd36e55802de190ead0ad9dd..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 ;; \
@@ -48,4 +48,4 @@ clean:
 install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(template_instdir_SQ)'
        (cd blt && $(TAR) cf - .) | \
-       (cd '$(DESTDIR_SQ)$(template_instdir_SQ)' && umask 022 && $(TAR) xf -)
+       (cd '$(DESTDIR_SQ)$(template_instdir_SQ)' && umask 022 && $(TAR) xfo -)
index 6e18083a7d1655e9eabef197c58d92a910d2f67b..61d2c39814529bd0264e4c9e40241131d51d819c 100644 (file)
@@ -15,7 +15,7 @@ int length_callback(const struct option *opt, const char *arg, int unset)
        if (unset)
                return 1; /* do not support unset */
 
-       *(unsigned long *)opt->value = strlen(arg);
+       *(int *)opt->value = strlen(arg);
        return 0;
 }
 
index cba0aca062f201c5cd5f8799f2190d4a6f06e7c7..ef21c62195d61980d4727e3f6d9c285422fcfe91 100644 (file)
@@ -376,6 +376,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        state.refresh_cache = 1;
 
        memset(&o->result, 0, sizeof(o->result));
+       o->result.initialized = 1;
        if (o->src_index)
                o->result.timestamp = o->src_index->timestamp;
        o->merge_size = len;
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);