Merge branch 'jc/pickaxe'
authorJunio C Hamano <junkio@cox.net>
Wed, 8 Nov 2006 00:33:59 +0000 (16:33 -0800)
committerJunio C Hamano <junkio@cox.net>
Wed, 8 Nov 2006 00:33:59 +0000 (16:33 -0800)
1  2 
Documentation/git-rev-parse.txt
Documentation/git.txt
Makefile
blame.c
builtin.h
diff.h
git.c
revision.c
index ed938aafb0f7043131489a79e197f1c266cf9607,cda80b18f45c51b038838a62d0707172f2f273c9..4eaf5a0d1ea26798298c4f572c29d90af9dc7cf1
@@@ -122,30 -122,14 +122,30 @@@ blobs contained in a commit
    your repository whose object name starts with dae86e.
  
  * An output from `git-describe`; i.e. a closest tag, followed by a
 -  dash, a 'g', and an abbreviated object name.
 +  dash, a `g`, and an abbreviated object name.
  
  * A symbolic ref name.  E.g. 'master' typically means the commit
    object referenced by $GIT_DIR/refs/heads/master.  If you
    happen to have both heads/master and tags/master, you can
    explicitly say 'heads/master' to tell git which one you mean.
 +  When ambiguous, a `<name>` is disambiguated by taking the
 +  first match in the following rules:
  
 -* A suffix '@' followed by a date specification enclosed in a brace
 +  . if `$GIT_DIR/<name>` exists, that is what you mean (this is usually
 +    useful only for `HEAD`, `FETCH_HEAD` and `MERGE_HEAD`);
 +
 +  . otherwise, `$GIT_DIR/refs/<name>` if exists;
 +
 +  . otherwise, `$GIT_DIR/refs/tags/<name>` if exists;
 +
 +  . otherwise, `$GIT_DIR/refs/heads/<name>` if exists;
 +
 +  . otherwise, `$GIT_DIR/refs/remotes/<name>` if exists;
 +
 +  . otherwise, `$GIT_DIR/refs/remotes/<name>/HEAD` if exists.
 +
 +* A ref followed by the suffix '@' with a date specification
 +  enclosed in a brace
    pair (e.g. '\{yesterday\}', '\{1 month 2 weeks 3 days 1 hour 1
    second ago\}' or '\{1979-02-26 18:30:00\}') to specify the value
    of the ref at a prior point in time.  This suffix may only be
  * A suffix '{tilde}<n>' to a revision parameter means the commit
    object that is the <n>th generation grand-parent of the named
    commit object, following only the first parent.  I.e. rev~3 is
 -  equivalent to rev{caret}{caret}{caret} which is equivalent to\
 -  rev{caret}1{caret}1{caret}1.
 +  equivalent to rev{caret}{caret}{caret} which is equivalent to
 +  rev{caret}1{caret}1{caret}1.  See below for a illustration of
 +  the usage of this form.
  
  * A suffix '{caret}' followed by an object type name enclosed in
    brace pair (e.g. `v0.99.8{caret}\{commit\}`) means the object
@@@ -239,14 -222,21 +239,21 @@@ of `r1` and `r2` and is defined a
  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:
+ Two other shorthands for naming a set that is formed by a commit
+ and its parent commits exists.  `r1{caret}@` notation means all
+ parents of `r1`.  `r1{caret}!` includes commit `r1` but excludes
+ its all parents.
+ Here are a handful examples:
  
     D                A B D
     D F              A B C D F
-    ^A G                   B D
+    ^A G             B D
     ^A F             B C F
     G...I            C D F G I
-    ^B G I         C D F G I
+    ^B G I           C D F G I
+    F^@              A B C
+    F^! H            D F H
  
  Author
  ------
diff --combined Documentation/git.txt
index 4ce4f8d1c6efd18088c046a682ef66d6ae067bdc,7074e324584b60746d8797e918f3bdd7d0cb8e41..4facf2309512380fc796e5f64e400c6ebcb08dbd
@@@ -72,6 -72,185 +72,6 @@@ GIT COMMAND
  We divide git into high level ("porcelain") commands and low level
  ("plumbing") commands.
  
 -Low-level commands (plumbing)
 ------------------------------
 -
 -Although git includes its
 -own porcelain layer, its low-level commands are sufficient to support
 -development of alternative porcelains.  Developers of such porcelains
 -might start by reading about gitlink:git-update-index[1] and
 -gitlink:git-read-tree[1].
 -
 -We divide the low-level commands into commands that manipulate objects (in
 -the repository, index, and working tree), commands that interrogate and
 -compare objects, and commands that move objects and references between
 -repositories.
 -
 -Manipulation commands
 -~~~~~~~~~~~~~~~~~~~~~
 -gitlink:git-apply[1]::
 -      Reads a "diff -up1" or git generated patch file and
 -      applies it to the working tree.
 -
 -gitlink:git-checkout-index[1]::
 -      Copy files from the index to the working tree.
 -
 -gitlink:git-commit-tree[1]::
 -      Creates a new commit object.
 -
 -gitlink:git-hash-object[1]::
 -      Computes the object ID from a file.
 -
 -gitlink:git-index-pack[1]::
 -      Build pack idx file for an existing packed archive.
 -
 -gitlink:git-init-db[1]::
 -      Creates an empty git object database, or reinitialize an
 -      existing one.
 -
 -gitlink:git-merge-index[1]::
 -      Runs a merge for files needing merging.
 -
 -gitlink:git-mktag[1]::
 -      Creates a tag object.
 -
 -gitlink:git-mktree[1]::
 -      Build a tree-object from ls-tree formatted text.
 -
 -gitlink:git-pack-objects[1]::
 -      Creates a packed archive of objects.
 -
 -gitlink:git-prune-packed[1]::
 -      Remove extra objects that are already in pack files.
 -
 -gitlink:git-read-tree[1]::
 -      Reads tree information into the index.
 -
 -gitlink:git-repo-config[1]::
 -      Get and set options in .git/config.
 -
 -gitlink:git-unpack-objects[1]::
 -      Unpacks objects out of a packed archive.
 -
 -gitlink:git-update-index[1]::
 -      Registers files in the working tree to the index.
 -
 -gitlink:git-write-tree[1]::
 -      Creates a tree from the index.
 -
 -
 -Interrogation commands
 -~~~~~~~~~~~~~~~~~~~~~~
 -
 -gitlink:git-cat-file[1]::
 -      Provide content or type/size information for repository objects.
 -
 -gitlink:git-describe[1]::
 -      Show the most recent tag that is reachable from a commit.
 -
 -gitlink:git-diff-index[1]::
 -      Compares content and mode of blobs between the index and repository.
 -
 -gitlink:git-diff-files[1]::
 -      Compares files in the working tree and the index.
 -
 -gitlink:git-diff-stages[1]::
 -      Compares two "merge stages" in the index.
 -
 -gitlink:git-diff-tree[1]::
 -      Compares the content and mode of blobs found via two tree objects.
 -
 -gitlink:git-fsck-objects[1]::
 -      Verifies the connectivity and validity of the objects in the database.
 -
 -gitlink:git-ls-files[1]::
 -      Information about files in the index and the working tree.
 -
 -gitlink:git-ls-tree[1]::
 -      Displays a tree object in human readable form.
 -
 -gitlink:git-merge-base[1]::
 -      Finds as good common ancestors as possible for a merge.
 -
 -gitlink:git-name-rev[1]::
 -      Find symbolic names for given revs.
 -
 -gitlink:git-pack-redundant[1]::
 -      Find redundant pack files.
 -
 -gitlink:git-rev-list[1]::
 -      Lists commit objects in reverse chronological order.
 -
 -gitlink:git-show-index[1]::
 -      Displays contents of a pack idx file.
 -
 -gitlink:git-tar-tree[1]::
 -      Creates a tar archive of the files in the named tree object.
 -
 -gitlink:git-unpack-file[1]::
 -      Creates a temporary file with a blob's contents.
 -
 -gitlink:git-var[1]::
 -      Displays a git logical variable.
 -
 -gitlink:git-verify-pack[1]::
 -      Validates packed git archive files.
 -
 -In general, the interrogate commands do not touch the files in
 -the working tree.
 -
 -
 -Synching repositories
 -~~~~~~~~~~~~~~~~~~~~~
 -
 -gitlink:git-fetch-pack[1]::
 -      Updates from a remote repository (engine for ssh and
 -      local transport).
 -
 -gitlink:git-http-fetch[1]::
 -      Downloads a remote git repository via HTTP by walking
 -      commit chain.
 -
 -gitlink:git-local-fetch[1]::
 -      Duplicates another git repository on a local system by
 -      walking commit chain.
 -
 -gitlink:git-peek-remote[1]::
 -      Lists references on a remote repository using
 -      upload-pack protocol (engine for ssh and local
 -      transport).
 -
 -gitlink:git-receive-pack[1]::
 -      Invoked by 'git-send-pack' to receive what is pushed to it.
 -
 -gitlink:git-send-pack[1]::
 -      Pushes to a remote repository, intelligently.
 -
 -gitlink:git-http-push[1]::
 -      Push missing objects using HTTP/DAV.
 -
 -gitlink:git-shell[1]::
 -      Restricted shell for GIT-only SSH access.
 -
 -gitlink:git-ssh-fetch[1]::
 -      Pulls from a remote repository over ssh connection by
 -      walking commit chain.
 -
 -gitlink:git-ssh-upload[1]::
 -      Helper "server-side" program used by git-ssh-fetch.
 -
 -gitlink:git-update-server-info[1]::
 -      Updates auxiliary information on a dumb server to help
 -      clients discover references and packs on it.
 -
 -gitlink:git-upload-archive[1]::
 -      Invoked by 'git-archive' to send a generated archive.
 -
 -gitlink:git-upload-pack[1]::
 -      Invoked by 'git-fetch-pack' to push
 -      what are asked for.
 -
 -
  High-level commands (porcelain)
  -------------------------------
  
@@@ -141,11 -320,8 +141,11 @@@ gitlink:git-merge[1]:
  gitlink:git-mv[1]::
        Move or rename a file, a directory, or a symlink.
  
 +gitlink:git-pack-refs[1]::
 +      Pack heads and tags for efficient repository access.
 +
  gitlink:git-pull[1]::
 -      Fetch from and merge with a remote repository.
 +      Fetch from and merge with a remote repository or a local branch.
  
  gitlink:git-push[1]::
        Update remote refs along with associated objects.
@@@ -254,6 -430,9 +254,9 @@@ gitlink:git-annotate[1]:
  gitlink:git-blame[1]::
        Blame file lines on commits.
  
+ gitlink:git-pickaxe[1]::
+       Find out where each line in a file came from.
  gitlink:git-check-ref-format[1]::
        Make sure ref name is well formed.
  
@@@ -312,191 -491,6 +315,191 @@@ gitlink:git-stripspace[1]:
        Filter out empty lines.
  
  
 +Low-level commands (plumbing)
 +-----------------------------
 +
 +Although git includes its
 +own porcelain layer, its low-level commands are sufficient to support
 +development of alternative porcelains.  Developers of such porcelains
 +might start by reading about gitlink:git-update-index[1] and
 +gitlink:git-read-tree[1].
 +
 +We divide the low-level commands into commands that manipulate objects (in
 +the repository, index, and working tree), commands that interrogate and
 +compare objects, and commands that move objects and references between
 +repositories.
 +
 +Manipulation commands
 +~~~~~~~~~~~~~~~~~~~~~
 +gitlink:git-apply[1]::
 +      Reads a "diff -up1" or git generated patch file and
 +      applies it to the working tree.
 +
 +gitlink:git-checkout-index[1]::
 +      Copy files from the index to the working tree.
 +
 +gitlink:git-commit-tree[1]::
 +      Creates a new commit object.
 +
 +gitlink:git-hash-object[1]::
 +      Computes the object ID from a file.
 +
 +gitlink:git-index-pack[1]::
 +      Build pack idx file for an existing packed archive.
 +
 +gitlink:git-init-db[1]::
 +      Creates an empty git object database, or reinitialize an
 +      existing one.
 +
 +gitlink:git-merge-index[1]::
 +      Runs a merge for files needing merging.
 +
 +gitlink:git-mktag[1]::
 +      Creates a tag object.
 +
 +gitlink:git-mktree[1]::
 +      Build a tree-object from ls-tree formatted text.
 +
 +gitlink:git-pack-objects[1]::
 +      Creates a packed archive of objects.
 +
 +gitlink:git-prune-packed[1]::
 +      Remove extra objects that are already in pack files.
 +
 +gitlink:git-read-tree[1]::
 +      Reads tree information into the index.
 +
 +gitlink:git-repo-config[1]::
 +      Get and set options in .git/config.
 +
 +gitlink:git-unpack-objects[1]::
 +      Unpacks objects out of a packed archive.
 +
 +gitlink:git-update-index[1]::
 +      Registers files in the working tree to the index.
 +
 +gitlink:git-write-tree[1]::
 +      Creates a tree from the index.
 +
 +
 +Interrogation commands
 +~~~~~~~~~~~~~~~~~~~~~~
 +
 +gitlink:git-cat-file[1]::
 +      Provide content or type/size information for repository objects.
 +
 +gitlink:git-describe[1]::
 +      Show the most recent tag that is reachable from a commit.
 +
 +gitlink:git-diff-index[1]::
 +      Compares content and mode of blobs between the index and repository.
 +
 +gitlink:git-diff-files[1]::
 +      Compares files in the working tree and the index.
 +
 +gitlink:git-diff-stages[1]::
 +      Compares two "merge stages" in the index.
 +
 +gitlink:git-diff-tree[1]::
 +      Compares the content and mode of blobs found via two tree objects.
 +
 +gitlink:git-for-each-ref[1]::
 +      Output information on each ref.
 +
 +gitlink:git-fsck-objects[1]::
 +      Verifies the connectivity and validity of the objects in the database.
 +
 +gitlink:git-ls-files[1]::
 +      Information about files in the index and the working tree.
 +
 +gitlink:git-ls-tree[1]::
 +      Displays a tree object in human readable form.
 +
 +gitlink:git-merge-base[1]::
 +      Finds as good common ancestors as possible for a merge.
 +
 +gitlink:git-name-rev[1]::
 +      Find symbolic names for given revs.
 +
 +gitlink:git-pack-redundant[1]::
 +      Find redundant pack files.
 +
 +gitlink:git-rev-list[1]::
 +      Lists commit objects in reverse chronological order.
 +
 +gitlink:git-show-index[1]::
 +      Displays contents of a pack idx file.
 +
 +gitlink:git-show-ref[1]::
 +      List references in a local repository.
 +
 +gitlink:git-tar-tree[1]::
 +      Creates a tar archive of the files in the named tree object.
 +
 +gitlink:git-unpack-file[1]::
 +      Creates a temporary file with a blob's contents.
 +
 +gitlink:git-var[1]::
 +      Displays a git logical variable.
 +
 +gitlink:git-verify-pack[1]::
 +      Validates packed git archive files.
 +
 +In general, the interrogate commands do not touch the files in
 +the working tree.
 +
 +
 +Synching repositories
 +~~~~~~~~~~~~~~~~~~~~~
 +
 +gitlink:git-fetch-pack[1]::
 +      Updates from a remote repository (engine for ssh and
 +      local transport).
 +
 +gitlink:git-http-fetch[1]::
 +      Downloads a remote git repository via HTTP by walking
 +      commit chain.
 +
 +gitlink:git-local-fetch[1]::
 +      Duplicates another git repository on a local system by
 +      walking commit chain.
 +
 +gitlink:git-peek-remote[1]::
 +      Lists references on a remote repository using
 +      upload-pack protocol (engine for ssh and local
 +      transport).
 +
 +gitlink:git-receive-pack[1]::
 +      Invoked by 'git-send-pack' to receive what is pushed to it.
 +
 +gitlink:git-send-pack[1]::
 +      Pushes to a remote repository, intelligently.
 +
 +gitlink:git-http-push[1]::
 +      Push missing objects using HTTP/DAV.
 +
 +gitlink:git-shell[1]::
 +      Restricted shell for GIT-only SSH access.
 +
 +gitlink:git-ssh-fetch[1]::
 +      Pulls from a remote repository over ssh connection by
 +      walking commit chain.
 +
 +gitlink:git-ssh-upload[1]::
 +      Helper "server-side" program used by git-ssh-fetch.
 +
 +gitlink:git-update-server-info[1]::
 +      Updates auxiliary information on a dumb server to help
 +      clients discover references and packs on it.
 +
 +gitlink:git-upload-archive[1]::
 +      Invoked by 'git-archive' to send a generated archive.
 +
 +gitlink:git-upload-pack[1]::
 +      Invoked by 'git-fetch-pack' to push
 +      what are asked for.
 +
 +
  Configuration Mechanism
  -----------------------
  
@@@ -571,9 -565,6 +574,9 @@@ HEAD:
        a valid head 'name'
        (i.e. the contents of `$GIT_DIR/refs/heads/<head>`).
  
 +For a more complete list of ways to spell object names, see
 +"SPECIFYING REVISIONS" section in gitlink:git-rev-parse[1].
 +
  
  File/Directory Structure
  ------------------------
diff --combined Makefile
index 6d324fb56e9feaaf0780b11a6eb190dbd9bf1675,461fef636d59ebf46848b48636ae770e8684558f..a042df4a2ca7cb6e523151fb22d005ffe3d41b84
+++ b/Makefile
@@@ -132,8 -132,6 +132,8 @@@ GITWEB_HOMETEXT = indextext.htm
  GITWEB_CSS = gitweb.css
  GITWEB_LOGO = git-logo.png
  GITWEB_FAVICON = git-favicon.png
 +GITWEB_SITE_HEADER =
 +GITWEB_SITE_FOOTER =
  
  export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR
  
@@@ -158,8 -156,8 +158,8 @@@ BASIC_CFLAGS 
  BASIC_LDFLAGS =
  
  SCRIPT_SH = \
 -      git-bisect.sh git-branch.sh git-checkout.sh \
 -      git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
 +      git-bisect.sh git-checkout.sh \
 +      git-clean.sh git-clone.sh git-commit.sh \
        git-fetch.sh \
        git-ls-remote.sh \
        git-merge-one-file.sh git-parse-remote.sh \
  SCRIPT_PERL = \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
        git-shortlog.perl git-rerere.perl \
 -      git-annotate.perl git-cvsserver.perl \
 +      git-cvsserver.perl \
        git-svnimport.perl git-cvsexportcommit.perl \
        git-send-email.perl git-svn.perl
  
@@@ -187,12 -185,15 +187,12 @@@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH
          $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
          git-cherry-pick git-status git-instaweb
  
 -# The ones that do not have to link with lcrypto, lz nor xdiff.
 -SIMPLE_PROGRAMS = \
 -      git-daemon$X
 -
  # ... and all the rest that could be moved out of bindir to gitexecdir
  PROGRAMS = \
        git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
        git-hash-object$X git-index-pack$X git-local-fetch$X \
        git-merge-base$X \
 +      git-daemon$X \
        git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
        git-peek-remote$X git-receive-pack$X \
        git-send-pack$X git-shell$X \
  EXTRA_PROGRAMS =
  
  BUILT_INS = \
 -      git-format-patch$X git-show$X git-whatchanged$X \
 +      git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
        git-get-tar-commit-id$X \
        $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
  
  # what 'all' will build and 'install' will install, in gitexecdir
 -ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) \
 +ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) \
        git-merge-recur$X
  
  # Backward compatibility -- to be removed after 1.0
@@@ -257,17 -258,15 +257,17 @@@ LIB_OBJS = 
        quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
        tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
 -      fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
 +      revision.o pager.o tree-walk.o xdiff-interface.o \
        write_or_die.o trace.o list-objects.o grep.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
        color.o wt-status.o archive-zip.o archive-tar.o
  
  BUILTIN_OBJS = \
        builtin-add.o \
 +      builtin-annotate.o \
        builtin-apply.o \
        builtin-archive.o \
 +      builtin-branch.o \
        builtin-cat-file.o \
        builtin-checkout-index.o \
        builtin-check-ref-format.o \
        builtin-diff-stages.o \
        builtin-diff-tree.o \
        builtin-fmt-merge-msg.o \
 +      builtin-for-each-ref.o \
        builtin-grep.o \
        builtin-init-db.o \
        builtin-log.o \
        builtin-mv.o \
        builtin-name-rev.o \
        builtin-pack-objects.o \
+       builtin-pickaxe.o \
        builtin-prune.o \
        builtin-prune-packed.o \
        builtin-push.o \
        builtin-update-ref.o \
        builtin-upload-archive.o \
        builtin-verify-pack.o \
 -      builtin-write-tree.o
 +      builtin-write-tree.o \
 +      builtin-show-ref.o \
 +      builtin-pack-refs.o
  
  GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
  EXTLIBS = -lz
@@@ -483,9 -480,11 +484,9 @@@ ifdef NEEDS_LIBICON
  endif
  ifdef NEEDS_SOCKET
        EXTLIBS += -lsocket
 -      SIMPLE_LIB += -lsocket
  endif
  ifdef NEEDS_NSL
        EXTLIBS += -lnsl
 -      SIMPLE_LIB += -lnsl
  endif
  ifdef NO_D_TYPE_IN_DIRENT
        BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT
@@@ -677,8 -676,6 +678,8 @@@ gitweb/gitweb.cgi: gitweb/gitweb.per
            -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
            -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
            -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
 +          -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
 +          -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
            $< >$@+
        chmod +x $@+
        mv $@+ $@
@@@ -732,6 -729,11 +733,6 @@@ endi
  git-%$X: %.o $(GITLIBS)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
 -$(SIMPLE_PROGRAMS) : $(LIB_FILE)
 -$(SIMPLE_PROGRAMS) : git-%$X : %.o
 -      $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 -              $(LIB_FILE) $(SIMPLE_LIB)
 -
  ssh-pull.o: ssh-fetch.c
  ssh-push.o: ssh-upload.c
  git-local-fetch$X: fetch.o
@@@ -932,8 -934,3 +933,8 @@@ check-docs:
                *) echo "no link: $$v";; \
                esac ; \
        done | sort
 +
 +### Make sure built-ins do not have dups and listed in git.c
 +#
 +check-builtins::
 +      ./check-builtins.sh
diff --combined blame.c
index 3ec1c8f1b287654cccb0f2427810d0afead5c44b,d0e506c02b2e2d0c8fa834ddc86c6b325ea1a8cb..1144c8533724b06534caad7f693bdb65df496682
+++ b/blame.c
  #include "diffcore.h"
  #include "revision.h"
  #include "xdiff-interface.h"
 +#include "quote.h"
  
 +#ifndef DEBUG
  #define DEBUG 0
 +#endif
  
 -static const char blame_usage[] = "git-blame [-c] [-l] [-t] [-S <revs-file>] [--] file [commit]\n"
 -      "  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
 -      "  -l, --long          Show long commit SHA1 (Default: off)\n"
 -      "  -t, --time          Show raw timestamp (Default: off)\n"
 -      "  -S, --revs-file     Use revisions from revs-file instead of calling git-rev-list\n"
 -      "  -h, --help          This message";
 +static const char blame_usage[] =
 +"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-S <revs-file>] [--] file [commit]\n"
 +"  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
 +"  -l, --long          Show long commit SHA1 (Default: off)\n"
 +"  -t, --time          Show raw timestamp (Default: off)\n"
 +"  -f, --show-name     Show original filename (Default: auto)\n"
 +"  -n, --show-number   Show original linenumber (Default: off)\n"
 +"  -p, --porcelain     Show in a format designed for machine consumption\n"
 +"  -S revs-file        Use revisions from revs-file instead of calling git-rev-list\n"
 +"  -h, --help          This message";
  
  static struct commit **blame_lines;
  static int num_blame_lines;
 -static charblame_contents;
 +static char *blame_contents;
  static int blame_len;
  
  struct util_info {
        char *buf;
        unsigned long size;
        int num_lines;
 -      const char* pathname;
 +      const char *pathname;
 +      unsigned meta_given:1;
  
 -      voidtopo_data;
 +      void *topo_data;
  };
  
  struct chunk {
@@@ -67,6 -59,7 +67,7 @@@ static void get_blob(struct commit *com
  static int num_get_patch;
  static int num_commits;
  static int patch_time;
+ static int num_read_blob;
  
  struct blame_diff_state {
        struct xdiff_emit_state xm;
@@@ -164,10 -157,11 +165,10 @@@ static int get_blob_sha1_internal(cons
                                  unsigned mode, int stage);
  
  static unsigned char blob_sha1[20];
 -static const charblame_file;
 +static const char *blame_file;
  static int get_blob_sha1(struct tree *t, const char *pathname,
                         unsigned char *sha1)
  {
 -      int i;
        const char *pathspec[2];
        blame_file = pathname;
        pathspec[0] = pathname;
        hashclr(blob_sha1);
        read_tree_recursive(t, "", 0, 0, pathspec, get_blob_sha1_internal);
  
 -      for (i = 0; i < 20; i++) {
 -              if (blob_sha1[i] != 0)
 -                      break;
 -      }
 -
 -      if (i == 20)
 +      if (is_null_sha1(blob_sha1))
                return -1;
  
        hashcpy(sha1, blob_sha1);
@@@ -206,6 -205,7 +207,7 @@@ static void get_blob(struct commit *com
                return;
  
        info->buf = read_sha1_file(info->sha1, type, &info->size);
+       num_read_blob++;
  
        assert(!strcmp(type, blob_type));
  }
@@@ -234,9 -234,6 +236,9 @@@ static void print_map(struct commit *cm
            util2->num_lines ? util->num_lines : util2->num_lines;
        int num;
  
 +      if (print_map == NULL)
 +              ; /* to avoid "unused function" warning */
 +
        for (i = 0; i < max; i++) {
                printf("i: %d ", i);
                num = -1;
                if (i < util->num_lines) {
                        num = util->line_map[i];
                        printf("%d\t", num);
 -              } else
 +              }
 +              else
                        printf("\t");
  
                if (i < util2->num_lines) {
                        printf("%d\t", num2);
                        if (num != -1 && num2 != num)
                                printf("---");
 -              } else
 +              }
 +              else
                        printf("\t");
  
                printf("\n");
@@@ -273,12 -268,12 +275,12 @@@ static void fill_line_map(struct commi
        int cur_chunk = 0;
        int i1, i2;
  
 -      if (p->num && DEBUG)
 -              print_patch(p);
 -
 -      if (DEBUG)
 +      if (DEBUG) {
 +              if (p->num)
 +                      print_patch(p);
                printf("num lines 1: %d num lines 2: %d\n", util->num_lines,
                       util2->num_lines);
 +      }
  
        for (i1 = 0, i2 = 0; i1 < util->num_lines; i1++, i2++) {
                struct chunk *chunk = NULL;
                                i2 += chunk->len2;
  
                        cur_chunk++;
 -              } else {
 +              }
 +              else {
                        if (i2 >= util2->num_lines)
                                break;
  
@@@ -335,15 -329,19 +337,15 @@@ static int map_line(struct commit *comm
        return info->line_map[line];
  }
  
 -static struct util_infoget_util(struct commit *commit)
 +static struct util_info *get_util(struct commit *commit)
  {
        struct util_info *util = commit->util;
  
        if (util)
                return util;
  
 -      util = xmalloc(sizeof(struct util_info));
 -      util->buf = NULL;
 -      util->size = 0;
 -      util->line_map = NULL;
 +      util = xcalloc(1, sizeof(struct util_info));
        util->num_lines = -1;
 -      util->pathname = NULL;
        commit->util = util;
        return util;
  }
@@@ -373,7 -371,7 +375,7 @@@ static void alloc_line_map(struct commi
                if (util->buf[i] == '\n')
                        util->num_lines++;
        }
 -      if(util->buf[util->size - 1] != '\n')
 +      if (util->buf[util->size - 1] != '\n')
                util->num_lines++;
  
        util->line_map = xmalloc(sizeof(int) * util->num_lines);
                util->line_map[i] = -1;
  }
  
 -static void init_first_commit(struct commit* commit, const char* filename)
 +static void init_first_commit(struct commit *commit, const char *filename)
  {
 -      struct util_infoutil = commit->util;
 +      struct util_info *util = commit->util;
        int i;
  
        util->pathname = filename;
                util->line_map[i] = i;
  }
  
 -
  static void process_commits(struct rev_info *rev, const char *path,
 -                          struct commit** initial)
 +                          struct commit **initial)
  {
        int i;
 -      struct util_infoutil;
 +      struct util_info *util;
        int lines_left;
        int *blame_p;
        int *new_lines;
        int new_lines_len;
  
 -      struct commitcommit = get_revision(rev);
 +      struct commit *commit = get_revision(rev);
        assert(commit);
        init_first_commit(commit, path);
  
                     parents != NULL; parents = parents->next)
                        num_parents++;
  
 -              if(num_parents == 0)
 +              if (num_parents == 0)
                        *initial = commit;
  
                if (fill_util_info(commit))
        } while ((commit = get_revision(rev)) != NULL);
  }
  
 -
 -static int compare_tree_path(struct rev_info* revs,
 -                           struct commit* c1, struct commit* c2)
 +static int compare_tree_path(struct rev_info *revs,
 +                           struct commit *c1, struct commit *c2)
  {
        int ret;
 -      const charpaths[2];
 -      struct util_infoutil = c2->util;
 +      const char *paths[2];
 +      struct util_info *util = c2->util;
        paths[0] = util->pathname;
        paths[1] = NULL;
  
        return ret;
  }
  
 -
 -static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1,
 -                                 const char* path)
 +static int same_tree_as_empty_path(struct rev_info *revs, struct tree *t1,
 +                                 const char *path)
  {
        int ret;
 -      const charpaths[2];
 +      const char *paths[2];
        paths[0] = path;
        paths[1] = NULL;
  
        return ret;
  }
  
 -static const char* find_rename(struct commit* commit, struct commit* parent)
 +static const char *find_rename(struct commit *commit, struct commit *parent)
  {
 -      struct util_infocutil = commit->util;
 +      struct util_info *cutil = commit->util;
        struct diff_options diff_opts;
        const char *paths[1];
        int i;
        for (i = 0; i < diff_queued_diff.nr; i++) {
                struct diff_filepair *p = diff_queued_diff.queue[i];
  
 -              if (p->status == 'R' && !strcmp(p->one->path, cutil->pathname)) {
 +              if (p->status == 'R' &&
 +                  !strcmp(p->one->path, cutil->pathname)) {
                        if (DEBUG)
 -                              printf("rename %s -> %s\n", p->one->path, p->two->path);
 +                              printf("rename %s -> %s\n",
 +                                     p->one->path, p->two->path);
                        return p->two->path;
                }
        }
@@@ -585,7 -584,7 +587,7 @@@ static void simplify_commit(struct rev_
                return;
  
        if (!commit->parents) {
 -              struct util_infoutil = commit->util;
 +              struct util_info *util = commit->util;
                if (!same_tree_as_empty_path(revs, commit->tree,
                                             util->pathname))
                        commit->object.flags |= TREECHANGE;
  
                case REV_TREE_NEW:
                {
 -
 -                      struct util_info* util = commit->util;
 +                      struct util_info *util = commit->util;
                        if (revs->remove_empty_trees &&
                            same_tree_as_empty_path(revs, p->tree,
                                                    util->pathname)) {
 -                              const charnew_name = find_rename(commit, p);
 +                              const char *new_name = find_rename(commit, p);
                                if (new_name) {
 -                                      struct util_infoputil = get_util(p);
 +                                      struct util_info *putil = get_util(p);
                                        if (!putil->pathname)
                                                putil->pathname = xstrdup(new_name);
 -                              } else {
 +                              }
 +                              else {
                                        *pp = parent->next;
                                        continue;
                                }
        commit->object.flags |= TREECHANGE;
  }
  
 -
  struct commit_info
  {
 -      charauthor;
 -      charauthor_mail;
 +      char *author;
 +      char *author_mail;
        unsigned long author_time;
 -      char* author_tz;
 +      char *author_tz;
 +
 +      /* filled only when asked for details */
 +      char *committer;
 +      char *committer_mail;
 +      unsigned long committer_time;
 +      char *committer_tz;
 +
 +      char *summary;
  };
  
 -static void get_commit_info(struct commit* commit, struct commit_info* ret)
 +static void get_ac_line(const char *inbuf, const char *what,
 +                      int bufsz, char *person, char **mail,
 +                      unsigned long *time, char **tz)
  {
        int len;
 -      char* tmp;
 -      static char author_buf[1024];
 -
 -      tmp = strstr(commit->buffer, "\nauthor ") + 8;
 -      len = strchr(tmp, '\n') - tmp;
 -      ret->author = author_buf;
 -      memcpy(ret->author, tmp, len);
 +      char *tmp, *endp;
 +
 +      tmp = strstr(inbuf, what);
 +      if (!tmp)
 +              goto error_out;
 +      tmp += strlen(what);
 +      endp = strchr(tmp, '\n');
 +      if (!endp)
 +              len = strlen(tmp);
 +      else
 +              len = endp - tmp;
 +      if (bufsz <= len) {
 +      error_out:
 +              /* Ugh */
 +              person = *mail = *tz = "(unknown)";
 +              *time = 0;
 +              return;
 +      }
 +      memcpy(person, tmp, len);
  
 -      tmp = ret->author;
 +      tmp = person;
        tmp += len;
        *tmp = 0;
 -      while(*tmp != ' ')
 +      while (*tmp != ' ')
                tmp--;
 -      ret->author_tz = tmp+1;
 +      *tz = tmp+1;
  
        *tmp = 0;
 -      while(*tmp != ' ')
 +      while (*tmp != ' ')
                tmp--;
 -      ret->author_time = strtoul(tmp, NULL, 10);
 +      *time = strtoul(tmp, NULL, 10);
  
        *tmp = 0;
 -      while(*tmp != ' ')
 +      while (*tmp != ' ')
                tmp--;
 -      ret->author_mail = tmp + 1;
 -
 +      *mail = tmp + 1;
        *tmp = 0;
  }
  
 -static const char* format_time(unsigned long time, const char* tz_str,
 +static void get_commit_info(struct commit *commit, struct commit_info *ret, int detailed)
 +{
 +      int len;
 +      char *tmp, *endp;
 +      static char author_buf[1024];
 +      static char committer_buf[1024];
 +      static char summary_buf[1024];
 +
 +      ret->author = author_buf;
 +      get_ac_line(commit->buffer, "\nauthor ",
 +                  sizeof(author_buf), author_buf, &ret->author_mail,
 +                  &ret->author_time, &ret->author_tz);
 +
 +      if (!detailed)
 +              return;
 +
 +      ret->committer = committer_buf;
 +      get_ac_line(commit->buffer, "\ncommitter ",
 +                  sizeof(committer_buf), committer_buf, &ret->committer_mail,
 +                  &ret->committer_time, &ret->committer_tz);
 +
 +      ret->summary = summary_buf;
 +      tmp = strstr(commit->buffer, "\n\n");
 +      if (!tmp) {
 +      error_out:
 +              sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
 +              return;
 +      }
 +      tmp += 2;
 +      endp = strchr(tmp, '\n');
 +      if (!endp)
 +              goto error_out;
 +      len = endp - tmp;
 +      if (len >= sizeof(summary_buf))
 +              goto error_out;
 +      memcpy(summary_buf, tmp, len);
 +      summary_buf[len] = 0;
 +}
 +
 +static const char *format_time(unsigned long time, const char *tz_str,
                               int show_raw_time)
  {
        static char time_buf[128];
        return time_buf;
  }
  
 -static void topo_setter(struct commit* c, void* data)
 +static void topo_setter(struct commit *c, void *data)
  {
 -      struct util_infoutil = c->util;
 +      struct util_info *util = c->util;
        util->topo_data = data;
  }
  
 -static void* topo_getter(struct commit* c)
 +static void *topo_getter(struct commit *c)
  {
 -      struct util_infoutil = c->util;
 +      struct util_info *util = c->util;
        return util->topo_data;
  }
  
@@@ -797,101 -737,6 +799,101 @@@ static int read_ancestry(const char *gr
        return 0;
  }
  
 +static int lineno_width(int lines)
 +{
 +      int i, width;
 +
 +      for (width = 1, i = 10; i <= lines + 1; width++)
 +              i *= 10;
 +      return width;
 +}
 +
 +static int find_orig_linenum(struct util_info *u, int lineno)
 +{
 +      int i;
 +
 +      for (i = 0; i < u->num_lines; i++)
 +              if (lineno == u->line_map[i])
 +                      return i + 1;
 +      return 0;
 +}
 +
 +static void emit_meta(struct commit *c, int lno,
 +                    int sha1_len, int compatibility, int porcelain,
 +                    int show_name, int show_number, int show_raw_time,
 +                    int longest_file, int longest_author,
 +                    int max_digits, int max_orig_digits)
 +{
 +      struct util_info *u;
 +      int lineno;
 +      struct commit_info ci;
 +
 +      u = c->util;
 +      lineno = find_orig_linenum(u, lno);
 +
 +      if (porcelain) {
 +              int group_size = -1;
 +              struct commit *cc = (lno == 0) ? NULL : blame_lines[lno-1];
 +              if (cc != c) {
 +                      /* This is the beginning of this group */
 +                      int i;
 +                      for (i = lno + 1; i < num_blame_lines; i++)
 +                              if (blame_lines[i] != c)
 +                                      break;
 +                      group_size = i - lno;
 +              }
 +              if (0 < group_size)
 +                      printf("%s %d %d %d\n", sha1_to_hex(c->object.sha1),
 +                             lineno, lno + 1, group_size);
 +              else
 +                      printf("%s %d %d\n", sha1_to_hex(c->object.sha1),
 +                             lineno, lno + 1);
 +              if (!u->meta_given) {
 +                      get_commit_info(c, &ci, 1);
 +                      printf("author %s\n", ci.author);
 +                      printf("author-mail %s\n", ci.author_mail);
 +                      printf("author-time %lu\n", ci.author_time);
 +                      printf("author-tz %s\n", ci.author_tz);
 +                      printf("committer %s\n", ci.committer);
 +                      printf("committer-mail %s\n", ci.committer_mail);
 +                      printf("committer-time %lu\n", ci.committer_time);
 +                      printf("committer-tz %s\n", ci.committer_tz);
 +                      printf("filename ");
 +                      if (quote_c_style(u->pathname, NULL, NULL, 0))
 +                              quote_c_style(u->pathname, NULL, stdout, 0);
 +                      else
 +                              fputs(u->pathname, stdout);
 +                      printf("\nsummary %s\n", ci.summary);
 +
 +                      u->meta_given = 1;
 +              }
 +              putchar('\t');
 +              return;
 +      }
 +
 +      get_commit_info(c, &ci, 0);
 +      fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout);
 +      if (compatibility) {
 +              printf("\t(%10s\t%10s\t%d)", ci.author,
 +                     format_time(ci.author_time, ci.author_tz,
 +                                 show_raw_time),
 +                     lno + 1);
 +      }
 +      else {
 +              if (show_name)
 +                      printf(" %-*.*s", longest_file, longest_file,
 +                             u->pathname);
 +              if (show_number)
 +                      printf(" %*d", max_orig_digits,
 +                             lineno);
 +              printf(" (%-*.*s %10s %*d) ",
 +                     longest_author, longest_author, ci.author,
 +                     format_time(ci.author_time, ci.author_tz,
 +                                 show_raw_time),
 +                     max_digits, lno + 1);
 +      }
 +}
 +
  int main(int argc, const char **argv)
  {
        int i;
        int compatibility = 0;
        int show_raw_time = 0;
        int options = 1;
 -      struct commitstart_commit;
 +      struct commit *start_commit;
  
 -      const charargs[10];
 +      const char *args[10];
        struct rev_info rev;
  
        struct commit_info ci;
        const char *buf;
 -      int max_digits;
 -      int longest_file, longest_author;
 -      int found_rename;
 +      int max_digits, max_orig_digits;
 +      int longest_file, longest_author, longest_file_lines;
 +      int show_name = 0;
 +      int show_number = 0;
 +      int porcelain = 0;
  
 -      const charprefix = setup_git_directory();
 +      const char *prefix = setup_git_directory();
        git_config(git_default_config);
  
 -      for(i = 1; i < argc; i++) {
 -              if(options) {
 -                      if(!strcmp(argv[i], "-h") ||
 +      for (i = 1; i < argc; i++) {
 +              if (options) {
 +                      if (!strcmp(argv[i], "-h") ||
                           !strcmp(argv[i], "--help"))
                                usage(blame_usage);
 -                      else if(!strcmp(argv[i], "-l") ||
 -                              !strcmp(argv[i], "--long")) {
 +                      if (!strcmp(argv[i], "-l") ||
 +                          !strcmp(argv[i], "--long")) {
                                sha1_len = 40;
                                continue;
 -                      } else if(!strcmp(argv[i], "-c") ||
 -                                !strcmp(argv[i], "--compatibility")) {
 +                      }
 +                      if (!strcmp(argv[i], "-c") ||
 +                          !strcmp(argv[i], "--compatibility")) {
                                compatibility = 1;
                                continue;
 -                      } else if(!strcmp(argv[i], "-t") ||
 -                                !strcmp(argv[i], "--time")) {
 +                      }
 +                      if (!strcmp(argv[i], "-t") ||
 +                          !strcmp(argv[i], "--time")) {
                                show_raw_time = 1;
                                continue;
 -                      } else if(!strcmp(argv[i], "-S")) {
 +                      }
 +                      if (!strcmp(argv[i], "-S")) {
                                if (i + 1 < argc &&
                                    !read_ancestry(argv[i + 1], &sha1_p)) {
                                        compatibility = 1;
                                        continue;
                                }
                                usage(blame_usage);
 -                      } else if(!strcmp(argv[i], "--")) {
 +                      }
 +                      if (!strcmp(argv[i], "-f") ||
 +                          !strcmp(argv[i], "--show-name")) {
 +                              show_name = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(argv[i], "-n") ||
 +                          !strcmp(argv[i], "--show-number")) {
 +                              show_number = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(argv[i], "-p") ||
 +                          !strcmp(argv[i], "--porcelain")) {
 +                              porcelain = 1;
 +                              sha1_len = 40;
 +                              show_raw_time = 1;
 +                              continue;
 +                      }
 +                      if (!strcmp(argv[i], "--")) {
                                options = 0;
                                continue;
 -                      } else if(argv[i][0] == '-')
 +                      }
 +                      if (argv[i][0] == '-')
                                usage(blame_usage);
 -                      else
 -                              options = 0;
 +                      options = 0;
                }
  
 -              if(!options) {
 -                      if(!filename)
 +              if (!options) {
 +                      if (!filename)
                                filename = argv[i];
 -                      else if(!commit)
 +                      else if (!commit)
                                commit = argv[i];
                        else
                                usage(blame_usage);
                }
        }
  
 -      if(!filename)
 +      if (!filename)
                usage(blame_usage);
        if (commit && sha1_p)
                usage(blame_usage);
 -      else if(!commit)
 +      else if (!commit)
                commit = "HEAD";
  
 -      if(prefix)
 +      if (prefix)
                sprintf(filename_buf, "%s%s", prefix, filename);
        else
                strcpy(filename_buf, filename);
                return 1;
        }
  
 -
        init_revisions(&rev, setup_git_directory());
        rev.remove_empty_trees = 1;
        rev.topo_order = 1;
        prepare_revision_walk(&rev);
        process_commits(&rev, filename, &initial);
  
 +      for (i = 0; i < num_blame_lines; i++)
 +              if (!blame_lines[i])
 +                      blame_lines[i] = initial;
 +
        buf = blame_contents;
 -      for (max_digits = 1, i = 10; i <= num_blame_lines + 1; max_digits++)
 -              i *= 10;
 +      max_digits = lineno_width(num_blame_lines);
  
        longest_file = 0;
        longest_author = 0;
 -      found_rename = 0;
 +      longest_file_lines = 0;
        for (i = 0; i < num_blame_lines; i++) {
                struct commit *c = blame_lines[i];
 -              struct util_info* u;
 -              if (!c)
 -                      c = initial;
 +              struct util_info *u;
                u = c->util;
  
 -              if (!found_rename && strcmp(filename, u->pathname))
 -                      found_rename = 1;
 +              if (!show_name && strcmp(filename, u->pathname))
 +                      show_name = 1;
                if (longest_file < strlen(u->pathname))
                        longest_file = strlen(u->pathname);
 -              get_commit_info(c, &ci);
 +              if (longest_file_lines < u->num_lines)
 +                      longest_file_lines = u->num_lines;
 +              get_commit_info(c, &ci, 0);
                if (longest_author < strlen(ci.author))
                        longest_author = strlen(ci.author);
        }
  
 -      for (i = 0; i < num_blame_lines; i++) {
 -              struct commit *c = blame_lines[i];
 -              struct util_info* u;
 +      max_orig_digits = lineno_width(longest_file_lines);
  
 -              if (!c)
 -                      c = initial;
 -
 -              u = c->util;
 -              get_commit_info(c, &ci);
 -              fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout);
 -              if(compatibility) {
 -                      printf("\t(%10s\t%10s\t%d)", ci.author,
 -                             format_time(ci.author_time, ci.author_tz,
 -                                         show_raw_time),
 -                             i+1);
 -              } else {
 -                      if (found_rename)
 -                              printf(" %-*.*s", longest_file, longest_file,
 -                                     u->pathname);
 -                      printf(" (%-*.*s %10s %*d) ",
 -                             longest_author, longest_author, ci.author,
 -                             format_time(ci.author_time, ci.author_tz,
 -                                         show_raw_time),
 -                             max_digits, i+1);
 -              }
 +      for (i = 0; i < num_blame_lines; i++) {
 +              emit_meta(blame_lines[i], i,
 +                        sha1_len, compatibility, porcelain,
 +                        show_name, show_number, show_raw_time,
 +                        longest_file, longest_author,
 +                        max_digits, max_orig_digits);
  
 -              if(i == num_blame_lines - 1) {
 +              if (i == num_blame_lines - 1) {
                        fwrite(buf, blame_len - (buf - blame_contents),
                               1, stdout);
 -                      if(blame_contents[blame_len-1] != '\n')
 +                      if (blame_contents[blame_len-1] != '\n')
                                putc('\n', stdout);
 -              } else {
 -                      char* next_buf = strchr(buf, '\n') + 1;
 +              }
 +              else {
 +                      char *next_buf = strchr(buf, '\n') + 1;
                        fwrite(buf, next_buf - buf, 1, stdout);
                        buf = next_buf;
                }
        }
  
        if (DEBUG) {
+               printf("num read blob: %d\n", num_read_blob);
                printf("num get patch: %d\n", num_get_patch);
                printf("num commits: %d\n", num_commits);
                printf("patch time: %f\n", patch_time / 1000000.0);
diff --combined builtin.h
index a9b28ed8996ffc255f82ebc35f32386865b7f227,7451ce64eb8ea50ccfa7d5bb07db39efafc79738..24803dfea778c42d748aeba1df74571ad4a66493
+++ b/builtin.h
@@@ -11,17 -11,13 +11,17 @@@ extern int mailinfo(FILE *in, FILE *out
  extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
  extern void stripspace(FILE *in, FILE *out);
  extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 +extern void prune_packed_objects(int);
  
  extern int cmd_add(int argc, const char **argv, const char *prefix);
 +extern int cmd_annotate(int argc, const char **argv, const char *prefix);
  extern int cmd_apply(int argc, const char **argv, const char *prefix);
  extern int cmd_archive(int argc, const char **argv, const char *prefix);
 +extern int cmd_branch(int argc, const char **argv, const char *prefix);
  extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
  extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
  extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
 +extern int cmd_cherry(int argc, const char **argv, const char *prefix);
  extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
  extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
  extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
@@@ -30,7 -26,6 +30,7 @@@ extern int cmd_diff(int argc, const cha
  extern int cmd_diff_stages(int argc, const char **argv, const char *prefix);
  extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
  extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
 +extern int cmd_for_each_ref(int argc, const char **argv, const char *prefix);
  extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
  extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
  extern int cmd_grep(int argc, const char **argv, const char *prefix);
@@@ -44,6 -39,7 +44,7 @@@ extern int cmd_mailsplit(int argc, cons
  extern int cmd_mv(int argc, const char **argv, const char *prefix);
  extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
  extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
+ extern int cmd_pickaxe(int argc, const char **argv, const char *prefix);
  extern int cmd_prune(int argc, const char **argv, const char *prefix);
  extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
  extern int cmd_push(int argc, const char **argv, const char *prefix);
@@@ -53,8 -49,8 +54,8 @@@ extern int cmd_rev_list(int argc, cons
  extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
  extern int cmd_rm(int argc, const char **argv, const char *prefix);
  extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
 -extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
  extern int cmd_show(int argc, const char **argv, const char *prefix);
 +extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
  extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
  extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
  extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
@@@ -67,7 -63,5 +68,7 @@@ extern int cmd_version(int argc, const 
  extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
  extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
  extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
 +extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
 +extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
  
  #endif
diff --combined diff.h
index ac7b21c46bdf958df5d7c8162c7aa248af54dbdb,6fda92a7b5e69cbedfe5b13e9c4e7fba54a7ed7f..101b2b505dcace41754687a427ad0523daaa50a0
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -46,6 -46,7 +46,7 @@@ struct diff_options 
        const char *filter;
        const char *orderfile;
        const char *pickaxe;
+       const char *single_follow;
        unsigned recursive:1,
                 tree_in_recursive:1,
                 binary:1,
@@@ -102,8 -103,6 +103,8 @@@ extern int diff_tree(struct tree_desc *
                     const char *base, struct diff_options *opt);
  extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
                          const char *base, struct diff_options *opt);
 +extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
 +                               struct diff_options *opt);
  
  struct combine_diff_path {
        struct combine_diff_path *next;
diff --combined git.c
index d2460c861885a50df9ff8b3acb11c1b45bcc366e,b944c3740122aa2ecb204bd8a3690c14cd225a44..9a9addf71d3f2f7d712ff3cd2e0fa6dfec31dd28
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -219,14 -219,11 +219,14 @@@ static void handle_internal_command(in
                int option;
        } commands[] = {
                { "add", cmd_add, RUN_SETUP },
 +              { "annotate", cmd_annotate, },
                { "apply", cmd_apply },
                { "archive", cmd_archive },
 +              { "branch", cmd_branch, RUN_SETUP },
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "checkout-index", cmd_checkout_index, RUN_SETUP },
                { "check-ref-format", cmd_check_ref_format },
 +              { "cherry", cmd_cherry, RUN_SETUP },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "count-objects", cmd_count_objects, RUN_SETUP },
                { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
                { "diff-stages", cmd_diff_stages, RUN_SETUP },
                { "diff-tree", cmd_diff_tree, RUN_SETUP },
                { "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },
 +              { "for-each-ref", cmd_for_each_ref, RUN_SETUP },
                { "format-patch", cmd_format_patch, RUN_SETUP },
                { "get-tar-commit-id", cmd_get_tar_commit_id },
                { "grep", cmd_grep, RUN_SETUP },
                { "mv", cmd_mv, RUN_SETUP },
                { "name-rev", cmd_name_rev, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
+               { "pickaxe", cmd_pickaxe, RUN_SETUP | USE_PAGER },
                { "prune", cmd_prune, RUN_SETUP },
                { "prune-packed", cmd_prune_packed, RUN_SETUP },
                { "push", cmd_push, RUN_SETUP },
                { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
                { "write-tree", cmd_write_tree, RUN_SETUP },
                { "verify-pack", cmd_verify_pack },
 +              { "show-ref", cmd_show_ref, RUN_SETUP },
 +              { "pack-refs", cmd_pack_refs, RUN_SETUP },
        };
        int i;
  
diff --combined revision.c
index 3dbc26c49c585088ff4861992ce70a94465df401,b021d3354e8b0f2f1efb6dbda0aa1289914eb112..993bb668a205a9fb3e2f5ddd2f378f10ac7905b4
@@@ -418,6 -418,9 +418,6 @@@ static void limit_list(struct rev_info 
  
                if (revs->max_age != -1 && (commit->date < revs->max_age))
                        obj->flags |= UNINTERESTING;
 -              if (revs->unpacked &&
 -                  has_sha1_pack(obj->sha1, revs->ignore_packed))
 -                      obj->flags |= UNINTERESTING;
                add_parents_to_list(revs, commit, &list);
                if (obj->flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
  static int all_flags;
  static struct rev_info *all_revs;
  
 -static int handle_one_ref(const char *path, const unsigned char *sha1)
 +static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
  {
        struct object *object = get_reference(all_revs, path, sha1, all_flags);
        add_pending_object(all_revs, object, "");
@@@ -476,7 -479,7 +476,7 @@@ static void handle_all(struct rev_info 
  {
        all_revs = revs;
        all_flags = flags;
 -      for_each_ref(handle_one_ref);
 +      for_each_ref(handle_one_ref, NULL);
  }
  
  static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
@@@ -657,6 -660,13 +657,13 @@@ int handle_revision_arg(const char *arg
                        return 0;
                *dotdot = '^';
        }
+       dotdot = strstr(arg, "^!");
+       if (dotdot && !dotdot[2]) {
+               *dotdot = 0;
+               if (!add_parents_only(revs, arg, flags ^ UNINTERESTING))
+                       *dotdot = '^';
+       }
        local_flags = 0;
        if (*arg == '^') {
                local_flags = UNINTERESTING;
@@@ -1012,7 -1022,7 +1019,7 @@@ int setup_revisions(int argc, const cha
                add_pending_object(revs, object, def);
        }
  
 -      if (revs->topo_order || revs->unpacked)
 +      if (revs->topo_order)
                revs->limited = 1;
  
        if (revs->prune_data) {
@@@ -1146,18 -1156,17 +1153,18 @@@ struct commit *get_revision(struct rev_
                 * that we'd otherwise have done in limit_list().
                 */
                if (!revs->limited) {
 -                      if ((revs->unpacked &&
 -                           has_sha1_pack(commit->object.sha1,
 -                                         revs->ignore_packed)) ||
 -                          (revs->max_age != -1 &&
 -                           (commit->date < revs->max_age)))
 +                      if (revs->max_age != -1 &&
 +                          (commit->date < revs->max_age))
                                continue;
                        add_parents_to_list(revs, commit, &revs->commits);
                }
                if (commit->object.flags & SHOWN)
                        continue;
  
 +              if (revs->unpacked && has_sha1_pack(commit->object.sha1,
 +                                                  revs->ignore_packed))
 +                  continue;
 +
                /* We want to show boundary commits only when their
                 * children are shown.  When path-limiter is in effect,
                 * rewrite_parents() drops some commits from getting shown,