##// END OF EJS Templates
largefiles: use the core file copy logic to validate the destination path...
Matt Harbison -
r24006:42fa7eeb default
parent child Browse files
Show More
@@ -1,1346 +1,1347 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
15 archival, pathutil, revset
15 archival, pathutil, revset
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18
18
19 import lfutil
19 import lfutil
20 import lfcommands
20 import lfcommands
21 import basestore
21 import basestore
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def composelargefilematcher(match, manifest):
25 def composelargefilematcher(match, manifest):
26 '''create a matcher that matches only the largefiles in the original
26 '''create a matcher that matches only the largefiles in the original
27 matcher'''
27 matcher'''
28 m = copy.copy(match)
28 m = copy.copy(match)
29 lfile = lambda f: lfutil.standin(f) in manifest
29 lfile = lambda f: lfutil.standin(f) in manifest
30 m._files = filter(lfile, m._files)
30 m._files = filter(lfile, m._files)
31 m._fmap = set(m._files)
31 m._fmap = set(m._files)
32 m._always = False
32 m._always = False
33 origmatchfn = m.matchfn
33 origmatchfn = m.matchfn
34 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
34 m.matchfn = lambda f: lfile(f) and origmatchfn(f)
35 return m
35 return m
36
36
37 def composenormalfilematcher(match, manifest, exclude=None):
37 def composenormalfilematcher(match, manifest, exclude=None):
38 excluded = set()
38 excluded = set()
39 if exclude is not None:
39 if exclude is not None:
40 excluded.update(exclude)
40 excluded.update(exclude)
41
41
42 m = copy.copy(match)
42 m = copy.copy(match)
43 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
43 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
44 manifest or f in excluded)
44 manifest or f in excluded)
45 m._files = filter(notlfile, m._files)
45 m._files = filter(notlfile, m._files)
46 m._fmap = set(m._files)
46 m._fmap = set(m._files)
47 m._always = False
47 m._always = False
48 origmatchfn = m.matchfn
48 origmatchfn = m.matchfn
49 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
49 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
50 return m
50 return m
51
51
52 def installnormalfilesmatchfn(manifest):
52 def installnormalfilesmatchfn(manifest):
53 '''installmatchfn with a matchfn that ignores all largefiles'''
53 '''installmatchfn with a matchfn that ignores all largefiles'''
54 def overridematch(ctx, pats=[], opts={}, globbed=False,
54 def overridematch(ctx, pats=[], opts={}, globbed=False,
55 default='relpath'):
55 default='relpath'):
56 match = oldmatch(ctx, pats, opts, globbed, default)
56 match = oldmatch(ctx, pats, opts, globbed, default)
57 return composenormalfilematcher(match, manifest)
57 return composenormalfilematcher(match, manifest)
58 oldmatch = installmatchfn(overridematch)
58 oldmatch = installmatchfn(overridematch)
59
59
60 def installmatchfn(f):
60 def installmatchfn(f):
61 '''monkey patch the scmutil module with a custom match function.
61 '''monkey patch the scmutil module with a custom match function.
62 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
62 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
63 oldmatch = scmutil.match
63 oldmatch = scmutil.match
64 setattr(f, 'oldmatch', oldmatch)
64 setattr(f, 'oldmatch', oldmatch)
65 scmutil.match = f
65 scmutil.match = f
66 return oldmatch
66 return oldmatch
67
67
68 def restorematchfn():
68 def restorematchfn():
69 '''restores scmutil.match to what it was before installmatchfn
69 '''restores scmutil.match to what it was before installmatchfn
70 was called. no-op if scmutil.match is its original function.
70 was called. no-op if scmutil.match is its original function.
71
71
72 Note that n calls to installmatchfn will require n calls to
72 Note that n calls to installmatchfn will require n calls to
73 restore the original matchfn.'''
73 restore the original matchfn.'''
74 scmutil.match = getattr(scmutil.match, 'oldmatch')
74 scmutil.match = getattr(scmutil.match, 'oldmatch')
75
75
76 def installmatchandpatsfn(f):
76 def installmatchandpatsfn(f):
77 oldmatchandpats = scmutil.matchandpats
77 oldmatchandpats = scmutil.matchandpats
78 setattr(f, 'oldmatchandpats', oldmatchandpats)
78 setattr(f, 'oldmatchandpats', oldmatchandpats)
79 scmutil.matchandpats = f
79 scmutil.matchandpats = f
80 return oldmatchandpats
80 return oldmatchandpats
81
81
82 def restorematchandpatsfn():
82 def restorematchandpatsfn():
83 '''restores scmutil.matchandpats to what it was before
83 '''restores scmutil.matchandpats to what it was before
84 installmatchandpatsfn was called. No-op if scmutil.matchandpats
84 installmatchandpatsfn was called. No-op if scmutil.matchandpats
85 is its original function.
85 is its original function.
86
86
87 Note that n calls to installmatchandpatsfn will require n calls
87 Note that n calls to installmatchandpatsfn will require n calls
88 to restore the original matchfn.'''
88 to restore the original matchfn.'''
89 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
89 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
90 scmutil.matchandpats)
90 scmutil.matchandpats)
91
91
92 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
92 def addlargefiles(ui, repo, isaddremove, matcher, **opts):
93 large = opts.get('large')
93 large = opts.get('large')
94 lfsize = lfutil.getminsize(
94 lfsize = lfutil.getminsize(
95 ui, lfutil.islfilesrepo(repo), opts.get('lfsize'))
95 ui, lfutil.islfilesrepo(repo), opts.get('lfsize'))
96
96
97 lfmatcher = None
97 lfmatcher = None
98 if lfutil.islfilesrepo(repo):
98 if lfutil.islfilesrepo(repo):
99 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
99 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
100 if lfpats:
100 if lfpats:
101 lfmatcher = match_.match(repo.root, '', list(lfpats))
101 lfmatcher = match_.match(repo.root, '', list(lfpats))
102
102
103 lfnames = []
103 lfnames = []
104 m = copy.copy(matcher)
104 m = copy.copy(matcher)
105 m.bad = lambda x, y: None
105 m.bad = lambda x, y: None
106 wctx = repo[None]
106 wctx = repo[None]
107 for f in repo.walk(m):
107 for f in repo.walk(m):
108 exact = m.exact(f)
108 exact = m.exact(f)
109 lfile = lfutil.standin(f) in wctx
109 lfile = lfutil.standin(f) in wctx
110 nfile = f in wctx
110 nfile = f in wctx
111 exists = lfile or nfile
111 exists = lfile or nfile
112
112
113 # addremove in core gets fancy with the name, add doesn't
113 # addremove in core gets fancy with the name, add doesn't
114 if isaddremove:
114 if isaddremove:
115 name = m.uipath(f)
115 name = m.uipath(f)
116 else:
116 else:
117 name = m.rel(f)
117 name = m.rel(f)
118
118
119 # Don't warn the user when they attempt to add a normal tracked file.
119 # Don't warn the user when they attempt to add a normal tracked file.
120 # The normal add code will do that for us.
120 # The normal add code will do that for us.
121 if exact and exists:
121 if exact and exists:
122 if lfile:
122 if lfile:
123 ui.warn(_('%s already a largefile\n') % name)
123 ui.warn(_('%s already a largefile\n') % name)
124 continue
124 continue
125
125
126 if (exact or not exists) and not lfutil.isstandin(f):
126 if (exact or not exists) and not lfutil.isstandin(f):
127 # In case the file was removed previously, but not committed
127 # In case the file was removed previously, but not committed
128 # (issue3507)
128 # (issue3507)
129 if not repo.wvfs.exists(f):
129 if not repo.wvfs.exists(f):
130 continue
130 continue
131
131
132 abovemin = (lfsize and
132 abovemin = (lfsize and
133 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
133 repo.wvfs.lstat(f).st_size >= lfsize * 1024 * 1024)
134 if large or abovemin or (lfmatcher and lfmatcher(f)):
134 if large or abovemin or (lfmatcher and lfmatcher(f)):
135 lfnames.append(f)
135 lfnames.append(f)
136 if ui.verbose or not exact:
136 if ui.verbose or not exact:
137 ui.status(_('adding %s as a largefile\n') % name)
137 ui.status(_('adding %s as a largefile\n') % name)
138
138
139 bad = []
139 bad = []
140
140
141 # Need to lock, otherwise there could be a race condition between
141 # Need to lock, otherwise there could be a race condition between
142 # when standins are created and added to the repo.
142 # when standins are created and added to the repo.
143 wlock = repo.wlock()
143 wlock = repo.wlock()
144 try:
144 try:
145 if not opts.get('dry_run'):
145 if not opts.get('dry_run'):
146 standins = []
146 standins = []
147 lfdirstate = lfutil.openlfdirstate(ui, repo)
147 lfdirstate = lfutil.openlfdirstate(ui, repo)
148 for f in lfnames:
148 for f in lfnames:
149 standinname = lfutil.standin(f)
149 standinname = lfutil.standin(f)
150 lfutil.writestandin(repo, standinname, hash='',
150 lfutil.writestandin(repo, standinname, hash='',
151 executable=lfutil.getexecutable(repo.wjoin(f)))
151 executable=lfutil.getexecutable(repo.wjoin(f)))
152 standins.append(standinname)
152 standins.append(standinname)
153 if lfdirstate[f] == 'r':
153 if lfdirstate[f] == 'r':
154 lfdirstate.normallookup(f)
154 lfdirstate.normallookup(f)
155 else:
155 else:
156 lfdirstate.add(f)
156 lfdirstate.add(f)
157 lfdirstate.write()
157 lfdirstate.write()
158 bad += [lfutil.splitstandin(f)
158 bad += [lfutil.splitstandin(f)
159 for f in repo[None].add(standins)
159 for f in repo[None].add(standins)
160 if f in m.files()]
160 if f in m.files()]
161
161
162 added = [f for f in lfnames if f not in bad]
162 added = [f for f in lfnames if f not in bad]
163 finally:
163 finally:
164 wlock.release()
164 wlock.release()
165 return added, bad
165 return added, bad
166
166
167 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
167 def removelargefiles(ui, repo, isaddremove, matcher, **opts):
168 after = opts.get('after')
168 after = opts.get('after')
169 m = composelargefilematcher(matcher, repo[None].manifest())
169 m = composelargefilematcher(matcher, repo[None].manifest())
170 try:
170 try:
171 repo.lfstatus = True
171 repo.lfstatus = True
172 s = repo.status(match=m, clean=not isaddremove)
172 s = repo.status(match=m, clean=not isaddremove)
173 finally:
173 finally:
174 repo.lfstatus = False
174 repo.lfstatus = False
175 manifest = repo[None].manifest()
175 manifest = repo[None].manifest()
176 modified, added, deleted, clean = [[f for f in list
176 modified, added, deleted, clean = [[f for f in list
177 if lfutil.standin(f) in manifest]
177 if lfutil.standin(f) in manifest]
178 for list in (s.modified, s.added,
178 for list in (s.modified, s.added,
179 s.deleted, s.clean)]
179 s.deleted, s.clean)]
180
180
181 def warn(files, msg):
181 def warn(files, msg):
182 for f in files:
182 for f in files:
183 ui.warn(msg % m.rel(f))
183 ui.warn(msg % m.rel(f))
184 return int(len(files) > 0)
184 return int(len(files) > 0)
185
185
186 result = 0
186 result = 0
187
187
188 if after:
188 if after:
189 remove = deleted
189 remove = deleted
190 result = warn(modified + added + clean,
190 result = warn(modified + added + clean,
191 _('not removing %s: file still exists\n'))
191 _('not removing %s: file still exists\n'))
192 else:
192 else:
193 remove = deleted + clean
193 remove = deleted + clean
194 result = warn(modified, _('not removing %s: file is modified (use -f'
194 result = warn(modified, _('not removing %s: file is modified (use -f'
195 ' to force removal)\n'))
195 ' to force removal)\n'))
196 result = warn(added, _('not removing %s: file has been marked for add'
196 result = warn(added, _('not removing %s: file has been marked for add'
197 ' (use forget to undo)\n')) or result
197 ' (use forget to undo)\n')) or result
198
198
199 # Need to lock because standin files are deleted then removed from the
199 # Need to lock because standin files are deleted then removed from the
200 # repository and we could race in-between.
200 # repository and we could race in-between.
201 wlock = repo.wlock()
201 wlock = repo.wlock()
202 try:
202 try:
203 lfdirstate = lfutil.openlfdirstate(ui, repo)
203 lfdirstate = lfutil.openlfdirstate(ui, repo)
204 for f in sorted(remove):
204 for f in sorted(remove):
205 if ui.verbose or not m.exact(f):
205 if ui.verbose or not m.exact(f):
206 # addremove in core gets fancy with the name, remove doesn't
206 # addremove in core gets fancy with the name, remove doesn't
207 if isaddremove:
207 if isaddremove:
208 name = m.uipath(f)
208 name = m.uipath(f)
209 else:
209 else:
210 name = m.rel(f)
210 name = m.rel(f)
211 ui.status(_('removing %s\n') % name)
211 ui.status(_('removing %s\n') % name)
212
212
213 if not opts.get('dry_run'):
213 if not opts.get('dry_run'):
214 if not after:
214 if not after:
215 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
215 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
216
216
217 if opts.get('dry_run'):
217 if opts.get('dry_run'):
218 return result
218 return result
219
219
220 remove = [lfutil.standin(f) for f in remove]
220 remove = [lfutil.standin(f) for f in remove]
221 # If this is being called by addremove, let the original addremove
221 # If this is being called by addremove, let the original addremove
222 # function handle this.
222 # function handle this.
223 if not isaddremove:
223 if not isaddremove:
224 for f in remove:
224 for f in remove:
225 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
225 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
226 repo[None].forget(remove)
226 repo[None].forget(remove)
227
227
228 for f in remove:
228 for f in remove:
229 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
229 lfutil.synclfdirstate(repo, lfdirstate, lfutil.splitstandin(f),
230 False)
230 False)
231
231
232 lfdirstate.write()
232 lfdirstate.write()
233 finally:
233 finally:
234 wlock.release()
234 wlock.release()
235
235
236 return result
236 return result
237
237
238 # For overriding mercurial.hgweb.webcommands so that largefiles will
238 # For overriding mercurial.hgweb.webcommands so that largefiles will
239 # appear at their right place in the manifests.
239 # appear at their right place in the manifests.
240 def decodepath(orig, path):
240 def decodepath(orig, path):
241 return lfutil.splitstandin(path) or path
241 return lfutil.splitstandin(path) or path
242
242
243 # -- Wrappers: modify existing commands --------------------------------
243 # -- Wrappers: modify existing commands --------------------------------
244
244
245 def overrideadd(orig, ui, repo, *pats, **opts):
245 def overrideadd(orig, ui, repo, *pats, **opts):
246 if opts.get('normal') and opts.get('large'):
246 if opts.get('normal') and opts.get('large'):
247 raise util.Abort(_('--normal cannot be used with --large'))
247 raise util.Abort(_('--normal cannot be used with --large'))
248 return orig(ui, repo, *pats, **opts)
248 return orig(ui, repo, *pats, **opts)
249
249
250 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
250 def cmdutiladd(orig, ui, repo, matcher, prefix, explicitonly, **opts):
251 # The --normal flag short circuits this override
251 # The --normal flag short circuits this override
252 if opts.get('normal'):
252 if opts.get('normal'):
253 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
253 return orig(ui, repo, matcher, prefix, explicitonly, **opts)
254
254
255 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
255 ladded, lbad = addlargefiles(ui, repo, False, matcher, **opts)
256 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
256 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest(),
257 ladded)
257 ladded)
258 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
258 bad = orig(ui, repo, normalmatcher, prefix, explicitonly, **opts)
259
259
260 bad.extend(f for f in lbad)
260 bad.extend(f for f in lbad)
261 return bad
261 return bad
262
262
263 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
263 def cmdutilremove(orig, ui, repo, matcher, prefix, after, force, subrepos):
264 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
264 normalmatcher = composenormalfilematcher(matcher, repo[None].manifest())
265 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
265 result = orig(ui, repo, normalmatcher, prefix, after, force, subrepos)
266 return removelargefiles(ui, repo, False, matcher, after=after,
266 return removelargefiles(ui, repo, False, matcher, after=after,
267 force=force) or result
267 force=force) or result
268
268
269 def overridestatusfn(orig, repo, rev2, **opts):
269 def overridestatusfn(orig, repo, rev2, **opts):
270 try:
270 try:
271 repo._repo.lfstatus = True
271 repo._repo.lfstatus = True
272 return orig(repo, rev2, **opts)
272 return orig(repo, rev2, **opts)
273 finally:
273 finally:
274 repo._repo.lfstatus = False
274 repo._repo.lfstatus = False
275
275
276 def overridestatus(orig, ui, repo, *pats, **opts):
276 def overridestatus(orig, ui, repo, *pats, **opts):
277 try:
277 try:
278 repo.lfstatus = True
278 repo.lfstatus = True
279 return orig(ui, repo, *pats, **opts)
279 return orig(ui, repo, *pats, **opts)
280 finally:
280 finally:
281 repo.lfstatus = False
281 repo.lfstatus = False
282
282
283 def overridedirty(orig, repo, ignoreupdate=False):
283 def overridedirty(orig, repo, ignoreupdate=False):
284 try:
284 try:
285 repo._repo.lfstatus = True
285 repo._repo.lfstatus = True
286 return orig(repo, ignoreupdate)
286 return orig(repo, ignoreupdate)
287 finally:
287 finally:
288 repo._repo.lfstatus = False
288 repo._repo.lfstatus = False
289
289
290 def overridelog(orig, ui, repo, *pats, **opts):
290 def overridelog(orig, ui, repo, *pats, **opts):
291 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
291 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
292 default='relpath'):
292 default='relpath'):
293 """Matcher that merges root directory with .hglf, suitable for log.
293 """Matcher that merges root directory with .hglf, suitable for log.
294 It is still possible to match .hglf directly.
294 It is still possible to match .hglf directly.
295 For any listed files run log on the standin too.
295 For any listed files run log on the standin too.
296 matchfn tries both the given filename and with .hglf stripped.
296 matchfn tries both the given filename and with .hglf stripped.
297 """
297 """
298 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
298 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
299 m, p = copy.copy(matchandpats)
299 m, p = copy.copy(matchandpats)
300
300
301 if m.always():
301 if m.always():
302 # We want to match everything anyway, so there's no benefit trying
302 # We want to match everything anyway, so there's no benefit trying
303 # to add standins.
303 # to add standins.
304 return matchandpats
304 return matchandpats
305
305
306 pats = set(p)
306 pats = set(p)
307 # TODO: handling of patterns in both cases below
307 # TODO: handling of patterns in both cases below
308 if m._cwd:
308 if m._cwd:
309 if os.path.isabs(m._cwd):
309 if os.path.isabs(m._cwd):
310 # TODO: handle largefile magic when invoked from other cwd
310 # TODO: handle largefile magic when invoked from other cwd
311 return matchandpats
311 return matchandpats
312 back = (m._cwd.count('/') + 1) * '../'
312 back = (m._cwd.count('/') + 1) * '../'
313 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
313 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
314 else:
314 else:
315 pats.update(lfutil.standin(f) for f in p)
315 pats.update(lfutil.standin(f) for f in p)
316
316
317 for i in range(0, len(m._files)):
317 for i in range(0, len(m._files)):
318 standin = lfutil.standin(m._files[i])
318 standin = lfutil.standin(m._files[i])
319 # If the "standin" is a directory, append instead of replace to
319 # If the "standin" is a directory, append instead of replace to
320 # support naming a directory on the command line with only
320 # support naming a directory on the command line with only
321 # largefiles. The original directory is kept to support normal
321 # largefiles. The original directory is kept to support normal
322 # files.
322 # files.
323 if standin in repo[ctx.node()]:
323 if standin in repo[ctx.node()]:
324 m._files[i] = standin
324 m._files[i] = standin
325 elif m._files[i] not in repo[ctx.node()] \
325 elif m._files[i] not in repo[ctx.node()] \
326 and repo.wvfs.isdir(standin):
326 and repo.wvfs.isdir(standin):
327 m._files.append(standin)
327 m._files.append(standin)
328 pats.add(standin)
328 pats.add(standin)
329
329
330 m._fmap = set(m._files)
330 m._fmap = set(m._files)
331 m._always = False
331 m._always = False
332 origmatchfn = m.matchfn
332 origmatchfn = m.matchfn
333 def lfmatchfn(f):
333 def lfmatchfn(f):
334 lf = lfutil.splitstandin(f)
334 lf = lfutil.splitstandin(f)
335 if lf is not None and origmatchfn(lf):
335 if lf is not None and origmatchfn(lf):
336 return True
336 return True
337 r = origmatchfn(f)
337 r = origmatchfn(f)
338 return r
338 return r
339 m.matchfn = lfmatchfn
339 m.matchfn = lfmatchfn
340
340
341 return m, pats
341 return m, pats
342
342
343 # For hg log --patch, the match object is used in two different senses:
343 # For hg log --patch, the match object is used in two different senses:
344 # (1) to determine what revisions should be printed out, and
344 # (1) to determine what revisions should be printed out, and
345 # (2) to determine what files to print out diffs for.
345 # (2) to determine what files to print out diffs for.
346 # The magic matchandpats override should be used for case (1) but not for
346 # The magic matchandpats override should be used for case (1) but not for
347 # case (2).
347 # case (2).
348 def overridemakelogfilematcher(repo, pats, opts):
348 def overridemakelogfilematcher(repo, pats, opts):
349 pctx = repo[None]
349 pctx = repo[None]
350 match, pats = oldmatchandpats(pctx, pats, opts)
350 match, pats = oldmatchandpats(pctx, pats, opts)
351 return lambda rev: match
351 return lambda rev: match
352
352
353 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
353 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
354 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
354 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
355 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
355 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
356
356
357 try:
357 try:
358 return orig(ui, repo, *pats, **opts)
358 return orig(ui, repo, *pats, **opts)
359 finally:
359 finally:
360 restorematchandpatsfn()
360 restorematchandpatsfn()
361 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
361 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
362
362
363 def overrideverify(orig, ui, repo, *pats, **opts):
363 def overrideverify(orig, ui, repo, *pats, **opts):
364 large = opts.pop('large', False)
364 large = opts.pop('large', False)
365 all = opts.pop('lfa', False)
365 all = opts.pop('lfa', False)
366 contents = opts.pop('lfc', False)
366 contents = opts.pop('lfc', False)
367
367
368 result = orig(ui, repo, *pats, **opts)
368 result = orig(ui, repo, *pats, **opts)
369 if large or all or contents:
369 if large or all or contents:
370 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
370 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
371 return result
371 return result
372
372
373 def overridedebugstate(orig, ui, repo, *pats, **opts):
373 def overridedebugstate(orig, ui, repo, *pats, **opts):
374 large = opts.pop('large', False)
374 large = opts.pop('large', False)
375 if large:
375 if large:
376 class fakerepo(object):
376 class fakerepo(object):
377 dirstate = lfutil.openlfdirstate(ui, repo)
377 dirstate = lfutil.openlfdirstate(ui, repo)
378 orig(ui, fakerepo, *pats, **opts)
378 orig(ui, fakerepo, *pats, **opts)
379 else:
379 else:
380 orig(ui, repo, *pats, **opts)
380 orig(ui, repo, *pats, **opts)
381
381
382 # Override needs to refresh standins so that update's normal merge
382 # Override needs to refresh standins so that update's normal merge
383 # will go through properly. Then the other update hook (overriding repo.update)
383 # will go through properly. Then the other update hook (overriding repo.update)
384 # will get the new files. Filemerge is also overridden so that the merge
384 # will get the new files. Filemerge is also overridden so that the merge
385 # will merge standins correctly.
385 # will merge standins correctly.
386 def overrideupdate(orig, ui, repo, *pats, **opts):
386 def overrideupdate(orig, ui, repo, *pats, **opts):
387 # Need to lock between the standins getting updated and their
387 # Need to lock between the standins getting updated and their
388 # largefiles getting updated
388 # largefiles getting updated
389 wlock = repo.wlock()
389 wlock = repo.wlock()
390 try:
390 try:
391 if opts['check']:
391 if opts['check']:
392 lfdirstate = lfutil.openlfdirstate(ui, repo)
392 lfdirstate = lfutil.openlfdirstate(ui, repo)
393 unsure, s = lfdirstate.status(
393 unsure, s = lfdirstate.status(
394 match_.always(repo.root, repo.getcwd()),
394 match_.always(repo.root, repo.getcwd()),
395 [], False, False, False)
395 [], False, False, False)
396
396
397 mod = len(s.modified) > 0
397 mod = len(s.modified) > 0
398 for lfile in unsure:
398 for lfile in unsure:
399 standin = lfutil.standin(lfile)
399 standin = lfutil.standin(lfile)
400 if repo['.'][standin].data().strip() != \
400 if repo['.'][standin].data().strip() != \
401 lfutil.hashfile(repo.wjoin(lfile)):
401 lfutil.hashfile(repo.wjoin(lfile)):
402 mod = True
402 mod = True
403 else:
403 else:
404 lfdirstate.normal(lfile)
404 lfdirstate.normal(lfile)
405 lfdirstate.write()
405 lfdirstate.write()
406 if mod:
406 if mod:
407 raise util.Abort(_('uncommitted changes'))
407 raise util.Abort(_('uncommitted changes'))
408 return orig(ui, repo, *pats, **opts)
408 return orig(ui, repo, *pats, **opts)
409 finally:
409 finally:
410 wlock.release()
410 wlock.release()
411
411
412 # Before starting the manifest merge, merge.updates will call
412 # Before starting the manifest merge, merge.updates will call
413 # _checkunknownfile to check if there are any files in the merged-in
413 # _checkunknownfile to check if there are any files in the merged-in
414 # changeset that collide with unknown files in the working copy.
414 # changeset that collide with unknown files in the working copy.
415 #
415 #
416 # The largefiles are seen as unknown, so this prevents us from merging
416 # The largefiles are seen as unknown, so this prevents us from merging
417 # in a file 'foo' if we already have a largefile with the same name.
417 # in a file 'foo' if we already have a largefile with the same name.
418 #
418 #
419 # The overridden function filters the unknown files by removing any
419 # The overridden function filters the unknown files by removing any
420 # largefiles. This makes the merge proceed and we can then handle this
420 # largefiles. This makes the merge proceed and we can then handle this
421 # case further in the overridden calculateupdates function below.
421 # case further in the overridden calculateupdates function below.
422 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
422 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
423 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
423 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
424 return False
424 return False
425 return origfn(repo, wctx, mctx, f, f2)
425 return origfn(repo, wctx, mctx, f, f2)
426
426
427 # The manifest merge handles conflicts on the manifest level. We want
427 # The manifest merge handles conflicts on the manifest level. We want
428 # to handle changes in largefile-ness of files at this level too.
428 # to handle changes in largefile-ness of files at this level too.
429 #
429 #
430 # The strategy is to run the original calculateupdates and then process
430 # The strategy is to run the original calculateupdates and then process
431 # the action list it outputs. There are two cases we need to deal with:
431 # the action list it outputs. There are two cases we need to deal with:
432 #
432 #
433 # 1. Normal file in p1, largefile in p2. Here the largefile is
433 # 1. Normal file in p1, largefile in p2. Here the largefile is
434 # detected via its standin file, which will enter the working copy
434 # detected via its standin file, which will enter the working copy
435 # with a "get" action. It is not "merge" since the standin is all
435 # with a "get" action. It is not "merge" since the standin is all
436 # Mercurial is concerned with at this level -- the link to the
436 # Mercurial is concerned with at this level -- the link to the
437 # existing normal file is not relevant here.
437 # existing normal file is not relevant here.
438 #
438 #
439 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
439 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
440 # since the largefile will be present in the working copy and
440 # since the largefile will be present in the working copy and
441 # different from the normal file in p2. Mercurial therefore
441 # different from the normal file in p2. Mercurial therefore
442 # triggers a merge action.
442 # triggers a merge action.
443 #
443 #
444 # In both cases, we prompt the user and emit new actions to either
444 # In both cases, we prompt the user and emit new actions to either
445 # remove the standin (if the normal file was kept) or to remove the
445 # remove the standin (if the normal file was kept) or to remove the
446 # normal file and get the standin (if the largefile was kept). The
446 # normal file and get the standin (if the largefile was kept). The
447 # default prompt answer is to use the largefile version since it was
447 # default prompt answer is to use the largefile version since it was
448 # presumably changed on purpose.
448 # presumably changed on purpose.
449 #
449 #
450 # Finally, the merge.applyupdates function will then take care of
450 # Finally, the merge.applyupdates function will then take care of
451 # writing the files into the working copy and lfcommands.updatelfiles
451 # writing the files into the working copy and lfcommands.updatelfiles
452 # will update the largefiles.
452 # will update the largefiles.
453 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
453 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
454 partial, acceptremote, followcopies):
454 partial, acceptremote, followcopies):
455 overwrite = force and not branchmerge
455 overwrite = force and not branchmerge
456 actions, diverge, renamedelete = origfn(
456 actions, diverge, renamedelete = origfn(
457 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
457 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
458 followcopies)
458 followcopies)
459
459
460 if overwrite:
460 if overwrite:
461 return actions, diverge, renamedelete
461 return actions, diverge, renamedelete
462
462
463 # Convert to dictionary with filename as key and action as value.
463 # Convert to dictionary with filename as key and action as value.
464 lfiles = set()
464 lfiles = set()
465 for f in actions:
465 for f in actions:
466 splitstandin = f and lfutil.splitstandin(f)
466 splitstandin = f and lfutil.splitstandin(f)
467 if splitstandin in p1:
467 if splitstandin in p1:
468 lfiles.add(splitstandin)
468 lfiles.add(splitstandin)
469 elif lfutil.standin(f) in p1:
469 elif lfutil.standin(f) in p1:
470 lfiles.add(f)
470 lfiles.add(f)
471
471
472 for lfile in lfiles:
472 for lfile in lfiles:
473 standin = lfutil.standin(lfile)
473 standin = lfutil.standin(lfile)
474 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
474 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
475 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
475 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
476 if sm in ('g', 'dc') and lm != 'r':
476 if sm in ('g', 'dc') and lm != 'r':
477 # Case 1: normal file in the working copy, largefile in
477 # Case 1: normal file in the working copy, largefile in
478 # the second parent
478 # the second parent
479 usermsg = _('remote turned local normal file %s into a largefile\n'
479 usermsg = _('remote turned local normal file %s into a largefile\n'
480 'use (l)argefile or keep (n)ormal file?'
480 'use (l)argefile or keep (n)ormal file?'
481 '$$ &Largefile $$ &Normal file') % lfile
481 '$$ &Largefile $$ &Normal file') % lfile
482 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
482 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
483 actions[lfile] = ('r', None, 'replaced by standin')
483 actions[lfile] = ('r', None, 'replaced by standin')
484 actions[standin] = ('g', sargs, 'replaces standin')
484 actions[standin] = ('g', sargs, 'replaces standin')
485 else: # keep local normal file
485 else: # keep local normal file
486 actions[lfile] = ('k', None, 'replaces standin')
486 actions[lfile] = ('k', None, 'replaces standin')
487 if branchmerge:
487 if branchmerge:
488 actions[standin] = ('k', None, 'replaced by non-standin')
488 actions[standin] = ('k', None, 'replaced by non-standin')
489 else:
489 else:
490 actions[standin] = ('r', None, 'replaced by non-standin')
490 actions[standin] = ('r', None, 'replaced by non-standin')
491 elif lm in ('g', 'dc') and sm != 'r':
491 elif lm in ('g', 'dc') and sm != 'r':
492 # Case 2: largefile in the working copy, normal file in
492 # Case 2: largefile in the working copy, normal file in
493 # the second parent
493 # the second parent
494 usermsg = _('remote turned local largefile %s into a normal file\n'
494 usermsg = _('remote turned local largefile %s into a normal file\n'
495 'keep (l)argefile or use (n)ormal file?'
495 'keep (l)argefile or use (n)ormal file?'
496 '$$ &Largefile $$ &Normal file') % lfile
496 '$$ &Largefile $$ &Normal file') % lfile
497 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
497 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
498 if branchmerge:
498 if branchmerge:
499 # largefile can be restored from standin safely
499 # largefile can be restored from standin safely
500 actions[lfile] = ('k', None, 'replaced by standin')
500 actions[lfile] = ('k', None, 'replaced by standin')
501 actions[standin] = ('k', None, 'replaces standin')
501 actions[standin] = ('k', None, 'replaces standin')
502 else:
502 else:
503 # "lfile" should be marked as "removed" without
503 # "lfile" should be marked as "removed" without
504 # removal of itself
504 # removal of itself
505 actions[lfile] = ('lfmr', None,
505 actions[lfile] = ('lfmr', None,
506 'forget non-standin largefile')
506 'forget non-standin largefile')
507
507
508 # linear-merge should treat this largefile as 're-added'
508 # linear-merge should treat this largefile as 're-added'
509 actions[standin] = ('a', None, 'keep standin')
509 actions[standin] = ('a', None, 'keep standin')
510 else: # pick remote normal file
510 else: # pick remote normal file
511 actions[lfile] = ('g', largs, 'replaces standin')
511 actions[lfile] = ('g', largs, 'replaces standin')
512 actions[standin] = ('r', None, 'replaced by non-standin')
512 actions[standin] = ('r', None, 'replaced by non-standin')
513
513
514 return actions, diverge, renamedelete
514 return actions, diverge, renamedelete
515
515
516 def mergerecordupdates(orig, repo, actions, branchmerge):
516 def mergerecordupdates(orig, repo, actions, branchmerge):
517 if 'lfmr' in actions:
517 if 'lfmr' in actions:
518 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
518 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
519 for lfile, args, msg in actions['lfmr']:
519 for lfile, args, msg in actions['lfmr']:
520 # this should be executed before 'orig', to execute 'remove'
520 # this should be executed before 'orig', to execute 'remove'
521 # before all other actions
521 # before all other actions
522 repo.dirstate.remove(lfile)
522 repo.dirstate.remove(lfile)
523 # make sure lfile doesn't get synclfdirstate'd as normal
523 # make sure lfile doesn't get synclfdirstate'd as normal
524 lfdirstate.add(lfile)
524 lfdirstate.add(lfile)
525 lfdirstate.write()
525 lfdirstate.write()
526
526
527 return orig(repo, actions, branchmerge)
527 return orig(repo, actions, branchmerge)
528
528
529
529
530 # Override filemerge to prompt the user about how they wish to merge
530 # Override filemerge to prompt the user about how they wish to merge
531 # largefiles. This will handle identical edits without prompting the user.
531 # largefiles. This will handle identical edits without prompting the user.
532 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
532 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
533 if not lfutil.isstandin(orig):
533 if not lfutil.isstandin(orig):
534 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
534 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
535
535
536 ahash = fca.data().strip().lower()
536 ahash = fca.data().strip().lower()
537 dhash = fcd.data().strip().lower()
537 dhash = fcd.data().strip().lower()
538 ohash = fco.data().strip().lower()
538 ohash = fco.data().strip().lower()
539 if (ohash != ahash and
539 if (ohash != ahash and
540 ohash != dhash and
540 ohash != dhash and
541 (dhash == ahash or
541 (dhash == ahash or
542 repo.ui.promptchoice(
542 repo.ui.promptchoice(
543 _('largefile %s has a merge conflict\nancestor was %s\n'
543 _('largefile %s has a merge conflict\nancestor was %s\n'
544 'keep (l)ocal %s or\ntake (o)ther %s?'
544 'keep (l)ocal %s or\ntake (o)ther %s?'
545 '$$ &Local $$ &Other') %
545 '$$ &Local $$ &Other') %
546 (lfutil.splitstandin(orig), ahash, dhash, ohash),
546 (lfutil.splitstandin(orig), ahash, dhash, ohash),
547 0) == 1)):
547 0) == 1)):
548 repo.wwrite(fcd.path(), fco.data(), fco.flags())
548 repo.wwrite(fcd.path(), fco.data(), fco.flags())
549 return 0
549 return 0
550
550
551 # Copy first changes the matchers to match standins instead of
551 # Copy first changes the matchers to match standins instead of
552 # largefiles. Then it overrides util.copyfile in that function it
552 # largefiles. Then it overrides util.copyfile in that function it
553 # checks if the destination largefile already exists. It also keeps a
553 # checks if the destination largefile already exists. It also keeps a
554 # list of copied files so that the largefiles can be copied and the
554 # list of copied files so that the largefiles can be copied and the
555 # dirstate updated.
555 # dirstate updated.
556 def overridecopy(orig, ui, repo, pats, opts, rename=False):
556 def overridecopy(orig, ui, repo, pats, opts, rename=False):
557 # doesn't remove largefile on rename
557 # doesn't remove largefile on rename
558 if len(pats) < 2:
558 if len(pats) < 2:
559 # this isn't legal, let the original function deal with it
559 # this isn't legal, let the original function deal with it
560 return orig(ui, repo, pats, opts, rename)
560 return orig(ui, repo, pats, opts, rename)
561
561
562 def makestandin(relpath):
563 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
564 return os.path.join(repo.wjoin(lfutil.standin(path)))
565
566 fullpats = scmutil.expandpats(pats)
567 dest = fullpats[-1]
568
569 if os.path.isdir(dest):
570 if not os.path.isdir(makestandin(dest)):
571 os.makedirs(makestandin(dest))
572 # This could copy both lfiles and normal files in one command,
562 # This could copy both lfiles and normal files in one command,
573 # but we don't want to do that. First replace their matcher to
563 # but we don't want to do that. First replace their matcher to
574 # only match normal files and run it, then replace it to just
564 # only match normal files and run it, then replace it to just
575 # match largefiles and run it again.
565 # match largefiles and run it again.
576 nonormalfiles = False
566 nonormalfiles = False
577 nolfiles = False
567 nolfiles = False
578 installnormalfilesmatchfn(repo[None].manifest())
568 installnormalfilesmatchfn(repo[None].manifest())
579 try:
569 try:
580 try:
570 try:
581 result = orig(ui, repo, pats, opts, rename)
571 result = orig(ui, repo, pats, opts, rename)
582 except util.Abort, e:
572 except util.Abort, e:
583 if str(e) != _('no files to copy'):
573 if str(e) != _('no files to copy'):
584 raise e
574 raise e
585 else:
575 else:
586 nonormalfiles = True
576 nonormalfiles = True
587 result = 0
577 result = 0
588 finally:
578 finally:
589 restorematchfn()
579 restorematchfn()
590
580
591 # The first rename can cause our current working directory to be removed.
581 # The first rename can cause our current working directory to be removed.
592 # In that case there is nothing left to copy/rename so just quit.
582 # In that case there is nothing left to copy/rename so just quit.
593 try:
583 try:
594 repo.getcwd()
584 repo.getcwd()
595 except OSError:
585 except OSError:
596 return result
586 return result
597
587
588 def makestandin(relpath):
589 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
590 return os.path.join(repo.wjoin(lfutil.standin(path)))
591
592 fullpats = scmutil.expandpats(pats)
593 dest = fullpats[-1]
594
595 if os.path.isdir(dest):
596 if not os.path.isdir(makestandin(dest)):
597 os.makedirs(makestandin(dest))
598
598 try:
599 try:
599 try:
600 try:
600 # When we call orig below it creates the standins but we don't add
601 # When we call orig below it creates the standins but we don't add
601 # them to the dir state until later so lock during that time.
602 # them to the dir state until later so lock during that time.
602 wlock = repo.wlock()
603 wlock = repo.wlock()
603
604
604 manifest = repo[None].manifest()
605 manifest = repo[None].manifest()
605 def overridematch(ctx, pats=[], opts={}, globbed=False,
606 def overridematch(ctx, pats=[], opts={}, globbed=False,
606 default='relpath'):
607 default='relpath'):
607 newpats = []
608 newpats = []
608 # The patterns were previously mangled to add the standin
609 # The patterns were previously mangled to add the standin
609 # directory; we need to remove that now
610 # directory; we need to remove that now
610 for pat in pats:
611 for pat in pats:
611 if match_.patkind(pat) is None and lfutil.shortname in pat:
612 if match_.patkind(pat) is None and lfutil.shortname in pat:
612 newpats.append(pat.replace(lfutil.shortname, ''))
613 newpats.append(pat.replace(lfutil.shortname, ''))
613 else:
614 else:
614 newpats.append(pat)
615 newpats.append(pat)
615 match = oldmatch(ctx, newpats, opts, globbed, default)
616 match = oldmatch(ctx, newpats, opts, globbed, default)
616 m = copy.copy(match)
617 m = copy.copy(match)
617 lfile = lambda f: lfutil.standin(f) in manifest
618 lfile = lambda f: lfutil.standin(f) in manifest
618 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
619 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
619 m._fmap = set(m._files)
620 m._fmap = set(m._files)
620 origmatchfn = m.matchfn
621 origmatchfn = m.matchfn
621 m.matchfn = lambda f: (lfutil.isstandin(f) and
622 m.matchfn = lambda f: (lfutil.isstandin(f) and
622 (f in manifest) and
623 (f in manifest) and
623 origmatchfn(lfutil.splitstandin(f)) or
624 origmatchfn(lfutil.splitstandin(f)) or
624 None)
625 None)
625 return m
626 return m
626 oldmatch = installmatchfn(overridematch)
627 oldmatch = installmatchfn(overridematch)
627 listpats = []
628 listpats = []
628 for pat in pats:
629 for pat in pats:
629 if match_.patkind(pat) is not None:
630 if match_.patkind(pat) is not None:
630 listpats.append(pat)
631 listpats.append(pat)
631 else:
632 else:
632 listpats.append(makestandin(pat))
633 listpats.append(makestandin(pat))
633
634
634 try:
635 try:
635 origcopyfile = util.copyfile
636 origcopyfile = util.copyfile
636 copiedfiles = []
637 copiedfiles = []
637 def overridecopyfile(src, dest):
638 def overridecopyfile(src, dest):
638 if (lfutil.shortname in src and
639 if (lfutil.shortname in src and
639 dest.startswith(repo.wjoin(lfutil.shortname))):
640 dest.startswith(repo.wjoin(lfutil.shortname))):
640 destlfile = dest.replace(lfutil.shortname, '')
641 destlfile = dest.replace(lfutil.shortname, '')
641 if not opts['force'] and os.path.exists(destlfile):
642 if not opts['force'] and os.path.exists(destlfile):
642 raise IOError('',
643 raise IOError('',
643 _('destination largefile already exists'))
644 _('destination largefile already exists'))
644 copiedfiles.append((src, dest))
645 copiedfiles.append((src, dest))
645 origcopyfile(src, dest)
646 origcopyfile(src, dest)
646
647
647 util.copyfile = overridecopyfile
648 util.copyfile = overridecopyfile
648 result += orig(ui, repo, listpats, opts, rename)
649 result += orig(ui, repo, listpats, opts, rename)
649 finally:
650 finally:
650 util.copyfile = origcopyfile
651 util.copyfile = origcopyfile
651
652
652 lfdirstate = lfutil.openlfdirstate(ui, repo)
653 lfdirstate = lfutil.openlfdirstate(ui, repo)
653 for (src, dest) in copiedfiles:
654 for (src, dest) in copiedfiles:
654 if (lfutil.shortname in src and
655 if (lfutil.shortname in src and
655 dest.startswith(repo.wjoin(lfutil.shortname))):
656 dest.startswith(repo.wjoin(lfutil.shortname))):
656 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
657 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
657 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
658 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
658 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
659 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
659 if not os.path.isdir(destlfiledir):
660 if not os.path.isdir(destlfiledir):
660 os.makedirs(destlfiledir)
661 os.makedirs(destlfiledir)
661 if rename:
662 if rename:
662 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
663 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
663
664
664 # The file is gone, but this deletes any empty parent
665 # The file is gone, but this deletes any empty parent
665 # directories as a side-effect.
666 # directories as a side-effect.
666 util.unlinkpath(repo.wjoin(srclfile), True)
667 util.unlinkpath(repo.wjoin(srclfile), True)
667 lfdirstate.remove(srclfile)
668 lfdirstate.remove(srclfile)
668 else:
669 else:
669 util.copyfile(repo.wjoin(srclfile),
670 util.copyfile(repo.wjoin(srclfile),
670 repo.wjoin(destlfile))
671 repo.wjoin(destlfile))
671
672
672 lfdirstate.add(destlfile)
673 lfdirstate.add(destlfile)
673 lfdirstate.write()
674 lfdirstate.write()
674 except util.Abort, e:
675 except util.Abort, e:
675 if str(e) != _('no files to copy'):
676 if str(e) != _('no files to copy'):
676 raise e
677 raise e
677 else:
678 else:
678 nolfiles = True
679 nolfiles = True
679 finally:
680 finally:
680 restorematchfn()
681 restorematchfn()
681 wlock.release()
682 wlock.release()
682
683
683 if nolfiles and nonormalfiles:
684 if nolfiles and nonormalfiles:
684 raise util.Abort(_('no files to copy'))
685 raise util.Abort(_('no files to copy'))
685
686
686 return result
687 return result
687
688
688 # When the user calls revert, we have to be careful to not revert any
689 # When the user calls revert, we have to be careful to not revert any
689 # changes to other largefiles accidentally. This means we have to keep
690 # changes to other largefiles accidentally. This means we have to keep
690 # track of the largefiles that are being reverted so we only pull down
691 # track of the largefiles that are being reverted so we only pull down
691 # the necessary largefiles.
692 # the necessary largefiles.
692 #
693 #
693 # Standins are only updated (to match the hash of largefiles) before
694 # Standins are only updated (to match the hash of largefiles) before
694 # commits. Update the standins then run the original revert, changing
695 # commits. Update the standins then run the original revert, changing
695 # the matcher to hit standins instead of largefiles. Based on the
696 # the matcher to hit standins instead of largefiles. Based on the
696 # resulting standins update the largefiles.
697 # resulting standins update the largefiles.
697 def overriderevert(orig, ui, repo, *pats, **opts):
698 def overriderevert(orig, ui, repo, *pats, **opts):
698 # Because we put the standins in a bad state (by updating them)
699 # Because we put the standins in a bad state (by updating them)
699 # and then return them to a correct state we need to lock to
700 # and then return them to a correct state we need to lock to
700 # prevent others from changing them in their incorrect state.
701 # prevent others from changing them in their incorrect state.
701 wlock = repo.wlock()
702 wlock = repo.wlock()
702 try:
703 try:
703 lfdirstate = lfutil.openlfdirstate(ui, repo)
704 lfdirstate = lfutil.openlfdirstate(ui, repo)
704 s = lfutil.lfdirstatestatus(lfdirstate, repo)
705 s = lfutil.lfdirstatestatus(lfdirstate, repo)
705 lfdirstate.write()
706 lfdirstate.write()
706 for lfile in s.modified:
707 for lfile in s.modified:
707 lfutil.updatestandin(repo, lfutil.standin(lfile))
708 lfutil.updatestandin(repo, lfutil.standin(lfile))
708 for lfile in s.deleted:
709 for lfile in s.deleted:
709 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
710 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
710 os.unlink(repo.wjoin(lfutil.standin(lfile)))
711 os.unlink(repo.wjoin(lfutil.standin(lfile)))
711
712
712 oldstandins = lfutil.getstandinsstate(repo)
713 oldstandins = lfutil.getstandinsstate(repo)
713
714
714 def overridematch(ctx, pats=[], opts={}, globbed=False,
715 def overridematch(ctx, pats=[], opts={}, globbed=False,
715 default='relpath'):
716 default='relpath'):
716 match = oldmatch(ctx, pats, opts, globbed, default)
717 match = oldmatch(ctx, pats, opts, globbed, default)
717 m = copy.copy(match)
718 m = copy.copy(match)
718 def tostandin(f):
719 def tostandin(f):
719 if lfutil.standin(f) in ctx:
720 if lfutil.standin(f) in ctx:
720 return lfutil.standin(f)
721 return lfutil.standin(f)
721 elif lfutil.standin(f) in repo[None]:
722 elif lfutil.standin(f) in repo[None]:
722 return None
723 return None
723 return f
724 return f
724 m._files = [tostandin(f) for f in m._files]
725 m._files = [tostandin(f) for f in m._files]
725 m._files = [f for f in m._files if f is not None]
726 m._files = [f for f in m._files if f is not None]
726 m._fmap = set(m._files)
727 m._fmap = set(m._files)
727 origmatchfn = m.matchfn
728 origmatchfn = m.matchfn
728 def matchfn(f):
729 def matchfn(f):
729 if lfutil.isstandin(f):
730 if lfutil.isstandin(f):
730 return (origmatchfn(lfutil.splitstandin(f)) and
731 return (origmatchfn(lfutil.splitstandin(f)) and
731 (f in repo[None] or f in ctx))
732 (f in repo[None] or f in ctx))
732 return origmatchfn(f)
733 return origmatchfn(f)
733 m.matchfn = matchfn
734 m.matchfn = matchfn
734 return m
735 return m
735 oldmatch = installmatchfn(overridematch)
736 oldmatch = installmatchfn(overridematch)
736 try:
737 try:
737 orig(ui, repo, *pats, **opts)
738 orig(ui, repo, *pats, **opts)
738 finally:
739 finally:
739 restorematchfn()
740 restorematchfn()
740
741
741 newstandins = lfutil.getstandinsstate(repo)
742 newstandins = lfutil.getstandinsstate(repo)
742 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
743 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
743 # lfdirstate should be 'normallookup'-ed for updated files,
744 # lfdirstate should be 'normallookup'-ed for updated files,
744 # because reverting doesn't touch dirstate for 'normal' files
745 # because reverting doesn't touch dirstate for 'normal' files
745 # when target revision is explicitly specified: in such case,
746 # when target revision is explicitly specified: in such case,
746 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
747 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
747 # of target (standin) file.
748 # of target (standin) file.
748 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
749 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
749 normallookup=True)
750 normallookup=True)
750
751
751 finally:
752 finally:
752 wlock.release()
753 wlock.release()
753
754
754 # after pulling changesets, we need to take some extra care to get
755 # after pulling changesets, we need to take some extra care to get
755 # largefiles updated remotely
756 # largefiles updated remotely
756 def overridepull(orig, ui, repo, source=None, **opts):
757 def overridepull(orig, ui, repo, source=None, **opts):
757 revsprepull = len(repo)
758 revsprepull = len(repo)
758 if not source:
759 if not source:
759 source = 'default'
760 source = 'default'
760 repo.lfpullsource = source
761 repo.lfpullsource = source
761 result = orig(ui, repo, source, **opts)
762 result = orig(ui, repo, source, **opts)
762 revspostpull = len(repo)
763 revspostpull = len(repo)
763 lfrevs = opts.get('lfrev', [])
764 lfrevs = opts.get('lfrev', [])
764 if opts.get('all_largefiles'):
765 if opts.get('all_largefiles'):
765 lfrevs.append('pulled()')
766 lfrevs.append('pulled()')
766 if lfrevs and revspostpull > revsprepull:
767 if lfrevs and revspostpull > revsprepull:
767 numcached = 0
768 numcached = 0
768 repo.firstpulled = revsprepull # for pulled() revset expression
769 repo.firstpulled = revsprepull # for pulled() revset expression
769 try:
770 try:
770 for rev in scmutil.revrange(repo, lfrevs):
771 for rev in scmutil.revrange(repo, lfrevs):
771 ui.note(_('pulling largefiles for revision %s\n') % rev)
772 ui.note(_('pulling largefiles for revision %s\n') % rev)
772 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
773 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
773 numcached += len(cached)
774 numcached += len(cached)
774 finally:
775 finally:
775 del repo.firstpulled
776 del repo.firstpulled
776 ui.status(_("%d largefiles cached\n") % numcached)
777 ui.status(_("%d largefiles cached\n") % numcached)
777 return result
778 return result
778
779
779 def pulledrevsetsymbol(repo, subset, x):
780 def pulledrevsetsymbol(repo, subset, x):
780 """``pulled()``
781 """``pulled()``
781 Changesets that just has been pulled.
782 Changesets that just has been pulled.
782
783
783 Only available with largefiles from pull --lfrev expressions.
784 Only available with largefiles from pull --lfrev expressions.
784
785
785 .. container:: verbose
786 .. container:: verbose
786
787
787 Some examples:
788 Some examples:
788
789
789 - pull largefiles for all new changesets::
790 - pull largefiles for all new changesets::
790
791
791 hg pull -lfrev "pulled()"
792 hg pull -lfrev "pulled()"
792
793
793 - pull largefiles for all new branch heads::
794 - pull largefiles for all new branch heads::
794
795
795 hg pull -lfrev "head(pulled()) and not closed()"
796 hg pull -lfrev "head(pulled()) and not closed()"
796
797
797 """
798 """
798
799
799 try:
800 try:
800 firstpulled = repo.firstpulled
801 firstpulled = repo.firstpulled
801 except AttributeError:
802 except AttributeError:
802 raise util.Abort(_("pulled() only available in --lfrev"))
803 raise util.Abort(_("pulled() only available in --lfrev"))
803 return revset.baseset([r for r in subset if r >= firstpulled])
804 return revset.baseset([r for r in subset if r >= firstpulled])
804
805
805 def overrideclone(orig, ui, source, dest=None, **opts):
806 def overrideclone(orig, ui, source, dest=None, **opts):
806 d = dest
807 d = dest
807 if d is None:
808 if d is None:
808 d = hg.defaultdest(source)
809 d = hg.defaultdest(source)
809 if opts.get('all_largefiles') and not hg.islocal(d):
810 if opts.get('all_largefiles') and not hg.islocal(d):
810 raise util.Abort(_(
811 raise util.Abort(_(
811 '--all-largefiles is incompatible with non-local destination %s') %
812 '--all-largefiles is incompatible with non-local destination %s') %
812 d)
813 d)
813
814
814 return orig(ui, source, dest, **opts)
815 return orig(ui, source, dest, **opts)
815
816
816 def hgclone(orig, ui, opts, *args, **kwargs):
817 def hgclone(orig, ui, opts, *args, **kwargs):
817 result = orig(ui, opts, *args, **kwargs)
818 result = orig(ui, opts, *args, **kwargs)
818
819
819 if result is not None:
820 if result is not None:
820 sourcerepo, destrepo = result
821 sourcerepo, destrepo = result
821 repo = destrepo.local()
822 repo = destrepo.local()
822
823
823 # Caching is implicitly limited to 'rev' option, since the dest repo was
824 # Caching is implicitly limited to 'rev' option, since the dest repo was
824 # truncated at that point. The user may expect a download count with
825 # truncated at that point. The user may expect a download count with
825 # this option, so attempt whether or not this is a largefile repo.
826 # this option, so attempt whether or not this is a largefile repo.
826 if opts.get('all_largefiles'):
827 if opts.get('all_largefiles'):
827 success, missing = lfcommands.downloadlfiles(ui, repo, None)
828 success, missing = lfcommands.downloadlfiles(ui, repo, None)
828
829
829 if missing != 0:
830 if missing != 0:
830 return None
831 return None
831
832
832 return result
833 return result
833
834
834 def overriderebase(orig, ui, repo, **opts):
835 def overriderebase(orig, ui, repo, **opts):
835 resuming = opts.get('continue')
836 resuming = opts.get('continue')
836 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
837 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
837 repo._lfstatuswriters.append(lambda *msg, **opts: None)
838 repo._lfstatuswriters.append(lambda *msg, **opts: None)
838 try:
839 try:
839 return orig(ui, repo, **opts)
840 return orig(ui, repo, **opts)
840 finally:
841 finally:
841 repo._lfstatuswriters.pop()
842 repo._lfstatuswriters.pop()
842 repo._lfcommithooks.pop()
843 repo._lfcommithooks.pop()
843
844
844 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
845 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
845 prefix=None, mtime=None, subrepos=None):
846 prefix=None, mtime=None, subrepos=None):
846 # No need to lock because we are only reading history and
847 # No need to lock because we are only reading history and
847 # largefile caches, neither of which are modified.
848 # largefile caches, neither of which are modified.
848 lfcommands.cachelfiles(repo.ui, repo, node)
849 lfcommands.cachelfiles(repo.ui, repo, node)
849
850
850 if kind not in archival.archivers:
851 if kind not in archival.archivers:
851 raise util.Abort(_("unknown archive type '%s'") % kind)
852 raise util.Abort(_("unknown archive type '%s'") % kind)
852
853
853 ctx = repo[node]
854 ctx = repo[node]
854
855
855 if kind == 'files':
856 if kind == 'files':
856 if prefix:
857 if prefix:
857 raise util.Abort(
858 raise util.Abort(
858 _('cannot give prefix when archiving to files'))
859 _('cannot give prefix when archiving to files'))
859 else:
860 else:
860 prefix = archival.tidyprefix(dest, kind, prefix)
861 prefix = archival.tidyprefix(dest, kind, prefix)
861
862
862 def write(name, mode, islink, getdata):
863 def write(name, mode, islink, getdata):
863 if matchfn and not matchfn(name):
864 if matchfn and not matchfn(name):
864 return
865 return
865 data = getdata()
866 data = getdata()
866 if decode:
867 if decode:
867 data = repo.wwritedata(name, data)
868 data = repo.wwritedata(name, data)
868 archiver.addfile(prefix + name, mode, islink, data)
869 archiver.addfile(prefix + name, mode, islink, data)
869
870
870 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
871 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
871
872
872 if repo.ui.configbool("ui", "archivemeta", True):
873 if repo.ui.configbool("ui", "archivemeta", True):
873 def metadata():
874 def metadata():
874 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
875 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
875 hex(repo.changelog.node(0)), hex(node), ctx.branch())
876 hex(repo.changelog.node(0)), hex(node), ctx.branch())
876
877
877 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
878 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
878 if repo.tagtype(t) == 'global')
879 if repo.tagtype(t) == 'global')
879 if not tags:
880 if not tags:
880 repo.ui.pushbuffer()
881 repo.ui.pushbuffer()
881 opts = {'template': '{latesttag}\n{latesttagdistance}',
882 opts = {'template': '{latesttag}\n{latesttagdistance}',
882 'style': '', 'patch': None, 'git': None}
883 'style': '', 'patch': None, 'git': None}
883 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
884 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
884 ltags, dist = repo.ui.popbuffer().split('\n')
885 ltags, dist = repo.ui.popbuffer().split('\n')
885 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
886 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
886 tags += 'latesttagdistance: %s\n' % dist
887 tags += 'latesttagdistance: %s\n' % dist
887
888
888 return base + tags
889 return base + tags
889
890
890 write('.hg_archival.txt', 0644, False, metadata)
891 write('.hg_archival.txt', 0644, False, metadata)
891
892
892 for f in ctx:
893 for f in ctx:
893 ff = ctx.flags(f)
894 ff = ctx.flags(f)
894 getdata = ctx[f].data
895 getdata = ctx[f].data
895 if lfutil.isstandin(f):
896 if lfutil.isstandin(f):
896 path = lfutil.findfile(repo, getdata().strip())
897 path = lfutil.findfile(repo, getdata().strip())
897 if path is None:
898 if path is None:
898 raise util.Abort(
899 raise util.Abort(
899 _('largefile %s not found in repo store or system cache')
900 _('largefile %s not found in repo store or system cache')
900 % lfutil.splitstandin(f))
901 % lfutil.splitstandin(f))
901 f = lfutil.splitstandin(f)
902 f = lfutil.splitstandin(f)
902
903
903 def getdatafn():
904 def getdatafn():
904 fd = None
905 fd = None
905 try:
906 try:
906 fd = open(path, 'rb')
907 fd = open(path, 'rb')
907 return fd.read()
908 return fd.read()
908 finally:
909 finally:
909 if fd:
910 if fd:
910 fd.close()
911 fd.close()
911
912
912 getdata = getdatafn
913 getdata = getdatafn
913 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
914 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
914
915
915 if subrepos:
916 if subrepos:
916 for subpath in sorted(ctx.substate):
917 for subpath in sorted(ctx.substate):
917 sub = ctx.sub(subpath)
918 sub = ctx.sub(subpath)
918 submatch = match_.narrowmatcher(subpath, matchfn)
919 submatch = match_.narrowmatcher(subpath, matchfn)
919 sub.archive(archiver, prefix, submatch)
920 sub.archive(archiver, prefix, submatch)
920
921
921 archiver.done()
922 archiver.done()
922
923
923 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
924 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
924 repo._get(repo._state + ('hg',))
925 repo._get(repo._state + ('hg',))
925 rev = repo._state[1]
926 rev = repo._state[1]
926 ctx = repo._repo[rev]
927 ctx = repo._repo[rev]
927
928
928 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
929 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
929
930
930 def write(name, mode, islink, getdata):
931 def write(name, mode, islink, getdata):
931 # At this point, the standin has been replaced with the largefile name,
932 # At this point, the standin has been replaced with the largefile name,
932 # so the normal matcher works here without the lfutil variants.
933 # so the normal matcher works here without the lfutil variants.
933 if match and not match(f):
934 if match and not match(f):
934 return
935 return
935 data = getdata()
936 data = getdata()
936
937
937 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
938 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
938
939
939 for f in ctx:
940 for f in ctx:
940 ff = ctx.flags(f)
941 ff = ctx.flags(f)
941 getdata = ctx[f].data
942 getdata = ctx[f].data
942 if lfutil.isstandin(f):
943 if lfutil.isstandin(f):
943 path = lfutil.findfile(repo._repo, getdata().strip())
944 path = lfutil.findfile(repo._repo, getdata().strip())
944 if path is None:
945 if path is None:
945 raise util.Abort(
946 raise util.Abort(
946 _('largefile %s not found in repo store or system cache')
947 _('largefile %s not found in repo store or system cache')
947 % lfutil.splitstandin(f))
948 % lfutil.splitstandin(f))
948 f = lfutil.splitstandin(f)
949 f = lfutil.splitstandin(f)
949
950
950 def getdatafn():
951 def getdatafn():
951 fd = None
952 fd = None
952 try:
953 try:
953 fd = open(os.path.join(prefix, path), 'rb')
954 fd = open(os.path.join(prefix, path), 'rb')
954 return fd.read()
955 return fd.read()
955 finally:
956 finally:
956 if fd:
957 if fd:
957 fd.close()
958 fd.close()
958
959
959 getdata = getdatafn
960 getdata = getdatafn
960
961
961 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
962 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
962
963
963 for subpath in sorted(ctx.substate):
964 for subpath in sorted(ctx.substate):
964 sub = ctx.sub(subpath)
965 sub = ctx.sub(subpath)
965 submatch = match_.narrowmatcher(subpath, match)
966 submatch = match_.narrowmatcher(subpath, match)
966 sub.archive(archiver, os.path.join(prefix, repo._path) + '/', submatch)
967 sub.archive(archiver, os.path.join(prefix, repo._path) + '/', submatch)
967
968
968 # If a largefile is modified, the change is not reflected in its
969 # If a largefile is modified, the change is not reflected in its
969 # standin until a commit. cmdutil.bailifchanged() raises an exception
970 # standin until a commit. cmdutil.bailifchanged() raises an exception
970 # if the repo has uncommitted changes. Wrap it to also check if
971 # if the repo has uncommitted changes. Wrap it to also check if
971 # largefiles were changed. This is used by bisect, backout and fetch.
972 # largefiles were changed. This is used by bisect, backout and fetch.
972 def overridebailifchanged(orig, repo):
973 def overridebailifchanged(orig, repo):
973 orig(repo)
974 orig(repo)
974 repo.lfstatus = True
975 repo.lfstatus = True
975 s = repo.status()
976 s = repo.status()
976 repo.lfstatus = False
977 repo.lfstatus = False
977 if s.modified or s.added or s.removed or s.deleted:
978 if s.modified or s.added or s.removed or s.deleted:
978 raise util.Abort(_('uncommitted changes'))
979 raise util.Abort(_('uncommitted changes'))
979
980
980 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
981 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
981 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
982 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
982 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
983 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
983 m = composelargefilematcher(match, repo[None].manifest())
984 m = composelargefilematcher(match, repo[None].manifest())
984
985
985 try:
986 try:
986 repo.lfstatus = True
987 repo.lfstatus = True
987 s = repo.status(match=m, clean=True)
988 s = repo.status(match=m, clean=True)
988 finally:
989 finally:
989 repo.lfstatus = False
990 repo.lfstatus = False
990 forget = sorted(s.modified + s.added + s.deleted + s.clean)
991 forget = sorted(s.modified + s.added + s.deleted + s.clean)
991 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
992 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
992
993
993 for f in forget:
994 for f in forget:
994 if lfutil.standin(f) not in repo.dirstate and not \
995 if lfutil.standin(f) not in repo.dirstate and not \
995 repo.wvfs.isdir(lfutil.standin(f)):
996 repo.wvfs.isdir(lfutil.standin(f)):
996 ui.warn(_('not removing %s: file is already untracked\n')
997 ui.warn(_('not removing %s: file is already untracked\n')
997 % m.rel(f))
998 % m.rel(f))
998 bad.append(f)
999 bad.append(f)
999
1000
1000 for f in forget:
1001 for f in forget:
1001 if ui.verbose or not m.exact(f):
1002 if ui.verbose or not m.exact(f):
1002 ui.status(_('removing %s\n') % m.rel(f))
1003 ui.status(_('removing %s\n') % m.rel(f))
1003
1004
1004 # Need to lock because standin files are deleted then removed from the
1005 # Need to lock because standin files are deleted then removed from the
1005 # repository and we could race in-between.
1006 # repository and we could race in-between.
1006 wlock = repo.wlock()
1007 wlock = repo.wlock()
1007 try:
1008 try:
1008 lfdirstate = lfutil.openlfdirstate(ui, repo)
1009 lfdirstate = lfutil.openlfdirstate(ui, repo)
1009 for f in forget:
1010 for f in forget:
1010 if lfdirstate[f] == 'a':
1011 if lfdirstate[f] == 'a':
1011 lfdirstate.drop(f)
1012 lfdirstate.drop(f)
1012 else:
1013 else:
1013 lfdirstate.remove(f)
1014 lfdirstate.remove(f)
1014 lfdirstate.write()
1015 lfdirstate.write()
1015 standins = [lfutil.standin(f) for f in forget]
1016 standins = [lfutil.standin(f) for f in forget]
1016 for f in standins:
1017 for f in standins:
1017 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1018 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1018 rejected = repo[None].forget(standins)
1019 rejected = repo[None].forget(standins)
1019 finally:
1020 finally:
1020 wlock.release()
1021 wlock.release()
1021
1022
1022 bad.extend(f for f in rejected if f in m.files())
1023 bad.extend(f for f in rejected if f in m.files())
1023 forgot.extend(f for f in forget if f not in rejected)
1024 forgot.extend(f for f in forget if f not in rejected)
1024 return bad, forgot
1025 return bad, forgot
1025
1026
1026 def _getoutgoings(repo, other, missing, addfunc):
1027 def _getoutgoings(repo, other, missing, addfunc):
1027 """get pairs of filename and largefile hash in outgoing revisions
1028 """get pairs of filename and largefile hash in outgoing revisions
1028 in 'missing'.
1029 in 'missing'.
1029
1030
1030 largefiles already existing on 'other' repository are ignored.
1031 largefiles already existing on 'other' repository are ignored.
1031
1032
1032 'addfunc' is invoked with each unique pairs of filename and
1033 'addfunc' is invoked with each unique pairs of filename and
1033 largefile hash value.
1034 largefile hash value.
1034 """
1035 """
1035 knowns = set()
1036 knowns = set()
1036 lfhashes = set()
1037 lfhashes = set()
1037 def dedup(fn, lfhash):
1038 def dedup(fn, lfhash):
1038 k = (fn, lfhash)
1039 k = (fn, lfhash)
1039 if k not in knowns:
1040 if k not in knowns:
1040 knowns.add(k)
1041 knowns.add(k)
1041 lfhashes.add(lfhash)
1042 lfhashes.add(lfhash)
1042 lfutil.getlfilestoupload(repo, missing, dedup)
1043 lfutil.getlfilestoupload(repo, missing, dedup)
1043 if lfhashes:
1044 if lfhashes:
1044 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1045 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1045 for fn, lfhash in knowns:
1046 for fn, lfhash in knowns:
1046 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1047 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1047 addfunc(fn, lfhash)
1048 addfunc(fn, lfhash)
1048
1049
1049 def outgoinghook(ui, repo, other, opts, missing):
1050 def outgoinghook(ui, repo, other, opts, missing):
1050 if opts.pop('large', None):
1051 if opts.pop('large', None):
1051 lfhashes = set()
1052 lfhashes = set()
1052 if ui.debugflag:
1053 if ui.debugflag:
1053 toupload = {}
1054 toupload = {}
1054 def addfunc(fn, lfhash):
1055 def addfunc(fn, lfhash):
1055 if fn not in toupload:
1056 if fn not in toupload:
1056 toupload[fn] = []
1057 toupload[fn] = []
1057 toupload[fn].append(lfhash)
1058 toupload[fn].append(lfhash)
1058 lfhashes.add(lfhash)
1059 lfhashes.add(lfhash)
1059 def showhashes(fn):
1060 def showhashes(fn):
1060 for lfhash in sorted(toupload[fn]):
1061 for lfhash in sorted(toupload[fn]):
1061 ui.debug(' %s\n' % (lfhash))
1062 ui.debug(' %s\n' % (lfhash))
1062 else:
1063 else:
1063 toupload = set()
1064 toupload = set()
1064 def addfunc(fn, lfhash):
1065 def addfunc(fn, lfhash):
1065 toupload.add(fn)
1066 toupload.add(fn)
1066 lfhashes.add(lfhash)
1067 lfhashes.add(lfhash)
1067 def showhashes(fn):
1068 def showhashes(fn):
1068 pass
1069 pass
1069 _getoutgoings(repo, other, missing, addfunc)
1070 _getoutgoings(repo, other, missing, addfunc)
1070
1071
1071 if not toupload:
1072 if not toupload:
1072 ui.status(_('largefiles: no files to upload\n'))
1073 ui.status(_('largefiles: no files to upload\n'))
1073 else:
1074 else:
1074 ui.status(_('largefiles to upload (%d entities):\n')
1075 ui.status(_('largefiles to upload (%d entities):\n')
1075 % (len(lfhashes)))
1076 % (len(lfhashes)))
1076 for file in sorted(toupload):
1077 for file in sorted(toupload):
1077 ui.status(lfutil.splitstandin(file) + '\n')
1078 ui.status(lfutil.splitstandin(file) + '\n')
1078 showhashes(file)
1079 showhashes(file)
1079 ui.status('\n')
1080 ui.status('\n')
1080
1081
1081 def summaryremotehook(ui, repo, opts, changes):
1082 def summaryremotehook(ui, repo, opts, changes):
1082 largeopt = opts.get('large', False)
1083 largeopt = opts.get('large', False)
1083 if changes is None:
1084 if changes is None:
1084 if largeopt:
1085 if largeopt:
1085 return (False, True) # only outgoing check is needed
1086 return (False, True) # only outgoing check is needed
1086 else:
1087 else:
1087 return (False, False)
1088 return (False, False)
1088 elif largeopt:
1089 elif largeopt:
1089 url, branch, peer, outgoing = changes[1]
1090 url, branch, peer, outgoing = changes[1]
1090 if peer is None:
1091 if peer is None:
1091 # i18n: column positioning for "hg summary"
1092 # i18n: column positioning for "hg summary"
1092 ui.status(_('largefiles: (no remote repo)\n'))
1093 ui.status(_('largefiles: (no remote repo)\n'))
1093 return
1094 return
1094
1095
1095 toupload = set()
1096 toupload = set()
1096 lfhashes = set()
1097 lfhashes = set()
1097 def addfunc(fn, lfhash):
1098 def addfunc(fn, lfhash):
1098 toupload.add(fn)
1099 toupload.add(fn)
1099 lfhashes.add(lfhash)
1100 lfhashes.add(lfhash)
1100 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1101 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1101
1102
1102 if not toupload:
1103 if not toupload:
1103 # i18n: column positioning for "hg summary"
1104 # i18n: column positioning for "hg summary"
1104 ui.status(_('largefiles: (no files to upload)\n'))
1105 ui.status(_('largefiles: (no files to upload)\n'))
1105 else:
1106 else:
1106 # i18n: column positioning for "hg summary"
1107 # i18n: column positioning for "hg summary"
1107 ui.status(_('largefiles: %d entities for %d files to upload\n')
1108 ui.status(_('largefiles: %d entities for %d files to upload\n')
1108 % (len(lfhashes), len(toupload)))
1109 % (len(lfhashes), len(toupload)))
1109
1110
1110 def overridesummary(orig, ui, repo, *pats, **opts):
1111 def overridesummary(orig, ui, repo, *pats, **opts):
1111 try:
1112 try:
1112 repo.lfstatus = True
1113 repo.lfstatus = True
1113 orig(ui, repo, *pats, **opts)
1114 orig(ui, repo, *pats, **opts)
1114 finally:
1115 finally:
1115 repo.lfstatus = False
1116 repo.lfstatus = False
1116
1117
1117 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1118 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1118 similarity=None):
1119 similarity=None):
1119 if not lfutil.islfilesrepo(repo):
1120 if not lfutil.islfilesrepo(repo):
1120 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1121 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1121 # Get the list of missing largefiles so we can remove them
1122 # Get the list of missing largefiles so we can remove them
1122 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1123 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1123 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1124 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1124 False, False, False)
1125 False, False, False)
1125
1126
1126 # Call into the normal remove code, but the removing of the standin, we want
1127 # Call into the normal remove code, but the removing of the standin, we want
1127 # to have handled by original addremove. Monkey patching here makes sure
1128 # to have handled by original addremove. Monkey patching here makes sure
1128 # we don't remove the standin in the largefiles code, preventing a very
1129 # we don't remove the standin in the largefiles code, preventing a very
1129 # confused state later.
1130 # confused state later.
1130 if s.deleted:
1131 if s.deleted:
1131 m = copy.copy(matcher)
1132 m = copy.copy(matcher)
1132
1133
1133 # The m._files and m._map attributes are not changed to the deleted list
1134 # The m._files and m._map attributes are not changed to the deleted list
1134 # because that affects the m.exact() test, which in turn governs whether
1135 # because that affects the m.exact() test, which in turn governs whether
1135 # or not the file name is printed, and how. Simply limit the original
1136 # or not the file name is printed, and how. Simply limit the original
1136 # matches to those in the deleted status list.
1137 # matches to those in the deleted status list.
1137 matchfn = m.matchfn
1138 matchfn = m.matchfn
1138 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1139 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1139
1140
1140 removelargefiles(repo.ui, repo, True, m, **opts)
1141 removelargefiles(repo.ui, repo, True, m, **opts)
1141 # Call into the normal add code, and any files that *should* be added as
1142 # Call into the normal add code, and any files that *should* be added as
1142 # largefiles will be
1143 # largefiles will be
1143 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1144 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1144 # Now that we've handled largefiles, hand off to the original addremove
1145 # Now that we've handled largefiles, hand off to the original addremove
1145 # function to take care of the rest. Make sure it doesn't do anything with
1146 # function to take care of the rest. Make sure it doesn't do anything with
1146 # largefiles by passing a matcher that will ignore them.
1147 # largefiles by passing a matcher that will ignore them.
1147 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1148 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1148 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1149 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1149
1150
1150 # Calling purge with --all will cause the largefiles to be deleted.
1151 # Calling purge with --all will cause the largefiles to be deleted.
1151 # Override repo.status to prevent this from happening.
1152 # Override repo.status to prevent this from happening.
1152 def overridepurge(orig, ui, repo, *dirs, **opts):
1153 def overridepurge(orig, ui, repo, *dirs, **opts):
1153 # XXX Monkey patching a repoview will not work. The assigned attribute will
1154 # XXX Monkey patching a repoview will not work. The assigned attribute will
1154 # be set on the unfiltered repo, but we will only lookup attributes in the
1155 # be set on the unfiltered repo, but we will only lookup attributes in the
1155 # unfiltered repo if the lookup in the repoview object itself fails. As the
1156 # unfiltered repo if the lookup in the repoview object itself fails. As the
1156 # monkey patched method exists on the repoview class the lookup will not
1157 # monkey patched method exists on the repoview class the lookup will not
1157 # fail. As a result, the original version will shadow the monkey patched
1158 # fail. As a result, the original version will shadow the monkey patched
1158 # one, defeating the monkey patch.
1159 # one, defeating the monkey patch.
1159 #
1160 #
1160 # As a work around we use an unfiltered repo here. We should do something
1161 # As a work around we use an unfiltered repo here. We should do something
1161 # cleaner instead.
1162 # cleaner instead.
1162 repo = repo.unfiltered()
1163 repo = repo.unfiltered()
1163 oldstatus = repo.status
1164 oldstatus = repo.status
1164 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1165 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1165 clean=False, unknown=False, listsubrepos=False):
1166 clean=False, unknown=False, listsubrepos=False):
1166 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1167 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1167 listsubrepos)
1168 listsubrepos)
1168 lfdirstate = lfutil.openlfdirstate(ui, repo)
1169 lfdirstate = lfutil.openlfdirstate(ui, repo)
1169 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1170 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1170 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1171 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1171 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1172 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1172 unknown, ignored, r.clean)
1173 unknown, ignored, r.clean)
1173 repo.status = overridestatus
1174 repo.status = overridestatus
1174 orig(ui, repo, *dirs, **opts)
1175 orig(ui, repo, *dirs, **opts)
1175 repo.status = oldstatus
1176 repo.status = oldstatus
1176 def overriderollback(orig, ui, repo, **opts):
1177 def overriderollback(orig, ui, repo, **opts):
1177 wlock = repo.wlock()
1178 wlock = repo.wlock()
1178 try:
1179 try:
1179 before = repo.dirstate.parents()
1180 before = repo.dirstate.parents()
1180 orphans = set(f for f in repo.dirstate
1181 orphans = set(f for f in repo.dirstate
1181 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1182 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1182 result = orig(ui, repo, **opts)
1183 result = orig(ui, repo, **opts)
1183 after = repo.dirstate.parents()
1184 after = repo.dirstate.parents()
1184 if before == after:
1185 if before == after:
1185 return result # no need to restore standins
1186 return result # no need to restore standins
1186
1187
1187 pctx = repo['.']
1188 pctx = repo['.']
1188 for f in repo.dirstate:
1189 for f in repo.dirstate:
1189 if lfutil.isstandin(f):
1190 if lfutil.isstandin(f):
1190 orphans.discard(f)
1191 orphans.discard(f)
1191 if repo.dirstate[f] == 'r':
1192 if repo.dirstate[f] == 'r':
1192 repo.wvfs.unlinkpath(f, ignoremissing=True)
1193 repo.wvfs.unlinkpath(f, ignoremissing=True)
1193 elif f in pctx:
1194 elif f in pctx:
1194 fctx = pctx[f]
1195 fctx = pctx[f]
1195 repo.wwrite(f, fctx.data(), fctx.flags())
1196 repo.wwrite(f, fctx.data(), fctx.flags())
1196 else:
1197 else:
1197 # content of standin is not so important in 'a',
1198 # content of standin is not so important in 'a',
1198 # 'm' or 'n' (coming from the 2nd parent) cases
1199 # 'm' or 'n' (coming from the 2nd parent) cases
1199 lfutil.writestandin(repo, f, '', False)
1200 lfutil.writestandin(repo, f, '', False)
1200 for standin in orphans:
1201 for standin in orphans:
1201 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1202 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1202
1203
1203 lfdirstate = lfutil.openlfdirstate(ui, repo)
1204 lfdirstate = lfutil.openlfdirstate(ui, repo)
1204 orphans = set(lfdirstate)
1205 orphans = set(lfdirstate)
1205 lfiles = lfutil.listlfiles(repo)
1206 lfiles = lfutil.listlfiles(repo)
1206 for file in lfiles:
1207 for file in lfiles:
1207 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1208 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1208 orphans.discard(file)
1209 orphans.discard(file)
1209 for lfile in orphans:
1210 for lfile in orphans:
1210 lfdirstate.drop(lfile)
1211 lfdirstate.drop(lfile)
1211 lfdirstate.write()
1212 lfdirstate.write()
1212 finally:
1213 finally:
1213 wlock.release()
1214 wlock.release()
1214 return result
1215 return result
1215
1216
1216 def overridetransplant(orig, ui, repo, *revs, **opts):
1217 def overridetransplant(orig, ui, repo, *revs, **opts):
1217 resuming = opts.get('continue')
1218 resuming = opts.get('continue')
1218 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1219 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1219 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1220 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1220 try:
1221 try:
1221 result = orig(ui, repo, *revs, **opts)
1222 result = orig(ui, repo, *revs, **opts)
1222 finally:
1223 finally:
1223 repo._lfstatuswriters.pop()
1224 repo._lfstatuswriters.pop()
1224 repo._lfcommithooks.pop()
1225 repo._lfcommithooks.pop()
1225 return result
1226 return result
1226
1227
1227 def overridecat(orig, ui, repo, file1, *pats, **opts):
1228 def overridecat(orig, ui, repo, file1, *pats, **opts):
1228 ctx = scmutil.revsingle(repo, opts.get('rev'))
1229 ctx = scmutil.revsingle(repo, opts.get('rev'))
1229 err = 1
1230 err = 1
1230 notbad = set()
1231 notbad = set()
1231 m = scmutil.match(ctx, (file1,) + pats, opts)
1232 m = scmutil.match(ctx, (file1,) + pats, opts)
1232 origmatchfn = m.matchfn
1233 origmatchfn = m.matchfn
1233 def lfmatchfn(f):
1234 def lfmatchfn(f):
1234 if origmatchfn(f):
1235 if origmatchfn(f):
1235 return True
1236 return True
1236 lf = lfutil.splitstandin(f)
1237 lf = lfutil.splitstandin(f)
1237 if lf is None:
1238 if lf is None:
1238 return False
1239 return False
1239 notbad.add(lf)
1240 notbad.add(lf)
1240 return origmatchfn(lf)
1241 return origmatchfn(lf)
1241 m.matchfn = lfmatchfn
1242 m.matchfn = lfmatchfn
1242 origbadfn = m.bad
1243 origbadfn = m.bad
1243 def lfbadfn(f, msg):
1244 def lfbadfn(f, msg):
1244 if not f in notbad:
1245 if not f in notbad:
1245 origbadfn(f, msg)
1246 origbadfn(f, msg)
1246 m.bad = lfbadfn
1247 m.bad = lfbadfn
1247 for f in ctx.walk(m):
1248 for f in ctx.walk(m):
1248 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1249 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1249 pathname=f)
1250 pathname=f)
1250 lf = lfutil.splitstandin(f)
1251 lf = lfutil.splitstandin(f)
1251 if lf is None or origmatchfn(f):
1252 if lf is None or origmatchfn(f):
1252 # duplicating unreachable code from commands.cat
1253 # duplicating unreachable code from commands.cat
1253 data = ctx[f].data()
1254 data = ctx[f].data()
1254 if opts.get('decode'):
1255 if opts.get('decode'):
1255 data = repo.wwritedata(f, data)
1256 data = repo.wwritedata(f, data)
1256 fp.write(data)
1257 fp.write(data)
1257 else:
1258 else:
1258 hash = lfutil.readstandin(repo, lf, ctx.rev())
1259 hash = lfutil.readstandin(repo, lf, ctx.rev())
1259 if not lfutil.inusercache(repo.ui, hash):
1260 if not lfutil.inusercache(repo.ui, hash):
1260 store = basestore._openstore(repo)
1261 store = basestore._openstore(repo)
1261 success, missing = store.get([(lf, hash)])
1262 success, missing = store.get([(lf, hash)])
1262 if len(success) != 1:
1263 if len(success) != 1:
1263 raise util.Abort(
1264 raise util.Abort(
1264 _('largefile %s is not in cache and could not be '
1265 _('largefile %s is not in cache and could not be '
1265 'downloaded') % lf)
1266 'downloaded') % lf)
1266 path = lfutil.usercachepath(repo.ui, hash)
1267 path = lfutil.usercachepath(repo.ui, hash)
1267 fpin = open(path, "rb")
1268 fpin = open(path, "rb")
1268 for chunk in util.filechunkiter(fpin, 128 * 1024):
1269 for chunk in util.filechunkiter(fpin, 128 * 1024):
1269 fp.write(chunk)
1270 fp.write(chunk)
1270 fpin.close()
1271 fpin.close()
1271 fp.close()
1272 fp.close()
1272 err = 0
1273 err = 0
1273 return err
1274 return err
1274
1275
1275 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1276 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1276 *args, **kwargs):
1277 *args, **kwargs):
1277 wlock = repo.wlock()
1278 wlock = repo.wlock()
1278 try:
1279 try:
1279 # branch | | |
1280 # branch | | |
1280 # merge | force | partial | action
1281 # merge | force | partial | action
1281 # -------+-------+---------+--------------
1282 # -------+-------+---------+--------------
1282 # x | x | x | linear-merge
1283 # x | x | x | linear-merge
1283 # o | x | x | branch-merge
1284 # o | x | x | branch-merge
1284 # x | o | x | overwrite (as clean update)
1285 # x | o | x | overwrite (as clean update)
1285 # o | o | x | force-branch-merge (*1)
1286 # o | o | x | force-branch-merge (*1)
1286 # x | x | o | (*)
1287 # x | x | o | (*)
1287 # o | x | o | (*)
1288 # o | x | o | (*)
1288 # x | o | o | overwrite (as revert)
1289 # x | o | o | overwrite (as revert)
1289 # o | o | o | (*)
1290 # o | o | o | (*)
1290 #
1291 #
1291 # (*) don't care
1292 # (*) don't care
1292 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1293 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1293
1294
1294 linearmerge = not branchmerge and not force and not partial
1295 linearmerge = not branchmerge and not force and not partial
1295
1296
1296 if linearmerge or (branchmerge and force and not partial):
1297 if linearmerge or (branchmerge and force and not partial):
1297 # update standins for linear-merge or force-branch-merge,
1298 # update standins for linear-merge or force-branch-merge,
1298 # because largefiles in the working directory may be modified
1299 # because largefiles in the working directory may be modified
1299 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1300 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1300 unsure, s = lfdirstate.status(match_.always(repo.root,
1301 unsure, s = lfdirstate.status(match_.always(repo.root,
1301 repo.getcwd()),
1302 repo.getcwd()),
1302 [], False, False, False)
1303 [], False, False, False)
1303 pctx = repo['.']
1304 pctx = repo['.']
1304 for lfile in unsure + s.modified:
1305 for lfile in unsure + s.modified:
1305 lfileabs = repo.wvfs.join(lfile)
1306 lfileabs = repo.wvfs.join(lfile)
1306 if not os.path.exists(lfileabs):
1307 if not os.path.exists(lfileabs):
1307 continue
1308 continue
1308 lfhash = lfutil.hashrepofile(repo, lfile)
1309 lfhash = lfutil.hashrepofile(repo, lfile)
1309 standin = lfutil.standin(lfile)
1310 standin = lfutil.standin(lfile)
1310 lfutil.writestandin(repo, standin, lfhash,
1311 lfutil.writestandin(repo, standin, lfhash,
1311 lfutil.getexecutable(lfileabs))
1312 lfutil.getexecutable(lfileabs))
1312 if (standin in pctx and
1313 if (standin in pctx and
1313 lfhash == lfutil.readstandin(repo, lfile, '.')):
1314 lfhash == lfutil.readstandin(repo, lfile, '.')):
1314 lfdirstate.normal(lfile)
1315 lfdirstate.normal(lfile)
1315 for lfile in s.added:
1316 for lfile in s.added:
1316 lfutil.updatestandin(repo, lfutil.standin(lfile))
1317 lfutil.updatestandin(repo, lfutil.standin(lfile))
1317 lfdirstate.write()
1318 lfdirstate.write()
1318
1319
1319 if linearmerge:
1320 if linearmerge:
1320 # Only call updatelfiles on the standins that have changed
1321 # Only call updatelfiles on the standins that have changed
1321 # to save time
1322 # to save time
1322 oldstandins = lfutil.getstandinsstate(repo)
1323 oldstandins = lfutil.getstandinsstate(repo)
1323
1324
1324 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1325 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1325
1326
1326 filelist = None
1327 filelist = None
1327 if linearmerge:
1328 if linearmerge:
1328 newstandins = lfutil.getstandinsstate(repo)
1329 newstandins = lfutil.getstandinsstate(repo)
1329 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1330 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1330
1331
1331 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1332 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1332 normallookup=partial, checked=linearmerge)
1333 normallookup=partial, checked=linearmerge)
1333
1334
1334 return result
1335 return result
1335 finally:
1336 finally:
1336 wlock.release()
1337 wlock.release()
1337
1338
1338 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1339 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1339 result = orig(repo, files, *args, **kwargs)
1340 result = orig(repo, files, *args, **kwargs)
1340
1341
1341 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1342 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1342 if filelist:
1343 if filelist:
1343 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1344 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1344 printmessage=False, normallookup=True)
1345 printmessage=False, normallookup=True)
1345
1346
1346 return result
1347 return result
@@ -1,647 +1,653 b''
1 $ hg init
1 $ hg init
2 $ mkdir d1 d1/d11 d2
2 $ mkdir d1 d1/d11 d2
3 $ echo d1/a > d1/a
3 $ echo d1/a > d1/a
4 $ echo d1/ba > d1/ba
4 $ echo d1/ba > d1/ba
5 $ echo d1/a1 > d1/d11/a1
5 $ echo d1/a1 > d1/d11/a1
6 $ echo d1/b > d1/b
6 $ echo d1/b > d1/b
7 $ echo d2/b > d2/b
7 $ echo d2/b > d2/b
8 $ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
8 $ hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
9 $ hg commit -m "1"
9 $ hg commit -m "1"
10
10
11 rename a single file
11 rename a single file
12
12
13 $ hg rename d1/d11/a1 d2/c
13 $ hg rename d1/d11/a1 d2/c
14 $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
14 $ hg --config ui.portablefilenames=abort rename d1/a d1/con.xml
15 abort: filename contains 'con', which is reserved on Windows: 'd1/con.xml'
15 abort: filename contains 'con', which is reserved on Windows: 'd1/con.xml'
16 [255]
16 [255]
17 $ hg sum
17 $ hg sum
18 parent: 0:9b4b6e7b2c26 tip
18 parent: 0:9b4b6e7b2c26 tip
19 1
19 1
20 branch: default
20 branch: default
21 commit: 1 renamed
21 commit: 1 renamed
22 update: (current)
22 update: (current)
23 $ hg status -C
23 $ hg status -C
24 A d2/c
24 A d2/c
25 d1/d11/a1
25 d1/d11/a1
26 R d1/d11/a1
26 R d1/d11/a1
27 $ hg update -C
27 $ hg update -C
28 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 $ rm d2/c
29 $ rm d2/c
30
30
31 rename a single file using absolute paths
31 rename a single file using absolute paths
32
32
33 $ hg rename `pwd`/d1/d11/a1 `pwd`/d2/c
33 $ hg rename `pwd`/d1/d11/a1 `pwd`/d2/c
34 $ hg status -C
34 $ hg status -C
35 A d2/c
35 A d2/c
36 d1/d11/a1
36 d1/d11/a1
37 R d1/d11/a1
37 R d1/d11/a1
38 $ hg update -C
38 $ hg update -C
39 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
39 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
40 $ rm d2/c
40 $ rm d2/c
41
41
42 rename --after a single file
42 rename --after a single file
43
43
44 $ mv d1/d11/a1 d2/c
44 $ mv d1/d11/a1 d2/c
45 $ hg rename --after d1/d11/a1 d2/c
45 $ hg rename --after d1/d11/a1 d2/c
46 $ hg status -C
46 $ hg status -C
47 A d2/c
47 A d2/c
48 d1/d11/a1
48 d1/d11/a1
49 R d1/d11/a1
49 R d1/d11/a1
50 $ hg update -C
50 $ hg update -C
51 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
51 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
52 $ rm d2/c
52 $ rm d2/c
53
53
54 rename --after a single file when src and tgt already tracked
54 rename --after a single file when src and tgt already tracked
55
55
56 $ mv d1/d11/a1 d2/c
56 $ mv d1/d11/a1 d2/c
57 $ hg addrem -s 0
57 $ hg addrem -s 0
58 removing d1/d11/a1
58 removing d1/d11/a1
59 adding d2/c
59 adding d2/c
60 $ hg rename --after d1/d11/a1 d2/c
60 $ hg rename --after d1/d11/a1 d2/c
61 $ hg status -C
61 $ hg status -C
62 A d2/c
62 A d2/c
63 d1/d11/a1
63 d1/d11/a1
64 R d1/d11/a1
64 R d1/d11/a1
65 $ hg update -C
65 $ hg update -C
66 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 $ rm d2/c
67 $ rm d2/c
68
68
69 rename --after a single file to a nonexistent target filename
69 rename --after a single file to a nonexistent target filename
70
70
71 $ hg rename --after d1/a dummy
71 $ hg rename --after d1/a dummy
72 d1/a: not recording move - dummy does not exist (glob)
72 d1/a: not recording move - dummy does not exist (glob)
73
73
74 move a single file to an existing directory
74 move a single file to an existing directory
75
75
76 $ hg rename d1/d11/a1 d2
76 $ hg rename d1/d11/a1 d2
77 $ hg status -C
77 $ hg status -C
78 A d2/a1
78 A d2/a1
79 d1/d11/a1
79 d1/d11/a1
80 R d1/d11/a1
80 R d1/d11/a1
81 $ hg update -C
81 $ hg update -C
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
82 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
83 $ rm d2/a1
83 $ rm d2/a1
84
84
85 move --after a single file to an existing directory
85 move --after a single file to an existing directory
86
86
87 $ mv d1/d11/a1 d2
87 $ mv d1/d11/a1 d2
88 $ hg rename --after d1/d11/a1 d2
88 $ hg rename --after d1/d11/a1 d2
89 $ hg status -C
89 $ hg status -C
90 A d2/a1
90 A d2/a1
91 d1/d11/a1
91 d1/d11/a1
92 R d1/d11/a1
92 R d1/d11/a1
93 $ hg update -C
93 $ hg update -C
94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
95 $ rm d2/a1
95 $ rm d2/a1
96
96
97 rename a file using a relative path
97 rename a file using a relative path
98
98
99 $ (cd d1/d11; hg rename ../../d2/b e)
99 $ (cd d1/d11; hg rename ../../d2/b e)
100 $ hg status -C
100 $ hg status -C
101 A d1/d11/e
101 A d1/d11/e
102 d2/b
102 d2/b
103 R d2/b
103 R d2/b
104 $ hg update -C
104 $ hg update -C
105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
106 $ rm d1/d11/e
106 $ rm d1/d11/e
107
107
108 rename --after a file using a relative path
108 rename --after a file using a relative path
109
109
110 $ (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
110 $ (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
111 $ hg status -C
111 $ hg status -C
112 A d1/d11/e
112 A d1/d11/e
113 d2/b
113 d2/b
114 R d2/b
114 R d2/b
115 $ hg update -C
115 $ hg update -C
116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 $ rm d1/d11/e
117 $ rm d1/d11/e
118
118
119 rename directory d1 as d3
119 rename directory d1 as d3
120
120
121 $ hg rename d1/ d3
121 $ hg rename d1/ d3
122 moving d1/a to d3/a (glob)
122 moving d1/a to d3/a (glob)
123 moving d1/b to d3/b (glob)
123 moving d1/b to d3/b (glob)
124 moving d1/ba to d3/ba (glob)
124 moving d1/ba to d3/ba (glob)
125 moving d1/d11/a1 to d3/d11/a1 (glob)
125 moving d1/d11/a1 to d3/d11/a1 (glob)
126 $ hg status -C
126 $ hg status -C
127 A d3/a
127 A d3/a
128 d1/a
128 d1/a
129 A d3/b
129 A d3/b
130 d1/b
130 d1/b
131 A d3/ba
131 A d3/ba
132 d1/ba
132 d1/ba
133 A d3/d11/a1
133 A d3/d11/a1
134 d1/d11/a1
134 d1/d11/a1
135 R d1/a
135 R d1/a
136 R d1/b
136 R d1/b
137 R d1/ba
137 R d1/ba
138 R d1/d11/a1
138 R d1/d11/a1
139 $ hg update -C
139 $ hg update -C
140 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
140 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
141 $ rm -rf d3
141 $ rm -rf d3
142
142
143 rename --after directory d1 as d3
143 rename --after directory d1 as d3
144
144
145 $ mv d1 d3
145 $ mv d1 d3
146 $ hg rename --after d1 d3
146 $ hg rename --after d1 d3
147 moving d1/a to d3/a (glob)
147 moving d1/a to d3/a (glob)
148 moving d1/b to d3/b (glob)
148 moving d1/b to d3/b (glob)
149 moving d1/ba to d3/ba (glob)
149 moving d1/ba to d3/ba (glob)
150 moving d1/d11/a1 to d3/d11/a1 (glob)
150 moving d1/d11/a1 to d3/d11/a1 (glob)
151 $ hg status -C
151 $ hg status -C
152 A d3/a
152 A d3/a
153 d1/a
153 d1/a
154 A d3/b
154 A d3/b
155 d1/b
155 d1/b
156 A d3/ba
156 A d3/ba
157 d1/ba
157 d1/ba
158 A d3/d11/a1
158 A d3/d11/a1
159 d1/d11/a1
159 d1/d11/a1
160 R d1/a
160 R d1/a
161 R d1/b
161 R d1/b
162 R d1/ba
162 R d1/ba
163 R d1/d11/a1
163 R d1/d11/a1
164 $ hg update -C
164 $ hg update -C
165 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
165 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
166 $ rm -rf d3
166 $ rm -rf d3
167
167
168 move a directory using a relative path
168 move a directory using a relative path
169
169
170 $ (cd d2; mkdir d3; hg rename ../d1/d11 d3)
170 $ (cd d2; mkdir d3; hg rename ../d1/d11 d3)
171 moving ../d1/d11/a1 to d3/d11/a1 (glob)
171 moving ../d1/d11/a1 to d3/d11/a1 (glob)
172 $ hg status -C
172 $ hg status -C
173 A d2/d3/d11/a1
173 A d2/d3/d11/a1
174 d1/d11/a1
174 d1/d11/a1
175 R d1/d11/a1
175 R d1/d11/a1
176 $ hg update -C
176 $ hg update -C
177 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
177 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 $ rm -rf d2/d3
178 $ rm -rf d2/d3
179
179
180 move --after a directory using a relative path
180 move --after a directory using a relative path
181
181
182 $ (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
182 $ (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
183 moving ../d1/d11/a1 to d3/d11/a1 (glob)
183 moving ../d1/d11/a1 to d3/d11/a1 (glob)
184 $ hg status -C
184 $ hg status -C
185 A d2/d3/d11/a1
185 A d2/d3/d11/a1
186 d1/d11/a1
186 d1/d11/a1
187 R d1/d11/a1
187 R d1/d11/a1
188 $ hg update -C
188 $ hg update -C
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 $ rm -rf d2/d3
190 $ rm -rf d2/d3
191
191
192 move directory d1/d11 to an existing directory d2 (removes empty d1)
192 move directory d1/d11 to an existing directory d2 (removes empty d1)
193
193
194 $ hg rename d1/d11/ d2
194 $ hg rename d1/d11/ d2
195 moving d1/d11/a1 to d2/d11/a1 (glob)
195 moving d1/d11/a1 to d2/d11/a1 (glob)
196 $ hg status -C
196 $ hg status -C
197 A d2/d11/a1
197 A d2/d11/a1
198 d1/d11/a1
198 d1/d11/a1
199 R d1/d11/a1
199 R d1/d11/a1
200 $ hg update -C
200 $ hg update -C
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
201 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 $ rm -rf d2/d11
202 $ rm -rf d2/d11
203
203
204 move directories d1 and d2 to a new directory d3
204 move directories d1 and d2 to a new directory d3
205
205
206 $ mkdir d3
206 $ mkdir d3
207 $ hg rename d1 d2 d3
207 $ hg rename d1 d2 d3
208 moving d1/a to d3/d1/a (glob)
208 moving d1/a to d3/d1/a (glob)
209 moving d1/b to d3/d1/b (glob)
209 moving d1/b to d3/d1/b (glob)
210 moving d1/ba to d3/d1/ba (glob)
210 moving d1/ba to d3/d1/ba (glob)
211 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
211 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
212 moving d2/b to d3/d2/b (glob)
212 moving d2/b to d3/d2/b (glob)
213 $ hg status -C
213 $ hg status -C
214 A d3/d1/a
214 A d3/d1/a
215 d1/a
215 d1/a
216 A d3/d1/b
216 A d3/d1/b
217 d1/b
217 d1/b
218 A d3/d1/ba
218 A d3/d1/ba
219 d1/ba
219 d1/ba
220 A d3/d1/d11/a1
220 A d3/d1/d11/a1
221 d1/d11/a1
221 d1/d11/a1
222 A d3/d2/b
222 A d3/d2/b
223 d2/b
223 d2/b
224 R d1/a
224 R d1/a
225 R d1/b
225 R d1/b
226 R d1/ba
226 R d1/ba
227 R d1/d11/a1
227 R d1/d11/a1
228 R d2/b
228 R d2/b
229 $ hg update -C
229 $ hg update -C
230 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
230 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
231 $ rm -rf d3
231 $ rm -rf d3
232
232
233 move --after directories d1 and d2 to a new directory d3
233 move --after directories d1 and d2 to a new directory d3
234
234
235 $ mkdir d3
235 $ mkdir d3
236 $ mv d1 d2 d3
236 $ mv d1 d2 d3
237 $ hg rename --after d1 d2 d3
237 $ hg rename --after d1 d2 d3
238 moving d1/a to d3/d1/a (glob)
238 moving d1/a to d3/d1/a (glob)
239 moving d1/b to d3/d1/b (glob)
239 moving d1/b to d3/d1/b (glob)
240 moving d1/ba to d3/d1/ba (glob)
240 moving d1/ba to d3/d1/ba (glob)
241 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
241 moving d1/d11/a1 to d3/d1/d11/a1 (glob)
242 moving d2/b to d3/d2/b (glob)
242 moving d2/b to d3/d2/b (glob)
243 $ hg status -C
243 $ hg status -C
244 A d3/d1/a
244 A d3/d1/a
245 d1/a
245 d1/a
246 A d3/d1/b
246 A d3/d1/b
247 d1/b
247 d1/b
248 A d3/d1/ba
248 A d3/d1/ba
249 d1/ba
249 d1/ba
250 A d3/d1/d11/a1
250 A d3/d1/d11/a1
251 d1/d11/a1
251 d1/d11/a1
252 A d3/d2/b
252 A d3/d2/b
253 d2/b
253 d2/b
254 R d1/a
254 R d1/a
255 R d1/b
255 R d1/b
256 R d1/ba
256 R d1/ba
257 R d1/d11/a1
257 R d1/d11/a1
258 R d2/b
258 R d2/b
259 $ hg update -C
259 $ hg update -C
260 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
260 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
261 $ rm -rf d3
261 $ rm -rf d3
262
262
263 move everything under directory d1 to existing directory d2, do not
263 move everything under directory d1 to existing directory d2, do not
264 overwrite existing files (d2/b)
264 overwrite existing files (d2/b)
265
265
266 $ hg rename d1/* d2
266 $ hg rename d1/* d2
267 d2/b: not overwriting - file exists
267 d2/b: not overwriting - file exists
268 moving d1/d11/a1 to d2/d11/a1 (glob)
268 moving d1/d11/a1 to d2/d11/a1 (glob)
269 $ hg status -C
269 $ hg status -C
270 A d2/a
270 A d2/a
271 d1/a
271 d1/a
272 A d2/ba
272 A d2/ba
273 d1/ba
273 d1/ba
274 A d2/d11/a1
274 A d2/d11/a1
275 d1/d11/a1
275 d1/d11/a1
276 R d1/a
276 R d1/a
277 R d1/ba
277 R d1/ba
278 R d1/d11/a1
278 R d1/d11/a1
279 $ diff -u d1/b d2/b
279 $ diff -u d1/b d2/b
280 --- d1/b * (glob)
280 --- d1/b * (glob)
281 +++ d2/b * (glob)
281 +++ d2/b * (glob)
282 @@ * (glob)
282 @@ * (glob)
283 -d1/b
283 -d1/b
284 +d2/b
284 +d2/b
285 [1]
285 [1]
286 $ hg update -C
286 $ hg update -C
287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
288 $ rm d2/a d2/ba d2/d11/a1
288 $ rm d2/a d2/ba d2/d11/a1
289
289
290 attempt to move one file into a non-existent directory
290 attempt to move one file into a non-existent directory
291
291
292 $ hg rename d1/a dx/
292 $ hg rename d1/a dx/
293 abort: destination dx/ is not a directory
293 abort: destination dx/ is not a directory
294 [255]
294 [255]
295 $ hg status -C
295 $ hg status -C
296 $ hg update -C
296 $ hg update -C
297 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
297 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
298
298
299 attempt to move potentially more than one file into a non-existent directory
299 attempt to move potentially more than one file into a non-existent directory
300
300
301 $ hg rename 'glob:d1/**' dx
301 $ hg rename 'glob:d1/**' dx
302 abort: with multiple sources, destination must be an existing directory
302 abort: with multiple sources, destination must be an existing directory
303 [255]
303 [255]
304
304
305 move every file under d1 to d2/d21 (glob)
305 move every file under d1 to d2/d21 (glob)
306
306
307 $ mkdir d2/d21
307 $ mkdir d2/d21
308 $ hg rename 'glob:d1/**' d2/d21
308 $ hg rename 'glob:d1/**' d2/d21
309 moving d1/a to d2/d21/a (glob)
309 moving d1/a to d2/d21/a (glob)
310 moving d1/b to d2/d21/b (glob)
310 moving d1/b to d2/d21/b (glob)
311 moving d1/ba to d2/d21/ba (glob)
311 moving d1/ba to d2/d21/ba (glob)
312 moving d1/d11/a1 to d2/d21/a1 (glob)
312 moving d1/d11/a1 to d2/d21/a1 (glob)
313 $ hg status -C
313 $ hg status -C
314 A d2/d21/a
314 A d2/d21/a
315 d1/a
315 d1/a
316 A d2/d21/a1
316 A d2/d21/a1
317 d1/d11/a1
317 d1/d11/a1
318 A d2/d21/b
318 A d2/d21/b
319 d1/b
319 d1/b
320 A d2/d21/ba
320 A d2/d21/ba
321 d1/ba
321 d1/ba
322 R d1/a
322 R d1/a
323 R d1/b
323 R d1/b
324 R d1/ba
324 R d1/ba
325 R d1/d11/a1
325 R d1/d11/a1
326 $ hg update -C
326 $ hg update -C
327 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
328 $ rm -rf d2/d21
328 $ rm -rf d2/d21
329
329
330 move --after some files under d1 to d2/d21 (glob)
330 move --after some files under d1 to d2/d21 (glob)
331
331
332 $ mkdir d2/d21
332 $ mkdir d2/d21
333 $ mv d1/a d1/d11/a1 d2/d21
333 $ mv d1/a d1/d11/a1 d2/d21
334 $ hg rename --after 'glob:d1/**' d2/d21
334 $ hg rename --after 'glob:d1/**' d2/d21
335 moving d1/a to d2/d21/a (glob)
335 moving d1/a to d2/d21/a (glob)
336 d1/b: not recording move - d2/d21/b does not exist (glob)
336 d1/b: not recording move - d2/d21/b does not exist (glob)
337 d1/ba: not recording move - d2/d21/ba does not exist (glob)
337 d1/ba: not recording move - d2/d21/ba does not exist (glob)
338 moving d1/d11/a1 to d2/d21/a1 (glob)
338 moving d1/d11/a1 to d2/d21/a1 (glob)
339 $ hg status -C
339 $ hg status -C
340 A d2/d21/a
340 A d2/d21/a
341 d1/a
341 d1/a
342 A d2/d21/a1
342 A d2/d21/a1
343 d1/d11/a1
343 d1/d11/a1
344 R d1/a
344 R d1/a
345 R d1/d11/a1
345 R d1/d11/a1
346 $ hg update -C
346 $ hg update -C
347 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 $ rm -rf d2/d21
348 $ rm -rf d2/d21
349
349
350 move every file under d1 starting with an 'a' to d2/d21 (regexp)
350 move every file under d1 starting with an 'a' to d2/d21 (regexp)
351
351
352 $ mkdir d2/d21
352 $ mkdir d2/d21
353 $ hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
353 $ hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
354 moving d1/a to d2/d21/a (glob)
354 moving d1/a to d2/d21/a (glob)
355 moving d1/d11/a1 to d2/d21/a1 (glob)
355 moving d1/d11/a1 to d2/d21/a1 (glob)
356 $ hg status -C
356 $ hg status -C
357 A d2/d21/a
357 A d2/d21/a
358 d1/a
358 d1/a
359 A d2/d21/a1
359 A d2/d21/a1
360 d1/d11/a1
360 d1/d11/a1
361 R d1/a
361 R d1/a
362 R d1/d11/a1
362 R d1/d11/a1
363 $ hg update -C
363 $ hg update -C
364 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
364 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 $ rm -rf d2/d21
365 $ rm -rf d2/d21
366
366
367 attempt to overwrite an existing file
367 attempt to overwrite an existing file
368
368
369 $ echo "ca" > d1/ca
369 $ echo "ca" > d1/ca
370 $ hg rename d1/ba d1/ca
370 $ hg rename d1/ba d1/ca
371 d1/ca: not overwriting - file exists
371 d1/ca: not overwriting - file exists
372 $ hg status -C
372 $ hg status -C
373 ? d1/ca
373 ? d1/ca
374 $ hg update -C
374 $ hg update -C
375 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
375 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
376
376
377 forced overwrite of an existing file
377 forced overwrite of an existing file
378
378
379 $ echo "ca" > d1/ca
379 $ echo "ca" > d1/ca
380 $ hg rename --force d1/ba d1/ca
380 $ hg rename --force d1/ba d1/ca
381 $ hg status -C
381 $ hg status -C
382 A d1/ca
382 A d1/ca
383 d1/ba
383 d1/ba
384 R d1/ba
384 R d1/ba
385 $ hg update -C
385 $ hg update -C
386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
386 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
387 $ rm d1/ca
387 $ rm d1/ca
388
388
389 attempt to overwrite an existing broken symlink
389 attempt to overwrite an existing broken symlink
390
390
391 #if symlink
391 #if symlink
392 $ ln -s ba d1/ca
392 $ ln -s ba d1/ca
393 $ hg rename --traceback d1/ba d1/ca
393 $ hg rename --traceback d1/ba d1/ca
394 d1/ca: not overwriting - file exists
394 d1/ca: not overwriting - file exists
395 $ hg status -C
395 $ hg status -C
396 ? d1/ca
396 ? d1/ca
397 $ hg update -C
397 $ hg update -C
398 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
398 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 $ rm d1/ca
399 $ rm d1/ca
400
400
401 replace a symlink with a file
401 replace a symlink with a file
402
402
403 $ ln -s ba d1/ca
403 $ ln -s ba d1/ca
404 $ hg rename --force d1/ba d1/ca
404 $ hg rename --force d1/ba d1/ca
405 $ hg status -C
405 $ hg status -C
406 A d1/ca
406 A d1/ca
407 d1/ba
407 d1/ba
408 R d1/ba
408 R d1/ba
409 $ hg update -C
409 $ hg update -C
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
410 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
411 $ rm d1/ca
411 $ rm d1/ca
412 #endif
412 #endif
413
413
414 do not copy more than one source file to the same destination file
414 do not copy more than one source file to the same destination file
415
415
416 $ mkdir d3
416 $ mkdir d3
417 $ hg rename d1/* d2/* d3
417 $ hg rename d1/* d2/* d3
418 moving d1/d11/a1 to d3/d11/a1 (glob)
418 moving d1/d11/a1 to d3/d11/a1 (glob)
419 d3/b: not overwriting - d2/b collides with d1/b
419 d3/b: not overwriting - d2/b collides with d1/b
420 $ hg status -C
420 $ hg status -C
421 A d3/a
421 A d3/a
422 d1/a
422 d1/a
423 A d3/b
423 A d3/b
424 d1/b
424 d1/b
425 A d3/ba
425 A d3/ba
426 d1/ba
426 d1/ba
427 A d3/d11/a1
427 A d3/d11/a1
428 d1/d11/a1
428 d1/d11/a1
429 R d1/a
429 R d1/a
430 R d1/b
430 R d1/b
431 R d1/ba
431 R d1/ba
432 R d1/d11/a1
432 R d1/d11/a1
433 $ hg update -C
433 $ hg update -C
434 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
434 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
435 $ rm -rf d3
435 $ rm -rf d3
436
436
437 move a whole subtree with "hg rename ."
437 move a whole subtree with "hg rename ."
438
438
439 $ mkdir d3
439 $ mkdir d3
440 $ (cd d1; hg rename . ../d3)
440 $ (cd d1; hg rename . ../d3)
441 moving a to ../d3/d1/a
441 moving a to ../d3/d1/a
442 moving b to ../d3/d1/b
442 moving b to ../d3/d1/b
443 moving ba to ../d3/d1/ba
443 moving ba to ../d3/d1/ba
444 moving d11/a1 to ../d3/d1/d11/a1 (glob)
444 moving d11/a1 to ../d3/d1/d11/a1 (glob)
445 $ hg status -C
445 $ hg status -C
446 A d3/d1/a
446 A d3/d1/a
447 d1/a
447 d1/a
448 A d3/d1/b
448 A d3/d1/b
449 d1/b
449 d1/b
450 A d3/d1/ba
450 A d3/d1/ba
451 d1/ba
451 d1/ba
452 A d3/d1/d11/a1
452 A d3/d1/d11/a1
453 d1/d11/a1
453 d1/d11/a1
454 R d1/a
454 R d1/a
455 R d1/b
455 R d1/b
456 R d1/ba
456 R d1/ba
457 R d1/d11/a1
457 R d1/d11/a1
458 $ hg update -C
458 $ hg update -C
459 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
459 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
460 $ rm -rf d3
460 $ rm -rf d3
461
461
462 move a whole subtree with "hg rename --after ."
462 move a whole subtree with "hg rename --after ."
463
463
464 $ mkdir d3
464 $ mkdir d3
465 $ mv d1/* d3
465 $ mv d1/* d3
466 $ (cd d1; hg rename --after . ../d3)
466 $ (cd d1; hg rename --after . ../d3)
467 moving a to ../d3/a
467 moving a to ../d3/a
468 moving b to ../d3/b
468 moving b to ../d3/b
469 moving ba to ../d3/ba
469 moving ba to ../d3/ba
470 moving d11/a1 to ../d3/d11/a1 (glob)
470 moving d11/a1 to ../d3/d11/a1 (glob)
471 $ hg status -C
471 $ hg status -C
472 A d3/a
472 A d3/a
473 d1/a
473 d1/a
474 A d3/b
474 A d3/b
475 d1/b
475 d1/b
476 A d3/ba
476 A d3/ba
477 d1/ba
477 d1/ba
478 A d3/d11/a1
478 A d3/d11/a1
479 d1/d11/a1
479 d1/d11/a1
480 R d1/a
480 R d1/a
481 R d1/b
481 R d1/b
482 R d1/ba
482 R d1/ba
483 R d1/d11/a1
483 R d1/d11/a1
484 $ hg update -C
484 $ hg update -C
485 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
485 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
486 $ rm -rf d3
486 $ rm -rf d3
487
487
488 move the parent tree with "hg rename .."
488 move the parent tree with "hg rename .."
489
489
490 $ (cd d1/d11; hg rename .. ../../d3)
490 $ (cd d1/d11; hg rename .. ../../d3)
491 moving ../a to ../../d3/a (glob)
491 moving ../a to ../../d3/a (glob)
492 moving ../b to ../../d3/b (glob)
492 moving ../b to ../../d3/b (glob)
493 moving ../ba to ../../d3/ba (glob)
493 moving ../ba to ../../d3/ba (glob)
494 moving a1 to ../../d3/d11/a1
494 moving a1 to ../../d3/d11/a1
495 $ hg status -C
495 $ hg status -C
496 A d3/a
496 A d3/a
497 d1/a
497 d1/a
498 A d3/b
498 A d3/b
499 d1/b
499 d1/b
500 A d3/ba
500 A d3/ba
501 d1/ba
501 d1/ba
502 A d3/d11/a1
502 A d3/d11/a1
503 d1/d11/a1
503 d1/d11/a1
504 R d1/a
504 R d1/a
505 R d1/b
505 R d1/b
506 R d1/ba
506 R d1/ba
507 R d1/d11/a1
507 R d1/d11/a1
508 $ hg update -C
508 $ hg update -C
509 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
509 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
510 $ rm -rf d3
510 $ rm -rf d3
511
511
512 skip removed files
512 skip removed files
513
513
514 $ hg remove d1/b
514 $ hg remove d1/b
515 $ hg rename d1 d3
515 $ hg rename d1 d3
516 moving d1/a to d3/a (glob)
516 moving d1/a to d3/a (glob)
517 moving d1/ba to d3/ba (glob)
517 moving d1/ba to d3/ba (glob)
518 moving d1/d11/a1 to d3/d11/a1 (glob)
518 moving d1/d11/a1 to d3/d11/a1 (glob)
519 $ hg status -C
519 $ hg status -C
520 A d3/a
520 A d3/a
521 d1/a
521 d1/a
522 A d3/ba
522 A d3/ba
523 d1/ba
523 d1/ba
524 A d3/d11/a1
524 A d3/d11/a1
525 d1/d11/a1
525 d1/d11/a1
526 R d1/a
526 R d1/a
527 R d1/b
527 R d1/b
528 R d1/ba
528 R d1/ba
529 R d1/d11/a1
529 R d1/d11/a1
530 $ hg update -C
530 $ hg update -C
531 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
531 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
532 $ rm -rf d3
532 $ rm -rf d3
533
533
534 transitive rename
534 transitive rename
535
535
536 $ hg rename d1/b d1/bb
536 $ hg rename d1/b d1/bb
537 $ hg rename d1/bb d1/bc
537 $ hg rename d1/bb d1/bc
538 $ hg status -C
538 $ hg status -C
539 A d1/bc
539 A d1/bc
540 d1/b
540 d1/b
541 R d1/b
541 R d1/b
542 $ hg update -C
542 $ hg update -C
543 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
543 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
544 $ rm d1/bc
544 $ rm d1/bc
545
545
546 transitive rename --after
546 transitive rename --after
547
547
548 $ hg rename d1/b d1/bb
548 $ hg rename d1/b d1/bb
549 $ mv d1/bb d1/bc
549 $ mv d1/bb d1/bc
550 $ hg rename --after d1/bb d1/bc
550 $ hg rename --after d1/bb d1/bc
551 $ hg status -C
551 $ hg status -C
552 A d1/bc
552 A d1/bc
553 d1/b
553 d1/b
554 R d1/b
554 R d1/b
555 $ hg update -C
555 $ hg update -C
556 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
556 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
557 $ rm d1/bc
557 $ rm d1/bc
558
558
559 $ echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
559 $ echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
560 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
560 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
561 $ hg rename d1/b d1/bb
561 $ hg rename d1/b d1/bb
562 $ echo "some stuff added to d1/bb" >> d1/bb
562 $ echo "some stuff added to d1/bb" >> d1/bb
563 $ hg rename d1/bb d1/b
563 $ hg rename d1/bb d1/b
564 $ hg status -C
564 $ hg status -C
565 M d1/b
565 M d1/b
566 $ hg update -C
566 $ hg update -C
567 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
567 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
568
568
569 overwriting with renames (issue1959)
569 overwriting with renames (issue1959)
570
570
571 $ hg rename d1/a d1/c
571 $ hg rename d1/a d1/c
572 $ hg rename d1/b d1/a
572 $ hg rename d1/b d1/a
573 $ hg status -C
573 $ hg status -C
574 M d1/a
574 M d1/a
575 d1/b
575 d1/b
576 A d1/c
576 A d1/c
577 d1/a
577 d1/a
578 R d1/b
578 R d1/b
579 $ hg diff --git
579 $ hg diff --git
580 diff --git a/d1/a b/d1/a
580 diff --git a/d1/a b/d1/a
581 --- a/d1/a
581 --- a/d1/a
582 +++ b/d1/a
582 +++ b/d1/a
583 @@ -1,1 +1,1 @@
583 @@ -1,1 +1,1 @@
584 -d1/a
584 -d1/a
585 +d1/b
585 +d1/b
586 diff --git a/d1/b b/d1/b
586 diff --git a/d1/b b/d1/b
587 deleted file mode 100644
587 deleted file mode 100644
588 --- a/d1/b
588 --- a/d1/b
589 +++ /dev/null
589 +++ /dev/null
590 @@ -1,1 +0,0 @@
590 @@ -1,1 +0,0 @@
591 -d1/b
591 -d1/b
592 diff --git a/d1/a b/d1/c
592 diff --git a/d1/a b/d1/c
593 copy from d1/a
593 copy from d1/a
594 copy to d1/c
594 copy to d1/c
595 $ hg update -C
595 $ hg update -C
596 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
596 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
597 $ rm d1/c # The file was marked as added, so 'hg update' action was 'forget'
597 $ rm d1/c # The file was marked as added, so 'hg update' action was 'forget'
598
598
599 check illegal path components
599 check illegal path components
600
600
601 $ hg rename d1/d11/a1 .hg/foo
601 $ hg rename d1/d11/a1 .hg/foo
602 abort: path contains illegal component: .hg/foo (glob)
602 abort: path contains illegal component: .hg/foo (glob)
603 [255]
603 [255]
604 $ hg status -C
604 $ hg status -C
605 $ hg rename d1/d11/a1 ../foo
605 $ hg rename d1/d11/a1 ../foo
606 abort: ../foo not under root '$TESTTMP'
606 abort: ../foo not under root '$TESTTMP'
607 [255]
607 [255]
608 $ hg status -C
608 $ hg status -C
609
609
610 $ mv d1/d11/a1 .hg/foo
610 $ mv d1/d11/a1 .hg/foo
611 $ hg rename --after d1/d11/a1 .hg/foo
611 $ hg rename --after d1/d11/a1 .hg/foo
612 abort: path contains illegal component: .hg/foo (glob)
612 abort: path contains illegal component: .hg/foo (glob)
613 [255]
613 [255]
614 $ hg status -C
614 $ hg status -C
615 ! d1/d11/a1
615 ! d1/d11/a1
616 $ hg update -C
616 $ hg update -C
617 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
617 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
618 $ rm .hg/foo
618 $ rm .hg/foo
619
619
620 $ hg rename d1/d11/a1 .hg
620 $ hg rename d1/d11/a1 .hg
621 abort: path contains illegal component: .hg/a1 (glob)
621 abort: path contains illegal component: .hg/a1 (glob)
622 [255]
622 [255]
623 $ hg --config extensions.largefiles= rename d1/d11/a1 .hg
624 abort: path contains illegal component: .hg/a1 (glob)
625 [255]
623 $ hg status -C
626 $ hg status -C
624 $ hg rename d1/d11/a1 ..
627 $ hg rename d1/d11/a1 ..
625 abort: ../a1 not under root '$TESTTMP' (glob)
628 abort: ../a1 not under root '$TESTTMP' (glob)
626 [255]
629 [255]
630 $ hg --config extensions.largefiles= rename d1/d11/a1 ..
631 abort: ../a1 not under root '$TESTTMP' (glob)
632 [255]
627 $ hg status -C
633 $ hg status -C
628
634
629 $ mv d1/d11/a1 .hg
635 $ mv d1/d11/a1 .hg
630 $ hg rename --after d1/d11/a1 .hg
636 $ hg rename --after d1/d11/a1 .hg
631 abort: path contains illegal component: .hg/a1 (glob)
637 abort: path contains illegal component: .hg/a1 (glob)
632 [255]
638 [255]
633 $ hg status -C
639 $ hg status -C
634 ! d1/d11/a1
640 ! d1/d11/a1
635 $ hg update -C
641 $ hg update -C
636 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
642 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
637 $ rm .hg/a1
643 $ rm .hg/a1
638
644
639 $ (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
645 $ (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
640 abort: path contains illegal component: .hg/foo (glob)
646 abort: path contains illegal component: .hg/foo (glob)
641 [255]
647 [255]
642 $ hg status -C
648 $ hg status -C
643 $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
649 $ (cd d1/d11; hg rename ../../d2/b ../../../foo)
644 abort: ../../../foo not under root '$TESTTMP'
650 abort: ../../../foo not under root '$TESTTMP'
645 [255]
651 [255]
646 $ hg status -C
652 $ hg status -C
647
653
General Comments 0
You need to be logged in to leave comments. Login now