chiark / gitweb /
git-debrebase: Rename a variable $ups_tag to $ups_rev
[dgit.git] / Debian / Dgit.pm
1 # -*- perl -*-
2 # dgit
3 # Debian::Dgit: functions common to dgit and its helpers and servers
4 #
5 # Copyright (C) 2015-2019  Ian Jackson
6 #
7 #    This program is free software; you can redistribute it and/or modify
8 #    it under the terms of the GNU General Public License as published by
9 #    the Free Software Foundation; either version 3 of the License, or
10 #    (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU General Public License for more details.
16 #
17 #    You should have received a copy of the GNU General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 package Debian::Dgit;
21
22 use strict;
23 use warnings;
24
25 use Carp;
26 use POSIX;
27 use IO::Handle;
28 use Config;
29 use Digest::SHA;
30 use Data::Dumper;
31 use IPC::Open2;
32 use File::Path qw(:DEFAULT make_path);
33 use File::Basename;
34 use Dpkg::Control::Hash;
35 use Debian::Dgit::ExitStatus;
36 use Debian::Dgit::I18n;
37
38 BEGIN {
39     use Exporter   ();
40     our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
41
42     $VERSION     = 1.00;
43     @ISA         = qw(Exporter);
44     @EXPORT      = qw(setup_sigwarn forkcheck_setup forkcheck_mainprocess
45                       dep14_version_mangle
46                       debiantags debiantag_new
47                       debiantag_maintview
48                       upstreamversion
49                       upstream_commitish_search resolve_upstream_version
50                       stripepoch source_file_leafname is_orig_file_of_p_v
51                       server_branch server_ref
52                       stat_exists link_ltarget rename_link_xf
53                       hashfile
54                       fail failmsg ensuredir ensurepath
55                       must_getcwd executable_on_path
56                       waitstatusmsg failedcmd_waitstatus
57                       failedcmd_report_cmd failedcmd
58                       runcmd shell_cmd cmdoutput cmdoutput_errok
59                       @git
60                       git_rev_parse changedir_git_toplevel git_cat_file
61                       git_get_ref git_get_symref git_for_each_ref
62                       git_for_each_tag_referring is_fast_fwd
63                       git_check_unmodified
64                       git_reflog_action_msg  git_update_ref_cmd
65                       rm_subdir_cached read_tree_subdir
66                       read_tree_debian read_tree_upstream
67                       make_commit hash_commit hash_commit_text
68                       reflog_cache_insert reflog_cache_lookup
69                       $package_re $component_re $suite_re $deliberately_re
70                       $distro_re $versiontag_re $series_filename_re
71                       $orig_f_comp_re $orig_f_sig_re
72                       $tarball_f_ext_re $orig_f_tail_re
73                       $extra_orig_namepart_re
74                       $git_null_obj
75                       $branchprefix
76                       $ffq_refprefix $gdrlast_refprefix
77                       initdebug enabledebug enabledebuglevel
78                       printdebug debugcmd
79                       $printdebug_when_debuglevel $debugcmd_when_debuglevel
80                       $debugprefix *debuglevel *DEBUG
81                       shellquote printcmd messagequote
82                       $negate_harmful_gitattrs
83                       changedir git_slurp_config_src
84                       gdr_ffq_prev_branchinfo
85                       parsecontrolfh parsecontrol parsechangelog
86                       getfield parsechangelog_loop
87                       playtree_setup);
88     # implicitly uses $main::us
89     %EXPORT_TAGS = ( policyflags => [qw(NOFFCHECK FRESHREPO NOCOMMITCHECK)],
90                      playground => [qw(record_maindir $maindir $local_git_cfg
91                                        $maindir_gitdir $maindir_gitcommon
92                                        fresh_playground
93                                        ensure_a_playground)]);
94     @EXPORT_OK   = ( @{ $EXPORT_TAGS{policyflags} },
95                      @{ $EXPORT_TAGS{playground} } );
96 }
97
98 our @EXPORT_OK;
99
100 our $package_re = '[0-9a-z][-+.0-9a-z]*';
101 our $component_re = '[0-9a-zA-Z][-+.0-9a-zA-Z]*';
102 our $suite_re = '[-+.0-9a-z]+';
103 our $deliberately_re = "(?:TEST-)?$package_re";
104 our $distro_re = $component_re;
105 our $versiontag_re = qr{[-+.\%_0-9a-zA-Z/]+};
106 our $branchprefix = 'dgit';
107 our $series_filename_re = qr{(?:^|\.)series(?!\n)$}s;
108 our $extra_orig_namepart_re = qr{[-0-9a-zA-Z]+};
109 our $orig_f_comp_re = qr{orig(?:-$extra_orig_namepart_re)?};
110 our $orig_f_sig_re = '\\.(?:asc|gpg|pgp)';
111 our $tarball_f_ext_re = "\\.tar(?:\\.\\w+)?(?:$orig_f_sig_re)?";
112 our $orig_f_tail_re = "$orig_f_comp_re$tarball_f_ext_re";
113 our $git_null_obj = '0' x 40;
114 our $ffq_refprefix = 'ffq-prev';
115 our $gdrlast_refprefix = 'debrebase-last';
116 our $printdebug_when_debuglevel = 1;
117 our $debugcmd_when_debuglevel = 1;
118
119 our (@git) = qw(git);
120
121 # these three all go together, only valid after record_maindir
122 our $maindir;
123 our $maindir_gitdir;
124 our $maindir_gitcommon;
125
126 # policy hook exit status bits
127 # see dgit-repos-server head comment for documentation
128 # 1 is reserved in case something fails with `exit 1' and to spot
129 # dynamic loader, runtime, etc., failures, which report 127 or 255
130 sub NOFFCHECK () { return 0x2; }
131 sub FRESHREPO () { return 0x4; }
132 sub NOCOMMITCHECK () { return 0x8; }
133
134 our $debugprefix;
135 our $debuglevel = 0;
136
137 our $negate_harmful_gitattrs =
138     "-text -eol -crlf -ident -filter -working-tree-encoding";
139     # ^ when updating this, alter the regexp in dgit:is_gitattrs_setup
140
141 our $forkcheck_mainprocess;
142
143 sub forkcheck_setup () {
144     $forkcheck_mainprocess = $$;
145 }
146
147 sub forkcheck_mainprocess () {
148     # You must have called forkcheck_setup or setup_sigwarn already
149     getppid != $forkcheck_mainprocess;
150 }
151
152 sub setup_sigwarn () {
153     forkcheck_setup();
154     $SIG{__WARN__} = sub { 
155         confess $_[0] if forkcheck_mainprocess;
156     };
157 }
158
159 sub initdebug ($) { 
160     ($debugprefix) = @_;
161     open DEBUG, ">/dev/null" or confess "$!";
162 }
163
164 sub enabledebug () {
165     open DEBUG, ">&STDERR" or confess "$!";
166     DEBUG->autoflush(1);
167     $debuglevel ||= 1;
168 }
169     
170 sub enabledebuglevel ($) {
171     my ($newlevel) = @_; # may be undef (eg from env var)
172     confess if $debuglevel;
173     $newlevel //= 0;
174     $newlevel += 0;
175     return unless $newlevel;
176     $debuglevel = $newlevel;
177     enabledebug();
178 }
179     
180 sub printdebug {
181     # Prints a prefix, and @_, to DEBUG.  @_ should normally contain
182     # a trailing \n.
183
184     # With no (or only empty) arguments just prints the prefix and
185     # leaves the caller to do more with DEBUG.  The caller should make
186     # sure then to call printdebug with something ending in "\n" to
187     # get the prefix right in subsequent calls.
188
189     return unless $debuglevel >= $printdebug_when_debuglevel;
190     our $printdebug_noprefix;
191     print DEBUG $debugprefix unless $printdebug_noprefix;
192     pop @_ while @_ and !length $_[-1];
193     return unless @_;
194     print DEBUG @_ or confess "$!";
195     $printdebug_noprefix = $_[-1] !~ m{\n$};
196 }
197
198 sub messagequote ($) {
199     local ($_) = @_;
200     s{\\}{\\\\}g;
201     s{\n}{\\n}g;
202     s{\x08}{\\b}g;
203     s{\t}{\\t}g;
204     s{[\000-\037\177]}{ sprintf "\\x%02x", ord $& }ge;
205     $_;
206 }
207
208 sub shellquote {
209     my @out;
210     local $_;
211     defined or confess __ 'internal error' foreach @_;
212     foreach my $a (@_) {
213         $_ = $a;
214         if (!length || m{[^-=_./:0-9a-z]}i) {
215             s{['\\]}{'\\$&'}g;
216             push @out, "'$_'";
217         } else {
218             push @out, $_;
219         }
220     }
221     return join ' ', @out;
222 }
223
224 sub printcmd {
225     my $fh = shift @_;
226     my $intro = shift @_;
227     print $fh $intro." ".(join '', shellquote @_)."\n" or confess "$!";
228 }
229
230 sub debugcmd {
231     my $extraprefix = shift @_;
232     printcmd(\*DEBUG,$debugprefix.$extraprefix,@_)
233         if $debuglevel >= $debugcmd_when_debuglevel;
234 }
235
236 sub dep14_version_mangle ($) {
237     my ($v) = @_;
238     # DEP-14 patch proposed 2016-11-09  "Version Mangling"
239     $v =~ y/~:/_%/;
240     $v =~ s/\.(?=\.|$|lock$)/.#/g;
241     return $v;
242 }
243
244 sub debiantag_new ($$) { 
245     my ($v,$distro) = @_;
246     return "archive/$distro/".dep14_version_mangle $v;
247 }
248
249 sub debiantag_maintview ($$) { 
250     my ($v,$distro) = @_;
251     return "$distro/".dep14_version_mangle $v;
252 }
253
254 sub debiantags ($$) {
255     my ($version,$distro) = @_;
256     map { $_->($version, $distro) } (\&debiantag_new, \&debiantag_maintview);
257 }
258
259 sub stripepoch ($) {
260     my ($vsn) = @_;
261     $vsn =~ s/^\d+\://;
262     return $vsn;
263 }
264
265 sub upstreamversion ($) {
266     my ($vsn) = @_;
267     $vsn =~ s/-[^-]+$//;
268     return $vsn;
269 }
270
271 sub source_file_leafname ($$$) {
272     my ($package,$vsn,$sfx) = @_;
273     return "${package}_".(stripepoch $vsn).$sfx
274 }
275
276 sub is_orig_file_of_p_v ($$$) {
277     my ($f, $package, $upstreamvsn) = @_;
278     my $base = source_file_leafname $package, $upstreamvsn, '';
279     return 0 unless $f =~ m/^\Q$base\E\.$orig_f_tail_re$/;
280     return 1;
281 }
282
283 sub server_branch ($) { return "$branchprefix/$_[0]"; }
284 sub server_ref ($) { return "refs/".server_branch($_[0]); }
285
286 sub stat_exists ($) {
287     my ($f) = @_;
288     return 1 if stat $f;
289     return 0 if $!==&ENOENT;
290     confess "stat $f: $!";
291 }
292
293 sub _us () {
294     $::us // ($0 =~ m#[^/]*$#, $&);
295 }
296
297 sub failmsg {
298     my $s = f_ "error: %s\n", "@_";
299     $s =~ s/\n\n$/\n/g;
300     my $prefix = _us().": ";
301     $s =~ s/^/$prefix/gm;
302     return "\n".$s;
303 }
304
305 sub fail {
306     die failmsg @_;
307 }
308
309 sub ensuredir ($) {
310     my ($dir) = @_; # does not create parents
311     return if mkdir $dir;
312     return if $! == EEXIST;
313     confess "mkdir $dir: $!";
314 }
315
316 sub ensurepath ($$) {
317     my ($firsttocreate, $subdir) = @_; # creates necessary bits of $subidr
318     ensuredir $firsttocreate;
319     make_path "$firsttocreate/$subdir";
320 }
321
322 sub must_getcwd () {
323     my $d = getcwd();
324     defined $d or fail f_ "getcwd failed: %s\n", $!;
325     return $d;
326 }
327
328 sub executable_on_path ($) {
329     my ($program) = @_;
330     return 1 if $program =~ m{/};
331     my @path = split /:/, ($ENV{PATH} // "/usr/local/bin:/bin:/usr/bin");
332     foreach my $pe (@path) {
333         my $here = "$pe/$program";
334         return $here if stat_exists $here && -x _;
335     }
336     return undef;
337 }
338
339 our @signames = split / /, $Config{sig_name};
340
341 sub waitstatusmsg () {
342     if (!$?) {
343         return __ "terminated, reporting successful completion";
344     } elsif (!($? & 255)) {
345         return f_ "failed with error exit status %s", WEXITSTATUS($?);
346     } elsif (WIFSIGNALED($?)) {
347         my $signum=WTERMSIG($?);
348         return f_ "died due to fatal signal %s",
349             ($signames[$signum] // "number $signum").
350             ($? & 128 ? " (core dumped)" : ""); # POSIX(3pm) has no WCOREDUMP
351     } else {
352         return f_ "failed with unknown wait status %s", $?;
353     }
354 }
355
356 sub failedcmd_report_cmd {
357     my $intro = shift @_;
358     $intro //= __ "failed command";
359     { local ($!); printcmd \*STDERR, _us().": $intro:", @_ or confess "$!"; };
360 }
361
362 sub failedcmd_waitstatus {
363     if ($? < 0) {
364         return f_ "failed to fork/exec: %s", $!;
365     } elsif ($?) {
366         return f_ "subprocess %s", waitstatusmsg();
367     } else {
368         return __ "subprocess produced invalid output";
369     }
370 }
371
372 sub failedcmd {
373     # Expects $!,$? as set by close - see below.
374     # To use with system(), set $?=-1 first.
375     #
376     # Actual behaviour of perl operations:
377     #   success              $!==0       $?==0       close of piped open
378     #   program failed       $!==0       $? >0       close of piped open
379     #   syscall failure      $! >0       $?=-1       close of piped open
380     #   failure              $! >0       unchanged   close of something else
381     #   success              trashed     $?==0       system
382     #   program failed       trashed     $? >0       system
383     #   syscall failure      $! >0       unchanged   system
384     failedcmd_report_cmd undef, @_;
385     fail failedcmd_waitstatus();
386 }
387
388 sub runcmd {
389     debugcmd "+",@_;
390     $!=0; $?=-1;
391     failedcmd @_ if system @_;
392 }
393
394 sub shell_cmd {
395     my ($first_shell, @cmd) = @_;
396     return qw(sh -ec), $first_shell.'; exec "$@"', 'x', @cmd;
397 }
398
399 sub cmdoutput_errok {
400     confess Dumper(\@_)." ?" if grep { !defined } @_;
401     local $printdebug_when_debuglevel = $debugcmd_when_debuglevel;
402     debugcmd "|",@_;
403     open P, "-|", @_ or confess "$_[0] $!";
404     my $d;
405     $!=0; $?=0;
406     { local $/ = undef; $d = <P>; }
407     confess "$!" if P->error;
408     if (!close P) { printdebug "=>!$?\n"; return undef; }
409     chomp $d;
410     if ($debuglevel > 0) {
411         $d =~ m/^.*/;
412         my $dd = $&;
413         my $more = (length $' ? '...' : ''); #');
414         $dd =~ s{[^\n -~]|\\}{ sprintf "\\x%02x", ord $& }ge;
415         printdebug "=> \`$dd'",$more,"\n";
416     }
417     return $d;
418 }
419
420 sub cmdoutput {
421     my $d = cmdoutput_errok @_;
422     defined $d or failedcmd @_;
423     return $d;
424 }
425
426 sub link_ltarget ($$) {
427     my ($old,$new) = @_;
428     lstat $old or return undef;
429     if (-l _) {
430         $old = cmdoutput qw(realpath  --), $old;
431     }
432     my $r = link $old, $new;
433     $r = symlink $old, $new if !$r && $!==EXDEV;
434     $r or fail "(sym)link $old $new: $!\n";
435 }
436
437 sub rename_link_xf ($$$) {
438     # renames/moves or links/copies $src to $dst,
439     # even if $dst is on a different fs
440     # (May use the filename "$dst.tmp".);
441     # On success, returns true.
442     # On failure, returns false and sets
443     #    $@ to a reason message
444     #    $! to an errno value, or -1 if not known
445     # having possibly printed something about mv to stderr.
446     # Not safe to use without $keeporig if $dst might be a symlink
447     # to $src, as it might delete $src leaving $dst invalid.
448     my ($keeporig,$src,$dst) = @_;
449     if ($keeporig
450         ? link   $src, $dst
451         : rename $src, $dst) {
452         return 1;
453     }
454     if ($! != EXDEV) {
455         $@ = "$!";
456         return 0;
457     }
458     if (!stat $src) {
459         $@ = f_ "stat source file: %S", $!;
460         return 0;
461     }
462     my @src_stat = (stat _)[0..1];
463
464     my @dst_stat;
465     if (stat $dst) {
466         @dst_stat = (stat _)[0..1];
467     } elsif ($! == ENOENT) {
468     } else {
469         $@ = f_ "stat destination file: %S", $!;
470         return 0;
471     }
472
473     if ("@src_stat" eq "@dst_stat") {
474         # (Symlinks to) the same file.  No need for a copy but
475         # we may need to delete the original.
476         printdebug "rename_link_xf $keeporig $src $dst EXDEV but same\n";
477     } else {
478         $!=0; $?=0;
479         my @cmd = (qw(cp --), $src, "$dst.tmp");
480         debugcmd '+',@cmd;
481         if (system @cmd) {
482             failedcmd_report_cmd undef, @cmd;
483             $@ = failedcmd_waitstatus();
484             $! = -1;
485             return 0;
486         }
487         if (!rename "$dst.tmp", $dst) {
488             $@ = f_ "finally install file after cp: %S", $!;
489             return 0;
490         }
491     }
492     if (!$keeporig) {
493         if (!unlink $src) {
494             $@ = f_ "delete old file after cp: %S", $!;
495             return 0;
496         }
497     }
498     return 1;
499 }
500
501 sub hashfile ($) {
502     my ($fn) = @_;
503     my $h = Digest::SHA->new(256);
504     $h->addfile($fn);
505     return $h->hexdigest();
506 }
507
508 sub git_rev_parse ($) {
509     return cmdoutput qw(git rev-parse), "$_[0]~0";
510 }
511
512 sub changedir_git_toplevel () {
513     my $toplevel = cmdoutput qw(git rev-parse --show-toplevel);
514     length $toplevel or fail __ <<END;
515 not in a git working tree?
516 (git rev-parse --show-toplevel produced no output)
517 END
518     chdir $toplevel or fail f_ "chdir toplevel %s: %s\n", $toplevel, $!;
519 }
520
521 sub git_cat_file ($;$) {
522     my ($objname, $etype) = @_;
523     # => ($type, $data) or ('missing', undef)
524     # in scalar context, just the data
525     # if $etype defined, dies unless type is $etype or in @$etype
526     our ($gcf_pid, $gcf_i, $gcf_o);
527     local $printdebug_when_debuglevel = $debugcmd_when_debuglevel;
528     my $chk = sub {
529         my ($gtype, $data) = @_;
530         if ($etype) {
531             $etype = [$etype] unless ref $etype;
532             confess "$objname expected @$etype but is $gtype"
533                 unless grep { $gtype eq $_ } @$etype;
534         }
535         return ($gtype, $data);
536     };
537     if (!$gcf_pid) {
538         my @cmd = qw(git cat-file --batch);
539         debugcmd "GCF|", @cmd;
540         $gcf_pid = open2 $gcf_o, $gcf_i, @cmd or confess "$!";
541     }
542     printdebug "GCF>| $objname\n";
543     print $gcf_i $objname, "\n" or confess "$!";
544     my $x = <$gcf_o>;
545     printdebug "GCF<| ", $x;
546     if ($x =~ m/ (missing)$/) { return $chk->($1, undef); }
547     my ($type, $size) = $x =~ m/^.* (\w+) (\d+)\n/ or confess "$objname ?";
548     my $data;
549     (read $gcf_o, $data, $size) == $size or confess "$objname $!";
550     $x = <$gcf_o>;
551     $x eq "\n" or confess "$objname ($_) $!";
552     return $chk->($type, $data);
553 }
554
555 sub git_get_symref (;$) {
556     my ($symref) = @_;  $symref //= 'HEAD';
557     # => undef if not a symref, otherwise refs/...
558     my @cmd = (qw(git symbolic-ref -q HEAD));
559     my $branch = cmdoutput_errok @cmd;
560     if (!defined $branch) {
561         $?==256 or failedcmd @cmd;
562     } else {
563         chomp $branch;
564     }
565     return $branch;
566 }
567
568 sub git_for_each_ref ($$;$) {
569     my ($pattern,$func,$gitdir) = @_;
570     # calls $func->($objid,$objtype,$fullrefname,$reftail);
571     # $reftail is RHS of ref after refs/[^/]+/
572     # breaks if $pattern matches any ref `refs/blah' where blah has no `/'
573     # $pattern may be an array ref to mean multiple patterns
574     $pattern = [ $pattern ] unless ref $pattern;
575     my @cmd = (qw(git for-each-ref), @$pattern);
576     if (defined $gitdir) {
577         @cmd = ('sh','-ec','cd "$1"; shift; exec "$@"','x', $gitdir, @cmd);
578     }
579     open GFER, "-|", @cmd or confess "$!";
580     debugcmd "|", @cmd;
581     while (<GFER>) {
582         chomp or confess "$_ ?";
583         printdebug "|> ", $_, "\n";
584         m#^(\w+)\s+(\w+)\s+(refs/[^/]+/(\S+))$# or confess "$_ ?";
585         $func->($1,$2,$3,$4);
586     }
587     $!=0; $?=0; close GFER or confess "$pattern $? $!";
588 }
589
590 sub git_get_ref ($) {
591     # => '' if no such ref
592     my ($refname) = @_;
593     local $_ = $refname;
594     s{^refs/}{[r]efs/} or confess "$refname $_ ?";
595     return cmdoutput qw(git for-each-ref --format=%(objectname)), $_;
596 }
597
598 sub git_for_each_tag_referring ($$) {
599     my ($objreferring, $func) = @_;
600     # calls $func->($tagobjid,$refobjid,$fullrefname,$tagname);
601     printdebug "git_for_each_tag_referring ",
602         ($objreferring // 'UNDEF'),"\n";
603     git_for_each_ref('refs/tags', sub {
604         my ($tagobjid,$objtype,$fullrefname,$tagname) = @_;
605         return unless $objtype eq 'tag';
606         my $refobjid = git_rev_parse $tagobjid;
607         return unless
608             !defined $objreferring # caller wants them all
609             or $tagobjid eq $objreferring
610             or $refobjid eq $objreferring;
611         $func->($tagobjid,$refobjid,$fullrefname,$tagname);
612     });
613 }
614
615 sub git_check_unmodified () {
616     foreach my $cached (qw(0 1)) {
617         my @cmd = qw(git diff --quiet);
618         push @cmd, qw(--cached) if $cached;
619         push @cmd, qw(HEAD);
620         debugcmd "+",@cmd;
621         $!=0; $?=-1; system @cmd;
622         return if !$?;
623         if ($?==256) {
624             fail
625                 $cached
626                 ? __ "git index contains changes (does not match HEAD)"
627                 : __ "working tree is dirty (does not match HEAD)";
628         } else {
629             failedcmd @cmd;
630         }
631     }
632 }
633
634 sub upstream_commitish_search ($$) {
635     my ($upstream_version, $tried) = @_;
636     # todo: at some point maybe use git-deborig to do this
637     my @found;
638     foreach my $tagpfx ('', 'v', 'upstream/') {
639         my $tag = $tagpfx.(dep14_version_mangle $upstream_version);
640         my $new_upstream = git_get_ref "refs/tags/$tag";
641         push @$tried, $tag;
642         push @found, $tag if $new_upstream;
643     }
644     return $found[0] if @found == 1;
645 }
646
647 sub resolve_upstream_version ($$) {
648     my ($new_upstream, $upstream_version) = @_;
649
650     my $used = $new_upstream;
651     my $message = __ 'using specified upstream commitish';
652     if (!defined $new_upstream) {
653         my @tried;
654         $new_upstream = upstream_commitish_search $upstream_version, \@tried;
655         if (!length $new_upstream) {
656             fail f_
657                 "Could not determine appropriate upstream commitish.\n".
658                 " (Tried these tags: %s)\n".
659                 " Check version, and specify upstream commitish explicitly.",
660                 "@tried";
661         }
662         $used = $tried[-1];
663         $message = f_ 'using upstream from git tag %s', $used;
664     } elsif ($new_upstream =~ m{^refs/tags/($versiontag_re)$}s) {
665         $message = f_ 'using upstream from git tag %s', $1;
666         $used = $1;
667     }   
668     $new_upstream = git_rev_parse $new_upstream;
669
670     return ($new_upstream, $used, $message);
671     # used is a human-readable idea of what we found
672 }
673
674 sub is_fast_fwd ($$) {
675     my ($ancestor,$child) = @_;
676     my @cmd = (qw(git merge-base), $ancestor, $child);
677     my $mb = cmdoutput_errok @cmd;
678     if (defined $mb) {
679         return git_rev_parse($mb) eq git_rev_parse($ancestor);
680     } else {
681         $?==256 or failedcmd @cmd;
682         return 0;
683     }
684 }
685
686 sub git_reflog_action_msg ($) {
687     my ($msg) = @_;
688     my $rla = $ENV{GIT_REFLOG_ACTION};
689     $msg = "$rla: $msg" if length $rla;
690     return $msg;
691 }
692
693 sub git_update_ref_cmd {
694     # returns  qw(git update-ref), qw(-m), @_
695     # except that message may be modified to honour GIT_REFLOG_ACTION
696     my $msg = shift @_;
697     $msg = git_reflog_action_msg $msg;
698     return qw(git update-ref -m), $msg, @_;
699 }
700
701 sub rm_subdir_cached ($) {
702     my ($subdir) = @_;
703     runcmd qw(git rm --quiet -rf --cached --ignore-unmatch), $subdir;
704 }
705
706 sub read_tree_subdir ($$) {
707     my ($subdir, $new_tree_object) = @_;
708     # If $new_tree_object is '', the subtree is deleted.
709     confess unless defined $new_tree_object;
710     rm_subdir_cached $subdir;
711     runcmd qw(git read-tree), "--prefix=$subdir/", $new_tree_object
712         if length $new_tree_object;
713 }
714
715 sub read_tree_debian ($) {
716     my ($treeish) = @_;
717     read_tree_subdir 'debian', "$treeish:debian";
718     rm_subdir_cached 'debian/patches';
719 }
720
721 sub read_tree_upstream ($;$$) {
722     my ($treeish, $keep_patches, $tree_with_debian) = @_;
723     # if $tree_with_debian is supplied, will use that for debian/
724     # otherwise will save and restore it.  If $tree_with_debian
725     # is '' then debian/ is deleted.
726     my $debian =
727         defined $tree_with_debian ? "$tree_with_debian:debian"
728         : cmdoutput qw(git write-tree --prefix=debian/);
729     runcmd qw(git read-tree), $treeish;
730     read_tree_subdir 'debian', $debian;
731     rm_subdir_cached 'debian/patches' unless $keep_patches;
732 }
733
734 sub changedir ($) {
735     my ($newdir) = @_;
736     printdebug "CD $newdir\n";
737     chdir $newdir or confess "chdir: $newdir: $!";
738 }
739
740 sub git_slurp_config_src ($) {
741     my ($src) = @_;
742     # returns $r such that $r->{KEY}[] = VALUE
743     my @cmd = (qw(git config -z --get-regexp), "--$src", qw(.*));
744     debugcmd "|",@cmd;
745
746     local ($debuglevel) = $debuglevel-2;
747     local $/="\0";
748
749     my $r = { };
750     open GITS, "-|", @cmd or confess "$!";
751     while (<GITS>) {
752         chomp or confess;
753         printdebug "=> ", (messagequote $_), "\n";
754         m/\n/ or confess "$_ ?";
755         push @{ $r->{$`} }, $'; #';
756     }
757     $!=0; $?=0;
758     close GITS
759         or ($!==0 && $?==256)
760         or failedcmd @cmd;
761     return $r;
762 }
763
764 sub gdr_ffq_prev_branchinfo ($) {
765     my ($symref) = @_;
766     # => ('status', "message", [$symref, $ffq_prev, $gdrlast])
767     # 'status' may be
768     #    branch         message is undef
769     #    weird-symref   } no $symref,
770     #    notbranch      }  no $ffq_prev
771     return ('detached', __ 'detached HEAD') unless defined $symref;
772     return ('weird-symref', __ 'HEAD symref is not to refs/')
773         unless $symref =~ m{^refs/};
774     my $ffq_prev = "refs/$ffq_refprefix/$'";
775     my $gdrlast = "refs/$gdrlast_refprefix/$'";
776     printdebug "ffq_prev_branchinfo branch current $symref\n";
777     return ('branch', undef, $symref, $ffq_prev, $gdrlast);
778 }
779
780 sub parsecontrolfh ($$;$) {
781     my ($fh, $desc, $allowsigned) = @_;
782     our $dpkgcontrolhash_noissigned;
783     my $c;
784     for (;;) {
785         my %opts = ('name' => $desc);
786         $opts{allow_pgp}= $allowsigned || !$dpkgcontrolhash_noissigned;
787         $c = Dpkg::Control::Hash->new(%opts);
788         $c->parse($fh,$desc) or fail f_ "parsing of %s failed", $desc;
789         last if $allowsigned;
790         last if $dpkgcontrolhash_noissigned;
791         my $issigned= $c->get_option('is_pgp_signed');
792         if (!defined $issigned) {
793             $dpkgcontrolhash_noissigned= 1;
794             seek $fh, 0,0 or confess "seek $desc: $!";
795         } elsif ($issigned) {
796             fail f_
797                 "control file %s is (already) PGP-signed. ".
798                 " Note that dgit push needs to modify the .dsc and then".
799                 " do the signature itself",
800                 $desc;
801         } else {
802             last;
803         }
804     }
805     return $c;
806 }
807
808 sub parsecontrol {
809     my ($file, $desc, $allowsigned) = @_;
810     my $fh = new IO::Handle;
811     open $fh, '<', $file or fail f_ "open %s (%s): %s", $file, $desc, $!;
812     my $c = parsecontrolfh($fh,$desc,$allowsigned);
813     $fh->error and confess "$!";
814     close $fh;
815     return $c;
816 }
817
818 sub parsechangelog {
819     my $c = Dpkg::Control::Hash->new(name => 'parsed changelog');
820     my $p = new IO::Handle;
821     my @cmd = (qw(dpkg-parsechangelog), @_);
822     open $p, '-|', @cmd or confess "$!";
823     $c->parse($p);
824     $?=0; $!=0; close $p or failedcmd @cmd;
825     return $c;
826 }
827
828 sub getfield ($$) {
829     my ($dctrl,$field) = @_;
830     my $v = $dctrl->{$field};
831     return $v if defined $v;
832     fail f_ "missing field %s in %s", $field, $dctrl->get_option('name');
833 }
834
835 sub parsechangelog_loop ($$$) {
836     my ($clogcmd, $descbase, $fn) = @_;
837     # @$clogcmd is qw(dpkg-parsechangelog ...some...options...)
838     # calls $fn->($thisstanza, $desc);
839     debugcmd "|",@$clogcmd;
840     open CLOGS, "-|", @$clogcmd or confess "$!";
841     for (;;) {
842         my $stanzatext = do { local $/=""; <CLOGS>; };
843         printdebug "clogp stanza ".Dumper($stanzatext) if $debuglevel>1;
844         last if !defined $stanzatext;
845
846         my $desc = "$descbase, entry no.$.";
847         open my $stanzafh, "<", \$stanzatext or confess;
848         my $thisstanza = parsecontrolfh $stanzafh, $desc, 1;
849
850         $fn->($thisstanza, $desc);
851     }
852     confess "$!" if CLOGS->error;
853     close CLOGS or $?==SIGPIPE or failedcmd @$clogcmd;
854 }       
855
856 sub make_commit ($$) {
857     my ($parents, $message_paras) = @_;
858     my $tree = cmdoutput qw(git write-tree);
859     my @cmd = (qw(git commit-tree), $tree);
860     push @cmd, qw(-p), $_ foreach @$parents;
861     push @cmd, qw(-m), $_ foreach @$message_paras;
862     return cmdoutput @cmd;
863 }
864
865 sub hash_commit ($) {
866     my ($file) = @_;
867     return cmdoutput qw(git hash-object -w -t commit), $file;
868 }
869
870 sub hash_commit_text ($) {
871     my ($text) = @_;
872     my ($out, $in);
873     my @cmd = (qw(git hash-object -w -t commit --stdin));
874     debugcmd "|",@cmd;
875     print Dumper($text) if $debuglevel > 1;
876     my $child = open2($out, $in, @cmd) or confess "$!";
877     my $h;
878     eval {
879         print $in $text or confess "$!";
880         close $in or confess "$!";
881         $h = <$out>;
882         $h =~ m/^\w+$/ or confess;
883         $h = $&;
884         printdebug "=> $h\n";
885     };
886     close $out;
887     waitpid $child, 0 == $child or confess "$child $!";
888     $? and failedcmd @cmd;
889     return $h;
890 }
891
892 sub reflog_cache_insert ($$$) {
893     my ($ref, $cachekey, $value) = @_;
894     # you must call this in $maindir
895     # you must have called record_maindir
896
897     # When we no longer need to support squeeze, use --create-reflog
898     # instead of this:
899     my $parent = $ref; $parent =~ s{/[^/]+$}{};
900     ensurepath "$maindir_gitcommon/logs", "$parent";
901     my $makelogfh = new IO::File "$maindir_gitcommon/logs/$ref", '>>'
902       or confess "$!";
903
904     my $oldcache = git_get_ref $ref;
905
906     if ($oldcache eq $value) {
907         my $tree = cmdoutput qw(git rev-parse), "$value:";
908         # git update-ref doesn't always update, in this case.  *sigh*
909         my $authline = (ucfirst _us()).
910             ' <'._us().'@example.com> 1000000000 +0000';
911         my $dummy = hash_commit_text <<ENDU.(__ <<END);
912 tree $tree
913 parent $value
914 author $authline
915 committer $authline
916
917 ENDU
918 Dummy commit - do not use
919 END
920         runcmd qw(git update-ref -m), _us()." - dummy", $ref, $dummy;
921     }
922     runcmd qw(git update-ref -m), $cachekey, $ref, $value;
923 }
924
925 sub reflog_cache_lookup ($$) {
926     my ($ref, $cachekey) = @_;
927     # you may call this in $maindir or in a playtree
928     # you must have called record_maindir
929     my @cmd = (qw(git log -g), '--pretty=format:%H %gs', $ref);
930     debugcmd "|(probably)",@cmd;
931     my $child = open GC, "-|";  defined $child or confess "$!";
932     if (!$child) {
933         chdir $maindir or confess "$!";
934         if (!stat "$maindir_gitcommon/logs/$ref") {
935             $! == ENOENT or confess "$!";
936             printdebug ">(no reflog)\n";
937             finish 0;
938         }
939         exec @cmd; die f_ "exec %s: %s\n", $cmd[0], $!;
940     }
941     while (<GC>) {
942         chomp;
943         printdebug ">| ", $_, "\n" if $debuglevel > 1;
944         next unless m/^(\w+) (\S.*\S)$/ && $2 eq $cachekey;
945         close GC;
946         return $1;
947     }
948     confess "$!" if GC->error;
949     failedcmd unless close GC;
950     return undef;
951 }
952
953 # ========== playground handling ==========
954
955 # terminology:
956 #
957 #   $maindir      user's git working tree
958 #   playground    area in .git/ where we can make files, unpack, etc. etc.
959 #   playtree      git working tree sharing object store with the user's
960 #                 inside playground, or identical to it
961 #
962 # other globals
963 #
964 #   $local_git_cfg    hash of arrays of values: git config from $maindir
965 #
966 # expected calling pattern
967 #
968 #  firstly
969 #
970 #    [record_maindir]
971 #      must be run in directory containing .git
972 #      assigns to $maindir if not already set
973 #      also calls git_slurp_config_src to record git config
974 #        in $local_git_cfg, unless it's already set
975 #
976 #    fresh_playground SUBDIR_PATH_COMPONENTS
977 #      e.g fresh_playground 'dgit/unpack' ('.git/' is implied)
978 #      default SUBDIR_PATH_COMPONENTS is playground_subdir
979 #      calls record_maindir
980 #      sets up a new playground (destroying any old one)
981 #      returns playground pathname
982 #      caller may call multiple times with different subdir paths
983 #       createing different playgrounds
984 #
985 #    ensure_a_playground SUBDIR_PATH_COMPONENTS
986 #      like fresh_playground except:
987 #      merely ensures the directory exists; does not delete an existing one
988 #
989 #  then can use
990 #
991 #    changedir playground
992 #    changedir $maindir
993 #
994 #    playtree_setup
995 #            # ^ call in some (perhaps trivial) subdir of playground
996 #
997 #    rmtree playground
998
999 # ----- maindir -----
1000
1001 our $local_git_cfg;
1002
1003 sub record_maindir () {
1004     if (!defined $maindir) {
1005         $maindir = must_getcwd();
1006         if (!stat "$maindir/.git") {
1007             fail f_ "cannot stat %s/.git: %s", $maindir, $!;
1008         }
1009         if (-d _) {
1010             # we fall back to this in case we have a pre-worktree
1011             # git, which may not know git rev-parse --git-common-dir
1012             $maindir_gitdir    = "$maindir/.git";
1013             $maindir_gitcommon = "$maindir/.git";
1014         } else {
1015             $maindir_gitdir    = cmdoutput qw(git rev-parse --git-dir);
1016             $maindir_gitcommon = cmdoutput qw(git rev-parse --git-common-dir);
1017         }
1018     }
1019     $local_git_cfg //= git_slurp_config_src 'local';
1020 }
1021
1022 # ----- playgrounds -----
1023
1024 sub ensure_a_playground_parent ($) {
1025     my ($spc) = @_;
1026     record_maindir();
1027     $spc = "$maindir_gitdir/$spc";
1028     my $parent = dirname $spc;
1029     mkdir $parent or $!==EEXIST or fail f_
1030         "failed to mkdir playground parent %s: %s", $parent, $!;
1031     return $spc;
1032 }    
1033
1034 sub ensure_a_playground ($) {
1035     my ($spc) = @_;
1036     $spc = ensure_a_playground_parent $spc;
1037     mkdir $spc or $!==EEXIST or fail f_
1038         "failed to mkdir a playground %s: %s", $spc, $!;
1039     return $spc;
1040 }    
1041
1042 sub fresh_playground ($) {
1043     my ($spc) = @_;
1044     $spc = ensure_a_playground_parent $spc;
1045     rmtree $spc;
1046     mkdir $spc or fail f_
1047         "failed to mkdir the playground %s: %s", $spc, $!;
1048     return $spc;
1049 }
1050
1051 # ----- playtrees -----
1052
1053 sub playtree_setup () {
1054     # for use in the playtree
1055     # $maindir must be set, eg by calling record_maindir or fresh_playground
1056     # this is confusing: we have
1057     #   .                   playtree, not a worktree, has .git/, our cwd
1058     #   $maindir            might be a worktree so
1059     #   $maindir_gitdir     contains our main working "dgit", HEAD, etc.
1060     #   $maindir_gitcommon  the shared stuff, including .objects
1061
1062     # we need to invoke git-playtree-setup via git because
1063     # there may be config options it needs which are only available
1064     # to us, sensibly, in @git
1065
1066     # And, we look for it in @INC too.  This is a bit perverse.
1067     # We do this because in the Debian packages we want to have
1068     # a copy of this script in each binary package, rather than
1069     # making yet another .deb or tangling the dependencies.
1070     # @INC is conveniently available.
1071     my $newpath = join ':', +(grep { !m/:/ } @INC),
1072                   '/usr/share/dgit', $ENV{PATH};
1073     runcmd qw(env), "PATH=$newpath", @git, qw(playtree-setup .);
1074
1075     ensuredir '.git/info';
1076     open GA, "> .git/info/attributes" or confess "$!";
1077     print GA "* $negate_harmful_gitattrs\n" or confess "$!";
1078     close GA or confess "$!";
1079 }
1080
1081 1;