diff options
author | Ben Hutchings <ben.hutchings@codethink.co.uk> | 2020-08-06 18:39:05 +0000 |
---|---|---|
committer | Ben Hutchings <ben.hutchings@codethink.co.uk> | 2020-08-06 18:39:05 +0000 |
commit | e24858ad11582082f0a329650325c1f8b0fda277 (patch) | |
tree | 037ae94ad3eb960e0c312c44aba4977a04a6219f /lorry | |
parent | 39e5fc62a338cf2ff0b3c6dd9aa8ad7547e87945 (diff) | |
parent | acc048b4ee9aea7dc90395a091b0de5968047268 (diff) | |
download | lorry-e24858ad11582082f0a329650325c1f8b0fda277.tar.gz |
Merge branch 'bwh/mercurial-work-around-unnamed-heads' into 'master'
lorry: Prune unreachable commits from hg-fast-export marks file
Closes #7
See merge request CodethinkLabs/lorry/lorry!12
Diffstat (limited to 'lorry')
-rwxr-xr-x | lorry | 57 |
1 files changed, 56 insertions, 1 deletions
@@ -31,6 +31,8 @@ import email.message import email.utils import ftplib import re +import subprocess +import tempfile import yaml @@ -625,7 +627,13 @@ class Lorry(cliapp.Application): if not os.path.exists(gitdir): self.needs_aggressive = True self.run_program(['git', 'init', '--bare', gitdir]) - + + # Since there are marks files in existing deployments that + # have broken references, fix up the marks file before rather + # than after running hg-fast-export + self.prune_unreachable_marks(gitdir, + os.path.join(gitdir, 'hg2git-marks')) + self.progress('.. fast-exporting into git') self.run_program(['hg-fast-export', '-r', '../hg', '--quiet', '--force'], cwd=gitdir) @@ -722,6 +730,53 @@ class Lorry(cliapp.Application): if self.settings['verbose']: self.output.write('%s\n' % msg) + def prune_unreachable_marks(self, gitdir, marks_name): + if not os.path.exists(marks_name): + return + + # Find reachable commits + reachable = set() + with subprocess.Popen(['git', 'rev-list', '--all'], + cwd=gitdir, stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + universal_newlines=True) as rev_list_proc: + for line in rev_list_proc.stdout: + reachable.add(line.rstrip('\n')) + + # Filter marks file to temporary file + mark_re = re.compile(r':(\S+) ([0-9a-f]{40,})\n') + marks_temp_fd, marks_temp_name = \ + tempfile.mkstemp(dir=os.path.dirname(marks_name)) + n_pruned = 0 + try: + with open(marks_temp_fd, 'w') as marks_out, \ + open(marks_name, 'r') as marks_in: + for line in marks_in: + match = mark_re.match(line) + if not match: + msg = ('%s: failed to parse line "%s"' + % (marks_name, line.rstrip('\n'))) + logging.warning(msg) + self.output.write('%s\n' % msg) + # We don't know whether it should be kept; err + # on the side of caution + marks_out.write(line) + elif match.group(2) in reachable: + marks_out.write(line) + else: + n_pruned += 1 + + # On success, replace marks file with temporary file + os.rename(marks_temp_name, marks_name) + + if n_pruned: + self.progress('%s: pruned %d unreachable commit(s)' + % (marks_name, n_pruned)) + except: + # On failure, delete temporary file + os.unlink(marks_temp_name) + raise + if __name__ == '__main__': Lorry(version=__version__).run() |