Mercurial > hg-git-sync
annotate hggit_sync.py @ 3:7d99080f276f default tip
Fix rebuild option.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Fri, 29 Jul 2016 21:33:53 -0700 |
parents | 19156ccdc3e1 |
children |
rev | line source |
---|---|
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) | |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
40 |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
41 # Build the commit map with the "left" commits' info. |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
42 commit_map = {} |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
43 for c1 in commits1: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
44 if c1.timestamp not in commit_map: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
45 commit_map[c1.timestamp] = [(c1, None)] |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
46 else: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
47 # Each entry in the map is a list in case we have commits at the |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
48 # same timestamp. It's rare, but it happens, especially with |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
49 # people who have some fucked-up Git-fu. |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
50 print("Conflicting timestamps between %s and %s" % |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
51 (c1.nodeid, commit_map[c1.timestamp][0][0].nodeid)) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
52 commit_map[c1.timestamp].append((c1, None)) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
53 |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
54 # Now put the "easy" matches from the "right" commits. |
1
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
55 orphan_commits2 = [] |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
56 print("Building commit map...") |
0 | 57 for c in commits2: |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
58 entry = commit_map.get(c.timestamp) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
59 if entry is None: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
60 # No "left" commit had this timestamp... we'll have an orphan. |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
61 entry = [(None, None)] |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
62 commit_map[c.timestamp] = entry |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
63 |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
64 # Add the commit info. |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
65 idx = 0 |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
66 if len(entry) > 1: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
67 for i, e in enumerate(entry): |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
68 if e[0] and e[0].description.strip() == c.description.strip(): |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
69 idx = i |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
70 break |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
71 if entry[idx][1] is None: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
72 entry[idx] = (entry[idx][0], c) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
73 else: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
74 print("Attempting to match 2 commits (%s and %s) to the same base " |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
75 "commit %s... creating orphan instead." % |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
76 (entry[idx][1].nodeid, c.nodeid, entry[idx][0].nodeid)) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
77 entry.append((None, c)) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
78 idx = len(entry) - 1 |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
79 if entry[idx][0] is None: |
1
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
80 orphan_commits2.append(c) |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
81 |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
82 orphan_commits1 = [] |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
83 for entry in commit_map.values(): |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
84 for e in entry: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
85 if e[1] is None: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
86 orphan_commits1.append(e[0]) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
87 |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
88 if orphan_commits1 or orphan_commits2: |
1
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
89 print("Fixing orphaned commits...") |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
90 did_fix = True |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
91 while did_fix: |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
92 did_fix = False |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
93 for c2 in orphan_commits2: |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
94 for c1 in orphan_commits1: |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
95 if c1.description.strip() == c2.description.strip(): |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
96 print("Mapping '%s' to '%s'" % (c1.nodeid, c2.nodeid)) |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
97 print(" Same description: %s" % c1.description) |
1
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
98 print(" Timestamp difference: %d" % (c2.timestamp - c1.timestamp)) |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
99 entry = commit_map[c1.timestamp] |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
100 for i, e in enumerate(entry): |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
101 if e[0] and e[0].nodeid == c1.nodeid: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
102 entry[i] = (c1, c2) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
103 break |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
104 entry = commit_map[c2.timestamp] |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
105 for i, e in enumerate(entry): |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
106 if e[1] and e[1].nodeid == c2.nodeid: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
107 assert e[0] is None |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
108 del entry[i] |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
109 if len(entry) == 0: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
110 del commit_map[c2.timestamp] |
1
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
111 orphan_commits1.remove(c1) |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
112 orphan_commits2.remove(c2) |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
113 did_fix = True |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
114 break |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
115 if did_fix: |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
116 break |
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
117 |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
118 if orphan_commits1 or orphan_commits2: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
119 print("Still have %d and %d orphaned commits." % |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
120 (len(orphan_commits1), len(orphan_commits2))) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
121 |
0 | 122 return commit_map |
123 | |
124 | |
125 def main(): | |
126 parser = argparse.ArgumentParser( | |
127 description="Helps you fix problems with hg-git. Maybe.", | |
128 epilog="Don't trust scripts you found on the web! Backup your stuff!") | |
129 parser.add_argument( | |
130 '--rebuild', | |
131 metavar='REMOTE', | |
132 help="Rebuild the Git repo from the given remote URL.") | |
133 parser.add_argument( | |
134 'mapfile', | |
135 metavar='MAPFILE', | |
1
e9f44d2deb94
Add some lame improvements to the map building.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
136 nargs='?', |
0 | 137 help="The path to the mapfile to generate.") |
138 res = parser.parse_args() | |
139 | |
140 hg_repo = os.getcwd() | |
141 if not os.path.exists(os.path.join(hg_repo, '.hg')): | |
142 print("You must run this in the root of a Mercurial repository.") | |
143 return 1 | |
144 | |
145 git_repo = os.path.join(hg_repo, '.hg', 'git') | |
146 if res.rebuild: | |
147 print("Removing existing Git repo...") | |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
148 if os.path.isdir(git_repo): |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
149 shutil.rmtree(git_repo) |
0 | 150 print("Syncing it again into: %s" % git_repo) |
3 | 151 print(" from: %s" % res.rebuild) |
0 | 152 git_output = subprocess.check_output([ |
153 'git', 'clone', '--bare', res.rebuild, git_repo]) | |
154 | |
155 if not os.path.exists(git_repo): | |
156 print("This Mercurial repository doesn't seem to have any Git mirror " | |
157 "to sync with.") | |
158 return 1 | |
159 | |
160 hg_output = subprocess.check_output([ | |
161 'hg', 'log', | |
162 '--template', "{node} {date}\n{firstline(desc)}\n\n"]) | |
163 hg_commits = parse_commits(hg_output) | |
164 | |
165 os.chdir(git_repo) | |
166 git_output = subprocess.check_output([ | |
167 'git', 'log', '--format=%H %ct%n%s%n%n']) | |
168 git_commits = parse_commits(git_output) | |
169 os.chdir(hg_repo) | |
170 | |
171 commit_map = build_commit_map(git_commits, hg_commits) | |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
172 for key, vals in commit_map.iteritems(): |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
173 for val in vals: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
174 if val[0] is None: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
175 print("Mercurial commit '%s' (%s) has no Git mirror yet: %s" % |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
176 (val[1].nodeid, val[1].timestamp, val[1].description)) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
177 if val[1] is None: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
178 print("Git commit '%s' (%s) is new: %s" % |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
179 (val[0].nodeid, val[0].timestamp, val[0].description)) |
0 | 180 |
181 map_file = res.mapfile or os.path.join(hg_repo, '.hg', 'git-mapfile') | |
182 print("Saving map file: %s" % map_file) | |
183 with codecs.open(map_file, 'w', encoding='utf8') as fp: | |
2
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
184 for key, vals in commit_map.iteritems(): |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
185 for val in vals: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
186 if val[0] is None or val[1] is None: |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
187 continue |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
188 fp.write(val[0].nodeid) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
189 fp.write(' ') |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
190 fp.write(val[1].nodeid) |
19156ccdc3e1
Handle multiple commits at the same timestamp. Yes, that happens.
Ludovic Chabant <ludovic@chabant.com>
parents:
1
diff
changeset
|
191 fp.write('\n') |
0 | 192 |
193 | |
194 if __name__ == '__main__': | |
195 res = main() | |
196 sys.exit(res) | |
197 |