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