Git development
 help / color / mirror / Atom feed
* [PATCH] remove unnecessary 'if'
From: Alexander Potashev @ 2008-12-10 14:09 UTC (permalink / raw)
  To: git; +Cc: gitster, Alexander Potashev

'patch->is_new' is always <= 0 at this point (look at 'assert' at the
beginning of the function). In both cases ('is_new < 0' and 'is_new == 0')
the result of those two lines is zeroing of 'is_new'.

Signed-off-by: Alexander Potashev <aspotashev@gmail.com>
---
 builtin-apply.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)

diff --git a/builtin-apply.c b/builtin-apply.c
index 4c4d1e1..904a748 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2440,8 +2440,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
 	if (!cached)
 		st_mode = ce_mode_from_stat(*ce, st->st_mode);
 
-	if (patch->is_new < 0)
-		patch->is_new = 0;
+	patch->is_new = 0;
 	if (!patch->old_mode)
 		patch->old_mode = st_mode;
 	if ((st_mode ^ patch->old_mode) & S_IFMT)
-- 
1.6.0.4

^ permalink raw reply related

* Re: builtin-add.c patch
From: Alexander Potashev @ 2008-12-10 14:26 UTC (permalink / raw)
  To: daly; +Cc: git
In-Reply-To: <200812101238.mBACcWQk023480@axiom-developer.org>

Hello, Tim!

On 06:38 Wed 10 Dec     , daly@axiom-developer.org wrote:
> A trivial patch to fix a typo -- Tim Daly
> 
> 
> diff --git a/builtin-add.c b/builtin-add.c
> index ea4e771..5f2e68b 100644
> --- a/builtin-add.c
> +++ b/builtin-add.c
> @@ -23,7 +23,7 @@ static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
>  	int num_unmatched = 0, i;
>  
>  	/*
> -	 * Since we are walking the index as if we are warlking the directory,
> +	 * Since we are walking the index as if we are walking the directory,
We probably should use subjunctive here:
"Since we are walking the index as if we _were_ walking the directory,".

Are there any native English speakers? :)
>  	 * we have to mark the matched pathspec as seen; otherwise we will
>  	 * mistakenly think that the user gave a pathspec that did not match
>  	 * anything.
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

It's also better to change the commit message, one should be able to
realize from it that the change is a typo fix in comments.

^ permalink raw reply

* [PATCH v2] git-sh-setup: Fix scripts whose PWD is a symlink into a git work-dir
From: Marcel M. Cary @ 2008-12-10 15:04 UTC (permalink / raw)
  To: gitster, git; +Cc: jnareb, ae, j.sixt, Marcel M. Cary
In-Reply-To: <7viqq1hghw.fsf@gitster.siamese.dyndns.org>

* When interpretting a relative upward (../) path in cd_to_toplevel,
  prepend the cwd without symlinks, given by /bin/pwd
* Add tests for cd_to_toplevel and "git pull" in a symlinked
  directory that failed before this fix, plus constrasting
  scenarios that already worked

Signed-off-by: Marcel M. Cary <marcel@oak.homeunix.org>
---

Now /bin/pwd is only used when $cdup both is relative
and contains a ".." component, which I think is most of
the time that git didn't start out in the top-level
directory.

I can't seem to exercise the case that show-cdup prints
something other than "../" or "", even when setting
GIT_WORK_TREE in or out of the work tree.  Is it premature
to anticipate that show-cdup might print an arbitrary path
sometime in the future, given the 
"if (!is_inside_work_tree())" branch of show-cdup?  Or maybe
it does now and I just don't see the use case.

Also, the additional invocation of "cd" is now removed.


 git-sh-setup.sh           |   23 ++++++++++++++-
 t/t2300-cd-to-toplevel.sh |   37 +++++++++++++++++++++++++
 t/t5521-pull-symlink.sh   |   67 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 125 insertions(+), 2 deletions(-)
 create mode 100755 t/t2300-cd-to-toplevel.sh
 create mode 100755 t/t5521-pull-symlink.sh

diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index dbdf209..f07d96b 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -85,8 +85,27 @@ cd_to_toplevel () {
 	cdup=$(git rev-parse --show-cdup)
 	if test ! -z "$cdup"
 	then
-		cd "$cdup" || {
-			echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
+		case "$cdup" in
+		/*)
+			# Not quite the same as if we did "cd -P '$cdup'" when
+			# $cdup contains ".." after symlink path components.
+			# Don't fix that case at least until Git switches to
+			# "cd -P" across the board.
+			phys="$cdup"
+			;;
+		..|../*|*/..|*/../*)
+			# Interpret $cdup relative to the physical, not logical, cwd.
+			# Probably /bin/pwd is more portable than passing -P to cd or pwd.
+			phys="$(/bin/pwd)/$cdup"
+			;;
+		*)
+			# There's no "..", so no need to make things absolute.
+			phys="$cdup"
+			;;
+		esac
+
+		cd "$phys" || {
+			echo >&2 "Cannot chdir to $phys, the toplevel of the working tree"
 			exit 1
 		}
 	fi
diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh
new file mode 100755
index 0000000..293dc35
--- /dev/null
+++ b/t/t2300-cd-to-toplevel.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='cd_to_toplevel'
+
+. ./test-lib.sh
+
+test_cd_to_toplevel () {
+	test_expect_success "$2" '
+		(
+			cd '"'$1'"' &&
+			. git-sh-setup &&
+			cd_to_toplevel &&
+			[ "$(pwd -P)" = "$TOPLEVEL" ]
+		)
+	'
+}
+
+TOPLEVEL="$(pwd -P)/repo"
+mkdir -p repo/sub/dir
+mv .git repo/
+SUBDIRECTORY_OK=1
+
+test_cd_to_toplevel repo 'at physical root'
+
+test_cd_to_toplevel repo/sub/dir 'at physical subdir'
+
+ln -s repo symrepo
+test_cd_to_toplevel symrepo 'at symbolic root'
+
+ln -s repo/sub/dir subdir-link
+test_cd_to_toplevel subdir-link 'at symbolic subdir'
+
+cd repo
+ln -s sub/dir internal-link
+test_cd_to_toplevel internal-link 'at internal symbolic subdir'
+
+test_done
diff --git a/t/t5521-pull-symlink.sh b/t/t5521-pull-symlink.sh
new file mode 100755
index 0000000..f18fec7
--- /dev/null
+++ b/t/t5521-pull-symlink.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='pulling from symlinked subdir'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+# The scenario we are building:
+#
+#   trash\ directory/
+#     clone-repo/
+#       subdir/
+#         bar
+#     subdir-link -> clone-repo/subdir/
+#
+# The working directory is subdir-link.
+#
+test_expect_success setup '
+
+    mkdir subdir &&
+    touch subdir/bar &&
+    git add subdir/bar &&
+    git commit -m empty &&
+    git clone . clone-repo &&
+    # demonstrate that things work without the symlink
+    test_debug "cd clone-repo/subdir/ && git pull; cd ../.." &&
+    ln -s clone-repo/subdir/ subdir-link &&
+    cd subdir-link/ &&
+    test_debug "set +x"
+'
+
+# From subdir-link, pulling should work as it does from
+# clone-repo/subdir/.
+#
+# Instead, the error pull gave was:
+#
+#   fatal: 'origin': unable to chdir or not a git archive
+#   fatal: The remote end hung up unexpectedly
+#
+# because git would find the .git/config for the "trash directory"
+# repo, not for the clone-repo repo.  The "trash directory" repo
+# had no entry for origin.  Git found the wrong .git because
+# git rev-parse --show-cdup printed a path relative to
+# clone-repo/subdir/, not subdir-link/.  Git rev-parse --show-cdup
+# used the correct .git, but when the git pull shell script did
+# "cd `git rev-parse --show-cdup`", it ended up in the wrong
+# directory.  Shell "cd" works a little different from chdir() in C.
+# Bash's "cd -P" works like chdir() in C.
+#
+test_expect_success 'pulling from symlinked subdir' '
+
+    git pull
+'
+
+# Prove that the remote end really is a repo, and other commands
+# work fine in this context.
+#
+test_debug "
+    test_expect_success 'pushing from symlinked subdir' '
+
+        git push
+    '
+"
+cd "$D"
+
+test_done
-- 
1.6.0.3

^ permalink raw reply related

* Re: [PATCH 2/3] gitweb: Cache $parent_commit info in git_blame()
From: Jakub Narebski @ 2008-12-10 15:15 UTC (permalink / raw)
  To: Luben Tuikov; +Cc: git
In-Reply-To: <182871.96175.qm@web31804.mail.mud.yahoo.com>

On Wed, 10 Dec 2008, Luben Tuikov wrote:
> --- On Tue, 12/9/08, Jakub Narebski <jnareb@gmail.com> wrote:

> > This patch attempts to migitate issue [of performance] a bit by
> > caching $parent_commit info in %metainfo, which makes gitweb to
> > call git-rev-parse only once per unique commit in blame output.
> 
> Have you tested this patch that it gives the same commit chain
> as before it?

The only difference between precious version and this patch is that
now, if you calculate sha-1 of $long_rev^, it is stored in 
$metainfo{$long_rev}{'parent'} and not calculated second time.

But I have checked that (at least for single example file) the blame
output is identical for before and after this patch.


> > That is what I have noticed during browsing git_blame()
> > code.
> 
> What?

What I have noticed? I have noticed this inefficiency.
Why I was browsing git_blame? To write git_blame_incremental...

See also my reply to Nanako Shiraishi with simple benchmark.

> > We can change it to even more effective implementation
> > (like the ones proposed above in the commit message) later.
> 
> Where?

jn> Unfortunately the implementation in 244a70e used one call for
jn> git-rev-parse to find parent revision per line in file, instead of
jn> using long lived "git cat-file --batch-check" (which might not existed
jn> then), or changing validate_refname to validate_revision and made it
jn> accept <rev>^, <rev>^^, <rev>^^^ etc. syntax.

One solution mentioned there is to open bidi pipe (like in Git::Repo
in gitweb caching by Lea Wiemann) to "git cat-file --batch-check"
(the '--batch-check' option was added to git-cat-file by Adam Roben
on Apr 23, 2008 in v1.5.6-rc0~8^2~9), feed it $long_rev^, and parse
its output of the form:

  926b07e694599d86cec668475071b32147c95034 commit 637

see manpage for git-cat-file(1). Unfortunately it seems like 
command_bidi_pipe doesn't work as _I_ expected...


Another solution mentioned there is to change validate_refname to
validate_revision when checking script parameters (CGI query or
path_info), with validate_revision being something like:

  sub validate_revision {
  	my $rev = shift;
	return validate_refname(strip_rev_suffixes($rev));
  }

or something like that, so we don't need to calculate $long_rev^,
but can pass "$long_rev^" as 'hb' parameter ($long_rev can in turn
also end in '^').

-- 
Jakub Narebski
Poland

^ permalink raw reply

* malloc fails when dealing with huge files
From: Jonathan Blanton @ 2008-12-10 15:42 UTC (permalink / raw)
  To: git

I'm using Git for a project that contains huge (multi-gigabyte) files.
 I need to track these files, but with some of the really big ones,
git-add aborts with the message "fatal: Out of memory, malloc failed".
 Also, git-gc sometimes fails because it can't allocate enough memory.
 I've been using the "--window-memory" option to git- repack to work
around the git-gc problem, but I don't know of a similar trick for
git-add.  Is there any way (aside from adding more memory, of course)
that I can deal with these huge files?  I'm using git 1.5.6.2 on
Debian 4.0.

Thanks in advance.

Jonathan Blanton

^ permalink raw reply

* Re: builtin-add.c patch
From: Boyd Stephen Smith Jr. @ 2008-12-10 16:01 UTC (permalink / raw)
  To: Alexander Potashev; +Cc: daly, git
In-Reply-To: <20081210142632.GA4137@myhost>

[-- Attachment #1: Type: text/plain, Size: 1019 bytes --]

On Wednesday 2008 December 10 08:26:32 Alexander Potashev wrote:
>> diff --git a/builtin-add.c b/builtin-add.c
>> index ea4e771..5f2e68b 100644
>> --- a/builtin-add.c
>> +++ b/builtin-add.c
>> @@ -23,7 +23,7 @@ static void fill_pathspec_matches(const char **pathspec,
>> char *seen, int specs) int num_unmatched = 0, i;
>>
>>  	/*
>> -	 * Since we are walking the index as if we are warlking the directory,
>> +	 * Since we are walking the index as if we are walking the directory,
>
>We probably should use subjunctive here:
>"Since we are walking the index as if we _were_ walking the directory,".
>
>Are there any native English speakers? :)

Southern bah the grace o' gawd. :)

Yes, the subjunctive is the appropriate tense for use in hypotheticals ("as 
if").
-- 
Boyd Stephen Smith Jr.                     ,= ,-_-. =. 
bss03@volumehost.net                      ((_/)o o(\_))
ICQ: 514984 YM/AIM: DaTwinkDaddy           `-'(. .)`-' 
http://iguanasuicide.org/                      \_/     

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* fatal output from git-show really wants a terminal
From: Tim Olsen @ 2008-12-10 16:01 UTC (permalink / raw)
  To: git

It appears that when outputting a fatal error, git-show will choose
stdout over stderr if stdout is a terminal and stderr is not.  How do I
redirect the error but still allow stdout to be displayed?

~/git$ mkdir test

~/git$ cd test

~/git/test$ git init

~/git/test$ git show 12345

fatal: ambiguous argument '12345': unknown revision or path not in the
working tree.
Use '--' to separate paths from revisions

~/git/test$ git show 12345 2> /dev/null

fatal: ambiguous argument '12345': unknown revision or path not in the
working tree.
Use '--' to separate paths from revisions

~/git/test$ git show 12345 > /dev/null

fatal: ambiguous argument '12345': unknown revision or path not in the
working tree.
Use '--' to separate paths from revisions

~/git/test$ git show 12345 > /dev/null 2> /dev/null

~/git/test$ git show 12345 > /tmp/out 2> /tmp/err

~/git/test$ cat /tmp/out

~/git/test$ cat /tmp/err

fatal: ambiguous argument '12345': unknown revision or path not in the
working tree.
Use '--' to separate paths from revisions

^ permalink raw reply

* Re: fatal output from git-show really wants a terminal
From: Boyd Stephen Smith Jr. @ 2008-12-10 16:10 UTC (permalink / raw)
  To: git; +Cc: Tim Olsen
In-Reply-To: <ghop5d$qud$1@ger.gmane.org>

[-- Attachment #1: Type: text/plain, Size: 701 bytes --]

On Wednesday 2008 December 10 10:01:49 you wrote:
>It appears that when outputting a fatal error, git-show will choose
>stdout over stderr if stdout is a terminal and stderr is not.  How do I
>redirect the error but still allow stdout to be displayed?

Gah, I think that's really bad behavior. Anyway, something like:
git show 12345 2>/dev/null | cat
should work.  Neither stdout nor stderr will be a terminal, but stdout will be 
displayed to your terminal.
-- 
Boyd Stephen Smith Jr.                     ,= ,-_-. =. 
bss03@volumehost.net                      ((_/)o o(\_))
ICQ: 514984 YM/AIM: DaTwinkDaddy           `-'(. .)`-' 
http://iguanasuicide.org/                      \_/     

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: Annotating patches inside diff
From: Johannes Schindelin @ 2008-12-10 16:58 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <200812101445.48034.jnareb@gmail.com>

Hi,

On Wed, 10 Dec 2008, Jakub Narebski wrote:

> I remember that long time ago on git mailing list there was discussed 
> extending git-apply and friends (including git-am), to be able to 
> ignore lines in patches with selected special prefix, different from 
> '@' for chunks headers, ' ' for context, '+'/'-' for added/deleted 
> lines.  IIRC it was chose '|' for this purpose.
> 
> This way you could annotate patch
> 
> @@ -4667,7 +4667,6 @@ HTML
>                                   hash_base => $parent_commit);
>                 print "<td class=\"linenr\">";
>                 print $cgi->a({ -href => "$blamed#l$orig_lineno",
> | moved to <tr>
> -                               -id => "l$lineno",
>                                 -class => "linenr" },
>                               esc_html($lineno));
>                 print "</td>";
> 
> 
> Was it accepted or dropped, or is this feature present but not 
> documented?

As I said on IRC, I think that if you are too good in the hiding-comments 
business, you can just spare the time to write them, 'cause nobody will 
find them.

IOW such a comment needs to go either into the commit message (if it is an 
important API change), so that people who do not remember discussions on 
the mailing list still have a chance to find the comment, or between the 
message and the diffstat (if it is less important).

Ciao,
Dscho

^ permalink raw reply

* Re: help needed: Splitting a git repository after subversion migration
From: Thomas Jarosch @ 2008-12-10 16:33 UTC (permalink / raw)
  To: Björn Steinbrink; +Cc: Michael J Gruber, git
In-Reply-To: <200812081834.26688.thomas.jarosch@intra2net.com>

On Monday, 8. December 2008 18:34:20 Thomas Jarosch wrote:
> 1. When I run "git rev-list --all --objects", I can see file names that
> look like "SVN-branchname/directory/filename". Is it normal that "git svn"
> creates a directory with the name of the branch and puts files below it?

Ok, this seems to be a PEBKAC: In the history of the subversion repository, 
f.e. I once copied the "branches" root folder to tags/xyz. One revision later 
I noticed this and retagged the correct branch. git-svn imports all branches
from the first tag, which is the correct thing to do :o)

Now I'll manually check the history of the tags/ and branches/ folder
for more funny tags and write down the revision. If I understood
the git-svn man page correctly, I should be able to specifiy
revision ranges it's going to import. I'll try to skip the broken tags.

Cheers,
Thomas

^ permalink raw reply

* after first git clone of linux kernel repository there are changed files in working dir
From: rdkrsr @ 2008-12-10 18:22 UTC (permalink / raw)
  To: git
In-Reply-To: <d304880b0812101019ufe85095h46ff0fe00d32bbd0@mail.gmail.com>

I just fetched the sources without changing anything, but git diff
shows, that there are changes that are not yet updated (changed but not
updated: use git add to ...). Why is it like that?

I use msysgit on windows, maybe that is one reason?

^ permalink raw reply

* Re: Annotating patches inside diff
From: Junio C Hamano @ 2008-12-10 18:40 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <200812101445.48034.jnareb@gmail.com>

Jakub Narebski <jnareb@gmail.com> writes:

> I remember that long time ago on git mailing list there was discussed 
> extending git-apply and friends (including git-am), to be able to 
> ignore lines in patches with selected special prefix, different from 
> '@' for chunks headers, ' ' for context, '+'/'-' for added/deleted 
> lines.  IIRC it was chose '|' for this purpose.

That's a blast from the past.

> This way you could annotate patch
>
> @@ -4667,7 +4667,6 @@ HTML
> ...
>
> Was it accepted or dropped, or is this feature present but not 
> documented?

The code is simple, but I do not think it is a good idea to do so:

 * If it is about describing the state (what it does, how it does it and
   why), you should be writing that as in-code comments;

 * If it is about describing the change (what it used to do and how, and
   what it does after your change and how, and why you changed it that
   way), i.e. "we used to do X but now we do Y for such and such reasons"
   (your example is a good one, except in real life you would want to
   state "why" as well), you should be writing that in the commit log
   message; and

 * If describing the change in the commit log message feels insufficient,
   it probably is a sign that the patch is too big and covers too many
   topics.

In other words, I think the problem the old patch attempted to solve was
this:

   When you do too many things in a patch touching many places of the
   code, and want to explain and justify all of them, your commit log
   message becomes a list of "we used to do X but 7th hunk changes it to Y
   because of Z" entries for different X, Y and Z.  When this list gets
   too big, it is easier to read through the patch if such explanation are
   done close to where the changes described are.

For your reference, this was the dropped patch.

From: Junio C Hamano <junkio@cox.net>
Date: Fri, 8 Dec 2006 21:03:52 -0800
Subject: [PATCH] mailinfo: hack to accept in-line annotations in patches.

Long before git-apply, when I wanted to talk about rationale of
individual changes, I used to add annotation between hunks
(delimited @@ -n,m, +l,k @@) as unindented plain text and rely
on GNU patch to discard them as garbage.  Because git-apply is
much less forgiving than GNU patch, this is not possible.

This patch teaches mailinfo that lines that begin with a '|' would
never appear in the patch text and can be discarded safely.
Which means that we can generate a patch as usual using format-patch,
and add annotations inline, prefixed with '|'.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-mailinfo.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index c95e477..2608260 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -720,8 +720,10 @@ static int handle_commit_msg(char *line)
 
 static int handle_patch(char *line)
 {
-	fputs(line, patchfile);
-	patch_lines++;
+	if (line[0] != '|') {
+		fputs(line, patchfile);
+		patch_lines++;
+	}
 	return 0;
 }
 

^ permalink raw reply related

* Re: Forcing --no-ff on pull
From: Stephen Haberman @ 2008-12-10 19:07 UTC (permalink / raw)
  To: Daniel Barkalow; +Cc: R. Tyler Ballance, Nanako Shiraishi, git
In-Reply-To: <alpine.LNX.1.00.0812091651360.19665@iabervon.org>

On Tue, 9 Dec 2008 17:32:36 -0500 (EST)
Daniel Barkalow <barkalow@iabervon.org> wrote:

> > At this point, QA is involved and what can happen is that QA realizes
> > that this code is *not* stable and *never* should have been brought into
> > the stable branch.
>
> How do you prevent the (IMHO more likely) case of:
> 
> % git checkout -b project
> % git checkout stable
> <fix some bug in stable>
> % git commit -a
> <forget to switch branches back>
> <work>
> % git commit -am "A"
> <work>
> % git commit -am "B"
> ...
> % git push origin stable
> 
> That is, the developer makes a whole bunch of inappropriate commits on 
> their stable branch instead of their project branch and then pushes it out 
> (perhaps as part of a push rule, or thinking only the bug fix went there). 
> I suspect that "pull" step there isn't the point where things are going 
> wrong.

Well, two things:

1) The hook script at [1] really would prevent this from getting published.
   Although it only looks for "stable"--if you have per-team stable branches,
   you might need to match on "*-stable" or something like that. But it does
   (copy/paste from [1]):

# * stable must move by only 1 commit-per-push
# * the stable commit must have 2 and only 2 parents
#   * The first parent must be the previous stable commit
#   * The second parent is the tip of the candidate branch being released
# * the stable commit must have the same contents as the candidate tip
#   * Any merge conflicts should have been resolved in the candidate tip
#     by pulling stable into the candidate and having qa/tests done--pulling
#     candidate into stable should then apply cleanly

So, no fast forwards, no direct commits, only "good"/empty merges of
topic branches can move stable. Anything else is rejected and LazyDev
has to try again.

2) As far as "pull" isn't where things are going wrong, that is not
   entirely true, as even with the server-side enforcement like [1],
   I think you'd still like to help LazyDev out and have `git pull`
   "just work" for your given setup. Especially if you don't have full
   management buy-in to git, pacifying LazyDev's can be necessary.

- Stephen

1: http://github.com/stephenh/gc/tree/master/server/update-stable

^ permalink raw reply

* Re: builtin-add.c patch
From: root @ 2008-12-10 19:10 UTC (permalink / raw)
  To: aspotashev; +Cc: daly, git
In-Reply-To: <20081210142632.GA4137@myhost>

Alexander,

Feel free to change the patch.
Its hardly worth the bits of email used
but it does need to be fixed.

Tim

^ permalink raw reply

* Re: builtin-add.c patch
From: root @ 2008-12-10 19:14 UTC (permalink / raw)
  To: aspotashev; +Cc: daly, git
In-Reply-To: <20081210142632.GA4137@myhost>

Alexander,

I saw a suggestion that git could be used as a filesystem rather
than as a code repository. I'm looking to convert it for this
purpose to sit underneath Axiom, a computer algebra system written
in common lisp. Basically the idea is that a "close" operation does
a 'git add foo ; git commit'. 

Are you aware of anyone who has used git as a filesystem?

Tim Daly

^ permalink raw reply

* Re: malloc fails when dealing with huge files
From: Linus Torvalds @ 2008-12-10 19:32 UTC (permalink / raw)
  To: Jonathan Blanton; +Cc: git
In-Reply-To: <43c10b980812100742t3a65466yb9b7310bfedb2b18@mail.gmail.com>



On Wed, 10 Dec 2008, Jonathan Blanton wrote:
>
> I'm using Git for a project that contains huge (multi-gigabyte) files.
>  I need to track these files, but with some of the really big ones,
> git-add aborts with the message "fatal: Out of memory, malloc failed".

git is _really_ not designed for huge files.

By design - good or bad - git does pretty much all single file operations 
with the whole file in memory as one single allocation. 

Now, some of that is hard to fix - or at least would generate much more 
complex code. The _particular_ case of "git add" could be fixed without 
undue pain, but it's not entirely trivial either.

The main offender is probably "index_fd()" that just mmap's the whole file 
in one go and then calls write_sha1_file() which really expects it to be 
one single memory area both for the initial SHA1 create and for the 
compression and writing out of the result.

Changing that to do big files in pieces would not be _too_ painful, but 
it's not just a couple of lines either.

However, git performance with big files would never be wonderful, and 
things like "git diff" would still end up reading not just the whole file, 
but _both_versions_ at the same time. Marking the big files as being 
no-diff might help, though.


			Linus

^ permalink raw reply

* [PATCH 1/2] bash completion: Add '--intent-to-add' long option for 'git add'
From: Lee Marlow @ 2008-12-10 19:39 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git, Lee Marlow

Signed-off-by: Lee Marlow <lee.marlow@gmail.com>
---
 contrib/completion/git-completion.bash |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index c79c98f..5356e5b 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -563,7 +563,7 @@ _git_add ()
 	--*)
 		__gitcomp "
 			--interactive --refresh --patch --update --dry-run
-			--ignore-errors
+			--ignore-errors --intent-to-add
 			"
 		return
 	esac
-- 
1.6.1.rc2.14.g5363d

^ permalink raw reply related

* [PATCH 2/2] bash completion: Use 'git add' completions for 'git stage'
From: Lee Marlow @ 2008-12-10 19:39 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git, Lee Marlow
In-Reply-To: <1228937958-5091-1-git-send-email-lee.marlow@gmail.com>

Signed-off-by: Lee Marlow <lee.marlow@gmail.com>
---
 contrib/completion/git-completion.bash |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 5356e5b..7e2b482 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1660,6 +1660,7 @@ _git ()
 	show)        _git_show ;;
 	show-branch) _git_show_branch ;;
 	stash)       _git_stash ;;
+	stage)       _git_add ;;
 	submodule)   _git_submodule ;;
 	svn)         _git_svn ;;
 	tag)         _git_tag ;;
-- 
1.6.1.rc2.14.g5363d

^ permalink raw reply related

* Re: [PATCH 1/2] bash completion: Add '--intent-to-add' long option for 'git add'
From: Shawn O. Pearce @ 2008-12-10 19:41 UTC (permalink / raw)
  To: Lee Marlow; +Cc: git
In-Reply-To: <1228937958-5091-1-git-send-email-lee.marlow@gmail.com>

Lee Marlow <lee.marlow@gmail.com> wrote:
> Signed-off-by: Lee Marlow <lee.marlow@gmail.com>
> ---
>  contrib/completion/git-completion.bash |    2 +-
>  1 files changed, 1 insertions(+), 1 deletions(-)

Trivially-Acked-by: Shawn O. Pearce <spearce@spearce.org>

 
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index c79c98f..5356e5b 100755
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -563,7 +563,7 @@ _git_add ()
>  	--*)
>  		__gitcomp "
>  			--interactive --refresh --patch --update --dry-run
> -			--ignore-errors
> +			--ignore-errors --intent-to-add
>  			"
>  		return
>  	esac
> -- 
> 1.6.1.rc2.14.g5363d
> 

-- 
Shawn.

^ permalink raw reply

* Re: fatal output from git-show really wants a terminal
From: Johannes Sixt @ 2008-12-10 19:46 UTC (permalink / raw)
  To: Tim Olsen; +Cc: git
In-Reply-To: <ghop5d$qud$1@ger.gmane.org>

On Mittwoch, 10. Dezember 2008, Tim Olsen wrote:
> It appears that when outputting a fatal error, git-show will choose
> stdout over stderr if stdout is a terminal and stderr is not.

This is by design.

> How do I 
> redirect the error but still allow stdout to be displayed?

$ git show 12345 2> /dev/null | less

> ~/git$ mkdir test
> ~/git$ cd test
> ~/git/test$ git init
> ~/git/test$ git show 12345
> fatal: ambiguous argument '12345': unknown revision or path not in the
> working tree.
> Use '--' to separate paths from revisions

You see this through the pager.

> ~/git/test$ git show 12345 2> /dev/null
> fatal: ambiguous argument '12345': unknown revision or path not in the
> working tree.
> Use '--' to separate paths from revisions

And this went through the pager as well.

> ~/git/test$ git show 12345 > /dev/null
> fatal: ambiguous argument '12345': unknown revision or path not in the
> working tree.
> Use '--' to separate paths from revisions

This went straight to the terminal.

The pattern is that if stdout is a terminal, the pager is thrown up and both 
stdout and stderr of git show proper are redirected to the pager. If you 
redirect only stderr, then this redirection is actually ignored.

-- Hannes

^ permalink raw reply

* Re: [PATCH 2/2] bash completion: Use 'git add' completions for 'git stage'
From: Shawn O. Pearce @ 2008-12-10 19:59 UTC (permalink / raw)
  To: Lee Marlow; +Cc: git
In-Reply-To: <1228937958-5091-2-git-send-email-lee.marlow@gmail.com>

Lee Marlow <lee.marlow@gmail.com> wrote:
> Signed-off-by: Lee Marlow <lee.marlow@gmail.com>
> ---
>  contrib/completion/git-completion.bash |    1 +
>  1 files changed, 1 insertions(+), 0 deletions(-)

Also,

Trivially-Acked-by: Shawn O. Pearce <spearce@spearce.org>
 
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index 5356e5b..7e2b482 100755
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -1660,6 +1660,7 @@ _git ()
>  	show)        _git_show ;;
>  	show-branch) _git_show_branch ;;
>  	stash)       _git_stash ;;
> +	stage)       _git_add ;;
>  	submodule)   _git_submodule ;;
>  	svn)         _git_svn ;;
>  	tag)         _git_tag ;;
> -- 
> 1.6.1.rc2.14.g5363d
> 
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Shawn.

^ permalink raw reply

* Re: [PATCH 2/3] gitweb: Cache $parent_commit info in git_blame()
From: Luben Tuikov @ 2008-12-10 20:05 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <200812101615.55340.jnareb@gmail.com>


--- On Wed, 12/10/08, Jakub Narebski <jnareb@gmail.com> wrote:
> > Have you tested this patch that it gives the same
> commit chain
> > as before it?
> 
> The only difference between precious version and this patch
> is that
> now, if you calculate sha-1 of $long_rev^, it is stored in 
> $metainfo{$long_rev}{'parent'} and not calculated
> second time.

Yes, I agree a patch to this effect would improve performance
proportionally to the history of the lines of a file.  So it's a
good thing, as most commits change a contiguous block of size more
than one line of a file.

"$parent_commit" depends on "$full_rev^" which depends on "$full_rev".
So as soon as "$full_rev" != "$old_full_rev", you'd know that you need
to update "$parent_commit".  "$old_full_rev" needs to exist outside the
scope of "while (1)".  I didn't see this in the code or in the patch.

> But I have checked that (at least for single example file)
> the blame output is identical for before and after this patch.

I've always tested it on something like "gitweb.perl", etc.

    Luben

^ permalink raw reply

* Re: builtin-add.c patch
From: Alexander Potashev @ 2008-12-10 20:10 UTC (permalink / raw)
  To: root; +Cc: git
In-Reply-To: <200812101914.mBAJEAS04718@localhost.localdomain>

Hello, Tim!

On 14:14 Wed 10 Dec     , root wrote:
> Alexander,
> 
> I saw a suggestion that git could be used as a filesystem rather
> than as a code repository. I'm looking to convert it for this
> purpose to sit underneath Axiom, a computer algebra system written
> in common lisp. Basically the idea is that a "close" operation does
> a 'git add foo ; git commit'. 
> 
> Are you aware of anyone who has used git as a filesystem?
> 
> Tim Daly
> 

It's a quite off-topic question. But Git is not optimized to track
individual files with separate history
( see http://www.youtube.com/watch?v=8dhZ9BXQgc4 ).
Also, Git uses only 644 and 755 permissions (755 stands for
executables, often scripts - shell scripts, perl, ...), but
usual filesystems provide full range of premissions/ownership.

                                 Alexander

^ permalink raw reply

* Re: fatal output from git-show really wants a terminal
From: Tim Olsen @ 2008-12-10 20:10 UTC (permalink / raw)
  To: git; +Cc: git
In-Reply-To: <200812102046.50186.j6t@kdbg.org>

Johannes Sixt wrote:
> The pattern is that if stdout is a terminal, the pager is thrown up and both 
> stdout and stderr of git show proper are redirected to the pager. If you 
> redirect only stderr, then this redirection is actually ignored.

I see.  So I probably want to use --no-pager.

Thanks for the help.

-Tim

> 
> -- Hannes

^ permalink raw reply

* [RFC/PATCH 4/3] gitweb: Incremental blame (proof of concept)
From: Jakub Narebski @ 2008-12-10 20:11 UTC (permalink / raw)
  To: git
  Cc: Luben Tuikov, Nanako Shiraishi, Petr Baudis, Fredrik Kuivinen,
	Fredrik Kuivinen, Petr Baudis, Jakub Narebski
In-Reply-To: <20081209223703.28106.29198.stgit@localhost.localdomain>

This is tweaked up version of Petr Baudis <pasky@suse.cz> patch, which
in turn was tweaked up version of Fredrik Kuivinen <frekui@gmail.com>'s
proof of concept patch.  It adds 'blame_incremental' view, which
incrementally displays line data in blame view using JavaScript (AJAX).

The original patch by Fredrik Kuivinen has been lightly tested in a
couple of browsers (Firefox, Mozilla, Konqueror, Galeon, Opera and IE6).
The next patch by Petr Baudis has been tested in Firefox and Epiphany.
This patch has been lightly tested in Mozilla 1.17.2 and Konqueror
3.5.3.

This patch does not (contrary to the one by Petr Baudis) enable this
view in gitweb: there are no links leading to 'blame_incremental'
action.  You would have to generate URL 'by hand' (e.g. changing 'blame'
or 'blob' in gitweb URL to 'blame_incremental').  Having links in gitweb
lead to this new action (e.g. by rewriting them like in previous patch),
if JavaScript is enabled in browser, is left for later.

Like earlier patch by Per Baudis it avoids code duplication, but it goes
one step further and use git_blame_common for ordinary blame view, for
incremental blame, and for incremental blame data.

How the 'blame_incremental' view works:
* gitweb generates initial info by putting file contents (from
  git-cat-file) together with line numbers in blame table
* then gitweb makes web browser JavaScript engine call startBlame()
  function from blame.js
* startBlame() opens connection to 'blame_data' view, which in turn
  calls "git blame --incremental" for a file, and streams output of
  git-blame to JavaScript (blame.js)
* blame.js updates line info in blame view, coloring it, and updating
  progress info; note that it has to use 3 colors to ensure that
  different neighbour groups have different styles
* when 'blame_data' ends, and blame.js finishes updating line info,
  it fixes colors to match (as far as possible) ordinary 'blame' view,
  and updates generating time info.

This code uses http://ajaxpatterns.org/HTTP_Streaming pattern.

It deals with streamed 'blame_data' server error by notifying about them
in the progress info area (just in case).

This patch adds GITWEB_BLAMEJS compile configuration option, and
modifies git-instaweb.sh to take blame.js into account, but it does not
update gitweb/README file (as it is only proof of concept patch).  The
code for git-instaweb.sh was taken from Pasky's patch.

While at it, this patch uniquifies td.dark and td.dark2 style: they
differ only in that td.dark2 doesn't have style for :hover.


This patch also adds showing time (in seconds) it took to generate
a page in page footer (based on example code by Pasky), even though
it is independent change, to be able to evaluate incremental blame in
gitweb better.  In proper patch series it would be independent commit;
and it probably would provide fallback if Time::HiRes is not available
(by e.g. not showing generating time info), even though this is
unlikely.

Cc: Fredrik Kuivinen <frekui@gmail.com>
Signed-off-by: Petr Baudis <pasky@suse.cz>
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
I'm sorry if you have received duplicate copy of this message...

NOTE: This patch is RFC proof of concept patch!: it should be split
onto many smaller patches for easy review (and bug finding) in version
meant to be applied.

[Please tell me if some of info below should be put in the commit
 message instead of here]

Patch by Petr Baudis this one is based on:
  http://permalink.gmane.org/gmane.comp.version-control.git/56657

Original patch by Fredrik Kuivinen:
  http://article.gmane.org/gmane.comp.version-control.git/41361

Snippet adding 'generated in' to gitweb, by Petr Baudis:
  http://article.gmane.org/gmane.comp.version-control.git/83306

Should I post interdiff to Petr Baudis patch, and comments about
difference between them? This post is quite long as it is now...


Differences between 'blame' and 'blame_incremental' output:
* Line number links in 'blame' lead to parent of blamed commit, i.e. to
  the view where given line _changed_; this behavior was introduced in
  244a70e (Blame "linenr" link jumps to previous state at "orig_lineno")
  by Luben Tuikov on Jan 2008 to make data mining possible.

  Currently 'blame_incremental' lead to version at blamed commit; this
  would be hard to change without opening another stream, or without
  having gitweb accept <sha1>^ for 'hb' (hash_base) parameter.

* The title attribute (shown in popup on mouseover) for "sha1" cell
  for 'blame_incremental' view looks like the following:
    'Kay Sievers, 2005-08-07 19:49:46 +0000 (21:49:46 +0200)'
  more like the date format used in 'commit' view, rather than shorter
    'Kay Sievers, 2005-08-07 21:49:46 +0200'

  This is a bit of accident, as in originla patch(es) there was error
  where the time and date shown were for UTC (GMT), and not for shown
  together timezone, i.e.
    'Kay Sievers, 2005-08-07 19:49:46 +0200'
  and I have added local time instead of replacing it. But perhaps it
  is 'blame' view that should change format? 

* In 'blame_incremental' the cell (<td>) with sha1 has rowspan
  attribute set even if it is at its default value rowspan="1".
  This should be very easy to fix.

* The 'blame_incremental' view has new feature inspired by output of
  "git gui blame <file>", that if group of lines blamed to the same
  commit has more than two lines, then below shortened sha-1 of a
  commit it is shown shortened author (initials, in uppercase).

  If you think it is worth it, this feature should be fairly easy to
  port to ordinary non-incremental 'blame' view.

* Sometimes "git blame --porcelain" and "git blame --incremental" can
  give different output.  Compare for example 'blame' and
  'blame_incremental' view for GIT-VERSION-GEN in git.git repository,
  and note that in 'blame_incremental' the last two groups are for the
  same commit (compare bottom parts of pages).  'blame_incremental'
  currently does not consolidate groups; if it did that, it should do
  it before fixColors()

* During filling blame info 'blame_incremental' view uses three colors
  (three styles: color1, color2, color3) instead of two color zebra
  coloring (two styles: light2, dark2) to ensure that the color of
  current group is different from both of its neighbours.

  This is non-issue: this is fixed at the end (although intermediate
  versions of blame.js script didn't fo fixColors() to make it easier
  to check if the 3-coloring is correct).

* The 'blame_incremental' view contains unique progress bar and
  progress report; perhaps they should vanish after succesfull loading
  of all data?


Bugs and suspected bugs in Mozilla 1.17.2 (my main browser); perhaps
those got corrected, as 1.17.2 is ancient (Gecko/20050923) version:
* HTML 4.01 Transitional specification at W3C states only ID and NAME
  tokens must begin with a letter ([A-Za-z]); it looks like class
  named "1" or "2" or "3" has style specified by CSS ignored.

* With XHTML 1.0 DTD and application/xhtml+xml content type, if there
  were <col /> elements in blame table (currently commented out in the
  source), then row with column headings (with <td> elements) was not
  visible.

* With XHTML 1.0 DTD and application/xhtml+xml content type, if there
  was an error in JavaScript, instead of having it visible as error
  message in JavaScript Console, Mozilla behaved as if the script
  wasn't there at all.

* With XHTML 1.0 DTD and application/xhtml+xml content type, setting
  innerHTML doesn't work: it raises cryptic JavaScript exception:

    Component returned failure code: 0x80004005 (NS_ERROR_FAILURE)
    [nsIDOMNSHTMLElement.innerHTML]	

  Correct solution would be to use only DOM, or rather depending on
  what web browser supports, use either .innerHTML (which is
  proprietary Microsoft extension) or DOM (which is standard but not
  all browser use it).  Current *workaround* is to simply always use
  'text/html' content type, and HTML 4.01 DTD.

  I wonder whether innerHTML or DOM is faster; and how many of web
  browser implements other similar properties like innerText,
  outerHTML and insertAdjacentHTML.

* XHTML 1.0 doesn't allow for trick with using HTML comments to hide
  contents of <script> element from old browsers, as XML compliant
  browser can remove XML comments before seeing JavaScript, so we
  don't use it.  Besides I think this issue is irrelevant now.


P.S. I have removed a few spurious one line change chunks from
gitweb/gitweb.perl, which were done to not confuse Perl syntax
highlighting (lazy-lock-mode) from cperl-mode in GNU Emacs 21.4.1,
and to not confuse imenu parser in GNU Emacs (which allow to go to
specified subroutine, and to display which-function-info in
modeline), so diffstat is slightly out of sync.

Those were #" or #' after regexp with single unescaped " or ',
and ($) in definition of "sub S_ISGITLINK($)".

P.P.S. What is the stance for copyrigth assesments in the files
for git code, like the ones in gitweb/gitweb.perl and gitweb/blame.js?

P.P.P.S. Should I use Signed-off-by from Pasky and Fredrik if I based
my code on theirs, and if they all signed their patches?

 Makefile           |    4 +
 git-instaweb.sh    |    6 +
 gitweb/blame.js    |  398 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 gitweb/gitweb.css  |   27 +++-
 gitweb/gitweb.perl |  252 ++++++++++++++++++++++-----------
 5 files changed, 597 insertions(+), 90 deletions(-)
 create mode 100644 gitweb/blame.js

diff --git a/Makefile b/Makefile
index 5158197..05ac246 100644
--- a/Makefile
+++ b/Makefile
@@ -218,6 +218,7 @@ GITWEB_HOMETEXT = indextext.html
 GITWEB_CSS = gitweb.css
 GITWEB_LOGO = git-logo.png
 GITWEB_FAVICON = git-favicon.png
+GITWEB_BLAMEJS = blame.js
 GITWEB_SITE_HEADER =
 GITWEB_SITE_FOOTER =
 
@@ -1209,6 +1210,7 @@ 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_BLAMEJS++|$(GITWEB_BLAMEJS)|g' \
 	    -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
 	    -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
 	    $< >$@+ && \
@@ -1224,6 +1226,8 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
 	    -e '/@@GITWEB_CGI@@/d' \
 	    -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
 	    -e '/@@GITWEB_CSS@@/d' \
+	    -e '/@@GITWEB_BLAMEJS@@/r gitweb/blame.js' \
+	    -e '/@@GITWEB_BLAMEJS@@/d' \
 	    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
 	    $@.sh > $@+ && \
 	chmod +x $@+ && \
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 0843372..f41354c 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -268,6 +268,12 @@ gitweb_css () {
 EOFGITWEB
 }
 
+gitweb_blamejs () {
+	cat > "$1" <<\EOFGITWEB
+@@GITWEB_BLAMEJS@@
+EOFGITWEB
+}
+
 gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
 gitweb_css "$GIT_DIR/gitweb/gitweb.css"
 
diff --git a/gitweb/blame.js b/gitweb/blame.js
new file mode 100644
index 0000000..197d615
--- /dev/null
+++ b/gitweb/blame.js
@@ -0,0 +1,398 @@
+// Copyright (C) 2007, Fredrik Kuivinen <frekui@gmail.com>
+
+var DEBUG = 0;
+function debug(str) {
+	if (DEBUG)
+		alert(str);
+}
+
+function createRequestObject() {
+	var ro;
+	if (window.XMLHttpRequest) {
+		ro = new XMLHttpRequest();
+	} else {
+		ro = new ActiveXObject("Microsoft.XMLHTTP");
+	}
+	if (!ro)
+		debug("Couldn't start XMLHttpRequest object");
+	return ro;
+}
+
+var http;
+var baseUrl;
+
+// 'commits' is an associative map. It maps SHA1s to Commit objects.
+var commits = new Object();
+
+function Commit(sha1) {
+	this.sha1 = sha1;
+}
+
+// convert month or day of the month to string, padding it with
+// '0' (zero) to two characters width if necessary
+function zeroPad(n) {
+	if (n < 10)
+		return '0' + n;
+	else
+		return n.toString();
+}
+
+function spacePad(n,width) {
+	var scale = 1;
+	var str = '';
+
+	while (width > 1) {
+		scale *= 10;
+		if (n < scale)
+			str += '&nbsp;';
+		width--;
+	}
+	return str + n.toString();
+}
+
+
+var blamedLines = 0;
+var totalLines  = '???';
+var div_progress_bar;
+var div_progress_info;
+
+function countLines() {
+	var table = document.getElementById('blame_table');
+	if (!table)
+		table = document.getElementsByTagName('table').item(0);
+
+	if (table)
+		return table.getElementsByTagName('tr').length - 1; // for header
+	else
+		return '...';
+}
+
+function updateProgressInfo() {
+	if (!div_progress_info)
+		div_progress_info = document.getElementById('progress_info');
+	if (!div_progress_bar)
+		div_progress_bar = document.getElementById('progress_bar');
+	if (!div_progress_info && !div_progress_bar)
+		return;
+
+	var percentage = Math.floor(100.0*blamedLines/totalLines);
+
+	if (div_progress_info) {
+		div_progress_info.innerHTML  = blamedLines + ' / ' + totalLines
+			+ ' ('+spacePad(percentage,3)+'%)';
+	}
+
+	if (div_progress_bar) {
+		div_progress_bar.setAttribute('style', 'width: '+percentage+'%;');
+	}
+}
+
+var colorRe = new RegExp('color([0-9]*)');
+/* return N if <tr class="colorN">, otherwise return null */
+function getColorNo(tr) {
+	var className = tr.getAttribute('class');
+	if (className) {
+		match = colorRe.exec(className);
+		if (match)
+			return parseInt(match[1]);
+	}
+	return null;
+}
+
+function findColorNo(tr_prev, tr_next) {
+	var color_prev;
+	var color_next;
+
+	if (tr_prev)
+		color_prev = getColorNo(tr_prev);
+	if (tr_next)
+		color_next = getColorNo(tr_next);
+
+	if (!color_prev && !color_next)
+		return 1;
+	if (color_prev == color_next)
+		return ((color_prev % 3) + 1);
+	if (!color_prev)
+		return ((color_next % 3) + 1);
+	if (!color_next)
+		return ((color_prev % 3) + 1);
+	return (3 - ((color_prev + color_next) % 3));
+}
+
+var tzRe = new RegExp('^([+-][0-9][0-9])([0-9][0-9])$');
+// called for each blame entry, as soon as it finishes
+function handleLine(commit) {
+	/* This is the structure of the HTML fragment we are working
+	   with:
+	   
+	   <tr id="l123" class="">
+	     <td class="sha1" title=""><a href=""></a></td>
+	     <td class="linenr"><a class="linenr" href="">123</a></td>
+	     <td class="pre"># times (my ext3 doesn&#39;t).</td>
+	   </tr>
+	*/
+
+	var resline = commit.resline;
+
+	if (!commit.info) {
+		var date = new Date();
+		date.setTime(commit.authorTime * 1000); // milliseconds
+		var dateStr =
+			date.getUTCFullYear() + '-'
+			+ zeroPad(date.getUTCMonth()+1) + '-'
+			+ zeroPad(date.getUTCDate());
+		var timeStr =
+			zeroPad(date.getUTCHours()) + ':'
+			+ zeroPad(date.getUTCMinutes()) + ':'
+			+ zeroPad(date.getUTCSeconds());
+
+		var localDate = new Date();
+		var match = tzRe.exec(commit.authorTimezone);
+		localDate.setTime(1000 * (commit.authorTime
+			+ (parseInt(match[1],10)*3600 + parseInt(match[2],10)*60)));
+		var localTimeStr =
+			zeroPad(localDate.getUTCHours()) + ':'
+			+ zeroPad(localDate.getUTCMinutes()) + ':'
+			+ zeroPad(localDate.getUTCSeconds());
+
+		/* e.g. 'Kay Sievers, 2005-08-07 19:49:46 +0000 (21:49:46 +0200)' */
+		commit.info = commit.author + ', ' + dateStr + ' '
+			+ timeStr + ' +0000'
+			+ ' (' + localTimeStr + ' ' + commit.authorTimezone + ')';
+	}
+
+	var color = findColorNo(
+		document.getElementById('l'+(resline-1)),
+		document.getElementById('l'+(resline+commit.numlines))
+	);
+
+
+	for (var i = 0; i < commit.numlines; i++) {
+		var tr = document.getElementById('l'+resline);
+		if (!tr) {
+			debug('tr is null! resline: ' + resline);
+			break;
+		}
+		/*
+			<tr id="l123" class="">
+			  <td class="sha1" title=""><a href=""></a></td>
+			  <td class="linenr"><a class="linenr" href="">123</a></td>
+			  <td class="pre"># times (my ext3 doesn&#39;t).</td>
+			</tr>
+		*/
+		var td_sha1  = tr.firstChild;
+		var a_sha1   = td_sha1.firstChild;
+		var a_linenr = td_sha1.nextSibling.firstChild;
+
+		/* <tr id="l123" class=""> */
+		if (color) {
+			tr.setAttribute('class', 'color'+color.toString());
+			// Internet Explorer needs this
+			//tr.setAttribute('className', color.toString);
+		}
+		/* <td class="sha1" title="?" rowspan="?"><a href="?">?</a></td> */
+		if (i == 0) {
+			td_sha1.title = commit.info;
+			td_sha1.setAttribute('rowspan', commit.numlines);
+
+			a_sha1.href = baseUrl + '?a=commit;h=' + commit.sha1;
+			a_sha1.innerHTML = commit.sha1.substr(0, 8);
+			if (commit.numlines >= 2) {
+				var br   = document.createElement("br");
+				var text = document.createTextNode(commit.author.match(/\b([A-Z])\B/g).join(''));
+				if (br && text) {
+					td_sha1.appendChild(br);
+					td_sha1.appendChild(text);
+				}
+			}
+		} else {
+			tr.removeChild(td_sha1);
+		}
+
+		/* <td class="linenr"><a class="linenr" href="?">123</a></td> */
+		a_linenr.href = baseUrl + '?a=blame;hb=' + commit.sha1
+			+ ';f=' + commit.filename + '#l' + (commit.srcline + i);
+
+		resline++;
+		blamedLines++;
+
+		//updateProgressInfo();
+	}
+}
+
+function startOfGroup(tr) {
+	return tr.firstChild.getAttribute('class') == 'sha1';
+}
+
+function fixColors() {
+	var colorClasses = ['light2', 'dark2'];
+	var linenum = 1;
+	var tr;
+	var colorClass = 0;
+
+	while ((tr = document.getElementById('l'+linenum))) {
+		if (startOfGroup(tr, linenum, document)) {
+			colorClass = (colorClass + 1) % 2;
+		}
+		tr.setAttribute('class', colorClasses[colorClass]);
+		// Internet Explorer needs this
+		tr.setAttribute('className', colorClasses[colorClass]);
+		linenum++;
+	}
+}
+
+var t_interval_server = '';
+var t0 = new Date();
+
+function writeTimeInterval() {
+	var info = document.getElementById('generate_time');
+	if (!info)
+		return;
+	var t1 = new Date();
+
+	info.innerHTML += ' + '
+		+ t_interval_server+'s server (blame_data) / '
+		+ (t1.getTime() - t0.getTime())/1000 + 's client (JavaScript)';
+}
+
+// ----------------------------------------------------------------------
+
+var prevDataLength = -1;
+var nextLine = 0;
+var inProgress = false;
+
+var sha1Re = new RegExp('([0-9a-f]{40}) ([0-9]+) ([0-9]+) ([0-9]+)');
+var infoRe = new RegExp('([a-z-]+) ?(.*)');
+var endRe = new RegExp('END ?(.*)');
+var curCommit = new Commit();
+
+var pollTimer = null;
+
+function handleResponse() {
+	debug('handleResp ready: ' + http.readyState
+	      + ' respText null?: ' + (http.responseText === null)
+	      + ' progress: ' + inProgress);
+
+	if (http.readyState != 4 && http.readyState != 3)
+		return;
+
+	// the server stream is incorrect
+	if (http.readyState == 3 && http.status != 200)
+		return;
+	if (http.readyState == 4 && http.status != 200) {
+		if (!div_progress_info)
+			div_progress_info = document.getElementById('progress_info');
+
+		if (div_progress_info) {
+			div_progress_info.setAttribute('class', 'error');
+			div_progress_info.innerHTML =
+				http.status+' - Error contacting server\n';
+		} else {
+			document.write("<b>ERROR:</b> HTTP status is "+http.status+"<br />\n");
+		}
+
+		clearInterval(pollTimer);
+		inProgress = false;
+	}
+
+	// In konqueror http.responseText is sometimes null here...
+	if (http.responseText === null)
+		return;
+
+	/*
+	var resp = document.getElementById('state');
+	if (resp) {
+		resp.innerHTML = http.readyState + ' : ' + http.status
+			+ '<br />len = ' + http.responseText.length
+			+ '; inProgress='+inProgress;
+		//inProgress = true;
+	}
+	*/
+
+	if (inProgress)
+		return;
+	else
+		inProgress = true;
+
+	while (prevDataLength != http.responseText.length) {
+		if (http.readyState == 4
+		    && prevDataLength == http.responseText.length) {
+			break;
+		}
+
+		prevDataLength = http.responseText.length;
+		var response = http.responseText.substring(nextLine);
+		var lines = response.split('\n');
+		nextLine = nextLine + response.lastIndexOf('\n') + 1;
+		if (response[response.length-1] != '\n') {
+			lines.pop();
+		}
+
+		for (var i = 0; i < lines.length; i++) {
+			var match = sha1Re.exec(lines[i]);
+			if (match) {
+				var sha1 = match[1];
+				var srcline = parseInt(match[2]);
+				var resline = parseInt(match[3]);
+				var numlines = parseInt(match[4]);
+				var c = commits[sha1];
+				if (!c) {
+					c = new Commit(sha1);
+					commits[sha1] = c;
+				}
+
+				c.srcline = srcline;
+				c.resline = resline;
+				c.numlines = numlines;
+				curCommit = c;
+			} else if ((match = infoRe.exec(lines[i]))) {
+				var info = match[1];
+				var data = match[2];
+				if (info == 'filename') {
+					curCommit.filename = data;
+					handleLine(curCommit);
+					updateProgressInfo();
+				} else if (info == 'author') {
+					curCommit.author = data;
+				} else if (info == 'author-time') {
+					curCommit.authorTime = parseInt(data);
+				} else if (info == 'author-tz') {
+					curCommit.authorTimezone = data;
+				}
+			} else if ((match = endRe.exec(lines[i]))) {
+				t_interval_server = match[1];
+				debug('END: '+lines[i]);
+			} else if (lines[i] != '') {
+				debug('malformed line: ' + lines[i]);
+			}
+		}
+	}
+
+	if (http.readyState == 4 &&
+	    prevDataLength == http.responseText.length) {
+		clearInterval(pollTimer);
+
+		fixColors();
+		writeTimeInterval();
+	}
+
+	inProgress = false;
+}
+
+function startBlame(blamedataUrl, bUrl) {
+	debug('startBlame('+blamedataUrl+', '+bUrl+')');
+
+	t0 = new Date();
+	baseUrl = bUrl;
+	totalLines = countLines();
+	updateProgressInfo();
+
+	http = createRequestObject();
+	http.open('get', blamedataUrl);
+	http.onreadystatechange = handleResponse;
+	http.send(null);
+
+	pollTimer = setInterval(handleResponse, 1000);
+}
+
+// end of blame.js
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index a01eac8..e359618 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -223,11 +223,7 @@ tr.light:hover {
 	background-color: #edece6;
 }
 
-tr.dark {
-	background-color: #f6f6f0;
-}
-
-tr.dark2 {
+tr.dark, tr.dark2 {
 	background-color: #f6f6f0;
 }
 
@@ -235,6 +231,14 @@ tr.dark:hover {
 	background-color: #edece6;
 }
 
+tr.color1:hover { background-color: #e6ede6; }
+tr.color2:hover { background-color: #e6e6ed; }
+tr.color3:hover { background-color: #ede6e6; }
+
+tr.color1 { background-color: #f6fff6; }
+tr.color2 { background-color: #f6f6ff; }
+tr.color3 { background-color: #fff6f6; }
+
 td {
 	padding: 2px 5px;
 	font-size: 100%;
@@ -255,7 +259,7 @@ td.sha1 {
 	font-family: monospace;
 }
 
-td.error {
+.error {
 	color: red;
 	background-color: yellow;
 }
@@ -326,6 +330,17 @@ td.mode {
 	font-family: monospace;
 }
 
+/* progress of blame_interactive */
+div#progress_bar {
+	height: 2px;
+	margin-bottom: -2px;
+	background-color: #d8d9d0;
+}
+div#progress_info {
+	float: right;
+	text-align: right;
+}
+
 /* styling of diffs (patchsets): commitdiff and blobdiff views */
 div.diff.header,
 div.diff.extended_header {
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 4987fdc..774bcc6 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -18,6 +18,9 @@ use File::Find qw();
 use File::Basename qw(basename);
 binmode STDOUT, ':utf8';
 
+use Time::HiRes qw(gettimeofday tv_interval);
+our $t0 = [gettimeofday];
+
 BEGIN {
 	CGI->compile() if $ENV{'MOD_PERL'};
 }
@@ -74,6 +77,8 @@ our $stylesheet = undef;
 our $logo = "++GITWEB_LOGO++";
 # URI of GIT favicon, assumed to be image/png type
 our $favicon = "++GITWEB_FAVICON++";
+# URI of blame.js
+our $blamejs = "++GITWEB_BLAMEJS++";
 
 # URI and label (title) of GIT logo link
 #our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
@@ -493,6 +498,8 @@ our %cgi_param_mapping = @cgi_param_mapping;
 # we will also need to know the possible actions, for validation
 our %actions = (
 	"blame" => \&git_blame,
+	"blame_incremental" => \&git_blame_incremental,
+	"blame_data" => \&git_blame_data,
 	"blobdiff" => \&git_blobdiff,
 	"blobdiff_plain" => \&git_blobdiff_plain,
 	"blob" => \&git_blob,
@@ -2874,13 +2881,13 @@ sub git_header_html {
 	# 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
 	# we have to do this because MSIE sometimes globs '*/*', pretending to
 	# support xhtml+xml but choking when it gets what it asked for.
-	if (defined $cgi->http('HTTP_ACCEPT') &&
-	    $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ &&
-	    $cgi->Accept('application/xhtml+xml') != 0) {
-		$content_type = 'application/xhtml+xml';
-	} else {
+	#if (defined $cgi->http('HTTP_ACCEPT') &&
+	#    $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ &&
+	#    $cgi->Accept('application/xhtml+xml') != 0) {
+	#	$content_type = 'application/xhtml+xml';
+	#} else {
 		$content_type = 'text/html';
-	}
+	#}
 	print $cgi->header(-type=>$content_type, -charset => 'utf-8',
 	                   -status=> $status, -expires => $expires);
 	my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
@@ -3042,6 +3049,14 @@ sub git_footer_html {
 	}
 	print "</div>\n"; # class="page_footer"
 
+	print "<div class=\"page_footer\">\n";
+	print 'This page took '.
+	      '<span id="generate_time" class="time_span">'.
+	      tv_interval($t0, [gettimeofday]).'s'.
+	      '</span>'.
+	      " to generate.\n";
+	print "</div>\n"; # class="page_footer"
+
 	if (-f $site_footer) {
 		insert_file($site_footer);
 	}
@@ -4574,7 +4589,9 @@ sub git_tag {
 	git_footer_html();
 }
 
-sub git_blame {
+sub git_blame_common {
+	my $format = shift || 'porcelain';
+
 	# permissions
 	gitweb_check_feature('blame')
 		or die_error(403, "Blame view not allowed");
@@ -4596,10 +4613,36 @@ sub git_blame {
 		}
 	}
 
-	# run git-blame --porcelain
-	open my $fd, "-|", git_cmd(), "blame", '-p',
-		$hash_base, '--', $file_name
-		or die_error(500, "Open git-blame failed");
+	my $fd;
+	if ($format eq 'incremental') {
+		# get file contents (as base)
+		open $fd, "-|", git_cmd(), 'cat-file', 'blob', $hash
+			or die_error(500, "Open git-cat-file failed");
+	} elsif ($format eq 'data') {
+		# run git-blame --incremental
+		open $fd, "-|", git_cmd(), "blame", "--incremental",
+			$hash_base, "--", $file_name
+			or die_error(500, "Open git-blame --incremental failed");
+	} else {
+		# run git-blame --porcelain
+		open $fd, "-|", git_cmd(), "blame", '-p',
+			$hash_base, '--', $file_name
+			or die_error(500, "Open git-blame --porcelain failed");
+	}
+
+	# incremental blame data returns early
+	if ($format eq 'data') {
+		print $cgi->header(
+			-type=>"text/plain", -charset => "utf-8",
+			-status=> "200 OK");
+		local $| = 1; # output autoflush
+		print while <$fd>;
+		close $fd
+			or print "ERROR $!\n";
+		print "END ".tv_interval($t0, [gettimeofday])."\n";
+
+		return;
+	}
 
 	# page header
 	git_header_html();
@@ -4610,93 +4653,134 @@ sub git_blame {
 		$cgi->a({-href => href(action=>"history", -replay=>1)},
 		        "history") .
 		" | " .
-		$cgi->a({-href => href(action=>"blame", file_name=>$file_name)},
+		$cgi->a({-href => href(action=>$action, file_name=>$file_name)},
 		        "HEAD");
 	git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
 	git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
 	git_print_page_path($file_name, $ftype, $hash_base);
 
 	# page body
+	print qq!<div id="progress_bar" style="width: 100%; background-color: yellow"></div>\n!
+		if ($format eq 'incremental');
+	print qq!<div class="page_body">\n!;
+	print qq!<div id="progress_info">... / ...</div>\n!
+		if ($format eq 'incremental');
+	print qq!<table id="blame_table" class="blame" width="100%">\n!.
+	      #qq!<col width="5.5em" /><col width="2.5em" /><col width="*" />\n!.
+	      qq!<tr><th>Commit</th><th>Line</th><th>Data</th></tr>\n!;
+
 	my @rev_color = qw(light2 dark2);
 	my $num_colors = scalar(@rev_color);
 	my $current_color = 0;
-	my %metainfo = ();
 
-	print <<HTML;
-<div class="page_body">
-<table class="blame">
-<tr><th>Commit</th><th>Line</th><th>Data</th></tr>
-HTML
- LINE:
-	while (my $line = <$fd>) {
-		chomp $line;
-		# the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
-		# no <lines in group> for subsequent lines in group of lines
-		my ($full_rev, $orig_lineno, $lineno, $group_size) =
-		   ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
-		if (!exists $metainfo{$full_rev}) {
-			$metainfo{$full_rev} = {};
-		}
-		my $meta = $metainfo{$full_rev};
-		my $data; # last line is used later
-		while ($data = <$fd>) {
-			chomp $data;
-			last if ($data =~ s/^\t//); # contents of line
-			if ($data =~ /^(\S+) (.*)$/) {
-				$meta->{$1} = $2;
-			}
-		}
-		my $short_rev = substr($full_rev, 0, 8);
-		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 + 1) % $num_colors;
-		}
-		print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n";
-		if ($group_size) {
-			print "<td class=\"sha1\"";
-			print " title=\"". esc_html($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($short_rev));
-			print "</td>\n";
+	if ($format eq 'incremental') {
+		my $color_class = $rev_color[$current_color];
+
+		#contents of a file
+		my $linenr = 0;
+	LINE:
+		while (my $line = <$fd>) {
+			chomp $line;
+			$linenr++;
+
+			print qq!<tr id="l$linenr" class="$color_class">!.
+			      qq!<td class="sha1"><a href=""></a></td>!.
+			      qq!<td class="linenr">!.
+			      qq!<a class="linenr" href="">$linenr</a></td>!;
+			print qq!<td class="pre">! . esc_html($line) . "</td>\n";
+			print qq!</tr>\n!;
 		}
-		my $parent_commit;
-		if (!exists $meta->{'parent'}) {
-			open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
-				or die_error(500, "Open git-rev-parse failed");
-			$parent_commit = <$dd>;
-			close $dd;
-			chomp($parent_commit);
-			$meta->{'parent'} = $parent_commit;
-		} else {
-			$parent_commit = $meta->{'parent'};
-		}
-		my $blamed = href(action => 'blame',
-		                  file_name => $meta->{'filename'},
-		                  hash_base => $parent_commit);
-		print "<td class=\"linenr\">";
-		print $cgi->a({ -href => "$blamed#l$orig_lineno",
-		                -class => "linenr" },
-		              esc_html($lineno));
-		print "</td>";
-		print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
-		print "</tr>\n";
-	}
-	print "</table>\n";
-	print "</div>";
+
+	} else { # porcelain, i.e. ordinary blame
+		my %metainfo = (); # saves information about commits
+
+		# blame data
+	LINE:
+		while (my $line = <$fd>) {
+			chomp $line;
+			# the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
+			# no <lines in group> for subsequent lines in group of lines
+			my ($full_rev, $orig_lineno, $lineno, $group_size) =
+				($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
+			$metainfo{$full_rev} ||= {};
+			my $meta = $metainfo{$full_rev};
+			my $data; # last line is used later
+			while ($data = <$fd>) {
+				chomp $data;
+				last if ($data =~ s/^\t//); # contents of line
+				if ($data =~ /^(\S+) (.*)$/) {
+					$meta->{$1} = $2;
+				}
+			}
+			my $short_rev = substr($full_rev, 0, 8);
+			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 + 1) % $num_colors;
+			}
+			print qq!<tr id="l$lineno" class="$rev_color[$current_color]">\n!;
+			if ($group_size) {
+				print qq!<td class="sha1"!.
+				      qq! title="!. esc_html($author) . qq!, $date"!;
+				print qq! rowspan="$group_size"! if ($group_size > 1);
+				print qq!>!;
+				print $cgi->a({-href => href(action=>"commit",
+				                             hash=>$full_rev,
+				                             file_name=>$file_name)},
+				              esc_html($short_rev));
+				print qq!</td>\n!;
+			}
+			if (!exists $meta->{'parent'}) {
+				open my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^"
+					or die_error(500, "Open git-rev-parse failed");
+				$meta->{'parent'} = <$dd>;
+				close $dd;
+				chomp $meta->{'parent'};
+			}
+			my $blamed = href(action => 'blame',
+			                  file_name => $meta->{'filename'},
+			                  hash_base => $meta->{'parent'});
+			print qq!<td class="linenr">!.
+			       $cgi->a({ -href => "$blamed#l$orig_lineno",
+			                -class => "linenr" },
+			               esc_html($lineno)).
+			      qq!</td>!;
+			print qq!<td class="pre">! . esc_html($data) . "</td>\n";
+			print qq!</tr>\n!;
+		}
+	}
+
+	# footer
+	print "</table>\n"; # class="blame"
+	print "</div>\n";   # class="blame_body"
 	close $fd
 		or print "Reading blob failed\n";
 
-	# page footer
+	if ($format eq 'incremental') {
+		print qq!<script type="text/javascript" src="$blamejs"></script>\n!.
+		      qq!<script type="text/javascript">\n!.
+		      qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
+		      qq!           "$my_url");\n!.
+		      qq!</script>\n!;
+	}
+
 	git_footer_html();
 }
 
+sub git_blame {
+	git_blame_common();
+}
+
+sub git_blame_incremental {
+	git_blame_common('incremental');
+}
+
+sub git_blame_data {
+	git_blame_common('data');
+}
+
 sub git_tags {
 	my $head = git_get_head_hash($project);
 	git_header_html();

-- 
Stacked GIT 0.14.3
git version 1.6.0.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox