Re: Improving merge of tricky conflicts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jul 22, 2020 at 10:26:04AM -0700, Junio C Hamano wrote:

> > The big downside here, of course, is that it's showing the diff for the
> > whole file, not just one hunk (on the other hand, I often find the
> > trickiest conflicts are ones where the changes unexpectedly span
> > multiple hunks).
> 
> Yup, I often find myself comparing the base part (lines between |||
> and ===) with our part (lines between <<< and |||) and their part
> (lines between === and >>>) while looking at the diff3 output to see
> what unique change each side did, in order to come up with a
> conflict resolution.
> 
> I do this often enough to wonder if I should write a small "filter"
> that I can pipe a whole "diff3" <<< ... ||| ... === ... >>> region
> to and convert it into to diffs, but not often enough to motivate
> me to actually write one ;-).

I would definitely have found that useful before (usually when one side
made a tiny one-line change and the other side deleted or drastically
changed a huge chunk).

It might even be possible to stuff it into xdiff's fill_conflict_hunk().
We have all of the data there, and xdiff in theory can make diffs. :) It
might be easier to prototype it as an external filter, though.

Something like the script below seems to work; you can run whole files
through it, or do something like ":10,20r!perl foo.pl" in vim to filter
a snippet. I won't be at all surprised if somebody more familiar with
vim tells me that you can already do something way better than this
(I've always found vimdiff pretty confusing).

-- >8 --
#!/usr/bin/perl

use File::Temp;
use strict;

my (@base, @ours, @theirs);
my $state;

sub flush {
  print @ours;
  print "|||||||\n";
  show_diff(base => \@base, ours => \@ours);
  print "|||||||\n";
  show_diff(base => \@base, theirs => \@theirs);
  print "=======\n";
  print @theirs;
  @ours = @base = @theirs = ();
}

sub show_diff {
  my ($pre_name, $pre_data, $post_name, $post_data) = @_;

  my $pre = File::Temp->new;
  print $pre @$pre_data;
  $pre->flush;

  my $post = File::Temp->new;
  print $post @$post_data;
  $post->flush;

  open(my $diff, '-|', qw(diff -u), $pre->filename, $post->filename);
  # throw away file header, which just mentions tempfiles, and replace
  # it with our own
  <$diff>; <$diff>;
  print "--- $pre_name\n";
  print "+++ $post_name\n";
  while (<$diff>) {
    print;
  }
}

sub state_none {
  if (/^<{7}/) { $state = \&state_ours }
  print
}

sub state_ours {
  if (/^\|{7}/) { $state = \&state_base }
  else { push @ours, $_ }
}

sub state_base {
  if (/^={7}/) { $state = \&state_theirs }
  else { push @base, $_ }
}

sub state_theirs {
  if (/^>{7}/) { flush(); print; $state = \&state_none }
  else { push @theirs, $_ }
}

$state = \&state_none;
while (<>) {
  $state->();
}



[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux