X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=dgit;h=5d1a95c532a19c0c49f6c9538e227c732cc02b76;hb=95872ab5c8650af9003677b0ad25d05c2d1ae4d0;hp=b59d00a9effe3cc84eaeaffee1b1f778d384f628;hpb=6ebc225def00c400db654fa7fa562725432fe3a7;p=dgit.git diff --git a/dgit b/dgit index b59d00a9..5d1a95c5 100755 --- a/dgit +++ b/dgit @@ -36,6 +36,7 @@ use Digest::SHA; use Digest::MD5; use List::Util qw(any); use List::MoreUtils qw(pairwise); +use Carp; use Debian::Dgit; @@ -157,6 +158,27 @@ sub rrref () { return server_ref($csuite); } 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+\://; @@ -1606,10 +1628,138 @@ sub ensure_we_have_orig () { } 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 () { + printdebug "=> ", $_; + m/^(\w+)\s+(\S+)\n/ or die "ls-remote $_ ?"; + my ($objid,$rrefname) = ($1,$2); + if (!$wanted_rref->($rrefname)) { + print STDERR <($rrefname)) { + printdebug <'; + my $want = $wantr{$rrefname}; + next if $got eq $want; + if (!defined $objgot{$want}) { + print STDERR < "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() @@ -1825,6 +1996,7 @@ But we were not able to obtain any version from the archive or git. END } + unshift @end, $del_lrfetchrefs; return 0; } @@ -1972,6 +2144,10 @@ END dryrun_report @upd_cmd; } } + + lrfetchref_used lrfetchref(); + + unshift @end, $del_lrfetchrefs; return 1; }