topgit import
topbloke {base,tip} is ff desc of topgit {base,tip}
with metadata adjusted
+ topbloke branch has all the deps of the topgit
+ branch (topgit branches mapped into topbloke
+ branches) plus the topgit branch itself named in
+ flags
reject topbloke imports of topgit branches which
also contain topbloke metadata
need patch name in metadata
Files which are per-patch and do not inherit any contents
or changes from bases or dependencies:
- msg brach "commit message"
+ msg patch "commit message"
("# not applicable" in bases)
deps direct dependencies, one per line
as either:
- topbloke patch name
- /refs/heads/<something>
- ("# not applicable" in bases)
-
- flags flags that apply to this patch, one per line
- base has its own, perhaps different, set of flags;
- Unknown flags starting with [a-z] are ok;
- otherwise fatal. Currently defined flags:
- Deleted patch is deleted
- (valid on tip only)
+ <topbloke patch name>
+ .f <ref name including refs/heads/>
+ .tg <ref name not including refs/heads/>
+ (empty in base branches)
+
+ props properties that apply to this patch, one per line
+ in the format "<name>[ <value>]" (exactly one
+ space) where value can contain spaces and
+ a missing value is distinct from a missing property
+ but value if present must be nonempty
+ base has its own, perhaps different, set of props;
+ Unknown props starting with [a-z] are ok;
+ otherwise fatal. Currently defined props:
+ patch <name> topbloke patch name (in tip and base)
+ topgit <name> this is a topgit import (in tip and base)
+ Deleted patch is deleted (in tip only, no value)
Files which not inherit contents and changes from dependencies:
included actual included deps, one per line
format as for deps
- pflags flags that apply to this patch and all its
- dependencies (ie, flags that propagate)
- Unknown flags starting with [a-z] are ok;
- otherwise fatal. No currently defined flags
+ pprops properties that apply to this patch and all its
+ dependencies (ie, properties that propagate)
+ Unknown props starting with [a-z] are ok;
+ otherwise fatal. No currently defined props.
<full-name> has the format:
setup_config check_no_unwanted_metadata
patch_matches_spec
foreach_patch
- flagsfile_add_flag
+ propsfile_add_prop depsfile_add_dep
wf_start wf wf_abort wf_done wf_contents
closeout);
%EXPORT_TAGS = ( );
#----- configuring a tree -----
sub setup_config () {
- my (@files) = (qw(msg deps included flags pflags));
+ my (@files) = (qw(msg deps included props pprops));
my $version = 1;
foreach my $iteration (qw(0 1)) {
foreach my $file (@files) {
return {
Kind => 'foreign',
Ref => $ref,
- DepSpec => "/$ref",
+ DepSpec => ".f $ref",
};
} else {
return {
sub foreach_patch ($$$$) {
my ($spec, $deleted_ok, $want, $body) = @_;
- # runs $body->($patch, $parsedname, \%flags, \%deps, \%pflags, \%included)
+ # runs $body->($patch, $parsedname, \%props, \%deps, \%pprops, \%included)
# $want->[0] 1 2 3
# where $deps->{$fullname} etc. are 1 for true or nonexistent for false
# and if $want->[$item] is not true, the corresponding item may be undef
my @out;
my $patch = substr($',19); #');
my $wantix = 0;
- foreach my $file (qw(flags deps pflags included)) {
+ foreach my $file (qw(props deps pprops included)) {
if ($file eq 'deps') {
# do this check after checking for deleted patches,
my ($got, $data) = git_get_object("$objname:.topbloke/$file");
die "$patch $file ?" unless defined $data;
my %data;
- $data{$_}=1 foreach split /\n/, $data;
+ if ($file !~ m/props/) {
+ $data{$_}=1 foreach split /\n/, $data;
+ } elseif {
+ foreach (split /\n/, $data) {
+ m/ / or m/$/;
+ $data{$`} = $'; #';
+ }
+ }
- if ($file eq 'flags') {
+ if ($file eq 'props') {
debug("foreach_patch Deleted"), return
if !$deleted_ok && $data{Deleted};
}
#----- updating topbloke metadata -----
-sub flagsfile_add_flag ($$) {
- # works on "deps" too
- my ($flagsfile, $flag) = @_;
- my $wf = wf_start(".topbloke/$flagsfile");
- open FI, '<', ".topbloke/$flagsfile" or die $!;
+sub propsfile_set_prop ($$$) {
+ # set $value to undef to delete; returns old value
+ my ($propsfile, $prop, $value) = @_;
+ my $wf = wf_start(".topbloke/$propsfile");
+ my $oldvalue;
+ open FI, '<', ".topbloke/$propsfile" or die $!;
+ while (<FI>) {
+ chomp or die;
+ m/ / or m/$/;
+ if ($` eq $prop) {
+ die "prop $prop repeated in $propsfile ?!" if defined $oldvalue;
+ $oldvalue = $'; #';
+ } else {
+ wf($wf, "$_\n");
+ }
+ }
+ FI->error and die $!;
+ close FI or die $!;
+ wf($wf, "$prop $value\n") if defined $value;
+ wf_done($wf);
+ return $oldvalue;
+}
+
+sub depssfile_add_dep ($$) {
+ my ($depsfile, $depspec) = @_;
+ my $wf = wf_start(".topbloke/$depsfile");
+ open FI, '<', ".topbloke/$depsfile" or die $!;
while (<FI>) {
chomp or die;
- die "flag $flag already set in $flagsfile ?!" if $_ eq $flag;
+ die "dep $depspec already set in $depsfile ?!" if $_ eq $depspec;
wf($wf, "$_\n");
}
FI->error and die $!;
close FI or die $!;
- wf($wf, "$flag\n");
+ wf($wf, "$depspec\n");
wf_done($wf);
}
create_and_switch($baseref, 'base');
meta_and_stage('msg', "# not applicable\n");
-meta_and_stage('deps', "# not applicable\n");
-meta_and_stage('flags', '');
+meta_and_stage('deps', "");
+meta_and_stage('props', "patch $current->{Fullname}\n");
if ($current->{Kind} eq 'foreign') {
meta_and_stage('included', $current->{DepSpec}."\n");
- meta_and_stage('pflags', '');
+ meta_and_stage('pprops', '');
}
run_git(qw(commit -q -m), "tb-create $newpatch base");
stage_meta('msg');
meta_and_stage('deps', "$current->{DepSpec}\n");
-# we inherit empty flags from the base branch
+# we inherit correct props and pprops from the base branch
-flagsfile_add_flag('included',$newpatch);
+depsfile_add_dep('included','tb',$newpatch);
stage_meta('included');
run_git(qw(commit -q -m), "tb-create $newpatch tip");
fail () { echo >&2 "$0: $*"; exit 127; }
case "$1" in
---v1) how=tip ;;
---v1-base) how=base ;;
---v1-dep) how=dep ;;
+--v1) how=tip ;; # merge into tip from another tip of this patch
+--v1-base) how=base ;; # merge into tip from a base of this patch
+--v1-dep) how=dep ;; # merge into base from a dep's tip
*) fail "bad usage" ;;
esac
whichfile=$1
ancestor=$2
-current=$3
-other=$4
+ours=$3
+theirs=$4
markersize=$5
+set -- "$ours" "$ancestor" "$theirs"
+
case $on.$whichfile in
-dep.msg|dep.deps|dep.flags)
- touch "$current"
- exit 0
+dep.msg|dep.deps|dep.props)
+ touch "$ours"
;;
-base.msg|base.deps)
- echo '# not applicable' >"$current"
- exit 0
+base.msg)
+ echo '# not applicable' >"$ours"
+base.deps)
+ echo >"$ours"
+ touch >"$ours"
;;
-base.flags)
- exec topbloke-merge-lists -UDeleted "$current" "$ancestor" "$other"
+base.props)
+ exec topbloke-merge-lists -M$markersize -P -UDeleted "$@"
;;
tip.msg)
- exec git-merge-file --marker-size=$markersize \
- "$current" "$ancestor" "$other"
+ exec git-merge-file --marker-size=$markersize "$@"
+ ;;
+tip.deps|*.included)
+ exec topbloke-merge-lists -M$markersize "$@"
;;
-tip.deps|tip.flags|*.included|*.pflags)
- exec topbloke-merge-lists "$current" "$ancestor" "$other"
+tip.props|*.pprops)
+ exec topbloke-merge-lists -M$markersize -P "$@"
;;
*)
fail "huh $on $whichfile ?"
our %flag;
our (@order, @order_final_maybe);
our $verbose=0;
+our $propsmode=0;
+our $marker=5;
# we don't use Getopt::Long because we want to be quick to start up
# and we're not really a very user-facing utility
while (@ARGV && $ARGV[0] =~ m/^\-/) {
$_ = shift @ARGV;
if (s/^-U//) {
- $flag{$_}{Result} = 0;
+ $flag{$_}{ForceResult} = 1;
+ $flag{$_}{Result} = undef;
push @order_final_maybe, $_;
} elsif (s/^-D//) {
- $flag{$_}{Result} = 1;
+ $flag{$_}{ForceResult} = 1;
+ $flag{$_}{Result} = '';
push @order_final_maybe, $_;
} elsif (m/^-v$/) {
$verbose = 1;
+ } elsif (m/^-P$/) {
+ $propsmode = 1;
+ } elsif (m/^-M(\d+)$/) {
+ $marker = $1;
} elsif (m/^--$/) {
last;
} else {
open F, '<', $ARGV[$ix] or die "$ix $!";
while (<F>) {
chomp or die;
- $flag{$_}{Input}[$ix] = 1;
- my $ff = $flag{$_};
- push @order, $_ unless $ff->{InOrder}++;
+ ($propsmode && m/ /) or m/$/;
+ $flag{$`}{Input}[$ix] = $'; #';
+ push @order, $` unless $flag{$`}{InOrder}++;
}
F->error and die $!;
close F or die $!;
open O, '>', "$current.tmp" or die "$current.tmp $!";
-foreach $_ (@order) {
- my $ff = $flag{$_};
- if (!defined $ff->{Result}) {
- my $s = '';
- foreach my $ix (qw(0 1 2)) {
- $ff->{Input}[$ix] ||= 0;
- $s .= $ff->{Input}[$ix];
- }
- if ($verbose) {
- print STDERR "MERGE-LISTS $s $_\n" or die $!;
+sub prmark ($) {
+ print O $_[0] x $marker, "\n" or die $!;
+ }
+sub prval ($) {
+ my $v = @_;
+ print O $v,"\n" or die $! if defined $v;
+}
+
+our $f;
+
+if ($verbose) {
+ sub verb ($) { print STDERR "MERGE_LISTS $f @_\n" or die $!; }
+} else {
+ sub verb { }
+}
+
+our $conflicts = 0;
+
+foreach $f (@order) {
+ my $ff = $flag{$f};
+ verb("BEGIN");
+ if (defined $ff->{ForceResult}) {
+ verb("FORCE");
+ } else {
+ my @in = @{ $ff->{Input} };
+ verb(defined ? "DEF $_": "U") foreach @in;
+ if (iseq($in[0], $in[2])) {
+ verb("SAME");
+ $ff->{Result} = [0];
+ } elsif (iseq($in[0], $in[1])) {
+ verb("THEIRS");
+ $ff->{Result} = $in[2];
+ } elsif (iseq($in[2], $in[1])) {
+ verb("OURS");
+ $ff->{Result} = $in[0];
+ } else {
+ $conflicts++;
+ verb("CONFLICT");
+ prmark('<');
+ prval($in[0]);
+ prmark('=');
+ prval($in[2]);
+ prmark('>');
+ next;
}
- $ff->{Result} =
- $s eq '000' ? '000' :
- $s eq '111' ? '111' :
- !$ff->{Input}[1];
- }
- if ($ff->{Result}) {
- print O "$_\n" or die $!;
}
+ prval($ff->{Result});
}
close O or die $!;
rename "$current.tmp", "$current" or die "$current $!";
-exit 0;
+exit $conficts ? 1 :0;