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