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