summaryrefslogtreecommitdiff
path: root/lorry
diff options
context:
space:
mode:
authorBen Hutchings <ben.hutchings@codethink.co.uk>2020-08-06 18:39:05 +0000
committerBen Hutchings <ben.hutchings@codethink.co.uk>2020-08-06 18:39:05 +0000
commite24858ad11582082f0a329650325c1f8b0fda277 (patch)
tree037ae94ad3eb960e0c312c44aba4977a04a6219f /lorry
parent39e5fc62a338cf2ff0b3c6dd9aa8ad7547e87945 (diff)
parentacc048b4ee9aea7dc90395a091b0de5968047268 (diff)
downloadlorry-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-xlorry57
1 files changed, 56 insertions, 1 deletions
diff --git a/lorry b/lorry
index 1989400..b42861b 100755
--- a/lorry
+++ b/lorry
@@ -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()