##// END OF EJS Templates
patch: move updatedir() from cmdutil into patch...
Patrick Mezard -
r14259:df9ccd39 default
parent child Browse files
Show More
@@ -0,0 +1,162 b''
1 # wdutil.py - working dir utilities
2 #
3 # Copyright 2011 Patrick Mezard <pmezard@gmail.com>
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
7
8 import glob, os
9 import util, similar, scmutil
10 import match as matchmod
11 from i18n import _
12
13 def expandpats(pats):
14 if not util.expandglobs:
15 return list(pats)
16 ret = []
17 for p in pats:
18 kind, name = matchmod._patsplit(p, None)
19 if kind is None:
20 try:
21 globbed = glob.glob(name)
22 except re.error:
23 globbed = [name]
24 if globbed:
25 ret.extend(globbed)
26 continue
27 ret.append(p)
28 return ret
29
30 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
31 if pats == ("",):
32 pats = []
33 if not globbed and default == 'relpath':
34 pats = expandpats(pats or [])
35 m = matchmod.match(repo.root, repo.getcwd(), pats,
36 opts.get('include'), opts.get('exclude'), default,
37 auditor=repo.auditor)
38 def badfn(f, msg):
39 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
40 m.bad = badfn
41 return m
42
43 def matchall(repo):
44 return matchmod.always(repo.root, repo.getcwd())
45
46 def matchfiles(repo, files):
47 return matchmod.exact(repo.root, repo.getcwd(), files)
48
49 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
50 if dry_run is None:
51 dry_run = opts.get('dry_run')
52 if similarity is None:
53 similarity = float(opts.get('similarity') or 0)
54 # we'd use status here, except handling of symlinks and ignore is tricky
55 added, unknown, deleted, removed = [], [], [], []
56 audit_path = scmutil.pathauditor(repo.root)
57 m = match(repo, pats, opts)
58 for abs in repo.walk(m):
59 target = repo.wjoin(abs)
60 good = True
61 try:
62 audit_path(abs)
63 except (OSError, util.Abort):
64 good = False
65 rel = m.rel(abs)
66 exact = m.exact(abs)
67 if good and abs not in repo.dirstate:
68 unknown.append(abs)
69 if repo.ui.verbose or not exact:
70 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
71 elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
72 or (os.path.isdir(target) and not os.path.islink(target))):
73 deleted.append(abs)
74 if repo.ui.verbose or not exact:
75 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
76 # for finding renames
77 elif repo.dirstate[abs] == 'r':
78 removed.append(abs)
79 elif repo.dirstate[abs] == 'a':
80 added.append(abs)
81 copies = {}
82 if similarity > 0:
83 for old, new, score in similar.findrenames(repo,
84 added + unknown, removed + deleted, similarity):
85 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
86 repo.ui.status(_('recording removal of %s as rename to %s '
87 '(%d%% similar)\n') %
88 (m.rel(old), m.rel(new), score * 100))
89 copies[new] = old
90
91 if not dry_run:
92 wctx = repo[None]
93 wlock = repo.wlock()
94 try:
95 wctx.remove(deleted)
96 wctx.add(unknown)
97 for new, old in copies.iteritems():
98 wctx.copy(old, new)
99 finally:
100 wlock.release()
101
102 def updatedir(ui, repo, patches, similarity=0):
103 '''Update dirstate after patch application according to metadata'''
104 if not patches:
105 return []
106 copies = []
107 removes = set()
108 cfiles = patches.keys()
109 cwd = repo.getcwd()
110 if cwd:
111 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
112 for f in patches:
113 gp = patches[f]
114 if not gp:
115 continue
116 if gp.op == 'RENAME':
117 copies.append((gp.oldpath, gp.path))
118 removes.add(gp.oldpath)
119 elif gp.op == 'COPY':
120 copies.append((gp.oldpath, gp.path))
121 elif gp.op == 'DELETE':
122 removes.add(gp.path)
123
124 wctx = repo[None]
125 for src, dst in copies:
126 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
127 if (not similarity) and removes:
128 wctx.remove(sorted(removes), True)
129
130 for f in patches:
131 gp = patches[f]
132 if gp and gp.mode:
133 islink, isexec = gp.mode
134 dst = repo.wjoin(gp.path)
135 # patch won't create empty files
136 if gp.op == 'ADD' and not os.path.lexists(dst):
137 flags = (isexec and 'x' or '') + (islink and 'l' or '')
138 repo.wwrite(gp.path, '', flags)
139 util.setflags(dst, islink, isexec)
140 addremove(repo, cfiles, similarity=similarity)
141 files = patches.keys()
142 files.extend([r for r in removes if r not in files])
143 return sorted(files)
144
145 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
146 """Update the dirstate to reflect the intent of copying src to dst. For
147 different reasons it might not end with dst being marked as copied from src.
148 """
149 origsrc = repo.dirstate.copied(src) or src
150 if dst == origsrc: # copying back a copy?
151 if repo.dirstate[dst] not in 'mn' and not dryrun:
152 repo.dirstate.normallookup(dst)
153 else:
154 if repo.dirstate[origsrc] == 'a' and origsrc == src:
155 if not ui.quiet:
156 ui.warn(_("%s has not been committed yet, so no copy "
157 "data will be stored for %s.\n")
158 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
159 if repo.dirstate[dst] in '?r' and not dryrun:
160 wctx.add([dst])
161 elif not dryrun:
162 wctx.copy(origsrc, dst)
@@ -618,7 +618,7 b' class queue(object):'
618 fuzz = patchmod.patch(patchfile, self.ui, strip=1,
618 fuzz = patchmod.patch(patchfile, self.ui, strip=1,
619 cwd=repo.root, files=files, eolmode=None)
619 cwd=repo.root, files=files, eolmode=None)
620 finally:
620 finally:
621 files = cmdutil.updatedir(self.ui, repo, files)
621 files = patchmod.updatedir(self.ui, repo, files)
622 return (True, files, fuzz)
622 return (True, files, fuzz)
623 except Exception, inst:
623 except Exception, inst:
624 self.ui.note(str(inst) + '\n')
624 self.ui.note(str(inst) + '\n')
@@ -482,7 +482,7 b' def dorecord(ui, repo, commitfunc, *pats'
482 patch.internalpatch(fp, ui, 1, repo.root, files=pfiles,
482 patch.internalpatch(fp, ui, 1, repo.root, files=pfiles,
483 eolmode=None)
483 eolmode=None)
484 finally:
484 finally:
485 cmdutil.updatedir(ui, repo, pfiles)
485 patch.updatedir(ui, repo, pfiles)
486 except patch.PatchError, err:
486 except patch.PatchError, err:
487 raise util.Abort(str(err))
487 raise util.Abort(str(err))
488 del fp
488 del fp
@@ -232,7 +232,7 b' class transplanter(object):'
232 % revlog.hex(node))
232 % revlog.hex(node))
233 return None
233 return None
234 finally:
234 finally:
235 files = cmdutil.updatedir(self.ui, repo, files)
235 files = patch.updatedir(self.ui, repo, files)
236 except Exception, inst:
236 except Exception, inst:
237 seriespath = os.path.join(self.path, 'series')
237 seriespath = os.path.join(self.path, 'series')
238 if os.path.exists(seriespath):
238 if os.path.exists(seriespath):
@@ -8,10 +8,17 b''
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, glob, tempfile
10 import os, sys, errno, re, glob, tempfile
11 import util, scmutil, templater, patch, error, templatekw
11 import util, scmutil, templater, patch, error, templatekw, wdutil
12 import match as matchmod
12 import match as matchmod
13 import similar, revset, subrepo
13 import similar, revset, subrepo
14
14
15 expandpats = wdutil.expandpats
16 match = wdutil.match
17 matchall = wdutil.matchall
18 matchfiles = wdutil.matchfiles
19 addremove = wdutil.addremove
20 dirstatecopy = wdutil.dirstatecopy
21
15 revrangesep = ':'
22 revrangesep = ':'
16
23
17 def parsealiases(cmd):
24 def parsealiases(cmd):
@@ -243,157 +250,6 b' def make_file(repo, pat, node=None,'
243 pathname),
250 pathname),
244 mode)
251 mode)
245
252
246 def expandpats(pats):
247 if not util.expandglobs:
248 return list(pats)
249 ret = []
250 for p in pats:
251 kind, name = matchmod._patsplit(p, None)
252 if kind is None:
253 try:
254 globbed = glob.glob(name)
255 except re.error:
256 globbed = [name]
257 if globbed:
258 ret.extend(globbed)
259 continue
260 ret.append(p)
261 return ret
262
263 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
264 if pats == ("",):
265 pats = []
266 if not globbed and default == 'relpath':
267 pats = expandpats(pats or [])
268 m = matchmod.match(repo.root, repo.getcwd(), pats,
269 opts.get('include'), opts.get('exclude'), default,
270 auditor=repo.auditor)
271 def badfn(f, msg):
272 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
273 m.bad = badfn
274 return m
275
276 def matchall(repo):
277 return matchmod.always(repo.root, repo.getcwd())
278
279 def matchfiles(repo, files):
280 return matchmod.exact(repo.root, repo.getcwd(), files)
281
282 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
283 if dry_run is None:
284 dry_run = opts.get('dry_run')
285 if similarity is None:
286 similarity = float(opts.get('similarity') or 0)
287 # we'd use status here, except handling of symlinks and ignore is tricky
288 added, unknown, deleted, removed = [], [], [], []
289 audit_path = scmutil.pathauditor(repo.root)
290 m = match(repo, pats, opts)
291 for abs in repo.walk(m):
292 target = repo.wjoin(abs)
293 good = True
294 try:
295 audit_path(abs)
296 except (OSError, util.Abort):
297 good = False
298 rel = m.rel(abs)
299 exact = m.exact(abs)
300 if good and abs not in repo.dirstate:
301 unknown.append(abs)
302 if repo.ui.verbose or not exact:
303 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
304 elif repo.dirstate[abs] != 'r' and (not good or not os.path.lexists(target)
305 or (os.path.isdir(target) and not os.path.islink(target))):
306 deleted.append(abs)
307 if repo.ui.verbose or not exact:
308 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
309 # for finding renames
310 elif repo.dirstate[abs] == 'r':
311 removed.append(abs)
312 elif repo.dirstate[abs] == 'a':
313 added.append(abs)
314 copies = {}
315 if similarity > 0:
316 for old, new, score in similar.findrenames(repo,
317 added + unknown, removed + deleted, similarity):
318 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
319 repo.ui.status(_('recording removal of %s as rename to %s '
320 '(%d%% similar)\n') %
321 (m.rel(old), m.rel(new), score * 100))
322 copies[new] = old
323
324 if not dry_run:
325 wctx = repo[None]
326 wlock = repo.wlock()
327 try:
328 wctx.remove(deleted)
329 wctx.add(unknown)
330 for new, old in copies.iteritems():
331 wctx.copy(old, new)
332 finally:
333 wlock.release()
334
335 def updatedir(ui, repo, patches, similarity=0):
336 '''Update dirstate after patch application according to metadata'''
337 if not patches:
338 return []
339 copies = []
340 removes = set()
341 cfiles = patches.keys()
342 cwd = repo.getcwd()
343 if cwd:
344 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
345 for f in patches:
346 gp = patches[f]
347 if not gp:
348 continue
349 if gp.op == 'RENAME':
350 copies.append((gp.oldpath, gp.path))
351 removes.add(gp.oldpath)
352 elif gp.op == 'COPY':
353 copies.append((gp.oldpath, gp.path))
354 elif gp.op == 'DELETE':
355 removes.add(gp.path)
356
357 wctx = repo[None]
358 for src, dst in copies:
359 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
360 if (not similarity) and removes:
361 wctx.remove(sorted(removes), True)
362
363 for f in patches:
364 gp = patches[f]
365 if gp and gp.mode:
366 islink, isexec = gp.mode
367 dst = repo.wjoin(gp.path)
368 # patch won't create empty files
369 if gp.op == 'ADD' and not os.path.lexists(dst):
370 flags = (isexec and 'x' or '') + (islink and 'l' or '')
371 repo.wwrite(gp.path, '', flags)
372 util.setflags(dst, islink, isexec)
373 addremove(repo, cfiles, similarity=similarity)
374 files = patches.keys()
375 files.extend([r for r in removes if r not in files])
376 return sorted(files)
377
378 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
379 """Update the dirstate to reflect the intent of copying src to dst. For
380 different reasons it might not end with dst being marked as copied from src.
381 """
382 origsrc = repo.dirstate.copied(src) or src
383 if dst == origsrc: # copying back a copy?
384 if repo.dirstate[dst] not in 'mn' and not dryrun:
385 repo.dirstate.normallookup(dst)
386 else:
387 if repo.dirstate[origsrc] == 'a' and origsrc == src:
388 if not ui.quiet:
389 ui.warn(_("%s has not been committed yet, so no copy "
390 "data will be stored for %s.\n")
391 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
392 if repo.dirstate[dst] in '?r' and not dryrun:
393 wctx.add([dst])
394 elif not dryrun:
395 wctx.copy(origsrc, dst)
396
397 def copy(ui, repo, pats, opts, rename=False):
253 def copy(ui, repo, pats, opts, rename=False):
398 # called with the repo lock held
254 # called with the repo lock held
399 #
255 #
@@ -2627,8 +2627,8 b' def import_(ui, repo, patch1, *patches, '
2627 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2627 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
2628 files=files, eolmode=None)
2628 files=files, eolmode=None)
2629 finally:
2629 finally:
2630 files = cmdutil.updatedir(ui, repo, files,
2630 files = patch.updatedir(ui, repo, files,
2631 similarity=sim / 100.0)
2631 similarity=sim / 100.0)
2632 if opts.get('no_commit'):
2632 if opts.get('no_commit'):
2633 if message:
2633 if message:
2634 msgs.append(message)
2634 msgs.append(message)
@@ -11,7 +11,7 b' import tempfile, zlib'
11
11
12 from i18n import _
12 from i18n import _
13 from node import hex, nullid, short
13 from node import hex, nullid, short
14 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding
14 import base85, mdiff, scmutil, util, diffhelpers, copies, encoding, wdutil
15
15
16 gitre = re.compile('diff --git a/(.*) b/(.*)')
16 gitre = re.compile('diff --git a/(.*) b/(.*)')
17
17
@@ -1157,6 +1157,49 b' def _applydiff(ui, fp, patcher, copyfn, '
1157 return -1
1157 return -1
1158 return err
1158 return err
1159
1159
1160 def updatedir(ui, repo, patches, similarity=0):
1161 '''Update dirstate after patch application according to metadata'''
1162 if not patches:
1163 return []
1164 copies = []
1165 removes = set()
1166 cfiles = patches.keys()
1167 cwd = repo.getcwd()
1168 if cwd:
1169 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
1170 for f in patches:
1171 gp = patches[f]
1172 if not gp:
1173 continue
1174 if gp.op == 'RENAME':
1175 copies.append((gp.oldpath, gp.path))
1176 removes.add(gp.oldpath)
1177 elif gp.op == 'COPY':
1178 copies.append((gp.oldpath, gp.path))
1179 elif gp.op == 'DELETE':
1180 removes.add(gp.path)
1181
1182 wctx = repo[None]
1183 for src, dst in copies:
1184 wdutil.dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
1185 if (not similarity) and removes:
1186 wctx.remove(sorted(removes), True)
1187
1188 for f in patches:
1189 gp = patches[f]
1190 if gp and gp.mode:
1191 islink, isexec = gp.mode
1192 dst = repo.wjoin(gp.path)
1193 # patch won't create empty files
1194 if gp.op == 'ADD' and not os.path.lexists(dst):
1195 flags = (isexec and 'x' or '') + (islink and 'l' or '')
1196 repo.wwrite(gp.path, '', flags)
1197 util.setflags(dst, islink, isexec)
1198 wdutil.addremove(repo, cfiles, similarity=similarity)
1199 files = patches.keys()
1200 files.extend([r for r in removes if r not in files])
1201 return sorted(files)
1202
1160 def _externalpatch(patcher, patchname, ui, strip, cwd, files):
1203 def _externalpatch(patcher, patchname, ui, strip, cwd, files):
1161 """use <patcher> to apply <patchname> to the working directory.
1204 """use <patcher> to apply <patchname> to the working directory.
1162 returns whether patch was applied with fuzz factor."""
1205 returns whether patch was applied with fuzz factor."""
General Comments 0
You need to be logged in to leave comments. Login now