summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/SubmittingPatches7
-rw-r--r--Documentation/asciidoc.conf1
-rw-r--r--Documentation/config.txt16
-rw-r--r--Documentation/diff-format.txt82
-rw-r--r--Documentation/diff-options.txt5
-rw-r--r--Documentation/everyday.txt54
-rw-r--r--Documentation/git-blame.txt39
-rw-r--r--Documentation/git-cherry-pick.txt23
-rw-r--r--Documentation/git-cherry.txt24
-rw-r--r--Documentation/git-daemon.txt18
-rw-r--r--Documentation/git-for-each-ref.txt10
-rw-r--r--Documentation/git-grep.txt11
-rw-r--r--Documentation/git-http-push.txt2
-rw-r--r--Documentation/git-pack-objects.txt20
-rw-r--r--Documentation/git-pull.txt2
-rw-r--r--Documentation/git-rebase.txt5
-rw-r--r--Documentation/git-repack.txt17
-rw-r--r--Documentation/git-rev-parse.txt41
-rw-r--r--Documentation/git-send-email.txt9
-rw-r--r--Documentation/git-send-pack.txt2
-rw-r--r--Documentation/git-shortlog.txt17
-rw-r--r--Documentation/git-svn.txt139
-rw-r--r--Documentation/git.txt363
-rw-r--r--Documentation/glossary.txt4
-rw-r--r--Documentation/urls.txt8
-rw-r--r--Makefile14
-rw-r--r--archive-zip.c17
-rw-r--r--blame.c458
-rw-r--r--builtin-annotate.c25
-rw-r--r--builtin-apply.c23
-rw-r--r--builtin-archive.c2
-rw-r--r--builtin-grep.c4
-rw-r--r--builtin-pack-objects.c252
-rw-r--r--builtin-prune-packed.c11
-rw-r--r--builtin-prune.c2
-rw-r--r--builtin-unpack-objects.c144
-rw-r--r--builtin.h2
-rw-r--r--cache-tree.c10
-rw-r--r--cache.h13
-rw-r--r--combine-diff.c67
-rw-r--r--commit.c11
-rwxr-xr-xcontrib/completion/git-completion.bash29
-rw-r--r--contrib/emacs/git.el4
-rw-r--r--contrib/emacs/vc-git.el11
-rw-r--r--daemon.c6
-rw-r--r--diff-delta.c8
-rw-r--r--diff.c30
-rw-r--r--diff.h16
-rw-r--r--environment.c1
-rw-r--r--fetch-clone.c4
-rw-r--r--fetch-pack.c5
-rwxr-xr-xgit-annotate.perl708
-rwxr-xr-xgit-bisect.sh11
-rwxr-xr-xgit-cherry.sh4
-rwxr-xr-xgit-clone.sh66
-rwxr-xr-xgit-commit.sh491
-rwxr-xr-xgit-cvsimport.perl25
-rwxr-xr-xgit-cvsserver.perl79
-rwxr-xr-xgit-fetch.sh50
-rwxr-xr-xgit-merge.sh4
-rwxr-xr-xgit-pull.sh2
-rwxr-xr-xgit-rebase.sh14
-rwxr-xr-xgit-repack.sh11
-rwxr-xr-xgit-resolve.sh2
-rwxr-xr-xgit-revert.sh14
-rwxr-xr-xgit-send-email.perl40
-rwxr-xr-xgit-shortlog.perl44
-rwxr-xr-xgit-svn.perl87
-rwxr-xr-xgit-svnimport.perl124
-rw-r--r--git.c7
-rw-r--r--git.spec.in14
-rw-r--r--gitweb/README49
-rw-r--r--gitweb/gitweb.css13
-rwxr-xr-xgitweb/gitweb.perl612
-rw-r--r--grep.c114
-rw-r--r--grep.h2
-rw-r--r--http-fetch.c257
-rw-r--r--imap-send.c10
-rw-r--r--index-pack.c331
-rw-r--r--merge-recursive.c5
-rw-r--r--pack.h5
-rw-r--r--pager.c2
-rw-r--r--quote.c2
-rw-r--r--receive-pack.c16
-rw-r--r--revision.c25
-rw-r--r--setup.c2
-rw-r--r--sha1_file.c171
-rw-r--r--sha1_name.c2
-rw-r--r--show-index.c2
-rw-r--r--sideband.c28
-rw-r--r--sideband.h2
-rwxr-xr-xt/t1200-tutorial.sh2
-rwxr-xr-xt/t3401-rebase-partial.sh5
-rwxr-xr-xt/t3402-rebase-merge.sh6
-rwxr-xr-xt/t3403-rebase-skip.sh6
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--summary_initial2
-rwxr-xr-xt/t4015-diff-whitespace.sh122
-rwxr-xr-xt/t4118-apply-empty-context.sh55
-rwxr-xr-xt/t5000-tar-tree.sh35
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh6
-rwxr-xr-xt/t6021-merge-criss-cross.sh6
-rwxr-xr-xt/t6022-merge-rename.sh6
-rwxr-xr-xt/test-lib.sh6
-rw-r--r--trace.c3
-rw-r--r--upload-pack.c10
-rw-r--r--wt-status.c14
-rw-r--r--xdiff/xemit.c5
-rw-r--r--xdiff/xmacros.h5
-rw-r--r--xdiff/xutils.c29
109 files changed, 3318 insertions, 2540 deletions
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 90722c21fa..8a3d31631c 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -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
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 8196d787ab..44b1ce4c6b 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -11,6 +11,7 @@
caret=^
startsb=[
endsb=]
+tilde=~
ifdef::backend-docbook[]
[gitlink-inlinemacro]
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 232e2a9732..d9e73da2a7 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -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].
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index 617d8f526f..e4520e28e5 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -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
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 7b7b9e8ce9..e112172ca5 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -16,6 +16,11 @@
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.
diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt
index b935c18088..9677671892 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/everyday.txt
@@ -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
}
------------
diff --git a/Documentation/git-blame.txt b/Documentation/git-blame.txt
index e1f89444a9..9891c1d377 100644
--- a/Documentation/git-blame.txt
+++ b/Documentation/git-blame.txt
@@ -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]
diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index bfa950ca19..875edb6b9f 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -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
diff --git a/Documentation/git-cherry.txt b/Documentation/git-cherry.txt
index 893baaa6f6..27b67b81a5 100644
--- a/Documentation/git-cherry.txt
+++ b/Documentation/git-cherry.txt
@@ -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
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index d562232e52..993adc7c5a 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -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
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index d5fdcef8d9..2bf6aef735 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -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
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index d8af4d961b..bfbece9864 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -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
diff --git a/Documentation/git-http-push.txt b/Documentation/git-http-push.txt
index 7e1f894a92..c2485c6e9c 100644
--- a/Documentation/git-http-push.txt
+++ b/Documentation/git-http-push.txt
@@ -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.
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index d4661ddc2f..a1e55054bd 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -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
------
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 51577fcbe6..2a5aea73ba 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -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
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9d7bcaa38c..10f2924f4d 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -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
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 49f7e0a4a4..0fa47e3b01 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -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
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index b761b4b965..ed938aafb0 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -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.
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 481b3f50e3..4c8d907bd5 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -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.
diff --git a/Documentation/git-send-pack.txt b/Documentation/git-send-pack.txt
index 9e67f17302..5376f68548 100644
--- a/Documentation/git-send-pack.txt
+++ b/Documentation/git-send-pack.txt
@@ -43,7 +43,7 @@ OPTIONS
<directory>::
The repository to update.
-<ref>...:
+<ref>...::
The remote refs to update.
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index 7486ebe785..d54fc3e5c6 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -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'::
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 1cfa3e342c..450ff1f85b 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -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
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 2135b65516..0679e3c209 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -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
------------------------
diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt
index 14449ca8ba..7e560b0eea 100644
--- a/Documentation/glossary.txt
+++ b/Documentation/glossary.txt
@@ -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::
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 26ecba53fb..670827c323 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -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
diff --git a/Makefile b/Makefile
index be8bf392a2..40e2a680f0 100644
--- 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
diff --git a/archive-zip.c b/archive-zip.c
index 3ffdad68d1..28e7352e98 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -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 8cfd5d94c7..3ec1c8f1b2 100644
--- a/blame.c
+++ b/blame.c
@@ -17,19 +17,26 @@
#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 char* blame_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;
- void* topo_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 char* blame_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_info* get_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_info* util = 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_info* util;
+ struct util_info *util;
int lines_left;
int *blame_p;
int *new_lines;
int new_lines_len;
- struct commit* commit = 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 char* paths[2];
- struct util_info* util = 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 char* paths[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_info* cutil = 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_info* util = 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 char* new_name = find_rename(commit, p);
+ const char *new_name = find_rename(commit, p);
if (new_name) {
- struct util_info* putil = 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
{
- char* author;
- char* author_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_info* util = 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_info* util = 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 commit* start_commit;
+ struct commit *start_commit;
- const char* args[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 char* prefix = 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
index 0000000000..25ad473711
--- /dev/null
+++ b/builtin-annotate.c
@@ -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);
+}
+
diff --git a/builtin-apply.c b/builtin-apply.c
index de5f855266..11397f5504 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -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);
}
}
diff --git a/builtin-archive.c b/builtin-archive.c
index 6dabdee201..9177379122 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -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);
diff --git a/builtin-grep.c b/builtin-grep.c
index 4205e5d38d..ad7dc00cde 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -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],
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 96c069a81d..41e1e74533 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -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;
diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c
index 960db49859..24e3b0a8c2 100644
--- a/builtin-prune-packed.c
+++ b/builtin-prune-packed.c
@@ -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;
}
diff --git a/builtin-prune.c b/builtin-prune.c
index e79b515c76..d853902c51 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -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;
}
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index 4f96bcae32..74a90c1129 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -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");
}
diff --git a/builtin.h b/builtin.h
index db9b369e27..f7150aa6b8 100644
--- 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);
diff --git a/cache-tree.c b/cache-tree.c
index 323c68a670..a80326289d 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -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 0565333f05..e997a85005 100644
--- 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,
};
diff --git a/combine-diff.c b/combine-diff.c
index 46d9121baf..29d0c9cf95 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -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)
diff --git a/commit.c b/commit.c
index 5b6e082c85..a6d543eee7 100644
--- 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);
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d9cb17d0b2..b074f4fe57 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -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 ;;
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 68de9be0c7..5354cd67b3 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -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)
"")))
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
index 4a8f79092d..8b6361922f 100644
--- a/contrib/emacs/vc-git.el
+++ b/contrib/emacs/vc-git.el
@@ -23,13 +23,18 @@
;; 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.")
@@ -119,10 +124,10 @@
(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))))))
diff --git a/daemon.c b/daemon.c
index ad8492873e..e66bb802da 100644
--- 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.
diff --git a/diff-delta.c b/diff-delta.c
index 51df4608a8..fa16d06c8d 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -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 fb8243261c..33153787b8 100644
--- 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 b48c9914e7..ce3058e437 100644
--- 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" \
diff --git a/environment.c b/environment.c
index 63b1d155be..84d870ca4e 100644
--- a/environment.c
+++ b/environment.c
@@ -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;
diff --git a/fetch-clone.c b/fetch-clone.c
index b632ca0438..76b99afcdb 100644
--- a/fetch-clone.c
+++ b/fetch-clone.c
@@ -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);
}
diff --git a/fetch-pack.c b/fetch-pack.c
index 99ac08b2c2..90b79407c6 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -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
index 215ed26f3a..0000000000
--- a/git-annotate.perl
+++ /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}});
-}
diff --git a/git-bisect.sh b/git-bisect.sh
index 06a8d26945..6da31e87a0 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -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 () {
diff --git a/git-cherry.sh b/git-cherry.sh
index 8832573fee..cf7af5502c 100755
--- a/git-cherry.sh
+++ b/git-cherry.sh
@@ -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
diff --git a/git-clone.sh b/git-clone.sh
index 3998c55cef..3f006d1a77 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -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"
diff --git a/git-commit.sh b/git-commit.sh
index 8ac8dcc348..81c3a0cb61 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -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.
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index e5a00a1285..14e2c6131b 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -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
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 2130d57020..8817f8bb4f 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -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 M \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
diff --git a/git-fetch.sh b/git-fetch.sh
index e8a7668182..fa73ad2260 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -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
diff --git a/git-merge.sh b/git-merge.sh
index 5b34b4de99..cb094388bb 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -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" &&
diff --git a/git-pull.sh b/git-pull.sh
index f380437997..ed04e7d8d8 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -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.
diff --git a/git-rebase.sh b/git-rebase.sh
index a7373c0532..546fa446fc 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -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 $?
diff --git a/git-repack.sh b/git-repack.sh
index f2c9071d11..17e24526c2 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -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"-*
diff --git a/git-resolve.sh b/git-resolve.sh
index 729ec65dc9..36b90e3849 100755
--- a/git-resolve.sh
+++ b/git-resolve.sh
@@ -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"
diff --git a/git-revert.sh b/git-revert.sh
index 066e67753e..6eab3c72df 100755
--- a/git-revert.sh
+++ b/git-revert.sh
@@ -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: $@)"
;;
diff --git a/git-send-email.perl b/git-send-email.perl
index 4a20310841..4c87c20c15 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -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
@@ -229,6 +231,9 @@ if (!defined $initial_reply_to && $prompting) {
}
if (!$smtp_server) {
+ $smtp_server = $repo->config('sendemail.smtpserver');
+}
+if (!$smtp_server) {
foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
if (-x $_) {
$smtp_server = $_;
@@ -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";
diff --git a/git-shortlog.perl b/git-shortlog.perl
index 0b14f833ee..334fec7477 100755
--- a/git-shortlog.perl
+++ b/git-shortlog.perl
@@ -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);
diff --git a/git-svn.perl b/git-svn.perl
index f5c7d46341..37ecc51787 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -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 $!;
}
diff --git a/git-svnimport.perl b/git-svnimport.perl
index 988514e293..cbaa8ab37c 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -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 f197169df2..af181d93b1 100644
--- 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 },
diff --git a/git.spec.in b/git.spec.in
index 6d900342e3..83268fc9d9 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -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
@@ -126,10 +126,10 @@ rm -rf $RPM_BUILD_ROOT
%files arch
%defattr(-,root,root)
-%doc Documentation/*arch*.txt
-%{_bindir}/*arch*
-%{!?_without_docs: %{_mandir}/man1/*arch*.1*}
-%{!?_without_docs: %doc Documentation/*arch*.html }
+%doc Documentation/git-archimport.txt
+%{_bindir}/git-archimport
+%{!?_without_docs: %{_mandir}/man1/git-archimport.1*}
+%{!?_without_docs: %doc Documentation/git-archimport.html }
%files email
%defattr(-,root,root)
diff --git a/gitweb/README b/gitweb/README
index 27c6dac143..e02e90f042 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -1,4 +1,5 @@
GIT web Interface
+=================
The one working on:
http://www.kernel.org/git/
@@ -6,7 +7,8 @@ The one working on:
From the git version 1.4.0 gitweb is bundled with git.
-How to configure gitweb for your local system:
+How to configure gitweb for your local system
+---------------------------------------------
You can specify the following configuration variables when building GIT:
* GITWEB_SITENAME
@@ -24,11 +26,54 @@ You can specify the following configuration variables when building GIT:
* GITWEB_LOGO
Points to the location where you put git-logo.png on your web server.
* GITWEB_CONFIG
- This file will be loaded using 'require'. If the environment
+ This file will be loaded using 'require' and can be used to override any
+ of the options above as well as some other options - see the top of
+ 'gitweb.cgi' for their full list and description. If the environment
$GITWEB_CONFIG is set when gitweb.cgi is executed the file in the
environment variable will be loaded instead of the file
specified when gitweb.cgi was created.
+
+Runtime gitweb configuration
+----------------------------
+
+You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
+(defaults to 'gitweb_config.perl' in the same directory as the CGI).
+See the top of 'gitweb.cgi' for the list of variables and some description.
+The most notable thing that is not configurable at compile time are the
+optional features, stored in the '%features' variable. You can find further
+description on how to reconfigure the default features setting in your
+`GITWEB_CONFIG` or per-project in `project.git/config` inside 'gitweb.cgi'.
+
+
+Webserver configuration
+-----------------------
+
+If you want to have one URL for both gitweb and your http://
+repositories, you can configure apache like this:
+
+<VirtualHost www:80>
+ ServerName git.domain.org
+ DocumentRoot /pub/git
+ RewriteEngine on
+ RewriteRule ^/(.*\.git/(?!/?(info|objects|refs)).*)?$ /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT]
+ SetEnv GITWEB_CONFIG /etc/gitweb.conf
+</VirtualHost>
+
+The above configuration expects your public repositories to live under
+/pub/git and will serve them as http://git.domain.org/dir-under-pub-git,
+both as cloneable GIT URL and as browseable gitweb interface.
+If you then start your git-daemon with --base-path=/pub/git --export-all
+then you can even use the git:// URL with exactly the same path.
+
+Setting the environment variable GITWEB_CONFIG will tell gitweb to use
+the named file (i.e. in this example /etc/gitweb.conf) as a
+configuration for gitweb. Perl variables defined in here will
+override the defaults given at the head of the gitweb.perl (or
+gitweb.cgi). Look at the comments in that file for information on
+which variables and what they mean.
+
+
Originally written by:
Kay Sievers <kay.sievers@vrfy.org>
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index eb9fc3804b..0eda98237e 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -16,6 +16,11 @@ a:hover, a:visited, a:active {
color: #880000;
}
+img.logo {
+ float: right;
+ border-width: 0px;
+}
+
div.page_header {
height: 25px;
padding: 8px;
@@ -173,6 +178,12 @@ table.blame {
border-collapse: collapse;
}
+table.blame td {
+ padding: 0px 5px;
+ font-size: 12px;
+ vertical-align: top;
+}
+
th {
padding: 2px 5px;
font-size: 12px;
@@ -322,6 +333,8 @@ div.index_include {
}
div.search {
+ font-size: 12px;
+ font-weight: normal;
margin: 4px 8px;
position: absolute;
top: 56px;
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 44991b1538..ec46b8091b 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -39,18 +39,31 @@ our $home_link_str = "++GITWEB_HOME_LINK_STR++";
# name of your site or organization to appear in page titles
# replace this with something more descriptive for clearer bookmarks
-our $site_name = "++GITWEB_SITENAME++" || $ENV{'SERVER_NAME'} || "Untitled";
+our $site_name = "++GITWEB_SITENAME++"
+ || ($ENV{'SERVER_NAME'} || "Untitled") . " Git";
+# filename of html text to include at top of each page
+our $site_header = "++GITWEB_SITE_HEADER++";
# html text to include at home page
our $home_text = "++GITWEB_HOMETEXT++";
-
-# URI of default stylesheet
-our $stylesheet = "++GITWEB_CSS++";
-# URI of GIT logo
+# filename of html text to include at bottom of each page
+our $site_footer = "++GITWEB_SITE_FOOTER++";
+
+# URI of stylesheets
+our @stylesheets = ("++GITWEB_CSS++");
+# URI of a single stylesheet, which can be overridden in GITWEB_CONFIG.
+our $stylesheet = undef;
+# URI of GIT logo (72x27 size)
our $logo = "++GITWEB_LOGO++";
# URI of GIT favicon, assumed to be image/png type
our $favicon = "++GITWEB_FAVICON++";
+# URI and label (title) of GIT logo link
+#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
+#our $logo_label = "git documentation";
+our $logo_url = "http://git.or.cz/";
+our $logo_label = "git homepage";
+
# source of projects list
our $projects_list = "++GITWEB_LIST++";
@@ -63,7 +76,7 @@ our $strict_export = "++GITWEB_STRICT_EXPORT++";
# list of git base URLs used for URL to where fetch project from,
# i.e. full URL is "$git_base_url/$project"
-our @git_base_url_list = ("++GITWEB_BASE_URL++");
+our @git_base_url_list = grep { $_ ne '' } ("++GITWEB_BASE_URL++");
# default blob_plain mimetype and default charset for text/plain blob
our $default_blob_plain_mimetype = 'text/plain';
@@ -87,21 +100,66 @@ our %feature = (
#
# use gitweb_check_feature(<feature>) to check if <feature> is enabled
+ # Enable the 'blame' blob view, showing the last commit that modified
+ # each line in the file. This can be very CPU-intensive.
+
+ # To enable system wide have in $GITWEB_CONFIG
+ # $feature{'blame'}{'default'} = [1];
+ # To have project specific config enable override in $GITWEB_CONFIG
+ # $feature{'blame'}{'override'} = 1;
+ # and in project config gitweb.blame = 0|1;
'blame' => {
'sub' => \&feature_blame,
'override' => 0,
'default' => [0]},
+ # Enable the 'snapshot' link, providing a compressed tarball of any
+ # tree. This can potentially generate high traffic if you have large
+ # project.
+
+ # To disable system wide have in $GITWEB_CONFIG
+ # $feature{'snapshot'}{'default'} = [undef];
+ # To have project specific config enable override in $GITWEB_CONFIG
+ # $feature{'blame'}{'override'} = 1;
+ # and in project config gitweb.snapshot = none|gzip|bzip2;
'snapshot' => {
'sub' => \&feature_snapshot,
'override' => 0,
# => [content-encoding, suffix, program]
'default' => ['x-gzip', 'gz', 'gzip']},
+ # Enable the pickaxe search, which will list the commits that modified
+ # a given string in a file. This can be practical and quite faster
+ # alternative to 'blame', but still potentially CPU-intensive.
+
+ # To enable system wide have in $GITWEB_CONFIG
+ # $feature{'pickaxe'}{'default'} = [1];
+ # To have project specific config enable override in $GITWEB_CONFIG
+ # $feature{'pickaxe'}{'override'} = 1;
+ # and in project config gitweb.pickaxe = 0|1;
'pickaxe' => {
'sub' => \&feature_pickaxe,
'override' => 0,
'default' => [1]},
+
+ # Make gitweb use an alternative format of the URLs which can be
+ # more readable and natural-looking: project name is embedded
+ # directly in the path and the query string contains other
+ # auxiliary information. All gitweb installations recognize
+ # URL in either format; this configures in which formats gitweb
+ # generates links.
+
+ # To enable system wide have in $GITWEB_CONFIG
+ # $feature{'pathinfo'}{'default'} = [1];
+ # Project specific override is not supported.
+
+ # Note that you will need to change the default location of CSS,
+ # favicon, logo and possibly other files to an absolute URL. Also,
+ # if gitweb.cgi serves as your indexfile, you will need to force
+ # $my_uri to contain the script name in your $GITWEB_CONFIG.
+ 'pathinfo' => {
+ 'override' => 0,
+ 'default' => [0]},
);
sub gitweb_check_feature {
@@ -112,15 +170,13 @@ sub gitweb_check_feature {
$feature{$name}{'override'},
@{$feature{$name}{'default'}});
if (!$override) { return @defaults; }
+ if (!defined $sub) {
+ warn "feature $name is not overrideable";
+ return @defaults;
+ }
return $sub->(@defaults);
}
-# To enable system wide have in $GITWEB_CONFIG
-# $feature{'blame'}{'default'} = [1];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'blame'}{'override'} = 1;
-# and in project config gitweb.blame = 0|1;
-
sub feature_blame {
my ($val) = git_get_project_config('blame', '--bool');
@@ -133,12 +189,6 @@ sub feature_blame {
return $_[0];
}
-# To disable system wide have in $GITWEB_CONFIG
-# $feature{'snapshot'}{'default'} = [undef];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'blame'}{'override'} = 1;
-# and in project config gitweb.snapshot = none|gzip|bzip2
-
sub feature_snapshot {
my ($ctype, $suffix, $command) = @_;
@@ -162,12 +212,6 @@ sub gitweb_have_snapshot {
return $have_snapshot;
}
-# To enable system wide have in $GITWEB_CONFIG
-# $feature{'pickaxe'}{'default'} = [1];
-# To have project specific config enable override in $GITWEB_CONFIG
-# $feature{'pickaxe'}{'override'} = 1;
-# and in project config gitweb.pickaxe = 0|1;
-
sub feature_pickaxe {
my ($val) = git_get_project_config('pickaxe', '--bool');
@@ -180,6 +224,22 @@ sub feature_pickaxe {
return ($_[0]);
}
+# checking HEAD file with -e is fragile if the repository was
+# initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
+# and then pruned.
+sub check_head_link {
+ my ($dir) = @_;
+ my $headfile = "$dir/HEAD";
+ return ((-e $headfile) ||
+ (-l $headfile && readlink($headfile) =~ /^refs\/heads\//));
+}
+
+sub check_export_ok {
+ my ($dir) = @_;
+ return (check_head_link($dir) &&
+ (!$export_ok || -e "$dir/$export_ok"));
+}
+
# rename detection options for git-diff and git-diff-tree
# - default is '-M', with the cost proportional to
# (number of removed files) * (number of new files).
@@ -212,7 +272,7 @@ our $project = $cgi->param('p');
if (defined $project) {
if (!validate_pathname($project) ||
!(-d "$projectroot/$project") ||
- !(-e "$projectroot/$project/HEAD") ||
+ !check_head_link("$projectroot/$project") ||
($export_ok && !(-e "$projectroot/$project/$export_ok")) ||
($strict_export && !project_in_list($project))) {
undef $project;
@@ -279,6 +339,13 @@ if (defined $searchtext) {
$searchtext = quotemeta $searchtext;
}
+our $searchtype = $cgi->param('st');
+if (defined $searchtype) {
+ if ($searchtype =~ m/[^a-z]/) {
+ die_error(undef, "Invalid searchtype parameter");
+ }
+}
+
# now read PATH_INFO and use it as alternative to parameters
sub evaluate_path_info {
return if defined $project;
@@ -289,7 +356,7 @@ sub evaluate_path_info {
# find which part of PATH_INFO is project
$project = $path_info;
$project =~ s,/+$,,;
- while ($project && !-e "$projectroot/$project/HEAD") {
+ while ($project && !check_head_link("$projectroot/$project")) {
$project =~ s,/*[^/]*$,,;
}
# validate project
@@ -343,6 +410,7 @@ my %actions = (
"log" => \&git_log,
"rss" => \&git_rss,
"search" => \&git_search,
+ "search_help" => \&git_search_help,
"shortlog" => \&git_shortlog,
"summary" => \&git_summary,
"tag" => \&git_tag,
@@ -375,6 +443,10 @@ exit;
sub href(%) {
my %params = @_;
+ my $href = $my_uri;
+
+ # XXX: Warning: If you touch this, check the search form for updating,
+ # too.
my @mapping = (
project => "p",
@@ -388,11 +460,25 @@ sub href(%) {
page => "pg",
order => "o",
searchtext => "s",
+ searchtype => "st",
);
my %mapping = @mapping;
$params{'project'} = $project unless exists $params{'project'};
+ my ($use_pathinfo) = gitweb_check_feature('pathinfo');
+ if ($use_pathinfo) {
+ # use PATH_INFO for project name
+ $href .= "/$params{'project'}" if defined $params{'project'};
+ delete $params{'project'};
+
+ # Summary just uses the project path URL
+ if (defined $params{'action'} && $params{'action'} eq 'summary') {
+ delete $params{'action'};
+ }
+ }
+
+ # now encode the parameters explicitly
my @result = ();
for (my $i = 0; $i < @mapping; $i += 2) {
my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
@@ -400,7 +486,9 @@ sub href(%) {
push @result, $symbol . "=" . esc_param($params{$name});
}
}
- return "$my_uri?" . join(';', @result);
+ $href .= "?" . join(';', @result) if scalar @result;
+
+ return $href;
}
@@ -440,6 +528,12 @@ sub validate_refname {
return $input;
}
+# very thin wrapper for decode("utf8", $str, Encode::FB_DEFAULT);
+sub to_utf8 {
+ my $str = shift;
+ return decode("utf8", $str, Encode::FB_DEFAULT);
+}
+
# quote unsafe chars, but keep the slash, even when it's not
# correct, but quoted slashes look too horrible in bookmarks
sub esc_param {
@@ -462,9 +556,10 @@ sub esc_url {
# replace invalid utf8 character with SUBSTITUTION sequence
sub esc_html {
my $str = shift;
- $str = decode("utf8", $str, Encode::FB_DEFAULT);
+ $str = to_utf8($str);
$str = escapeHTML($str);
$str =~ s/\014/^L/g; # escape FORM FEED (FF) character (e.g. in COPYING file)
+ $str =~ s/\033/^[/g; # "escape" ESCAPE (\e) character (e.g. commit 20a3847d8a5032ce41f90dcc68abfb36e6fee9b1)
return $str;
}
@@ -664,7 +759,7 @@ sub format_subject_html {
if (length($short) < length($long)) {
return $cgi->a({-href => $href, -class => "list subject",
- -title => decode("utf8", $long, Encode::FB_DEFAULT)},
+ -title => to_utf8($long)},
esc_html($short) . $extra);
} else {
return $cgi->a({-href => $href, -class => "list subject"},
@@ -815,8 +910,7 @@ sub git_get_projects_list {
my $subdir = substr($File::Find::name, $pfxlen + 1);
# we check related file in $projectroot
- if (-e "$projectroot/$subdir/HEAD" && (!$export_ok ||
- -e "$projectroot/$subdir/$export_ok")) {
+ if (check_export_ok("$projectroot/$subdir")) {
push @list, { path => $subdir };
$File::Find::prune = 1;
}
@@ -837,11 +931,10 @@ sub git_get_projects_list {
if (!defined $path) {
next;
}
- if (-e "$projectroot/$path/HEAD" && (!$export_ok ||
- -e "$projectroot/$path/$export_ok")) {
+ if (check_export_ok("$projectroot/$path")) {
my $pr = {
path => $path,
- owner => decode("utf8", $owner, Encode::FB_DEFAULT),
+ owner => to_utf8($owner),
};
push @list, $pr
}
@@ -870,7 +963,7 @@ sub git_get_project_owner {
$pr = unescape($pr);
$ow = unescape($ow);
if ($pr eq $project) {
- $owner = decode("utf8", $ow, Encode::FB_DEFAULT);
+ $owner = to_utf8($ow);
last;
}
}
@@ -883,6 +976,24 @@ sub git_get_project_owner {
return $owner;
}
+sub git_get_last_activity {
+ my ($path) = @_;
+ my $fd;
+
+ $git_dir = "$projectroot/$path";
+ open($fd, "-|", git_cmd(), 'for-each-ref',
+ '--format=%(refname) %(committer)',
+ '--sort=-committerdate',
+ 'refs/heads') or return;
+ my $most_recent = <$fd>;
+ close $fd or return;
+ if ($most_recent =~ / (\d+) [-+][01]\d\d\d$/) {
+ my $timestamp = $1;
+ my $age = time - $timestamp;
+ return ($age, age_string($age));
+ }
+}
+
sub git_get_references {
my $type = shift || "";
my %refs;
@@ -948,6 +1059,9 @@ sub parse_date {
$date{'hour_local'} = $hour;
$date{'minute_local'} = $min;
$date{'tz_local'} = $tz;
+ $date{'iso-tz'} = sprintf ("%04d-%02d-%02d %02d:%02d:%02d %s",
+ 1900+$year, $mon+1, $mday,
+ $hour, $min, $sec, $tz);
return %date;
}
@@ -996,12 +1110,11 @@ sub parse_commit {
if (defined $commit_text) {
@commit_lines = @$commit_text;
} else {
- $/ = "\0";
+ local $/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", "--max-count=1", $commit_id
or return;
@commit_lines = split '\n', <$fd>;
close $fd or return;
- $/ = "\n";
pop @commit_lines;
}
my $header = shift @commit_lines;
@@ -1061,6 +1174,9 @@ sub parse_commit {
last;
}
}
+ if ($co{'title'} eq "") {
+ $co{'title'} = $co{'title_short'} = '(no commit message)';
+ }
# remove added spaces
foreach my $line (@commit_lines) {
$line =~ s/^ //;
@@ -1232,7 +1348,7 @@ sub get_file_owner {
}
my $owner = $gcos;
$owner =~ s/[,;].*$//;
- return decode("utf8", $owner, Encode::FB_DEFAULT);
+ return to_utf8($owner);
}
## ......................................................................
@@ -1313,7 +1429,7 @@ sub git_header_html {
my $status = shift || "200 OK";
my $expires = shift;
- my $title = "$site_name git";
+ my $title = "$site_name";
if (defined $project) {
$title .= " - $project";
if (defined $action) {
@@ -1351,8 +1467,17 @@ sub git_header_html {
<meta name="generator" content="gitweb/$version git/$git_version"/>
<meta name="robots" content="index, nofollow"/>
<title>$title</title>
-<link rel="stylesheet" type="text/css" href="$stylesheet"/>
EOF
+# print out each stylesheet that exist
+ if (defined $stylesheet) {
+#provides backwards capability for those people who define style sheet in a config file
+ print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
+ } else {
+ foreach my $stylesheet (@stylesheets) {
+ next unless $stylesheet;
+ print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
+ }
+ }
if (defined $project) {
printf('<link rel="alternate" title="%s log" '.
'href="%s" type="application/rss+xml"/>'."\n",
@@ -1370,11 +1495,18 @@ EOF
}
print "</head>\n" .
- "<body>\n" .
- "<div class=\"page_header\">\n" .
- "<a href=\"http://www.kernel.org/pub/software/scm/git/docs/\" title=\"git documentation\">" .
- "<img src=\"$logo\" width=\"72\" height=\"27\" alt=\"git\" style=\"float:right; border-width:0px;\"/>" .
- "</a>\n";
+ "<body>\n";
+
+ if (-f $site_header) {
+ open (my $fd, $site_header);
+ print <$fd>;
+ close $fd;
+ }
+
+ print "<div class=\"page_header\">\n" .
+ $cgi->a({-href => esc_url($logo_url),
+ -title => $logo_label},
+ qq(<img src="$logo" width="72" height="27" alt="git" class="logo"/>));
print $cgi->a({-href => esc_url($home_link)}, $home_link_str) . " / ";
if (defined $project) {
print $cgi->a({-href => href(action=>"summary")}, esc_html($project));
@@ -1395,11 +1527,16 @@ EOF
}
$cgi->param("a", "search");
$cgi->param("h", $search_hash);
+ $cgi->param("p", $project);
print $cgi->startform(-method => "get", -action => $my_uri) .
"<div class=\"search\">\n" .
$cgi->hidden(-name => "p") . "\n" .
$cgi->hidden(-name => "a") . "\n" .
$cgi->hidden(-name => "h") . "\n" .
+ $cgi->popup_menu(-name => 'st', -default => 'commit',
+ -values => ['commit', 'author', 'committer', 'pickaxe']) .
+ $cgi->sup($cgi->a({-href => href(action=>"search_help")}, "?")) .
+ " search:\n",
$cgi->textfield(-name => "s", -value => $searchtext) . "\n" .
"</div>" .
$cgi->end_form() . "\n";
@@ -1422,8 +1559,15 @@ sub git_footer_html {
print $cgi->a({-href => href(project=>undef, action=>"project_index"),
-class => "rss_logo"}, "TXT") . "\n";
}
- print "</div>\n" .
- "</body>\n" .
+ print "</div>\n" ;
+
+ if (-f $site_footer) {
+ open (my $fd, $site_footer);
+ print <$fd>;
+ close $fd;
+ }
+
+ print "</body>\n" .
"</html>";
}
@@ -1548,17 +1692,16 @@ sub git_print_page_path {
my $type = shift;
my $hb = shift;
- if (!defined $name) {
- print "<div class=\"page_path\">/</div>\n";
- } else {
+
+ print "<div class=\"page_path\">";
+ print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
+ -title => 'tree root'}, "[$project]");
+ print " / ";
+ if (defined $name) {
my @dirname = split '/', $name;
my $basename = pop @dirname;
my $fullname = '';
- print "<div class=\"page_path\">";
- print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
- -title => 'tree root'}, "[$project]");
- print " / ";
foreach my $dir (@dirname) {
$fullname .= ($fullname ? '/' : '') . $dir;
print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
@@ -1574,11 +1717,12 @@ sub git_print_page_path {
print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
hash_base=>$hb),
-title => $name}, esc_html($basename));
+ print " / ";
} else {
print esc_html($basename);
}
- print "<br/></div>\n";
}
+ print "<br/></div>\n";
}
# sub git_print_log (\@;%) {
@@ -1631,15 +1775,6 @@ sub git_print_log ($;%) {
}
}
-sub git_print_simplified_log {
- my $log = shift;
- my $remove_title = shift;
-
- git_print_log($log,
- -final_empty_line=> 1,
- -remove_title => $remove_title);
-}
-
# print tree entry (row of git_tree), but without encompassing <tr> element
sub git_print_tree_entry {
my ($t, $basedir, $hash_base, $have_blame) = @_;
@@ -1655,26 +1790,28 @@ sub git_print_tree_entry {
if ($t->{'type'} eq "blob") {
print "<td class=\"list\">" .
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key),
- -class => "list"}, esc_html($t->{'name'})) . "</td>\n";
+ file_name=>"$basedir$t->{'name'}", %base_key),
+ -class => "list"}, esc_html($t->{'name'})) . "</td>\n";
print "<td class=\"link\">";
+ print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "blob");
if ($have_blame) {
- print $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
- "blame");
+ print " | " .
+ $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "blame");
}
if (defined $hash_base) {
- if ($have_blame) {
- print " | ";
- }
- print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+ print " | " .
+ $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
"history");
}
print " | " .
$cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base,
- file_name=>"$basedir$t->{'name'}")},
- "raw");
+ file_name=>"$basedir$t->{'name'}")},
+ "raw");
print "</td>\n";
} elsif ($t->{'type'} eq "tree") {
@@ -1684,8 +1821,12 @@ sub git_print_tree_entry {
esc_html($t->{'name'}));
print "</td>\n";
print "<td class=\"link\">";
+ print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+ file_name=>"$basedir$t->{'name'}", %base_key)},
+ "tree");
if (defined $hash_base) {
- print $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+ print " | " .
+ $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
file_name=>"$basedir$t->{'name'}")},
"history");
}
@@ -1742,7 +1883,7 @@ sub git_difftree_body {
print "<td>";
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'}));
+ -class => "list"}, esc_html($diff{'file'}));
print "</td>\n";
print "<td>$mode_chng</td>\n";
print "<td class=\"link\">";
@@ -1768,12 +1909,15 @@ sub git_difftree_body {
print $cgi->a({-href => "#patch$patchno"}, "patch");
print " | ";
}
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
+ hash_base=>$parent, file_name=>$diff{'file'})},
+ "blob") . " | ";
print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
- file_name=>$diff{'file'})},
- "blame") . " | ";
+ file_name=>$diff{'file'})},
+ "blame") . " | ";
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
- file_name=>$diff{'file'})},
- "history");
+ file_name=>$diff{'file'})},
+ "history");
print "</td>\n";
} elsif ($diff{'status'} eq "M" || $diff{'status'} eq "T") { # modified, or type changed
@@ -1794,8 +1938,8 @@ sub git_difftree_body {
}
print "<td>";
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
- hash_base=>$hash, file_name=>$diff{'file'}),
- -class => "list"}, esc_html($diff{'file'}));
+ hash_base=>$hash, file_name=>$diff{'file'}),
+ -class => "list"}, esc_html($diff{'file'}));
print "</td>\n";
print "<td>$mode_chnge</td>\n";
print "<td class=\"link\">";
@@ -1806,19 +1950,22 @@ sub git_difftree_body {
print $cgi->a({-href => "#patch$patchno"}, "patch");
} else {
print $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
- hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'file'})},
- "diff");
+ hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+ hash_base=>$hash, hash_parent_base=>$parent,
+ file_name=>$diff{'file'})},
+ "diff");
}
print " | ";
}
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
+ hash_base=>$hash, file_name=>$diff{'file'})},
+ "blob") . " | ";
print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
- file_name=>$diff{'file'})},
- "blame") . " | ";
+ file_name=>$diff{'file'})},
+ "blame") . " | ";
print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
- file_name=>$diff{'file'})},
- "history");
+ file_name=>$diff{'file'})},
+ "history");
print "</td>\n";
} elsif ($diff{'status'} eq "R" || $diff{'status'} eq "C") { # renamed or copied
@@ -1846,19 +1993,22 @@ sub git_difftree_body {
print $cgi->a({-href => "#patch$patchno"}, "patch");
} else {
print $cgi->a({-href => href(action=>"blobdiff",
- hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
- hash_base=>$hash, hash_parent_base=>$parent,
- file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
- "diff");
+ hash=>$diff{'to_id'}, hash_parent=>$diff{'from_id'},
+ hash_base=>$hash, hash_parent_base=>$parent,
+ file_name=>$diff{'to_file'}, file_parent=>$diff{'from_file'})},
+ "diff");
}
print " | ";
}
+ print $cgi->a({-href => href(action=>"blob", hash=>$diff{'from_id'},
+ hash_base=>$parent, file_name=>$diff{'from_file'})},
+ "blob") . " | ";
print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
- file_name=>$diff{'from_file'})},
- "blame") . " | ";
+ file_name=>$diff{'from_file'})},
+ "blame") . " | ";
print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
- file_name=>$diff{'from_file'})},
- "history");
+ file_name=>$diff{'from_file'})},
+ "history");
print "</td>\n";
} # we should not encounter Unmerged (U) or Unknown (X) status
@@ -1910,14 +2060,14 @@ sub git_patchset_body {
print "<div class=\"diff_info\">" . file_type($diffinfo->{'to_mode'}) . ":" .
$cgi->a({-href => href(action=>"blob", hash_base=>$hash,
hash=>$diffinfo->{'to_id'}, file_name=>$diffinfo->{'file'})},
- $diffinfo->{'to_id'}) . "(new)" .
+ $diffinfo->{'to_id'}) . " (new)" .
"</div>\n"; # class="diff_info"
} elsif ($diffinfo->{'status'} eq "D") { # deleted
print "<div class=\"diff_info\">" . file_type($diffinfo->{'from_mode'}) . ":" .
$cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
hash=>$diffinfo->{'from_id'}, file_name=>$diffinfo->{'file'})},
- $diffinfo->{'from_id'}) . "(deleted)" .
+ $diffinfo->{'from_id'}) . " (deleted)" .
"</div>\n"; # class="diff_info"
} elsif ($diffinfo->{'status'} eq "R" || # renamed
@@ -2020,9 +2170,12 @@ sub git_shortlog_body {
href(action=>"commit", hash=>$commit), $ref);
print "</td>\n" .
"<td class=\"link\">" .
+ $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
$cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
- $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") . " | " .
- $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
+ $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
+ if (gitweb_have_snapshot()) {
+ print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
+ }
print "</td>\n" .
"</tr>\n";
}
@@ -2212,16 +2365,11 @@ sub git_project_list {
die_error(undef, "No projects found");
}
foreach my $pr (@list) {
- my $head = git_get_head_hash($pr->{'path'});
- if (!defined $head) {
- next;
- }
- $git_dir = "$projectroot/$pr->{'path'}";
- my %co = parse_commit($head);
- if (!%co) {
+ my (@aa) = git_get_last_activity($pr->{'path'});
+ unless (@aa) {
next;
}
- $pr->{'commit'} = \%co;
+ ($pr->{'age'}, $pr->{'age_string'}) = @aa;
if (!defined $pr->{'descr'}) {
my $descr = git_get_project_description($pr->{'path'}) || "";
$pr->{'descr'} = chop_str($descr, 25, 5);
@@ -2271,7 +2419,7 @@ sub git_project_list {
"</th>\n";
}
if ($order eq "age") {
- @projects = sort {$a->{'commit'}{'age'} <=> $b->{'commit'}{'age'}} @projects;
+ @projects = sort {$a->{'age'} <=> $b->{'age'}} @projects;
print "<th>Last Change</th>\n";
} else {
print "<th>" .
@@ -2293,8 +2441,8 @@ sub git_project_list {
-class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
"<td>" . esc_html($pr->{'descr'}) . "</td>\n" .
"<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
- print "<td class=\"". age_class($pr->{'commit'}{'age'}) . "\">" .
- $pr->{'commit'}{'age_string'} . "</td>\n" .
+ print "<td class=\"". age_class($pr->{'age'}) . "\">" .
+ $pr->{'age_string'} . "</td>\n" .
"<td class=\"link\">" .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary") . " | " .
$cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
@@ -2372,6 +2520,14 @@ sub git_summary {
}
print "</table>\n";
+ if (-s "$projectroot/$project/README.html") {
+ if (open my $fd, "$projectroot/$project/README.html") {
+ print "<div class=\"title\">readme</div>\n";
+ print $_ while (<$fd>);
+ close $fd;
+ }
+ }
+
open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17",
git_get_head_hash($project)
or die_error(undef, "Open git-rev-list failed");
@@ -2450,7 +2606,8 @@ sub git_blame2 {
if ($ftype !~ "blob") {
die_error("400 Bad Request", "Object is not a blob");
}
- open ($fd, "-|", git_cmd(), "blame", '-l', '--', $file_name, $hash_base)
+ open ($fd, "-|", git_cmd(), "blame", '-p', '--',
+ $file_name, $hash_base)
or die_error(undef, "Open git-blame failed");
git_header_html();
my $formats_nav =
@@ -2474,25 +2631,52 @@ sub git_blame2 {
<table class="blame">
<tr><th>Commit</th><th>Line</th><th>Data</th></tr>
HTML
- while (<$fd>) {
- /^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/;
- my $full_rev = $1;
+ my %metainfo = ();
+ while (1) {
+ $_ = <$fd>;
+ last unless defined $_;
+ my ($full_rev, $orig_lineno, $lineno, $group_size) =
+ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/;
+ if (!exists $metainfo{$full_rev}) {
+ $metainfo{$full_rev} = {};
+ }
+ my $meta = $metainfo{$full_rev};
+ while (<$fd>) {
+ last if (s/^\t//);
+ if (/^(\S+) (.*)$/) {
+ $meta->{$1} = $2;
+ }
+ }
+ my $data = $_;
my $rev = substr($full_rev, 0, 8);
- my $lineno = $2;
- my $data = $3;
-
- if (!defined $last_rev) {
- $last_rev = $full_rev;
- } elsif ($last_rev ne $full_rev) {
- $last_rev = $full_rev;
+ my $author = $meta->{'author'};
+ my %date = parse_date($meta->{'author-time'},
+ $meta->{'author-tz'});
+ my $date = $date{'iso-tz'};
+ if ($group_size) {
$current_color = ++$current_color % $num_colors;
}
print "<tr class=\"$rev_color[$current_color]\">\n";
- print "<td class=\"sha1\">" .
- $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)},
- esc_html($rev)) . "</td>\n";
- print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" .
- esc_html($lineno) . "</a></td>\n";
+ if ($group_size) {
+ print "<td class=\"sha1\"";
+ print " title=\"$author, $date\"";
+ print " rowspan=\"$group_size\"" if ($group_size > 1);
+ print ">";
+ print $cgi->a({-href => href(action=>"commit",
+ hash=>$full_rev,
+ file_name=>$file_name)},
+ esc_html($rev));
+ print "</td>\n";
+ }
+ my $blamed = href(action => 'blame',
+ file_name => $meta->{'filename'},
+ hash_base => $full_rev);
+ print "<td class=\"linenr\">";
+ print $cgi->a({ -href => "$blamed#l$orig_lineno",
+ -id => "l$lineno",
+ -class => "linenr" },
+ esc_html($lineno));
+ print "</td>";
print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
print "</tr>\n";
}
@@ -2765,7 +2949,7 @@ sub git_tree {
my $refs = git_get_references();
my $ref = format_ref_marker($refs, $hash_base);
git_header_html();
- my $base = "";
+ my $basedir = '';
my ($have_blame) = gitweb_check_feature('blame');
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
my @views_nav = ();
@@ -2782,7 +2966,7 @@ sub git_tree {
# FIXME: Should be available when we have no hash base as well.
push @views_nav,
$cgi->a({-href => href(action=>"snapshot", hash=>$hash)},
- "snapshot");
+ "snapshot");
}
git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
@@ -2793,12 +2977,39 @@ sub git_tree {
print "<div class=\"title\">$hash</div>\n";
}
if (defined $file_name) {
- $base = esc_html("$file_name/");
+ $basedir = $file_name;
+ if ($basedir ne '' && substr($basedir, -1) ne '/') {
+ $basedir .= '/';
+ }
}
git_print_page_path($file_name, 'tree', $hash_base);
print "<div class=\"page_body\">\n";
print "<table cellspacing=\"0\">\n";
my $alternate = 1;
+ # '..' (top directory) link if possible
+ if (defined $hash_base &&
+ defined $file_name && $file_name =~ m![^/]+$!) {
+ if ($alternate) {
+ print "<tr class=\"dark\">\n";
+ } else {
+ print "<tr class=\"light\">\n";
+ }
+ $alternate ^= 1;
+
+ my $up = $file_name;
+ $up =~ s!/?[^/]+$!!;
+ undef $up unless $up;
+ # based on git_print_tree_entry
+ print '<td class="mode">' . mode_str('040000') . "</td>\n";
+ print '<td class="list">';
+ print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base,
+ file_name=>$up)},
+ "..");
+ print "</td>\n";
+ print "<td class=\"link\"></td>\n";
+
+ print "</tr>\n";
+ }
foreach my $line (@entries) {
my %t = parse_ls_tree_line($line, -z => 1);
@@ -2809,7 +3020,7 @@ sub git_tree {
}
$alternate ^= 1;
- git_print_tree_entry(\%t, $base, $hash_base, $have_blame);
+ git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame);
print "</tr>\n";
}
@@ -2837,9 +3048,12 @@ sub git_snapshot {
-content_disposition => 'inline; filename="' . "$filename" . '"',
-status => '200 OK');
- my $git_command = git_cmd_str();
- open my $fd, "-|", "$git_command tar-tree $hash \'$project\' | $command" or
- die_error(undef, "Execute git-tar-tree failed.");
+ my $git = git_cmd_str();
+ my $name = $project;
+ $name =~ s/\047/\047\\\047\047/g;
+ open my $fd, "-|",
+ "$git archive --format=tar --prefix=\'$name\'/ $hash | $command"
+ or die_error(undef, "Execute git-tar-tree failed.");
binmode STDOUT, ':raw';
print <$fd>;
binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
@@ -2897,7 +3111,7 @@ sub git_log {
"</div>\n";
print "<div class=\"log_body\">\n";
- git_print_simplified_log($co{'comment'});
+ git_print_log($co{'comment'}, -final_empty_line=> 1);
print "</div>\n";
}
git_footer_html();
@@ -2915,7 +3129,8 @@ sub git_commit {
if (!defined $parent) {
$parent = "--root";
}
- open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts, $parent, $hash
+ open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
+ @diff_opts, $parent, $hash
or die_error(undef, "Open git-diff-tree failed");
my @difftree = map { chomp; $_ } <$fd>;
close $fd or die_error(undef, "Reading git-diff-tree failed");
@@ -2936,13 +3151,8 @@ sub git_commit {
$cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
"blame");
}
- if (defined $co{'parent'}) {
- push @views_nav,
- $cgi->a({-href => href(action=>"shortlog", hash=>$hash)}, "shortlog"),
- $cgi->a({-href => href(action=>"log", hash=>$hash)}, "log");
- }
git_header_html(undef, $expires);
- git_print_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff',
+ git_print_page_nav('commit', '',
$hash, $co{'tree'}, $hash,
join (' | ', @views_nav));
@@ -3186,6 +3396,51 @@ sub git_commitdiff {
if (!%co) {
die_error(undef, "Unknown commit object");
}
+
+ # we need to prepare $formats_nav before any parameter munging
+ my $formats_nav;
+ if ($format eq 'html') {
+ $formats_nav =
+ $cgi->a({-href => href(action=>"commitdiff_plain",
+ hash=>$hash, hash_parent=>$hash_parent)},
+ "raw");
+
+ if (defined $hash_parent) {
+ # commitdiff with two commits given
+ my $hash_parent_short = $hash_parent;
+ if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
+ $hash_parent_short = substr($hash_parent, 0, 7);
+ }
+ $formats_nav .=
+ ' (from: ' .
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$hash_parent)},
+ esc_html($hash_parent_short)) .
+ ')';
+ } elsif (!$co{'parent'}) {
+ # --root commitdiff
+ $formats_nav .= ' (initial)';
+ } elsif (scalar @{$co{'parents'}} == 1) {
+ # single parent commit
+ $formats_nav .=
+ ' (parent: ' .
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$co{'parent'})},
+ esc_html(substr($co{'parent'}, 0, 7))) .
+ ')';
+ } else {
+ # merge commit
+ $formats_nav .=
+ ' (merge: ' .
+ join(' ', map {
+ $cgi->a({-href => href(action=>"commitdiff",
+ hash=>$_)},
+ esc_html(substr($_, 0, 7)));
+ } @{$co{'parents'}} ) .
+ ')';
+ }
+ }
+
if (!defined $hash_parent) {
$hash_parent = $co{'parent'} || '--root';
}
@@ -3195,6 +3450,7 @@ sub git_commitdiff {
my @difftree;
if ($format eq 'html') {
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+ "--no-commit-id",
"--patch-with-raw", "--full-index", $hash_parent, $hash
or die_error(undef, "Open git-diff-tree failed");
@@ -3223,19 +3479,17 @@ sub git_commitdiff {
if ($format eq 'html') {
my $refs = git_get_references();
my $ref = format_ref_marker($refs, $co{'id'});
- my $formats_nav =
- $cgi->a({-href => href(action=>"commitdiff_plain",
- hash=>$hash, hash_parent=>$hash_parent)},
- "raw");
git_header_html(undef, $expires);
git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);
git_print_authorship(\%co);
print "<div class=\"page_body\">\n";
- print "<div class=\"log\">\n";
- git_print_simplified_log($co{'comment'}, 1); # skip title
- print "</div>\n"; # class="log"
+ if (@{$co{'comment'}} > 1) {
+ print "<div class=\"log\">\n";
+ git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1);
+ print "</div>\n"; # class="log"
+ }
} elsif ($format eq 'plain') {
my $refs = git_get_references("tags");
@@ -3367,18 +3621,8 @@ sub git_search {
die_error(undef, "Unknown commit object");
}
- my $commit_search = 1;
- my $author_search = 0;
- my $committer_search = 0;
- my $pickaxe_search = 0;
- if ($searchtext =~ s/^author\\://i) {
- $author_search = 1;
- } elsif ($searchtext =~ s/^committer\\://i) {
- $committer_search = 1;
- } elsif ($searchtext =~ s/^pickaxe\\://i) {
- $commit_search = 0;
- $pickaxe_search = 1;
-
+ $searchtype ||= 'commit';
+ if ($searchtype eq 'pickaxe') {
# pickaxe may take all resources of your box and run for several minutes
# with every query - so decide by yourself how public you make this feature
my ($have_pickaxe) = gitweb_check_feature('pickaxe');
@@ -3386,23 +3630,24 @@ sub git_search {
die_error('403 Permission denied', "Permission denied");
}
}
+
git_header_html();
git_print_page_nav('','', $hash,$co{'tree'},$hash);
git_print_header_div('commit', esc_html($co{'title'}), $hash);
print "<table cellspacing=\"0\">\n";
my $alternate = 1;
- if ($commit_search) {
+ if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') {
$/ = "\0";
open my $fd, "-|", git_cmd(), "rev-list", "--header", "--parents", $hash or next;
while (my $commit_text = <$fd>) {
if (!grep m/$searchtext/i, $commit_text) {
next;
}
- if ($author_search && !grep m/\nauthor .*$searchtext/i, $commit_text) {
+ if ($searchtype eq 'author' && !grep m/\nauthor .*$searchtext/i, $commit_text) {
next;
}
- if ($committer_search && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
+ if ($searchtype eq 'committer' && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
next;
}
my @commit_lines = split "\n", $commit_text;
@@ -3444,7 +3689,7 @@ sub git_search {
close $fd;
}
- if ($pickaxe_search) {
+ if ($searchtype eq 'pickaxe') {
$/ = "\n";
my $git_command = git_cmd_str();
open my $fd, "-|", "$git_command rev-list $hash | " .
@@ -3504,6 +3749,31 @@ sub git_search {
git_footer_html();
}
+sub git_search_help {
+ git_header_html();
+ git_print_page_nav('','', $hash,$hash,$hash);
+ print <<EOT;
+<dl>
+<dt><b>commit</b></dt>
+<dd>The commit messages and authorship information will be scanned for the given string.</dd>
+<dt><b>author</b></dt>
+<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given string.</dd>
+<dt><b>committer</b></dt>
+<dd>Name and e-mail of the committer and date of commit will be scanned for the given string.</dd>
+EOT
+ my ($have_pickaxe) = gitweb_check_feature('pickaxe');
+ if ($have_pickaxe) {
+ print <<EOT;
+<dt><b>pickaxe</b></dt>
+<dd>All commits that caused the string to appear or disappear from any file (changes that
+added, removed or "modified" the string) will be listed. This search can take a while and
+takes a lot of strain on the server, so please use it wisely.</dd>
+EOT
+ }
+ print "</dl>\n";
+ git_footer_html();
+}
+
sub git_shortlog {
my $head = git_get_head_hash($project);
if (!defined $hash) {
@@ -3585,7 +3855,7 @@ XML
"<![CDATA[\n";
my $comment = $co{'comment'};
foreach my $line (@$comment) {
- $line = decode("utf8", $line, Encode::FB_DEFAULT);
+ $line = to_utf8($line);
print "$line<br/>\n";
}
print "<br/>\n";
@@ -3594,7 +3864,7 @@ XML
next;
}
my $file = esc_html(unquote($7));
- $file = decode("utf8", $file, Encode::FB_DEFAULT);
+ $file = to_utf8($file);
print "$file<br/>\n";
}
print "]]>\n" .
@@ -3612,7 +3882,7 @@ sub git_opml {
<?xml version="1.0" encoding="utf-8"?>
<opml version="1.0">
<head>
- <title>$site_name Git OPML Export</title>
+ <title>$site_name OPML Export</title>
</head>
<body>
<outline text="git RSS feeds">
diff --git a/grep.c b/grep.c
index c411ddd4d5..0fc078ec0a 100644
--- a/grep.c
+++ b/grep.c
@@ -34,7 +34,7 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
}
}
-static struct grep_expr *compile_pattern_expr(struct grep_pat **);
+static struct grep_expr *compile_pattern_or(struct grep_pat **);
static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
{
struct grep_pat *p;
@@ -52,7 +52,7 @@ static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
return x;
case GREP_OPEN_PAREN:
*list = p->next;
- x = compile_pattern_expr(list);
+ x = compile_pattern_or(list);
if (!x)
return NULL;
if (!*list || (*list)->token != GREP_CLOSE_PAREN)
@@ -138,6 +138,9 @@ void compile_grep_patterns(struct grep_opt *opt)
{
struct grep_pat *p;
+ if (opt->all_match)
+ opt->extended = 1;
+
for (p = opt->pattern_list; p; p = p->next) {
switch (p->token) {
case GREP_PATTERN: /* atom */
@@ -309,40 +312,63 @@ static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol
return hit;
}
-static int match_expr_eval(struct grep_opt *opt,
+static int match_expr_eval(struct grep_opt *o,
struct grep_expr *x,
char *bol, char *eol,
- enum grep_context ctx)
+ enum grep_context ctx,
+ int collect_hits)
{
+ int h = 0;
+
switch (x->node) {
case GREP_NODE_ATOM:
- return match_one_pattern(opt, x->u.atom, bol, eol, ctx);
+ h = match_one_pattern(o, x->u.atom, bol, eol, ctx);
break;
case GREP_NODE_NOT:
- return !match_expr_eval(opt, x->u.unary, bol, eol, ctx);
+ h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0);
+ break;
case GREP_NODE_AND:
- return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) &&
- match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
+ if (!collect_hits)
+ return (match_expr_eval(o, x->u.binary.left,
+ bol, eol, ctx, 0) &&
+ match_expr_eval(o, x->u.binary.right,
+ bol, eol, ctx, 0));
+ h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
+ h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0);
+ break;
case GREP_NODE_OR:
- return (match_expr_eval(opt, x->u.binary.left, bol, eol, ctx) ||
- match_expr_eval(opt, x->u.binary.right, bol, eol, ctx));
+ if (!collect_hits)
+ return (match_expr_eval(o, x->u.binary.left,
+ bol, eol, ctx, 0) ||
+ match_expr_eval(o, x->u.binary.right,
+ bol, eol, ctx, 0));
+ h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0);
+ x->u.binary.left->hit |= h;
+ h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
+ break;
+ default:
+ die("Unexpected node type (internal error) %d\n", x->node);
}
- die("Unexpected node type (internal error) %d\n", x->node);
+ if (collect_hits)
+ x->hit |= h;
+ return h;
}
static int match_expr(struct grep_opt *opt, char *bol, char *eol,
- enum grep_context ctx)
+ enum grep_context ctx, int collect_hits)
{
struct grep_expr *x = opt->pattern_expression;
- return match_expr_eval(opt, x, bol, eol, ctx);
+ return match_expr_eval(opt, x, bol, eol, ctx, collect_hits);
}
static int match_line(struct grep_opt *opt, char *bol, char *eol,
- enum grep_context ctx)
+ enum grep_context ctx, int collect_hits)
{
struct grep_pat *p;
if (opt->extended)
- return match_expr(opt, bol, eol, ctx);
+ return match_expr(opt, bol, eol, ctx, collect_hits);
+
+ /* we do not call with collect_hits without being extended */
for (p = opt->pattern_list; p; p = p->next) {
if (match_one_pattern(opt, p, bol, eol, ctx))
return 1;
@@ -350,7 +376,8 @@ static int match_line(struct grep_opt *opt, char *bol, char *eol,
return 0;
}
-int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
+static int grep_buffer_1(struct grep_opt *opt, const char *name,
+ char *buf, unsigned long size, int collect_hits)
{
char *bol = buf;
unsigned long left = size;
@@ -386,7 +413,7 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
while (left) {
char *eol, ch;
- int hit = 0;
+ int hit;
eol = end_of_line(bol, &left);
ch = *eol;
@@ -395,9 +422,12 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
ctx = GREP_CONTEXT_BODY;
- hit = match_line(opt, bol, eol, ctx);
+ hit = match_line(opt, bol, eol, ctx, collect_hits);
*eol = ch;
+ if (collect_hits)
+ goto next_line;
+
/* "grep -v -e foo -e bla" should list lines
* that do not have either, so inversion should
* be done outside.
@@ -477,6 +507,8 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
}
free(prev);
+ if (collect_hits)
+ return 0;
if (opt->status_only)
return 0;
@@ -496,3 +528,49 @@ int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long
return !!last_hit;
}
+static void clr_hit_marker(struct grep_expr *x)
+{
+ /* All-hit markers are meaningful only at the very top level
+ * OR node.
+ */
+ while (1) {
+ x->hit = 0;
+ if (x->node != GREP_NODE_OR)
+ return;
+ x->u.binary.left->hit = 0;
+ x = x->u.binary.right;
+ }
+}
+
+static int chk_hit_marker(struct grep_expr *x)
+{
+ /* Top level nodes have hit markers. See if they all are hits */
+ while (1) {
+ if (x->node != GREP_NODE_OR)
+ return x->hit;
+ if (!x->u.binary.left->hit)
+ return 0;
+ x = x->u.binary.right;
+ }
+}
+
+int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size)
+{
+ /*
+ * we do not have to do the two-pass grep when we do not check
+ * buffer-wide "all-match".
+ */
+ if (!opt->all_match)
+ return grep_buffer_1(opt, name, buf, size, 0);
+
+ /* Otherwise the toplevel "or" terms hit a bit differently.
+ * We first clear hit markers from them.
+ */
+ clr_hit_marker(opt->pattern_expression);
+ grep_buffer_1(opt, name, buf, size, 1);
+
+ if (!chk_hit_marker(opt->pattern_expression))
+ return 0;
+
+ return grep_buffer_1(opt, name, buf, size, 0);
+}
diff --git a/grep.h b/grep.h
index af9098cfe8..d252dd25f8 100644
--- a/grep.h
+++ b/grep.h
@@ -35,6 +35,7 @@ enum grep_expr_node {
struct grep_expr {
enum grep_expr_node node;
+ unsigned hit;
union {
struct grep_pat *atom;
struct grep_expr *unary;
@@ -59,6 +60,7 @@ struct grep_opt {
unsigned count:1;
unsigned word_regexp:1;
unsigned fixed:1;
+ unsigned all_match:1;
#define GREP_BINARY_DEFAULT 0
#define GREP_BINARY_NOMATCH 1
#define GREP_BINARY_TEXT 2
diff --git a/http-fetch.c b/http-fetch.c
index bc74f30f76..396552da02 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -4,35 +4,6 @@
#include "fetch.h"
#include "http.h"
-#ifndef NO_EXPAT
-#include <expat.h>
-
-/* Definitions for DAV requests */
-#define DAV_PROPFIND "PROPFIND"
-#define DAV_PROPFIND_RESP ".multistatus.response"
-#define DAV_PROPFIND_NAME ".multistatus.response.href"
-#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"
-#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"
-
-/* Definitions for processing XML DAV responses */
-#ifndef XML_STATUS_OK
-enum XML_Status {
- XML_STATUS_OK = 1,
- XML_STATUS_ERROR = 0
-};
-#define XML_STATUS_OK 1
-#define XML_STATUS_ERROR 0
-#endif
-
-/* Flags that control remote_ls processing */
-#define PROCESS_FILES (1u << 0)
-#define PROCESS_DIRS (1u << 1)
-#define RECURSIVE (1u << 2)
-
-/* Flags that remote_ls passes to callback functions */
-#define IS_DIR (1u << 0)
-#endif
-
#define PREV_BUF_SIZE 4096
#define RANGE_HEADER_SIZE 30
@@ -90,30 +61,6 @@ struct alternates_request {
int http_specific;
};
-#ifndef NO_EXPAT
-struct xml_ctx
-{
- char *name;
- int len;
- char *cdata;
- void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
- void *userData;
-};
-
-struct remote_ls_ctx
-{
- struct alt_base *repo;
- char *path;
- void (*userFunc)(struct remote_ls_ctx *ls);
- void *userData;
- int flags;
- char *dentry_name;
- int dentry_flags;
- int rc;
- struct remote_ls_ctx *parent;
-};
-#endif
-
static struct object_request *object_queue_head;
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
@@ -714,204 +661,6 @@ static void fetch_alternates(const char *base)
free(url);
}
-#ifndef NO_EXPAT
-static void
-xml_start_tag(void *userData, const char *name, const char **atts)
-{
- struct xml_ctx *ctx = (struct xml_ctx *)userData;
- const char *c = strchr(name, ':');
- int new_len;
-
- if (c == NULL)
- c = name;
- else
- c++;
-
- new_len = strlen(ctx->name) + strlen(c) + 2;
-
- if (new_len > ctx->len) {
- ctx->name = xrealloc(ctx->name, new_len);
- ctx->len = new_len;
- }
- strcat(ctx->name, ".");
- strcat(ctx->name, c);
-
- free(ctx->cdata);
- ctx->cdata = NULL;
-
- ctx->userFunc(ctx, 0);
-}
-
-static void
-xml_end_tag(void *userData, const char *name)
-{
- struct xml_ctx *ctx = (struct xml_ctx *)userData;
- const char *c = strchr(name, ':');
- char *ep;
-
- ctx->userFunc(ctx, 1);
-
- if (c == NULL)
- c = name;
- else
- c++;
-
- ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
- *ep = 0;
-}
-
-static void
-xml_cdata(void *userData, const XML_Char *s, int len)
-{
- struct xml_ctx *ctx = (struct xml_ctx *)userData;
- free(ctx->cdata);
- ctx->cdata = xmalloc(len + 1);
- strlcpy(ctx->cdata, s, len + 1);
-}
-
-static int remote_ls(struct alt_base *repo, const char *path, int flags,
- void (*userFunc)(struct remote_ls_ctx *ls),
- void *userData);
-
-static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
-{
- struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;
-
- if (tag_closed) {
- if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
- if (ls->dentry_flags & IS_DIR) {
- if (ls->flags & PROCESS_DIRS) {
- ls->userFunc(ls);
- }
- if (strcmp(ls->dentry_name, ls->path) &&
- ls->flags & RECURSIVE) {
- ls->rc = remote_ls(ls->repo,
- ls->dentry_name,
- ls->flags,
- ls->userFunc,
- ls->userData);
- }
- } else if (ls->flags & PROCESS_FILES) {
- ls->userFunc(ls);
- }
- } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
- ls->dentry_name = xmalloc(strlen(ctx->cdata) -
- ls->repo->path_len + 1);
- strcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len);
- } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
- ls->dentry_flags |= IS_DIR;
- }
- } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
- free(ls->dentry_name);
- ls->dentry_name = NULL;
- ls->dentry_flags = 0;
- }
-}
-
-static int remote_ls(struct alt_base *repo, const char *path, int flags,
- void (*userFunc)(struct remote_ls_ctx *ls),
- void *userData)
-{
- char *url = xmalloc(strlen(repo->base) + strlen(path) + 1);
- struct active_request_slot *slot;
- struct slot_results results;
- struct buffer in_buffer;
- struct buffer out_buffer;
- char *in_data;
- char *out_data;
- XML_Parser parser = XML_ParserCreate(NULL);
- enum XML_Status result;
- struct curl_slist *dav_headers = NULL;
- struct xml_ctx ctx;
- struct remote_ls_ctx ls;
-
- ls.flags = flags;
- ls.repo = repo;
- ls.path = xstrdup(path);
- ls.dentry_name = NULL;
- ls.dentry_flags = 0;
- ls.userData = userData;
- ls.userFunc = userFunc;
- ls.rc = 0;
-
- sprintf(url, "%s%s", repo->base, path);
-
- out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
- out_data = xmalloc(out_buffer.size + 1);
- snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
- out_buffer.posn = 0;
- out_buffer.buffer = out_data;
-
- in_buffer.size = 4096;
- in_data = xmalloc(in_buffer.size);
- in_buffer.posn = 0;
- in_buffer.buffer = in_data;
-
- dav_headers = curl_slist_append(dav_headers, "Depth: 1");
- dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
-
- slot = get_active_slot();
- slot->results = &results;
- curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
- curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
- curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
- curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
- curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
- curl_easy_setopt(slot->curl, CURLOPT_URL, url);
- curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
- curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
- curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
-
- if (start_active_slot(slot)) {
- run_active_slot(slot);
- if (results.curl_result == CURLE_OK) {
- ctx.name = xcalloc(10, 1);
- ctx.len = 0;
- ctx.cdata = NULL;
- ctx.userFunc = handle_remote_ls_ctx;
- ctx.userData = &ls;
- XML_SetUserData(parser, &ctx);
- XML_SetElementHandler(parser, xml_start_tag,
- xml_end_tag);
- XML_SetCharacterDataHandler(parser, xml_cdata);
- result = XML_Parse(parser, in_buffer.buffer,
- in_buffer.posn, 1);
- free(ctx.name);
-
- if (result != XML_STATUS_OK) {
- ls.rc = error("XML error: %s",
- XML_ErrorString(
- XML_GetErrorCode(parser)));
- }
- } else {
- ls.rc = -1;
- }
- } else {
- ls.rc = error("Unable to start PROPFIND request");
- }
-
- free(ls.path);
- free(url);
- free(out_data);
- free(in_buffer.buffer);
- curl_slist_free_all(dav_headers);
-
- return ls.rc;
-}
-
-static void process_ls_pack(struct remote_ls_ctx *ls)
-{
- unsigned char sha1[20];
-
- if (strlen(ls->dentry_name) == 63 &&
- !strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
- has_extension(ls->dentry_name, ".pack")) {
- get_sha1_hex(ls->dentry_name + 18, sha1);
- setup_index(ls->repo, sha1);
- }
-}
-#endif
-
static int fetch_indices(struct alt_base *repo)
{
unsigned char sha1[20];
@@ -934,12 +683,6 @@ static int fetch_indices(struct alt_base *repo)
if (get_verbosely)
fprintf(stderr, "Getting pack list for %s\n", repo->base);
-#ifndef NO_EXPAT
- if (remote_ls(repo, "objects/pack/", PROCESS_FILES,
- process_ls_pack, NULL) == 0)
- return 0;
-#endif
-
url = xmalloc(strlen(repo->base) + 21);
sprintf(url, "%s/objects/info/packs", repo->base);
diff --git a/imap-send.c b/imap-send.c
index 362e474374..a6a65680ee 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -272,7 +272,7 @@ buffer_gets( buffer_t * b, char **s )
n = b->bytes - start;
if (n)
- memcpy( b->buf, b->buf + start, n );
+ memmove(b->buf, b->buf + start, n);
b->offset -= start;
b->bytes = n;
start = 0;
@@ -1226,6 +1226,14 @@ split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
if (msg->len < 5 || strncmp( data, "From ", 5 ))
return 0;
+ p = strchr( data, '\n' );
+ if (p) {
+ p = &p[1];
+ msg->len -= p-data;
+ *ofs += p-data;
+ data = p;
+ }
+
p = strstr( data, "\nFrom " );
if (p)
msg->len = &p[1] - data;
diff --git a/index-pack.c b/index-pack.c
index 80bc6cb45b..70640e14d8 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -13,63 +13,93 @@ static const char index_pack_usage[] =
struct object_entry
{
unsigned long offset;
+ unsigned long size;
+ unsigned int hdr_size;
enum object_type type;
enum object_type real_type;
unsigned char sha1[20];
};
+union delta_base {
+ unsigned char sha1[20];
+ unsigned long offset;
+};
+
+/*
+ * Even if sizeof(union delta_base) == 24 on 64-bit archs, we really want
+ * to memcmp() only the first 20 bytes.
+ */
+#define UNION_BASE_SZ 20
+
struct delta_entry
{
struct object_entry *obj;
- unsigned char base_sha1[20];
+ union delta_base base;
};
static const char *pack_name;
-static unsigned char *pack_base;
-static unsigned long pack_size;
static struct object_entry *objects;
static struct delta_entry *deltas;
static int nr_objects;
static int nr_deltas;
-static void open_pack_file(void)
+/* We always read in 4kB chunks. */
+static unsigned char input_buffer[4096];
+static unsigned long input_offset, input_len, consumed_bytes;
+static SHA_CTX input_ctx;
+static int input_fd;
+
+/*
+ * Make sure at least "min" bytes are available in the buffer, and
+ * return the pointer to the buffer.
+ */
+static void *fill(int min)
{
- int fd;
- struct stat st;
+ if (min <= input_len)
+ return input_buffer + input_offset;
+ if (min > sizeof(input_buffer))
+ die("cannot fill %d bytes", min);
+ if (input_offset) {
+ SHA1_Update(&input_ctx, input_buffer, input_offset);
+ memmove(input_buffer, input_buffer + input_offset, input_len);
+ input_offset = 0;
+ }
+ do {
+ int ret = xread(input_fd, input_buffer + input_len,
+ sizeof(input_buffer) - input_len);
+ if (ret <= 0) {
+ if (!ret)
+ die("early EOF");
+ die("read error on input: %s", strerror(errno));
+ }
+ input_len += ret;
+ } while (input_len < min);
+ return input_buffer;
+}
+
+static void use(int bytes)
+{
+ if (bytes > input_len)
+ die("used more bytes than were available");
+ input_len -= bytes;
+ input_offset += bytes;
+ consumed_bytes += bytes;
+}
- fd = open(pack_name, O_RDONLY);
- if (fd < 0)
+static void open_pack_file(void)
+{
+ input_fd = open(pack_name, O_RDONLY);
+ if (input_fd < 0)
die("cannot open packfile '%s': %s", pack_name,
strerror(errno));
- if (fstat(fd, &st)) {
- int err = errno;
- close(fd);
- die("cannot fstat packfile '%s': %s", pack_name,
- strerror(err));
- }
- pack_size = st.st_size;
- pack_base = mmap(NULL, pack_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (pack_base == MAP_FAILED) {
- int err = errno;
- close(fd);
- die("cannot mmap packfile '%s': %s", pack_name,
- strerror(err));
- }
- close(fd);
+ SHA1_Init(&input_ctx);
}
static void parse_pack_header(void)
{
- const struct pack_header *hdr;
- unsigned char sha1[20];
- SHA_CTX ctx;
-
- /* Ensure there are enough bytes for the header and final SHA1 */
- if (pack_size < sizeof(struct pack_header) + 20)
- die("packfile '%s' is too small", pack_name);
+ struct pack_header *hdr = fill(sizeof(struct pack_header));
/* Header consistency check */
- hdr = (void *)pack_base;
if (hdr->hdr_signature != htonl(PACK_SIGNATURE))
die("packfile '%s' signature mismatch", pack_name);
if (!pack_version_ok(hdr->hdr_version))
@@ -77,13 +107,8 @@ static void parse_pack_header(void)
pack_name, ntohl(hdr->hdr_version));
nr_objects = ntohl(hdr->hdr_entries);
-
- /* Check packfile integrity */
- SHA1_Init(&ctx);
- SHA1_Update(&ctx, pack_base, pack_size - 20);
- SHA1_Final(sha1, &ctx);
- if (hashcmp(sha1, pack_base + pack_size - 20))
- die("packfile '%s' SHA1 mismatch", pack_name);
+ use(sizeof(struct pack_header));
+ /*fprintf(stderr, "Indexing %d objects\n", nr_objects);*/
}
static void bad_object(unsigned long offset, const char *format,
@@ -101,86 +126,121 @@ static void bad_object(unsigned long offset, const char *format, ...)
pack_name, offset, buf);
}
-static void *unpack_entry_data(unsigned long offset,
- unsigned long *current_pos, unsigned long size)
+static void *unpack_entry_data(unsigned long offset, unsigned long size)
{
- unsigned long pack_limit = pack_size - 20;
- unsigned long pos = *current_pos;
z_stream stream;
void *buf = xmalloc(size);
memset(&stream, 0, sizeof(stream));
stream.next_out = buf;
stream.avail_out = size;
- stream.next_in = pack_base + pos;
- stream.avail_in = pack_limit - pos;
+ stream.next_in = fill(1);
+ stream.avail_in = input_len;
inflateInit(&stream);
for (;;) {
int ret = inflate(&stream, 0);
- if (ret == Z_STREAM_END)
+ use(input_len - stream.avail_in);
+ if (stream.total_out == size && ret == Z_STREAM_END)
break;
if (ret != Z_OK)
bad_object(offset, "inflate returned %d", ret);
+ stream.next_in = fill(1);
+ stream.avail_in = input_len;
}
inflateEnd(&stream);
- if (stream.total_out != size)
- bad_object(offset, "size mismatch (expected %lu, got %lu)",
- size, stream.total_out);
- *current_pos = pack_limit - stream.avail_in;
return buf;
}
-static void *unpack_raw_entry(unsigned long offset,
- enum object_type *obj_type,
- unsigned long *obj_size,
- unsigned char *delta_base,
- unsigned long *next_obj_offset)
+static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
{
- unsigned long pack_limit = pack_size - 20;
- unsigned long pos = offset;
- unsigned char c;
- unsigned long size;
+ unsigned char *p, c;
+ unsigned long size, base_offset;
unsigned shift;
- enum object_type type;
- void *data;
- c = pack_base[pos++];
- type = (c >> 4) & 7;
+ obj->offset = consumed_bytes;
+
+ p = fill(1);
+ c = *p;
+ use(1);
+ obj->type = (c >> 4) & 7;
size = (c & 15);
shift = 4;
while (c & 0x80) {
- if (pos >= pack_limit)
- bad_object(offset, "object extends past end of pack");
- c = pack_base[pos++];
+ p = fill(1);
+ c = *p;
+ use(1);
size += (c & 0x7fUL) << shift;
shift += 7;
}
+ obj->size = size;
- switch (type) {
- case OBJ_DELTA:
- if (pos + 20 >= pack_limit)
- bad_object(offset, "object extends past end of pack");
- hashcpy(delta_base, pack_base + pos);
- pos += 20;
- /* fallthru */
+ switch (obj->type) {
+ case OBJ_REF_DELTA:
+ hashcpy(delta_base->sha1, fill(20));
+ use(20);
+ break;
+ case OBJ_OFS_DELTA:
+ memset(delta_base, 0, sizeof(*delta_base));
+ p = fill(1);
+ c = *p;
+ use(1);
+ base_offset = c & 127;
+ while (c & 128) {
+ base_offset += 1;
+ if (!base_offset || base_offset & ~(~0UL >> 7))
+ bad_object(obj->offset, "offset value overflow for delta base object");
+ p = fill(1);
+ c = *p;
+ use(1);
+ base_offset = (base_offset << 7) + (c & 127);
+ }
+ delta_base->offset = obj->offset - base_offset;
+ if (delta_base->offset >= obj->offset)
+ bad_object(obj->offset, "delta base offset is out of bound");
+ break;
case OBJ_COMMIT:
case OBJ_TREE:
case OBJ_BLOB:
case OBJ_TAG:
- data = unpack_entry_data(offset, &pos, size);
break;
default:
- bad_object(offset, "bad object type %d", type);
+ bad_object(obj->offset, "bad object type %d", obj->type);
}
+ obj->hdr_size = consumed_bytes - obj->offset;
- *obj_type = type;
- *obj_size = size;
- *next_obj_offset = pos;
+ return unpack_entry_data(obj->offset, obj->size);
+}
+
+static void * get_data_from_pack(struct object_entry *obj)
+{
+ unsigned long from = obj[0].offset + obj[0].hdr_size;
+ unsigned long len = obj[1].offset - from;
+ unsigned pg_offset = from % getpagesize();
+ unsigned char *map, *data;
+ z_stream stream;
+ int st;
+
+ map = mmap(NULL, len + pg_offset, PROT_READ, MAP_PRIVATE,
+ input_fd, from - pg_offset);
+ if (map == MAP_FAILED)
+ die("cannot mmap packfile '%s': %s", pack_name, strerror(errno));
+ data = xmalloc(obj->size);
+ memset(&stream, 0, sizeof(stream));
+ stream.next_out = data;
+ stream.avail_out = obj->size;
+ stream.next_in = map + pg_offset;
+ stream.avail_in = len;
+ inflateInit(&stream);
+ while ((st = inflate(&stream, Z_FINISH)) == Z_OK);
+ inflateEnd(&stream);
+ if (st != Z_STREAM_END || stream.total_out != obj->size)
+ die("serious inflate inconsistency");
+ munmap(map, len + pg_offset);
return data;
}
-static int find_delta(const unsigned char *base_sha1)
+static int find_delta(const union delta_base *base)
{
int first = 0, last = nr_deltas;
@@ -189,7 +249,7 @@ static int find_delta(const unsigned char *base_sha1)
struct delta_entry *delta = &deltas[next];
int cmp;
- cmp = hashcmp(base_sha1, delta->base_sha1);
+ cmp = memcmp(base, &delta->base, UNION_BASE_SZ);
if (!cmp)
return next;
if (cmp < 0) {
@@ -201,18 +261,18 @@ static int find_delta(const unsigned char *base_sha1)
return -first-1;
}
-static int find_deltas_based_on_sha1(const unsigned char *base_sha1,
- int *first_index, int *last_index)
+static int find_delta_childs(const union delta_base *base,
+ int *first_index, int *last_index)
{
- int first = find_delta(base_sha1);
+ int first = find_delta(base);
int last = first;
int end = nr_deltas - 1;
if (first < 0)
return -1;
- while (first > 0 && !hashcmp(deltas[first - 1].base_sha1, base_sha1))
+ while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
--first;
- while (last < end && !hashcmp(deltas[last + 1].base_sha1, base_sha1))
+ while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
++last;
*first_index = first;
*last_index = last;
@@ -252,25 +312,34 @@ static void resolve_delta(struct delta_entry *delta, void *base_data,
unsigned long delta_size;
void *result;
unsigned long result_size;
- enum object_type delta_type;
- unsigned char base_sha1[20];
- unsigned long next_obj_offset;
+ union delta_base delta_base;
int j, first, last;
obj->real_type = type;
- delta_data = unpack_raw_entry(obj->offset, &delta_type,
- &delta_size, base_sha1,
- &next_obj_offset);
+ delta_data = get_data_from_pack(obj);
+ delta_size = obj->size;
result = patch_delta(base_data, base_size, delta_data, delta_size,
&result_size);
free(delta_data);
if (!result)
bad_object(obj->offset, "failed to apply delta");
sha1_object(result, result_size, type, obj->sha1);
- if (!find_deltas_based_on_sha1(obj->sha1, &first, &last)) {
+
+ hashcpy(delta_base.sha1, obj->sha1);
+ if (!find_delta_childs(&delta_base, &first, &last)) {
+ for (j = first; j <= last; j++)
+ if (deltas[j].obj->type == OBJ_REF_DELTA)
+ resolve_delta(&deltas[j], result, result_size, type);
+ }
+
+ memset(&delta_base, 0, sizeof(delta_base));
+ delta_base.offset = obj->offset;
+ if (!find_delta_childs(&delta_base, &first, &last)) {
for (j = first; j <= last; j++)
- resolve_delta(&deltas[j], result, result_size, type);
+ if (deltas[j].obj->type == OBJ_OFS_DELTA)
+ resolve_delta(&deltas[j], result, result_size, type);
}
+
free(result);
}
@@ -278,16 +347,16 @@ static int compare_delta_entry(const void *a, const void *b)
{
const struct delta_entry *delta_a = a;
const struct delta_entry *delta_b = b;
- return hashcmp(delta_a->base_sha1, delta_b->base_sha1);
+ return memcmp(&delta_a->base, &delta_b->base, UNION_BASE_SZ);
}
-static void parse_pack_objects(void)
+/* Parse all objects and return the pack content SHA1 hash */
+static void parse_pack_objects(unsigned char *sha1)
{
int i;
- unsigned long offset = sizeof(struct pack_header);
- unsigned char base_sha1[20];
+ struct delta_entry *delta = deltas;
void *data;
- unsigned long data_size;
+ struct stat st;
/*
* First pass:
@@ -297,22 +366,32 @@ static void parse_pack_objects(void)
*/
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
- obj->offset = offset;
- data = unpack_raw_entry(offset, &obj->type, &data_size,
- base_sha1, &offset);
+ data = unpack_raw_entry(obj, &delta->base);
obj->real_type = obj->type;
- if (obj->type == OBJ_DELTA) {
- struct delta_entry *delta = &deltas[nr_deltas++];
+ if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA) {
+ nr_deltas++;
delta->obj = obj;
- hashcpy(delta->base_sha1, base_sha1);
+ delta++;
} else
- sha1_object(data, data_size, obj->type, obj->sha1);
+ sha1_object(data, obj->size, obj->type, obj->sha1);
free(data);
}
- if (offset != pack_size - 20)
+ objects[i].offset = consumed_bytes;
+
+ /* Check pack integrity */
+ SHA1_Update(&input_ctx, input_buffer, input_offset);
+ SHA1_Final(sha1, &input_ctx);
+ if (hashcmp(fill(20), sha1))
+ die("packfile '%s' SHA1 mismatch", pack_name);
+ use(20);
+
+ /* If input_fd is a file, we should have reached its end now. */
+ if (fstat(input_fd, &st))
+ die("cannot fstat packfile '%s': %s", pack_name, strerror(errno));
+ if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
die("packfile '%s' has junk at the end", pack_name);
- /* Sort deltas by base SHA1 for fast searching */
+ /* Sort deltas by base SHA1/offset for fast searching */
qsort(deltas, nr_deltas, sizeof(struct delta_entry),
compare_delta_entry);
@@ -326,22 +405,36 @@ static void parse_pack_objects(void)
*/
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
- int j, first, last;
+ union delta_base base;
+ int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last;
- if (obj->type == OBJ_DELTA)
+ if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
continue;
- if (find_deltas_based_on_sha1(obj->sha1, &first, &last))
+ hashcpy(base.sha1, obj->sha1);
+ ref = !find_delta_childs(&base, &ref_first, &ref_last);
+ memset(&base, 0, sizeof(base));
+ base.offset = obj->offset;
+ ofs = !find_delta_childs(&base, &ofs_first, &ofs_last);
+ if (!ref && !ofs)
continue;
- data = unpack_raw_entry(obj->offset, &obj->type, &data_size,
- base_sha1, &offset);
- for (j = first; j <= last; j++)
- resolve_delta(&deltas[j], data, data_size, obj->type);
+ data = get_data_from_pack(obj);
+ if (ref)
+ for (j = ref_first; j <= ref_last; j++)
+ if (deltas[j].obj->type == OBJ_REF_DELTA)
+ resolve_delta(&deltas[j], data,
+ obj->size, obj->type);
+ if (ofs)
+ for (j = ofs_first; j <= ofs_last; j++)
+ if (deltas[j].obj->type == OBJ_OFS_DELTA)
+ resolve_delta(&deltas[j], data,
+ obj->size, obj->type);
free(data);
}
/* Check for unresolved deltas */
for (i = 0; i < nr_deltas; i++) {
- if (deltas[i].obj->real_type == OBJ_DELTA)
+ if (deltas[i].obj->real_type == OBJ_REF_DELTA ||
+ deltas[i].obj->real_type == OBJ_OFS_DELTA)
die("packfile '%s' has unresolved deltas", pack_name);
}
}
@@ -353,6 +446,10 @@ static int sha1_compare(const void *_a, const void *_b)
return hashcmp(a->sha1, b->sha1);
}
+/*
+ * On entry *sha1 contains the pack content SHA1 hash, on exit it is
+ * the SHA1 hash of sorted object names.
+ */
static void write_index_file(const char *index_name, unsigned char *sha1)
{
struct sha1file *f;
@@ -412,7 +509,7 @@ static void write_index_file(const char *index_name, unsigned char *sha1)
sha1write(f, obj->sha1, 20);
SHA1_Update(&ctx, obj->sha1, 20);
}
- sha1write(f, pack_base + pack_size - 20, 20);
+ sha1write(f, sha1, 20);
sha1close(f, NULL, 1);
free(sorted_by_sha);
SHA1_Final(sha1, &ctx);
@@ -458,9 +555,9 @@ int main(int argc, char **argv)
open_pack_file();
parse_pack_header();
- objects = xcalloc(nr_objects, sizeof(struct object_entry));
+ objects = xcalloc(nr_objects + 1, sizeof(struct object_entry));
deltas = xcalloc(nr_objects, sizeof(struct delta_entry));
- parse_pack_objects();
+ parse_pack_objects(sha1);
free(deltas);
write_index_file(index_name, sha1);
free(objects);
diff --git a/merge-recursive.c b/merge-recursive.c
index 611cd95cf5..2ba43ae84b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1235,13 +1235,10 @@ int merge(struct commit *h1,
if (merged_common_ancestors == NULL) {
/* if there is no common ancestor, make an empty tree */
struct tree *tree = xcalloc(1, sizeof(struct tree));
- unsigned char hdr[40];
- int hdrlen;
tree->object.parsed = 1;
tree->object.type = OBJ_TREE;
- write_sha1_file_prepare(NULL, 0, tree_type, tree->object.sha1,
- hdr, &hdrlen);
+ hash_sha1_file(NULL, 0, tree_type, tree->object.sha1);
merged_common_ancestors = make_virtual_commit(tree, "ancestor");
}
diff --git a/pack.h b/pack.h
index 05557da152..4814800f28 100644
--- a/pack.h
+++ b/pack.h
@@ -7,7 +7,7 @@
* Packed object header
*/
#define PACK_SIGNATURE 0x5041434b /* "PACK" */
-#define PACK_VERSION 3
+#define PACK_VERSION 2
#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
struct pack_header {
unsigned int hdr_signature;
@@ -16,7 +16,4 @@ struct pack_header {
};
extern int verify_pack(struct packed_git *, int);
-extern int check_reuse_pack_delta(struct packed_git *, unsigned long,
- unsigned char *, unsigned long *,
- enum object_type *);
#endif
diff --git a/pager.c b/pager.c
index dcb398da8e..4587fbbdb5 100644
--- a/pager.c
+++ b/pager.c
@@ -50,7 +50,7 @@ void setup_pager(void)
close(fd[0]);
close(fd[1]);
- setenv("LESS", "-RS", 0);
+ setenv("LESS", "FRSX", 0);
run_pager(pager);
die("unable to execute pager '%s'", pager);
exit(255);
diff --git a/quote.c b/quote.c
index ee7d62c751..a418a0f803 100644
--- a/quote.c
+++ b/quote.c
@@ -209,7 +209,7 @@ static int quote_c_style_counted(const char *name, int namelen,
if (!ch)
break;
if ((ch < ' ') || (ch == '"') || (ch == '\\') ||
- (ch == 0177)) {
+ (ch >= 0177)) {
needquote = 1;
switch (ch) {
case '\a': EMITQ(); ch = 'a'; break;
diff --git a/receive-pack.c b/receive-pack.c
index 1fcf3a9112..de1d6a4b1c 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -9,11 +9,25 @@ static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
static const char *unpacker[] = { "unpack-objects", NULL };
+static int deny_non_fast_forwards = 0;
static int report_status;
static char capabilities[] = "report-status";
static int capabilities_sent;
+static int receive_pack_config(const char *var, const char *value)
+{
+ git_default_config(var, value);
+
+ if (strcmp(var, "receive.denynonfastforwards") == 0)
+ {
+ deny_non_fast_forwards = git_config_bool(var, value);
+ return 0;
+ }
+
+ return 0;
+}
+
static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
if (capabilities_sent)
@@ -279,7 +293,7 @@ int main(int argc, char **argv)
die("'%s': unable to chdir or not a git archive", dir);
setup_ident();
- git_config(git_default_config);
+ git_config(receive_pack_config);
write_head_info();
diff --git a/revision.c b/revision.c
index d87cb6cd64..3dbc26c49c 100644
--- a/revision.c
+++ b/revision.c
@@ -418,9 +418,6 @@ static void limit_list(struct rev_info *revs)
if (revs->max_age != -1 && (commit->date < revs->max_age))
obj->flags |= UNINTERESTING;
- if (revs->unpacked &&
- has_sha1_pack(obj->sha1, revs->ignore_packed))
- obj->flags |= UNINTERESTING;
add_parents_to_list(revs, commit, &list);
if (obj->flags & UNINTERESTING) {
mark_parents_uninteresting(commit);
@@ -732,6 +729,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
int i, flags, seen_dashdash, show_merge;
const char **unrecognized = argv + 1;
int left = 1;
+ int all_match = 0;
/* First, search for "--" */
seen_dashdash = 0;
@@ -967,6 +965,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
add_message_grep(revs, arg+7);
continue;
}
+ if (!strcmp(arg, "--all-match")) {
+ all_match = 1;
+ continue;
+ }
opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
if (opts > 0) {
@@ -1010,7 +1012,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
add_pending_object(revs, object, def);
}
- if (revs->topo_order || revs->unpacked)
+ if (revs->topo_order)
revs->limited = 1;
if (revs->prune_data) {
@@ -1028,8 +1030,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
if (diff_setup_done(&revs->diffopt) < 0)
die("diff_setup_done failed");
- if (revs->grep_filter)
+ if (revs->grep_filter) {
+ revs->grep_filter->all_match = all_match;
compile_grep_patterns(revs->grep_filter);
+ }
return left;
}
@@ -1142,17 +1146,18 @@ struct commit *get_revision(struct rev_info *revs)
* that we'd otherwise have done in limit_list().
*/
if (!revs->limited) {
- if ((revs->unpacked &&
- has_sha1_pack(commit->object.sha1,
- revs->ignore_packed)) ||
- (revs->max_age != -1 &&
- (commit->date < revs->max_age)))
+ if (revs->max_age != -1 &&
+ (commit->date < revs->max_age))
continue;
add_parents_to_list(revs, commit, &revs->commits);
}
if (commit->object.flags & SHOWN)
continue;
+ if (revs->unpacked && has_sha1_pack(commit->object.sha1,
+ revs->ignore_packed))
+ continue;
+
/* We want to show boundary commits only when their
* children are shown. When path-limiter is in effect,
* rewrite_parents() drops some commits from getting shown,
diff --git a/setup.c b/setup.c
index 9a46a58a4a..2afdba414a 100644
--- a/setup.c
+++ b/setup.c
@@ -244,8 +244,6 @@ int check_repository_format_version(const char *var, const char *value)
repository_format_version = git_config_int(var, value);
else if (strcmp(var, "core.sharedrepository") == 0)
shared_repository = git_config_perm(var, value);
- else if (strcmp(var, "receive.denynonfastforwards") == 0)
- deny_non_fast_forwards = git_config_bool(var, value);
return 0;
}
diff --git a/sha1_file.c b/sha1_file.c
index 27b1ebb720..e89d24c015 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -671,14 +671,8 @@ static void reprepare_packed_git(void)
int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long size, const char *type)
{
- char header[100];
unsigned char real_sha1[20];
- SHA_CTX c;
-
- SHA1_Init(&c);
- SHA1_Update(&c, header, 1+sprintf(header, "%s %lu", type, size));
- SHA1_Update(&c, map, size);
- SHA1_Final(real_sha1, &c);
+ hash_sha1_file(map, size, type, real_sha1);
return hashcmp(sha1, real_sha1) ? -1 : 0;
}
@@ -883,26 +877,61 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l
return unpack_sha1_rest(&stream, hdr, *size);
}
+static unsigned long get_delta_base(struct packed_git *p,
+ unsigned long offset,
+ enum object_type kind,
+ unsigned long delta_obj_offset,
+ unsigned long *base_obj_offset)
+{
+ unsigned char *base_info = (unsigned char *) p->pack_base + offset;
+ unsigned long base_offset;
+
+ /* there must be at least 20 bytes left regardless of delta type */
+ if (p->pack_size <= offset + 20)
+ die("truncated pack file");
+
+ if (kind == OBJ_OFS_DELTA) {
+ unsigned used = 0;
+ unsigned char c = base_info[used++];
+ 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");
+ c = base_info[used++];
+ base_offset = (base_offset << 7) + (c & 127);
+ }
+ base_offset = delta_obj_offset - base_offset;
+ if (base_offset >= delta_obj_offset)
+ die("delta base offset out of bound");
+ offset += used;
+ } else if (kind == OBJ_REF_DELTA) {
+ /* The base entry _must_ be in the same pack */
+ base_offset = find_pack_entry_one(base_info, p);
+ if (!base_offset)
+ die("failed to find delta-pack base object %s",
+ sha1_to_hex(base_info));
+ offset += 20;
+ } else
+ die("I am totally screwed");
+ *base_obj_offset = base_offset;
+ return offset;
+}
+
/* forward declaration for a mutually recursive function */
static int packed_object_info(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep);
static int packed_delta_info(struct packed_git *p,
unsigned long offset,
+ enum object_type kind,
+ unsigned long obj_offset,
char *type,
unsigned long *sizep)
{
unsigned long base_offset;
- unsigned char *base_sha1 = (unsigned char *) p->pack_base + offset;
- if (p->pack_size < offset + 20)
- die("truncated pack file");
- /* The base entry _must_ be in the same pack */
- base_offset = find_pack_entry_one(base_sha1, p);
- if (!base_offset)
- die("failed to find delta-pack base object %s",
- sha1_to_hex(base_sha1));
- offset += 20;
+ offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
/* We choose to only get the type of the base object and
* ignore potentially corrupt pack file that expects the delta
@@ -914,7 +943,7 @@ static int packed_delta_info(struct packed_git *p,
if (sizep) {
const unsigned char *data;
- unsigned char delta_head[64];
+ unsigned char delta_head[20];
unsigned long result_size;
z_stream stream;
int st;
@@ -965,25 +994,6 @@ static unsigned long unpack_object_header(struct packed_git *p, unsigned long of
return offset + used;
}
-int check_reuse_pack_delta(struct packed_git *p, unsigned long offset,
- unsigned char *base, unsigned long *sizep,
- enum object_type *kindp)
-{
- unsigned long ptr;
- int status = -1;
-
- use_packed_git(p);
- ptr = offset;
- ptr = unpack_object_header(p, ptr, kindp, sizep);
- if (*kindp != OBJ_DELTA)
- goto done;
- hashcpy(base, (unsigned char *) p->pack_base + ptr);
- status = 0;
- done:
- unuse_packed_git(p);
- return status;
-}
-
void packed_object_info_detail(struct packed_git *p,
unsigned long offset,
char *type,
@@ -992,11 +1002,12 @@ void packed_object_info_detail(struct packed_git *p,
unsigned int *delta_chain_length,
unsigned char *base_sha1)
{
- unsigned long val;
+ unsigned long obj_offset, val;
unsigned char *next_sha1;
enum object_type kind;
*delta_chain_length = 0;
+ obj_offset = offset;
offset = unpack_object_header(p, offset, &kind, size);
for (;;) {
@@ -1011,7 +1022,13 @@ void packed_object_info_detail(struct packed_git *p,
strcpy(type, type_names[kind]);
*store_size = 0; /* notyet */
return;
- case OBJ_DELTA:
+ case OBJ_OFS_DELTA:
+ get_delta_base(p, offset, kind, obj_offset, &offset);
+ if (*delta_chain_length == 0) {
+ /* TODO: find base_sha1 as pointed by offset */
+ }
+ break;
+ case OBJ_REF_DELTA:
if (p->pack_size <= offset + 20)
die("pack file %s records an incomplete delta base",
p->pack_name);
@@ -1021,6 +1038,7 @@ void packed_object_info_detail(struct packed_git *p,
offset = find_pack_entry_one(next_sha1, p);
break;
}
+ obj_offset = offset;
offset = unpack_object_header(p, offset, &kind, &val);
(*delta_chain_length)++;
}
@@ -1029,15 +1047,15 @@ void packed_object_info_detail(struct packed_git *p,
static int packed_object_info(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep)
{
- unsigned long size;
+ unsigned long size, obj_offset = offset;
enum object_type kind;
offset = unpack_object_header(p, offset, &kind, &size);
- if (kind == OBJ_DELTA)
- return packed_delta_info(p, offset, type, sizep);
-
switch (kind) {
+ case OBJ_OFS_DELTA:
+ case OBJ_REF_DELTA:
+ return packed_delta_info(p, offset, kind, obj_offset, type, sizep);
case OBJ_COMMIT:
case OBJ_TREE:
case OBJ_BLOB:
@@ -1083,23 +1101,15 @@ static void *unpack_compressed_entry(struct packed_git *p,
static void *unpack_delta_entry(struct packed_git *p,
unsigned long offset,
unsigned long delta_size,
+ enum object_type kind,
+ unsigned long obj_offset,
char *type,
unsigned long *sizep)
{
void *delta_data, *result, *base;
unsigned long result_size, base_size, base_offset;
- unsigned char *base_sha1;
-
- if (p->pack_size < offset + 20)
- die("truncated pack file");
- /* The base entry _must_ be in the same pack */
- base_sha1 = (unsigned char*)p->pack_base + offset;
- base_offset = find_pack_entry_one(base_sha1, p);
- if (!base_offset)
- die("failed to find delta-pack base object %s",
- sha1_to_hex(base_sha1));
- offset += 20;
+ offset = get_delta_base(p, offset, kind, obj_offset, &base_offset);
base = unpack_entry_gently(p, base_offset, type, &base_size);
if (!base)
die("failed to read delta base object at %lu from %s",
@@ -1136,13 +1146,14 @@ static void *unpack_entry(struct pack_entry *entry,
void *unpack_entry_gently(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep)
{
- unsigned long size;
+ unsigned long size, obj_offset = offset;
enum object_type kind;
offset = unpack_object_header(p, offset, &kind, &size);
switch (kind) {
- case OBJ_DELTA:
- return unpack_delta_entry(p, offset, size, type, sizep);
+ case OBJ_OFS_DELTA:
+ case OBJ_REF_DELTA:
+ return unpack_delta_entry(p, offset, size, kind, obj_offset, type, sizep);
case OBJ_COMMIT:
case OBJ_TREE:
case OBJ_BLOB:
@@ -1347,12 +1358,9 @@ void *read_object_with_reference(const unsigned char *sha1,
}
}
-char *write_sha1_file_prepare(void *buf,
- unsigned long len,
- const char *type,
- unsigned char *sha1,
- unsigned char *hdr,
- int *hdrlen)
+static void write_sha1_file_prepare(void *buf, unsigned long len,
+ const char *type, unsigned char *sha1,
+ unsigned char *hdr, int *hdrlen)
{
SHA_CTX c;
@@ -1364,8 +1372,6 @@ char *write_sha1_file_prepare(void *buf,
SHA1_Update(&c, hdr, *hdrlen);
SHA1_Update(&c, buf, len);
SHA1_Final(sha1, &c);
-
- return sha1_file_name(sha1);
}
/*
@@ -1501,6 +1507,15 @@ static void setup_object_header(z_stream *stream, const char *type, unsigned lon
stream->avail_out -= hdr;
}
+int hash_sha1_file(void *buf, unsigned long len, const char *type,
+ unsigned char *sha1)
+{
+ unsigned char hdr[50];
+ int hdrlen;
+ write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
+ return 0;
+}
+
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
int size;
@@ -1515,7 +1530,8 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
/* Normally if we have it in the pack then we do not bother writing
* it out into .git/objects/??/?{38} file.
*/
- filename = write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
+ write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
+ filename = sha1_file_name(sha1);
if (returnsha1)
hashcpy(returnsha1, sha1);
if (has_sha1_file(sha1))
@@ -1784,8 +1800,6 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
unsigned long size = 4096;
char *buf = xmalloc(size);
int ret;
- unsigned char hdr[50];
- int hdrlen;
if (read_pipe(fd, &buf, &size)) {
free(buf);
@@ -1796,10 +1810,8 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
type = blob_type;
if (write_object)
ret = write_sha1_file(buf, size, type, sha1);
- else {
- write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
- ret = 0;
- }
+ else
+ ret = hash_sha1_file(buf, size, type, sha1);
free(buf);
return ret;
}
@@ -1809,8 +1821,6 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
unsigned long size = st->st_size;
void *buf;
int ret;
- unsigned char hdr[50];
- int hdrlen;
buf = "";
if (size)
@@ -1823,10 +1833,8 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
type = blob_type;
if (write_object)
ret = write_sha1_file(buf, size, type, sha1);
- else {
- write_sha1_file_prepare(buf, size, type, sha1, hdr, &hdrlen);
- ret = 0;
- }
+ else
+ ret = hash_sha1_file(buf, size, type, sha1);
if (size)
munmap(buf, size);
return ret;
@@ -1855,12 +1863,9 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
return error("readlink(\"%s\"): %s", path,
errstr);
}
- if (!write_object) {
- unsigned char hdr[50];
- int hdrlen;
- write_sha1_file_prepare(target, st->st_size, blob_type,
- sha1, hdr, &hdrlen);
- } else if (write_sha1_file(target, st->st_size, blob_type, sha1))
+ if (!write_object)
+ hash_sha1_file(target, st->st_size, blob_type, sha1);
+ else if (write_sha1_file(target, st->st_size, blob_type, sha1))
return error("%s: failed to insert into database",
path);
free(target);
diff --git a/sha1_name.c b/sha1_name.c
index 5cf5578af1..6d7cd78381 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -157,7 +157,7 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
char canonical[40];
unsigned char res[20];
- if (len < MINIMUM_ABBREV)
+ if (len < MINIMUM_ABBREV || len > 40)
return -1;
hashclr(res);
memset(canonical, 'x', 40);
diff --git a/show-index.c b/show-index.c
index c21d660b62..a30a2de5d1 100644
--- a/show-index.c
+++ b/show-index.c
@@ -8,7 +8,7 @@ int main(int argc, char **argv)
static unsigned int top_index[256];
if (fread(top_index, sizeof(top_index), 1, stdin) != 1)
- die("unable to read idex");
+ die("unable to read index");
nr = 0;
for (i = 0; i < 256; i++) {
unsigned n = ntohl(top_index[i]);
diff --git a/sideband.c b/sideband.c
index 1b14ff8892..277fa3c10d 100644
--- a/sideband.c
+++ b/sideband.c
@@ -11,10 +11,13 @@
* stream, aka "verbose"). A message over band #3 is a signal that
* the remote died unexpectedly. A flush() concludes the stream.
*/
-int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, int bufsz)
+int recv_sideband(const char *me, int in_stream, int out, int err)
{
+ char buf[7 + LARGE_PACKET_MAX + 1];
+ strcpy(buf, "remote:");
while (1) {
- int len = packet_read_line(in_stream, buf, bufsz);
+ int band, len;
+ len = packet_read_line(in_stream, buf+7, LARGE_PACKET_MAX);
if (len == 0)
break;
if (len < 1) {
@@ -22,25 +25,26 @@ int recv_sideband(const char *me, int in_stream, int out, int err, char *buf, in
safe_write(err, buf, len);
return SIDEBAND_PROTOCOL_ERROR;
}
+ band = buf[7] & 0xff;
len--;
- switch (buf[0] & 0xFF) {
+ switch (band) {
case 3:
- safe_write(err, "remote: ", 8);
- safe_write(err, buf+1, len);
- safe_write(err, "\n", 1);
+ buf[7] = ' ';
+ buf[8+len] = '\n';
+ safe_write(err, buf, 8+len+1);
return SIDEBAND_REMOTE_ERROR;
case 2:
- safe_write(err, "remote: ", 8);
- safe_write(err, buf+1, len);
+ buf[7] = ' ';
+ safe_write(err, buf, 8+len);
continue;
case 1:
- safe_write(out, buf+1, len);
+ safe_write(out, buf+8, len);
continue;
default:
- len = sprintf(buf + 1,
+ len = sprintf(buf,
"%s: protocol error: bad band #%d\n",
- me, buf[0] & 0xFF);
- safe_write(err, buf+1, len);
+ me, band);
+ safe_write(err, buf, len);
return SIDEBAND_PROTOCOL_ERROR;
}
}
diff --git a/sideband.h b/sideband.h
index 4872106fa0..a84b6917c7 100644
--- a/sideband.h
+++ b/sideband.h
@@ -7,7 +7,7 @@
#define DEFAULT_PACKET_MAX 1000
#define LARGE_PACKET_MAX 65520
-int recv_sideband(const char *me, int in_stream, int out, int err, char *, int);
+int recv_sideband(const char *me, int in_stream, int out, int err);
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);
#endif
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
index c7db20e7f3..0272dd4293 100755
--- a/t/t1200-tutorial.sh
+++ b/t/t1200-tutorial.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Johannes Schindelin
#
-test_description='Test git-rev-parse with different parent options'
+test_description='A simple turial in the form of a test case'
. ./test-lib.sh
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
index 360a67060e..8b19d3ccea 100755
--- a/t/t3401-rebase-partial.sh
+++ b/t/t3401-rebase-partial.sh
@@ -52,13 +52,10 @@ test_expect_success \
'rebase topic branch against new master and check git-am did not get halted' \
'git-rebase master && test ! -d .dotest'
-if test -z "$no_python"
-then
- test_expect_success \
+test_expect_success \
'rebase --merge topic branch that was partially merged upstream' \
'git-checkout -f my-topic-branch-merge &&
git-rebase --merge master-merge &&
test ! -d .git/.dotest-merge'
-fi
test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
index d34c6cf6f3..0779aaa9ab 100755
--- a/t/t3402-rebase-merge.sh
+++ b/t/t3402-rebase-merge.sh
@@ -7,12 +7,6 @@ test_description='git rebase --merge test'
. ./test-lib.sh
-if test "$no_python"; then
- echo "Skipping: no python => no recursive merge"
- test_done
- exit 0
-fi
-
T="A quick brown fox
jumps over the lazy dog."
for i in 1 2 3 4 5 6 7 8 9 10
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
index bb25315361..977c498f00 100755
--- a/t/t3403-rebase-skip.sh
+++ b/t/t3403-rebase-skip.sh
@@ -10,12 +10,6 @@ test_description='git rebase --merge --skip tests'
# we assume the default git-am -3 --skip strategy is tested independently
# and always works :)
-if test "$no_python"; then
- echo "Skipping: no python => no recursive merge"
- test_done
- exit 0
-fi
-
test_expect_success setup '
echo hello > hello &&
git add hello &&
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
index ea48205537..58e5f74aea 100644
--- a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
@@ -5,7 +5,7 @@ Date: Mon Jun 26 00:00:00 2006 +0000
Initial
- create mode 040000 dir
+ create mode 100644 dir/sub
create mode 100644 file0
create mode 100644 file2
$
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
new file mode 100755
index 0000000000..1bc5b7a412
--- /dev/null
+++ b/t/t4015-diff-whitespace.sh
@@ -0,0 +1,122 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='Test special whitespace in diff engine.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh
+
+# Ray Lehtiniemi's example
+
+cat << EOF > x
+do {
+ nothing;
+} while (0);
+EOF
+
+git-update-index --add x
+
+cat << EOF > x
+do
+{
+ nothing;
+}
+while (0);
+EOF
+
+cat << EOF > expect
+diff --git a/x b/x
+index adf3937..6edc172 100644
+--- a/x
++++ b/x
+@@ -1,3 +1,5 @@
+-do {
++do
++{
+ nothing;
+-} while (0);
++}
++while (0);
+EOF
+
+git-diff > out
+test_expect_success "Ray's example without options" 'diff -u expect out'
+
+git-diff -w > out
+test_expect_success "Ray's example with -w" 'diff -u expect out'
+
+git-diff -b > out
+test_expect_success "Ray's example with -b" 'diff -u expect out'
+
+tr 'Q' '\015' << EOF > x
+whitespace at beginning
+whitespace change
+whitespace in the middle
+whitespace at end
+unchanged line
+CR at endQ
+EOF
+
+git-update-index x
+
+cat << EOF > x
+ whitespace at beginning
+whitespace change
+white space in the middle
+whitespace at end
+unchanged line
+CR at end
+EOF
+
+tr 'Q' '\015' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
+-whitespace change
+-whitespace in the middle
+-whitespace at end
++ whitespace at beginning
++whitespace change
++white space in the middle
++whitespace at end
+ unchanged line
+-CR at endQ
++CR at end
+EOF
+git-diff > out
+test_expect_success 'another test, without options' 'diff -u expect out'
+
+cat << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+EOF
+git-diff -w > out
+test_expect_success 'another test, with -w' 'diff -u expect out'
+
+tr 'Q' '\015' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
++ whitespace at beginning
+ whitespace change
+-whitespace in the middle
+-whitespace at end
++white space in the middle
++whitespace at end
+ unchanged line
+-CR at endQ
++CR at end
+EOF
+git-diff -b > out
+test_expect_success 'another test, with -b' 'diff -u expect out'
+
+test_done
diff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh
new file mode 100755
index 0000000000..7309422fe5
--- /dev/null
+++ b/t/t4118-apply-empty-context.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git-apply with new style GNU diff with empty context
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ {
+ echo; echo;
+ echo A; echo B; echo C;
+ echo;
+ } >file1 &&
+ cat file1 >file1.orig &&
+ {
+ cat file1 &&
+ echo Q | tr -d "\\012"
+ } >file2 &&
+ cat file2 >file2.orig
+ git add file1 file2 &&
+ sed -e "/^B/d" <file1.orig >file1 &&
+ sed -e "/^B/d" <file2.orig >file2 &&
+ cat file1 >file1.mods &&
+ cat file2 >file2.mods &&
+ git diff |
+ sed -e "s/^ \$//" >diff.output
+'
+
+test_expect_success 'apply --numstat' '
+
+ git apply --numstat diff.output >actual &&
+ {
+ echo "0 1 file1" &&
+ echo "0 1 file2"
+ } >expect &&
+ diff -u expect actual
+
+'
+
+test_expect_success 'apply --apply' '
+
+ cat file1.orig >file1 &&
+ cat file2.orig >file2 &&
+ git update-index file1 file2 &&
+ git apply --index diff.output &&
+ diff -u file1.mods file1 &&
+ diff -u file2.mods file2
+'
+
+test_done
+
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 278eb66701..cf08e9279c 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -26,6 +26,7 @@ commit id embedding:
. ./test-lib.sh
TAR=${TAR:-tar}
+UNZIP=${UNZIP:-unzip}
test_expect_success \
'populate workdir' \
@@ -95,4 +96,38 @@ test_expect_success \
'validate file contents with prefix' \
'diff -r a c/prefix/a'
+test_expect_success \
+ 'git-archive --format=zip' \
+ 'git-archive --format=zip HEAD >d.zip'
+
+test_expect_success \
+ 'extract ZIP archive' \
+ '(mkdir d && cd d && $UNZIP ../d.zip)'
+
+test_expect_success \
+ 'validate filenames' \
+ '(cd d/a && find .) | sort >d.lst &&
+ diff a.lst d.lst'
+
+test_expect_success \
+ 'validate file contents' \
+ 'diff -r a d/a'
+
+test_expect_success \
+ 'git-archive --format=zip with prefix' \
+ 'git-archive --format=zip --prefix=prefix/ HEAD >e.zip'
+
+test_expect_success \
+ 'extract ZIP archive with prefix' \
+ '(mkdir e && cd e && $UNZIP ../e.zip)'
+
+test_expect_success \
+ 'validate filenames with prefix' \
+ '(cd e/prefix/a && find .) | sort >e.lst &&
+ diff a.lst e.lst'
+
+test_expect_success \
+ 'validate file contents with prefix' \
+ 'diff -r a e/prefix/a'
+
test_done
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 0c6a363be9..041be04f5c 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -25,6 +25,12 @@ test_create_repo foo
# clone doesn't like it if there is no HEAD. Is that a bug?
(cd foo && touch file && git add file && git commit -m 'add file' >/dev/null 2>&1)
+# source repository given to git-clone should be relative to the
+# current path not to the target dir
+test_expect_failure \
+ 'clone of non-existent (relative to $PWD) source should fail' \
+ 'git-clone ../foo baz'
+
test_expect_success \
'clone should work now that source exists' \
'git-clone foo bar'
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index 8f7366da8d..499cafb882 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -10,12 +10,6 @@
test_description='Test criss-cross merge'
. ./test-lib.sh
-if test "$no_python"; then
- echo "Skipping: no python => no recursive merge"
- test_done
- exit 0
-fi
-
test_expect_success 'prepare repository' \
'echo "1
2
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
index 5ac25647ab..497ef36ad8 100755
--- a/t/t6022-merge-rename.sh
+++ b/t/t6022-merge-rename.sh
@@ -3,12 +3,6 @@
test_description='Merge-recursive merging renames'
. ./test-lib.sh
-if test "$no_python"; then
- echo "Skipping: no python => no recursive merge"
- test_done
- exit 0
-fi
-
test_expect_success setup \
'
cat >A <<\EOF &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index b523fef339..07cb706fa8 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -135,6 +135,7 @@ test_expect_failure () {
else
test_failure_ "$@"
fi
+ echo >&3 ""
}
test_expect_success () {
@@ -148,6 +149,7 @@ test_expect_success () {
else
test_failure_ "$@"
fi
+ echo >&3 ""
}
test_expect_code () {
@@ -161,6 +163,7 @@ test_expect_code () {
else
test_failure_ "$@"
fi
+ echo >&3 ""
}
# Most tests can use the created repository, but some amy need to create more.
@@ -204,7 +207,8 @@ test_done () {
# t/ subdirectory and are run in trash subdirectory.
PATH=$(pwd)/..:$PATH
GIT_EXEC_PATH=$(pwd)/..
-export PATH GIT_EXEC_PATH
+HOME=$(pwd)/trash
+export PATH GIT_EXEC_PATH HOME
# Similarly use ../compat/subprocess.py if our python does not
# have subprocess.py on its own.
diff --git a/trace.c b/trace.c
index f9efc918b8..495e5ed92a 100644
--- a/trace.c
+++ b/trace.c
@@ -55,7 +55,8 @@ static int get_trace_fd(int *need_close)
{
char *trace = getenv("GIT_TRACE");
- if (!trace || !strcmp(trace, "0") || !strcasecmp(trace, "false"))
+ if (!trace || !strcmp(trace, "") ||
+ !strcmp(trace, "0") || !strcasecmp(trace, "false"))
return 0;
if (!strcmp(trace, "1") || !strcasecmp(trace, "true"))
return STDERR_FILENO;
diff --git a/upload-pack.c b/upload-pack.c
index 9412a9b260..ddaa72f0a9 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -16,7 +16,7 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n
#define OUR_REF (1U << 1)
#define WANTED (1U << 2)
static int multi_ack, nr_our_refs;
-static int use_thin_pack;
+static int use_thin_pack, use_ofs_delta;
static struct object_array have_obj;
static struct object_array want_obj;
static unsigned int timeout;
@@ -137,7 +137,9 @@ static void create_pack_file(void)
close(pu_pipe[1]);
close(pe_pipe[0]);
close(pe_pipe[1]);
- execl_git_cmd("pack-objects", "--stdout", "--progress", NULL);
+ execl_git_cmd("pack-objects", "--stdout", "--progress",
+ use_ofs_delta ? "--delta-base-offset" : NULL,
+ NULL);
kill(pid_rev_list, SIGKILL);
die("git-upload-pack: unable to exec git-pack-objects");
}
@@ -393,6 +395,8 @@ static void receive_needs(void)
multi_ack = 1;
if (strstr(line+45, "thin-pack"))
use_thin_pack = 1;
+ if (strstr(line+45, "ofs-delta"))
+ use_ofs_delta = 1;
if (strstr(line+45, "side-band-64k"))
use_sideband = LARGE_PACKET_MAX;
else if (strstr(line+45, "side-band"))
@@ -418,7 +422,7 @@ static void receive_needs(void)
static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
- static const char *capabilities = "multi_ack thin-pack side-band side-band-64k";
+ static const char *capabilities = "multi_ack thin-pack side-band side-band-64k ofs-delta";
struct object *o = parse_object(sha1);
if (!o)
diff --git a/wt-status.c b/wt-status.c
index d8e284c311..7dd68575d1 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -70,25 +70,25 @@ static void wt_status_print_filepair(int t, struct diff_filepair *p)
color_printf(color(WT_STATUS_HEADER), "#\t");
switch (p->status) {
case DIFF_STATUS_ADDED:
- color_printf(c, "new file: %s", p->one->path); break;
+ color_printf(c, "new file: %s", p->one->path); break;
case DIFF_STATUS_COPIED:
- color_printf(c, "copied: %s -> %s",
+ color_printf(c, "copied: %s -> %s",
p->one->path, p->two->path);
break;
case DIFF_STATUS_DELETED:
- color_printf(c, "deleted: %s", p->one->path); break;
+ color_printf(c, "deleted: %s", p->one->path); break;
case DIFF_STATUS_MODIFIED:
- color_printf(c, "modified: %s", p->one->path); break;
+ color_printf(c, "modified: %s", p->one->path); break;
case DIFF_STATUS_RENAMED:
- color_printf(c, "renamed: %s -> %s",
+ color_printf(c, "renamed: %s -> %s",
p->one->path, p->two->path);
break;
case DIFF_STATUS_TYPE_CHANGED:
color_printf(c, "typechange: %s", p->one->path); break;
case DIFF_STATUS_UNKNOWN:
- color_printf(c, "unknown: %s", p->one->path); break;
+ color_printf(c, "unknown: %s", p->one->path); break;
case DIFF_STATUS_UNMERGED:
- color_printf(c, "unmerged: %s", p->one->path); break;
+ color_printf(c, "unmerged: %s", p->one->path); break;
default:
die("bug: unhandled diff status %c", p->status);
}
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 714c563547..07995ec33e 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -86,11 +86,10 @@ static void xdl_find_func(xdfile_t *xf, long i, char *buf, long sz, long *ll) {
if (len > 0 &&
(isalpha((unsigned char)*rec) || /* identifier? */
*rec == '_' || /* also identifier? */
- *rec == '(' || /* lisp defun? */
- *rec == '#')) { /* #define? */
+ *rec == '$')) { /* mysterious GNU diff's invention */
if (len > sz)
len = sz;
- if (len && rec[len - 1] == '\n')
+ while (0 < len && isspace((unsigned char)rec[len - 1]))
len--;
memcpy(buf, rec, len);
*ll = len;
diff --git a/xdiff/xmacros.h b/xdiff/xmacros.h
index 4c2fde80c1..e2cd2023b3 100644
--- a/xdiff/xmacros.h
+++ b/xdiff/xmacros.h
@@ -24,14 +24,15 @@
#define XMACROS_H
-#define GR_PRIME 0x9e370001UL
#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
-#define XDL_HASHLONG(v, b) (((unsigned long)(v) * GR_PRIME) >> ((CHAR_BIT * sizeof(unsigned long)) - (b)))
+#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b)))
+#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
+#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
#define XDL_LE32_PUT(p, v) \
do { \
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index f7bdd395ad..9e4bb47ee9 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -191,36 +191,30 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
int i1, i2;
if (flags & XDF_IGNORE_WHITESPACE) {
- for (i1 = i2 = 0; i1 < s1 && i2 < s2; i1++, i2++) {
+ for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
if (isspace(l1[i1]))
while (isspace(l1[i1]) && i1 < s1)
i1++;
- else if (isspace(l2[i2]))
+ if (isspace(l2[i2]))
while (isspace(l2[i2]) && i2 < s2)
i2++;
- else if (l1[i1] != l2[i2])
- return l2[i2] - l1[i1];
+ if (i1 < s1 && i2 < s2 && l1[i1++] != l2[i2++])
+ return 0;
}
- if (i1 >= s1)
- return 1;
- else if (i2 >= s2)
- return -1;
+ return (i1 >= s1 && i2 >= s2);
} else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
- for (i1 = i2 = 0; i1 < s1 && i2 < s2; i1++, i2++) {
+ for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
if (isspace(l1[i1])) {
if (!isspace(l2[i2]))
- return -1;
+ return 0;
while (isspace(l1[i1]) && i1 < s1)
i1++;
while (isspace(l2[i2]) && i2 < s2)
i2++;
- } else if (l1[i1] != l2[i2])
- return l2[i2] - l1[i1];
+ } else if (l1[i1++] != l2[i2++])
+ return 0;
}
- if (i1 >= s1)
- return 1;
- else if (i2 >= s2)
- return -1;
+ return (i1 >= s1 && i2 >= s2);
} else
return s1 == s2 && !memcmp(l1, l2, s1);
@@ -233,7 +227,8 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
for (; ptr < top && *ptr != '\n'; ptr++) {
if (isspace(*ptr) && (flags & XDF_WHITESPACE_FLAGS)) {
- while (ptr < top && isspace(*ptr) && ptr[1] != '\n')
+ while (ptr + 1 < top && isspace(ptr[1])
+ && ptr[1] != '\n')
ptr++;
if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
ha += (ha << 5);