##// END OF EJS Templates
convert: split converter into convertsource and convertsink
Brendan Cully -
r4763:8e9d3fae default
parent child Browse files
Show More
@@ -1,334 +1,341
1 # convert.py Foreign SCM converter
1 # convert.py Foreign SCM converter
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from common import NoRepo
8 from common import NoRepo, converter_source, converter_sink
9 from cvs import convert_cvs
9 from cvs import convert_cvs
10 from git import convert_git
10 from git import convert_git
11 from hg import convert_mercurial
11 from hg import convert_mercurial
12
12
13 import os, shutil
13 import os, shutil
14 from mercurial import hg, ui, util, commands
14 from mercurial import hg, ui, util, commands
15
15
16 commands.norepo += " convert"
16 commands.norepo += " convert"
17
17
18 converters = [convert_cvs, convert_git, convert_mercurial]
18 converters = [convert_cvs, convert_git, convert_mercurial]
19
19
20 def converter(ui, path, rev=None):
20 def convertsource(ui, path, rev=None):
21 for c in converters:
22 try:
23 converter = c(ui, path, rev=rev)
24 if not isinstance(converter, converter_source):
25 raise util.Abort('%s: cannot read from this repository type' % path)
26 return converter
27 except NoRepo:
28 pass
29 raise util.Abort('%s: unknown repository type' % path)
30
31 def convertsink(ui, path):
21 if not os.path.isdir(path):
32 if not os.path.isdir(path):
22 raise util.Abort("%s: not a directory" % path)
33 raise util.Abort("%s: not a directory" % path)
23 for c in converters:
34 for c in converters:
24 try:
35 try:
25 if rev:
36 converter = c(ui, path)
26 return c(ui, path, rev=rev)
37 if not isinstance(converter, converter_sink):
27 else:
38 raise util.Abort('%s: cannot write to this repository type' % path)
28 return c(ui, path)
39 return converter
29 except NoRepo:
40 except NoRepo:
30 pass
41 pass
31 raise util.Abort("%s: unknown repository type" % path)
42 raise util.Abort('%s: unknown repository type' % path)
32
43
33 class convert(object):
44 class convert(object):
34 def __init__(self, ui, source, dest, mapfile, opts):
45 def __init__(self, ui, source, dest, mapfile, opts):
35
46
36 self.source = source
47 self.source = source
37 self.dest = dest
48 self.dest = dest
38 self.ui = ui
49 self.ui = ui
39 self.opts = opts
50 self.opts = opts
40 self.commitcache = {}
51 self.commitcache = {}
41 self.mapfile = mapfile
52 self.mapfile = mapfile
42 self.mapfilefd = None
53 self.mapfilefd = None
43 self.authors = {}
54 self.authors = {}
44 self.authorfile = None
55 self.authorfile = None
45
56
46 self.map = {}
57 self.map = {}
47 try:
58 try:
48 origmapfile = open(self.mapfile, 'r')
59 origmapfile = open(self.mapfile, 'r')
49 for l in origmapfile:
60 for l in origmapfile:
50 sv, dv = l[:-1].split()
61 sv, dv = l[:-1].split()
51 self.map[sv] = dv
62 self.map[sv] = dv
52 origmapfile.close()
63 origmapfile.close()
53 except IOError:
64 except IOError:
54 pass
65 pass
55
66
56 # Read first the dst author map if any
67 # Read first the dst author map if any
57 authorfile = self.dest.authorfile()
68 authorfile = self.dest.authorfile()
58 if authorfile and os.path.exists(authorfile):
69 if authorfile and os.path.exists(authorfile):
59 self.readauthormap(authorfile)
70 self.readauthormap(authorfile)
60 # Extend/Override with new author map if necessary
71 # Extend/Override with new author map if necessary
61 if opts.get('authors'):
72 if opts.get('authors'):
62 self.readauthormap(opts.get('authors'))
73 self.readauthormap(opts.get('authors'))
63 self.authorfile = self.dest.authorfile()
74 self.authorfile = self.dest.authorfile()
64
75
65 def walktree(self, heads):
76 def walktree(self, heads):
66 '''Return a mapping that identifies the uncommitted parents of every
77 '''Return a mapping that identifies the uncommitted parents of every
67 uncommitted changeset.'''
78 uncommitted changeset.'''
68 visit = heads
79 visit = heads
69 known = {}
80 known = {}
70 parents = {}
81 parents = {}
71 while visit:
82 while visit:
72 n = visit.pop(0)
83 n = visit.pop(0)
73 if n in known or n in self.map: continue
84 if n in known or n in self.map: continue
74 known[n] = 1
85 known[n] = 1
75 self.commitcache[n] = self.source.getcommit(n)
86 self.commitcache[n] = self.source.getcommit(n)
76 cp = self.commitcache[n].parents
87 cp = self.commitcache[n].parents
77 parents[n] = []
88 parents[n] = []
78 for p in cp:
89 for p in cp:
79 parents[n].append(p)
90 parents[n].append(p)
80 visit.append(p)
91 visit.append(p)
81
92
82 return parents
93 return parents
83
94
84 def toposort(self, parents):
95 def toposort(self, parents):
85 '''Return an ordering such that every uncommitted changeset is
96 '''Return an ordering such that every uncommitted changeset is
86 preceeded by all its uncommitted ancestors.'''
97 preceeded by all its uncommitted ancestors.'''
87 visit = parents.keys()
98 visit = parents.keys()
88 seen = {}
99 seen = {}
89 children = {}
100 children = {}
90
101
91 while visit:
102 while visit:
92 n = visit.pop(0)
103 n = visit.pop(0)
93 if n in seen: continue
104 if n in seen: continue
94 seen[n] = 1
105 seen[n] = 1
95 # Ensure that nodes without parents are present in the 'children'
106 # Ensure that nodes without parents are present in the 'children'
96 # mapping.
107 # mapping.
97 children.setdefault(n, [])
108 children.setdefault(n, [])
98 for p in parents[n]:
109 for p in parents[n]:
99 if not p in self.map:
110 if not p in self.map:
100 visit.append(p)
111 visit.append(p)
101 children.setdefault(p, []).append(n)
112 children.setdefault(p, []).append(n)
102
113
103 s = []
114 s = []
104 removed = {}
115 removed = {}
105 visit = children.keys()
116 visit = children.keys()
106 while visit:
117 while visit:
107 n = visit.pop(0)
118 n = visit.pop(0)
108 if n in removed: continue
119 if n in removed: continue
109 dep = 0
120 dep = 0
110 if n in parents:
121 if n in parents:
111 for p in parents[n]:
122 for p in parents[n]:
112 if p in self.map: continue
123 if p in self.map: continue
113 if p not in removed:
124 if p not in removed:
114 # we're still dependent
125 # we're still dependent
115 visit.append(n)
126 visit.append(n)
116 dep = 1
127 dep = 1
117 break
128 break
118
129
119 if not dep:
130 if not dep:
120 # all n's parents are in the list
131 # all n's parents are in the list
121 removed[n] = 1
132 removed[n] = 1
122 if n not in self.map:
133 if n not in self.map:
123 s.append(n)
134 s.append(n)
124 if n in children:
135 if n in children:
125 for c in children[n]:
136 for c in children[n]:
126 visit.insert(0, c)
137 visit.insert(0, c)
127
138
128 if self.opts.get('datesort'):
139 if self.opts.get('datesort'):
129 depth = {}
140 depth = {}
130 for n in s:
141 for n in s:
131 depth[n] = 0
142 depth[n] = 0
132 pl = [p for p in self.commitcache[n].parents
143 pl = [p for p in self.commitcache[n].parents
133 if p not in self.map]
144 if p not in self.map]
134 if pl:
145 if pl:
135 depth[n] = max([depth[p] for p in pl]) + 1
146 depth[n] = max([depth[p] for p in pl]) + 1
136
147
137 s = [(depth[n], self.commitcache[n].date, n) for n in s]
148 s = [(depth[n], self.commitcache[n].date, n) for n in s]
138 s.sort()
149 s.sort()
139 s = [e[2] for e in s]
150 s = [e[2] for e in s]
140
151
141 return s
152 return s
142
153
143 def mapentry(self, src, dst):
154 def mapentry(self, src, dst):
144 if self.mapfilefd is None:
155 if self.mapfilefd is None:
145 try:
156 try:
146 self.mapfilefd = open(self.mapfile, "a")
157 self.mapfilefd = open(self.mapfile, "a")
147 except IOError, (errno, strerror):
158 except IOError, (errno, strerror):
148 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror))
159 raise util.Abort("Could not open map file %s: %s, %s\n" % (self.mapfile, errno, strerror))
149 self.map[src] = dst
160 self.map[src] = dst
150 self.mapfilefd.write("%s %s\n" % (src, dst))
161 self.mapfilefd.write("%s %s\n" % (src, dst))
151 self.mapfilefd.flush()
162 self.mapfilefd.flush()
152
163
153 def writeauthormap(self):
164 def writeauthormap(self):
154 authorfile = self.authorfile
165 authorfile = self.authorfile
155 if authorfile:
166 if authorfile:
156 self.ui.status('Writing author map file %s\n' % authorfile)
167 self.ui.status('Writing author map file %s\n' % authorfile)
157 ofile = open(authorfile, 'w+')
168 ofile = open(authorfile, 'w+')
158 for author in self.authors:
169 for author in self.authors:
159 ofile.write("%s=%s\n" % (author, self.authors[author]))
170 ofile.write("%s=%s\n" % (author, self.authors[author]))
160 ofile.close()
171 ofile.close()
161
172
162 def readauthormap(self, authorfile):
173 def readauthormap(self, authorfile):
163 afile = open(authorfile, 'r')
174 afile = open(authorfile, 'r')
164 for line in afile:
175 for line in afile:
165 try:
176 try:
166 srcauthor = line.split('=')[0].strip()
177 srcauthor = line.split('=')[0].strip()
167 dstauthor = line.split('=')[1].strip()
178 dstauthor = line.split('=')[1].strip()
168 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
179 if srcauthor in self.authors and dstauthor != self.authors[srcauthor]:
169 self.ui.status(
180 self.ui.status(
170 'Overriding mapping for author %s, was %s, will be %s\n'
181 'Overriding mapping for author %s, was %s, will be %s\n'
171 % (srcauthor, self.authors[srcauthor], dstauthor))
182 % (srcauthor, self.authors[srcauthor], dstauthor))
172 else:
183 else:
173 self.ui.debug('Mapping author %s to %s\n'
184 self.ui.debug('Mapping author %s to %s\n'
174 % (srcauthor, dstauthor))
185 % (srcauthor, dstauthor))
175 self.authors[srcauthor] = dstauthor
186 self.authors[srcauthor] = dstauthor
176 except IndexError:
187 except IndexError:
177 self.ui.warn(
188 self.ui.warn(
178 'Ignoring bad line in author file map %s: %s\n'
189 'Ignoring bad line in author file map %s: %s\n'
179 % (authorfile, line))
190 % (authorfile, line))
180 afile.close()
191 afile.close()
181
192
182 def copy(self, rev):
193 def copy(self, rev):
183 c = self.commitcache[rev]
194 c = self.commitcache[rev]
184 files = self.source.getchanges(rev)
195 files = self.source.getchanges(rev)
185
196
186 for f, v in files:
197 for f, v in files:
187 try:
198 try:
188 data = self.source.getfile(f, v)
199 data = self.source.getfile(f, v)
189 except IOError, inst:
200 except IOError, inst:
190 self.dest.delfile(f)
201 self.dest.delfile(f)
191 else:
202 else:
192 e = self.source.getmode(f, v)
203 e = self.source.getmode(f, v)
193 self.dest.putfile(f, e, data)
204 self.dest.putfile(f, e, data)
194
205
195 r = [self.map[v] for v in c.parents]
206 r = [self.map[v] for v in c.parents]
196 f = [f for f, v in files]
207 f = [f for f, v in files]
197 newnode = self.dest.putcommit(f, r, c)
208 newnode = self.dest.putcommit(f, r, c)
198 self.mapentry(rev, newnode)
209 self.mapentry(rev, newnode)
199
210
200 def convert(self):
211 def convert(self):
201 try:
212 try:
202 self.ui.status("scanning source...\n")
213 self.ui.status("scanning source...\n")
203 heads = self.source.getheads()
214 heads = self.source.getheads()
204 parents = self.walktree(heads)
215 parents = self.walktree(heads)
205 self.ui.status("sorting...\n")
216 self.ui.status("sorting...\n")
206 t = self.toposort(parents)
217 t = self.toposort(parents)
207 num = len(t)
218 num = len(t)
208 c = None
219 c = None
209
220
210 self.ui.status("converting...\n")
221 self.ui.status("converting...\n")
211 for c in t:
222 for c in t:
212 num -= 1
223 num -= 1
213 desc = self.commitcache[c].desc
224 desc = self.commitcache[c].desc
214 if "\n" in desc:
225 if "\n" in desc:
215 desc = desc.splitlines()[0]
226 desc = desc.splitlines()[0]
216 author = self.commitcache[c].author
227 author = self.commitcache[c].author
217 author = self.authors.get(author, author)
228 author = self.authors.get(author, author)
218 self.commitcache[c].author = author
229 self.commitcache[c].author = author
219 self.ui.status("%d %s\n" % (num, desc))
230 self.ui.status("%d %s\n" % (num, desc))
220 self.copy(c)
231 self.copy(c)
221
232
222 tags = self.source.gettags()
233 tags = self.source.gettags()
223 ctags = {}
234 ctags = {}
224 for k in tags:
235 for k in tags:
225 v = tags[k]
236 v = tags[k]
226 if v in self.map:
237 if v in self.map:
227 ctags[k] = self.map[v]
238 ctags[k] = self.map[v]
228
239
229 if c and ctags:
240 if c and ctags:
230 nrev = self.dest.puttags(ctags)
241 nrev = self.dest.puttags(ctags)
231 # write another hash correspondence to override the previous
242 # write another hash correspondence to override the previous
232 # one so we don't end up with extra tag heads
243 # one so we don't end up with extra tag heads
233 if nrev:
244 if nrev:
234 self.mapentry(c, nrev)
245 self.mapentry(c, nrev)
235
246
236 self.writeauthormap()
247 self.writeauthormap()
237 finally:
248 finally:
238 self.cleanup()
249 self.cleanup()
239
250
240 def cleanup(self):
251 def cleanup(self):
241 if self.mapfilefd:
252 if self.mapfilefd:
242 self.mapfilefd.close()
253 self.mapfilefd.close()
243
254
244 def _convert(ui, src, dest=None, mapfile=None, **opts):
255 def _convert(ui, src, dest=None, mapfile=None, **opts):
245 '''Convert a foreign SCM repository to a Mercurial one.
256 '''Convert a foreign SCM repository to a Mercurial one.
246
257
247 Accepted source formats:
258 Accepted source formats:
248 - GIT
259 - GIT
249 - CVS
260 - CVS
250
261
251 Accepted destination formats:
262 Accepted destination formats:
252 - Mercurial
263 - Mercurial
253
264
254 If no revision is given, all revisions will be converted. Otherwise,
265 If no revision is given, all revisions will be converted. Otherwise,
255 convert will only import up to the named revision (given in a format
266 convert will only import up to the named revision (given in a format
256 understood by the source).
267 understood by the source).
257
268
258 If destination isn't given, a new Mercurial repo named <src>-hg will
269 If destination isn't given, a new Mercurial repo named <src>-hg will
259 be created. If <mapfile> isn't given, it will be put in a default
270 be created. If <mapfile> isn't given, it will be put in a default
260 location (<dest>/.hg/shamap by default)
271 location (<dest>/.hg/shamap by default)
261
272
262 The <mapfile> is a simple text file that maps each source commit ID to
273 The <mapfile> is a simple text file that maps each source commit ID to
263 the destination ID for that revision, like so:
274 the destination ID for that revision, like so:
264 <source ID> <destination ID>
275 <source ID> <destination ID>
265
276
266 If the file doesn't exist, it's automatically created. It's updated
277 If the file doesn't exist, it's automatically created. It's updated
267 on each commit copied, so convert-repo can be interrupted and can
278 on each commit copied, so convert-repo can be interrupted and can
268 be run repeatedly to copy new commits.
279 be run repeatedly to copy new commits.
269
280
270 The [username mapping] file is a simple text file that maps each source
281 The [username mapping] file is a simple text file that maps each source
271 commit author to a destination commit author. It is handy for source SCMs
282 commit author to a destination commit author. It is handy for source SCMs
272 that use unix logins to identify authors (eg: CVS). One line per author
283 that use unix logins to identify authors (eg: CVS). One line per author
273 mapping and the line format is:
284 mapping and the line format is:
274 srcauthor=whatever string you want
285 srcauthor=whatever string you want
275 '''
286 '''
276
287
277 if not dest:
288 if not dest:
278 dest = src + "-hg"
289 dest = src + "-hg"
279 ui.status("assuming destination %s\n" % dest)
290 ui.status("assuming destination %s\n" % dest)
280
291
281 # Try to be smart and initalize things when required
292 # Try to be smart and initalize things when required
282 created = False
293 created = False
283 if os.path.isdir(dest):
294 if os.path.isdir(dest):
284 if len(os.listdir(dest)) > 0:
295 if len(os.listdir(dest)) > 0:
285 try:
296 try:
286 hg.repository(ui, dest)
297 hg.repository(ui, dest)
287 ui.status("destination %s is a Mercurial repository\n" % dest)
298 ui.status("destination %s is a Mercurial repository\n" % dest)
288 except hg.RepoError:
299 except hg.RepoError:
289 raise util.Abort(
300 raise util.Abort(
290 "destination directory %s is not empty.\n"
301 "destination directory %s is not empty.\n"
291 "Please specify an empty directory to be initialized\n"
302 "Please specify an empty directory to be initialized\n"
292 "or an already initialized mercurial repository"
303 "or an already initialized mercurial repository"
293 % dest)
304 % dest)
294 else:
305 else:
295 ui.status("initializing destination %s repository\n" % dest)
306 ui.status("initializing destination %s repository\n" % dest)
296 hg.repository(ui, dest, create=True)
307 hg.repository(ui, dest, create=True)
297 created = True
308 created = True
298 elif os.path.exists(dest):
309 elif os.path.exists(dest):
299 raise util.Abort("destination %s exists and is not a directory" % dest)
310 raise util.Abort("destination %s exists and is not a directory" % dest)
300 else:
311 else:
301 ui.status("initializing destination %s repository\n" % dest)
312 ui.status("initializing destination %s repository\n" % dest)
302 hg.repository(ui, dest, create=True)
313 hg.repository(ui, dest, create=True)
303 created = True
314 created = True
304
315
305 destc = converter(ui, dest)
316 destc = convertsink(ui, dest)
306 if not hasattr(destc, "putcommit"):
307 raise util.Abort("%s: can't write to this repo type" % src)
308
317
309 try:
318 try:
310 srcc = converter(ui, src, rev=opts.get('rev'))
319 srcc = convertsource(ui, src, rev=opts.get('rev'))
311 if not hasattr(srcc, "getcommit"):
312 raise util.Abort("%s: can't read from this repo type" % src)
313 except Exception:
320 except Exception:
314 if created:
321 if created:
315 shutil.rmtree(dest, True)
322 shutil.rmtree(dest, True)
316 raise
323 raise
317
324
318 if not mapfile:
325 if not mapfile:
319 try:
326 try:
320 mapfile = destc.mapfile()
327 mapfile = destc.mapfile()
321 except:
328 except:
322 mapfile = os.path.join(destc, "map")
329 mapfile = os.path.join(destc, "map")
323
330
324 c = convert(ui, srcc, destc, mapfile, opts)
331 c = convert(ui, srcc, destc, mapfile, opts)
325 c.convert()
332 c.convert()
326
333
327 cmdtable = {
334 cmdtable = {
328 "convert":
335 "convert":
329 (_convert,
336 (_convert,
330 [('A', 'authors', '', 'username mapping filename'),
337 [('A', 'authors', '', 'username mapping filename'),
331 ('r', 'rev', '', 'import up to target revision REV'),
338 ('r', 'rev', '', 'import up to target revision REV'),
332 ('', 'datesort', None, 'try to sort changesets by date')],
339 ('', 'datesort', None, 'try to sort changesets by date')],
333 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
340 'hg convert [OPTION]... SOURCE [DEST [MAPFILE]]'),
334 }
341 }
@@ -1,94 +1,94
1 # hg backend for convert extension
1 # hg backend for convert extension
2
2
3 import os, time
3 import os, time
4 from mercurial import hg
4 from mercurial import hg
5
5
6 from common import NoRepo, converter_sink
6 from common import NoRepo, converter_sink
7
7
8 class convert_mercurial(converter_sink):
8 class convert_mercurial(converter_sink):
9 def __init__(self, ui, path, rev=None):
9 def __init__(self, ui, path):
10 self.path = path
10 self.path = path
11 self.ui = ui
11 self.ui = ui
12 try:
12 try:
13 self.repo = hg.repository(self.ui, path)
13 self.repo = hg.repository(self.ui, path)
14 except:
14 except:
15 raise NoRepo("could open hg repo %s" % path)
15 raise NoRepo("could open hg repo %s" % path)
16
16
17 def mapfile(self):
17 def mapfile(self):
18 return os.path.join(self.path, ".hg", "shamap")
18 return os.path.join(self.path, ".hg", "shamap")
19
19
20 def authorfile(self):
20 def authorfile(self):
21 return os.path.join(self.path, ".hg", "authormap")
21 return os.path.join(self.path, ".hg", "authormap")
22
22
23 def getheads(self):
23 def getheads(self):
24 h = self.repo.changelog.heads()
24 h = self.repo.changelog.heads()
25 return [ hg.hex(x) for x in h ]
25 return [ hg.hex(x) for x in h ]
26
26
27 def putfile(self, f, e, data):
27 def putfile(self, f, e, data):
28 self.repo.wwrite(f, data, e)
28 self.repo.wwrite(f, data, e)
29 if self.repo.dirstate.state(f) == '?':
29 if self.repo.dirstate.state(f) == '?':
30 self.repo.dirstate.update([f], "a")
30 self.repo.dirstate.update([f], "a")
31
31
32 def delfile(self, f):
32 def delfile(self, f):
33 try:
33 try:
34 os.unlink(self.repo.wjoin(f))
34 os.unlink(self.repo.wjoin(f))
35 #self.repo.remove([f])
35 #self.repo.remove([f])
36 except:
36 except:
37 pass
37 pass
38
38
39 def putcommit(self, files, parents, commit):
39 def putcommit(self, files, parents, commit):
40 seen = {}
40 seen = {}
41 pl = []
41 pl = []
42 for p in parents:
42 for p in parents:
43 if p not in seen:
43 if p not in seen:
44 pl.append(p)
44 pl.append(p)
45 seen[p] = 1
45 seen[p] = 1
46 parents = pl
46 parents = pl
47
47
48 if len(parents) < 2: parents.append("0" * 40)
48 if len(parents) < 2: parents.append("0" * 40)
49 if len(parents) < 2: parents.append("0" * 40)
49 if len(parents) < 2: parents.append("0" * 40)
50 p2 = parents.pop(0)
50 p2 = parents.pop(0)
51
51
52 text = commit.desc
52 text = commit.desc
53 extra = {}
53 extra = {}
54 try:
54 try:
55 extra["branch"] = commit.branch
55 extra["branch"] = commit.branch
56 except AttributeError:
56 except AttributeError:
57 pass
57 pass
58
58
59 while parents:
59 while parents:
60 p1 = p2
60 p1 = p2
61 p2 = parents.pop(0)
61 p2 = parents.pop(0)
62 a = self.repo.rawcommit(files, text, commit.author, commit.date,
62 a = self.repo.rawcommit(files, text, commit.author, commit.date,
63 hg.bin(p1), hg.bin(p2), extra=extra)
63 hg.bin(p1), hg.bin(p2), extra=extra)
64 text = "(octopus merge fixup)\n"
64 text = "(octopus merge fixup)\n"
65 p2 = hg.hex(self.repo.changelog.tip())
65 p2 = hg.hex(self.repo.changelog.tip())
66
66
67 return p2
67 return p2
68
68
69 def puttags(self, tags):
69 def puttags(self, tags):
70 try:
70 try:
71 old = self.repo.wfile(".hgtags").read()
71 old = self.repo.wfile(".hgtags").read()
72 oldlines = old.splitlines(1)
72 oldlines = old.splitlines(1)
73 oldlines.sort()
73 oldlines.sort()
74 except:
74 except:
75 oldlines = []
75 oldlines = []
76
76
77 k = tags.keys()
77 k = tags.keys()
78 k.sort()
78 k.sort()
79 newlines = []
79 newlines = []
80 for tag in k:
80 for tag in k:
81 newlines.append("%s %s\n" % (tags[tag], tag))
81 newlines.append("%s %s\n" % (tags[tag], tag))
82
82
83 newlines.sort()
83 newlines.sort()
84
84
85 if newlines != oldlines:
85 if newlines != oldlines:
86 self.ui.status("updating tags\n")
86 self.ui.status("updating tags\n")
87 f = self.repo.wfile(".hgtags", "w")
87 f = self.repo.wfile(".hgtags", "w")
88 f.write("".join(newlines))
88 f.write("".join(newlines))
89 f.close()
89 f.close()
90 if not oldlines: self.repo.add([".hgtags"])
90 if not oldlines: self.repo.add([".hgtags"])
91 date = "%s 0" % int(time.mktime(time.gmtime()))
91 date = "%s 0" % int(time.mktime(time.gmtime()))
92 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
92 self.repo.rawcommit([".hgtags"], "update tags", "convert-repo",
93 date, self.repo.changelog.tip(), hg.nullid)
93 date, self.repo.changelog.tip(), hg.nullid)
94 return hg.hex(self.repo.changelog.tip())
94 return hg.hex(self.repo.changelog.tip())
General Comments 0
You need to be logged in to leave comments. Login now