hgext/git: octopus merge work-in-progress code
Josef 'Jeff' Sipek
jeffpc at josefsipek.net
Thu May 2 03:14:56 UTC 2024
This is a very rough work-in-progress that I've been sitting on for far too
long. Since I don't know when I'll have time to hack more on it, I thought
I'd share it.
The idea is to convert each octopus merge into a series of commits that pull
in one branch at a time. These intermediate commits use made up hashes that
are a simple counter printed as a 40-digit hex number.
If someone picks it up and makes it actually work - great. If nobody does,
I'll probably end up hacking on it some more in the next few months/years.
Jeff.
diff --git a/hgext/git/gitlog.py b/hgext/git/gitlog.py
--- a/hgext/git/gitlog.py
+++ b/hgext/git/gitlog.py
@@ -61,6 +61,14 @@ class baselog: # revlog.revlog):
raise error.LookupError(r, b'00changelog.i', _(b'no node'))
return bin(t[0])
+ def synthetic(self, n):
+ t = self._db.execute(
+ 'SELECT synthetic FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
+ ).fetchone()
+ if t is None or t[0] is None:
+ return n
+ return bin(t[0])
+
def hasnode(self, n):
t = self._db.execute(
'SELECT node FROM changelog WHERE node = ?',
@@ -222,6 +230,7 @@ class changelog(baselog):
return hgchangelog._changelogrevision(
extra=extra, manifest=sha1nodeconstants.nullid
)
+ n = self.synthetic(n)
hn = gitutil.togitnode(n)
# We've got a real commit!
files = [
@@ -367,20 +376,13 @@ class changelog(baselog):
return bool(self.reachableroots(a, [b], [a], includepath=False))
def parentrevs(self, rev):
- n = self.node(rev)
- hn = gitutil.togitnode(n)
- if hn != gitutil.nullgit:
- c = self.gitrepo[hn]
- else:
- return nullrev, nullrev
- p1 = p2 = nullrev
- if c.parents:
- p1 = self.rev(c.parents[0].id.raw)
- if len(c.parents) > 2:
- raise error.Abort(b'TODO octopus merge handling')
- if len(c.parents) == 2:
- p2 = self.rev(c.parents[1].id.raw)
- return p1, p2
+ assert rev >= 0, rev
+ t = self._db.execute(
+ 'SELECT p1, p2 FROM changelog WHERE rev = ?', (rev,)
+ ).fetchone()
+ if t is None:
+ raise error.LookupError(rev, b'00changelog.i', _(b'no rev %d'))
+ return self.rev(bin(t[0])), self.rev(bin(t[1]))
# Private method is used at least by the tags code.
_uncheckedparentrevs = parentrevs
@@ -459,6 +461,7 @@ class manifestlog(baselog):
if node == sha1nodeconstants.nullid:
# TODO: this should almost certainly be a memgittreemanifestctx
return manifest.memtreemanifestctx(self, relpath)
+ node = self.synthetic(node)
commit = self.gitrepo[gitutil.togitnode(node)]
t = commit.tree
if relpath:
diff --git a/hgext/git/index.py b/hgext/git/index.py
--- a/hgext/git/index.py
+++ b/hgext/git/index.py
@@ -43,7 +43,8 @@ CREATE TABLE changelog (
rev INTEGER NOT NULL PRIMARY KEY,
node TEXT NOT NULL,
p1 TEXT,
- p2 TEXT
+ p2 TEXT,
+ synthetic TEXT
);
CREATE UNIQUE INDEX changelog_node_idx ON changelog(node);
@@ -273,26 +274,43 @@ def _index_repo(
prog = progress_factory(b'commits')
# This walker is sure to visit all the revisions in history, but
# only once.
- for pos, commit in enumerate(walker):
+ pos = -1
+ for commit in walker:
if prog is not None:
prog.update(pos)
p1 = p2 = gitutil.nullgit
- if len(commit.parents) > 2:
- raise error.ProgrammingError(
- (
- b"git support can't handle octopus merges, "
- b"found a commit with %d parents :("
- )
- % len(commit.parents)
+ if len(commit.parents) <= 2:
+ if commit.parents:
+ p1 = commit.parents[0].id.hex
+ if len(commit.parents) == 2:
+ p2 = commit.parents[1].id.hex
+ pos += 1
+ db.execute(
+ 'INSERT INTO changelog (rev, node, p1, p2, synthetic) VALUES(?, ?, ?, ?, NULL)',
+ (pos, commit.id.hex, p1, p2),
)
- if commit.parents:
- p1 = commit.parents[0].id.hex
- if len(commit.parents) == 2:
- p2 = commit.parents[1].id.hex
- db.execute(
- 'INSERT INTO changelog (rev, node, p1, p2) VALUES(?, ?, ?, ?)',
- (pos, commit.id.hex, p1, p2),
- )
+ else:
+ parents = list(commit.parents)
+
+ p1 = parents.pop(0).id.hex
+ while parents:
+ pos += 1
+
+ if len(parents) == 1:
+ this = commit.id.hex
+ synth = None
+ else:
+ this = "%040x" % pos
+ synth = commit.id.hex
+
+ p2 = parents.pop(0).id.hex
+
+ db.execute(
+ 'INSERT INTO changelog (rev, node, p1, p2, synthetic) VALUES(?, ?, ?, ?, ?)',
+ (pos, this, p1, p2, synth),
+ )
+
+ p1 = this
num_changedfiles = db.execute(
"SELECT COUNT(*) from changedfiles WHERE node = ?",
More information about the Mercurial-devel
mailing list