Basic update algorithm:
1. recurse to all of our direct dependencies and
- update their bases and branches
+ update their bases and tips
2. Update our base.
- i. Compute our base's set of desired included branches:
- The set of desired included branches for a base
- is the union of the desired included branches for each
+ i. Compute our base's set of desired included deps:
+ The set of desired included deps for a base
+ is the union of the desired included deps for each
branch named in the base's branch's direct deps, plus
the name of every direct external dep.
- The set of desired included branches for a branch
- is the set of desired included branches for the branch's
+ The set of desired included deps for a branch
+ is the set of desired included deps for the branch's
base plus the branch name itself.
ii. For each source in the best order, do the following merge:
Check for unwanted dependency removals.
An unwanted dependency removal is
- A branch in the desired included branches
- Which exists in the common ancestor's actual included branches
- but which is missing in the source's actual included branches
+ A branch in the desired included deps
+ Which exists in the common ancestor's actual included deps
+ but which is missing in the source's actual included deps
(NB that this definition excludes dependency removals
which have already occurred on our base; these will be
reverted later.)
For each unwanted dependency removal (ie for each such
branch), find the most recent commit which unwantedly removed
- the dep from the source's actual included branches ("relevant
+ the dep from the source's actual included deps ("relevant
unwanted removal commit"). (Abort if any such commit is a
merge.) Select the earliest relevant unwanted removal commit
(from the set of relevant unwanted removal commits
tries to keep the unwanted dependency removal's reversions as
close as possible to their originating points. The
recursion, which processes dependencies before their clients,
- tries to keep the reversion churn localised: client branches
- of a branch affected by an unwanted removal will benefit from
+ tries to keep the reversion churn localised: client patches
+ of a patch affected by an unwanted removal will benefit from
that client's resolution of the situation.)
Now continue to the next unwanted dependency removal.
iii.
Check for missing or unwanted dependency inclusions. Compare
- our base's desired included branches with our base's actual
- included branches. In exceptional conditions, they will not
+ our base's desired included deps with our base's actual
+ included deps. In exceptional conditions, they will not
be identical. This can happen, for example, because a
dependency removal was incorporated into our base branch but
the removed branch was introduced as an explicit dependency.
"Recency" refers to the order from git-rev-list --date-order.
-Actual included branches:
+Actual included deps:
This is tracked explicitly in .topbloke/included, one branch per
line. For compatibility with older versions, every time we think
base, we advance the local branch or base accordingly.
When .topbloke/included is calculated in this way, it always gets
- the list of desired included branches. (topgit,
+ the list of desired included deps. (topgit,
which does not support dependency deletion, always has exactly the
- desired branches actually included.)
+ desired deps actually included.)
- Non-topbloke-controlled branches are never recorded in included.
+ Foreign branches cannot be removed from included and cannot
+ therefore be removed from dependency lists.
-Branch removal:
+Patch removal:
- - removed branch must be removed from the deps of its
+ - removed patch must be removed from the deps of its
ex-children, and replaced with the deps of the removed
- branch
+ patch
- - removed branch wants not to be in list of branches "git-branch"
+ - removed patch wants not to be in list of patches "tb-list"
et al any more
- branches in refs/top-branches ? yes
- deleted branches do something to the base ? no
+ branches in refs/topbloke-tips ? yes
+ deleted patches do something to the base ? no
- deleting empty branch: dependencies fine
- deleting nonempty branch: if any dependencies found, their
+ deleting empty patch: dependencies fine
+ deleting nonempty patch: if any dependencies found, their
updates break
purpose of deleting?
- - remove from views of active branches
+ - remove from views of active patches
- prevent new commits
- - remove from dependencies of active branches
- only makes sense if no active branches depend on it.
+ - remove from dependencies of active patches
+ only makes sense if no active patches depend on it.
undeleting
- - just unmark the branch as deleted
+ - just unmark the patch as deleted
Foreign branches:
When merging from a foreign dependency, check that it
- does not have .topbloke/included or .topbloke/flags; if it
+ does not have .topbloke metadata; LATER if it
does, could produce a new commit which has .topbloke removed
and merge from that
-Branch naming:
+Patch naming:
needs to be globally unique
so put email address in it
-tg operations search for applicable branches
-safe charset for branch names
+tg operations search for applicable patches
+safe charset for patch names
. - / 0-9 a-z
not permitted (git-check-ref-format(1))
spc ~ ^ : ? * [ \
(apropos of shell)
-When pulling, which remotes get to update which branches ?
+When pulling, which remotes get to update which patches ?
Complicated question!
For now, have "blessed" remotes, which we always pull and update from.
Concept of a "stack" ?
-Unnecessary - instead, deal with leaf branches
+Unnecessary - instead, deal with leaf patches
Operations like "go up the stack", goes towards leaf. Hopefully unique.
"Down" the stack, uses a "conventional" linearisation
Stack reordering op ? auto adjust deps
-Topbloke branch is:
+Topbloke patch is:
- branch in refs/topbloke-tips/<full-name>
contains the working version, fast-forwarding
- branch in refs/topbloke-bases/<full-name>
In-tree, there are metadata files in .topbloke
- Files which are per-branch and do not inherit any contents
+ Files which are per-patch and do not inherit any contents
or changes from bases or dependencies:
msg brach "commit message"
deps direct dependencies, one per line
as either:
- topbloke branch name
+ topbloke patch name
/refs/heads/<something>
("# not applicable" in bases)
- flags flags that apply to this branch, one per line
+ 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 branch is deleted
- (valid on branch only)
+ Deleted patch is deleted
+ (valid on tip only)
Files which not inherit contents and changes from dependencies:
- included actual included branches, one per line
- topbloke branch name
+ included actual included deps, one per line
+ format as for deps
- pflags flags that apply to this branch and all its
+ 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
ijackson@chiark.greenend.org.uk/2012-01-20T225127Z/reorg/sponge
NB only exactly that date format is allowed and timezone must be Z.
-Branches may be specified as
+Patches may be specified as
[<qualifier>/...]<nickname-path-spec>
where <qualifier>/ is one of
[<email>]@[<domain.name>/
- Only branches matching the specified email local part
+ Only patches matching the specified email local part
or domain name match
[<date-spec>/]
A prefix of the ISO8601 date spec, stopping
just after a numeric component (or at the end)
[<approx-date-containing-~>/]
Intepreted by date -d after ~s have been replaced by
- spaces. When we come to select, take the branch
+ spaces. When we come to select, take the patch
nearest that date rather than the most recent
<date-spec> may be
<approximate date spec containing at least one ~>
- means all branches are candidates
+ means all patches are candidates
-So overall, if the current branch is
+So overall, if the current patch is
ijackson@chiark.greenend.org.uk/2011-08-20T120320Z/fixes/pudding
-then all of the following can refer to the same branch
+then all of the following can refer to the same patch
./sponge
../reorg/sponge
reorg/sponge
ijackson@chiark.greenend.org.uk/2012-01-20T225127Z/reorg/sponge
Search algorithm:
- 1. prefer branches with email address (or domain)
- matching current branch
- 2. prefer branches with our own email address (or domain)
- 3. other branches
-Within this there may be branches with multiple dates; prefer the most
+ 1. prefer patches with email address (or domain)
+ matching current patch
+ 2. prefer patches with our own email address (or domain)
+ 3. other patches
+Within this there may be patches with multiple dates; prefer the most
recent.
- need new branch name
needed to allow branch "removal" (ie, hiding)
+- want new terminology
+
- therefore project needs new name
- existing code has bugs
--- /dev/null
+patch
+ a topbloke-controlled branch-pair
+base
+ the topbloke-controlled base branch for a patch
+tip
+ the topbloke-controlled head branch for a patch
+dep or dependency
+ a dependency of a patch, which may be a patch or foreign branch
+branch
+ any git branch including one of the two branches for a patch
+foreign branch
+ any non-topbloke-controlled branch
--- /dev/null
+enable reflog ?!
run_git run_git_1line run_git_check_nooutput
run_git_test_anyoutput
git_config git_dir chdir_toplevel
- current_branch parse_branch_spec
+ current_branch parse_patch_spec
setup_config check_no_unwanted_metadata
flagsfile_add_flag
wf_start wf wf_abort wf_done wf_contents);
}
}
-sub parse_branch_spec ($) {
+sub parse_patch_spec ($) {
my ($orig) = @_;
local $_ = $orig;
my $spec = { }; # Email Domain DatePrefix DateNear Nick
my $set = sub {
my ($key,$val,$whats) = @_;
- die "multiple $whats in branch spec\n" if exists $spec->{$key};
+ die "multiple $whats in patch spec\n" if exists $spec->{$key};
$spec->{$key} = $val;
};
my $rel_levels;
if (defined $rel_levels) {
my $branch = current_branch();
if (!defined $branch->{Nick}) {
- die "relative branch spec \`$orig',".
- " but current branch not a topbloke branch\n";
+ die "relative patch spec \`$orig',".
+ " but current branch not a topbloke patch\n";
}
my ($ceaddr,$cdate,@l) = split /\//, $branch->{Nick};
@l >= $rel_levels or
- die "relative branch spec \`$orig' has too many ../s\n";
+ die "relative patch spec \`$orig' has too many ../s\n";
$_ = (join '/', @l[0..$#l-$rel_levels]).'/'.$_;
}
$spec->{Nick} = $_;
#!/usr/bin/perl
-# usage: tb-create <branch-spec>
+# usage: tb-create <patch-spec>
use warnings;
use strict;
die "bad usage\n" unless @ARGV==1;
-our $spec = parse_branch_spec($ARGV[0]);
+our $spec = parse_patch_spec($ARGV[0]);
our $current = current_branch();
-die "cannot make branch starting at base of another;".
- " check out a real branch\n" if $current->{Kind} eq 'base';
+die "cannot make patch starting at base of another;".
+ " check out a real branch or patch\n" if $current->{Kind} eq 'base';
die "strange branch ref $current->{Kind} $current->{Ref},\n".
- " making new branch with this as dep is unwise\n"
+ " making new patch with this as dep is unwise\n"
unless ($current->{Kind} eq 'foreign' ||
$current->{Kind} eq 'tip');
check_no_unwanted_metadata('HEAD')
if $current->{Kind} ne 'tip';
-run_git_check_nooutput("cannot create new topbloke branch with staged file(s)",
+run_git_check_nooutput("cannot create new patch with staged file(s)",
qw(diff --cached --name-only HEAD --));
-run_git_check_nooutput("cannot create new topbloke branch with".
+run_git_check_nooutput("cannot create new patch with".
" modified metadata file(s)",
qw(diff --name-only HEAD -- .topbloke));
# For .topbloke/msg, if it's modified by the user (ie, if working
# version differs from HEAD) we keep that and stage it.
-my $newbranch = "$spec->{Email}\@$spec->{Domain}/$spec->{Date}/$spec->{Nick}";
+my $newpatch = "$spec->{Email}\@$spec->{Domain}/$spec->{Date}/$spec->{Nick}";
-$newbranch = run_git_1line(qw(check-ref-format --print), $newbranch);
+$newpatch = run_git_1line(qw(check-ref-format --print), $newpatch);
my $author = run_git_1line(qw(var GIT_AUTHOR_IDENT));
$author =~ s/ \d+ [-+]\d+$// or die $!;
my $subjprefix = git_config('topbloke.subjectprefix', '');
-printf "creating %s\n", $newbranch;
+printf "creating %s\n", $newpatch;
setup_config();
mkdir('.topbloke') or die "create .topbloke: $!\n";
}
-my $baseref = "refs/topbloke-bases/$newbranch";
+my $baseref = "refs/topbloke-bases/$newpatch";
create_and_switch($baseref);
-run_git(qw(update-ref -m), "tb-create base $newbranch", $baseref, 'HEAD');
+run_git(qw(update-ref -m), "tb-create base $newpatch", $baseref, 'HEAD');
run_git(qw(symbolic-ref HEAD), $baseref);
meta_and_stage('msg', "# not applicable\n");
meta_and_stage('pflags', '');
}
-run_git(qw(commit -q -m), "create base branch $newbranch");
+run_git(qw(commit -q -m), "tb-create base $newpatch");
#----- create the tip branch
-my $tipref = "refs/topbloke-tips/$newbranch";
+my $tipref = "refs/topbloke-tips/$newpatch";
create_and_switch($tipref);
my $nm = wf_start('.topbloke/msg');
meta_and_stage('deps', "$current->{DepSpec}\n");
# we inherit empty flags from the base branch
-flagsfile_add_flag('included',$newbranch);
+flagsfile_add_flag('included',$newpatch);
stage_meta('included');
-run_git(qw(commit -q -m), "create branch $spec->{Nick}\n$newbranch\n");
+run_git(qw(commit -q -m), "tb-create tip $spec->{Nick}\n$newpatch\n");