Git development
 help / color / mirror / Atom feed
* Re: git merging
From: Jeff Garzik @ 2005-06-17 23:51 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Jens Axboe, Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0506171629320.2268@ppc970.osdl.org>

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

Linus Torvalds wrote:
> Ok. The most likely reason is that your main branch is not a symlink to 
> ".git/refs/***" at all, but just a regular ".git/HEAD" file.

This is definitely not the case; my .git/HEAD is _always_ a symlink.

My git-switch-tree script, attached, demonstrates how .git/HEAD symlink 
is retargetted to the specified branch.  My workflow depends on 
.git/HEAD being a symlink.

I'll see if I can reproduce with the latest git.

	Jeff



[-- Attachment #2: git-switch-tree --]
[-- Type: text/plain, Size: 232 bytes --]

#!/bin/sh

if [ "x$1" != "x" ]
then
	if [ ! -f .git/refs/heads/$1 ]
	then
		echo Branch $1 not found.
		exit 1
	fi

	( cd .git && rm -f HEAD && ln -s refs/heads/$1 HEAD )
fi

git-read-tree -m HEAD && git-checkout-cache -q -f -u -a


^ permalink raw reply

* Re: git merging
From: Linus Torvalds @ 2005-06-18  0:13 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Jens Axboe, Git Mailing List
In-Reply-To: <42B36207.3020209@pobox.com>



On Fri, 17 Jun 2005, Jeff Garzik wrote:
> 
> This is definitely not the case; my .git/HEAD is _always_ a symlink.

Ok. Are you sure that you gave the same arguments (or rather, lack of
arguments) to both fsck and "git prune"? The thing is, they are both
really the same thing, so I'm pretty surprised. If git prune says 
something is unreachable, then git-fsck-cache shouldn't complain about it 
being gone, because one just depends on the other..

> My git-switch-tree script, attached, demonstrates how .git/HEAD symlink 
> is retargetted to the specified branch.  My workflow depends on 
> .git/HEAD being a symlink.

Btw, you can now do the same thing more safely and guarantee that it 
doesn't overwrite any old information by using

	git-read-tree -m -u <old-head> <new-head>

which basically switches from "old" to "new", and verifies that all the 
old index contents were valid in "old-head", and that any file that was 
dirty is not different in "new-head".

Your old script would silently overwrite any dirty state in your working
directory, and drop anything that you had done a git-update-cache on but
not committed.

Now, you may have _depended_ on that behaviour as a way to just reset the 
tree to a known state, but if so, I'd suggest using

	git-read-tree --reset HEAD && git-checkout-cache -q -f -u -a

for that instead (which will also throw away any partial merges).

So for the "switch" case, you might make it be something like

	if [ ! -f .git/refs/heads/$1 ]
	then
		echo "Branch '$1' not found"
		exit 1;
	fi
	git-read-tree -m -u HEAD "heads/$1" && ln -sf refs/heads/$1 .git/HEAD

which should do the right thing.

Totally untested, of course ;)

[ But the two-tree read-tree is definitely not untested: this is how we 
  do a safe "fast forward" in the git-resolve-script, which really ends up 
  being the exact same thing: it "switches" from one head to another ].

		Linus

^ permalink raw reply

* Re: Handling merge conflicts a bit more gracefully..
From: Herbert Xu @ 2005-06-18  0:15 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: junkio, git
In-Reply-To: <Pine.LNX.4.58.0506081629370.2286@ppc970.osdl.org>

Linus Torvalds <torvalds@osdl.org> wrote:
> 
>>  # Modified in both, but differently.
>> +     merge -p "$src1" "$orig" "$src2" > "$4"
>> 
>> Again, make sure "$4" is not a directory before redirecting into
>> it from merge, so that you can tell merge failures from it?
> 
> Hmm.. What's the cleanest way to check for redirection errors, but still
> be able to distinguish those cleanly from "merge" itself returning an
> error?

I don't know whether this is the cleanest, but this is one way:

redir=failed
{
	redir=ok
	merge -p "$src1" "$orig" "$src2"
} > "$4" || err=$?

if [ $redir = failed ]; then
	...
fi

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: Handling merge conflicts a bit more gracefully..
From: Linus Torvalds @ 2005-06-18  0:26 UTC (permalink / raw)
  To: Herbert Xu; +Cc: junkio, git
In-Reply-To: <E1DjQza-0001wP-00@gondolin.me.apana.org.au>



On Sat, 18 Jun 2005, Herbert Xu wrote:
> 
> I don't know whether this is the cleanest, but this is one way:

Oh, wow.

One thing I have to say is that I've learnt a lot more shell tricks. 

Now I'll just have to unlearn them, so that I won't have nightmares.

		Linus

^ permalink raw reply

* Re: Converting SVN repository to git
From: Seth W. Klein @ 2005-06-18  3:26 UTC (permalink / raw)
  To: Art Haas; +Cc: git
In-Reply-To: <20050615184720.GF31997@artsapartment.org>

On Wed, Jun 15, 2005 at 01:47:20PM -0500, Art Haas wrote:
> 
> [....] I've not seen anything, though,
> on switching Subversion repositories to git. Has there been any public
> activity in writing a tool/script to do this? Perhaps some offline
> discussion about doing this?

The following script is what I used to convert a simple svn repository.
It doesn't handle branches or tags and contains buggy use of grep and
inscrutable uncommented use of sed but it should be a starting place for
someone with a more complex repository available for testing.

export GIT_AUTHOR_EMAIL=sk@sethwklein.net
export GIT_COMMITTER_EMAIL=sk@sethwklein.net
svn co -r 1 file:///home/sk/rep/oakheart/trunk oakheart
cd oakheart
git-init-db
for REV in `seq 2 692`; do
    echo -n $REV' '
    svn update -r $REV > .svnupdate
    sed -nr 's/^A +//p' .svnupdate \
    | while read F; do [ -f "$F" ] && echo "$F"; done \
    | xargs -r git-update-cache --add --
    sed -nr 's/^D +//p' .svnupdate \ 
    | while read F; do git-ls-files | grep "^$F"'\(/\|$\)'; done \
    | xargs -r git-update-cache --remove --
    sed -nr 's/^U +//p' .svnupdate \
    | while read F; do [ -f "$F" ] && echo "$F"; done \
    | xargs -r git-update-cache --
    PARENT=
    [ -f .git/HEAD ] &&
        PARENT='-p '`cat .git/HEAD`
    svn log -r $REV | sed -n '1,3d;$d;H;/^$/!{s/.*//;x;s/\n//;p}' \
    | git-commit-tree `git-write-tree` $PARENT > .git/HEAD
done

Cheers,
Seth W. Klein
-- 
sk@sethwklein.net     AIM: sethwklein     http://www.sethwklein.net/

^ permalink raw reply

* git-rev-list: "--bisect" flag
From: Linus Torvalds @ 2005-06-18  6:31 UTC (permalink / raw)
  To: Git Mailing List


Ok, I just added a feature to "git-rev-list" that I hereby nominate to be
the coolest feature ever, namely the "--bisect" flag, which basically
tries to split the list of revisions in two, and prints out just the "most
middle" commit.

The idea is that you want to do binary-search for a bug, but since the git
history isn't a nice linear thing, you don't know where the hell the
mid-point is between two commits, and even if you _do_ know where it is,
it gets even more complicated when you have 3 points (on different
branches) that are known good, and one point that is known bad.

Now, assuming you have three known-good points, and one known bad point, 
the way to get all commits in between is

	git-rev-list bad ^good1 ^good2 ^good3

and with the new flag you can now trivially get an estimate for what the 
mid-point is in this list. So you just do

	git-rev-list --bisect bad ^good1 ^good2 ^good3

and you now have the point to test. If testing shows that mid-way-point 
was bad, you just continue with that as a "new bad":

	git-rev-list --bisect new-bad ^good1 ^good2 ^good3

but if it shows that that point was still good, you instead just add it to 
the list of uninteresting commits, and do

	git-rev-list --bisect bad ^good1 ^good2 ^good3 ^new-good

instead. Every iteration you basically cut down the number of commits by 
half - you're bisecting the set, and binary-searching for the first bad 
commit.

Note that git also makes resetting to the test-point be really trivial: 
you just do

	git-read-tree -m -u <prev-point> <test-point>

where "prev-point" was your previous state (ie usually "HEAD" the first 
time, but the previous test-point otherwise), and "test-point" is the new 
point you want to test.

I haven't scripted this, but it should basically be pretty easy to add a
couple of simple scripts to make it very easy to do the binary search be
totally automated, assuming just a simple "mark test as failed/good"  
interface. Even without the scripting, it shouldn't be that hard to do
entirely by hand.

The idea being that if you notice that something doesn't work (usually in 
HEAD), but you know it used to work in the previous release, you just 
start the whole thing going with

	# set up initial state
	cat .git/HEAD > .git/BAD
	cat .git/HEAD > .git/CURRENT
	echo "^v2.6.12" > .git/GOOD

and then you just do something like:

	# iterate
	git-rev-list --bisect BAD $(cat .git/BAD) > .git/HALFWAY
	if [ ! -s .git/HALFWAY ]; then
		echo FOUND IT:
		cat .git/CURRENT
		exit 1
	fi
	git-read-tree -m -u CURRENT HALFWAY
	cat .git/HALFWAY > .git/CURRENT

	make ..


	# if good
	echo '^'$(cat .git/CURENT) >> .git/GOOD
	.. iterate ..

	# if bad
	cat .git/CURRENT > .git/BAD
	.. iterate ..

until it says "FOUND IT".

(The above is untested, but the --bisect behaviour itself I tested, and it 
seems to do the right thing).

NOTE! I'm not convinced I actually find a particularly good place to split 
at, but I think my stupid "halfway point" decision is good enough in 
practice. So it might not really be a real binary search, but I think it 
comes pretty close to it unless you have some really odd cases.

			Linus

^ permalink raw reply

* qgit-0.6
From: Marco Costalba @ 2005-06-18 10:38 UTC (permalink / raw)
  To: git; +Cc: berkus

Here is qgit-0.6, a git GUI viewer

New in this version:

- added annotate

- added color highlighting to selected diff target

- added color to file list: green new file, red removed one

- fixed locale visualizations

- fixed correct git-rev-list range handling

- fixed center on deleted files in diff viewer

- fixed disappearing files when reloading (nasty one)

- clean up of diff target logic

- added README

You can download from 
http://prdownloads.sourceforge.net/qgit/qgit-0.6.tar.bz2?download

To try qgit:

1) Unpack downloaded file
2) make
3) cd bin
4) copy qgit bin file anywhere in your path

Some (updated) screenshots at:
http://sourceforge.net/project/screenshots.php?group_id=139897

A word on annotate: In file viewer, after a while :-), the file contents will change to show the
annotations. Annotations are calculated in background so it may takes some time to show (it
depends mostly on fetching history patches with git-diff-tree -p ). History is snapshotted to
actual loaded data so peraphs you need qgit to have loaded an interesting amount of data before
calling file viewer.


I think all known (to me) problems should be fixed now. Apart from the new annotate function, a
bit experimental, qgit should be quite usable. 
So if you find some bugs/issues/inconsistencies/ etc.. please drop me a line.


Marco




		
__________________________________ 
Do you Yahoo!? 
Yahoo! Mail - Helps protect you from nasty viruses. 
http://promotions.yahoo.com/new_mail

^ permalink raw reply

* gitweb and tar snapshots
From: Sven Verdoolaege @ 2005-06-18 11:31 UTC (permalink / raw)
  To: git; +Cc: Kay Sievers

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

Attached two patches.

The first factors out the generation of the navigation bar.
The second, which depends on the first, adds "snapshot" to
the navigation bar.

The patches are against (a slightly modified) version 222.

skimo

[-- Attachment #2: gitweb-nav --]
[-- Type: text/plain, Size: 13201 bytes --]

diff --git a/gitweb.cgi b/gitweb.cgi
--- a/gitweb.cgi
+++ b/gitweb.cgi
@@ -283,6 +283,27 @@ EOF
 	print "</div>\n";
 }
 
+sub git_page_nav {
+	my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_;
+	$extra = '' if !defined $extra;
+	my @navs = qw(summary shortlog log commit commitdiff tree);
+	my %arg = map { $_, ''} @navs;
+	if (defined $head) {
+		for (qw(shortlog log commit commitdiff)) {
+			$arg{$_} = ";h=$head";
+		}
+	}
+	$arg{tree} .= ";h=$treehead" if defined $treehead;
+	$arg{tree} .= ";hb=$treebase" if defined $treebase;
+
+	print "<div class=\"page_nav\">\n" .
+		(join " | ", map { $_ eq $current ? $_ :
+			$cgi->a({-href => "$my_uri?p=$project;a=$_$arg{$_}"}, "$_") }
+			grep { $_ ne $suppress } @navs) .
+	      "<br/>$extra<br/>\n" .
+	      "</div>\n";
+}
+
 sub git_footer_html {
 	print "<div class=\"page_footer\">\n";
 	if (defined $project) {
@@ -816,15 +837,7 @@ sub git_summary {
 	}
 
 	git_header_html();
-	print "<div class=\"page_nav\">\n" .
-	      "summary".
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "shortlog") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log"}, "log") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$head"}, "commit") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$head"}, "commitdiff") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree"}, "tree") .
-	      "<br/><br/>\n" .
-	      "</div>\n";
+	git_page_nav('summary', '', $head);
 	print "<div class=\"title\">&nbsp;</div>\n";
 	print "<table cellspacing=\"0\">\n" .
 	      "<tr><td>description</td><td>" . escapeHTML($descr) . "</td></tr>\n" .
@@ -949,15 +962,7 @@ sub git_summary {
 sub git_tags {
 	my $head = git_read_hash("$project/HEAD");
 	git_header_html();
-	print "<div class=\"page_nav\">\n" .
-	      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "shortlog") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log"}, "log") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$head"}, "commit") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$head"}, "commitdiff") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;hb=$head"}, "tree") . "<br/>\n" .
-	      "<br/>\n" .
-	      "</div>\n";
+	git_page_nav('', '', $head, undef, $head);
 	my $taglist = git_read_refs("refs/tags");
 	print "<div>\n" .
 	      $cgi->a({-href => "$my_uri?p=$project;a=summary", -class => "title"}, "&nbsp;") .
@@ -995,15 +1000,7 @@ sub git_tags {
 sub git_branches {
 	my $head = git_read_hash("$project/HEAD");
 	git_header_html();
-	print "<div class=\"page_nav\">\n" .
-	      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "shortlog") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log"}, "log") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$head"}, "commit") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$head"}, "commitdiff") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;hb=$head"}, "tree") . "<br/>\n" .
-	      "<br/>\n" .
-	      "</div>\n";
+	git_page_nav('', '', $head, undef, $head);
 	my $taglist = git_read_refs("refs/heads");
 	print "<div>\n" .
 	      $cgi->a({-href => "$my_uri?p=$project;a=summary", -class => "title"}, "&nbsp;") .
@@ -1073,15 +1070,9 @@ sub git_blob {
 	my $base = $file_name || "";
 	git_header_html();
 	if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
-		print "<div class=\"page_nav\">\n" .
-		      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "shortlog") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log"}, "log") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash_base"}, "commit") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash_base"}, "commitdiff") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$hash_base"}, "tree") . "<br/>\n";
-		print $cgi->a({-href => "$my_uri?p=$project;a=blob_plain;h=$hash"}, "plain") . "<br/>\n" .
-		      "</div>\n";
+		git_page_nav('', '', $hash_base, $co{'tree'}, $hash_base,
+			$cgi->a({-href => "$my_uri?p=$project;a=blob_plain;h=$hash"}, "plain")
+		);
 		print "<div>" .
 		      $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash_base", -class => "title"}, escapeHTML($co{'title'})) .
 		      "</div>\n";
@@ -1141,15 +1132,7 @@ sub git_tree {
 	my $base = "";
 	if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
 		$base_key = ";hb=$hash_base";
-		print "<div class=\"page_nav\">\n" .
-		      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$hash_base"}, "shortlog") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash_base"}, "log") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash_base"}, "commit") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash_base"}, "commitdiff") .
-		      " | tree" .
-		      "<br/><br/>\n" .
-		      "</div>\n";
+		git_page_nav('tree', '', $hash_base);
 		print "<div>\n" .
 		      $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash_base", -class => "title"}, escapeHTML($co{'title'})) . "\n" .
 		      "</div>\n";
@@ -1286,38 +1269,30 @@ sub git_log {
 		$page = 0;
 	}
 	git_header_html();
-	print "<div class=\"page_nav\">\n";
-	print $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$hash"}, "shortlog") .
-	      " | log" .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash"}, "commit") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash"}, "commitdiff") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$hash;hb=$hash"}, "tree") . "<br/>\n";
-
+	my $extra = '';
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
 	open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd;
 
 	if ($hash ne $head || $page) {
-		print $cgi->a({-href => "$my_uri?p=$project;a=log"}, "HEAD");
+		$extra .= $cgi->a({-href => "$my_uri?p=$project;a=log"}, "HEAD");
 	} else {
-		print "HEAD";
+		$extra .= "HEAD";
 	}
 	if ($page > 0) {
-		print " &sdot; " .
+		$extra .= " &sdot; " .
 		$cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash;pg=" . ($page-1), -accesskey => "p", -title => "Alt-p"}, "prev");
 	} else {
-		print " &sdot; prev";
+		$extra .= " &sdot; prev";
 	}
 	if ($#revlist >= (100 * ($page+1)-1)) {
-		print " &sdot; " .
+		$extra .= " &sdot; " .
 		$cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash;pg=" . ($page+1), -accesskey => "n", -title => "Alt-n"}, "next");
 	} else {
-		print " &sdot; next";
+		$extra .= " &sdot; next";
 	}
-	print "<br/>\n" .
-	      "</div>\n";
+	git_page_nav('log', '', $hash, $hash, $hash, $extra);
 	if (!@revlist) {
 		print "<div>\n" .
 		      $cgi->a({-href => "$my_uri?p=$project;a=summary", -class => "title"}, "&nbsp;") .
@@ -1384,16 +1359,8 @@ sub git_commit {
 	@difftree = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading diff-tree failed.");
 	git_header_html();
-	print "<div class=\"page_nav\">\n" .
-	      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$hash"}, "shortlog") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash"}, "log") .
-	      " | commit";
-	if (defined $co{'parent'}) {
-		print " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash"}, "commitdiff");
-	}
-	print " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$hash"}, "tree") . "\n" .
-	      "<br/><br/></div>\n";
+	git_page_nav('commit', defined $co{'parent'} ? '' : 'commitdiff',
+		$hash, $co{'tree'}, $hash);
 	if (defined $co{'parent'}) {
 		print "<div>\n" .
 		      $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash", -class => "title"}, escapeHTML($co{'title'})) . "\n" .
@@ -1563,16 +1530,9 @@ sub git_blobdiff {
 	mkdir($git_temp, 0700);
 	git_header_html();
 	if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
-		print "<div class=\"page_nav\">\n" .
-		      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "shortlog") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log"}, "log") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash_base"}, "commit") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash_base"}, "commitdiff") .
-		      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$hash_base"}, "tree") .
-		      "<br/>\n";
-		print $cgi->a({-href => "$my_uri?p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent"}, "plain") .
-		      "</div>\n";
+		git_page_nav('', '', $hash_base, $co{'tree'}, $hash_base,
+			$cgi->a({-href => "$my_uri?p=$project;a=blobdiff_plain;h=$hash;hp=$hash_parent"}, "plain")
+		);
 		print "<div>\n" .
 		      $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash_base", -class => "title"}, escapeHTML($co{'title'})) . "\n" .
 		      "</div>\n";
@@ -1615,15 +1575,9 @@ sub git_commitdiff {
 	close $fd or die_error(undef, "Reading diff-tree failed.");
 
 	git_header_html();
-	print "<div class=\"page_nav\">\n" .
-	      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog;h=$hash"}, "shortlog") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash"}, "log") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash"}, "commit") .
-	      " | commitdiff" .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$hash"}, "tree") . "<br/>\n";
-	print $cgi->a({-href => "$my_uri?p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent"}, "plain") . "\n" .
-	      "</div>\n";
+	git_page_nav('commitdiff', '', $hash, $co{'tree'}, $hash,
+		$cgi->a({-href => "$my_uri?p=$project;a=commitdiff_plain;h=$hash;hp=$hash_parent"}, "plain")
+	);
 	print "<div>\n" .
 	      $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash", -class => "title"}, escapeHTML($co{'title'})) . "\n" .
 	      "</div>\n";
@@ -1731,15 +1685,7 @@ sub git_history {
 		die_error(undef, "Unknown commit object.");
 	}
 	git_header_html();
-	print "<div class=\"page_nav\">\n" .
-	      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "shortlog") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log"}, "log") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash"}, "commit") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash"}, "commitdiff") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$hash"}, "tree") .
-	      "<br/><br/>\n" .
-	      "</div>\n";
+	git_page_nav('', '', $hash, $co{'tree'}, $hash);
 	print "<div>\n" .
 	      $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash", -class => "title"}, escapeHTML($co{'title'})) . "\n" .
 	      "</div>\n";
@@ -1815,15 +1761,7 @@ sub git_search {
 		$pickaxe_search = 1;
 	}
 	git_header_html();
-	print "<div class=\"page_nav\">\n" .
-	      $cgi->a({-href => "$my_uri?p=$project;a=summary;h=$hash"}, "summary") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=shortlog"}, "shortlog") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash"}, "log") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash"}, "commit") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash"}, "commitdiff") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$co{'tree'};hb=$hash"}, "tree") .
-	      "<br/><br/>\n" .
-	      "</div>\n";
+	git_page_nav('', '', $hash, $co{'tree'}, $hash);
 
 	print "<div>\n" .
 	      $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash", -class => "title"}, escapeHTML($co{'title'})) . "\n" .
@@ -1944,13 +1882,7 @@ sub git_shortlog {
 		$page = 0;
 	}
 	git_header_html();
-	print "<div class=\"page_nav\">\n" .
-	      $cgi->a({-href => "$my_uri?p=$project;a=summary"}, "summary") .
-	      " | shortlog" .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=log;h=$hash"}, "log") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash"}, "commit") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=commitdiff;h=$hash"}, "commitdiff") .
-	      " | " . $cgi->a({-href => "$my_uri?p=$project;a=tree;h=$hash;hb=$hash"}, "tree") . "<br/>\n";
+	git_page_nav('shortlog', '', $hash, $hash, $hash);
 
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
 	open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");

[-- Attachment #3: gitweb-snapshot --]
[-- Type: text/plain, Size: 2872 bytes --]

diff --git a/gitweb.cgi b/gitweb.cgi
--- a/gitweb.cgi
+++ b/gitweb.cgi
@@ -178,6 +178,9 @@ if ($action eq "summary") {
 } elsif ($action eq "shortlog") {
 	git_shortlog();
 	exit;
+} elsif ($action eq "snapshot") {
+	git_snapshot();
+	exit;
 } else {
 	undef $action;
 	die_error(undef, "Unknown action.");
@@ -286,10 +289,10 @@ EOF
 sub git_page_nav {
 	my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_;
 	$extra = '' if !defined $extra;
-	my @navs = qw(summary shortlog log commit commitdiff tree);
+	my @navs = qw(summary shortlog log commit commitdiff tree snapshot);
 	my %arg = map { $_, ''} @navs;
 	if (defined $head) {
-		for (qw(shortlog log commit commitdiff)) {
+		for (qw(shortlog log commit commitdiff snapshot)) {
 			$arg{$_} = ";h=$head";
 		}
 	}
@@ -1943,3 +1946,77 @@ sub git_shortlog {
 	print "</table\n>";
 	git_footer_html();
 }
+
+sub git_snapshot {
+	if (!defined $hash) {
+		$hash = git_read_hash("$project/HEAD");
+	}
+	my %co = git_read_commit($hash);
+	if (!%co) {
+		die_error(undef, "Unknown commit object.");
+	}
+	my $st = $cgi->param('st');
+	if (defined $st) {
+		return git_serve_snapshot($st);
+	}
+
+	git_header_html();
+	git_page_nav('snapshot', '', $hash, $co{'tree'}, $hash);
+	print "<div>\n" .
+	      $cgi->a({-href => "$my_uri?p=$project;a=commit;h=$hash", -class => "title"}, escapeHTML($co{'title'})) . "\n" .
+	      "</div>\n";
+	print "<table cellspacing=\"0\">\n" .
+	      "<tr>\n" .
+	      "<th>Type</th>\n" .
+	      "<th></th>\n" .
+	      "</tr>\n";
+	my %types = (
+		'Bzipped tar archive' => 'tar.bz2',
+		'Gzipped tar archive' => 'tar.gz',
+	);
+	my $alternate = 0;
+	for my $type (sort keys %types) {
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+		print "<td>$type</td>";
+		$cgi->param("a", "snapshot");
+		print "<td>" .
+		      $cgi->startform(-method => "get", -action => "$my_uri") .
+		      $cgi->hidden(-name => "p") . "\n" .
+		      $cgi->hidden(-name => "a") . "\n" .
+		      $cgi->hidden(-name => "h") . "\n" .
+		      $cgi->hidden(-name => "st", 
+				   -value => $types{$type}) . "\n" .
+		      $cgi->submit(-label => 'Download') . "\n" .
+		      $cgi->end_form() . "\n" .
+		      "</td>";
+	        print "</tr>\n";
+	}
+	print "</table>\n";
+	git_footer_html();
+	return;
+}
+
+sub git_serve_snapshot {
+	my ($st) = @_;
+	my %info = (
+		'tar.bz2' => [ 'application/x-bzip2', 'bzip2' ],
+		'tar.gz' => [ 'application/x-gzip', 'gzip' ],
+	);
+	if (!exists $info{$st}) {
+		die_error(undef, "Unknown snapshot type.");
+	}
+	my ($type, $zip) = @{$info{$st}};
+	print $cgi->header(-type => $type, 
+			   -attachment => "$project-$hash.$st");
+	open my $fd, "-|", "$gitbin/git-tar-tree $hash '$project-$hash' | $zip" 
+		or return;
+	undef $/;
+	print <$fd>;
+	$/ = "\n";
+	close $fd;
+}

^ permalink raw reply

* full kernel history?
From: Zack Brown @ 2005-06-18 15:29 UTC (permalink / raw)
  To: git

Hi folks,

Is anyone maintaining a public git repository of the Linux kernel from 2.5.4
to the present, that includes all version tags and also tracks the current
Linus tree? It's a shame to lose all the BK history, especially with so many
git repo viewers cropping up.

Be well,
Zack

-- 
Zack Brown

^ permalink raw reply

* Re: full kernel history?
From: Christopher Li @ 2005-06-18 13:22 UTC (permalink / raw)
  To: Zack Brown; +Cc: git
In-Reply-To: <20050618152910.GA2037@tumblerings.org>

A shameless plug:

http://www.kernel.org/hg/

It is not git, but if you just want to view stuff from the web should be fine.

Chris

On Sat, Jun 18, 2005 at 08:29:10AM -0700, Zack Brown wrote:
> Hi folks,
> 
> Is anyone maintaining a public git repository of the Linux kernel from 2.5.4
> to the present, that includes all version tags and also tracks the current
> Linus tree? It's a shame to lose all the BK history, especially with so many
> git repo viewers cropping up.
> 
> Be well,
> Zack
> 
> -- 
> Zack Brown
> -
> 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

^ permalink raw reply

* cvs2git and file permissions
From: Sven Verdoolaege @ 2005-06-18 20:52 UTC (permalink / raw)
  To: Git Mailing List, Linus Torvalds

cvs2git currently drops mode information.
I've been bitten by this behaviour twice already.
The patch below fixes that, though not very cleanly.

skimo
--
git-cvs2git: propagate mode information

Let cvs checkout in a temporary directory rather than
using the pipe option to avoid loss of mode information.

Signed-off-by: Sven Verdoolaege <skimo@liacs.nl>

---
commit df97fd9b709bd927f15adbc1a90ddfcfa79c7895
tree 0f223f52fdf7d61d7d1df0da1137a542811d27f9
parent fdf95bf8d4d1182db579bd25fe5e25811084eaa6
author Sven Verdoolaege <skimo@kotnet.org> Sat, 18 Jun 2005 22:41:46 +0200
committer Sven Verdoolaege <skimo@kotnet.org> Sat, 18 Jun 2005 22:41:46 +0200

 cvs2git.c |    9 +++++++--
 1 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/cvs2git.c b/cvs2git.c
--- a/cvs2git.c
+++ b/cvs2git.c
@@ -196,10 +196,15 @@ static void update_file(char *line)
 	}
 
 	dir = strrchr(name, '/');
-	if (dir)
+	if (dir) {
 		printf("mkdir -p %.*s\n", (int)(dir - name), name);
+		printf("mkdir -p .git-tmp/%.*s\n", (int)(dir - name), name);
+	}
 
-	printf("cvs -q -d %s checkout -r%s -p '%s/%s' > '%s'\n", cvsroot, version, cvsmodule, name, name);
+	printf("cvs -q -d %s checkout -N -d .git-tmp -r%s '%s/%s'\n", 
+		cvsroot, version, cvsmodule, name);
+	printf("mv -f .git-tmp/%s/%s %s\n", cvsmodule, name, name);
+	printf("rm -rf .git-tmp\n");
 	printf("git-update-cache --add -- '%s'\n", name);
 }
 

^ permalink raw reply

* Re: cvs2git and file permissions
From: Linus Torvalds @ 2005-06-18 21:23 UTC (permalink / raw)
  To: skimo; +Cc: Git Mailing List
In-Reply-To: <20050618205208.GA4917@billie.cs.kuleuven.ac.be>



On Sat, 18 Jun 2005, Sven Verdoolaege wrote:
> 
> Let cvs checkout in a temporary directory rather than
> using the pipe option to avoid loss of mode information.

Hmm.. Why do you use the "-N" flag?

Wouldn't it be much cleaner to _not_ create all those sub-directories 
under ".git-tmp", and instead do something like

	"cvs -q -d %s checkout -d .git-tmp -r%s '%s/%s'" ...
	"mv -f .git-tmp/%s %s\n", dir ? dir+1 : name, name

With that changed (and tested ;), I'll happily take it.

		Linus

^ permalink raw reply

* Re: Stacked GIT 0.1 (a.k.a. quilt for git)
From: Catalin Marinas @ 2005-06-18 21:35 UTC (permalink / raw)
  To: Daniel Barkalow; +Cc: git
In-Reply-To: <Pine.LNX.4.21.0506171750180.30848-100000@iabervon.org>

Daniel,

Thanks for your feedback.

Daniel Barkalow <barkalow@iabervon.org> wrote:
> It might be worth making the system work for having multiple series in the
> same tree. You could do this by saving the base commit for
> .git/refs/heads/<name> as .git/refs/bases/<name>, and putting patches in
> .git/patches/<name>/. Some people do a lot with
> .git/refs/heads/something; I, at least, symlink .git/HEAD to whichever one
> I'm using, so that might be the right way to tell what the user is
> doing.

Having different series would be a good idea but it might complicate
the tool usage. I will thing about it once I'm sure the basic
operations work fine.

Before commenting further on your or Jon's e-mail, I want to clarify
how I think this tool should be used (this might be misunderstood if
someone never used quilt before). First of all, a StGIT patch is a
collection of git commits. This tool *doesn't* remove or modify git
changesets.

Quilt or StGIT are intended for people maintaining their own (big)
patch on top of the Linux kernel. I will take the example of Con
Kolivas' kernel. His big patch for the -ck kernel is at
http://ck.kolivas.org/patches/2.6/2.6.12-rc6/2.6.12-rc6-ck2/patch-2.6.12-rc6-ck2.diff.bz2.
This big patch is provided mainly for the convenience of the people
wanting to test this kernel. If you want to get deeper into this
patch, you can get it split in several smaller patches
(http://ck.kolivas.org/patches/2.6/2.6.12-rc6/2.6.12-rc6-ck2/patches/).
This set of patches is called a 'series'. You even get a 'series' file
which defines the order in which the patches should be applied.

These patches in a series represent changes to different files grouped
after some criteria. Let's say you have 2 schedulers, you keep them in
2 separate patches. You can modify a scheduler and refresh
(re-generate) its patch with quilt or simply do a 'stg commit' with
StGIT and the change is taken into account for the topmost patch.

Note that the series might remain the same for a long time but the
patches it contains can change. When you want to upgrade the series of
patches to a new kernel version, with quilt or StGIT you pop all the
patches from the stack, upgrade the base (i.e. the mainline kernel)
and push the patches back onto the stack. You can have some of the
patches rejected in quilt or get merge conflicts with StGIT.

While you can simply use quilt patches on top of a git repository,
there are advantages in using StGIT:

- easier integration with a git repository with common commands like
  diff etc.

- there aren't separate commands for adding/removing files to/from the
  git repository and the StGIT patch since a StGIT patch is simply
  represented as two commit ids - the base (bottom) and the top of the
  patch (those familiar with quilt know what it means to modify a file
  without adding it first to the topmost patch)

- every time you commit some changes (with 'stg commit'), this
  changeset is added to the topmost patch on the stack (by replacing
  the top id of the patch with the new commit id). If you want to
  modify a patch, simply push/pop until it becomes the topmost one,
  make the changes and commit. All the commit history is preserved
  (unlike quilt where you update the patch with a 'refresh'
  command). With a future StGIT release you will be able to see the
  log for individual StGIT patches

- pushing a patch to the stack when the base changed is done by
  merging with a diff3 algorithm. I find this slightly superior to a
  simple 'patch -p1 < file.diff' (well, some people do not agree with
  this point)

> I think it would worth exploring defining a git type for patches and
> storing the patches inside git as well. Then a commit could identify the
> patch it applies (when it is from applying a patch), and a rebased patch
> could reference the patch it replaces, and then (with a certain amount of
> handwaving of implementation) the system could notice when the patch
> you're pushing got applied upstream. Or, at least, git could avoid 
> throwing away the history information when it goes through patches. I keep
> thinking that this would be an important feature, but I haven't got the
> familiarity with quilt to know how it should work.

I don't know whether this is still valid after clarifying the intended
use of StGIT. When a patch was merged upstream, the local push
operation should detect that the patch being pushed doesn't change
anything and, at this point, you can safely delete it.

-- 
Catalin


^ permalink raw reply

* Re: Stacked GIT 0.1 (a.k.a. quilt for git)
From: Catalin Marinas @ 2005-06-18 21:43 UTC (permalink / raw)
  To: jon; +Cc: Daniel Barkalow, Git Mailing List
In-Reply-To: <2cfc4032050617152878b75c97@mail.gmail.com>

Jon Seymour <jon.seymour@gmail.com> wrote:
> I also think it would be good if patches extracted from git
> repositories included some information about exactly where the patch
> was extracted from...something like...
>
> signed-off-by: Name <user@host.domain>
> ---
> commit: sha1 -> sha1
> tree: sha1 -> sha1
>
> The reason for including the commits is to allow the maintainer to
> track exactly where the a given rev of a patch was from. The reason
> for including the treeids is to allow appliers to verify that the
> patch has produced the same result as the patch submitter.

See my (long) reply to Daniel. A StGIT patch is a collection of git
commits, mixed in time with commits for other patches. There might not
be a single author. For example, I create a patch called
'stabilisation' where I gather different git changesets from different
authors and commit them one by one.

I think what you mean is similar to the cg-mkpatch command. The 'stg
export' is totally different. While it might be possible to generate a
set of changesets for a StGIT patch, this is not intended for the near
future.

-- 
Catalin


^ permalink raw reply

* Re: cvs2git and file permissions
From: Sven Verdoolaege @ 2005-06-18 22:31 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0506181421070.2268@ppc970.osdl.org>

On Sat, Jun 18, 2005 at 02:23:48PM -0700, Linus Torvalds wrote:
> On Sat, 18 Jun 2005, Sven Verdoolaege wrote:
> > 
> > Let cvs checkout in a temporary directory rather than
> > using the pipe option to avoid loss of mode information.
> 
> Hmm.. Why do you use the "-N" flag?

I didn't quite know how to interpret the "as short as possible" in

              Use the -d dir option to create a directory  called
              dir  for  the  working  files, instead of using the
              module name.  Unless you also  use  -N,  the  paths
              created under dir will be as short as possible.

It would appear you do.

> Wouldn't it be much cleaner to _not_ create all those sub-directories 
> under ".git-tmp", and instead do something like
> 
> 	"cvs -q -d %s checkout -d .git-tmp -r%s '%s/%s'" ...
> 	"mv -f .git-tmp/%s %s\n", dir ? dir+1 : name, name
> 
> With that changed (and tested ;), I'll happily take it.

Seems to work.

The rm is still needed though.  Without it, cvs can get confused.

skimo
--
git-cvs2git: propagate mode information

Let cvs checkout in a temporary directory rather than
using the pipe option to avoid loss of mode information.

Signed-off-by: Sven Verdoolaege <skimo@liacs.nl>

---
commit 188ea2ee70413147fc1b80fedc3fbee02843e590
tree f122fcf591013aff299a4d072eed47255892d3a1
parent fdf95bf8d4d1182db579bd25fe5e25811084eaa6
author Sven Verdoolaege <skimo@kotnet.org> Sat, 18 Jun 2005 23:55:49 +0200
committer Sven Verdoolaege <skimo@kotnet.org> Sat, 18 Jun 2005 23:55:49 +0200

 cvs2git.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/cvs2git.c b/cvs2git.c
--- a/cvs2git.c
+++ b/cvs2git.c
@@ -199,7 +199,10 @@ static void update_file(char *line)
 	if (dir)
 		printf("mkdir -p %.*s\n", (int)(dir - name), name);
 
-	printf("cvs -q -d %s checkout -r%s -p '%s/%s' > '%s'\n", cvsroot, version, cvsmodule, name, name);
+	printf("cvs -q -d %s checkout -d .git-tmp -r%s '%s/%s'\n", 
+		cvsroot, version, cvsmodule, name);
+	printf("mv -f .git-tmp/%s %s\n", dir ? dir+1 : name, name);
+	printf("rm -rf .git-tmp\n");
 	printf("git-update-cache --add -- '%s'\n", name);
 }
 

^ permalink raw reply

* Kernel git tree problem? gitk problem?
From: Frank Sorenson @ 2005-06-19  0:12 UTC (permalink / raw)
  To: Git Mailing List

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

With the new crop of post-2.6.12 updates, my kernel trees (tested on
several computers, and even with brand-new tree) now give the following
error messages when I run gitk:
ERROR: none of the pending commits can be done yet:
  bd6ae2f6d61da0f90c6b66e9a4ab6c53ef8c159a
  2512809255d018744fe6c2f5e996c83769846c07
  88d7bd8cb9eb8d64bf7997600b0d64f7834047c5
  b3214970abbe983cd89842ae24ea00e21bba79f6

git-fsck-cache doesn't appear to know about any problems.

Is this a gitk problem, or is there something wrong with the tree?

What is a "pending commit?"  Is that a commit that hasn't been
committed, or something? :)

Thanks,

Frank
- --
Frank Sorenson - KD7TZK
Systems Manager, Computer Science Department
Brigham Young University
frank@tuxrocks.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFCtLh8aI0dwg4A47wRAscuAJ4ut87UDbYimUbfxmYT06e1AlNpMwCeIGrA
wgDmnq31zPOcPEZr67BOb4E=
=rdnC
-----END PGP SIGNATURE-----

^ permalink raw reply

* Re: git-rev-list: "--bisect" flag
From: Jon Seymour @ 2005-06-19  0:18 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0506172306210.2268@ppc970.osdl.org>

On 6/18/05, Linus Torvalds <torvalds@osdl.org> wrote:
> 
> Ok, I just added a feature to "git-rev-list" that I hereby nominate to be
> the coolest feature ever, namely the "--bisect" flag, which basically
> tries to split the list of revisions in two, and prints out just the "most
> middle" commit.
> 

Perhaps in answering this question, you can help me understand the
intended behaviour of your bisection algorithm a little better. The
question is this: for your purposes, given a rev-list output, why not
simply use the literal middle element of the outputed list?

jon.

^ permalink raw reply

* Re: git-rev-list: "--bisect" flag
From: Jan Harkes @ 2005-06-19  3:38 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <2cfc403205061817181e4d6d5e@mail.gmail.com>

On Sun, Jun 19, 2005 at 10:18:45AM +1000, Jon Seymour wrote:
> On 6/18/05, Linus Torvalds <torvalds@osdl.org> wrote:
> > 
> > Ok, I just added a feature to "git-rev-list" that I hereby nominate to be
> > the coolest feature ever, namely the "--bisect" flag, which basically
> > tries to split the list of revisions in two, and prints out just the "most
> > middle" commit.
> > 
> 
> Perhaps in answering this question, you can help me understand the
> intended behaviour of your bisection algorithm a little better. The
> question is this: for your purposes, given a rev-list output, why not
> simply use the literal middle element of the outputed list?

A was known good, parallel development for commits B and C, finally
merged into D. A bug got introduced in B, which we discover by the time
we have a checked out copy of D.

     .--- B ---.
    /           \
   A             D
    \           /
     `--- C ---'

git-rev-list E ^A shows this as BCD. Pick the halfway point C, and this
one checks out ok. So at this point we would decide that the bug got
introduced by the C->D change.

My guess is that Linus's bisect algorithm considers the branches, so
once C is cleared as good, we get a rev-list that looks like BD, and as
such can still find the exact bad commit B.

Jan

^ permalink raw reply

* Re: git-rev-list: "--bisect" flag
From: Linus Torvalds @ 2005-06-19  3:43 UTC (permalink / raw)
  To: jon; +Cc: Git Mailing List
In-Reply-To: <2cfc403205061817181e4d6d5e@mail.gmail.com>



On Sun, 19 Jun 2005, Jon Seymour wrote:
> 
> Perhaps in answering this question, you can help me understand the
> intended behaviour of your bisection algorithm a little better. The
> question is this: for your purposes, given a rev-list output, why not
> simply use the literal middle element of the outputed list?

The literal middle tends to be in the middle _timewise_, but that's
irrelevant. It might be just a single step away from one of the commits
that has already been marked "good", and as such, testing that one might
well be totally pointless: if you have a thousand commits (not at all
unlikely), testing whether they are good or not one at a time is just not
reasonable.

What we want to get is the commit that basically bisects the other commits 
from a "reachability graph" angle. We want something that when selected as 
the new "top" will have roughly half the commits in it.

Put another way: let's say that we have those thousand commits that _may_ 
have introduced a bug, but we don't know which one. What do we want? We 
want to find a new head that contains roughly five hundred of the unknown 
commits. No more, no less. Maybe we won't find a  500/500 split, but we 
definitely want a 600/400 split over a 1/999 split, since the 1/999 split 
isn't likely to get us any closer to the answer.

Now, I say "roughly", because there may not _be_ any commit that exactly 
bisects the case. For a trivial case, let's say that we know that 'A' is 
bad, and 'B' is good, but the graph in between them looks like this:


                   A
                 / |
                a  | 
                |  b
                |  | \
                c  d  e
                 \ | /
                   B

so the bug might have been introduced by any of a-e, and we just don't
know which one was the cause.

Now, selecting either 'a' or 'b' as the new tip is a good choice, 'a'
reaches two unknowns (a+c) while b reaches 3 unknowns (b+d+e) but the
others only reach a single unknown.

And notice how the two good bisection places were both _recent_. They were 
not in the middle "time-wise".

Also note that we do NOT want to reach the maximal amount of commits: we 
want to reach _half_ the commits. If we select a commit that reaches a lot 
of other unknown commits ('A' is the maximal here, of course), then we're 
not actually bisecting the thing at all: we're just testing close to a 
"known bad" case.

But let's say that 'c' didn't actually exist (or, alternatively, we have
tested that one and know that "B+c" is good, which basically makes 'c'
irrelevant for bug-hunting): in that case there is no good bisection,
since whichever of the remaining ones we choose (a,b,d or e) we'll always
have split it up so that we basically test the addition or subtraction of
just one single commit. There's not anything "--bisect" can do about that, 
and it will just happen to pick one of them. And that's what I mean
by "there may not be any commit that is nicely in the middle".

		Linus

^ permalink raw reply

* Re: git-rev-list: "--bisect" flag
From: Jon Seymour @ 2005-06-19  4:07 UTC (permalink / raw)
  To: Git Mailing List
In-Reply-To: <20050619033821.GB24982@delft.aura.cs.cmu.edu>

On 6/19/05, Jan Harkes <jaharkes@cs.cmu.edu> wrote:
> On Sun, Jun 19, 2005 at 10:18:45AM +1000, Jon Seymour wr
> A was known good, parallel development for commits B and C, finally
> merged into D. A bug got introduced in B, which we discover by the time
> we have a checked out copy of D.
> 
>      .--- B ---.
>     /           \
>    A             D
>     \           /
>      `--- C ---'
> 
> git-rev-list E ^A shows this as BCD. Pick the halfway point C, and this

I assume you mean git-rev-list D ^A

> one checks out ok. So at this point we would decide that the bug got
> introduced by the C->D change.

I think you misunderstood the intent of my original question which was
implicitly referring to the implementation of the bisection algorithm,
not to the bug blatt algorithm that makes use of the bisection
algorithm.

My actual question was: "Why is the bisection algorithm itself the way it is? "

A simple bisection algorithm which simply chooses the middle of the
git-rev-list output chooses a point which one could in principle argue
is as good as the one chosen by the current bisection algorithm.
However, I must admit that I don't quite understand the subtleties of
Linus' bisection algorithm, hence the question - why is his bisection
algorithm better than simply choosing the literal middle?

I'll post a patch that I have already sent to Linus which demonstrates
an implementation of --bisect that chooses the literal middle. This
patch also includes a demonstration of Linus' binary bug blatting
technique (see t/t6001-rev-list-merge-order.sh)

jon.

^ permalink raw reply

* [PATCH] An alternative implementation of --bisect
From: Jon Seymour @ 2005-06-19  4:08 UTC (permalink / raw)
  To: git; +Cc: jon.seymour, jaharkes


This patch contains a suggested alternative implementation of the
git-rev-list --bisect flag.

This patch outputs the literal middle element of the list that would
have been output if the --bisect flag had not been present.

It does so by maintaining a list with the following invariant -
for every two elements added to the tail of the list, one is 
removed from the head. The head of the remaining list when the 
traversal stops in the bisection point.

This patch includes a demonstration of Linus' binary bug blatting
idea in t/t6001-rev-list-merge-order.sh which also includes
some additional rev-list tests

Signed-off-by: Jon Seymour <jon.seymour@gmail.com>
---
Note: I included the additional tests from another patch I have
submitted already because the test framework included in that
patch is much easier to use. So, for writing new tests, I wanted
to use the newer framework.

Linus: please let me know if/when you want a new series of patches
which contain my generalisation of epoch.c into a commit graph
traversal facility.
---

 rev-list.c                      |   71 +++----
 t/t6001-rev-list-merge-order.sh |  392 +++++++++++++++++++++++++++++++++------
 2 files changed, 357 insertions(+), 106 deletions(-)

diff --git a/rev-list.c b/rev-list.c
--- a/rev-list.c
+++ b/rev-list.c
@@ -26,6 +26,9 @@ static int max_count = -1;
 static enum cmit_fmt commit_format = CMIT_FMT_RAW;
 static int merge_order = 0;
 static int show_breaks = 0;
+static struct commit_list * bisected = NULL;
+static struct commit_list ** bisected_tail = &bisected;
+static int added_since_removed=0;
 
 static void show_commit(struct commit *commit)
 {
@@ -79,12 +82,29 @@ static int process_commit(struct commit 
 		return CONTINUE;
 	}
 
-	show_commit(commit);
+	if (!bisect_list) {
+		show_commit(commit);
+	} else {
+		bisected_tail = &commit_list_insert(commit, bisected_tail)->next;
+		added_since_removed++;
+		if (added_since_removed == 2) {
+			pop_commit(&bisected); /* one remove for every two adds */
+			added_since_removed = 0;
+                }
+	}
 
 	return CONTINUE;
 }
 
-static void show_commit_list(struct commit_list *list)
+static void show_bisection()
+{
+	if (bisected) {
+		show_commit(pop_commit(&bisected));
+		free_commit_list(bisected);
+	}
+}
+
+static void process_commit_list(struct commit_list *list)
 {
 	while (list) {
 		struct commit *commit = pop_most_recent_commit(&list, SEEN);
@@ -149,46 +169,6 @@ static int count_distance(struct commit_
 	return nr;
 }
 
-static int clear_distance(struct commit_list *list)
-{
-	while (list) {
-		struct commit *commit = list->item;
-		commit->object.flags &= ~COUNTED;
-		list = list->next;
-	}
-}
-
-static struct commit_list *find_bisection(struct commit_list *list)
-{
-	int nr, closest;
-	struct commit_list *p, *best;
-
-	nr = 0;
-	p = list;
-	while (p) {
-		nr++;
-		p = p->next;
-	}
-	closest = 0;
-	best = list;
-
-	p = list;
-	while (p) {
-		int distance = count_distance(p);
-		clear_distance(list);
-		if (nr - distance < distance)
-			distance = nr - distance;
-		if (distance > closest) {
-			best = p;
-			closest = distance;
-		}
-		p = p->next;
-	}
-	if (best)
-		best->next = NULL;
-	return best;
-}
-
 struct commit_list *limit_list(struct commit_list *list)
 {
 	struct commit_list *newlist = NULL;
@@ -205,8 +185,6 @@ struct commit_list *limit_list(struct co
 		}
 		p = &commit_list_insert(commit, p)->next;
 	} while (list);
-	if (bisect_list)
-		newlist = find_bisection(newlist);
 	return newlist;
 }
 
@@ -296,12 +274,15 @@ int main(int argc, char **argv)
 	if (!merge_order) {		
 	        if (limited)
 			list = limit_list(list);
-		show_commit_list(list);
+		process_commit_list(list);
 	} else {
 		if (sort_list_in_merge_order(list, &process_commit)) {
 			  die("merge order sort failed\n");
 		}
 	}
+	if (bisect_list) {
+		show_bisection();
+	}
 
 	return 0;
 }
diff --git a/t/t6001-rev-list-merge-order.sh b/t/t6001-rev-list-merge-order.sh
old mode 100644
new mode 100755
--- a/t/t6001-rev-list-merge-order.sh
+++ b/t/t6001-rev-list-merge-order.sh
@@ -3,15 +3,110 @@
 # Copyright (c) 2005 Jon Seymour
 #
 
-test_description='Test rev-list --merge-order
-'
+test_description='Tests git-rev-list --merge-order functionality'
+
 . ./test-lib.sh
 
-function do_commit
+#
+# TODO: move the following block (upto --- end ...) into testlib.sh
+#
+[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+
+sed_script="";
+
+# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
+function tag
+{
+	_tag=$1
+	[ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
+	cat .git/refs/tags/$_tag
+}
+
+# Generate a commit using the text specified to make it unique and the tree
+# named by the tag specified.
+function unique_commit
+{
+	_text=$1
+        _tree=$2
+	shift 2
+    	echo $_text | git-commit-tree $(tag $_tree) "$@"
+}
+
+# Save the output of a command into the tag specified. Prepend
+# a substitution script for the tag onto the front of $sed_script
+function save_tag
+{
+	_tag=$1	
+	[ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+	shift 1
+    	"$@" >.git/refs/tags/$_tag
+    	sed_script="s/$(tag $_tag)/$_tag/g${sed_script+;}$sed_script"
+}
+
+# Replace unhelpful sha1 hashses with their symbolic equivalents 
+function entag
+{
+	sed "$sed_script"
+}
+
+# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
+# tag to a specified value. Restore the original value on return.
+function as_author
+{
+	_author=$1
+	shift 1
+        _save=$GIT_AUTHOR_EMAIL
+
+	export GIT_AUTHOR_EMAIL="$_author"
+	"$@"
+        export GIT_AUTHOR_EMAIL="$_save"
+}
+
+# Execute a command and suppress any error output.
+function hide_error
 {
-    git-commit-tree "$@" </dev/null
+	"$@" 2>/dev/null
 }
 
+function check_output
+{
+	_name=$1
+	shift 1
+	if "$@" | entag > $_name.actual
+	then
+		diff $_name.expected $_name.actual
+	else
+		return 1;
+	fi
+	
+}
+
+# Turn a reasonable test description into a reasonable test name.
+# All alphanums translated into -'s which are then compressed and stripped
+# from front and back.
+function name_from_description
+{
+        tr "'" '-' | tr '~`!@#$%^&*()_+={}[]|\;:"<>,/? ' '-' | tr -s '-' | tr '[A-Z]' '[a-z]' | sed "s/^-*//;s/-*\$//"
+}
+
+
+# Execute the test described by the first argument, by eval'ing
+# command line specified in the 2nd argument. Check the status code
+# is zero and that the output matches the stream read from 
+# stdin.
+function test_output_expect_success
+{	
+	_description=$1
+        _test=$2
+        [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
+        _name=$(echo $_description | name_from_description)
+	cat > $_name.expected
+	test_expect_success "$_description" "check_output $_name $_test" 
+}
+
+# --- end of stuff to move ---
+
+# test-case specific test function
 function check_adjacency
 {
     read previous
@@ -28,43 +123,77 @@ function check_adjacency
     done
 }
 
-function sed_script
-{
-   for c in root a0 a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 l0 l1 l2 l3 l4 l5
-   do
-       echo -n "s/${!c}/$c/;"
-   done
-}
 
 date >path0
 git-update-cache --add path0
-tree=$(git-write-tree)
-root=$(do_commit $tree 2>/dev/null)
-export GIT_COMMITTER_NAME=foobar  # to guarantee that the commit is different
-l0=$(do_commit $tree -p $root)
-l1=$(do_commit $tree -p $l0)
-l2=$(do_commit $tree -p $l1)
-a0=$(do_commit $tree -p $l2)
-a1=$(do_commit $tree -p $a0)
-export GIT_COMMITTER_NAME=foobar2 # to guarantee that the commit is different
-b1=$(do_commit $tree -p $a0)
-c1=$(do_commit $tree -p $b1)
-export GIT_COMMITTER_NAME=foobar3 # to guarantee that the commit is different
-b2=$(do_commit $tree -p $b1)
-b3=$(do_commit $tree -p $b2)
-c2=$(do_commit $tree -p $c1 -p $b2)
-c3=$(do_commit $tree -p $c2)
-a2=$(do_commit $tree -p $a1)
-a3=$(do_commit $tree -p $a2)
-b4=$(do_commit $tree -p $b3 -p $a3)
-a4=$(do_commit $tree -p $a3 -p $b4 -p $c3)
-l3=$(do_commit $tree -p $a4)
-l4=$(do_commit $tree -p $l3)
-l5=$(do_commit $tree -p $l4)
-echo $l5 > .git/HEAD
+save_tag tree git-write-tree
+hide_error save_tag root unique_commit root tree
+save_tag l0 unique_commit l0 tree -p root
+save_tag l1 unique_commit l1 tree -p l0
+save_tag l2 unique_commit l2 tree -p l1
+save_tag a0 unique_commit a0 tree -p l2
+save_tag a1 unique_commit a1 tree -p a0
+save_tag b1 unique_commit b1 tree -p a0
+save_tag c1 unique_commit c1 tree -p b1
+as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
+save_tag b3 unique_commit b2 tree -p b2
+save_tag c2 unique_commit c2 tree -p c1 -p b2
+save_tag c3 unique_commit c3 tree -p c2
+save_tag a2 unique_commit a2 tree -p a1
+save_tag a3 unique_commit a3 tree -p a2
+save_tag b4 unique_commit b4 tree -p b3 -p a3
+save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+save_tag l3 unique_commit l3 tree -p a4
+save_tag l4 unique_commit l4 tree -p l3
+save_tag l5 unique_commit l5 tree -p l4
+hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
+save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
+save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
+save_tag e3 as_author e@example.com unique_commit e3 tree -p e2
+save_tag f2 as_author f@example.com unique_commit f2 tree -p f1
+save_tag e4 as_author e@example.com unique_commit e4 tree -p e3 -p f2
+save_tag e5 as_author e@example.com unique_commit e5 tree -p e4
+save_tag f3 as_author f@example.com unique_commit f3 tree -p f2
+save_tag f4 as_author f@example.com unique_commit f4 tree -p f3
+save_tag e6 as_author e@example.com unique_commit e6 tree -p e5 -p f4
+save_tag f5 as_author f@example.com unique_commit f5 tree -p f4
+save_tag f6 as_author f@example.com unique_commit f6 tree -p f5 -p e6
+save_tag e7 as_author e@example.com unique_commit e7 tree -p e6
+save_tag e8 as_author e@example.com unique_commit e8 tree -p e7
+save_tag e9 as_author e@example.com unique_commit e9 tree -p e8
+save_tag f7 as_author f@example.com unique_commit f7 tree -p f6
+save_tag f8 as_author f@example.com unique_commit f8 tree -p f7
+save_tag f9 as_author f@example.com unique_commit f9 tree -p f8
+save_tag e10 as_author e@example.com unique_commit e1 tree -p e9 -p f8
+
+hide_error save_tag g0 unique_commit g0 tree
+save_tag g1 unique_commit g1 tree -p g0
+save_tag h1 unique_commit g2 tree -p g0
+save_tag g2 unique_commit g3 tree -p g1 -p h1
+save_tag h2 unique_commit g4 tree -p g2
+save_tag g3 unique_commit g5 tree -p g2
+save_tag g4 unique_commit g6 tree -p g3 -p h2
+
+tag l5 > .git/HEAD
+
+#
+# cd to t/trash and use 
+#
+#    git-rev-list ... 2>&1 | sed "$(cat sed.script)" 
+#
+# if you ever want to manually debug the operation of git-rev-list
+#
+echo $sed_script > sed.script
+
+test_expect_success 'rev-list has correct number of entries' 'git-rev-list HEAD | wc -l | tr -s " "' <<EOF
+19
+EOF
 
-git-rev-list --merge-order --show-breaks HEAD | sed "$(sed_script)" > actual-merge-order
-cat > expected-merge-order <<EOF
+normal_adjacency_count=$(git-rev-list HEAD | check_adjacency | grep -c "\^" | tr -d ' ')
+merge_order_adjacency_count=$(git-rev-list --merge-order HEAD | check_adjacency | grep -c "\^" | tr -d ' ')
+test_expect_success '--merge-order produces as many or fewer discontinuities' '[ $merge_order_adjacency_count -le $normal_adjacency_count ]'
+
+test_output_expect_success 'simple merge order' 'git-rev-list --merge-order --show-breaks HEAD' <<EOF
 = l5
 | l4
 | l3
@@ -86,15 +215,17 @@ cat > expected-merge-order <<EOF
 = root
 EOF
 
-git-rev-list HEAD | check_adjacency | sed "$(sed_script)" > actual-default-order
-normal_adjacency_count=$(git-rev-list HEAD | check_adjacency | grep -c "\^" | tr -d ' ')
-merge_order_adjacency_count=$(git-rev-list --merge-order HEAD | check_adjacency | grep -c "\^" | tr -d ' ')
-
-test_expect_success 'Testing that the rev-list has correct number of entries' '[ $(git-rev-list HEAD | wc -l) -eq 19 ]'
-test_expect_success 'Testing that --merge-order produces the correct result' 'diff expected-merge-order actual-merge-order'
-test_expect_success 'Testing that --merge-order produces as many or fewer discontinuities' '[ $merge_order_adjacency_count -le $normal_adjacency_count ]'
+test_output_expect_success 'two diamonds merge order (g6)' 'git-rev-list --merge-order --show-breaks g4' <<EOF
+= g4
+| h2
+^ g3
+= g2
+| h1
+^ g1
+= g0
+EOF
 
-cat > expected-merge-order-1 <<EOF
+test_output_expect_success 'multiple heads' 'git-rev-list --merge-order a3 b3 c3' <<EOF
 c3
 c2
 c1
@@ -111,10 +242,7 @@ l0
 root
 EOF
 
-git-rev-list --merge-order $a3 $b3 $c3 | sed "$(sed_script)" > actual-merge-order-1
-test_expect_success 'Testing multiple heads' 'diff expected-merge-order-1 actual-merge-order-1'
-
-cat > expected-merge-order-2 <<EOF
+test_output_expect_success 'multiple heads, prune at a1' 'git-rev-list --merge-order a3 b3 c3 ^a1' <<EOF
 c3
 c2
 c1
@@ -125,10 +253,7 @@ a3
 a2
 EOF
 
-git-rev-list --merge-order $a3 $b3 $c3 ^$a1 | sed "$(sed_script)" > actual-merge-order-2
-test_expect_success 'Testing stop' 'diff expected-merge-order-2 actual-merge-order-2'
-
-cat > expected-merge-order-3 <<EOF
+test_output_expect_success 'multiple heads, prune at l1' 'git-rev-list --merge-order a3 b3 c3 ^l1' <<EOF
 c3
 c2
 c1
@@ -142,10 +267,26 @@ a0
 l2
 EOF
 
-git-rev-list --merge-order $a3 $b3 $c3 ^$l1 | sed "$(sed_script)" > actual-merge-order-3
-test_expect_success 'Testing stop in linear epoch' 'diff expected-merge-order-3 actual-merge-order-3'
+test_output_expect_success 'cross-epoch, head at l5, prune at l1' 'git-rev-list --merge-order l5 ^l1' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+b3
+b2
+b1
+a3
+a2
+a1
+a0
+l2
+EOF
 
-cat > expected-merge-order-4 <<EOF
+test_output_expect_success 'duplicated head arguments' 'git-rev-list --merge-order l5 l5 ^l1' <<EOF
 l5
 l4
 l3
@@ -164,12 +305,141 @@ a0
 l2
 EOF
 
-git-rev-list --merge-order $l5 ^$l1 | sed "$(sed_script)" > actual-merge-order-4
-test_expect_success 'Testing start in linear epoch, stop after non-linear epoch' 'diff expected-merge-order-4 actual-merge-order-4'
+test_output_expect_success 'prune near merge' 'git-rev-list --merge-order a4 ^c3' <<EOF
+a4
+b4
+b3
+a3
+a2
+a1
+EOF
+
+#
+# note this test fails - has been fixed in a later version of Jon Seymour's HEAD
+#
+#test_output_expect_success "head has no parent" 'git-rev-list --merge-order --show-breaks root' <<EOF
+#= root
+#EOF
+
+test_output_expect_success "two nodes - one head, one base" 'git-rev-list --merge-order --show-breaks l0' <<EOF
+= l0
+= root
+EOF
+
+test_output_expect_success "three nodes one head, one internal, one base" 'git-rev-list --merge-order --show-breaks l1' <<EOF
+= l1
+| l0
+= root
+EOF
+
+test_output_expect_success "linear prune l2 ^root" 'git-rev-list --merge-order --show-breaks l2 ^root' <<EOF
+= l2
+| l1
+| l0
+EOF
+
+test_output_expect_success "linear prune l2 ^l0" 'git-rev-list --merge-order --show-breaks l2 ^l0' <<EOF
+= l2
+| l1
+EOF
+
+test_output_expect_success "linear prune l2 ^l1" 'git-rev-list --merge-order --show-breaks l2 ^l1' <<EOF
+= l2
+EOF
+
+test_output_expect_success "linear prune l5 ^a4" 'git-rev-list --merge-order --show-breaks l5 ^a4' <<EOF
+= l5
+| l4
+| l3
+EOF
+
+test_output_expect_success "linear prune l5 ^l3" 'git-rev-list --merge-order --show-breaks l5 ^l3' <<EOF
+= l5
+| l4
+EOF
+
+test_output_expect_success "linear prune l5 ^l4" 'git-rev-list --merge-order --show-breaks l5 ^l4' <<EOF
+= l5
+EOF
+
+#
+# the following illustrate's Linus' binary bug blatt idea. 
+#
+# assume the bug is actually at l3, but you don't know that - all you know is that l3 is broken
+# and it wasn't broken before
+#
+# keep bisecting the list, advancing the "bad" head and accumulating "good" heads until
+# the bisection point is the head - this is the bad point.
+#
+
+test_output_expect_success "--bisect l5" 'git-rev-list --bisect l5' <<EOF
+a2
+EOF
+
+test_output_expect_success "--bisect l5 ^a2" 'git-rev-list --bisect l5 ^a2' <<EOF
+a3
+EOF
 
-git-rev-list --merge-order $l5 $l5 ^$l1 2>/dev/null | sed "$(sed_script)" > actual-merge-order-5
-test_expect_success 'Testing duplicated start arguments' 'diff expected-merge-order-4 actual-merge-order-5'
+test_output_expect_success "--bisect l5 ^a2 ^a3" 'git-rev-list --bisect l5 ^a2 ^a3' <<EOF
+b4
+EOF
 
-test_expect_success 'Testing exclusion near merge' 'git-rev-list --merge-order $a4 ^$c3 2>/dev/null'
+test_output_expect_success "--bisect l5 ^a2 ^a3 ^b4" 'git-rev-list --bisect l5 ^a2 ^a3 ^b4' <<EOF
+a4
+EOF
 
+test_output_expect_success "--bisect l5 ^a2 ^a3 ^b4 ^a4" 'git-rev-list --bisect l5 ^a2 ^a3 ^a4' <<EOF
+l4
+EOF
+
+#
+# if l3 is bad, then l4 is bad too - so advance the bad pointer by making b4 the known bad head
+#
+
+test_output_expect_success "--bisect l4 ^a2 ^a3 ^b ^a4" 'git-rev-list --bisect l4 ^a2 ^a3 ^a4' <<EOF
+l3
+EOF
+
+test_output_expect_success "--bisect l3 ^a2 ^a3 ^b ^a4" 'git-rev-list --bisect l3 ^a2 ^a3 ^a4' <<EOF
+l3
+EOF
+
+# found!
+
+#
+# as another example, let's consider a4 to be the bad head, in which case
+#
+
+test_output_expect_success "--bisect a4 ^a2 ^a3 ^b4" 'git-rev-list --bisect a4 ^a2 ^a3 ^b4' <<EOF
+c2
+EOF
+
+test_output_expect_success "--bisect a4 ^a2 ^a3 ^b4 ^c2" 'git-rev-list --bisect a4 ^a2 ^a3 ^b4 ^c2' <<EOF
+c3
+EOF
+
+test_output_expect_success "--bisect a4 ^a2 ^a3 ^b4 ^c2 ^c3" 'git-rev-list --bisect a4 ^a2 ^a3 ^b4 ^c2 ^c3' <<EOF
+a4
+EOF
+
+# found!
+
+#
+# or consider c3 to be the bad head
+#
+
+test_output_expect_success "--bisect a4 ^a2 ^a3 ^b4" 'git-rev-list --bisect a4 ^a2 ^a3 ^b4' <<EOF
+c2
+EOF
+
+test_output_expect_success "--bisect c3 ^a2 ^a3 ^b4 ^c2" 'git-rev-list --bisect c3 ^a2 ^a3 ^b4 ^c2' <<EOF
+c3
+EOF
+
+# found!
+
+
+test_expect_failure "all heads uninteresting" 'git-rev-list --merge-order --show-breaks a3 ^a3'
+#
+#
 test_done
------------

^ permalink raw reply

* Re: Stacked GIT 0.1 (a.k.a. quilt for git)
From: Daniel Barkalow @ 2005-06-19  4:26 UTC (permalink / raw)
  To: Catalin Marinas; +Cc: git
In-Reply-To: <tnxis0b1h7g.fsf@arm.com>

On Sat, 18 Jun 2005, Catalin Marinas wrote:

> Having different series would be a good idea but it might complicate
> the tool usage. I will thing about it once I'm sure the basic
> operations work fine.

You could future-proof yourself a bit by simply saving the base as
.git/refs/bases/master and making the patches be
.git/patches/master/. That way, you'd be ready when you start to support
multiple series.

> Before commenting further on your or Jon's e-mail, I want to clarify
> how I think this tool should be used (this might be misunderstood if
> someone never used quilt before). First of all, a StGIT patch is a
> collection of git commits. This tool *doesn't* remove or modify git
> changesets.

(snip)

Okay; this fits what I thought quilt and StGIT did. 

An aside:

> - pushing a patch to the stack when the base changed is done by
>   merging with a diff3 algorithm. I find this slightly superior to a
>   simple 'patch -p1 < file.diff' (well, some people do not agree with
>   this point)

diff3 is clearly superior to patch in how effective it is; the
disagreement is only over how useful the output format is. But diff3 gets
all the information that patch gets, plus more, so it would have to be
buggy if it did worse, aside from by patch getting lucky. (Also, patch can
be used in situations where diff3 couldn't, due to having no known common
ancestor). IIRC, diff3 can generate .rej files if you tell it to, rather
than reporting conflicts inline.

> I don't know whether this is still valid after clarifying the intended
> use of StGIT. When a patch was merged upstream, the local push
> operation should detect that the patch being pushed doesn't change
> anything and, at this point, you can safely delete it.

If you're lucky, it will generate only a warning that changes are present
in both trees, the push will have no effect, and StGIT can determine that
the rebased patch is now empty. But consider this situation:

You have three patches stacked on a base.

The mainline takes a patch from somewhere else, then the first of your
patches, then a second patch from somewhere else. The last of these is
based on your first patch (or on mainline after your patch went in).

Then you update.

When you try to push your first patch, merge sees the common base, your
change, and the current mainline. There is no way to tell, from the
information available, that the correct merge is to ignore your change in
favor of mainline's version; the program only sees that there were two
different changes to the same area. The information which would resolve
this were patches not used would be that there was a mainline commit that
merged your first patch; the left side of the merge is an ancestor of the
right side, so the merge is the right side. My goal is to get the same
information into the system so it can reach the same conclusion when
patches are used.

Note that it gets more complex if mainline takes only your second patch
(due to it not requiring your first, and your first not being as
acceptable). In this case, it needs to entire mainline as a patch, because
merges can't cherrypick, whereas patches can act arbitrarily. But it would
be nice to store the information of what happened even with patches that
cherrypick, such that we have a better chance for managing things later.

The above situation is not actually particular to StGIT or quilt; any case
where something gets exported as a patch and applied to a different base
has this problem. But I think that you have the right ideas about how
patches should be represented, and it would be good to get your
representation implemented inside git, because operations more central to
git would benefit from having this information.

	-Daniel
*This .sig left intentionally blank*


^ permalink raw reply

* Re: git-rev-list: "--bisect" flag
From: Linus Torvalds @ 2005-06-19  5:03 UTC (permalink / raw)
  To: jon; +Cc: Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0506182022130.2268@ppc970.osdl.org>



On Sat, 18 Jun 2005, Linus Torvalds wrote:
> 
> Now, I say "roughly", because there may not _be_ any commit that exactly 
> bisects the case. For a trivial case, let's say that we know that 'A' is 
> bad, and 'B' is good, but the graph in between them looks like this:

Let's make a different case, just to make it even more obvious.

Let's say that the graph is


        A
       / \
      a1  b1
      |   |
      a2  b2
      |   |
      a3  b3
      |   |
      a4  b4
      |   |
      a5  b5
      |   |
      a6  b6
      |   |
      a7  b7
      |   |
      a8  b8
      |   |
      a9  b9
       \ /
        B

and we know that "A" is bad, and "B" is good, but we don't know which of 
a1-a9/b1-b9 are buggy.

Where do we start testing?

We start testing at either 'a1' or 'b1', because those are the two values 
that bisect the list either into the "a1-a9" series, or the "b1-b9" 
series. Any other starting point would be a bug.

Or, let's say that the graph is

        A
       / \
      |   b1
      |   |
      |   b2
      |   |
      |   b3
      |   |
      a1  b4
      |   |
      a2  b5
      |   |
      a3  b6
      |   |
      |   b7
      |   |
      |   b8
      |   |
      |   b9
       \ /
        B


and in this case the right place to pick is 'b4', because that's the one
that reaches 6 commits (b4-b9), while it leaves 6 commits unreachable
(a1-a3, b1-b3): that's a perfect bisection, and now it's totally 
unambiguous (in the previous case a1 and b1 were equally good choices, in 
this case there is only one valid choice).

It gets more interesting when you have intermediate merges:

        A
       / \
      |   b1
      |   |
      |   b2
      |   |
      |   b3
      |   |
      a1  b4
      |   |
      a2  b5
      | / |
      a3  b6
      |   |
      |   b7
      |   |
      |   b8
      |   |
      |   b9
       \ /
        B

The above graph _looks_ very similar, but now the right place to bisect is
'b5', because that reaches six commits (a3 + b5-b9), and again there are
six commits unreachable (a1-a2 + b1-b4). Again, this is unambiguous -
there is one clearly superior choice.

The current --bisect algorithm may not always pick that best choice: I
think I should subtract one from the total number of commits, since right
now it counts the "good" commit too, ie 'A'. But I think it's at most
off-by-one.

The bigger problem is that the algorithm is something like O(N^3) in cost
- albeit with a fairly cheap constant factor. In other words, it might not
be worthwhile bisecting three years worth of development with it, and you
migth be better off starting with a rougher half-way-point algorithm
("let's try some release a year and a half ago first").

The performance problem seems to really be pretty theoretical: I can
bisect the developemnt from 2.6.12-rc2 to current head in 0.59 seconds, so
it's not like it's horribly slow for something like a couple of months
worth of development.

			Linus

^ permalink raw reply

* Re: git-rev-list: "--bisect" flag
From: David Lang @ 2005-06-19  5:05 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: jon, Git Mailing List
In-Reply-To: <Pine.LNX.4.58.0506182141400.2268@ppc970.osdl.org>

On Sat, 18 Jun 2005, Linus Torvalds wrote:

> The performance problem seems to really be pretty theoretical: I can
> bisect the developemnt from 2.6.12-rc2 to current head in 0.59 seconds, so
> it's not like it's horribly slow for something like a couple of months
> worth of development.

if it takes you about as long to type the command (and scan it to make 
sure you didn't mistype it) as it does to execute you don't have a 
performance problem :-)

David Lang

-- 
There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.
  -- C.A.R. Hoare

^ permalink raw reply

* Re: git-rev-list: "--bisect" flag
From: Linus Torvalds @ 2005-06-19  5:30 UTC (permalink / raw)
  To: David Lang; +Cc: jon, Git Mailing List
In-Reply-To: <Pine.LNX.4.62.0506182204350.11617@qynat.qvtvafvgr.pbz>



On Sat, 18 Jun 2005, David Lang wrote:
> 
> if it takes you about as long to type the command (and scan it to make 
> sure you didn't mistype it) as it does to execute you don't have a 
> performance problem :-)

Yeah, well, in all honesty, I haven't actually verified that my bisection 
gives the right answer.

I verified "visually" that it does something half-way sane with this:

  git-rev-list --bisect HEAD ^v2.6.12-rc2 > .git/refs/tags/bisect-1
  git-rev-list --bisect HEAD ^v2.6.12-rc2 ^bisect-1 > .git/refs/tags/bisect-2
  git-rev-list --bisect HEAD ^v2.6.12-rc2 ^bisect-1 ^bisect-2 > .git/refs/tags/bisect-3
  git-rev-list --bisect HEAD ^v2.6.12-rc2 ^bisect-1 ^bisect-2 ^bisect-3 > .git/refs/tags/bisect-4
  git-rev-list --bisect HEAD ^v2.6.12-rc2 ^bisect-1 ^bisect-2 ^bisect-3 ^bisect-4 > .git/refs/tags/bisect-5
  gitk

and it looked sane (ie "bisect-1" should show up as a tag about half-way, 
and the others should get progressively closer to the HEAD). But when I 
now tried it again (I did the above originally with plain 2.6.12), I get 
errors from "gitk":

	ERROR: none of the pending commits can be done yet:
	  bd6ae2f6d61da0f90c6b66e9a4ab6c53ef8c159a
	  2512809255d018744fe6c2f5e996c83769846c07
	  88d7bd8cb9eb8d64bf7997600b0d64f7834047c5
	  b3214970abbe983cd89842ae24ea00e21bba79f6

which looks like the current kernel overflows gitk some way (there's 
suddenly a lot more parallellism, since there were a number of merges that 
were pending, waiting for the 2.6.12 release).

		Linus

^ 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