##// END OF EJS Templates
convert: update source shamap when using filemap, just as when not using filemap...
Mads Kiilerich -
r19892:77872b00 default
parent child Browse files
Show More
@@ -1,411 +1,414 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 posixpath
8 8 import shlex
9 9 from mercurial.i18n import _
10 10 from mercurial import util, error
11 11 from common import SKIPREV, converter_source
12 12
13 13 def rpairs(name):
14 14 e = len(name)
15 15 while e != -1:
16 16 yield name[:e], name[e + 1:]
17 17 e = name.rfind('/', 0, e)
18 18 yield '.', name
19 19
20 20 def normalize(path):
21 21 ''' We use posixpath.normpath to support cross-platform path format.
22 22 However, it doesn't handle None input. So we wrap it up. '''
23 23 if path is None:
24 24 return None
25 25 return posixpath.normpath(path)
26 26
27 27 class filemapper(object):
28 28 '''Map and filter filenames when importing.
29 29 A name can be mapped to itself, a new name, or None (omit from new
30 30 repository).'''
31 31
32 32 def __init__(self, ui, path=None):
33 33 self.ui = ui
34 34 self.include = {}
35 35 self.exclude = {}
36 36 self.rename = {}
37 37 if path:
38 38 if self.parse(path):
39 39 raise util.Abort(_('errors in filemap'))
40 40
41 41 def parse(self, path):
42 42 errs = 0
43 43 def check(name, mapping, listname):
44 44 if not name:
45 45 self.ui.warn(_('%s:%d: path to %s is missing\n') %
46 46 (lex.infile, lex.lineno, listname))
47 47 return 1
48 48 if name in mapping:
49 49 self.ui.warn(_('%s:%d: %r already in %s list\n') %
50 50 (lex.infile, lex.lineno, name, listname))
51 51 return 1
52 52 if (name.startswith('/') or
53 53 name.endswith('/') or
54 54 '//' in name):
55 55 self.ui.warn(_('%s:%d: superfluous / in %s %r\n') %
56 56 (lex.infile, lex.lineno, listname, name))
57 57 return 1
58 58 return 0
59 59 lex = shlex.shlex(open(path), path, True)
60 60 lex.wordchars += '!@#$%^&*()-=+[]{}|;:,./<>?'
61 61 cmd = lex.get_token()
62 62 while cmd:
63 63 if cmd == 'include':
64 64 name = normalize(lex.get_token())
65 65 errs += check(name, self.exclude, 'exclude')
66 66 self.include[name] = name
67 67 elif cmd == 'exclude':
68 68 name = normalize(lex.get_token())
69 69 errs += check(name, self.include, 'include')
70 70 errs += check(name, self.rename, 'rename')
71 71 self.exclude[name] = name
72 72 elif cmd == 'rename':
73 73 src = normalize(lex.get_token())
74 74 dest = normalize(lex.get_token())
75 75 errs += check(src, self.exclude, 'exclude')
76 76 self.rename[src] = dest
77 77 elif cmd == 'source':
78 78 errs += self.parse(normalize(lex.get_token()))
79 79 else:
80 80 self.ui.warn(_('%s:%d: unknown directive %r\n') %
81 81 (lex.infile, lex.lineno, cmd))
82 82 errs += 1
83 83 cmd = lex.get_token()
84 84 return errs
85 85
86 86 def lookup(self, name, mapping):
87 87 name = normalize(name)
88 88 for pre, suf in rpairs(name):
89 89 try:
90 90 return mapping[pre], pre, suf
91 91 except KeyError:
92 92 pass
93 93 return '', name, ''
94 94
95 95 def __call__(self, name):
96 96 if self.include:
97 97 inc = self.lookup(name, self.include)[0]
98 98 else:
99 99 inc = name
100 100 if self.exclude:
101 101 exc = self.lookup(name, self.exclude)[0]
102 102 else:
103 103 exc = ''
104 104 if (not self.include and exc) or (len(inc) <= len(exc)):
105 105 return None
106 106 newpre, pre, suf = self.lookup(name, self.rename)
107 107 if newpre:
108 108 if newpre == '.':
109 109 return suf
110 110 if suf:
111 111 if newpre.endswith('/'):
112 112 return newpre + suf
113 113 return newpre + '/' + suf
114 114 return newpre
115 115 return name
116 116
117 117 def active(self):
118 118 return bool(self.include or self.exclude or self.rename)
119 119
120 120 # This class does two additional things compared to a regular source:
121 121 #
122 122 # - Filter and rename files. This is mostly wrapped by the filemapper
123 123 # class above. We hide the original filename in the revision that is
124 124 # returned by getchanges to be able to find things later in getfile.
125 125 #
126 126 # - Return only revisions that matter for the files we're interested in.
127 127 # This involves rewriting the parents of the original revision to
128 128 # create a graph that is restricted to those revisions.
129 129 #
130 130 # This set of revisions includes not only revisions that directly
131 131 # touch files we're interested in, but also merges that merge two
132 132 # or more interesting revisions.
133 133
134 134 class filemap_source(converter_source):
135 135 def __init__(self, ui, baseconverter, filemap):
136 136 super(filemap_source, self).__init__(ui)
137 137 self.base = baseconverter
138 138 self.filemapper = filemapper(ui, filemap)
139 139 self.commits = {}
140 140 # if a revision rev has parent p in the original revision graph, then
141 141 # rev will have parent self.parentmap[p] in the restricted graph.
142 142 self.parentmap = {}
143 143 # self.wantedancestors[rev] is the set of all ancestors of rev that
144 144 # are in the restricted graph.
145 145 self.wantedancestors = {}
146 146 self.convertedorder = None
147 147 self._rebuilt = False
148 148 self.origparents = {}
149 149 self.children = {}
150 150 self.seenchildren = {}
151 151
152 152 def before(self):
153 153 self.base.before()
154 154
155 155 def after(self):
156 156 self.base.after()
157 157
158 158 def setrevmap(self, revmap):
159 159 # rebuild our state to make things restartable
160 160 #
161 161 # To avoid calling getcommit for every revision that has already
162 162 # been converted, we rebuild only the parentmap, delaying the
163 163 # rebuild of wantedancestors until we need it (i.e. until a
164 164 # merge).
165 165 #
166 166 # We assume the order argument lists the revisions in
167 167 # topological order, so that we can infer which revisions were
168 168 # wanted by previous runs.
169 169 self._rebuilt = not revmap
170 170 seen = {SKIPREV: SKIPREV}
171 171 dummyset = set()
172 172 converted = []
173 173 for rev in revmap.order:
174 174 mapped = revmap[rev]
175 175 wanted = mapped not in seen
176 176 if wanted:
177 177 seen[mapped] = rev
178 178 self.parentmap[rev] = rev
179 179 else:
180 180 self.parentmap[rev] = seen[mapped]
181 181 self.wantedancestors[rev] = dummyset
182 182 arg = seen[mapped]
183 183 if arg == SKIPREV:
184 184 arg = None
185 185 converted.append((rev, wanted, arg))
186 186 self.convertedorder = converted
187 187 return self.base.setrevmap(revmap)
188 188
189 189 def rebuild(self):
190 190 if self._rebuilt:
191 191 return True
192 192 self._rebuilt = True
193 193 self.parentmap.clear()
194 194 self.wantedancestors.clear()
195 195 self.seenchildren.clear()
196 196 for rev, wanted, arg in self.convertedorder:
197 197 if rev not in self.origparents:
198 198 try:
199 199 self.origparents[rev] = self.getcommit(rev).parents
200 200 except error.RepoLookupError:
201 201 self.ui.debug("unknown revmap source: %s\n" % rev)
202 202 continue
203 203 if arg is not None:
204 204 self.children[arg] = self.children.get(arg, 0) + 1
205 205
206 206 for rev, wanted, arg in self.convertedorder:
207 207 try:
208 208 parents = self.origparents[rev]
209 209 except KeyError:
210 210 continue # unknown revmap source
211 211 if wanted:
212 212 self.mark_wanted(rev, parents)
213 213 else:
214 214 self.mark_not_wanted(rev, arg)
215 215 self._discard(arg, *parents)
216 216
217 217 return True
218 218
219 219 def getheads(self):
220 220 return self.base.getheads()
221 221
222 222 def getcommit(self, rev):
223 223 # We want to save a reference to the commit objects to be able
224 224 # to rewrite their parents later on.
225 225 c = self.commits[rev] = self.base.getcommit(rev)
226 226 for p in c.parents:
227 227 self.children[p] = self.children.get(p, 0) + 1
228 228 return c
229 229
230 230 def _cachedcommit(self, rev):
231 231 if rev in self.commits:
232 232 return self.commits[rev]
233 233 return self.base.getcommit(rev)
234 234
235 235 def _discard(self, *revs):
236 236 for r in revs:
237 237 if r is None:
238 238 continue
239 239 self.seenchildren[r] = self.seenchildren.get(r, 0) + 1
240 240 if self.seenchildren[r] == self.children[r]:
241 241 self.wantedancestors.pop(r, None)
242 242 self.parentmap.pop(r, None)
243 243 del self.seenchildren[r]
244 244 if self._rebuilt:
245 245 del self.children[r]
246 246
247 247 def wanted(self, rev, i):
248 248 # Return True if we're directly interested in rev.
249 249 #
250 250 # i is an index selecting one of the parents of rev (if rev
251 251 # has no parents, i is None). getchangedfiles will give us
252 252 # the list of files that are different in rev and in the parent
253 253 # indicated by i. If we're interested in any of these files,
254 254 # we're interested in rev.
255 255 try:
256 256 files = self.base.getchangedfiles(rev, i)
257 257 except NotImplementedError:
258 258 raise util.Abort(_("source repository doesn't support --filemap"))
259 259 for f in files:
260 260 if self.filemapper(f):
261 261 return True
262 262 return False
263 263
264 264 def mark_not_wanted(self, rev, p):
265 265 # Mark rev as not interesting and update data structures.
266 266
267 267 if p is None:
268 268 # A root revision. Use SKIPREV to indicate that it doesn't
269 269 # map to any revision in the restricted graph. Put SKIPREV
270 270 # in the set of wanted ancestors to simplify code elsewhere
271 271 self.parentmap[rev] = SKIPREV
272 272 self.wantedancestors[rev] = set((SKIPREV,))
273 273 return
274 274
275 275 # Reuse the data from our parent.
276 276 self.parentmap[rev] = self.parentmap[p]
277 277 self.wantedancestors[rev] = self.wantedancestors[p]
278 278
279 279 def mark_wanted(self, rev, parents):
280 280 # Mark rev ss wanted and update data structures.
281 281
282 282 # rev will be in the restricted graph, so children of rev in
283 283 # the original graph should still have rev as a parent in the
284 284 # restricted graph.
285 285 self.parentmap[rev] = rev
286 286
287 287 # The set of wanted ancestors of rev is the union of the sets
288 288 # of wanted ancestors of its parents. Plus rev itself.
289 289 wrev = set()
290 290 for p in parents:
291 291 if p in self.wantedancestors:
292 292 wrev.update(self.wantedancestors[p])
293 293 else:
294 294 self.ui.warn(_('warning: %s parent %s is missing\n') %
295 295 (rev, p))
296 296 wrev.add(rev)
297 297 self.wantedancestors[rev] = wrev
298 298
299 299 def getchanges(self, rev):
300 300 parents = self.commits[rev].parents
301 301 if len(parents) > 1:
302 302 self.rebuild()
303 303
304 304 # To decide whether we're interested in rev we:
305 305 #
306 306 # - calculate what parents rev will have if it turns out we're
307 307 # interested in it. If it's going to have more than 1 parent,
308 308 # we're interested in it.
309 309 #
310 310 # - otherwise, we'll compare it with the single parent we found.
311 311 # If any of the files we're interested in is different in the
312 312 # the two revisions, we're interested in rev.
313 313
314 314 # A parent p is interesting if its mapped version (self.parentmap[p]):
315 315 # - is not SKIPREV
316 316 # - is still not in the list of parents (we don't want duplicates)
317 317 # - is not an ancestor of the mapped versions of the other parents or
318 318 # there is no parent in the same branch than the current revision.
319 319 mparents = []
320 320 knownparents = set()
321 321 branch = self.commits[rev].branch
322 322 hasbranchparent = False
323 323 for i, p1 in enumerate(parents):
324 324 mp1 = self.parentmap[p1]
325 325 if mp1 == SKIPREV or mp1 in knownparents:
326 326 continue
327 327 isancestor = util.any(p2 for p2 in parents
328 328 if p1 != p2 and mp1 != self.parentmap[p2]
329 329 and mp1 in self.wantedancestors[p2])
330 330 if not isancestor and not hasbranchparent and len(parents) > 1:
331 331 # This could be expensive, avoid unnecessary calls.
332 332 if self._cachedcommit(p1).branch == branch:
333 333 hasbranchparent = True
334 334 mparents.append((p1, mp1, i, isancestor))
335 335 knownparents.add(mp1)
336 336 # Discard parents ancestors of other parents if there is a
337 337 # non-ancestor one on the same branch than current revision.
338 338 if hasbranchparent:
339 339 mparents = [p for p in mparents if not p[3]]
340 340 wp = None
341 341 if mparents:
342 342 wp = max(p[2] for p in mparents)
343 343 mparents = [p[1] for p in mparents]
344 344 elif parents:
345 345 wp = 0
346 346
347 347 self.origparents[rev] = parents
348 348
349 349 closed = False
350 350 if 'close' in self.commits[rev].extra:
351 351 # A branch closing revision is only useful if one of its
352 352 # parents belong to the branch being closed
353 353 pbranches = [self._cachedcommit(p).branch for p in mparents]
354 354 if branch in pbranches:
355 355 closed = True
356 356
357 357 if len(mparents) < 2 and not closed and not self.wanted(rev, wp):
358 358 # We don't want this revision.
359 359 # Update our state and tell the convert process to map this
360 360 # revision to the same revision its parent as mapped to.
361 361 p = None
362 362 if parents:
363 363 p = parents[wp]
364 364 self.mark_not_wanted(rev, p)
365 365 self.convertedorder.append((rev, False, p))
366 366 self._discard(*parents)
367 367 return self.parentmap[rev]
368 368
369 369 # We want this revision.
370 370 # Rewrite the parents of the commit object
371 371 self.commits[rev].parents = mparents
372 372 self.mark_wanted(rev, parents)
373 373 self.convertedorder.append((rev, True, None))
374 374 self._discard(*parents)
375 375
376 376 # Get the real changes and do the filtering/mapping. To be
377 377 # able to get the files later on in getfile, we hide the
378 378 # original filename in the rev part of the return value.
379 379 changes, copies = self.base.getchanges(rev)
380 380 files = {}
381 381 for f, r in changes:
382 382 newf = self.filemapper(f)
383 383 if newf and (newf != f or newf not in files):
384 384 files[newf] = (f, r)
385 385 files = sorted(files.items())
386 386
387 387 ncopies = {}
388 388 for c in copies:
389 389 newc = self.filemapper(c)
390 390 if newc:
391 391 newsource = self.filemapper(copies[c])
392 392 if newsource:
393 393 ncopies[newc] = newsource
394 394
395 395 return files, ncopies
396 396
397 397 def getfile(self, name, rev):
398 398 realname, realrev = rev
399 399 return self.base.getfile(realname, realrev)
400 400
401 401 def gettags(self):
402 402 return self.base.gettags()
403 403
404 404 def hasnativeorder(self):
405 405 return self.base.hasnativeorder()
406 406
407 407 def lookuprev(self, rev):
408 408 return self.base.lookuprev(rev)
409 409
410 410 def getbookmarks(self):
411 411 return self.base.getbookmarks()
412
413 def converted(self, rev, sinkrev):
414 self.base.converted(rev, sinkrev)
@@ -1,124 +1,392 b''
1 1
2 2 $ cat >> $HGRCPATH <<EOF
3 3 > [extensions]
4 4 > convert=
5 5 > [convert]
6 6 > hg.saverev=False
7 7 > EOF
8 8 $ hg init orig
9 9 $ cd orig
10 10 $ echo foo > foo
11 11 $ echo bar > bar
12 12 $ hg ci -qAm 'add foo and bar'
13 13 $ hg rm foo
14 14 $ hg ci -m 'remove foo'
15 15 $ mkdir foo
16 16 $ echo file > foo/file
17 17 $ hg ci -qAm 'add foo/file'
18 18 $ hg tag some-tag
19 19 $ hg log
20 20 changeset: 3:593cbf6fb2b4
21 21 tag: tip
22 22 user: test
23 23 date: Thu Jan 01 00:00:00 1970 +0000
24 24 summary: Added tag some-tag for changeset ad681a868e44
25 25
26 26 changeset: 2:ad681a868e44
27 27 tag: some-tag
28 28 user: test
29 29 date: Thu Jan 01 00:00:00 1970 +0000
30 30 summary: add foo/file
31 31
32 32 changeset: 1:cbba8ecc03b7
33 33 user: test
34 34 date: Thu Jan 01 00:00:00 1970 +0000
35 35 summary: remove foo
36 36
37 37 changeset: 0:327daa9251fa
38 38 user: test
39 39 date: Thu Jan 01 00:00:00 1970 +0000
40 40 summary: add foo and bar
41 41
42 42 $ cd ..
43 43 $ hg convert orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
44 44 initializing destination new repository
45 45 scanning source...
46 46 sorting...
47 47 converting...
48 48 3 add foo and bar
49 49 2 remove foo
50 50 1 add foo/file
51 51 0 Added tag some-tag for changeset ad681a868e44
52 52 $ cd new
53 53 $ hg out ../orig
54 54 comparing with ../orig
55 55 searching for changes
56 56 no changes found
57 57 [1]
58 58
59 59 dirstate should be empty:
60 60
61 61 $ hg debugstate
62 62 $ hg parents -q
63 63 $ hg up -C
64 64 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 65 $ hg copy bar baz
66 66
67 67 put something in the dirstate:
68 68
69 69 $ hg debugstate > debugstate
70 70 $ grep baz debugstate
71 71 a 0 -1 unset baz
72 72 copy: bar -> baz
73 73
74 74 add a new revision in the original repo
75 75
76 76 $ cd ../orig
77 77 $ echo baz > baz
78 78 $ hg ci -qAm 'add baz'
79 79 $ cd ..
80 80 $ hg convert orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
81 81 scanning source...
82 82 sorting...
83 83 converting...
84 84 0 add baz
85 85 $ cd new
86 86 $ hg out ../orig
87 87 comparing with ../orig
88 88 searching for changes
89 89 no changes found
90 90 [1]
91 91
92 92 dirstate should be the same (no output below):
93 93
94 94 $ hg debugstate > new-debugstate
95 95 $ diff debugstate new-debugstate
96 96
97 97 no copies
98 98
99 99 $ hg up -C
100 100 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 101 $ hg debugrename baz
102 102 baz not renamed
103 103 $ cd ..
104 104
105 105 test tag rewriting
106 106
107 107 $ cat > filemap <<EOF
108 108 > exclude foo
109 109 > EOF
110 110 $ hg convert --filemap filemap orig new-filemap 2>&1 | grep -v 'subversion python bindings could not be loaded'
111 111 initializing destination new-filemap repository
112 112 scanning source...
113 113 sorting...
114 114 converting...
115 115 4 add foo and bar
116 116 3 remove foo
117 117 2 add foo/file
118 118 1 Added tag some-tag for changeset ad681a868e44
119 119 0 add baz
120 120 $ cd new-filemap
121 121 $ hg tags
122 122 tip 2:6f4fd1df87fb
123 123 some-tag 0:ba8636729451
124 124 $ cd ..
125
126
127 Test cases for hg-hg roundtrip
128
129 Helper
130
131 $ glog()
132 > {
133 > hg log -G --template '{rev} {node|short} "{desc}" files: {files}\n' $*
134 > }
135
136 Create a tricky source repo
137
138 $ hg init source
139 $ cd source
140
141 $ echo 0 > 0
142 $ hg ci -Aqm '0: add 0'
143 $ echo a > a
144 $ mkdir dir
145 $ echo b > dir/b
146 $ hg ci -qAm '1: add a and dir/b'
147 $ echo c > dir/c
148 $ hg ci -qAm '2: add dir/c'
149 $ hg copy a e
150 $ echo b >> b
151 $ hg ci -qAm '3: copy a to e, change b'
152 $ hg up -qr -3
153 $ echo a >> a
154 $ hg ci -qAm '4: change a'
155 $ hg merge
156 merging a and e to e
157 2 files updated, 1 files merged, 0 files removed, 0 files unresolved
158 (branch merge, don't forget to commit)
159 $ hg copy b dir/d
160 $ hg ci -qAm '5: merge 2 and 3, copy b to dir/d'
161 $ echo a >> a
162 $ hg ci -qAm '6: change a'
163
164 $ hg mani
165 0
166 a
167 b
168 dir/b
169 dir/c
170 dir/d
171 e
172 $ glog
173 @ 6 0613c8e59a3d "6: change a" files: a
174 |
175 o 5 717e9b37cdb7 "5: merge 2 and 3, copy b to dir/d" files: dir/d e
176 |\
177 | o 4 86a55cb968d5 "4: change a" files: a
178 | |
179 o | 3 0e6e235919dd "3: copy a to e, change b" files: b e
180 | |
181 o | 2 0394b0d5e4f7 "2: add dir/c" files: dir/c
182 |/
183 o 1 333546584845 "1: add a and dir/b" files: a dir/b
184 |
185 o 0 d1a24e2ebd23 "0: add 0" files: 0
186
187 $ cd ..
188
189 Convert excluding rev 0 and dir/ (and thus rev2):
190
191 $ cat << EOF > filemap
192 > exclude dir
193 > EOF
194
195 $ hg convert --filemap filemap source dest --config convert.hg.revs=1::
196 initializing destination dest repository
197 scanning source...
198 sorting...
199 converting...
200 5 1: add a and dir/b
201 4 2: add dir/c
202 3 3: copy a to e, change b
203 2 4: change a
204 1 5: merge 2 and 3, copy b to dir/d
205 0 6: change a
206
207 Verify that conversion skipped rev 2:
208
209 $ glog -R dest
210 o 4 78814e84a217 "6: change a" files: a
211 |
212 o 3 f7cff662c5e5 "5: merge 2 and 3, copy b to dir/d" files: e
213 |\
214 | o 2 ab40a95b0072 "4: change a" files: a
215 | |
216 o | 1 bd51f17597bf "3: copy a to e, change b" files: b e
217 |/
218 o 0 a4a1dae0fe35 "1: add a and dir/b" files: 0 a
219
220
221 Verify mapping correct in both directions:
222
223 $ cat source/.hg/shamap
224 a4a1dae0fe3514cefd9b8541b7abbc8f44f946d5 333546584845f70c4cfecb992341aaef0e708166
225 bd51f17597bf32268e68a560b206898c3960cda2 0e6e235919dd8e9285ba8eb5adf703af9ad99378
226 ab40a95b00725307e79c2fd271000aa8af9759f4 86a55cb968d51770cba2a1630d6cc637b574580a
227 f7cff662c5e581e6f3f1a85ffdd2bcb35825f6ba 717e9b37cdb7eb9917ca8e30aa3f986e6d5b177d
228 78814e84a217894517c2de392b903ed05e6871a4 0613c8e59a3ddb9789072ef52f1ed13496489bb4
229 $ cat dest/.hg/shamap
230 333546584845f70c4cfecb992341aaef0e708166 a4a1dae0fe3514cefd9b8541b7abbc8f44f946d5
231 0394b0d5e4f761ced559fd0bbdc6afc16cb3f7d1 a4a1dae0fe3514cefd9b8541b7abbc8f44f946d5
232 0e6e235919dd8e9285ba8eb5adf703af9ad99378 bd51f17597bf32268e68a560b206898c3960cda2
233 86a55cb968d51770cba2a1630d6cc637b574580a ab40a95b00725307e79c2fd271000aa8af9759f4
234 717e9b37cdb7eb9917ca8e30aa3f986e6d5b177d f7cff662c5e581e6f3f1a85ffdd2bcb35825f6ba
235 0613c8e59a3ddb9789072ef52f1ed13496489bb4 78814e84a217894517c2de392b903ed05e6871a4
236
237 Verify meta data converted correctly:
238
239 $ hg -R dest log -r 1 --debug -p --git
240 changeset: 1:bd51f17597bf32268e68a560b206898c3960cda2
241 phase: draft
242 parent: 0:a4a1dae0fe3514cefd9b8541b7abbc8f44f946d5
243 parent: -1:0000000000000000000000000000000000000000
244 manifest: 1:040c72ed9b101773c24ac314776bfc846943781f
245 user: test
246 date: Thu Jan 01 00:00:00 1970 +0000
247 files+: b e
248 extra: branch=default
249 description:
250 3: copy a to e, change b
251
252
253 diff --git a/b b/b
254 new file mode 100644
255 --- /dev/null
256 +++ b/b
257 @@ -0,0 +1,1 @@
258 +b
259 diff --git a/a b/e
260 copy from a
261 copy to e
262
263 Verify files included and excluded correctly:
264
265 $ hg -R dest manifest -r tip
266 0
267 a
268 b
269 e
270
271
272 Make changes in dest and convert back:
273
274 $ hg -R dest up -q
275 $ echo dest > dest/dest
276 $ hg -R dest ci -Aqm 'change in dest'
277 $ hg -R dest tip
278 changeset: 5:a2e0e3cc6d1d
279 tag: tip
280 user: test
281 date: Thu Jan 01 00:00:00 1970 +0000
282 summary: change in dest
283
284
285 (converting merges back after using a filemap will probably cause chaos so we
286 exclude merges.)
287
288 $ hg convert dest source --config convert.hg.revs='!merge()'
289 scanning source...
290 sorting...
291 converting...
292 0 change in dest
293
294 Verify the conversion back:
295
296 $ hg -R source log --debug -r tip
297 changeset: 7:e6d364a69ff1248b2099e603b0c145504cade6f0
298 tag: tip
299 phase: draft
300 parent: 6:0613c8e59a3ddb9789072ef52f1ed13496489bb4
301 parent: -1:0000000000000000000000000000000000000000
302 manifest: 7:aa3e9542f3b76d4f1f1b2e9c7ce9dbb48b6a95ec
303 user: test
304 date: Thu Jan 01 00:00:00 1970 +0000
305 files+: dest
306 extra: branch=default
307 description:
308 change in dest
309
310
311 Files that had been excluded are still present:
312
313 $ hg -R source manifest -r tip
314 0
315 a
316 b
317 dest
318 dir/b
319 dir/c
320 dir/d
321 e
322
323 More source changes
324
325 $ cd source
326 $ echo 1 >> a
327 $ hg ci -m '8: source first branch'
328 created new head
329 $ hg up -qr -2
330 $ echo 2 >> a
331 $ hg ci -m '9: source second branch'
332 $ hg merge -q --tool internal:local
333 $ hg ci -m '10: source merge'
334 $ echo >> a
335 $ hg ci -m '11: source change'
336
337 $ hg mani
338 0
339 a
340 b
341 dest
342 dir/b
343 dir/c
344 dir/d
345 e
346
347 $ glog -r 6:
348 @ 11 0c8927d1f7f4 "11: source change" files: a
349 |
350 o 10 9ccb7ee8d261 "10: source merge" files: a
351 |\
352 | o 9 f131b1518dba "9: source second branch" files: a
353 | |
354 o | 8 669cf0e74b50 "8: source first branch" files: a
355 | |
356 | o 7 e6d364a69ff1 "change in dest" files: dest
357 |/
358 o 6 0613c8e59a3d "6: change a" files: a
359 |
360 $ cd ..
361
362 $ hg convert --filemap filemap source dest --config convert.hg.revs=3:
363 scanning source...
364 sorting...
365 converting...
366 3 8: source first branch
367 2 9: source second branch
368 1 10: source merge
369 0 11: source change
370
371 $ glog -R dest
372 o 9 8432d597b263 "11: source change" files: a
373 |
374 o 8 632ffacdcd6f "10: source merge" files: a
375 |\
376 | o 7 049cfee90ee6 "9: source second branch" files: a
377 | |
378 o | 6 9b6845e036e5 "8: source first branch" files: a
379 | |
380 | @ 5 a2e0e3cc6d1d "change in dest" files: dest
381 |/
382 o 4 78814e84a217 "6: change a" files: a
383 |
384 o 3 f7cff662c5e5 "5: merge 2 and 3, copy b to dir/d" files: e
385 |\
386 | o 2 ab40a95b0072 "4: change a" files: a
387 | |
388 o | 1 bd51f17597bf "3: copy a to e, change b" files: b e
389 |/
390 o 0 a4a1dae0fe35 "1: add a and dir/b" files: 0 a
391
392 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now