+
+sub find_parents {
+ #
+ # Identify what branches are merging into me
+ # and whether we are fully merged
+ # git-merge-base <headsha> <headsha> should tell
+ # me what the base of the merge should be
+ #
+ my $ps = shift;
+
+ my %branches; # holds an arrayref per branch
+ # the arrayref contains a list of
+ # merged patches between the base
+ # of the merge and the current head
+
+ my @parents; # parents found for this commit
+
+ # simple loop to split the merges
+ # per branch
+ foreach my $merge (@{$ps->{merges}}) {
+ my $branch = git_branchname($merge);
+ unless (defined $branches{$branch} ){
+ $branches{$branch} = [];
+ }
+ push @{$branches{$branch}}, $merge;
+ }
+
+ #
+ # foreach branch find a merge base and walk it to the
+ # head where we are, collecting the merged patchsets that
+ # Arch has recorded. Keep that in @have
+ # Compare that with the commits on the other branch
+ # between merge-base and the tip of the branch (@need)
+ # and see if we have a series of consecutive patches
+ # starting from the merge base. The tip of the series
+ # of consecutive patches merged is our new parent for
+ # that branch.
+ #
+ foreach my $branch (keys %branches) {
+
+ # check that we actually know about the branch
+ next unless -e "$git_dir/refs/heads/$branch";
+
+ my $mergebase = `git-merge-base $branch $ps->{branch}`;
+ if ($?) {
+ # Don't die here, Arch supports one-way cherry-picking
+ # between branches with no common base (or any relationship
+ # at all beforehand)
+ warn "Cannot find merge base for $branch and $ps->{branch}";
+ next;
+ }
+ chomp $mergebase;
+
+ # now walk up to the mergepoint collecting what patches we have
+ my $branchtip = git_rev_parse($ps->{branch});
+ my @ancestors = `git-rev-list --topo-order $branchtip ^$mergebase`;
+ my %have; # collected merges this branch has
+ foreach my $merge (@{$ps->{merges}}) {
+ $have{$merge} = 1;
+ }
+ my %ancestorshave;
+ foreach my $par (@ancestors) {
+ $par = commitid2pset($par);
+ if (defined $par->{merges}) {
+ foreach my $merge (@{$par->{merges}}) {
+ $ancestorshave{$merge}=1;
+ }
+ }
+ }
+ # print "++++ Merges in $ps->{id} are....\n";
+ # my @have = sort keys %have; print Dumper(\@have);
+
+ # merge what we have with what ancestors have
+ %have = (%have, %ancestorshave);
+
+ # see what the remote branch has - these are the merges we
+ # will want to have in a consecutive series from the mergebase
+ my $otherbranchtip = git_rev_parse($branch);
+ my @needraw = `git-rev-list --topo-order $otherbranchtip ^$mergebase`;
+ my @need;
+ foreach my $needps (@needraw) { # get the psets
+ $needps = commitid2pset($needps);
+ # git-rev-list will also
+ # list commits merged in via earlier
+ # merges. we are only interested in commits
+ # from the branch we're looking at
+ if ($branch eq $needps->{branch}) {
+ push @need, $needps->{id};
+ }
+ }
+
+ # print "++++ Merges from $branch we want are....\n";
+ # print Dumper(\@need);
+
+ my $newparent;
+ while (my $needed_commit = pop @need) {
+ if ($have{$needed_commit}) {
+ $newparent = $needed_commit;
+ } else {
+ last; # break out of the while
+ }
+ }
+ if ($newparent) {
+ push @parents, $newparent;
+ }
+
+
+ } # end foreach branch
+
+ # prune redundant parents
+ my %parents;
+ foreach my $p (@parents) {
+ $parents{$p} = 1;
+ }
+ foreach my $p (@parents) {
+ next unless exists $psets{$p}{merges};
+ next unless ref $psets{$p}{merges};
+ my @merges = @{$psets{$p}{merges}};
+ foreach my $merge (@merges) {
+ if ($parents{$merge}) {
+ delete $parents{$merge};
+ }
+ }
+ }
+
+ @parents = ();
+ foreach (keys %parents) {
+ push @parents, '-p', ptag($_);
+ }
+ return @parents;
+}
+
+sub git_rev_parse {
+ my $name = shift;
+ my $val = `git-rev-parse $name`;
+ die "Error: git-rev-parse $name" if $?;
+ chomp $val;
+ return $val;
+}
+
+# resolve a SHA1 to a known patchset
+sub commitid2pset {
+ my $commitid = shift;
+ chomp $commitid;
+ my $name = $rptags{$commitid}
+ || die "Cannot find reverse tag mapping for $commitid";
+ $name =~ s|,|/|;
+ my $ps = $psets{$name}
+ || (print Dumper(sort keys %psets)) && die "Cannot find patchset for $name";
+ return $ps;
+}
+
+
+# an alterative to `command` that allows input to be passed as an array
+# to work around shell problems with weird characters in arguments
+sub safe_pipe_capture {
+ my @output;
+ if (my $pid = open my $child, '-|') {
+ @output = (<$child>);
+ close $child or die join(' ',@_).": $! $?";
+ } else {
+ exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
+ }
+ return wantarray ? @output : join('',@output);
+}
+
+# `tla logs -rf -d <dir> | head -n1` or `baz tree-id <dir>`
+sub arch_tree_id {
+ my $dir = shift;
+ chomp( my $ret = (safe_pipe_capture($TLA,'logs','-rf','-d',$dir))[0] );
+ return $ret;
+}
+
+sub archive_reachable {
+ my $archive = shift;
+ return 1 if $reachable{$archive};
+ return 0 if $unreachable{$archive};
+
+ if (system "$TLA whereis-archive $archive >/dev/null") {
+ if ($opt_a && (system($TLA,'register-archive',
+ "http://mirrors.sourcecontrol.net/$archive") == 0)) {
+ $reachable{$archive} = 1;
+ return 1;
+ }
+ print STDERR "Archive is unreachable: $archive\n";
+ $unreachable{$archive} = 1;
+ return 0;
+ } else {
+ $reachable{$archive} = 1;
+ return 1;
+ }
+}
+