Updated aiohttp v3.12.12 -> v3.12.13 Updated attrs v23.1.0 -> v23.2.0 Updated blessed v1.19.1 -> v1.21.0 Updated cbor2 v4.0.1 -> v4.1.2 Updated compare-locales v9.0.1 -> v9.0.4 Updated distro v1.8.0 -> v1.9.0 Updated fluent-migrate v0.13.2 -> v0.13.3 Updated importlib-metadata v6.0.0 -> v6.11.0 Updated jsmin v3.0.0 -> v3.0.1 Updated json-e v4.5.3 -> v4.8.0 Updated looseversion v1.0.1 -> v1.3.0 Updated mozilla-taskgraph v3.3.1 -> v3.3.2 Updated multidict v6.4.4 -> v6.5.0 Updated pathspec v0.9.0 -> v0.12.1 Updated pyasn1 v0.4.8 -> v0.6.1 Updated pyasn1-modules v0.2.8 -> v0.4.2 Updated pylru v1.0.9 -> v1.2.1 Updated python-hglib v2.4 -> v2.6.2 Updated redo v2.0.3 -> v2.0.4 Updated requests-unixsocket v0.2.0 -> v0.4.1 Updated responses v0.10.6 -> v0.25.7 Updated rsa v4.9 -> v4.9.1 Updated sentry-sdk v1.14.0 -> v1.45.1 Updated six v1.16.0 -> v1.17.0 Updated tomlkit v0.12.3 -> v0.13.3 Updated tqdm v4.66.3 -> v4.67.1 Updated typing-extensions v4.12.2 -> v4.14.0 Updated voluptuous v0.12.1 -> v0.15.2 Updated yamllint v1.23.0 -> v1.37.1 Differential Revision: https://phabricator.services.mozilla.com/D254096
110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
from __future__ import annotations
|
|
from typing import Tuple
|
|
|
|
import json
|
|
from subprocess import run
|
|
|
|
from os.path import isdir, join
|
|
|
|
import hglib
|
|
|
|
|
|
def git(root: str, *args: str) -> str:
|
|
"""
|
|
Wrapper for calling command-line git in the `root` directory.
|
|
Raises an exception on any error, including a non-0 return code.
|
|
Returns the command's stdout as a string.
|
|
"""
|
|
git = ["git"]
|
|
git.extend(args)
|
|
proc = run(git, capture_output=True, cwd=root, encoding="utf-8")
|
|
if proc.returncode != 0:
|
|
raise Exception(proc.stderr or f"git command failed: {args}")
|
|
return proc.stdout
|
|
|
|
|
|
class RepoClient:
|
|
def __init__(self, root: str):
|
|
self.root = root
|
|
if isdir(join(root, ".hg")):
|
|
self.hgclient = hglib.open(root, "utf-8")
|
|
else:
|
|
self.hgclient = None
|
|
try:
|
|
stdout = git(self.root, "rev-parse", "--is-inside-work-tree")
|
|
except Exception:
|
|
stdout = ""
|
|
if stdout != "true\n":
|
|
raise Exception(f"Unsupported repository: {root}")
|
|
|
|
def close(self):
|
|
if self.hgclient:
|
|
self.hgclient.close()
|
|
|
|
def blame(self, file: str) -> list[Tuple[str, int]]:
|
|
"Return a list of (author, time) tuples for each line in `file`."
|
|
if self.hgclient:
|
|
args = hglib.util.cmdbuilder(
|
|
b"annotate",
|
|
file.encode("latin-1"),
|
|
template="json",
|
|
date=True,
|
|
user=True,
|
|
cwd=self.root,
|
|
)
|
|
blame_json = self.hgclient.rawcommand(args)
|
|
return [
|
|
(line["user"], int(line["date"][0]))
|
|
for line in json.loads(blame_json)[0]["lines"]
|
|
]
|
|
else:
|
|
lines: list[Tuple[str, int]] = []
|
|
user = ""
|
|
time = 0
|
|
stdout = git(self.root, "blame", "--porcelain", file)
|
|
for line in stdout.splitlines():
|
|
if line.startswith("author "):
|
|
user = line[7:] or "[noname]"
|
|
elif line.startswith("author-mail "):
|
|
email = line[11:] # includes leading space
|
|
user += email if email != ' <>' else ' <nomail>'
|
|
elif line.startswith("author-time "):
|
|
time = int(line[12:])
|
|
elif line.startswith("\t"):
|
|
lines.append((user, time))
|
|
return lines
|
|
|
|
def commit(self, message: str, author: str):
|
|
"Add and commit all work tree files"
|
|
if self.hgclient:
|
|
self.hgclient.commit(message, user=author.encode("utf-8"), addremove=True)
|
|
else:
|
|
git(self.root, "add", ".")
|
|
git(self.root, "commit", f"--author={author}", f"--message={message}")
|
|
|
|
def head(self) -> str:
|
|
"Identifier for the most recent commit"
|
|
if self.hgclient:
|
|
return self.hgclient.tip().node.decode("utf-8")
|
|
else:
|
|
return git(self.root, "rev-parse", "HEAD").strip()
|
|
|
|
def log(self, from_commit: str, to_commit: str) -> list[str]:
|
|
if self.hgclient:
|
|
return [
|
|
rev.desc.decode("utf-8")
|
|
for rev in self.hgclient.log(f"{to_commit} % {from_commit}")
|
|
]
|
|
else:
|
|
return (
|
|
git(
|
|
self.root,
|
|
"log",
|
|
"--reverse",
|
|
"--pretty=format:%s",
|
|
f"{from_commit}..{to_commit}",
|
|
)
|
|
.strip()
|
|
.splitlines()
|
|
)
|