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