chiark / gitweb /
git-debrebase: Prep for avoid read-tree in walk when not rewriting
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 19 Aug 2018 11:35:12 +0000 (12:35 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 19 Aug 2018 14:53:19 +0000 (15:53 +0100)
Because the git index is a flat list of files, not a directory
hierarchy, git-read-tree can be very slow.  We want to avoid it
if possible.

Introduce a new way of working, where we defer calls to read-tree.

In detail:
 * $read_tree_upstream and $read_tree_debian, which are the only ways
   that walk invokes read-tree, simply record their argument;
 * The actual read-tree is done just before the write-tree and
   commit generation.  read_tree_upstream, conveniently, does both
   halves, if it gets both arguments.
 * Put all of the read-tree and commit regeneration in a branch
   triggered if we are rewriting or "trying to be careful".

Right now we are always careful, but I have tested this with a hack
setting $opt_careful to 0, and the performance on the example branch
in #905995 is much improved: 3.4s rather than 77s for `git-debrebase'.

This is not suitable for enabling in its current form.  I want to
actuallly abolish the $opt_careful, and, instead, check with
get_differs or git-cat-file that the tree segments, and the parent
lists, are identical.

When we do that we won't be checking any more that the commit
generation can generate identical commits when not rewriting.  But it
doesn't really matter much any more provided the commits are
well-formed and right, and the test suite will check that.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
git-debrebase

index 1e205c6..3d4bb60 100755 (executable)
@@ -50,9 +50,11 @@ usages:
 See git-debrebase(1), git-debrebase(5), dgit-maint-debrebase(7) (in dgit).
 END
 
-our ($opt_force, $opt_noop_ok, @opt_anchors);
+our ($opt_force, $opt_careful, $opt_noop_ok, @opt_anchors);
 our ($opt_defaultcmd_interactive);
 
+$opt_careful = 1;
+
 our $us = qw(git-debrebase);
 
 our $wrecknoteprefix = 'refs/debrebase/wreckage';
@@ -1434,17 +1436,11 @@ sub walk ($;$$$) {
     in_workarea sub {
        mkdir $rd or $!==EEXIST or die $!;
        my $current_method;
-       runcmd @git, qw(read-tree), $build;
-
-       my $read_tree_upstream = sub {
-           my ($treeish) = @_;
-           read_tree_upstream $treeish, 0, $build;
-       };
+       my $want_tree_debian = $build;
+       my $want_tree_upstream = $build;
 
-       my $read_tree_debian = sub {
-           my ($treeish) = @_;
-           read_tree_debian($treeish);
-       };
+       my $read_tree_upstream = sub { ($want_tree_upstream) = @_; };
+       my $read_tree_debian = sub { ($want_tree_debian) = @_; };
 
        foreach my $cl (qw(Debian), (reverse @brw_cl),
                        { SpecialMethod => 'RecordBreakwaterTip' },
@@ -1533,27 +1529,34 @@ sub walk ($;$$$) {
                    printdebug "WALK REWRITING NOW cl=$cl procd=$procd\n";
                }
            }
-           my $newtree = cmdoutput @git, qw(write-tree);
-           my $ch = $cl->{Hdr};
-           $ch =~ s{^tree .*}{tree $newtree}m or confess "$ch ?";
-           $ch =~ s{^parent .*\n}{}mg;
-           $ch =~ s{(?=^author)}{
-               join '', map { "parent $_\n" } @parents
-           }me or confess "$ch ?";
-           if ($rewriting) {
-               $ch =~ s{^committer .*$}{$committer_authline}m
-                   or confess "$ch ?";
+           if ($rewriting || $opt_careful) {
+               read_tree_upstream $want_tree_upstream, 0, $want_tree_debian;
+
+               my $newtree = cmdoutput @git, qw(write-tree);
+               my $ch = $cl->{Hdr};
+               $ch =~ s{^tree .*}{tree $newtree}m or confess "$ch ?";
+               $ch =~ s{^parent .*\n}{}mg;
+               $ch =~ s{(?=^author)}{
+                   join '', map { "parent $_\n" } @parents
+               }me or confess "$ch ?";
+               if ($rewriting) {
+                   $ch =~ s{^committer .*$}{$committer_authline}m
+                       or confess "$ch ?";
+               }
+               my $cf = "$rd/m$rewriting";
+               open CD, ">", $cf or die $!;
+               print CD $ch, "\n", $cl->{Msg} or die $!;
+               close CD or die $!;
+               my @cmd = (@git, qw(hash-object));
+               push @cmd, qw(-w) if $rewriting;
+               push @cmd, qw(-t commit), $cf;
+               my $newcommit = cmdoutput @cmd;
+               confess "$ch ?" unless $rewriting
+                   or $newcommit eq $cl->{CommitId};
+               $build = $newcommit;
+           } else {
+               $build = $cl->{CommitId};
            }
-           my $cf = "$rd/m$rewriting";
-           open CD, ">", $cf or die $!;
-           print CD $ch, "\n", $cl->{Msg} or die $!;
-           close CD or die $!;
-           my @cmd = (@git, qw(hash-object));
-           push @cmd, qw(-w) if $rewriting;
-           push @cmd, qw(-t commit), $cf;
-           my $newcommit = cmdoutput @cmd;
-           confess "$ch ?" unless $rewriting or $newcommit eq $cl->{CommitId};
-           $build = $newcommit;
             if (grep { $method eq $_ } qw(DgitImportUpstreamUpdate)) {
                 $last_anchor = $cur;
             }