0
|
1 import os
|
|
2 import os.path
|
|
3 import sys
|
|
4 import codecs
|
|
5 import shutil
|
|
6 import argparse
|
|
7 import subprocess
|
|
8
|
|
9
|
|
10 class CommitInfo(object):
|
|
11 def __init__(self):
|
|
12 self.nodeid = None
|
|
13 self.timestamp = None
|
|
14 self.description = None
|
|
15
|
|
16
|
|
17 def parse_commits(text):
|
|
18 commits = []
|
|
19 cur_commit = None
|
|
20 for line in text.split('\n'):
|
|
21 if line == '':
|
|
22 if cur_commit:
|
|
23 commits.append(cur_commit)
|
|
24 cur_commit = None
|
|
25 continue
|
|
26 if cur_commit is None:
|
|
27 cur_commit = CommitInfo()
|
|
28 if cur_commit.nodeid is None:
|
|
29 id_and_date = line.split(' ', 1)
|
|
30 cur_commit.nodeid = id_and_date[0]
|
|
31 cur_commit.timestamp = int(id_and_date[1].split('.')[0])
|
|
32 else:
|
|
33 cur_commit.description = line
|
|
34 return commits
|
|
35
|
|
36
|
|
37 def build_commit_map(commits1, commits2):
|
|
38 commits1 = sorted(commits1, key=lambda c: c.timestamp)
|
|
39 commits2 = sorted(commits2, key=lambda c: c.timestamp)
|
|
40 commit_map = dict(map(lambda c: (c.timestamp, (c, None)), commits1))
|
|
41 for c in commits2:
|
|
42 entry = commit_map.get(c.timestamp, (None, None))
|
|
43 entry = (entry[0], c)
|
|
44 commit_map[c.timestamp] = entry
|
|
45 return commit_map
|
|
46
|
|
47
|
|
48 def main():
|
|
49 parser = argparse.ArgumentParser(
|
|
50 description="Helps you fix problems with hg-git. Maybe.",
|
|
51 epilog="Don't trust scripts you found on the web! Backup your stuff!")
|
|
52 parser.add_argument(
|
|
53 '--rebuild',
|
|
54 nargs=1,
|
|
55 metavar='REMOTE',
|
|
56 help="Rebuild the Git repo from the given remote URL.")
|
|
57 parser.add_argument(
|
|
58 'mapfile',
|
|
59 metavar='MAPFILE',
|
|
60 help="The path to the mapfile to generate.")
|
|
61 res = parser.parse_args()
|
|
62
|
|
63 hg_repo = os.getcwd()
|
|
64 if not os.path.exists(os.path.join(hg_repo, '.hg')):
|
|
65 print("You must run this in the root of a Mercurial repository.")
|
|
66 return 1
|
|
67
|
|
68 git_repo = os.path.join(hg_repo, '.hg', 'git')
|
|
69 if res.rebuild:
|
|
70 print("Removing existing Git repo...")
|
|
71 shutil.rmtree(git_repo)
|
|
72 print("Syncing it again into: %s" % git_repo)
|
|
73 git_output = subprocess.check_output([
|
|
74 'git', 'clone', '--bare', res.rebuild, git_repo])
|
|
75
|
|
76 if not os.path.exists(git_repo):
|
|
77 print("This Mercurial repository doesn't seem to have any Git mirror "
|
|
78 "to sync with.")
|
|
79 return 1
|
|
80
|
|
81 hg_output = subprocess.check_output([
|
|
82 'hg', 'log',
|
|
83 '--template', "{node} {date}\n{firstline(desc)}\n\n"])
|
|
84 hg_commits = parse_commits(hg_output)
|
|
85
|
|
86 os.chdir(git_repo)
|
|
87 git_output = subprocess.check_output([
|
|
88 'git', 'log', '--format=%H %ct%n%s%n%n'])
|
|
89 git_commits = parse_commits(git_output)
|
|
90 os.chdir(hg_repo)
|
|
91
|
|
92 commit_map = build_commit_map(git_commits, hg_commits)
|
|
93 for key, val in commit_map.iteritems():
|
|
94 if val[0] is None:
|
|
95 print("Mercurial commit '%s' (%s) has no Git mirror yet: %s" %
|
|
96 (val[1].nodeid, val[1].timestamp, val[1].description))
|
|
97 if val[1] is None:
|
|
98 print("Git commit '%s' (%s) is new: %s" %
|
|
99 (val[0].nodeid, val[0].timestamp, val[0].description))
|
|
100
|
|
101 map_file = res.mapfile or os.path.join(hg_repo, '.hg', 'git-mapfile')
|
|
102 print("Saving map file: %s" % map_file)
|
|
103 with codecs.open(map_file, 'w', encoding='utf8') as fp:
|
|
104 for key, val in commit_map.iteritems():
|
|
105 if val[0] is None or val[1] is None:
|
|
106 continue
|
|
107 fp.write(val[0].nodeid)
|
|
108 fp.write(' ')
|
|
109 fp.write(val[1].nodeid)
|
|
110 fp.write('\n')
|
|
111
|
|
112
|
|
113 if __name__ == '__main__':
|
|
114 res = main()
|
|
115 sys.exit(res)
|
|
116
|