* Re: git-fixup-assigner.perl -- automatically decide where to "fixup!"
2010-12-14 2:09 git-fixup-assigner.perl -- automatically decide where to "fixup!" Thomas Rast
@ 2011-10-26 14:37 ` fREW Schmidt
2011-10-26 19:40 ` Thomas Rast
0 siblings, 1 reply; 3+ messages in thread
From: fREW Schmidt @ 2011-10-26 14:37 UTC (permalink / raw)
To: Thomas Rast; +Cc: git
On Mon, Dec 13, 2010 at 8:09 PM, Thomas Rast <trast@student.ethz.ch> wrote:
>
> While cleaning up the 'log -L' series I gathered a large number of
> little fixups, and decided it would be smart if git could
> automatically figure out where to put them.
>
> It works like this:
>
> * Split the diff by hunk. I'm using -U1 here for finer splits, but it
> could be tunable.
>
> * For each hunk, run blame to find out which commit's lines were
> affected.
>
> * Group the hunks by this commit, and output them with a suitable
> command to make a fixup.
>
> My git-fixup is
>
> $ g config alias.fixup
> !sh -c 'r=$1; git commit -m"fixup! $(git log -1 --pretty=%s $r)"' -
>
> so that is "suitable".
>
> You would run it with the changes unstaged in your tree as
>
> ./git-fixup-assigner.perl > fixups
>
> and can then review with 'less fixups', or run 'sh fixups' to commit
> them.
>
> It's certainly not perfect, notably the detection logic should ignore
> context, but it got the job done.
>
> --- 8< ---
> #!/usr/bin/perl
>
> use warnings;
> use strict;
>
> sub parse_hunk_header {
> my ($line) = @_;
> my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
> $line =~ /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
> $o_cnt = 1 unless defined $o_cnt;
> $n_cnt = 1 unless defined $n_cnt;
> return ($o_ofs, $o_cnt, $n_ofs, $n_cnt);
> }
>
> sub find_commit {
> my ($file, $begin, $end) = @_;
> my $blame;
> open($blame, '-|', 'git', '--no-pager', 'blame', 'HEAD', "-L$begin,$end", $file) or die;
> my %candidate;
> while (<$blame>) {
> $candidate{$1} += 1 if /^([0-9a-f]+)/;
> }
> close $blame or die;
> my @sorted = sort { $candidate{$b} <=> $candidate{$a} } keys %candidate;
> if (1 < scalar @sorted) {
> print STDERR "ambiguous split $file:$begin..$end\n";
> foreach my $c (@sorted) {
> print STDERR "\t$candidate{$c}\t$c\n";
> }
> }
> return $sorted[0];
> }
>
> my $diff;
> open($diff, '-|', 'git', '--no-pager', 'diff', '-U1') or die;
>
> my %by_commit;
> my @cur_hunk = ();
> my $cur_commit;
> my ($filename, $prefilename, $postfilename);
>
> while (<$diff>) {
> if (m{^diff --git ./(.*) ./\1$}) {
> if (@cur_hunk) {
> push @{$by_commit{$cur_commit}{$filename}}, @cur_hunk;
> @cur_hunk = ();
> }
> $filename = $1;
> $prefilename = "./" . $1;
> $postfilename = "./" . $1;
> } elsif (m{^index}) {
> # ignore
> } elsif (m{^new file}) {
> $prefilename = '/dev/null';
> } elsif (m{^delete file}) {
> $postfilename = '/dev/null';
> } elsif (m{^--- $prefilename$}) {
> } elsif (m{^\+\+\+ $postfilename$}) {
> } elsif (m{^@@ }) {
> if (@cur_hunk) {
> push @{$by_commit{$cur_commit}{$filename}}, @cur_hunk;
> @cur_hunk = ();
> }
> push @cur_hunk, $_;
> die "I don't handle this diff" if ($prefilename ne $postfilename);
> my ($o_ofs, $o_cnt, $n_ofs, $n_cnt)
> = parse_hunk_header($_);
> my $o_end = $o_ofs + $o_cnt - 1;
> $cur_commit = find_commit($filename, $o_ofs, $o_end);
> } elsif (m{^[-+ \\]}) {
> push @cur_hunk, $_;
> } else {
> die "unhandled diff line: '$_'";
> }
> }
>
> close $diff or die;
>
> if (@cur_hunk) {
> push @{$by_commit{$cur_commit}{$filename}}, @cur_hunk;
> @cur_hunk = ();
> }
>
> print "#!/bin/sh\n\n";
>
> foreach my $commit (keys %by_commit) {
> print "git apply --cached <<EOF\n";
> foreach my $filename (keys %{$by_commit{$commit}}) {
> print "diff --git a/$filename b/$filename\n";
> print "--- a/$filename\n";
> print "+++ b/$filename\n";
> print @{$by_commit{$commit}{$filename}};
> }
> print "EOF\n\n";
> print "git fixup $commit\n\n";
> }
> --- >8 ---
This is super neat, but I'm having trouble getting it to work.
First, I made one small change:
- print "git fixup $commit\n\n";
+ print "git commit --fixup $commit\n\n";
To try it out I made a very simple change:
$ git diff
diff --git a/App/lib/MyApp/Controller/DashboardTemplates.pm
b/App/lib/MyApp/Controller/DashboardTemplates.pm
index aefdc3c..a53b534 100644
--- a/App/lib/MyApp/Controller/DashboardTemplates.pm
+++ b/App/lib/MyApp/Controller/DashboardTemplates.pm
@@ -13,7 +13,7 @@ cat_has $_ => ( is => 'rw' ) for qw(set);
sub base : Chained('/') PathPart('dashboard_templates') CaptureArgs(0) {
my ($self, $c) = @_;
- $self->set($c,
$c->model('DB')->schema->kiokudb_handle->lookup('dashboard
templates'));
+ $self->set($c, $c->model('Kioku')->lookup('dashboard templates'));
}
my $renderer = sub {
Then I tried it out
$ git fixup-assigner.pl > fixups && less fixups
#!/bin/sh
git apply --cached <<EOF
diff --git a/App/lib/MyApp/Controller/DashboardTemplates.pm
b/App/lib/MyApp/Controller/DashboardTemplates.pm
--- a/App/lib/MyApp/Controller/DashboardTemplates.pm
+++ b/App/lib/MyApp/Controller/DashboardTemplates.pm
@@ -15,3 +15,3 @@ sub base : Chained('/')
PathPart('dashboard_templates') CaptureArgs(0) {
my ($self, $c) = @_;
- $self->set($c,
$c->model('DB')->schema->kiokudb_handle->lookup('dashboard
templates'));
+ $self->set($c, $c->model('Kioku')->lookup('dashboard templates'));
}
EOF
git commit --fixup 7765cbd2
Looks fine to me. But then I try to use it:
$ git checkout . && sh fixups
error: patch failed: App/lib/MyApp/Controller/DashboardTemplates.pm:15
error: App/lib/MyApp/Controller/DashboardTemplates.pm: patch does not apply
Any ideas what I'm doing wrong?
--
fREW Schmidt
http://blog.afoolishmanifesto.com
^ permalink raw reply [flat|nested] 3+ messages in thread