##// END OF EJS Templates
convert: warn on superfluous / in paths...
Mads Kiilerich -
r11680:7fefef3c default
parent child Browse files
Show More
@@ -1,355 +1,365 b''
1 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
1 # Copyright 2007 Bryan O'Sullivan <bos@serpentine.com>
2 # Copyright 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
2 # Copyright 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
3 #
3 #
4 # This software may be used and distributed according to the terms of the
4 # This software may be used and distributed according to the terms of the
5 # GNU General Public License version 2 or any later version.
5 # GNU General Public License version 2 or any later version.
6
6
7 import shlex
7 import shlex
8 from mercurial.i18n import _
8 from mercurial.i18n import _
9 from mercurial import util
9 from mercurial import util
10 from common import SKIPREV, converter_source
10 from common import SKIPREV, converter_source
11
11
12 def rpairs(name):
12 def rpairs(name):
13 e = len(name)
13 e = len(name)
14 while e != -1:
14 while e != -1:
15 yield name[:e], name[e + 1:]
15 yield name[:e], name[e + 1:]
16 e = name.rfind('/', 0, e)
16 e = name.rfind('/', 0, e)
17 yield '.', name
17 yield '.', name
18
18
19 class filemapper(object):
19 class filemapper(object):
20 '''Map and filter filenames when importing.
20 '''Map and filter filenames when importing.
21 A name can be mapped to itself, a new name, or None (omit from new
21 A name can be mapped to itself, a new name, or None (omit from new
22 repository).'''
22 repository).'''
23
23
24 def __init__(self, ui, path=None):
24 def __init__(self, ui, path=None):
25 self.ui = ui
25 self.ui = ui
26 self.include = {}
26 self.include = {}
27 self.exclude = {}
27 self.exclude = {}
28 self.rename = {}
28 self.rename = {}
29 if path:
29 if path:
30 if self.parse(path):
30 if self.parse(path):
31 raise util.Abort(_('errors in filemap'))
31 raise util.Abort(_('errors in filemap'))
32
32
33 def parse(self, path):
33 def parse(self, path):
34 errs = 0
34 errs = 0
35 def check(name, mapping, listname):
35 def check(name, mapping, listname):
36 if not name:
37 self.ui.warn(_('%s:%d: path to %s is missing\n') %
38 (lex.infile, lex.lineno, listname))
39 return 1
36 if name in mapping:
40 if name in mapping:
37 self.ui.warn(_('%s:%d: %r already in %s list\n') %
41 self.ui.warn(_('%s:%d: %r already in %s list\n') %
38 (lex.infile, lex.lineno, name, listname))
42 (lex.infile, lex.lineno, name, listname))
39 return 1
43 return 1
44 if (name.startswith('/') or
45 name.endswith('/') or
46 '//' in name):
47 self.ui.warn(_('%s:%d: superfluous / in %s %r\n') %
48 (lex.infile, lex.lineno, listname, name))
49 return 1
40 return 0
50 return 0
41 lex = shlex.shlex(open(path), path, True)
51 lex = shlex.shlex(open(path), path, True)
42 lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?'
52 lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?'
43 cmd = lex.get_token()
53 cmd = lex.get_token()
44 while cmd:
54 while cmd:
45 if cmd == 'include':
55 if cmd == 'include':
46 name = lex.get_token()
56 name = lex.get_token()
47 errs += check(name, self.exclude, 'exclude')
57 errs += check(name, self.exclude, 'exclude')
48 self.include[name] = name
58 self.include[name] = name
49 elif cmd == 'exclude':
59 elif cmd == 'exclude':
50 name = lex.get_token()
60 name = lex.get_token()
51 errs += check(name, self.include, 'include')
61 errs += check(name, self.include, 'include')
52 errs += check(name, self.rename, 'rename')
62 errs += check(name, self.rename, 'rename')
53 self.exclude[name] = name
63 self.exclude[name] = name
54 elif cmd == 'rename':
64 elif cmd == 'rename':
55 src = lex.get_token()
65 src = lex.get_token()
56 dest = lex.get_token()
66 dest = lex.get_token()
57 errs += check(src, self.exclude, 'exclude')
67 errs += check(src, self.exclude, 'exclude')
58 self.rename[src] = dest
68 self.rename[src] = dest
59 elif cmd == 'source':
69 elif cmd == 'source':
60 errs += self.parse(lex.get_token())
70 errs += self.parse(lex.get_token())
61 else:
71 else:
62 self.ui.warn(_('%s:%d: unknown directive %r\n') %
72 self.ui.warn(_('%s:%d: unknown directive %r\n') %
63 (lex.infile, lex.lineno, cmd))
73 (lex.infile, lex.lineno, cmd))
64 errs += 1
74 errs += 1
65 cmd = lex.get_token()
75 cmd = lex.get_token()
66 return errs
76 return errs
67
77
68 def lookup(self, name, mapping):
78 def lookup(self, name, mapping):
69 for pre, suf in rpairs(name):
79 for pre, suf in rpairs(name):
70 try:
80 try:
71 return mapping[pre], pre, suf
81 return mapping[pre], pre, suf
72 except KeyError:
82 except KeyError:
73 pass
83 pass
74 return '', name, ''
84 return '', name, ''
75
85
76 def __call__(self, name):
86 def __call__(self, name):
77 if self.include:
87 if self.include:
78 inc = self.lookup(name, self.include)[0]
88 inc = self.lookup(name, self.include)[0]
79 else:
89 else:
80 inc = name
90 inc = name
81 if self.exclude:
91 if self.exclude:
82 exc = self.lookup(name, self.exclude)[0]
92 exc = self.lookup(name, self.exclude)[0]
83 else:
93 else:
84 exc = ''
94 exc = ''
85 if (not self.include and exc) or (len(inc) <= len(exc)):
95 if (not self.include and exc) or (len(inc) <= len(exc)):
86 return None
96 return None
87 newpre, pre, suf = self.lookup(name, self.rename)
97 newpre, pre, suf = self.lookup(name, self.rename)
88 if newpre:
98 if newpre:
89 if newpre == '.':
99 if newpre == '.':
90 return suf
100 return suf
91 if suf:
101 if suf:
92 return newpre + '/' + suf
102 return newpre + '/' + suf
93 return newpre
103 return newpre
94 return name
104 return name
95
105
96 def active(self):
106 def active(self):
97 return bool(self.include or self.exclude or self.rename)
107 return bool(self.include or self.exclude or self.rename)
98
108
99 # This class does two additional things compared to a regular source:
109 # This class does two additional things compared to a regular source:
100 #
110 #
101 # - Filter and rename files. This is mostly wrapped by the filemapper
111 # - Filter and rename files. This is mostly wrapped by the filemapper
102 # class above. We hide the original filename in the revision that is
112 # class above. We hide the original filename in the revision that is
103 # returned by getchanges to be able to find things later in getfile.
113 # returned by getchanges to be able to find things later in getfile.
104 #
114 #
105 # - Return only revisions that matter for the files we're interested in.
115 # - Return only revisions that matter for the files we're interested in.
106 # This involves rewriting the parents of the original revision to
116 # This involves rewriting the parents of the original revision to
107 # create a graph that is restricted to those revisions.
117 # create a graph that is restricted to those revisions.
108 #
118 #
109 # This set of revisions includes not only revisions that directly
119 # This set of revisions includes not only revisions that directly
110 # touch files we're interested in, but also merges that merge two
120 # touch files we're interested in, but also merges that merge two
111 # or more interesting revisions.
121 # or more interesting revisions.
112
122
113 class filemap_source(converter_source):
123 class filemap_source(converter_source):
114 def __init__(self, ui, baseconverter, filemap):
124 def __init__(self, ui, baseconverter, filemap):
115 super(filemap_source, self).__init__(ui)
125 super(filemap_source, self).__init__(ui)
116 self.base = baseconverter
126 self.base = baseconverter
117 self.filemapper = filemapper(ui, filemap)
127 self.filemapper = filemapper(ui, filemap)
118 self.commits = {}
128 self.commits = {}
119 # if a revision rev has parent p in the original revision graph, then
129 # if a revision rev has parent p in the original revision graph, then
120 # rev will have parent self.parentmap[p] in the restricted graph.
130 # rev will have parent self.parentmap[p] in the restricted graph.
121 self.parentmap = {}
131 self.parentmap = {}
122 # self.wantedancestors[rev] is the set of all ancestors of rev that
132 # self.wantedancestors[rev] is the set of all ancestors of rev that
123 # are in the restricted graph.
133 # are in the restricted graph.
124 self.wantedancestors = {}
134 self.wantedancestors = {}
125 self.convertedorder = None
135 self.convertedorder = None
126 self._rebuilt = False
136 self._rebuilt = False
127 self.origparents = {}
137 self.origparents = {}
128 self.children = {}
138 self.children = {}
129 self.seenchildren = {}
139 self.seenchildren = {}
130
140
131 def before(self):
141 def before(self):
132 self.base.before()
142 self.base.before()
133
143
134 def after(self):
144 def after(self):
135 self.base.after()
145 self.base.after()
136
146
137 def setrevmap(self, revmap):
147 def setrevmap(self, revmap):
138 # rebuild our state to make things restartable
148 # rebuild our state to make things restartable
139 #
149 #
140 # To avoid calling getcommit for every revision that has already
150 # To avoid calling getcommit for every revision that has already
141 # been converted, we rebuild only the parentmap, delaying the
151 # been converted, we rebuild only the parentmap, delaying the
142 # rebuild of wantedancestors until we need it (i.e. until a
152 # rebuild of wantedancestors until we need it (i.e. until a
143 # merge).
153 # merge).
144 #
154 #
145 # We assume the order argument lists the revisions in
155 # We assume the order argument lists the revisions in
146 # topological order, so that we can infer which revisions were
156 # topological order, so that we can infer which revisions were
147 # wanted by previous runs.
157 # wanted by previous runs.
148 self._rebuilt = not revmap
158 self._rebuilt = not revmap
149 seen = {SKIPREV: SKIPREV}
159 seen = {SKIPREV: SKIPREV}
150 dummyset = set()
160 dummyset = set()
151 converted = []
161 converted = []
152 for rev in revmap.order:
162 for rev in revmap.order:
153 mapped = revmap[rev]
163 mapped = revmap[rev]
154 wanted = mapped not in seen
164 wanted = mapped not in seen
155 if wanted:
165 if wanted:
156 seen[mapped] = rev
166 seen[mapped] = rev
157 self.parentmap[rev] = rev
167 self.parentmap[rev] = rev
158 else:
168 else:
159 self.parentmap[rev] = seen[mapped]
169 self.parentmap[rev] = seen[mapped]
160 self.wantedancestors[rev] = dummyset
170 self.wantedancestors[rev] = dummyset
161 arg = seen[mapped]
171 arg = seen[mapped]
162 if arg == SKIPREV:
172 if arg == SKIPREV:
163 arg = None
173 arg = None
164 converted.append((rev, wanted, arg))
174 converted.append((rev, wanted, arg))
165 self.convertedorder = converted
175 self.convertedorder = converted
166 return self.base.setrevmap(revmap)
176 return self.base.setrevmap(revmap)
167
177
168 def rebuild(self):
178 def rebuild(self):
169 if self._rebuilt:
179 if self._rebuilt:
170 return True
180 return True
171 self._rebuilt = True
181 self._rebuilt = True
172 self.parentmap.clear()
182 self.parentmap.clear()
173 self.wantedancestors.clear()
183 self.wantedancestors.clear()
174 self.seenchildren.clear()
184 self.seenchildren.clear()
175 for rev, wanted, arg in self.convertedorder:
185 for rev, wanted, arg in self.convertedorder:
176 if rev not in self.origparents:
186 if rev not in self.origparents:
177 self.origparents[rev] = self.getcommit(rev).parents
187 self.origparents[rev] = self.getcommit(rev).parents
178 if arg is not None:
188 if arg is not None:
179 self.children[arg] = self.children.get(arg, 0) + 1
189 self.children[arg] = self.children.get(arg, 0) + 1
180
190
181 for rev, wanted, arg in self.convertedorder:
191 for rev, wanted, arg in self.convertedorder:
182 parents = self.origparents[rev]
192 parents = self.origparents[rev]
183 if wanted:
193 if wanted:
184 self.mark_wanted(rev, parents)
194 self.mark_wanted(rev, parents)
185 else:
195 else:
186 self.mark_not_wanted(rev, arg)
196 self.mark_not_wanted(rev, arg)
187 self._discard(arg, *parents)
197 self._discard(arg, *parents)
188
198
189 return True
199 return True
190
200
191 def getheads(self):
201 def getheads(self):
192 return self.base.getheads()
202 return self.base.getheads()
193
203
194 def getcommit(self, rev):
204 def getcommit(self, rev):
195 # We want to save a reference to the commit objects to be able
205 # We want to save a reference to the commit objects to be able
196 # to rewrite their parents later on.
206 # to rewrite their parents later on.
197 c = self.commits[rev] = self.base.getcommit(rev)
207 c = self.commits[rev] = self.base.getcommit(rev)
198 for p in c.parents:
208 for p in c.parents:
199 self.children[p] = self.children.get(p, 0) + 1
209 self.children[p] = self.children.get(p, 0) + 1
200 return c
210 return c
201
211
202 def _discard(self, *revs):
212 def _discard(self, *revs):
203 for r in revs:
213 for r in revs:
204 if r is None:
214 if r is None:
205 continue
215 continue
206 self.seenchildren[r] = self.seenchildren.get(r, 0) + 1
216 self.seenchildren[r] = self.seenchildren.get(r, 0) + 1
207 if self.seenchildren[r] == self.children[r]:
217 if self.seenchildren[r] == self.children[r]:
208 del self.wantedancestors[r]
218 del self.wantedancestors[r]
209 del self.parentmap[r]
219 del self.parentmap[r]
210 del self.seenchildren[r]
220 del self.seenchildren[r]
211 if self._rebuilt:
221 if self._rebuilt:
212 del self.children[r]
222 del self.children[r]
213
223
214 def wanted(self, rev, i):
224 def wanted(self, rev, i):
215 # Return True if we're directly interested in rev.
225 # Return True if we're directly interested in rev.
216 #
226 #
217 # i is an index selecting one of the parents of rev (if rev
227 # i is an index selecting one of the parents of rev (if rev
218 # has no parents, i is None). getchangedfiles will give us
228 # has no parents, i is None). getchangedfiles will give us
219 # the list of files that are different in rev and in the parent
229 # the list of files that are different in rev and in the parent
220 # indicated by i. If we're interested in any of these files,
230 # indicated by i. If we're interested in any of these files,
221 # we're interested in rev.
231 # we're interested in rev.
222 try:
232 try:
223 files = self.base.getchangedfiles(rev, i)
233 files = self.base.getchangedfiles(rev, i)
224 except NotImplementedError:
234 except NotImplementedError:
225 raise util.Abort(_("source repository doesn't support --filemap"))
235 raise util.Abort(_("source repository doesn't support --filemap"))
226 for f in files:
236 for f in files:
227 if self.filemapper(f):
237 if self.filemapper(f):
228 return True
238 return True
229 return False
239 return False
230
240
231 def mark_not_wanted(self, rev, p):
241 def mark_not_wanted(self, rev, p):
232 # Mark rev as not interesting and update data structures.
242 # Mark rev as not interesting and update data structures.
233
243
234 if p is None:
244 if p is None:
235 # A root revision. Use SKIPREV to indicate that it doesn't
245 # A root revision. Use SKIPREV to indicate that it doesn't
236 # map to any revision in the restricted graph. Put SKIPREV
246 # map to any revision in the restricted graph. Put SKIPREV
237 # in the set of wanted ancestors to simplify code elsewhere
247 # in the set of wanted ancestors to simplify code elsewhere
238 self.parentmap[rev] = SKIPREV
248 self.parentmap[rev] = SKIPREV
239 self.wantedancestors[rev] = set((SKIPREV,))
249 self.wantedancestors[rev] = set((SKIPREV,))
240 return
250 return
241
251
242 # Reuse the data from our parent.
252 # Reuse the data from our parent.
243 self.parentmap[rev] = self.parentmap[p]
253 self.parentmap[rev] = self.parentmap[p]
244 self.wantedancestors[rev] = self.wantedancestors[p]
254 self.wantedancestors[rev] = self.wantedancestors[p]
245
255
246 def mark_wanted(self, rev, parents):
256 def mark_wanted(self, rev, parents):
247 # Mark rev ss wanted and update data structures.
257 # Mark rev ss wanted and update data structures.
248
258
249 # rev will be in the restricted graph, so children of rev in
259 # rev will be in the restricted graph, so children of rev in
250 # the original graph should still have rev as a parent in the
260 # the original graph should still have rev as a parent in the
251 # restricted graph.
261 # restricted graph.
252 self.parentmap[rev] = rev
262 self.parentmap[rev] = rev
253
263
254 # The set of wanted ancestors of rev is the union of the sets
264 # The set of wanted ancestors of rev is the union of the sets
255 # of wanted ancestors of its parents. Plus rev itself.
265 # of wanted ancestors of its parents. Plus rev itself.
256 wrev = set()
266 wrev = set()
257 for p in parents:
267 for p in parents:
258 wrev.update(self.wantedancestors[p])
268 wrev.update(self.wantedancestors[p])
259 wrev.add(rev)
269 wrev.add(rev)
260 self.wantedancestors[rev] = wrev
270 self.wantedancestors[rev] = wrev
261
271
262 def getchanges(self, rev):
272 def getchanges(self, rev):
263 parents = self.commits[rev].parents
273 parents = self.commits[rev].parents
264 if len(parents) > 1:
274 if len(parents) > 1:
265 self.rebuild()
275 self.rebuild()
266
276
267 # To decide whether we're interested in rev we:
277 # To decide whether we're interested in rev we:
268 #
278 #
269 # - calculate what parents rev will have if it turns out we're
279 # - calculate what parents rev will have if it turns out we're
270 # interested in it. If it's going to have more than 1 parent,
280 # interested in it. If it's going to have more than 1 parent,
271 # we're interested in it.
281 # we're interested in it.
272 #
282 #
273 # - otherwise, we'll compare it with the single parent we found.
283 # - otherwise, we'll compare it with the single parent we found.
274 # If any of the files we're interested in is different in the
284 # If any of the files we're interested in is different in the
275 # the two revisions, we're interested in rev.
285 # the two revisions, we're interested in rev.
276
286
277 # A parent p is interesting if its mapped version (self.parentmap[p]):
287 # A parent p is interesting if its mapped version (self.parentmap[p]):
278 # - is not SKIPREV
288 # - is not SKIPREV
279 # - is still not in the list of parents (we don't want duplicates)
289 # - is still not in the list of parents (we don't want duplicates)
280 # - is not an ancestor of the mapped versions of the other parents
290 # - is not an ancestor of the mapped versions of the other parents
281 mparents = []
291 mparents = []
282 wp = None
292 wp = None
283 for i, p1 in enumerate(parents):
293 for i, p1 in enumerate(parents):
284 mp1 = self.parentmap[p1]
294 mp1 = self.parentmap[p1]
285 if mp1 == SKIPREV or mp1 in mparents:
295 if mp1 == SKIPREV or mp1 in mparents:
286 continue
296 continue
287 for p2 in parents:
297 for p2 in parents:
288 if p1 == p2 or mp1 == self.parentmap[p2]:
298 if p1 == p2 or mp1 == self.parentmap[p2]:
289 continue
299 continue
290 if mp1 in self.wantedancestors[p2]:
300 if mp1 in self.wantedancestors[p2]:
291 break
301 break
292 else:
302 else:
293 mparents.append(mp1)
303 mparents.append(mp1)
294 wp = i
304 wp = i
295
305
296 if wp is None and parents:
306 if wp is None and parents:
297 wp = 0
307 wp = 0
298
308
299 self.origparents[rev] = parents
309 self.origparents[rev] = parents
300
310
301 closed = 'close' in self.commits[rev].extra
311 closed = 'close' in self.commits[rev].extra
302
312
303 if len(mparents) < 2 and not closed and not self.wanted(rev, wp):
313 if len(mparents) < 2 and not closed and not self.wanted(rev, wp):
304 # We don't want this revision.
314 # We don't want this revision.
305 # Update our state and tell the convert process to map this
315 # Update our state and tell the convert process to map this
306 # revision to the same revision its parent as mapped to.
316 # revision to the same revision its parent as mapped to.
307 p = None
317 p = None
308 if parents:
318 if parents:
309 p = parents[wp]
319 p = parents[wp]
310 self.mark_not_wanted(rev, p)
320 self.mark_not_wanted(rev, p)
311 self.convertedorder.append((rev, False, p))
321 self.convertedorder.append((rev, False, p))
312 self._discard(*parents)
322 self._discard(*parents)
313 return self.parentmap[rev]
323 return self.parentmap[rev]
314
324
315 # We want this revision.
325 # We want this revision.
316 # Rewrite the parents of the commit object
326 # Rewrite the parents of the commit object
317 self.commits[rev].parents = mparents
327 self.commits[rev].parents = mparents
318 self.mark_wanted(rev, parents)
328 self.mark_wanted(rev, parents)
319 self.convertedorder.append((rev, True, None))
329 self.convertedorder.append((rev, True, None))
320 self._discard(*parents)
330 self._discard(*parents)
321
331
322 # Get the real changes and do the filtering/mapping. To be
332 # Get the real changes and do the filtering/mapping. To be
323 # able to get the files later on in getfile, we hide the
333 # able to get the files later on in getfile, we hide the
324 # original filename in the rev part of the return value.
334 # original filename in the rev part of the return value.
325 changes, copies = self.base.getchanges(rev)
335 changes, copies = self.base.getchanges(rev)
326 newnames = {}
336 newnames = {}
327 files = []
337 files = []
328 for f, r in changes:
338 for f, r in changes:
329 newf = self.filemapper(f)
339 newf = self.filemapper(f)
330 if newf:
340 if newf:
331 files.append((newf, (f, r)))
341 files.append((newf, (f, r)))
332 newnames[f] = newf
342 newnames[f] = newf
333
343
334 ncopies = {}
344 ncopies = {}
335 for c in copies:
345 for c in copies:
336 newc = self.filemapper(c)
346 newc = self.filemapper(c)
337 if newc:
347 if newc:
338 newsource = self.filemapper(copies[c])
348 newsource = self.filemapper(copies[c])
339 if newsource:
349 if newsource:
340 ncopies[newc] = newsource
350 ncopies[newc] = newsource
341
351
342 return files, ncopies
352 return files, ncopies
343
353
344 def getfile(self, name, rev):
354 def getfile(self, name, rev):
345 realname, realrev = rev
355 realname, realrev = rev
346 return self.base.getfile(realname, realrev)
356 return self.base.getfile(realname, realrev)
347
357
348 def gettags(self):
358 def gettags(self):
349 return self.base.gettags()
359 return self.base.gettags()
350
360
351 def hasnativeorder(self):
361 def hasnativeorder(self):
352 return self.base.hasnativeorder()
362 return self.base.hasnativeorder()
353
363
354 def lookuprev(self, rev):
364 def lookuprev(self, rev):
355 return self.base.lookuprev(rev)
365 return self.base.lookuprev(rev)
@@ -1,130 +1,141 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 HGMERGE=true; export HGMERGE
3 HGMERGE=true; export HGMERGE
4
4
5 echo '[extensions]' >> $HGRCPATH
5 echo '[extensions]' >> $HGRCPATH
6 echo 'graphlog =' >> $HGRCPATH
6 echo 'graphlog =' >> $HGRCPATH
7 echo 'convert =' >> $HGRCPATH
7 echo 'convert =' >> $HGRCPATH
8
8
9 glog()
9 glog()
10 {
10 {
11 hg glog --template '{rev} "{desc}" files: {files}\n' "$@"
11 hg glog --template '{rev} "{desc}" files: {files}\n' "$@"
12 }
12 }
13
13
14 hg init source
14 hg init source
15 cd source
15 cd source
16
16
17 echo foo > foo
17 echo foo > foo
18 echo baz > baz
18 echo baz > baz
19 mkdir -p dir/subdir
19 mkdir -p dir/subdir
20 echo dir/file >> dir/file
20 echo dir/file >> dir/file
21 echo dir/file2 >> dir/file2
21 echo dir/file2 >> dir/file2
22 echo dir/subdir/file3 >> dir/subdir/file3
22 echo dir/subdir/file3 >> dir/subdir/file3
23 echo dir/subdir/file4 >> dir/subdir/file4
23 echo dir/subdir/file4 >> dir/subdir/file4
24 hg ci -d '0 0' -qAm '0: add foo baz dir/'
24 hg ci -d '0 0' -qAm '0: add foo baz dir/'
25
25
26 echo bar > bar
26 echo bar > bar
27 echo quux > quux
27 echo quux > quux
28 hg copy foo copied
28 hg copy foo copied
29 hg ci -d '1 0' -qAm '1: add bar quux; copy foo to copied'
29 hg ci -d '1 0' -qAm '1: add bar quux; copy foo to copied'
30
30
31 echo >> foo
31 echo >> foo
32 hg ci -d '2 0' -m '2: change foo'
32 hg ci -d '2 0' -m '2: change foo'
33
33
34 hg up -qC 1
34 hg up -qC 1
35 echo >> bar
35 echo >> bar
36 echo >> quux
36 echo >> quux
37 hg ci -d '3 0' -m '3: change bar quux'
37 hg ci -d '3 0' -m '3: change bar quux'
38
38
39 hg up -qC 2
39 hg up -qC 2
40 hg merge -qr 3
40 hg merge -qr 3
41 echo >> bar
41 echo >> bar
42 echo >> baz
42 echo >> baz
43 hg ci -d '4 0' -m '4: first merge; change bar baz'
43 hg ci -d '4 0' -m '4: first merge; change bar baz'
44
44
45 echo >> bar
45 echo >> bar
46 echo 1 >> baz
46 echo 1 >> baz
47 echo >> quux
47 echo >> quux
48 hg ci -d '5 0' -m '5: change bar baz quux'
48 hg ci -d '5 0' -m '5: change bar baz quux'
49
49
50 hg up -qC 4
50 hg up -qC 4
51 echo >> foo
51 echo >> foo
52 echo 2 >> baz
52 echo 2 >> baz
53 hg ci -d '6 0' -m '6: change foo baz'
53 hg ci -d '6 0' -m '6: change foo baz'
54
54
55 hg up -qC 5
55 hg up -qC 5
56 hg merge -qr 6
56 hg merge -qr 6
57 echo >> bar
57 echo >> bar
58 hg ci -d '7 0' -m '7: second merge; change bar'
58 hg ci -d '7 0' -m '7: second merge; change bar'
59
59
60 echo >> foo
60 echo >> foo
61 hg ci -m '8: change foo'
61 hg ci -m '8: change foo'
62
62
63 glog
63 glog
64
64
65 echo '% final file versions in this repo:'
65 echo '% final file versions in this repo:'
66 hg manifest --debug
66 hg manifest --debug
67 hg debugrename copied
67 hg debugrename copied
68 echo
68 echo
69
69
70 cd ..
70 cd ..
71
71
72 splitrepo()
72 splitrepo()
73 {
73 {
74 msg="$1"
74 msg="$1"
75 files="$2"
75 files="$2"
76 opts=$3
76 opts=$3
77 echo "% $files: $msg"
77 echo "% $files: $msg"
78 prefix=`echo "$files" | sed -e 's/ /-/g'`
78 prefix=`echo "$files" | sed -e 's/ /-/g'`
79 fmap="$prefix.fmap"
79 fmap="$prefix.fmap"
80 repo="$prefix.repo"
80 repo="$prefix.repo"
81 for i in $files; do
81 for i in $files; do
82 echo "include $i" >> "$fmap"
82 echo "include $i" >> "$fmap"
83 done
83 done
84 hg -q convert $opts --filemap "$fmap" --datesort source "$repo"
84 hg -q convert $opts --filemap "$fmap" --datesort source "$repo"
85 hg up -q -R "$repo"
85 hg up -q -R "$repo"
86 glog -R "$repo"
86 glog -R "$repo"
87 hg -R "$repo" manifest --debug
87 hg -R "$repo" manifest --debug
88 }
88 }
89
89
90 splitrepo 'skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd' foo
90 splitrepo 'skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd' foo
91
91
92 splitrepo 'merges are not merges anymore' bar
92 splitrepo 'merges are not merges anymore' bar
93
93
94 splitrepo '1st merge is not a merge anymore; 2nd still is' baz
94 splitrepo '1st merge is not a merge anymore; 2nd still is' baz
95
95
96 splitrepo 'we add additional merges when they are interesting' 'foo quux'
96 splitrepo 'we add additional merges when they are interesting' 'foo quux'
97
97
98 splitrepo 'partial conversion' 'bar quux' '-r 3'
98 splitrepo 'partial conversion' 'bar quux' '-r 3'
99 splitrepo 'complete the partial conversion' 'bar quux'
99 splitrepo 'complete the partial conversion' 'bar quux'
100
100
101 rm -r foo.repo
101 rm -r foo.repo
102 splitrepo 'partial conversion' 'foo' '-r 3'
102 splitrepo 'partial conversion' 'foo' '-r 3'
103 splitrepo 'complete the partial conversion' 'foo'
103 splitrepo 'complete the partial conversion' 'foo'
104
104
105 splitrepo 'copied file; source not included in new repo' copied
105 splitrepo 'copied file; source not included in new repo' copied
106 hg --cwd copied.repo debugrename copied
106 hg --cwd copied.repo debugrename copied
107
107
108 splitrepo 'copied file; source included in new repo' 'foo copied'
108 splitrepo 'copied file; source included in new repo' 'foo copied'
109 hg --cwd foo-copied.repo debugrename copied
109 hg --cwd foo-copied.repo debugrename copied
110
110
111 cat > renames.fmap <<EOF
111 cat > renames.fmap <<EOF
112 include dir
112 include dir
113 exclude dir/file2
113 exclude dir/file2
114 rename dir dir2
114 rename dir dir2
115 include foo
115 include foo
116 include copied
116 include copied
117 rename foo foo2
117 rename foo foo2
118 rename copied copied2
118 rename copied copied2
119 exclude dir/subdir
119 exclude dir/subdir
120 include dir/subdir/file3
120 include dir/subdir/file3
121 EOF
121 EOF
122 hg -q convert --filemap renames.fmap --datesort source renames.repo
122 hg -q convert --filemap renames.fmap --datesort source renames.repo
123 hg up -q -R renames.repo
123 hg up -q -R renames.repo
124 glog -R renames.repo
124 glog -R renames.repo
125 hg -R renames.repo manifest --debug
125 hg -R renames.repo manifest --debug
126 hg --cwd renames.repo debugrename copied2
126 hg --cwd renames.repo debugrename copied2
127 echo 'copied:'
127 echo 'copied:'
128 hg --cwd source cat copied
128 hg --cwd source cat copied
129 echo 'copied2:'
129 echo 'copied2:'
130 hg --cwd renames.repo cat copied2
130 hg --cwd renames.repo cat copied2
131
132 echo % filemap errors
133 cat > errors.fmap <<EOF
134 include dir/ # beware that comments changes error line numbers!
135 exclude /dir
136 rename dir//dir /dir//dir/ "out of sync"
137 include
138 EOF
139 hg -q convert --filemap errors.fmap source errors.repo
140
141 true # happy ending
@@ -1,159 +1,167 b''
1 created new head
1 created new head
2 created new head
2 created new head
3 @ 8 "8: change foo" files: foo
3 @ 8 "8: change foo" files: foo
4 |
4 |
5 o 7 "7: second merge; change bar" files: bar baz
5 o 7 "7: second merge; change bar" files: bar baz
6 |\
6 |\
7 | o 6 "6: change foo baz" files: baz foo
7 | o 6 "6: change foo baz" files: baz foo
8 | |
8 | |
9 o | 5 "5: change bar baz quux" files: bar baz quux
9 o | 5 "5: change bar baz quux" files: bar baz quux
10 |/
10 |/
11 o 4 "4: first merge; change bar baz" files: bar baz
11 o 4 "4: first merge; change bar baz" files: bar baz
12 |\
12 |\
13 | o 3 "3: change bar quux" files: bar quux
13 | o 3 "3: change bar quux" files: bar quux
14 | |
14 | |
15 o | 2 "2: change foo" files: foo
15 o | 2 "2: change foo" files: foo
16 |/
16 |/
17 o 1 "1: add bar quux; copy foo to copied" files: bar copied quux
17 o 1 "1: add bar quux; copy foo to copied" files: bar copied quux
18 |
18 |
19 o 0 "0: add foo baz dir/" files: baz dir/file dir/file2 dir/subdir/file3 dir/subdir/file4 foo
19 o 0 "0: add foo baz dir/" files: baz dir/file dir/file2 dir/subdir/file3 dir/subdir/file4 foo
20
20
21 % final file versions in this repo:
21 % final file versions in this repo:
22 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
22 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
23 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644 baz
23 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644 baz
24 7711d36246cc83e61fb29cd6d4ef394c63f1ceaf 644 copied
24 7711d36246cc83e61fb29cd6d4ef394c63f1ceaf 644 copied
25 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir/file
25 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir/file
26 75e6d3f8328f5f6ace6bf10b98df793416a09dca 644 dir/file2
26 75e6d3f8328f5f6ace6bf10b98df793416a09dca 644 dir/file2
27 5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir/subdir/file3
27 5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir/subdir/file3
28 57a1c1511590f3de52874adfa04effe8a77d64af 644 dir/subdir/file4
28 57a1c1511590f3de52874adfa04effe8a77d64af 644 dir/subdir/file4
29 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
29 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
30 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
30 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
31 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
31 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
32
32
33 % foo: skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd
33 % foo: skip unwanted merges; use 1st parent in 1st merge, 2nd in 2nd
34 @ 3 "8: change foo" files: foo
34 @ 3 "8: change foo" files: foo
35 |
35 |
36 o 2 "6: change foo baz" files: foo
36 o 2 "6: change foo baz" files: foo
37 |
37 |
38 o 1 "2: change foo" files: foo
38 o 1 "2: change foo" files: foo
39 |
39 |
40 o 0 "0: add foo baz dir/" files: foo
40 o 0 "0: add foo baz dir/" files: foo
41
41
42 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
42 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
43 % bar: merges are not merges anymore
43 % bar: merges are not merges anymore
44 @ 4 "7: second merge; change bar" files: bar
44 @ 4 "7: second merge; change bar" files: bar
45 |
45 |
46 o 3 "5: change bar baz quux" files: bar
46 o 3 "5: change bar baz quux" files: bar
47 |
47 |
48 o 2 "4: first merge; change bar baz" files: bar
48 o 2 "4: first merge; change bar baz" files: bar
49 |
49 |
50 o 1 "3: change bar quux" files: bar
50 o 1 "3: change bar quux" files: bar
51 |
51 |
52 o 0 "1: add bar quux; copy foo to copied" files: bar
52 o 0 "1: add bar quux; copy foo to copied" files: bar
53
53
54 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
54 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
55 % baz: 1st merge is not a merge anymore; 2nd still is
55 % baz: 1st merge is not a merge anymore; 2nd still is
56 @ 4 "7: second merge; change bar" files: baz
56 @ 4 "7: second merge; change bar" files: baz
57 |\
57 |\
58 | o 3 "6: change foo baz" files: baz
58 | o 3 "6: change foo baz" files: baz
59 | |
59 | |
60 o | 2 "5: change bar baz quux" files: baz
60 o | 2 "5: change bar baz quux" files: baz
61 |/
61 |/
62 o 1 "4: first merge; change bar baz" files: baz
62 o 1 "4: first merge; change bar baz" files: baz
63 |
63 |
64 o 0 "0: add foo baz dir/" files: baz
64 o 0 "0: add foo baz dir/" files: baz
65
65
66 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644 baz
66 94c1be4dfde2ee8d78db8bbfcf81210813307c3d 644 baz
67 % foo quux: we add additional merges when they are interesting
67 % foo quux: we add additional merges when they are interesting
68 @ 8 "8: change foo" files: foo
68 @ 8 "8: change foo" files: foo
69 |
69 |
70 o 7 "7: second merge; change bar" files:
70 o 7 "7: second merge; change bar" files:
71 |\
71 |\
72 | o 6 "6: change foo baz" files: foo
72 | o 6 "6: change foo baz" files: foo
73 | |
73 | |
74 o | 5 "5: change bar baz quux" files: quux
74 o | 5 "5: change bar baz quux" files: quux
75 |/
75 |/
76 o 4 "4: first merge; change bar baz" files:
76 o 4 "4: first merge; change bar baz" files:
77 |\
77 |\
78 | o 3 "3: change bar quux" files: quux
78 | o 3 "3: change bar quux" files: quux
79 | |
79 | |
80 o | 2 "2: change foo" files: foo
80 o | 2 "2: change foo" files: foo
81 |/
81 |/
82 o 1 "1: add bar quux; copy foo to copied" files: quux
82 o 1 "1: add bar quux; copy foo to copied" files: quux
83 |
83 |
84 o 0 "0: add foo baz dir/" files: foo
84 o 0 "0: add foo baz dir/" files: foo
85
85
86 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
86 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
87 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
87 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
88 % bar quux: partial conversion
88 % bar quux: partial conversion
89 @ 1 "3: change bar quux" files: bar quux
89 @ 1 "3: change bar quux" files: bar quux
90 |
90 |
91 o 0 "1: add bar quux; copy foo to copied" files: bar quux
91 o 0 "1: add bar quux; copy foo to copied" files: bar quux
92
92
93 b79105bedc55102f394e90a789c9c380117c1b4a 644 bar
93 b79105bedc55102f394e90a789c9c380117c1b4a 644 bar
94 db0421cc6b685a458c8d86c7d5c004f94429ea23 644 quux
94 db0421cc6b685a458c8d86c7d5c004f94429ea23 644 quux
95 % bar quux: complete the partial conversion
95 % bar quux: complete the partial conversion
96 @ 4 "7: second merge; change bar" files: bar
96 @ 4 "7: second merge; change bar" files: bar
97 |
97 |
98 o 3 "5: change bar baz quux" files: bar quux
98 o 3 "5: change bar baz quux" files: bar quux
99 |
99 |
100 o 2 "4: first merge; change bar baz" files: bar
100 o 2 "4: first merge; change bar baz" files: bar
101 |
101 |
102 o 1 "3: change bar quux" files: bar quux
102 o 1 "3: change bar quux" files: bar quux
103 |
103 |
104 o 0 "1: add bar quux; copy foo to copied" files: bar quux
104 o 0 "1: add bar quux; copy foo to copied" files: bar quux
105
105
106 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
106 9463f52fe115e377cf2878d4fc548117211063f2 644 bar
107 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
107 bc3eca3f47023a3e70ca0d8cc95a22a6827db19d 644 quux
108 % foo: partial conversion
108 % foo: partial conversion
109 @ 0 "0: add foo baz dir/" files: foo
109 @ 0 "0: add foo baz dir/" files: foo
110
110
111 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
111 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 foo
112 % foo: complete the partial conversion
112 % foo: complete the partial conversion
113 @ 3 "8: change foo" files: foo
113 @ 3 "8: change foo" files: foo
114 |
114 |
115 o 2 "6: change foo baz" files: foo
115 o 2 "6: change foo baz" files: foo
116 |
116 |
117 o 1 "2: change foo" files: foo
117 o 1 "2: change foo" files: foo
118 |
118 |
119 o 0 "0: add foo baz dir/" files: foo
119 o 0 "0: add foo baz dir/" files: foo
120
120
121 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
121 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
122 % copied: copied file; source not included in new repo
122 % copied: copied file; source not included in new repo
123 @ 0 "1: add bar quux; copy foo to copied" files: copied
123 @ 0 "1: add bar quux; copy foo to copied" files: copied
124
124
125 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 copied
125 2ed2a3912a0b24502043eae84ee4b279c18b90dd 644 copied
126 copied not renamed
126 copied not renamed
127 % foo copied: copied file; source included in new repo
127 % foo copied: copied file; source included in new repo
128 @ 4 "8: change foo" files: foo
128 @ 4 "8: change foo" files: foo
129 |
129 |
130 o 3 "6: change foo baz" files: foo
130 o 3 "6: change foo baz" files: foo
131 |
131 |
132 o 2 "2: change foo" files: foo
132 o 2 "2: change foo" files: foo
133 |
133 |
134 o 1 "1: add bar quux; copy foo to copied" files: copied
134 o 1 "1: add bar quux; copy foo to copied" files: copied
135 |
135 |
136 o 0 "0: add foo baz dir/" files: foo
136 o 0 "0: add foo baz dir/" files: foo
137
137
138 7711d36246cc83e61fb29cd6d4ef394c63f1ceaf 644 copied
138 7711d36246cc83e61fb29cd6d4ef394c63f1ceaf 644 copied
139 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
139 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo
140 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
140 copied renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
141 @ 4 "8: change foo" files: foo2
141 @ 4 "8: change foo" files: foo2
142 |
142 |
143 o 3 "6: change foo baz" files: foo2
143 o 3 "6: change foo baz" files: foo2
144 |
144 |
145 o 2 "2: change foo" files: foo2
145 o 2 "2: change foo" files: foo2
146 |
146 |
147 o 1 "1: add bar quux; copy foo to copied" files: copied2
147 o 1 "1: add bar quux; copy foo to copied" files: copied2
148 |
148 |
149 o 0 "0: add foo baz dir/" files: dir2/file dir2/subdir/file3 foo2
149 o 0 "0: add foo baz dir/" files: dir2/file dir2/subdir/file3 foo2
150
150
151 d43feacba7a4f1f2080dde4a4b985bd8a0236d46 644 copied2
151 d43feacba7a4f1f2080dde4a4b985bd8a0236d46 644 copied2
152 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir2/file
152 3e20847584beff41d7cd16136b7331ab3d754be0 644 dir2/file
153 5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir2/subdir/file3
153 5fe139720576e18e34bcc9f79174db8897c8afe9 644 dir2/subdir/file3
154 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo2
154 9a7b52012991e4873687192c3e17e61ba3e837a3 644 foo2
155 copied2 renamed from foo2:2ed2a3912a0b24502043eae84ee4b279c18b90dd
155 copied2 renamed from foo2:2ed2a3912a0b24502043eae84ee4b279c18b90dd
156 copied:
156 copied:
157 foo
157 foo
158 copied2:
158 copied2:
159 foo
159 foo
160 % filemap errors
161 errors.fmap:1: superfluous / in exclude 'dir/'
162 errors.fmap:3: superfluous / in include '/dir'
163 errors.fmap:3: superfluous / in rename '/dir'
164 errors.fmap:3: superfluous / in exclude 'dir//dir'
165 errors.fmap:4: unknown directive 'out of sync'
166 errors.fmap:5: path to exclude is missing
167 abort: errors in filemap
General Comments 0
You need to be logged in to leave comments. Login now