Git development
 help / color / mirror / Atom feed
* Re: impure renames / history tracking
From: linux @ 2006-03-02 22:24 UTC (permalink / raw)
  To: git, paul

>> Yes, but imo a poor one, as you're losing all the history.
>
> Well, not per se. You might keep the original 'detail' branch. It's a 
> terminal branch obviously, you can't pull master's changes to it once 
> the aggregate patch goes into master. But you can keep it around.

Actually, you can!  That's what the "ours" merge stratgy is for!
It creates a merge whose result is a verbatim copy of the first parent.

The intended use is for when you've cherry-picked or otherwise manually
merged everything interesting from a branch and want to tie up the loose
end so you can delete the branch name.

^ permalink raw reply

* Re: fatal: unexpected EOF
From: Alex Riesen @ 2006-03-02 22:23 UTC (permalink / raw)
  To: Tony Luck; +Cc: Brian Gerst, Linus Torvalds, Git Mailing List, Junio C Hamano
In-Reply-To: <12c511ca0602280759t2e584a4bkd7b6f4d97ade92f7@mail.gmail.com>

Tony Luck, Tue, Feb 28, 2006 16:59:09 +0100:
> > I doubt it is a problem with mirroring, since it affects all repos
> > (kernel, git, cogito, etc.) at the same time.
> 
> Ditto.  Jes has been grumbling overnight that he can't get a reliable pull
> from my kernel repo ... and that hasn't been updated in 10 days, so the
> mirror code shouldn't be touching it.  His error was:
> 
>   fatal: read error (Connection reset by peer)
>   Fetch failure: git://git.kernel.org/pub/...
> 
> He also reported that after a few retries it worked.

I had the problems too and even made the patch (below) to see what it
was. I saw to A-records for zeus-pub.kernel.org (git.kernel.org is an
alias of it) where one of them (I believe it was 204.152.191.37)
sometimes didn't answer or dropped connection.

---

 connect.c |   41 +++++++++++++++++++++++++++++++++++++++--
 1 files changed, 39 insertions(+), 2 deletions(-)

b5ceb5f3f1c6ff62a3ccb13f360a34b07b9c8482
diff --git a/connect.c b/connect.c
index 3f2d65c..e911fde 100644
--- a/connect.c
+++ b/connect.c
@@ -322,6 +322,23 @@ static enum protocol get_protocol(const 
 
 #ifndef NO_IPV6
 
+static const char *ai_name(const struct addrinfo *ai)
+{
+	static char addr[INET_ADDRSTRLEN];
+	if ( AF_INET == ai->ai_family ) {
+		struct sockaddr_in *in;
+		in = (struct sockaddr_in *)ai->ai_addr;
+		inet_ntop(ai->ai_family, &in->sin_addr, addr, sizeof(addr));
+	} else if ( AF_INET6 == ai->ai_family ) {
+		struct sockaddr_in6 *in;
+		in = (struct sockaddr_in6 *)ai->ai_addr;
+		inet_ntop(ai->ai_family, &in->sin6_addr, addr, sizeof(addr));
+	} else {
+		strcpy(addr, "(unknown)");
+	}
+	return addr;
+}
+
 static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
 {
 	int sockfd = -1;
@@ -329,6 +346,7 @@ static int git_tcp_connect(int fd[2], co
 	char *port = STR(DEFAULT_GIT_PORT);
 	struct addrinfo hints, *ai0, *ai;
 	int gai;
+	int cnt = 0;
 
 	if (host[0] == '[') {
 		end = strchr(host + 1, ']');
@@ -355,15 +373,23 @@ static int git_tcp_connect(int fd[2], co
 	if (gai)
 		die("Unable to look up %s (%s)", host, gai_strerror(gai));
 
-	for (ai0 = ai; ai; ai = ai->ai_next) {
+	for (ai0 = ai; ai; ++cnt, ai = ai->ai_next) {
 		sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 		if (sockfd < 0)
 			continue;
 		if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
+			fprintf(stderr, "%s: %s[%d: %s]: net=%s, errno=%s\n",
+				argv0,
+				host,
+				cnt,
+				ai_name(ai),
+				hstrerror(h_errno),
+				strerror(errno));
 			close(sockfd);
 			sockfd = -1;
 			continue;
 		}
+		fprintf(stderr, "using %s[%s]\n", host, ai_name(ai));
 		break;
 	}
 
@@ -389,6 +415,7 @@ static int git_tcp_connect(int fd[2], co
 	struct sockaddr_in sa;
 	char **ap;
 	unsigned int nport;
+	int cnt;
 
 	if (host[0] == '[') {
 		end = strchr(host + 1, ']');
@@ -420,7 +447,7 @@ static int git_tcp_connect(int fd[2], co
 		nport = se->s_port;
 	}
 
-	for (ap = he->h_addr_list; *ap; ap++) {
+	for (cnt = 0, ap = he->h_addr_list; *ap; ap++, cnt++) {
 		sockfd = socket(he->h_addrtype, SOCK_STREAM, 0);
 		if (sockfd < 0)
 			continue;
@@ -431,10 +458,20 @@ static int git_tcp_connect(int fd[2], co
 		memcpy(&sa.sin_addr, *ap, he->h_length);
 
 		if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) {
+			fprintf(stderr, "%s: %s[%d: %s]: net=%s, errno=%s\n",
+				argv0,
+				host,
+				cnt,
+				inet_ntoa(*(struct in_addr *)&sa.sin_addr),
+				hstrerror(h_errno),
+				strerror(errno));
 			close(sockfd);
 			sockfd = -1;
 			continue;
 		}
+		fprintf(stderr, "using %s[%s]\n",
+			host,
+			inet_ntoa(*(struct in_addr *)&sa.sin_addr));
 		break;
 	}
 
-- 
1.2.3.g3987

^ permalink raw reply related

* Re: [PATCH] fmt-merge-msg: avoid open "-|" list form for Perl 5.6
From: Alex Riesen @ 2006-03-02 22:09 UTC (permalink / raw)
  To: Shawn Pearce; +Cc: git
In-Reply-To: <20060302165510.GB18929@spearce.org>

Shawn Pearce, Thu, Mar 02, 2006 17:55:10 +0100:
> Maybe I missed this but why are people using the native Windows
> ActiveState Perl with GIT+Cygwin when Cygwin has a Cygwin-ized Perl
> installation available?

because the people _can't_ use cygwin's perl. There are a lot of
reasons mainly: administrative, perl script incompatibilities and
cygwin.dll incompatibilities (if you use perl from cygwin, it'll need
the correct cygwin.dll. And if a build process uses cygwin tools from,
for example, QNX Momentics it often comes to clashes).

> I've been using the Cygwin Perl with GIT without any problems
> whatsoever.  Including the open(I, "-|")... exec(@argv) code that
> doesn't work correctly in ActiveState and started this whole thread.

Unfortunately...

^ permalink raw reply

* Re: impure renames / history tracking
From: Andreas Ericsson @ 2006-03-02 22:06 UTC (permalink / raw)
  To: Paul Jakma; +Cc: git list
In-Reply-To: <Pine.LNX.4.64.0603012129310.13612@sheen.jakma.org>

Paul Jakma wrote:
> On Wed, 1 Mar 2006, Andreas Ericsson wrote:
> 
>> It's completely impossible to fold *ALL* the history into a single 
>> commit, and since you want heuristics I would imagine you wouldn't 
>> want that either.
> 
> 
> I want to know whether additional meta-data to help the existing 
> heuristics would be acceptable. From a discussion on #git yesterday I 
> gather the best way forward would to be to first prototype something 
> keeping state in a file in .git.
> 
> All that's needed really is something that relates the following 3 things:
> 
>     commit-id obj1-id obj2-id
> 
> Ie: For <commit-id>, <obj1-id> is similar to <obj2-id>.
> 
> Maintaining this state could be done via the git-mv/rename wrappers and 
> an additional git-edit wrapper. Those who are quite happy with the 
> existing diff-input only similarity heuristics wouldn't have to bother 
> using a git-edit wrapper obviously, those who want to let git gather 
> additional 'similarity hint' in this way could.
> 
> Aside:
> 
> Git might be easier to extend generally if it adopted just /one/ new 
> core header, say "see-also" - that could serve as a pointer to arbitrary 
> commit-related meta-info objects that aren't of immediate interest to 
> either:
> 
> a) core git
> 
> or
> 
> b) the user
> 

Things that aren't of interest to either core git or the user is already 
handled properly. It's called "cruft". ;)

However, I see what you're trying for here. Something like the X-* 
headers inside a mailer. Not all MUA's understand them, but if they do 
they can make use of them to the users benefit.


> Format:
> 
>     see-also <word> <obj-id>
> 
> E.g.:
> 
>     see-also similars <obj-id>
> 
> Where <obj-id> would list the 'commit obj1 obj2', but just as:
> 
>     obj1 obj2
> 
> Would ultimately be neater than fishing around in .git/, and would allow 
> other extensions in the future too.
> 
> The <word> identifier preferably would need to be centrally co-ordinated.
> 

With X-* headers I don't see why it should have to be. Only the X-* part 
is mentioned in the RFC, so with a proper format Junio won't have to 
coordinate cross-SCM tools, git-tortoise, etc, etc...


>> I'm confused. First you say you want to have one single mega-patch for 
>> each commit, then you say you want to be able to follow history back. 
>> It's like deciding to throw away your wallet and then trying to get 
>> someone to pick it up and carry it around for you.
> 
> 
> I'm not sure why think mega-patch. Collapsing a bunch of commits related 
> to one project need not result in a big patch relative to the repository 
> as a whole.
> 

Mainly I think it's because you mentioned several renames of a single 
file and many files renamed + rewritten (beyond gits current ability of 
recognizing it). That's definitely a mega-patch in my book.


> Where the project concerned is like BSD, not 
> just a kernel but a complete userland (so 1.1GB of source code).
> 

<just curious>
Such a large project surely must be split in several smaller 
sub-projects? GNU is, after all, several small (and not so small) 
components. X works the same way. Linux is a large project, but each 
compartment of code can be managed on its own, so long as they adhere to 
the ABI hooking them back in to the kernel core.
</just curious>

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ permalink raw reply

* Re: [PATCH] fmt-merge-msg: avoid open "-|" list form for Perl 5.6
From: Alex Riesen @ 2006-03-02 22:01 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: Christopher Faylor, git
In-Reply-To: <44072DEF.1070906@op5.se>

Andreas Ericsson, Thu, Mar 02, 2006 18:39:59 +0100:
> Ye gawds, Alex. If you complained this much to your employer you'd get 
> to run whatever OS you want.

I never stopped. I usually manage to convince them, it just hasn't
happened here yet.

^ permalink raw reply

* Re: cygwin: push/pull takes very long time
From: Alex Riesen @ 2006-03-02 21:54 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <81b0412b0603020909p179ed9bx4ed8fc2ddf77e868@mail.gmail.com>

Alex Riesen, Thu, Mar 02, 2006 18:09:23 +0100:
> I'll cleanup the profiling code and send it as well soon
> (I had to instrument x*alloc).

This is not exactly the same. It counts free as well, even if that is
not really interesting - there are places were there is more frees
than allocs. Probably something missed or a result coming from libc.

Also it is _not_ the code I used for windows. I had to have a global
variable for argv[0], which needs modification of all main()s, which
gets too easily out of sync.

BTW, maybe someone has an idea as to how attach valgrind to everything?
(I mean, without changing every script. Maybe modify git.c?)

Anyway, here it is, could be useful for something.

---

diff --git a/Makefile b/Makefile
index a5eb0c4..18fc802 100644
--- a/Makefile
+++ b/Makefile
@@ -206,7 +206,7 @@ LIB_OBJS = \
 	quote.o read-cache.o refs.o run-command.o \
 	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
 	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
-	fetch-clone.o \
+	fetch-clone.o alloc.o \
 	$(DIFF_OBJS)
 
 LIBS = $(LIB_FILE)
diff --git a/alloc.c b/alloc.c
new file mode 100644
index 0000000..76727e6
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,108 @@
+/* simple allocation logging */
+#include <unistd.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "git-compat-util.h"
+
+#undef free
+
+#define SHIFT 2
+static unsigned cnts[12] = {0,0,0,0, 0,0,0,0};
+static unsigned fcnt = 0;
+
+static int exit_hook = 1;
+
+static void alloc_profile()
+{
+	char argv0[PATH_MAX];
+	FILE *fp;
+	fp = fopen("/proc/self/cmdline", "r");
+	if ( !fp ) {
+		sprintf(argv0, "%d", getpid());
+	} else {
+		fread(argv0, sizeof(argv0), 1, fp);
+		fclose(fp);
+		char *s = strrchr(argv0, '/');
+		if ( s )
+			memmove(argv0, s + 1, strlen(s));
+	}
+	fp = fopen("/tmp/git-alloc", "ab");
+	if ( !fp )
+		return;
+	unsigned i, c = 0;
+	for ( i = 0; i < sizeof(cnts)/sizeof(*cnts); ++i ) {
+		if ( !cnts[i] )
+			continue;
+		fprintf(fp, "%s %u %u:%u %u times\n",
+			argv0,
+			i,
+			i ? 1 << i * SHIFT: 0,
+			1 << (i+1) * SHIFT,
+			cnts[i]);
+		c += cnts[i];
+	}
+	fprintf(fp, "%s alloc-free = %u\n", argv0, c - fcnt);
+	fclose(fp);
+}
+
+static inline void count(size_t size)
+{
+	if ( exit_hook ) {
+		exit_hook = 0;
+		atexit(alloc_profile);
+	}
+        unsigned i = 0;
+	while ( size && i < sizeof(cnts)/sizeof(*cnts) ) {
+		size >>= SHIFT;
+		if ( size )
+			++i;
+		else {
+			++cnts[i];
+			return;
+		}
+	}
+	++cnts[sizeof(cnts)/sizeof(*cnts)-1];
+}
+
+void *xmalloc(size_t size)
+{
+	void *ret = malloc(size);
+	count(size);
+	if (!ret && !size)
+		ret = malloc(1);
+	if (!ret)
+		die("Out of memory, malloc failed, %u bytes requested", size);
+	return ret;
+}
+
+void *xrealloc(void *ptr, size_t size)
+{
+	void *ret = realloc(ptr, size);
+	count(size);
+	if (!ret && !size)
+		ret = realloc(ptr, 1);
+	if (!ret)
+		die("Out of memory, realloc failed, %u bytes requested", size);
+	return ret;
+}
+
+void *xcalloc(size_t nmemb, size_t size)
+{
+	void *ret = calloc(nmemb, size);
+	count(size);
+	if (!ret && (!nmemb || !size))
+		ret = calloc(1, 1);
+	if (!ret)
+		die("Out of memory, calloc failed");
+	return ret;
+}
+
+void xfree(void *ptr)
+{
+	if ( !ptr )
+		return;
+	free(ptr);
+	++fcnt;
+}
+
diff --git a/git-compat-util.h b/git-compat-util.h
index f982b8e..ab4f855 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -73,35 +73,11 @@ extern void gitunsetenv(const char *);
 extern char *gitstrcasestr(const char *haystack, const char *needle);
 #endif
 
-static inline void *xmalloc(size_t size)
-{
-	void *ret = malloc(size);
-	if (!ret && !size)
-		ret = malloc(1);
-	if (!ret)
-		die("Out of memory, malloc failed");
-	return ret;
-}
-
-static inline void *xrealloc(void *ptr, size_t size)
-{
-	void *ret = realloc(ptr, size);
-	if (!ret && !size)
-		ret = realloc(ptr, 1);
-	if (!ret)
-		die("Out of memory, realloc failed");
-	return ret;
-}
-
-static inline void *xcalloc(size_t nmemb, size_t size)
-{
-	void *ret = calloc(nmemb, size);
-	if (!ret && (!nmemb || !size))
-		ret = calloc(1, 1);
-	if (!ret)
-		die("Out of memory, calloc failed");
-	return ret;
-}
+void *xmalloc(size_t size);
+void *xrealloc(void *ptr, size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+void xfree(void *ptr);
+#define free(p) xfree(p)
 
 static inline ssize_t xread(int fd, void *buf, size_t len)
 {
diff --git a/prof-decode.sh b/prof-decode.sh
new file mode 100755
index 0000000..9c07e05
--- /dev/null
+++ b/prof-decode.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+if [ $# = 0 ]; then
+    set -- /tmp/git-alloc
+fi
+cat "$@" | perl -e '
+    while(<>) {
+        if (/(\S*) \d+ (\d+:\d+) (\d+)/) {
+		$c{$1}->{$2} += $3;
+		$c{$1}->{all} += $3
+	}
+        if (/(\S*) alloc-free = (\d+)/) { $d{$1} += $2 }
+    }
+    foreach $k (sort {$c{$b}->{all} <=> $c{$a}->{all}} grep {$c{$_}} keys %c) {
+	for ( sort {
+		if ( $a eq "all" ) { -1 }
+		elsif ( $b eq "all" ) { 1 }
+		else {
+		"$a $b" =~ /(\d+):\d+ (\d+):\d+/;
+		$1 <=> $2 }
+	    } grep { $c{$k}->{$_} } keys %{$c{$k}} ) {
+	    printf "%20s\t%20s\t%d\n", $k, $_, $c{$k}->{$_}
+	}
+	printf "%20s\t%d leaks\n\n", $k, $d{$k}
+    }
+'

^ permalink raw reply related

* Re: [PATCH] git-blame, take 2
From: Linus Torvalds @ 2006-03-02 21:45 UTC (permalink / raw)
  To: Fredrik Kuivinen; +Cc: git, junkio
In-Reply-To: <20060302212816.GA11015@c165.ib.student.liu.se>



On Thu, 2 Mar 2006, Fredrik Kuivinen wrote:
> 
> Here is an updated version of git-blame. The main changes compared to
> the first version are:
> 
> * Use the new revision.h interface to do the revision walking
...
> +
> +	num_args = 0;
> +	args[num_args++] = NULL;
> +	args[num_args++] = "--topo-order";
> +	args[num_args++] = "--remove-empty";
> +	args[num_args++] = argv[1];
> +	args[num_args++] = "--";
> +	args[num_args++] = filename;
> +	args[num_args] = NULL;
> +	
> +	setup_revisions(num_args, args, &rev, "HEAD");

Ok, that wasn't really supposed to be necessary.

I guess the above works fine, but it should be perfectly fine to also just 
do a nicer helper function that just initializes rev_info with something 
like

	memset(revs, 0, sizeof(*revs));
	revs->max_count = -1;
	revs->min_date = -1;
	revs->max_date = -1;
	revs->dense = 1;

and then you can just fill in the things you want to directly. Just add 
the commits you want to populate things with to the "revs->commits" list, 
the paths to "revs->paths", set "revs->limited" to 1, and then do the 
"prepare_revision_walk()" and start walking.

But hey, the above "fake the arguments" thing also works. It makes me 
wince a bit, though.

		Linus

^ permalink raw reply

* [PATCH] git-blame, take 2
From: Fredrik Kuivinen @ 2006-03-02 21:28 UTC (permalink / raw)
  To: git; +Cc: junkio

Hi,

Here is an updated version of git-blame. The main changes compared to
the first version are:

* Use the new revision.h interface to do the revision walking
* Do the right thing in a lot of more cases than before. In particular
  parallel development tracks are hopefully handled sanely.
* Lots of clean-up

It still won't follow file renames though. The patch is against
current pu.

There are still some differences in the output between git-blame and
git-annotate. For example, in 'Makefile' git-blame assigns lines
354-358 to 455a7f3275d264f6e66045b92c83747ec461dda5 and git-annotate
assigns the same lines to 79a9d8ea0d88a3667ad19be8e705405ab5d896f1.

I think git-blame is correct in this case. This patterns occur in
several other places, git-annotate seems to sometimes assign lines to
merge commits when the lines actually changed in some other commit
which precedes the merge.

- Fredrik


Signed-off-by: Fredrik Kuivinen <freku045@student.liu.se>


---

 blame.c |  888 ++++++++++++++++++++++++++++++++++++---------------------------
 1 files changed, 504 insertions(+), 384 deletions(-)

3a81d6759da19a4776102a405505f35811320d61
diff --git a/blame.c b/blame.c
index dbce7e2..0b5d6bd 100644
--- a/blame.c
+++ b/blame.c
@@ -1,4 +1,10 @@
+/*
+ * Copyright (C) 2006, Fredrik Kuivinen <freku045@student.liu.se>
+ */
+
 #include <assert.h>
+#include <time.h>
+#include <sys/time.h>
 
 #include "cache.h"
 #include "refs.h"
@@ -7,436 +13,550 @@
 #include "tree.h"
 #include "blob.h"
 #include "diff.h"
+#include "revision.h"
 
 #define DEBUG 0
 
-struct commit** blame_lines;
+struct commit **blame_lines;
 int num_blame_lines;
 
-struct util_info
-{
-    int* line_map;
-    int num_lines;
-    unsigned char sha1[20]; /* blob sha, not commit! */
-    char* buf;
-    unsigned long size;
+struct util_info {
+	int *line_map;
+	unsigned char sha1[20];	/* blob sha, not commit! */
+	char *buf;
+	unsigned long size;
+	int num_lines;
 //    const char* path;
 };
 
-struct chunk
-{
-    int off1, len1; // ---
-    int off2, len2; // +++
+struct chunk {
+	int off1, len1;	// ---
+	int off2, len2;	// +++
 };
 
-struct patch
-{
-    struct chunk* chunks;
-    int num;
+struct patch {
+	struct chunk *chunks;
+	int num;
 };
 
-static void get_blob(struct commit* commit);
-
-int num_get_patch = 0;
-int num_commits = 0;
-
-struct patch* get_patch(struct commit* commit, struct commit* other)
-{
-    struct patch* ret = xmalloc(sizeof(struct patch));
-    ret->chunks = NULL;
-    ret->num = 0;
-
-    struct util_info* info_c = (struct util_info*) commit->object.util;
-    struct util_info* info_o = (struct util_info*) other->object.util;
-
-    if(!memcmp(info_c->sha1, info_o->sha1, 20))
-        return ret;
-
-    get_blob(commit);
-    get_blob(other);
-
-    FILE* fout = fopen("/tmp/git-blame-tmp1", "w");
-    if(!fout)
-        die("fopen tmp1 failed: %s", strerror(errno));
-
-    if(fwrite(info_c->buf, info_c->size, 1, fout) != 1)
-        die("fwrite 1 failed: %s", strerror(errno));
-    fclose(fout);
-
-    fout = fopen("/tmp/git-blame-tmp2", "w");
-    if(!fout)
-        die("fopen tmp2 failed: %s", strerror(errno));
-
-    if(fwrite(info_o->buf, info_o->size, 1, fout) != 1)
-        die("fwrite 2 failed: %s", strerror(errno));
-    fclose(fout);
-
-    FILE* fin = popen("diff -u0 /tmp/git-blame-tmp1 /tmp/git-blame-tmp2", "r");
-    if(!fin)
-        die("popen failed: %s", strerror(errno));
-
-    char buf[1024];
-    while(fgets(buf, sizeof(buf), fin)) {
-        if(buf[0] != '@' || buf[1] != '@')
-            continue;
-
-        if(DEBUG)
-            printf("chunk line: %s", buf);
-        ret->num++;
-        ret->chunks = xrealloc(ret->chunks, sizeof(struct chunk)*ret->num);
-        struct chunk* chunk = &ret->chunks[ret->num-1];
-
-        assert(!strncmp(buf, "@@ -", 4));
-
-        char* start = buf+4;
-        char* sp = index(start, ' ');
-        *sp = '\0';
-        if(index(start, ',')) {
-            int ret = sscanf(start, "%d,%d", &chunk->off1, &chunk->len1);
-            assert(ret == 2);
-        } else {
-            int ret = sscanf(start, "%d", &chunk->off1);
-            assert(ret == 1);
-            chunk->len1 = 1;
-        }
-        *sp = ' ';
-
-        start = sp+1;
-        sp = index(start, ' ');
-        *sp = '\0';
-        if(index(start, ',')) {
-            int ret = sscanf(start, "%d,%d", &chunk->off2, &chunk->len2);
-            assert(ret == 2);
-        } else {
-            int ret = sscanf(start, "%d", &chunk->off2);
-            assert(ret == 1);
-            chunk->len2 = 1;
-        }
-        *sp = ' ';
-
-        if(chunk->off1 > 0)
-            chunk->off1 -= 1;
-        if(chunk->off2 > 0)
-            chunk->off2 -= 1;
-
-        assert(chunk->off1 >= 0);
-        assert(chunk->off2 >= 0);
-    }
-    fclose(fin);
-
-    num_get_patch++;
-    return ret;
-}
-
-void free_patch(struct patch* p)
-{
-    free(p->chunks);
-    free(p);
-}
-
-static int get_blob_sha1_internal(unsigned char *sha1, const char *base, int baselen,
-                                  const char *pathname, unsigned mode, int stage);
+static void get_blob(struct commit *commit);
 
+/* Only used for statistics */
+static int num_get_patch = 0;
+static int num_commits = 0;
+static int patch_time = 0;
+
+#define TEMPFILE_PATH_LEN 60
+static struct patch *get_patch(struct commit *commit, struct commit *other)
+{
+	struct patch *ret;
+	struct util_info *info_c = (struct util_info *)commit->object.util;
+	struct util_info *info_o = (struct util_info *)other->object.util;
+	char tmp_path1[TEMPFILE_PATH_LEN], tmp_path2[TEMPFILE_PATH_LEN];
+	char diff_cmd[TEMPFILE_PATH_LEN*2 + 20];
+	struct timeval tv_start, tv_end;
+	int fd;
+	FILE *fin;
+	char buf[1024];
+	
+	ret = xmalloc(sizeof(struct patch));
+	ret->chunks = NULL;
+	ret->num = 0;
+
+	get_blob(commit);
+	get_blob(other);
+
+	gettimeofday(&tv_start, NULL);
+
+	fd = git_mkstemp(tmp_path1, TEMPFILE_PATH_LEN, "git-blame-XXXXXX");
+	if (fd < 0)
+		die("unable to create temp-file: %s", strerror(errno));
+
+	if (xwrite(fd, info_c->buf, info_c->size) != info_c->size)
+		die("write failed: %s", strerror(errno));
+	close(fd);
+
+	fd = git_mkstemp(tmp_path2, TEMPFILE_PATH_LEN, "git-blame-XXXXXX");
+	if (fd < 0)
+		die("unable to create temp-file: %s", strerror(errno));
+
+	if (xwrite(fd, info_o->buf, info_o->size) != info_o->size)
+		die("write failed: %s", strerror(errno));
+	close(fd);
+
+	sprintf(diff_cmd, "diff -u0 %s %s", tmp_path1, tmp_path2);
+	fin = popen(diff_cmd, "r");
+	if (!fin)
+		die("popen failed: %s", strerror(errno));
+
+	while (fgets(buf, sizeof(buf), fin)) {
+		struct chunk *chunk;
+		char *start, *sp;
+		
+		if (buf[0] != '@' || buf[1] != '@')
+			continue;
+
+		if (DEBUG)
+			printf("chunk line: %s", buf);
+		ret->num++;
+		ret->chunks = xrealloc(ret->chunks,
+				       sizeof(struct chunk) * ret->num);
+		chunk = &ret->chunks[ret->num - 1];
+
+		assert(!strncmp(buf, "@@ -", 4));
+
+		start = buf + 4;
+		sp = index(start, ' ');
+		*sp = '\0';
+		if (index(start, ',')) {
+			int ret =
+			    sscanf(start, "%d,%d", &chunk->off1, &chunk->len1);
+			assert(ret == 2);
+		} else {
+			int ret = sscanf(start, "%d", &chunk->off1);
+			assert(ret == 1);
+			chunk->len1 = 1;
+		}
+		*sp = ' ';
+
+		start = sp + 1;
+		sp = index(start, ' ');
+		*sp = '\0';
+		if (index(start, ',')) {
+			int ret =
+			    sscanf(start, "%d,%d", &chunk->off2, &chunk->len2);
+			assert(ret == 2);
+		} else {
+			int ret = sscanf(start, "%d", &chunk->off2);
+			assert(ret == 1);
+			chunk->len2 = 1;
+		}
+		*sp = ' ';
+
+		if (chunk->len1 == 0)
+			chunk->off1++;
+		if (chunk->len2 == 0)
+			chunk->off2++;
+
+		if (chunk->off1 > 0)
+			chunk->off1--;
+		if (chunk->off2 > 0)
+			chunk->off2--;
+
+		assert(chunk->off1 >= 0);
+		assert(chunk->off2 >= 0);
+	}
+	pclose(fin);
+	unlink(tmp_path1);
+	unlink(tmp_path2);
+	
+	gettimeofday(&tv_end, NULL);
+	patch_time += 1000000 * (tv_end.tv_sec - tv_start.tv_sec) +
+		tv_end.tv_usec - tv_start.tv_usec;
+
+	num_get_patch++;
+	return ret;
+}
+
+static void free_patch(struct patch *p)
+{
+	free(p->chunks);
+	free(p);
+}
+
+static int get_blob_sha1_internal(unsigned char *sha1, const char *base,
+				  int baselen, const char *pathname,
+				  unsigned mode, int stage);
 
 static unsigned char blob_sha1[20];
-static int get_blob_sha1(struct tree* t, const char* pathname, unsigned char* sha1)
+static int get_blob_sha1(struct tree *t, const char *pathname,
+			 unsigned char *sha1)
 {
-    const char *pathspec[2];
-    pathspec[0] = pathname;
-    pathspec[1] = NULL;
-    memset(blob_sha1, 0, sizeof(blob_sha1));
-    read_tree_recursive(t, "", 0, 0, pathspec, get_blob_sha1_internal);
+	int i;
+	const char *pathspec[2];
+	pathspec[0] = pathname;
+	pathspec[1] = NULL;
+	memset(blob_sha1, 0, sizeof(blob_sha1));
+	read_tree_recursive(t, "", 0, 0, pathspec, get_blob_sha1_internal);
 
-    int i;
-    for(i = 0; i < 20; i++) {
-        if(blob_sha1[i] != 0)
-            break;
-    }
+	for (i = 0; i < 20; i++) {
+		if (blob_sha1[i] != 0)
+			break;
+	}
 
-    if(i == 20)
-        return -1;
+	if (i == 20)
+		return -1;
 
-    memcpy(sha1, blob_sha1, 20);
-    return 0;
+	memcpy(sha1, blob_sha1, 20);
+	return 0;
 }
 
-static int get_blob_sha1_internal(unsigned char *sha1, const char *base, int baselen,
-                                  const char *pathname, unsigned mode, int stage)
+static int get_blob_sha1_internal(unsigned char *sha1, const char *base,
+				  int baselen, const char *pathname,
+				  unsigned mode, int stage)
 {
-//    printf("Got blob: %s base: '%s' baselen: %d pathname: '%s' mode: %o stage: %d\n",
-//           sha1_to_hex(sha1), base, baselen, pathname, mode, stage);
-
-    if(S_ISDIR(mode))
-        return READ_TREE_RECURSIVE;
+	if (S_ISDIR(mode))
+		return READ_TREE_RECURSIVE;
 
-    memcpy(blob_sha1, sha1, 20);
-    return -1;
+	memcpy(blob_sha1, sha1, 20);
+	return -1;
 }
 
-static void get_blob(struct commit* commit)
+static void get_blob(struct commit *commit)
 {
-    struct util_info* info = commit->object.util;
-    char type[20];
+	struct util_info *info = commit->object.util;
+	char type[20];
 
-    if(info->buf)
-        return;
+	if (info->buf)
+		return;
 
-    info->buf = read_sha1_file(info->sha1, type, &info->size);
-    assert(!strcmp(type, "blob"));
-}
+	info->buf = read_sha1_file(info->sha1, type, &info->size);
 
-void print_patch(struct patch* p)
-{
-    printf("Num chunks: %d\n", p->num);
-    int i;
-    for(i = 0; i < p->num; i++) {
-        printf("%d,%d %d,%d\n", p->chunks[i].off1, p->chunks[i].len1, p->chunks[i].off2, p->chunks[i].len2);
-    }
+	assert(!strcmp(type, "blob"));
 }
 
-
-// p is a patch from commit to other.
-void fill_line_map(struct commit* commit, struct commit* other, struct patch* p)
+/* For debugging only */
+static void print_patch(struct patch *p)
 {
-    int num_lines = ((struct util_info*) commit->object.util)->num_lines;
-    int* line_map = ((struct util_info*) commit->object.util)->line_map;
-    int num_lines2 = ((struct util_info*) other->object.util)->num_lines;
-    int* line_map2 = ((struct util_info*) other->object.util)->line_map;
-    int cur_chunk = 0;
-    int i1, i2;
-
-    if(p->num && DEBUG)
-        print_patch(p);
-
-    for(i1 = 0; i1 < num_lines; i1++)
-        line_map[i1] = -1;
-
-    if(DEBUG)
-        printf("num lines 1: %d num lines 2: %d\n", num_lines, num_lines2);
-
-    for(i1 = 0, i2 = 0; i1 < num_lines; i1++, i2++) {
-        if(DEBUG > 1)
-            printf("%d %d\n", i1, i2);
-
-        if(i2 >= num_lines2)
-            break;
-
-        line_map[i1] = line_map2[i2];
-
-        struct chunk* chunk = NULL;
-        if(cur_chunk < p->num)
-            chunk = &p->chunks[cur_chunk];
-
-        if(chunk && chunk->off1 == i1) {
-            i2 = chunk->off2;
-
-            if(chunk->len1 > 0)
-                i1 += chunk->len1-1;
-            if(chunk->len2 > 0)
-                i2 += chunk->len2-1;
-            cur_chunk++;
-        }
-    }
-}
-
-int map_line(struct commit* commit, int line)
-{
-    struct util_info* info = commit->object.util;
-    assert(line >= 0 && line < info->num_lines);
-    return info->line_map[line];
+	int i;
+	printf("Num chunks: %d\n", p->num);
+	for (i = 0; i < p->num; i++) {
+		printf("%d,%d %d,%d\n", p->chunks[i].off1, p->chunks[i].len1,
+		       p->chunks[i].off2, p->chunks[i].len2);
+	}
 }
 
-int fill_util_info(struct commit* commit, const char* path)
+/* For debugging only */
+static void print_map(struct commit *cmit, struct commit *other)
 {
-    if(commit->object.util)
-        return 0;
-
-    struct util_info* util = xmalloc(sizeof(struct util_info));
-    util->buf = NULL;
-    util->size = 0;
-    util->num_lines = -1;
-    util->line_map = NULL;
+	struct util_info *util = cmit->object.util;
+	struct util_info *util2 = other->object.util;
 
-    commit->object.util = util;
+	int i;
+	int max =
+	    util->num_lines >
+	    util2->num_lines ? util->num_lines : util2->num_lines;
+	int num;
 
-    if(get_blob_sha1(commit->tree, path, util->sha1))
-        return -1;
+	for (i = 0; i < max; i++) {
+		printf("i: %d ", i);
+		num = -1;
 
-    return 0;
-}
-
-void alloc_line_map(struct commit* commit)
-{
-    struct util_info* util = commit->object.util;
+		if (i < util->num_lines) {
+			num = util->line_map[i];
+			printf("%d\t", num);
+		} else
+			printf("\t");
 
-    if(util->line_map)
-        return;
+		if (i < util2->num_lines) {
+			int num2 = util2->line_map[i];
+			printf("%d\t", num2);
+			if (num != -1 && num2 != num)
+				printf("---");
+		} else
+			printf("\t");
 
-    get_blob(commit);
-
-    int i;
-    util->num_lines = 0;
-    for(i = 0; i < util->size; i++) {
-        if(util->buf[i] == '\n')
-            util->num_lines++;
-    }
-    util->line_map = xmalloc(sizeof(int)*util->num_lines);
+		printf("\n");
+	}
 }
 
-void copy_line_map(struct commit* dst, struct commit* src)
-{
-    struct util_info* u_dst = dst->object.util;
-    struct util_info* u_src = src->object.util;
-
-    u_dst->line_map = u_src->line_map;
-    u_dst->num_lines = u_src->num_lines;
-    u_dst->buf = u_src->buf;
-    u_dst->size = u_src->size;
-}
-
-void process_commits(struct commit_list* list, const char* path)
-{
-    int i;
-
-    while(list) {
-        struct commit* commit = pop_commit(&list);
-        struct commit_list* parents;
-        struct util_info* info;
-
-        info = commit->object.util;
-        num_commits++;
-        if(DEBUG)
-            printf("\nProcessing commit: %d %s\n", num_commits, sha1_to_hex(commit->object.sha1));
-        for(parents = commit->parents;
-            parents != NULL; parents = parents->next) {
-            struct commit* parent = parents->item;
-
-            if(parse_commit(parent) < 0)
-                die("parse_commit error");
-
-            if(DEBUG)
-                printf("parent: %s\n", sha1_to_hex(parent->object.sha1));
-
-            if(fill_util_info(parent, path))
-                continue;
-
-            // Temporarily assign everything to the parent.
-            int num_blame = 0;
-            for(i = 0; i < num_blame_lines; i++) {
-                if(blame_lines[i] == commit) {
-                    num_blame++;
-                    blame_lines[i] = parent;
-                }
-            }
-
-            if(num_blame == 0)
-                continue;
-
-            struct patch* patch = get_patch(parent, commit);
-            if(patch->num == 0) {
-                copy_line_map(parent, commit);
-            } else {
-                alloc_line_map(parent);
-                fill_line_map(parent, commit, patch);
-            }
-
-            for(i = 0; i < patch->num; i++) {
-                int l;
-                for(l = 0; l < patch->chunks[i].len2; l++) {
-                    int mapped_line = map_line(commit, patch->chunks[i].off2 + l);
-                    if(mapped_line != -1 && blame_lines[mapped_line] == parent)
-                        blame_lines[mapped_line] = commit;
-                }
-            }
-            free_patch(patch);
-        }
-    }
-}
-
-#define SEEN 1
-struct commit_list* get_commit_list(struct commit* commit, const char* pathname)
+// p is a patch from commit to other.
+static void fill_line_map(struct commit *commit, struct commit *other,
+			  struct patch *p)
 {
-    struct commit_list* ret = NULL;
-    struct commit_list* process = NULL;
-    unsigned char sha1[20];
-
-    commit_list_insert(commit, &process);
-
-    while(process) {
-        struct commit* com = pop_commit(&process);
-        if(com->object.flags & SEEN)
-            continue;
-
-        com->object.flags |= SEEN;
-        commit_list_insert(com, &ret);
-        struct commit_list* parents;
-
-        parse_commit(com);
-
-        for(parents = com->parents;
-            parents != NULL; parents = parents->next) {
-            struct commit* parent = parents->item;
-
-            parse_commit(parent);
-
-            if(!get_blob_sha1(parent->tree, pathname, sha1))
-                commit_list_insert(parent, &process);
-        }
-    }
-
-    return ret;
+	struct util_info *util = commit->object.util;
+	struct util_info *util2 = other->object.util;
+	int *map = util->line_map;
+	int *map2 = util2->line_map;
+	int cur_chunk = 0;
+	int i1, i2;
+
+	if (p->num && DEBUG)
+		print_patch(p);
+
+	if (DEBUG)
+		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;
+		if (cur_chunk < p->num)
+			chunk = &p->chunks[cur_chunk];
+
+		if (chunk && chunk->off1 == i1) {
+			if (DEBUG && i2 != chunk->off2)
+				printf("i2: %d off2: %d\n", i2, chunk->off2);
+
+			assert(i2 == chunk->off2);
+
+			i1--;
+			i2--;
+			if (chunk->len1 > 0)
+				i1 += chunk->len1;
+			
+			if (chunk->len2 > 0)
+				i2 += chunk->len2;
+
+			cur_chunk++;
+		} else {
+			if (i2 >= util2->num_lines)
+				break;
+
+			if (map[i1] != map2[i2] && map[i1] != -1) {
+				if (DEBUG)
+					printf("map: i1: %d %d %p i2: %d %d %p\n",
+					       i1, map[i1],
+					       i1 != -1 ? blame_lines[map[i1]] : NULL,
+					       i2, map2[i2],
+					       i2 != -1 ? blame_lines[map2[i2]] : NULL);
+				if (map2[i2] != -1 &&
+				    blame_lines[map[i1]] &&
+				    !blame_lines[map2[i2]])
+					map[i1] = map2[i2];
+			}
+
+			if (map[i1] == -1 && map2[i2] != -1)
+				map[i1] = map2[i2];
+		}
+
+		if (DEBUG > 1)
+			printf("l1: %d l2: %d i1: %d i2: %d\n",
+			       map[i1], map2[i2], i1, i2);
+	}
+}
+
+static int map_line(struct commit *commit, int line)
+{
+	struct util_info *info = commit->object.util;
+	assert(line >= 0 && line < info->num_lines);
+	return info->line_map[line];
+}
+
+static int fill_util_info(struct commit *commit, const char *path)
+{
+	struct util_info *util;
+	if (commit->object.util)
+		return 0;
+
+	util = xmalloc(sizeof(struct util_info));
+
+	if (get_blob_sha1(commit->tree, path, util->sha1)) {
+		free(util);
+		return 1;
+	} else {
+		util->buf = NULL;
+		util->size = 0;
+		util->line_map = NULL;
+		util->num_lines = -1;
+		commit->object.util = util;
+		return 0;
+	}
+}
+
+static void alloc_line_map(struct commit *commit)
+{
+	struct util_info *util = commit->object.util;
+	int i;
+
+	if (util->line_map)
+		return;
+
+	get_blob(commit);
+
+	util->num_lines = 0;
+	for (i = 0; i < util->size; i++) {
+		if (util->buf[i] == '\n')
+			util->num_lines++;
+	}
+	if(util->buf[util->size - 1] != '\n')
+		util->num_lines++;
+	
+	util->line_map = xmalloc(sizeof(int) * util->num_lines);
+
+	for (i = 0; i < util->num_lines; i++)
+		util->line_map[i] = -1;
+}
+
+static void init_first_commit(struct commit* commit, const char* filename)
+{
+	struct util_info* util;
+	int i;
+	
+	if (fill_util_info(commit, filename))
+		die("fill_util_info failed");
+
+	alloc_line_map(commit);
+
+	util = commit->object.util;
+	num_blame_lines = util->num_lines;
+
+	for (i = 0; i < num_blame_lines; i++)
+		util->line_map[i] = i;
+}
+
+
+static void process_commits(struct rev_info *rev, const char *path,
+			    struct commit** initial)
+{
+	int i;
+	struct util_info* util;
+	int lines_left;
+	int *blame_p;
+	int *new_lines;
+	int new_lines_len;
+
+	struct commit* commit = get_revision(rev);
+	assert(commit);
+	init_first_commit(commit, path);
+
+	util = commit->object.util;
+	num_blame_lines = util->num_lines;
+	blame_lines = xmalloc(sizeof(struct commit *) * num_blame_lines);
+	for (i = 0; i < num_blame_lines; i++)
+		blame_lines[i] = NULL;
+
+	lines_left = num_blame_lines;
+	blame_p = xmalloc(sizeof(int) * num_blame_lines);
+	new_lines = xmalloc(sizeof(int) * num_blame_lines);
+	do {
+		struct commit_list *parents;
+		int num_parents;
+		struct util_info *util;
+
+		if (DEBUG)
+			printf("\nProcessing commit: %d %s\n", num_commits,
+			       sha1_to_hex(commit->object.sha1));
+
+		if (lines_left == 0)
+			return;
+
+		num_commits++;
+		memset(blame_p, 0, sizeof(int) * num_blame_lines);
+		new_lines_len = 0;
+		num_parents = 0;
+		for (parents = commit->parents;
+		     parents != NULL; parents = parents->next)
+			num_parents++;
+
+		if(num_parents == 0)
+			*initial = commit;
+
+		if(fill_util_info(commit, path))
+			continue;
+
+		alloc_line_map(commit);
+		util = commit->object.util;
+
+		for (parents = commit->parents;
+		     parents != NULL; parents = parents->next) {
+			struct commit *parent = parents->item;
+			struct patch *patch;
+			
+			if (parse_commit(parent) < 0)
+				die("parse_commit error");
+
+			if (DEBUG)
+				printf("parent: %s\n",
+				       sha1_to_hex(parent->object.sha1));
+
+			if(fill_util_info(parent, path)) {
+				num_parents--;
+				continue;
+			}
+			
+			patch = get_patch(parent, commit);
+                        alloc_line_map(parent);
+                        fill_line_map(parent, commit, patch);
+                        
+                        for (i = 0; i < patch->num; i++) {
+                            int l;
+                            for (l = 0; l < patch->chunks[i].len2; l++) {
+                                int mapped_line =
+                                    map_line(commit, patch->chunks[i].off2 + l);
+                                if (mapped_line != -1) {
+                                    blame_p[mapped_line]++;
+                                    if (blame_p[mapped_line] == num_parents)
+                                        new_lines[new_lines_len++] = mapped_line;
+                                }
+                            }
+			}
+                        free_patch(patch);
+		}
+
+		if (DEBUG)
+			printf("parents: %d\n", num_parents);
+
+		for (i = 0; i < new_lines_len; i++) {
+			int mapped_line = new_lines[i];
+			if (blame_lines[mapped_line] == NULL) {
+				blame_lines[mapped_line] = commit;
+				lines_left--;
+				if (DEBUG)
+					printf("blame: mapped: %d i: %d\n",
+					       mapped_line, i);
+			}
+		}
+	} while ((commit = get_revision(rev)) != NULL);
 }
 
 int main(int argc, const char **argv)
 {
-    unsigned char sha1[20];
-    struct commit *commit;
-    const char* filename;
-    int i;
-
-    setup_git_directory();
-
-    if (argc != 3)
-        die("Usage: blame commit-ish file");
-
-    if (get_sha1(argv[1], sha1))
-        die("get_sha1 failed");
-
-    commit = lookup_commit_reference(sha1);
-
-    filename = argv[2];
-
-    struct commit_list* list = get_commit_list(commit, filename);
-    sort_in_topological_order(&list, 1);
-
-    if(fill_util_info(commit, filename)) {
-        printf("%s not found in %s\n", filename, argv[1]);
-        return 0;
-    }
-    alloc_line_map(commit);
-
-    struct util_info* util = commit->object.util;
-    num_blame_lines = util->num_lines;
-    blame_lines = xmalloc(sizeof(struct commit*)*num_blame_lines);
-
-
-    for(i = 0; i < num_blame_lines; i++) {
-        blame_lines[i] = commit;
-
-        ((struct util_info*) commit->object.util)->line_map[i] = i;
-    }
-
-    process_commits(list, filename);
-
-    for(i = 0; i < num_blame_lines; i++) {
-        printf("%d %s\n", i+1-1, sha1_to_hex(blame_lines[i]->object.sha1));
-//        printf("%d %s\n", i+1-1, find_unique_abbrev(blame_lines[i]->object.sha1, 6));
-    }
-
-    if(DEBUG) {
-        printf("num get patch: %d\n", num_get_patch);
-        printf("num commits: %d\n", num_commits);
-    }
+	int i;
+	struct commit *initial = NULL;
+	unsigned char sha1[20];
+	const char* filename;
+	int num_args;
+	const char* args[10];
+	struct rev_info rev;
+	
+	setup_git_directory();
+
+	if (argc != 3)
+		die("Usage: blame commit-ish file");
+
+
+	filename = argv[2];	
+	
+	{
+		struct commit* commit;
+		if (get_sha1(argv[1], sha1))
+			die("get_sha1 failed");	
+		commit = lookup_commit_reference(sha1);
+
+		if (fill_util_info(commit, filename)) {
+			printf("%s not found in %s\n", filename, argv[1]);
+			return 1;
+		}
+	}
+
+	num_args = 0;
+	args[num_args++] = NULL;
+	args[num_args++] = "--topo-order";
+	args[num_args++] = "--remove-empty";
+	args[num_args++] = argv[1];
+	args[num_args++] = "--";
+	args[num_args++] = filename;
+	args[num_args] = NULL;
+	
+	setup_revisions(num_args, args, &rev, "HEAD");
+	prepare_revision_walk(&rev);
+	process_commits(&rev, filename, &initial);
+
+	for (i = 0; i < num_blame_lines; i++) {
+		struct commit *c = blame_lines[i];
+		if (!c)
+			c = initial;
+
+		printf("%d %.8s\n", i, sha1_to_hex(c->object.sha1));
+// printf("%d %s\n", i, find_unique_abbrev(blame_lines[i]->object.sha1, 6));
+	}
+
+	if (DEBUG) {
+		printf("num get patch: %d\n", num_get_patch);
+		printf("num commits: %d\n", num_commits);
+		printf("patch time: %f\n", patch_time / 1000000.0);
+		printf("initial: %s\n", sha1_to_hex(initial->object.sha1));
+	}
 
-    return 0;
+	return 0;
 }
-- 
1.2.4.g90ab-dirty

^ permalink raw reply related

* Re: [PATCH] contrib/git-svn: use refs/remotes/git-svn instead of  git-svn-HEAD
From: Nicolas Vilz 'niv' @ 2006-03-02 21:17 UTC (permalink / raw)
  To: Eric Wong; +Cc: git list, Junio C Hamano
In-Reply-To: <20060302055831.GA16600@localdomain>

> After reading a lengthy discussion on the list, I've come to the
> conclusion that creating a 'remotes' directory in refs isn't
> such a bad idea.
>
> You can still branch from it by specifying remotes/git-svn (not
> needing the leading 'refs/'), and the documentation has been
> updated to reflect that.

i got stuck in that.

git-svn does the remotes/svn-git reference good, git pull hasn't
recognized it, yet. So i had to do a symlink from .git/refs/remotes to
.git/refs/heads/ to do the git pull...

then i got a bit stuck because of conflicts.... which i resolved (thank god).

the third thing is that the git-svn commit command does deletions of
certain files very often, when i commit some patches i pulled into my
private-talk-to-svn-branch. in on of the next commits the same files are
added again.. but i thought "o my god, i am screwed", when i saw the
scheduled deletions the first time... that is a bit confusing for the
users.

so in conclusion, i think, you should teach git pull to recognize
refs/remotes as heads to be pulled from (not to be pulled at)...

btw, i like the visualization of the remotes-refs in gitk.

Nicolas

^ permalink raw reply

* Re: impure renames / history tracking
From: Paul Jakma @ 2006-03-02 21:10 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: git list
In-Reply-To: <4405DD35.8060804@op5.se>

On Wed, 1 Mar 2006, Andreas Ericsson wrote:

> Yes, but imo a poor one, as you're losing all the history.

Well, not per se. You might keep the original 'detail' branch. It's a 
terminal branch obviously, you can't pull master's changes to it once 
the aggregate patch goes into master. But you can keep it around.

> git *can* do what you want, but it was designed to maintain a long 
> history so that everyone can see it and improve on the code with 
> many chains of small and simultanous changes.

Indeed, and I appreciate that.

> Perhpas we have a nomenclature clash here. When you say "one single 
> commit", I can't help but thinking "snapshot".

I mean:

 	git diff upstream..bugfix_xyz

or:

 	git diff upstream..project_foo_phase1

type of thing.

> It's completely impossible to fold *ALL* the history into a single 
> commit, and since you want heuristics I would imagine you wouldn't 
> want that either.

I want to know whether additional meta-data to help the existing 
heuristics would be acceptable. From a discussion on #git yesterday I 
gather the best way forward would to be to first prototype something 
keeping state in a file in .git.

All that's needed really is something that relates the following 3 
things:

 	commit-id obj1-id obj2-id

Ie: For <commit-id>, <obj1-id> is similar to <obj2-id>.

Maintaining this state could be done via the git-mv/rename wrappers 
and an additional git-edit wrapper. Those who are quite happy with 
the existing diff-input only similarity heuristics wouldn't have to 
bother using a git-edit wrapper obviously, those who want to let git 
gather additional 'similarity hint' in this way could.

Aside:

Git might be easier to extend generally if it adopted just /one/ new 
core header, say "see-also" - that could serve as a pointer to 
arbitrary commit-related meta-info objects that aren't of immediate 
interest to either:

a) core git

or

b) the user

Format:

 	see-also <word> <obj-id>

E.g.:

 	see-also similars <obj-id>

Where <obj-id> would list the 'commit obj1 obj2', but just as:

 	obj1 obj2

Would ultimately be neater than fishing around in .git/, and would 
allow other extensions in the future too.

The <word> identifier preferably would need to be centrally 
co-ordinated.

> I'm confused. First you say you want to have one single mega-patch 
> for each commit, then you say you want to be able to follow history 
> back. It's like deciding to throw away your wallet and then trying 
> to get someone to pick it up and carry it around for you.

I'm not sure why think mega-patch. Collapsing a bunch of commits 
related to one project need not result in a big patch relative to the 
repository as a whole.

In Linux terms think project == "Add ATAPI support to SATA" or 
"Change the foo VFS method and update its filesystem users" type of 
thing (ok, the latter would be big enough, but still not /that/ big 
in terms of the whole Linux source base). Where the project concerned 
is like BSD, not just a kernel but a complete userland (so 1.1GB of 
source code).

I'm aware of the workflow arguments, I /do/ intend to make those but 
elsewhere ;).

> As for convincing others, shove git-bisect under their noses and 
> ask them if they'd like a tool to find their bugs for them.

;)

[snip - thanks, interesting]

> The code is mightier than the mail. Perhaps if I see an implementation of 
> this I could wrap my head around what you really mean. I'm sure I must 
> misunderstand you one way or another.

Yes, you're right. I think Junio gave me the required hints on 
directions last night on #git.

I think now at least it's quite possible to achieve without violating 
git's "track the /content/" philosophy, via .git.

Thanks!

regards,
-- 
Paul Jakma	paul@clubi.ie	paul@jakma.org	Key ID: 64A2FF6A
Fortune:
Factorials were someone's attempt to make math LOOK exciting.

^ permalink raw reply

* [PATCH] git-branch: add -r switch to list refs/remotes/*
From: Eric Wong @ 2006-03-02 20:23 UTC (permalink / raw)
  To: git list, Junio C Hamano
In-Reply-To: <20060302055831.GA16600@localdomain>

If we decide to use refs/remotes/, having a convenient way to
list them would be nice.

Signed-off-by: Eric Wong <normalperson@yhbt.net>

---

 git-branch.sh |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)

05fb74c2a8d4bf3692a7553c5225d25ab54f358a
diff --git a/git-branch.sh b/git-branch.sh
index 6ac961e..663a3a3 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -48,6 +48,12 @@ If you are sure you want to delete it, r
     exit 0
 }
 
+ls_remote_branches () {
+    git-rev-parse --symbolic --all |
+    sed -ne 's|^refs/\(remotes/\)|\1|p' |
+    sort
+}
+
 force=
 while case "$#,$1" in 0,*) break ;; *,-*) ;; *) break ;; esac
 do
@@ -56,6 +62,10 @@ do
 		delete_branch "$@"
 		exit
 		;;
+	-r)
+		ls_remote_branches
+		exit
+		;;
 	-f)
 		force="$1"
 		;;
-- 
1.2.3.gc55f

^ permalink raw reply related

* Re: windows problems summary
From: Andreas Ericsson @ 2006-03-02 20:07 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <20060302195948.GA10740@trixie.casa.cgf.cx>

Christopher Faylor wrote:
> On Thu, Mar 02, 2006 at 08:54:50PM +0100, Alex Riesen wrote:
> 
>>Bertrand Jacquin, Thu, Mar 02, 2006 16:57:42 +0100:
>>
>>>Is the goal to have something like a git-turtoise (as
>>>{svn,cvs}-turtoise) ?  I personaly think that is could be benefic.
>>
>>Not in the original post.  It just about making git faster and more
>>stable in Windows.
> 
> 
> Can I request that people stop cc'ing me in this thread?
> 

That's most likely because people hit "reply all" so the reply goes to 
the list as well. A "normal" reply would go only to you.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ permalink raw reply

* Re: windows problems summary
From: Christopher Faylor @ 2006-03-02 19:59 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <20060302195450.GB6183@steel.home>

On Thu, Mar 02, 2006 at 08:54:50PM +0100, Alex Riesen wrote:
>Bertrand Jacquin, Thu, Mar 02, 2006 16:57:42 +0100:
>>Is the goal to have something like a git-turtoise (as
>>{svn,cvs}-turtoise) ?  I personaly think that is could be benefic.
>
>Not in the original post.  It just about making git faster and more
>stable in Windows.

Can I request that people stop cc'ing me in this thread?

I'd like to cut down on the blood pressure medication if I can.

cgf

^ permalink raw reply

* Re: windows problems summary
From: Alex Riesen @ 2006-03-02 19:54 UTC (permalink / raw)
  To: Bertrand Jacquin; +Cc: Christopher Faylor, Git Mailing List
In-Reply-To: <4fb292fa0603020757q4cd3e80cjbb63d0dc58d19756@mail.gmail.com>

Bertrand Jacquin, Thu, Mar 02, 2006 16:57:42 +0100:
> 
> Is the goal to have something like a git-turtoise (as {svn,cvs}-turtoise) ?
> I personaly think that is could be benefic.
> 

Not in the original post.
It just about making git faster and more stable in Windows.

^ permalink raw reply

* Re: [PATCH] fmt-merge-msg: avoid open "-|" list form for Perl 5.6
From: Andreas Ericsson @ 2006-03-02 17:39 UTC (permalink / raw)
  To: Alex Riesen; +Cc: Christopher Faylor, git
In-Reply-To: <81b0412b0603020810l57f9ee5p270f9c288770d1a7@mail.gmail.com>

Ye gawds, Alex. If you complained this much to your employer you'd get 
to run whatever OS you want.

Alex Riesen wrote:

[ lots of things ]

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

^ permalink raw reply

* Re: [PATCH] fmt-merge-msg: avoid open "-|" list form for Perl 5.6
From: Johannes Schindelin @ 2006-03-02 17:33 UTC (permalink / raw)
  To: Christopher Faylor; +Cc: git
In-Reply-To: <20060302164405.GB7292@trixie.casa.cgf.cx>

Hi,

On Thu, 2 Mar 2006, Christopher Faylor wrote:

> If anyone has a problem with Cygwin where signals do not seem to be
> working, I'd appreciate a bug report to the Cygwin list.  We really do
> expect that things should work and want to fix things if they don't.

I am glad to have you on this list. Thanks for all your efforts.

Ciao,
Dscho

^ permalink raw reply

* Re: windows problems summary
From: Johannes Schindelin @ 2006-03-02 17:33 UTC (permalink / raw)
  To: Alex Riesen; +Cc: Git Mailing List
In-Reply-To: <81b0412b0603020833j214556bek887c53a3ef43fd58@mail.gmail.com>

Hi,

On Thu, 2 Mar 2006, Alex Riesen wrote:

> On 3/2/06, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> >
> > > 1. opened and mmaped files can't be removed or renamed
> > >   (caused workaround with reading index in memory)
> >
> > It was not the locking which caused the workaround. It was the
> > not-working. (I still have to find a Windows machine where git-whatchanged
> > does not segfault without NO_MMAP.)
> 
> me too. It crashes everywhere here.
> 
> > > 4. non-unix permissions model
> > >   (breaks x-attr)
> > > 5. real slow filesystems and caching
> > >   (makes everything slow. I noticed I'm trying to avoid git status!).
> > >   Caused workaround with manual checkout)
> > > 6. real slow program startup
> > >   (makes everything slow, eventually may cause everything being put
> > >   in one super-executable, just to avoid spawning new processes,
> > >   with all associated problems. Makes scripting harder)
> >
> > Except for (4), these issues should be resolvable by the libifying effort.
> >
> 
> How can it help with 5? Less accesses to index?

Exactly.

Ciao,
Dscho

^ permalink raw reply

* [PATCH] Prevent --index-info from ignoring -z.
From: Shawn Pearce @ 2006-03-02 17:21 UTC (permalink / raw)
  To: git

If git-update-index --index-info -z is used only the first
record given to the process will actually be updated as
the -z option is ignored until after all index records
have been read and processed.  This meant that multiple
null terminated records were seen as a single record which
was lacking a trailing LF, however since the first record
ended in a null the C string handling functions ignored the
trailing garbage.  So --index-info should be required to be
the last command line option, much as --stdin is required
to be the last command line option.  Because --index-info
implies --stdin this isn't an issue as the user shouldn't
be passing --stdin when also passing --index-info.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>

---

 update-index.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

base eab8a06b5f0937ead199cbd35950a213486f34eb
last 77565623e6c7a02ead3d080816c761da85421781
diff --git a/update-index.c b/update-index.c
index ce1db38..797245a 100644
--- a/update-index.c
+++ b/update-index.c
@@ -577,9 +577,11 @@ int main(int argc, const char **argv)
 				break;
 			}
 			if (!strcmp(path, "--index-info")) {
+				if (i != argc - 1)
+					die("--index-info must be at the end");
 				allow_add = allow_replace = allow_remove = 1;
 				read_index_info(line_termination);
-				continue;
+				break;
 			}
 			if (!strcmp(path, "--ignore-missing")) {
 				not_new = 1;
-- 
1.2.3.g5f0e

^ permalink raw reply related

* Re: [PATCH] Teach git-checkout-index to use file suffixes.
From: Shawn Pearce @ 2006-03-02 17:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <7vbqwp6zvg.fsf@assigned-by-dhcp.cox.net>

Junio C Hamano <junkio@cox.net> wrote:
[...]
> However, --stage=all with --suffix would introduce name clashes
> between repeated conflicted merge runs, which requires Porcelain
> to be extra careful.  Your last merge run might have involved
> three conflicting stages (leaving a.c~1, a.c~2, and a.c~3 in
> your working tree) and this time it may be "we removed it while
> they modified it" situation (needing to extract a.c~1, a.c~3 but
> not a.c~2).  The Porcelain needs to make sure not to get
> confused by leftover a.c~2 file in the working tree from the
> previous run.

Clearly.  pg was trying to delete all of those files before doing its
merge work but failed because of the --ignored bug in git-ls-files;
but this is now fixed.  Still a possibility for confusion does exist.

But in at least one case my Porcelain is Eclipse and a Cygwin
prompt.  In this case I want to view and edit everything in Eclipse.
Having all of the files in the same directory just makes it easier
to view.  Random temporary names in the same directory as the tracked
file would be OK except if I had multiple conflicts in the same
directory, in which case I need some easy way to tell them apart.
At which point we're starting to derive off the tracked file name
and might as well always use well-known names.
 
> If what you are trying is to reduce the number of checkout-index
> calls by your Porcelain to extract conflicted stages, it _might_
> make more sense to do something like this instead (I am thinking
> aloud, so take this with a big grain of salt -- it may not make
> sense at all):
> 
>     checkout-index --stage=all checks out higher-order stages in
>     made-up temporary filenames, just like git-merge-one-file
>     does using git-unpack-file one-by-one, with a single
>     invocation.
> 
>     It reports the following to its standard output, one record
>     per pathname in the index:
> 
> 	tmp1 <SP> tmp2 <SP> tmp3 <TAB> pathname <RS>
[...]

That's not a bad idea.  The only thing I don't like about that
is that git-checkout-index won't build the directory tree for me;
the Porcelain must still be responsible for doing that before it
can rename the temporary files (if available) into the correct
subdirectory.

My plan with git-checkout-index though was originally to just have
it fail if the file already exists, unless -f is given.  So if
a left-over foo.c#2 was still on disk and git-checkout-index was
going to write to that name it would fail.


I see a lot of benefit from the checkout to temporary file names
and let the Porcelian rename (if it desires).

So I'm going to ask you to withdraw the --suffix patch from pu.
I'll write up a new patch using the ideas you suggest above and
submit that instead.

-- 
Shawn.

^ permalink raw reply

* Re: cygwin: push/pull takes very long time
From: Alex Riesen @ 2006-03-02 17:09 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <81b0412b0603020526w7db41994v54a96895c1a6e960@mail.gmail.com>

On 3/2/06, Alex Riesen <raa.lkml@gmail.com> wrote:
> It's still working, half an hour since it was started.
> It can be seen that it is constantly growing and shrinking
> (for about 20k,50k,80k,100k back and forth).

The distribution is (there are some git-commits and gitks
probably, here as git-read-tree?) below. The first number
after program name is lower range limit, the second - upper limit.
The number after them - counter of *alloc calls.
The strange pair 65536:99999 means very big objects, more than 1Mb.
I'll cleanup the profiling code and send it as well soon
(I had to instrument x*alloc).

      git-read-tree:16:256      127002
       git-rev-list:16:256      44775
        git-read-tree:0:16      22577
   git-pack-objects:16:256      12495
 git-pack-objects:256:4096      12303
git-pack-objects:4096:65536     6929
     git-diff-index:16:256      4381
     git-http-fetch:16:256      3535
     git-diff-index:16:256      2914
  git-write-tree:4096:65536     2876
       git-http-fetch:0:16      2111
     git-fetch-pack:16:256      1646
       git-fetch-pack:0:16      1522
     git-rev-list:256:4096      1067
                   :16:256      976
    git-read-tree:256:4096      965
   git-http-fetch:256:4096      835
                     :0:16      825
         git-ls-files:0:16      791
         git-rev-list:0:16      748
   git-fetch-pack:256:4096      698
    git-upload-pack:16:256      610
git-pack-objects:65536:1048576  594
      git-rev-parse:16:256      571
                 :256:4096      554
        git-rev-parse:0:16      492
      git-upload-pack:0:16      469
  git-upload-pack:256:4096      248
         git-cat-file:0:16      243
    git-peek-remote:16:256      242
       git-merge-base:0:16      229
     git-merge-base:16:256      208
       git-cat-file:16:256      167
    git-rev-parse:256:4096      141
      git-rev-parse:16:256      117
        git-rev-parse:0:16      116
     git-cat-file:256:4096      80
     git-pack-objects:0:16      73
       git-diff-index:0:16      65
   git-merge-base:256:4096      59
git-pack-objects:65536:99999    54
   git-current-branch:0:16      48
       git-diff-index:0:16      47
      git-diff-tree:16:256      42
   git-diff-index:256:4096      36
 git-unpack-objects:16:256      31
      git-diff-tree:16:256      30
       git-ls-files:16:256      27
   git-update-index:16:256      27
   git-diff-index:256:4096      24
     git-write-tree:16:256      23
       git-cat-file:16:256      22

^ permalink raw reply

* Re: [PATCH] fmt-merge-msg: avoid open "-|" list form for Perl 5.6
From: Shawn Pearce @ 2006-03-02 16:55 UTC (permalink / raw)
  To: git
In-Reply-To: <20060302164405.GB7292@trixie.casa.cgf.cx>

Maybe I missed this but why are people using the native Windows
ActiveState Perl with GIT+Cygwin when Cygwin has a Cygwin-ized Perl
installation available?

I've been using the Cygwin Perl with GIT without any problems
whatsoever.  Including the open(I, "-|")... exec(@argv) code that
doesn't work correctly in ActiveState and started this whole thread.

-- 
Shawn.

^ permalink raw reply

* Re: windows problems summary
From: Shawn Pearce @ 2006-03-02 16:51 UTC (permalink / raw)
  To: Alex Riesen; +Cc: Christopher Faylor, Git Mailing List
In-Reply-To: <81b0412b0603020735j603b3518ob5e27a02c531093a@mail.gmail.com>

Alex Riesen <raa.lkml@gmail.com> wrote:
> On 3/2/06, Christopher Faylor <me@cgf.cx> wrote:
> >
> > Are we *really* contemplating porting git to native Windows?
> >
> 
> Actually, I wasn't thinking about that when I was writing that mail,
> but ... why not?
> Cygwin makes syscalls many times slower, git is very slow on
> windows, users (well, I) want it faster, so if the needed api subset
> can be narrowed down to a reasonable amount of work - I think
> I'd give the idea a try.

I'd certainly appreciate faster response times from GIT on Windows.
Hell, I'd port GIT myself and cross my fingers that Junio, et. al.
would be open to accepting the changes into the tree.

But right now I don't really have a Windows development environment
available to me that I can hack on releasable changes from (damn
lawyers and those agreements about things on work computers being
owned by work).  If I get time I'll try rebuilding a system at
home that is currently offline due to failed disk as a Windows
development environment.

-- 
Shawn.

^ permalink raw reply

* Re: [PATCH] fmt-merge-msg: avoid open "-|" list form for Perl 5.6
From: Christopher Faylor @ 2006-03-02 16:44 UTC (permalink / raw)
  To: git

So to summarize:

If anyone has a problem with Cygwin where signals do not seem to be
working, I'd appreciate a bug report to the Cygwin list.  We really do
expect that things should work and want to fix things if they don't.

If that isn't possible to use the Cygwin list for some reason, I will
continue to read this mailing list and respond to Cygwin problems but I
would appreciate it if any Cygwin problem report contained details for
reproducing the problem.  We usually point people to this page
http://cygwin.com/problems.html when they have problems.  The basic take
away from that page is to provide the cygcheck output which shows what
settings have been used for your Cygwin installation.  The interesting
stuff in that output is the cygwin mount points, the CYGWIN environment
variable, and version information about the Cygwin DLL.

The Cygwin web site is http://cygwin.com/ and it has a lot of information
about Cygwin.  Some of it is undoubtedly out-of-date or unclear but we
do try to improve things if they are brought to our attention.

I don't see any reason to respond to this thread any further but I will
continue to rectify any misstatements that I see being made about
Windows or Cygwin here.

cgf (Cygwin Maintainer)

^ permalink raw reply

* Re: windows problems summary
From: Alex Riesen @ 2006-03-02 16:33 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.63.0603021636020.30490@wbgn013.biozentrum.uni-wuerzburg.de>

On 3/2/06, Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
>
> > 1. opened and mmaped files can't be removed or renamed
> >   (caused workaround with reading index in memory)
>
> It was not the locking which caused the workaround. It was the
> not-working. (I still have to find a Windows machine where git-whatchanged
> does not segfault without NO_MMAP.)

me too. It crashes everywhere here.

> > 4. non-unix permissions model
> >   (breaks x-attr)
> > 5. real slow filesystems and caching
> >   (makes everything slow. I noticed I'm trying to avoid git status!).
> >   Caused workaround with manual checkout)
> > 6. real slow program startup
> >   (makes everything slow, eventually may cause everything being put
> >   in one super-executable, just to avoid spawning new processes,
> >   with all associated problems. Makes scripting harder)
>
> Except for (4), these issues should be resolvable by the libifying effort.
>

How can it help with 5? Less accesses to index?

^ permalink raw reply

* Re: windows problems summary
From: Alex Riesen @ 2006-03-02 16:26 UTC (permalink / raw)
  To: Andreas Ericsson; +Cc: Christopher Faylor, Git Mailing List
In-Reply-To: <440716DB.7080108@op5.se>

On 3/2/06, Andreas Ericsson <ae@op5.se> wrote:
> >>Are we *really* contemplating porting git to native Windows?
> >
> > Actually, I wasn't thinking about that when I was writing that mail,
> > but ... why not?
>
> For the same reason we don't support Perl 5.4. It's too much effort for
> too little gain. OTOH, if you want to do the effort, I won't mind taking
> the gain. ;)

I don't care. I just have nothing for you to gain from yet.

^ permalink raw reply


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