13 our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
17 @EXPORT = qw(parse_branch_spec current_tb_branch run_git_1line
18 setup_config check_no_unwanted_metadata);
25 print STDERR "DEBUG: $msg\n" or die $!;
29 # takes optional prefix arguments:
30 # coderef hook to call for each line read,
31 # with $_ containing chomped line; if not supplied,
33 # scalarref place to store exit status; if not supplied,
34 # nonzero exit status is fatal
35 my ($estatusr,$linecallr);
38 if (ref $ref eq 'SCALAR') {
40 } elsif (ref $ref eq 'CODE') {
43 die ref($ref)." @_ ?";
46 open GIT, "-|", 'git', @_ or die $!;
55 die "git @_ $!" if $!;
57 die "git @_ ($?)" unless $estatusr;
60 $$estatusr = 0 if $estatusr;
66 run_git(sub { $l = $_; }, @_);
67 die "git @_ ?" unless defined $l;
71 sub run_git_check_nooutput {
72 my ($what) = shift @_;
73 run_git(sub { die "$what $_\n"; }, @_);
76 sub run_git_test_anyoutput {
78 run_git(sub { $any=1; }, @_);
83 my ($cfgvar, $default) = @_;
85 run_git(\$estatus, sub {
90 die "$cfgvar ($estatus)" if $estatus;
93 die "$cfgvar ($estatus)" unless $estatus==0 || $estatus==256;
100 if (!defined $git_dir) {
101 $git_dir = run_git_1line(qw(rev-parse --git-dir));
106 sub current_tb_branch () {
107 open R, git_dir().'/HEAD' or die "open HEAD $!";
108 my $ref = <R>; defined $ref or die $!;
111 if ($ref !~ s#^ref: ##) {
117 if ($ref =~ m#^refs/topbloke-(tip|base)s/([^/\@]*)\@([^/\@]*)/([^/]*)/#) {
125 DepSpec => "$2\@$3/$4/$'",
127 } elsif ($ref =~ m#^refs/heads/#) {
141 sub parse_branch_spec ($) {
144 my $spec = { }; # Email Domain DatePrefix DateNear Nick
146 my ($key,$val,$whats) = @_;
147 die "multiple $whats in branch spec\n" if exists $spec->{$key};
148 $spec->{$key} = $val;
152 if (s#([^/\@]*)\@([^/\@]*)/##) {
153 $set->('Email', $1, "email local parts") if length $1;
154 $set->('Domain', $2, "email domains") if length $1;
155 } elsif (s#([^/]*\~[^/]*)/##) {
158 open DATE, "-|", 'date','+%s','-d',$dspec or die $!;
160 close DATE or die "date parsing failed\n";
162 $set->('DateNear', $l, 'nearby dates');
163 } elsif (s#^([0-9][^/]*)/##) {
166 m/^\d{4}(?:-\d\d(?:-\d\d(?:T(?:\d\d(?:\d\d(?:\d\d(?:Z)?)?)?)?)?)?)?$/
167 or die "bad date prefix \`$dspec'\n";
168 $set->('DatePrefix', $dspec, 'date prefixes');
171 } elsif (s#^\.\./##) {
178 if (defined $rel_levels) {
179 my $branch = current_tb_branch();
180 if (!defined $branch->{Nick}) {
181 die "relative branch spec \`$orig',".
182 " but current branch not a topbloke branch\n";
184 my ($ceaddr,$cdate,@l) = split /\//, $branch->{Nick};
186 die "relative branch spec \`$orig' has too many ../s\n";
187 $_ = (join '/', @l[0..$#l-$rel_levels]).'/'.$_;
193 sub setup_config () {
194 my (@files) = (qw(msg deps included flags pflags));
196 foreach my $iteration (qw(0 1)) {
197 foreach my $file (@files) {
198 my $cfgname = "merge.topbloke-$file";
200 my $current = run_git_1line(\$current_estatus,
201 qw(config), "$cfgname.driver");
202 $current = "## failed $current_estatus" if $current_estatus;
203 next if $current =~ m/^topbloke-merge-driver --v$version /o;
204 die "$file $current ?" if $iteration;
205 debug("setting merge driver $file");
206 run_git(qw(config), "$cfgname.name",
207 "topbloke merge driver for $file");
208 run_git(qw(config), "$cfgname.driver",
209 "topbloke-merge-driver --v$version".
210 " $file %O %A %B %L");
212 my ($newattrs, $attrsfile);
213 foreach my $file (@files) {
214 my $path = ".topbloke/$file";
215 my $current = run_git_1line(qw(check-attr merge), $path);
216 $current =~ s#^\Q$path\E: merge: ## or die "$file $current ?";
217 my $want = "topbloke-$file";
218 next if $current eq $want;
219 die "$file $current ?" unless $current eq 'unspecified';
220 die "$file $current ?" if $iteration;
222 $attrsfile = git_dir()."/info/attributes";
223 $newattrs = new IO::File "$attrsfile.tmp", 'w'
224 or die "$attrsfile.tmp: $!";
225 if (!open OA, '<', "$attrsfile") {
226 die "$attrsfile $!" unless $!==&ENOENT;
229 print $newattrs $_ or die $!;
230 print "\n" or die $! unless chomp;
233 die $! unless close OA;
236 print $newattrs "$path\tmerge=$want\n" or die $!;
239 close $newattrs or die $!;
240 rename "$attrsfile.tmp", "$attrsfile" or die $!;
244 sub check_no_unwanted_metadata ($) {
245 # for checking foreign branches aren't contaminated
246 my ($gitbranch) = @_;
247 run_git_check_nooutput('foreign unexpectedly contains',
248 qw(ls-tree --name-only),