X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;ds=sidebyside;f=Debian%2FDgit.pm;h=80b693b68990be00a1f5d003a331d25e5fcefb90;hb=73806a4b22c2265bac6a269bd5ba8abc81f88ae1;hp=dcecbd1a840e7f0c7718b398d585e2e4d4e3fe77;hpb=643fe03fd85c430ca77c9a3a59a4d9e6f99e9f7c;p=dgit.git diff --git a/Debian/Dgit.pm b/Debian/Dgit.pm index dcecbd1a..80b693b6 100644 --- a/Debian/Dgit.pm +++ b/Debian/Dgit.pm @@ -29,6 +29,11 @@ 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 (); @@ -39,26 +44,49 @@ BEGIN { @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 + runcmd shell_cmd cmdoutput cmdoutput_errok git_rev_parse git_cat_file - git_get_ref git_for_each_ref + 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 NOCOMMITCHECK)] ); - @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; @@ -70,6 +98,20 @@ 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 @@ -82,6 +124,10 @@ 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 () { @@ -96,24 +142,24 @@ sub forkcheck_mainprocess () { sub setup_sigwarn () { forkcheck_setup(); $SIG{__WARN__} = sub { - die $_[0] if forkcheck_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; @@ -122,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 ($) { @@ -138,7 +198,7 @@ sub messagequote ($) { sub shellquote { my @out; local $_; - defined or confess 'internal error' foreach @_; + defined or confess __ 'internal error' foreach @_; foreach my $a (@_) { $_ = $a; if (!length || m{[^-=_./:0-9a-z]}i) { @@ -154,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 ($) { @@ -182,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]); } @@ -194,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 ($) { @@ -231,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"; } } @@ -276,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) {
@@ -310,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 ($) {
@@ -324,27 +436,51 @@ sub git_rev_parse ($) {
return cmdoutput qw(git rev-parse), "$_[0]~0";
}
-sub git_cat_file ($) {
- my ($objname) = @_;
+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 die $!;
+ $gcf_pid = open2 $gcf_o, $gcf_i, @cmd or confess $!;
}
printdebug "GCF>| ", $objname, "\n";
- print $gcf_i $objname, "\n" or die $!;
+ print $gcf_i $objname, "\n" or confess $!;
my $x = <$gcf_o>;
printdebug "GCF<| ", $x;
- if ($x =~ m/ (missing)$/) { return ($1, undef); }
- my ($type, $size) = $x =~ m/^.* (\w+) (\d+)\n/ or die "$objname ?";
+ 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 die "$objname $!";
+ (read $gcf_o, $data, $size) == $size or confess "$objname $!";
$x = <$gcf_o>;
- $x eq "\n" or die "$objname ($_) $!";
- return ($type, $data);
+ $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 ($$;$) {
@@ -358,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 (