use Digest::MD5;
use List::Util qw(any);
use List::MoreUtils qw(pairwise);
+use Carp;
use Debian::Dgit;
sub lrfetchrefs () { return "refs/dgit-fetch/$csuite"; }
sub lrfetchref () { return lrfetchrefs.'/'.server_branch($csuite); }
+# We fetch some parts of lrfetchrefs/*. Ideally we delete these
+# locally fetched refs because they have unhelpful names and clutter
+# up gitk etc. So we track whether we have "used up" head ref (ie,
+# whether we have made another local ref which refers to this object).
+#
+# (If we deleted them unconditionally, then we might end up
+# re-fetching the same git objects each time dgit fetch was run.)
+#
+# So, leach use of lrfetchrefs needs to be accompanied by arrangements
+# in git_fetch_us to fetch the refs in question, and possibly a call
+# to lrfetchref_used.
+
+our (%lrfetchrefs_f, %lrfetchrefs_d);
+# $lrfetchrefs_X{lrfetchrefs."/heads/whatever"} = $objid
+
+sub lrfetchref_used ($) {
+ my ($fullrefname) = @_;
+ my $objid = $lrfetchrefs_f{$fullrefname};
+ $lrfetchrefs_d{$fullrefname} = $objid if defined $objid;
+}
+
sub stripepoch ($) {
my ($vsn) = @_;
$vsn =~ s/^\d+\://;
END
@output = $lastpush_mergeinput;
} else {
+ # Same version. Use what's in the server git branch,
+ # discarding our own import. (This could happen if the
+ # server automatically imports all packages into git.)
@output = $lastpush_mergeinput;
}
}
}
sub git_fetch_us () {
- my @specs =
- map { "+refs/$_/*:".lrfetchrefs."/$_/*" }
- qw(tags heads), $branchprefix;
- runcmd_ordryrun_local @git, qw(fetch -p -n -q), access_giturl(), @specs;
+ # Want to fetch only what we are going to use, unless
+ # deliberately-not-ff, in which case we must fetch everything.
+
+ my @specs = deliberately_not_fast_forward ? qw(tags/*) :
+ map { "tags/$_" } debiantags('*',access_basedistro);
+ push @specs, server_branch($csuite);
+ push @specs, qw(heads/*) if deliberately_not_fast_forward;
+
+ # This is rather miserable:
+ # When git-fetch --prune is passed a fetchspec ending with a *,
+ # it does a plausible thing. If there is no * then:
+ # - it matches subpaths too, even if the supplied refspec
+ # starts refs, and behaves completely madly if the source
+ # has refs/refs/something. (See, for example, Debian #NNNN.)
+ # - if there is no matching remote ref, it bombs out the whole
+ # fetch.
+ # We want to fetch a fixed ref, and we don't know in advance
+ # if it exists, so this is not suitable.
+ #
+ # Our workaround is to use git-ls-remote. git-ls-remote has its
+ # own qairks. Notably, it has the absurd multi-tail-matching
+ # behaviour: git-ls-remote R refs/foo can report refs/foo AND
+ # refs/refs/foo etc.
+ #
+ # Also, we want an idempotent snapshot, but we have to make two
+ # calls to the remote: one to git-ls-remote and to git-fetch. The
+ # solution is use git-ls-remote to obtain a target state, and
+ # git-fetch to try to generate it. If we don't manage to generate
+ # the target state, we try again.
+
+ my $specre = join '|', map {
+ my $x = $_;
+ $x =~ s/\W/\\$&/g;
+ $x =~ s/\\\*$/.*/;
+ "(?:refs/$x)";
+ } @specs;
+ printdebug "git_fetch_us specre=$specre\n";
+ my $wanted_rref = sub {
+ local ($_) = @_;
+ return m/^(?:$specre)$/o;
+ };
+
+ my $fetch_iteration = 0;
+ FETCH_ITERATION:
+ for (;;) {
+ if (++$fetch_iteration > 10) {
+ fail "too many iterations trying to get sane fetch!";
+ }
+
+ my @look = map { "refs/$_" } @specs;
+ my @lcmd = (@git, qw(ls-remote -q --refs), access_giturl(), @look);
+ debugcmd "|",@lcmd;
+
+ my %wantr;
+ open GITLS, "-|", @lcmd or die $!;
+ while (<GITLS>) {
+ printdebug "=> ", $_;
+ m/^(\w+)\s+(\S+)\n/ or die "ls-remote $_ ?";
+ my ($objid,$rrefname) = ($1,$2);
+ if (!$wanted_rref->($rrefname)) {
+ print STDERR <<END;
+warning: git-ls-remote @look reported $rrefname; this is silly, ignoring it.
+END
+ next;
+ }
+ $wantr{$rrefname} = $objid;
+ }
+ $!=0; $?=0;
+ close GITLS or failedcmd @lcmd;
+
+ # OK, now %want is exactly what we want for refs in @specs
+ my @fspecs = map {
+ return () if !m/\*$/ && !exists $wantr{"refs/$_"};
+ "+refs/$_:".lrfetchrefs."/$_";
+ } @specs;
+
+ my @fcmd = (@git, qw(fetch -p -n -q), access_giturl(), @fspecs);
+ runcmd_ordryrun_local @git, qw(fetch -p -n -q), access_giturl(),
+ @fspecs;
+
+ %lrfetchrefs_f = ();
+ my %objgot;
+
+ git_for_each_ref(lrfetchrefs, sub {
+ my ($objid,$objtype,$lrefname,$reftail) = @_;
+ $lrfetchrefs_f{$lrefname} = $objid;
+ $objgot{$objid} = 1;
+ });
+
+ foreach my $lrefname (sort keys %lrfetchrefs_f) {
+ my $rrefname = 'refs'.substr($lrefname, length lrfetchrefs);
+ if (!exists $wantr{$rrefname}) {
+ if ($wanted_rref->($rrefname)) {
+ printdebug <<END;
+git-fetch @fspecs created $lrefname which git-ls-remote @look didn't list.
+END
+ } else {
+ print STDERR <<END
+warning: git-fetch @fspecs created $lrefname; this is silly, deleting it.
+END
+ }
+ runcmd_ordryrun_local @git, qw(update-ref -d), $lrefname;
+ delete $lrfetchrefs_f{$lrefname};
+ next;
+ }
+ }
+ foreach my $rrefname (sort keys %wantr) {
+ my $lrefname = lrfetchrefs.substr($rrefname, 4);
+ my $got = $lrfetchrefs_f{$lrefname} // '<none>';
+ my $want = $wantr{$rrefname};
+ next if $got eq $want;
+ if (!defined $objgot{$want}) {
+ print STDERR <<END;
+warning: git-ls-remote suggests we want $lrefname
+warning: and it should refer to $want
+warning: but git-fetch didn't fetch that object to any relevant ref.
+warning: This may be due to a race with someone updating the server.
+warning: Will try again...
+END
+ next FETCH_ITERATION;
+ }
+ printdebug <<END;
+git-fetch @fspecs made $lrefname=$got but want git-ls-remote @look says $want
+END
+ runcmd_ordryrun_local @git, qw(update-ref -m),
+ "dgit fetch git-fetch fixup", $lrefname, $want;
+ $lrfetchrefs_f{$lrefname} = $want;
+ }
+ last;
+ }
+ printdebug "git_fetch_us: git-fetch --no-insane emulation complete\n",
+ Dumper(\%lrfetchrefs_f);
my %here;
my @tagpats = debiantags('*',access_basedistro);
});
git_for_each_ref([map { lrfetchrefs."/tags/".$_ } @tagpats], sub {
my ($objid,$objtype,$fullrefname,$reftail) = @_;
- my $lref = "refs".substr($fullrefname, length lrfetchrefs);
+ my $lref = "refs".substr($fullrefname, length(lrfetchrefs));
printdebug "offered $lref=$objid\n";
if (!defined $here{$lref}) {
my @upd = (@git, qw(update-ref), $lref, $objid, '');
runcmd_ordryrun_local @upd;
+ lrfetchref_used $fullrefname;
} elsif ($here{$lref} eq $objid) {
+ lrfetchref_used $fullrefname;
} else {
print STDERR \
"Not updateting $lref from $here{$lref} to $objid.\n";
Info => "Dgit field in .dsc from archive",
};
+ my $cwd = getcwd();
+ my $del_lrfetchrefs = sub {
+ changedir $cwd;
+ my $gur;
+ printdebug "del_lrfetchrefs\n";
+ foreach my $fullrefname (sort keys %lrfetchrefs_d) {
+ my $objid = $lrfetchrefs_d{$fullrefname};
+ printdebug "del_lrfetchrefs: $fullrefname=$objid.\n";
+ if (!$gur) {
+ $gur ||= new IO::Handle;
+ open $gur, "|-", qw(git update-ref --stdin) or die $!;
+ }
+ printf $gur "delete %s %s\n", $fullrefname, $objid;
+ }
+ if ($gur) {
+ close $gur or failedcmd "git update-ref delete lrfetchrefs";
+ }
+ };
+
if (defined $dsc_hash) {
fail "missing remote git history even though dsc has hash -".
" could not find ref ".rref()." at ".access_giturl()
END
}
+ unshift @end, $del_lrfetchrefs;
return 0;
}
dryrun_report @upd_cmd;
}
}
+
+ lrfetchref_used lrfetchref();
+
+ unshift @end, $del_lrfetchrefs;
return 1;
}
create_remote_git_repo();
}
- my @pushrefs = $forceflag."HEAD:".rrref();
+ my @pushrefs = $forceflag.$dgithead.":".rrref();
foreach my $tw (@tagwants) {
my $view = $tw->{View};
next unless $view eq 'dgit'
or any { $_ eq $view } access_cfg_tagformats();
+ # ^ $view is "dgit" or "maint" so this looks for "maint"
+ # in archive supported tagformats.
push @pushrefs, $forceflag."refs/tags/$tw->{Tag}";
}
if $patches_applied_dirtily & 01;
rmtree '.pc'
if $patches_applied_dirtily & 02;
+ $patches_applied_dirtily = 0;
}
#----- other building -----
}
build_prep();
}
+ maybe_unapply_patches_again();
if ($wantsrc < 2) {
unless (grep { m/^--git-debian-branch|^--git-ignore-branch/ } @ARGV) {
canonicalise_suite();
push @cmd, "--git-debian-branch=".lbranch();
}
push @cmd, changesopts();
- maybe_apply_patches_dirtily();
runcmd_ordryrun_local @cmd, @ARGV;
}
- maybe_unapply_patches_again();
printdone "build successful\n";
}
sub cmd_git_build { cmd_gbp_build(); } # compatibility with <= 1.0
" building would result in ambiguity about the intended results"
if @unwanted;
}
+ my $wasdir = must_getcwd();
changedir "..";
if (act_local()) {
stat_exists $dscfn or fail "$dscfn (in parent directory): $!";
rename "$cf", "$cf.inmulti" or fail "$cf\{,.inmulti}: $!";
}
}
+ changedir $wasdir;
maybe_unapply_patches_again();
printdone "build successful, results in $multichanges\n" or die $!;
}