]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/SolverGit.pm
Merge remote-tracking branch 'origin/help-color'
[public-inbox.git] / lib / PublicInbox / SolverGit.pm
index 59d2c93ca4274af1b831e9b15a1a86e9cb8bd538..463a9b6906ba069f87a049ea7f4fc40a51334153 100644 (file)
@@ -23,6 +23,7 @@ use URI::Escape qw(uri_escape_utf8);
 # headroom into this.
 use POSIX qw(sysconf _SC_ARG_MAX);
 my $ARG_SIZE_MAX = (sysconf(_SC_ARG_MAX) || 4096) - 2048;
+my $OID_MIN = 7;
 
 # By default, "git format-patch" generates filenames with a four-digit
 # prefix, so that means 9999 patch series are OK, right? :>
@@ -170,6 +171,13 @@ sub extract_diff ($$$$$) {
 
 sub path_searchable ($) { defined($_[0]) && $_[0] =~ m!\A[\w/\. \-]+\z! }
 
+# ".." appears in path names, which confuses Xapian into treating
+# it as a range query.  So we split on ".." since Xapian breaks
+# on punctuation anyways:
+sub filename_query ($) {
+       join('', map { qq( dfn:"$_") } split(/\.\./, $_[0]));
+}
+
 sub find_extract_diff ($$$) {
        my ($self, $ibx, $want) = @_;
        my $srch = $ibx->search or return;
@@ -187,11 +195,11 @@ sub find_extract_diff ($$$) {
 
        my $path_b = $want->{path_b};
        if (path_searchable($path_b)) {
-               $q .= qq{ dfn:"$path_b"};
+               $q .= filename_query($path_b);
 
                my $path_a = $want->{path_a};
                if (path_searchable($path_a) && $path_a ne $path_b) {
-                       $q .= qq{ dfn:"$path_a"};
+                       $q .= filename_query($path_a);
                }
        }
 
@@ -294,6 +302,26 @@ sub extract_old_mode ($) {
        '100644';
 }
 
+sub do_finish ($$) {
+       my ($self, $user_cb) = @_;
+       my $found = $self->{found};
+       my $oid_want = $self->{oid_want};
+       if (my $exists = $found->{$oid_want}) {
+               return $user_cb->($exists);
+       }
+
+       # let git disambiguate if oid_want was too short,
+       # but long enough to be unambiguous:
+       my $tmp_git = $self->{tmp_git};
+       if (my @res = $tmp_git->check($oid_want)) {
+               return $user_cb->($found->{$res[0]});
+       }
+       if (my $err = $tmp_git->last_check_err) {
+               dbg($self, $err);
+       }
+       $user_cb->(undef);
+}
+
 sub do_step ($) {
        my ($self) = @_;
        eval {
@@ -315,8 +343,8 @@ sub do_step ($) {
                # our result: (which may be undef)
                # Other steps may call user_cb to terminate prematurely
                # on error
-               } elsif (my $ucb = delete($self->{user_cb})) {
-                       $ucb->($self->{found}->{$self->{oid_want}});
+               } elsif (my $user_cb = delete($self->{user_cb})) {
+                       do_finish($self, $user_cb);
                } else {
                        die 'about to call user_cb twice'; # Oops :x
                }
@@ -346,7 +374,13 @@ sub next_step ($) {
 
 sub mark_found ($$$) {
        my ($self, $oid, $found_info) = @_;
-       $self->{found}->{$oid} = $found_info;
+       my $found = $self->{found};
+       $found->{$oid} = $found_info;
+       my $oid_cur = $found_info->[1];
+       while ($oid_cur ne $oid && length($oid_cur) > $OID_MIN) {
+               $found->{$oid_cur} = $found_info;
+               chop($oid_cur);
+       }
 }
 
 sub parse_ls_files ($$$$) {
@@ -478,6 +512,14 @@ sub resolve_patch ($$) {
                }
                return next_step($self); # onto the next todo item
        }
+       if (length($cur_want) > $OID_MIN) {
+               chop($cur_want);
+               dbg($self, "retrying $want->{oid_b} as $cur_want");
+               $want->{oid_b} = $cur_want;
+               push @{$self->{todo}}, $want;
+               return next_step($self); # retry with shorter abbrev
+       }
+
        dbg($self, "could not find $cur_want");
        eval { delete($self->{user_cb})->(undef) }; # not found! :<
        die "E: $@" if $@;
@@ -516,7 +558,7 @@ sub solve ($$$$$) {
        $self->{todo} = [ { %$hints, oid_b => $oid_want } ];
        $self->{patches} = []; # [ $di, $di, ... ]
        $self->{found} = {}; # { abbr => [ ::Git, oid, type, size, $di ] }
-       $self->{tmp} = File::Temp->newdir('solver.tmp-XXXXXXXX', TMPDIR => 1);
+       $self->{tmp} = File::Temp->newdir("solver.$oid_want-XXXXXXXX", TMPDIR => 1);
 
        dbg($self, "solving $oid_want ...");
        my $step_cb = step_cb($self);