##// END OF EJS Templates
largefiles: use more reasonable locking for update
Mads Kiilerich -
r21089:278bd08a default
parent child Browse files
Show More
@@ -1,1168 +1,1168 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
15 archival, merge, pathutil, revset
15 archival, merge, pathutil, revset
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18 from hgext import rebase
18 from hgext import rebase
19
19
20 import lfutil
20 import lfutil
21 import lfcommands
21 import lfcommands
22 import basestore
22 import basestore
23
23
24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24 # -- Utility functions: commonly/repeatedly needed functionality ---------------
25
25
26 def installnormalfilesmatchfn(manifest):
26 def installnormalfilesmatchfn(manifest):
27 '''overrides scmutil.match so that the matcher it returns will ignore all
27 '''overrides scmutil.match so that the matcher it returns will ignore all
28 largefiles'''
28 largefiles'''
29 oldmatch = None # for the closure
29 oldmatch = None # for the closure
30 def overridematch(ctx, pats=[], opts={}, globbed=False,
30 def overridematch(ctx, pats=[], opts={}, globbed=False,
31 default='relpath'):
31 default='relpath'):
32 match = oldmatch(ctx, pats, opts, globbed, default)
32 match = oldmatch(ctx, pats, opts, globbed, default)
33 m = copy.copy(match)
33 m = copy.copy(match)
34 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
34 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
35 manifest)
35 manifest)
36 m._files = filter(notlfile, m._files)
36 m._files = filter(notlfile, m._files)
37 m._fmap = set(m._files)
37 m._fmap = set(m._files)
38 m._always = False
38 m._always = False
39 origmatchfn = m.matchfn
39 origmatchfn = m.matchfn
40 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
40 m.matchfn = lambda f: notlfile(f) and origmatchfn(f) or None
41 return m
41 return m
42 oldmatch = installmatchfn(overridematch)
42 oldmatch = installmatchfn(overridematch)
43
43
44 def installmatchfn(f):
44 def installmatchfn(f):
45 oldmatch = scmutil.match
45 oldmatch = scmutil.match
46 setattr(f, 'oldmatch', oldmatch)
46 setattr(f, 'oldmatch', oldmatch)
47 scmutil.match = f
47 scmutil.match = f
48 return oldmatch
48 return oldmatch
49
49
50 def restorematchfn():
50 def restorematchfn():
51 '''restores scmutil.match to what it was before installnormalfilesmatchfn
51 '''restores scmutil.match to what it was before installnormalfilesmatchfn
52 was called. no-op if scmutil.match is its original function.
52 was called. no-op if scmutil.match is its original function.
53
53
54 Note that n calls to installnormalfilesmatchfn will require n calls to
54 Note that n calls to installnormalfilesmatchfn will require n calls to
55 restore matchfn to reverse'''
55 restore matchfn to reverse'''
56 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
56 scmutil.match = getattr(scmutil.match, 'oldmatch', scmutil.match)
57
57
58 def addlargefiles(ui, repo, *pats, **opts):
58 def addlargefiles(ui, repo, *pats, **opts):
59 large = opts.pop('large', None)
59 large = opts.pop('large', None)
60 lfsize = lfutil.getminsize(
60 lfsize = lfutil.getminsize(
61 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
61 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
62
62
63 lfmatcher = None
63 lfmatcher = None
64 if lfutil.islfilesrepo(repo):
64 if lfutil.islfilesrepo(repo):
65 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
65 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
66 if lfpats:
66 if lfpats:
67 lfmatcher = match_.match(repo.root, '', list(lfpats))
67 lfmatcher = match_.match(repo.root, '', list(lfpats))
68
68
69 lfnames = []
69 lfnames = []
70 m = scmutil.match(repo[None], pats, opts)
70 m = scmutil.match(repo[None], pats, opts)
71 m.bad = lambda x, y: None
71 m.bad = lambda x, y: None
72 wctx = repo[None]
72 wctx = repo[None]
73 for f in repo.walk(m):
73 for f in repo.walk(m):
74 exact = m.exact(f)
74 exact = m.exact(f)
75 lfile = lfutil.standin(f) in wctx
75 lfile = lfutil.standin(f) in wctx
76 nfile = f in wctx
76 nfile = f in wctx
77 exists = lfile or nfile
77 exists = lfile or nfile
78
78
79 # Don't warn the user when they attempt to add a normal tracked file.
79 # Don't warn the user when they attempt to add a normal tracked file.
80 # The normal add code will do that for us.
80 # The normal add code will do that for us.
81 if exact and exists:
81 if exact and exists:
82 if lfile:
82 if lfile:
83 ui.warn(_('%s already a largefile\n') % f)
83 ui.warn(_('%s already a largefile\n') % f)
84 continue
84 continue
85
85
86 if (exact or not exists) and not lfutil.isstandin(f):
86 if (exact or not exists) and not lfutil.isstandin(f):
87 wfile = repo.wjoin(f)
87 wfile = repo.wjoin(f)
88
88
89 # In case the file was removed previously, but not committed
89 # In case the file was removed previously, but not committed
90 # (issue3507)
90 # (issue3507)
91 if not os.path.exists(wfile):
91 if not os.path.exists(wfile):
92 continue
92 continue
93
93
94 abovemin = (lfsize and
94 abovemin = (lfsize and
95 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
95 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
96 if large or abovemin or (lfmatcher and lfmatcher(f)):
96 if large or abovemin or (lfmatcher and lfmatcher(f)):
97 lfnames.append(f)
97 lfnames.append(f)
98 if ui.verbose or not exact:
98 if ui.verbose or not exact:
99 ui.status(_('adding %s as a largefile\n') % m.rel(f))
99 ui.status(_('adding %s as a largefile\n') % m.rel(f))
100
100
101 bad = []
101 bad = []
102 standins = []
102 standins = []
103
103
104 # Need to lock, otherwise there could be a race condition between
104 # Need to lock, otherwise there could be a race condition between
105 # when standins are created and added to the repo.
105 # when standins are created and added to the repo.
106 wlock = repo.wlock()
106 wlock = repo.wlock()
107 try:
107 try:
108 if not opts.get('dry_run'):
108 if not opts.get('dry_run'):
109 lfdirstate = lfutil.openlfdirstate(ui, repo)
109 lfdirstate = lfutil.openlfdirstate(ui, repo)
110 for f in lfnames:
110 for f in lfnames:
111 standinname = lfutil.standin(f)
111 standinname = lfutil.standin(f)
112 lfutil.writestandin(repo, standinname, hash='',
112 lfutil.writestandin(repo, standinname, hash='',
113 executable=lfutil.getexecutable(repo.wjoin(f)))
113 executable=lfutil.getexecutable(repo.wjoin(f)))
114 standins.append(standinname)
114 standins.append(standinname)
115 if lfdirstate[f] == 'r':
115 if lfdirstate[f] == 'r':
116 lfdirstate.normallookup(f)
116 lfdirstate.normallookup(f)
117 else:
117 else:
118 lfdirstate.add(f)
118 lfdirstate.add(f)
119 lfdirstate.write()
119 lfdirstate.write()
120 bad += [lfutil.splitstandin(f)
120 bad += [lfutil.splitstandin(f)
121 for f in repo[None].add(standins)
121 for f in repo[None].add(standins)
122 if f in m.files()]
122 if f in m.files()]
123 finally:
123 finally:
124 wlock.release()
124 wlock.release()
125 return bad
125 return bad
126
126
127 def removelargefiles(ui, repo, *pats, **opts):
127 def removelargefiles(ui, repo, *pats, **opts):
128 after = opts.get('after')
128 after = opts.get('after')
129 if not pats and not after:
129 if not pats and not after:
130 raise util.Abort(_('no files specified'))
130 raise util.Abort(_('no files specified'))
131 m = scmutil.match(repo[None], pats, opts)
131 m = scmutil.match(repo[None], pats, opts)
132 try:
132 try:
133 repo.lfstatus = True
133 repo.lfstatus = True
134 s = repo.status(match=m, clean=True)
134 s = repo.status(match=m, clean=True)
135 finally:
135 finally:
136 repo.lfstatus = False
136 repo.lfstatus = False
137 manifest = repo[None].manifest()
137 manifest = repo[None].manifest()
138 modified, added, deleted, clean = [[f for f in list
138 modified, added, deleted, clean = [[f for f in list
139 if lfutil.standin(f) in manifest]
139 if lfutil.standin(f) in manifest]
140 for list in [s[0], s[1], s[3], s[6]]]
140 for list in [s[0], s[1], s[3], s[6]]]
141
141
142 def warn(files, msg):
142 def warn(files, msg):
143 for f in files:
143 for f in files:
144 ui.warn(msg % m.rel(f))
144 ui.warn(msg % m.rel(f))
145 return int(len(files) > 0)
145 return int(len(files) > 0)
146
146
147 result = 0
147 result = 0
148
148
149 if after:
149 if after:
150 remove, forget = deleted, []
150 remove, forget = deleted, []
151 result = warn(modified + added + clean,
151 result = warn(modified + added + clean,
152 _('not removing %s: file still exists\n'))
152 _('not removing %s: file still exists\n'))
153 else:
153 else:
154 remove, forget = deleted + clean, []
154 remove, forget = deleted + clean, []
155 result = warn(modified, _('not removing %s: file is modified (use -f'
155 result = warn(modified, _('not removing %s: file is modified (use -f'
156 ' to force removal)\n'))
156 ' to force removal)\n'))
157 result = warn(added, _('not removing %s: file has been marked for add'
157 result = warn(added, _('not removing %s: file has been marked for add'
158 ' (use forget to undo)\n')) or result
158 ' (use forget to undo)\n')) or result
159
159
160 for f in sorted(remove + forget):
160 for f in sorted(remove + forget):
161 if ui.verbose or not m.exact(f):
161 if ui.verbose or not m.exact(f):
162 ui.status(_('removing %s\n') % m.rel(f))
162 ui.status(_('removing %s\n') % m.rel(f))
163
163
164 # Need to lock because standin files are deleted then removed from the
164 # Need to lock because standin files are deleted then removed from the
165 # repository and we could race in-between.
165 # repository and we could race in-between.
166 wlock = repo.wlock()
166 wlock = repo.wlock()
167 try:
167 try:
168 lfdirstate = lfutil.openlfdirstate(ui, repo)
168 lfdirstate = lfutil.openlfdirstate(ui, repo)
169 for f in remove:
169 for f in remove:
170 if not after:
170 if not after:
171 # If this is being called by addremove, notify the user that we
171 # If this is being called by addremove, notify the user that we
172 # are removing the file.
172 # are removing the file.
173 if getattr(repo, "_isaddremove", False):
173 if getattr(repo, "_isaddremove", False):
174 ui.status(_('removing %s\n') % f)
174 ui.status(_('removing %s\n') % f)
175 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
175 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
176 lfdirstate.remove(f)
176 lfdirstate.remove(f)
177 lfdirstate.write()
177 lfdirstate.write()
178 forget = [lfutil.standin(f) for f in forget]
178 forget = [lfutil.standin(f) for f in forget]
179 remove = [lfutil.standin(f) for f in remove]
179 remove = [lfutil.standin(f) for f in remove]
180 repo[None].forget(forget)
180 repo[None].forget(forget)
181 # If this is being called by addremove, let the original addremove
181 # If this is being called by addremove, let the original addremove
182 # function handle this.
182 # function handle this.
183 if not getattr(repo, "_isaddremove", False):
183 if not getattr(repo, "_isaddremove", False):
184 for f in remove:
184 for f in remove:
185 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
185 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
186 repo[None].forget(remove)
186 repo[None].forget(remove)
187 finally:
187 finally:
188 wlock.release()
188 wlock.release()
189
189
190 return result
190 return result
191
191
192 # For overriding mercurial.hgweb.webcommands so that largefiles will
192 # For overriding mercurial.hgweb.webcommands so that largefiles will
193 # appear at their right place in the manifests.
193 # appear at their right place in the manifests.
194 def decodepath(orig, path):
194 def decodepath(orig, path):
195 return lfutil.splitstandin(path) or path
195 return lfutil.splitstandin(path) or path
196
196
197 # -- Wrappers: modify existing commands --------------------------------
197 # -- Wrappers: modify existing commands --------------------------------
198
198
199 # Add works by going through the files that the user wanted to add and
199 # Add works by going through the files that the user wanted to add and
200 # checking if they should be added as largefiles. Then it makes a new
200 # checking if they should be added as largefiles. Then it makes a new
201 # matcher which matches only the normal files and runs the original
201 # matcher which matches only the normal files and runs the original
202 # version of add.
202 # version of add.
203 def overrideadd(orig, ui, repo, *pats, **opts):
203 def overrideadd(orig, ui, repo, *pats, **opts):
204 normal = opts.pop('normal')
204 normal = opts.pop('normal')
205 if normal:
205 if normal:
206 if opts.get('large'):
206 if opts.get('large'):
207 raise util.Abort(_('--normal cannot be used with --large'))
207 raise util.Abort(_('--normal cannot be used with --large'))
208 return orig(ui, repo, *pats, **opts)
208 return orig(ui, repo, *pats, **opts)
209 bad = addlargefiles(ui, repo, *pats, **opts)
209 bad = addlargefiles(ui, repo, *pats, **opts)
210 installnormalfilesmatchfn(repo[None].manifest())
210 installnormalfilesmatchfn(repo[None].manifest())
211 result = orig(ui, repo, *pats, **opts)
211 result = orig(ui, repo, *pats, **opts)
212 restorematchfn()
212 restorematchfn()
213
213
214 return (result == 1 or bad) and 1 or 0
214 return (result == 1 or bad) and 1 or 0
215
215
216 def overrideremove(orig, ui, repo, *pats, **opts):
216 def overrideremove(orig, ui, repo, *pats, **opts):
217 installnormalfilesmatchfn(repo[None].manifest())
217 installnormalfilesmatchfn(repo[None].manifest())
218 result = orig(ui, repo, *pats, **opts)
218 result = orig(ui, repo, *pats, **opts)
219 restorematchfn()
219 restorematchfn()
220 return removelargefiles(ui, repo, *pats, **opts) or result
220 return removelargefiles(ui, repo, *pats, **opts) or result
221
221
222 def overridestatusfn(orig, repo, rev2, **opts):
222 def overridestatusfn(orig, repo, rev2, **opts):
223 try:
223 try:
224 repo._repo.lfstatus = True
224 repo._repo.lfstatus = True
225 return orig(repo, rev2, **opts)
225 return orig(repo, rev2, **opts)
226 finally:
226 finally:
227 repo._repo.lfstatus = False
227 repo._repo.lfstatus = False
228
228
229 def overridestatus(orig, ui, repo, *pats, **opts):
229 def overridestatus(orig, ui, repo, *pats, **opts):
230 try:
230 try:
231 repo.lfstatus = True
231 repo.lfstatus = True
232 return orig(ui, repo, *pats, **opts)
232 return orig(ui, repo, *pats, **opts)
233 finally:
233 finally:
234 repo.lfstatus = False
234 repo.lfstatus = False
235
235
236 def overridedirty(orig, repo, ignoreupdate=False):
236 def overridedirty(orig, repo, ignoreupdate=False):
237 try:
237 try:
238 repo._repo.lfstatus = True
238 repo._repo.lfstatus = True
239 return orig(repo, ignoreupdate)
239 return orig(repo, ignoreupdate)
240 finally:
240 finally:
241 repo._repo.lfstatus = False
241 repo._repo.lfstatus = False
242
242
243 def overridelog(orig, ui, repo, *pats, **opts):
243 def overridelog(orig, ui, repo, *pats, **opts):
244 def overridematch(ctx, pats=[], opts={}, globbed=False,
244 def overridematch(ctx, pats=[], opts={}, globbed=False,
245 default='relpath'):
245 default='relpath'):
246 """Matcher that merges root directory with .hglf, suitable for log.
246 """Matcher that merges root directory with .hglf, suitable for log.
247 It is still possible to match .hglf directly.
247 It is still possible to match .hglf directly.
248 For any listed files run log on the standin too.
248 For any listed files run log on the standin too.
249 matchfn tries both the given filename and with .hglf stripped.
249 matchfn tries both the given filename and with .hglf stripped.
250 """
250 """
251 match = oldmatch(ctx, pats, opts, globbed, default)
251 match = oldmatch(ctx, pats, opts, globbed, default)
252 m = copy.copy(match)
252 m = copy.copy(match)
253 for i in range(0, len(m._files)):
253 for i in range(0, len(m._files)):
254 standin = lfutil.standin(m._files[i])
254 standin = lfutil.standin(m._files[i])
255 if standin in repo[ctx.node()]:
255 if standin in repo[ctx.node()]:
256 m._files[i] = standin
256 m._files[i] = standin
257 m._fmap = set(m._files)
257 m._fmap = set(m._files)
258 m._always = False
258 m._always = False
259 origmatchfn = m.matchfn
259 origmatchfn = m.matchfn
260 def lfmatchfn(f):
260 def lfmatchfn(f):
261 lf = lfutil.splitstandin(f)
261 lf = lfutil.splitstandin(f)
262 if lf is not None and origmatchfn(lf):
262 if lf is not None and origmatchfn(lf):
263 return True
263 return True
264 r = origmatchfn(f)
264 r = origmatchfn(f)
265 return r
265 return r
266 m.matchfn = lfmatchfn
266 m.matchfn = lfmatchfn
267 return m
267 return m
268 oldmatch = installmatchfn(overridematch)
268 oldmatch = installmatchfn(overridematch)
269 try:
269 try:
270 repo.lfstatus = True
270 repo.lfstatus = True
271 return orig(ui, repo, *pats, **opts)
271 return orig(ui, repo, *pats, **opts)
272 finally:
272 finally:
273 repo.lfstatus = False
273 repo.lfstatus = False
274 restorematchfn()
274 restorematchfn()
275
275
276 def overrideverify(orig, ui, repo, *pats, **opts):
276 def overrideverify(orig, ui, repo, *pats, **opts):
277 large = opts.pop('large', False)
277 large = opts.pop('large', False)
278 all = opts.pop('lfa', False)
278 all = opts.pop('lfa', False)
279 contents = opts.pop('lfc', False)
279 contents = opts.pop('lfc', False)
280
280
281 result = orig(ui, repo, *pats, **opts)
281 result = orig(ui, repo, *pats, **opts)
282 if large or all or contents:
282 if large or all or contents:
283 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
283 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
284 return result
284 return result
285
285
286 def overridedebugstate(orig, ui, repo, *pats, **opts):
286 def overridedebugstate(orig, ui, repo, *pats, **opts):
287 large = opts.pop('large', False)
287 large = opts.pop('large', False)
288 if large:
288 if large:
289 class fakerepo(object):
289 class fakerepo(object):
290 dirstate = lfutil.openlfdirstate(ui, repo)
290 dirstate = lfutil.openlfdirstate(ui, repo)
291 orig(ui, fakerepo, *pats, **opts)
291 orig(ui, fakerepo, *pats, **opts)
292 else:
292 else:
293 orig(ui, repo, *pats, **opts)
293 orig(ui, repo, *pats, **opts)
294
294
295 # Override needs to refresh standins so that update's normal merge
295 # Override needs to refresh standins so that update's normal merge
296 # will go through properly. Then the other update hook (overriding repo.update)
296 # will go through properly. Then the other update hook (overriding repo.update)
297 # will get the new files. Filemerge is also overridden so that the merge
297 # will get the new files. Filemerge is also overridden so that the merge
298 # will merge standins correctly.
298 # will merge standins correctly.
299 def overrideupdate(orig, ui, repo, *pats, **opts):
299 def overrideupdate(orig, ui, repo, *pats, **opts):
300 lfdirstate = lfutil.openlfdirstate(ui, repo)
301 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
302 False, False)
303 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
304
305 # Need to lock between the standins getting updated and their
300 # Need to lock between the standins getting updated and their
306 # largefiles getting updated
301 # largefiles getting updated
307 wlock = repo.wlock()
302 wlock = repo.wlock()
308 try:
303 try:
304 lfdirstate = lfutil.openlfdirstate(ui, repo)
305 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()),
306 [], False, False, False)
307 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
308
309 if opts['check']:
309 if opts['check']:
310 mod = len(modified) > 0
310 mod = len(modified) > 0
311 for lfile in unsure:
311 for lfile in unsure:
312 standin = lfutil.standin(lfile)
312 standin = lfutil.standin(lfile)
313 if repo['.'][standin].data().strip() != \
313 if repo['.'][standin].data().strip() != \
314 lfutil.hashfile(repo.wjoin(lfile)):
314 lfutil.hashfile(repo.wjoin(lfile)):
315 mod = True
315 mod = True
316 else:
316 else:
317 lfdirstate.normal(lfile)
317 lfdirstate.normal(lfile)
318 lfdirstate.write()
318 lfdirstate.write()
319 if mod:
319 if mod:
320 raise util.Abort(_('uncommitted changes'))
320 raise util.Abort(_('uncommitted changes'))
321 # XXX handle removed differently
321 # XXX handle removed differently
322 if not opts['clean']:
322 if not opts['clean']:
323 for lfile in unsure + modified + added:
323 for lfile in unsure + modified + added:
324 lfutil.updatestandin(repo, lfutil.standin(lfile))
324 lfutil.updatestandin(repo, lfutil.standin(lfile))
325 return orig(ui, repo, *pats, **opts)
325 finally:
326 finally:
326 wlock.release()
327 wlock.release()
327 return orig(ui, repo, *pats, **opts)
328
328
329 # Before starting the manifest merge, merge.updates will call
329 # Before starting the manifest merge, merge.updates will call
330 # _checkunknown to check if there are any files in the merged-in
330 # _checkunknown to check if there are any files in the merged-in
331 # changeset that collide with unknown files in the working copy.
331 # changeset that collide with unknown files in the working copy.
332 #
332 #
333 # The largefiles are seen as unknown, so this prevents us from merging
333 # The largefiles are seen as unknown, so this prevents us from merging
334 # in a file 'foo' if we already have a largefile with the same name.
334 # in a file 'foo' if we already have a largefile with the same name.
335 #
335 #
336 # The overridden function filters the unknown files by removing any
336 # The overridden function filters the unknown files by removing any
337 # largefiles. This makes the merge proceed and we can then handle this
337 # largefiles. This makes the merge proceed and we can then handle this
338 # case further in the overridden manifestmerge function below.
338 # case further in the overridden manifestmerge function below.
339 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
339 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
340 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
340 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
341 return False
341 return False
342 return origfn(repo, wctx, mctx, f)
342 return origfn(repo, wctx, mctx, f)
343
343
344 # The manifest merge handles conflicts on the manifest level. We want
344 # The manifest merge handles conflicts on the manifest level. We want
345 # to handle changes in largefile-ness of files at this level too.
345 # to handle changes in largefile-ness of files at this level too.
346 #
346 #
347 # The strategy is to run the original manifestmerge and then process
347 # The strategy is to run the original manifestmerge and then process
348 # the action list it outputs. There are two cases we need to deal with:
348 # the action list it outputs. There are two cases we need to deal with:
349 #
349 #
350 # 1. Normal file in p1, largefile in p2. Here the largefile is
350 # 1. Normal file in p1, largefile in p2. Here the largefile is
351 # detected via its standin file, which will enter the working copy
351 # detected via its standin file, which will enter the working copy
352 # with a "get" action. It is not "merge" since the standin is all
352 # with a "get" action. It is not "merge" since the standin is all
353 # Mercurial is concerned with at this level -- the link to the
353 # Mercurial is concerned with at this level -- the link to the
354 # existing normal file is not relevant here.
354 # existing normal file is not relevant here.
355 #
355 #
356 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
356 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
357 # since the largefile will be present in the working copy and
357 # since the largefile will be present in the working copy and
358 # different from the normal file in p2. Mercurial therefore
358 # different from the normal file in p2. Mercurial therefore
359 # triggers a merge action.
359 # triggers a merge action.
360 #
360 #
361 # In both cases, we prompt the user and emit new actions to either
361 # In both cases, we prompt the user and emit new actions to either
362 # remove the standin (if the normal file was kept) or to remove the
362 # remove the standin (if the normal file was kept) or to remove the
363 # normal file and get the standin (if the largefile was kept). The
363 # normal file and get the standin (if the largefile was kept). The
364 # default prompt answer is to use the largefile version since it was
364 # default prompt answer is to use the largefile version since it was
365 # presumably changed on purpose.
365 # presumably changed on purpose.
366 #
366 #
367 # Finally, the merge.applyupdates function will then take care of
367 # Finally, the merge.applyupdates function will then take care of
368 # writing the files into the working copy and lfcommands.updatelfiles
368 # writing the files into the working copy and lfcommands.updatelfiles
369 # will update the largefiles.
369 # will update the largefiles.
370 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
370 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
371 partial, acceptremote, followcopies):
371 partial, acceptremote, followcopies):
372 overwrite = force and not branchmerge
372 overwrite = force and not branchmerge
373 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
373 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
374 acceptremote, followcopies)
374 acceptremote, followcopies)
375
375
376 if overwrite:
376 if overwrite:
377 return actions
377 return actions
378
378
379 removes = set(a[0] for a in actions if a[1] == 'r')
379 removes = set(a[0] for a in actions if a[1] == 'r')
380 processed = []
380 processed = []
381
381
382 for action in actions:
382 for action in actions:
383 f, m, args, msg = action
383 f, m, args, msg = action
384
384
385 splitstandin = f and lfutil.splitstandin(f)
385 splitstandin = f and lfutil.splitstandin(f)
386 if (m == "g" and splitstandin is not None and
386 if (m == "g" and splitstandin is not None and
387 splitstandin in p1 and splitstandin not in removes):
387 splitstandin in p1 and splitstandin not in removes):
388 # Case 1: normal file in the working copy, largefile in
388 # Case 1: normal file in the working copy, largefile in
389 # the second parent
389 # the second parent
390 lfile = splitstandin
390 lfile = splitstandin
391 standin = f
391 standin = f
392 msg = _('remote turned local normal file %s into a largefile\n'
392 msg = _('remote turned local normal file %s into a largefile\n'
393 'use (l)argefile or keep (n)ormal file?'
393 'use (l)argefile or keep (n)ormal file?'
394 '$$ &Largefile $$ &Normal file') % lfile
394 '$$ &Largefile $$ &Normal file') % lfile
395 if repo.ui.promptchoice(msg, 0) == 0:
395 if repo.ui.promptchoice(msg, 0) == 0:
396 processed.append((lfile, "r", None, msg))
396 processed.append((lfile, "r", None, msg))
397 processed.append((standin, "g", (p2.flags(standin),), msg))
397 processed.append((standin, "g", (p2.flags(standin),), msg))
398 else:
398 else:
399 processed.append((standin, "r", None, msg))
399 processed.append((standin, "r", None, msg))
400 elif (m == "g" and
400 elif (m == "g" and
401 lfutil.standin(f) in p1 and lfutil.standin(f) not in removes):
401 lfutil.standin(f) in p1 and lfutil.standin(f) not in removes):
402 # Case 2: largefile in the working copy, normal file in
402 # Case 2: largefile in the working copy, normal file in
403 # the second parent
403 # the second parent
404 standin = lfutil.standin(f)
404 standin = lfutil.standin(f)
405 lfile = f
405 lfile = f
406 msg = _('remote turned local largefile %s into a normal file\n'
406 msg = _('remote turned local largefile %s into a normal file\n'
407 'keep (l)argefile or use (n)ormal file?'
407 'keep (l)argefile or use (n)ormal file?'
408 '$$ &Largefile $$ &Normal file') % lfile
408 '$$ &Largefile $$ &Normal file') % lfile
409 if repo.ui.promptchoice(msg, 0) == 0:
409 if repo.ui.promptchoice(msg, 0) == 0:
410 processed.append((lfile, "r", None, msg))
410 processed.append((lfile, "r", None, msg))
411 else:
411 else:
412 processed.append((standin, "r", None, msg))
412 processed.append((standin, "r", None, msg))
413 processed.append((lfile, "g", (p2.flags(lfile),), msg))
413 processed.append((lfile, "g", (p2.flags(lfile),), msg))
414 else:
414 else:
415 processed.append(action)
415 processed.append(action)
416
416
417 return processed
417 return processed
418
418
419 # Override filemerge to prompt the user about how they wish to merge
419 # Override filemerge to prompt the user about how they wish to merge
420 # largefiles. This will handle identical edits without prompting the user.
420 # largefiles. This will handle identical edits without prompting the user.
421 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
421 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca):
422 if not lfutil.isstandin(orig):
422 if not lfutil.isstandin(orig):
423 return origfn(repo, mynode, orig, fcd, fco, fca)
423 return origfn(repo, mynode, orig, fcd, fco, fca)
424
424
425 ahash = fca.data().strip().lower()
425 ahash = fca.data().strip().lower()
426 dhash = fcd.data().strip().lower()
426 dhash = fcd.data().strip().lower()
427 ohash = fco.data().strip().lower()
427 ohash = fco.data().strip().lower()
428 if (ohash != ahash and
428 if (ohash != ahash and
429 ohash != dhash and
429 ohash != dhash and
430 (dhash == ahash or
430 (dhash == ahash or
431 repo.ui.promptchoice(
431 repo.ui.promptchoice(
432 _('largefile %s has a merge conflict\nancestor was %s\n'
432 _('largefile %s has a merge conflict\nancestor was %s\n'
433 'keep (l)ocal %s or\ntake (o)ther %s?'
433 'keep (l)ocal %s or\ntake (o)ther %s?'
434 '$$ &Local $$ &Other') %
434 '$$ &Local $$ &Other') %
435 (lfutil.splitstandin(orig), ahash, dhash, ohash),
435 (lfutil.splitstandin(orig), ahash, dhash, ohash),
436 0) == 1)):
436 0) == 1)):
437 repo.wwrite(fcd.path(), fco.data(), fco.flags())
437 repo.wwrite(fcd.path(), fco.data(), fco.flags())
438 return 0
438 return 0
439
439
440 # Copy first changes the matchers to match standins instead of
440 # Copy first changes the matchers to match standins instead of
441 # largefiles. Then it overrides util.copyfile in that function it
441 # largefiles. Then it overrides util.copyfile in that function it
442 # checks if the destination largefile already exists. It also keeps a
442 # checks if the destination largefile already exists. It also keeps a
443 # list of copied files so that the largefiles can be copied and the
443 # list of copied files so that the largefiles can be copied and the
444 # dirstate updated.
444 # dirstate updated.
445 def overridecopy(orig, ui, repo, pats, opts, rename=False):
445 def overridecopy(orig, ui, repo, pats, opts, rename=False):
446 # doesn't remove largefile on rename
446 # doesn't remove largefile on rename
447 if len(pats) < 2:
447 if len(pats) < 2:
448 # this isn't legal, let the original function deal with it
448 # this isn't legal, let the original function deal with it
449 return orig(ui, repo, pats, opts, rename)
449 return orig(ui, repo, pats, opts, rename)
450
450
451 def makestandin(relpath):
451 def makestandin(relpath):
452 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
452 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
453 return os.path.join(repo.wjoin(lfutil.standin(path)))
453 return os.path.join(repo.wjoin(lfutil.standin(path)))
454
454
455 fullpats = scmutil.expandpats(pats)
455 fullpats = scmutil.expandpats(pats)
456 dest = fullpats[-1]
456 dest = fullpats[-1]
457
457
458 if os.path.isdir(dest):
458 if os.path.isdir(dest):
459 if not os.path.isdir(makestandin(dest)):
459 if not os.path.isdir(makestandin(dest)):
460 os.makedirs(makestandin(dest))
460 os.makedirs(makestandin(dest))
461 # This could copy both lfiles and normal files in one command,
461 # This could copy both lfiles and normal files in one command,
462 # but we don't want to do that. First replace their matcher to
462 # but we don't want to do that. First replace their matcher to
463 # only match normal files and run it, then replace it to just
463 # only match normal files and run it, then replace it to just
464 # match largefiles and run it again.
464 # match largefiles and run it again.
465 nonormalfiles = False
465 nonormalfiles = False
466 nolfiles = False
466 nolfiles = False
467 try:
467 try:
468 try:
468 try:
469 installnormalfilesmatchfn(repo[None].manifest())
469 installnormalfilesmatchfn(repo[None].manifest())
470 result = orig(ui, repo, pats, opts, rename)
470 result = orig(ui, repo, pats, opts, rename)
471 except util.Abort, e:
471 except util.Abort, e:
472 if str(e) != _('no files to copy'):
472 if str(e) != _('no files to copy'):
473 raise e
473 raise e
474 else:
474 else:
475 nonormalfiles = True
475 nonormalfiles = True
476 result = 0
476 result = 0
477 finally:
477 finally:
478 restorematchfn()
478 restorematchfn()
479
479
480 # The first rename can cause our current working directory to be removed.
480 # The first rename can cause our current working directory to be removed.
481 # In that case there is nothing left to copy/rename so just quit.
481 # In that case there is nothing left to copy/rename so just quit.
482 try:
482 try:
483 repo.getcwd()
483 repo.getcwd()
484 except OSError:
484 except OSError:
485 return result
485 return result
486
486
487 try:
487 try:
488 try:
488 try:
489 # When we call orig below it creates the standins but we don't add
489 # When we call orig below it creates the standins but we don't add
490 # them to the dir state until later so lock during that time.
490 # them to the dir state until later so lock during that time.
491 wlock = repo.wlock()
491 wlock = repo.wlock()
492
492
493 manifest = repo[None].manifest()
493 manifest = repo[None].manifest()
494 oldmatch = None # for the closure
494 oldmatch = None # for the closure
495 def overridematch(ctx, pats=[], opts={}, globbed=False,
495 def overridematch(ctx, pats=[], opts={}, globbed=False,
496 default='relpath'):
496 default='relpath'):
497 newpats = []
497 newpats = []
498 # The patterns were previously mangled to add the standin
498 # The patterns were previously mangled to add the standin
499 # directory; we need to remove that now
499 # directory; we need to remove that now
500 for pat in pats:
500 for pat in pats:
501 if match_.patkind(pat) is None and lfutil.shortname in pat:
501 if match_.patkind(pat) is None and lfutil.shortname in pat:
502 newpats.append(pat.replace(lfutil.shortname, ''))
502 newpats.append(pat.replace(lfutil.shortname, ''))
503 else:
503 else:
504 newpats.append(pat)
504 newpats.append(pat)
505 match = oldmatch(ctx, newpats, opts, globbed, default)
505 match = oldmatch(ctx, newpats, opts, globbed, default)
506 m = copy.copy(match)
506 m = copy.copy(match)
507 lfile = lambda f: lfutil.standin(f) in manifest
507 lfile = lambda f: lfutil.standin(f) in manifest
508 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
508 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
509 m._fmap = set(m._files)
509 m._fmap = set(m._files)
510 m._always = False
510 m._always = False
511 origmatchfn = m.matchfn
511 origmatchfn = m.matchfn
512 m.matchfn = lambda f: (lfutil.isstandin(f) and
512 m.matchfn = lambda f: (lfutil.isstandin(f) and
513 (f in manifest) and
513 (f in manifest) and
514 origmatchfn(lfutil.splitstandin(f)) or
514 origmatchfn(lfutil.splitstandin(f)) or
515 None)
515 None)
516 return m
516 return m
517 oldmatch = installmatchfn(overridematch)
517 oldmatch = installmatchfn(overridematch)
518 listpats = []
518 listpats = []
519 for pat in pats:
519 for pat in pats:
520 if match_.patkind(pat) is not None:
520 if match_.patkind(pat) is not None:
521 listpats.append(pat)
521 listpats.append(pat)
522 else:
522 else:
523 listpats.append(makestandin(pat))
523 listpats.append(makestandin(pat))
524
524
525 try:
525 try:
526 origcopyfile = util.copyfile
526 origcopyfile = util.copyfile
527 copiedfiles = []
527 copiedfiles = []
528 def overridecopyfile(src, dest):
528 def overridecopyfile(src, dest):
529 if (lfutil.shortname in src and
529 if (lfutil.shortname in src and
530 dest.startswith(repo.wjoin(lfutil.shortname))):
530 dest.startswith(repo.wjoin(lfutil.shortname))):
531 destlfile = dest.replace(lfutil.shortname, '')
531 destlfile = dest.replace(lfutil.shortname, '')
532 if not opts['force'] and os.path.exists(destlfile):
532 if not opts['force'] and os.path.exists(destlfile):
533 raise IOError('',
533 raise IOError('',
534 _('destination largefile already exists'))
534 _('destination largefile already exists'))
535 copiedfiles.append((src, dest))
535 copiedfiles.append((src, dest))
536 origcopyfile(src, dest)
536 origcopyfile(src, dest)
537
537
538 util.copyfile = overridecopyfile
538 util.copyfile = overridecopyfile
539 result += orig(ui, repo, listpats, opts, rename)
539 result += orig(ui, repo, listpats, opts, rename)
540 finally:
540 finally:
541 util.copyfile = origcopyfile
541 util.copyfile = origcopyfile
542
542
543 lfdirstate = lfutil.openlfdirstate(ui, repo)
543 lfdirstate = lfutil.openlfdirstate(ui, repo)
544 for (src, dest) in copiedfiles:
544 for (src, dest) in copiedfiles:
545 if (lfutil.shortname in src and
545 if (lfutil.shortname in src and
546 dest.startswith(repo.wjoin(lfutil.shortname))):
546 dest.startswith(repo.wjoin(lfutil.shortname))):
547 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
547 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
548 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
548 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
549 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
549 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
550 if not os.path.isdir(destlfiledir):
550 if not os.path.isdir(destlfiledir):
551 os.makedirs(destlfiledir)
551 os.makedirs(destlfiledir)
552 if rename:
552 if rename:
553 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
553 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
554 lfdirstate.remove(srclfile)
554 lfdirstate.remove(srclfile)
555 else:
555 else:
556 util.copyfile(repo.wjoin(srclfile),
556 util.copyfile(repo.wjoin(srclfile),
557 repo.wjoin(destlfile))
557 repo.wjoin(destlfile))
558
558
559 lfdirstate.add(destlfile)
559 lfdirstate.add(destlfile)
560 lfdirstate.write()
560 lfdirstate.write()
561 except util.Abort, e:
561 except util.Abort, e:
562 if str(e) != _('no files to copy'):
562 if str(e) != _('no files to copy'):
563 raise e
563 raise e
564 else:
564 else:
565 nolfiles = True
565 nolfiles = True
566 finally:
566 finally:
567 restorematchfn()
567 restorematchfn()
568 wlock.release()
568 wlock.release()
569
569
570 if nolfiles and nonormalfiles:
570 if nolfiles and nonormalfiles:
571 raise util.Abort(_('no files to copy'))
571 raise util.Abort(_('no files to copy'))
572
572
573 return result
573 return result
574
574
575 # When the user calls revert, we have to be careful to not revert any
575 # When the user calls revert, we have to be careful to not revert any
576 # changes to other largefiles accidentally. This means we have to keep
576 # changes to other largefiles accidentally. This means we have to keep
577 # track of the largefiles that are being reverted so we only pull down
577 # track of the largefiles that are being reverted so we only pull down
578 # the necessary largefiles.
578 # the necessary largefiles.
579 #
579 #
580 # Standins are only updated (to match the hash of largefiles) before
580 # Standins are only updated (to match the hash of largefiles) before
581 # commits. Update the standins then run the original revert, changing
581 # commits. Update the standins then run the original revert, changing
582 # the matcher to hit standins instead of largefiles. Based on the
582 # the matcher to hit standins instead of largefiles. Based on the
583 # resulting standins update the largefiles. Then return the standins
583 # resulting standins update the largefiles. Then return the standins
584 # to their proper state
584 # to their proper state
585 def overriderevert(orig, ui, repo, *pats, **opts):
585 def overriderevert(orig, ui, repo, *pats, **opts):
586 # Because we put the standins in a bad state (by updating them)
586 # Because we put the standins in a bad state (by updating them)
587 # and then return them to a correct state we need to lock to
587 # and then return them to a correct state we need to lock to
588 # prevent others from changing them in their incorrect state.
588 # prevent others from changing them in their incorrect state.
589 wlock = repo.wlock()
589 wlock = repo.wlock()
590 try:
590 try:
591 lfdirstate = lfutil.openlfdirstate(ui, repo)
591 lfdirstate = lfutil.openlfdirstate(ui, repo)
592 (modified, added, removed, missing, unknown, ignored, clean) = \
592 (modified, added, removed, missing, unknown, ignored, clean) = \
593 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
593 lfutil.lfdirstatestatus(lfdirstate, repo, repo['.'].rev())
594 lfdirstate.write()
594 lfdirstate.write()
595 for lfile in modified:
595 for lfile in modified:
596 lfutil.updatestandin(repo, lfutil.standin(lfile))
596 lfutil.updatestandin(repo, lfutil.standin(lfile))
597 for lfile in missing:
597 for lfile in missing:
598 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
598 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
599 os.unlink(repo.wjoin(lfutil.standin(lfile)))
599 os.unlink(repo.wjoin(lfutil.standin(lfile)))
600
600
601 try:
601 try:
602 ctx = scmutil.revsingle(repo, opts.get('rev'))
602 ctx = scmutil.revsingle(repo, opts.get('rev'))
603 oldmatch = None # for the closure
603 oldmatch = None # for the closure
604 def overridematch(ctx, pats=[], opts={}, globbed=False,
604 def overridematch(ctx, pats=[], opts={}, globbed=False,
605 default='relpath'):
605 default='relpath'):
606 match = oldmatch(ctx, pats, opts, globbed, default)
606 match = oldmatch(ctx, pats, opts, globbed, default)
607 m = copy.copy(match)
607 m = copy.copy(match)
608 def tostandin(f):
608 def tostandin(f):
609 if lfutil.standin(f) in ctx:
609 if lfutil.standin(f) in ctx:
610 return lfutil.standin(f)
610 return lfutil.standin(f)
611 elif lfutil.standin(f) in repo[None]:
611 elif lfutil.standin(f) in repo[None]:
612 return None
612 return None
613 return f
613 return f
614 m._files = [tostandin(f) for f in m._files]
614 m._files = [tostandin(f) for f in m._files]
615 m._files = [f for f in m._files if f is not None]
615 m._files = [f for f in m._files if f is not None]
616 m._fmap = set(m._files)
616 m._fmap = set(m._files)
617 m._always = False
617 m._always = False
618 origmatchfn = m.matchfn
618 origmatchfn = m.matchfn
619 def matchfn(f):
619 def matchfn(f):
620 if lfutil.isstandin(f):
620 if lfutil.isstandin(f):
621 # We need to keep track of what largefiles are being
621 # We need to keep track of what largefiles are being
622 # matched so we know which ones to update later --
622 # matched so we know which ones to update later --
623 # otherwise we accidentally revert changes to other
623 # otherwise we accidentally revert changes to other
624 # largefiles. This is repo-specific, so duckpunch the
624 # largefiles. This is repo-specific, so duckpunch the
625 # repo object to keep the list of largefiles for us
625 # repo object to keep the list of largefiles for us
626 # later.
626 # later.
627 if origmatchfn(lfutil.splitstandin(f)) and \
627 if origmatchfn(lfutil.splitstandin(f)) and \
628 (f in repo[None] or f in ctx):
628 (f in repo[None] or f in ctx):
629 lfileslist = getattr(repo, '_lfilestoupdate', [])
629 lfileslist = getattr(repo, '_lfilestoupdate', [])
630 lfileslist.append(lfutil.splitstandin(f))
630 lfileslist.append(lfutil.splitstandin(f))
631 repo._lfilestoupdate = lfileslist
631 repo._lfilestoupdate = lfileslist
632 return True
632 return True
633 else:
633 else:
634 return False
634 return False
635 return origmatchfn(f)
635 return origmatchfn(f)
636 m.matchfn = matchfn
636 m.matchfn = matchfn
637 return m
637 return m
638 oldmatch = installmatchfn(overridematch)
638 oldmatch = installmatchfn(overridematch)
639 scmutil.match
639 scmutil.match
640 matches = overridematch(repo[None], pats, opts)
640 matches = overridematch(repo[None], pats, opts)
641 orig(ui, repo, *pats, **opts)
641 orig(ui, repo, *pats, **opts)
642 finally:
642 finally:
643 restorematchfn()
643 restorematchfn()
644 lfileslist = getattr(repo, '_lfilestoupdate', [])
644 lfileslist = getattr(repo, '_lfilestoupdate', [])
645 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
645 lfcommands.updatelfiles(ui, repo, filelist=lfileslist,
646 printmessage=False)
646 printmessage=False)
647
647
648 # empty out the largefiles list so we start fresh next time
648 # empty out the largefiles list so we start fresh next time
649 repo._lfilestoupdate = []
649 repo._lfilestoupdate = []
650 for lfile in modified:
650 for lfile in modified:
651 if lfile in lfileslist:
651 if lfile in lfileslist:
652 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
652 if os.path.exists(repo.wjoin(lfutil.standin(lfile))) and lfile\
653 in repo['.']:
653 in repo['.']:
654 lfutil.writestandin(repo, lfutil.standin(lfile),
654 lfutil.writestandin(repo, lfutil.standin(lfile),
655 repo['.'][lfile].data().strip(),
655 repo['.'][lfile].data().strip(),
656 'x' in repo['.'][lfile].flags())
656 'x' in repo['.'][lfile].flags())
657 lfdirstate = lfutil.openlfdirstate(ui, repo)
657 lfdirstate = lfutil.openlfdirstate(ui, repo)
658 for lfile in added:
658 for lfile in added:
659 standin = lfutil.standin(lfile)
659 standin = lfutil.standin(lfile)
660 if standin not in ctx and (standin in matches or opts.get('all')):
660 if standin not in ctx and (standin in matches or opts.get('all')):
661 if lfile in lfdirstate:
661 if lfile in lfdirstate:
662 lfdirstate.drop(lfile)
662 lfdirstate.drop(lfile)
663 util.unlinkpath(repo.wjoin(standin))
663 util.unlinkpath(repo.wjoin(standin))
664 lfdirstate.write()
664 lfdirstate.write()
665 finally:
665 finally:
666 wlock.release()
666 wlock.release()
667
667
668 def hgupdaterepo(orig, repo, node, overwrite):
668 def hgupdaterepo(orig, repo, node, overwrite):
669 if not overwrite:
669 if not overwrite:
670 # Only call updatelfiles on the standins that have changed to save time
670 # Only call updatelfiles on the standins that have changed to save time
671 oldstandins = lfutil.getstandinsstate(repo)
671 oldstandins = lfutil.getstandinsstate(repo)
672
672
673 result = orig(repo, node, overwrite)
673 result = orig(repo, node, overwrite)
674
674
675 filelist = None
675 filelist = None
676 if not overwrite:
676 if not overwrite:
677 newstandins = lfutil.getstandinsstate(repo)
677 newstandins = lfutil.getstandinsstate(repo)
678 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
678 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
679 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
679 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist)
680 return result
680 return result
681
681
682 def hgmerge(orig, repo, node, force=None, remind=True):
682 def hgmerge(orig, repo, node, force=None, remind=True):
683 result = orig(repo, node, force, remind)
683 result = orig(repo, node, force, remind)
684 lfcommands.updatelfiles(repo.ui, repo)
684 lfcommands.updatelfiles(repo.ui, repo)
685 return result
685 return result
686
686
687 # When we rebase a repository with remotely changed largefiles, we need to
687 # When we rebase a repository with remotely changed largefiles, we need to
688 # take some extra care so that the largefiles are correctly updated in the
688 # take some extra care so that the largefiles are correctly updated in the
689 # working copy
689 # working copy
690 def overridepull(orig, ui, repo, source=None, **opts):
690 def overridepull(orig, ui, repo, source=None, **opts):
691 revsprepull = len(repo)
691 revsprepull = len(repo)
692 if not source:
692 if not source:
693 source = 'default'
693 source = 'default'
694 repo.lfpullsource = source
694 repo.lfpullsource = source
695 if opts.get('rebase', False):
695 if opts.get('rebase', False):
696 repo._isrebasing = True
696 repo._isrebasing = True
697 try:
697 try:
698 if opts.get('update'):
698 if opts.get('update'):
699 del opts['update']
699 del opts['update']
700 ui.debug('--update and --rebase are not compatible, ignoring '
700 ui.debug('--update and --rebase are not compatible, ignoring '
701 'the update flag\n')
701 'the update flag\n')
702 del opts['rebase']
702 del opts['rebase']
703 origpostincoming = commands.postincoming
703 origpostincoming = commands.postincoming
704 def _dummy(*args, **kwargs):
704 def _dummy(*args, **kwargs):
705 pass
705 pass
706 commands.postincoming = _dummy
706 commands.postincoming = _dummy
707 try:
707 try:
708 result = commands.pull(ui, repo, source, **opts)
708 result = commands.pull(ui, repo, source, **opts)
709 finally:
709 finally:
710 commands.postincoming = origpostincoming
710 commands.postincoming = origpostincoming
711 revspostpull = len(repo)
711 revspostpull = len(repo)
712 if revspostpull > revsprepull:
712 if revspostpull > revsprepull:
713 result = result or rebase.rebase(ui, repo)
713 result = result or rebase.rebase(ui, repo)
714 finally:
714 finally:
715 repo._isrebasing = False
715 repo._isrebasing = False
716 else:
716 else:
717 result = orig(ui, repo, source, **opts)
717 result = orig(ui, repo, source, **opts)
718 revspostpull = len(repo)
718 revspostpull = len(repo)
719 lfrevs = opts.get('lfrev', [])
719 lfrevs = opts.get('lfrev', [])
720 if opts.get('all_largefiles'):
720 if opts.get('all_largefiles'):
721 lfrevs.append('pulled()')
721 lfrevs.append('pulled()')
722 if lfrevs and revspostpull > revsprepull:
722 if lfrevs and revspostpull > revsprepull:
723 numcached = 0
723 numcached = 0
724 repo.firstpulled = revsprepull # for pulled() revset expression
724 repo.firstpulled = revsprepull # for pulled() revset expression
725 try:
725 try:
726 for rev in scmutil.revrange(repo, lfrevs):
726 for rev in scmutil.revrange(repo, lfrevs):
727 ui.note(_('pulling largefiles for revision %s\n') % rev)
727 ui.note(_('pulling largefiles for revision %s\n') % rev)
728 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
728 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
729 numcached += len(cached)
729 numcached += len(cached)
730 finally:
730 finally:
731 del repo.firstpulled
731 del repo.firstpulled
732 ui.status(_("%d largefiles cached\n") % numcached)
732 ui.status(_("%d largefiles cached\n") % numcached)
733 return result
733 return result
734
734
735 def pulledrevsetsymbol(repo, subset, x):
735 def pulledrevsetsymbol(repo, subset, x):
736 """``pulled()``
736 """``pulled()``
737 Changesets that just has been pulled.
737 Changesets that just has been pulled.
738
738
739 Only available with largefiles from pull --lfrev expressions.
739 Only available with largefiles from pull --lfrev expressions.
740
740
741 .. container:: verbose
741 .. container:: verbose
742
742
743 Some examples:
743 Some examples:
744
744
745 - pull largefiles for all new changesets::
745 - pull largefiles for all new changesets::
746
746
747 hg pull -lfrev "pulled()"
747 hg pull -lfrev "pulled()"
748
748
749 - pull largefiles for all new branch heads::
749 - pull largefiles for all new branch heads::
750
750
751 hg pull -lfrev "head(pulled()) and not closed()"
751 hg pull -lfrev "head(pulled()) and not closed()"
752
752
753 """
753 """
754
754
755 try:
755 try:
756 firstpulled = repo.firstpulled
756 firstpulled = repo.firstpulled
757 except AttributeError:
757 except AttributeError:
758 raise util.Abort(_("pulled() only available in --lfrev"))
758 raise util.Abort(_("pulled() only available in --lfrev"))
759 return revset.baseset([r for r in subset if r >= firstpulled])
759 return revset.baseset([r for r in subset if r >= firstpulled])
760
760
761 def overrideclone(orig, ui, source, dest=None, **opts):
761 def overrideclone(orig, ui, source, dest=None, **opts):
762 d = dest
762 d = dest
763 if d is None:
763 if d is None:
764 d = hg.defaultdest(source)
764 d = hg.defaultdest(source)
765 if opts.get('all_largefiles') and not hg.islocal(d):
765 if opts.get('all_largefiles') and not hg.islocal(d):
766 raise util.Abort(_(
766 raise util.Abort(_(
767 '--all-largefiles is incompatible with non-local destination %s' %
767 '--all-largefiles is incompatible with non-local destination %s' %
768 d))
768 d))
769
769
770 return orig(ui, source, dest, **opts)
770 return orig(ui, source, dest, **opts)
771
771
772 def hgclone(orig, ui, opts, *args, **kwargs):
772 def hgclone(orig, ui, opts, *args, **kwargs):
773 result = orig(ui, opts, *args, **kwargs)
773 result = orig(ui, opts, *args, **kwargs)
774
774
775 if result is not None:
775 if result is not None:
776 sourcerepo, destrepo = result
776 sourcerepo, destrepo = result
777 repo = destrepo.local()
777 repo = destrepo.local()
778
778
779 # Caching is implicitly limited to 'rev' option, since the dest repo was
779 # Caching is implicitly limited to 'rev' option, since the dest repo was
780 # truncated at that point. The user may expect a download count with
780 # truncated at that point. The user may expect a download count with
781 # this option, so attempt whether or not this is a largefile repo.
781 # this option, so attempt whether or not this is a largefile repo.
782 if opts.get('all_largefiles'):
782 if opts.get('all_largefiles'):
783 success, missing = lfcommands.downloadlfiles(ui, repo, None)
783 success, missing = lfcommands.downloadlfiles(ui, repo, None)
784
784
785 if missing != 0:
785 if missing != 0:
786 return None
786 return None
787
787
788 return result
788 return result
789
789
790 def overriderebase(orig, ui, repo, **opts):
790 def overriderebase(orig, ui, repo, **opts):
791 repo._isrebasing = True
791 repo._isrebasing = True
792 try:
792 try:
793 return orig(ui, repo, **opts)
793 return orig(ui, repo, **opts)
794 finally:
794 finally:
795 repo._isrebasing = False
795 repo._isrebasing = False
796
796
797 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
797 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
798 prefix=None, mtime=None, subrepos=None):
798 prefix=None, mtime=None, subrepos=None):
799 # No need to lock because we are only reading history and
799 # No need to lock because we are only reading history and
800 # largefile caches, neither of which are modified.
800 # largefile caches, neither of which are modified.
801 lfcommands.cachelfiles(repo.ui, repo, node)
801 lfcommands.cachelfiles(repo.ui, repo, node)
802
802
803 if kind not in archival.archivers:
803 if kind not in archival.archivers:
804 raise util.Abort(_("unknown archive type '%s'") % kind)
804 raise util.Abort(_("unknown archive type '%s'") % kind)
805
805
806 ctx = repo[node]
806 ctx = repo[node]
807
807
808 if kind == 'files':
808 if kind == 'files':
809 if prefix:
809 if prefix:
810 raise util.Abort(
810 raise util.Abort(
811 _('cannot give prefix when archiving to files'))
811 _('cannot give prefix when archiving to files'))
812 else:
812 else:
813 prefix = archival.tidyprefix(dest, kind, prefix)
813 prefix = archival.tidyprefix(dest, kind, prefix)
814
814
815 def write(name, mode, islink, getdata):
815 def write(name, mode, islink, getdata):
816 if matchfn and not matchfn(name):
816 if matchfn and not matchfn(name):
817 return
817 return
818 data = getdata()
818 data = getdata()
819 if decode:
819 if decode:
820 data = repo.wwritedata(name, data)
820 data = repo.wwritedata(name, data)
821 archiver.addfile(prefix + name, mode, islink, data)
821 archiver.addfile(prefix + name, mode, islink, data)
822
822
823 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
823 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
824
824
825 if repo.ui.configbool("ui", "archivemeta", True):
825 if repo.ui.configbool("ui", "archivemeta", True):
826 def metadata():
826 def metadata():
827 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
827 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
828 hex(repo.changelog.node(0)), hex(node), ctx.branch())
828 hex(repo.changelog.node(0)), hex(node), ctx.branch())
829
829
830 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
830 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
831 if repo.tagtype(t) == 'global')
831 if repo.tagtype(t) == 'global')
832 if not tags:
832 if not tags:
833 repo.ui.pushbuffer()
833 repo.ui.pushbuffer()
834 opts = {'template': '{latesttag}\n{latesttagdistance}',
834 opts = {'template': '{latesttag}\n{latesttagdistance}',
835 'style': '', 'patch': None, 'git': None}
835 'style': '', 'patch': None, 'git': None}
836 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
836 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
837 ltags, dist = repo.ui.popbuffer().split('\n')
837 ltags, dist = repo.ui.popbuffer().split('\n')
838 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
838 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
839 tags += 'latesttagdistance: %s\n' % dist
839 tags += 'latesttagdistance: %s\n' % dist
840
840
841 return base + tags
841 return base + tags
842
842
843 write('.hg_archival.txt', 0644, False, metadata)
843 write('.hg_archival.txt', 0644, False, metadata)
844
844
845 for f in ctx:
845 for f in ctx:
846 ff = ctx.flags(f)
846 ff = ctx.flags(f)
847 getdata = ctx[f].data
847 getdata = ctx[f].data
848 if lfutil.isstandin(f):
848 if lfutil.isstandin(f):
849 path = lfutil.findfile(repo, getdata().strip())
849 path = lfutil.findfile(repo, getdata().strip())
850 if path is None:
850 if path is None:
851 raise util.Abort(
851 raise util.Abort(
852 _('largefile %s not found in repo store or system cache')
852 _('largefile %s not found in repo store or system cache')
853 % lfutil.splitstandin(f))
853 % lfutil.splitstandin(f))
854 f = lfutil.splitstandin(f)
854 f = lfutil.splitstandin(f)
855
855
856 def getdatafn():
856 def getdatafn():
857 fd = None
857 fd = None
858 try:
858 try:
859 fd = open(path, 'rb')
859 fd = open(path, 'rb')
860 return fd.read()
860 return fd.read()
861 finally:
861 finally:
862 if fd:
862 if fd:
863 fd.close()
863 fd.close()
864
864
865 getdata = getdatafn
865 getdata = getdatafn
866 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
866 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
867
867
868 if subrepos:
868 if subrepos:
869 for subpath in sorted(ctx.substate):
869 for subpath in sorted(ctx.substate):
870 sub = ctx.sub(subpath)
870 sub = ctx.sub(subpath)
871 submatch = match_.narrowmatcher(subpath, matchfn)
871 submatch = match_.narrowmatcher(subpath, matchfn)
872 sub.archive(repo.ui, archiver, prefix, submatch)
872 sub.archive(repo.ui, archiver, prefix, submatch)
873
873
874 archiver.done()
874 archiver.done()
875
875
876 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
876 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
877 repo._get(repo._state + ('hg',))
877 repo._get(repo._state + ('hg',))
878 rev = repo._state[1]
878 rev = repo._state[1]
879 ctx = repo._repo[rev]
879 ctx = repo._repo[rev]
880
880
881 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
881 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
882
882
883 def write(name, mode, islink, getdata):
883 def write(name, mode, islink, getdata):
884 # At this point, the standin has been replaced with the largefile name,
884 # At this point, the standin has been replaced with the largefile name,
885 # so the normal matcher works here without the lfutil variants.
885 # so the normal matcher works here without the lfutil variants.
886 if match and not match(f):
886 if match and not match(f):
887 return
887 return
888 data = getdata()
888 data = getdata()
889
889
890 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
890 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
891
891
892 for f in ctx:
892 for f in ctx:
893 ff = ctx.flags(f)
893 ff = ctx.flags(f)
894 getdata = ctx[f].data
894 getdata = ctx[f].data
895 if lfutil.isstandin(f):
895 if lfutil.isstandin(f):
896 path = lfutil.findfile(repo._repo, getdata().strip())
896 path = lfutil.findfile(repo._repo, getdata().strip())
897 if path is None:
897 if path is None:
898 raise util.Abort(
898 raise util.Abort(
899 _('largefile %s not found in repo store or system cache')
899 _('largefile %s not found in repo store or system cache')
900 % lfutil.splitstandin(f))
900 % lfutil.splitstandin(f))
901 f = lfutil.splitstandin(f)
901 f = lfutil.splitstandin(f)
902
902
903 def getdatafn():
903 def getdatafn():
904 fd = None
904 fd = None
905 try:
905 try:
906 fd = open(os.path.join(prefix, path), 'rb')
906 fd = open(os.path.join(prefix, path), 'rb')
907 return fd.read()
907 return fd.read()
908 finally:
908 finally:
909 if fd:
909 if fd:
910 fd.close()
910 fd.close()
911
911
912 getdata = getdatafn
912 getdata = getdatafn
913
913
914 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
914 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
915
915
916 for subpath in sorted(ctx.substate):
916 for subpath in sorted(ctx.substate):
917 sub = ctx.sub(subpath)
917 sub = ctx.sub(subpath)
918 submatch = match_.narrowmatcher(subpath, match)
918 submatch = match_.narrowmatcher(subpath, match)
919 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
919 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
920 submatch)
920 submatch)
921
921
922 # If a largefile is modified, the change is not reflected in its
922 # If a largefile is modified, the change is not reflected in its
923 # standin until a commit. cmdutil.bailifchanged() raises an exception
923 # standin until a commit. cmdutil.bailifchanged() raises an exception
924 # if the repo has uncommitted changes. Wrap it to also check if
924 # if the repo has uncommitted changes. Wrap it to also check if
925 # largefiles were changed. This is used by bisect and backout.
925 # largefiles were changed. This is used by bisect and backout.
926 def overridebailifchanged(orig, repo):
926 def overridebailifchanged(orig, repo):
927 orig(repo)
927 orig(repo)
928 repo.lfstatus = True
928 repo.lfstatus = True
929 modified, added, removed, deleted = repo.status()[:4]
929 modified, added, removed, deleted = repo.status()[:4]
930 repo.lfstatus = False
930 repo.lfstatus = False
931 if modified or added or removed or deleted:
931 if modified or added or removed or deleted:
932 raise util.Abort(_('uncommitted changes'))
932 raise util.Abort(_('uncommitted changes'))
933
933
934 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
934 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
935 def overridefetch(orig, ui, repo, *pats, **opts):
935 def overridefetch(orig, ui, repo, *pats, **opts):
936 repo.lfstatus = True
936 repo.lfstatus = True
937 modified, added, removed, deleted = repo.status()[:4]
937 modified, added, removed, deleted = repo.status()[:4]
938 repo.lfstatus = False
938 repo.lfstatus = False
939 if modified or added or removed or deleted:
939 if modified or added or removed or deleted:
940 raise util.Abort(_('uncommitted changes'))
940 raise util.Abort(_('uncommitted changes'))
941 return orig(ui, repo, *pats, **opts)
941 return orig(ui, repo, *pats, **opts)
942
942
943 def overrideforget(orig, ui, repo, *pats, **opts):
943 def overrideforget(orig, ui, repo, *pats, **opts):
944 installnormalfilesmatchfn(repo[None].manifest())
944 installnormalfilesmatchfn(repo[None].manifest())
945 result = orig(ui, repo, *pats, **opts)
945 result = orig(ui, repo, *pats, **opts)
946 restorematchfn()
946 restorematchfn()
947 m = scmutil.match(repo[None], pats, opts)
947 m = scmutil.match(repo[None], pats, opts)
948
948
949 try:
949 try:
950 repo.lfstatus = True
950 repo.lfstatus = True
951 s = repo.status(match=m, clean=True)
951 s = repo.status(match=m, clean=True)
952 finally:
952 finally:
953 repo.lfstatus = False
953 repo.lfstatus = False
954 forget = sorted(s[0] + s[1] + s[3] + s[6])
954 forget = sorted(s[0] + s[1] + s[3] + s[6])
955 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
955 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
956
956
957 for f in forget:
957 for f in forget:
958 if lfutil.standin(f) not in repo.dirstate and not \
958 if lfutil.standin(f) not in repo.dirstate and not \
959 os.path.isdir(m.rel(lfutil.standin(f))):
959 os.path.isdir(m.rel(lfutil.standin(f))):
960 ui.warn(_('not removing %s: file is already untracked\n')
960 ui.warn(_('not removing %s: file is already untracked\n')
961 % m.rel(f))
961 % m.rel(f))
962 result = 1
962 result = 1
963
963
964 for f in forget:
964 for f in forget:
965 if ui.verbose or not m.exact(f):
965 if ui.verbose or not m.exact(f):
966 ui.status(_('removing %s\n') % m.rel(f))
966 ui.status(_('removing %s\n') % m.rel(f))
967
967
968 # Need to lock because standin files are deleted then removed from the
968 # Need to lock because standin files are deleted then removed from the
969 # repository and we could race in-between.
969 # repository and we could race in-between.
970 wlock = repo.wlock()
970 wlock = repo.wlock()
971 try:
971 try:
972 lfdirstate = lfutil.openlfdirstate(ui, repo)
972 lfdirstate = lfutil.openlfdirstate(ui, repo)
973 for f in forget:
973 for f in forget:
974 if lfdirstate[f] == 'a':
974 if lfdirstate[f] == 'a':
975 lfdirstate.drop(f)
975 lfdirstate.drop(f)
976 else:
976 else:
977 lfdirstate.remove(f)
977 lfdirstate.remove(f)
978 lfdirstate.write()
978 lfdirstate.write()
979 standins = [lfutil.standin(f) for f in forget]
979 standins = [lfutil.standin(f) for f in forget]
980 for f in standins:
980 for f in standins:
981 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
981 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
982 repo[None].forget(standins)
982 repo[None].forget(standins)
983 finally:
983 finally:
984 wlock.release()
984 wlock.release()
985
985
986 return result
986 return result
987
987
988 def outgoinghook(ui, repo, other, opts, missing):
988 def outgoinghook(ui, repo, other, opts, missing):
989 if opts.pop('large', None):
989 if opts.pop('large', None):
990 toupload = set()
990 toupload = set()
991 lfutil.getlfilestoupload(repo, missing,
991 lfutil.getlfilestoupload(repo, missing,
992 lambda fn, lfhash: toupload.add(fn))
992 lambda fn, lfhash: toupload.add(fn))
993 if not toupload:
993 if not toupload:
994 ui.status(_('largefiles: no files to upload\n'))
994 ui.status(_('largefiles: no files to upload\n'))
995 else:
995 else:
996 ui.status(_('largefiles to upload:\n'))
996 ui.status(_('largefiles to upload:\n'))
997 for file in sorted(toupload):
997 for file in sorted(toupload):
998 ui.status(lfutil.splitstandin(file) + '\n')
998 ui.status(lfutil.splitstandin(file) + '\n')
999 ui.status('\n')
999 ui.status('\n')
1000
1000
1001 def summaryremotehook(ui, repo, opts, changes):
1001 def summaryremotehook(ui, repo, opts, changes):
1002 largeopt = opts.get('large', False)
1002 largeopt = opts.get('large', False)
1003 if changes is None:
1003 if changes is None:
1004 if largeopt:
1004 if largeopt:
1005 return (False, True) # only outgoing check is needed
1005 return (False, True) # only outgoing check is needed
1006 else:
1006 else:
1007 return (False, False)
1007 return (False, False)
1008 elif largeopt:
1008 elif largeopt:
1009 url, branch, peer, outgoing = changes[1]
1009 url, branch, peer, outgoing = changes[1]
1010 if peer is None:
1010 if peer is None:
1011 # i18n: column positioning for "hg summary"
1011 # i18n: column positioning for "hg summary"
1012 ui.status(_('largefiles: (no remote repo)\n'))
1012 ui.status(_('largefiles: (no remote repo)\n'))
1013 return
1013 return
1014
1014
1015 toupload = set()
1015 toupload = set()
1016 lfutil.getlfilestoupload(repo, outgoing.missing,
1016 lfutil.getlfilestoupload(repo, outgoing.missing,
1017 lambda fn, lfhash: toupload.add(fn))
1017 lambda fn, lfhash: toupload.add(fn))
1018 if not toupload:
1018 if not toupload:
1019 # i18n: column positioning for "hg summary"
1019 # i18n: column positioning for "hg summary"
1020 ui.status(_('largefiles: (no files to upload)\n'))
1020 ui.status(_('largefiles: (no files to upload)\n'))
1021 else:
1021 else:
1022 # i18n: column positioning for "hg summary"
1022 # i18n: column positioning for "hg summary"
1023 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1023 ui.status(_('largefiles: %d to upload\n') % len(toupload))
1024
1024
1025 def overridesummary(orig, ui, repo, *pats, **opts):
1025 def overridesummary(orig, ui, repo, *pats, **opts):
1026 try:
1026 try:
1027 repo.lfstatus = True
1027 repo.lfstatus = True
1028 orig(ui, repo, *pats, **opts)
1028 orig(ui, repo, *pats, **opts)
1029 finally:
1029 finally:
1030 repo.lfstatus = False
1030 repo.lfstatus = False
1031
1031
1032 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1032 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1033 similarity=None):
1033 similarity=None):
1034 if not lfutil.islfilesrepo(repo):
1034 if not lfutil.islfilesrepo(repo):
1035 return orig(repo, pats, opts, dry_run, similarity)
1035 return orig(repo, pats, opts, dry_run, similarity)
1036 # Get the list of missing largefiles so we can remove them
1036 # Get the list of missing largefiles so we can remove them
1037 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1037 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1038 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1038 s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
1039 False, False)
1039 False, False)
1040 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1040 (unsure, modified, added, removed, missing, unknown, ignored, clean) = s
1041
1041
1042 # Call into the normal remove code, but the removing of the standin, we want
1042 # Call into the normal remove code, but the removing of the standin, we want
1043 # to have handled by original addremove. Monkey patching here makes sure
1043 # to have handled by original addremove. Monkey patching here makes sure
1044 # we don't remove the standin in the largefiles code, preventing a very
1044 # we don't remove the standin in the largefiles code, preventing a very
1045 # confused state later.
1045 # confused state later.
1046 if missing:
1046 if missing:
1047 m = [repo.wjoin(f) for f in missing]
1047 m = [repo.wjoin(f) for f in missing]
1048 repo._isaddremove = True
1048 repo._isaddremove = True
1049 removelargefiles(repo.ui, repo, *m, **opts)
1049 removelargefiles(repo.ui, repo, *m, **opts)
1050 repo._isaddremove = False
1050 repo._isaddremove = False
1051 # Call into the normal add code, and any files that *should* be added as
1051 # Call into the normal add code, and any files that *should* be added as
1052 # largefiles will be
1052 # largefiles will be
1053 addlargefiles(repo.ui, repo, *pats, **opts)
1053 addlargefiles(repo.ui, repo, *pats, **opts)
1054 # Now that we've handled largefiles, hand off to the original addremove
1054 # Now that we've handled largefiles, hand off to the original addremove
1055 # function to take care of the rest. Make sure it doesn't do anything with
1055 # function to take care of the rest. Make sure it doesn't do anything with
1056 # largefiles by installing a matcher that will ignore them.
1056 # largefiles by installing a matcher that will ignore them.
1057 installnormalfilesmatchfn(repo[None].manifest())
1057 installnormalfilesmatchfn(repo[None].manifest())
1058 result = orig(repo, pats, opts, dry_run, similarity)
1058 result = orig(repo, pats, opts, dry_run, similarity)
1059 restorematchfn()
1059 restorematchfn()
1060 return result
1060 return result
1061
1061
1062 # Calling purge with --all will cause the largefiles to be deleted.
1062 # Calling purge with --all will cause the largefiles to be deleted.
1063 # Override repo.status to prevent this from happening.
1063 # Override repo.status to prevent this from happening.
1064 def overridepurge(orig, ui, repo, *dirs, **opts):
1064 def overridepurge(orig, ui, repo, *dirs, **opts):
1065 # XXX large file status is buggy when used on repo proxy.
1065 # XXX large file status is buggy when used on repo proxy.
1066 # XXX this needs to be investigate.
1066 # XXX this needs to be investigate.
1067 repo = repo.unfiltered()
1067 repo = repo.unfiltered()
1068 oldstatus = repo.status
1068 oldstatus = repo.status
1069 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1069 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1070 clean=False, unknown=False, listsubrepos=False):
1070 clean=False, unknown=False, listsubrepos=False):
1071 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1071 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1072 listsubrepos)
1072 listsubrepos)
1073 lfdirstate = lfutil.openlfdirstate(ui, repo)
1073 lfdirstate = lfutil.openlfdirstate(ui, repo)
1074 modified, added, removed, deleted, unknown, ignored, clean = r
1074 modified, added, removed, deleted, unknown, ignored, clean = r
1075 unknown = [f for f in unknown if lfdirstate[f] == '?']
1075 unknown = [f for f in unknown if lfdirstate[f] == '?']
1076 ignored = [f for f in ignored if lfdirstate[f] == '?']
1076 ignored = [f for f in ignored if lfdirstate[f] == '?']
1077 return modified, added, removed, deleted, unknown, ignored, clean
1077 return modified, added, removed, deleted, unknown, ignored, clean
1078 repo.status = overridestatus
1078 repo.status = overridestatus
1079 orig(ui, repo, *dirs, **opts)
1079 orig(ui, repo, *dirs, **opts)
1080 repo.status = oldstatus
1080 repo.status = oldstatus
1081
1081
1082 def overriderollback(orig, ui, repo, **opts):
1082 def overriderollback(orig, ui, repo, **opts):
1083 result = orig(ui, repo, **opts)
1083 result = orig(ui, repo, **opts)
1084 merge.update(repo, node=None, branchmerge=False, force=True,
1084 merge.update(repo, node=None, branchmerge=False, force=True,
1085 partial=lfutil.isstandin)
1085 partial=lfutil.isstandin)
1086 wlock = repo.wlock()
1086 wlock = repo.wlock()
1087 try:
1087 try:
1088 lfdirstate = lfutil.openlfdirstate(ui, repo)
1088 lfdirstate = lfutil.openlfdirstate(ui, repo)
1089 lfiles = lfutil.listlfiles(repo)
1089 lfiles = lfutil.listlfiles(repo)
1090 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1090 oldlfiles = lfutil.listlfiles(repo, repo[None].parents()[0].rev())
1091 for file in lfiles:
1091 for file in lfiles:
1092 if file in oldlfiles:
1092 if file in oldlfiles:
1093 lfdirstate.normallookup(file)
1093 lfdirstate.normallookup(file)
1094 else:
1094 else:
1095 lfdirstate.add(file)
1095 lfdirstate.add(file)
1096 lfdirstate.write()
1096 lfdirstate.write()
1097 finally:
1097 finally:
1098 wlock.release()
1098 wlock.release()
1099 return result
1099 return result
1100
1100
1101 def overridetransplant(orig, ui, repo, *revs, **opts):
1101 def overridetransplant(orig, ui, repo, *revs, **opts):
1102 try:
1102 try:
1103 oldstandins = lfutil.getstandinsstate(repo)
1103 oldstandins = lfutil.getstandinsstate(repo)
1104 repo._istransplanting = True
1104 repo._istransplanting = True
1105 result = orig(ui, repo, *revs, **opts)
1105 result = orig(ui, repo, *revs, **opts)
1106 newstandins = lfutil.getstandinsstate(repo)
1106 newstandins = lfutil.getstandinsstate(repo)
1107 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1107 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1108 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1108 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1109 printmessage=True)
1109 printmessage=True)
1110 finally:
1110 finally:
1111 repo._istransplanting = False
1111 repo._istransplanting = False
1112 return result
1112 return result
1113
1113
1114 def overridecat(orig, ui, repo, file1, *pats, **opts):
1114 def overridecat(orig, ui, repo, file1, *pats, **opts):
1115 ctx = scmutil.revsingle(repo, opts.get('rev'))
1115 ctx = scmutil.revsingle(repo, opts.get('rev'))
1116 err = 1
1116 err = 1
1117 notbad = set()
1117 notbad = set()
1118 m = scmutil.match(ctx, (file1,) + pats, opts)
1118 m = scmutil.match(ctx, (file1,) + pats, opts)
1119 origmatchfn = m.matchfn
1119 origmatchfn = m.matchfn
1120 def lfmatchfn(f):
1120 def lfmatchfn(f):
1121 if origmatchfn(f):
1121 if origmatchfn(f):
1122 return True
1122 return True
1123 lf = lfutil.splitstandin(f)
1123 lf = lfutil.splitstandin(f)
1124 if lf is None:
1124 if lf is None:
1125 return False
1125 return False
1126 notbad.add(lf)
1126 notbad.add(lf)
1127 return origmatchfn(lf)
1127 return origmatchfn(lf)
1128 m.matchfn = lfmatchfn
1128 m.matchfn = lfmatchfn
1129 origbadfn = m.bad
1129 origbadfn = m.bad
1130 def lfbadfn(f, msg):
1130 def lfbadfn(f, msg):
1131 if not f in notbad:
1131 if not f in notbad:
1132 origbadfn(f, msg)
1132 origbadfn(f, msg)
1133 m.bad = lfbadfn
1133 m.bad = lfbadfn
1134 for f in ctx.walk(m):
1134 for f in ctx.walk(m):
1135 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1135 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1136 pathname=f)
1136 pathname=f)
1137 lf = lfutil.splitstandin(f)
1137 lf = lfutil.splitstandin(f)
1138 if lf is None or origmatchfn(f):
1138 if lf is None or origmatchfn(f):
1139 # duplicating unreachable code from commands.cat
1139 # duplicating unreachable code from commands.cat
1140 data = ctx[f].data()
1140 data = ctx[f].data()
1141 if opts.get('decode'):
1141 if opts.get('decode'):
1142 data = repo.wwritedata(f, data)
1142 data = repo.wwritedata(f, data)
1143 fp.write(data)
1143 fp.write(data)
1144 else:
1144 else:
1145 hash = lfutil.readstandin(repo, lf, ctx.rev())
1145 hash = lfutil.readstandin(repo, lf, ctx.rev())
1146 if not lfutil.inusercache(repo.ui, hash):
1146 if not lfutil.inusercache(repo.ui, hash):
1147 store = basestore._openstore(repo)
1147 store = basestore._openstore(repo)
1148 success, missing = store.get([(lf, hash)])
1148 success, missing = store.get([(lf, hash)])
1149 if len(success) != 1:
1149 if len(success) != 1:
1150 raise util.Abort(
1150 raise util.Abort(
1151 _('largefile %s is not in cache and could not be '
1151 _('largefile %s is not in cache and could not be '
1152 'downloaded') % lf)
1152 'downloaded') % lf)
1153 path = lfutil.usercachepath(repo.ui, hash)
1153 path = lfutil.usercachepath(repo.ui, hash)
1154 fpin = open(path, "rb")
1154 fpin = open(path, "rb")
1155 for chunk in util.filechunkiter(fpin, 128 * 1024):
1155 for chunk in util.filechunkiter(fpin, 128 * 1024):
1156 fp.write(chunk)
1156 fp.write(chunk)
1157 fpin.close()
1157 fpin.close()
1158 fp.close()
1158 fp.close()
1159 err = 0
1159 err = 0
1160 return err
1160 return err
1161
1161
1162 def mercurialsinkbefore(orig, sink):
1162 def mercurialsinkbefore(orig, sink):
1163 sink.repo._isconverting = True
1163 sink.repo._isconverting = True
1164 orig(sink)
1164 orig(sink)
1165
1165
1166 def mercurialsinkafter(orig, sink):
1166 def mercurialsinkafter(orig, sink):
1167 sink.repo._isconverting = False
1167 sink.repo._isconverting = False
1168 orig(sink)
1168 orig(sink)
General Comments 0
You need to be logged in to leave comments. Login now