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