Age | Commit message (Collapse) | Author |
|
|
|
|
|
The current range algorithm does not work correctly when merge
commits are involved with an ancestor of the base commit.
For example, consider the graph
b
↙ ↖
a d ← e ← f
↖ ↙
c
where b is d's first parent, and c is d's second parent.
This graph is symmetrical, so we should get the same results for
b..f and c..d (modulo the symmetry), but currently range() returns
[d] for b..f and [d, e, f] for c..f. In the first case, we traverse
to b, our base, then unwind to d and add [d] to our results. Then,
we traverse [c, a], but don't find b this time, so unwind back to
f. In the second case, we traverse all the way to a through b,
unwind to d, then find our base, c, and unwind back to f and add
[d, e, f] to our results.
The problem here is that the determining factor for whether a commit
is pushed during unwinding is whether the *last* parent reached the
base commit, when it should be if *any* parent reached the base
commit.
Also, when a is not an ancestor of b, the current algorithm traverses
the entire graph then returns an empty list, despite git's behavior
and git9's documentation ("Between is defined as the set of all
commits which are reachable from b but not reachable from a"). For
the above example, we'd expect that b..f contain c, since c is not
reachable from b.
Range is almost identical to findtwixt, so to fix this we can reuse
the same paint() algorithm for both operations. The only difference
is that we want results in traversal order (rather than an arbitrary
ordering). Add a third mode to paint() to save 'keep' commits in
an array in the order we reach them, then return the non-dropped
and non-skipped commits, preserving that order.
|
|
We now keep track of 3 sets during traversal:
- keep: commits we've reached from head commits
- drop: commits we've reached from tail commits
- skip: ancestors of commits in both 'keep' and 'drop'
Commits in 'keep' and/or 'drop' may be added later to the 'skip' set
if we discover later that they are part of a common subgraph of the
head and tail commits.
From these sets we can calculate the commits we are interested in:
lca commits are those in 'keep' and 'drop', but not in 'skip'.
findtwixt commits are those in 'keep', but not in 'drop' or 'skip'.
The "LCA" commit returned is a common ancestor such that there are no
other common ancestors that can reach that commit. Although there can
be multiple commits that meet this criteria, where one is technically
lower on the commit-graph than the other, these cases only happen in
complex merge arrangements and any choice is likely a decent merge
base.
Repainting is now done in paint() directly. When we find a boundary
commit, we switch our paint color to 'skip'. 'skip' painting does
not stop when it hits another color; we continue until we are left
with only 'skip' commits on the queue.
This fixes several mishandled cases in the current algorithm:
1. If we hit the common subgraph from tail commits first (if the tail
commit was newer than the head commit), we ended up traversing the
entire commit graph. This is because we couldn't distinguish
between 'drop' commits that were part of the common subgraph, and
those that were still looking for it.
2. If we traversed through an initial part of the common subgraph from
head commits before reaching it from tail commits, these commits
were returned from findtwixt even though they were also reachable
from tail commits.
3. In the same case as 2, we might end up choosing an incorrect
commit as the LCA, which is an ancestor of the real LCA.
|
|
This prevents commits from getting reordered incorrectly during rebase
or export.
|
|
we were leaving objq.best uninitialized, and
would therefore read garbage if we didn't
find a best match.
|
|
Due to the way LCA is defined, a using a strict LCA
on a graph like this:
<--a--b--c--d--e--f--g
\ /
+-----h-------
can lead to spurious requests to merge. This happens
because 'lca(b, g)' would return 'a', since it can be
reached in one step from 'b', and 2 steps from 'g', while
reaching 'b' from 'a' would be a longer path.
As a result, we need to implement an lca variant that
returns the starting node if one is reachable from the
other, even if it's already found the technically correct
least common ancestor.
This replaces our LCA algorithm with one based on the
painting we do while finding a twixt, making it give
the resutls we want.
git/query: fix spurious merge requests
Due to the way LCA is defined, a using a strict LCA
on a graph like this:
<--a--b--c--d--e--f--g
\ /
+-----h-------
can lead to spurious requests to merge. This happens
because 'lca(b, g)' would return 'a', since it can be
reached in one step from 'b', and 2 steps from 'g', while
reaching 'b' from 'a' would be a longer path.
As a result, we need to implement an lca variant that
returns the starting node if one is reachable from the
other, even if it's already found the technically correct
least common ancestor.
This replaces our LCA algorithm with one based on the
painting we do while finding a twixt.
|
|
We weren't giving all objects to the twixt() function, and
it was making bad life choices -- gambling, smoking, drinking,
and packing in too much data.
With more information, it doesn't do the last.
|
|
Add a snapshot of git9 to 9front.
|