##// END OF EJS Templates
largefiles: remove a mutable default argument...
Pierre-Yves David -
r26341:f46e7f3b default
parent child Browse files
Show More
@@ -1,1406 +1,1408 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={}, 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:
623 opts = {}
622 newpats = []
624 newpats = []
623 # The patterns were previously mangled to add the standin
625 # The patterns were previously mangled to add the standin
624 # directory; we need to remove that now
626 # directory; we need to remove that now
625 for pat in pats:
627 for pat in pats:
626 if match_.patkind(pat) is None and lfutil.shortname in pat:
628 if match_.patkind(pat) is None and lfutil.shortname in pat:
627 newpats.append(pat.replace(lfutil.shortname, ''))
629 newpats.append(pat.replace(lfutil.shortname, ''))
628 else:
630 else:
629 newpats.append(pat)
631 newpats.append(pat)
630 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
632 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
631 m = copy.copy(match)
633 m = copy.copy(match)
632 lfile = lambda f: lfutil.standin(f) in manifest
634 lfile = lambda f: lfutil.standin(f) in manifest
633 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)]
634 m._fileroots = set(m._files)
636 m._fileroots = set(m._files)
635 origmatchfn = m.matchfn
637 origmatchfn = m.matchfn
636 m.matchfn = lambda f: (lfutil.isstandin(f) and
638 m.matchfn = lambda f: (lfutil.isstandin(f) and
637 (f in manifest) and
639 (f in manifest) and
638 origmatchfn(lfutil.splitstandin(f)) or
640 origmatchfn(lfutil.splitstandin(f)) or
639 None)
641 None)
640 return m
642 return m
641 oldmatch = installmatchfn(overridematch)
643 oldmatch = installmatchfn(overridematch)
642 listpats = []
644 listpats = []
643 for pat in pats:
645 for pat in pats:
644 if match_.patkind(pat) is not None:
646 if match_.patkind(pat) is not None:
645 listpats.append(pat)
647 listpats.append(pat)
646 else:
648 else:
647 listpats.append(makestandin(pat))
649 listpats.append(makestandin(pat))
648
650
649 try:
651 try:
650 origcopyfile = util.copyfile
652 origcopyfile = util.copyfile
651 copiedfiles = []
653 copiedfiles = []
652 def overridecopyfile(src, dest):
654 def overridecopyfile(src, dest):
653 if (lfutil.shortname in src and
655 if (lfutil.shortname in src and
654 dest.startswith(repo.wjoin(lfutil.shortname))):
656 dest.startswith(repo.wjoin(lfutil.shortname))):
655 destlfile = dest.replace(lfutil.shortname, '')
657 destlfile = dest.replace(lfutil.shortname, '')
656 if not opts['force'] and os.path.exists(destlfile):
658 if not opts['force'] and os.path.exists(destlfile):
657 raise IOError('',
659 raise IOError('',
658 _('destination largefile already exists'))
660 _('destination largefile already exists'))
659 copiedfiles.append((src, dest))
661 copiedfiles.append((src, dest))
660 origcopyfile(src, dest)
662 origcopyfile(src, dest)
661
663
662 util.copyfile = overridecopyfile
664 util.copyfile = overridecopyfile
663 result += orig(ui, repo, listpats, opts, rename)
665 result += orig(ui, repo, listpats, opts, rename)
664 finally:
666 finally:
665 util.copyfile = origcopyfile
667 util.copyfile = origcopyfile
666
668
667 lfdirstate = lfutil.openlfdirstate(ui, repo)
669 lfdirstate = lfutil.openlfdirstate(ui, repo)
668 for (src, dest) in copiedfiles:
670 for (src, dest) in copiedfiles:
669 if (lfutil.shortname in src and
671 if (lfutil.shortname in src and
670 dest.startswith(repo.wjoin(lfutil.shortname))):
672 dest.startswith(repo.wjoin(lfutil.shortname))):
671 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
673 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
672 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
674 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
673 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
675 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
674 if not os.path.isdir(destlfiledir):
676 if not os.path.isdir(destlfiledir):
675 os.makedirs(destlfiledir)
677 os.makedirs(destlfiledir)
676 if rename:
678 if rename:
677 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
679 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
678
680
679 # The file is gone, but this deletes any empty parent
681 # The file is gone, but this deletes any empty parent
680 # directories as a side-effect.
682 # directories as a side-effect.
681 util.unlinkpath(repo.wjoin(srclfile), True)
683 util.unlinkpath(repo.wjoin(srclfile), True)
682 lfdirstate.remove(srclfile)
684 lfdirstate.remove(srclfile)
683 else:
685 else:
684 util.copyfile(repo.wjoin(srclfile),
686 util.copyfile(repo.wjoin(srclfile),
685 repo.wjoin(destlfile))
687 repo.wjoin(destlfile))
686
688
687 lfdirstate.add(destlfile)
689 lfdirstate.add(destlfile)
688 lfdirstate.write()
690 lfdirstate.write()
689 except util.Abort as e:
691 except util.Abort as e:
690 if str(e) != _('no files to copy'):
692 if str(e) != _('no files to copy'):
691 raise e
693 raise e
692 else:
694 else:
693 nolfiles = True
695 nolfiles = True
694 finally:
696 finally:
695 restorematchfn()
697 restorematchfn()
696 wlock.release()
698 wlock.release()
697
699
698 if nolfiles and nonormalfiles:
700 if nolfiles and nonormalfiles:
699 raise util.Abort(_('no files to copy'))
701 raise util.Abort(_('no files to copy'))
700
702
701 return result
703 return result
702
704
703 # 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
704 # changes to other largefiles accidentally. This means we have to keep
706 # changes to other largefiles accidentally. This means we have to keep
705 # 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
706 # the necessary largefiles.
708 # the necessary largefiles.
707 #
709 #
708 # Standins are only updated (to match the hash of largefiles) before
710 # Standins are only updated (to match the hash of largefiles) before
709 # commits. Update the standins then run the original revert, changing
711 # commits. Update the standins then run the original revert, changing
710 # the matcher to hit standins instead of largefiles. Based on the
712 # the matcher to hit standins instead of largefiles. Based on the
711 # resulting standins update the largefiles.
713 # resulting standins update the largefiles.
712 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
714 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
713 # 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)
714 # 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
715 # prevent others from changing them in their incorrect state.
717 # prevent others from changing them in their incorrect state.
716 wlock = repo.wlock()
718 wlock = repo.wlock()
717 try:
719 try:
718 lfdirstate = lfutil.openlfdirstate(ui, repo)
720 lfdirstate = lfutil.openlfdirstate(ui, repo)
719 s = lfutil.lfdirstatestatus(lfdirstate, repo)
721 s = lfutil.lfdirstatestatus(lfdirstate, repo)
720 lfdirstate.write()
722 lfdirstate.write()
721 for lfile in s.modified:
723 for lfile in s.modified:
722 lfutil.updatestandin(repo, lfutil.standin(lfile))
724 lfutil.updatestandin(repo, lfutil.standin(lfile))
723 for lfile in s.deleted:
725 for lfile in s.deleted:
724 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
726 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
725 os.unlink(repo.wjoin(lfutil.standin(lfile)))
727 os.unlink(repo.wjoin(lfutil.standin(lfile)))
726
728
727 oldstandins = lfutil.getstandinsstate(repo)
729 oldstandins = lfutil.getstandinsstate(repo)
728
730
729 def overridematch(mctx, pats=[], opts={}, globbed=False,
731 def overridematch(mctx, pats=[], opts={}, globbed=False,
730 default='relpath', badfn=None):
732 default='relpath', badfn=None):
731 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
733 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
732 m = copy.copy(match)
734 m = copy.copy(match)
733
735
734 # revert supports recursing into subrepos, and though largefiles
736 # revert supports recursing into subrepos, and though largefiles
735 # currently doesn't work correctly in that case, this match is
737 # currently doesn't work correctly in that case, this match is
736 # called, so the lfdirstate above may not be the correct one for
738 # called, so the lfdirstate above may not be the correct one for
737 # this invocation of match.
739 # this invocation of match.
738 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
740 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
739 False)
741 False)
740
742
741 def tostandin(f):
743 def tostandin(f):
742 standin = lfutil.standin(f)
744 standin = lfutil.standin(f)
743 if standin in ctx or standin in mctx:
745 if standin in ctx or standin in mctx:
744 return standin
746 return standin
745 elif standin in repo[None] or lfdirstate[f] == 'r':
747 elif standin in repo[None] or lfdirstate[f] == 'r':
746 return None
748 return None
747 return f
749 return f
748 m._files = [tostandin(f) for f in m._files]
750 m._files = [tostandin(f) for f in m._files]
749 m._files = [f for f in m._files if f is not None]
751 m._files = [f for f in m._files if f is not None]
750 m._fileroots = set(m._files)
752 m._fileroots = set(m._files)
751 origmatchfn = m.matchfn
753 origmatchfn = m.matchfn
752 def matchfn(f):
754 def matchfn(f):
753 if lfutil.isstandin(f):
755 if lfutil.isstandin(f):
754 return (origmatchfn(lfutil.splitstandin(f)) and
756 return (origmatchfn(lfutil.splitstandin(f)) and
755 (f in ctx or f in mctx))
757 (f in ctx or f in mctx))
756 return origmatchfn(f)
758 return origmatchfn(f)
757 m.matchfn = matchfn
759 m.matchfn = matchfn
758 return m
760 return m
759 oldmatch = installmatchfn(overridematch)
761 oldmatch = installmatchfn(overridematch)
760 try:
762 try:
761 orig(ui, repo, ctx, parents, *pats, **opts)
763 orig(ui, repo, ctx, parents, *pats, **opts)
762 finally:
764 finally:
763 restorematchfn()
765 restorematchfn()
764
766
765 newstandins = lfutil.getstandinsstate(repo)
767 newstandins = lfutil.getstandinsstate(repo)
766 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
768 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
767 # lfdirstate should be 'normallookup'-ed for updated files,
769 # lfdirstate should be 'normallookup'-ed for updated files,
768 # because reverting doesn't touch dirstate for 'normal' files
770 # because reverting doesn't touch dirstate for 'normal' files
769 # when target revision is explicitly specified: in such case,
771 # when target revision is explicitly specified: in such case,
770 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
772 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
771 # of target (standin) file.
773 # of target (standin) file.
772 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
774 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
773 normallookup=True)
775 normallookup=True)
774
776
775 finally:
777 finally:
776 wlock.release()
778 wlock.release()
777
779
778 # after pulling changesets, we need to take some extra care to get
780 # after pulling changesets, we need to take some extra care to get
779 # largefiles updated remotely
781 # largefiles updated remotely
780 def overridepull(orig, ui, repo, source=None, **opts):
782 def overridepull(orig, ui, repo, source=None, **opts):
781 revsprepull = len(repo)
783 revsprepull = len(repo)
782 if not source:
784 if not source:
783 source = 'default'
785 source = 'default'
784 repo.lfpullsource = source
786 repo.lfpullsource = source
785 result = orig(ui, repo, source, **opts)
787 result = orig(ui, repo, source, **opts)
786 revspostpull = len(repo)
788 revspostpull = len(repo)
787 lfrevs = opts.get('lfrev', [])
789 lfrevs = opts.get('lfrev', [])
788 if opts.get('all_largefiles'):
790 if opts.get('all_largefiles'):
789 lfrevs.append('pulled()')
791 lfrevs.append('pulled()')
790 if lfrevs and revspostpull > revsprepull:
792 if lfrevs and revspostpull > revsprepull:
791 numcached = 0
793 numcached = 0
792 repo.firstpulled = revsprepull # for pulled() revset expression
794 repo.firstpulled = revsprepull # for pulled() revset expression
793 try:
795 try:
794 for rev in scmutil.revrange(repo, lfrevs):
796 for rev in scmutil.revrange(repo, lfrevs):
795 ui.note(_('pulling largefiles for revision %s\n') % rev)
797 ui.note(_('pulling largefiles for revision %s\n') % rev)
796 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
798 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
797 numcached += len(cached)
799 numcached += len(cached)
798 finally:
800 finally:
799 del repo.firstpulled
801 del repo.firstpulled
800 ui.status(_("%d largefiles cached\n") % numcached)
802 ui.status(_("%d largefiles cached\n") % numcached)
801 return result
803 return result
802
804
803 def pulledrevsetsymbol(repo, subset, x):
805 def pulledrevsetsymbol(repo, subset, x):
804 """``pulled()``
806 """``pulled()``
805 Changesets that just has been pulled.
807 Changesets that just has been pulled.
806
808
807 Only available with largefiles from pull --lfrev expressions.
809 Only available with largefiles from pull --lfrev expressions.
808
810
809 .. container:: verbose
811 .. container:: verbose
810
812
811 Some examples:
813 Some examples:
812
814
813 - pull largefiles for all new changesets::
815 - pull largefiles for all new changesets::
814
816
815 hg pull -lfrev "pulled()"
817 hg pull -lfrev "pulled()"
816
818
817 - pull largefiles for all new branch heads::
819 - pull largefiles for all new branch heads::
818
820
819 hg pull -lfrev "head(pulled()) and not closed()"
821 hg pull -lfrev "head(pulled()) and not closed()"
820
822
821 """
823 """
822
824
823 try:
825 try:
824 firstpulled = repo.firstpulled
826 firstpulled = repo.firstpulled
825 except AttributeError:
827 except AttributeError:
826 raise util.Abort(_("pulled() only available in --lfrev"))
828 raise util.Abort(_("pulled() only available in --lfrev"))
827 return revset.baseset([r for r in subset if r >= firstpulled])
829 return revset.baseset([r for r in subset if r >= firstpulled])
828
830
829 def overrideclone(orig, ui, source, dest=None, **opts):
831 def overrideclone(orig, ui, source, dest=None, **opts):
830 d = dest
832 d = dest
831 if d is None:
833 if d is None:
832 d = hg.defaultdest(source)
834 d = hg.defaultdest(source)
833 if opts.get('all_largefiles') and not hg.islocal(d):
835 if opts.get('all_largefiles') and not hg.islocal(d):
834 raise util.Abort(_(
836 raise util.Abort(_(
835 '--all-largefiles is incompatible with non-local destination %s') %
837 '--all-largefiles is incompatible with non-local destination %s') %
836 d)
838 d)
837
839
838 return orig(ui, source, dest, **opts)
840 return orig(ui, source, dest, **opts)
839
841
840 def hgclone(orig, ui, opts, *args, **kwargs):
842 def hgclone(orig, ui, opts, *args, **kwargs):
841 result = orig(ui, opts, *args, **kwargs)
843 result = orig(ui, opts, *args, **kwargs)
842
844
843 if result is not None:
845 if result is not None:
844 sourcerepo, destrepo = result
846 sourcerepo, destrepo = result
845 repo = destrepo.local()
847 repo = destrepo.local()
846
848
847 # When cloning to a remote repo (like through SSH), no repo is available
849 # When cloning to a remote repo (like through SSH), no repo is available
848 # from the peer. Therefore the largefiles can't be downloaded and the
850 # from the peer. Therefore the largefiles can't be downloaded and the
849 # hgrc can't be updated.
851 # hgrc can't be updated.
850 if not repo:
852 if not repo:
851 return result
853 return result
852
854
853 # If largefiles is required for this repo, permanently enable it locally
855 # If largefiles is required for this repo, permanently enable it locally
854 if 'largefiles' in repo.requirements:
856 if 'largefiles' in repo.requirements:
855 fp = repo.vfs('hgrc', 'a', text=True)
857 fp = repo.vfs('hgrc', 'a', text=True)
856 try:
858 try:
857 fp.write('\n[extensions]\nlargefiles=\n')
859 fp.write('\n[extensions]\nlargefiles=\n')
858 finally:
860 finally:
859 fp.close()
861 fp.close()
860
862
861 # Caching is implicitly limited to 'rev' option, since the dest repo was
863 # Caching is implicitly limited to 'rev' option, since the dest repo was
862 # truncated at that point. The user may expect a download count with
864 # truncated at that point. The user may expect a download count with
863 # this option, so attempt whether or not this is a largefile repo.
865 # this option, so attempt whether or not this is a largefile repo.
864 if opts.get('all_largefiles'):
866 if opts.get('all_largefiles'):
865 success, missing = lfcommands.downloadlfiles(ui, repo, None)
867 success, missing = lfcommands.downloadlfiles(ui, repo, None)
866
868
867 if missing != 0:
869 if missing != 0:
868 return None
870 return None
869
871
870 return result
872 return result
871
873
872 def overriderebase(orig, ui, repo, **opts):
874 def overriderebase(orig, ui, repo, **opts):
873 if not util.safehasattr(repo, '_largefilesenabled'):
875 if not util.safehasattr(repo, '_largefilesenabled'):
874 return orig(ui, repo, **opts)
876 return orig(ui, repo, **opts)
875
877
876 resuming = opts.get('continue')
878 resuming = opts.get('continue')
877 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
879 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
878 repo._lfstatuswriters.append(lambda *msg, **opts: None)
880 repo._lfstatuswriters.append(lambda *msg, **opts: None)
879 try:
881 try:
880 return orig(ui, repo, **opts)
882 return orig(ui, repo, **opts)
881 finally:
883 finally:
882 repo._lfstatuswriters.pop()
884 repo._lfstatuswriters.pop()
883 repo._lfcommithooks.pop()
885 repo._lfcommithooks.pop()
884
886
885 def overridearchivecmd(orig, ui, repo, dest, **opts):
887 def overridearchivecmd(orig, ui, repo, dest, **opts):
886 repo.unfiltered().lfstatus = True
888 repo.unfiltered().lfstatus = True
887
889
888 try:
890 try:
889 return orig(ui, repo.unfiltered(), dest, **opts)
891 return orig(ui, repo.unfiltered(), dest, **opts)
890 finally:
892 finally:
891 repo.unfiltered().lfstatus = False
893 repo.unfiltered().lfstatus = False
892
894
893 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
895 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
894 prefix='', mtime=None, subrepos=None):
896 prefix='', mtime=None, subrepos=None):
895 if not repo.lfstatus:
897 if not repo.lfstatus:
896 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
898 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
897 subrepos)
899 subrepos)
898
900
899 # No need to lock because we are only reading history and
901 # No need to lock because we are only reading history and
900 # largefile caches, neither of which are modified.
902 # largefile caches, neither of which are modified.
901 if node is not None:
903 if node is not None:
902 lfcommands.cachelfiles(repo.ui, repo, node)
904 lfcommands.cachelfiles(repo.ui, repo, node)
903
905
904 if kind not in archival.archivers:
906 if kind not in archival.archivers:
905 raise util.Abort(_("unknown archive type '%s'") % kind)
907 raise util.Abort(_("unknown archive type '%s'") % kind)
906
908
907 ctx = repo[node]
909 ctx = repo[node]
908
910
909 if kind == 'files':
911 if kind == 'files':
910 if prefix:
912 if prefix:
911 raise util.Abort(
913 raise util.Abort(
912 _('cannot give prefix when archiving to files'))
914 _('cannot give prefix when archiving to files'))
913 else:
915 else:
914 prefix = archival.tidyprefix(dest, kind, prefix)
916 prefix = archival.tidyprefix(dest, kind, prefix)
915
917
916 def write(name, mode, islink, getdata):
918 def write(name, mode, islink, getdata):
917 if matchfn and not matchfn(name):
919 if matchfn and not matchfn(name):
918 return
920 return
919 data = getdata()
921 data = getdata()
920 if decode:
922 if decode:
921 data = repo.wwritedata(name, data)
923 data = repo.wwritedata(name, data)
922 archiver.addfile(prefix + name, mode, islink, data)
924 archiver.addfile(prefix + name, mode, islink, data)
923
925
924 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
926 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
925
927
926 if repo.ui.configbool("ui", "archivemeta", True):
928 if repo.ui.configbool("ui", "archivemeta", True):
927 write('.hg_archival.txt', 0o644, False,
929 write('.hg_archival.txt', 0o644, False,
928 lambda: archival.buildmetadata(ctx))
930 lambda: archival.buildmetadata(ctx))
929
931
930 for f in ctx:
932 for f in ctx:
931 ff = ctx.flags(f)
933 ff = ctx.flags(f)
932 getdata = ctx[f].data
934 getdata = ctx[f].data
933 if lfutil.isstandin(f):
935 if lfutil.isstandin(f):
934 if node is not None:
936 if node is not None:
935 path = lfutil.findfile(repo, getdata().strip())
937 path = lfutil.findfile(repo, getdata().strip())
936
938
937 if path is None:
939 if path is None:
938 raise util.Abort(
940 raise util.Abort(
939 _('largefile %s not found in repo store or system cache')
941 _('largefile %s not found in repo store or system cache')
940 % lfutil.splitstandin(f))
942 % lfutil.splitstandin(f))
941 else:
943 else:
942 path = lfutil.splitstandin(f)
944 path = lfutil.splitstandin(f)
943
945
944 f = lfutil.splitstandin(f)
946 f = lfutil.splitstandin(f)
945
947
946 def getdatafn():
948 def getdatafn():
947 fd = None
949 fd = None
948 try:
950 try:
949 fd = open(path, 'rb')
951 fd = open(path, 'rb')
950 return fd.read()
952 return fd.read()
951 finally:
953 finally:
952 if fd:
954 if fd:
953 fd.close()
955 fd.close()
954
956
955 getdata = getdatafn
957 getdata = getdatafn
956 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
958 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
957
959
958 if subrepos:
960 if subrepos:
959 for subpath in sorted(ctx.substate):
961 for subpath in sorted(ctx.substate):
960 sub = ctx.workingsub(subpath)
962 sub = ctx.workingsub(subpath)
961 submatch = match_.narrowmatcher(subpath, matchfn)
963 submatch = match_.narrowmatcher(subpath, matchfn)
962 sub._repo.lfstatus = True
964 sub._repo.lfstatus = True
963 sub.archive(archiver, prefix, submatch)
965 sub.archive(archiver, prefix, submatch)
964
966
965 archiver.done()
967 archiver.done()
966
968
967 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
969 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
968 if not repo._repo.lfstatus:
970 if not repo._repo.lfstatus:
969 return orig(repo, archiver, prefix, match)
971 return orig(repo, archiver, prefix, match)
970
972
971 repo._get(repo._state + ('hg',))
973 repo._get(repo._state + ('hg',))
972 rev = repo._state[1]
974 rev = repo._state[1]
973 ctx = repo._repo[rev]
975 ctx = repo._repo[rev]
974
976
975 if ctx.node() is not None:
977 if ctx.node() is not None:
976 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
978 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
977
979
978 def write(name, mode, islink, getdata):
980 def write(name, mode, islink, getdata):
979 # At this point, the standin has been replaced with the largefile name,
981 # At this point, the standin has been replaced with the largefile name,
980 # so the normal matcher works here without the lfutil variants.
982 # so the normal matcher works here without the lfutil variants.
981 if match and not match(f):
983 if match and not match(f):
982 return
984 return
983 data = getdata()
985 data = getdata()
984
986
985 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
987 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
986
988
987 for f in ctx:
989 for f in ctx:
988 ff = ctx.flags(f)
990 ff = ctx.flags(f)
989 getdata = ctx[f].data
991 getdata = ctx[f].data
990 if lfutil.isstandin(f):
992 if lfutil.isstandin(f):
991 if ctx.node() is not None:
993 if ctx.node() is not None:
992 path = lfutil.findfile(repo._repo, getdata().strip())
994 path = lfutil.findfile(repo._repo, getdata().strip())
993
995
994 if path is None:
996 if path is None:
995 raise util.Abort(
997 raise util.Abort(
996 _('largefile %s not found in repo store or system cache')
998 _('largefile %s not found in repo store or system cache')
997 % lfutil.splitstandin(f))
999 % lfutil.splitstandin(f))
998 else:
1000 else:
999 path = lfutil.splitstandin(f)
1001 path = lfutil.splitstandin(f)
1000
1002
1001 f = lfutil.splitstandin(f)
1003 f = lfutil.splitstandin(f)
1002
1004
1003 def getdatafn():
1005 def getdatafn():
1004 fd = None
1006 fd = None
1005 try:
1007 try:
1006 fd = open(os.path.join(prefix, path), 'rb')
1008 fd = open(os.path.join(prefix, path), 'rb')
1007 return fd.read()
1009 return fd.read()
1008 finally:
1010 finally:
1009 if fd:
1011 if fd:
1010 fd.close()
1012 fd.close()
1011
1013
1012 getdata = getdatafn
1014 getdata = getdatafn
1013
1015
1014 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1016 write(f, 'x' in ff and 0o755 or 0o644, 'l' in ff, getdata)
1015
1017
1016 for subpath in sorted(ctx.substate):
1018 for subpath in sorted(ctx.substate):
1017 sub = ctx.workingsub(subpath)
1019 sub = ctx.workingsub(subpath)
1018 submatch = match_.narrowmatcher(subpath, match)
1020 submatch = match_.narrowmatcher(subpath, match)
1019 sub._repo.lfstatus = True
1021 sub._repo.lfstatus = True
1020 sub.archive(archiver, prefix + repo._path + '/', submatch)
1022 sub.archive(archiver, prefix + repo._path + '/', submatch)
1021
1023
1022 # If a largefile is modified, the change is not reflected in its
1024 # If a largefile is modified, the change is not reflected in its
1023 # standin until a commit. cmdutil.bailifchanged() raises an exception
1025 # standin until a commit. cmdutil.bailifchanged() raises an exception
1024 # if the repo has uncommitted changes. Wrap it to also check if
1026 # if the repo has uncommitted changes. Wrap it to also check if
1025 # largefiles were changed. This is used by bisect, backout and fetch.
1027 # largefiles were changed. This is used by bisect, backout and fetch.
1026 def overridebailifchanged(orig, repo, *args, **kwargs):
1028 def overridebailifchanged(orig, repo, *args, **kwargs):
1027 orig(repo, *args, **kwargs)
1029 orig(repo, *args, **kwargs)
1028 repo.lfstatus = True
1030 repo.lfstatus = True
1029 s = repo.status()
1031 s = repo.status()
1030 repo.lfstatus = False
1032 repo.lfstatus = False
1031 if s.modified or s.added or s.removed or s.deleted:
1033 if s.modified or s.added or s.removed or s.deleted:
1032 raise util.Abort(_('uncommitted changes'))
1034 raise util.Abort(_('uncommitted changes'))
1033
1035
1034 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1036 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1035 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1037 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1036 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1038 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1037 m = composelargefilematcher(match, repo[None].manifest())
1039 m = composelargefilematcher(match, repo[None].manifest())
1038
1040
1039 try:
1041 try:
1040 repo.lfstatus = True
1042 repo.lfstatus = True
1041 s = repo.status(match=m, clean=True)
1043 s = repo.status(match=m, clean=True)
1042 finally:
1044 finally:
1043 repo.lfstatus = False
1045 repo.lfstatus = False
1044 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1046 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1045 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
1047 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
1046
1048
1047 for f in forget:
1049 for f in forget:
1048 if lfutil.standin(f) not in repo.dirstate and not \
1050 if lfutil.standin(f) not in repo.dirstate and not \
1049 repo.wvfs.isdir(lfutil.standin(f)):
1051 repo.wvfs.isdir(lfutil.standin(f)):
1050 ui.warn(_('not removing %s: file is already untracked\n')
1052 ui.warn(_('not removing %s: file is already untracked\n')
1051 % m.rel(f))
1053 % m.rel(f))
1052 bad.append(f)
1054 bad.append(f)
1053
1055
1054 for f in forget:
1056 for f in forget:
1055 if ui.verbose or not m.exact(f):
1057 if ui.verbose or not m.exact(f):
1056 ui.status(_('removing %s\n') % m.rel(f))
1058 ui.status(_('removing %s\n') % m.rel(f))
1057
1059
1058 # Need to lock because standin files are deleted then removed from the
1060 # Need to lock because standin files are deleted then removed from the
1059 # repository and we could race in-between.
1061 # repository and we could race in-between.
1060 wlock = repo.wlock()
1062 wlock = repo.wlock()
1061 try:
1063 try:
1062 lfdirstate = lfutil.openlfdirstate(ui, repo)
1064 lfdirstate = lfutil.openlfdirstate(ui, repo)
1063 for f in forget:
1065 for f in forget:
1064 if lfdirstate[f] == 'a':
1066 if lfdirstate[f] == 'a':
1065 lfdirstate.drop(f)
1067 lfdirstate.drop(f)
1066 else:
1068 else:
1067 lfdirstate.remove(f)
1069 lfdirstate.remove(f)
1068 lfdirstate.write()
1070 lfdirstate.write()
1069 standins = [lfutil.standin(f) for f in forget]
1071 standins = [lfutil.standin(f) for f in forget]
1070 for f in standins:
1072 for f in standins:
1071 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1073 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1072 rejected = repo[None].forget(standins)
1074 rejected = repo[None].forget(standins)
1073 finally:
1075 finally:
1074 wlock.release()
1076 wlock.release()
1075
1077
1076 bad.extend(f for f in rejected if f in m.files())
1078 bad.extend(f for f in rejected if f in m.files())
1077 forgot.extend(f for f in forget if f not in rejected)
1079 forgot.extend(f for f in forget if f not in rejected)
1078 return bad, forgot
1080 return bad, forgot
1079
1081
1080 def _getoutgoings(repo, other, missing, addfunc):
1082 def _getoutgoings(repo, other, missing, addfunc):
1081 """get pairs of filename and largefile hash in outgoing revisions
1083 """get pairs of filename and largefile hash in outgoing revisions
1082 in 'missing'.
1084 in 'missing'.
1083
1085
1084 largefiles already existing on 'other' repository are ignored.
1086 largefiles already existing on 'other' repository are ignored.
1085
1087
1086 'addfunc' is invoked with each unique pairs of filename and
1088 'addfunc' is invoked with each unique pairs of filename and
1087 largefile hash value.
1089 largefile hash value.
1088 """
1090 """
1089 knowns = set()
1091 knowns = set()
1090 lfhashes = set()
1092 lfhashes = set()
1091 def dedup(fn, lfhash):
1093 def dedup(fn, lfhash):
1092 k = (fn, lfhash)
1094 k = (fn, lfhash)
1093 if k not in knowns:
1095 if k not in knowns:
1094 knowns.add(k)
1096 knowns.add(k)
1095 lfhashes.add(lfhash)
1097 lfhashes.add(lfhash)
1096 lfutil.getlfilestoupload(repo, missing, dedup)
1098 lfutil.getlfilestoupload(repo, missing, dedup)
1097 if lfhashes:
1099 if lfhashes:
1098 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1100 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1099 for fn, lfhash in knowns:
1101 for fn, lfhash in knowns:
1100 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1102 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1101 addfunc(fn, lfhash)
1103 addfunc(fn, lfhash)
1102
1104
1103 def outgoinghook(ui, repo, other, opts, missing):
1105 def outgoinghook(ui, repo, other, opts, missing):
1104 if opts.pop('large', None):
1106 if opts.pop('large', None):
1105 lfhashes = set()
1107 lfhashes = set()
1106 if ui.debugflag:
1108 if ui.debugflag:
1107 toupload = {}
1109 toupload = {}
1108 def addfunc(fn, lfhash):
1110 def addfunc(fn, lfhash):
1109 if fn not in toupload:
1111 if fn not in toupload:
1110 toupload[fn] = []
1112 toupload[fn] = []
1111 toupload[fn].append(lfhash)
1113 toupload[fn].append(lfhash)
1112 lfhashes.add(lfhash)
1114 lfhashes.add(lfhash)
1113 def showhashes(fn):
1115 def showhashes(fn):
1114 for lfhash in sorted(toupload[fn]):
1116 for lfhash in sorted(toupload[fn]):
1115 ui.debug(' %s\n' % (lfhash))
1117 ui.debug(' %s\n' % (lfhash))
1116 else:
1118 else:
1117 toupload = set()
1119 toupload = set()
1118 def addfunc(fn, lfhash):
1120 def addfunc(fn, lfhash):
1119 toupload.add(fn)
1121 toupload.add(fn)
1120 lfhashes.add(lfhash)
1122 lfhashes.add(lfhash)
1121 def showhashes(fn):
1123 def showhashes(fn):
1122 pass
1124 pass
1123 _getoutgoings(repo, other, missing, addfunc)
1125 _getoutgoings(repo, other, missing, addfunc)
1124
1126
1125 if not toupload:
1127 if not toupload:
1126 ui.status(_('largefiles: no files to upload\n'))
1128 ui.status(_('largefiles: no files to upload\n'))
1127 else:
1129 else:
1128 ui.status(_('largefiles to upload (%d entities):\n')
1130 ui.status(_('largefiles to upload (%d entities):\n')
1129 % (len(lfhashes)))
1131 % (len(lfhashes)))
1130 for file in sorted(toupload):
1132 for file in sorted(toupload):
1131 ui.status(lfutil.splitstandin(file) + '\n')
1133 ui.status(lfutil.splitstandin(file) + '\n')
1132 showhashes(file)
1134 showhashes(file)
1133 ui.status('\n')
1135 ui.status('\n')
1134
1136
1135 def summaryremotehook(ui, repo, opts, changes):
1137 def summaryremotehook(ui, repo, opts, changes):
1136 largeopt = opts.get('large', False)
1138 largeopt = opts.get('large', False)
1137 if changes is None:
1139 if changes is None:
1138 if largeopt:
1140 if largeopt:
1139 return (False, True) # only outgoing check is needed
1141 return (False, True) # only outgoing check is needed
1140 else:
1142 else:
1141 return (False, False)
1143 return (False, False)
1142 elif largeopt:
1144 elif largeopt:
1143 url, branch, peer, outgoing = changes[1]
1145 url, branch, peer, outgoing = changes[1]
1144 if peer is None:
1146 if peer is None:
1145 # i18n: column positioning for "hg summary"
1147 # i18n: column positioning for "hg summary"
1146 ui.status(_('largefiles: (no remote repo)\n'))
1148 ui.status(_('largefiles: (no remote repo)\n'))
1147 return
1149 return
1148
1150
1149 toupload = set()
1151 toupload = set()
1150 lfhashes = set()
1152 lfhashes = set()
1151 def addfunc(fn, lfhash):
1153 def addfunc(fn, lfhash):
1152 toupload.add(fn)
1154 toupload.add(fn)
1153 lfhashes.add(lfhash)
1155 lfhashes.add(lfhash)
1154 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1156 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1155
1157
1156 if not toupload:
1158 if not toupload:
1157 # i18n: column positioning for "hg summary"
1159 # i18n: column positioning for "hg summary"
1158 ui.status(_('largefiles: (no files to upload)\n'))
1160 ui.status(_('largefiles: (no files to upload)\n'))
1159 else:
1161 else:
1160 # i18n: column positioning for "hg summary"
1162 # i18n: column positioning for "hg summary"
1161 ui.status(_('largefiles: %d entities for %d files to upload\n')
1163 ui.status(_('largefiles: %d entities for %d files to upload\n')
1162 % (len(lfhashes), len(toupload)))
1164 % (len(lfhashes), len(toupload)))
1163
1165
1164 def overridesummary(orig, ui, repo, *pats, **opts):
1166 def overridesummary(orig, ui, repo, *pats, **opts):
1165 try:
1167 try:
1166 repo.lfstatus = True
1168 repo.lfstatus = True
1167 orig(ui, repo, *pats, **opts)
1169 orig(ui, repo, *pats, **opts)
1168 finally:
1170 finally:
1169 repo.lfstatus = False
1171 repo.lfstatus = False
1170
1172
1171 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1173 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1172 similarity=None):
1174 similarity=None):
1173 if not lfutil.islfilesrepo(repo):
1175 if not lfutil.islfilesrepo(repo):
1174 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1176 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1175 # Get the list of missing largefiles so we can remove them
1177 # Get the list of missing largefiles so we can remove them
1176 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1178 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1177 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1179 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1178 False, False, False)
1180 False, False, False)
1179
1181
1180 # Call into the normal remove code, but the removing of the standin, we want
1182 # Call into the normal remove code, but the removing of the standin, we want
1181 # to have handled by original addremove. Monkey patching here makes sure
1183 # to have handled by original addremove. Monkey patching here makes sure
1182 # we don't remove the standin in the largefiles code, preventing a very
1184 # we don't remove the standin in the largefiles code, preventing a very
1183 # confused state later.
1185 # confused state later.
1184 if s.deleted:
1186 if s.deleted:
1185 m = copy.copy(matcher)
1187 m = copy.copy(matcher)
1186
1188
1187 # The m._files and m._map attributes are not changed to the deleted list
1189 # The m._files and m._map attributes are not changed to the deleted list
1188 # because that affects the m.exact() test, which in turn governs whether
1190 # because that affects the m.exact() test, which in turn governs whether
1189 # or not the file name is printed, and how. Simply limit the original
1191 # or not the file name is printed, and how. Simply limit the original
1190 # matches to those in the deleted status list.
1192 # matches to those in the deleted status list.
1191 matchfn = m.matchfn
1193 matchfn = m.matchfn
1192 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1194 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1193
1195
1194 removelargefiles(repo.ui, repo, True, m, **opts)
1196 removelargefiles(repo.ui, repo, True, m, **opts)
1195 # Call into the normal add code, and any files that *should* be added as
1197 # Call into the normal add code, and any files that *should* be added as
1196 # largefiles will be
1198 # largefiles will be
1197 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1199 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1198 # Now that we've handled largefiles, hand off to the original addremove
1200 # Now that we've handled largefiles, hand off to the original addremove
1199 # function to take care of the rest. Make sure it doesn't do anything with
1201 # function to take care of the rest. Make sure it doesn't do anything with
1200 # largefiles by passing a matcher that will ignore them.
1202 # largefiles by passing a matcher that will ignore them.
1201 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1203 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1202 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1204 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1203
1205
1204 # Calling purge with --all will cause the largefiles to be deleted.
1206 # Calling purge with --all will cause the largefiles to be deleted.
1205 # Override repo.status to prevent this from happening.
1207 # Override repo.status to prevent this from happening.
1206 def overridepurge(orig, ui, repo, *dirs, **opts):
1208 def overridepurge(orig, ui, repo, *dirs, **opts):
1207 # XXX Monkey patching a repoview will not work. The assigned attribute will
1209 # XXX Monkey patching a repoview will not work. The assigned attribute will
1208 # be set on the unfiltered repo, but we will only lookup attributes in the
1210 # be set on the unfiltered repo, but we will only lookup attributes in the
1209 # unfiltered repo if the lookup in the repoview object itself fails. As the
1211 # unfiltered repo if the lookup in the repoview object itself fails. As the
1210 # monkey patched method exists on the repoview class the lookup will not
1212 # monkey patched method exists on the repoview class the lookup will not
1211 # fail. As a result, the original version will shadow the monkey patched
1213 # fail. As a result, the original version will shadow the monkey patched
1212 # one, defeating the monkey patch.
1214 # one, defeating the monkey patch.
1213 #
1215 #
1214 # As a work around we use an unfiltered repo here. We should do something
1216 # As a work around we use an unfiltered repo here. We should do something
1215 # cleaner instead.
1217 # cleaner instead.
1216 repo = repo.unfiltered()
1218 repo = repo.unfiltered()
1217 oldstatus = repo.status
1219 oldstatus = repo.status
1218 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1220 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1219 clean=False, unknown=False, listsubrepos=False):
1221 clean=False, unknown=False, listsubrepos=False):
1220 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1222 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1221 listsubrepos)
1223 listsubrepos)
1222 lfdirstate = lfutil.openlfdirstate(ui, repo)
1224 lfdirstate = lfutil.openlfdirstate(ui, repo)
1223 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1225 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1224 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1226 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1225 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1227 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1226 unknown, ignored, r.clean)
1228 unknown, ignored, r.clean)
1227 repo.status = overridestatus
1229 repo.status = overridestatus
1228 orig(ui, repo, *dirs, **opts)
1230 orig(ui, repo, *dirs, **opts)
1229 repo.status = oldstatus
1231 repo.status = oldstatus
1230 def overriderollback(orig, ui, repo, **opts):
1232 def overriderollback(orig, ui, repo, **opts):
1231 wlock = repo.wlock()
1233 wlock = repo.wlock()
1232 try:
1234 try:
1233 before = repo.dirstate.parents()
1235 before = repo.dirstate.parents()
1234 orphans = set(f for f in repo.dirstate
1236 orphans = set(f for f in repo.dirstate
1235 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1237 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1236 result = orig(ui, repo, **opts)
1238 result = orig(ui, repo, **opts)
1237 after = repo.dirstate.parents()
1239 after = repo.dirstate.parents()
1238 if before == after:
1240 if before == after:
1239 return result # no need to restore standins
1241 return result # no need to restore standins
1240
1242
1241 pctx = repo['.']
1243 pctx = repo['.']
1242 for f in repo.dirstate:
1244 for f in repo.dirstate:
1243 if lfutil.isstandin(f):
1245 if lfutil.isstandin(f):
1244 orphans.discard(f)
1246 orphans.discard(f)
1245 if repo.dirstate[f] == 'r':
1247 if repo.dirstate[f] == 'r':
1246 repo.wvfs.unlinkpath(f, ignoremissing=True)
1248 repo.wvfs.unlinkpath(f, ignoremissing=True)
1247 elif f in pctx:
1249 elif f in pctx:
1248 fctx = pctx[f]
1250 fctx = pctx[f]
1249 repo.wwrite(f, fctx.data(), fctx.flags())
1251 repo.wwrite(f, fctx.data(), fctx.flags())
1250 else:
1252 else:
1251 # content of standin is not so important in 'a',
1253 # content of standin is not so important in 'a',
1252 # 'm' or 'n' (coming from the 2nd parent) cases
1254 # 'm' or 'n' (coming from the 2nd parent) cases
1253 lfutil.writestandin(repo, f, '', False)
1255 lfutil.writestandin(repo, f, '', False)
1254 for standin in orphans:
1256 for standin in orphans:
1255 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1257 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1256
1258
1257 lfdirstate = lfutil.openlfdirstate(ui, repo)
1259 lfdirstate = lfutil.openlfdirstate(ui, repo)
1258 orphans = set(lfdirstate)
1260 orphans = set(lfdirstate)
1259 lfiles = lfutil.listlfiles(repo)
1261 lfiles = lfutil.listlfiles(repo)
1260 for file in lfiles:
1262 for file in lfiles:
1261 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1263 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1262 orphans.discard(file)
1264 orphans.discard(file)
1263 for lfile in orphans:
1265 for lfile in orphans:
1264 lfdirstate.drop(lfile)
1266 lfdirstate.drop(lfile)
1265 lfdirstate.write()
1267 lfdirstate.write()
1266 finally:
1268 finally:
1267 wlock.release()
1269 wlock.release()
1268 return result
1270 return result
1269
1271
1270 def overridetransplant(orig, ui, repo, *revs, **opts):
1272 def overridetransplant(orig, ui, repo, *revs, **opts):
1271 resuming = opts.get('continue')
1273 resuming = opts.get('continue')
1272 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1274 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1273 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1275 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1274 try:
1276 try:
1275 result = orig(ui, repo, *revs, **opts)
1277 result = orig(ui, repo, *revs, **opts)
1276 finally:
1278 finally:
1277 repo._lfstatuswriters.pop()
1279 repo._lfstatuswriters.pop()
1278 repo._lfcommithooks.pop()
1280 repo._lfcommithooks.pop()
1279 return result
1281 return result
1280
1282
1281 def overridecat(orig, ui, repo, file1, *pats, **opts):
1283 def overridecat(orig, ui, repo, file1, *pats, **opts):
1282 ctx = scmutil.revsingle(repo, opts.get('rev'))
1284 ctx = scmutil.revsingle(repo, opts.get('rev'))
1283 err = 1
1285 err = 1
1284 notbad = set()
1286 notbad = set()
1285 m = scmutil.match(ctx, (file1,) + pats, opts)
1287 m = scmutil.match(ctx, (file1,) + pats, opts)
1286 origmatchfn = m.matchfn
1288 origmatchfn = m.matchfn
1287 def lfmatchfn(f):
1289 def lfmatchfn(f):
1288 if origmatchfn(f):
1290 if origmatchfn(f):
1289 return True
1291 return True
1290 lf = lfutil.splitstandin(f)
1292 lf = lfutil.splitstandin(f)
1291 if lf is None:
1293 if lf is None:
1292 return False
1294 return False
1293 notbad.add(lf)
1295 notbad.add(lf)
1294 return origmatchfn(lf)
1296 return origmatchfn(lf)
1295 m.matchfn = lfmatchfn
1297 m.matchfn = lfmatchfn
1296 origbadfn = m.bad
1298 origbadfn = m.bad
1297 def lfbadfn(f, msg):
1299 def lfbadfn(f, msg):
1298 if not f in notbad:
1300 if not f in notbad:
1299 origbadfn(f, msg)
1301 origbadfn(f, msg)
1300 m.bad = lfbadfn
1302 m.bad = lfbadfn
1301
1303
1302 origvisitdirfn = m.visitdir
1304 origvisitdirfn = m.visitdir
1303 def lfvisitdirfn(dir):
1305 def lfvisitdirfn(dir):
1304 if dir == lfutil.shortname:
1306 if dir == lfutil.shortname:
1305 return True
1307 return True
1306 ret = origvisitdirfn(dir)
1308 ret = origvisitdirfn(dir)
1307 if ret:
1309 if ret:
1308 return ret
1310 return ret
1309 lf = lfutil.splitstandin(dir)
1311 lf = lfutil.splitstandin(dir)
1310 if lf is None:
1312 if lf is None:
1311 return False
1313 return False
1312 return origvisitdirfn(lf)
1314 return origvisitdirfn(lf)
1313 m.visitdir = lfvisitdirfn
1315 m.visitdir = lfvisitdirfn
1314
1316
1315 for f in ctx.walk(m):
1317 for f in ctx.walk(m):
1316 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1318 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1317 pathname=f)
1319 pathname=f)
1318 lf = lfutil.splitstandin(f)
1320 lf = lfutil.splitstandin(f)
1319 if lf is None or origmatchfn(f):
1321 if lf is None or origmatchfn(f):
1320 # duplicating unreachable code from commands.cat
1322 # duplicating unreachable code from commands.cat
1321 data = ctx[f].data()
1323 data = ctx[f].data()
1322 if opts.get('decode'):
1324 if opts.get('decode'):
1323 data = repo.wwritedata(f, data)
1325 data = repo.wwritedata(f, data)
1324 fp.write(data)
1326 fp.write(data)
1325 else:
1327 else:
1326 hash = lfutil.readstandin(repo, lf, ctx.rev())
1328 hash = lfutil.readstandin(repo, lf, ctx.rev())
1327 if not lfutil.inusercache(repo.ui, hash):
1329 if not lfutil.inusercache(repo.ui, hash):
1328 store = basestore._openstore(repo)
1330 store = basestore._openstore(repo)
1329 success, missing = store.get([(lf, hash)])
1331 success, missing = store.get([(lf, hash)])
1330 if len(success) != 1:
1332 if len(success) != 1:
1331 raise util.Abort(
1333 raise util.Abort(
1332 _('largefile %s is not in cache and could not be '
1334 _('largefile %s is not in cache and could not be '
1333 'downloaded') % lf)
1335 'downloaded') % lf)
1334 path = lfutil.usercachepath(repo.ui, hash)
1336 path = lfutil.usercachepath(repo.ui, hash)
1335 fpin = open(path, "rb")
1337 fpin = open(path, "rb")
1336 for chunk in util.filechunkiter(fpin, 128 * 1024):
1338 for chunk in util.filechunkiter(fpin, 128 * 1024):
1337 fp.write(chunk)
1339 fp.write(chunk)
1338 fpin.close()
1340 fpin.close()
1339 fp.close()
1341 fp.close()
1340 err = 0
1342 err = 0
1341 return err
1343 return err
1342
1344
1343 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1345 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1344 *args, **kwargs):
1346 *args, **kwargs):
1345 wlock = repo.wlock()
1347 wlock = repo.wlock()
1346 try:
1348 try:
1347 # branch | | |
1349 # branch | | |
1348 # merge | force | partial | action
1350 # merge | force | partial | action
1349 # -------+-------+---------+--------------
1351 # -------+-------+---------+--------------
1350 # x | x | x | linear-merge
1352 # x | x | x | linear-merge
1351 # o | x | x | branch-merge
1353 # o | x | x | branch-merge
1352 # x | o | x | overwrite (as clean update)
1354 # x | o | x | overwrite (as clean update)
1353 # o | o | x | force-branch-merge (*1)
1355 # o | o | x | force-branch-merge (*1)
1354 # x | x | o | (*)
1356 # x | x | o | (*)
1355 # o | x | o | (*)
1357 # o | x | o | (*)
1356 # x | o | o | overwrite (as revert)
1358 # x | o | o | overwrite (as revert)
1357 # o | o | o | (*)
1359 # o | o | o | (*)
1358 #
1360 #
1359 # (*) don't care
1361 # (*) don't care
1360 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1362 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1361
1363
1362 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1364 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1363 unsure, s = lfdirstate.status(match_.always(repo.root,
1365 unsure, s = lfdirstate.status(match_.always(repo.root,
1364 repo.getcwd()),
1366 repo.getcwd()),
1365 [], False, False, False)
1367 [], False, False, False)
1366 pctx = repo['.']
1368 pctx = repo['.']
1367 for lfile in unsure + s.modified:
1369 for lfile in unsure + s.modified:
1368 lfileabs = repo.wvfs.join(lfile)
1370 lfileabs = repo.wvfs.join(lfile)
1369 if not os.path.exists(lfileabs):
1371 if not os.path.exists(lfileabs):
1370 continue
1372 continue
1371 lfhash = lfutil.hashrepofile(repo, lfile)
1373 lfhash = lfutil.hashrepofile(repo, lfile)
1372 standin = lfutil.standin(lfile)
1374 standin = lfutil.standin(lfile)
1373 lfutil.writestandin(repo, standin, lfhash,
1375 lfutil.writestandin(repo, standin, lfhash,
1374 lfutil.getexecutable(lfileabs))
1376 lfutil.getexecutable(lfileabs))
1375 if (standin in pctx and
1377 if (standin in pctx and
1376 lfhash == lfutil.readstandin(repo, lfile, '.')):
1378 lfhash == lfutil.readstandin(repo, lfile, '.')):
1377 lfdirstate.normal(lfile)
1379 lfdirstate.normal(lfile)
1378 for lfile in s.added:
1380 for lfile in s.added:
1379 lfutil.updatestandin(repo, lfutil.standin(lfile))
1381 lfutil.updatestandin(repo, lfutil.standin(lfile))
1380 lfdirstate.write()
1382 lfdirstate.write()
1381
1383
1382 oldstandins = lfutil.getstandinsstate(repo)
1384 oldstandins = lfutil.getstandinsstate(repo)
1383
1385
1384 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1386 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1385
1387
1386 newstandins = lfutil.getstandinsstate(repo)
1388 newstandins = lfutil.getstandinsstate(repo)
1387 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1389 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1388 if branchmerge or force or partial:
1390 if branchmerge or force or partial:
1389 filelist.extend(s.deleted + s.removed)
1391 filelist.extend(s.deleted + s.removed)
1390
1392
1391 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1393 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1392 normallookup=partial)
1394 normallookup=partial)
1393
1395
1394 return result
1396 return result
1395 finally:
1397 finally:
1396 wlock.release()
1398 wlock.release()
1397
1399
1398 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1400 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1399 result = orig(repo, files, *args, **kwargs)
1401 result = orig(repo, files, *args, **kwargs)
1400
1402
1401 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1403 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1402 if filelist:
1404 if filelist:
1403 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1405 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1404 printmessage=False, normallookup=True)
1406 printmessage=False, normallookup=True)
1405
1407
1406 return result
1408 return result
General Comments 0
You need to be logged in to leave comments. Login now