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