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