From 2a9ff2ee15968da90256113cd475565aa0e1ee49 Mon Sep 17 00:00:00 2001 From: JustAnotherArchivist Date: Thu, 9 Mar 2023 11:19:59 +0000 Subject: [PATCH] Support empty incremental bundles --- codearchiver/modules/git.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/codearchiver/modules/git.py b/codearchiver/modules/git.py index 2035e2a..ec475e1 100644 --- a/codearchiver/modules/git.py +++ b/codearchiver/modules/git.py @@ -2,6 +2,7 @@ import codearchiver.core import codearchiver.subprocess import datetime import functools +import hashlib import logging import os.path import shutil @@ -16,7 +17,7 @@ class GitIndex(codearchiver.core.Index): codearchiver.core.IndexField(key = 'Based on bundle', required = False, repeatable = True), codearchiver.core.IndexField(key = 'Ref', required = True, repeatable = True), codearchiver.core.IndexField(key = 'Root commit', required = True, repeatable = True), - codearchiver.core.IndexField(key = 'Commit', required = True, repeatable = True), + codearchiver.core.IndexField(key = 'Commit', required = False, repeatable = True), ] @@ -82,7 +83,21 @@ class Git(codearchiver.core.Module): basedOnBundles[oldBundle] = True _logger.info(f'Bundling into {bundle}') - codearchiver.subprocess.run_with_log(['git', 'bundle', 'create', '--progress', f'../{bundle}', '--stdin', '--reflog', '--all'], cwd = directory, input = ''.join(f'^{commit}\n' for commit in oldCommits).encode('ascii')) + status , _, stderr = codearchiver.subprocess.run_with_log(['git', 'bundle', 'create', '--progress', f'../{bundle}', '--stdin', '--reflog', '--all'], cwd = directory, input = ''.join(f'^{commit}\n' for commit in oldCommits).encode('ascii'), check = False) + if status == 128 and stderr == 'fatal: Refusing to create empty bundle.\n': + # Manually write an empty bundle instead + # Cf. Documentation/technical/bundle-format.txt and Documentation/technical/pack-format.txt in git's repository for details on the formats + _logger.info('Writing empty bundle directly instead') + with open(bundle, 'wb') as fp: + fp.write(b'# v2 git bundle\n') # bundle signature + fp.write(b'\n') # bundle end of prerequisites and refs + packdata = b'PACK' # pack signature + packdata += b'\0\0\0\x02' # pack version + packdata += b'\0\0\0\0' # pack number of objects + fp.write(packdata) + fp.write(hashlib.sha1(packdata).digest()) # pack checksum trailer + elif status != 0: + raise RuntimeError(f'git bundle creation returned with non-zero exit status {status}.') _logger.info(f'Removing clone') shutil.rmtree(directory)