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