* More gitweb queries.. @ 2005-05-27 19:24 Linus Torvalds 2005-05-27 19:29 ` Thomas Glanzmann ` (5 more replies) 0 siblings, 6 replies; 42+ messages in thread From: Linus Torvalds @ 2005-05-27 19:24 UTC (permalink / raw) To: Kay Sievers, Git Mailing List Kay, just a few more quickie suggestions if you don't mind.. - looking around, the ALSA guys aren't the only ones that start off with an empty line, so it's probably worth fixing the summary etc to ignore whitespace at the beginning rather than give empty summary reasons. - any reason to limit the "summary" page to just the last 14 changes? The "log" thing you can ask to go back further, it would be nice to have something like a "last 100" thing for summaries too, especially since the summary is so nice and dense, so you can actually get a nice view of what has happened without scrolling _too_ much. - I was in the "commitdiff" thing, and initially thought that there was no way to get back to the "summary" view. It turns out I was wrong (the summary is reachable by just clicking at the project name itself in the top header), but it's a bit strange that the "commitdiff" thing has an explicit link back to itself (hey, consistency is good, so I'm not complaining), but the link back to the summary page is implicit. So how about adding an explicit "summary" link to the list of other explicit links (log, commit, commitdiff and tree) at the top of the page? I actually like browsing other peoples projects with the gitweb interfaces, it's both responsive and verbose enough to really say something good. In contrast, cvsweb is always just a mess of "these are the files, go at it", which is totally pointless and doesn't tell anything about what is actually happening in the project. So dammit, I'm very biased indeed, but I'm just looking at gitweb, and comparing it to both the CVS and SVN web things, and they just reinforce my conviction that CVS is absolute crap, and I find myself surprised by how crap SVN also appears. [ In other words, while I have all these stupid requests for you, I think gitweb is just _way_ better and more useful than something like viewcvs (or cvsweb). So don't mind my small gripes, I only have them because I like this thing. On that small note, I also find "gitk" very cool indeed, too bad about the fact that tk/tcl seems to always end up looking so _ugly_. Is there any way to get anti-aliased fonts and a less 'Motify' blocky look from tcl/tk? Every time I see that, I feel like I'm back in the last century or something. Combining some of the features of the two (that über-cool revision history graph from gitk rules, for example) might be cool. I get the urge to do octopus-merges in the kernel just because of how good they look in gitk ;) ] Linus ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:24 More gitweb queries Linus Torvalds @ 2005-05-27 19:29 ` Thomas Glanzmann 2005-05-27 19:52 ` Junio C Hamano ` (2 more replies) 2005-05-27 19:31 ` Thomas Glanzmann ` (4 subsequent siblings) 5 siblings, 3 replies; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-27 19:29 UTC (permalink / raw) To: Linus Torvalds; +Cc: Kay Sievers, Git Mailing List Hello, > I get the urge to do octopus-merges in the kernel just because of how > good they look in gitk ;) ] talking about octopus-merges ... I don't understand how they work. What happens if one file is touched in every of the 8 trees. How can that be handled? Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:29 ` Thomas Glanzmann @ 2005-05-27 19:52 ` Junio C Hamano 2005-05-27 19:55 ` Thomas Glanzmann 2005-05-27 19:54 ` Junio C Hamano 2005-05-27 20:03 ` Linus Torvalds 2 siblings, 1 reply; 42+ messages in thread From: Junio C Hamano @ 2005-05-27 19:52 UTC (permalink / raw) To: Linus Torvalds; +Cc: Kay Sievers, Git Mailing List >>>>> "TG" == Thomas Glanzmann <sithglan@stud.uni-erlangen.de> writes: TG> talking about octopus-merges ... I don't understand how they work. What TG> happens if one file is touched in every of the 8 trees. How can that be TG> handled? You merge by hand and resolve if they have conflicts, just like what you already do in two head merge case. Octopus is only about how you record the results. Instead of making 7 consecutive "merge from A" "merge from B" to record two head merges, you just say "I merged these 8 heads" in a single commit. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:52 ` Junio C Hamano @ 2005-05-27 19:55 ` Thomas Glanzmann 2005-05-27 20:13 ` Junio C Hamano 2005-05-27 20:17 ` Linus Torvalds 0 siblings, 2 replies; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-27 19:55 UTC (permalink / raw) To: Junio C Hamano; +Cc: Linus Torvalds, Kay Sievers, Git Mailing List Hello, > You merge by hand and resolve if they have conflicts, just like > what you already do in two head merge case. I see. Does that mean that 'git-ls-files --unmerged' will report upto 9 stages per file? > Octopus is only about how you record the results. Instead of > making 7 consecutive "merge from A" "merge from B" to record two > head merges, you just say "I merged these 8 heads" in a single > commit. I got that part. :-) Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:55 ` Thomas Glanzmann @ 2005-05-27 20:13 ` Junio C Hamano 2005-05-27 20:32 ` Thomas Glanzmann 2005-05-27 20:17 ` Linus Torvalds 1 sibling, 1 reply; 42+ messages in thread From: Junio C Hamano @ 2005-05-27 20:13 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Git Mailing List >>>>> "TG" == Thomas Glanzmann <sithglan@stud.uni-erlangen.de> writes: >> You merge by hand and resolve if they have conflicts, just like >> what you already do in two head merge case. TG> I see. Does that mean that 'git-ls-files --unmerged' will report upto 9 TG> stages per file? No, I think my description was unclear. You still merge two at a time because that is what git-read-tree -m gives you (3-way merge is between $(merge-base $A $B) and $A and $B so you are merging two heads). To confess, my workflow to merge with Linus is currently primarily patch based, so I do not even use git-read-tree -m 3-way merge when I make an Octopus (for that matter, I do not myself do Octopus at all these days). When I have bunch of independent changes, I would first prepare and test these: -- JC#1 / - JC#2 / - JC#3 Linus#1- - JC#4 \ ... \-- JC#7 By the time I am done and happy with them, tip of Linus tree may have already advanced and he is at Linus#2. I would then apply diffs between Linus#1 and JC#n (1 <= n <= 7) on top of Linus #2, and commit the result with parents set to Linus #2 and JC#1, JC#2, ..., JC#7. ------- Linus#2 / \ / -- JC#1 --------\ / /-- JC#2 ---------\ / /--- JC#3 ----------\ Linus#1---- JC#4 ---------- Octopus \ ... / \-- JC#7 ---------- ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 20:13 ` Junio C Hamano @ 2005-05-27 20:32 ` Thomas Glanzmann 2005-05-27 20:40 ` Junio C Hamano 2005-05-29 23:02 ` Thomas Glanzmann 0 siblings, 2 replies; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-27 20:32 UTC (permalink / raw) To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List Hello, okay thanks for the elaboration on the topic. I will now adopt my scripts to handle it. I think I already have a use for it. -- mutt-hcache -- /-- mutt-imap --\ /--- mutt-whatever ---\ mutt-cvs ---- ... ----- mutt-tg (my working tree) \ ... ----/ \-- ... -/ Actually, I have already 12 trees with different features which I work on. 1 mutt-attach-file 5 mutt-hcache 9 mutt-menu-move 2 mutt-collapse-flags 6 mutt-headers 10 mutt-move-hook 3 mutt-cstatus 7 mutt-imap 11 mutt-setenv-hack 4 mutt-edit-threads 8 mutt-maildir-mtime 12 mutt-thread-pattern But I guess 8 is the limit, isn't it? Did you thought to make this 8 a 'n' or is 8 just enough? :-) Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 20:32 ` Thomas Glanzmann @ 2005-05-27 20:40 ` Junio C Hamano 2005-05-27 22:00 ` Linus Torvalds 2005-05-29 23:02 ` Thomas Glanzmann 1 sibling, 1 reply; 42+ messages in thread From: Junio C Hamano @ 2005-05-27 20:40 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Linus Torvalds, Git Mailing List >>>>> "TG" == Thomas Glanzmann <sithglan@stud.uni-erlangen.de> writes: TG> But I guess 8 is the limit, isn't it? Did you thought to make this 8 a TG> 'n' or is 8 just enough? :-) Built-in limit of commit object is 16, not 8. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 20:40 ` Junio C Hamano @ 2005-05-27 22:00 ` Linus Torvalds 2005-05-27 22:04 ` Thomas Glanzmann 2005-05-28 2:26 ` Junio C Hamano 0 siblings, 2 replies; 42+ messages in thread From: Linus Torvalds @ 2005-05-27 22:00 UTC (permalink / raw) To: Junio C Hamano; +Cc: Thomas Glanzmann, Git Mailing List On Fri, 27 May 2005, Junio C Hamano wrote: > > >>>>> "TG" == Thomas Glanzmann <sithglan@stud.uni-erlangen.de> writes: > > TG> But I guess 8 is the limit, isn't it? Did you thought to make this 8 a > TG> 'n' or is 8 just enough? :-) > > Built-in limit of commit object is 16, not 8. Actually, even that is not actually built into the commit object itself, that's just a #define in commit-tree.c. Change the MAXPARENT design from 16 to 1024, and nobody will notice any difference at all, except "git-commit-tree.c" will use 20kB more memory ;) There's no limit in the data structures, although there clearly is a "sanity" limit (and I personally suspect it comes before you hit 16 ;) Linus ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 22:00 ` Linus Torvalds @ 2005-05-27 22:04 ` Thomas Glanzmann 2005-05-28 2:26 ` Junio C Hamano 1 sibling, 0 replies; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-27 22:04 UTC (permalink / raw) To: Linus Torvalds; +Cc: Git Mailing List Hello, > Actually, even that is not actually built into the commit object > itself, that's just a #define in commit-tree.c. Change the MAXPARENT > design from 16 to 1024, and nobody will notice any difference at all, > except "git-commit-tree.c" will use 20kB more memory ;) That sounds just way to perfect. :-) > There's no limit in the data structures, although there clearly is a > "sanity" limit (and I personally suspect it comes before you hit 16 ;) I like how this 'simple' git concepts just fits into all this usage scenarios. Including this one or the way we can track renames. Name it! Thanks for giving us this perfect piece of software! :-) Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 22:00 ` Linus Torvalds 2005-05-27 22:04 ` Thomas Glanzmann @ 2005-05-28 2:26 ` Junio C Hamano 1 sibling, 0 replies; 42+ messages in thread From: Junio C Hamano @ 2005-05-28 2:26 UTC (permalink / raw) To: Linus Torvalds; +Cc: Thomas Glanzmann, Git Mailing List >>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes: LT> There's no limit in the data structures, although there clearly is a LT> "sanity" limit (and I personally suspect it comes before you hit 16 ;) I know that my head would start hurting way before I hit 16. I probably shouldn't have coined the word Octopus in the first place, giving people a false impression that somehow 8 is a magic number. To begin with, what I inflicted on you was not even an Octopus but a Pentapus, merge of 5 IIRC. Also we are counting heads, not legs. Should have said Hydra, but I do not offhand know how many heads it has --- I've never met one. I know King Ghidorah has 3 heads ;-). ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 20:32 ` Thomas Glanzmann 2005-05-27 20:40 ` Junio C Hamano @ 2005-05-29 23:02 ` Thomas Glanzmann 2005-05-29 23:10 ` Thomas Glanzmann 1 sibling, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-29 23:02 UTC (permalink / raw) To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List [-- Attachment #1: Type: text/plain, Size: 4310 bytes --] Hello, here we go! gitk screenshot: http://wwwcip.informatik.uni-erlangen.de/~sithglan/shot.png (64k) (faui00u) [~/work/mutt/git/mutt-test] ../../../git/yagf/git pull ../mutt-attach-file/ ../mutt-collapse-flags/ ../mutt-cstatus/ ../mutt-cvs/ ../mutt-edit-threads/ ../mutt-hcache/ ../mutt-headers/ ../mutt-imap/ ../mutt-maildir-mtime/ ../mutt-move-hook/ ../mutt-setenv-hack/ ../mutt-thread-pattern/ ../mutt-menu-move/ <chatty output> (faui00u) [~/work/mutt/git/mutt-test] git treediff ../mutt-tg-solaris (faui00u) [~/work/mutt/git/mutt-test] git changes -m | perl -pe '$a += /^diff-tree/; exit if $a==2' diff-tree 5b22da9792b6f6a968dc0a916275d6c301575f75 (from e8f4a291a81f0a8fb24555f0e36e4b75e2d3f4c8) Author: Thomas Glanzmann <sithglan@stud.uni-erlangen.de> Date: Mon May 30 00:28:53 2005 +0200 => faui00u:/home/cip/adm/sithglan/work/mutt/git/mutt-test <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-attach-file/.git (bringing head ahead) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-collapse-flags/.git (automatic merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-cstatus/.git (threeway merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-cvs/.git (nothing to merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-edit-threads/.git (threeway merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-hcache/.git (threeway merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-headers/.git (threeway merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-imap/.git (automatic merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-maildir-mtime/.git (threeway merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-move-hook/.git (nothing to merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-setenv-hack/.git (automatic merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-thread-pattern/.git (threeway merge) <= /home/cip/adm/sithglan/work/mutt/git/mutt-test/../mutt-menu-move/.git (threeway merge) Could please someone who has a clue how this merge should work (which it hopefully already does) look at my code to check for obvious mistakes in the merge *logic*. I call 'merge' with all involved trees (with current head first, if there is a current head). If everything is fine. I would like to write a seperate git-resolve script in sh or adopt the current to multi head merge - whatever you please. sub merge { my $message = undef; my $head = undef; my $last_tree = undef; my $fh; my @heads = (); foreach my $r (@_) { my $current_head = @{$r}[0]; my $current_url = @{$r}[1]; print "current_head => $current_head\ncurrent_url => $current_url\n"; push(@heads, '-p', ${current_head}); if (! defined($last_tree)) { $message = "=> ${current_url}\n"; $head = $current_head; $last_tree = $current_head; if (@_ == 1) { head($head); return; } next; } my $merge_base = gitcmdout('git-merge-base', $head, $current_head) || die ("no merge-base"); chomp($merge_base); print "head => $head\nremote => $current_head\nbase => $merge_base\n"; if ($merge_base eq $current_head) { $message .= "<= ${current_url} (nothing to merge)\n"; if (@_ == 2) { return; } next; } if ($merge_base eq $head) { $message .= "<= ${current_url} (bringing head ahead)\n"; $head = ${current_head}; $last_tree = ${current_head}; if (@_ == 2) { head($head); return; } next; } gitcmd('git-read-tree', '-m', $merge_base, $last_tree, $current_head); if (! defined($last_tree = write_tree())) { system('git-merge-cache', '-o', 'git-merge-one-file-script', '-a'); if (! defined($last_tree = write_tree())) { # FIXME: Make manual intervention possible # --tg 23:11 05-05-29 die("Couldn't merge automatically: Call 'git resolve'"); } $message .= "<= ${current_url} (threeway merge)\n"; } else { $message .= "<= ${current_url} (automatic merge)\n"; } } open($fh, "+>", undef); print $fh $message; seek($fh, 0, 0); $head = gitcmdinout($fh, 'git-commit-tree', $last_tree, @heads); chomp($head); close $fh; head($head); return; } Thomas [-- Attachment #2: git --] [-- Type: text/plain, Size: 25336 bytes --] #!/usr/bin/env perl use strict; use warnings; use IO::Handle; use File::Temp qw/ tempfile tempdir /; use File::Copy; use Cwd; use Getopt::Long; STDOUT->autoflush(1); my $hostname = gitcmdout('hostname'); chomp($hostname); my $DIFF = undef; my $PATCH = undef; if (-x '/opt/csw/bin/gdiff') { $DIFF='/opt/csw/bin/gdiff'; } else { $DIFF='diff'; } if (-x '/usr/bin/gpatch') { $PATCH='/usr/bin/gpatch'; } else { $PATCH='patch'; } my %commands = ( "add" => \&add, "checkout" => \&checkout, "ci" => \&ci, "clone" => \&clone, "commit" => \&commit, "diff" => \&diff, "treediff" => \&treediff, "dirty" => \&dirty, "help" => \&usage, "init-db" => \&init_db, "log" => \&log, "orphan" => \&orphan, "parent" => \&parent, "patch" => \&patch, "pull" => \&pull, "push" => \&_push, "refresh" => \&refresh, "revert" => \&revert, "rm" => \&rm, "setup" => \&setup, "status" => \&status, "undo" => \&undo, "changed" => \&changed, "resolve" => \&resolve, "changes" => \&changes, ); my %touched = (); sub usage { print STDERR <<"__EOF__"; Usage: $0 COMMAND [ARG]... Available commands: __EOF__ print "\t" . join("\n\t", sort(keys(%commands))) . "\n\n"; return 1; } sub refresh { `git-update-cache --refresh`; } sub process_git_diff_output { my $str = shift || return (()); my @in = split("\0", $str); my @out = (); while (@in) { my @tmp = split(' ', shift(@in)); $tmp[0] =~ s/^://g; push(@tmp, shift(@in)); push(@out, [@tmp]); } return(@out); } sub dirty_files { refresh(); my @dirty = (); my $str = gitcmdout('git-diff-files', '-z', '-r'); foreach (process_git_diff_output($str)) { if ((@{$_})[1] ne '000000') { push(@dirty, @{$_}[5]); } #print "<" . join("> <", @{$_}) . ">\n"; } return @dirty; } sub orphan_files { my @orphan = gitcmdout('git-ls-files', '--others'); chomp(@orphan); my $regexp = '^\.'; if (-f '.git/ignore') { my @ignore = (); chomp (@ignore = _read_file('.git/ignore')); $regexp = join('|', '^\.', @ignore); } @orphan = grep(! /$regexp/, @orphan); return @orphan; } sub orphan { foreach (orphan_files()) { print $_ . "\n"; } } sub dirty { foreach (dirty_files()) { print $_ . "\n"; } } sub changed { foreach my $rev (gitcmdout('git-rev-list', 'HEAD')) { chomp($rev); my %hash = commit_hash($rev); my $str = gitcmdout('git-diff-tree', '--root', '-r', '-z', $rev); foreach (process_git_diff_output($str)) { my ($time, $rest) = split(/\s/, $hash{committer_date}); push(@{$touched{@{$_}[5]}}, $time); } } } sub status { my %hash = (); foreach (orphan_files()) { $hash{$_} = '?'; } foreach (dirty_files()) { $hash{$_} = 'D'; } my $str = gitcmdout('git-diff-cache', '-r', '--cached', '-z', 'HEAD'); foreach (process_git_diff_output($str)) { if (@{$_}[0] eq '000000') { $hash{@{$_}[5]} .= '+'; } elsif (@{$_}[1] eq '000000') { $hash{@{$_}[5]} .= '-'; } else { $hash{@{$_}[5]} .= '*'; } } foreach (sort(keys(%hash))) { if ($hash{$_} eq "D") { $hash{$_} = "D "; } printf("% 2s %s\n", $hash{$_}, $_); } } sub write_tree { chomp (my $tree = `git-write-tree`); if ($?) { return undef; } else { return $tree; } } sub retrieve_unmerged { my %hash = (); foreach my $line (gitcmdout('git-ls-files', '--unmerged')) { chomp($line); # 100644 cde27275fad8103084d7ed2d08d246ba4ce6eb9c 1 Makefile # 100644 d311a35e5e8a09629ea9e6051a43710c76fa8f6d 2 Makefile # 100644 ec2b76bf90fb105b2aaf00a66f44b135046d3002 3 Makefile if ($line =~ /^(\d{6})\s([a-z0-9]{40})\s(\d)\s(.+)$/) { push(@{$hash{$4}}, $line); } else { die("Can't match: <$line>\n"); } } return %hash; } sub process_unmerged_file { my $line; my $file; my @files = (); while($line = shift) { if ($line =~ /^(\d{6})\s([a-z0-9]{40})\s(\d)\s(.+)$/) { if ($3 eq '1') { $file = "$4.GCA"; } if ($3 eq '2') { $file = "$4.LOCAL"; } if ($3 eq '3') { $file = "$4.REMOTE"; } my $mode = substr($1, 2); if (-f $file) { die("Please get rid of $file\n"); } print STDERR "Checking out: $file with permissions $mode\n"; system("git-cat-file blob $2 > $file"); chmod oct($mode), $file; push(@files, $file); } } if (@files == 3) { my $filename = (@files)[0]; $filename =~ s#\.(GCA|LOCAL|REMOTE)$##; print STDERR <<"EOF"; You got GCA, LOCAL and REMOTE, so I run merge for you and leaving the merges in ${filename} .\n EOF unlink($filename); system('cp', "${filename}.LOCAL", "${filename}"); system('merge', "${filename}", "${filename}.GCA", "${filename}.REMOTE"); } print STDERR <<"EOF"; Please resolve the conflict and add the file using the command 'git ci file'. Droping you to a login shell now. Please exit the shell as soon as you resolved the conflict. EOF system($ENV{'SHELL'}, '--login'); foreach (@files) { unlink($_); } } sub resolve { if (! -f '.git/RESOLVE') { die("Nothing to resolve"); } if ( ! -f '.git/HEAD' && ! -l '.git/HEAD') { die("How the hell I am supposed to resolve without a head?"); } my $fh; my $head = head(); my $merge_tree = undef; my $pwd = getcwd; chomp (my ($remote_head, $url) = _read_file('.git/RESOLVE')); print "remote_head => $remote_head\nurl => $url\n"; my %unmerged = retrieve_unmerged(); foreach my $file (keys(%unmerged)) { process_unmerged_file(@{$unmerged{$file}}); } if (! defined($merge_tree = write_tree())) { die("Still unresolved conflicts. Run git resolve again."); } open($fh, "+>", undef); print $fh "Manual Merge $url => ${hostname}:${pwd}\n"; seek($fh, 0, 0); chomp($head = gitcmdinout($fh, 'git-commit-tree', $merge_tree, '-p', $head, '-p', $remote_head)); close $fh; head($head); unlink('.git/RESOLVE'); checkout('-f'); print STDERR "All issues resolved manual: Commited as ${head}.\n"; print STDERR "Have a pleasant day.\n"; return; } sub merge { my $message = undef; my $head = undef; my $last_tree = undef; my $fh; my @heads = (); foreach my $r (@_) { my $current_head = @{$r}[0]; my $current_url = @{$r}[1]; print "current_head => $current_head\ncurrent_url => $current_url\n"; push(@heads, '-p', ${current_head}); if (! defined($last_tree)) { $message = "=> ${current_url}\n"; $head = $current_head; $last_tree = $current_head; if (@_ == 1) { print "Setting HEAD\n"; head($head); return; } print "Next round\n"; next; } my $merge_base = gitcmdout('git-merge-base', $head, $current_head) || die ("no merge-base"); chomp($merge_base); print "head => $head\nremote => $current_head\nbase => $merge_base\n"; if ($merge_base eq $current_head) { $message .= "<= ${current_url} (nothing to merge)\n"; if (@_ == 2) { return; } next; } if ($merge_base eq $head) { $message .= "<= ${current_url} (bringing head ahead)\n"; $head = ${current_head}; $last_tree = ${current_head}; if (@_ == 2) { head($head); return; } next; } gitcmd('git-read-tree', '-m', $merge_base, $last_tree, $current_head); if (! defined($last_tree = write_tree())) { system('git-merge-cache', '-o', 'git-merge-one-file-script', '-a'); if (! defined($last_tree = write_tree())) { # FIXME: Make manual intervention possible # --tg 23:11 05-05-29 die("Couldn't merge automatically: Call 'git resolve'"); } $message .= "<= ${current_url} (threeway merge)\n"; } else { $message .= "<= ${current_url} (automatic merge)\n"; } } open($fh, "+>", undef); print $fh $message; seek($fh, 0, 0); $head = gitcmdinout($fh, 'git-commit-tree', $last_tree, @heads); chomp($head); close $fh; head($head); return; } sub patch { my @files = (); my ($fh, $patch); my $file = shift || die("Need at least on argument.\n"); my $head = undef; my $dir = undef; if (head() && ( dirty_files() || `git-diff-cache -r --cached HEAD`)) { print STDERR "Get rid of dirty files / uncommited deltas first.\n"; exit 1; } ($fh, $patch) = tempfile(CLEANUP => 1); `filterdiff -x '*/.*' $file > $patch`; @files = `lsdiff --strip 1 $patch`; chomp(@files); $dir = tempdir(CLEANUP => 1); $ENV{GIT_INDEX_FILE} = "$dir/.index"; if ($head = head()) { gitcmd('git-read-tree', $head); foreach my $file (@files) { gitcmd('git-checkout-cache', '-q', "--prefix=$dir/", $file); } } my $pwd = getcwd; symlink("$pwd/.git", "$dir/.git"); chdir($dir); # FIXME call in batch modus and check return value --tg 08:30 05-05-21 system("${PATCH} -p1 < $patch"); foreach my $file (@files) { gitcmd('git-update-cache', '--add', '--remove', $file); } commit(); chdir($pwd); delete($ENV{GIT_INDEX_FILE}); checkout('-f'); } sub checkout { my $head = head(); gitcmd('git-read-tree', '-m', $head); # FIXME if this fails call it without -m --tg 20:25 05-05-09 if (defined($_[0]) && $_[0] eq '-f') { gitcmd('git-checkout-cache', '-u', '-f', '-a'); } else { gitcmd('git-checkout-cache', '-u', '-q', '-a'); } # changed(); # # foreach my $file (gitcmdout('git-ls-files')) { # chomp($file); # my $time = (sort {$b <=> $a} @{$touched{$file}})[0]; # utime $time, $time, $file; # } } sub generate_url { my $url = shift; if (! defined($url)) { if ( -f '.git/PARENT' || -l '.git/PARENT') { chomp ($url = _read_file('.git/PARENT')); } else { die("$0: No URL specified and no parent found: Where to pull from?\n"); } } else { $url =~ s#\/$##; if (-d $url && ! ($url =~ /\.git$/)) { $url .= "/.git"; } if (-d $url && ! ($url =~ /^\//)) { # FIXME: use Cwd; my $pwd = getcwd; --tg 21:32 05-05-03 chomp(my $pwd = `pwd`); $url = "${pwd}/${url}"; } } return $url; } sub pull { my @references = (); my %options; local @ARGV = @_; GetOptions(\%options, 'o', 'l'); @_ = @ARGV; if (head() && (! defined($options{'o'})) && ( dirty_files() || `git-diff-cache -r --cached HEAD`)) { print STDERR "Get rid of dirty files / uncommited deltas first.\n"; exit 1; } # FIXME SANITY CHECK. It is only possible to pull in 15 trees # --tg 17:58 05-05-28 if (! defined($_[0])) { unshift(@_, generate_url(shift)); } if ( -f '.git/HEAD' || -l '.git/HEAD') { push(@references, [head(), "${hostname}:" . getcwd()]); } while(defined (my $url = shift)) { $url = generate_url($url); my $remote_head = undef; if (defined($options{'l'}) && -d $url) { $ENV{'GIT_ALTERNATE_OBJECT_DIRECTORIES'} = "${url}/objects"; } else { gitcmd('rsync', '-qa', '--ignore-existing', "$url/objects/.", ".git/objects/."); } gitcmd('rsync', '-qL', "$url/HEAD", '.git/REMOTE_HEAD'); chomp($remote_head = _read_file('.git/REMOTE_HEAD')); push(@references, [$remote_head, $url]); } if (! defined($options{'o'})) { merge(@references); checkout('-f'); } } sub changes { my %options; local @ARGV = @_; GetOptions(\%options, "L", "R", "d", "m", "n", "t=s", "S=s", 'root'); @_ = @ARGV; my $git_diff_tree_options = '-s'; if (defined ($options{'d'})) { $git_diff_tree_options = '-p'; } if (defined ($options{'m'})) { $git_diff_tree_options .= ' -m'; } if (defined ($options{'S'})) { $git_diff_tree_options .= " -S'$options{'S'}'"; } if (defined ($options{'root'})) { $git_diff_tree_options .= " --root"; } if (defined ($options{'L'})) { if (! defined ($options{'n'})) { pull('-o', '-l', shift); } system("git-rev-tree HEAD '^REMOTE_HEAD' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV)); } elsif (defined ($options{'R'})) { if (! defined ($options{'n'})) { pull('-o', '-l', shift); } system("git-rev-tree REMOTE_HEAD '^HEAD' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV)); } elsif (defined ($options{'t'})) { system("git-rev-tree HEAD '^$options{'t'}' | sed -e 's/^[0-9]* //' | git-diff-tree --stdin -v $git_diff_tree_options " . join (' ', @ARGV)); } else { system("git-rev-list HEAD | git-diff-tree --stdin -v $git_diff_tree_options " . join(' ', @ARGV)); } } sub _push { my $url = generate_url(shift); gitcmd('rsync', '-qL', "$url/HEAD", '.git/REMOTE_HEAD'); chomp (my $remote_head = _read_file('.git/REMOTE_HEAD')); my $head = head() || die("No local HEAD\n"); if ($head eq $remote_head) { print STDERR "Nothing to push.\n"; return; } print "head => $head\nremote => $remote_head\n"; my @revlist = gitcmdout('git-rev-list', $head); if (! grep(/^${remote_head}$/, @revlist)) { print STDERR "Remote is ahead or unrelated: Need to pull first?\n"; exit 1; } gitcmd('rsync', '-qa', '--ignore-existing', ".git/objects/.", "$url/objects/."); gitcmd('rsync', '-q', '.git/HEAD', "$url/HEAD"); } sub clone { my %options; GetOptions(\%options, "l"); my $url = shift(@ARGV); my $project = shift(@ARGV); if (! defined($url) || ! defined($project)) { die("Usage: $0 clone <url> <project>\n"); } $url = generate_url($url); if (defined($options{l})) { if (-d $url) { -d ".git" && die("$0 clone $project: Don\'t create repository in a repository."); mkdir($project, 0755) || die("$0 clone $project: mkdir: $!"); chdir($project) || die("$0 clone $project: chdir: $!"); mkdir('.git', 0755) || die("$0 clone $project: mkdir: $!"); symlink("$url/objects", '.git/objects') || die("Can\'t symlink object database"); } else { die("Can't symlink from network repositories\n"); } } else { setup($project); } parent($url); pull(); } sub parent { my $parent = $_[0]; if (defined($parent)) { if ($parent eq "-c") { if ( -f ".git/PARENT" || -l ".git/PARENT") { unlink(".git/PARENT") || die("can't delete .git/PARENT: $!"); } print STDERR "$0 parent: Parent removed\n"; } else { $parent = generate_url($parent); _write_file('.git/PARENT', "$parent\n"); } } else { if ( -f ".git/PARENT" || -l ".git/PARENT") { chomp($parent = _read_file('.git/PARENT')); print "${parent}\n"; } else { print STDERR "$0 parent: No parent specified\n"; } } } sub diff { my %options; local @ARGV = @_; GetOptions(\%options, 'r=s', 'f', 'c', 'v', "S=s", 'root'); @_ = @ARGV; my $dir = tempdir(CLEANUP => 1); refresh(); my $git_diff_tree_options = ''; if (defined ($options{'S'})) { $git_diff_tree_options .= " -S'$options{'S'}'"; } if (defined ($options{'root'})) { $git_diff_tree_options .= " --root"; } if (defined($options{'r'})) { if($options{'r'} =~ /:/) { my ($first, $second) = split(/:/, $options{'r'}); system("git-diff-tree -p -r $first $second $git_diff_tree_options " . join(' ', @_)); } else { if (defined ($options{'v'})) { $git_diff_tree_options .= ' -v'; } system("git-diff-tree -p -r $options{'r'} $git_diff_tree_options " . join(' ', @_)); } } elsif (defined($options{"f"})) { system("git-diff-files -r -z $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z"); } elsif (defined($options{"c"})) { system("git-diff-cache --cached -r -z HEAD $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z"); } else { system("git-diff-cache -r -z HEAD $git_diff_tree_options " . join(' ', @_) . " | git-diff-helper -z"); } } sub treediff { my %options; local @ARGV = @_; Getopt::Long::Configure("pass_through"); GetOptions(\%options, 'p=s'); Getopt::Long::Configure("no_pass_through"); @_ = @ARGV; pull('-o', '-l', $options{'p'}); diff('-r', 'REMOTE_HEAD:HEAD', @_); } sub init_db { gitcmd( 'init-db', @_ ); } sub setup { my $project = shift || die ("usage: $0 setup project"); -d ".git" && die ("$0 setup $project: Don\'t create repository in a repository."); mkdir($project, 0755) || die ("$0 setup $project: mkdir: $!"); chdir($project) || die ("$0 setup $project: chdir: $!"); gitcmd('git-init-db'); } sub revert { # TODO handle revert of add/remove --tg 02:32 05-05-06 my @in = (); my $fh; if (! defined($_[0])) { return; } if ($_[0] eq "-") { @in = <STDIN>; } else { @in = @_; } my $head = head(); ($fh, $ENV{GIT_INDEX_FILE}) = tempfile(CLEANUP => 1); gitcmd('git-read-tree', $head); foreach (@in) { chomp($_); $_ =~ s#^\.\/##; if (/^\./) { print STDERR "Warning: Skipping $_\n"; next; } if (-d $_) { next; } gitcmd('git-checkout-cache', '-f', $_); } delete($ENV{GIT_INDEX_FILE}); foreach (@in) { chomp($_); $_ =~ s#^\.\/##; if (/^\./) { next; } if (! -f $_) { next; } gitcmd('git-update-cache', $_); } } sub add { my @in = (); if (defined($_[0]) && $_[0] eq "-") { @in = <STDIN>; } else { @in = @_; } foreach (@in) { chomp($_); $_ =~ s#^\.\/##; if (/^\./) { print STDERR "Warning: Skipping $_\n"; next; } if (-d $_) { next; } if (! -f $_) { print STDERR "Warning: Skipping nonexisting file: $_\n"; next; } gitcmd( 'git-update-cache', '--add', '--', $_ ); } } sub ci { my @in = (); if (defined($_[0]) && $_[0] eq "-") { @in = <STDIN>; } else { @in = @_; } foreach (@in) { chomp($_); $_ =~ s#^\.\/##; if (/^\./) { print STDERR "Warning: Skipping $_\n"; next; } if (-d $_) { next; } if (! -f $_) { print STDERR "Warning: Skipping nonexisting file: $_\n"; next; } gitcmd( 'git-update-cache', '--', $_ ); } } sub rm { my @in = (); if (defined($_[0]) && $_[0] eq "-") { @in = <STDIN>; } else { @in = @_; } foreach (@in) { chomp($_); $_ =~ s#^\.\/##; if (/^\./) { print STDERR "Warning: Skipping $_\n"; next; } if (-d $_) { next; } if (-f $_) { unlink($_) || die ("$0 rm $_: $!"); } gitcmd( 'git-update-cache', '--remove', '--' , $_ ); } } sub head { my $head = $_[0]; if (defined($head)) { if ($head eq "") { if ( -f ".git/HEAD" || -l ".git/HEAD") { unlink(".git/HEAD") || die "failed to delete .git/HEAD: $!\n"; } } else { chomp($head); _write_file( '.git/HEAD', "$head\n" ); } } else { if (-f '.git/HEAD') { chomp( $head = _read_file( '.git/HEAD' ) ); } else { $head = undef; } } return $head; } sub print_commit { my $commit = shift || die("need commit"); print "commit $commit\n"; foreach (gitcmdout('git-cat-file', 'commit', $commit)) { if (/^(author|committer)(.+>\s)(\d+)\s([+-]?\d{4})$/) { # local $ENV{TZ} = $4; print "$1$2" . localtime($3) . "\n"; } else { print "$_\n"; } } print "\n"; } sub commit { my $head = head(); chomp (my $tree = gitcmdout('git-write-tree')); if (dirty_files()) { die "$0 commit: Get rid of dirty files first.\n"; } if (defined($head)) { my %hash = commit_hash($head); if ($hash{tree} eq $tree) { die "$0 commit: The commit wouldn't commit anything different.\n"; } $head = gitcmdout('git-commit-tree', $tree, '-p', $head); } else { $head = gitcmdout('git-commit-tree', $tree); } head($head); } sub log { my $head = head() || return ; my $pid; if ( ! -p STDIN && ! -p STDOUT ) { my ( $r, $w ); pipe( $r, $w ) || die "Failed to pipe: $!"; defined( $pid = fork ) || die "Failed to fork: $!"; if ( $pid ) { # Parent $SIG{INT} = $SIG{TERM} = $SIG{HUP} = sub { kill 15, $pid; exit 1; }; close $r; close STDOUT; open STDOUT, '>&', $w || die "Failed to redirect STDOUT: $!"; } else { # pager child close $w; close STDIN; open STDIN, '<&', $r || die "Failed to redirect STDIN: $!"; if ( $ENV{PAGER} ) { exec( $ENV{PAGER} ); } else { exec( 'less', '-r', '-' ); } } } foreach (gitcmdout('git-rev-list', $head)) { print_commit($_); } if ( $pid ) { close STDOUT; waitpid($pid, 0); } } sub commit_hash { my %hash = (); my $id = shift || die ("$0: commit_hash: expect one argument"); my $comment = 0; my @lines = gitcmdout('git-cat-file', 'commit', $id); foreach (@lines) { chomp; if ($comment) { push(@{$hash{comment}}, $_); next; } if (/^tree\s(\w{40})$/) { $hash{tree} = $1; next; } if (/^parent\s(\w{40})$/) { push(@{$hash{parent}}, $1); next; } if (/^author\s(.*)\s<(.*)>\s(.*)$/) { $hash{author_name} = $1; $hash{author_eMail} = $2; $hash{author_date} = $3; next; } if (/^committer\s(.*)\s<(.*)>\s(.*)$/) { $hash{committer_name} = $1; $hash{committer_eMail} = $2; $hash{committer_date} = $3; next; } if (/^$/) { $comment = 1; } } return(%hash); } sub first_parent { my $this = shift || die ("$0: parent called without object"); my %hash = commit_hash($this); if (! defined(@{$hash{parent}}[0])) { return ""; } return(@{$hash{parent}}[0]); } sub undo { my $head = head(); if (! defined($head)) { return; } if ( dirty_files() || `git-diff-cache -r --cached HEAD`) { print STDERR "Get rid of dirty files / uncommited deltas first.\n"; exit 1; } my $newhead = first_parent($head); print "oldhead $head\nnewhead $newhead\n"; head($newhead); checkout('-f'); } sub object_type { my $id = shift; defined $id && $id =~ /^[A-Za-z0-9]{40}$/ || die "Invalid sha1 id '$id'"; my $type = gitcmdout('git-cat-file', '-t', $id ); chomp $type; return $type; } sub tree_id { my $id = shift; ( $id ) = grep { defined } map { /^tree ([A-Za-z0-9]{40})$/ ? $1 : undef } gitcmdout( 'git-cat-file', 'commit', $id ) or die "Unable to find tree id for commit id $id"; object_type( $id ) eq 'tree' || die "tree id from commit is not a tree object!"; return $id; } sub parent_id { my $id = shift; my ( $pid ) = grep { defined } map { /^parent ([A-Za-z0-9]{40})$/ ? $1 : undef } gitcmdout( 'git-cat-file', 'commit', $id ) or die "Unable to determine parent commit of commit $id"; return $pid; } # int # main(int argc, char **argv) # { if (defined($ARGV[0]) && defined($commands{$ARGV[0]})) { my $string = shift; if ( ! -d ".git" && ! ($string eq "setup" || $string eq "init-db" || $string eq "clone")) { die("$0 $string: Not in a git BASE directory"); } $commands{$string}->(@ARGV); } else { if (defined($ARGV[0])) { print STDERR "No such command: $ARGV[0]\n\n"; } usage(); } # } { my %gitcmd; sub gitcmdpath { my $cmd = shift; unless ( defined $gitcmd{$cmd} ) { local $/ = "\n"; chomp( $gitcmd{$cmd} = `which $cmd` ); return undef if $gitcmd{$cmd} eq ''; } return $gitcmd{$cmd}; } sub gitcmd { my $cmd = shift; gitcmdpath( $cmd ) || die "command '$cmd' not found"; my $r = system( $gitcmd{$cmd}, @_ ); die "$cmd failed: " . _gitcmderrmsg( $cmd ) if $r != 0; return 1; } sub gitcmdinout { my $infh = shift; my $cmd = shift; gitcmdpath( $cmd ) || die "command '$cmd' not found"; my ( $r, $w ); pipe( $r, $w ) || die "Failed to pipe: $!"; my $pid = fork(); die "Failed to fork: $!" unless defined $pid; if ( $pid ) { close $w; local $/; local $_ = <$r>; close $r; my $kid = waitpid( $pid, 0 ); die "Hmm, auto reaping in place?" if $kid == -1; die "$cmd failed: " . _gitcmderrmsg( $cmd ) if $? & 127 || $? >> 8 != 0; if ( wantarray ) { return split( "\n", $_ ); } else { return $_; } } else { close $r; close STDOUT; close STDIN; open STDIN, '<&', $infh || die "Failed to rediret STDIN"; open STDOUT, '>&', $w || die "Failed to redirect STDOUT"; exec( $gitcmd{$cmd}, @_ ); } } sub gitcmdout { my $cmd = shift; gitcmdpath( $cmd ) || die "command '$cmd' not found"; my ( $r, $w ); pipe( $r, $w ) || die "Failed to pipe: $!"; my $pid = fork(); die "Failed to fork: $!" unless defined $pid; if ( $pid ) { close $w; local $/; my $ret = <$r>; close $r; my $kid = waitpid( $pid, 0 ); die "Hmm, auto reaping in place?" if $kid == -1; die "$cmd failed: " . _gitcmderrmsg( $cmd ) if $? & 127 || $? >> 8 != 0; if (wantarray) { return split("\n", $ret); } else { return $ret; } } else { close $r; close STDOUT; open STDOUT, '>&', $w || die "Failed to redirect STDOUT"; exec( $gitcmd{$cmd}, @_ ); } } sub _gitcmderrmsg { my $cmd = shift; my $e; if ( $? == -1 ) { $e = "failed to execute $gitcmd{$cmd}: $!"; } elsif ( $? & 127 ) { $e = sprintf( 'child die from signal %d', ( $? & 127 ) ); $e .= ' (with coredump)' if $? & 128; } else { $e = sprintf( 'child exit value: %d', $? >> 8 ); } return $e; } } sub _recur_mkdir { my $dir = shift; my @dir = split( /\//, $dir ); my $path = ''; while ( @dir ) { $path .= '/' . shift @dir; ( -d $path ) || mkdir( $path ) || die "Failed to mkdir $path: $!"; } } sub _read_file { my $file = shift; my $fh; open $fh, '<', $file || die "failed to read $file: $!\n"; if ( wantarray ) { my @r = <$fh>; close $fh || die "failed to close $file: $!\n"; return @r; } else { local $/; my $r = <$fh>; close $fh || die "failed to close $file: $!\n"; return $r; } } sub _write_file { my $file = shift; my $fh; open $fh, '>', $file || die "failed to write $file: $!\n"; if ( @_ ) { print $fh join( $/, @_ ); } close $fh || die "failed to close $file: $!\n"; return 1; } # vim:set noexpandtab: ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-29 23:02 ` Thomas Glanzmann @ 2005-05-29 23:10 ` Thomas Glanzmann 2005-05-29 23:16 ` Thomas Glanzmann 0 siblings, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-29 23:10 UTC (permalink / raw) To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List Hello, one obvious fix: > $message .= "<= ${current_url} (nothing to merge)\n"; > $message .= "<= ${current_url} (bringing head ahead)\n"; the parents of such 'merges' should *not* be referenced in the commit object E.g. this is wrong: 'git-commit-tree -p nothing_to_merge -p just_bringing_head_ahead'. Leaving the message in the log section of the commit object could be useful, maybe. Thoughts? And I should doublecheck if more than 16 parents show up in git-commit-tree when doing the commits and throw an error. Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-29 23:10 ` Thomas Glanzmann @ 2005-05-29 23:16 ` Thomas Glanzmann 2005-05-29 23:46 ` Thomas Glanzmann 0 siblings, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-29 23:16 UTC (permalink / raw) To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List Hello, > > $message .= "<= ${current_url} (nothing to merge)\n"; > > $message .= "<= ${current_url} (bringing head ahead)\n"; > the parents of such 'merges' should *not* be referenced in the commit > object E.g. this is wrong: 'git-commit-tree -p nothing_to_merge -p > just_bringing_head_ahead'. Leaving the message in the log section of the > commit object could be useful, maybe. Thoughts? Wrong, wrong, wrong. If 'head ahead' condition shows up, I have to kick the previous head out (not the current), don't I? Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-29 23:16 ` Thomas Glanzmann @ 2005-05-29 23:46 ` Thomas Glanzmann 2005-05-29 23:56 ` Thomas Glanzmann 0 siblings, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-29 23:46 UTC (permalink / raw) To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List Hello, yet another bug. If there is only one head left after the for loop this head is *set* not *commitet* as new HEAD because it comes from a 'pull into empty tree' or from a 'just head ahead'-condition. Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-29 23:46 ` Thomas Glanzmann @ 2005-05-29 23:56 ` Thomas Glanzmann 2005-05-30 0:50 ` Junio C Hamano 0 siblings, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-29 23:56 UTC (permalink / raw) To: Junio C Hamano, Linus Torvalds; +Cc: Git Mailing List Hello, here is the actual version of my merge function. However I have a question left over: When I do an automatic merge or a threeway merge should I set the head for the next round to the remote one or just let it be the current one (this is the current behaviour) or maybe make it configurable? Note: At the moment the *local* head for the merge-base is only modified during the set of the first head *or* on a head forward condition. I have no clue. I am going to write the 'more on merging chapter' after I have figured this out. ;-) sub merge { my $message = undef; my $head = undef; my $last_tree = undef; my $fh; my @heads = (); foreach my $r (@_) { my $current_head = @{$r}[0]; my $current_url = @{$r}[1]; print "current_head => $current_head\ncurrent_url => $current_url\n"; push(@heads, '-p', ${current_head}); if (! defined($last_tree)) { $message = "=> ${current_url}\n"; $head = $current_head; $last_tree = $current_head; if (@_ == 1) { head($head); return; } next; } my $merge_base = gitcmdout('git-merge-base', $head, $current_head) || die ("no merge-base"); chomp($merge_base); print "head => $head\nremote => $current_head\nbase => $merge_base\n"; if ($merge_base eq $current_head) { $message .= "<= ${current_url} (nothing to merge)\n"; $#heads -= 2; next; } if ($merge_base eq $head) { $message .= "<= ${current_url} (bringing head ahead)\n"; $head = ${current_head}; $last_tree = ${current_head}; $#heads -= 4; push(@heads, '-p', $current_head); next; } gitcmd('git-read-tree', '-m', $merge_base, $last_tree, $current_head); if (! defined($last_tree = write_tree())) { system('git-merge-cache', '-o', 'git-merge-one-file-script', '-a'); if (! defined($last_tree = write_tree())) { # FIXME: Make manual intervention possible # --tg 23:11 05-05-29 die("Couldn't merge automatically: Call 'git resolve'"); } $message .= "<= ${current_url} (threeway merge)\n"; } else { $message .= "<= ${current_url} (automatic merge)\n"; } } if (@heads == 1) { head(@head[0]); return; } open($fh, "+>", undef); print $fh $message; seek($fh, 0, 0); $head = gitcmdinout($fh, 'git-commit-tree', $last_tree, @heads); chomp($head); close $fh; head($head); return; } Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-29 23:56 ` Thomas Glanzmann @ 2005-05-30 0:50 ` Junio C Hamano 2005-05-30 0:57 ` Junio C Hamano 2005-05-30 1:30 ` Thomas Glanzmann 0 siblings, 2 replies; 42+ messages in thread From: Junio C Hamano @ 2005-05-30 0:50 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Linus Torvalds, Git Mailing List Instead of inflicting a Perl script on us, maybe writing a textual specification of what you want it to do would help to clarify your thinking and help us understand the problem you are trying to describe a lot better. I think Linus publicly stated he does not do Perl much. I am OK with Perl but I'd rather answer questions posed in a more reader-friendly manner, rather than having to guess what the caller is expected to give this "merge" sub, which you do not document well. I think I've already asked you something quite similar when you posted another part of your script for parsing the new diff-raw format, which I responded with something like: "Without knowing how this sub is supposed to be called, I think you are stripping leading colon from a filename if there is one". Anyhow. Are you trying to implement an Octopus capable N-way merger? If so, the way I would do would be something like this: - Accept N parameters, which are heads being merged. - Sanity check that given heads are commits, and N <= 16. - Initialize a set, HTM (heads to be merged), to contain all of the supplied heads. - Remove one commit from HTM, call it H0. - Initialize a variable, BASE, with H0. This variable determines the base of the merge in the commit topology. - Initialize a variable, T, with tree associated with H0. This variable holds the "current intermediate merge result" tree. - While HTM is not empty, loop over the following: - Remove one commit out of HTM; call it H1. - MB = git-merge-base BASE H1; - If MB is either BASE or H1, then you have a fast forward. Take either BASE or H1 that is not MB and update variable BASE with it, and update variable T with the tree associated with it. Continue with the loop (i.e. Perl "next"). - Run your usual read-tree -m MB T H1 and git-merge-cache; as Linus explained, if this step ends up involving any non-trivial merges, you should not do an Octopus. So in such a case, if HTM is not empty yet, barf (i.e. Perl "die", or at least "last"). - Do not touch your ${GIT-.git}/HEAD in any way at this moment. - Update variable T with git-write-tree of the resolved cache contents. - Update varaible BASE with MB. - Continue with the loop. - We exited the loop by now. HTM being empty means that T has the result of N-way merge. Create a single commit object that has all the commits you have merged as its parents, and register T as its associated tree. I would imagine recording that commit in ${GIT-.git}/HEAD is what the user usually wants but there may be use cases that it may not be appropriate (I do not do Porcelain so I do not know). ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 0:50 ` Junio C Hamano @ 2005-05-30 0:57 ` Junio C Hamano 2005-05-30 1:33 ` Thomas Glanzmann 2005-05-30 1:30 ` Thomas Glanzmann 1 sibling, 1 reply; 42+ messages in thread From: Junio C Hamano @ 2005-05-30 0:57 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Linus Torvalds, Git Mailing List >>>>> "JCH" == Junio C Hamano <junkio@cox.net> writes: JCH> - If MB is either BASE or H1, then you have a fast forward. JCH> Take either BASE or H1 that is not MB and update variable JCH> BASE with it, and update variable T with the tree JCH> associated with it. Continue with the loop (i.e. Perl JCH> "next"). Chuck this part please. I was not thinking. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 0:57 ` Junio C Hamano @ 2005-05-30 1:33 ` Thomas Glanzmann 0 siblings, 0 replies; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-30 1:33 UTC (permalink / raw) To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List Hello, > JCH> - If MB is either BASE or H1, then you have a fast forward. > JCH> Take either BASE or H1 that is not MB and update variable > JCH> BASE with it, and update variable T with the tree > JCH> associated with it. Continue with the loop (i.e. Perl > JCH> "next"). > Chuck this part please. I was not thinking. No, I don't because I think this exactly what I have to do here. And yes, there can be fast forwards. :-) But note: On a fast forward condition we have to remove a element from COMMIT_HEADS: the (last) or (last - 1). Depending on if 'local' == MERGE_BASE or 'remote' == MERGE_BASE. Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 0:50 ` Junio C Hamano 2005-05-30 0:57 ` Junio C Hamano @ 2005-05-30 1:30 ` Thomas Glanzmann 2005-05-30 7:57 ` Junio C Hamano 1 sibling, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-30 1:30 UTC (permalink / raw) To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List Hello, okay let me try again. I have a function merge which gets a sorted array of heads. Heads can be unlimited at the time because some of the heads can be included into other heads (they're a subset) and so they don't show up in the commit object. I call this array MERGE_HEADS. Note: If I pull into an empty tree (no HEAD) there is only one head in this array which corresponds to the remote_head. Otherwise the first element is *always* the local HEAD. After that I am starting looping over MERGE_HEADS. The first thing I have to do is getting the first element out of this array and safe it for later reference I call this 'head'. Also I have to push this head in a another array called COMMIT_HEADS which will be used to create the final commit object later on. The latter will be done for every loop pass. next; Note: If I left the the loop because there are no more MERGE_HEADS to work on and my COMMIT_HEADS array consists only of *one* member I don't create a COMMIT object, but save it as new HEAD because we're in a fast forward condition (this could be pulling into an empty tree; having many fast forward object (remote is ahead or included into the current 'head'). On the contrary if I have *more* than one object I call commit-tree with the COMMIT_HEADS as arguments and save the new head return from this call. Now I start processing the second HEAD from MERGE_HEADS. I use merge_base to find out the MERGE_BASE. If this MERGE_BASE == head than we have a (remote is fast forward condition) so our CURRENT_HEAD becomes head and I delete the week of the last element of COMMIT_HEADS (but leaving the CURRENT_HEAD in COMMIT_HEADS). next; If MERGE_BASE == CURRENT_HEAD than CURRENT_HEAD is already included in our history so no need to anything, but get it out of COMMIT_HEADS. next; If it isn't a fast forward or already included case, we do automatic/threeway/manual merge and save the resulting tree for the maybe to come next automatic/threeway/manual merge. And of course also leaving the CURRENT_HEAD in COMMIT_HEADS. FIXME: Do we need to update our 'head' to the REMOTE_HEAD? next; Oh and of course the sanity check: I can't commit-tree more than 16 parents at a time. (16 is of course the define mentioned by Linus before). That's it. Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 1:30 ` Thomas Glanzmann @ 2005-05-30 7:57 ` Junio C Hamano 2005-05-30 8:36 ` Thomas Glanzmann 0 siblings, 1 reply; 42+ messages in thread From: Junio C Hamano @ 2005-05-30 7:57 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Linus Torvalds, Git Mailing List >>>>> "TG" == Thomas Glanzmann <sithglan@stud.uni-erlangen.de> writes: TG> Note: If I pull into an empty tree (no HEAD) there is only one head in TG> this array which corresponds to the remote_head. Otherwise the first TG> element is *always* the local HEAD. "An empty tree (no HEAD)"? Is your definition of "an empty tree" the same as "empty" directory after you do "mkdir empty && cd empty && git-init-db", followed by bunch of git-*-pull to get the objects and commits from other reposititories being involved in the merge but without touching .git/HEAD? If so, why cannot I do the git-*-pull from multiple repositories and merge them together? Why "there is only one head in this array that is remote_head"? Oh, I guess I am missing your definition of "remote_head". Puzzled... Anyhow I presume that if your ${GIT-.git}/HEAD exists, you include it as the first element of MERGE_HEADS. TG> I have a function merge which gets a sorted array of heads. Heads can be TG> unlimited at the time because some of the heads can be included into TG> other heads (they're a subset) and so they don't show up in the commit TG> object. I call this array MERGE_HEADS. Sorry I am not very good at this "thinking" thing, and I need to draw pictures. Please bear with me. (line of dev C)-------------C We are here, trying to merge (line of dev B)---(merge)---B these three lines of devs: (line of dev A)---A/ A, B and C MERGE_HEADS = (A B C) A is actually a "subset" of B Is this what you mean by "subset"? Are these "subset" HEAD the only thing that causes fast forwards? My gut feeling without thinking much is that it might be easier to first cull such fast forward heads by using N-way rev-tree before you do anything else. If only one head survives after that, then that head would be your new head and you do not have to go through any merges. Otherwise you merge those independent heads without worrying about fast forwards. How does that sound? ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 7:57 ` Junio C Hamano @ 2005-05-30 8:36 ` Thomas Glanzmann 2005-05-30 9:21 ` Thomas Glanzmann 0 siblings, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-30 8:36 UTC (permalink / raw) To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List Hello, [ => Skip till next ']' if you want because my approach doesn't work out for all cases in the optimal way. > "An empty tree (no HEAD)"? Is your definition of "an empty > tree" the same as "empty" directory after you do "mkdir empty && > cd empty && git-init-db", followed by bunch of git-*-pull to get > the objects and commits from other reposititories being involved > in the merge but without touching .git/HEAD? Yep. In practice it isn't anything else than just using the first 'remote' HEAD as 'local' HEAD if there is no 'local' HEAD. But for merge this doesn't really matter. > If so, why cannot I do the git-*-pull from multiple repositories and > merge them together? Why "there is only one head in this array that > is remote_head"? Oh, I guess I am missing your definition of > "remote_head". Puzzled... My intention by writing this note was to clear things up. That it also works for cases where you do the 'initial clone' for one and multiple remotes. > Anyhow I presume that if your ${GIT-.git}/HEAD exists, you > include it as the first element of MERGE_HEADS. Affirmative. > TG> I have a function merge which gets a sorted array of heads. Heads can be > TG> unlimited at the time because some of the heads can be included into > TG> other heads (they're a subset) and so they don't show up in the commit > TG> object. I call this array MERGE_HEADS. Here is a typo: MERGE_HEADS is *not* sorted. But it is 'ordered'. > Sorry I am not very good at this "thinking" thing, and I need to > draw pictures. Please bear with me. Of course, I do. > (line of dev C)-------------C We are here, trying to merge > (line of dev B)---(merge)---B these three lines of devs: > (line of dev A)---A/ A, B and C > MERGE_HEADS = (A B C) > A is actually a "subset" of B > Is this what you mean by "subset"? Are these "subset" HEAD the > only thing that causes fast forwards? Exactly, but it has not to be a merge it also can be a linear development. What matters that A is referenced in any way in the history of B. In this case where A is referenced in the history of B, we just discard HEAD A, because it is already merged. So A will never show up in git-commit-tree in one of its "-p" options. But I mention it in the commit-text as 'there was nothing todo'. If we take now your example and switch the order A and B we have MERGE_HEADS = (B A C) and A is still in the history of B: We do exactly the same here only that in the above scenario we have to kick out the previous COMMIT_HEAD when processing B while we drop the current COMMIT_HEAD when processing A in this scenario. So to clear things up: The HEAD we are working on is surrouned by '*'s. MERGE_HEADS = (*B* A C) => COMMIT_HEADS = (B) MERGE_HEADS = (B *A* C) => COMMIT_HEADS = (B) /* note: A never did it in COMMIT_HEADS because it was referenced in history of B */ MERGE_HEADS = (B A *C*) => COMMIT_HEADS = (B C) while in the above example with your initial order it is: MERGE_HEADS = (*A* B C) => COMMIT_HEADS = (A) MERGE_HEADS = (A *B* C) => COMMIT_HEADS = (B) /* note: A is kicked out of COMMIT_HEADS ... see above */ MERGE_HEADS = (A B *C*) => COMMIT_HEADS = (B C) ] > My gut feeling without thinking much is that it might be easier > to first cull such fast forward heads by using N-way rev-tree > before you do anything else. If only one head survives after > that, then that head would be your new head and you do not have > to go through any merges. Otherwise you merge those independent > heads without worrying about fast forwards. How does that > sound? You're right. Because this would work out bad (unneccessary automatic/threeway/manual merge) if we twist MERGE_HEADS again: MERGE_HEADS = (*C* A B) => COMMIT_HEADS (C) MERGE_HEADS = (C *A* B) => COMMIT_HEADS (C A) /* A stays in because it is not in the history of C; -> unneccessary merge */ MERGE_HEADS = (C A *B*) => COMMIT_HEADS (C A B) Okay, so I have to elminate all fast forward conditions in the first place? How do I do this: foreach CURRENT_HEAD (@MERGE_HEADS) { foreach COMPARE_HEAD (@MERGE_HEADS) { if (COMPARE_HEAD != CURRENT_HEAD && COMPARE_HEAD is_included_into_history_of CURRENT_HEAD) { @WIPE_HEADS += COMPARE_HEAD; } } } foreach (@WIPE_HEADS) { grep -v @WIPE_HEADS @MERGE_HEADS; } Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 8:36 ` Thomas Glanzmann @ 2005-05-30 9:21 ` Thomas Glanzmann 2005-05-30 10:20 ` Junio C Hamano 0 siblings, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-30 9:21 UTC (permalink / raw) To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List Hello, I also have to strip duplicate HEADs out. So we do the following: run 0: kill dups run 1: kill HEADs which are referenced in the history of other HEADs run 2: do the merging (still don't know to what I should set the local HEAD to the 'left' or 'right' part. Maybe we should create temporary commit object so that 'merge-base' can better work on them? Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 9:21 ` Thomas Glanzmann @ 2005-05-30 10:20 ` Junio C Hamano 2005-05-30 12:11 ` Thomas Glanzmann 0 siblings, 1 reply; 42+ messages in thread From: Junio C Hamano @ 2005-05-30 10:20 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Linus Torvalds, Git Mailing List >>>>> "TG" == Thomas Glanzmann <sithglan@stud.uni-erlangen.de> writes: TG> Hello, TG> I also have to strip duplicate HEADs out. So we do the following: TG> run 0: kill dups TG> run 1: kill HEADs which are referenced in the history of other HEADs I think merge-base between A and A is always A, so the second pass would eliminate the dups anyway... ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 10:20 ` Junio C Hamano @ 2005-05-30 12:11 ` Thomas Glanzmann 2005-05-30 17:54 ` Junio C Hamano 0 siblings, 1 reply; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-30 12:11 UTC (permalink / raw) To: Junio C Hamano; +Cc: Linus Torvalds, Git Mailing List Hello, > I think merge-base between A and A is always A, so the second pass > would eliminate the dups anyway... two scenarios come into my mind which could clash (second ommited because it would rely that the 'local' HEAD is changes on threeway merge) consider the follow MERGE_HEADS = (A B B) /----- A / -GCA------ B Now B gets twice automatic/threeway/manual merged into A which is just stupid and couldn't work out. ------------------------------------------------------------------------------ But I still don't know how to handle the following scenario: /----- A / -GCA------GCA---- B \---- C MERGES_HEAD = (A B C). I think the best way would be introduce a temporary commit object otherwise C into AB merge would have merge_base on the first GCA which is suboptimal and maybe wrong isn't it? /----- A -------D---E / / / -GCA------GCA---- B / \------- C Where D is a temporary COMMIT obeject to use the second GCA to merge C with D and gets E. Gruesse, Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 12:11 ` Thomas Glanzmann @ 2005-05-30 17:54 ` Junio C Hamano 0 siblings, 0 replies; 42+ messages in thread From: Junio C Hamano @ 2005-05-30 17:54 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Linus Torvalds, Git Mailing List >>>>> "TG" == Thomas Glanzmann <sithglan@stud.uni-erlangen.de> writes: Disregard my last MB(A,A)===A thing. I really was not thinking, but in N-way merge I think rev-tree is your friend. Unlike merge-base which always does two heads at a time, rev-tree works N-way. You should be able to give many heads to it and have it help you figure out the ancestory relationship across them. TG> But I still don't know how to handle the following scenario: TG> /----- A TG> / TG> -GCA#1---GCA#2--- B TG> \---- C TG> MERGES_HEAD = (A B C). I think the best way would be introduce a TG> temporary commit object otherwise C into AB merge would have merge_base TG> on the first GCA which is suboptimal and maybe wrong isn't it? I think you can run the same algorithm for those pairwise ancestors and favor the less common ones. In the above drawing, you would first find out the three common pair-wise ancestors. In your drawing, GCA(A,B) = GCA#1 GCA(B,C) = GCA#2 GCA(C,A) = GCA#1 GCA(GCA#1,GCA#2) = GCA#1 Then you notice that GCA#2 is less common than GCA#1 (indeed GCA#1 is contained in GCA#2). So favoring the GCA#2, you do the merge pair that uses it as the merge base, namely B and C, first. You scan the pairwise list again and find the pair that have not merged and uses the least common GCA as merge base and keep going. For the definition of less common, in many cases those GCA#n would not be related at all and cannot be compared by topology only, so you would probably need to come up with a heuristics to break such ties. Off the top of my head, you could compare commit timestamps (prefer younger), sum of commit chain length from GCA#n to its two head pairs (prefer shorter), sum of number of changed files from GCA#n to its two head pairs (prefer smaller). TG> /----- A -------D---E TG> / / / TG> -GCA------GCA---- B / TG> \------- C TG> Where D is a temporary COMMIT obeject to use the second GCA to merge C TG> with D and gets E. Yes, if you ended up merging A and B first you would have something like that. I think you should not do that in the first place, and try to merge the "most recently diverged" pair first, like this: /----- A -----------Y / / -GCA#1---GCA#2--- B --X \---- C -/ If we are still talking about Octopus (Tripus in this case), you would not want to actually make a commit X. When you merge B and C, you consider that such a resulting tree resides at GCA#2 (merge base of B and C) for the purposes of further merge computation. Then you merge that resulting tree with A, using GCA#1. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:55 ` Thomas Glanzmann 2005-05-27 20:13 ` Junio C Hamano @ 2005-05-27 20:17 ` Linus Torvalds 1 sibling, 0 replies; 42+ messages in thread From: Linus Torvalds @ 2005-05-27 20:17 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Junio C Hamano, Kay Sievers, Git Mailing List On Fri, 27 May 2005, Thomas Glanzmann wrote: > > You merge by hand and resolve if they have conflicts, just like > > what you already do in two head merge case. > > I see. Does that mean that 'git-ls-files --unmerged' will report upto 9 > stages per file? No, you must always merge trees one by one against each other. The simplest ordering is to merge trees 1-2 first, then the result of that with 3, then the result of _that_ with 4 etc etc, but you can - if you really want to - do 1-2 and 3-4 separately and then merge those two together. The ordering does actually end up mattering a bit when it comes to deciding on parenthood, but in the end you will have used the same most remote common parent for _one_ of the merges anyway, so assuming all the merges were automatically resolved by the regular 3-way thing, I claim that it doesn't really matter noticeably (*). Regardless, you'd end up with seven "git-read-tree -m x y z" invocations (plus possibly a few git-merge-cache calls), and one final commit. Linus (*) I bet you could find some case where the ordering either generates a create-create conflict or it doesn't, depending on how you pair things up. But I also claim that you'd be crazy to do a octopus merge for something like that anyway, and that the reason to do one is that you've had five totally disjoint things you've been working on - like updating five different drivers or five different filesystems in different branches, and there are no conflicts however you turn. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:29 ` Thomas Glanzmann 2005-05-27 19:52 ` Junio C Hamano @ 2005-05-27 19:54 ` Junio C Hamano 2005-05-27 19:58 ` Thomas Glanzmann 2005-05-27 20:03 ` Linus Torvalds 2 siblings, 1 reply; 42+ messages in thread From: Junio C Hamano @ 2005-05-27 19:54 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Kay Sievers, Git Mailing List Thomas, could you please stop doing Mail-Followup-To in your header please? I automatically did 'reply all' and ended up preaching Linus (because that was the first mailbox on your Mail-Followup-to header) how Octopus works, when he knows what it is already. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:54 ` Junio C Hamano @ 2005-05-27 19:58 ` Thomas Glanzmann 0 siblings, 0 replies; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-27 19:58 UTC (permalink / raw) To: Junio C Hamano; +Cc: Git Mailing List Hello, * Junio C Hamano <junkio@cox.net> [050527 21:54]: > Thomas, could you please stop doing Mail-Followup-To in your > header please? I automatically did 'reply all' and ended up > preaching Linus (because that was the first mailbox on your > Mail-Followup-to header) how Octopus works, when he knows what > it is already. test without the mft. Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:29 ` Thomas Glanzmann 2005-05-27 19:52 ` Junio C Hamano 2005-05-27 19:54 ` Junio C Hamano @ 2005-05-27 20:03 ` Linus Torvalds 2005-05-27 20:24 ` Junio C Hamano 2 siblings, 1 reply; 42+ messages in thread From: Linus Torvalds @ 2005-05-27 20:03 UTC (permalink / raw) To: Thomas Glanzmann; +Cc: Kay Sievers, Git Mailing List On Fri, 27 May 2005, Thomas Glanzmann wrote: > > > I get the urge to do octopus-merges in the kernel just because of how > > good they look in gitk ;) ] > > talking about octopus-merges ... I don't understand how they work. What > happens if one file is touched in every of the 8 trees. How can that be > handled? Automatically? You can do multiple three-way merges, no problem. In fact, the general algorithm for an n-way merge is to just do the "git-resolve-script" n-1 times, but _without_ the commit. Then you just commit the result, and the only thing to keep in mind is to get the parents right, because if you don't, you're screwed. This does imply a merge ordering, but since we order the parents anyway, that's actually also described 100% by the commit, so the end result is clean and good. There are two reasons not to do octopus-merges, and neither of them is huge, but they've kept me from doing them.. - if you screw up half-way through the merge, it's a lot harder to recover without blowing away all the other merges too and having to re-do them. You certainly _can_ do it (say, by just recording the trees in between merges - it's definitely not rocket science), but it basically means that you need to keep track of things _outside_ of the normal "what was the last HEAD" model. More importantly, since an octopus merge has only one commit message associated with it, you really should never use one for anything that needs any manual intervention. Otherwise you'll have to start explaining which merge you needed to fix up manually etc, and it just gets complex for no actual gain. IOW, this argument is only against complex merges. The trivial ones can easily be done as octopuses, and in many ways the resulting history may actually reflect what you did better. For example, for somebody like Jeff, who maintains 50 different branches, and merges 5 of them to send them to me, an octopus merge in many ways is much more intuitive: it really says "I took these five branches and combined them", while a series of four regular merges just gets messy. - Compatibility with other systems. I don't care one whit about stuff I consider broken (ie CVS), but there are SCM's out there that I _don't_ think are broken, and that don't do multi-parent merges for "nrparent > 2". You can always split an octopus merge that didn't have any manual intervention, so again, this is not a huge argument if you follow rule #1, but unless you have a reason for doing an octopus merge, it means that you should probably avoid it. So _I_ usually don't have any reason at all, it would be stupid of me to merge trees from different people as an octopus, but usage like Jeff's (where the merge is due to "pass these <n> trees upwards") is different. So there you have it. Don't do it just because you can, but if you have a good reason for them and they were done automatically without any human intervention (apart from having to change the scripts, of course), I won't argue too much against them either. I already took one such merge from Junio in the GIT tree, and I actually like having that as a way to make sure the tools can handle it. Linus ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 20:03 ` Linus Torvalds @ 2005-05-27 20:24 ` Junio C Hamano 0 siblings, 0 replies; 42+ messages in thread From: Junio C Hamano @ 2005-05-27 20:24 UTC (permalink / raw) To: Linus Torvalds; +Cc: Thomas Glanzmann, Kay Sievers, Git Mailing List >>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes: LT> For example, for somebody like LT> Jeff, who maintains 50 different branches, and merges 5 of them to send LT> them to me, an octopus merge in many ways is much more intuitive: it LT> really says "I took these five branches and combined them", while a LT> series of four regular merges just gets messy. This really is a good use case for an Octopus. I hope Jeff is reading this thread. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:24 More gitweb queries Linus Torvalds 2005-05-27 19:29 ` Thomas Glanzmann @ 2005-05-27 19:31 ` Thomas Glanzmann 2005-05-27 19:32 ` Junio C Hamano ` (3 subsequent siblings) 5 siblings, 0 replies; 42+ messages in thread From: Thomas Glanzmann @ 2005-05-27 19:31 UTC (permalink / raw) To: Linus Torvalds; +Cc: Kay Sievers, Git Mailing List Hello, > - looking around, the ALSA guys aren't the only ones that start off with > an empty line, so it's probably worth fixing the summary etc to ignore > whitespace at the beginning rather than give empty summary reasons. for me the formatting of an empty commit comment screw up the layout. I don't know if it already is fixed, if not, I will soon report a patch. Thomas ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:24 More gitweb queries Linus Torvalds 2005-05-27 19:29 ` Thomas Glanzmann 2005-05-27 19:31 ` Thomas Glanzmann @ 2005-05-27 19:32 ` Junio C Hamano 2005-05-27 19:48 ` Linus Torvalds 2005-05-27 23:12 ` Benjamin Herrenschmidt ` (2 subsequent siblings) 5 siblings, 1 reply; 42+ messages in thread From: Junio C Hamano @ 2005-05-27 19:32 UTC (permalink / raw) To: Linus Torvalds; +Cc: Kay Sievers, Git Mailing List [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: text/plain; charset=iso-2022-jp-2, Size: 376 bytes --] >>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes: LT> Combining some of the features of the two (that ^[.A^[N|ber-cool revision LT> history graph from gitk rules, for example) might be cool. I get the LT> urge to do octopus-merges in the kernel just because of how good they LT> look in gitk ;) ] Hey, Octopus is what you explicitly told me not to do ;-). ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:32 ` Junio C Hamano @ 2005-05-27 19:48 ` Linus Torvalds 0 siblings, 0 replies; 42+ messages in thread From: Linus Torvalds @ 2005-05-27 19:48 UTC (permalink / raw) To: Junio C Hamano; +Cc: Kay Sievers, Git Mailing List [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Type: TEXT/PLAIN; charset=iso-2022-jp-2, Size: 546 bytes --] On Fri, 27 May 2005, Junio C Hamano wrote: > > >>>>> "LT" == Linus Torvalds <torvalds@osdl.org> writes: > > LT> Combining some of the features of the two (that ^[.A^[N|ber-cool revision > LT> history graph from gitk rules, for example) might be cool. I get the > LT> urge to do octopus-merges in the kernel just because of how good they > LT> look in gitk ;) ] > > Hey, Octopus is what you explicitly told me not to do ;-). I know, I know. I said "I get urges", I didn't say I'll do it. I'll try to control myself. Maybe. Linus ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:24 More gitweb queries Linus Torvalds ` (2 preceding siblings ...) 2005-05-27 19:32 ` Junio C Hamano @ 2005-05-27 23:12 ` Benjamin Herrenschmidt 2005-05-27 23:59 ` Kay Sievers 2005-05-30 23:03 ` Paul Mackerras 5 siblings, 0 replies; 42+ messages in thread From: Benjamin Herrenschmidt @ 2005-05-27 23:12 UTC (permalink / raw) To: Linus Torvalds; +Cc: Kay Sievers, Git Mailing List > On that small note, I also find "gitk" very cool indeed, too bad about > the fact that tk/tcl seems to always end up looking so _ugly_. Is there > any way to get anti-aliased fonts and a less 'Motify' blocky look from > tcl/tk? Every time I see that, I feel like I'm back in the last century > or something. Heh, tell paulus, he loves Tk :-) He told me the next version of Tk will have anti aliased fonts. I don't know about blockyness of the widgets tho. > Combining some of the features of the two (that über-cool revision > history graph from gitk rules, for example) might be cool. I get the > urge to do octopus-merges in the kernel just because of how good they > look in gitk ;) ] ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:24 More gitweb queries Linus Torvalds ` (3 preceding siblings ...) 2005-05-27 23:12 ` Benjamin Herrenschmidt @ 2005-05-27 23:59 ` Kay Sievers 2005-05-28 1:03 ` Daniel Serpell 2005-05-30 23:03 ` Paul Mackerras 5 siblings, 1 reply; 42+ messages in thread From: Kay Sievers @ 2005-05-27 23:59 UTC (permalink / raw) To: Linus Torvalds; +Cc: Git Mailing List On Fri, May 27, 2005 at 12:24:20PM -0700, Linus Torvalds wrote: > - looking around, the ALSA guys aren't the only ones that start off with > an empty line, so it's probably worth fixing the summary etc to ignore > whitespace at the beginning rather than give empty summary reasons. That is already fixed a few days ago, but unfortunately only in my devel version. After the catch-up on the recent format changes of the git-output, I need to wait now until the new git-binaries are hitting kernel.org. > - any reason to limit the "summary" page to just the last 14 changes? The > "log" thing you can ask to go back further, it would be nice to have > something like a "last 100" thing for summaries too, especially since > the summary is so nice and dense, so you can actually get a nice view > of what has happened without scrolling _too_ much. Yes, I recognized that too. Jeff asked for branches and I did the summary page just for the branches. :) With that I realized that the dense log of the summary is sometimes nicer than the full log. How about this: http://ehlo.org/~kay/gitweb.cgi?p=git/git.git;a=shortlog It is reachable on the summary page by clicking in the title of the shortlog. > - I was in the "commitdiff" thing, and initially thought that there was > no way to get back to the "summary" view. > > It turns out I was wrong (the summary is reachable by just clicking at > the project name itself in the top header), but it's a bit strange that > the "commitdiff" thing has an explicit link back to itself (hey, > consistency is good, so I'm not complaining) Well, yes I was trying to get a navigation which is not changing with every new page... It's not an active link now. Maybe that's better. > but the link back to the > summary page is implicit. > > So how about adding an explicit "summary" link to the list of other > explicit links (log, commit, commitdiff and tree) at the top of the > page? Done! That was easy. It will show up on kernel.org when the actual git-binaries are installed. > I actually like browsing other peoples projects with the gitweb > interfaces, it's both responsive and verbose enough to really say > something good. In contrast, cvsweb is always just a mess of "these are > the files, go at it", which is totally pointless and doesn't tell anything > about what is actually happening in the project. > > So dammit, I'm very biased indeed, but I'm just looking at gitweb, and > comparing it to both the CVS and SVN web things, and they just reinforce > my conviction that CVS is absolute crap, and I find myself surprised by > how crap SVN also appears. Good to hear that. It's a long road to make software from "working" to "nice to use". That was probably never the goal of some SCM-web-interfaces. :) > Combining some of the features of the two (that über-cool revision > history graph from gitk rules, for example) might be cool. I get the > urge to do octopus-merges in the kernel just because of how good they > look in gitk ;) ] I would like to show something like the graph too, but I don't really know how to do this in html. Seems slippery if not impossible. If anybody has a nice idea how to represent that, I will give it a try. Kay ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 23:59 ` Kay Sievers @ 2005-05-28 1:03 ` Daniel Serpell 2005-05-28 2:51 ` David Lang 2005-05-28 8:42 ` Kay Sievers 0 siblings, 2 replies; 42+ messages in thread From: Daniel Serpell @ 2005-05-28 1:03 UTC (permalink / raw) To: Git Mailing List Hi! On 5/27/05, Kay Sievers <kay.sievers@vrfy.org> wrote: > On Fri, May 27, 2005 at 12:24:20PM -0700, Linus Torvalds wrote: > > Combining some of the features of the two (that über-cool revision > > history graph from gitk rules, for example) might be cool. I get the > > urge to do octopus-merges in the kernel just because of how good they > > look in gitk ;) ] > > I would like to show something like the graph too, but I don't really know > how to do this in html. Seems slippery if not impossible. > If anybody has a nice idea how to represent that, I will give it a try. Well, you could draw them in javascript, using http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm :-) Alternatively, you could use a fixed set of little images, a bar "|", a dot "o" and branches like "Y", "7" and "\". Obviously, octopus-merges are very difficult to draw using only those. BTW, I tried searching on gitweb, and I think that found a problem, see: http://ehlo.org/~kay/gitweb.cgi?p=git/git.git;a=search;s=check At the bottom of the page, highlighting of the search term stops and the commits are all the same color. Daniel. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-28 1:03 ` Daniel Serpell @ 2005-05-28 2:51 ` David Lang 2005-05-28 10:56 ` Kay Sievers 2005-05-28 8:42 ` Kay Sievers 1 sibling, 1 reply; 42+ messages in thread From: David Lang @ 2005-05-28 2:51 UTC (permalink / raw) To: Daniel Serpell; +Cc: Git Mailing List > Hi! > > On 5/27/05, Kay Sievers <kay.sievers@vrfy.org> wrote: >> On Fri, May 27, 2005 at 12:24:20PM -0700, Linus Torvalds wrote: >>> Combining some of the features of the two (that über-cool revision >>> history graph from gitk rules, for example) might be cool. I get the >>> urge to do octopus-merges in the kernel just because of how good they >>> look in gitk ;) ] >> >> I would like to show something like the graph too, but I don't really know >> how to do this in html. Seems slippery if not impossible. >> If anybody has a nice idea how to represent that, I will give it a try. > > Well, you could draw them in javascript, using > http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm :-) > > Alternatively, you could use a fixed set of little images, a bar "|", a > dot "o" and branches like "Y", "7" and "\". Obviously, octopus-merges > are very difficult to draw using only those. you could look into SVG (scaleable vector graphics or some such thing) that are supposed to be in the newest browsers (or soon to be added, I'm not sure). this should let you do all the drawing nessasary reasonably easily (if you are willing to limit users to that, which is probably not that big of a problem for git) 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 [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-28 2:51 ` David Lang @ 2005-05-28 10:56 ` Kay Sievers 0 siblings, 0 replies; 42+ messages in thread From: Kay Sievers @ 2005-05-28 10:56 UTC (permalink / raw) To: David Lang; +Cc: Daniel Serpell, Git Mailing List On Fri, May 27, 2005 at 07:51:39PM -0700, David Lang wrote: > >Hi! > > > >On 5/27/05, Kay Sievers <kay.sievers@vrfy.org> wrote: > >>On Fri, May 27, 2005 at 12:24:20PM -0700, Linus Torvalds wrote: > >>> Combining some of the features of the two (that über-cool revision > >>> history graph from gitk rules, for example) might be cool. I get the > >>> urge to do octopus-merges in the kernel just because of how good they > >>> look in gitk ;) ] > >> > >>I would like to show something like the graph too, but I don't really know > >>how to do this in html. Seems slippery if not impossible. > >>If anybody has a nice idea how to represent that, I will give it a try. > > > >Well, you could draw them in javascript, using > >http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm :-) > > > >Alternatively, you could use a fixed set of little images, a bar "|", a > >dot "o" and branches like "Y", "7" and "\". Obviously, octopus-merges > >are very difficult to draw using only those. > > you could look into SVG (scaleable vector graphics or some such thing) > that are supposed to be in the newest browsers (or soon to be added, I'm > not sure). this should let you do all the drawing nessasary reasonably > easily (if you are willing to limit users to that, which is probably not > that big of a problem for git) Well, technology that is "soon to be added" since years is probably not my favorite thing to base my work on. SVG could be nice, sure, but nearly noone is able to see it today and I expect, I will need to wait wait for another few years. :) Kay ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-28 1:03 ` Daniel Serpell 2005-05-28 2:51 ` David Lang @ 2005-05-28 8:42 ` Kay Sievers 2005-05-28 22:43 ` Benjamin Herrenschmidt 1 sibling, 1 reply; 42+ messages in thread From: Kay Sievers @ 2005-05-28 8:42 UTC (permalink / raw) To: Daniel Serpell; +Cc: Git Mailing List On Fri, May 27, 2005 at 09:03:24PM -0400, Daniel Serpell wrote: > Hi! > > On 5/27/05, Kay Sievers <kay.sievers@vrfy.org> wrote: > > On Fri, May 27, 2005 at 12:24:20PM -0700, Linus Torvalds wrote: > > > Combining some of the features of the two (that über-cool revision > > > history graph from gitk rules, for example) might be cool. I get the > > > urge to do octopus-merges in the kernel just because of how good they > > > look in gitk ;) ] > > > > I would like to show something like the graph too, but I don't really know > > how to do this in html. Seems slippery if not impossible. > > If anybody has a nice idea how to represent that, I will give it a try. > > Well, you could draw them in javascript, using > http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm :-) You know how that stuff works? :) It is a very nice idea for small stuff, but it uses a <div> for every pixel/line you draw and places this in the background and I expect it to kill your browser if you try to draw things like gitk does. > Alternatively, you could use a fixed set of little images, a bar "|", a > dot "o" and branches like "Y", "7" and "\". Obviously, octopus-merges > are very difficult to draw using only those. Did you look at gitk? With a all the crossing and long lines, you definitely need to draw the lines with colors. Otherwise you will see _nothing_, but random characters. :) > BTW, I tried searching on gitweb, and I think that found a problem, see: > http://ehlo.org/~kay/gitweb.cgi?p=git/git.git;a=search;s=check > At the bottom of the page, highlighting of the search term stops and the > commits are all the same color. Well, you see a list of files which contain the text, not the text itself. I can print the filename in red. :) Thanks, Kay ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-28 8:42 ` Kay Sievers @ 2005-05-28 22:43 ` Benjamin Herrenschmidt 0 siblings, 0 replies; 42+ messages in thread From: Benjamin Herrenschmidt @ 2005-05-28 22:43 UTC (permalink / raw) To: Kay Sievers; +Cc: Daniel Serpell, Git Mailing List On Sat, 2005-05-28 at 10:42 +0200, Kay Sievers wrote: > You know how that stuff works? :) It is a very nice idea for > small stuff, but it uses a <div> for every pixel/line you draw and > places this in the background and I expect it to kill your browser if > you try to draw things like gitk does. > > > Alternatively, you could use a fixed set of little images, a bar "|", a > > dot "o" and branches like "Y", "7" and "\". Obviously, octopus-merges > > are very difficult to draw using only those. > > Did you look at gitk? With a all the crossing and long lines, you definitely > need to draw the lines with colors. Otherwise you will see _nothing_, but > random characters. :) > > > BTW, I tried searching on gitweb, and I think that found a problem, see: > > http://ehlo.org/~kay/gitweb.cgi?p=git/git.git;a=search;s=check > > At the bottom of the page, highlighting of the search term stops and the > > commits are all the same color. > > Well, you see a list of files which contain the text, not the text > itself. I can print the filename in red. :) Best may be to have the server generate a picture ... ? Ben. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-27 19:24 More gitweb queries Linus Torvalds ` (4 preceding siblings ...) 2005-05-27 23:59 ` Kay Sievers @ 2005-05-30 23:03 ` Paul Mackerras 2005-05-31 2:27 ` Jeff Epler 5 siblings, 1 reply; 42+ messages in thread From: Paul Mackerras @ 2005-05-30 23:03 UTC (permalink / raw) To: Linus Torvalds; +Cc: Kay Sievers, Git Mailing List Linus Torvalds writes: > On that small note, I also find "gitk" very cool indeed, too bad about > the fact that tk/tcl seems to always end up looking so _ugly_. Is there > any way to get anti-aliased fonts and a less 'Motify' blocky look from > tcl/tk? Every time I see that, I feel like I'm back in the last century > or something. Tcl/Tk 8.5 will have anti-aliased fonts (8.5a2 is available on sourceforge). There is apparently some support for widget styling in 8.4 which I will have to look into. The Motif look doesn't bother me, but it does seem to bother you and Ben H. :) > Combining some of the features of the two (that über-cool revision > history graph from gitk rules, for example) might be cool. I get the > urge to do octopus-merges in the kernel just because of how good they > look in gitk ;) ] Yes, it's nice that a relatively simple algorithm without much lookahead produces reasonable results. Paul. ^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: More gitweb queries.. 2005-05-30 23:03 ` Paul Mackerras @ 2005-05-31 2:27 ` Jeff Epler 0 siblings, 0 replies; 42+ messages in thread From: Jeff Epler @ 2005-05-31 2:27 UTC (permalink / raw) To: Paul Mackerras; +Cc: Linus Torvalds, Kay Sievers, Git Mailing List [-- Attachment #1: Type: text/plain, Size: 1599 bytes --] On Tue, May 31, 2005 at 09:03:06AM +1000, Paul Mackerras wrote: > Tcl/Tk 8.5 will have anti-aliased fonts (8.5a2 is available on > sourceforge). There is apparently some support for widget styling in > 8.4 which I will have to look into. The Motif look doesn't bother me, > but it does seem to bother you and Ben H. :) I have a somewhat stale copy of Tk 8.5 CVS and Tile CVS here. I tried gitk on it. I added the lines package require tile; namespace import -force ::ttk::* tile::setTheme alt near the top, which is a simple way to use the new "tile" theme engine with an included, redmond-style theme. Here's a screenshot: http://emergent.unpy.net/index.cgi-files/sandbox/gitk-tk85+tile.png I have no idea why the menu is in greek. The rest of the buttons and labels were too, before I switched to Tile for rendering them. I get an odd traceback that I don't get using the same gitk with tcl-8.4.5: number too large to represent as a Posix time while executing "return -options $opts $retval" (procedure "ConvertUTCToLocal" line 20) invoked from within "ConvertUTCToLocal $date[set date {}] $timezone" (procedure "::tcl::clock::format" line 10) invoked from within "clock format $audate -format "%Y-%m-%d %H:%M:%S"" (procedure "readcommit" line 37) invoked from within "readcommit $p" (procedure "drawgraph" line 52) invoked from within "drawgraph" ("after" script) which stops the graph before it's complete. Laying out the graph seems much slower than with tk8.4. Jeff [-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --] ^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2005-05-31 2:25 UTC | newest] Thread overview: 42+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2005-05-27 19:24 More gitweb queries Linus Torvalds 2005-05-27 19:29 ` Thomas Glanzmann 2005-05-27 19:52 ` Junio C Hamano 2005-05-27 19:55 ` Thomas Glanzmann 2005-05-27 20:13 ` Junio C Hamano 2005-05-27 20:32 ` Thomas Glanzmann 2005-05-27 20:40 ` Junio C Hamano 2005-05-27 22:00 ` Linus Torvalds 2005-05-27 22:04 ` Thomas Glanzmann 2005-05-28 2:26 ` Junio C Hamano 2005-05-29 23:02 ` Thomas Glanzmann 2005-05-29 23:10 ` Thomas Glanzmann 2005-05-29 23:16 ` Thomas Glanzmann 2005-05-29 23:46 ` Thomas Glanzmann 2005-05-29 23:56 ` Thomas Glanzmann 2005-05-30 0:50 ` Junio C Hamano 2005-05-30 0:57 ` Junio C Hamano 2005-05-30 1:33 ` Thomas Glanzmann 2005-05-30 1:30 ` Thomas Glanzmann 2005-05-30 7:57 ` Junio C Hamano 2005-05-30 8:36 ` Thomas Glanzmann 2005-05-30 9:21 ` Thomas Glanzmann 2005-05-30 10:20 ` Junio C Hamano 2005-05-30 12:11 ` Thomas Glanzmann 2005-05-30 17:54 ` Junio C Hamano 2005-05-27 20:17 ` Linus Torvalds 2005-05-27 19:54 ` Junio C Hamano 2005-05-27 19:58 ` Thomas Glanzmann 2005-05-27 20:03 ` Linus Torvalds 2005-05-27 20:24 ` Junio C Hamano 2005-05-27 19:31 ` Thomas Glanzmann 2005-05-27 19:32 ` Junio C Hamano 2005-05-27 19:48 ` Linus Torvalds 2005-05-27 23:12 ` Benjamin Herrenschmidt 2005-05-27 23:59 ` Kay Sievers 2005-05-28 1:03 ` Daniel Serpell 2005-05-28 2:51 ` David Lang 2005-05-28 10:56 ` Kay Sievers 2005-05-28 8:42 ` Kay Sievers 2005-05-28 22:43 ` Benjamin Herrenschmidt 2005-05-30 23:03 ` Paul Mackerras 2005-05-31 2:27 ` Jeff Epler
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).