Merge branch 'for-junio' of git://bogomips.org/git-svn
* 'for-junio' of git://bogomips.org/git-svn: git-svn: use SVN::Ra::get_dir2 when possible git-svn: add space after "W:" prefix in warning git-svn: (cleanup) remove editor param passing git-svn: prepare SVN::Ra config pieces once Git.pm: add specified name to tempfile template git-svn: disable _rev_list memoization git-svn: save a little memory as fetch progresses git-svn: remove unnecessary DESTROY override git-svn: reload RA every log-window-size git-svn.txt: advertise pushurl with dcommit git-svn: remove mergeinfo rev caching git-svn: cache only mergeinfo revisions git-svn: reduce check_cherry_pick cache overhead git-svn: only look at the root path for svn:mergeinfo git-svn: only look at the new parts of svn:mergeinfo
This commit is contained in:
commit
ef59f324b0
@ -252,6 +252,10 @@ Use of 'dcommit' is preferred to 'set-tree' (below).
|
||||
config key: svn-remote.<name>.commiturl
|
||||
config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
|
||||
+
|
||||
Note that the SVN URL of the commiturl config key includes the SVN branch.
|
||||
If you rather want to set the commit URL for an entire SVN repository use
|
||||
svn-remote.<name>.pushurl instead.
|
||||
+
|
||||
Using this option for any other purpose (don't ask) is very strongly
|
||||
discouraged.
|
||||
|
||||
|
@ -1294,8 +1294,11 @@ sub _temp_cache {
|
||||
$tmpdir = $self->repo_path();
|
||||
}
|
||||
|
||||
my $n = $name;
|
||||
$n =~ s/\W/_/g; # no strange chars
|
||||
|
||||
($$temp_fd, $fname) = File::Temp::tempfile(
|
||||
'Git_XXXXXX', UNLINK => 1, DIR => $tmpdir,
|
||||
"Git_${n}_XXXXXX", UNLINK => 1, DIR => $tmpdir,
|
||||
) or throw Error::Simple("couldn't open new temp file");
|
||||
|
||||
$$temp_fd->autoflush;
|
||||
|
125
perl/Git/SVN.pm
125
perl/Git/SVN.pm
@ -1178,7 +1178,7 @@ sub find_parent_branch {
|
||||
or die "SVN connection failed somewhere...\n";
|
||||
}
|
||||
print STDERR "Successfully followed parent\n" unless $::_q > 1;
|
||||
return $self->make_log_entry($rev, [$parent], $ed);
|
||||
return $self->make_log_entry($rev, [$parent], $ed, $r0, $branch_from);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
@ -1210,7 +1210,7 @@ sub do_fetch {
|
||||
unless ($self->ra->gs_do_update($last_rev, $rev, $self, $ed)) {
|
||||
die "SVN connection failed somewhere...\n";
|
||||
}
|
||||
$self->make_log_entry($rev, \@parents, $ed);
|
||||
$self->make_log_entry($rev, \@parents, $ed, $last_rev, $self->path);
|
||||
}
|
||||
|
||||
sub mkemptydirs {
|
||||
@ -1433,7 +1433,7 @@ sub check_author {
|
||||
}
|
||||
|
||||
sub find_extra_svk_parents {
|
||||
my ($self, $ed, $tickets, $parents) = @_;
|
||||
my ($self, $tickets, $parents) = @_;
|
||||
# aha! svk:merge property changed...
|
||||
my @tickets = split "\n", $tickets;
|
||||
my @known_parents;
|
||||
@ -1478,9 +1478,9 @@ sub find_extra_svk_parents {
|
||||
sub lookup_svn_merge {
|
||||
my $uuid = shift;
|
||||
my $url = shift;
|
||||
my $merge = shift;
|
||||
my $source = shift;
|
||||
my $revs = shift;
|
||||
|
||||
my ($source, $revs) = split ":", $merge;
|
||||
my $path = $source;
|
||||
$path =~ s{^/}{};
|
||||
my $gs = Git::SVN->find_by_url($url.$source, $url, $path);
|
||||
@ -1537,7 +1537,7 @@ sub _rev_list {
|
||||
@rv;
|
||||
}
|
||||
|
||||
sub check_cherry_pick {
|
||||
sub check_cherry_pick2 {
|
||||
my $base = shift;
|
||||
my $tip = shift;
|
||||
my $parents = shift;
|
||||
@ -1552,7 +1552,8 @@ sub check_cherry_pick {
|
||||
delete $commits{$commit};
|
||||
}
|
||||
}
|
||||
return (keys %commits);
|
||||
my @k = (keys %commits);
|
||||
return (scalar @k, $k[0]);
|
||||
}
|
||||
|
||||
sub has_no_changes {
|
||||
@ -1597,9 +1598,8 @@ sub tie_for_persistent_memoization {
|
||||
mkpath([$cache_path]) unless -d $cache_path;
|
||||
|
||||
my %lookup_svn_merge_cache;
|
||||
my %check_cherry_pick_cache;
|
||||
my %check_cherry_pick2_cache;
|
||||
my %has_no_changes_cache;
|
||||
my %_rev_list_cache;
|
||||
|
||||
tie_for_persistent_memoization(\%lookup_svn_merge_cache,
|
||||
"$cache_path/lookup_svn_merge");
|
||||
@ -1608,11 +1608,11 @@ sub tie_for_persistent_memoization {
|
||||
LIST_CACHE => ['HASH' => \%lookup_svn_merge_cache],
|
||||
;
|
||||
|
||||
tie_for_persistent_memoization(\%check_cherry_pick_cache,
|
||||
"$cache_path/check_cherry_pick");
|
||||
memoize 'check_cherry_pick',
|
||||
tie_for_persistent_memoization(\%check_cherry_pick2_cache,
|
||||
"$cache_path/check_cherry_pick2");
|
||||
memoize 'check_cherry_pick2',
|
||||
SCALAR_CACHE => 'FAULT',
|
||||
LIST_CACHE => ['HASH' => \%check_cherry_pick_cache],
|
||||
LIST_CACHE => ['HASH' => \%check_cherry_pick2_cache],
|
||||
;
|
||||
|
||||
tie_for_persistent_memoization(\%has_no_changes_cache,
|
||||
@ -1621,14 +1621,6 @@ sub tie_for_persistent_memoization {
|
||||
SCALAR_CACHE => ['HASH' => \%has_no_changes_cache],
|
||||
LIST_CACHE => 'FAULT',
|
||||
;
|
||||
|
||||
tie_for_persistent_memoization(\%_rev_list_cache,
|
||||
"$cache_path/_rev_list");
|
||||
memoize '_rev_list',
|
||||
SCALAR_CACHE => 'FAULT',
|
||||
LIST_CACHE => ['HASH' => \%_rev_list_cache],
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
sub unmemoize_svn_mergeinfo_functions {
|
||||
@ -1636,9 +1628,8 @@ sub tie_for_persistent_memoization {
|
||||
$memoized = 0;
|
||||
|
||||
Memoize::unmemoize 'lookup_svn_merge';
|
||||
Memoize::unmemoize 'check_cherry_pick';
|
||||
Memoize::unmemoize 'check_cherry_pick2';
|
||||
Memoize::unmemoize 'has_no_changes';
|
||||
Memoize::unmemoize '_rev_list';
|
||||
}
|
||||
|
||||
sub clear_memoized_mergeinfo_caches {
|
||||
@ -1648,7 +1639,8 @@ sub tie_for_persistent_memoization {
|
||||
return unless -d $cache_path;
|
||||
|
||||
for my $cache_file (("$cache_path/lookup_svn_merge",
|
||||
"$cache_path/check_cherry_pick",
|
||||
"$cache_path/check_cherry_pick", # old
|
||||
"$cache_path/check_cherry_pick2",
|
||||
"$cache_path/has_no_changes")) {
|
||||
for my $suffix (qw(yaml db)) {
|
||||
my $file = "$cache_file.$suffix";
|
||||
@ -1702,11 +1694,49 @@ sub parents_exclude {
|
||||
return @excluded;
|
||||
}
|
||||
|
||||
# Compute what's new in svn:mergeinfo.
|
||||
sub mergeinfo_changes {
|
||||
my ($self, $old_path, $old_rev, $path, $rev, $mergeinfo_prop) = @_;
|
||||
my %minfo = map {split ":", $_ } split "\n", $mergeinfo_prop;
|
||||
my $old_minfo = {};
|
||||
|
||||
my $ra = $self->ra;
|
||||
# Give up if $old_path isn't in the repo.
|
||||
# This is probably a merge on a subtree.
|
||||
if ($ra->check_path($old_path, $old_rev) != $SVN::Node::dir) {
|
||||
warn "W: ignoring svn:mergeinfo on $old_path, ",
|
||||
"directory didn't exist in r$old_rev\n";
|
||||
return {};
|
||||
}
|
||||
my (undef, undef, $props) = $ra->get_dir($old_path, $old_rev);
|
||||
if (defined $props->{"svn:mergeinfo"}) {
|
||||
my %omi = map {split ":", $_ } split "\n",
|
||||
$props->{"svn:mergeinfo"};
|
||||
$old_minfo = \%omi;
|
||||
}
|
||||
|
||||
my %changes = ();
|
||||
foreach my $p (keys %minfo) {
|
||||
my $a = $old_minfo->{$p} || "";
|
||||
my $b = $minfo{$p};
|
||||
# Omit merged branches whose ranges lists are unchanged.
|
||||
next if $a eq $b;
|
||||
# Remove any common range list prefix.
|
||||
($a ^ $b) =~ /^[\0]*/;
|
||||
my $common_prefix = rindex $b, ",", $+[0] - 1;
|
||||
$changes{$p} = substr $b, $common_prefix + 1;
|
||||
}
|
||||
print STDERR "Checking svn:mergeinfo changes since r$old_rev: ",
|
||||
scalar(keys %minfo), " sources, ",
|
||||
scalar(keys %changes), " changed\n";
|
||||
|
||||
return \%changes;
|
||||
}
|
||||
|
||||
# note: this function should only be called if the various dirprops
|
||||
# have actually changed
|
||||
sub find_extra_svn_parents {
|
||||
my ($self, $ed, $mergeinfo, $parents) = @_;
|
||||
my ($self, $mergeinfo, $parents) = @_;
|
||||
# aha! svk:merge property changed...
|
||||
|
||||
memoize_svn_mergeinfo_functions();
|
||||
@ -1715,14 +1745,15 @@ sub find_extra_svn_parents {
|
||||
# history. Then, we figure out which git revisions are in
|
||||
# that tip, but not this revision. If all of those revisions
|
||||
# are now marked as merge, we can add the tip as a parent.
|
||||
my @merges = split "\n", $mergeinfo;
|
||||
my @merges = sort keys %$mergeinfo;
|
||||
my @merge_tips;
|
||||
my $url = $self->url;
|
||||
my $uuid = $self->ra_uuid;
|
||||
my @all_ranges;
|
||||
for my $merge ( @merges ) {
|
||||
my ($tip_commit, @ranges) =
|
||||
lookup_svn_merge( $uuid, $url, $merge );
|
||||
lookup_svn_merge( $uuid, $url,
|
||||
$merge, $mergeinfo->{$merge} );
|
||||
unless (!$tip_commit or
|
||||
grep { $_ eq $tip_commit } @$parents ) {
|
||||
push @merge_tips, $tip_commit;
|
||||
@ -1738,8 +1769,9 @@ sub find_extra_svn_parents {
|
||||
# check merge tips for new parents
|
||||
my @new_parents;
|
||||
for my $merge_tip ( @merge_tips ) {
|
||||
my $spec = shift @merges;
|
||||
my $merge = shift @merges;
|
||||
next unless $merge_tip and $excluded{$merge_tip};
|
||||
my $spec = "$merge:$mergeinfo->{$merge}";
|
||||
|
||||
# check out 'new' tips
|
||||
my $merge_base;
|
||||
@ -1759,19 +1791,17 @@ sub find_extra_svn_parents {
|
||||
}
|
||||
|
||||
# double check that there are no missing non-merge commits
|
||||
my (@incomplete) = check_cherry_pick(
|
||||
my ($ninc, $ifirst) = check_cherry_pick2(
|
||||
$merge_base, $merge_tip,
|
||||
$parents,
|
||||
@all_ranges,
|
||||
);
|
||||
|
||||
if ( @incomplete ) {
|
||||
warn "W:svn cherry-pick ignored ($spec) - missing "
|
||||
.@incomplete." commit(s) (eg $incomplete[0])\n";
|
||||
if ($ninc) {
|
||||
warn "W: svn cherry-pick ignored ($spec) - missing " .
|
||||
"$ninc commit(s) (eg $ifirst)\n";
|
||||
} else {
|
||||
warn
|
||||
"Found merge parent (svn:mergeinfo prop): ",
|
||||
$merge_tip, "\n";
|
||||
warn "Found merge parent ($spec): ", $merge_tip, "\n";
|
||||
push @new_parents, $merge_tip;
|
||||
}
|
||||
}
|
||||
@ -1797,23 +1827,20 @@ sub find_extra_svn_parents {
|
||||
}
|
||||
|
||||
sub make_log_entry {
|
||||
my ($self, $rev, $parents, $ed) = @_;
|
||||
my ($self, $rev, $parents, $ed, $parent_rev, $parent_path) = @_;
|
||||
my $untracked = $self->get_untracked($ed);
|
||||
|
||||
my @parents = @$parents;
|
||||
my $ps = $ed->{path_strip} || "";
|
||||
for my $path ( grep { m/$ps/ } %{$ed->{dir_prop}} ) {
|
||||
my $props = $ed->{dir_prop}{$path};
|
||||
if ( $props->{"svk:merge"} ) {
|
||||
$self->find_extra_svk_parents
|
||||
($ed, $props->{"svk:merge"}, \@parents);
|
||||
}
|
||||
if ( $props->{"svn:mergeinfo"} ) {
|
||||
$self->find_extra_svn_parents
|
||||
($ed,
|
||||
$props->{"svn:mergeinfo"},
|
||||
\@parents);
|
||||
}
|
||||
my $props = $ed->{dir_prop}{$self->path};
|
||||
if ( $props->{"svk:merge"} ) {
|
||||
$self->find_extra_svk_parents($props->{"svk:merge"}, \@parents);
|
||||
}
|
||||
if ( $props->{"svn:mergeinfo"} ) {
|
||||
my $mi_changes = $self->mergeinfo_changes
|
||||
($parent_path, $parent_rev,
|
||||
$self->path, $rev,
|
||||
$props->{"svn:mergeinfo"});
|
||||
$self->find_extra_svn_parents($mi_changes, \@parents);
|
||||
}
|
||||
|
||||
open my $un, '>>', "$self->{dir}/unhandled.log" or croak $!;
|
||||
|
@ -2,6 +2,7 @@ package Git::SVN::Ra;
|
||||
use vars qw/@ISA $config_dir $_ignore_refs_regex $_log_window_size/;
|
||||
use strict;
|
||||
use warnings;
|
||||
use Memoize;
|
||||
use SVN::Client;
|
||||
use Git::SVN::Utils qw(
|
||||
canonicalize_url
|
||||
@ -76,6 +77,40 @@ sub _auth_providers () {
|
||||
\@rv;
|
||||
}
|
||||
|
||||
sub prepare_config_once {
|
||||
SVN::_Core::svn_config_ensure($config_dir, undef);
|
||||
my ($baton, $callbacks) = SVN::Core::auth_open_helper(_auth_providers);
|
||||
my $config = SVN::Core::config_get_config($config_dir);
|
||||
my $dont_store_passwords = 1;
|
||||
my $conf_t = $config->{'config'};
|
||||
|
||||
no warnings 'once';
|
||||
# The usage of $SVN::_Core::SVN_CONFIG_* variables
|
||||
# produces warnings that variables are used only once.
|
||||
# I had not found the better way to shut them up, so
|
||||
# the warnings of type 'once' are disabled in this block.
|
||||
if (SVN::_Core::svn_config_get_bool($conf_t,
|
||||
$SVN::_Core::SVN_CONFIG_SECTION_AUTH,
|
||||
$SVN::_Core::SVN_CONFIG_OPTION_STORE_PASSWORDS,
|
||||
1) == 0) {
|
||||
SVN::_Core::svn_auth_set_parameter($baton,
|
||||
$SVN::_Core::SVN_AUTH_PARAM_DONT_STORE_PASSWORDS,
|
||||
bless (\$dont_store_passwords, "_p_void"));
|
||||
}
|
||||
if (SVN::_Core::svn_config_get_bool($conf_t,
|
||||
$SVN::_Core::SVN_CONFIG_SECTION_AUTH,
|
||||
$SVN::_Core::SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
|
||||
1) == 0) {
|
||||
$Git::SVN::Prompt::_no_auth_cache = 1;
|
||||
}
|
||||
|
||||
return ($config, $baton, $callbacks);
|
||||
} # no warnings 'once'
|
||||
|
||||
INIT {
|
||||
Memoize::memoize '_auth_providers';
|
||||
Memoize::memoize 'prepare_config_once';
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, $url) = @_;
|
||||
@ -84,34 +119,8 @@ sub new {
|
||||
|
||||
::_req_svn();
|
||||
|
||||
SVN::_Core::svn_config_ensure($config_dir, undef);
|
||||
my ($baton, $callbacks) = SVN::Core::auth_open_helper(_auth_providers);
|
||||
my $config = SVN::Core::config_get_config($config_dir);
|
||||
$RA = undef;
|
||||
my $dont_store_passwords = 1;
|
||||
my $conf_t = ${$config}{'config'};
|
||||
{
|
||||
no warnings 'once';
|
||||
# The usage of $SVN::_Core::SVN_CONFIG_* variables
|
||||
# produces warnings that variables are used only once.
|
||||
# I had not found the better way to shut them up, so
|
||||
# the warnings of type 'once' are disabled in this block.
|
||||
if (SVN::_Core::svn_config_get_bool($conf_t,
|
||||
$SVN::_Core::SVN_CONFIG_SECTION_AUTH,
|
||||
$SVN::_Core::SVN_CONFIG_OPTION_STORE_PASSWORDS,
|
||||
1) == 0) {
|
||||
SVN::_Core::svn_auth_set_parameter($baton,
|
||||
$SVN::_Core::SVN_AUTH_PARAM_DONT_STORE_PASSWORDS,
|
||||
bless (\$dont_store_passwords, "_p_void"));
|
||||
}
|
||||
if (SVN::_Core::svn_config_get_bool($conf_t,
|
||||
$SVN::_Core::SVN_CONFIG_SECTION_AUTH,
|
||||
$SVN::_Core::SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
|
||||
1) == 0) {
|
||||
$Git::SVN::Prompt::_no_auth_cache = 1;
|
||||
}
|
||||
} # no warnings 'once'
|
||||
|
||||
my ($config, $baton, $callbacks) = prepare_config_once();
|
||||
my $self = SVN::Ra->new(url => $url, auth => $baton,
|
||||
config => $config,
|
||||
pool => SVN::Pool->new,
|
||||
@ -166,7 +175,17 @@ sub get_dir {
|
||||
}
|
||||
}
|
||||
my $pool = SVN::Pool->new;
|
||||
my ($d, undef, $props) = $self->SUPER::get_dir($dir, $r, $pool);
|
||||
my ($d, undef, $props);
|
||||
|
||||
if (::compare_svn_version('1.4.0') >= 0) {
|
||||
# n.b. in addition to being potentially more efficient,
|
||||
# this works around what appears to be a bug in some
|
||||
# SVN 1.8 versions
|
||||
my $kind = 1; # SVN_DIRENT_KIND
|
||||
($d, undef, $props) = $self->get_dir2($dir, $r, $kind, $pool);
|
||||
} else {
|
||||
($d, undef, $props) = $self->SUPER::get_dir($dir, $r, $pool);
|
||||
}
|
||||
my %dirents = map { $_ => { kind => $d->{$_}->kind } } keys %$d;
|
||||
$pool->clear;
|
||||
if ($r != $cache->{r}) {
|
||||
@ -177,10 +196,6 @@ sub get_dir {
|
||||
wantarray ? (\%dirents, $r, $props) : \%dirents;
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
# do not call the real DESTROY since we store ourselves in $RA
|
||||
}
|
||||
|
||||
# get_log(paths, start, end, limit,
|
||||
# discover_changed_paths, strict_node_history, receiver)
|
||||
sub get_log {
|
||||
@ -376,10 +391,19 @@ sub longest_common_path {
|
||||
sub gs_fetch_loop_common {
|
||||
my ($self, $base, $head, $gsv, $globs) = @_;
|
||||
return if ($base > $head);
|
||||
my $gpool = SVN::Pool->new_default;
|
||||
my $ra_url = $self->url;
|
||||
my $reload_ra = sub {
|
||||
$_[0] = undef;
|
||||
$self = undef;
|
||||
$RA = undef;
|
||||
$gpool->clear;
|
||||
$self = Git::SVN::Ra->new($ra_url);
|
||||
$ra_invalid = undef;
|
||||
};
|
||||
my $inc = $_log_window_size;
|
||||
my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
|
||||
my $longest_path = longest_common_path($gsv, $globs);
|
||||
my $ra_url = $self->url;
|
||||
my $find_trailing_edge;
|
||||
while (1) {
|
||||
my %revs;
|
||||
@ -426,7 +450,7 @@ sub gs_fetch_loop_common {
|
||||
|
||||
my %exists = map { $_->path => $_ } @$gsv;
|
||||
foreach my $r (sort {$a <=> $b} keys %revs) {
|
||||
my ($paths, $logged) = @{$revs{$r}};
|
||||
my ($paths, $logged) = @{delete $revs{$r}};
|
||||
|
||||
foreach my $gs ($self->match_globs(\%exists, $paths,
|
||||
$globs, $r)) {
|
||||
@ -449,13 +473,7 @@ sub gs_fetch_loop_common {
|
||||
"$g->{t}-maxRev";
|
||||
Git::SVN::tmp_config($k, $r);
|
||||
}
|
||||
if ($ra_invalid) {
|
||||
$_[0] = undef;
|
||||
$self = undef;
|
||||
$RA = undef;
|
||||
$self = Git::SVN::Ra->new($ra_url);
|
||||
$ra_invalid = undef;
|
||||
}
|
||||
$reload_ra->() if $ra_invalid;
|
||||
}
|
||||
# pre-fill the .rev_db since it'll eventually get filled in
|
||||
# with '0' x40 if something new gets committed
|
||||
@ -472,6 +490,8 @@ sub gs_fetch_loop_common {
|
||||
$min = $max + 1;
|
||||
$max += $inc;
|
||||
$max = $head if ($max > $head);
|
||||
|
||||
$reload_ra->();
|
||||
}
|
||||
Git::SVN::gc();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user