Merge branch 'jc/grep'
authorJunio C Hamano <junkio@cox.net>
Mon, 10 Jul 2006 06:49:03 +0000 (23:49 -0700)
committerJunio C Hamano <junkio@cox.net>
Mon, 10 Jul 2006 06:49:03 +0000 (23:49 -0700)
* jc/grep:
  git-grep: boolean expression on pattern matching.

80 files changed:
.gitignore
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/cvs-migration.txt
Documentation/diff-options.txt
Documentation/git-cvsexportcommit.txt
Documentation/git-diff-files.txt
Documentation/git-diff.txt
Documentation/git-init-db.txt
Documentation/git-instaweb.txt [new file with mode: 0644]
Documentation/git-mailsplit.txt
Documentation/git-merge.txt
Documentation/git-p4import.txt
Documentation/git-pack-redundant.txt
Documentation/git-push.txt
Documentation/git-repo-config.txt
Documentation/git-rev-list.txt
Documentation/git-rev-parse.txt
Documentation/git-show-branch.txt
Documentation/git-svn.txt [moved from contrib/git-svn/git-svn.txt with 99% similarity]
Documentation/git-tools.txt
Documentation/git-upload-tar.txt
Documentation/git.txt
Documentation/glossary.txt
Documentation/howto/isolate-bugs-with-bisect.txt
Documentation/howto/rebase-from-internal-branch.txt
Documentation/technical/pack-heuristics.txt
Documentation/urls.txt
INSTALL
Makefile
builtin-add.c
builtin-apply.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-stages.c
builtin-diff-tree.c
builtin-diff.c
builtin-log.c
builtin-mailinfo.c
builtin-read-tree.c
builtin-rev-parse.c
builtin-rm.c
builtin-show-branch.c
builtin-update-index.c
builtin-write-tree.c
cache.h
checkout-index.c
commit.c
commit.h
contrib/git-svn/.gitignore [deleted file]
contrib/git-svn/Makefile [deleted file]
contrib/git-svn/t/lib-git-svn.sh [deleted file]
diff.c
diff.h
git-bisect.sh
git-clone.sh
git-commit.sh
git-cvsexportcommit.perl
git-cvsserver.perl
git-fetch.sh
git-instaweb.sh [new file with mode: 0755]
git-rebase.sh
git-reset.sh
git-send-email.perl
git-svn.perl [moved from contrib/git-svn/git-svn.perl with 99% similarity]
git.c
merge-base.c
pager.c
refs.c
revision.c
server-info.c
t/Makefile
t/lib-git-svn.sh [new file with mode: 0644]
t/t9100-git-svn-basic.sh [moved from contrib/git-svn/t/t0000-contrib-git-svn.sh with 98% similarity, mode: 0755]
t/t9101-git-svn-props.sh [moved from contrib/git-svn/t/t0001-contrib-git-svn-props.sh with 100% similarity, mode: 0755]
t/t9102-git-svn-deep-rmdir.sh [moved from contrib/git-svn/t/t0002-deep-rmdir.sh with 100% similarity, mode: 0755]
t/t9103-git-svn-graft-branches.sh [moved from contrib/git-svn/t/t0003-graft-branches.sh with 100% similarity, mode: 0755]
t/t9104-git-svn-follow-parent.sh [moved from contrib/git-svn/t/t0004-follow-parent.sh with 100% similarity, mode: 0755]
t/t9105-git-svn-commit-diff.sh [moved from contrib/git-svn/t/t0005-commit-diff.sh with 100% similarity, mode: 0755]
templates/hooks--update

index 7b954d5..52d61f3 100644 (file)
@@ -46,6 +46,7 @@ git-http-push
 git-imap-send
 git-index-pack
 git-init-db
+git-instaweb
 git-local-fetch
 git-log
 git-lost-found
@@ -106,6 +107,7 @@ git-ssh-push
 git-ssh-upload
 git-status
 git-stripspace
+git-svn
 git-svnimport
 git-symbolic-ref
 git-tag
index 8601949..90722c2 100644 (file)
@@ -49,7 +49,7 @@ People on the git mailing list need to be able to read and
 comment on the changes you are submitting.  It is important for
 a developer to be able to "quote" your changes, using standard
 e-mail tools, so that they may comment on specific portions of
-your code.  For this reason, all patches should be submited
+your code.  For this reason, all patches should be submitted
 "inline".  WARNING: Be wary of your MUAs word-wrap
 corrupting your patch.  Do not cut-n-paste your patch; you can
 lose tabs that way if you are not careful.
index 16bdd55..0b434c1 100644 (file)
@@ -91,7 +91,7 @@ core.warnAmbiguousRefs::
        If true, git will warn you if the ref name you passed it is ambiguous
        and might match multiple refs in the .git/refs/ tree. True by default.
 
-core.compression:
+core.compression::
        An integer -1..9, indicating the compression level for objects that
        are not in a pack file. -1 is the zlib and git default. 0 means no
        compression, and 1..9 are various speed/size tradeoffs, 9 being
@@ -110,10 +110,31 @@ apply.whitespace::
        Tells `git-apply` how to handle whitespaces, in the same way
        as the '--whitespace' option. See gitlink:git-apply[1].
 
+diff.color::
+       When true (or `always`), always use colors in patch.
+       When false (or `never`), never.  When set to `auto`, use
+       colors only when the output is to the terminal.
+
+diff.color.<slot>::
+       Use customized color for diff colorization.  `<slot>`
+       specifies which part of the patch to use the specified
+       color, and is one of `plain` (context text), `meta`
+       (metainformation), `frag` (hunk header), `old` (removed
+       lines), or `new` (added lines).  The value for these
+       configuration variables can be one of: `normal`, `bold`,
+       `dim`, `ul`, `blink`, `reverse`, `reset`, `black`,
+       `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, or
+       `white`.
+
 diff.renameLimit::
        The number of files to consider when performing the copy/rename
        detection; equivalent to the git diff option '-l'.
 
+diff.renames::
+       Tells git to detect renames.  If set to any boolean value, it
+       will enable basic rename detection.  If set to "copies" or
+       "copy", it will detect copies, as well.
+
 format.headers::
        Additional email headers to include in a patch to be submitted
        by mail.  See gitlink:git-format-patch[1].
index 1fbca83..d2b0bd3 100644 (file)
@@ -93,7 +93,7 @@ machine where the repository is hosted.  If you don't want to give them a
 full shell on the machine, there is a restricted shell which only allows
 users to do git pushes and pulls; see gitlink:git-shell[1].
 
-Put all the committers should in the same group, and make the repository
+Put all the committers in the same group, and make the repository
 writable by that group:
 
 ------------------------------------------------
index f523ec2..47ba9a4 100644 (file)
@@ -4,18 +4,21 @@
 -u::
        Synonym for "-p".
 
+--raw::
+       Generate the raw format.
+
 --patch-with-raw::
-       Generate patch but keep also the default raw diff output.
+       Synonym for "-p --raw".
 
 --stat::
-       Generate a diffstat instead of a patch.
+       Generate a diffstat.
 
 --summary::
        Output a condensed summary of extended header information
        such as creations, renames and mode changes.
 
 --patch-with-stat::
-       Generate patch and prepend its diffstat.
+       Synonym for "-p --stat".
 
 -z::
        \0 line termination on output
 --name-status::
        Show only names and status of changed files.
 
+--color::
+       Show colored diff.
+
+--no-color::
+       Turn off colored diff, even when the configuration file
+       gives the default to color output.
+
+--no-renames::
+       Turn off rename detection, even when the configuration
+       file gives the default to do so.
+
 --full-index::
        Instead of the first handful characters, show full
        object name of pre- and post-image blob on the "index"
-       line when generating a patch format output.     
+       line when generating a patch format output.
+
+--binary::
+       In addition to --full-index, output "binary diff" that
+       can be applied with "git apply".
 
 --abbrev[=<n>]::
        Instead of showing the full 40-byte hexadecimal object
        Swap two inputs; that is, show differences from index or
        on-disk file to tree contents.
 
+--text::
+       Treat all files as text.
+
+-a::
+       Shorthand for "--text".
+
 For more detailed explanation on these common options, see also
 link:diffcore.html[diffcore documentation].
index 56bd3e5..27ac72d 100644 (file)
@@ -8,7 +8,7 @@ git-cvsexportcommit - Export a commit to a CVS checkout
 
 SYNOPSIS
 --------
-'git-cvsexportcommmit' [-h] [-v] [-c] [-p] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
+'git-cvsexportcommit' [-h] [-v] [-c] [-p] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
 
 
 DESCRIPTION
index 481b8b3..7248b35 100644 (file)
@@ -37,7 +37,7 @@ omit diff output for unmerged entries and just show "Unmerged".
        commit with these flags.
 
 -q::
-       Remain silent even on nonexisting files
+       Remain silent even on nonexistent files
 
 Output format
 -------------
index 7ab2080..228c4d9 100644 (file)
@@ -8,24 +8,24 @@ git-diff - Show changes between commits, commit and working tree, etc
 
 SYNOPSIS
 --------
-'git-diff' [ --diff-options ] <ent>{0,2} [<path>...]
+'git-diff' [ --diff-options ] <tree-ish>{0,2} [<path>...]
 
 DESCRIPTION
 -----------
-Show changes between two ents, an ent and the working tree, an
-ent and the index file, or the index file and the working tree.
+Show changes between two trees, a tree and the working tree, a
+tree and the index file, or the index file and the working tree.
 The combination of what is compared with what is determined by
-the number of ents given to the command.
+the number of trees given to the command.
 
-* When no <ent> is given, the working tree and the index
-  file is compared, using `git-diff-files`.
+* When no <tree-ish> is given, the working tree and the index
+  file are compared, using `git-diff-files`.
 
-* When one <ent> is given, the working tree and the named
-  tree is compared, using `git-diff-index`.  The option
+* When one <tree-ish> is given, the working tree and the named
+  tree are compared, using `git-diff-index`.  The option
   `--cached` can be given to compare the index file and
   the named tree.
 
-* When two <ent>s are given, these two trees are compared
+* When two <tree-ish>s are given, these two trees are compared
   using `git-diff-tree`.
 
 OPTIONS
index 8a150d8..0a4fc14 100644 (file)
@@ -25,7 +25,7 @@ DESCRIPTION
 -----------
 This command creates an empty git repository - basically a `.git` directory
 with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
-templated files.
+template files.
 An initial `HEAD` file that references the HEAD of the master branch
 is also created.
 
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
new file mode 100644 (file)
index 0000000..7dd393b
--- /dev/null
@@ -0,0 +1,84 @@
+git-instaweb(1)
+===============
+
+NAME
+----
+git-instaweb - instantly browse your working repository in gitweb
+
+SYNOPSIS
+--------
+'git-instaweb' [--local] [--httpd=<httpd>] [--port=<port>] [--browser=<browser>]
+
+'git-instaweb' [--start] [--stop] [--restart]
+
+DESCRIPTION
+-----------
+A simple script to setup gitweb and a web server for browsing the local
+repository.
+
+OPTIONS
+-------
+
+-l|--local::
+       Only bind the web server to the local IP (127.0.0.1).
+
+-d|--httpd::
+       The HTTP daemon command-line that will be executed.
+       Command-line options may be specified here, and the
+       configuration file will be added at the end of the command-line.
+       Currently, lighttpd and apache2 are the only supported servers.
+       (Default: lighttpd)
+
+-m|--module-path::
+       The module path (only needed if httpd is Apache).
+       (Default: /usr/lib/apache2/modules)
+
+-p|--port::
+       The port number to bind the httpd to.  (Default: 1234)
+
+-b|--browser::
+
+       The web browser command-line to execute to view the gitweb page.
+       If blank, the URL of the gitweb instance will be printed to
+       stdout.  (Default: 'firefox')
+
+--start::
+       Start the httpd instance and exit.  This does not generate
+       any of the configuration files for spawning a new instance.
+
+--stop::
+       Stop the httpd instance and exit.  This does not generate
+       any of the configuration files for spawning a new instance,
+       nor does it close the browser.
+
+--restart::
+       Restart the httpd instance and exit.  This does not generate
+       any of the configuration files for spawning a new instance.
+
+CONFIGURATION
+-------------
+
+You may specify configuration in your .git/config
+
+-----------------------------------------------------------------------
+[instaweb]
+       local = true
+       httpd = apache2 -f
+       port = 4321
+       browser = konqueror
+       modulepath = /usr/lib/apache2/modules
+
+-----------------------------------------------------------------------
+
+Author
+------
+Written by Eric Wong <normalperson@yhbt.net>
+
+Documentation
+--------------
+Documentation by Eric Wong <normalperson@yhbt.net>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
index 209e36b..5a17801 100644 (file)
@@ -25,7 +25,7 @@ OPTIONS
 
 -b::
        If any file doesn't begin with a From line, assume it is a
-       single mail message instead of signalling error.
+       single mail message instead of signaling error.
 
 -d<prec>::
        Instead of the default 4 digits with leading zeros,
index 4ce799b..bebf30a 100644 (file)
@@ -83,7 +83,7 @@ your local modifications interfere with the merge, again, it
 stops before touching anything.
 
 So in the above two "failed merge" case, you do not have to
-worry about lossage of data --- you simply were not ready to do
+worry about loss of data --- you simply were not ready to do
 a merge, so no merge happened at all.  You may want to finish
 whatever you were in the middle of doing, and retry the same
 pull after you are done and ready.
index 0858e5e..ee9e8fa 100644 (file)
@@ -128,7 +128,7 @@ Tags
 A git tag of the form p4/xx is created for every change imported from
 the Perforce repository where xx is the Perforce changeset number.
 Therefore after the import you can use git to access any commit by its
-Perforce number, eg. git show p4/327.
+Perforce number, e.g. git show p4/327.
 
 The tag associated with the HEAD commit is also how `git-p4import`
 determines if there are new changes to incrementally import from the
@@ -143,7 +143,7 @@ may delete the tags.
 
 Notes
 -----
-You can interrupt the import (eg. ctrl-c) at any time and restart it
+You can interrupt the import (e.g. ctrl-c) at any time and restart it
 without worry.
 
 Author information is automatically determined by querying the
index 8fb0659..7d54b17 100644 (file)
@@ -29,7 +29,7 @@ OPTIONS
 
 
 --all::
-       Processes all packs. Any filenames on the commandline are ignored.
+       Processes all packs. Any filenames on the command line are ignored.
 
 --alt-odb::
        Don't require objects present in packs from alternate object
index d5b5ca1..56afd64 100644 (file)
@@ -67,7 +67,7 @@ Some short-cut notations are also supported.
 
 -f, \--force::
        Usually, the command refuses to update a remote ref that is
-       not a descendent of the local ref used to overwrite it.
+       not a descendant of the local ref used to overwrite it.
        This flag disables the check.  This can cause the
        remote repository to lose commits; use it with care.
 
index 803c0d5..b03d66f 100644 (file)
@@ -119,8 +119,8 @@ you can set the filemode to true with
 % git repo-config core.filemode true
 ------------
 
-The hypothetic proxy command entries actually have a postfix to discern
-to what URL they apply. Here is how to change the entry for kernel.org
+The hypothetical proxy command entries actually have a postfix to discern
+what URL they apply to. Here is how to change the entry for kernel.org
 to "ssh".
 
 ------------
index ad6d14c..e220842 100644 (file)
@@ -15,6 +15,7 @@ SYNOPSIS
             [ \--sparse ]
             [ \--no-merges ]
             [ \--remove-empty ]
+            [ \--not ]
             [ \--all ]
             [ \--topo-order ]
             [ \--parents ]
@@ -37,6 +38,14 @@ not in 'baz'".
 A special notation <commit1>..<commit2> can be used as a
 short-hand for {caret}<commit1> <commit2>.
 
+Another special notation is <commit1>...<commit2> which is useful for
+merges.  The resulting set of commits is the symmetric difference
+between the two operands.  The following two commands are equivalent:
+
+------------
+$ git-rev-list A B --not $(git-merge-base --all A B)
+$ git-rev-list A...B
+------------
 
 OPTIONS
 -------
@@ -55,7 +64,7 @@ OPTIONS
 
 --objects-edge::
        Similar to `--objects`, but also print the IDs of
-       excluded commits refixed with a `-` character.  This is
+       excluded commits prefixed with a `-` character.  This is
        used by `git-pack-objects` to build 'thin' pack, which
        records objects in deltified form based on objects
        contained in these excluded commits to reduce network
@@ -93,6 +102,11 @@ OPTIONS
 --remove-empty::
        Stop when a given path disappears from the tree.
 
+--not::
+       Reverses the meaning of the '{caret}' prefix (or lack
+       thereof) for all following revision specifiers, up to
+       the next `--not`.
+
 --all::
        Pretend as if all the refs in `$GIT_DIR/refs/` are
        listed on the command line as <commit>.
index 627cde8..b761b4b 100644 (file)
@@ -156,11 +156,6 @@ syntax.
   and dereference the tag recursively until a non-tag object is
   found.
 
-'git-rev-parse' also accepts a prefix '{caret}' to revision parameter,
-which is passed to 'git-rev-list'.  Two revision parameters
-concatenated with '..' is a short-hand for writing a range
-between them.  I.e. 'r1..r2' is equivalent to saying '{caret}r1 r2'
-
 Here is an illustration, by Jon Loeliger.  Both node B and C are
 a commit parents of commit node A.  Parent commits are ordered
 left-to-right.
@@ -168,9 +163,9 @@ left-to-right.
     G   H   I   J
      \ /     \ /
       D   E   F
-       \  |  /
-        \ | /
-         \|/
+       \  |  / \
+        \ | /   |
+         \|/    |
           B     C
            \   /
             \ /
@@ -188,6 +183,40 @@ left-to-right.
     J = F^2  = B^3^2   = A^^3^2
 
 
+SPECIFYING RANGES
+-----------------
+
+History traversing commands such as `git-log` operate on a set
+of commits, not just a single commit.  To these commands,
+specifying a single revision with the notation described in the
+previous section means the set of commits reachable from that
+commit, following the commit ancestry chain.
+
+To exclude commits reachable from a commit, a prefix `{caret}`
+notation is used.  E.g. "`{caret}r1 r2`" means commits reachable
+from `r2` but exclude the ones reachable from `r1`.
+
+This set operation appears so often that there is a shorthand
+for it.  "`r1..r2`" is equivalent to "`{caret}r1 r2`".  It is
+the difference of two sets (subtract the set of commits
+reachable from `r1` from the set of commits reachable from
+`r2`).
+
+A similar notation "`r1\...r2`" is called symmetric difference
+of `r1` and `r2` and is defined as
+"`r1 r2 --not $(git-merge-base --all r1 r2)`".
+It it the set of commits that are reachable from either one of
+`r1` or `r2` but not from both.
+
+Here are a few examples:
+
+   D                A B D
+   D F              A B C D F
+   ^A G                    B D
+   ^A F             B C F
+   G...I            C D F G I
+   ^B G I          C D F G I
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org> and
index f115b45..a2445a4 100644 (file)
@@ -52,6 +52,11 @@ OPTIONS
         appear in topological order (i.e., descendant commits
         are shown before their parents).
 
+--sparse::
+       By default, the output omits merges that are reachable
+       from only one tip being shown.  This option makes them
+       visible.
+
 --more=<n>::
        Usually the command stops output upon showing the commit
        that is the common ancestor of all the branches.  This
similarity index 99%
rename from contrib/git-svn/git-svn.txt
rename to Documentation/git-svn.txt
index f7d3de4..7d86809 100644 (file)
@@ -224,7 +224,7 @@ Merge tracking in Subversion is lacking and doing branched development
 with Subversion is cumbersome as a result.  git-svn completely forgoes
 any automated merge/branch tracking on the Subversion side and leaves it
 entirely up to the user on the git side.  It's simply not worth it to do
-a useful translation when the the original signal is weak.
+a useful translation when the original signal is weak.
 
 TRACKING MULTIPLE REPOSITORIES OR BRANCHES
 ------------------------------------------
index d79523f..0914cbb 100644 (file)
@@ -42,7 +42,7 @@ History Viewers
 
    - *gitk* (shipped with git-core)
 
-   gitk is a simple TK GUI for browsing history of GIT repositories easily.
+   gitk is a simple Tk GUI for browsing history of GIT repositories easily.
 
 
    - *gitview*  (contrib/)
index a1019a0..394af62 100644 (file)
@@ -17,7 +17,7 @@ to the other end over the git protocol.
 
 This command is usually not invoked directly by the end user.
 The UI for the protocol is on the 'git-tar-tree' side, and the
-program pair is meant to be used to get a tar achive from a
+program pair is meant to be used to get a tar archive from a
 remote repository.
 
 
index 51f20c6..d00cc3e 100644 (file)
@@ -478,7 +478,7 @@ Configuration Mechanism
 
 Starting from 0.99.9 (actually mid 0.99.8.GIT), `.git/config` file
 is used to hold per-repository configuration options.  It is a
-simple text file modelled after `.ini` format familiar to some
+simple text file modeled after `.ini` format familiar to some
 people.  Here is an example:
 
 ------------
index 116ddb7..14449ca 100644 (file)
@@ -86,7 +86,7 @@ directory::
 ent::
        Favorite synonym to "tree-ish" by some total geeks. See
        `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
-       explanation.
+       explanation.  Avoid this term, not to confuse people.
 
 fast forward::
        A fast-forward is a special type of merge where you have
index edbcd4c..926bbdc 100644 (file)
@@ -28,7 +28,7 @@ Then do
 
 and at this point "git bisect" will churn for a while, and tell you what 
 the mid-point between those two commits are, and check that state out as 
-the head of the bew "bisect" branch.
+the head of the new "bisect" branch.
 
 Compile and reboot.
 
index c2d4a91..fcd64e9 100644 (file)
@@ -124,7 +124,7 @@ up your changes, along with other changes.
 
 The two commits #2' and #3' in the above picture record the same
 changes your e-mail submission for #2 and #3 contained, but
-probably with the new sign-off line added by the upsteam
+probably with the new sign-off line added by the upstream
 maintainer and definitely with different committer and ancestry
 information, they are different objects from #2 and #3 commits.
 
index 9aadd5c..103eb5d 100644 (file)
@@ -73,7 +73,7 @@ The traditional insight:
 
     <pasky> yes
 
-And Bable-like confusion flowed.
+And Babel-like confusion flowed.
 
     <njs`> oh, hmm, and I'm not sure what this sliding window means either
 
@@ -257,7 +257,7 @@ proclaim it a non-issue.  Good style too!
         (type, basename, size)).
 
         Then we walk through this list, and calculate a delta of
-        each object against the last n (tunable paramater) objects,
+        each object against the last n (tunable parameter) objects,
         and pick the smallest of these deltas.
 
 Vastly simplified, but the essence is there!
@@ -395,7 +395,7 @@ used as setup for a later optimization, which is a real word:
         do "object name->location in packfile" translation.
 
     <njs`> I'm assuming the real win for delta-ing large->small is
-        more homogenous statistics for gzip to run over?
+        more homogeneous statistics for gzip to run over?
 
         (You have to put the bytes in one place or another, but
         putting them in a larger blob wins on compression)
@@ -448,7 +448,7 @@ design options, etc.
 
         Bugs happen, but they are "simple" bugs. And bugs that
         actually get some object store detail wrong are almost always
-        so obious that they never go anywhere.
+        so obvious that they never go anywhere.
 
     <njs`> Yeah.
 
index 7477413..d60b371 100644 (file)
@@ -47,7 +47,7 @@ Then such a short-hand is specified in place of
 <repository> without <refspec> parameters on the command
 line, <refspec> specified on `Push:` lines or `Pull:`
 lines are used for `git-push` and `git-fetch`/`git-pull`,
-respectively.  Multiple `Push:` and and `Pull:` lines may
+respectively.  Multiple `Push:` and `Pull:` lines may
 be specified for additional branch mappings.
 
 The name of a file in `$GIT_DIR/branches` directory can be
diff --git a/INSTALL b/INSTALL
index f8337e2..7da2c89 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -44,7 +44,7 @@ Issues of note:
 
        - "libcurl" and "curl" executable.  git-http-fetch and
          git-fetch use them.  If you do not use http
-         transfer, you are probabaly OK if you do not have
+         transfer, you are probably OK if you do not have
          them.
 
        - expat library; git-http-push uses it for remote lock
@@ -69,7 +69,7 @@ Issues of note:
          git, and if you only use git to track other peoples work you'll
          never notice the lack of it. 
 
-        - "wish", the TCL/Tk windowing shell is used in gitk to show the
+        - "wish", the Tcl/Tk windowing shell is used in gitk to show the
           history graphically
 
        - "ssh" is used to push and pull over the net
index 7fa4a27..e75fb13 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -33,6 +33,10 @@ all:
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
 # Enable it on Windows.  By default, symrefs are still used.
 #
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
+# tests.  These tests take up a significant amount of the total test time
+# but are not needed unless you plan to talk to SVN repos.
+#
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
@@ -60,7 +64,7 @@ all:
 # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
 # a missing newline at the end of the file.
 #
-# Define NO_PYTHON if you want to loose all benefits of the recursive merge.
+# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
 #
 # Define COLLISION_CHECK below if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
@@ -134,7 +138,7 @@ SCRIPT_PERL = \
        git-shortlog.perl git-rerere.perl \
        git-annotate.perl git-cvsserver.perl \
        git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \
-       git-send-email.perl
+       git-send-email.perl git-svn.perl
 
 SCRIPT_PYTHON = \
        git-merge-recursive.py
@@ -142,7 +146,7 @@ SCRIPT_PYTHON = \
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
          $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
-         git-cherry-pick git-status
+         git-cherry-pick git-status git-instaweb
 
 # The ones that do not have to link with lcrypto, lz nor xdiff.
 SIMPLE_PROGRAMS = \
@@ -469,7 +473,7 @@ ifdef NO_ACCURATE_DIFF
        ALL_CFLAGS += -DNO_ACCURATE_DIFF
 endif
 
-# Shell quote (do not use $(call) to accomodate ancient setups);
+# Shell quote (do not use $(call) to accommodate ancient setups);
 
 SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 
@@ -514,6 +518,7 @@ common-cmds.h: Documentation/git-*.txt
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
        rm -f $@ $@+
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+           -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
            -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
@@ -546,6 +551,20 @@ git-status: git-commit
        cp $< $@+
        mv $@+ $@
 
+git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
+       rm -f $@ $@+
+       sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+           -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+           -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+           -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
+           -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
+           -e '/@@GITWEB_CGI@@/d' \
+           -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
+           -e '/@@GITWEB_CSS@@/d' \
+           $@.sh > $@+
+       chmod +x $@+
+       mv $@+ $@
+
 # These can record GIT_VERSION
 git$X git.spec \
        $(patsubst %.sh,%,$(SCRIPT_SH)) \
@@ -638,6 +657,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
 # with that.
 
 export NO_PYTHON
+export NO_SVN_TESTS
 
 test: all
        $(MAKE) -C t/ all
index bfbbb1b..2d25698 100644 (file)
@@ -181,7 +181,7 @@ int cmd_add(int argc, const char **argv, char **envp)
 
        if (active_cache_changed) {
                if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_lock_file(&lock_file))
+                   close(newfd) || commit_lock_file(&lock_file))
                        die("Unable to write new index file");
        }
 
index e9ead00..c3af489 100644 (file)
@@ -2323,7 +2323,7 @@ int cmd_apply(int argc, const char **argv, char **envp)
 
        if (write_index) {
                if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_lock_file(&lock_file))
+                   close(newfd) || commit_lock_file(&lock_file))
                        die("Unable to write new index file");
        }
 
index a655eea..81ac2fe 100644 (file)
@@ -18,7 +18,7 @@ int cmd_diff_files(int argc, const char **argv, char **envp)
        struct rev_info rev;
        int silent = 0;
 
-       git_config(git_diff_config);
+       git_config(git_default_config); /* no "diff" UI options */
        init_revisions(&rev);
        rev.abbrev = 0;
 
index b37c9e8..a1fa1b8 100644 (file)
@@ -15,7 +15,7 @@ int cmd_diff_index(int argc, const char **argv, char **envp)
        int cached = 0;
        int i;
 
-       git_config(git_diff_config);
+       git_config(git_default_config); /* no "diff" UI options */
        init_revisions(&rev);
        rev.abbrev = 0;
 
index 30931fe..9c62702 100644 (file)
@@ -61,7 +61,7 @@ int cmd_diff_stages(int ac, const char **av, char **envp)
        const char *prefix = setup_git_directory();
        const char **pathspec = NULL;
 
-       git_config(git_diff_config);
+       git_config(git_default_config); /* no "diff" UI options */
        read_cache();
        diff_setup(&diff_options);
        while (1 < ac && av[1][0] == '-') {
index ae1cde9..b610668 100644 (file)
@@ -67,7 +67,7 @@ int cmd_diff_tree(int argc, const char **argv, char **envp)
        static struct rev_info *opt = &log_tree_opt;
        int read_stdin = 0;
 
-       git_config(git_diff_config);
+       git_config(git_default_config); /* no "diff" UI options */
        nr_sha1 = 0;
        init_revisions(opt);
        opt->abbrev = 0;
index d520c7c..1df531b 100644 (file)
@@ -250,7 +250,7 @@ int cmd_diff(int argc, const char **argv, char **envp)
         * Other cases are errors.
         */
 
-       git_config(git_diff_config);
+       git_config(git_diff_ui_config);
        init_revisions(&rev);
 
        argc = setup_revisions(argc, argv, &rev, NULL);
index 864c6cd..7e5cab1 100644 (file)
@@ -47,6 +47,7 @@ int cmd_whatchanged(int argc, const char **argv, char **envp)
 {
        struct rev_info rev;
 
+       git_config(git_diff_ui_config);
        init_revisions(&rev);
        rev.diff = 1;
        rev.diffopt.recursive = 1;
@@ -61,6 +62,7 @@ int cmd_show(int argc, const char **argv, char **envp)
 {
        struct rev_info rev;
 
+       git_config(git_diff_ui_config);
        init_revisions(&rev);
        rev.diff = 1;
        rev.diffopt.recursive = 1;
@@ -77,6 +79,7 @@ int cmd_log(int argc, const char **argv, char **envp)
 {
        struct rev_info rev;
 
+       git_config(git_diff_ui_config);
        init_revisions(&rev);
        rev.always_show_header = 1;
        cmd_log_init(argc, argv, envp, &rev);
@@ -102,7 +105,10 @@ static int git_format_config(const char *var, const char *value)
                strcat(extra_headers, value);
                return 0;
        }
-       return git_default_config(var, value);
+       if (!strcmp(var, "diff.color")) {
+               return 0;
+       }
+       return git_diff_ui_config(var, value);
 }
 
 
@@ -234,6 +240,7 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        struct diff_options patch_id_opts;
        char *add_signoff = NULL;
 
+       git_config(git_format_config);
        init_revisions(&rev);
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.verbose_header = 1;
@@ -243,7 +250,6 @@ int cmd_format_patch(int argc, const char **argv, char **envp)
        rev.diffopt.msg_sep = "";
        rev.diffopt.recursive = 1;
 
-       git_config(git_format_config);
        rev.extra_headers = extra_headers;
 
        /*
index 3e40747..ac53f76 100644 (file)
@@ -348,7 +348,7 @@ static void cleanup_space(char *buf)
        }
 }
 
-static void decode_header_bq(char *it);
+static void decode_header(char *it);
 typedef int (*header_fn_t)(char *);
 struct header_def {
        const char *name;
@@ -371,7 +371,7 @@ static void check_header(char *line, struct header_def *header)
                        /* Unwrap inline B and Q encoding, and optionally
                         * normalize the meta information to utf8.
                         */
-                       decode_header_bq(line + len + 2);
+                       decode_header(line + len + 2);
                        header[i].func(line + len + 2);
                        break;
                }
@@ -566,16 +566,19 @@ static void convert_to_utf8(char *line, char *charset)
 #endif
 }
 
-static void decode_header_bq(char *it)
+static int decode_header_bq(char *it)
 {
        char *in, *out, *ep, *cp, *sp;
        char outbuf[1000];
+       int rfc2047 = 0;
 
        in = it;
        out = outbuf;
        while ((ep = strstr(in, "=?")) != NULL) {
                int sz, encoding;
                char charset_q[256], piecebuf[256];
+               rfc2047 = 1;
+
                if (in != ep) {
                        sz = ep - in;
                        memcpy(out, in, sz);
@@ -589,19 +592,19 @@ static void decode_header_bq(char *it)
                ep += 2;
                cp = strchr(ep, '?');
                if (!cp)
-                       return; /* no munging */
+                       return rfc2047; /* no munging */
                for (sp = ep; sp < cp; sp++)
                        charset_q[sp - ep] = tolower(*sp);
                charset_q[cp - ep] = 0;
                encoding = cp[1];
                if (!encoding || cp[2] != '?')
-                       return; /* no munging */
+                       return rfc2047; /* no munging */
                ep = strstr(cp + 3, "?=");
                if (!ep)
-                       return; /* no munging */
+                       return rfc2047; /* no munging */
                switch (tolower(encoding)) {
                default:
-                       return; /* no munging */
+                       return rfc2047; /* no munging */
                case 'b':
                        sz = decode_b_segment(cp + 3, piecebuf, ep);
                        break;
@@ -610,7 +613,7 @@ static void decode_header_bq(char *it)
                        break;
                }
                if (sz < 0)
-                       return;
+                       return rfc2047;
                if (metainfo_charset)
                        convert_to_utf8(piecebuf, charset_q);
                strcpy(out, piecebuf);
@@ -619,6 +622,19 @@ static void decode_header_bq(char *it)
        }
        strcpy(out, in);
        strcpy(it, outbuf);
+       return rfc2047;
+}
+
+static void decode_header(char *it)
+{
+
+       if (decode_header_bq(it))
+               return;
+       /* otherwise "it" is a straight copy of the input.
+        * This can be binary guck but there is no charset specified.
+        */
+       if (metainfo_charset)
+               convert_to_utf8(it, "");
 }
 
 static void decode_transfer_encoding(char *line)
index 9a2099d..23a8d92 100644 (file)
@@ -1038,7 +1038,7 @@ int cmd_read_tree(int argc, const char **argv, char **envp)
        }
 
        if (write_cache(newfd, active_cache, active_nr) ||
-           commit_lock_file(&lock_file))
+           close(newfd) || commit_lock_file(&lock_file))
                die("unable to write new index file");
        return 0;
 }
index 5f5ade4..b3e4386 100644 (file)
@@ -164,6 +164,51 @@ static int show_file(const char *arg)
        return 0;
 }
 
+static int try_difference(const char *arg)
+{
+       char *dotdot;
+       unsigned char sha1[20];
+       unsigned char end[20];
+       const char *next;
+       const char *this;
+       int symmetric;
+
+       if (!(dotdot = strstr(arg, "..")))
+               return 0;
+       next = dotdot + 2;
+       this = arg;
+       symmetric = (*next == '.');
+
+       *dotdot = 0;
+       next += symmetric;
+
+       if (!*next)
+               next = "HEAD";
+       if (dotdot == arg)
+               this = "HEAD";
+       if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
+               show_rev(NORMAL, end, next);
+               show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
+               if (symmetric) {
+                       struct commit_list *exclude;
+                       struct commit *a, *b;
+                       a = lookup_commit_reference(sha1);
+                       b = lookup_commit_reference(end);
+                       exclude = get_merge_bases(a, b, 1);
+                       while (exclude) {
+                               struct commit_list *n = exclude->next;
+                               show_rev(REVERSED,
+                                        exclude->item->object.sha1,NULL);
+                               free(exclude);
+                               exclude = n;
+                       }
+               }
+               return 1;
+       }
+       *dotdot = '.';
+       return 0;
+}
+
 int cmd_rev_parse(int argc, const char **argv, char **envp)
 {
        int i, as_is = 0, verify = 0;
@@ -174,7 +219,6 @@ int cmd_rev_parse(int argc, const char **argv, char **envp)
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
-               char *dotdot;
 
                if (as_is) {
                        if (show_file(arg) && as_is < 2)
@@ -326,23 +370,8 @@ int cmd_rev_parse(int argc, const char **argv, char **envp)
                }
 
                /* Not a flag argument */
-               dotdot = strstr(arg, "..");
-               if (dotdot) {
-                       unsigned char end[20];
-                       const char *next = dotdot + 2;
-                       const char *this = arg;
-                       *dotdot = 0;
-                       if (!*next)
-                               next = "HEAD";
-                       if (dotdot == arg)
-                               this = "HEAD";
-                       if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
-                               show_rev(NORMAL, end, next);
-                               show_rev(REVERSED, sha1, this);
-                               continue;
-                       }
-                       *dotdot = '.';
-               }
+               if (try_difference(arg))
+                       continue;
                if (!get_sha1(arg, sha1)) {
                        show_rev(NORMAL, sha1, arg);
                        continue;
index 4d56a1f..875d825 100644 (file)
@@ -147,7 +147,7 @@ int cmd_rm(int argc, const char **argv, char **envp)
 
        if (active_cache_changed) {
                if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_lock_file(&lock_file))
+                   close(newfd) || commit_lock_file(&lock_file))
                        die("Unable to write new index file");
        }
 
index 09d8227..260cb22 100644 (file)
@@ -6,7 +6,7 @@
 #include "builtin.h"
 
 static const char show_branch_usage[] =
-"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
+"git-show-branch [--sparse] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
 
 static int default_num = 0;
 static int default_alloc = 0;
index ef50243..1a4200d 100644 (file)
@@ -648,7 +648,7 @@ int cmd_update_index(int argc, const char **argv, char **envp)
  finish:
        if (active_cache_changed) {
                if (write_cache(newfd, active_cache, active_nr) ||
-                   commit_lock_file(lock_file))
+                   close(newfd) || commit_lock_file(lock_file))
                        die("Unable to write new index file");
        }
 
index 70e9b6f..449a4d1 100644 (file)
@@ -35,7 +35,8 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
                                      missing_ok, 0) < 0)
                        die("git-write-tree: error building trees");
                if (0 <= newfd) {
-                       if (!write_cache(newfd, active_cache, active_nr))
+                       if (!write_cache(newfd, active_cache, active_nr)
+                                       && !close(newfd))
                                commit_lock_file(lock_file);
                }
                /* Not being able to write is fine -- we are only interested
diff --git a/cache.h b/cache.h
index 7b5c91c..b5e3f8f 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -382,6 +382,7 @@ extern int receive_keep_pack(int fd[2], const char *me, int quiet, int);
 
 /* pager.c */
 extern void setup_pager(void);
+extern int pager_in_use;
 
 /* base85 */
 int decode_85(char *dst, char *line, int linelen);
index ea40bc2..2927955 100644 (file)
@@ -311,7 +311,7 @@ int main(int argc, char **argv)
 
        if (0 <= newfd &&
            (write_cache(newfd, active_cache, active_nr) ||
-            commit_lock_file(&lock_file)))
+            close(newfd) || commit_lock_file(&lock_file)))
                die("Unable to write new index file");
        return 0;
 }
index e51ffa1..c6bf10d 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -397,12 +397,13 @@ void clear_commit_marks(struct commit *commit, unsigned int mark)
 {
        struct commit_list *parents;
 
-       parents = commit->parents;
        commit->object.flags &= ~mark;
+       parents = commit->parents;
        while (parents) {
                struct commit *parent = parents->item;
-               if (parent && parent->object.parsed &&
-                   (parent->object.flags & mark))
+
+               /* Have we already cleared this? */
+               if (mark & parent->object.flags)
                        clear_commit_marks(parent, mark);
                parents = parents->next;
        }
@@ -846,3 +847,247 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
        }
        free(nodes);
 }
+
+/* merge-rebase stuff */
+
+/* bits #0..7 in revision.h */
+#define PARENT1                (1u<< 8)
+#define PARENT2                (1u<< 9)
+#define STALE          (1u<<10)
+
+static struct commit *interesting(struct commit_list *list)
+{
+       while (list) {
+               struct commit *commit = list->item;
+               list = list->next;
+               if (commit->object.flags & STALE)
+                       continue;
+               return commit;
+       }
+       return NULL;
+}
+
+/*
+ * A pathological example of how this thing works.
+ *
+ * Suppose we had this commit graph, where chronologically
+ * the timestamp on the commit are A <= B <= C <= D <= E <= F
+ * and we are trying to figure out the merge base for E and F
+ * commits.
+ *
+ *                  F
+ *                 / \
+ *            E   A   D
+ *             \ /   /
+ *              B   /
+ *               \ /
+ *                C
+ *
+ * First we push E and F to list to be processed.  E gets bit 1
+ * and F gets bit 2.  The list becomes:
+ *
+ *     list=F(2) E(1), result=empty
+ *
+ * Then we pop F, the newest commit, from the list.  Its flag is 2.
+ * We scan its parents, mark them reachable from the side that F is
+ * reachable from, and push them to the list:
+ *
+ *     list=E(1) D(2) A(2), result=empty
+ *
+ * Next pop E and do the same.
+ *
+ *     list=D(2) B(1) A(2), result=empty
+ *
+ * Next pop D and do the same.
+ *
+ *     list=C(2) B(1) A(2), result=empty
+ *
+ * Next pop C and do the same.
+ *
+ *     list=B(1) A(2), result=empty
+ *
+ * Now it is B's turn.  We mark its parent, C, reachable from B's side,
+ * and push it to the list:
+ *
+ *     list=C(3) A(2), result=empty
+ *
+ * Now pop C and notice it has flags==3.  It is placed on the result list,
+ * and the list now contains:
+ *
+ *     list=A(2), result=C(3)
+ *
+ * We pop A and do the same.
+ *
+ *     list=B(3), result=C(3)
+ *
+ * Next, we pop B and something very interesting happens.  It has flags==3
+ * so it is also placed on the result list, and its parents are marked
+ * stale, retroactively, and placed back on the list:
+ *
+ *    list=C(7), result=C(7) B(3)
+ *
+ * Now, list does not have any interesting commit.  So we find the newest
+ * commit from the result list that is not marked stale.  Which is
+ * commit B.
+ *
+ *
+ * Another pathological example how this thing used to fail to mark an
+ * ancestor of a merge base as STALE before we introduced the
+ * postprocessing phase (mark_reachable_commits).
+ *
+ *               2
+ *               H
+ *         1    / \
+ *         G   A   \
+ *         |\ /     \
+ *         | B       \
+ *         |  \       \
+ *          \  C       F
+ *           \  \     /
+ *            \  D   /
+ *             \ |  /
+ *              \| /
+ *               E
+ *
+ *      list                   A B C D E F G H
+ *      G1 H2                  - - - - - - 1 2
+ *      H2 E1 B1               - 1 - - 1 - 1 2
+ *      F2 E1 B1 A2            2 1 - - 1 2 1 2
+ *      E3 B1 A2               2 1 - - 3 2 1 2
+ *      B1 A2                  2 1 - - 3 2 1 2
+ *      C1 A2                  2 1 1 - 3 2 1 2
+ *      D1 A2                  2 1 1 1 3 2 1 2
+ *      A2                     2 1 1 1 3 2 1 2
+ *      B3                     2 3 1 1 3 2 1 2
+ *      C7                     2 3 7 1 3 2 1 2
+ *
+ * At this point, unfortunately, everybody in the list is
+ * stale, so we fail to complete the following two
+ * steps to fully marking stale commits.
+ *
+ *      D7                     2 3 7 7 3 2 1 2
+ *      E7                     2 3 7 7 7 2 1 2
+ *
+ * and we ended up showing E as an interesting merge base.
+ * The postprocessing phase re-injects C and continues traversal
+ * to contaminate D and E.
+ */
+
+static void mark_reachable_commits(struct commit_list *result,
+                                  struct commit_list *list)
+{
+       struct commit_list *tmp;
+
+       /*
+        * Postprocess to fully contaminate the well.
+        */
+       for (tmp = result; tmp; tmp = tmp->next) {
+               struct commit *c = tmp->item;
+               /* Reinject stale ones to list,
+                * so we can scan their parents.
+                */
+               if (c->object.flags & STALE)
+                       commit_list_insert(c, &list);
+       }
+       while (list) {
+               struct commit *c = list->item;
+               struct commit_list *parents;
+
+               tmp = list;
+               list = list->next;
+               free(tmp);
+
+               /* Anything taken out of the list is stale, so
+                * mark all its parents stale.  We do not
+                * parse new ones (we already parsed all the relevant
+                * ones).
+                */
+               parents = c->parents;
+               while (parents) {
+                       struct commit *p = parents->item;
+                       parents = parents->next;
+                       if (!(p->object.flags & STALE)) {
+                               p->object.flags |= STALE;
+                               commit_list_insert(p, &list);
+                       }
+               }
+       }
+}
+
+struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2,
+                                    int cleanup)
+{
+       struct commit_list *list = NULL;
+       struct commit_list *result = NULL;
+       struct commit_list *tmp = NULL;
+
+       if (rev1 == rev2)
+               return commit_list_insert(rev1, &result);
+
+       parse_commit(rev1);
+       parse_commit(rev2);
+
+       rev1->object.flags |= PARENT1;
+       rev2->object.flags |= PARENT2;
+       insert_by_date(rev1, &list);
+       insert_by_date(rev2, &list);
+
+       while (interesting(list)) {
+               struct commit *commit = list->item;
+               struct commit_list *parents;
+               int flags = commit->object.flags
+                       & (PARENT1 | PARENT2 | STALE);
+
+               tmp = list;
+               list = list->next;
+               free(tmp);
+               if (flags == (PARENT1 | PARENT2)) {
+                       insert_by_date(commit, &result);
+
+                       /* Mark parents of a found merge stale */
+                       flags |= STALE;
+               }
+               parents = commit->parents;
+               while (parents) {
+                       struct commit *p = parents->item;
+                       parents = parents->next;
+                       if ((p->object.flags & flags) == flags)
+                               continue;
+                       parse_commit(p);
+                       p->object.flags |= flags;
+                       insert_by_date(p, &list);
+               }
+       }
+
+       if (!result)
+               goto finish;
+
+       if (result->next && list)
+               mark_reachable_commits(result, list);
+
+       /* cull duplicates */
+       for (tmp = result, list = NULL; tmp; ) {
+               struct commit *commit = tmp->item;
+               struct commit_list *next = tmp->next;
+               if (commit->object.flags & STALE) {
+                       if (list != NULL)
+                               list->next = next;
+                       free(tmp);
+               } else {
+                       if (list == NULL)
+                               result = tmp;
+                       list = tmp;
+                       commit->object.flags |= STALE;
+               }
+
+               tmp = next;
+       }
+
+ finish:
+       if (cleanup) {
+               clear_commit_marks(rev1, PARENT1 | PARENT2 | STALE);
+               clear_commit_marks(rev2, PARENT1 | PARENT2 | STALE);
+       }
+
+       return result;
+}
index 7c9ca3f..779ed82 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -105,4 +105,6 @@ struct commit_graft *read_graft_line(char *buf, int len);
 int register_commit_graft(struct commit_graft *, int);
 int read_graft_file(const char *graft_file);
 
+extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
+
 #endif /* COMMIT_H */
diff --git a/contrib/git-svn/.gitignore b/contrib/git-svn/.gitignore
deleted file mode 100644 (file)
index d8d87e3..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-git-svn
-git-svn.xml
-git-svn.html
-git-svn.1
diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile
deleted file mode 100644 (file)
index 7c20946..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-all: git-svn
-
-prefix?=$(HOME)
-bindir=$(prefix)/bin
-mandir=$(prefix)/man
-man1=$(mandir)/man1
-INSTALL?=install
-doc_conf=../../Documentation/asciidoc.conf
--include ../../config.mak
-
-git-svn: git-svn.perl
-       cp $< $@
-       chmod +x $@
-
-install: all
-       $(INSTALL) -d -m755 $(DESTDIR)$(bindir)
-       $(INSTALL) git-svn $(DESTDIR)$(bindir)
-
-install-doc: doc
-       $(INSTALL) git-svn.1 $(DESTDIR)$(man1)
-
-doc: git-svn.1
-git-svn.1 : git-svn.xml
-       xmlto man git-svn.xml
-git-svn.xml : git-svn.txt
-       asciidoc -b docbook -d manpage \
-               -f ../../Documentation/asciidoc.conf $<
-git-svn.html : git-svn.txt
-       asciidoc -b xhtml11 -d manpage \
-               -f ../../Documentation/asciidoc.conf $<
-test: git-svn
-       cd t && for i in t????-*.sh; do $(SHELL) ./$$i $(TEST_FLAGS); done
-
-# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
-full-test:
-       $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
-       $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
-       $(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
-                                                       LC_ALL=en_US.UTF-8
-       $(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
-                                                       LC_ALL=en_US.UTF-8
-
-clean:
-       rm -f git-svn *.xml *.html *.1
diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh
deleted file mode 100644 (file)
index d7f972a..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-PATH=$PWD/../:$PATH
-if test -d ../../../t
-then
-    cd ../../../t
-else
-    echo "Must be run in contrib/git-svn/t" >&2
-    exit 1
-fi
-
-. ./test-lib.sh
-
-GIT_DIR=$PWD/.git
-GIT_SVN_DIR=$GIT_DIR/svn/git-svn
-SVN_TREE=$GIT_SVN_DIR/svn-tree
-
-svnadmin >/dev/null 2>&1
-if test $? != 1
-then
-    test_expect_success 'skipping contrib/git-svn test' :
-    test_done
-    exit
-fi
-
-svn >/dev/null 2>&1
-if test $? != 1
-then
-    test_expect_success 'skipping contrib/git-svn test' :
-    test_done
-    exit
-fi
-
-svnrepo=$PWD/svnrepo
-
-set -e
-
-if svnadmin create --help | grep fs-type >/dev/null
-then
-       svnadmin create --fs-type fsfs "$svnrepo"
-else
-       svnadmin create "$svnrepo"
-fi
-
-svnrepo="file://$svnrepo/test-git-svn"
-
-
diff --git a/diff.c b/diff.c
index 507e401..e101bfd 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -13,6 +13,7 @@
 
 static int use_size_cache;
 
+static int diff_detect_rename_default = 0;
 static int diff_rename_limit_default = -1;
 static int diff_use_color_default = 0;
 
@@ -101,7 +102,13 @@ static const char *parse_diff_color_value(const char *value, const char *var)
        die("bad config value '%s' for variable '%s'", value, var);
 }
 
-int git_diff_config(const char *var, const char *value)
+/*
+ * These are to give UI layer defaults.
+ * The core-level commands such as git-diff-files should
+ * never be affected by the setting of diff.renames
+ * the user happens to have in the configuration file.
+ */
+int git_diff_ui_config(const char *var, const char *value)
 {
        if (!strcmp(var, "diff.renamelimit")) {
                diff_rename_limit_default = git_config_int(var, value);
@@ -110,8 +117,14 @@ int git_diff_config(const char *var, const char *value)
        if (!strcmp(var, "diff.color")) {
                if (!value)
                        diff_use_color_default = 1; /* bool */
-               else if (!strcasecmp(value, "auto"))
-                       diff_use_color_default = isatty(1);
+               else if (!strcasecmp(value, "auto")) {
+                       diff_use_color_default = 0;
+                       if (isatty(1) || pager_in_use) {
+                               char *term = getenv("TERM");
+                               if (term && strcmp(term, "dumb"))
+                                       diff_use_color_default = 1;
+                       }
+               }
                else if (!strcasecmp(value, "never"))
                        diff_use_color_default = 0;
                else if (!strcasecmp(value, "always"))
@@ -120,6 +133,16 @@ int git_diff_config(const char *var, const char *value)
                        diff_use_color_default = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.renames")) {
+               if (!value)
+                       diff_detect_rename_default = DIFF_DETECT_RENAME;
+               else if (!strcasecmp(value, "copies") ||
+                        !strcasecmp(value, "copy"))
+                       diff_detect_rename_default = DIFF_DETECT_COPY;
+               else if (git_config_bool(var,value))
+                       diff_detect_rename_default = DIFF_DETECT_RENAME;
+               return 0;
+       }
        if (!strncmp(var, "diff.color.", 11)) {
                int slot = parse_diff_color_slot(var, 11);
                diff_colors[slot] = parse_diff_color_value(value, var);
@@ -329,7 +352,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        }
        if (len > 0 && line[len-1] == '\n')
                len--;
-       printf("%s%.*s%s\n", set, (int) len, line, reset);
+       fputs (set, stdout);
+       fwrite (line, len, 1, stdout);
+       puts (reset);
 }
 
 static char *pprint_rename(const char *a, const char *b)
@@ -721,7 +746,7 @@ static void builtin_diff(const char *name_a,
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
 
-       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+       if (!o->text && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) {
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1429,6 +1454,7 @@ void diff_setup(struct diff_options *options)
        options->change = diff_change;
        options->add_remove = diff_addremove;
        options->color_diff = diff_use_color_default;
+       options->detect_rename = diff_detect_rename_default;
 }
 
 int diff_setup_done(struct diff_options *options)
@@ -1559,6 +1585,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_PATCH;
                options->full_index = options->binary = 1;
        }
+       else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) {
+               options->text = 1;
+       }
        else if (!strcmp(arg, "--name-only"))
                options->output_format |= DIFF_FORMAT_NAME;
        else if (!strcmp(arg, "--name-status"))
@@ -1608,10 +1637,14 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        }
        else if (!strcmp(arg, "--color"))
                options->color_diff = 1;
+       else if (!strcmp(arg, "--no-color"))
+               options->color_diff = 0;
        else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE;
        else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
                options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+       else if (!strcmp(arg, "--no-renames"))
+               options->detect_rename = 0;
        else
                return 0;
        return 1;
diff --git a/diff.h b/diff.h
index d557394..a06f959 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -42,6 +42,7 @@ struct diff_options {
        unsigned recursive:1,
                 tree_in_recursive:1,
                 binary:1,
+                text:1,
                 full_index:1,
                 silent_on_remove:1,
                 find_copies_harder:1,
@@ -122,7 +123,7 @@ extern int diff_scoreopt_parse(const char *opt);
 #define DIFF_SETUP_USE_CACHE           2
 #define DIFF_SETUP_USE_SIZE_CACHE      4
 
-extern int git_diff_config(const char *var, const char *value);
+extern int git_diff_ui_config(const char *var, const char *value);
 extern void diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int);
 extern int diff_setup_done(struct diff_options *);
@@ -161,7 +162,8 @@ extern void diffcore_std_no_resolve(struct diff_options *);
 "  -O<file>      reorder diffs according to the <file>.\n" \
 "  -S<string>    find filepair whose only one side contains the string.\n" \
 "  --pickaxe-all\n" \
-"                show all files diff when -S is used and hit is found.\n"
+"                show all files diff when -S is used and hit is found.\n" \
+"  -a  --text    treat all files as text.\n"
 
 extern int diff_queue_is_empty(void);
 extern void diff_flush(struct diff_options*);
index 03df143..06a8d26 100755 (executable)
@@ -13,7 +13,7 @@ git bisect log                        show bisect log.'
 . git-sh-setup
 
 sq() {
-       perl -e '
+       @@PERL@@ -e '
                for (@ARGV) {
                        s/'\''/'\'\\\\\'\''/g;
                        print " '\''$_'\''";
index 6a14b25..0368803 100755 (executable)
@@ -324,7 +324,7 @@ test -d "$GIT_DIR/refs/reference-tmp" && rm -fr "$GIT_DIR/refs/reference-tmp"
 if test -f "$GIT_DIR/CLONE_HEAD"
 then
        # Read git-fetch-pack -k output and store the remote branches.
-       perl -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin"
+       @@PERL@@ -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin"
 fi
 
 cd "$D" || exit
index 22c4ce8..08d786d 100755 (executable)
@@ -147,7 +147,7 @@ run_status () {
            git-ls-files -z --others $option \
                --exclude-per-directory=.gitignore
        fi |
-       perl -e '$/ = "\0";
+       @@PERL@@ -e '$/ = "\0";
            my $shown = 0;
            while (<>) {
                chomp;
index d1051d0..5dcb2f9 100755 (executable)
@@ -63,15 +63,15 @@ foreach my $p (@commit) {
 }
 
 if ($parent) {
+    my $found;
     # double check that it's a valid parent
     foreach my $p (@parents) {
-       my $found;
        if ($p eq $parent) {
            $found = 1;
            last;
        }; # found it
-       die "Did not find $parent in the parents for this commit!";
     }
+    die "Did not find $parent in the parents for this commit!" if !$found;
 } else { # we don't have a parent from the cmdline...
     if (@parents == 1) { # it's safe to get it from the commit
        $parent = $parents[0];
index 5ccca4f..c30ef70 100755 (executable)
@@ -779,7 +779,7 @@ sub req_update
 
     #$log->debug("update state : " . Dumper($state));
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1031,7 +1031,7 @@ sub req_ci
 
     my @committedfiles = ();
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         my $committedfile = $filename;
@@ -1145,7 +1145,7 @@ sub req_ci
 
     $updater->update();
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @committedfiles )
     {
         $filename = filecleanup($filename);
@@ -1190,7 +1190,7 @@ sub req_status
     # if no files were specified, we need to work out what files we should be providing status on ...
     argsfromdir($updater);
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1291,7 +1291,7 @@ sub req_diff
     # if no files were specified, we need to work out what files we should be providing status on ...
     argsfromdir($updater);
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1433,7 +1433,7 @@ sub req_log
     # if no files were specified, we need to work out what files we should be providing status on ...
     argsfromdir($updater);
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1519,7 +1519,7 @@ sub req_annotate
 
     chdir $tmpdir;
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
index 48818f8..f80299d 100755 (executable)
@@ -278,7 +278,7 @@ fetch_main () {
          head="ref: $remote_name"
          while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
          do
-           remote_name_quoted=$(perl -e '
+           remote_name_quoted=$(@@PERL@@ -e '
              my $u = $ARGV[0];
               $u =~ s/^ref:\s*//;
              $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
diff --git a/git-instaweb.sh b/git-instaweb.sh
new file mode 100755 (executable)
index 0000000..69aef3c
--- /dev/null
@@ -0,0 +1,235 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+USAGE='[--start] [--stop] [--restart]
+  [--local] [--httpd=<httpd>] [--port=<port>] [--browser=<browser>]
+  [--module-path=<path> (for Apache2 only)]'
+
+. git-sh-setup
+
+case "$GIT_DIR" in
+/*)
+       fqgitdir="$GIT_DIR" ;;
+*)
+       fqgitdir="$PWD/$GIT_DIR" ;;
+esac
+
+local="`git repo-config --bool --get instaweb.local`"
+httpd="`git repo-config --get instaweb.httpd`"
+browser="`git repo-config --get instaweb.browser`"
+port=`git repo-config --get instaweb.port`
+module_path="`git repo-config --get instaweb.modulepath`"
+
+conf=$GIT_DIR/gitweb/httpd.conf
+
+# Defaults:
+
+# if installed, it doens't need further configuration (module_path)
+test -z "$httpd" && httpd='lighttpd -f'
+
+# probably the most popular browser among gitweb users
+test -z "$browser" && browser='firefox'
+
+# any untaken local port will do...
+test -z "$port" && port=1234
+
+start_httpd () {
+       httpd_only="`echo $httpd | cut -f1 -d' '`"
+       if test "`expr index $httpd_only /`" -eq '1' || \
+                               which $httpd_only >/dev/null
+       then
+               $httpd $fqgitdir/gitweb/httpd.conf
+       else
+               # many httpds are installed in /usr/sbin or /usr/local/sbin
+               # these days and those are not in most users $PATHs
+               for i in /usr/local/sbin /usr/sbin
+               do
+                       if test -x "$i/$httpd_only"
+                       then
+                               # don't quote $httpd, there can be
+                               # arguments to it (-f)
+                               $i/$httpd "$fqgitdir/gitweb/httpd.conf"
+                               return
+                       fi
+               done
+       fi
+}
+
+stop_httpd () {
+       test -f "$fqgitdir/pid" && kill `cat "$fqgitdir/pid"`
+}
+
+while case "$#" in 0) break ;; esac
+do
+       case "$1" in
+       --stop|stop)
+               stop_httpd
+               exit 0
+               ;;
+       --start|start)
+               start_httpd
+               exit 0
+               ;;
+       --restart|restart)
+               stop_httpd
+               start_httpd
+               exit 0
+               ;;
+       --local|-l)
+               local=true
+               ;;
+       -d|--httpd|--httpd=*)
+               case "$#,$1" in
+               *,*=*)
+                       httpd=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+               1,*)
+                       usage ;;
+               *)
+                       httpd="$2"
+                       shift ;;
+               esac
+               ;;
+       -b|--browser|--browser=*)
+               case "$#,$1" in
+               *,*=*)
+                       browser=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+               1,*)
+                       usage ;;
+               *)
+                       browser="$2"
+                       shift ;;
+               esac
+               ;;
+       -p|--port|--port=*)
+               case "$#,$1" in
+               *,*=*)
+                       port=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+               1,*)
+                       usage ;;
+               *)
+                       port="$2"
+                       shift ;;
+               esac
+               ;;
+       -m|--module-path=*|--module-path)
+               case "$#,$1" in
+               *,*=*)
+                       module_path=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+               1,*)
+                       usage ;;
+               *)
+                       module_path="$2"
+                       shift ;;
+               esac
+               ;;
+       *)
+               usage
+               ;;
+       esac
+       shift
+done
+
+mkdir -p "$GIT_DIR/gitweb/tmp"
+GIT_EXEC_PATH="`git --exec-path`"
+GIT_DIR="$fqgitdir"
+export GIT_EXEC_PATH GIT_DIR
+
+
+lighttpd_conf () {
+       cat > "$conf" <<EOF
+server.document-root = "$fqgitdir/gitweb"
+server.port = $port
+server.modules = ( "mod_cgi" )
+server.indexfiles = ( "gitweb.cgi" )
+server.pid-file = "$fqgitdir/pid"
+cgi.assign = ( ".cgi" => "" )
+mimetype.assign = ( ".css" => "text/css" )
+EOF
+       test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf"
+}
+
+apache2_conf () {
+       test -z "$module_path" && module_path=/usr/lib/apache2/modules
+       mkdir -p "$GIT_DIR/gitweb/logs"
+       bind=
+       test "$local" = true && bind='127.0.0.1:'
+       echo 'text/css css' > $fqgitdir/mime.types
+       cat > "$conf" <<EOF
+ServerRoot "$fqgitdir/gitweb"
+DocumentRoot "$fqgitdir/gitweb"
+PidFile "$fqgitdir/pid"
+Listen $bind$port
+TypesConfig $fqgitdir/mime.types
+DirectoryIndex gitweb.cgi
+EOF
+
+       # check to see if Dennis Stosberg's mod_perl compatibility patch
+       # (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
+       if test -f "$module_path/mod_perl.so" && grep '^our $gitbin' \
+                               "$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
+       then
+               # favor mod_perl if available
+               cat >> "$conf" <<EOF
+LoadModule perl_module $module_path/mod_perl.so
+PerlPassEnv GIT_DIR
+PerlPassEnv GIT_EXEC_DIR
+<Location /gitweb.cgi>
+       SetHandler perl-script
+       PerlResponseHandler ModPerl::Registry
+       PerlOptions +ParseHeaders
+       Options +ExecCGI
+</Location>
+EOF
+       else
+               # plain-old CGI
+               cat >> "$conf" <<EOF
+LoadModule cgi_module $module_path/mod_cgi.so
+AddHandler cgi-script .cgi
+<Location /gitweb.cgi>
+       Options +ExecCGI
+</Location>
+EOF
+       fi
+}
+
+script='
+s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'`dirname $fqgitdir`'";#
+s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";#
+s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;#
+s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#'
+
+gitweb_cgi () {
+       cat > "$1.tmp" <<\EOFGITWEB
+@@GITWEB_CGI@@
+EOFGITWEB
+       sed "$script" "$1.tmp"  > "$1"
+       chmod +x "$1"
+       rm -f "$1.tmp"
+}
+
+gitweb_css () {
+       cat > "$1" <<\EOFGITWEB
+@@GITWEB_CSS@@
+EOFGITWEB
+}
+
+gitweb_cgi $GIT_DIR/gitweb/gitweb.cgi
+gitweb_css $GIT_DIR/gitweb/gitweb.css
+
+case "$httpd" in
+*lighttpd*)
+       lighttpd_conf
+       ;;
+*apache2*)
+       apache2_conf
+       ;;
+*)
+       echo "Unknown httpd specified: $httpd"
+       exit 1
+       ;;
+esac
+
+start_httpd
+test -z "$browser" && browser=echo
+$browser http://127.0.0.1:$port
index 3945e06..1b9e986 100755 (executable)
@@ -311,7 +311,7 @@ echo "$prev_head" > "$dotest/prev_head"
 
 msgnum=0
 for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
-                       | perl -e 'print reverse <>'`
+                       | @@PERL@@ -e 'print reverse <>'`
 do
        msgnum=$(($msgnum + 1))
        echo "$cmt" > "$dotest/cmt.$msgnum"
index 46451d0..5c02240 100755 (executable)
@@ -17,7 +17,11 @@ case "$1" in
         usage ;;
 esac
 
-rev=$(git-rev-parse --verify --default HEAD "$@") || exit
+case $# in
+0) rev=HEAD ;;
+1) rev=$(git-rev-parse --verify "$1") || exit ;;
+*) usage ;;
+esac
 rev=$(git-rev-parse --verify $rev^0) || exit
 
 # We need to remember the set of paths that _could_ be left
index b04b8f4..c9c1975 100755 (executable)
@@ -34,8 +34,43 @@ sub readline {
 package main;
 
 # most mail servers generate the Date: header, but not all...
-$ENV{LC_ALL} = 'C';
-use POSIX qw/strftime/;
+sub format_2822_time {
+       my ($time) = @_;
+       my @localtm = localtime($time);
+       my @gmttm = gmtime($time);
+       my $localmin = $localtm[1] + $localtm[2] * 60;
+       my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
+       if ($localtm[0] != $gmttm[0]) {
+               die "local zone differs from GMT by a non-minute interval\n";
+       }
+       if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
+               $localmin += 1440;
+       } elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
+               $localmin -= 1440;
+       } elsif ($gmttm[6] != $localtm[6]) {
+               die "local time offset greater than or equal to 24 hours\n";
+       }
+       my $offset = $localmin - $gmtmin;
+       my $offhour = $offset / 60;
+       my $offmin = abs($offset % 60);
+       if (abs($offhour) >= 24) {
+               die ("local time offset greater than or equal to 24 hours\n");
+       }
+
+       return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
+                      qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]],
+                      $localtm[3],
+                      qw(Jan Feb Mar Apr May Jun
+                         Jul Aug Sep Oct Nov Dec)[$localtm[4]],
+                      $localtm[5]+1900,
+                      $localtm[2],
+                      $localtm[1],
+                      $localtm[0],
+                      ($offset >= 0) ? '+' : '-',
+                      abs($offhour),
+                      $offmin,
+                      );
+}
 
 my $have_email_valid = eval { require Email::Valid; 1 };
 my $smtp;
@@ -387,7 +422,7 @@ sub send_message
        my @recipients = unique_email_list(@to);
        my $to = join (",\n\t", @recipients);
        @recipients = unique_email_list(@recipients,@cc,@bcclist);
-       my $date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime($time++));
+       my $date = format_2822_time($time++);
        my $gitversion = '@@GIT_VERSION@@';
        if ($gitversion =~ m/..GIT_VERSION../) {
            $gitversion = `git --version`;
similarity index 99%
rename from contrib/git-svn/git-svn.perl
rename to git-svn.perl
index 8bc4188..145eaa8 100755 (executable)
@@ -8,7 +8,7 @@ use vars qw/    $AUTHOR $VERSION
                $GIT_SVN_INDEX $GIT_SVN
                $GIT_DIR $GIT_SVN_DIR $REVDB/;
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '1.1.1-broken';
+$VERSION = '@@GIT_VERSION@@';
 
 use Cwd qw/abs_path/;
 $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
diff --git a/git.c b/git.c
index 2567301..49062ca 100644 (file)
--- a/git.c
+++ b/git.c
@@ -251,6 +251,11 @@ int main(int argc, const char **argv, char **envp)
                cmd = *++argv;
                argc--;
 
+               if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
+                       setup_pager();
+                       continue;
+               }
+
                if (strncmp(cmd, "--", 2))
                        break;
 
index 4856ca0..59f723f 100644 (file)
 #include "cache.h"
 #include "commit.h"
 
-#define PARENT1 1
-#define PARENT2 2
-#define UNINTERESTING 4
-
-static struct commit *interesting(struct commit_list *list)
-{
-       while (list) {
-               struct commit *commit = list->item;
-               list = list->next;
-               if (commit->object.flags & UNINTERESTING)
-                       continue;
-               return commit;
-       }
-       return NULL;
-}
-
-/*
- * A pathological example of how this thing works.
- *
- * Suppose we had this commit graph, where chronologically
- * the timestamp on the commit are A <= B <= C <= D <= E <= F
- * and we are trying to figure out the merge base for E and F
- * commits.
- *
- *                  F
- *                 / \
- *            E   A   D
- *             \ /   /  
- *              B   /
- *               \ /
- *                C
- *
- * First we push E and F to list to be processed.  E gets bit 1
- * and F gets bit 2.  The list becomes:
- *
- *     list=F(2) E(1), result=empty
- *
- * Then we pop F, the newest commit, from the list.  Its flag is 2.
- * We scan its parents, mark them reachable from the side that F is
- * reachable from, and push them to the list:
- *
- *     list=E(1) D(2) A(2), result=empty
- *
- * Next pop E and do the same.
- *
- *     list=D(2) B(1) A(2), result=empty
- *
- * Next pop D and do the same.
- *
- *     list=C(2) B(1) A(2), result=empty
- *
- * Next pop C and do the same.
- *
- *     list=B(1) A(2), result=empty
- *
- * Now it is B's turn.  We mark its parent, C, reachable from B's side,
- * and push it to the list:
- *
- *     list=C(3) A(2), result=empty
- *
- * Now pop C and notice it has flags==3.  It is placed on the result list,
- * and the list now contains:
- *
- *     list=A(2), result=C(3)
- *
- * We pop A and do the same.
- * 
- *     list=B(3), result=C(3)
- *
- * Next, we pop B and something very interesting happens.  It has flags==3
- * so it is also placed on the result list, and its parents are marked
- * uninteresting, retroactively, and placed back on the list:
- *
- *    list=C(7), result=C(7) B(3)
- * 
- * Now, list does not have any interesting commit.  So we find the newest
- * commit from the result list that is not marked uninteresting.  Which is
- * commit B.
- *
- *
- * Another pathological example how this thing used to fail to mark an
- * ancestor of a merge base as UNINTERESTING before we introduced the
- * postprocessing phase (mark_reachable_commits).
- *
- *               2
- *               H
- *         1    / \
- *         G   A   \
- *         |\ /     \ 
- *         | B       \
- *         |  \       \
- *          \  C       F
- *           \  \     / 
- *            \  D   /   
- *             \ |  /
- *              \| /
- *               E
- *
- *      list                   A B C D E F G H
- *      G1 H2                  - - - - - - 1 2
- *      H2 E1 B1               - 1 - - 1 - 1 2
- *      F2 E1 B1 A2            2 1 - - 1 2 1 2
- *      E3 B1 A2               2 1 - - 3 2 1 2
- *      B1 A2                  2 1 - - 3 2 1 2
- *      C1 A2                  2 1 1 - 3 2 1 2
- *      D1 A2                  2 1 1 1 3 2 1 2
- *      A2                     2 1 1 1 3 2 1 2
- *      B3                     2 3 1 1 3 2 1 2
- *      C7                     2 3 7 1 3 2 1 2
- *
- * At this point, unfortunately, everybody in the list is
- * uninteresting, so we fail to complete the following two
- * steps to fully marking uninteresting commits.
- *
- *      D7                     2 3 7 7 3 2 1 2
- *      E7                     2 3 7 7 7 2 1 2
- *
- * and we ended up showing E as an interesting merge base.
- * The postprocessing phase re-injects C and continues traversal
- * to contaminate D and E.
- */
-
 static int show_all = 0;
 
-static void mark_reachable_commits(struct commit_list *result,
-                                  struct commit_list *list)
-{
-       struct commit_list *tmp;
-
-       /*
-        * Postprocess to fully contaminate the well.
-        */
-       for (tmp = result; tmp; tmp = tmp->next) {
-               struct commit *c = tmp->item;
-               /* Reinject uninteresting ones to list,
-                * so we can scan their parents.
-                */
-               if (c->object.flags & UNINTERESTING)
-                       commit_list_insert(c, &list);
-       }
-       while (list) {
-               struct commit *c = list->item;
-               struct commit_list *parents;
-
-               tmp = list;
-               list = list->next;
-               free(tmp);
-
-               /* Anything taken out of the list is uninteresting, so
-                * mark all its parents uninteresting.  We do not
-                * parse new ones (we already parsed all the relevant
-                * ones).
-                */
-               parents = c->parents;
-               while (parents) {
-                       struct commit *p = parents->item;
-                       parents = parents->next;
-                       if (!(p->object.flags & UNINTERESTING)) {
-                               p->object.flags |= UNINTERESTING;
-                               commit_list_insert(p, &list);
-                       }
-               }
-       }
-}
-
 static int merge_base(struct commit *rev1, struct commit *rev2)
 {
-       struct commit_list *list = NULL;
-       struct commit_list *result = NULL;
-       struct commit_list *tmp = NULL;
-
-       if (rev1 == rev2) {
-               printf("%s\n", sha1_to_hex(rev1->object.sha1));
-               return 0;
-       }
-
-       parse_commit(rev1);
-       parse_commit(rev2);
-
-       rev1->object.flags |= 1;
-       rev2->object.flags |= 2;
-       insert_by_date(rev1, &list);
-       insert_by_date(rev2, &list);
-
-       while (interesting(list)) {
-               struct commit *commit = list->item;
-               struct commit_list *parents;
-               int flags = commit->object.flags & 7;
-
-               tmp = list;
-               list = list->next;
-               free(tmp);
-               if (flags == 3) {
-                       insert_by_date(commit, &result);
-
-                       /* Mark parents of a found merge uninteresting */
-                       flags |= UNINTERESTING;
-               }
-               parents = commit->parents;
-               while (parents) {
-                       struct commit *p = parents->item;
-                       parents = parents->next;
-                       if ((p->object.flags & flags) == flags)
-                               continue;
-                       parse_commit(p);
-                       p->object.flags |= flags;
-                       insert_by_date(p, &list);
-               }
-       }
+       struct commit_list *result = get_merge_bases(rev1, rev2, 0);
 
        if (!result)
                return 1;
 
-       if (result->next && list)
-               mark_reachable_commits(result, list);
-
        while (result) {
-               struct commit *commit = result->item;
-               result = result->next;
-               if (commit->object.flags & UNINTERESTING)
-                       continue;
-               printf("%s\n", sha1_to_hex(commit->object.sha1));
+               printf("%s\n", sha1_to_hex(result->item->object.sha1));
                if (!show_all)
                        return 0;
-               commit->object.flags |= UNINTERESTING;
+               result = result->next;
        }
+
        return 0;
 }
 
diff --git a/pager.c b/pager.c
index 2d186e8..bb14e99 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -5,6 +5,8 @@
  * something different on Windows, for example.
  */
 
+int pager_in_use;
+
 static void run_pager(const char *pager)
 {
        execlp(pager, pager, NULL);
@@ -24,6 +26,8 @@ void setup_pager(void)
        else if (!*pager || !strcmp(pager, "cat"))
                return;
 
+       pager_in_use = 1; /* means we are emitting to terminal */
+
        if (pipe(fd) < 0)
                return;
        pid = fork();
diff --git a/refs.c b/refs.c
index 713ca46..2d9c1dc 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -362,7 +362,7 @@ static int log_ref_write(struct ref_lock *lock,
        int logfd, written, oflags = O_APPEND | O_WRONLY;
        unsigned maxlen, len;
        char *logrec;
-       const char *comitter;
+       const char *committer;
 
        if (log_all_ref_updates) {
                if (safe_create_leading_directories(lock->log_file) < 0)
@@ -380,23 +380,23 @@ static int log_ref_write(struct ref_lock *lock,
        }
 
        setup_ident();
-       comitter = git_committer_info(1);
+       committer = git_committer_info(1);
        if (msg) {
-               maxlen = strlen(comitter) + strlen(msg) + 2*40 + 5;
+               maxlen = strlen(committer) + strlen(msg) + 2*40 + 5;
                logrec = xmalloc(maxlen);
                len = snprintf(logrec, maxlen, "%s %s %s\t%s\n",
                        sha1_to_hex(lock->old_sha1),
                        sha1_to_hex(sha1),
-                       comitter,
+                       committer,
                        msg);
        }
        else {
-               maxlen = strlen(comitter) + 2*40 + 4;
+               maxlen = strlen(committer) + 2*40 + 4;
                logrec = xmalloc(maxlen);
                len = snprintf(logrec, maxlen, "%s %s %s\n",
                        sha1_to_hex(lock->old_sha1),
                        sha1_to_hex(sha1),
-                       comitter);
+                       committer);
        }
        written = len <= maxlen ? write(logfd, logrec, len) : -1;
        free(logrec);
index ab89c22..a7750e6 100644 (file)
@@ -537,6 +537,18 @@ void init_revisions(struct rev_info *revs)
        diff_setup(&revs->diffopt);
 }
 
+static void add_pending_commit_list(struct rev_info *revs,
+                                    struct commit_list *commit_list,
+                                    unsigned int flags)
+{
+       while (commit_list) {
+               struct object *object = &commit_list->item->object;
+               object->flags |= flags;
+               add_pending_object(revs, object, sha1_to_hex(object->sha1));
+               commit_list = commit_list->next;
+       }
+}
+
 /*
  * Parse revision information, filling in the "rev_info" structure,
  * and removing the used arguments from the argument list.
@@ -772,27 +784,46 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        unsigned char from_sha1[20];
                        const char *next = dotdot + 2;
                        const char *this = arg;
+                       int symmetric = *next == '.';
+                       unsigned int flags_exclude = flags ^ UNINTERESTING;
+
                        *dotdot = 0;
+                       next += symmetric;
+
                        if (!*next)
                                next = "HEAD";
                        if (dotdot == arg)
                                this = "HEAD";
                        if (!get_sha1(this, from_sha1) &&
                            !get_sha1(next, sha1)) {
-                               struct object *exclude;
-                               struct object *include;
-
-                               exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
-                               include = get_reference(revs, next, sha1, flags);
-                               if (!exclude || !include)
-                                       die("Invalid revision range %s..%s", arg, next);
+                               struct commit *a, *b;
+                               struct commit_list *exclude;
+
+                               a = lookup_commit_reference(from_sha1);
+                               b = lookup_commit_reference(sha1);
+                               if (!a || !b) {
+                                       die(symmetric ?
+                                           "Invalid symmetric difference expression %s...%s" :
+                                           "Invalid revision range %s..%s",
+                                           arg, next);
+                               }
 
                                if (!seen_dashdash) {
                                        *dotdot = '.';
                                        verify_non_filename(revs->prefix, arg);
                                }
-                               add_pending_object(revs, exclude, this);
-                               add_pending_object(revs, include, next);
+
+                               if (symmetric) {
+                                       exclude = get_merge_bases(a, b, 1);
+                                       add_pending_commit_list(revs, exclude,
+                                                               flags_exclude);
+                                       free_commit_list(exclude);
+                                       a->object.flags |= flags;
+                               } else
+                                       a->object.flags |= flags_exclude;
+                               b->object.flags |= flags;
+                               add_pending_object(revs, &a->object, this);
+                               add_pending_object(revs, &b->object, next);
                                continue;
                        }
                        *dotdot = '.';
index 0eb5132..fdfe05a 100644 (file)
@@ -94,7 +94,7 @@ static int read_pack_info_file(const char *infofile)
 
        fp = fopen(infofile, "r");
        if (!fp)
-               return 1; /* nonexisting is not an error. */
+               return 1; /* nonexistent is not an error. */
 
        while (fgets(line, sizeof(line), fp)) {
                int len = strlen(line);
index 632c55f..8983509 100644 (file)
@@ -11,6 +11,7 @@ TAR ?= $(TAR)
 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)
 
 ifdef NO_PYTHON
        GIT_TEST_OPTS += --no-python
@@ -24,6 +25,15 @@ $(T):
 clean:
        rm -fr trash
 
+# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
+full-svn-test:
+       $(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
+       $(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
+       $(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
+                                                       LC_ALL=en_US.UTF-8
+       $(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
+                                                       LC_ALL=en_US.UTF-8
+
 .PHONY: $(T) clean
 .NOTPARALLEL:
 
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
new file mode 100644 (file)
index 0000000..29a1e72
--- /dev/null
@@ -0,0 +1,50 @@
+. ./test-lib.sh
+
+if test -n "$NO_SVN_TESTS"
+then
+       test_expect_success 'skipping git-svn tests, NO_SVN_TESTS defined' :
+       test_done
+       exit
+fi
+
+GIT_DIR=$PWD/.git
+GIT_SVN_DIR=$GIT_DIR/svn/git-svn
+SVN_TREE=$GIT_SVN_DIR/svn-tree
+
+perl -e 'use SVN::Core' >/dev/null 2>&1
+if test $? -ne 0
+then
+   echo 'Perl SVN libraries not found, tests requiring those will be skipped'
+   GIT_SVN_NO_LIB=1
+fi
+
+svnadmin >/dev/null 2>&1
+if test $? -ne 1
+then
+    test_expect_success 'skipping git-svn tests, svnadmin not found' :
+    test_done
+    exit
+fi
+
+svn >/dev/null 2>&1
+if test $? -ne 1
+then
+    test_expect_success 'skipping git-svn tests, svn not found' :
+    test_done
+    exit
+fi
+
+svnrepo=$PWD/svnrepo
+
+set -e
+
+if svnadmin create --help | grep fs-type >/dev/null
+then
+       svnadmin create --fs-type fsfs "$svnrepo"
+else
+       svnadmin create "$svnrepo"
+fi
+
+svnrepo="file://$svnrepo/test-git-svn"
+
+
old mode 100644 (file)
new mode 100755 (executable)
similarity index 98%
rename from contrib/git-svn/t/t0000-contrib-git-svn.sh
rename to t/t9100-git-svn-basic.sh
index b482bb6..bf1d638
@@ -3,7 +3,7 @@
 # Copyright (c) 2006 Eric Wong
 #
 
-test_description='git-svn tests'
+test_description='git-svn basic tests'
 GIT_SVN_LC_ALL=$LC_ALL
 
 case "$LC_ALL" in
@@ -17,6 +17,8 @@ esac
 
 . ./lib-git-svn.sh
 
+echo 'define NO_SVN_TESTS to skip git-svn tests'
+
 mkdir import
 cd import
 
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from contrib/git-svn/t/t0001-contrib-git-svn-props.sh
rename to t/t9101-git-svn-props.sh
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from contrib/git-svn/t/t0002-deep-rmdir.sh
rename to t/t9102-git-svn-deep-rmdir.sh
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from contrib/git-svn/t/t0003-graft-branches.sh
rename to t/t9103-git-svn-graft-branches.sh
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from contrib/git-svn/t/t0004-follow-parent.sh
rename to t/t9104-git-svn-follow-parent.sh
old mode 100644 (file)
new mode 100755 (executable)
similarity index 100%
rename from contrib/git-svn/t/t0005-commit-diff.sh
rename to t/t9105-git-svn-commit-diff.sh
index d7a8f0a..76d5ac2 100644 (file)
@@ -60,7 +60,7 @@ then
                        echo "Changes since $prev:"
                        git rev-list --pretty $prev..$3 | $short
                        echo ---
-                       git diff $prev..$3 | diffstat -p1
+                       git diff --stat $prev..$3
                        echo ---
                fi
                ;;
@@ -75,7 +75,7 @@ else
        base=$(git-merge-base "$2" "$3")
        case "$base" in
        "$2")
-               git diff "$3" "^$base" | diffstat -p1
+               git diff --stat "$3" "^$base"
                echo
                echo "New commits:"
                ;;