X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;ds=sidebyside;f=Debian%2FDgit.pm;h=fc146a3aebe0febd0ed589168860710c1cd84c6b;hb=f7a2d017449c7877b095b5761aedef5e5b29c458;hp=50e3b860a71ae409c6d803b1cd36788f7617e61a;hpb=801b029f697680642110406338ccae7a0179e6de;p=dgit.git diff --git a/Debian/Dgit.pm b/Debian/Dgit.pm index 50e3b860..fc146a3a 100644 --- a/Debian/Dgit.pm +++ b/Debian/Dgit.pm @@ -28,6 +28,12 @@ use IO::Handle; use Config; use Digest::SHA; use Data::Dumper; +use IPC::Open2; +use File::Path; +use File::Basename; +use Dpkg::Control::Hash; +use Debian::Dgit::ExitStatus; +use Debian::Dgit::I18n; BEGIN { use Exporter (); @@ -35,27 +41,52 @@ BEGIN { $VERSION = 1.00; @ISA = qw(Exporter); - @EXPORT = qw(setup_sigwarn + @EXPORT = qw(setup_sigwarn forkcheck_setup forkcheck_mainprocess dep14_version_mangle debiantags debiantag_old debiantag_new + debiantag_maintview + upstreamversion + stripepoch source_file_leafname is_orig_file_of_p_v server_branch server_ref stat_exists link_ltarget hashfile - fail ensuredir executable_on_path + fail failmsg ensuredir must_getcwd executable_on_path waitstatusmsg failedcmd_waitstatus failedcmd_report_cmd failedcmd - cmdoutput cmdoutput_errok - git_rev_parse git_get_ref git_for_each_ref + runcmd shell_cmd cmdoutput cmdoutput_errok + git_rev_parse git_cat_file + git_get_ref git_get_symref git_for_each_ref git_for_each_tag_referring is_fast_fwd + git_check_unmodified + git_reflog_action_msg git_update_ref_cmd + make_commit_text + reflog_cache_insert reflog_cache_lookup $package_re $component_re $deliberately_re + $distro_re $versiontag_re $series_filename_re + $orig_f_comp_re $orig_f_sig_re $orig_f_tail_re + $extra_orig_namepart_re + $git_null_obj $branchprefix + $ffq_refprefix $gdrlast_refprefix initdebug enabledebug enabledebuglevel printdebug debugcmd + $printdebug_when_debuglevel $debugcmd_when_debuglevel $debugprefix *debuglevel *DEBUG - shellquote printcmd messagequote); + shellquote printcmd messagequote + $negate_harmful_gitattrs + changedir git_slurp_config_src + gdr_ffq_prev_branchinfo + parsecontrolfh parsecontrol parsechangelog + getfield parsechangelog_loop + playtree_setup); # implicitly uses $main::us - %EXPORT_TAGS = ( policyflags => [qw(NOFFCHECK FRESHREPO)] ); - @EXPORT_OK = @{ $EXPORT_TAGS{policyflags} }; + %EXPORT_TAGS = ( policyflags => [qw(NOFFCHECK FRESHREPO NOCOMMITCHECK)], + playground => [qw(record_maindir $maindir $local_git_cfg + $maindir_gitdir $maindir_gitcommon + fresh_playground + ensure_a_playground)]); + @EXPORT_OK = ( @{ $EXPORT_TAGS{policyflags} }, + @{ $EXPORT_TAGS{playground} } ); } our @EXPORT_OK; @@ -63,7 +94,24 @@ our @EXPORT_OK; our $package_re = '[0-9a-z][-+.0-9a-z]*'; our $component_re = '[0-9a-zA-Z][-+.0-9a-zA-Z]*'; our $deliberately_re = "(?:TEST-)?$package_re"; +our $distro_re = $component_re; +our $versiontag_re = qr{[-+.\%_0-9a-zA-Z/]+}; our $branchprefix = 'dgit'; +our $series_filename_re = qr{(?:^|\.)series(?!\n)$}s; +our $extra_orig_namepart_re = qr{[-0-9a-z]+}; +our $orig_f_comp_re = qr{orig(?:-$extra_orig_namepart_re)?}; +our $orig_f_sig_re = '\\.(?:asc|gpg|pgp)'; +our $orig_f_tail_re = "$orig_f_comp_re\\.tar(?:\\.\\w+)?(?:$orig_f_sig_re)?"; +our $git_null_obj = '0' x 40; +our $ffq_refprefix = 'ffq-prev'; +our $gdrlast_refprefix = 'debrebase-last'; +our $printdebug_when_debuglevel = 1; +our $debugcmd_when_debuglevel = 1; + +# these three all go together, only valid after record_maindir +our $maindir; +our $maindir_gitdir; +our $maindir_gitcommon; # policy hook exit status bits # see dgit-repos-server head comment for documentation @@ -71,31 +119,47 @@ our $branchprefix = 'dgit'; # dynamic loader, runtime, etc., failures, which report 127 or 255 sub NOFFCHECK () { return 0x2; } sub FRESHREPO () { return 0x4; } +sub NOCOMMITCHECK () { return 0x8; } our $debugprefix; our $debuglevel = 0; +our $negate_harmful_gitattrs = + "-text -eol -crlf -ident -filter -working-tree-encoding"; + # ^ when updating this, alter the regexp in dgit:is_gitattrs_setup + +our $forkcheck_mainprocess; + +sub forkcheck_setup () { + $forkcheck_mainprocess = $$; +} + +sub forkcheck_mainprocess () { + # You must have called forkcheck_setup or setup_sigwarn already + getppid != $forkcheck_mainprocess; +} + sub setup_sigwarn () { - our $sigwarn_mainprocess = $$; + forkcheck_setup(); $SIG{__WARN__} = sub { - die $_[0] unless getppid == $sigwarn_mainprocess; + confess $_[0] if forkcheck_mainprocess; }; } sub initdebug ($) { ($debugprefix) = @_; - open DEBUG, ">/dev/null" or die $!; + open DEBUG, ">/dev/null" or confess $!; } sub enabledebug () { - open DEBUG, ">&STDERR" or die $!; + open DEBUG, ">&STDERR" or confess $!; DEBUG->autoflush(1); $debuglevel ||= 1; } sub enabledebuglevel ($) { my ($newlevel) = @_; # may be undef (eg from env var) - die if $debuglevel; + confess if $debuglevel; $newlevel //= 0; $newlevel += 0; return unless $newlevel; @@ -104,7 +168,21 @@ sub enabledebuglevel ($) { } sub printdebug { - print DEBUG $debugprefix, @_ or die $! if $debuglevel>0; + # Prints a prefix, and @_, to DEBUG. @_ should normally contain + # a trailing \n. + + # With no (or only empty) arguments just prints the prefix and + # leaves the caller to do more with DEBUG. The caller should make + # sure then to call printdebug with something ending in "\n" to + # get the prefix right in subsequent calls. + + return unless $debuglevel >= $printdebug_when_debuglevel; + our $printdebug_noprefix; + print DEBUG $debugprefix unless $printdebug_noprefix; + pop @_ while @_ and !length $_[-1]; + return unless @_; + print DEBUG @_ or confess $!; + $printdebug_noprefix = $_[-1] !~ m{\n$}; } sub messagequote ($) { @@ -120,6 +198,7 @@ sub messagequote ($) { sub shellquote { my @out; local $_; + defined or confess __ 'internal error' foreach @_; foreach my $a (@_) { $_ = $a; if (!length || m{[^-=_./:0-9a-z]}i) { @@ -135,14 +214,15 @@ sub shellquote { sub printcmd { my $fh = shift @_; my $intro = shift @_; - print $fh $intro," " or die $!; - print $fh shellquote @_ or die $!; - print $fh "\n" or die $!; + print $fh $intro," " or confess $!; + print $fh shellquote @_ or confess $!; + print $fh "\n" or confess $!; } sub debugcmd { my $extraprefix = shift @_; - printcmd(\*DEBUG,$debugprefix.$extraprefix,@_) if $debuglevel>0; + printcmd(\*DEBUG,$debugprefix.$extraprefix,@_) + if $debuglevel >= $debugcmd_when_debuglevel; } sub dep14_version_mangle ($) { @@ -163,11 +243,40 @@ sub debiantag_new ($$) { return "archive/$distro/".dep14_version_mangle $v; } +sub debiantag_maintview ($$) { + my ($v,$distro) = @_; + return "$distro/".dep14_version_mangle $v; +} + sub debiantags ($$) { my ($version,$distro) = @_; map { $_->($version, $distro) } (\&debiantag_new, \&debiantag_old); } +sub stripepoch ($) { + my ($vsn) = @_; + $vsn =~ s/^\d+\://; + return $vsn; +} + +sub upstreamversion ($) { + my ($vsn) = @_; + $vsn =~ s/-[^-]+$//; + return $vsn; +} + +sub source_file_leafname ($$$) { + my ($package,$vsn,$sfx) = @_; + return "${package}_".(stripepoch $vsn).$sfx +} + +sub is_orig_file_of_p_v ($$$) { + my ($f, $package, $upstreamvsn) = @_; + my $base = source_file_leafname $package, $upstreamvsn, ''; + return 0 unless $f =~ m/^\Q$base\E\.$orig_f_tail_re$/; + return 1; +} + sub server_branch ($) { return "$branchprefix/$_[0]"; } sub server_ref ($) { return "refs/".server_branch($_[0]); } @@ -175,26 +284,36 @@ sub stat_exists ($) { my ($f) = @_; return 1 if stat $f; return 0 if $!==&ENOENT; - die "stat $f: $!"; + confess "stat $f: $!"; } sub _us () { $::us // ($0 =~ m#[^/]*$#, $&); } -sub fail { - my $s = "@_\n"; - $s =~ s/\n\n$/\n/; +sub failmsg { + my $s = f_ "error: %s\n", "@_"; + $s =~ s/\n\n$/\n/g; my $prefix = _us().": "; $s =~ s/^/$prefix/gm; - die $s; + return "\n".$s; +} + +sub fail { + die failmsg @_; } sub ensuredir ($) { my ($dir) = @_; # does not create parents return if mkdir $dir; return if $! == EEXIST; - die "mkdir $dir: $!"; + confess "mkdir $dir: $!"; +} + +sub must_getcwd () { + my $d = getcwd(); + defined $d or fail f_ "getcwd failed: %s\n", $!; + return $d; } sub executable_on_path ($) { @@ -212,32 +331,32 @@ our @signames = split / /, $Config{sig_name}; sub waitstatusmsg () { if (!$?) { - return "terminated, reporting successful completion"; + return __ "terminated, reporting successful completion"; } elsif (!($? & 255)) { - return "failed with error exit status ".WEXITSTATUS($?); + return f_ "failed with error exit status %s", WEXITSTATUS($?); } elsif (WIFSIGNALED($?)) { my $signum=WTERMSIG($?); - return "died due to fatal signal ". + return f_ "died due to fatal signal %s", ($signames[$signum] // "number $signum"). ($? & 128 ? " (core dumped)" : ""); # POSIX(3pm) has no WCOREDUMP } else { - return "failed with unknown wait status ".$?; + return f_ "failed with unknown wait status %s", $?; } } sub failedcmd_report_cmd { my $intro = shift @_; - $intro //= "failed command"; - { local ($!); printcmd \*STDERR, _us().": $intro:", @_ or die $!; }; + $intro //= __ "failed command"; + { local ($!); printcmd \*STDERR, _us().": $intro:", @_ or confess $!; }; } sub failedcmd_waitstatus { if ($? < 0) { - return "failed to fork/exec: $!"; + return f_ "failed to fork/exec: %s", $!; } elsif ($?) { - return "subprocess ".waitstatusmsg(); + return f_ "subprocess %s", waitstatusmsg(); } else { - return "subprocess produced invalid output"; + return __ "subprocess produced invalid output"; } } @@ -257,14 +376,26 @@ sub failedcmd { fail failedcmd_waitstatus(); } +sub runcmd { + debugcmd "+",@_; + $!=0; $?=-1; + failedcmd @_ if system @_; +} + +sub shell_cmd { + my ($first_shell, @cmd) = @_; + return qw(sh -ec), $first_shell.'; exec "$@"', 'x', @cmd; +} + sub cmdoutput_errok { confess Dumper(\@_)." ?" if grep { !defined } @_; + local $printdebug_when_debuglevel = $debugcmd_when_debuglevel; debugcmd "|",@_; - open P, "-|", @_ or die "$_[0] $!"; + open P, "-|", @_ or confess "$_[0] $!"; my $d; $!=0; $?=0; { local $/ = undef; $d =
; }
- die $! if P->error;
+ confess $! if P->error;
if (!close P) { printdebug "=>!$?\n"; return undef; }
chomp $d;
if ($debuglevel > 0) {
@@ -291,7 +422,7 @@ sub link_ltarget ($$) {
}
my $r = link $old, $new;
$r = symlink $old, $new if !$r && $!==EXDEV;
- $r or die "(sym)link $old $new: $!";
+ $r or fail "(sym)link $old $new: $!\n";
}
sub hashfile ($) {
@@ -305,6 +436,53 @@ sub git_rev_parse ($) {
return cmdoutput qw(git rev-parse), "$_[0]~0";
}
+sub git_cat_file ($;$) {
+ my ($objname, $etype) = @_;
+ # => ($type, $data) or ('missing', undef)
+ # in scalar context, just the data
+ # if $etype defined, dies unless type is $etype or in @$etype
+ our ($gcf_pid, $gcf_i, $gcf_o);
+ local $printdebug_when_debuglevel = $debugcmd_when_debuglevel;
+ my $chk = sub {
+ my ($gtype, $data) = @_;
+ if ($etype) {
+ $etype = [$etype] unless ref $etype;
+ confess "$objname expected @$etype but is $gtype"
+ unless grep { $gtype eq $_ } @$etype;
+ }
+ return ($gtype, $data);
+ };
+ if (!$gcf_pid) {
+ my @cmd = qw(git cat-file --batch);
+ debugcmd "GCF|", @cmd;
+ $gcf_pid = open2 $gcf_o, $gcf_i, @cmd or confess $!;
+ }
+ printdebug "GCF>| ", $objname, "\n";
+ print $gcf_i $objname, "\n" or confess $!;
+ my $x = <$gcf_o>;
+ printdebug "GCF<| ", $x;
+ if ($x =~ m/ (missing)$/) { return $chk->($1, undef); }
+ my ($type, $size) = $x =~ m/^.* (\w+) (\d+)\n/ or confess "$objname ?";
+ my $data;
+ (read $gcf_o, $data, $size) == $size or confess "$objname $!";
+ $x = <$gcf_o>;
+ $x eq "\n" or confess "$objname ($_) $!";
+ return $chk->($type, $data);
+}
+
+sub git_get_symref (;$) {
+ my ($symref) = @_; $symref //= 'HEAD';
+ # => undef if not a symref, otherwise refs/...
+ my @cmd = (qw(git symbolic-ref -q HEAD));
+ my $branch = cmdoutput_errok @cmd;
+ if (!defined $branch) {
+ $?==256 or failedcmd @cmd;
+ } else {
+ chomp $branch;
+ }
+ return $branch;
+}
+
sub git_for_each_ref ($$;$) {
my ($pattern,$func,$gitdir) = @_;
# calls $func->($objid,$objtype,$fullrefname,$reftail);
@@ -316,22 +494,22 @@ sub git_for_each_ref ($$;$) {
if (defined $gitdir) {
@cmd = ('sh','-ec','cd "$1"; shift; exec "$@"','x', $gitdir, @cmd);
}
- open GFER, "-|", @cmd or die $!;
+ open GFER, "-|", @cmd or confess $!;
debugcmd "|", @cmd;
while (