##// END OF EJS Templates
largefiles: remove a mutable default argument...
Pierre-Yves David -
r26339:4afdd428 default
parent child Browse files
Show More
@@ -1,1404 +1,1406
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={}, 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:
300 opts = {}
299 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default,
301 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default,
300 badfn=badfn)
302 badfn=badfn)
301 m, p = copy.copy(matchandpats)
303 m, p = copy.copy(matchandpats)
302
304
303 if m.always():
305 if m.always():
304 # 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
305 # to add standins.
307 # to add standins.
306 return matchandpats
308 return matchandpats
307
309
308 pats = set(p)
310 pats = set(p)
309
311
310 def fixpats(pat, tostandin=lfutil.standin):
312 def fixpats(pat, tostandin=lfutil.standin):
311 if pat.startswith('set:'):
313 if pat.startswith('set:'):
312 return pat
314 return pat
313
315
314 kindpat = match_._patsplit(pat, None)
316 kindpat = match_._patsplit(pat, None)
315
317
316 if kindpat[0] is not None:
318 if kindpat[0] is not None:
317 return kindpat[0] + ':' + tostandin(kindpat[1])
319 return kindpat[0] + ':' + tostandin(kindpat[1])
318 return tostandin(kindpat[1])
320 return tostandin(kindpat[1])
319
321
320 if m._cwd:
322 if m._cwd:
321 hglf = lfutil.shortname
323 hglf = lfutil.shortname
322 back = util.pconvert(m.rel(hglf)[:-len(hglf)])
324 back = util.pconvert(m.rel(hglf)[:-len(hglf)])
323
325
324 def tostandin(f):
326 def tostandin(f):
325 # The file may already be a standin, so trucate the back
327 # The file may already be a standin, so trucate the back
326 # prefix and test before mangling it. This avoids turning
328 # prefix and test before mangling it. This avoids turning
327 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
329 # 'glob:../.hglf/foo*' into 'glob:../.hglf/../.hglf/foo*'.
328 if f.startswith(back) and lfutil.splitstandin(f[len(back):]):
330 if f.startswith(back) and lfutil.splitstandin(f[len(back):]):
329 return f
331 return f
330
332
331 # An absolute path is from outside the repo, so truncate the
333 # An absolute path is from outside the repo, so truncate the
332 # path to the root before building the standin. Otherwise cwd
334 # path to the root before building the standin. Otherwise cwd
333 # 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
334 # prepended before building the standin.
336 # prepended before building the standin.
335 if os.path.isabs(m._cwd):
337 if os.path.isabs(m._cwd):
336 f = f[len(back):]
338 f = f[len(back):]
337 else:
339 else:
338 f = m._cwd + '/' + f
340 f = m._cwd + '/' + f
339 return back + lfutil.standin(f)
341 return back + lfutil.standin(f)
340
342
341 pats.update(fixpats(f, tostandin) for f in p)
343 pats.update(fixpats(f, tostandin) for f in p)
342 else:
344 else:
343 def tostandin(f):
345 def tostandin(f):
344 if lfutil.splitstandin(f):
346 if lfutil.splitstandin(f):
345 return f
347 return f
346 return lfutil.standin(f)
348 return lfutil.standin(f)
347 pats.update(fixpats(f, tostandin) for f in p)
349 pats.update(fixpats(f, tostandin) for f in p)
348
350
349 for i in range(0, len(m._files)):
351 for i in range(0, len(m._files)):
350 # 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 '.'
351 if m._files[i] == '.':
353 if m._files[i] == '.':
352 continue
354 continue
353 standin = lfutil.standin(m._files[i])
355 standin = lfutil.standin(m._files[i])
354 # If the "standin" is a directory, append instead of replace to
356 # If the "standin" is a directory, append instead of replace to
355 # support naming a directory on the command line with only
357 # support naming a directory on the command line with only
356 # largefiles. The original directory is kept to support normal
358 # largefiles. The original directory is kept to support normal
357 # files.
359 # files.
358 if standin in repo[ctx.node()]:
360 if standin in repo[ctx.node()]:
359 m._files[i] = standin
361 m._files[i] = standin
360 elif m._files[i] not in repo[ctx.node()] \
362 elif m._files[i] not in repo[ctx.node()] \
361 and repo.wvfs.isdir(standin):
363 and repo.wvfs.isdir(standin):
362 m._files.append(standin)
364 m._files.append(standin)
363
365
364 m._fileroots = set(m._files)
366 m._fileroots = set(m._files)
365 m._always = False
367 m._always = False
366 origmatchfn = m.matchfn
368 origmatchfn = m.matchfn
367 def lfmatchfn(f):
369 def lfmatchfn(f):
368 lf = lfutil.splitstandin(f)
370 lf = lfutil.splitstandin(f)
369 if lf is not None and origmatchfn(lf):
371 if lf is not None and origmatchfn(lf):
370 return True
372 return True
371 r = origmatchfn(f)
373 r = origmatchfn(f)
372 return r
374 return r
373 m.matchfn = lfmatchfn
375 m.matchfn = lfmatchfn
374
376
375 ui.debug('updated patterns: %s\n' % sorted(pats))
377 ui.debug('updated patterns: %s\n' % sorted(pats))
376 return m, pats
378 return m, pats
377
379
378 # 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:
379 # (1) to determine what revisions should be printed out, and
381 # (1) to determine what revisions should be printed out, and
380 # (2) to determine what files to print out diffs for.
382 # (2) to determine what files to print out diffs for.
381 # 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
382 # case (2).
384 # case (2).
383 def overridemakelogfilematcher(repo, pats, opts, badfn=None):
385 def overridemakelogfilematcher(repo, pats, opts, badfn=None):
384 wctx = repo[None]
386 wctx = repo[None]
385 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
387 match, pats = oldmatchandpats(wctx, pats, opts, badfn=badfn)
386 return lambda rev: match
388 return lambda rev: match
387
389
388 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
390 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
389 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
391 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
390 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
392 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
391
393
392 try:
394 try:
393 return orig(ui, repo, *pats, **opts)
395 return orig(ui, repo, *pats, **opts)
394 finally:
396 finally:
395 restorematchandpatsfn()
397 restorematchandpatsfn()
396 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
398 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
397
399
398 def overrideverify(orig, ui, repo, *pats, **opts):
400 def overrideverify(orig, ui, repo, *pats, **opts):
399 large = opts.pop('large', False)
401 large = opts.pop('large', False)
400 all = opts.pop('lfa', False)
402 all = opts.pop('lfa', False)
401 contents = opts.pop('lfc', False)
403 contents = opts.pop('lfc', False)
402
404
403 result = orig(ui, repo, *pats, **opts)
405 result = orig(ui, repo, *pats, **opts)
404 if large or all or contents:
406 if large or all or contents:
405 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
407 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
406 return result
408 return result
407
409
408 def overridedebugstate(orig, ui, repo, *pats, **opts):
410 def overridedebugstate(orig, ui, repo, *pats, **opts):
409 large = opts.pop('large', False)
411 large = opts.pop('large', False)
410 if large:
412 if large:
411 class fakerepo(object):
413 class fakerepo(object):
412 dirstate = lfutil.openlfdirstate(ui, repo)
414 dirstate = lfutil.openlfdirstate(ui, repo)
413 orig(ui, fakerepo, *pats, **opts)
415 orig(ui, fakerepo, *pats, **opts)
414 else:
416 else:
415 orig(ui, repo, *pats, **opts)
417 orig(ui, repo, *pats, **opts)
416
418
417 # Before starting the manifest merge, merge.updates will call
419 # Before starting the manifest merge, merge.updates will call
418 # _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
419 # changeset that collide with unknown files in the working copy.
421 # changeset that collide with unknown files in the working copy.
420 #
422 #
421 # 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
422 # 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.
423 #
425 #
424 # The overridden function filters the unknown files by removing any
426 # The overridden function filters the unknown files by removing any
425 # 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
426 # case further in the overridden calculateupdates function below.
428 # case further in the overridden calculateupdates function below.
427 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
429 def overridecheckunknownfile(origfn, repo, wctx, mctx, f, f2=None):
428 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
430 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
429 return False
431 return False
430 return origfn(repo, wctx, mctx, f, f2)
432 return origfn(repo, wctx, mctx, f, f2)
431
433
432 # The manifest merge handles conflicts on the manifest level. We want
434 # The manifest merge handles conflicts on the manifest level. We want
433 # 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.
434 #
436 #
435 # The strategy is to run the original calculateupdates and then process
437 # The strategy is to run the original calculateupdates and then process
436 # the action list it outputs. There are two cases we need to deal with:
438 # the action list it outputs. There are two cases we need to deal with:
437 #
439 #
438 # 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
439 # detected via its standin file, which will enter the working copy
441 # detected via its standin file, which will enter the working copy
440 # with a "get" action. It is not "merge" since the standin is all
442 # with a "get" action. It is not "merge" since the standin is all
441 # Mercurial is concerned with at this level -- the link to the
443 # Mercurial is concerned with at this level -- the link to the
442 # existing normal file is not relevant here.
444 # existing normal file is not relevant here.
443 #
445 #
444 # 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
445 # since the largefile will be present in the working copy and
447 # since the largefile will be present in the working copy and
446 # different from the normal file in p2. Mercurial therefore
448 # different from the normal file in p2. Mercurial therefore
447 # triggers a merge action.
449 # triggers a merge action.
448 #
450 #
449 # 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
450 # 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
451 # 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
452 # 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
453 # presumably changed on purpose.
455 # presumably changed on purpose.
454 #
456 #
455 # Finally, the merge.applyupdates function will then take care of
457 # Finally, the merge.applyupdates function will then take care of
456 # writing the files into the working copy and lfcommands.updatelfiles
458 # writing the files into the working copy and lfcommands.updatelfiles
457 # will update the largefiles.
459 # will update the largefiles.
458 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
460 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
459 partial, acceptremote, followcopies):
461 partial, acceptremote, followcopies):
460 overwrite = force and not branchmerge
462 overwrite = force and not branchmerge
461 actions, diverge, renamedelete = origfn(
463 actions, diverge, renamedelete = origfn(
462 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
464 repo, p1, p2, pas, branchmerge, force, partial, acceptremote,
463 followcopies)
465 followcopies)
464
466
465 if overwrite:
467 if overwrite:
466 return actions, diverge, renamedelete
468 return actions, diverge, renamedelete
467
469
468 # Convert to dictionary with filename as key and action as value.
470 # Convert to dictionary with filename as key and action as value.
469 lfiles = set()
471 lfiles = set()
470 for f in actions:
472 for f in actions:
471 splitstandin = f and lfutil.splitstandin(f)
473 splitstandin = f and lfutil.splitstandin(f)
472 if splitstandin in p1:
474 if splitstandin in p1:
473 lfiles.add(splitstandin)
475 lfiles.add(splitstandin)
474 elif lfutil.standin(f) in p1:
476 elif lfutil.standin(f) in p1:
475 lfiles.add(f)
477 lfiles.add(f)
476
478
477 for lfile in lfiles:
479 for lfile in lfiles:
478 standin = lfutil.standin(lfile)
480 standin = lfutil.standin(lfile)
479 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
481 (lm, largs, lmsg) = actions.get(lfile, (None, None, None))
480 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
482 (sm, sargs, smsg) = actions.get(standin, (None, None, None))
481 if sm in ('g', 'dc') and lm != 'r':
483 if sm in ('g', 'dc') and lm != 'r':
482 # Case 1: normal file in the working copy, largefile in
484 # Case 1: normal file in the working copy, largefile in
483 # the second parent
485 # the second parent
484 usermsg = _('remote turned local normal file %s into a largefile\n'
486 usermsg = _('remote turned local normal file %s into a largefile\n'
485 'use (l)argefile or keep (n)ormal file?'
487 'use (l)argefile or keep (n)ormal file?'
486 '$$ &Largefile $$ &Normal file') % lfile
488 '$$ &Largefile $$ &Normal file') % lfile
487 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
489 if repo.ui.promptchoice(usermsg, 0) == 0: # pick remote largefile
488 actions[lfile] = ('r', None, 'replaced by standin')
490 actions[lfile] = ('r', None, 'replaced by standin')
489 actions[standin] = ('g', sargs, 'replaces standin')
491 actions[standin] = ('g', sargs, 'replaces standin')
490 else: # keep local normal file
492 else: # keep local normal file
491 actions[lfile] = ('k', None, 'replaces standin')
493 actions[lfile] = ('k', None, 'replaces standin')
492 if branchmerge:
494 if branchmerge:
493 actions[standin] = ('k', None, 'replaced by non-standin')
495 actions[standin] = ('k', None, 'replaced by non-standin')
494 else:
496 else:
495 actions[standin] = ('r', None, 'replaced by non-standin')
497 actions[standin] = ('r', None, 'replaced by non-standin')
496 elif lm in ('g', 'dc') and sm != 'r':
498 elif lm in ('g', 'dc') and sm != 'r':
497 # Case 2: largefile in the working copy, normal file in
499 # Case 2: largefile in the working copy, normal file in
498 # the second parent
500 # the second parent
499 usermsg = _('remote turned local largefile %s into a normal file\n'
501 usermsg = _('remote turned local largefile %s into a normal file\n'
500 'keep (l)argefile or use (n)ormal file?'
502 'keep (l)argefile or use (n)ormal file?'
501 '$$ &Largefile $$ &Normal file') % lfile
503 '$$ &Largefile $$ &Normal file') % lfile
502 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
504 if repo.ui.promptchoice(usermsg, 0) == 0: # keep local largefile
503 if branchmerge:
505 if branchmerge:
504 # largefile can be restored from standin safely
506 # largefile can be restored from standin safely
505 actions[lfile] = ('k', None, 'replaced by standin')
507 actions[lfile] = ('k', None, 'replaced by standin')
506 actions[standin] = ('k', None, 'replaces standin')
508 actions[standin] = ('k', None, 'replaces standin')
507 else:
509 else:
508 # "lfile" should be marked as "removed" without
510 # "lfile" should be marked as "removed" without
509 # removal of itself
511 # removal of itself
510 actions[lfile] = ('lfmr', None,
512 actions[lfile] = ('lfmr', None,
511 'forget non-standin largefile')
513 'forget non-standin largefile')
512
514
513 # linear-merge should treat this largefile as 're-added'
515 # linear-merge should treat this largefile as 're-added'
514 actions[standin] = ('a', None, 'keep standin')
516 actions[standin] = ('a', None, 'keep standin')
515 else: # pick remote normal file
517 else: # pick remote normal file
516 actions[lfile] = ('g', largs, 'replaces standin')
518 actions[lfile] = ('g', largs, 'replaces standin')
517 actions[standin] = ('r', None, 'replaced by non-standin')
519 actions[standin] = ('r', None, 'replaced by non-standin')
518
520
519 return actions, diverge, renamedelete
521 return actions, diverge, renamedelete
520
522
521 def mergerecordupdates(orig, repo, actions, branchmerge):
523 def mergerecordupdates(orig, repo, actions, branchmerge):
522 if 'lfmr' in actions:
524 if 'lfmr' in actions:
523 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
525 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
524 for lfile, args, msg in actions['lfmr']:
526 for lfile, args, msg in actions['lfmr']:
525 # this should be executed before 'orig', to execute 'remove'
527 # this should be executed before 'orig', to execute 'remove'
526 # before all other actions
528 # before all other actions
527 repo.dirstate.remove(lfile)
529 repo.dirstate.remove(lfile)
528 # make sure lfile doesn't get synclfdirstate'd as normal
530 # make sure lfile doesn't get synclfdirstate'd as normal
529 lfdirstate.add(lfile)
531 lfdirstate.add(lfile)
530 lfdirstate.write()
532 lfdirstate.write()
531
533
532 return orig(repo, actions, branchmerge)
534 return orig(repo, actions, branchmerge)
533
535
534
536
535 # 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
536 # largefiles. This will handle identical edits without prompting the user.
538 # largefiles. This will handle identical edits without prompting the user.
537 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
539 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
538 if not lfutil.isstandin(orig):
540 if not lfutil.isstandin(orig):
539 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
541 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
540
542
541 ahash = fca.data().strip().lower()
543 ahash = fca.data().strip().lower()
542 dhash = fcd.data().strip().lower()
544 dhash = fcd.data().strip().lower()
543 ohash = fco.data().strip().lower()
545 ohash = fco.data().strip().lower()
544 if (ohash != ahash and
546 if (ohash != ahash and
545 ohash != dhash and
547 ohash != dhash and
546 (dhash == ahash or
548 (dhash == ahash or
547 repo.ui.promptchoice(
549 repo.ui.promptchoice(
548 _('largefile %s has a merge conflict\nancestor was %s\n'
550 _('largefile %s has a merge conflict\nancestor was %s\n'
549 'keep (l)ocal %s or\ntake (o)ther %s?'
551 'keep (l)ocal %s or\ntake (o)ther %s?'
550 '$$ &Local $$ &Other') %
552 '$$ &Local $$ &Other') %
551 (lfutil.splitstandin(orig), ahash, dhash, ohash),
553 (lfutil.splitstandin(orig), ahash, dhash, ohash),
552 0) == 1)):
554 0) == 1)):
553 repo.wwrite(fcd.path(), fco.data(), fco.flags())
555 repo.wwrite(fcd.path(), fco.data(), fco.flags())
554 return 0
556 return 0
555
557
556 def copiespathcopies(orig, ctx1, ctx2, match=None):
558 def copiespathcopies(orig, ctx1, ctx2, match=None):
557 copies = orig(ctx1, ctx2, match=match)
559 copies = orig(ctx1, ctx2, match=match)
558 updated = {}
560 updated = {}
559
561
560 for k, v in copies.iteritems():
562 for k, v in copies.iteritems():
561 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
563 updated[lfutil.splitstandin(k) or k] = lfutil.splitstandin(v) or v
562
564
563 return updated
565 return updated
564
566
565 # Copy first changes the matchers to match standins instead of
567 # Copy first changes the matchers to match standins instead of
566 # largefiles. Then it overrides util.copyfile in that function it
568 # largefiles. Then it overrides util.copyfile in that function it
567 # checks if the destination largefile already exists. It also keeps a
569 # checks if the destination largefile already exists. It also keeps a
568 # 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
569 # dirstate updated.
571 # dirstate updated.
570 def overridecopy(orig, ui, repo, pats, opts, rename=False):
572 def overridecopy(orig, ui, repo, pats, opts, rename=False):
571 # doesn't remove largefile on rename
573 # doesn't remove largefile on rename
572 if len(pats) < 2:
574 if len(pats) < 2:
573 # this isn't legal, let the original function deal with it
575 # this isn't legal, let the original function deal with it
574 return orig(ui, repo, pats, opts, rename)
576 return orig(ui, repo, pats, opts, rename)
575
577
576 # This could copy both lfiles and normal files in one command,
578 # This could copy both lfiles and normal files in one command,
577 # 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
578 # 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
579 # match largefiles and run it again.
581 # match largefiles and run it again.
580 nonormalfiles = False
582 nonormalfiles = False
581 nolfiles = False
583 nolfiles = False
582 installnormalfilesmatchfn(repo[None].manifest())
584 installnormalfilesmatchfn(repo[None].manifest())
583 try:
585 try:
584 result = orig(ui, repo, pats, opts, rename)
586 result = orig(ui, repo, pats, opts, rename)
585 except util.Abort as e:
587 except util.Abort as e:
586 if str(e) != _('no files to copy'):
588 if str(e) != _('no files to copy'):
587 raise e
589 raise e
588 else:
590 else:
589 nonormalfiles = True
591 nonormalfiles = True
590 result = 0
592 result = 0
591 finally:
593 finally:
592 restorematchfn()
594 restorematchfn()
593
595
594 # 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.
595 # 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.
596 try:
598 try:
597 repo.getcwd()
599 repo.getcwd()
598 except OSError:
600 except OSError:
599 return result
601 return result
600
602
601 def makestandin(relpath):
603 def makestandin(relpath):
602 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
604 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
603 return os.path.join(repo.wjoin(lfutil.standin(path)))
605 return os.path.join(repo.wjoin(lfutil.standin(path)))
604
606
605 fullpats = scmutil.expandpats(pats)
607 fullpats = scmutil.expandpats(pats)
606 dest = fullpats[-1]
608 dest = fullpats[-1]
607
609
608 if os.path.isdir(dest):
610 if os.path.isdir(dest):
609 if not os.path.isdir(makestandin(dest)):
611 if not os.path.isdir(makestandin(dest)):
610 os.makedirs(makestandin(dest))
612 os.makedirs(makestandin(dest))
611
613
612 try:
614 try:
613 # 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
614 # 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.
615 wlock = repo.wlock()
617 wlock = repo.wlock()
616
618
617 manifest = repo[None].manifest()
619 manifest = repo[None].manifest()
618 def overridematch(ctx, pats=[], opts={}, globbed=False,
620 def overridematch(ctx, pats=[], opts={}, globbed=False,
619 default='relpath', badfn=None):
621 default='relpath', badfn=None):
620 newpats = []
622 newpats = []
621 # The patterns were previously mangled to add the standin
623 # The patterns were previously mangled to add the standin
622 # directory; we need to remove that now
624 # directory; we need to remove that now
623 for pat in pats:
625 for pat in pats:
624 if match_.patkind(pat) is None and lfutil.shortname in pat:
626 if match_.patkind(pat) is None and lfutil.shortname in pat:
625 newpats.append(pat.replace(lfutil.shortname, ''))
627 newpats.append(pat.replace(lfutil.shortname, ''))
626 else:
628 else:
627 newpats.append(pat)
629 newpats.append(pat)
628 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
630 match = oldmatch(ctx, newpats, opts, globbed, default, badfn=badfn)
629 m = copy.copy(match)
631 m = copy.copy(match)
630 lfile = lambda f: lfutil.standin(f) in manifest
632 lfile = lambda f: lfutil.standin(f) in manifest
631 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)]
632 m._fileroots = set(m._files)
634 m._fileroots = set(m._files)
633 origmatchfn = m.matchfn
635 origmatchfn = m.matchfn
634 m.matchfn = lambda f: (lfutil.isstandin(f) and
636 m.matchfn = lambda f: (lfutil.isstandin(f) and
635 (f in manifest) and
637 (f in manifest) and
636 origmatchfn(lfutil.splitstandin(f)) or
638 origmatchfn(lfutil.splitstandin(f)) or
637 None)
639 None)
638 return m
640 return m
639 oldmatch = installmatchfn(overridematch)
641 oldmatch = installmatchfn(overridematch)
640 listpats = []
642 listpats = []
641 for pat in pats:
643 for pat in pats:
642 if match_.patkind(pat) is not None:
644 if match_.patkind(pat) is not None:
643 listpats.append(pat)
645 listpats.append(pat)
644 else:
646 else:
645 listpats.append(makestandin(pat))
647 listpats.append(makestandin(pat))
646
648
647 try:
649 try:
648 origcopyfile = util.copyfile
650 origcopyfile = util.copyfile
649 copiedfiles = []
651 copiedfiles = []
650 def overridecopyfile(src, dest):
652 def overridecopyfile(src, dest):
651 if (lfutil.shortname in src and
653 if (lfutil.shortname in src and
652 dest.startswith(repo.wjoin(lfutil.shortname))):
654 dest.startswith(repo.wjoin(lfutil.shortname))):
653 destlfile = dest.replace(lfutil.shortname, '')
655 destlfile = dest.replace(lfutil.shortname, '')
654 if not opts['force'] and os.path.exists(destlfile):
656 if not opts['force'] and os.path.exists(destlfile):
655 raise IOError('',
657 raise IOError('',
656 _('destination largefile already exists'))
658 _('destination largefile already exists'))
657 copiedfiles.append((src, dest))
659 copiedfiles.append((src, dest))
658 origcopyfile(src, dest)
660 origcopyfile(src, dest)
659
661
660 util.copyfile = overridecopyfile
662 util.copyfile = overridecopyfile
661 result += orig(ui, repo, listpats, opts, rename)
663 result += orig(ui, repo, listpats, opts, rename)
662 finally:
664 finally:
663 util.copyfile = origcopyfile
665 util.copyfile = origcopyfile
664
666
665 lfdirstate = lfutil.openlfdirstate(ui, repo)
667 lfdirstate = lfutil.openlfdirstate(ui, repo)
666 for (src, dest) in copiedfiles:
668 for (src, dest) in copiedfiles:
667 if (lfutil.shortname in src and
669 if (lfutil.shortname in src and
668 dest.startswith(repo.wjoin(lfutil.shortname))):
670 dest.startswith(repo.wjoin(lfutil.shortname))):
669 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
671 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
670 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
672 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
671 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
673 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
672 if not os.path.isdir(destlfiledir):
674 if not os.path.isdir(destlfiledir):
673 os.makedirs(destlfiledir)
675 os.makedirs(destlfiledir)
674 if rename:
676 if rename:
675 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
677 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
676
678
677 # The file is gone, but this deletes any empty parent
679 # The file is gone, but this deletes any empty parent
678 # directories as a side-effect.
680 # directories as a side-effect.
679 util.unlinkpath(repo.wjoin(srclfile), True)
681 util.unlinkpath(repo.wjoin(srclfile), True)
680 lfdirstate.remove(srclfile)
682 lfdirstate.remove(srclfile)
681 else:
683 else:
682 util.copyfile(repo.wjoin(srclfile),
684 util.copyfile(repo.wjoin(srclfile),
683 repo.wjoin(destlfile))
685 repo.wjoin(destlfile))
684
686
685 lfdirstate.add(destlfile)
687 lfdirstate.add(destlfile)
686 lfdirstate.write()
688 lfdirstate.write()
687 except util.Abort as e:
689 except util.Abort as e:
688 if str(e) != _('no files to copy'):
690 if str(e) != _('no files to copy'):
689 raise e
691 raise e
690 else:
692 else:
691 nolfiles = True
693 nolfiles = True
692 finally:
694 finally:
693 restorematchfn()
695 restorematchfn()
694 wlock.release()
696 wlock.release()
695
697
696 if nolfiles and nonormalfiles:
698 if nolfiles and nonormalfiles:
697 raise util.Abort(_('no files to copy'))
699 raise util.Abort(_('no files to copy'))
698
700
699 return result
701 return result
700
702
701 # 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
702 # changes to other largefiles accidentally. This means we have to keep
704 # changes to other largefiles accidentally. This means we have to keep
703 # 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
704 # the necessary largefiles.
706 # the necessary largefiles.
705 #
707 #
706 # Standins are only updated (to match the hash of largefiles) before
708 # Standins are only updated (to match the hash of largefiles) before
707 # commits. Update the standins then run the original revert, changing
709 # commits. Update the standins then run the original revert, changing
708 # the matcher to hit standins instead of largefiles. Based on the
710 # the matcher to hit standins instead of largefiles. Based on the
709 # resulting standins update the largefiles.
711 # resulting standins update the largefiles.
710 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
712 def overriderevert(orig, ui, repo, ctx, parents, *pats, **opts):
711 # 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)
712 # 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
713 # prevent others from changing them in their incorrect state.
715 # prevent others from changing them in their incorrect state.
714 wlock = repo.wlock()
716 wlock = repo.wlock()
715 try:
717 try:
716 lfdirstate = lfutil.openlfdirstate(ui, repo)
718 lfdirstate = lfutil.openlfdirstate(ui, repo)
717 s = lfutil.lfdirstatestatus(lfdirstate, repo)
719 s = lfutil.lfdirstatestatus(lfdirstate, repo)
718 lfdirstate.write()
720 lfdirstate.write()
719 for lfile in s.modified:
721 for lfile in s.modified:
720 lfutil.updatestandin(repo, lfutil.standin(lfile))
722 lfutil.updatestandin(repo, lfutil.standin(lfile))
721 for lfile in s.deleted:
723 for lfile in s.deleted:
722 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
724 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
723 os.unlink(repo.wjoin(lfutil.standin(lfile)))
725 os.unlink(repo.wjoin(lfutil.standin(lfile)))
724
726
725 oldstandins = lfutil.getstandinsstate(repo)
727 oldstandins = lfutil.getstandinsstate(repo)
726
728
727 def overridematch(mctx, pats=[], opts={}, globbed=False,
729 def overridematch(mctx, pats=[], opts={}, globbed=False,
728 default='relpath', badfn=None):
730 default='relpath', badfn=None):
729 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
731 match = oldmatch(mctx, pats, opts, globbed, default, badfn=badfn)
730 m = copy.copy(match)
732 m = copy.copy(match)
731
733
732 # revert supports recursing into subrepos, and though largefiles
734 # revert supports recursing into subrepos, and though largefiles
733 # currently doesn't work correctly in that case, this match is
735 # currently doesn't work correctly in that case, this match is
734 # 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
735 # this invocation of match.
737 # this invocation of match.
736 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
738 lfdirstate = lfutil.openlfdirstate(mctx.repo().ui, mctx.repo(),
737 False)
739 False)
738
740
739 def tostandin(f):
741 def tostandin(f):
740 standin = lfutil.standin(f)
742 standin = lfutil.standin(f)
741 if standin in ctx or standin in mctx:
743 if standin in ctx or standin in mctx:
742 return standin
744 return standin
743 elif standin in repo[None] or lfdirstate[f] == 'r':
745 elif standin in repo[None] or lfdirstate[f] == 'r':
744 return None
746 return None
745 return f
747 return f
746 m._files = [tostandin(f) for f in m._files]
748 m._files = [tostandin(f) for f in m._files]
747 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]
748 m._fileroots = set(m._files)
750 m._fileroots = set(m._files)
749 origmatchfn = m.matchfn
751 origmatchfn = m.matchfn
750 def matchfn(f):
752 def matchfn(f):
751 if lfutil.isstandin(f):
753 if lfutil.isstandin(f):
752 return (origmatchfn(lfutil.splitstandin(f)) and
754 return (origmatchfn(lfutil.splitstandin(f)) and
753 (f in ctx or f in mctx))
755 (f in ctx or f in mctx))
754 return origmatchfn(f)
756 return origmatchfn(f)
755 m.matchfn = matchfn
757 m.matchfn = matchfn
756 return m
758 return m
757 oldmatch = installmatchfn(overridematch)
759 oldmatch = installmatchfn(overridematch)
758 try:
760 try:
759 orig(ui, repo, ctx, parents, *pats, **opts)
761 orig(ui, repo, ctx, parents, *pats, **opts)
760 finally:
762 finally:
761 restorematchfn()
763 restorematchfn()
762
764
763 newstandins = lfutil.getstandinsstate(repo)
765 newstandins = lfutil.getstandinsstate(repo)
764 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
766 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
765 # lfdirstate should be 'normallookup'-ed for updated files,
767 # lfdirstate should be 'normallookup'-ed for updated files,
766 # because reverting doesn't touch dirstate for 'normal' files
768 # because reverting doesn't touch dirstate for 'normal' files
767 # when target revision is explicitly specified: in such case,
769 # when target revision is explicitly specified: in such case,
768 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
770 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
769 # of target (standin) file.
771 # of target (standin) file.
770 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
772 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
771 normallookup=True)
773 normallookup=True)
772
774
773 finally:
775 finally:
774 wlock.release()
776 wlock.release()
775
777
776 # 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
777 # largefiles updated remotely
779 # largefiles updated remotely
778 def overridepull(orig, ui, repo, source=None, **opts):
780 def overridepull(orig, ui, repo, source=None, **opts):
779 revsprepull = len(repo)
781 revsprepull = len(repo)
780 if not source:
782 if not source:
781 source = 'default'
783 source = 'default'
782 repo.lfpullsource = source
784 repo.lfpullsource = source
783 result = orig(ui, repo, source, **opts)
785 result = orig(ui, repo, source, **opts)
784 revspostpull = len(repo)
786 revspostpull = len(repo)
785 lfrevs = opts.get('lfrev', [])
787 lfrevs = opts.get('lfrev', [])
786 if opts.get('all_largefiles'):
788 if opts.get('all_largefiles'):
787 lfrevs.append('pulled()')
789 lfrevs.append('pulled()')
788 if lfrevs and revspostpull > revsprepull:
790 if lfrevs and revspostpull > revsprepull:
789 numcached = 0
791 numcached = 0
790 repo.firstpulled = revsprepull # for pulled() revset expression
792 repo.firstpulled = revsprepull # for pulled() revset expression
791 try:
793 try:
792 for rev in scmutil.revrange(repo, lfrevs):
794 for rev in scmutil.revrange(repo, lfrevs):
793 ui.note(_('pulling largefiles for revision %s\n') % rev)
795 ui.note(_('pulling largefiles for revision %s\n') % rev)
794 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
796 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
795 numcached += len(cached)
797 numcached += len(cached)
796 finally:
798 finally:
797 del repo.firstpulled
799 del repo.firstpulled
798 ui.status(_("%d largefiles cached\n") % numcached)
800 ui.status(_("%d largefiles cached\n") % numcached)
799 return result
801 return result
800
802
801 def pulledrevsetsymbol(repo, subset, x):
803 def pulledrevsetsymbol(repo, subset, x):
802 """``pulled()``
804 """``pulled()``
803 Changesets that just has been pulled.
805 Changesets that just has been pulled.
804
806
805 Only available with largefiles from pull --lfrev expressions.
807 Only available with largefiles from pull --lfrev expressions.
806
808
807 .. container:: verbose
809 .. container:: verbose
808
810
809 Some examples:
811 Some examples:
810
812
811 - pull largefiles for all new changesets::
813 - pull largefiles for all new changesets::
812
814
813 hg pull -lfrev "pulled()"
815 hg pull -lfrev "pulled()"
814
816
815 - pull largefiles for all new branch heads::
817 - pull largefiles for all new branch heads::
816
818
817 hg pull -lfrev "head(pulled()) and not closed()"
819 hg pull -lfrev "head(pulled()) and not closed()"
818
820
819 """
821 """
820
822
821 try:
823 try:
822 firstpulled = repo.firstpulled
824 firstpulled = repo.firstpulled
823 except AttributeError:
825 except AttributeError:
824 raise util.Abort(_("pulled() only available in --lfrev"))
826 raise util.Abort(_("pulled() only available in --lfrev"))
825 return revset.baseset([r for r in subset if r >= firstpulled])
827 return revset.baseset([r for r in subset if r >= firstpulled])
826
828
827 def overrideclone(orig, ui, source, dest=None, **opts):
829 def overrideclone(orig, ui, source, dest=None, **opts):
828 d = dest
830 d = dest
829 if d is None:
831 if d is None:
830 d = hg.defaultdest(source)
832 d = hg.defaultdest(source)
831 if opts.get('all_largefiles') and not hg.islocal(d):
833 if opts.get('all_largefiles') and not hg.islocal(d):
832 raise util.Abort(_(
834 raise util.Abort(_(
833 '--all-largefiles is incompatible with non-local destination %s') %
835 '--all-largefiles is incompatible with non-local destination %s') %
834 d)
836 d)
835
837
836 return orig(ui, source, dest, **opts)
838 return orig(ui, source, dest, **opts)
837
839
838 def hgclone(orig, ui, opts, *args, **kwargs):
840 def hgclone(orig, ui, opts, *args, **kwargs):
839 result = orig(ui, opts, *args, **kwargs)
841 result = orig(ui, opts, *args, **kwargs)
840
842
841 if result is not None:
843 if result is not None:
842 sourcerepo, destrepo = result
844 sourcerepo, destrepo = result
843 repo = destrepo.local()
845 repo = destrepo.local()
844
846
845 # 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
846 # 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
847 # hgrc can't be updated.
849 # hgrc can't be updated.
848 if not repo:
850 if not repo:
849 return result
851 return result
850
852
851 # If largefiles is required for this repo, permanently enable it locally
853 # If largefiles is required for this repo, permanently enable it locally
852 if 'largefiles' in repo.requirements:
854 if 'largefiles' in repo.requirements:
853 fp = repo.vfs('hgrc', 'a', text=True)
855 fp = repo.vfs('hgrc', 'a', text=True)
854 try:
856 try:
855 fp.write('\n[extensions]\nlargefiles=\n')
857 fp.write('\n[extensions]\nlargefiles=\n')
856 finally:
858 finally:
857 fp.close()
859 fp.close()
858
860
859 # 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
860 # 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
861 # 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.
862 if opts.get('all_largefiles'):
864 if opts.get('all_largefiles'):
863 success, missing = lfcommands.downloadlfiles(ui, repo, None)
865 success, missing = lfcommands.downloadlfiles(ui, repo, None)
864
866
865 if missing != 0:
867 if missing != 0:
866 return None
868 return None
867
869
868 return result
870 return result
869
871
870 def overriderebase(orig, ui, repo, **opts):
872 def overriderebase(orig, ui, repo, **opts):
871 if not util.safehasattr(repo, '_largefilesenabled'):
873 if not util.safehasattr(repo, '_largefilesenabled'):
872 return orig(ui, repo, **opts)
874 return orig(ui, repo, **opts)
873
875
874 resuming = opts.get('continue')
876 resuming = opts.get('continue')
875 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
877 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
876 repo._lfstatuswriters.append(lambda *msg, **opts: None)
878 repo._lfstatuswriters.append(lambda *msg, **opts: None)
877 try:
879 try:
878 return orig(ui, repo, **opts)
880 return orig(ui, repo, **opts)
879 finally:
881 finally:
880 repo._lfstatuswriters.pop()
882 repo._lfstatuswriters.pop()
881 repo._lfcommithooks.pop()
883 repo._lfcommithooks.pop()
882
884
883 def overridearchivecmd(orig, ui, repo, dest, **opts):
885 def overridearchivecmd(orig, ui, repo, dest, **opts):
884 repo.unfiltered().lfstatus = True
886 repo.unfiltered().lfstatus = True
885
887
886 try:
888 try:
887 return orig(ui, repo.unfiltered(), dest, **opts)
889 return orig(ui, repo.unfiltered(), dest, **opts)
888 finally:
890 finally:
889 repo.unfiltered().lfstatus = False
891 repo.unfiltered().lfstatus = False
890
892
891 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
893 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
892 prefix='', mtime=None, subrepos=None):
894 prefix='', mtime=None, subrepos=None):
893 if not repo.lfstatus:
895 if not repo.lfstatus:
894 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
896 return orig(repo, dest, node, kind, decode, matchfn, prefix, mtime,
895 subrepos)
897 subrepos)
896
898
897 # No need to lock because we are only reading history and
899 # No need to lock because we are only reading history and
898 # largefile caches, neither of which are modified.
900 # largefile caches, neither of which are modified.
899 if node is not None:
901 if node is not None:
900 lfcommands.cachelfiles(repo.ui, repo, node)
902 lfcommands.cachelfiles(repo.ui, repo, node)
901
903
902 if kind not in archival.archivers:
904 if kind not in archival.archivers:
903 raise util.Abort(_("unknown archive type '%s'") % kind)
905 raise util.Abort(_("unknown archive type '%s'") % kind)
904
906
905 ctx = repo[node]
907 ctx = repo[node]
906
908
907 if kind == 'files':
909 if kind == 'files':
908 if prefix:
910 if prefix:
909 raise util.Abort(
911 raise util.Abort(
910 _('cannot give prefix when archiving to files'))
912 _('cannot give prefix when archiving to files'))
911 else:
913 else:
912 prefix = archival.tidyprefix(dest, kind, prefix)
914 prefix = archival.tidyprefix(dest, kind, prefix)
913
915
914 def write(name, mode, islink, getdata):
916 def write(name, mode, islink, getdata):
915 if matchfn and not matchfn(name):
917 if matchfn and not matchfn(name):
916 return
918 return
917 data = getdata()
919 data = getdata()
918 if decode:
920 if decode:
919 data = repo.wwritedata(name, data)
921 data = repo.wwritedata(name, data)
920 archiver.addfile(prefix + name, mode, islink, data)
922 archiver.addfile(prefix + name, mode, islink, data)
921
923
922 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
924 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
923
925
924 if repo.ui.configbool("ui", "archivemeta", True):
926 if repo.ui.configbool("ui", "archivemeta", True):
925 write('.hg_archival.txt', 0o644, False,
927 write('.hg_archival.txt', 0o644, False,
926 lambda: archival.buildmetadata(ctx))
928 lambda: archival.buildmetadata(ctx))
927
929
928 for f in ctx:
930 for f in ctx:
929 ff = ctx.flags(f)
931 ff = ctx.flags(f)
930 getdata = ctx[f].data
932 getdata = ctx[f].data
931 if lfutil.isstandin(f):
933 if lfutil.isstandin(f):
932 if node is not None:
934 if node is not None:
933 path = lfutil.findfile(repo, getdata().strip())
935 path = lfutil.findfile(repo, getdata().strip())
934
936
935 if path is None:
937 if path is None:
936 raise util.Abort(
938 raise util.Abort(
937 _('largefile %s not found in repo store or system cache')
939 _('largefile %s not found in repo store or system cache')
938 % lfutil.splitstandin(f))
940 % lfutil.splitstandin(f))
939 else:
941 else:
940 path = lfutil.splitstandin(f)
942 path = lfutil.splitstandin(f)
941
943
942 f = lfutil.splitstandin(f)
944 f = lfutil.splitstandin(f)
943
945
944 def getdatafn():
946 def getdatafn():
945 fd = None
947 fd = None
946 try:
948 try:
947 fd = open(path, 'rb')
949 fd = open(path, 'rb')
948 return fd.read()
950 return fd.read()
949 finally:
951 finally:
950 if fd:
952 if fd:
951 fd.close()
953 fd.close()
952
954
953 getdata = getdatafn
955 getdata = getdatafn
954 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)
955
957
956 if subrepos:
958 if subrepos:
957 for subpath in sorted(ctx.substate):
959 for subpath in sorted(ctx.substate):
958 sub = ctx.workingsub(subpath)
960 sub = ctx.workingsub(subpath)
959 submatch = match_.narrowmatcher(subpath, matchfn)
961 submatch = match_.narrowmatcher(subpath, matchfn)
960 sub._repo.lfstatus = True
962 sub._repo.lfstatus = True
961 sub.archive(archiver, prefix, submatch)
963 sub.archive(archiver, prefix, submatch)
962
964
963 archiver.done()
965 archiver.done()
964
966
965 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
967 def hgsubrepoarchive(orig, repo, archiver, prefix, match=None):
966 if not repo._repo.lfstatus:
968 if not repo._repo.lfstatus:
967 return orig(repo, archiver, prefix, match)
969 return orig(repo, archiver, prefix, match)
968
970
969 repo._get(repo._state + ('hg',))
971 repo._get(repo._state + ('hg',))
970 rev = repo._state[1]
972 rev = repo._state[1]
971 ctx = repo._repo[rev]
973 ctx = repo._repo[rev]
972
974
973 if ctx.node() is not None:
975 if ctx.node() is not None:
974 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
976 lfcommands.cachelfiles(repo.ui, repo._repo, ctx.node())
975
977
976 def write(name, mode, islink, getdata):
978 def write(name, mode, islink, getdata):
977 # 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,
978 # so the normal matcher works here without the lfutil variants.
980 # so the normal matcher works here without the lfutil variants.
979 if match and not match(f):
981 if match and not match(f):
980 return
982 return
981 data = getdata()
983 data = getdata()
982
984
983 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
985 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
984
986
985 for f in ctx:
987 for f in ctx:
986 ff = ctx.flags(f)
988 ff = ctx.flags(f)
987 getdata = ctx[f].data
989 getdata = ctx[f].data
988 if lfutil.isstandin(f):
990 if lfutil.isstandin(f):
989 if ctx.node() is not None:
991 if ctx.node() is not None:
990 path = lfutil.findfile(repo._repo, getdata().strip())
992 path = lfutil.findfile(repo._repo, getdata().strip())
991
993
992 if path is None:
994 if path is None:
993 raise util.Abort(
995 raise util.Abort(
994 _('largefile %s not found in repo store or system cache')
996 _('largefile %s not found in repo store or system cache')
995 % lfutil.splitstandin(f))
997 % lfutil.splitstandin(f))
996 else:
998 else:
997 path = lfutil.splitstandin(f)
999 path = lfutil.splitstandin(f)
998
1000
999 f = lfutil.splitstandin(f)
1001 f = lfutil.splitstandin(f)
1000
1002
1001 def getdatafn():
1003 def getdatafn():
1002 fd = None
1004 fd = None
1003 try:
1005 try:
1004 fd = open(os.path.join(prefix, path), 'rb')
1006 fd = open(os.path.join(prefix, path), 'rb')
1005 return fd.read()
1007 return fd.read()
1006 finally:
1008 finally:
1007 if fd:
1009 if fd:
1008 fd.close()
1010 fd.close()
1009
1011
1010 getdata = getdatafn
1012 getdata = getdatafn
1011
1013
1012 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)
1013
1015
1014 for subpath in sorted(ctx.substate):
1016 for subpath in sorted(ctx.substate):
1015 sub = ctx.workingsub(subpath)
1017 sub = ctx.workingsub(subpath)
1016 submatch = match_.narrowmatcher(subpath, match)
1018 submatch = match_.narrowmatcher(subpath, match)
1017 sub._repo.lfstatus = True
1019 sub._repo.lfstatus = True
1018 sub.archive(archiver, prefix + repo._path + '/', submatch)
1020 sub.archive(archiver, prefix + repo._path + '/', submatch)
1019
1021
1020 # 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
1021 # standin until a commit. cmdutil.bailifchanged() raises an exception
1023 # standin until a commit. cmdutil.bailifchanged() raises an exception
1022 # 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
1023 # largefiles were changed. This is used by bisect, backout and fetch.
1025 # largefiles were changed. This is used by bisect, backout and fetch.
1024 def overridebailifchanged(orig, repo, *args, **kwargs):
1026 def overridebailifchanged(orig, repo, *args, **kwargs):
1025 orig(repo, *args, **kwargs)
1027 orig(repo, *args, **kwargs)
1026 repo.lfstatus = True
1028 repo.lfstatus = True
1027 s = repo.status()
1029 s = repo.status()
1028 repo.lfstatus = False
1030 repo.lfstatus = False
1029 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:
1030 raise util.Abort(_('uncommitted changes'))
1032 raise util.Abort(_('uncommitted changes'))
1031
1033
1032 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1034 def cmdutilforget(orig, ui, repo, match, prefix, explicitonly):
1033 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1035 normalmatcher = composenormalfilematcher(match, repo[None].manifest())
1034 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1036 bad, forgot = orig(ui, repo, normalmatcher, prefix, explicitonly)
1035 m = composelargefilematcher(match, repo[None].manifest())
1037 m = composelargefilematcher(match, repo[None].manifest())
1036
1038
1037 try:
1039 try:
1038 repo.lfstatus = True
1040 repo.lfstatus = True
1039 s = repo.status(match=m, clean=True)
1041 s = repo.status(match=m, clean=True)
1040 finally:
1042 finally:
1041 repo.lfstatus = False
1043 repo.lfstatus = False
1042 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1044 forget = sorted(s.modified + s.added + s.deleted + s.clean)
1043 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()]
1044
1046
1045 for f in forget:
1047 for f in forget:
1046 if lfutil.standin(f) not in repo.dirstate and not \
1048 if lfutil.standin(f) not in repo.dirstate and not \
1047 repo.wvfs.isdir(lfutil.standin(f)):
1049 repo.wvfs.isdir(lfutil.standin(f)):
1048 ui.warn(_('not removing %s: file is already untracked\n')
1050 ui.warn(_('not removing %s: file is already untracked\n')
1049 % m.rel(f))
1051 % m.rel(f))
1050 bad.append(f)
1052 bad.append(f)
1051
1053
1052 for f in forget:
1054 for f in forget:
1053 if ui.verbose or not m.exact(f):
1055 if ui.verbose or not m.exact(f):
1054 ui.status(_('removing %s\n') % m.rel(f))
1056 ui.status(_('removing %s\n') % m.rel(f))
1055
1057
1056 # 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
1057 # repository and we could race in-between.
1059 # repository and we could race in-between.
1058 wlock = repo.wlock()
1060 wlock = repo.wlock()
1059 try:
1061 try:
1060 lfdirstate = lfutil.openlfdirstate(ui, repo)
1062 lfdirstate = lfutil.openlfdirstate(ui, repo)
1061 for f in forget:
1063 for f in forget:
1062 if lfdirstate[f] == 'a':
1064 if lfdirstate[f] == 'a':
1063 lfdirstate.drop(f)
1065 lfdirstate.drop(f)
1064 else:
1066 else:
1065 lfdirstate.remove(f)
1067 lfdirstate.remove(f)
1066 lfdirstate.write()
1068 lfdirstate.write()
1067 standins = [lfutil.standin(f) for f in forget]
1069 standins = [lfutil.standin(f) for f in forget]
1068 for f in standins:
1070 for f in standins:
1069 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1071 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
1070 rejected = repo[None].forget(standins)
1072 rejected = repo[None].forget(standins)
1071 finally:
1073 finally:
1072 wlock.release()
1074 wlock.release()
1073
1075
1074 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())
1075 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)
1076 return bad, forgot
1078 return bad, forgot
1077
1079
1078 def _getoutgoings(repo, other, missing, addfunc):
1080 def _getoutgoings(repo, other, missing, addfunc):
1079 """get pairs of filename and largefile hash in outgoing revisions
1081 """get pairs of filename and largefile hash in outgoing revisions
1080 in 'missing'.
1082 in 'missing'.
1081
1083
1082 largefiles already existing on 'other' repository are ignored.
1084 largefiles already existing on 'other' repository are ignored.
1083
1085
1084 'addfunc' is invoked with each unique pairs of filename and
1086 'addfunc' is invoked with each unique pairs of filename and
1085 largefile hash value.
1087 largefile hash value.
1086 """
1088 """
1087 knowns = set()
1089 knowns = set()
1088 lfhashes = set()
1090 lfhashes = set()
1089 def dedup(fn, lfhash):
1091 def dedup(fn, lfhash):
1090 k = (fn, lfhash)
1092 k = (fn, lfhash)
1091 if k not in knowns:
1093 if k not in knowns:
1092 knowns.add(k)
1094 knowns.add(k)
1093 lfhashes.add(lfhash)
1095 lfhashes.add(lfhash)
1094 lfutil.getlfilestoupload(repo, missing, dedup)
1096 lfutil.getlfilestoupload(repo, missing, dedup)
1095 if lfhashes:
1097 if lfhashes:
1096 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1098 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1097 for fn, lfhash in knowns:
1099 for fn, lfhash in knowns:
1098 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1100 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1099 addfunc(fn, lfhash)
1101 addfunc(fn, lfhash)
1100
1102
1101 def outgoinghook(ui, repo, other, opts, missing):
1103 def outgoinghook(ui, repo, other, opts, missing):
1102 if opts.pop('large', None):
1104 if opts.pop('large', None):
1103 lfhashes = set()
1105 lfhashes = set()
1104 if ui.debugflag:
1106 if ui.debugflag:
1105 toupload = {}
1107 toupload = {}
1106 def addfunc(fn, lfhash):
1108 def addfunc(fn, lfhash):
1107 if fn not in toupload:
1109 if fn not in toupload:
1108 toupload[fn] = []
1110 toupload[fn] = []
1109 toupload[fn].append(lfhash)
1111 toupload[fn].append(lfhash)
1110 lfhashes.add(lfhash)
1112 lfhashes.add(lfhash)
1111 def showhashes(fn):
1113 def showhashes(fn):
1112 for lfhash in sorted(toupload[fn]):
1114 for lfhash in sorted(toupload[fn]):
1113 ui.debug(' %s\n' % (lfhash))
1115 ui.debug(' %s\n' % (lfhash))
1114 else:
1116 else:
1115 toupload = set()
1117 toupload = set()
1116 def addfunc(fn, lfhash):
1118 def addfunc(fn, lfhash):
1117 toupload.add(fn)
1119 toupload.add(fn)
1118 lfhashes.add(lfhash)
1120 lfhashes.add(lfhash)
1119 def showhashes(fn):
1121 def showhashes(fn):
1120 pass
1122 pass
1121 _getoutgoings(repo, other, missing, addfunc)
1123 _getoutgoings(repo, other, missing, addfunc)
1122
1124
1123 if not toupload:
1125 if not toupload:
1124 ui.status(_('largefiles: no files to upload\n'))
1126 ui.status(_('largefiles: no files to upload\n'))
1125 else:
1127 else:
1126 ui.status(_('largefiles to upload (%d entities):\n')
1128 ui.status(_('largefiles to upload (%d entities):\n')
1127 % (len(lfhashes)))
1129 % (len(lfhashes)))
1128 for file in sorted(toupload):
1130 for file in sorted(toupload):
1129 ui.status(lfutil.splitstandin(file) + '\n')
1131 ui.status(lfutil.splitstandin(file) + '\n')
1130 showhashes(file)
1132 showhashes(file)
1131 ui.status('\n')
1133 ui.status('\n')
1132
1134
1133 def summaryremotehook(ui, repo, opts, changes):
1135 def summaryremotehook(ui, repo, opts, changes):
1134 largeopt = opts.get('large', False)
1136 largeopt = opts.get('large', False)
1135 if changes is None:
1137 if changes is None:
1136 if largeopt:
1138 if largeopt:
1137 return (False, True) # only outgoing check is needed
1139 return (False, True) # only outgoing check is needed
1138 else:
1140 else:
1139 return (False, False)
1141 return (False, False)
1140 elif largeopt:
1142 elif largeopt:
1141 url, branch, peer, outgoing = changes[1]
1143 url, branch, peer, outgoing = changes[1]
1142 if peer is None:
1144 if peer is None:
1143 # i18n: column positioning for "hg summary"
1145 # i18n: column positioning for "hg summary"
1144 ui.status(_('largefiles: (no remote repo)\n'))
1146 ui.status(_('largefiles: (no remote repo)\n'))
1145 return
1147 return
1146
1148
1147 toupload = set()
1149 toupload = set()
1148 lfhashes = set()
1150 lfhashes = set()
1149 def addfunc(fn, lfhash):
1151 def addfunc(fn, lfhash):
1150 toupload.add(fn)
1152 toupload.add(fn)
1151 lfhashes.add(lfhash)
1153 lfhashes.add(lfhash)
1152 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1154 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1153
1155
1154 if not toupload:
1156 if not toupload:
1155 # i18n: column positioning for "hg summary"
1157 # i18n: column positioning for "hg summary"
1156 ui.status(_('largefiles: (no files to upload)\n'))
1158 ui.status(_('largefiles: (no files to upload)\n'))
1157 else:
1159 else:
1158 # i18n: column positioning for "hg summary"
1160 # i18n: column positioning for "hg summary"
1159 ui.status(_('largefiles: %d entities for %d files to upload\n')
1161 ui.status(_('largefiles: %d entities for %d files to upload\n')
1160 % (len(lfhashes), len(toupload)))
1162 % (len(lfhashes), len(toupload)))
1161
1163
1162 def overridesummary(orig, ui, repo, *pats, **opts):
1164 def overridesummary(orig, ui, repo, *pats, **opts):
1163 try:
1165 try:
1164 repo.lfstatus = True
1166 repo.lfstatus = True
1165 orig(ui, repo, *pats, **opts)
1167 orig(ui, repo, *pats, **opts)
1166 finally:
1168 finally:
1167 repo.lfstatus = False
1169 repo.lfstatus = False
1168
1170
1169 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1171 def scmutiladdremove(orig, repo, matcher, prefix, opts={}, dry_run=None,
1170 similarity=None):
1172 similarity=None):
1171 if not lfutil.islfilesrepo(repo):
1173 if not lfutil.islfilesrepo(repo):
1172 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1174 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1173 # Get the list of missing largefiles so we can remove them
1175 # Get the list of missing largefiles so we can remove them
1174 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1176 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1175 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1177 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1176 False, False, False)
1178 False, False, False)
1177
1179
1178 # 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
1179 # to have handled by original addremove. Monkey patching here makes sure
1181 # to have handled by original addremove. Monkey patching here makes sure
1180 # 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
1181 # confused state later.
1183 # confused state later.
1182 if s.deleted:
1184 if s.deleted:
1183 m = copy.copy(matcher)
1185 m = copy.copy(matcher)
1184
1186
1185 # 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
1186 # 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
1187 # 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
1188 # matches to those in the deleted status list.
1190 # matches to those in the deleted status list.
1189 matchfn = m.matchfn
1191 matchfn = m.matchfn
1190 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1192 m.matchfn = lambda f: f in s.deleted and matchfn(f)
1191
1193
1192 removelargefiles(repo.ui, repo, True, m, **opts)
1194 removelargefiles(repo.ui, repo, True, m, **opts)
1193 # 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
1194 # largefiles will be
1196 # largefiles will be
1195 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1197 added, bad = addlargefiles(repo.ui, repo, True, matcher, **opts)
1196 # 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
1197 # 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
1198 # largefiles by passing a matcher that will ignore them.
1200 # largefiles by passing a matcher that will ignore them.
1199 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1201 matcher = composenormalfilematcher(matcher, repo[None].manifest(), added)
1200 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1202 return orig(repo, matcher, prefix, opts, dry_run, similarity)
1201
1203
1202 # Calling purge with --all will cause the largefiles to be deleted.
1204 # Calling purge with --all will cause the largefiles to be deleted.
1203 # Override repo.status to prevent this from happening.
1205 # Override repo.status to prevent this from happening.
1204 def overridepurge(orig, ui, repo, *dirs, **opts):
1206 def overridepurge(orig, ui, repo, *dirs, **opts):
1205 # 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
1206 # 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
1207 # 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
1208 # 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
1209 # 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
1210 # one, defeating the monkey patch.
1212 # one, defeating the monkey patch.
1211 #
1213 #
1212 # 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
1213 # cleaner instead.
1215 # cleaner instead.
1214 repo = repo.unfiltered()
1216 repo = repo.unfiltered()
1215 oldstatus = repo.status
1217 oldstatus = repo.status
1216 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1218 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1217 clean=False, unknown=False, listsubrepos=False):
1219 clean=False, unknown=False, listsubrepos=False):
1218 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1220 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1219 listsubrepos)
1221 listsubrepos)
1220 lfdirstate = lfutil.openlfdirstate(ui, repo)
1222 lfdirstate = lfutil.openlfdirstate(ui, repo)
1221 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1223 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1222 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1224 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1223 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1225 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1224 unknown, ignored, r.clean)
1226 unknown, ignored, r.clean)
1225 repo.status = overridestatus
1227 repo.status = overridestatus
1226 orig(ui, repo, *dirs, **opts)
1228 orig(ui, repo, *dirs, **opts)
1227 repo.status = oldstatus
1229 repo.status = oldstatus
1228 def overriderollback(orig, ui, repo, **opts):
1230 def overriderollback(orig, ui, repo, **opts):
1229 wlock = repo.wlock()
1231 wlock = repo.wlock()
1230 try:
1232 try:
1231 before = repo.dirstate.parents()
1233 before = repo.dirstate.parents()
1232 orphans = set(f for f in repo.dirstate
1234 orphans = set(f for f in repo.dirstate
1233 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1235 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1234 result = orig(ui, repo, **opts)
1236 result = orig(ui, repo, **opts)
1235 after = repo.dirstate.parents()
1237 after = repo.dirstate.parents()
1236 if before == after:
1238 if before == after:
1237 return result # no need to restore standins
1239 return result # no need to restore standins
1238
1240
1239 pctx = repo['.']
1241 pctx = repo['.']
1240 for f in repo.dirstate:
1242 for f in repo.dirstate:
1241 if lfutil.isstandin(f):
1243 if lfutil.isstandin(f):
1242 orphans.discard(f)
1244 orphans.discard(f)
1243 if repo.dirstate[f] == 'r':
1245 if repo.dirstate[f] == 'r':
1244 repo.wvfs.unlinkpath(f, ignoremissing=True)
1246 repo.wvfs.unlinkpath(f, ignoremissing=True)
1245 elif f in pctx:
1247 elif f in pctx:
1246 fctx = pctx[f]
1248 fctx = pctx[f]
1247 repo.wwrite(f, fctx.data(), fctx.flags())
1249 repo.wwrite(f, fctx.data(), fctx.flags())
1248 else:
1250 else:
1249 # content of standin is not so important in 'a',
1251 # content of standin is not so important in 'a',
1250 # 'm' or 'n' (coming from the 2nd parent) cases
1252 # 'm' or 'n' (coming from the 2nd parent) cases
1251 lfutil.writestandin(repo, f, '', False)
1253 lfutil.writestandin(repo, f, '', False)
1252 for standin in orphans:
1254 for standin in orphans:
1253 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1255 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1254
1256
1255 lfdirstate = lfutil.openlfdirstate(ui, repo)
1257 lfdirstate = lfutil.openlfdirstate(ui, repo)
1256 orphans = set(lfdirstate)
1258 orphans = set(lfdirstate)
1257 lfiles = lfutil.listlfiles(repo)
1259 lfiles = lfutil.listlfiles(repo)
1258 for file in lfiles:
1260 for file in lfiles:
1259 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1261 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1260 orphans.discard(file)
1262 orphans.discard(file)
1261 for lfile in orphans:
1263 for lfile in orphans:
1262 lfdirstate.drop(lfile)
1264 lfdirstate.drop(lfile)
1263 lfdirstate.write()
1265 lfdirstate.write()
1264 finally:
1266 finally:
1265 wlock.release()
1267 wlock.release()
1266 return result
1268 return result
1267
1269
1268 def overridetransplant(orig, ui, repo, *revs, **opts):
1270 def overridetransplant(orig, ui, repo, *revs, **opts):
1269 resuming = opts.get('continue')
1271 resuming = opts.get('continue')
1270 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1272 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1271 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1273 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1272 try:
1274 try:
1273 result = orig(ui, repo, *revs, **opts)
1275 result = orig(ui, repo, *revs, **opts)
1274 finally:
1276 finally:
1275 repo._lfstatuswriters.pop()
1277 repo._lfstatuswriters.pop()
1276 repo._lfcommithooks.pop()
1278 repo._lfcommithooks.pop()
1277 return result
1279 return result
1278
1280
1279 def overridecat(orig, ui, repo, file1, *pats, **opts):
1281 def overridecat(orig, ui, repo, file1, *pats, **opts):
1280 ctx = scmutil.revsingle(repo, opts.get('rev'))
1282 ctx = scmutil.revsingle(repo, opts.get('rev'))
1281 err = 1
1283 err = 1
1282 notbad = set()
1284 notbad = set()
1283 m = scmutil.match(ctx, (file1,) + pats, opts)
1285 m = scmutil.match(ctx, (file1,) + pats, opts)
1284 origmatchfn = m.matchfn
1286 origmatchfn = m.matchfn
1285 def lfmatchfn(f):
1287 def lfmatchfn(f):
1286 if origmatchfn(f):
1288 if origmatchfn(f):
1287 return True
1289 return True
1288 lf = lfutil.splitstandin(f)
1290 lf = lfutil.splitstandin(f)
1289 if lf is None:
1291 if lf is None:
1290 return False
1292 return False
1291 notbad.add(lf)
1293 notbad.add(lf)
1292 return origmatchfn(lf)
1294 return origmatchfn(lf)
1293 m.matchfn = lfmatchfn
1295 m.matchfn = lfmatchfn
1294 origbadfn = m.bad
1296 origbadfn = m.bad
1295 def lfbadfn(f, msg):
1297 def lfbadfn(f, msg):
1296 if not f in notbad:
1298 if not f in notbad:
1297 origbadfn(f, msg)
1299 origbadfn(f, msg)
1298 m.bad = lfbadfn
1300 m.bad = lfbadfn
1299
1301
1300 origvisitdirfn = m.visitdir
1302 origvisitdirfn = m.visitdir
1301 def lfvisitdirfn(dir):
1303 def lfvisitdirfn(dir):
1302 if dir == lfutil.shortname:
1304 if dir == lfutil.shortname:
1303 return True
1305 return True
1304 ret = origvisitdirfn(dir)
1306 ret = origvisitdirfn(dir)
1305 if ret:
1307 if ret:
1306 return ret
1308 return ret
1307 lf = lfutil.splitstandin(dir)
1309 lf = lfutil.splitstandin(dir)
1308 if lf is None:
1310 if lf is None:
1309 return False
1311 return False
1310 return origvisitdirfn(lf)
1312 return origvisitdirfn(lf)
1311 m.visitdir = lfvisitdirfn
1313 m.visitdir = lfvisitdirfn
1312
1314
1313 for f in ctx.walk(m):
1315 for f in ctx.walk(m):
1314 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1316 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1315 pathname=f)
1317 pathname=f)
1316 lf = lfutil.splitstandin(f)
1318 lf = lfutil.splitstandin(f)
1317 if lf is None or origmatchfn(f):
1319 if lf is None or origmatchfn(f):
1318 # duplicating unreachable code from commands.cat
1320 # duplicating unreachable code from commands.cat
1319 data = ctx[f].data()
1321 data = ctx[f].data()
1320 if opts.get('decode'):
1322 if opts.get('decode'):
1321 data = repo.wwritedata(f, data)
1323 data = repo.wwritedata(f, data)
1322 fp.write(data)
1324 fp.write(data)
1323 else:
1325 else:
1324 hash = lfutil.readstandin(repo, lf, ctx.rev())
1326 hash = lfutil.readstandin(repo, lf, ctx.rev())
1325 if not lfutil.inusercache(repo.ui, hash):
1327 if not lfutil.inusercache(repo.ui, hash):
1326 store = basestore._openstore(repo)
1328 store = basestore._openstore(repo)
1327 success, missing = store.get([(lf, hash)])
1329 success, missing = store.get([(lf, hash)])
1328 if len(success) != 1:
1330 if len(success) != 1:
1329 raise util.Abort(
1331 raise util.Abort(
1330 _('largefile %s is not in cache and could not be '
1332 _('largefile %s is not in cache and could not be '
1331 'downloaded') % lf)
1333 'downloaded') % lf)
1332 path = lfutil.usercachepath(repo.ui, hash)
1334 path = lfutil.usercachepath(repo.ui, hash)
1333 fpin = open(path, "rb")
1335 fpin = open(path, "rb")
1334 for chunk in util.filechunkiter(fpin, 128 * 1024):
1336 for chunk in util.filechunkiter(fpin, 128 * 1024):
1335 fp.write(chunk)
1337 fp.write(chunk)
1336 fpin.close()
1338 fpin.close()
1337 fp.close()
1339 fp.close()
1338 err = 0
1340 err = 0
1339 return err
1341 return err
1340
1342
1341 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1343 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1342 *args, **kwargs):
1344 *args, **kwargs):
1343 wlock = repo.wlock()
1345 wlock = repo.wlock()
1344 try:
1346 try:
1345 # branch | | |
1347 # branch | | |
1346 # merge | force | partial | action
1348 # merge | force | partial | action
1347 # -------+-------+---------+--------------
1349 # -------+-------+---------+--------------
1348 # x | x | x | linear-merge
1350 # x | x | x | linear-merge
1349 # o | x | x | branch-merge
1351 # o | x | x | branch-merge
1350 # x | o | x | overwrite (as clean update)
1352 # x | o | x | overwrite (as clean update)
1351 # o | o | x | force-branch-merge (*1)
1353 # o | o | x | force-branch-merge (*1)
1352 # x | x | o | (*)
1354 # x | x | o | (*)
1353 # o | x | o | (*)
1355 # o | x | o | (*)
1354 # x | o | o | overwrite (as revert)
1356 # x | o | o | overwrite (as revert)
1355 # o | o | o | (*)
1357 # o | o | o | (*)
1356 #
1358 #
1357 # (*) don't care
1359 # (*) don't care
1358 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1360 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1359
1361
1360 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1362 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1361 unsure, s = lfdirstate.status(match_.always(repo.root,
1363 unsure, s = lfdirstate.status(match_.always(repo.root,
1362 repo.getcwd()),
1364 repo.getcwd()),
1363 [], False, False, False)
1365 [], False, False, False)
1364 pctx = repo['.']
1366 pctx = repo['.']
1365 for lfile in unsure + s.modified:
1367 for lfile in unsure + s.modified:
1366 lfileabs = repo.wvfs.join(lfile)
1368 lfileabs = repo.wvfs.join(lfile)
1367 if not os.path.exists(lfileabs):
1369 if not os.path.exists(lfileabs):
1368 continue
1370 continue
1369 lfhash = lfutil.hashrepofile(repo, lfile)
1371 lfhash = lfutil.hashrepofile(repo, lfile)
1370 standin = lfutil.standin(lfile)
1372 standin = lfutil.standin(lfile)
1371 lfutil.writestandin(repo, standin, lfhash,
1373 lfutil.writestandin(repo, standin, lfhash,
1372 lfutil.getexecutable(lfileabs))
1374 lfutil.getexecutable(lfileabs))
1373 if (standin in pctx and
1375 if (standin in pctx and
1374 lfhash == lfutil.readstandin(repo, lfile, '.')):
1376 lfhash == lfutil.readstandin(repo, lfile, '.')):
1375 lfdirstate.normal(lfile)
1377 lfdirstate.normal(lfile)
1376 for lfile in s.added:
1378 for lfile in s.added:
1377 lfutil.updatestandin(repo, lfutil.standin(lfile))
1379 lfutil.updatestandin(repo, lfutil.standin(lfile))
1378 lfdirstate.write()
1380 lfdirstate.write()
1379
1381
1380 oldstandins = lfutil.getstandinsstate(repo)
1382 oldstandins = lfutil.getstandinsstate(repo)
1381
1383
1382 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1384 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1383
1385
1384 newstandins = lfutil.getstandinsstate(repo)
1386 newstandins = lfutil.getstandinsstate(repo)
1385 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1387 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1386 if branchmerge or force or partial:
1388 if branchmerge or force or partial:
1387 filelist.extend(s.deleted + s.removed)
1389 filelist.extend(s.deleted + s.removed)
1388
1390
1389 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1391 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1390 normallookup=partial)
1392 normallookup=partial)
1391
1393
1392 return result
1394 return result
1393 finally:
1395 finally:
1394 wlock.release()
1396 wlock.release()
1395
1397
1396 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1398 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1397 result = orig(repo, files, *args, **kwargs)
1399 result = orig(repo, files, *args, **kwargs)
1398
1400
1399 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)]
1400 if filelist:
1402 if filelist:
1401 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1403 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1402 printmessage=False, normallookup=True)
1404 printmessage=False, normallookup=True)
1403
1405
1404 return result
1406 return result
General Comments 0
You need to be logged in to leave comments. Login now