Merge branch 'lj/refs'
authorJunio C Hamano <junkio@cox.net>
Wed, 1 Nov 2006 16:48:50 +0000 (08:48 -0800)
committerJunio C Hamano <junkio@cox.net>
Wed, 1 Nov 2006 16:48:50 +0000 (08:48 -0800)
* lj/refs: (63 commits)
  Fix show-ref usagestring
  t3200: git-branch testsuite update
  sha1_name.c: avoid compilation warnings.
  Make git-branch a builtin
  ref-log: fix D/F conflict coming from deleted refs.
  git-revert with conflicts to behave as git-merge with conflicts
  core.logallrefupdates thinko-fix
  git-pack-refs --all
  core.logallrefupdates create new log file only for branch heads.
  Remove bashism from t3210-pack-refs.sh
  ref-log: allow ref@{count} syntax.
  pack-refs: call fflush before fsync.
  pack-refs: use lockfile as everybody else does.
  git-fetch: do not look into $GIT_DIR/refs to see if a tag exists.
  lock_ref_sha1_basic does not remove empty directories on BSD
  Do not create tag leading directories since git update-ref does it.
  Check that a tag exists using show-ref instead of looking for the ref file.
  Use git-update-ref to delete a tag instead of rm()ing the ref file.
  Fix refs.c;:repack_without_ref() clean-up path
  Clean up "git-branch.sh" and add remove recursive dir test cases.
  ...

109 files changed:
Documentation/SubmittingPatches
Documentation/asciidoc.conf
Documentation/config.txt
Documentation/diff-format.txt
Documentation/diff-options.txt
Documentation/everyday.txt
Documentation/git-blame.txt
Documentation/git-cherry-pick.txt
Documentation/git-cherry.txt
Documentation/git-daemon.txt
Documentation/git-for-each-ref.txt
Documentation/git-grep.txt
Documentation/git-http-push.txt
Documentation/git-pack-objects.txt
Documentation/git-pull.txt
Documentation/git-rebase.txt
Documentation/git-repack.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-send-pack.txt
Documentation/git-shortlog.txt
Documentation/git-svn.txt
Documentation/git.txt
Documentation/glossary.txt
Documentation/urls.txt
Makefile
archive-zip.c
blame.c
builtin-annotate.c [new file with mode: 0644]
builtin-apply.c
builtin-archive.c
builtin-grep.c
builtin-pack-objects.c
builtin-prune-packed.c
builtin-prune.c
builtin-unpack-objects.c
builtin.h
cache-tree.c
cache.h
combine-diff.c
commit.c
contrib/completion/git-completion.bash
contrib/emacs/git.el
contrib/emacs/vc-git.el
daemon.c
diff-delta.c
diff.c
diff.h
environment.c
fetch-clone.c
fetch-pack.c
git-annotate.perl [deleted file]
git-bisect.sh
git-cherry.sh
git-clone.sh
git-commit.sh
git-cvsimport.perl
git-cvsserver.perl
git-fetch.sh
git-merge.sh
git-pull.sh
git-rebase.sh
git-repack.sh
git-resolve.sh
git-revert.sh
git-send-email.perl
git-shortlog.perl
git-svn.perl
git-svnimport.perl
git.c
git.spec.in
gitweb/README
gitweb/gitweb.css
gitweb/gitweb.perl
grep.c
grep.h
http-fetch.c
imap-send.c
index-pack.c
merge-recursive.c
pack.h
pager.c
quote.c
receive-pack.c
revision.c
setup.c
sha1_file.c
sha1_name.c
show-index.c
sideband.c
sideband.h
t/t1200-tutorial.sh
t/t3401-rebase-partial.sh
t/t3402-rebase-merge.sh
t/t3403-rebase-skip.sh
t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
t/t4015-diff-whitespace.sh [new file with mode: 0755]
t/t4118-apply-empty-context.sh [new file with mode: 0755]
t/t5000-tar-tree.sh
t/t5600-clone-fail-cleanup.sh
t/t6021-merge-criss-cross.sh
t/t6022-merge-rename.sh
t/test-lib.sh
trace.c
upload-pack.c
wt-status.c
xdiff/xemit.c
xdiff/xmacros.h
xdiff/xutils.c

index 90722c2..8a3d316 100644 (file)
@@ -101,8 +101,13 @@ send it "To:" the mailing list, and optionally "cc:" him.  If it
 is trivially correct or after the list reached a consensus, send
 it "To:" the maintainer and optionally "cc:" the list.
 
+Also note that your maintainer does not actively involve himself in
+maintaining what are in contrib/ hierarchy.  When you send fixes and
+enhancements to them, do not forget to "cc: " the person who primarily
+worked on that hierarchy in contrib/.
 
-(6) Sign your work
+
+(4) Sign your work
 
 To improve tracking of who did what, we've borrowed the
 "sign-off" procedure from the Linux kernel project on patches
index 8196d78..44b1ce4 100644 (file)
@@ -11,6 +11,7 @@
 caret=^
 startsb=&#91;
 endsb=&#93;
+tilde=&#126;
 
 ifdef::backend-docbook[]
 [gitlink-inlinemacro]
index 232e2a9..d9e73da 100644 (file)
@@ -234,6 +234,22 @@ pull.octopus::
 pull.twohead::
        The default merge strategy to use when pulling a single branch.
 
+remote.<name>.url::
+       The URL of a remote repository.  See gitlink:git-fetch[1] or
+       gitlink:git-push[1].
+
+remote.<name>.fetch::
+       The default set of "refspec" for gitlink:git-fetch[1]. See
+       gitlink:git-fetch[1].
+
+remote.<name>.push::
+       The default set of "refspec" for gitlink:git-push[1]. See
+       gitlink:git-push[1].
+
+repack.usedeltabaseoffset::
+       Allow gitlink:git-repack[1] to create packs that uses
+       delta-base offset.  Defaults to false.
+
 show.difftree::
        The default gitlink:git-diff-tree[1] arguments to be used
        for gitlink:git-show[1].
index 617d8f5..e4520e2 100644 (file)
@@ -144,8 +144,10 @@ the file that rename/copy produces, respectively.
        dissimilarity index <number>
        index <hash>..<hash> <mode>
 
-3.  TAB, LF, and backslash characters in pathnames are
-    represented as `\t`, `\n`, and `\\`, respectively.
+3.  TAB, LF, double quote and backslash characters in pathnames
+    are represented as `\t`, `\n`, `\"` and `\\`, respectively.
+    If there is need for such substitution then the whole
+    pathname is put in double quotes.
 
 
 combined diff format
@@ -156,31 +158,91 @@ to produce 'combined diff', which looks like this:
 
 ------------
 diff --combined describe.c
-@@@ +98,7 @@@
-   return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
+index fabadb8,cc95eb0..4866510
+--- a/describe.c
++++ b/describe.c
+@@@ -98,20 -98,12 +98,20 @@@
+       return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
   }
-
+  
 - static void describe(char *arg)
  -static void describe(struct commit *cmit, int last_one)
 ++static void describe(char *arg, int last_one)
   {
- +     unsigned char sha1[20];
- +     struct commit *cmit;
+ +     unsigned char sha1[20];
+ +     struct commit *cmit;
+       struct commit_list *list;
+       static int initialized = 0;
+       struct commit_name *n;
+  
+ +     if (get_sha1(arg, sha1) < 0)
+ +             usage(describe_usage);
+ +     cmit = lookup_commit_reference(sha1);
+ +     if (!cmit)
+ +             usage(describe_usage);
+ +
+       if (!initialized) {
+               initialized = 1;
+               for_each_ref(get_name);
 ------------
 
+1.   It is preceded with a "git diff" header, that looks like
+     this (when '-c' option is used):
+
+       diff --combined file
++
+or like this (when '--cc' option is used):
+
+       diff --c file
+
+2.   It is followed by one or more extended header lines
+     (this example shows a merge with two parents):
+
+       index <hash>,<hash>..<hash>
+       mode <mode>,<mode>..<mode>
+       new file mode <mode>
+       deleted file mode <mode>,<mode>
++
+The `mode <mode>,<mode>..<mode>` line appears only if at least one of
+the <mode> is diferent from the rest. Extended headers with
+information about detected contents movement (renames and
+copying detection) are designed to work with diff of two
+<tree-ish> and are not used by combined diff format.
+
+3.   It is followed by two-line from-file/to-file header
+
+       --- a/file
+       +++ b/file
++
+Similar to two-line header for traditional 'unified' diff
+format, `/dev/null` is used to signal created or deleted
+files.
+
+4.   Chunk header format is modified to prevent people from
+     accidentally feeding it to `patch -p1`. Combined diff format
+     was created for review of merge commit changes, and was not
+     meant for apply. The change is similar to the change in the
+     extended 'index' header:
+
+       @@@ <from-file-range> <from-file-range> <to-file-range> @@@
++
+There are (number of parents + 1) `@` characters in the chunk
+header for combined diff format.
+
 Unlike the traditional 'unified' diff format, which shows two
 files A and B with a single column that has `-` (minus --
 appears in A but removed in B), `+` (plus -- missing in A but
-added to B), or ` ` (space -- unchanged) prefix, this format
+added to B), or `" "` (space -- unchanged) prefix, this format
 compares two or more files file1, file2,... with one file X, and
 shows how X differs from each of fileN.  One column for each of
 fileN is prepended to the output line to note how X's line is
 different from it.
 
 A `-` character in the column N means that the line appears in
-fileN but it does not appear in the last file.  A `+` character
+fileN but it does not appear in the result.  A `+` character
 in the column N means that the line appears in the last file,
-and fileN does not have that line.
+and fileN does not have that line (in other words, the line was
+added, from the point of view of that parent).
 
 In the above example output, the function signature was changed
 from both files (hence two `-` removals from both file1 and
index 7b7b9e8..e112172 100644 (file)
        The width of the filename part can be controlled by
        giving another width to it separated by a comma.
 
+--numstat::
+       Similar to \--stat, but shows number of added and
+       deleted lines in decimal notation and pathname without
+       abbreviation, to make it more machine friendly.
+
 --summary::
        Output a condensed summary of extended header information
        such as creations, renames and mode changes.
index b935c18..9677671 100644 (file)
@@ -1,22 +1,7 @@
 Everyday GIT With 20 Commands Or So
 ===================================
 
-GIT suite has over 100 commands, and the manual page for each of
-them discusses what the command does and how it is used in
-detail, but until you know what command should be used in order
-to achieve what you want to do, you cannot tell which manual
-page to look at, and if you know that already you do not need
-the manual.
-
-Does that mean you need to know all of them before you can use
-git?  Not at all.  Depending on the role you play, the set of
-commands you need to know is slightly different, but in any case
-what you need to learn is far smaller than the full set of
-commands to carry out your day-to-day work.  This document is to
-serve as a cheat-sheet and a set of pointers for people playing
-various roles.
-
-<<Basic Repository>> commands are needed by people who has a
+<<Basic Repository>> commands are needed by people who have a
 repository --- that is everybody, because every working tree of
 git is a repository.
 
@@ -25,28 +10,27 @@ essential for anybody who makes a commit, even for somebody who
 works alone.
 
 If you work with other people, you will need commands listed in
-<<Individual Developer (Participant)>> section as well.
+the <<Individual Developer (Participant)>> section as well.
 
-People who play <<Integrator>> role need to learn some more
+People who play the <<Integrator>> role need to learn some more
 commands in addition to the above.
 
 <<Repository Administration>> commands are for system
-administrators who are responsible to care and feed git
-repositories to support developers.
+administrators who are responsible for the care and feeding
+of git repositories.
 
 
 Basic Repository[[Basic Repository]]
 ------------------------------------
 
-Everybody uses these commands to feed and care git repositories.
+Everybody uses these commands to maintain git repositories.
 
   * gitlink:git-init-db[1] or gitlink:git-clone[1] to create a
     new repository.
 
-  * gitlink:git-fsck-objects[1] to validate the repository.
+  * gitlink:git-fsck-objects[1] to check the repository for errors.
 
-  * gitlink:git-prune[1] to garbage collect cruft in the
-    repository.
+  * gitlink:git-prune[1] to remove unused objects in the repository.
 
   * gitlink:git-repack[1] to pack loose objects for efficiency.
 
@@ -78,8 +62,8 @@ $ git repack -a -d <1>
 $ git prune
 ------------
 +
-<1> pack all the objects reachable from the refs into one pack
-and remove unneeded other packs
+<1> pack all the objects reachable from the refs into one pack,
+then remove the other packs.
 
 
 Individual Developer (Standalone)[[Individual Developer (Standalone)]]
@@ -93,9 +77,6 @@ following commands.
 
   * gitlink:git-log[1] to see what happened.
 
-  * gitlink:git-whatchanged[1] to find out where things have
-    come from.
-
   * gitlink:git-checkout[1] and gitlink:git-branch[1] to switch
     branches.
 
@@ -120,7 +101,7 @@ following commands.
 Examples
 ~~~~~~~~
 
-Extract a tarball and create a working tree and a new repository to keep track of it.::
+Use a tarball as a starting point for a new repository:
 +
 ------------
 $ tar zxf frotz.tar.gz
@@ -203,7 +184,7 @@ $ cd my2.6
 $ edit/compile/test; git commit -a -s <1>
 $ git format-patch origin <2>
 $ git pull <3>
-$ git whatchanged -p ORIG_HEAD.. arch/i386 include/asm-i386 <4>
+$ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <4>
 $ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <5>
 $ git reset --hard ORIG_HEAD <6>
 $ git prune <7>
@@ -372,12 +353,19 @@ example of managing a shared central repository.
 
 Examples
 ~~~~~~~~
+We assume the following in /etc/services::
++
+------------
+$ grep 9418 /etc/services
+git            9418/tcp                # Git Version Control System
+------------
+
 Run git-daemon to serve /pub/scm from inetd.::
 +
 ------------
 $ grep git /etc/inetd.conf
 git    stream  tcp     nowait  nobody \
-  /usr/bin/git-daemon git-daemon --inetd --syslog --export-all /pub/scm
+  /usr/bin/git-daemon git-daemon --inetd --export-all /pub/scm
 ------------
 +
 The actual configuration line should be on one line.
@@ -397,7 +385,7 @@ service git
         wait            = no
         user            = nobody
         server          = /usr/bin/git-daemon
-        server_args     = --inetd --syslog --export-all --base-path=/pub/scm
+        server_args     = --inetd --export-all --base-path=/pub/scm
         log_on_failure  += USERID
 }
 ------------
index e1f8944..9891c1d 100644 (file)
@@ -7,7 +7,7 @@ git-blame - Show what revision and author last modified each line of a file
 
 SYNOPSIS
 --------
-'git-blame' [-c] [-l] [-t] [-S <revs-file>] [--] <file> [<rev>]
+'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [-S <revs-file>] [--] <file> [<rev>]
 
 DESCRIPTION
 -----------
@@ -45,10 +45,47 @@ OPTIONS
 -S, --rev-file <revs-file>::
        Use revs from revs-file instead of calling gitlink:git-rev-list[1].
 
+-f, --show-name::
+       Show filename in the original commit.  By default
+       filename is shown if there is any line that came from a
+       file with different name, due to rename detection.
+
+-n, --show-number::
+       Show line number in the original commit (Default: off).
+
+-p, --porcelain::
+       Show in a format designed for machine consumption.
+
 -h, --help::
        Show help message.
 
 
+THE PORCELAIN FORMAT
+--------------------
+
+In this format, each line is output after a header; the
+header at the minumum has the first line which has:
+
+- 40-byte SHA-1 of the commit the line is attributed to;
+- the line number of the line in the original file;
+- the line number of the line in the final file;
+- on a line that starts a group of line from a different
+  commit than the previous one, the number of lines in this
+  group.  On subsequent lines this field is absent.
+
+This header line is followed by the following information
+at least once for each commit:
+
+- author name ("author"), email ("author-mail"), time
+  ("author-time"), and timezone ("author-tz"); similarly
+  for committer.
+- filename in the commit the line is attributed to.
+- the first line of the commit log message ("summary").
+
+The contents of the actual line is output after the above
+header, prefixed by a TAB. This is to allow adding more
+header elements later.
+
 SEE ALSO
 --------
 gitlink:git-annotate[1]
index bfa950c..875edb6 100644 (file)
@@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit
 
 SYNOPSIS
 --------
-'git-cherry-pick' [--edit] [-n] [-r] <commit>
+'git-cherry-pick' [--edit] [-n] [-x] <commit>
 
 DESCRIPTION
 -----------
@@ -24,13 +24,22 @@ OPTIONS
        With this option, `git-cherry-pick` will let you edit the commit
        message prior committing.
 
--r|--replay::
-       Usually the command appends which commit was
+-x::
+       Cause the command to append which commit was
        cherry-picked after the original commit message when
-       making a commit.  This option, '--replay', causes it to
-       use the original commit message intact.  This is useful
-       when you are reordering the patches in your private tree
-       before publishing.
+       making a commit.  Do not use this option if you are
+       cherry-picking from your private branch because the
+       information is useless to the recipient.  If on the
+       other hand you are cherry-picking between two publicly
+       visible branches (e.g. backporting a fix to a
+       maintenance branch for an older release from a
+       development branch), adding this information can be
+       useful.
+
+-r|--replay::
+       It used to be that the command defaulted to do `-x`
+       described above, and `-r` was to disable it.  Now the
+       default is not to do `-x` so this option is a no-op.
 
 -n|--no-commit::
        Usually the command automatically creates a commit with
index 893baaa..27b67b8 100644 (file)
@@ -7,17 +7,33 @@ git-cherry - Find commits not merged upstream
 
 SYNOPSIS
 --------
-'git-cherry' [-v] <upstream> [<head>]
+'git-cherry' [-v] <upstream> [<head>] [<limit>]
 
 DESCRIPTION
 -----------
 The changeset (or "diff") of each commit between the fork-point and <head>
 is compared against each commit between the fork-point and <upstream>.
 
-Every commit with a changeset that doesn't exist in the other branch
-has its id (sha1) reported, prefixed by a symbol.  Those existing only
+Every commit that doesn't exist in the <upstream> branch
+has its id (sha1) reported, prefixed by a symbol.  The ones that have
+equivalent change already
 in the <upstream> branch are prefixed with a minus (-) sign, and those
-that only exist in the <head> branch are prefixed with a plus (+) symbol.
+that only exist in the <head> branch are prefixed with a plus (+) symbol:
+
+               __*__*__*__*__> <upstream>
+              /
+    fork-point
+              \__+__+__-__+__+__-__+__> <head>
+
+
+If a <limit> has been given then the commits along the <head> branch up
+to and including <limit> are not reported:
+
+               __*__*__*__*__> <upstream>
+              /
+    fork-point
+              \__*__*__<limit>__-__+__> <head>
+
 
 Because git-cherry compares the changeset rather than the commit id
 (sha1), you can use git-cherry to find out if a commit you made locally
index d562232..993adc7 100644 (file)
@@ -37,6 +37,8 @@ from `git-fetch`, `git-ls-remote`, and `git-clone`.
 This is ideally suited for read-only updates, i.e., pulling from
 git repositories.
 
+An `upload-archive` also exists to serve `git-archive`.
+
 OPTIONS
 -------
 --strict-paths::
@@ -155,8 +157,18 @@ upload-pack::
        disable it by setting `daemon.uploadpack` configuration
        item to `false`.
 
+upload-archive::
+       This serves `git-archive --remote`.
+
 EXAMPLES
 --------
+We assume the following in /etc/services::
++
+------------
+$ grep 9418 /etc/services
+git            9418/tcp                # Git Version Control System
+------------
+
 git-daemon as inetd server::
        To set up `git-daemon` as an inetd service that handles any
        repository under the whitelisted set of directories, /pub/foo
@@ -165,8 +177,7 @@ git-daemon as inetd server::
 +
 ------------------------------------------------
        git stream tcp nowait nobody  /usr/bin/git-daemon
-               git-daemon --inetd --verbose
-               --syslog --export-all
+               git-daemon --inetd --verbose --export-all
                /pub/foo /pub/bar
 ------------------------------------------------
 
@@ -179,8 +190,7 @@ git-daemon as inetd server for virtual hosts::
 +
 ------------------------------------------------
        git stream tcp nowait nobody /usr/bin/git-daemon
-               git-daemon --inetd --verbose
-               --syslog --export-all
+               git-daemon --inetd --verbose --export-all
                --interpolated-path=/pub/%H%D
                /pub/www.example.org/software
                /pub/www.example.com/software
index d5fdcef..2bf6aef 100644 (file)
@@ -7,14 +7,14 @@ git-for-each-ref - Output information on each ref
 
 SYNOPSIS
 --------
-'git-for-each-ref' [--count=<count>]* [--shell|--perl|--python] [--sort=<key>]* [--format=<format>] [<pattern>]
+'git-for-each-ref' [--count=<count>]\* [--shell|--perl|--python] [--sort=<key>]\* [--format=<format>] [<pattern>]
 
 DESCRIPTION
 -----------
 
 Iterate over all refs that match `<pattern>` and show them
 according to the given `<format>`, after sorting them according
-to the given set of `<key>`s.  If `<max>` is given, stop after
+to the given set of `<key>`.  If `<max>` is given, stop after
 showing that many refs.  The interporated values in `<format>`
 can optionally be quoted as string literals in the specified
 host language allowing their direct evaluation in that language.
@@ -38,7 +38,11 @@ OPTIONS
        is prefixed with an asterisk (`*`) and the ref points
        at a tag object, the value for the field in the object
        tag refers is used.  When unspecified, defaults to
-       `%(refname)`.
+       `%(objectname) SPC %(objecttype) TAB %(refname)`.
+       It also interpolates `%%` to `%`, and `%xx` where `xx`
+       are hex digits interpolates to character with hex code
+       `xx`; for example `%00` interpolates to `\0` (NUL),
+       `%09` to `\t` (TAB) and `%0a` to `\n` (LF).
 
 <pattern>::
        If given, the name of the ref is matched against this
index d8af4d9..bfbece9 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
           [-v | --invert-match] [-h|-H] [--full-name]
           [-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
           [-n] [-l | --files-with-matches] [-L | --files-without-match]
-          [-c | --count]
+          [-c | --count] [--all-match]
           [-A <post-context>] [-B <pre-context>] [-C <context>]
           [-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...]
           [<tree>...]
@@ -96,6 +96,11 @@ OPTIONS
        higher precedence than `--or`.  `-e` has to be used for all
        patterns.
 
+--all-match::
+       When giving multiple pattern expressions combined with `--or`,
+       this flag is specified to limit the match to files that
+       have lines to match all of them.
+
 `<tree>...`::
        Search blobs in the trees for specified patterns.
 
@@ -111,6 +116,10 @@ git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
        Looks for a line that has `#define` and either `MAX_PATH` or
        `PATH_MAX`.
 
+git grep --all-match -e NODE -e Unexpected::
+       Looks for a line that has `NODE` or `Unexpected` in
+       files that have lines that match both.
+
 Author
 ------
 Originally written by Linus Torvalds <torvalds@osdl.org>, later
index 7e1f894..c2485c6 100644 (file)
@@ -34,7 +34,7 @@ OPTIONS
        Report the list of objects being walked locally and the
        list of objects successfully sent to the remote repository.
 
-<ref>...:
+<ref>...::
        The remote refs to update.
 
 
index d4661dd..a1e5505 100644 (file)
@@ -9,7 +9,7 @@ git-pack-objects - Create a packed archive of objects
 SYNOPSIS
 --------
 [verse]
-'git-pack-objects' [-q] [--no-reuse-delta] [--non-empty]
+'git-pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
        [--local] [--incremental] [--window=N] [--depth=N]
        [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
 
@@ -71,11 +71,11 @@ base-name::
 --all::
        This implies `--revs`.  In addition to the list of
        revision arguments read from the standard input, pretend
-       as if all refs under `$GIT_DIR/refs` are specifed to be
+       as if all refs under `$GIT_DIR/refs` are specified to be
        included.
 
---window and --depth::
-       These two options affects how the objects contained in
+--window=[N], --depth=[N]::
+       These two options affect how the objects contained in
        the pack are stored using delta compression.  The
        objects are first internally sorted by type, size and
        optionally names and compared against the other objects
@@ -84,6 +84,7 @@ base-name::
        it too deep affects the performance on the unpacker
        side, because delta data needs to be applied that many
        times to get to the necessary object.
+       The default value for both --window and --depth is 10.
 
 --incremental::
        This flag causes an object already in a pack ignored
@@ -110,6 +111,17 @@ base-name::
        This flag tells the command not to reuse existing deltas
        but compute them from scratch.
 
+--delta-base-offset::
+       A packed archive can express base object of a delta as
+       either 20-byte object name or as an offset in the
+       stream, but older version of git does not understand the
+       latter.  By default, git-pack-objects only uses the
+       former format for better compatibility.  This option
+       allows the command to use the latter format for
+       compactness.  Depending on the average delta chain
+       length, this option typically shrinks the resulting
+       packfile by 3-5 per-cent.
+
 
 Author
 ------
index 51577fc..2a5aea7 100644 (file)
@@ -3,7 +3,7 @@ git-pull(1)
 
 NAME
 ----
-git-pull - Pull and merge from another repository
+git-pull - Pull and merge from another repository or a local branch
 
 
 SYNOPSIS
index 9d7bcaa..10f2924 100644 (file)
@@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a new head
 
 SYNOPSIS
 --------
-'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [-v] [--merge] [--onto <newbase>] <upstream> [<branch>]
 
 'git-rebase' --continue | --skip | --abort
 
@@ -121,6 +121,9 @@ OPTIONS
        is used instead (`git-merge-recursive` when merging a single
        head, `git-merge-octopus` otherwise).  This implies --merge.
 
+-v, \--verbose::
+       Display a diffstat of what changed upstream since the last rebase.
+
 include::merge-strategies.txt[]
 
 NOTES
index 49f7e0a..0fa47e3 100644 (file)
@@ -57,13 +57,28 @@ OPTIONS
         `git update-server-info`.
 
 --window=[N], --depth=[N]::
-       These two options affects how the objects contained in the pack are
+       These two options affect how the objects contained in the pack are
        stored using delta compression. The objects are first internally
        sorted by type, size and optionally names and compared against the
        other objects within `--window` to see if using delta compression saves
        space. `--depth` limits the maximum delta depth; making it too deep
        affects the performance on the unpacker side, because delta data needs
        to be applied that many times to get to the necessary object.
+       The default value for both --window and --depth is 10.
+
+
+Configuration
+-------------
+
+When configuration variable `repack.UseDeltaBaseOffset` is set
+for the repository, the command passes `--delta-base-offset`
+option to `git-pack-objects`; this typically results in slightly
+smaller packs, but the generated packs are incompatible with
+versions of git older than (and including) v1.4.3; do not set
+the variable in a repository that older version of git needs to
+be able to read (this includes repositories from which packs can
+be copied out over http or rsync, and people who obtained packs
+that way can try to use older git with it).
 
 
 Author
index b761b4b..ed938aa 100644 (file)
@@ -111,7 +111,9 @@ SPECIFYING REVISIONS
 
 A revision parameter typically, but not necessarily, names a
 commit object.  They use what is called an 'extended SHA1'
-syntax.
+syntax.  Here are various ways to spell object names.  The
+ones listed near the end of this list are to name trees and
+blobs contained in a commit.
 
 * The full SHA1 object name (40-byte hexadecimal string), or
   a substring of such that is unique within the repository.
@@ -119,12 +121,31 @@ syntax.
   name the same commit object if there are no other object in
   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.
+
 * 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:
+
+  . 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;
 
-* A suffix '@' followed by a date specification enclosed in a brace
+  . 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
@@ -138,11 +159,12 @@ syntax.
   'rev{caret}0' means the commit itself and is used when 'rev' is the
   object name of a tag object that refers to a commit object.
 
-* A suffix '~<n>' to a revision parameter means the commit
+* 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
@@ -156,6 +178,15 @@ syntax.
   and dereference the tag recursively until a non-tag object is
   found.
 
+* A suffix ':' followed by a path; this names the blob or tree
+  at the given path in the tree-ish object named by the part
+  before the colon.
+
+* A colon, optionally followed by a stage number (0 to 3) and a
+  colon, followed by a path; this names a blob object in the
+  index at the given path.  Missing stage number (and the colon
+  that follows it) names an stage 0 entry.
+
 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.
index 481b3f5..4c8d907 100644 (file)
@@ -66,8 +66,13 @@ The options available are:
        all that is output.
 
 --smtp-server::
-       If set, specifies the outgoing SMTP server to use.  Defaults to
-       localhost.
+       If set, specifies the outgoing SMTP server to use.  A full
+       pathname of a sendmail-like program can be specified instead;
+       the program must support the `-i` option.  Default value can
+       be specified by the 'sendemail.smtpserver' configuration
+       option; the built-in default is `/usr/sbin/sendmail` or
+       `/usr/lib/sendmail` if such program is available, or
+       `localhost` otherwise.
 
 --subject::
        Specify the initial subject of the email thread.
index 9e67f17..5376f68 100644 (file)
@@ -43,7 +43,7 @@ OPTIONS
 <directory>::
        The repository to update.
 
-<ref>...:
+<ref>...::
        The remote refs to update.
 
 
index 7486ebe..d54fc3e 100644 (file)
@@ -7,16 +7,29 @@ git-shortlog - Summarize 'git log' output
 
 SYNOPSIS
 --------
-git-log --pretty=short | 'git-shortlog'
+git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s]
 
 DESCRIPTION
 -----------
 Summarizes 'git log' output in a format suitable for inclusion
-in release announcements. Each commit will be grouped by author
+in release announcements. Each commit will be grouped by author and
 the first line of the commit message will be shown.
 
 Additionally, "[PATCH]" will be stripped from the commit description.
 
+OPTIONS
+-------
+
+-h::
+       Print a short usage message and exit.
+
+-n::
+       Sort output according to the number of commits per author instead
+       of author alphabetic order.
+
+-s::
+       Supress commit description and provide a commit count summary only.
+
 FILES
 -----
 '.mailmap'::
index 1cfa3e3..450ff1f 100644 (file)
@@ -37,7 +37,9 @@ COMMANDS
 'init'::
        Creates an empty git repository with additional metadata
        directories for git-svn.  The Subversion URL must be specified
-       as a command-line argument.
+       as a command-line argument.  Optionally, the target directory
+       to operate on can be specified as a second argument.  Normally
+       this command initializes the current directory.
 
 'fetch'::
 
@@ -63,7 +65,30 @@ manually joining branches on commit.
        This is advantageous over 'commit' (below) because it produces
        cleaner, more linear history.
 
+'log'::
+       This should make it easy to look up svn log messages when svn
+       users refer to -r/--revision numbers.
+
+       The following features from `svn log' are supported:
+
+       --revision=<n>[:<n>] - is supported, non-numeric args are not:
+                              HEAD, NEXT, BASE, PREV, etc ...
+       -v/--verbose         - it's not completely compatible with
+                              the --verbose output in svn log, but
+                              reasonably close.
+       --limit=<n>          - is NOT the same as --max-count,
+                              doesn't count merged/excluded commits
+       --incremental        - supported
+
+       New features:
+
+       --show-commit        - shows the git commit sha1, as well
+       --oneline            - our version of --pretty=oneline
+
+       Any other arguments are passed directly to `git log'
+
 'commit'::
+       You should consider using 'dcommit' instead of this command.
        Commit specified commit or tree objects to SVN.  This relies on
        your imported fetch data being up-to-date.  This makes
        absolutely no attempts to do patching when committing to SVN, it
@@ -86,12 +111,49 @@ manually joining branches on commit.
        directories.  The output is suitable for appending to
        the $GIT_DIR/info/exclude file.
 
+'commit-diff'::
+       Commits the diff of two tree-ish arguments from the
+       command-line.  This command is intended for interopability with
+       git-svnimport and does not rely on being inside an git-svn
+       init-ed repository.  This command takes three arguments, (a) the
+       original tree to diff against, (b) the new tree result, (c) the
+       URL of the target Subversion repository.  The final argument
+       (URL) may be omitted if you are working from a git-svn-aware
+       repository (that has been init-ed with git-svn).
+
+'graft-branches'::
+       This command attempts to detect merges/branches from already
+       imported history.  Techniques used currently include regexes,
+       file copies, and tree-matches).  This command generates (or
+       modifies) the $GIT_DIR/info/grafts file.  This command is
+       considered experimental, and inherently flawed because
+       merge-tracking in SVN is inherently flawed and inconsistent
+       across different repositories.
+
+'multi-init'::
+       This command supports git-svnimport-like command-line syntax for
+       importing repositories that are layed out as recommended by the
+       SVN folks.  This is a bit more tolerant than the git-svnimport
+       command-line syntax and doesn't require the user to figure out
+       where the repository URL ends and where the repository path
+       begins.
+
+'multi-fetch'::
+       This runs fetch on all known SVN branches we're tracking.  This
+       will NOT discover new branches (unlike git-svnimport), so
+       multi-init will need to be re-run (it's idempotent).
+
 --
 
 OPTIONS
 -------
 --
 
+--shared::
+--template=<template_directory>::
+       Only used with the 'init' command.
+       These are passed directly to gitlink:git-init-db[1].
+
 -r <ARG>::
 --revision <ARG>::
 
@@ -115,7 +177,7 @@ git-rev-list --pretty=oneline output can be used.
 
 --rmdir::
 
-Only used with the 'commit' command.
+Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
 
 Remove directories from the SVN tree if there are no files left
 behind.  SVN can version empty directories, and they are not
@@ -128,7 +190,7 @@ repo-config key: svn.rmdir
 -e::
 --edit::
 
-Only used with the 'commit' command.
+Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
 
 Edit the commit message before committing to SVN.  This is off by
 default for objects that are commits, and forced on when committing
@@ -139,7 +201,7 @@ repo-config key: svn.edit
 -l<num>::
 --find-copies-harder::
 
-Both of these are only used with the 'commit' command.
+Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
 
 They are both passed directly to git-diff-tree see
 gitlink:git-diff-tree[1] for more information.
@@ -164,7 +226,26 @@ will abort operation. The user will then have to add the
 appropriate entry.  Re-running the previous git-svn command
 after the authors-file is modified should continue operation.
 
-repo-config key: svn.authors-file
+repo-config key: svn.authorsfile
+
+-q::
+--quiet::
+       Make git-svn less verbose.  This only affects git-svn if you
+       have the SVN::* libraries installed and are using them.
+
+--repack[=<n>]::
+--repack-flags=<flags>
+       These should help keep disk usage sane for large fetches
+       with many revisions.
+
+       --repack takes an optional argument for the number of revisions
+       to fetch before repacking.  This defaults to repacking every
+       1000 commits fetched if no argument is specified.
+
+       --repack-flags are passed directly to gitlink:git-repack[1].
+
+repo-config key: svn.repack
+repo-config key: svn.repackflags
 
 -m::
 --merge::
@@ -215,6 +296,28 @@ section on
 '<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>'
 for more information on using GIT_SVN_ID.
 
+--follow-parent::
+       This is especially helpful when we're tracking a directory
+       that has been moved around within the repository, or if we
+       started tracking a branch and never tracked the trunk it was
+       descended from.
+
+       This relies on the SVN::* libraries to work.
+
+repo-config key: svn.followparent
+
+--no-metadata::
+       This gets rid of the git-svn-id: lines at the end of every commit.
+
+       With this, you lose the ability to use the rebuild command.  If
+       you ever lose your .git/svn/git-svn/.rev_db file, you won't be
+       able to fetch again, either.  This is fine for one-shot imports.
+
+       The 'git-svn log' command will not work on repositories using this,
+       either.
+
+repo-config key: svn.nometadata
+
 --
 
 COMPATIBILITY OPTIONS
@@ -231,6 +334,9 @@ for tracking the remote.
 --no-ignore-externals::
 Only used with the 'fetch' and 'rebuild' command.
 
+This command has no effect when you are using the SVN::*
+libraries with git, svn:externals are always avoided.
+
 By default, git-svn passes --ignore-externals to svn to avoid
 fetching svn:external trees into git.  Pass this flag to enable
 externals tracking directly via git.
@@ -264,7 +370,7 @@ Basic Examples
 Tracking and contributing to an Subversion managed-project:
 
 ------------------------------------------------------------------------
-# Initialize a tree (like git init-db):
+# Initialize a repo (like git init-db):
        git-svn init http://svn.foo.org/project/trunk
 # Fetch remote revisions:
        git-svn fetch
@@ -312,8 +418,8 @@ branches or directories in a Subversion repository, git-svn has a simple
 hack to allow it to track an arbitrary number of related _or_ unrelated
 SVN repositories via one git repository.  Simply set the GIT_SVN_ID
 environment variable to a name other other than "git-svn" (the default)
-and git-svn will ignore the contents of the $GIT_DIR/git-svn directory
-and instead do all of its work in $GIT_DIR/$GIT_SVN_ID for that
+and git-svn will ignore the contents of the $GIT_DIR/svn/git-svn directory
+and instead do all of its work in $GIT_DIR/svn/$GIT_SVN_ID for that
 invocation.  The interface branch will be remotes/$GIT_SVN_ID, instead of
 remotes/git-svn.  Any remotes/$GIT_SVN_ID branch should never be modified
 by the user outside of git-svn commands.
@@ -341,6 +447,9 @@ This allows you to tie unfetched SVN revision 375 to your current HEAD:
 
 Advanced Example: Tracking a Reorganized Repository
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Note: this example is now obsolete if you have SVN::* libraries
+installed.  Simply use --follow-parent when fetching.
+
 If you're tracking a directory that has moved, or otherwise been
 branched or tagged off of another directory in the repository and you
 care about the full history of the project, then you can read this
@@ -371,20 +480,18 @@ he needed to continue tracking /ufoai/trunk where /trunk left off.
 
 BUGS
 ----
-If somebody commits a conflicting changeset to SVN at a bad moment
-(right before you commit) causing a conflict and your commit to fail,
-your svn working tree ($GIT_DIR/git-svn/tree) may be dirtied.  The
-easiest thing to do is probably just to rm -rf $GIT_DIR/git-svn/tree and
-run 'rebuild'.
+
+If you are not using the SVN::* Perl libraries and somebody commits a
+conflicting changeset to SVN at a bad moment (right before you commit)
+causing a conflict and your commit to fail, your svn working tree
+($GIT_DIR/git-svn/tree) may be dirtied.  The easiest thing to do is
+probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'.
 
 We ignore all SVN properties except svn:executable.  Too difficult to
 map them since we rely heavily on git write-tree being _exactly_ the
 same on both the SVN and git working trees and I prefer not to clutter
 working trees with metadata files.
 
-svn:keywords can't be ignored in Subversion (at least I don't know of
-a way to ignore them).
-
 Renamed and copied directories are not detected by git and hence not
 tracked when committing to SVN.  I do not plan on adding support for
 this as it's quite difficult and time-consuming to get working for all
index 2135b65..0679e3c 100644 (file)
@@ -72,182 +72,6 @@ GIT COMMANDS
 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-pack[1]::
-       Invoked by 'git-fetch-pack' to push
-       what are asked for.
-
-
 High-level commands (porcelain)
 -------------------------------
 
@@ -318,7 +142,7 @@ gitlink:git-mv[1]::
        Move or rename a file, a directory, or a symlink.
 
 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.
@@ -485,6 +309,188 @@ 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-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
 -----------------------
 
@@ -559,6 +565,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
 ------------------------
index 14449ca..7e560b0 100644 (file)
@@ -179,7 +179,7 @@ object name::
        character hexadecimal encoding of the hash of the object (possibly
        followed by a white space).
 
-object type:
+object type::
        One of the identifiers "commit","tree","tag" and "blob" describing
        the type of an object.
 
@@ -324,7 +324,7 @@ tag::
        A tag is most typically used to mark a particular point in the
        commit ancestry chain.
 
-unmerged index:
+unmerged index::
        An index which contains unmerged index entries.
 
 working tree::
index 26ecba5..670827c 100644 (file)
@@ -51,6 +51,14 @@ lines are used for `git-push` and `git-fetch`/`git-pull`,
 respectively.  Multiple `Push:` and `Pull:` lines may
 be specified for additional branch mappings.
 
+Or, equivalently, in the `$GIT_DIR/config` (note the use
+of `fetch` instead of `Pull:`):
+
+[remote "<remote>"]
+       url = <url>
+       push = <refspec>
+       fetch = <refspec>
+
 The name of a file in `$GIT_DIR/branches` directory can be
 specified as an older notation short-hand; the named
 file should contain a single line, a URL in one of the
index be8bf39..40e2a68 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -132,6 +132,8 @@ GITWEB_HOMETEXT = indextext.html
 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
 
@@ -173,7 +175,7 @@ SCRIPT_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
 
@@ -265,6 +267,7 @@ LIB_OBJS = \
 
 BUILTIN_OBJS = \
        builtin-add.o \
+       builtin-annotate.o \
        builtin-apply.o \
        builtin-archive.o \
        builtin-branch.o \
@@ -679,6 +682,8 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
            -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 $@+ $@
@@ -764,6 +769,8 @@ $(LIB_FILE): $(LIB_OBJS)
        rm -f $@ && $(AR) rcs $@ $(LIB_OBJS)
 
 XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o
+$(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
+       xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
 
 $(XDIFF_LIB): $(XDIFF_OBJS)
        rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
@@ -860,8 +867,9 @@ git.spec: git.spec.in
        mv $@+ $@
 
 GIT_TARNAME=git-$(GIT_VERSION)
-dist: git.spec git-tar-tree
-       ./git-tar-tree HEAD^{tree} $(GIT_TARNAME) > $(GIT_TARNAME).tar
+dist: git.spec git-archive
+       ./git-archive --format=tar \
+               --prefix=$(GIT_TARNAME)/ HEAD^{tree} > $(GIT_TARNAME).tar
        @mkdir -p $(GIT_TARNAME)
        @cp git.spec $(GIT_TARNAME)
        @echo $(GIT_VERSION) > $(GIT_TARNAME)/version
index 3ffdad6..28e7352 100644 (file)
@@ -145,6 +145,7 @@ static int write_zip_entry(const unsigned char *sha1,
 {
        struct zip_local_header header;
        struct zip_dir_header dirent;
+       unsigned long attr2;
        unsigned long compressed_size;
        unsigned long uncompressed_size;
        unsigned long crc;
@@ -172,12 +173,16 @@ static int write_zip_entry(const unsigned char *sha1,
 
        if (S_ISDIR(mode)) {
                method = 0;
+               attr2 = 16;
                result = READ_TREE_RECURSIVE;
                out = NULL;
                uncompressed_size = 0;
                compressed_size = 0;
-       } else if (S_ISREG(mode)) {
-               method = zlib_compression_level == 0 ? 0 : 8;
+       } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+               method = 0;
+               attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0;
+               if (S_ISREG(mode) && zlib_compression_level != 0)
+                       method = 8;
                result = 0;
                buffer = read_sha1_file(sha1, type, &size);
                if (!buffer)
@@ -213,8 +218,8 @@ static int write_zip_entry(const unsigned char *sha1,
        }
 
        copy_le32(dirent.magic, 0x02014b50);
-       copy_le16(dirent.creator_version, 0);
-       copy_le16(dirent.version, 20);
+       copy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0);
+       copy_le16(dirent.version, 10);
        copy_le16(dirent.flags, 0);
        copy_le16(dirent.compression_method, method);
        copy_le16(dirent.mtime, zip_time);
@@ -227,7 +232,7 @@ static int write_zip_entry(const unsigned char *sha1,
        copy_le16(dirent.comment_length, 0);
        copy_le16(dirent.disk, 0);
        copy_le16(dirent.attr1, 0);
-       copy_le32(dirent.attr2, 0);
+       copy_le32(dirent.attr2, attr2);
        copy_le32(dirent.offset, zip_offset);
        memcpy(zip_dir + zip_dir_offset, &dirent, sizeof(struct zip_dir_header));
        zip_dir_offset += sizeof(struct zip_dir_header);
@@ -236,7 +241,7 @@ static int write_zip_entry(const unsigned char *sha1,
        zip_dir_entries++;
 
        copy_le32(header.magic, 0x04034b50);
-       copy_le16(header.version, 20);
+       copy_le16(header.version, 10);
        copy_le16(header.flags, 0);
        copy_le16(header.compression_method, method);
        copy_le16(header.mtime, zip_time);
diff --git a/blame.c b/blame.c
index 8cfd5d9..3ec1c8f 100644 (file)
--- a/blame.c
+++ 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 {
@@ -38,9 +45,10 @@ 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 {
@@ -156,11 +164,10 @@ static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
                                  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;
@@ -168,12 +175,7 @@ static int get_blob_sha1(struct tree *t, const char *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);
@@ -232,6 +234,9 @@ static void print_map(struct commit *cmit, struct commit *other)
            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;
@@ -239,7 +244,8 @@ static void print_map(struct commit *cmit, struct commit *other)
                if (i < util->num_lines) {
                        num = util->line_map[i];
                        printf("%d\t", num);
-               } else
+               }
+               else
                        printf("\t");
 
                if (i < util2->num_lines) {
@@ -247,7 +253,8 @@ static void print_map(struct commit *cmit, struct commit *other)
                        printf("%d\t", num2);
                        if (num != -1 && num2 != num)
                                printf("---");
-               } else
+               }
+               else
                        printf("\t");
 
                printf("\n");
@@ -266,12 +273,12 @@ static void fill_line_map(struct commit *commit, struct commit *other,
        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;
@@ -293,7 +300,8 @@ static void fill_line_map(struct commit *commit, struct commit *other,
                                i2 += chunk->len2;
 
                        cur_chunk++;
-               } else {
+               }
+               else {
                        if (i2 >= util2->num_lines)
                                break;
 
@@ -327,19 +335,15 @@ static int map_line(struct commit *commit, int line)
        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;
 }
@@ -369,7 +373,7 @@ static void alloc_line_map(struct commit *commit)
                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);
@@ -378,9 +382,9 @@ static void alloc_line_map(struct commit *commit)
                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;
@@ -395,18 +399,17 @@ static void init_first_commit(struct commit* commit, const char* 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);
 
@@ -442,7 +445,7 @@ static void process_commits(struct rev_info *rev, const char *path,
                     parents != NULL; parents = parents->next)
                        num_parents++;
 
-               if(num_parents == 0)
+               if (num_parents == 0)
                        *initial = commit;
 
                if (fill_util_info(commit))
@@ -503,13 +506,12 @@ static void process_commits(struct rev_info *rev, const char *path,
        } 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;
 
@@ -520,12 +522,11 @@ static int compare_tree_path(struct rev_info* revs,
        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;
 
@@ -536,9 +537,9 @@ static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1,
        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;
@@ -564,9 +565,11 @@ static const char* find_rename(struct commit* commit, struct commit* parent)
        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;
                }
        }
@@ -582,7 +585,7 @@ static void simplify_commit(struct rev_info *revs, struct commit *commit)
                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;
@@ -608,17 +611,17 @@ static void simplify_commit(struct rev_info *revs, struct commit *commit)
 
                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;
                                }
@@ -639,47 +642,106 @@ static void simplify_commit(struct rev_info *revs, struct commit *commit)
        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];
@@ -704,15 +766,15 @@ static const char* format_time(unsigned long time, const char* tz_str,
        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;
 }
 
@@ -735,6 +797,101 @@ static int read_ancestry(const char *graft_file,
        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;
@@ -747,38 +904,43 @@ int main(int argc, const char **argv)
        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;
@@ -786,33 +948,51 @@ int main(int argc, const char **argv)
                                        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);
@@ -830,7 +1010,6 @@ int main(int argc, const char **argv)
                return 1;
        }
 
-
        init_revisions(&rev, setup_git_directory());
        rev.remove_empty_trees = 1;
        rev.topo_order = 1;
@@ -848,62 +1027,49 @@ int main(int argc, const char **argv)
        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;
                }
diff --git a/builtin-annotate.c b/builtin-annotate.c
new file mode 100644 (file)
index 0000000..25ad473
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * "git annotate" builtin alias
+ *
+ * Copyright (C) 2006 Ryan Anderson
+ */
+#include "git-compat-util.h"
+#include "exec_cmd.h"
+
+int cmd_annotate(int argc, const char **argv, const char *prefix)
+{
+       const char **nargv;
+       int i;
+       nargv = xmalloc(sizeof(char *) * (argc + 2));
+
+       nargv[0] = "blame";
+       nargv[1] = "-c";
+
+       for (i = 1; i < argc; i++) {
+               nargv[i+1] = argv[i];
+       }
+       nargv[argc + 1] = NULL;
+
+       return execv_git_cmd(nargv);
+}
+
index de5f855..11397f5 100644 (file)
@@ -360,7 +360,7 @@ static int gitdiff_hdrend(const char *line, struct patch *patch)
 static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
 {
        if (!orig_name && !isnull)
-               return find_name(line, NULL, 1, 0);
+               return find_name(line, NULL, 1, TERM_TAB);
 
        if (orig_name) {
                int len;
@@ -370,7 +370,7 @@ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name,
                len = strlen(name);
                if (isnull)
                        die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
-               another = find_name(line, NULL, 1, 0);
+               another = find_name(line, NULL, 1, TERM_TAB);
                if (!another || memcmp(another, name, len))
                        die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
                free(another);
@@ -934,6 +934,7 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
                switch (*line) {
                default:
                        return -1;
+               case '\n': /* newer GNU diff, an empty context line */
                case ' ':
                        oldlines--;
                        newlines--;
@@ -1623,6 +1624,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
                                first = '-';
                }
                switch (first) {
+               case '\n':
+                       /* Newer GNU diff, empty context line */
+                       if (plen < 0)
+                               /* ... followed by '\No newline'; nothing */
+                               break;
+                       old[oldsize++] = '\n';
+                       new[newsize++] = '\n';
+                       break;
                case ' ':
                case '-':
                        memcpy(old + oldsize, patch + 1, plen);
@@ -1783,8 +1792,6 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
 {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
        unsigned char sha1[20];
-       unsigned char hdr[50];
-       int hdrlen;
 
        /* For safety, we require patch index line to contain
         * full 40-byte textual SHA1 for old and new, at least for now.
@@ -1800,8 +1807,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
                /* See if the old one matches what the patch
                 * applies to.
                 */
-               write_sha1_file_prepare(desc->buffer, desc->size,
-                                       blob_type, sha1, hdr, &hdrlen);
+               hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
                        return error("the patch applies to '%s' (%s), "
                                     "which does not match the "
@@ -1846,8 +1852,7 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
                                     name);
 
                /* verify that the result matches */
-               write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
-                                       sha1, hdr, &hdrlen);
+               hash_sha1_file(desc->buffer, desc->size, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
                        return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)", name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
@@ -2112,7 +2117,7 @@ static void numstat_patch_list(struct patch *patch)
                        quote_c_style(name, NULL, stdout, 0);
                else
                        fputs(name, stdout);
-               putchar('\n');
+               putchar(line_termination);
        }
 }
 
index 6dabdee..9177379 100644 (file)
@@ -75,7 +75,7 @@ static int run_remote_archiver(const char *remote, int argc,
                die("git-archive: expected a flush");
 
        /* Now, start reading from fd[0] and spit it out to stdout */
-       rv = recv_sideband("archive", fd[0], 1, 2, buf, sizeof(buf));
+       rv = recv_sideband("archive", fd[0], 1, 2);
        close(fd[0]);
        rv |= finish_connect(pid);
 
index 4205e5d..ad7dc00 100644 (file)
@@ -596,6 +596,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                                            GREP_CLOSE_PAREN);
                        continue;
                }
+               if (!strcmp("--all-match", arg)) {
+                       opt.all_match = 1;
+                       continue;
+               }
                if (!strcmp("-e", arg)) {
                        if (1 < argc) {
                                append_grep_pattern(&opt, argv[1],
index 96c069a..41e1e74 100644 (file)
@@ -15,7 +15,7 @@
 #include <sys/time.h>
 #include <signal.h>
 
-static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
+static const char pack_usage[] = "git-pack-objects [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] [--revs [--unpacked | --all]*] [--stdout | base-name] <ref-list | <object-list]";
 
 struct object_entry {
        unsigned char sha1[20];
@@ -29,6 +29,7 @@ struct object_entry {
        enum object_type type;
        enum object_type in_pack_type;  /* could be delta */
        unsigned long delta_size;       /* delta data size (uncompressed) */
+#define in_pack_header_size delta_size /* only when reusing pack data */
        struct object_entry *delta;     /* delta base object */
        struct packed_git *in_pack;     /* already in pack */
        unsigned int in_pack_offset;
@@ -60,6 +61,8 @@ static int non_empty;
 static int no_reuse_delta;
 static int local;
 static int incremental;
+static int allow_ofs_delta;
+
 static struct object_entry **sorted_by_sha, **sorted_by_type;
 static struct object_entry *objects;
 static int nr_objects, nr_alloc, nr_result;
@@ -84,17 +87,25 @@ static int object_ix_hashsz;
  * Pack index for existing packs give us easy access to the offsets into
  * corresponding pack file where each object's data starts, but the entries
  * do not store the size of the compressed representation (uncompressed
- * size is easily available by examining the pack entry header).  We build
- * a hashtable of existing packs (pack_revindex), and keep reverse index
- * here -- pack index file is sorted by object name mapping to offset; this
- * pack_revindex[].revindex array is an ordered list of offsets, so if you
- * know the offset of an object, next offset is where its packed
- * representation ends.
+ * size is easily available by examining the pack entry header).  It is
+ * also rather expensive to find the sha1 for an object given its offset.
+ *
+ * We build a hashtable of existing packs (pack_revindex), and keep reverse
+ * index here -- pack index file is sorted by object name mapping to offset;
+ * this pack_revindex[].revindex array is a list of offset/index_nr pairs
+ * ordered by offset, so if you know the offset of an object, next offset
+ * is where its packed representation ends and the index_nr can be used to
+ * get the object sha1 from the main index.
  */
+struct revindex_entry {
+       unsigned int offset;
+       unsigned int nr;
+};
 struct pack_revindex {
        struct packed_git *p;
-       unsigned long *revindex;
-} *pack_revindex = NULL;
+       struct revindex_entry *revindex;
+};
+static struct  pack_revindex *pack_revindex;
 static int pack_revindex_hashsz;
 
 /*
@@ -141,14 +152,9 @@ static void prepare_pack_ix(void)
 
 static int cmp_offset(const void *a_, const void *b_)
 {
-       unsigned long a = *(unsigned long *) a_;
-       unsigned long b = *(unsigned long *) b_;
-       if (a < b)
-               return -1;
-       else if (a == b)
-               return 0;
-       else
-               return 1;
+       const struct revindex_entry *a = a_;
+       const struct revindex_entry *b = b_;
+       return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0;
 }
 
 /*
@@ -161,25 +167,27 @@ static void prepare_pack_revindex(struct pack_revindex *rix)
        int i;
        void *index = p->index_base + 256;
 
-       rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));
+       rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1));
        for (i = 0; i < num_ent; i++) {
                unsigned int hl = *((unsigned int *)((char *) index + 24*i));
-               rix->revindex[i] = ntohl(hl);
+               rix->revindex[i].offset = ntohl(hl);
+               rix->revindex[i].nr = i;
        }
        /* This knows the pack format -- the 20-byte trailer
         * follows immediately after the last object data.
         */
-       rix->revindex[num_ent] = p->pack_size - 20;
-       qsort(rix->revindex, num_ent, sizeof(unsigned long), cmp_offset);
+       rix->revindex[num_ent].offset = p->pack_size - 20;
+       rix->revindex[num_ent].nr = -1;
+       qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset);
 }
 
-static unsigned long find_packed_object_size(struct packed_git *p,
-                                            unsigned long ofs)
+static struct revindex_entry * find_packed_object(struct packed_git *p,
+                                                 unsigned int ofs)
 {
        int num;
        int lo, hi;
        struct pack_revindex *rix;
-       unsigned long *revindex;
+       struct revindex_entry *revindex;
        num = pack_revindex_ix(p);
        if (num < 0)
                die("internal error: pack revindex uninitialized");
@@ -191,10 +199,10 @@ static unsigned long find_packed_object_size(struct packed_git *p,
        hi = num_packed_objects(p) + 1;
        do {
                int mi = (lo + hi) / 2;
-               if (revindex[mi] == ofs) {
-                       return revindex[mi+1] - ofs;
+               if (revindex[mi].offset == ofs) {
+                       return revindex + mi;
                }
-               else if (ofs < revindex[mi])
+               else if (ofs < revindex[mi].offset)
                        hi = mi;
                else
                        lo = mi + 1;
@@ -202,6 +210,20 @@ static unsigned long find_packed_object_size(struct packed_git *p,
        die("internal error: pack revindex corrupt");
 }
 
+static unsigned long find_packed_object_size(struct packed_git *p,
+                                            unsigned long ofs)
+{
+       struct revindex_entry *entry = find_packed_object(p, ofs);
+       return entry[1].offset - ofs;
+}
+
+static unsigned char *find_packed_object_name(struct packed_git *p,
+                                             unsigned long ofs)
+{
+       struct revindex_entry *entry = find_packed_object(p, ofs);
+       return (unsigned char *)(p->index_base + 256) + 24 * entry->nr + 4;
+}
+
 static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
 {
        unsigned long othersize, delta_size;
@@ -232,7 +254,7 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha
        int n = 1;
        unsigned char c;
 
-       if (type < OBJ_COMMIT || type > OBJ_DELTA)
+       if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
                die("bad type %d", type);
 
        c = (type << 4) | (size & 15);
@@ -247,6 +269,10 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha
        return n;
 }
 
+/*
+ * we are going to reuse the existing object data as is.  make
+ * sure it is not corrupt.
+ */
 static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect)
 {
        z_stream stream;
@@ -278,32 +304,6 @@ static int check_inflate(unsigned char *data, unsigned long len, unsigned long e
        return st;
 }
 
-/*
- * we are going to reuse the existing pack entry data.  make
- * sure it is not corrupt.
- */
-static int revalidate_pack_entry(struct object_entry *entry, unsigned char *data, unsigned long len)
-{
-       enum object_type type;
-       unsigned long size, used;
-
-       if (pack_to_stdout)
-               return 0;
-
-       /* the caller has already called use_packed_git() for us,
-        * so it is safe to access the pack data from mmapped location.
-        * make sure the entry inflates correctly.
-        */
-       used = unpack_object_header_gently(data, len, &type, &size);
-       if (!used)
-               return -1;
-       if (type == OBJ_DELTA)
-               used += 20; /* skip base object name */
-       data += used;
-       len -= used;
-       return check_inflate(data, len, entry->size);
-}
-
 static int revalidate_loose_object(struct object_entry *entry,
                                   unsigned char *map,
                                   unsigned long mapsize)
@@ -334,13 +334,10 @@ static unsigned long write_object(struct sha1file *f,
        enum object_type obj_type;
        int to_reuse = 0;
 
-       if (entry->preferred_base)
-               return 0;
-
        obj_type = entry->type;
        if (! entry->in_pack)
                to_reuse = 0;   /* can't reuse what we don't have */
-       else if (obj_type == OBJ_DELTA)
+       else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
                to_reuse = 1;   /* check_object() decided it for us */
        else if (obj_type != entry->in_pack_type)
                to_reuse = 0;   /* pack has delta which is unusable */
@@ -380,18 +377,35 @@ static unsigned long write_object(struct sha1file *f,
                if (entry->delta) {
                        buf = delta_against(buf, size, entry);
                        size = entry->delta_size;
-                       obj_type = OBJ_DELTA;
+                       obj_type = (allow_ofs_delta && entry->delta->offset) ?
+                               OBJ_OFS_DELTA : OBJ_REF_DELTA;
                }
                /*
                 * The object header is a byte of 'type' followed by zero or
-                * more bytes of length.  For deltas, the 20 bytes of delta
-                * sha1 follows that.
+                * more bytes of length.
                 */
                hdrlen = encode_header(obj_type, size, header);
                sha1write(f, header, hdrlen);
 
-               if (entry->delta) {
-                       sha1write(f, entry->delta, 20);
+               if (obj_type == OBJ_OFS_DELTA) {
+                       /*
+                        * Deltas with relative base contain an additional
+                        * encoding of the relative offset for the delta
+                        * base from this object's position in the pack.
+                        */
+                       unsigned long ofs = entry->offset - entry->delta->offset;
+                       unsigned pos = sizeof(header) - 1;
+                       header[pos] = ofs & 127;
+                       while (ofs >>= 7)
+                               header[--pos] = 128 | (--ofs & 127);
+                       sha1write(f, header + pos, sizeof(header) - pos);
+                       hdrlen += sizeof(header) - pos;
+               } else if (obj_type == OBJ_REF_DELTA) {
+                       /*
+                        * Deltas with a base reference contain
+                        * an additional 20 bytes for the base sha1.
+                        */
+                       sha1write(f, entry->delta->sha1, 20);
                        hdrlen += 20;
                }
                datalen = sha1write_compressed(f, buf, size);
@@ -399,21 +413,40 @@ static unsigned long write_object(struct sha1file *f,
        }
        else {
                struct packed_git *p = entry->in_pack;
-               use_packed_git(p);
 
-               datalen = find_packed_object_size(p, entry->in_pack_offset);
-               buf = (char *) p->pack_base + entry->in_pack_offset;
+               if (entry->delta) {
+                       obj_type = (allow_ofs_delta && entry->delta->offset) ?
+                               OBJ_OFS_DELTA : OBJ_REF_DELTA;
+                       reused_delta++;
+               }
+               hdrlen = encode_header(obj_type, entry->size, header);
+               sha1write(f, header, hdrlen);
+               if (obj_type == OBJ_OFS_DELTA) {
+                       unsigned long ofs = entry->offset - entry->delta->offset;
+                       unsigned pos = sizeof(header) - 1;
+                       header[pos] = ofs & 127;
+                       while (ofs >>= 7)
+                               header[--pos] = 128 | (--ofs & 127);
+                       sha1write(f, header + pos, sizeof(header) - pos);
+                       hdrlen += sizeof(header) - pos;
+               } else if (obj_type == OBJ_REF_DELTA) {
+                       sha1write(f, entry->delta->sha1, 20);
+                       hdrlen += 20;
+               }
 
-               if (revalidate_pack_entry(entry, buf, datalen))
+               use_packed_git(p);
+               buf = (char *) p->pack_base
+                       + entry->in_pack_offset
+                       + entry->in_pack_header_size;
+               datalen = find_packed_object_size(p, entry->in_pack_offset)
+                               - entry->in_pack_header_size;
+               if (!pack_to_stdout && check_inflate(buf, datalen, entry->size))
                        die("corrupt delta in pack %s", sha1_to_hex(entry->sha1));
                sha1write(f, buf, datalen);
                unuse_packed_git(p);
-               hdrlen = 0; /* not really */
-               if (obj_type == OBJ_DELTA)
-                       reused_delta++;
                reused++;
        }
-       if (obj_type == OBJ_DELTA)
+       if (entry->delta)
                written_delta++;
        written++;
        return hdrlen + datalen;
@@ -423,17 +456,16 @@ static unsigned long write_one(struct sha1file *f,
                               struct object_entry *e,
                               unsigned long offset)
 {
-       if (e->offset)
+       if (e->offset || e->preferred_base)
                /* offset starts from header size and cannot be zero
                 * if it is written already.
                 */
                return offset;
-       e->offset = offset;
-       offset += write_object(f, e);
-       /* if we are deltified, write out its base object. */
+       /* if we are deltified, write out its base object first. */
        if (e->delta)
                offset = write_one(f, e->delta, offset);
-       return offset;
+       e->offset = offset;
+       return offset + write_object(f, e);
 }
 
 static void write_pack_file(void)
@@ -899,26 +931,64 @@ static void check_object(struct object_entry *entry)
        char type[20];
 
        if (entry->in_pack && !entry->preferred_base) {
-               unsigned char base[20];
-               unsigned long size;
-               struct object_entry *base_entry;
+               struct packed_git *p = entry->in_pack;
+               unsigned long left = p->pack_size - entry->in_pack_offset;
+               unsigned long size, used;
+               unsigned char *buf;
+               struct object_entry *base_entry = NULL;
+
+               use_packed_git(p);
+               buf = p->pack_base;
+               buf += entry->in_pack_offset;
 
                /* We want in_pack_type even if we do not reuse delta.
                 * There is no point not reusing non-delta representations.
                 */
-               check_reuse_pack_delta(entry->in_pack,
-                                      entry->in_pack_offset,
-                                      base, &size,
-                                      &entry->in_pack_type);
+               used = unpack_object_header_gently(buf, left,
+                                                  &entry->in_pack_type, &size);
+               if (!used || left - used <= 20)
+                       die("corrupt pack for %s", sha1_to_hex(entry->sha1));
 
                /* Check if it is delta, and the base is also an object
                 * we are going to pack.  If so we will reuse the existing
                 * delta.
                 */
-               if (!no_reuse_delta &&
-                   entry->in_pack_type == OBJ_DELTA &&
-                   (base_entry = locate_object_entry(base)) &&
-                   (!base_entry->preferred_base)) {
+               if (!no_reuse_delta) {
+                       unsigned char c, *base_name;
+                       unsigned long ofs;
+                       /* there is at least 20 bytes left in the pack */
+                       switch (entry->in_pack_type) {
+                       case OBJ_REF_DELTA:
+                               base_name = buf + used;
+                               used += 20;
+                               break;
+                       case OBJ_OFS_DELTA:
+                               c = buf[used++];
+                               ofs = c & 127;
+                               while (c & 128) {
+                                       ofs += 1;
+                                       if (!ofs || ofs & ~(~0UL >> 7))
+                                               die("delta base offset overflow in pack for %s",
+                                                   sha1_to_hex(entry->sha1));
+                                       c = buf[used++];
+                                       ofs = (ofs << 7) + (c & 127);
+                               }
+                               if (ofs >= entry->in_pack_offset)
+                                       die("delta base offset out of bound for %s",
+                                           sha1_to_hex(entry->sha1));
+                               ofs = entry->in_pack_offset - ofs;
+                               base_name = find_packed_object_name(p, ofs);
+                               break;
+                       default:
+                               base_name = NULL;
+                       }
+                       if (base_name)
+                               base_entry = locate_object_entry(base_name);
+               }
+               unuse_packed_git(p);
+               entry->in_pack_header_size = used;
+
+               if (base_entry) {
 
                        /* Depth value does not matter - find_deltas()
                         * will never consider reused delta as the
@@ -927,9 +997,9 @@ static void check_object(struct object_entry *entry)
                         */
 
                        /* uncompressed size of the delta data */
-                       entry->size = entry->delta_size = size;
+                       entry->size = size;
                        entry->delta = base_entry;
-                       entry->type = OBJ_DELTA;
+                       entry->type = entry->in_pack_type;
 
                        entry->delta_sibling = base_entry->delta_child;
                        base_entry->delta_child = entry;
@@ -1484,6 +1554,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        no_reuse_delta = 1;
                        continue;
                }
+               if (!strcmp("--delta-base-offset", arg)) {
+                       allow_ofs_delta = 1;
+                       continue;
+               }
                if (!strcmp("--stdout", arg)) {
                        pack_to_stdout = 1;
                        continue;
index 960db49..24e3b0a 100644 (file)
@@ -4,9 +4,7 @@
 static const char prune_packed_usage[] =
 "git-prune-packed [-n]";
 
-static int dryrun;
-
-static void prune_dir(int i, DIR *dir, char *pathname, int len)
+static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun)
 {
        struct dirent *de;
        char hex[40];
@@ -31,7 +29,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len)
        rmdir(pathname);
 }
 
-static void prune_packed_objects(void)
+void prune_packed_objects(int dryrun)
 {
        int i;
        static char pathname[PATH_MAX];
@@ -50,7 +48,7 @@ static void prune_packed_objects(void)
                d = opendir(pathname);
                if (!d)
                        continue;
-               prune_dir(i, d, pathname, len + 3);
+               prune_dir(i, d, pathname, len + 3, dryrun);
                closedir(d);
        }
 }
@@ -58,6 +56,7 @@ static void prune_packed_objects(void)
 int cmd_prune_packed(int argc, const char **argv, const char *prefix)
 {
        int i;
+       int dryrun = 0;
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -73,6 +72,6 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)
                usage(prune_packed_usage);
        }
        sync();
-       prune_packed_objects();
+       prune_packed_objects(dryrun);
        return 0;
 }
index e79b515..d853902 100644 (file)
@@ -255,5 +255,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 
        prune_object_dir(get_object_directory());
 
+       sync();
+       prune_packed_objects(show_only);
        return 0;
 }
index 4f96bca..74a90c1 100644 (file)
@@ -15,14 +15,14 @@ static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-fil
 
 /* We always read in 4kB chunks. */
 static unsigned char buffer[4096];
-static unsigned long offset, len;
+static unsigned long offset, len, consumed_bytes;
 static SHA_CTX ctx;
 
 /*
  * Make sure at least "min" bytes are available in the buffer, and
  * return the pointer to the buffer.
  */
-static void * fill(int min)
+static void *fill(int min)
 {
        if (min <= len)
                return buffer + offset;
@@ -30,7 +30,7 @@ static void * fill(int min)
                die("cannot fill %d bytes", min);
        if (offset) {
                SHA1_Update(&ctx, buffer, offset);
-               memcpy(buffer, buffer + offset, len);
+               memmove(buffer, buffer + offset, len);
                offset = 0;
        }
        do {
@@ -51,6 +51,7 @@ static void use(int bytes)
                die("used more bytes than were available");
        len -= bytes;
        offset += bytes;
+       consumed_bytes += bytes;
 }
 
 static void *get_data(unsigned long size)
@@ -89,35 +90,49 @@ static void *get_data(unsigned long size)
 
 struct delta_info {
        unsigned char base_sha1[20];
+       unsigned long base_offset;
        unsigned long size;
        void *delta;
+       unsigned nr;
        struct delta_info *next;
 };
 
 static struct delta_info *delta_list;
 
-static void add_delta_to_list(unsigned char *base_sha1, void *delta, unsigned long size)
+static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
+                             unsigned long base_offset,
+                             void *delta, unsigned long size)
 {
        struct delta_info *info = xmalloc(sizeof(*info));
 
        hashcpy(info->base_sha1, base_sha1);
+       info->base_offset = base_offset;
        info->size = size;
        info->delta = delta;
+       info->nr = nr;
        info->next = delta_list;
        delta_list = info;
 }
 
-static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size);
+struct obj_info {
+       unsigned long offset;
+       unsigned char sha1[20];
+};
+
+static struct obj_info *obj_list;
 
-static void write_object(void *buf, unsigned long size, const char *type)
+static void added_object(unsigned nr, const char *type, void *data,
+                        unsigned long size);
+
+static void write_object(unsigned nr, void *buf, unsigned long size,
+                        const char *type)
 {
-       unsigned char sha1[20];
-       if (write_sha1_file(buf, size, type, sha1) < 0)
+       if (write_sha1_file(buf, size, type, obj_list[nr].sha1) < 0)
                die("failed to write object");
-       added_object(sha1, type, buf, size);
+       added_object(nr, type, buf, size);
 }
 
-static void resolve_delta(const char *type,
+static void resolve_delta(unsigned nr, const char *type,
                          void *base, unsigned long base_size,
                          void *delta, unsigned long delta_size)
 {
@@ -130,20 +145,23 @@ static void resolve_delta(const char *type,
        if (!result)
                die("failed to apply delta");
        free(delta);
-       write_object(result, result_size, type);
+       write_object(nr, result, result_size, type);
        free(result);
 }
 
-static void added_object(unsigned char *sha1, const char *type, void *data, unsigned long size)
+static void added_object(unsigned nr, const char *type, void *data,
+                        unsigned long size)
 {
        struct delta_info **p = &delta_list;
        struct delta_info *info;
 
        while ((info = *p) != NULL) {
-               if (!hashcmp(info->base_sha1, sha1)) {
+               if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
+                   info->base_offset == obj_list[nr].offset) {
                        *p = info->next;
                        p = &delta_list;
-                       resolve_delta(type, data, size, info->delta, info->size);
+                       resolve_delta(info->nr, type, data, size,
+                                     info->delta, info->size);
                        free(info);
                        continue;
                }
@@ -151,7 +169,8 @@ static void added_object(unsigned char *sha1, const char *type, void *data, unsi
        }
 }
 
-static void unpack_non_delta_entry(enum object_type kind, unsigned long size)
+static void unpack_non_delta_entry(enum object_type kind, unsigned long size,
+                                  unsigned nr)
 {
        void *buf = get_data(size);
        const char *type;
@@ -164,30 +183,80 @@ static void unpack_non_delta_entry(enum object_type kind, unsigned long size)
        default: die("bad type %d", kind);
        }
        if (!dry_run && buf)
-               write_object(buf, size, type);
+               write_object(nr, buf, size, type);
        free(buf);
 }
 
-static void unpack_delta_entry(unsigned long delta_size)
+static void unpack_delta_entry(enum object_type kind, unsigned long delta_size,
+                              unsigned nr)
 {
        void *delta_data, *base;
        unsigned long base_size;
        char type[20];
        unsigned char base_sha1[20];
 
-       hashcpy(base_sha1, fill(20));
-       use(20);
+       if (kind == OBJ_REF_DELTA) {
+               hashcpy(base_sha1, fill(20));
+               use(20);
+               delta_data = get_data(delta_size);
+               if (dry_run || !delta_data) {
+                       free(delta_data);
+                       return;
+               }
+               if (!has_sha1_file(base_sha1)) {
+                       hashcpy(obj_list[nr].sha1, null_sha1);
+                       add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
+                       return;
+               }
+       } else {
+               unsigned base_found = 0;
+               unsigned char *pack, c;
+               unsigned long base_offset;
+               unsigned lo, mid, hi;
 
-       delta_data = get_data(delta_size);
-       if (dry_run || !delta_data) {
-               free(delta_data);
-               return;
-       }
+               pack = fill(1);
+               c = *pack;
+               use(1);
+               base_offset = c & 127;
+               while (c & 128) {
+                       base_offset += 1;
+                       if (!base_offset || base_offset & ~(~0UL >> 7))
+                               die("offset value overflow for delta base object");
+                       pack = fill(1);
+                       c = *pack;
+                       use(1);
+                       base_offset = (base_offset << 7) + (c & 127);
+               }
+               base_offset = obj_list[nr].offset - base_offset;
 
-       if (!has_sha1_file(base_sha1)) {
-               add_delta_to_list(base_sha1, delta_data, delta_size);
-               return;
+               delta_data = get_data(delta_size);
+               if (dry_run || !delta_data) {
+                       free(delta_data);
+                       return;
+               }
+               lo = 0;
+               hi = nr;
+               while (lo < hi) {
+                       mid = (lo + hi)/2;
+                       if (base_offset < obj_list[mid].offset) {
+                               hi = mid;
+                       } else if (base_offset > obj_list[mid].offset) {
+                               lo = mid + 1;
+                       } else {
+                               hashcpy(base_sha1, obj_list[mid].sha1);
+                               base_found = !is_null_sha1(base_sha1);
+                               break;
+                       }
+               }
+               if (!base_found) {
+                       /* The delta base object is itself a delta that
+                          has not been resolved yet. */
+                       hashcpy(obj_list[nr].sha1, null_sha1);
+                       add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
+                       return;
+               }
        }
+
        base = read_sha1_file(base_sha1, type, &base_size);
        if (!base) {
                error("failed to read delta-pack base object %s",
@@ -197,7 +266,7 @@ static void unpack_delta_entry(unsigned long delta_size)
                has_errors = 1;
                return;
        }
-       resolve_delta(type, base, base_size, delta_data, delta_size);
+       resolve_delta(nr, type, base, base_size, delta_data, delta_size);
        free(base);
 }
 
@@ -208,6 +277,8 @@ static void unpack_one(unsigned nr, unsigned total)
        unsigned long size;
        enum object_type type;
 
+       obj_list[nr].offset = consumed_bytes;
+
        pack = fill(1);
        c = *pack;
        use(1);
@@ -216,7 +287,7 @@ static void unpack_one(unsigned nr, unsigned total)
        shift = 4;
        while (c & 0x80) {
                pack = fill(1);
-               c = *pack++;
+               c = *pack;
                use(1);
                size += (c & 0x7f) << shift;
                shift += 7;
@@ -225,13 +296,14 @@ static void unpack_one(unsigned nr, unsigned total)
                static unsigned long last_sec;
                static unsigned last_percent;
                struct timeval now;
-               unsigned percentage = (nr * 100) / total;
+               unsigned percentage = ((nr+1) * 100) / total;
 
                gettimeofday(&now, NULL);
                if (percentage != last_percent || now.tv_sec != last_sec) {
                        last_sec = now.tv_sec;
                        last_percent = percentage;
-                       fprintf(stderr, "%4u%% (%u/%u) done\r", percentage, nr, total);
+                       fprintf(stderr, "%4u%% (%u/%u) done\r",
+                                       percentage, (nr+1), total);
                }
        }
        switch (type) {
@@ -239,10 +311,11 @@ static void unpack_one(unsigned nr, unsigned total)
        case OBJ_TREE:
        case OBJ_BLOB:
        case OBJ_TAG:
-               unpack_non_delta_entry(type, size);
+               unpack_non_delta_entry(type, size, nr);
                return;
-       case OBJ_DELTA:
-               unpack_delta_entry(size);
+       case OBJ_REF_DELTA:
+       case OBJ_OFS_DELTA:
+               unpack_delta_entry(type, size, nr);
                return;
        default:
                error("bad object type %d", type);
@@ -265,9 +338,10 @@ static void unpack_all(void)
                die("unknown pack file version %d", ntohl(hdr->hdr_version));
        fprintf(stderr, "Unpacking %d objects\n", nr_objects);
 
+       obj_list = xmalloc(nr_objects * sizeof(*obj_list));
        use(sizeof(struct pack_header));
        for (i = 0; i < nr_objects; i++)
-               unpack_one(i+1, nr_objects);
+               unpack_one(i, nr_objects);
        if (delta_list)
                die("unresolved deltas left after unpacking");
 }
index db9b369..f7150aa 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -11,8 +11,10 @@ extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const cha
 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);
index 323c68a..a803262 100644 (file)
@@ -2,7 +2,9 @@
 #include "tree.h"
 #include "cache-tree.h"
 
+#ifndef DEBUG
 #define DEBUG 0
+#endif
 
 struct cache_tree *cache_tree(void)
 {
@@ -344,12 +346,8 @@ static int update_one(struct cache_tree *it,
 #endif
        }
 
-       if (dryrun) {
-               unsigned char hdr[200];
-               int hdrlen;
-               write_sha1_file_prepare(buffer, offset, tree_type, it->sha1,
-                                       hdr, &hdrlen);
-       }
+       if (dryrun)
+               hash_sha1_file(buffer, offset, tree_type, it->sha1);
        else
                write_sha1_file(buffer, offset, tree_type, it->sha1);
        free(buffer);
diff --git a/cache.h b/cache.h
index 0565333..e997a85 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -189,7 +189,6 @@ extern int prefer_symlink_refs;
 extern int log_all_ref_updates;
 extern int warn_ambiguous_refs;
 extern int shared_repository;
-extern int deny_non_fast_forwards;
 extern const char *apply_default_whitespace;
 extern int zlib_compression_level;
 
@@ -246,13 +245,8 @@ char *enter_repo(char *path, int strict);
 extern int sha1_object_info(const unsigned char *, char *, unsigned long *);
 extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
 extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
+extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
 extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
-extern char *write_sha1_file_prepare(void *buf,
-                                    unsigned long len,
-                                    const char *type,
-                                    unsigned char *sha1,
-                                    unsigned char *hdr,
-                                    int *hdrlen);
 
 extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 
@@ -275,8 +269,9 @@ enum object_type {
        OBJ_TREE = 2,
        OBJ_BLOB = 3,
        OBJ_TAG = 4,
-       /* 5/6 for future expansion */
-       OBJ_DELTA = 7,
+       /* 5 for future expansion */
+       OBJ_OFS_DELTA = 6,
+       OBJ_REF_DELTA = 7,
        OBJ_BAD,
 };
 
index 46d9121..29d0c9c 100644 (file)
@@ -489,6 +489,16 @@ static void show_parent_lno(struct sline *sline, unsigned long l0, unsigned long
        printf(" -%lu,%lu", l0, l1-l0);
 }
 
+static int hunk_comment_line(const char *bol)
+{
+       int ch;
+
+       if (!bol)
+               return 0;
+       ch = *bol & 0xff;
+       return (isalpha(ch) || ch == '_' || ch == '$');
+}
+
 static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                       int use_color)
 {
@@ -508,8 +518,13 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                struct sline *sl = &sline[lno];
                unsigned long hunk_end;
                unsigned long rlines;
-               while (lno <= cnt && !(sline[lno].flag & mark))
+               const char *hunk_comment = NULL;
+
+               while (lno <= cnt && !(sline[lno].flag & mark)) {
+                       if (hunk_comment_line(sline[lno].bol))
+                               hunk_comment = sline[lno].bol;
                        lno++;
+               }
                if (cnt < lno)
                        break;
                else {
@@ -526,6 +541,22 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                        show_parent_lno(sline, lno, hunk_end, i);
                printf(" +%lu,%lu ", lno+1, rlines);
                for (i = 0; i <= num_parent; i++) putchar(combine_marker);
+
+               if (hunk_comment) {
+                       int comment_end = 0;
+                       for (i = 0; i < 40; i++) {
+                               int ch = hunk_comment[i] & 0xff;
+                               if (!ch || ch == '\n')
+                                       break;
+                               if (!isspace(ch))
+                                   comment_end = i;
+                       }
+                       if (comment_end)
+                               putchar(' ');
+                       for (i = 0; i < comment_end; i++)
+                               putchar(hunk_comment[i]);
+               }
+
                printf("%s\n", c_reset);
                while (lno < hunk_end) {
                        struct lline *ll;
@@ -707,8 +738,10 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                int use_color = opt->color_diff;
                const char *c_meta = diff_get_color(use_color, DIFF_METAINFO);
                const char *c_reset = diff_get_color(use_color, DIFF_RESET);
+               int added = 0;
+               int deleted = 0;
 
-               if (rev->loginfo)
+               if (rev->loginfo && !rev->no_commit_id)
                        show_log(rev, opt->msg_sep);
                dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
                                 elem->path, c_meta, c_reset);
@@ -722,7 +755,10 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                printf("..%s%s\n", abb, c_reset);
 
                if (mode_differs) {
-                       int added = !!elem->mode;
+                       deleted = !elem->mode;
+
+                       /* We say it was added if nobody had it */
+                       added = !deleted;
                        for (i = 0; added && i < num_parent; i++)
                                if (elem->parent[i].status !=
                                    DIFF_STATUS_ADDED)
@@ -731,7 +767,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                                printf("%snew file mode %06o",
                                       c_meta, elem->mode);
                        else {
-                               if (!elem->mode)
+                               if (deleted)
                                        printf("%sdeleted file ", c_meta);
                                printf("mode ");
                                for (i = 0; i < num_parent; i++) {
@@ -743,8 +779,14 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        }
                        printf("%s\n", c_reset);
                }
-               dump_quoted_path("--- a/", elem->path, c_meta, c_reset);
-               dump_quoted_path("+++ b/", elem->path, c_meta, c_reset);
+               if (added)
+                       dump_quoted_path("--- /dev/", "null", c_meta, c_reset);
+               else
+                       dump_quoted_path("--- a/", elem->path, c_meta, c_reset);
+               if (deleted)
+                       dump_quoted_path("+++ /dev/", "null", c_meta, c_reset);
+               else
+                       dump_quoted_path("+++ b/", elem->path, c_meta, c_reset);
                dump_sline(sline, cnt, num_parent, opt->color_diff);
        }
        free(result);
@@ -777,7 +819,7 @@ static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct re
        if (!line_termination)
                inter_name_termination = 0;
 
-       if (rev->loginfo)
+       if (rev->loginfo && !rev->no_commit_id)
                show_log(rev, opt->msg_sep);
 
        if (opt->output_format & DIFF_FORMAT_RAW) {
@@ -849,15 +891,17 @@ void diff_tree_combined(const unsigned char *sha1,
        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diffopts.recursive = 1;
 
-       show_log_first = !!rev->loginfo;
+       show_log_first = !!rev->loginfo && !rev->no_commit_id;
        needsep = 0;
        /* find set of paths that everybody touches */
        for (i = 0; i < num_parent; i++) {
                /* show stat against the first parent even
                 * when doing combined diff.
                 */
-               if (i == 0 && opt->output_format & DIFF_FORMAT_DIFFSTAT)
-                       diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
+               int stat_opt = (opt->output_format &
+                               (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT));
+               if (i == 0 && stat_opt)
+                       diffopts.output_format = stat_opt;
                else
                        diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
                diff_tree_sha1(parent[i], sha1, "", &diffopts);
@@ -887,7 +931,8 @@ void diff_tree_combined(const unsigned char *sha1,
                        }
                        needsep = 1;
                }
-               else if (opt->output_format & DIFF_FORMAT_DIFFSTAT)
+               else if (opt->output_format &
+                        (DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIFFSTAT))
                        needsep = 1;
                if (opt->output_format & DIFF_FORMAT_PATCH) {
                        if (needsep)
index 5b6e082..a6d543e 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -548,10 +548,13 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com
 
        while (parent) {
                struct commit *p = parent->item;
-               const char *hex = abbrev
-                       ? find_unique_abbrev(p->object.sha1, abbrev)
-                       : sha1_to_hex(p->object.sha1);
-               const char *dots = (abbrev && strlen(hex) != 40) ? "..." : "";
+               const char *hex = NULL;
+               const char *dots;
+               if (abbrev)
+                       hex = find_unique_abbrev(p->object.sha1, abbrev);
+               if (!hex)
+                       hex = sha1_to_hex(p->object.sha1);
+               dots = (abbrev && strlen(hex) != 40) ?  "..." : "";
                parent = parent->next;
 
                offset += sprintf(buf + offset, " %s%s", hex, dots);
index d9cb17d..b074f4f 100755 (executable)
@@ -101,6 +101,23 @@ __git_complete_file ()
        esac
 }
 
+__git_aliases ()
+{
+       git repo-config --list | grep '^alias\.' \
+               | sed -e 's/^alias\.//' -e 's/=.*$//'
+}
+
+__git_aliased_command ()
+{
+       local cmdline=$(git repo-config alias.$1)
+       for word in $cmdline; do
+               if [ "${word##-*}" ]; then
+                       echo $word
+                       return
+               fi
+       done
+}
+
 _git_branch ()
 {
        local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -264,10 +281,18 @@ _git ()
 {
        if [ $COMP_CWORD = 1 ]; then
                COMPREPLY=($(compgen \
-                       -W "--version $(git help -a|egrep '^ ')" \
+                       -W "--version $(git help -a|egrep '^ ') \
+                           $(__git_aliases)" \
                        -- "${COMP_WORDS[COMP_CWORD]}"))
        else
-               case "${COMP_WORDS[1]}" in
+               local command="${COMP_WORDS[1]}"
+               local expansion=$(__git_aliased_command "$command")
+
+               if [ "$expansion" ]; then
+                       command="$expansion"
+               fi
+
+               case "$command" in
                branch)      _git_branch ;;
                cat-file)    _git_cat_file ;;
                checkout)    _git_checkout ;;
index 68de9be..5354cd6 100644 (file)
@@ -422,8 +422,8 @@ and returns the process output as a string."
         (propertize
          (concat "   ("
                  (if (eq state 'copy) "copied from "
-                   (if (eq (git-fileinfo->state info) 'added) "renamed to "
-                     "renamed from "))
+                   (if (eq (git-fileinfo->state info) 'added) "renamed from "
+                     "renamed to "))
                  (git-escape-file-name (git-fileinfo->orig-name info))
                  ")") 'face 'git-status-face)
       "")))
index 4a8f790..8b63619 100644 (file)
 ;; system.
 ;;
 ;; To install: put this file on the load-path and add GIT to the list
-;; of supported backends in `vc-handled-backends'.
+;; of supported backends in `vc-handled-backends'; the following line,
+;; placed in your ~/.emacs, will accomplish this:
+;;
+;;     (add-to-list 'vc-handled-backends 'GIT)
 ;;
 ;; TODO
 ;;  - changelog generation
 ;;  - working with revisions other than HEAD
 ;;
 
+(eval-when-compile (require 'cl))
+
 (defvar git-commits-coding-system 'utf-8
   "Default coding system for git commits.")
 
 (defun vc-git-annotate-command (file buf &optional rev)
   ; FIXME: rev is ignored
   (let ((name (file-relative-name file)))
-    (call-process "git" nil buf nil "annotate" name)))
+    (call-process "git" nil buf nil "blame" name)))
 
 (defun vc-git-annotate-time ()
-  (and (re-search-forward "[0-9a-f]+\t(.*\t\\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\)\t[0-9]+)" nil t)
+  (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t)
        (vc-annotate-convert-time
         (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7))))))
 
index ad84928..e66bb80 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -450,6 +450,8 @@ void fill_in_extra_table_entries(struct interp *itable)
         * Replace literal host with lowercase-ized hostname.
         */
        hp = interp_table[INTERP_SLOT_HOST].value;
+       if (!hp)
+               return;
        for ( ; *hp; hp++)
                *hp = tolower(*hp);
 
@@ -544,8 +546,10 @@ static int execute(struct sockaddr *addr)
                loginfo("Extended attributes (%d bytes) exist <%.*s>",
                        (int) pktlen - len,
                        (int) pktlen - len, line + len + 1);
-       if (len && line[len-1] == '\n')
+       if (len && line[len-1] == '\n') {
                line[--len] = 0;
+               pktlen--;
+       }
 
        /*
         * Initialize the path interpolation table for this connection.
index 51df460..fa16d06 100644 (file)
@@ -308,8 +308,8 @@ create_delta(const struct delta_index *index,
                                continue;
                        if (ref_size > top - src)
                                ref_size = top - src;
-                       if (ref_size > 0xffffff)
-                               ref_size = 0xffffff;
+                       if (ref_size > 0x10000)
+                               ref_size = 0x10000;
                        if (ref_size <= msize)
                                break;
                        while (ref_size-- && *src++ == *ref)
@@ -318,8 +318,6 @@ create_delta(const struct delta_index *index,
                                /* this is our best match so far */
                                msize = ref - entry->ptr;
                                moff = entry->ptr - ref_data;
-                               if (msize >= 0x10000)
-                                       break;  /* this is good enough */
                        }
                }
 
@@ -383,8 +381,6 @@ create_delta(const struct delta_index *index,
                        if (msize & 0xff) { out[outpos++] = msize; i |= 0x10; }
                        msize >>= 8;
                        if (msize & 0xff) { out[outpos++] = msize; i |= 0x20; }
-                       msize >>= 8;
-                       if (msize & 0xff) { out[outpos++] = msize; i |= 0x40; }
 
                        *op = i;
                }
diff --git a/diff.c b/diff.c
index fb82432..3315378 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -795,6 +795,23 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
               set, total_files, adds, dels, reset);
 }
 
+static void show_numstat(struct diffstat_t* data, struct diff_options *options)
+{
+       int i;
+
+       for (i = 0; i < data->nr; i++) {
+               struct diffstat_file *file = data->files[i];
+
+               printf("%d\t%d\t", file->added, file->deleted);
+               if (options->line_termination &&
+                   quote_c_style(file->name, NULL, NULL, 0))
+                       quote_c_style(file->name, NULL, stdout, 0);
+               else
+                       fputs(file->name, stdout);
+               putchar(options->line_termination);
+       }
+}
+
 struct checkdiff_t {
        struct xdiff_emit_state xm;
        const char *filename;
@@ -1731,6 +1748,7 @@ int diff_setup_done(struct diff_options *options)
                                      DIFF_FORMAT_CHECKDIFF |
                                      DIFF_FORMAT_NO_OUTPUT))
                options->output_format &= ~(DIFF_FORMAT_RAW |
+                                           DIFF_FORMAT_NUMSTAT |
                                            DIFF_FORMAT_DIFFSTAT |
                                            DIFF_FORMAT_SUMMARY |
                                            DIFF_FORMAT_PATCH);
@@ -1740,7 +1758,9 @@ int diff_setup_done(struct diff_options *options)
         * recursive bits for other formats here.
         */
        if (options->output_format & (DIFF_FORMAT_PATCH |
+                                     DIFF_FORMAT_NUMSTAT |
                                      DIFF_FORMAT_DIFFSTAT |
+                                     DIFF_FORMAT_SUMMARY |
                                      DIFF_FORMAT_CHECKDIFF))
                options->recursive = 1;
        /*
@@ -1828,6 +1848,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--patch-with-raw")) {
                options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
        }
+       else if (!strcmp(arg, "--numstat")) {
+               options->output_format |= DIFF_FORMAT_NUMSTAT;
+       }
        else if (!strncmp(arg, "--stat", 6)) {
                char *end;
                int width = options->stat_width;
@@ -2602,7 +2625,7 @@ void diff_flush(struct diff_options *options)
                separator++;
        }
 
-       if (output_format & DIFF_FORMAT_DIFFSTAT) {
+       if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_NUMSTAT)) {
                struct diffstat_t diffstat;
 
                memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -2612,7 +2635,10 @@ void diff_flush(struct diff_options *options)
                        if (check_pair_status(p))
                                diff_flush_stat(p, options, &diffstat);
                }
-               show_stats(&diffstat, options);
+               if (output_format & DIFF_FORMAT_NUMSTAT)
+                       show_numstat(&diffstat, options);
+               if (output_format & DIFF_FORMAT_DIFFSTAT)
+                       show_stats(&diffstat, options);
                separator++;
        }
 
diff --git a/diff.h b/diff.h
index b48c991..ce3058e 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -26,20 +26,21 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 
 #define DIFF_FORMAT_RAW                0x0001
 #define DIFF_FORMAT_DIFFSTAT   0x0002
-#define DIFF_FORMAT_SUMMARY    0x0004
-#define DIFF_FORMAT_PATCH      0x0008
+#define DIFF_FORMAT_NUMSTAT    0x0004
+#define DIFF_FORMAT_SUMMARY    0x0008
+#define DIFF_FORMAT_PATCH      0x0010
 
 /* These override all above */
-#define DIFF_FORMAT_NAME       0x0010
-#define DIFF_FORMAT_NAME_STATUS        0x0020
-#define DIFF_FORMAT_CHECKDIFF  0x0040
+#define DIFF_FORMAT_NAME       0x0100
+#define DIFF_FORMAT_NAME_STATUS        0x0200
+#define DIFF_FORMAT_CHECKDIFF  0x0400
 
 /* Same as output_format = 0 but we know that -s flag was given
  * and we should not give default value to output_format.
  */
-#define DIFF_FORMAT_NO_OUTPUT  0x0080
+#define DIFF_FORMAT_NO_OUTPUT  0x0800
 
-#define DIFF_FORMAT_CALLBACK   0x0100
+#define DIFF_FORMAT_CALLBACK   0x1000
 
 struct diff_options {
        const char *filter;
@@ -170,6 +171,7 @@ extern void diffcore_std_no_resolve(struct diff_options *);
 "  --patch-with-raw\n" \
 "                output both a patch and the diff-raw format.\n" \
 "  --stat        show diffstat instead of patch.\n" \
+"  --numstat     show numeric diffstat instead of patch.\n" \
 "  --patch-with-stat\n" \
 "                output a patch and prepend its diffstat.\n" \
 "  --name-only   show only names of changed files.\n" \
index 63b1d15..84d870c 100644 (file)
@@ -20,7 +20,6 @@ int warn_ambiguous_refs = 1;
 int repository_format_version;
 char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
 int shared_repository = PERM_UMASK;
-int deny_non_fast_forwards = 0;
 const char *apply_default_whitespace;
 int zlib_compression_level = Z_DEFAULT_COMPRESSION;
 int pager_in_use;
index b632ca0..76b99af 100644 (file)
@@ -115,12 +115,10 @@ static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2])
                die("%s: unable to fork off sideband demultiplexer", me);
        if (!side_pid) {
                /* subprocess */
-               char buf[LARGE_PACKET_MAX];
-
                close(fd[0]);
                if (xd[0] != xd[1])
                        close(xd[1]);
-               if (recv_sideband(me, xd[0], fd[1], 2, buf, sizeof(buf)))
+               if (recv_sideband(me, xd[0], fd[1], 2))
                        exit(1);
                exit(0);
        }
index 99ac08b..90b7940 100644 (file)
@@ -166,12 +166,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
                }
 
                if (!fetching)
-                       packet_write(fd[1], "want %s%s%s%s%s\n",
+                       packet_write(fd[1], "want %s%s%s%s%s%s\n",
                                     sha1_to_hex(remote),
                                     (multi_ack ? " multi_ack" : ""),
                                     (use_sideband == 2 ? " side-band-64k" : ""),
                                     (use_sideband == 1 ? " side-band" : ""),
-                                    (use_thin_pack ? " thin-pack" : ""));
+                                    (use_thin_pack ? " thin-pack" : ""),
+                                    " ofs-delta");
                else
                        packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
                fetching++;
diff --git a/git-annotate.perl b/git-annotate.perl
deleted file mode 100755 (executable)
index 215ed26..0000000
+++ /dev/null
@@ -1,708 +0,0 @@
-#!/usr/bin/perl
-# Copyright 2006, Ryan Anderson <ryan@michonline.com>
-#
-# GPL v2 (See COPYING)
-#
-# This file is licensed under the GPL v2, or a later version
-# at the discretion of Linus Torvalds.
-
-use warnings;
-use strict;
-use Getopt::Long;
-use POSIX qw(strftime gmtime);
-use File::Basename qw(basename dirname);
-
-sub usage() {
-       print STDERR "Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ]
-       -l, --long
-                       Show long rev (Defaults off)
-       -t, --time
-                       Show raw timestamp (Defaults off)
-       -r, --rename
-                       Follow renames (Defaults on).
-       -S, --rev-file revs-file
-                       Use revs from revs-file instead of calling git-rev-list
-       -h, --help
-                       This message.
-";
-
-       exit(1);
-}
-
-our ($help, $longrev, $rename, $rawtime, $starting_rev, $rev_file) = (0, 0, 1);
-
-my $rc = GetOptions(   "long|l" => \$longrev,
-                       "time|t" => \$rawtime,
-                       "help|h" => \$help,
-                       "rename|r" => \$rename,
-                       "rev-file|S=s" => \$rev_file);
-if (!$rc or $help or !@ARGV) {
-       usage();
-}
-
-my $filename = shift @ARGV;
-if (@ARGV) {
-       $starting_rev = shift @ARGV;
-}
-
-my @stack = (
-       {
-               'rev' => defined $starting_rev ? $starting_rev : "HEAD",
-               'filename' => $filename,
-       },
-);
-
-our @filelines = ();
-
-if (defined $starting_rev) {
-       @filelines = git_cat_file($starting_rev, $filename);
-} else {
-       open(F,"<",$filename)
-               or die "Failed to open filename: $!";
-
-       while(<F>) {
-               chomp;
-               push @filelines, $_;
-       }
-       close(F);
-
-}
-
-our %revs;
-our @revqueue;
-our $head;
-
-my $revsprocessed = 0;
-while (my $bound = pop @stack) {
-       my @revisions = git_rev_list($bound->{'rev'}, $bound->{'filename'});
-       foreach my $revinst (@revisions) {
-               my ($rev, @parents) = @$revinst;
-               $head ||= $rev;
-
-               if (!defined($rev)) {
-                       $rev = "";
-               }
-               $revs{$rev}{'filename'} = $bound->{'filename'};
-               if (scalar @parents > 0) {
-                       $revs{$rev}{'parents'} = \@parents;
-                       next;
-               }
-
-               if (!$rename) {
-                       next;
-               }
-
-               my $newbound = find_parent_renames($rev, $bound->{'filename'});
-               if ( exists $newbound->{'filename'} && $newbound->{'filename'} ne $bound->{'filename'}) {
-                       push @stack, $newbound;
-                       $revs{$rev}{'parents'} = [$newbound->{'rev'}];
-               }
-       }
-}
-push @revqueue, $head;
-init_claim( defined $starting_rev ? $head : 'dirty');
-unless (defined $starting_rev) {
-       my $diff = open_pipe("git","diff","HEAD", "--",$filename)
-               or die "Failed to call git diff to check for dirty state: $!";
-
-       _git_diff_parse($diff, [$head], "dirty", (
-                               'author' => gitvar_name("GIT_AUTHOR_IDENT"),
-                               'author_date' => sprintf("%s +0000",time()),
-                               )
-                       );
-       close($diff);
-}
-handle_rev();
-
-
-my $i = 0;
-foreach my $l (@filelines) {
-       my ($output, $rev, $committer, $date);
-       if (ref $l eq 'ARRAY') {
-               ($output, $rev, $committer, $date) = @$l;
-               if (!$longrev && length($rev) > 8) {
-                       $rev = substr($rev,0,8);
-               }
-       } else {
-               $output = $l;
-               ($rev, $committer, $date) = ('unknown', 'unknown', 'unknown');
-       }
-
-       printf("%s\t(%10s\t%10s\t%d)%s\n", $rev, $committer,
-               format_date($date), ++$i, $output);
-}
-
-sub init_claim {
-       my ($rev) = @_;
-       for (my $i = 0; $i < @filelines; $i++) {
-               $filelines[$i] = [ $filelines[$i], '', '', '', 1];
-                       # line,
-                       # rev,
-                       # author,
-                       # date,
-                       # 1 <-- belongs to the original file.
-       }
-       $revs{$rev}{'lines'} = \@filelines;
-}
-
-
-sub handle_rev {
-       my $revseen = 0;
-       my %seen;
-       while (my $rev = shift @revqueue) {
-               next if $seen{$rev}++;
-
-               my %revinfo = git_commit_info($rev);
-
-               if (exists $revs{$rev}{parents} &&
-                   scalar @{$revs{$rev}{parents}} != 0) {
-
-                       git_diff_parse($revs{$rev}{'parents'}, $rev, %revinfo);
-                       push @revqueue, @{$revs{$rev}{'parents'}};
-
-               } else {
-                       # We must be at the initial rev here, so claim everything that is left.
-                       for (my $i = 0; $i < @{$revs{$rev}{lines}}; $i++) {
-                               if (ref ${$revs{$rev}{lines}}[$i] eq '' || ${$revs{$rev}{lines}}[$i][1] eq '') {
-                                       claim_line($i, $rev, $revs{$rev}{lines}, %revinfo);
-                               }
-                       }
-               }
-       }
-}
-
-
-sub git_rev_list {
-       my ($rev, $file) = @_;
-
-       my $revlist;
-       if ($rev_file) {
-               open($revlist, '<' . $rev_file)
-                   or die "Failed to open $rev_file : $!";
-       } else {
-               $revlist = open_pipe("git-rev-list","--parents","--remove-empty",$rev,"--",$file)
-                       or die "Failed to exec git-rev-list: $!";
-       }
-
-       my @revs;
-       while(my $line = <$revlist>) {
-               chomp $line;
-               my ($rev, @parents) = split /\s+/, $line;
-               push @revs, [ $rev, @parents ];
-       }
-       close($revlist);
-
-       printf("0 revs found for rev %s (%s)\n", $rev, $file) if (@revs == 0);
-       return @revs;
-}
-
-sub find_parent_renames {
-       my ($rev, $file) = @_;
-
-       my $patch = open_pipe("git-diff-tree", "-M50", "-r","--name-status", "-z","$rev")
-               or die "Failed to exec git-diff: $!";
-
-       local $/ = "\0";
-       my %bound;
-       my $junk = <$patch>;
-       while (my $change = <$patch>) {
-               chomp $change;
-               my $filename = <$patch>;
-               if (!defined $filename) {
-                       next;
-               }
-               chomp $filename;
-
-               if ($change =~ m/^[AMD]$/ ) {
-                       next;
-               } elsif ($change =~ m/^R/ ) {
-                       my $oldfilename = $filename;
-                       $filename = <$patch>;
-                       chomp $filename;
-                       if ( $file eq $filename ) {
-                               my $parent = git_find_parent($rev, $oldfilename);
-                               @bound{'rev','filename'} = ($parent, $oldfilename);
-                               last;
-                       }
-               }
-       }
-       close($patch);
-
-       return \%bound;
-}
-
-
-sub git_find_parent {
-       my ($rev, $filename) = @_;
-
-       my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev","--",$filename)
-               or die "Failed to open git-rev-list to find a single parent: $!";
-
-       my $parentline = <$revparent>;
-       chomp $parentline;
-       my ($revfound,$parent) = split m/\s+/, $parentline;
-
-       close($revparent);
-
-       return $parent;
-}
-
-sub git_find_all_parents {
-       my ($rev) = @_;
-
-       my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev")
-               or die "Failed to open git-rev-list to find a single parent: $!";
-
-       my $parentline = <$revparent>;
-       chomp $parentline;
-       my ($origrev, @parents) = split m/\s+/, $parentline;
-
-       close($revparent);
-
-       return @parents;
-}
-
-sub git_merge_base {
-       my ($rev1, $rev2) = @_;
-
-       my $mb = open_pipe("git-merge-base", $rev1, $rev2)
-               or die "Failed to open git-merge-base: $!";
-
-       my $base = <$mb>;
-       chomp $base;
-
-       close($mb);
-
-       return $base;
-}
-
-# Construct a set of pseudo parents that are in the same order,
-# and the same quantity as the real parents,
-# but whose SHA1s are as similar to the logical parents
-# as possible.
-sub get_pseudo_parents {
-       my ($all, $fake) = @_;
-
-       my @all = @$all;
-       my @fake = @$fake;
-
-       my @pseudo;
-
-       my %fake = map {$_ => 1} @fake;
-       my %seenfake;
-
-       my $fakeidx = 0;
-       foreach my $p (@all) {
-               if (exists $fake{$p}) {
-                       if ($fake[$fakeidx] ne $p) {
-                               die sprintf("parent mismatch: %s != %s\nall:%s\nfake:%s\n",
-                                           $fake[$fakeidx], $p,
-                                           join(", ", @all),
-                                           join(", ", @fake),
-                                          );
-                       }
-
-                       push @pseudo, $p;
-                       $fakeidx++;
-                       $seenfake{$p}++;
-
-               } else {
-                       my $base = git_merge_base($fake[$fakeidx], $p);
-                       if ($base ne $fake[$fakeidx]) {
-                               die sprintf("Result of merge-base doesn't match fake: %s,%s != %s\n",
-                                      $fake[$fakeidx], $p, $base);
-                       }
-
-                       # The details of how we parse the diffs
-                       # mean that we cannot have a duplicate
-                       # revision in the list, so if we've already
-                       # seen the revision we would normally add, just use
-                       # the actual revision.
-                       if ($seenfake{$base}) {
-                               push @pseudo, $p;
-                       } else {
-                               push @pseudo, $base;
-                               $seenfake{$base}++;
-                       }
-               }
-       }
-
-       return @pseudo;
-}
-
-
-# Get a diff between the current revision and a parent.
-# Record the commit information that results.
-sub git_diff_parse {
-       my ($parents, $rev, %revinfo) = @_;
-
-       my @pseudo_parents;
-       my @command = ("git-diff-tree");
-       my $revision_spec;
-
-       if (scalar @$parents == 1) {
-
-               $revision_spec = join("..", $parents->[0], $rev);
-               @pseudo_parents = @$parents;
-       } else {
-               my @all_parents = git_find_all_parents($rev);
-
-               if (@all_parents !=  @$parents) {
-                       @pseudo_parents = get_pseudo_parents(\@all_parents, $parents);
-               } else {
-                       @pseudo_parents = @$parents;
-               }
-
-               $revision_spec = $rev;
-               push @command, "-c";
-       }
-
-       my @filenames = ( $revs{$rev}{'filename'} );
-
-       foreach my $parent (@$parents) {
-               push @filenames, $revs{$parent}{'filename'};
-       }
-
-       push @command, "-p", "-M", $revision_spec, "--", @filenames;
-
-
-       my $diff = open_pipe( @command )
-               or die "Failed to call git-diff for annotation: $!";
-
-       _git_diff_parse($diff, \@pseudo_parents, $rev, %revinfo);
-
-       close($diff);
-}
-
-sub _git_diff_parse {
-       my ($diff, $parents, $rev, %revinfo) = @_;
-
-       my $ri = 0;
-
-       my $slines = $revs{$rev}{'lines'};
-       my (%plines, %pi);
-
-       my $gotheader = 0;
-       my ($remstart);
-       my $parent_count = @$parents;
-
-       my $diff_header_regexp = "^@";
-       $diff_header_regexp .= "@" x @$parents;
-       $diff_header_regexp .= ' -\d+,\d+' x @$parents;
-       $diff_header_regexp .= ' \+(\d+),\d+';
-       $diff_header_regexp .= " " . ("@" x @$parents);
-
-       my %claim_regexps;
-       my $allparentplus = '^' . '\\+' x @$parents . '(.*)$';
-
-       {
-               my $i = 0;
-               foreach my $parent (@$parents) {
-
-                       $pi{$parent} = 0;
-                       my $r = '^' . '.' x @$parents . '(.*)$';
-                       my $p = $r;
-                       substr($p,$i+1, 1) = '\\+';
-
-                       my $m = $r;
-                       substr($m,$i+1, 1) = '-';
-
-                       $claim_regexps{$parent}{plus} = $p;
-                       $claim_regexps{$parent}{minus} = $m;
-
-                       $plines{$parent} = [];
-
-                       $i++;
-               }
-       }
-
-       DIFF:
-       while(<$diff>) {
-               chomp;
-               #printf("%d:%s:\n", $gotheader, $_);
-               if (m/$diff_header_regexp/) {
-                       $remstart = $1 - 1;
-                       # (0-based arrays)
-
-                       $gotheader = 1;
-
-                       foreach my $parent (@$parents) {
-                               for (my $i = $ri; $i < $remstart; $i++) {
-                                       $plines{$parent}[$pi{$parent}++] = $slines->[$i];
-                               }
-                       }
-                       $ri = $remstart;
-
-                       next DIFF;
-
-               } elsif (!$gotheader) {
-                       # Skip over the leadin.
-                       next DIFF;
-               }
-
-               if (m/^\\/) {
-                       ;
-                       # Skip \No newline at end of file.
-                       # But this can be internationalized, so only look
-                       # for an initial \
-
-               } else {
-                       my %claims = ();
-                       my $negclaim = 0;
-                       my $allclaimed = 0;
-                       my $line;
-
-                       if (m/$allparentplus/) {
-                               claim_line($ri, $rev, $slines, %revinfo);
-                               $allclaimed = 1;
-
-                       }
-
-                       PARENT:
-                       foreach my $parent (keys %claim_regexps) {
-                               my $m = $claim_regexps{$parent}{minus};
-                               my $p = $claim_regexps{$parent}{plus};
-
-                               if (m/$m/) {
-                                       $line = $1;
-                                       $plines{$parent}[$pi{$parent}++] = [ $line, '', '', '', 0 ];
-                                       $negclaim++;
-
-                               } elsif (m/$p/) {
-                                       $line = $1;
-                                       if (get_line($slines, $ri) eq $line) {
-                                               # Found a match, claim
-                                               $claims{$parent}++;
-
-                                       } else {
-                                               die sprintf("Sync error: %d\n|%s\n|%s\n%s => %s\n",
-                                                               $ri, $line,
-                                                               get_line($slines, $ri),
-                                                               $rev, $parent);
-                                       }
-                               }
-                       }
-
-                       if (%claims) {
-                               foreach my $parent (@$parents) {
-                                       next if $claims{$parent} || $allclaimed;
-                                       $plines{$parent}[$pi{$parent}++] = $slines->[$ri];
-                                           #[ $line, '', '', '', 0 ];
-                               }
-                               $ri++;
-
-                       } elsif ($negclaim) {
-                               next DIFF;
-
-                       } else {
-                               if (substr($_,scalar @$parents) ne get_line($slines,$ri) ) {
-                                       foreach my $parent (@$parents) {
-                                               printf("parent %s is on line %d\n", $parent, $pi{$parent});
-                                       }
-
-                                       my @context;
-                                       for (my $i = -2; $i < 2; $i++) {
-                                               push @context, get_line($slines, $ri + $i);
-                                       }
-                                       my $context = join("\n", @context);
-
-                                       my $justline = substr($_, scalar @$parents);
-                                       die sprintf("Line %d, does not match:\n|%s|\n|%s|\n%s\n",
-                                                   $ri,
-                                                   $justline,
-                                                   $context);
-                               }
-                               foreach my $parent (@$parents) {
-                                       $plines{$parent}[$pi{$parent}++] = $slines->[$ri];
-                               }
-                               $ri++;
-                       }
-               }
-       }
-
-       for (my $i = $ri; $i < @{$slines} ; $i++) {
-               foreach my $parent (@$parents) {
-                       push @{$plines{$parent}}, $slines->[$ri];
-               }
-               $ri++;
-       }
-
-       foreach my $parent (@$parents) {
-               $revs{$parent}{lines} = $plines{$parent};
-       }
-
-       return;
-}
-
-sub get_line {
-       my ($lines, $index) = @_;
-
-       return ref $lines->[$index] ne '' ? $lines->[$index][0] : $lines->[$index];
-}
-
-sub git_cat_file {
-       my ($rev, $filename) = @_;
-       return () unless defined $rev && defined $filename;
-
-       my $blob = git_ls_tree($rev, $filename);
-       die "Failed to find a blob for $filename in rev $rev\n" if !defined $blob;
-
-       my $catfile = open_pipe("git","cat-file", "blob", $blob)
-               or die "Failed to git-cat-file blob $blob (rev $rev, file $filename): " . $!;
-
-       my @lines;
-       while(<$catfile>) {
-               chomp;
-               push @lines, $_;
-       }
-       close($catfile);
-
-       return @lines;
-}
-
-sub git_ls_tree {
-       my ($rev, $filename) = @_;
-
-       my $lstree = open_pipe("git","ls-tree",$rev,$filename)
-               or die "Failed to call git ls-tree: $!";
-
-       my ($mode, $type, $blob, $tfilename);
-       while(<$lstree>) {
-               chomp;
-               ($mode, $type, $blob, $tfilename) = split(/\s+/, $_, 4);
-               last if ($tfilename eq $filename);
-       }
-       close($lstree);
-
-       return $blob if ($tfilename eq $filename);
-       die "git-ls-tree failed to find blob for $filename";
-
-}
-
-
-
-sub claim_line {
-       my ($floffset, $rev, $lines, %revinfo) = @_;
-       my $oline = get_line($lines, $floffset);
-       @{$lines->[$floffset]} = ( $oline, $rev,
-               $revinfo{'author'}, $revinfo{'author_date'} );
-       #printf("Claiming line %d with rev %s: '%s'\n",
-       #               $floffset, $rev, $oline) if 1;
-}
-
-sub git_commit_info {
-       my ($rev) = @_;
-       my $commit = open_pipe("git-cat-file", "commit", $rev)
-               or die "Failed to call git-cat-file: $!";
-
-       my %info;
-       while(<$commit>) {
-               chomp;
-               last if (length $_ == 0);
-
-               if (m/^author (.*) <(.*)> (.*)$/) {
-                       $info{'author'} = $1;
-                       $info{'author_email'} = $2;
-                       $info{'author_date'} = $3;
-               } elsif (m/^committer (.*) <(.*)> (.*)$/) {
-                       $info{'committer'} = $1;
-                       $info{'committer_email'} = $2;
-                       $info{'committer_date'} = $3;
-               }
-       }
-       close($commit);
-
-       return %info;
-}
-
-sub format_date {
-       if ($rawtime) {
-               return $_[0];
-       }
-       my ($timestamp, $timezone) = split(' ', $_[0]);
-       my $minutes = abs($timezone);
-       $minutes = int($minutes / 100) * 60 + ($minutes % 100);
-       if ($timezone < 0) {
-           $minutes = -$minutes;
-       }
-       my $t = $timestamp + $minutes * 60;
-       return strftime("%Y-%m-%d %H:%M:%S " . $timezone, gmtime($t));
-}
-
-# Copied from git-send-email.perl - We need a Git.pm module..
-sub gitvar {
-    my ($var) = @_;
-    my $fh;
-    my $pid = open($fh, '-|');
-    die "$!" unless defined $pid;
-    if (!$pid) {
-       exec('git-var', $var) or die "$!";
-    }
-    my ($val) = <$fh>;
-    close $fh or die "$!";
-    chomp($val);
-    return $val;
-}
-
-sub gitvar_name {
-    my ($name) = @_;
-    my $val = gitvar($name);
-    my @field = split(/\s+/, $val);
-    return join(' ', @field[0...(@field-4)]);
-}
-
-sub open_pipe {
-       if ($^O eq '##INSERT_ACTIVESTATE_STRING_HERE##') {
-               return open_pipe_activestate(@_);
-       } else {
-               return open_pipe_normal(@_);
-       }
-}
-
-sub open_pipe_activestate {
-       tie *fh, "Git::ActiveStatePipe", @_;
-       return *fh;
-}
-
-sub open_pipe_normal {
-       my (@execlist) = @_;
-
-       my $pid = open my $kid, "-|";
-       defined $pid or die "Cannot fork: $!";
-
-       unless ($pid) {
-               exec @execlist;
-               die "Cannot exec @execlist: $!";
-       }
-
-       return $kid;
-}
-
-package Git::ActiveStatePipe;
-use strict;
-
-sub TIEHANDLE {
-       my ($class, @params) = @_;
-       my $cmdline = join " ", @params;
-       my  @data = qx{$cmdline};
-       bless { i => 0, data => \@data }, $class;
-}
-
-sub READLINE {
-       my $self = shift;
-       if ($self->{i} >= scalar @{$self->{data}}) {
-               return undef;
-       }
-       return $self->{'data'}->[ $self->{i}++ ];
-}
-
-sub CLOSE {
-       my $self = shift;
-       delete $self->{data};
-       delete $self->{i};
-}
-
-sub EOF {
-       my $self = shift;
-       return ($self->{i} >= scalar @{$self->{data}});
-}
index 06a8d26..6da31e8 100755 (executable)
@@ -179,11 +179,12 @@ bisect_reset() {
         *)
            usage ;;
        esac
-       git checkout "$branch" &&
-       rm -fr "$GIT_DIR/refs/bisect"
-       rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
-       rm -f "$GIT_DIR/BISECT_LOG"
-       rm -f "$GIT_DIR/BISECT_NAMES"
+       if git checkout "$branch"; then
+               rm -fr "$GIT_DIR/refs/bisect"
+               rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
+               rm -f "$GIT_DIR/BISECT_LOG"
+               rm -f "$GIT_DIR/BISECT_NAMES"
+       fi
 }
 
 bisect_replay () {
index 8832573..cf7af55 100755 (executable)
@@ -12,8 +12,8 @@ LONG_USAGE='             __*__*__*__*__> <upstream>
 Each commit between the fork-point (or <limit> if given) and <head> is
 examined, and compared against the change each commit between the
 fork-point and <upstream> introduces.  If the change seems to be in
-the upstream, it is shown on the standard output with prefix "+".
-Otherwise it is shown with prefix "-".'
+the upstream, it is shown on the standard output with prefix "-".
+Otherwise it is shown with prefix "+".'
 . git-sh-setup
 
 case "$1" in -v) verbose=t; shift ;; esac 
index 3998c55..3f006d1 100755 (executable)
@@ -8,11 +8,15 @@
 # See git-sh-setup why.
 unset CDPATH
 
-usage() {
-       echo >&2 "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+die() {
+       echo >&2 "$@"
        exit 1
 }
 
+usage() {
+       die "Usage: $0 [--template=<template_directory>] [--use-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+}
+
 get_repo_base() {
        (cd "$1" && (cd .git ; pwd)) 2> /dev/null
 }
@@ -35,11 +39,9 @@ clone_dumb_http () {
                "`git-repo-config --bool http.noEPSV`" = true ]; then
                curl_extra_args="${curl_extra_args} --disable-epsv"
        fi
-       http_fetch "$1/info/refs" "$clone_tmp/refs" || {
-               echo >&2 "Cannot get remote repository information.
+       http_fetch "$1/info/refs" "$clone_tmp/refs" ||
+               die "Cannot get remote repository information.
 Perhaps git-update-server-info needs to be run there?"
-               exit 1;
-       }
        while read sha1 refname
        do
                name=`expr "z$refname" : 'zrefs/\(.*\)'` &&
@@ -143,17 +145,12 @@ while
                '')
                    usage ;;
                */*)
-                   echo >&2 "'$2' is not suitable for an origin name"
-                   exit 1
+                   die "'$2' is not suitable for an origin name"
                esac
-               git-check-ref-format "heads/$2" || {
-                   echo >&2 "'$2' is not suitable for a branch name"
-                   exit 1
-               }
-               test -z "$origin_override" || {
-                   echo >&2 "Do not give more than one --origin options."
-                   exit 1
-               }
+               git-check-ref-format "heads/$2" ||
+                   die "'$2' is not suitable for a branch name"
+               test -z "$origin_override" ||
+                   die "Do not give more than one --origin options."
                origin_override=yes
                origin="$2"; shift
                ;;
@@ -169,24 +166,19 @@ do
 done
 
 repo="$1"
-if test -z "$repo"
-then
-    echo >&2 'you must specify a repository to clone.'
-    exit 1
-fi
+test -n "$repo" ||
+    die 'you must specify a repository to clone.'
 
 # --bare implies --no-checkout
 if test yes = "$bare"
 then
        if test yes = "$origin_override"
        then
-               echo >&2 '--bare and --origin $origin options are incompatible.'
-               exit 1
+               die '--bare and --origin $origin options are incompatible.'
        fi
        if test t = "$use_separate_remote"
        then
-               echo >&2 '--bare and --use-separate-remote options are incompatible.'
-               exit 1
+               die '--bare and --use-separate-remote options are incompatible.'
        fi
        no_checkout=yes
 fi
@@ -206,7 +198,7 @@ fi
 dir="$2"
 # Try using "humanish" part of source repo if user didn't specify one
 [ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
-[ -e "$dir" ] && echo "$dir already exists." && usage
+[ -e "$dir" ] && die "destination directory '$dir' already exists."
 mkdir -p "$dir" &&
 D=$(cd "$dir" && pwd) &&
 trap 'err=$?; cd ..; rm -rf "$D"; exit $err' 0
@@ -233,7 +225,7 @@ then
                 cd reference-tmp &&
                 tar xf -)
        else
-               echo >&2 "$reference: not a local directory." && usage
+               die "reference repository '$reference' is not a local directory."
        fi
 fi
 
@@ -242,10 +234,8 @@ rm -f "$GIT_DIR/CLONE_HEAD"
 # We do local magic only when the user tells us to.
 case "$local,$use_local" in
 yes,yes)
-       ( cd "$repo/objects" ) || {
-               echo >&2 "-l flag seen but $repo is not local."
-               exit 1
-       }
+       ( cd "$repo/objects" ) ||
+               die "-l flag seen but repository '$repo' is not local."
 
        case "$local_shared" in
        no)
@@ -307,18 +297,15 @@ yes,yes)
                then
                        clone_dumb_http "$repo" "$D"
                else
-                       echo >&2 "http transport not supported, rebuild Git with curl support"
-                       exit 1
+                       die "http transport not supported, rebuild Git with curl support"
                fi
                ;;
        *)
-               cd "$D" && case "$upload_pack" in
+               case "$upload_pack" in
                '') git-fetch-pack --all -k $quiet "$repo" ;;
                *) git-fetch-pack --all -k $quiet "$upload_pack" "$repo" ;;
-               esac >"$GIT_DIR/CLONE_HEAD" || {
-                       echo >&2 "fetch-pack from '$repo' failed."
-                       exit 1
-               }
+               esac >"$GIT_DIR/CLONE_HEAD" ||
+                       die "fetch-pack from '$repo' failed."
                ;;
        esac
        ;;
@@ -414,7 +401,8 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
 
        case "$no_checkout" in
        '')
-               git-read-tree -m -u -v HEAD HEAD
+               test "z$quiet" = z && v=-v || v=
+               git-read-tree -m -u $v HEAD HEAD
        esac
 fi
 rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD"
index 8ac8dcc..81c3a0c 100755 (executable)
@@ -32,33 +32,6 @@ save_index () {
        cp -p "$THIS_INDEX" "$NEXT_INDEX"
 }
 
-report () {
-  header="#
-# $1:
-#   ($2)
-#
-"
-  trailer=""
-  while read status name newname
-  do
-    printf '%s' "$header"
-    header=""
-    trailer="#
-"
-    case "$status" in
-    M ) echo "#        modified: $name";;
-    D*) echo "#        deleted:  $name";;
-    T ) echo "#        typechange: $name";;
-    C*) echo "#        copied: $name -> $newname";;
-    R*) echo "#        renamed: $name -> $newname";;
-    A*) echo "#        new file: $name";;
-    U ) echo "#        unmerged: $name";;
-    esac
-  done
-  printf '%s' "$trailer"
-  [ "$header" ]
-}
-
 run_status () {
        # If TMP_INDEX is defined, that means we are doing
        # "--only" partial commit, and that index file is used
@@ -68,21 +41,21 @@ run_status () {
        # so the regular index file is what we use to compare.
        if test '' != "$TMP_INDEX"
        then
-           GIT_INDEX_FILE="$TMP_INDEX"
-           export GIT_INDEX_FILE
+               GIT_INDEX_FILE="$TMP_INDEX"
+               export GIT_INDEX_FILE
        elif test -f "$NEXT_INDEX"
        then
-           GIT_INDEX_FILE="$NEXT_INDEX"
-           export GIT_INDEX_FILE
+               GIT_INDEX_FILE="$NEXT_INDEX"
+               export GIT_INDEX_FILE
        fi
 
-  case "$status_only" in
-    t) color= ;;
-    *) color=--nocolor ;;
-  esac
-  git-runstatus ${color} \
-                ${verbose:+--verbose} \
-                ${amend:+--amend} \
+       case "$status_only" in
+       t) color= ;;
+       *) color=--nocolor ;;
+       esac
+       git-runstatus ${color} \
+               ${verbose:+--verbose} \
+               ${amend:+--amend} \
                ${untracked_files:+--untracked}
 }
 
@@ -114,179 +87,181 @@ only_include_assumed=
 untracked_files=
 while case "$#" in 0) break;; esac
 do
-  case "$1" in
-  -F|--F|-f|--f|--fi|--fil|--file)
-      case "$#" in 1) usage ;; esac
-      shift
-      no_edit=t
-      log_given=t$log_given
-      logfile="$1"
-      shift
-      ;;
-  -F*|-f*)
-      no_edit=t
-      log_given=t$log_given
-      logfile=`expr "z$1" : 'z-[Ff]\(.*\)'`
-      shift
-      ;;
-  --F=*|--f=*|--fi=*|--fil=*|--file=*)
-      no_edit=t
-      log_given=t$log_given
-      logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-      shift
-      ;;
-  -a|--a|--al|--all)
-      all=t
-      shift
-      ;;
-  --au=*|--aut=*|--auth=*|--autho=*|--author=*)
-      force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-      shift
-      ;;
-  --au|--aut|--auth|--autho|--author)
-      case "$#" in 1) usage ;; esac
-      shift
-      force_author="$1"
-      shift
-      ;;
-  -e|--e|--ed|--edi|--edit)
-      edit_flag=t
-      shift
-      ;;
-  -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
-      also=t
-      shift
-      ;;
-  -o|--o|--on|--onl|--only)
-      only=t
-      shift
-      ;;
-  -m|--m|--me|--mes|--mess|--messa|--messag|--message)
-      case "$#" in 1) usage ;; esac
-      shift
-      log_given=m$log_given
-      if test "$log_message" = ''
-      then
-          log_message="$1"
-      else
-          log_message="$log_message
+       case "$1" in
+       -F|--F|-f|--f|--fi|--fil|--file)
+               case "$#" in 1) usage ;; esac
+               shift
+               no_edit=t
+               log_given=t$log_given
+               logfile="$1"
+               shift
+               ;;
+       -F*|-f*)
+               no_edit=t
+               log_given=t$log_given
+               logfile=`expr "z$1" : 'z-[Ff]\(.*\)'`
+               shift
+               ;;
+       --F=*|--f=*|--fi=*|--fil=*|--file=*)
+               no_edit=t
+               log_given=t$log_given
+               logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+               shift
+               ;;
+       -a|--a|--al|--all)
+               all=t
+               shift
+               ;;
+       --au=*|--aut=*|--auth=*|--autho=*|--author=*)
+               force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+               shift
+               ;;
+       --au|--aut|--auth|--autho|--author)
+               case "$#" in 1) usage ;; esac
+               shift
+               force_author="$1"
+               shift
+               ;;
+       -e|--e|--ed|--edi|--edit)
+               edit_flag=t
+               shift
+               ;;
+       -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
+               also=t
+               shift
+               ;;
+       -o|--o|--on|--onl|--only)
+               only=t
+               shift
+               ;;
+       -m|--m|--me|--mes|--mess|--messa|--messag|--message)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=m$log_given
+               if test "$log_message" = ''
+               then
+                   log_message="$1"
+               else
+                   log_message="$log_message
 
 $1"
-      fi
-      no_edit=t
-      shift
-      ;;
-  -m*)
-      log_given=m$log_given
-      if test "$log_message" = ''
-      then
-          log_message=`expr "z$1" : 'z-m\(.*\)'`
-      else
-          log_message="$log_message
+               fi
+               no_edit=t
+               shift
+               ;;
+       -m*)
+               log_given=m$log_given
+               if test "$log_message" = ''
+               then
+                   log_message=`expr "z$1" : 'z-m\(.*\)'`
+               else
+                   log_message="$log_message
 
 `expr "z$1" : 'z-m\(.*\)'`"
-      fi
-      no_edit=t
-      shift
-      ;;
-  --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
-      log_given=m$log_given
-      if test "$log_message" = ''
-      then
-          log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-      else
-          log_message="$log_message
+               fi
+               no_edit=t
+               shift
+               ;;
+       --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
+               log_given=m$log_given
+               if test "$log_message" = ''
+               then
+                   log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+               else
+                   log_message="$log_message
 
 `expr "z$1" : 'zq-[^=]*=\(.*\)'`"
-      fi
-      no_edit=t
-      shift
-      ;;
-  -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|--no-verify)
-      verify=
-      shift
-      ;;
-  --a|--am|--ame|--amen|--amend)
-      amend=t
-      log_given=t$log_given
-      use_commit=HEAD
-      shift
-      ;;
-  -c)
-      case "$#" in 1) usage ;; esac
-      shift
-      log_given=t$log_given
-      use_commit="$1"
-      no_edit=
-      shift
-      ;;
-  --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
-  --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
-  --reedit-messag=*|--reedit-message=*)
-      log_given=t$log_given
-      use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-      no_edit=
-      shift
-      ;;
-  --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
-  --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|--reedit-message)
-      case "$#" in 1) usage ;; esac
-      shift
-      log_given=t$log_given
-      use_commit="$1"
-      no_edit=
-      shift
-      ;;
-  -C)
-      case "$#" in 1) usage ;; esac
-      shift
-      log_given=t$log_given
-      use_commit="$1"
-      no_edit=t
-      shift
-      ;;
-  --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
-  --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
-  --reuse-message=*)
-      log_given=t$log_given
-      use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-      no_edit=t
-      shift
-      ;;
-  --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
-  --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
-      case "$#" in 1) usage ;; esac
-      shift
-      log_given=t$log_given
-      use_commit="$1"
-      no_edit=t
-      shift
-      ;;
-  -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
-      signoff=t
-      shift
-      ;;
-  -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
-      verbose=t
-      shift
-      ;;
-  -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|--untracked|\
-  --untracked-|--untracked-f|--untracked-fi|--untracked-fil|--untracked-file|\
-  --untracked-files)
-      untracked_files=t
-      shift
-      ;;
-  --)
-      shift
-      break
-      ;;
-  -*)
-      usage
-      ;;
-  *)
-      break
-      ;;
-  esac
+               fi
+               no_edit=t
+               shift
+               ;;
+       -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
+       --no-verify)
+               verify=
+               shift
+               ;;
+       --a|--am|--ame|--amen|--amend)
+               amend=t
+               log_given=t$log_given
+               use_commit=HEAD
+               shift
+               ;;
+       -c)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=t$log_given
+               use_commit="$1"
+               no_edit=
+               shift
+               ;;
+       --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
+       --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
+       --reedit-messag=*|--reedit-message=*)
+               log_given=t$log_given
+               use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+               no_edit=
+               shift
+               ;;
+       --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
+       --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
+       --reedit-message)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=t$log_given
+               use_commit="$1"
+               no_edit=
+               shift
+               ;;
+       -C)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=t$log_given
+               use_commit="$1"
+               no_edit=t
+               shift
+               ;;
+       --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
+       --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
+       --reuse-message=*)
+               log_given=t$log_given
+               use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+               no_edit=t
+               shift
+               ;;
+       --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
+       --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
+               case "$#" in 1) usage ;; esac
+               shift
+               log_given=t$log_given
+               use_commit="$1"
+               no_edit=t
+               shift
+               ;;
+       -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
+               signoff=t
+               shift
+               ;;
+       -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+               verbose=t
+               shift
+               ;;
+       -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
+       --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
+       --untracked-file|--untracked-files)
+               untracked_files=t
+               shift
+               ;;
+       --)
+               shift
+               break
+               ;;
+       -*)
+               usage
+               ;;
+       *)
+               break
+               ;;
+       esac
 done
 case "$edit_flag" in t) no_edit= ;; esac
 
@@ -295,33 +270,33 @@ case "$edit_flag" in t) no_edit= ;; esac
 
 case "$amend,$initial_commit" in
 t,t)
-  die "You do not have anything to amend." ;;
+       die "You do not have anything to amend." ;;
 t,)
-  if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
-    die "You are in the middle of a merge -- cannot amend."
-  fi ;;
+       if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
+               die "You are in the middle of a merge -- cannot amend."
+       fi ;;
 esac
 
 case "$log_given" in
 tt*)
-  die "Only one of -c/-C/-F can be used." ;;
+       die "Only one of -c/-C/-F can be used." ;;
 *tm*|*mt*)
-  die "Option -m cannot be combined with -c/-C/-F." ;;
+       die "Option -m cannot be combined with -c/-C/-F." ;;
 esac
 
 case "$#,$also,$only,$amend" in
 *,t,t,*)
-  die "Only one of --include/--only can be used." ;;
+       die "Only one of --include/--only can be used." ;;
 0,t,,* | 0,,t,)
-  die "No paths with --include/--only does not make sense." ;;
+       die "No paths with --include/--only does not make sense." ;;
 0,,t,t)
-  only_include_assumed="# Clever... amending the last one with dirty index." ;;
+       only_include_assumed="# Clever... amending the last one with dirty index." ;;
 0,,,*)
-  ;;
+       ;;
 *,,,*)
-  only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
-  also=
-  ;;
+       only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
+       also=
+       ;;
 esac
 unset only
 case "$all,$also,$#" in
@@ -368,47 +343,47 @@ t,)
 ,)
        case "$#" in
        0)
-           ;; # commit as-is
+               ;; # commit as-is
        *)
-           if test -f "$GIT_DIR/MERGE_HEAD"
-           then
-               refuse_partial "Cannot do a partial commit during a merge."
-           fi
-           TMP_INDEX="$GIT_DIR/tmp-index$$"
-           if test -z "$initial_commit"
-           then
-               # make sure index is clean at the specified paths, or
-               # they are additions.
-               dirty_in_index=`git-diff-index --cached --name-status \
-                       --diff-filter=DMTU HEAD -- "$@"`
-               test -z "$dirty_in_index" ||
-               refuse_partial "Different in index and the last commit:
+               if test -f "$GIT_DIR/MERGE_HEAD"
+               then
+                       refuse_partial "Cannot do a partial commit during a merge."
+               fi
+               TMP_INDEX="$GIT_DIR/tmp-index$$"
+               if test -z "$initial_commit"
+               then
+                       # make sure index is clean at the specified paths, or
+                       # they are additions.
+                       dirty_in_index=`git-diff-index --cached --name-status \
+                               --diff-filter=DMTU HEAD -- "$@"`
+                       test -z "$dirty_in_index" ||
+                       refuse_partial "Different in index and the last commit:
 $dirty_in_index"
-           fi
-           commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
-
-           # Build the temporary index and update the real index
-           # the same way.
-           if test -z "$initial_commit"
-           then
-               cp "$THIS_INDEX" "$TMP_INDEX"
-               GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
-           else
-                   rm -f "$TMP_INDEX"
-           fi || exit
-
-           echo "$commit_only" |
-           GIT_INDEX_FILE="$TMP_INDEX" \
-           git-update-index --add --remove --stdin &&
-
-           save_index &&
-           echo "$commit_only" |
-           (
-               GIT_INDEX_FILE="$NEXT_INDEX"
-               export GIT_INDEX_FILE
-               git-update-index --remove --stdin
-           ) || exit
-           ;;
+               fi
+               commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
+
+               # Build the temporary index and update the real index
+               # the same way.
+               if test -z "$initial_commit"
+               then
+                       cp "$THIS_INDEX" "$TMP_INDEX"
+                       GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
+               else
+                       rm -f "$TMP_INDEX"
+               fi || exit
+
+               echo "$commit_only" |
+               GIT_INDEX_FILE="$TMP_INDEX" \
+               git-update-index --add --remove --stdin &&
+
+               save_index &&
+               echo "$commit_only" |
+               (
+                       GIT_INDEX_FILE="$NEXT_INDEX"
+                       export GIT_INDEX_FILE
+                       git-update-index --remove --stdin
+               ) || exit
+               ;;
        esac
        ;;
 esac
@@ -426,7 +401,7 @@ else
 fi
 
 GIT_INDEX_FILE="$USE_INDEX" \
-    git-update-index -q $unmerged_ok_if_status --refresh || exit
+       git-update-index -q $unmerged_ok_if_status --refresh || exit
 
 ################################################################
 # If the request is status, just show it and exit.
index e5a00a1..14e2c61 100755 (executable)
@@ -495,22 +495,17 @@ unless(-d $git_dir) {
        $tip_at_start = `git-rev-parse --verify HEAD`;
 
        # Get the last import timestamps
-       opendir(D,"$git_dir/refs/heads");
-       while(defined(my $head = readdir(D))) {
-               next if $head =~ /^\./;
-               open(F,"$git_dir/refs/heads/$head")
-                       or die "Bad head branch: $head: $!\n";
-               chomp(my $ftag = <F>);
-               close(F);
-               open(F,"git-cat-file commit $ftag |");
-               while(<F>) {
-                       next unless /^author\s.*\s(\d+)\s[-+]\d{4}$/;
-                       $branch_date{$head} = $1;
-                       last;
-               }
-               close(F);
+       my $fmt = '($ref, $author) = (%(refname), %(author));';
+       open(H, "git-for-each-ref --perl --format='$fmt' refs/heads |") or
+               die "Cannot run git-for-each-ref: $!\n";
+       while(defined(my $entry = <H>)) {
+               my ($ref, $author);
+               eval($entry) || die "cannot eval refs list: $@";
+               my ($head) = ($ref =~ m|^refs/heads/(.*)|);
+               $author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
+               $branch_date{$head} = $1;
        }
-       closedir(D);
+       close(H);
 }
 
 -d $git_dir
index 2130d57..8817f8b 100755 (executable)
@@ -275,7 +275,7 @@ sub req_Directory
     $state->{directory} = "" if ( $state->{directory} eq "." );
     $state->{directory} .= "/" if ( $state->{directory} =~ /\S/ );
 
-    if ( not defined($state->{prependdir}) and $state->{localdir} eq "." and $state->{path} =~ /\S/ )
+    if ( (not defined($state->{prependdir}) or $state->{prependdir} eq '') and $state->{localdir} eq "." and $state->{path} =~ /\S/ )
     {
         $log->info("Setting prepend to '$state->{path}'");
         $state->{prependdir} = $state->{path};
@@ -805,7 +805,14 @@ sub req_update
             $meta = $updater->getmeta($filename);
         }
 
-        next unless ( $meta->{revision} );
+       if ( ! defined $meta )
+       {
+           $meta = {
+               name => $filename,
+               revision => 0,
+               filehash => 'added'
+           };
+       }
 
         my $oldmeta = $meta;
 
@@ -835,7 +842,7 @@ sub req_update
              and not exists ( $state->{opt}{C} ) )
         {
             $log->info("Tell the client the file is modified");
-            print "MT text U\n";
+            print "MT text \n";
             print "MT fname $filename\n";
             print "MT newline\n";
             next;
@@ -855,15 +862,36 @@ sub req_update
            }
         }
         elsif ( not defined ( $state->{entries}{$filename}{modified_hash} )
-               or $state->{entries}{$filename}{modified_hash} eq $oldmeta->{filehash} )
+               or $state->{entries}{$filename}{modified_hash} eq $oldmeta->{filehash}
+               or $meta->{filehash} eq 'added' )
         {
-            $log->info("Updating '$filename'");
-            # normal update, just send the new revision (either U=Update, or A=Add, or R=Remove)
-            print "MT +updated\n";
-            print "MT text U\n";
-            print "MT fname $filename\n";
-            print "MT newline\n";
-            print "MT -updated\n";
+            # normal update, just send the new revision (either U=Update,
+            # or A=Add, or R=Remove)
+           if ( defined($wrev) && $wrev < 0 )
+           {
+               $log->info("Tell the client the file is scheduled for removal");
+               print "MT text R \n";
+                print "MT fname $filename\n";
+                print "MT newline\n";
+               next;
+           }
+           elsif ( !defined($wrev) || $wrev == 0 )
+           {
+               $log->info("Tell the client the file will be added");
+               print "MT text A \n";
+                print "MT fname $filename\n";
+                print "MT newline\n";
+               next;
+
+           }
+           else {
+                $log->info("Updating '$filename' $wrev");
+                print "MT +updated\n";
+                print "MT text U \n";
+                print "MT fname $filename\n";
+                print "MT newline\n";
+               print "MT -updated\n";
+           }
 
             my ( $filepart, $dirpart ) = filenamesplit($filename,1);
 
@@ -1709,6 +1737,17 @@ sub argsfromdir
 
     return if ( scalar ( @{$state->{args}} ) > 1 );
 
+    my @gethead = @{$updater->gethead};
+
+    # push added files
+    foreach my $file (keys %{$state->{entries}}) {
+       if ( exists $state->{entries}{$file}{revision} &&
+               $state->{entries}{$file}{revision} == 0 )
+       {
+           push @gethead, { name => $file, filehash => 'added' };
+       }
+    }
+
     if ( scalar(@{$state->{args}}) == 1 )
     {
         my $arg = $state->{args}[0];
@@ -1716,7 +1755,7 @@ sub argsfromdir
 
         $log->info("Only one arg specified, checking for directory expansion on '$arg'");
 
-        foreach my $file ( @{$updater->gethead} )
+        foreach my $file ( @gethead )
         {
             next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
             next unless ( $file->{name} =~ /^$arg\// or $file->{name} eq $arg  );
@@ -1729,7 +1768,7 @@ sub argsfromdir
 
         $state->{args} = [];
 
-        foreach my $file ( @{$updater->gethead} )
+        foreach my $file ( @gethead )
         {
             next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
             next unless ( $file->{name} =~ s/^$state->{prependdir}// );
@@ -2079,9 +2118,17 @@ sub new
                 mode       TEXT NOT NULL
             )
         ");
+        $self->{dbh}->do("
+            CREATE INDEX revision_ix1
+            ON revision (name,revision)
+        ");
+        $self->{dbh}->do("
+            CREATE INDEX revision_ix2
+            ON revision (name,commithash)
+        ");
     }
 
-    # Construct the revision table if required
+    # Construct the head table if required
     unless ( $self->{tables}{head} )
     {
         $self->{dbh}->do("
@@ -2095,6 +2142,10 @@ sub new
                 mode       TEXT NOT NULL
             )
         ");
+        $self->{dbh}->do("
+            CREATE INDEX head_ix1
+            ON head (name)
+        ");
     }
 
     # Construct the properties table if required
index e8a7668..fa73ad2 100755 (executable)
@@ -129,22 +129,25 @@ append_fetch_head () {
     then
        headc_=$(git-rev-parse --verify "$head_^0") || exit
        echo "$headc_   $not_for_merge_ $note_" >>"$GIT_DIR/FETCH_HEAD"
-       [ "$verbose" ] && echo >&2 "* committish: $head_"
-       [ "$verbose" ] && echo >&2 "  $note_"
     else
        echo "$head_    not-for-merge   $note_" >>"$GIT_DIR/FETCH_HEAD"
-       [ "$verbose" ] && echo >&2 "* non-commit: $head_"
-       [ "$verbose" ] && echo >&2 "  $note_"
-    fi
-    if test "$local_name_" != ""
-    then
-       # We are storing the head locally.  Make sure that it is
-       # a fast forward (aka "reverse push").
-       fast_forward_local "$local_name_" "$head_" "$note_"
     fi
+
+    update_local_ref "$local_name_" "$head_" "$note_"
 }
 
-fast_forward_local () {
+update_local_ref () {
+    # If we are storing the head locally make sure that it is
+    # a fast forward (aka "reverse push").
+
+    label_=$(git-cat-file -t $2)
+    newshort_=$(git-rev-parse --short $2)
+    if test -z "$1" ; then
+       [ "$verbose" ] && echo >&2 "* fetched $3"
+       [ "$verbose" ] && echo >&2 "  $label_: $newshort_"
+       return 0
+    fi
+    oldshort_=$(git-rev-parse --short "$1" 2>/dev/null)
     mkdir -p "$(dirname "$GIT_DIR/$1")"
     case "$1" in
     refs/tags/*)
@@ -154,13 +157,16 @@ fast_forward_local () {
        then
                if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
                then
-                       [ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
+                       [ "$verbose" ] && echo >&2 "* $1: same as $3"
+                       [ "$verbose" ] && echo >&2 "  $label_: $newshort_" ||:
                else
                        echo >&2 "* $1: updating with $3"
+                       echo >&2 "  $label_: $newshort_"
                        git-update-ref -m "$rloga: updating tag" "$1" "$2"
                fi
        else
                echo >&2 "* $1: storing $3"
+               echo >&2 "  $label_: $newshort_"
                git-update-ref -m "$rloga: storing tag" "$1" "$2"
        fi
        ;;
@@ -178,31 +184,34 @@ fast_forward_local () {
                if test -n "$verbose"
                then
                        echo >&2 "* $1: same as $3"
+                       echo >&2 "  $label_: $newshort_"
                fi
                ;;
            *,$local)
                echo >&2 "* $1: fast forward to $3"
-               echo >&2 "  from $local to $2"
+               echo >&2 "  old..new: $oldshort_..$newshort_"
                git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
                ;;
            *)
                false
                ;;
            esac || {
-               echo >&2 "* $1: does not fast forward to $3;"
                case ",$force,$single_force," in
                *,t,*)
-                       echo >&2 "  forcing update."
+                       echo >&2 "* $1: forcing update to non-fast forward $3"
+                       echo >&2 "  old...new: $oldshort_...$newshort_"
                        git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local"
                        ;;
                *)
-                       echo >&2 "  not updating."
+                       echo >&2 "* $1: not updating to non-fast forward $3"
+                       echo >&2 "  old...new: $oldshort_...$newshort_"
                        exit 1
                        ;;
                esac
            }
        else
            echo >&2 "* $1: storing $3"
+           echo >&2 "  $label_: $newshort_"
            git-update-ref -m "$rloga: storing head" "$1" "$2"
        fi
        ;;
@@ -287,6 +296,7 @@ fetch_main () {
       # There are transports that can fetch only one head at a time...
       case "$remote" in
       http://* | https://* | ftp://*)
+         proto=`expr "$remote" : '\([^:]*\):'`
          if [ -n "$GIT_SSL_NO_VERIFY" ]; then
              curl_extra_args="-k"
          fi
@@ -310,7 +320,7 @@ fetch_main () {
          done
          expr "z$head" : "z$_x40\$" >/dev/null ||
              die "Failed to fetch $remote_name from $remote"
-         echo >&2 Fetching "$remote_name from $remote" using http
+         echo >&2 "Fetching $remote_name from $remote using $proto"
          git-http-fetch -v -a "$head" "$remote/" || exit
          ;;
       rsync://*)
@@ -436,10 +446,10 @@ esac
 
 # If the original head was empty (i.e. no "master" yet), or
 # if we were told not to worry, we do not have to check.
-case ",$update_head_ok,$orig_head," in
-*,, | t,* )
+case "$orig_head" in
+'')
        ;;
-*)
+?*)
        curr_head=$(git-rev-parse --verify HEAD 2>/dev/null)
        if test "$curr_head" != "$orig_head"
        then
index 5b34b4d..cb09438 100755 (executable)
@@ -93,6 +93,8 @@ finish () {
        esac
 }
 
+case "$#" in 0) usage ;; esac
+
 rloga=
 while case "$#" in 0) break ;; esac
 do
@@ -197,7 +199,7 @@ f,*)
        ;;
 ?,1,"$head",*)
        # Again the most common case of merging one remote.
-       echo "Updating from $head to $1"
+       echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
        git-update-index --refresh 2>/dev/null
        new_head=$(git-rev-parse --verify "$1^0") &&
        git-read-tree -u -v -m $head "$new_head" &&
index f380437..ed04e7d 100755 (executable)
@@ -58,7 +58,7 @@ then
 
        echo >&2 "Warning: fetch updated the current branch head."
        echo >&2 "Warning: fast forwarding your working tree from"
-       echo >&2 "Warning: $orig_head commit."
+       echo >&2 "Warning: commit $orig_head."
        git-update-index --refresh 2>/dev/null
        git-read-tree -u -m "$orig_head" "$curr_head" ||
                die 'Cannot fast-forward your working tree.
index a7373c0..546fa44 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Junio C Hamano.
 #
 
-USAGE='[--onto <newbase>] <upstream> [<branch>]'
+USAGE='[-v] [--onto <newbase>] <upstream> [<branch>]'
 LONG_USAGE='git-rebase replaces <branch> with a new branch of the
 same name.  When the --onto option is provided the new branch starts
 out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
@@ -39,6 +39,7 @@ strategy=recursive
 do_merge=
 dotest=$GIT_DIR/.dotest-merge
 prec=4
+verbose=
 
 continue_merge () {
        test -n "$prev_head" || die "prev_head must be defined"
@@ -190,6 +191,9 @@ do
                esac
                do_merge=t
                ;;
+       -v|--verbose)
+               verbose=t
+               ;;
        -*)
                usage
                ;;
@@ -273,6 +277,12 @@ then
        exit 0
 fi
 
+if test -n "$verbose"
+then
+       echo "Changes from $mb to $onto:"
+       git-diff-tree --stat --summary "$mb" "$onto"
+fi
+
 # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
 git-reset --hard "$onto"
 
@@ -286,7 +296,7 @@ fi
 
 if test -z "$do_merge"
 then
-       git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
+       git-format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
        git am --binary -3 -k --resolvemsg="$RESOLVEMSG" \
                --reflog-action=rebase
        exit $?
index f2c9071..17e2452 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2005 Linus Torvalds
 #
 
-USAGE='[-a] [-d] [-f] [-l] [-n] [-q]'
+USAGE='[-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]'
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
@@ -25,6 +25,15 @@ do
        shift
 done
 
+# Later we will default repack.UseDeltaBaseOffset to true
+default_dbo=false
+
+case "`git repo-config --bool repack.usedeltabaseoffset ||
+       echo $default_dbo`" in
+true)
+       extra="$extra --delta-base-offset" ;;
+esac
+
 PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
 PACKTMP="$GIT_DIR/.tmp-$$-pack"
 rm -f "$PACKTMP"-*
index 729ec65..36b90e3 100755 (executable)
@@ -46,7 +46,7 @@ case "$common" in
        exit 0
        ;;
 "$head")
-       echo "Updating from $head to $merge"
+       echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)"
        git-read-tree -u -m $head $merge || exit 1
        git-update-ref -m "resolve $merge_name: Fast forward" \
                HEAD "$merge" "$head"
index 066e677..6eab3c7 100755 (executable)
@@ -7,18 +7,20 @@
 case "$0" in
 *-revert* )
        test -t 0 && edit=-e
+       replay=
        me=revert
        USAGE='[--edit | --no-edit] [-n] <commit-ish>' ;;
 *-cherry-pick* )
+       replay=t
        edit=
        me=cherry-pick
-       USAGE='[--edit] [-n] [-r] <commit-ish>'  ;;
+       USAGE='[--edit] [-n] [-r] [-x] <commit-ish>'  ;;
 * )
        die "What are you talking about?" ;;
 esac
 . git-sh-setup
 
-no_commit= replay=
+no_commit=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
@@ -32,8 +34,10 @@ do
        --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)
                edit=
                ;;
-       -r|--r|--re|--rep|--repl|--repla|--replay)
-               replay=t
+       -r)
+               : no-op ;;
+       -x|--i-really-want-to-expose-my-private-commit-object-name)
+               replay=
                ;;
        -*)
                usage
@@ -121,7 +125,7 @@ cherry-pick)
        git-cat-file commit $commit | sed -e '1,/^$/d'
        case "$replay" in
        '')
-               echo "(cherry picked from $commit commit)"
+               echo "(cherry picked from commit $commit)"
                test "$rev" = "$commit" ||
                echo "(original 'git cherry-pick' arguments: $@)"
                ;;
index 4a20310..4c87c20 100755 (executable)
@@ -83,11 +83,12 @@ sub cleanup_compose_files();
 my $compose_filename = ".msg.$$";
 
 # Variables we fill in automatically, or via prompting:
-my (@to,@cc,@initial_cc,@bcclist,
+my (@to,@cc,@initial_cc,@bcclist,@xh,
        $initial_reply_to,$initial_subject,@files,$from,$compose,$time);
 
 # Behavior modification variables
-my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc) = (1, 0, 0, 0);
+my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc,
+       $dry_run) = (1, 0, 0, 0, 0);
 my $smtp_server;
 
 # Example reply to:
@@ -116,6 +117,7 @@ my $rc = GetOptions("from=s" => \$from,
                    "quiet" => \$quiet,
                    "suppress-from" => \$suppress_from,
                    "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
+                   "dry-run" => \$dry_run,
         );
 
 # Verify the user input
@@ -228,6 +230,9 @@ if (!defined $initial_reply_to && $prompting) {
        $initial_reply_to =~ s/(^\s+|\s+$)//g;
 }
 
+if (!$smtp_server) {
+       $smtp_server = $repo->config('sendemail.smtpserver');
+}
 if (!$smtp_server) {
        foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
                if (-x $_) {
@@ -409,6 +414,11 @@ sub send_message
            $gitversion = Git::version();
        }
 
+       my ($author_name) = ($from =~ /^(.*?)\s+</);
+       if ($author_name && $author_name =~ /\./ && $author_name !~ /^".*"$/) {
+               my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
+               $from = "\"$name\"$addr";
+       }
        my $header = "From: $from
 To: $to
 Cc: $cc
@@ -422,8 +432,13 @@ X-Mailer: git-send-email $gitversion
                $header .= "In-Reply-To: $reply_to\n";
                $header .= "References: $references\n";
        }
+       if (@xh) {
+               $header .= join("\n", @xh) . "\n";
+       }
 
-       if ($smtp_server =~ m#^/#) {
+       if ($dry_run) {
+               # We don't want to send the email.
+       } elsif ($smtp_server =~ m#^/#) {
                my $pid = open my $sm, '|-';
                defined $pid or die $!;
                if (!$pid) {
@@ -472,15 +487,22 @@ foreach my $t (@files) {
 
        my $author_not_sender = undef;
        @cc = @initial_cc;
-       my $found_mbox = 0;
+       @xh = ();
+       my $input_format = undef;
        my $header_done = 0;
        $message = "";
        while(<F>) {
                if (!$header_done) {
-                       $found_mbox = 1, next if (/^From /);
+                       if (/^From /) {
+                               $input_format = 'mbox';
+                               next;
+                       }
                        chomp;
+                       if (!defined $input_format && /^[-A-Za-z]+:\s/) {
+                               $input_format = 'mbox';
+                       }
 
-                       if ($found_mbox) {
+                       if (defined $input_format && $input_format eq 'mbox') {
                                if (/^Subject:\s+(.*)$/) {
                                        $subject = $1;
 
@@ -495,6 +517,9 @@ foreach my $t (@files) {
                                                $2, $_) unless $quiet;
                                        push @cc, $2;
                                }
+                               elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
+                                       push @xh, $_;
+                               }
 
                        } else {
                                # In the traditional
@@ -502,6 +527,7 @@ foreach my $t (@files) {
                                # line 1 = cc
                                # line 2 = subject
                                # So let's support that, too.
+                               $input_format = 'lots';
                                if (@cc == 0) {
                                        printf("(non-mbox) Adding cc: %s from line '%s'\n",
                                                $_, $_) unless $quiet;
@@ -538,7 +564,7 @@ foreach my $t (@files) {
        send_message();
 
        # set up for the next message
-       if ($chain_reply_to || length($reply_to) == 0) {
+       if ($chain_reply_to || !defined $reply_to || length($reply_to) == 0) {
                $reply_to = $message_id;
                if (length $references > 0) {
                        $references .= " $message_id";
index 0b14f83..334fec7 100755 (executable)
@@ -1,6 +1,18 @@
 #!/usr/bin/perl -w
 
 use strict;
+use Getopt::Std;
+use File::Basename qw(basename dirname);
+
+our ($opt_h, $opt_n, $opt_s);
+getopts('hns');
+
+$opt_h && usage();
+
+sub usage {
+       print STDERR "Usage: ${\basename $0} [-h] [-n] [-s] < <log_data>\n";
+        exit(1);
+}
 
 my (%mailmap);
 my (%email);
@@ -38,16 +50,38 @@ sub by_name($$) {
 
        uc($a) cmp uc($b);
 }
+sub by_nbentries($$) {
+       my ($a, $b) = @_;
+       my $a_entries = $map{$a};
+       my $b_entries = $map{$b};
+
+       @$b_entries - @$a_entries || by_name $a, $b;
+}
+
+my $sort_method = $opt_n ? \&by_nbentries : \&by_name;
+
+sub summary_output {
+       my ($obj, $num, $key);
+
+       foreach $key (sort $sort_method keys %map) {
+               $obj = $map{$key};
+               $num = @$obj;
+               printf "%s: %u\n", $key, $num;
+               $n_output += $num;
+       }
+}
 
 sub shortlog_output {
-       my ($obj, $key, $desc);
+       my ($obj, $num, $key, $desc);
+
+       foreach $key (sort $sort_method keys %map) {
+               $obj = $map{$key};
+               $num = @$obj;
 
-       foreach $key (sort by_name keys %map) {
                # output author
-               printf "%s:\n", $key;
+               printf "%s (%u):\n", $key, $num;
 
                # output author's 1-line summaries
-               $obj = $map{$key};
                foreach $desc (reverse @$obj) {
                        print "  $desc\n";
                        $n_output++;
@@ -152,7 +186,7 @@ sub finalize {
 
 &setup_mailmap;
 &changelog_input;
-&shortlog_output;
+$opt_s ? &summary_output : &shortlog_output;
 &finalize;
 exit(0);
 
index f5c7d46..37ecc51 100755 (executable)
@@ -40,8 +40,22 @@ memoize('cmt_metadata');
 memoize('get_commit_time');
 
 my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib);
+
+sub nag_lib {
+       print STDERR <<EOF;
+! Please consider installing the SVN Perl libraries (version 1.1.0 or
+! newer).  You will generally get better performance and fewer bugs,
+! especially if you:
+! 1) have a case-insensitive filesystem
+! 2) replace symlinks with files (and vice-versa) in commits
+
+EOF
+}
+
 $_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB};
 libsvn_load();
+nag_lib() unless $_use_lib;
+
 my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
 my $sha1 = qr/[a-f\d]{40}/;
 my $sha1_short = qr/[a-f\d]{4,40}/;
@@ -52,7 +66,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
        $_template, $_shared, $_no_default_regex, $_no_graft_copy,
        $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
        $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
-       $_merge, $_strategy, $_dry_run, $_ignore_nodate);
+       $_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive);
 my (@_branch_from, %tree_map, %users, %rusers, %equiv);
 my ($_svn_co_url_revs, $_svn_pg_peg_revs);
 my @repo_path_split_cache;
@@ -114,6 +128,7 @@ my %cmd = (
                          'incremental' => \$_incremental,
                          'oneline' => \$_oneline,
                          'show-commit' => \$_show_commit,
+                         'non-recursive' => \$_non_recursive,
                          'authors-file|A=s' => \$_authors,
                        } ],
        'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees',
@@ -168,11 +183,11 @@ Usage: $0 <command> [options] [arguments]\n
 
        foreach (sort keys %cmd) {
                next if $cmd && $cmd ne $_;
-               print $fd '  ',pack('A13',$_),$cmd{$_}->[1],"\n";
+               print $fd '  ',pack('A17',$_),$cmd{$_}->[1],"\n";
                foreach (keys %{$cmd{$_}->[2]}) {
                        # prints out arguments as they should be passed:
                        my $x = s#[:=]s$## ? '<arg>' : s#[:=]i$## ? '<num>' : '';
-                       print $fd ' ' x 17, join(', ', map { length $_ > 1 ?
+                       print $fd ' ' x 21, join(', ', map { length $_ > 1 ?
                                                        "--$_" : "-$_" }
                                                split /\|/,$_)," $x\n";
                }
@@ -521,7 +536,7 @@ sub commit_lib {
                        $SVN = libsvn_connect($repo);
                        my $ed = SVN::Git::Editor->new(
                                        {       r => $r_last,
-                                               ra => $SVN,
+                                               ra => $SVN_LOG,
                                                c => $c,
                                                svn_path => $SVN_PATH
                                        },
@@ -682,12 +697,17 @@ sub multi_init {
                }
                $_trunk = $url . $_trunk;
        }
+       my $ch_id;
        if ($GIT_SVN eq 'git-svn') {
-               print "GIT_SVN_ID set to 'trunk' for $_trunk\n";
+               $ch_id = 1;
                $GIT_SVN = $ENV{GIT_SVN_ID} = 'trunk';
        }
        init_vars();
-       init($_trunk);
+       unless (-d $GIT_SVN_DIR) {
+               print "GIT_SVN_ID set to 'trunk' for $_trunk\n" if $ch_id;
+               init($_trunk);
+               sys('git-repo-config', 'svn.trunk', $_trunk);
+       }
        complete_url_ls_init($url, $_branches, '--branches/-b', '');
        complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/');
 }
@@ -747,13 +767,18 @@ sub show_log {
                        # ignore
                } elsif (/^:\d{6} \d{6} $sha1_short/o) {
                        push @{$c->{raw}}, $_;
+               } elsif (/^[ACRMDT]\t/) {
+                       # we could add $SVN_PATH here, but that requires
+                       # remote access at the moment (repo_path_split)...
+                       s#^([ACRMDT])\t#   $1 #;
+                       push @{$c->{changed}}, $_;
                } elsif (/^diff /) {
                        $d = 1;
                        push @{$c->{diff}}, $_;
                } elsif ($d) {
                        push @{$c->{diff}}, $_;
                } elsif (/^    (git-svn-id:.+)$/) {
-                       (undef, $c->{r}, undef) = extract_metadata($1);
+                       ($c->{url}, $c->{r}, undef) = extract_metadata($1);
                } elsif (s/^    //) {
                        push @{$c->{l}}, $_;
                }
@@ -807,7 +832,7 @@ sub commit_diff {
        $SVN ||= libsvn_connect($repo);
        my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
        my $ed = SVN::Git::Editor->new({        r => $SVN->get_latest_revnum,
-                                               ra => $SVN, c => $tb,
+                                               ra => $SVN_LOG, c => $tb,
                                                svn_path => $SVN_PATH
                                        },
                                $SVN->get_commit_editor($_message,
@@ -845,7 +870,8 @@ sub git_svn_log_cmd {
        my ($r_min, $r_max) = @_;
        my @cmd = (qw/git-log --abbrev-commit --pretty=raw
                        --default/, "refs/remotes/$GIT_SVN");
-       push @cmd, '--summary' if $_verbose;
+       push @cmd, '-r' unless $_non_recursive;
+       push @cmd, qw/--raw --name-status/ if $_verbose;
        return @cmd unless defined $r_max;
        if ($r_max == $r_min) {
                push @cmd, '--max-count=1';
@@ -856,7 +882,7 @@ sub git_svn_log_cmd {
                my ($c_min, $c_max);
                $c_max = revdb_get($REVDB, $r_max);
                $c_min = revdb_get($REVDB, $r_min);
-               if ($c_min && $c_max) {
+               if (defined $c_min && defined $c_max) {
                        if ($r_max > $r_max) {
                                push @cmd, "$c_min..$c_max";
                        } else {
@@ -937,16 +963,21 @@ sub complete_url_ls_init {
                                print STDERR "W: Unrecognized URL: $u\n";
                                die "This should never happen\n";
                        }
+                       # don't try to init already existing refs
                        my $id = $pfx.$1;
-                       print "init $u => $id\n";
                        $GIT_SVN = $ENV{GIT_SVN_ID} = $id;
                        init_vars();
-                       init($u);
+                       unless (-d $GIT_SVN_DIR) {
+                               print "init $u => $id\n";
+                               init($u);
+                       }
                }
                exit 0;
        }
        waitpid $pid, 0;
        croak $? if $?;
+       my ($n) = ($switch =~ /^--(\w+)/);
+       sys('git-repo-config', "svn.$n", $var);
 }
 
 sub common_prefix {
@@ -1470,10 +1501,13 @@ sub svn_checkout_tree {
                        apply_mod_line_blob($m);
                        svn_check_prop_executable($m);
                } elsif ($m->{chg} eq 'T') {
-                       sys(qw(svn rm --force),$m->{file_b});
-                       apply_mod_line_blob($m);
-                       sys(qw(svn add), $m->{file_b});
                        svn_check_prop_executable($m);
+                       apply_mod_line_blob($m);
+                       if ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) {
+                               sys(qw(svn propdel svn:special), $m->{file_b});
+                       } else {
+                               sys(qw(svn propset svn:special *),$m->{file_b});
+                       }
                } elsif ($m->{chg} eq 'A') {
                        svn_ensure_parent_path( $m->{file_b} );
                        apply_mod_line_blob($m);
@@ -2551,6 +2585,12 @@ sub show_commit {
        }
 }
 
+sub show_commit_changed_paths {
+       my ($c) = @_;
+       return unless $c->{changed};
+       print "Changed paths:\n", @{$c->{changed}};
+}
+
 sub show_commit_normal {
        my ($c) = @_;
        print '-' x72, "\nr$c->{r} | ";
@@ -2560,7 +2600,8 @@ sub show_commit_normal {
        my $nr_line = 0;
 
        if (my $l = $c->{l}) {
-               while ($l->[$#$l] eq "\n" && $l->[($#$l - 1)] eq "\n") {
+               while ($l->[$#$l] eq "\n" && $#$l > 0
+                                         && $l->[($#$l - 1)] eq "\n") {
                        pop @$l;
                }
                $nr_line = scalar @$l;
@@ -2572,11 +2613,15 @@ sub show_commit_normal {
                        } else {
                                $nr_line .= ' lines';
                        }
-                       print $nr_line, "\n\n";
+                       print $nr_line, "\n";
+                       show_commit_changed_paths($c);
+                       print "\n";
                        print $_ foreach @$l;
                }
        } else {
-               print "1 line\n\n";
+               print "1 line\n";
+               show_commit_changed_paths($c);
+               print "\n";
 
        }
        foreach my $x (qw/raw diff/) {
@@ -3312,9 +3357,11 @@ sub chg_file {
        seek $fh, 0, 0 or croak $!;
 
        my $exp = $md5->hexdigest;
-       my $atd = $self->apply_textdelta($fbat, undef, $self->{pool});
-       my $got = SVN::TxDelta::send_stream($fh, @$atd, $self->{pool});
+       my $pool = SVN::Pool->new;
+       my $atd = $self->apply_textdelta($fbat, undef, $pool);
+       my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool);
        die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
+       $pool->clear;
 
        close $fh or croak $!;
 }
index 988514e..cbaa8ab 100755 (executable)
@@ -31,7 +31,7 @@ $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
 our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T,
-    $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F);
+    $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F,$opt_P);
 
 sub usage() {
        print STDERR <<END;
@@ -39,17 +39,19 @@ Usage: ${\basename $0}     # fetch/update GIT from SVN
        [-o branch-for-HEAD] [-h] [-v] [-l max_rev]
        [-C GIT_repository] [-t tagname] [-T trunkname] [-b branchname]
        [-d|-D] [-i] [-u] [-r] [-I ignorefilename] [-s start_chg]
-       [-m] [-M regex] [-A author_file] [-S] [-F] [SVN_URL]
+       [-m] [-M regex] [-A author_file] [-S] [-F] [-P project_name] [SVN_URL]
 END
        exit(1);
 }
 
-getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:Suv") or usage();
+getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:uv") or usage();
 usage if $opt_h;
 
 my $tag_name = $opt_t || "tags";
 my $trunk_name = $opt_T || "trunk";
 my $branch_name = $opt_b || "branches";
+my $project_name = $opt_P || "";
+$project_name = "/" . $project_name if ($project_name);
 
 @ARGV == 1 or @ARGV == 2 or usage();
 
@@ -193,6 +195,13 @@ sub ignore {
        }
 }
 
+sub dir_list {
+       my($self,$path,$rev) = @_;
+       my ($dirents,undef,$properties)
+           = $self->{'svn'}->get_dir($path,$rev,undef);
+       return $dirents;
+}
+
 package main;
 use URI;
 
@@ -342,35 +351,16 @@ if ($opt_A) {
 
 open BRANCHES,">>", "$git_dir/svn2git";
 
-sub node_kind($$$) {
-       my ($branch, $path, $revision) = @_;
+sub node_kind($$) {
+       my ($svnpath, $revision) = @_;
        my $pool=SVN::Pool->new;
-       my $kind = $svn->{'svn'}->check_path(revert_split_path($branch,$path),$revision,$pool);
+       my $kind = $svn->{'svn'}->check_path($svnpath,$revision,$pool);
        $pool->clear;
        return $kind;
 }
 
-sub revert_split_path($$) {
-       my($branch,$path) = @_;
-
-       my $svnpath;
-       $path = "" if $path eq "/"; # this should not happen, but ...
-       if($branch eq "/") {
-               $svnpath = "$trunk_name/$path";
-       } elsif($branch =~ m#^/#) {
-               $svnpath = "$tag_name$branch/$path";
-       } else {
-               $svnpath = "$branch_name/$branch/$path";
-       }
-
-       $svnpath =~ s#/+$##;
-       return $svnpath;
-}
-
 sub get_file($$$) {
-       my($rev,$branch,$path) = @_;
-
-       my $svnpath = revert_split_path($branch,$path);
+       my($svnpath,$rev,$path) = @_;
 
        # now get it
        my ($name,$mode);
@@ -413,10 +403,9 @@ sub get_file($$$) {
 }
 
 sub get_ignore($$$$$) {
-       my($new,$old,$rev,$branch,$path) = @_;
+       my($new,$old,$rev,$path,$svnpath) = @_;
 
        return unless $opt_I;
-       my $svnpath = revert_split_path($branch,$path);
        my $name = $svn->ignore("$svnpath",$rev);
        if ($path eq '/') {
                $path = $opt_I;
@@ -435,11 +424,25 @@ sub get_ignore($$$$$) {
                close $F;
                unlink $name;
                push(@$new,['0644',$sha,$path]);
-       } else {
+       } elsif (defined $old) {
                push(@$old,$path);
        }
 }
 
+sub project_path($$)
+{
+       my ($path, $project) = @_;
+
+       $path = "/".$path unless ($path =~ m#^\/#) ;
+       return $1 if ($path =~ m#^$project\/(.*)$#);
+
+       $path =~ s#\.#\\\.#g;
+       $path =~ s#\+#\\\+#g;
+       return "/" if ($project =~ m#^$path.*$#);
+
+       return undef;
+}
+
 sub split_path($$) {
        my($rev,$path) = @_;
        my $branch;
@@ -459,7 +462,11 @@ sub split_path($$) {
                print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path});
                return ()
        }
-       $path = "/" if $path eq "";
+       if ($path eq "") {
+               $path = "/";
+       } elsif ($project_name) {
+               $path = project_path($path, $project_name);
+       }
        return ($branch,$path);
 }
 
@@ -480,6 +487,27 @@ sub branch_rev($$) {
        return $therev;
 }
 
+sub expand_svndir($$$);
+
+sub expand_svndir($$$)
+{
+       my ($svnpath, $rev, $path) = @_;
+       my @list;
+       get_ignore(\@list, undef, $rev, $path, $svnpath);
+       my $dirents = $svn->dir_list($svnpath, $rev);
+       foreach my $p(keys %$dirents) {
+               my $kind = node_kind($svnpath.'/'.$p, $rev);
+               if ($kind eq $SVN::Node::file) {
+                       my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p);
+                       push(@list, $f) if $f;
+               } elsif ($kind eq $SVN::Node::dir) {
+                       push(@list,
+                            expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p));
+               }
+       }
+       return @list;
+}
+
 sub copy_path($$$$$$$$) {
        # Somebody copied a whole subdirectory.
        # We need to find the index entries from the old version which the
@@ -488,8 +516,11 @@ sub copy_path($$$$$$$$) {
        my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_;
 
        my($srcbranch,$srcpath) = split_path($rev,$oldpath);
-       unless(defined $srcbranch) {
-               print "Path not found when copying from $oldpath @ $rev\n";
+       unless(defined $srcbranch && defined $srcpath) {
+               print "Path not found when copying from $oldpath @ $rev.\n".
+                       "Will try to copy from original SVN location...\n"
+                       if $opt_v;
+               push (@$new, expand_svndir($oldpath, $rev, $path));
                return;
        }
        my $therev = branch_rev($srcbranch, $rev);
@@ -503,7 +534,7 @@ sub copy_path($$$$$$$$) {
        }
        print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v;
        if ($node_kind eq $SVN::Node::dir) {
-                       $srcpath =~ s#/*$#/#;
+               $srcpath =~ s#/*$#/#;
        }
        
        my $pid = open my $f,'-|';
@@ -582,10 +613,12 @@ sub commit {
                if(defined $oldpath) {
                        my $p;
                        ($parent,$p) = split_path($revision,$oldpath);
-                       if($parent eq "/") {
-                               $parent = $opt_o;
-                       } else {
-                               $parent =~ s#^/##; # if it's a tag
+                       if(defined $parent) {
+                               if($parent eq "/") {
+                                       $parent = $opt_o;
+                               } else {
+                                       $parent =~ s#^/##; # if it's a tag
+                               }
                        }
                } else {
                        $parent = undef;
@@ -651,9 +684,10 @@ sub commit {
                                push(@old,$path); # remove any old stuff
                        }
                        if(($action->[0] eq "A") || ($action->[0] eq "R")) {
-                               my $node_kind = node_kind($branch,$path,$revision);
+                               my $node_kind = node_kind($action->[3], $revision);
                                if ($node_kind eq $SVN::Node::file) {
-                                       my $f = get_file($revision,$branch,$path);
+                                       my $f = get_file($action->[3],
+                                                        $revision, $path);
                                        if ($f) {
                                                push(@new,$f) if $f;
                                        } else {
@@ -668,19 +702,20 @@ sub commit {
                                                          \@new, \@parents);
                                        } else {
                                                get_ignore(\@new, \@old, $revision,
-                                                          $branch, $path);
+                                                          $path, $action->[3]);
                                        }
                                }
                        } elsif ($action->[0] eq "D") {
                                push(@old,$path);
                        } elsif ($action->[0] eq "M") {
-                               my $node_kind = node_kind($branch,$path,$revision);
+                               my $node_kind = node_kind($action->[3], $revision);
                                if ($node_kind eq $SVN::Node::file) {
-                                       my $f = get_file($revision,$branch,$path);
+                                       my $f = get_file($action->[3],
+                                                        $revision, $path);
                                        push(@new,$f) if $f;
                                } elsif ($node_kind eq $SVN::Node::dir) {
                                        get_ignore(\@new, \@old, $revision,
-                                                  $branch,$path);
+                                                  $path, $action->[3]);
                                }
                        } else {
                                die "$revision: unknown action '".$action->[0]."' for $path\n";
@@ -838,7 +873,7 @@ sub commit {
                print $out ("object $cid\n".
                    "type commit\n".
                    "tag $dest\n".
-                   "tagger $committer_name <$committer_email>\n") and
+                   "tagger $committer_name <$committer_email> 0 +0000\n") and
                close($out)
                    or die "Cannot create tag object $dest: $!\n";
 
@@ -883,6 +918,7 @@ sub commit_all {
        while(my($path,$action) = each %$changed_paths) {
                ($branch,$path) = split_path($revision,$path);
                next if not defined $branch;
+               next if not defined $path;
                $done{$branch}{$path} = $action;
        }
        while(($branch,$changed_paths) = each %done) {
diff --git a/git.c b/git.c
index f197169..af181d9 100644 (file)
--- a/git.c
+++ b/git.c
@@ -16,7 +16,7 @@
 #include "builtin.h"
 
 const char git_usage_string[] =
-       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
+       "git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]";
 
 static void prepend_to_path(const char *dir, int len)
 {
@@ -219,6 +219,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                int option;
        } commands[] = {
                { "add", cmd_add, RUN_SETUP },
+               { "annotate", cmd_annotate, },
                { "apply", cmd_apply },
                { "archive", cmd_archive },
                { "branch", cmd_branch },
@@ -227,7 +228,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "check-ref-format", cmd_check_ref_format },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "count-objects", cmd_count_objects, RUN_SETUP },
-               { "diff", cmd_diff, RUN_SETUP },
+               { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
                { "diff-files", cmd_diff_files, RUN_SETUP },
                { "diff-index", cmd_diff_index, RUN_SETUP },
                { "diff-stages", cmd_diff_stages, RUN_SETUP },
@@ -260,7 +261,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "show", cmd_show, RUN_SETUP | USE_PAGER },
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
-               { "tar-tree", cmd_tar_tree, RUN_SETUP },
+               { "tar-tree", cmd_tar_tree },
                { "unpack-objects", cmd_unpack_objects, RUN_SETUP },
                { "update-index", cmd_update_index, RUN_SETUP },
                { "update-ref", cmd_update_ref, RUN_SETUP },
index 6d90034..83268fc 100644 (file)
@@ -96,10 +96,10 @@ find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
 find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
 find $RPM_BUILD_ROOT -type f -name perllocal.pod -exec rm -f {} ';'
 
-(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "arch|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@)               > bin-man-doc-files
-(find $RPM_BUILD_ROOT%{perl_vendorarch} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
+(find $RPM_BUILD_ROOT%{_bindir} -type f | grep -vE "archimport|svn|cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@)               > bin-man-doc-files
+(find $RPM_BUILD_ROOT%{perl_vendorlib} -type f | sed -e s@^$RPM_BUILD_ROOT@@) >> perl-files
 %if %{!?_without_docs:1}0
-(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "arch|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
+(find $RPM_BUILD_ROOT%{_mandir} $RPM_BUILD_ROOT/Documentation -type f | grep -vE "archimport|svn|git-cvs|email|gitk" | sed -e s@^$RPM_BUILD_ROOT@@ -e 's/$/*/' ) >> bin-man-doc-files
 %else
 rm -rf $RPM_BUILD_ROOT%{_mandir}
 %endif
@@