chiark / gitweb /
using-these: New script to help with ad-hoc testing
[dgit.git] / dgit
diff --git a/dgit b/dgit
index dbfe1ac3278061116197233f630730564eefd121..caa2d75ce85e3ac3592d77a8eae47fa16b8213e7 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -143,6 +143,8 @@ our %opts_cfg_insertpos = map {
 } keys %opts_opt_map;
 
 sub parseopts_late_defaults();
+sub setup_gitattrs(;$);
+sub check_gitattrs($$);
 
 our $keyid;
 
@@ -667,7 +669,7 @@ sub git_get_config ($) {
     my ($c) = @_;
     foreach my $src (@gitcfgsources) {
        my $l = $gitcfgs{$src}{$c};
-       croak "$l $c" if $l && !ref $l;
+       confess "internal error ($l $c)" if $l && !ref $l;
        printdebug"C $c ".(defined $l ?
                           join " ", map { messagequote "'$_'" } @$l :
                           "undef")."\n"
@@ -1317,6 +1319,8 @@ sub archive_query_aptget {
     return [ (getfield $pre_dsc, 'Version'), $uri ];
 }
 
+sub file_in_archive_aptget () { return undef; }
+
 #---------- `dummyapicat' archive query method ----------
 
 sub archive_query_dummycatapi { archive_query_ftpmasterapi @_; }
@@ -1695,8 +1699,14 @@ sub prep_ud (;$) {
 sub mktree_in_ud_here () {
     runcmd qw(git init -q);
     runcmd qw(git config gc.auto 0);
+    foreach my $copy (qw(user.email user.name user.useConfigOnly)) {
+       my $v = $gitcfgs{local}{$copy};
+       next unless $v;
+       runcmd qw(git config), $copy, $_ foreach @$v;
+    }
     rmtree('.git/objects');
     symlink '../../../../objects','.git/objects' or die $!;
+    setup_gitattrs(1);
 }
 
 sub git_write_tree () {
@@ -1985,7 +1995,14 @@ sub make_commit_text ($) {
 sub clogp_authline ($) {
     my ($clogp) = @_;
     my $author = getfield $clogp, 'Maintainer';
-    $author =~ s#,.*##ms;
+    if ($author =~ m/^[^"\@]+\,/) {
+       # single entry Maintainer field with unquoted comma
+       $author = ($& =~ y/,//rd).$'; # strip the comma
+    }
+    # git wants a single author; any remaining commas in $author
+    # are by now preceded by @ (or ").  It seems safer to punt on
+    # "..." for now rather than attempting to dequote or something.
+    $author =~ s#,.*##ms unless $author =~ m/"/;
     my $date = cmdoutput qw(date), '+%s %z', qw(-d), getfield($clogp,'Date');
     my $authline = "$author $date";
     $authline =~ m/$git_authline_re/o or
@@ -2307,6 +2324,7 @@ sub generate_commits_from_dsc () {
 
     my $authline = clogp_authline $clogp;
     my $changes = getfield $clogp, 'Changes';
+    $changes =~ s/^\n//; # Changes: \n
     my $cversion = getfield $clogp, 'Version';
 
     if (@tartrees) {
@@ -2569,7 +2587,7 @@ sub lrfetchref_used ($) {
 }
 
 sub git_lrfetch_sane {
-    my ($supplementary, @specs) = @_;
+    my ($url, $supplementary, @specs) = @_;
     # Make a 'refs/'.lrfetchrefs.'/*' be just like on server,
     # at least as regards @specs.  Also leave the results in
     # %lrfetchrefs_f, and arrange for lrfetchref_used to be
@@ -2600,8 +2618,6 @@ sub git_lrfetch_sane {
     # git fetch to try to generate it.  If we don't manage to generate
     # the target state, we try again.
 
-    my $url = access_giturl();
-
     printdebug "git_lrfetch_sane suppl=$supplementary specs @specs\n";
 
     my $specre = join '|', map {
@@ -2741,7 +2757,8 @@ sub git_fetch_us () {
     push @specs, $rewritemap;
     push @specs, qw(heads/*) if deliberately_not_fast_forward;
 
-    git_lrfetch_sane 0, @specs;
+    my $url = access_giturl();
+    git_lrfetch_sane $url, 0, @specs;
 
     my %here;
     my @tagpats = debiantags('*',access_nomdistro);
@@ -2870,13 +2887,13 @@ END
                or fail <<END;
 .dsc Dgit metadata is in context of distro $dsc_distro
 for which we have no configured url;
-.dsc provices hinted url with protocol $proto which is unsafe.
+.dsc provides hinted url with protocol $proto which is unsafe.
 (can be overridden by config - consult documentation)
 END
            $url = $dsc_hint_url;
        }
 
-       git_lrfetch_sane 1, @fetch;
+       git_lrfetch_sane $url, 1, @fetch;
 
        return $lrf;
     };
@@ -3254,6 +3271,8 @@ END
 
     lrfetchref_used lrfetchref();
 
+    check_gitattrs($hash, "fetched source tree");
+
     unshift @end, $del_lrfetchrefs;
     return $hash;
 }
@@ -3315,11 +3334,88 @@ sub ensure_setup_existing_tree () {
     set_local_git_config $k, 'true';
 }
 
+sub open_gitattrs () {
+    my $gai = new IO::File ".git/info/attributes"
+       or $!==ENOENT
+       or die "open .git/info/attributes: $!";
+    return $gai;
+}
+
+sub is_gitattrs_setup () {
+    my $gai = open_gitattrs();
+    return 0 unless $gai;
+    while (<$gai>) {
+       return 1 if m{^\[attr\]dgit-defuse-attrs\s};
+    }
+    $gai->error and die $!;
+    return 0;
+}    
+
+sub setup_gitattrs (;$) {
+    my ($always) = @_;
+    return unless $always || access_cfg_bool(1, 'setup-gitattributes');
+
+    if (is_gitattrs_setup()) {
+       progress <<END;
+[attr]dgit-defuse-attrs already found in .git/info/attributes
+ not doing further gitattributes setup
+END
+       return;
+    }
+    my $af = ".git/info/attributes";
+    open GAO, "> $af.new" or die $!;
+    print GAO <<END or die $!;
+*      dgit-defuse-attrs
+[attr]dgit-defuse-attrs        -text -eol -crlf -ident -filter
+# ^ see GITATTRIBUTES in dgit(7) and dgit setup-new-tree in dgit(1)
+END
+    my $gai = open_gitattrs();
+    if ($gai) {
+       while (<$gai>) {
+           chomp;
+           print GAO $_, "\n" or die $!;
+       }
+       $gai->error and die $!;
+    }
+    close GAO or die $!;
+    rename "$af.new", "$af" or die "install $af: $!";
+}
+
 sub setup_new_tree () {
     setup_mergechangelogs();
     setup_useremail();
+    setup_gitattrs();
 }
 
+sub check_gitattrs ($$) {
+    my ($treeish, $what) = @_;
+
+    return if is_gitattrs_setup;
+
+    local $/="\0";
+    my @cmd = (@git, qw(ls-tree -lrz --), "${treeish}:");
+    debugcmd "|",@cmd;
+    my $gafl = new IO::File;
+    open $gafl, "-|", @cmd or die $!;
+    while (<$gafl>) {
+       chomp or die;
+       s/^\d+\s+\w+\s+\w+\s+(\d+)\t// or die;
+       next if $1 == 0;
+       next unless m{(?:^|/)\.gitattributes$};
+
+       # oh dear, found one
+       print STDERR <<END;
+dgit: warning: $what contains .gitattributes
+dgit: .gitattributes have not been defused.  Recommended: dgit setup-new-tree.
+END
+       close $gafl;
+       return;
+    }
+    # tree contains no .gitattributes files
+    $?=0; $!=0; close $gafl or failedcmd @cmd;
+}
+
+
 sub multisuite_suite_child ($$$) {
     my ($tsuite, $merginputs, $fn) = @_;
     # in child, sets things up, calls $fn->(), and returns undef
@@ -3519,6 +3615,7 @@ sub clone ($) {
     mkdir $dstdir or fail "create \`$dstdir': $!";
     changedir $dstdir;
     runcmd @git, qw(init -q);
+    setup_new_tree();
     clone_set_head();
     my $giturl = access_giturl(1);
     if (defined $giturl) {
@@ -3537,7 +3634,6 @@ sub clone ($) {
        $vcsgiturl =~ s/\s+-b\s+\S+//g;
        runcmd @git, qw(remote add vcs-git), $vcsgiturl;
     }
-    setup_new_tree();
     clone_finish($dstdir);
 }
 
@@ -3716,18 +3812,28 @@ sub pseudomerge_version_check ($$) {
        } else {
            my $v = $i_arch_v->[0];
            progress "Checking package changelog for archive version $v ...";
+           my $cd;
            eval {
                my @xa = ("-f$v", "-t$v");
                my $vclogp = parsechangelog @xa;
-               my $cv = [ (getfield $vclogp, 'Version'),
-                          "Version field from dpkg-parsechangelog @xa" ];
+               my $gf = sub {
+                   my ($fn) = @_;
+                   [ (getfield $vclogp, $fn),
+                     "$fn field from dpkg-parsechangelog @xa" ];
+               };
+               my $cv = $gf->('Version');
                infopair_cond_equal($i_arch_v, $cv);
+               $cd = $gf->('Distribution');
            };
            if ($@) {
                $@ =~ s/^dgit: //gm;
                fail "$@".
                    "Perhaps debian/changelog does not mention $v ?";
            }
+           fail <<END if $cd->[0] =~ m/UNRELEASED/;
+$cd->[1] is $cd->[0]
+Your tree seems to based on earlier (not uploaded) $v.
+END
        }
     }
     
@@ -3785,6 +3891,7 @@ sub splitbrain_pseudomerge ($$$$) {
     #
 
     return $dgitview unless defined $archive_hash;
+    return $dgitview if deliberately_not_fast_forward();
 
     printdebug "splitbrain_pseudomerge...\n";
 
@@ -4336,7 +4443,12 @@ sub cmd_clone {
 }
 
 sub branchsuite () {
-    my $branch = cmdoutput_errok @git, qw(symbolic-ref HEAD);
+    my @cmd = (@git, qw(symbolic-ref -q HEAD));
+    my $branch = cmdoutput_errok @cmd;
+    if (!defined $branch) {
+       $?==256 or failedcmd @cmd;
+       return undef;
+    }
     if ($branch =~ m#$lbranch_re#o) {
        return $1;
     } else {
@@ -6053,6 +6165,8 @@ sub import_dsc_result {
     my ($dstref, $newhash, $what_log, $what_msg) = @_;
     my @cmd = (@git, qw(update-ref -m), $what_log, $dstref, $newhash);
     runcmd @cmd;
+    check_gitattrs($newhash, "source tree");
+
     progress "dgit: import-dsc: $what_msg";
 }
 
@@ -6228,12 +6342,18 @@ sub cmd_archive_api_query {
     exec @cmd or fail "exec curl: $!\n";
 }
 
+sub repos_server_url () {
+    $package = '_dgit-repos-server';
+    local $access_forpush = 1;
+    local $isuite = 'DGIT-REPOS-SERVER';
+    my $url = access_giturl();
+}    
+
 sub cmd_clone_dgit_repos_server {
     badusage "need destination argument" unless @ARGV==1;
     my ($destdir) = @ARGV;
-    $package = '_dgit-repos-server';
-    local $access_forpush = 0;
-    my @cmd = (@git, qw(clone), access_giturl(), $destdir);
+    my $url = repos_server_url();
+    my @cmd = (@git, qw(clone), $url, $destdir);
     debugcmd ">",@cmd;
     exec @cmd or fail "exec git clone: $!\n";
 }
@@ -6241,24 +6361,31 @@ sub cmd_clone_dgit_repos_server {
 sub cmd_print_dgit_repos_server_source_url {
     badusage "no arguments allowed to dgit print-dgit-repos-server-source-url"
        if @ARGV;
-    $package = '_dgit-repos-server';
-    local $access_forpush = 0;
-    my $url = access_giturl();
+    my $url = repos_server_url();
     print $url, "\n" or die $!;
 }
 
 sub cmd_setup_mergechangelogs {
     badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV;
+    local $isuite = 'DGIT-SETUP-TREE';
     setup_mergechangelogs(1);
 }
 
 sub cmd_setup_useremail {
     badusage "no arguments allowed to dgit setup-useremail" if @ARGV;
+    local $isuite = 'DGIT-SETUP-TREE';
     setup_useremail(1);
 }
 
+sub cmd_setup_gitattributes {
+    badusage "no arguments allowed to dgit setup-useremail" if @ARGV;
+    local $isuite = 'DGIT-SETUP-TREE';
+    setup_gitattrs(1);
+}
+
 sub cmd_setup_new_tree {
     badusage "no arguments allowed to dgit setup-tree" if @ARGV;
+    local $isuite = 'DGIT-SETUP-TREE';
     setup_new_tree();
 }