In-tree, there are metadata files in .topbloke
+ Files which are per-branch and do not inherit any contents
+ or changes from dependencies:
+
msg brach "commit message"
deps direct dependencies, one per line
topbloke branch name
/refs/heads/<something>
- included actual included branches, one per line
- topbloke branch name
-
flags flags that apply to this branch, one per line
- unknown flags starting with [a-z] are ok;
+ Unknown flags starting with [a-z] are ok;
otherwise fatal. Currently defined flags:
Deleted branch is deleted
+ Files which not inherit contents and changes from dependencies:
+
+ included actual included branches, one per line
+ topbloke branch name
+
+ pflags flags that apply to this branch and all its
+ dependencies (ie, flags that propagate)
+ Unknown flags starting with [a-z] are ok;
+ otherwise fatal. No currently defined flags
+
+
<full-name> has the format:
<email>@<domain.name>/<yyyy>-<mm>-<dd>T<hh><mm><ss>Z/<nickname-path>
where nickname-path's first component must not start with a digit
print STDERR "DEBUG: $msg\n" or die $!;
}
-sub run_git_1line {
+sub run_git {
+ # takes optional prefix arguments:
+ # coderef hook to call for each line read,
+ # with $_ containing chomped line; if not supplied,
+ # output is not read
+ # scalarref place to store exit status; if not supplied,
+ # nonzero exit status is fatal
+ my ($estatusr,$linecallr);
+ while (ref $_[0]) {
+ my $ref = shift @_;
+ if (ref $ref eq 'SCALAR') {
+ $estatusr = $ref;
+ } elsif (ref $ref eq 'CODE') {
+ $linecallr = $ref;
+ } else {
+ die ref($ref)." @_ ?";
+ }
+ }
open GIT, "-|", 'git', @_ or die $!;
- my $l = <GIT>;
- $?=0;
- close GIT or die "git @_ failed ($?)\n";
- chomp $l or die "@_ ?";
+ if ($linecallr) {
+ while (<GIT>) {
+ chomp or die "$_ ?";
+ $linecallr->();
+ }
+ GIT->eof or die $!;
+ }
+ if (!close GIT) {
+ die "git @_ $!" if $!;
+ die unless $?;
+ die "git @_ ($?)" unless $estatusr;
+ $$estatusr = $?;
+ } else {
+ $$estatusr = 0 if $estatusr;
+ }
+}
+
+sub run_git_1line {
+ my $l;
+ run_git(sub { $l = $_; }, @_);
+ die "git @_ ?" unless defined $l;
return $l;
}
-sub run_git_1line_estatus {
- open GIT, "-|", 'git', @_ or die $!;
- my $l = <GIT>;
- $?=0;
- if (close GIT) {
- chomp $l or die "@_ ?";
- return (0,$l);
- } else {
- die unless $?;
- return ($?,undef);
- }
+sub run_git_check_nooutput {
+ my ($what) = shift @_;
+ run_git(sub { die "$what $_\n"; }, @_);
}
-sub run_git_nooutput {
- my $rc = system('git', @_);
- die "git @_ failed ($rc)" if $rc;
+sub run_git_test_anyoutput {
+ my $any = 0;
+ run_git(sub { $any=1; }, @_);
+ return $any;
+}
+
+sub git_config ($$) {
+ my ($cfgvar, $default) = @_;
+ my ($l, $estatus);
+ run_git(\$estatus, sub {
+ die if defined $l;
+ $l = $_; },
+ qw(config), $cfgvar);
+ if (defined $l) {
+ die "$cfgvar ($estatus)" if $estatus;
+ return $l;
+ } else {
+ die "$cfgvar ($estatus)" unless $estatus==0 || $estatus==256;
+ return $default;
+ }
}
sub git_dir () {
}
sub setup_config () {
- my (@files) = (qw(msg deps included flags));
+ my (@files) = (qw(msg deps included flags pflags));
my $version = 1;
foreach my $iteration (qw(0 1)) {
foreach my $file (@files) {
my $cfgname = "merge.topbloke-$file";
- my ($current_estatus, $current) =
- run_git_1line_estatus(qw(config), "$cfgname.driver");
+ my $current_estatus;
+ my $current = run_git_1line(\$current_estatus,
+ qw(config), "$cfgname.driver");
$current = "## failed $current_estatus" if $current_estatus;
next if $current =~ m/^topbloke-merge-driver --v$version /o;
die "$file $current ?" if $iteration;
debug("setting merge driver $file");
- run_git_nooutput(qw(config), "$cfgname.name",
- "topbloke merge driver for $file");
- run_git_nooutput(qw(config), "$cfgname.driver",
- "topbloke-merge-driver --v$version".
- " $file %O %A %B %L");
+ run_git(qw(config), "$cfgname.name",
+ "topbloke merge driver for $file");
+ run_git(qw(config), "$cfgname.driver",
+ "topbloke-merge-driver --v$version".
+ " $file %O %A %B %L");
}
my ($newattrs, $attrsfile);
foreach my $file (@files) {
}
sub check_no_unwanted_metadata ($) {
+ # for checking foreign branches aren't contaminated
my ($gitbranch) = @_;
- open GIT, "-|", 'git', qw(ls-tree --name-status),
- "$gitbranch:", qw(.topbloke/included .topbloke/flags)
- or die $!;
- while (<GIT>) {
- chomp or die;
- die "foreign unexpectedly contains $_\n";
- }
- GIT->error and die $!;
- close GIT or die $!;
+ run_git_check_nooutput('foreign unexpectedly contains',
+ qw(ls-tree --name-only),
+ "$gitbranch:",
+ qw(.topbloke));
}
1;
check_no_unwanted_metadata('HEAD')
if $current->{Kind} ne 'tip';
+# For the metadata files in .topbloke, we hope that the user
+# doesn't modify them. If they do then they get to keep all the pieces.
+#
+# For .topbloke/msg, if it's modified by the user (ie, if working
+# version differs from HEAD) we keep that, and we stage it unless
+# the cached version differs from the HEAD.
+
my $newbranch = "$spec->{Email}\@$spec->{Domain}/$spec->{Date}/$spec->{Nick}";
$newbranch = run_git_1line(qw(check-ref-format --print), $newbranch);
setup_config();
+if (!run_git_test_anyoutput(qw(diff --name-only HEAD -- .topbloke/msg)) {
+ open NM, '>', ".topbloke/msg.tmp" or die $!;
+ my $author = run_git_1line(qw(var GIT_AUTHOR_IDENT));
+ $author =~ s/ \d+ [-+]\d+$//;
+ print NM "From: $author\n" or die $!;
+ foreach my $h (qw(To CC BCC)) {
+ my $estatus;
+ run_git(\$estatus, sub { print NM "$h: $_" or die $!; },
+ qw(config), "topbloke.".lc $h);
+ die "$h $estatus" unless $estatus==0 || $estatus==256;
+ }
+ $subjprefix = git_config('topbloke.subjectprefix', '');
+ print NM <END or die $!;
+Subject: [${subprefix}PATCH] $spec->{Nick}
+
+<patch description>
+
+Signed-off-by: $author
+END
+ run_git(qw(add .topbloke/msg));
+ print " created and staged new .topbloke/msg\n";
+} else {
+ if (!run_git_test_anyoutput(qw(diff --cached --name-only HEAD --
+ .topblokemsg))) {
+ print " staged your modified .topbloke/msg\n";
+ run_git(qw(add .topbloke/msg));
+ } else {
+ print " left your (partially staged?) .topbloke/msg\n";
+ }
+}
+
fail () { echo >&2 "$0: $*"; exit 127; }
case "$1" in
---v1) on=tip ;;
---base-v1) on=base ;;
+--v1) how=tip ;;
+--v1-base) how=base ;;
+--v1-dep) how=dep ;;
*) fail "bad usage" ;;
esac
markersize=$5
case $on.$whichfile in
-tip.msg)
- exec git-merge-file --marker-size=$markersize \
- "$current" "$ancestor" "$other"
+dep.msg|dep.deps|dep.flags)
+ touch "$current"
+ exit 0
;;
base.msg|base.deps)
echo '# not applicable' >"$current"
exit 0
;;
-*.included|tip.deps|tip.flags)
- exec topbloke-merge-lists "$current" "$ancestor" "$other"
- ;;
base.flags)
- exec topbloke-merge-lists -UDeleted \
+ exec topbloke-merge-lists -UDeleted "$current" "$ancestor" "$other"
+ ;;
+tip.msg)
+ exec git-merge-file --marker-size=$markersize \
"$current" "$ancestor" "$other"
;;
+tip.deps|tip.flags|*.included|*.pflags)
+ exec topbloke-merge-lists "$current" "$ancestor" "$other"
+ ;;
*)
fail "huh $on $whichfile ?"
;;