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