Added rngit client-side handling for direct ref updates

This commit is contained in:
Mark Qvist 2026-04-28 19:09:45 +02:00
parent b60eab0fcf
commit 70f5126499

View file

@ -532,6 +532,17 @@ class ReticulumGitClient():
stderr_arg = sys.stderr if progress_enabled else subprocess.DEVNULL stderr_arg = sys.stderr if progress_enabled else subprocess.DEVNULL
# Resolve the SHA that local_ref points to
sha_result = subprocess.run(["git", "rev-parse", local_ref], capture_output=True, text=True, check=False)
if sha_result.returncode != 0:
error_msg = f"Could not resolve local ref {local_ref}"
git_stdout.write(f"error {remote_ref} {self.escape_for_stdout(error_msg)}\n")
git_stdout.flush()
continue
local_sha = sha_result.stdout.strip()
bundle_empty = False
with tempfile.TemporaryDirectory() as tmpdir: with tempfile.TemporaryDirectory() as tmpdir:
bundle_path = tmpdir + "/push.bundle" bundle_path = tmpdir + "/push.bundle"
@ -542,7 +553,7 @@ class ReticulumGitClient():
exclude_count = 0 exclude_count = 0
for sha in self.remote_refs.values(): for sha in self.remote_refs.values():
try: try:
# We need to verify Each SHA actually exists locally, since git # We need to verify each SHA actually exists locally, since git
# bundle create will fail if a ^<sha> argument references an object # bundle create will fail if a ^<sha> argument references an object
# not present in the local repository. # not present in the local repository.
result = subprocess.run(["git", "cat-file", "-t", sha], capture_output=True, check=False) result = subprocess.run(["git", "cat-file", "-t", sha], capture_output=True, check=False)
@ -556,14 +567,26 @@ class ReticulumGitClient():
if progress_enabled: create_cmd.insert(3, "--progress") if progress_enabled: create_cmd.insert(3, "--progress")
create_result = subprocess.run(create_cmd, stderr=stderr_arg, stdout=subprocess.DEVNULL) create_result = subprocess.run(create_cmd, capture_output=True, text=True, check=False)
if create_result.returncode != 0: if create_result.returncode == 0:
if result.stderr: git_stderr.write(result.stderr.decode("utf-8"))
else:
if "empty bundle" in create_result.stderr.lower():
# All objects reachable from local_ref already exist on
# the remote. In this case, no bundle is needed and we can
# update the ref directly via the operations path instead.
bundle_empty = True
RNS.log(f"Empty bundle for {local_ref}, all objects already on remote", RNS.LOG_DEBUG)
else:
if progress_enabled and create_result.stderr: git_stderr.write(create_result.stderr)
error_msg = "Bundle creation failed" error_msg = "Bundle creation failed"
git_stdout.write(f"error {remote_ref} {self.escape_for_stdout(error_msg)}\n") git_stdout.write(f"error {remote_ref} {self.escape_for_stdout(error_msg)}\n")
git_stdout.flush() git_stdout.flush()
continue continue
if not bundle_empty:
with open(bundle_path, "rb") as f: bundle_data = f.read() with open(bundle_path, "rb") as f: bundle_data = f.read()
request_data = { self.IDX_REPOSITORY: self.repo_path, "local_ref": local_ref, "remote_ref": remote_ref, request_data = { self.IDX_REPOSITORY: self.repo_path, "local_ref": local_ref, "remote_ref": remote_ref,
@ -583,6 +606,27 @@ class ReticulumGitClient():
git_stdout.flush() git_stdout.flush()
continue continue
# When all reachable objects already exist on the remote, send a
# direct ref update operation instead of a bundle.
if bundle_empty:
openration = {"action": "update_ref", "ref": remote_ref, "sha": local_sha, "force": force}
request_data = { self.IDX_REPOSITORY: self.repo_path,
"operations": [operation] }
response, metadata = self.send_request(self.PATH_PUSH, request_data)
if not response or not isinstance(response, bytes):
git_stdout.write(f"error {remote_ref} {self.escape_for_stdout('No response from server')}\n")
git_stdout.flush()
continue
status_byte = response[0]
if status_byte != 0:
error_msg = response[1:].decode('utf-8', errors='ignore')
git_stdout.write(f"error {remote_ref} {self.escape_for_stdout(error_msg)}\n")
git_stdout.flush()
continue
git_stdout.write(f"ok {remote_ref}\n") git_stdout.write(f"ok {remote_ref}\n")
git_stdout.flush() git_stdout.flush()