+sub index_diff ($$$) {
+ my ($self, $txt, $doc) = @_;
+ my %seen;
+ my $in_diff;
+ my @xnq;
+ my $xnq = \@xnq;
+ foreach (split(/\n/, $txt)) {
+ if ($in_diff && s/^ //) { # diff context
+ index_diff_inc($self, $_, 'XDFCTX', $xnq);
+ } elsif (/^-- $/) { # email signature begins
+ $in_diff = undef;
+ } elsif (m!^diff --git ("?a/.+) ("?b/.+)\z!) {
+ my ($fa, $fb) = ($1, $2);
+ my $fn = (split('/', git_unquote($fa), 2))[1];
+ $seen{$fn}++ or index_diff_inc($self, $fn, 'XDFN', $xnq);
+ $fn = (split('/', git_unquote($fb), 2))[1];
+ $seen{$fn}++ or index_diff_inc($self, $fn, 'XDFN', $xnq);
+ $in_diff = 1;
+ # traditional diff:
+ } elsif (m/^diff -(.+) (\S+) (\S+)$/) {
+ my ($opt, $fa, $fb) = ($1, $2, $3);
+ push @xnq, $_;
+ # only support unified:
+ next unless $opt =~ /[uU]/;
+ $in_diff = index_old_diff_fn($self, \%seen, $fa, $fb,
+ $xnq);
+ } elsif (m!^--- ("?a/.+)!) {
+ my $fn = $1;
+ $fn = (split('/', git_unquote($fn), 2))[1];
+ $seen{$fn}++ or index_diff_inc($self, $fn, 'XDFN', $xnq);
+ $in_diff = 1;
+ } elsif (m!^\+\+\+ ("?b/.+)!) {
+ my $fn = $1;
+ $fn = (split('/', git_unquote($fn), 2))[1];
+ $seen{$fn}++ or index_diff_inc($self, $fn, 'XDFN', $xnq);
+ $in_diff = 1;
+ } elsif (/^--- (\S+)/) {
+ $in_diff = $1;
+ push @xnq, $_;
+ } elsif (defined $in_diff && /^\+\+\+ (\S+)/) {
+ $in_diff = index_old_diff_fn($self, \%seen, $in_diff,
+ $1, $xnq);
+ } elsif ($in_diff && s/^\+//) { # diff added
+ index_diff_inc($self, $_, 'XDFB', $xnq);
+ } elsif ($in_diff && s/^-//) { # diff removed
+ index_diff_inc($self, $_, 'XDFA', $xnq);
+ } elsif (m!^index ([a-f0-9]+)\.\.([a-f0-9]+)!) {
+ my ($ba, $bb) = ($1, $2);
+ index_git_blob_id($doc, 'XDFPRE', $ba);
+ index_git_blob_id($doc, 'XDFPOST', $bb);
+ $in_diff = 1;
+ } elsif (/^@@ (?:\S+) (?:\S+) @@\s*$/) {
+ # traditional diff w/o -p
+ } elsif (/^@@ (?:\S+) (?:\S+) @@\s*(\S+.*)$/) {
+ # hunk header context
+ index_diff_inc($self, $1, 'XDFHH', $xnq);
+ # ignore the following lines:
+ } elsif (/^(?:dis)similarity index/ ||
+ /^(?:old|new) mode/ ||
+ /^(?:deleted|new) file mode/ ||
+ /^(?:copy|rename) (?:from|to) / ||
+ /^(?:dis)?similarity index / ||
+ /^\\ No newline at end of file/ ||
+ /^Binary files .* differ/) {
+ push @xnq, $_;
+ } elsif ($_ eq '') {
+ # possible to be in diff context, some mail may be
+ # stripped by MUA or even GNU diff(1). "git apply"
+ # treats a bare "\n" as diff context, too
+ } else {
+ push @xnq, $_;
+ warn "non-diff line: $_\n" if DEBUG && $_ ne '';
+ $in_diff = undef;
+ }
+ }