mirror of
https://github.com/kristoferssolo/School.git
synced 2025-10-21 20:10:38 +00:00
170 lines
5.0 KiB
Python
170 lines
5.0 KiB
Python
import os
|
|
from pathlib import Path
|
|
|
|
from .config import Configuration
|
|
from .scm_workdir import Workdir
|
|
from .utils import data_from_mime
|
|
from .utils import do_ex
|
|
from .utils import require_command
|
|
from .utils import trace
|
|
from .version import meta
|
|
from .version import tag_to_version
|
|
|
|
|
|
class HgWorkdir(Workdir):
|
|
|
|
COMMAND = "hg"
|
|
|
|
@classmethod
|
|
def from_potential_worktree(cls, wd):
|
|
require_command(cls.COMMAND)
|
|
root, err, ret = do_ex("hg root", wd)
|
|
if ret:
|
|
return
|
|
return cls(root)
|
|
|
|
def get_meta(self, config):
|
|
|
|
node, tags, bookmark, node_date = self.hg_log(
|
|
".", "{node}\n{tag}\n{bookmark}\n{date|shortdate}"
|
|
).split("\n")
|
|
|
|
# TODO: support bookmarks and topics (but nowadays bookmarks are
|
|
# mainly used to emulate Git branches, which is already supported with
|
|
# the dedicated class GitWorkdirHgClient)
|
|
|
|
branch, dirty, dirty_date = self.do(
|
|
["hg", "id", "-T", "{branch}\n{if(dirty, 1, 0)}\n{date|shortdate}"]
|
|
).split("\n")
|
|
dirty = bool(int(dirty))
|
|
|
|
if dirty:
|
|
date = dirty_date
|
|
else:
|
|
date = node_date
|
|
|
|
if all(c == "0" for c in node):
|
|
trace("initial node", self.path)
|
|
return meta("0.0", config=config, dirty=dirty, branch=branch)
|
|
|
|
node = "h" + node[:7]
|
|
|
|
tags = tags.split()
|
|
if "tip" in tags:
|
|
# tip is not a real tag
|
|
tags = tags.remove("tip")
|
|
|
|
if tags:
|
|
tag = tags[0]
|
|
tag = tag_to_version(tag)
|
|
if tag:
|
|
return meta(tag, dirty=dirty, branch=branch, config=config)
|
|
|
|
try:
|
|
tag = self.get_latest_normalizable_tag()
|
|
dist = self.get_distance_revs(tag)
|
|
if tag == "null":
|
|
tag = "0.0"
|
|
dist = int(dist) + 1
|
|
|
|
if self.check_changes_since_tag(tag) or dirty:
|
|
return meta(
|
|
tag,
|
|
distance=dist,
|
|
node=node,
|
|
dirty=dirty,
|
|
branch=branch,
|
|
config=config,
|
|
node_date=date,
|
|
)
|
|
else:
|
|
return meta(tag, config=config)
|
|
|
|
except ValueError:
|
|
pass # unpacking failed, old hg
|
|
|
|
def hg_log(self, revset, template):
|
|
cmd = ["hg", "log", "-r", revset, "-T", template]
|
|
return self.do(cmd)
|
|
|
|
def get_latest_normalizable_tag(self):
|
|
# Gets all tags containing a '.' (see #229) from oldest to newest
|
|
outlines = self.hg_log(
|
|
revset="ancestors(.) and tag('re:\\.')",
|
|
template="{tags}{if(tags, '\n', '')}",
|
|
).split()
|
|
if not outlines:
|
|
return "null"
|
|
tag = outlines[-1].split()[-1]
|
|
return tag
|
|
|
|
def get_distance_revs(self, rev1, rev2="."):
|
|
revset = f"({rev1}::{rev2})"
|
|
out = self.hg_log(revset, ".")
|
|
return len(out) - 1
|
|
|
|
def check_changes_since_tag(self, tag):
|
|
|
|
if tag == "0.0":
|
|
return True
|
|
|
|
revset = (
|
|
"(branch(.)" # look for revisions in this branch only
|
|
f" and tag({tag!r})::." # after the last tag
|
|
# ignore commits that only modify .hgtags and nothing else:
|
|
" and (merge() or file('re:^(?!\\.hgtags).*$'))"
|
|
f" and not tag({tag!r}))" # ignore the tagged commit itself
|
|
)
|
|
|
|
return bool(self.hg_log(revset, "."))
|
|
|
|
|
|
def parse(root, config=None):
|
|
if not config:
|
|
config = Configuration(root=root)
|
|
|
|
if os.path.exists(os.path.join(root, ".hg/git")):
|
|
paths, _, ret = do_ex("hg path", root)
|
|
if not ret:
|
|
for line in paths.split("\n"):
|
|
if line.startswith("default ="):
|
|
path = Path(line.split()[2])
|
|
if path.name.endswith(".git") or (path / ".git").exists():
|
|
from .git import _git_parse_inner
|
|
from .hg_git import GitWorkdirHgClient
|
|
|
|
wd = GitWorkdirHgClient.from_potential_worktree(root)
|
|
if wd:
|
|
return _git_parse_inner(config, wd)
|
|
|
|
wd = HgWorkdir.from_potential_worktree(config.absolute_root)
|
|
|
|
if wd is None:
|
|
return
|
|
|
|
return wd.get_meta(config)
|
|
|
|
|
|
def archival_to_version(data, config: "Configuration | None" = None):
|
|
trace("data", data)
|
|
node = data.get("node", "")[:12]
|
|
if node:
|
|
node = "h" + node
|
|
if "tag" in data:
|
|
return meta(data["tag"], config=config)
|
|
elif "latesttag" in data:
|
|
return meta(
|
|
data["latesttag"],
|
|
distance=data["latesttagdistance"],
|
|
node=node,
|
|
config=config,
|
|
)
|
|
else:
|
|
return meta("0.0", node=node, config=config)
|
|
|
|
|
|
def parse_archival(root, config=None):
|
|
archival = os.path.join(root, ".hg_archival.txt")
|
|
data = data_from_mime(archival)
|
|
return archival_to_version(data, config=config)
|